char/misc driver patches for 4.11-rc1

Here is the big char/misc driver patchset for 4.11-rc1.
 
 Lots of different driver subsystems updated here.  Rework for the hyperv
 subsystem to handle new platforms better, mei and w1 and extcon driver
 updates, as well as a number of other "minor" driver updates.  Full
 details are in the shortlog below.
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCWK2iRQ8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ynhFACguVE+/ixj5u5bT5DXQaZNai/6zIAAmgMWwd/t
 YTD2cwsJsGbTT1fY3SUe
 =CiSI
 -----END PGP SIGNATURE-----

Merge tag 'char-misc-4.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc driver updates from Greg KH:
 "Here is the big char/misc driver patchset for 4.11-rc1.

  Lots of different driver subsystems updated here: rework for the
  hyperv subsystem to handle new platforms better, mei and w1 and extcon
  driver updates, as well as a number of other "minor" driver updates.

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

* tag 'char-misc-4.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (169 commits)
  goldfish: Sanitize the broken interrupt handler
  x86/platform/goldfish: Prevent unconditional loading
  vmbus: replace modulus operation with subtraction
  vmbus: constify parameters where possible
  vmbus: expose hv_begin/end_read
  vmbus: remove conditional locking of vmbus_write
  vmbus: add direct isr callback mode
  vmbus: change to per channel tasklet
  vmbus: put related per-cpu variable together
  vmbus: callback is in softirq not workqueue
  binder: Add support for file-descriptor arrays
  binder: Add support for scatter-gather
  binder: Add extra size to allocator
  binder: Refactor binder_transact()
  binder: Support multiple /dev instances
  binder: Deal with contexts in debugfs
  binder: Support multiple context managers
  binder: Split flat_binder_object
  auxdisplay: ht16k33: remove private workqueue
  auxdisplay: ht16k33: rework input device initialization
  ...
This commit is contained in:
Linus Torvalds 2017-02-22 11:38:22 -08:00
commit e30aee9e10
134 changed files with 7042 additions and 3867 deletions

View File

@ -11,7 +11,7 @@ DOCBOOKS := z8530book.xml \
writing_usb_driver.xml networking.xml \
kernel-api.xml filesystems.xml lsm.xml kgdb.xml \
gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
genericirq.xml s390-drivers.xml scsi.xml \
sh.xml regulator.xml w1.xml \
writing_musb_glue_layer.xml iio.xml

File diff suppressed because it is too large Load Diff

View File

@ -1195,6 +1195,10 @@
When zero, profiling data is discarded and associated
debugfs files are removed at module unload time.
goldfish [X86] Enable the goldfish android emulator platform.
Don't use this when you are not running on the
android emulator
gpt [EFI] Forces disk with valid GPT signature but
invalid Protective MBR to be treated as GPT. If the
primary GPT is corrupted, it enables the backup/alternate

View File

@ -0,0 +1,44 @@
EEPROM / CSR SMBus-slave interface of IDT 89HPESx devices
Required properties:
- compatible : should be "<manufacturer>,<type>"
Basically there is only one manufacturer: idt, but some
compatible devices may be produced in future. Following devices
are supported: 89hpes8nt2, 89hpes12nt3, 89hpes24nt6ag2,
89hpes32nt8ag2, 89hpes32nt8bg2, 89hpes12nt12g2, 89hpes16nt16g2,
89hpes24nt24g2, 89hpes32nt24ag2, 89hpes32nt24bg2;
89hpes12n3, 89hpes12n3a, 89hpes24n3, 89hpes24n3a;
89hpes32h8, 89hpes32h8g2, 89hpes48h12, 89hpes48h12g2,
89hpes48h12ag2, 89hpes16h16, 89hpes22h16, 89hpes22h16g2,
89hpes34h16, 89hpes34h16g2, 89hpes64h16, 89hpes64h16g2,
89hpes64h16ag2;
89hpes12t3g2, 89hpes24t3g2, 89hpes16t4, 89hpes4t4g2,
89hpes10t4g2, 89hpes16t4g2, 89hpes16t4ag2, 89hpes5t5,
89hpes6t5, 89hpes8t5, 89hpes8t5a, 89hpes24t6, 89hpes6t6g2,
89hpes24t6g2, 89hpes16t7, 89hpes32t8, 89hpes32t8g2,
89hpes48t12, 89hpes48t12g2.
- reg : I2C address of the IDT 89HPESx device.
Optionally there can be EEPROM-compatible subnode:
- compatible: There are five EEPROM devices supported: 24c32, 24c64, 24c128,
24c256 and 24c512 differed by size.
- reg: Custom address of EEPROM device (If not specified IDT 89HPESx
(optional) device will try to communicate with EEPROM sited by default
address - 0x50)
- read-only : Parameterless property disables writes to the EEPROM
(optional)
Example:
idt@60 {
compatible = "idt,89hpes32nt8ag2";
reg = <0x74>;
#address-cells = <1>;
#size-cells = <0>;
eeprom@50 {
compatible = "onsemi,24c64";
reg = <0x50>;
read-only;
};
};

View File

@ -1,13 +1,15 @@
Freescale i.MX6 On-Chip OTP Controller (OCOTP) device tree bindings
This binding represents the on-chip eFuse OTP controller found on
i.MX6Q/D, i.MX6DL/S, i.MX6SL, and i.MX6SX SoCs.
i.MX6Q/D, i.MX6DL/S, i.MX6SL, i.MX6SX and i.MX6UL SoCs.
Required properties:
- compatible: should be one of
"fsl,imx6q-ocotp" (i.MX6Q/D/DL/S),
"fsl,imx6sl-ocotp" (i.MX6SL), or
"fsl,imx6sx-ocotp" (i.MX6SX), followed by "syscon".
"fsl,imx6sx-ocotp" (i.MX6SX),
"fsl,imx6ul-ocotp" (i.MX6UL),
followed by "syscon".
- reg: Should contain the register base and length.
- clocks: Should contain a phandle pointing to the gated peripheral clock.

View File

@ -42,6 +42,12 @@ Optional properties in the area nodes:
and in use by another device or devices
- export : indicates that the reserved SRAM area may be accessed outside
of the kernel, e.g. by bootloader or userspace
- protect-exec : Same as 'pool' above but with the additional
constraint that code wil be run from the region and
that the memory is maintained as read-only, executable
during code execution. NOTE: This region must be page
aligned on start and end in order to properly allow
manipulation of the page attributes.
- label : the name for the reserved partition, if omitted, the label
is taken from the node name excluding the unit address.

View File

@ -30,6 +30,7 @@ available subsections can be seen below.
miscellaneous
vme
80211/index
uio-howto
.. only:: subproject and html

View File

@ -0,0 +1,705 @@
=======================
The Userspace I/O HOWTO
=======================
:Author: Hans-Jürgen Koch Linux developer, Linutronix
:Date: 2006-12-11
About this document
===================
Translations
------------
If you know of any translations for this document, or you are interested
in translating it, please email me hjk@hansjkoch.de.
Preface
-------
For many types of devices, creating a Linux kernel driver is overkill.
All that is really needed is some way to handle an interrupt and provide
access to the memory space of the device. The logic of controlling the
device does not necessarily have to be within the kernel, as the device
does not need to take advantage of any of other resources that the
kernel provides. One such common class of devices that are like this are
for industrial I/O cards.
To address this situation, the userspace I/O system (UIO) was designed.
For typical industrial I/O cards, only a very small kernel module is
needed. The main part of the driver will run in user space. This
simplifies development and reduces the risk of serious bugs within a
kernel module.
Please note that UIO is not an universal driver interface. Devices that
are already handled well by other kernel subsystems (like networking or
serial or USB) are no candidates for an UIO driver. Hardware that is
ideally suited for an UIO driver fulfills all of the following:
- The device has memory that can be mapped. The device can be
controlled completely by writing to this memory.
- The device usually generates interrupts.
- The device does not fit into one of the standard kernel subsystems.
Acknowledgments
---------------
I'd like to thank Thomas Gleixner and Benedikt Spranger of Linutronix,
who have not only written most of the UIO code, but also helped greatly
writing this HOWTO by giving me all kinds of background information.
Feedback
--------
Find something wrong with this document? (Or perhaps something right?) I
would love to hear from you. Please email me at hjk@hansjkoch.de.
About UIO
=========
If you use UIO for your card's driver, here's what you get:
- only one small kernel module to write and maintain.
- develop the main part of your driver in user space, with all the
tools and libraries you're used to.
- bugs in your driver won't crash the kernel.
- updates of your driver can take place without recompiling the kernel.
How UIO works
-------------
Each UIO device is accessed through a device file and several sysfs
attribute files. The device file will be called ``/dev/uio0`` for the
first device, and ``/dev/uio1``, ``/dev/uio2`` and so on for subsequent
devices.
``/dev/uioX`` is used to access the address space of the card. Just use
:c:func:`mmap()` to access registers or RAM locations of your card.
Interrupts are handled by reading from ``/dev/uioX``. A blocking
:c:func:`read()` from ``/dev/uioX`` will return as soon as an
interrupt occurs. You can also use :c:func:`select()` on
``/dev/uioX`` to wait for an interrupt. The integer value read from
``/dev/uioX`` represents the total interrupt count. You can use this
number to figure out if you missed some interrupts.
For some hardware that has more than one interrupt source internally,
but not separate IRQ mask and status registers, there might be
situations where userspace cannot determine what the interrupt source
was if the kernel handler disables them by writing to the chip's IRQ
register. In such a case, the kernel has to disable the IRQ completely
to leave the chip's register untouched. Now the userspace part can
determine the cause of the interrupt, but it cannot re-enable
interrupts. Another cornercase is chips where re-enabling interrupts is
a read-modify-write operation to a combined IRQ status/acknowledge
register. This would be racy if a new interrupt occurred simultaneously.
To address these problems, UIO also implements a write() function. It is
normally not used and can be ignored for hardware that has only a single
interrupt source or has separate IRQ mask and status registers. If you
need it, however, a write to ``/dev/uioX`` will call the
:c:func:`irqcontrol()` function implemented by the driver. You have
to write a 32-bit value that is usually either 0 or 1 to disable or
enable interrupts. If a driver does not implement
:c:func:`irqcontrol()`, :c:func:`write()` will return with
``-ENOSYS``.
To handle interrupts properly, your custom kernel module can provide its
own interrupt handler. It will automatically be called by the built-in
handler.
For cards that don't generate interrupts but need to be polled, there is
the possibility to set up a timer that triggers the interrupt handler at
configurable time intervals. This interrupt simulation is done by
calling :c:func:`uio_event_notify()` from the timer's event
handler.
Each driver provides attributes that are used to read or write
variables. These attributes are accessible through sysfs files. A custom
kernel driver module can add its own attributes to the device owned by
the uio driver, but not added to the UIO device itself at this time.
This might change in the future if it would be found to be useful.
The following standard attributes are provided by the UIO framework:
- ``name``: The name of your device. It is recommended to use the name
of your kernel module for this.
- ``version``: A version string defined by your driver. This allows the
user space part of your driver to deal with different versions of the
kernel module.
- ``event``: The total number of interrupts handled by the driver since
the last time the device node was read.
These attributes appear under the ``/sys/class/uio/uioX`` directory.
Please note that this directory might be a symlink, and not a real
directory. Any userspace code that accesses it must be able to handle
this.
Each UIO device can make one or more memory regions available for memory
mapping. This is necessary because some industrial I/O cards require
access to more than one PCI memory region in a driver.
Each mapping has its own directory in sysfs, the first mapping appears
as ``/sys/class/uio/uioX/maps/map0/``. Subsequent mappings create
directories ``map1/``, ``map2/``, and so on. These directories will only
appear if the size of the mapping is not 0.
Each ``mapX/`` directory contains four read-only files that show
attributes of the memory:
- ``name``: A string identifier for this mapping. This is optional, the
string can be empty. Drivers can set this to make it easier for
userspace to find the correct mapping.
- ``addr``: The address of memory that can be mapped.
- ``size``: The size, in bytes, of the memory pointed to by addr.
- ``offset``: The offset, in bytes, that has to be added to the pointer
returned by :c:func:`mmap()` to get to the actual device memory.
This is important if the device's memory is not page aligned.
Remember that pointers returned by :c:func:`mmap()` are always
page aligned, so it is good style to always add this offset.
From userspace, the different mappings are distinguished by adjusting
the ``offset`` parameter of the :c:func:`mmap()` call. To map the
memory of mapping N, you have to use N times the page size as your
offset::
offset = N * getpagesize();
Sometimes there is hardware with memory-like regions that can not be
mapped with the technique described here, but there are still ways to
access them from userspace. The most common example are x86 ioports. On
x86 systems, userspace can access these ioports using
:c:func:`ioperm()`, :c:func:`iopl()`, :c:func:`inb()`,
:c:func:`outb()`, and similar functions.
Since these ioport regions can not be mapped, they will not appear under
``/sys/class/uio/uioX/maps/`` like the normal memory described above.
Without information about the port regions a hardware has to offer, it
becomes difficult for the userspace part of the driver to find out which
ports belong to which UIO device.
To address this situation, the new directory
``/sys/class/uio/uioX/portio/`` was added. It only exists if the driver
wants to pass information about one or more port regions to userspace.
If that is the case, subdirectories named ``port0``, ``port1``, and so
on, will appear underneath ``/sys/class/uio/uioX/portio/``.
Each ``portX/`` directory contains four read-only files that show name,
start, size, and type of the port region:
- ``name``: A string identifier for this port region. The string is
optional and can be empty. Drivers can set it to make it easier for
userspace to find a certain port region.
- ``start``: The first port of this region.
- ``size``: The number of ports in this region.
- ``porttype``: A string describing the type of port.
Writing your own kernel module
==============================
Please have a look at ``uio_cif.c`` as an example. The following
paragraphs explain the different sections of this file.
struct uio_info
---------------
This structure tells the framework the details of your driver, Some of
the members are required, others are optional.
- ``const char *name``: Required. The name of your driver as it will
appear in sysfs. I recommend using the name of your module for this.
- ``const char *version``: Required. This string appears in
``/sys/class/uio/uioX/version``.
- ``struct uio_mem mem[ MAX_UIO_MAPS ]``: Required if you have memory
that can be mapped with :c:func:`mmap()`. For each mapping you
need to fill one of the ``uio_mem`` structures. See the description
below for details.
- ``struct uio_port port[ MAX_UIO_PORTS_REGIONS ]``: Required if you
want to pass information about ioports to userspace. For each port
region you need to fill one of the ``uio_port`` structures. See the
description below for details.
- ``long irq``: Required. If your hardware generates an interrupt, it's
your modules task to determine the irq number during initialization.
If you don't have a hardware generated interrupt but want to trigger
the interrupt handler in some other way, set ``irq`` to
``UIO_IRQ_CUSTOM``. If you had no interrupt at all, you could set
``irq`` to ``UIO_IRQ_NONE``, though this rarely makes sense.
- ``unsigned long irq_flags``: Required if you've set ``irq`` to a
hardware interrupt number. The flags given here will be used in the
call to :c:func:`request_irq()`.
- ``int (*mmap)(struct uio_info *info, struct vm_area_struct *vma)``:
Optional. If you need a special :c:func:`mmap()`
function, you can set it here. If this pointer is not NULL, your
:c:func:`mmap()` will be called instead of the built-in one.
- ``int (*open)(struct uio_info *info, struct inode *inode)``:
Optional. You might want to have your own :c:func:`open()`,
e.g. to enable interrupts only when your device is actually used.
- ``int (*release)(struct uio_info *info, struct inode *inode)``:
Optional. If you define your own :c:func:`open()`, you will
probably also want a custom :c:func:`release()` function.
- ``int (*irqcontrol)(struct uio_info *info, s32 irq_on)``:
Optional. If you need to be able to enable or disable interrupts
from userspace by writing to ``/dev/uioX``, you can implement this
function. The parameter ``irq_on`` will be 0 to disable interrupts
and 1 to enable them.
Usually, your device will have one or more memory regions that can be
mapped to user space. For each region, you have to set up a
``struct uio_mem`` in the ``mem[]`` array. Here's a description of the
fields of ``struct uio_mem``:
- ``const char *name``: Optional. Set this to help identify the memory
region, it will show up in the corresponding sysfs node.
- ``int memtype``: Required if the mapping is used. Set this to
``UIO_MEM_PHYS`` if you you have physical memory on your card to be
mapped. Use ``UIO_MEM_LOGICAL`` for logical memory (e.g. allocated
with :c:func:`kmalloc()`). There's also ``UIO_MEM_VIRTUAL`` for
virtual memory.
- ``phys_addr_t addr``: Required if the mapping is used. Fill in the
address of your memory block. This address is the one that appears in
sysfs.
- ``resource_size_t size``: Fill in the size of the memory block that
``addr`` points to. If ``size`` is zero, the mapping is considered
unused. Note that you *must* initialize ``size`` with zero for all
unused mappings.
- ``void *internal_addr``: If you have to access this memory region
from within your kernel module, you will want to map it internally by
using something like :c:func:`ioremap()`. Addresses returned by
this function cannot be mapped to user space, so you must not store
it in ``addr``. Use ``internal_addr`` instead to remember such an
address.
Please do not touch the ``map`` element of ``struct uio_mem``! It is
used by the UIO framework to set up sysfs files for this mapping. Simply
leave it alone.
Sometimes, your device can have one or more port regions which can not
be mapped to userspace. But if there are other possibilities for
userspace to access these ports, it makes sense to make information
about the ports available in sysfs. For each region, you have to set up
a ``struct uio_port`` in the ``port[]`` array. Here's a description of
the fields of ``struct uio_port``:
- ``char *porttype``: Required. Set this to one of the predefined
constants. Use ``UIO_PORT_X86`` for the ioports found in x86
architectures.
- ``unsigned long start``: Required if the port region is used. Fill in
the number of the first port of this region.
- ``unsigned long size``: Fill in the number of ports in this region.
If ``size`` is zero, the region is considered unused. Note that you
*must* initialize ``size`` with zero for all unused regions.
Please do not touch the ``portio`` element of ``struct uio_port``! It is
used internally by the UIO framework to set up sysfs files for this
region. Simply leave it alone.
Adding an interrupt handler
---------------------------
What you need to do in your interrupt handler depends on your hardware
and on how you want to handle it. You should try to keep the amount of
code in your kernel interrupt handler low. If your hardware requires no
action that you *have* to perform after each interrupt, then your
handler can be empty.
If, on the other hand, your hardware *needs* some action to be performed
after each interrupt, then you *must* do it in your kernel module. Note
that you cannot rely on the userspace part of your driver. Your
userspace program can terminate at any time, possibly leaving your
hardware in a state where proper interrupt handling is still required.
There might also be applications where you want to read data from your
hardware at each interrupt and buffer it in a piece of kernel memory
you've allocated for that purpose. With this technique you could avoid
loss of data if your userspace program misses an interrupt.
A note on shared interrupts: Your driver should support interrupt
sharing whenever this is possible. It is possible if and only if your
driver can detect whether your hardware has triggered the interrupt or
not. This is usually done by looking at an interrupt status register. If
your driver sees that the IRQ bit is actually set, it will perform its
actions, and the handler returns IRQ_HANDLED. If the driver detects
that it was not your hardware that caused the interrupt, it will do
nothing and return IRQ_NONE, allowing the kernel to call the next
possible interrupt handler.
If you decide not to support shared interrupts, your card won't work in
computers with no free interrupts. As this frequently happens on the PC
platform, you can save yourself a lot of trouble by supporting interrupt
sharing.
Using uio_pdrv for platform devices
-----------------------------------
In many cases, UIO drivers for platform devices can be handled in a
generic way. In the same place where you define your
``struct platform_device``, you simply also implement your interrupt
handler and fill your ``struct uio_info``. A pointer to this
``struct uio_info`` is then used as ``platform_data`` for your platform
device.
You also need to set up an array of ``struct resource`` containing
addresses and sizes of your memory mappings. This information is passed
to the driver using the ``.resource`` and ``.num_resources`` elements of
``struct platform_device``.
You now have to set the ``.name`` element of ``struct platform_device``
to ``"uio_pdrv"`` to use the generic UIO platform device driver. This
driver will fill the ``mem[]`` array according to the resources given,
and register the device.
The advantage of this approach is that you only have to edit a file you
need to edit anyway. You do not have to create an extra driver.
Using uio_pdrv_genirq for platform devices
------------------------------------------
Especially in embedded devices, you frequently find chips where the irq
pin is tied to its own dedicated interrupt line. In such cases, where
you can be really sure the interrupt is not shared, we can take the
concept of ``uio_pdrv`` one step further and use a generic interrupt
handler. That's what ``uio_pdrv_genirq`` does.
The setup for this driver is the same as described above for
``uio_pdrv``, except that you do not implement an interrupt handler. The
``.handler`` element of ``struct uio_info`` must remain ``NULL``. The
``.irq_flags`` element must not contain ``IRQF_SHARED``.
You will set the ``.name`` element of ``struct platform_device`` to
``"uio_pdrv_genirq"`` to use this driver.
The generic interrupt handler of ``uio_pdrv_genirq`` will simply disable
the interrupt line using :c:func:`disable_irq_nosync()`. After
doing its work, userspace can reenable the interrupt by writing
0x00000001 to the UIO device file. The driver already implements an
:c:func:`irq_control()` to make this possible, you must not
implement your own.
Using ``uio_pdrv_genirq`` not only saves a few lines of interrupt
handler code. You also do not need to know anything about the chip's
internal registers to create the kernel part of the driver. All you need
to know is the irq number of the pin the chip is connected to.
Using uio_dmem_genirq for platform devices
------------------------------------------
In addition to statically allocated memory ranges, they may also be a
desire to use dynamically allocated regions in a user space driver. In
particular, being able to access memory made available through the
dma-mapping API, may be particularly useful. The ``uio_dmem_genirq``
driver provides a way to accomplish this.
This driver is used in a similar manner to the ``"uio_pdrv_genirq"``
driver with respect to interrupt configuration and handling.
Set the ``.name`` element of ``struct platform_device`` to
``"uio_dmem_genirq"`` to use this driver.
When using this driver, fill in the ``.platform_data`` element of
``struct platform_device``, which is of type
``struct uio_dmem_genirq_pdata`` and which contains the following
elements:
- ``struct uio_info uioinfo``: The same structure used as the
``uio_pdrv_genirq`` platform data
- ``unsigned int *dynamic_region_sizes``: Pointer to list of sizes of
dynamic memory regions to be mapped into user space.
- ``unsigned int num_dynamic_regions``: Number of elements in
``dynamic_region_sizes`` array.
The dynamic regions defined in the platform data will be appended to the
`` mem[] `` array after the platform device resources, which implies
that the total number of static and dynamic memory regions cannot exceed
``MAX_UIO_MAPS``.
The dynamic memory regions will be allocated when the UIO device file,
``/dev/uioX`` is opened. Similar to static memory resources, the memory
region information for dynamic regions is then visible via sysfs at
``/sys/class/uio/uioX/maps/mapY/*``. The dynamic memory regions will be
freed when the UIO device file is closed. When no processes are holding
the device file open, the address returned to userspace is ~0.
Writing a driver in userspace
=============================
Once you have a working kernel module for your hardware, you can write
the userspace part of your driver. You don't need any special libraries,
your driver can be written in any reasonable language, you can use
floating point numbers and so on. In short, you can use all the tools
and libraries you'd normally use for writing a userspace application.
Getting information about your UIO device
-----------------------------------------
Information about all UIO devices is available in sysfs. The first thing
you should do in your driver is check ``name`` and ``version`` to make
sure your talking to the right device and that its kernel driver has the
version you expect.
You should also make sure that the memory mapping you need exists and
has the size you expect.
There is a tool called ``lsuio`` that lists UIO devices and their
attributes. It is available here:
http://www.osadl.org/projects/downloads/UIO/user/
With ``lsuio`` you can quickly check if your kernel module is loaded and
which attributes it exports. Have a look at the manpage for details.
The source code of ``lsuio`` can serve as an example for getting
information about an UIO device. The file ``uio_helper.c`` contains a
lot of functions you could use in your userspace driver code.
mmap() device memory
--------------------
After you made sure you've got the right device with the memory mappings
you need, all you have to do is to call :c:func:`mmap()` to map the
device's memory to userspace.
The parameter ``offset`` of the :c:func:`mmap()` call has a special
meaning for UIO devices: It is used to select which mapping of your
device you want to map. To map the memory of mapping N, you have to use
N times the page size as your offset::
offset = N * getpagesize();
N starts from zero, so if you've got only one memory range to map, set
``offset = 0``. A drawback of this technique is that memory is always
mapped beginning with its start address.
Waiting for interrupts
----------------------
After you successfully mapped your devices memory, you can access it
like an ordinary array. Usually, you will perform some initialization.
After that, your hardware starts working and will generate an interrupt
as soon as it's finished, has some data available, or needs your
attention because an error occurred.
``/dev/uioX`` is a read-only file. A :c:func:`read()` will always
block until an interrupt occurs. There is only one legal value for the
``count`` parameter of :c:func:`read()`, and that is the size of a
signed 32 bit integer (4). Any other value for ``count`` causes
:c:func:`read()` to fail. The signed 32 bit integer read is the
interrupt count of your device. If the value is one more than the value
you read the last time, everything is OK. If the difference is greater
than one, you missed interrupts.
You can also use :c:func:`select()` on ``/dev/uioX``.
Generic PCI UIO driver
======================
The generic driver is a kernel module named uio_pci_generic. It can
work with any device compliant to PCI 2.3 (circa 2002) and any compliant
PCI Express device. Using this, you only need to write the userspace
driver, removing the need to write a hardware-specific kernel module.
Making the driver recognize the device
--------------------------------------
Since the driver does not declare any device ids, it will not get loaded
automatically and will not automatically bind to any devices, you must
load it and allocate id to the driver yourself. For example::
modprobe uio_pci_generic
echo "8086 10f5" > /sys/bus/pci/drivers/uio_pci_generic/new_id
If there already is a hardware specific kernel driver for your device,
the generic driver still won't bind to it, in this case if you want to
use the generic driver (why would you?) you'll have to manually unbind
the hardware specific driver and bind the generic driver, like this::
echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind
echo -n 0000:00:19.0 > /sys/bus/pci/drivers/uio_pci_generic/bind
You can verify that the device has been bound to the driver by looking
for it in sysfs, for example like the following::
ls -l /sys/bus/pci/devices/0000:00:19.0/driver
Which if successful should print::
.../0000:00:19.0/driver -> ../../../bus/pci/drivers/uio_pci_generic
Note that the generic driver will not bind to old PCI 2.2 devices. If
binding the device failed, run the following command::
dmesg
and look in the output for failure reasons.
Things to know about uio_pci_generic
------------------------------------
Interrupts are handled using the Interrupt Disable bit in the PCI
command register and Interrupt Status bit in the PCI status register.
All devices compliant to PCI 2.3 (circa 2002) and all compliant PCI
Express devices should support these bits. uio_pci_generic detects
this support, and won't bind to devices which do not support the
Interrupt Disable Bit in the command register.
On each interrupt, uio_pci_generic sets the Interrupt Disable bit.
This prevents the device from generating further interrupts until the
bit is cleared. The userspace driver should clear this bit before
blocking and waiting for more interrupts.
Writing userspace driver using uio_pci_generic
------------------------------------------------
Userspace driver can use pci sysfs interface, or the libpci library that
wraps it, to talk to the device and to re-enable interrupts by writing
to the command register.
Example code using uio_pci_generic
----------------------------------
Here is some sample userspace driver code using uio_pci_generic::
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main()
{
int uiofd;
int configfd;
int err;
int i;
unsigned icount;
unsigned char command_high;
uiofd = open("/dev/uio0", O_RDONLY);
if (uiofd < 0) {
perror("uio open:");
return errno;
}
configfd = open("/sys/class/uio/uio0/device/config", O_RDWR);
if (configfd < 0) {
perror("config open:");
return errno;
}
/* Read and cache command value */
err = pread(configfd, &command_high, 1, 5);
if (err != 1) {
perror("command config read:");
return errno;
}
command_high &= ~0x4;
for(i = 0;; ++i) {
/* Print out a message, for debugging. */
if (i == 0)
fprintf(stderr, "Started uio test driver.\n");
else
fprintf(stderr, "Interrupts: %d\n", icount);
/****************************************/
/* Here we got an interrupt from the
device. Do something to it. */
/****************************************/
/* Re-enable interrupts. */
err = pwrite(configfd, &command_high, 1, 5);
if (err != 1) {
perror("config write:");
break;
}
/* Wait for next interrupt. */
err = read(uiofd, &icount, 4);
if (err != 4) {
perror("uio read:");
break;
}
}
return errno;
}
Generic Hyper-V UIO driver
==========================
The generic driver is a kernel module named uio_hv_generic. It
supports devices on the Hyper-V VMBus similar to uio_pci_generic on
PCI bus.
Making the driver recognize the device
--------------------------------------
Since the driver does not declare any device GUID's, it will not get
loaded automatically and will not automatically bind to any devices, you
must load it and allocate id to the driver yourself. For example, to use
the network device GUID::
modprobe uio_hv_generic
echo "f8615163-df3e-46c5-913f-f2d2f965ed0e" > /sys/bus/vmbus/drivers/uio_hv_generic/new_id
If there already is a hardware specific kernel driver for the device,
the generic driver still won't bind to it, in this case if you want to
use the generic driver (why would you?) you'll have to manually unbind
the hardware specific driver and bind the generic driver, like this::
echo -n vmbus-ed963694-e847-4b2a-85af-bc9cfc11d6f3 > /sys/bus/vmbus/drivers/hv_netvsc/unbind
echo -n vmbus-ed963694-e847-4b2a-85af-bc9cfc11d6f3 > /sys/bus/vmbus/drivers/uio_hv_generic/bind
You can verify that the device has been bound to the driver by looking
for it in sysfs, for example like the following::
ls -l /sys/bus/vmbus/devices/vmbus-ed963694-e847-4b2a-85af-bc9cfc11d6f3/driver
Which if successful should print::
.../vmbus-ed963694-e847-4b2a-85af-bc9cfc11d6f3/driver -> ../../../bus/vmbus/drivers/uio_hv_generic
Things to know about uio_hv_generic
-----------------------------------
On each interrupt, uio_hv_generic sets the Interrupt Disable bit. This
prevents the device from generating further interrupts until the bit is
cleared. The userspace driver should clear this bit before blocking and
waiting for more interrupts.
Further information
===================
- `OSADL homepage. <http://www.osadl.org>`_
- `Linutronix homepage. <http://www.linutronix.de>`_

View File

@ -0,0 +1,22 @@
Intel INT3496 ACPI device extcon driver documentation
-----------------------------------------------------
The Intel INT3496 ACPI device extcon driver is a driver for ACPI
devices with an acpi-id of INT3496, such as found for example on
Intel Baytrail and Cherrytrail tablets.
This ACPI device describes how the OS can read the id-pin of the devices'
USB-otg port, as well as how it optionally can enable Vbus output on the
otg port and how it can optionally control the muxing of the data pins
between an USB host and an USB peripheral controller.
The ACPI devices exposes this functionality by returning an array with up
to 3 gpio descriptors from its ACPI _CRS (Current Resource Settings) call:
Index 0: The input gpio for the id-pin, this is always present and valid
Index 1: The output gpio for enabling Vbus output from the device to the otg
port, write 1 to enable the Vbus output (this gpio descriptor may
be absent or invalid)
Index 2: The output gpio for muxing of the data pins between the USB host and
the USB peripheral controller, write 1 to mux to the peripheral
controller

View File

@ -22,7 +22,16 @@ To program the FPGA from a file or from a buffer:
struct fpga_image_info *info,
const char *buf, size_t count);
Load the FPGA from an image which exists as a buffer in memory.
Load the FPGA from an image which exists as a contiguous buffer in
memory. Allocating contiguous kernel memory for the buffer should be avoided,
users are encouraged to use the _sg interface instead of this.
int fpga_mgr_buf_load_sg(struct fpga_manager *mgr,
struct fpga_image_info *info,
struct sg_table *sgt);
Load the FPGA from an image from non-contiguous in memory. Callers can
construct a sg_table using alloc_page backed memory.
int fpga_mgr_firmware_load(struct fpga_manager *mgr,
struct fpga_image_info *info,
@ -166,7 +175,7 @@ success or negative error codes otherwise.
The programming sequence is:
1. .write_init
2. .write (may be called once or multiple times)
2. .write or .write_sg (may be called once or multiple times)
3. .write_complete
The .write_init function will prepare the FPGA to receive the image data. The
@ -176,7 +185,11 @@ buffer up at least this much before starting.
The .write function writes a buffer to the FPGA. The buffer may be contain the
whole FPGA image or may be a smaller chunk of an FPGA image. In the latter
case, this function is called multiple times for successive chunks.
case, this function is called multiple times for successive chunks. This interface
is suitable for drivers which use PIO.
The .write_sg version behaves the same as .write except the input is a sg_table
scatter list. This interface is suitable for drivers which use DMA.
The .write_complete function is called after all the image has been written
to put the FPGA into operating mode.

View File

@ -5993,6 +5993,7 @@ S: Maintained
F: arch/x86/include/asm/mshyperv.h
F: arch/x86/include/uapi/asm/hyperv.h
F: arch/x86/kernel/cpu/mshyperv.c
F: arch/x86/hyperv
F: drivers/hid/hid-hyperv.c
F: drivers/hv/
F: drivers/input/serio/hyperv-keyboard.c
@ -13071,7 +13072,7 @@ USERSPACE I/O (UIO)
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
F: Documentation/DocBook/uio-howto.tmpl
F: Documentation/driver-api/uio-howto.rst
F: drivers/uio/
F: include/linux/uio*.h

View File

@ -557,15 +557,7 @@ static struct clk_lookup da850_clks[] = {
CLK("da830-mmc.0", NULL, &mmcsd0_clk),
CLK("da830-mmc.1", NULL, &mmcsd1_clk),
CLK("ti-aemif", NULL, &aemif_clk),
/*
* The only user of this clock is davinci_nand and it get's it through
* con_id. The nand node itself is created from within the aemif
* driver to guarantee that it's probed after the aemif timing
* parameters are configured. of_dev_auxdata is not accessible from
* the aemif driver and can't be passed to of_platform_populate(). For
* that reason we're leaving the dev_id here as NULL.
*/
CLK(NULL, "aemif", &aemif_nand_clk),
CLK("davinci-nand.0", "aemif", &aemif_nand_clk),
CLK("ohci-da8xx", "usb11", &usb11_clk),
CLK("musb-da8xx", "usb20", &usb20_clk),
CLK("spi_davinci.0", NULL, &spi0_clk),

View File

@ -11,6 +11,7 @@
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/irqdomain.h>
#include <linux/platform_data/ti-aemif.h>
#include <asm/mach/arch.h>
@ -18,6 +19,15 @@
#include "cp_intc.h"
#include <mach/da8xx.h>
static struct of_dev_auxdata da850_aemif_auxdata_lookup[] = {
OF_DEV_AUXDATA("ti,davinci-nand", 0x62000000, "davinci-nand.0", NULL),
{}
};
static struct aemif_platform_data aemif_data = {
.dev_lookup = da850_aemif_auxdata_lookup,
};
static struct of_dev_auxdata da850_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("ti,davinci-i2c", 0x01c22000, "i2c_davinci.1", NULL),
OF_DEV_AUXDATA("ti,davinci-i2c", 0x01e28000, "i2c_davinci.2", NULL),
@ -37,7 +47,7 @@ static struct of_dev_auxdata da850_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("ti,davinci-dm6467-emac", 0x01e20000, "davinci_emac.1",
NULL),
OF_DEV_AUXDATA("ti,da830-mcasp-audio", 0x01d00000, "davinci-mcasp.0", NULL),
OF_DEV_AUXDATA("ti,da850-aemif", 0x68000000, "ti-aemif", NULL),
OF_DEV_AUXDATA("ti,da850-aemif", 0x68000000, "ti-aemif", &aemif_data),
OF_DEV_AUXDATA("ti,da850-tilcdc", 0x01e13000, "da8xx_lcdc.0", NULL),
OF_DEV_AUXDATA("ti,da830-ohci", 0x01e25000, "ohci-da8xx", NULL),
OF_DEV_AUXDATA("ti,da830-musb", 0x01e00000, "musb-da8xx", NULL),

View File

@ -7,6 +7,9 @@ obj-$(CONFIG_KVM) += kvm/
# Xen paravirtualization support
obj-$(CONFIG_XEN) += xen/
# Hyper-V paravirtualization support
obj-$(CONFIG_HYPERVISOR_GUEST) += hyperv/
# lguest paravirtualization support
obj-$(CONFIG_LGUEST_GUEST) += lguest/

1
arch/x86/hyperv/Makefile Normal file
View File

@ -0,0 +1 @@
obj-y := hv_init.o

277
arch/x86/hyperv/hv_init.c Normal file
View File

@ -0,0 +1,277 @@
/*
* X86 specific Hyper-V initialization code.
*
* Copyright (C) 2016, Microsoft, Inc.
*
* Author : K. Y. Srinivasan <kys@microsoft.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
* NON INFRINGEMENT. See the GNU General Public License for more
* details.
*
*/
#include <linux/types.h>
#include <asm/hypervisor.h>
#include <asm/hyperv.h>
#include <asm/mshyperv.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/clockchips.h>
#ifdef CONFIG_X86_64
static struct ms_hyperv_tsc_page *tsc_pg;
static u64 read_hv_clock_tsc(struct clocksource *arg)
{
u64 current_tick;
if (tsc_pg->tsc_sequence != 0) {
/*
* Use the tsc page to compute the value.
*/
while (1) {
u64 tmp;
u32 sequence = tsc_pg->tsc_sequence;
u64 cur_tsc;
u64 scale = tsc_pg->tsc_scale;
s64 offset = tsc_pg->tsc_offset;
rdtscll(cur_tsc);
/* current_tick = ((cur_tsc *scale) >> 64) + offset */
asm("mulq %3"
: "=d" (current_tick), "=a" (tmp)
: "a" (cur_tsc), "r" (scale));
current_tick += offset;
if (tsc_pg->tsc_sequence == sequence)
return current_tick;
if (tsc_pg->tsc_sequence != 0)
continue;
/*
* Fallback using MSR method.
*/
break;
}
}
rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick);
return current_tick;
}
static struct clocksource hyperv_cs_tsc = {
.name = "hyperv_clocksource_tsc_page",
.rating = 400,
.read = read_hv_clock_tsc,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
#endif
static u64 read_hv_clock_msr(struct clocksource *arg)
{
u64 current_tick;
/*
* Read the partition counter to get the current tick count. This count
* is set to 0 when the partition is created and is incremented in
* 100 nanosecond units.
*/
rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick);
return current_tick;
}
static struct clocksource hyperv_cs_msr = {
.name = "hyperv_clocksource_msr",
.rating = 400,
.read = read_hv_clock_msr,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static void *hypercall_pg;
struct clocksource *hyperv_cs;
EXPORT_SYMBOL_GPL(hyperv_cs);
/*
* This function is to be invoked early in the boot sequence after the
* hypervisor has been detected.
*
* 1. Setup the hypercall page.
* 2. Register Hyper-V specific clocksource.
*/
void hyperv_init(void)
{
u64 guest_id;
union hv_x64_msr_hypercall_contents hypercall_msr;
if (x86_hyper != &x86_hyper_ms_hyperv)
return;
/*
* Setup the hypercall page and enable hypercalls.
* 1. Register the guest ID
* 2. Enable the hypercall and register the hypercall page
*/
guest_id = generate_guest_id(0, LINUX_VERSION_CODE, 0);
wrmsrl(HV_X64_MSR_GUEST_OS_ID, guest_id);
hypercall_pg = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_RX);
if (hypercall_pg == NULL) {
wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
return;
}
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
hypercall_msr.enable = 1;
hypercall_msr.guest_physical_address = vmalloc_to_pfn(hypercall_pg);
wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
/*
* Register Hyper-V specific clocksource.
*/
#ifdef CONFIG_X86_64
if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) {
union hv_x64_msr_hypercall_contents tsc_msr;
tsc_pg = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL);
if (!tsc_pg)
goto register_msr_cs;
hyperv_cs = &hyperv_cs_tsc;
rdmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64);
tsc_msr.enable = 1;
tsc_msr.guest_physical_address = vmalloc_to_pfn(tsc_pg);
wrmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64);
clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100);
return;
}
#endif
/*
* For 32 bit guests just use the MSR based mechanism for reading
* the partition counter.
*/
register_msr_cs:
hyperv_cs = &hyperv_cs_msr;
if (ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE)
clocksource_register_hz(&hyperv_cs_msr, NSEC_PER_SEC/100);
}
/*
* This routine is called before kexec/kdump, it does the required cleanup.
*/
void hyperv_cleanup(void)
{
union hv_x64_msr_hypercall_contents hypercall_msr;
/* Reset our OS id */
wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
/* Reset the hypercall page */
hypercall_msr.as_uint64 = 0;
wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
/* Reset the TSC page */
hypercall_msr.as_uint64 = 0;
wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64);
}
EXPORT_SYMBOL_GPL(hyperv_cleanup);
/*
* hv_do_hypercall- Invoke the specified hypercall
*/
u64 hv_do_hypercall(u64 control, void *input, void *output)
{
u64 input_address = (input) ? virt_to_phys(input) : 0;
u64 output_address = (output) ? virt_to_phys(output) : 0;
#ifdef CONFIG_X86_64
u64 hv_status = 0;
if (!hypercall_pg)
return (u64)ULLONG_MAX;
__asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8");
__asm__ __volatile__("call *%3" : "=a" (hv_status) :
"c" (control), "d" (input_address),
"m" (hypercall_pg));
return hv_status;
#else
u32 control_hi = control >> 32;
u32 control_lo = control & 0xFFFFFFFF;
u32 hv_status_hi = 1;
u32 hv_status_lo = 1;
u32 input_address_hi = input_address >> 32;
u32 input_address_lo = input_address & 0xFFFFFFFF;
u32 output_address_hi = output_address >> 32;
u32 output_address_lo = output_address & 0xFFFFFFFF;
if (!hypercall_pg)
return (u64)ULLONG_MAX;
__asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi),
"=a"(hv_status_lo) : "d" (control_hi),
"a" (control_lo), "b" (input_address_hi),
"c" (input_address_lo), "D"(output_address_hi),
"S"(output_address_lo), "m" (hypercall_pg));
return hv_status_lo | ((u64)hv_status_hi << 32);
#endif /* !x86_64 */
}
EXPORT_SYMBOL_GPL(hv_do_hypercall);
void hyperv_report_panic(struct pt_regs *regs)
{
static bool panic_reported;
/*
* We prefer to report panic on 'die' chain as we have proper
* registers to report, but if we miss it (e.g. on BUG()) we need
* to report it on 'panic'.
*/
if (panic_reported)
return;
panic_reported = true;
wrmsrl(HV_X64_MSR_CRASH_P0, regs->ip);
wrmsrl(HV_X64_MSR_CRASH_P1, regs->ax);
wrmsrl(HV_X64_MSR_CRASH_P2, regs->bx);
wrmsrl(HV_X64_MSR_CRASH_P3, regs->cx);
wrmsrl(HV_X64_MSR_CRASH_P4, regs->dx);
/*
* Let Hyper-V know there is crash data available
*/
wrmsrl(HV_X64_MSR_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY);
}
EXPORT_SYMBOL_GPL(hyperv_report_panic);
bool hv_is_hypercall_page_setup(void)
{
union hv_x64_msr_hypercall_contents hypercall_msr;
/* Check if the hypercall page is setup */
hypercall_msr.as_uint64 = 0;
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
if (!hypercall_msr.enable)
return false;
return true;
}
EXPORT_SYMBOL_GPL(hv_is_hypercall_page_setup);

