Commit f4dd029e authored by Linus Torvalds's avatar Linus Torvalds

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

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

  Lots of stuff in here, a large thunderbolt update, w1 driver header
  reorg, the new mux driver subsystem, google firmware driver updates,
  and a raft of other smaller things. Full details in the shortlog.

  All of these have been in linux-next for a while with the only
  reported issue being a merge problem with this tree and the jc-docs
  tree in the w1 documentation area"

* tag 'char-misc-4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (147 commits)
  misc: apds990x: Use sysfs_match_string() helper
  mei: drop unreachable code in mei_start
  mei: validate the message header only in first fragment.
  DocBook: w1: Update W1 file locations and names in DocBook
  mux: adg792a: always require I2C support
  nvmem: rockchip-efuse: add support for rk322x-efuse
  nvmem: core: add locking to nvmem_find_cell
  nvmem: core: Call put_device() in nvmem_unregister()
  nvmem: core: fix leaks on registration errors
  nvmem: correct Broadcom OTP controller driver writes
  w1: Add subsystem kernel public interface
  drivers/fsi: Add module license to core driver
  drivers/fsi: Use asynchronous slave mode
  drivers/fsi: Add hub master support
  drivers/fsi: Add SCOM FSI client device driver
  drivers/fsi/gpio: Add tracepoints for GPIO master
  drivers/fsi: Add GPIO based FSI master
  drivers/fsi: Document FSI master sysfs files in ABI
  drivers/fsi: Add error handling for slave
  drivers/fsi: Add tracepoints for low-level operations
  ...
