]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'char-misc/char-misc-next'
authorStephen Rothwell <sfr@canb.auug.org.au>
Thu, 9 Apr 2015 07:10:26 +0000 (17:10 +1000)
committerStephen Rothwell <sfr@canb.auug.org.au>
Thu, 9 Apr 2015 07:10:26 +0000 (17:10 +1000)
119 files changed:
Documentation/devicetree/bindings/arm/coresight.txt
Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/memory-controllers/ingenic,jz4780-nemc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/misc/lis302.txt
Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
Documentation/driver-model/devres.txt
Documentation/trace/coresight.txt
MAINTAINERS
arch/arm/Kconfig.debug
arch/arm/boot/dts/hip04.dtsi
arch/arm/boot/dts/omap3-beagle-xm.dts
arch/arm/boot/dts/omap3-beagle.dts
arch/arm/boot/dts/omap3-n900.dts
arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
arch/arm64/Kconfig.debug
arch/m32r/include/asm/io.h
arch/x86/include/uapi/asm/hyperv.h
drivers/Makefile
drivers/char/hw_random/core.c
drivers/char/hw_random/pasemi-rng.c
drivers/char/hw_random/powernv-rng.c
drivers/char/hw_random/ppc4xx-rng.c
drivers/char/i8k.c
drivers/char/ipmi/ipmi_si_intf.c
drivers/char/misc.c
drivers/char/virtio_console.c
drivers/char/xillybus/xillybus_core.c
drivers/char/xillybus/xillybus_of.c
drivers/extcon/Kconfig
drivers/extcon/Makefile
drivers/extcon/extcon-arizona.c
drivers/extcon/extcon-max14577.c
drivers/extcon/extcon-max77693.c
drivers/extcon/extcon-max77843.c [new file with mode: 0644]
drivers/extcon/extcon-max8997.c
drivers/extcon/extcon-rt8973a.c
drivers/extcon/extcon-sm5502.c
drivers/extcon/extcon-usb-gpio.c [new file with mode: 0644]
drivers/extcon/extcon.c [moved from drivers/extcon/extcon-class.c with 97% similarity]
drivers/hv/channel.c
drivers/hv/channel_mgmt.c
drivers/hv/connection.c
drivers/hv/hv.c
drivers/hv/hv_balloon.c
drivers/hv/hv_util.c
drivers/hv/hyperv_vmbus.h
drivers/hv/vmbus_drv.c
drivers/hwtracing/coresight/Kconfig [new file with mode: 0644]
drivers/hwtracing/coresight/Makefile [moved from drivers/coresight/Makefile with 100% similarity]
drivers/hwtracing/coresight/coresight-etb10.c [moved from drivers/coresight/coresight-etb10.c with 99% similarity]
drivers/hwtracing/coresight/coresight-etm-cp14.c [moved from drivers/coresight/coresight-etm-cp14.c with 100% similarity]
drivers/hwtracing/coresight/coresight-etm.h [moved from drivers/coresight/coresight-etm.h with 100% similarity]
drivers/hwtracing/coresight/coresight-etm3x.c [moved from drivers/coresight/coresight-etm3x.c with 100% similarity]
drivers/hwtracing/coresight/coresight-funnel.c [moved from drivers/coresight/coresight-funnel.c with 100% similarity]
drivers/hwtracing/coresight/coresight-priv.h [moved from drivers/coresight/coresight-priv.h with 100% similarity]
drivers/hwtracing/coresight/coresight-replicator.c [moved from drivers/coresight/coresight-replicator.c with 98% similarity]
drivers/hwtracing/coresight/coresight-tmc.c [moved from drivers/coresight/coresight-tmc.c with 91% similarity]
drivers/hwtracing/coresight/coresight-tpiu.c [moved from drivers/coresight/coresight-tpiu.c with 100% similarity]
drivers/hwtracing/coresight/coresight.c [moved from drivers/coresight/coresight.c with 99% similarity]
drivers/hwtracing/coresight/of_coresight.c [moved from drivers/coresight/of_coresight.c with 94% similarity]
drivers/mcb/mcb-pci.c
drivers/memory/Kconfig
drivers/memory/Makefile
drivers/memory/jz4780-nemc.c [new file with mode: 0644]
drivers/misc/bh1780gli.c
drivers/misc/carma/carma-fpga-program.c
drivers/misc/carma/carma-fpga.c
drivers/misc/lis3lv02d/lis3lv02d.c
drivers/misc/lis3lv02d/lis3lv02d_i2c.c
drivers/misc/lis3lv02d/lis3lv02d_spi.c
drivers/misc/mei/Makefile
drivers/misc/mei/amthif.c
drivers/misc/mei/bus.c
drivers/misc/mei/client.c
drivers/misc/mei/client.h
drivers/misc/mei/debugfs.c
drivers/misc/mei/hbm.c
drivers/misc/mei/hw-me.c
drivers/misc/mei/hw-me.h
drivers/misc/mei/hw-txe.c
drivers/misc/mei/init.c
drivers/misc/mei/interrupt.c
drivers/misc/mei/main.c
drivers/misc/mei/mei-trace.c [new file with mode: 0644]
drivers/misc/mei/mei-trace.h [new file with mode: 0644]
drivers/misc/mei/mei_dev.h
drivers/misc/mei/nfc.c
drivers/misc/mei/pci-me.c
drivers/misc/mei/pci-txe.c
drivers/misc/mei/wd.c
drivers/misc/mic/host/mic_boot.c
drivers/misc/mic/host/mic_intr.c
drivers/misc/sram.c
drivers/misc/tifm_7xx1.c
drivers/misc/vmw_vmci/vmci_driver.c
drivers/misc/vmw_vmci/vmci_host.c
drivers/misc/vmw_vmci/vmci_queue_pair.c
drivers/pcmcia/omap_cf.c
drivers/pcmcia/pd6729.c
drivers/pcmcia/soc_common.c
drivers/pcmcia/yenta_socket.c
drivers/spmi/Kconfig
drivers/spmi/spmi-pmic-arb.c
drivers/spmi/spmi.c
drivers/uio/uio.c
drivers/w1/masters/mxc_w1.c
drivers/w1/masters/omap_hdq.c
drivers/w1/masters/w1-gpio.c
include/linux/hyperv.h
include/linux/io.h
include/linux/jz4780-nemc.h [new file with mode: 0644]
include/linux/mfd/arizona/core.h
include/linux/miscdevice.h
kernel/time/clockevents.c
lib/devres.c
scripts/checkkconfigsymbols.py [changed mode: 0644->0755]
sound/soc/codecs/arizona.c
tools/hv/Makefile
tools/hv/hv_vss_daemon.c

index a3089359aaa6e58dfdf9588995d72b53ff2d1938..88602b75418e841a66d568d59652707eeb41bb4a 100644 (file)
@@ -61,7 +61,6 @@ Example:
                compatible = "arm,coresight-etb10", "arm,primecell";
                reg = <0 0x20010000 0 0x1000>;
 
-               coresight-default-sink;
                clocks = <&oscclk6a>;
                clock-names = "apb_pclk";
                port {
diff --git a/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt b/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt
new file mode 100644 (file)
index 0000000..af0b903
--- /dev/null
@@ -0,0 +1,18 @@
+USB GPIO Extcon device
+
+This is a virtual device used to generate USB cable states from the USB ID pin
+connected to a GPIO pin.
+
+Required properties:
+- compatible: Should be "linux,extcon-usb-gpio"
+- id-gpio: gpio for USB ID pin. See gpio binding.
+
+Example: Examples of extcon-usb-gpio node in dra7-evm.dts as listed below:
+       extcon_usb1 {
+               compatible = "linux,extcon-usb-gpio";
+               id-gpio = <&gpio6 1 GPIO_ACTIVE_HIGH>;
+       }
+
+       &omap_dwc3_1 {
+               extcon = <&extcon_usb1>;
+       };
diff --git a/Documentation/devicetree/bindings/memory-controllers/ingenic,jz4780-nemc.txt b/Documentation/devicetree/bindings/memory-controllers/ingenic,jz4780-nemc.txt
new file mode 100644 (file)
index 0000000..f936b55
--- /dev/null
@@ -0,0 +1,75 @@
+* Ingenic JZ4780 NAND/external memory controller (NEMC)
+
+This file documents the device tree bindings for the NEMC external memory
+controller in Ingenic JZ4780
+
+Required properties:
+- compatible: Should be set to one of:
+    "ingenic,jz4780-nemc" (JZ4780)
+- reg: Should specify the NEMC controller registers location and length.
+- clocks: Clock for the NEMC controller.
+- #address-cells: Must be set to 2.
+- #size-cells: Must be set to 1.
+- ranges: A set of ranges for each bank describing the physical memory layout.
+  Each should specify the following 4 integer values:
+
+    <cs number> 0 <physical address of mapping> <size of mapping>
+
+Each child of the NEMC node describes a device connected to the NEMC.
+
+Required child node properties:
+- reg: Should contain at least one register specifier, given in the following
+  format:
+
+    <cs number> <offset> <size>
+
+  Multiple registers can be specified across multiple banks. This is needed,
+  for example, for packaged NAND devices with multiple dies. Such devices
+  should be grouped into a single node.
+
+Optional child node properties:
+- ingenic,nemc-bus-width: Specifies the bus width in bits. Defaults to 8 bits.
+- ingenic,nemc-tAS: Address setup time in nanoseconds.
+- ingenic,nemc-tAH: Address hold time in nanoseconds.
+- ingenic,nemc-tBP: Burst pitch time in nanoseconds.
+- ingenic,nemc-tAW: Access wait time in nanoseconds.
+- ingenic,nemc-tSTRV: Static memory recovery time in nanoseconds.
+
+If a child node references multiple banks in its "reg" property, the same value
+for all optional parameters will be configured for all banks. If any optional
+parameters are omitted, they will be left unchanged from whatever they are
+configured to when the NEMC device is probed (which may be the reset value as
+given in the hardware reference manual, or a value configured by the boot
+loader).
+
+Example (NEMC node with a NAND child device attached at CS1):
+
+nemc: nemc@13410000 {
+       compatible = "ingenic,jz4780-nemc";
+       reg = <0x13410000 0x10000>;
+
+       #address-cells = <2>;
+       #size-cells = <1>;
+
+       ranges = <1 0 0x1b000000 0x1000000
+                 2 0 0x1a000000 0x1000000
+                 3 0 0x19000000 0x1000000
+                 4 0 0x18000000 0x1000000
+                 5 0 0x17000000 0x1000000
+                 6 0 0x16000000 0x1000000>;
+
+       clocks = <&cgu JZ4780_CLK_NEMC>;
+
+       nand: nand@1 {
+               compatible = "ingenic,jz4780-nand";
+               reg = <1 0 0x1000000>;
+
+               ingenic,nemc-tAS = <10>;
+               ingenic,nemc-tAH = <5>;
+               ingenic,nemc-tBP = <10>;
+               ingenic,nemc-tAW = <15>;
+               ingenic,nemc-tSTRV = <100>;
+
+               ...
+       };
+};
index 6def86f6b053bea992004509bd812c4e56627aed..2a19bff9693fc55a7b61bb45dcf32963bc7f224a 100644 (file)
@@ -46,11 +46,18 @@ Optional properties for all bus drivers:
                                interrupt 2
  - st,wakeup-{x,y,z}-{lo,hi}:  set wakeup condition on x/y/z axis for
                                upper/lower limit
+ - st,wakeup-threshold:                set wakeup threshold
+ - st,wakeup2-{x,y,z}-{lo,hi}: set wakeup condition on x/y/z axis for
+                               upper/lower limit for second wakeup
+                               engine.
+ - st,wakeup2-threshold:       set wakeup threshold for second wakeup
+                               engine.
  - st,highpass-cutoff-hz=:     1, 2, 4 or 8 for 1Hz, 2Hz, 4Hz or 8Hz of
                                highpass cut-off frequency
  - st,hipass{1,2}-disable:     disable highpass 1/2.
  - st,default-rate=:           set the default rate
- - st,axis-{x,y,z}=:           set the axis to map to the three coordinates
+ - st,axis-{x,y,z}=:           set the axis to map to the three coordinates.
+                               Negative values can be used for inverted axis.
  - st,{min,max}-limit-{x,y,z}  set the min/max limits for x/y/z axis
                                (used by self-test)
 
index 715d0998af8e2fbce29e16ea7cbc96e391aa6bf2..e16b9b5afc70a139087dea1ca3a64e9989525112 100644 (file)
@@ -1,6 +1,6 @@
 Qualcomm SPMI Controller (PMIC Arbiter)
 
-The SPMI PMIC Arbiter is found on the Snapdragon 800 Series.  It is an SPMI
+The SPMI PMIC Arbiter is found on Snapdragon chipsets.  It is an SPMI
 controller with wrapping arbitration logic to allow for multiple on-chip
 devices to control a single SPMI master.
 
@@ -19,6 +19,10 @@ Required properties:
      "core" - core registers
      "intr" - interrupt controller registers
      "cnfg" - configuration registers
+   Registers used only for V2 PMIC Arbiter:
+     "chnls"  - tx-channel per virtual slave registers.
+     "obsrvr" - rx-channel (called observer) per virtual slave 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
index e1e2bbd7a40404344adb1c46697cfb47dd47f9ee..831a5363f6be37756b0016c1c30fc0f46055aff8 100644 (file)
@@ -276,6 +276,7 @@ IOMAP
   devm_ioport_unmap()
   devm_ioremap()
   devm_ioremap_nocache()
+  devm_ioremap_wc()
   devm_ioremap_resource() : checks resource, requests memory region, ioremaps
   devm_iounmap()
   pcim_iomap()
index 02361552a3eac314a68338c5638f4239ed0d28d7..77d14d51a670a523a14ec9b4581f66d720ba3e76 100644 (file)
@@ -14,7 +14,7 @@ document is concerned with the latter.
 HW assisted tracing is becoming increasingly useful when dealing with systems
 that have many SoCs and other components like GPU and DMA engines.  ARM has
 developed a HW assisted tracing solution by means of different components, each
-being added to a design at systhesis time to cater to specific tracing needs.
+being added to a design at synthesis time to cater to specific tracing needs.
 Compoments are generally categorised as source, link and sinks and are
 (usually) discovered using the AMBA bus.
 
index f1c07b28d93547a0934500c1aae41fcb03d65726..596a358369378ce6e7ed1b61c918a532ad13adcf 100644 (file)
@@ -957,7 +957,7 @@ ARM/CORESIGHT FRAMEWORK AND DRIVERS
 M:     Mathieu Poirier <mathieu.poirier@linaro.org>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
-F:     drivers/coresight/*
+F:     drivers/hwtracing/coresight/*
 F:     Documentation/trace/coresight.txt
 F:     Documentation/devicetree/bindings/arm/coresight.txt
 F:     Documentation/ABI/testing/sysfs-bus-coresight-devices-*
@@ -1811,7 +1811,7 @@ S:        Supported
 F:     drivers/spi/spi-atmel.*
 
 ATMEL SSC DRIVER
-M:     Bo Shen <voice.shen@atmel.com>
+M:     Nicolas Ferre <nicolas.ferre@atmel.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Supported
 F:     drivers/misc/atmel-ssc.c
index 1ced63c642d2361d9282c70eeabcc6a6f66fff02..0c12ffb155a23c604c9bbb9b849a913d359e34ae 100644 (file)
@@ -1599,59 +1599,6 @@ config DEBUG_SET_MODULE_RONX
          against certain classes of kernel exploits.
          If in doubt, say "N".
 
-menuconfig CORESIGHT
-       bool "CoreSight Tracing Support"
-       select ARM_AMBA
-       help
-         This framework provides a kernel interface for the CoreSight debug
-         and trace drivers to register themselves with. It's intended to build
-         a topological view of the CoreSight components based on a DT
-         specification and configure the right serie of components when a
-         trace source gets enabled.
-
-if CORESIGHT
-config CORESIGHT_LINKS_AND_SINKS
-       bool "CoreSight Link and Sink drivers"
-       help
-         This enables support for CoreSight link and sink drivers that are
-         responsible for transporting and collecting the trace data
-         respectively.  Link and sinks are dynamically aggregated with a trace
-         entity at run time to form a complete trace path.
-
-config CORESIGHT_LINK_AND_SINK_TMC
-       bool "Coresight generic TMC driver"
-       depends on CORESIGHT_LINKS_AND_SINKS
-       help
-         This enables support for the Trace Memory Controller driver.  Depending
-         on its configuration the device can act as a link (embedded trace router
-         - ETR) or sink (embedded trace FIFO).  The driver complies with the
-         generic implementation of the component without special enhancement or
-         added features.
-
-config CORESIGHT_SINK_TPIU
-       bool "Coresight generic TPIU driver"
-       depends on CORESIGHT_LINKS_AND_SINKS
-       help
-         This enables support for the Trace Port Interface Unit driver, responsible
-         for bridging the gap between the on-chip coresight components and a trace
-         port collection engine, typically connected to an external host for use
-         case capturing more traces than the on-board coresight memory can handle.
-
-config CORESIGHT_SINK_ETBV10
-       bool "Coresight ETBv1.0 driver"
-       depends on CORESIGHT_LINKS_AND_SINKS
-       help
-         This enables support for the Embedded Trace Buffer version 1.0 driver
-         that complies with the generic implementation of the component without
-         special enhancement or added features.
+source "drivers/hwtracing/coresight/Kconfig"
 
-config CORESIGHT_SOURCE_ETM3X
-       bool "CoreSight Embedded Trace Macrocell 3.x driver"
-       select CORESIGHT_LINKS_AND_SINKS
-       help
-         This driver provides support for processor ETM3.x and PTM1.x modules,
-         which allows tracing the instructions that a processor is executing
-         This is primarily useful for instruction level tracing.  Depending
-         the ETM version data tracing may also be available.
-endif
 endmenu
index 238814596a8787b5135df02f592306a823a1319e..44044f2751151c6a244036de3d665da9da4bd32f 100644 (file)
                compatible = "arm,coresight-etb10", "arm,primecell";
                reg = <0 0xe3c42000 0 0x1000>;
 
-               coresight-default-sink;
                clocks = <&clk_375m>;
                clock-names = "apb_pclk";
                port {
index da1464bfbc603ae1b569980627e0121b06ce44be..7c4dca122a91d99a9848a691ce96a917294f59eb 100644 (file)
                compatible = "arm,coresight-etb10", "arm,primecell";
                reg = <0x5401b000 0x1000>;
 
-               coresight-default-sink;
                clocks = <&emu_src_ck>;
                clock-names = "apb_pclk";
                port {
index 28f99a14c0a1fe8677987b180db4cd13e9ad621a..a5474113cd50647f4b945b8db7ea3ff3a7643c83 100644 (file)
                compatible = "arm,coresight-etb10", "arm,primecell";
                reg = <0x5401b000 0x1000>;
 
-               coresight-default-sink;
                clocks = <&emu_src_ck>;
                clock-names = "apb_pclk";
                port {
index 499e228cc965e77e0babff69fad22b6e93ea3a58..5c16145920eafd9604f0571dfe60a23986313871 100644 (file)
        pinctrl-0 = <&i2c3_pins>;
 
        clock-frequency = <400000>;
+
+       lis302dl: lis3lv02d@1d {
+               compatible = "st,lis3lv02d";
+               reg = <0x1d>;
+
+               Vdd-supply = <&vaux1>;
+               Vdd_IO-supply = <&vio>;
+
+               interrupt-parent = <&gpio6>;
+               interrupts = <21 20>; /* 181 and 180 */
+
+               /* click flags */
+               st,click-single-x;
+               st,click-single-y;
+               st,click-single-z;
+
+               /* Limits are 0.5g * value */
+               st,click-threshold-x = <8>;
+               st,click-threshold-y = <8>;
+               st,click-threshold-z = <10>;
+
+               /* Click must be longer than time limit */
+               st,click-time-limit = <9>;
+
+               /* Kind of debounce filter */
+               st,click-latency = <50>;
+
+               /* Interrupt line 2 for click detection */
+               st,irq2-click;
+
+               st,wakeup-x-hi;
+               st,wakeup-y-hi;
+               st,wakeup-threshold = <(800/18)>; /* millig-value / 18 to get HW values */
+
+               st,wakeup2-z-hi;
+               st,wakeup2-threshold = <(900/18)>; /* millig-value / 18 to get HW values */
+
+               st,hipass1-disable;
+               st,hipass2-disable;
+
+               st,axis-x = <1>;    /* LIS3_DEV_X */
+               st,axis-y = <(-2)>; /* LIS3_INV_DEV_Y */
+               st,axis-z = <(-3)>; /* LIS3_INV_DEV_Z */
+
+               st,min-limit-x = <(-32)>;
+               st,min-limit-y = <3>;
+               st,min-limit-z = <3>;
+
+               st,max-limit-x = <(-3)>;
+               st,max-limit-y = <32>;
+               st,max-limit-z = <32>;
+       };
 };
 
 &mmc1 {
index 33920df036401a76c2a821089b3d093c4967a67e..7a2aeacd62c0c2cb23b1247ce3b5ca6c55225d24 100644 (file)
                compatible = "arm,coresight-etb10", "arm,primecell";
                reg = <0 0x20010000 0 0x1000>;
 
-               coresight-default-sink;
                clocks = <&oscclk6a>;
                clock-names = "apb_pclk";
                port {
index 4a8741073c90c0585772cff64093b9f3f0e63aa9..d6285ef9b5f976639630606b04d7244a05ac6130 100644 (file)
@@ -89,4 +89,6 @@ config DEBUG_ALIGN_RODATA
 
          If in doubt, say N
 
+source "drivers/hwtracing/coresight/Kconfig"
+
 endmenu
index 6e7787f3dac7bb52f87ec33378193b14f65bf70c..9cc00dbd59cef4096b186fd1ffe5e4b51766f430 100644 (file)
@@ -67,6 +67,7 @@ static inline void __iomem *ioremap(unsigned long offset, unsigned long size)
 
 extern void iounmap(volatile void __iomem *addr);
 #define ioremap_nocache(off,size) ioremap(off,size)
+#define ioremap_wc ioremap_nocache
 
 /*
  * IO bus memory addresses are also 1:1 with the physical address
index 90c458e66e13332b847f2a5fb1f0772d3f70e39f..ce6068dbcfbc6e84c76b43239d80f5d020d431ee 100644 (file)
 #define HV_STATUS_INVALID_HYPERCALL_CODE       2
 #define HV_STATUS_INVALID_HYPERCALL_INPUT      3
 #define HV_STATUS_INVALID_ALIGNMENT            4
+#define HV_STATUS_INSUFFICIENT_MEMORY          11
+#define HV_STATUS_INVALID_CONNECTION_ID                18
 #define HV_STATUS_INSUFFICIENT_BUFFERS         19
 
 typedef struct _HV_REFERENCE_TSC_PAGE {
index 527a6da8d539ad2abb84c5397e0087c1914501af..46d2554be40459060ae8ab3e7bba1b21a453c7d0 100644 (file)
@@ -163,5 +163,5 @@ obj-$(CONFIG_POWERCAP)              += powercap/
 obj-$(CONFIG_MCB)              += mcb/
 obj-$(CONFIG_RAS)              += ras/
 obj-$(CONFIG_THUNDERBOLT)      += thunderbolt/
-obj-$(CONFIG_CORESIGHT)                += coresight/
+obj-$(CONFIG_CORESIGHT)                += hwtracing/coresight/
 obj-$(CONFIG_ANDROID)          += android/
index 571ef61f8ea94eb36bd34b4ab459b4c3435f5247..da8faf78536a3ae01827a2ee9480c486a04297a5 100644 (file)
@@ -300,11 +300,14 @@ static const struct file_operations rng_chrdev_ops = {
        .llseek         = noop_llseek,
 };
 
+static const struct attribute_group *rng_dev_groups[];
+
 static struct miscdevice rng_miscdev = {
        .minor          = RNG_MISCDEV_MINOR,
        .name           = RNG_MODULE_NAME,
        .nodename       = "hwrng",
        .fops           = &rng_chrdev_ops,
+       .groups         = rng_dev_groups,
 };
 
 
@@ -377,37 +380,22 @@ static DEVICE_ATTR(rng_available, S_IRUGO,
                   hwrng_attr_available_show,
                   NULL);
 
+static struct attribute *rng_dev_attrs[] = {
+       &dev_attr_rng_current.attr,
+       &dev_attr_rng_available.attr,
+       NULL
+};
+
+ATTRIBUTE_GROUPS(rng_dev);
 
 static void __exit unregister_miscdev(void)
 {
-       device_remove_file(rng_miscdev.this_device, &dev_attr_rng_available);
-       device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
        misc_deregister(&rng_miscdev);
 }
 
 static int __init register_miscdev(void)
 {
-       int err;
-
-       err = misc_register(&rng_miscdev);
-       if (err)
-               goto out;
-       err = device_create_file(rng_miscdev.this_device,
-                                &dev_attr_rng_current);
-       if (err)
-               goto err_misc_dereg;
-       err = device_create_file(rng_miscdev.this_device,
-                                &dev_attr_rng_available);
-       if (err)
-               goto err_remove_current;
-out:
-       return err;
-
-err_remove_current:
-       device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
-err_misc_dereg:
-       misc_deregister(&rng_miscdev);
-       goto out;
+       return misc_register(&rng_miscdev);
 }
 
 static int hwrng_fillfn(void *unused)
index 3eb7bdd7f93bc31f39e70e7416b8ae85a46c7590..51cb1d5cc489773072edcaf637091172c55ef1fe 100644 (file)
@@ -133,7 +133,7 @@ static int rng_remove(struct platform_device *dev)
        return 0;
 }
 
-static struct of_device_id rng_match[] = {
+static const struct of_device_id rng_match[] = {
        { .compatible      = "1682m-rng", },
        { .compatible      = "pasemi,pwrficient-rng", },
        { },
index 3f4f63204560f932a5160b8d403baa51dac9b4bf..263a5bb8e605a729a7e68fa056f43d377fd69d41 100644 (file)
@@ -61,7 +61,7 @@ static int powernv_rng_probe(struct platform_device *pdev)
        return 0;
 }
 
-static struct of_device_id powernv_rng_match[] = {
+static const struct of_device_id powernv_rng_match[] = {
        { .compatible   = "ibm,power-rng",},
        {},
 };
index c85d31a5f9e3f57acff8ca95338e15a2f6ab5419..b2cfda0fa93e28535772cd67237ee681885d274f 100644 (file)
@@ -123,7 +123,7 @@ static int ppc4xx_rng_remove(struct platform_device *dev)
        return 0;
 }
 
-static struct of_device_id ppc4xx_rng_match[] = {
+static const struct of_device_id ppc4xx_rng_match[] = {
        { .compatible = "ppc4xx-rng", },
        { .compatible = "amcc,ppc460ex-rng", },
        { .compatible = "amcc,ppc440epx-rng", },
index 24cc4ed9a78014509371815cbe0dfda3ef5ca679..a43048b5b05fde621b836a8ef444c381313ce3c8 100644 (file)
@@ -510,13 +510,15 @@ static int i8k_proc_show(struct seq_file *seq, void *offset)
         * 9)  AC power
         * 10) Fn Key status
         */
-       return seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n",
-                         I8K_PROC_FMT,
-                         bios_version,
-                         i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
-                         cpu_temp,
-                         left_fan, right_fan, left_speed, right_speed,
-                         ac_power, fn_key);
+       seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n",
+                  I8K_PROC_FMT,
+                  bios_version,
+                  i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
+                  cpu_temp,
+                  left_fan, right_fan, left_speed, right_speed,
+                  ac_power, fn_key);
+
+       return 0;
 }
 
 static int i8k_open_fs(struct inode *inode, struct file *file)
index d411e8c6f0a37295b6e9ddc9e428c2c2f4509b68..468c75e103300b39070c1e404f0ef864d71e8ab1 100644 (file)
@@ -2667,7 +2667,7 @@ static struct pci_driver ipmi_pci_driver = {
 };
 #endif /* CONFIG_PCI */
 
-static struct of_device_id ipmi_match[];
+static const struct of_device_id ipmi_match[];
 static int ipmi_probe(struct platform_device *dev)
 {
 #ifdef CONFIG_OF
@@ -2764,7 +2764,7 @@ static int ipmi_remove(struct platform_device *dev)
        return 0;
 }
 
-static struct of_device_id ipmi_match[] =
+static const struct of_device_id ipmi_match[] =
 {
        { .type = "ipmi", .compatible = "ipmi-kcs",
          .data = (void *)(unsigned long) SI_KCS },
index ffa97d261cf3322615bc403931d99c71d1896a50..9fd5a91e0d81e4f5f2ec2abe714ca0d0ef88972c 100644 (file)
@@ -140,12 +140,17 @@ static int misc_open(struct inode * inode, struct file * file)
                        goto fail;
        }
 
+       /*
+        * Place the miscdevice in the file's
+        * private_data so it can be used by the
+        * file operations, including f_op->open below
+        */
+       file->private_data = c;
+
        err = 0;
        replace_fops(file, new_fops);
-       if (file->f_op->open) {
-               file->private_data = c;
+       if (file->f_op->open)
                err = file->f_op->open(inode,file);
-       }
 fail:
        mutex_unlock(&misc_mtx);
        return err;
@@ -169,7 +174,9 @@ static const struct file_operations misc_fops = {
  *     the minor number requested is used.
  *
  *     The structure passed is linked into the kernel and may not be
- *     destroyed until it has been unregistered.
+ *     destroyed until it has been unregistered. By default, an open()
+ *     syscall to the device sets file->private_data to point to the
+ *     structure. Drivers don't need open in fops for this.
  *
  *     A zero is returned on success and a negative errno code for
  *     failure.
@@ -205,8 +212,9 @@ int misc_register(struct miscdevice * misc)
 
        dev = MKDEV(MISC_MAJOR, misc->minor);
 
-       misc->this_device = device_create(misc_class, misc->parent, dev,
-                                         misc, "%s", misc->name);
+       misc->this_device =
+               device_create_with_groups(misc_class, misc->parent, dev,
+                                         misc, misc->groups, "%s", misc->name);
        if (IS_ERR(misc->this_device)) {
                int i = DYNAMIC_MINORS - misc->minor - 1;
                if (i < DYNAMIC_MINORS && i >= 0)
index 72d7028f779b55801795ef0fe49d9dda8cde7970..50754d2033100851b87f78d06401a3c5c137262d 100644 (file)
@@ -355,7 +355,7 @@ static inline bool use_multiport(struct ports_device *portdev)
         * early_init
         */
        if (!portdev->vdev)
-               return 0;
+               return false;
        return __virtio_test_bit(portdev->vdev, VIRTIO_CONSOLE_F_MULTIPORT);
 }
 
index b827fa095f1b324231e3916bc2e22f2315ec8bbf..77d6c127e691a4a8a6417bd4cd0760fed0d513e8 100644 (file)
@@ -1237,6 +1237,8 @@ static ssize_t xillybus_write(struct file *filp, const char __user *userbuf,
                                        unsigned char *tail;
                                        int i;
 
+                                       howmany = 0;
+
                                        end_offset_plus1 = bufpos >>
                                                channel->log2_element_size;
 
index 2002a3a28146985f911b449fb7da72a4cbbec9fd..781865084dc17444d74d39fb87d5aa5c9099988d 100644 (file)
@@ -31,7 +31,7 @@ MODULE_LICENSE("GPL v2");
 static const char xillyname[] = "xillybus_of";
 
 /* Match table for of_platform binding */
-static struct of_device_id xillybus_of_match[] = {
+static const struct of_device_id xillybus_of_match[] = {
        { .compatible = "xillybus,xillybus-1.00.a", },
        { .compatible = "xlnx,xillybus-1.00.a", }, /* Deprecated */
        {}
index 6a1f7de6fa542f3da3ceb472e39c784efcc161ab..fdc0bf0543ce43ec8bf34ad601a03eee40089eb6 100644 (file)
@@ -55,6 +55,16 @@ config EXTCON_MAX77693
          Maxim MAX77693 PMIC. The MAX77693 MUIC is a USB port accessory
          detector and switch.
 
+config EXTCON_MAX77843
+       tristate "MAX77843 EXTCON Support"
+       depends on MFD_MAX77843
+       select IRQ_DOMAIN
+       select REGMAP_I2C
+       help
+         If you say yes here you get support for the MUIC device of
+         Maxim MAX77843. The MAX77843 MUIC is a USB port accessory
+         detector add switch.
+
 config EXTCON_MAX8997
        tristate "MAX8997 EXTCON Support"
        depends on MFD_MAX8997 && IRQ_DOMAIN
@@ -93,4 +103,11 @@ config EXTCON_SM5502
          Silicon Mitus SM5502. The SM5502 is a USB port accessory
          detector and switch.
 
+config EXTCON_USB_GPIO
+       tristate "USB GPIO extcon support"
+       depends on GPIOLIB
+       help
+         Say Y here to enable GPIO based USB cable detection extcon support.
+         Used typically if GPIO is used for USB ID pin detection.
+
 endif # MULTISTATE_SWITCH
index 0370b42e5a27e97425417e075fe3ca5db649e0ce..9204114791a3f749fac44d88ade5e8f4f8d7d175 100644 (file)
@@ -2,13 +2,15 @@
 # Makefile for external connector class (extcon) devices
 #
 
-obj-$(CONFIG_EXTCON)           += extcon-class.o
+obj-$(CONFIG_EXTCON)           += extcon.o
 obj-$(CONFIG_EXTCON_ADC_JACK)  += extcon-adc-jack.o
 obj-$(CONFIG_EXTCON_ARIZONA)   += extcon-arizona.o
 obj-$(CONFIG_EXTCON_GPIO)      += extcon-gpio.o
 obj-$(CONFIG_EXTCON_MAX14577)  += extcon-max14577.o
 obj-$(CONFIG_EXTCON_MAX77693)  += extcon-max77693.o
+obj-$(CONFIG_EXTCON_MAX77843)  += extcon-max77843.o
 obj-$(CONFIG_EXTCON_MAX8997)   += extcon-max8997.o
 obj-$(CONFIG_EXTCON_PALMAS)    += extcon-palmas.o
 obj-$(CONFIG_EXTCON_RT8973A)   += extcon-rt8973a.o
 obj-$(CONFIG_EXTCON_SM5502)    += extcon-sm5502.o
+obj-$(CONFIG_EXTCON_USB_GPIO)  += extcon-usb-gpio.o
index 6b5e795f3fe201cc0c29542a7ef61399fc4cb658..a0ed35b336e499e4c9e22e2cba97fa80f01287dd 100644 (file)
@@ -136,18 +136,35 @@ static const char *arizona_cable[] = {
 
 static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info);
 
-static void arizona_extcon_do_magic(struct arizona_extcon_info *info,
-                                   unsigned int magic)
+static void arizona_extcon_hp_clamp(struct arizona_extcon_info *info,
+                                   bool clamp)
 {
        struct arizona *arizona = info->arizona;
+       unsigned int mask = 0, val = 0;
        int ret;
 
+       switch (arizona->type) {
+       case WM5110:
+               mask = ARIZONA_HP1L_SHRTO | ARIZONA_HP1L_FLWR |
+                      ARIZONA_HP1L_SHRTI;
+               if (clamp)
+                       val = ARIZONA_HP1L_SHRTO;
+               else
+                       val = ARIZONA_HP1L_FLWR | ARIZONA_HP1L_SHRTI;
+               break;
+       default:
+               mask = ARIZONA_RMV_SHRT_HP1L;
+               if (clamp)
+                       val = ARIZONA_RMV_SHRT_HP1L;
+               break;
+       };
+
        mutex_lock(&arizona->dapm->card->dapm_mutex);
 
-       arizona->hpdet_magic = magic;
+       arizona->hpdet_clamp = clamp;
 
-       /* Keep the HP output stages disabled while doing the magic */
-       if (magic) {
+       /* Keep the HP output stages disabled while doing the clamp */
+       if (clamp) {
                ret = regmap_update_bits(arizona->regmap,
                                         ARIZONA_OUTPUT_ENABLES_1,
                                         ARIZONA_OUT1L_ENA |
@@ -158,20 +175,20 @@ static void arizona_extcon_do_magic(struct arizona_extcon_info *info,
                                 ret);
        }
 
-       ret = regmap_update_bits(arizona->regmap, 0x225, 0x4000,
-                                magic);
+       ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1L,
+                                mask, val);
        if (ret != 0)
-               dev_warn(arizona->dev, "Failed to do magic: %d\n",
+               dev_warn(arizona->dev, "Failed to do clamp: %d\n",
                                 ret);
 
-       ret = regmap_update_bits(arizona->regmap, 0x226, 0x4000,
-                                magic);
+       ret = regmap_update_bits(arizona->regmap, ARIZONA_HP_CTRL_1R,
+                                mask, val);
        if (ret != 0)
-               dev_warn(arizona->dev, "Failed to do magic: %d\n",
+               dev_warn(arizona->dev, "Failed to do clamp: %d\n",
                         ret);
 
-       /* Restore the desired state while not doing the magic */
-       if (!magic) {
+       /* Restore the desired state while not doing the clamp */
+       if (!clamp) {
                ret = regmap_update_bits(arizona->regmap,
                                         ARIZONA_OUTPUT_ENABLES_1,
                                         ARIZONA_OUT1L_ENA |
@@ -603,7 +620,7 @@ done:
                           ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL,
                           0);
 
-       arizona_extcon_do_magic(info, 0);
+       arizona_extcon_hp_clamp(info, false);
 
        if (id_gpio)
                gpio_set_value_cansleep(id_gpio, 0);
@@ -648,7 +665,7 @@ static void arizona_identify_headphone(struct arizona_extcon_info *info)
        if (info->mic)
                arizona_stop_mic(info);
 
-       arizona_extcon_do_magic(info, 0x4000);
+       arizona_extcon_hp_clamp(info, true);
 
        ret = regmap_update_bits(arizona->regmap,
                                 ARIZONA_ACCESSORY_DETECT_MODE_1,
@@ -699,7 +716,7 @@ static void arizona_start_hpdet_acc_id(struct arizona_extcon_info *info)
 
        info->hpdet_active = true;
 
-       arizona_extcon_do_magic(info, 0x4000);
+       arizona_extcon_hp_clamp(info, true);
 
        ret = regmap_update_bits(arizona->regmap,
                                 ARIZONA_ACCESSORY_DETECT_MODE_1,
index c1bf0cf747b09fc7e414aecf8349cb5e5a449878..3823aa4a3a8093631c616131dd9c06fc7a77ce33 100644 (file)
@@ -539,8 +539,6 @@ static void max14577_muic_irq_work(struct work_struct *work)
                dev_err(info->dev, "failed to handle MUIC interrupt\n");
 
        mutex_unlock(&info->mutex);
-
-       return;
 }
 
 /*
@@ -730,8 +728,7 @@ static int max14577_muic_probe(struct platform_device *pdev)
                                muic_irq->name, info);
                if (ret) {
                        dev_err(&pdev->dev,
-                               "failed: irq request (IRQ: %d,"
-                               " error :%d)\n",
+                               "failed: irq request (IRQ: %d, error :%d)\n",
                                muic_irq->irq, ret);
                        return ret;
                }
index af165fd0c6f54761a56d1cf75d81663173ff400a..a66bec8f6252dc140fbe6919b3cb846740f70a64 100644 (file)
@@ -190,8 +190,8 @@ enum max77693_muic_acc_type {
        /* The below accessories have same ADC value so ADCLow and
           ADC1K bit is used to separate specific accessory */
                                                /* ADC|VBVolot|ADCLow|ADC1K| */
-       MAX77693_MUIC_GND_USB_OTG = 0x100,      /* 0x0|      0|     0|    0| */
-       MAX77693_MUIC_GND_USB_OTG_VB = 0x104,   /* 0x0|      1|     0|    0| */
+       MAX77693_MUIC_GND_USB_HOST = 0x100,     /* 0x0|      0|     0|    0| */
+       MAX77693_MUIC_GND_USB_HOST_VB = 0x104,  /* 0x0|      1|     0|    0| */
        MAX77693_MUIC_GND_AV_CABLE_LOAD = 0x102,/* 0x0|      0|     1|    0| */
        MAX77693_MUIC_GND_MHL = 0x103,          /* 0x0|      0|     1|    1| */
        MAX77693_MUIC_GND_MHL_VB = 0x107,       /* 0x0|      1|     1|    1| */
@@ -228,7 +228,7 @@ static const char *max77693_extcon_cable[] = {
        [EXTCON_CABLE_SLOW_CHARGER]             = "Slow-charger",
        [EXTCON_CABLE_CHARGE_DOWNSTREAM]        = "Charge-downstream",
        [EXTCON_CABLE_MHL]                      = "MHL",
-       [EXTCON_CABLE_MHL_TA]                   = "MHL_TA",
+       [EXTCON_CABLE_MHL_TA]                   = "MHL-TA",
        [EXTCON_CABLE_JIG_USB_ON]               = "JIG-USB-ON",
        [EXTCON_CABLE_JIG_USB_OFF]              = "JIG-USB-OFF",
        [EXTCON_CABLE_JIG_UART_OFF]             = "JIG-UART-OFF",
@@ -403,8 +403,8 @@ static int max77693_muic_get_cable_type(struct max77693_muic_info *info,
 
                        /**
                         * [0x1|VBVolt|ADCLow|ADC1K]
-                        * [0x1|     0|     0|    0] USB_OTG
-                        * [0x1|     1|     0|    0] USB_OTG_VB
+                        * [0x1|     0|     0|    0] USB_HOST
+                        * [0x1|     1|     0|    0] USB_HSOT_VB
                         * [0x1|     0|     1|    0] Audio Video cable with load
                         * [0x1|     0|     1|    1] MHL without charging cable
                         * [0x1|     1|     1|    1] MHL with charging cable
@@ -523,7 +523,7 @@ static int max77693_muic_dock_handler(struct max77693_muic_info *info,
                 * - Support charging and data connection through micro-usb port
                 *           if USB cable is connected between target and host
                 *           device.
-                * - Support OTG device (Mouse/Keyboard)
+                * - Support OTG(On-The-Go) device (Ex: Mouse/Keyboard)
                 */
                ret = max77693_muic_set_path(info, info->path_usb, attached);
                if (ret < 0)
@@ -609,9 +609,9 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info)
                                MAX77693_CABLE_GROUP_ADC_GND, &attached);
 
        switch (cable_type_gnd) {
-       case MAX77693_MUIC_GND_USB_OTG:
-       case MAX77693_MUIC_GND_USB_OTG_VB:
-               /* USB_OTG, PATH: AP_USB */
+       case MAX77693_MUIC_GND_USB_HOST:
+       case MAX77693_MUIC_GND_USB_HOST_VB:
+               /* USB_HOST, PATH: AP_USB */
                ret = max77693_muic_set_path(info, CONTROL1_SW_USB, attached);
                if (ret < 0)
                        return ret;
@@ -704,7 +704,7 @@ static int max77693_muic_adc_handler(struct max77693_muic_info *info)
 
        switch (cable_type) {
        case MAX77693_MUIC_ADC_GROUND:
-               /* USB_OTG/MHL/Audio */
+               /* USB_HOST/MHL/Audio */
                max77693_muic_adc_ground_handler(info);
                break;
        case MAX77693_MUIC_ADC_FACTORY_MODE_USB_OFF:
@@ -823,19 +823,19 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
                case MAX77693_MUIC_GND_MHL:
                case MAX77693_MUIC_GND_MHL_VB:
                        /*
-                        * MHL cable with MHL_TA(USB/TA) cable
+                        * MHL cable with MHL-TA(USB/TA) cable
                         * - MHL cable include two port(HDMI line and separate
                         * micro-usb port. When the target connect MHL cable,
-                        * extcon driver check whether MHL_TA(USB/TA) cable is
-                        * connected. If MHL_TA cable is connected, extcon
+                        * extcon driver check whether MHL-TA(USB/TA) cable is
+                        * connected. If MHL-TA cable is connected, extcon
                         * driver notify state to notifiee for charging battery.
                         *
-                        * Features of 'MHL_TA(USB/TA) with MHL cable'
+                        * Features of 'MHL-TA(USB/TA) with MHL cable'
                         * - Support MHL
                         * - Support charging through micro-usb port without
                         *   data connection
                         */
-                       extcon_set_cable_state(info->edev, "MHL_TA", attached);
+                       extcon_set_cable_state(info->edev, "MHL-TA", attached);
                        if (!cable_attached)
                                extcon_set_cable_state(info->edev,
                                                      "MHL", cable_attached);
@@ -886,7 +886,7 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
                         * - Support charging and data connection through micro-
                         *   usb port if USB cable is connected between target
                         *   and host device
-                        * - Support OTG device (Mouse/Keyboard)
+                        * - Support OTG(On-The-Go) device (Ex: Mouse/Keyboard)
                         */
                        ret = max77693_muic_set_path(info, info->path_usb,
                                                    attached);
@@ -1019,8 +1019,6 @@ static void max77693_muic_irq_work(struct work_struct *work)
                dev_err(info->dev, "failed to handle MUIC interrupt\n");
 
        mutex_unlock(&info->mutex);
-
-       return;
 }
 
 static irqreturn_t max77693_muic_irq_handler(int irq, void *data)
@@ -1171,8 +1169,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
                                muic_irq->name, info);
                if (ret) {
                        dev_err(&pdev->dev,
-                               "failed: irq request (IRQ: %d,"
-                               " error :%d)\n",
+                               "failed: irq request (IRQ: %d, error :%d)\n",
                                muic_irq->irq, ret);
                        return ret;
                }
diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c
new file mode 100644 (file)
index 0000000..8db6a92
--- /dev/null
@@ -0,0 +1,881 @@
+/*
+ * extcon-max77843.c - Maxim MAX77843 extcon driver to support
+ *                     MUIC(Micro USB Interface Controller)
+ *
+ * Copyright (C) 2015 Samsung Electronics
+ * Author: Jaewon Kim <jaewon02.kim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/extcon.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/max77843-private.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+
+#define DELAY_MS_DEFAULT               15000   /* unit: millisecond */
+
+enum max77843_muic_status {
+       MAX77843_MUIC_STATUS1 = 0,
+       MAX77843_MUIC_STATUS2,
+       MAX77843_MUIC_STATUS3,
+
+       MAX77843_MUIC_STATUS_NUM,
+};
+
+struct max77843_muic_info {
+       struct device *dev;
+       struct max77843 *max77843;
+       struct extcon_dev *edev;
+
+       struct mutex mutex;
+       struct work_struct irq_work;
+       struct delayed_work wq_detcable;
+
+       u8 status[MAX77843_MUIC_STATUS_NUM];
+       int prev_cable_type;
+       int prev_chg_type;
+       int prev_gnd_type;
+
+       bool irq_adc;
+       bool irq_chg;
+};
+
+enum max77843_muic_cable_group {
+       MAX77843_CABLE_GROUP_ADC = 0,
+       MAX77843_CABLE_GROUP_ADC_GND,
+       MAX77843_CABLE_GROUP_CHG,
+};
+
+enum max77843_muic_adc_debounce_time {
+       MAX77843_DEBOUNCE_TIME_5MS = 0,
+       MAX77843_DEBOUNCE_TIME_10MS,
+       MAX77843_DEBOUNCE_TIME_25MS,
+       MAX77843_DEBOUNCE_TIME_38_62MS,
+};
+
+/* Define accessory cable type */
+enum max77843_muic_accessory_type {
+       MAX77843_MUIC_ADC_GROUND = 0,
+       MAX77843_MUIC_ADC_SEND_END_BUTTON,
+       MAX77843_MUIC_ADC_REMOTE_S1_BUTTON,
+       MAX77843_MUIC_ADC_REMOTE_S2_BUTTON,
+       MAX77843_MUIC_ADC_REMOTE_S3_BUTTON,
+       MAX77843_MUIC_ADC_REMOTE_S4_BUTTON,
+       MAX77843_MUIC_ADC_REMOTE_S5_BUTTON,
+       MAX77843_MUIC_ADC_REMOTE_S6_BUTTON,
+       MAX77843_MUIC_ADC_REMOTE_S7_BUTTON,
+       MAX77843_MUIC_ADC_REMOTE_S8_BUTTON,
+       MAX77843_MUIC_ADC_REMOTE_S9_BUTTON,
+       MAX77843_MUIC_ADC_REMOTE_S10_BUTTON,
+       MAX77843_MUIC_ADC_REMOTE_S11_BUTTON,
+       MAX77843_MUIC_ADC_REMOTE_S12_BUTTON,
+       MAX77843_MUIC_ADC_RESERVED_ACC_1,
+       MAX77843_MUIC_ADC_RESERVED_ACC_2,
+       MAX77843_MUIC_ADC_RESERVED_ACC_3,
+       MAX77843_MUIC_ADC_RESERVED_ACC_4,
+       MAX77843_MUIC_ADC_RESERVED_ACC_5,
+       MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE2,
+       MAX77843_MUIC_ADC_PHONE_POWERED_DEV,
+       MAX77843_MUIC_ADC_TTY_CONVERTER,
+       MAX77843_MUIC_ADC_UART_CABLE,
+       MAX77843_MUIC_ADC_CEA936A_TYPE1_CHG,
+       MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF,
+       MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON,
+       MAX77843_MUIC_ADC_AV_CABLE_NOLOAD,
+       MAX77843_MUIC_ADC_CEA936A_TYPE2_CHG,
+       MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF,
+       MAX77843_MUIC_ADC_FACTORY_MODE_UART_ON,
+       MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE1,
+       MAX77843_MUIC_ADC_OPEN,
+
+       /* The blow accessories should check
+          not only ADC value but also ADC1K and VBVolt value. */
+                                               /* Offset|ADC1K|VBVolt| */
+       MAX77843_MUIC_GND_USB_HOST = 0x100,     /*    0x1|    0|     0| */
+       MAX77843_MUIC_GND_USB_HOST_VB = 0x101,  /*    0x1|    0|     1| */
+       MAX77843_MUIC_GND_MHL = 0x102,          /*    0x1|    1|     0| */
+       MAX77843_MUIC_GND_MHL_VB = 0x103,       /*    0x1|    1|     1| */
+};
+
+/* Define charger cable type */
+enum max77843_muic_charger_type {
+       MAX77843_MUIC_CHG_NONE = 0,
+       MAX77843_MUIC_CHG_USB,
+       MAX77843_MUIC_CHG_DOWNSTREAM,
+       MAX77843_MUIC_CHG_DEDICATED,
+       MAX77843_MUIC_CHG_SPECIAL_500MA,
+       MAX77843_MUIC_CHG_SPECIAL_1A,
+       MAX77843_MUIC_CHG_SPECIAL_BIAS,
+       MAX77843_MUIC_CHG_RESERVED,
+       MAX77843_MUIC_CHG_GND,
+};
+
+enum {
+       MAX77843_CABLE_USB = 0,
+       MAX77843_CABLE_USB_HOST,
+       MAX77843_CABLE_TA,
+       MAX77843_CABLE_CHARGE_DOWNSTREAM,
+       MAX77843_CABLE_FAST_CHARGER,
+       MAX77843_CABLE_SLOW_CHARGER,
+       MAX77843_CABLE_MHL,
+       MAX77843_CABLE_MHL_TA,
+       MAX77843_CABLE_JIG_USB_ON,
+       MAX77843_CABLE_JIG_USB_OFF,
+       MAX77843_CABLE_JIG_UART_ON,
+       MAX77843_CABLE_JIG_UART_OFF,
+
+       MAX77843_CABLE_NUM,
+};
+
+static const char *max77843_extcon_cable[] = {
+       [MAX77843_CABLE_USB]                    = "USB",
+       [MAX77843_CABLE_USB_HOST]               = "USB-HOST",
+       [MAX77843_CABLE_TA]                     = "TA",
+       [MAX77843_CABLE_CHARGE_DOWNSTREAM]      = "CHARGER-DOWNSTREAM",
+       [MAX77843_CABLE_FAST_CHARGER]           = "FAST-CHARGER",
+       [MAX77843_CABLE_SLOW_CHARGER]           = "SLOW-CHARGER",
+       [MAX77843_CABLE_MHL]                    = "MHL",
+       [MAX77843_CABLE_MHL_TA]                 = "MHL-TA",
+       [MAX77843_CABLE_JIG_USB_ON]             = "JIG-USB-ON",
+       [MAX77843_CABLE_JIG_USB_OFF]            = "JIG-USB-OFF",
+       [MAX77843_CABLE_JIG_UART_ON]            = "JIG-UART-ON",
+       [MAX77843_CABLE_JIG_UART_OFF]           = "JIG-UART-OFF",
+};
+
+struct max77843_muic_irq {
+       unsigned int irq;
+       const char *name;
+       unsigned int virq;
+};
+
+static struct max77843_muic_irq max77843_muic_irqs[] = {
+       { MAX77843_MUIC_IRQ_INT1_ADC,           "MUIC-ADC" },
+       { MAX77843_MUIC_IRQ_INT1_ADCERROR,      "MUIC-ADC_ERROR" },
+       { MAX77843_MUIC_IRQ_INT1_ADC1K,         "MUIC-ADC1K" },
+       { MAX77843_MUIC_IRQ_INT2_CHGTYP,        "MUIC-CHGTYP" },
+       { MAX77843_MUIC_IRQ_INT2_CHGDETRUN,     "MUIC-CHGDETRUN" },
+       { MAX77843_MUIC_IRQ_INT2_DCDTMR,        "MUIC-DCDTMR" },
+       { MAX77843_MUIC_IRQ_INT2_DXOVP,         "MUIC-DXOVP" },
+       { MAX77843_MUIC_IRQ_INT2_VBVOLT,        "MUIC-VBVOLT" },
+       { MAX77843_MUIC_IRQ_INT3_VBADC,         "MUIC-VBADC" },
+       { MAX77843_MUIC_IRQ_INT3_VDNMON,        "MUIC-VDNMON" },
+       { MAX77843_MUIC_IRQ_INT3_DNRES,         "MUIC-DNRES" },
+       { MAX77843_MUIC_IRQ_INT3_MPNACK,        "MUIC-MPNACK"},
+       { MAX77843_MUIC_IRQ_INT3_MRXBUFOW,      "MUIC-MRXBUFOW"},
+       { MAX77843_MUIC_IRQ_INT3_MRXTRF,        "MUIC-MRXTRF"},
+       { MAX77843_MUIC_IRQ_INT3_MRXPERR,       "MUIC-MRXPERR"},
+       { MAX77843_MUIC_IRQ_INT3_MRXRDY,        "MUIC-MRXRDY"},
+};
+
+static const struct regmap_config max77843_muic_regmap_config = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
+       .max_register   = MAX77843_MUIC_REG_END,
+};
+
+static const struct regmap_irq max77843_muic_irq[] = {
+       /* INT1 interrupt */
+       { .reg_offset = 0, .mask = MAX77843_MUIC_ADC, },
+       { .reg_offset = 0, .mask = MAX77843_MUIC_ADCERROR, },
+       { .reg_offset = 0, .mask = MAX77843_MUIC_ADC1K, },
+
+       /* INT2 interrupt */
+       { .reg_offset = 1, .mask = MAX77843_MUIC_CHGTYP, },
+       { .reg_offset = 1, .mask = MAX77843_MUIC_CHGDETRUN, },
+       { .reg_offset = 1, .mask = MAX77843_MUIC_DCDTMR, },
+       { .reg_offset = 1, .mask = MAX77843_MUIC_DXOVP, },
+       { .reg_offset = 1, .mask = MAX77843_MUIC_VBVOLT, },
+
+       /* INT3 interrupt */
+       { .reg_offset = 2, .mask = MAX77843_MUIC_VBADC, },
+       { .reg_offset = 2, .mask = MAX77843_MUIC_VDNMON, },
+       { .reg_offset = 2, .mask = MAX77843_MUIC_DNRES, },
+       { .reg_offset = 2, .mask = MAX77843_MUIC_MPNACK, },
+       { .reg_offset = 2, .mask = MAX77843_MUIC_MRXBUFOW, },
+       { .reg_offset = 2, .mask = MAX77843_MUIC_MRXTRF, },
+       { .reg_offset = 2, .mask = MAX77843_MUIC_MRXPERR, },
+       { .reg_offset = 2, .mask = MAX77843_MUIC_MRXRDY, },
+};
+
+static const struct regmap_irq_chip max77843_muic_irq_chip = {
+       .name           = "max77843-muic",
+       .status_base    = MAX77843_MUIC_REG_INT1,
+       .mask_base      = MAX77843_MUIC_REG_INTMASK1,
+       .mask_invert    = true,
+       .num_regs       = 3,
+       .irqs           = max77843_muic_irq,
+       .num_irqs       = ARRAY_SIZE(max77843_muic_irq),
+};
+
+static int max77843_muic_set_path(struct max77843_muic_info *info,
+               u8 val, bool attached)
+{
+       struct max77843 *max77843 = info->max77843;
+       int ret = 0;
+       unsigned int ctrl1, ctrl2;
+
+       if (attached)
+               ctrl1 = val;
+       else
+               ctrl1 = CONTROL1_SW_OPEN;
+
+       ret = regmap_update_bits(max77843->regmap_muic,
+                       MAX77843_MUIC_REG_CONTROL1,
+                       CONTROL1_COM_SW, ctrl1);
+       if (ret < 0) {
+               dev_err(info->dev, "Cannot switch MUIC port\n");
+               return ret;
+       }
+
+       if (attached)
+               ctrl2 = MAX77843_MUIC_CONTROL2_CPEN_MASK;
+       else
+               ctrl2 = MAX77843_MUIC_CONTROL2_LOWPWR_MASK;
+
+       ret = regmap_update_bits(max77843->regmap_muic,
+                       MAX77843_MUIC_REG_CONTROL2,
+                       MAX77843_MUIC_CONTROL2_LOWPWR_MASK |
+                       MAX77843_MUIC_CONTROL2_CPEN_MASK, ctrl2);
+       if (ret < 0) {
+               dev_err(info->dev, "Cannot update lowpower mode\n");
+               return ret;
+       }
+
+       dev_dbg(info->dev,
+               "CONTROL1 : 0x%02x, CONTROL2 : 0x%02x, state : %s\n",
+               ctrl1, ctrl2, attached ? "attached" : "detached");
+
+       return 0;
+}
+
+static int max77843_muic_get_cable_type(struct max77843_muic_info *info,
+               enum max77843_muic_cable_group group, bool *attached)
+{
+       int adc, chg_type, cable_type, gnd_type;
+
+       adc = info->status[MAX77843_MUIC_STATUS1] &
+                       MAX77843_MUIC_STATUS1_ADC_MASK;
+       adc >>= STATUS1_ADC_SHIFT;
+
+       switch (group) {
+       case MAX77843_CABLE_GROUP_ADC:
+               if (adc == MAX77843_MUIC_ADC_OPEN) {
+                       *attached = false;
+                       cable_type = info->prev_cable_type;
+                       info->prev_cable_type = MAX77843_MUIC_ADC_OPEN;
+               } else {
+                       *attached = true;
+                       cable_type = info->prev_cable_type = adc;
+               }
+               break;
+       case MAX77843_CABLE_GROUP_CHG:
+               chg_type = info->status[MAX77843_MUIC_STATUS2] &
+                               MAX77843_MUIC_STATUS2_CHGTYP_MASK;
+
+               /* Check GROUND accessory with charger cable */
+               if (adc == MAX77843_MUIC_ADC_GROUND) {
+                       if (chg_type == MAX77843_MUIC_CHG_NONE) {
+                               /* The following state when charger cable is
+                                * disconnected but the GROUND accessory still
+                                * connected */
+                               *attached = false;
+                               cable_type = info->prev_chg_type;
+                               info->prev_chg_type = MAX77843_MUIC_CHG_NONE;
+                       } else {
+
+                               /* The following state when charger cable is
+                                * connected on the GROUND accessory */
+                               *attached = true;
+                               cable_type = MAX77843_MUIC_CHG_GND;
+                               info->prev_chg_type = MAX77843_MUIC_CHG_GND;
+                       }
+                       break;
+               }
+
+               if (chg_type == MAX77843_MUIC_CHG_NONE) {
+                       *attached = false;
+                       cable_type = info->prev_chg_type;
+                       info->prev_chg_type = MAX77843_MUIC_CHG_NONE;
+               } else {
+                       *attached = true;
+                       cable_type = info->prev_chg_type = chg_type;
+               }
+               break;
+       case MAX77843_CABLE_GROUP_ADC_GND:
+               if (adc == MAX77843_MUIC_ADC_OPEN) {
+                       *attached = false;
+                       cable_type = info->prev_gnd_type;
+                       info->prev_gnd_type = MAX77843_MUIC_ADC_OPEN;
+               } else {
+                       *attached = true;
+
+                       /* Offset|ADC1K|VBVolt|
+                        *    0x1|    0|     0| USB-HOST
+                        *    0x1|    0|     1| USB-HOST with VB
+                        *    0x1|    1|     0| MHL
+                        *    0x1|    1|     1| MHL with VB */
+                       /* Get ADC1K register bit */
+                       gnd_type = (info->status[MAX77843_MUIC_STATUS1] &
+                                       MAX77843_MUIC_STATUS1_ADC1K_MASK);
+
+                       /* Get VBVolt register bit */
+                       gnd_type |= (info->status[MAX77843_MUIC_STATUS2] &
+                                       MAX77843_MUIC_STATUS2_VBVOLT_MASK);
+                       gnd_type >>= STATUS2_VBVOLT_SHIFT;
+
+                       /* Offset of GND cable */
+                       gnd_type |= MAX77843_MUIC_GND_USB_HOST;
+                       cable_type = info->prev_gnd_type = gnd_type;
+               }
+               break;
+       default:
+               dev_err(info->dev, "Unknown cable group (%d)\n", group);
+               cable_type = -EINVAL;
+               break;
+       }
+
+       return cable_type;
+}
+
+static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info)
+{
+       int ret, gnd_cable_type;
+       bool attached;
+
+       gnd_cable_type = max77843_muic_get_cable_type(info,
+                       MAX77843_CABLE_GROUP_ADC_GND, &attached);
+       dev_dbg(info->dev, "external connector is %s (gnd:0x%02x)\n",
+                       attached ? "attached" : "detached", gnd_cable_type);
+
+       switch (gnd_cable_type) {
+       case MAX77843_MUIC_GND_USB_HOST:
+       case MAX77843_MUIC_GND_USB_HOST_VB:
+               ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached);
+               if (ret < 0)
+                       return ret;
+
+               extcon_set_cable_state(info->edev, "USB-HOST", attached);
+               break;
+       case MAX77843_MUIC_GND_MHL_VB:
+       case MAX77843_MUIC_GND_MHL:
+               ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
+               if (ret < 0)
+                       return ret;
+
+               extcon_set_cable_state(info->edev, "MHL", attached);
+               break;
+       default:
+               dev_err(info->dev, "failed to detect %s accessory(gnd:0x%x)\n",
+                       attached ? "attached" : "detached", gnd_cable_type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int max77843_muic_jig_handler(struct max77843_muic_info *info,
+               int cable_type, bool attached)
+{
+       int ret;
+
+       dev_dbg(info->dev, "external connector is %s (adc:0x%02x)\n",
+                       attached ? "attached" : "detached", cable_type);
+
+       switch (cable_type) {
+       case MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF:
+               ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached);
+               if (ret < 0)
+                       return ret;
+               extcon_set_cable_state(info->edev, "JIG-USB-OFF", attached);
+               break;
+       case MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON:
+               ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached);
+               if (ret < 0)
+                       return ret;
+               extcon_set_cable_state(info->edev, "JIG-USB-ON", attached);
+               break;
+       case MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF:
+               ret = max77843_muic_set_path(info, CONTROL1_SW_UART, attached);
+               if (ret < 0)
+                       return ret;
+               extcon_set_cable_state(info->edev, "JIG-UART-OFF", attached);
+               break;
+       default:
+               ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
+               if (ret < 0)
+                       return ret;
+               break;
+       }
+
+       return 0;
+}
+
+static int max77843_muic_adc_handler(struct max77843_muic_info *info)
+{
+       int ret, cable_type;
+       bool attached;
+
+       cable_type = max77843_muic_get_cable_type(info,
+                       MAX77843_CABLE_GROUP_ADC, &attached);
+
+       dev_dbg(info->dev,
+               "external connector is %s (adc:0x%02x, prev_adc:0x%x)\n",
+               attached ? "attached" : "detached", cable_type,
+               info->prev_cable_type);
+
+       switch (cable_type) {
+       case MAX77843_MUIC_ADC_GROUND:
+               ret = max77843_muic_adc_gnd_handler(info);
+               if (ret < 0)
+                       return ret;
+               break;
+       case MAX77843_MUIC_ADC_FACTORY_MODE_USB_OFF:
+       case MAX77843_MUIC_ADC_FACTORY_MODE_USB_ON:
+       case MAX77843_MUIC_ADC_FACTORY_MODE_UART_OFF:
+               ret = max77843_muic_jig_handler(info, cable_type, attached);
+               if (ret < 0)
+                       return ret;
+               break;
+       case MAX77843_MUIC_ADC_SEND_END_BUTTON:
+       case MAX77843_MUIC_ADC_REMOTE_S1_BUTTON:
+       case MAX77843_MUIC_ADC_REMOTE_S2_BUTTON:
+       case MAX77843_MUIC_ADC_REMOTE_S3_BUTTON:
+       case MAX77843_MUIC_ADC_REMOTE_S4_BUTTON:
+       case MAX77843_MUIC_ADC_REMOTE_S5_BUTTON:
+       case MAX77843_MUIC_ADC_REMOTE_S6_BUTTON:
+       case MAX77843_MUIC_ADC_REMOTE_S7_BUTTON:
+       case MAX77843_MUIC_ADC_REMOTE_S8_BUTTON:
+       case MAX77843_MUIC_ADC_REMOTE_S9_BUTTON:
+       case MAX77843_MUIC_ADC_REMOTE_S10_BUTTON:
+       case MAX77843_MUIC_ADC_REMOTE_S11_BUTTON:
+       case MAX77843_MUIC_ADC_REMOTE_S12_BUTTON:
+       case MAX77843_MUIC_ADC_RESERVED_ACC_1:
+       case MAX77843_MUIC_ADC_RESERVED_ACC_2:
+       case MAX77843_MUIC_ADC_RESERVED_ACC_3:
+       case MAX77843_MUIC_ADC_RESERVED_ACC_4:
+       case MAX77843_MUIC_ADC_RESERVED_ACC_5:
+       case MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE2:
+       case MAX77843_MUIC_ADC_PHONE_POWERED_DEV:
+       case MAX77843_MUIC_ADC_TTY_CONVERTER:
+       case MAX77843_MUIC_ADC_UART_CABLE:
+       case MAX77843_MUIC_ADC_CEA936A_TYPE1_CHG:
+       case MAX77843_MUIC_ADC_AV_CABLE_NOLOAD:
+       case MAX77843_MUIC_ADC_CEA936A_TYPE2_CHG:
+       case MAX77843_MUIC_ADC_FACTORY_MODE_UART_ON:
+       case MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE1:
+       case MAX77843_MUIC_ADC_OPEN:
+               dev_err(info->dev,
+                       "accessory is %s but it isn't used (adc:0x%x)\n",
+                       attached ? "attached" : "detached", cable_type);
+               return -EAGAIN;
+       default:
+               dev_err(info->dev,
+                       "failed to detect %s accessory (adc:0x%x)\n",
+                       attached ? "attached" : "detached", cable_type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int max77843_muic_chg_handler(struct max77843_muic_info *info)
+{
+       int ret, chg_type, gnd_type;
+       bool attached;
+
+       chg_type = max77843_muic_get_cable_type(info,
+                       MAX77843_CABLE_GROUP_CHG, &attached);
+
+       dev_dbg(info->dev,
+               "external connector is %s(chg_type:0x%x, prev_chg_type:0x%x)\n",
+               attached ? "attached" : "detached",
+               chg_type, info->prev_chg_type);
+
+       switch (chg_type) {
+       case MAX77843_MUIC_CHG_USB:
+               ret = max77843_muic_set_path(info, CONTROL1_SW_USB, attached);
+               if (ret < 0)
+                       return ret;
+
+               extcon_set_cable_state(info->edev, "USB", attached);
+               break;
+       case MAX77843_MUIC_CHG_DOWNSTREAM:
+               ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
+               if (ret < 0)
+                       return ret;
+
+               extcon_set_cable_state(info->edev,
+                               "CHARGER-DOWNSTREAM", attached);
+               break;
+       case MAX77843_MUIC_CHG_DEDICATED:
+               ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
+               if (ret < 0)
+                       return ret;
+
+               extcon_set_cable_state(info->edev, "TA", attached);
+               break;
+       case MAX77843_MUIC_CHG_SPECIAL_500MA:
+               ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
+               if (ret < 0)
+                       return ret;
+
+               extcon_set_cable_state(info->edev, "SLOW-CHAREGER", attached);
+               break;
+       case MAX77843_MUIC_CHG_SPECIAL_1A:
+               ret = max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
+               if (ret < 0)
+                       return ret;
+
+               extcon_set_cable_state(info->edev, "FAST-CHARGER", attached);
+               break;
+       case MAX77843_MUIC_CHG_GND:
+               gnd_type = max77843_muic_get_cable_type(info,
+                               MAX77843_CABLE_GROUP_ADC_GND, &attached);
+
+               /* Charger cable on MHL accessory is attach or detach */
+               if (gnd_type == MAX77843_MUIC_GND_MHL_VB)
+                       extcon_set_cable_state(info->edev, "MHL-TA", true);
+               else if (gnd_type == MAX77843_MUIC_GND_MHL)
+                       extcon_set_cable_state(info->edev, "MHL-TA", false);
+               break;
+       case MAX77843_MUIC_CHG_NONE:
+               break;
+       default:
+               dev_err(info->dev,
+                       "failed to detect %s accessory (chg_type:0x%x)\n",
+                       attached ? "attached" : "detached", chg_type);
+
+               max77843_muic_set_path(info, CONTROL1_SW_OPEN, attached);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void max77843_muic_irq_work(struct work_struct *work)
+{
+       struct max77843_muic_info *info = container_of(work,
+                       struct max77843_muic_info, irq_work);
+       struct max77843 *max77843 = info->max77843;
+       int ret = 0;
+
+       mutex_lock(&info->mutex);
+
+       ret = regmap_bulk_read(max77843->regmap_muic,
+                       MAX77843_MUIC_REG_STATUS1, info->status,
+                       MAX77843_MUIC_STATUS_NUM);
+       if (ret) {
+               dev_err(info->dev, "Cannot read STATUS registers\n");
+               mutex_unlock(&info->mutex);
+               return;
+       }
+
+       if (info->irq_adc) {
+               ret = max77843_muic_adc_handler(info);
+               if (ret)
+                       dev_err(info->dev, "Unknown cable type\n");
+               info->irq_adc = false;
+       }
+
+       if (info->irq_chg) {
+               ret = max77843_muic_chg_handler(info);
+               if (ret)
+                       dev_err(info->dev, "Unknown charger type\n");
+               info->irq_chg = false;
+       }
+
+       mutex_unlock(&info->mutex);
+}
+
+static irqreturn_t max77843_muic_irq_handler(int irq, void *data)
+{
+       struct max77843_muic_info *info = data;
+       int i, irq_type = -1;
+
+       for (i = 0; i < ARRAY_SIZE(max77843_muic_irqs); i++)
+               if (irq == max77843_muic_irqs[i].virq)
+                       irq_type = max77843_muic_irqs[i].irq;
+
+       switch (irq_type) {
+       case MAX77843_MUIC_IRQ_INT1_ADC:
+       case MAX77843_MUIC_IRQ_INT1_ADCERROR:
+       case MAX77843_MUIC_IRQ_INT1_ADC1K:
+               info->irq_adc = true;
+               break;
+       case MAX77843_MUIC_IRQ_INT2_CHGTYP:
+       case MAX77843_MUIC_IRQ_INT2_CHGDETRUN:
+       case MAX77843_MUIC_IRQ_INT2_DCDTMR:
+       case MAX77843_MUIC_IRQ_INT2_DXOVP:
+       case MAX77843_MUIC_IRQ_INT2_VBVOLT:
+               info->irq_chg = true;
+               break;
+       case MAX77843_MUIC_IRQ_INT3_VBADC:
+       case MAX77843_MUIC_IRQ_INT3_VDNMON:
+       case MAX77843_MUIC_IRQ_INT3_DNRES:
+       case MAX77843_MUIC_IRQ_INT3_MPNACK:
+       case MAX77843_MUIC_IRQ_INT3_MRXBUFOW:
+       case MAX77843_MUIC_IRQ_INT3_MRXTRF:
+       case MAX77843_MUIC_IRQ_INT3_MRXPERR:
+       case MAX77843_MUIC_IRQ_INT3_MRXRDY:
+               break;
+       default:
+               dev_err(info->dev, "Cannot recognize IRQ(%d)\n", irq_type);
+               break;
+       }
+
+       schedule_work(&info->irq_work);
+
+       return IRQ_HANDLED;
+}
+
+static void max77843_muic_detect_cable_wq(struct work_struct *work)
+{
+       struct max77843_muic_info *info = container_of(to_delayed_work(work),
+                       struct max77843_muic_info, wq_detcable);
+       struct max77843 *max77843 = info->max77843;
+       int chg_type, adc, ret;
+       bool attached;
+
+       mutex_lock(&info->mutex);
+
+       ret = regmap_bulk_read(max77843->regmap_muic,
+                       MAX77843_MUIC_REG_STATUS1, info->status,
+                       MAX77843_MUIC_STATUS_NUM);
+       if (ret) {
+               dev_err(info->dev, "Cannot read STATUS registers\n");
+               goto err_cable_wq;
+       }
+
+       adc = max77843_muic_get_cable_type(info,
+                       MAX77843_CABLE_GROUP_ADC, &attached);
+       if (attached && adc != MAX77843_MUIC_ADC_OPEN) {
+               ret = max77843_muic_adc_handler(info);
+               if (ret < 0) {
+                       dev_err(info->dev, "Cannot detect accessory\n");
+                       goto err_cable_wq;
+               }
+       }
+
+       chg_type = max77843_muic_get_cable_type(info,
+                       MAX77843_CABLE_GROUP_CHG, &attached);
+       if (attached && chg_type != MAX77843_MUIC_CHG_NONE) {
+               ret = max77843_muic_chg_handler(info);
+               if (ret < 0) {
+                       dev_err(info->dev, "Cannot detect charger accessory\n");
+                       goto err_cable_wq;
+               }
+       }
+
+err_cable_wq:
+       mutex_unlock(&info->mutex);
+}
+
+static int max77843_muic_set_debounce_time(struct max77843_muic_info *info,
+               enum max77843_muic_adc_debounce_time time)
+{
+       struct max77843 *max77843 = info->max77843;
+       int ret;
+
+       switch (time) {
+       case MAX77843_DEBOUNCE_TIME_5MS:
+       case MAX77843_DEBOUNCE_TIME_10MS:
+       case MAX77843_DEBOUNCE_TIME_25MS:
+       case MAX77843_DEBOUNCE_TIME_38_62MS:
+               ret = regmap_update_bits(max77843->regmap_muic,
+                               MAX77843_MUIC_REG_CONTROL4,
+                               MAX77843_MUIC_CONTROL4_ADCDBSET_MASK,
+                               time << CONTROL4_ADCDBSET_SHIFT);
+               if (ret < 0) {
+                       dev_err(info->dev, "Cannot write MUIC regmap\n");
+                       return ret;
+               }
+               break;
+       default:
+               dev_err(info->dev, "Invalid ADC debounce time\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int max77843_init_muic_regmap(struct max77843 *max77843)
+{
+       int ret;
+
+       max77843->i2c_muic = i2c_new_dummy(max77843->i2c->adapter,
+                       I2C_ADDR_MUIC);
+       if (!max77843->i2c_muic) {
+               dev_err(&max77843->i2c->dev,
+                               "Cannot allocate I2C device for MUIC\n");
+               return -ENOMEM;
+       }
+
+       i2c_set_clientdata(max77843->i2c_muic, max77843);
+
+       max77843->regmap_muic = devm_regmap_init_i2c(max77843->i2c_muic,
+                       &max77843_muic_regmap_config);
+       if (IS_ERR(max77843->regmap_muic)) {
+               ret = PTR_ERR(max77843->regmap_muic);
+               goto err_muic_i2c;
+       }
+
+       ret = regmap_add_irq_chip(max77843->regmap_muic, max77843->irq,
+                       IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_SHARED,
+                       0, &max77843_muic_irq_chip, &max77843->irq_data_muic);
+       if (ret < 0) {
+               dev_err(&max77843->i2c->dev, "Cannot add MUIC IRQ chip\n");
+               goto err_muic_i2c;
+       }
+
+       return 0;
+
+err_muic_i2c:
+       i2c_unregister_device(max77843->i2c_muic);
+
+       return ret;
+}
+
+static int max77843_muic_probe(struct platform_device *pdev)
+{
+       struct max77843 *max77843 = dev_get_drvdata(pdev->dev.parent);
+       struct max77843_muic_info *info;
+       unsigned int id;
+       int i, ret;
+
+       info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->dev = &pdev->dev;
+       info->max77843 = max77843;
+
+       platform_set_drvdata(pdev, info);
+       mutex_init(&info->mutex);
+
+       /* Initialize i2c and regmap */
+       ret = max77843_init_muic_regmap(max77843);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to init MUIC regmap\n");
+               return ret;
+       }
+
+       /* Turn off auto detection configuration */
+       ret = regmap_update_bits(max77843->regmap_muic,
+                       MAX77843_MUIC_REG_CONTROL4,
+                       MAX77843_MUIC_CONTROL4_USBAUTO_MASK |
+                       MAX77843_MUIC_CONTROL4_FCTAUTO_MASK,
+                       CONTROL4_AUTO_DISABLE);
+
+       /* Initialize extcon device */
+       info->edev = devm_extcon_dev_allocate(&pdev->dev,
+                       max77843_extcon_cable);
+       if (IS_ERR(info->edev)) {
+               dev_err(&pdev->dev, "Failed to allocate memory for extcon\n");
+               ret = -ENODEV;
+               goto err_muic_irq;
+       }
+
+       ret = devm_extcon_dev_register(&pdev->dev, info->edev);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register extcon device\n");
+               goto err_muic_irq;
+       }
+
+       /* Set ADC debounce time */
+       max77843_muic_set_debounce_time(info, MAX77843_DEBOUNCE_TIME_25MS);
+
+       /* Set initial path for UART */
+       max77843_muic_set_path(info, CONTROL1_SW_UART, true);
+
+       /* Check revision number of MUIC device */
+       ret = regmap_read(max77843->regmap_muic, MAX77843_MUIC_REG_ID, &id);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to read revision number\n");
+               goto err_muic_irq;
+       }
+       dev_info(info->dev, "MUIC device ID : 0x%x\n", id);
+
+       /* Support virtual irq domain for max77843 MUIC device */
+       INIT_WORK(&info->irq_work, max77843_muic_irq_work);
+
+       for (i = 0; i < ARRAY_SIZE(max77843_muic_irqs); i++) {
+               struct max77843_muic_irq *muic_irq = &max77843_muic_irqs[i];
+               unsigned int virq = 0;
+
+               virq = regmap_irq_get_virq(max77843->irq_data_muic,
+                               muic_irq->irq);
+               if (virq <= 0) {
+                       ret = -EINVAL;
+                       goto err_muic_irq;
+               }
+               muic_irq->virq = virq;
+
+               ret = devm_request_threaded_irq(&pdev->dev, virq, NULL,
+                               max77843_muic_irq_handler, IRQF_NO_SUSPEND,
+                               muic_irq->name, info);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "Failed to request irq (IRQ: %d, error: %d)\n",
+                               muic_irq->irq, ret);
+                       goto err_muic_irq;
+               }
+       }
+
+       /* Detect accessory after completing the initialization of platform */
+       INIT_DELAYED_WORK(&info->wq_detcable, max77843_muic_detect_cable_wq);
+       queue_delayed_work(system_power_efficient_wq,
+                       &info->wq_detcable, msecs_to_jiffies(DELAY_MS_DEFAULT));
+
+       return 0;
+
+err_muic_irq:
+       regmap_del_irq_chip(max77843->irq, max77843->irq_data_muic);
+       i2c_unregister_device(max77843->i2c_muic);
+
+       return ret;
+}
+
+static int max77843_muic_remove(struct platform_device *pdev)
+{
+       struct max77843_muic_info *info = platform_get_drvdata(pdev);
+       struct max77843 *max77843 = info->max77843;
+
+       cancel_work_sync(&info->irq_work);
+       regmap_del_irq_chip(max77843->irq, max77843->irq_data_muic);
+       i2c_unregister_device(max77843->i2c_muic);
+
+       return 0;
+}
+
+static const struct platform_device_id max77843_muic_id[] = {
+       { "max77843-muic", },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, max77843_muic_id);
+
+static struct platform_driver max77843_muic_driver = {
+       .driver         = {
+               .name           = "max77843-muic",
+       },
+       .probe          = max77843_muic_probe,
+       .remove         = max77843_muic_remove,
+       .id_table       = max77843_muic_id,
+};
+
+static int __init max77843_muic_init(void)
+{
+       return platform_driver_register(&max77843_muic_driver);
+}
+subsys_initcall(max77843_muic_init);
+
+MODULE_DESCRIPTION("Maxim MAX77843 Extcon driver");
+MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
+MODULE_LICENSE("GPL");
index fc1678fa95c4cd258401a1eee871c8ed8ee6f59f..5774e56c64222a43f21eb28e9baeecc79bc176e1 100644 (file)
@@ -579,8 +579,6 @@ static void max8997_muic_irq_work(struct work_struct *work)
                dev_err(info->dev, "failed to handle MUIC interrupt\n");
 
        mutex_unlock(&info->mutex);
-
-       return;
 }
 
 static irqreturn_t max8997_muic_irq_handler(int irq, void *data)
@@ -689,8 +687,7 @@ static int max8997_muic_probe(struct platform_device *pdev)
                                muic_irq->name, info);
                if (ret) {
                        dev_err(&pdev->dev,
-                               "failed: irq request (IRQ: %d,"
-                               " error :%d)\n",
+                               "failed: irq request (IRQ: %d, error :%d)\n",
                                muic_irq->irq, ret);
                        goto err_irq;
                }
index a784b2d5ee72b57fb1537438a661c7ead9d88f15..9ccd5af89d1c058bab1564340f63a337a7d17c47 100644 (file)
@@ -582,10 +582,8 @@ static int rt8973a_muic_i2c_probe(struct i2c_client *i2c,
                return -EINVAL;
 
        info = devm_kzalloc(&i2c->dev, sizeof(*info), GFP_KERNEL);
-       if (!info) {
-               dev_err(&i2c->dev, "failed to allocate memory\n");
+       if (!info)
                return -ENOMEM;
-       }
        i2c_set_clientdata(i2c, info);
 
        info->dev = &i2c->dev;
@@ -681,7 +679,7 @@ static int rt8973a_muic_i2c_remove(struct i2c_client *i2c)
        return 0;
 }
 
-static struct of_device_id rt8973a_dt_match[] = {
+static const struct of_device_id rt8973a_dt_match[] = {
        { .compatible = "richtek,rt8973a-muic" },
        { },
 };
index b0f7bd82af90436d7e433a7350f80195f57d7e52..2f93cf3078521e7c2f635ed9320b1d7984a383e5 100644 (file)
@@ -359,8 +359,8 @@ static unsigned int sm5502_muic_get_cable_type(struct sm5502_muic_info *info)
                        break;
                default:
                        dev_dbg(info->dev,
-                               "cannot identify the cable type: adc(0x%x) "
-                               "dev_type1(0x%x)\n", adc, dev_type1);
+                               "cannot identify the cable type: adc(0x%x)\n",
+                               adc);
                        return -EINVAL;
                };
                break;
@@ -659,7 +659,7 @@ static int sm5502_muic_i2c_remove(struct i2c_client *i2c)
        return 0;
 }
 
-static struct of_device_id sm5502_dt_match[] = {
+static const struct of_device_id sm5502_dt_match[] = {
        { .compatible = "siliconmitus,sm5502-muic" },
        { },
 };
diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c
new file mode 100644 (file)
index 0000000..de67fce
--- /dev/null
@@ -0,0 +1,237 @@
+/**
+ * drivers/extcon/extcon-usb-gpio.c - USB GPIO extcon driver
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Roger Quadros <rogerq@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/extcon.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#define USB_GPIO_DEBOUNCE_MS   20      /* ms */
+
+struct usb_extcon_info {
+       struct device *dev;
+       struct extcon_dev *edev;
+
+       struct gpio_desc *id_gpiod;
+       int id_irq;
+
+       unsigned long debounce_jiffies;
+       struct delayed_work wq_detcable;
+};
+
+/* List of detectable cables */
+enum {
+       EXTCON_CABLE_USB = 0,
+       EXTCON_CABLE_USB_HOST,
+
+       EXTCON_CABLE_END,
+};
+
+static const char *usb_extcon_cable[] = {
+       [EXTCON_CABLE_USB] = "USB",
+       [EXTCON_CABLE_USB_HOST] = "USB-HOST",
+       NULL,
+};
+
+static void usb_extcon_detect_cable(struct work_struct *work)
+{
+       int id;
+       struct usb_extcon_info *info = container_of(to_delayed_work(work),
+                                                   struct usb_extcon_info,
+                                                   wq_detcable);
+
+       /* check ID and update cable state */
+       id = gpiod_get_value_cansleep(info->id_gpiod);
+       if (id) {
+               /*
+                * ID = 1 means USB HOST cable detached.
+                * As we don't have event for USB peripheral cable attached,
+                * we simulate USB peripheral attach here.
+                */
+               extcon_set_cable_state(info->edev,
+                                      usb_extcon_cable[EXTCON_CABLE_USB_HOST],
+                                      false);
+               extcon_set_cable_state(info->edev,
+                                      usb_extcon_cable[EXTCON_CABLE_USB],
+                                      true);
+       } else {
+               /*
+                * ID = 0 means USB HOST cable attached.
+                * As we don't have event for USB peripheral cable detached,
+                * we simulate USB peripheral detach here.
+                */
+               extcon_set_cable_state(info->edev,
+                                      usb_extcon_cable[EXTCON_CABLE_USB],
+                                      false);
+               extcon_set_cable_state(info->edev,
+                                      usb_extcon_cable[EXTCON_CABLE_USB_HOST],
+                                      true);
+       }
+}
+
+static irqreturn_t usb_irq_handler(int irq, void *dev_id)
+{
+       struct usb_extcon_info *info = dev_id;
+
+       queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
+                          info->debounce_jiffies);
+
+       return IRQ_HANDLED;
+}
+
+static int usb_extcon_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct usb_extcon_info *info;
+       int ret;
+
+       if (!np)
+               return -EINVAL;
+
+       info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->dev = dev;
+       info->id_gpiod = devm_gpiod_get(&pdev->dev, "id");
+       if (IS_ERR(info->id_gpiod)) {
+               dev_err(dev, "failed to get ID GPIO\n");
+               return PTR_ERR(info->id_gpiod);
+       }
+
+       ret = gpiod_set_debounce(info->id_gpiod,
+                                USB_GPIO_DEBOUNCE_MS * 1000);
+       if (ret < 0)
+               info->debounce_jiffies = msecs_to_jiffies(USB_GPIO_DEBOUNCE_MS);
+
+       INIT_DELAYED_WORK(&info->wq_detcable, usb_extcon_detect_cable);
+
+       info->id_irq = gpiod_to_irq(info->id_gpiod);
+       if (info->id_irq < 0) {
+               dev_err(dev, "failed to get ID IRQ\n");
+               return info->id_irq;
+       }
+
+       ret = devm_request_threaded_irq(dev, info->id_irq, NULL,
+                                       usb_irq_handler,
+                                       IRQF_TRIGGER_RISING |
+                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                       pdev->name, info);
+       if (ret < 0) {
+               dev_err(dev, "failed to request handler for ID IRQ\n");
+               return ret;
+       }
+
+       info->edev = devm_extcon_dev_allocate(dev, usb_extcon_cable);
+       if (IS_ERR(info->edev)) {
+               dev_err(dev, "failed to allocate extcon device\n");
+               return -ENOMEM;
+       }
+
+       ret = devm_extcon_dev_register(dev, info->edev);
+       if (ret < 0) {
+               dev_err(dev, "failed to register extcon device\n");
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, info);
+       device_init_wakeup(dev, 1);
+
+       /* Perform initial detection */
+       usb_extcon_detect_cable(&info->wq_detcable.work);
+
+       return 0;
+}
+
+static int usb_extcon_remove(struct platform_device *pdev)
+{
+       struct usb_extcon_info *info = platform_get_drvdata(pdev);
+
+       cancel_delayed_work_sync(&info->wq_detcable);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int usb_extcon_suspend(struct device *dev)
+{
+       struct usb_extcon_info *info = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (device_may_wakeup(dev)) {
+               ret = enable_irq_wake(info->id_irq);
+               if (ret)
+                       return ret;
+       }
+
+       /*
+        * We don't want to process any IRQs after this point
+        * as GPIOs used behind I2C subsystem might not be
+        * accessible until resume completes. So disable IRQ.
+        */
+       disable_irq(info->id_irq);
+
+       return ret;
+}
+
+static int usb_extcon_resume(struct device *dev)
+{
+       struct usb_extcon_info *info = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (device_may_wakeup(dev)) {
+               ret = disable_irq_wake(info->id_irq);
+               if (ret)
+                       return ret;
+       }
+
+       enable_irq(info->id_irq);
+
+       return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(usb_extcon_pm_ops,
+                        usb_extcon_suspend, usb_extcon_resume);
+
+static const struct of_device_id usb_extcon_dt_match[] = {
+       { .compatible = "linux,extcon-usb-gpio", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, usb_extcon_dt_match);
+
+static struct platform_driver usb_extcon_driver = {
+       .probe          = usb_extcon_probe,
+       .remove         = usb_extcon_remove,
+       .driver         = {
+               .name   = "extcon-usb-gpio",
+               .pm     = &usb_extcon_pm_ops,
+               .of_match_table = usb_extcon_dt_match,
+       },
+};
+
+module_platform_driver(usb_extcon_driver);
+
+MODULE_AUTHOR("Roger Quadros <rogerq@ti.com>");
+MODULE_DESCRIPTION("USB GPIO extcon driver");
+MODULE_LICENSE("GPL v2");
similarity index 97%
rename from drivers/extcon/extcon-class.c
rename to drivers/extcon/extcon.c
index 8319f25b7145fb6c5dbe1d1f31fa384b1e086b7b..4c9f165e4a04e0b8a49ff36e42b8cfc078f2b8a6 100644 (file)
@@ -158,6 +158,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
        /* Optional callback given by the user */
        if (edev->print_name) {
                int ret = edev->print_name(edev, buf);
+
                if (ret >= 0)
                        return ret;
        }
@@ -444,6 +445,9 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,
                             const char *extcon_name, const char *cable_name,
                             struct notifier_block *nb)
 {
+       unsigned long flags;
+       int ret;
+
        if (!obj || !cable_name || !nb)
                return -EINVAL;
 
@@ -461,8 +465,11 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,
 
                obj->internal_nb.notifier_call = _call_per_cable;
 
-               return raw_notifier_chain_register(&obj->edev->nh,
+               spin_lock_irqsave(&obj->edev->lock, flags);
+               ret = raw_notifier_chain_register(&obj->edev->nh,
                                                  &obj->internal_nb);
+               spin_unlock_irqrestore(&obj->edev->lock, flags);
+               return ret;
        } else {
                struct class_dev_iter iter;
                struct extcon_dev *extd;
@@ -495,10 +502,17 @@ EXPORT_SYMBOL_GPL(extcon_register_interest);
  */
 int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
 {
+       unsigned long flags;
+       int ret;
+
        if (!obj)
                return -EINVAL;
 
-       return raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb);
+       spin_lock_irqsave(&obj->edev->lock, flags);
+       ret = raw_notifier_chain_unregister(&obj->edev->nh, &obj->internal_nb);
+       spin_unlock_irqrestore(&obj->edev->lock, flags);
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(extcon_unregister_interest);
 
@@ -515,7 +529,14 @@ EXPORT_SYMBOL_GPL(extcon_unregister_interest);
 int extcon_register_notifier(struct extcon_dev *edev,
                        struct notifier_block *nb)
 {
-       return raw_notifier_chain_register(&edev->nh, nb);
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&edev->lock, flags);
+       ret = raw_notifier_chain_register(&edev->nh, nb);
+       spin_unlock_irqrestore(&edev->lock, flags);
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(extcon_register_notifier);
 
@@ -527,7 +548,14 @@ EXPORT_SYMBOL_GPL(extcon_register_notifier);
 int extcon_unregister_notifier(struct extcon_dev *edev,
                        struct notifier_block *nb)
 {
-       return raw_notifier_chain_unregister(&edev->nh, nb);
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&edev->lock, flags);
+       ret = raw_notifier_chain_unregister(&edev->nh, nb);
+       spin_unlock_irqrestore(&edev->lock, flags);
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(extcon_unregister_notifier);
 
index 2978f5ee8d2a0abdeacdc8f7fc2121d35c0260a2..54da66dc7d16c033db21664606a0fe7efc576535 100644 (file)
@@ -71,7 +71,8 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
        struct vmbus_channel_msginfo *open_info = NULL;
        void *in, *out;
        unsigned long flags;
-       int ret, t, err = 0;
+       int ret, err = 0;
+       unsigned long t;
 
        spin_lock_irqsave(&newchannel->lock, flags);
        if (newchannel->state == CHANNEL_OPEN_STATE) {
@@ -89,9 +90,10 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
        out = (void *)__get_free_pages(GFP_KERNEL|__GFP_ZERO,
                get_order(send_ringbuffer_size + recv_ringbuffer_size));
 
-       if (!out)
-               return -ENOMEM;
-
+       if (!out) {
+               err = -ENOMEM;
+               goto error0;
+       }
 
        in = (void *)((unsigned long)out + send_ringbuffer_size);
 
@@ -135,7 +137,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
                           GFP_KERNEL);
        if (!open_info) {
                err = -ENOMEM;
-               goto error0;
+               goto error_gpadl;
        }
 
        init_completion(&open_info->waitevent);
@@ -151,7 +153,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
 
        if (userdatalen > MAX_USER_DEFINED_BYTES) {
                err = -EINVAL;
-               goto error0;
+               goto error_gpadl;
        }
 
        if (userdatalen)
@@ -195,10 +197,14 @@ error1:
        list_del(&open_info->msglistentry);
        spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
 
+error_gpadl:
+       vmbus_teardown_gpadl(newchannel, newchannel->ringbuffer_gpadlhandle);
+
 error0:
        free_pages((unsigned long)out,
                get_order(send_ringbuffer_size + recv_ringbuffer_size));
        kfree(open_info);
+       newchannel->state = CHANNEL_OPEN_STATE;
        return err;
 }
 EXPORT_SYMBOL_GPL(vmbus_open);
@@ -534,6 +540,12 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
        free_pages((unsigned long)channel->ringbuffer_pages,
                get_order(channel->ringbuffer_pagecount * PAGE_SIZE));
 
+       /*
+        * If the channel has been rescinded; process device removal.
+        */
+       if (channel->rescind)
+               hv_process_channel_removal(channel,
+                                          channel->offermsg.child_relid);
        return ret;
 }
 
@@ -569,23 +581,9 @@ void vmbus_close(struct vmbus_channel *channel)
 }
 EXPORT_SYMBOL_GPL(vmbus_close);
 
-/**
- * vmbus_sendpacket() - Send the specified buffer on the given channel
- * @channel: Pointer to vmbus_channel structure.
- * @buffer: Pointer to the buffer you want to receive the data into.
- * @bufferlen: Maximum size of what the the buffer will hold
- * @requestid: Identifier of the request
- * @type: Type of packet that is being send e.g. negotiate, time
- * packet etc.
- *
- * Sends data in @buffer directly to hyper-v via the vmbus
- * This will send the data unparsed to hyper-v.
- *
- * Mainly used by Hyper-V drivers.
- */
-int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
+int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
                           u32 bufferlen, u64 requestid,
-                          enum vmbus_packet_type type, u32 flags)
+                          enum vmbus_packet_type type, u32 flags, bool kick_q)
 {
        struct vmpacket_descriptor desc;
        u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen;
@@ -613,21 +611,61 @@ int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
 
        ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
 
-       if (ret == 0 && signal)
+       /*
+        * Signalling the host is conditional on many factors:
+        * 1. The ring state changed from being empty to non-empty.
+        *    This is tracked by the variable "signal".
+        * 2. The variable kick_q tracks if more data will be placed
+        *    on the ring. We will not signal if more data is
+        *    to be placed.
+        *
+        * If we cannot write to the ring-buffer; signal the host
+        * even if we may not have written anything. This is a rare
+        * enough condition that it should not matter.
+        */
+       if (((ret == 0) && kick_q && signal) || (ret))
                vmbus_setevent(channel);
 
        return ret;
 }
+EXPORT_SYMBOL(vmbus_sendpacket_ctl);
+
+/**
+ * vmbus_sendpacket() - Send the specified buffer on the given channel
+ * @channel: Pointer to vmbus_channel structure.
+ * @buffer: Pointer to the buffer you want to receive the data into.
+ * @bufferlen: Maximum size of what the the buffer will hold
+ * @requestid: Identifier of the request
+ * @type: Type of packet that is being send e.g. negotiate, time
+ * packet etc.
+ *
+ * Sends data in @buffer directly to hyper-v via the vmbus
+ * This will send the data unparsed to hyper-v.
+ *
+ * Mainly used by Hyper-V drivers.
+ */
+int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
+                          u32 bufferlen, u64 requestid,
+                          enum vmbus_packet_type type, u32 flags)
+{
+       return vmbus_sendpacket_ctl(channel, buffer, bufferlen, requestid,
+                                   type, flags, true);
+}
 EXPORT_SYMBOL(vmbus_sendpacket);
 
 /*
- * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer
- * packets using a GPADL Direct packet type.
+ * vmbus_sendpacket_pagebuffer_ctl - Send a range of single-page buffer
+ * packets using a GPADL Direct packet type. This interface allows you
+ * to control notifying the host. This will be useful for sending
+ * batched data. Also the sender can control the send flags
+ * explicitly.
  */
-int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
+int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
                                     struct hv_page_buffer pagebuffers[],
                                     u32 pagecount, void *buffer, u32 bufferlen,
-                                    u64 requestid)
+                                    u64 requestid,
+                                    u32 flags,
+                                    bool kick_q)
 {
        int ret;
        int i;
@@ -655,7 +693,7 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
 
        /* Setup the descriptor */
        desc.type = VM_PKT_DATA_USING_GPA_DIRECT;
-       desc.flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
+       desc.flags = flags;
        desc.dataoffset8 = descsize >> 3; /* in 8-bytes grandularity */
        desc.length8 = (u16)(packetlen_aligned >> 3);
        desc.transactionid = requestid;
@@ -676,11 +714,40 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
 
        ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal);
 
-       if (ret == 0 && signal)
+       /*
+        * Signalling the host is conditional on many factors:
+        * 1. The ring state changed from being empty to non-empty.
+        *    This is tracked by the variable "signal".
+        * 2. The variable kick_q tracks if more data will be placed
+        *    on the ring. We will not signal if more data is
+        *    to be placed.
+        *
+        * If we cannot write to the ring-buffer; signal the host
+        * even if we may not have written anything. This is a rare
+        * enough condition that it should not matter.
+        */
+       if (((ret == 0) && kick_q && signal) || (ret))
                vmbus_setevent(channel);
 
        return ret;
 }
+EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer_ctl);
+
+/*
+ * vmbus_sendpacket_pagebuffer - Send a range of single-page buffer
+ * packets using a GPADL Direct packet type.
+ */
+int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
+                                    struct hv_page_buffer pagebuffers[],
+                                    u32 pagecount, void *buffer, u32 bufferlen,
+                                    u64 requestid)
+{
+       u32 flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
+       return vmbus_sendpacket_pagebuffer_ctl(channel, pagebuffers, pagecount,
+                                              buffer, bufferlen, requestid,
+                                              flags, true);
+
+}
 EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer);
 
 /*
index 3736f71bdec5ba3ea314fd2f0ae069dfb70133bf..0eeb1b3bc0484ce75572b0f6326ca0857cb73268 100644 (file)
 
 #include "hyperv_vmbus.h"
 
-struct vmbus_channel_message_table_entry {
-       enum vmbus_channel_message_type message_type;
-       void (*message_handler)(struct vmbus_channel_message_header *msg);
-};
-
-
 /**
  * vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message
  * @icmsghdrp: Pointer to msg header structure
@@ -139,54 +133,29 @@ EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp);
  */
 static struct vmbus_channel *alloc_channel(void)
 {
+       static atomic_t chan_num = ATOMIC_INIT(0);
        struct vmbus_channel *channel;
 
        channel = kzalloc(sizeof(*channel), GFP_ATOMIC);
        if (!channel)
                return NULL;
 
+       channel->id = atomic_inc_return(&chan_num);
        spin_lock_init(&channel->inbound_lock);
        spin_lock_init(&channel->lock);
 
        INIT_LIST_HEAD(&channel->sc_list);
        INIT_LIST_HEAD(&channel->percpu_list);
 
-       channel->controlwq = create_workqueue("hv_vmbus_ctl");
-       if (!channel->controlwq) {
-               kfree(channel);
-               return NULL;
-       }
-
        return channel;
 }
 
-/*
- * release_hannel - Release the vmbus channel object itself
- */
-static void release_channel(struct work_struct *work)
-{
-       struct vmbus_channel *channel = container_of(work,
-                                                    struct vmbus_channel,
-                                                    work);
-
-       destroy_workqueue(channel->controlwq);
-
-       kfree(channel);
-}
-
 /*
  * free_channel - Release the resources used by the vmbus channel object
  */
 static void free_channel(struct vmbus_channel *channel)
 {
-
-       /*
-        * We have to release the channel's workqueue/thread in the vmbus's
-        * workqueue/thread context
-        * ie we can't destroy ourselves.
-        */
-       INIT_WORK(&channel->work, release_channel);
-       queue_work(vmbus_connection.work_queue, &channel->work);
+       kfree(channel);
 }
 
 static void percpu_channel_enq(void *arg)
@@ -204,33 +173,21 @@ static void percpu_channel_deq(void *arg)
        list_del(&channel->percpu_list);
 }
 
-/*
- * vmbus_process_rescind_offer -
- * Rescind the offer by initiating a device removal
- */
-static void vmbus_process_rescind_offer(struct work_struct *work)
+
+void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
 {
-       struct vmbus_channel *channel = container_of(work,
-                                                    struct vmbus_channel,
-                                                    work);
+       struct vmbus_channel_relid_released msg;
        unsigned long flags;
        struct vmbus_channel *primary_channel;
-       struct vmbus_channel_relid_released msg;
-       struct device *dev;
-
-       if (channel->device_obj) {
-               dev = get_device(&channel->device_obj->device);
-               if (dev) {
-                       vmbus_device_unregister(channel->device_obj);
-                       put_device(dev);
-               }
-       }
 
        memset(&msg, 0, sizeof(struct vmbus_channel_relid_released));
-       msg.child_relid = channel->offermsg.child_relid;
+       msg.child_relid = relid;
        msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
        vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
 
+       if (channel == NULL)
+               return;
+
        if (channel->target_cpu != get_cpu()) {
                put_cpu();
                smp_call_function_single(channel->target_cpu,
@@ -259,7 +216,6 @@ void vmbus_free_channels(void)
 
        list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
                vmbus_device_unregister(channel->device_obj);
-               kfree(channel->device_obj);
                free_channel(channel);
        }
 }
@@ -268,15 +224,11 @@ void vmbus_free_channels(void)
  * vmbus_process_offer - Process the offer by creating a channel/device
  * associated with this offer
  */
-static void vmbus_process_offer(struct work_struct *work)
+static void vmbus_process_offer(struct vmbus_channel *newchannel)
 {
-       struct vmbus_channel *newchannel = container_of(work,
-                                                       struct vmbus_channel,
-                                                       work);
        struct vmbus_channel *channel;
        bool fnew = true;
        bool enq = false;
-       int ret;
        unsigned long flags;
 
        /* Make sure this is a new offer */
@@ -335,10 +287,11 @@ static void vmbus_process_offer(struct work_struct *work)
                        }
 
                        newchannel->state = CHANNEL_OPEN_STATE;
+                       channel->num_sc++;
                        if (channel->sc_creation_callback != NULL)
                                channel->sc_creation_callback(newchannel);
 
-                       goto done_init_rescind;
+                       return;
                }
 
                goto err_free_chan;
@@ -361,33 +314,35 @@ static void vmbus_process_offer(struct work_struct *work)
                &newchannel->offermsg.offer.if_instance,
                newchannel);
        if (!newchannel->device_obj)
-               goto err_free_chan;
+               goto err_deq_chan;
 
        /*
         * Add the new device to the bus. This will kick off device-driver
         * binding which eventually invokes the device driver's AddDevice()
         * method.
         */
-       ret = vmbus_device_register(newchannel->device_obj);
-       if (ret != 0) {
+       if (vmbus_device_register(newchannel->device_obj) != 0) {
                pr_err("unable to add child device object (relid %d)\n",
-                          newchannel->offermsg.child_relid);
-
-               spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
-               list_del(&newchannel->listentry);
-               spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
+                       newchannel->offermsg.child_relid);
                kfree(newchannel->device_obj);
-               goto err_free_chan;
+               goto err_deq_chan;
        }
-done_init_rescind:
-       spin_lock_irqsave(&newchannel->lock, flags);
-       /* The next possible work is rescind handling */
-       INIT_WORK(&newchannel->work, vmbus_process_rescind_offer);
-       /* Check if rescind offer was already received */
-       if (newchannel->rescind)
-               queue_work(newchannel->controlwq, &newchannel->work);
-       spin_unlock_irqrestore(&newchannel->lock, flags);
        return;
+
+err_deq_chan:
+       spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
+       list_del(&newchannel->listentry);
+       spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
+
+       if (newchannel->target_cpu != get_cpu()) {
+               put_cpu();
+               smp_call_function_single(newchannel->target_cpu,
+                                        percpu_channel_deq, newchannel, true);
+       } else {
+               percpu_channel_deq(newchannel);
+               put_cpu();
+       }
+
 err_free_chan:
        free_channel(newchannel);
 }
@@ -411,6 +366,8 @@ static const struct hv_vmbus_device_id hp_devs[] = {
        { HV_SCSI_GUID, },
        /* Network */
        { HV_NIC_GUID, },
+       /* NetworkDirect Guest RDMA */
+       { HV_ND_GUID, },
 };
 
 
@@ -511,8 +468,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
        newchannel->monitor_grp = (u8)offer->monitorid / 32;
        newchannel->monitor_bit = (u8)offer->monitorid % 32;
 
-       INIT_WORK(&newchannel->work, vmbus_process_offer);
-       queue_work(newchannel->controlwq, &newchannel->work);
+       vmbus_process_offer(newchannel);
 }
 
 /*
@@ -525,28 +481,34 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
        struct vmbus_channel_rescind_offer *rescind;
        struct vmbus_channel *channel;
        unsigned long flags;
+       struct device *dev;
 
        rescind = (struct vmbus_channel_rescind_offer *)hdr;
        channel = relid2channel(rescind->child_relid);
 
-       if (channel == NULL)
-               /* Just return here, no channel found */
+       if (channel == NULL) {
+               hv_process_channel_removal(NULL, rescind->child_relid);
                return;
+       }
 
        spin_lock_irqsave(&channel->lock, flags);
        channel->rescind = true;
-       /*
-        * channel->work.func != vmbus_process_rescind_offer means we are still
-        * processing offer request and the rescind offer processing should be
-        * postponed. It will be done at the very end of vmbus_process_offer()
-        * as rescind flag is being checked there.
-        */
-       if (channel->work.func == vmbus_process_rescind_offer)
-               /* work is initialized for vmbus_process_rescind_offer() from
-                * vmbus_process_offer() where the channel got created */
-               queue_work(channel->controlwq, &channel->work);
-
        spin_unlock_irqrestore(&channel->lock, flags);
+
+       if (channel->device_obj) {
+               /*
+                * We will have to unregister this device from the
+                * driver core.
+                */
+               dev = get_device(&channel->device_obj->device);
+               if (dev) {
+                       vmbus_device_unregister(channel->device_obj);
+                       put_device(dev);
+               }
+       } else {
+               hv_process_channel_removal(channel,
+                       channel->offermsg.child_relid);
+       }
 }
 
 /*
@@ -731,25 +693,25 @@ static void vmbus_onversion_response(
 }
 
 /* Channel message dispatch table */
-static struct vmbus_channel_message_table_entry
+struct vmbus_channel_message_table_entry
        channel_message_table[CHANNELMSG_COUNT] = {
-       {CHANNELMSG_INVALID,                    NULL},
-       {CHANNELMSG_OFFERCHANNEL,               vmbus_onoffer},
-       {CHANNELMSG_RESCIND_CHANNELOFFER,       vmbus_onoffer_rescind},
-       {CHANNELMSG_REQUESTOFFERS,              NULL},
-       {CHANNELMSG_ALLOFFERS_DELIVERED,        vmbus_onoffers_delivered},
-       {CHANNELMSG_OPENCHANNEL,                NULL},
-       {CHANNELMSG_OPENCHANNEL_RESULT, vmbus_onopen_result},
-       {CHANNELMSG_CLOSECHANNEL,               NULL},
-       {CHANNELMSG_GPADL_HEADER,               NULL},
-       {CHANNELMSG_GPADL_BODY,         NULL},
-       {CHANNELMSG_GPADL_CREATED,              vmbus_ongpadl_created},
-       {CHANNELMSG_GPADL_TEARDOWN,             NULL},
-       {CHANNELMSG_GPADL_TORNDOWN,             vmbus_ongpadl_torndown},
-       {CHANNELMSG_RELID_RELEASED,             NULL},
-       {CHANNELMSG_INITIATE_CONTACT,           NULL},
-       {CHANNELMSG_VERSION_RESPONSE,           vmbus_onversion_response},
-       {CHANNELMSG_UNLOAD,                     NULL},
+       {CHANNELMSG_INVALID,                    0, NULL},
+       {CHANNELMSG_OFFERCHANNEL,               0, vmbus_onoffer},
+       {CHANNELMSG_RESCIND_CHANNELOFFER,       0, vmbus_onoffer_rescind},
+       {CHANNELMSG_REQUESTOFFERS,              0, NULL},
+       {CHANNELMSG_ALLOFFERS_DELIVERED,        1, vmbus_onoffers_delivered},
+       {CHANNELMSG_OPENCHANNEL,                0, NULL},
+       {CHANNELMSG_OPENCHANNEL_RESULT,         1, vmbus_onopen_result},
+       {CHANNELMSG_CLOSECHANNEL,               0, NULL},
+       {CHANNELMSG_GPADL_HEADER,               0, NULL},
+       {CHANNELMSG_GPADL_BODY,                 0, NULL},
+       {CHANNELMSG_GPADL_CREATED,              1, vmbus_ongpadl_created},
+       {CHANNELMSG_GPADL_TEARDOWN,             0, NULL},
+       {CHANNELMSG_GPADL_TORNDOWN,             1, vmbus_ongpadl_torndown},
+       {CHANNELMSG_RELID_RELEASED,             0, NULL},
+       {CHANNELMSG_INITIATE_CONTACT,           0, NULL},
+       {CHANNELMSG_VERSION_RESPONSE,           1, vmbus_onversion_response},
+       {CHANNELMSG_UNLOAD,                     0, NULL},
 };
 
 /*
@@ -787,7 +749,7 @@ int vmbus_request_offers(void)
 {
        struct vmbus_channel_message_header *msg;
        struct vmbus_channel_msginfo *msginfo;
-       int ret, t;
+       int ret;
 
        msginfo = kmalloc(sizeof(*msginfo) +
                          sizeof(struct vmbus_channel_message_header),
@@ -795,8 +757,6 @@ int vmbus_request_offers(void)
        if (!msginfo)
                return -ENOMEM;
 
-       init_completion(&msginfo->waitevent);
-
        msg = (struct vmbus_channel_message_header *)msginfo->msg;
 
        msg->msgtype = CHANNELMSG_REQUESTOFFERS;
@@ -810,14 +770,6 @@ int vmbus_request_offers(void)
                goto cleanup;
        }
 
-       t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ);
-       if (t == 0) {
-               ret = -ETIMEDOUT;
-               goto cleanup;
-       }
-
-
-
 cleanup:
        kfree(msginfo);
 
@@ -826,9 +778,8 @@ cleanup:
 
 /*
  * Retrieve the (sub) channel on which to send an outgoing request.
- * When a primary channel has multiple sub-channels, we choose a
- * channel whose VCPU binding is closest to the VCPU on which
- * this call is being made.
+ * When a primary channel has multiple sub-channels, we try to
+ * distribute the load equally amongst all available channels.
  */
 struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary)
 {
@@ -836,11 +787,19 @@ struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary)
        int cur_cpu;
        struct vmbus_channel *cur_channel;
        struct vmbus_channel *outgoing_channel = primary;
-       int cpu_distance, new_cpu_distance;
+       int next_channel;
+       int i = 1;
 
        if (list_empty(&primary->sc_list))
                return outgoing_channel;
 
+       next_channel = primary->next_oc++;
+
+       if (next_channel > (primary->num_sc)) {
+               primary->next_oc = 0;
+               return outgoing_channel;
+       }
+
        cur_cpu = hv_context.vp_index[get_cpu()];
        put_cpu();
        list_for_each_safe(cur, tmp, &primary->sc_list) {
@@ -851,18 +810,10 @@ struct vmbus_channel *vmbus_get_outgoing_channel(struct vmbus_channel *primary)
                if (cur_channel->target_vp == cur_cpu)
                        return cur_channel;
 
-               cpu_distance = ((outgoing_channel->target_vp > cur_cpu) ?
-                               (outgoing_channel->target_vp - cur_cpu) :
-                               (cur_cpu - outgoing_channel->target_vp));
-
-               new_cpu_distance = ((cur_channel->target_vp > cur_cpu) ?
-                               (cur_channel->target_vp - cur_cpu) :
-                               (cur_cpu - cur_channel->target_vp));
-
-               if (cpu_distance < new_cpu_distance)
-                       continue;
+               if (i == next_channel)
+                       return cur_channel;
 
-               outgoing_channel = cur_channel;
+               i++;
        }
 
        return outgoing_channel;
index a63a795300b9ebbec393154fdfdce27e38ad3b0c..b27220a425f4a44b6b1143f39017998f1b77533a 100644 (file)
@@ -216,10 +216,21 @@ int vmbus_connect(void)
 
 cleanup:
        pr_err("Unable to connect to host\n");
+
        vmbus_connection.conn_state = DISCONNECTED;
+       vmbus_disconnect();
+
+       kfree(msginfo);
 
-       if (vmbus_connection.work_queue)
+       return ret;
+}
+
+void vmbus_disconnect(void)
+{
+       if (vmbus_connection.work_queue) {
+               drain_workqueue(vmbus_connection.work_queue);
                destroy_workqueue(vmbus_connection.work_queue);
+       }
 
        if (vmbus_connection.int_page) {
                free_pages((unsigned long)vmbus_connection.int_page, 0);
@@ -230,10 +241,6 @@ cleanup:
        free_pages((unsigned long)vmbus_connection.monitor_pages[1], 0);
        vmbus_connection.monitor_pages[0] = NULL;
        vmbus_connection.monitor_pages[1] = NULL;
-
-       kfree(msginfo);
-
-       return ret;
 }
 
 /*
@@ -311,10 +318,8 @@ static void process_chn_event(u32 relid)
         */
        channel = pcpu_relid2channel(relid);
 
-       if (!channel) {
-               pr_err("channel not found for relid - %u\n", relid);
+       if (!channel)
                return;
-       }
 
        /*
         * A channel once created is persistent even when there
@@ -349,10 +354,7 @@ static void process_chn_event(u32 relid)
                        else
                                bytes_to_read = 0;
                } while (read_state && (bytes_to_read != 0));
-       } else {
-               pr_err("no channel callback for relid - %u\n", relid);
        }
-
 }
 
 /*
@@ -420,6 +422,7 @@ int vmbus_post_msg(void *buffer, size_t buflen)
        union hv_connection_id conn_id;
        int ret = 0;
        int retries = 0;
+       u32 msec = 1;
 
        conn_id.asu32 = 0;
        conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID;
@@ -429,13 +432,20 @@ int vmbus_post_msg(void *buffer, size_t buflen)
         * insufficient resources. Retry the operation a couple of
         * times before giving up.
         */
-       while (retries < 10) {
+       while (retries < 20) {
                ret = hv_post_message(conn_id, 1, buffer, buflen);
 
                switch (ret) {
+               case HV_STATUS_INVALID_CONNECTION_ID:
+                       /*
+                        * We could get this if we send messages too
+                        * frequently.
+                        */
+                       ret = -EAGAIN;
+                       break;
+               case HV_STATUS_INSUFFICIENT_MEMORY:
                case HV_STATUS_INSUFFICIENT_BUFFERS:
                        ret = -ENOMEM;
-               case -ENOMEM:
                        break;
                case HV_STATUS_SUCCESS:
                        return ret;
@@ -445,7 +455,9 @@ int vmbus_post_msg(void *buffer, size_t buflen)
                }
 
                retries++;
-               msleep(100);
+               msleep(msec);
+               if (msec < 2048)
+                       msec *= 2;
        }
        return ret;
 }
index 50e51a51ff8bde317def929e744a810e2ccaa62d..d3943bceecc367c7b5241de3ba7933aae45f41c4 100644 (file)
@@ -312,7 +312,11 @@ static void hv_init_clockevent_device(struct clock_event_device *dev, int cpu)
        dev->features = CLOCK_EVT_FEAT_ONESHOT;
        dev->cpumask = cpumask_of(cpu);
        dev->rating = 1000;
-       dev->owner = THIS_MODULE;
+       /*
+        * Avoid settint dev->owner = THIS_MODULE deliberately as doing so will
+        * result in clockevents_config_and_register() taking additional
+        * references to the hv_vmbus module making it impossible to unload.
+        */
 
        dev->set_mode = hv_ce_setmode;
        dev->set_next_event = hv_ce_set_next_event;
@@ -469,6 +473,20 @@ void hv_synic_init(void *arg)
        return;
 }
 
+/*
+ * hv_synic_clockevents_cleanup - Cleanup clockevent devices
+ */
+void hv_synic_clockevents_cleanup(void)
+{
+       int cpu;
+
+       if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE))
+               return;
+
+       for_each_online_cpu(cpu)
+               clockevents_unbind_device(hv_context.clk_evt[cpu], cpu);
+}
+
 /*
  * hv_synic_cleanup - Cleanup routine for hv_synic_init().
  */
@@ -477,11 +495,17 @@ void hv_synic_cleanup(void *arg)
        union hv_synic_sint shared_sint;
        union hv_synic_simp simp;
        union hv_synic_siefp siefp;
+       union hv_synic_scontrol sctrl;
        int cpu = smp_processor_id();
 
        if (!hv_context.synic_initialized)
                return;
 
+       /* Turn off clockevent device */
+       if (ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE)
+               hv_ce_setmode(CLOCK_EVT_MODE_SHUTDOWN,
+                             hv_context.clk_evt[cpu]);
+
        rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
 
        shared_sint.masked = 1;
@@ -502,6 +526,10 @@ void hv_synic_cleanup(void *arg)
 
        wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
 
-       free_page((unsigned long)hv_context.synic_message_page[cpu]);
-       free_page((unsigned long)hv_context.synic_event_page[cpu]);
+       /* Disable the global synic bit */
+       rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
+       sctrl.enable = 0;
+       wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
+
+       hv_synic_free_cpu(cpu);
 }
index ff169386b2c716bc04c8dcd03f54ad28615cc9de..cb5b7dc9797fad344f1ddba31d80ab135bcec5a3 100644 (file)
@@ -428,14 +428,13 @@ struct dm_info_msg {
  * currently hot added. We hot add in multiples of 128M
  * chunks; it is possible that we may not be able to bring
  * online all the pages in the region. The range
- * covered_start_pfn : covered_end_pfn defines the pages that can
+ * covered_end_pfn defines the pages that can
  * be brough online.
  */
 
 struct hv_hotadd_state {
        struct list_head list;
        unsigned long start_pfn;
-       unsigned long covered_start_pfn;
        unsigned long covered_end_pfn;
        unsigned long ha_end_pfn;
        unsigned long end_pfn;
@@ -503,6 +502,8 @@ struct hv_dynmem_device {
         * Number of pages we have currently ballooned out.
         */
        unsigned int num_pages_ballooned;
+       unsigned int num_pages_onlined;
+       unsigned int num_pages_added;
 
        /*
         * State to manage the ballooning (up) operation.
@@ -534,7 +535,6 @@ struct hv_dynmem_device {
        struct task_struct *thread;
 
        struct mutex ha_region_mutex;
-       struct completion waiter_event;
 
        /*
         * A list of hot-add regions.
@@ -554,46 +554,32 @@ static struct hv_dynmem_device dm_device;
 static void post_status(struct hv_dynmem_device *dm);
 
 #ifdef CONFIG_MEMORY_HOTPLUG
-static void acquire_region_mutex(bool trylock)
-{
-       if (trylock) {
-               reinit_completion(&dm_device.waiter_event);
-               while (!mutex_trylock(&dm_device.ha_region_mutex))
-                       wait_for_completion(&dm_device.waiter_event);
-       } else {
-               mutex_lock(&dm_device.ha_region_mutex);
-       }
-}
-
-static void release_region_mutex(bool trylock)
-{
-       if (trylock) {
-               mutex_unlock(&dm_device.ha_region_mutex);
-       } else {
-               mutex_unlock(&dm_device.ha_region_mutex);
-               complete(&dm_device.waiter_event);
-       }
-}
-
 static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
                              void *v)
 {
+       struct memory_notify *mem = (struct memory_notify *)v;
+
        switch (val) {
        case MEM_GOING_ONLINE:
-               acquire_region_mutex(true);
+               mutex_lock(&dm_device.ha_region_mutex);
                break;
 
        case MEM_ONLINE:
+               dm_device.num_pages_onlined += mem->nr_pages;
        case MEM_CANCEL_ONLINE:
-               release_region_mutex(true);
+               mutex_unlock(&dm_device.ha_region_mutex);
                if (dm_device.ha_waiting) {
                        dm_device.ha_waiting = false;
                        complete(&dm_device.ol_waitevent);
                }
                break;
 
-       case MEM_GOING_OFFLINE:
        case MEM_OFFLINE:
+               mutex_lock(&dm_device.ha_region_mutex);
+               dm_device.num_pages_onlined -= mem->nr_pages;
+               mutex_unlock(&dm_device.ha_region_mutex);
+               break;
+       case MEM_GOING_OFFLINE:
        case MEM_CANCEL_OFFLINE:
                break;
        }
@@ -646,7 +632,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
                init_completion(&dm_device.ol_waitevent);
                dm_device.ha_waiting = true;
 
-               release_region_mutex(false);
+               mutex_unlock(&dm_device.ha_region_mutex);
                nid = memory_add_physaddr_to_nid(PFN_PHYS(start_pfn));
                ret = add_memory(nid, PFN_PHYS((start_pfn)),
                                (HA_CHUNK << PAGE_SHIFT));
@@ -665,6 +651,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
                        }
                        has->ha_end_pfn -= HA_CHUNK;
                        has->covered_end_pfn -=  processed_pfn;
+                       mutex_lock(&dm_device.ha_region_mutex);
                        break;
                }
 
@@ -675,7 +662,7 @@ static void hv_mem_hot_add(unsigned long start, unsigned long size,
                 * have not been "onlined" within the allowed time.
                 */
                wait_for_completion_timeout(&dm_device.ol_waitevent, 5*HZ);
-               acquire_region_mutex(false);
+               mutex_lock(&dm_device.ha_region_mutex);
                post_status(&dm_device);
        }
 
@@ -691,8 +678,7 @@ static void hv_online_page(struct page *pg)
 
        list_for_each(cur, &dm_device.ha_region_list) {
                has = list_entry(cur, struct hv_hotadd_state, list);
-               cur_start_pgp = (unsigned long)
-                               pfn_to_page(has->covered_start_pfn);
+               cur_start_pgp = (unsigned long)pfn_to_page(has->start_pfn);
                cur_end_pgp = (unsigned long)pfn_to_page(has->covered_end_pfn);
 
                if (((unsigned long)pg >= cur_start_pgp) &&
@@ -704,7 +690,6 @@ static void hv_online_page(struct page *pg)
                        __online_page_set_limits(pg);
                        __online_page_increment_counters(pg);
                        __online_page_free(pg);
-                       has->covered_start_pfn++;
                }
        }
 }
@@ -748,10 +733,9 @@ static bool pfn_covered(unsigned long start_pfn, unsigned long pfn_cnt)
                 * is, update it.
                 */
 
-               if (has->covered_end_pfn != start_pfn) {
+               if (has->covered_end_pfn != start_pfn)
                        has->covered_end_pfn = start_pfn;
-                       has->covered_start_pfn = start_pfn;
-               }
+
                return true;
 
        }
@@ -794,9 +778,18 @@ static unsigned long handle_pg_range(unsigned long pg_start,
                        pgs_ol = has->ha_end_pfn - start_pfn;
                        if (pgs_ol > pfn_cnt)
                                pgs_ol = pfn_cnt;
-                       hv_bring_pgs_online(start_pfn, pgs_ol);
+
+                       /*
+                        * Check if the corresponding memory block is already
+                        * online by checking its last previously backed page.
+                        * In case it is we need to bring rest (which was not
+                        * backed previously) online too.
+                        */
+                       if (start_pfn > has->start_pfn &&
+                           !PageReserved(pfn_to_page(start_pfn - 1)))
+                               hv_bring_pgs_online(start_pfn, pgs_ol);
+
                        has->covered_end_pfn +=  pgs_ol;
-                       has->covered_start_pfn +=  pgs_ol;
                        pfn_cnt -= pgs_ol;
                }
 
@@ -857,7 +850,6 @@ static unsigned long process_hot_add(unsigned long pg_start,
                list_add_tail(&ha_region->list, &dm_device.ha_region_list);
                ha_region->start_pfn = rg_start;
                ha_region->ha_end_pfn = rg_start;
-               ha_region->covered_start_pfn = pg_start;
                ha_region->covered_end_pfn = pg_start;
                ha_region->end_pfn = rg_start + rg_size;
        }
@@ -886,7 +878,7 @@ static void hot_add_req(struct work_struct *dummy)
        resp.hdr.size = sizeof(struct dm_hot_add_response);
 
 #ifdef CONFIG_MEMORY_HOTPLUG
-       acquire_region_mutex(false);
+       mutex_lock(&dm_device.ha_region_mutex);
        pg_start = dm->ha_wrk.ha_page_range.finfo.start_page;
        pfn_cnt = dm->ha_wrk.ha_page_range.finfo.page_cnt;
 
@@ -918,7 +910,9 @@ static void hot_add_req(struct work_struct *dummy)
        if (do_hot_add)
                resp.page_count = process_hot_add(pg_start, pfn_cnt,
                                                rg_start, rg_sz);
-       release_region_mutex(false);
+
+       dm->num_pages_added += resp.page_count;
+       mutex_unlock(&dm_device.ha_region_mutex);
 #endif
        /*
         * The result field of the response structure has the
@@ -982,8 +976,8 @@ static unsigned long compute_balloon_floor(void)
         *     128        72    (1/2)
         *     512       168    (1/4)
         *    2048       360    (1/8)
-        *    8192       768    (1/16)
-        *   32768      1536    (1/32)
+        *    8192       744    (1/16)
+        *   32768      1512    (1/32)
         */
        if (totalram_pages < MB2PAGES(128))
                min_pages = MB2PAGES(8) + (totalram_pages >> 1);
@@ -992,9 +986,9 @@ static unsigned long compute_balloon_floor(void)
        else if (totalram_pages < MB2PAGES(2048))
                min_pages = MB2PAGES(104) + (totalram_pages >> 3);
        else if (totalram_pages < MB2PAGES(8192))
-               min_pages = MB2PAGES(256) + (totalram_pages >> 4);
+               min_pages = MB2PAGES(232) + (totalram_pages >> 4);
        else
-               min_pages = MB2PAGES(512) + (totalram_pages >> 5);
+               min_pages = MB2PAGES(488) + (totalram_pages >> 5);
 #undef MB2PAGES
        return min_pages;
 }
@@ -1031,17 +1025,21 @@ static void post_status(struct hv_dynmem_device *dm)
        status.hdr.trans_id = atomic_inc_return(&trans_id);
 
        /*
-        * The host expects the guest to report free memory.
-        * Further, the host expects the pressure information to
-        * include the ballooned out pages.
-        * For a given amount of memory that we are managing, we
-        * need to compute a floor below which we should not balloon.
-        * Compute this and add it to the pressure report.
+        * The host expects the guest to report free and committed memory.
+        * Furthermore, the host expects the pressure information to include
+        * the ballooned out pages. For a given amount of memory that we are
+        * managing we need to compute a floor below which we should not
+        * balloon. Compute this and add it to the pressure report.
+        * We also need to report all offline pages (num_pages_added -
+        * num_pages_onlined) as committed to the host, otherwise it can try
+        * asking us to balloon them out.
         */
        status.num_avail = val.freeram;
        status.num_committed = vm_memory_committed() +
-                               dm->num_pages_ballooned +
-                               compute_balloon_floor();
+               dm->num_pages_ballooned +
+               (dm->num_pages_added > dm->num_pages_onlined ?
+                dm->num_pages_added - dm->num_pages_onlined : 0) +
+               compute_balloon_floor();
 
        /*
         * If our transaction ID is no longer current, just don't
@@ -1083,11 +1081,12 @@ static void free_balloon_pages(struct hv_dynmem_device *dm,
 
 
 
-static int  alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages,
-                        struct dm_balloon_response *bl_resp, int alloc_unit,
-                        bool *alloc_error)
+static unsigned int alloc_balloon_pages(struct hv_dynmem_device *dm,
+                                       unsigned int num_pages,
+                                       struct dm_balloon_response *bl_resp,
+                                       int alloc_unit)
 {
-       int i = 0;
+       unsigned int i = 0;
        struct page *pg;
 
        if (num_pages < alloc_unit)
@@ -1106,11 +1105,8 @@ static int  alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages,
                                __GFP_NOMEMALLOC | __GFP_NOWARN,
                                get_order(alloc_unit << PAGE_SHIFT));
 
-               if (!pg) {
-                       *alloc_error = true;
+               if (!pg)
                        return i * alloc_unit;
-               }
-
 
                dm->num_pages_ballooned += alloc_unit;
 
@@ -1137,14 +1133,15 @@ static int  alloc_balloon_pages(struct hv_dynmem_device *dm, int num_pages,
 
 static void balloon_up(struct work_struct *dummy)
 {
-       int num_pages = dm_device.balloon_wrk.num_pages;
-       int num_ballooned = 0;
+       unsigned int num_pages = dm_device.balloon_wrk.num_pages;
+       unsigned int num_ballooned = 0;
        struct dm_balloon_response *bl_resp;
        int alloc_unit;
        int ret;
-       bool alloc_error;
        bool done = false;
        int i;
+       struct sysinfo val;
+       unsigned long floor;
 
        /* The host balloons pages in 2M granularity. */
        WARN_ON_ONCE(num_pages % PAGES_IN_2M != 0);
@@ -1155,6 +1152,15 @@ static void balloon_up(struct work_struct *dummy)
         */
        alloc_unit = 512;
 
+       si_meminfo(&val);
+       floor = compute_balloon_floor();
+
+       /* Refuse to balloon below the floor, keep the 2M granularity. */
+       if (val.freeram < num_pages || val.freeram - num_pages < floor) {
+               num_pages = val.freeram > floor ? (val.freeram - floor) : 0;
+               num_pages -= num_pages % PAGES_IN_2M;
+       }
+
        while (!done) {
                bl_resp = (struct dm_balloon_response *)send_buffer;
                memset(send_buffer, 0, PAGE_SIZE);
@@ -1164,18 +1170,15 @@ static void balloon_up(struct work_struct *dummy)
 
 
                num_pages -= num_ballooned;
-               alloc_error = false;
                num_ballooned = alloc_balloon_pages(&dm_device, num_pages,
-                                               bl_resp, alloc_unit,
-                                                &alloc_error);
+                                                   bl_resp, alloc_unit);
 
                if (alloc_unit != 1 && num_ballooned == 0) {
                        alloc_unit = 1;
                        continue;
                }
 
-               if ((alloc_unit == 1 && alloc_error) ||
-                       (num_ballooned == num_pages)) {
+               if (num_ballooned == 0 || num_ballooned == num_pages) {
                        bl_resp->more_pages = 0;
                        done = true;
                        dm_device.state = DM_INITIALIZED;
@@ -1414,7 +1417,8 @@ static void balloon_onchannelcallback(void *context)
 static int balloon_probe(struct hv_device *dev,
                        const struct hv_vmbus_device_id *dev_id)
 {
-       int ret, t;
+       int ret;
+       unsigned long t;
        struct dm_version_request version_req;
        struct dm_capabilities cap_msg;
 
@@ -1439,7 +1443,6 @@ static int balloon_probe(struct hv_device *dev,
        dm_device.next_version = DYNMEM_PROTOCOL_VERSION_WIN7;
        init_completion(&dm_device.host_event);
        init_completion(&dm_device.config_event);
-       init_completion(&dm_device.waiter_event);
        INIT_LIST_HEAD(&dm_device.ha_region_list);
        mutex_init(&dm_device.ha_region_mutex);
        INIT_WORK(&dm_device.balloon_wrk.wrk, balloon_up);
index 3b9c9ef0deb8788da071344a4948c933100e9529..7994ec2e4151b085bbcb596d8ddbd4d8765b939e 100644 (file)
@@ -340,12 +340,8 @@ static int util_probe(struct hv_device *dev,
 
        set_channel_read_state(dev->channel, false);
 
-       ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
-                       srv->util_cb, dev->channel);
-       if (ret)
-               goto error;
-
        hv_set_drvdata(dev, srv);
+
        /*
         * Based on the host; initialize the framework and
         * service version numbers we will negotiate.
@@ -365,6 +361,11 @@ static int util_probe(struct hv_device *dev,
                hb_srv_version = HB_VERSION;
        }
 
+       ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
+                       srv->util_cb, dev->channel);
+       if (ret)
+               goto error;
+
        return 0;
 
 error:
@@ -379,9 +380,9 @@ static int util_remove(struct hv_device *dev)
 {
        struct hv_util_service *srv = hv_get_drvdata(dev);
 
-       vmbus_close(dev->channel);
        if (srv->util_deinit)
                srv->util_deinit();
+       vmbus_close(dev->channel);
        kfree(srv->recv_buffer);
 
        return 0;
index 44b1c94247129b8e1d17467b4871c919b5a2b40f..887287ad411f767dd3b7329e8494cd3a8a6d37aa 100644 (file)
@@ -49,6 +49,17 @@ enum hv_cpuid_function {
        HVCPUID_IMPLEMENTATION_LIMITS           = 0x40000005,
 };
 
+#define  HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE   0x400
+
+#define HV_X64_MSR_CRASH_P0   0x40000100
+#define HV_X64_MSR_CRASH_P1   0x40000101
+#define HV_X64_MSR_CRASH_P2   0x40000102
+#define HV_X64_MSR_CRASH_P3   0x40000103
+#define HV_X64_MSR_CRASH_P4   0x40000104
+#define HV_X64_MSR_CRASH_CTL  0x40000105
+
+#define HV_CRASH_CTL_CRASH_NOTIFY (1ULL << 63)
+
 /* Define version of the synthetic interrupt controller. */
 #define HV_SYNIC_VERSION               (1)
 
@@ -572,6 +583,8 @@ extern void hv_synic_init(void *irqarg);
 
 extern void hv_synic_cleanup(void *arg);
 
+extern void hv_synic_clockevents_cleanup(void);
+
 /*
  * Host version information.
  */
@@ -672,6 +685,23 @@ struct vmbus_msginfo {
 
 extern struct vmbus_connection vmbus_connection;
 
+enum vmbus_message_handler_type {
+       /* The related handler can sleep. */
+       VMHT_BLOCKING = 0,
+
+       /* The related handler must NOT sleep. */
+       VMHT_NON_BLOCKING = 1,
+};
+
+struct vmbus_channel_message_table_entry {
+       enum vmbus_channel_message_type message_type;
+       enum vmbus_message_handler_type handler_type;
+       void (*message_handler)(struct vmbus_channel_message_header *msg);
+};
+
+extern struct vmbus_channel_message_table_entry
+       channel_message_table[CHANNELMSG_COUNT];
+
 /* General vmbus interface */
 
 struct hv_device *vmbus_device_create(const uuid_le *type,
@@ -692,6 +722,7 @@ void vmbus_free_channels(void);
 /* Connection interface */
 
 int vmbus_connect(void);
+void vmbus_disconnect(void);
 
 int vmbus_post_msg(void *buffer, size_t buflen);
 
index f518b8d7a5b5190eb01b741d0961f6b9fbf6cd33..c85235e9f245b6d210384673168bef12c6f9f0de 100644 (file)
 #include <linux/hyperv.h>
 #include <linux/kernel_stat.h>
 #include <linux/clockchips.h>
+#include <linux/cpu.h>
 #include <asm/hyperv.h>
 #include <asm/hypervisor.h>
 #include <asm/mshyperv.h>
+#include <linux/notifier.h>
+#include <linux/ptrace.h>
 #include "hyperv_vmbus.h"
 
 static struct acpi_device  *hv_acpi_dev;
@@ -44,6 +47,31 @@ static struct tasklet_struct msg_dpc;
 static struct completion probe_event;
 static int irq;
 
+
+static int hyperv_panic_event(struct notifier_block *nb,
+                       unsigned long event, void *ptr)
+{
+       struct pt_regs *regs;
+
+       regs = current_pt_regs();
+
+       wrmsrl(HV_X64_MSR_CRASH_P0, regs->ip);
+       wrmsrl(HV_X64_MSR_CRASH_P1, regs->ax);
+       wrmsrl(HV_X64_MSR_CRASH_P2, regs->bx);
+       wrmsrl(HV_X64_MSR_CRASH_P3, regs->cx);
+       wrmsrl(HV_X64_MSR_CRASH_P4, regs->dx);
+
+       /*
+        * Let Hyper-V know there is crash data available
+        */
+       wrmsrl(HV_X64_MSR_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY);
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block hyperv_panic_block = {
+       .notifier_call = hyperv_panic_event,
+};
+
 struct resource hyperv_mmio = {
        .name  = "hyperv mmio",
        .flags = IORESOURCE_MEM,
@@ -507,14 +535,26 @@ static int vmbus_probe(struct device *child_device)
  */
 static int vmbus_remove(struct device *child_device)
 {
-       struct hv_driver *drv = drv_to_hv_drv(child_device->driver);
+       struct hv_driver *drv;
        struct hv_device *dev = device_to_hv_device(child_device);
-
-       if (drv->remove)
-               drv->remove(dev);
-       else
-               pr_err("remove not set for driver %s\n",
-                       dev_name(child_device));
+       u32 relid = dev->channel->offermsg.child_relid;
+
+       if (child_device->driver) {
+               drv = drv_to_hv_drv(child_device->driver);
+               if (drv->remove)
+                       drv->remove(dev);
+               else {
+                       hv_process_channel_removal(dev->channel, relid);
+                       pr_err("remove not set for driver %s\n",
+                               dev_name(child_device));
+               }
+       } else {
+               /*
+                * We don't have a driver for this device; deal with the
+                * rescind message by removing the channel.
+                */
+               hv_process_channel_removal(dev->channel, relid);
+       }
 
        return 0;
 }
@@ -573,6 +613,10 @@ static void vmbus_onmessage_work(struct work_struct *work)
 {
        struct onmessage_work_context *ctx;
 
+       /* Do not process messages if we're in DISCONNECTED state */
+       if (vmbus_connection.conn_state == DISCONNECTED)
+               return;
+
        ctx = container_of(work, struct onmessage_work_context,
                           work);
        vmbus_onmessage(&ctx->msg);
@@ -613,21 +657,36 @@ static void vmbus_on_msg_dpc(unsigned long data)
        void *page_addr = hv_context.synic_message_page[cpu];
        struct hv_message *msg = (struct hv_message *)page_addr +
                                  VMBUS_MESSAGE_SINT;
+       struct vmbus_channel_message_header *hdr;
+       struct vmbus_channel_message_table_entry *entry;
        struct onmessage_work_context *ctx;
 
        while (1) {
-               if (msg->header.message_type == HVMSG_NONE) {
+               if (msg->header.message_type == HVMSG_NONE)
                        /* no msg */
                        break;
-               } else {
+
+               hdr = (struct vmbus_channel_message_header *)msg->u.payload;
+
+               if (hdr->msgtype >= CHANNELMSG_COUNT) {
+                       WARN_ONCE(1, "unknown msgtype=%d\n", hdr->msgtype);
+                       goto msg_handled;
+               }
+
+               entry = &channel_message_table[hdr->msgtype];
+               if (entry->handler_type == VMHT_BLOCKING) {
                        ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC);
                        if (ctx == NULL)
                                continue;
+
                        INIT_WORK(&ctx->work, vmbus_onmessage_work);
                        memcpy(&ctx->msg, msg, sizeof(*msg));
+
                        queue_work(vmbus_connection.work_queue, &ctx->work);
-               }
+               } else
+                       entry->message_handler(hdr);
 
+msg_handled:
                msg->header.message_type = HVMSG_NONE;
 
                /*
@@ -704,6 +763,39 @@ static void vmbus_isr(void)
        }
 }
 
+#ifdef CONFIG_HOTPLUG_CPU
+static int hyperv_cpu_disable(void)
+{
+       return -ENOSYS;
+}
+
+static void hv_cpu_hotplug_quirk(bool vmbus_loaded)
+{
+       static void *previous_cpu_disable;
+
+       /*
+        * Offlining a CPU when running on newer hypervisors (WS2012R2, Win8,
+        * ...) is not supported at this moment as channel interrupts are
+        * distributed across all of them.
+        */
+
+       if ((vmbus_proto_version == VERSION_WS2008) ||
+           (vmbus_proto_version == VERSION_WIN7))
+               return;
+
+       if (vmbus_loaded) {
+               previous_cpu_disable = smp_ops.cpu_disable;
+               smp_ops.cpu_disable = hyperv_cpu_disable;
+               pr_notice("CPU offlining is not supported by hypervisor\n");
+       } else if (previous_cpu_disable)
+               smp_ops.cpu_disable = previous_cpu_disable;
+}
+#else
+static void hv_cpu_hotplug_quirk(bool vmbus_loaded)
+{
+}
+#endif
+
 /*
  * vmbus_bus_init -Main vmbus driver initialization routine.
  *
@@ -744,6 +836,16 @@ static int vmbus_bus_init(int irq)
        if (ret)
                goto err_alloc;
 
+       hv_cpu_hotplug_quirk(true);
+
+       /*
+        * Only register if the crash MSRs are available
+        */
+       if (ms_hyperv.features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
+               atomic_notifier_chain_register(&panic_notifier_list,
+                                              &hyperv_panic_block);
+       }
+
        vmbus_request_offers();
 
        return 0;
@@ -840,10 +942,8 @@ int vmbus_device_register(struct hv_device *child_device_obj)
 {
        int ret = 0;
 
-       static atomic_t device_num = ATOMIC_INIT(0);
-
-       dev_set_name(&child_device_obj->device, "vmbus_0_%d",
-                    atomic_inc_return(&device_num));
+       dev_set_name(&child_device_obj->device, "vmbus_%d",
+                    child_device_obj->channel->id);
 
        child_device_obj->device.bus = &hv_bus;
        child_device_obj->device.parent = &hv_acpi_dev->dev;
@@ -992,11 +1092,19 @@ cleanup:
 
 static void __exit vmbus_exit(void)
 {
+       int cpu;
+
+       vmbus_connection.conn_state = DISCONNECTED;
+       hv_synic_clockevents_cleanup();
        hv_remove_vmbus_irq();
        vmbus_free_channels();
        bus_unregister(&hv_bus);
        hv_cleanup();
+       for_each_online_cpu(cpu)
+               smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1);
        acpi_bus_unregister_driver(&vmbus_acpi_driver);
+       hv_cpu_hotplug_quirk(false);
+       vmbus_disconnect();
 }
 
 
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
new file mode 100644 (file)
index 0000000..fc1f1ae
--- /dev/null
@@ -0,0 +1,61 @@
+#
+# Coresight configuration
+#
+menuconfig CORESIGHT
+       bool "CoreSight Tracing Support"
+       select ARM_AMBA
+       help
+         This framework provides a kernel interface for the CoreSight debug
+         and trace drivers to register themselves with. It's intended to build
+         a topological view of the CoreSight components based on a DT
+         specification and configure the right serie of components when a
+         trace source gets enabled.
+
+if CORESIGHT
+config CORESIGHT_LINKS_AND_SINKS
+       bool "CoreSight Link and Sink drivers"
+       help
+         This enables support for CoreSight link and sink drivers that are
+         responsible for transporting and collecting the trace data
+         respectively.  Link and sinks are dynamically aggregated with a trace
+         entity at run time to form a complete trace path.
+
+config CORESIGHT_LINK_AND_SINK_TMC
+       bool "Coresight generic TMC driver"
+       depends on CORESIGHT_LINKS_AND_SINKS
+       help
+         This enables support for the Trace Memory Controller driver.
+         Depending on its configuration the device can act as a link (embedded
+         trace router - ETR) or sink (embedded trace FIFO).  The driver
+         complies with the generic implementation of the component without
+         special enhancement or added features.
+
+config CORESIGHT_SINK_TPIU
+       bool "Coresight generic TPIU driver"
+       depends on CORESIGHT_LINKS_AND_SINKS
+       help
+         This enables support for the Trace Port Interface Unit driver,
+         responsible for bridging the gap between the on-chip coresight
+         components and a trace for bridging the gap between the on-chip
+         coresight components and a trace port collection engine, typically
+         connected to an external host for use case capturing more traces than
+         the on-board coresight memory can handle.
+
+config CORESIGHT_SINK_ETBV10
+       bool "Coresight ETBv1.0 driver"
+       depends on CORESIGHT_LINKS_AND_SINKS
+       help
+         This enables support for the Embedded Trace Buffer version 1.0 driver
+         that complies with the generic implementation of the component without
+         special enhancement or added features.
+
+config CORESIGHT_SOURCE_ETM3X
+       bool "CoreSight Embedded Trace Macrocell 3.x driver"
+       depends on !ARM64
+       select CORESIGHT_LINKS_AND_SINKS
+       help
+         This driver provides support for processor ETM3.x and PTM1.x modules,
+         which allows tracing the instructions that a processor is executing
+         This is primarily useful for instruction level tracing.  Depending
+         the ETM version data tracing may also be available.
+endif
similarity index 99%
rename from drivers/coresight/coresight-etb10.c
rename to drivers/hwtracing/coresight/coresight-etb10.c
index c9acd406f0d02a4e8223b20d323c028d1938af9b..40049869aecdfb3909f018145b90b32846af8588 100644 (file)
@@ -313,8 +313,8 @@ static ssize_t etb_read(struct file *file, char __user *data,
 
        *ppos += len;
 
-       dev_dbg(drvdata->dev, "%s: %d bytes copied, %d bytes left\n",
-               __func__, len, (int) (depth * 4 - *ppos));
+       dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n",
+               __func__, len, (int)(depth * 4 - *ppos));
        return len;
 }
 
similarity index 98%
rename from drivers/coresight/coresight-replicator.c
rename to drivers/hwtracing/coresight/coresight-replicator.c
index cdf05537d57482ebdc83e428ebc41854650f3e39..75b9abd804e6bdf4d22d35943da29b7ecffbae91 100644 (file)
@@ -107,7 +107,7 @@ static int replicator_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct of_device_id replicator_match[] = {
+static const struct of_device_id replicator_match[] = {
        {.compatible = "arm,coresight-replicator"},
        {}
 };
similarity index 91%
rename from drivers/coresight/coresight-tmc.c
rename to drivers/hwtracing/coresight/coresight-tmc.c
index 3ff232f9ddf78c5a3c4f6146b1ec2dac7aef87b0..7147f3dd363c62a856f84aa3952514c380561c6c 100644 (file)
@@ -533,8 +533,8 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
 
        *ppos += len;
 
-       dev_dbg(drvdata->dev, "%s: %d bytes copied, %d bytes left\n",
-               __func__, len, (int) (drvdata->size - *ppos));
+       dev_dbg(drvdata->dev, "%s: %zu bytes copied, %d bytes left\n",
+               __func__, len, (int)(drvdata->size - *ppos));
        return len;
 }
 
@@ -565,6 +565,59 @@ static const struct file_operations tmc_fops = {
        .llseek         = no_llseek,
 };
 
+static ssize_t status_show(struct device *dev,
+                          struct device_attribute *attr, char *buf)
+{
+       int ret;
+       unsigned long flags;
+       u32 tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg;
+       u32 tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr;
+       u32 devid;
+       struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
+
+       ret = clk_prepare_enable(drvdata->clk);
+       if (ret)
+               goto out;
+
+       spin_lock_irqsave(&drvdata->spinlock, flags);
+       CS_UNLOCK(drvdata->base);
+
+       tmc_rsz = readl_relaxed(drvdata->base + TMC_RSZ);
+       tmc_sts = readl_relaxed(drvdata->base + TMC_STS);
+       tmc_rrp = readl_relaxed(drvdata->base + TMC_RRP);
+       tmc_rwp = readl_relaxed(drvdata->base + TMC_RWP);
+       tmc_trg = readl_relaxed(drvdata->base + TMC_TRG);
+       tmc_ctl = readl_relaxed(drvdata->base + TMC_CTL);
+       tmc_ffsr = readl_relaxed(drvdata->base + TMC_FFSR);
+       tmc_ffcr = readl_relaxed(drvdata->base + TMC_FFCR);
+       tmc_mode = readl_relaxed(drvdata->base + TMC_MODE);
+       tmc_pscr = readl_relaxed(drvdata->base + TMC_PSCR);
+       devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
+
+       CS_LOCK(drvdata->base);
+       spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
+       clk_disable_unprepare(drvdata->clk);
+
+       return sprintf(buf,
+                      "Depth:\t\t0x%x\n"
+                      "Status:\t\t0x%x\n"
+                      "RAM read ptr:\t0x%x\n"
+                      "RAM wrt ptr:\t0x%x\n"
+                      "Trigger cnt:\t0x%x\n"
+                      "Control:\t0x%x\n"
+                      "Flush status:\t0x%x\n"
+                      "Flush ctrl:\t0x%x\n"
+                      "Mode:\t\t0x%x\n"
+                      "PSRC:\t\t0x%x\n"
+                      "DEVID:\t\t0x%x\n",
+                       tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg,
+                       tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr, devid);
+out:
+       return -EINVAL;
+}
+static DEVICE_ATTR_RO(status);
+
 static ssize_t trigger_cntr_show(struct device *dev,
                            struct device_attribute *attr, char *buf)
 {
@@ -593,18 +646,21 @@ static DEVICE_ATTR_RW(trigger_cntr);
 
 static struct attribute *coresight_etb_attrs[] = {
        &dev_attr_trigger_cntr.attr,
+       &dev_attr_status.attr,
        NULL,
 };
 ATTRIBUTE_GROUPS(coresight_etb);
 
 static struct attribute *coresight_etr_attrs[] = {
        &dev_attr_trigger_cntr.attr,
+       &dev_attr_status.attr,
        NULL,
 };
 ATTRIBUTE_GROUPS(coresight_etr);
 
 static struct attribute *coresight_etf_attrs[] = {
        &dev_attr_trigger_cntr.attr,
+       &dev_attr_status.attr,
        NULL,
 };
 ATTRIBUTE_GROUPS(coresight_etf);
similarity index 99%
rename from drivers/coresight/coresight.c
rename to drivers/hwtracing/coresight/coresight.c
index c5def93823570d72b5962be46e07c5a89c1ac892..894531d315b8312f481a15ce64b17bdebe7ffbb4 100644 (file)
@@ -305,7 +305,9 @@ static int coresight_build_paths(struct coresight_device *csdev,
 
        list_add(&csdev->path_link, path);
 
-       if (csdev->type == CORESIGHT_DEV_TYPE_SINK && csdev->activated) {
+       if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
+           csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
+           csdev->activated) {
                if (enable)
                        ret = coresight_enable_path(path);
                else
similarity index 94%
rename from drivers/coresight/of_coresight.c
rename to drivers/hwtracing/coresight/of_coresight.c
index 6f75e9d5b6fb0755d41b3e252daea83179efb4b6..35e51ce93a5c6af546dcc655a63870b0a7326b81 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/platform_device.h>
 #include <linux/amba/bus.h>
 #include <linux/coresight.h>
+#include <linux/cpumask.h>
 #include <asm/smp_plat.h>
 
 
@@ -104,7 +105,7 @@ static int of_coresight_alloc_memory(struct device *dev,
 struct coresight_platform_data *of_get_coresight_platform_data(
                                struct device *dev, struct device_node *node)
 {
-       int i = 0, ret = 0;
+       int i = 0, ret = 0, cpu;
        struct coresight_platform_data *pdata;
        struct of_endpoint endpoint, rendpoint;
        struct device *rdev;
@@ -178,17 +179,10 @@ struct coresight_platform_data *of_get_coresight_platform_data(
        /* Affinity defaults to CPU0 */
        pdata->cpu = 0;
        dn = of_parse_phandle(node, "cpu", 0);
-       if (dn) {
-               const u32 *cell;
-               int len, index;
-               u64 hwid;
-
-               cell = of_get_property(dn, "reg", &len);
-               if (cell) {
-                       hwid = of_read_number(cell, of_n_addr_cells(dn));
-                       index = get_logical_index(hwid);
-                       if (index != -EINVAL)
-                               pdata->cpu = index;
+       for (cpu = 0; dn && cpu < nr_cpu_ids; cpu++) {
+               if (dn == of_get_cpu_node(cpu, NULL)) {
+                       pdata->cpu = cpu;
+                       break;
                }
        }
 
index 0af7361e377f61565258346f3f84a3933b9f15ba..de36237d7c6b45de10fca57cc1870fde5a0620e5 100644 (file)
@@ -56,9 +56,9 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        res = request_mem_region(priv->mapbase, CHAM_HEADER_SIZE,
                                 KBUILD_MODNAME);
-       if (IS_ERR(res)) {
+       if (!res) {
                dev_err(&pdev->dev, "Failed to request PCI memory\n");
-               ret = PTR_ERR(res);
+               ret = -EBUSY;
                goto out_disable;
        }
 
index 191383d8c94d6e1d8e6d67bfc3e0ec5494b1d323..868036f70f8f126c0e81e16fe8bd4af63319909a 100644 (file)
@@ -83,6 +83,15 @@ config FSL_IFC
        bool
        depends on FSL_SOC
 
+config JZ4780_NEMC
+       bool "Ingenic JZ4780 SoC NEMC driver"
+       default y
+       depends on MACH_JZ4780
+       help
+         This driver is for the NAND/External Memory Controller (NEMC) in
+         the Ingenic JZ4780. This controller is used to handle external
+         memory devices such as NAND and SRAM.
+
 source "drivers/memory/tegra/Kconfig"
 
 endif
index 6b6548124473350a453b8982e341e4bb27264458..b670441e3cdf9a2f6816741797722e03b9d72466 100644 (file)
@@ -13,5 +13,6 @@ obj-$(CONFIG_FSL_CORENET_CF)  += fsl-corenet-cf.o
 obj-$(CONFIG_FSL_IFC)          += fsl_ifc.o
 obj-$(CONFIG_MVEBU_DEVBUS)     += mvebu-devbus.o
 obj-$(CONFIG_TEGRA20_MC)       += tegra20-mc.o
+obj-$(CONFIG_JZ4780_NEMC)      += jz4780-nemc.o
 
 obj-$(CONFIG_TEGRA_MC)         += tegra/
diff --git a/drivers/memory/jz4780-nemc.c b/drivers/memory/jz4780-nemc.c
new file mode 100644 (file)
index 0000000..919d192
--- /dev/null
@@ -0,0 +1,391 @@
+/*
+ * JZ4780 NAND/external memory controller (NEMC)
+ *
+ * Copyright (c) 2015 Imagination Technologies
+ * Author: Alex Smith <alex@alex-smith.me.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/math64.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <linux/jz4780-nemc.h>
+
+#define NEMC_SMCRn(n)          (0x14 + (((n) - 1) * 4))
+#define NEMC_NFCSR             0x50
+
+#define NEMC_SMCR_SMT          BIT(0)
+#define NEMC_SMCR_BW_SHIFT     6
+#define NEMC_SMCR_BW_MASK      (0x3 << NEMC_SMCR_BW_SHIFT)
+#define NEMC_SMCR_BW_8         (0 << 6)
+#define NEMC_SMCR_TAS_SHIFT    8
+#define NEMC_SMCR_TAS_MASK     (0xf << NEMC_SMCR_TAS_SHIFT)
+#define NEMC_SMCR_TAH_SHIFT    12
+#define NEMC_SMCR_TAH_MASK     (0xf << NEMC_SMCR_TAH_SHIFT)
+#define NEMC_SMCR_TBP_SHIFT    16
+#define NEMC_SMCR_TBP_MASK     (0xf << NEMC_SMCR_TBP_SHIFT)
+#define NEMC_SMCR_TAW_SHIFT    20
+#define NEMC_SMCR_TAW_MASK     (0xf << NEMC_SMCR_TAW_SHIFT)
+#define NEMC_SMCR_TSTRV_SHIFT  24
+#define NEMC_SMCR_TSTRV_MASK   (0x3f << NEMC_SMCR_TSTRV_SHIFT)
+
+#define NEMC_NFCSR_NFEn(n)     BIT(((n) - 1) << 1)
+#define NEMC_NFCSR_NFCEn(n)    BIT((((n) - 1) << 1) + 1)
+#define NEMC_NFCSR_TNFEn(n)    BIT(16 + (n) - 1)
+
+struct jz4780_nemc {
+       spinlock_t lock;
+       struct device *dev;
+       void __iomem *base;
+       struct clk *clk;
+       uint32_t clk_period;
+       unsigned long banks_present;
+};
+
+/**
+ * jz4780_nemc_num_banks() - count the number of banks referenced by a device
+ * @dev: device to count banks for, must be a child of the NEMC.
+ *
+ * Return: The number of unique NEMC banks referred to by the specified NEMC
+ * child device. Unique here means that a device that references the same bank
+ * multiple times in the its "reg" property will only count once.
+ */
+unsigned int jz4780_nemc_num_banks(struct device *dev)
+{
+       const __be32 *prop;
+       unsigned int bank, count = 0;
+       unsigned long referenced = 0;
+       int i = 0;
+
+       while ((prop = of_get_address(dev->of_node, i++, NULL, NULL))) {
+               bank = of_read_number(prop, 1);
+               if (!(referenced & BIT(bank))) {
+                       referenced |= BIT(bank);
+                       count++;
+               }
+       }
+
+       return count;
+}
+EXPORT_SYMBOL(jz4780_nemc_num_banks);
+
+/**
+ * jz4780_nemc_set_type() - set the type of device connected to a bank
+ * @dev: child device of the NEMC.
+ * @bank: bank number to configure.
+ * @type: type of device connected to the bank.
+ */
+void jz4780_nemc_set_type(struct device *dev, unsigned int bank,
+                         enum jz4780_nemc_bank_type type)
+{
+       struct jz4780_nemc *nemc = dev_get_drvdata(dev->parent);
+       uint32_t nfcsr;
+
+       nfcsr = readl(nemc->base + NEMC_NFCSR);
+
+       /* TODO: Support toggle NAND devices. */
+       switch (type) {
+       case JZ4780_NEMC_BANK_SRAM:
+               nfcsr &= ~(NEMC_NFCSR_TNFEn(bank) | NEMC_NFCSR_NFEn(bank));
+               break;
+       case JZ4780_NEMC_BANK_NAND:
+               nfcsr &= ~NEMC_NFCSR_TNFEn(bank);
+               nfcsr |= NEMC_NFCSR_NFEn(bank);
+               break;
+       }
+
+       writel(nfcsr, nemc->base + NEMC_NFCSR);
+}
+EXPORT_SYMBOL(jz4780_nemc_set_type);
+
+/**
+ * jz4780_nemc_assert() - (de-)assert a NAND device's chip enable pin
+ * @dev: child device of the NEMC.
+ * @bank: bank number of device.
+ * @assert: whether the chip enable pin should be asserted.
+ *
+ * (De-)asserts the chip enable pin for the NAND device connected to the
+ * specified bank.
+ */
+void jz4780_nemc_assert(struct device *dev, unsigned int bank, bool assert)
+{
+       struct jz4780_nemc *nemc = dev_get_drvdata(dev->parent);
+       uint32_t nfcsr;
+
+       nfcsr = readl(nemc->base + NEMC_NFCSR);
+
+       if (assert)
+               nfcsr |= NEMC_NFCSR_NFCEn(bank);
+       else
+               nfcsr &= ~NEMC_NFCSR_NFCEn(bank);
+
+       writel(nfcsr, nemc->base + NEMC_NFCSR);
+}
+EXPORT_SYMBOL(jz4780_nemc_assert);
+
+static uint32_t jz4780_nemc_clk_period(struct jz4780_nemc *nemc)
+{
+       unsigned long rate;
+
+       rate = clk_get_rate(nemc->clk);
+       if (!rate)
+               return 0;
+
+       /* Return in picoseconds. */
+       return div64_ul(1000000000000ull, rate);
+}
+
+static uint32_t jz4780_nemc_ns_to_cycles(struct jz4780_nemc *nemc, uint32_t ns)
+{
+       return ((ns * 1000) + nemc->clk_period - 1) / nemc->clk_period;
+}
+
+static bool jz4780_nemc_configure_bank(struct jz4780_nemc *nemc,
+                                      unsigned int bank,
+                                      struct device_node *node)
+{
+       uint32_t smcr, val, cycles;
+
+       /*
+        * Conversion of tBP and tAW cycle counts to values supported by the
+        * hardware (round up to the next supported value).
+        */
+       static const uint32_t convert_tBP_tAW[] = {
+               0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+
+               /* 11 - 12 -> 12 cycles */
+               11, 11,
+
+               /* 13 - 15 -> 15 cycles */
+               12, 12, 12,
+
+               /* 16 - 20 -> 20 cycles */
+               13, 13, 13, 13, 13,
+
+               /* 21 - 25 -> 25 cycles */
+               14, 14, 14, 14, 14,
+
+               /* 26 - 31 -> 31 cycles */
+               15, 15, 15, 15, 15, 15
+       };
+
+       smcr = readl(nemc->base + NEMC_SMCRn(bank));
+       smcr &= ~NEMC_SMCR_SMT;
+
+       if (!of_property_read_u32(node, "ingenic,nemc-bus-width", &val)) {
+               smcr &= ~NEMC_SMCR_BW_MASK;
+               switch (val) {
+               case 8:
+                       smcr |= NEMC_SMCR_BW_8;
+                       break;
+               default:
+                       /*
+                        * Earlier SoCs support a 16 bit bus width (the 4780
+                        * does not), until those are properly supported, error.
+                        */
+                       dev_err(nemc->dev, "unsupported bus width: %u\n", val);
+                       return false;
+               }
+       }
+
+       if (of_property_read_u32(node, "ingenic,nemc-tAS", &val) == 0) {
+               smcr &= ~NEMC_SMCR_TAS_MASK;
+               cycles = jz4780_nemc_ns_to_cycles(nemc, val);
+               if (cycles > 15) {
+                       dev_err(nemc->dev, "tAS %u is too high (%u cycles)\n",
+                               val, cycles);
+                       return false;
+               }
+
+               smcr |= cycles << NEMC_SMCR_TAS_SHIFT;
+       }
+
+       if (of_property_read_u32(node, "ingenic,nemc-tAH", &val) == 0) {
+               smcr &= ~NEMC_SMCR_TAH_MASK;
+               cycles = jz4780_nemc_ns_to_cycles(nemc, val);
+               if (cycles > 15) {
+                       dev_err(nemc->dev, "tAH %u is too high (%u cycles)\n",
+                               val, cycles);
+                       return false;
+               }
+
+               smcr |= cycles << NEMC_SMCR_TAH_SHIFT;
+       }
+
+       if (of_property_read_u32(node, "ingenic,nemc-tBP", &val) == 0) {
+               smcr &= ~NEMC_SMCR_TBP_MASK;
+               cycles = jz4780_nemc_ns_to_cycles(nemc, val);
+               if (cycles > 31) {
+                       dev_err(nemc->dev, "tBP %u is too high (%u cycles)\n",
+                               val, cycles);
+                       return false;
+               }
+
+               smcr |= convert_tBP_tAW[cycles] << NEMC_SMCR_TBP_SHIFT;
+       }
+
+       if (of_property_read_u32(node, "ingenic,nemc-tAW", &val) == 0) {
+               smcr &= ~NEMC_SMCR_TAW_MASK;
+               cycles = jz4780_nemc_ns_to_cycles(nemc, val);
+               if (cycles > 31) {
+                       dev_err(nemc->dev, "tAW %u is too high (%u cycles)\n",
+                               val, cycles);
+                       return false;
+               }
+
+               smcr |= convert_tBP_tAW[cycles] << NEMC_SMCR_TAW_SHIFT;
+       }
+
+       if (of_property_read_u32(node, "ingenic,nemc-tSTRV", &val) == 0) {
+               smcr &= ~NEMC_SMCR_TSTRV_MASK;
+               cycles = jz4780_nemc_ns_to_cycles(nemc, val);
+               if (cycles > 63) {
+                       dev_err(nemc->dev, "tSTRV %u is too high (%u cycles)\n",
+                               val, cycles);
+                       return false;
+               }
+
+               smcr |= cycles << NEMC_SMCR_TSTRV_SHIFT;
+       }
+
+       writel(smcr, nemc->base + NEMC_SMCRn(bank));
+       return true;
+}
+
+static int jz4780_nemc_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct jz4780_nemc *nemc;
+       struct resource *res;
+       struct device_node *child;
+       const __be32 *prop;
+       unsigned int bank;
+       unsigned long referenced;
+       int i, ret;
+
+       nemc = devm_kzalloc(dev, sizeof(*nemc), GFP_KERNEL);
+       if (!nemc)
+               return -ENOMEM;
+
+       spin_lock_init(&nemc->lock);
+       nemc->dev = dev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       nemc->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(nemc->base)) {
+               dev_err(dev, "failed to get I/O memory\n");
+               return PTR_ERR(nemc->base);
+       }
+
+       writel(0, nemc->base + NEMC_NFCSR);
+
+       nemc->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(nemc->clk)) {
+               dev_err(dev, "failed to get clock\n");
+               return PTR_ERR(nemc->clk);
+       }
+
+       ret = clk_prepare_enable(nemc->clk);
+       if (ret) {
+               dev_err(dev, "failed to enable clock: %d\n", ret);
+               return ret;
+       }
+
+       nemc->clk_period = jz4780_nemc_clk_period(nemc);
+       if (!nemc->clk_period) {
+               dev_err(dev, "failed to calculate clock period\n");
+               clk_disable_unprepare(nemc->clk);
+               return -EINVAL;
+       }
+
+       /*
+        * Iterate over child devices, check that they do not conflict with
+        * each other, and register child devices for them. If a child device
+        * has invalid properties, it is ignored and no platform device is
+        * registered for it.
+        */
+       for_each_child_of_node(nemc->dev->of_node, child) {
+               referenced = 0;
+               i = 0;
+               while ((prop = of_get_address(child, i++, NULL, NULL))) {
+                       bank = of_read_number(prop, 1);
+                       if (bank < 1 || bank >= JZ4780_NEMC_NUM_BANKS) {
+                               dev_err(nemc->dev,
+                                       "%s requests invalid bank %u\n",
+                                       child->full_name, bank);
+
+                               /* Will continue the outer loop below. */
+                               referenced = 0;
+                               break;
+                       }
+
+                       referenced |= BIT(bank);
+               }
+
+               if (!referenced) {
+                       dev_err(nemc->dev, "%s has no addresses\n",
+                               child->full_name);
+                       continue;
+               } else if (nemc->banks_present & referenced) {
+                       dev_err(nemc->dev, "%s conflicts with another node\n",
+                               child->full_name);
+                       continue;
+               }
+
+               /* Configure bank parameters. */
+               for_each_set_bit(bank, &referenced, JZ4780_NEMC_NUM_BANKS) {
+                       if (!jz4780_nemc_configure_bank(nemc, bank, child)) {
+                               referenced = 0;
+                               break;
+                       }
+               }
+
+               if (referenced) {
+                       if (of_platform_device_create(child, NULL, nemc->dev))
+                               nemc->banks_present |= referenced;
+               }
+       }
+
+       platform_set_drvdata(pdev, nemc);
+       dev_info(dev, "JZ4780 NEMC initialised\n");
+       return 0;
+}
+
+static int jz4780_nemc_remove(struct platform_device *pdev)
+{
+       struct jz4780_nemc *nemc = platform_get_drvdata(pdev);
+
+       clk_disable_unprepare(nemc->clk);
+       return 0;
+}
+
+static const struct of_device_id jz4780_nemc_dt_match[] = {
+       { .compatible = "ingenic,jz4780-nemc" },
+       {},
+};
+
+static struct platform_driver jz4780_nemc_driver = {
+       .probe          = jz4780_nemc_probe,
+       .remove         = jz4780_nemc_remove,
+       .driver = {
+               .name   = "jz4780-nemc",
+               .of_match_table = of_match_ptr(jz4780_nemc_dt_match),
+       },
+};
+
+static int __init jz4780_nemc_init(void)
+{
+       return platform_driver_register(&jz4780_nemc_driver);
+}
+subsys_initcall(jz4780_nemc_init);
index 4c4a59b2553797b52cde8c4465da82d979eee1ed..7f90ce5a569a434e68da725d3a71b59bd86b5453 100644 (file)
@@ -230,6 +230,8 @@ static const struct i2c_device_id bh1780_id[] = {
        { },
 };
 
+MODULE_DEVICE_TABLE(i2c, bh1780_id);
+
 #ifdef CONFIG_OF
 static const struct of_device_id of_bh1780_match[] = {
        { .compatible = "rohm,bh1780gli", },
index 06166ac000e08c8788725916b5a3a68469ac7e3a..0b1bd85e4ae677f115c1b60b45f156c8ddd67327 100644 (file)
@@ -479,6 +479,7 @@ static int fpga_program_block(struct fpga_dev *priv, void *buf, size_t count)
 static noinline int fpga_program_cpu(struct fpga_dev *priv)
 {
        int ret;
+       unsigned long timeout;
 
        /* Disable the programmer */
        fpga_programmer_disable(priv);
@@ -497,8 +498,8 @@ static noinline int fpga_program_cpu(struct fpga_dev *priv)
                goto out_disable_controller;
 
        /* Wait for the interrupt handler to signal that programming finished */
-       ret = wait_for_completion_timeout(&priv->completion, 2 * HZ);
-       if (!ret) {
+       timeout = wait_for_completion_timeout(&priv->completion, 2 * HZ);
+       if (!timeout) {
                dev_err(priv->dev, "Timed out waiting for completion\n");
                ret = -ETIMEDOUT;
                goto out_disable_controller;
@@ -536,6 +537,7 @@ static noinline int fpga_program_dma(struct fpga_dev *priv)
        struct sg_table table;
        dma_cookie_t cookie;
        int ret, i;
+       unsigned long timeout;
 
        /* Disable the programmer */
        fpga_programmer_disable(priv);
@@ -623,8 +625,8 @@ static noinline int fpga_program_dma(struct fpga_dev *priv)
        dev_dbg(priv->dev, "enabled the controller\n");
 
        /* Wait for the interrupt handler to signal that programming finished */
-       ret = wait_for_completion_timeout(&priv->completion, 2 * HZ);
-       if (!ret) {
+       timeout = wait_for_completion_timeout(&priv->completion, 2 * HZ);
+       if (!timeout) {
                dev_err(priv->dev, "Timed out waiting for completion\n");
                ret = -ETIMEDOUT;
                goto out_disable_controller;
@@ -1142,7 +1144,7 @@ out_return:
        return ret;
 }
 
-static struct of_device_id fpga_of_match[] = {
+static const struct of_device_id fpga_of_match[] = {
        { .compatible = "carma,fpga-programmer", },
        {},
 };
index 68cdfe151bdb8481f964e6a12039e95dd85a8399..5aba3fd789deb25ddced039dab80bf4ae0489692 100644 (file)
@@ -1486,7 +1486,7 @@ static int data_of_remove(struct platform_device *op)
        return 0;
 }
 
-static struct of_device_id data_of_match[] = {
+static const struct of_device_id data_of_match[] = {
        { .compatible = "carma,carma-fpga", },
        {},
 };
index 3ef4627f9cb1552a7585787f6da81cb880c4b784..4739689d23ad08aec40c4478d57e3fae3a118adb 100644 (file)
@@ -950,6 +950,7 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3)
        struct lis3lv02d_platform_data *pdata;
        struct device_node *np = lis3->of_node;
        u32 val;
+       s32 sval;
 
        if (!lis3->of_node)
                return 0;
@@ -1031,6 +1032,23 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3)
                pdata->wakeup_flags |= LIS3_WAKEUP_Z_LO;
        if (of_get_property(np, "st,wakeup-z-hi", NULL))
                pdata->wakeup_flags |= LIS3_WAKEUP_Z_HI;
+       if (of_get_property(np, "st,wakeup-threshold", &val))
+               pdata->wakeup_thresh = val;
+
+       if (of_get_property(np, "st,wakeup2-x-lo", NULL))
+               pdata->wakeup_flags2 |= LIS3_WAKEUP_X_LO;
+       if (of_get_property(np, "st,wakeup2-x-hi", NULL))
+               pdata->wakeup_flags2 |= LIS3_WAKEUP_X_HI;
+       if (of_get_property(np, "st,wakeup2-y-lo", NULL))
+               pdata->wakeup_flags2 |= LIS3_WAKEUP_Y_LO;
+       if (of_get_property(np, "st,wakeup2-y-hi", NULL))
+               pdata->wakeup_flags2 |= LIS3_WAKEUP_Y_HI;
+       if (of_get_property(np, "st,wakeup2-z-lo", NULL))
+               pdata->wakeup_flags2 |= LIS3_WAKEUP_Z_LO;
+       if (of_get_property(np, "st,wakeup2-z-hi", NULL))
+               pdata->wakeup_flags2 |= LIS3_WAKEUP_Z_HI;
+       if (of_get_property(np, "st,wakeup2-threshold", &val))
+               pdata->wakeup_thresh2 = val;
 
        if (!of_property_read_u32(np, "st,highpass-cutoff-hz", &val)) {
                switch (val) {
@@ -1054,29 +1072,29 @@ int lis3lv02d_init_dt(struct lis3lv02d *lis3)
        if (of_get_property(np, "st,hipass2-disable", NULL))
                pdata->hipass_ctrl |= LIS3_HIPASS2_DISABLE;
 
-       if (of_get_property(np, "st,axis-x", &val))
-               pdata->axis_x = val;
-       if (of_get_property(np, "st,axis-y", &val))
-               pdata->axis_y = val;
-       if (of_get_property(np, "st,axis-z", &val))
-               pdata->axis_z = val;
+       if (of_property_read_s32(np, "st,axis-x", &sval) == 0)
+               pdata->axis_x = sval;
+       if (of_property_read_s32(np, "st,axis-y", &sval) == 0)
+               pdata->axis_y = sval;
+       if (of_property_read_s32(np, "st,axis-z", &sval) == 0)
+               pdata->axis_z = sval;
 
        if (of_get_property(np, "st,default-rate", NULL))
                pdata->default_rate = val;
 
-       if (of_get_property(np, "st,min-limit-x", &val))
-               pdata->st_min_limits[0] = val;
-       if (of_get_property(np, "st,min-limit-y", &val))
-               pdata->st_min_limits[1] = val;
-       if (of_get_property(np, "st,min-limit-z", &val))
-               pdata->st_min_limits[2] = val;
-
-       if (of_get_property(np, "st,max-limit-x", &val))
-               pdata->st_max_limits[0] = val;
-       if (of_get_property(np, "st,max-limit-y", &val))
-               pdata->st_max_limits[1] = val;
-       if (of_get_property(np, "st,max-limit-z", &val))
-               pdata->st_max_limits[2] = val;
+       if (of_property_read_s32(np, "st,min-limit-x", &sval) == 0)
+               pdata->st_min_limits[0] = sval;
+       if (of_property_read_s32(np, "st,min-limit-y", &sval) == 0)
+               pdata->st_min_limits[1] = sval;
+       if (of_property_read_s32(np, "st,min-limit-z", &sval) == 0)
+               pdata->st_min_limits[2] = sval;
+
+       if (of_property_read_s32(np, "st,max-limit-x", &sval) == 0)
+               pdata->st_max_limits[0] = sval;
+       if (of_property_read_s32(np, "st,max-limit-y", &sval) == 0)
+               pdata->st_max_limits[1] = sval;
+       if (of_property_read_s32(np, "st,max-limit-z", &sval) == 0)
+               pdata->st_max_limits[2] = sval;
 
 
        lis3->pdata = pdata;
index 63fe096d44621a3c771b500452240e234da6357b..e3e7f1dc27ba5df5daf8ad647953c10d92a69caa 100644 (file)
@@ -106,7 +106,7 @@ static union axis_conversion lis3lv02d_axis_map =
        { .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } };
 
 #ifdef CONFIG_OF
-static struct of_device_id lis3lv02d_i2c_dt_ids[] = {
+static const struct of_device_id lis3lv02d_i2c_dt_ids[] = {
        { .compatible = "st,lis3lv02d" },
        {}
 };
index bd06d0cfac45e15a2f1ee21f1e4ab856867e0e98..b2f6e1651ac941a37d5ff57bfde8ab0b286442da 100644 (file)
@@ -61,7 +61,7 @@ static union axis_conversion lis3lv02d_axis_normal =
        { .as_array = { 1, 2, 3 } };
 
 #ifdef CONFIG_OF
-static struct of_device_id lis302dl_spi_dt_ids[] = {
+static const struct of_device_id lis302dl_spi_dt_ids[] = {
        { .compatible = "st,lis302dl-spi" },
        {}
 };
index 8ebc6cda1373d6c7e543d785064559720bbf0391..518914a82b83755ba05d6bdb214aabcf2c05d563 100644 (file)
@@ -21,3 +21,6 @@ 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
+
+mei-$(CONFIG_EVENT_TRACING) += mei-trace.o
+CFLAGS_mei-trace.o = -I$(src)
index 40ea639fa413a92f0239e1835894e73d36ddc1c1..d2cd53e3fac378b27e0694e04ea8cee0438d784d 100644 (file)
@@ -48,10 +48,7 @@ void mei_amthif_reset_params(struct mei_device *dev)
 {
        /* reset iamthif parameters. */
        dev->iamthif_current_cb = NULL;
-       dev->iamthif_msg_buf_size = 0;
-       dev->iamthif_msg_buf_index = 0;
        dev->iamthif_canceled = false;
-       dev->iamthif_ioctl = false;
        dev->iamthif_state = MEI_IAMTHIF_IDLE;
        dev->iamthif_timer = 0;
        dev->iamthif_stall_timer = 0;
@@ -69,7 +66,6 @@ int mei_amthif_host_init(struct mei_device *dev)
 {
        struct mei_cl *cl = &dev->iamthif_cl;
        struct mei_me_client *me_cl;
-       unsigned char *msg_buf;
        int ret;
 
        dev->iamthif_state = MEI_IAMTHIF_IDLE;
@@ -90,18 +86,6 @@ int mei_amthif_host_init(struct mei_device *dev)
        dev->iamthif_mtu = me_cl->props.max_msg_length;
        dev_dbg(dev->dev, "IAMTHIF_MTU = %d\n", dev->iamthif_mtu);
 
-       kfree(dev->iamthif_msg_buf);
-       dev->iamthif_msg_buf = NULL;
-
-       /* allocate storage for ME message buffer */
-       msg_buf = kcalloc(dev->iamthif_mtu,
-                       sizeof(unsigned char), GFP_KERNEL);
-       if (!msg_buf) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       dev->iamthif_msg_buf = msg_buf;
 
        ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID);
        if (ret < 0) {
@@ -194,30 +178,33 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
                dev_dbg(dev->dev, "woke up from sleep\n");
        }
 
+       if (cb->status) {
+               rets = cb->status;
+               dev_dbg(dev->dev, "read operation failed %d\n", rets);
+               goto free;
+       }
 
        dev_dbg(dev->dev, "Got amthif data\n");
        dev->iamthif_timer = 0;
 
-       if (cb) {
-               timeout = cb->read_time +
-                       mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
-               dev_dbg(dev->dev, "amthif timeout = %lud\n",
-                               timeout);
-
-               if  (time_after(jiffies, timeout)) {
-                       dev_dbg(dev->dev, "amthif Time out\n");
-                       /* 15 sec for the message has expired */
-                       list_del(&cb->list);
-                       rets = -ETIME;
-                       goto free;
-               }
+       timeout = cb->read_time +
+               mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
+       dev_dbg(dev->dev, "amthif timeout = %lud\n",
+                       timeout);
+
+       if  (time_after(jiffies, timeout)) {
+               dev_dbg(dev->dev, "amthif Time out\n");
+               /* 15 sec for the message has expired */
+               list_del_init(&cb->list);
+               rets = -ETIME;
+               goto free;
        }
        /* if the whole message will fit remove it from the list */
        if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset))
-               list_del(&cb->list);
+               list_del_init(&cb->list);
        else if (cb->buf_idx > 0 && cb->buf_idx <= *offset) {
                /* end of the message has been reached */
-               list_del(&cb->list);
+               list_del_init(&cb->list);
                rets = 0;
                goto free;
        }
@@ -225,15 +212,15 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
                 * remove message from deletion list
                 */
 
-       dev_dbg(dev->dev, "amthif cb->response_buffer size - %d\n",
-           cb->response_buffer.size);
+       dev_dbg(dev->dev, "amthif cb->buf size - %d\n",
+           cb->buf.size);
        dev_dbg(dev->dev, "amthif cb->buf_idx - %lu\n", cb->buf_idx);
 
        /* length is being truncated to PAGE_SIZE, however,
         * the buf_idx may point beyond */
        length = min_t(size_t, length, (cb->buf_idx - *offset));
 
-       if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) {
+       if (copy_to_user(ubuf, cb->buf.data + *offset, length)) {
                dev_dbg(dev->dev, "failed to copy data to userland\n");
                rets = -EFAULT;
        } else {
@@ -252,126 +239,88 @@ out:
 }
 
 /**
- * mei_amthif_send_cmd - send amthif command to the ME
+ * mei_amthif_read_start - queue message for sending read credential
  *
- * @dev: the device structure
- * @cb: mei call back struct
+ * @cl: host client
+ * @file: file pointer of message recipient
  *
  * Return: 0 on success, <0 on failure.
- *
  */
-static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb)
+static int mei_amthif_read_start(struct mei_cl *cl, struct file *file)
 {
-       struct mei_msg_hdr mei_hdr;
-       struct mei_cl *cl;
-       int ret;
-
-       if (!dev || !cb)
-               return -ENODEV;
+       struct mei_device *dev = cl->dev;
+       struct mei_cl_cb *cb;
+       size_t length = dev->iamthif_mtu;
+       int rets;
 
-       dev_dbg(dev->dev, "write data to amthif client.\n");
+       cb = mei_io_cb_init(cl, MEI_FOP_READ, file);
+       if (!cb) {
+               rets = -ENOMEM;
+               goto err;
+       }
 
-       dev->iamthif_state = MEI_IAMTHIF_WRITING;
-       dev->iamthif_current_cb = cb;
-       dev->iamthif_file_object = cb->file_object;
-       dev->iamthif_canceled = false;
-       dev->iamthif_ioctl = true;
-       dev->iamthif_msg_buf_size = cb->request_buffer.size;
-       memcpy(dev->iamthif_msg_buf, cb->request_buffer.data,
-              cb->request_buffer.size);
-       cl = &dev->iamthif_cl;
+       rets = mei_io_cb_alloc_buf(cb, length);
+       if (rets)
+               goto err;
 
-       ret = mei_cl_flow_ctrl_creds(cl);
-       if (ret < 0)
-               return ret;
+       list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
 
-       if (ret && mei_hbuf_acquire(dev)) {
-               ret = 0;
-               if (cb->request_buffer.size > mei_hbuf_max_len(dev)) {
-                       mei_hdr.length = mei_hbuf_max_len(dev);
-                       mei_hdr.msg_complete = 0;
-               } else {
-                       mei_hdr.length = cb->request_buffer.size;
-                       mei_hdr.msg_complete = 1;
-               }
+       dev->iamthif_state = MEI_IAMTHIF_READING;
+       dev->iamthif_file_object = cb->file_object;
+       dev->iamthif_current_cb = cb;
 
-               mei_hdr.host_addr = cl->host_client_id;
-               mei_hdr.me_addr = cl->me_client_id;
-               mei_hdr.reserved = 0;
-               mei_hdr.internal = 0;
-               dev->iamthif_msg_buf_index += mei_hdr.length;
-               ret = mei_write_message(dev, &mei_hdr, dev->iamthif_msg_buf);
-               if (ret)
-                       return ret;
-
-               if (mei_hdr.msg_complete) {
-                       if (mei_cl_flow_ctrl_reduce(cl))
-                               return -EIO;
-                       dev->iamthif_flow_control_pending = true;
-                       dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL;
-                       dev_dbg(dev->dev, "add amthif cb to write waiting list\n");
-                       dev->iamthif_current_cb = cb;
-                       dev->iamthif_file_object = cb->file_object;
-                       list_add_tail(&cb->list, &dev->write_waiting_list.list);
-               } else {
-                       dev_dbg(dev->dev, "message does not complete, so add amthif cb to write list.\n");
-                       list_add_tail(&cb->list, &dev->write_list.list);
-               }
-       } else {
-               list_add_tail(&cb->list, &dev->write_list.list);
-       }
        return 0;
+err:
+       mei_io_cb_free(cb);
+       return rets;
 }
 
 /**
- * mei_amthif_write - write amthif data to amthif client
+ * mei_amthif_send_cmd - send amthif command to the ME
  *
- * @dev: the device structure
+ * @cl: the host client
  * @cb: mei call back struct
  *
  * Return: 0 on success, <0 on failure.
- *
  */
-int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb)
+static int mei_amthif_send_cmd(struct mei_cl *cl, struct mei_cl_cb *cb)
 {
+       struct mei_device *dev;
        int ret;
 
-       if (!dev || !cb)
+       if (!cl->dev || !cb)
                return -ENODEV;
 
-       ret = mei_io_cb_alloc_resp_buf(cb, dev->iamthif_mtu);
-       if (ret)
+       dev = cl->dev;
+
+       dev->iamthif_state = MEI_IAMTHIF_WRITING;
+       dev->iamthif_current_cb = cb;
+       dev->iamthif_file_object = cb->file_object;
+       dev->iamthif_canceled = false;
+
+       ret = mei_cl_write(cl, cb, false);
+       if (ret < 0)
                return ret;
 
-       cb->fop_type = MEI_FOP_WRITE;
+       if (cb->completed)
+               cb->status = mei_amthif_read_start(cl, cb->file_object);
 
-       if (!list_empty(&dev->amthif_cmd_list.list) ||
-           dev->iamthif_state != MEI_IAMTHIF_IDLE) {
-               dev_dbg(dev->dev,
-                       "amthif state = %d\n", dev->iamthif_state);
-               dev_dbg(dev->dev, "AMTHIF: add cb to the wait list\n");
-               list_add_tail(&cb->list, &dev->amthif_cmd_list.list);
-               return 0;
-       }
-       return mei_amthif_send_cmd(dev, cb);
+       return 0;
 }
+
 /**
  * mei_amthif_run_next_cmd - send next amt command from queue
  *
  * @dev: the device structure
+ *
+ * Return: 0 on success, <0 on failure.
  */
-void mei_amthif_run_next_cmd(struct mei_device *dev)
+int mei_amthif_run_next_cmd(struct mei_device *dev)
 {
+       struct mei_cl *cl = &dev->iamthif_cl;
        struct mei_cl_cb *cb;
-       int ret;
-
-       if (!dev)
-               return;
 
-       dev->iamthif_msg_buf_size = 0;
-       dev->iamthif_msg_buf_index = 0;
        dev->iamthif_canceled = false;
-       dev->iamthif_ioctl = true;
        dev->iamthif_state = MEI_IAMTHIF_IDLE;
        dev->iamthif_timer = 0;
        dev->iamthif_file_object = NULL;
@@ -381,13 +330,48 @@ void mei_amthif_run_next_cmd(struct mei_device *dev)
        cb = list_first_entry_or_null(&dev->amthif_cmd_list.list,
                                        typeof(*cb), list);
        if (!cb)
-               return;
-       list_del(&cb->list);
-       ret =  mei_amthif_send_cmd(dev, cb);
-       if (ret)
-               dev_warn(dev->dev, "amthif write failed status = %d\n", ret);
+               return 0;
+
+       list_del_init(&cb->list);
+       return mei_amthif_send_cmd(cl, cb);
 }
 
+/**
+ * mei_amthif_write - write amthif data to amthif client
+ *
+ * @cl: host client
+ * @cb: mei call back struct
+ *
+ * Return: 0 on success, <0 on failure.
+ */
+int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb)
+{
+
+       struct mei_device *dev;
+
+       if (WARN_ON(!cl || !cl->dev))
+               return -ENODEV;
+
+       if (WARN_ON(!cb))
+               return -EINVAL;
+
+       dev = cl->dev;
+
+       list_add_tail(&cb->list, &dev->amthif_cmd_list.list);
+       return mei_amthif_run_next_cmd(dev);
+}
+
+/**
+ * mei_amthif_poll - the amthif poll function
+ *
+ * @dev: the device structure
+ * @file: pointer to file structure
+ * @wait: pointer to poll_table structure
+ *
+ * Return: poll mask
+ *
+ * Locking: called under "dev->device_lock" lock
+ */
 
 unsigned int mei_amthif_poll(struct mei_device *dev,
                struct file *file, poll_table *wait)
@@ -396,19 +380,12 @@ unsigned int mei_amthif_poll(struct mei_device *dev,
 
        poll_wait(file, &dev->iamthif_cl.wait, wait);
 
-       mutex_lock(&dev->device_lock);
-       if (!mei_cl_is_connected(&dev->iamthif_cl)) {
-
-               mask = POLLERR;
-
-       } else if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
-                  dev->iamthif_file_object == file) {
+       if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
+           dev->iamthif_file_object == file) {
 
-               mask |= (POLLIN | POLLRDNORM);
-               dev_dbg(dev->dev, "run next amthif cb\n");
+               mask |= POLLIN | POLLRDNORM;
                mei_amthif_run_next_cmd(dev);
        }
-       mutex_unlock(&dev->device_lock);
 
        return mask;
 }
@@ -427,71 +404,14 @@ unsigned int mei_amthif_poll(struct mei_device *dev,
 int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
                         struct mei_cl_cb *cmpl_list)
 {
-       struct mei_device *dev = cl->dev;
-       struct mei_msg_hdr mei_hdr;
-       size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index;
-       u32 msg_slots = mei_data2slots(len);
-       int slots;
-       int rets;
-
-       rets = mei_cl_flow_ctrl_creds(cl);
-       if (rets < 0)
-               return rets;
-
-       if (rets == 0) {
-               cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
-               return 0;
-       }
-
-       mei_hdr.host_addr = cl->host_client_id;
-       mei_hdr.me_addr = cl->me_client_id;
-       mei_hdr.reserved = 0;
-       mei_hdr.internal = 0;
-
-       slots = mei_hbuf_empty_slots(dev);
-
-       if (slots >= msg_slots) {
-               mei_hdr.length = len;
-               mei_hdr.msg_complete = 1;
-       /* Split the message only if we can write the whole host buffer */
-       } else if (slots == dev->hbuf_depth) {
-               msg_slots = slots;
-               len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
-               mei_hdr.length = len;
-               mei_hdr.msg_complete = 0;
-       } else {
-               /* wait for next time the host buffer is empty */
-               return 0;
-       }
-
-       dev_dbg(dev->dev, MEI_HDR_FMT,  MEI_HDR_PRM(&mei_hdr));
-
-       rets = mei_write_message(dev, &mei_hdr,
-                       dev->iamthif_msg_buf + dev->iamthif_msg_buf_index);
-       if (rets) {
-               dev->iamthif_state = MEI_IAMTHIF_IDLE;
-               cl->status = rets;
-               list_del(&cb->list);
-               return rets;
-       }
-
-       if (mei_cl_flow_ctrl_reduce(cl))
-               return -EIO;
-
-       dev->iamthif_msg_buf_index += mei_hdr.length;
-       cl->status = 0;
-
-       if (mei_hdr.msg_complete) {
-               dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL;
-               dev->iamthif_flow_control_pending = true;
-
-               /* save iamthif cb sent to amthif client */
-               cb->buf_idx = dev->iamthif_msg_buf_index;
-               dev->iamthif_current_cb = cb;
+       int ret;
 
-               list_move_tail(&cb->list, &dev->write_waiting_list.list);
-       }
+       ret = mei_cl_irq_write(cl, cb, cmpl_list);
+       if (ret)
+               return ret;
 
+       if (cb->completed)
+               cb->status = mei_amthif_read_start(cl, cb->file_object);
 
        return 0;
 }
@@ -500,83 +420,35 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
  * mei_amthif_irq_read_msg - read routine after ISR to
  *                     handle the read amthif message
  *
- * @dev: the device structure
+ * @cl: mei client
  * @mei_hdr: header of amthif message
- * @complete_list: An instance of our list structure
+ * @cmpl_list: completed callbacks list
  *
- * Return: 0 on success, <0 on failure.
+ * Return: -ENODEV if cb is NULL 0 otherwise; error message is in cb->status
  */
-int mei_amthif_irq_read_msg(struct mei_device *dev,
+int mei_amthif_irq_read_msg(struct mei_cl *cl,
                            struct mei_msg_hdr *mei_hdr,
-                           struct mei_cl_cb *complete_list)
+                           struct mei_cl_cb *cmpl_list)
 {
-       struct mei_cl_cb *cb;
-       unsigned char *buffer;
-
-       BUG_ON(mei_hdr->me_addr != dev->iamthif_cl.me_client_id);
-       BUG_ON(dev->iamthif_state != MEI_IAMTHIF_READING);
+       struct mei_device *dev;
+       int ret;
 
-       buffer = dev->iamthif_msg_buf + dev->iamthif_msg_buf_index;
-       BUG_ON(dev->iamthif_mtu < dev->iamthif_msg_buf_index + mei_hdr->length);
+       dev = cl->dev;
 
-       mei_read_slots(dev, buffer, mei_hdr->length);
+       if (dev->iamthif_state != MEI_IAMTHIF_READING)
+               return 0;
 
-       dev->iamthif_msg_buf_index += mei_hdr->length;
+       ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list);
+       if (ret)
+               return ret;
 
        if (!mei_hdr->msg_complete)
                return 0;
 
-       dev_dbg(dev->dev, "amthif_message_buffer_index =%d\n",
-                       mei_hdr->length);
-
        dev_dbg(dev->dev, "completed amthif read.\n ");
-       if (!dev->iamthif_current_cb)
-               return -ENODEV;
-
-       cb = dev->iamthif_current_cb;
        dev->iamthif_current_cb = NULL;
-
        dev->iamthif_stall_timer = 0;
-       cb->buf_idx = dev->iamthif_msg_buf_index;
-       cb->read_time = jiffies;
-       if (dev->iamthif_ioctl) {
-               /* found the iamthif cb */
-               dev_dbg(dev->dev, "complete the amthif read cb.\n ");
-               dev_dbg(dev->dev, "add the amthif read cb to complete.\n ");
-               list_add_tail(&cb->list, &complete_list->list);
-       }
-       return 0;
-}
-
-/**
- * mei_amthif_irq_read - prepares to read amthif data.
- *
- * @dev: the device structure.
- * @slots: free slots.
- *
- * Return: 0, OK; otherwise, error.
- */
-int mei_amthif_irq_read(struct mei_device *dev, s32 *slots)
-{
-       u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));
-
-       if (*slots < msg_slots)
-               return -EMSGSIZE;
-
-       *slots -= msg_slots;
-
-       if (mei_hbm_cl_flow_control_req(dev, &dev->iamthif_cl)) {
-               dev_dbg(dev->dev, "iamthif flow control failed\n");
-               return -EIO;
-       }
 
-       dev_dbg(dev->dev, "iamthif flow control success\n");
-       dev->iamthif_state = MEI_IAMTHIF_READING;
-       dev->iamthif_flow_control_pending = false;
-       dev->iamthif_msg_buf_index = 0;
-       dev->iamthif_msg_buf_size = 0;
-       dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER;
-       dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
        return 0;
 }
 
@@ -588,17 +460,30 @@ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots)
  */
 void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb)
 {
+
+       if (cb->fop_type == MEI_FOP_WRITE) {
+               if (!cb->status) {
+                       dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER;
+                       mei_io_cb_free(cb);
+                       return;
+               }
+               /*
+                * in case of error enqueue the write cb to complete read list
+                * so it can be propagated to the reader
+                */
+               list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list);
+               wake_up_interruptible(&dev->iamthif_cl.wait);
+               return;
+       }
+
        if (dev->iamthif_canceled != 1) {
                dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE;
                dev->iamthif_stall_timer = 0;
-               memcpy(cb->response_buffer.data,
-                               dev->iamthif_msg_buf,
-                               dev->iamthif_msg_buf_index);
                list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list);
                dev_dbg(dev->dev, "amthif read completed\n");
                dev->iamthif_timer = jiffies;
                dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n",
-                               dev->iamthif_timer);
+                       dev->iamthif_timer);
        } else {
                mei_amthif_run_next_cmd(dev);
        }
@@ -623,26 +508,22 @@ void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb)
 static bool mei_clear_list(struct mei_device *dev,
                const struct file *file, struct list_head *mei_cb_list)
 {
-       struct mei_cl_cb *cb_pos = NULL;
-       struct mei_cl_cb *cb_next = NULL;
+       struct mei_cl *cl = &dev->iamthif_cl;
+       struct mei_cl_cb *cb, *next;
        bool removed = false;
 
        /* list all list member */
-       list_for_each_entry_safe(cb_pos, cb_next, mei_cb_list, list) {
+       list_for_each_entry_safe(cbnext, mei_cb_list, list) {
                /* check if list member associated with a file */
-               if (file == cb_pos->file_object) {
-                       /* remove member from the list */
-                       list_del(&cb_pos->list);
+               if (file == cb->file_object) {
                        /* check if cb equal to current iamthif cb */
-                       if (dev->iamthif_current_cb == cb_pos) {
+                       if (dev->iamthif_current_cb == cb) {
                                dev->iamthif_current_cb = NULL;
                                /* send flow control to iamthif client */
-                               mei_hbm_cl_flow_control_req(dev,
-                                                       &dev->iamthif_cl);
+                               mei_hbm_cl_flow_control_req(dev, cl);
                        }
                        /* free all allocated buffers */
-                       mei_io_cb_free(cb_pos);
-                       cb_pos = NULL;
+                       mei_io_cb_free(cb);
                        removed = true;
                }
        }
index be767f4db26a137bb93043bf448891031428e9fe..4cf38c39878aa1787de6cb36cf2a5c045f01b875 100644 (file)
@@ -238,7 +238,7 @@ static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
        dev = cl->dev;
 
        mutex_lock(&dev->device_lock);
-       if (cl->state != MEI_FILE_CONNECTED) {
+       if (!mei_cl_is_connected(cl)) {
                rets = -ENODEV;
                goto out;
        }
@@ -255,17 +255,13 @@ static ssize_t ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
                goto out;
        }
 
-       cb = mei_io_cb_init(cl, NULL);
+       cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, NULL);
        if (!cb) {
                rets = -ENOMEM;
                goto out;
        }
 
-       rets = mei_io_cb_alloc_req_buf(cb, length);
-       if (rets < 0)
-               goto out;
-
-       memcpy(cb->request_buffer.data, buf, length);
+       memcpy(cb->buf.data, buf, length);
 
        rets = mei_cl_write(cl, cb, blocking);
 
@@ -292,20 +288,21 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
 
        mutex_lock(&dev->device_lock);
 
-       if (!cl->read_cb) {
-               rets = mei_cl_read_start(cl, length);
-               if (rets < 0)
-                       goto out;
-       }
+       cb = mei_cl_read_cb(cl, NULL);
+       if (cb)
+               goto copy;
 
-       if (cl->reading_state != MEI_READ_COMPLETE &&
-           !waitqueue_active(&cl->rx_wait)) {
+       rets = mei_cl_read_start(cl, length, NULL);
+       if (rets && rets != -EBUSY)
+               goto out;
+
+       if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) {
 
                mutex_unlock(&dev->device_lock);
 
                if (wait_event_interruptible(cl->rx_wait,
-                               cl->reading_state == MEI_READ_COMPLETE  ||
-                               mei_cl_is_transitioning(cl))) {
+                               (!list_empty(&cl->rd_completed)) ||
+                               (!mei_cl_is_connected(cl)))) {
 
                        if (signal_pending(current))
                                return -EINTR;
@@ -313,23 +310,31 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length)
                }
 
                mutex_lock(&dev->device_lock);
-       }
 
-       cb = cl->read_cb;
+               if (!mei_cl_is_connected(cl)) {
+                       rets = -EBUSY;
+                       goto out;
+               }
+       }
 
-       if (cl->reading_state != MEI_READ_COMPLETE) {
+       cb = mei_cl_read_cb(cl, NULL);
+       if (!cb) {
                rets = 0;
                goto out;
        }
 
+copy:
+       if (cb->status) {
+               rets = cb->status;
+               goto free;
+       }
+
        r_length = min_t(size_t, length, cb->buf_idx);
-       memcpy(buf, cb->response_buffer.data, r_length);
+       memcpy(buf, cb->buf.data, r_length);
        rets = r_length;
 
+free:
        mei_io_cb_free(cb);
-       cl->reading_state = MEI_IDLE;
-       cl->read_cb = NULL;
-
 out:
        mutex_unlock(&dev->device_lock);
 
@@ -386,7 +391,7 @@ static void mei_bus_event_work(struct work_struct *work)
        device->events = 0;
 
        /* Prepare for the next read */
-       mei_cl_read_start(device->cl, 0);
+       mei_cl_read_start(device->cl, 0, NULL);
 }
 
 int mei_cl_register_event_cb(struct mei_cl_device *device,
@@ -400,7 +405,7 @@ int mei_cl_register_event_cb(struct mei_cl_device *device,
        device->event_context = context;
        INIT_WORK(&device->event_work, mei_bus_event_work);
 
-       mei_cl_read_start(device->cl, 0);
+       mei_cl_read_start(device->cl, 0, NULL);
 
        return 0;
 }
@@ -441,8 +446,8 @@ int mei_cl_enable_device(struct mei_cl_device *device)
 
        mutex_unlock(&dev->device_lock);
 
-       if (device->event_cb && !cl->read_cb)
-               mei_cl_read_start(device->cl, 0);
+       if (device->event_cb)
+               mei_cl_read_start(device->cl, 0, NULL);
 
        if (!device->ops || !device->ops->enable)
                return 0;
@@ -462,54 +467,34 @@ int mei_cl_disable_device(struct mei_cl_device *device)
 
        dev = cl->dev;
 
+       if (device->ops && device->ops->disable)
+               device->ops->disable(device);
+
+       device->event_cb = NULL;
+
        mutex_lock(&dev->device_lock);
 
-       if (cl->state != MEI_FILE_CONNECTED) {
-               mutex_unlock(&dev->device_lock);
+       if (!mei_cl_is_connected(cl)) {
                dev_err(dev->dev, "Already disconnected");
-
-               return 0;
+               err = 0;
+               goto out;
        }
 
        cl->state = MEI_FILE_DISCONNECTING;
 
        err = mei_cl_disconnect(cl);
        if (err < 0) {
-               mutex_unlock(&dev->device_lock);
-               dev_err(dev->dev,
-                       "Could not disconnect from the ME client");
-
-               return err;
+               dev_err(dev->dev, "Could not disconnect from the ME client");
+               goto out;
        }
 
        /* Flush queues and remove any pending read */
-       mei_cl_flush_queues(cl);
-
-       if (cl->read_cb) {
-               struct mei_cl_cb *cb = NULL;
-
-               cb = mei_cl_find_read_cb(cl);
-               /* Remove entry from read list */
-               if (cb)
-                       list_del(&cb->list);
-
-               cb = cl->read_cb;
-               cl->read_cb = NULL;
-
-               if (cb) {
-                       mei_io_cb_free(cb);
-                       cb = NULL;
-               }
-       }
-
-       device->event_cb = NULL;
+       mei_cl_flush_queues(cl, NULL);
 
+out:
        mutex_unlock(&dev->device_lock);
+       return err;
 
-       if (!device->ops || !device->ops->disable)
-               return 0;
-
-       return device->ops->disable(device);
 }
 EXPORT_SYMBOL_GPL(mei_cl_disable_device);
 
index dfbddfe1c7a06f9e1ea435ffac42ab31b7a9511c..1e99ef6a54a2b83153e6e6053e45a9a5df85bee9 100644 (file)
@@ -48,14 +48,14 @@ void mei_me_cl_init(struct mei_me_client *me_cl)
  */
 struct mei_me_client *mei_me_cl_get(struct mei_me_client *me_cl)
 {
-       if (me_cl)
-               kref_get(&me_cl->refcnt);
+       if (me_cl && kref_get_unless_zero(&me_cl->refcnt))
+               return me_cl;
 
-       return me_cl;
+       return NULL;
 }
 
 /**
- * mei_me_cl_release - unlink and free me client
+ * mei_me_cl_release - free me client
  *
  * Locking: called under "dev->device_lock" lock
  *
@@ -65,9 +65,10 @@ static void mei_me_cl_release(struct kref *ref)
 {
        struct mei_me_client *me_cl =
                container_of(ref, struct mei_me_client, refcnt);
-       list_del(&me_cl->list);
+
        kfree(me_cl);
 }
+
 /**
  * mei_me_cl_put - decrease me client refcount and free client if necessary
  *
@@ -82,28 +83,87 @@ void mei_me_cl_put(struct mei_me_client *me_cl)
 }
 
 /**
- * mei_me_cl_by_uuid - locate me client by uuid
+ * __mei_me_cl_del  - delete me client form the list and decrease
+ *     reference counter
+ *
+ * @dev: mei device
+ * @me_cl: me client
+ *
+ * Locking: dev->me_clients_rwsem
+ */
+static void __mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl)
+{
+       if (!me_cl)
+               return;
+
+       list_del(&me_cl->list);
+       mei_me_cl_put(me_cl);
+}
+
+/**
+ * mei_me_cl_add - add me client to the list
+ *
+ * @dev: mei device
+ * @me_cl: me client
+ */
+void mei_me_cl_add(struct mei_device *dev, struct mei_me_client *me_cl)
+{
+       down_write(&dev->me_clients_rwsem);
+       list_add(&me_cl->list, &dev->me_clients);
+       up_write(&dev->me_clients_rwsem);
+}
+
+/**
+ * __mei_me_cl_by_uuid - locate me client by uuid
  *     increases ref count
  *
  * @dev: mei device
  * @uuid: me client uuid
  *
- * Locking: called under "dev->device_lock" lock
- *
  * Return: me client or NULL if not found
+ *
+ * Locking: dev->me_clients_rwsem
  */
-struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev,
+static struct mei_me_client *__mei_me_cl_by_uuid(struct mei_device *dev,
                                        const uuid_le *uuid)
 {
        struct mei_me_client *me_cl;
+       const uuid_le *pn;
 
-       list_for_each_entry(me_cl, &dev->me_clients, list)
-               if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0)
+       WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem));
+
+       list_for_each_entry(me_cl, &dev->me_clients, list) {
+               pn = &me_cl->props.protocol_name;
+               if (uuid_le_cmp(*uuid, *pn) == 0)
                        return mei_me_cl_get(me_cl);
+       }
 
        return NULL;
 }
 
+/**
+ * mei_me_cl_by_uuid - locate me client by uuid
+ *     increases ref count
+ *
+ * @dev: mei device
+ * @uuid: me client uuid
+ *
+ * Return: me client or NULL if not found
+ *
+ * Locking: dev->me_clients_rwsem
+ */
+struct mei_me_client *mei_me_cl_by_uuid(struct mei_device *dev,
+                                       const uuid_le *uuid)
+{
+       struct mei_me_client *me_cl;
+
+       down_read(&dev->me_clients_rwsem);
+       me_cl = __mei_me_cl_by_uuid(dev, uuid);
+       up_read(&dev->me_clients_rwsem);
+
+       return me_cl;
+}
+
 /**
  * mei_me_cl_by_id - locate me client by client id
  *     increases ref count
@@ -111,22 +171,58 @@ struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev,
  * @dev: the device structure
  * @client_id: me client id
  *
- * Locking: called under "dev->device_lock" lock
- *
  * Return: me client or NULL if not found
+ *
+ * Locking: dev->me_clients_rwsem
  */
 struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id)
 {
 
+       struct mei_me_client *__me_cl, *me_cl = NULL;
+
+       down_read(&dev->me_clients_rwsem);
+       list_for_each_entry(__me_cl, &dev->me_clients, list) {
+               if (__me_cl->client_id == client_id) {
+                       me_cl = mei_me_cl_get(__me_cl);
+                       break;
+               }
+       }
+       up_read(&dev->me_clients_rwsem);
+
+       return me_cl;
+}
+
+/**
+ * __mei_me_cl_by_uuid_id - locate me client by client id and uuid
+ *     increases ref count
+ *
+ * @dev: the device structure
+ * @uuid: me client uuid
+ * @client_id: me client id
+ *
+ * Return: me client or null if not found
+ *
+ * Locking: dev->me_clients_rwsem
+ */
+static struct mei_me_client *__mei_me_cl_by_uuid_id(struct mei_device *dev,
+                                          const uuid_le *uuid, u8 client_id)
+{
        struct mei_me_client *me_cl;
+       const uuid_le *pn;
+
+       WARN_ON(!rwsem_is_locked(&dev->me_clients_rwsem));
 
-       list_for_each_entry(me_cl, &dev->me_clients, list)
-               if (me_cl->client_id == client_id)
+       list_for_each_entry(me_cl, &dev->me_clients, list) {
+               pn = &me_cl->props.protocol_name;
+               if (uuid_le_cmp(*uuid, *pn) == 0 &&
+                   me_cl->client_id == client_id)
                        return mei_me_cl_get(me_cl);
+       }
 
        return NULL;
 }
 
+
 /**
  * mei_me_cl_by_uuid_id - locate me client by client id and uuid
  *     increases ref count
@@ -135,21 +231,18 @@ struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id)
  * @uuid: me client uuid
  * @client_id: me client id
  *
- * Locking: called under "dev->device_lock" lock
- *
- * Return: me client or NULL if not found
+ * Return: me client or null if not found
  */
 struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev,
                                           const uuid_le *uuid, u8 client_id)
 {
        struct mei_me_client *me_cl;
 
-       list_for_each_entry(me_cl, &dev->me_clients, list)
-               if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0 &&
-                   me_cl->client_id == client_id)
-                       return mei_me_cl_get(me_cl);
+       down_read(&dev->me_clients_rwsem);
+       me_cl = __mei_me_cl_by_uuid_id(dev, uuid, client_id);
+       up_read(&dev->me_clients_rwsem);
 
-       return NULL;
+       return me_cl;
 }
 
 /**
@@ -162,12 +255,14 @@ struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev,
  */
 void mei_me_cl_rm_by_uuid(struct mei_device *dev, const uuid_le *uuid)
 {
-       struct mei_me_client *me_cl, *next;
+       struct mei_me_client *me_cl;
 
        dev_dbg(dev->dev, "remove %pUl\n", uuid);
-       list_for_each_entry_safe(me_cl, next, &dev->me_clients, list)
-               if (uuid_le_cmp(*uuid, me_cl->props.protocol_name) == 0)
-                       mei_me_cl_put(me_cl);
+
+       down_write(&dev->me_clients_rwsem);
+       me_cl = __mei_me_cl_by_uuid(dev, uuid);
+       __mei_me_cl_del(dev, me_cl);
+       up_write(&dev->me_clients_rwsem);
 }
 
 /**
@@ -181,15 +276,14 @@ void mei_me_cl_rm_by_uuid(struct mei_device *dev, const uuid_le *uuid)
  */
 void mei_me_cl_rm_by_uuid_id(struct mei_device *dev, const uuid_le *uuid, u8 id)
 {
-       struct mei_me_client *me_cl, *next;
-       const uuid_le *pn;
+       struct mei_me_client *me_cl;
 
        dev_dbg(dev->dev, "remove %pUl %d\n", uuid, id);
-       list_for_each_entry_safe(me_cl, next, &dev->me_clients, list) {
-               pn =  &me_cl->props.protocol_name;
-               if (me_cl->client_id == id && uuid_le_cmp(*uuid, *pn) == 0)
-                       mei_me_cl_put(me_cl);
-       }
+
+       down_write(&dev->me_clients_rwsem);
+       me_cl = __mei_me_cl_by_uuid_id(dev, uuid, id);
+       __mei_me_cl_del(dev, me_cl);
+       up_write(&dev->me_clients_rwsem);
 }
 
 /**
@@ -203,12 +297,12 @@ void mei_me_cl_rm_all(struct mei_device *dev)
 {
        struct mei_me_client *me_cl, *next;
 
+       down_write(&dev->me_clients_rwsem);
        list_for_each_entry_safe(me_cl, next, &dev->me_clients, list)
-                       mei_me_cl_put(me_cl);
+               __mei_me_cl_del(dev, me_cl);
+       up_write(&dev->me_clients_rwsem);
 }
 
-
-
 /**
  * mei_cl_cmp_id - tells if the clients are the same
  *
@@ -227,7 +321,48 @@ static inline bool mei_cl_cmp_id(const struct mei_cl *cl1,
 }
 
 /**
- * mei_io_list_flush - removes cbs belonging to cl.
+ * mei_io_cb_free - free mei_cb_private related memory
+ *
+ * @cb: mei callback struct
+ */
+void mei_io_cb_free(struct mei_cl_cb *cb)
+{
+       if (cb == NULL)
+               return;
+
+       list_del(&cb->list);
+       kfree(cb->buf.data);
+       kfree(cb);
+}
+
+/**
+ * mei_io_cb_init - allocate and initialize io callback
+ *
+ * @cl: mei client
+ * @type: operation type
+ * @fp: pointer to file structure
+ *
+ * Return: mei_cl_cb pointer or NULL;
+ */
+struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type,
+                                struct file *fp)
+{
+       struct mei_cl_cb *cb;
+
+       cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
+       if (!cb)
+               return NULL;
+
+       INIT_LIST_HEAD(&cb->list);
+       cb->file_object = fp;
+       cb->cl = cl;
+       cb->buf_idx = 0;
+       cb->fop_type = type;
+       return cb;
+}
+
+/**
+ * __mei_io_list_flush - removes and frees cbs belonging to cl.
  *
  * @list:  an instance of our list structure
  * @cl:    host client, can be NULL for flushing the whole list
@@ -236,13 +371,12 @@ static inline bool mei_cl_cmp_id(const struct mei_cl *cl1,
 static void __mei_io_list_flush(struct mei_cl_cb *list,
                                struct mei_cl *cl, bool free)
 {
-       struct mei_cl_cb *cb;
-       struct mei_cl_cb *next;
+       struct mei_cl_cb *cb, *next;
 
        /* enable removing everything if no cl is specified */
        list_for_each_entry_safe(cb, next, &list->list, list) {
                if (!cl || mei_cl_cmp_id(cl, cb->cl)) {
-                       list_del(&cb->list);
+                       list_del_init(&cb->list);
                        if (free)
                                mei_io_cb_free(cb);
                }
@@ -260,7 +394,6 @@ void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl)
        __mei_io_list_flush(list, cl, false);
 }
 
-
 /**
  * mei_io_list_free - removes cb belonging to cl and free them
  *
@@ -273,103 +406,107 @@ static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl)
 }
 
 /**
- * mei_io_cb_free - free mei_cb_private related memory
+ * mei_io_cb_alloc_buf - allocate callback buffer
  *
- * @cb: mei callback struct
+ * @cb: io callback structure
+ * @length: size of the buffer
+ *
+ * Return: 0 on success
+ *         -EINVAL if cb is NULL
+ *         -ENOMEM if allocation failed
  */
-void mei_io_cb_free(struct mei_cl_cb *cb)
+int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length)
 {
-       if (cb == NULL)
-               return;
+       if (!cb)
+               return -EINVAL;
 
-       kfree(cb->request_buffer.data);
-       kfree(cb->response_buffer.data);
-       kfree(cb);
+       if (length == 0)
+               return 0;
+
+       cb->buf.data = kmalloc(length, GFP_KERNEL);
+       if (!cb->buf.data)
+               return -ENOMEM;
+       cb->buf.size = length;
+       return 0;
 }
 
 /**
- * mei_io_cb_init - allocate and initialize io callback
+ * mei_cl_alloc_cb - a convenient wrapper for allocating read cb
  *
- * @cl: mei client
- * @fp: pointer to file structure
+ * @cl: host client
+ * @length: size of the buffer
+ * @type: operation type
+ * @fp: associated file pointer (might be NULL)
  *
- * Return: mei_cl_cb pointer or NULL;
+ * Return: cb on success and NULL on failure
  */
-struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp)
+struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
+                                 enum mei_cb_file_ops type, struct file *fp)
 {
        struct mei_cl_cb *cb;
 
-       cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
+       cb = mei_io_cb_init(cl, type, fp);
        if (!cb)
                return NULL;
 
-       mei_io_list_init(cb);
+       if (mei_io_cb_alloc_buf(cb, length)) {
+               mei_io_cb_free(cb);
+               return NULL;
+       }
 
-       cb->file_object = fp;
-       cb->cl = cl;
-       cb->buf_idx = 0;
        return cb;
 }
 
 /**
- * mei_io_cb_alloc_req_buf - allocate request buffer
+ * mei_cl_read_cb - find this cl's callback in the read list
+ *     for a specific file
  *
- * @cb: io callback structure
- * @length: size of the buffer
+ * @cl: host client
+ * @fp: file pointer (matching cb file object), may be NULL
  *
- * Return: 0 on success
- *         -EINVAL if cb is NULL
- *         -ENOMEM if allocation failed
+ * Return: cb on success, NULL if cb is not found
  */
-int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length)
+struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp)
 {
-       if (!cb)
-               return -EINVAL;
+       struct mei_cl_cb *cb;
 
-       if (length == 0)
-               return 0;
+       list_for_each_entry(cb, &cl->rd_completed, list)
+               if (!fp || fp == cb->file_object)
+                       return cb;
 
-       cb->request_buffer.data = kmalloc(length, GFP_KERNEL);
-       if (!cb->request_buffer.data)
-               return -ENOMEM;
-       cb->request_buffer.size = length;
-       return 0;
+       return NULL;
 }
+
 /**
- * mei_io_cb_alloc_resp_buf - allocate response buffer
- *
- * @cb: io callback structure
- * @length: size of the buffer
+ * mei_cl_read_cb_flush - free client's read pending and completed cbs
+ *   for a specific file
  *
- * Return: 0 on success
- *         -EINVAL if cb is NULL
- *         -ENOMEM if allocation failed
+ * @cl: host client
+ * @fp: file pointer (matching cb file object), may be NULL
  */
-int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length)
+void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp)
 {
-       if (!cb)
-               return -EINVAL;
+       struct mei_cl_cb *cb, *next;
 
-       if (length == 0)
-               return 0;
-
-       cb->response_buffer.data = kmalloc(length, GFP_KERNEL);
-       if (!cb->response_buffer.data)
-               return -ENOMEM;
-       cb->response_buffer.size = length;
-       return 0;
-}
+       list_for_each_entry_safe(cb, next, &cl->rd_completed, list)
+               if (!fp || fp == cb->file_object)
+                       mei_io_cb_free(cb);
 
 
+       list_for_each_entry_safe(cb, next, &cl->rd_pending, list)
+               if (!fp || fp == cb->file_object)
+                       mei_io_cb_free(cb);
+}
 
 /**
  * mei_cl_flush_queues - flushes queue lists belonging to cl.
  *
  * @cl: host client
+ * @fp: file pointer (matching cb file object), may be NULL
  *
  * Return: 0 on success, -EINVAL if cl or cl->dev is NULL.
  */
-int mei_cl_flush_queues(struct mei_cl *cl)
+int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
 {
        struct mei_device *dev;
 
@@ -379,13 +516,15 @@ int mei_cl_flush_queues(struct mei_cl *cl)
        dev = cl->dev;
 
        cl_dbg(dev, cl, "remove list entry belonging to cl\n");
-       mei_io_list_flush(&cl->dev->read_list, cl);
        mei_io_list_free(&cl->dev->write_list, cl);
        mei_io_list_free(&cl->dev->write_waiting_list, cl);
        mei_io_list_flush(&cl->dev->ctrl_wr_list, cl);
        mei_io_list_flush(&cl->dev->ctrl_rd_list, cl);
        mei_io_list_flush(&cl->dev->amthif_cmd_list, cl);
        mei_io_list_flush(&cl->dev->amthif_rd_complete_list, cl);
+
+       mei_cl_read_cb_flush(cl, fp);
+
        return 0;
 }
 
@@ -402,9 +541,10 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev)
        init_waitqueue_head(&cl->wait);
        init_waitqueue_head(&cl->rx_wait);
        init_waitqueue_head(&cl->tx_wait);
+       INIT_LIST_HEAD(&cl->rd_completed);
+       INIT_LIST_HEAD(&cl->rd_pending);
        INIT_LIST_HEAD(&cl->link);
        INIT_LIST_HEAD(&cl->device_link);
-       cl->reading_state = MEI_IDLE;
        cl->writing_state = MEI_IDLE;
        cl->dev = dev;
 }
@@ -429,31 +569,14 @@ struct mei_cl *mei_cl_allocate(struct mei_device *dev)
 }
 
 /**
- * mei_cl_find_read_cb - find this cl's callback in the read list
+ * mei_cl_link - allocate host id in the host map
  *
  * @cl: host client
- *
- * Return: cb on success, NULL on error
- */
-struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl)
-{
-       struct mei_device *dev = cl->dev;
-       struct mei_cl_cb *cb;
-
-       list_for_each_entry(cb, &dev->read_list.list, list)
-               if (mei_cl_cmp_id(cl, cb->cl))
-                       return cb;
-       return NULL;
-}
-
-/** mei_cl_link: allocate host id in the host map
- *
- * @cl - host client
- * @id - fixed host id or -1 for generic one
+ * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one
  *
  * Return: 0 on success
  *     -EINVAL on incorrect values
- *     -ENONET if client not found
+ *     -EMFILE if open count exceeded.
  */
 int mei_cl_link(struct mei_cl *cl, int id)
 {
@@ -535,28 +658,31 @@ int mei_cl_unlink(struct mei_cl *cl)
 
 void mei_host_client_init(struct work_struct *work)
 {
-       struct mei_device *dev = container_of(work,
-                                             struct mei_device, init_work);
+       struct mei_device *dev =
+               container_of(work, struct mei_device, init_work);
        struct mei_me_client *me_cl;
-       struct mei_client_properties *props;
 
        mutex_lock(&dev->device_lock);
 
-       list_for_each_entry(me_cl, &dev->me_clients, list) {
-               props = &me_cl->props;
 
-               if (!uuid_le_cmp(props->protocol_name, mei_amthif_guid))
-                       mei_amthif_host_init(dev);
-               else if (!uuid_le_cmp(props->protocol_name, mei_wd_guid))
-                       mei_wd_host_init(dev);
-               else if (!uuid_le_cmp(props->protocol_name, mei_nfc_guid))
-                       mei_nfc_host_init(dev);
+       me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid);
+       if (me_cl)
+               mei_amthif_host_init(dev);
+       mei_me_cl_put(me_cl);
+
+       me_cl = mei_me_cl_by_uuid(dev, &mei_wd_guid);
+       if (me_cl)
+               mei_wd_host_init(dev);
+       mei_me_cl_put(me_cl);
+
+       me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_guid);
+       if (me_cl)
+               mei_nfc_host_init(dev);
+       mei_me_cl_put(me_cl);
 
-       }
 
        dev->dev_state = MEI_DEV_ENABLED;
        dev->reset_count = 0;
-
        mutex_unlock(&dev->device_lock);
 
        pm_runtime_mark_last_busy(dev->dev);
@@ -620,13 +746,10 @@ int mei_cl_disconnect(struct mei_cl *cl)
                return rets;
        }
 
-       cb = mei_io_cb_init(cl, NULL);
-       if (!cb) {
-               rets = -ENOMEM;
+       cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL);
+       rets = cb ? 0 : -ENOMEM;
+       if (rets)
                goto free;
-       }
-
-       cb->fop_type = MEI_FOP_DISCONNECT;
 
        if (mei_hbuf_acquire(dev)) {
                if (mei_hbm_cl_disconnect_req(dev, cl)) {
@@ -727,13 +850,10 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
                return rets;
        }
 
-       cb = mei_io_cb_init(cl, file);
-       if (!cb) {
-               rets = -ENOMEM;
+       cb = mei_io_cb_init(cl, MEI_FOP_CONNECT, file);
+       rets = cb ? 0 : -ENOMEM;
+       if (rets)
                goto out;
-       }
-
-       cb->fop_type = MEI_FOP_CONNECT;
 
        /* run hbuf acquire last so we don't have to undo */
        if (!mei_cl_is_other_connecting(cl) && mei_hbuf_acquire(dev)) {
@@ -756,7 +876,7 @@ int mei_cl_connect(struct mei_cl *cl, struct file *file)
                        mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT));
        mutex_lock(&dev->device_lock);
 
-       if (cl->state != MEI_FILE_CONNECTED) {
+       if (!mei_cl_is_connected(cl)) {
                cl->state = MEI_FILE_DISCONNECTED;
                /* something went really wrong */
                if (!cl->status)
@@ -777,6 +897,37 @@ out:
        return rets;
 }
 
+/**
+ * mei_cl_alloc_linked - allocate and link host client
+ *
+ * @dev: the device structure
+ * @id: fixed host id or MEI_HOST_CLIENT_ID_ANY (-1) for generic one
+ *
+ * Return: cl on success ERR_PTR on failure
+ */
+struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id)
+{
+       struct mei_cl *cl;
+       int ret;
+
+       cl = mei_cl_allocate(dev);
+       if (!cl) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       ret = mei_cl_link(cl, id);
+       if (ret)
+               goto err;
+
+       return cl;
+err:
+       kfree(cl);
+       return ERR_PTR(ret);
+}
+
+
+
 /**
  * mei_cl_flow_ctrl_creds - checks flow_control credits for cl.
  *
@@ -866,10 +1017,11 @@ out:
  *
  * @cl: host client
  * @length: number of bytes to read
+ * @fp: pointer to file structure
  *
  * Return: 0 on success, <0 on failure.
  */
-int mei_cl_read_start(struct mei_cl *cl, size_t length)
+int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp)
 {
        struct mei_device *dev;
        struct mei_cl_cb *cb;
@@ -884,10 +1036,10 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
        if (!mei_cl_is_connected(cl))
                return -ENODEV;
 
-       if (cl->read_cb) {
-               cl_dbg(dev, cl, "read is pending.\n");
+       /* HW currently supports only one pending read */
+       if (!list_empty(&cl->rd_pending))
                return -EBUSY;
-       }
+
        me_cl = mei_me_cl_by_uuid_id(dev, &cl->cl_uuid, cl->me_client_id);
        if (!me_cl) {
                cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
@@ -904,29 +1056,21 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
                return rets;
        }
 
-       cb = mei_io_cb_init(cl, NULL);
-       if (!cb) {
-               rets = -ENOMEM;
-               goto out;
-       }
-
-       rets = mei_io_cb_alloc_resp_buf(cb, length);
+       cb = mei_cl_alloc_cb(cl, length, MEI_FOP_READ, fp);
+       rets = cb ? 0 : -ENOMEM;
        if (rets)
                goto out;
 
-       cb->fop_type = MEI_FOP_READ;
        if (mei_hbuf_acquire(dev)) {
                rets = mei_hbm_cl_flow_control_req(dev, cl);
                if (rets < 0)
                        goto out;
 
-               list_add_tail(&cb->list, &dev->read_list.list);
+               list_add_tail(&cb->list, &cl->rd_pending);
        } else {
                list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
        }
 
-       cl->read_cb = cb;
-
 out:
        cl_dbg(dev, cl, "rpm: autosuspend\n");
        pm_runtime_mark_last_busy(dev->dev);
@@ -964,7 +1108,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
 
        dev = cl->dev;
 
-       buf = &cb->request_buffer;
+       buf = &cb->buf;
 
        rets = mei_cl_flow_ctrl_creds(cl);
        if (rets < 0)
@@ -999,7 +1143,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
        }
 
        cl_dbg(dev, cl, "buf: size = %d idx = %lu\n",
-                       cb->request_buffer.size, cb->buf_idx);
+                       cb->buf.size, cb->buf_idx);
 
        rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx);
        if (rets) {
@@ -1011,6 +1155,7 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
        cl->status = 0;
        cl->writing_state = MEI_WRITING;
        cb->buf_idx += mei_hdr.length;
+       cb->completed = mei_hdr.msg_complete == 1;
 
        if (mei_hdr.msg_complete) {
                if (mei_cl_flow_ctrl_reduce(cl))
@@ -1048,7 +1193,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
        dev = cl->dev;
 
 
-       buf = &cb->request_buffer;
+       buf = &cb->buf;
 
        cl_dbg(dev, cl, "size=%d\n", buf->size);
 
@@ -1059,7 +1204,6 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
                return rets;
        }
 
-       cb->fop_type = MEI_FOP_WRITE;
        cb->buf_idx = 0;
        cl->writing_state = MEI_IDLE;
 
@@ -1099,6 +1243,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
 
        cl->writing_state = MEI_WRITING;
        cb->buf_idx = mei_hdr.length;
+       cb->completed = mei_hdr.msg_complete == 1;
 
 out:
        if (mei_hdr.msg_complete) {
@@ -1151,11 +1296,10 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
                if (waitqueue_active(&cl->tx_wait))
                        wake_up_interruptible(&cl->tx_wait);
 
-       } else if (cb->fop_type == MEI_FOP_READ &&
-                       MEI_READING == cl->reading_state) {
-               cl->reading_state = MEI_READ_COMPLETE;
+       } else if (cb->fop_type == MEI_FOP_READ) {
+               list_add_tail(&cb->list, &cl->rd_completed);
                if (waitqueue_active(&cl->rx_wait))
-                       wake_up_interruptible(&cl->rx_wait);
+                       wake_up_interruptible_all(&cl->rx_wait);
                else
                        mei_cl_bus_rx_event(cl);
 
index cfcde8e97fc489a3e0ba966d06def8f1fde2c0c4..0a39e5d45171855a642c2bca7d550a223fe94cd7 100644 (file)
@@ -31,7 +31,10 @@ void mei_me_cl_init(struct mei_me_client *me_cl);
 void mei_me_cl_put(struct mei_me_client *me_cl);
 struct mei_me_client *mei_me_cl_get(struct mei_me_client *me_cl);
 
-struct mei_me_client *mei_me_cl_by_uuid(const struct mei_device *dev,
+void mei_me_cl_add(struct mei_device *dev, struct mei_me_client *me_cl);
+void mei_me_cl_del(struct mei_device *dev, struct mei_me_client *me_cl);
+
+struct mei_me_client *mei_me_cl_by_uuid(struct mei_device *dev,
                                        const uuid_le *uuid);
 struct mei_me_client *mei_me_cl_by_id(struct mei_device *dev, u8 client_id);
 struct mei_me_client *mei_me_cl_by_uuid_id(struct mei_device *dev,
@@ -44,10 +47,10 @@ void mei_me_cl_rm_all(struct mei_device *dev);
 /*
  * MEI IO Functions
  */
-struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp);
+struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, enum mei_cb_file_ops type,
+                                struct file *fp);
 void mei_io_cb_free(struct mei_cl_cb *priv_cb);
-int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length);
-int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length);
+int mei_io_cb_alloc_buf(struct mei_cl_cb *cb, size_t length);
 
 
 /**
@@ -72,9 +75,14 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev);
 int mei_cl_link(struct mei_cl *cl, int id);
 int mei_cl_unlink(struct mei_cl *cl);
 
-int mei_cl_flush_queues(struct mei_cl *cl);
-struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl);
+struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev, int id);
 
+struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl,
+                                const struct file *fp);
+void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp);
+struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
+                                 enum mei_cb_file_ops type, struct file *fp);
+int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp);
 
 int mei_cl_flow_ctrl_creds(struct mei_cl *cl);
 
@@ -82,23 +90,25 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl);
 /*
  *  MEI input output function prototype
  */
+
+/**
+ * mei_cl_is_connected - host client is connected
+ *
+ * @cl: host clinet
+ *
+ * Return: true if the host clinet is connected
+ */
 static inline bool mei_cl_is_connected(struct mei_cl *cl)
 {
-       return  cl->dev &&
-               cl->dev->dev_state == MEI_DEV_ENABLED &&
-               cl->state == MEI_FILE_CONNECTED;
-}
-static inline bool mei_cl_is_transitioning(struct mei_cl *cl)
-{
-       return  MEI_FILE_INITIALIZING == cl->state ||
-               MEI_FILE_DISCONNECTED == cl->state ||
-               MEI_FILE_DISCONNECTING == cl->state;
+       return  cl->state == MEI_FILE_CONNECTED;
 }
 
 bool mei_cl_is_other_connecting(struct mei_cl *cl);
 int mei_cl_disconnect(struct mei_cl *cl);
 int mei_cl_connect(struct mei_cl *cl, struct file *file);
-int mei_cl_read_start(struct mei_cl *cl, size_t length);
+int mei_cl_read_start(struct mei_cl *cl, size_t length, struct file *fp);
+int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *hdr,
+                       struct mei_cl_cb *cmpl_list);
 int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking);
 int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
                     struct mei_cl_cb *cmpl_list);
index b125380ee8710162923bc246e0712bfca6d92690..d9cd7e6ee48466acf4afcbbb19b2655ca3eb3439 100644 (file)
@@ -28,7 +28,7 @@ static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf,
                                        size_t cnt, loff_t *ppos)
 {
        struct mei_device *dev = fp->private_data;
-       struct mei_me_client *me_cl, *n;
+       struct mei_me_client *me_cl;
        size_t bufsz = 1;
        char *buf;
        int i = 0;
@@ -38,15 +38,14 @@ static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf,
 #define HDR \
 "  |id|fix|         UUID                       |con|msg len|sb|refc|\n"
 
-       mutex_lock(&dev->device_lock);
-
+       down_read(&dev->me_clients_rwsem);
        list_for_each_entry(me_cl, &dev->me_clients, list)
                bufsz++;
 
        bufsz *= sizeof(HDR) + 1;
        buf = kzalloc(bufsz, GFP_KERNEL);
        if (!buf) {
-               mutex_unlock(&dev->device_lock);
+               up_read(&dev->me_clients_rwsem);
                return -ENOMEM;
        }
 
@@ -56,10 +55,9 @@ static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf,
        if (dev->dev_state != MEI_DEV_ENABLED)
                goto out;
 
-       list_for_each_entry_safe(me_cl, n, &dev->me_clients, list) {
+       list_for_each_entry(me_cl, &dev->me_clients, list) {
 
-               me_cl = mei_me_cl_get(me_cl);
-               if (me_cl) {
+               if (mei_me_cl_get(me_cl)) {
                        pos += scnprintf(buf + pos, bufsz - pos,
                                "%2d|%2d|%3d|%pUl|%3d|%7d|%2d|%4d|\n",
                                i++, me_cl->client_id,
@@ -69,12 +67,13 @@ static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf,
                                me_cl->props.max_msg_length,
                                me_cl->props.single_recv_buf,
                                atomic_read(&me_cl->refcnt.refcount));
-               }
 
-               mei_me_cl_put(me_cl);
+                       mei_me_cl_put(me_cl);
+               }
        }
+
 out:
-       mutex_unlock(&dev->device_lock);
+       up_read(&dev->me_clients_rwsem);
        ret = simple_read_from_buffer(ubuf, cnt, ppos, buf, pos);
        kfree(buf);
        return ret;
@@ -118,7 +117,7 @@ static ssize_t mei_dbgfs_read_active(struct file *fp, char __user *ubuf,
                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);
+                       !list_empty(&cl->rd_completed), cl->writing_state);
                i++;
        }
 out:
index c8412d41e4f163410b771dfcdd7126f201f3e937..58da92565c5ed332aca5101d7930722a759f4f31 100644 (file)
@@ -338,7 +338,8 @@ static int mei_hbm_me_cl_add(struct mei_device *dev,
        me_cl->client_id = res->me_addr;
        me_cl->mei_flow_ctrl_creds = 0;
 
-       list_add(&me_cl->list, &dev->me_clients);
+       mei_me_cl_add(dev, me_cl);
+
        return 0;
 }
 
@@ -638,7 +639,7 @@ static void mei_hbm_cl_res(struct mei_device *dev,
                        continue;
 
                if (mei_hbm_cl_addr_equal(cl, rs)) {
-                       list_del(&cb->list);
+                       list_del_init(&cb->list);
                        break;
                }
        }
@@ -683,10 +684,9 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev,
                cl->state = MEI_FILE_DISCONNECTED;
                cl->timer_count = 0;
 
-               cb = mei_io_cb_init(cl, NULL);
+               cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT_RSP, 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);
        }
index f8fd503dfbd69b09cc192c655127cf9aeb950e77..6fb75e62a764c5af4f57fbbe79340d91d6e1e8ed 100644 (file)
@@ -25,6 +25,8 @@
 #include "hw-me.h"
 #include "hw-me-regs.h"
 
+#include "mei-trace.h"
+
 /**
  * mei_me_reg_read - Reads 32bit data from the mei device
  *
@@ -61,45 +63,79 @@ static inline void mei_me_reg_write(const struct mei_me_hw *hw,
  *
  * Return: ME_CB_RW register value (u32)
  */
-static u32 mei_me_mecbrw_read(const struct mei_device *dev)
+static inline u32 mei_me_mecbrw_read(const struct mei_device *dev)
 {
        return mei_me_reg_read(to_me_hw(dev), ME_CB_RW);
 }
+
+/**
+ * mei_me_hcbww_write - write 32bit data to the host circular buffer
+ *
+ * @dev: the device structure
+ * @data: 32bit data to be written to the host circular buffer
+ */
+static inline void mei_me_hcbww_write(struct mei_device *dev, u32 data)
+{
+       mei_me_reg_write(to_me_hw(dev), H_CB_WW, data);
+}
+
 /**
  * mei_me_mecsr_read - Reads 32bit data from the ME CSR
  *
- * @hw: the me hardware structure
+ * @dev: the device structure
  *
  * Return: ME_CSR_HA register value (u32)
  */
-static inline u32 mei_me_mecsr_read(const struct mei_me_hw *hw)
+static inline u32 mei_me_mecsr_read(const struct mei_device *dev)
 {
-       return mei_me_reg_read(hw, ME_CSR_HA);
+       u32 reg;
+
+       reg = mei_me_reg_read(to_me_hw(dev), ME_CSR_HA);
+       trace_mei_reg_read(dev->dev, "ME_CSR_HA", ME_CSR_HA, reg);
+
+       return reg;
 }
 
 /**
  * mei_hcsr_read - Reads 32bit data from the host CSR
  *
- * @hw: the me hardware structure
+ * @dev: the device structure
  *
  * Return: H_CSR register value (u32)
  */
-static inline u32 mei_hcsr_read(const struct mei_me_hw *hw)
+static inline u32 mei_hcsr_read(const struct mei_device *dev)
+{
+       u32 reg;
+
+       reg = mei_me_reg_read(to_me_hw(dev), H_CSR);
+       trace_mei_reg_read(dev->dev, "H_CSR", H_CSR, reg);
+
+       return reg;
+}
+
+/**
+ * mei_hcsr_write - writes H_CSR register to the mei device
+ *
+ * @dev: the device structure
+ * @reg: new register value
+ */
+static inline void mei_hcsr_write(struct mei_device *dev, u32 reg)
 {
-       return mei_me_reg_read(hw, H_CSR);
+       trace_mei_reg_write(dev->dev, "H_CSR", H_CSR, reg);
+       mei_me_reg_write(to_me_hw(dev), H_CSR, reg);
 }
 
 /**
  * mei_hcsr_set - writes H_CSR register to the mei device,
  * and ignores the H_IS bit for it is write-one-to-zero.
  *
- * @hw: the me hardware structure
- * @hcsr: new register value
+ * @dev: the device structure
+ * @reg: new register value
  */
-static inline void mei_hcsr_set(struct mei_me_hw *hw, u32 hcsr)
+static inline void mei_hcsr_set(struct mei_device *dev, u32 reg)
 {
-       hcsr &= ~H_IS;
-       mei_me_reg_write(hw, H_CSR, hcsr);
+       reg &= ~H_IS;
+       mei_hcsr_write(dev, reg);
 }
 
 /**
@@ -141,7 +177,7 @@ static int mei_me_fw_status(struct mei_device *dev,
 static void mei_me_hw_config(struct mei_device *dev)
 {
        struct mei_me_hw *hw = to_me_hw(dev);
-       u32 hcsr = mei_hcsr_read(to_me_hw(dev));
+       u32 hcsr = mei_hcsr_read(dev);
        /* Doesn't change in runtime */
        dev->hbuf_depth = (hcsr & H_CBD) >> 24;
 
@@ -170,11 +206,10 @@ static inline enum mei_pg_state mei_me_pg_state(struct mei_device *dev)
  */
 static void mei_me_intr_clear(struct mei_device *dev)
 {
-       struct mei_me_hw *hw = to_me_hw(dev);
-       u32 hcsr = mei_hcsr_read(hw);
+       u32 hcsr = mei_hcsr_read(dev);
 
        if ((hcsr & H_IS) == H_IS)
-               mei_me_reg_write(hw, H_CSR, hcsr);
+               mei_hcsr_write(dev, hcsr);
 }
 /**
  * mei_me_intr_enable - enables mei device interrupts
@@ -183,11 +218,10 @@ static void mei_me_intr_clear(struct mei_device *dev)
  */
 static void mei_me_intr_enable(struct mei_device *dev)
 {
-       struct mei_me_hw *hw = to_me_hw(dev);
-       u32 hcsr = mei_hcsr_read(hw);
+       u32 hcsr = mei_hcsr_read(dev);
 
        hcsr |= H_IE;
-       mei_hcsr_set(hw, hcsr);
+       mei_hcsr_set(dev, hcsr);
 }
 
 /**
@@ -197,11 +231,10 @@ static void mei_me_intr_enable(struct mei_device *dev)
  */
 static void mei_me_intr_disable(struct mei_device *dev)
 {
-       struct mei_me_hw *hw = to_me_hw(dev);
-       u32 hcsr = mei_hcsr_read(hw);
+       u32 hcsr = mei_hcsr_read(dev);
 
        hcsr  &= ~H_IE;
-       mei_hcsr_set(hw, hcsr);
+       mei_hcsr_set(dev, hcsr);
 }
 
 /**
@@ -211,12 +244,11 @@ static void mei_me_intr_disable(struct mei_device *dev)
  */
 static void mei_me_hw_reset_release(struct mei_device *dev)
 {
-       struct mei_me_hw *hw = to_me_hw(dev);
-       u32 hcsr = mei_hcsr_read(hw);
+       u32 hcsr = mei_hcsr_read(dev);
 
        hcsr |= H_IG;
        hcsr &= ~H_RST;
-       mei_hcsr_set(hw, hcsr);
+       mei_hcsr_set(dev, hcsr);
 
        /* complete this write before we set host ready on another CPU */
        mmiowb();
@@ -231,8 +263,7 @@ static void mei_me_hw_reset_release(struct mei_device *dev)
  */
 static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
 {
-       struct mei_me_hw *hw = to_me_hw(dev);
-       u32 hcsr = mei_hcsr_read(hw);
+       u32 hcsr = mei_hcsr_read(dev);
 
        /* H_RST may be found lit before reset is started,
         * for example if preceding reset flow hasn't completed.
@@ -242,8 +273,8 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
        if ((hcsr & H_RST) == H_RST) {
                dev_warn(dev->dev, "H_RST is set = 0x%08X", hcsr);
                hcsr &= ~H_RST;
-               mei_hcsr_set(hw, hcsr);
-               hcsr = mei_hcsr_read(hw);
+               mei_hcsr_set(dev, hcsr);
+               hcsr = mei_hcsr_read(dev);
        }
 
        hcsr |= H_RST | H_IG | H_IS;
@@ -254,13 +285,13 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
                hcsr &= ~H_IE;
 
        dev->recvd_hw_ready = false;
-       mei_me_reg_write(hw, H_CSR, hcsr);
+       mei_hcsr_write(dev, hcsr);
 
        /*
         * Host reads the H_CSR once to ensure that the
         * posted write to H_CSR completes.
         */
-       hcsr = mei_hcsr_read(hw);
+       hcsr = mei_hcsr_read(dev);
 
        if ((hcsr & H_RST) == 0)
                dev_warn(dev->dev, "H_RST is not set = 0x%08X", hcsr);
@@ -281,11 +312,10 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
  */
 static void mei_me_host_set_ready(struct mei_device *dev)
 {
-       struct mei_me_hw *hw = to_me_hw(dev);
-       u32 hcsr = mei_hcsr_read(hw);
+       u32 hcsr = mei_hcsr_read(dev);
 
        hcsr |= H_IE | H_IG | H_RDY;
-       mei_hcsr_set(hw, hcsr);
+       mei_hcsr_set(dev, hcsr);
 }
 
 /**
@@ -296,8 +326,7 @@ static void mei_me_host_set_ready(struct mei_device *dev)
  */
 static bool mei_me_host_is_ready(struct mei_device *dev)
 {
-       struct mei_me_hw *hw = to_me_hw(dev);
-       u32 hcsr = mei_hcsr_read(hw);
+       u32 hcsr = mei_hcsr_read(dev);
 
        return (hcsr & H_RDY) == H_RDY;
 }
@@ -310,8 +339,7 @@ static bool mei_me_host_is_ready(struct mei_device *dev)
  */
 static bool mei_me_hw_is_ready(struct mei_device *dev)
 {
-       struct mei_me_hw *hw = to_me_hw(dev);
-       u32 mecsr = mei_me_mecsr_read(hw);
+       u32 mecsr = mei_me_mecsr_read(dev);
 
        return (mecsr & ME_RDY_HRA) == ME_RDY_HRA;
 }
@@ -368,11 +396,10 @@ static int mei_me_hw_start(struct mei_device *dev)
  */
 static unsigned char mei_hbuf_filled_slots(struct mei_device *dev)
 {
-       struct mei_me_hw *hw = to_me_hw(dev);
        u32 hcsr;
        char read_ptr, write_ptr;
 
-       hcsr = mei_hcsr_read(hw);
+       hcsr = mei_hcsr_read(dev);
 
        read_ptr = (char) ((hcsr & H_CBRP) >> 8);
        write_ptr = (char) ((hcsr & H_CBWP) >> 16);
@@ -439,7 +466,6 @@ static int mei_me_write_message(struct mei_device *dev,
                        struct mei_msg_hdr *header,
                        unsigned char *buf)
 {
-       struct mei_me_hw *hw = to_me_hw(dev);
        unsigned long rem;
        unsigned long length = header->length;
        u32 *reg_buf = (u32 *)buf;
@@ -457,21 +483,21 @@ static int mei_me_write_message(struct mei_device *dev,
        if (empty_slots < 0 || dw_cnt > empty_slots)
                return -EMSGSIZE;
 
-       mei_me_reg_write(hw, H_CB_WW, *((u32 *) header));
+       mei_me_hcbww_write(dev, *((u32 *) header));
 
        for (i = 0; i < length / 4; i++)
-               mei_me_reg_write(hw, H_CB_WW, reg_buf[i]);
+               mei_me_hcbww_write(dev, reg_buf[i]);
 
        rem = length & 0x3;
        if (rem > 0) {
                u32 reg = 0;
 
                memcpy(&reg, &buf[length - rem], rem);
-               mei_me_reg_write(hw, H_CB_WW, reg);
+               mei_me_hcbww_write(dev, reg);
        }
 
-       hcsr = mei_hcsr_read(hw) | H_IG;
-       mei_hcsr_set(hw, hcsr);
+       hcsr = mei_hcsr_read(dev) | H_IG;
+       mei_hcsr_set(dev, hcsr);
        if (!mei_me_hw_is_ready(dev))
                return -EIO;
 
@@ -487,12 +513,11 @@ static int mei_me_write_message(struct mei_device *dev,
  */
 static int mei_me_count_full_read_slots(struct mei_device *dev)
 {
-       struct mei_me_hw *hw = to_me_hw(dev);
        u32 me_csr;
        char read_ptr, write_ptr;
        unsigned char buffer_depth, filled_slots;
 
-       me_csr = mei_me_mecsr_read(hw);
+       me_csr = mei_me_mecsr_read(dev);
        buffer_depth = (unsigned char)((me_csr & ME_CBD_HRA) >> 24);
        read_ptr = (char) ((me_csr & ME_CBRP_HRA) >> 8);
        write_ptr = (char) ((me_csr & ME_CBWP_HRA) >> 16);
@@ -518,7 +543,6 @@ static int mei_me_count_full_read_slots(struct mei_device *dev)
 static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer,
                    unsigned long buffer_length)
 {
-       struct mei_me_hw *hw = to_me_hw(dev);
        u32 *reg_buf = (u32 *)buffer;
        u32 hcsr;
 
@@ -531,49 +555,59 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer,
                memcpy(reg_buf, &reg, buffer_length);
        }
 
-       hcsr = mei_hcsr_read(hw) | H_IG;
-       mei_hcsr_set(hw, hcsr);
+       hcsr = mei_hcsr_read(dev) | H_IG;
+       mei_hcsr_set(dev, hcsr);
        return 0;
 }
 
 /**
- * mei_me_pg_enter - write pg enter register
+ * mei_me_pg_set - write pg enter register
  *
  * @dev: the device structure
  */
-static void mei_me_pg_enter(struct mei_device *dev)
+static void mei_me_pg_set(struct mei_device *dev)
 {
        struct mei_me_hw *hw = to_me_hw(dev);
-       u32 reg = mei_me_reg_read(hw, H_HPG_CSR);
+       u32 reg;
+
+       reg = mei_me_reg_read(hw, H_HPG_CSR);
+       trace_mei_reg_read(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg);
 
        reg |= H_HPG_CSR_PGI;
+
+       trace_mei_reg_write(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg);
        mei_me_reg_write(hw, H_HPG_CSR, reg);
 }
 
 /**
- * mei_me_pg_exit - write pg exit register
+ * mei_me_pg_unset - write pg exit register
  *
  * @dev: the device structure
  */
-static void mei_me_pg_exit(struct mei_device *dev)
+static void mei_me_pg_unset(struct mei_device *dev)
 {
        struct mei_me_hw *hw = to_me_hw(dev);
-       u32 reg = mei_me_reg_read(hw, H_HPG_CSR);
+       u32 reg;
+
+       reg = mei_me_reg_read(hw, H_HPG_CSR);
+       trace_mei_reg_read(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg);
 
        WARN(!(reg & H_HPG_CSR_PGI), "PGI is not set\n");
 
        reg |= H_HPG_CSR_PGIHEXR;
+
+       trace_mei_reg_write(dev->dev, "H_HPG_CSR", H_HPG_CSR, reg);
        mei_me_reg_write(hw, H_HPG_CSR, reg);
 }
 
 /**
- * mei_me_pg_set_sync - perform pg entry procedure
+ * mei_me_pg_enter_sync - perform pg entry procedure
  *
  * @dev: the device structure
  *
  * Return: 0 on success an error code otherwise
  */
-int mei_me_pg_set_sync(struct mei_device *dev)
+int mei_me_pg_enter_sync(struct mei_device *dev)
 {
        struct mei_me_hw *hw = to_me_hw(dev);
        unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT);
@@ -591,7 +625,7 @@ int mei_me_pg_set_sync(struct mei_device *dev)
        mutex_lock(&dev->device_lock);
 
        if (dev->pg_event == MEI_PG_EVENT_RECEIVED) {
-               mei_me_pg_enter(dev);
+               mei_me_pg_set(dev);
                ret = 0;
        } else {
                ret = -ETIME;
@@ -604,13 +638,13 @@ int mei_me_pg_set_sync(struct mei_device *dev)
 }
 
 /**
- * mei_me_pg_unset_sync - perform pg exit procedure
+ * mei_me_pg_exit_sync - perform pg exit procedure
  *
  * @dev: the device structure
  *
  * Return: 0 on success an error code otherwise
  */
-int mei_me_pg_unset_sync(struct mei_device *dev)
+int mei_me_pg_exit_sync(struct mei_device *dev)
 {
        struct mei_me_hw *hw = to_me_hw(dev);
        unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT);
@@ -621,7 +655,7 @@ int mei_me_pg_unset_sync(struct mei_device *dev)
 
        dev->pg_event = MEI_PG_EVENT_WAIT;
 
-       mei_me_pg_exit(dev);
+       mei_me_pg_unset(dev);
 
        mutex_unlock(&dev->device_lock);
        wait_event_timeout(dev->wait_pg,
@@ -649,8 +683,7 @@ reply:
  */
 static bool mei_me_pg_is_enabled(struct mei_device *dev)
 {
-       struct mei_me_hw *hw = to_me_hw(dev);
-       u32 reg = mei_me_reg_read(hw, ME_CSR_HA);
+       u32 reg = mei_me_mecsr_read(dev);
 
        if ((reg & ME_PGIC_HRA) == 0)
                goto notsupported;
@@ -683,14 +716,13 @@ notsupported:
 irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id)
 {
        struct mei_device *dev = (struct mei_device *) dev_id;
-       struct mei_me_hw *hw = to_me_hw(dev);
-       u32 csr_reg = mei_hcsr_read(hw);
+       u32 hcsr = mei_hcsr_read(dev);
 
-       if ((csr_reg & H_IS) != H_IS)
+       if ((hcsr & H_IS) != H_IS)
                return IRQ_NONE;
 
        /* clear H_IS bit in H_CSR */
-       mei_me_reg_write(hw, H_CSR, csr_reg);
+       mei_hcsr_write(dev, hcsr);
 
        return IRQ_WAKE_THREAD;
 }
index d6567af44377e8deaf33a5a60888f58a128a7588..6022d52af6f6fb8ad57e6339b38f9c4c3cc133ba 100644 (file)
@@ -71,8 +71,8 @@ extern const struct mei_cfg mei_me_pch8_sps_cfg;
 struct mei_device *mei_me_dev_init(struct pci_dev *pdev,
                                   const struct mei_cfg *cfg);
 
-int mei_me_pg_set_sync(struct mei_device *dev);
-int mei_me_pg_unset_sync(struct mei_device *dev);
+int mei_me_pg_enter_sync(struct mei_device *dev);
+int mei_me_pg_exit_sync(struct mei_device *dev);
 
 irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id);
 irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id);
index 618ea721aca8845dec1049cfa164d0fe4e3cedfa..7abafe7d120d8b085511b2c6f31021ffa6522d2a 100644 (file)
@@ -412,7 +412,7 @@ static void mei_txe_intr_disable(struct mei_device *dev)
        mei_txe_br_reg_write(hw, HIER_REG, 0);
 }
 /**
- * mei_txe_intr_disable - enable all interrupts
+ * mei_txe_intr_enable - enable all interrupts
  *
  * @dev: the device structure
  */
index 6ad049a08e4d9d27880e62b69a8d249207fa5a13..97353cf8d9b6b7186ae13485382da2cb0da59e4f 100644 (file)
@@ -389,6 +389,7 @@ void mei_device_init(struct mei_device *dev,
        INIT_LIST_HEAD(&dev->device_list);
        INIT_LIST_HEAD(&dev->me_clients);
        mutex_init(&dev->device_lock);
+       init_rwsem(&dev->me_clients_rwsem);
        init_waitqueue_head(&dev->wait_hw_ready);
        init_waitqueue_head(&dev->wait_pg);
        init_waitqueue_head(&dev->wait_hbm_start);
@@ -396,7 +397,6 @@ void mei_device_init(struct mei_device *dev,
        dev->dev_state = MEI_DEV_INITIALIZING;
        dev->reset_count = 0;
 
-       mei_io_list_init(&dev->read_list);
        mei_io_list_init(&dev->write_list);
        mei_io_list_init(&dev->write_waiting_list);
        mei_io_list_init(&dev->ctrl_wr_list);
index 711cddfa9c997b1a0986c8b94433327bdb21229a..3f84d2edcde44f1f7a06377abe903a6ca2f86fd1 100644 (file)
@@ -43,7 +43,7 @@ void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list)
 
        list_for_each_entry_safe(cb, next, &compl_list->list, list) {
                cl = cb->cl;
-               list_del(&cb->list);
+               list_del_init(&cb->list);
 
                dev_dbg(dev->dev, "completing call back.\n");
                if (cl == &dev->iamthif_cl)
@@ -68,91 +68,91 @@ static inline int mei_cl_hbm_equal(struct mei_cl *cl,
        return cl->host_client_id == mei_hdr->host_addr &&
                cl->me_client_id == mei_hdr->me_addr;
 }
+
 /**
- * mei_cl_is_reading - checks if the client
- *             is the one to read this message
- *
- * @cl: mei client
- * @mei_hdr: header of mei message
+ * mei_irq_discard_msg  - discard received message
  *
- * Return: true on match and false otherwise
+ * @dev: mei device
+ * @hdr: message header
  */
-static bool mei_cl_is_reading(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr)
+static inline
+void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr)
 {
-       return mei_cl_hbm_equal(cl, mei_hdr) &&
-               cl->state == MEI_FILE_CONNECTED &&
-               cl->reading_state != MEI_READ_COMPLETE;
+       /*
+        * no need to check for size as it is guarantied
+        * that length fits into rd_msg_buf
+        */
+       mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
+       dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n",
+               MEI_HDR_PRM(hdr));
 }
 
 /**
  * mei_cl_irq_read_msg - process client message
  *
- * @dev: the device structure
+ * @cl: reading client
  * @mei_hdr: header of mei client message
- * @complete_list: An instance of our list structure
+ * @complete_list: completion list
  *
- * Return: 0 on success, <0 on failure.
+ * Return: always 0
  */
-static int mei_cl_irq_read_msg(struct mei_device *dev,
-                              struct mei_msg_hdr *mei_hdr,
-                              struct mei_cl_cb *complete_list)
+int mei_cl_irq_read_msg(struct mei_cl *cl,
+                      struct mei_msg_hdr *mei_hdr,
+                      struct mei_cl_cb *complete_list)
 {
-       struct mei_cl *cl;
-       struct mei_cl_cb *cb, *next;
+       struct mei_device *dev = cl->dev;
+       struct mei_cl_cb *cb;
        unsigned char *buffer = NULL;
 
-       list_for_each_entry_safe(cb, next, &dev->read_list.list, list) {
-               cl = cb->cl;
-               if (!mei_cl_is_reading(cl, mei_hdr))
-                       continue;
-
-               cl->reading_state = MEI_READING;
+       cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list);
+       if (!cb) {
+               cl_err(dev, cl, "pending read cb not found\n");
+               goto out;
+       }
 
-               if (cb->response_buffer.size == 0 ||
-                   cb->response_buffer.data == NULL) {
-                       cl_err(dev, cl, "response buffer is not allocated.\n");
-                       list_del(&cb->list);
-                       return -ENOMEM;
-               }
+       if (!mei_cl_is_connected(cl)) {
+               cl_dbg(dev, cl, "not connected\n");
+               cb->status = -ENODEV;
+               goto out;
+       }
 
-               if (cb->response_buffer.size < mei_hdr->length + cb->buf_idx) {
-                       cl_dbg(dev, cl, "message overflow. size %d len %d idx %ld\n",
-                               cb->response_buffer.size,
-                               mei_hdr->length, cb->buf_idx);
-                       buffer = krealloc(cb->response_buffer.data,
-                                         mei_hdr->length + cb->buf_idx,
-                                         GFP_KERNEL);
-
-                       if (!buffer) {
-                               list_del(&cb->list);
-                               return -ENOMEM;
-                       }
-                       cb->response_buffer.data = buffer;
-                       cb->response_buffer.size =
-                               mei_hdr->length + cb->buf_idx;
-               }
+       if (cb->buf.size == 0 || cb->buf.data == NULL) {
+               cl_err(dev, cl, "response buffer is not allocated.\n");
+               list_move_tail(&cb->list, &complete_list->list);
+               cb->status = -ENOMEM;
+               goto out;
+       }
 
-               buffer = cb->response_buffer.data + cb->buf_idx;
-               mei_read_slots(dev, buffer, mei_hdr->length);
+       if (cb->buf.size < mei_hdr->length + cb->buf_idx) {
+               cl_dbg(dev, cl, "message overflow. size %d len %d idx %ld\n",
+                       cb->buf.size, mei_hdr->length, cb->buf_idx);
+               buffer = krealloc(cb->buf.data, mei_hdr->length + cb->buf_idx,
+                                 GFP_KERNEL);
 
-               cb->buf_idx += mei_hdr->length;
-               if (mei_hdr->msg_complete) {
-                       cl->status = 0;
-                       list_del(&cb->list);
-                       cl_dbg(dev, cl, "completed read length = %lu\n",
-                               cb->buf_idx);
-                       list_add_tail(&cb->list, &complete_list->list);
+               if (!buffer) {
+                       cb->status = -ENOMEM;
+                       list_move_tail(&cb->list, &complete_list->list);
+                       goto out;
                }
-               break;
+               cb->buf.data = buffer;
+               cb->buf.size = mei_hdr->length + cb->buf_idx;
        }
 
-       dev_dbg(dev->dev, "message read\n");
-       if (!buffer) {
-               mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length);
-               dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n",
-                               MEI_HDR_PRM(mei_hdr));
+       buffer = cb->buf.data + cb->buf_idx;
+       mei_read_slots(dev, buffer, mei_hdr->length);
+
+       cb->buf_idx += mei_hdr->length;
+
+       if (mei_hdr->msg_complete) {
+               cb->read_time = jiffies;
+               cl_dbg(dev, cl, "completed read length = %lu\n", cb->buf_idx);
+               list_move_tail(&cb->list, &complete_list->list);
        }
 
+out:
+       if (!buffer)
+               mei_irq_discard_msg(dev, mei_hdr);
+
        return 0;
 }
 
@@ -183,7 +183,6 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb,
 
        cl->state = MEI_FILE_DISCONNECTED;
        cl->status = 0;
-       list_del(&cb->list);
        mei_io_cb_free(cb);
 
        return ret;
@@ -263,7 +262,7 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
                return ret;
        }
 
-       list_move_tail(&cb->list, &dev->read_list.list);
+       list_move_tail(&cb->list, &cl->rd_pending);
 
        return 0;
 }
@@ -301,7 +300,7 @@ static int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
        if (ret) {
                cl->status = ret;
                cb->buf_idx = 0;
-               list_del(&cb->list);
+               list_del_init(&cb->list);
                return ret;
        }
 
@@ -378,25 +377,13 @@ int mei_irq_read_handler(struct mei_device *dev,
                goto end;
        }
 
-       if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&
-           MEI_FILE_CONNECTED == dev->iamthif_cl.state &&
-           dev->iamthif_state == MEI_IAMTHIF_READING) {
-
-               ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list);
-               if (ret) {
-                       dev_err(dev->dev, "mei_amthif_irq_read_msg failed = %d\n",
-                                       ret);
-                       goto end;
-               }
+       if (cl == &dev->iamthif_cl) {
+               ret = mei_amthif_irq_read_msg(cl, mei_hdr, cmpl_list);
        } else {
-               ret = mei_cl_irq_read_msg(dev, mei_hdr, cmpl_list);
-               if (ret) {
-                       dev_err(dev->dev, "mei_cl_irq_read_msg failed = %d\n",
-                                       ret);
-                       goto end;
-               }
+               ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list);
        }
 
+
 reset_slots:
        /* reset the number of slots and header */
        *slots = mei_count_full_read_slots(dev);
@@ -449,21 +436,9 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
                cl = cb->cl;
 
                cl->status = 0;
-               list_del(&cb->list);
-               if (cb->fop_type == MEI_FOP_WRITE &&
-                   cl != &dev->iamthif_cl) {
-                       cl_dbg(dev, cl, "MEI WRITE COMPLETE\n");
-                       cl->writing_state = MEI_WRITE_COMPLETE;
-                       list_add_tail(&cb->list, &cmpl_list->list);
-               }
-               if (cl == &dev->iamthif_cl) {
-                       cl_dbg(dev, cl, "check iamthif flow control.\n");
-                       if (dev->iamthif_flow_control_pending) {
-                               ret = mei_amthif_irq_read(dev, &slots);
-                               if (ret)
-                                       return ret;
-                       }
-               }
+               cl_dbg(dev, cl, "MEI WRITE COMPLETE\n");
+               cl->writing_state = MEI_WRITE_COMPLETE;
+               list_move_tail(&cb->list, &cmpl_list->list);
        }
 
        if (dev->wd_state == MEI_WD_STOPPING) {
@@ -587,10 +562,7 @@ void mei_timer(struct work_struct *work)
                if (--dev->iamthif_stall_timer == 0) {
                        dev_err(dev->dev, "timer: amthif  hanged.\n");
                        mei_reset(dev);
-                       dev->iamthif_msg_buf_size = 0;
-                       dev->iamthif_msg_buf_index = 0;
                        dev->iamthif_canceled = false;
-                       dev->iamthif_ioctl = true;
                        dev->iamthif_state = MEI_IAMTHIF_IDLE;
                        dev->iamthif_timer = 0;
 
@@ -636,4 +608,3 @@ out:
                schedule_delayed_work(&dev->timer_work, 2 * HZ);
        mutex_unlock(&dev->device_lock);
 }
-
index 47680c84801c766f158bf65c2e2dd3893fdff300..3e29681595064a74de8c856f782402d82aa08fe0 100644 (file)
@@ -58,24 +58,18 @@ static int mei_open(struct inode *inode, struct file *file)
 
        mutex_lock(&dev->device_lock);
 
-       cl = NULL;
-
-       err = -ENODEV;
        if (dev->dev_state != MEI_DEV_ENABLED) {
                dev_dbg(dev->dev, "dev_state != MEI_ENABLED  dev_state = %s\n",
                    mei_dev_state_str(dev->dev_state));
+               err = -ENODEV;
                goto err_unlock;
        }
 
-       err = -ENOMEM;
-       cl = mei_cl_allocate(dev);
-       if (!cl)
-               goto err_unlock;
-
-       /* open_handle_count check is handled in the mei_cl_link */
-       err = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY);
-       if (err)
+       cl = mei_cl_alloc_linked(dev, MEI_HOST_CLIENT_ID_ANY);
+       if (IS_ERR(cl)) {
+               err = PTR_ERR(cl);
                goto err_unlock;
+       }
 
        file->private_data = cl;
 
@@ -85,7 +79,6 @@ static int mei_open(struct inode *inode, struct file *file)
 
 err_unlock:
        mutex_unlock(&dev->device_lock);
-       kfree(cl);
        return err;
 }
 
@@ -100,7 +93,6 @@ err_unlock:
 static int mei_release(struct inode *inode, struct file *file)
 {
        struct mei_cl *cl = file->private_data;
-       struct mei_cl_cb *cb;
        struct mei_device *dev;
        int rets = 0;
 
@@ -114,33 +106,18 @@ static int mei_release(struct inode *inode, struct file *file)
                rets = mei_amthif_release(dev, file);
                goto out;
        }
-       if (cl->state == MEI_FILE_CONNECTED) {
+       if (mei_cl_is_connected(cl)) {
                cl->state = MEI_FILE_DISCONNECTING;
                cl_dbg(dev, cl, "disconnecting\n");
                rets = mei_cl_disconnect(cl);
        }
-       mei_cl_flush_queues(cl);
+       mei_cl_flush_queues(cl, file);
        cl_dbg(dev, cl, "removing\n");
 
        mei_cl_unlink(cl);
 
-
-       /* free read cb */
-       cb = NULL;
-       if (cl->read_cb) {
-               cb = mei_cl_find_read_cb(cl);
-               /* Remove entry from read list */
-               if (cb)
-                       list_del(&cb->list);
-
-               cb = cl->read_cb;
-               cl->read_cb = NULL;
-       }
-
        file->private_data = NULL;
 
-       mei_io_cb_free(cb);
-
        kfree(cl);
 out:
        mutex_unlock(&dev->device_lock);
@@ -162,9 +139,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
                        size_t length, loff_t *offset)
 {
        struct mei_cl *cl = file->private_data;
-       struct mei_cl_cb *cb_pos = NULL;
-       struct mei_cl_cb *cb = NULL;
        struct mei_device *dev;
+       struct mei_cl_cb *cb = NULL;
        int rets;
        int err;
 
@@ -191,8 +167,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
                goto out;
        }
 
-       if (cl->read_cb) {
-               cb = cl->read_cb;
+       cb = mei_cl_read_cb(cl, file);
+       if (cb) {
                /* read what left */
                if (cb->buf_idx > *offset)
                        goto copy_buffer;
@@ -208,7 +184,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
                *offset = 0;
        }
 
-       err = mei_cl_read_start(cl, length);
+       err = mei_cl_read_start(cl, length, file);
        if (err && err != -EBUSY) {
                dev_dbg(dev->dev,
                        "mei start read failure with status = %d\n", err);
@@ -216,8 +192,7 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
                goto out;
        }
 
-       if (MEI_READ_COMPLETE != cl->reading_state &&
-                       !waitqueue_active(&cl->rx_wait)) {
+       if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) {
                if (file->f_flags & O_NONBLOCK) {
                        rets = -EAGAIN;
                        goto out;
@@ -226,8 +201,8 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
                mutex_unlock(&dev->device_lock);
 
                if (wait_event_interruptible(cl->rx_wait,
-                               MEI_READ_COMPLETE == cl->reading_state ||
-                               mei_cl_is_transitioning(cl))) {
+                               (!list_empty(&cl->rd_completed)) ||
+                               (!mei_cl_is_connected(cl)))) {
 
                        if (signal_pending(current))
                                return -EINTR;
@@ -235,26 +210,28 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
                }
 
                mutex_lock(&dev->device_lock);
-               if (mei_cl_is_transitioning(cl)) {
+               if (!mei_cl_is_connected(cl)) {
                        rets = -EBUSY;
                        goto out;
                }
        }
 
-       cb = cl->read_cb;
-
+       cb = mei_cl_read_cb(cl, file);
        if (!cb) {
-               rets = -ENODEV;
-               goto out;
-       }
-       if (cl->reading_state != MEI_READ_COMPLETE) {
                rets = 0;
                goto out;
        }
-       /* now copy the data to user space */
+
 copy_buffer:
+       /* now copy the data to user space */
+       if (cb->status) {
+               rets = cb->status;
+               dev_dbg(dev->dev, "read operation failed %d\n", rets);
+               goto free;
+       }
+
        dev_dbg(dev->dev, "buf.size = %d buf.idx= %ld\n",
-           cb->response_buffer.size, cb->buf_idx);
+           cb->buf.size, cb->buf_idx);
        if (length == 0 || ubuf == NULL || *offset > cb->buf_idx) {
                rets = -EMSGSIZE;
                goto free;
@@ -264,7 +241,7 @@ copy_buffer:
         * however buf_idx may point beyond that */
        length = min_t(size_t, length, cb->buf_idx - *offset);
 
-       if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) {
+       if (copy_to_user(ubuf, cb->buf.data + *offset, length)) {
                dev_dbg(dev->dev, "failed to copy data to userland\n");
                rets = -EFAULT;
                goto free;
@@ -276,13 +253,8 @@ copy_buffer:
                goto out;
 
 free:
-       cb_pos = mei_cl_find_read_cb(cl);
-       /* Remove entry from read list */
-       if (cb_pos)
-               list_del(&cb_pos->list);
        mei_io_cb_free(cb);
-       cl->reading_state = MEI_IDLE;
-       cl->read_cb = NULL;
+
 out:
        dev_dbg(dev->dev, "end mei read rets= %d\n", rets);
        mutex_unlock(&dev->device_lock);
@@ -336,9 +308,8 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
                goto out;
        }
 
-       if (cl->state != MEI_FILE_CONNECTED) {
-               dev_err(dev->dev, "host client = %d,  is not connected to ME client = %d",
-                       cl->host_client_id, cl->me_client_id);
+       if (!mei_cl_is_connected(cl)) {
+               cl_err(dev, cl, "is not connected");
                rets = -ENODEV;
                goto out;
        }
@@ -349,41 +320,22 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
                        timeout = write_cb->read_time +
                                mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
 
-                       if (time_after(jiffies, timeout) ||
-                           cl->reading_state == MEI_READ_COMPLETE) {
+                       if (time_after(jiffies, timeout)) {
                                *offset = 0;
-                               list_del(&write_cb->list);
                                mei_io_cb_free(write_cb);
                                write_cb = NULL;
                        }
                }
        }
 
-       /* free entry used in read */
-       if (cl->reading_state == MEI_READ_COMPLETE) {
-               *offset = 0;
-               write_cb = mei_cl_find_read_cb(cl);
-               if (write_cb) {
-                       list_del(&write_cb->list);
-                       mei_io_cb_free(write_cb);
-                       write_cb = NULL;
-                       cl->reading_state = MEI_IDLE;
-                       cl->read_cb = NULL;
-               }
-       } else if (cl->reading_state == MEI_IDLE)
-               *offset = 0;
-
-
-       write_cb = mei_io_cb_init(cl, file);
+       *offset = 0;
+       write_cb = mei_cl_alloc_cb(cl, length, MEI_FOP_WRITE, file);
        if (!write_cb) {
                rets = -ENOMEM;
                goto out;
        }
-       rets = mei_io_cb_alloc_req_buf(write_cb, length);
-       if (rets)
-               goto out;
 
-       rets = copy_from_user(write_cb->request_buffer.data, ubuf, length);
+       rets = copy_from_user(write_cb->buf.data, ubuf, length);
        if (rets) {
                dev_dbg(dev->dev, "failed to copy data from userland\n");
                rets = -EFAULT;
@@ -391,7 +343,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
        }
 
        if (cl == &dev->iamthif_cl) {
-               rets = mei_amthif_write(dev, write_cb);
+               rets = mei_amthif_write(cl, write_cb);
 
                if (rets) {
                        dev_err(dev->dev,
@@ -464,7 +416,7 @@ static int mei_ioctl_connect_client(struct file *file,
         */
        if (uuid_le_cmp(data->in_client_uuid, mei_amthif_guid) == 0) {
                dev_dbg(dev->dev, "FW Client is amthi\n");
-               if (dev->iamthif_cl.state != MEI_FILE_CONNECTED) {
+               if (!mei_cl_is_connected(&dev->iamthif_cl)) {
                        rets = -ENODEV;
                        goto end;
                }
@@ -588,6 +540,7 @@ static long mei_compat_ioctl(struct file *file,
  */
 static unsigned int mei_poll(struct file *file, poll_table *wait)
 {
+       unsigned long req_events = poll_requested_events(wait);
        struct mei_cl *cl = file->private_data;
        struct mei_device *dev;
        unsigned int mask = 0;
@@ -599,27 +552,26 @@ static unsigned int mei_poll(struct file *file, poll_table *wait)
 
        mutex_lock(&dev->device_lock);
 
-       if (!mei_cl_is_connected(cl)) {
+
+       if (dev->dev_state != MEI_DEV_ENABLED ||
+           !mei_cl_is_connected(cl)) {
                mask = POLLERR;
                goto out;
        }
 
-       mutex_unlock(&dev->device_lock);
-
-
-       if (cl == &dev->iamthif_cl)
-               return mei_amthif_poll(dev, file, wait);
-
-       poll_wait(file, &cl->tx_wait, wait);
-
-       mutex_lock(&dev->device_lock);
-
-       if (!mei_cl_is_connected(cl)) {
-               mask = POLLERR;
+       if (cl == &dev->iamthif_cl) {
+               mask = mei_amthif_poll(dev, file, wait);
                goto out;
        }
 
-       mask |= (POLLIN | POLLRDNORM);
+       if (req_events & (POLLIN | POLLRDNORM)) {
+               poll_wait(file, &cl->rx_wait, wait);
+
+               if (!list_empty(&cl->rd_completed))
+                       mask |= POLLIN | POLLRDNORM;
+               else
+                       mei_cl_read_start(cl, 0, file);
+       }
 
 out:
        mutex_unlock(&dev->device_lock);
diff --git a/drivers/misc/mei/mei-trace.c b/drivers/misc/mei/mei-trace.c
new file mode 100644 (file)
index 0000000..388efb5
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+#include <linux/module.h>
+
+/* sparse doesn't like tracepoint macros */
+#ifndef __CHECKER__
+#define CREATE_TRACE_POINTS
+#include "mei-trace.h"
+
+EXPORT_TRACEPOINT_SYMBOL(mei_reg_read);
+EXPORT_TRACEPOINT_SYMBOL(mei_reg_write);
+#endif /* __CHECKER__ */
diff --git a/drivers/misc/mei/mei-trace.h b/drivers/misc/mei/mei-trace.h
new file mode 100644 (file)
index 0000000..5f4e1a1
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#if !defined(_MEI_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _MEI_TRACE_H_
+
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+#include <linux/device.h>
+
+#undef TRACE_SYSTEM
+
+#define TRACE_SYSTEM mei
+#define TRACE_SYSTEM_STRING __stringify(TRACE_SYSTEM)
+
+TRACE_EVENT(mei_reg_read,
+       TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val),
+       TP_ARGS(dev, reg, offs, val),
+       TP_STRUCT__entry(
+               __string(dev, dev_name(dev))
+               __field(const char *, reg)
+               __field(u32, offs)
+               __field(u32, val)
+       ),
+       TP_fast_assign(
+               __assign_str(dev, dev_name(dev))
+               __entry->reg  = reg;
+               __entry->offs = offs;
+               __entry->val = val;
+       ),
+       TP_printk("[%s] read %s:[%#x] = %#x",
+                 __get_str(dev), __entry->reg, __entry->offs, __entry->val)
+);
+
+TRACE_EVENT(mei_reg_write,
+       TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val),
+       TP_ARGS(dev, reg, offs, val),
+       TP_STRUCT__entry(
+               __string(dev, dev_name(dev))
+               __field(const char *, reg)
+               __field(u32, offs)
+               __field(u32, val)
+       ),
+       TP_fast_assign(
+               __assign_str(dev, dev_name(dev))
+               __entry->reg = reg;
+               __entry->offs = offs;
+               __entry->val = val;
+       ),
+       TP_printk("[%s] write %s[%#x] = %#x)",
+                 __get_str(dev), __entry->reg,  __entry->offs, __entry->val)
+);
+
+#endif /* _MEI_TRACE_H_ */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE mei-trace
+#include <trace/define_trace.h>
index 6c6ce9381535151a437612ad83bae65b08dfed2e..f066ecd719393aaa8f52a165d5d0b8b13416bf8e 100644 (file)
@@ -194,23 +194,25 @@ struct mei_cl;
  * @list: link in callback queue
  * @cl: file client who is running this operation
  * @fop_type: file operation type
- * @request_buffer: buffer to store request data
- * @response_buffer: buffer to store response data
+ * @buf: buffer for data associated with the callback
  * @buf_idx: last read index
  * @read_time: last read operation time stamp (iamthif)
  * @file_object: pointer to file structure
+ * @status: io status of the cb
  * @internal: communication between driver and FW flag
+ * @completed: the transfer or reception has completed
  */
 struct mei_cl_cb {
        struct list_head list;
        struct mei_cl *cl;
        enum mei_cb_file_ops fop_type;
-       struct mei_msg_data request_buffer;
-       struct mei_msg_data response_buffer;
+       struct mei_msg_data buf;
        unsigned long buf_idx;
        unsigned long read_time;
        struct file *file_object;
+       int status;
        u32 internal:1;
+       u32 completed:1;
 };
 
 /**
@@ -229,9 +231,9 @@ struct mei_cl_cb {
  * @me_client_id: me/fw id
  * @mei_flow_ctrl_creds: transmit flow credentials
  * @timer_count:  watchdog timer for operation completion
- * @reading_state: state of the rx
  * @writing_state: state of the tx
- * @read_cb: current pending reading callback
+ * @rd_pending: pending read credits
+ * @rd_completed: completed read
  *
  * @device: device on the mei client bus
  * @device_link:  link to bus clients
@@ -249,9 +251,9 @@ struct mei_cl {
        u8 me_client_id;
        u8 mei_flow_ctrl_creds;
        u8 timer_count;
-       enum mei_file_transaction_states reading_state;
        enum mei_file_transaction_states writing_state;
-       struct mei_cl_cb *read_cb;
+       struct list_head rd_pending;
+       struct list_head rd_completed;
 
        /* MEI CL bus data */
        struct mei_cl_device *device;
@@ -423,7 +425,6 @@ const char *mei_pg_state_str(enum mei_pg_state state);
  * @cdev        : character device
  * @minor       : minor number allocated for device
  *
- * @read_list   : read completion list
  * @write_list  : write pending list
  * @write_waiting_list : write completion list
  * @ctrl_wr_list : pending control write list
@@ -460,6 +461,7 @@ const char *mei_pg_state_str(enum mei_pg_state state);
  * @version     : HBM protocol version in use
  * @hbm_f_pg_supported : hbm feature pgi protocol
  *
+ * @me_clients_rwsem: rw lock over me_clients list
  * @me_clients  : list of FW clients
  * @me_clients_map : FW clients bit map
  * @host_clients_map : host clients id pool
@@ -480,12 +482,7 @@ const char *mei_pg_state_str(enum mei_pg_state state);
  * @iamthif_mtu : amthif client max message length
  * @iamthif_timer : time stamp of current amthif command completion
  * @iamthif_stall_timer : timer to detect amthif hang
- * @iamthif_msg_buf : amthif current message buffer
- * @iamthif_msg_buf_size : size of current amthif message request buffer
- * @iamthif_msg_buf_index : current index in amthif message request buffer
  * @iamthif_state : amthif processor state
- * @iamthif_flow_control_pending: amthif waits for flow control
- * @iamthif_ioctl : wait for completion if amthif control message
  * @iamthif_canceled : current amthif command is canceled
  *
  * @init_work   : work item for the device init
@@ -503,7 +500,6 @@ struct mei_device {
        struct cdev cdev;
        int minor;
 
-       struct mei_cl_cb read_list;
        struct mei_cl_cb write_list;
        struct mei_cl_cb write_waiting_list;
        struct mei_cl_cb ctrl_wr_list;
@@ -556,6 +552,7 @@ struct mei_device {
        struct hbm_version version;
        unsigned int hbm_f_pg_supported:1;
 
+       struct rw_semaphore me_clients_rwsem;
        struct list_head me_clients;
        DECLARE_BITMAP(me_clients_map, MEI_CLIENTS_MAX);
        DECLARE_BITMAP(host_clients_map, MEI_CLIENTS_MAX);
@@ -579,12 +576,7 @@ struct mei_device {
        int iamthif_mtu;
        unsigned long iamthif_timer;
        u32 iamthif_stall_timer;
-       unsigned char *iamthif_msg_buf; /* Note: memory has to be allocated */
-       u32 iamthif_msg_buf_size;
-       u32 iamthif_msg_buf_index;
        enum iamthif_states iamthif_state;
-       bool iamthif_flow_control_pending;
-       bool iamthif_ioctl;
        bool iamthif_canceled;
 
        struct work_struct init_work;
@@ -662,8 +654,6 @@ void mei_amthif_reset_params(struct mei_device *dev);
 
 int mei_amthif_host_init(struct mei_device *dev);
 
-int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *priv_cb);
-
 int mei_amthif_read(struct mei_device *dev, struct file *file,
                char __user *ubuf, size_t length, loff_t *offset);
 
@@ -675,13 +665,13 @@ int mei_amthif_release(struct mei_device *dev, struct file *file);
 struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev,
                                                struct file *file);
 
-void mei_amthif_run_next_cmd(struct mei_device *dev);
-
+int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb);
+int mei_amthif_run_next_cmd(struct mei_device *dev);
 int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
                        struct mei_cl_cb *cmpl_list);
 
 void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb);
-int mei_amthif_irq_read_msg(struct mei_device *dev,
+int mei_amthif_irq_read_msg(struct mei_cl *cl,
                            struct mei_msg_hdr *mei_hdr,
                            struct mei_cl_cb *complete_list);
 int mei_amthif_irq_read(struct mei_device *dev, s32 *slots);
index bb61a119b8bbb4fdd8ee7d09da5fb520bab72573..c3bcb63686d7279dc29792ff62eb5dd8c7e7b15b 100644 (file)
@@ -482,8 +482,8 @@ err:
 int mei_nfc_host_init(struct mei_device *dev)
 {
        struct mei_nfc_dev *ndev;
-       struct mei_cl *cl_info, *cl = NULL;
-       struct mei_me_client *me_cl;
+       struct mei_cl *cl_info, *cl;
+       struct mei_me_client *me_cl = NULL;
        int ret;
 
 
@@ -500,17 +500,6 @@ int mei_nfc_host_init(struct mei_device *dev)
                goto err;
        }
 
-       ndev->cl_info = mei_cl_allocate(dev);
-       ndev->cl = mei_cl_allocate(dev);
-
-       cl = ndev->cl;
-       cl_info = ndev->cl_info;
-
-       if (!cl || !cl_info) {
-               ret = -ENOMEM;
-               goto err;
-       }
-
        /* check for valid client id */
        me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid);
        if (!me_cl) {
@@ -519,17 +508,21 @@ int mei_nfc_host_init(struct mei_device *dev)
                goto err;
        }
 
+       cl_info = mei_cl_alloc_linked(dev, MEI_HOST_CLIENT_ID_ANY);
+       if (IS_ERR(cl_info)) {
+               ret = PTR_ERR(cl_info);
+               goto err;
+       }
+
        cl_info->me_client_id = me_cl->client_id;
        cl_info->cl_uuid = me_cl->props.protocol_name;
        mei_me_cl_put(me_cl);
-
-       ret = mei_cl_link(cl_info, MEI_HOST_CLIENT_ID_ANY);
-       if (ret)
-               goto err;
-
+       me_cl = NULL;
 
        list_add_tail(&cl_info->device_link, &dev->device_list);
 
+       ndev->cl_info = cl_info;
+
        /* check for valid client id */
        me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_guid);
        if (!me_cl) {
@@ -538,16 +531,21 @@ int mei_nfc_host_init(struct mei_device *dev)
                goto err;
        }
 
+       cl = mei_cl_alloc_linked(dev, MEI_HOST_CLIENT_ID_ANY);
+       if (IS_ERR(cl)) {
+               ret = PTR_ERR(cl);
+               goto err;
+       }
+
        cl->me_client_id = me_cl->client_id;
        cl->cl_uuid = me_cl->props.protocol_name;
        mei_me_cl_put(me_cl);
-
-       ret = mei_cl_link(cl, MEI_HOST_CLIENT_ID_ANY);
-       if (ret)
-               goto err;
+       me_cl = NULL;
 
        list_add_tail(&cl->device_link, &dev->device_list);
 
+       ndev->cl = cl;
+
        ndev->req_id = 1;
 
        INIT_WORK(&ndev->init_work, mei_nfc_init);
@@ -557,6 +555,7 @@ int mei_nfc_host_init(struct mei_device *dev)
        return 0;
 
 err:
+       mei_me_cl_put(me_cl);
        mei_nfc_free(ndev);
 
        return ret;
index af44ee26075d8b520401a2cc52150b74d45157f0..23f71f5ce4fb07be4bb9c32021bf3babef6b938d 100644 (file)
@@ -388,7 +388,7 @@ static int mei_me_pm_runtime_suspend(struct device *device)
        mutex_lock(&dev->device_lock);
 
        if (mei_write_is_idle(dev))
-               ret = mei_me_pg_set_sync(dev);
+               ret = mei_me_pg_enter_sync(dev);
        else
                ret = -EAGAIN;
 
@@ -413,7 +413,7 @@ static int mei_me_pm_runtime_resume(struct device *device)
 
        mutex_lock(&dev->device_lock);
 
-       ret = mei_me_pg_unset_sync(dev);
+       ret = mei_me_pg_exit_sync(dev);
 
        mutex_unlock(&dev->device_lock);
 
index c86e2ddbe30af004ce38f4fe7f9ee5f0d5475209..dcfcba44b6f7c2fe2f54a67e0ccb5cde2c11b652 100644 (file)
@@ -63,7 +63,7 @@ static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw)
        }
 }
 /**
- * mei_probe - Device Initialization Routine
+ * mei_txe_probe - Device Initialization Routine
  *
  * @pdev: PCI device structure
  * @ent: entry in mei_txe_pci_tbl
@@ -193,7 +193,7 @@ end:
 }
 
 /**
- * mei_remove - Device Removal Routine
+ * mei_txe_remove - Device Removal Routine
  *
  * @pdev: PCI device structure
  *
index 475f1dea45bfd06c5ecfa9e2fdb40da2da32b34f..2725f865c3d6b814ef7b532069f42c6902572e8f 100644 (file)
@@ -160,9 +160,10 @@ int mei_wd_send(struct mei_device *dev)
  */
 int mei_wd_stop(struct mei_device *dev)
 {
+       struct mei_cl *cl = &dev->wd_cl;
        int ret;
 
-       if (dev->wd_cl.state != MEI_FILE_CONNECTED ||
+       if (!mei_cl_is_connected(cl) ||
            dev->wd_state != MEI_WD_RUNNING)
                return 0;
 
@@ -170,7 +171,7 @@ int mei_wd_stop(struct mei_device *dev)
 
        dev->wd_state = MEI_WD_STOPPING;
 
-       ret = mei_cl_flow_ctrl_creds(&dev->wd_cl);
+       ret = mei_cl_flow_ctrl_creds(cl);
        if (ret < 0)
                goto err;
 
@@ -202,22 +203,25 @@ err:
        return ret;
 }
 
-/*
+/**
  * mei_wd_ops_start - wd start command from the watchdog core.
  *
- * @wd_dev - watchdog device struct
+ * @wd_dev: watchdog device struct
  *
  * Return: 0 if success, negative errno code for failure
  */
 static int mei_wd_ops_start(struct watchdog_device *wd_dev)
 {
-       int err = -ENODEV;
        struct mei_device *dev;
+       struct mei_cl *cl;
+       int err = -ENODEV;
 
        dev = watchdog_get_drvdata(wd_dev);
        if (!dev)
                return -ENODEV;
 
+       cl = &dev->wd_cl;
+
        mutex_lock(&dev->device_lock);
 
        if (dev->dev_state != MEI_DEV_ENABLED) {
@@ -226,8 +230,8 @@ static int mei_wd_ops_start(struct watchdog_device *wd_dev)
                goto end_unlock;
        }
 
-       if (dev->wd_cl.state != MEI_FILE_CONNECTED)     {
-               dev_dbg(dev->dev, "MEI Driver is not connected to Watchdog Client\n");
+       if (!mei_cl_is_connected(cl)) {
+               cl_dbg(dev, cl, "MEI Driver is not connected to Watchdog Client\n");
                goto end_unlock;
        }
 
@@ -239,10 +243,10 @@ end_unlock:
        return err;
 }
 
-/*
+/**
  * mei_wd_ops_stop -  wd stop command from the watchdog core.
  *
- * @wd_dev - watchdog device struct
+ * @wd_dev: watchdog device struct
  *
  * Return: 0 if success, negative errno code for failure
  */
@@ -261,10 +265,10 @@ static int mei_wd_ops_stop(struct watchdog_device *wd_dev)
        return 0;
 }
 
-/*
+/**
  * mei_wd_ops_ping - wd ping command from the watchdog core.
  *
- * @wd_dev - watchdog device struct
+ * @wd_dev: watchdog device struct
  *
  * Return: 0 if success, negative errno code for failure
  */
@@ -282,8 +286,8 @@ static int mei_wd_ops_ping(struct watchdog_device *wd_dev)
 
        mutex_lock(&dev->device_lock);
 
-       if (cl->state != MEI_FILE_CONNECTED) {
-               dev_err(dev->dev, "wd: not connected.\n");
+       if (!mei_cl_is_connected(cl)) {
+               cl_err(dev, cl, "wd: not connected.\n");
                ret = -ENODEV;
                goto end;
        }
@@ -311,11 +315,11 @@ end:
        return ret;
 }
 
-/*
+/**
  * mei_wd_ops_set_timeout - wd set timeout command from the watchdog core.
  *
- * @wd_dev - watchdog device struct
- * @timeout - timeout value to set
+ * @wd_dev: watchdog device struct
+ * @timeout: timeout value to set
  *
  * Return: 0 if success, negative errno code for failure
  */
index ff2b0fb1a6be03c3d94169670e4fd5c83b79a9dc..d9fa609da061433f3ae6d2e6133abf9921b7b1a4 100644 (file)
@@ -309,7 +309,7 @@ void mic_complete_resume(struct mic_device *mdev)
  */
 void mic_prepare_suspend(struct mic_device *mdev)
 {
-       int rc;
+       unsigned long timeout;
 
 #define MIC_SUSPEND_TIMEOUT (60 * HZ)
 
@@ -331,10 +331,10 @@ void mic_prepare_suspend(struct mic_device *mdev)
                 */
                mic_set_state(mdev, MIC_SUSPENDING);
                mutex_unlock(&mdev->mic_mutex);
-               rc = wait_for_completion_timeout(&mdev->reset_wait,
-                                               MIC_SUSPEND_TIMEOUT);
+               timeout = wait_for_completion_timeout(&mdev->reset_wait,
+                                                     MIC_SUSPEND_TIMEOUT);
                /* Force reset the card if the shutdown completion timed out */
-               if (!rc) {
+               if (!timeout) {
                        mutex_lock(&mdev->mic_mutex);
                        mic_set_state(mdev, MIC_SUSPENDED);
                        mutex_unlock(&mdev->mic_mutex);
@@ -348,10 +348,10 @@ void mic_prepare_suspend(struct mic_device *mdev)
                 */
                mic_set_state(mdev, MIC_SUSPENDED);
                mutex_unlock(&mdev->mic_mutex);
-               rc = wait_for_completion_timeout(&mdev->reset_wait,
-                                               MIC_SUSPEND_TIMEOUT);
+               timeout = wait_for_completion_timeout(&mdev->reset_wait,
+                                                     MIC_SUSPEND_TIMEOUT);
                /* Force reset the card if the shutdown completion timed out */
-               if (!rc)
+               if (!timeout)
                        mic_stop(mdev, true);
                break;
        default:
index d686f2846ac7ba7ba8b0508d56c76ac0f950b3a8..b4ca6c884d198d68dcee378083db37decfa907e8 100644 (file)
@@ -363,8 +363,6 @@ static int mic_setup_intx(struct mic_device *mdev, struct pci_dev *pdev)
 {
        int rc;
 
-       pci_msi_off(pdev);
-
        /* Enable intx */
        pci_intx(pdev, 1);
        rc = mic_setup_callbacks(mdev);
index 21181fa243dfd8705f10e113f4293c990b231c37..eeaaf5fca105d9aeed160fbd13c73ba0bbee539d 100644 (file)
@@ -69,12 +69,23 @@ static int sram_probe(struct platform_device *pdev)
        INIT_LIST_HEAD(&reserve_list);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       virt_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(virt_base))
-               return PTR_ERR(virt_base);
+       if (!res) {
+               dev_err(&pdev->dev, "found no memory resource\n");
+               return -EINVAL;
+       }
 
        size = resource_size(res);
 
+       if (!devm_request_mem_region(&pdev->dev,
+                       res->start, size, pdev->name)) {
+               dev_err(&pdev->dev, "could not request region for resource\n");
+               return -EBUSY;
+       }
+
+       virt_base = devm_ioremap_wc(&pdev->dev, res->start, size);
+       if (IS_ERR(virt_base))
+               return PTR_ERR(virt_base);
+
        sram = devm_kzalloc(&pdev->dev, sizeof(*sram), GFP_KERNEL);
        if (!sram)
                return -ENOMEM;
@@ -205,7 +216,7 @@ static int sram_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_OF
-static struct of_device_id sram_dt_ids[] = {
+static const struct of_device_id sram_dt_ids[] = {
        { .compatible = "mmio-sram" },
        {}
 };
index a606c8901e1859099bf387e8a7d1f8b07b0f3cd0..a37a42f67088fada76104cf3d988d98f34aca3b7 100644 (file)
@@ -236,6 +236,7 @@ static int tifm_7xx1_resume(struct pci_dev *dev)
 {
        struct tifm_adapter *fm = pci_get_drvdata(dev);
        int rc;
+       unsigned long timeout;
        unsigned int good_sockets = 0, bad_sockets = 0;
        unsigned long flags;
        unsigned char new_ids[fm->num_sockets];
@@ -272,8 +273,8 @@ static int tifm_7xx1_resume(struct pci_dev *dev)
        if (good_sockets) {
                fm->finish_me = &finish_resume;
                spin_unlock_irqrestore(&fm->lock, flags);
-               rc = wait_for_completion_timeout(&finish_resume, HZ);
-               dev_dbg(&dev->dev, "wait returned %d\n", rc);
+               timeout = wait_for_completion_timeout(&finish_resume, HZ);
+               dev_dbg(&dev->dev, "wait returned %lu\n", timeout);
                writel(TIFM_IRQ_FIFOMASK(good_sockets)
                       | TIFM_IRQ_CARDMASK(good_sockets),
                       fm->addr + FM_CLEAR_INTERRUPT_ENABLE);
index 032d35cf93ca97dfaed554e6e77832672cfbca29..b823f9a6e4641c69af5cc9f7e5c9c438433ffa01 100644 (file)
@@ -113,5 +113,5 @@ module_exit(vmci_drv_exit);
 
 MODULE_AUTHOR("VMware, Inc.");
 MODULE_DESCRIPTION("VMware Virtual Machine Communication Interface.");
-MODULE_VERSION("1.1.1.0-k");
+MODULE_VERSION("1.1.3.0-k");
 MODULE_LICENSE("GPL v2");
index 66fc9921fc85d22b927ea2b297fde5ca199866ee..a721b5d8a9dacdafe85257c64ea7c1f545e6f743 100644 (file)
@@ -395,6 +395,12 @@ static int vmci_host_do_send_datagram(struct vmci_host_dev *vmci_host_dev,
                return -EFAULT;
        }
 
+       if (VMCI_DG_SIZE(dg) != send_info.len) {
+               vmci_ioctl_err("datagram size mismatch\n");
+               kfree(dg);
+               return -EINVAL;
+       }
+
        pr_devel("Datagram dst (handle=0x%x:0x%x) src (handle=0x%x:0x%x), payload (size=%llu bytes)\n",
                 dg->dst.context, dg->dst.resource,
                 dg->src.context, dg->src.resource,
index 35f19a6838222e411e3ab4e427488915a9a841f8..f42d9c4e45619ae5f15cbf7cc270ad1f57b2c3f7 100644 (file)
@@ -295,12 +295,20 @@ static void *qp_alloc_queue(u64 size, u32 flags)
 {
        u64 i;
        struct vmci_queue *queue;
-       const size_t num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1;
-       const size_t pas_size = num_pages * sizeof(*queue->kernel_if->u.g.pas);
-       const size_t vas_size = num_pages * sizeof(*queue->kernel_if->u.g.vas);
-       const size_t queue_size =
-               sizeof(*queue) + sizeof(*queue->kernel_if) +
-               pas_size + vas_size;
+       size_t pas_size;
+       size_t vas_size;
+       size_t queue_size = sizeof(*queue) + sizeof(*queue->kernel_if);
+       const u64 num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1;
+
+       if (num_pages >
+                (SIZE_MAX - queue_size) /
+                (sizeof(*queue->kernel_if->u.g.pas) +
+                 sizeof(*queue->kernel_if->u.g.vas)))
+               return NULL;
+
+       pas_size = num_pages * sizeof(*queue->kernel_if->u.g.pas);
+       vas_size = num_pages * sizeof(*queue->kernel_if->u.g.vas);
+       queue_size += pas_size + vas_size;
 
        queue = vmalloc(queue_size);
        if (!queue)
@@ -615,10 +623,15 @@ static int qp_memcpy_from_queue_iov(void *dest,
 static struct vmci_queue *qp_host_alloc_queue(u64 size)
 {
        struct vmci_queue *queue;
-       const size_t num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1;
+       size_t queue_page_size;
+       const u64 num_pages = DIV_ROUND_UP(size, PAGE_SIZE) + 1;
        const size_t queue_size = sizeof(*queue) + sizeof(*(queue->kernel_if));
-       const size_t queue_page_size =
-           num_pages * sizeof(*queue->kernel_if->u.h.page);
+
+       if (num_pages > (SIZE_MAX - queue_size) /
+                sizeof(*queue->kernel_if->u.h.page))
+               return NULL;
+
+       queue_page_size = num_pages * sizeof(*queue->kernel_if->u.h.page);
 
        queue = kzalloc(queue_size + queue_page_size, GFP_KERNEL);
        if (queue) {
@@ -737,7 +750,8 @@ static int qp_host_get_user_memory(u64 produce_uva,
                                     produce_q->kernel_if->num_pages, 1,
                                     produce_q->kernel_if->u.h.header_page);
        if (retval < produce_q->kernel_if->num_pages) {
-               pr_warn("get_user_pages(produce) failed (retval=%d)", retval);
+               pr_debug("get_user_pages_fast(produce) failed (retval=%d)",
+                       retval);
                qp_release_pages(produce_q->kernel_if->u.h.header_page,
                                 retval, false);
                err = VMCI_ERROR_NO_MEM;
@@ -748,7 +762,8 @@ static int qp_host_get_user_memory(u64 produce_uva,
                                     consume_q->kernel_if->num_pages, 1,
                                     consume_q->kernel_if->u.h.header_page);
        if (retval < consume_q->kernel_if->num_pages) {
-               pr_warn("get_user_pages(consume) failed (retval=%d)", retval);
+               pr_debug("get_user_pages_fast(consume) failed (retval=%d)",
+                       retval);
                qp_release_pages(consume_q->kernel_if->u.h.header_page,
                                 retval, false);
                qp_release_pages(produce_q->kernel_if->u.h.header_page,
index 8170102d1e9389d166396b5699ea480f2ec2e099..4e2f501e55486dbee297865f03148e5b46d09d91 100644 (file)
@@ -220,9 +220,7 @@ static int __init omap_cf_probe(struct platform_device *pdev)
        cf = kzalloc(sizeof *cf, GFP_KERNEL);
        if (!cf)
                return -ENOMEM;
-       init_timer(&cf->timer);
-       cf->timer.function = omap_cf_timer;
-       cf->timer.data = (unsigned long) cf;
+       setup_timer(&cf->timer, omap_cf_timer, (unsigned long)cf);
 
        cf->pdev = pdev;
        platform_set_drvdata(pdev, cf);
index 34ace4854dc242513730582b5a51bbcdaf730cce..0f70b4d58f9e7c2d8bb2b9b6c0dc00d2dca3f308 100644 (file)
@@ -707,11 +707,9 @@ static int pd6729_pci_probe(struct pci_dev *dev,
                }
        } else {
                /* poll Card status change */
-               init_timer(&socket->poll_timer);
-               socket->poll_timer.function = pd6729_interrupt_wrapper;
-               socket->poll_timer.data = (unsigned long)socket;
-               socket->poll_timer.expires = jiffies + HZ;
-               add_timer(&socket->poll_timer);
+               setup_timer(&socket->poll_timer, pd6729_interrupt_wrapper,
+                           (unsigned long)socket);
+               mod_timer(&socket->poll_timer, jiffies + HZ);
        }
 
        for (i = 0; i < MAX_SOCKETS; i++) {
index 933f4657515b56f170bdda60558a2bc9e9a1222c..eed5e9c05353c2fafb30b4b0cc8745472b44aff8 100644 (file)
@@ -726,9 +726,8 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt)
 {
        int ret;
 
-       init_timer(&skt->poll_timer);
-       skt->poll_timer.function = soc_common_pcmcia_poll_event;
-       skt->poll_timer.data = (unsigned long)skt;
+       setup_timer(&skt->poll_timer, soc_common_pcmcia_poll_event,
+                   (unsigned long)skt);
        skt->poll_timer.expires = jiffies + SOC_PCMCIA_POLL_PERIOD;
 
        ret = request_resource(&iomem_resource, &skt->res_skt);
index 8a23ccb41213762317216a8cd10e7be0138638be..965bd84912335868ad92f995bcbe6b553b65da37 100644 (file)
@@ -1236,11 +1236,9 @@ static int yenta_probe(struct pci_dev *dev, const struct pci_device_id *id)
        if (!socket->cb_irq || request_irq(socket->cb_irq, yenta_interrupt, IRQF_SHARED, "yenta", socket)) {
                /* No IRQ or request_irq failed. Poll */
                socket->cb_irq = 0; /* But zero is a valid IRQ number. */
-               init_timer(&socket->poll_timer);
-               socket->poll_timer.function = yenta_interrupt_wrapper;
-               socket->poll_timer.data = (unsigned long)socket;
-               socket->poll_timer.expires = jiffies + HZ;
-               add_timer(&socket->poll_timer);
+               setup_timer(&socket->poll_timer, yenta_interrupt_wrapper,
+                           (unsigned long)socket);
+               mod_timer(&socket->poll_timer, jiffies + HZ);
                dev_printk(KERN_INFO, &dev->dev,
                           "no PCI IRQ, CardBus support disabled for this "
                           "socket.\n");
index bf1295e19f892526f03c9fa3fbc28b8a7a8b20bd..c8d99563d2454fe686aaa69b3b7854e34adbb4ce 100644 (file)
@@ -12,7 +12,6 @@ if SPMI
 
 config SPMI_MSM_PMIC_ARB
        tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)"
-       depends on ARM
        depends on IRQ_DOMAIN
        depends on ARCH_QCOM || COMPILE_TEST
        default ARCH_QCOM
index 20559ab3466da6f87e96283cb917b06f75b3bb29..d7119db49cfe561b5cd534ccf5c0c1bf45ac0ce0 100644 (file)
@@ -1,4 +1,5 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2012-2015, 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
 
 /* PMIC Arbiter configuration registers */
 #define PMIC_ARB_VERSION               0x0000
+#define PMIC_ARB_VERSION_V2_MIN                0x20010000
 #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)))
+/* PMIC Arbiter channel registers offsets */
+#define PMIC_ARB_CMD                   0x00
+#define PMIC_ARB_CONFIG                        0x04
+#define PMIC_ARB_STATUS                        0x08
+#define PMIC_ARB_WDATA0                        0x10
+#define PMIC_ARB_WDATA1                        0x14
+#define PMIC_ARB_RDATA0                        0x18
+#define PMIC_ARB_RDATA1                        0x1C
+#define PMIC_ARB_REG_CHNL(N)           (0x800 + 0x4 * (N))
 
 /* Mapping Table */
 #define SPMI_MAPPING_TABLE_REG(N)      (0x0B00 + (4 * (N)))
@@ -52,6 +49,7 @@
 
 #define SPMI_MAPPING_TABLE_LEN         255
 #define SPMI_MAPPING_TABLE_TREE_DEPTH  16      /* Maximum of 16-bits */
+#define PPID_TO_CHAN_TABLE_SZ          BIT(12) /* PPID is 12bit chan is 1byte*/
 
 /* Ownership Table */
 #define SPMI_OWNERSHIP_TABLE_REG(N)    (0x0700 + (4 * (N)))
@@ -88,6 +86,7 @@ enum pmic_arb_cmd_op_code {
 
 /* Maximum number of support PMIC peripherals */
 #define PMIC_ARB_MAX_PERIPHS           256
+#define PMIC_ARB_MAX_CHNL              128
 #define PMIC_ARB_PERIPH_ID_VALID       (1 << 15)
 #define PMIC_ARB_TIMEOUT_US            100
 #define PMIC_ARB_MAX_TRANS_BYTES       (8)
@@ -98,14 +97,17 @@ enum pmic_arb_cmd_op_code {
 /* interrupt enable bit */
 #define SPMI_PIC_ACC_ENABLE_BIT                BIT(0)
 
+struct pmic_arb_ver_ops;
+
 /**
  * spmi_pmic_arb_dev - SPMI PMIC Arbiter object
  *
- * @base:              address of the PMIC Arbiter core registers.
+ * @rd_base:           on v1 "core", on v2 "observer" register base off DT.
+ * @wr_base:           on v1 "core", on v2 "chnls"    register base off DT.
  * @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.
+ * @channel:           execution environment channel to use for accesses.
  * @irq:               PMIC ARB interrupt.
  * @ee:                        the current Execution Environment
  * @min_apid:          minimum APID (used for bounding IRQ search)
@@ -113,10 +115,14 @@ enum pmic_arb_cmd_op_code {
  * @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
+ * @apid_to_ppid:      in-memory copy of APID -> PPID mapping table.
+ * @ver_ops:           version dependent operations.
+ * @ppid_to_chan       in-memory copy of PPID -> channel (APID) mapping table.
+ *                     v2 only.
  */
 struct spmi_pmic_arb_dev {
-       void __iomem            *base;
+       void __iomem            *rd_base;
+       void __iomem            *wr_base;
        void __iomem            *intr;
        void __iomem            *cnfg;
        raw_spinlock_t          lock;
@@ -129,17 +135,54 @@ struct spmi_pmic_arb_dev {
        struct irq_domain       *domain;
        struct spmi_controller  *spmic;
        u16                     apid_to_ppid[256];
+       const struct pmic_arb_ver_ops *ver_ops;
+       u8                      *ppid_to_chan;
+};
+
+/**
+ * pmic_arb_ver: version dependent functionality.
+ *
+ * @non_data_cmd:      on v1 issues an spmi non-data command.
+ *                     on v2 no HW support, returns -EOPNOTSUPP.
+ * @offset:            on v1 offset of per-ee channel.
+ *                     on v2 offset of per-ee and per-ppid channel.
+ * @fmt_cmd:           formats a GENI/SPMI command.
+ * @owner_acc_status:  on v1 offset of PMIC_ARB_SPMI_PIC_OWNERm_ACC_STATUSn
+ *                     on v2 offset of SPMI_PIC_OWNERm_ACC_STATUSn.
+ * @acc_enable:                on v1 offset of PMIC_ARB_SPMI_PIC_ACC_ENABLEn
+ *                     on v2 offset of SPMI_PIC_ACC_ENABLEn.
+ * @irq_status:                on v1 offset of PMIC_ARB_SPMI_PIC_IRQ_STATUSn
+ *                     on v2 offset of SPMI_PIC_IRQ_STATUSn.
+ * @irq_clear:         on v1 offset of PMIC_ARB_SPMI_PIC_IRQ_CLEARn
+ *                     on v2 offset of SPMI_PIC_IRQ_CLEARn.
+ */
+struct pmic_arb_ver_ops {
+       /* spmi commands (read_cmd, write_cmd, cmd) functionality */
+       u32 (*offset)(struct spmi_pmic_arb_dev *dev, u8 sid, u16 addr);
+       u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc);
+       int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid);
+       /* Interrupts controller functionality (offset of PIC registers) */
+       u32 (*owner_acc_status)(u8 m, u8 n);
+       u32 (*acc_enable)(u8 n);
+       u32 (*irq_status)(u8 n);
+       u32 (*irq_clear)(u8 n);
 };
 
 static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset)
 {
-       return readl_relaxed(dev->base + offset);
+       return readl_relaxed(dev->rd_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);
+       writel_relaxed(val, dev->wr_base + offset);
+}
+
+static inline void pmic_arb_set_rd_cmd(struct spmi_pmic_arb_dev *dev,
+                                      u32 offset, u32 val)
+{
+       writel_relaxed(val, dev->rd_base + offset);
 }
 
 /**
@@ -168,15 +211,16 @@ pa_write_data(struct spmi_pmic_arb_dev *dev, const u8 *buf, u32 reg, u8 bc)
        pmic_arb_base_write(dev, reg, data);
 }
 
-static int pmic_arb_wait_for_done(struct spmi_controller *ctrl)
+static int pmic_arb_wait_for_done(struct spmi_controller *ctrl,
+                                 void __iomem *base, u8 sid, u16 addr)
 {
        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);
+       u32 offset = dev->ver_ops->offset(dev, sid, addr) + PMIC_ARB_STATUS;
 
        while (timeout--) {
-               status = pmic_arb_base_read(dev, offset);
+               status = readl_relaxed(base + offset);
 
                if (status & PMIC_ARB_STATUS_DONE) {
                        if (status & PMIC_ARB_STATUS_DENIED) {
@@ -211,28 +255,45 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl)
        return -ETIMEDOUT;
 }
 
-/* Non-data command */
-static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
+static int
+pmic_arb_non_data_cmd_v1(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;
+       u32 offset = pmic_arb->ver_ops->offset(pmic_arb, sid, 0);
 
        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);
+       pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd);
+       rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, 0);
        raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
 
        return rc;
 }
 
+static int
+pmic_arb_non_data_cmd_v2(struct spmi_controller *ctrl, u8 opc, u8 sid)
+{
+       return -EOPNOTSUPP;
+}
+
+/* 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);
+
+       dev_dbg(&ctrl->dev, "cmd op:0x%x sid:%d\n", opc, sid);
+
+       /* Check for valid non-data command */
+       if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP)
+               return -EINVAL;
+
+       return pmic_arb->ver_ops->non_data_cmd(ctrl, opc, sid);
+}
+
 static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
                             u16 addr, u8 *buf, size_t len)
 {
@@ -241,10 +302,11 @@ static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
        u8 bc = len - 1;
        u32 cmd;
        int rc;
+       u32 offset = pmic_arb->ver_ops->offset(pmic_arb, sid, addr);
 
        if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
                dev_err(&ctrl->dev,
-                       "pmic-arb supports 1..%d bytes per trans, but %d requested",
+                       "pmic-arb supports 1..%d bytes per trans, but:%zu requested",
                        PMIC_ARB_MAX_TRANS_BYTES, len);
                return  -EINVAL;
        }
@@ -259,20 +321,20 @@ static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
        else
                return -EINVAL;
 
-       cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
+       cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc);
 
        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);
+       pmic_arb_set_rd_cmd(pmic_arb, offset + PMIC_ARB_CMD, cmd);
+       rc = pmic_arb_wait_for_done(ctrl, pmic_arb->rd_base, sid, addr);
        if (rc)
                goto done;
 
-       pa_read_data(pmic_arb, buf, PMIC_ARB_RDATA0(pmic_arb->channel),
+       pa_read_data(pmic_arb, buf, offset + PMIC_ARB_RDATA0,
                     min_t(u8, bc, 3));
 
        if (bc > 3)
                pa_read_data(pmic_arb, buf + 4,
-                               PMIC_ARB_RDATA1(pmic_arb->channel), bc - 4);
+                               offset + PMIC_ARB_RDATA1, bc - 4);
 
 done:
        raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
@@ -287,10 +349,11 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
        u8 bc = len - 1;
        u32 cmd;
        int rc;
+       u32 offset = pmic_arb->ver_ops->offset(pmic_arb, sid, addr);
 
        if (bc >= PMIC_ARB_MAX_TRANS_BYTES) {
                dev_err(&ctrl->dev,
-                       "pmic-arb supports 1..%d bytes per trans, but:%d requested",
+                       "pmic-arb supports 1..%d bytes per trans, but:%zu requested",
                        PMIC_ARB_MAX_TRANS_BYTES, len);
                return  -EINVAL;
        }
@@ -307,19 +370,19 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid,
        else
                return -EINVAL;
 
-       cmd = (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
+       cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc);
 
        /* 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));
+       pa_write_data(pmic_arb, buf, offset + PMIC_ARB_WDATA0,
+                     min_t(u8, bc, 3));
        if (bc > 3)
                pa_write_data(pmic_arb, buf + 4,
-                               PMIC_ARB_WDATA1(pmic_arb->channel), bc - 4);
+                               offset + PMIC_ARB_WDATA1, 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);
+       pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd);
+       rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, addr);
        raw_spin_unlock_irqrestore(&pmic_arb->lock, flags);
 
        return rc;
@@ -376,7 +439,7 @@ static void periph_interrupt(struct spmi_pmic_arb_dev *pa, u8 apid)
        u32 status;
        int id;
 
-       status = readl_relaxed(pa->intr + SPMI_PIC_IRQ_STATUS(apid));
+       status = readl_relaxed(pa->intr + pa->ver_ops->irq_status(apid));
        while (status) {
                id = ffs(status) - 1;
                status &= ~(1 << id);
@@ -402,7 +465,7 @@ static void pmic_arb_chained_irq(unsigned int irq, struct irq_desc *desc)
 
        for (i = first; i <= last; ++i) {
                status = readl_relaxed(intr +
-                                      SPMI_PIC_OWNER_ACC_STATUS(pa->ee, i));
+                                     pa->ver_ops->owner_acc_status(pa->ee, i));
                while (status) {
                        id = ffs(status) - 1;
                        status &= ~(1 << id);
@@ -422,7 +485,7 @@ static void qpnpint_irq_ack(struct irq_data *d)
        u8 data;
 
        raw_spin_lock_irqsave(&pa->lock, flags);
-       writel_relaxed(1 << irq, pa->intr + SPMI_PIC_IRQ_CLEAR(apid));
+       writel_relaxed(1 << irq, pa->intr + pa->ver_ops->irq_clear(apid));
        raw_spin_unlock_irqrestore(&pa->lock, flags);
 
        data = 1 << irq;
@@ -439,10 +502,11 @@ static void qpnpint_irq_mask(struct irq_data *d)
        u8 data;
 
        raw_spin_lock_irqsave(&pa->lock, flags);
-       status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid));
+       status = readl_relaxed(pa->intr + pa->ver_ops->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));
+               writel_relaxed(status, pa->intr +
+                              pa->ver_ops->acc_enable(apid));
        }
        raw_spin_unlock_irqrestore(&pa->lock, flags);
 
@@ -460,10 +524,10 @@ static void qpnpint_irq_unmask(struct irq_data *d)
        u8 data;
 
        raw_spin_lock_irqsave(&pa->lock, flags);
-       status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid));
+       status = readl_relaxed(pa->intr + pa->ver_ops->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));
+                               pa->intr + pa->ver_ops->acc_enable(apid));
        }
        raw_spin_unlock_irqrestore(&pa->lock, flags);
 
@@ -624,6 +688,91 @@ static int qpnpint_irq_domain_map(struct irq_domain *d,
        return 0;
 }
 
+/* v1 offset per ee */
+static u32 pmic_arb_offset_v1(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr)
+{
+       return 0x800 + 0x80 * pa->channel;
+}
+
+/* v2 offset per ppid (chan) and per ee */
+static u32 pmic_arb_offset_v2(struct spmi_pmic_arb_dev *pa, u8 sid, u16 addr)
+{
+       u16 ppid = (sid << 8) | (addr >> 8);
+       u8  chan = pa->ppid_to_chan[ppid];
+
+       return 0x1000 * pa->ee + 0x8000 * chan;
+}
+
+static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc)
+{
+       return (opc << 27) | ((sid & 0xf) << 20) | (addr << 4) | (bc & 0x7);
+}
+
+static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc)
+{
+       return (opc << 27) | ((addr & 0xff) << 4) | (bc & 0x7);
+}
+
+static u32 pmic_arb_owner_acc_status_v1(u8 m, u8 n)
+{
+       return 0x20 * m + 0x4 * n;
+}
+
+static u32 pmic_arb_owner_acc_status_v2(u8 m, u8 n)
+{
+       return 0x100000 + 0x1000 * m + 0x4 * n;
+}
+
+static u32 pmic_arb_acc_enable_v1(u8 n)
+{
+       return 0x200 + 0x4 * n;
+}
+
+static u32 pmic_arb_acc_enable_v2(u8 n)
+{
+       return 0x1000 * n;
+}
+
+static u32 pmic_arb_irq_status_v1(u8 n)
+{
+       return 0x600 + 0x4 * n;
+}
+
+static u32 pmic_arb_irq_status_v2(u8 n)
+{
+       return 0x4 + 0x1000 * n;
+}
+
+static u32 pmic_arb_irq_clear_v1(u8 n)
+{
+       return 0xA00 + 0x4 * n;
+}
+
+static u32 pmic_arb_irq_clear_v2(u8 n)
+{
+       return 0x8 + 0x1000 * n;
+}
+
+static const struct pmic_arb_ver_ops pmic_arb_v1 = {
+       .non_data_cmd           = pmic_arb_non_data_cmd_v1,
+       .offset                 = pmic_arb_offset_v1,
+       .fmt_cmd                = pmic_arb_fmt_cmd_v1,
+       .owner_acc_status       = pmic_arb_owner_acc_status_v1,
+       .acc_enable             = pmic_arb_acc_enable_v1,
+       .irq_status             = pmic_arb_irq_status_v1,
+       .irq_clear              = pmic_arb_irq_clear_v1,
+};
+
+static const struct pmic_arb_ver_ops pmic_arb_v2 = {
+       .non_data_cmd           = pmic_arb_non_data_cmd_v2,
+       .offset                 = pmic_arb_offset_v2,
+       .fmt_cmd                = pmic_arb_fmt_cmd_v2,
+       .owner_acc_status       = pmic_arb_owner_acc_status_v2,
+       .acc_enable             = pmic_arb_acc_enable_v2,
+       .irq_status             = pmic_arb_irq_status_v2,
+       .irq_clear              = pmic_arb_irq_clear_v2,
+};
+
 static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
        .map    = qpnpint_irq_domain_map,
        .xlate  = qpnpint_irq_domain_dt_translate,
@@ -634,8 +783,10 @@ 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;
+       void __iomem *core;
+       u32 channel, ee, hw_ver;
        int err, i;
+       bool is_v1;
 
        ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa));
        if (!ctrl)
@@ -645,12 +796,65 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
        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);
+       core = devm_ioremap_resource(&ctrl->dev, res);
+       if (IS_ERR(core)) {
+               err = PTR_ERR(core);
                goto err_put_ctrl;
        }
 
+       hw_ver = readl_relaxed(core + PMIC_ARB_VERSION);
+       is_v1  = (hw_ver < PMIC_ARB_VERSION_V2_MIN);
+
+       dev_info(&ctrl->dev, "PMIC Arb Version-%d (0x%x)\n", (is_v1 ? 1 : 2),
+               hw_ver);
+
+       if (is_v1) {
+               pa->ver_ops = &pmic_arb_v1;
+               pa->wr_base = core;
+               pa->rd_base = core;
+       } else {
+               u8  chan;
+               u16 ppid;
+               u32 regval;
+
+               pa->ver_ops = &pmic_arb_v2;
+
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                  "obsrvr");
+               pa->rd_base = devm_ioremap_resource(&ctrl->dev, res);
+               if (IS_ERR(pa->rd_base)) {
+                       err = PTR_ERR(pa->rd_base);
+                       goto err_put_ctrl;
+               }
+
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                  "chnls");
+               pa->wr_base = devm_ioremap_resource(&ctrl->dev, res);
+               if (IS_ERR(pa->wr_base)) {
+                       err = PTR_ERR(pa->wr_base);
+                       goto err_put_ctrl;
+               }
+
+               pa->ppid_to_chan = devm_kzalloc(&ctrl->dev,
+                                       PPID_TO_CHAN_TABLE_SZ, GFP_KERNEL);
+               if (!pa->ppid_to_chan) {
+                       err = -ENOMEM;
+                       goto err_put_ctrl;
+               }
+               /*
+                * PMIC_ARB_REG_CHNL is a table in HW mapping channel to ppid.
+                * ppid_to_chan is an in-memory invert of that table.
+                */
+               for (chan = 0; chan < PMIC_ARB_MAX_CHNL; ++chan) {
+                       regval = readl_relaxed(core + PMIC_ARB_REG_CHNL(chan));
+                       if (!regval)
+                               continue;
+
+                       ppid = (regval >> 8) & 0xFFF;
+                       pa->ppid_to_chan[ppid] = chan;
+               }
+       }
+
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr");
        pa->intr = devm_ioremap_resource(&ctrl->dev, res);
        if (IS_ERR(pa->intr)) {
@@ -731,9 +935,6 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev)
        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:
index 1d92f5103ebfe885028a28b93aa9fcef87921d39..94938436aef9c7313403882141a8fc1858413190 100644 (file)
@@ -1,4 +1,5 @@
-/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2012-2015, 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
@@ -316,11 +317,6 @@ static int spmi_drv_probe(struct device *dev)
        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);
@@ -335,7 +331,6 @@ fail_probe:
        pm_runtime_disable(dev);
        pm_runtime_set_suspended(dev);
        pm_runtime_put_noidle(dev);
-fail_wakeup:
        return err;
 }
 
index 6276f13e9e126f8085fdc8949d0d8b784e4df14e..65bf0676d54afc785107822f51a696ae373a2702 100644 (file)
@@ -835,7 +835,15 @@ int __uio_register_device(struct module *owner,
        info->uio_dev = idev;
 
        if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) {
-               ret = devm_request_irq(idev->dev, info->irq, uio_interrupt,
+               /*
+                * Note that we deliberately don't use devm_request_irq
+                * here. The parent module can unregister the UIO device
+                * and call pci_disable_msi, which requires that this
+                * irq has been freed. However, the device may have open
+                * FDs at the time of unregister and therefore may not be
+                * freed until they are released.
+                */
+               ret = request_irq(info->irq, uio_interrupt,
                                  info->irq_flags, info->name, idev);
                if (ret)
                        goto err_request_irq;
@@ -871,6 +879,8 @@ void uio_unregister_device(struct uio_info *info)
 
        uio_dev_del_attributes(idev);
 
+       free_irq(idev->info->irq, idev);
+
        device_destroy(&uio_class, MKDEV(uio_major, idev->minor));
 
        return;
index 53bf2c860ad3503a497b0f656ff5306a21ceb5ff..a4621757a47f5184d96b99fda56c20f11562425d 100644 (file)
@@ -166,7 +166,7 @@ static int mxc_w1_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct of_device_id mxc_w1_dt_ids[] = {
+static const struct of_device_id mxc_w1_dt_ids[] = {
        { .compatible = "fsl,imx21-owire" },
        { /* sentinel */ }
 };
index 03321d6a268465a7f9c19754ea84f299dda9c95d..e7d448963a24355551b008495f7193d3e2c97da9 100644 (file)
@@ -72,7 +72,7 @@ struct hdq_data {
 static int omap_hdq_probe(struct platform_device *pdev);
 static int omap_hdq_remove(struct platform_device *pdev);
 
-static struct of_device_id omap_hdq_dt_ids[] = {
+static const struct of_device_id omap_hdq_dt_ids[] = {
        { .compatible = "ti,omap3-1w" },
        {}
 };
index b99a932ad901ff5b81e458349e0100be564fd37f..8f7848c62811aff90bf273b14c1e8146a2f07d32 100644 (file)
@@ -68,7 +68,7 @@ static u8 w1_gpio_read_bit(void *data)
 }
 
 #if defined(CONFIG_OF)
-static struct of_device_id w1_gpio_dt_ids[] = {
+static const struct of_device_id w1_gpio_dt_ids[] = {
        { .compatible = "w1-gpio" },
        {}
 };
index 5a2ba674795eaf2b2b281eaa17289748cbc84320..902c37aef67e6cf308b0777e157a42dead971c6c 100644 (file)
@@ -646,12 +646,13 @@ struct hv_input_signal_event_buffer {
 };
 
 struct vmbus_channel {
+       /* Unique channel id */
+       int id;
+
        struct list_head listentry;
 
        struct hv_device *device_obj;
 
-       struct work_struct work;
-
        enum vmbus_channel_state state;
 
        struct vmbus_channel_offer_channel offermsg;
@@ -672,7 +673,6 @@ struct vmbus_channel {
        struct hv_ring_buffer_info outbound;    /* send to parent */
        struct hv_ring_buffer_info inbound;     /* receive from parent */
        spinlock_t inbound_lock;
-       struct workqueue_struct *controlwq;
 
        struct vmbus_close_msg close_msg;
 
@@ -758,6 +758,9 @@ struct vmbus_channel {
         * link up channels based on their CPU affinity.
         */
        struct list_head percpu_list;
+
+       int num_sc;
+       int next_oc;
 };
 
 static inline void set_channel_read_state(struct vmbus_channel *c, bool state)
@@ -861,6 +864,14 @@ extern int vmbus_sendpacket(struct vmbus_channel *channel,
                                  enum vmbus_packet_type type,
                                  u32 flags);
 
+extern int vmbus_sendpacket_ctl(struct vmbus_channel *channel,
+                                 void *buffer,
+                                 u32 bufferLen,
+                                 u64 requestid,
+                                 enum vmbus_packet_type type,
+                                 u32 flags,
+                                 bool kick_q);
+
 extern int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
                                            struct hv_page_buffer pagebuffers[],
                                            u32 pagecount,
@@ -868,6 +879,15 @@ extern int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
                                            u32 bufferlen,
                                            u64 requestid);
 
+extern int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
+                                          struct hv_page_buffer pagebuffers[],
+                                          u32 pagecount,
+                                          void *buffer,
+                                          u32 bufferlen,
+                                          u64 requestid,
+                                          u32 flags,
+                                          bool kick_q);
+
 extern int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
                                        struct hv_multipage_buffer *mpb,
                                        void *buffer,
@@ -1106,6 +1126,16 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver);
                        0x9A, 0xE7, 0x6B, 0x17, 0x49, 0x77, 0xC1, 0x92 \
                }
 
+/*
+ * NetworkDirect. This is the guest RDMA service.
+ * {8c2eaf3d-32a7-4b09-ab99-bd1f1c86b501}
+ */
+#define HV_ND_GUID \
+       .guid = { \
+                       0x3d, 0xaf, 0x2e, 0x8c, 0xa7, 0x32, 0x09, 0x4b, \
+                       0xab, 0x99, 0xbd, 0x1f, 0x1c, 0x86, 0xb5, 0x01 \
+               }
+
 /*
  * Common header for Hyper-V ICs
  */
@@ -1213,6 +1243,7 @@ void hv_kvp_onchannelcallback(void *);
 int hv_vss_init(struct hv_util_service *);
 void hv_vss_deinit(void);
 void hv_vss_onchannelcallback(void *);
+void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid);
 
 extern struct resource hyperv_mmio;
 
index fa02e55e5a2e1e2ea564829e1025a09d0722e4e2..42b33f03d1dfac0d823c942a133830801d138a32 100644 (file)
@@ -64,6 +64,8 @@ void __iomem *devm_ioremap(struct device *dev, resource_size_t offset,
                           resource_size_t size);
 void __iomem *devm_ioremap_nocache(struct device *dev, resource_size_t offset,
                                   resource_size_t size);
+void __iomem *devm_ioremap_wc(struct device *dev, resource_size_t offset,
+                                  resource_size_t size);
 void devm_iounmap(struct device *dev, void __iomem *addr);
 int check_signature(const volatile void __iomem *io_addr,
                        const unsigned char *signature, int length);
diff --git a/include/linux/jz4780-nemc.h b/include/linux/jz4780-nemc.h
new file mode 100644 (file)
index 0000000..e7f1cc7
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * JZ4780 NAND/external memory controller (NEMC)
+ *
+ * Copyright (c) 2015 Imagination Technologies
+ * Author: Alex Smith <alex@alex-smith.me.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __LINUX_JZ4780_NEMC_H__
+#define __LINUX_JZ4780_NEMC_H__
+
+#include <linux/types.h>
+
+struct device;
+
+/*
+ * Number of NEMC banks. Note that there are actually 6, but they are numbered
+ * from 1.
+ */
+#define JZ4780_NEMC_NUM_BANKS  7
+
+/**
+ * enum jz4780_nemc_bank_type - device types which can be connected to a bank
+ * @JZ4780_NEMC_BANK_SRAM: SRAM
+ * @JZ4780_NEMC_BANK_NAND: NAND
+ */
+enum jz4780_nemc_bank_type {
+       JZ4780_NEMC_BANK_SRAM,
+       JZ4780_NEMC_BANK_NAND,
+};
+
+extern unsigned int jz4780_nemc_num_banks(struct device *dev);
+
+extern void jz4780_nemc_set_type(struct device *dev, unsigned int bank,
+                                enum jz4780_nemc_bank_type type);
+extern void jz4780_nemc_assert(struct device *dev, unsigned int bank,
+                              bool assert);
+
+#endif /* __LINUX_JZ4780_NEMC_H__ */
index f97010576f56181be46683434b27d1095c855409..16a498f48169a186d45ccd74366873e3d6878ffe 100644 (file)
@@ -127,7 +127,7 @@ struct arizona {
        struct regmap_irq_chip_data *aod_irq_chip;
        struct regmap_irq_chip_data *irq_chip;
 
-       bool hpdet_magic;
+       bool hpdet_clamp;
        unsigned int hp_ena;
 
        struct mutex clk_lock;
index ee80dd7d9f60fb6e289f798d37b8725d55a71d02..819077c326901384ec3bdb8d16217a1363ab8e28 100644 (file)
@@ -52,6 +52,7 @@
 #define MISC_DYNAMIC_MINOR     255
 
 struct device;
+struct attribute_group;
 
 struct miscdevice  {
        int minor;
@@ -60,6 +61,7 @@ struct miscdevice  {
        struct list_head list;
        struct device *parent;
        struct device *this_device;
+       const struct attribute_group **groups;
        const char *nodename;
        umode_t mode;
 };
index 25d942d1da27095e6366d720a0f8de58009cb5f3..11dc22a6983b55da3c2c356cb375fa27dbad0ed8 100644 (file)
@@ -440,7 +440,7 @@ int clockevents_unbind_device(struct clock_event_device *ced, int cpu)
        mutex_unlock(&clockevents_mutex);
        return ret;
 }
-EXPORT_SYMBOL_GPL(clockevents_unbind);
+EXPORT_SYMBOL_GPL(clockevents_unbind_device);
 
 /* Sanity check of state transition callbacks */
 static int clockevents_sanity_check(struct clock_event_device *dev)
index 0f1dd2e9d2c1e572386acb280695e76a2f58406e..fbe2aac522e67dc16708040128337eec946cd4ad 100644 (file)
@@ -71,6 +71,34 @@ void __iomem *devm_ioremap_nocache(struct device *dev, resource_size_t offset,
 }
 EXPORT_SYMBOL(devm_ioremap_nocache);
 
+/**
+ * devm_ioremap_wc - Managed ioremap_wc()
+ * @dev: Generic device to remap IO address for
+ * @offset: BUS offset to map
+ * @size: Size of map
+ *
+ * Managed ioremap_wc().  Map is automatically unmapped on driver detach.
+ */
+void __iomem *devm_ioremap_wc(struct device *dev, resource_size_t offset,
+                             resource_size_t size)
+{
+       void __iomem **ptr, *addr;
+
+       ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return NULL;
+
+       addr = ioremap_wc(offset, size);
+       if (addr) {
+               *ptr = addr;
+               devres_add(dev, ptr);
+       } else
+               devres_free(ptr);
+
+       return addr;
+}
+EXPORT_SYMBOL(devm_ioremap_wc);
+
 /**
  * devm_iounmap - Managed iounmap()
  * @dev: Generic device to unmap for
old mode 100644 (file)
new mode 100755 (executable)
index e9cc689..74086a5
@@ -1,8 +1,8 @@
 #!/usr/bin/env python
 
-"""Find Kconfig identifiers that are referenced but not defined."""
+"""Find Kconfig symbols that are referenced but not defined."""
 
-# (c) 2014 Valentin Rothberg <valentinrothberg@gmail.com>
+# (c) 2014-2015 Valentin Rothberg <Valentin.Rothberg@lip6.fr>
 # (c) 2014 Stefan Hengelein <stefan.hengelein@fau.de>
 #
 # Licensed under the terms of the GNU GPL License version 2
@@ -10,7 +10,9 @@
 
 import os
 import re
+import sys
 from subprocess import Popen, PIPE, STDOUT
+from optparse import OptionParser
 
 
 # regex expressions
@@ -32,22 +34,149 @@ REGEX_KCONFIG_HELP = re.compile(r"^\s+(help|---help---)\s*$")
 REGEX_FILTER_FEATURES = re.compile(r"[A-Za-z0-9]$")
 
 
+def parse_options():
+    """The user interface of this module."""
+    usage = "%prog [options]\n\n"                                              \
+            "Run this tool to detect Kconfig symbols that are referenced but " \
+            "not defined in\nKconfig.  The output of this tool has the "       \
+            "format \'Undefined symbol\\tFile list\'\n\n"                      \
+            "If no option is specified, %prog will default to check your\n"    \
+            "current tree.  Please note that specifying commits will "         \
+            "\'git reset --hard\'\nyour current tree!  You may save "          \
+            "uncommitted changes to avoid losing data."
+
+    parser = OptionParser(usage=usage)
+
+    parser.add_option('-c', '--commit', dest='commit', action='store',
+                      default="",
+                      help="Check if the specified commit (hash) introduces "
+                           "undefined Kconfig symbols.")
+
+    parser.add_option('-d', '--diff', dest='diff', action='store',
+                      default="",
+                      help="Diff undefined symbols between two commits.  The "
+                           "input format bases on Git log's "
+                           "\'commmit1..commit2\'.")
+
+    parser.add_option('', '--force', dest='force', action='store_true',
+                      default=False,
+                      help="Reset current Git tree even when it's dirty.")
+
+    (opts, _) = parser.parse_args()
+
+    if opts.commit and opts.diff:
+        sys.exit("Please specify only one option at once.")
+
+    if opts.diff and not re.match(r"^[\w\-\.]+\.\.[\w\-\.]+$", opts.diff):
+        sys.exit("Please specify valid input in the following format: "
+                 "\'commmit1..commit2\'")
+
+    if opts.commit or opts.diff:
+        if not opts.force and tree_is_dirty():
+            sys.exit("The current Git tree is dirty (see 'git status').  "
+                     "Running this script may\ndelete important data since it "
+                     "calls 'git reset --hard' for some performance\nreasons. "
+                     " Please run this script in a clean Git tree or pass "
+                     "'--force' if you\nwant to ignore this warning and "
+                     "continue.")
+
+    return opts
+
+
 def main():
     """Main function of this module."""
+    opts = parse_options()
+
+    if opts.commit or opts.diff:
+        head = get_head()
+
+        # get commit range
+        commit_a = None
+        commit_b = None
+        if opts.commit:
+            commit_a = opts.commit + "~"
+            commit_b = opts.commit
+        elif opts.diff:
+            split = opts.diff.split("..")
+            commit_a = split[0]
+            commit_b = split[1]
+            undefined_a = {}
+            undefined_b = {}
+
+        # get undefined items before the commit
+        execute("git reset --hard %s" % commit_a)
+        undefined_a = check_symbols()
+
+        # get undefined items for the commit
+        execute("git reset --hard %s" % commit_b)
+        undefined_b = check_symbols()
+
+        # report cases that are present for the commit but not before
+        for feature in sorted(undefined_b):
+            # feature has not been undefined before
+            if not feature in undefined_a:
+                files = sorted(undefined_b.get(feature))
+                print "%s\t%s" % (feature, ", ".join(files))
+            # check if there are new files that reference the undefined feature
+            else:
+                files = sorted(undefined_b.get(feature) -
+                               undefined_a.get(feature))
+                if files:
+                    print "%s\t%s" % (feature, ", ".join(files))
+
+        # reset to head
+        execute("git reset --hard %s" % head)
+
+    # default to check the entire tree
+    else:
+        undefined = check_symbols()
+        for feature in sorted(undefined):
+            files = sorted(undefined.get(feature))
+            print "%s\t%s" % (feature, ", ".join(files))
+
+
+def execute(cmd):
+    """Execute %cmd and return stdout.  Exit in case of error."""
+    pop = Popen(cmd, stdout=PIPE, stderr=STDOUT, shell=True)
+    (stdout, _) = pop.communicate()  # wait until finished
+    if pop.returncode != 0:
+        sys.exit(stdout)
+    return stdout
+
+
+def tree_is_dirty():
+    """Return true if the current working tree is dirty (i.e., if any file has
+    been added, deleted, modified, renamed or copied but not committed)."""
+    stdout = execute("git status --porcelain")
+    for line in stdout:
+        if re.findall(r"[URMADC]{1}", line[:2]):
+            return True
+    return False
+
+
+def get_head():
+    """Return commit hash of current HEAD."""
+    stdout = execute("git rev-parse HEAD")
+    return stdout.strip('\n')
+
+
+def check_symbols():
+    """Find undefined Kconfig symbols and return a dict with the symbol as key
+    and a list of referencing files as value."""
     source_files = []
     kconfig_files = []
     defined_features = set()
     referenced_features = dict()  # {feature: [files]}
 
     # use 'git ls-files' to get the worklist
-    pop = Popen("git ls-files", stdout=PIPE, stderr=STDOUT, shell=True)
-    (stdout, _) = pop.communicate()  # wait until finished
+    stdout = execute("git ls-files")
     if len(stdout) > 0 and stdout[-1] == "\n":
         stdout = stdout[:-1]
 
     for gitfile in stdout.rsplit("\n"):
-        if ".git" in gitfile or "ChangeLog" in gitfile or \
-                ".log" in gitfile or os.path.isdir(gitfile):
+        if ".git" in gitfile or "ChangeLog" in gitfile or      \
+                ".log" in gitfile or os.path.isdir(gitfile) or \
+                gitfile.startswith("tools/"):
             continue
         if REGEX_FILE_KCONFIG.match(gitfile):
             kconfig_files.append(gitfile)
@@ -61,7 +190,7 @@ def main():
     for kfile in kconfig_files:
         parse_kconfig_file(kfile, defined_features, referenced_features)
 
-    print "Undefined symbol used\tFile list"
+    undefined = {}  # {feature: [files]}
     for feature in sorted(referenced_features):
         # filter some false positives
         if feature == "FOO" or feature == "BAR" or \
@@ -72,8 +201,8 @@ def main():
                 # avoid false positives for kernel modules
                 if feature[:-len("_MODULE")] in defined_features:
                     continue
-            files = referenced_features.get(feature)
-            print "%s\t%s" % (feature, ", ".join(files))
+            undefined[feature] = referenced_features.get(feature)
+    return undefined
 
 
 def parse_source_file(sfile, referenced_features):
index 57da0ceda03fa7eb0f4ce384ae040703a7c99f5c..eff4b4d512b7b8fbd8bfa31c3c0ee3a79474a478 100644 (file)
@@ -840,8 +840,8 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w,
        priv->arizona->hp_ena &= ~mask;
        priv->arizona->hp_ena |= val;
 
-       /* Force off if HPDET magic is active */
-       if (priv->arizona->hpdet_magic)
+       /* Force off if HPDET clamp is active */
+       if (priv->arizona->hpdet_clamp)
                val = 0;
 
        regmap_update_bits_async(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1,
index 99ffe61051a702ab386a45a13c364bc1a77f0f25..a8ab795569266f6e48550e4fb400d27a6f4c817d 100644 (file)
@@ -3,7 +3,7 @@
 CC = $(CROSS_COMPILE)gcc
 PTHREAD_LIBS = -lpthread
 WARNINGS = -Wall -Wextra
-CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS)
+CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS) $(shell getconf LFS_CFLAGS)
 
 all: hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon
 %: %.c
index 5e63f70bd956e56f9506ef306e11ff2206c23b52..506dd01488286f8033f3ceaf3915fd06742f33de 100644 (file)
@@ -81,6 +81,7 @@ static int vss_operate(int operation)
        char match[] = "/dev/";
        FILE *mounts;
        struct mntent *ent;
+       char errdir[1024] = {0};
        unsigned int cmd;
        int error = 0, root_seen = 0, save_errno = 0;
 
@@ -115,6 +116,8 @@ static int vss_operate(int operation)
                        goto err;
        }
 
+       endmntent(mounts);
+
        if (root_seen) {
                error |= vss_do_freeze("/", cmd);
                if (error && operation == VSS_OP_FREEZE)
@@ -124,16 +127,19 @@ static int vss_operate(int operation)
        goto out;
 err:
        save_errno = errno;
+       if (ent) {
+               strncpy(errdir, ent->mnt_dir, sizeof(errdir)-1);
+               endmntent(mounts);
+       }
        vss_operate(VSS_OP_THAW);
        /* Call syslog after we thaw all filesystems */
        if (ent)
                syslog(LOG_ERR, "FREEZE of %s failed; error:%d %s",
-                      ent->mnt_dir, save_errno, strerror(save_errno));
+                      errdir, save_errno, strerror(save_errno));
        else
                syslog(LOG_ERR, "FREEZE of / failed; error:%d %s", save_errno,
                       strerror(save_errno));
 out:
-       endmntent(mounts);
        return error;
 }