forked from Minki/linux
Char/Misc patches for 3.13-rc1
Here's the big char/misc driver patchset for 3.13-rc1. Lots of stuff in here, including some new drivers for Intel's "MIC" co-processor devices, and a new eeprom driver. Other things include the driver attribute cleanups, extcon driver updates, hyperv updates, and a raft of other miscellaneous driver fixes. All of these have been in linux-next for a while. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.19 (GNU/Linux) iEUEABECAAYFAlJ6v9kACgkQMUfUDdst+ykPzACXdwm/1DryfqnyhVPyITNAKcma WACg1Yu5mtIvJg3NsN/7Ff0Qfj6GzYY= =MIEe -----END PGP SIGNATURE----- Merge tag 'char-misc-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc Pull char/misc patches from Greg KH: "Here's the big char/misc driver patchset for 3.13-rc1. Lots of stuff in here, including some new drivers for Intel's "MIC" co-processor devices, and a new eeprom driver. Other things include the driver attribute cleanups, extcon driver updates, hyperv updates, and a raft of other miscellaneous driver fixes. All of these have been in linux-next for a while" * tag 'char-misc-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (121 commits) misc: mic: Fixes for randconfig build errors and warnings. tifm: fix error return code in tifm_7xx1_probe() w1-gpio: Use devm_* functions w1-gpio: Detect of_gpio_error for first gpio uio: Pass pointers to virt_to_page(), not integers uio: fix memory leak misc/at24: avoid infinite loop on write() misc/93xx46: avoid infinite loop on write() misc: atmel_pwm: add deferred-probing support mei: wd: host_init propagate error codes from called functions mei: replace stray pr_debug with dev_dbg mei: bus: propagate error code returned by mei_me_cl_by_id mei: mei_cl_link remove duplicated check for open_handle_count mei: print correct device state during unexpected reset mei: nfc: fix memory leak in error path lkdtm: add tests for additional page permissions lkdtm: adjust recursion size to avoid warnings lkdtm: isolate stack corruption test mei: move host_clients_map cleanup to device init mei: me: downgrade two errors to debug level ...
This commit is contained in:
commit
1071ec7bc2
157
Documentation/ABI/testing/sysfs-class-mic.txt
Normal file
157
Documentation/ABI/testing/sysfs-class-mic.txt
Normal file
@ -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.
|
22
Documentation/ABI/testing/sysfs-driver-sunxi-sid
Normal file
22
Documentation/ABI/testing/sysfs-driver-sunxi-sid
Normal file
@ -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.
|
@ -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>
|
||||
};
|
20
Documentation/devicetree/bindings/misc/ti,dac7512.txt
Normal file
20
Documentation/devicetree/bindings/misc/ti,dac7512.txt
Normal file
@ -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>;
|
||||
};
|
||||
};
|
||||
|
@ -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)
|
||||
|
51
Documentation/mic/mic_overview.txt
Normal file
51
Documentation/mic/mic_overview.txt
Normal file
@ -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 |
|
||||
+-------------------------------------------------------------+
|
1
Documentation/mic/mpssd/.gitignore
vendored
Normal file
1
Documentation/mic/mpssd/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
mpssd
|
19
Documentation/mic/mpssd/Makefile
Normal file
19
Documentation/mic/mpssd/Makefile
Normal file
@ -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
|
173
Documentation/mic/mpssd/micctrl
Executable file
173
Documentation/mic/mpssd/micctrl
Executable file
@ -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 $?
|
202
Documentation/mic/mpssd/mpss
Executable file
202
Documentation/mic/mpssd/mpss
Executable file
@ -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 $?
|
1721
Documentation/mic/mpssd/mpssd.c
Normal file
1721
Documentation/mic/mpssd/mpssd.c
Normal file
File diff suppressed because it is too large
Load Diff
102
Documentation/mic/mpssd/mpssd.h
Normal file
102
Documentation/mic/mpssd/mpssd.h
Normal file
@ -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
|
102
Documentation/mic/mpssd/sysfs.c
Normal file
102
Documentation/mic/mpssd/sysfs.c
Normal file
@ -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;
|
||||
}
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -27,16 +27,16 @@
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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]);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,52 +54,6 @@ static int vmbus_exists(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void get_channel_info(struct hv_device *device,
|
||||
struct hv_device_info *info)
|
||||
{
|
||||
struct vmbus_channel_debug_info debug_info;
|
||||
|
||||
if (!device->channel)
|
||||
return;
|
||||
|
||||
vmbus_get_debug_info(device->channel, &debug_info);
|
||||
|
||||
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));
|
||||
|
||||
info->monitor_id = debug_info.monitorid;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
@ -126,115 +62,353 @@ static void print_alias_name(struct hv_device *hv_dev, char *alias_name)
|
||||
sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 u8 channel_monitor_group(struct vmbus_channel *channel)
|
||||
{
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
kfree(device_info);
|
||||
return ret;
|
||||
return (u8)channel->offermsg.monitorid / 32;
|
||||
}
|
||||
|
||||
static u8 channel_monitor_offset(struct vmbus_channel *channel)
|
||||
{
|
||||
return (u8)channel->offermsg.monitorid % 32;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (!hv_dev->channel)
|
||||
return -ENODEV;
|
||||
return sprintf(buf, "%d\n", hv_dev->channel->offermsg.child_relid);
|
||||
}
|
||||
static DEVICE_ATTR_RO(id);
|
||||
|
||||
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);
|
||||
|
||||
if (!hv_dev->channel)
|
||||
return -ENODEV;
|
||||
return sprintf(buf, "%d\n", hv_dev->channel->state);
|
||||
}
|
||||
static DEVICE_ATTR_RO(state);
|
||||
|
||||
static ssize_t monitor_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", hv_dev->channel->offermsg.monitorid);
|
||||
}
|
||||
static DEVICE_ATTR_RO(monitor_id);
|
||||
|
||||
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);
|
||||
char alias_name[VMBUS_ALIAS_LEN + 1];
|
||||
|
||||
print_alias_name(hv_dev, alias_name);
|
||||
return sprintf(buf, "vmbus:%s\n", alias_name);
|
||||
}
|
||||
static DEVICE_ATTR_RO(modalias);
|
||||
|
||||
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);
|
||||
|
||||
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";
|
||||
|
@ -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
|
||||
|
@ -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/
|
||||
|
@ -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;
|
||||
|
@ -90,8 +90,10 @@ int pwm_channel_alloc(int index, struct pwm_channel *ch)
|
||||
unsigned long flags;
|
||||
int status = 0;
|
||||
|
||||
/* insist on PWM init, with this signal pinned out */
|
||||
if (!pwm || !(pwm->mask & 1 << index))
|
||||
if (!pwm)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (!(pwm->mask & 1 << index))
|
||||
return -ENODEV;
|
||||
|
||||
if (index < 0 || index >= PWM_NCHAN || !ch)
|
||||
|
@ -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),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -428,6 +428,9 @@ static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj,
|
||||
{
|
||||
struct at24_data *at24;
|
||||
|
||||
if (unlikely(off >= attr->size))
|
||||
return -EFBIG;
|
||||
|
||||
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
|
||||
return at24_write(at24, buf, off, count);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -202,7 +202,7 @@ eeprom_93xx46_bin_write(struct file *filp, struct kobject *kobj,
|
||||
edev = dev_get_drvdata(dev);
|
||||
|
||||
if (unlikely(off >= edev->bin.size))
|
||||
return 0;
|
||||
return -EFBIG;
|
||||
if ((off + count) > edev->bin.size)
|
||||
count = edev->bin.size - off;
|
||||
if (unlikely(!count))
|
||||
|
158
drivers/misc/eeprom/sunxi_sid.c
Normal file
158
drivers/misc/eeprom/sunxi_sid.c
Normal file
@ -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");
|
@ -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);
|
||||
|
@ -44,13 +44,25 @@
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mman.h>
|
||||
|
||||
#ifdef CONFIG_IDE
|
||||
#include <linux/ide.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Make sure our attempts to over run the kernel stack doesn't trigger
|
||||
* a compiler warning when CONFIG_FRAME_WARN is set. Then make sure we
|
||||
* recurse past the end of THREAD_SIZE by default.
|
||||
*/
|
||||
#if defined(CONFIG_FRAME_WARN) && (CONFIG_FRAME_WARN > 0)
|
||||
#define REC_STACK_SIZE (CONFIG_FRAME_WARN / 2)
|
||||
#else
|
||||
#define REC_STACK_SIZE (THREAD_SIZE / 8)
|
||||
#endif
|
||||
#define REC_NUM_DEFAULT ((THREAD_SIZE / REC_STACK_SIZE) * 2)
|
||||
|
||||
#define DEFAULT_COUNT 10
|
||||
#define REC_NUM_DEFAULT 10
|
||||
#define EXEC_SIZE 64
|
||||
|
||||
enum cname {
|
||||
@ -86,6 +98,9 @@ enum ctype {
|
||||
CT_EXEC_STACK,
|
||||
CT_EXEC_KMALLOC,
|
||||
CT_EXEC_VMALLOC,
|
||||
CT_EXEC_USERSPACE,
|
||||
CT_ACCESS_USERSPACE,
|
||||
CT_WRITE_RO,
|
||||
};
|
||||
|
||||
static char* cp_name[] = {
|
||||
@ -119,6 +134,9 @@ static char* cp_type[] = {
|
||||
"EXEC_STACK",
|
||||
"EXEC_KMALLOC",
|
||||
"EXEC_VMALLOC",
|
||||
"EXEC_USERSPACE",
|
||||
"ACCESS_USERSPACE",
|
||||
"WRITE_RO",
|
||||
};
|
||||
|
||||
static struct jprobe lkdtm;
|
||||
@ -139,9 +157,10 @@ static DEFINE_SPINLOCK(lock_me_up);
|
||||
|
||||
static u8 data_area[EXEC_SIZE];
|
||||
|
||||
static const unsigned long rodata = 0xAA55AA55;
|
||||
|
||||
module_param(recur_count, int, 0644);
|
||||
MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test, "\
|
||||
"default is 10");
|
||||
MODULE_PARM_DESC(recur_count, " Recursion level for the stack overflow test");
|
||||
module_param(cpoint_name, charp, 0444);
|
||||
MODULE_PARM_DESC(cpoint_name, " Crash Point, where kernel is to be crashed");
|
||||
module_param(cpoint_type, charp, 0444);
|
||||
@ -280,16 +299,16 @@ static int lkdtm_parse_commandline(void)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int recursive_loop(int a)
|
||||
static int recursive_loop(int remaining)
|
||||
{
|
||||
char buf[1024];
|
||||
char buf[REC_STACK_SIZE];
|
||||
|
||||
memset(buf,0xFF,1024);
|
||||
recur_count--;
|
||||
if (!recur_count)
|
||||
/* Make sure compiler does not optimize this away. */
|
||||
memset(buf, (remaining & 0xff) | 0x1, REC_STACK_SIZE);
|
||||
if (!remaining)
|
||||
return 0;
|
||||
else
|
||||
return recursive_loop(a);
|
||||
return recursive_loop(remaining - 1);
|
||||
}
|
||||
|
||||
static void do_nothing(void)
|
||||
@ -297,6 +316,14 @@ static void do_nothing(void)
|
||||
return;
|
||||
}
|
||||
|
||||
static noinline void corrupt_stack(void)
|
||||
{
|
||||
/* Use default char array length that triggers stack protection. */
|
||||
char data[8];
|
||||
|
||||
memset((void *)data, 0, 64);
|
||||
}
|
||||
|
||||
static void execute_location(void *dst)
|
||||
{
|
||||
void (*func)(void) = dst;
|
||||
@ -305,6 +332,15 @@ static void execute_location(void *dst)
|
||||
func();
|
||||
}
|
||||
|
||||
static void execute_user_location(void *dst)
|
||||
{
|
||||
void (*func)(void) = dst;
|
||||
|
||||
if (copy_to_user(dst, do_nothing, EXEC_SIZE))
|
||||
return;
|
||||
func();
|
||||
}
|
||||
|
||||
static void lkdtm_do_action(enum ctype which)
|
||||
{
|
||||
switch (which) {
|
||||
@ -325,15 +361,11 @@ static void lkdtm_do_action(enum ctype which)
|
||||
;
|
||||
break;
|
||||
case CT_OVERFLOW:
|
||||
(void) recursive_loop(0);
|
||||
(void) recursive_loop(recur_count);
|
||||
break;
|
||||
case CT_CORRUPT_STACK: {
|
||||
/* Make sure the compiler creates and uses an 8 char array. */
|
||||
volatile char data[8];
|
||||
|
||||
memset((void *)data, 0, 64);
|
||||
case CT_CORRUPT_STACK:
|
||||
corrupt_stack();
|
||||
break;
|
||||
}
|
||||
case CT_UNALIGNED_LOAD_STORE_WRITE: {
|
||||
static u8 data[5] __attribute__((aligned(4))) = {1, 2,
|
||||
3, 4, 5};
|
||||
@ -401,6 +433,49 @@ static void lkdtm_do_action(enum ctype which)
|
||||
vfree(vmalloc_area);
|
||||
break;
|
||||
}
|
||||
case CT_EXEC_USERSPACE: {
|
||||
unsigned long user_addr;
|
||||
|
||||
user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, 0);
|
||||
if (user_addr >= TASK_SIZE) {
|
||||
pr_warn("Failed to allocate user memory\n");
|
||||
return;
|
||||
}
|
||||
execute_user_location((void *)user_addr);
|
||||
vm_munmap(user_addr, PAGE_SIZE);
|
||||
break;
|
||||
}
|
||||
case CT_ACCESS_USERSPACE: {
|
||||
unsigned long user_addr, tmp;
|
||||
unsigned long *ptr;
|
||||
|
||||
user_addr = vm_mmap(NULL, 0, PAGE_SIZE,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, 0);
|
||||
if (user_addr >= TASK_SIZE) {
|
||||
pr_warn("Failed to allocate user memory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = (unsigned long *)user_addr;
|
||||
tmp = *ptr;
|
||||
tmp += 0xc0dec0de;
|
||||
*ptr = tmp;
|
||||
|
||||
vm_munmap(user_addr, PAGE_SIZE);
|
||||
|
||||
break;
|
||||
}
|
||||
case CT_WRITE_RO: {
|
||||
unsigned long *ptr;
|
||||
|
||||
ptr = (unsigned long *)&rodata;
|
||||
*ptr ^= 0xabcd1234;
|
||||
|
||||
break;
|
||||
}
|
||||
case CT_NONE:
|
||||
default:
|
||||
break;
|
||||
|
@ -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) {
|
||||
|
@ -245,7 +245,7 @@ static int ___mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length,
|
||||
/* Check if we have an ME client device */
|
||||
id = mei_me_cl_by_id(dev, cl->me_client_id);
|
||||
if (id < 0)
|
||||
return -ENODEV;
|
||||
return id;
|
||||
|
||||
if (length > dev->me_clients[id].props.max_msg_length)
|
||||
return -EINVAL;
|
||||
|
@ -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,7 +289,14 @@ 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 -ENOENT;
|
||||
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;
|
||||
}
|
||||
|
||||
dev->open_handle_count++;
|
||||
@ -296,7 +308,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 +320,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 +331,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 +359,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 +397,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 +411,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 +430,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 +647,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 +671,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 +699,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 +744,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 +761,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 +796,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 +829,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 +923,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);
|
||||
}
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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;
|
||||
|
@ -110,6 +110,7 @@
|
||||
#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
|
||||
|
@ -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);
|
||||
|
||||
@ -139,6 +147,10 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
|
||||
dev->dev_state != MEI_DEV_POWER_DOWN &&
|
||||
dev->dev_state != MEI_DEV_POWER_UP);
|
||||
|
||||
if (unexpected)
|
||||
dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
|
||||
mei_dev_state_str(dev->dev_state));
|
||||
|
||||
ret = mei_hw_reset(dev, interrupts_enabled);
|
||||
if (ret) {
|
||||
dev_err(&dev->pdev->dev, "hw reset failed disabling the device\n");
|
||||
@ -165,12 +177,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));
|
||||
}
|
||||
@ -182,10 +189,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
|
||||
dev->rd_msg_hdr = 0;
|
||||
dev->wd_pending = false;
|
||||
|
||||
if (unexpected)
|
||||
dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
|
||||
mei_dev_state_str(dev->dev_state));
|
||||
|
||||
if (!interrupts_enabled) {
|
||||
dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n");
|
||||
return;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -485,8 +485,11 @@ int mei_nfc_host_init(struct mei_device *dev)
|
||||
if (ndev->cl_info)
|
||||
return 0;
|
||||
|
||||
cl_info = mei_cl_allocate(dev);
|
||||
cl = mei_cl_allocate(dev);
|
||||
ndev->cl_info = mei_cl_allocate(dev);
|
||||
ndev->cl = mei_cl_allocate(dev);
|
||||
|
||||
cl = ndev->cl;
|
||||
cl_info = ndev->cl_info;
|
||||
|
||||
if (!cl || !cl_info) {
|
||||
ret = -ENOMEM;
|
||||
@ -527,10 +530,9 @@ int mei_nfc_host_init(struct mei_device *dev)
|
||||
|
||||
cl->device_uuid = mei_nfc_guid;
|
||||
|
||||
|
||||
list_add_tail(&cl->device_link, &dev->device_list);
|
||||
|
||||
ndev->cl_info = cl_info;
|
||||
ndev->cl = cl;
|
||||
ndev->req_id = 1;
|
||||
|
||||
INIT_WORK(&ndev->init_work, mei_nfc_init);
|
||||
|
@ -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 */
|
||||
@ -189,7 +190,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
|
||||
schedule_delayed_work(&dev->timer_work, HZ);
|
||||
|
||||
pr_debug("initialization successful.\n");
|
||||
dev_dbg(&pdev->dev, "initialization successful.\n");
|
||||
|
||||
return 0;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
@ -60,7 +60,7 @@ static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout)
|
||||
int mei_wd_host_init(struct mei_device *dev)
|
||||
{
|
||||
struct mei_cl *cl = &dev->wd_cl;
|
||||
int i;
|
||||
int id;
|
||||
int ret;
|
||||
|
||||
mei_cl_init(cl, dev);
|
||||
@ -70,19 +70,19 @@ int mei_wd_host_init(struct mei_device *dev)
|
||||
|
||||
|
||||
/* check for valid client id */
|
||||
i = mei_me_cl_by_uuid(dev, &mei_wd_guid);
|
||||
if (i < 0) {
|
||||
id = mei_me_cl_by_uuid(dev, &mei_wd_guid);
|
||||
if (id < 0) {
|
||||
dev_info(&dev->pdev->dev, "wd: failed to find the client\n");
|
||||
return -ENOENT;
|
||||
return id;
|
||||
}
|
||||
|
||||
cl->me_client_id = dev->me_clients[i].client_id;
|
||||
cl->me_client_id = dev->me_clients[id].client_id;
|
||||
|
||||
ret = mei_cl_link(cl, MEI_WD_HOST_CLIENT_ID);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_info(&dev->pdev->dev, "wd: failed link client\n");
|
||||
return -ENOENT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
cl->state = MEI_FILE_CONNECTING;
|
||||
|
39
drivers/misc/mic/Kconfig
Normal file
39
drivers/misc/mic/Kconfig
Normal file
@ -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>.
|
6
drivers/misc/mic/Makefile
Normal file
6
drivers/misc/mic/Makefile
Normal file
@ -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/
|
11
drivers/misc/mic/card/Makefile
Normal file
11
drivers/misc/mic/card/Makefile
Normal file
@ -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
|
130
drivers/misc/mic/card/mic_debugfs.c
Normal file
130
drivers/misc/mic/card/mic_debugfs.c
Normal file
@ -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);
|
||||
}
|
305
drivers/misc/mic/card/mic_device.c
Normal file
305
drivers/misc/mic/card/mic_device.c
Normal file
@ -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);
|
||||
}
|
133
drivers/misc/mic/card/mic_device.h
Normal file
133
drivers/misc/mic/card/mic_device.h
Normal file
@ -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
|
630
drivers/misc/mic/card/mic_virtio.c
Normal file
630
drivers/misc/mic/card/mic_virtio.c
Normal file
@ -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);
|
||||
}
|
77
drivers/misc/mic/card/mic_virtio.h
Normal file
77
drivers/misc/mic/card/mic_virtio.h
Normal file
@ -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
|
256
drivers/misc/mic/card/mic_x100.c
Normal file
256
drivers/misc/mic/card/mic_x100.c
Normal file
@ -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");
|
48
drivers/misc/mic/card/mic_x100.h
Normal file
48
drivers/misc/mic/card/mic_x100.h
Normal file
@ -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
|
51
drivers/misc/mic/common/mic_dev.h
Normal file
51
drivers/misc/mic/common/mic_dev.h
Normal file
@ -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
|
14
drivers/misc/mic/host/Makefile
Normal file
14
drivers/misc/mic/host/Makefile
Normal file
@ -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
|
300
drivers/misc/mic/host/mic_boot.c
Normal file
300
drivers/misc/mic/host/mic_boot.c
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* 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/pci.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);
|
||||
}
|
491
drivers/misc/mic/host/mic_debugfs.c
Normal file
491
drivers/misc/mic/host/mic_debugfs.c
Normal file
@ -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);
|
||||
}
|
203
drivers/misc/mic/host/mic_device.h
Normal file
203
drivers/misc/mic/host/mic_device.h
Normal file
@ -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
|
222
drivers/misc/mic/host/mic_fops.c
Normal file
222
drivers/misc/mic/host/mic_fops.c
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* 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/pci.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(©, 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, ©);
|
||||
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,
|
||||
©.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;
|
||||
}
|
32
drivers/misc/mic/host/mic_fops.h
Normal file
32
drivers/misc/mic/host/mic_fops.h
Normal file
@ -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
|
630
drivers/misc/mic/host/mic_intr.c
Normal file
630
drivers/misc/mic/host/mic_intr.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
137
drivers/misc/mic/host/mic_intr.h
Normal file
137
drivers/misc/mic/host/mic_intr.h
Normal file
@ -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
|
537
drivers/misc/mic/host/mic_main.c
Normal file
537
drivers/misc/mic/host/mic_main.c
Normal file
@ -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");
|
442
drivers/misc/mic/host/mic_smpt.c
Normal file
442
drivers/misc/mic/host/mic_smpt.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
98
drivers/misc/mic/host/mic_smpt.h
Normal file
98
drivers/misc/mic/host/mic_smpt.h
Normal file
@ -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
|
459
drivers/misc/mic/host/mic_sysfs.c
Normal file
459
drivers/misc/mic/host/mic_sysfs.c
Normal file
@ -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;
|
||||
}
|
700
drivers/misc/mic/host/mic_virtio.c
Normal file
700
drivers/misc/mic/host/mic_virtio.c
Normal file
@ -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 = NULL;
|
||||
struct mic_vqconfig *vqconfig;
|
||||
int vr_size, i, j, ret;
|
||||
u8 type = 0;
|
||||
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);
|
||||
}
|
138
drivers/misc/mic/host/mic_virtio.h
Normal file
138
drivers/misc/mic/host/mic_virtio.h
Normal file
@ -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
|
570
drivers/misc/mic/host/mic_x100.c
Normal file
570
drivers/misc/mic/host/mic_x100.c
Normal file
@ -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, ®);
|
||||
else
|
||||
__clear_bit(offset, ®);
|
||||
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,
|
||||
};
|
98
drivers/misc/mic/host/mic_x100.h
Normal file
98
drivers/misc/mic/host/mic_x100.h
Normal file
@ -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
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -356,8 +356,10 @@ static int tifm_7xx1_probe(struct pci_dev *dev,
|
||||
pci_set_drvdata(dev, fm);
|
||||
|
||||
fm->addr = pci_ioremap_bar(dev, 0);
|
||||
if (!fm->addr)
|
||||
if (!fm->addr) {
|
||||
rc = -ENODEV;
|
||||
goto err_out_free;
|
||||
}
|
||||
|
||||
rc = request_irq(dev->irq, tifm_7xx1_isr, IRQF_SHARED, DRIVER_NAME, fm);
|
||||
if (rc)
|
||||
@ -378,7 +380,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 +406,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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -243,11 +243,7 @@ static int vmci_host_setup_notify(struct vmci_ctx *context,
|
||||
/*
|
||||
* Lock physical page backing a given user VA.
|
||||
*/
|
||||
down_read(¤t->mm->mmap_sem);
|
||||
retval = get_user_pages(current, current->mm,
|
||||
PAGE_ALIGN(uva),
|
||||
1, 1, 0, &page, NULL);
|
||||
up_read(¤t->mm->mmap_sem);
|
||||
retval = get_user_pages_fast(PAGE_ALIGN(uva), 1, 1, &page);
|
||||
if (retval != 1)
|
||||
return VMCI_ERROR_GENERIC;
|
||||
|
||||
|
@ -732,13 +732,9 @@ static int qp_host_get_user_memory(u64 produce_uva,
|
||||
int retval;
|
||||
int err = VMCI_SUCCESS;
|
||||
|
||||
down_write(¤t->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(¤t->mm->mmap_sem);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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(¥ta_cardbus_driver);
|
||||
}
|
||||
|
||||
|
||||
static void __exit yenta_socket_exit(void)
|
||||
{
|
||||
pci_unregister_driver(¥ta_cardbus_driver);
|
||||
}
|
||||
|
||||
|
||||
module_init(yenta_socket_init);
|
||||
module_exit(yenta_socket_exit);
|
||||
module_pci_driver(yenta_cardbus_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -288,13 +288,13 @@ static int uio_dev_add_attributes(struct uio_device *idev)
|
||||
}
|
||||
map = kzalloc(sizeof(*map), GFP_KERNEL);
|
||||
if (!map)
|
||||
goto err_map;
|
||||
goto err_map_kobj;
|
||||
kobject_init(&map->kobj, &map_attr_type);
|
||||
map->mem = mem;
|
||||
mem->map = map;
|
||||
ret = kobject_add(&map->kobj, idev->map_dir, "map%d", mi);
|
||||
if (ret)
|
||||
goto err_map;
|
||||
goto err_map_kobj;
|
||||
ret = kobject_uevent(&map->kobj, KOBJ_ADD);
|
||||
if (ret)
|
||||
goto err_map;
|
||||
@ -313,14 +313,14 @@ static int uio_dev_add_attributes(struct uio_device *idev)
|
||||
}
|
||||
portio = kzalloc(sizeof(*portio), GFP_KERNEL);
|
||||
if (!portio)
|
||||
goto err_portio;
|
||||
goto err_portio_kobj;
|
||||
kobject_init(&portio->kobj, &portio_attr_type);
|
||||
portio->port = port;
|
||||
port->portio = portio;
|
||||
ret = kobject_add(&portio->kobj, idev->portio_dir,
|
||||
"port%d", pi);
|
||||
if (ret)
|
||||
goto err_portio;
|
||||
goto err_portio_kobj;
|
||||
ret = kobject_uevent(&portio->kobj, KOBJ_ADD);
|
||||
if (ret)
|
||||
goto err_portio;
|
||||
@ -329,14 +329,18 @@ static int uio_dev_add_attributes(struct uio_device *idev)
|
||||
return 0;
|
||||
|
||||
err_portio:
|
||||
for (pi--; pi >= 0; pi--) {
|
||||
pi--;
|
||||
err_portio_kobj:
|
||||
for (; pi >= 0; pi--) {
|
||||
port = &idev->info->port[pi];
|
||||
portio = port->portio;
|
||||
kobject_put(&portio->kobj);
|
||||
}
|
||||
kobject_put(idev->portio_dir);
|
||||
err_map:
|
||||
for (mi--; mi>=0; mi--) {
|
||||
mi--;
|
||||
err_map_kobj:
|
||||
for (; mi >= 0; mi--) {
|
||||
mem = &idev->info->mem[mi];
|
||||
map = mem->map;
|
||||
kobject_put(&map->kobj);
|
||||
@ -601,6 +605,7 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||
struct uio_device *idev = vma->vm_private_data;
|
||||
struct page *page;
|
||||
unsigned long offset;
|
||||
void *addr;
|
||||
|
||||
int mi = uio_find_mem_index(vma);
|
||||
if (mi < 0)
|
||||
@ -612,10 +617,11 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||
*/
|
||||
offset = (vmf->pgoff - mi) << PAGE_SHIFT;
|
||||
|
||||
addr = (void *)(unsigned long)idev->info->mem[mi].addr + offset;
|
||||
if (idev->info->mem[mi].memtype == UIO_MEM_LOGICAL)
|
||||
page = virt_to_page(idev->info->mem[mi].addr + offset);
|
||||
page = virt_to_page(addr);
|
||||
else
|
||||
page = vmalloc_to_page((void *)(unsigned long)idev->info->mem[mi].addr + offset);
|
||||
page = vmalloc_to_page(addr);
|
||||
get_page(page);
|
||||
vmf->page = page;
|
||||
return 0;
|
||||
@ -809,10 +815,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;
|
||||
@ -822,7 +827,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,
|
||||
@ -840,7 +845,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;
|
||||
@ -854,9 +859,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);
|
||||
@ -877,13 +879,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;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user