View File

@ -3,8 +3,28 @@
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/clocksource.h>
#include <asm/hyperv.h>
/*
* The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent
* is set by CPUID(HVCPUID_VERSION_FEATURES).
*/
enum hv_cpuid_function {
HVCPUID_VERSION_FEATURES = 0x00000001,
HVCPUID_VENDOR_MAXFUNCTION = 0x40000000,
HVCPUID_INTERFACE = 0x40000001,
/*
* The remaining functions depend on the value of
* HVCPUID_INTERFACE
*/
HVCPUID_VERSION = 0x40000002,
HVCPUID_FEATURES = 0x40000003,
HVCPUID_ENLIGHTENMENT_INFO = 0x40000004,
HVCPUID_IMPLEMENTATION_LIMITS = 0x40000005,
};
struct ms_hyperv_info {
u32 features;
u32 misc_features;
@ -13,6 +33,128 @@ struct ms_hyperv_info {
extern struct ms_hyperv_info ms_hyperv;
/*
* Declare the MSR used to setup pages used to communicate with the hypervisor.
*/
union hv_x64_msr_hypercall_contents {
u64 as_uint64;
struct {
u64 enable:1;
u64 reserved:11;
u64 guest_physical_address:52;
};
};
/*
* TSC page layout.
*/
struct ms_hyperv_tsc_page {
volatile u32 tsc_sequence;
u32 reserved1;
volatile u64 tsc_scale;
volatile s64 tsc_offset;
u64 reserved2[509];
};
/*
* The guest OS needs to register the guest ID with the hypervisor.
* The guest ID is a 64 bit entity and the structure of this ID is
* specified in the Hyper-V specification:
*
* msdn.microsoft.com/en-us/library/windows/hardware/ff542653%28v=vs.85%29.aspx
*
* While the current guideline does not specify how Linux guest ID(s)
* need to be generated, our plan is to publish the guidelines for
* Linux and other guest operating systems that currently are hosted
* on Hyper-V. The implementation here conforms to this yet
* unpublished guidelines.
*
*
* Bit(s)
* 63 - Indicates if the OS is Open Source or not; 1 is Open Source
* 62:56 - Os Type; Linux is 0x100
* 55:48 - Distro specific identification
* 47:16 - Linux kernel version number
* 15:0 - Distro specific identification
*
*
*/
#define HV_LINUX_VENDOR_ID 0x8100
/*
* Generate the guest ID based on the guideline described above.
*/
static inline __u64 generate_guest_id(__u64 d_info1, __u64 kernel_version,
__u64 d_info2)
{
__u64 guest_id = 0;
guest_id = (((__u64)HV_LINUX_VENDOR_ID) << 48);
guest_id |= (d_info1 << 48);
guest_id |= (kernel_version << 16);
guest_id |= d_info2;
return guest_id;
}
/* Free the message slot and signal end-of-message if required */
static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)
{
/*
* On crash we're reading some other CPU's message page and we need
* to be careful: this other CPU may already had cleared the header
* and the host may already had delivered some other message there.
* In case we blindly write msg->header.message_type we're going
* to lose it. We can still lose a message of the same type but
* we count on the fact that there can only be one
* CHANNELMSG_UNLOAD_RESPONSE and we don't care about other messages
* on crash.
*/
if (cmpxchg(&msg->header.message_type, old_msg_type,
HVMSG_NONE) != old_msg_type)
return;
/*
* Make sure the write to MessageType (ie set to
* HVMSG_NONE) happens before we read the
* MessagePending and EOMing. Otherwise, the EOMing
* will not deliver any more messages since there is
* no empty slot
*/
mb();
if (msg->header.message_flags.msg_pending) {
/*
* This will cause message queue rescan to
* possibly deliver another msg from the
* hypervisor
*/
wrmsrl(HV_X64_MSR_EOM, 0);
}
}
#define hv_get_current_tick(tick) rdmsrl(HV_X64_MSR_TIME_REF_COUNT, tick)
#define hv_init_timer(timer, tick) wrmsrl(timer, tick)
#define hv_init_timer_config(config, val) wrmsrl(config, val)
#define hv_get_simp(val) rdmsrl(HV_X64_MSR_SIMP, val)
#define hv_set_simp(val) wrmsrl(HV_X64_MSR_SIMP, val)
#define hv_get_siefp(val) rdmsrl(HV_X64_MSR_SIEFP, val)
#define hv_set_siefp(val) wrmsrl(HV_X64_MSR_SIEFP, val)
#define hv_get_synic_state(val) rdmsrl(HV_X64_MSR_SCONTROL, val)
#define hv_set_synic_state(val) wrmsrl(HV_X64_MSR_SCONTROL, val)
#define hv_get_vp_index(index) rdmsrl(HV_X64_MSR_VP_INDEX, index)
#define hv_get_synint_state(int_num, val) rdmsrl(int_num, val)
#define hv_set_synint_state(int_num, val) wrmsrl(int_num, val)
void hyperv_callback_vector(void);
#ifdef CONFIG_TRACING
#define trace_hyperv_callback_vector hyperv_callback_vector
@ -25,4 +167,13 @@ void hv_setup_kexec_handler(void (*handler)(void));
void hv_remove_kexec_handler(void);
void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs));
void hv_remove_crash_handler(void);
#if IS_ENABLED(CONFIG_HYPERV)
extern struct clocksource *hyperv_cs;
void hyperv_init(void);
void hyperv_report_panic(struct pt_regs *regs);
bool hv_is_hypercall_page_setup(void);
void hyperv_cleanup(void);
#endif
#endif

View File

@ -73,6 +73,9 @@
*/
#define HV_X64_MSR_STAT_PAGES_AVAILABLE (1 << 8)
/* Crash MSR available */
#define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE (1 << 10)
/*
* Feature identification: EBX indicates which flags were specified at
* partition creation. The format is the same as the partition creation
@ -144,6 +147,11 @@
*/
#define HV_X64_RELAXED_TIMING_RECOMMENDED (1 << 5)
/*
* Crash notification flag.
*/
#define HV_CRASH_CTL_CRASH_NOTIFY (1ULL << 63)
/* MSR used to identify the guest OS. */
#define HV_X64_MSR_GUEST_OS_ID 0x40000000

View File