parents 97466841 cbbdc608
What: /sys/bus/platform/devices/fsi-master/rescan
Date: May 2017
KernelVersion: 4.12
Contact: cbostic@linux.vnet.ibm.com
Description:
Initiates a FSI master scan for all connected slave devices
on its links.
What: /sys/bus/platform/devices/fsi-master/break
Date: May 2017
KernelVersion: 4.12
Contact: cbostic@linux.vnet.ibm.com
Description:
Sends an FSI BREAK command on a master's communication
link to any connnected slaves. A BREAK resets connected
device's logic and preps it to receive further commands
from the master.
What: /sys/bus/platform/devices/fsi-master/slave@00:00/term
Date: May 2017
KernelVersion: 4.12
Contact: cbostic@linux.vnet.ibm.com
Description:
Sends an FSI terminate command from the master to its
connected slave. A terminate resets the slave's state machines
that control access to the internally connected engines. In
addition the slave freezes its internal error register for
debugging purposes. This command is also needed to abort any
ongoing operation in case of an expired 'Master Time Out'
timer.
What: /sys/bus/platform/devices/fsi-master/slave@00:00/raw
Date: May 2017
KernelVersion: 4.12
Contact: cbostic@linux.vnet.ibm.com
Description:
Provides a means of reading/writing a 32 bit value from/to a
specified FSI bus address.
What: /sys/bus/thunderbolt/devices/.../domainX/security
Date: Sep 2017
KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org
Description: This attribute holds current Thunderbolt security level
set by the system BIOS. Possible values are:
none: All devices are automatically authorized
user: Devices are only authorized based on writing
appropriate value to the authorized attribute
secure: Require devices that support secure connect at
minimum. User needs to authorize each device.
dponly: Automatically tunnel Display port (and USB). No
PCIe tunnels are created.
What: /sys/bus/thunderbolt/devices/.../authorized
Date: Sep 2017
KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org
Description: This attribute is used to authorize Thunderbolt devices
after they have been connected. If the device is not
authorized, no devices such as PCIe and Display port are
available to the system.
Contents of this attribute will be 0 when the device is not
yet authorized.
Possible values are supported:
1: The device will be authorized and connected
When key attribute contains 32 byte hex string the possible
values are:
1: The 32 byte hex string is added to the device NVM and
the device is authorized.
2: Send a challenge based on the 32 byte hex string. If the
challenge response from device is valid, the device is
authorized. In case of failure errno will be ENOKEY if
the device did not contain a key at all, and
EKEYREJECTED if the challenge response did not match.
What: /sys/bus/thunderbolt/devices/.../key
Date: Sep 2017
KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org
Description: When a devices supports Thunderbolt secure connect it will
have this attribute. Writing 32 byte hex string changes
authorization to use the secure connection method instead.
What: /sys/bus/thunderbolt/devices/.../device
Date: Sep 2017
KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org
Description: This attribute contains id of this device extracted from
the device DROM.
What: /sys/bus/thunderbolt/devices/.../device_name
Date: Sep 2017
KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org
Description: This attribute contains name of this device extracted from
the device DROM.
What: /sys/bus/thunderbolt/devices/.../vendor
Date: Sep 2017
KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org
Description: This attribute contains vendor id of this device extracted
from the device DROM.
What: /sys/bus/thunderbolt/devices/.../vendor_name
Date: Sep 2017
KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org
Description: This attribute contains vendor name of this device extracted
from the device DROM.
What: /sys/bus/thunderbolt/devices/.../unique_id
Date: Sep 2017
KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org
Description: This attribute contains unique_id string of this device.
This is either read from hardware registers (UUID on
newer hardware) or based on UID from the device DROM.
Can be used to uniquely identify particular device.
What: /sys/bus/thunderbolt/devices/.../nvm_version
Date: Sep 2017
KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org
Description: If the device has upgradeable firmware the version
number is available here. Format: %x.%x, major.minor.
If the device is in safe mode reading the file returns
-ENODATA instead as the NVM version is not available.
What: /sys/bus/thunderbolt/devices/.../nvm_authenticate
Date: Sep 2017
KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org
Description: When new NVM image is written to the non-active NVM
area (through non_activeX NVMem device), the
authentication procedure is started by writing 1 to
this file. If everything goes well, the device is
restarted with the new NVM firmware. If the image
verification fails an error code is returned instead.
When read holds status of the last authentication
operation if an error occurred during the process. This
is directly the status value from the DMA configuration
based mailbox before the device is power cycled. Writing
0 here clears the status.
What: /sys/class/mux/
Date: April 2017
KernelVersion: 4.13
Contact: Peter Rosin <peda@axentia.se>
Description:
The mux/ class sub-directory belongs to the Generic MUX
Framework and provides a sysfs interface for using MUX
controllers.
What: /sys/class/mux/muxchipN/
Date: April 2017
KernelVersion: 4.13
Contact: Peter Rosin <peda@axentia.se>
Description:
A /sys/class/mux/muxchipN directory is created for each
probed MUX chip where N is a simple enumeration.
...@@ -51,9 +51,9 @@ ...@@ -51,9 +51,9 @@
<sect1 id="w1_internal_api"> <sect1 id="w1_internal_api">
<title>W1 API internal to the kernel</title> <title>W1 API internal to the kernel</title>
<sect2 id="w1.h"> <sect2 id="w1.h">
<title>drivers/w1/w1.h</title> <title>include/linux/w1.h</title>
<para>W1 core functions.</para> <para>W1 kernel API functions.</para>
!Idrivers/w1/w1.h !Iinclude/linux/w1.h
</sect2> </sect2>
<sect2 id="w1.c"> <sect2 id="w1.c">
...@@ -62,18 +62,18 @@ ...@@ -62,18 +62,18 @@
!Idrivers/w1/w1.c !Idrivers/w1/w1.c
</sect2> </sect2>
<sect2 id="w1_family.h">
<title>drivers/w1/w1_family.h</title>
<para>Allows registering device family operations.</para>
!Idrivers/w1/w1_family.h
</sect2>
<sect2 id="w1_family.c"> <sect2 id="w1_family.c">
<title>drivers/w1/w1_family.c</title> <title>drivers/w1/w1_family.c</title>
<para>Allows registering device family operations.</para> <para>Allows registering device family operations.</para>
!Edrivers/w1/w1_family.c !Edrivers/w1/w1_family.c
</sect2> </sect2>
<sect2 id="w1_internal.h">
<title>drivers/w1/w1_internal.h</title>
<para>W1 internal initialization for master devices.</para>
!Idrivers/w1/w1_internal.h
</sect2>
<sect2 id="w1_int.c"> <sect2 id="w1_int.c">
<title>drivers/w1/w1_int.c</title> <title>drivers/w1/w1_int.c</title>
<para>W1 internal initialization for master devices.</para> <para>W1 internal initialization for master devices.</para>
......
...@@ -369,8 +369,10 @@ ...@@ -369,8 +369,10 @@
237 = /dev/loop-control Loopback control device 237 = /dev/loop-control Loopback control device
238 = /dev/vhost-net Host kernel accelerator for virtio net 238 = /dev/vhost-net Host kernel accelerator for virtio net
239 = /dev/uhid User-space I/O driver support for HID subsystem 239 = /dev/uhid User-space I/O driver support for HID subsystem
240 = /dev/userio Serio driver testing device
241 = /dev/vhost-vsock Host kernel driver for virtio vsock
240-254 Reserved for local use 242-254 Reserved for local use
255 Reserved for MISC_DYNAMIC_MINOR 255 Reserved for MISC_DYNAMIC_MINOR
11 char Raw keyboard device (Linux/SPARC only) 11 char Raw keyboard device (Linux/SPARC only)
......
...@@ -61,6 +61,7 @@ configure specific aspects of kernel behavior to your liking. ...@@ -61,6 +61,7 @@ configure specific aspects of kernel behavior to your liking.
java java
ras ras
pm/index pm/index
thunderbolt
.. only:: subproject and html .. only:: subproject and html
......
...@@ -649,6 +649,13 @@ ...@@ -649,6 +649,13 @@
/proc/<pid>/coredump_filter. /proc/<pid>/coredump_filter.
See also Documentation/filesystems/proc.txt. See also Documentation/filesystems/proc.txt.
coresight_cpu_debug.enable
[ARM,ARM64]
Format: <bool>
Enable/disable the CPU sampling based debugging.
0: default value, disable debugging
1: enable debugging at boot time
cpuidle.off=1 [CPU_IDLE] cpuidle.off=1 [CPU_IDLE]
disable the cpuidle sub-system disable the cpuidle sub-system
......
=============
Thunderbolt
=============
The interface presented here is not meant for end users. Instead there
should be a userspace tool that handles all the low-level details, keeps
database of the authorized devices and prompts user for new connections.
More details about the sysfs interface for Thunderbolt devices can be
found in ``Documentation/ABI/testing/sysfs-bus-thunderbolt``.
Those users who just want to connect any device without any sort of
manual work, can add following line to
``/etc/udev/rules.d/99-local.rules``::
ACTION=="add", SUBSYSTEM=="thunderbolt", ATTR{authorized}=="0", ATTR{authorized}="1"
This will authorize all devices automatically when they appear. However,
keep in mind that this bypasses the security levels and makes the system
vulnerable to DMA attacks.
Security levels and how to use them
-----------------------------------
Starting from Intel Falcon Ridge Thunderbolt controller there are 4
security levels available. The reason for these is the fact that the
connected devices can be DMA masters and thus read contents of the host
memory without CPU and OS knowing about it. There are ways to prevent
this by setting up an IOMMU but it is not always available for various
reasons.
The security levels are as follows:
none
All devices are automatically connected by the firmware. No user
approval is needed. In BIOS settings this is typically called
*Legacy mode*.
user
User is asked whether the device is allowed to be connected.
Based on the device identification information available through
``/sys/bus/thunderbolt/devices``. user then can do the decision.
In BIOS settings this is typically called *Unique ID*.
secure
User is asked whether the device is allowed to be connected. In
addition to UUID the device (if it supports secure connect) is sent
a challenge that should match the expected one based on a random key
written to ``key`` sysfs attribute. In BIOS settings this is
typically called *One time saved key*.
dponly
The firmware automatically creates tunnels for Display Port and
USB. No PCIe tunneling is done. In BIOS settings this is
typically called *Display Port Only*.
The current security level can be read from
``/sys/bus/thunderbolt/devices/domainX/security`` where ``domainX`` is
the Thunderbolt domain the host controller manages. There is typically
one domain per Thunderbolt host controller.
If the security level reads as ``user`` or ``secure`` the connected
device must be authorized by the user before PCIe tunnels are created
(e.g the PCIe device appears).
Each Thunderbolt device plugged in will appear in sysfs under
``/sys/bus/thunderbolt/devices``. The device directory carries
information that can be used to identify the particular device,
including its name and UUID.
Authorizing devices when security level is ``user`` or ``secure``
-----------------------------------------------------------------
When a device is plugged in it will appear in sysfs as follows::
/sys/bus/thunderbolt/devices/0-1/authorized - 0
/sys/bus/thunderbolt/devices/0-1/device - 0x8004
/sys/bus/thunderbolt/devices/0-1/device_name - Thunderbolt to FireWire Adapter
/sys/bus/thunderbolt/devices/0-1/vendor - 0x1
/sys/bus/thunderbolt/devices/0-1/vendor_name - Apple, Inc.
/sys/bus/thunderbolt/devices/0-1/unique_id - e0376f00-0300-0100-ffff-ffffffffffff
The ``authorized`` attribute reads 0 which means no PCIe tunnels are
created yet. The user can authorize the device by simply::
# echo 1 > /sys/bus/thunderbolt/devices/0-1/authorized
This will create the PCIe tunnels and the device is now connected.
If the device supports secure connect, and the domain security level is
set to ``secure``, it has an additional attribute ``key`` which can hold
a random 32 byte value used for authorization and challenging the device in
future connects::
/sys/bus/thunderbolt/devices/0-3/authorized - 0
/sys/bus/thunderbolt/devices/0-3/device - 0x305
/sys/bus/thunderbolt/devices/0-3/device_name - AKiTiO Thunder3 PCIe Box
/sys/bus/thunderbolt/devices/0-3/key -
/sys/bus/thunderbolt/devices/0-3/vendor - 0x41
/sys/bus/thunderbolt/devices/0-3/vendor_name - inXtron
/sys/bus/thunderbolt/devices/0-3/unique_id - dc010000-0000-8508-a22d-32ca6421cb16
Notice the key is empty by default.
If the user does not want to use secure connect it can just ``echo 1``
to the ``authorized`` attribute and the PCIe tunnels will be created in
the same way than in ``user`` security level.
If the user wants to use secure connect, the first time the device is
plugged a key needs to be created and send to the device::
# key=$(openssl rand -hex 32)
# echo $key > /sys/bus/thunderbolt/devices/0-3/key
# echo 1 > /sys/bus/thunderbolt/devices/0-3/authorized
Now the device is connected (PCIe tunnels are created) and in addition
the key is stored on the device NVM.
Next time the device is plugged in the user can verify (challenge) the
device using the same key::
# echo $key > /sys/bus/thunderbolt/devices/0-3/key
# echo 2 > /sys/bus/thunderbolt/devices/0-3/authorized
If the challenge the device returns back matches the one we expect based
on the key, the device is connected and the PCIe tunnels are created.
However, if the challenge failed no tunnels are created and error is
returned to the user.
If the user still wants to connect the device it can either approve
the device without a key or write new key and write 1 to the
``authorized`` file to get the new key stored on the device NVM.
Upgrading NVM on Thunderbolt device or host
-------------------------------------------
Since most of the functionality is handled in a firmware running on a
host controller or a device, it is important that the firmware can be
upgraded to the latest where possible bugs in it have been fixed.
Typically OEMs provide this firmware from their support site.
There is also a central site which has links where to download firmwares
for some machines:
`Thunderbolt Updates <https://thunderbolttechnology.net/updates>`_
Before you upgrade firmware on a device or host, please make sure it is
the suitable. Failing to do that may render the device (or host) in a
state where it cannot be used properly anymore without special tools!
Host NVM upgrade on Apple Macs is not supported.
Once the NVM image has been downloaded, you need to plug in a
Thunderbolt device so that the host controller appears. It does not
matter which device is connected (unless you are upgrading NVM on a
device - then you need to connect that particular device).
Note OEM-specific method to power the controller up ("force power") may
be available for your system in which case there is no need to plug in a
Thunderbolt device.
After that we can write the firmware to the non-active parts of the NVM
of the host or device. As an example here is how Intel NUC6i7KYK (Skull
Canyon) Thunderbolt controller NVM is upgraded::
# dd if=KYK_TBT_FW_0018.bin of=/sys/bus/thunderbolt/devices/0-0/nvm_non_active0/nvmem
Once the operation completes we can trigger NVM authentication and
upgrade process as follows::
# echo 1 > /sys/bus/thunderbolt/devices/0-0/nvm_authenticate
If no errors are returned, the host controller shortly disappears. Once
it comes back the driver notices it and initiates a full power cycle.
After a while the host controller appears again and this time it should
be fully functional.
We can verify that the new NVM firmware is active by running following
commands::
# cat /sys/bus/thunderbolt/devices/0-0/nvm_authenticate
0x0
# cat /sys/bus/thunderbolt/devices/0-0/nvm_version
18.0
If ``nvm_authenticate`` contains anything else than 0x0 it is the error
code from the last authentication cycle, which means the authentication
of the NVM image failed.
Note names of the NVMem devices ``nvm_activeN`` and ``nvm_non_activeN``
depends on the order they are registered in the NVMem subsystem. N in
the name is the identifier added by the NVMem subsystem.
Upgrading NVM when host controller is in safe mode
--------------------------------------------------
If the existing NVM is not properly authenticated (or is missing) the
host controller goes into safe mode which means that only available
functionality is flashing new NVM image. When in this mode the reading
``nvm_version`` fails with ``ENODATA`` and the device identification
information is missing.
To recover from this mode, one needs to flash a valid NVM image to the
host host controller in the same way it is done in the previous chapter.
* CoreSight CPU Debug Component:
CoreSight CPU debug component are compliant with the ARMv8 architecture
reference manual (ARM DDI 0487A.k) Chapter 'Part H: External debug'. The
external debug module is mainly used for two modes: self-hosted debug and
external debug, and it can be accessed from mmio region from Coresight
and eventually the debug module connects with CPU for debugging. And the
debug module provides sample-based profiling extension, which can be used
to sample CPU program counter, secure state and exception level, etc;
usually every CPU has one dedicated debug module to be connected.
Required properties:
- compatible : should be "arm,coresight-cpu-debug"; supplemented with
"arm,primecell" since this driver is using the AMBA bus
interface.
- reg : physical base address and length of the register set.
- clocks : the clock associated to this component.
- clock-names : the name of the clock referenced by the code. Since we are
using the AMBA framework, the name of the clock providing
the interconnect should be "apb_pclk" and the clock is
mandatory. The interface between the debug logic and the
processor core is clocked by the internal CPU clock, so it
is enabled with CPU clock by default.
- cpu : the CPU phandle the debug module is affined to. When omitted
the module is considered to belong to CPU0.
Optional properties:
- power-domains: a phandle to the debug power domain. We use "power-domains"
binding to turn on the debug logic if it has own dedicated
power domain and if necessary to use "cpuidle.off=1" or
"nohlt" in the kernel command line or sysfs node to
constrain idle states to ensure registers in the CPU power
domain are accessible.
Example:
debug@f6590000 {
compatible = "arm,coresight-cpu-debug","arm,primecell";
reg = <0 0xf6590000 0 0x1000>;
clocks = <&sys_ctrl HI6220_DAPB_CLK>;
clock-names = "apb_pclk";
cpu = <&cpu0>;
};
Device-tree bindings for gpio-based FSI master driver
-----------------------------------------------------
Required properties:
- compatible = "fsi-master-gpio";
- clock-gpios = <gpio-descriptor>; : GPIO for FSI clock
- data-gpios = <gpio-descriptor>; : GPIO for FSI data signal
Optional properties:
- enable-gpios = <gpio-descriptor>; : GPIO for enable signal
- trans-gpios = <gpio-descriptor>; : GPIO for voltage translator enable
- mux-gpios = <gpio-descriptor>; : GPIO for pin multiplexing with other
functions (eg, external FSI masters)
Examples:
fsi-master {
compatible = "fsi-master-gpio", "fsi-master";
clock-gpios = <&gpio 0>;
data-gpios = <&gpio 1>;
enable-gpios = <&gpio 2>;
trans-gpios = <&gpio 3>;
mux-gpios = <&gpio 4>;
}
General Purpose I2C Bus Mux
This binding describes an I2C bus multiplexer that uses a mux controller
from the mux subsystem to route the I2C signals.
.-----. .-----.
| dev | | dev |
.------------. '-----' '-----'
| SoC | | |
| | .--------+--------'
| .------. | .------+ child bus A, on MUX value set to 0
| | I2C |-|--| Mux |
| '------' | '--+---+ child bus B, on MUX value set to 1
| .------. | | '----------+--------+--------.
| | MUX- | | | | | |
| | Ctrl |-|-----+ .-----. .-----. .-----.
| '------' | | dev | | dev | | dev |
'------------' '-----' '-----' '-----'
Required properties:
- compatible: i2c-mux
- i2c-parent: The phandle of the I2C bus that this multiplexer's master-side
port is connected to.
- mux-controls: The phandle of the mux controller to use for operating the
mux.
* Standard I2C mux properties. See i2c-mux.txt in this directory.
* I2C child bus nodes. See i2c-mux.txt in this directory. The sub-bus number
is also the mux-controller state described in ../mux/mux-controller.txt
Optional properties:
- mux-locked: If present, explicitly allow unrelated I2C transactions on the
parent I2C adapter at these times:
+ during setup of the multiplexer
+ between setup of the multiplexer and the child bus I2C transaction
+ between the child bus I2C transaction and releasing of the multiplexer
+ during releasing of the multiplexer
However, I2C transactions to devices behind all I2C multiplexers connected
to the same parent adapter that this multiplexer is connected to are blocked
for the full duration of the complete multiplexed I2C transaction (i.e.
including the times covered by the above list).
If mux-locked is not present, the multiplexer is assumed to be parent-locked.
This means that no unrelated I2C transactions are allowed on the parent I2C
adapter for the complete multiplexed I2C transaction.
The properties of mux-locked and parent-locked multiplexers are discussed
in more detail in Documentation/i2c/i2c-topology.
For each i2c child node, an I2C child bus will be created. They will
be numbered based on their order in the device tree.
Whenever an access is made to a device on a child bus, the value set
in the relevant node's reg property will be set as the state in the
mux controller.
Example:
mux: mux-controller {
compatible = "gpio-mux";
#mux-control-cells = <0>;
mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
<&pioA 1 GPIO_ACTIVE_HIGH>;
};
i2c-mux {
compatible = "i2c-mux";
mux-locked;
i2c-parent = <&i2c1>;
mux-controls = <&mux>;
#address-cells = <1>;
#size-cells = <0>;
i2c@1 {
reg = <1>;
#address-cells = <1>;
#size-cells = <0>;
ssd1307: oled@3c {
compatible = "solomon,ssd1307fb-i2c";
reg = <0x3c>;
pwms = <&pwm 4 3000>;
reset-gpios = <&gpio2 7 1>;
reset-active-low;
};
};
i2c@3 {
reg = <3>;
#address-cells = <1>;
#size-cells = <0>;
pca9555: pca9555@20 {
compatible = "nxp,pca9555";
gpio-controller;
#gpio-cells = <2>;
reg = <0x20>;
};
};
};
I/O channel multiplexer bindings
If a multiplexer is used to select which hardware signal is fed to
e.g. an ADC channel, these bindings describe that situation.
Required properties:
- compatible : "io-channel-mux"
- io-channels : Channel node of the parent channel that has multiplexed
input.
- io-channel-names : Should be "parent".
- #address-cells = <1>;
- #size-cells = <0>;
- mux-controls : Mux controller node to use for operating the mux
- channels : List of strings, labeling the mux controller states.
For each non-empty string in the channels property, an io-channel will
be created. The number of this io-channel is the same as the index into
the list of strings in the channels property, and also matches the mux
controller state. The mux controller state is described in
../mux/mux-controller.txt
Example:
mux: mux-controller {
compatible = "mux-gpio";
#mux-control-cells = <0>;
mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
<&pioA 1 GPIO_ACTIVE_HIGH>;
};
adc-mux {
compatible = "io-channel-mux";
io-channels = <&adc 0>;
io-channel-names = "parent";
mux-controls = <&mux>;
channels = "sync", "in", "system-regulator";
};
Bindings for Analog Devices ADG792A/G Triple 4:1 Multiplexers
Required properties:
- compatible : "adi,adg792a" or "adi,adg792g"
- #mux-control-cells : <0> if parallel (the three muxes are bound together
with a single mux controller controlling all three muxes), or <1> if
not (one mux controller for each mux).
* Standard mux-controller bindings as described in mux-controller.txt
Optional properties for ADG792G:
- gpio-controller : if present, #gpio-cells below is required.
- #gpio-cells : should be <2>
- First cell is the GPO line number, i.e. 0 or 1
- Second cell is used to specify active high (0)
or active low (1)
Optional properties:
- idle-state : if present, array of states that the mux controllers will have
when idle. The special state MUX_IDLE_AS_IS is the default and
MUX_IDLE_DISCONNECT is also supported.
States 0 through 3 correspond to signals A through D in the datasheet.
Example:
/*
* Three independent mux controllers (of which one is used).
* Mux 0 is disconnected when idle, mux 1 idles in the previously
* selected state and mux 2 idles with signal B.
*/
&i2c0 {
mux: mux-controller@50 {
compatible = "adi,adg792a";
reg = <0x50>;
#mux-control-cells = <1>;
idle-state = <MUX_IDLE_DISCONNECT MUX_IDLE_AS_IS 1>;
};
};
adc-mux {
compatible = "io-channel-mux";
io-channels = <&adc 0>;
io-channel-names = "parent";
mux-controls = <&mux 2>;
channels = "sync-1", "", "out";
};
/*
* Three parallel muxes with one mux controller, useful e.g. if
* the adc is differential, thus needing two signals to be muxed
* simultaneously for correct operation.
*/
&i2c0 {
pmux: mux-controller@50 {
compatible = "adi,adg792a";
reg = <0x50>;
#mux-control-cells = <0>;
idle-state = <1>;
};
};
diff-adc-mux {
compatible = "io-channel-mux";
io-channels = <&adc 0>;
io-channel-names = "parent";
mux-controls = <&pmux>;
channels = "sync-1", "", "out";
};
GPIO-based multiplexer controller bindings
Define what GPIO pins are used to control a multiplexer. Or several
multiplexers, if the same pins control more than one multiplexer.
Required properties:
- compatible : "gpio-mux"
- mux-gpios : list of gpios used to control the multiplexer, least
significant bit first.
- #mux-control-cells : <0>
* Standard mux-controller bindings as decribed in mux-controller.txt
Optional properties:
- idle-state : if present, the state the mux will have when idle. The
special state MUX_IDLE_AS_IS is the default.
The multiplexer state is defined as the number represented by the
multiplexer GPIO pins, where the first pin is the least significant
bit. An active pin is a binary 1, an inactive pin is a binary 0.
Example:
mux: mux-controller {
compatible = "gpio-mux";
#mux-control-cells = <0>;
mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
<&pioA 1 GPIO_ACTIVE_HIGH>;
};
adc-mux {
compatible = "io-channel-mux";
io-channels = <&adc 0>;
io-channel-names = "parent";
mux-controls = <&mux>;
channels = "sync-1", "in", "out", "sync-2";
};
i2c-mux {
compatible = "i2c-mux";
i2c-parent = <&i2c1>;
mux-controls = <&mux>;
#address-cells = <1>;
#size-cells = <0>;
i2c@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
ssd1307: oled@3c {
/* ... */
};
};
i2c@3 {
reg = <3>;
#address-cells = <1>;
#size-cells = <0>;
pca9555: pca9555@20 {
/* ... */
};
};
};
MMIO register bitfield-based multiplexer controller bindings
Define register bitfields to be used to control multiplexers. The parent
device tree node must be a syscon node to provide register access.
Required properties:
- compatible : "mmio-mux"
- #mux-control-cells : <1>
- mux-reg-masks : an array of register offset and pre-shifted bitfield mask
pairs, each describing a single mux control.
* Standard mux-controller bindings as decribed in mux-controller.txt
Optional properties:
- idle-states : if present, the state the muxes will have when idle. The
special state MUX_IDLE_AS_IS is the default.
The multiplexer state of each multiplexer is defined as the value of the
bitfield described by the corresponding register offset and bitfield mask pair
in the mux-reg-masks array, accessed through the parent syscon.
Example:
syscon {
compatible = "syscon";
mux: mux-controller {
compatible = "mmio-mux";
#mux-control-cells = <1>;
mux-reg-masks = <0x3 0x30>, /* 0: reg 0x3, bits 5:4 */
<0x3 0x40>, /* 1: reg 0x3, bit 6 */
idle-states = <MUX_IDLE_AS_IS>, <0>;
};
};
video-mux {
compatible = "video-mux";
mux-controls = <&mux 0>;
ports {
/* inputs 0..3 */
port@0 {
reg = <0>;
};
port@1 {
reg = <1>;
};
port@2 {
reg = <2>;
};
port@3 {
reg = <3>;
};
/* output */
port@4 {
reg = <4>;
};
};
};
Common multiplexer controller bindings
======================================
A multiplexer (or mux) controller will have one, or several, consumer devices
that uses the mux controller. Thus, a mux controller can possibly control
several parallel multiplexers. Presumably there will be at least one
multiplexer needed by each consumer, but a single mux controller can of course
control several multiplexers for a single consumer.
A mux controller provides a number of states to its consumers, and the state
space is a simple zero-based enumeration. I.e. 0-1 for a 2-way multiplexer,
0-7 for an 8-way multiplexer, etc.
Consumers
---------
Mux controller consumers should specify a list of mux controllers that they
want to use with a property containing a 'mux-ctrl-list':
mux-ctrl-list ::= <single-mux-ctrl> [mux-ctrl-list]
single-mux-ctrl ::= <mux-ctrl-phandle> [mux-ctrl-specifier]
mux-ctrl-phandle : phandle to mux controller node
mux-ctrl-specifier : array of #mux-control-cells specifying the
given mux controller (controller specific)
Mux controller properties should be named "mux-controls". The exact meaning of
each mux controller property must be documented in the device tree binding for
each consumer. An optional property "mux-control-names" may contain a list of
strings to label each of the mux controllers listed in the "mux-controls"
property.
Drivers for devices that use more than a single mux controller can use the
"mux-control-names" property to map the name of the requested mux controller
to an index into the list given by the "mux-controls" property.
mux-ctrl-specifier typically encodes the chip-relative mux controller number.
If the mux controller chip only provides a single mux controller, the
mux-ctrl-specifier can typically be left out.
Example:
/* One consumer of a 2-way mux controller (one GPIO-line) */
mux: mux-controller {
compatible = "gpio-mux";
#mux-control-cells = <0>;
mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>;
};
adc-mux {
compatible = "io-channel-mux";
io-channels = <&adc 0>;
io-channel-names = "parent";
mux-controls = <&mux>;
mux-control-names = "adc";
channels = "sync", "in";
};
Note that in the example above, specifying the "mux-control-names" is redundant
because there is only one mux controller in the list. However, if the driver
for the consumer node in fact asks for a named mux controller, that name is of
course still required.
/*
* Two consumers (one for an ADC line and one for an i2c bus) of
* parallel 4-way multiplexers controlled by the same two GPIO-lines.
*/
mux: mux-controller {
compatible = "gpio-mux";
#mux-control-cells = <0>;
mux-gpios = <&pioA 0 GPIO_ACTIVE_HIGH>,
<&pioA 1 GPIO_ACTIVE_HIGH>;
};
adc-mux {
compatible = "io-channel-mux";
io-channels = <&adc 0>;
io-channel-names = "parent";
mux-controls = <&mux>;
channels = "sync-1", "in", "out", "sync-2";
};
i2c-mux {
compatible = "i2c-mux";
i2c-parent = <&i2c1>;
mux-controls = <&mux>;
#address-cells = <1>;
#size-cells = <0>;
i2c@0 {
reg = <0>;
#address-cells = <1>;
#size-cells = <0>;
ssd1307: oled@3c {
/* ... */
};
};
i2c@3 {
reg = <3>;
#address-cells = <1>;
#size-cells = <0>;
pca9555: pca9555@20 {
/* ... */
};
};
};
Mux controller nodes
--------------------
Mux controller nodes must specify the number of cells used for the
specifier using the '#mux-control-cells' property.
Optionally, mux controller nodes can also specify the state the mux should
have when it is idle. The idle-state property is used for this. If the
idle-state is not present, the mux controller is typically left as is when
it is idle. For multiplexer chips that expose several mux controllers, the
idle-state property is an array with one idle state for each mux controller.
The special value (-1) may be used to indicate that the mux should be left
as is when it is idle. This is the default, but can still be useful for
mux controller chips with more than one mux controller, particularly when
there is a need to "step past" a mux controller and set some other idle
state for a mux controller with a higher index.
Some mux controllers have the ability to disconnect the input/output of the
multiplexer. Using this disconnected high-impedance state as the idle state
is indicated with idle state (-2).
These constants are available in
#include <dt-bindings/mux/mux.h>
as MUX_IDLE_AS_IS (-1) and MUX_IDLE_DISCONNECT (-2).
An example mux controller node look like this (the adg972a chip is a triple
4-way multiplexer):
mux: mux-controller@50 {
compatible = "adi,adg792a";
reg = <0x50>;
#mux-control-cells = <1>;
idle-state = <MUX_IDLE_DISCONNECT MUX_IDLE_AS_IS 2>;
};
...@@ -4,6 +4,7 @@ Required properties: ...@@ -4,6 +4,7 @@ Required properties:
- compatible: Should be one of the following. - compatible: Should be one of the following.
- "rockchip,rk3066a-efuse" - for RK3066a SoCs. - "rockchip,rk3066a-efuse" - for RK3066a SoCs.
- "rockchip,rk3188-efuse" - for RK3188 SoCs. - "rockchip,rk3188-efuse" - for RK3188 SoCs.
- "rockchip,rk322x-efuse" - for RK322x SoCs.
- "rockchip,rk3288-efuse" - for RK3288 SoCs. - "rockchip,rk3288-efuse" - for RK3288 SoCs.
- "rockchip,rk3399-efuse" - for RK3399 SoCs. - "rockchip,rk3399-efuse" - for RK3399 SoCs.
- reg: Should contain the registers location and exact eFuse size - reg: Should contain the registers location and exact eFuse size
......
...@@ -337,7 +337,12 @@ MEM ...@@ -337,7 +337,12 @@ MEM
devm_kzalloc() devm_kzalloc()
MFD MFD
devm_mfd_add_devices() devm_mfd_add_devices()
MUX
devm_mux_chip_alloc()
devm_mux_chip_register()
devm_mux_control_get()
PER-CPU MEM PER-CPU MEM
devm_alloc_percpu() devm_alloc_percpu()
......
Coresight CPU Debug Module
==========================
Author: Leo Yan <leo.yan@linaro.org>
Date: April 5th, 2017
Introduction
------------
Coresight CPU debug module is defined in ARMv8-a architecture reference manual
(ARM DDI 0487A.k) Chapter 'Part H: External debug', the CPU can integrate
debug module and it is mainly used for two modes: self-hosted debug and
external debug. Usually the external debug mode is well known as the external
debugger connects with SoC from JTAG port; on the other hand the program can
explore debugging method which rely on self-hosted debug mode, this document
is to focus on this part.
The debug module provides sample-based profiling extension, which can be used
to sample CPU program counter, secure state and exception level, etc; usually
every CPU has one dedicated debug module to be connected. Based on self-hosted
debug mechanism, Linux kernel can access these related registers from mmio
region when the kernel panic happens. The callback notifier for kernel panic
will dump related registers for every CPU; finally this is good for assistant
analysis for panic.
Implementation
--------------
- During driver registration, it uses EDDEVID and EDDEVID1 - two device ID
registers to decide if sample-based profiling is implemented or not. On some
platforms this hardware feature is fully or partially implemented; and if
this feature is not supported then registration will fail.
- At the time this documentation was written, the debug driver mainly relies on
information gathered by the kernel panic callback notifier from three
sampling registers: EDPCSR, EDVIDSR and EDCIDSR: from EDPCSR we can get
program counter; EDVIDSR has information for secure state, exception level,
bit width, etc; EDCIDSR is context ID value which contains the sampled value
of CONTEXTIDR_EL1.
- The driver supports a CPU running in either AArch64 or AArch32 mode. The
registers naming convention is a bit different between them, AArch64 uses
'ED' for register prefix (ARM DDI 0487A.k, chapter H9.1) and AArch32 uses
'DBG' as prefix (ARM DDI 0487A.k, chapter G5.1). The driver is unified to
use AArch64 naming convention.
- ARMv8-a (ARM DDI 0487A.k) and ARMv7-a (ARM DDI 0406C.b) have different
register bits definition. So the driver consolidates two difference:
If PCSROffset=0b0000, on ARMv8-a the feature of EDPCSR is not implemented;
but ARMv7-a defines "PCSR samples are offset by a value that depends on the
instruction set state". For ARMv7-a, the driver checks furthermore if CPU
runs with ARM or thumb instruction set and calibrate PCSR value, the
detailed description for offset is in ARMv7-a ARM (ARM DDI 0406C.b) chapter
C11.11.34 "DBGPCSR, Program Counter Sampling Register".
If PCSROffset=0b0010, ARMv8-a defines "EDPCSR implemented, and samples have
no offset applied and do not sample the instruction set state in AArch32
state". So on ARMv8 if EDDEVID1.PCSROffset is 0b0010 and the CPU operates
in AArch32 state, EDPCSR is not sampled; when the CPU operates in AArch64
state EDPCSR is sampled and no offset are applied.
Clock and power domain
----------------------
Before accessing debug registers, we should ensure the clock and power domain
have been enabled properly. In ARMv8-a ARM (ARM DDI 0487A.k) chapter 'H9.1
Debug registers', the debug registers are spread into two domains: the debug
domain and the CPU domain.
+---------------+
| |
| |
+----------+--+ |
dbg_clock -->| |**| |<-- cpu_clock
| Debug |**| CPU |
dbg_power_domain -->| |**| |<-- cpu_power_domain
+----------+--+ |
| |
| |
+---------------+
For debug domain, the user uses DT binding "clocks" and "power-domains" to
specify the corresponding clock source and power supply for the debug logic.
The driver calls the pm_runtime_{put|get} operations as needed to handle the
debug power domain.
For CPU domain, the different SoC designs have different power management
schemes and finally this heavily impacts external debug module. So we can
divide into below cases:
- On systems with a sane power controller which can behave correctly with
respect to CPU power domain, the CPU power domain can be controlled by
register EDPRCR in driver. The driver firstly writes bit EDPRCR.COREPURQ
to power up the CPU, and then writes bit EDPRCR.CORENPDRQ for emulation
of CPU power down. As result, this can ensure the CPU power domain is
powered on properly during the period when access debug related registers;
- Some designs will power down an entire cluster if all CPUs on the cluster
are powered down - including the parts of the debug registers that should
remain powered in the debug power domain. The bits in EDPRCR are not
respected in these cases, so these designs do not support debug over
power down in the way that the CoreSight / Debug designers anticipated.
This means that even checking EDPRSR has the potential to cause a bus hang
if the target register is unpowered.
In this case, accessing to the debug registers while they are not powered
is a recipe for disaster; so we need preventing CPU low power states at boot
time or when user enable module at the run time. Please see chapter
"How to use the module" for detailed usage info for this.
Device Tree Bindings
--------------------
See Documentation/devicetree/bindings/arm/coresight-cpu-debug.txt for details.
How to use the module
---------------------
If you want to enable debugging functionality at boot time, you can add
"coresight_cpu_debug.enable=1" to the kernel command line parameter.
The driver also can work as module, so can enable the debugging when insmod
module:
# insmod coresight_cpu_debug.ko debug=1
When boot time or insmod module you have not enabled the debugging, the driver
uses the debugfs file system to provide a knob to dynamically enable or disable
debugging:
To enable it, write a '1' into /sys/kernel/debug/coresight_cpu_debug/enable:
# echo 1 > /sys/kernel/debug/coresight_cpu_debug/enable
To disable it, write a '0' into /sys/kernel/debug/coresight_cpu_debug/enable:
# echo 0 > /sys/kernel/debug/coresight_cpu_debug/enable
As explained in chapter "Clock and power domain", if you are working on one
platform which has idle states to power off debug logic and the power
controller cannot work well for the request from EDPRCR, then you should
firstly constraint CPU idle states before enable CPU debugging feature; so can
ensure the accessing to debug logic.
If you want to limit idle states at boot time, you can use "nohlt" or
"cpuidle.off=1" in the kernel command line.
At the runtime you can disable idle states with below methods:
Set latency request to /dev/cpu_dma_latency to disable all CPUs specific idle
states (if latency = 0uS then disable all idle states):
# echo "what_ever_latency_you_need_in_uS" > /dev/cpu_dma_latency
Disable specific CPU's specific idle state:
# echo 1 > /sys/devices/system/cpu/cpu$cpu/cpuidle/state$state/disable
Output format
-------------
Here is an example of the debugging output format:
ARM external debug module:
coresight-cpu-debug 850000.debug: CPU[0]:
coresight-cpu-debug 850000.debug: EDPRSR: 00000001 (Power:On DLK:Unlock)
coresight-cpu-debug 850000.debug: EDPCSR: [<ffff00000808e9bc>] handle_IPI+0x174/0x1d8
coresight-cpu-debug 850000.debug: EDCIDSR: 00000000
coresight-cpu-debug 850000.debug: EDVIDSR: 90000000 (State:Non-secure Mode:EL1/0 Width:64bits VMID:0)
coresight-cpu-debug 852000.debug: CPU[1]:
coresight-cpu-debug 852000.debug: EDPRSR: 00000001 (Power:On DLK:Unlock)
coresight-cpu-debug 852000.debug: EDPCSR: [<ffff0000087fab34>] debug_notifier_call+0x23c/0x358
coresight-cpu-debug 852000.debug: EDCIDSR: 00000000
coresight-cpu-debug 852000.debug: EDVIDSR: 90000000 (State:Non-secure Mode:EL1/0 Width:64bits VMID:0)
...@@ -1207,7 +1207,9 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) ...@@ -1207,7 +1207,9 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained S: Maintained
F: drivers/hwtracing/coresight/* F: drivers/hwtracing/coresight/*
F: Documentation/trace/coresight.txt F: Documentation/trace/coresight.txt
F: Documentation/trace/coresight-cpu-debug.txt
F: Documentation/devicetree/bindings/arm/coresight.txt F: Documentation/devicetree/bindings/arm/coresight.txt
F: Documentation/devicetree/bindings/arm/coresight-cpu-debug.txt
F: Documentation/ABI/testing/sysfs-bus-coresight-devices-* F: Documentation/ABI/testing/sysfs-bus-coresight-devices-*
F: tools/perf/arch/arm/util/pmu.c F: tools/perf/arch/arm/util/pmu.c
F: tools/perf/arch/arm/util/auxtrace.c F: tools/perf/arch/arm/util/auxtrace.c
...@@ -6489,6 +6491,13 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector ...@@ -6489,6 +6491,13 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-envelope-detector
F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt F: Documentation/devicetree/bindings/iio/adc/envelope-detector.txt
F: drivers/iio/adc/envelope-detector.c F: drivers/iio/adc/envelope-detector.c
IIO MULTIPLEXER
M: Peter Rosin <peda@axentia.se>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/multiplexer/iio-mux.txt
F: drivers/iio/multiplexer/iio-mux.c
IIO SUBSYSTEM AND DRIVERS IIO SUBSYSTEM AND DRIVERS
M: Jonathan Cameron <jic23@kernel.org> M: Jonathan Cameron <jic23@kernel.org>
R: Hartmut Knaack <knaack.h@gmx.de> R: Hartmut Knaack <knaack.h@gmx.de>
...@@ -8724,6 +8733,15 @@ S: Orphan ...@@ -8724,6 +8733,15 @@ S: Orphan
F: drivers/mmc/host/mmc_spi.c F: drivers/mmc/host/mmc_spi.c
F: include/linux/spi/mmc_spi.h F: include/linux/spi/mmc_spi.h
MULTIPLEXER SUBSYSTEM
M: Peter Rosin <peda@axentia.se>
S: Maintained
F: Documentation/ABI/testing/mux/sysfs-class-mux*
F: Documentation/devicetree/bindings/mux/
F: include/linux/dt-bindings/mux/
F: include/linux/mux/
F: drivers/mux/
MULTISOUND SOUND DRIVER MULTISOUND SOUND DRIVER
M: Andrew Veliath <andrewtv@usa.net> M: Andrew Veliath <andrewtv@usa.net>
S: Maintained S: Maintained
...@@ -11336,6 +11354,9 @@ F: Documentation/tee.txt ...@@ -11336,6 +11354,9 @@ F: Documentation/tee.txt
THUNDERBOLT DRIVER THUNDERBOLT DRIVER
M: Andreas Noever <andreas.noever@gmail.com> M: Andreas Noever <andreas.noever@gmail.com>
M: Michael Jamet <michael.jamet@intel.com>
M: Mika Westerberg <mika.westerberg@linux.intel.com>
M: Yehezkel Bernat <yehezkel.bernat@intel.com>
S: Maintained S: Maintained
F: drivers/thunderbolt/ F: drivers/thunderbolt/
...@@ -13789,6 +13810,7 @@ M: Evgeniy Polyakov <zbr@ioremap.net> ...@@ -13789,6 +13810,7 @@ M: Evgeniy Polyakov <zbr@ioremap.net>
S: Maintained S: Maintained
F: Documentation/w1/ F: Documentation/w1/
F: drivers/w1/ F: drivers/w1/
F: include/linux/w1.h
W83791D HARDWARE MONITORING DRIVER W83791D HARDWARE MONITORING DRIVER
M: Marc Hulsman <m.hulsman@tudelft.nl> M: Marc Hulsman <m.hulsman@tudelft.nl>
......
...@@ -887,5 +887,69 @@ dsi_in: endpoint { ...@@ -887,5 +887,69 @@ dsi_in: endpoint {
}; };
}; };
}; };
debug@f6590000 {
compatible = "arm,coresight-cpu-debug","arm,primecell";
reg = <0 0xf6590000 0 0x1000>;
clocks = <&sys_ctrl HI6220_DAPB_CLK>;
clock-names = "apb_pclk";
cpu = <&cpu0>;
};
debug@f6592000 {
compatible = "arm,coresight-cpu-debug","arm,primecell";
reg = <0 0xf6592000 0 0x1000>;
clocks = <&sys_ctrl HI6220_DAPB_CLK>;
clock-names = "apb_pclk";
cpu = <&cpu1>;
};
debug@f6594000 {
compatible = "arm,coresight-cpu-debug","arm,primecell";
reg = <0 0xf6594000 0 0x1000>;
clocks = <&sys_ctrl HI6220_DAPB_CLK>;
clock-names = "apb_pclk";
cpu = <&cpu2>;
};
debug@f6596000 {
compatible = "arm,coresight-cpu-debug","arm,primecell";
reg = <0 0xf6596000 0 0x1000>;
clocks = <&sys_ctrl HI6220_DAPB_CLK>;
clock-names = "apb_pclk";
cpu = <&cpu3>;
};
debug@f65d0000 {
compatible = "arm,coresight-cpu-debug","arm,primecell";
reg = <0 0xf65d0000 0 0x1000>;
clocks = <&sys_ctrl HI6220_DAPB_CLK>;
clock-names = "apb_pclk";
cpu = <&cpu4>;
};
debug@f65d2000 {
compatible = "arm,coresight-cpu-debug","arm,primecell";
reg = <0 0xf65d2000 0 0x1000>;
clocks = <&sys_ctrl HI6220_DAPB_CLK>;
clock-names = "apb_pclk";
cpu = <&cpu5>;
};
debug@f65d4000 {
compatible = "arm,coresight-cpu-debug","arm,primecell";
reg = <0 0xf65d4000 0 0x1000>;
clocks = <&sys_ctrl HI6220_DAPB_CLK>;
clock-names = "apb_pclk";
cpu = <&cpu6>;
};
debug@f65d6000 {
compatible = "arm,coresight-cpu-debug","arm,primecell";
reg = <0 0xf65d6000 0 0x1000>;
clocks = <&sys_ctrl HI6220_DAPB_CLK>;
clock-names = "apb_pclk";
cpu = <&cpu7>;
};
}; };
}; };
...@@ -1116,6 +1116,38 @@ funnel1_out: endpoint { ...@@ -1116,6 +1116,38 @@ funnel1_out: endpoint {
}; };
}; };
debug@850000 {
compatible = "arm,coresight-cpu-debug","arm,primecell";
reg = <0x850000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
cpu = <&CPU0>;
};
debug@852000 {
compatible = "arm,coresight-cpu-debug","arm,primecell";
reg = <0x852000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
cpu = <&CPU1>;
};
debug@854000 {
compatible = "arm,coresight-cpu-debug","arm,primecell";
reg = <0x854000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
cpu = <&CPU2>;
};
debug@856000 {
compatible = "arm,coresight-cpu-debug","arm,primecell";
reg = <0x856000 0x1000>;
clocks = <&rpmcc RPM_QDSS_CLK>;
clock-names = "apb_pclk";
cpu = <&CPU3>;
};
etm@85c000 { etm@85c000 {
compatible = "arm,coresight-etm4x", "arm,primecell"; compatible = "arm,coresight-etm4x", "arm,primecell";
reg = <0x85c000 0x1000>; reg = <0x85c000 0x1000>;
......
...@@ -136,7 +136,6 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type) ...@@ -136,7 +136,6 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)
} }
} }
#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(timer, tick) wrmsrl(timer, tick)
#define hv_init_timer_config(config, val) wrmsrl(config, val) #define hv_init_timer_config(config, val) wrmsrl(config, val)
......
...@@ -206,4 +206,6 @@ source "drivers/fsi/Kconfig" ...@@ -206,4 +206,6 @@ source "drivers/fsi/Kconfig"
source "drivers/tee/Kconfig" source "drivers/tee/Kconfig"
source "drivers/mux/Kconfig"
endmenu endmenu
...@@ -181,3 +181,4 @@ obj-$(CONFIG_NVMEM) += nvmem/ ...@@ -181,3 +181,4 @@ obj-$(CONFIG_NVMEM) += nvmem/
obj-$(CONFIG_FPGA) += fpga/ obj-$(CONFIG_FPGA) += fpga/
obj-$(CONFIG_FSI) += fsi/ obj-$(CONFIG_FSI) += fsi/
obj-$(CONFIG_TEE) += tee/ obj-$(CONFIG_TEE) += tee/
obj-$(CONFIG_MULTIPLEXER) += mux/
...@@ -1345,14 +1345,11 @@ static inline void input_state_falling(struct logical_input *input) ...@@ -1345,14 +1345,11 @@ static inline void input_state_falling(struct logical_input *input)
static void panel_process_inputs(void) static void panel_process_inputs(void)
{ {
struct list_head *item;
struct logical_input *input; struct logical_input *input;
keypressed = 0; keypressed = 0;
inputs_stable = 1; inputs_stable = 1;
list_for_each(item, &logical_inputs) { list_for_each_entry(input, &logical_inputs, list) {
input = list_entry(item, struct logical_input, list);
switch (input->state) { switch (input->state) {
case INPUT_ST_LOW: case INPUT_ST_LOW:
if ((phys_curr & input->mask) != input->value) if ((phys_curr & input->mask) != input->value)
......
...@@ -26,12 +26,52 @@ ...@@ -26,12 +26,52 @@
/* CBMEM firmware console log descriptor. */ /* CBMEM firmware console log descriptor. */
struct cbmem_cons { struct cbmem_cons {
u32 buffer_size; u32 size_dont_access_after_boot;
u32 buffer_cursor; u32 cursor;
u8 buffer_body[0]; u8 body[0];
} __packed; } __packed;
#define CURSOR_MASK ((1 << 28) - 1)
#define OVERFLOW (1 << 31)
static struct cbmem_cons __iomem *cbmem_console; static struct cbmem_cons __iomem *cbmem_console;
static u32 cbmem_console_size;
/*
* The cbmem_console structure is read again on every access because it may
* change at any time if runtime firmware logs new messages. This may rarely
* lead to race conditions where the firmware overwrites the beginning of the
* ring buffer with more lines after we have already read |cursor|. It should be
* rare and harmless enough that we don't spend extra effort working around it.
*/
static ssize_t memconsole_coreboot_read(char *buf, loff_t pos, size_t count)
{
u32 cursor = cbmem_console->cursor & CURSOR_MASK;
u32 flags = cbmem_console->cursor & ~CURSOR_MASK;
u32 size = cbmem_console_size;
struct seg { /* describes ring buffer segments in logical order */
u32 phys; /* physical offset from start of mem buffer */
u32 len; /* length of segment */
} seg[2] = { {0}, {0} };
size_t done = 0;
int i;
if (flags & OVERFLOW) {
if (cursor > size) /* Shouldn't really happen, but... */
cursor = 0;
seg[0] = (struct seg){.phys = cursor, .len = size - cursor};
seg[1] = (struct seg){.phys = 0, .len = cursor};
} else {
seg[0] = (struct seg){.phys = 0, .len = min(cursor, size)};
}
for (i = 0; i < ARRAY_SIZE(seg) && count > done; i++) {
done += memory_read_from_buffer(buf + done, count - done, &pos,
cbmem_console->body + seg[i].phys, seg[i].len);
pos -= seg[i].len;
}
return done;
}
static int memconsole_coreboot_init(phys_addr_t physaddr) static int memconsole_coreboot_init(phys_addr_t physaddr)
{ {
...@@ -42,17 +82,17 @@ static int memconsole_coreboot_init(phys_addr_t physaddr) ...@@ -42,17 +82,17 @@ static int memconsole_coreboot_init(phys_addr_t physaddr)
if (!tmp_cbmc) if (!tmp_cbmc)
return -ENOMEM; return -ENOMEM;
/* Read size only once to prevent overrun attack through /dev/mem. */
cbmem_console_size = tmp_cbmc->size_dont_access_after_boot;
cbmem_console = memremap(physaddr, cbmem_console = memremap(physaddr,
tmp_cbmc->buffer_size + sizeof(*cbmem_console), cbmem_console_size + sizeof(*cbmem_console),
MEMREMAP_WB); MEMREMAP_WB);
memunmap(tmp_cbmc); memunmap(tmp_cbmc);
if (!cbmem_console) if (!cbmem_console)
return -ENOMEM; return -ENOMEM;
memconsole_setup(cbmem_console->buffer_body, memconsole_setup(memconsole_coreboot_read);
min(cbmem_console->buffer_cursor, cbmem_console->buffer_size));
return 0; return 0;
} }
......
...@@ -48,6 +48,15 @@ struct biosmemcon_ebda { ...@@ -48,6 +48,15 @@ struct biosmemcon_ebda {
}; };
} __packed; } __packed;
static char *memconsole_baseaddr;
static size_t memconsole_length;
static ssize_t memconsole_read(char *buf, loff_t pos, size_t count)
{
return memory_read_from_buffer(buf, count, &pos, memconsole_baseaddr,
memconsole_length);
}
static void found_v1_header(struct biosmemcon_ebda *hdr) static void found_v1_header(struct biosmemcon_ebda *hdr)
{ {
pr_info("memconsole: BIOS console v1 EBDA structure found at %p\n", pr_info("memconsole: BIOS console v1 EBDA structure found at %p\n",
...@@ -56,7 +65,9 @@ static void found_v1_header(struct biosmemcon_ebda *hdr) ...@@ -56,7 +65,9 @@ static void found_v1_header(struct biosmemcon_ebda *hdr)
hdr->v1.buffer_addr, hdr->v1.start, hdr->v1.buffer_addr, hdr->v1.start,
hdr->v1.end, hdr->v1.num_chars); hdr->v1.end, hdr->v1.num_chars);
memconsole_setup(phys_to_virt(hdr->v1.buffer_addr), hdr->v1.num_chars); memconsole_baseaddr = phys_to_virt(hdr->v1.buffer_addr);
memconsole_length = hdr->v1.num_chars;
memconsole_setup(memconsole_read);
} }
static void found_v2_header(struct biosmemcon_ebda *hdr) static void found_v2_header(struct biosmemcon_ebda *hdr)
...@@ -67,8 +78,9 @@ static void found_v2_header(struct biosmemcon_ebda *hdr) ...@@ -67,8 +78,9 @@ static void found_v2_header(struct biosmemcon_ebda *hdr)
hdr->v2.buffer_addr, hdr->v2.start, hdr->v2.buffer_addr, hdr->v2.start,
hdr->v2.end, hdr->v2.num_bytes); hdr->v2.end, hdr->v2.num_bytes);
memconsole_setup(phys_to_virt(hdr->v2.buffer_addr + hdr->v2.start), memconsole_baseaddr = phys_to_virt(hdr->v2.buffer_addr + hdr->v2.start);
hdr->v2.end - hdr->v2.start); memconsole_length = hdr->v2.end - hdr->v2.start;
memconsole_setup(memconsole_read);
} }
/* /*
......
...@@ -22,15 +22,15 @@ ...@@ -22,15 +22,15 @@
#include "memconsole.h" #include "memconsole.h"
static char *memconsole_baseaddr; static ssize_t (*memconsole_read_func)(char *, loff_t, size_t);
static size_t memconsole_length;
static ssize_t memconsole_read(struct file *filp, struct kobject *kobp, static ssize_t memconsole_read(struct file *filp, struct kobject *kobp,
struct bin_attribute *bin_attr, char *buf, struct bin_attribute *bin_attr, char *buf,
loff_t pos, size_t count) loff_t pos, size_t count)
{ {
return memory_read_from_buffer(buf, count, &pos, memconsole_baseaddr, if (WARN_ON_ONCE(!memconsole_read_func))
memconsole_length); return -EIO;
return memconsole_read_func(buf, pos, count);
} }
static struct bin_attribute memconsole_bin_attr = { static struct bin_attribute memconsole_bin_attr = {
...@@ -38,16 +38,14 @@ static struct bin_attribute memconsole_bin_attr = { ...@@ -38,16 +38,14 @@ static struct bin_attribute memconsole_bin_attr = {
.read = memconsole_read, .read = memconsole_read,
}; };
void memconsole_setup(void *baseaddr, size_t length) void memconsole_setup(ssize_t (*read_func)(char *, loff_t, size_t))
{ {
memconsole_baseaddr = baseaddr; memconsole_read_func = read_func;
memconsole_length = length;
} }
EXPORT_SYMBOL(memconsole_setup); EXPORT_SYMBOL(memconsole_setup);
int memconsole_sysfs_init(void) int memconsole_sysfs_init(void)
{ {
memconsole_bin_attr.size = memconsole_length;
return sysfs_create_bin_file(firmware_kobj, &memconsole_bin_attr); return sysfs_create_bin_file(firmware_kobj, &memconsole_bin_attr);
} }
EXPORT_SYMBOL(memconsole_sysfs_init); EXPORT_SYMBOL(memconsole_sysfs_init);
......
...@@ -18,13 +18,14 @@ ...@@ -18,13 +18,14 @@
#ifndef __FIRMWARE_GOOGLE_MEMCONSOLE_H #ifndef __FIRMWARE_GOOGLE_MEMCONSOLE_H
#define __FIRMWARE_GOOGLE_MEMCONSOLE_H #define __FIRMWARE_GOOGLE_MEMCONSOLE_H
#include <linux/types.h>
/* /*
* memconsole_setup * memconsole_setup
* *
* Initialize the memory console from raw (virtual) base * Initialize the memory console, passing the function to handle read accesses.
* address and length.
*/ */
void memconsole_setup(void *baseaddr, size_t length); void memconsole_setup(ssize_t (*read_func)(char *, loff_t, size_t));
/* /*
* memconsole_sysfs_init * memconsole_sysfs_init
......
...@@ -118,14 +118,13 @@ static int vpd_section_attrib_add(const u8 *key, s32 key_len, ...@@ -118,14 +118,13 @@ static int vpd_section_attrib_add(const u8 *key, s32 key_len,
info = kzalloc(sizeof(*info), GFP_KERNEL); info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;
info->key = kzalloc(key_len + 1, GFP_KERNEL);
info->key = kstrndup(key, key_len, GFP_KERNEL);
if (!info->key) { if (!info->key) {
ret = -ENOMEM; ret = -ENOMEM;
goto free_info; goto free_info;
} }
memcpy(info->key, key, key_len);
sysfs_bin_attr_init(&info->bin_attr); sysfs_bin_attr_init(&info->bin_attr);
info->bin_attr.attr.name = info->key; info->bin_attr.attr.name = info->key;
info->bin_attr.attr.mode = 0444; info->bin_attr.attr.mode = 0444;
...@@ -191,8 +190,7 @@ static int vpd_section_create_attribs(struct vpd_section *sec) ...@@ -191,8 +190,7 @@ static int vpd_section_create_attribs(struct vpd_section *sec)
static int vpd_section_init(const char *name, struct vpd_section *sec, static int vpd_section_init(const char *name, struct vpd_section *sec,
phys_addr_t physaddr, size_t size) phys_addr_t physaddr, size_t size)
{ {
int ret; int err;
int raw_len;
sec->baseaddr = memremap(physaddr, size, MEMREMAP_WB); sec->baseaddr = memremap(physaddr, size, MEMREMAP_WB);
if (!sec->baseaddr) if (!sec->baseaddr)
...@@ -201,10 +199,11 @@ static int vpd_section_init(const char *name, struct vpd_section *sec, ...@@ -201,10 +199,11 @@ static int vpd_section_init(const char *name, struct vpd_section *sec,
sec->name = name; sec->name = name;
/* We want to export the raw partion with name ${name}_raw */ /* We want to export the raw partion with name ${name}_raw */
raw_len = strlen(name) + 5; sec->raw_name = kasprintf(GFP_KERNEL, "%s_raw", name);
sec->raw_name = kzalloc(raw_len, GFP_KERNEL); if (!sec->raw_name) {
strncpy(sec->raw_name, name, raw_len); err = -ENOMEM;
strncat(sec->raw_name, "_raw", raw_len); goto err_iounmap;
}
sysfs_bin_attr_init(&sec->bin_attr); sysfs_bin_attr_init(&sec->bin_attr);
sec->bin_attr.attr.name = sec->raw_name; sec->bin_attr.attr.name = sec->raw_name;
...@@ -213,14 +212,14 @@ static int vpd_section_init(const char *name, struct vpd_section *sec, ...@@ -213,14 +212,14 @@ static int vpd_section_init(const char *name, struct vpd_section *sec,
sec->bin_attr.read = vpd_section_read; sec->bin_attr.read = vpd_section_read;
sec->bin_attr.private = sec; sec->bin_attr.private = sec;
ret = sysfs_create_bin_file(vpd_kobj, &sec->bin_attr); err = sysfs_create_bin_file(vpd_kobj, &sec->bin_attr);
if (ret) if (err)
goto free_sec; goto err_free_raw_name;
sec->kobj = kobject_create_and_add(name, vpd_kobj); sec->kobj = kobject_create_and_add(name, vpd_kobj);
if (!sec->kobj) { if (!sec->kobj) {
ret = -EINVAL; err = -EINVAL;
goto sysfs_remove; goto err_sysfs_remove;
} }
INIT_LIST_HEAD(&sec->attribs); INIT_LIST_HEAD(&sec->attribs);
...@@ -230,14 +229,13 @@ static int vpd_section_init(const char *name, struct vpd_section *sec, ...@@ -230,14 +229,13 @@ static int vpd_section_init(const char *name, struct vpd_section *sec,
return 0; return 0;
sysfs_remove: err_sysfs_remove:
sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr); sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr);
err_free_raw_name:
free_sec:
kfree(sec->raw_name); kfree(sec->raw_name);
err_iounmap:
iounmap(sec->baseaddr); iounmap(sec->baseaddr);
return err;
return ret;
} }
static int vpd_section_destroy(struct vpd_section *sec) static int vpd_section_destroy(struct vpd_section *sec)
...@@ -319,9 +317,6 @@ static int __init vpd_platform_init(void) ...@@ -319,9 +317,6 @@ static int __init vpd_platform_init(void)
if (!vpd_kobj) if (!vpd_kobj)
return -ENOMEM; return -ENOMEM;
memset(&ro_vpd, 0, sizeof(ro_vpd));
memset(&rw_vpd, 0, sizeof(rw_vpd));
platform_driver_register(&vpd_driver); platform_driver_register(&vpd_driver);
return 0; return 0;
......
...@@ -6,7 +6,33 @@ menu "FSI support" ...@@ -6,7 +6,33 @@ menu "FSI support"
config FSI config FSI
tristate "FSI support" tristate "FSI support"
select CRC4
---help--- ---help---
FSI - the FRU Support Interface - is a simple bus for low-level FSI - the FRU Support Interface - is a simple bus for low-level
access to POWER-based hardware. access to POWER-based hardware.
if FSI
config FSI_MASTER_GPIO
tristate "GPIO-based FSI master"
depends on GPIOLIB
select CRC4
---help---
This option enables a FSI master driver using GPIO lines.
config FSI_MASTER_HUB
tristate "FSI hub master"
---help---
This option enables a FSI hub master driver. Hub is a type of FSI
master that is connected to the upstream master via a slave. Hubs
allow chaining of FSI links to an arbitrary depth. This allows for
a high target device fanout.
config FSI_SCOM
tristate "SCOM FSI client device driver"
---help---
This option enables an FSI based SCOM device driver.
endif
endmenu endmenu
obj-$(CONFIG_FSI) += fsi-core.o obj-$(CONFIG_FSI) += fsi-core.o
obj-$(CONFIG_FSI_MASTER_HUB) += fsi-master-hub.o
obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
This diff is collapsed.
This diff is collapsed.
/*
* FSI hub master 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/delay.h>
#include <linux/fsi.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "fsi-master.h"
/* Control Registers */
#define FSI_MMODE 0x0 /* R/W: mode */
#define FSI_MDLYR 0x4 /* R/W: delay */
#define FSI_MCRSP 0x8 /* R/W: clock rate */
#define FSI_MENP0 0x10 /* R/W: enable */
#define FSI_MLEVP0 0x18 /* R: plug detect */
#define FSI_MSENP0 0x18 /* S: Set enable */
#define FSI_MCENP0 0x20 /* C: Clear enable */
#define FSI_MAEB 0x70 /* R: Error address */
#define FSI_MVER 0x74 /* R: master version/type */
#define FSI_MRESP0 0xd0 /* W: Port reset */
#define FSI_MESRB0 0x1d0 /* R: Master error status */
#define FSI_MRESB0 0x1d0 /* W: Reset bridge */
#define FSI_MECTRL 0x2e0 /* W: Error control */
/* MMODE: Mode control */
#define FSI_MMODE_EIP 0x80000000 /* Enable interrupt polling */
#define FSI_MMODE_ECRC 0x40000000 /* Enable error recovery */
#define FSI_MMODE_EPC 0x10000000 /* Enable parity checking */
#define FSI_MMODE_P8_TO_LSB 0x00000010 /* Timeout value LSB */
/* MSB=1, LSB=0 is 0.8 ms */
/* MSB=0, LSB=1 is 0.9 ms */
#define FSI_MMODE_CRS0SHFT 18 /* Clk rate selection 0 shift */
#define FSI_MMODE_CRS0MASK 0x3ff /* Clk rate selection 0 mask */
#define FSI_MMODE_CRS1SHFT 8 /* Clk rate selection 1 shift */
#define FSI_MMODE_CRS1MASK 0x3ff /* Clk rate selection 1 mask */
/* MRESB: Reset brindge */
#define FSI_MRESB_RST_GEN 0x80000000 /* General reset */
#define FSI_MRESB_RST_ERR 0x40000000 /* Error Reset */
/* MRESB: Reset port */
#define FSI_MRESP_RST_ALL_MASTER 0x20000000 /* Reset all FSI masters */
#define FSI_MRESP_RST_ALL_LINK 0x10000000 /* Reset all FSI port contr. */
#define FSI_MRESP_RST_MCR 0x08000000 /* Reset FSI master reg. */
#define FSI_MRESP_RST_PYE 0x04000000 /* Reset FSI parity error */
#define FSI_MRESP_RST_ALL 0xfc000000 /* Reset any error */
/* MECTRL: Error control */
#define FSI_MECTRL_EOAE 0x8000 /* Enable machine check when */
/* master 0 in error */
#define FSI_MECTRL_P8_AUTO_TERM 0x4000 /* Auto terminate */
#define FSI_ENGID_HUB_MASTER 0x1c
#define FSI_HUB_LINK_OFFSET 0x80000
#define FSI_HUB_LINK_SIZE 0x80000
#define FSI_HUB_MASTER_MAX_LINKS 8
#define FSI_LINK_ENABLE_SETUP_TIME 10 /* in mS */
/*
* FSI hub master support
*
* A hub master increases the number of potential target devices that the
* primary FSI master can access. For each link a primary master supports,
* each of those links can in turn be chained to a hub master with multiple
* links of its own.
*
* The hub is controlled by a set of control registers exposed as a regular fsi
* device (the hub->upstream device), and provides access to the downstream FSI
* bus as through an address range on the slave itself (->addr and ->size).
*
* [This differs from "cascaded" masters, which expose the entire downstream
* bus entirely through the fsi device address range, and so have a smaller
* accessible address space.]
*/
struct fsi_master_hub {
struct fsi_master master;
struct fsi_device *upstream;
uint32_t addr, size; /* slave-relative addr of */
/* master address space */
};
#define to_fsi_master_hub(m) container_of(m, struct fsi_master_hub, master)
static int hub_master_read(struct fsi_master *master, int link,
uint8_t id, uint32_t addr, void *val, size_t size)
{
struct fsi_master_hub *hub = to_fsi_master_hub(master);
if (id != 0)
return -EINVAL;
addr += hub->addr + (link * FSI_HUB_LINK_SIZE);
return fsi_slave_read(hub->upstream->slave, addr, val, size);
}
static int hub_master_write(struct fsi_master *master, int link,
uint8_t id, uint32_t addr, const void *val, size_t size)
{
struct fsi_master_hub *hub = to_fsi_master_hub(master);
if (id != 0)
return -EINVAL;
addr += hub->addr + (link * FSI_HUB_LINK_SIZE);
return fsi_slave_write(hub->upstream->slave, addr, val, size);
}
static int hub_master_break(struct fsi_master *master, int link)
{
uint32_t addr, cmd;
addr = 0x4;
cmd = cpu_to_be32(0xc0de0000);
return hub_master_write(master, link, 0, addr, &cmd, sizeof(cmd));
}
static int hub_master_link_enable(struct fsi_master *master, int link)
{
struct fsi_master_hub *hub = to_fsi_master_hub(master);
int idx, bit;
__be32 reg;
int rc;
idx = link / 32;
bit = link % 32;
reg = cpu_to_be32(0x80000000 >> bit);
rc = fsi_device_write(hub->upstream, FSI_MSENP0 + (4 * idx), &reg, 4);
mdelay(FSI_LINK_ENABLE_SETUP_TIME);
fsi_device_read(hub->upstream, FSI_MENP0 + (4 * idx), &reg, 4);
return rc;
}
static void hub_master_release(struct device *dev)
{
struct fsi_master_hub *hub = to_fsi_master_hub(dev_to_fsi_master(dev));
kfree(hub);
}
/* mmode encoders */
static inline u32 fsi_mmode_crs0(u32 x)
{
return (x & FSI_MMODE_CRS0MASK) << FSI_MMODE_CRS0SHFT;
}
static inline u32 fsi_mmode_crs1(u32 x)
{
return (x & FSI_MMODE_CRS1MASK) << FSI_MMODE_CRS1SHFT;
}
static int hub_master_init(struct fsi_master_hub *hub)
{
struct fsi_device *dev = hub->upstream;
__be32 reg;
int rc;
reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
| FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
rc = fsi_device_write(dev, FSI_MRESP0, &reg, sizeof(reg));
if (rc)
return rc;
/* Initialize the MFSI (hub master) engine */
reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
| FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
rc = fsi_device_write(dev, FSI_MRESP0, &reg, sizeof(reg));
if (rc)
return rc;
reg = cpu_to_be32(FSI_MECTRL_EOAE | FSI_MECTRL_P8_AUTO_TERM);
rc = fsi_device_write(dev, FSI_MECTRL, &reg, sizeof(reg));
if (rc)
return rc;
reg = cpu_to_be32(FSI_MMODE_EIP | FSI_MMODE_ECRC | FSI_MMODE_EPC
| fsi_mmode_crs0(1) | fsi_mmode_crs1(1)
| FSI_MMODE_P8_TO_LSB);
rc = fsi_device_write(dev, FSI_MMODE, &reg, sizeof(reg));
if (rc)
return rc;
reg = cpu_to_be32(0xffff0000);
rc = fsi_device_write(dev, FSI_MDLYR, &reg, sizeof(reg));
if (rc)
return rc;
reg = ~0;
rc = fsi_device_write(dev, FSI_MSENP0, &reg, sizeof(reg));
if (rc)
return rc;
/* Leave enabled long enough for master logic to set up */
mdelay(FSI_LINK_ENABLE_SETUP_TIME);
rc = fsi_device_write(dev, FSI_MCENP0, &reg, sizeof(reg));
if (rc)
return rc;
rc = fsi_device_read(dev, FSI_MAEB, &reg, sizeof(reg));
if (rc)
return rc;
reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK);
rc = fsi_device_write(dev, FSI_MRESP0, &reg, sizeof(reg));
if (rc)
return rc;
rc = fsi_device_read(dev, FSI_MLEVP0, &reg, sizeof(reg));
if (rc)
return rc;
/* Reset the master bridge */
reg = cpu_to_be32(FSI_MRESB_RST_GEN);
rc = fsi_device_write(dev, FSI_MRESB0, &reg, sizeof(reg));
if (rc)
return rc;
reg = cpu_to_be32(FSI_MRESB_RST_ERR);
return fsi_device_write(dev, FSI_MRESB0, &reg, sizeof(reg));
}
static int hub_master_probe(struct device *dev)
{
struct fsi_device *fsi_dev = to_fsi_dev(dev);
struct fsi_master_hub *hub;
uint32_t reg, links;
__be32 __reg;
int rc;
rc = fsi_device_read(fsi_dev, FSI_MVER, &__reg, sizeof(__reg));
if (rc)
return rc;
reg = be32_to_cpu(__reg);
links = (reg >> 8) & 0xff;
dev_info(dev, "hub version %08x (%d links)\n", reg, links);
rc = fsi_slave_claim_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
FSI_HUB_LINK_SIZE * links);
if (rc) {
dev_err(dev, "can't claim slave address range for links");
return rc;
}
hub = kzalloc(sizeof(*hub), GFP_KERNEL);
if (!hub) {
rc = -ENOMEM;
goto err_release;
}
hub->addr = FSI_HUB_LINK_OFFSET;
hub->size = FSI_HUB_LINK_SIZE * links;
hub->upstream = fsi_dev;
hub->master.dev.parent = dev;
hub->master.dev.release = hub_master_release;
hub->master.n_links = links;
hub->master.read = hub_master_read;
hub->master.write = hub_master_write;
hub->master.send_break = hub_master_break;
hub->master.link_enable = hub_master_link_enable;
dev_set_drvdata(dev, hub);
hub_master_init(hub);
rc = fsi_master_register(&hub->master);
if (!rc)
return 0;
kfree(hub);
err_release:
fsi_slave_release_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
FSI_HUB_LINK_SIZE * links);
return rc;
}
static int hub_master_remove(struct device *dev)
{
struct fsi_master_hub *hub = dev_get_drvdata(dev);
fsi_master_unregister(&hub->master);
fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size);
return 0;
}
static struct fsi_device_id hub_master_ids[] = {
{
.engine_type = FSI_ENGID_HUB_MASTER,
.version = FSI_VERSION_ANY,
},
{ 0 }
};
static struct fsi_driver hub_master_driver = {
.id_table = hub_master_ids,
.drv = {
.name = "fsi-master-hub",
.bus = &fsi_bus_type,
.probe = hub_master_probe,
.remove = hub_master_remove,
}
};
module_fsi_driver(hub_master_driver);
MODULE_LICENSE("GPL");
/*
* FSI master definitions. These comprise the core <--> master interface,
* to allow the core to interact with the (hardware-specific) masters.
*
* 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.
*/
#ifndef DRIVERS_FSI_MASTER_H
#define DRIVERS_FSI_MASTER_H
#include <linux/device.h>
#define FSI_MASTER_FLAG_SWCLOCK 0x1
struct fsi_master {
struct device dev;
int idx;
int n_links;
int flags;
int (*read)(struct fsi_master *, int link, uint8_t id,
uint32_t addr, void *val, size_t size);
int (*write)(struct fsi_master *, int link, uint8_t id,
uint32_t addr, const void *val, size_t size);
int (*term)(struct fsi_master *, int link, uint8_t id);
int (*send_break)(struct fsi_master *, int link);
int (*link_enable)(struct fsi_master *, int link);
};
#define dev_to_fsi_master(d) container_of(d, struct fsi_master, dev)
extern int fsi_master_register(struct fsi_master *master);
extern void fsi_master_unregister(struct fsi_master *master);
#endif /* DRIVERS_FSI_MASTER_H */
/*
* SCOM FSI Client device 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
* MERGCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/fsi.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/list.h>
#include <linux/idr.h>
#define FSI_ENGID_SCOM 0x5
#define SCOM_FSI2PIB_DELAY 50
/* SCOM engine register set */
#define SCOM_DATA0_REG 0x00
#define SCOM_DATA1_REG 0x04
#define SCOM_CMD_REG 0x08
#define SCOM_RESET_REG 0x1C
#define SCOM_RESET_CMD 0x80000000
#define SCOM_WRITE_CMD 0x80000000
struct scom_device {
struct list_head link;
struct fsi_device *fsi_dev;
struct miscdevice mdev;
char name[32];
int idx;
};
#define to_scom_dev(x) container_of((x), struct scom_device, mdev)
static struct list_head scom_devices;
static DEFINE_IDA(scom_ida);
static int put_scom(struct scom_device *scom_dev, uint64_t value,
uint32_t addr)
{
int rc;
uint32_t data;
data = cpu_to_be32(SCOM_RESET_CMD);
rc = fsi_device_write(scom_dev->fsi_dev, SCOM_RESET_REG, &data,
sizeof(uint32_t));
if (rc)
return rc;
data = cpu_to_be32((value >> 32) & 0xffffffff);
rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
sizeof(uint32_t));
if (rc)
return rc;
data = cpu_to_be32(value & 0xffffffff);
rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
sizeof(uint32_t));
if (rc)
return rc;
data = cpu_to_be32(SCOM_WRITE_CMD | addr);
return fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
sizeof(uint32_t));
}
static int get_scom(struct scom_device *scom_dev, uint64_t *value,
uint32_t addr)
{
uint32_t result, data;
int rc;
*value = 0ULL;
data = cpu_to_be32(addr);
rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
sizeof(uint32_t));
if (rc)
return rc;
rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA0_REG, &result,
sizeof(uint32_t));
if (rc)
return rc;
*value |= (uint64_t)cpu_to_be32(result) << 32;
rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &result,
sizeof(uint32_t));
if (rc)
return rc;
*value |= cpu_to_be32(result);
return 0;
}
static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
loff_t *offset)
{
int rc;
struct miscdevice *mdev =
(struct miscdevice *)filep->private_data;
struct scom_device *scom = to_scom_dev(mdev);
struct device *dev = &scom->fsi_dev->dev;
uint64_t val;
if (len != sizeof(uint64_t))
return -EINVAL;
rc = get_scom(scom, &val, *offset);
if (rc) {
dev_dbg(dev, "get_scom fail:%d\n", rc);
return rc;
}
rc = copy_to_user(buf, &val, len);
if (rc)
dev_dbg(dev, "copy to user failed:%d\n", rc);
return rc ? rc : len;
}
static ssize_t scom_write(struct file *filep, const char __user *buf,
size_t len, loff_t *offset)
{
int rc;
struct miscdevice *mdev = filep->private_data;
struct scom_device *scom = to_scom_dev(mdev);
struct device *dev = &scom->fsi_dev->dev;
uint64_t val;
if (len != sizeof(uint64_t))
return -EINVAL;
rc = copy_from_user(&val, buf, len);
if (rc) {
dev_dbg(dev, "copy from user failed:%d\n", rc);
return -EINVAL;
}
rc = put_scom(scom, val, *offset);
if (rc) {
dev_dbg(dev, "put_scom failed with:%d\n", rc);
return rc;
}
return len;
}
static loff_t scom_llseek(struct file *file, loff_t offset, int whence)
{
switch (whence) {
case SEEK_CUR:
break;
case SEEK_SET:
file->f_pos = offset;
break;
default:
return -EINVAL;
}
return offset;
}
static const struct file_operations scom_fops = {
.owner = THIS_MODULE,
.llseek = scom_llseek,
.read = scom_read,
.write = scom_write,
};
static int scom_probe(struct device *dev)
{
struct fsi_device *fsi_dev = to_fsi_dev(dev);
struct scom_device *scom;
scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
if (!scom)
return -ENOMEM;
scom->idx = ida_simple_get(&scom_ida, 1, INT_MAX, GFP_KERNEL);
snprintf(scom->name, sizeof(scom->name), "scom%d", scom->idx);
scom->fsi_dev = fsi_dev;
scom->mdev.minor = MISC_DYNAMIC_MINOR;
scom->mdev.fops = &scom_fops;
scom->mdev.name = scom->name;
scom->mdev.parent = dev;
list_add(&scom->link, &scom_devices);
return misc_register(&scom->mdev);
}
static int scom_remove(struct device *dev)
{
struct scom_device *scom, *scom_tmp;
struct fsi_device *fsi_dev = to_fsi_dev(dev);
list_for_each_entry_safe(scom, scom_tmp, &scom_devices, link) {
if (scom->fsi_dev == fsi_dev) {
list_del(&scom->link);
ida_simple_remove(&scom_ida, scom->idx);
misc_deregister(&scom->mdev);
}
}
return 0;
}
static struct fsi_device_id scom_ids[] = {
{
.engine_type = FSI_ENGID_SCOM,
.version = FSI_VERSION_ANY,
},
{ 0 }
};
static struct fsi_driver scom_drv = {
.id_table = scom_ids,
.drv = {
.name = "scom",
.bus = &fsi_bus_type,
.probe = scom_probe,
.remove = scom_remove,
}
};
static int scom_init(void)
{
INIT_LIST_HEAD(&scom_devices);
return fsi_driver_register(&scom_drv);
}
static void scom_exit(void)
{
struct list_head *pos;
struct scom_device *scom;
list_for_each(pos, &scom_devices) {
scom = list_entry(pos, struct scom_device, link);
misc_deregister(&scom->mdev);
devm_kfree(&scom->fsi_dev->dev, scom);
}
fsi_driver_unregister(&scom_drv);
}
module_init(scom_init);
module_exit(scom_exit);
MODULE_LICENSE("GPL");
...@@ -630,9 +630,13 @@ void vmbus_close(struct vmbus_channel *channel) ...@@ -630,9 +630,13 @@ void vmbus_close(struct vmbus_channel *channel)
*/ */
list_for_each_safe(cur, tmp, &channel->sc_list) { list_for_each_safe(cur, tmp, &channel->sc_list) {
cur_channel = list_entry(cur, struct vmbus_channel, sc_list); cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
if (cur_channel->state != CHANNEL_OPENED_STATE)
continue;
vmbus_close_internal(cur_channel); vmbus_close_internal(cur_channel);
if (cur_channel->rescind) {
mutex_lock(&vmbus_connection.channel_mutex);
hv_process_channel_removal(cur_channel,
cur_channel->offermsg.child_relid);
mutex_unlock(&vmbus_connection.channel_mutex);
}
} }
/* /*
* Now close the primary. * Now close the primary.
......
...@@ -428,7 +428,6 @@ void vmbus_free_channels(void) ...@@ -428,7 +428,6 @@ void vmbus_free_channels(void)
{ {
struct vmbus_channel *channel, *tmp; struct vmbus_channel *channel, *tmp;
mutex_lock(&vmbus_connection.channel_mutex);
list_for_each_entry_safe(channel, tmp, &vmbus_connection.chn_list, list_for_each_entry_safe(channel, tmp, &vmbus_connection.chn_list,
listentry) { listentry) {
/* hv_process_channel_removal() needs this */ /* hv_process_channel_removal() needs this */
...@@ -436,7 +435,6 @@ void vmbus_free_channels(void) ...@@ -436,7 +435,6 @@ void vmbus_free_channels(void)
vmbus_device_unregister(channel->device_obj); vmbus_device_unregister(channel->device_obj);
} }
mutex_unlock(&vmbus_connection.channel_mutex);
} }
/* /*
...@@ -483,8 +481,10 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) ...@@ -483,8 +481,10 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
list_add_tail(&newchannel->sc_list, &channel->sc_list); list_add_tail(&newchannel->sc_list, &channel->sc_list);
channel->num_sc++; channel->num_sc++;
spin_unlock_irqrestore(&channel->lock, flags); spin_unlock_irqrestore(&channel->lock, flags);
} else } else {
atomic_dec(&vmbus_connection.offer_in_progress);
goto err_free_chan; goto err_free_chan;
}
} }
dev_type = hv_get_dev_type(newchannel); dev_type = hv_get_dev_type(newchannel);
...@@ -511,6 +511,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) ...@@ -511,6 +511,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
if (!fnew) { if (!fnew) {
if (channel->sc_creation_callback != NULL) if (channel->sc_creation_callback != NULL)
channel->sc_creation_callback(newchannel); channel->sc_creation_callback(newchannel);
atomic_dec(&vmbus_connection.offer_in_progress);
return; return;
} }
...@@ -532,9 +533,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) ...@@ -532,9 +533,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
* binding which eventually invokes the device driver's AddDevice() * binding which eventually invokes the device driver's AddDevice()
* method. * method.
*/ */
mutex_lock(&vmbus_connection.channel_mutex);
ret = vmbus_device_register(newchannel->device_obj); ret = vmbus_device_register(newchannel->device_obj);
mutex_unlock(&vmbus_connection.channel_mutex);
if (ret != 0) { if (ret != 0) {
pr_err("unable to add child device object (relid %d)\n", pr_err("unable to add child device object (relid %d)\n",
...@@ -542,6 +541,8 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) ...@@ -542,6 +541,8 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
kfree(newchannel->device_obj); kfree(newchannel->device_obj);
goto err_deq_chan; goto err_deq_chan;
} }
atomic_dec(&vmbus_connection.offer_in_progress);
return; return;
err_deq_chan: err_deq_chan:
...@@ -797,6 +798,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) ...@@ -797,6 +798,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
newchannel = alloc_channel(); newchannel = alloc_channel();
if (!newchannel) { if (!newchannel) {
vmbus_release_relid(offer->child_relid); vmbus_release_relid(offer->child_relid);
atomic_dec(&vmbus_connection.offer_in_progress);
pr_err("Unable to allocate channel object\n"); pr_err("Unable to allocate channel object\n");
return; return;
} }
...@@ -843,16 +845,38 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) ...@@ -843,16 +845,38 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
rescind = (struct vmbus_channel_rescind_offer *)hdr; rescind = (struct vmbus_channel_rescind_offer *)hdr;
/*
* The offer msg and the corresponding rescind msg
* from the host are guranteed to be ordered -
* offer comes in first and then the rescind.
* Since we process these events in work elements,
* and with preemption, we may end up processing
* the events out of order. Given that we handle these
* work elements on the same CPU, this is possible only
* in the case of preemption. In any case wait here
* until the offer processing has moved beyond the
* point where the channel is discoverable.
*/
while (atomic_read(&vmbus_connection.offer_in_progress) != 0) {
/*
* We wait here until any channel offer is currently
* being processed.
*/
msleep(1);
}
mutex_lock(&vmbus_connection.channel_mutex); mutex_lock(&vmbus_connection.channel_mutex);
channel = relid2channel(rescind->child_relid); channel = relid2channel(rescind->child_relid);
mutex_unlock(&vmbus_connection.channel_mutex);
if (channel == NULL) { if (channel == NULL) {
/* /*
* This is very impossible, because in * We failed in processing the offer message;
* vmbus_process_offer(), we have already invoked * we would have cleaned up the relid in that
* vmbus_release_relid() on error. * failure path.
*/ */
goto out; return;
} }
spin_lock_irqsave(&channel->lock, flags); spin_lock_irqsave(&channel->lock, flags);
...@@ -864,7 +888,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) ...@@ -864,7 +888,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
if (channel->device_obj) { if (channel->device_obj) {
if (channel->chn_rescind_callback) { if (channel->chn_rescind_callback) {
channel->chn_rescind_callback(channel); channel->chn_rescind_callback(channel);
goto out; return;
} }
/* /*
* We will have to unregister this device from the * We will have to unregister this device from the
...@@ -875,13 +899,26 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) ...@@ -875,13 +899,26 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
vmbus_device_unregister(channel->device_obj); vmbus_device_unregister(channel->device_obj);
put_device(dev); put_device(dev);
} }
} else {
hv_process_channel_removal(channel,
channel->offermsg.child_relid);
} }
if (channel->primary_channel != NULL) {
out: /*
mutex_unlock(&vmbus_connection.channel_mutex); * Sub-channel is being rescinded. Following is the channel
* close sequence when initiated from the driveri (refer to
* vmbus_close() for details):
* 1. Close all sub-channels first
* 2. Then close the primary channel.
*/
if (channel->state == CHANNEL_OPEN_STATE) {
/*
* The channel is currently not open;
* it is safe for us to cleanup the channel.
*/
mutex_lock(&vmbus_connection.channel_mutex);
hv_process_channel_removal(channel,
channel->offermsg.child_relid);
mutex_unlock(&vmbus_connection.channel_mutex);
}
}
} }
void vmbus_hvsock_device_unregister(struct vmbus_channel *channel) void vmbus_hvsock_device_unregister(struct vmbus_channel *channel)
......
...@@ -93,10 +93,13 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, ...@@ -93,10 +93,13 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
* all the CPUs. This is needed for kexec to work correctly where * all the CPUs. This is needed for kexec to work correctly where
* the CPU attempting to connect may not be CPU 0. * the CPU attempting to connect may not be CPU 0.
*/ */
if (version >= VERSION_WIN8_1) if (version >= VERSION_WIN8_1) {
msg->target_vcpu = hv_context.vp_index[smp_processor_id()]; msg->target_vcpu = hv_context.vp_index[smp_processor_id()];
else vmbus_connection.connect_cpu = smp_processor_id();
} else {
msg->target_vcpu = 0; msg->target_vcpu = 0;
vmbus_connection.connect_cpu = 0;
}
/* /*
* Add to list before we send the request since we may * Add to list before we send the request since we may
...@@ -370,7 +373,7 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep) ...@@ -370,7 +373,7 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
break; break;
case HV_STATUS_INSUFFICIENT_MEMORY: case HV_STATUS_INSUFFICIENT_MEMORY:
case HV_STATUS_INSUFFICIENT_BUFFERS: case HV_STATUS_INSUFFICIENT_BUFFERS:
ret = -ENOMEM; ret = -ENOBUFS;
break; break;
case HV_STATUS_SUCCESS: case HV_STATUS_SUCCESS:
return ret; return ret;
...@@ -387,7 +390,7 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep) ...@@ -387,7 +390,7 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
else else
mdelay(usec / 1000); mdelay(usec / 1000);
if (usec < 256000) if (retries < 22)
usec *= 2; usec *= 2;
} }
return ret; return ret;
......
...@@ -82,10 +82,15 @@ int hv_post_message(union hv_connection_id connection_id, ...@@ -82,10 +82,15 @@ int hv_post_message(union hv_connection_id connection_id,
aligned_msg->message_type = message_type; aligned_msg->message_type = message_type;
aligned_msg->payload_size = payload_size; aligned_msg->payload_size = payload_size;
memcpy((void *)aligned_msg->payload, payload, 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); status = hv_do_hypercall(HVCALL_POST_MESSAGE, aligned_msg, NULL);
/* Preemption must remain disabled until after the hypercall
* so some other thread can't get scheduled onto this cpu and
* corrupt the per-cpu post_msg_page
*/
put_cpu_ptr(hv_cpu);
return status & 0xFFFF; return status & 0xFFFF;
} }
...@@ -96,7 +101,7 @@ static int hv_ce_set_next_event(unsigned long delta, ...@@ -96,7 +101,7 @@ static int hv_ce_set_next_event(unsigned long delta,
WARN_ON(!clockevent_state_oneshot(evt)); WARN_ON(!clockevent_state_oneshot(evt));
hv_get_current_tick(current_tick); current_tick = hyperv_cs->read(NULL);
current_tick += delta; current_tick += delta;
hv_init_timer(HV_X64_MSR_STIMER0_COUNT, current_tick); hv_init_timer(HV_X64_MSR_STIMER0_COUNT, current_tick);
return 0; return 0;
......
...@@ -112,7 +112,7 @@ static void kvp_poll_wrapper(void *channel) ...@@ -112,7 +112,7 @@ static void kvp_poll_wrapper(void *channel)
{ {
/* Transaction is finished, reset the state here to avoid races. */ /* Transaction is finished, reset the state here to avoid races. */
kvp_transaction.state = HVUTIL_READY; kvp_transaction.state = HVUTIL_READY;
hv_kvp_onchannelcallback(channel); tasklet_schedule(&((struct vmbus_channel *)channel)->callback_event);
} }
static void kvp_register_done(void) static void kvp_register_done(void)
...@@ -159,7 +159,7 @@ static void kvp_timeout_func(struct work_struct *dummy) ...@@ -159,7 +159,7 @@ static void kvp_timeout_func(struct work_struct *dummy)
static void kvp_host_handshake_func(struct work_struct *dummy) static void kvp_host_handshake_func(struct work_struct *dummy)
{ {
hv_poll_channel(kvp_transaction.recv_channel, hv_kvp_onchannelcallback); tasklet_schedule(&kvp_transaction.recv_channel->callback_event);
} }
static int kvp_handle_handshake(struct hv_kvp_msg *msg) static int kvp_handle_handshake(struct hv_kvp_msg *msg)
...@@ -625,16 +625,17 @@ void hv_kvp_onchannelcallback(void *context) ...@@ -625,16 +625,17 @@ void hv_kvp_onchannelcallback(void *context)
NEGO_IN_PROGRESS, NEGO_IN_PROGRESS,
NEGO_FINISHED} host_negotiatied = NEGO_NOT_STARTED; NEGO_FINISHED} host_negotiatied = NEGO_NOT_STARTED;
if (host_negotiatied == NEGO_NOT_STARTED && if (kvp_transaction.state < HVUTIL_READY) {
kvp_transaction.state < HVUTIL_READY) {
/* /*
* If userspace daemon is not connected and host is asking * If userspace daemon is not connected and host is asking
* us to negotiate we need to delay to not lose messages. * us to negotiate we need to delay to not lose messages.
* This is important for Failover IP setting. * This is important for Failover IP setting.
*/ */
host_negotiatied = NEGO_IN_PROGRESS; if (host_negotiatied == NEGO_NOT_STARTED) {
schedule_delayed_work(&kvp_host_handshake_work, host_negotiatied = NEGO_IN_PROGRESS;
schedule_delayed_work(&kvp_host_handshake_work,
HV_UTIL_NEGO_TIMEOUT * HZ); HV_UTIL_NEGO_TIMEOUT * HZ);
}
return; return;
} }
if (kvp_transaction.state > HVUTIL_READY) if (kvp_transaction.state > HVUTIL_READY)
...@@ -702,6 +703,7 @@ void hv_kvp_onchannelcallback(void *context) ...@@ -702,6 +703,7 @@ void hv_kvp_onchannelcallback(void *context)
VM_PKT_DATA_INBAND, 0); VM_PKT_DATA_INBAND, 0);
host_negotiatied = NEGO_FINISHED; host_negotiatied = NEGO_FINISHED;
hv_poll_channel(kvp_transaction.recv_channel, kvp_poll_wrapper);
} }
} }
......
...@@ -202,27 +202,39 @@ static void shutdown_onchannelcallback(void *context) ...@@ -202,27 +202,39 @@ static void shutdown_onchannelcallback(void *context)
/* /*
* Set the host time in a process context. * Set the host time in a process context.
*/ */
static struct work_struct adj_time_work;
struct adj_time_work { /*
struct work_struct work; * The last time sample, received from the host. PTP device responds to
u64 host_time; * requests by using this data and the current partition-wide time reference
u64 ref_time; * count.
u8 flags; */
}; static struct {
u64 host_time;
u64 ref_time;
spinlock_t lock;
} host_ts;
static void hv_set_host_time(struct work_struct *work) static struct timespec64 hv_get_adj_host_time(void)
{ {
struct adj_time_work *wrk; struct timespec64 ts;
struct timespec64 host_ts; u64 newtime, reftime;
u64 reftime, newtime; unsigned long flags;
wrk = container_of(work, struct adj_time_work, work);
spin_lock_irqsave(&host_ts.lock, flags);
reftime = hyperv_cs->read(hyperv_cs); reftime = hyperv_cs->read(hyperv_cs);
newtime = wrk->host_time + (reftime - wrk->ref_time); newtime = host_ts.host_time + (reftime - host_ts.ref_time);
host_ts = ns_to_timespec64((newtime - WLTIMEDELTA) * 100); ts = ns_to_timespec64((newtime - WLTIMEDELTA) * 100);
spin_unlock_irqrestore(&host_ts.lock, flags);
do_settimeofday64(&host_ts); return ts;
}
static void hv_set_host_time(struct work_struct *work)
{
struct timespec64 ts = hv_get_adj_host_time();
do_settimeofday64(&ts);
} }
/* /*
...@@ -238,62 +250,35 @@ static void hv_set_host_time(struct work_struct *work) ...@@ -238,62 +250,35 @@ static void hv_set_host_time(struct work_struct *work)
* typically used as a hint to the guest. The guest is under no obligation * typically used as a hint to the guest. The guest is under no obligation
* to discipline the clock. * to discipline the clock.
*/ */
static struct adj_time_work wrk;
/*
* 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) static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 adj_flags)
{ {
unsigned long flags; unsigned long flags;
u64 cur_reftime; u64 cur_reftime;
/* /*
* This check is safe since we are executing in the * Save the adjusted time sample from the host and the snapshot
* interrupt context and time synch messages are always * of the current system time.
* delivered on the same CPU.
*/ */
if (adj_flags & ICTIMESYNCFLAG_SYNC) { spin_lock_irqsave(&host_ts.lock, flags);
/* Queue a job to do do_settimeofday64() */
if (work_pending(&wrk.work)) cur_reftime = hyperv_cs->read(hyperv_cs);
return; host_ts.host_time = hosttime;
host_ts.ref_time = cur_reftime;
wrk.host_time = hosttime;
wrk.ref_time = reftime; /*
wrk.flags = adj_flags; * TimeSync v4 messages contain reference time (guest's Hyper-V
schedule_work(&wrk.work); * clocksource read when the time sample was generated), we can
} else { * improve the precision by adding the delta between now and the
/* * time of generation. For older protocols we set
* Save the adjusted time sample from the host and the snapshot * reftime == cur_reftime on call.
* of the current system time for PTP device. */
*/ host_ts.host_time += (cur_reftime - reftime);
spin_lock_irqsave(&host_ts.lock, flags);
spin_unlock_irqrestore(&host_ts.lock, flags);
cur_reftime = hyperv_cs->read(hyperv_cs);
host_ts.host_time = hosttime; /* Schedule work to do do_settimeofday64() */
host_ts.ref_time = cur_reftime; if (adj_flags & ICTIMESYNCFLAG_SYNC)
ktime_get_snapshot(&host_ts.snap); schedule_work(&adj_time_work);
/*
* 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);
}
} }
/* /*
...@@ -341,8 +326,8 @@ static void timesync_onchannelcallback(void *context) ...@@ -341,8 +326,8 @@ static void timesync_onchannelcallback(void *context)
sizeof(struct vmbuspipe_hdr) + sizeof(struct vmbuspipe_hdr) +
sizeof(struct icmsg_hdr)]; sizeof(struct icmsg_hdr)];
adj_guesttime(timedatap->parenttime, adj_guesttime(timedatap->parenttime,
0, hyperv_cs->read(hyperv_cs),
timedatap->flags); timedatap->flags);
} }
} }
...@@ -526,58 +511,17 @@ static int hv_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) ...@@ -526,58 +511,17 @@ static int hv_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
static int hv_ptp_gettime(struct ptp_clock_info *info, struct timespec64 *ts) static int hv_ptp_gettime(struct ptp_clock_info *info, struct timespec64 *ts)
{ {
unsigned long flags; *ts = hv_get_adj_host_time();
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; 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 = { static struct ptp_clock_info ptp_hyperv_info = {
.name = "hyperv", .name = "hyperv",
.enable = hv_ptp_enable, .enable = hv_ptp_enable,
.adjtime = hv_ptp_adjtime, .adjtime = hv_ptp_adjtime,
.adjfreq = hv_ptp_adjfreq, .adjfreq = hv_ptp_adjfreq,
.gettime64 = hv_ptp_gettime, .gettime64 = hv_ptp_gettime,
.getcrosststamp = hv_ptp_getcrosststamp,
.settime64 = hv_ptp_settime, .settime64 = hv_ptp_settime,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
...@@ -592,7 +536,7 @@ static int hv_timesync_init(struct hv_util_service *srv) ...@@ -592,7 +536,7 @@ static int hv_timesync_init(struct hv_util_service *srv)
spin_lock_init(&host_ts.lock); spin_lock_init(&host_ts.lock);
INIT_WORK(&wrk.work, hv_set_host_time); INIT_WORK(&adj_time_work, hv_set_host_time);
/* /*
* ptp_clock_register() returns NULL when CONFIG_PTP_1588_CLOCK is * ptp_clock_register() returns NULL when CONFIG_PTP_1588_CLOCK is
...@@ -613,7 +557,7 @@ static void hv_timesync_deinit(void) ...@@ -613,7 +557,7 @@ static void hv_timesync_deinit(void)
{ {
if (hv_ptp_clock) if (hv_ptp_clock)
ptp_clock_unregister(hv_ptp_clock); ptp_clock_unregister(hv_ptp_clock);
cancel_work_sync(&wrk.work); cancel_work_sync(&adj_time_work);
} }
static int __init init_hyperv_utils(void) static int __init init_hyperv_utils(void)
......
...@@ -303,6 +303,13 @@ enum vmbus_connect_state { ...@@ -303,6 +303,13 @@ enum vmbus_connect_state {
#define MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT #define MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT
struct vmbus_connection { struct vmbus_connection {
/*
* CPU on which the initial host contact was made.
*/
int connect_cpu;
atomic_t offer_in_progress;
enum vmbus_connect_state conn_state; enum vmbus_connect_state conn_state;
atomic_t next_gpadl_handle; atomic_t next_gpadl_handle;
...@@ -411,6 +418,10 @@ static inline void hv_poll_channel(struct vmbus_channel *channel, ...@@ -411,6 +418,10 @@ static inline void hv_poll_channel(struct vmbus_channel *channel,
if (!channel) if (!channel)
return; return;
if (in_interrupt() && (channel->target_cpu == smp_processor_id())) {
cb(channel);
return;
}
smp_call_function_single(channel->target_cpu, cb, channel, true); smp_call_function_single(channel->target_cpu, cb, channel, true);
} }
......
...@@ -608,40 +608,6 @@ static void vmbus_free_dynids(struct hv_driver *drv) ...@@ -608,40 +608,6 @@ static void vmbus_free_dynids(struct hv_driver *drv)
spin_unlock(&drv->dynids.lock); spin_unlock(&drv->dynids.lock);
} }
/* Parse string of form: 1b4e28ba-2fa1-11d2-883f-b9a761bde3f */
static int get_uuid_le(const char *str, uuid_le *uu)
{
unsigned int b[16];
int i;
if (strlen(str) < 37)
return -1;
for (i = 0; i < 36; i++) {
switch (i) {
case 8: case 13: case 18: case 23:
if (str[i] != '-')
return -1;
break;
default:
if (!isxdigit(str[i]))
return -1;
}
}
/* unparse little endian output byte order */
if (sscanf(str,
"%2x%2x%2x%2x-%2x%2x-%2x%2x-%2x%2x-%2x%2x%2x%2x%2x%2x",
&b[3], &b[2], &b[1], &b[0],
&b[5], &b[4], &b[7], &b[6], &b[8], &b[9],
&b[10], &b[11], &b[12], &b[13], &b[14], &b[15]) != 16)
return -1;
for (i = 0; i < 16; i++)
uu->b[i] = b[i];
return 0;
}
/* /*
* store_new_id - sysfs frontend to vmbus_add_dynid() * store_new_id - sysfs frontend to vmbus_add_dynid()
* *
...@@ -651,11 +617,12 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf, ...@@ -651,11 +617,12 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf,
size_t count) size_t count)
{ {
struct hv_driver *drv = drv_to_hv_drv(driver); struct hv_driver *drv = drv_to_hv_drv(driver);
uuid_le guid = NULL_UUID_LE; uuid_le guid;
ssize_t retval; ssize_t retval;
if (get_uuid_le(buf, &guid) != 0) retval = uuid_le_to_bin(buf, &guid);
return -EINVAL; if (retval)
return retval;
if (hv_vmbus_get_id(drv, &guid)) if (hv_vmbus_get_id(drv, &guid))
return -EEXIST; return -EEXIST;
...@@ -677,12 +644,14 @@ static ssize_t remove_id_store(struct device_driver *driver, const char *buf, ...@@ -677,12 +644,14 @@ static ssize_t remove_id_store(struct device_driver *driver, const char *buf,
{ {
struct hv_driver *drv = drv_to_hv_drv(driver); struct hv_driver *drv = drv_to_hv_drv(driver);
struct vmbus_dynid *dynid, *n; struct vmbus_dynid *dynid, *n;
uuid_le guid = NULL_UUID_LE; uuid_le guid;
size_t retval = -ENODEV; ssize_t retval;
if (get_uuid_le(buf, &guid)) retval = uuid_le_to_bin(buf, &guid);
return -EINVAL; if (retval)
return retval;
retval = -ENODEV;
spin_lock(&drv->dynids.lock); spin_lock(&drv->dynids.lock);
list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
struct hv_vmbus_device_id *id = &dynid->id; struct hv_vmbus_device_id *id = &dynid->id;
...@@ -798,8 +767,10 @@ static void vmbus_device_release(struct device *device) ...@@ -798,8 +767,10 @@ static void vmbus_device_release(struct device *device)
struct hv_device *hv_dev = device_to_hv_device(device); struct hv_device *hv_dev = device_to_hv_device(device);
struct vmbus_channel *channel = hv_dev->channel; struct vmbus_channel *channel = hv_dev->channel;
mutex_lock(&vmbus_connection.channel_mutex);
hv_process_channel_removal(channel, hv_process_channel_removal(channel,
channel->offermsg.child_relid); channel->offermsg.child_relid);
mutex_unlock(&vmbus_connection.channel_mutex);
kfree(hv_dev); kfree(hv_dev);
} }
...@@ -877,7 +848,32 @@ void vmbus_on_msg_dpc(unsigned long data) ...@@ -877,7 +848,32 @@ void vmbus_on_msg_dpc(unsigned long data)
INIT_WORK(&ctx->work, vmbus_onmessage_work); INIT_WORK(&ctx->work, vmbus_onmessage_work);
memcpy(&ctx->msg, msg, sizeof(*msg)); memcpy(&ctx->msg, msg, sizeof(*msg));
queue_work(vmbus_connection.work_queue, &ctx->work); /*
* The host can generate a rescind message while we
* may still be handling the original offer. We deal with
* this condition by ensuring the processing is done on the
* same CPU.
*/
switch (hdr->msgtype) {
case CHANNELMSG_RESCIND_CHANNELOFFER:
/*
* If we are handling the rescind message;
* schedule the work on the global work queue.
*/
schedule_work_on(vmbus_connection.connect_cpu,
&ctx->work);
break;
case CHANNELMSG_OFFERCHANNEL:
atomic_inc(&vmbus_connection.offer_in_progress);
queue_work_on(vmbus_connection.connect_cpu,
vmbus_connection.work_queue,
&ctx->work);
break;
default:
queue_work(vmbus_connection.work_queue, &ctx->work);
}
} else } else
entry->message_handler(hdr); entry->message_handler(hdr);
......
...@@ -89,4 +89,18 @@ config CORESIGHT_STM ...@@ -89,4 +89,18 @@ config CORESIGHT_STM
logging useful software events or data coming from various entities logging useful software events or data coming from various entities
in the system, possibly running different OSs in the system, possibly running different OSs
config CORESIGHT_CPU_DEBUG
tristate "CoreSight CPU Debug driver"
depends on ARM || ARM64
depends on DEBUG_FS
help
This driver provides support for coresight debugging module. This
is primarily used to dump sample-based profiling registers when
system triggers panic, the driver will parse context registers so
can quickly get to know program counter (PC), secure state,
exception level, etc. Before use debugging functionality, platform
needs to ensure the clock domain and power domain are enabled
properly, please refer Documentation/trace/coresight-cpu-debug.txt
for detailed description and the example for usage.
endif endif
...@@ -16,3 +16,4 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \ ...@@ -16,3 +16,4 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \
coresight-etm4x-sysfs.o coresight-etm4x-sysfs.o
obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o
obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o
obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o
This diff is collapsed.
...@@ -375,7 +375,7 @@ static void etb_update_buffer(struct coresight_device *csdev, ...@@ -375,7 +375,7 @@ static void etb_update_buffer(struct coresight_device *csdev,
/* /*
* Entries should be aligned to the frame size. If they are not * Entries should be aligned to the frame size. If they are not
* go back to the last alignement point to give decoding tools a * go back to the last alignment point to give decoding tools a
* chance to fix things. * chance to fix things.
*/ */
if (write_ptr % ETB_FRAME_SIZE_WORDS) { if (write_ptr % ETB_FRAME_SIZE_WORDS) {
...@@ -675,11 +675,8 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -675,11 +675,8 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->buf = devm_kzalloc(dev, drvdata->buf = devm_kzalloc(dev,
drvdata->buffer_depth * 4, GFP_KERNEL); drvdata->buffer_depth * 4, GFP_KERNEL);
if (!drvdata->buf) { if (!drvdata->buf)
dev_err(dev, "Failed to allocate %u bytes for buffer data\n",
drvdata->buffer_depth * 4);
return -ENOMEM; return -ENOMEM;
}
desc.type = CORESIGHT_DEV_TYPE_SINK; desc.type = CORESIGHT_DEV_TYPE_SINK;
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
......
...@@ -201,6 +201,7 @@ static void *etm_setup_aux(int event_cpu, void **pages, ...@@ -201,6 +201,7 @@ static void *etm_setup_aux(int event_cpu, void **pages,
event_data = alloc_event_data(event_cpu); event_data = alloc_event_data(event_cpu);
if (!event_data) if (!event_data)
return NULL; return NULL;
INIT_WORK(&event_data->work, free_event_data);
/* /*
* In theory nothing prevent tracers in a trace session from being * In theory nothing prevent tracers in a trace session from being
...@@ -217,8 +218,6 @@ static void *etm_setup_aux(int event_cpu, void **pages, ...@@ -217,8 +218,6 @@ static void *etm_setup_aux(int event_cpu, void **pages,
if (!sink) if (!sink)
goto err; goto err;
INIT_WORK(&event_data->work, free_event_data);
mask = &event_data->mask; mask = &event_data->mask;
/* Setup the path for each CPU in a trace session */ /* Setup the path for each CPU in a trace session */
......
...@@ -166,9 +166,6 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev) ...@@ -166,9 +166,6 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
if (!used) if (!used)
kfree(buf); kfree(buf);
if (!ret)
dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n");
return ret; return ret;
} }
...@@ -204,15 +201,27 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev) ...@@ -204,15 +201,27 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev)
static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode)
{ {
int ret;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
switch (mode) { switch (mode) {
case CS_MODE_SYSFS: case CS_MODE_SYSFS:
return tmc_enable_etf_sink_sysfs(csdev); ret = tmc_enable_etf_sink_sysfs(csdev);
break;
case CS_MODE_PERF: case CS_MODE_PERF:
return tmc_enable_etf_sink_perf(csdev); ret = tmc_enable_etf_sink_perf(csdev);
break;
/* We shouldn't be here */
default:
ret = -EINVAL;
break;
} }
/* We shouldn't be here */ if (ret)
return -EINVAL; return ret;
dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n");
return 0;
} }
static void tmc_disable_etf_sink(struct coresight_device *csdev) static void tmc_disable_etf_sink(struct coresight_device *csdev)
...@@ -273,7 +282,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, ...@@ -273,7 +282,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
drvdata->mode = CS_MODE_DISABLED; drvdata->mode = CS_MODE_DISABLED;
spin_unlock_irqrestore(&drvdata->spinlock, flags); spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_info(drvdata->dev, "TMC disabled\n"); dev_info(drvdata->dev, "TMC-ETF disabled\n");
} }
static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, int cpu, static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, int cpu,
......
...@@ -362,6 +362,13 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) ...@@ -362,6 +362,13 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
desc.type = CORESIGHT_DEV_TYPE_SINK; desc.type = CORESIGHT_DEV_TYPE_SINK;
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
desc.ops = &tmc_etr_cs_ops; desc.ops = &tmc_etr_cs_ops;
/*
* ETR configuration uses a 40-bit AXI master in place of
* the embedded SRAM of ETB/ETF.
*/
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
if (ret)
goto out;
} else { } else {
desc.type = CORESIGHT_DEV_TYPE_LINKSINK; desc.type = CORESIGHT_DEV_TYPE_LINKSINK;
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO;
......
...@@ -253,14 +253,22 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode) ...@@ -253,14 +253,22 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode)
return 0; return 0;
} }
static void coresight_disable_source(struct coresight_device *csdev) /**
* coresight_disable_source - Drop the reference count by 1 and disable
* the device if there are no users left.
*
* @csdev - The coresight device to disable
*
* Returns true if the device has been disabled.
*/
static bool coresight_disable_source(struct coresight_device *csdev)
{ {
if (atomic_dec_return(csdev->refcnt) == 0) { if (atomic_dec_return(csdev->refcnt) == 0) {
if (source_ops(csdev)->disable) { if (source_ops(csdev)->disable)
source_ops(csdev)->disable(csdev, NULL); source_ops(csdev)->disable(csdev, NULL);
csdev->enable = false; csdev->enable = false;
}
} }
return !csdev->enable;
} }
void coresight_disable_path(struct list_head *path) void coresight_disable_path(struct list_head *path)
...@@ -550,6 +558,9 @@ int coresight_enable(struct coresight_device *csdev) ...@@ -550,6 +558,9 @@ int coresight_enable(struct coresight_device *csdev)
int cpu, ret = 0; int cpu, ret = 0;
struct coresight_device *sink; struct coresight_device *sink;
struct list_head *path; struct list_head *path;
enum coresight_dev_subtype_source subtype;
subtype = csdev->subtype.source_subtype;
mutex_lock(&coresight_mutex); mutex_lock(&coresight_mutex);
...@@ -557,8 +568,16 @@ int coresight_enable(struct coresight_device *csdev) ...@@ -557,8 +568,16 @@ int coresight_enable(struct coresight_device *csdev)
if (ret) if (ret)
goto out; goto out;
if (csdev->enable) if (csdev->enable) {
/*
* There could be multiple applications driving the software
* source. So keep the refcount for each such user when the
* source is already enabled.
*/
if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE)
atomic_inc(csdev->refcnt);
goto out; goto out;
}
/* /*
* Search for a valid sink for this session but don't reset the * Search for a valid sink for this session but don't reset the
...@@ -585,7 +604,7 @@ int coresight_enable(struct coresight_device *csdev) ...@@ -585,7 +604,7 @@ int coresight_enable(struct coresight_device *csdev)
if (ret) if (ret)
goto err_source; goto err_source;
switch (csdev->subtype.source_subtype) { switch (subtype) {
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
/* /*
* When working from sysFS it is important to keep track * When working from sysFS it is important to keep track
...@@ -629,7 +648,7 @@ void coresight_disable(struct coresight_device *csdev) ...@@ -629,7 +648,7 @@ void coresight_disable(struct coresight_device *csdev)
if (ret) if (ret)
goto out; goto out;
if (!csdev->enable) if (!csdev->enable || !coresight_disable_source(csdev))
goto out; goto out;
switch (csdev->subtype.source_subtype) { switch (csdev->subtype.source_subtype) {
...@@ -647,7 +666,6 @@ void coresight_disable(struct coresight_device *csdev) ...@@ -647,7 +666,6 @@ void coresight_disable(struct coresight_device *csdev)
break; break;
} }
coresight_disable_source(csdev);
coresight_disable_path(path); coresight_disable_path(path);
coresight_release_path(path); coresight_release_path(path);
......
...@@ -52,7 +52,7 @@ of_coresight_get_endpoint_device(struct device_node *endpoint) ...@@ -52,7 +52,7 @@ of_coresight_get_endpoint_device(struct device_node *endpoint)
endpoint, of_dev_node_match); endpoint, of_dev_node_match);
} }
static void of_coresight_get_ports(struct device_node *node, static void of_coresight_get_ports(const struct device_node *node,
int *nr_inport, int *nr_outport) int *nr_inport, int *nr_outport)
{ {
struct device_node *ep = NULL; struct device_node *ep = NULL;
...@@ -101,14 +101,40 @@ static int of_coresight_alloc_memory(struct device *dev, ...@@ -101,14 +101,40 @@ static int of_coresight_alloc_memory(struct device *dev,
return 0; return 0;
} }
struct coresight_platform_data *of_get_coresight_platform_data( int of_coresight_get_cpu(const struct device_node *node)
struct device *dev, struct device_node *node)
{ {
int i = 0, ret = 0, cpu; int cpu;
bool found;
struct device_node *dn, *np;
dn = of_parse_phandle(node, "cpu", 0);
/* Affinity defaults to CPU0 */
if (!dn)
return 0;
for_each_possible_cpu(cpu) {
np = of_cpu_device_node_get(cpu);
found = (dn == np);
of_node_put(np);
if (found)
break;
}
of_node_put(dn);
/* Affinity to CPU0 if no cpu nodes are found */
return found ? cpu : 0;
}
EXPORT_SYMBOL_GPL(of_coresight_get_cpu);
struct coresight_platform_data *
of_get_coresight_platform_data(struct device *dev,
const struct device_node *node)
{
int i = 0, ret = 0;
struct coresight_platform_data *pdata; struct coresight_platform_data *pdata;
struct of_endpoint endpoint, rendpoint; struct of_endpoint endpoint, rendpoint;
struct device *rdev; struct device *rdev;
struct device_node *dn;
struct device_node *ep = NULL; struct device_node *ep = NULL;
struct device_node *rparent = NULL; struct device_node *rparent = NULL;
struct device_node *rport = NULL; struct device_node *rport = NULL;
...@@ -175,16 +201,7 @@ struct coresight_platform_data *of_get_coresight_platform_data( ...@@ -175,16 +201,7 @@ struct coresight_platform_data *of_get_coresight_platform_data(
} while (ep); } while (ep);
} }
/* Affinity defaults to CPU0 */ pdata->cpu = of_coresight_get_cpu(node);
pdata->cpu = 0;
dn = of_parse_phandle(node, "cpu", 0);
for (cpu = 0; dn && cpu < nr_cpu_ids; cpu++) {
if (dn == of_get_cpu_node(cpu, NULL)) {
pdata->cpu = cpu;
break;
}
}
of_node_put(dn);
return pdata; return pdata;
} }
......
...@@ -30,6 +30,19 @@ config I2C_MUX_GPIO ...@@ -30,6 +30,19 @@ config I2C_MUX_GPIO
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called i2c-mux-gpio. will be called i2c-mux-gpio.
config I2C_MUX_GPMUX
tristate "General Purpose I2C multiplexer"
select MULTIPLEXER
depends on OF || COMPILE_TEST
help
If you say yes to this option, support will be included for a
general purpose I2C multiplexer. This driver provides access to
I2C busses connected through a MUX, which in turn is controlled
by a MUX-controller from the MUX subsystem.
This driver can also be built as a module. If so, the module
will be called i2c-mux-gpmux.
config I2C_MUX_LTC4306 config I2C_MUX_LTC4306
tristate "LTC LTC4306/5 I2C multiplexer" tristate "LTC LTC4306/5 I2C multiplexer"
select GPIOLIB select GPIOLIB
......
...@@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o ...@@ -6,6 +6,7 @@ obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o obj-$(CONFIG_I2C_DEMUX_PINCTRL) += i2c-demux-pinctrl.o
obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
obj-$(CONFIG_I2C_MUX_GPMUX) += i2c-mux-gpmux.o
obj-$(CONFIG_I2C_MUX_LTC4306) += i2c-mux-ltc4306.o obj-$(CONFIG_I2C_MUX_LTC4306) += i2c-mux-ltc4306.o
obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o
obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
......
/*
* General Purpose I2C multiplexer
*
* Copyright (C) 2017 Axentia Technologies AB
*
* Author: Peter Rosin <peda@axentia.se>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/module.h>
#include <linux/mux/consumer.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
struct mux {
struct mux_control *control;
bool do_not_deselect;
};
static int i2c_mux_select(struct i2c_mux_core *muxc, u32 chan)
{
struct mux *mux = i2c_mux_priv(muxc);
int ret;
ret = mux_control_select(mux->control, chan);
mux->do_not_deselect = ret < 0;
return ret;
}
static int i2c_mux_deselect(struct i2c_mux_core *muxc, u32 chan)
{
struct mux *mux = i2c_mux_priv(muxc);
if (mux->do_not_deselect)
return 0;
return mux_control_deselect(mux->control);
}
static struct i2c_adapter *mux_parent_adapter(struct device *dev)
{
struct device_node *np = dev->of_node;
struct device_node *parent_np;
struct i2c_adapter *parent;
parent_np = of_parse_phandle(np, "i2c-parent", 0);
if (!parent_np) {
dev_err(dev, "Cannot parse i2c-parent\n");
return ERR_PTR(-ENODEV);
}
parent = of_find_i2c_adapter_by_node(parent_np);
of_node_put(parent_np);
if (!parent)
return ERR_PTR(-EPROBE_DEFER);
return parent;
}
static const struct of_device_id i2c_mux_of_match[] = {
{ .compatible = "i2c-mux", },
{},
};
MODULE_DEVICE_TABLE(of, i2c_mux_of_match);
static int i2c_mux_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct device_node *child;
struct i2c_mux_core *muxc;
struct mux *mux;
struct i2c_adapter *parent;
int children;
int ret;
if (!np)
return -ENODEV;
mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
if (!mux)
return -ENOMEM;
mux->control = devm_mux_control_get(dev, NULL);
if (IS_ERR(mux->control)) {
if (PTR_ERR(mux->control) != -EPROBE_DEFER)
dev_err(dev, "failed to get control-mux\n");
return PTR_ERR(mux->control);
}
parent = mux_parent_adapter(dev);
if (IS_ERR(parent)) {
if (PTR_ERR(parent) != -EPROBE_DEFER)
dev_err(dev, "failed to get i2c-parent adapter\n");
return PTR_ERR(parent);
}
children = of_get_child_count(np);
muxc = i2c_mux_alloc(parent, dev, children, 0, 0,
i2c_mux_select, i2c_mux_deselect);
if (!muxc) {
ret = -ENOMEM;
goto err_parent;
}
muxc->priv = mux;
platform_set_drvdata(pdev, muxc);
muxc->mux_locked = of_property_read_bool(np, "mux-locked");
for_each_child_of_node(np, child) {
u32 chan;
ret = of_property_read_u32(child, "reg", &chan);
if (ret < 0) {
dev_err(dev, "no reg property for node '%s'\n",
child->name);
goto err_children;
}
if (chan >= mux_control_states(mux->control)) {
dev_err(dev, "invalid reg %u\n", chan);
ret = -EINVAL;
goto err_children;
}
ret = i2c_mux_add_adapter(muxc, 0, chan, 0);
if (ret)
goto err_children;
}
dev_info(dev, "%d-port mux on %s adapter\n", children, parent->name);
return 0;
err_children:
i2c_mux_del_adapters(muxc);
err_parent:
i2c_put_adapter(parent);
return ret;
}
static int i2c_mux_remove(struct platform_device *pdev)
{
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
i2c_mux_del_adapters(muxc);
i2c_put_adapter(muxc->parent);
return 0;
}
static struct platform_driver i2c_mux_driver = {
.probe = i2c_mux_probe,
.remove = i2c_mux_remove,
.driver = {
.name = "i2c-mux-gpmux",
.of_match_table = i2c_mux_of_match,
},
};
module_platform_driver(i2c_mux_driver);
MODULE_DESCRIPTION("General Purpose I2C multiplexer driver");
MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
MODULE_LICENSE("GPL v2");
...@@ -83,6 +83,7 @@ source "drivers/iio/humidity/Kconfig" ...@@ -83,6 +83,7 @@ source "drivers/iio/humidity/Kconfig"
source "drivers/iio/imu/Kconfig" source "drivers/iio/imu/Kconfig"
source "drivers/iio/light/Kconfig" source "drivers/iio/light/Kconfig"
source "drivers/iio/magnetometer/Kconfig" source "drivers/iio/magnetometer/Kconfig"
source "drivers/iio/multiplexer/Kconfig"
source "drivers/iio/orientation/Kconfig" source "drivers/iio/orientation/Kconfig"
if IIO_TRIGGER if IIO_TRIGGER
source "drivers/iio/trigger/Kconfig" source "drivers/iio/trigger/Kconfig"
......
...@@ -28,6 +28,7 @@ obj-y += humidity/ ...@@ -28,6 +28,7 @@ obj-y += humidity/
obj-y += imu/ obj-y += imu/
obj-y += light/ obj-y += light/
obj-y += magnetometer/ obj-y += magnetometer/
obj-y += multiplexer/
obj-y += orientation/ obj-y += orientation/
obj-y += potentiometer/ obj-y += potentiometer/
obj-y += potentiostat/ obj-y += potentiostat/
......
...@@ -867,3 +867,63 @@ int iio_write_channel_raw(struct iio_channel *chan, int val) ...@@ -867,3 +867,63 @@ int iio_write_channel_raw(struct iio_channel *chan, int val)
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(iio_write_channel_raw); EXPORT_SYMBOL_GPL(iio_write_channel_raw);
unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan)
{
const struct iio_chan_spec_ext_info *ext_info;
unsigned int i = 0;
if (!chan->channel->ext_info)
return i;
for (ext_info = chan->channel->ext_info; ext_info->name; ext_info++)
++i;
return i;
}
EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count);
static const struct iio_chan_spec_ext_info *iio_lookup_ext_info(
const struct iio_channel *chan,
const char *attr)
{
const struct iio_chan_spec_ext_info *ext_info;
if (!chan->channel->ext_info)
return NULL;
for (ext_info = chan->channel->ext_info; ext_info->name; ++ext_info) {
if (!strcmp(attr, ext_info->name))
return ext_info;
}
return NULL;
}
ssize_t iio_read_channel_ext_info(struct iio_channel *chan,
const char *attr, char *buf)
{
const struct iio_chan_spec_ext_info *ext_info;
ext_info = iio_lookup_ext_info(chan, attr);
if (!ext_info)
return -EINVAL;
return ext_info->read(chan->indio_dev, ext_info->private,
chan->channel, buf);
}
EXPORT_SYMBOL_GPL(iio_read_channel_ext_info);
ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr,
const char *buf, size_t len)
{
const struct iio_chan_spec_ext_info *ext_info;
ext_info = iio_lookup_ext_info(chan, attr);
if (!ext_info)
return -EINVAL;
return ext_info->write(chan->indio_dev, ext_info->private,
chan->channel, buf, len);
}
EXPORT_SYMBOL_GPL(iio_write_channel_ext_info);
#
# Multiplexer drivers
#
# When adding new entries keep the list in alphabetical order
menu "Multiplexers"
config IIO_MUX
tristate "IIO multiplexer driver"
select MULTIPLEXER
depends on OF || COMPILE_TEST
help
Say yes here to build support for the IIO multiplexer.
To compile this driver as a module, choose M here: the
module will be called iio-mux.
endmenu
#
# Makefile for industrial I/O multiplexer drivers
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_IIO_MUX) += iio-mux.o
This diff is collapsed.
...@@ -212,7 +212,7 @@ struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots, ...@@ -212,7 +212,7 @@ struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots,
int bus_nr; int bus_nr;
struct ipack_bus_device *bus; struct ipack_bus_device *bus;
bus = kzalloc(sizeof(struct ipack_bus_device), GFP_KERNEL); bus = kzalloc(sizeof(*bus), GFP_KERNEL);
if (!bus) if (!bus)
return NULL; return NULL;
...@@ -402,7 +402,6 @@ static int ipack_device_read_id(struct ipack_device *dev) ...@@ -402,7 +402,6 @@ static int ipack_device_read_id(struct ipack_device *dev)
* ID ROM contents */ * ID ROM contents */
dev->id = kmalloc(dev->id_avail, GFP_KERNEL); dev->id = kmalloc(dev->id_avail, GFP_KERNEL);
if (!dev->id) { if (!dev->id) {
dev_err(&dev->dev, "dev->id alloc failed.\n");
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
} }
......
...@@ -357,7 +357,10 @@ static int aemif_probe(struct platform_device *pdev) ...@@ -357,7 +357,10 @@ static int aemif_probe(struct platform_device *pdev)
return PTR_ERR(aemif->clk); return PTR_ERR(aemif->clk);
} }
clk_prepare_enable(aemif->clk); ret = clk_prepare_enable(aemif->clk);
if (ret)
return ret;
aemif->clk_rate = clk_get_rate(aemif->clk) / MSEC_PER_SEC; aemif->clk_rate = clk_get_rate(aemif->clk) / MSEC_PER_SEC;
if (of_device_is_compatible(np, "ti,da850-aemif")) if (of_device_is_compatible(np, "ti,da850-aemif"))
......
...@@ -490,6 +490,14 @@ config ASPEED_LPC_CTRL ...@@ -490,6 +490,14 @@ config ASPEED_LPC_CTRL
ioctl()s, the driver also provides a read/write interface to a BMC ram ioctl()s, the driver also provides a read/write interface to a BMC ram
region where the host LPC read/write region can be buffered. region where the host LPC read/write region can be buffered.
config ASPEED_LPC_SNOOP
tristate "Aspeed ast2500 HOST LPC snoop support"
depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON
help
Provides a driver to control the LPC snoop interface which
allows the BMC to listen on and save the data written by
the host to an arbitrary LPC I/O port.
config PCI_ENDPOINT_TEST config PCI_ENDPOINT_TEST
depends on PCI depends on PCI
select CRC32 select CRC32
......
...@@ -53,6 +53,7 @@ obj-$(CONFIG_ECHO) += echo/ ...@@ -53,6 +53,7 @@ obj-$(CONFIG_ECHO) += echo/
obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
obj-$(CONFIG_CXL_BASE) += cxl/ obj-$(CONFIG_CXL_BASE) += cxl/
obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment