]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'char-misc/char-misc-next'
authorThierry Reding <treding@nvidia.com>
Thu, 24 Oct 2013 12:59:31 +0000 (14:59 +0200)
committerThierry Reding <treding@nvidia.com>
Thu, 24 Oct 2013 12:59:31 +0000 (14:59 +0200)
110 files changed:
Documentation/ABI/testing/sysfs-class-mic.txt [new file with mode: 0644]
Documentation/ABI/testing/sysfs-driver-sunxi-sid [new file with mode: 0644]
Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt [new file with mode: 0644]
Documentation/devicetree/bindings/misc/ti,dac7512.txt [new file with mode: 0644]
Documentation/extcon/porting-android-switch-class
Documentation/mic/mic_overview.txt [new file with mode: 0644]
Documentation/mic/mpssd/.gitignore [new file with mode: 0644]
Documentation/mic/mpssd/Makefile [new file with mode: 0644]
Documentation/mic/mpssd/micctrl [new file with mode: 0755]
Documentation/mic/mpssd/mpss [new file with mode: 0755]
Documentation/mic/mpssd/mpssd.c [new file with mode: 0644]
Documentation/mic/mpssd/mpssd.h [new file with mode: 0644]
Documentation/mic/mpssd/sysfs.c [new file with mode: 0644]
drivers/char/hpet.c
drivers/char/misc.c
drivers/char/nwbutton.c
drivers/char/rtc.c
drivers/char/snsc.c
drivers/char/snsc_event.c
drivers/char/tlclk.c
drivers/char/xilinx_hwicap/xilinx_hwicap.c
drivers/extcon/extcon-adc-jack.c
drivers/extcon/extcon-arizona.c
drivers/extcon/extcon-class.c
drivers/extcon/extcon-gpio.c
drivers/extcon/extcon-max77693.c
drivers/extcon/extcon-max8997.c
drivers/extcon/extcon-palmas.c
drivers/hv/channel.c
drivers/hv/channel_mgmt.c
drivers/hv/connection.c
drivers/hv/hv.c
drivers/hv/hv_util.c
drivers/hv/hyperv_vmbus.h
drivers/hv/vmbus_drv.c
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/arm-charlcd.c
drivers/misc/bh1780gli.c
drivers/misc/bmp085.c
drivers/misc/cb710/core.c
drivers/misc/eeprom/Kconfig
drivers/misc/eeprom/Makefile
drivers/misc/eeprom/at25.c
drivers/misc/eeprom/sunxi_sid.c [new file with mode: 0644]
drivers/misc/ibmasm/module.c
drivers/misc/mei/amthif.c
drivers/misc/mei/client.c
drivers/misc/mei/client.h
drivers/misc/mei/hbm.c
drivers/misc/mei/hw-me-regs.h
drivers/misc/mei/init.c
drivers/misc/mei/interrupt.c
drivers/misc/mei/main.c
drivers/misc/mei/mei_dev.h
drivers/misc/mei/pci-me.c
drivers/misc/mic/Kconfig [new file with mode: 0644]
drivers/misc/mic/Makefile [new file with mode: 0644]
drivers/misc/mic/card/Makefile [new file with mode: 0644]
drivers/misc/mic/card/mic_debugfs.c [new file with mode: 0644]
drivers/misc/mic/card/mic_device.c [new file with mode: 0644]
drivers/misc/mic/card/mic_device.h [new file with mode: 0644]
drivers/misc/mic/card/mic_virtio.c [new file with mode: 0644]
drivers/misc/mic/card/mic_virtio.h [new file with mode: 0644]
drivers/misc/mic/card/mic_x100.c [new file with mode: 0644]
drivers/misc/mic/card/mic_x100.h [new file with mode: 0644]
drivers/misc/mic/common/mic_dev.h [new file with mode: 0644]
drivers/misc/mic/host/Makefile [new file with mode: 0644]
drivers/misc/mic/host/mic_boot.c [new file with mode: 0644]
drivers/misc/mic/host/mic_debugfs.c [new file with mode: 0644]
drivers/misc/mic/host/mic_device.h [new file with mode: 0644]
drivers/misc/mic/host/mic_fops.c [new file with mode: 0644]
drivers/misc/mic/host/mic_fops.h [new file with mode: 0644]
drivers/misc/mic/host/mic_intr.c [new file with mode: 0644]
drivers/misc/mic/host/mic_intr.h [new file with mode: 0644]
drivers/misc/mic/host/mic_main.c [new file with mode: 0644]
drivers/misc/mic/host/mic_smpt.c [new file with mode: 0644]
drivers/misc/mic/host/mic_smpt.h [new file with mode: 0644]
drivers/misc/mic/host/mic_sysfs.c [new file with mode: 0644]
drivers/misc/mic/host/mic_virtio.c [new file with mode: 0644]
drivers/misc/mic/host/mic_virtio.h [new file with mode: 0644]
drivers/misc/mic/host/mic_x100.c [new file with mode: 0644]
drivers/misc/mic/host/mic_x100.h [new file with mode: 0644]
drivers/misc/phantom.c
drivers/misc/pti.c
drivers/misc/ti_dac7512.c
drivers/misc/tifm_7xx1.c
drivers/misc/vmw_vmci/vmci_guest.c
drivers/misc/vmw_vmci/vmci_host.c
drivers/misc/vmw_vmci/vmci_queue_pair.c
drivers/pcmcia/pd6729.c
drivers/pcmcia/yenta_socket.c
drivers/uio/uio.c
drivers/uio/uio_aec.c
drivers/uio/uio_cif.c
drivers/uio/uio_mf624.c
drivers/uio/uio_netx.c
drivers/uio/uio_pdrv_genirq.c
drivers/uio/uio_sercos3.c
drivers/w1/masters/ds1wm.c
drivers/w1/masters/omap_hdq.c
include/linux/extcon.h
include/linux/extcon/extcon-adc-jack.h
include/linux/extcon/extcon-gpio.h
include/linux/hyperv.h
include/uapi/linux/Kbuild
include/uapi/linux/mic_common.h [new file with mode: 0644]
include/uapi/linux/mic_ioctl.h [new file with mode: 0644]
tools/hv/hv_kvp_daemon.c
tools/hv/hv_vss_daemon.c

diff --git a/Documentation/ABI/testing/sysfs-class-mic.txt b/Documentation/ABI/testing/sysfs-class-mic.txt
new file mode 100644 (file)
index 0000000..13f48af
--- /dev/null
@@ -0,0 +1,157 @@
+What:          /sys/class/mic/
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               The mic class directory belongs to Intel MIC devices and
+               provides information per MIC device. An Intel MIC device is a
+               PCIe form factor add-in Coprocessor card based on the Intel Many
+               Integrated Core (MIC) architecture that runs a Linux OS.
+
+What:          /sys/class/mic/mic(x)
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               The directories /sys/class/mic/mic0, /sys/class/mic/mic1 etc.,
+               represent MIC devices (0,1,..etc). Each directory has
+               information specific to that MIC device.
+
+What:          /sys/class/mic/mic(x)/family
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               Provides information about the Coprocessor family for an Intel
+               MIC device. For example - "x100"
+
+What:          /sys/class/mic/mic(x)/stepping
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               Provides information about the silicon stepping for an Intel
+               MIC device. For example - "A0" or "B0"
+
+What:          /sys/class/mic/mic(x)/state
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               When read, this entry provides the current state of an Intel
+               MIC device in the context of the card OS. Possible values that
+               will be read are:
+               "offline" - The MIC device is ready to boot the card OS. On
+               reading this entry after an OSPM resume, a "boot" has to be
+               written to this entry if the card was previously shutdown
+               during OSPM suspend.
+               "online" - The MIC device has initiated booting a card OS.
+               "shutting_down" - The card OS is shutting down.
+               "reset_failed" - The MIC device has failed to reset.
+               "suspending" - The MIC device is currently being prepared for
+               suspend. On reading this entry, a "suspend" has to be written
+               to the state sysfs entry to ensure the card is shutdown during
+               OSPM suspend.
+               "suspended" - The MIC device has been suspended.
+
+               When written, this sysfs entry triggers different state change
+               operations depending upon the current state of the card OS.
+               Acceptable values are:
+               "boot" - Boot the card OS image specified by the combination
+                        of firmware, ramdisk, cmdline and bootmode
+                       sysfs entries.
+               "reset" - Initiates device reset.
+               "shutdown" - Initiates card OS shutdown.
+               "suspend" - Initiates card OS shutdown and also marks the card
+               as suspended.
+
+What:          /sys/class/mic/mic(x)/shutdown_status
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               An Intel MIC device runs a Linux OS during its operation. This
+               OS can shutdown because of various reasons. When read, this
+               entry provides the status on why the card OS was shutdown.
+               Possible values are:
+               "nop" -  shutdown status is not applicable, when the card OS is
+                       "online"
+               "crashed" - Shutdown because of a HW or SW crash.
+               "halted" - Shutdown because of a halt command.
+               "poweroff" - Shutdown because of a poweroff command.
+               "restart" - Shutdown because of a restart command.
+
+What:          /sys/class/mic/mic(x)/cmdline
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               An Intel MIC device runs a Linux OS during its operation. Before
+               booting this card OS, it is possible to pass kernel command line
+               options to configure various features in it, similar to
+               self-bootable machines. When read, this entry provides
+               information about the current kernel command line options set to
+               boot the card OS. This entry can be written to change the
+               existing kernel command line options. Typically, the user would
+               want to read the current command line options, append new ones
+               or modify existing ones and then write the whole kernel command
+               line back to this entry.
+
+What:          /sys/class/mic/mic(x)/firmware
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               When read, this sysfs entry provides the path name under
+               /lib/firmware/ where the firmware image to be booted on the
+               card can be found. The entry can be written to change the
+               firmware image location under /lib/firmware/.
+
+What:          /sys/class/mic/mic(x)/ramdisk
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               When read, this sysfs entry provides the path name under
+               /lib/firmware/ where the ramdisk image to be used during card
+               OS boot can be found. The entry can be written to change
+               the ramdisk image location under /lib/firmware/.
+
+What:          /sys/class/mic/mic(x)/bootmode
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               When read, this sysfs entry provides the current bootmode for
+               the card. This sysfs entry can be written with the following
+               valid strings:
+               a) linux - Boot a Linux image.
+               b) elf - Boot an elf image for flash updates.
+
+What:          /sys/class/mic/mic(x)/log_buf_addr
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               An Intel MIC device runs a Linux OS during its operation. For
+               debugging purpose and early kernel boot messages, the user can
+               access the card OS log buffer via debugfs. When read, this entry
+               provides the kernel virtual address of the buffer where the card
+               OS log buffer can be read. This entry is written by the host
+               configuration daemon to set the log buffer address. The correct
+               log buffer address to be written can be found in the System.map
+               file of the card OS.
+
+What:          /sys/class/mic/mic(x)/log_buf_len
+Date:          October 2013
+KernelVersion: 3.13
+Contact:       Sudeep Dutt <sudeep.dutt@intel.com>
+Description:
+               An Intel MIC device runs a Linux OS during its operation. For
+               debugging purpose and early kernel boot messages, the user can
+               access the card OS log buffer via debugfs. When read, this entry
+               provides the kernel virtual address where the card OS log buffer
+               length can be read. This entry is written by host configuration
+               daemon to set the log buffer length address. The correct log
+               buffer length address to be written can be found in the
+               System.map file of the card OS.
diff --git a/Documentation/ABI/testing/sysfs-driver-sunxi-sid b/Documentation/ABI/testing/sysfs-driver-sunxi-sid
new file mode 100644 (file)
index 0000000..ffb9536
--- /dev/null
@@ -0,0 +1,22 @@
+What:          /sys/devices/*/<our-device>/eeprom
+Date:          August 2013
+Contact:       Oliver Schinagl <oliver@schinagl.nl>
+Description:   read-only access to the SID (Security-ID) on current
+               A-series SoC's from Allwinner. Currently supports A10, A10s, A13
+               and A20 CPU's. The earlier A1x series of SoCs exports 16 bytes,
+               whereas the newer A20 SoC exposes 512 bytes split into sections.
+               Besides the 16 bytes of SID, there's also an SJTAG area,
+               HDMI-HDCP key and some custom keys. Below a quick overview, for
+               details see the user manual:
+               0x000  128 bit root-key (sun[457]i)
+               0x010  128 bit boot-key (sun7i)
+               0x020   64 bit security-jtag-key (sun7i)
+               0x028   16 bit key configuration (sun7i)
+               0x02b   16 bit custom-vendor-key (sun7i)
+               0x02c  320 bit low general key (sun7i)
+               0x040   32 bit read-control access (sun7i)
+               0x064  224 bit low general key (sun7i)
+               0x080 2304 bit HDCP-key (sun7i)
+               0x1a0  768 bit high general key (sun7i)
+Users:         any user space application which wants to read the SID on
+               Allwinner's A-series of CPU's.
diff --git a/Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt b/Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt
new file mode 100644 (file)
index 0000000..68ba372
--- /dev/null
@@ -0,0 +1,17 @@
+Allwinner sunxi-sid
+
+Required properties:
+- compatible: "allwinner,sun4i-sid" or "allwinner,sun7i-a20-sid".
+- reg: Should contain registers location and length
+
+Example for sun4i:
+       sid@01c23800 {
+               compatible = "allwinner,sun4i-sid";
+               reg = <0x01c23800 0x10>
+       };
+
+Example for sun7i:
+       sid@01c23800 {
+               compatible = "allwinner,sun7i-a20-sid";
+               reg = <0x01c23800 0x200>
+       };
diff --git a/Documentation/devicetree/bindings/misc/ti,dac7512.txt b/Documentation/devicetree/bindings/misc/ti,dac7512.txt
new file mode 100644 (file)
index 0000000..1db4593
--- /dev/null
@@ -0,0 +1,20 @@
+TI DAC7512 DEVICETREE BINDINGS
+
+Required properties:
+
+       - "compatible"          Must be set to "ti,dac7512"
+
+Property rules described in Documentation/devicetree/bindings/spi/spi-bus.txt
+apply. In particular, "reg" and "spi-max-frequency" properties must be given.
+
+
+Example:
+
+       spi_master {
+               dac7512: dac7512@0 {
+                       compatible = "ti,dac7512";
+                       reg = <0>; /* CS0 */
+                       spi-max-frequency = <1000000>;
+               };
+       };
+
index eb0fa5f4fe88f4b6e6b2b381f41561367bd80be8..5377f63179613e6f09721bf878e52e7f04a141a8 100644 (file)
@@ -25,8 +25,10 @@ MyungJoo Ham <myungjoo.ham@samsung.com>
     @print_state: no change but type change (switch_dev->extcon_dev)
 
 - switch_dev_register(sdev, dev)
-       => extcon_dev_register(edev, dev)
-       : no change but type change (sdev->edev)
+       => extcon_dev_register(edev)
+       : type change (sdev->edev)
+       : remove second param('dev'). if edev has parent device, should store
+         'dev' to 'edev.dev.parent' before registering extcon device
 - switch_dev_unregister(sdev)
        => extcon_dev_unregister(edev)
        : no change but type change (sdev->edev)
diff --git a/Documentation/mic/mic_overview.txt b/Documentation/mic/mic_overview.txt
new file mode 100644 (file)
index 0000000..b419292
--- /dev/null
@@ -0,0 +1,51 @@
+An Intel MIC X100 device is a PCIe form factor add-in coprocessor
+card based on the Intel Many Integrated Core (MIC) architecture
+that runs a Linux OS. It is a PCIe endpoint in a platform and therefore
+implements the three required standard address spaces i.e. configuration,
+memory and I/O. The host OS loads a device driver as is typical for
+PCIe devices. The card itself runs a bootstrap after reset that
+transfers control to the card OS downloaded from the host driver. The
+host driver supports OSPM suspend and resume operations. It shuts down
+the card during suspend and reboots the card OS during resume.
+The card OS as shipped by Intel is a Linux kernel with modifications
+for the X100 devices.
+
+Since it is a PCIe card, it does not have the ability to host hardware
+devices for networking, storage and console. We provide these devices
+on X100 coprocessors thus enabling a self-bootable equivalent environment
+for applications. A key benefit of our solution is that it leverages
+the standard virtio framework for network, disk and console devices,
+though in our case the virtio framework is used across a PCIe bus.
+
+Here is a block diagram of the various components described above. The
+virtio backends are situated on the host rather than the card given better
+single threaded performance for the host compared to MIC, the ability of
+the host to initiate DMA's to/from the card using the MIC DMA engine and
+the fact that the virtio block storage backend can only be on the host.
+
+                              |
+       +----------+           |             +----------+
+       | Card OS  |           |             | Host OS  |
+       +----------+           |             +----------+
+                              |
++-------+ +--------+ +------+ | +---------+  +--------+ +--------+
+| Virtio| |Virtio  | |Virtio| | |Virtio   |  |Virtio  | |Virtio  |
+| Net   | |Console | |Block | | |Net      |  |Console | |Block   |
+| Driver| |Driver  | |Driver| | |backend  |  |backend | |backend |
++-------+ +--------+ +------+ | +---------+  +--------+ +--------+
+    |         |         |     |      |            |         |
+    |         |         |     |User  |            |         |
+    |         |         |     |------|------------|---------|-------
+    +-------------------+     |Kernel +--------------------------+
+              |               |       | Virtio over PCIe IOCTLs  |
+              |               |       +--------------------------+
+      +--------------+        |                   |
+      |Intel MIC     |        |            +---------------+
+      |Card Driver   |        |            |Intel MIC      |
+      +--------------+        |            |Host Driver    |
+              |               |            +---------------+
+              |               |                   |
+     +-------------------------------------------------------------+
+     |                                                             |
+     |                    PCIe Bus                                 |
+     +-------------------------------------------------------------+
diff --git a/Documentation/mic/mpssd/.gitignore b/Documentation/mic/mpssd/.gitignore
new file mode 100644 (file)
index 0000000..8b7c72f
--- /dev/null
@@ -0,0 +1 @@
+mpssd
diff --git a/Documentation/mic/mpssd/Makefile b/Documentation/mic/mpssd/Makefile
new file mode 100644 (file)
index 0000000..eb860a7
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# Makefile - Intel MIC User Space Tools.
+# Copyright(c) 2013, Intel Corporation.
+#
+ifdef DEBUG
+CFLAGS += $(USERWARNFLAGS) -I. -g -Wall -DDEBUG=$(DEBUG)
+else
+CFLAGS += $(USERWARNFLAGS) -I. -g -Wall
+endif
+
+mpssd: mpssd.o sysfs.o
+       $(CC) $(CFLAGS) -o $@ $^ -lpthread
+
+install:
+       install mpssd /usr/sbin/mpssd
+       install micctrl /usr/sbin/micctrl
+
+clean:
+       rm -f mpssd *.o
diff --git a/Documentation/mic/mpssd/micctrl b/Documentation/mic/mpssd/micctrl
new file mode 100755 (executable)
index 0000000..8f2629b
--- /dev/null
@@ -0,0 +1,173 @@
+#!/bin/bash
+# Intel MIC Platform Software Stack (MPSS)
+#
+# Copyright(c) 2013 Intel Corporation.
+#
+# 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.
+#
+# The full GNU General Public License is included in this distribution in
+# the file called "COPYING".
+#
+# Intel MIC User Space Tools.
+#
+# micctrl - Controls MIC boot/start/stop.
+#
+# chkconfig: 2345 95 05
+# description: start MPSS stack processing.
+#
+### BEGIN INIT INFO
+# Provides: micctrl
+### END INIT INFO
+
+# Source function library.
+. /etc/init.d/functions
+
+sysfs="/sys/class/mic"
+
+_status()
+{
+       f=$sysfs/$1
+       echo -e $1 state: "`cat $f/state`" shutdown_status: "`cat $f/shutdown_status`"
+}
+
+status()
+{
+       if [ "`echo $1 | head -c3`" == "mic" ]; then
+               _status $1
+               return $?
+       fi
+       for f in $sysfs/*
+       do
+               _status `basename $f`
+               RETVAL=$?
+               [ $RETVAL -ne 0 ] && return $RETVAL
+       done
+       return 0
+}
+
+_reset()
+{
+       f=$sysfs/$1
+       echo reset > $f/state
+}
+
+reset()
+{
+       if [ "`echo $1 | head -c3`" == "mic" ]; then
+               _reset $1
+               return $?
+       fi
+       for f in $sysfs/*
+       do
+               _reset `basename $f`
+               RETVAL=$?
+               [ $RETVAL -ne 0 ] && return $RETVAL
+       done
+       return 0
+}
+
+_boot()
+{
+       f=$sysfs/$1
+       echo "linux" > $f/bootmode
+       echo "mic/uos.img" > $f/firmware
+       echo "mic/$1.image" > $f/ramdisk
+       echo "boot" > $f/state
+}
+
+boot()
+{
+       if [ "`echo $1 | head -c3`" == "mic" ]; then
+               _boot $1
+               return $?
+       fi
+       for f in $sysfs/*
+       do
+               _boot `basename $f`
+               RETVAL=$?
+               [ $RETVAL -ne 0 ] && return $RETVAL
+       done
+       return 0
+}
+
+_shutdown()
+{
+       f=$sysfs/$1
+       echo shutdown > $f/state
+}
+
+shutdown()
+{
+       if [ "`echo $1 | head -c3`" == "mic" ]; then
+               _shutdown $1
+               return $?
+       fi
+       for f in $sysfs/*
+       do
+               _shutdown `basename $f`
+               RETVAL=$?
+               [ $RETVAL -ne 0 ] && return $RETVAL
+       done
+       return 0
+}
+
+_wait()
+{
+       f=$sysfs/$1
+       while [ "`cat $f/state`" != "offline" -a "`cat $f/state`" != "online" ]
+       do
+               sleep 1
+               echo -e "Waiting for $1 to go offline"
+       done
+}
+
+wait()
+{
+       if [ "`echo $1 | head -c3`" == "mic" ]; then
+               _wait $1
+               return $?
+       fi
+       # Wait for the cards to go offline
+       for f in $sysfs/*
+       do
+               _wait `basename $f`
+               RETVAL=$?
+               [ $RETVAL -ne 0 ] && return $RETVAL
+       done
+       return 0
+}
+
+if [ ! -d "$sysfs" ]; then
+       echo -e $"Module unloaded "
+       exit 3
+fi
+
+case $1 in
+       -s)
+               status $2
+               ;;
+       -r)
+               reset $2
+               ;;
+       -b)
+               boot $2
+               ;;
+       -S)
+               shutdown $2
+               ;;
+       -w)
+               wait $2
+               ;;
+       *)
+               echo $"Usage: $0 {-s (status) |-r (reset) |-b (boot) |-S (shutdown) |-w (wait)}"
+               exit 2
+esac
+
+exit $?
diff --git a/Documentation/mic/mpssd/mpss b/Documentation/mic/mpssd/mpss
new file mode 100755 (executable)
index 0000000..3136c68
--- /dev/null
@@ -0,0 +1,202 @@
+#!/bin/bash
+# Intel MIC Platform Software Stack (MPSS)
+#
+# Copyright(c) 2013 Intel Corporation.
+#
+# 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.
+#
+# The full GNU General Public License is included in this distribution in
+# the file called "COPYING".
+#
+# Intel MIC User Space Tools.
+#
+# mpss Start mpssd.
+#
+# chkconfig: 2345 95 05
+# description: start MPSS stack processing.
+#
+### BEGIN INIT INFO
+# Provides: mpss
+# Required-Start:
+# Required-Stop:
+# Short-Description: MPSS stack control
+# Description: MPSS stack control
+### END INIT INFO
+
+# Source function library.
+. /etc/init.d/functions
+
+exec=/usr/sbin/mpssd
+sysfs="/sys/class/mic"
+
+start()
+{
+       [ -x $exec ] || exit 5
+
+       if [ "`ps -e | awk '{print $4}' | grep mpssd | head -1`" = "mpssd" ]; then
+               echo -e $"MPSSD already running! "
+               success
+               echo
+               return 0
+       fi
+
+       echo -e $"Starting MPSS Stack"
+       echo -e $"Loading MIC_HOST Module"
+
+       # Ensure the driver is loaded
+       if [ ! -d "$sysfs" ]; then
+               modprobe mic_host
+               RETVAL=$?
+               if [ $RETVAL -ne 0 ]; then
+                       failure
+                       echo
+                       return $RETVAL
+               fi
+       fi
+
+       # Start the daemon
+       echo -n $"Starting MPSSD "
+       $exec
+       RETVAL=$?
+       if [ $RETVAL -ne 0 ]; then
+               failure
+               echo
+               return $RETVAL
+       fi
+       success
+       echo
+
+       sleep 5
+
+       # Boot the cards
+       micctrl -b
+
+       # Wait till ping works
+       for f in $sysfs/*
+       do
+               count=100
+               ipaddr=`cat $f/cmdline`
+               ipaddr=${ipaddr#*address,}
+               ipaddr=`echo $ipaddr | cut -d, -f1 | cut -d\; -f1`
+               while [ $count -ge 0 ]
+               do
+                       echo -e "Pinging "`basename $f`" "
+                       ping -c 1 $ipaddr &> /dev/null
+                       RETVAL=$?
+                       if [ $RETVAL -eq 0 ]; then
+                               success
+                               break
+                       fi
+                       sleep 1
+                       count=`expr $count - 1`
+               done
+               [ $RETVAL -ne 0 ] && failure || success
+               echo
+       done
+       return $RETVAL
+}
+
+stop()
+{
+       echo -e $"Shutting down MPSS Stack: "
+
+       # Bail out if module is unloaded
+       if [ ! -d "$sysfs" ]; then
+               echo -n $"Module unloaded "
+               success
+               echo
+               return 0
+       fi
+
+       # Shut down the cards.
+       micctrl -S
+
+       # Wait for the cards to go offline
+       for f in $sysfs/*
+       do
+               while [ "`cat $f/state`" != "offline" ]
+               do
+                       sleep 1
+                       echo -e "Waiting for "`basename $f`" to go offline"
+               done
+       done
+
+       # Display the status of the cards
+       micctrl -s
+
+       # Kill MPSSD now
+       echo -n $"Killing MPSSD"
+       killall -9 mpssd 2>/dev/null
+       RETVAL=$?
+       [ $RETVAL -ne 0 ] && failure || success
+       echo
+       return $RETVAL
+}
+
+restart()
+{
+       stop
+       sleep 5
+       start
+}
+
+status()
+{
+       micctrl -s
+       if [ "`ps -e | awk '{print $4}' | grep mpssd | head -n 1`" = "mpssd" ]; then
+               echo "mpssd is running"
+       else
+               echo "mpssd is stopped"
+       fi
+       return 0
+}
+
+unload()
+{
+       if [ ! -d "$sysfs" ]; then
+               echo -n $"No MIC_HOST Module: "
+               success
+               echo
+               return
+       fi
+
+       stop
+
+       sleep 5
+       echo -n $"Removing MIC_HOST Module: "
+       modprobe -r mic_host
+       RETVAL=$?
+       [ $RETVAL -ne 0 ] && failure || success
+       echo
+       return $RETVAL
+}
+
+case $1 in
+       start)
+               start
+               ;;
+       stop)
+               stop
+               ;;
+       restart)
+               restart
+               ;;
+       status)
+               status
+               ;;
+       unload)
+               unload
+               ;;
+       *)
+               echo $"Usage: $0 {start|stop|restart|status|unload}"
+               exit 2
+esac
+
+exit $?
diff --git a/Documentation/mic/mpssd/mpssd.c b/Documentation/mic/mpssd/mpssd.c
new file mode 100644 (file)
index 0000000..0c980ad
--- /dev/null
@@ -0,0 +1,1721 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC User Space Tools.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <poll.h>
+#include <features.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <linux/virtio_ring.h>
+#include <linux/virtio_net.h>
+#include <linux/virtio_console.h>
+#include <linux/virtio_blk.h>
+#include <linux/version.h>
+#include "mpssd.h"
+#include <linux/mic_ioctl.h>
+#include <linux/mic_common.h>
+
+static void init_mic(struct mic_info *mic);
+
+static FILE *logfp;
+static struct mic_info mic_list;
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define min_t(type, x, y) ({                           \
+               type __min1 = (x);                      \
+               type __min2 = (y);                      \
+               __min1 < __min2 ? __min1 : __min2; })
+
+/* align addr on a size boundary - adjust address up/down if needed */
+#define _ALIGN_DOWN(addr, size)  ((addr)&(~((size)-1)))
+#define _ALIGN_UP(addr, size)    _ALIGN_DOWN(addr + size - 1, size)
+
+/* align addr on a size boundary - adjust address up if needed */
+#define _ALIGN(addr, size)     _ALIGN_UP(addr, size)
+
+/* to align the pointer to the (next) page boundary */
+#define PAGE_ALIGN(addr)        _ALIGN(addr, PAGE_SIZE)
+
+#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
+
+#define GSO_ENABLED            1
+#define MAX_GSO_SIZE           (64 * 1024)
+#define ETH_H_LEN              14
+#define MAX_NET_PKT_SIZE       (_ALIGN_UP(MAX_GSO_SIZE + ETH_H_LEN, 64))
+#define MIC_DEVICE_PAGE_END    0x1000
+
+#ifndef VIRTIO_NET_HDR_F_DATA_VALID
+#define VIRTIO_NET_HDR_F_DATA_VALID    2       /* Csum is valid */
+#endif
+
+static struct {
+       struct mic_device_desc dd;
+       struct mic_vqconfig vqconfig[2];
+       __u32 host_features, guest_acknowledgements;
+       struct virtio_console_config cons_config;
+} virtcons_dev_page = {
+       .dd = {
+               .type = VIRTIO_ID_CONSOLE,
+               .num_vq = ARRAY_SIZE(virtcons_dev_page.vqconfig),
+               .feature_len = sizeof(virtcons_dev_page.host_features),
+               .config_len = sizeof(virtcons_dev_page.cons_config),
+       },
+       .vqconfig[0] = {
+               .num = htole16(MIC_VRING_ENTRIES),
+       },
+       .vqconfig[1] = {
+               .num = htole16(MIC_VRING_ENTRIES),
+       },
+};
+
+static struct {
+       struct mic_device_desc dd;
+       struct mic_vqconfig vqconfig[2];
+       __u32 host_features, guest_acknowledgements;
+       struct virtio_net_config net_config;
+} virtnet_dev_page = {
+       .dd = {
+               .type = VIRTIO_ID_NET,
+               .num_vq = ARRAY_SIZE(virtnet_dev_page.vqconfig),
+               .feature_len = sizeof(virtnet_dev_page.host_features),
+               .config_len = sizeof(virtnet_dev_page.net_config),
+       },
+       .vqconfig[0] = {
+               .num = htole16(MIC_VRING_ENTRIES),
+       },
+       .vqconfig[1] = {
+               .num = htole16(MIC_VRING_ENTRIES),
+       },
+#if GSO_ENABLED
+               .host_features = htole32(
+               1 << VIRTIO_NET_F_CSUM |
+               1 << VIRTIO_NET_F_GSO |
+               1 << VIRTIO_NET_F_GUEST_TSO4 |
+               1 << VIRTIO_NET_F_GUEST_TSO6 |
+               1 << VIRTIO_NET_F_GUEST_ECN |
+               1 << VIRTIO_NET_F_GUEST_UFO),
+#else
+               .host_features = 0,
+#endif
+};
+
+static const char *mic_config_dir = "/etc/sysconfig/mic";
+static const char *virtblk_backend = "VIRTBLK_BACKEND";
+static struct {
+       struct mic_device_desc dd;
+       struct mic_vqconfig vqconfig[1];
+       __u32 host_features, guest_acknowledgements;
+       struct virtio_blk_config blk_config;
+} virtblk_dev_page = {
+       .dd = {
+               .type = VIRTIO_ID_BLOCK,
+               .num_vq = ARRAY_SIZE(virtblk_dev_page.vqconfig),
+               .feature_len = sizeof(virtblk_dev_page.host_features),
+               .config_len = sizeof(virtblk_dev_page.blk_config),
+       },
+       .vqconfig[0] = {
+               .num = htole16(MIC_VRING_ENTRIES),
+       },
+       .host_features =
+               htole32(1<<VIRTIO_BLK_F_SEG_MAX),
+       .blk_config = {
+               .seg_max = htole32(MIC_VRING_ENTRIES - 2),
+               .capacity = htole64(0),
+        }
+};
+
+static char *myname;
+
+static int
+tap_configure(struct mic_info *mic, char *dev)
+{
+       pid_t pid;
+       char *ifargv[7];
+       char ipaddr[IFNAMSIZ];
+       int ret = 0;
+
+       pid = fork();
+       if (pid == 0) {
+               ifargv[0] = "ip";
+               ifargv[1] = "link";
+               ifargv[2] = "set";
+               ifargv[3] = dev;
+               ifargv[4] = "up";
+               ifargv[5] = NULL;
+               mpsslog("Configuring %s\n", dev);
+               ret = execvp("ip", ifargv);
+               if (ret < 0) {
+                       mpsslog("%s execvp failed errno %s\n",
+                               mic->name, strerror(errno));
+                       return ret;
+               }
+       }
+       if (pid < 0) {
+               mpsslog("%s fork failed errno %s\n",
+                       mic->name, strerror(errno));
+               return ret;
+       }
+
+       ret = waitpid(pid, NULL, 0);
+       if (ret < 0) {
+               mpsslog("%s waitpid failed errno %s\n",
+                       mic->name, strerror(errno));
+               return ret;
+       }
+
+       snprintf(ipaddr, IFNAMSIZ, "172.31.%d.254/24", mic->id);
+
+       pid = fork();
+       if (pid == 0) {
+               ifargv[0] = "ip";
+               ifargv[1] = "addr";
+               ifargv[2] = "add";
+               ifargv[3] = ipaddr;
+               ifargv[4] = "dev";
+               ifargv[5] = dev;
+               ifargv[6] = NULL;
+               mpsslog("Configuring %s ipaddr %s\n", dev, ipaddr);
+               ret = execvp("ip", ifargv);
+               if (ret < 0) {
+                       mpsslog("%s execvp failed errno %s\n",
+                               mic->name, strerror(errno));
+                       return ret;
+               }
+       }
+       if (pid < 0) {
+               mpsslog("%s fork failed errno %s\n",
+                       mic->name, strerror(errno));
+               return ret;
+       }
+
+       ret = waitpid(pid, NULL, 0);
+       if (ret < 0) {
+               mpsslog("%s waitpid failed errno %s\n",
+                       mic->name, strerror(errno));
+               return ret;
+       }
+       mpsslog("MIC name %s %s %d DONE!\n",
+               mic->name, __func__, __LINE__);
+       return 0;
+}
+
+static int tun_alloc(struct mic_info *mic, char *dev)
+{
+       struct ifreq ifr;
+       int fd, err;
+#if GSO_ENABLED
+       unsigned offload;
+#endif
+       fd = open("/dev/net/tun", O_RDWR);
+       if (fd < 0) {
+               mpsslog("Could not open /dev/net/tun %s\n", strerror(errno));
+               goto done;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+
+       ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
+       if (*dev)
+               strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+
+       err = ioctl(fd, TUNSETIFF, (void *)&ifr);
+       if (err < 0) {
+               mpsslog("%s %s %d TUNSETIFF failed %s\n",
+                       mic->name, __func__, __LINE__, strerror(errno));
+               close(fd);
+               return err;
+       }
+#if GSO_ENABLED
+       offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 |
+               TUN_F_TSO_ECN | TUN_F_UFO;
+
+       err = ioctl(fd, TUNSETOFFLOAD, offload);
+       if (err < 0) {
+               mpsslog("%s %s %d TUNSETOFFLOAD failed %s\n",
+                       mic->name, __func__, __LINE__, strerror(errno));
+               close(fd);
+               return err;
+       }
+#endif
+       strcpy(dev, ifr.ifr_name);
+       mpsslog("Created TAP %s\n", dev);
+done:
+       return fd;
+}
+
+#define NET_FD_VIRTIO_NET 0
+#define NET_FD_TUN 1
+#define MAX_NET_FD 2
+
+static void set_dp(struct mic_info *mic, int type, void *dp)
+{
+       switch (type) {
+       case VIRTIO_ID_CONSOLE:
+               mic->mic_console.console_dp = dp;
+               return;
+       case VIRTIO_ID_NET:
+               mic->mic_net.net_dp = dp;
+               return;
+       case VIRTIO_ID_BLOCK:
+               mic->mic_virtblk.block_dp = dp;
+               return;
+       }
+       mpsslog("%s %s %d not found\n", mic->name, __func__, type);
+       assert(0);
+}
+
+static void *get_dp(struct mic_info *mic, int type)
+{
+       switch (type) {
+       case VIRTIO_ID_CONSOLE:
+               return mic->mic_console.console_dp;
+       case VIRTIO_ID_NET:
+               return mic->mic_net.net_dp;
+       case VIRTIO_ID_BLOCK:
+               return mic->mic_virtblk.block_dp;
+       }
+       mpsslog("%s %s %d not found\n", mic->name, __func__, type);
+       assert(0);
+       return NULL;
+}
+
+static struct mic_device_desc *get_device_desc(struct mic_info *mic, int type)
+{
+       struct mic_device_desc *d;
+       int i;
+       void *dp = get_dp(mic, type);
+
+       for (i = mic_aligned_size(struct mic_bootparam); i < PAGE_SIZE;
+               i += mic_total_desc_size(d)) {
+               d = dp + i;
+
+               /* End of list */
+               if (d->type == 0)
+                       break;
+
+               if (d->type == -1)
+                       continue;
+
+               mpsslog("%s %s d-> type %d d %p\n",
+                       mic->name, __func__, d->type, d);
+
+               if (d->type == (__u8)type)
+                       return d;
+       }
+       mpsslog("%s %s %d not found\n", mic->name, __func__, type);
+       assert(0);
+       return NULL;
+}
+
+/* See comments in vhost.c for explanation of next_desc() */
+static unsigned next_desc(struct vring_desc *desc)
+{
+       unsigned int next;
+
+       if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT))
+               return -1U;
+       next = le16toh(desc->next);
+       return next;
+}
+
+/* Sum up all the IOVEC length */
+static ssize_t
+sum_iovec_len(struct mic_copy_desc *copy)
+{
+       ssize_t sum = 0;
+       int i;
+
+       for (i = 0; i < copy->iovcnt; i++)
+               sum += copy->iov[i].iov_len;
+       return sum;
+}
+
+static inline void verify_out_len(struct mic_info *mic,
+       struct mic_copy_desc *copy)
+{
+       if (copy->out_len != sum_iovec_len(copy)) {
+               mpsslog("%s %s %d BUG copy->out_len 0x%x len 0x%zx\n",
+                       mic->name, __func__, __LINE__,
+                       copy->out_len, sum_iovec_len(copy));
+               assert(copy->out_len == sum_iovec_len(copy));
+       }
+}
+
+/* Display an iovec */
+static void
+disp_iovec(struct mic_info *mic, struct mic_copy_desc *copy,
+          const char *s, int line)
+{
+       int i;
+
+       for (i = 0; i < copy->iovcnt; i++)
+               mpsslog("%s %s %d copy->iov[%d] addr %p len 0x%zx\n",
+                       mic->name, s, line, i,
+                       copy->iov[i].iov_base, copy->iov[i].iov_len);
+}
+
+static inline __u16 read_avail_idx(struct mic_vring *vr)
+{
+       return ACCESS_ONCE(vr->info->avail_idx);
+}
+
+static inline void txrx_prepare(int type, bool tx, struct mic_vring *vr,
+                               struct mic_copy_desc *copy, ssize_t len)
+{
+       copy->vr_idx = tx ? 0 : 1;
+       copy->update_used = true;
+       if (type == VIRTIO_ID_NET)
+               copy->iov[1].iov_len = len - sizeof(struct virtio_net_hdr);
+       else
+               copy->iov[0].iov_len = len;
+}
+
+/* Central API which triggers the copies */
+static int
+mic_virtio_copy(struct mic_info *mic, int fd,
+               struct mic_vring *vr, struct mic_copy_desc *copy)
+{
+       int ret;
+
+       ret = ioctl(fd, MIC_VIRTIO_COPY_DESC, copy);
+       if (ret) {
+               mpsslog("%s %s %d errno %s ret %d\n",
+                       mic->name, __func__, __LINE__,
+                       strerror(errno), ret);
+       }
+       return ret;
+}
+
+/*
+ * This initialization routine requires at least one
+ * vring i.e. vr0. vr1 is optional.
+ */
+static void *
+init_vr(struct mic_info *mic, int fd, int type,
+       struct mic_vring *vr0, struct mic_vring *vr1, int num_vq)
+{
+       int vr_size;
+       char *va;
+
+       vr_size = PAGE_ALIGN(vring_size(MIC_VRING_ENTRIES,
+               MIC_VIRTIO_RING_ALIGN) + sizeof(struct _mic_vring_info));
+       va = mmap(NULL, MIC_DEVICE_PAGE_END + vr_size * num_vq,
+               PROT_READ, MAP_SHARED, fd, 0);
+       if (MAP_FAILED == va) {
+               mpsslog("%s %s %d mmap failed errno %s\n",
+                       mic->name, __func__, __LINE__,
+                       strerror(errno));
+               goto done;
+       }
+       set_dp(mic, type, va);
+       vr0->va = (struct mic_vring *)&va[MIC_DEVICE_PAGE_END];
+       vr0->info = vr0->va +
+               vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN);
+       vring_init(&vr0->vr,
+                  MIC_VRING_ENTRIES, vr0->va, MIC_VIRTIO_RING_ALIGN);
+       mpsslog("%s %s vr0 %p vr0->info %p vr_size 0x%x vring 0x%x ",
+               __func__, mic->name, vr0->va, vr0->info, vr_size,
+               vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN));
+       mpsslog("magic 0x%x expected 0x%x\n",
+               vr0->info->magic, MIC_MAGIC + type);
+       assert(vr0->info->magic == MIC_MAGIC + type);
+       if (vr1) {
+               vr1->va = (struct mic_vring *)
+                       &va[MIC_DEVICE_PAGE_END + vr_size];
+               vr1->info = vr1->va + vring_size(MIC_VRING_ENTRIES,
+                       MIC_VIRTIO_RING_ALIGN);
+               vring_init(&vr1->vr,
+                          MIC_VRING_ENTRIES, vr1->va, MIC_VIRTIO_RING_ALIGN);
+               mpsslog("%s %s vr1 %p vr1->info %p vr_size 0x%x vring 0x%x ",
+                       __func__, mic->name, vr1->va, vr1->info, vr_size,
+                       vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN));
+               mpsslog("magic 0x%x expected 0x%x\n",
+                       vr1->info->magic, MIC_MAGIC + type + 1);
+               assert(vr1->info->magic == MIC_MAGIC + type + 1);
+       }
+done:
+       return va;
+}
+
+static void
+wait_for_card_driver(struct mic_info *mic, int fd, int type)
+{
+       struct pollfd pollfd;
+       int err;
+       struct mic_device_desc *desc = get_device_desc(mic, type);
+
+       pollfd.fd = fd;
+       mpsslog("%s %s Waiting .... desc-> type %d status 0x%x\n",
+               mic->name, __func__, type, desc->status);
+       while (1) {
+               pollfd.events = POLLIN;
+               pollfd.revents = 0;
+               err = poll(&pollfd, 1, -1);
+               if (err < 0) {
+                       mpsslog("%s %s poll failed %s\n",
+                               mic->name, __func__, strerror(errno));
+                       continue;
+               }
+
+               if (pollfd.revents) {
+                       mpsslog("%s %s Waiting... desc-> type %d status 0x%x\n",
+                               mic->name, __func__, type, desc->status);
+                       if (desc->status & VIRTIO_CONFIG_S_DRIVER_OK) {
+                               mpsslog("%s %s poll.revents %d\n",
+                                       mic->name, __func__, pollfd.revents);
+                               mpsslog("%s %s desc-> type %d status 0x%x\n",
+                                       mic->name, __func__, type,
+                                       desc->status);
+                               break;
+                       }
+               }
+       }
+}
+
+/* Spin till we have some descriptors */
+static void
+spin_for_descriptors(struct mic_info *mic, struct mic_vring *vr)
+{
+       __u16 avail_idx = read_avail_idx(vr);
+
+       while (avail_idx == le16toh(ACCESS_ONCE(vr->vr.avail->idx))) {
+#ifdef DEBUG
+               mpsslog("%s %s waiting for desc avail %d info_avail %d\n",
+                       mic->name, __func__,
+                       le16toh(vr->vr.avail->idx), vr->info->avail_idx);
+#endif
+               sched_yield();
+       }
+}
+
+static void *
+virtio_net(void *arg)
+{
+       static __u8 vnet_hdr[2][sizeof(struct virtio_net_hdr)];
+       static __u8 vnet_buf[2][MAX_NET_PKT_SIZE] __aligned(64);
+       struct iovec vnet_iov[2][2] = {
+               { { .iov_base = vnet_hdr[0], .iov_len = sizeof(vnet_hdr[0]) },
+                 { .iov_base = vnet_buf[0], .iov_len = sizeof(vnet_buf[0]) } },
+               { { .iov_base = vnet_hdr[1], .iov_len = sizeof(vnet_hdr[1]) },
+                 { .iov_base = vnet_buf[1], .iov_len = sizeof(vnet_buf[1]) } },
+       };
+       struct iovec *iov0 = vnet_iov[0], *iov1 = vnet_iov[1];
+       struct mic_info *mic = (struct mic_info *)arg;
+       char if_name[IFNAMSIZ];
+       struct pollfd net_poll[MAX_NET_FD];
+       struct mic_vring tx_vr, rx_vr;
+       struct mic_copy_desc copy;
+       struct mic_device_desc *desc;
+       int err;
+
+       snprintf(if_name, IFNAMSIZ, "mic%d", mic->id);
+       mic->mic_net.tap_fd = tun_alloc(mic, if_name);
+       if (mic->mic_net.tap_fd < 0)
+               goto done;
+
+       if (tap_configure(mic, if_name))
+               goto done;
+       mpsslog("MIC name %s id %d\n", mic->name, mic->id);
+
+       net_poll[NET_FD_VIRTIO_NET].fd = mic->mic_net.virtio_net_fd;
+       net_poll[NET_FD_VIRTIO_NET].events = POLLIN;
+       net_poll[NET_FD_TUN].fd = mic->mic_net.tap_fd;
+       net_poll[NET_FD_TUN].events = POLLIN;
+
+       if (MAP_FAILED == init_vr(mic, mic->mic_net.virtio_net_fd,
+                                 VIRTIO_ID_NET, &tx_vr, &rx_vr,
+               virtnet_dev_page.dd.num_vq)) {
+               mpsslog("%s init_vr failed %s\n",
+                       mic->name, strerror(errno));
+               goto done;
+       }
+
+       copy.iovcnt = 2;
+       desc = get_device_desc(mic, VIRTIO_ID_NET);
+
+       while (1) {
+               ssize_t len;
+
+               net_poll[NET_FD_VIRTIO_NET].revents = 0;
+               net_poll[NET_FD_TUN].revents = 0;
+
+               /* Start polling for data from tap and virtio net */
+               err = poll(net_poll, 2, -1);
+               if (err < 0) {
+                       mpsslog("%s poll failed %s\n",
+                               __func__, strerror(errno));
+                       continue;
+               }
+               if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK))
+                       wait_for_card_driver(mic, mic->mic_net.virtio_net_fd,
+                                            VIRTIO_ID_NET);
+               /*
+                * Check if there is data to be read from TUN and write to
+                * virtio net fd if there is.
+                */
+               if (net_poll[NET_FD_TUN].revents & POLLIN) {
+                       copy.iov = iov0;
+                       len = readv(net_poll[NET_FD_TUN].fd,
+                               copy.iov, copy.iovcnt);
+                       if (len > 0) {
+                               struct virtio_net_hdr *hdr
+                                       = (struct virtio_net_hdr *)vnet_hdr[0];
+
+                               /* Disable checksums on the card since we are on
+                                  a reliable PCIe link */
+                               hdr->flags |= VIRTIO_NET_HDR_F_DATA_VALID;
+#ifdef DEBUG
+                               mpsslog("%s %s %d hdr->flags 0x%x ", mic->name,
+                                       __func__, __LINE__, hdr->flags);
+                               mpsslog("copy.out_len %d hdr->gso_type 0x%x\n",
+                                       copy.out_len, hdr->gso_type);
+#endif
+#ifdef DEBUG
+                               disp_iovec(mic, copy, __func__, __LINE__);
+                               mpsslog("%s %s %d read from tap 0x%lx\n",
+                                       mic->name, __func__, __LINE__,
+                                       len);
+#endif
+                               spin_for_descriptors(mic, &tx_vr);
+                               txrx_prepare(VIRTIO_ID_NET, 1, &tx_vr, &copy,
+                                            len);
+
+                               err = mic_virtio_copy(mic,
+                                       mic->mic_net.virtio_net_fd, &tx_vr,
+                                       &copy);
+                               if (err < 0) {
+                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
+                                               mic->name, __func__, __LINE__,
+                                               strerror(errno));
+                               }
+                               if (!err)
+                                       verify_out_len(mic, &copy);
+#ifdef DEBUG
+                               disp_iovec(mic, copy, __func__, __LINE__);
+                               mpsslog("%s %s %d wrote to net 0x%lx\n",
+                                       mic->name, __func__, __LINE__,
+                                       sum_iovec_len(&copy));
+#endif
+                               /* Reinitialize IOV for next run */
+                               iov0[1].iov_len = MAX_NET_PKT_SIZE;
+                       } else if (len < 0) {
+                               disp_iovec(mic, &copy, __func__, __LINE__);
+                               mpsslog("%s %s %d read failed %s ", mic->name,
+                                       __func__, __LINE__, strerror(errno));
+                               mpsslog("cnt %d sum %zd\n",
+                                       copy.iovcnt, sum_iovec_len(&copy));
+                       }
+               }
+
+               /*
+                * Check if there is data to be read from virtio net and
+                * write to TUN if there is.
+                */
+               if (net_poll[NET_FD_VIRTIO_NET].revents & POLLIN) {
+                       while (rx_vr.info->avail_idx !=
+                               le16toh(rx_vr.vr.avail->idx)) {
+                               copy.iov = iov1;
+                               txrx_prepare(VIRTIO_ID_NET, 0, &rx_vr, &copy,
+                                            MAX_NET_PKT_SIZE
+                                       + sizeof(struct virtio_net_hdr));
+
+                               err = mic_virtio_copy(mic,
+                                       mic->mic_net.virtio_net_fd, &rx_vr,
+                                       &copy);
+                               if (!err) {
+#ifdef DEBUG
+                                       struct virtio_net_hdr *hdr
+                                               = (struct virtio_net_hdr *)
+                                                       vnet_hdr[1];
+
+                                       mpsslog("%s %s %d hdr->flags 0x%x, ",
+                                               mic->name, __func__, __LINE__,
+                                               hdr->flags);
+                                       mpsslog("out_len %d gso_type 0x%x\n",
+                                               copy.out_len,
+                                               hdr->gso_type);
+#endif
+                                       /* Set the correct output iov_len */
+                                       iov1[1].iov_len = copy.out_len -
+                                               sizeof(struct virtio_net_hdr);
+                                       verify_out_len(mic, &copy);
+#ifdef DEBUG
+                                       disp_iovec(mic, copy, __func__,
+                                                  __LINE__);
+                                       mpsslog("%s %s %d ",
+                                               mic->name, __func__, __LINE__);
+                                       mpsslog("read from net 0x%lx\n",
+                                               sum_iovec_len(copy));
+#endif
+                                       len = writev(net_poll[NET_FD_TUN].fd,
+                                               copy.iov, copy.iovcnt);
+                                       if (len != sum_iovec_len(&copy)) {
+                                               mpsslog("Tun write failed %s ",
+                                                       strerror(errno));
+                                               mpsslog("len 0x%zx ", len);
+                                               mpsslog("read_len 0x%zx\n",
+                                                       sum_iovec_len(&copy));
+                                       } else {
+#ifdef DEBUG
+                                               disp_iovec(mic, &copy, __func__,
+                                                          __LINE__);
+                                               mpsslog("%s %s %d ",
+                                                       mic->name, __func__,
+                                                       __LINE__);
+                                               mpsslog("wrote to tap 0x%lx\n",
+                                                       len);
+#endif
+                                       }
+                               } else {
+                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
+                                               mic->name, __func__, __LINE__,
+                                               strerror(errno));
+                                       break;
+                               }
+                       }
+               }
+               if (net_poll[NET_FD_VIRTIO_NET].revents & POLLERR)
+                       mpsslog("%s: %s: POLLERR\n", __func__, mic->name);
+       }
+done:
+       pthread_exit(NULL);
+}
+
+/* virtio_console */
+#define VIRTIO_CONSOLE_FD 0
+#define MONITOR_FD (VIRTIO_CONSOLE_FD + 1)
+#define MAX_CONSOLE_FD (MONITOR_FD + 1)  /* must be the last one + 1 */
+#define MAX_BUFFER_SIZE PAGE_SIZE
+
+static void *
+virtio_console(void *arg)
+{
+       static __u8 vcons_buf[2][PAGE_SIZE];
+       struct iovec vcons_iov[2] = {
+               { .iov_base = vcons_buf[0], .iov_len = sizeof(vcons_buf[0]) },
+               { .iov_base = vcons_buf[1], .iov_len = sizeof(vcons_buf[1]) },
+       };
+       struct iovec *iov0 = &vcons_iov[0], *iov1 = &vcons_iov[1];
+       struct mic_info *mic = (struct mic_info *)arg;
+       int err;
+       struct pollfd console_poll[MAX_CONSOLE_FD];
+       int pty_fd;
+       char *pts_name;
+       ssize_t len;
+       struct mic_vring tx_vr, rx_vr;
+       struct mic_copy_desc copy;
+       struct mic_device_desc *desc;
+
+       pty_fd = posix_openpt(O_RDWR);
+       if (pty_fd < 0) {
+               mpsslog("can't open a pseudoterminal master device: %s\n",
+                       strerror(errno));
+               goto _return;
+       }
+       pts_name = ptsname(pty_fd);
+       if (pts_name == NULL) {
+               mpsslog("can't get pts name\n");
+               goto _close_pty;
+       }
+       printf("%s console message goes to %s\n", mic->name, pts_name);
+       mpsslog("%s console message goes to %s\n", mic->name, pts_name);
+       err = grantpt(pty_fd);
+       if (err < 0) {
+               mpsslog("can't grant access: %s %s\n",
+                       pts_name, strerror(errno));
+               goto _close_pty;
+       }
+       err = unlockpt(pty_fd);
+       if (err < 0) {
+               mpsslog("can't unlock a pseudoterminal: %s %s\n",
+                       pts_name, strerror(errno));
+               goto _close_pty;
+       }
+       console_poll[MONITOR_FD].fd = pty_fd;
+       console_poll[MONITOR_FD].events = POLLIN;
+
+       console_poll[VIRTIO_CONSOLE_FD].fd = mic->mic_console.virtio_console_fd;
+       console_poll[VIRTIO_CONSOLE_FD].events = POLLIN;
+
+       if (MAP_FAILED == init_vr(mic, mic->mic_console.virtio_console_fd,
+                                 VIRTIO_ID_CONSOLE, &tx_vr, &rx_vr,
+               virtcons_dev_page.dd.num_vq)) {
+               mpsslog("%s init_vr failed %s\n",
+                       mic->name, strerror(errno));
+               goto _close_pty;
+       }
+
+       copy.iovcnt = 1;
+       desc = get_device_desc(mic, VIRTIO_ID_CONSOLE);
+
+       for (;;) {
+               console_poll[MONITOR_FD].revents = 0;
+               console_poll[VIRTIO_CONSOLE_FD].revents = 0;
+               err = poll(console_poll, MAX_CONSOLE_FD, -1);
+               if (err < 0) {
+                       mpsslog("%s %d: poll failed: %s\n", __func__, __LINE__,
+                               strerror(errno));
+                       continue;
+               }
+               if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK))
+                       wait_for_card_driver(mic,
+                                            mic->mic_console.virtio_console_fd,
+                               VIRTIO_ID_CONSOLE);
+
+               if (console_poll[MONITOR_FD].revents & POLLIN) {
+                       copy.iov = iov0;
+                       len = readv(pty_fd, copy.iov, copy.iovcnt);
+                       if (len > 0) {
+#ifdef DEBUG
+                               disp_iovec(mic, copy, __func__, __LINE__);
+                               mpsslog("%s %s %d read from tap 0x%lx\n",
+                                       mic->name, __func__, __LINE__,
+                                       len);
+#endif
+                               spin_for_descriptors(mic, &tx_vr);
+                               txrx_prepare(VIRTIO_ID_CONSOLE, 1, &tx_vr,
+                                            &copy, len);
+
+                               err = mic_virtio_copy(mic,
+                                       mic->mic_console.virtio_console_fd,
+                                       &tx_vr, &copy);
+                               if (err < 0) {
+                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
+                                               mic->name, __func__, __LINE__,
+                                               strerror(errno));
+                               }
+                               if (!err)
+                                       verify_out_len(mic, &copy);
+#ifdef DEBUG
+                               disp_iovec(mic, copy, __func__, __LINE__);
+                               mpsslog("%s %s %d wrote to net 0x%lx\n",
+                                       mic->name, __func__, __LINE__,
+                                       sum_iovec_len(copy));
+#endif
+                               /* Reinitialize IOV for next run */
+                               iov0->iov_len = PAGE_SIZE;
+                       } else if (len < 0) {
+                               disp_iovec(mic, &copy, __func__, __LINE__);
+                               mpsslog("%s %s %d read failed %s ",
+                                       mic->name, __func__, __LINE__,
+                                       strerror(errno));
+                               mpsslog("cnt %d sum %zd\n",
+                                       copy.iovcnt, sum_iovec_len(&copy));
+                       }
+               }
+
+               if (console_poll[VIRTIO_CONSOLE_FD].revents & POLLIN) {
+                       while (rx_vr.info->avail_idx !=
+                               le16toh(rx_vr.vr.avail->idx)) {
+                               copy.iov = iov1;
+                               txrx_prepare(VIRTIO_ID_CONSOLE, 0, &rx_vr,
+                                            &copy, PAGE_SIZE);
+
+                               err = mic_virtio_copy(mic,
+                                       mic->mic_console.virtio_console_fd,
+                                       &rx_vr, &copy);
+                               if (!err) {
+                                       /* Set the correct output iov_len */
+                                       iov1->iov_len = copy.out_len;
+                                       verify_out_len(mic, &copy);
+#ifdef DEBUG
+                                       disp_iovec(mic, copy, __func__,
+                                                  __LINE__);
+                                       mpsslog("%s %s %d ",
+                                               mic->name, __func__, __LINE__);
+                                       mpsslog("read from net 0x%lx\n",
+                                               sum_iovec_len(copy));
+#endif
+                                       len = writev(pty_fd,
+                                               copy.iov, copy.iovcnt);
+                                       if (len != sum_iovec_len(&copy)) {
+                                               mpsslog("Tun write failed %s ",
+                                                       strerror(errno));
+                                               mpsslog("len 0x%zx ", len);
+                                               mpsslog("read_len 0x%zx\n",
+                                                       sum_iovec_len(&copy));
+                                       } else {
+#ifdef DEBUG
+                                               disp_iovec(mic, copy, __func__,
+                                                          __LINE__);
+                                               mpsslog("%s %s %d ",
+                                                       mic->name, __func__,
+                                                       __LINE__);
+                                               mpsslog("wrote to tap 0x%lx\n",
+                                                       len);
+#endif
+                                       }
+                               } else {
+                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
+                                               mic->name, __func__, __LINE__,
+                                               strerror(errno));
+                                       break;
+                               }
+                       }
+               }
+               if (console_poll[NET_FD_VIRTIO_NET].revents & POLLERR)
+                       mpsslog("%s: %s: POLLERR\n", __func__, mic->name);
+       }
+_close_pty:
+       close(pty_fd);
+_return:
+       pthread_exit(NULL);
+}
+
+static void
+add_virtio_device(struct mic_info *mic, struct mic_device_desc *dd)
+{
+       char path[PATH_MAX];
+       int fd, err;
+
+       snprintf(path, PATH_MAX, "/dev/mic%d", mic->id);
+       fd = open(path, O_RDWR);
+       if (fd < 0) {
+               mpsslog("Could not open %s %s\n", path, strerror(errno));
+               return;
+       }
+
+       err = ioctl(fd, MIC_VIRTIO_ADD_DEVICE, dd);
+       if (err < 0) {
+               mpsslog("Could not add %d %s\n", dd->type, strerror(errno));
+               close(fd);
+               return;
+       }
+       switch (dd->type) {
+       case VIRTIO_ID_NET:
+               mic->mic_net.virtio_net_fd = fd;
+               mpsslog("Added VIRTIO_ID_NET for %s\n", mic->name);
+               break;
+       case VIRTIO_ID_CONSOLE:
+               mic->mic_console.virtio_console_fd = fd;
+               mpsslog("Added VIRTIO_ID_CONSOLE for %s\n", mic->name);
+               break;
+       case VIRTIO_ID_BLOCK:
+               mic->mic_virtblk.virtio_block_fd = fd;
+               mpsslog("Added VIRTIO_ID_BLOCK for %s\n", mic->name);
+               break;
+       }
+}
+
+static bool
+set_backend_file(struct mic_info *mic)
+{
+       FILE *config;
+       char buff[PATH_MAX], *line, *evv, *p;
+
+       snprintf(buff, PATH_MAX, "%s/mpssd%03d.conf", mic_config_dir, mic->id);
+       config = fopen(buff, "r");
+       if (config == NULL)
+               return false;
+       do {  /* look for "virtblk_backend=XXXX" */
+               line = fgets(buff, PATH_MAX, config);
+               if (line == NULL)
+                       break;
+               if (*line == '#')
+                       continue;
+               p = strchr(line, '\n');
+               if (p)
+                       *p = '\0';
+       } while (strncmp(line, virtblk_backend, strlen(virtblk_backend)) != 0);
+       fclose(config);
+       if (line == NULL)
+               return false;
+       evv = strchr(line, '=');
+       if (evv == NULL)
+               return false;
+       mic->mic_virtblk.backend_file = malloc(strlen(evv) + 1);
+       if (mic->mic_virtblk.backend_file == NULL) {
+               mpsslog("%s %d can't allocate memory\n", mic->name, mic->id);
+               return false;
+       }
+       strcpy(mic->mic_virtblk.backend_file, evv + 1);
+       return true;
+}
+
+#define SECTOR_SIZE 512
+static bool
+set_backend_size(struct mic_info *mic)
+{
+       mic->mic_virtblk.backend_size = lseek(mic->mic_virtblk.backend, 0,
+               SEEK_END);
+       if (mic->mic_virtblk.backend_size < 0) {
+               mpsslog("%s: can't seek: %s\n",
+                       mic->name, mic->mic_virtblk.backend_file);
+               return false;
+       }
+       virtblk_dev_page.blk_config.capacity =
+               mic->mic_virtblk.backend_size / SECTOR_SIZE;
+       if ((mic->mic_virtblk.backend_size % SECTOR_SIZE) != 0)
+               virtblk_dev_page.blk_config.capacity++;
+
+       virtblk_dev_page.blk_config.capacity =
+               htole64(virtblk_dev_page.blk_config.capacity);
+
+       return true;
+}
+
+static bool
+open_backend(struct mic_info *mic)
+{
+       if (!set_backend_file(mic))
+               goto _error_exit;
+       mic->mic_virtblk.backend = open(mic->mic_virtblk.backend_file, O_RDWR);
+       if (mic->mic_virtblk.backend < 0) {
+               mpsslog("%s: can't open: %s\n", mic->name,
+                       mic->mic_virtblk.backend_file);
+               goto _error_free;
+       }
+       if (!set_backend_size(mic))
+               goto _error_close;
+       mic->mic_virtblk.backend_addr = mmap(NULL,
+               mic->mic_virtblk.backend_size,
+               PROT_READ|PROT_WRITE, MAP_SHARED,
+               mic->mic_virtblk.backend, 0L);
+       if (mic->mic_virtblk.backend_addr == MAP_FAILED) {
+               mpsslog("%s: can't map: %s %s\n",
+                       mic->name, mic->mic_virtblk.backend_file,
+                       strerror(errno));
+               goto _error_close;
+       }
+       return true;
+
+ _error_close:
+       close(mic->mic_virtblk.backend);
+ _error_free:
+       free(mic->mic_virtblk.backend_file);
+ _error_exit:
+       return false;
+}
+
+static void
+close_backend(struct mic_info *mic)
+{
+       munmap(mic->mic_virtblk.backend_addr, mic->mic_virtblk.backend_size);
+       close(mic->mic_virtblk.backend);
+       free(mic->mic_virtblk.backend_file);
+}
+
+static bool
+start_virtblk(struct mic_info *mic, struct mic_vring *vring)
+{
+       if (((unsigned long)&virtblk_dev_page.blk_config % 8) != 0) {
+               mpsslog("%s: blk_config is not 8 byte aligned.\n",
+                       mic->name);
+               return false;
+       }
+       add_virtio_device(mic, &virtblk_dev_page.dd);
+       if (MAP_FAILED == init_vr(mic, mic->mic_virtblk.virtio_block_fd,
+                                 VIRTIO_ID_BLOCK, vring, NULL,
+                                 virtblk_dev_page.dd.num_vq)) {
+               mpsslog("%s init_vr failed %s\n",
+                       mic->name, strerror(errno));
+               return false;
+       }
+       return true;
+}
+
+static void
+stop_virtblk(struct mic_info *mic)
+{
+       int vr_size, ret;
+
+       vr_size = PAGE_ALIGN(vring_size(MIC_VRING_ENTRIES,
+               MIC_VIRTIO_RING_ALIGN) + sizeof(struct _mic_vring_info));
+       ret = munmap(mic->mic_virtblk.block_dp,
+               MIC_DEVICE_PAGE_END + vr_size * virtblk_dev_page.dd.num_vq);
+       if (ret < 0)
+               mpsslog("%s munmap errno %d\n", mic->name, errno);
+       close(mic->mic_virtblk.virtio_block_fd);
+}
+
+static __u8
+header_error_check(struct vring_desc *desc)
+{
+       if (le32toh(desc->len) != sizeof(struct virtio_blk_outhdr)) {
+               mpsslog("%s() %d: length is not sizeof(virtio_blk_outhd)\n",
+                       __func__, __LINE__);
+               return -EIO;
+       }
+       if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT)) {
+               mpsslog("%s() %d: alone\n",
+                       __func__, __LINE__);
+               return -EIO;
+       }
+       if (le16toh(desc->flags) & VRING_DESC_F_WRITE) {
+               mpsslog("%s() %d: not read\n",
+                       __func__, __LINE__);
+               return -EIO;
+       }
+       return 0;
+}
+
+static int
+read_header(int fd, struct virtio_blk_outhdr *hdr, __u32 desc_idx)
+{
+       struct iovec iovec;
+       struct mic_copy_desc copy;
+
+       iovec.iov_len = sizeof(*hdr);
+       iovec.iov_base = hdr;
+       copy.iov = &iovec;
+       copy.iovcnt = 1;
+       copy.vr_idx = 0;  /* only one vring on virtio_block */
+       copy.update_used = false;  /* do not update used index */
+       return ioctl(fd, MIC_VIRTIO_COPY_DESC, &copy);
+}
+
+static int
+transfer_blocks(int fd, struct iovec *iovec, __u32 iovcnt)
+{
+       struct mic_copy_desc copy;
+
+       copy.iov = iovec;
+       copy.iovcnt = iovcnt;
+       copy.vr_idx = 0;  /* only one vring on virtio_block */
+       copy.update_used = false;  /* do not update used index */
+       return ioctl(fd, MIC_VIRTIO_COPY_DESC, &copy);
+}
+
+static __u8
+status_error_check(struct vring_desc *desc)
+{
+       if (le32toh(desc->len) != sizeof(__u8)) {
+               mpsslog("%s() %d: length is not sizeof(status)\n",
+                       __func__, __LINE__);
+               return -EIO;
+       }
+       return 0;
+}
+
+static int
+write_status(int fd, __u8 *status)
+{
+       struct iovec iovec;
+       struct mic_copy_desc copy;
+
+       iovec.iov_base = status;
+       iovec.iov_len = sizeof(*status);
+       copy.iov = &iovec;
+       copy.iovcnt = 1;
+       copy.vr_idx = 0;  /* only one vring on virtio_block */
+       copy.update_used = true; /* Update used index */
+       return ioctl(fd, MIC_VIRTIO_COPY_DESC, &copy);
+}
+
+static void *
+virtio_block(void *arg)
+{
+       struct mic_info *mic = (struct mic_info *)arg;
+       int ret;
+       struct pollfd block_poll;
+       struct mic_vring vring;
+       __u16 avail_idx;
+       __u32 desc_idx;
+       struct vring_desc *desc;
+       struct iovec *iovec, *piov;
+       __u8 status;
+       __u32 buffer_desc_idx;
+       struct virtio_blk_outhdr hdr;
+       void *fos;
+
+       for (;;) {  /* forever */
+               if (!open_backend(mic)) { /* No virtblk */
+                       for (mic->mic_virtblk.signaled = 0;
+                               !mic->mic_virtblk.signaled;)
+                               sleep(1);
+                       continue;
+               }
+
+               /* backend file is specified. */
+               if (!start_virtblk(mic, &vring))
+                       goto _close_backend;
+               iovec = malloc(sizeof(*iovec) *
+                       le32toh(virtblk_dev_page.blk_config.seg_max));
+               if (!iovec) {
+                       mpsslog("%s: can't alloc iovec: %s\n",
+                               mic->name, strerror(ENOMEM));
+                       goto _stop_virtblk;
+               }
+
+               block_poll.fd = mic->mic_virtblk.virtio_block_fd;
+               block_poll.events = POLLIN;
+               for (mic->mic_virtblk.signaled = 0;
+                    !mic->mic_virtblk.signaled;) {
+                       block_poll.revents = 0;
+                                       /* timeout in 1 sec to see signaled */
+                       ret = poll(&block_poll, 1, 1000);
+                       if (ret < 0) {
+                               mpsslog("%s %d: poll failed: %s\n",
+                                       __func__, __LINE__,
+                                       strerror(errno));
+                               continue;
+                       }
+
+                       if (!(block_poll.revents & POLLIN)) {
+#ifdef DEBUG
+                               mpsslog("%s %d: block_poll.revents=0x%x\n",
+                                       __func__, __LINE__, block_poll.revents);
+#endif
+                               continue;
+                       }
+
+                       /* POLLIN */
+                       while (vring.info->avail_idx !=
+                               le16toh(vring.vr.avail->idx)) {
+                               /* read header element */
+                               avail_idx =
+                                       vring.info->avail_idx &
+                                       (vring.vr.num - 1);
+                               desc_idx = le16toh(
+                                       vring.vr.avail->ring[avail_idx]);
+                               desc = &vring.vr.desc[desc_idx];
+#ifdef DEBUG
+                               mpsslog("%s() %d: avail_idx=%d ",
+                                       __func__, __LINE__,
+                                       vring.info->avail_idx);
+                               mpsslog("vring.vr.num=%d desc=%p\n",
+                                       vring.vr.num, desc);
+#endif
+                               status = header_error_check(desc);
+                               ret = read_header(
+                                       mic->mic_virtblk.virtio_block_fd,
+                                       &hdr, desc_idx);
+                               if (ret < 0) {
+                                       mpsslog("%s() %d %s: ret=%d %s\n",
+                                               __func__, __LINE__,
+                                               mic->name, ret,
+                                               strerror(errno));
+                                       break;
+                               }
+                               /* buffer element */
+                               piov = iovec;
+                               status = 0;
+                               fos = mic->mic_virtblk.backend_addr +
+                                       (hdr.sector * SECTOR_SIZE);
+                               buffer_desc_idx = next_desc(desc);
+                               desc_idx = buffer_desc_idx;
+                               for (desc = &vring.vr.desc[buffer_desc_idx];
+                                    desc->flags & VRING_DESC_F_NEXT;
+                                    desc_idx = next_desc(desc),
+                                            desc = &vring.vr.desc[desc_idx]) {
+                                       piov->iov_len = desc->len;
+                                       piov->iov_base = fos;
+                                       piov++;
+                                       fos += desc->len;
+                               }
+                               /* Returning NULLs for VIRTIO_BLK_T_GET_ID. */
+                               if (hdr.type & ~(VIRTIO_BLK_T_OUT |
+                                       VIRTIO_BLK_T_GET_ID)) {
+                                       /*
+                                         VIRTIO_BLK_T_IN - does not do
+                                         anything. Probably for documenting.
+                                         VIRTIO_BLK_T_SCSI_CMD - for
+                                         virtio_scsi.
+                                         VIRTIO_BLK_T_FLUSH - turned off in
+                                         config space.
+                                         VIRTIO_BLK_T_BARRIER - defined but not
+                                         used in anywhere.
+                                       */
+                                       mpsslog("%s() %d: type %x ",
+                                               __func__, __LINE__,
+                                               hdr.type);
+                                       mpsslog("is not supported\n");
+                                       status = -ENOTSUP;
+
+                               } else {
+                                       ret = transfer_blocks(
+                                       mic->mic_virtblk.virtio_block_fd,
+                                               iovec,
+                                               piov - iovec);
+                                       if (ret < 0 &&
+                                           status != 0)
+                                               status = ret;
+                               }
+                               /* write status and update used pointer */
+                               if (status != 0)
+                                       status = status_error_check(desc);
+                               ret = write_status(
+                                       mic->mic_virtblk.virtio_block_fd,
+                                       &status);
+#ifdef DEBUG
+                               mpsslog("%s() %d: write status=%d on desc=%p\n",
+                                       __func__, __LINE__,
+                                       status, desc);
+#endif
+                       }
+               }
+               free(iovec);
+_stop_virtblk:
+               stop_virtblk(mic);
+_close_backend:
+               close_backend(mic);
+       }  /* forever */
+
+       pthread_exit(NULL);
+}
+
+static void
+reset(struct mic_info *mic)
+{
+#define RESET_TIMEOUT 120
+       int i = RESET_TIMEOUT;
+       setsysfs(mic->name, "state", "reset");
+       while (i) {
+               char *state;
+               state = readsysfs(mic->name, "state");
+               if (!state)
+                       goto retry;
+               mpsslog("%s: %s %d state %s\n",
+                       mic->name, __func__, __LINE__, state);
+
+               /*
+                * If the shutdown was initiated by OSPM, the state stays
+                * in "suspended" which is also a valid condition for reset.
+                */
+               if ((!strcmp(state, "offline")) ||
+                   (!strcmp(state, "suspended"))) {
+                       free(state);
+                       break;
+               }
+               free(state);
+retry:
+               sleep(1);
+               i--;
+       }
+}
+
+static int
+get_mic_shutdown_status(struct mic_info *mic, char *shutdown_status)
+{
+       if (!strcmp(shutdown_status, "nop"))
+               return MIC_NOP;
+       if (!strcmp(shutdown_status, "crashed"))
+               return MIC_CRASHED;
+       if (!strcmp(shutdown_status, "halted"))
+               return MIC_HALTED;
+       if (!strcmp(shutdown_status, "poweroff"))
+               return MIC_POWER_OFF;
+       if (!strcmp(shutdown_status, "restart"))
+               return MIC_RESTART;
+       mpsslog("%s: BUG invalid status %s\n", mic->name, shutdown_status);
+       /* Invalid state */
+       assert(0);
+};
+
+static int get_mic_state(struct mic_info *mic, char *state)
+{
+       if (!strcmp(state, "offline"))
+               return MIC_OFFLINE;
+       if (!strcmp(state, "online"))
+               return MIC_ONLINE;
+       if (!strcmp(state, "shutting_down"))
+               return MIC_SHUTTING_DOWN;
+       if (!strcmp(state, "reset_failed"))
+               return MIC_RESET_FAILED;
+       if (!strcmp(state, "suspending"))
+               return MIC_SUSPENDING;
+       if (!strcmp(state, "suspended"))
+               return MIC_SUSPENDED;
+       mpsslog("%s: BUG invalid state %s\n", mic->name, state);
+       /* Invalid state */
+       assert(0);
+};
+
+static void mic_handle_shutdown(struct mic_info *mic)
+{
+#define SHUTDOWN_TIMEOUT 60
+       int i = SHUTDOWN_TIMEOUT, ret, stat = 0;
+       char *shutdown_status;
+       while (i) {
+               shutdown_status = readsysfs(mic->name, "shutdown_status");
+               if (!shutdown_status)
+                       continue;
+               mpsslog("%s: %s %d shutdown_status %s\n",
+                       mic->name, __func__, __LINE__, shutdown_status);
+               switch (get_mic_shutdown_status(mic, shutdown_status)) {
+               case MIC_RESTART:
+                       mic->restart = 1;
+               case MIC_HALTED:
+               case MIC_POWER_OFF:
+               case MIC_CRASHED:
+                       free(shutdown_status);
+                       goto reset;
+               default:
+                       break;
+               }
+               free(shutdown_status);
+               sleep(1);
+               i--;
+       }
+reset:
+       ret = kill(mic->pid, SIGTERM);
+       mpsslog("%s: %s %d kill pid %d ret %d\n",
+               mic->name, __func__, __LINE__,
+               mic->pid, ret);
+       if (!ret) {
+               ret = waitpid(mic->pid, &stat,
+                       WIFSIGNALED(stat));
+               mpsslog("%s: %s %d waitpid ret %d pid %d\n",
+                       mic->name, __func__, __LINE__,
+                       ret, mic->pid);
+       }
+       if (ret == mic->pid)
+               reset(mic);
+}
+
+static void *
+mic_config(void *arg)
+{
+       struct mic_info *mic = (struct mic_info *)arg;
+       char *state = NULL;
+       char pathname[PATH_MAX];
+       int fd, ret;
+       struct pollfd ufds[1];
+       char value[4096];
+
+       snprintf(pathname, PATH_MAX - 1, "%s/%s/%s",
+                MICSYSFSDIR, mic->name, "state");
+
+       fd = open(pathname, O_RDONLY);
+       if (fd < 0) {
+               mpsslog("%s: opening file %s failed %s\n",
+                       mic->name, pathname, strerror(errno));
+               goto error;
+       }
+
+       do {
+               ret = read(fd, value, sizeof(value));
+               if (ret < 0) {
+                       mpsslog("%s: Failed to read sysfs entry '%s': %s\n",
+                               mic->name, pathname, strerror(errno));
+                       goto close_error1;
+               }
+retry:
+               state = readsysfs(mic->name, "state");
+               if (!state)
+                       goto retry;
+               mpsslog("%s: %s %d state %s\n",
+                       mic->name, __func__, __LINE__, state);
+               switch (get_mic_state(mic, state)) {
+               case MIC_SHUTTING_DOWN:
+                       mic_handle_shutdown(mic);
+                       goto close_error;
+               case MIC_SUSPENDING:
+                       mic->boot_on_resume = 1;
+                       setsysfs(mic->name, "state", "suspend");
+                       mic_handle_shutdown(mic);
+                       goto close_error;
+               case MIC_OFFLINE:
+                       if (mic->boot_on_resume) {
+                               setsysfs(mic->name, "state", "boot");
+                               mic->boot_on_resume = 0;
+                       }
+                       break;
+               default:
+                       break;
+               }
+               free(state);
+
+               ufds[0].fd = fd;
+               ufds[0].events = POLLERR | POLLPRI;
+               ret = poll(ufds, 1, -1);
+               if (ret < 0) {
+                       mpsslog("%s: poll failed %s\n",
+                               mic->name, strerror(errno));
+                       goto close_error1;
+               }
+       } while (1);
+close_error:
+       free(state);
+close_error1:
+       close(fd);
+error:
+       init_mic(mic);
+       pthread_exit(NULL);
+}
+
+static void
+set_cmdline(struct mic_info *mic)
+{
+       char buffer[PATH_MAX];
+       int len;
+
+       len = snprintf(buffer, PATH_MAX,
+               "clocksource=tsc highres=off nohz=off ");
+       len += snprintf(buffer + len, PATH_MAX,
+               "cpufreq_on;corec6_off;pc3_off;pc6_off ");
+       len += snprintf(buffer + len, PATH_MAX,
+               "ifcfg=static;address,172.31.%d.1;netmask,255.255.255.0",
+               mic->id);
+
+       setsysfs(mic->name, "cmdline", buffer);
+       mpsslog("%s: Command line: \"%s\"\n", mic->name, buffer);
+       snprintf(buffer, PATH_MAX, "172.31.%d.1", mic->id);
+       mpsslog("%s: IPADDR: \"%s\"\n", mic->name, buffer);
+}
+
+static void
+set_log_buf_info(struct mic_info *mic)
+{
+       int fd;
+       off_t len;
+       char system_map[] = "/lib/firmware/mic/System.map";
+       char *map, *temp, log_buf[17] = {'\0'};
+
+       fd = open(system_map, O_RDONLY);
+       if (fd < 0) {
+               mpsslog("%s: Opening System.map failed: %d\n",
+                       mic->name, errno);
+               return;
+       }
+       len = lseek(fd, 0, SEEK_END);
+       if (len < 0) {
+               mpsslog("%s: Reading System.map size failed: %d\n",
+                       mic->name, errno);
+               close(fd);
+               return;
+       }
+       map = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (map == MAP_FAILED) {
+               mpsslog("%s: mmap of System.map failed: %d\n",
+                       mic->name, errno);
+               close(fd);
+               return;
+       }
+       temp = strstr(map, "__log_buf");
+       if (!temp) {
+               mpsslog("%s: __log_buf not found: %d\n", mic->name, errno);
+               munmap(map, len);
+               close(fd);
+               return;
+       }
+       strncpy(log_buf, temp - 19, 16);
+       setsysfs(mic->name, "log_buf_addr", log_buf);
+       mpsslog("%s: log_buf_addr: %s\n", mic->name, log_buf);
+       temp = strstr(map, "log_buf_len");
+       if (!temp) {
+               mpsslog("%s: log_buf_len not found: %d\n", mic->name, errno);
+               munmap(map, len);
+               close(fd);
+               return;
+       }
+       strncpy(log_buf, temp - 19, 16);
+       setsysfs(mic->name, "log_buf_len", log_buf);
+       mpsslog("%s: log_buf_len: %s\n", mic->name, log_buf);
+       munmap(map, len);
+       close(fd);
+}
+
+static void init_mic(struct mic_info *mic);
+
+static void
+change_virtblk_backend(int x, siginfo_t *siginfo, void *p)
+{
+       struct mic_info *mic;
+
+       for (mic = mic_list.next; mic != NULL; mic = mic->next)
+               mic->mic_virtblk.signaled = 1/* true */;
+}
+
+static void
+init_mic(struct mic_info *mic)
+{
+       struct sigaction ignore = {
+               .sa_flags = 0,
+               .sa_handler = SIG_IGN
+       };
+       struct sigaction act = {
+               .sa_flags = SA_SIGINFO,
+               .sa_sigaction = change_virtblk_backend,
+       };
+       char buffer[PATH_MAX];
+       int err;
+
+       /*
+        * Currently, one virtio block device is supported for each MIC card
+        * at a time. Any user (or test) can send a SIGUSR1 to the MIC daemon.
+        * The signal informs the virtio block backend about a change in the
+        * configuration file which specifies the virtio backend file name on
+        * the host. Virtio block backend then re-reads the configuration file
+        * and switches to the new block device. This signalling mechanism may
+        * not be required once multiple virtio block devices are supported by
+        * the MIC daemon.
+        */
+       sigaction(SIGUSR1, &ignore, NULL);
+
+       mic->pid = fork();
+       switch (mic->pid) {
+       case 0:
+               set_log_buf_info(mic);
+               set_cmdline(mic);
+               add_virtio_device(mic, &virtcons_dev_page.dd);
+               add_virtio_device(mic, &virtnet_dev_page.dd);
+               err = pthread_create(&mic->mic_console.console_thread, NULL,
+                       virtio_console, mic);
+               if (err)
+                       mpsslog("%s virtcons pthread_create failed %s\n",
+                               mic->name, strerror(err));
+               err = pthread_create(&mic->mic_net.net_thread, NULL,
+                       virtio_net, mic);
+               if (err)
+                       mpsslog("%s virtnet pthread_create failed %s\n",
+                               mic->name, strerror(err));
+               err = pthread_create(&mic->mic_virtblk.block_thread, NULL,
+                       virtio_block, mic);
+               if (err)
+                       mpsslog("%s virtblk pthread_create failed %s\n",
+                               mic->name, strerror(err));
+               sigemptyset(&act.sa_mask);
+               err = sigaction(SIGUSR1, &act, NULL);
+               if (err)
+                       mpsslog("%s sigaction SIGUSR1 failed %s\n",
+                               mic->name, strerror(errno));
+               while (1)
+                       sleep(60);
+       case -1:
+               mpsslog("fork failed MIC name %s id %d errno %d\n",
+                       mic->name, mic->id, errno);
+               break;
+       default:
+               if (mic->restart) {
+                       snprintf(buffer, PATH_MAX, "boot");
+                       setsysfs(mic->name, "state", buffer);
+                       mpsslog("%s restarting mic %d\n",
+                               mic->name, mic->restart);
+                       mic->restart = 0;
+               }
+               pthread_create(&mic->config_thread, NULL, mic_config, mic);
+       }
+}
+
+static void
+start_daemon(void)
+{
+       struct mic_info *mic;
+
+       for (mic = mic_list.next; mic != NULL; mic = mic->next)
+               init_mic(mic);
+
+       while (1)
+               sleep(60);
+}
+
+static int
+init_mic_list(void)
+{
+       struct mic_info *mic = &mic_list;
+       struct dirent *file;
+       DIR *dp;
+       int cnt = 0;
+
+       dp = opendir(MICSYSFSDIR);
+       if (!dp)
+               return 0;
+
+       while ((file = readdir(dp)) != NULL) {
+               if (!strncmp(file->d_name, "mic", 3)) {
+                       mic->next = calloc(1, sizeof(struct mic_info));
+                       if (mic->next) {
+                               mic = mic->next;
+                               mic->id = atoi(&file->d_name[3]);
+                               mic->name = malloc(strlen(file->d_name) + 16);
+                               if (mic->name)
+                                       strcpy(mic->name, file->d_name);
+                               mpsslog("MIC name %s id %d\n", mic->name,
+                                       mic->id);
+                               cnt++;
+                       }
+               }
+       }
+
+       closedir(dp);
+       return cnt;
+}
+
+void
+mpsslog(char *format, ...)
+{
+       va_list args;
+       char buffer[4096];
+       char ts[52], *ts1;
+       time_t t;
+
+       if (logfp == NULL)
+               return;
+
+       va_start(args, format);
+       vsprintf(buffer, format, args);
+       va_end(args);
+
+       time(&t);
+       ts1 = ctime_r(&t, ts);
+       ts1[strlen(ts1) - 1] = '\0';
+       fprintf(logfp, "%s: %s", ts1, buffer);
+
+       fflush(logfp);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int cnt;
+       pid_t pid;
+
+       myname = argv[0];
+
+       logfp = fopen(LOGFILE_NAME, "a+");
+       if (!logfp) {
+               fprintf(stderr, "cannot open logfile '%s'\n", LOGFILE_NAME);
+               exit(1);
+       }
+       pid = fork();
+       switch (pid) {
+       case 0:
+               break;
+       case -1:
+               exit(2);
+       default:
+               exit(0);
+       }
+
+       mpsslog("MIC Daemon start\n");
+
+       cnt = init_mic_list();
+       if (cnt == 0) {
+               mpsslog("MIC module not loaded\n");
+               exit(3);
+       }
+       mpsslog("MIC found %d devices\n", cnt);
+
+       start_daemon();
+
+       exit(0);
+}
diff --git a/Documentation/mic/mpssd/mpssd.h b/Documentation/mic/mpssd/mpssd.h
new file mode 100644 (file)
index 0000000..f5f18b1
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC User Space Tools.
+ */
+#ifndef _MPSSD_H_
+#define _MPSSD_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/dir.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <signal.h>
+#include <limits.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+#include <linux/if_tun.h>
+#include <linux/virtio_ids.h>
+
+#define MICSYSFSDIR "/sys/class/mic"
+#define LOGFILE_NAME "/var/log/mpssd"
+#define PAGE_SIZE 4096
+
+struct mic_console_info {
+       pthread_t       console_thread;
+       int             virtio_console_fd;
+       void            *console_dp;
+};
+
+struct mic_net_info {
+       pthread_t       net_thread;
+       int             virtio_net_fd;
+       int             tap_fd;
+       void            *net_dp;
+};
+
+struct mic_virtblk_info {
+       pthread_t       block_thread;
+       int             virtio_block_fd;
+       void            *block_dp;
+       volatile sig_atomic_t   signaled;
+       char            *backend_file;
+       int             backend;
+       void            *backend_addr;
+       long            backend_size;
+};
+
+struct mic_info {
+       int             id;
+       char            *name;
+       pthread_t       config_thread;
+       pid_t           pid;
+       struct mic_console_info mic_console;
+       struct mic_net_info     mic_net;
+       struct mic_virtblk_info mic_virtblk;
+       int             restart;
+       int             boot_on_resume;
+       struct mic_info *next;
+};
+
+__attribute__((format(printf, 1, 2)))
+void mpsslog(char *format, ...);
+char *readsysfs(char *dir, char *entry);
+int setsysfs(char *dir, char *entry, char *value);
+#endif
diff --git a/Documentation/mic/mpssd/sysfs.c b/Documentation/mic/mpssd/sysfs.c
new file mode 100644 (file)
index 0000000..8dd3269
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC User Space Tools.
+ */
+
+#include "mpssd.h"
+
+#define PAGE_SIZE 4096
+
+char *
+readsysfs(char *dir, char *entry)
+{
+       char filename[PATH_MAX];
+       char value[PAGE_SIZE];
+       char *string = NULL;
+       int fd;
+       int len;
+
+       if (dir == NULL)
+               snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry);
+       else
+               snprintf(filename, PATH_MAX,
+                        "%s/%s/%s", MICSYSFSDIR, dir, entry);
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0) {
+               mpsslog("Failed to open sysfs entry '%s': %s\n",
+                       filename, strerror(errno));
+               return NULL;
+       }
+
+       len = read(fd, value, sizeof(value));
+       if (len < 0) {
+               mpsslog("Failed to read sysfs entry '%s': %s\n",
+                       filename, strerror(errno));
+               goto readsys_ret;
+       }
+       if (len == 0)
+               goto readsys_ret;
+
+       value[len - 1] = '\0';
+
+       string = malloc(strlen(value) + 1);
+       if (string)
+               strcpy(string, value);
+
+readsys_ret:
+       close(fd);
+       return string;
+}
+
+int
+setsysfs(char *dir, char *entry, char *value)
+{
+       char filename[PATH_MAX];
+       char *oldvalue;
+       int fd, ret = 0;
+
+       if (dir == NULL)
+               snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry);
+       else
+               snprintf(filename, PATH_MAX, "%s/%s/%s",
+                        MICSYSFSDIR, dir, entry);
+
+       oldvalue = readsysfs(dir, entry);
+
+       fd = open(filename, O_RDWR);
+       if (fd < 0) {
+               ret = errno;
+               mpsslog("Failed to open sysfs entry '%s': %s\n",
+                       filename, strerror(errno));
+               goto done;
+       }
+
+       if (!oldvalue || strcmp(value, oldvalue)) {
+               if (write(fd, value, strlen(value)) < 0) {
+                       ret = errno;
+                       mpsslog("Failed to write new sysfs entry '%s': %s\n",
+                               filename, strerror(errno));
+               }
+       }
+       close(fd);
+done:
+       if (oldvalue)
+               free(oldvalue);
+       return ret;
+}
index 448ce5e29c561bcf4fb8d1a6d3013fb6339e69be..dca5834685cf93d0cd105e66a7cf656329fc3e2e 100644 (file)
@@ -486,8 +486,7 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp)
                }
 
                sprintf(devp->hd_name, "hpet%d", (int)(devp - hpetp->hp_dev));
-               irq_flags = devp->hd_flags & HPET_SHARED_IRQ
-                                               ? IRQF_SHARED : IRQF_DISABLED;
+               irq_flags = devp->hd_flags & HPET_SHARED_IRQ ? IRQF_SHARED : 0;
                if (request_irq(irq, hpet_interrupt, irq_flags,
                                devp->hd_name, (void *)devp)) {
                        printk(KERN_ERR "hpet: IRQ %d is not free\n", irq);
@@ -971,8 +970,6 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data)
                struct acpi_resource_fixed_memory32 *fixmem32;
 
                fixmem32 = &res->data.fixed_memory32;
-               if (!fixmem32)
-                       return AE_NO_MEMORY;
 
                hdp->hd_phys_address = fixmem32->address;
                hdp->hd_address = ioremap(fixmem32->address,
index 190d4423653f6b99690967ba3602915eda5463fb..2f685f6eda4868195d9dcc3ac7d317e9cc2fc42a 100644 (file)
@@ -193,8 +193,8 @@ int misc_register(struct miscdevice * misc)
        if (misc->minor == MISC_DYNAMIC_MINOR) {
                int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
                if (i >= DYNAMIC_MINORS) {
-                       mutex_unlock(&misc_mtx);
-                       return -EBUSY;
+                       err = -EBUSY;
+                       goto out;
                }
                misc->minor = DYNAMIC_MINORS - i - 1;
                set_bit(i, misc_minors);
@@ -203,8 +203,8 @@ int misc_register(struct miscdevice * misc)
 
                list_for_each_entry(c, &misc_list, list) {
                        if (c->minor == misc->minor) {
-                               mutex_unlock(&misc_mtx);
-                               return -EBUSY;
+                               err = -EBUSY;
+                               goto out;
                        }
                }
        }
index cfdfe493c6af007a4d2455ab0315ef2611d8ebb3..1fd00dc0689721ae967af86aa2c88dbb903ddef2 100644 (file)
@@ -220,7 +220,7 @@ static int __init nwbutton_init(void)
                return -EBUSY;
        }
 
-       if (request_irq (IRQ_NETWINDER_BUTTON, button_handler, IRQF_DISABLED,
+       if (request_irq (IRQ_NETWINDER_BUTTON, button_handler, 0,
                        "nwbutton", NULL)) {
                printk (KERN_WARNING "nwbutton: IRQ %d is not free.\n",
                                IRQ_NETWINDER_BUTTON);
index c0cbbd429bdc7abcf0d3055404224bff7d367196..35259961cc38f74bbf0f6727dc205f5ec03368da 100644 (file)
@@ -227,7 +227,7 @@ static inline unsigned char rtc_is_updating(void)
 
 #ifdef RTC_IRQ
 /*
- *     A very tiny interrupt handler. It runs with IRQF_DISABLED set,
+ *     A very tiny interrupt handler. It runs with interrupts disabled,
  *     but there is possibility of conflicting with the set_rtc_mmss()
  *     call (the rtc irq and the timer irq can easily run at the same
  *     time in two different CPUs). So we need to serialize
@@ -1040,8 +1040,7 @@ no_irq:
                rtc_int_handler_ptr = rtc_interrupt;
        }
 
-       if (request_irq(RTC_IRQ, rtc_int_handler_ptr, IRQF_DISABLED,
-                       "rtc", NULL)) {
+       if (request_irq(RTC_IRQ, rtc_int_handler_ptr, 0, "rtc", NULL)) {
                /* Yeah right, seeing as irq 8 doesn't even hit the bus. */
                rtc_has_irq = 0;
                printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ);
index 5816b39ff5a9cf43cbba56579d9b0b2d021f8883..8bab59292a0d0a42f2a89914edc36824483160f0 100644 (file)
@@ -108,8 +108,7 @@ scdrv_open(struct inode *inode, struct file *file)
        /* hook this subchannel up to the system controller interrupt */
        mutex_lock(&scdrv_mutex);
        rv = request_irq(SGI_UART_VECTOR, scdrv_interrupt,
-                        IRQF_SHARED | IRQF_DISABLED,
-                        SYSCTL_BASENAME, sd);
+                        IRQF_SHARED, SYSCTL_BASENAME, sd);
        if (rv) {
                ia64_sn_irtr_close(sd->sd_nasid, sd->sd_subch);
                kfree(sd);
index ee156948b9f810d3cd66fbb3ff303e218f884a53..59bcefd6ec7c8ba86ceacce796af03fdce7e99ad 100644 (file)
@@ -292,8 +292,7 @@ scdrv_event_init(struct sysctl_data_s *scd)
 
        /* hook event subchannel up to the system controller interrupt */
        rv = request_irq(SGI_UART_VECTOR, scdrv_event_interrupt,
-                        IRQF_SHARED | IRQF_DISABLED,
-                        "system controller events", event_sd);
+                        IRQF_SHARED, "system controller events", event_sd);
        if (rv) {
                printk(KERN_WARNING "%s: irq request failed (%d)\n",
                       __func__, rv);
index e95e0ab0bd870a4c8d679d7dbb092ea2521ee42e..100cd1de9939dc77c5c523b10e15e1bc80908449 100644 (file)
@@ -222,7 +222,7 @@ static int tlclk_open(struct inode *inode, struct file *filp)
        /* This device is wired through the FPGA IO space of the ATCA blade
         * we can't share this IRQ */
        result = request_irq(telclk_interrupt, &tlclk_interrupt,
-                            IRQF_DISABLED, "telco_clock", tlclk_interrupt);
+                            0, "telco_clock", tlclk_interrupt);
        if (result == -EBUSY)
                printk(KERN_ERR "tlclk: Interrupt can't be reserved.\n");
        else
index 5224da5202d3497589d4d74df171cde3efd7ba67..f6345f932e46c8e841ae2f7e9777da0ed9a905f3 100644 (file)
@@ -721,7 +721,7 @@ static int hwicap_remove(struct device *dev)
 {
        struct hwicap_drvdata *drvdata;
 
-       drvdata = (struct hwicap_drvdata *)dev_get_drvdata(dev);
+       drvdata = dev_get_drvdata(dev);
 
        if (!drvdata)
                return 0;
@@ -731,7 +731,6 @@ static int hwicap_remove(struct device *dev)
        iounmap(drvdata->base_address);
        release_mem_region(drvdata->mem_start, drvdata->mem_size);
        kfree(drvdata);
-       dev_set_drvdata(dev, NULL);
 
        mutex_lock(&icap_sem);
        probed_devices[MINOR(dev->devt)-XHWICAP_MINOR] = 0;
index 5985807e52c9439fc1f7587aa2c0645f5e344c57..e23f1c2e505366dd24cb2ff6037f31074f7331ff 100644 (file)
 
 /**
  * struct adc_jack_data - internal data for adc_jack device driver
- * @edev        - extcon device.
- * @cable_names - list of supported cables.
- * @num_cables  - size of cable_names.
- * @adc_conditions       - list of adc value conditions.
- * @num_conditions       - size of adc_conditions.
- * @irq         - irq number of attach/detach event (0 if not exist).
- * @handling_delay      - interrupt handler will schedule extcon event
- *                      handling at handling_delay jiffies.
- * @handler     - extcon event handler called by interrupt handler.
- * @chan       - iio channel being queried.
+ * @edev:              extcon device.
+ * @cable_names:       list of supported cables.
+ * @num_cables:                size of cable_names.
+ * @adc_conditions:    list of adc value conditions.
+ * @num_conditions:    size of adc_conditions.
+ * @irq:               irq number of attach/detach event (0 if not exist).
+ * @handling_delay:    interrupt handler will schedule extcon event
+ *                     handling at handling_delay jiffies.
+ * @handler:           extcon event handler called by interrupt handler.
+ * @chan:              iio channel being queried.
  */
 struct adc_jack_data {
        struct extcon_dev edev;
@@ -64,7 +64,7 @@ static void adc_jack_handler(struct work_struct *work)
 
        ret = iio_read_channel_raw(data->chan, &adc_val);
        if (ret < 0) {
-               dev_err(data->edev.dev, "read channel() error: %d\n", ret);
+               dev_err(&data->edev.dev, "read channel() error: %d\n", ret);
                return;
        }
 
@@ -95,7 +95,7 @@ static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
 static int adc_jack_probe(struct platform_device *pdev)
 {
        struct adc_jack_data *data;
-       struct adc_jack_pdata *pdata = pdev->dev.platform_data;
+       struct adc_jack_pdata *pdata = dev_get_platdata(&pdev->dev);
        int i, err = 0;
 
        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
@@ -110,6 +110,7 @@ static int adc_jack_probe(struct platform_device *pdev)
                goto out;
        }
 
+       data->edev.dev.parent = &pdev->dev;
        data->edev.supported_cable = pdata->cable_names;
 
        /* Check the length of array and set num_cables */
@@ -148,7 +149,7 @@ static int adc_jack_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, data);
 
-       err = extcon_dev_register(&data->edev, &pdev->dev);
+       err = extcon_dev_register(&data->edev);
        if (err)
                goto out;
 
index e55713083c7887abe7baa60d24396126446bbf30..3c55ec856e39c714e7b474f081c01180b9aa9e2d 100644 (file)
@@ -86,8 +86,8 @@ struct arizona_extcon_info {
 };
 
 static const struct arizona_micd_config micd_default_modes[] = {
-       { ARIZONA_ACCDET_SRC, 1 << ARIZONA_MICD_BIAS_SRC_SHIFT, 0 },
-       { 0,                  2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 },
+       { ARIZONA_ACCDET_SRC, 1, 0 },
+       { 0,                  2, 1 },
 };
 
 static const struct arizona_micd_range micd_default_ranges[] = {
@@ -182,7 +182,8 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
                                        info->micd_modes[mode].gpio);
        regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
                           ARIZONA_MICD_BIAS_SRC_MASK,
-                          info->micd_modes[mode].bias);
+                          info->micd_modes[mode].bias <<
+                          ARIZONA_MICD_BIAS_SRC_SHIFT);
        regmap_update_bits(arizona->regmap, ARIZONA_ACCESSORY_DETECT_MODE_1,
                           ARIZONA_ACCDET_SRC, info->micd_modes[mode].src);
 
@@ -193,7 +194,7 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
 
 static const char *arizona_extcon_get_micbias(struct arizona_extcon_info *info)
 {
-       switch (info->micd_modes[0].bias >> ARIZONA_MICD_BIAS_SRC_SHIFT) {
+       switch (info->micd_modes[0].bias) {
        case 1:
                return "MICBIAS1";
        case 2:
@@ -388,7 +389,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
                           >> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT;
 
                if (range < ARRAY_SIZE(arizona_hpdet_b_ranges) - 1 &&
-                   (val < 100 || val > 0x3fb)) {
+                   (val < 100 || val >= 0x3fb)) {
                        range++;
                        dev_dbg(arizona->dev, "Moving to HPDET range %d\n",
                                range);
@@ -401,7 +402,7 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
                }
 
                /* If we go out of range report top of range */
-               if (val < 100 || val > 0x3fb) {
+               if (val < 100 || val >= 0x3fb) {
                        dev_dbg(arizona->dev, "Measurement out of range\n");
                        return ARIZONA_HPDET_MAX;
                }
@@ -514,7 +515,7 @@ static int arizona_hpdet_do_id(struct arizona_extcon_info *info, int *reading,
                }
 
                /*
-                * If we measure the mic as 
+                * If we measure the mic as high impedance
                 */
                if (!id_gpio || info->hpdet_res[1] > 50) {
                        dev_dbg(arizona->dev, "Detected mic\n");
@@ -564,11 +565,10 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
        }
 
        ret = arizona_hpdet_read(info);
-       if (ret == -EAGAIN) {
+       if (ret == -EAGAIN)
                goto out;
-       } else if (ret < 0) {
+       else if (ret < 0)
                goto done;
-       }
        reading = ret;
 
        /* Reset back to starting range */
@@ -578,11 +578,10 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
                           0);
 
        ret = arizona_hpdet_do_id(info, &reading, &mic);
-       if (ret == -EAGAIN) {
+       if (ret == -EAGAIN)
                goto out;
-       } else if (ret < 0) {
+       else if (ret < 0)
                goto done;
-       }
 
        /* Report high impedence cables as line outputs */
        if (reading >= 5000)
@@ -738,8 +737,8 @@ err:
 static void arizona_micd_timeout_work(struct work_struct *work)
 {
        struct arizona_extcon_info *info = container_of(work,
-                                                       struct arizona_extcon_info,
-                                                       micd_timeout_work.work);
+                                               struct arizona_extcon_info,
+                                               micd_timeout_work.work);
 
        mutex_lock(&info->lock);
 
@@ -756,8 +755,8 @@ static void arizona_micd_timeout_work(struct work_struct *work)
 static void arizona_micd_detect(struct work_struct *work)
 {
        struct arizona_extcon_info *info = container_of(work,
-                                                       struct arizona_extcon_info,
-                                                       micd_detect_work.work);
+                                               struct arizona_extcon_info,
+                                               micd_detect_work.work);
        struct arizona *arizona = info->arizona;
        unsigned int val = 0, lvl;
        int ret, i, key;
@@ -769,7 +768,8 @@ static void arizona_micd_detect(struct work_struct *work)
        for (i = 0; i < 10 && !(val & 0x7fc); i++) {
                ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
                if (ret != 0) {
-                       dev_err(arizona->dev, "Failed to read MICDET: %d\n", ret);
+                       dev_err(arizona->dev,
+                               "Failed to read MICDET: %d\n", ret);
                        mutex_unlock(&info->lock);
                        return;
                }
@@ -777,7 +777,8 @@ static void arizona_micd_detect(struct work_struct *work)
                dev_dbg(arizona->dev, "MICDET: %x\n", val);
 
                if (!(val & ARIZONA_MICD_VALID)) {
-                       dev_warn(arizona->dev, "Microphone detection state invalid\n");
+                       dev_warn(arizona->dev,
+                                "Microphone detection state invalid\n");
                        mutex_unlock(&info->lock);
                        return;
                }
@@ -925,8 +926,8 @@ static irqreturn_t arizona_micdet(int irq, void *data)
 static void arizona_hpdet_work(struct work_struct *work)
 {
        struct arizona_extcon_info *info = container_of(work,
-                                                       struct arizona_extcon_info,
-                                                       hpdet_work.work);
+                                               struct arizona_extcon_info,
+                                               hpdet_work.work);
 
        mutex_lock(&info->lock);
        arizona_start_hpdet_acc_id(info);
@@ -973,10 +974,13 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
                                           &info->hpdet_work,
                                           msecs_to_jiffies(HPDET_DEBOUNCE));
 
-               if (cancelled_mic)
+               if (cancelled_mic) {
+                       int micd_timeout = info->micd_timeout;
+
                        queue_delayed_work(system_power_efficient_wq,
                                           &info->micd_timeout_work,
-                                          msecs_to_jiffies(info->micd_timeout));
+                                          msecs_to_jiffies(micd_timeout));
+               }
 
                goto out;
        }
@@ -1039,6 +1043,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
        else
                info->micd_timeout = DEFAULT_MICD_TIMEOUT;
 
+out:
        /* Clear trig_sts to make sure DCVDD is not forced up */
        regmap_write(arizona->regmap, ARIZONA_AOD_WKUP_AND_TRIG,
                     ARIZONA_MICD_CLAMP_FALL_TRIG_STS |
@@ -1046,7 +1051,6 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
                     ARIZONA_JD1_FALL_TRIG_STS |
                     ARIZONA_JD1_RISE_TRIG_STS);
 
-out:
        mutex_unlock(&info->lock);
 
        pm_runtime_mark_last_busy(info->dev);
@@ -1129,9 +1133,10 @@ static int arizona_extcon_probe(struct platform_device *pdev)
        }
 
        info->edev.name = "Headset Jack";
+       info->edev.dev.parent = arizona->dev;
        info->edev.supported_cable = arizona_cable;
 
-       ret = extcon_dev_register(&info->edev, arizona->dev);
+       ret = extcon_dev_register(&info->edev);
        if (ret < 0) {
                dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
                        ret);
index 148382faded9f125940ee11ca211bcea38f36290..15443d3b6be18049ddddbb7ec035eb05ce7dc409 100644 (file)
@@ -74,7 +74,7 @@ static DEFINE_MUTEX(extcon_dev_list_lock);
 
 /**
  * check_mutually_exclusive - Check if new_state violates mutually_exclusive
- *                         condition.
+ *                           condition.
  * @edev:      the extcon device
  * @new_state: new cable attach status for @edev
  *
@@ -105,7 +105,7 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
                          char *buf)
 {
        int i, count = 0;
-       struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+       struct extcon_dev *edev = dev_get_drvdata(dev);
 
        if (edev->print_state) {
                int ret = edev->print_state(edev, buf);
@@ -129,13 +129,12 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
        return count;
 }
 
-int extcon_set_state(struct extcon_dev *edev, u32 state);
 static ssize_t state_store(struct device *dev, struct device_attribute *attr,
                           const char *buf, size_t count)
 {
        u32 state;
        ssize_t ret = 0;
-       struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+       struct extcon_dev *edev = dev_get_drvdata(dev);
 
        ret = sscanf(buf, "0x%x", &state);
        if (ret == 0)
@@ -153,7 +152,7 @@ static DEVICE_ATTR_RW(state);
 static ssize_t name_show(struct device *dev, struct device_attribute *attr,
                char *buf)
 {
-       struct extcon_dev *edev = (struct extcon_dev *) dev_get_drvdata(dev);
+       struct extcon_dev *edev = dev_get_drvdata(dev);
 
        /* Optional callback given by the user */
        if (edev->print_name) {
@@ -162,7 +161,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
                        return ret;
        }
 
-       return sprintf(buf, "%s\n", dev_name(edev->dev));
+       return sprintf(buf, "%s\n", dev_name(&edev->dev));
 }
 static DEVICE_ATTR_RO(name);
 
@@ -189,7 +188,7 @@ static ssize_t cable_state_show(struct device *dev,
 
 /**
  * extcon_update_state() - Update the cable attach states of the extcon device
- *                     only for the masked bits.
+ *                        only for the masked bits.
  * @edev:      the extcon device
  * @mask:      the bit mask to designate updated bits.
  * @state:     new cable attach status for @edev
@@ -227,11 +226,10 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
                edev->state |= state & mask;
 
                raw_notifier_call_chain(&edev->nh, old_state, edev);
-
                /* This could be in interrupt handler */
                prop_buf = (char *)get_zeroed_page(GFP_ATOMIC);
                if (prop_buf) {
-                       length = name_show(edev->dev, NULL, prop_buf);
+                       length = name_show(&edev->dev, NULL, prop_buf);
                        if (length > 0) {
                                if (prop_buf[length - 1] == '\n')
                                        prop_buf[length - 1] = 0;
@@ -239,7 +237,7 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
                                        "NAME=%s", prop_buf);
                                envp[env_offset++] = name_buf;
                        }
-                       length = state_show(edev->dev, NULL, prop_buf);
+                       length = state_show(&edev->dev, NULL, prop_buf);
                        if (length > 0) {
                                if (prop_buf[length - 1] == '\n')
                                        prop_buf[length - 1] = 0;
@@ -251,14 +249,14 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state)
                        /* Unlock early before uevent */
                        spin_unlock_irqrestore(&edev->lock, flags);
 
-                       kobject_uevent_env(&edev->dev->kobj, KOBJ_CHANGE, envp);
+                       kobject_uevent_env(&edev->dev.kobj, KOBJ_CHANGE, envp);
                        free_page((unsigned long)prop_buf);
                } else {
                        /* Unlock early before uevent */
                        spin_unlock_irqrestore(&edev->lock, flags);
 
-                       dev_err(edev->dev, "out of memory in extcon_set_state\n");
-                       kobject_uevent(&edev->dev->kobj, KOBJ_CHANGE);
+                       dev_err(&edev->dev, "out of memory in extcon_set_state\n");
+                       kobject_uevent(&edev->dev.kobj, KOBJ_CHANGE);
                }
        } else {
                /* No changes */
@@ -339,8 +337,9 @@ EXPORT_SYMBOL_GPL(extcon_get_cable_state);
 
 /**
  * extcon_set_cable_state_() - Set the status of a specific cable.
- * @edev:      the extcon device that has the cable.
- * @index:     cable index that can be retrieved by extcon_find_cable_index().
+ * @edev:              the extcon device that has the cable.
+ * @index:             cable index that can be retrieved by
+ *                     extcon_find_cable_index().
  * @cable_state:       the new cable status. The default semantics is
  *                     true: attached / false: detached.
  */
@@ -359,8 +358,8 @@ EXPORT_SYMBOL_GPL(extcon_set_cable_state_);
 
 /**
  * extcon_set_cable_state() - Set the status of a specific cable.
- * @edev:      the extcon device that has the cable.
- * @cable_name:        cable name.
+ * @edev:              the extcon device that has the cable.
+ * @cable_name:                cable name.
  * @cable_state:       the new cable status. The default semantics is
  *                     true: attached / false: detached.
  *
@@ -419,14 +418,14 @@ static int _call_per_cable(struct notifier_block *nb, unsigned long val,
 
 /**
  * extcon_register_interest() - Register a notifier for a state change of a
- *                           specific cable, not an entier set of cables of a
- *                           extcon device.
- * @obj:       an empty extcon_specific_cable_nb object to be returned.
+ *                             specific cable, not an entier set of cables of a
+ *                             extcon device.
+ * @obj:               an empty extcon_specific_cable_nb object to be returned.
  * @extcon_name:       the name of extcon device.
  *                     if NULL, extcon_register_interest will register
  *                     every cable with the target cable_name given.
  * @cable_name:                the target cable name.
- * @nb:                the notifier block to get notified.
+ * @nb:                        the notifier block to get notified.
  *
  * Provide an empty extcon_specific_cable_nb. extcon_register_interest() sets
  * the struct for you.
@@ -452,7 +451,8 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,
                if (!obj->edev)
                        return -ENODEV;
 
-               obj->cable_index = extcon_find_cable_index(obj->edev, cable_name);
+               obj->cable_index = extcon_find_cable_index(obj->edev,
+                                                         cable_name);
                if (obj->cable_index < 0)
                        return obj->cable_index;
 
@@ -460,7 +460,8 @@ 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, &obj->internal_nb);
+               return raw_notifier_chain_register(&obj->edev->nh,
+                                                 &obj->internal_nb);
        } else {
                struct class_dev_iter iter;
                struct extcon_dev *extd;
@@ -470,7 +471,7 @@ int extcon_register_interest(struct extcon_specific_cable_nb *obj,
                        return -ENODEV;
                class_dev_iter_init(&iter, extcon_class, NULL, NULL);
                while ((dev = class_dev_iter_next(&iter))) {
-                       extd = (struct extcon_dev *)dev_get_drvdata(dev);
+                       extd = dev_get_drvdata(dev);
 
                        if (extcon_find_cable_index(extd, cable_name) < 0)
                                continue;
@@ -487,7 +488,7 @@ EXPORT_SYMBOL_GPL(extcon_register_interest);
 
 /**
  * extcon_unregister_interest() - Unregister the notifier registered by
- *                             extcon_register_interest().
+ *                               extcon_register_interest().
  * @obj:       the extcon_specific_cable_nb object returned by
  *             extcon_register_interest().
  */
@@ -502,7 +503,7 @@ EXPORT_SYMBOL_GPL(extcon_unregister_interest);
 
 /**
  * extcon_register_notifier() - Register a notifiee to get notified by
- *                           any attach status changes from the extcon.
+ *                             any attach status changes from the extcon.
  * @edev:      the extcon device.
  * @nb:                a notifier block to be registered.
  *
@@ -556,7 +557,6 @@ static int create_extcon_class(void)
 
 static void extcon_dev_release(struct device *dev)
 {
-       kfree(dev);
 }
 
 static const char *muex_name = "mutually_exclusive";
@@ -567,14 +567,13 @@ static void dummy_sysfs_dev_release(struct device *dev)
 /**
  * extcon_dev_register() - Register a new extcon device
  * @edev       : the new extcon device (should be allocated before calling)
- * @dev                : the parent device for this extcon device.
  *
  * Among the members of edev struct, please set the "user initializing data"
  * in any case and set the "optional callbacks" if required. However, please
  * do not set the values of "internal data", which are initialized by
  * this function.
  */
-int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
+int extcon_dev_register(struct extcon_dev *edev)
 {
        int ret, index = 0;
 
@@ -594,19 +593,20 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
        }
 
        if (index > SUPPORTED_CABLE_MAX) {
-               dev_err(edev->dev, "extcon: maximum number of supported cables exceeded.\n");
+               dev_err(&edev->dev, "extcon: maximum number of supported cables exceeded.\n");
                return -EINVAL;
        }
 
-       edev->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
-       if (!edev->dev)
-               return -ENOMEM;
-       edev->dev->parent = dev;
-       edev->dev->class = extcon_class;
-       edev->dev->release = extcon_dev_release;
+       edev->dev.class = extcon_class;
+       edev->dev.release = extcon_dev_release;
 
-       edev->name = edev->name ? edev->name : dev_name(dev);
-       dev_set_name(edev->dev, "%s", edev->name);
+       edev->name = edev->name ? edev->name : dev_name(edev->dev.parent);
+       if (IS_ERR_OR_NULL(edev->name)) {
+               dev_err(&edev->dev,
+                       "extcon device name is null\n");
+               return -EINVAL;
+       }
+       dev_set_name(&edev->dev, "%s", edev->name);
 
        if (edev->max_supported) {
                char buf[10];
@@ -714,7 +714,7 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
                        goto err_alloc_groups;
                }
 
-               edev->extcon_dev_type.name = dev_name(edev->dev);
+               edev->extcon_dev_type.name = dev_name(&edev->dev);
                edev->extcon_dev_type.release = dummy_sysfs_dev_release;
 
                for (index = 0; index < edev->max_supported; index++)
@@ -724,25 +724,24 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
                        edev->extcon_dev_type.groups[index] =
                                &edev->attr_g_muex;
 
-               edev->dev->type = &edev->extcon_dev_type;
+               edev->dev.type = &edev->extcon_dev_type;
        }
 
-       ret = device_register(edev->dev);
+       ret = device_register(&edev->dev);
        if (ret) {
-               put_device(edev->dev);
+               put_device(&edev->dev);
                goto err_dev;
        }
 #if defined(CONFIG_ANDROID)
        if (switch_class)
-               ret = class_compat_create_link(switch_class, edev->dev,
-                                              NULL);
+               ret = class_compat_create_link(switch_class, &edev->dev, NULL);
 #endif /* CONFIG_ANDROID */
 
        spin_lock_init(&edev->lock);
 
        RAW_INIT_NOTIFIER_HEAD(&edev->nh);
 
-       dev_set_drvdata(edev->dev, edev);
+       dev_set_drvdata(&edev->dev, edev);
        edev->state = 0;
 
        mutex_lock(&extcon_dev_list_lock);
@@ -768,7 +767,6 @@ err_alloc_cables:
        if (edev->max_supported)
                kfree(edev->cables);
 err_sysfs_alloc:
-       kfree(edev->dev);
        return ret;
 }
 EXPORT_SYMBOL_GPL(extcon_dev_register);
@@ -788,9 +786,9 @@ void extcon_dev_unregister(struct extcon_dev *edev)
        list_del(&edev->entry);
        mutex_unlock(&extcon_dev_list_lock);
 
-       if (IS_ERR_OR_NULL(get_device(edev->dev))) {
-               dev_err(edev->dev, "Failed to unregister extcon_dev (%s)\n",
-                               dev_name(edev->dev));
+       if (IS_ERR_OR_NULL(get_device(&edev->dev))) {
+               dev_err(&edev->dev, "Failed to unregister extcon_dev (%s)\n",
+                               dev_name(&edev->dev));
                return;
        }
 
@@ -812,10 +810,10 @@ void extcon_dev_unregister(struct extcon_dev *edev)
 
 #if defined(CONFIG_ANDROID)
        if (switch_class)
-               class_compat_remove_link(switch_class, edev->dev, NULL);
+               class_compat_remove_link(switch_class, &edev->dev, NULL);
 #endif
-       device_unregister(edev->dev);
-       put_device(edev->dev);
+       device_unregister(&edev->dev);
+       put_device(&edev->dev);
 }
 EXPORT_SYMBOL_GPL(extcon_dev_unregister);
 
index f874c30ddbff0a0bb51949d7aa2de90c67c7227b..7e0dff58e4943e8b1b09a44c4adba854898cda9d 100644 (file)
@@ -34,6 +34,7 @@
 struct gpio_extcon_data {
        struct extcon_dev edev;
        unsigned gpio;
+       bool gpio_active_low;
        const char *state_on;
        const char *state_off;
        int irq;
@@ -49,6 +50,8 @@ static void gpio_extcon_work(struct work_struct *work)
                             work);
 
        state = gpio_get_value(data->gpio);
+       if (data->gpio_active_low)
+               state = !state;
        extcon_set_state(&data->edev, state);
 }
 
@@ -78,9 +81,9 @@ static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)
 
 static int gpio_extcon_probe(struct platform_device *pdev)
 {
-       struct gpio_extcon_platform_data *pdata = pdev->dev.platform_data;
+       struct gpio_extcon_platform_data *pdata = dev_get_platdata(&pdev->dev);
        struct gpio_extcon_data *extcon_data;
-       int ret = 0;
+       int ret;
 
        if (!pdata)
                return -EBUSY;
@@ -95,14 +98,22 @@ static int gpio_extcon_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        extcon_data->edev.name = pdata->name;
+       extcon_data->edev.dev.parent = &pdev->dev;
        extcon_data->gpio = pdata->gpio;
+       extcon_data->gpio_active_low = pdata->gpio_active_low;
        extcon_data->state_on = pdata->state_on;
        extcon_data->state_off = pdata->state_off;
        if (pdata->state_on && pdata->state_off)
                extcon_data->edev.print_state = extcon_gpio_print_state;
-       extcon_data->debounce_jiffies = msecs_to_jiffies(pdata->debounce);
+       if (pdata->debounce) {
+               ret = gpio_set_debounce(extcon_data->gpio,
+                                       pdata->debounce * 1000);
+               if (ret < 0)
+                       extcon_data->debounce_jiffies =
+                               msecs_to_jiffies(pdata->debounce);
+       }
 
-       ret = extcon_dev_register(&extcon_data->edev, &pdev->dev);
+       ret = extcon_dev_register(&extcon_data->edev);
        if (ret < 0)
                return ret;
 
index b56bdaa27d4ba15464c645184b2c18ee2df7e530..da268fbc901beae35c56c8da9930f0ddca5dc0dd 100644 (file)
@@ -189,14 +189,17 @@ enum max77693_muic_acc_type {
 
        /* The below accessories have same ADC value so ADCLow and
           ADC1K bit is used to separate specific accessory */
-       MAX77693_MUIC_GND_USB_OTG = 0x100,      /* ADC:0x0, VBVolot:0, ADCLow:0, ADC1K:0 */
-       MAX77693_MUIC_GND_USB_OTG_VB = 0x104,   /* ADC:0x0, VBVolot:1, ADCLow:0, ADC1K:0 */
-       MAX77693_MUIC_GND_AV_CABLE_LOAD = 0x102,/* ADC:0x0, VBVolot:0, ADCLow:1, ADC1K:0 */
-       MAX77693_MUIC_GND_MHL = 0x103,          /* ADC:0x0, VBVolot:0, ADCLow:1, ADC1K:1 */
-       MAX77693_MUIC_GND_MHL_VB = 0x107,       /* ADC:0x0, VBVolot:1, ADCLow:1, ADC1K:1 */
+                                               /* 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_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| */
 };
 
-/* MAX77693 MUIC device support below list of accessories(external connector) */
+/*
+ * MAX77693 MUIC device support below list of accessories(external connector)
+ */
 enum {
        EXTCON_CABLE_USB = 0,
        EXTCON_CABLE_USB_HOST,
@@ -395,12 +398,12 @@ static int max77693_muic_get_cable_type(struct max77693_muic_info *info,
                        vbvolt >>= STATUS2_VBVOLT_SHIFT;
 
                        /**
-                        * [0x1][VBVolt][ADCLow][ADC1K]
-                        * [0x1    0       0       0  ] : USB_OTG
-                        * [0x1    1       0       0  ] : USB_OTG_VB
-                        * [0x1    0       1       0  ] : Audio Video Cable with load
-                        * [0x1    0       1       1  ] : MHL without charging connector
-                        * [0x1    1       1       1  ] : MHL with charging connector
+                        * [0x1|VBVolt|ADCLow|ADC1K]
+                        * [0x1|     0|     0|    0] USB_OTG
+                        * [0x1|     1|     0|    0] USB_OTG_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
                         */
                        cable_type = ((0x1 << 8)
                                        | (vbvolt << 2)
@@ -723,11 +726,11 @@ static int max77693_muic_adc_handler(struct max77693_muic_info *info)
                if (ret < 0)
                        return ret;
                break;
-       case MAX77693_MUIC_ADC_REMOTE_S3_BUTTON:        /* DOCK_KEY_PREV */
-       case MAX77693_MUIC_ADC_REMOTE_S7_BUTTON:        /* DOCK_KEY_NEXT */
-       case MAX77693_MUIC_ADC_REMOTE_S9_BUTTON:        /* DOCK_VOL_DOWN */
-       case MAX77693_MUIC_ADC_REMOTE_S10_BUTTON:       /* DOCK_VOL_UP */
-       case MAX77693_MUIC_ADC_REMOTE_S12_BUTTON:       /* DOCK_KEY_PLAY_PAUSE */
+       case MAX77693_MUIC_ADC_REMOTE_S3_BUTTON:      /* DOCK_KEY_PREV */
+       case MAX77693_MUIC_ADC_REMOTE_S7_BUTTON:      /* DOCK_KEY_NEXT */
+       case MAX77693_MUIC_ADC_REMOTE_S9_BUTTON:      /* DOCK_VOL_DOWN */
+       case MAX77693_MUIC_ADC_REMOTE_S10_BUTTON:     /* DOCK_VOL_UP */
+       case MAX77693_MUIC_ADC_REMOTE_S12_BUTTON:     /* DOCK_KEY_PLAY_PAUSE */
                /*
                 * Button of DOCK device
                 * - the Prev/Next/Volume Up/Volume Down/Play-Pause button
@@ -815,19 +818,21 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
                case MAX77693_MUIC_GND_MHL_VB:
                        /*
                         * 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 driver notify state to notifiee
-                        * for charging battery.
+                        * - 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
+                        * driver notify state to notifiee for charging battery.
                         *
                         * Features of 'MHL_TA(USB/TA) with MHL cable'
                         * - Support MHL
-                        * - Support charging through micro-usb port without data connection
+                        * - Support charging through micro-usb port without
+                        *   data connection
                         */
                        extcon_set_cable_state(info->edev, "MHL_TA", attached);
                        if (!cable_attached)
-                               extcon_set_cable_state(info->edev, "MHL", cable_attached);
+                               extcon_set_cable_state(info->edev,
+                                                     "MHL", cable_attached);
                        break;
                }
 
@@ -839,47 +844,51 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
                case MAX77693_MUIC_ADC_AV_CABLE_NOLOAD:         /* Dock-Audio */
                        /*
                         * Dock-Audio device with USB/TA cable
-                        * - Dock device include two port(Dock-Audio and micro-usb
-                        * port). When the target connect Dock-Audio device, extcon
-                        * driver check whether USB/TA cable is connected. If USB/TA
-                        * cable is connected, extcon driver notify state to notifiee
-                        * for charging battery.
+                        * - Dock device include two port(Dock-Audio and micro-
+                        * usb port). When the target connect Dock-Audio device,
+                        * extcon driver check whether USB/TA cable is connected
+                        * or not. If USB/TA cable is connected, extcon driver
+                        * notify state to notifiee for charging battery.
                         *
                         * Features of 'USB/TA cable with Dock-Audio device'
                         * - Support external output feature of audio.
-                        * - Support charging through micro-usb port without data
-                        *           connection.
+                        * - Support charging through micro-usb port without
+                        *   data connection.
                         */
                        extcon_set_cable_state(info->edev, "USB", attached);
 
                        if (!cable_attached)
-                               extcon_set_cable_state(info->edev, "Dock-Audio", cable_attached);
+                               extcon_set_cable_state(info->edev, "Dock-Audio",
+                                                     cable_attached);
                        break;
                case MAX77693_MUIC_ADC_RESERVED_ACC_3:          /* Dock-Smart */
                        /*
                         * Dock-Smart device with USB/TA cable
                         * - Dock-Desk device include three type of cable which
                         * are HDMI, USB for mouse/keyboard and micro-usb port
-                        * for USB/TA cable. Dock-Smart device need always exteranl
-                        * power supply(USB/TA cable through micro-usb cable). Dock-
-                        * Smart device support screen output of target to separate
-                        * monitor and mouse/keyboard for desktop mode.
+                        * for USB/TA cable. Dock-Smart device need always
+                        * exteranl power supply(USB/TA cable through micro-usb
+                        * cable). Dock-Smart device support screen output of
+                        * target to separate monitor and mouse/keyboard for
+                        * desktop mode.
                         *
                         * Features of 'USB/TA cable with Dock-Smart device'
                         * - Support MHL
                         * - Support external output feature of audio
-                        * - Support charging through micro-usb port without data
-                        *           connection if TA cable is connected to target.
-                        * - Support charging and data connection through micro-usb port
-                        *           if USB cable is connected between target and host
-                        *           device.
+                        * - Support charging through micro-usb port without
+                        *   data connection if TA cable is connected to target.
+                        * - Support charging and data connection through micro-
+                        *   usb port if USB cable is connected between target
+                        *   and host device
                         * - Support OTG device (Mouse/Keyboard)
                         */
-                       ret = max77693_muic_set_path(info, info->path_usb, attached);
+                       ret = max77693_muic_set_path(info, info->path_usb,
+                                                   attached);
                        if (ret < 0)
                                return ret;
 
-                       extcon_set_cable_state(info->edev, "Dock-Smart", attached);
+                       extcon_set_cable_state(info->edev, "Dock-Smart",
+                                             attached);
                        extcon_set_cable_state(info->edev, "MHL", attached);
 
                        break;
@@ -889,25 +898,28 @@ static int max77693_muic_chg_handler(struct max77693_muic_info *info)
                switch (chg_type) {
                case MAX77693_CHARGER_TYPE_NONE:
                        /*
-                        * When MHL(with USB/TA cable) or Dock-Audio with USB/TA cable
-                        * is attached, muic device happen below two interrupt.
-                        * - 'MAX77693_MUIC_IRQ_INT1_ADC' for detecting MHL/Dock-Audio.
-                        * - 'MAX77693_MUIC_IRQ_INT2_CHGTYP' for detecting USB/TA cable
-                        *   connected to MHL or Dock-Audio.
-                        * Always, happen eariler MAX77693_MUIC_IRQ_INT1_ADC interrupt
-                        * than MAX77693_MUIC_IRQ_INT2_CHGTYP interrupt.
+                        * When MHL(with USB/TA cable) or Dock-Audio with USB/TA
+                        * cable is attached, muic device happen below two irq.
+                        * - 'MAX77693_MUIC_IRQ_INT1_ADC' for detecting
+                        *    MHL/Dock-Audio.
+                        * - 'MAX77693_MUIC_IRQ_INT2_CHGTYP' for detecting
+                        *    USB/TA cable connected to MHL or Dock-Audio.
+                        * Always, happen eariler MAX77693_MUIC_IRQ_INT1_ADC
+                        * irq than MAX77693_MUIC_IRQ_INT2_CHGTYP irq.
                         *
-                        * If user attach MHL (with USB/TA cable and immediately detach
-                        * MHL with USB/TA cable before MAX77693_MUIC_IRQ_INT2_CHGTYP
-                        * interrupt is happened, USB/TA cable remain connected state to
-                        * target. But USB/TA cable isn't connected to target. The user
-                        * be face with unusual action. So, driver should check this
-                        * situation in spite of, that previous charger type is N/A.
+                        * If user attach MHL (with USB/TA cable and immediately
+                        * detach MHL with USB/TA cable before MAX77693_MUIC_IRQ
+                        * _INT2_CHGTYP irq is happened, USB/TA cable remain
+                        * connected state to target. But USB/TA cable isn't
+                        * connected to target. The user be face with unusual
+                        * action. So, driver should check this situation in
+                        * spite of, that previous charger type is N/A.
                         */
                        break;
                case MAX77693_CHARGER_TYPE_USB:
                        /* Only USB cable, PATH:AP_USB */
-                       ret = max77693_muic_set_path(info, info->path_usb, attached);
+                       ret = max77693_muic_set_path(info, info->path_usb,
+                                                   attached);
                        if (ret < 0)
                                return ret;
 
@@ -953,7 +965,7 @@ static void max77693_muic_irq_work(struct work_struct *work)
 
        mutex_lock(&info->mutex);
 
-       for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++)
+       for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
                if (info->irq == muic_irqs[i].virq)
                        irq_type = muic_irqs[i].irq;
 
@@ -1171,8 +1183,9 @@ static int max77693_muic_probe(struct platform_device *pdev)
                goto err_irq;
        }
        info->edev->name = DEV_NAME;
+       info->edev->dev.parent = &pdev->dev;
        info->edev->supported_cable = max77693_extcon_cable;
-       ret = extcon_dev_register(info->edev, NULL);
+       ret = extcon_dev_register(info->edev);
        if (ret) {
                dev_err(&pdev->dev, "failed to register extcon device\n");
                goto err_irq;
@@ -1188,7 +1201,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
                num_init_data = ARRAY_SIZE(default_init_data);
        }
 
-       for (i = 0 ; i < num_init_data ; i++) {
+       for (i = 0; i < num_init_data; i++) {
                enum max77693_irq_source irq_src
                                = MAX77693_IRQ_GROUP_NR;
 
@@ -1214,7 +1227,8 @@ static int max77693_muic_probe(struct platform_device *pdev)
        }
 
        if (pdata->muic_data) {
-               struct max77693_muic_platform_data *muic_pdata = pdata->muic_data;
+               struct max77693_muic_platform_data *muic_pdata
+                                                  = pdata->muic_data;
 
                /*
                 * Default usb/uart path whether UART/USB or AUX_UART/AUX_USB
index 67d6738d85a00d514d3d4c6692e969331663abde..6a00464658c50a6fb1aadecdf8e22467bc7963ca 100644 (file)
@@ -426,7 +426,8 @@ static int max8997_muic_adc_handler(struct max8997_muic_info *info)
                break;
        case MAX8997_MUIC_ADC_FACTORY_MODE_USB_OFF:
        case MAX8997_MUIC_ADC_FACTORY_MODE_USB_ON:
-               ret = max8997_muic_handle_usb(info, MAX8997_USB_DEVICE, attached);
+               ret = max8997_muic_handle_usb(info,
+                                            MAX8997_USB_DEVICE, attached);
                if (ret < 0)
                        return ret;
                break;
@@ -504,7 +505,8 @@ static int max8997_muic_chg_handler(struct max8997_muic_info *info)
                }
                break;
        case MAX8997_CHARGER_TYPE_DOWNSTREAM_PORT:
-               extcon_set_cable_state(info->edev, "Charge-downstream", attached);
+               extcon_set_cable_state(info->edev,
+                                     "Charge-downstream", attached);
                break;
        case MAX8997_CHARGER_TYPE_DEDICATED_CHG:
                extcon_set_cable_state(info->edev, "TA", attached);
@@ -537,7 +539,7 @@ static void max8997_muic_irq_work(struct work_struct *work)
 
        mutex_lock(&info->mutex);
 
-       for (i = 0 ; i < ARRAY_SIZE(muic_irqs) ; i++)
+       for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
                if (info->irq == muic_irqs[i].virq)
                        irq_type = muic_irqs[i].irq;
 
@@ -705,8 +707,9 @@ static int max8997_muic_probe(struct platform_device *pdev)
                goto err_irq;
        }
        info->edev->name = DEV_NAME;
+       info->edev->dev.parent = &pdev->dev;
        info->edev->supported_cable = max8997_extcon_cable;
-       ret = extcon_dev_register(info->edev, NULL);
+       ret = extcon_dev_register(info->edev);
        if (ret) {
                dev_err(&pdev->dev, "failed to register extcon device\n");
                goto err_irq;
index 89fdd05c5fd61a39cb75f7c9488f261a9e04412a..6c91976dd82371d63008474cb030aa7e4cfeb507 100644 (file)
@@ -135,7 +135,7 @@ static void palmas_enable_irq(struct palmas_usb *palmas_usb)
 static int palmas_usb_probe(struct platform_device *pdev)
 {
        struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
-       struct palmas_usb_platform_data *pdata = pdev->dev.platform_data;
+       struct palmas_usb_platform_data *pdata = dev_get_platdata(&pdev->dev);
        struct device_node *node = pdev->dev.of_node;
        struct palmas_usb *palmas_usb;
        int status;
@@ -178,9 +178,10 @@ static int palmas_usb_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, palmas_usb);
 
        palmas_usb->edev.supported_cable = palmas_extcon_cable;
+       palmas_usb->edev.dev.parent = palmas_usb->dev;
        palmas_usb->edev.mutually_exclusive = mutually_exclusive;
 
-       status = extcon_dev_register(&palmas_usb->edev, palmas_usb->dev);
+       status = extcon_dev_register(&palmas_usb->edev);
        if (status) {
                dev_err(&pdev->dev, "failed to register extcon device\n");
                return status;
index 6de6c98ce6eb73636d06123024e9f84324947f59..cea623c36ae23cc007138371b14ef05e770fe6d0 100644 (file)
@@ -47,8 +47,8 @@ static void vmbus_setevent(struct vmbus_channel *channel)
                        (unsigned long *) vmbus_connection.send_int_page +
                        (channel->offermsg.child_relid >> 5));
 
-               monitorpage = vmbus_connection.monitor_pages;
-               monitorpage++; /* Get the child to parent monitor page */
+               /* Get the child to parent monitor page */
+               monitorpage = vmbus_connection.monitor_pages[1];
 
                sync_set_bit(channel->monitor_bit,
                        (unsigned long *)&monitorpage->trigger_group
@@ -59,50 +59,6 @@ static void vmbus_setevent(struct vmbus_channel *channel)
        }
 }
 
-/*
- * vmbus_get_debug_info -Retrieve various channel debug info
- */
-void vmbus_get_debug_info(struct vmbus_channel *channel,
-                             struct vmbus_channel_debug_info *debuginfo)
-{
-       struct hv_monitor_page *monitorpage;
-       u8 monitor_group = (u8)channel->offermsg.monitorid / 32;
-       u8 monitor_offset = (u8)channel->offermsg.monitorid % 32;
-
-       debuginfo->relid = channel->offermsg.child_relid;
-       debuginfo->state = channel->state;
-       memcpy(&debuginfo->interfacetype,
-              &channel->offermsg.offer.if_type, sizeof(uuid_le));
-       memcpy(&debuginfo->interface_instance,
-              &channel->offermsg.offer.if_instance,
-              sizeof(uuid_le));
-
-       monitorpage = (struct hv_monitor_page *)vmbus_connection.monitor_pages;
-
-       debuginfo->monitorid = channel->offermsg.monitorid;
-
-       debuginfo->servermonitor_pending =
-                       monitorpage->trigger_group[monitor_group].pending;
-       debuginfo->servermonitor_latency =
-                       monitorpage->latency[monitor_group][monitor_offset];
-       debuginfo->servermonitor_connectionid =
-                       monitorpage->parameter[monitor_group]
-                                       [monitor_offset].connectionid.u.id;
-
-       monitorpage++;
-
-       debuginfo->clientmonitor_pending =
-                       monitorpage->trigger_group[monitor_group].pending;
-       debuginfo->clientmonitor_latency =
-                       monitorpage->latency[monitor_group][monitor_offset];
-       debuginfo->clientmonitor_connectionid =
-                       monitorpage->parameter[monitor_group]
-                                       [monitor_offset].connectionid.u.id;
-
-       hv_ringbuffer_get_debuginfo(&channel->inbound, &debuginfo->inbound);
-       hv_ringbuffer_get_debuginfo(&channel->outbound, &debuginfo->outbound);
-}
-
 /*
  * vmbus_open - Open the specified channel.
  */
@@ -855,6 +811,6 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer,
        if (signal)
                vmbus_setevent(channel);
 
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(vmbus_recvpacket_raw);
index bbff5f200bef7a7b070abec6c0f7c202f335159c..fa920469bf104fda2e57d9c2bce2f15f90d5b2a4 100644 (file)
@@ -203,7 +203,8 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
        struct vmbus_channel *primary_channel;
        struct vmbus_channel_relid_released msg;
 
-       vmbus_device_unregister(channel->device_obj);
+       if (channel->device_obj)
+               vmbus_device_unregister(channel->device_obj);
        memset(&msg, 0, sizeof(struct vmbus_channel_relid_released));
        msg.child_relid = channel->offermsg.child_relid;
        msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
@@ -216,7 +217,7 @@ static void vmbus_process_rescind_offer(struct work_struct *work)
        } else {
                primary_channel = channel->primary_channel;
                spin_lock_irqsave(&primary_channel->sc_lock, flags);
-               list_del(&channel->listentry);
+               list_del(&channel->sc_list);
                spin_unlock_irqrestore(&primary_channel->sc_lock, flags);
        }
        free_channel(channel);
index 936093e0271e3c7fffccce7fe0e688c4d6f3ad01..af6edf9b19365a4938ca16f6a913a08cc6a35fa7 100644 (file)
@@ -76,10 +76,8 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
        msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT;
        msg->vmbus_version_requested = version;
        msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
-       msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages);
-       msg->monitor_page2 = virt_to_phys(
-                       (void *)((unsigned long)vmbus_connection.monitor_pages +
-                                PAGE_SIZE));
+       msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages[0]);
+       msg->monitor_page2 = virt_to_phys(vmbus_connection.monitor_pages[1]);
 
        /*
         * Add to list before we send the request since we may
@@ -169,9 +167,10 @@ int vmbus_connect(void)
         * Setup the monitor notification facility. The 1st page for
         * parent->child and the 2nd page for child->parent
         */
-       vmbus_connection.monitor_pages =
-       (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 1);
-       if (vmbus_connection.monitor_pages == NULL) {
+       vmbus_connection.monitor_pages[0] = (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 0);
+       vmbus_connection.monitor_pages[1] = (void *)__get_free_pages((GFP_KERNEL|__GFP_ZERO), 0);
+       if ((vmbus_connection.monitor_pages[0] == NULL) ||
+           (vmbus_connection.monitor_pages[1] == NULL)) {
                ret = -ENOMEM;
                goto cleanup;
        }
@@ -229,10 +228,10 @@ cleanup:
                vmbus_connection.int_page = NULL;
        }
 
-       if (vmbus_connection.monitor_pages) {
-               free_pages((unsigned long)vmbus_connection.monitor_pages, 1);
-               vmbus_connection.monitor_pages = NULL;
-       }
+       free_pages((unsigned long)vmbus_connection.monitor_pages[0], 1);
+       free_pages((unsigned long)vmbus_connection.monitor_pages[1], 1);
+       vmbus_connection.monitor_pages[0] = NULL;
+       vmbus_connection.monitor_pages[1] = NULL;
 
        kfree(msginfo);
 
index 88f4096fa078d7f4e2ae1eeee41fd7188ab1a313..f0c5e07c25ec9f88d4bc0cee66a990da77f3636e 100644 (file)
@@ -304,7 +304,7 @@ err:
 void hv_synic_free_cpu(int cpu)
 {
        kfree(hv_context.event_dpc[cpu]);
-       if (hv_context.synic_message_page[cpu])
+       if (hv_context.synic_event_page[cpu])
                free_page((unsigned long)hv_context.synic_event_page[cpu]);
        if (hv_context.synic_message_page[cpu])
                free_page((unsigned long)hv_context.synic_message_page[cpu]);
index 273e3ddb3a20b00c5d1ae03bab6368c5fcfbc26f..62dfd246b94838e622c02bd125650a79a21c3f32 100644 (file)
@@ -97,7 +97,7 @@ static void shutdown_onchannelcallback(void *context)
        struct vmbus_channel *channel = context;
        u32 recvlen;
        u64 requestid;
-       u8  execute_shutdown = false;
+       bool execute_shutdown = false;
        u8  *shut_txf_buf = util_shutdown.recv_buffer;
 
        struct shutdown_msg_data *shutdown_msg;
index d84918fe19ab7023e4189b0cb52d98495340abd1..e05517616a06e549407f422a4713145b0f44e1de 100644 (file)
@@ -514,6 +514,13 @@ struct hv_context {
 
 extern struct hv_context hv_context;
 
+struct hv_ring_buffer_debug_info {
+       u32 current_interrupt_mask;
+       u32 current_read_index;
+       u32 current_write_index;
+       u32 bytes_avail_toread;
+       u32 bytes_avail_towrite;
+};
 
 /* Hv Interface */
 
@@ -612,7 +619,7 @@ struct vmbus_connection {
         * 2 pages - 1st page for parent->child notification and 2nd
         * is child->parent notification
         */
-       void *monitor_pages;
+       struct hv_monitor_page *monitor_pages[2];
        struct list_head chn_msg_list;
        spinlock_t channelmsg_lock;
 
index f9fe46f52cfa35d3d006260ed6a082f41466ef58..48aad4faea068e88cbe61976aa3444d9c8a80a58 100644 (file)
@@ -46,24 +46,6 @@ static struct tasklet_struct msg_dpc;
 static struct completion probe_event;
 static int irq;
 
-struct hv_device_info {
-       u32 chn_id;
-       u32 chn_state;
-       uuid_le chn_type;
-       uuid_le chn_instance;
-
-       u32 monitor_id;
-       u32 server_monitor_pending;
-       u32 server_monitor_latency;
-       u32 server_monitor_conn_id;
-       u32 client_monitor_pending;
-       u32 client_monitor_latency;
-       u32 client_monitor_conn_id;
-
-       struct hv_dev_port_info inbound;
-       struct hv_dev_port_info outbound;
-};
-
 static int vmbus_exists(void)
 {
        if (hv_acpi_dev == NULL)
@@ -72,169 +54,361 @@ static int vmbus_exists(void)
        return 0;
 }
 
+#define VMBUS_ALIAS_LEN ((sizeof((struct hv_vmbus_device_id *)0)->guid) * 2)
+static void print_alias_name(struct hv_device *hv_dev, char *alias_name)
+{
+       int i;
+       for (i = 0; i < VMBUS_ALIAS_LEN; i += 2)
+               sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]);
+}
 
-static void get_channel_info(struct hv_device *device,
-                            struct hv_device_info *info)
+static u8 channel_monitor_group(struct vmbus_channel *channel)
 {
-       struct vmbus_channel_debug_info debug_info;
+       return (u8)channel->offermsg.monitorid / 32;
+}
 
-       if (!device->channel)
-               return;
+static u8 channel_monitor_offset(struct vmbus_channel *channel)
+{
+       return (u8)channel->offermsg.monitorid % 32;
+}
 
-       vmbus_get_debug_info(device->channel, &debug_info);
+static u32 channel_pending(struct vmbus_channel *channel,
+                          struct hv_monitor_page *monitor_page)
+{
+       u8 monitor_group = channel_monitor_group(channel);
+       return monitor_page->trigger_group[monitor_group].pending;
+}
 
-       info->chn_id = debug_info.relid;
-       info->chn_state = debug_info.state;
-       memcpy(&info->chn_type, &debug_info.interfacetype,
-              sizeof(uuid_le));
-       memcpy(&info->chn_instance, &debug_info.interface_instance,
-              sizeof(uuid_le));
+static u32 channel_latency(struct vmbus_channel *channel,
+                          struct hv_monitor_page *monitor_page)
+{
+       u8 monitor_group = channel_monitor_group(channel);
+       u8 monitor_offset = channel_monitor_offset(channel);
+       return monitor_page->latency[monitor_group][monitor_offset];
+}
 
-       info->monitor_id = debug_info.monitorid;
+static u32 channel_conn_id(struct vmbus_channel *channel,
+                          struct hv_monitor_page *monitor_page)
+{
+       u8 monitor_group = channel_monitor_group(channel);
+       u8 monitor_offset = channel_monitor_offset(channel);
+       return monitor_page->parameter[monitor_group][monitor_offset].connectionid.u.id;
+}
 
-       info->server_monitor_pending = debug_info.servermonitor_pending;
-       info->server_monitor_latency = debug_info.servermonitor_latency;
-       info->server_monitor_conn_id = debug_info.servermonitor_connectionid;
+static ssize_t id_show(struct device *dev, struct device_attribute *dev_attr,
+                      char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
 
-       info->client_monitor_pending = debug_info.clientmonitor_pending;
-       info->client_monitor_latency = debug_info.clientmonitor_latency;
-       info->client_monitor_conn_id = debug_info.clientmonitor_connectionid;
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n", hv_dev->channel->offermsg.child_relid);
+}
+static DEVICE_ATTR_RO(id);
 
-       info->inbound.int_mask = debug_info.inbound.current_interrupt_mask;
-       info->inbound.read_idx = debug_info.inbound.current_read_index;
-       info->inbound.write_idx = debug_info.inbound.current_write_index;
-       info->inbound.bytes_avail_toread =
-               debug_info.inbound.bytes_avail_toread;
-       info->inbound.bytes_avail_towrite =
-               debug_info.inbound.bytes_avail_towrite;
+static ssize_t state_show(struct device *dev, struct device_attribute *dev_attr,
+                         char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
 
-       info->outbound.int_mask =
-               debug_info.outbound.current_interrupt_mask;
-       info->outbound.read_idx = debug_info.outbound.current_read_index;
-       info->outbound.write_idx = debug_info.outbound.current_write_index;
-       info->outbound.bytes_avail_toread =
-               debug_info.outbound.bytes_avail_toread;
-       info->outbound.bytes_avail_towrite =
-               debug_info.outbound.bytes_avail_towrite;
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n", hv_dev->channel->state);
 }
+static DEVICE_ATTR_RO(state);
 
-#define VMBUS_ALIAS_LEN ((sizeof((struct hv_vmbus_device_id *)0)->guid) * 2)
-static void print_alias_name(struct hv_device *hv_dev, char *alias_name)
+static ssize_t monitor_id_show(struct device *dev,
+                              struct device_attribute *dev_attr, char *buf)
 {
-       int i;
-       for (i = 0; i < VMBUS_ALIAS_LEN; i += 2)
-               sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]);
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n", hv_dev->channel->offermsg.monitorid);
 }
+static DEVICE_ATTR_RO(monitor_id);
 
-/*
- * vmbus_show_device_attr - Show the device attribute in sysfs.
- *
- * This is invoked when user does a
- * "cat /sys/bus/vmbus/devices/<busdevice>/<attr name>"
- */
-static ssize_t vmbus_show_device_attr(struct device *dev,
-                                     struct device_attribute *dev_attr,
-                                     char *buf)
+static ssize_t class_id_show(struct device *dev,
+                              struct device_attribute *dev_attr, char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "{%pUl}\n",
+                      hv_dev->channel->offermsg.offer.if_type.b);
+}
+static DEVICE_ATTR_RO(class_id);
+
+static ssize_t device_id_show(struct device *dev,
+                             struct device_attribute *dev_attr, char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "{%pUl}\n",
+                      hv_dev->channel->offermsg.offer.if_instance.b);
+}
+static DEVICE_ATTR_RO(device_id);
+
+static ssize_t modalias_show(struct device *dev,
+                            struct device_attribute *dev_attr, char *buf)
 {
        struct hv_device *hv_dev = device_to_hv_device(dev);
-       struct hv_device_info *device_info;
        char alias_name[VMBUS_ALIAS_LEN + 1];
-       int ret = 0;
 
-       device_info = kzalloc(sizeof(struct hv_device_info), GFP_KERNEL);
-       if (!device_info)
-               return ret;
+       print_alias_name(hv_dev, alias_name);
+       return sprintf(buf, "vmbus:%s\n", alias_name);
+}
+static DEVICE_ATTR_RO(modalias);
 
-       get_channel_info(hv_dev, device_info);
-
-       if (!strcmp(dev_attr->attr.name, "class_id")) {
-               ret = sprintf(buf, "{%pUl}\n", device_info->chn_type.b);
-       } else if (!strcmp(dev_attr->attr.name, "device_id")) {
-               ret = sprintf(buf, "{%pUl}\n", device_info->chn_instance.b);
-       } else if (!strcmp(dev_attr->attr.name, "modalias")) {
-               print_alias_name(hv_dev, alias_name);
-               ret = sprintf(buf, "vmbus:%s\n", alias_name);
-       } else if (!strcmp(dev_attr->attr.name, "state")) {
-               ret = sprintf(buf, "%d\n", device_info->chn_state);
-       } else if (!strcmp(dev_attr->attr.name, "id")) {
-               ret = sprintf(buf, "%d\n", device_info->chn_id);
-       } else if (!strcmp(dev_attr->attr.name, "out_intr_mask")) {
-               ret = sprintf(buf, "%d\n", device_info->outbound.int_mask);
-       } else if (!strcmp(dev_attr->attr.name, "out_read_index")) {
-               ret = sprintf(buf, "%d\n", device_info->outbound.read_idx);
-       } else if (!strcmp(dev_attr->attr.name, "out_write_index")) {
-               ret = sprintf(buf, "%d\n", device_info->outbound.write_idx);
-       } else if (!strcmp(dev_attr->attr.name, "out_read_bytes_avail")) {
-               ret = sprintf(buf, "%d\n",
-                              device_info->outbound.bytes_avail_toread);
-       } else if (!strcmp(dev_attr->attr.name, "out_write_bytes_avail")) {
-               ret = sprintf(buf, "%d\n",
-                              device_info->outbound.bytes_avail_towrite);
-       } else if (!strcmp(dev_attr->attr.name, "in_intr_mask")) {
-               ret = sprintf(buf, "%d\n", device_info->inbound.int_mask);
-       } else if (!strcmp(dev_attr->attr.name, "in_read_index")) {
-               ret = sprintf(buf, "%d\n", device_info->inbound.read_idx);
-       } else if (!strcmp(dev_attr->attr.name, "in_write_index")) {
-               ret = sprintf(buf, "%d\n", device_info->inbound.write_idx);
-       } else if (!strcmp(dev_attr->attr.name, "in_read_bytes_avail")) {
-               ret = sprintf(buf, "%d\n",
-                              device_info->inbound.bytes_avail_toread);
-       } else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail")) {
-               ret = sprintf(buf, "%d\n",
-                              device_info->inbound.bytes_avail_towrite);
-       } else if (!strcmp(dev_attr->attr.name, "monitor_id")) {
-               ret = sprintf(buf, "%d\n", device_info->monitor_id);
-       } else if (!strcmp(dev_attr->attr.name, "server_monitor_pending")) {
-               ret = sprintf(buf, "%d\n", device_info->server_monitor_pending);
-       } else if (!strcmp(dev_attr->attr.name, "server_monitor_latency")) {
-               ret = sprintf(buf, "%d\n", device_info->server_monitor_latency);
-       } else if (!strcmp(dev_attr->attr.name, "server_monitor_conn_id")) {
-               ret = sprintf(buf, "%d\n",
-                              device_info->server_monitor_conn_id);
-       } else if (!strcmp(dev_attr->attr.name, "client_monitor_pending")) {
-               ret = sprintf(buf, "%d\n", device_info->client_monitor_pending);
-       } else if (!strcmp(dev_attr->attr.name, "client_monitor_latency")) {
-               ret = sprintf(buf, "%d\n", device_info->client_monitor_latency);
-       } else if (!strcmp(dev_attr->attr.name, "client_monitor_conn_id")) {
-               ret = sprintf(buf, "%d\n",
-                              device_info->client_monitor_conn_id);
-       }
+static ssize_t server_monitor_pending_show(struct device *dev,
+                                          struct device_attribute *dev_attr,
+                                          char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
 
-       kfree(device_info);
-       return ret;
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n",
+                      channel_pending(hv_dev->channel,
+                                      vmbus_connection.monitor_pages[1]));
+}
+static DEVICE_ATTR_RO(server_monitor_pending);
+
+static ssize_t client_monitor_pending_show(struct device *dev,
+                                          struct device_attribute *dev_attr,
+                                          char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n",
+                      channel_pending(hv_dev->channel,
+                                      vmbus_connection.monitor_pages[1]));
+}
+static DEVICE_ATTR_RO(client_monitor_pending);
+
+static ssize_t server_monitor_latency_show(struct device *dev,
+                                          struct device_attribute *dev_attr,
+                                          char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n",
+                      channel_latency(hv_dev->channel,
+                                      vmbus_connection.monitor_pages[0]));
+}
+static DEVICE_ATTR_RO(server_monitor_latency);
+
+static ssize_t client_monitor_latency_show(struct device *dev,
+                                          struct device_attribute *dev_attr,
+                                          char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n",
+                      channel_latency(hv_dev->channel,
+                                      vmbus_connection.monitor_pages[1]));
+}
+static DEVICE_ATTR_RO(client_monitor_latency);
+
+static ssize_t server_monitor_conn_id_show(struct device *dev,
+                                          struct device_attribute *dev_attr,
+                                          char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n",
+                      channel_conn_id(hv_dev->channel,
+                                      vmbus_connection.monitor_pages[0]));
+}
+static DEVICE_ATTR_RO(server_monitor_conn_id);
+
+static ssize_t client_monitor_conn_id_show(struct device *dev,
+                                          struct device_attribute *dev_attr,
+                                          char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       return sprintf(buf, "%d\n",
+                      channel_conn_id(hv_dev->channel,
+                                      vmbus_connection.monitor_pages[1]));
+}
+static DEVICE_ATTR_RO(client_monitor_conn_id);
+
+static ssize_t out_intr_mask_show(struct device *dev,
+                                 struct device_attribute *dev_attr, char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info outbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound);
+       return sprintf(buf, "%d\n", outbound.current_interrupt_mask);
+}
+static DEVICE_ATTR_RO(out_intr_mask);
+
+static ssize_t out_read_index_show(struct device *dev,
+                                  struct device_attribute *dev_attr, char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info outbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound);
+       return sprintf(buf, "%d\n", outbound.current_read_index);
+}
+static DEVICE_ATTR_RO(out_read_index);
+
+static ssize_t out_write_index_show(struct device *dev,
+                                   struct device_attribute *dev_attr,
+                                   char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info outbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound);
+       return sprintf(buf, "%d\n", outbound.current_write_index);
+}
+static DEVICE_ATTR_RO(out_write_index);
+
+static ssize_t out_read_bytes_avail_show(struct device *dev,
+                                        struct device_attribute *dev_attr,
+                                        char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info outbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound);
+       return sprintf(buf, "%d\n", outbound.bytes_avail_toread);
 }
+static DEVICE_ATTR_RO(out_read_bytes_avail);
+
+static ssize_t out_write_bytes_avail_show(struct device *dev,
+                                         struct device_attribute *dev_attr,
+                                         char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info outbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->outbound, &outbound);
+       return sprintf(buf, "%d\n", outbound.bytes_avail_towrite);
+}
+static DEVICE_ATTR_RO(out_write_bytes_avail);
+
+static ssize_t in_intr_mask_show(struct device *dev,
+                                struct device_attribute *dev_attr, char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info inbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound);
+       return sprintf(buf, "%d\n", inbound.current_interrupt_mask);
+}
+static DEVICE_ATTR_RO(in_intr_mask);
+
+static ssize_t in_read_index_show(struct device *dev,
+                                 struct device_attribute *dev_attr, char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info inbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound);
+       return sprintf(buf, "%d\n", inbound.current_read_index);
+}
+static DEVICE_ATTR_RO(in_read_index);
+
+static ssize_t in_write_index_show(struct device *dev,
+                                  struct device_attribute *dev_attr, char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info inbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound);
+       return sprintf(buf, "%d\n", inbound.current_write_index);
+}
+static DEVICE_ATTR_RO(in_write_index);
+
+static ssize_t in_read_bytes_avail_show(struct device *dev,
+                                       struct device_attribute *dev_attr,
+                                       char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info inbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound);
+       return sprintf(buf, "%d\n", inbound.bytes_avail_toread);
+}
+static DEVICE_ATTR_RO(in_read_bytes_avail);
+
+static ssize_t in_write_bytes_avail_show(struct device *dev,
+                                        struct device_attribute *dev_attr,
+                                        char *buf)
+{
+       struct hv_device *hv_dev = device_to_hv_device(dev);
+       struct hv_ring_buffer_debug_info inbound;
+
+       if (!hv_dev->channel)
+               return -ENODEV;
+       hv_ringbuffer_get_debuginfo(&hv_dev->channel->inbound, &inbound);
+       return sprintf(buf, "%d\n", inbound.bytes_avail_towrite);
+}
+static DEVICE_ATTR_RO(in_write_bytes_avail);
 
 /* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */
-static struct device_attribute vmbus_device_attrs[] = {
-       __ATTR(id, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(state, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(monitor_id, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(modalias, S_IRUGO, vmbus_show_device_attr, NULL),
-
-       __ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(server_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(server_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL),
-
-       __ATTR(client_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(client_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(client_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL),
-
-       __ATTR(out_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(out_read_index, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(out_write_index, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(out_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(out_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
-
-       __ATTR(in_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(in_read_index, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(in_write_index, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(in_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR(in_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
-       __ATTR_NULL
+static struct attribute *vmbus_attrs[] = {
+       &dev_attr_id.attr,
+       &dev_attr_state.attr,
+       &dev_attr_monitor_id.attr,
+       &dev_attr_class_id.attr,
+       &dev_attr_device_id.attr,
+       &dev_attr_modalias.attr,
+       &dev_attr_server_monitor_pending.attr,
+       &dev_attr_client_monitor_pending.attr,
+       &dev_attr_server_monitor_latency.attr,
+       &dev_attr_client_monitor_latency.attr,
+       &dev_attr_server_monitor_conn_id.attr,
+       &dev_attr_client_monitor_conn_id.attr,
+       &dev_attr_out_intr_mask.attr,
+       &dev_attr_out_read_index.attr,
+       &dev_attr_out_write_index.attr,
+       &dev_attr_out_read_bytes_avail.attr,
+       &dev_attr_out_write_bytes_avail.attr,
+       &dev_attr_in_intr_mask.attr,
+       &dev_attr_in_read_index.attr,
+       &dev_attr_in_write_index.attr,
+       &dev_attr_in_read_bytes_avail.attr,
+       &dev_attr_in_write_bytes_avail.attr,
+       NULL,
 };
-
+ATTRIBUTE_GROUPS(vmbus);
 
 /*
  * vmbus_uevent - add uevent for our device
@@ -383,7 +557,7 @@ static struct bus_type  hv_bus = {
        .remove =               vmbus_remove,
        .probe =                vmbus_probe,
        .uevent =               vmbus_uevent,
-       .dev_attrs =    vmbus_device_attrs,
+       .dev_groups =           vmbus_groups,
 };
 
 static const char *driver_name = "hyperv";
index 8dacd4c9ee8745f6449aaff8f6e9c03a4dd20341..e760715bd9cbd30c01c6c16da430c06931fb04c1 100644 (file)
@@ -537,4 +537,5 @@ source "drivers/misc/carma/Kconfig"
 source "drivers/misc/altera-stapl/Kconfig"
 source "drivers/misc/mei/Kconfig"
 source "drivers/misc/vmw_vmci/Kconfig"
+source "drivers/misc/mic/Kconfig"
 endmenu
index c235d5b683111534a39996b75d8832f063ea0ae9..0b7ea3ea8bb86cc3971c7af15258c285100bd8e1 100644 (file)
@@ -53,3 +53,4 @@ obj-$(CONFIG_INTEL_MEI)               += mei/
 obj-$(CONFIG_VMWARE_VMCI)      += vmw_vmci/
 obj-$(CONFIG_LATTICE_ECP3_CONFIG)      += lattice-ecp3-config.o
 obj-$(CONFIG_SRAM)             += sram.o
+obj-y                          += mic/
index 1256a4bf1c04aca6465bcdde05158ade90c7881e..b7ebf8021d99d8c392d6f58961a7e3524f2d71ba 100644 (file)
@@ -297,7 +297,7 @@ static int __init charlcd_probe(struct platform_device *pdev)
        lcd->irq = platform_get_irq(pdev, 0);
        /* If no IRQ is supplied, we'll survive without it */
        if (lcd->irq >= 0) {
-               if (request_irq(lcd->irq, charlcd_interrupt, IRQF_DISABLED,
+               if (request_irq(lcd->irq, charlcd_interrupt, 0,
                                DRIVERNAME, lcd)) {
                        ret = -EIO;
                        goto out_no_irq;
index 057580e026c056f7b6ff576d51fbe225db0efd47..48ea33d15a79eca71bce4c7f3c986d7609880304 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/module.h>
+#include <linux/of.h>
 
 #define BH1780_REG_CONTROL     0x80
 #define BH1780_REG_PARTID      0x8A
@@ -244,6 +245,15 @@ static const struct i2c_device_id bh1780_id[] = {
        { },
 };
 
+#ifdef CONFIG_OF
+static const struct of_device_id of_bh1780_match[] = {
+       { .compatible = "rohm,bh1780gli", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, of_bh1780_match);
+#endif
+
 static struct i2c_driver bh1780_driver = {
        .probe          = bh1780_probe,
        .remove         = bh1780_remove,
@@ -251,6 +261,7 @@ static struct i2c_driver bh1780_driver = {
        .driver = {
                .name = "bh1780",
                .pm     = &bh1780_pm,
+               .of_match_table = of_match_ptr(of_bh1780_match),
        },
 };
 
index 849e2fed4da2841310ee9a5cd80430cb29f6d950..2704d885a9b30517cea14c91fda89d5e6748a64e 100644 (file)
@@ -374,7 +374,7 @@ int bmp085_detect(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(bmp085_detect);
 
-static void __init bmp085_get_of_properties(struct bmp085_data *data)
+static void bmp085_get_of_properties(struct bmp085_data *data)
 {
 #ifdef CONFIG_OF
        struct device_node *np = data->dev->of_node;
index 2e50f811ff599205aade6f1b877bc7877e3917bb..fb397e7d1cce8a17ab2583c2bedc49438d72b03b 100644 (file)
@@ -176,7 +176,7 @@ static int cb710_suspend(struct pci_dev *pdev, pm_message_t state)
 {
        struct cb710_chip *chip = pci_get_drvdata(pdev);
 
-       free_irq(pdev->irq, chip);
+       devm_free_irq(&pdev->dev, pdev->irq, chip);
        pci_save_state(pdev);
        pci_disable_device(pdev);
        if (state.event & PM_EVENT_SLEEP)
index 04f2e1fa9dd15b10871919a788c81aff782178f8..9536852fd4c68e28673b6325737fdf1cd82df5c1 100644 (file)
@@ -96,4 +96,17 @@ config EEPROM_DIGSY_MTC_CFG
 
          If unsure, say N.
 
+config EEPROM_SUNXI_SID
+       tristate "Allwinner sunxi security ID support"
+       depends on ARCH_SUNXI && SYSFS
+       help
+         This is a driver for the 'security ID' available on various Allwinner
+         devices.
+
+         Due to the potential risks involved with changing e-fuses,
+         this driver is read-only.
+
+         This driver can also be built as a module. If so, the module
+         will be called sunxi_sid.
+
 endmenu
index fc1e81d292673b14732e9268b7cb9cdca9d921fb..9507aec95e948f88f31579585e60b8632c0b3782 100644 (file)
@@ -4,4 +4,5 @@ obj-$(CONFIG_EEPROM_LEGACY)     += eeprom.o
 obj-$(CONFIG_EEPROM_MAX6875)   += max6875.o
 obj-$(CONFIG_EEPROM_93CX6)     += eeprom_93cx6.o
 obj-$(CONFIG_EEPROM_93XX46)    += eeprom_93xx46.o
+obj-$(CONFIG_EEPROM_SUNXI_SID) += sunxi_sid.o
 obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o
index 840b3594a5ae3c852b1e643a9f4b7e167033ad4f..4f3bca1003a175d2eb0ea7fdf423cd8dfa70e14d 100644 (file)
@@ -462,10 +462,17 @@ static int at25_remove(struct spi_device *spi)
 
 /*-------------------------------------------------------------------------*/
 
+static const struct of_device_id at25_of_match[] = {
+       { .compatible = "atmel,at25", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, at25_of_match);
+
 static struct spi_driver at25_driver = {
        .driver = {
                .name           = "at25",
                .owner          = THIS_MODULE,
+               .of_match_table = at25_of_match,
        },
        .probe          = at25_probe,
        .remove         = at25_remove,
diff --git a/drivers/misc/eeprom/sunxi_sid.c b/drivers/misc/eeprom/sunxi_sid.c
new file mode 100644 (file)
index 0000000..9c34e57
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl>
+ * http://www.linux-sunxi.org
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * This driver exposes the Allwinner security ID, efuses exported in byte-
+ * sized chunks.
+ */
+
+#include <linux/compiler.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+#define DRV_NAME "sunxi-sid"
+
+struct sunxi_sid_data {
+       void __iomem *reg_base;
+       unsigned int keysize;
+};
+
+/* We read the entire key, due to a 32 bit read alignment requirement. Since we
+ * want to return the requested byte, this results in somewhat slower code and
+ * uses 4 times more reads as needed but keeps code simpler. Since the SID is
+ * only very rarely probed, this is not really an issue.
+ */
+static u8 sunxi_sid_read_byte(const struct sunxi_sid_data *sid_data,
+                             const unsigned int offset)
+{
+       u32 sid_key;
+
+       if (offset >= sid_data->keysize)
+               return 0;
+
+       sid_key = ioread32be(sid_data->reg_base + round_down(offset, 4));
+       sid_key >>= (offset % 4) * 8;
+
+       return sid_key; /* Only return the last byte */
+}
+
+static ssize_t sid_read(struct file *fd, struct kobject *kobj,
+                       struct bin_attribute *attr, char *buf,
+                       loff_t pos, size_t size)
+{
+       struct platform_device *pdev;
+       struct sunxi_sid_data *sid_data;
+       int i;
+
+       pdev = to_platform_device(kobj_to_dev(kobj));
+       sid_data = platform_get_drvdata(pdev);
+
+       if (pos < 0 || pos >= sid_data->keysize)
+               return 0;
+       if (size > sid_data->keysize - pos)
+               size = sid_data->keysize - pos;
+
+       for (i = 0; i < size; i++)
+               buf[i] = sunxi_sid_read_byte(sid_data, pos + i);
+
+       return i;
+}
+
+static struct bin_attribute sid_bin_attr = {
+       .attr = { .name = "eeprom", .mode = S_IRUGO, },
+       .read = sid_read,
+};
+
+static int sunxi_sid_remove(struct platform_device *pdev)
+{
+       device_remove_bin_file(&pdev->dev, &sid_bin_attr);
+       dev_dbg(&pdev->dev, "driver unloaded\n");
+
+       return 0;
+}
+
+static const struct of_device_id sunxi_sid_of_match[] = {
+       { .compatible = "allwinner,sun4i-sid", .data = (void *)16},
+       { .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512},
+       {/* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, sunxi_sid_of_match);
+
+static int sunxi_sid_probe(struct platform_device *pdev)
+{
+       struct sunxi_sid_data *sid_data;
+       struct resource *res;
+       const struct of_device_id *of_dev_id;
+       u8 *entropy;
+       unsigned int i;
+
+       sid_data = devm_kzalloc(&pdev->dev, sizeof(struct sunxi_sid_data),
+                               GFP_KERNEL);
+       if (!sid_data)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       sid_data->reg_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(sid_data->reg_base))
+               return PTR_ERR(sid_data->reg_base);
+
+       of_dev_id = of_match_device(sunxi_sid_of_match, &pdev->dev);
+       if (!of_dev_id)
+               return -ENODEV;
+       sid_data->keysize = (int)of_dev_id->data;
+
+       platform_set_drvdata(pdev, sid_data);
+
+       sid_bin_attr.size = sid_data->keysize;
+       if (device_create_bin_file(&pdev->dev, &sid_bin_attr))
+               return -ENODEV;
+
+       entropy = kzalloc(sizeof(u8) * sid_data->keysize, GFP_KERNEL);
+       for (i = 0; i < sid_data->keysize; i++)
+               entropy[i] = sunxi_sid_read_byte(sid_data, i);
+       add_device_randomness(entropy, sid_data->keysize);
+       kfree(entropy);
+
+       dev_dbg(&pdev->dev, "loaded\n");
+
+       return 0;
+}
+
+static struct platform_driver sunxi_sid_driver = {
+       .probe = sunxi_sid_probe,
+       .remove = sunxi_sid_remove,
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = sunxi_sid_of_match,
+       },
+};
+module_platform_driver(sunxi_sid_driver);
+
+MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>");
+MODULE_DESCRIPTION("Allwinner sunxi security id driver");
+MODULE_LICENSE("GPL");
index 0346d87c5fed9f20fffa622269b4102bbdeb516f..6b3bf9ab051db1e5e0c1ef15dec4bbd50906da42 100644 (file)
@@ -153,7 +153,6 @@ error_ioremap:
 error_heartbeat:
        ibmasm_event_buffer_exit(sp);
 error_eventbuffer:
-       pci_set_drvdata(pdev, NULL);
        kfree(sp);
 error_kmalloc:
         pci_release_regions(pdev);
@@ -165,7 +164,7 @@ error_resources:
 
 static void ibmasm_remove_one(struct pci_dev *pdev)
 {
-       struct service_processor *sp = (struct service_processor *)pci_get_drvdata(pdev);
+       struct service_processor *sp = pci_get_drvdata(pdev);
 
        dbg("Unregistering UART\n");
        ibmasm_unregister_uart(sp);
@@ -182,7 +181,6 @@ static void ibmasm_remove_one(struct pci_dev *pdev)
        ibmasm_free_remote_input_dev(sp);
        iounmap(sp->base_address);
        ibmasm_event_buffer_exit(sp);
-       pci_set_drvdata(pdev, NULL);
        kfree(sp);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
index f6ff711aa5bbbaa4a177dbd416ccdab53017bb23..d22c6864508b2de3505f56fa7c0a44b97dbe90fb 100644 (file)
@@ -58,6 +58,7 @@ void mei_amthif_reset_params(struct mei_device *dev)
        dev->iamthif_state = MEI_IAMTHIF_IDLE;
        dev->iamthif_timer = 0;
        dev->iamthif_stall_timer = 0;
+       dev->iamthif_open_count = 0;
 }
 
 /**
@@ -78,8 +79,10 @@ int mei_amthif_host_init(struct mei_device *dev)
 
        i = mei_me_cl_by_uuid(dev, &mei_amthif_guid);
        if (i < 0) {
-               dev_info(&dev->pdev->dev, "amthif: failed to find the client\n");
-               return -ENOENT;
+               ret = i;
+               dev_info(&dev->pdev->dev,
+                       "amthif: failed to find the client %d\n", ret);
+               return ret;
        }
 
        cl->me_client_id = dev->me_clients[i].client_id;
@@ -106,8 +109,9 @@ int mei_amthif_host_init(struct mei_device *dev)
        ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID);
 
        if (ret < 0) {
-               dev_err(&dev->pdev->dev, "amthif: failed link client\n");
-               return -ENOENT;
+               dev_err(&dev->pdev->dev,
+                       "amthif: failed link client %d\n", ret);
+               return ret;
        }
 
        cl->state = MEI_FILE_CONNECTING;
@@ -313,13 +317,13 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb)
                mei_hdr.me_addr = dev->iamthif_cl.me_client_id;
                mei_hdr.reserved = 0;
                dev->iamthif_msg_buf_index += mei_hdr.length;
-               if (mei_write_message(dev, &mei_hdr,
-                                       (unsigned char *)dev->iamthif_msg_buf))
-                       return -ENODEV;
+               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(&dev->iamthif_cl))
-                               return -ENODEV;
+                               return -EIO;
                        dev->iamthif_flow_control_pending = true;
                        dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL;
                        dev_dbg(&dev->pdev->dev, "add amthif cb to write waiting list\n");
@@ -459,6 +463,16 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
        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 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;
@@ -481,16 +495,17 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
        dev_dbg(&dev->pdev->dev, MEI_HDR_FMT,  MEI_HDR_PRM(&mei_hdr));
 
        *slots -=  msg_slots;
-       if (mei_write_message(dev, &mei_hdr,
-               dev->iamthif_msg_buf + dev->iamthif_msg_buf_index)) {
-                       dev->iamthif_state = MEI_IAMTHIF_IDLE;
-                       cl->status = -ENODEV;
-                       list_del(&cb->list);
-                       return -ENODEV;
+       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 -ENODEV;
+               return -EIO;
 
        dev->iamthif_msg_buf_index += mei_hdr.length;
        cl->status = 0;
@@ -720,8 +735,8 @@ static bool mei_clear_lists(struct mei_device *dev, struct file *file)
 */
 int mei_amthif_release(struct mei_device *dev, struct file *file)
 {
-       if (dev->open_handle_count > 0)
-               dev->open_handle_count--;
+       if (dev->iamthif_open_count > 0)
+               dev->iamthif_open_count--;
 
        if (dev->iamthif_file_object == file &&
            dev->iamthif_state != MEI_IAMTHIF_IDLE) {
index e0684b4d9a08aebd26453de23c5b88fb69b6b7d7..0ccc22ce09045b77f6b5a413867653f29382c4a3 100644 (file)
@@ -187,10 +187,14 @@ int mei_io_cb_alloc_resp_buf(struct mei_cl_cb *cb, size_t length)
  */
 int mei_cl_flush_queues(struct mei_cl *cl)
 {
+       struct mei_device *dev;
+
        if (WARN_ON(!cl || !cl->dev))
                return -EINVAL;
 
-       dev_dbg(&cl->dev->pdev->dev, "remove list entry belonging to cl\n");
+       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_flush(&cl->dev->write_list, cl);
        mei_io_list_flush(&cl->dev->write_waiting_list, cl);
@@ -271,6 +275,7 @@ struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl)
 int mei_cl_link(struct mei_cl *cl, int id)
 {
        struct mei_device *dev;
+       long open_handle_count;
 
        if (WARN_ON(!cl || !cl->dev))
                return -EINVAL;
@@ -284,6 +289,19 @@ int mei_cl_link(struct mei_cl *cl, int id)
 
        if (id >= MEI_CLIENTS_MAX) {
                dev_err(&dev->pdev->dev, "id exceded %d", MEI_CLIENTS_MAX) ;
+               return -EMFILE;
+       }
+
+       open_handle_count = dev->open_handle_count + dev->iamthif_open_count;
+       if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
+               dev_err(&dev->pdev->dev, "open_handle_count exceded %d",
+                       MEI_MAX_OPEN_HANDLE_COUNT);
+               return -EMFILE;
+       }
+
+       if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
+               dev_err(&dev->pdev->dev, "open_handle_count exceded %d",
+                       MEI_MAX_OPEN_HANDLE_COUNT);
                return -ENOENT;
        }
 
@@ -296,7 +314,7 @@ int mei_cl_link(struct mei_cl *cl, int id)
 
        cl->state = MEI_FILE_INITIALIZING;
 
-       dev_dbg(&dev->pdev->dev, "link cl host id = %d\n", cl->host_client_id);
+       cl_dbg(dev, cl, "link cl\n");
        return 0;
 }
 
@@ -308,7 +326,6 @@ int mei_cl_link(struct mei_cl *cl, int id)
 int mei_cl_unlink(struct mei_cl *cl)
 {
        struct mei_device *dev;
-       struct mei_cl *pos, *next;
 
        /* don't shout on error exit path */
        if (!cl)
@@ -320,14 +337,21 @@ int mei_cl_unlink(struct mei_cl *cl)
 
        dev = cl->dev;
 
-       list_for_each_entry_safe(pos, next, &dev->file_list, link) {
-               if (cl->host_client_id == pos->host_client_id) {
-                       dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n",
-                               pos->host_client_id, pos->me_client_id);
-                       list_del_init(&pos->link);
-                       break;
-               }
-       }
+       cl_dbg(dev, cl, "unlink client");
+
+       if (dev->open_handle_count > 0)
+               dev->open_handle_count--;
+
+       /* never clear the 0 bit */
+       if (cl->host_client_id)
+               clear_bit(cl->host_client_id, dev->host_clients_map);
+
+       list_del_init(&cl->link);
+
+       cl->state = MEI_FILE_INITIALIZING;
+
+       list_del_init(&cl->link);
+
        return 0;
 }
 
@@ -341,17 +365,6 @@ void mei_host_client_init(struct work_struct *work)
 
        mutex_lock(&dev->device_lock);
 
-       bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
-       dev->open_handle_count = 0;
-
-       /*
-        * Reserving the first three client IDs
-        * 0: Reserved for MEI Bus Message communications
-        * 1: Reserved for Watchdog
-        * 2: Reserved for AMTHI
-        */
-       bitmap_set(dev->host_clients_map, 0, 3);
-
        for (i = 0; i < dev->me_clients_num; i++) {
                client_props = &dev->me_clients[i].props;
 
@@ -390,6 +403,8 @@ int mei_cl_disconnect(struct mei_cl *cl)
 
        dev = cl->dev;
 
+       cl_dbg(dev, cl, "disconnecting");
+
        if (cl->state != MEI_FILE_DISCONNECTING)
                return 0;
 
@@ -402,13 +417,13 @@ int mei_cl_disconnect(struct mei_cl *cl)
                dev->hbuf_is_ready = false;
                if (mei_hbm_cl_disconnect_req(dev, cl)) {
                        rets = -ENODEV;
-                       dev_err(&dev->pdev->dev, "failed to disconnect.\n");
+                       cl_err(dev, cl, "failed to disconnect.\n");
                        goto free;
                }
                mdelay(10); /* Wait for hardware disconnection ready */
                list_add_tail(&cb->list, &dev->ctrl_rd_list.list);
        } else {
-               dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n");
+               cl_dbg(dev, cl, "add disconnect cb to control write list\n");
                list_add_tail(&cb->list, &dev->ctrl_wr_list.list);
 
        }
@@ -421,18 +436,17 @@ int mei_cl_disconnect(struct mei_cl *cl)
        mutex_lock(&dev->device_lock);
        if (MEI_FILE_DISCONNECTED == cl->state) {
                rets = 0;
-               dev_dbg(&dev->pdev->dev, "successfully disconnected from FW client.\n");
+               cl_dbg(dev, cl, "successfully disconnected from FW client.\n");
        } else {
                rets = -ENODEV;
                if (MEI_FILE_DISCONNECTED != cl->state)
-                       dev_dbg(&dev->pdev->dev, "wrong status client disconnect.\n");
+                       cl_err(dev, cl, "wrong status client disconnect.\n");
 
                if (err)
-                       dev_dbg(&dev->pdev->dev,
-                                       "wait failed disconnect err=%08x\n",
+                       cl_dbg(dev, cl, "wait failed disconnect err=%08x\n",
                                        err);
 
-               dev_dbg(&dev->pdev->dev, "failed to disconnect from FW client.\n");
+               cl_err(dev, cl, "failed to disconnect from FW client.\n");
        }
 
        mei_io_list_flush(&dev->ctrl_rd_list, cl);
@@ -639,13 +653,12 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
                return -ENODEV;
 
        if (cl->read_cb) {
-               dev_dbg(&dev->pdev->dev, "read is pending.\n");
+               cl_dbg(dev, cl, "read is pending.\n");
                return -EBUSY;
        }
        i = mei_me_cl_by_id(dev, cl->me_client_id);
        if (i < 0) {
-               dev_err(&dev->pdev->dev, "no such me client %d\n",
-                       cl->me_client_id);
+               cl_err(dev, cl, "no such me client %d\n", cl->me_client_id);
                return  -ENODEV;
        }
 
@@ -664,6 +677,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length)
        if (dev->hbuf_is_ready) {
                dev->hbuf_is_ready = false;
                if (mei_hbm_cl_flow_control_req(dev, cl)) {
+                       cl_err(dev, cl, "flow control send failed\n");
                        rets = -ENODEV;
                        goto err;
                }
@@ -691,10 +705,32 @@ err:
 int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
                                     s32 *slots, struct mei_cl_cb *cmpl_list)
 {
-       struct mei_device *dev = cl->dev;
+       struct mei_device *dev;
+       struct mei_msg_data *buf;
        struct mei_msg_hdr mei_hdr;
-       size_t len = cb->request_buffer.size - cb->buf_idx;
-       u32 msg_slots = mei_data2slots(len);
+       size_t len;
+       u32 msg_slots;
+       int rets;
+
+
+       if (WARN_ON(!cl || !cl->dev))
+               return -ENODEV;
+
+       dev = cl->dev;
+
+       buf = &cb->request_buffer;
+
+       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;
+       }
+
+       len = buf->size - cb->buf_idx;
+       msg_slots = mei_data2slots(len);
 
        mei_hdr.host_addr = cl->host_client_id;
        mei_hdr.me_addr = cl->me_client_id;
@@ -714,16 +750,15 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
                return 0;
        }
 
-       dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n",
+       cl_dbg(dev, cl, "buf: size = %d idx = %lu\n",
                        cb->request_buffer.size, cb->buf_idx);
-       dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr));
 
        *slots -=  msg_slots;
-       if (mei_write_message(dev, &mei_hdr,
-                       cb->request_buffer.data + cb->buf_idx)) {
-               cl->status = -ENODEV;
+       rets = mei_write_message(dev, &mei_hdr, buf->data + cb->buf_idx);
+       if (rets) {
+               cl->status = rets;
                list_move_tail(&cb->list, &cmpl_list->list);
-               return -ENODEV;
+               return rets;
        }
 
        cl->status = 0;
@@ -732,7 +767,7 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
 
        if (mei_hdr.msg_complete) {
                if (mei_cl_flow_ctrl_reduce(cl))
-                       return -ENODEV;
+                       return -EIO;
                list_move_tail(&cb->list, &dev->write_waiting_list.list);
        }
 
@@ -767,7 +802,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
 
        buf = &cb->request_buffer;
 
-       dev_dbg(&dev->pdev->dev, "mei_cl_write %d\n", buf->size);
+       cl_dbg(dev, cl, "mei_cl_write %d\n", buf->size);
 
 
        cb->fop_type = MEI_FOP_WRITE;
@@ -800,14 +835,10 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
        mei_hdr.me_addr = cl->me_client_id;
        mei_hdr.reserved = 0;
 
-       dev_dbg(&dev->pdev->dev, "write " MEI_HDR_FMT "\n",
-               MEI_HDR_PRM(&mei_hdr));
-
 
-       if (mei_write_message(dev, &mei_hdr, buf->data)) {
-               rets = -EIO;
+       rets = mei_write_message(dev, &mei_hdr, buf->data);
+       if (rets)
                goto err;
-       }
 
        cl->writing_state = MEI_WRITING;
        cb->buf_idx = mei_hdr.length;
@@ -898,11 +929,11 @@ void mei_cl_all_wakeup(struct mei_device *dev)
        struct mei_cl *cl, *next;
        list_for_each_entry_safe(cl, next, &dev->file_list, link) {
                if (waitqueue_active(&cl->rx_wait)) {
-                       dev_dbg(&dev->pdev->dev, "Waking up reading client!\n");
+                       cl_dbg(dev, cl, "Waking up reading client!\n");
                        wake_up_interruptible(&cl->rx_wait);
                }
                if (waitqueue_active(&cl->tx_wait)) {
-                       dev_dbg(&dev->pdev->dev, "Waking up writing client!\n");
+                       cl_dbg(dev, cl, "Waking up writing client!\n");
                        wake_up_interruptible(&cl->tx_wait);
                }
        }
index 892cc4207fa202629e27698c0ae536f0b81880b0..c8396e582f1c697d296834cb62f19442998b59be 100644 (file)
@@ -115,4 +115,13 @@ void mei_cl_all_disconnect(struct mei_device *dev);
 void mei_cl_all_wakeup(struct mei_device *dev);
 void mei_cl_all_write_clear(struct mei_device *dev);
 
+#define MEI_CL_FMT "cl:host=%02d me=%02d "
+#define MEI_CL_PRM(cl) (cl)->host_client_id, (cl)->me_client_id
+
+#define cl_dbg(dev, cl, format, arg...) \
+       dev_dbg(&(dev)->pdev->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg)
+
+#define cl_err(dev, cl, format, arg...) \
+       dev_err(&(dev)->pdev->dev, MEI_CL_FMT format, MEI_CL_PRM(cl), ##arg)
+
 #endif /* _MEI_CLIENT_H_ */
index 0a0448326e9d583f951932b220d90186c59f92b7..9b3a0fb7f265861bc23e42797281e5d2423bc5fc 100644 (file)
@@ -49,7 +49,7 @@ static void mei_hbm_me_cl_allocate(struct mei_device *dev)
        kfree(dev->me_clients);
        dev->me_clients = NULL;
 
-       dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%zd.\n",
+       dev_dbg(&dev->pdev->dev, "memory allocation for ME clients size=%ld.\n",
                dev->me_clients_num * sizeof(struct mei_me_client));
        /* allocate storage for ME clients representation */
        clients = kcalloc(dev->me_clients_num,
@@ -174,7 +174,7 @@ int mei_hbm_start_req(struct mei_device *dev)
                dev_err(&dev->pdev->dev, "version message write failed\n");
                dev->dev_state = MEI_DEV_RESETTING;
                mei_reset(dev, 1);
-               return -ENODEV;
+               return -EIO;
        }
        dev->hbm_state = MEI_HBM_START;
        dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
@@ -677,7 +677,10 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
 
        case HOST_ENUM_RES_CMD:
                enum_res = (struct hbm_host_enum_response *) mei_msg;
-               memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
+               BUILD_BUG_ON(sizeof(dev->me_clients_map)
+                               < sizeof(enum_res->valid_addresses));
+               memcpy(dev->me_clients_map, enum_res->valid_addresses,
+                       sizeof(enum_res->valid_addresses));
                if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
                    dev->hbm_state == MEI_HBM_ENUM_CLIENTS) {
                                dev->init_clients_timer = 0;
index 6a203b6e83463e6252cdb6123780b1c5de63c8f0..6c0fde55270d3681c6048c1516b6915c291e4c45 100644 (file)
 #define MEI_DEV_ID_PPT_3      0x1DBA  /* Panther Point */
 
 #define MEI_DEV_ID_LPT        0x8C3A  /* Lynx Point */
+#define MEI_DEV_ID_LPT_W      0x8D3A  /* Lynx Point - Wellsburg */
 #define MEI_DEV_ID_LPT_LP     0x9C3A  /* Lynx Point LP */
 /*
  * MEI HW Section
index 6197018e2f16a24296619442fe6c8744c7c55b88..c32d45209cc68ca9e45a4e57895be1c0406ebe41 100644 (file)
@@ -68,6 +68,14 @@ void mei_device_init(struct mei_device *dev)
        mei_io_list_init(&dev->amthif_cmd_list);
        mei_io_list_init(&dev->amthif_rd_complete_list);
 
+       bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
+       dev->open_handle_count = 0;
+
+       /*
+        * Reserving the first client ID
+        * 0: Reserved for MEI Bus Message communications
+        */
+       bitmap_set(dev->host_clients_map, 0, 1);
 }
 EXPORT_SYMBOL_GPL(mei_device_init);
 
@@ -165,12 +173,7 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
                /* remove entry if already in list */
                dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n");
                mei_cl_unlink(&dev->wd_cl);
-               if (dev->open_handle_count > 0)
-                       dev->open_handle_count--;
                mei_cl_unlink(&dev->iamthif_cl);
-               if (dev->open_handle_count > 0)
-                       dev->open_handle_count--;
-
                mei_amthif_reset_params(dev);
                memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg));
        }
index 4b59cb742dee9bac451b4cbf5d268083c8534ea7..7a95c07e59a6d675a4cc37a41c70fbd3e95961d7 100644 (file)
@@ -113,13 +113,13 @@ static int mei_cl_irq_read_msg(struct mei_device *dev,
 
                if (cb->response_buffer.size == 0 ||
                    cb->response_buffer.data == NULL) {
-                       dev_err(&dev->pdev->dev, "response buffer is not allocated.\n");
+                       cl_err(dev, cl, "response buffer is not allocated.\n");
                        list_del(&cb->list);
                        return -ENOMEM;
                }
 
                if (cb->response_buffer.size < mei_hdr->length + cb->buf_idx) {
-                       dev_dbg(&dev->pdev->dev, "message overflow. size %d len %d idx %ld\n",
+                       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,
@@ -127,7 +127,7 @@ static int mei_cl_irq_read_msg(struct mei_device *dev,
                                          GFP_KERNEL);
 
                        if (!buffer) {
-                               dev_err(&dev->pdev->dev, "allocation failed.\n");
+                               cl_err(dev, cl, "allocation failed.\n");
                                list_del(&cb->list);
                                return -ENOMEM;
                        }
@@ -143,9 +143,7 @@ static int mei_cl_irq_read_msg(struct mei_device *dev,
                if (mei_hdr->msg_complete) {
                        cl->status = 0;
                        list_del(&cb->list);
-                       dev_dbg(&dev->pdev->dev, "completed read H cl = %d, ME cl = %d, length = %lu\n",
-                               cl->host_client_id,
-                               cl->me_client_id,
+                       cl_dbg(dev, cl, "completed read length = %lu\n",
                                cb->buf_idx);
                        list_add_tail(&cb->list, &complete_list->list);
                }
@@ -218,9 +216,11 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
                           s32 *slots, struct mei_cl_cb *cmpl_list)
 {
        struct mei_device *dev = cl->dev;
-
        u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));
 
+       int ret;
+
+
        if (*slots < msg_slots) {
                /* return the cancel routine */
                list_del(&cb->list);
@@ -229,12 +229,14 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
 
        *slots -= msg_slots;
 
-       if (mei_hbm_cl_flow_control_req(dev, cl)) {
-               cl->status = -ENODEV;
+       ret = mei_hbm_cl_flow_control_req(dev, cl);
+       if (ret) {
+               cl->status = ret;
                cb->buf_idx = 0;
                list_move_tail(&cb->list, &cmpl_list->list);
-               return -ENODEV;
+               return ret;
        }
+
        list_move_tail(&cb->list, &dev->read_list.list);
 
        return 0;
@@ -256,6 +258,7 @@ static int mei_cl_irq_ioctl(struct mei_cl *cl, struct mei_cl_cb *cb,
                           s32 *slots, struct mei_cl_cb *cmpl_list)
 {
        struct mei_device *dev = cl->dev;
+       int ret;
 
        u32 msg_slots =
                mei_data2slots(sizeof(struct hbm_client_connect_request));
@@ -270,11 +273,12 @@ static int mei_cl_irq_ioctl(struct mei_cl *cl, struct mei_cl_cb *cb,
 
        cl->state = MEI_FILE_CONNECTING;
 
-       if (mei_hbm_cl_connect_req(dev, cl)) {
-               cl->status = -ENODEV;
+       ret = mei_hbm_cl_connect_req(dev, cl);
+       if (ret) {
+               cl->status = ret;
                cb->buf_idx = 0;
                list_del(&cb->list);
-               return -ENODEV;
+               return ret;
        }
 
        list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
@@ -345,14 +349,14 @@ int mei_irq_read_handler(struct mei_device *dev,
 
        /* decide where to read the message too */
        if (!mei_hdr->host_addr) {
-               dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n");
+               dev_dbg(&dev->pdev->dev, "call mei_hbm_dispatch.\n");
                mei_hbm_dispatch(dev, mei_hdr);
-               dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n");
+               dev_dbg(&dev->pdev->dev, "end mei_hbm_dispatch.\n");
        } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&
                   (MEI_FILE_CONNECTED == dev->iamthif_cl.state) &&
                   (dev->iamthif_state == MEI_IAMTHIF_READING)) {
 
-               dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");
+               dev_dbg(&dev->pdev->dev, "call mei_amthif_irq_read_msg.\n");
                dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
 
                ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list);
@@ -423,12 +427,12 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
                if (MEI_WRITING == cl->writing_state &&
                    cb->fop_type == MEI_FOP_WRITE &&
                    cl != &dev->iamthif_cl) {
-                       dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n");
+                       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) {
-                       dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n");
+                       cl_dbg(dev, cl, "check iamthif flow control.\n");
                        if (dev->iamthif_flow_control_pending) {
                                ret = mei_amthif_irq_read(dev, &slots);
                                if (ret)
@@ -509,13 +513,6 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
                cl = cb->cl;
                if (cl == NULL)
                        continue;
-               if (mei_cl_flow_ctrl_creds(cl) <= 0) {
-                       dev_dbg(&dev->pdev->dev,
-                               "No flow control credentials for client %d, not sending.\n",
-                               cl->host_client_id);
-                       continue;
-               }
-
                if (cl == &dev->iamthif_cl)
                        ret = mei_amthif_irq_write_complete(cl, cb,
                                                &slots, cmpl_list);
index cabeddd66c1f406f73f7e5bed2a56b7e52cd7621..9661a812f55045d2cd522a7877ed8d4759f49b38 100644 (file)
@@ -60,48 +60,45 @@ static int mei_open(struct inode *inode, struct file *file)
 
        int err;
 
-       err = -ENODEV;
        if (!misc->parent)
-               goto out;
+               return -ENODEV;
 
        pdev = container_of(misc->parent, struct pci_dev, dev);
 
        dev = pci_get_drvdata(pdev);
        if (!dev)
-               goto out;
+               return -ENODEV;
 
        mutex_lock(&dev->device_lock);
-       err = -ENOMEM;
-       cl = mei_cl_allocate(dev);
-       if (!cl)
-               goto out_unlock;
+
+       cl = NULL;
 
        err = -ENODEV;
        if (dev->dev_state != MEI_DEV_ENABLED) {
                dev_dbg(&dev->pdev->dev, "dev_state != MEI_ENABLED  dev_state = %s\n",
                    mei_dev_state_str(dev->dev_state));
-               goto out_unlock;
-       }
-       err = -EMFILE;
-       if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
-               dev_err(&dev->pdev->dev, "open_handle_count exceded %d",
-                       MEI_MAX_OPEN_HANDLE_COUNT);
-               goto out_unlock;
+               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)
-               goto out_unlock;
+               goto err_unlock;
 
        file->private_data = cl;
+
        mutex_unlock(&dev->device_lock);
 
        return nonseekable_open(inode, file);
 
-out_unlock:
+err_unlock:
        mutex_unlock(&dev->device_lock);
        kfree(cl);
-out:
        return err;
 }
 
@@ -144,10 +141,6 @@ static int mei_release(struct inode *inode, struct file *file)
            cl->host_client_id,
            cl->me_client_id);
 
-       if (dev->open_handle_count > 0) {
-               clear_bit(cl->host_client_id, dev->host_clients_map);
-               dev->open_handle_count--;
-       }
        mei_cl_unlink(cl);
 
 
@@ -165,10 +158,7 @@ static int mei_release(struct inode *inode, struct file *file)
 
        file->private_data = NULL;
 
-       if (cb) {
-               mei_io_cb_free(cb);
-               cb = NULL;
-       }
+       mei_io_cb_free(cb);
 
        kfree(cl);
 out:
@@ -203,12 +193,18 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
 
        dev = cl->dev;
 
+
        mutex_lock(&dev->device_lock);
        if (dev->dev_state != MEI_DEV_ENABLED) {
                rets = -ENODEV;
                goto out;
        }
 
+       if (length == 0) {
+               rets = 0;
+               goto out;
+       }
+
        if (cl == &dev->iamthif_cl) {
                rets = mei_amthif_read(dev, file, ubuf, length, offset);
                goto out;
@@ -347,8 +343,14 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
                rets = -ENODEV;
                goto out;
        }
-       if (length > dev->me_clients[id].props.max_msg_length || length <= 0) {
-               rets = -EMSGSIZE;
+
+       if (length == 0) {
+               rets = 0;
+               goto out;
+       }
+
+       if (length > dev->me_clients[id].props.max_msg_length) {
+               rets = -EFBIG;
                goto out;
        }
 
@@ -401,8 +403,11 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
                goto out;
 
        rets = copy_from_user(write_cb->request_buffer.data, ubuf, length);
-       if (rets)
+       if (rets) {
+               dev_err(&dev->pdev->dev, "failed to copy data from userland\n");
+               rets = -EFAULT;
                goto out;
+       }
 
        if (cl == &dev->iamthif_cl) {
                rets = mei_amthif_write(dev, write_cb);
@@ -489,11 +494,11 @@ static int mei_ioctl_connect_client(struct file *file,
                        rets = -ENODEV;
                        goto end;
                }
-               clear_bit(cl->host_client_id, dev->host_clients_map);
                mei_cl_unlink(cl);
 
                kfree(cl);
                cl = NULL;
+               dev->iamthif_open_count++;
                file->private_data = &dev->iamthif_cl;
 
                client = &data->out_client_properties;
@@ -564,7 +569,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
        dev_dbg(&dev->pdev->dev, "copy connect data from user\n");
        if (copy_from_user(connect_data, (char __user *)data,
                                sizeof(struct mei_connect_client_data))) {
-               dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n");
+               dev_err(&dev->pdev->dev, "failed to copy data from userland\n");
                rets = -EFAULT;
                goto out;
        }
index 456b322013e269fc61f911d6f5761768c2863d4f..406f68e05b4ed4ccdbc6d9f886386a5d110e307b 100644 (file)
@@ -414,6 +414,7 @@ struct mei_device {
        struct file *iamthif_file_object;
        struct mei_cl iamthif_cl;
        struct mei_cl_cb *iamthif_current_cb;
+       long iamthif_open_count;
        int iamthif_mtu;
        unsigned long iamthif_timer;
        u32 iamthif_stall_timer;
index 1b3844e823792a0893ee9b200cbc7a681ee7025f..1bf300e089939f020fd8e90195130b5d7270b582 100644 (file)
@@ -77,6 +77,7 @@ static DEFINE_PCI_DEVICE_TABLE(mei_me_pci_tbl) = {
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)},
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)},
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_W)},
        {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)},
 
        /* required last entry */
@@ -231,7 +232,7 @@ static void mei_me_remove(struct pci_dev *pdev)
        hw = to_me_hw(dev);
 
 
-       dev_err(&pdev->dev, "stop\n");
+       dev_dbg(&pdev->dev, "stop\n");
        mei_stop(dev);
 
        /* disable interrupts */
@@ -239,7 +240,6 @@ static void mei_me_remove(struct pci_dev *pdev)
 
        free_irq(pdev->irq, dev);
        pci_disable_msi(pdev);
-       pci_set_drvdata(pdev, NULL);
 
        if (hw->mem_addr)
                pci_iounmap(pdev, hw->mem_addr);
@@ -262,7 +262,7 @@ static int mei_me_pci_suspend(struct device *device)
        if (!dev)
                return -ENODEV;
 
-       dev_err(&pdev->dev, "suspend\n");
+       dev_dbg(&pdev->dev, "suspend\n");
 
        mei_stop(dev);
 
diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig
new file mode 100644 (file)
index 0000000..e42b331
--- /dev/null
@@ -0,0 +1,39 @@
+comment "Intel MIC Host Driver"
+
+config INTEL_MIC_HOST
+       tristate "Intel MIC Host Driver"
+       depends on 64BIT && PCI && X86
+       select VHOST_RING
+       default N
+       help
+         This enables Host Driver support for the Intel Many Integrated
+         Core (MIC) family of PCIe form factor coprocessor devices that
+         run a 64 bit Linux OS. The driver manages card OS state and
+         enables communication between host and card. Intel MIC X100
+         devices are currently supported.
+
+         If you are building a host kernel with an Intel MIC device then
+         say M (recommended) or Y, else say N. If unsure say N.
+
+         More information about the Intel MIC family as well as the Linux
+         OS and tools for MIC to use with this driver are available from
+         <http://software.intel.com/en-us/mic-developer>.
+
+comment "Intel MIC Card Driver"
+
+config INTEL_MIC_CARD
+       tristate "Intel MIC Card Driver"
+       depends on 64BIT && X86
+       select VIRTIO
+       default N
+       help
+         This enables card driver support for the Intel Many Integrated
+         Core (MIC) device family. The card driver communicates shutdown/
+         crash events to the host and allows registration/configuration of
+         virtio devices. Intel MIC X100 devices are currently supported.
+
+         If you are building a card kernel for an Intel MIC device then
+         say M (recommended) or Y, else say N. If unsure say N.
+
+         For more information see
+         <http://software.intel.com/en-us/mic-developer>.
diff --git a/drivers/misc/mic/Makefile b/drivers/misc/mic/Makefile
new file mode 100644 (file)
index 0000000..05b34d6
--- /dev/null
@@ -0,0 +1,6 @@
+#
+# Makefile - Intel MIC Linux driver.
+# Copyright(c) 2013, Intel Corporation.
+#
+obj-$(CONFIG_INTEL_MIC_HOST) += host/
+obj-$(CONFIG_INTEL_MIC_CARD) += card/
diff --git a/drivers/misc/mic/card/Makefile b/drivers/misc/mic/card/Makefile
new file mode 100644 (file)
index 0000000..69d58be
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# Makefile - Intel MIC Linux driver.
+# Copyright(c) 2013, Intel Corporation.
+#
+ccflags-y += -DINTEL_MIC_CARD
+
+obj-$(CONFIG_INTEL_MIC_CARD) += mic_card.o
+mic_card-y += mic_x100.o
+mic_card-y += mic_device.o
+mic_card-y += mic_debugfs.o
+mic_card-y += mic_virtio.o
diff --git a/drivers/misc/mic/card/mic_debugfs.c b/drivers/misc/mic/card/mic_debugfs.c
new file mode 100644 (file)
index 0000000..421b3d7
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Disclaimer: The codes contained in these modules may be specific to
+ * the Intel Software Development Platform codenamed: Knights Ferry, and
+ * the Intel product codenamed: Knights Corner, and are not backward
+ * compatible with other Intel products. Additionally, Intel will NOT
+ * support the codes or instruction set in future products.
+ *
+ * Intel MIC Card driver.
+ *
+ */
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+
+/* Debugfs parent dir */
+static struct dentry *mic_dbg;
+
+/**
+ * mic_intr_test - Send interrupts to host.
+ */
+static int mic_intr_test(struct seq_file *s, void *unused)
+{
+       struct mic_driver *mdrv = s->private;
+       struct mic_device *mdev = &mdrv->mdev;
+
+       mic_send_intr(mdev, 0);
+       msleep(1000);
+       mic_send_intr(mdev, 1);
+       msleep(1000);
+       mic_send_intr(mdev, 2);
+       msleep(1000);
+       mic_send_intr(mdev, 3);
+       msleep(1000);
+
+       return 0;
+}
+
+static int mic_intr_test_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_intr_test, inode->i_private);
+}
+
+static int mic_intr_test_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations intr_test_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_intr_test_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_intr_test_release
+};
+
+/**
+ * mic_create_card_debug_dir - Initialize MIC debugfs entries.
+ */
+void __init mic_create_card_debug_dir(struct mic_driver *mdrv)
+{
+       struct dentry *d;
+
+       if (!mic_dbg)
+               return;
+
+       mdrv->dbg_dir = debugfs_create_dir(mdrv->name, mic_dbg);
+       if (!mdrv->dbg_dir) {
+               dev_err(mdrv->dev, "Cant create dbg_dir %s\n", mdrv->name);
+               return;
+       }
+
+       d = debugfs_create_file("intr_test", 0444, mdrv->dbg_dir,
+               mdrv, &intr_test_ops);
+
+       if (!d) {
+               dev_err(mdrv->dev,
+                       "Cant create dbg intr_test %s\n", mdrv->name);
+               return;
+       }
+}
+
+/**
+ * mic_delete_card_debug_dir - Uninitialize MIC debugfs entries.
+ */
+void mic_delete_card_debug_dir(struct mic_driver *mdrv)
+{
+       if (!mdrv->dbg_dir)
+               return;
+
+       debugfs_remove_recursive(mdrv->dbg_dir);
+}
+
+/**
+ * mic_init_card_debugfs - Initialize global debugfs entry.
+ */
+void __init mic_init_card_debugfs(void)
+{
+       mic_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL);
+       if (!mic_dbg)
+               pr_err("can't create debugfs dir\n");
+}
+
+/**
+ * mic_exit_card_debugfs - Uninitialize global debugfs entry
+ */
+void mic_exit_card_debugfs(void)
+{
+       debugfs_remove(mic_dbg);
+}
diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c
new file mode 100644 (file)
index 0000000..d0980ff
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Disclaimer: The codes contained in these modules may be specific to
+ * the Intel Software Development Platform codenamed: Knights Ferry, and
+ * the Intel product codenamed: Knights Corner, and are not backward
+ * compatible with other Intel products. Additionally, Intel will NOT
+ * support the codes or instruction set in future products.
+ *
+ * Intel MIC Card driver.
+ *
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/reboot.h>
+
+#include <linux/mic_common.h>
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_virtio.h"
+
+static struct mic_driver *g_drv;
+static struct mic_irq *shutdown_cookie;
+
+static void mic_notify_host(u8 state)
+{
+       struct mic_driver *mdrv = g_drv;
+       struct mic_bootparam __iomem *bootparam = mdrv->dp;
+
+       iowrite8(state, &bootparam->shutdown_status);
+       dev_dbg(mdrv->dev, "%s %d system_state %d\n",
+               __func__, __LINE__, state);
+       mic_send_intr(&mdrv->mdev, ioread8(&bootparam->c2h_shutdown_db));
+}
+
+static int mic_panic_event(struct notifier_block *this, unsigned long event,
+               void *ptr)
+{
+       struct mic_driver *mdrv = g_drv;
+       struct mic_bootparam __iomem *bootparam = mdrv->dp;
+
+       iowrite8(-1, &bootparam->h2c_config_db);
+       iowrite8(-1, &bootparam->h2c_shutdown_db);
+       mic_notify_host(MIC_CRASHED);
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block mic_panic = {
+       .notifier_call  = mic_panic_event,
+};
+
+static irqreturn_t mic_shutdown_isr(int irq, void *data)
+{
+       struct mic_driver *mdrv = g_drv;
+       struct mic_bootparam __iomem *bootparam = mdrv->dp;
+
+       mic_ack_interrupt(&g_drv->mdev);
+       if (ioread8(&bootparam->shutdown_card))
+               orderly_poweroff(true);
+       return IRQ_HANDLED;
+}
+
+static int mic_shutdown_init(void)
+{
+       int rc = 0;
+       struct mic_driver *mdrv = g_drv;
+       struct mic_bootparam __iomem *bootparam = mdrv->dp;
+       int shutdown_db;
+
+       shutdown_db = mic_next_card_db();
+       shutdown_cookie = mic_request_card_irq(mic_shutdown_isr,
+                       "Shutdown", mdrv, shutdown_db);
+       if (IS_ERR(shutdown_cookie))
+               rc = PTR_ERR(shutdown_cookie);
+       else
+               iowrite8(shutdown_db, &bootparam->h2c_shutdown_db);
+       return rc;
+}
+
+static void mic_shutdown_uninit(void)
+{
+       struct mic_driver *mdrv = g_drv;
+       struct mic_bootparam __iomem *bootparam = mdrv->dp;
+
+       iowrite8(-1, &bootparam->h2c_shutdown_db);
+       mic_free_card_irq(shutdown_cookie, mdrv);
+}
+
+static int __init mic_dp_init(void)
+{
+       struct mic_driver *mdrv = g_drv;
+       struct mic_device *mdev = &mdrv->mdev;
+       struct mic_bootparam __iomem *bootparam;
+       u64 lo, hi, dp_dma_addr;
+       u32 magic;
+
+       lo = mic_read_spad(&mdrv->mdev, MIC_DPLO_SPAD);
+       hi = mic_read_spad(&mdrv->mdev, MIC_DPHI_SPAD);
+
+       dp_dma_addr = lo | (hi << 32);
+       mdrv->dp = mic_card_map(mdev, dp_dma_addr, MIC_DP_SIZE);
+       if (!mdrv->dp) {
+               dev_err(mdrv->dev, "Cannot remap Aperture BAR\n");
+               return -ENOMEM;
+       }
+       bootparam = mdrv->dp;
+       magic = ioread32(&bootparam->magic);
+       if (MIC_MAGIC != magic) {
+               dev_err(mdrv->dev, "bootparam magic mismatch 0x%x\n", magic);
+               return -EIO;
+       }
+       return 0;
+}
+
+/* Uninitialize the device page */
+static void mic_dp_uninit(void)
+{
+       mic_card_unmap(&g_drv->mdev, g_drv->dp);
+}
+
+/**
+ * mic_request_card_irq - request an irq.
+ *
+ * @func: The callback function that handles the interrupt.
+ * @name: The ASCII name of the callee requesting the irq.
+ * @data: private data that is returned back when calling the
+ * function handler.
+ * @index: The doorbell index of the requester.
+ *
+ * returns: The cookie that is transparent to the caller. Passed
+ * back when calling mic_free_irq. An appropriate error code
+ * is returned on failure. Caller needs to use IS_ERR(return_val)
+ * to check for failure and PTR_ERR(return_val) to obtained the
+ * error code.
+ *
+ */
+struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data),
+       const char *name, void *data, int index)
+{
+       int rc = 0;
+       unsigned long cookie;
+       struct mic_driver *mdrv = g_drv;
+
+       rc  = request_irq(mic_db_to_irq(mdrv, index), func,
+               0, name, data);
+       if (rc) {
+               dev_err(mdrv->dev, "request_irq failed rc = %d\n", rc);
+               goto err;
+       }
+       mdrv->irq_info.irq_usage_count[index]++;
+       cookie = index;
+       return (struct mic_irq *)cookie;
+err:
+       return ERR_PTR(rc);
+}
+
+/**
+ * mic_free_card_irq - free irq.
+ *
+ * @cookie: cookie obtained during a successful call to mic_request_irq
+ * @data: private data specified by the calling function during the
+ * mic_request_irq
+ *
+ * returns: none.
+ */
+void mic_free_card_irq(struct mic_irq *cookie, void *data)
+{
+       int index;
+       struct mic_driver *mdrv = g_drv;
+
+       index = (unsigned long)cookie & 0xFFFFU;
+       free_irq(mic_db_to_irq(mdrv, index), data);
+       mdrv->irq_info.irq_usage_count[index]--;
+}
+
+/**
+ * mic_next_card_db - Get the doorbell with minimum usage count.
+ *
+ * Returns the irq index.
+ */
+int mic_next_card_db(void)
+{
+       int i;
+       int index = 0;
+       struct mic_driver *mdrv = g_drv;
+
+       for (i = 0; i < mdrv->intr_info.num_intr; i++) {
+               if (mdrv->irq_info.irq_usage_count[i] <
+                       mdrv->irq_info.irq_usage_count[index])
+                       index = i;
+       }
+
+       return index;
+}
+
+/**
+ * mic_init_irq - Initialize irq information.
+ *
+ * Returns 0 in success. Appropriate error code on failure.
+ */
+static int mic_init_irq(void)
+{
+       struct mic_driver *mdrv = g_drv;
+
+       mdrv->irq_info.irq_usage_count = kzalloc((sizeof(u32) *
+                       mdrv->intr_info.num_intr),
+                       GFP_KERNEL);
+       if (!mdrv->irq_info.irq_usage_count)
+               return -ENOMEM;
+       return 0;
+}
+
+/**
+ * mic_uninit_irq - Uninitialize irq information.
+ *
+ * None.
+ */
+static void mic_uninit_irq(void)
+{
+       struct mic_driver *mdrv = g_drv;
+
+       kfree(mdrv->irq_info.irq_usage_count);
+}
+
+/*
+ * mic_driver_init - MIC driver initialization tasks.
+ *
+ * Returns 0 in success. Appropriate error code on failure.
+ */
+int __init mic_driver_init(struct mic_driver *mdrv)
+{
+       int rc;
+
+       g_drv = mdrv;
+       /*
+        * Unloading the card module is not supported. The MIC card module
+        * handles fundamental operations like host/card initiated shutdowns
+        * and informing the host about card crashes and cannot be unloaded.
+        */
+       if (!try_module_get(mdrv->dev->driver->owner)) {
+               rc = -ENODEV;
+               goto done;
+       }
+       rc = mic_dp_init();
+       if (rc)
+               goto put;
+       rc = mic_init_irq();
+       if (rc)
+               goto dp_uninit;
+       rc = mic_shutdown_init();
+       if (rc)
+               goto irq_uninit;
+       rc = mic_devices_init(mdrv);
+       if (rc)
+               goto shutdown_uninit;
+       mic_create_card_debug_dir(mdrv);
+       atomic_notifier_chain_register(&panic_notifier_list, &mic_panic);
+done:
+       return rc;
+shutdown_uninit:
+       mic_shutdown_uninit();
+irq_uninit:
+       mic_uninit_irq();
+dp_uninit:
+       mic_dp_uninit();
+put:
+       module_put(mdrv->dev->driver->owner);
+       return rc;
+}
+
+/*
+ * mic_driver_uninit - MIC driver uninitialization tasks.
+ *
+ * Returns None
+ */
+void mic_driver_uninit(struct mic_driver *mdrv)
+{
+       mic_delete_card_debug_dir(mdrv);
+       mic_devices_uninit(mdrv);
+       /*
+        * Inform the host about the shutdown status i.e. poweroff/restart etc.
+        * The module cannot be unloaded so the only code path to call
+        * mic_devices_uninit(..) is the shutdown callback.
+        */
+       mic_notify_host(system_state);
+       mic_shutdown_uninit();
+       mic_uninit_irq();
+       mic_dp_uninit();
+       module_put(mdrv->dev->driver->owner);
+}
diff --git a/drivers/misc/mic/card/mic_device.h b/drivers/misc/mic/card/mic_device.h
new file mode 100644 (file)
index 0000000..347b9b3
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Disclaimer: The codes contained in these modules may be specific to
+ * the Intel Software Development Platform codenamed: Knights Ferry, and
+ * the Intel product codenamed: Knights Corner, and are not backward
+ * compatible with other Intel products. Additionally, Intel will NOT
+ * support the codes or instruction set in future products.
+ *
+ * Intel MIC Card driver.
+ *
+ */
+#ifndef _MIC_CARD_DEVICE_H_
+#define _MIC_CARD_DEVICE_H_
+
+#include <linux/workqueue.h>
+#include <linux/io.h>
+
+/**
+ * struct mic_intr_info - Contains h/w specific interrupt sources info
+ *
+ * @num_intr: The number of irqs available
+ */
+struct mic_intr_info {
+       u32 num_intr;
+};
+
+/**
+ * struct mic_irq_info - OS specific irq information
+ *
+ * @irq_usage_count: usage count array tracking the number of sources
+ * assigned for each irq.
+ */
+struct mic_irq_info {
+       int *irq_usage_count;
+};
+
+/**
+ * struct mic_device -  MIC device information.
+ *
+ * @mmio: MMIO bar information.
+ */
+struct mic_device {
+       struct mic_mw mmio;
+};
+
+/**
+ * struct mic_driver - MIC card driver information.
+ *
+ * @name: Name for MIC driver.
+ * @dbg_dir: debugfs directory of this MIC device.
+ * @dev: The device backing this MIC.
+ * @dp: The pointer to the virtio device page.
+ * @mdev: MIC device information for the host.
+ * @hotplug_work: Hot plug work for adding/removing virtio devices.
+ * @irq_info: The OS specific irq information
+ * @intr_info: H/W specific interrupt information.
+ */
+struct mic_driver {
+       char name[20];
+       struct dentry *dbg_dir;
+       struct device *dev;
+       void __iomem *dp;
+       struct mic_device mdev;
+       struct work_struct hotplug_work;
+       struct mic_irq_info irq_info;
+       struct mic_intr_info intr_info;
+};
+
+/**
+ * struct mic_irq - opaque pointer used as cookie
+ */
+struct mic_irq;
+
+/**
+ * mic_mmio_read - read from an MMIO register.
+ * @mw: MMIO register base virtual address.
+ * @offset: register offset.
+ *
+ * RETURNS: register value.
+ */
+static inline u32 mic_mmio_read(struct mic_mw *mw, u32 offset)
+{
+       return ioread32(mw->va + offset);
+}
+
+/**
+ * mic_mmio_write - write to an MMIO register.
+ * @mw: MMIO register base virtual address.
+ * @val: the data value to put into the register
+ * @offset: register offset.
+ *
+ * RETURNS: none.
+ */
+static inline void
+mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset)
+{
+       iowrite32(val, mw->va + offset);
+}
+
+int mic_driver_init(struct mic_driver *mdrv);
+void mic_driver_uninit(struct mic_driver *mdrv);
+int mic_next_card_db(void);
+struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data),
+       const char *name, void *data, int intr_src);
+void mic_free_card_irq(struct mic_irq *cookie, void *data);
+u32 mic_read_spad(struct mic_device *mdev, unsigned int idx);
+void mic_send_intr(struct mic_device *mdev, int doorbell);
+int mic_db_to_irq(struct mic_driver *mdrv, int db);
+u32 mic_ack_interrupt(struct mic_device *mdev);
+void mic_hw_intr_init(struct mic_driver *mdrv);
+void __iomem *
+mic_card_map(struct mic_device *mdev, dma_addr_t addr, size_t size);
+void mic_card_unmap(struct mic_device *mdev, void __iomem *addr);
+void __init mic_create_card_debug_dir(struct mic_driver *mdrv);
+void mic_delete_card_debug_dir(struct mic_driver *mdrv);
+void __init mic_init_card_debugfs(void);
+void mic_exit_card_debugfs(void);
+#endif
diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c
new file mode 100644 (file)
index 0000000..914cc9b
--- /dev/null
@@ -0,0 +1,630 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Disclaimer: The codes contained in these modules may be specific to
+ * the Intel Software Development Platform codenamed: Knights Ferry, and
+ * the Intel product codenamed: Knights Corner, and are not backward
+ * compatible with other Intel products. Additionally, Intel will NOT
+ * support the codes or instruction set in future products.
+ *
+ * Adapted from:
+ *
+ * virtio for kvm on s390
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ *
+ *    Author(s): Christian Borntraeger <borntraeger@de.ibm.com>
+ *
+ * Intel MIC Card driver.
+ *
+ */
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/virtio_config.h>
+
+#include "../common/mic_dev.h"
+#include "mic_virtio.h"
+
+#define VIRTIO_SUBCODE_64 0x0D00
+
+#define MIC_MAX_VRINGS                4
+struct mic_vdev {
+       struct virtio_device vdev;
+       struct mic_device_desc __iomem *desc;
+       struct mic_device_ctrl __iomem *dc;
+       struct mic_device *mdev;
+       void __iomem *vr[MIC_MAX_VRINGS];
+       int used_size[MIC_MAX_VRINGS];
+       struct completion reset_done;
+       struct mic_irq *virtio_cookie;
+       int c2h_vdev_db;
+};
+
+static struct mic_irq *virtio_config_cookie;
+#define to_micvdev(vd) container_of(vd, struct mic_vdev, vdev)
+
+/* Helper API to obtain the parent of the virtio device */
+static inline struct device *mic_dev(struct mic_vdev *mvdev)
+{
+       return mvdev->vdev.dev.parent;
+}
+
+/* This gets the device's feature bits. */
+static u32 mic_get_features(struct virtio_device *vdev)
+{
+       unsigned int i, bits;
+       u32 features = 0;
+       struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc;
+       u8 __iomem *in_features = mic_vq_features(desc);
+       int feature_len = ioread8(&desc->feature_len);
+
+       bits = min_t(unsigned, feature_len,
+               sizeof(vdev->features)) * 8;
+       for (i = 0; i < bits; i++)
+               if (ioread8(&in_features[i / 8]) & (BIT(i % 8)))
+                       features |= BIT(i);
+
+       return features;
+}
+
+static void mic_finalize_features(struct virtio_device *vdev)
+{
+       unsigned int i, bits;
+       struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc;
+       u8 feature_len = ioread8(&desc->feature_len);
+       /* Second half of bitmap is features we accept. */
+       u8 __iomem *out_features =
+               mic_vq_features(desc) + feature_len;
+
+       /* Give virtio_ring a chance to accept features. */
+       vring_transport_features(vdev);
+
+       memset_io(out_features, 0, feature_len);
+       bits = min_t(unsigned, feature_len,
+               sizeof(vdev->features)) * 8;
+       for (i = 0; i < bits; i++) {
+               if (test_bit(i, vdev->features))
+                       iowrite8(ioread8(&out_features[i / 8]) | (1 << (i % 8)),
+                                &out_features[i / 8]);
+       }
+}
+
+/*
+ * Reading and writing elements in config space
+ */
+static void mic_get(struct virtio_device *vdev, unsigned int offset,
+                  void *buf, unsigned len)
+{
+       struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc;
+
+       if (offset + len > ioread8(&desc->config_len))
+               return;
+       memcpy_fromio(buf, mic_vq_configspace(desc) + offset, len);
+}
+
+static void mic_set(struct virtio_device *vdev, unsigned int offset,
+                  const void *buf, unsigned len)
+{
+       struct mic_device_desc __iomem *desc = to_micvdev(vdev)->desc;
+
+       if (offset + len > ioread8(&desc->config_len))
+               return;
+       memcpy_toio(mic_vq_configspace(desc) + offset, buf, len);
+}
+
+/*
+ * The operations to get and set the status word just access the status
+ * field of the device descriptor. set_status also interrupts the host
+ * to tell about status changes.
+ */
+static u8 mic_get_status(struct virtio_device *vdev)
+{
+       return ioread8(&to_micvdev(vdev)->desc->status);
+}
+
+static void mic_set_status(struct virtio_device *vdev, u8 status)
+{
+       struct mic_vdev *mvdev = to_micvdev(vdev);
+       if (!status)
+               return;
+       iowrite8(status, &mvdev->desc->status);
+       mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db);
+}
+
+/* Inform host on a virtio device reset and wait for ack from host */
+static void mic_reset_inform_host(struct virtio_device *vdev)
+{
+       struct mic_vdev *mvdev = to_micvdev(vdev);
+       struct mic_device_ctrl __iomem *dc = mvdev->dc;
+       int retry = 100, i;
+
+       iowrite8(0, &dc->host_ack);
+       iowrite8(1, &dc->vdev_reset);
+       mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db);
+
+       /* Wait till host completes all card accesses and acks the reset */
+       for (i = retry; i--;) {
+               if (ioread8(&dc->host_ack))
+                       break;
+               msleep(100);
+       };
+
+       dev_dbg(mic_dev(mvdev), "%s: retry: %d\n", __func__, retry);
+
+       /* Reset status to 0 in case we timed out */
+       iowrite8(0, &mvdev->desc->status);
+}
+
+static void mic_reset(struct virtio_device *vdev)
+{
+       struct mic_vdev *mvdev = to_micvdev(vdev);
+
+       dev_dbg(mic_dev(mvdev), "%s: virtio id %d\n",
+               __func__, vdev->id.device);
+
+       mic_reset_inform_host(vdev);
+       complete_all(&mvdev->reset_done);
+}
+
+/*
+ * The virtio_ring code calls this API when it wants to notify the Host.
+ */
+static void mic_notify(struct virtqueue *vq)
+{
+       struct mic_vdev *mvdev = vq->priv;
+
+       mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db);
+}
+
+static void mic_del_vq(struct virtqueue *vq, int n)
+{
+       struct mic_vdev *mvdev = to_micvdev(vq->vdev);
+       struct vring *vr = (struct vring *)(vq + 1);
+
+       free_pages((unsigned long) vr->used, get_order(mvdev->used_size[n]));
+       vring_del_virtqueue(vq);
+       mic_card_unmap(mvdev->mdev, mvdev->vr[n]);
+       mvdev->vr[n] = NULL;
+}
+
+static void mic_del_vqs(struct virtio_device *vdev)
+{
+       struct mic_vdev *mvdev = to_micvdev(vdev);
+       struct virtqueue *vq, *n;
+       int idx = 0;
+
+       dev_dbg(mic_dev(mvdev), "%s\n", __func__);
+
+       list_for_each_entry_safe(vq, n, &vdev->vqs, list)
+               mic_del_vq(vq, idx++);
+}
+
+/*
+ * This routine will assign vring's allocated in host/io memory. Code in
+ * virtio_ring.c however continues to access this io memory as if it were local
+ * memory without io accessors.
+ */
+static struct virtqueue *mic_find_vq(struct virtio_device *vdev,
+                                    unsigned index,
+                                    void (*callback)(struct virtqueue *vq),
+                                    const char *name)
+{
+       struct mic_vdev *mvdev = to_micvdev(vdev);
+       struct mic_vqconfig __iomem *vqconfig;
+       struct mic_vqconfig config;
+       struct virtqueue *vq;
+       void __iomem *va;
+       struct _mic_vring_info __iomem *info;
+       void *used;
+       int vr_size, _vr_size, err, magic;
+       struct vring *vr;
+       u8 type = ioread8(&mvdev->desc->type);
+
+       if (index >= ioread8(&mvdev->desc->num_vq))
+               return ERR_PTR(-ENOENT);
+
+       if (!name)
+               return ERR_PTR(-ENOENT);
+
+       /* First assign the vring's allocated in host memory */
+       vqconfig = mic_vq_config(mvdev->desc) + index;
+       memcpy_fromio(&config, vqconfig, sizeof(config));
+       _vr_size = vring_size(config.num, MIC_VIRTIO_RING_ALIGN);
+       vr_size = PAGE_ALIGN(_vr_size + sizeof(struct _mic_vring_info));
+       va = mic_card_map(mvdev->mdev, config.address, vr_size);
+       if (!va)
+               return ERR_PTR(-ENOMEM);
+       mvdev->vr[index] = va;
+       memset_io(va, 0x0, _vr_size);
+       vq = vring_new_virtqueue(index,
+                               config.num, MIC_VIRTIO_RING_ALIGN, vdev,
+                               false,
+                               va, mic_notify, callback, name);
+       if (!vq) {
+               err = -ENOMEM;
+               goto unmap;
+       }
+       info = va + _vr_size;
+       magic = ioread32(&info->magic);
+
+       if (WARN(magic != MIC_MAGIC + type + index, "magic mismatch")) {
+               err = -EIO;
+               goto unmap;
+       }
+
+       /* Allocate and reassign used ring now */
+       mvdev->used_size[index] = PAGE_ALIGN(sizeof(__u16) * 3 +
+                       sizeof(struct vring_used_elem) * config.num);
+       used = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+                                       get_order(mvdev->used_size[index]));
+       if (!used) {
+               err = -ENOMEM;
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, err);
+               goto del_vq;
+       }
+       iowrite64(virt_to_phys(used), &vqconfig->used_address);
+
+       /*
+        * To reassign the used ring here we are directly accessing
+        * struct vring_virtqueue which is a private data structure
+        * in virtio_ring.c. At the minimum, a BUILD_BUG_ON() in
+        * vring_new_virtqueue() would ensure that
+        *  (&vq->vring == (struct vring *) (&vq->vq + 1));
+        */
+       vr = (struct vring *)(vq + 1);
+       vr->used = used;
+
+       vq->priv = mvdev;
+       return vq;
+del_vq:
+       vring_del_virtqueue(vq);
+unmap:
+       mic_card_unmap(mvdev->mdev, mvdev->vr[index]);
+       return ERR_PTR(err);
+}
+
+static int mic_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+                       struct virtqueue *vqs[],
+                       vq_callback_t *callbacks[],
+                       const char *names[])
+{
+       struct mic_vdev *mvdev = to_micvdev(vdev);
+       struct mic_device_ctrl __iomem *dc = mvdev->dc;
+       int i, err, retry = 100;
+
+       /* We must have this many virtqueues. */
+       if (nvqs > ioread8(&mvdev->desc->num_vq))
+               return -ENOENT;
+
+       for (i = 0; i < nvqs; ++i) {
+               dev_dbg(mic_dev(mvdev), "%s: %d: %s\n",
+                       __func__, i, names[i]);
+               vqs[i] = mic_find_vq(vdev, i, callbacks[i], names[i]);
+               if (IS_ERR(vqs[i])) {
+                       err = PTR_ERR(vqs[i]);
+                       goto error;
+               }
+       }
+
+       iowrite8(1, &dc->used_address_updated);
+       /*
+        * Send an interrupt to the host to inform it that used
+        * rings have been re-assigned.
+        */
+       mic_send_intr(mvdev->mdev, mvdev->c2h_vdev_db);
+       for (i = retry; i--;) {
+               if (!ioread8(&dc->used_address_updated))
+                       break;
+               msleep(100);
+       };
+
+       dev_dbg(mic_dev(mvdev), "%s: retry: %d\n", __func__, retry);
+       if (!retry) {
+               err = -ENODEV;
+               goto error;
+       }
+
+       return 0;
+error:
+       mic_del_vqs(vdev);
+       return err;
+}
+
+/*
+ * The config ops structure as defined by virtio config
+ */
+static struct virtio_config_ops mic_vq_config_ops = {
+       .get_features = mic_get_features,
+       .finalize_features = mic_finalize_features,
+       .get = mic_get,
+       .set = mic_set,
+       .get_status = mic_get_status,
+       .set_status = mic_set_status,
+       .reset = mic_reset,
+       .find_vqs = mic_find_vqs,
+       .del_vqs = mic_del_vqs,
+};
+
+static irqreturn_t
+mic_virtio_intr_handler(int irq, void *data)
+{
+       struct mic_vdev *mvdev = data;
+       struct virtqueue *vq;
+
+       mic_ack_interrupt(mvdev->mdev);
+       list_for_each_entry(vq, &mvdev->vdev.vqs, list)
+               vring_interrupt(0, vq);
+
+       return IRQ_HANDLED;
+}
+
+static void mic_virtio_release_dev(struct device *_d)
+{
+       /*
+        * No need for a release method similar to virtio PCI.
+        * Provide an empty one to avoid getting a warning from core.
+        */
+}
+
+/*
+ * adds a new device and register it with virtio
+ * appropriate drivers are loaded by the device model
+ */
+static int mic_add_device(struct mic_device_desc __iomem *d,
+       unsigned int offset, struct mic_driver *mdrv)
+{
+       struct mic_vdev *mvdev;
+       int ret;
+       int virtio_db;
+       u8 type = ioread8(&d->type);
+
+       mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL);
+       if (!mvdev) {
+               dev_err(mdrv->dev, "Cannot allocate mic dev %u type %u\n",
+                       offset, type);
+               return -ENOMEM;
+       }
+
+       mvdev->mdev = &mdrv->mdev;
+       mvdev->vdev.dev.parent = mdrv->dev;
+       mvdev->vdev.dev.release = mic_virtio_release_dev;
+       mvdev->vdev.id.device = type;
+       mvdev->vdev.config = &mic_vq_config_ops;
+       mvdev->desc = d;
+       mvdev->dc = (void __iomem *)d + mic_aligned_desc_size(d);
+       init_completion(&mvdev->reset_done);
+
+       virtio_db = mic_next_card_db();
+       mvdev->virtio_cookie = mic_request_card_irq(mic_virtio_intr_handler,
+                       "virtio intr", mvdev, virtio_db);
+       if (IS_ERR(mvdev->virtio_cookie)) {
+               ret = PTR_ERR(mvdev->virtio_cookie);
+               goto kfree;
+       }
+       iowrite8((u8)virtio_db, &mvdev->dc->h2c_vdev_db);
+       mvdev->c2h_vdev_db = ioread8(&mvdev->dc->c2h_vdev_db);
+
+       ret = register_virtio_device(&mvdev->vdev);
+       if (ret) {
+               dev_err(mic_dev(mvdev),
+                       "Failed to register mic device %u type %u\n",
+                       offset, type);
+               goto free_irq;
+       }
+       iowrite64((u64)mvdev, &mvdev->dc->vdev);
+       dev_dbg(mic_dev(mvdev), "%s: registered mic device %u type %u mvdev %p\n",
+               __func__, offset, type, mvdev);
+
+       return 0;
+
+free_irq:
+       mic_free_card_irq(mvdev->virtio_cookie, mvdev);
+kfree:
+       kfree(mvdev);
+       return ret;
+}
+
+/*
+ * match for a mic device with a specific desc pointer
+ */
+static int mic_match_desc(struct device *dev, void *data)
+{
+       struct virtio_device *vdev = dev_to_virtio(dev);
+       struct mic_vdev *mvdev = to_micvdev(vdev);
+
+       return mvdev->desc == (void __iomem *)data;
+}
+
+static void mic_handle_config_change(struct mic_device_desc __iomem *d,
+       unsigned int offset, struct mic_driver *mdrv)
+{
+       struct mic_device_ctrl __iomem *dc
+               = (void __iomem *)d + mic_aligned_desc_size(d);
+       struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev);
+       struct virtio_driver *drv;
+
+       if (ioread8(&dc->config_change) != MIC_VIRTIO_PARAM_CONFIG_CHANGED)
+               return;
+
+       dev_dbg(mdrv->dev, "%s %d\n", __func__, __LINE__);
+       drv = container_of(mvdev->vdev.dev.driver,
+                               struct virtio_driver, driver);
+       if (drv->config_changed)
+               drv->config_changed(&mvdev->vdev);
+       iowrite8(1, &dc->guest_ack);
+}
+
+/*
+ * removes a virtio device if a hot remove event has been
+ * requested by the host.
+ */
+static int mic_remove_device(struct mic_device_desc __iomem *d,
+       unsigned int offset, struct mic_driver *mdrv)
+{
+       struct mic_device_ctrl __iomem *dc
+               = (void __iomem *)d + mic_aligned_desc_size(d);
+       struct mic_vdev *mvdev = (struct mic_vdev *)ioread64(&dc->vdev);
+       u8 status;
+       int ret = -1;
+
+       if (ioread8(&dc->config_change) == MIC_VIRTIO_PARAM_DEV_REMOVE) {
+               dev_dbg(mdrv->dev,
+                       "%s %d config_change %d type %d mvdev %p\n",
+                       __func__, __LINE__,
+                       ioread8(&dc->config_change), ioread8(&d->type), mvdev);
+
+               status = ioread8(&d->status);
+               INIT_COMPLETION(mvdev->reset_done);
+               unregister_virtio_device(&mvdev->vdev);
+               mic_free_card_irq(mvdev->virtio_cookie, mvdev);
+               if (status & VIRTIO_CONFIG_S_DRIVER_OK)
+                       wait_for_completion(&mvdev->reset_done);
+               kfree(mvdev);
+               iowrite8(1, &dc->guest_ack);
+               dev_dbg(mdrv->dev, "%s %d guest_ack %d\n",
+                       __func__, __LINE__, ioread8(&dc->guest_ack));
+               ret = 0;
+       }
+
+       return ret;
+}
+
+#define REMOVE_DEVICES true
+
+static void mic_scan_devices(struct mic_driver *mdrv, bool remove)
+{
+       s8 type;
+       unsigned int i;
+       struct mic_device_desc __iomem *d;
+       struct mic_device_ctrl __iomem *dc;
+       struct device *dev;
+       int ret;
+
+       for (i = mic_aligned_size(struct mic_bootparam);
+               i < MIC_DP_SIZE; i += mic_total_desc_size(d)) {
+               d = mdrv->dp + i;
+               dc = (void __iomem *)d + mic_aligned_desc_size(d);
+               /*
+                * This read barrier is paired with the corresponding write
+                * barrier on the host which is inserted before adding or
+                * removing a virtio device descriptor, by updating the type.
+                */
+               rmb();
+               type = ioread8(&d->type);
+
+               /* end of list */
+               if (type == 0)
+                       break;
+
+               if (type == -1)
+                       continue;
+
+               /* device already exists */
+               dev = device_find_child(mdrv->dev, d, mic_match_desc);
+               if (dev) {
+                       if (remove)
+                               iowrite8(MIC_VIRTIO_PARAM_DEV_REMOVE,
+                                        &dc->config_change);
+                       put_device(dev);
+                       mic_handle_config_change(d, i, mdrv);
+                       ret = mic_remove_device(d, i, mdrv);
+                       if (!ret && !remove)
+                               iowrite8(-1, &d->type);
+                       if (remove) {
+                               iowrite8(0, &dc->config_change);
+                               iowrite8(0, &dc->guest_ack);
+                       }
+                       continue;
+               }
+
+               /* new device */
+               dev_dbg(mdrv->dev, "%s %d Adding new virtio device %p\n",
+                       __func__, __LINE__, d);
+               if (!remove)
+                       mic_add_device(d, i, mdrv);
+       }
+}
+
+/*
+ * mic_hotplug_device tries to find changes in the device page.
+ */
+static void mic_hotplug_devices(struct work_struct *work)
+{
+       struct mic_driver *mdrv = container_of(work,
+               struct mic_driver, hotplug_work);
+
+       mic_scan_devices(mdrv, !REMOVE_DEVICES);
+}
+
+/*
+ * Interrupt handler for hot plug/config changes etc.
+ */
+static irqreturn_t
+mic_extint_handler(int irq, void *data)
+{
+       struct mic_driver *mdrv = (struct mic_driver *)data;
+
+       dev_dbg(mdrv->dev, "%s %d hotplug work\n",
+               __func__, __LINE__);
+       mic_ack_interrupt(&mdrv->mdev);
+       schedule_work(&mdrv->hotplug_work);
+       return IRQ_HANDLED;
+}
+
+/*
+ * Init function for virtio
+ */
+int mic_devices_init(struct mic_driver *mdrv)
+{
+       int rc;
+       struct mic_bootparam __iomem *bootparam;
+       int config_db;
+
+       INIT_WORK(&mdrv->hotplug_work, mic_hotplug_devices);
+       mic_scan_devices(mdrv, !REMOVE_DEVICES);
+
+       config_db = mic_next_card_db();
+       virtio_config_cookie = mic_request_card_irq(mic_extint_handler,
+                       "virtio_config_intr", mdrv, config_db);
+       if (IS_ERR(virtio_config_cookie)) {
+               rc = PTR_ERR(virtio_config_cookie);
+               goto exit;
+       }
+
+       bootparam = mdrv->dp;
+       iowrite8(config_db, &bootparam->h2c_config_db);
+       return 0;
+exit:
+       return rc;
+}
+
+/*
+ * Uninit function for virtio
+ */
+void mic_devices_uninit(struct mic_driver *mdrv)
+{
+       struct mic_bootparam __iomem *bootparam = mdrv->dp;
+       iowrite8(-1, &bootparam->h2c_config_db);
+       mic_free_card_irq(virtio_config_cookie, mdrv);
+       flush_work(&mdrv->hotplug_work);
+       mic_scan_devices(mdrv, REMOVE_DEVICES);
+}
diff --git a/drivers/misc/mic/card/mic_virtio.h b/drivers/misc/mic/card/mic_virtio.h
new file mode 100644 (file)
index 0000000..2c5c22c
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Disclaimer: The codes contained in these modules may be specific to
+ * the Intel Software Development Platform codenamed: Knights Ferry, and
+ * the Intel product codenamed: Knights Corner, and are not backward
+ * compatible with other Intel products. Additionally, Intel will NOT
+ * support the codes or instruction set in future products.
+ *
+ * Intel MIC Card driver.
+ *
+ */
+#ifndef __MIC_CARD_VIRTIO_H
+#define __MIC_CARD_VIRTIO_H
+
+#include <linux/mic_common.h>
+#include "mic_device.h"
+
+/*
+ * 64 bit I/O access
+ */
+#ifndef ioread64
+#define ioread64 readq
+#endif
+#ifndef iowrite64
+#define iowrite64 writeq
+#endif
+
+static inline unsigned mic_desc_size(struct mic_device_desc __iomem *desc)
+{
+       return mic_aligned_size(*desc)
+               + ioread8(&desc->num_vq) * mic_aligned_size(struct mic_vqconfig)
+               + ioread8(&desc->feature_len) * 2
+               + ioread8(&desc->config_len);
+}
+
+static inline struct mic_vqconfig __iomem *
+mic_vq_config(struct mic_device_desc __iomem *desc)
+{
+       return (struct mic_vqconfig __iomem *)(desc + 1);
+}
+
+static inline __u8 __iomem *
+mic_vq_features(struct mic_device_desc __iomem *desc)
+{
+       return (__u8 __iomem *)(mic_vq_config(desc) + ioread8(&desc->num_vq));
+}
+
+static inline __u8 __iomem *
+mic_vq_configspace(struct mic_device_desc __iomem *desc)
+{
+       return mic_vq_features(desc) + ioread8(&desc->feature_len) * 2;
+}
+static inline unsigned mic_total_desc_size(struct mic_device_desc __iomem *desc)
+{
+       return mic_aligned_desc_size(desc) +
+               mic_aligned_size(struct mic_device_ctrl);
+}
+
+int mic_devices_init(struct mic_driver *mdrv);
+void mic_devices_uninit(struct mic_driver *mdrv);
+
+#endif
diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c
new file mode 100644 (file)
index 0000000..2868945
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Disclaimer: The codes contained in these modules may be specific to
+ * the Intel Software Development Platform codenamed: Knights Ferry, and
+ * the Intel product codenamed: Knights Corner, and are not backward
+ * compatible with other Intel products. Additionally, Intel will NOT
+ * support the codes or instruction set in future products.
+ *
+ * Intel MIC Card driver.
+ *
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_x100.h"
+
+static const char mic_driver_name[] = "mic";
+
+static struct mic_driver g_drv;
+
+/**
+ * mic_read_spad - read from the scratchpad register
+ * @mdev: pointer to mic_device instance
+ * @idx: index to scratchpad register, 0 based
+ *
+ * This function allows reading of the 32bit scratchpad register.
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+u32 mic_read_spad(struct mic_device *mdev, unsigned int idx)
+{
+       return mic_mmio_read(&mdev->mmio,
+               MIC_X100_SBOX_BASE_ADDRESS +
+               MIC_X100_SBOX_SPAD0 + idx * 4);
+}
+
+/**
+ * __mic_send_intr - Send interrupt to Host.
+ * @mdev: pointer to mic_device instance
+ * @doorbell: Doorbell number.
+ */
+void mic_send_intr(struct mic_device *mdev, int doorbell)
+{
+       struct mic_mw *mw = &mdev->mmio;
+
+       if (doorbell > MIC_X100_MAX_DOORBELL_IDX)
+               return;
+       /* Ensure that the interrupt is ordered w.r.t previous stores. */
+       wmb();
+       mic_mmio_write(mw, MIC_X100_SBOX_SDBIC0_DBREQ_BIT,
+                      MIC_X100_SBOX_BASE_ADDRESS +
+                      (MIC_X100_SBOX_SDBIC0 + (4 * doorbell)));
+}
+
+/**
+ * mic_ack_interrupt - Device specific interrupt handling.
+ * @mdev: pointer to mic_device instance
+ *
+ * Returns: bitmask of doorbell events triggered.
+ */
+u32 mic_ack_interrupt(struct mic_device *mdev)
+{
+       return 0;
+}
+
+static inline int mic_get_sbox_irq(int db)
+{
+       return MIC_X100_IRQ_BASE + db;
+}
+
+static inline int mic_get_rdmasr_irq(int index)
+{
+       return  MIC_X100_RDMASR_IRQ_BASE + index;
+}
+
+/**
+ * mic_hw_intr_init - Initialize h/w specific interrupt
+ * information.
+ * @mdrv: pointer to mic_driver
+ */
+void mic_hw_intr_init(struct mic_driver *mdrv)
+{
+       mdrv->intr_info.num_intr = MIC_X100_NUM_SBOX_IRQ +
+                               MIC_X100_NUM_RDMASR_IRQ;
+}
+
+/**
+ * mic_db_to_irq - Retrieve irq number corresponding to a doorbell.
+ * @mdrv: pointer to mic_driver
+ * @db: The doorbell obtained for which the irq is needed. Doorbell
+ * may correspond to an sbox doorbell or an rdmasr index.
+ *
+ * Returns the irq corresponding to the doorbell.
+ */
+int mic_db_to_irq(struct mic_driver *mdrv, int db)
+{
+       int rdmasr_index;
+       if (db < MIC_X100_NUM_SBOX_IRQ) {
+               return mic_get_sbox_irq(db);
+       } else {
+               rdmasr_index = db - MIC_X100_NUM_SBOX_IRQ +
+                       MIC_X100_RDMASR_IRQ_BASE;
+               return mic_get_rdmasr_irq(rdmasr_index);
+       }
+}
+
+/*
+ * mic_card_map - Allocate virtual address for a remote memory region.
+ * @mdev: pointer to mic_device instance.
+ * @addr: Remote DMA address.
+ * @size: Size of the region.
+ *
+ * Returns: Virtual address backing the remote memory region.
+ */
+void __iomem *
+mic_card_map(struct mic_device *mdev, dma_addr_t addr, size_t size)
+{
+       return ioremap(addr, size);
+}
+
+/*
+ * mic_card_unmap - Unmap the virtual address for a remote memory region.
+ * @mdev: pointer to mic_device instance.
+ * @addr: Virtual address for remote memory region.
+ *
+ * Returns: None.
+ */
+void mic_card_unmap(struct mic_device *mdev, void __iomem *addr)
+{
+       iounmap(addr);
+}
+
+static int __init mic_probe(struct platform_device *pdev)
+{
+       struct mic_driver *mdrv = &g_drv;
+       struct mic_device *mdev = &mdrv->mdev;
+       int rc = 0;
+
+       mdrv->dev = &pdev->dev;
+       snprintf(mdrv->name, sizeof(mic_driver_name), mic_driver_name);
+
+       mdev->mmio.pa = MIC_X100_MMIO_BASE;
+       mdev->mmio.len = MIC_X100_MMIO_LEN;
+       mdev->mmio.va = ioremap(MIC_X100_MMIO_BASE, MIC_X100_MMIO_LEN);
+       if (!mdev->mmio.va) {
+               dev_err(&pdev->dev, "Cannot remap MMIO BAR\n");
+               rc = -EIO;
+               goto done;
+       }
+       mic_hw_intr_init(mdrv);
+       rc = mic_driver_init(mdrv);
+       if (rc) {
+               dev_err(&pdev->dev, "mic_driver_init failed rc %d\n", rc);
+               goto iounmap;
+       }
+done:
+       return rc;
+iounmap:
+       iounmap(mdev->mmio.va);
+       return rc;
+}
+
+static int mic_remove(struct platform_device *pdev)
+{
+       struct mic_driver *mdrv = &g_drv;
+       struct mic_device *mdev = &mdrv->mdev;
+
+       mic_driver_uninit(mdrv);
+       iounmap(mdev->mmio.va);
+       return 0;
+}
+
+static void mic_platform_shutdown(struct platform_device *pdev)
+{
+       mic_remove(pdev);
+}
+
+static struct platform_device mic_platform_dev = {
+       .name = mic_driver_name,
+       .id   = 0,
+       .num_resources = 0,
+};
+
+static struct platform_driver __refdata mic_platform_driver = {
+       .probe = mic_probe,
+       .remove = mic_remove,
+       .shutdown = mic_platform_shutdown,
+       .driver         = {
+               .name   = mic_driver_name,
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init mic_init(void)
+{
+       int ret;
+       struct cpuinfo_x86 *c = &cpu_data(0);
+
+       if (!(c->x86 == 11 && c->x86_model == 1)) {
+               ret = -ENODEV;
+               pr_err("%s not running on X100 ret %d\n", __func__, ret);
+               goto done;
+       }
+
+       mic_init_card_debugfs();
+       ret = platform_device_register(&mic_platform_dev);
+       if (ret) {
+               pr_err("platform_device_register ret %d\n", ret);
+               goto cleanup_debugfs;
+       }
+       ret = platform_driver_register(&mic_platform_driver);
+       if (ret) {
+               pr_err("platform_driver_register ret %d\n", ret);
+               goto device_unregister;
+       }
+       return ret;
+
+device_unregister:
+       platform_device_unregister(&mic_platform_dev);
+cleanup_debugfs:
+       mic_exit_card_debugfs();
+done:
+       return ret;
+}
+
+static void __exit mic_exit(void)
+{
+       platform_driver_unregister(&mic_platform_driver);
+       platform_device_unregister(&mic_platform_dev);
+       mic_exit_card_debugfs();
+}
+
+module_init(mic_init);
+module_exit(mic_exit);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("Intel(R) MIC X100 Card driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/mic/card/mic_x100.h b/drivers/misc/mic/card/mic_x100.h
new file mode 100644 (file)
index 0000000..d66ea55
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Disclaimer: The codes contained in these modules may be specific to
+ * the Intel Software Development Platform codenamed: Knights Ferry, and
+ * the Intel product codenamed: Knights Corner, and are not backward
+ * compatible with other Intel products. Additionally, Intel will NOT
+ * support the codes or instruction set in future products.
+ *
+ * Intel MIC Card driver.
+ *
+ */
+#ifndef _MIC_X100_CARD_H_
+#define _MIC_X100_CARD_H_
+
+#define MIC_X100_MMIO_BASE 0x08007C0000ULL
+#define MIC_X100_MMIO_LEN 0x00020000ULL
+#define MIC_X100_SBOX_BASE_ADDRESS 0x00010000ULL
+
+#define MIC_X100_SBOX_SPAD0 0x0000AB20
+#define MIC_X100_SBOX_SDBIC0 0x0000CC90
+#define MIC_X100_SBOX_SDBIC0_DBREQ_BIT 0x80000000
+#define MIC_X100_SBOX_RDMASR0  0x0000B180
+
+#define MIC_X100_MAX_DOORBELL_IDX 8
+
+#define MIC_X100_NUM_SBOX_IRQ 8
+#define MIC_X100_NUM_RDMASR_IRQ 8
+#define MIC_X100_SBOX_IRQ_BASE 0
+#define MIC_X100_RDMASR_IRQ_BASE 17
+
+#define MIC_X100_IRQ_BASE 26
+
+#endif
diff --git a/drivers/misc/mic/common/mic_dev.h b/drivers/misc/mic/common/mic_dev.h
new file mode 100644 (file)
index 0000000..92999c2
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC driver.
+ *
+ */
+#ifndef __MIC_DEV_H__
+#define __MIC_DEV_H__
+
+/**
+ * struct mic_mw - MIC memory window
+ *
+ * @pa: Base physical address.
+ * @va: Base ioremap'd virtual address.
+ * @len: Size of the memory window.
+ */
+struct mic_mw {
+       phys_addr_t pa;
+       void __iomem *va;
+       resource_size_t len;
+};
+
+/*
+ * Scratch pad register offsets used by the host to communicate
+ * device page DMA address to the card.
+ */
+#define MIC_DPLO_SPAD 14
+#define MIC_DPHI_SPAD 15
+
+/*
+ * These values are supposed to be in the config_change field of the
+ * device page when the host sends a config change interrupt to the card.
+ */
+#define MIC_VIRTIO_PARAM_DEV_REMOVE 0x1
+#define MIC_VIRTIO_PARAM_CONFIG_CHANGED 0x2
+
+#endif
diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile
new file mode 100644 (file)
index 0000000..c2197f9
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# Makefile - Intel MIC Linux driver.
+# Copyright(c) 2013, Intel Corporation.
+#
+obj-$(CONFIG_INTEL_MIC_HOST) += mic_host.o
+mic_host-objs := mic_main.o
+mic_host-objs += mic_x100.o
+mic_host-objs += mic_sysfs.o
+mic_host-objs += mic_smpt.o
+mic_host-objs += mic_intr.o
+mic_host-objs += mic_boot.o
+mic_host-objs += mic_debugfs.o
+mic_host-objs += mic_fops.o
+mic_host-objs += mic_virtio.o
diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c
new file mode 100644 (file)
index 0000000..d56b8bf
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/delay.h>
+#include <linux/firmware.h>
+
+#include <linux/mic_common.h>
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_smpt.h"
+#include "mic_virtio.h"
+
+/**
+ * mic_reset - Reset the MIC device.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_reset(struct mic_device *mdev)
+{
+       int i;
+
+#define MIC_RESET_TO (45)
+
+       INIT_COMPLETION(mdev->reset_wait);
+       mdev->ops->reset_fw_ready(mdev);
+       mdev->ops->reset(mdev);
+
+       for (i = 0; i < MIC_RESET_TO; i++) {
+               if (mdev->ops->is_fw_ready(mdev))
+                       goto done;
+               /*
+                * Resets typically take 10s of seconds to complete.
+                * Since an MMIO read is required to check if the
+                * firmware is ready or not, a 1 second delay works nicely.
+                */
+               msleep(1000);
+       }
+       mic_set_state(mdev, MIC_RESET_FAILED);
+done:
+       complete_all(&mdev->reset_wait);
+}
+
+/* Initialize the MIC bootparams */
+void mic_bootparam_init(struct mic_device *mdev)
+{
+       struct mic_bootparam *bootparam = mdev->dp;
+
+       bootparam->magic = MIC_MAGIC;
+       bootparam->c2h_shutdown_db = mdev->shutdown_db;
+       bootparam->h2c_shutdown_db = -1;
+       bootparam->h2c_config_db = -1;
+       bootparam->shutdown_status = 0;
+       bootparam->shutdown_card = 0;
+}
+
+/**
+ * mic_start - Start the MIC.
+ * @mdev: pointer to mic_device instance
+ * @buf: buffer containing boot string including firmware/ramdisk path.
+ *
+ * This function prepares an MIC for boot and initiates boot.
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+int mic_start(struct mic_device *mdev, const char *buf)
+{
+       int rc;
+       mutex_lock(&mdev->mic_mutex);
+retry:
+       if (MIC_OFFLINE != mdev->state) {
+               rc = -EINVAL;
+               goto unlock_ret;
+       }
+       if (!mdev->ops->is_fw_ready(mdev)) {
+               mic_reset(mdev);
+               /*
+                * The state will either be MIC_OFFLINE if the reset succeeded
+                * or MIC_RESET_FAILED if the firmware reset failed.
+                */
+               goto retry;
+       }
+       rc = mdev->ops->load_mic_fw(mdev, buf);
+       if (rc)
+               goto unlock_ret;
+       mic_smpt_restore(mdev);
+       mic_intr_restore(mdev);
+       mdev->intr_ops->enable_interrupts(mdev);
+       mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr);
+       mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32);
+       mdev->ops->send_firmware_intr(mdev);
+       mic_set_state(mdev, MIC_ONLINE);
+unlock_ret:
+       mutex_unlock(&mdev->mic_mutex);
+       return rc;
+}
+
+/**
+ * mic_stop - Prepare the MIC for reset and trigger reset.
+ * @mdev: pointer to mic_device instance
+ * @force: force a MIC to reset even if it is already offline.
+ *
+ * RETURNS: None.
+ */
+void mic_stop(struct mic_device *mdev, bool force)
+{
+       mutex_lock(&mdev->mic_mutex);
+       if (MIC_OFFLINE != mdev->state || force) {
+               mic_virtio_reset_devices(mdev);
+               mic_bootparam_init(mdev);
+               mic_reset(mdev);
+               if (MIC_RESET_FAILED == mdev->state)
+                       goto unlock;
+               mic_set_shutdown_status(mdev, MIC_NOP);
+               if (MIC_SUSPENDED != mdev->state)
+                       mic_set_state(mdev, MIC_OFFLINE);
+       }
+unlock:
+       mutex_unlock(&mdev->mic_mutex);
+}
+
+/**
+ * mic_shutdown - Initiate MIC shutdown.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: None.
+ */
+void mic_shutdown(struct mic_device *mdev)
+{
+       struct mic_bootparam *bootparam = mdev->dp;
+       s8 db = bootparam->h2c_shutdown_db;
+
+       mutex_lock(&mdev->mic_mutex);
+       if (MIC_ONLINE == mdev->state && db != -1) {
+               bootparam->shutdown_card = 1;
+               mdev->ops->send_intr(mdev, db);
+               mic_set_state(mdev, MIC_SHUTTING_DOWN);
+       }
+       mutex_unlock(&mdev->mic_mutex);
+}
+
+/**
+ * mic_shutdown_work - Handle shutdown interrupt from MIC.
+ * @work: The work structure.
+ *
+ * This work is scheduled whenever the host has received a shutdown
+ * interrupt from the MIC.
+ */
+void mic_shutdown_work(struct work_struct *work)
+{
+       struct mic_device *mdev = container_of(work, struct mic_device,
+                       shutdown_work);
+       struct mic_bootparam *bootparam = mdev->dp;
+
+       mutex_lock(&mdev->mic_mutex);
+       mic_set_shutdown_status(mdev, bootparam->shutdown_status);
+       bootparam->shutdown_status = 0;
+
+       /*
+        * if state is MIC_SUSPENDED, OSPM suspend is in progress. We do not
+        * change the state here so as to prevent users from booting the card
+        * during and after the suspend operation.
+        */
+       if (MIC_SHUTTING_DOWN != mdev->state &&
+           MIC_SUSPENDED != mdev->state)
+               mic_set_state(mdev, MIC_SHUTTING_DOWN);
+       mutex_unlock(&mdev->mic_mutex);
+}
+
+/**
+ * mic_reset_trigger_work - Trigger MIC reset.
+ * @work: The work structure.
+ *
+ * This work is scheduled whenever the host wants to reset the MIC.
+ */
+void mic_reset_trigger_work(struct work_struct *work)
+{
+       struct mic_device *mdev = container_of(work, struct mic_device,
+                       reset_trigger_work);
+
+       mic_stop(mdev, false);
+}
+
+/**
+ * mic_complete_resume - Complete MIC Resume after an OSPM suspend/hibernate
+ * event.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: None.
+ */
+void mic_complete_resume(struct mic_device *mdev)
+{
+       if (mdev->state != MIC_SUSPENDED) {
+               dev_warn(mdev->sdev->parent, "state %d should be %d\n",
+                        mdev->state, MIC_SUSPENDED);
+               return;
+       }
+
+       /* Make sure firmware is ready */
+       if (!mdev->ops->is_fw_ready(mdev))
+               mic_stop(mdev, true);
+
+       mutex_lock(&mdev->mic_mutex);
+       mic_set_state(mdev, MIC_OFFLINE);
+       mutex_unlock(&mdev->mic_mutex);
+}
+
+/**
+ * mic_prepare_suspend - Handle suspend notification for the MIC device.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: None.
+ */
+void mic_prepare_suspend(struct mic_device *mdev)
+{
+       int rc;
+
+#define MIC_SUSPEND_TIMEOUT (60 * HZ)
+
+       mutex_lock(&mdev->mic_mutex);
+       switch (mdev->state) {
+       case MIC_OFFLINE:
+               /*
+                * Card is already offline. Set state to MIC_SUSPENDED
+                * to prevent users from booting the card.
+                */
+               mic_set_state(mdev, MIC_SUSPENDED);
+               mutex_unlock(&mdev->mic_mutex);
+               break;
+       case MIC_ONLINE:
+               /*
+                * Card is online. Set state to MIC_SUSPENDING and notify
+                * MIC user space daemon which will issue card
+                * shutdown and reset.
+                */
+               mic_set_state(mdev, MIC_SUSPENDING);
+               mutex_unlock(&mdev->mic_mutex);
+               rc = wait_for_completion_timeout(&mdev->reset_wait,
+                                               MIC_SUSPEND_TIMEOUT);
+               /* Force reset the card if the shutdown completion timed out */
+               if (!rc) {
+                       mutex_lock(&mdev->mic_mutex);
+                       mic_set_state(mdev, MIC_SUSPENDED);
+                       mutex_unlock(&mdev->mic_mutex);
+                       mic_stop(mdev, true);
+               }
+               break;
+       case MIC_SHUTTING_DOWN:
+               /*
+                * Card is shutting down. Set state to MIC_SUSPENDED
+                * to prevent further boot of the card.
+                */
+               mic_set_state(mdev, MIC_SUSPENDED);
+               mutex_unlock(&mdev->mic_mutex);
+               rc = wait_for_completion_timeout(&mdev->reset_wait,
+                                               MIC_SUSPEND_TIMEOUT);
+               /* Force reset the card if the shutdown completion timed out */
+               if (!rc)
+                       mic_stop(mdev, true);
+               break;
+       default:
+               mutex_unlock(&mdev->mic_mutex);
+               break;
+       }
+}
+
+/**
+ * mic_suspend - Initiate MIC suspend. Suspend merely issues card shutdown.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: None.
+ */
+void mic_suspend(struct mic_device *mdev)
+{
+       struct mic_bootparam *bootparam = mdev->dp;
+       s8 db = bootparam->h2c_shutdown_db;
+
+       mutex_lock(&mdev->mic_mutex);
+       if (MIC_SUSPENDING == mdev->state && db != -1) {
+               bootparam->shutdown_card = 1;
+               mdev->ops->send_intr(mdev, db);
+               mic_set_state(mdev, MIC_SUSPENDED);
+       }
+       mutex_unlock(&mdev->mic_mutex);
+}
diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c
new file mode 100644 (file)
index 0000000..028ba5d
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/debugfs.h>
+#include <linux/pci.h>
+#include <linux/seq_file.h>
+
+#include <linux/mic_common.h>
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_smpt.h"
+#include "mic_virtio.h"
+
+/* Debugfs parent dir */
+static struct dentry *mic_dbg;
+
+/**
+ * mic_log_buf_show - Display MIC kernel log buffer.
+ *
+ * log_buf addr/len is read from System.map by user space
+ * and populated in sysfs entries.
+ */
+static int mic_log_buf_show(struct seq_file *s, void *unused)
+{
+       void __iomem *log_buf_va;
+       int __iomem *log_buf_len_va;
+       struct mic_device *mdev = s->private;
+       void *kva;
+       int size;
+       unsigned long aper_offset;
+
+       if (!mdev || !mdev->log_buf_addr || !mdev->log_buf_len)
+               goto done;
+       /*
+        * Card kernel will never be relocated and any kernel text/data mapping
+        * can be translated to phys address by subtracting __START_KERNEL_map.
+        */
+       aper_offset = (unsigned long)mdev->log_buf_len - __START_KERNEL_map;
+       log_buf_len_va = mdev->aper.va + aper_offset;
+       aper_offset = (unsigned long)mdev->log_buf_addr - __START_KERNEL_map;
+       log_buf_va = mdev->aper.va + aper_offset;
+       size = ioread32(log_buf_len_va);
+
+       kva = kmalloc(size, GFP_KERNEL);
+       if (!kva)
+               goto done;
+       mutex_lock(&mdev->mic_mutex);
+       memcpy_fromio(kva, log_buf_va, size);
+       switch (mdev->state) {
+       case MIC_ONLINE:
+               /* Fall through */
+       case MIC_SHUTTING_DOWN:
+               seq_write(s, kva, size);
+               break;
+       default:
+               break;
+       }
+       mutex_unlock(&mdev->mic_mutex);
+       kfree(kva);
+done:
+       return 0;
+}
+
+static int mic_log_buf_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_log_buf_show, inode->i_private);
+}
+
+static int mic_log_buf_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations log_buf_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_log_buf_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_log_buf_release
+};
+
+static int mic_smpt_show(struct seq_file *s, void *pos)
+{
+       int i;
+       struct mic_device *mdev = s->private;
+       unsigned long flags;
+
+       seq_printf(s, "MIC %-2d |%-10s| %-14s %-10s\n",
+                  mdev->id, "SMPT entry", "SW DMA addr", "RefCount");
+       seq_puts(s, "====================================================\n");
+
+       if (mdev->smpt) {
+               struct mic_smpt_info *smpt_info = mdev->smpt;
+               spin_lock_irqsave(&smpt_info->smpt_lock, flags);
+               for (i = 0; i < smpt_info->info.num_reg; i++) {
+                       seq_printf(s, "%9s|%-10d| %-#14llx %-10lld\n",
+                                  " ",  i, smpt_info->entry[i].dma_addr,
+                                  smpt_info->entry[i].ref_count);
+               }
+               spin_unlock_irqrestore(&smpt_info->smpt_lock, flags);
+       }
+       seq_puts(s, "====================================================\n");
+       return 0;
+}
+
+static int mic_smpt_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_smpt_show, inode->i_private);
+}
+
+static int mic_smpt_debug_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations smpt_file_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_smpt_debug_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_smpt_debug_release
+};
+
+static int mic_soft_reset_show(struct seq_file *s, void *pos)
+{
+       struct mic_device *mdev = s->private;
+
+       mic_stop(mdev, true);
+       return 0;
+}
+
+static int mic_soft_reset_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_soft_reset_show, inode->i_private);
+}
+
+static int mic_soft_reset_debug_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations soft_reset_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_soft_reset_debug_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_soft_reset_debug_release
+};
+
+static int mic_post_code_show(struct seq_file *s, void *pos)
+{
+       struct mic_device *mdev = s->private;
+       u32 reg = mdev->ops->get_postcode(mdev);
+
+       seq_printf(s, "%c%c", reg & 0xff, (reg >> 8) & 0xff);
+       return 0;
+}
+
+static int mic_post_code_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_post_code_show, inode->i_private);
+}
+
+static int mic_post_code_debug_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations post_code_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_post_code_debug_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_post_code_debug_release
+};
+
+static int mic_dp_show(struct seq_file *s, void *pos)
+{
+       struct mic_device *mdev = s->private;
+       struct mic_device_desc *d;
+       struct mic_device_ctrl *dc;
+       struct mic_vqconfig *vqconfig;
+       __u32 *features;
+       __u8 *config;
+       struct mic_bootparam *bootparam = mdev->dp;
+       int i, j;
+
+       seq_printf(s, "Bootparam: magic 0x%x\n",
+                  bootparam->magic);
+       seq_printf(s, "Bootparam: h2c_shutdown_db %d\n",
+                  bootparam->h2c_shutdown_db);
+       seq_printf(s, "Bootparam: h2c_config_db %d\n",
+                  bootparam->h2c_config_db);
+       seq_printf(s, "Bootparam: c2h_shutdown_db %d\n",
+                  bootparam->c2h_shutdown_db);
+       seq_printf(s, "Bootparam: shutdown_status %d\n",
+                  bootparam->shutdown_status);
+       seq_printf(s, "Bootparam: shutdown_card %d\n",
+                  bootparam->shutdown_card);
+
+       for (i = sizeof(*bootparam); i < MIC_DP_SIZE;
+            i += mic_total_desc_size(d)) {
+               d = mdev->dp + i;
+               dc = (void *)d + mic_aligned_desc_size(d);
+
+               /* end of list */
+               if (d->type == 0)
+                       break;
+
+               if (d->type == -1)
+                       continue;
+
+               seq_printf(s, "Type %d ", d->type);
+               seq_printf(s, "Num VQ %d ", d->num_vq);
+               seq_printf(s, "Feature Len %d\n", d->feature_len);
+               seq_printf(s, "Config Len %d ", d->config_len);
+               seq_printf(s, "Shutdown Status %d\n", d->status);
+
+               for (j = 0; j < d->num_vq; j++) {
+                       vqconfig = mic_vq_config(d) + j;
+                       seq_printf(s, "vqconfig[%d]: ", j);
+                       seq_printf(s, "address 0x%llx ", vqconfig->address);
+                       seq_printf(s, "num %d ", vqconfig->num);
+                       seq_printf(s, "used address 0x%llx\n",
+                                  vqconfig->used_address);
+               }
+
+               features = (__u32 *)mic_vq_features(d);
+               seq_printf(s, "Features: Host 0x%x ", features[0]);
+               seq_printf(s, "Guest 0x%x\n", features[1]);
+
+               config = mic_vq_configspace(d);
+               for (j = 0; j < d->config_len; j++)
+                       seq_printf(s, "config[%d]=%d\n", j, config[j]);
+
+               seq_puts(s, "Device control:\n");
+               seq_printf(s, "Config Change %d ", dc->config_change);
+               seq_printf(s, "Vdev reset %d\n", dc->vdev_reset);
+               seq_printf(s, "Guest Ack %d ", dc->guest_ack);
+               seq_printf(s, "Host ack %d\n", dc->host_ack);
+               seq_printf(s, "Used address updated %d ",
+                          dc->used_address_updated);
+               seq_printf(s, "Vdev 0x%llx\n", dc->vdev);
+               seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db);
+               seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db);
+       }
+
+       return 0;
+}
+
+static int mic_dp_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_dp_show, inode->i_private);
+}
+
+static int mic_dp_debug_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations dp_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_dp_debug_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_dp_debug_release
+};
+
+static int mic_vdev_info_show(struct seq_file *s, void *unused)
+{
+       struct mic_device *mdev = s->private;
+       struct list_head *pos, *tmp;
+       struct mic_vdev *mvdev;
+       int i, j;
+
+       mutex_lock(&mdev->mic_mutex);
+       list_for_each_safe(pos, tmp, &mdev->vdev_list) {
+               mvdev = list_entry(pos, struct mic_vdev, list);
+               seq_printf(s, "VDEV type %d state %s in %ld out %ld\n",
+                          mvdev->virtio_id,
+                          mic_vdevup(mvdev) ? "UP" : "DOWN",
+                          mvdev->in_bytes,
+                          mvdev->out_bytes);
+               for (i = 0; i < MIC_MAX_VRINGS; i++) {
+                       struct vring_desc *desc;
+                       struct vring_avail *avail;
+                       struct vring_used *used;
+                       struct mic_vringh *mvr = &mvdev->mvr[i];
+                       struct vringh *vrh = &mvr->vrh;
+                       int num = vrh->vring.num;
+                       if (!num)
+                               continue;
+                       desc = vrh->vring.desc;
+                       seq_printf(s, "vring i %d avail_idx %d",
+                                  i, mvr->vring.info->avail_idx & (num - 1));
+                       seq_printf(s, " vring i %d avail_idx %d\n",
+                                  i, mvr->vring.info->avail_idx);
+                       seq_printf(s, "vrh i %d weak_barriers %d",
+                                  i, vrh->weak_barriers);
+                       seq_printf(s, " last_avail_idx %d last_used_idx %d",
+                                  vrh->last_avail_idx, vrh->last_used_idx);
+                       seq_printf(s, " completed %d\n", vrh->completed);
+                       for (j = 0; j < num; j++) {
+                               seq_printf(s, "desc[%d] addr 0x%llx len %d",
+                                          j, desc->addr, desc->len);
+                               seq_printf(s, " flags 0x%x next %d\n",
+                                          desc->flags, desc->next);
+                               desc++;
+                       }
+                       avail = vrh->vring.avail;
+                       seq_printf(s, "avail flags 0x%x idx %d\n",
+                                  avail->flags, avail->idx & (num - 1));
+                       seq_printf(s, "avail flags 0x%x idx %d\n",
+                                  avail->flags, avail->idx);
+                       for (j = 0; j < num; j++)
+                               seq_printf(s, "avail ring[%d] %d\n",
+                                          j, avail->ring[j]);
+                       used = vrh->vring.used;
+                       seq_printf(s, "used flags 0x%x idx %d\n",
+                                  used->flags, used->idx & (num - 1));
+                       seq_printf(s, "used flags 0x%x idx %d\n",
+                                  used->flags, used->idx);
+                       for (j = 0; j < num; j++)
+                               seq_printf(s, "used ring[%d] id %d len %d\n",
+                                          j, used->ring[j].id,
+                                          used->ring[j].len);
+               }
+       }
+       mutex_unlock(&mdev->mic_mutex);
+
+       return 0;
+}
+
+static int mic_vdev_info_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_vdev_info_show, inode->i_private);
+}
+
+static int mic_vdev_info_debug_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations vdev_info_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_vdev_info_debug_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_vdev_info_debug_release
+};
+
+static int mic_msi_irq_info_show(struct seq_file *s, void *pos)
+{
+       struct mic_device *mdev  = s->private;
+       int reg;
+       int i, j;
+       u16 entry;
+       u16 vector;
+       struct pci_dev *pdev = container_of(mdev->sdev->parent,
+               struct pci_dev, dev);
+
+       if (pci_dev_msi_enabled(pdev)) {
+               for (i = 0; i < mdev->irq_info.num_vectors; i++) {
+                       if (pdev->msix_enabled) {
+                               entry = mdev->irq_info.msix_entries[i].entry;
+                               vector = mdev->irq_info.msix_entries[i].vector;
+                       } else {
+                               entry = 0;
+                               vector = pdev->irq;
+                       }
+
+                       reg = mdev->intr_ops->read_msi_to_src_map(mdev, entry);
+
+                       seq_printf(s, "%s %-10d %s %-10d MXAR[%d]: %08X\n",
+                                  "IRQ:", vector, "Entry:", entry, i, reg);
+
+                       seq_printf(s, "%-10s", "offset:");
+                       for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--)
+                               seq_printf(s, "%4d ", j);
+                       seq_puts(s, "\n");
+
+
+                       seq_printf(s, "%-10s", "count:");
+                       for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--)
+                               seq_printf(s, "%4d ",
+                                          (mdev->irq_info.mic_msi_map[i] &
+                                          BIT(j)) ? 1 : 0);
+                       seq_puts(s, "\n\n");
+               }
+       } else {
+               seq_puts(s, "MSI/MSIx interrupts not enabled\n");
+       }
+
+       return 0;
+}
+
+static int mic_msi_irq_info_debug_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, mic_msi_irq_info_show, inode->i_private);
+}
+
+static int
+mic_msi_irq_info_debug_release(struct inode *inode, struct file *file)
+{
+       return single_release(inode, file);
+}
+
+static const struct file_operations msi_irq_info_ops = {
+       .owner   = THIS_MODULE,
+       .open    = mic_msi_irq_info_debug_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = mic_msi_irq_info_debug_release
+};
+
+/**
+ * mic_create_debug_dir - Initialize MIC debugfs entries.
+ */
+void mic_create_debug_dir(struct mic_device *mdev)
+{
+       if (!mic_dbg)
+               return;
+
+       mdev->dbg_dir = debugfs_create_dir(dev_name(mdev->sdev), mic_dbg);
+       if (!mdev->dbg_dir)
+               return;
+
+       debugfs_create_file("log_buf", 0444, mdev->dbg_dir, mdev, &log_buf_ops);
+
+       debugfs_create_file("smpt", 0444, mdev->dbg_dir, mdev, &smpt_file_ops);
+
+       debugfs_create_file("soft_reset", 0444, mdev->dbg_dir, mdev,
+                           &soft_reset_ops);
+
+       debugfs_create_file("post_code", 0444, mdev->dbg_dir, mdev,
+                           &post_code_ops);
+
+       debugfs_create_file("dp", 0444, mdev->dbg_dir, mdev, &dp_ops);
+
+       debugfs_create_file("vdev_info", 0444, mdev->dbg_dir, mdev,
+                           &vdev_info_ops);
+
+       debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, mdev,
+                           &msi_irq_info_ops);
+}
+
+/**
+ * mic_delete_debug_dir - Uninitialize MIC debugfs entries.
+ */
+void mic_delete_debug_dir(struct mic_device *mdev)
+{
+       if (!mdev->dbg_dir)
+               return;
+
+       debugfs_remove_recursive(mdev->dbg_dir);
+}
+
+/**
+ * mic_init_debugfs - Initialize global debugfs entry.
+ */
+void __init mic_init_debugfs(void)
+{
+       mic_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL);
+       if (!mic_dbg)
+               pr_err("can't create debugfs dir\n");
+}
+
+/**
+ * mic_exit_debugfs - Uninitialize global debugfs entry
+ */
+void mic_exit_debugfs(void)
+{
+       debugfs_remove(mic_dbg);
+}
diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h
new file mode 100644 (file)
index 0000000..3574cc3
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#ifndef _MIC_DEVICE_H_
+#define _MIC_DEVICE_H_
+
+#include <linux/cdev.h>
+#include <linux/idr.h>
+#include <linux/notifier.h>
+
+#include "mic_intr.h"
+
+/* The maximum number of MIC devices supported in a single host system. */
+#define MIC_MAX_NUM_DEVS 256
+
+/**
+ * enum mic_hw_family - The hardware family to which a device belongs.
+ */
+enum mic_hw_family {
+       MIC_FAMILY_X100 = 0,
+       MIC_FAMILY_UNKNOWN
+};
+
+/**
+ * enum mic_stepping - MIC stepping ids.
+ */
+enum mic_stepping {
+       MIC_A0_STEP = 0x0,
+       MIC_B0_STEP = 0x10,
+       MIC_B1_STEP = 0x11,
+       MIC_C0_STEP = 0x20,
+};
+
+/**
+ * struct mic_device -  MIC device information for each card.
+ *
+ * @mmio: MMIO bar information.
+ * @aper: Aperture bar information.
+ * @family: The MIC family to which this device belongs.
+ * @ops: MIC HW specific operations.
+ * @id: The unique device id for this MIC device.
+ * @stepping: Stepping ID.
+ * @attr_group: Pointer to list of sysfs attribute groups.
+ * @sdev: Device for sysfs entries.
+ * @mic_mutex: Mutex for synchronizing access to mic_device.
+ * @intr_ops: HW specific interrupt operations.
+ * @smpt_ops: Hardware specific SMPT operations.
+ * @smpt: MIC SMPT information.
+ * @intr_info: H/W specific interrupt information.
+ * @irq_info: The OS specific irq information
+ * @dbg_dir: debugfs directory of this MIC device.
+ * @cmdline: Kernel command line.
+ * @firmware: Firmware file name.
+ * @ramdisk: Ramdisk file name.
+ * @bootmode: Boot mode i.e. "linux" or "elf" for flash updates.
+ * @bootaddr: MIC boot address.
+ * @reset_trigger_work: Work for triggering reset requests.
+ * @shutdown_work: Work for handling shutdown interrupts.
+ * @state: MIC state.
+ * @shutdown_status: MIC status reported by card for shutdown/crashes.
+ * @state_sysfs: Sysfs dirent for notifying ring 3 about MIC state changes.
+ * @reset_wait: Waitqueue for sleeping while reset completes.
+ * @log_buf_addr: Log buffer address for MIC.
+ * @log_buf_len: Log buffer length address for MIC.
+ * @dp: virtio device page
+ * @dp_dma_addr: virtio device page DMA address.
+ * @shutdown_db: shutdown doorbell.
+ * @shutdown_cookie: shutdown cookie.
+ * @cdev: Character device for MIC.
+ * @vdev_list: list of virtio devices.
+ * @pm_notifier: Handles PM notifications from the OS.
+ */
+struct mic_device {
+       struct mic_mw mmio;
+       struct mic_mw aper;
+       enum mic_hw_family family;
+       struct mic_hw_ops *ops;
+       int id;
+       enum mic_stepping stepping;
+       const struct attribute_group **attr_group;
+       struct device *sdev;
+       struct mutex mic_mutex;
+       struct mic_hw_intr_ops *intr_ops;
+       struct mic_smpt_ops *smpt_ops;
+       struct mic_smpt_info *smpt;
+       struct mic_intr_info *intr_info;
+       struct mic_irq_info irq_info;
+       struct dentry *dbg_dir;
+       char *cmdline;
+       char *firmware;
+       char *ramdisk;
+       char *bootmode;
+       u32 bootaddr;
+       struct work_struct reset_trigger_work;
+       struct work_struct shutdown_work;
+       u8 state;
+       u8 shutdown_status;
+       struct sysfs_dirent *state_sysfs;
+       struct completion reset_wait;
+       void *log_buf_addr;
+       int *log_buf_len;
+       void *dp;
+       dma_addr_t dp_dma_addr;
+       int shutdown_db;
+       struct mic_irq *shutdown_cookie;
+       struct cdev cdev;
+       struct list_head vdev_list;
+       struct notifier_block pm_notifier;
+};
+
+/**
+ * struct mic_hw_ops - MIC HW specific operations.
+ * @aper_bar: Aperture bar resource number.
+ * @mmio_bar: MMIO bar resource number.
+ * @read_spad: Read from scratch pad register.
+ * @write_spad: Write to scratch pad register.
+ * @send_intr: Send an interrupt for a particular doorbell on the card.
+ * @ack_interrupt: Hardware specific operations to ack the h/w on
+ * receipt of an interrupt.
+ * @reset: Reset the remote processor.
+ * @reset_fw_ready: Reset firmware ready field.
+ * @is_fw_ready: Check if firmware is ready for OS download.
+ * @send_firmware_intr: Send an interrupt to the card firmware.
+ * @load_mic_fw: Load firmware segments required to boot the card
+ * into card memory. This includes the kernel, command line, ramdisk etc.
+ * @get_postcode: Get post code status from firmware.
+ */
+struct mic_hw_ops {
+       u8 aper_bar;
+       u8 mmio_bar;
+       u32 (*read_spad)(struct mic_device *mdev, unsigned int idx);
+       void (*write_spad)(struct mic_device *mdev, unsigned int idx, u32 val);
+       void (*send_intr)(struct mic_device *mdev, int doorbell);
+       u32 (*ack_interrupt)(struct mic_device *mdev);
+       void (*reset)(struct mic_device *mdev);
+       void (*reset_fw_ready)(struct mic_device *mdev);
+       bool (*is_fw_ready)(struct mic_device *mdev);
+       void (*send_firmware_intr)(struct mic_device *mdev);
+       int (*load_mic_fw)(struct mic_device *mdev, const char *buf);
+       u32 (*get_postcode)(struct mic_device *mdev);
+};
+
+/**
+ * mic_mmio_read - read from an MMIO register.
+ * @mw: MMIO register base virtual address.
+ * @offset: register offset.
+ *
+ * RETURNS: register value.
+ */
+static inline u32 mic_mmio_read(struct mic_mw *mw, u32 offset)
+{
+       return ioread32(mw->va + offset);
+}
+
+/**
+ * mic_mmio_write - write to an MMIO register.
+ * @mw: MMIO register base virtual address.
+ * @val: the data value to put into the register
+ * @offset: register offset.
+ *
+ * RETURNS: none.
+ */
+static inline void
+mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset)
+{
+       iowrite32(val, mw->va + offset);
+}
+
+void mic_sysfs_init(struct mic_device *mdev);
+int mic_start(struct mic_device *mdev, const char *buf);
+void mic_stop(struct mic_device *mdev, bool force);
+void mic_shutdown(struct mic_device *mdev);
+void mic_reset_delayed_work(struct work_struct *work);
+void mic_reset_trigger_work(struct work_struct *work);
+void mic_shutdown_work(struct work_struct *work);
+void mic_bootparam_init(struct mic_device *mdev);
+void mic_set_state(struct mic_device *mdev, u8 state);
+void mic_set_shutdown_status(struct mic_device *mdev, u8 status);
+void mic_create_debug_dir(struct mic_device *dev);
+void mic_delete_debug_dir(struct mic_device *dev);
+void __init mic_init_debugfs(void);
+void mic_exit_debugfs(void);
+void mic_prepare_suspend(struct mic_device *mdev);
+void mic_complete_resume(struct mic_device *mdev);
+void mic_suspend(struct mic_device *mdev);
+#endif
diff --git a/drivers/misc/mic/host/mic_fops.c b/drivers/misc/mic/host/mic_fops.c
new file mode 100644 (file)
index 0000000..8dc6ff1
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/poll.h>
+
+#include <linux/mic_common.h>
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_fops.h"
+#include "mic_virtio.h"
+
+int mic_open(struct inode *inode, struct file *f)
+{
+       struct mic_vdev *mvdev;
+       struct mic_device *mdev = container_of(inode->i_cdev,
+               struct mic_device, cdev);
+
+       mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL);
+       if (!mvdev)
+               return -ENOMEM;
+
+       init_waitqueue_head(&mvdev->waitq);
+       INIT_LIST_HEAD(&mvdev->list);
+       mvdev->mdev = mdev;
+       mvdev->virtio_id = -1;
+
+       f->private_data = mvdev;
+       return 0;
+}
+
+int mic_release(struct inode *inode, struct file *f)
+{
+       struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
+
+       if (-1 != mvdev->virtio_id)
+               mic_virtio_del_device(mvdev);
+       f->private_data = NULL;
+       kfree(mvdev);
+       return 0;
+}
+
+long mic_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
+{
+       struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
+       void __user *argp = (void __user *)arg;
+       int ret;
+
+       switch (cmd) {
+       case MIC_VIRTIO_ADD_DEVICE:
+       {
+               ret = mic_virtio_add_device(mvdev, argp);
+               if (ret < 0) {
+                       dev_err(mic_dev(mvdev),
+                               "%s %d errno ret %d\n",
+                               __func__, __LINE__, ret);
+                       return ret;
+               }
+               break;
+       }
+       case MIC_VIRTIO_COPY_DESC:
+       {
+               struct mic_copy_desc copy;
+
+               ret = mic_vdev_inited(mvdev);
+               if (ret)
+                       return ret;
+
+               if (copy_from_user(&copy, argp, sizeof(copy)))
+                       return -EFAULT;
+
+               dev_dbg(mic_dev(mvdev),
+                       "%s %d === iovcnt 0x%x vr_idx 0x%x update_used %d\n",
+                       __func__, __LINE__, copy.iovcnt, copy.vr_idx,
+                       copy.update_used);
+
+               ret = mic_virtio_copy_desc(mvdev, &copy);
+               if (ret < 0) {
+                       dev_err(mic_dev(mvdev),
+                               "%s %d errno ret %d\n",
+                               __func__, __LINE__, ret);
+                       return ret;
+               }
+               if (copy_to_user(
+                       &((struct mic_copy_desc __user *)argp)->out_len,
+                       &copy.out_len, sizeof(copy.out_len))) {
+                       dev_err(mic_dev(mvdev), "%s %d errno ret %d\n",
+                               __func__, __LINE__, -EFAULT);
+                       return -EFAULT;
+               }
+               break;
+       }
+       case MIC_VIRTIO_CONFIG_CHANGE:
+       {
+               ret = mic_vdev_inited(mvdev);
+               if (ret)
+                       return ret;
+
+               ret = mic_virtio_config_change(mvdev, argp);
+               if (ret < 0) {
+                       dev_err(mic_dev(mvdev),
+                               "%s %d errno ret %d\n",
+                               __func__, __LINE__, ret);
+                       return ret;
+               }
+               break;
+       }
+       default:
+               return -ENOIOCTLCMD;
+       };
+       return 0;
+}
+
+/*
+ * We return POLLIN | POLLOUT from poll when new buffers are enqueued, and
+ * not when previously enqueued buffers may be available. This means that
+ * in the card->host (TX) path, when userspace is unblocked by poll it
+ * must drain all available descriptors or it can stall.
+ */
+unsigned int mic_poll(struct file *f, poll_table *wait)
+{
+       struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
+       int mask = 0;
+
+       poll_wait(f, &mvdev->waitq, wait);
+
+       if (mic_vdev_inited(mvdev)) {
+               mask = POLLERR;
+       } else if (mvdev->poll_wake) {
+               mvdev->poll_wake = 0;
+               mask = POLLIN | POLLOUT;
+       }
+
+       return mask;
+}
+
+static inline int
+mic_query_offset(struct mic_vdev *mvdev, unsigned long offset,
+                unsigned long *size, unsigned long *pa)
+{
+       struct mic_device *mdev = mvdev->mdev;
+       unsigned long start = MIC_DP_SIZE;
+       int i;
+
+       /*
+        * MMAP interface is as follows:
+        * offset                               region
+        * 0x0                                  virtio device_page
+        * 0x1000                               first vring
+        * 0x1000 + size of 1st vring           second vring
+        * ....
+        */
+       if (!offset) {
+               *pa = virt_to_phys(mdev->dp);
+               *size = MIC_DP_SIZE;
+               return 0;
+       }
+
+       for (i = 0; i < mvdev->dd->num_vq; i++) {
+               struct mic_vringh *mvr = &mvdev->mvr[i];
+               if (offset == start) {
+                       *pa = virt_to_phys(mvr->vring.va);
+                       *size = mvr->vring.len;
+                       return 0;
+               }
+               start += mvr->vring.len;
+       }
+       return -1;
+}
+
+/*
+ * Maps the device page and virtio rings to user space for readonly access.
+ */
+int
+mic_mmap(struct file *f, struct vm_area_struct *vma)
+{
+       struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
+       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+       unsigned long pa, size = vma->vm_end - vma->vm_start, size_rem = size;
+       int i, err;
+
+       err = mic_vdev_inited(mvdev);
+       if (err)
+               return err;
+
+       if (vma->vm_flags & VM_WRITE)
+               return -EACCES;
+
+       while (size_rem) {
+               i = mic_query_offset(mvdev, offset, &size, &pa);
+               if (i < 0)
+                       return -EINVAL;
+               err = remap_pfn_range(vma, vma->vm_start + offset,
+                       pa >> PAGE_SHIFT, size, vma->vm_page_prot);
+               if (err)
+                       return err;
+               dev_dbg(mic_dev(mvdev),
+                       "%s %d type %d size 0x%lx off 0x%lx pa 0x%lx vma 0x%lx\n",
+                       __func__, __LINE__, mvdev->virtio_id, size, offset,
+                       pa, vma->vm_start + offset);
+               size_rem -= size;
+               offset += size;
+       }
+       return 0;
+}
diff --git a/drivers/misc/mic/host/mic_fops.h b/drivers/misc/mic/host/mic_fops.h
new file mode 100644 (file)
index 0000000..dc3893d
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#ifndef _MIC_FOPS_H_
+#define _MIC_FOPS_H_
+
+int mic_open(struct inode *inode, struct file *filp);
+int mic_release(struct inode *inode, struct file *filp);
+ssize_t mic_read(struct file *filp, char __user *buf,
+                       size_t count, loff_t *pos);
+long mic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
+int mic_mmap(struct file *f, struct vm_area_struct *vma);
+unsigned int mic_poll(struct file *f, poll_table *wait);
+
+#endif
diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c
new file mode 100644 (file)
index 0000000..f9c29bc
--- /dev/null
@@ -0,0 +1,630 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+
+/*
+ * mic_invoke_callback - Invoke callback functions registered for
+ * the corresponding source id.
+ *
+ * @mdev: pointer to the mic_device instance
+ * @idx: The interrupt source id.
+ *
+ * Returns none.
+ */
+static inline void mic_invoke_callback(struct mic_device *mdev, int idx)
+{
+       struct mic_intr_cb *intr_cb;
+       struct pci_dev *pdev = container_of(mdev->sdev->parent,
+               struct pci_dev, dev);
+
+       spin_lock(&mdev->irq_info.mic_intr_lock);
+       list_for_each_entry(intr_cb, &mdev->irq_info.cb_list[idx], list)
+               if (intr_cb->func)
+                       intr_cb->func(pdev->irq, intr_cb->data);
+       spin_unlock(&mdev->irq_info.mic_intr_lock);
+}
+
+/**
+ * mic_interrupt - Generic interrupt handler for
+ * MSI and INTx based interrupts.
+ */
+static irqreturn_t mic_interrupt(int irq, void *dev)
+{
+       struct mic_device *mdev = dev;
+       struct mic_intr_info *info = mdev->intr_info;
+       u32 mask;
+       int i;
+
+       mask = mdev->ops->ack_interrupt(mdev);
+       if (!mask)
+               return IRQ_NONE;
+
+       for (i = info->intr_start_idx[MIC_INTR_DB];
+                       i < info->intr_len[MIC_INTR_DB]; i++)
+               if (mask & BIT(i))
+                       mic_invoke_callback(mdev, i);
+
+       return IRQ_HANDLED;
+}
+
+/* Return the interrupt offset from the index. Index is 0 based. */
+static u16 mic_map_src_to_offset(struct mic_device *mdev,
+               int intr_src, enum mic_intr_type type)
+{
+       if (type >= MIC_NUM_INTR_TYPES)
+               return MIC_NUM_OFFSETS;
+       if (intr_src >= mdev->intr_info->intr_len[type])
+               return MIC_NUM_OFFSETS;
+
+       return mdev->intr_info->intr_start_idx[type] + intr_src;
+}
+
+/* Return next available msix_entry. */
+static struct msix_entry *mic_get_available_vector(struct mic_device *mdev)
+{
+       int i;
+       struct mic_irq_info *info = &mdev->irq_info;
+
+       for (i = 0; i < info->num_vectors; i++)
+               if (!info->mic_msi_map[i])
+                       return &info->msix_entries[i];
+       return NULL;
+}
+
+/**
+ * mic_register_intr_callback - Register a callback handler for the
+ * given source id.
+ *
+ * @mdev: pointer to the mic_device instance
+ * @idx: The source id to be registered.
+ * @func: The function to be called when the source id receives
+ * the interrupt.
+ * @data: Private data of the requester.
+ * Return the callback structure that was registered or an
+ * appropriate error on failure.
+ */
+static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev,
+                       u8 idx, irqreturn_t (*func) (int irq, void *dev),
+                       void *data)
+{
+       struct mic_intr_cb *intr_cb;
+       unsigned long flags;
+       int rc;
+       intr_cb = kmalloc(sizeof(*intr_cb), GFP_KERNEL);
+
+       if (!intr_cb)
+               return ERR_PTR(-ENOMEM);
+
+       intr_cb->func = func;
+       intr_cb->data = data;
+       intr_cb->cb_id = ida_simple_get(&mdev->irq_info.cb_ida,
+               0, 0, GFP_KERNEL);
+       if (intr_cb->cb_id < 0) {
+               rc = intr_cb->cb_id;
+               goto ida_fail;
+       }
+
+       spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
+       list_add_tail(&intr_cb->list, &mdev->irq_info.cb_list[idx]);
+       spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
+
+       return intr_cb;
+ida_fail:
+       kfree(intr_cb);
+       return ERR_PTR(rc);
+}
+
+/**
+ * mic_unregister_intr_callback - Unregister the callback handler
+ * identified by its callback id.
+ *
+ * @mdev: pointer to the mic_device instance
+ * @idx: The callback structure id to be unregistered.
+ * Return the source id that was unregistered or MIC_NUM_OFFSETS if no
+ * such callback handler was found.
+ */
+static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx)
+{
+       struct list_head *pos, *tmp;
+       struct mic_intr_cb *intr_cb;
+       unsigned long flags;
+       int i;
+
+       for (i = 0;  i < MIC_NUM_OFFSETS; i++) {
+               spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
+               list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) {
+                       intr_cb = list_entry(pos, struct mic_intr_cb, list);
+                       if (intr_cb->cb_id == idx) {
+                               list_del(pos);
+                               ida_simple_remove(&mdev->irq_info.cb_ida,
+                                                 intr_cb->cb_id);
+                               kfree(intr_cb);
+                               spin_unlock_irqrestore(
+                                       &mdev->irq_info.mic_intr_lock, flags);
+                               return i;
+                       }
+               }
+               spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
+       }
+       return MIC_NUM_OFFSETS;
+}
+
+/**
+ * mic_setup_msix - Initializes MSIx interrupts.
+ *
+ * @mdev: pointer to mic_device instance
+ *
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+static int mic_setup_msix(struct mic_device *mdev, struct pci_dev *pdev)
+{
+       int rc, i;
+       int entry_size = sizeof(*mdev->irq_info.msix_entries);
+
+       mdev->irq_info.msix_entries = kmalloc_array(MIC_MIN_MSIX,
+                                                   entry_size, GFP_KERNEL);
+       if (!mdev->irq_info.msix_entries) {
+               rc = -ENOMEM;
+               goto err_nomem1;
+       }
+
+       for (i = 0; i < MIC_MIN_MSIX; i++)
+               mdev->irq_info.msix_entries[i].entry = i;
+
+       rc = pci_enable_msix(pdev, mdev->irq_info.msix_entries,
+               MIC_MIN_MSIX);
+       if (rc) {
+               dev_dbg(&pdev->dev, "Error enabling MSIx. rc = %d\n", rc);
+               goto err_enable_msix;
+       }
+
+       mdev->irq_info.num_vectors = MIC_MIN_MSIX;
+       mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) *
+               mdev->irq_info.num_vectors), GFP_KERNEL);
+
+       if (!mdev->irq_info.mic_msi_map) {
+               rc = -ENOMEM;
+               goto err_nomem2;
+       }
+
+       dev_dbg(mdev->sdev->parent,
+               "%d MSIx irqs setup\n", mdev->irq_info.num_vectors);
+       return 0;
+err_nomem2:
+       pci_disable_msix(pdev);
+err_enable_msix:
+       kfree(mdev->irq_info.msix_entries);
+err_nomem1:
+       mdev->irq_info.num_vectors = 0;
+       return rc;
+}
+
+/**
+ * mic_setup_callbacks - Initialize data structures needed
+ * to handle callbacks.
+ *
+ * @mdev: pointer to mic_device instance
+ */
+static int mic_setup_callbacks(struct mic_device *mdev)
+{
+       int i;
+
+       mdev->irq_info.cb_list = kmalloc_array(MIC_NUM_OFFSETS,
+                                              sizeof(*mdev->irq_info.cb_list),
+                                              GFP_KERNEL);
+       if (!mdev->irq_info.cb_list)
+               return -ENOMEM;
+
+       for (i = 0; i < MIC_NUM_OFFSETS; i++)
+               INIT_LIST_HEAD(&mdev->irq_info.cb_list[i]);
+       ida_init(&mdev->irq_info.cb_ida);
+       spin_lock_init(&mdev->irq_info.mic_intr_lock);
+       return 0;
+}
+
+/**
+ * mic_release_callbacks - Uninitialize data structures needed
+ * to handle callbacks.
+ *
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_release_callbacks(struct mic_device *mdev)
+{
+       unsigned long flags;
+       struct list_head *pos, *tmp;
+       struct mic_intr_cb *intr_cb;
+       int i;
+
+       for (i = 0; i < MIC_NUM_OFFSETS; i++) {
+               spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
+
+               if (list_empty(&mdev->irq_info.cb_list[i])) {
+                       spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock,
+                                              flags);
+                       break;
+               }
+
+               list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) {
+                       intr_cb = list_entry(pos, struct mic_intr_cb, list);
+                       list_del(pos);
+                       ida_simple_remove(&mdev->irq_info.cb_ida,
+                                         intr_cb->cb_id);
+                       kfree(intr_cb);
+               }
+               spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
+       }
+       ida_destroy(&mdev->irq_info.cb_ida);
+       kfree(mdev->irq_info.cb_list);
+}
+
+/**
+ * mic_setup_msi - Initializes MSI interrupts.
+ *
+ * @mdev: pointer to mic_device instance
+ * @pdev: PCI device structure
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+static int mic_setup_msi(struct mic_device *mdev, struct pci_dev *pdev)
+{
+       int rc;
+
+       rc = pci_enable_msi(pdev);
+       if (rc) {
+               dev_dbg(&pdev->dev, "Error enabling MSI. rc = %d\n", rc);
+               return rc;
+       }
+
+       mdev->irq_info.num_vectors = 1;
+       mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) *
+               mdev->irq_info.num_vectors), GFP_KERNEL);
+
+       if (!mdev->irq_info.mic_msi_map) {
+               rc = -ENOMEM;
+               goto err_nomem1;
+       }
+
+       rc = mic_setup_callbacks(mdev);
+       if (rc) {
+               dev_err(&pdev->dev, "Error setting up callbacks\n");
+               goto err_nomem2;
+       }
+
+       rc = request_irq(pdev->irq, mic_interrupt, 0 , "mic-msi", mdev);
+       if (rc) {
+               dev_err(&pdev->dev, "Error allocating MSI interrupt\n");
+               goto err_irq_req_fail;
+       }
+
+       dev_dbg(&pdev->dev, "%d MSI irqs setup\n", mdev->irq_info.num_vectors);
+       return 0;
+err_irq_req_fail:
+       mic_release_callbacks(mdev);
+err_nomem2:
+       kfree(mdev->irq_info.mic_msi_map);
+err_nomem1:
+       pci_disable_msi(pdev);
+       mdev->irq_info.num_vectors = 0;
+       return rc;
+}
+
+/**
+ * mic_setup_intx - Initializes legacy interrupts.
+ *
+ * @mdev: pointer to mic_device instance
+ * @pdev: PCI device structure
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+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);
+       if (rc) {
+               dev_err(&pdev->dev, "Error setting up callbacks\n");
+               goto err_nomem;
+       }
+
+       rc = request_irq(pdev->irq, mic_interrupt,
+               IRQF_SHARED, "mic-intx", mdev);
+       if (rc)
+               goto err;
+
+       dev_dbg(&pdev->dev, "intx irq setup\n");
+       return 0;
+err:
+       mic_release_callbacks(mdev);
+err_nomem:
+       return rc;
+}
+
+/**
+ * mic_next_db - Retrieve the next doorbell interrupt source id.
+ * The id is picked sequentially from the available pool of
+ * doorlbell ids.
+ *
+ * @mdev: pointer to the mic_device instance.
+ *
+ * Returns the next doorbell interrupt source.
+ */
+int mic_next_db(struct mic_device *mdev)
+{
+       int next_db;
+
+       next_db = mdev->irq_info.next_avail_src %
+               mdev->intr_info->intr_len[MIC_INTR_DB];
+       mdev->irq_info.next_avail_src++;
+       return next_db;
+}
+
+#define COOKIE_ID_SHIFT 16
+#define GET_ENTRY(cookie) ((cookie) & 0xFFFF)
+#define GET_OFFSET(cookie) ((cookie) >> COOKIE_ID_SHIFT)
+#define MK_COOKIE(x, y) ((x) | (y) << COOKIE_ID_SHIFT)
+
+/**
+ * mic_request_irq - request an irq. mic_mutex needs
+ * to be held before calling this function.
+ *
+ * @mdev: pointer to mic_device instance
+ * @func: The callback function that handles the interrupt.
+ * The function needs to call ack_interrupts
+ * (mdev->ops->ack_interrupt(mdev)) when handling the interrupts.
+ * @name: The ASCII name of the callee requesting the irq.
+ * @data: private data that is returned back when calling the
+ * function handler.
+ * @intr_src: The source id of the requester. Its the doorbell id
+ * for Doorbell interrupts and DMA channel id for DMA interrupts.
+ * @type: The type of interrupt. Values defined in mic_intr_type
+ *
+ * returns: The cookie that is transparent to the caller. Passed
+ * back when calling mic_free_irq. An appropriate error code
+ * is returned on failure. Caller needs to use IS_ERR(return_val)
+ * to check for failure and PTR_ERR(return_val) to obtained the
+ * error code.
+ *
+ */
+struct mic_irq *mic_request_irq(struct mic_device *mdev,
+       irqreturn_t (*func)(int irq, void *dev),
+       const char *name, void *data, int intr_src,
+       enum mic_intr_type type)
+{
+       u16 offset;
+       int rc = 0;
+       struct msix_entry *msix = NULL;
+       unsigned long cookie = 0;
+       u16 entry;
+       struct mic_intr_cb *intr_cb;
+       struct pci_dev *pdev = container_of(mdev->sdev->parent,
+               struct pci_dev, dev);
+
+       offset = mic_map_src_to_offset(mdev, intr_src, type);
+       if (offset >= MIC_NUM_OFFSETS) {
+               dev_err(mdev->sdev->parent,
+                       "Error mapping index %d to a valid source id.\n",
+                       intr_src);
+               rc = -EINVAL;
+               goto err;
+       }
+
+       if (mdev->irq_info.num_vectors > 1) {
+               msix = mic_get_available_vector(mdev);
+               if (!msix) {
+                       dev_err(mdev->sdev->parent,
+                               "No MSIx vectors available for use.\n");
+                       rc = -ENOSPC;
+                       goto err;
+               }
+
+               rc = request_irq(msix->vector, func, 0, name, data);
+               if (rc) {
+                       dev_dbg(mdev->sdev->parent,
+                               "request irq failed rc = %d\n", rc);
+                       goto err;
+               }
+               entry = msix->entry;
+               mdev->irq_info.mic_msi_map[entry] |= BIT(offset);
+               mdev->intr_ops->program_msi_to_src_map(mdev,
+                               entry, offset, true);
+               cookie = MK_COOKIE(entry, offset);
+               dev_dbg(mdev->sdev->parent, "irq: %d assigned for src: %d\n",
+                       msix->vector, intr_src);
+       } else {
+               intr_cb = mic_register_intr_callback(mdev,
+                               offset, func, data);
+               if (IS_ERR(intr_cb)) {
+                       dev_err(mdev->sdev->parent,
+                               "No available callback entries for use\n");
+                       rc = PTR_ERR(intr_cb);
+                       goto err;
+               }
+
+               entry = 0;
+               if (pci_dev_msi_enabled(pdev)) {
+                       mdev->irq_info.mic_msi_map[entry] |= (1 << offset);
+                       mdev->intr_ops->program_msi_to_src_map(mdev,
+                               entry, offset, true);
+               }
+               cookie = MK_COOKIE(entry, intr_cb->cb_id);
+               dev_dbg(mdev->sdev->parent, "callback %d registered for src: %d\n",
+                       intr_cb->cb_id, intr_src);
+       }
+       return (struct mic_irq *)cookie;
+err:
+       return ERR_PTR(rc);
+}
+
+/**
+ * mic_free_irq - free irq. mic_mutex
+ *  needs to be held before calling this function.
+ *
+ * @mdev: pointer to mic_device instance
+ * @cookie: cookie obtained during a successful call to mic_request_irq
+ * @data: private data specified by the calling function during the
+ * mic_request_irq
+ *
+ * returns: none.
+ */
+void mic_free_irq(struct mic_device *mdev,
+       struct mic_irq *cookie, void *data)
+{
+       u32 offset;
+       u32 entry;
+       u8 src_id;
+       unsigned int irq;
+       struct pci_dev *pdev = container_of(mdev->sdev->parent,
+               struct pci_dev, dev);
+
+       entry = GET_ENTRY((unsigned long)cookie);
+       offset = GET_OFFSET((unsigned long)cookie);
+       if (mdev->irq_info.num_vectors > 1) {
+               if (entry >= mdev->irq_info.num_vectors) {
+                       dev_warn(mdev->sdev->parent,
+                                "entry %d should be < num_irq %d\n",
+                               entry, mdev->irq_info.num_vectors);
+                       return;
+               }
+               irq = mdev->irq_info.msix_entries[entry].vector;
+               free_irq(irq, data);
+               mdev->irq_info.mic_msi_map[entry] &= ~(BIT(offset));
+               mdev->intr_ops->program_msi_to_src_map(mdev,
+                       entry, offset, false);
+
+               dev_dbg(mdev->sdev->parent, "irq: %d freed\n", irq);
+       } else {
+               irq = pdev->irq;
+               src_id = mic_unregister_intr_callback(mdev, offset);
+               if (src_id >= MIC_NUM_OFFSETS) {
+                       dev_warn(mdev->sdev->parent, "Error unregistering callback\n");
+                       return;
+               }
+               if (pci_dev_msi_enabled(pdev)) {
+                       mdev->irq_info.mic_msi_map[entry] &= ~(BIT(src_id));
+                       mdev->intr_ops->program_msi_to_src_map(mdev,
+                               entry, src_id, false);
+               }
+               dev_dbg(mdev->sdev->parent, "callback %d unregistered for src: %d\n",
+                       offset, src_id);
+       }
+}
+
+/**
+ * mic_setup_interrupts - Initializes interrupts.
+ *
+ * @mdev: pointer to mic_device instance
+ * @pdev: PCI device structure
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev)
+{
+       int rc;
+
+       rc = mic_setup_msix(mdev, pdev);
+       if (!rc)
+               goto done;
+
+       rc = mic_setup_msi(mdev, pdev);
+       if (!rc)
+               goto done;
+
+       rc = mic_setup_intx(mdev, pdev);
+       if (rc) {
+               dev_err(mdev->sdev->parent, "no usable interrupts\n");
+               return rc;
+       }
+done:
+       mdev->intr_ops->enable_interrupts(mdev);
+       return 0;
+}
+
+/**
+ * mic_free_interrupts - Frees interrupts setup by mic_setup_interrupts
+ *
+ * @mdev: pointer to mic_device instance
+ * @pdev: PCI device structure
+ *
+ * returns none.
+ */
+void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev)
+{
+       int i;
+
+       mdev->intr_ops->disable_interrupts(mdev);
+       if (mdev->irq_info.num_vectors > 1) {
+               for (i = 0; i < mdev->irq_info.num_vectors; i++) {
+                       if (mdev->irq_info.mic_msi_map[i])
+                               dev_warn(&pdev->dev, "irq %d may still be in use.\n",
+                                        mdev->irq_info.msix_entries[i].vector);
+               }
+               kfree(mdev->irq_info.mic_msi_map);
+               kfree(mdev->irq_info.msix_entries);
+               pci_disable_msix(pdev);
+       } else {
+               if (pci_dev_msi_enabled(pdev)) {
+                       free_irq(pdev->irq, mdev);
+                       kfree(mdev->irq_info.mic_msi_map);
+                       pci_disable_msi(pdev);
+               } else {
+                       free_irq(pdev->irq, mdev);
+               }
+               mic_release_callbacks(mdev);
+       }
+}
+
+/**
+ * mic_intr_restore - Restore MIC interrupt registers.
+ *
+ * @mdev: pointer to mic_device instance.
+ *
+ * Restore the interrupt registers to values previously
+ * stored in the SW data structures. mic_mutex needs to
+ * be held before calling this function.
+ *
+ * returns None.
+ */
+void mic_intr_restore(struct mic_device *mdev)
+{
+       int entry, offset;
+       struct pci_dev *pdev = container_of(mdev->sdev->parent,
+               struct pci_dev, dev);
+
+       if (!pci_dev_msi_enabled(pdev))
+               return;
+
+       for (entry = 0; entry < mdev->irq_info.num_vectors; entry++) {
+               for (offset = 0; offset < MIC_NUM_OFFSETS; offset++) {
+                       if (mdev->irq_info.mic_msi_map[entry] & BIT(offset))
+                               mdev->intr_ops->program_msi_to_src_map(mdev,
+                                       entry, offset, true);
+               }
+       }
+}
diff --git a/drivers/misc/mic/host/mic_intr.h b/drivers/misc/mic/host/mic_intr.h
new file mode 100644 (file)
index 0000000..6091aa9
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#ifndef _MIC_INTR_H_
+#define _MIC_INTR_H_
+
+/*
+ * The minimum number of msix vectors required for normal operation.
+ * 3 for virtio network, console and block devices.
+ * 1 for card shutdown notifications.
+ */
+#define MIC_MIN_MSIX 4
+#define MIC_NUM_OFFSETS 32
+
+/**
+ * mic_intr_source - The type of source that will generate
+ * the interrupt.The number of types needs to be in sync with
+ * MIC_NUM_INTR_TYPES
+ *
+ * MIC_INTR_DB: The source is a doorbell
+ * MIC_INTR_DMA: The source is a DMA channel
+ * MIC_INTR_ERR: The source is an error interrupt e.g. SBOX ERR
+ * MIC_NUM_INTR_TYPES: Total number of interrupt sources.
+ */
+enum mic_intr_type {
+       MIC_INTR_DB = 0,
+       MIC_INTR_DMA,
+       MIC_INTR_ERR,
+       MIC_NUM_INTR_TYPES
+};
+
+/**
+ * struct mic_intr_info - Contains h/w specific interrupt sources
+ * information.
+ *
+ * @intr_start_idx: Contains the starting indexes of the
+ * interrupt types.
+ * @intr_len: Contains the length of the interrupt types.
+ */
+struct mic_intr_info {
+       u16 intr_start_idx[MIC_NUM_INTR_TYPES];
+       u16 intr_len[MIC_NUM_INTR_TYPES];
+};
+
+/**
+ * struct mic_irq_info - OS specific irq information
+ *
+ * @next_avail_src: next available doorbell that can be assigned.
+ * @msix_entries: msix entries allocated while setting up MSI-x
+ * @mic_msi_map: The MSI/MSI-x mapping information.
+ * @num_vectors: The number of MSI/MSI-x vectors that have been allocated.
+ * @cb_ida: callback ID allocator to track the callbacks registered.
+ * @mic_intr_lock: spinlock to protect the interrupt callback list.
+ * @cb_list: Array of callback lists one for each source.
+ */
+struct mic_irq_info {
+       int next_avail_src;
+       struct msix_entry *msix_entries;
+       u32 *mic_msi_map;
+       u16 num_vectors;
+       struct ida cb_ida;
+       spinlock_t mic_intr_lock;
+       struct list_head *cb_list;
+};
+
+/**
+ * struct mic_intr_cb - Interrupt callback structure.
+ *
+ * @func: The callback function
+ * @data: Private data of the requester.
+ * @cb_id: The callback id. Identifies this callback.
+ * @list: list head pointing to the next callback structure.
+ */
+struct mic_intr_cb {
+       irqreturn_t (*func) (int irq, void *data);
+       void *data;
+       int cb_id;
+       struct list_head list;
+};
+
+/**
+ * struct mic_irq - opaque pointer used as cookie
+ */
+struct mic_irq;
+
+/* Forward declaration */
+struct mic_device;
+
+/**
+ * struct mic_hw_intr_ops: MIC HW specific interrupt operations
+ * @intr_init: Initialize H/W specific interrupt information.
+ * @enable_interrupts: Enable interrupts from the hardware.
+ * @disable_interrupts: Disable interrupts from the hardware.
+ * @program_msi_to_src_map: Update MSI mapping registers with
+ * irq information.
+ * @read_msi_to_src_map: Read MSI mapping registers containing
+ * irq information.
+ */
+struct mic_hw_intr_ops {
+       void (*intr_init)(struct mic_device *mdev);
+       void (*enable_interrupts)(struct mic_device *mdev);
+       void (*disable_interrupts)(struct mic_device *mdev);
+       void (*program_msi_to_src_map) (struct mic_device *mdev,
+                       int idx, int intr_src, bool set);
+       u32 (*read_msi_to_src_map) (struct mic_device *mdev,
+                       int idx);
+};
+
+int mic_next_db(struct mic_device *mdev);
+struct mic_irq *mic_request_irq(struct mic_device *mdev,
+       irqreturn_t (*func)(int irq, void *data),
+       const char *name, void *data, int intr_src,
+       enum mic_intr_type type);
+
+void mic_free_irq(struct mic_device *mdev,
+               struct mic_irq *cookie, void *data);
+int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev);
+void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev);
+void mic_intr_restore(struct mic_device *mdev);
+#endif
diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c
new file mode 100644 (file)
index 0000000..b352085
--- /dev/null
@@ -0,0 +1,537 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ * Global TODO's across the driver to be added after initial base
+ * patches are accepted upstream:
+ * 1) Enable DMA support.
+ * 2) Enable per vring interrupt support.
+ */
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/poll.h>
+#include <linux/suspend.h>
+
+#include <linux/mic_common.h>
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_x100.h"
+#include "mic_smpt.h"
+#include "mic_fops.h"
+#include "mic_virtio.h"
+
+static const char mic_driver_name[] = "mic";
+
+static DEFINE_PCI_DEVICE_TABLE(mic_pci_tbl) = {
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2250)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2251)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2252)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2253)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2254)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2255)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2256)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2257)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2258)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2259)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225a)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225b)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225c)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225d)},
+       {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225e)},
+
+       /* required last entry */
+       { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, mic_pci_tbl);
+
+/* ID allocator for MIC devices */
+static struct ida g_mic_ida;
+/* Class of MIC devices for sysfs accessibility. */
+static struct class *g_mic_class;
+/* Base device node number for MIC devices */
+static dev_t g_mic_devno;
+
+static const struct file_operations mic_fops = {
+       .open = mic_open,
+       .release = mic_release,
+       .unlocked_ioctl = mic_ioctl,
+       .poll = mic_poll,
+       .mmap = mic_mmap,
+       .owner = THIS_MODULE,
+};
+
+/* Initialize the device page */
+static int mic_dp_init(struct mic_device *mdev)
+{
+       mdev->dp = kzalloc(MIC_DP_SIZE, GFP_KERNEL);
+       if (!mdev->dp) {
+               dev_err(mdev->sdev->parent, "%s %d err %d\n",
+                       __func__, __LINE__, -ENOMEM);
+               return -ENOMEM;
+       }
+
+       mdev->dp_dma_addr = mic_map_single(mdev,
+               mdev->dp, MIC_DP_SIZE);
+       if (mic_map_error(mdev->dp_dma_addr)) {
+               kfree(mdev->dp);
+               dev_err(mdev->sdev->parent, "%s %d err %d\n",
+                       __func__, __LINE__, -ENOMEM);
+               return -ENOMEM;
+       }
+       mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr);
+       mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32);
+       return 0;
+}
+
+/* Uninitialize the device page */
+static void mic_dp_uninit(struct mic_device *mdev)
+{
+       mic_unmap_single(mdev, mdev->dp_dma_addr, MIC_DP_SIZE);
+       kfree(mdev->dp);
+}
+
+/**
+ * mic_shutdown_db - Shutdown doorbell interrupt handler.
+ */
+static irqreturn_t mic_shutdown_db(int irq, void *data)
+{
+       struct mic_device *mdev = data;
+       struct mic_bootparam *bootparam = mdev->dp;
+
+       mdev->ops->ack_interrupt(mdev);
+
+       switch (bootparam->shutdown_status) {
+       case MIC_HALTED:
+       case MIC_POWER_OFF:
+       case MIC_RESTART:
+               /* Fall through */
+       case MIC_CRASHED:
+               schedule_work(&mdev->shutdown_work);
+               break;
+       default:
+               break;
+       };
+       return IRQ_HANDLED;
+}
+
+/**
+ * mic_ops_init: Initialize HW specific operation tables.
+ *
+ * @mdev: pointer to mic_device instance
+ *
+ * returns none.
+ */
+static void mic_ops_init(struct mic_device *mdev)
+{
+       switch (mdev->family) {
+       case MIC_FAMILY_X100:
+               mdev->ops = &mic_x100_ops;
+               mdev->intr_ops = &mic_x100_intr_ops;
+               mdev->smpt_ops = &mic_x100_smpt_ops;
+               break;
+       default:
+               break;
+       }
+}
+
+/**
+ * mic_get_family - Determine hardware family to which this MIC belongs.
+ *
+ * @pdev: The pci device structure
+ *
+ * returns family.
+ */
+static enum mic_hw_family mic_get_family(struct pci_dev *pdev)
+{
+       enum mic_hw_family family;
+
+       switch (pdev->device) {
+       case MIC_X100_PCI_DEVICE_2250:
+       case MIC_X100_PCI_DEVICE_2251:
+       case MIC_X100_PCI_DEVICE_2252:
+       case MIC_X100_PCI_DEVICE_2253:
+       case MIC_X100_PCI_DEVICE_2254:
+       case MIC_X100_PCI_DEVICE_2255:
+       case MIC_X100_PCI_DEVICE_2256:
+       case MIC_X100_PCI_DEVICE_2257:
+       case MIC_X100_PCI_DEVICE_2258:
+       case MIC_X100_PCI_DEVICE_2259:
+       case MIC_X100_PCI_DEVICE_225a:
+       case MIC_X100_PCI_DEVICE_225b:
+       case MIC_X100_PCI_DEVICE_225c:
+       case MIC_X100_PCI_DEVICE_225d:
+       case MIC_X100_PCI_DEVICE_225e:
+               family = MIC_FAMILY_X100;
+               break;
+       default:
+               family = MIC_FAMILY_UNKNOWN;
+               break;
+       }
+       return family;
+}
+
+/**
+* mic_pm_notifier: Notifier callback function that handles
+* PM notifications.
+*
+* @notifier_block: The notifier structure.
+* @pm_event: The event for which the driver was notified.
+* @unused: Meaningless. Always NULL.
+*
+* returns NOTIFY_DONE
+*/
+static int mic_pm_notifier(struct notifier_block *notifier,
+               unsigned long pm_event, void *unused)
+{
+       struct mic_device *mdev = container_of(notifier,
+               struct mic_device, pm_notifier);
+
+       switch (pm_event) {
+       case PM_HIBERNATION_PREPARE:
+               /* Fall through */
+       case PM_SUSPEND_PREPARE:
+               mic_prepare_suspend(mdev);
+               break;
+       case PM_POST_HIBERNATION:
+               /* Fall through */
+       case PM_POST_SUSPEND:
+               /* Fall through */
+       case PM_POST_RESTORE:
+               mic_complete_resume(mdev);
+               break;
+       case PM_RESTORE_PREPARE:
+               break;
+       default:
+               break;
+       }
+       return NOTIFY_DONE;
+}
+
+/**
+ * mic_device_init - Allocates and initializes the MIC device structure
+ *
+ * @mdev: pointer to mic_device instance
+ * @pdev: The pci device structure
+ *
+ * returns none.
+ */
+static int
+mic_device_init(struct mic_device *mdev, struct pci_dev *pdev)
+{
+       int rc;
+
+       mdev->family = mic_get_family(pdev);
+       mdev->stepping = pdev->revision;
+       mic_ops_init(mdev);
+       mic_sysfs_init(mdev);
+       mutex_init(&mdev->mic_mutex);
+       mdev->irq_info.next_avail_src = 0;
+       INIT_WORK(&mdev->reset_trigger_work, mic_reset_trigger_work);
+       INIT_WORK(&mdev->shutdown_work, mic_shutdown_work);
+       init_completion(&mdev->reset_wait);
+       INIT_LIST_HEAD(&mdev->vdev_list);
+       mdev->pm_notifier.notifier_call = mic_pm_notifier;
+       rc = register_pm_notifier(&mdev->pm_notifier);
+       if (rc) {
+               dev_err(&pdev->dev, "register_pm_notifier failed rc %d\n",
+                       rc);
+               goto register_pm_notifier_fail;
+       }
+       return 0;
+register_pm_notifier_fail:
+       flush_work(&mdev->shutdown_work);
+       flush_work(&mdev->reset_trigger_work);
+       return rc;
+}
+
+/**
+ * mic_device_uninit - Frees resources allocated during mic_device_init(..)
+ *
+ * @mdev: pointer to mic_device instance
+ *
+ * returns none
+ */
+static void mic_device_uninit(struct mic_device *mdev)
+{
+       /* The cmdline sysfs entry might have allocated cmdline */
+       kfree(mdev->cmdline);
+       kfree(mdev->firmware);
+       kfree(mdev->ramdisk);
+       kfree(mdev->bootmode);
+       flush_work(&mdev->reset_trigger_work);
+       flush_work(&mdev->shutdown_work);
+       unregister_pm_notifier(&mdev->pm_notifier);
+}
+
+/**
+ * mic_probe - Device Initialization Routine
+ *
+ * @pdev: PCI device structure
+ * @ent: entry in mic_pci_tbl
+ *
+ * returns 0 on success, < 0 on failure.
+ */
+static int mic_probe(struct pci_dev *pdev,
+               const struct pci_device_id *ent)
+{
+       int rc;
+       struct mic_device *mdev;
+
+       mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
+       if (!mdev) {
+               rc = -ENOMEM;
+               dev_err(&pdev->dev, "mdev kmalloc failed rc %d\n", rc);
+               goto mdev_alloc_fail;
+       }
+       mdev->id = ida_simple_get(&g_mic_ida, 0, MIC_MAX_NUM_DEVS, GFP_KERNEL);
+       if (mdev->id < 0) {
+               rc = mdev->id;
+               dev_err(&pdev->dev, "ida_simple_get failed rc %d\n", rc);
+               goto ida_fail;
+       }
+
+       rc = mic_device_init(mdev, pdev);
+       if (rc) {
+               dev_err(&pdev->dev, "mic_device_init failed rc %d\n", rc);
+               goto device_init_fail;
+       }
+
+       rc = pci_enable_device(pdev);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to enable pci device.\n");
+               goto uninit_device;
+       }
+
+       pci_set_master(pdev);
+
+       rc = pci_request_regions(pdev, mic_driver_name);
+       if (rc) {
+               dev_err(&pdev->dev, "failed to get pci regions.\n");
+               goto disable_device;
+       }
+
+       rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
+       if (rc) {
+               dev_err(&pdev->dev, "Cannot set DMA mask\n");
+               goto release_regions;
+       }
+
+       mdev->mmio.pa = pci_resource_start(pdev, mdev->ops->mmio_bar);
+       mdev->mmio.len = pci_resource_len(pdev, mdev->ops->mmio_bar);
+       mdev->mmio.va = pci_ioremap_bar(pdev, mdev->ops->mmio_bar);
+       if (!mdev->mmio.va) {
+               dev_err(&pdev->dev, "Cannot remap MMIO BAR\n");
+               rc = -EIO;
+               goto release_regions;
+       }
+
+       mdev->aper.pa = pci_resource_start(pdev, mdev->ops->aper_bar);
+       mdev->aper.len = pci_resource_len(pdev, mdev->ops->aper_bar);
+       mdev->aper.va = ioremap_wc(mdev->aper.pa, mdev->aper.len);
+       if (!mdev->aper.va) {
+               dev_err(&pdev->dev, "Cannot remap Aperture BAR\n");
+               rc = -EIO;
+               goto unmap_mmio;
+       }
+
+       mdev->intr_ops->intr_init(mdev);
+       rc = mic_setup_interrupts(mdev, pdev);
+       if (rc) {
+               dev_err(&pdev->dev, "mic_setup_interrupts failed %d\n", rc);
+               goto unmap_aper;
+       }
+       rc = mic_smpt_init(mdev);
+       if (rc) {
+               dev_err(&pdev->dev, "smpt_init failed %d\n", rc);
+               goto free_interrupts;
+       }
+
+       pci_set_drvdata(pdev, mdev);
+
+       mdev->sdev = device_create_with_groups(g_mic_class, &pdev->dev,
+               MKDEV(MAJOR(g_mic_devno), mdev->id), NULL,
+               mdev->attr_group, "mic%d", mdev->id);
+       if (IS_ERR(mdev->sdev)) {
+               rc = PTR_ERR(mdev->sdev);
+               dev_err(&pdev->dev,
+                       "device_create_with_groups failed rc %d\n", rc);
+               goto smpt_uninit;
+       }
+       mdev->state_sysfs = sysfs_get_dirent(mdev->sdev->kobj.sd,
+               NULL, "state");
+       if (!mdev->state_sysfs) {
+               rc = -ENODEV;
+               dev_err(&pdev->dev, "sysfs_get_dirent failed rc %d\n", rc);
+               goto destroy_device;
+       }
+
+       rc = mic_dp_init(mdev);
+       if (rc) {
+               dev_err(&pdev->dev, "mic_dp_init failed rc %d\n", rc);
+               goto sysfs_put;
+       }
+       mutex_lock(&mdev->mic_mutex);
+
+       mdev->shutdown_db = mic_next_db(mdev);
+       mdev->shutdown_cookie = mic_request_irq(mdev, mic_shutdown_db,
+               "shutdown-interrupt", mdev, mdev->shutdown_db, MIC_INTR_DB);
+       if (IS_ERR(mdev->shutdown_cookie)) {
+               rc = PTR_ERR(mdev->shutdown_cookie);
+               mutex_unlock(&mdev->mic_mutex);
+               goto dp_uninit;
+       }
+       mutex_unlock(&mdev->mic_mutex);
+       mic_bootparam_init(mdev);
+
+       mic_create_debug_dir(mdev);
+       cdev_init(&mdev->cdev, &mic_fops);
+       mdev->cdev.owner = THIS_MODULE;
+       rc = cdev_add(&mdev->cdev, MKDEV(MAJOR(g_mic_devno), mdev->id), 1);
+       if (rc) {
+               dev_err(&pdev->dev, "cdev_add err id %d rc %d\n", mdev->id, rc);
+               goto cleanup_debug_dir;
+       }
+       return 0;
+cleanup_debug_dir:
+       mic_delete_debug_dir(mdev);
+       mutex_lock(&mdev->mic_mutex);
+       mic_free_irq(mdev, mdev->shutdown_cookie, mdev);
+       mutex_unlock(&mdev->mic_mutex);
+dp_uninit:
+       mic_dp_uninit(mdev);
+sysfs_put:
+       sysfs_put(mdev->state_sysfs);
+destroy_device:
+       device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id));
+smpt_uninit:
+       mic_smpt_uninit(mdev);
+free_interrupts:
+       mic_free_interrupts(mdev, pdev);
+unmap_aper:
+       iounmap(mdev->aper.va);
+unmap_mmio:
+       iounmap(mdev->mmio.va);
+release_regions:
+       pci_release_regions(pdev);
+disable_device:
+       pci_disable_device(pdev);
+uninit_device:
+       mic_device_uninit(mdev);
+device_init_fail:
+       ida_simple_remove(&g_mic_ida, mdev->id);
+ida_fail:
+       kfree(mdev);
+mdev_alloc_fail:
+       dev_err(&pdev->dev, "Probe failed rc %d\n", rc);
+       return rc;
+}
+
+/**
+ * mic_remove - Device Removal Routine
+ * mic_remove is called by the PCI subsystem to alert the driver
+ * that it should release a PCI device.
+ *
+ * @pdev: PCI device structure
+ */
+static void mic_remove(struct pci_dev *pdev)
+{
+       struct mic_device *mdev;
+
+       mdev = pci_get_drvdata(pdev);
+       if (!mdev)
+               return;
+
+       mic_stop(mdev, false);
+       cdev_del(&mdev->cdev);
+       mic_delete_debug_dir(mdev);
+       mutex_lock(&mdev->mic_mutex);
+       mic_free_irq(mdev, mdev->shutdown_cookie, mdev);
+       mutex_unlock(&mdev->mic_mutex);
+       flush_work(&mdev->shutdown_work);
+       mic_dp_uninit(mdev);
+       sysfs_put(mdev->state_sysfs);
+       device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id));
+       mic_smpt_uninit(mdev);
+       mic_free_interrupts(mdev, pdev);
+       iounmap(mdev->mmio.va);
+       iounmap(mdev->aper.va);
+       mic_device_uninit(mdev);
+       pci_release_regions(pdev);
+       pci_disable_device(pdev);
+       ida_simple_remove(&g_mic_ida, mdev->id);
+       kfree(mdev);
+}
+static struct pci_driver mic_driver = {
+       .name = mic_driver_name,
+       .id_table = mic_pci_tbl,
+       .probe = mic_probe,
+       .remove = mic_remove
+};
+
+static int __init mic_init(void)
+{
+       int ret;
+
+       ret = alloc_chrdev_region(&g_mic_devno, 0,
+               MIC_MAX_NUM_DEVS, mic_driver_name);
+       if (ret) {
+               pr_err("alloc_chrdev_region failed ret %d\n", ret);
+               goto error;
+       }
+
+       g_mic_class = class_create(THIS_MODULE, mic_driver_name);
+       if (IS_ERR(g_mic_class)) {
+               ret = PTR_ERR(g_mic_class);
+               pr_err("class_create failed ret %d\n", ret);
+               goto cleanup_chrdev;
+       }
+
+       mic_init_debugfs();
+       ida_init(&g_mic_ida);
+       ret = pci_register_driver(&mic_driver);
+       if (ret) {
+               pr_err("pci_register_driver failed ret %d\n", ret);
+               goto cleanup_debugfs;
+       }
+       return ret;
+cleanup_debugfs:
+       mic_exit_debugfs();
+       class_destroy(g_mic_class);
+cleanup_chrdev:
+       unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS);
+error:
+       return ret;
+}
+
+static void __exit mic_exit(void)
+{
+       pci_unregister_driver(&mic_driver);
+       ida_destroy(&g_mic_ida);
+       mic_exit_debugfs();
+       class_destroy(g_mic_class);
+       unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS);
+}
+
+module_init(mic_init);
+module_exit(mic_exit);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("Intel(R) MIC X100 Host driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/mic/host/mic_smpt.c b/drivers/misc/mic/host/mic_smpt.c
new file mode 100644 (file)
index 0000000..fae474c
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/pci.h>
+
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_smpt.h"
+
+static inline u64 mic_system_page_mask(struct mic_device *mdev)
+{
+       return (1ULL << mdev->smpt->info.page_shift) - 1ULL;
+}
+
+static inline u8 mic_sys_addr_to_smpt(struct mic_device *mdev, dma_addr_t pa)
+{
+       return (pa - mdev->smpt->info.base) >> mdev->smpt->info.page_shift;
+}
+
+static inline u64 mic_smpt_to_pa(struct mic_device *mdev, u8 index)
+{
+       return mdev->smpt->info.base + (index * mdev->smpt->info.page_size);
+}
+
+static inline u64 mic_smpt_offset(struct mic_device *mdev, dma_addr_t pa)
+{
+       return pa & mic_system_page_mask(mdev);
+}
+
+static inline u64 mic_smpt_align_low(struct mic_device *mdev, dma_addr_t pa)
+{
+       return ALIGN(pa - mic_system_page_mask(mdev),
+               mdev->smpt->info.page_size);
+}
+
+static inline u64 mic_smpt_align_high(struct mic_device *mdev, dma_addr_t pa)
+{
+       return ALIGN(pa, mdev->smpt->info.page_size);
+}
+
+/* Total Cumulative system memory accessible by MIC across all SMPT entries */
+static inline u64 mic_max_system_memory(struct mic_device *mdev)
+{
+       return mdev->smpt->info.num_reg * mdev->smpt->info.page_size;
+}
+
+/* Maximum system memory address accessible by MIC */
+static inline u64 mic_max_system_addr(struct mic_device *mdev)
+{
+       return mdev->smpt->info.base + mic_max_system_memory(mdev) - 1ULL;
+}
+
+/* Check if the DMA address is a MIC system memory address */
+static inline bool
+mic_is_system_addr(struct mic_device *mdev, dma_addr_t pa)
+{
+       return pa >= mdev->smpt->info.base && pa <= mic_max_system_addr(mdev);
+}
+
+/* Populate an SMPT entry and update the reference counts. */
+static void mic_add_smpt_entry(int spt, s64 *ref, u64 addr,
+               int entries, struct mic_device *mdev)
+{
+       struct mic_smpt_info *smpt_info = mdev->smpt;
+       int i;
+
+       for (i = spt; i < spt + entries; i++,
+               addr += smpt_info->info.page_size) {
+               if (!smpt_info->entry[i].ref_count &&
+                   (smpt_info->entry[i].dma_addr != addr)) {
+                       mdev->smpt_ops->set(mdev, addr, i);
+                       smpt_info->entry[i].dma_addr = addr;
+               }
+               smpt_info->entry[i].ref_count += ref[i - spt];
+       }
+}
+
+/*
+ * Find an available MIC address in MIC SMPT address space
+ * for a given DMA address and size.
+ */
+static dma_addr_t mic_smpt_op(struct mic_device *mdev, u64 dma_addr,
+                               int entries, s64 *ref, size_t size)
+{
+       int spt;
+       int ae = 0;
+       int i;
+       unsigned long flags;
+       dma_addr_t mic_addr = 0;
+       dma_addr_t addr = dma_addr;
+       struct mic_smpt_info *smpt_info = mdev->smpt;
+
+       spin_lock_irqsave(&smpt_info->smpt_lock, flags);
+
+       /* find existing entries */
+       for (i = 0; i < smpt_info->info.num_reg; i++) {
+               if (smpt_info->entry[i].dma_addr == addr) {
+                       ae++;
+                       addr += smpt_info->info.page_size;
+               } else if (ae) /* cannot find contiguous entries */
+                       goto not_found;
+
+               if (ae == entries)
+                       goto found;
+       }
+
+       /* find free entry */
+       for (ae = 0, i = 0; i < smpt_info->info.num_reg; i++) {
+               ae = (smpt_info->entry[i].ref_count == 0) ? ae + 1 : 0;
+               if (ae == entries)
+                       goto found;
+       }
+
+not_found:
+       spin_unlock_irqrestore(&smpt_info->smpt_lock, flags);
+       return mic_addr;
+
+found:
+       spt = i - entries + 1;
+       mic_addr = mic_smpt_to_pa(mdev, spt);
+       mic_add_smpt_entry(spt, ref, dma_addr, entries, mdev);
+       smpt_info->map_count++;
+       smpt_info->ref_count += (s64)size;
+       spin_unlock_irqrestore(&smpt_info->smpt_lock, flags);
+       return mic_addr;
+}
+
+/*
+ * Returns number of smpt entries needed for dma_addr to dma_addr + size
+ * also returns the reference count array for each of those entries
+ * and the starting smpt address
+ */
+static int mic_get_smpt_ref_count(struct mic_device *mdev, dma_addr_t dma_addr,
+                               size_t size, s64 *ref,  u64 *smpt_start)
+{
+       u64 start =  dma_addr;
+       u64 end = dma_addr + size;
+       int i = 0;
+
+       while (start < end) {
+               ref[i++] = min(mic_smpt_align_high(mdev, start + 1),
+                       end) - start;
+               start = mic_smpt_align_high(mdev, start + 1);
+       }
+
+       if (smpt_start)
+               *smpt_start = mic_smpt_align_low(mdev, dma_addr);
+
+       return i;
+}
+
+/*
+ * mic_to_dma_addr - Converts a MIC address to a DMA address.
+ *
+ * @mdev: pointer to mic_device instance.
+ * @mic_addr: MIC address.
+ *
+ * returns a DMA address.
+ */
+static dma_addr_t
+mic_to_dma_addr(struct mic_device *mdev, dma_addr_t mic_addr)
+{
+       struct mic_smpt_info *smpt_info = mdev->smpt;
+       int spt;
+       dma_addr_t dma_addr;
+
+       if (!mic_is_system_addr(mdev, mic_addr)) {
+               dev_err(mdev->sdev->parent,
+                       "mic_addr is invalid. mic_addr = 0x%llx\n", mic_addr);
+               return -EINVAL;
+       }
+       spt = mic_sys_addr_to_smpt(mdev, mic_addr);
+       dma_addr = smpt_info->entry[spt].dma_addr +
+               mic_smpt_offset(mdev, mic_addr);
+       return dma_addr;
+}
+
+/**
+ * mic_map - Maps a DMA address to a MIC physical address.
+ *
+ * @mdev: pointer to mic_device instance.
+ * @dma_addr: DMA address.
+ * @size: Size of the region to be mapped.
+ *
+ * This API converts the DMA address provided to a DMA address understood
+ * by MIC. Caller should check for errors by calling mic_map_error(..).
+ *
+ * returns DMA address as required by MIC.
+ */
+dma_addr_t mic_map(struct mic_device *mdev, dma_addr_t dma_addr, size_t size)
+{
+       dma_addr_t mic_addr = 0;
+       int num_entries;
+       s64 *ref;
+       u64 smpt_start;
+
+       if (!size || size > mic_max_system_memory(mdev))
+               return mic_addr;
+
+       ref = kmalloc(mdev->smpt->info.num_reg * sizeof(s64), GFP_KERNEL);
+       if (!ref)
+               return mic_addr;
+
+       num_entries = mic_get_smpt_ref_count(mdev, dma_addr, size,
+               ref, &smpt_start);
+
+       /* Set the smpt table appropriately and get 16G aligned mic address */
+       mic_addr = mic_smpt_op(mdev, smpt_start, num_entries, ref, size);
+
+       kfree(ref);
+
+       /*
+        * If mic_addr is zero then its an error case
+        * since mic_addr can never be zero.
+        * else generate mic_addr by adding the 16G offset in dma_addr
+        */
+       if (!mic_addr && MIC_FAMILY_X100 == mdev->family) {
+               dev_err(mdev->sdev->parent,
+                       "mic_map failed dma_addr 0x%llx size 0x%lx\n",
+                       dma_addr, size);
+               return mic_addr;
+       } else {
+               return mic_addr + mic_smpt_offset(mdev, dma_addr);
+       }
+}
+
+/**
+ * mic_unmap - Unmaps a MIC physical address.
+ *
+ * @mdev: pointer to mic_device instance.
+ * @mic_addr: MIC physical address.
+ * @size: Size of the region to be unmapped.
+ *
+ * This API unmaps the mappings created by mic_map(..).
+ *
+ * returns None.
+ */
+void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size)
+{
+       struct mic_smpt_info *smpt_info = mdev->smpt;
+       s64 *ref;
+       int num_smpt;
+       int spt;
+       int i;
+       unsigned long flags;
+
+       if (!size)
+               return;
+
+       if (!mic_is_system_addr(mdev, mic_addr)) {
+               dev_err(mdev->sdev->parent,
+                       "invalid address: 0x%llx\n", mic_addr);
+               return;
+       }
+
+       spt = mic_sys_addr_to_smpt(mdev, mic_addr);
+       ref = kmalloc(mdev->smpt->info.num_reg * sizeof(s64), GFP_KERNEL);
+       if (!ref)
+               return;
+
+       /* Get number of smpt entries to be mapped, ref count array */
+       num_smpt = mic_get_smpt_ref_count(mdev, mic_addr, size, ref, NULL);
+
+       spin_lock_irqsave(&smpt_info->smpt_lock, flags);
+       smpt_info->unmap_count++;
+       smpt_info->ref_count -= (s64)size;
+
+       for (i = spt; i < spt + num_smpt; i++) {
+               smpt_info->entry[i].ref_count -= ref[i - spt];
+               if (smpt_info->entry[i].ref_count < 0)
+                       dev_warn(mdev->sdev->parent,
+                                "ref count for entry %d is negative\n", i);
+       }
+       spin_unlock_irqrestore(&smpt_info->smpt_lock, flags);
+       kfree(ref);
+}
+
+/**
+ * mic_map_single - Maps a virtual address to a MIC physical address.
+ *
+ * @mdev: pointer to mic_device instance.
+ * @va: Kernel direct mapped virtual address.
+ * @size: Size of the region to be mapped.
+ *
+ * This API calls pci_map_single(..) for the direct mapped virtual address
+ * and then converts the DMA address provided to a DMA address understood
+ * by MIC. Caller should check for errors by calling mic_map_error(..).
+ *
+ * returns DMA address as required by MIC.
+ */
+dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size)
+{
+       dma_addr_t mic_addr = 0;
+       struct pci_dev *pdev = container_of(mdev->sdev->parent,
+               struct pci_dev, dev);
+       dma_addr_t dma_addr =
+               pci_map_single(pdev, va, size, PCI_DMA_BIDIRECTIONAL);
+
+       if (!pci_dma_mapping_error(pdev, dma_addr)) {
+               mic_addr = mic_map(mdev, dma_addr, size);
+               if (!mic_addr) {
+                       dev_err(mdev->sdev->parent,
+                               "mic_map failed dma_addr 0x%llx size 0x%lx\n",
+                               dma_addr, size);
+                       pci_unmap_single(pdev, dma_addr,
+                                        size, PCI_DMA_BIDIRECTIONAL);
+               }
+       }
+       return mic_addr;
+}
+
+/**
+ * mic_unmap_single - Unmaps a MIC physical address.
+ *
+ * @mdev: pointer to mic_device instance.
+ * @mic_addr: MIC physical address.
+ * @size: Size of the region to be unmapped.
+ *
+ * This API unmaps the mappings created by mic_map_single(..).
+ *
+ * returns None.
+ */
+void
+mic_unmap_single(struct mic_device *mdev, dma_addr_t mic_addr, size_t size)
+{
+       struct pci_dev *pdev = container_of(mdev->sdev->parent,
+               struct pci_dev, dev);
+       dma_addr_t dma_addr = mic_to_dma_addr(mdev, mic_addr);
+       mic_unmap(mdev, mic_addr, size);
+       pci_unmap_single(pdev, dma_addr, size, PCI_DMA_BIDIRECTIONAL);
+}
+
+/**
+ * mic_smpt_init - Initialize MIC System Memory Page Tables.
+ *
+ * @mdev: pointer to mic_device instance.
+ *
+ * returns 0 for success and -errno for error.
+ */
+int mic_smpt_init(struct mic_device *mdev)
+{
+       int i, err = 0;
+       dma_addr_t dma_addr;
+       struct mic_smpt_info *smpt_info;
+
+       mdev->smpt = kmalloc(sizeof(*mdev->smpt), GFP_KERNEL);
+       if (!mdev->smpt)
+               return -ENOMEM;
+
+       smpt_info = mdev->smpt;
+       mdev->smpt_ops->init(mdev);
+       smpt_info->entry = kmalloc_array(smpt_info->info.num_reg,
+                                        sizeof(*smpt_info->entry), GFP_KERNEL);
+       if (!smpt_info->entry) {
+               err = -ENOMEM;
+               goto free_smpt;
+       }
+       spin_lock_init(&smpt_info->smpt_lock);
+       for (i = 0; i < smpt_info->info.num_reg; i++) {
+               dma_addr = i * smpt_info->info.page_size;
+               smpt_info->entry[i].dma_addr = dma_addr;
+               smpt_info->entry[i].ref_count = 0;
+               mdev->smpt_ops->set(mdev, dma_addr, i);
+       }
+       smpt_info->ref_count = 0;
+       smpt_info->map_count = 0;
+       smpt_info->unmap_count = 0;
+       return 0;
+free_smpt:
+       kfree(smpt_info);
+       return err;
+}
+
+/**
+ * mic_smpt_uninit - UnInitialize MIC System Memory Page Tables.
+ *
+ * @mdev: pointer to mic_device instance.
+ *
+ * returns None.
+ */
+void mic_smpt_uninit(struct mic_device *mdev)
+{
+       struct mic_smpt_info *smpt_info = mdev->smpt;
+       int i;
+
+       dev_dbg(mdev->sdev->parent,
+               "nodeid %d SMPT ref count %lld map %lld unmap %lld\n",
+               mdev->id, smpt_info->ref_count,
+               smpt_info->map_count, smpt_info->unmap_count);
+
+       for (i = 0; i < smpt_info->info.num_reg; i++) {
+               dev_dbg(mdev->sdev->parent,
+                       "SMPT entry[%d] dma_addr = 0x%llx ref_count = %lld\n",
+                       i, smpt_info->entry[i].dma_addr,
+                       smpt_info->entry[i].ref_count);
+               if (smpt_info->entry[i].ref_count)
+                       dev_warn(mdev->sdev->parent,
+                                "ref count for entry %d is not zero\n", i);
+       }
+       kfree(smpt_info->entry);
+       kfree(smpt_info);
+}
+
+/**
+ * mic_smpt_restore - Restore MIC System Memory Page Tables.
+ *
+ * @mdev: pointer to mic_device instance.
+ *
+ * Restore the SMPT registers to values previously stored in the
+ * SW data structures. Some MIC steppings lose register state
+ * across resets and this API should be called for performing
+ * a restore operation if required.
+ *
+ * returns None.
+ */
+void mic_smpt_restore(struct mic_device *mdev)
+{
+       int i;
+       dma_addr_t dma_addr;
+
+       for (i = 0; i < mdev->smpt->info.num_reg; i++) {
+               dma_addr = mdev->smpt->entry[i].dma_addr;
+               mdev->smpt_ops->set(mdev, dma_addr, i);
+       }
+}
diff --git a/drivers/misc/mic/host/mic_smpt.h b/drivers/misc/mic/host/mic_smpt.h
new file mode 100644 (file)
index 0000000..51970ab
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#ifndef MIC_SMPT_H
+#define MIC_SMPT_H
+/**
+ * struct mic_smpt_ops - MIC HW specific SMPT operations.
+ * @init: Initialize hardware specific SMPT information in mic_smpt_hw_info.
+ * @set: Set the value for a particular SMPT entry.
+ */
+struct mic_smpt_ops {
+       void (*init)(struct mic_device *mdev);
+       void (*set)(struct mic_device *mdev, dma_addr_t dma_addr, u8 index);
+};
+
+/**
+ * struct mic_smpt - MIC SMPT entry information.
+ * @dma_addr: Base DMA address for this SMPT entry.
+ * @ref_count: Number of active mappings for this SMPT entry in bytes.
+ */
+struct mic_smpt {
+       dma_addr_t dma_addr;
+       s64 ref_count;
+};
+
+/**
+ * struct mic_smpt_hw_info - MIC SMPT hardware specific information.
+ * @num_reg: Number of SMPT registers.
+ * @page_shift: System memory page shift.
+ * @page_size: System memory page size.
+ * @base: System address base.
+ */
+struct mic_smpt_hw_info {
+       u8 num_reg;
+       u8 page_shift;
+       u64 page_size;
+       u64 base;
+};
+
+/**
+ * struct mic_smpt_info - MIC SMPT information.
+ * @entry: Array of SMPT entries.
+ * @smpt_lock: Spin lock protecting access to SMPT data structures.
+ * @info: Hardware specific SMPT information.
+ * @ref_count: Number of active SMPT mappings (for debug).
+ * @map_count: Number of SMPT mappings created (for debug).
+ * @unmap_count: Number of SMPT mappings destroyed (for debug).
+ */
+struct mic_smpt_info {
+       struct mic_smpt *entry;
+       spinlock_t smpt_lock;
+       struct mic_smpt_hw_info info;
+       s64 ref_count;
+       s64 map_count;
+       s64 unmap_count;
+};
+
+dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size);
+void mic_unmap_single(struct mic_device *mdev,
+       dma_addr_t mic_addr, size_t size);
+dma_addr_t mic_map(struct mic_device *mdev,
+       dma_addr_t dma_addr, size_t size);
+void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size);
+
+/**
+ * mic_map_error - Check a MIC address for errors.
+ *
+ * @mdev: pointer to mic_device instance.
+ *
+ * returns Whether there was an error during mic_map..(..) APIs.
+ */
+static inline bool mic_map_error(dma_addr_t mic_addr)
+{
+       return !mic_addr;
+}
+
+int mic_smpt_init(struct mic_device *mdev);
+void mic_smpt_uninit(struct mic_device *mdev);
+void mic_smpt_restore(struct mic_device *mdev);
+
+#endif
diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c
new file mode 100644 (file)
index 0000000..6dd864e
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/pci.h>
+
+#include <linux/mic_common.h>
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+
+/*
+ * A state-to-string lookup table, for exposing a human readable state
+ * via sysfs. Always keep in sync with enum mic_states
+ */
+static const char * const mic_state_string[] = {
+       [MIC_OFFLINE] = "offline",
+       [MIC_ONLINE] = "online",
+       [MIC_SHUTTING_DOWN] = "shutting_down",
+       [MIC_RESET_FAILED] = "reset_failed",
+       [MIC_SUSPENDING] = "suspending",
+       [MIC_SUSPENDED] = "suspended",
+};
+
+/*
+ * A shutdown-status-to-string lookup table, for exposing a human
+ * readable state via sysfs. Always keep in sync with enum mic_shutdown_status
+ */
+static const char * const mic_shutdown_status_string[] = {
+       [MIC_NOP] = "nop",
+       [MIC_CRASHED] = "crashed",
+       [MIC_HALTED] = "halted",
+       [MIC_POWER_OFF] = "poweroff",
+       [MIC_RESTART] = "restart",
+};
+
+void mic_set_shutdown_status(struct mic_device *mdev, u8 shutdown_status)
+{
+       dev_dbg(mdev->sdev->parent, "Shutdown Status %s -> %s\n",
+               mic_shutdown_status_string[mdev->shutdown_status],
+               mic_shutdown_status_string[shutdown_status]);
+       mdev->shutdown_status = shutdown_status;
+}
+
+void mic_set_state(struct mic_device *mdev, u8 state)
+{
+       dev_dbg(mdev->sdev->parent, "State %s -> %s\n",
+               mic_state_string[mdev->state],
+               mic_state_string[state]);
+       mdev->state = state;
+       sysfs_notify_dirent(mdev->state_sysfs);
+}
+
+static ssize_t
+family_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       static const char x100[] = "x100";
+       static const char unknown[] = "Unknown";
+       const char *card = NULL;
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev)
+               return -EINVAL;
+
+       switch (mdev->family) {
+       case MIC_FAMILY_X100:
+               card = x100;
+               break;
+       default:
+               card = unknown;
+               break;
+       }
+       return scnprintf(buf, PAGE_SIZE, "%s\n", card);
+}
+static DEVICE_ATTR_RO(family);
+
+static ssize_t
+stepping_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       char *string = "??";
+
+       if (!mdev)
+               return -EINVAL;
+
+       switch (mdev->stepping) {
+       case MIC_A0_STEP:
+               string = "A0";
+               break;
+       case MIC_B0_STEP:
+               string = "B0";
+               break;
+       case MIC_B1_STEP:
+               string = "B1";
+               break;
+       case MIC_C0_STEP:
+               string = "C0";
+               break;
+       default:
+               break;
+       }
+       return scnprintf(buf, PAGE_SIZE, "%s\n", string);
+}
+static DEVICE_ATTR_RO(stepping);
+
+static ssize_t
+state_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev || mdev->state >= MIC_LAST)
+               return -EINVAL;
+
+       return scnprintf(buf, PAGE_SIZE, "%s\n",
+               mic_state_string[mdev->state]);
+}
+
+static ssize_t
+state_store(struct device *dev, struct device_attribute *attr,
+           const char *buf, size_t count)
+{
+       int rc = 0;
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       if (!mdev)
+               return -EINVAL;
+       if (sysfs_streq(buf, "boot")) {
+               rc = mic_start(mdev, buf);
+               if (rc) {
+                       dev_err(mdev->sdev->parent,
+                               "mic_boot failed rc %d\n", rc);
+                       count = rc;
+               }
+               goto done;
+       }
+
+       if (sysfs_streq(buf, "reset")) {
+               schedule_work(&mdev->reset_trigger_work);
+               goto done;
+       }
+
+       if (sysfs_streq(buf, "shutdown")) {
+               mic_shutdown(mdev);
+               goto done;
+       }
+
+       if (sysfs_streq(buf, "suspend")) {
+               mic_suspend(mdev);
+               goto done;
+       }
+
+       count = -EINVAL;
+done:
+       return count;
+}
+static DEVICE_ATTR_RW(state);
+
+static ssize_t shutdown_status_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev || mdev->shutdown_status >= MIC_STATUS_LAST)
+               return -EINVAL;
+
+       return scnprintf(buf, PAGE_SIZE, "%s\n",
+               mic_shutdown_status_string[mdev->shutdown_status]);
+}
+static DEVICE_ATTR_RO(shutdown_status);
+
+static ssize_t
+cmdline_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       char *cmdline;
+
+       if (!mdev)
+               return -EINVAL;
+
+       cmdline = mdev->cmdline;
+
+       if (cmdline)
+               return scnprintf(buf, PAGE_SIZE, "%s\n", cmdline);
+       return 0;
+}
+
+static ssize_t
+cmdline_store(struct device *dev, struct device_attribute *attr,
+             const char *buf, size_t count)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev)
+               return -EINVAL;
+
+       mutex_lock(&mdev->mic_mutex);
+       kfree(mdev->cmdline);
+
+       mdev->cmdline = kmalloc(count + 1, GFP_KERNEL);
+       if (!mdev->cmdline) {
+               count = -ENOMEM;
+               goto unlock;
+       }
+
+       strncpy(mdev->cmdline, buf, count);
+
+       if (mdev->cmdline[count - 1] == '\n')
+               mdev->cmdline[count - 1] = '\0';
+       else
+               mdev->cmdline[count] = '\0';
+unlock:
+       mutex_unlock(&mdev->mic_mutex);
+       return count;
+}
+static DEVICE_ATTR_RW(cmdline);
+
+static ssize_t
+firmware_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       char *firmware;
+
+       if (!mdev)
+               return -EINVAL;
+
+       firmware = mdev->firmware;
+
+       if (firmware)
+               return scnprintf(buf, PAGE_SIZE, "%s\n", firmware);
+       return 0;
+}
+
+static ssize_t
+firmware_store(struct device *dev, struct device_attribute *attr,
+              const char *buf, size_t count)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev)
+               return -EINVAL;
+
+       mutex_lock(&mdev->mic_mutex);
+       kfree(mdev->firmware);
+
+       mdev->firmware = kmalloc(count + 1, GFP_KERNEL);
+       if (!mdev->firmware) {
+               count = -ENOMEM;
+               goto unlock;
+       }
+       strncpy(mdev->firmware, buf, count);
+
+       if (mdev->firmware[count - 1] == '\n')
+               mdev->firmware[count - 1] = '\0';
+       else
+               mdev->firmware[count] = '\0';
+unlock:
+       mutex_unlock(&mdev->mic_mutex);
+       return count;
+}
+static DEVICE_ATTR_RW(firmware);
+
+static ssize_t
+ramdisk_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       char *ramdisk;
+
+       if (!mdev)
+               return -EINVAL;
+
+       ramdisk = mdev->ramdisk;
+
+       if (ramdisk)
+               return scnprintf(buf, PAGE_SIZE, "%s\n", ramdisk);
+       return 0;
+}
+
+static ssize_t
+ramdisk_store(struct device *dev, struct device_attribute *attr,
+             const char *buf, size_t count)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev)
+               return -EINVAL;
+
+       mutex_lock(&mdev->mic_mutex);
+       kfree(mdev->ramdisk);
+
+       mdev->ramdisk = kmalloc(count + 1, GFP_KERNEL);
+       if (!mdev->ramdisk) {
+               count = -ENOMEM;
+               goto unlock;
+       }
+
+       strncpy(mdev->ramdisk, buf, count);
+
+       if (mdev->ramdisk[count - 1] == '\n')
+               mdev->ramdisk[count - 1] = '\0';
+       else
+               mdev->ramdisk[count] = '\0';
+unlock:
+       mutex_unlock(&mdev->mic_mutex);
+       return count;
+}
+static DEVICE_ATTR_RW(ramdisk);
+
+static ssize_t
+bootmode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       char *bootmode;
+
+       if (!mdev)
+               return -EINVAL;
+
+       bootmode = mdev->bootmode;
+
+       if (bootmode)
+               return scnprintf(buf, PAGE_SIZE, "%s\n", bootmode);
+       return 0;
+}
+
+static ssize_t
+bootmode_store(struct device *dev, struct device_attribute *attr,
+              const char *buf, size_t count)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev)
+               return -EINVAL;
+
+       if (!sysfs_streq(buf, "linux") && !sysfs_streq(buf, "elf"))
+               return -EINVAL;
+
+       mutex_lock(&mdev->mic_mutex);
+       kfree(mdev->bootmode);
+
+       mdev->bootmode = kmalloc(count + 1, GFP_KERNEL);
+       if (!mdev->bootmode) {
+               count = -ENOMEM;
+               goto unlock;
+       }
+
+       strncpy(mdev->bootmode, buf, count);
+
+       if (mdev->bootmode[count - 1] == '\n')
+               mdev->bootmode[count - 1] = '\0';
+       else
+               mdev->bootmode[count] = '\0';
+unlock:
+       mutex_unlock(&mdev->mic_mutex);
+       return count;
+}
+static DEVICE_ATTR_RW(bootmode);
+
+static ssize_t
+log_buf_addr_show(struct device *dev, struct device_attribute *attr,
+                 char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev)
+               return -EINVAL;
+
+       return scnprintf(buf, PAGE_SIZE, "%p\n", mdev->log_buf_addr);
+}
+
+static ssize_t
+log_buf_addr_store(struct device *dev, struct device_attribute *attr,
+                  const char *buf, size_t count)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       int ret;
+       unsigned long addr;
+
+       if (!mdev)
+               return -EINVAL;
+
+       ret = kstrtoul(buf, 16, &addr);
+       if (ret)
+               goto exit;
+
+       mdev->log_buf_addr = (void *)addr;
+       ret = count;
+exit:
+       return ret;
+}
+static DEVICE_ATTR_RW(log_buf_addr);
+
+static ssize_t
+log_buf_len_show(struct device *dev, struct device_attribute *attr,
+                char *buf)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+       if (!mdev)
+               return -EINVAL;
+
+       return scnprintf(buf, PAGE_SIZE, "%p\n", mdev->log_buf_len);
+}
+
+static ssize_t
+log_buf_len_store(struct device *dev, struct device_attribute *attr,
+                 const char *buf, size_t count)
+{
+       struct mic_device *mdev = dev_get_drvdata(dev->parent);
+       int ret;
+       unsigned long addr;
+
+       if (!mdev)
+               return -EINVAL;
+
+       ret = kstrtoul(buf, 16, &addr);
+       if (ret)
+               goto exit;
+
+       mdev->log_buf_len = (int *)addr;
+       ret = count;
+exit:
+       return ret;
+}
+static DEVICE_ATTR_RW(log_buf_len);
+
+static struct attribute *mic_default_attrs[] = {
+       &dev_attr_family.attr,
+       &dev_attr_stepping.attr,
+       &dev_attr_state.attr,
+       &dev_attr_shutdown_status.attr,
+       &dev_attr_cmdline.attr,
+       &dev_attr_firmware.attr,
+       &dev_attr_ramdisk.attr,
+       &dev_attr_bootmode.attr,
+       &dev_attr_log_buf_addr.attr,
+       &dev_attr_log_buf_len.attr,
+
+       NULL
+};
+
+ATTRIBUTE_GROUPS(mic_default);
+
+void mic_sysfs_init(struct mic_device *mdev)
+{
+       mdev->attr_group = mic_default_groups;
+}
diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c
new file mode 100644 (file)
index 0000000..0c883cd
--- /dev/null
@@ -0,0 +1,700 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+
+#include <linux/mic_common.h>
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_smpt.h"
+#include "mic_virtio.h"
+
+/*
+ * Initiates the copies across the PCIe bus from card memory to
+ * a user space buffer.
+ */
+static int mic_virtio_copy_to_user(struct mic_vdev *mvdev,
+               void __user *ubuf, size_t len, u64 addr)
+{
+       int err;
+       void __iomem *dbuf = mvdev->mdev->aper.va + addr;
+       /*
+        * We are copying from IO below an should ideally use something
+        * like copy_to_user_fromio(..) if it existed.
+        */
+       if (copy_to_user(ubuf, dbuf, len)) {
+               err = -EFAULT;
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, err);
+               goto err;
+       }
+       mvdev->in_bytes += len;
+       err = 0;
+err:
+       return err;
+}
+
+/*
+ * Initiates copies across the PCIe bus from a user space
+ * buffer to card memory.
+ */
+static int mic_virtio_copy_from_user(struct mic_vdev *mvdev,
+               void __user *ubuf, size_t len, u64 addr)
+{
+       int err;
+       void __iomem *dbuf = mvdev->mdev->aper.va + addr;
+       /*
+        * We are copying to IO below and should ideally use something
+        * like copy_from_user_toio(..) if it existed.
+        */
+       if (copy_from_user(dbuf, ubuf, len)) {
+               err = -EFAULT;
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, err);
+               goto err;
+       }
+       mvdev->out_bytes += len;
+       err = 0;
+err:
+       return err;
+}
+
+#define MIC_VRINGH_READ true
+
+/* The function to call to notify the card about added buffers */
+static void mic_notify(struct vringh *vrh)
+{
+       struct mic_vringh *mvrh = container_of(vrh, struct mic_vringh, vrh);
+       struct mic_vdev *mvdev = mvrh->mvdev;
+       s8 db = mvdev->dc->h2c_vdev_db;
+
+       if (db != -1)
+               mvdev->mdev->ops->send_intr(mvdev->mdev, db);
+}
+
+/* Determine the total number of bytes consumed in a VRINGH KIOV */
+static inline u32 mic_vringh_iov_consumed(struct vringh_kiov *iov)
+{
+       int i;
+       u32 total = iov->consumed;
+
+       for (i = 0; i < iov->i; i++)
+               total += iov->iov[i].iov_len;
+       return total;
+}
+
+/*
+ * Traverse the VRINGH KIOV and issue the APIs to trigger the copies.
+ * This API is heavily based on the vringh_iov_xfer(..) implementation
+ * in vringh.c. The reason we cannot reuse vringh_iov_pull_kern(..)
+ * and vringh_iov_push_kern(..) directly is because there is no
+ * way to override the VRINGH xfer(..) routines as of v3.10.
+ */
+static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov,
+       void __user *ubuf, size_t len, bool read, size_t *out_len)
+{
+       int ret = 0;
+       size_t partlen, tot_len = 0;
+
+       while (len && iov->i < iov->used) {
+               partlen = min(iov->iov[iov->i].iov_len, len);
+               if (read)
+                       ret = mic_virtio_copy_to_user(mvdev,
+                               ubuf, partlen,
+                               (u64)iov->iov[iov->i].iov_base);
+               else
+                       ret = mic_virtio_copy_from_user(mvdev,
+                               ubuf, partlen,
+                               (u64)iov->iov[iov->i].iov_base);
+               if (ret) {
+                       dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                               __func__, __LINE__, ret);
+                       break;
+               }
+               len -= partlen;
+               ubuf += partlen;
+               tot_len += partlen;
+               iov->consumed += partlen;
+               iov->iov[iov->i].iov_len -= partlen;
+               iov->iov[iov->i].iov_base += partlen;
+               if (!iov->iov[iov->i].iov_len) {
+                       /* Fix up old iov element then increment. */
+                       iov->iov[iov->i].iov_len = iov->consumed;
+                       iov->iov[iov->i].iov_base -= iov->consumed;
+
+                       iov->consumed = 0;
+                       iov->i++;
+               }
+       }
+       *out_len = tot_len;
+       return ret;
+}
+
+/*
+ * Use the standard VRINGH infrastructure in the kernel to fetch new
+ * descriptors, initiate the copies and update the used ring.
+ */
+static int _mic_virtio_copy(struct mic_vdev *mvdev,
+       struct mic_copy_desc *copy)
+{
+       int ret = 0, iovcnt = copy->iovcnt;
+       struct iovec iov;
+       struct iovec __user *u_iov = copy->iov;
+       void __user *ubuf = NULL;
+       struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx];
+       struct vringh_kiov *riov = &mvr->riov;
+       struct vringh_kiov *wiov = &mvr->wiov;
+       struct vringh *vrh = &mvr->vrh;
+       u16 *head = &mvr->head;
+       struct mic_vring *vr = &mvr->vring;
+       size_t len = 0, out_len;
+
+       copy->out_len = 0;
+       /* Fetch a new IOVEC if all previous elements have been processed */
+       if (riov->i == riov->used && wiov->i == wiov->used) {
+               ret = vringh_getdesc_kern(vrh, riov, wiov,
+                               head, GFP_KERNEL);
+               /* Check if there are available descriptors */
+               if (ret <= 0)
+                       return ret;
+       }
+       while (iovcnt) {
+               if (!len) {
+                       /* Copy over a new iovec from user space. */
+                       ret = copy_from_user(&iov, u_iov, sizeof(*u_iov));
+                       if (ret) {
+                               ret = -EINVAL;
+                               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                                       __func__, __LINE__, ret);
+                               break;
+                       }
+                       len = iov.iov_len;
+                       ubuf = iov.iov_base;
+               }
+               /* Issue all the read descriptors first */
+               ret = mic_vringh_copy(mvdev, riov, ubuf, len,
+                       MIC_VRINGH_READ, &out_len);
+               if (ret) {
+                       dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                               __func__, __LINE__, ret);
+                       break;
+               }
+               len -= out_len;
+               ubuf += out_len;
+               copy->out_len += out_len;
+               /* Issue the write descriptors next */
+               ret = mic_vringh_copy(mvdev, wiov, ubuf, len,
+                       !MIC_VRINGH_READ, &out_len);
+               if (ret) {
+                       dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                               __func__, __LINE__, ret);
+                       break;
+               }
+               len -= out_len;
+               ubuf += out_len;
+               copy->out_len += out_len;
+               if (!len) {
+                       /* One user space iovec is now completed */
+                       iovcnt--;
+                       u_iov++;
+               }
+               /* Exit loop if all elements in KIOVs have been processed. */
+               if (riov->i == riov->used && wiov->i == wiov->used)
+                       break;
+       }
+       /*
+        * Update the used ring if a descriptor was available and some data was
+        * copied in/out and the user asked for a used ring update.
+        */
+       if (*head != USHRT_MAX && copy->out_len && copy->update_used) {
+               u32 total = 0;
+
+               /* Determine the total data consumed */
+               total += mic_vringh_iov_consumed(riov);
+               total += mic_vringh_iov_consumed(wiov);
+               vringh_complete_kern(vrh, *head, total);
+               *head = USHRT_MAX;
+               if (vringh_need_notify_kern(vrh) > 0)
+                       vringh_notify(vrh);
+               vringh_kiov_cleanup(riov);
+               vringh_kiov_cleanup(wiov);
+               /* Update avail idx for user space */
+               vr->info->avail_idx = vrh->last_avail_idx;
+       }
+       return ret;
+}
+
+static inline int mic_verify_copy_args(struct mic_vdev *mvdev,
+               struct mic_copy_desc *copy)
+{
+       if (copy->vr_idx >= mvdev->dd->num_vq) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, -EINVAL);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/* Copy a specified number of virtio descriptors in a chain */
+int mic_virtio_copy_desc(struct mic_vdev *mvdev,
+               struct mic_copy_desc *copy)
+{
+       int err;
+       struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx];
+
+       err = mic_verify_copy_args(mvdev, copy);
+       if (err)
+               return err;
+
+       mutex_lock(&mvr->vr_mutex);
+       if (!mic_vdevup(mvdev)) {
+               err = -ENODEV;
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, err);
+               goto err;
+       }
+       err = _mic_virtio_copy(mvdev, copy);
+       if (err) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, err);
+       }
+err:
+       mutex_unlock(&mvr->vr_mutex);
+       return err;
+}
+
+static void mic_virtio_init_post(struct mic_vdev *mvdev)
+{
+       struct mic_vqconfig *vqconfig = mic_vq_config(mvdev->dd);
+       int i;
+
+       for (i = 0; i < mvdev->dd->num_vq; i++) {
+               if (!le64_to_cpu(vqconfig[i].used_address)) {
+                       dev_warn(mic_dev(mvdev), "used_address zero??\n");
+                       continue;
+               }
+               mvdev->mvr[i].vrh.vring.used =
+                       mvdev->mdev->aper.va +
+                       le64_to_cpu(vqconfig[i].used_address);
+       }
+
+       mvdev->dc->used_address_updated = 0;
+
+       dev_dbg(mic_dev(mvdev), "%s: device type %d LINKUP\n",
+               __func__, mvdev->virtio_id);
+}
+
+static inline void mic_virtio_device_reset(struct mic_vdev *mvdev)
+{
+       int i;
+
+       dev_dbg(mic_dev(mvdev), "%s: status %d device type %d RESET\n",
+               __func__, mvdev->dd->status, mvdev->virtio_id);
+
+       for (i = 0; i < mvdev->dd->num_vq; i++)
+               /*
+                * Avoid lockdep false positive. The + 1 is for the mic
+                * mutex which is held in the reset devices code path.
+                */
+               mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1);
+
+       /* 0 status means "reset" */
+       mvdev->dd->status = 0;
+       mvdev->dc->vdev_reset = 0;
+       mvdev->dc->host_ack = 1;
+
+       for (i = 0; i < mvdev->dd->num_vq; i++) {
+               struct vringh *vrh = &mvdev->mvr[i].vrh;
+               mvdev->mvr[i].vring.info->avail_idx = 0;
+               vrh->completed = 0;
+               vrh->last_avail_idx = 0;
+               vrh->last_used_idx = 0;
+       }
+
+       for (i = 0; i < mvdev->dd->num_vq; i++)
+               mutex_unlock(&mvdev->mvr[i].vr_mutex);
+}
+
+void mic_virtio_reset_devices(struct mic_device *mdev)
+{
+       struct list_head *pos, *tmp;
+       struct mic_vdev *mvdev;
+
+       dev_dbg(mdev->sdev->parent, "%s\n",  __func__);
+
+       list_for_each_safe(pos, tmp, &mdev->vdev_list) {
+               mvdev = list_entry(pos, struct mic_vdev, list);
+               mic_virtio_device_reset(mvdev);
+               mvdev->poll_wake = 1;
+               wake_up(&mvdev->waitq);
+       }
+}
+
+void mic_bh_handler(struct work_struct *work)
+{
+       struct mic_vdev *mvdev = container_of(work, struct mic_vdev,
+                       virtio_bh_work);
+
+       if (mvdev->dc->used_address_updated)
+               mic_virtio_init_post(mvdev);
+
+       if (mvdev->dc->vdev_reset)
+               mic_virtio_device_reset(mvdev);
+
+       mvdev->poll_wake = 1;
+       wake_up(&mvdev->waitq);
+}
+
+static irqreturn_t mic_virtio_intr_handler(int irq, void *data)
+{
+       struct mic_vdev *mvdev = data;
+       struct mic_device *mdev = mvdev->mdev;
+
+       mdev->ops->ack_interrupt(mdev);
+       schedule_work(&mvdev->virtio_bh_work);
+       return IRQ_HANDLED;
+}
+
+int mic_virtio_config_change(struct mic_vdev *mvdev,
+                       void __user *argp)
+{
+       DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake);
+       int ret = 0, retry = 100, i;
+       struct mic_bootparam *bootparam = mvdev->mdev->dp;
+       s8 db = bootparam->h2c_config_db;
+
+       mutex_lock(&mvdev->mdev->mic_mutex);
+       for (i = 0; i < mvdev->dd->num_vq; i++)
+               mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1);
+
+       if (db == -1 || mvdev->dd->type == -1) {
+               ret = -EIO;
+               goto exit;
+       }
+
+       if (copy_from_user(mic_vq_configspace(mvdev->dd),
+                          argp, mvdev->dd->config_len)) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, -EFAULT);
+               ret = -EFAULT;
+               goto exit;
+       }
+       mvdev->dc->config_change = MIC_VIRTIO_PARAM_CONFIG_CHANGED;
+       mvdev->mdev->ops->send_intr(mvdev->mdev, db);
+
+       for (i = retry; i--;) {
+               ret = wait_event_timeout(wake,
+                       mvdev->dc->guest_ack, msecs_to_jiffies(100));
+               if (ret)
+                       break;
+       }
+
+       dev_dbg(mic_dev(mvdev),
+               "%s %d retry: %d\n", __func__, __LINE__, retry);
+       mvdev->dc->config_change = 0;
+       mvdev->dc->guest_ack = 0;
+exit:
+       for (i = 0; i < mvdev->dd->num_vq; i++)
+               mutex_unlock(&mvdev->mvr[i].vr_mutex);
+       mutex_unlock(&mvdev->mdev->mic_mutex);
+       return ret;
+}
+
+static int mic_copy_dp_entry(struct mic_vdev *mvdev,
+                                       void __user *argp,
+                                       __u8 *type,
+                                       struct mic_device_desc **devpage)
+{
+       struct mic_device *mdev = mvdev->mdev;
+       struct mic_device_desc dd, *dd_config, *devp;
+       struct mic_vqconfig *vqconfig;
+       int ret = 0, i;
+       bool slot_found = false;
+
+       if (copy_from_user(&dd, argp, sizeof(dd))) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, -EFAULT);
+               return -EFAULT;
+       }
+
+       if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE ||
+           dd.num_vq > MIC_MAX_VRINGS) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, -EINVAL);
+               return -EINVAL;
+       }
+
+       dd_config = kmalloc(mic_desc_size(&dd), GFP_KERNEL);
+       if (dd_config == NULL) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, -ENOMEM);
+               return -ENOMEM;
+       }
+       if (copy_from_user(dd_config, argp, mic_desc_size(&dd))) {
+               ret = -EFAULT;
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, ret);
+               goto exit;
+       }
+
+       vqconfig = mic_vq_config(dd_config);
+       for (i = 0; i < dd.num_vq; i++) {
+               if (le16_to_cpu(vqconfig[i].num) > MIC_MAX_VRING_ENTRIES) {
+                       ret =  -EINVAL;
+                       dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                               __func__, __LINE__, ret);
+                       goto exit;
+               }
+       }
+
+       /* Find the first free device page entry */
+       for (i = mic_aligned_size(struct mic_bootparam);
+               i < MIC_DP_SIZE - mic_total_desc_size(dd_config);
+               i += mic_total_desc_size(devp)) {
+               devp = mdev->dp + i;
+               if (devp->type == 0 || devp->type == -1) {
+                       slot_found = true;
+                       break;
+               }
+       }
+       if (!slot_found) {
+               ret =  -EINVAL;
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, ret);
+               goto exit;
+       }
+       /*
+        * Save off the type before doing the memcpy. Type will be set in the
+        * end after completing all initialization for the new device.
+        */
+       *type = dd_config->type;
+       dd_config->type = 0;
+       memcpy(devp, dd_config, mic_desc_size(dd_config));
+
+       *devpage = devp;
+exit:
+       kfree(dd_config);
+       return ret;
+}
+
+static void mic_init_device_ctrl(struct mic_vdev *mvdev,
+                               struct mic_device_desc *devpage)
+{
+       struct mic_device_ctrl *dc;
+
+       dc = (void *)devpage + mic_aligned_desc_size(devpage);
+
+       dc->config_change = 0;
+       dc->guest_ack = 0;
+       dc->vdev_reset = 0;
+       dc->host_ack = 0;
+       dc->used_address_updated = 0;
+       dc->c2h_vdev_db = -1;
+       dc->h2c_vdev_db = -1;
+       mvdev->dc = dc;
+}
+
+int mic_virtio_add_device(struct mic_vdev *mvdev,
+                       void __user *argp)
+{
+       struct mic_device *mdev = mvdev->mdev;
+       struct mic_device_desc *dd;
+       struct mic_vqconfig *vqconfig;
+       int vr_size, i, j, ret;
+       u8 type;
+       s8 db;
+       char irqname[10];
+       struct mic_bootparam *bootparam = mdev->dp;
+       u16 num;
+
+       mutex_lock(&mdev->mic_mutex);
+
+       ret = mic_copy_dp_entry(mvdev, argp, &type, &dd);
+       if (ret) {
+               mutex_unlock(&mdev->mic_mutex);
+               return ret;
+       }
+
+       mic_init_device_ctrl(mvdev, dd);
+
+       mvdev->dd = dd;
+       mvdev->virtio_id = type;
+       vqconfig = mic_vq_config(dd);
+       INIT_WORK(&mvdev->virtio_bh_work, mic_bh_handler);
+
+       for (i = 0; i < dd->num_vq; i++) {
+               struct mic_vringh *mvr = &mvdev->mvr[i];
+               struct mic_vring *vr = &mvdev->mvr[i].vring;
+               num = le16_to_cpu(vqconfig[i].num);
+               mutex_init(&mvr->vr_mutex);
+               vr_size = PAGE_ALIGN(vring_size(num, MIC_VIRTIO_RING_ALIGN) +
+                       sizeof(struct _mic_vring_info));
+               vr->va = (void *)
+                       __get_free_pages(GFP_KERNEL | __GFP_ZERO,
+                                        get_order(vr_size));
+               if (!vr->va) {
+                       ret = -ENOMEM;
+                       dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                               __func__, __LINE__, ret);
+                       goto err;
+               }
+               vr->len = vr_size;
+               vr->info = vr->va + vring_size(num, MIC_VIRTIO_RING_ALIGN);
+               vr->info->magic = MIC_MAGIC + mvdev->virtio_id + i;
+               vqconfig[i].address = mic_map_single(mdev,
+                       vr->va, vr_size);
+               if (mic_map_error(vqconfig[i].address)) {
+                       free_pages((unsigned long)vr->va, get_order(vr_size));
+                       ret = -ENOMEM;
+                       dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                               __func__, __LINE__, ret);
+                       goto err;
+               }
+               vqconfig[i].address = cpu_to_le64(vqconfig[i].address);
+
+               vring_init(&vr->vr, num, vr->va, MIC_VIRTIO_RING_ALIGN);
+               ret = vringh_init_kern(&mvr->vrh,
+                       *(u32 *)mic_vq_features(mvdev->dd), num, false,
+                       vr->vr.desc, vr->vr.avail, vr->vr.used);
+               if (ret) {
+                       dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                               __func__, __LINE__, ret);
+                       goto err;
+               }
+               vringh_kiov_init(&mvr->riov, NULL, 0);
+               vringh_kiov_init(&mvr->wiov, NULL, 0);
+               mvr->head = USHRT_MAX;
+               mvr->mvdev = mvdev;
+               mvr->vrh.notify = mic_notify;
+               dev_dbg(mdev->sdev->parent,
+                       "%s %d index %d va %p info %p vr_size 0x%x\n",
+                       __func__, __LINE__, i, vr->va, vr->info, vr_size);
+       }
+
+       snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id,
+                mvdev->virtio_id);
+       mvdev->virtio_db = mic_next_db(mdev);
+       mvdev->virtio_cookie = mic_request_irq(mdev, mic_virtio_intr_handler,
+                       irqname, mvdev, mvdev->virtio_db, MIC_INTR_DB);
+       if (IS_ERR(mvdev->virtio_cookie)) {
+               ret = PTR_ERR(mvdev->virtio_cookie);
+               dev_dbg(mdev->sdev->parent, "request irq failed\n");
+               goto err;
+       }
+
+       mvdev->dc->c2h_vdev_db = mvdev->virtio_db;
+
+       list_add_tail(&mvdev->list, &mdev->vdev_list);
+       /*
+        * Order the type update with previous stores. This write barrier
+        * is paired with the corresponding read barrier before the uncached
+        * system memory read of the type, on the card while scanning the
+        * device page.
+        */
+       smp_wmb();
+       dd->type = type;
+
+       dev_dbg(mdev->sdev->parent, "Added virtio device id %d\n", dd->type);
+
+       db = bootparam->h2c_config_db;
+       if (db != -1)
+               mdev->ops->send_intr(mdev, db);
+       mutex_unlock(&mdev->mic_mutex);
+       return 0;
+err:
+       vqconfig = mic_vq_config(dd);
+       for (j = 0; j < i; j++) {
+               struct mic_vringh *mvr = &mvdev->mvr[j];
+               mic_unmap_single(mdev, le64_to_cpu(vqconfig[j].address),
+                                mvr->vring.len);
+               free_pages((unsigned long)mvr->vring.va,
+                          get_order(mvr->vring.len));
+       }
+       mutex_unlock(&mdev->mic_mutex);
+       return ret;
+}
+
+void mic_virtio_del_device(struct mic_vdev *mvdev)
+{
+       struct list_head *pos, *tmp;
+       struct mic_vdev *tmp_mvdev;
+       struct mic_device *mdev = mvdev->mdev;
+       DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake);
+       int i, ret, retry = 100;
+       struct mic_vqconfig *vqconfig;
+       struct mic_bootparam *bootparam = mdev->dp;
+       s8 db;
+
+       mutex_lock(&mdev->mic_mutex);
+       db = bootparam->h2c_config_db;
+       if (db == -1)
+               goto skip_hot_remove;
+       dev_dbg(mdev->sdev->parent,
+               "Requesting hot remove id %d\n", mvdev->virtio_id);
+       mvdev->dc->config_change = MIC_VIRTIO_PARAM_DEV_REMOVE;
+       mdev->ops->send_intr(mdev, db);
+       for (i = retry; i--;) {
+               ret = wait_event_timeout(wake,
+                       mvdev->dc->guest_ack, msecs_to_jiffies(100));
+               if (ret)
+                       break;
+       }
+       dev_dbg(mdev->sdev->parent,
+               "Device id %d config_change %d guest_ack %d\n",
+               mvdev->virtio_id, mvdev->dc->config_change,
+               mvdev->dc->guest_ack);
+       mvdev->dc->config_change = 0;
+       mvdev->dc->guest_ack = 0;
+skip_hot_remove:
+       mic_free_irq(mdev, mvdev->virtio_cookie, mvdev);
+       flush_work(&mvdev->virtio_bh_work);
+       vqconfig = mic_vq_config(mvdev->dd);
+       for (i = 0; i < mvdev->dd->num_vq; i++) {
+               struct mic_vringh *mvr = &mvdev->mvr[i];
+               vringh_kiov_cleanup(&mvr->riov);
+               vringh_kiov_cleanup(&mvr->wiov);
+               mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address),
+                                mvr->vring.len);
+               free_pages((unsigned long)mvr->vring.va,
+                          get_order(mvr->vring.len));
+       }
+
+       list_for_each_safe(pos, tmp, &mdev->vdev_list) {
+               tmp_mvdev = list_entry(pos, struct mic_vdev, list);
+               if (tmp_mvdev == mvdev) {
+                       list_del(pos);
+                       dev_dbg(mdev->sdev->parent,
+                               "Removing virtio device id %d\n",
+                               mvdev->virtio_id);
+                       break;
+               }
+       }
+       /*
+        * Order the type update with previous stores. This write barrier
+        * is paired with the corresponding read barrier before the uncached
+        * system memory read of the type, on the card while scanning the
+        * device page.
+        */
+       smp_wmb();
+       mvdev->dd->type = -1;
+       mutex_unlock(&mdev->mic_mutex);
+}
diff --git a/drivers/misc/mic/host/mic_virtio.h b/drivers/misc/mic/host/mic_virtio.h
new file mode 100644 (file)
index 0000000..184f3c8
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#ifndef MIC_VIRTIO_H
+#define MIC_VIRTIO_H
+
+#include <linux/virtio_config.h>
+#include <linux/mic_ioctl.h>
+
+/*
+ * Note on endianness.
+ * 1. Host can be both BE or LE
+ * 2. Guest/card is LE. Host uses le_to_cpu to access desc/avail
+ *    rings and ioreadXX/iowriteXX to access used ring.
+ * 3. Device page exposed by host to guest contains LE values. Guest
+ *    accesses these using ioreadXX/iowriteXX etc. This way in general we
+ *    obey the virtio spec according to which guest works with native
+ *    endianness and host is aware of guest endianness and does all
+ *    required endianness conversion.
+ * 4. Data provided from user space to guest (in ADD_DEVICE and
+ *    CONFIG_CHANGE ioctl's) is not interpreted by the driver and should be
+ *    in guest endianness.
+ */
+
+/**
+ * struct mic_vringh - Virtio ring host information.
+ *
+ * @vring: The MIC vring used for setting up user space mappings.
+ * @vrh: The host VRINGH used for accessing the card vrings.
+ * @riov: The VRINGH read kernel IOV.
+ * @wiov: The VRINGH write kernel IOV.
+ * @head: The VRINGH head index address passed to vringh_getdesc_kern(..).
+ * @vr_mutex: Mutex for synchronizing access to the VRING.
+ * @mvdev: Back pointer to MIC virtio device for vringh_notify(..).
+ */
+struct mic_vringh {
+       struct mic_vring vring;
+       struct vringh vrh;
+       struct vringh_kiov riov;
+       struct vringh_kiov wiov;
+       u16 head;
+       struct mutex vr_mutex;
+       struct mic_vdev *mvdev;
+};
+
+/**
+ * struct mic_vdev - Host information for a card Virtio device.
+ *
+ * @virtio_id - Virtio device id.
+ * @waitq - Waitqueue to allow ring3 apps to poll.
+ * @mdev - Back pointer to host MIC device.
+ * @poll_wake - Used for waking up threads blocked in poll.
+ * @out_bytes - Debug stats for number of bytes copied from host to card.
+ * @in_bytes - Debug stats for number of bytes copied from card to host.
+ * @mvr - Store per VRING data structures.
+ * @virtio_bh_work - Work struct used to schedule virtio bottom half handling.
+ * @dd - Virtio device descriptor.
+ * @dc - Virtio device control fields.
+ * @list - List of Virtio devices.
+ * @virtio_db - The doorbell used by the card to interrupt the host.
+ * @virtio_cookie - The cookie returned while requesting interrupts.
+ */
+struct mic_vdev {
+       int virtio_id;
+       wait_queue_head_t waitq;
+       struct mic_device *mdev;
+       int poll_wake;
+       unsigned long out_bytes;
+       unsigned long in_bytes;
+       struct mic_vringh mvr[MIC_MAX_VRINGS];
+       struct work_struct virtio_bh_work;
+       struct mic_device_desc *dd;
+       struct mic_device_ctrl *dc;
+       struct list_head list;
+       int virtio_db;
+       struct mic_irq *virtio_cookie;
+};
+
+void mic_virtio_uninit(struct mic_device *mdev);
+int mic_virtio_add_device(struct mic_vdev *mvdev,
+                       void __user *argp);
+void mic_virtio_del_device(struct mic_vdev *mvdev);
+int mic_virtio_config_change(struct mic_vdev *mvdev,
+                       void __user *argp);
+int mic_virtio_copy_desc(struct mic_vdev *mvdev,
+       struct mic_copy_desc *request);
+void mic_virtio_reset_devices(struct mic_device *mdev);
+void mic_bh_handler(struct work_struct *work);
+
+/* Helper API to obtain the MIC PCIe device */
+static inline struct device *mic_dev(struct mic_vdev *mvdev)
+{
+       return mvdev->mdev->sdev->parent;
+}
+
+/* Helper API to check if a virtio device is initialized */
+static inline int mic_vdev_inited(struct mic_vdev *mvdev)
+{
+       /* Device has not been created yet */
+       if (!mvdev->dd || !mvdev->dd->type) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, -EINVAL);
+               return -EINVAL;
+       }
+
+       /* Device has been removed/deleted */
+       if (mvdev->dd->type == -1) {
+               dev_err(mic_dev(mvdev), "%s %d err %d\n",
+                       __func__, __LINE__, -ENODEV);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+/* Helper API to check if a virtio device is running */
+static inline bool mic_vdevup(struct mic_vdev *mvdev)
+{
+       return !!mvdev->dd->status;
+}
+#endif
diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c
new file mode 100644 (file)
index 0000000..81e9541
--- /dev/null
@@ -0,0 +1,570 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+
+#include "../common/mic_dev.h"
+#include "mic_device.h"
+#include "mic_x100.h"
+#include "mic_smpt.h"
+
+/**
+ * mic_x100_write_spad - write to the scratchpad register
+ * @mdev: pointer to mic_device instance
+ * @idx: index to the scratchpad register, 0 based
+ * @val: the data value to put into the register
+ *
+ * This function allows writing of a 32bit value to the indexed scratchpad
+ * register.
+ *
+ * RETURNS: none.
+ */
+static void
+mic_x100_write_spad(struct mic_device *mdev, unsigned int idx, u32 val)
+{
+       dev_dbg(mdev->sdev->parent, "Writing 0x%x to scratch pad index %d\n",
+               val, idx);
+       mic_mmio_write(&mdev->mmio, val,
+                      MIC_X100_SBOX_BASE_ADDRESS +
+                      MIC_X100_SBOX_SPAD0 + idx * 4);
+}
+
+/**
+ * mic_x100_read_spad - read from the scratchpad register
+ * @mdev: pointer to mic_device instance
+ * @idx: index to scratchpad register, 0 based
+ *
+ * This function allows reading of the 32bit scratchpad register.
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+static u32
+mic_x100_read_spad(struct mic_device *mdev, unsigned int idx)
+{
+       u32 val = mic_mmio_read(&mdev->mmio,
+               MIC_X100_SBOX_BASE_ADDRESS +
+               MIC_X100_SBOX_SPAD0 + idx * 4);
+
+       dev_dbg(mdev->sdev->parent,
+               "Reading 0x%x from scratch pad index %d\n", val, idx);
+       return val;
+}
+
+/**
+ * mic_x100_enable_interrupts - Enable interrupts.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_enable_interrupts(struct mic_device *mdev)
+{
+       u32 reg;
+       struct mic_mw *mw = &mdev->mmio;
+       u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0;
+       u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0;
+
+       reg = mic_mmio_read(mw, sice0);
+       reg |= MIC_X100_SBOX_DBR_BITS(0xf) | MIC_X100_SBOX_DMA_BITS(0xff);
+       mic_mmio_write(mw, reg, sice0);
+
+       /*
+        * Enable auto-clear when enabling interrupts. Applicable only for
+        * MSI-x. Legacy and MSI mode cannot have auto-clear enabled.
+        */
+       if (mdev->irq_info.num_vectors > 1) {
+               reg = mic_mmio_read(mw, siac0);
+               reg |= MIC_X100_SBOX_DBR_BITS(0xf) |
+                       MIC_X100_SBOX_DMA_BITS(0xff);
+               mic_mmio_write(mw, reg, siac0);
+       }
+}
+
+/**
+ * mic_x100_disable_interrupts - Disable interrupts.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_disable_interrupts(struct mic_device *mdev)
+{
+       u32 reg;
+       struct mic_mw *mw = &mdev->mmio;
+       u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0;
+       u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0;
+       u32 sicc0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICC0;
+
+       reg = mic_mmio_read(mw, sice0);
+       mic_mmio_write(mw, reg, sicc0);
+
+       if (mdev->irq_info.num_vectors > 1) {
+               reg = mic_mmio_read(mw, siac0);
+               reg &= ~(MIC_X100_SBOX_DBR_BITS(0xf) |
+                       MIC_X100_SBOX_DMA_BITS(0xff));
+               mic_mmio_write(mw, reg, siac0);
+       }
+}
+
+/**
+ * mic_x100_send_sbox_intr - Send an MIC_X100_SBOX interrupt to MIC.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_send_sbox_intr(struct mic_device *mdev,
+                       int doorbell)
+{
+       struct mic_mw *mw = &mdev->mmio;
+       u64 apic_icr_offset = MIC_X100_SBOX_APICICR0 + doorbell * 8;
+       u32 apicicr_low = mic_mmio_read(mw, MIC_X100_SBOX_BASE_ADDRESS +
+                                       apic_icr_offset);
+
+       /* for MIC we need to make sure we "hit" the send_icr bit (13) */
+       apicicr_low = (apicicr_low | (1 << 13));
+
+       /* Ensure that the interrupt is ordered w.r.t. previous stores. */
+       wmb();
+       mic_mmio_write(mw, apicicr_low,
+                      MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset);
+}
+
+/**
+ * mic_x100_send_rdmasr_intr - Send an RDMASR interrupt to MIC.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_send_rdmasr_intr(struct mic_device *mdev,
+                       int doorbell)
+{
+       int rdmasr_offset = MIC_X100_SBOX_RDMASR0 + (doorbell << 2);
+       /* Ensure that the interrupt is ordered w.r.t. previous stores. */
+       wmb();
+       mic_mmio_write(&mdev->mmio, 0,
+                      MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset);
+}
+
+/**
+ * __mic_x100_send_intr - Send interrupt to MIC.
+ * @mdev: pointer to mic_device instance
+ * @doorbell: doorbell number.
+ */
+static void mic_x100_send_intr(struct mic_device *mdev, int doorbell)
+{
+       int rdmasr_db;
+       if (doorbell < MIC_X100_NUM_SBOX_IRQ) {
+               mic_x100_send_sbox_intr(mdev, doorbell);
+       } else {
+               rdmasr_db = doorbell - MIC_X100_NUM_SBOX_IRQ +
+                       MIC_X100_RDMASR_IRQ_BASE;
+               mic_x100_send_rdmasr_intr(mdev, rdmasr_db);
+       }
+}
+
+/**
+ * mic_ack_interrupt - Device specific interrupt handling.
+ * @mdev: pointer to mic_device instance
+ *
+ * Returns: bitmask of doorbell events triggered.
+ */
+static u32 mic_x100_ack_interrupt(struct mic_device *mdev)
+{
+       u32 reg = 0;
+       struct mic_mw *mw = &mdev->mmio;
+       u32 sicr0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICR0;
+
+       /* Clear pending bit array. */
+       if (MIC_A0_STEP == mdev->stepping)
+               mic_mmio_write(mw, 1, MIC_X100_SBOX_BASE_ADDRESS +
+                       MIC_X100_SBOX_MSIXPBACR);
+
+       if (mdev->irq_info.num_vectors <= 1) {
+               reg = mic_mmio_read(mw, sicr0);
+
+               if (unlikely(!reg))
+                       goto done;
+
+               mic_mmio_write(mw, reg, sicr0);
+       }
+
+       if (mdev->stepping >= MIC_B0_STEP)
+               mdev->intr_ops->enable_interrupts(mdev);
+done:
+       return reg;
+}
+
+/**
+ * mic_x100_hw_intr_init - Initialize h/w specific interrupt
+ * information.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_hw_intr_init(struct mic_device *mdev)
+{
+       mdev->intr_info = (struct mic_intr_info *)mic_x100_intr_init;
+}
+
+/**
+ * mic_x100_read_msi_to_src_map - read from the MSI mapping registers
+ * @mdev: pointer to mic_device instance
+ * @idx: index to the mapping register, 0 based
+ *
+ * This function allows reading of the 32bit MSI mapping register.
+ *
+ * RETURNS: The value in the register.
+ */
+static u32
+mic_x100_read_msi_to_src_map(struct mic_device *mdev, int idx)
+{
+       return mic_mmio_read(&mdev->mmio,
+               MIC_X100_SBOX_BASE_ADDRESS +
+               MIC_X100_SBOX_MXAR0 + idx * 4);
+}
+
+/**
+ * mic_x100_program_msi_to_src_map - program the MSI mapping registers
+ * @mdev: pointer to mic_device instance
+ * @idx: index to the mapping register, 0 based
+ * @offset: The bit offset in the register that needs to be updated.
+ * @set: boolean specifying if the bit in the specified offset needs
+ * to be set or cleared.
+ *
+ * RETURNS: None.
+ */
+static void
+mic_x100_program_msi_to_src_map(struct mic_device *mdev,
+                               int idx, int offset, bool set)
+{
+       unsigned long reg;
+       struct mic_mw *mw = &mdev->mmio;
+       u32 mxar = MIC_X100_SBOX_BASE_ADDRESS +
+               MIC_X100_SBOX_MXAR0 + idx * 4;
+
+       reg = mic_mmio_read(mw, mxar);
+       if (set)
+               __set_bit(offset, &reg);
+       else
+               __clear_bit(offset, &reg);
+       mic_mmio_write(mw, reg, mxar);
+}
+
+/*
+ * mic_x100_reset_fw_ready - Reset Firmware ready status field.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_reset_fw_ready(struct mic_device *mdev)
+{
+       mdev->ops->write_spad(mdev, MIC_X100_DOWNLOAD_INFO, 0);
+}
+
+/*
+ * mic_x100_is_fw_ready - Check if firmware is ready.
+ * @mdev: pointer to mic_device instance
+ */
+static bool mic_x100_is_fw_ready(struct mic_device *mdev)
+{
+       u32 scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO);
+       return MIC_X100_SPAD2_DOWNLOAD_STATUS(scratch2) ? true : false;
+}
+
+/**
+ * mic_x100_get_apic_id - Get bootstrap APIC ID.
+ * @mdev: pointer to mic_device instance
+ */
+static u32 mic_x100_get_apic_id(struct mic_device *mdev)
+{
+       u32 scratch2 = 0;
+
+       scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO);
+       return MIC_X100_SPAD2_APIC_ID(scratch2);
+}
+
+/**
+ * mic_x100_send_firmware_intr - Send an interrupt to the firmware on MIC.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_send_firmware_intr(struct mic_device *mdev)
+{
+       u32 apicicr_low;
+       u64 apic_icr_offset = MIC_X100_SBOX_APICICR7;
+       int vector = MIC_X100_BSP_INTERRUPT_VECTOR;
+       struct mic_mw *mw = &mdev->mmio;
+
+       /*
+        * For MIC we need to make sure we "hit"
+        * the send_icr bit (13).
+        */
+       apicicr_low = (vector | (1 << 13));
+
+       mic_mmio_write(mw, mic_x100_get_apic_id(mdev),
+                      MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset + 4);
+
+       /* Ensure that the interrupt is ordered w.r.t. previous stores. */
+       wmb();
+       mic_mmio_write(mw, apicicr_low,
+                      MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset);
+}
+
+/**
+ * mic_x100_hw_reset - Reset the MIC device.
+ * @mdev: pointer to mic_device instance
+ */
+static void mic_x100_hw_reset(struct mic_device *mdev)
+{
+       u32 reset_reg;
+       u32 rgcr = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_RGCR;
+       struct mic_mw *mw = &mdev->mmio;
+
+       /* Ensure that the reset is ordered w.r.t. previous loads and stores */
+       mb();
+       /* Trigger reset */
+       reset_reg = mic_mmio_read(mw, rgcr);
+       reset_reg |= 0x1;
+       mic_mmio_write(mw, reset_reg, rgcr);
+       /*
+        * It seems we really want to delay at least 1 second
+        * after touching reset to prevent a lot of problems.
+        */
+       msleep(1000);
+}
+
+/**
+ * mic_x100_load_command_line - Load command line to MIC.
+ * @mdev: pointer to mic_device instance
+ * @fw: the firmware image
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+static int
+mic_x100_load_command_line(struct mic_device *mdev, const struct firmware *fw)
+{
+       u32 len = 0;
+       u32 boot_mem;
+       char *buf;
+       void __iomem *cmd_line_va = mdev->aper.va + mdev->bootaddr + fw->size;
+#define CMDLINE_SIZE 2048
+
+       boot_mem = mdev->aper.len >> 20;
+       buf = kzalloc(CMDLINE_SIZE, GFP_KERNEL);
+       if (!buf) {
+               dev_err(mdev->sdev->parent,
+                       "%s %d allocation failed\n", __func__, __LINE__);
+               return -ENOMEM;
+       }
+       len += snprintf(buf, CMDLINE_SIZE - len,
+               " mem=%dM", boot_mem);
+       if (mdev->cmdline)
+               snprintf(buf + len, CMDLINE_SIZE - len, " %s", mdev->cmdline);
+       memcpy_toio(cmd_line_va, buf, strlen(buf) + 1);
+       kfree(buf);
+       return 0;
+}
+
+/**
+ * mic_x100_load_ramdisk - Load ramdisk to MIC.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+static int
+mic_x100_load_ramdisk(struct mic_device *mdev)
+{
+       const struct firmware *fw;
+       int rc;
+       struct boot_params __iomem *bp = mdev->aper.va + mdev->bootaddr;
+
+       rc = request_firmware(&fw,
+                       mdev->ramdisk, mdev->sdev->parent);
+       if (rc < 0) {
+               dev_err(mdev->sdev->parent,
+                       "ramdisk request_firmware failed: %d %s\n",
+                       rc, mdev->ramdisk);
+               goto error;
+       }
+       /*
+        * Typically the bootaddr for card OS is 64M
+        * so copy over the ramdisk @ 128M.
+        */
+       memcpy_toio(mdev->aper.va + (mdev->bootaddr << 1), fw->data, fw->size);
+       iowrite32(cpu_to_le32(mdev->bootaddr << 1), &bp->hdr.ramdisk_image);
+       iowrite32(cpu_to_le32(fw->size), &bp->hdr.ramdisk_size);
+       release_firmware(fw);
+error:
+       return rc;
+}
+
+/**
+ * mic_x100_get_boot_addr - Get MIC boot address.
+ * @mdev: pointer to mic_device instance
+ *
+ * This function is called during firmware load to determine
+ * the address at which the OS should be downloaded in card
+ * memory i.e. GDDR.
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+static int
+mic_x100_get_boot_addr(struct mic_device *mdev)
+{
+       u32 scratch2, boot_addr;
+       int rc = 0;
+
+       scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO);
+       boot_addr = MIC_X100_SPAD2_DOWNLOAD_ADDR(scratch2);
+       dev_dbg(mdev->sdev->parent, "%s %d boot_addr 0x%x\n",
+               __func__, __LINE__, boot_addr);
+       if (boot_addr > (1 << 31)) {
+               dev_err(mdev->sdev->parent,
+                       "incorrect bootaddr 0x%x\n",
+                       boot_addr);
+               rc = -EINVAL;
+               goto error;
+       }
+       mdev->bootaddr = boot_addr;
+error:
+       return rc;
+}
+
+/**
+ * mic_x100_load_firmware - Load firmware to MIC.
+ * @mdev: pointer to mic_device instance
+ * @buf: buffer containing boot string including firmware/ramdisk path.
+ *
+ * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
+ */
+static int
+mic_x100_load_firmware(struct mic_device *mdev, const char *buf)
+{
+       int rc;
+       const struct firmware *fw;
+
+       rc = mic_x100_get_boot_addr(mdev);
+       if (rc)
+               goto error;
+       /* load OS */
+       rc = request_firmware(&fw, mdev->firmware, mdev->sdev->parent);
+       if (rc < 0) {
+               dev_err(mdev->sdev->parent,
+                       "ramdisk request_firmware failed: %d %s\n",
+                       rc, mdev->firmware);
+               goto error;
+       }
+       if (mdev->bootaddr > mdev->aper.len - fw->size) {
+               rc = -EINVAL;
+               dev_err(mdev->sdev->parent, "%s %d rc %d bootaddr 0x%x\n",
+                       __func__, __LINE__, rc, mdev->bootaddr);
+               release_firmware(fw);
+               goto error;
+       }
+       memcpy_toio(mdev->aper.va + mdev->bootaddr, fw->data, fw->size);
+       mdev->ops->write_spad(mdev, MIC_X100_FW_SIZE, fw->size);
+       if (!strcmp(mdev->bootmode, "elf"))
+               goto done;
+       /* load command line */
+       rc = mic_x100_load_command_line(mdev, fw);
+       if (rc) {
+               dev_err(mdev->sdev->parent, "%s %d rc %d\n",
+                       __func__, __LINE__, rc);
+               goto error;
+       }
+       release_firmware(fw);
+       /* load ramdisk */
+       if (mdev->ramdisk)
+               rc = mic_x100_load_ramdisk(mdev);
+error:
+       dev_dbg(mdev->sdev->parent, "%s %d rc %d\n", __func__, __LINE__, rc);
+done:
+       return rc;
+}
+
+/**
+ * mic_x100_get_postcode - Get postcode status from firmware.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: postcode.
+ */
+static u32 mic_x100_get_postcode(struct mic_device *mdev)
+{
+       return mic_mmio_read(&mdev->mmio, MIC_X100_POSTCODE);
+}
+
+/**
+ * mic_x100_smpt_set - Update an SMPT entry with a DMA address.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: none.
+ */
+static void
+mic_x100_smpt_set(struct mic_device *mdev, dma_addr_t dma_addr, u8 index)
+{
+#define SNOOP_ON       (0 << 0)
+#define SNOOP_OFF      (1 << 0)
+/*
+ * Sbox Smpt Reg Bits:
+ * Bits        31:2    Host address
+ * Bits        1       RSVD
+ * Bits        0       No snoop
+ */
+#define BUILD_SMPT(NO_SNOOP, HOST_ADDR)  \
+       (u32)(((HOST_ADDR) << 2) | ((NO_SNOOP) & 0x01))
+
+       uint32_t smpt_reg_val = BUILD_SMPT(SNOOP_ON,
+                       dma_addr >> mdev->smpt->info.page_shift);
+       mic_mmio_write(&mdev->mmio, smpt_reg_val,
+                      MIC_X100_SBOX_BASE_ADDRESS +
+                      MIC_X100_SBOX_SMPT00 + (4 * index));
+}
+
+/**
+ * mic_x100_smpt_hw_init - Initialize SMPT X100 specific fields.
+ * @mdev: pointer to mic_device instance
+ *
+ * RETURNS: none.
+ */
+static void mic_x100_smpt_hw_init(struct mic_device *mdev)
+{
+       struct mic_smpt_hw_info *info = &mdev->smpt->info;
+
+       info->num_reg = 32;
+       info->page_shift = 34;
+       info->page_size = (1ULL << info->page_shift);
+       info->base = 0x8000000000ULL;
+}
+
+struct mic_smpt_ops mic_x100_smpt_ops = {
+       .init = mic_x100_smpt_hw_init,
+       .set = mic_x100_smpt_set,
+};
+
+struct mic_hw_ops mic_x100_ops = {
+       .aper_bar = MIC_X100_APER_BAR,
+       .mmio_bar = MIC_X100_MMIO_BAR,
+       .read_spad = mic_x100_read_spad,
+       .write_spad = mic_x100_write_spad,
+       .send_intr = mic_x100_send_intr,
+       .ack_interrupt = mic_x100_ack_interrupt,
+       .reset = mic_x100_hw_reset,
+       .reset_fw_ready = mic_x100_reset_fw_ready,
+       .is_fw_ready = mic_x100_is_fw_ready,
+       .send_firmware_intr = mic_x100_send_firmware_intr,
+       .load_mic_fw = mic_x100_load_firmware,
+       .get_postcode = mic_x100_get_postcode,
+};
+
+struct mic_hw_intr_ops mic_x100_intr_ops = {
+       .intr_init = mic_x100_hw_intr_init,
+       .enable_interrupts = mic_x100_enable_interrupts,
+       .disable_interrupts = mic_x100_disable_interrupts,
+       .program_msi_to_src_map = mic_x100_program_msi_to_src_map,
+       .read_msi_to_src_map = mic_x100_read_msi_to_src_map,
+};
diff --git a/drivers/misc/mic/host/mic_x100.h b/drivers/misc/mic/host/mic_x100.h
new file mode 100644 (file)
index 0000000..8b7daa1
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#ifndef _MIC_X100_HW_H_
+#define _MIC_X100_HW_H_
+
+#define MIC_X100_PCI_DEVICE_2250 0x2250
+#define MIC_X100_PCI_DEVICE_2251 0x2251
+#define MIC_X100_PCI_DEVICE_2252 0x2252
+#define MIC_X100_PCI_DEVICE_2253 0x2253
+#define MIC_X100_PCI_DEVICE_2254 0x2254
+#define MIC_X100_PCI_DEVICE_2255 0x2255
+#define MIC_X100_PCI_DEVICE_2256 0x2256
+#define MIC_X100_PCI_DEVICE_2257 0x2257
+#define MIC_X100_PCI_DEVICE_2258 0x2258
+#define MIC_X100_PCI_DEVICE_2259 0x2259
+#define MIC_X100_PCI_DEVICE_225a 0x225a
+#define MIC_X100_PCI_DEVICE_225b 0x225b
+#define MIC_X100_PCI_DEVICE_225c 0x225c
+#define MIC_X100_PCI_DEVICE_225d 0x225d
+#define MIC_X100_PCI_DEVICE_225e 0x225e
+
+#define MIC_X100_APER_BAR 0
+#define MIC_X100_MMIO_BAR 4
+
+#define MIC_X100_SBOX_BASE_ADDRESS 0x00010000
+#define MIC_X100_SBOX_SPAD0 0x0000AB20
+#define MIC_X100_SBOX_SICR0_DBR(x) ((x) & 0xf)
+#define MIC_X100_SBOX_SICR0_DMA(x) (((x) >> 8) & 0xff)
+#define MIC_X100_SBOX_SICE0_DBR(x) ((x) & 0xf)
+#define MIC_X100_SBOX_DBR_BITS(x) ((x) & 0xf)
+#define MIC_X100_SBOX_SICE0_DMA(x) (((x) >> 8) & 0xff)
+#define MIC_X100_SBOX_DMA_BITS(x) (((x) & 0xff) << 8)
+
+#define MIC_X100_SBOX_APICICR0 0x0000A9D0
+#define MIC_X100_SBOX_SICR0 0x00009004
+#define MIC_X100_SBOX_SICE0 0x0000900C
+#define MIC_X100_SBOX_SICC0 0x00009010
+#define MIC_X100_SBOX_SIAC0 0x00009014
+#define MIC_X100_SBOX_MSIXPBACR 0x00009084
+#define MIC_X100_SBOX_MXAR0 0x00009044
+#define MIC_X100_SBOX_SMPT00 0x00003100
+#define MIC_X100_SBOX_RDMASR0 0x0000B180
+
+#define MIC_X100_DOORBELL_IDX_START 0
+#define MIC_X100_NUM_DOORBELL 4
+#define MIC_X100_DMA_IDX_START 8
+#define MIC_X100_NUM_DMA 8
+#define MIC_X100_ERR_IDX_START 30
+#define MIC_X100_NUM_ERR 1
+
+#define MIC_X100_NUM_SBOX_IRQ 8
+#define MIC_X100_NUM_RDMASR_IRQ 8
+#define MIC_X100_RDMASR_IRQ_BASE 17
+#define MIC_X100_SPAD2_DOWNLOAD_STATUS(x) ((x) & 0x1)
+#define MIC_X100_SPAD2_APIC_ID(x)      (((x) >> 1) & 0x1ff)
+#define MIC_X100_SPAD2_DOWNLOAD_ADDR(x) ((x) & 0xfffff000)
+#define MIC_X100_SBOX_APICICR7 0x0000AA08
+#define MIC_X100_SBOX_RGCR 0x00004010
+#define MIC_X100_SBOX_SDBIC0 0x0000CC90
+#define MIC_X100_DOWNLOAD_INFO 2
+#define MIC_X100_FW_SIZE 5
+#define MIC_X100_POSTCODE 0x242c
+
+static const u16 mic_x100_intr_init[] = {
+               MIC_X100_DOORBELL_IDX_START,
+               MIC_X100_DMA_IDX_START,
+               MIC_X100_ERR_IDX_START,
+               MIC_X100_NUM_DOORBELL,
+               MIC_X100_NUM_DMA,
+               MIC_X100_NUM_ERR,
+};
+
+/* Host->Card(bootstrap) Interrupt Vector */
+#define MIC_X100_BSP_INTERRUPT_VECTOR 229
+
+extern struct mic_hw_ops mic_x100_ops;
+extern struct mic_smpt_ops mic_x100_smpt_ops;
+extern struct mic_hw_intr_ops mic_x100_intr_ops;
+
+#endif
index 68b7c773d2cf472e23395ec563a7ed3abfbb9c9c..30754927fd80d584881b3ce5475ddef874bb353c 100644 (file)
@@ -395,7 +395,7 @@ static int phantom_probe(struct pci_dev *pdev,
        iowrite32(0, pht->caddr + PHN_IRQCTL);
        ioread32(pht->caddr + PHN_IRQCTL); /* PCI posting */
        retval = request_irq(pdev->irq, phantom_isr,
-                       IRQF_SHARED | IRQF_DISABLED, "phantom", pht);
+                       IRQF_SHARED, "phantom", pht);
        if (retval) {
                dev_err(&pdev->dev, "can't establish ISR\n");
                goto err_unmo;
index f84ff0c060358d2d4e2fa6e78d070fd385f0eaa4..eda38cbe85307edd67863122e24451d269d66866 100644 (file)
@@ -892,7 +892,6 @@ static void pti_pci_remove(struct pci_dev *pdev)
        }
 
        iounmap(drv_data->pti_ioaddr);
-       pci_set_drvdata(pdev, NULL);
        kfree(drv_data);
        pci_release_region(pdev, 1);
        pci_disable_device(pdev);
index 9b237221bc4e8abf19391886ee6465065db2e16a..83da711ce9f13688660192f55fac62571961ec93 100644 (file)
@@ -22,9 +22,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/spi/spi.h>
-
-#define DAC7512_DRV_NAME       "dac7512"
-#define DRIVER_VERSION         "1.0"
+#include <linux/of.h>
 
 static ssize_t dac7512_store_val(struct device *dev,
                                 struct device_attribute *attr,
@@ -75,13 +73,29 @@ static int dac7512_remove(struct spi_device *spi)
        return 0;
 }
 
+static const struct spi_device_id dac7512_id_table[] = {
+       { "dac7512", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(spi, dac7512_id_table);
+
+#ifdef CONFIG_OF
+static const struct of_device_id dac7512_of_match[] = {
+       { .compatible = "ti,dac7512", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, dac7512_of_match);
+#endif
+
 static struct spi_driver dac7512_driver = {
        .driver = {
-               .name   = DAC7512_DRV_NAME,
+               .name   = "dac7512",
                .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(dac7512_of_match),
        },
        .probe  = dac7512_probe,
        .remove = dac7512_remove,
+       .id_table = dac7512_id_table,
 };
 
 module_spi_driver(dac7512_driver);
@@ -89,4 +103,3 @@ module_spi_driver(dac7512_driver);
 MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
 MODULE_DESCRIPTION("DAC7512 16-bit DAC");
 MODULE_LICENSE("GPL v2");
-MODULE_VERSION(DRIVER_VERSION);
index f8d6654391e5df35f13ec7b7e825baf3710baee6..ae282a1004295157a7e0e8667fac515e5fdc476c 100644 (file)
@@ -378,7 +378,6 @@ err_out_irq:
 err_out_unmap:
        iounmap(fm->addr);
 err_out_free:
-       pci_set_drvdata(dev, NULL);
        tifm_free_adapter(fm);
 err_out_int:
        pci_intx(dev, 0);
@@ -405,8 +404,6 @@ static void tifm_7xx1_remove(struct pci_dev *dev)
        for (cnt = 0; cnt < fm->num_sockets; cnt++)
                tifm_7xx1_sock_power_off(tifm_7xx1_sock_addr(fm->addr, cnt));
 
-       pci_set_drvdata(dev, NULL);
-
        iounmap(fm->addr);
        pci_intx(dev, 0);
        pci_release_regions(dev);
index b3a2b763ecf25bd792b762c8f30778012398320a..c98b03b993537aa62ff1e38242b6ad9dbc59b96d 100644 (file)
@@ -649,7 +649,7 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
        return 0;
 
 err_free_irq:
-       free_irq(vmci_dev->irq, &vmci_dev);
+       free_irq(vmci_dev->irq, vmci_dev);
        tasklet_kill(&vmci_dev->datagram_tasklet);
        tasklet_kill(&vmci_dev->bm_tasklet);
 
index d4722b3dc8ec762987f18d25268727ea9954b32c..1723a6e4f2e8ea97b70e3d151f0bc0e29dc29c15 100644 (file)
@@ -243,11 +243,7 @@ static int vmci_host_setup_notify(struct vmci_ctx *context,
        /*
         * Lock physical page backing a given user VA.
         */
-       down_read(&current->mm->mmap_sem);
-       retval = get_user_pages(current, current->mm,
-                               PAGE_ALIGN(uva),
-                               1, 1, 0, &page, NULL);
-       up_read(&current->mm->mmap_sem);
+       retval = get_user_pages_fast(PAGE_ALIGN(uva), 1, 1, &page);
        if (retval != 1)
                return VMCI_ERROR_GENERIC;
 
index a0515a6d6ebdbcf1438e4e8c544d879fc478e08a..1b7b303085d28d459c2595776fd82b15cf9a0dc5 100644 (file)
@@ -732,13 +732,9 @@ static int qp_host_get_user_memory(u64 produce_uva,
        int retval;
        int err = VMCI_SUCCESS;
 
-       down_write(&current->mm->mmap_sem);
-       retval = get_user_pages(current,
-                               current->mm,
-                               (uintptr_t) produce_uva,
-                               produce_q->kernel_if->num_pages,
-                               1, 0,
-                               produce_q->kernel_if->u.h.header_page, NULL);
+       retval = get_user_pages_fast((uintptr_t) 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);
                qp_release_pages(produce_q->kernel_if->u.h.header_page,
@@ -747,12 +743,9 @@ static int qp_host_get_user_memory(u64 produce_uva,
                goto out;
        }
 
-       retval = get_user_pages(current,
-                               current->mm,
-                               (uintptr_t) consume_uva,
-                               consume_q->kernel_if->num_pages,
-                               1, 0,
-                               consume_q->kernel_if->u.h.header_page, NULL);
+       retval = get_user_pages_fast((uintptr_t) consume_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);
                qp_release_pages(consume_q->kernel_if->u.h.header_page,
@@ -763,8 +756,6 @@ static int qp_host_get_user_memory(u64 produce_uva,
        }
 
  out:
-       up_write(&current->mm->mmap_sem);
-
        return err;
 }
 
index a4c16ee5c7188d24bce41f834851f61f5e56d9ea..622dd6fe7347c7e3aec8fb3d337347ec8bd03537 100644 (file)
@@ -777,15 +777,4 @@ static struct pci_driver pd6729_pci_driver = {
        .remove         = pd6729_pci_remove,
 };
 
-static int pd6729_module_init(void)
-{
-       return pci_register_driver(&pd6729_pci_driver);
-}
-
-static void pd6729_module_exit(void)
-{
-       pci_unregister_driver(&pd6729_pci_driver);
-}
-
-module_init(pd6729_module_init);
-module_exit(pd6729_module_exit);
+module_pci_driver(pd6729_pci_driver);
index 6b4ff099fb13c8de403f20e1920c002787c8a81d..dc18a3a5e010479756545400a3c84f7dc0409685 100644 (file)
@@ -1439,20 +1439,6 @@ static struct pci_driver yenta_cardbus_driver = {
        .driver.pm      = YENTA_PM_OPS,
 };
 
-
-static int __init yenta_socket_init(void)
-{
-       return pci_register_driver(&yenta_cardbus_driver);
-}
-
-
-static void __exit yenta_socket_exit(void)
-{
-       pci_unregister_driver(&yenta_cardbus_driver);
-}
-
-
-module_init(yenta_socket_init);
-module_exit(yenta_socket_exit);
+module_pci_driver(yenta_cardbus_driver);
 
 MODULE_LICENSE("GPL");
index ba475632c5fa2aea166efa3b03985139e071db2f..11d4e0a5257911b19773cdfa223af13c785bc973 100644 (file)
@@ -796,10 +796,9 @@ int __uio_register_device(struct module *owner,
 
        info->uio_dev = NULL;
 
-       idev = kzalloc(sizeof(*idev), GFP_KERNEL);
+       idev = devm_kzalloc(parent, sizeof(*idev), GFP_KERNEL);
        if (!idev) {
-               ret = -ENOMEM;
-               goto err_kzalloc;
+               return -ENOMEM;
        }
 
        idev->owner = owner;
@@ -809,7 +808,7 @@ int __uio_register_device(struct module *owner,
 
        ret = uio_get_minor(idev);
        if (ret)
-               goto err_get_minor;
+               return ret;
 
        idev->dev = device_create(&uio_class, parent,
                                  MKDEV(uio_major, idev->minor), idev,
@@ -827,7 +826,7 @@ int __uio_register_device(struct module *owner,
        info->uio_dev = idev;
 
        if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) {
-               ret = request_irq(info->irq, uio_interrupt,
+               ret = devm_request_irq(parent, info->irq, uio_interrupt,
                                  info->irq_flags, info->name, idev);
                if (ret)
                        goto err_request_irq;
@@ -841,9 +840,6 @@ err_uio_dev_add_attributes:
        device_destroy(&uio_class, MKDEV(uio_major, idev->minor));
 err_device_create:
        uio_free_minor(idev);
-err_get_minor:
-       kfree(idev);
-err_kzalloc:
        return ret;
 }
 EXPORT_SYMBOL_GPL(__uio_register_device);
@@ -864,13 +860,9 @@ void uio_unregister_device(struct uio_info *info)
 
        uio_free_minor(idev);
 
-       if (info->irq && (info->irq != UIO_IRQ_CUSTOM))
-               free_irq(info->irq, idev);
-
        uio_dev_del_attributes(idev);
 
        device_destroy(&uio_class, MKDEV(uio_major, idev->minor));
-       kfree(idev);
 
        return;
 }
index f3611c2d83b681e1903d9bc502347108f50263bc..1549fab633c6a6790dd9aeb2dacdc02ee61f4d9f 100644 (file)
@@ -147,7 +147,6 @@ static void remove(struct pci_dev *pdev)
        uio_unregister_device(info);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
-       pci_set_drvdata(pdev, NULL);
        iounmap(info->priv);
 
        kfree(info);
index 22cdf385ab33159cc99c37fd10946f96bbefdf85..30f533ce37585ed6eb7f82c56c91830442b2809a 100644 (file)
@@ -106,7 +106,6 @@ static void hilscher_pci_remove(struct pci_dev *dev)
        uio_unregister_device(info);
        pci_release_regions(dev);
        pci_disable_device(dev);
-       pci_set_drvdata(dev, NULL);
        iounmap(info->mem[0].internal_addr);
 
        kfree (info);
index a1768b2f449395b1f58220cbb649bc76ee5d42ce..f764adbfe036861dfccbada4ac9f27152e2f88aa 100644 (file)
@@ -42,7 +42,7 @@
 
 enum mf624_interrupt_source {ADC, CTR4, ALL};
 
-void mf624_disable_interrupt(enum mf624_interrupt_source source,
+static void mf624_disable_interrupt(enum mf624_interrupt_source source,
                             struct uio_info *info)
 {
        void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
@@ -70,7 +70,7 @@ void mf624_disable_interrupt(enum mf624_interrupt_source source,
        }
 }
 
-void mf624_enable_interrupt(enum mf624_interrupt_source source,
+static void mf624_enable_interrupt(enum mf624_interrupt_source source,
                            struct uio_info *info)
 {
        void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
@@ -220,7 +220,6 @@ static void mf624_pci_remove(struct pci_dev *dev)
        uio_unregister_device(info);
        pci_release_regions(dev);
        pci_disable_device(dev);
-       pci_set_drvdata(dev, NULL);
 
        iounmap(info->mem[0].internal_addr);
        iounmap(info->mem[1].internal_addr);
index 28a766b9e198af4ded7bc8ddfe2c28181909ddea..4c345db8b016e8adab57c49e2f19992113ec41b8 100644 (file)
@@ -127,7 +127,6 @@ static void netx_pci_remove(struct pci_dev *dev)
        uio_unregister_device(info);
        pci_release_regions(dev);
        pci_disable_device(dev);
-       pci_set_drvdata(dev, NULL);
        iounmap(info->mem[0].internal_addr);
 
        kfree(info);
index 90ff17a0202fb17e090985b7798848213aa2acf6..76669313e9a705679af830b23b2d2d8f48a30e4e 100644 (file)
@@ -112,11 +112,11 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
 
        if (pdev->dev.of_node) {
                /* alloc uioinfo for one device */
-               uioinfo = kzalloc(sizeof(*uioinfo), GFP_KERNEL);
+               uioinfo = devm_kzalloc(&pdev->dev, sizeof(*uioinfo),
+                                      GFP_KERNEL);
                if (!uioinfo) {
-                       ret = -ENOMEM;
                        dev_err(&pdev->dev, "unable to kmalloc\n");
-                       return ret;
+                       return -ENOMEM;
                }
                uioinfo->name = pdev->dev.of_node->name;
                uioinfo->version = "devicetree";
@@ -125,20 +125,19 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
 
        if (!uioinfo || !uioinfo->name || !uioinfo->version) {
                dev_err(&pdev->dev, "missing platform_data\n");
-               goto bad0;
+               return ret;
        }
 
        if (uioinfo->handler || uioinfo->irqcontrol ||
            uioinfo->irq_flags & IRQF_SHARED) {
                dev_err(&pdev->dev, "interrupt configuration error\n");
-               goto bad0;
+               return ret;
        }
 
-       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv) {
-               ret = -ENOMEM;
                dev_err(&pdev->dev, "unable to kmalloc\n");
-               goto bad0;
+               return -ENOMEM;
        }
 
        priv->uioinfo = uioinfo;
@@ -153,7 +152,7 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
                        uioinfo->irq = UIO_IRQ_NONE;
                else if (ret < 0) {
                        dev_err(&pdev->dev, "failed to get IRQ\n");
-                       goto bad1;
+                       return ret;
                }
        }
 
@@ -209,20 +208,12 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
        ret = uio_register_device(&pdev->dev, priv->uioinfo);
        if (ret) {
                dev_err(&pdev->dev, "unable to register uio device\n");
-               goto bad2;
+               pm_runtime_disable(&pdev->dev);
+               return ret;
        }
 
        platform_set_drvdata(pdev, priv);
        return 0;
- bad2:
-       pm_runtime_disable(&pdev->dev);
- bad1:
-       kfree(priv);
- bad0:
-       /* kfree uioinfo for OF */
-       if (pdev->dev.of_node)
-               kfree(uioinfo);
-       return ret;
 }
 
 static int uio_pdrv_genirq_remove(struct platform_device *pdev)
@@ -235,11 +226,6 @@ static int uio_pdrv_genirq_remove(struct platform_device *pdev)
        priv->uioinfo->handler = NULL;
        priv->uioinfo->irqcontrol = NULL;
 
-       /* kfree uioinfo for OF */
-       if (pdev->dev.of_node)
-               kfree(priv->uioinfo);
-
-       kfree(priv);
        return 0;
 }
 
index 5419832170856b78b384659f8bc961c3a283f2ee..9cfdfcafa2621de3feab90f003a68854a8b36eca 100644 (file)
@@ -188,7 +188,6 @@ static void sercos3_pci_remove(struct pci_dev *dev)
        uio_unregister_device(info);
        pci_release_regions(dev);
        pci_disable_device(dev);
-       pci_set_drvdata(dev, NULL);
        for (i = 0; i < 5; i++) {
                if (info->mem[i].internal_addr)
                        iounmap(info->mem[i].internal_addr);
index 96cab6ac2b4e6fdda01cff9d5bdb7d185fb1f82f..41613f92a723448c1b4091a7e357fc05b2af9638 100644 (file)
@@ -498,7 +498,7 @@ static int ds1wm_probe(struct platform_device *pdev)
                irq_set_irq_type(ds1wm_data->irq, IRQ_TYPE_EDGE_FALLING);
 
        ret = devm_request_irq(&pdev->dev, ds1wm_data->irq, ds1wm_isr,
-                       IRQF_DISABLED | IRQF_SHARED, "ds1wm", ds1wm_data);
+                       IRQF_SHARED, "ds1wm", ds1wm_data);
        if (ret)
                return ret;
 
index 6e94d8dd3d00a31d696fc7e140329d9b4dc72c6b..9900e8ec73939fef918abff3bda6829e6b25b4d3 100644 (file)
@@ -577,8 +577,7 @@ static int omap_hdq_probe(struct platform_device *pdev)
                goto err_irq;
        }
 
-       ret = devm_request_irq(dev, irq, hdq_isr, IRQF_DISABLED,
-                       "omap_hdq", hdq_data);
+       ret = devm_request_irq(dev, irq, hdq_isr, 0, "omap_hdq", hdq_data);
        if (ret < 0) {
                dev_dbg(&pdev->dev, "could not request irq\n");
                goto err_irq;
index fcb51c88319f1a2b3d09715f9d74a12dc25cf49a..21c59af1150b57588fbe697c9ef9fe38aacff06f 100644 (file)
 enum extcon_cable_name {
        EXTCON_USB = 0,
        EXTCON_USB_HOST,
-       EXTCON_TA, /* Travel Adaptor */
+       EXTCON_TA,                      /* Travel Adaptor */
        EXTCON_FAST_CHARGER,
        EXTCON_SLOW_CHARGER,
-       EXTCON_CHARGE_DOWNSTREAM, /* Charging an external device */
+       EXTCON_CHARGE_DOWNSTREAM,       /* Charging an external device */
        EXTCON_HDMI,
        EXTCON_MHL,
        EXTCON_DVI,
@@ -76,8 +76,8 @@ struct extcon_cable;
 
 /**
  * struct extcon_dev - An extcon device represents one external connector.
- * @name:      The name of this extcon device. Parent device name is used
- *             if NULL.
+ * @name:              The name of this extcon device. Parent device name is
+ *                     used if NULL.
  * @supported_cable:   Array of supported cable names ending with NULL.
  *                     If supported_cable is NULL, cable name related APIs
  *                     are disabled.
@@ -89,21 +89,21 @@ struct extcon_cable;
  *                     be attached simulataneously. {0x7, 0} is equivalent to
  *                     {0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there
  *                     can be no simultaneous connections.
- * @print_name:        An optional callback to override the method to print the
- *             name of the extcon device.
+ * @print_name:                An optional callback to override the method to print the
+ *                     name of the extcon device.
  * @print_state:       An optional callback to override the method to print the
- *             status of the extcon device.
- * @dev:       Device of this extcon. Do not provide at register-time.
- * @state:     Attach/detach state of this extcon. Do not provide at
- *             register-time
- * @nh:        Notifier for the state change events from this extcon
- * @entry:     To support list of extcon devices so that users can search
- *             for extcon devices based on the extcon name.
+ *                     status of the extcon device.
+ * @dev:               Device of this extcon.
+ * @state:             Attach/detach state of this extcon. Do not provide at
+ *                     register-time.
+ * @nh:                        Notifier for the state change events from this extcon
+ * @entry:             To support list of extcon devices so that users can search
+ *                     for extcon devices based on the extcon name.
  * @lock:
  * @max_supported:     Internal value to store the number of cables.
  * @extcon_dev_type:   Device_type struct to provide attribute_groups
  *                     customized for each extcon device.
- * @cables:    Sysfs subdirectories. Each represents one cable.
+ * @cables:            Sysfs subdirectories. Each represents one cable.
  *
  * In most cases, users only need to provide "User initializing data" of
  * this struct when registering an extcon. In some exceptional cases,
@@ -111,26 +111,27 @@ struct extcon_cable;
  * are overwritten by register function.
  */
 struct extcon_dev {
-       /* --- Optional user initializing data --- */
-       const char      *name;
+       /* Optional user initializing data */
+       const char *name;
        const char **supported_cable;
-       const u32       *mutually_exclusive;
+       const u32 *mutually_exclusive;
 
-       /* --- Optional callbacks to override class functions --- */
+       /* Optional callbacks to override class functions */
        ssize_t (*print_name)(struct extcon_dev *edev, char *buf);
        ssize_t (*print_state)(struct extcon_dev *edev, char *buf);
 
-       /* --- Internal data. Please do not set. --- */
-       struct device   *dev;
-       u32             state;
+       /* Internal data. Please do not set. */
+       struct device dev;
        struct raw_notifier_head nh;
        struct list_head entry;
-       spinlock_t lock; /* could be called by irq handler */
        int max_supported;
+       spinlock_t lock;        /* could be called by irq handler */
+       u32 state;
 
        /* /sys/class/extcon/.../cable.n/... */
        struct device_type extcon_dev_type;
        struct extcon_cable *cables;
+
        /* /sys/class/extcon/.../mutually_exclusive/... */
        struct attribute_group attr_g_muex;
        struct attribute **attrs_muex;
@@ -138,13 +139,13 @@ struct extcon_dev {
 };
 
 /**
- * struct extcon_cable - An internal data for each cable of extcon device.
- * @edev:      The extcon device
+ * struct extcon_cable - An internal data for each cable of extcon device.
+ * @edev:              The extcon device
  * @cable_index:       Index of this cable in the edev
- * @attr_g:    Attribute group for the cable
- * @attr_name: "name" sysfs entry
- * @attr_state:        "state" sysfs entry
- * @attrs:     Array pointing to attr_name and attr_state for attr_g
+ * @attr_g:            Attribute group for the cable
+ * @attr_name:         "name" sysfs entry
+ * @attr_state:                "state" sysfs entry
+ * @attrs:             Array pointing to attr_name and attr_state for attr_g
  */
 struct extcon_cable {
        struct extcon_dev *edev;
@@ -159,11 +160,13 @@ struct extcon_cable {
 
 /**
  * struct extcon_specific_cable_nb - An internal data for
- *                             extcon_register_interest().
- * @internal_nb:       a notifier block bridging extcon notifier and cable notifier.
- * @user_nb:   user provided notifier block for events from a specific cable.
+ *                                  extcon_register_interest().
+ * @internal_nb:       A notifier block bridging extcon notifier
+ *                     and cable notifier.
+ * @user_nb:           user provided notifier block for events from
+ *                     a specific cable.
  * @cable_index:       the target cable.
- * @edev:      the target extcon device.
+ * @edev:              the target extcon device.
  * @previous_value:    the saved previous event value.
  */
 struct extcon_specific_cable_nb {
@@ -180,7 +183,7 @@ struct extcon_specific_cable_nb {
  * Following APIs are for notifiers or configurations.
  * Notifiers are the external port and connection devices.
  */
-extern int extcon_dev_register(struct extcon_dev *edev, struct device *dev);
+extern int extcon_dev_register(struct extcon_dev *edev);
 extern void extcon_dev_unregister(struct extcon_dev *edev);
 extern struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name);
 
@@ -238,8 +241,7 @@ extern int extcon_register_notifier(struct extcon_dev *edev,
 extern int extcon_unregister_notifier(struct extcon_dev *edev,
                                      struct notifier_block *nb);
 #else /* CONFIG_EXTCON */
-static inline int extcon_dev_register(struct extcon_dev *edev,
-                                     struct device *dev)
+static inline int extcon_dev_register(struct extcon_dev *edev)
 {
        return 0;
 }
index 20e9eef25d4c2d30e33fe4986e6eeb6276647bae..9ca958c4e94c79f9ae7997e47b6c521478930b19 100644 (file)
 
 /**
  * struct adc_jack_cond - condition to use an extcon state
- * @state      - the corresponding extcon state (if 0, this struct denotes
- *             the last adc_jack_cond element among the array)
- * @min_adc    - min adc value for this condition
- * @max_adc    - max adc value for this condition
+ * @state:             the corresponding extcon state (if 0, this struct
+ *                     denotes the last adc_jack_cond element among the array)
+ * @min_adc:           min adc value for this condition
+ * @max_adc:           max adc value for this condition
  *
  * For example, if { .state = 0x3, .min_adc = 100, .max_adc = 200}, it means
  * that if ADC value is between (inclusive) 100 and 200, than the cable 0 and
  * because when no adc_jack_cond is met, state = 0 is automatically chosen.
  */
 struct adc_jack_cond {
-       u32 state; /* extcon state value. 0 if invalid */
+       u32 state;      /* extcon state value. 0 if invalid */
        u32 min_adc;
        u32 max_adc;
 };
 
 /**
  * struct adc_jack_pdata - platform data for adc jack device.
- * @name       - name of the extcon device. If null, "adc-jack" is used.
- * @consumer_channel - Unique name to identify the channel on the consumer
- *                   side. This typically describes the channels used within
- *                   the consumer. E.g. 'battery_voltage'
- * @cable_names        - array of cable names ending with null.
- * @adc_contitions     - array of struct adc_jack_cond conditions ending
- *                     with .state = 0 entry. This describes how to decode
- *                     adc values into extcon state.
- * @irq_flags  - irq flags used for the @irq
- * @handling_delay_ms  - in some devices, we need to read ADC value some
- *                     milli-seconds after the interrupt occurs. You may
- *                     describe such delays with @handling_delay_ms, which
- *                     is rounded-off by jiffies.
+ * @name:              name of the extcon device. If null, "adc-jack" is used.
+ * @consumer_channel Unique name to identify the channel on the consumer
+ *                     side. This typically describes the channels used within
+ *                     the consumer. E.g. 'battery_voltage'
+ * @cable_names:       array of cable names ending with null.
+ * @adc_contitions:    array of struct adc_jack_cond conditions ending
+ *                     with .state = 0 entry. This describes how to decode
+ *                     adc values into extcon state.
+ * @irq_flags:         irq flags used for the @irq
+ * @handling_delay_ms: in some devices, we need to read ADC value some
+ *                     milli-seconds after the interrupt occurs. You may
+ *                     describe such delays with @handling_delay_ms, which
+ *                     is rounded-off by jiffies.
  */
 struct adc_jack_pdata {
        const char *name;
        const char *consumer_channel;
-       /*
-        * The last entry should be NULL
-        */
+
+       /* The last entry should be NULL */
        const char **cable_names;
+
        /* The last entry's state should be 0 */
        struct adc_jack_cond *adc_conditions;
 
index 2d8307f7d67decd0f0729b1f121cf0475e4b04b7..4195810f87fe6a3eda2eaf928b3361cb795626b8 100644 (file)
 
 /**
  * struct gpio_extcon_platform_data - A simple GPIO-controlled extcon device.
- * @name       The name of this GPIO extcon device.
- * @gpio       Corresponding GPIO.
- * @debounce   Debounce time for GPIO IRQ in ms.
- * @irq_flags  IRQ Flags (e.g., IRQF_TRIGGER_LOW).
- * @state_on   print_state is overriden with state_on if attached. If Null,
- *             default method of extcon class is used.
- * @state_off  print_state is overriden with state_on if detached. If Null,
- *             default method of extcon class is used.
+ * @name:              The name of this GPIO extcon device.
+ * @gpio:              Corresponding GPIO.
+ * @gpio_active_low:   Boolean describing whether gpio active state is 1 or 0
+ *                     If true, low state of gpio means active.
+ *                     If false, high state of gpio means active.
+ * @debounce:          Debounce time for GPIO IRQ in ms.
+ * @irq_flags:         IRQ Flags (e.g., IRQF_TRIGGER_LOW).
+ * @state_on:          print_state is overriden with state_on if attached.
+ *                     If NULL, default method of extcon class is used.
+ * @state_off:         print_state is overriden with state_on if detached.
+ *                     If NUll, default method of extcon class is used.
  *
  * Note that in order for state_on or state_off to be valid, both state_on
  * and state_off should be not NULL. If at least one of them is NULL,
@@ -41,6 +44,7 @@
 struct gpio_extcon_platform_data {
        const char *name;
        unsigned gpio;
+       bool gpio_active_low;
        unsigned long debounce;
        unsigned long irq_flags;
 
index d98503bde7e9bc7ace0e2c3ddc5f2ae019f527ff..15da677478ddc353b151d81362043bcbdfc3d089 100644 (file)
@@ -432,15 +432,6 @@ struct hv_ring_buffer_info {
        u32 ring_data_startoffset;
 };
 
-struct hv_ring_buffer_debug_info {
-       u32 current_interrupt_mask;
-       u32 current_read_index;
-       u32 current_write_index;
-       u32 bytes_avail_toread;
-       u32 bytes_avail_towrite;
-};
-
-
 /*
  *
  * hv_get_ringbuffer_availbytes()
@@ -902,23 +893,6 @@ enum vmbus_channel_state {
        CHANNEL_OPENED_STATE,
 };
 
-struct vmbus_channel_debug_info {
-       u32 relid;
-       enum vmbus_channel_state state;
-       uuid_le interfacetype;
-       uuid_le interface_instance;
-       u32 monitorid;
-       u32 servermonitor_pending;
-       u32 servermonitor_latency;
-       u32 servermonitor_connectionid;
-       u32 clientmonitor_pending;
-       u32 clientmonitor_latency;
-       u32 clientmonitor_connectionid;
-
-       struct hv_ring_buffer_debug_info inbound;
-       struct hv_ring_buffer_debug_info outbound;
-};
-
 /*
  * Represents each channel msg on the vmbus connection This is a
  * variable-size data structure depending on the msg type itself
@@ -1184,19 +1158,8 @@ extern int vmbus_recvpacket_raw(struct vmbus_channel *channel,
                                     u64 *requestid);
 
 
-extern void vmbus_get_debug_info(struct vmbus_channel *channel,
-                                    struct vmbus_channel_debug_info *debug);
-
 extern void vmbus_ontimer(unsigned long data);
 
-struct hv_dev_port_info {
-       u32 int_mask;
-       u32 read_idx;
-       u32 write_idx;
-       u32 bytes_avail_toread;
-       u32 bytes_avail_towrite;
-};
-
 /* Base driver object */
 struct hv_driver {
        const char *name;
index 115add2515aaad2bc5caebd1bf330060fee75ebc..33d2b8fe166dafcd8dd39c5b7a092b8b3d5befa6 100644 (file)
@@ -241,6 +241,8 @@ header-y += media.h
 header-y += mei.h
 header-y += mempolicy.h
 header-y += meye.h
+header-y += mic_common.h
+header-y += mic_ioctl.h
 header-y += mii.h
 header-y += minix_fs.h
 header-y += mman.h
diff --git a/include/uapi/linux/mic_common.h b/include/uapi/linux/mic_common.h
new file mode 100644 (file)
index 0000000..17e7d95
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC driver.
+ *
+ */
+#ifndef __MIC_COMMON_H_
+#define __MIC_COMMON_H_
+
+#include <linux/virtio_ring.h>
+
+#ifndef __KERNEL__
+#define ALIGN(a, x)    (((a) + (x) - 1) & ~((x) - 1))
+#define __aligned(x)   __attribute__ ((aligned(x)))
+#endif
+
+#define mic_aligned_size(x) ALIGN(sizeof(x), 8)
+
+/**
+ * struct mic_device_desc: Virtio device information shared between the
+ * virtio driver and userspace backend
+ *
+ * @type: Device type: console/network/disk etc.  Type 0/-1 terminates.
+ * @num_vq: Number of virtqueues.
+ * @feature_len: Number of bytes of feature bits.  Multiply by 2: one for
+   host features and one for guest acknowledgements.
+ * @config_len: Number of bytes of the config array after virtqueues.
+ * @status: A status byte, written by the Guest.
+ * @config: Start of the following variable length config.
+ */
+struct mic_device_desc {
+       __s8 type;
+       __u8 num_vq;
+       __u8 feature_len;
+       __u8 config_len;
+       __u8 status;
+       __u64 config[0];
+} __aligned(8);
+
+/**
+ * struct mic_device_ctrl: Per virtio device information in the device page
+ * used internally by the host and card side drivers.
+ *
+ * @vdev: Used for storing MIC vdev information by the guest.
+ * @config_change: Set to 1 by host when a config change is requested.
+ * @vdev_reset: Set to 1 by guest to indicate virtio device has been reset.
+ * @guest_ack: Set to 1 by guest to ack a command.
+ * @host_ack: Set to 1 by host to ack a command.
+ * @used_address_updated: Set to 1 by guest when the used address should be
+ * updated.
+ * @c2h_vdev_db: The doorbell number to be used by guest. Set by host.
+ * @h2c_vdev_db: The doorbell number to be used by host. Set by guest.
+ */
+struct mic_device_ctrl {
+       __u64 vdev;
+       __u8 config_change;
+       __u8 vdev_reset;
+       __u8 guest_ack;
+       __u8 host_ack;
+       __u8 used_address_updated;
+       __s8 c2h_vdev_db;
+       __s8 h2c_vdev_db;
+} __aligned(8);
+
+/**
+ * struct mic_bootparam: Virtio device independent information in device page
+ *
+ * @magic: A magic value used by the card to ensure it can see the host
+ * @c2h_shutdown_db: Card to Host shutdown doorbell set by host
+ * @h2c_shutdown_db: Host to Card shutdown doorbell set by card
+ * @h2c_config_db: Host to Card Virtio config doorbell set by card
+ * @shutdown_status: Card shutdown status set by card
+ * @shutdown_card: Set to 1 by the host when a card shutdown is initiated
+ */
+struct mic_bootparam {
+       __u32 magic;
+       __s8 c2h_shutdown_db;
+       __s8 h2c_shutdown_db;
+       __s8 h2c_config_db;
+       __u8 shutdown_status;
+       __u8 shutdown_card;
+} __aligned(8);
+
+/**
+ * struct mic_device_page: High level representation of the device page
+ *
+ * @bootparam: The bootparam structure is used for sharing information and
+ * status updates between MIC host and card drivers.
+ * @desc: Array of MIC virtio device descriptors.
+ */
+struct mic_device_page {
+       struct mic_bootparam bootparam;
+       struct mic_device_desc desc[0];
+};
+/**
+ * struct mic_vqconfig: This is how we expect the device configuration field
+ * for a virtqueue to be laid out in config space.
+ *
+ * @address: Guest/MIC physical address of the virtio ring
+ * (avail and desc rings)
+ * @used_address: Guest/MIC physical address of the used ring
+ * @num: The number of entries in the virtio_ring
+ */
+struct mic_vqconfig {
+       __u64 address;
+       __u64 used_address;
+       __u16 num;
+} __aligned(8);
+
+/*
+ * The alignment to use between consumer and producer parts of vring.
+ * This is pagesize for historical reasons.
+ */
+#define MIC_VIRTIO_RING_ALIGN          4096
+
+#define MIC_MAX_VRINGS                 4
+#define MIC_VRING_ENTRIES              128
+
+/*
+ * Max vring entries (power of 2) to ensure desc and avail rings
+ * fit in a single page
+ */
+#define MIC_MAX_VRING_ENTRIES          128
+
+/**
+ * Max size of the desc block in bytes: includes:
+ *     - struct mic_device_desc
+ *     - struct mic_vqconfig (num_vq of these)
+ *     - host and guest features
+ *     - virtio device config space
+ */
+#define MIC_MAX_DESC_BLK_SIZE          256
+
+/**
+ * struct _mic_vring_info - Host vring info exposed to userspace backend
+ * for the avail index and magic for the card.
+ *
+ * @avail_idx: host avail idx
+ * @magic: A magic debug cookie.
+ */
+struct _mic_vring_info {
+       __u16 avail_idx;
+       int magic;
+};
+
+/**
+ * struct mic_vring - Vring information.
+ *
+ * @vr: The virtio ring.
+ * @info: Host vring information exposed to the userspace backend for the
+ * avail index and magic for the card.
+ * @va: The va for the buffer allocated for vr and info.
+ * @len: The length of the buffer required for allocating vr and info.
+ */
+struct mic_vring {
+       struct vring vr;
+       struct _mic_vring_info *info;
+       void *va;
+       int len;
+};
+
+#define mic_aligned_desc_size(d) ALIGN(mic_desc_size(d), 8)
+
+#ifndef INTEL_MIC_CARD
+static inline unsigned mic_desc_size(const struct mic_device_desc *desc)
+{
+       return mic_aligned_size(*desc)
+               + desc->num_vq * mic_aligned_size(struct mic_vqconfig)
+               + desc->feature_len * 2
+               + desc->config_len;
+}
+
+static inline struct mic_vqconfig *
+mic_vq_config(const struct mic_device_desc *desc)
+{
+       return (struct mic_vqconfig *)(desc + 1);
+}
+
+static inline __u8 *mic_vq_features(const struct mic_device_desc *desc)
+{
+       return (__u8 *)(mic_vq_config(desc) + desc->num_vq);
+}
+
+static inline __u8 *mic_vq_configspace(const struct mic_device_desc *desc)
+{
+       return mic_vq_features(desc) + desc->feature_len * 2;
+}
+static inline unsigned mic_total_desc_size(struct mic_device_desc *desc)
+{
+       return mic_aligned_desc_size(desc) +
+               mic_aligned_size(struct mic_device_ctrl);
+}
+#endif
+
+/* Device page size */
+#define MIC_DP_SIZE 4096
+
+#define MIC_MAGIC 0xc0ffee00
+
+/**
+ * enum mic_states - MIC states.
+ */
+enum mic_states {
+       MIC_OFFLINE = 0,
+       MIC_ONLINE,
+       MIC_SHUTTING_DOWN,
+       MIC_RESET_FAILED,
+       MIC_SUSPENDING,
+       MIC_SUSPENDED,
+       MIC_LAST
+};
+
+/**
+ * enum mic_status - MIC status reported by card after
+ * a host or card initiated shutdown or a card crash.
+ */
+enum mic_status {
+       MIC_NOP = 0,
+       MIC_CRASHED,
+       MIC_HALTED,
+       MIC_POWER_OFF,
+       MIC_RESTART,
+       MIC_STATUS_LAST
+};
+
+#endif
diff --git a/include/uapi/linux/mic_ioctl.h b/include/uapi/linux/mic_ioctl.h
new file mode 100644 (file)
index 0000000..7fabba5
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * 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.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Host driver.
+ *
+ */
+#ifndef _MIC_IOCTL_H_
+#define _MIC_IOCTL_H_
+
+#include <linux/types.h>
+
+/*
+ * mic_copy - MIC virtio descriptor copy.
+ *
+ * @iov: An array of IOVEC structures containing user space buffers.
+ * @iovcnt: Number of IOVEC structures in iov.
+ * @vr_idx: The vring index.
+ * @update_used: A non zero value results in used index being updated.
+ * @out_len: The aggregate of the total length written to or read from
+ *     the virtio device.
+ */
+struct mic_copy_desc {
+#ifdef __KERNEL__
+       struct iovec __user *iov;
+#else
+       struct iovec *iov;
+#endif
+       int iovcnt;
+       __u8 vr_idx;
+       __u8 update_used;
+       __u32 out_len;
+};
+
+/*
+ * Add a new virtio device
+ * The (struct mic_device_desc *) pointer points to a device page entry
+ *     for the virtio device consisting of:
+ *     - struct mic_device_desc
+ *     - struct mic_vqconfig (num_vq of these)
+ *     - host and guest features
+ *     - virtio device config space
+ * The total size referenced by the pointer should equal the size returned
+ * by desc_size() in mic_common.h
+ */
+#define MIC_VIRTIO_ADD_DEVICE _IOWR('s', 1, struct mic_device_desc *)
+
+/*
+ * Copy the number of entries in the iovec and update the used index
+ * if requested by the user.
+ */
+#define MIC_VIRTIO_COPY_DESC   _IOWR('s', 2, struct mic_copy_desc *)
+
+/*
+ * Notify virtio device of a config change
+ * The (__u8 *) pointer points to config space values for the device
+ * as they should be written into the device page. The total size
+ * referenced by the pointer should equal the config_len field of struct
+ * mic_device_desc.
+ */
+#define MIC_VIRTIO_CONFIG_CHANGE _IOWR('s', 5, __u8 *)
+
+#endif
index 8fd9ec66121c111b5bcfc29fd6e0e286f125ad67..b8d6d541d854b7d719056cec96640548980eb79e 100644 (file)
@@ -89,6 +89,7 @@ static char *processor_arch;
 static char *os_build;
 static char *os_version;
 static char *lic_version = "Unknown version";
+static char full_domain_name[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
 static struct utsname uts_buf;
 
 /*
@@ -1367,7 +1368,7 @@ setval_error:
 }
 
 
-static int
+static void
 kvp_get_domain_name(char *buffer, int length)
 {
        struct addrinfo hints, *info ;
@@ -1381,12 +1382,12 @@ kvp_get_domain_name(char *buffer, int length)
 
        error = getaddrinfo(buffer, NULL, &hints, &info);
        if (error != 0) {
-               strcpy(buffer, "getaddrinfo failed\n");
-               return error;
+               snprintf(buffer, length, "getaddrinfo failed: 0x%x %s",
+                       error, gai_strerror(error));
+               return;
        }
-       strcpy(buffer, info->ai_canonname);
+       snprintf(buffer, length, "%s", info->ai_canonname);
        freeaddrinfo(info);
-       return error;
 }
 
 static int
@@ -1433,7 +1434,6 @@ int main(void)
        int     pool;
        char    *if_name;
        struct hv_kvp_ipaddr_value *kvp_ip_val;
-       char *kvp_send_buffer;
        char *kvp_recv_buffer;
        size_t kvp_recv_buffer_len;
 
@@ -1442,17 +1442,21 @@ int main(void)
        openlog("KVP", 0, LOG_USER);
        syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
 
-       kvp_recv_buffer_len = NLMSG_HDRLEN + sizeof(struct cn_msg) + sizeof(struct hv_kvp_msg);
-       kvp_send_buffer = calloc(1, kvp_recv_buffer_len);
+       kvp_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_kvp_msg);
        kvp_recv_buffer = calloc(1, kvp_recv_buffer_len);
-       if (!(kvp_send_buffer && kvp_recv_buffer)) {
-               syslog(LOG_ERR, "Failed to allocate netlink buffers");
+       if (!kvp_recv_buffer) {
+               syslog(LOG_ERR, "Failed to allocate netlink buffer");
                exit(EXIT_FAILURE);
        }
        /*
         * Retrieve OS release information.
         */
        kvp_get_os_info();
+       /*
+        * Cache Fully Qualified Domain Name because getaddrinfo takes an
+        * unpredictable amount of time to finish.
+        */
+       kvp_get_domain_name(full_domain_name, sizeof(full_domain_name));
 
        if (kvp_file_init()) {
                syslog(LOG_ERR, "Failed to initialize the pools");
@@ -1488,7 +1492,7 @@ int main(void)
        /*
         * Register ourselves with the kernel.
         */
-       message = (struct cn_msg *)kvp_send_buffer;
+       message = (struct cn_msg *)kvp_recv_buffer;
        message->id.idx = CN_KVP_IDX;
        message->id.val = CN_KVP_VAL;
 
@@ -1671,8 +1675,7 @@ int main(void)
 
                switch (hv_msg->body.kvp_enum_data.index) {
                case FullyQualifiedDomainName:
-                       kvp_get_domain_name(key_value,
-                                       HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
+                       strcpy(key_value, full_domain_name);
                        strcpy(key_name, "FullyQualifiedDomainName");
                        break;
                case IntegrationServicesVersion:
index 8611962c672c70dd282443a59cda62f10523ac0f..8bcb04096eb267a1f6d49e672720661e779ec5c3 100644 (file)
@@ -140,7 +140,6 @@ int main(void)
        struct cn_msg   *incoming_cn_msg;
        int     op;
        struct hv_vss_msg *vss_msg;
-       char *vss_send_buffer;
        char *vss_recv_buffer;
        size_t vss_recv_buffer_len;
 
@@ -150,10 +149,9 @@ int main(void)
        openlog("Hyper-V VSS", 0, LOG_USER);
        syslog(LOG_INFO, "VSS starting; pid is:%d", getpid());
 
-       vss_recv_buffer_len = NLMSG_HDRLEN + sizeof(struct cn_msg) + sizeof(struct hv_vss_msg);
-       vss_send_buffer = calloc(1, vss_recv_buffer_len);
+       vss_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_vss_msg);
        vss_recv_buffer = calloc(1, vss_recv_buffer_len);
-       if (!(vss_send_buffer && vss_recv_buffer)) {
+       if (!vss_recv_buffer) {
                syslog(LOG_ERR, "Failed to allocate netlink buffers");
                exit(EXIT_FAILURE);
        }
@@ -185,7 +183,7 @@ int main(void)
        /*
         * Register ourselves with the kernel.
         */
-       message = (struct cn_msg *)vss_send_buffer;
+       message = (struct cn_msg *)vss_recv_buffer;
        message->id.idx = CN_VSS_IDX;
        message->id.val = CN_VSS_VAL;
        message->ack = 0;