@ -133,26 +133,6 @@ static uint32_t __init ms_hyperv_platform(void)
return 0;
}
static u64 read_hv_clock(struct clocksource *arg)
{
u64 current_tick;
/*
* Read the partition counter to get the current tick count. This count
* is set to 0 when the partition is created and is incremented in
* 100 nanosecond units.
*/
rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick);
return current_tick;
}
static struct clocksource hyperv_cs = {
.name = "hyperv_clocksource",
.rating = 400, /* use this when running on Hyperv*/
.read = read_hv_clock,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
static unsigned char hv_get_nmi_reason(void)
{
return 0;
@ -180,6 +160,11 @@ static int hv_nmi_unknown(unsigned int val, struct pt_regs *regs)
static void __init ms_hyperv_init_platform(void)
{
int hv_host_info_eax;
int hv_host_info_ebx;
int hv_host_info_ecx;
int hv_host_info_edx;
/*
* Extract the features and hints
*/
@ -190,6 +175,21 @@ static void __init ms_hyperv_init_platform(void)
pr_info("HyperV: features 0x%x, hints 0x%x\n",
ms_hyperv.features, ms_hyperv.hints);
/*
* Extract host information.
*/
if (cpuid_eax(HVCPUID_VENDOR_MAXFUNCTION) >= HVCPUID_VERSION) {
hv_host_info_eax = cpuid_eax(HVCPUID_VERSION);
hv_host_info_ebx = cpuid_ebx(HVCPUID_VERSION);
hv_host_info_ecx = cpuid_ecx(HVCPUID_VERSION);
hv_host_info_edx = cpuid_edx(HVCPUID_VERSION);
pr_info("Hyper-V Host Build:%d-%d.%d-%d-%d.%d\n",
hv_host_info_eax, hv_host_info_ebx >> 16,
hv_host_info_ebx & 0xFFFF, hv_host_info_ecx,
hv_host_info_edx >> 24, hv_host_info_edx & 0xFFFFFF);
}
#ifdef CONFIG_X86_LOCAL_APIC
if (ms_hyperv.features & HV_X64_MSR_APIC_FREQUENCY_AVAILABLE) {
/*
@ -208,9 +208,6 @@ static void __init ms_hyperv_init_platform(void)
"hv_nmi_unknown");
#endif
if (ms_hyperv.features & HV_X64_MSR_TIME_REF_COUNT_AVAILABLE)
clocksource_register_hz(&hyperv_cs, NSEC_PER_SEC/100);
#ifdef CONFIG_X86_IO_APIC
no_timer_check = 1;
#endif
@ -227,6 +224,13 @@ static void __init ms_hyperv_init_platform(void)
*/
if (efi_enabled(EFI_BOOT))
x86_platform.get_nmi_reason = hv_get_nmi_reason;
#if IS_ENABLED(CONFIG_HYPERV)
/*
* Setup the hook to get control post apic initialization.
*/
x86_platform.apic_post_init = hyperv_init;
#endif
}
const __refconst struct hypervisor_x86 x86_hyper_ms_hyperv = {

View File

@ -42,10 +42,22 @@ static struct resource goldfish_pdev_bus_resources[] = {
}
};
static bool goldfish_enable __initdata;
static int __init goldfish_setup(char *str)
{
goldfish_enable = true;
return 0;
}
__setup("goldfish", goldfish_setup);
static int __init goldfish_init(void)
{
if (!goldfish_enable)
return -ENODEV;
platform_device_register_simple("goldfish_pdev_bus", -1,
goldfish_pdev_bus_resources, 2);
goldfish_pdev_bus_resources, 2);
return 0;
}
device_initcall(goldfish_init);

View File

@ -202,4 +202,6 @@ source "drivers/hwtracing/intel_th/Kconfig"
source "drivers/fpga/Kconfig"
source "drivers/fsi/Kconfig"
endmenu

View File

@ -173,3 +173,4 @@ obj-$(CONFIG_STM) += hwtracing/stm/
obj-$(CONFIG_ANDROID) += android/
obj-$(CONFIG_NVMEM) += nvmem/
obj-$(CONFIG_FPGA) += fpga/
obj-$(CONFIG_FSI) += fsi/

View File

@ -19,6 +19,18 @@ config ANDROID_BINDER_IPC
Android process, using Binder to identify, invoke and pass arguments
between said processes.
config ANDROID_BINDER_DEVICES
string "Android Binder devices"
depends on ANDROID_BINDER_IPC
default "binder"
---help---
Default value for the binder.devices parameter.
The binder.devices parameter is a comma-separated list of strings
that specifies the names of the binder device nodes that will be
created. Each binder device has its own context manager, and is
therefore logically separated from the other devices.
config ANDROID_BINDER_IPC_32BIT
bool
depends on !64BIT && ANDROID_BINDER_IPC

File diff suppressed because it is too large Load Diff

View File

@ -56,14 +56,16 @@
#define HT16K33_FB_SIZE (HT16K33_MATRIX_LED_MAX_COLS * BYTES_PER_ROW)
struct ht16k33_keypad {
struct i2c_client *client;
struct input_dev *dev;
spinlock_t lock;
struct delayed_work work;
uint32_t cols;
uint32_t rows;
uint32_t row_shift;
uint32_t debounce_ms;
uint16_t last_key_state[HT16K33_MATRIX_KEYPAD_MAX_COLS];
wait_queue_head_t wait;
bool stopped;
};
struct ht16k33_fbdev {
@ -78,7 +80,6 @@ struct ht16k33_priv {
struct i2c_client *client;
struct ht16k33_keypad keypad;
struct ht16k33_fbdev fbdev;
struct workqueue_struct *workqueue;
};
static struct fb_fix_screeninfo ht16k33_fb_fix = {
@ -124,16 +125,8 @@ static void ht16k33_fb_queue(struct ht16k33_priv *priv)
{
struct ht16k33_fbdev *fbdev = &priv->fbdev;
queue_delayed_work(priv->workqueue, &fbdev->work,
msecs_to_jiffies(HZ / fbdev->refresh_rate));
}
static void ht16k33_keypad_queue(struct ht16k33_priv *priv)
{
struct ht16k33_keypad *keypad = &priv->keypad;
queue_delayed_work(priv->workqueue, &keypad->work,
msecs_to_jiffies(keypad->debounce_ms));
schedule_delayed_work(&fbdev->work,
msecs_to_jiffies(HZ / fbdev->refresh_rate));
}
/*
@ -182,32 +175,6 @@ static void ht16k33_fb_update(struct work_struct *work)
ht16k33_fb_queue(priv);
}
static int ht16k33_keypad_start(struct input_dev *dev)
{
struct ht16k33_priv *priv = input_get_drvdata(dev);
struct ht16k33_keypad *keypad = &priv->keypad;
/*
* Schedule an immediate key scan to capture current key state;
* columns will be activated and IRQs be enabled after the scan.
*/
queue_delayed_work(priv->workqueue, &keypad->work, 0);
return 0;
}
static void ht16k33_keypad_stop(struct input_dev *dev)
{
struct ht16k33_priv *priv = input_get_drvdata(dev);
struct ht16k33_keypad *keypad = &priv->keypad;
cancel_delayed_work(&keypad->work);
/*
* ht16k33_keypad_scan() will leave IRQs enabled;
* we should disable them now.
*/
disable_irq_nosync(priv->client->irq);
}
static int ht16k33_initialize(struct ht16k33_priv *priv)
{
uint8_t byte;
@ -233,61 +200,6 @@ static int ht16k33_initialize(struct ht16k33_priv *priv)
return i2c_smbus_write_byte(priv->client, byte);
}
/*
* This gets the keys from keypad and reports it to input subsystem
*/
static void ht16k33_keypad_scan(struct work_struct *work)
{
struct ht16k33_keypad *keypad =
container_of(work, struct ht16k33_keypad, work.work);
struct ht16k33_priv *priv =
container_of(keypad, struct ht16k33_priv, keypad);
const unsigned short *keycodes = keypad->dev->keycode;
uint16_t bits_changed, new_state[HT16K33_MATRIX_KEYPAD_MAX_COLS];
uint8_t data[HT16K33_MATRIX_KEYPAD_MAX_COLS * 2];
int row, col, code;
bool reschedule = false;
if (i2c_smbus_read_i2c_block_data(priv->client, 0x40, 6, data) != 6) {
dev_err(&priv->client->dev, "Failed to read key data\n");
goto end;
}
for (col = 0; col < keypad->cols; col++) {
new_state[col] = (data[col * 2 + 1] << 8) | data[col * 2];
if (new_state[col])
reschedule = true;
bits_changed = keypad->last_key_state[col] ^ new_state[col];
while (bits_changed) {
row = ffs(bits_changed) - 1;
code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
input_event(keypad->dev, EV_MSC, MSC_SCAN, code);
input_report_key(keypad->dev, keycodes[code],
new_state[col] & BIT(row));
bits_changed &= ~BIT(row);
}
}
input_sync(keypad->dev);
memcpy(keypad->last_key_state, new_state, sizeof(new_state));
end:
if (reschedule)
ht16k33_keypad_queue(priv);
else
enable_irq(priv->client->irq);
}
static irqreturn_t ht16k33_irq_thread(int irq, void *dev)
{
struct ht16k33_priv *priv = dev;
disable_irq_nosync(priv->client->irq);
ht16k33_keypad_queue(priv);
return IRQ_HANDLED;
}
static int ht16k33_bl_update_status(struct backlight_device *bl)
{
int brightness = bl->props.brightness;
@ -334,15 +246,152 @@ static struct fb_ops ht16k33_fb_ops = {
.fb_mmap = ht16k33_mmap,
};
/*
* This gets the keys from keypad and reports it to input subsystem.
* Returns true if a key is pressed.
*/
static bool ht16k33_keypad_scan(struct ht16k33_keypad *keypad)
{
const unsigned short *keycodes = keypad->dev->keycode;
u16 new_state[HT16K33_MATRIX_KEYPAD_MAX_COLS];
u8 data[HT16K33_MATRIX_KEYPAD_MAX_COLS * 2];
unsigned long bits_changed;
int row, col, code;
bool pressed = false;
if (i2c_smbus_read_i2c_block_data(keypad->client, 0x40, 6, data) != 6) {
dev_err(&keypad->client->dev, "Failed to read key data\n");
return false;
}
for (col = 0; col < keypad->cols; col++) {
new_state[col] = (data[col * 2 + 1] << 8) | data[col * 2];
if (new_state[col])
pressed = true;
bits_changed = keypad->last_key_state[col] ^ new_state[col];
for_each_set_bit(row, &bits_changed, BITS_PER_LONG) {
code = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
input_event(keypad->dev, EV_MSC, MSC_SCAN, code);
input_report_key(keypad->dev, keycodes[code],
new_state[col] & BIT(row));
}
}
input_sync(keypad->dev);
memcpy(keypad->last_key_state, new_state, sizeof(new_state));
return pressed;
}
static irqreturn_t ht16k33_keypad_irq_thread(int irq, void *dev)
{
struct ht16k33_keypad *keypad = dev;
do {
wait_event_timeout(keypad->wait, keypad->stopped,
msecs_to_jiffies(keypad->debounce_ms));
if (keypad->stopped)
break;
} while (ht16k33_keypad_scan(keypad));
return IRQ_HANDLED;
}
static int ht16k33_keypad_start(struct input_dev *dev)
{
struct ht16k33_keypad *keypad = input_get_drvdata(dev);
keypad->stopped = false;
mb();
enable_irq(keypad->client->irq);
return 0;
}
static void ht16k33_keypad_stop(struct input_dev *dev)
{
struct ht16k33_keypad *keypad = input_get_drvdata(dev);
keypad->stopped = true;
mb();
wake_up(&keypad->wait);
disable_irq(keypad->client->irq);
}
static int ht16k33_keypad_probe(struct i2c_client *client,
struct ht16k33_keypad *keypad)
{
struct device_node *node = client->dev.of_node;
u32 rows = HT16K33_MATRIX_KEYPAD_MAX_ROWS;
u32 cols = HT16K33_MATRIX_KEYPAD_MAX_COLS;
int err;
keypad->client = client;
init_waitqueue_head(&keypad->wait);
keypad->dev = devm_input_allocate_device(&client->dev);
if (!keypad->dev)
return -ENOMEM;
input_set_drvdata(keypad->dev, keypad);
keypad->dev->name = DRIVER_NAME"-keypad";
keypad->dev->id.bustype = BUS_I2C;
keypad->dev->open = ht16k33_keypad_start;
keypad->dev->close = ht16k33_keypad_stop;
if (!of_get_property(node, "linux,no-autorepeat", NULL))
__set_bit(EV_REP, keypad->dev->evbit);
err = of_property_read_u32(node, "debounce-delay-ms",
&keypad->debounce_ms);
if (err) {
dev_err(&client->dev, "key debounce delay not specified\n");
return err;
}
err = matrix_keypad_parse_of_params(&client->dev, &rows, &cols);
if (err)
return err;
keypad->rows = rows;
keypad->cols = cols;
keypad->row_shift = get_count_order(cols);
err = matrix_keypad_build_keymap(NULL, NULL, rows, cols, NULL,
keypad->dev);
if (err) {
dev_err(&client->dev, "failed to build keymap\n");
return err;
}
err = devm_request_threaded_irq(&client->dev, client->irq,
NULL, ht16k33_keypad_irq_thread,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
DRIVER_NAME, keypad);
if (err) {
dev_err(&client->dev, "irq request failed %d, error %d\n",
client->irq, err);
return err;
}
ht16k33_keypad_stop(keypad->dev);
err = input_register_device(keypad->dev);
if (err)
return err;
return 0;
}
static int ht16k33_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int err;
uint32_t rows, cols, dft_brightness;
uint32_t dft_brightness;
struct backlight_device *bl;
struct backlight_properties bl_props;
struct ht16k33_priv *priv;
struct ht16k33_keypad *keypad;
struct ht16k33_fbdev *fbdev;
struct device_node *node = client->dev.of_node;
@ -363,23 +412,16 @@ static int ht16k33_probe(struct i2c_client *client,
priv->client = client;
i2c_set_clientdata(client, priv);
fbdev = &priv->fbdev;
keypad = &priv->keypad;
priv->workqueue = create_singlethread_workqueue(DRIVER_NAME "-wq");
if (priv->workqueue == NULL)
return -ENOMEM;
err = ht16k33_initialize(priv);
if (err)
goto err_destroy_wq;
return err;
/* Framebuffer (2 bytes per column) */
BUILD_BUG_ON(PAGE_SIZE < HT16K33_FB_SIZE);
fbdev->buffer = (unsigned char *) get_zeroed_page(GFP_KERNEL);
if (!fbdev->buffer) {
err = -ENOMEM;
goto err_free_fbdev;
}
if (!fbdev->buffer)
return -ENOMEM;
fbdev->cache = devm_kmalloc(&client->dev, HT16K33_FB_SIZE, GFP_KERNEL);
if (!fbdev->cache) {
@ -415,59 +457,7 @@ static int ht16k33_probe(struct i2c_client *client,
if (err)
goto err_fbdev_info;
/* Keypad */
keypad->dev = devm_input_allocate_device(&client->dev);
if (!keypad->dev) {
err = -ENOMEM;
goto err_fbdev_unregister;
}
keypad->dev->name = DRIVER_NAME"-keypad";
keypad->dev->id.bustype = BUS_I2C;
keypad->dev->open = ht16k33_keypad_start;
keypad->dev->close = ht16k33_keypad_stop;
if (!of_get_property(node, "linux,no-autorepeat", NULL))
__set_bit(EV_REP, keypad->dev->evbit);
err = of_property_read_u32(node, "debounce-delay-ms",
&keypad->debounce_ms);
if (err) {
dev_err(&client->dev, "key debounce delay not specified\n");
goto err_fbdev_unregister;
}
err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
ht16k33_irq_thread,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
DRIVER_NAME, priv);
if (err) {
dev_err(&client->dev, "irq request failed %d, error %d\n",
client->irq, err);
goto err_fbdev_unregister;
}
disable_irq_nosync(client->irq);
rows = HT16K33_MATRIX_KEYPAD_MAX_ROWS;
cols = HT16K33_MATRIX_KEYPAD_MAX_COLS;
err = matrix_keypad_parse_of_params(&client->dev, &rows, &cols);
if (err)
goto err_fbdev_unregister;
err = matrix_keypad_build_keymap(NULL, NULL, rows, cols, NULL,
keypad->dev);
if (err) {
dev_err(&client->dev, "failed to build keymap\n");
goto err_fbdev_unregister;
}
input_set_drvdata(keypad->dev, priv);
keypad->rows = rows;
keypad->cols = cols;
keypad->row_shift = get_count_order(cols);
INIT_DELAYED_WORK(&keypad->work, ht16k33_keypad_scan);
err = input_register_device(keypad->dev);
err = ht16k33_keypad_probe(client, &priv->keypad);
if (err)
goto err_fbdev_unregister;
@ -482,7 +472,7 @@ static int ht16k33_probe(struct i2c_client *client,
if (IS_ERR(bl)) {
dev_err(&client->dev, "failed to register backlight\n");
err = PTR_ERR(bl);
goto err_keypad_unregister;
goto err_fbdev_unregister;
}
err = of_property_read_u32(node, "default-brightness-level",
@ -502,18 +492,12 @@ static int ht16k33_probe(struct i2c_client *client,
ht16k33_fb_queue(priv);
return 0;
err_keypad_unregister:
input_unregister_device(keypad->dev);
err_fbdev_unregister:
unregister_framebuffer(fbdev->info);
err_fbdev_info:
framebuffer_release(fbdev->info);
err_fbdev_buffer:
free_page((unsigned long) fbdev->buffer);
err_free_fbdev:
kfree(fbdev);
err_destroy_wq:
destroy_workqueue(priv->workqueue);
return err;
}
@ -521,17 +505,13 @@ static int ht16k33_probe(struct i2c_client *client,
static int ht16k33_remove(struct i2c_client *client)
{
struct ht16k33_priv *priv = i2c_get_clientdata(client);
struct ht16k33_keypad *keypad = &priv->keypad;
struct ht16k33_fbdev *fbdev = &priv->fbdev;
ht16k33_keypad_stop(keypad->dev);
cancel_delayed_work(&fbdev->work);
unregister_framebuffer(fbdev->info);
framebuffer_release(fbdev->info);
free_page((unsigned long) fbdev->buffer);
destroy_workqueue(priv->workqueue);
return 0;
}

View File

@ -571,9 +571,12 @@ config TELCLOCK
controlling the behavior of this hardware.
config DEVPORT
bool
bool "/dev/port character device"
depends on ISA || PCI
default y
help
Say Y here if you want to support the /dev/port device. The /dev/port
device is similar to /dev/mem, but for I/O ports.
source "drivers/s390/char/Kconfig"

View File

@ -31,13 +31,6 @@
#include <linux/kthread.h>
#include <linux/delay.h>
/*
* The apm_bios device is one of the misc char devices.
* This is its minor number.
*/
#define APM_MINOR_DEV 134
/*
* One option can be changed at boot time as follows:
* apm=on/off enable/disable APM

View File

@ -17,7 +17,6 @@
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/bcd.h>
#include <linux/mutex.h>

View File

@ -463,9 +463,9 @@ static int mmtimer_mmap(struct file *file, struct vm_area_struct *vma)
}
static struct miscdevice mmtimer_miscdev = {
SGI_MMTIMER,
MMTIMER_NAME,
&mmtimer_fops
.minor = SGI_MMTIMER,
.name = MMTIMER_NAME,
.fops = &mmtimer_fops
};
static struct timespec sgi_clock_offset;

View File

@ -269,7 +269,6 @@ int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data,
{
int status;
s32 buffer_count = 0;
s32 num_writes = 0;
bool dirty = false;
u32 i;
void __iomem *base_address = drvdata->base_address;
@ -298,7 +297,6 @@ int buffer_icap_set_configuration(struct hwicap_drvdata *drvdata, u32 *data,
}
buffer_count = 0;
num_writes++;
dirty = false;
}
@ -328,7 +326,6 @@ int buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data,
{
int status;
s32 buffer_count = 0;
s32 read_count = 0;
u32 i;
void __iomem *base_address = drvdata->base_address;
@ -353,7 +350,6 @@ int buffer_icap_get_configuration(struct hwicap_drvdata *drvdata, u32 *data,
}
buffer_count = 0;
read_count++;
}
/* Copy data from bram */

View File

@ -42,6 +42,16 @@ config EXTCON_GPIO
Say Y here to enable GPIO based extcon support. Note that GPIO
extcon supports single state per extcon instance.
config EXTCON_INTEL_INT3496
tristate "Intel INT3496 ACPI device extcon driver"
depends on GPIOLIB && ACPI
help
Say Y here to enable extcon support for USB OTG ports controlled by
an Intel INT3496 ACPI device.
This ACPI device is typically found on Intel Baytrail or Cherrytrail
based tablets, or other Baytrail / Cherrytrail devices.
config EXTCON_MAX14577
tristate "Maxim MAX14577/77836 EXTCON Support"
depends on MFD_MAX14577

View File

@ -8,6 +8,7 @@ obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o
obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
obj-$(CONFIG_EXTCON_AXP288) += extcon-axp288.o
obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
obj-$(CONFIG_EXTCON_INTEL_INT3496) += extcon-intel-int3496.o
obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o
obj-$(CONFIG_EXTCON_MAX3355) += extcon-max3355.o
obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o

View File

@ -14,7 +14,7 @@
* GNU General Public License for more details.
*/
#include <linux/extcon.h>
#include "extcon.h"
static int devm_extcon_dev_match(struct device *dev, void *res, void *data)
{

View File

@ -67,7 +67,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->dev, "read channel() error: %d\n", ret);
return;
}

View File

@ -236,12 +236,8 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode)
mode %= info->micd_num_modes;
if (arizona->pdata.micd_pol_gpio > 0)
gpio_set_value_cansleep(arizona->pdata.micd_pol_gpio,
info->micd_modes[mode].gpio);
else
gpiod_set_value_cansleep(info->micd_pol_gpio,
info->micd_modes[mode].gpio);
gpiod_set_value_cansleep(info->micd_pol_gpio,
info->micd_modes[mode].gpio);
regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1,
ARIZONA_MICD_BIAS_SRC_MASK,
@ -1412,21 +1408,21 @@ static int arizona_extcon_probe(struct platform_device *pdev)
regmap_update_bits(arizona->regmap, ARIZONA_GP_SWITCH_1,
ARIZONA_SW1_MODE_MASK, arizona->pdata.gpsw);
if (arizona->pdata.micd_pol_gpio > 0) {
if (pdata->micd_pol_gpio > 0) {
if (info->micd_modes[0].gpio)
mode = GPIOF_OUT_INIT_HIGH;
else
mode = GPIOF_OUT_INIT_LOW;
ret = devm_gpio_request_one(&pdev->dev,
arizona->pdata.micd_pol_gpio,
mode,
"MICD polarity");
ret = devm_gpio_request_one(&pdev->dev, pdata->micd_pol_gpio,
mode, "MICD polarity");
if (ret != 0) {
dev_err(arizona->dev, "Failed to request GPIO%d: %d\n",
arizona->pdata.micd_pol_gpio, ret);
pdata->micd_pol_gpio, ret);
goto err_register;
}
info->micd_pol_gpio = gpio_to_desc(pdata->micd_pol_gpio);
} else {
if (info->micd_modes[0].gpio)
mode = GPIOD_OUT_HIGH;

View File

@ -21,7 +21,6 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/usb/phy.h>
#include <linux/notifier.h>
#include <linux/extcon.h>
#include <linux/regmap.h>
@ -71,12 +70,6 @@
#define DET_STAT_CDP 2
#define DET_STAT_DCP 3
/* IRQ enable-1 register */
#define PWRSRC_IRQ_CFG_MASK (BIT(4)|BIT(3)|BIT(2))
/* IRQ enable-6 register */
#define BC12_IRQ_CFG_MASK BIT(1)
enum axp288_extcon_reg {
AXP288_PS_STAT_REG = 0x00,
AXP288_PS_BOOT_REASON_REG = 0x02,
@ -84,8 +77,6 @@ enum axp288_extcon_reg {
AXP288_BC_VBUS_CNTL_REG = 0x2d,
AXP288_BC_USB_STAT_REG = 0x2e,
AXP288_BC_DET_STAT_REG = 0x2f,
AXP288_PWRSRC_IRQ_CFG_REG = 0x40,
AXP288_BC12_IRQ_CFG_REG = 0x45,
};
enum axp288_mux_select {
@ -105,6 +96,7 @@ static const unsigned int axp288_extcon_cables[] = {
EXTCON_CHG_USB_SDP,
EXTCON_CHG_USB_CDP,
EXTCON_CHG_USB_DCP,
EXTCON_USB,
EXTCON_NONE,
};
@ -112,11 +104,11 @@ struct axp288_extcon_info {
struct device *dev;
struct regmap *regmap;
struct regmap_irq_chip_data *regmap_irqc;
struct axp288_extcon_pdata *pdata;
struct gpio_desc *gpio_mux_cntl;
int irq[EXTCON_IRQ_END];
struct extcon_dev *edev;
struct notifier_block extcon_nb;
struct usb_phy *otg;
unsigned int previous_cable;
};
/* Power up/down reason string array */
@ -156,10 +148,9 @@ static void axp288_extcon_log_rsi(struct axp288_extcon_info *info)
static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
{
static bool notify_otg, notify_charger;
static unsigned int cable;
int ret, stat, cfg, pwr_stat;
u8 chrg_type;
unsigned int cable = info->previous_cable;
bool vbus_attach = false;
ret = regmap_read(info->regmap, AXP288_PS_STAT_REG, &pwr_stat);
@ -168,9 +159,9 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
return ret;
}
vbus_attach = (pwr_stat & PS_STAT_VBUS_PRESENT);
vbus_attach = (pwr_stat & PS_STAT_VBUS_VALID);
if (!vbus_attach)
goto notify_otg;
goto no_vbus;
/* Check charger detection completion status */
ret = regmap_read(info->regmap, AXP288_BC_GLOBAL_REG, &cfg);
@ -190,19 +181,14 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
switch (chrg_type) {
case DET_STAT_SDP:
dev_dbg(info->dev, "sdp cable is connected\n");
notify_otg = true;
notify_charger = true;
cable = EXTCON_CHG_USB_SDP;
break;
case DET_STAT_CDP:
dev_dbg(info->dev, "cdp cable is connected\n");
notify_otg = true;
notify_charger = true;
cable = EXTCON_CHG_USB_CDP;
break;
case DET_STAT_DCP:
dev_dbg(info->dev, "dcp cable is connected\n");
notify_charger = true;
cable = EXTCON_CHG_USB_DCP;
break;
default:
@ -210,27 +196,28 @@ static int axp288_handle_chrg_det_event(struct axp288_extcon_info *info)
"disconnect or unknown or ID event\n");
}
notify_otg:
if (notify_otg) {
/*
* If VBUS is absent Connect D+/D- lines to PMIC for BC
* detection. Else connect them to SOC for USB communication.
*/
if (info->pdata->gpio_mux_cntl)
gpiod_set_value(info->pdata->gpio_mux_cntl,
vbus_attach ? EXTCON_GPIO_MUX_SEL_SOC
: EXTCON_GPIO_MUX_SEL_PMIC);
no_vbus:
/*
* If VBUS is absent Connect D+/D- lines to PMIC for BC
* detection. Else connect them to SOC for USB communication.
*/
if (info->gpio_mux_cntl)
gpiod_set_value(info->gpio_mux_cntl,
vbus_attach ? EXTCON_GPIO_MUX_SEL_SOC
: EXTCON_GPIO_MUX_SEL_PMIC);
atomic_notifier_call_chain(&info->otg->notifier,
vbus_attach ? USB_EVENT_VBUS : USB_EVENT_NONE, NULL);
}
extcon_set_state_sync(info->edev, info->previous_cable, false);
if (info->previous_cable == EXTCON_CHG_USB_SDP)
extcon_set_state_sync(info->edev, EXTCON_USB, false);
if (notify_charger)
if (vbus_attach) {
extcon_set_state_sync(info->edev, cable, vbus_attach);
if (cable == EXTCON_CHG_USB_SDP)
extcon_set_state_sync(info->edev, EXTCON_USB,
vbus_attach);
/* Clear the flags on disconnect event */
if (!vbus_attach)
notify_otg = notify_charger = false;
info->previous_cable = cable;
}
return 0;
@ -253,15 +240,10 @@ static irqreturn_t axp288_extcon_isr(int irq, void *data)
return IRQ_HANDLED;
}
static void axp288_extcon_enable_irq(struct axp288_extcon_info *info)
static void axp288_extcon_enable(struct axp288_extcon_info *info)
{
/* Unmask VBUS interrupt */
regmap_write(info->regmap, AXP288_PWRSRC_IRQ_CFG_REG,
PWRSRC_IRQ_CFG_MASK);
regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
BC_GLOBAL_RUN, 0);
/* Unmask the BC1.2 complete interrupts */
regmap_write(info->regmap, AXP288_BC12_IRQ_CFG_REG, BC12_IRQ_CFG_MASK);
/* Enable the charger detection logic */
regmap_update_bits(info->regmap, AXP288_BC_GLOBAL_REG,
BC_GLOBAL_RUN, BC_GLOBAL_RUN);
@ -271,6 +253,7 @@ static int axp288_extcon_probe(struct platform_device *pdev)
{
struct axp288_extcon_info *info;
struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
struct axp288_extcon_pdata *pdata = pdev->dev.platform_data;
int ret, i, pirq, gpio;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
@ -280,15 +263,10 @@ static int axp288_extcon_probe(struct platform_device *pdev)
info->dev = &pdev->dev;
info->regmap = axp20x->regmap;
info->regmap_irqc = axp20x->regmap_irqc;
info->pdata = pdev->dev.platform_data;
info->previous_cable = EXTCON_NONE;
if (pdata)
info->gpio_mux_cntl = pdata->gpio_mux_cntl;
if (!info->pdata) {
/* Try ACPI provided pdata via device properties */
if (!device_property_present(&pdev->dev,
"axp288_extcon_data\n"))
dev_err(&pdev->dev, "failed to get platform data\n");
return -ENODEV;
}
platform_set_drvdata(pdev, info);
axp288_extcon_log_rsi(info);
@ -308,23 +286,16 @@ static int axp288_extcon_probe(struct platform_device *pdev)
return ret;
}
/* Get otg transceiver phy */
info->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (IS_ERR(info->otg)) {
dev_err(&pdev->dev, "failed to get otg transceiver\n");
return PTR_ERR(info->otg);
}
/* Set up gpio control for USB Mux */
if (info->pdata->gpio_mux_cntl) {
gpio = desc_to_gpio(info->pdata->gpio_mux_cntl);
if (info->gpio_mux_cntl) {
gpio = desc_to_gpio(info->gpio_mux_cntl);
ret = devm_gpio_request(&pdev->dev, gpio, "USB_MUX");
if (ret < 0) {
dev_err(&pdev->dev,
"failed to request the gpio=%d\n", gpio);
return ret;
}
gpiod_direction_output(info->pdata->gpio_mux_cntl,
gpiod_direction_output(info->gpio_mux_cntl,
EXTCON_GPIO_MUX_SEL_PMIC);
}
@ -349,14 +320,21 @@ static int axp288_extcon_probe(struct platform_device *pdev)
}
}
/* Enable interrupts */
axp288_extcon_enable_irq(info);
/* Start charger cable type detection */
axp288_extcon_enable(info);
return 0;
}
static const struct platform_device_id axp288_extcon_table[] = {
{ .name = "axp288_extcon" },
{},
};
MODULE_DEVICE_TABLE(platform, axp288_extcon_table);
static struct platform_driver axp288_extcon_driver = {
.probe = axp288_extcon_probe,
.id_table = axp288_extcon_table,
.driver = {
.name = "axp288_extcon",
},

View File

@ -0,0 +1,179 @@
/*
* Intel INT3496 ACPI device extcon driver
*
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
*
* Based on android x86 kernel code which is:
*
* Copyright (c) 2014, Intel Corporation.
* Author: David Cohen <david.a.cohen@linux.intel.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/acpi.h>
#include <linux/extcon.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#define INT3496_GPIO_USB_ID 0
#define INT3496_GPIO_VBUS_EN 1
#define INT3496_GPIO_USB_MUX 2
#define DEBOUNCE_TIME msecs_to_jiffies(50)
struct int3496_data {
struct device *dev;
struct extcon_dev *edev;
struct delayed_work work;
struct gpio_desc *gpio_usb_id;
struct gpio_desc *gpio_vbus_en;
struct gpio_desc *gpio_usb_mux;
int usb_id_irq;
};
static const unsigned int int3496_cable[] = {
EXTCON_USB_HOST,
EXTCON_NONE,
};
static void int3496_do_usb_id(struct work_struct *work)
{
struct int3496_data *data =
container_of(work, struct int3496_data, work.work);
int id = gpiod_get_value_cansleep(data->gpio_usb_id);
/* id == 1: PERIPHERAL, id == 0: HOST */
dev_dbg(data->dev, "Connected %s cable\n", id ? "PERIPHERAL" : "HOST");
/*
* Peripheral: set USB mux to peripheral and disable VBUS
* Host: set USB mux to host and enable VBUS
*/
if (!IS_ERR(data->gpio_usb_mux))
gpiod_direction_output(data->gpio_usb_mux, id);
if (!IS_ERR(data->gpio_vbus_en))
gpiod_direction_output(data->gpio_vbus_en, !id);
extcon_set_state_sync(data->edev, EXTCON_USB_HOST, !id);
}
static irqreturn_t int3496_thread_isr(int irq, void *priv)
{
struct int3496_data *data = priv;
/* Let the pin settle before processing it */
mod_delayed_work(system_wq, &data->work, DEBOUNCE_TIME);
return IRQ_HANDLED;
}
static int int3496_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct int3496_data *data;
int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->dev = dev;
INIT_DELAYED_WORK(&data->work, int3496_do_usb_id);
data->gpio_usb_id = devm_gpiod_get_index(dev, "id",
INT3496_GPIO_USB_ID,
GPIOD_IN);
if (IS_ERR(data->gpio_usb_id)) {
ret = PTR_ERR(data->gpio_usb_id);
dev_err(dev, "can't request USB ID GPIO: %d\n", ret);
return ret;
}
data->usb_id_irq = gpiod_to_irq(data->gpio_usb_id);
if (data->usb_id_irq <= 0) {
dev_err(dev, "can't get USB ID IRQ: %d\n", data->usb_id_irq);
return -EINVAL;
}
data->gpio_vbus_en = devm_gpiod_get_index(dev, "vbus en",
INT3496_GPIO_VBUS_EN,
GPIOD_ASIS);
if (IS_ERR(data->gpio_vbus_en))
dev_info(dev, "can't request VBUS EN GPIO\n");
data->gpio_usb_mux = devm_gpiod_get_index(dev, "usb mux",
INT3496_GPIO_USB_MUX,
GPIOD_ASIS);
if (IS_ERR(data->gpio_usb_mux))
dev_info(dev, "can't request USB MUX GPIO\n");
/* register extcon device */
data->edev = devm_extcon_dev_allocate(dev, int3496_cable);
if (IS_ERR(data->edev))
return -ENOMEM;
ret = devm_extcon_dev_register(dev, data->edev);
if (ret < 0) {
dev_err(dev, "can't register extcon device: %d\n", ret);
return ret;
}
ret = devm_request_threaded_irq(dev, data->usb_id_irq,
NULL, int3496_thread_isr,
IRQF_SHARED | IRQF_ONESHOT |
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
dev_name(dev), data);
if (ret < 0) {
dev_err(dev, "can't request IRQ for USB ID GPIO: %d\n", ret);
return ret;
}
/* queue initial processing of id-pin */
queue_delayed_work(system_wq, &data->work, 0);
platform_set_drvdata(pdev, data);
return 0;
}
static int int3496_remove(struct platform_device *pdev)
{
struct int3496_data *data = platform_get_drvdata(pdev);
devm_free_irq(&pdev->dev, data->usb_id_irq, data);
cancel_delayed_work_sync(&data->work);
return 0;
}
static struct acpi_device_id int3496_acpi_match[] = {
{ "INT3496" },
{ }
};
MODULE_DEVICE_TABLE(acpi, int3496_acpi_match);
static struct platform_driver int3496_driver = {
.driver = {
.name = "intel-int3496",
.acpi_match_table = int3496_acpi_match,
},
.probe = int3496_probe,
.remove = int3496_remove,
};
module_platform_driver(int3496_driver);
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("Intel INT3496 ACPI device extcon driver");
MODULE_LICENSE("GPL");

View File

@ -531,8 +531,10 @@ static int max14577_parse_irq(struct max14577_muic_info *info, int irq_type)
case MAX14577_IRQ_INT1_ADC:
case MAX14577_IRQ_INT1_ADCLOW:
case MAX14577_IRQ_INT1_ADCERR:
/* Handle all of accessory except for
type of charger accessory */
/*
* Handle all of accessory except for
* type of charger accessory.
*/
info->irq_adc = true;
return 1;
case MAX14577_IRQ_INT2_CHGTYP:

View File

@ -188,8 +188,10 @@ enum max77693_muic_acc_type {
MAX77693_MUIC_ADC_AUDIO_MODE_REMOTE,
MAX77693_MUIC_ADC_OPEN,
/* The below accessories have same ADC value so ADCLow and
ADC1K bit is used to separate specific accessory */
/*
* The below accessories have same ADC value so ADCLow and
* ADC1K bit is used to separate specific accessory.
*/
/* ADC|VBVolot|ADCLow|ADC1K| */
MAX77693_MUIC_GND_USB_HOST = 0x100, /* 0x0| 0| 0| 0| */
MAX77693_MUIC_GND_USB_HOST_VB = 0x104, /* 0x0| 1| 0| 0| */
@ -970,8 +972,10 @@ static void max77693_muic_irq_work(struct work_struct *work)
case MAX77693_MUIC_IRQ_INT1_ADC_LOW:
case MAX77693_MUIC_IRQ_INT1_ADC_ERR:
case MAX77693_MUIC_IRQ_INT1_ADC1K:
/* Handle all of accessory except for
type of charger accessory */
/*
* Handle all of accessory except for
* type of charger accessory.
*/
ret = max77693_muic_adc_handler(info);
break;
case MAX77693_MUIC_IRQ_INT2_CHGTYP:

View File

@ -97,8 +97,10 @@ enum max77843_muic_accessory_type {
MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE1,
MAX77843_MUIC_ADC_OPEN,
/* The blow accessories should check
not only ADC value but also ADC1K and VBVolt value. */
/*
* The below accessories should check
* not only ADC value but also ADC1K and VBVolt value.
*/
/* Offset|ADC1K|VBVolt| */
MAX77843_MUIC_GND_USB_HOST = 0x100, /* 0x1| 0| 0| */
MAX77843_MUIC_GND_USB_HOST_VB = 0x101, /* 0x1| 0| 1| */
@ -265,16 +267,20 @@ static int max77843_muic_get_cable_type(struct max77843_muic_info *info,
/* Check GROUND accessory with charger cable */
if (adc == MAX77843_MUIC_ADC_GROUND) {
if (chg_type == MAX77843_MUIC_CHG_NONE) {
/* The following state when charger cable is
/*
* The following state when charger cable is
* disconnected but the GROUND accessory still
* connected */
* connected.
*/
*attached = false;
cable_type = info->prev_chg_type;
info->prev_chg_type = MAX77843_MUIC_CHG_NONE;
} else {
/* The following state when charger cable is
* connected on the GROUND accessory */
/*
* The following state when charger cable is
* connected on the GROUND accessory.
*/
*attached = true;
cable_type = MAX77843_MUIC_CHG_GND;
info->prev_chg_type = MAX77843_MUIC_CHG_GND;
@ -299,11 +305,13 @@ static int max77843_muic_get_cable_type(struct max77843_muic_info *info,
} else {
*attached = true;
/* Offset|ADC1K|VBVolt|
/*
* Offset|ADC1K|VBVolt|
* 0x1| 0| 0| USB-HOST
* 0x1| 0| 1| USB-HOST with VB
* 0x1| 1| 0| MHL
* 0x1| 1| 1| MHL with VB */
* 0x1| 1| 1| MHL with VB
*/
/* Get ADC1K register bit */
gnd_type = (info->status[MAX77843_MUIC_STATUS1] &
MAX77843_MUIC_STATUS1_ADC1K_MASK);

View File

@ -62,7 +62,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
if (palmas_usb->linkstat != PALMAS_USB_STATE_VBUS) {
palmas_usb->linkstat = PALMAS_USB_STATE_VBUS;
extcon_set_state_sync(edev, EXTCON_USB, true);
dev_info(palmas_usb->dev, "USB cable is attached\n");
dev_dbg(palmas_usb->dev, "USB cable is attached\n");
} else {
dev_dbg(palmas_usb->dev,
"Spurious connect event detected\n");
@ -71,7 +71,7 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
if (palmas_usb->linkstat == PALMAS_USB_STATE_VBUS) {
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_state_sync(edev, EXTCON_USB, false);
dev_info(palmas_usb->dev, "USB cable is detached\n");
dev_dbg(palmas_usb->dev, "USB cable is detached\n");
} else {
dev_dbg(palmas_usb->dev,
"Spurious disconnect event detected\n");
@ -99,7 +99,7 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
dev_info(palmas_usb->dev, "USB-HOST cable is attached\n");
dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n");
} else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) &&
(id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) {
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
@ -107,17 +107,17 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n");
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_ID) &&
(!(set & PALMAS_USB_ID_INT_SRC_ID_GND))) {
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n");
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) &&
(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
dev_info(palmas_usb->dev, " USB-HOST cable is attached\n");
dev_dbg(palmas_usb->dev, " USB-HOST cable is attached\n");
}
return IRQ_HANDLED;
@ -138,10 +138,10 @@ static void palmas_gpio_id_detect(struct work_struct *work)
if (id) {
extcon_set_state_sync(edev, EXTCON_USB_HOST, false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
dev_dbg(palmas_usb->dev, "USB-HOST cable is detached\n");
} else {
extcon_set_state_sync(edev, EXTCON_USB_HOST, true);
dev_info(palmas_usb->dev, "USB-HOST cable is attached\n");
dev_dbg(palmas_usb->dev, "USB-HOST cable is attached\n");
}
}
@ -190,6 +190,11 @@ static int palmas_usb_probe(struct platform_device *pdev)
struct palmas_usb *palmas_usb;
int status;
if (!palmas) {
dev_err(&pdev->dev, "failed to get valid parent\n");
return -EINVAL;
}
palmas_usb = devm_kzalloc(&pdev->dev, sizeof(*palmas_usb), GFP_KERNEL);
if (!palmas_usb)
return -ENOMEM;

View File

@ -142,8 +142,10 @@ enum rt8973a_muic_acc_type {
RT8973A_MUIC_ADC_UNKNOWN_ACC_5,
RT8973A_MUIC_ADC_OPEN = 0x1f,
/* The below accessories has same ADC value (0x1f).
So, Device type1 is used to separate specific accessory. */
/*
* The below accessories has same ADC value (0x1f).
* So, Device type1 is used to separate specific accessory.
*/
/* |---------|--ADC| */
/* | [7:5]|[4:0]| */
RT8973A_MUIC_ADC_USB = 0x3f, /* | 001|11111| */

View File

@ -135,8 +135,10 @@ enum sm5502_muic_acc_type {
SM5502_MUIC_ADC_AUDIO_TYPE1,
SM5502_MUIC_ADC_OPEN = 0x1f,
/* The below accessories have same ADC value (0x1f or 0x1e).
So, Device type1 is used to separate specific accessory. */
/*
* The below accessories have same ADC value (0x1f or 0x1e).
* So, Device type1 is used to separate specific accessory.
*/
/* |---------|--ADC| */
/* | [7:5]|[4:0]| */
SM5502_MUIC_ADC_AUDIO_TYPE1_FULL_REMOTE = 0x3e, /* | 001|11110| */

View File

@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/acpi.h>
#include <linux/pinctrl/consumer.h>
#define USB_GPIO_DEBOUNCE_MS 20 /* ms */
@ -245,6 +246,9 @@ static int usb_extcon_suspend(struct device *dev)
if (info->vbus_gpiod)
disable_irq(info->vbus_irq);
if (!device_may_wakeup(dev))
pinctrl_pm_select_sleep_state(dev);
return ret;
}
@ -253,6 +257,9 @@ static int usb_extcon_resume(struct device *dev)
struct usb_extcon_info *info = dev_get_drvdata(dev);
int ret = 0;
if (!device_may_wakeup(dev))
pinctrl_pm_select_default_state(dev);
if (device_may_wakeup(dev)) {
if (info->id_gpiod) {
ret = disable_irq_wake(info->id_irq);

View File

@ -30,11 +30,12 @@
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/err.h>
#include <linux/extcon.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include "extcon.h"
#define SUPPORTED_CABLE_MAX 32
#define CABLE_NAME_MAX 30
@ -59,7 +60,7 @@ struct __extcon_info {
[EXTCON_USB_HOST] = {
.type = EXTCON_TYPE_USB,
.id = EXTCON_USB_HOST,
.name = "USB_HOST",
.name = "USB-HOST",
},
/* Charging external connector */
@ -98,6 +99,11 @@ struct __extcon_info {
.id = EXTCON_CHG_WPT,
.name = "WPT",
},
[EXTCON_CHG_USB_PD] = {
.type = EXTCON_TYPE_CHG | EXTCON_TYPE_USB,
.id = EXTCON_CHG_USB_PD,
.name = "PD",
},
/* Jack external connector */
[EXTCON_JACK_MICROPHONE] = {
@ -906,35 +912,16 @@ int extcon_register_notifier(struct extcon_dev *edev, unsigned int id,
unsigned long flags;
int ret, idx = -EINVAL;
if (!nb)
if (!edev || !nb)
return -EINVAL;
if (edev) {
idx = find_cable_index_by_id(edev, id);
if (idx < 0)
return idx;
idx = find_cable_index_by_id(edev, id);
if (idx < 0)
return idx;
spin_lock_irqsave(&edev->lock, flags);
ret = raw_notifier_chain_register(&edev->nh[idx], nb);
spin_unlock_irqrestore(&edev->lock, flags);
} else {
struct extcon_dev *extd;
mutex_lock(&extcon_dev_list_lock);
list_for_each_entry(extd, &extcon_dev_list, entry) {
idx = find_cable_index_by_id(extd, id);
if (idx >= 0)
break;
}
mutex_unlock(&extcon_dev_list_lock);
if (idx >= 0) {
edev = extd;
return extcon_register_notifier(extd, id, nb);
} else {
ret = -ENODEV;
}
}
spin_lock_irqsave(&edev->lock, flags);
ret = raw_notifier_chain_register(&edev->nh[idx], nb);
spin_unlock_irqrestore(&edev->lock, flags);
return ret;
}

62
drivers/extcon/extcon.h Normal file
View File

@ -0,0 +1,62 @@
#ifndef __LINUX_EXTCON_INTERNAL_H__
#define __LINUX_EXTCON_INTERNAL_H__
#include <linux/extcon.h>
/**
* struct extcon_dev - An extcon device represents one external connector.
* @name: The name of this extcon device. Parent device name is
* used if NULL.
* @supported_cable: Array of supported cable names ending with EXTCON_NONE.
* If supported_cable is NULL, cable name related APIs
* are disabled.
* @mutually_exclusive: Array of mutually exclusive set of cables that cannot
* be attached simultaneously. The array should be
* ending with NULL or be NULL (no mutually exclusive
* cables). For example, if it is { 0x7, 0x30, 0}, then,
* {0, 1}, {0, 1, 2}, {0, 2}, {1, 2}, or {4, 5} cannot
* be attached simulataneously. {0x7, 0} is equivalent to
* {0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there
* can be no simultaneous connections.
* @dev: Device of this extcon.
* @state: Attach/detach state of this extcon. Do not provide at
* register-time.
* @nh: Notifier for the state change events from this extcon
* @entry: To support list of extcon devices so that users can
* search for extcon devices based on the extcon name.
* @lock:
* @max_supported: Internal value to store the number of cables.
* @extcon_dev_type: Device_type struct to provide attribute_groups
* customized for each extcon device.
* @cables: Sysfs subdirectories. Each represents one cable.
*
* In most cases, users only need to provide "User initializing data" of
* this struct when registering an extcon. In some exceptional cases,
* optional callbacks may be needed. However, the values in "internal data"
* are overwritten by register function.
*/
struct extcon_dev {
/* Optional user initializing data */
const char *name;
const unsigned int *supported_cable;
const u32 *mutually_exclusive;
/* Internal data. Please do not set. */
struct device dev;
struct raw_notifier_head *nh;
struct list_head entry;
int max_supported;
spinlock_t lock; /* could be called by irq handler */
u32 state;
/* /sys/class/extcon/.../cable.n/... */
struct device_type extcon_dev_type;
struct extcon_cable *cables;
/* /sys/class/extcon/.../mutually_exclusive/... */
struct attribute_group attr_g_muex;
struct attribute **attrs_muex;
struct device_attribute *d_attrs_muex;
};
#endif /* __LINUX_EXTCON_INTERNAL_H__ */

View File

@ -25,16 +25,106 @@
#include <linux/of.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/scatterlist.h>
#include <linux/highmem.h>
static DEFINE_IDA(fpga_mgr_ida);
static struct class *fpga_mgr_class;
/*
* Call the low level driver's write_init function. This will do the
* device-specific things to get the FPGA into the state where it is ready to
* receive an FPGA image. The low level driver only gets to see the first
* initial_header_size bytes in the buffer.
*/
static int fpga_mgr_write_init_buf(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count)
{
int ret;
mgr->state = FPGA_MGR_STATE_WRITE_INIT;
if (!mgr->mops->initial_header_size)
ret = mgr->mops->write_init(mgr, info, NULL, 0);
else
ret = mgr->mops->write_init(
mgr, info, buf, min(mgr->mops->initial_header_size, count));
if (ret) {
dev_err(&mgr->dev, "Error preparing FPGA for writing\n");
mgr->state = FPGA_MGR_STATE_WRITE_INIT_ERR;
return ret;
}
return 0;
}
static int fpga_mgr_write_init_sg(struct fpga_manager *mgr,
struct fpga_image_info *info,
struct sg_table *sgt)
{
struct sg_mapping_iter miter;
size_t len;
char *buf;
int ret;
if (!mgr->mops->initial_header_size)
return fpga_mgr_write_init_buf(mgr, info, NULL, 0);
/*
* First try to use miter to map the first fragment to access the
* header, this is the typical path.
*/
sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG);
if (sg_miter_next(&miter) &&
miter.length >= mgr->mops->initial_header_size) {
ret = fpga_mgr_write_init_buf(mgr, info, miter.addr,
miter.length);
sg_miter_stop(&miter);
return ret;
}
sg_miter_stop(&miter);
/* Otherwise copy the fragments into temporary memory. */
buf = kmalloc(mgr->mops->initial_header_size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
len = sg_copy_to_buffer(sgt->sgl, sgt->nents, buf,
mgr->mops->initial_header_size);
ret = fpga_mgr_write_init_buf(mgr, info, buf, len);
kfree(buf);
return ret;
}
/*
* After all the FPGA image has been written, do the device specific steps to
* finish and set the FPGA into operating mode.
*/
static int fpga_mgr_write_complete(struct fpga_manager *mgr,
struct fpga_image_info *info)
{
int ret;
mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE;
ret = mgr->mops->write_complete(mgr, info);
if (ret) {
dev_err(&mgr->dev, "Error after writing image data to FPGA\n");
mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR;
return ret;
}
mgr->state = FPGA_MGR_STATE_OPERATING;
return 0;
}
/**
* fpga_mgr_buf_load - load fpga from image in buffer
* fpga_mgr_buf_load_sg - load fpga from image in buffer from a scatter list
* @mgr: fpga manager
* @info: fpga image specific information
* @buf: buffer contain fpga image
* @count: byte count of buf
* @sgt: scatterlist table
*
* Step the low level fpga manager through the device-specific steps of getting
* an FPGA ready to be configured, writing the image to it, then doing whatever
@ -42,54 +132,139 @@ static struct class *fpga_mgr_class;
* mgr pointer from of_fpga_mgr_get() or fpga_mgr_get() and checked that it is
* not an error code.
*
* This is the preferred entry point for FPGA programming, it does not require
* any contiguous kernel memory.
*
* Return: 0 on success, negative error code otherwise.
*/
int fpga_mgr_buf_load(struct fpga_manager *mgr, struct fpga_image_info *info,
const char *buf, size_t count)
int fpga_mgr_buf_load_sg(struct fpga_manager *mgr, struct fpga_image_info *info,
struct sg_table *sgt)
{
struct device *dev = &mgr->dev;
int ret;
/*
* Call the low level driver's write_init function. This will do the
* device-specific things to get the FPGA into the state where it is
* ready to receive an FPGA image. The low level driver only gets to
* see the first initial_header_size bytes in the buffer.
*/
mgr->state = FPGA_MGR_STATE_WRITE_INIT;
ret = mgr->mops->write_init(mgr, info, buf,
min(mgr->mops->initial_header_size, count));
ret = fpga_mgr_write_init_sg(mgr, info, sgt);
if (ret)
return ret;
/* Write the FPGA image to the FPGA. */
mgr->state = FPGA_MGR_STATE_WRITE;
if (mgr->mops->write_sg) {
ret = mgr->mops->write_sg(mgr, sgt);
} else {
struct sg_mapping_iter miter;
sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG);
while (sg_miter_next(&miter)) {
ret = mgr->mops->write(mgr, miter.addr, miter.length);
if (ret)
break;
}
sg_miter_stop(&miter);
}
if (ret) {
dev_err(dev, "Error preparing FPGA for writing\n");
mgr->state = FPGA_MGR_STATE_WRITE_INIT_ERR;
dev_err(&mgr->dev, "Error while writing image data to FPGA\n");
mgr->state = FPGA_MGR_STATE_WRITE_ERR;
return ret;
}
return fpga_mgr_write_complete(mgr, info);
}
EXPORT_SYMBOL_GPL(fpga_mgr_buf_load_sg);
static int fpga_mgr_buf_load_mapped(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count)
{
int ret;
ret = fpga_mgr_write_init_buf(mgr, info, buf, count);
if (ret)
return ret;
/*
* Write the FPGA image to the FPGA.
*/
mgr->state = FPGA_MGR_STATE_WRITE;
ret = mgr->mops->write(mgr, buf, count);
if (ret) {
dev_err(dev, "Error while writing image data to FPGA\n");
dev_err(&mgr->dev, "Error while writing image data to FPGA\n");
mgr->state = FPGA_MGR_STATE_WRITE_ERR;
return ret;
}
/*
* After all the FPGA image has been written, do the device specific
* steps to finish and set the FPGA into operating mode.
*/
mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE;
ret = mgr->mops->write_complete(mgr, info);
if (ret) {
dev_err(dev, "Error after writing image data to FPGA\n");
mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR;
return ret;
}
mgr->state = FPGA_MGR_STATE_OPERATING;
return fpga_mgr_write_complete(mgr, info);
}
return 0;
/**
* fpga_mgr_buf_load - load fpga from image in buffer
* @mgr: fpga manager
* @flags: flags setting fpga confuration modes
* @buf: buffer contain fpga image
* @count: byte count of buf
*
* Step the low level fpga manager through the device-specific steps of getting
* an FPGA ready to be configured, writing the image to it, then doing whatever
* post-configuration steps necessary. This code assumes the caller got the
* mgr pointer from of_fpga_mgr_get() and checked that it is not an error code.
*
* Return: 0 on success, negative error code otherwise.
*/
int fpga_mgr_buf_load(struct fpga_manager *mgr, struct fpga_image_info *info,
const char *buf, size_t count)
{
struct page **pages;
struct sg_table sgt;
const void *p;
int nr_pages;
int index;
int rc;
/*
* This is just a fast path if the caller has already created a
* contiguous kernel buffer and the driver doesn't require SG, non-SG
* drivers will still work on the slow path.
*/
if (mgr->mops->write)
return fpga_mgr_buf_load_mapped(mgr, info, buf, count);
/*
* Convert the linear kernel pointer into a sg_table of pages for use
* by the driver.
*/
nr_pages = DIV_ROUND_UP((unsigned long)buf + count, PAGE_SIZE) -
(unsigned long)buf / PAGE_SIZE;
pages = kmalloc_array(nr_pages, sizeof(struct page *), GFP_KERNEL);
if (!pages)
return -ENOMEM;
p = buf - offset_in_page(buf);
for (index = 0; index < nr_pages; index++) {
if (is_vmalloc_addr(p))
pages[index] = vmalloc_to_page(p);
else
pages[index] = kmap_to_page((void *)p);
if (!pages[index]) {
kfree(pages);
return -EFAULT;
}
p += PAGE_SIZE;
}
/*
* The temporary pages list is used to code share the merging algorithm
* in sg_alloc_table_from_pages
*/
rc = sg_alloc_table_from_pages(&sgt, pages, index, offset_in_page(buf),
count, GFP_KERNEL);
kfree(pages);
if (rc)
return rc;
rc = fpga_mgr_buf_load_sg(mgr, info, &sgt);
sg_free_table(&sgt);
return rc;
}
EXPORT_SYMBOL_GPL(fpga_mgr_buf_load);
@ -291,8 +466,9 @@ int fpga_mgr_register(struct device *dev, const char *name,
struct fpga_manager *mgr;
int id, ret;
if (!mops || !mops->write_init || !mops->write ||
!mops->write_complete || !mops->state) {
if (!mops || !mops->write_complete || !mops->state ||
!mops->write_init || (!mops->write && !mops->write_sg) ||
(mops->write && mops->write_sg)) {
dev_err(dev, "Attempt to register without fpga_manager_ops\n");
return -EINVAL;
}

View File

@ -30,6 +30,7 @@
#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/string.h>
#include <linux/scatterlist.h>
/* Offsets into SLCR regmap */
@ -80,6 +81,7 @@
/* FPGA init status */
#define STATUS_DMA_Q_F BIT(31)
#define STATUS_DMA_Q_E BIT(30)
#define STATUS_PCFG_INIT_MASK BIT(4)
/* Interrupt Status/Mask Register Bit definitions */
@ -89,7 +91,7 @@
#define IXR_D_P_DONE_MASK BIT(12)
/* FPGA programmed */
#define IXR_PCFG_DONE_MASK BIT(2)
#define IXR_ERROR_FLAGS_MASK 0x00F0F860
#define IXR_ERROR_FLAGS_MASK 0x00F0C860
#define IXR_ALL_MASK 0xF8F7F87F
/* Miscellaneous constant values */
@ -98,12 +100,16 @@
#define DMA_INVALID_ADDRESS GENMASK(31, 0)
/* Used to unlock the dev */
#define UNLOCK_MASK 0x757bdf0d
/* Timeout for DMA to complete */
#define DMA_DONE_TIMEOUT msecs_to_jiffies(1000)
/* Timeout for polling reset bits */
#define INIT_POLL_TIMEOUT 2500000
/* Delay for polling reset bits */
#define INIT_POLL_DELAY 20
/* Signal this is the last DMA transfer, wait for the AXI and PCAP before
* interrupting
*/
#define DMA_SRC_LAST_TRANSFER 1
/* Timeout for DMA completion */
#define DMA_TIMEOUT_MS 5000
/* Masks for controlling stuff in SLCR */
/* Disable all Level shifters */
@ -124,6 +130,11 @@ struct zynq_fpga_priv {
void __iomem *io_base;
struct regmap *slcr;
spinlock_t dma_lock;
unsigned int dma_elm;
unsigned int dma_nelms;
struct scatterlist *cur_sg;
struct completion dma_done;
};
@ -143,37 +154,104 @@ static inline u32 zynq_fpga_read(const struct zynq_fpga_priv *priv,
readl_poll_timeout(priv->io_base + addr, val, cond, sleep_us, \
timeout_us)
static void zynq_fpga_mask_irqs(struct zynq_fpga_priv *priv)
/* Cause the specified irq mask bits to generate IRQs */
static inline void zynq_fpga_set_irq(struct zynq_fpga_priv *priv, u32 enable)
{
u32 intr_mask;
intr_mask = zynq_fpga_read(priv, INT_MASK_OFFSET);
zynq_fpga_write(priv, INT_MASK_OFFSET,
intr_mask | IXR_DMA_DONE_MASK | IXR_ERROR_FLAGS_MASK);
zynq_fpga_write(priv, INT_MASK_OFFSET, ~enable);
}
static void zynq_fpga_unmask_irqs(struct zynq_fpga_priv *priv)
/* Must be called with dma_lock held */
static void zynq_step_dma(struct zynq_fpga_priv *priv)
{
u32 intr_mask;
u32 addr;
u32 len;
bool first;
intr_mask = zynq_fpga_read(priv, INT_MASK_OFFSET);
zynq_fpga_write(priv, INT_MASK_OFFSET,
intr_mask
& ~(IXR_D_P_DONE_MASK | IXR_ERROR_FLAGS_MASK));
first = priv->dma_elm == 0;
while (priv->cur_sg) {
/* Feed the DMA queue until it is full. */
if (zynq_fpga_read(priv, STATUS_OFFSET) & STATUS_DMA_Q_F)
break;
addr = sg_dma_address(priv->cur_sg);
len = sg_dma_len(priv->cur_sg);
if (priv->dma_elm + 1 == priv->dma_nelms) {
/* The last transfer waits for the PCAP to finish too,
* notice this also changes the irq_mask to ignore
* IXR_DMA_DONE_MASK which ensures we do not trigger
* the completion too early.
*/
addr |= DMA_SRC_LAST_TRANSFER;
priv->cur_sg = NULL;
} else {
priv->cur_sg = sg_next(priv->cur_sg);
priv->dma_elm++;
}
zynq_fpga_write(priv, DMA_SRC_ADDR_OFFSET, addr);
zynq_fpga_write(priv, DMA_DST_ADDR_OFFSET, DMA_INVALID_ADDRESS);
zynq_fpga_write(priv, DMA_SRC_LEN_OFFSET, len / 4);
zynq_fpga_write(priv, DMA_DEST_LEN_OFFSET, 0);
}
/* Once the first transfer is queued we can turn on the ISR, future
* calls to zynq_step_dma will happen from the ISR context. The
* dma_lock spinlock guarentees this handover is done coherently, the
* ISR enable is put at the end to avoid another CPU spinning in the
* ISR on this lock.
*/
if (first && priv->cur_sg) {
zynq_fpga_set_irq(priv,
IXR_DMA_DONE_MASK | IXR_ERROR_FLAGS_MASK);
} else if (!priv->cur_sg) {
/* The last transfer changes to DMA & PCAP mode since we do
* not want to continue until everything has been flushed into
* the PCAP.
*/
zynq_fpga_set_irq(priv,
IXR_D_P_DONE_MASK | IXR_ERROR_FLAGS_MASK);
}
}
static irqreturn_t zynq_fpga_isr(int irq, void *data)
{
struct zynq_fpga_priv *priv = data;
u32 intr_status;
/* disable DMA and error IRQs */
zynq_fpga_mask_irqs(priv);
/* If anything other than DMA completion is reported stop and hand
* control back to zynq_fpga_ops_write, something went wrong,
* otherwise progress the DMA.
*/
spin_lock(&priv->dma_lock);
intr_status = zynq_fpga_read(priv, INT_STS_OFFSET);
if (!(intr_status & IXR_ERROR_FLAGS_MASK) &&
(intr_status & IXR_DMA_DONE_MASK) && priv->cur_sg) {
zynq_fpga_write(priv, INT_STS_OFFSET, IXR_DMA_DONE_MASK);
zynq_step_dma(priv);
spin_unlock(&priv->dma_lock);
return IRQ_HANDLED;
}
spin_unlock(&priv->dma_lock);
zynq_fpga_set_irq(priv, 0);
complete(&priv->dma_done);
return IRQ_HANDLED;
}
/* Sanity check the proposed bitstream. It must start with the sync word in
* the correct byte order, and be dword aligned. The input is a Xilinx .bin
* file with every 32 bit quantity swapped.
*/
static bool zynq_fpga_has_sync(const u8 *buf, size_t count)
{
for (; count >= 4; buf += 4, count -= 4)
if (buf[0] == 0x66 && buf[1] == 0x55 && buf[2] == 0x99 &&
buf[3] == 0xaa)
return true;
return false;
}
static int zynq_fpga_ops_write_init(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count)
@ -190,6 +268,13 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr,
/* don't globally reset PL if we're doing partial reconfig */
if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
if (!zynq_fpga_has_sync(buf, count)) {
dev_err(&mgr->dev,
"Invalid bitstream, could not find a sync word. Bitstream must be a byte swapped .bin file\n");
err = -EINVAL;
goto out_err;
}
/* assert AXI interface resets */
regmap_write(priv->slcr, SLCR_FPGA_RST_CTRL_OFFSET,
FPGA_RST_ALL_MASK);
@ -259,10 +344,11 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr,
zynq_fpga_write(priv, CTRL_OFFSET,
(CTRL_PCAP_PR_MASK | CTRL_PCAP_MODE_MASK | ctrl));
/* check that we have room in the command queue */
/* We expect that the command queue is empty right now. */
status = zynq_fpga_read(priv, STATUS_OFFSET);
if (status & STATUS_DMA_Q_F) {
dev_err(&mgr->dev, "DMA command queue full\n");
if ((status & STATUS_DMA_Q_F) ||
(status & STATUS_DMA_Q_E) != STATUS_DMA_Q_E) {
dev_err(&mgr->dev, "DMA command queue not right\n");
err = -EBUSY;
goto out_err;
}
@ -281,26 +367,36 @@ static int zynq_fpga_ops_write_init(struct fpga_manager *mgr,
return err;
}
static int zynq_fpga_ops_write(struct fpga_manager *mgr,
const char *buf, size_t count)
static int zynq_fpga_ops_write(struct fpga_manager *mgr, struct sg_table *sgt)
{
struct zynq_fpga_priv *priv;
const char *why;
int err;
char *kbuf;
size_t in_count;
dma_addr_t dma_addr;
u32 transfer_length;
u32 intr_status;
unsigned long timeout;
unsigned long flags;
struct scatterlist *sg;
int i;
in_count = count;
priv = mgr->priv;
kbuf =
dma_alloc_coherent(mgr->dev.parent, count, &dma_addr, GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
/* The hardware can only DMA multiples of 4 bytes, and it requires the
* starting addresses to be aligned to 64 bits (UG585 pg 212).
*/
for_each_sg(sgt->sgl, sg, sgt->nents, i) {
if ((sg->offset % 8) || (sg->length % 4)) {
dev_err(&mgr->dev,
"Invalid bitstream, chunks must be aligned\n");
return -EINVAL;
}
}
memcpy(kbuf, buf, count);
priv->dma_nelms =
dma_map_sg(mgr->dev.parent, sgt->sgl, sgt->nents, DMA_TO_DEVICE);
if (priv->dma_nelms == 0) {
dev_err(&mgr->dev, "Unable to DMA map (TO_DEVICE)\n");
return -ENOMEM;
}
/* enable clock */
err = clk_enable(priv->clk);
@ -308,38 +404,67 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr,
goto out_free;
zynq_fpga_write(priv, INT_STS_OFFSET, IXR_ALL_MASK);
reinit_completion(&priv->dma_done);
/* enable DMA and error IRQs */
zynq_fpga_unmask_irqs(priv);
/* zynq_step_dma will turn on interrupts */
spin_lock_irqsave(&priv->dma_lock, flags);
priv->dma_elm = 0;
priv->cur_sg = sgt->sgl;
zynq_step_dma(priv);
spin_unlock_irqrestore(&priv->dma_lock, flags);
/* the +1 in the src addr is used to hold off on DMA_DONE IRQ
* until both AXI and PCAP are done ...
*/
zynq_fpga_write(priv, DMA_SRC_ADDR_OFFSET, (u32)(dma_addr) + 1);
zynq_fpga_write(priv, DMA_DST_ADDR_OFFSET, (u32)DMA_INVALID_ADDRESS);
timeout = wait_for_completion_timeout(&priv->dma_done,
msecs_to_jiffies(DMA_TIMEOUT_MS));
/* convert #bytes to #words */
transfer_length = (count + 3) / 4;
zynq_fpga_write(priv, DMA_SRC_LEN_OFFSET, transfer_length);
zynq_fpga_write(priv, DMA_DEST_LEN_OFFSET, 0);
wait_for_completion(&priv->dma_done);
spin_lock_irqsave(&priv->dma_lock, flags);
zynq_fpga_set_irq(priv, 0);
priv->cur_sg = NULL;
spin_unlock_irqrestore(&priv->dma_lock, flags);
intr_status = zynq_fpga_read(priv, INT_STS_OFFSET);
zynq_fpga_write(priv, INT_STS_OFFSET, intr_status);
zynq_fpga_write(priv, INT_STS_OFFSET, IXR_ALL_MASK);
if (!((intr_status & IXR_D_P_DONE_MASK) == IXR_D_P_DONE_MASK)) {
dev_err(&mgr->dev, "Error configuring FPGA\n");
err = -EFAULT;
/* There doesn't seem to be a way to force cancel any DMA, so if
* something went wrong we are relying on the hardware to have halted
* the DMA before we get here, if there was we could use
* wait_for_completion_interruptible too.
*/
if (intr_status & IXR_ERROR_FLAGS_MASK) {
why = "DMA reported error";
err = -EIO;
goto out_report;
}
if (priv->cur_sg ||
!((intr_status & IXR_D_P_DONE_MASK) == IXR_D_P_DONE_MASK)) {
if (timeout == 0)
why = "DMA timed out";
else
why = "DMA did not complete";
err = -EIO;
goto out_report;
}
err = 0;
goto out_clk;
out_report:
dev_err(&mgr->dev,
"%s: INT_STS:0x%x CTRL:0x%x LOCK:0x%x INT_MASK:0x%x STATUS:0x%x MCTRL:0x%x\n",
why,
intr_status,
zynq_fpga_read(priv, CTRL_OFFSET),
zynq_fpga_read(priv, LOCK_OFFSET),
zynq_fpga_read(priv, INT_MASK_OFFSET),
zynq_fpga_read(priv, STATUS_OFFSET),
zynq_fpga_read(priv, MCTRL_OFFSET));
out_clk:
clk_disable(priv->clk);
out_free:
dma_free_coherent(mgr->dev.parent, count, kbuf, dma_addr);
dma_unmap_sg(mgr->dev.parent, sgt->sgl, sgt->nents, DMA_TO_DEVICE);
return err;
}
@ -400,9 +525,10 @@ static enum fpga_mgr_states zynq_fpga_ops_state(struct fpga_manager *mgr)
}
static const struct fpga_manager_ops zynq_fpga_ops = {
.initial_header_size = 128,
.state = zynq_fpga_ops_state,
.write_init = zynq_fpga_ops_write_init,
.write = zynq_fpga_ops_write,
.write_sg = zynq_fpga_ops_write,
.write_complete = zynq_fpga_ops_write_complete,
};
@ -416,6 +542,7 @@ static int zynq_fpga_probe(struct platform_device *pdev)
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
spin_lock_init(&priv->dma_lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->io_base = devm_ioremap_resource(dev, res);
@ -452,7 +579,7 @@ static int zynq_fpga_probe(struct platform_device *pdev)
/* unlock the device */
zynq_fpga_write(priv, UNLOCK_OFFSET, UNLOCK_MASK);
zynq_fpga_write(priv, INT_MASK_OFFSET, 0xFFFFFFFF);
zynq_fpga_set_irq(priv, 0);
zynq_fpga_write(priv, INT_STS_OFFSET, IXR_ALL_MASK);
err = devm_request_irq(dev, priv->irq, zynq_fpga_isr, 0, dev_name(dev),
priv);

12
drivers/fsi/Kconfig Normal file
View File

@ -0,0 +1,12 @@
#
# FSI subsystem
#
menu "FSI support"
config FSI
tristate "FSI support"
---help---
FSI - the FRU Support Interface - is a simple bus for low-level
access to POWER-based hardware.
endmenu

2
drivers/fsi/Makefile Normal file
View File

@ -0,0 +1,2 @@
obj-$(CONFIG_FSI) += fsi-core.o

59
drivers/fsi/fsi-core.c Normal file
View File

@ -0,0 +1,59 @@
/*
* FSI core driver
*
* Copyright (C) IBM Corporation 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/device.h>
#include <linux/fsi.h>
#include <linux/module.h>
/* FSI core & Linux bus type definitions */
static int fsi_bus_match(struct device *dev, struct device_driver *drv)
{
struct fsi_device *fsi_dev = to_fsi_dev(dev);
struct fsi_driver *fsi_drv = to_fsi_drv(drv);
const struct fsi_device_id *id;
if (!fsi_drv->id_table)
return 0;
for (id = fsi_drv->id_table; id->engine_type; id++) {
if (id->engine_type != fsi_dev->engine_type)
continue;
if (id->version == FSI_VERSION_ANY ||
id->version == fsi_dev->version)
return 1;
}
return 0;
}
struct bus_type fsi_bus_type = {
.name = "fsi",
.match = fsi_bus_match,
};
EXPORT_SYMBOL_GPL(fsi_bus_type);
static int fsi_init(void)
{
return bus_register(&fsi_bus_type);
}
static void fsi_exit(void)
{
bus_unregister(&fsi_bus_type);
}
module_init(fsi_init);
module_exit(fsi_exit);

View File

@ -47,12 +47,8 @@ void vmbus_setevent(struct vmbus_channel *channel)
* For channels marked as in "low latency" mode
* bypass the monitor page mechanism.
*/
if ((channel->offermsg.monitor_allocated) &&
(!channel->low_latency)) {
/* Each u32 represents 32 channels */
sync_set_bit(channel->offermsg.child_relid & 31,
(unsigned long *) vmbus_connection.send_int_page +
(channel->offermsg.child_relid >> 5));
if (channel->offermsg.monitor_allocated && !channel->low_latency) {
vmbus_send_interrupt(channel->offermsg.child_relid);
/* Get the child to parent monitor page */
monitorpage = vmbus_connection.monitor_pages[1];
@ -157,6 +153,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
}
init_completion(&open_info->waitevent);
open_info->waiting_channel = newchannel;
open_msg = (struct vmbus_channel_open_channel *)open_info->msg;
open_msg->header.msgtype = CHANNELMSG_OPENCHANNEL;
@ -181,7 +178,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
ret = vmbus_post_msg(open_msg,
sizeof(struct vmbus_channel_open_channel));
sizeof(struct vmbus_channel_open_channel), true);
if (ret != 0) {
err = ret;
@ -194,6 +191,11 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
list_del(&open_info->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
if (newchannel->rescind) {
err = -ENODEV;
goto error_free_gpadl;
}
if (open_info->response.open_result.status) {
err = -EAGAIN;
goto error_free_gpadl;
@ -233,7 +235,7 @@ int vmbus_send_tl_connect_request(const uuid_le *shv_guest_servie_id,
conn_msg.guest_endpoint_id = *shv_guest_servie_id;
conn_msg.host_service_id = *shv_host_servie_id;
return vmbus_post_msg(&conn_msg, sizeof(conn_msg));
return vmbus_post_msg(&conn_msg, sizeof(conn_msg), true);
}
EXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request);
@ -405,6 +407,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
return ret;
init_completion(&msginfo->waitevent);
msginfo->waiting_channel = channel;
gpadlmsg = (struct vmbus_channel_gpadl_header *)msginfo->msg;
gpadlmsg->header.msgtype = CHANNELMSG_GPADL_HEADER;
@ -419,7 +422,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize -
sizeof(*msginfo));
sizeof(*msginfo), true);
if (ret != 0)
goto cleanup;
@ -433,14 +436,19 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
gpadl_body->gpadl = next_gpadl_handle;
ret = vmbus_post_msg(gpadl_body,
submsginfo->msgsize -
sizeof(*submsginfo));
submsginfo->msgsize - sizeof(*submsginfo),
true);
if (ret != 0)
goto cleanup;
}
wait_for_completion(&msginfo->waitevent);
if (channel->rescind) {
ret = -ENODEV;
goto cleanup;
}
/* At this point, we received the gpadl created msg */
*gpadl_handle = gpadlmsg->gpadl;
@ -474,6 +482,7 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
return -ENOMEM;
init_completion(&info->waitevent);
info->waiting_channel = channel;
msg = (struct vmbus_channel_gpadl_teardown *)info->msg;
@ -485,14 +494,19 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
list_add_tail(&info->msglistentry,
&vmbus_connection.chn_msg_list);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
ret = vmbus_post_msg(msg,
sizeof(struct vmbus_channel_gpadl_teardown));
ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_gpadl_teardown),
true);
if (ret)
goto post_msg_err;
wait_for_completion(&info->waitevent);
if (channel->rescind) {
ret = -ENODEV;
goto post_msg_err;
}
post_msg_err:
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&info->msglistentry);
@ -516,7 +530,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
int ret;
/*
* process_chn_event(), running in the tasklet, can race
* vmbus_on_event(), running in the tasklet, can race
* with vmbus_close_internal() in the case of SMP guest, e.g., when
* the former is accessing channel->inbound.ring_buffer, the latter
* could be freeing the ring_buffer pages.
@ -557,7 +571,8 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
msg->header.msgtype = CHANNELMSG_CLOSECHANNEL;
msg->child_relid = channel->offermsg.child_relid;
ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel));
ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel),
true);
if (ret) {
pr_err("Close failed: close post msg return is %d\n", ret);
@ -628,15 +643,14 @@ void vmbus_close(struct vmbus_channel *channel)
EXPORT_SYMBOL_GPL(vmbus_close);
int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
u32 bufferlen, u64 requestid,
enum vmbus_packet_type type, u32 flags, bool kick_q)
u32 bufferlen, u64 requestid,
enum vmbus_packet_type type, u32 flags)
{
struct vmpacket_descriptor desc;
u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen;
u32 packetlen_aligned = ALIGN(packetlen, sizeof(u64));
struct kvec bufferlist[3];
u64 aligned_data = 0;
bool lock = channel->acquire_ring_lock;
int num_vecs = ((bufferlen != 0) ? 3 : 1);
@ -655,9 +669,7 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer,
bufferlist[2].iov_base = &aligned_data;
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
return hv_ringbuffer_write(channel, bufferlist, num_vecs,
lock, kick_q);
return hv_ringbuffer_write(channel, bufferlist, num_vecs);
}
EXPORT_SYMBOL(vmbus_sendpacket_ctl);
@ -680,7 +692,7 @@ int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
enum vmbus_packet_type type, u32 flags)
{
return vmbus_sendpacket_ctl(channel, buffer, bufferlen, requestid,
type, flags, true);
type, flags);
}
EXPORT_SYMBOL(vmbus_sendpacket);
@ -692,11 +704,9 @@ EXPORT_SYMBOL(vmbus_sendpacket);
* explicitly.
*/
int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
struct hv_page_buffer pagebuffers[],
u32 pagecount, void *buffer, u32 bufferlen,
u64 requestid,
u32 flags,
bool kick_q)
struct hv_page_buffer pagebuffers[],
u32 pagecount, void *buffer, u32 bufferlen,
u64 requestid, u32 flags)
{
int i;
struct vmbus_channel_packet_page_buffer desc;
@ -705,12 +715,10 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
u32 packetlen_aligned;
struct kvec bufferlist[3];
u64 aligned_data = 0;
bool lock = channel->acquire_ring_lock;
if (pagecount > MAX_PAGE_BUFFER_COUNT)
return -EINVAL;
/*
* Adjust the size down since vmbus_channel_packet_page_buffer is the
* largest size we support
@ -742,8 +750,7 @@ int vmbus_sendpacket_pagebuffer_ctl(struct vmbus_channel *channel,
bufferlist[2].iov_base = &aligned_data;
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
return hv_ringbuffer_write(channel, bufferlist, 3,
lock, kick_q);
return hv_ringbuffer_write(channel, bufferlist, 3);
}
EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer_ctl);
@ -757,9 +764,10 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
u64 requestid)
{
u32 flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
return vmbus_sendpacket_pagebuffer_ctl(channel, pagebuffers, pagecount,
buffer, bufferlen, requestid,
flags, true);
buffer, bufferlen,
requestid, flags);
}
EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer);
@ -778,7 +786,6 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel,
u32 packetlen_aligned;
struct kvec bufferlist[3];
u64 aligned_data = 0;
bool lock = channel->acquire_ring_lock;
packetlen = desc_size + bufferlen;
packetlen_aligned = ALIGN(packetlen, sizeof(u64));
@ -798,8 +805,7 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel,
bufferlist[2].iov_base = &aligned_data;
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
return hv_ringbuffer_write(channel, bufferlist, 3,
lock, true);
return hv_ringbuffer_write(channel, bufferlist, 3);
}
EXPORT_SYMBOL_GPL(vmbus_sendpacket_mpb_desc);
@ -817,7 +823,6 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
u32 packetlen_aligned;
struct kvec bufferlist[3];
u64 aligned_data = 0;
bool lock = channel->acquire_ring_lock;
u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset,
multi_pagebuffer->len);
@ -856,8 +861,7 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel,
bufferlist[2].iov_base = &aligned_data;
bufferlist[2].iov_len = (packetlen_aligned - packetlen);
return hv_ringbuffer_write(channel, bufferlist, 3,
lock, true);
return hv_ringbuffer_write(channel, bufferlist, 3);
}
EXPORT_SYMBOL_GPL(vmbus_sendpacket_multipagebuffer);

View File

@ -31,6 +31,7 @@
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/hyperv.h>
#include <asm/mshyperv.h>
#include "hyperv_vmbus.h"
@ -147,6 +148,29 @@ static const struct {
{ HV_RDV_GUID },
};
/*
* The rescinded channel may be blocked waiting for a response from the host;
* take care of that.
*/
static void vmbus_rescind_cleanup(struct vmbus_channel *channel)
{
struct vmbus_channel_msginfo *msginfo;
unsigned long flags;
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
msglistentry) {
if (msginfo->waiting_channel == channel) {
complete(&msginfo->waitevent);
break;
}
}
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
}
static bool is_unsupported_vmbus_devs(const uuid_le *guid)
{
int i;
@ -180,33 +204,34 @@ static u16 hv_get_dev_type(const struct vmbus_channel *channel)
* @buf: Raw buffer channel data
*
* @icmsghdrp is of type &struct icmsg_hdr.
* @negop is of type &struct icmsg_negotiate.
* Set up and fill in default negotiate response message.
*
* The fw_version specifies the framework version that
* we can support and srv_version specifies the service
* version we can support.
* The fw_version and fw_vercnt specifies the framework version that
* we can support.
*
* The srv_version and srv_vercnt specifies the service
* versions we can support.
*
* Versions are given in decreasing order.
*
* nego_fw_version and nego_srv_version store the selected protocol versions.
*
* Mainly used by Hyper-V drivers.
*/
bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
struct icmsg_negotiate *negop, u8 *buf,
int fw_version, int srv_version)
u8 *buf, const int *fw_version, int fw_vercnt,
const int *srv_version, int srv_vercnt,
int *nego_fw_version, int *nego_srv_version)
{
int icframe_major, icframe_minor;
int icmsg_major, icmsg_minor;
int fw_major, fw_minor;
int srv_major, srv_minor;
int i;
int i, j;
bool found_match = false;
struct icmsg_negotiate *negop;
icmsghdrp->icmsgsize = 0x10;
fw_major = (fw_version >> 16);
fw_minor = (fw_version & 0xFFFF);
srv_major = (srv_version >> 16);
srv_minor = (srv_version & 0xFFFF);
negop = (struct icmsg_negotiate *)&buf[
sizeof(struct vmbuspipe_hdr) +
sizeof(struct icmsg_hdr)];
@ -222,13 +247,22 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
* support.
*/
for (i = 0; i < negop->icframe_vercnt; i++) {
if ((negop->icversion_data[i].major == fw_major) &&
(negop->icversion_data[i].minor == fw_minor)) {
icframe_major = negop->icversion_data[i].major;
icframe_minor = negop->icversion_data[i].minor;
found_match = true;
for (i = 0; i < fw_vercnt; i++) {
fw_major = (fw_version[i] >> 16);
fw_minor = (fw_version[i] & 0xFFFF);
for (j = 0; j < negop->icframe_vercnt; j++) {
if ((negop->icversion_data[j].major == fw_major) &&
(negop->icversion_data[j].minor == fw_minor)) {
icframe_major = negop->icversion_data[j].major;
icframe_minor = negop->icversion_data[j].minor;
found_match = true;
break;
}
}
if (found_match)
break;
}
if (!found_match)
@ -236,14 +270,26 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
found_match = false;
for (i = negop->icframe_vercnt;
(i < negop->icframe_vercnt + negop->icmsg_vercnt); i++) {
if ((negop->icversion_data[i].major == srv_major) &&
(negop->icversion_data[i].minor == srv_minor)) {
icmsg_major = negop->icversion_data[i].major;
icmsg_minor = negop->icversion_data[i].minor;
found_match = true;
for (i = 0; i < srv_vercnt; i++) {
srv_major = (srv_version[i] >> 16);
srv_minor = (srv_version[i] & 0xFFFF);
for (j = negop->icframe_vercnt;
(j < negop->icframe_vercnt + negop->icmsg_vercnt);
j++) {
if ((negop->icversion_data[j].major == srv_major) &&
(negop->icversion_data[j].minor == srv_minor)) {
icmsg_major = negop->icversion_data[j].major;
icmsg_minor = negop->icversion_data[j].minor;
found_match = true;
break;
}
}
if (found_match)
break;
}
/*
@ -260,6 +306,12 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp,
negop->icmsg_vercnt = 1;
}
if (nego_fw_version)
*nego_fw_version = (icframe_major << 16) | icframe_minor;
if (nego_srv_version)
*nego_srv_version = (icmsg_major << 16) | icmsg_minor;
negop->icversion_data[0].major = icframe_major;
negop->icversion_data[0].minor = icframe_minor;
negop->icversion_data[1].major = icmsg_major;
@ -280,13 +332,15 @@ static struct vmbus_channel *alloc_channel(void)
if (!channel)
return NULL;
channel->acquire_ring_lock = true;
spin_lock_init(&channel->inbound_lock);
spin_lock_init(&channel->lock);
INIT_LIST_HEAD(&channel->sc_list);
INIT_LIST_HEAD(&channel->percpu_list);
tasklet_init(&channel->callback_event,
vmbus_on_event, (unsigned long)channel);
return channel;
}
@ -295,15 +349,17 @@ static struct vmbus_channel *alloc_channel(void)
*/
static void free_channel(struct vmbus_channel *channel)
{
tasklet_kill(&channel->callback_event);
kfree(channel);
}
static void percpu_channel_enq(void *arg)
{
struct vmbus_channel *channel = arg;
int cpu = smp_processor_id();
struct hv_per_cpu_context *hv_cpu
= this_cpu_ptr(hv_context.cpu_context);
list_add_tail(&channel->percpu_list, &hv_context.percpu_list[cpu]);
list_add_tail(&channel->percpu_list, &hv_cpu->chan_list);
}
static void percpu_channel_deq(void *arg)
@ -321,24 +377,21 @@ static void vmbus_release_relid(u32 relid)
memset(&msg, 0, sizeof(struct vmbus_channel_relid_released));
msg.child_relid = relid;
msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released),
true);
}
void hv_event_tasklet_disable(struct vmbus_channel *channel)
{
struct tasklet_struct *tasklet;
tasklet = hv_context.event_dpc[channel->target_cpu];
tasklet_disable(tasklet);
tasklet_disable(&channel->callback_event);
}
void hv_event_tasklet_enable(struct vmbus_channel *channel)
{
struct tasklet_struct *tasklet;
tasklet = hv_context.event_dpc[channel->target_cpu];
tasklet_enable(tasklet);
tasklet_enable(&channel->callback_event);
/* In case there is any pending event */
tasklet_schedule(tasklet);
tasklet_schedule(&channel->callback_event);
}
void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
@ -673,9 +726,12 @@ static void vmbus_wait_for_unload(void)
break;
for_each_online_cpu(cpu) {
page_addr = hv_context.synic_message_page[cpu];
msg = (struct hv_message *)page_addr +
VMBUS_MESSAGE_SINT;
struct hv_per_cpu_context *hv_cpu
= per_cpu_ptr(hv_context.cpu_context, cpu);
page_addr = hv_cpu->synic_message_page;
msg = (struct hv_message *)page_addr
+ VMBUS_MESSAGE_SINT;
message_type = READ_ONCE(msg->header.message_type);
if (message_type == HVMSG_NONE)
@ -699,7 +755,10 @@ static void vmbus_wait_for_unload(void)
* messages after we reconnect.
*/
for_each_online_cpu(cpu) {
page_addr = hv_context.synic_message_page[cpu];
struct hv_per_cpu_context *hv_cpu
= per_cpu_ptr(hv_context.cpu_context, cpu);
page_addr = hv_cpu->synic_message_page;
msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
msg->header.message_type = HVMSG_NONE;
}
@ -728,7 +787,8 @@ void vmbus_initiate_unload(bool crash)
init_completion(&vmbus_connection.unload_event);
memset(&hdr, 0, sizeof(struct vmbus_channel_message_header));
hdr.msgtype = CHANNELMSG_UNLOAD;
vmbus_post_msg(&hdr, sizeof(struct vmbus_channel_message_header));
vmbus_post_msg(&hdr, sizeof(struct vmbus_channel_message_header),
!crash);
/*
* vmbus_initiate_unload() is also called on crash and the crash can be
@ -758,13 +818,6 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
return;
}
/*
* By default we setup state to enable batched
* reading. A specific service can choose to
* disable this prior to opening the channel.
*/
newchannel->batched_reading = true;
/*
* Setup state for signalling the host.
*/
@ -823,6 +876,8 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
channel->rescind = true;
spin_unlock_irqrestore(&channel->lock, flags);
vmbus_rescind_cleanup(channel);
if (channel->device_obj) {
if (channel->chn_rescind_callback) {
channel->chn_rescind_callback(channel);
@ -1116,8 +1171,8 @@ int vmbus_request_offers(void)
msg->msgtype = CHANNELMSG_REQUESTOFFERS;
ret = vmbus_post_msg(msg,
sizeof(struct vmbus_channel_message_header));
ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_message_header),
true);
if (ret != 0) {
pr_err("Unable to request offers - %d\n", ret);

View File

@ -93,12 +93,10 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
* all the CPUs. This is needed for kexec to work correctly where
* the CPU attempting to connect may not be CPU 0.
*/
if (version >= VERSION_WIN8_1) {
msg->target_vcpu = hv_context.vp_index[get_cpu()];
put_cpu();
} else {
if (version >= VERSION_WIN8_1)
msg->target_vcpu = hv_context.vp_index[smp_processor_id()];
else
msg->target_vcpu = 0;
}
/*
* Add to list before we send the request since we may
@ -111,7 +109,8 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
ret = vmbus_post_msg(msg,
sizeof(struct vmbus_channel_initiate_contact));
sizeof(struct vmbus_channel_initiate_contact),
true);
if (ret != 0) {
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&msginfo->msglistentry);
@ -220,11 +219,8 @@ int vmbus_connect(void)
goto cleanup;
vmbus_proto_version = version;
pr_info("Hyper-V Host Build:%d-%d.%d-%d-%d.%d; Vmbus version:%d.%d\n",
host_info_eax, host_info_ebx >> 16,
host_info_ebx & 0xFFFF, host_info_ecx,
host_info_edx >> 24, host_info_edx & 0xFFFFFF,
version >> 16, version & 0xFFFF);
pr_info("Vmbus version:%d.%d\n",
version >> 16, version & 0xFFFF);
kfree(msginfo);
return 0;
@ -263,29 +259,6 @@ void vmbus_disconnect(void)
vmbus_connection.monitor_pages[1] = NULL;
}
/*
* Map the given relid to the corresponding channel based on the
* per-cpu list of channels that have been affinitized to this CPU.
* This will be used in the channel callback path as we can do this
* mapping in a lock-free fashion.
*/
static struct vmbus_channel *pcpu_relid2channel(u32 relid)
{
struct vmbus_channel *channel;
struct vmbus_channel *found_channel = NULL;
int cpu = smp_processor_id();
struct list_head *pcpu_head = &hv_context.percpu_list[cpu];
list_for_each_entry(channel, pcpu_head, percpu_list) {
if (channel->offermsg.child_relid == relid) {
found_channel = channel;
break;
}
}
return found_channel;
}
/*
* relid2channel - Get the channel object given its
* child relative id (ie channel id)
@ -322,23 +295,12 @@ struct vmbus_channel *relid2channel(u32 relid)
}
/*
* process_chn_event - Process a channel event notification
* vmbus_on_event - Process a channel event notification
*/
static void process_chn_event(u32 relid)
void vmbus_on_event(unsigned long data)
{
struct vmbus_channel *channel;
void *arg;
bool read_state;
u32 bytes_to_read;
/*
* Find the channel based on this relid and invokes the
* channel callback to process the event
*/
channel = pcpu_relid2channel(relid);
if (!channel)
return;
struct vmbus_channel *channel = (void *) data;
void (*callback_fn)(void *);
/*
* A channel once created is persistent even when there
@ -348,10 +310,13 @@ static void process_chn_event(u32 relid)
* Thus, checking and invoking the driver specific callback takes
* care of orderly unloading of the driver.
*/
callback_fn = READ_ONCE(channel->onchannel_callback);
if (unlikely(callback_fn == NULL))
return;
if (channel->onchannel_callback != NULL) {
arg = channel->channel_callback_context;
read_state = channel->batched_reading;
(*callback_fn)(channel->channel_callback_context);
if (channel->callback_mode == HV_CALL_BATCHED) {
/*
* This callback reads the messages sent by the host.
* We can optimize host to guest signaling by ensuring:
@ -363,71 +328,10 @@ static void process_chn_event(u32 relid)
* state is set we check to see if additional packets are
* available to read. In this case we repeat the process.
*/
if (hv_end_read(&channel->inbound) != 0) {
hv_begin_read(&channel->inbound);
do {
if (read_state)
hv_begin_read(&channel->inbound);
channel->onchannel_callback(arg);
if (read_state)
bytes_to_read = hv_end_read(&channel->inbound);
else
bytes_to_read = 0;
} while (read_state && (bytes_to_read != 0));
}
}
/*
* vmbus_on_event - Handler for events
*/
void vmbus_on_event(unsigned long data)
{
u32 dword;
u32 maxdword;
int bit;
u32 relid;
u32 *recv_int_page = NULL;
void *page_addr;
int cpu = smp_processor_id();
union hv_synic_event_flags *event;
if (vmbus_proto_version < VERSION_WIN8) {
maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5;
recv_int_page = vmbus_connection.recv_int_page;
} else {
/*
* When the host is win8 and beyond, the event page
* can be directly checked to get the id of the channel
* that has the interrupt pending.
*/
maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
page_addr = hv_context.synic_event_page[cpu];
event = (union hv_synic_event_flags *)page_addr +
VMBUS_MESSAGE_SINT;
recv_int_page = event->flags32;
}
/* Check events */
if (!recv_int_page)
return;
for (dword = 0; dword < maxdword; dword++) {
if (!recv_int_page[dword])
continue;
for (bit = 0; bit < 32; bit++) {
if (sync_test_and_clear_bit(bit,
(unsigned long *)&recv_int_page[dword])) {
relid = (dword << 5) + bit;
if (relid == 0)
/*
* Special case - vmbus
* channel protocol msg
*/
continue;
process_chn_event(relid);
}
tasklet_schedule(&channel->callback_event);
}
}
}
@ -435,7 +339,7 @@ void vmbus_on_event(unsigned long data)
/*
* vmbus_post_msg - Send a msg on the vmbus's message connection
*/
int vmbus_post_msg(void *buffer, size_t buflen)
int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
{
union hv_connection_id conn_id;
int ret = 0;
@ -450,7 +354,7 @@ int vmbus_post_msg(void *buffer, size_t buflen)
* insufficient resources. Retry the operation a couple of
* times before giving up.
*/
while (retries < 20) {
while (retries < 100) {
ret = hv_post_message(conn_id, 1, buffer, buflen);
switch (ret) {
@ -473,8 +377,14 @@ int vmbus_post_msg(void *buffer, size_t buflen)
}
retries++;
udelay(usec);
if (usec < 2048)
if (can_sleep && usec > 1000)
msleep(usec / 1000);
else if (usec < MAX_UDELAY_MS * 1000)
udelay(usec);
else
mdelay(usec / 1000);
if (usec < 256000)
usec *= 2;
}
return ret;
@ -487,12 +397,8 @@ void vmbus_set_event(struct vmbus_channel *channel)
{
u32 child_relid = channel->offermsg.child_relid;
if (!channel->is_dedicated_interrupt) {
/* Each u32 represents 32 channels */
sync_set_bit(child_relid & 31,
(unsigned long *)vmbus_connection.send_int_page +
(child_relid >> 5));
}
if (!channel->is_dedicated_interrupt)
vmbus_send_interrupt(child_relid);
hv_do_hypercall(HVCALL_SIGNAL_EVENT, channel->sig_event, NULL);
}

View File

@ -36,154 +36,12 @@
/* The one and only */
struct hv_context hv_context = {
.synic_initialized = false,
.hypercall_page = NULL,
};
#define HV_TIMER_FREQUENCY (10 * 1000 * 1000) /* 100ns period */
#define HV_MAX_MAX_DELTA_TICKS 0xffffffff
#define HV_MIN_DELTA_TICKS 1
/*
* query_hypervisor_info - Get version info of the windows hypervisor
*/
unsigned int host_info_eax;
unsigned int host_info_ebx;
unsigned int host_info_ecx;
unsigned int host_info_edx;
static int query_hypervisor_info(void)
{
unsigned int eax;
unsigned int ebx;
unsigned int ecx;
unsigned int edx;
unsigned int max_leaf;
unsigned int op;
/*
* Its assumed that this is called after confirming that Viridian
* is present. Query id and revision.
*/
eax = 0;
ebx = 0;
ecx = 0;
edx = 0;
op = HVCPUID_VENDOR_MAXFUNCTION;
cpuid(op, &eax, &ebx, &ecx, &edx);
max_leaf = eax;
if (max_leaf >= HVCPUID_VERSION) {
eax = 0;
ebx = 0;
ecx = 0;
edx = 0;
op = HVCPUID_VERSION;
cpuid(op, &eax, &ebx, &ecx, &edx);
host_info_eax = eax;
host_info_ebx = ebx;
host_info_ecx = ecx;
host_info_edx = edx;
}
return max_leaf;
}
/*
* hv_do_hypercall- Invoke the specified hypercall
*/
u64 hv_do_hypercall(u64 control, void *input, void *output)
{
u64 input_address = (input) ? virt_to_phys(input) : 0;
u64 output_address = (output) ? virt_to_phys(output) : 0;
void *hypercall_page = hv_context.hypercall_page;
#ifdef CONFIG_X86_64
u64 hv_status = 0;
if (!hypercall_page)
return (u64)ULLONG_MAX;
__asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8");
__asm__ __volatile__("call *%3" : "=a" (hv_status) :
"c" (control), "d" (input_address),
"m" (hypercall_page));
return hv_status;
#else
u32 control_hi = control >> 32;
u32 control_lo = control & 0xFFFFFFFF;
u32 hv_status_hi = 1;
u32 hv_status_lo = 1;
u32 input_address_hi = input_address >> 32;
u32 input_address_lo = input_address & 0xFFFFFFFF;
u32 output_address_hi = output_address >> 32;
u32 output_address_lo = output_address & 0xFFFFFFFF;
if (!hypercall_page)
return (u64)ULLONG_MAX;
__asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi),
"=a"(hv_status_lo) : "d" (control_hi),
"a" (control_lo), "b" (input_address_hi),
"c" (input_address_lo), "D"(output_address_hi),
"S"(output_address_lo), "m" (hypercall_page));
return hv_status_lo | ((u64)hv_status_hi << 32);
#endif /* !x86_64 */
}
EXPORT_SYMBOL_GPL(hv_do_hypercall);
#ifdef CONFIG_X86_64
static u64 read_hv_clock_tsc(struct clocksource *arg)
{
u64 current_tick;
struct ms_hyperv_tsc_page *tsc_pg = hv_context.tsc_page;
if (tsc_pg->tsc_sequence != 0) {
/*
* Use the tsc page to compute the value.
*/
while (1) {
u64 tmp;
u32 sequence = tsc_pg->tsc_sequence;
u64 cur_tsc;
u64 scale = tsc_pg->tsc_scale;
s64 offset = tsc_pg->tsc_offset;
rdtscll(cur_tsc);
/* current_tick = ((cur_tsc *scale) >> 64) + offset */
asm("mulq %3"
: "=d" (current_tick), "=a" (tmp)
: "a" (cur_tsc), "r" (scale));
current_tick += offset;
if (tsc_pg->tsc_sequence == sequence)
return current_tick;
if (tsc_pg->tsc_sequence != 0)
continue;
/*
* Fallback using MSR method.
*/
break;
}
}
rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick);
return current_tick;
}
static struct clocksource hyperv_cs_tsc = {
.name = "hyperv_clocksource_tsc_page",
.rating = 425,
.read = read_hv_clock_tsc,
.mask = CLOCKSOURCE_MASK(64),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
#endif
/*
* hv_init - Main initialization routine.
*
@ -191,129 +49,14 @@ static struct clocksource hyperv_cs_tsc = {
*/
int hv_init(void)
{
int max_leaf;
union hv_x64_msr_hypercall_contents hypercall_msr;
void *virtaddr = NULL;
if (!hv_is_hypercall_page_setup())
return -ENOTSUPP;
memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS);
memset(hv_context.synic_message_page, 0,
sizeof(void *) * NR_CPUS);
memset(hv_context.post_msg_page, 0,
sizeof(void *) * NR_CPUS);
memset(hv_context.vp_index, 0,
sizeof(int) * NR_CPUS);
memset(hv_context.event_dpc, 0,
sizeof(void *) * NR_CPUS);
memset(hv_context.msg_dpc, 0,
sizeof(void *) * NR_CPUS);
memset(hv_context.clk_evt, 0,
sizeof(void *) * NR_CPUS);
hv_context.cpu_context = alloc_percpu(struct hv_per_cpu_context);
if (!hv_context.cpu_context)
return -ENOMEM;
max_leaf = query_hypervisor_info();
/*
* Write our OS ID.
*/
hv_context.guestid = generate_guest_id(0, LINUX_VERSION_CODE, 0);
wrmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid);
/* See if the hypercall page is already set */
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
virtaddr = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL_EXEC);
if (!virtaddr)
goto cleanup;
hypercall_msr.enable = 1;
hypercall_msr.guest_physical_address = vmalloc_to_pfn(virtaddr);
wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
/* Confirm that hypercall page did get setup. */
hypercall_msr.as_uint64 = 0;
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
if (!hypercall_msr.enable)
goto cleanup;
hv_context.hypercall_page = virtaddr;
#ifdef CONFIG_X86_64
if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) {
union hv_x64_msr_hypercall_contents tsc_msr;
void *va_tsc;
va_tsc = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL);
if (!va_tsc)
goto cleanup;
hv_context.tsc_page = va_tsc;
rdmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64);
tsc_msr.enable = 1;
tsc_msr.guest_physical_address = vmalloc_to_pfn(va_tsc);
wrmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64);
clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100);
}
#endif
return 0;
cleanup:
if (virtaddr) {
if (hypercall_msr.enable) {
hypercall_msr.as_uint64 = 0;
wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
}
vfree(virtaddr);
}
return -ENOTSUPP;
}
/*
* hv_cleanup - Cleanup routine.
*
* This routine is called normally during driver unloading or exiting.
*/
void hv_cleanup(bool crash)
{
union hv_x64_msr_hypercall_contents hypercall_msr;
/* Reset our OS id */
wrmsrl(HV_X64_MSR_GUEST_OS_ID, 0);
if (hv_context.hypercall_page) {
hypercall_msr.as_uint64 = 0;
wrmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
if (!crash)
vfree(hv_context.hypercall_page);
hv_context.hypercall_page = NULL;
}
#ifdef CONFIG_X86_64
/*
* Cleanup the TSC page based CS.
*/
if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) {
/*
* Crash can happen in an interrupt context and unregistering
* a clocksource is impossible and redundant in this case.
*/
if (!oops_in_progress) {
clocksource_change_rating(&hyperv_cs_tsc, 10);
clocksource_unregister(&hyperv_cs_tsc);
}
hypercall_msr.as_uint64 = 0;
wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64);
if (!crash)
vfree(hv_context.tsc_page);
hv_context.tsc_page = NULL;
}
#endif
}
/*
@ -325,25 +68,24 @@ int hv_post_message(union hv_connection_id connection_id,
enum hv_message_type message_type,
void *payload, size_t payload_size)
{
struct hv_input_post_message *aligned_msg;
struct hv_per_cpu_context *hv_cpu;
u64 status;
if (payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
return -EMSGSIZE;
aligned_msg = (struct hv_input_post_message *)
hv_context.post_msg_page[get_cpu()];
hv_cpu = get_cpu_ptr(hv_context.cpu_context);
aligned_msg = hv_cpu->post_msg_page;
aligned_msg->connectionid = connection_id;
aligned_msg->reserved = 0;
aligned_msg->message_type = message_type;
aligned_msg->payload_size = payload_size;
memcpy((void *)aligned_msg->payload, payload, payload_size);
put_cpu_ptr(hv_cpu);
status = hv_do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL);
put_cpu();
return status & 0xFFFF;
}
@ -354,16 +96,16 @@ static int hv_ce_set_next_event(unsigned long delta,
WARN_ON(!clockevent_state_oneshot(evt));
rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick);
hv_get_current_tick(current_tick);
current_tick += delta;
wrmsrl(HV_X64_MSR_STIMER0_COUNT, current_tick);
hv_init_timer(HV_X64_MSR_STIMER0_COUNT, current_tick);
return 0;
}
static int hv_ce_shutdown(struct clock_event_device *evt)
{
wrmsrl(HV_X64_MSR_STIMER0_COUNT, 0);
wrmsrl(HV_X64_MSR_STIMER0_CONFIG, 0);
hv_init_timer(HV_X64_MSR_STIMER0_COUNT, 0);
hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, 0);
return 0;
}
@ -375,7 +117,7 @@ static int hv_ce_set_oneshot(struct clock_event_device *evt)
timer_cfg.enable = 1;
timer_cfg.auto_enable = 1;
timer_cfg.sintx = VMBUS_MESSAGE_SINT;
wrmsrl(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64);
hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64);
return 0;
}
@ -400,8 +142,6 @@ static void hv_init_clockevent_device(struct clock_event_device *dev, int cpu)
int hv_synic_alloc(void)
{
size_t size = sizeof(struct tasklet_struct);
size_t ced_size = sizeof(struct clock_event_device);
int cpu;
hv_context.hv_numa_map = kzalloc(sizeof(struct cpumask) * nr_node_ids,
@ -411,52 +151,42 @@ int hv_synic_alloc(void)
goto err;
}
for_each_online_cpu(cpu) {
hv_context.event_dpc[cpu] = kmalloc(size, GFP_ATOMIC);
if (hv_context.event_dpc[cpu] == NULL) {
pr_err("Unable to allocate event dpc\n");
goto err;
}
tasklet_init(hv_context.event_dpc[cpu], vmbus_on_event, cpu);
for_each_present_cpu(cpu) {
struct hv_per_cpu_context *hv_cpu
= per_cpu_ptr(hv_context.cpu_context, cpu);
hv_context.msg_dpc[cpu] = kmalloc(size, GFP_ATOMIC);
if (hv_context.msg_dpc[cpu] == NULL) {
pr_err("Unable to allocate event dpc\n");
goto err;
}
tasklet_init(hv_context.msg_dpc[cpu], vmbus_on_msg_dpc, cpu);
memset(hv_cpu, 0, sizeof(*hv_cpu));
tasklet_init(&hv_cpu->msg_dpc,
vmbus_on_msg_dpc, (unsigned long) hv_cpu);
hv_context.clk_evt[cpu] = kzalloc(ced_size, GFP_ATOMIC);
if (hv_context.clk_evt[cpu] == NULL) {
hv_cpu->clk_evt = kzalloc(sizeof(struct clock_event_device),
GFP_KERNEL);
if (hv_cpu->clk_evt == NULL) {
pr_err("Unable to allocate clock event device\n");
goto err;
}
hv_init_clockevent_device(hv_cpu->clk_evt, cpu);
hv_init_clockevent_device(hv_context.clk_evt[cpu], cpu);
hv_context.synic_message_page[cpu] =
hv_cpu->synic_message_page =
(void *)get_zeroed_page(GFP_ATOMIC);
if (hv_context.synic_message_page[cpu] == NULL) {
if (hv_cpu->synic_message_page == NULL) {
pr_err("Unable to allocate SYNIC message page\n");
goto err;
}
hv_context.synic_event_page[cpu] =
(void *)get_zeroed_page(GFP_ATOMIC);
if (hv_context.synic_event_page[cpu] == NULL) {
hv_cpu->synic_event_page = (void *)get_zeroed_page(GFP_ATOMIC);
if (hv_cpu->synic_event_page == NULL) {
pr_err("Unable to allocate SYNIC event page\n");
goto err;
}
hv_context.post_msg_page[cpu] =
(void *)get_zeroed_page(GFP_ATOMIC);
if (hv_context.post_msg_page[cpu] == NULL) {
hv_cpu->post_msg_page = (void *)get_zeroed_page(GFP_ATOMIC);
if (hv_cpu->post_msg_page == NULL) {
pr_err("Unable to allocate post msg page\n");
goto err;
}
INIT_LIST_HEAD(&hv_cpu->chan_list);
}
return 0;
@ -464,26 +194,24 @@ int hv_synic_alloc(void)
return -ENOMEM;
}
static void hv_synic_free_cpu(int cpu)
{
kfree(hv_context.event_dpc[cpu]);
kfree(hv_context.msg_dpc[cpu]);
kfree(hv_context.clk_evt[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]);
if (hv_context.post_msg_page[cpu])
free_page((unsigned long)hv_context.post_msg_page[cpu]);
}
void hv_synic_free(void)
{
int cpu;
for_each_present_cpu(cpu) {
struct hv_per_cpu_context *hv_cpu
= per_cpu_ptr(hv_context.cpu_context, cpu);
if (hv_cpu->synic_event_page)
free_page((unsigned long)hv_cpu->synic_event_page);
if (hv_cpu->synic_message_page)
free_page((unsigned long)hv_cpu->synic_message_page);
if (hv_cpu->post_msg_page)
free_page((unsigned long)hv_cpu->post_msg_page);
}
kfree(hv_context.hv_numa_map);
for_each_online_cpu(cpu)
hv_synic_free_cpu(cpu);
}
/*
@ -493,54 +221,49 @@ void hv_synic_free(void)
* retrieve the initialized message and event pages. Otherwise, we create and
* initialize the message and event pages.
*/
void hv_synic_init(void *arg)
int hv_synic_init(unsigned int cpu)
{
u64 version;
struct hv_per_cpu_context *hv_cpu
= per_cpu_ptr(hv_context.cpu_context, cpu);
union hv_synic_simp simp;
union hv_synic_siefp siefp;
union hv_synic_sint shared_sint;
union hv_synic_scontrol sctrl;
u64 vp_index;
int cpu = smp_processor_id();
if (!hv_context.hypercall_page)
return;
/* Check the version */
rdmsrl(HV_X64_MSR_SVERSION, version);
/* Setup the Synic's message page */
rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64);
hv_get_simp(simp.as_uint64);
simp.simp_enabled = 1;
simp.base_simp_gpa = virt_to_phys(hv_context.synic_message_page[cpu])
simp.base_simp_gpa = virt_to_phys(hv_cpu->synic_message_page)
>> PAGE_SHIFT;
wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64);
hv_set_simp(simp.as_uint64);
/* Setup the Synic's event page */
rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
hv_get_siefp(siefp.as_uint64);
siefp.siefp_enabled = 1;
siefp.base_siefp_gpa = virt_to_phys(hv_context.synic_event_page[cpu])
siefp.base_siefp_gpa = virt_to_phys(hv_cpu->synic_event_page)
>> PAGE_SHIFT;
wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
hv_set_siefp(siefp.as_uint64);
/* Setup the shared SINT. */
rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
hv_get_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT,
shared_sint.as_uint64);
shared_sint.as_uint64 = 0;
shared_sint.vector = HYPERVISOR_CALLBACK_VECTOR;
shared_sint.masked = false;
shared_sint.auto_eoi = true;
wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
hv_set_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT,
shared_sint.as_uint64);
/* Enable the global synic bit */
rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
hv_get_synic_state(sctrl.as_uint64);
sctrl.enable = 1;
wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
hv_set_synic_state(sctrl.as_uint64);
hv_context.synic_initialized = true;
@ -549,20 +272,18 @@ void hv_synic_init(void *arg)
* of cpuid and Linux' notion of cpuid.
* This array will be indexed using Linux cpuid.
*/
rdmsrl(HV_X64_MSR_VP_INDEX, vp_index);
hv_get_vp_index(vp_index);
hv_context.vp_index[cpu] = (u32)vp_index;
INIT_LIST_HEAD(&hv_context.percpu_list[cpu]);
/*
* Register the per-cpu clockevent source.
*/
if (ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE)
clockevents_config_and_register(hv_context.clk_evt[cpu],
clockevents_config_and_register(hv_cpu->clk_evt,
HV_TIMER_FREQUENCY,
HV_MIN_DELTA_TICKS,
HV_MAX_MAX_DELTA_TICKS);
return;
return 0;
}
/*
@ -575,52 +296,94 @@ void hv_synic_clockevents_cleanup(void)
if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE))
return;
for_each_present_cpu(cpu)
clockevents_unbind_device(hv_context.clk_evt[cpu], cpu);
for_each_present_cpu(cpu) {
struct hv_per_cpu_context *hv_cpu
= per_cpu_ptr(hv_context.cpu_context, cpu);
clockevents_unbind_device(hv_cpu->clk_evt, cpu);
}
}
/*
* hv_synic_cleanup - Cleanup routine for hv_synic_init().
*/
void hv_synic_cleanup(void *arg)
int hv_synic_cleanup(unsigned int cpu)
{
union hv_synic_sint shared_sint;
union hv_synic_simp simp;
union hv_synic_siefp siefp;
union hv_synic_scontrol sctrl;
int cpu = smp_processor_id();
struct vmbus_channel *channel, *sc;
bool channel_found = false;
unsigned long flags;
if (!hv_context.synic_initialized)
return;
return -EFAULT;
/*
* Search for channels which are bound to the CPU we're about to
* cleanup. In case we find one and vmbus is still connected we need to
* fail, this will effectively prevent CPU offlining. There is no way
* we can re-bind channels to different CPUs for now.
*/
mutex_lock(&vmbus_connection.channel_mutex);
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
if (channel->target_cpu == cpu) {
channel_found = true;
break;
}
spin_lock_irqsave(&channel->lock, flags);
list_for_each_entry(sc, &channel->sc_list, sc_list) {
if (sc->target_cpu == cpu) {
channel_found = true;
break;
}
}
spin_unlock_irqrestore(&channel->lock, flags);
if (channel_found)
break;
}
mutex_unlock(&vmbus_connection.channel_mutex);
if (channel_found && vmbus_connection.conn_state == CONNECTED)
return -EBUSY;
/* Turn off clockevent device */
if (ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE) {
clockevents_unbind_device(hv_context.clk_evt[cpu], cpu);
hv_ce_shutdown(hv_context.clk_evt[cpu]);
struct hv_per_cpu_context *hv_cpu
= this_cpu_ptr(hv_context.cpu_context);
clockevents_unbind_device(hv_cpu->clk_evt, cpu);
hv_ce_shutdown(hv_cpu->clk_evt);
put_cpu_ptr(hv_cpu);
}
rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
hv_get_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT,
shared_sint.as_uint64);
shared_sint.masked = 1;
/* Need to correctly cleanup in the case of SMP!!! */
/* Disable the interrupt */
wrmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64);
hv_set_synint_state(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT,
shared_sint.as_uint64);
rdmsrl(HV_X64_MSR_SIMP, simp.as_uint64);
hv_get_simp(simp.as_uint64);
simp.simp_enabled = 0;
simp.base_simp_gpa = 0;
wrmsrl(HV_X64_MSR_SIMP, simp.as_uint64);
hv_set_simp(simp.as_uint64);
rdmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
hv_get_siefp(siefp.as_uint64);
siefp.siefp_enabled = 0;
siefp.base_siefp_gpa = 0;
wrmsrl(HV_X64_MSR_SIEFP, siefp.as_uint64);
hv_set_siefp(siefp.as_uint64);
/* Disable the global synic bit */
rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
hv_get_synic_state(sctrl.as_uint64);
sctrl.enable = 0;
wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64);
hv_set_synic_state(sctrl.as_uint64);
return 0;
}

View File

@ -587,6 +587,7 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val,
spin_lock_irqsave(&dm_device.ha_lock, flags);
dm_device.num_pages_onlined += mem->nr_pages;
spin_unlock_irqrestore(&dm_device.ha_lock, flags);
/* Fall through */
case MEM_CANCEL_ONLINE:
if (dm_device.ha_waiting) {
dm_device.ha_waiting = false;

View File

@ -31,6 +31,16 @@
#define WIN8_SRV_MINOR 1
#define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
#define FCOPY_VER_COUNT 1
static const int fcopy_versions[] = {
WIN8_SRV_VERSION
};
#define FW_VER_COUNT 1
static const int fw_versions[] = {
UTIL_FW_VERSION
};
/*
* Global state maintained for transaction that is being processed.
* For a class of integration services, including the "file copy service",
@ -61,6 +71,7 @@ static DECLARE_WORK(fcopy_send_work, fcopy_send_data);
static const char fcopy_devname[] = "vmbus/hv_fcopy";
static u8 *recv_buffer;
static struct hvutil_transport *hvt;
static struct completion release_event;
/*
* This state maintains the version number registered by the daemon.
*/
@ -227,8 +238,6 @@ void hv_fcopy_onchannelcallback(void *context)
u64 requestid;
struct hv_fcopy_hdr *fcopy_msg;
struct icmsg_hdr *icmsghdr;
struct icmsg_negotiate *negop = NULL;
int util_fw_version;
int fcopy_srv_version;
if (fcopy_transaction.state > HVUTIL_READY)
@ -242,10 +251,15 @@ void hv_fcopy_onchannelcallback(void *context)
icmsghdr = (struct icmsg_hdr *)&recv_buffer[
sizeof(struct vmbuspipe_hdr)];
if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) {
util_fw_version = UTIL_FW_VERSION;
fcopy_srv_version = WIN8_SRV_VERSION;
vmbus_prep_negotiate_resp(icmsghdr, negop, recv_buffer,
util_fw_version, fcopy_srv_version);
if (vmbus_prep_negotiate_resp(icmsghdr, recv_buffer,
fw_versions, FW_VER_COUNT,
fcopy_versions, FCOPY_VER_COUNT,
NULL, &fcopy_srv_version)) {
pr_info("FCopy IC version %d.%d\n",
fcopy_srv_version >> 16,
fcopy_srv_version & 0xFFFF);
}
} else {
fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[
sizeof(struct vmbuspipe_hdr) +
@ -317,6 +331,7 @@ static void fcopy_on_reset(void)
if (cancel_delayed_work_sync(&fcopy_timeout_work))
fcopy_respond_to_host(HV_E_FAIL);
complete(&release_event);
}
int hv_fcopy_init(struct hv_util_service *srv)
@ -324,6 +339,7 @@ int hv_fcopy_init(struct hv_util_service *srv)
recv_buffer = srv->recv_buffer;
fcopy_transaction.recv_channel = srv->channel;
init_completion(&release_event);
/*
* When this driver loads, the user level daemon that
* processes the host requests may not yet be running.
@ -345,4 +361,5 @@ void hv_fcopy_deinit(void)
fcopy_transaction.state = HVUTIL_DEVICE_DYING;
cancel_delayed_work_sync(&fcopy_timeout_work);
hvutil_transport_destroy(hvt);
wait_for_completion(&release_event);
}

View File

@ -46,6 +46,19 @@
#define WIN8_SRV_MINOR 0
#define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
#define KVP_VER_COUNT 3
static const int kvp_versions[] = {
WIN8_SRV_VERSION,
WIN7_SRV_VERSION,
WS2008_SRV_VERSION
};
#define FW_VER_COUNT 2
static const int fw_versions[] = {
UTIL_FW_VERSION,
UTIL_WS2K8_FW_VERSION
};
/*
* Global state maintained for transaction that is being processed. For a class
* of integration services, including the "KVP service", the specified protocol
@ -88,6 +101,7 @@ static DECLARE_WORK(kvp_sendkey_work, kvp_send_key);
static const char kvp_devname[] = "vmbus/hv_kvp";
static u8 *recv_buffer;
static struct hvutil_transport *hvt;
static struct completion release_event;
/*
* Register the kernel component with the user-level daemon.
* As part of this registration, pass the LIC version number.
@ -609,8 +623,6 @@ void hv_kvp_onchannelcallback(void *context)
struct hv_kvp_msg *kvp_msg;
struct icmsg_hdr *icmsghdrp;
struct icmsg_negotiate *negop = NULL;
int util_fw_version;
int kvp_srv_version;
static enum {NEGO_NOT_STARTED,
NEGO_IN_PROGRESS,
@ -639,28 +651,14 @@ void hv_kvp_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
/*
* Based on the host, select appropriate
* framework and service versions we will
* negotiate.
*/
switch (vmbus_proto_version) {
case (VERSION_WS2008):
util_fw_version = UTIL_WS2K8_FW_VERSION;
kvp_srv_version = WS2008_SRV_VERSION;
break;
case (VERSION_WIN7):
util_fw_version = UTIL_FW_VERSION;
kvp_srv_version = WIN7_SRV_VERSION;
break;
default:
util_fw_version = UTIL_FW_VERSION;
kvp_srv_version = WIN8_SRV_VERSION;
if (vmbus_prep_negotiate_resp(icmsghdrp,
recv_buffer, fw_versions, FW_VER_COUNT,
kvp_versions, KVP_VER_COUNT,
NULL, &kvp_srv_version)) {
pr_info("KVP IC version %d.%d\n",
kvp_srv_version >> 16,
kvp_srv_version & 0xFFFF);
}
vmbus_prep_negotiate_resp(icmsghdrp, negop,
recv_buffer, util_fw_version,
kvp_srv_version);
} else {
kvp_msg = (struct hv_kvp_msg *)&recv_buffer[
sizeof(struct vmbuspipe_hdr) +
@ -716,6 +714,7 @@ static void kvp_on_reset(void)
if (cancel_delayed_work_sync(&kvp_timeout_work))
kvp_respond_to_host(NULL, HV_E_FAIL);
kvp_transaction.state = HVUTIL_DEVICE_INIT;
complete(&release_event);
}
int
@ -724,6 +723,7 @@ hv_kvp_init(struct hv_util_service *srv)
recv_buffer = srv->recv_buffer;
kvp_transaction.recv_channel = srv->channel;
init_completion(&release_event);
/*
* When this driver loads, the user level daemon that
* processes the host requests may not yet be running.
@ -747,4 +747,5 @@ void hv_kvp_deinit(void)
cancel_delayed_work_sync(&kvp_timeout_work);
cancel_work_sync(&kvp_sendkey_work);
hvutil_transport_destroy(hvt);
wait_for_completion(&release_event);
}

View File

@ -31,6 +31,16 @@
#define VSS_MINOR 0
#define VSS_VERSION (VSS_MAJOR << 16 | VSS_MINOR)
#define VSS_VER_COUNT 1
static const int vss_versions[] = {
VSS_VERSION
};
#define FW_VER_COUNT 1
static const int fw_versions[] = {
UTIL_FW_VERSION
};
/*
* Timeout values are based on expecations from host
*/
@ -69,6 +79,7 @@ static int dm_reg_value;
static const char vss_devname[] = "vmbus/hv_vss";
static __u8 *recv_buffer;
static struct hvutil_transport *hvt;
static struct completion release_event;
static void vss_timeout_func(struct work_struct *dummy);
static void vss_handle_request(struct work_struct *dummy);
@ -293,10 +304,9 @@ void hv_vss_onchannelcallback(void *context)
u32 recvlen;
u64 requestid;
struct hv_vss_msg *vss_msg;
int vss_srv_version;
struct icmsg_hdr *icmsghdrp;
struct icmsg_negotiate *negop = NULL;
if (vss_transaction.state > HVUTIL_READY)
return;
@ -309,9 +319,15 @@ void hv_vss_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, negop,
recv_buffer, UTIL_FW_VERSION,
VSS_VERSION);
if (vmbus_prep_negotiate_resp(icmsghdrp,
recv_buffer, fw_versions, FW_VER_COUNT,
vss_versions, VSS_VER_COUNT,
NULL, &vss_srv_version)) {
pr_info("VSS IC version %d.%d\n",
vss_srv_version >> 16,
vss_srv_version & 0xFFFF);
}
} else {
vss_msg = (struct hv_vss_msg *)&recv_buffer[
sizeof(struct vmbuspipe_hdr) +
@ -345,11 +361,13 @@ static void vss_on_reset(void)
if (cancel_delayed_work_sync(&vss_timeout_work))
vss_respond_to_host(HV_E_FAIL);
vss_transaction.state = HVUTIL_DEVICE_INIT;
complete(&release_event);
}
int
hv_vss_init(struct hv_util_service *srv)
{
init_completion(&release_event);
if (vmbus_proto_version < VERSION_WIN8_1) {
pr_warn("Integration service 'Backup (volume snapshot)'"
" not supported on this host version.\n");
@ -382,4 +400,5 @@ void hv_vss_deinit(void)
cancel_delayed_work_sync(&vss_timeout_work);
cancel_work_sync(&vss_handle_request_work);
hvutil_transport_destroy(hvt);
wait_for_completion(&release_event);
}

View File

@ -27,6 +27,9 @@
#include <linux/sysctl.h>
#include <linux/reboot.h>
#include <linux/hyperv.h>
#include <linux/clockchips.h>
#include <linux/ptp_clock_kernel.h>
#include <asm/mshyperv.h>
#include "hyperv_vmbus.h"
@ -57,7 +60,31 @@
static int sd_srv_version;
static int ts_srv_version;
static int hb_srv_version;
static int util_fw_version;
#define SD_VER_COUNT 2
static const int sd_versions[] = {
SD_VERSION,
SD_VERSION_1
};
#define TS_VER_COUNT 3
static const int ts_versions[] = {
TS_VERSION,
TS_VERSION_3,
TS_VERSION_1
};
#define HB_VER_COUNT 2
static const int hb_versions[] = {
HB_VERSION,
HB_VERSION_1
};
#define FW_VER_COUNT 2
static const int fw_versions[] = {
UTIL_FW_VERSION,
UTIL_WS2K8_FW_VERSION
};
static void shutdown_onchannelcallback(void *context);
static struct hv_util_service util_shutdown = {
@ -118,7 +145,6 @@ static void shutdown_onchannelcallback(void *context)
struct shutdown_msg_data *shutdown_msg;
struct icmsg_hdr *icmsghdrp;
struct icmsg_negotiate *negop = NULL;
vmbus_recvpacket(channel, shut_txf_buf,
PAGE_SIZE, &recvlen, &requestid);
@ -128,9 +154,14 @@ static void shutdown_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, negop,
shut_txf_buf, util_fw_version,
sd_srv_version);
if (vmbus_prep_negotiate_resp(icmsghdrp, shut_txf_buf,
fw_versions, FW_VER_COUNT,
sd_versions, SD_VER_COUNT,
NULL, &sd_srv_version)) {
pr_info("Shutdown IC version %d.%d\n",
sd_srv_version >> 16,
sd_srv_version & 0xFFFF);
}
} else {
shutdown_msg =
(struct shutdown_msg_data *)&shut_txf_buf[
@ -181,31 +212,17 @@ struct adj_time_work {
static void hv_set_host_time(struct work_struct *work)
{
struct adj_time_work *wrk;
s64 host_tns;
u64 newtime;
struct timespec host_ts;
struct adj_time_work *wrk;
struct timespec64 host_ts;
u64 reftime, newtime;
wrk = container_of(work, struct adj_time_work, work);
newtime = wrk->host_time;
if (ts_srv_version > TS_VERSION_3) {
/*
* Some latency has been introduced since Hyper-V generated
* its time sample. Take that latency into account before
* using TSC reference time sample from Hyper-V.
*
* This sample is given by TimeSync v4 and above hosts.
*/
u64 current_tick;
reftime = hyperv_cs->read(hyperv_cs);
newtime = wrk->host_time + (reftime - wrk->ref_time);
host_ts = ns_to_timespec64((newtime - WLTIMEDELTA) * 100);
rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick);
newtime += (current_tick - wrk->ref_time);
}
host_tns = (newtime - WLTIMEDELTA) * 100;
host_ts = ns_to_timespec(host_tns);
do_settimeofday(&host_ts);
do_settimeofday64(&host_ts);
}
/*
@ -222,22 +239,60 @@ static void hv_set_host_time(struct work_struct *work)
* to discipline the clock.
*/
static struct adj_time_work wrk;
static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 flags)
/*
* The last time sample, received from the host. PTP device responds to
* requests by using this data and the current partition-wide time reference
* count.
*/
static struct {
u64 host_time;
u64 ref_time;
struct system_time_snapshot snap;
spinlock_t lock;
} host_ts;
static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 adj_flags)
{
unsigned long flags;
u64 cur_reftime;
/*
* This check is safe since we are executing in the
* interrupt context and time synch messages arre always
* interrupt context and time synch messages are always
* delivered on the same CPU.
*/
if (work_pending(&wrk.work))
return;
if (adj_flags & ICTIMESYNCFLAG_SYNC) {
/* Queue a job to do do_settimeofday64() */
if (work_pending(&wrk.work))
return;
wrk.host_time = hosttime;
wrk.ref_time = reftime;
wrk.flags = flags;
if ((flags & (ICTIMESYNCFLAG_SYNC | ICTIMESYNCFLAG_SAMPLE)) != 0) {
wrk.host_time = hosttime;
wrk.ref_time = reftime;
wrk.flags = adj_flags;
schedule_work(&wrk.work);
} else {
/*
* Save the adjusted time sample from the host and the snapshot
* of the current system time for PTP device.
*/
spin_lock_irqsave(&host_ts.lock, flags);
cur_reftime = hyperv_cs->read(hyperv_cs);
host_ts.host_time = hosttime;
host_ts.ref_time = cur_reftime;
ktime_get_snapshot(&host_ts.snap);
/*
* TimeSync v4 messages contain reference time (guest's Hyper-V
* clocksource read when the time sample was generated), we can
* improve the precision by adding the delta between now and the
* time of generation.
*/
if (ts_srv_version > TS_VERSION_3)
host_ts.host_time += (cur_reftime - reftime);
spin_unlock_irqrestore(&host_ts.lock, flags);
}
}
@ -253,7 +308,6 @@ static void timesync_onchannelcallback(void *context)
struct ictimesync_data *timedatap;
struct ictimesync_ref_data *refdata;
u8 *time_txf_buf = util_timesynch.recv_buffer;
struct icmsg_negotiate *negop = NULL;
vmbus_recvpacket(channel, time_txf_buf,
PAGE_SIZE, &recvlen, &requestid);
@ -263,12 +317,14 @@ static void timesync_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, negop,
time_txf_buf,
util_fw_version,
ts_srv_version);
pr_info("Using TimeSync version %d.%d\n",
ts_srv_version >> 16, ts_srv_version & 0xFFFF);
if (vmbus_prep_negotiate_resp(icmsghdrp, time_txf_buf,
fw_versions, FW_VER_COUNT,
ts_versions, TS_VER_COUNT,
NULL, &ts_srv_version)) {
pr_info("TimeSync IC version %d.%d\n",
ts_srv_version >> 16,
ts_srv_version & 0xFFFF);
}
} else {
if (ts_srv_version > TS_VERSION_3) {
refdata = (struct ictimesync_ref_data *)
@ -312,7 +368,6 @@ static void heartbeat_onchannelcallback(void *context)
struct icmsg_hdr *icmsghdrp;
struct heartbeat_msg_data *heartbeat_msg;
u8 *hbeat_txf_buf = util_heartbeat.recv_buffer;
struct icmsg_negotiate *negop = NULL;
while (1) {
@ -326,9 +381,16 @@ static void heartbeat_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr)];
if (icmsghdrp->icmsgtype == ICMSGTYPE_NEGOTIATE) {
vmbus_prep_negotiate_resp(icmsghdrp, negop,
hbeat_txf_buf, util_fw_version,
hb_srv_version);
if (vmbus_prep_negotiate_resp(icmsghdrp,
hbeat_txf_buf,
fw_versions, FW_VER_COUNT,
hb_versions, HB_VER_COUNT,
NULL, &hb_srv_version)) {
pr_info("Heartbeat IC version %d.%d\n",
hb_srv_version >> 16,
hb_srv_version & 0xFFFF);
}
} else {
heartbeat_msg =
(struct heartbeat_msg_data *)&hbeat_txf_buf[
@ -373,38 +435,10 @@ static int util_probe(struct hv_device *dev,
* Turn off batched reading for all util drivers before we open the
* channel.
*/
set_channel_read_state(dev->channel, false);
set_channel_read_mode(dev->channel, HV_CALL_DIRECT);
hv_set_drvdata(dev, srv);
/*
* Based on the host; initialize the framework and
* service version numbers we will negotiate.
*/
switch (vmbus_proto_version) {
case (VERSION_WS2008):
util_fw_version = UTIL_WS2K8_FW_VERSION;
sd_srv_version = SD_VERSION_1;
ts_srv_version = TS_VERSION_1;
hb_srv_version = HB_VERSION_1;
break;
case VERSION_WIN7:
case VERSION_WIN8:
case VERSION_WIN8_1:
util_fw_version = UTIL_FW_VERSION;
sd_srv_version = SD_VERSION;
ts_srv_version = TS_VERSION_3;
hb_srv_version = HB_VERSION;
break;
case VERSION_WIN10:
default:
util_fw_version = UTIL_FW_VERSION;
sd_srv_version = SD_VERSION;
ts_srv_version = TS_VERSION;
hb_srv_version = HB_VERSION;
}
ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
srv->util_cb, dev->channel);
if (ret)
@ -470,14 +504,113 @@ static struct hv_driver util_drv = {
.remove = util_remove,
};
static int hv_ptp_enable(struct ptp_clock_info *info,
struct ptp_clock_request *request, int on)
{
return -EOPNOTSUPP;
}
static int hv_ptp_settime(struct ptp_clock_info *p, const struct timespec64 *ts)
{
return -EOPNOTSUPP;
}
static int hv_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta)
{
return -EOPNOTSUPP;
}
static int hv_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
return -EOPNOTSUPP;
}
static int hv_ptp_gettime(struct ptp_clock_info *info, struct timespec64 *ts)
{
unsigned long flags;
u64 newtime, reftime;
spin_lock_irqsave(&host_ts.lock, flags);
reftime = hyperv_cs->read(hyperv_cs);
newtime = host_ts.host_time + (reftime - host_ts.ref_time);
*ts = ns_to_timespec64((newtime - WLTIMEDELTA) * 100);
spin_unlock_irqrestore(&host_ts.lock, flags);
return 0;
}
static int hv_ptp_get_syncdevicetime(ktime_t *device,
struct system_counterval_t *system,
void *ctx)
{
system->cs = hyperv_cs;
system->cycles = host_ts.ref_time;
*device = ns_to_ktime((host_ts.host_time - WLTIMEDELTA) * 100);
return 0;
}
static int hv_ptp_getcrosststamp(struct ptp_clock_info *ptp,
struct system_device_crosststamp *xtstamp)
{
unsigned long flags;
int ret;
spin_lock_irqsave(&host_ts.lock, flags);
/*
* host_ts contains the last time sample from the host and the snapshot
* of system time. We don't need to calculate the time delta between
* the reception and now as get_device_system_crosststamp() does the
* required interpolation.
*/
ret = get_device_system_crosststamp(hv_ptp_get_syncdevicetime,
NULL, &host_ts.snap, xtstamp);
spin_unlock_irqrestore(&host_ts.lock, flags);
return ret;
}
static struct ptp_clock_info ptp_hyperv_info = {
.name = "hyperv",
.enable = hv_ptp_enable,
.adjtime = hv_ptp_adjtime,
.adjfreq = hv_ptp_adjfreq,
.gettime64 = hv_ptp_gettime,
.getcrosststamp = hv_ptp_getcrosststamp,
.settime64 = hv_ptp_settime,
.owner = THIS_MODULE,
};
static struct ptp_clock *hv_ptp_clock;
static int hv_timesync_init(struct hv_util_service *srv)
{
/* TimeSync requires Hyper-V clocksource. */
if (!hyperv_cs)
return -ENODEV;
INIT_WORK(&wrk.work, hv_set_host_time);
/*
* ptp_clock_register() returns NULL when CONFIG_PTP_1588_CLOCK is
* disabled but the driver is still useful without the PTP device
* as it still handles the ICTIMESYNCFLAG_SYNC case.
*/
hv_ptp_clock = ptp_clock_register(&ptp_hyperv_info, NULL);
if (IS_ERR_OR_NULL(hv_ptp_clock)) {
pr_err("cannot register PTP clock: %ld\n",
PTR_ERR(hv_ptp_clock));
hv_ptp_clock = NULL;
}
return 0;
}
static void hv_timesync_deinit(void)
{
if (hv_ptp_clock)
ptp_clock_unregister(hv_ptp_clock);
cancel_work_sync(&wrk.work);
}

View File

@ -29,6 +29,7 @@
#include <asm/sync_bitops.h>
#include <linux/atomic.h>
#include <linux/hyperv.h>
#include <linux/interrupt.h>
/*
* Timeout for services such as KVP and fcopy.
@ -40,95 +41,9 @@
*/
#define HV_UTIL_NEGO_TIMEOUT 55
/*
* The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent
* is set by CPUID(HVCPUID_VERSION_FEATURES).
*/
enum hv_cpuid_function {
HVCPUID_VERSION_FEATURES = 0x00000001,
HVCPUID_VENDOR_MAXFUNCTION = 0x40000000,
HVCPUID_INTERFACE = 0x40000001,
/*
* The remaining functions depend on the value of
* HVCPUID_INTERFACE
*/
HVCPUID_VERSION = 0x40000002,
HVCPUID_FEATURES = 0x40000003,
HVCPUID_ENLIGHTENMENT_INFO = 0x40000004,
HVCPUID_IMPLEMENTATION_LIMITS = 0x40000005,
};
#define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE 0x400
#define HV_X64_MSR_CRASH_P0 0x40000100
#define HV_X64_MSR_CRASH_P1 0x40000101
#define HV_X64_MSR_CRASH_P2 0x40000102
#define HV_X64_MSR_CRASH_P3 0x40000103
#define HV_X64_MSR_CRASH_P4 0x40000104
#define HV_X64_MSR_CRASH_CTL 0x40000105
#define HV_CRASH_CTL_CRASH_NOTIFY (1ULL << 63)
/* Define version of the synthetic interrupt controller. */
#define HV_SYNIC_VERSION (1)
#define HV_ANY_VP (0xFFFFFFFF)
/* Define synthetic interrupt controller flag constants. */
#define HV_EVENT_FLAGS_COUNT (256 * 8)
#define HV_EVENT_FLAGS_BYTE_COUNT (256)
#define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(u32))
/* Define invalid partition identifier. */
#define HV_PARTITION_ID_INVALID ((u64)0x0)
/* Define port type. */
enum hv_port_type {
HVPORT_MSG = 1,
HVPORT_EVENT = 2,
HVPORT_MONITOR = 3
};
/* Define port information structure. */
struct hv_port_info {
enum hv_port_type port_type;
u32 padding;
union {
struct {
u32 target_sint;
u32 target_vp;
u64 rsvdz;
} message_port_info;
struct {
u32 target_sint;
u32 target_vp;
u16 base_flag_number;
u16 flag_count;
u32 rsvdz;
} event_port_info;
struct {
u64 monitor_address;
u64 rsvdz;
} monitor_port_info;
};
};
struct hv_connection_info {
enum hv_port_type port_type;
u32 padding;
union {
struct {
u64 rsvdz;
} message_connection_info;
struct {
u64 rsvdz;
} event_connection_info;
struct {
u64 monitor_address;
} monitor_connection_info;
};
};
#define HV_EVENT_FLAGS_LONG_COUNT (256 / sizeof(unsigned long))
/*
* Timer configuration register.
@ -146,18 +61,10 @@ union hv_timer_config {
};
};
/* Define the number of message buffers associated with each port. */
#define HV_PORT_MESSAGE_BUFFER_COUNT (16)
/* Define the synthetic interrupt controller event flags format. */
union hv_synic_event_flags {
u8 flags8[HV_EVENT_FLAGS_BYTE_COUNT];
u32 flags32[HV_EVENT_FLAGS_DWORD_COUNT];
};
/* Define the synthetic interrupt flags page layout. */
struct hv_synic_event_flags_page {
union hv_synic_event_flags sintevent_flags[HV_SYNIC_SINT_COUNT];
unsigned long flags[HV_EVENT_FLAGS_LONG_COUNT];
};
/* Define SynIC control register. */
@ -261,6 +168,8 @@ struct hv_monitor_page {
u8 rsvdz4[1984];
};
#define HV_HYPERCALL_PARAM_ALIGN sizeof(u64)
/* Definition of the hv_post_message hypercall input structure. */
struct hv_input_post_message {
union hv_connection_id connectionid;
@ -270,56 +179,6 @@ struct hv_input_post_message {
u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT];
};
/*
* Versioning definitions used for guests reporting themselves to the
* hypervisor, and visa versa.
*/
/* Version info reported by guest OS's */
enum hv_guest_os_vendor {
HVGUESTOS_VENDOR_MICROSOFT = 0x0001
};
enum hv_guest_os_microsoft_ids {
HVGUESTOS_MICROSOFT_UNDEFINED = 0x00,
HVGUESTOS_MICROSOFT_MSDOS = 0x01,
HVGUESTOS_MICROSOFT_WINDOWS3X = 0x02,
HVGUESTOS_MICROSOFT_WINDOWS9X = 0x03,
HVGUESTOS_MICROSOFT_WINDOWSNT = 0x04,
HVGUESTOS_MICROSOFT_WINDOWSCE = 0x05
};
/*
* Declare the MSR used to identify the guest OS.
*/
#define HV_X64_MSR_GUEST_OS_ID 0x40000000
union hv_x64_msr_guest_os_id_contents {
u64 as_uint64;
struct {
u64 build_number:16;
u64 service_version:8; /* Service Pack, etc. */
u64 minor_version:8;
u64 major_version:8;
u64 os_id:8; /* enum hv_guest_os_microsoft_ids (if Vendor=MS) */
u64 vendor_id:16; /* enum hv_guest_os_vendor */
};
};
/*
* Declare the MSR used to setup pages used to communicate with the hypervisor.
*/
#define HV_X64_MSR_HYPERCALL 0x40000001
union hv_x64_msr_hypercall_contents {
u64 as_uint64;
struct {
u64 enable:1;
u64 reserved:11;
u64 guest_physical_address:52;
};
};
enum {
VMBUS_MESSAGE_CONNECTION_ID = 1,
@ -331,111 +190,44 @@ enum {
VMBUS_MESSAGE_SINT = 2,
};
/* #defines */
#define HV_PRESENT_BIT 0x80000000
/*
* The guest OS needs to register the guest ID with the hypervisor.
* The guest ID is a 64 bit entity and the structure of this ID is
* specified in the Hyper-V specification:
*
* http://msdn.microsoft.com/en-us/library/windows/hardware/ff542653%28v=vs.85%29.aspx
*
* While the current guideline does not specify how Linux guest ID(s)
* need to be generated, our plan is to publish the guidelines for
* Linux and other guest operating systems that currently are hosted
* on Hyper-V. The implementation here conforms to this yet
* unpublished guidelines.
*
*
* Bit(s)
* 63 - Indicates if the OS is Open Source or not; 1 is Open Source
* 62:56 - Os Type; Linux is 0x100
* 55:48 - Distro specific identification
* 47:16 - Linux kernel version number
* 15:0 - Distro specific identification
*
*
* Per cpu state for channel handling
*/
struct hv_per_cpu_context {
void *synic_message_page;
void *synic_event_page;
/*
* buffer to post messages to the host.
*/
void *post_msg_page;
#define HV_LINUX_VENDOR_ID 0x8100
/*
* Starting with win8, we can take channel interrupts on any CPU;
* we will manage the tasklet that handles events messages on a per CPU
* basis.
*/
struct tasklet_struct msg_dpc;
/*
* Generate the guest ID based on the guideline described above.
*/
static inline __u64 generate_guest_id(__u8 d_info1, __u32 kernel_version,
__u16 d_info2)
{
__u64 guest_id = 0;
guest_id = (((__u64)HV_LINUX_VENDOR_ID) << 48);
guest_id |= (((__u64)(d_info1)) << 48);
guest_id |= (((__u64)(kernel_version)) << 16);
guest_id |= ((__u64)(d_info2));
return guest_id;
}
#define HV_CPU_POWER_MANAGEMENT (1 << 0)
#define HV_RECOMMENDATIONS_MAX 4
#define HV_X64_MAX 5
#define HV_CAPS_MAX 8
#define HV_HYPERCALL_PARAM_ALIGN sizeof(u64)
/* Service definitions */
#define HV_SERVICE_PARENT_PORT (0)
#define HV_SERVICE_PARENT_CONNECTION (0)
#define HV_SERVICE_CONNECT_RESPONSE_SUCCESS (0)
#define HV_SERVICE_CONNECT_RESPONSE_INVALID_PARAMETER (1)
#define HV_SERVICE_CONNECT_RESPONSE_UNKNOWN_SERVICE (2)
#define HV_SERVICE_CONNECT_RESPONSE_CONNECTION_REJECTED (3)
#define HV_SERVICE_CONNECT_REQUEST_MESSAGE_ID (1)
#define HV_SERVICE_CONNECT_RESPONSE_MESSAGE_ID (2)
#define HV_SERVICE_DISCONNECT_REQUEST_MESSAGE_ID (3)
#define HV_SERVICE_DISCONNECT_RESPONSE_MESSAGE_ID (4)
#define HV_SERVICE_MAX_MESSAGE_ID (4)
#define HV_SERVICE_PROTOCOL_VERSION (0x0010)
#define HV_CONNECT_PAYLOAD_BYTE_COUNT 64
/* #define VMBUS_REVISION_NUMBER 6 */
/* Our local vmbus's port and connection id. Anything >0 is fine */
/* #define VMBUS_PORT_ID 11 */
/* 628180B8-308D-4c5e-B7DB-1BEB62E62EF4 */
static const uuid_le VMBUS_SERVICE_ID = {
.b = {
0xb8, 0x80, 0x81, 0x62, 0x8d, 0x30, 0x5e, 0x4c,
0xb7, 0xdb, 0x1b, 0xeb, 0x62, 0xe6, 0x2e, 0xf4
},
/*
* To optimize the mapping of relid to channel, maintain
* per-cpu list of the channels based on their CPU affinity.
*/
struct list_head chan_list;
struct clock_event_device *clk_evt;
};
struct hv_context {
/* We only support running on top of Hyper-V
* So at this point this really can only contain the Hyper-V ID
*/
u64 guestid;
void *hypercall_page;
void *tsc_page;
bool synic_initialized;
void *synic_message_page[NR_CPUS];
void *synic_event_page[NR_CPUS];
struct hv_per_cpu_context __percpu *cpu_context;
/*
* Hypervisor's notion of virtual processor ID is different from
* Linux' notion of CPU ID. This information can only be retrieved
@ -446,26 +238,7 @@ struct hv_context {
* Linux cpuid 'a'.
*/
u32 vp_index[NR_CPUS];
/*
* Starting with win8, we can take channel interrupts on any CPU;
* we will manage the tasklet that handles events messages on a per CPU
* basis.
*/
struct tasklet_struct *event_dpc[NR_CPUS];
struct tasklet_struct *msg_dpc[NR_CPUS];
/*
* To optimize the mapping of relid to channel, maintain
* per-cpu list of the channels based on their CPU affinity.
*/
struct list_head percpu_list[NR_CPUS];
/*
* buffer to post messages to the host.
*/
void *post_msg_page[NR_CPUS];
/*
* Support PV clockevent device.
*/
struct clock_event_device *clk_evt[NR_CPUS];
/*
* To manage allocations in a NUMA node.
* Array indexed by numa node ID.
@ -475,14 +248,6 @@ struct hv_context {
extern struct hv_context hv_context;
struct ms_hyperv_tsc_page {
volatile u32 tsc_sequence;
u32 reserved1;
volatile u64 tsc_scale;
volatile s64 tsc_offset;
u64 reserved2[509];
};
struct hv_ring_buffer_debug_info {
u32 current_interrupt_mask;
u32 current_read_index;
@ -495,8 +260,6 @@ struct hv_ring_buffer_debug_info {
extern int hv_init(void);
extern void hv_cleanup(bool crash);
extern int hv_post_message(union hv_connection_id connection_id,
enum hv_message_type message_type,
void *payload, size_t payload_size);
@ -505,20 +268,12 @@ extern int hv_synic_alloc(void);
extern void hv_synic_free(void);
extern void hv_synic_init(void *irqarg);
extern int hv_synic_init(unsigned int cpu);
extern void hv_synic_cleanup(void *arg);
extern int hv_synic_cleanup(unsigned int cpu);
extern void hv_synic_clockevents_cleanup(void);
/*
* Host version information.
*/
extern unsigned int host_info_eax;
extern unsigned int host_info_ebx;
extern unsigned int host_info_ecx;
extern unsigned int host_info_edx;
/* Interface */
@ -528,20 +283,14 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info,
void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info);
int hv_ringbuffer_write(struct vmbus_channel *channel,
struct kvec *kv_list,
u32 kv_count, bool lock,
bool kick_q);
const struct kvec *kv_list, u32 kv_count);
int hv_ringbuffer_read(struct vmbus_channel *channel,
void *buffer, u32 buflen, u32 *buffer_actual_len,
u64 *requestid, bool raw);
void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info,
struct hv_ring_buffer_debug_info *debug_info);
void hv_begin_read(struct hv_ring_buffer_info *rbi);
u32 hv_end_read(struct hv_ring_buffer_info *rbi);
void hv_ringbuffer_get_debuginfo(const struct hv_ring_buffer_info *ring_info,
struct hv_ring_buffer_debug_info *debug_info);
/*
* Maximum channels is determined by the size of the interrupt page
@ -608,6 +357,11 @@ struct vmbus_msginfo {
extern struct vmbus_connection vmbus_connection;
static inline void vmbus_send_interrupt(u32 relid)
{
sync_set_bit(relid, vmbus_connection.send_int_page);
}
enum vmbus_message_handler_type {
/* The related handler can sleep. */
VMHT_BLOCKING = 0,
@ -625,41 +379,6 @@ struct vmbus_channel_message_table_entry {
extern struct vmbus_channel_message_table_entry
channel_message_table[CHANNELMSG_COUNT];
/* Free the message slot and signal end-of-message if required */
static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)
{
/*
* On crash we're reading some other CPU's message page and we need
* to be careful: this other CPU may already had cleared the header
* and the host may already had delivered some other message there.
* In case we blindly write msg->header.message_type we're going
* to lose it. We can still lose a message of the same type but
* we count on the fact that there can only be one
* CHANNELMSG_UNLOAD_RESPONSE and we don't care about other messages
* on crash.
*/
if (cmpxchg(&msg->header.message_type, old_msg_type,
HVMSG_NONE) != old_msg_type)
return;
/*
* Make sure the write to MessageType (ie set to
* HVMSG_NONE) happens before we read the
* MessagePending and EOMing. Otherwise, the EOMing
* will not deliver any more messages since there is
* no empty slot
*/
mb();
if (msg->header.message_flags.msg_pending) {
/*
* This will cause message queue rescan to
* possibly deliver another msg from the
* hypervisor
*/
wrmsrl(HV_X64_MSR_EOM, 0);
}
}
/* General vmbus interface */
@ -670,10 +389,6 @@ struct hv_device *vmbus_device_create(const uuid_le *type,
int vmbus_device_register(struct hv_device *child_device_obj);
void vmbus_device_unregister(struct hv_device *device_obj);
/* static void */
/* VmbusChildDeviceDestroy( */
/* struct hv_device *); */
struct vmbus_channel *relid2channel(u32 relid);
void vmbus_free_channels(void);
@ -683,7 +398,7 @@ void vmbus_free_channels(void);
int vmbus_connect(void);
void vmbus_disconnect(void);
int vmbus_post_msg(void *buffer, size_t buflen);
int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep);
void vmbus_on_event(unsigned long data);
void vmbus_on_msg_dpc(unsigned long data);

View File

@ -32,26 +32,6 @@
#include "hyperv_vmbus.h"
void hv_begin_read(struct hv_ring_buffer_info *rbi)
{
rbi->ring_buffer->interrupt_mask = 1;
virt_mb();
}
u32 hv_end_read(struct hv_ring_buffer_info *rbi)
{
rbi->ring_buffer->interrupt_mask = 0;
virt_mb();
/*
* Now check to see if the ring buffer is still empty.
* If it is not, we raced and we need to process new
* incoming messages.
*/
return hv_get_bytes_to_read(rbi);
}
/*
* When we write to the ring buffer, check if the host needs to
* be signaled. Here is the details of this protocol:
@ -77,8 +57,7 @@ u32 hv_end_read(struct hv_ring_buffer_info *rbi)
* host logic is fixed.
*/
static void hv_signal_on_write(u32 old_write, struct vmbus_channel *channel,
bool kick_q)
static void hv_signal_on_write(u32 old_write, struct vmbus_channel *channel)
{
struct hv_ring_buffer_info *rbi = &channel->outbound;
@ -117,11 +96,9 @@ hv_set_next_write_location(struct hv_ring_buffer_info *ring_info,
/* Get the next read location for the specified ring buffer. */
static inline u32
hv_get_next_read_location(struct hv_ring_buffer_info *ring_info)
hv_get_next_read_location(const struct hv_ring_buffer_info *ring_info)
{
u32 next = ring_info->ring_buffer->read_index;
return next;
return ring_info->ring_buffer->read_index;
}
/*
@ -129,13 +106,14 @@ hv_get_next_read_location(struct hv_ring_buffer_info *ring_info)
* This allows the caller to skip.
*/
static inline u32
hv_get_next_readlocation_withoffset(struct hv_ring_buffer_info *ring_info,
u32 offset)
hv_get_next_readlocation_withoffset(const struct hv_ring_buffer_info *ring_info,
u32 offset)
{
u32 next = ring_info->ring_buffer->read_index;
next += offset;
next %= ring_info->ring_datasize;
if (next >= ring_info->ring_datasize)
next -= ring_info->ring_datasize;
return next;
}
@ -151,7 +129,7 @@ hv_set_next_read_location(struct hv_ring_buffer_info *ring_info,
/* Get the size of the ring buffer. */
static inline u32
hv_get_ring_buffersize(struct hv_ring_buffer_info *ring_info)
hv_get_ring_buffersize(const struct hv_ring_buffer_info *ring_info)
{
return ring_info->ring_datasize;
}
@ -168,7 +146,7 @@ hv_get_ring_bufferindices(struct hv_ring_buffer_info *ring_info)
* Assume there is enough room. Handles wrap-around in src case only!!
*/
static u32 hv_copyfrom_ringbuffer(
struct hv_ring_buffer_info *ring_info,
const struct hv_ring_buffer_info *ring_info,
void *dest,
u32 destlen,
u32 start_read_offset)
@ -179,7 +157,8 @@ static u32 hv_copyfrom_ringbuffer(
memcpy(dest, ring_buffer + start_read_offset, destlen);
start_read_offset += destlen;
start_read_offset %= ring_buffer_size;
if (start_read_offset >= ring_buffer_size)
start_read_offset -= ring_buffer_size;
return start_read_offset;
}
@ -192,7 +171,7 @@ static u32 hv_copyfrom_ringbuffer(
static u32 hv_copyto_ringbuffer(
struct hv_ring_buffer_info *ring_info,
u32 start_write_offset,
void *src,
const void *src,
u32 srclen)
{
void *ring_buffer = hv_get_ring_buffer(ring_info);
@ -201,14 +180,15 @@ static u32 hv_copyto_ringbuffer(
memcpy(ring_buffer + start_write_offset, src, srclen);
start_write_offset += srclen;
start_write_offset %= ring_buffer_size;
if (start_write_offset >= ring_buffer_size)
start_write_offset -= ring_buffer_size;
return start_write_offset;
}
/* Get various debug metrics for the specified ring buffer. */
void hv_ringbuffer_get_debuginfo(struct hv_ring_buffer_info *ring_info,
struct hv_ring_buffer_debug_info *debug_info)
void hv_ringbuffer_get_debuginfo(const struct hv_ring_buffer_info *ring_info,
struct hv_ring_buffer_debug_info *debug_info)
{
u32 bytes_avail_towrite;
u32 bytes_avail_toread;
@ -285,8 +265,7 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info)
/* Write to the ring buffer. */
int hv_ringbuffer_write(struct vmbus_channel *channel,
struct kvec *kv_list, u32 kv_count, bool lock,
bool kick_q)
const struct kvec *kv_list, u32 kv_count)
{
int i = 0;
u32 bytes_avail_towrite;
@ -298,13 +277,15 @@ int hv_ringbuffer_write(struct vmbus_channel *channel,
unsigned long flags = 0;
struct hv_ring_buffer_info *outring_info = &channel->outbound;
if (channel->rescind)
return -ENODEV;
for (i = 0; i < kv_count; i++)
totalbytes_towrite += kv_list[i].iov_len;
totalbytes_towrite += sizeof(u64);
if (lock)
spin_lock_irqsave(&outring_info->ring_lock, flags);
spin_lock_irqsave(&outring_info->ring_lock, flags);
bytes_avail_towrite = hv_get_bytes_to_write(outring_info);
@ -314,8 +295,7 @@ int hv_ringbuffer_write(struct vmbus_channel *channel,
* is empty since the read index == write index.
*/
if (bytes_avail_towrite <= totalbytes_towrite) {
if (lock)
spin_unlock_irqrestore(&outring_info->ring_lock, flags);
spin_unlock_irqrestore(&outring_info->ring_lock, flags);
return -EAGAIN;
}
@ -346,10 +326,13 @@ int hv_ringbuffer_write(struct vmbus_channel *channel,
hv_set_next_write_location(outring_info, next_write_location);
if (lock)
spin_unlock_irqrestore(&outring_info->ring_lock, flags);
spin_unlock_irqrestore(&outring_info->ring_lock, flags);
hv_signal_on_write(old_write, channel);
if (channel->rescind)
return -ENODEV;
hv_signal_on_write(old_write, channel, kick_q);
return 0;
}

View File

@ -54,31 +54,7 @@ static struct acpi_device *hv_acpi_dev;
static struct completion probe_event;
static void hyperv_report_panic(struct pt_regs *regs)
{
static bool panic_reported;
/*
* We prefer to report panic on 'die' chain as we have proper
* registers to report, but if we miss it (e.g. on BUG()) we need
* to report it on 'panic'.
*/
if (panic_reported)
return;
panic_reported = true;
wrmsrl(HV_X64_MSR_CRASH_P0, regs->ip);
wrmsrl(HV_X64_MSR_CRASH_P1, regs->ax);
wrmsrl(HV_X64_MSR_CRASH_P2, regs->bx);
wrmsrl(HV_X64_MSR_CRASH_P3, regs->cx);
wrmsrl(HV_X64_MSR_CRASH_P4, regs->dx);
/*
* Let Hyper-V know there is crash data available
*/
wrmsrl(HV_X64_MSR_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY);
}
static int hyperv_cpuhp_online;
static int hyperv_panic_event(struct notifier_block *nb, unsigned long val,
void *args)
@ -859,9 +835,10 @@ static void vmbus_onmessage_work(struct work_struct *work)
kfree(ctx);
}
static void hv_process_timer_expiration(struct hv_message *msg, int cpu)
static void hv_process_timer_expiration(struct hv_message *msg,
struct hv_per_cpu_context *hv_cpu)
{
struct clock_event_device *dev = hv_context.clk_evt[cpu];
struct clock_event_device *dev = hv_cpu->clk_evt;
if (dev->event_handler)
dev->event_handler(dev);
@ -871,8 +848,8 @@ static void hv_process_timer_expiration(struct hv_message *msg, int cpu)
void vmbus_on_msg_dpc(unsigned long data)
{
int cpu = smp_processor_id();
void *page_addr = hv_context.synic_message_page[cpu];
struct hv_per_cpu_context *hv_cpu = (void *)data;
void *page_addr = hv_cpu->synic_message_page;
struct hv_message *msg = (struct hv_message *)page_addr +
VMBUS_MESSAGE_SINT;
struct vmbus_channel_message_header *hdr;
@ -908,16 +885,88 @@ void vmbus_on_msg_dpc(unsigned long data)
vmbus_signal_eom(msg, message_type);
}
/*
* Direct callback for channels using other deferred processing
*/
static void vmbus_channel_isr(struct vmbus_channel *channel)
{
void (*callback_fn)(void *);
callback_fn = READ_ONCE(channel->onchannel_callback);
if (likely(callback_fn != NULL))
(*callback_fn)(channel->channel_callback_context);
}
/*
* Schedule all channels with events pending
*/
static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu)
{
unsigned long *recv_int_page;
u32 maxbits, relid;
if (vmbus_proto_version < VERSION_WIN8) {
maxbits = MAX_NUM_CHANNELS_SUPPORTED;
recv_int_page = vmbus_connection.recv_int_page;
} else {
/*
* When the host is win8 and beyond, the event page
* can be directly checked to get the id of the channel
* that has the interrupt pending.
*/
void *page_addr = hv_cpu->synic_event_page;
union hv_synic_event_flags *event
= (union hv_synic_event_flags *)page_addr +
VMBUS_MESSAGE_SINT;
maxbits = HV_EVENT_FLAGS_COUNT;
recv_int_page = event->flags;
}
if (unlikely(!recv_int_page))
return;
for_each_set_bit(relid, recv_int_page, maxbits) {
struct vmbus_channel *channel;
if (!sync_test_and_clear_bit(relid, recv_int_page))
continue;
/* Special case - vmbus channel protocol msg */
if (relid == 0)
continue;
/* Find channel based on relid */
list_for_each_entry(channel, &hv_cpu->chan_list, percpu_list) {
if (channel->offermsg.child_relid != relid)
continue;
switch (channel->callback_mode) {
case HV_CALL_ISR:
vmbus_channel_isr(channel);
break;
case HV_CALL_BATCHED:
hv_begin_read(&channel->inbound);
/* fallthrough */
case HV_CALL_DIRECT:
tasklet_schedule(&channel->callback_event);
}
}
}
}
static void vmbus_isr(void)
{
int cpu = smp_processor_id();
void *page_addr;
struct hv_per_cpu_context *hv_cpu
= this_cpu_ptr(hv_context.cpu_context);
void *page_addr = hv_cpu->synic_event_page;
struct hv_message *msg;
union hv_synic_event_flags *event;
bool handled = false;
page_addr = hv_context.synic_event_page[cpu];
if (page_addr == NULL)
if (unlikely(page_addr == NULL))
return;
event = (union hv_synic_event_flags *)page_addr +
@ -932,10 +981,8 @@ static void vmbus_isr(void)
(vmbus_proto_version == VERSION_WIN7)) {
/* Since we are a child, we only need to check bit 0 */
if (sync_test_and_clear_bit(0,
(unsigned long *) &event->flags32[0])) {
if (sync_test_and_clear_bit(0, event->flags))
handled = true;
}
} else {
/*
* Our host is win8 or above. The signaling mechanism
@ -947,18 +994,17 @@ static void vmbus_isr(void)
}
if (handled)
tasklet_schedule(hv_context.event_dpc[cpu]);
vmbus_chan_sched(hv_cpu);
page_addr = hv_context.synic_message_page[cpu];
page_addr = hv_cpu->synic_message_page;
msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
/* Check if there are actual msgs to be processed */
if (msg->header.message_type != HVMSG_NONE) {
if (msg->header.message_type == HVMSG_TIMER_EXPIRED)
hv_process_timer_expiration(msg, cpu);
hv_process_timer_expiration(msg, hv_cpu);
else
tasklet_schedule(hv_context.msg_dpc[cpu]);
tasklet_schedule(&hv_cpu->msg_dpc);
}
add_interrupt_randomness(HYPERVISOR_CALLBACK_VECTOR, 0);
@ -986,7 +1032,7 @@ static int vmbus_bus_init(void)
ret = bus_register(&hv_bus);
if (ret)
goto err_cleanup;
return ret;
hv_setup_vmbus_irq(vmbus_isr);
@ -997,14 +1043,16 @@ static int vmbus_bus_init(void)
* Initialize the per-cpu interrupt state and
* connect to the host.
*/
on_each_cpu(hv_synic_init, NULL, 1);
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/hyperv:online",
hv_synic_init, hv_synic_cleanup);
if (ret < 0)
goto err_alloc;
hyperv_cpuhp_online = ret;
ret = vmbus_connect();
if (ret)
goto err_connect;
if (vmbus_proto_version > VERSION_WIN7)
cpu_hotplug_disable();
/*
* Only register if the crash MSRs are available
*/
@ -1019,16 +1067,13 @@ static int vmbus_bus_init(void)
return 0;
err_connect:
on_each_cpu(hv_synic_cleanup, NULL, 1);
cpuhp_remove_state(hyperv_cpuhp_online);
err_alloc:
hv_synic_free();
hv_remove_vmbus_irq();
bus_unregister(&hv_bus);
err_cleanup:
hv_cleanup(false);
return ret;
}
@ -1478,13 +1523,13 @@ static struct acpi_driver vmbus_acpi_driver = {
static void hv_kexec_handler(void)
{
int cpu;
hv_synic_clockevents_cleanup();
vmbus_initiate_unload(false);
for_each_online_cpu(cpu)
smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1);
hv_cleanup(false);
vmbus_connection.conn_state = DISCONNECTED;
/* Make sure conn_state is set as hv_synic_cleanup checks for it */
mb();
cpuhp_remove_state(hyperv_cpuhp_online);
hyperv_cleanup();
};
static void hv_crash_handler(struct pt_regs *regs)
@ -1495,8 +1540,9 @@ static void hv_crash_handler(struct pt_regs *regs)
* doing the cleanup for current CPU only. This should be sufficient
* for kdump.
*/
hv_synic_cleanup(NULL);
hv_cleanup(true);
vmbus_connection.conn_state = DISCONNECTED;
hv_synic_cleanup(smp_processor_id());
hyperv_cleanup();
};
static int __init hv_acpi_init(void)
@ -1547,24 +1593,24 @@ static void __exit vmbus_exit(void)
hv_synic_clockevents_cleanup();
vmbus_disconnect();
hv_remove_vmbus_irq();
for_each_online_cpu(cpu)
tasklet_kill(hv_context.msg_dpc[cpu]);
for_each_online_cpu(cpu) {
struct hv_per_cpu_context *hv_cpu
= per_cpu_ptr(hv_context.cpu_context, cpu);
tasklet_kill(&hv_cpu->msg_dpc);
}
vmbus_free_channels();
if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
unregister_die_notifier(&hyperv_die_block);
atomic_notifier_chain_unregister(&panic_notifier_list,
&hyperv_panic_block);
}
bus_unregister(&hv_bus);
hv_cleanup(false);
for_each_online_cpu(cpu) {
tasklet_kill(hv_context.event_dpc[cpu]);
smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1);
}
cpuhp_remove_state(hyperv_cpuhp_online);
hv_synic_free();
acpi_bus_unregister_driver(&vmbus_acpi_driver);
if (vmbus_proto_version > VERSION_WIN7)
cpu_hotplug_enable();
}

View File

@ -242,6 +242,7 @@ static void *etm_setup_aux(int event_cpu, void **pages,
if (!sink_ops(sink)->alloc_buffer)
goto err;
cpu = cpumask_first(mask);
/* Get the AUX specific data from the sink buffer */
event_data->snk_config =
sink_ops(sink)->alloc_buffer(sink, cpu, pages,

View File

@ -216,10 +216,14 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata,
goto out;
/* Go from generic option to ETMv4 specifics */
if (attr->config & BIT(ETM_OPT_CYCACC))
config->cfg |= ETMv4_MODE_CYCACC;
if (attr->config & BIT(ETM_OPT_CYCACC)) {
config->cfg |= BIT(4);
/* TRM: Must program this for cycacc to work */
config->ccctlr = ETM_CYC_THRESHOLD_DEFAULT;
}
if (attr->config & BIT(ETM_OPT_TS))
config->cfg |= ETMv4_MODE_TIMESTAMP;
/* bit[11], Global timestamp tracing bit */
config->cfg |= BIT(11);
out:
return ret;

View File

@ -146,6 +146,7 @@
#define ETM_ARCH_V4 0x40
#define ETMv4_SYNC_MASK 0x1F
#define ETM_CYC_THRESHOLD_MASK 0xFFF
#define ETM_CYC_THRESHOLD_DEFAULT 0x100
#define ETMv4_EVENT_MASK 0xFF
#define ETM_CNTR_MAX_VAL 0xFFFF
#define ETM_TRACEID_MASK 0x3f

View File

@ -356,7 +356,7 @@ static void stm_generic_unlink(struct stm_data *stm_data,
if (!drvdata || !drvdata->csdev)
return;
stm_disable(drvdata->csdev, NULL);
coresight_disable(drvdata->csdev);
}
static phys_addr_t

View File

@ -20,6 +20,7 @@
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/platform_data/ti-aemif.h>
#define TA_SHIFT 2
#define RHOLD_SHIFT 4
@ -335,6 +336,8 @@ static int aemif_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
struct device_node *child_np;
struct aemif_device *aemif;
struct aemif_platform_data *pdata;
struct of_dev_auxdata *dev_lookup;
if (np == NULL)
return 0;
@ -343,6 +346,9 @@ static int aemif_probe(struct platform_device *pdev)
if (!aemif)
return -ENOMEM;
pdata = dev_get_platdata(&pdev->dev);
dev_lookup = pdata ? pdata->dev_lookup : NULL;
platform_set_drvdata(pdev, aemif);
aemif->clk = devm_clk_get(dev, NULL);
@ -390,7 +396,7 @@ static int aemif_probe(struct platform_device *pdev)
* parameters are set.
*/
for_each_available_child_of_node(np, child_np) {
ret = of_platform_populate(child_np, NULL, NULL, dev);
ret = of_platform_populate(child_np, NULL, dev_lookup, dev);
if (ret < 0)
goto error;
}

View File

@ -474,11 +474,15 @@ config SRAM
bool "Generic on-chip SRAM driver"
depends on HAS_IOMEM
select GENERIC_ALLOCATOR
select SRAM_EXEC if ARM
help
This driver allows you to declare a memory region to be managed by
the genalloc API. It is supposed to be used for small on-chip SRAM
areas found on many SoCs.
config SRAM_EXEC
bool
config VEXPRESS_SYSCFG
bool "Versatile Express System Configuration driver"
depends on VEXPRESS_CONFIG
@ -487,6 +491,7 @@ config VEXPRESS_SYSCFG
ARM Ltd. Versatile Express uses specialised platform configuration
bus. System Configuration interface is one of the possible means
of generating transactions on this bus.
config PANEL
tristate "Parallel port LCD/Keypad Panel support"
depends on PARPORT
@ -494,14 +499,14 @@ config PANEL
Say Y here if you have an HD44780 or KS-0074 LCD connected to your
parallel port. This driver also features 4 and 6-key keypads. The LCD
is accessible through the /dev/lcd char device (10, 156), and the
keypad through /dev/keypad (10, 185). Both require misc device to be
enabled. This code can either be compiled as a module, or linked into
the kernel and started at boot. If you don't understand what all this
is about, say N.
keypad through /dev/keypad (10, 185). This code can either be
compiled as a module, or linked into the kernel and started at boot.
If you don't understand what all this is about, say N.
if PANEL
config PANEL_PARPORT
int "Default parallel port number (0=LPT1)"
depends on PANEL
range 0 255
default "0"
---help---
@ -513,7 +518,6 @@ config PANEL_PARPORT
config PANEL_PROFILE
int "Default panel profile (0-5, 0=custom)"
depends on PANEL
range 0 5
default "5"
---help---
@ -534,7 +538,7 @@ config PANEL_PROFILE
for experts.
config PANEL_KEYPAD
depends on PANEL && PANEL_PROFILE="0"
depends on PANEL_PROFILE="0"
int "Keypad type (0=none, 1=old 6 keys, 2=new 6 keys, 3=Nexcom 4 keys)"
range 0 3
default 0
@ -551,7 +555,7 @@ config PANEL_KEYPAD
supports simultaneous keys pressed when the keypad supports them.
config PANEL_LCD
depends on PANEL && PANEL_PROFILE="0"
depends on PANEL_PROFILE="0"
int "LCD type (0=none, 1=custom, 2=old //, 3=ks0074, 4=hantronix, 5=Nexcom)"
range 0 5
default 0
@ -574,7 +578,7 @@ config PANEL_LCD
that those values changed from the 2.4 driver for better consistency.
config PANEL_LCD_HEIGHT
depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
depends on PANEL_PROFILE="0" && PANEL_LCD="1"
int "Number of lines on the LCD (1-2)"
range 1 2
default 2
@ -583,7 +587,7 @@ config PANEL_LCD_HEIGHT
It can either be 1 or 2.
config PANEL_LCD_WIDTH
depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
depends on PANEL_PROFILE="0" && PANEL_LCD="1"
int "Number of characters per line on the LCD (1-40)"
range 1 40
default 40
@ -592,7 +596,7 @@ config PANEL_LCD_WIDTH
Common values are 16,20,24,40.
config PANEL_LCD_BWIDTH
depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
depends on PANEL_PROFILE="0" && PANEL_LCD="1"
int "Internal LCD line width (1-40, 40 by default)"
range 1 40
default 40
@ -608,7 +612,7 @@ config PANEL_LCD_BWIDTH
If you don't know, put '40' here.
config PANEL_LCD_HWIDTH
depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
depends on PANEL_PROFILE="0" && PANEL_LCD="1"
int "Hardware LCD line width (1-64, 64 by default)"
range 1 64
default 64
@ -622,7 +626,7 @@ config PANEL_LCD_HWIDTH
64 here for a 2x40.
config PANEL_LCD_CHARSET
depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
depends on PANEL_PROFILE="0" && PANEL_LCD="1"
int "LCD character set (0=normal, 1=KS0074)"
range 0 1
default 0
@ -638,7 +642,7 @@ config PANEL_LCD_CHARSET
If you don't know, use the normal one (0).
config PANEL_LCD_PROTO
depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
depends on PANEL_PROFILE="0" && PANEL_LCD="1"
int "LCD communication mode (0=parallel 8 bits, 1=serial)"
range 0 1
default 0
@ -651,7 +655,7 @@ config PANEL_LCD_PROTO
parallel LCD, and 1 for a serial LCD.
config PANEL_LCD_PIN_E
depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
int "Parallel port pin number & polarity connected to the LCD E signal (-17...17) "
range -17 17
default 14
@ -666,7 +670,7 @@ config PANEL_LCD_PIN_E
Default for the 'E' pin in custom profile is '14' (AUTOFEED).
config PANEL_LCD_PIN_RS
depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
int "Parallel port pin number & polarity connected to the LCD RS signal (-17...17) "
range -17 17
default 17
@ -681,7 +685,7 @@ config PANEL_LCD_PIN_RS
Default for the 'RS' pin in custom profile is '17' (SELECT IN).
config PANEL_LCD_PIN_RW
depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO="0"
int "Parallel port pin number & polarity connected to the LCD RW signal (-17...17) "
range -17 17
default 16
@ -696,7 +700,7 @@ config PANEL_LCD_PIN_RW
Default for the 'RW' pin in custom profile is '16' (INIT).
config PANEL_LCD_PIN_SCL
depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0"
depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0"
int "Parallel port pin number & polarity connected to the LCD SCL signal (-17...17) "
range -17 17
default 1
@ -711,7 +715,7 @@ config PANEL_LCD_PIN_SCL
Default for the 'SCL' pin in custom profile is '1' (STROBE).
config PANEL_LCD_PIN_SDA
depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0"
depends on PANEL_PROFILE="0" && PANEL_LCD="1" && PANEL_LCD_PROTO!="0"
int "Parallel port pin number & polarity connected to the LCD SDA signal (-17...17) "
range -17 17
default 2
@ -726,7 +730,7 @@ config PANEL_LCD_PIN_SDA
Default for the 'SDA' pin in custom profile is '2' (D0).
config PANEL_LCD_PIN_BL
depends on PANEL && PANEL_PROFILE="0" && PANEL_LCD="1"
depends on PANEL_PROFILE="0" && PANEL_LCD="1"
int "Parallel port pin number & polarity connected to the LCD backlight signal (-17...17) "
range -17 17
default 0
@ -741,7 +745,6 @@ config PANEL_LCD_PIN_BL
Default for the 'BL' pin in custom profile is '0' (uncontrolled).
config PANEL_CHANGE_MESSAGE
depends on PANEL
bool "Change LCD initialization message ?"
default "n"
---help---
@ -754,7 +757,7 @@ config PANEL_CHANGE_MESSAGE
say 'N' and keep the default message with the version.
config PANEL_BOOT_MESSAGE
depends on PANEL && PANEL_CHANGE_MESSAGE="y"
depends on PANEL_CHANGE_MESSAGE="y"
string "New initialization message"
default ""
---help---
@ -766,6 +769,8 @@ config PANEL_BOOT_MESSAGE
An empty message will only clear the display at driver init time. Any other
printf()-formatted message is valid with newline and escape codes.
endif # PANEL
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"

View File

@ -47,6 +47,7 @@ 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-$(CONFIG_SRAM_EXEC) += sram-exec.o
obj-y += mic/
obj-$(CONFIG_GENWQE) += genwqe/
obj-$(CONFIG_ECHO) += echo/

View File

@ -100,4 +100,14 @@ config EEPROM_DIGSY_MTC_CFG
If unsure, say N.
config EEPROM_IDT_89HPESX
tristate "IDT 89HPESx PCIe-swtiches EEPROM / CSR support"
depends on I2C && SYSFS
help
Enable this driver to get read/write access to EEPROM / CSRs
over IDT PCIe-swtich i2c-slave interface.
This driver can also be built as a module. If so, the module
will be called idt_89hpesx.
endmenu

View File

@ -5,3 +5,4 @@ obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o
obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o
obj-$(CONFIG_EEPROM_IDT_89HPESX) += idt_89hpesx.o

File diff suppressed because it is too large Load Diff

View File

@ -1336,7 +1336,6 @@ static int genwqe_sriov_configure(struct pci_dev *dev, int numvfs)
static struct pci_error_handlers genwqe_err_handler = {
.error_detected = genwqe_err_error_detected,
.mmio_enabled = genwqe_err_result_none,
.link_reset = genwqe_err_result_none,
.slot_reset = genwqe_err_slot_reset,
.resume = genwqe_err_resume,
};

View File

@ -81,12 +81,17 @@ void lkdtm_OVERFLOW(void)
(void) recursive_loop(recur_count);
}
static noinline void __lkdtm_CORRUPT_STACK(void *stack)
{
memset(stack, 'a', 64);
}
noinline void lkdtm_CORRUPT_STACK(void)
{
/* Use default char array length that triggers stack protection. */
char data[8];
__lkdtm_CORRUPT_STACK(&data);
memset((void *)data, 'a', 64);
pr_info("Corrupted stack with '%16s'...\n", data);
}

View File

@ -539,7 +539,9 @@ static void __exit lkdtm_module_exit(void)
/* Handle test-specific clean-up. */
lkdtm_usercopy_exit();
unregister_jprobe(lkdtm_jprobe);
if (lkdtm_jprobe != NULL)
unregister_jprobe(lkdtm_jprobe);
pr_info("Crash point unregistered\n");
}

View File

@ -132,8 +132,7 @@ int mei_amthif_run_next_cmd(struct mei_device *dev)
dev_dbg(dev->dev, "complete amthif cmd_list cb.\n");
cb = list_first_entry_or_null(&dev->amthif_cmd_list.list,
typeof(*cb), list);
cb = list_first_entry_or_null(&dev->amthif_cmd_list, typeof(*cb), list);
if (!cb) {
dev->iamthif_state = MEI_IAMTHIF_IDLE;
cl->fp = NULL;
@ -167,7 +166,7 @@ int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb)
struct mei_device *dev = cl->dev;
list_add_tail(&cb->list, &dev->amthif_cmd_list.list);
list_add_tail(&cb->list, &dev->amthif_cmd_list);
/*
* The previous request is still in processing, queue this one.
@ -211,7 +210,7 @@ unsigned int mei_amthif_poll(struct file *file, poll_table *wait)
* Return: 0, OK; otherwise, error.
*/
int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list)
struct list_head *cmpl_list)
{
int ret;
@ -237,7 +236,7 @@ int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
*/
int mei_amthif_irq_read_msg(struct mei_cl *cl,
struct mei_msg_hdr *mei_hdr,
struct mei_cl_cb *cmpl_list)
struct list_head *cmpl_list)
{
struct mei_device *dev;
int ret;
@ -311,51 +310,31 @@ void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
}
}
/**
* mei_clear_list - removes all callbacks associated with file
* from mei_cb_list
*
* @file: file structure
* @mei_cb_list: callbacks list
*
* mei_clear_list is called to clear resources associated with file
* when application calls close function or Ctrl-C was pressed
*/
static void mei_clear_list(const struct file *file,
struct list_head *mei_cb_list)
{
struct mei_cl_cb *cb, *next;
list_for_each_entry_safe(cb, next, mei_cb_list, list)
if (file == cb->fp)
mei_io_cb_free(cb);
}
/**
* mei_amthif_release - the release function
*
* @dev: device structure
* @file: pointer to file structure
* @fp: pointer to file structure
*
* Return: 0 on success, <0 on error
*/
int mei_amthif_release(struct mei_device *dev, struct file *file)
int mei_amthif_release(struct mei_device *dev, struct file *fp)
{
struct mei_cl *cl = file->private_data;
struct mei_cl *cl = fp->private_data;
if (dev->iamthif_open_count > 0)
dev->iamthif_open_count--;
if (cl->fp == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) {
if (cl->fp == fp && dev->iamthif_state != MEI_IAMTHIF_IDLE) {
dev_dbg(dev->dev, "amthif canceled iamthif state %d\n",
dev->iamthif_state);
dev->iamthif_state);
dev->iamthif_canceled = true;
}
mei_clear_list(file, &dev->amthif_cmd_list.list);
mei_clear_list(file, &cl->rd_completed);
mei_clear_list(file, &dev->ctrl_rd_list.list);
/* Don't clean ctrl_rd_list here, the reads has to be completed */
mei_io_list_free_fp(&dev->amthif_cmd_list, fp);
mei_io_list_free_fp(&cl->rd_completed, fp);
return 0;
}

View File

@ -498,6 +498,25 @@ int mei_cldev_enable(struct mei_cl_device *cldev)
}
EXPORT_SYMBOL_GPL(mei_cldev_enable);
/**
* mei_cldev_unregister_callbacks - internal wrapper for unregistering
* callbacks.
*
* @cldev: client device
*/
static void mei_cldev_unregister_callbacks(struct mei_cl_device *cldev)
{
if (cldev->rx_cb) {
cancel_work_sync(&cldev->rx_work);
cldev->rx_cb = NULL;
}
if (cldev->notif_cb) {
cancel_work_sync(&cldev->notif_work);
cldev->notif_cb = NULL;
}
}
/**
* mei_cldev_disable - disable me client device
* disconnect form the me client
@ -519,6 +538,8 @@ int mei_cldev_disable(struct mei_cl_device *cldev)
bus = cldev->bus;
mei_cldev_unregister_callbacks(cldev);
mutex_lock(&bus->device_lock);
if (!mei_cl_is_connected(cl)) {
@ -541,6 +562,37 @@ int mei_cldev_disable(struct mei_cl_device *cldev)
}
EXPORT_SYMBOL_GPL(mei_cldev_disable);
/**
* mei_cl_bus_module_get - acquire module of the underlying
* hw module.
*
* @cl: host client
*
* Return: true on success; false if the module was removed.
*/
bool mei_cl_bus_module_get(struct mei_cl *cl)
{
struct mei_cl_device *cldev = cl->cldev;
if (!cldev)
return true;
return try_module_get(cldev->bus->dev->driver->owner);
}
/**
* mei_cl_bus_module_put - release the underlying hw module.
*
* @cl: host client
*/
void mei_cl_bus_module_put(struct mei_cl *cl)
{
struct mei_cl_device *cldev = cl->cldev;
if (cldev)
module_put(cldev->bus->dev->driver->owner);
}
/**
* mei_cl_device_find - find matching entry in the driver id table
*
@ -665,19 +717,12 @@ static int mei_cl_device_remove(struct device *dev)
if (!cldev || !dev->driver)
return 0;
if (cldev->rx_cb) {
cancel_work_sync(&cldev->rx_work);
cldev->rx_cb = NULL;
}
if (cldev->notif_cb) {
cancel_work_sync(&cldev->notif_work);
cldev->notif_cb = NULL;
}
cldrv = to_mei_cl_driver(dev->driver);
if (cldrv->remove)
ret = cldrv->remove(cldev);
mei_cldev_unregister_callbacks(cldev);
module_put(THIS_MODULE);
dev->driver = NULL;
return ret;

View File

@ -377,19 +377,19 @@ static struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl,
}
/**
* __mei_io_list_flush - removes and frees cbs belonging to cl.
* __mei_io_list_flush_cl - removes and frees cbs belonging to cl.
*
* @list: an instance of our list structure
* @head: an instance of our list structure
* @cl: host client, can be NULL for flushing the whole list
* @free: whether to free the cbs
*/
static void __mei_io_list_flush(struct mei_cl_cb *list,
struct mei_cl *cl, bool free)
static void __mei_io_list_flush_cl(struct list_head *head,
const struct mei_cl *cl, bool free)
{
struct mei_cl_cb *cb, *next;
/* enable removing everything if no cl is specified */
list_for_each_entry_safe(cb, next, &list->list, list) {
list_for_each_entry_safe(cb, next, head, list) {
if (!cl || mei_cl_cmp_id(cl, cb->cl)) {
list_del_init(&cb->list);
if (free)
@ -399,25 +399,42 @@ static void __mei_io_list_flush(struct mei_cl_cb *list,
}
/**
* mei_io_list_flush - removes list entry belonging to cl.
* mei_io_list_flush_cl - removes list entry belonging to cl.
*
* @list: An instance of our list structure
* @head: An instance of our list structure
* @cl: host client
*/
void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl)
static inline void mei_io_list_flush_cl(struct list_head *head,
const struct mei_cl *cl)
{
__mei_io_list_flush(list, cl, false);
__mei_io_list_flush_cl(head, cl, false);
}
/**
* mei_io_list_free - removes cb belonging to cl and free them
* mei_io_list_free_cl - removes cb belonging to cl and free them
*
* @list: An instance of our list structure
* @head: An instance of our list structure
* @cl: host client
*/
static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl)
static inline void mei_io_list_free_cl(struct list_head *head,
const struct mei_cl *cl)
{
__mei_io_list_flush(list, cl, true);
__mei_io_list_flush_cl(head, cl, true);
}
/**
* mei_io_list_free_fp - free cb from a list that matches file pointer
*
* @head: io list
* @fp: file pointer (matching cb file object), may be NULL
*/
void mei_io_list_free_fp(struct list_head *head, const struct file *fp)
{
struct mei_cl_cb *cb, *next;
list_for_each_entry_safe(cb, next, head, list)
if (!fp || fp == cb->fp)
mei_io_cb_free(cb);
}
/**
@ -479,7 +496,7 @@ struct mei_cl_cb *mei_cl_enqueue_ctrl_wr_cb(struct mei_cl *cl, size_t length,
if (!cb)
return NULL;
list_add_tail(&cb->list, &cl->dev->ctrl_wr_list.list);
list_add_tail(&cb->list, &cl->dev->ctrl_wr_list);
return cb;
}
@ -503,27 +520,6 @@ struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl, const struct file *fp)
return NULL;
}
/**
* mei_cl_read_cb_flush - free client's read pending and completed cbs
* for a specific file
*
* @cl: host client
* @fp: file pointer (matching cb file object), may be NULL
*/
void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp)
{
struct mei_cl_cb *cb, *next;
list_for_each_entry_safe(cb, next, &cl->rd_completed, list)
if (!fp || fp == cb->fp)
mei_io_cb_free(cb);
list_for_each_entry_safe(cb, next, &cl->rd_pending, list)
if (!fp || fp == cb->fp)
mei_io_cb_free(cb);
}
/**
* mei_cl_flush_queues - flushes queue lists belonging to cl.
*
@ -542,18 +538,16 @@ int mei_cl_flush_queues(struct mei_cl *cl, const struct file *fp)
dev = cl->dev;
cl_dbg(dev, cl, "remove list entry belonging to cl\n");
mei_io_list_free(&cl->dev->write_list, cl);
mei_io_list_free(&cl->dev->write_waiting_list, cl);
mei_io_list_flush(&cl->dev->ctrl_wr_list, cl);
mei_io_list_flush(&cl->dev->ctrl_rd_list, cl);
mei_io_list_flush(&cl->dev->amthif_cmd_list, cl);
mei_cl_read_cb_flush(cl, fp);
mei_io_list_free_cl(&cl->dev->write_list, cl);
mei_io_list_free_cl(&cl->dev->write_waiting_list, cl);
mei_io_list_flush_cl(&cl->dev->ctrl_wr_list, cl);
mei_io_list_flush_cl(&cl->dev->ctrl_rd_list, cl);
mei_io_list_free_fp(&cl->rd_pending, fp);
mei_io_list_free_fp(&cl->rd_completed, fp);
return 0;
}
/**
* mei_cl_init - initializes cl.
*
@ -756,7 +750,7 @@ static void mei_cl_wake_all(struct mei_cl *cl)
*
* @cl: host client
*/
void mei_cl_set_disconnected(struct mei_cl *cl)
static void mei_cl_set_disconnected(struct mei_cl *cl)
{
struct mei_device *dev = cl->dev;
@ -765,15 +759,18 @@ void mei_cl_set_disconnected(struct mei_cl *cl)
return;
cl->state = MEI_FILE_DISCONNECTED;
mei_io_list_free(&dev->write_list, cl);
mei_io_list_free(&dev->write_waiting_list, cl);
mei_io_list_flush(&dev->ctrl_rd_list, cl);
mei_io_list_flush(&dev->ctrl_wr_list, cl);
mei_io_list_free_cl(&dev->write_list, cl);
mei_io_list_free_cl(&dev->write_waiting_list, cl);
mei_io_list_flush_cl(&dev->ctrl_rd_list, cl);
mei_io_list_flush_cl(&dev->ctrl_wr_list, cl);
mei_io_list_free_cl(&dev->amthif_cmd_list, cl);
mei_cl_wake_all(cl);
cl->rx_flow_ctrl_creds = 0;
cl->tx_flow_ctrl_creds = 0;
cl->timer_count = 0;
mei_cl_bus_module_put(cl);
if (!cl->me_cl)
return;
@ -829,7 +826,7 @@ static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb)
return ret;
}
list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
list_move_tail(&cb->list, &dev->ctrl_rd_list);
cl->timer_count = MEI_CONNECT_TIMEOUT;
mei_schedule_stall_timer(dev);
@ -847,7 +844,7 @@ static int mei_cl_send_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb)
* Return: 0, OK; otherwise, error.
*/
int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list)
struct list_head *cmpl_list)
{
struct mei_device *dev = cl->dev;
u32 msg_slots;
@ -862,7 +859,7 @@ int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb,
ret = mei_cl_send_disconnect(cl, cb);
if (ret)
list_move_tail(&cb->list, &cmpl_list->list);
list_move_tail(&cb->list, cmpl_list);
return ret;
}
@ -984,7 +981,7 @@ static bool mei_cl_is_other_connecting(struct mei_cl *cl)
dev = cl->dev;
list_for_each_entry(cb, &dev->ctrl_rd_list.list, list) {
list_for_each_entry(cb, &dev->ctrl_rd_list, list) {
if (cb->fop_type == MEI_FOP_CONNECT &&
mei_cl_me_id(cl) == mei_cl_me_id(cb->cl))
return true;
@ -1015,7 +1012,7 @@ static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb)
return ret;
}
list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
list_move_tail(&cb->list, &dev->ctrl_rd_list);
cl->timer_count = MEI_CONNECT_TIMEOUT;
mei_schedule_stall_timer(dev);
return 0;
@ -1031,7 +1028,7 @@ static int mei_cl_send_connect(struct mei_cl *cl, struct mei_cl_cb *cb)
* Return: 0, OK; otherwise, error.
*/
int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list)
struct list_head *cmpl_list)
{
struct mei_device *dev = cl->dev;
u32 msg_slots;
@ -1049,7 +1046,7 @@ int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
rets = mei_cl_send_connect(cl, cb);
if (rets)
list_move_tail(&cb->list, &cmpl_list->list);
list_move_tail(&cb->list, cmpl_list);
return rets;
}
@ -1077,13 +1074,17 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
dev = cl->dev;
if (!mei_cl_bus_module_get(cl))
return -ENODEV;
rets = mei_cl_set_connecting(cl, me_cl);
if (rets)
return rets;
goto nortpm;
if (mei_cl_is_fixed_address(cl)) {
cl->state = MEI_FILE_CONNECTED;
return 0;
rets = 0;
goto nortpm;
}
rets = pm_runtime_get(dev->dev);
@ -1117,8 +1118,8 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
if (!mei_cl_is_connected(cl)) {
if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) {
mei_io_list_flush(&dev->ctrl_rd_list, cl);
mei_io_list_flush(&dev->ctrl_wr_list, cl);
mei_io_list_flush_cl(&dev->ctrl_rd_list, cl);
mei_io_list_flush_cl(&dev->ctrl_wr_list, cl);
/* ignore disconnect return valuue;
* in case of failure reset will be invoked
*/
@ -1270,7 +1271,7 @@ enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req)
* Return: 0 on such and error otherwise.
*/
int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list)
struct list_head *cmpl_list)
{
struct mei_device *dev = cl->dev;
u32 msg_slots;
@ -1288,11 +1289,11 @@ int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
ret = mei_hbm_cl_notify_req(dev, cl, request);
if (ret) {
cl->status = ret;
list_move_tail(&cb->list, &cmpl_list->list);
list_move_tail(&cb->list, cmpl_list);
return ret;
}
list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
list_move_tail(&cb->list, &dev->ctrl_rd_list);
return 0;
}
@ -1325,6 +1326,9 @@ int mei_cl_notify_request(struct mei_cl *cl,
return -EOPNOTSUPP;
}
if (!mei_cl_is_connected(cl))
return -ENODEV;
rets = pm_runtime_get(dev->dev);
if (rets < 0 && rets != -EINPROGRESS) {
pm_runtime_put_noidle(dev->dev);
@ -1344,7 +1348,7 @@ int mei_cl_notify_request(struct mei_cl *cl,
rets = -ENODEV;
goto out;
}
list_move_tail(&cb->list, &dev->ctrl_rd_list.list);
list_move_tail(&cb->list, &dev->ctrl_rd_list);
}
mutex_unlock(&dev->device_lock);
@ -1419,6 +1423,11 @@ int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev)
dev = cl->dev;
if (!dev->hbm_f_ev_supported) {
cl_dbg(dev, cl, "notifications not supported\n");
return -EOPNOTSUPP;
}
if (!mei_cl_is_connected(cl))
return -ENODEV;
@ -1519,7 +1528,7 @@ int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp)
* Return: 0, OK; otherwise error.
*/
int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list)
struct list_head *cmpl_list)
{
struct mei_device *dev;
struct mei_msg_data *buf;
@ -1591,13 +1600,13 @@ int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
}
if (mei_hdr.msg_complete)
list_move_tail(&cb->list, &dev->write_waiting_list.list);
list_move_tail(&cb->list, &dev->write_waiting_list);
return 0;
err:
cl->status = rets;
list_move_tail(&cb->list, &cmpl_list->list);
list_move_tail(&cb->list, cmpl_list);
return rets;
}
@ -1687,9 +1696,9 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
out:
if (mei_hdr.msg_complete)
list_add_tail(&cb->list, &dev->write_waiting_list.list);
list_add_tail(&cb->list, &dev->write_waiting_list);
else
list_add_tail(&cb->list, &dev->write_list.list);
list_add_tail(&cb->list, &dev->write_list);
cb = NULL;
if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {

View File

@ -83,17 +83,7 @@ static inline u8 mei_me_cl_ver(const struct mei_me_client *me_cl)
* MEI IO Functions
*/
void mei_io_cb_free(struct mei_cl_cb *priv_cb);
/**
* mei_io_list_init - Sets up a queue list.
*
* @list: An instance cl callback structure
*/
static inline void mei_io_list_init(struct mei_cl_cb *list)
{
INIT_LIST_HEAD(&list->list);
}
void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl);
void mei_io_list_free_fp(struct list_head *head, const struct file *fp);
/*
* MEI Host Client Functions
@ -110,7 +100,6 @@ struct mei_cl *mei_cl_alloc_linked(struct mei_device *dev);
struct mei_cl_cb *mei_cl_read_cb(const struct mei_cl *cl,
const struct file *fp);
void mei_cl_read_cb_flush(const struct mei_cl *cl, const struct file *fp);
struct mei_cl_cb *mei_cl_alloc_cb(struct mei_cl *cl, size_t length,
enum mei_cb_file_ops type,
const struct file *fp);
@ -209,19 +198,18 @@ static inline u8 mei_cl_host_addr(const struct mei_cl *cl)
}
int mei_cl_disconnect(struct mei_cl *cl);
void mei_cl_set_disconnected(struct mei_cl *cl);
int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list);
struct list_head *cmpl_list);
int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl,
const struct file *file);
int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list);
struct list_head *cmpl_list);
int mei_cl_read_start(struct mei_cl *cl, size_t length, const struct file *fp);
int mei_cl_irq_read_msg(struct mei_cl *cl, struct mei_msg_hdr *hdr,
struct mei_cl_cb *cmpl_list);
struct list_head *cmpl_list);
int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb);
int mei_cl_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list);
struct list_head *cmpl_list);
void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb);
@ -232,7 +220,7 @@ enum mei_cb_file_ops mei_cl_notify_req2fop(u8 request);
int mei_cl_notify_request(struct mei_cl *cl,
const struct file *file, u8 request);
int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list);
struct list_head *cmpl_list);
int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev);
void mei_cl_notify(struct mei_cl *cl);

View File

@ -815,7 +815,7 @@ static void mei_hbm_cl_res(struct mei_device *dev,
struct mei_cl_cb *cb, *next;
cl = NULL;
list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list.list, list) {
list_for_each_entry_safe(cb, next, &dev->ctrl_rd_list, list) {
cl = cb->cl;

View File

@ -139,6 +139,19 @@ static inline void mei_hcsr_set(struct mei_device *dev, u32 reg)
mei_hcsr_write(dev, reg);
}
/**
* mei_hcsr_set_hig - set host interrupt (set H_IG)
*
* @dev: the device structure
*/
static inline void mei_hcsr_set_hig(struct mei_device *dev)
{
u32 hcsr;
hcsr = mei_hcsr_read(dev) | H_IG;
mei_hcsr_set(dev, hcsr);
}
/**
* mei_me_d0i3c_read - Reads 32bit data from the D0I3C register
*
@ -380,6 +393,19 @@ static bool mei_me_hw_is_ready(struct mei_device *dev)
return (mecsr & ME_RDY_HRA) == ME_RDY_HRA;
}
/**
* mei_me_hw_is_resetting - check whether the me(hw) is in reset
*
* @dev: mei device
* Return: bool
*/
static bool mei_me_hw_is_resetting(struct mei_device *dev)
{
u32 mecsr = mei_me_mecsr_read(dev);
return (mecsr & ME_RST_HRA) == ME_RST_HRA;
}
/**
* mei_me_hw_ready_wait - wait until the me(hw) has turned ready
* or timeout is reached
@ -505,7 +531,6 @@ static int mei_me_hbuf_write(struct mei_device *dev,
unsigned long rem;
unsigned long length = header->length;
u32 *reg_buf = (u32 *)buf;
u32 hcsr;
u32 dw_cnt;
int i;
int empty_slots;
@ -532,8 +557,7 @@ static int mei_me_hbuf_write(struct mei_device *dev,
mei_me_hcbww_write(dev, reg);
}
hcsr = mei_hcsr_read(dev) | H_IG;
mei_hcsr_set(dev, hcsr);
mei_hcsr_set_hig(dev);
if (!mei_me_hw_is_ready(dev))
return -EIO;
@ -580,7 +604,6 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer,
unsigned long buffer_length)
{
u32 *reg_buf = (u32 *)buffer;
u32 hcsr;
for (; buffer_length >= sizeof(u32); buffer_length -= sizeof(u32))
*reg_buf++ = mei_me_mecbrw_read(dev);
@ -591,8 +614,7 @@ static int mei_me_read_slots(struct mei_device *dev, unsigned char *buffer,
memcpy(reg_buf, &reg, buffer_length);
}
hcsr = mei_hcsr_read(dev) | H_IG;
mei_hcsr_set(dev, hcsr);
mei_hcsr_set_hig(dev);
return 0;
}
@ -1189,7 +1211,7 @@ irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id)
irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
{
struct mei_device *dev = (struct mei_device *) dev_id;
struct mei_cl_cb complete_list;
struct list_head cmpl_list;
s32 slots;
u32 hcsr;
int rets = 0;
@ -1201,7 +1223,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
hcsr = mei_hcsr_read(dev);
me_intr_clear(dev, hcsr);
mei_io_list_init(&complete_list);
INIT_LIST_HEAD(&cmpl_list);
/* check if ME wants a reset */
if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) {
@ -1210,6 +1232,9 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
goto end;
}
if (mei_me_hw_is_resetting(dev))
mei_hcsr_set_hig(dev);
mei_me_pg_intr(dev, me_intr_src(hcsr));
/* check if we need to start the dev */
@ -1227,7 +1252,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
slots = mei_count_full_read_slots(dev);
while (slots > 0) {
dev_dbg(dev->dev, "slots to read = %08x\n", slots);
rets = mei_irq_read_handler(dev, &complete_list, &slots);
rets = mei_irq_read_handler(dev, &cmpl_list, &slots);
/* There is a race between ME write and interrupt delivery:
* Not all data is always available immediately after the
* interrupt, so try to read again on the next interrupt.
@ -1252,11 +1277,11 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
*/
if (dev->pg_event != MEI_PG_EVENT_WAIT &&
dev->pg_event != MEI_PG_EVENT_RECEIVED) {
rets = mei_irq_write_handler(dev, &complete_list);
rets = mei_irq_write_handler(dev, &cmpl_list);
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
}
mei_irq_compl_handler(dev, &complete_list);
mei_irq_compl_handler(dev, &cmpl_list);
end:
dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets);
@ -1389,7 +1414,7 @@ const struct mei_cfg mei_me_pch8_sps_cfg = {
* @pdev: The pci device structure
* @cfg: per device generation config
*
* Return: The mei_device_device pointer on success, NULL on failure.
* Return: The mei_device pointer on success, NULL on failure.
*/
struct mei_device *mei_me_dev_init(struct pci_dev *pdev,
const struct mei_cfg *cfg)
@ -1397,8 +1422,8 @@ struct mei_device *mei_me_dev_init(struct pci_dev *pdev,
struct mei_device *dev;
struct mei_me_hw *hw;
dev = kzalloc(sizeof(struct mei_device) +
sizeof(struct mei_me_hw), GFP_KERNEL);
dev = devm_kzalloc(&pdev->dev, sizeof(struct mei_device) +
sizeof(struct mei_me_hw), GFP_KERNEL);
if (!dev)
return NULL;
hw = to_me_hw(dev);

View File

@ -1057,7 +1057,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
{
struct mei_device *dev = (struct mei_device *) dev_id;
struct mei_txe_hw *hw = to_txe_hw(dev);
struct mei_cl_cb complete_list;
struct list_head cmpl_list;
s32 slots;
int rets = 0;
@ -1069,7 +1069,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
/* initialize our complete list */
mutex_lock(&dev->device_lock);
mei_io_list_init(&complete_list);
INIT_LIST_HEAD(&cmpl_list);
if (pci_dev_msi_enabled(to_pci_dev(dev->dev)))
mei_txe_check_and_ack_intrs(dev, true);
@ -1126,7 +1126,7 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
slots = mei_count_full_read_slots(dev);
if (test_and_clear_bit(TXE_INTR_OUT_DB_BIT, &hw->intr_cause)) {
/* Read from TXE */
rets = mei_irq_read_handler(dev, &complete_list, &slots);
rets = mei_irq_read_handler(dev, &cmpl_list, &slots);
if (rets && dev->dev_state != MEI_DEV_RESETTING) {
dev_err(dev->dev,
"mei_irq_read_handler ret = %d.\n", rets);
@ -1144,14 +1144,14 @@ irqreturn_t mei_txe_irq_thread_handler(int irq, void *dev_id)
if (hw->aliveness && dev->hbuf_is_ready) {
/* get the real register value */
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
rets = mei_irq_write_handler(dev, &complete_list);
rets = mei_irq_write_handler(dev, &cmpl_list);
if (rets && rets != -EMSGSIZE)
dev_err(dev->dev, "mei_irq_write_handler ret = %d.\n",
rets);
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
}
mei_irq_compl_handler(dev, &complete_list);
mei_irq_compl_handler(dev, &cmpl_list);
end:
dev_dbg(dev->dev, "interrupt thread end ret = %d\n", rets);
@ -1207,8 +1207,8 @@ struct mei_device *mei_txe_dev_init(struct pci_dev *pdev)
struct mei_device *dev;
struct mei_txe_hw *hw;
dev = kzalloc(sizeof(struct mei_device) +
sizeof(struct mei_txe_hw), GFP_KERNEL);
dev = devm_kzalloc(&pdev->dev, sizeof(struct mei_device) +
sizeof(struct mei_txe_hw), GFP_KERNEL);
if (!dev)
return NULL;

View File

@ -45,7 +45,7 @@
* @intr_cause: translated interrupt cause
*/
struct mei_txe_hw {
void __iomem *mem_addr[NUM_OF_MEM_BARS];
void __iomem * const *mem_addr;
u32 aliveness;
u32 readiness;
u32 slots;

View File

@ -349,16 +349,16 @@ EXPORT_SYMBOL_GPL(mei_stop);
bool mei_write_is_idle(struct mei_device *dev)
{
bool idle = (dev->dev_state == MEI_DEV_ENABLED &&
list_empty(&dev->ctrl_wr_list.list) &&
list_empty(&dev->write_list.list) &&
list_empty(&dev->write_waiting_list.list));
list_empty(&dev->ctrl_wr_list) &&
list_empty(&dev->write_list) &&
list_empty(&dev->write_waiting_list));
dev_dbg(dev->dev, "write pg: is idle[%d] state=%s ctrl=%01d write=%01d wwait=%01d\n",
idle,
mei_dev_state_str(dev->dev_state),
list_empty(&dev->ctrl_wr_list.list),
list_empty(&dev->write_list.list),
list_empty(&dev->write_waiting_list.list));
list_empty(&dev->ctrl_wr_list),
list_empty(&dev->write_list),
list_empty(&dev->write_waiting_list));
return idle;
}
@ -388,17 +388,17 @@ void mei_device_init(struct mei_device *dev,
dev->dev_state = MEI_DEV_INITIALIZING;
dev->reset_count = 0;
mei_io_list_init(&dev->write_list);
mei_io_list_init(&dev->write_waiting_list);
mei_io_list_init(&dev->ctrl_wr_list);
mei_io_list_init(&dev->ctrl_rd_list);
INIT_LIST_HEAD(&dev->write_list);
INIT_LIST_HEAD(&dev->write_waiting_list);
INIT_LIST_HEAD(&dev->ctrl_wr_list);
INIT_LIST_HEAD(&dev->ctrl_rd_list);
INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
INIT_WORK(&dev->reset_work, mei_reset_work);
INIT_WORK(&dev->bus_rescan_work, mei_cl_bus_rescan_work);
INIT_LIST_HEAD(&dev->iamthif_cl.link);
mei_io_list_init(&dev->amthif_cmd_list);
INIT_LIST_HEAD(&dev->amthif_cmd_list);
bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
dev->open_handle_count = 0;

View File

@ -35,14 +35,14 @@
* for the completed callbacks
*
* @dev: mei device
* @compl_list: list of completed cbs
* @cmpl_list: list of completed cbs
*/
void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list)
void mei_irq_compl_handler(struct mei_device *dev, struct list_head *cmpl_list)
{
struct mei_cl_cb *cb, *next;
struct mei_cl *cl;
list_for_each_entry_safe(cb, next, &compl_list->list, list) {
list_for_each_entry_safe(cb, next, cmpl_list, list) {
cl = cb->cl;
list_del_init(&cb->list);
@ -92,13 +92,13 @@ void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr)
*
* @cl: reading client
* @mei_hdr: header of mei client message
* @complete_list: completion list
* @cmpl_list: completion list
*
* Return: always 0
*/
int mei_cl_irq_read_msg(struct mei_cl *cl,
struct mei_msg_hdr *mei_hdr,
struct mei_cl_cb *complete_list)
struct list_head *cmpl_list)
{
struct mei_device *dev = cl->dev;
struct mei_cl_cb *cb;
@ -144,7 +144,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
if (mei_hdr->msg_complete) {
cl_dbg(dev, cl, "completed read length = %zu\n", cb->buf_idx);
list_move_tail(&cb->list, &complete_list->list);
list_move_tail(&cb->list, cmpl_list);
} else {
pm_runtime_mark_last_busy(dev->dev);
pm_request_autosuspend(dev->dev);
@ -154,7 +154,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
discard:
if (cb)
list_move_tail(&cb->list, &complete_list->list);
list_move_tail(&cb->list, cmpl_list);
mei_irq_discard_msg(dev, mei_hdr);
return 0;
}
@ -169,7 +169,7 @@ int mei_cl_irq_read_msg(struct mei_cl *cl,
* Return: 0, OK; otherwise, error.
*/
static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list)
struct list_head *cmpl_list)
{
struct mei_device *dev = cl->dev;
u32 msg_slots;
@ -183,7 +183,7 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb,
return -EMSGSIZE;
ret = mei_hbm_cl_disconnect_rsp(dev, cl);
list_move_tail(&cb->list, &cmpl_list->list);
list_move_tail(&cb->list, cmpl_list);
return ret;
}
@ -199,7 +199,7 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb,
* Return: 0, OK; otherwise, error.
*/
static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list)
struct list_head *cmpl_list)
{
struct mei_device *dev = cl->dev;
u32 msg_slots;
@ -219,7 +219,7 @@ static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb,
if (ret) {
cl->status = ret;
cb->buf_idx = 0;
list_move_tail(&cb->list, &cmpl_list->list);
list_move_tail(&cb->list, cmpl_list);
return ret;
}
@ -249,7 +249,7 @@ static inline bool hdr_is_fixed(struct mei_msg_hdr *mei_hdr)
* Return: 0 on success, <0 on failure.
*/
int mei_irq_read_handler(struct mei_device *dev,
struct mei_cl_cb *cmpl_list, s32 *slots)
struct list_head *cmpl_list, s32 *slots)
{
struct mei_msg_hdr *mei_hdr;
struct mei_cl *cl;
@ -347,12 +347,11 @@ EXPORT_SYMBOL_GPL(mei_irq_read_handler);
*
* Return: 0 on success, <0 on failure.
*/
int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
int mei_irq_write_handler(struct mei_device *dev, struct list_head *cmpl_list)
{
struct mei_cl *cl;
struct mei_cl_cb *cb, *next;
struct mei_cl_cb *list;
s32 slots;
int ret;
@ -367,19 +366,18 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
/* complete all waiting for write CB */
dev_dbg(dev->dev, "complete all waiting for write cb.\n");
list = &dev->write_waiting_list;
list_for_each_entry_safe(cb, next, &list->list, list) {
list_for_each_entry_safe(cb, next, &dev->write_waiting_list, list) {
cl = cb->cl;
cl->status = 0;
cl_dbg(dev, cl, "MEI WRITE COMPLETE\n");
cl->writing_state = MEI_WRITE_COMPLETE;
list_move_tail(&cb->list, &cmpl_list->list);
list_move_tail(&cb->list, cmpl_list);
}
/* complete control write list CB */
dev_dbg(dev->dev, "complete control write list cb.\n");
list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list.list, list) {
list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list, list) {
cl = cb->cl;
switch (cb->fop_type) {
case MEI_FOP_DISCONNECT:
@ -423,7 +421,7 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
}
/* complete write list CB */
dev_dbg(dev->dev, "complete write list cb.\n");
list_for_each_entry_safe(cb, next, &dev->write_list.list, list) {
list_for_each_entry_safe(cb, next, &dev->write_list, list) {
cl = cb->cl;
if (cl == &dev->iamthif_cl)
ret = mei_amthif_irq_write(cl, cb, cmpl_list);

View File

@ -182,32 +182,36 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
goto out;
}
if (rets == -EBUSY &&
!mei_cl_enqueue_ctrl_wr_cb(cl, length, MEI_FOP_READ, file)) {
rets = -ENOMEM;
again:
mutex_unlock(&dev->device_lock);
if (wait_event_interruptible(cl->rx_wait,
!list_empty(&cl->rd_completed) ||
!mei_cl_is_connected(cl))) {
if (signal_pending(current))
return -EINTR;
return -ERESTARTSYS;
}
mutex_lock(&dev->device_lock);
if (!mei_cl_is_connected(cl)) {
rets = -ENODEV;
goto out;
}
do {
mutex_unlock(&dev->device_lock);
cb = mei_cl_read_cb(cl, file);
if (!cb) {
/*
* For amthif all the waiters are woken up,
* but only fp with matching cb->fp get the cb,
* the others have to return to wait on read.
*/
if (cl == &dev->iamthif_cl)
goto again;
if (wait_event_interruptible(cl->rx_wait,
(!list_empty(&cl->rd_completed)) ||
(!mei_cl_is_connected(cl)))) {
if (signal_pending(current))
return -EINTR;
return -ERESTARTSYS;
}
mutex_lock(&dev->device_lock);
if (!mei_cl_is_connected(cl)) {
rets = -ENODEV;
goto out;
}
cb = mei_cl_read_cb(cl, file);
} while (!cb);
rets = 0;
goto out;
}
copy_buffer:
/* now copy the data to user space */

View File

@ -328,6 +328,8 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length,
bool mei_cl_bus_rx_event(struct mei_cl *cl);
bool mei_cl_bus_notify_event(struct mei_cl *cl);
void mei_cl_bus_remove_devices(struct mei_device *bus);
bool mei_cl_bus_module_get(struct mei_cl *cl);
void mei_cl_bus_module_put(struct mei_cl *cl);
int mei_cl_bus_init(void);
void mei_cl_bus_exit(void);
@ -439,10 +441,10 @@ struct mei_device {
struct cdev cdev;
int minor;
struct mei_cl_cb write_list;
struct mei_cl_cb write_waiting_list;
struct mei_cl_cb ctrl_wr_list;
struct mei_cl_cb ctrl_rd_list;
struct list_head write_list;
struct list_head write_waiting_list;
struct list_head ctrl_wr_list;
struct list_head ctrl_rd_list;
struct list_head file_list;
long open_handle_count;
@ -499,7 +501,7 @@ struct mei_device {
bool override_fixed_address;
/* amthif list for cmd waiting */
struct mei_cl_cb amthif_cmd_list;
struct list_head amthif_cmd_list;
struct mei_cl iamthif_cl;
long iamthif_open_count;
u32 iamthif_stall_timer;
@ -571,10 +573,10 @@ void mei_cancel_work(struct mei_device *dev);
void mei_timer(struct work_struct *work);
void mei_schedule_stall_timer(struct mei_device *dev);
int mei_irq_read_handler(struct mei_device *dev,
struct mei_cl_cb *cmpl_list, s32 *slots);
struct list_head *cmpl_list, s32 *slots);
int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list);
void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list);
int mei_irq_write_handler(struct mei_device *dev, struct list_head *cmpl_list);
void mei_irq_compl_handler(struct mei_device *dev, struct list_head *cmpl_list);
/*
* AMTHIF - AMT Host Interface Functions
@ -590,12 +592,12 @@ int mei_amthif_release(struct mei_device *dev, struct file *file);
int mei_amthif_write(struct mei_cl *cl, struct mei_cl_cb *cb);
int mei_amthif_run_next_cmd(struct mei_device *dev);
int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list);
struct list_head *cmpl_list);
void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb);
int mei_amthif_irq_read_msg(struct mei_cl *cl,
struct mei_msg_hdr *mei_hdr,
struct mei_cl_cb *complete_list);
struct list_head *cmpl_list);
int mei_amthif_irq_read(struct mei_device *dev, s32 *slots);
/*

View File

@ -149,18 +149,18 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return -ENODEV;
/* enable pci dev */
err = pci_enable_device(pdev);
err = pcim_enable_device(pdev);
if (err) {
dev_err(&pdev->dev, "failed to enable pci device.\n");
goto end;
}
/* set PCI host mastering */
pci_set_master(pdev);
/* pci request regions for mei driver */
err = pci_request_regions(pdev, KBUILD_MODNAME);
/* pci request regions and mapping IO device memory for mei driver */
err = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME);
if (err) {
dev_err(&pdev->dev, "failed to get pci regions.\n");
goto disable_device;
goto end;
}
if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) ||
@ -173,24 +173,18 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
if (err) {
dev_err(&pdev->dev, "No usable DMA configuration, aborting\n");
goto release_regions;
goto end;
}
/* allocates and initializes the mei dev structure */
dev = mei_me_dev_init(pdev, cfg);
if (!dev) {
err = -ENOMEM;
goto release_regions;
goto end;
}
hw = to_me_hw(dev);
/* mapping IO device memory */
hw->mem_addr = pci_iomap(pdev, 0, 0);
if (!hw->mem_addr) {
dev_err(&pdev->dev, "mapping I/O device memory failure.\n");
err = -ENOMEM;
goto free_device;
}
hw->mem_addr = pcim_iomap_table(pdev)[0];
pci_enable_msi(pdev);
/* request and enable interrupt */
@ -203,7 +197,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (err) {
dev_err(&pdev->dev, "request_threaded_irq failure. irq = %d\n",
pdev->irq);
goto disable_msi;
goto end;
}
if (mei_start(dev)) {
@ -242,15 +236,6 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
mei_cancel_work(dev);
mei_disable_interrupts(dev);
free_irq(pdev->irq, dev);
disable_msi:
pci_disable_msi(pdev);
pci_iounmap(pdev, hw->mem_addr);
free_device:
kfree(dev);
release_regions:
pci_release_regions(pdev);
disable_device:
pci_disable_device(pdev);
end:
dev_err(&pdev->dev, "initialization failed.\n");
return err;
@ -267,7 +252,6 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
static void mei_me_remove(struct pci_dev *pdev)
{
struct mei_device *dev;
struct mei_me_hw *hw;
dev = pci_get_drvdata(pdev);
if (!dev)
@ -276,33 +260,19 @@ static void mei_me_remove(struct pci_dev *pdev)
if (mei_pg_is_enabled(dev))
pm_runtime_get_noresume(&pdev->dev);
hw = to_me_hw(dev);
dev_dbg(&pdev->dev, "stop\n");
mei_stop(dev);
if (!pci_dev_run_wake(pdev))
mei_me_unset_pm_domain(dev);
/* disable interrupts */
mei_disable_interrupts(dev);
free_irq(pdev->irq, dev);
pci_disable_msi(pdev);
if (hw->mem_addr)
pci_iounmap(pdev, hw->mem_addr);
mei_deregister(dev);
kfree(dev);
pci_release_regions(pdev);
pci_disable_device(pdev);
}
#ifdef CONFIG_PM_SLEEP
static int mei_me_pci_suspend(struct device *device)
{

View File

@ -52,17 +52,6 @@ static inline void mei_txe_set_pm_domain(struct mei_device *dev) {}
static inline void mei_txe_unset_pm_domain(struct mei_device *dev) {}
#endif /* CONFIG_PM */
static void mei_txe_pci_iounmap(struct pci_dev *pdev, struct mei_txe_hw *hw)
{
int i;
for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) {
if (hw->mem_addr[i]) {
pci_iounmap(pdev, hw->mem_addr[i]);
hw->mem_addr[i] = NULL;
}
}
}
/**
* mei_txe_probe - Device Initialization Routine
*
@ -75,22 +64,22 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct mei_device *dev;
struct mei_txe_hw *hw;
const int mask = BIT(SEC_BAR) | BIT(BRIDGE_BAR);
int err;
int i;
/* enable pci dev */
err = pci_enable_device(pdev);
err = pcim_enable_device(pdev);
if (err) {
dev_err(&pdev->dev, "failed to enable pci device.\n");
goto end;
}
/* set PCI host mastering */
pci_set_master(pdev);
/* pci request regions for mei driver */
err = pci_request_regions(pdev, KBUILD_MODNAME);
/* pci request regions and mapping IO device memory for mei driver */
err = pcim_iomap_regions(pdev, mask, KBUILD_MODNAME);
if (err) {
dev_err(&pdev->dev, "failed to get pci regions.\n");
goto disable_device;
goto end;
}
err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
@ -98,7 +87,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (err) {
dev_err(&pdev->dev, "No suitable DMA available.\n");
goto release_regions;
goto end;
}
}
@ -106,20 +95,10 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
dev = mei_txe_dev_init(pdev);
if (!dev) {
err = -ENOMEM;
goto release_regions;
goto end;
}
hw = to_txe_hw(dev);
/* mapping IO device memory */
for (i = SEC_BAR; i < NUM_OF_MEM_BARS; i++) {
hw->mem_addr[i] = pci_iomap(pdev, i, 0);
if (!hw->mem_addr[i]) {
dev_err(&pdev->dev, "mapping I/O device memory failure.\n");
err = -ENOMEM;
goto free_device;
}
}
hw->mem_addr = pcim_iomap_table(pdev);
pci_enable_msi(pdev);
@ -140,7 +119,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (err) {
dev_err(&pdev->dev, "mei: request_threaded_irq failure. irq = %d\n",
pdev->irq);
goto free_device;
goto end;
}
if (mei_start(dev)) {
@ -173,23 +152,9 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
stop:
mei_stop(dev);
release_irq:
mei_cancel_work(dev);
/* disable interrupts */
mei_disable_interrupts(dev);
free_irq(pdev->irq, dev);
pci_disable_msi(pdev);
free_device:
mei_txe_pci_iounmap(pdev, hw);
kfree(dev);
release_regions:
pci_release_regions(pdev);
disable_device:
pci_disable_device(pdev);
end:
dev_err(&pdev->dev, "initialization failed.\n");
return err;
@ -206,38 +171,24 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
static void mei_txe_remove(struct pci_dev *pdev)
{
struct mei_device *dev;
struct mei_txe_hw *hw;
dev = pci_get_drvdata(pdev);
if (!dev) {
dev_err(&pdev->dev, "mei: dev =NULL\n");
dev_err(&pdev->dev, "mei: dev == NULL\n");
return;
}
pm_runtime_get_noresume(&pdev->dev);
hw = to_txe_hw(dev);
mei_stop(dev);
if (!pci_dev_run_wake(pdev))
mei_txe_unset_pm_domain(dev);
/* disable interrupts */
mei_disable_interrupts(dev);
free_irq(pdev->irq, dev);
pci_disable_msi(pdev);
pci_set_drvdata(pdev, NULL);
mei_txe_pci_iounmap(pdev, hw);
mei_deregister(dev);
kfree(dev);
pci_release_regions(pdev);
pci_disable_device(pdev);
}

View File

@ -292,7 +292,6 @@ static int vop_virtio_add_device(struct vop_vdev *vdev,
if (ret) {
dev_err(vop_dev(vdev), "%s %d err %d\n",
__func__, __LINE__, ret);
kfree(vdev);
return ret;
}

View File

@ -56,6 +56,7 @@
#include <linux/list.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/workqueue.h>
#include <generated/utsrelease.h>
#include <linux/io.h>
@ -64,8 +65,6 @@
#define LCD_MINOR 156
#define KEYPAD_MINOR 185
#define PANEL_VERSION "0.9.5"
#define LCD_MAXBYTES 256 /* max burst write */
#define KEYPAD_BUFFER 64
@ -77,8 +76,8 @@
/* a key repeats this times INPUT_POLL_TIME */
#define KEYPAD_REP_DELAY (2)
/* keep the light on this times INPUT_POLL_TIME for each flash */
#define FLASH_LIGHT_TEMPO (200)
/* keep the light on this many seconds for each flash */
#define FLASH_LIGHT_TEMPO (4)
/* converts an r_str() input to an active high, bits string : 000BAOSE */
#define PNL_PINPUT(a) ((((unsigned char)(a)) ^ 0x7F) >> 3)
@ -121,8 +120,6 @@
#define PIN_SELECP 17
#define PIN_NOT_SET 127
#define LCD_FLAG_S 0x0001
#define LCD_FLAG_ID 0x0002
#define LCD_FLAG_B 0x0004 /* blink on */
#define LCD_FLAG_C 0x0008 /* cursor on */
#define LCD_FLAG_D 0x0010 /* display on */
@ -256,7 +253,10 @@ static struct {
int hwidth;
int charset;
int proto;
int light_tempo;
struct delayed_work bl_work;
struct mutex bl_tempo_lock; /* Protects access to bl_tempo */
bool bl_tempo;
/* TODO: use union here? */
struct {
@ -661,8 +661,6 @@ static void lcd_get_bits(unsigned int port, int *val)
}
}
static void init_scan_timer(void);
/* sets data port bits according to current signals values */
static int set_data_bits(void)
{
@ -794,11 +792,8 @@ static void lcd_send_serial(int byte)
}
/* turn the backlight on or off */
static void lcd_backlight(int on)
static void __lcd_backlight(int on)
{
if (lcd.pins.bl == PIN_NONE)
return;
/* The backlight is activated by setting the AUTOFEED line to +5V */
spin_lock_irq(&pprt_lock);
if (on)
@ -809,6 +804,44 @@ static void lcd_backlight(int on)
spin_unlock_irq(&pprt_lock);
}
static void lcd_backlight(int on)
{
if (lcd.pins.bl == PIN_NONE)
return;
mutex_lock(&lcd.bl_tempo_lock);
if (!lcd.bl_tempo)
__lcd_backlight(on);
mutex_unlock(&lcd.bl_tempo_lock);
}
static void lcd_bl_off(struct work_struct *work)
{
mutex_lock(&lcd.bl_tempo_lock);
if (lcd.bl_tempo) {
lcd.bl_tempo = false;
if (!(lcd.flags & LCD_FLAG_L))
__lcd_backlight(0);
}
mutex_unlock(&lcd.bl_tempo_lock);
}
/* turn the backlight on for a little while */
static void lcd_poke(void)
{
if (lcd.pins.bl == PIN_NONE)
return;
cancel_delayed_work_sync(&lcd.bl_work);
mutex_lock(&lcd.bl_tempo_lock);
if (!lcd.bl_tempo && !(lcd.flags & LCD_FLAG_L))
__lcd_backlight(1);
lcd.bl_tempo = true;
schedule_delayed_work(&lcd.bl_work, FLASH_LIGHT_TEMPO * HZ);
mutex_unlock(&lcd.bl_tempo_lock);
}
/* send a command to the LCD panel in serial mode */
static void lcd_write_cmd_s(int cmd)
{
@ -907,6 +940,13 @@ static void lcd_gotoxy(void)
(lcd.hwidth - 1) : lcd.bwidth - 1));
}
static void lcd_home(void)
{
lcd.addr.x = 0;
lcd.addr.y = 0;
lcd_gotoxy();
}
static void lcd_print(char c)
{
if (lcd.addr.x < lcd.bwidth) {
@ -925,9 +965,7 @@ static void lcd_clear_fast_s(void)
{
int pos;
lcd.addr.x = 0;
lcd.addr.y = 0;
lcd_gotoxy();
lcd_home();
spin_lock_irq(&pprt_lock);
for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
@ -939,9 +977,7 @@ static void lcd_clear_fast_s(void)
}
spin_unlock_irq(&pprt_lock);
lcd.addr.x = 0;
lcd.addr.y = 0;
lcd_gotoxy();
lcd_home();
}
/* fills the display with spaces and resets X/Y */
@ -949,9 +985,7 @@ static void lcd_clear_fast_p8(void)
{
int pos;
lcd.addr.x = 0;
lcd.addr.y = 0;
lcd_gotoxy();
lcd_home();
spin_lock_irq(&pprt_lock);
for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
@ -977,9 +1011,7 @@ static void lcd_clear_fast_p8(void)
}
spin_unlock_irq(&pprt_lock);
lcd.addr.x = 0;
lcd.addr.y = 0;
lcd_gotoxy();
lcd_home();
}
/* fills the display with spaces and resets X/Y */
@ -987,9 +1019,7 @@ static void lcd_clear_fast_tilcd(void)
{
int pos;
lcd.addr.x = 0;
lcd.addr.y = 0;
lcd_gotoxy();
lcd_home();
spin_lock_irq(&pprt_lock);
for (pos = 0; pos < lcd.height * lcd.hwidth; pos++) {
@ -1000,9 +1030,7 @@ static void lcd_clear_fast_tilcd(void)
spin_unlock_irq(&pprt_lock);
lcd.addr.x = 0;
lcd.addr.y = 0;
lcd_gotoxy();
lcd_home();
}
/* clears the display and resets X/Y */
@ -1108,13 +1136,8 @@ static inline int handle_lcd_special_code(void)
processed = 1;
break;
case '*':
/* flash back light using the keypad timer */
if (scan_timer.function) {
if (lcd.light_tempo == 0 &&
((lcd.flags & LCD_FLAG_L) == 0))
lcd_backlight(1);
lcd.light_tempo = FLASH_LIGHT_TEMPO;
}
/* flash back light */
lcd_poke();
processed = 1;
break;
case 'f': /* Small Font */
@ -1278,21 +1301,14 @@ static inline int handle_lcd_special_code(void)
lcd_write_cmd(LCD_CMD_FUNCTION_SET
| LCD_CMD_DATA_LEN_8BITS
| ((lcd.flags & LCD_FLAG_F)
? LCD_CMD_TWO_LINES : 0)
| ((lcd.flags & LCD_FLAG_N)
? LCD_CMD_FONT_5X10_DOTS
: 0)
| ((lcd.flags & LCD_FLAG_N)
? LCD_CMD_TWO_LINES
: 0));
/* check whether L flag was changed */
else if ((oldflags ^ lcd.flags) & (LCD_FLAG_L)) {
if (lcd.flags & (LCD_FLAG_L))
lcd_backlight(1);
else if (lcd.light_tempo == 0)
/*
* switch off the light only when the tempo
* lighting is gone
*/
lcd_backlight(0);
}
else if ((oldflags ^ lcd.flags) & (LCD_FLAG_L))
lcd_backlight(!!(lcd.flags & LCD_FLAG_L));
}
return processed;
@ -1376,9 +1392,7 @@ static void lcd_write_char(char c)
processed = 1;
} else if (!strcmp(lcd.esc_seq.buf, "[H")) {
/* cursor to home */
lcd.addr.x = 0;
lcd.addr.y = 0;
lcd_gotoxy();
lcd_home();
processed = 1;
}
/* codes starting with ^[[L */
@ -1625,8 +1639,10 @@ static void lcd_init(void)
else
lcd_char_conv = NULL;
if (lcd.pins.bl != PIN_NONE)
init_scan_timer();
if (lcd.pins.bl != PIN_NONE) {
mutex_init(&lcd.bl_tempo_lock);
INIT_DELAYED_WORK(&lcd.bl_work, lcd_bl_off);
}
pin_to_bits(lcd.pins.e, lcd_bits[LCD_PORT_D][LCD_BIT_E],
lcd_bits[LCD_PORT_C][LCD_BIT_E]);
@ -1655,14 +1671,11 @@ static void lcd_init(void)
panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*" CONFIG_PANEL_BOOT_MESSAGE);
#endif
#else
panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE "\nPanel-"
PANEL_VERSION);
panel_lcd_print("\x1b[Lc\x1b[Lb\x1b[L*Linux-" UTS_RELEASE);
#endif
lcd.addr.x = 0;
lcd.addr.y = 0;
/* clear the display on the next device opening */
lcd.must_clear = true;
lcd_gotoxy();
lcd_home();
}
/*
@ -1997,19 +2010,8 @@ static void panel_scan_timer(void)
panel_process_inputs();
}
if (lcd.enabled && lcd.initialized) {
if (keypressed) {
if (lcd.light_tempo == 0 &&
((lcd.flags & LCD_FLAG_L) == 0))
lcd_backlight(1);
lcd.light_tempo = FLASH_LIGHT_TEMPO;
} else if (lcd.light_tempo > 0) {
lcd.light_tempo--;
if (lcd.light_tempo == 0 &&
((lcd.flags & LCD_FLAG_L) == 0))
lcd_backlight(0);
}
}
if (keypressed && lcd.enabled && lcd.initialized)
lcd_poke();
mod_timer(&scan_timer, jiffies + INPUT_POLL_TIME);
}
@ -2270,25 +2272,26 @@ static void panel_detach(struct parport *port)
if (scan_timer.function)
del_timer_sync(&scan_timer);
if (pprt) {
if (keypad.enabled) {
misc_deregister(&keypad_dev);
keypad_initialized = 0;
}
if (lcd.enabled) {
panel_lcd_print("\x0cLCD driver " PANEL_VERSION
"\nunloaded.\x1b[Lc\x1b[Lb\x1b[L-");
misc_deregister(&lcd_dev);
lcd.initialized = false;
}
/* TODO: free all input signals */
parport_release(pprt);
parport_unregister_device(pprt);
pprt = NULL;
unregister_reboot_notifier(&panel_notifier);
if (keypad.enabled) {
misc_deregister(&keypad_dev);
keypad_initialized = 0;
}
if (lcd.enabled) {
panel_lcd_print("\x0cLCD driver unloaded.\x1b[Lc\x1b[Lb\x1b[L-");
misc_deregister(&lcd_dev);
if (lcd.pins.bl != PIN_NONE) {
cancel_delayed_work_sync(&lcd.bl_work);
__lcd_backlight(0);
}
lcd.initialized = false;
}
/* TODO: free all input signals */
parport_release(pprt);
parport_unregister_device(pprt);
pprt = NULL;
unregister_reboot_notifier(&panel_notifier);
}
static struct parport_driver panel_driver = {
@ -2400,7 +2403,7 @@ static int __init panel_init_module(void)
if (!lcd.enabled && !keypad.enabled) {
/* no device enabled, let's exit */
pr_err("driver version " PANEL_VERSION " disabled.\n");
pr_err("panel driver disabled.\n");
return -ENODEV;
}
@ -2411,12 +2414,10 @@ static int __init panel_init_module(void)
}
if (pprt)
pr_info("driver version " PANEL_VERSION
" registered on parport%d (io=0x%lx).\n", parport,
pprt->port->base);
pr_info("panel driver registered on parport%d (io=0x%lx).\n",
parport, pprt->port->base);
else
pr_info("driver version " PANEL_VERSION
" not yet registered\n");
pr_info("panel driver not yet registered\n");
return 0;
}

105
drivers/misc/sram-exec.c Normal file
View File

@ -0,0 +1,105 @@
/*
* SRAM protect-exec region helper functions
*
* Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/
* Dave Gerlach
*
* 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 "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/device.h>
#include <linux/genalloc.h>
#include <linux/sram.h>
#include <asm/cacheflush.h>
#include "sram.h"
static DEFINE_MUTEX(exec_pool_list_mutex);
static LIST_HEAD(exec_pool_list);
int sram_check_protect_exec(struct sram_dev *sram, struct sram_reserve *block,
struct sram_partition *part)
{
unsigned long base = (unsigned long)part->base;
unsigned long end = base + block->size;
if (!PAGE_ALIGNED(base) || !PAGE_ALIGNED(end)) {
dev_err(sram->dev,
"SRAM pool marked with 'protect-exec' is not page aligned and will not be created.\n");
return -ENOMEM;
}
return 0;
}
int sram_add_protect_exec(struct sram_partition *part)
{
mutex_lock(&exec_pool_list_mutex);
list_add_tail(&part->list, &exec_pool_list);
mutex_unlock(&exec_pool_list_mutex);
return 0;
}
/**
* sram_exec_copy - copy data to a protected executable region of sram
*
* @pool: struct gen_pool retrieved that is part of this sram
* @dst: Destination address for the copy, that must be inside pool
* @src: Source address for the data to copy
* @size: Size of copy to perform, which starting from dst, must reside in pool
*
* This helper function allows sram driver to act as central control location
* of 'protect-exec' pools which are normal sram pools but are always set
* read-only and executable except when copying data to them, at which point
* they are set to read-write non-executable, to make sure no memory is
* writeable and executable at the same time. This region must be page-aligned
* and is checked during probe, otherwise page attribute manipulation would
* not be possible.
*/
int sram_exec_copy(struct gen_pool *pool, void *dst, void *src,
size_t size)
{
struct sram_partition *part = NULL, *p;
unsigned long base;
int pages;
mutex_lock(&exec_pool_list_mutex);
list_for_each_entry(p, &exec_pool_list, list) {
if (p->pool == pool)
part = p;
}
mutex_unlock(&exec_pool_list_mutex);
if (!part)
return -EINVAL;
if (!addr_in_gen_pool(pool, (unsigned long)dst, size))
return -EINVAL;
base = (unsigned long)part->base;
pages = PAGE_ALIGN(size) / PAGE_SIZE;
mutex_lock(&part->lock);
set_memory_nx((unsigned long)base, pages);
set_memory_rw((unsigned long)base, pages);
memcpy(dst, src, size);
set_memory_ro((unsigned long)base, pages);
set_memory_x((unsigned long)base, pages);
mutex_unlock(&part->lock);
return 0;
}
EXPORT_SYMBOL_GPL(sram_exec_copy);

View File

@ -31,36 +31,10 @@
#include <linux/mfd/syscon.h>
#include <soc/at91/atmel-secumod.h>
#include "sram.h"
#define SRAM_GRANULARITY 32
struct sram_partition {
void __iomem *base;
struct gen_pool *pool;
struct bin_attribute battr;
struct mutex lock;
};
struct sram_dev {
struct device *dev;
void __iomem *virt_base;
struct gen_pool *pool;
struct clk *clk;
struct sram_partition *partition;
u32 partitions;
};
struct sram_reserve {
struct list_head list;
u32 start;
u32 size;
bool export;
bool pool;
const char *label;
};
static ssize_t sram_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t pos, size_t count)
@ -148,6 +122,18 @@ static int sram_add_partition(struct sram_dev *sram, struct sram_reserve *block,
if (ret)
return ret;
}
if (block->protect_exec) {
ret = sram_check_protect_exec(sram, block, part);
if (ret)
return ret;
ret = sram_add_pool(sram, block, start, part);
if (ret)
return ret;
sram_add_protect_exec(part);
}
sram->partitions++;
return 0;
@ -233,7 +219,11 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res)
if (of_find_property(child, "pool", NULL))
block->pool = true;
if ((block->export || block->pool) && block->size) {
if (of_find_property(child, "protect-exec", NULL))
block->protect_exec = true;
if ((block->export || block->pool || block->protect_exec) &&
block->size) {
exports++;
label = NULL;
@ -249,8 +239,10 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res)
block->label = devm_kstrdup(sram->dev,
label, GFP_KERNEL);
if (!block->label)
if (!block->label) {
ret = -ENOMEM;
goto err_chunks;
}
dev_dbg(sram->dev, "found %sblock '%s' 0x%x-0x%x\n",
block->export ? "exported " : "", block->label,
@ -293,7 +285,8 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res)
goto err_chunks;
}
if ((block->export || block->pool) && block->size) {
if ((block->export || block->pool || block->protect_exec) &&
block->size) {
ret = sram_add_partition(sram, block,
res->start + block->start);
if (ret) {

58
drivers/misc/sram.h Normal file
View File

@ -0,0 +1,58 @@
/*
* Defines for the SRAM driver
*
* 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.
*/
#ifndef __SRAM_H
#define __SRAM_H
struct sram_partition {
void __iomem *base;
struct gen_pool *pool;
struct bin_attribute battr;
struct mutex lock;
struct list_head list;
};
struct sram_dev {
struct device *dev;
void __iomem *virt_base;
struct gen_pool *pool;
struct clk *clk;
struct sram_partition *partition;
u32 partitions;
};
struct sram_reserve {
struct list_head list;
u32 start;
u32 size;
bool export;
bool pool;
bool protect_exec;
const char *label;
};
#ifdef CONFIG_SRAM_EXEC
int sram_check_protect_exec(struct sram_dev *sram, struct sram_reserve *block,
struct sram_partition *part);
int sram_add_protect_exec(struct sram_partition *part);
#else
static inline int sram_check_protect_exec(struct sram_dev *sram,
struct sram_reserve *block,
struct sram_partition *part)
{
return -ENODEV;
}
static inline int sram_add_protect_exec(struct sram_partition *part)
{
return -ENODEV;
}
#endif /* CONFIG_SRAM_EXEC */
#endif /* __SRAM_H */

View File

@ -54,10 +54,7 @@ struct vmci_guest_device {
struct device *dev; /* PCI device we are attached to */
void __iomem *iobase;
unsigned int irq;
unsigned int intr_type;
bool exclusive_vectors;
struct msix_entry msix_entries[VMCI_MAX_INTRS];
struct tasklet_struct datagram_tasklet;
struct tasklet_struct bm_tasklet;
@ -368,30 +365,6 @@ static void vmci_process_bitmap(unsigned long data)
vmci_dbell_scan_notification_entries(dev->notification_bitmap);
}
/*
* Enable MSI-X. Try exclusive vectors first, then shared vectors.
*/
static int vmci_enable_msix(struct pci_dev *pdev,
struct vmci_guest_device *vmci_dev)
{
int i;
int result;
for (i = 0; i < VMCI_MAX_INTRS; ++i) {
vmci_dev->msix_entries[i].entry = i;
vmci_dev->msix_entries[i].vector = i;
}
result = pci_enable_msix_exact(pdev,
vmci_dev->msix_entries, VMCI_MAX_INTRS);
if (result == 0)
vmci_dev->exclusive_vectors = true;
else if (result == -ENOSPC)
result = pci_enable_msix_exact(pdev, vmci_dev->msix_entries, 1);
return result;
}
/*
* Interrupt handler for legacy or MSI interrupt, or for first MSI-X
* interrupt (vector VMCI_INTR_DATAGRAM).
@ -406,7 +379,7 @@ static irqreturn_t vmci_interrupt(int irq, void *_dev)
* Otherwise we must read the ICR to determine what to do.
*/
if (dev->intr_type == VMCI_INTR_TYPE_MSIX && dev->exclusive_vectors) {
if (dev->exclusive_vectors) {
tasklet_schedule(&dev->datagram_tasklet);
} else {
unsigned int icr;
@ -491,7 +464,6 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
}
vmci_dev->dev = &pdev->dev;
vmci_dev->intr_type = VMCI_INTR_TYPE_INTX;
vmci_dev->exclusive_vectors = false;
vmci_dev->iobase = iobase;
@ -592,26 +564,26 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
* Enable interrupts. Try MSI-X first, then MSI, and then fallback on
* legacy interrupts.
*/
if (!vmci_disable_msix && !vmci_enable_msix(pdev, vmci_dev)) {
vmci_dev->intr_type = VMCI_INTR_TYPE_MSIX;
vmci_dev->irq = vmci_dev->msix_entries[0].vector;
} else if (!vmci_disable_msi && !pci_enable_msi(pdev)) {
vmci_dev->intr_type = VMCI_INTR_TYPE_MSI;
vmci_dev->irq = pdev->irq;
error = pci_alloc_irq_vectors(pdev, VMCI_MAX_INTRS, VMCI_MAX_INTRS,
PCI_IRQ_MSIX);
if (error) {
error = pci_alloc_irq_vectors(pdev, 1, 1,
PCI_IRQ_MSIX | PCI_IRQ_MSI | PCI_IRQ_LEGACY);
if (error)
goto err_remove_bitmap;
} else {
vmci_dev->intr_type = VMCI_INTR_TYPE_INTX;
vmci_dev->irq = pdev->irq;
vmci_dev->exclusive_vectors = true;
}
/*
* Request IRQ for legacy or MSI interrupts, or for first
* MSI-X vector.
*/
error = request_irq(vmci_dev->irq, vmci_interrupt, IRQF_SHARED,
KBUILD_MODNAME, vmci_dev);
error = request_irq(pci_irq_vector(pdev, 0), vmci_interrupt,
IRQF_SHARED, KBUILD_MODNAME, vmci_dev);
if (error) {
dev_err(&pdev->dev, "Irq %u in use: %d\n",
vmci_dev->irq, error);
pci_irq_vector(pdev, 0), error);
goto err_disable_msi;
}
@ -622,13 +594,13 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
* between the vectors.
*/
if (vmci_dev->exclusive_vectors) {
error = request_irq(vmci_dev->msix_entries[1].vector,
error = request_irq(pci_irq_vector(pdev, 1),
vmci_interrupt_bm, 0, KBUILD_MODNAME,
vmci_dev);
if (error) {
dev_err(&pdev->dev,
"Failed to allocate irq %u: %d\n",
vmci_dev->msix_entries[1].vector, error);
pci_irq_vector(pdev, 1), error);
goto err_free_irq;
}
}
@ -651,15 +623,12 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
return 0;
err_free_irq:
free_irq(vmci_dev->irq, vmci_dev);
free_irq(pci_irq_vector(pdev, 0), vmci_dev);
tasklet_kill(&vmci_dev->datagram_tasklet);
tasklet_kill(&vmci_dev->bm_tasklet);
err_disable_msi:
if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSIX)
pci_disable_msix(pdev);
else if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSI)
pci_disable_msi(pdev);
pci_free_irq_vectors(pdev);
vmci_err = vmci_event_unsubscribe(ctx_update_sub_id);
if (vmci_err < VMCI_SUCCESS)
@ -719,14 +688,10 @@ static void vmci_guest_remove_device(struct pci_dev *pdev)
* MSI-X, we might have multiple vectors, each with their own
* IRQ, which we must free too.
*/
free_irq(vmci_dev->irq, vmci_dev);
if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSIX) {
if (vmci_dev->exclusive_vectors)
free_irq(vmci_dev->msix_entries[1].vector, vmci_dev);
pci_disable_msix(pdev);
} else if (vmci_dev->intr_type == VMCI_INTR_TYPE_MSI) {
pci_disable_msi(pdev);
}
if (vmci_dev->exclusive_vectors)
free_irq(pci_irq_vector(pdev, 1), vmci_dev);
free_irq(pci_irq_vector(pdev, 0), vmci_dev);
pci_free_irq_vectors(pdev);
tasklet_kill(&vmci_dev->datagram_tasklet);
tasklet_kill(&vmci_dev->bm_tasklet);

View File

@ -703,8 +703,6 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device,
char *dest = start + (section_index * net_device->send_section_size)
+ pend_size;
int i;
bool is_data_pkt = (skb != NULL) ? true : false;
bool xmit_more = (skb != NULL) ? skb->xmit_more : false;
u32 msg_size = 0;
u32 padding = 0;
u32 remain = packet->total_data_buflen % net_device->pkt_align;
@ -712,7 +710,7 @@ static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device,
packet->page_buf_cnt;
/* Add padding */
if (is_data_pkt && xmit_more && remain &&
if (skb && skb->xmit_more && remain &&
!packet->cp_partial) {
padding = net_device->pkt_align - remain;
rndis_msg->msg_len += padding;
@ -754,7 +752,6 @@ static inline int netvsc_send_pkt(
int ret;
struct hv_page_buffer *pgbuf;
u32 ring_avail = hv_ringbuf_avail_percent(&out_channel->outbound);
bool xmit_more = (skb != NULL) ? skb->xmit_more : false;
nvmsg.hdr.msg_type = NVSP_MSG1_TYPE_SEND_RNDIS_PKT;
if (skb != NULL) {
@ -778,16 +775,6 @@ static inline int netvsc_send_pkt(
if (out_channel->rescind)
return -ENODEV;
/*
* It is possible that once we successfully place this packet
* on the ringbuffer, we may stop the queue. In that case, we want
* to notify the host independent of the xmit_more flag. We don't
* need to be precise here; in the worst case we may signal the host
* unnecessarily.
*/
if (ring_avail < (RING_AVAIL_PERCENT_LOWATER + 1))
xmit_more = false;
if (packet->page_buf_cnt) {
pgbuf = packet->cp_partial ? (*pb) +
packet->rmsg_pgcnt : (*pb);
@ -797,15 +784,13 @@ static inline int netvsc_send_pkt(
&nvmsg,
sizeof(struct nvsp_message),
req_id,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED,
!xmit_more);
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
} else {
ret = vmbus_sendpacket_ctl(out_channel, &nvmsg,
sizeof(struct nvsp_message),
req_id,
VM_PKT_DATA_INBAND,
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED,
!xmit_more);
VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
}
if (ret == 0) {

View File

@ -608,7 +608,7 @@ static struct nvmem_device *nvmem_find(const char *name)
/**
* of_nvmem_device_get() - Get nvmem device from a given id
*
* @dev node: Device tree node that uses the nvmem device
* @np: Device tree node that uses the nvmem device.
* @id: nvmem name from nvmem-names property.
*
* Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device
@ -634,8 +634,8 @@ EXPORT_SYMBOL_GPL(of_nvmem_device_get);
/**
* nvmem_device_get() - Get nvmem device from a given id
*
* @dev : Device that uses the nvmem device
* @id: nvmem name from nvmem-names property.
* @dev: Device that uses the nvmem device.
* @dev_name: name of the requested nvmem device.
*
* Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device
* on success.
@ -674,6 +674,7 @@ static void devm_nvmem_device_release(struct device *dev, void *res)
/**
* devm_nvmem_device_put() - put alredy got nvmem device
*
* @dev: Device that uses the nvmem device.
* @nvmem: pointer to nvmem device allocated by devm_nvmem_cell_get(),
* that needs to be released.
*/
@ -702,8 +703,8 @@ EXPORT_SYMBOL_GPL(nvmem_device_put);
/**
* devm_nvmem_device_get() - Get nvmem cell of device form a given id
*
* @dev node: Device tree node that uses the nvmem cell
* @id: nvmem name in nvmems property.
* @dev: Device that requests the nvmem device.
* @id: name id for the requested nvmem device.
*
* Return: ERR_PTR() on error or a valid pointer to a struct nvmem_cell
* on success. The nvmem_cell will be freed by the automatically once the
@ -745,8 +746,10 @@ static struct nvmem_cell *nvmem_cell_get_from_list(const char *cell_id)
/**
* of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id
*
* @dev node: Device tree node that uses the nvmem cell
* @id: nvmem cell name from nvmem-cell-names property.
* @np: Device tree node that uses the nvmem cell.
* @name: nvmem cell name from nvmem-cell-names property, or NULL
* for the cell at index 0 (the lone cell with no accompanying
* nvmem-cell-names property).
*
* Return: Will be an ERR_PTR() on error or a valid pointer
* to a struct nvmem_cell. The nvmem_cell will be freed by the
@ -759,9 +762,12 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
struct nvmem_cell *cell;
struct nvmem_device *nvmem;
const __be32 *addr;
int rval, len, index;
int rval, len;
int index = 0;
index = of_property_match_string(np, "nvmem-cell-names", name);
/* if cell name exists, find index to the name */
if (name)
index = of_property_match_string(np, "nvmem-cell-names", name);
cell_np = of_parse_phandle(np, "nvmem-cells", index);
if (!cell_np)
@ -830,8 +836,8 @@ EXPORT_SYMBOL_GPL(of_nvmem_cell_get);
/**
* nvmem_cell_get() - Get nvmem cell of device form a given cell name
*
* @dev node: Device tree node that uses the nvmem cell
* @id: nvmem cell name to get.
* @dev: Device that requests the nvmem cell.
* @cell_id: nvmem cell name to get.
*
* Return: Will be an ERR_PTR() on error or a valid pointer
* to a struct nvmem_cell. The nvmem_cell will be freed by the
@ -859,8 +865,8 @@ static void devm_nvmem_cell_release(struct device *dev, void *res)
/**
* devm_nvmem_cell_get() - Get nvmem cell of device form a given id
*
* @dev node: Device tree node that uses the nvmem cell
* @id: nvmem id in nvmem-names property.
* @dev: Device that requests the nvmem cell.
* @id: nvmem cell name id to get.
*
* Return: Will be an ERR_PTR() on error or a valid pointer
* to a struct nvmem_cell. The nvmem_cell will be freed by the
@ -900,7 +906,8 @@ static int devm_nvmem_cell_match(struct device *dev, void *res, void *data)
* devm_nvmem_cell_put() - Release previously allocated nvmem cell
* from devm_nvmem_cell_get.
*
* @cell: Previously allocated nvmem cell by devm_nvmem_cell_get()
* @dev: Device that requests the nvmem cell.
* @cell: Previously allocated nvmem cell by devm_nvmem_cell_get().
*/
void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell)
{
@ -916,7 +923,7 @@ EXPORT_SYMBOL(devm_nvmem_cell_put);
/**
* nvmem_cell_put() - Release previously allocated nvmem cell.
*
* @cell: Previously allocated nvmem cell by nvmem_cell_get()
* @cell: Previously allocated nvmem cell by nvmem_cell_get().
*/
void nvmem_cell_put(struct nvmem_cell *cell)
{
@ -970,7 +977,8 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem,
if (cell->bit_offset || cell->nbits)
nvmem_shift_read_buffer_in_place(cell, buf);
*len = cell->bytes;
if (len)
*len = cell->bytes;
return 0;
}
@ -979,7 +987,8 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem,
* nvmem_cell_read() - Read a given nvmem cell
*
* @cell: nvmem cell to be read.
* @len: pointer to length of cell which will be populated on successful read.
* @len: pointer to length of cell which will be populated on successful read;
* can be NULL.
*
* Return: ERR_PTR() on error or a valid pointer to a buffer on success. The
* buffer should be freed by the consumer with a kfree().
@ -1126,7 +1135,7 @@ EXPORT_SYMBOL_GPL(nvmem_device_cell_read);
* nvmem_device_cell_write() - Write cell to a given nvmem device
*
* @nvmem: nvmem device to be written to.
* @info: nvmem cell info to be written
* @info: nvmem cell info to be written.
* @buf: buffer to be written to cell.
*
* Return: length of bytes written or negative error code on failure.

View File

@ -73,6 +73,7 @@ static const struct of_device_id imx_ocotp_dt_ids[] = {
{ .compatible = "fsl,imx6q-ocotp", (void *)128 },
{ .compatible = "fsl,imx6sl-ocotp", (void *)64 },
{ .compatible = "fsl,imx6sx-ocotp", (void *)128 },
{ .compatible = "fsl,imx6ul-ocotp", (void *)128 },
{ },
};
MODULE_DEVICE_TABLE(of, imx_ocotp_dt_ids);

View File

@ -157,23 +157,26 @@ static int goldfish_new_pdev(void)
static irqreturn_t goldfish_pdev_bus_interrupt(int irq, void *dev_id)
{
irqreturn_t ret = IRQ_NONE;
while (1) {
u32 op = readl(pdev_bus_base + PDEV_BUS_OP);
switch (op) {
case PDEV_BUS_OP_DONE:
return IRQ_NONE;
switch (op) {
case PDEV_BUS_OP_REMOVE_DEV:
goldfish_pdev_remove();
ret = IRQ_HANDLED;
break;
case PDEV_BUS_OP_ADD_DEV:
goldfish_new_pdev();
ret = IRQ_HANDLED;
break;
case PDEV_BUS_OP_DONE:
default:
return ret;
}
ret = IRQ_HANDLED;
}
return ret;
}
static int goldfish_pdev_bus_probe(struct platform_device *pdev)

Some files were not shown because too many files have changed in this diff Show More