1
0
Fork 0

Char/Misc driver patches for 5.10-rc1

Here is the big set of char, misc, and other assorted driver subsystem
 patches for 5.10-rc1.
 
 There's a lot of different things in here, all over the drivers/
 directory.  Some summaries:
 	- soundwire driver updates
 	- habanalabs driver updates
 	- extcon driver updates
 	- nitro_enclaves new driver
 	- fsl-mc driver and core updates
 	- mhi core and bus updates
 	- nvmem driver updates
 	- eeprom driver updates
 	- binder driver updates and fixes
 	- vbox minor bugfixes
 	- fsi driver updates
 	- w1 driver updates
 	- coresight driver updates
 	- interconnect driver updates
 	- misc driver updates
 	- other minor driver updates
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCX4g8YQ8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+yngKgCeNpArCP/9vQJRK9upnDm8ZLunSCUAn1wUT/2A
 /bTQ42c/WRQ+LU828GSM
 =6sO2
 -----END PGP SIGNATURE-----

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

Pull char/misc driver updates from Greg KH:
 "Here is the big set of char, misc, and other assorted driver subsystem
  patches for 5.10-rc1.

  There's a lot of different things in here, all over the drivers/
  directory. Some summaries:

   - soundwire driver updates

   - habanalabs driver updates

   - extcon driver updates

   - nitro_enclaves new driver

   - fsl-mc driver and core updates

   - mhi core and bus updates

   - nvmem driver updates

   - eeprom driver updates

   - binder driver updates and fixes

   - vbox minor bugfixes

   - fsi driver updates

   - w1 driver updates

   - coresight driver updates

   - interconnect driver updates

   - misc driver updates

   - other minor driver updates

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

* tag 'char-misc-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (396 commits)
  binder: fix UAF when releasing todo list
  docs: w1: w1_therm: Fix broken xref, mistakes, clarify text
  misc: Kconfig: fix a HISI_HIKEY_USB dependency
  LSM: Fix type of id parameter in kernel_post_load_data prototype
  misc: Kconfig: add a new dependency for HISI_HIKEY_USB
  firmware_loader: fix a kernel-doc markup
  w1: w1_therm: make w1_poll_completion static
  binder: simplify the return expression of binder_mmap
  test_firmware: Test partial read support
  firmware: Add request_partial_firmware_into_buf()
  firmware: Store opt_flags in fw_priv
  fs/kernel_file_read: Add "offset" arg for partial reads
  IMA: Add support for file reads without contents
  LSM: Add "contents" flag to kernel_read_file hook
  module: Call security_kernel_post_load_data()
  firmware_loader: Use security_post_load_data()
  LSM: Introduce kernel_post_load_data() hook
  fs/kernel_read_file: Add file_size output argument
  fs/kernel_read_file: Switch buffer size arg to size_t
  fs/kernel_read_file: Remove redundant size argument
  ...
pull/193/head
Linus Torvalds 2020-10-15 10:01:51 -07:00
commit 726eb70e0d
297 changed files with 24461 additions and 10014 deletions

View File

@ -0,0 +1,21 @@
What: /sys/bus/mhi/devices/.../serialnumber
Date: Sept 2020
KernelVersion: 5.10
Contact: Bhaumik Bhatt <bbhatt@codeaurora.org>
Description: The file holds the serial number of the client device obtained
using a BHI (Boot Host Interface) register read after at least
one attempt to power up the device has been done. If read
without having the device power on at least once, the file will
read all 0's.
Users: Any userspace application or clients interested in device info.
What: /sys/bus/mhi/devices/.../oem_pk_hash
Date: Sept 2020
KernelVersion: 5.10
Contact: Bhaumik Bhatt <bbhatt@codeaurora.org>
Description: The file holds the OEM PK Hash value of the endpoint device
obtained using a BHI (Boot Host Interface) register read after
at least one attempt to power up the device has been done. If
read without having the device power on at least once, the file
will read all 0's.
Users: Any userspace application or clients interested in device info.

View File

@ -0,0 +1,15 @@
What: /sys/bus/dfl/devices/dfl_dev.X/type
Date: Aug 2020
KernelVersion: 5.10
Contact: Xu Yilun <yilun.xu@intel.com>
Description: Read-only. It returns type of DFL FIU of the device. Now DFL
supports 2 FIU types, 0 for FME, 1 for PORT.
Format: 0x%x
What: /sys/bus/dfl/devices/dfl_dev.X/feature_id
Date: Aug 2020
KernelVersion: 5.10
Contact: Xu Yilun <yilun.xu@intel.com>
Description: Read-only. It returns feature identifier local to its DFL FIU
type.
Format: 0x%x

View File

@ -36,3 +36,11 @@ Contact: linux-fsi@lists.ozlabs.org
Description:
Provides a means of reading/writing a 32 bit value from/to a
specified FSI bus address.
What: /sys/bus/platform/devices/../cfam_reset
Date: Sept 2020
KernelVersion: 5.10
Contact: linux-fsi@lists.ozlabs.org
Description:
Provides a means of resetting the cfam that is attached to the
FSI device.

View File

@ -41,6 +41,13 @@ Contact: Tomas Winkler <tomas.winkler@intel.com>
Description: Stores mei client fixed address, if any
Format: %d
What: /sys/bus/mei/devices/.../vtag
Date: Nov 2020
KernelVersion: 5.9
Contact: Tomas Winkler <tomas.winkler@intel.com>
Description: Stores mei client vtag support status
Format: %d
What: /sys/bus/mei/devices/.../max_len
Date: Nov 2019
KernelVersion: 5.5

View File

@ -1,3 +1,21 @@
What: /sys/bus/soundwire/devices/sdw:.../status
/sys/bus/soundwire/devices/sdw:.../device_number
Date: September 2020
Contact: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Bard Liao <yung-chuan.liao@linux.intel.com>
Vinod Koul <vkoul@kernel.org>
Description: SoundWire Slave status
These properties report the Slave status, e.g. if it
is UNATTACHED or not, and in the latter case show the
device_number. This status information is useful to
detect devices exposed by platform firmware but not
physically present on the bus, and conversely devices
not exposed in platform firmware but enumerated.
What: /sys/bus/soundwire/devices/sdw:.../dev-properties/mipi_revision
/sys/bus/soundwire/devices/sdw:.../dev-properties/wake_capable
/sys/bus/soundwire/devices/sdw:.../dev-properties/test_mode_capable

View File

@ -2,13 +2,17 @@ What: /sys/class/habanalabs/hl<n>/armcp_kernel_ver
Date: Jan 2019
KernelVersion: 5.1
Contact: oded.gabbay@gmail.com
Description: Version of the Linux kernel running on the device's CPU
Description: Version of the Linux kernel running on the device's CPU.
Will be DEPRECATED in Linux kernel version 5.10, and be
replaced with cpucp_kernel_ver
What: /sys/class/habanalabs/hl<n>/armcp_ver
Date: Jan 2019
KernelVersion: 5.1
Contact: oded.gabbay@gmail.com
Description: Version of the application running on the device's CPU
Will be DEPRECATED in Linux kernel version 5.10, and be
replaced with cpucp_ver
What: /sys/class/habanalabs/hl<n>/clk_max_freq_mhz
Date: Jun 2019
@ -33,6 +37,18 @@ KernelVersion: 5.1
Contact: oded.gabbay@gmail.com
Description: Version of the Device's CPLD F/W
What: /sys/class/habanalabs/hl<n>/cpucp_kernel_ver
Date: Oct 2020
KernelVersion: 5.10
Contact: oded.gabbay@gmail.com
Description: Version of the Linux kernel running on the device's CPU
What: /sys/class/habanalabs/hl<n>/cpucp_ver
Date: Oct 2020
KernelVersion: 5.10
Contact: oded.gabbay@gmail.com
Description: Version of the application running on the device's CPU
What: /sys/class/habanalabs/hl<n>/device_type
Date: Jan 2019
KernelVersion: 5.1

View File

@ -49,10 +49,13 @@ Description:
will be changed only in device RAM, so it will be cleared when
power is lost. Trigger a 'save' to EEPROM command to keep
values after power-on. Read or write are :
* '9..12': device resolution in bit
* '9..14': device resolution in bit
or resolution to set in bit
* '-xx': xx is kernel error when reading the resolution
* Anything else: do nothing
Some DS18B20 clones are fixed in 12-bit resolution, so the
actual resolution is read back from the chip and verified. Error
is reported if the results differ.
Users: any user space application which wants to communicate with
w1_term device
@ -86,7 +89,7 @@ Description:
*write* :
* '0' : save the 2 or 3 bytes to the device EEPROM
(i.e. TH, TL and config register)
* '9..12' : set the device resolution in RAM
* '9..14' : set the device resolution in RAM
(if supported)
* Anything else: do nothing
refer to Documentation/w1/slaves/w1_therm.rst for detailed
@ -114,3 +117,47 @@ Description:
of the bulk read command (not the current temperature).
Users: any user space application which wants to communicate with
w1_term device
What: /sys/bus/w1/devices/.../conv_time
Date: July 2020
Contact: Ivan Zaentsev <ivan.zaentsev@wirenboard.ru>
Description:
(RW) Get, set, or measure a temperature conversion time. The
setting remains active until a resolution change. Then it is
reset to default (datasheet) conversion time for a new
resolution.
*read*: Actual conversion time in milliseconds. *write*:
'0': Set the default conversion time from the datasheet.
'1': Measure and set the conversion time. Make a single
temperature conversion, measure an actual value.
Increase it by 20% for temperature range. A new
conversion time can be obtained by reading this
same attribute.
other positive value:
Set the conversion time in milliseconds.
Users: An application using the w1_term device
What: /sys/bus/w1/devices/.../features
Date: July 2020
Contact: Ivan Zaentsev <ivan.zaentsev@wirenboard.ru>
Description:
(RW) Control optional driver settings.
Bit masks to read/write (bitwise OR):
1: Enable check for conversion success. If byte 6 of
scratchpad memory is 0xC after conversion, and
temperature reads 85.00 (powerup value) or 127.94
(insufficient power) - return a conversion error.
2: Enable poll for conversion completion. Generate read cycles
after the conversion start and wait for 1's. In parasite
power mode this feature is not available.
*read*: Currently selected features.
*write*: Select features.
Users: An application using the w1_term device

View File

@ -1,27 +0,0 @@
* PTN5150 CC (Configuration Channel) Logic device
PTN5150 is a small thin low power CC logic chip supporting the USB Type-C
connector application with CC control logic detection and indication functions.
It is interfaced to the host controller using an I2C interface.
Required properties:
- compatible: should be "nxp,ptn5150"
- reg: specifies the I2C slave address of the device
- int-gpio: should contain a phandle and GPIO specifier for the GPIO pin
connected to the PTN5150's INTB pin.
- vbus-gpio: should contain a phandle and GPIO specifier for the GPIO pin which
is used to control VBUS.
- pinctrl-names : a pinctrl state named "default" must be defined.
- pinctrl-0 : phandle referencing pin configuration of interrupt and vbus
control.
Example:
ptn5150@1d {
compatible = "nxp,ptn5150";
reg = <0x1d>;
int-gpio = <&msmgpio 78 GPIO_ACTIVE_HIGH>;
vbus-gpio = <&msmgpio 148 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&ptn5150_default>;
status = "okay";
};

View File

@ -0,0 +1,60 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/extcon/extcon-ptn5150.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: PTN5150 CC (Configuration Channel) Logic device
maintainers:
- Krzysztof Kozlowski <krzk@kernel.org>
description: |
PTN5150 is a small thin low power CC logic chip supporting the USB Type-C
connector application with CC control logic detection and indication
functions. It is interfaced to the host controller using an I2C interface.
properties:
compatible:
const: nxp,ptn5150
int-gpios:
deprecated: true
description:
GPIO pin (input) connected to the PTN5150's INTB pin.
Use "interrupts" instead.
interrupts:
maxItems: 1
reg:
maxItems: 1
vbus-gpios:
description:
GPIO pin (output) used to control VBUS. If skipped, no such control
takes place.
required:
- compatible
- interrupts
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
ptn5150@1d {
compatible = "nxp,ptn5150";
reg = <0x1d>;
interrupt-parent = <&msmgpio>;
interrupts = <78 IRQ_TYPE_LEVEL_HIGH>;
vbus-gpios = <&msmgpio 148 GPIO_ACTIVE_HIGH>;
};
};

View File

@ -12,6 +12,13 @@ Required properties:
- pinctrl-0: phandle to pinctrl node
- pinctrl-names: pinctrl state
Optional properties:
- cfam-reset-gpios: GPIO for CFAM reset
- fsi-routing-gpios: GPIO for setting the FSI mux (internal or cabled)
- fsi-mux-gpios: GPIO for detecting the desired FSI mux state
Examples:
fsi-master {
@ -21,4 +28,9 @@ Examples:
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fsi1_default>;
clocks = <&syscon ASPEED_CLK_GATE_FSICLK>;
fsi-routing-gpios = <&gpio0 ASPEED_GPIO(Q, 7) GPIO_ACTIVE_HIGH>;
fsi-mux-gpios = <&gpio0 ASPEED_GPIO(B, 0) GPIO_ACTIVE_HIGH>;
cfam-reset-gpios = <&gpio0 ASPEED_GPIO(Q, 0) GPIO_ACTIVE_LOW>;
};

View File

@ -19,7 +19,8 @@ directly.
Required properties:
- compatible : contains the interconnect provider compatible string
- #interconnect-cells : number of cells in a interconnect specifier needed to
encode the interconnect node id
encode the interconnect node id and optionally add a
path tag
Example:
@ -44,6 +45,10 @@ components it has to interact with.
Required properties:
interconnects : Pairs of phandles and interconnect provider specifier to denote
the edge source and destination ports of the interconnect path.
An optional path tag value could specified as additional argument
to both endpoints and in such cases, this information will be passed
to the interconnect framework to do aggregation based on the attached
tag.
Optional properties:
interconnect-names : List of interconnect path name strings sorted in the same
@ -62,3 +67,20 @@ Example:
interconnects = <&pnoc MASTER_SDCC_1 &bimc SLAVE_EBI_CH0>;
interconnect-names = "sdhc-mem";
};
Example with path tags:
gnoc: interconnect@17900000 {
...
interconnect-cells = <2>;
};
mnoc: interconnect@1380000 {
...
interconnect-cells = <2>;
};
cpu@0 {
...
interconnects = <&gnoc MASTER_APPSS_PROC 3 &mnoc SLAVE_EBI1 3>;
}

View File

@ -21,6 +21,23 @@ properties:
enum:
- qcom,bcm-voter
qcom,tcs-wait:
description: |
Optional mask of which TCSs (Triggered Command Sets) wait for completion
upon triggering. If not specified, then the AMC and WAKE sets wait for
completion. The mask bits are available in the QCOM_ICC_TAG_* defines.
The AMC TCS is triggered immediately when icc_set_bw() is called. The
WAKE/SLEEP TCSs are triggered when the RSC transitions between active and
sleep modes.
In most cases, it's necessary to wait in both the AMC and WAKE sets to
ensure resources are available before use. If a specific RSC and its use
cases can ensure sufficient delay by other means, then this can be
overridden to reduce latencies.
$ref: /schemas/types.yaml#/definitions/uint32
required:
- compatible
@ -39,7 +56,10 @@ examples:
# as defined in Documentation/devicetree/bindings/soc/qcom/rpmh-rsc.txt
- |
#include <dt-bindings/interconnect/qcom,icc.h>
disp_bcm_voter: bcm_voter {
compatible = "qcom,bcm-voter";
qcom,tcs-wait = <QCOM_ICC_TAG_AMC>;
};
...

View File

@ -19,6 +19,8 @@ properties:
enum:
- qcom,sc7180-osm-l3
- qcom,sdm845-osm-l3
- qcom,sm8150-osm-l3
- qcom,sm8250-epss-l3
reg:
maxItems: 1

View File

@ -1,16 +1,17 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interconnect/qcom,sdm845.yaml#
$id: http://devicetree.org/schemas/interconnect/qcom,rpmh.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SDM845 Network-On-Chip Interconnect
title: Qualcomm RPMh Network-On-Chip Interconnect
maintainers:
- Georgi Djakov <georgi.djakov@linaro.org>
- Odelu Kukatla <okukatla@codeaurora.org>
description: |
SDM845 interconnect providers support system bandwidth requirements through
RPMh interconnect providers support system bandwidth requirements through
RPMh hardware accelerators known as Bus Clock Manager (BCM). The provider is
able to communicate with the BCM through the Resource State Coordinator (RSC)
associated with each execution environment. Provider nodes must point to at
@ -23,6 +24,19 @@ properties:
compatible:
enum:
- qcom,sc7180-aggre1-noc
- qcom,sc7180-aggre2-noc
- qcom,sc7180-camnoc-virt
- qcom,sc7180-compute-noc
- qcom,sc7180-config-noc
- qcom,sc7180-dc-noc
- qcom,sc7180-gem-noc
- qcom,sc7180-ipa-virt
- qcom,sc7180-mc-virt
- qcom,sc7180-mmss-noc
- qcom,sc7180-npu-noc
- qcom,sc7180-qup-virt
- qcom,sc7180-system-noc
- qcom,sdm845-aggre1-noc
- qcom,sdm845-aggre2-noc
- qcom,sdm845-config-noc
@ -31,6 +45,28 @@ properties:
- qcom,sdm845-mem-noc
- qcom,sdm845-mmss-noc
- qcom,sdm845-system-noc
- qcom,sm8150-aggre1-noc
- qcom,sm8150-aggre2-noc
- qcom,sm8150-camnoc-noc
- qcom,sm8150-compute-noc
- qcom,sm8150-config-noc
- qcom,sm8150-dc-noc
- qcom,sm8150-gem-noc
- qcom,sm8150-ipa-virt
- qcom,sm8150-mc-virt
- qcom,sm8150-mmss-noc
- qcom,sm8150-system-noc
- qcom,sm8250-aggre1-noc
- qcom,sm8250-aggre2-noc
- qcom,sm8250-compute-noc
- qcom,sm8250-config-noc
- qcom,sm8250-dc-noc
- qcom,sm8250-gem-noc
- qcom,sm8250-ipa-virt
- qcom,sm8250-mc-virt
- qcom,sm8250-mmss-noc
- qcom,sm8250-npu-noc
- qcom,sm8250-system-noc
'#interconnect-cells':
const: 1

View File

@ -1,85 +0,0 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interconnect/qcom,sc7180.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm SC7180 Network-On-Chip Interconnect
maintainers:
- Odelu Kukatla <okukatla@codeaurora.org>
description: |
SC7180 interconnect providers support system bandwidth requirements through
RPMh hardware accelerators known as Bus Clock Manager (BCM). The provider is
able to communicate with the BCM through the Resource State Coordinator (RSC)
associated with each execution environment. Provider nodes must point to at
least one RPMh device child node pertaining to their RSC and each provider
can map to multiple RPMh resources.
properties:
reg:
maxItems: 1
compatible:
enum:
- qcom,sc7180-aggre1-noc
- qcom,sc7180-aggre2-noc
- qcom,sc7180-camnoc-virt
- qcom,sc7180-compute-noc
- qcom,sc7180-config-noc
- qcom,sc7180-dc-noc
- qcom,sc7180-gem-noc
- qcom,sc7180-ipa-virt
- qcom,sc7180-mc-virt
- qcom,sc7180-mmss-noc
- qcom,sc7180-npu-noc
- qcom,sc7180-qup-virt
- qcom,sc7180-system-noc
'#interconnect-cells':
const: 1
qcom,bcm-voters:
$ref: /schemas/types.yaml#/definitions/phandle-array
description: |
List of phandles to qcom,bcm-voter nodes that are required by
this interconnect to send RPMh commands.
qcom,bcm-voter-names:
$ref: /schemas/types.yaml#/definitions/string-array
description: |
Names for each of the qcom,bcm-voters specified.
required:
- compatible
- reg
- '#interconnect-cells'
- qcom,bcm-voters
additionalProperties: false
examples:
- |
#include <dt-bindings/interconnect/qcom,sc7180.h>
config_noc: interconnect@1500000 {
compatible = "qcom,sc7180-config-noc";
reg = <0x01500000 0x28000>;
#interconnect-cells = <1>;
qcom,bcm-voters = <&apps_bcm_voter>;
};
system_noc: interconnect@1620000 {
compatible = "qcom,sc7180-system-noc";
reg = <0x01620000 0x17080>;
#interconnect-cells = <1>;
qcom,bcm-voters = <&apps_bcm_voter>;
};
mmss_noc: interconnect@1740000 {
compatible = "qcom,sc7180-mmss-noc";
reg = <0x01740000 0x1c100>;
#interconnect-cells = <1>;
qcom,bcm-voters = <&apps_bcm_voter>;
};

View File

@ -11,6 +11,7 @@ board specific bus parameters.
Example:
"qcom,soundwire-v1.3.0"
"qcom,soundwire-v1.5.0"
"qcom,soundwire-v1.5.1"
"qcom,soundwire-v1.6.0"
- reg:
Usage: required

View File

@ -42,6 +42,11 @@ The session is terminated calling :c:func:`close(int fd)`.
A code snippet for an application communicating with Intel AMTHI client:
In order to support virtualization or sandboxing a trusted supervisor
can use :c:macro:`MEI_CONNECT_CLIENT_IOCTL_VTAG` to create
virtual channels with an Intel ME feature. Not all features support
virtual channels such client with answer EOPNOTSUPP.
.. code-block:: C
struct mei_connect_client_data data;
@ -110,6 +115,38 @@ Connect to firmware Feature/Client.
data that can be sent or received. (e.g. if MTU=2K, can send
requests up to bytes 2k and received responses up to 2k bytes).
IOCTL_MEI_CONNECT_CLIENT_VTAG:
------------------------------
.. code-block:: none
Usage:
struct mei_connect_client_data_vtag client_data_vtag;
ioctl(fd, IOCTL_MEI_CONNECT_CLIENT_VTAG, &client_data_vtag);
Inputs:
struct mei_connect_client_data_vtag - contain the following
Input field:
in_client_uuid - GUID of the FW Feature that needs
to connect to.
vtag - virtual tag [1, 255]
Outputs:
out_client_properties - Client Properties: MTU and Protocol Version.
Error returns:
ENOTTY No such client (i.e. wrong GUID) or connection is not allowed.
EINVAL Wrong IOCTL Number or tag == 0
ENODEV Device or Connection is not initialized or ready.
ENOMEM Unable to allocate memory to client internal data.
EFAULT Fatal Error (e.g. Unable to access user input data)
EBUSY Connection Already Open
EOPNOTSUPP Vtag is not supported
IOCTL_MEI_NOTIFY_SET
---------------------

View File

@ -328,8 +328,11 @@ Code Seq# Include File Comments
0xAC 00-1F linux/raw.h
0xAD 00 Netfilter device in development:
<mailto:rusty@rustcorp.com.au>
0xAE all linux/kvm.h Kernel-based Virtual Machine
0xAE 00-1F linux/kvm.h Kernel-based Virtual Machine
<mailto:kvm@vger.kernel.org>
0xAE 40-FF linux/kvm.h Kernel-based Virtual Machine
<mailto:kvm@vger.kernel.org>
0xAE 20-3F linux/nitro_enclaves.h Nitro Enclaves
0xAF 00-1F linux/fsl_hypervisor.h Freescale hypervisor
0xB0 all RATIO devices in development:
<mailto:vgo@ratio.de>

View File

@ -11,6 +11,7 @@ Linux Virtualization Support
uml/user_mode_linux_howto_v2
paravirt_ops
guest-halt-polling
ne_overview
.. only:: html and subproject

View File

@ -0,0 +1,95 @@
.. SPDX-License-Identifier: GPL-2.0
==============
Nitro Enclaves
==============
Overview
========
Nitro Enclaves (NE) is a new Amazon Elastic Compute Cloud (EC2) capability
that allows customers to carve out isolated compute environments within EC2
instances [1].
For example, an application that processes sensitive data and runs in a VM,
can be separated from other applications running in the same VM. This
application then runs in a separate VM than the primary VM, namely an enclave.
An enclave runs alongside the VM that spawned it. This setup matches low latency
applications needs. The resources that are allocated for the enclave, such as
memory and CPUs, are carved out of the primary VM. Each enclave is mapped to a
process running in the primary VM, that communicates with the NE driver via an
ioctl interface.
In this sense, there are two components:
1. An enclave abstraction process - a user space process running in the primary
VM guest that uses the provided ioctl interface of the NE driver to spawn an
enclave VM (that's 2 below).
There is a NE emulated PCI device exposed to the primary VM. The driver for this
new PCI device is included in the NE driver.
The ioctl logic is mapped to PCI device commands e.g. the NE_START_ENCLAVE ioctl
maps to an enclave start PCI command. The PCI device commands are then
translated into actions taken on the hypervisor side; that's the Nitro
hypervisor running on the host where the primary VM is running. The Nitro
hypervisor is based on core KVM technology.
2. The enclave itself - a VM running on the same host as the primary VM that
spawned it. Memory and CPUs are carved out of the primary VM and are dedicated
for the enclave VM. An enclave does not have persistent storage attached.
The memory regions carved out of the primary VM and given to an enclave need to
be aligned 2 MiB / 1 GiB physically contiguous memory regions (or multiple of
this size e.g. 8 MiB). The memory can be allocated e.g. by using hugetlbfs from
user space [2][3]. The memory size for an enclave needs to be at least 64 MiB.
The enclave memory and CPUs need to be from the same NUMA node.
An enclave runs on dedicated cores. CPU 0 and its CPU siblings need to remain
available for the primary VM. A CPU pool has to be set for NE purposes by an
user with admin capability. See the cpu list section from the kernel
documentation [4] for how a CPU pool format looks.
An enclave communicates with the primary VM via a local communication channel,
using virtio-vsock [5]. The primary VM has virtio-pci vsock emulated device,
while the enclave VM has a virtio-mmio vsock emulated device. The vsock device
uses eventfd for signaling. The enclave VM sees the usual interfaces - local
APIC and IOAPIC - to get interrupts from virtio-vsock device. The virtio-mmio
device is placed in memory below the typical 4 GiB.
The application that runs in the enclave needs to be packaged in an enclave
image together with the OS ( e.g. kernel, ramdisk, init ) that will run in the
enclave VM. The enclave VM has its own kernel and follows the standard Linux
boot protocol [6].
The kernel bzImage, the kernel command line, the ramdisk(s) are part of the
Enclave Image Format (EIF); plus an EIF header including metadata such as magic
number, eif version, image size and CRC.
Hash values are computed for the entire enclave image (EIF), the kernel and
ramdisk(s). That's used, for example, to check that the enclave image that is
loaded in the enclave VM is the one that was intended to be run.
These crypto measurements are included in a signed attestation document
generated by the Nitro Hypervisor and further used to prove the identity of the
enclave; KMS is an example of service that NE is integrated with and that checks
the attestation doc.
The enclave image (EIF) is loaded in the enclave memory at offset 8 MiB. The
init process in the enclave connects to the vsock CID of the primary VM and a
predefined port - 9000 - to send a heartbeat value - 0xb7. This mechanism is
used to check in the primary VM that the enclave has booted. The CID of the
primary VM is 3.
If the enclave VM crashes or gracefully exits, an interrupt event is received by
the NE driver. This event is sent further to the user space enclave process
running in the primary VM via a poll notification mechanism. Then the user space
enclave process can exit.
[1] https://aws.amazon.com/ec2/nitro/nitro-enclaves/
[2] https://www.kernel.org/doc/html/latest/admin-guide/mm/hugetlbpage.html
[3] https://lwn.net/Articles/807108/
[4] https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html
[5] https://man7.org/linux/man-pages/man7/vsock.7.html
[6] https://www.kernel.org/doc/html/latest/x86/boot.html

View File

@ -6,6 +6,7 @@ Supported chips:
* Maxim ds18*20 based temperature sensors.
* Maxim ds1825 based temperature sensors.
* GXCAS GC20MH01 temperature sensor.
Author: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
@ -13,8 +14,8 @@ Author: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Description
-----------
w1_therm provides basic temperature conversion for ds18*20 devices, and the
ds28ea00 device.
w1_therm provides basic temperature conversion for ds18*20, ds28ea00, GX20MH01
devices.
Supported family codes:
@ -26,59 +27,72 @@ W1_THERM_DS1825 0x3B
W1_THERM_DS28EA00 0x42
==================== ====
Support is provided through the sysfs w1_slave file. Each open and
read sequence will initiate a temperature conversion then provide two
Support is provided through the sysfs entry ``w1_slave``. Each open and
read sequence will initiate a temperature conversion, then provide two
lines of ASCII output. The first line contains the nine hex bytes
read along with a calculated crc value and YES or NO if it matched.
If the crc matched the returned values are retained. The second line
displays the retained values along with a temperature in millidegrees
Centigrade after t=.
Alternatively, temperature can be read using temperature sysfs, it
return only temperature in millidegrees Centigrade.
Alternatively, temperature can be read using ``temperature`` sysfs, it
returns only the temperature in millidegrees Centigrade.
A bulk read of all devices on the bus could be done writing 'trigger'
in the therm_bulk_read sysfs entry at w1_bus_master level. This will
sent the convert command on all devices on the bus, and if parasite
powered devices are detected on the bus (and strong pullup is enable
A bulk read of all devices on the bus could be done writing ``trigger``
to ``therm_bulk_read`` entry at w1_bus_master level. This will
send the convert command to all devices on the bus, and if parasite
powered devices are detected on the bus (and strong pullup is enabled
in the module), it will drive the line high during the longer conversion
time required by parasited powered device on the line. Reading
therm_bulk_read will return 0 if no bulk conversion pending,
``therm_bulk_read`` will return 0 if no bulk conversion pending,
-1 if at least one sensor still in conversion, 1 if conversion is complete
but at least one sensor value has not been read yet. Result temperature is
then accessed by reading the temperature sysfs entry of each device, which
then accessed by reading the ``temperature`` entry of each device, which
may return empty if conversion is still in progress. Note that if a bulk
read is sent but one sensor is not read immediately, the next access to
temperature on this device will return the temperature measured at the
``temperature`` on this device will return the temperature measured at the
time of issue of the bulk read command (not the current temperature).
Writing a value between 9 and 12 to the sysfs w1_slave file will change the
precision of the sensor for the next readings. This value is in (volatile)
SRAM, so it is reset when the sensor gets power-cycled.
A strong pullup will be applied during the conversion if required.
To store the current precision configuration into EEPROM, the value 0
has to be written to the sysfs w1_slave file. Since the EEPROM has a limited
amount of writes (>50k), this command should be used wisely.
``conv_time`` is used to get current conversion time (read), and
adjust it (write). A temperature conversion time depends on the device type and
it's current resolution. Default conversion time is set by the driver according
to the device datasheet. A conversion time for many original device clones
deviate from datasheet specs. There are three options: 1) manually set the
correct conversion time by writing a value in milliseconds to ``conv_time``; 2)
auto measure and set a conversion time by writing ``1`` to
``conv_time``; 3) use ``features`` to enable poll for conversion
completion. Options 2, 3 can't be used in parasite power mode. To get back to
the default conversion time write ``0`` to ``conv_time``.
Alternatively, resolution can be set or read (value from 9 to 12) using the
dedicated resolution sysfs entry on each device. This sysfs entry is not
present for devices not supporting this feature. Driver will adjust the
correct conversion time for each device regarding to its resolution setting.
In particular, strong pullup will be applied if required during the conversion
duration.
Writing a resolution value (in bits) to ``w1_slave`` will change the
precision of the sensor for the next readings. Allowed resolutions are defined by
the sensor. Resolution is reset when the sensor gets power-cycled.
The write-only sysfs entry eeprom is an alternative for EEPROM operations:
* 'save': will save device RAM to EEPROM
* 'restore': will restore EEPROM data in device RAM.
To store the current resolution in EEPROM, write ``0`` to ``w1_slave``.
Since the EEPROM has a limited amount of writes (>50k), this command should be
used wisely.
ext_power syfs entry allow tho check the power status of each device.
* '0': device parasite powered
* '1': device externally powered
Alternatively, resolution can be read or written using the dedicated
``resolution`` entry on each device, if supported by the sensor.
sysfs alarms allow read or write TH and TL (Temperature High an Low) alarms.
Some non-genuine DS18B20 chips are fixed in 12-bit mode only, so the actual
resolution is read back from the chip and verified.
Note: Changing the resolution reverts the conversion time to default.
The write-only sysfs entry ``eeprom`` is an alternative for EEPROM operations.
Write ``save`` to save device RAM to EEPROM. Write ``restore`` to restore EEPROM
data in device RAM.
``ext_power`` entry allows checking the power state of each device. Reads
``0`` if the device is parasite powered, ``1`` if the device is externally powered.
Sysfs ``alarms`` allow read or write TH and TL (Temperature High an Low) alarms.
Values shall be space separated and in the device range (typical -55 degC
to 125 degC). Values are integer as they are store in a 8bit register in
the device. Lowest value is automatically put to TL.Once set, alarms could
the device. Lowest value is automatically put to TL. Once set, alarms could
be search at master level.
The module parameter strong_pullup can be set to 0 to disable the
@ -102,5 +116,24 @@ The DS28EA00 provides an additional two pins for implementing a sequence
detection algorithm. This feature allows you to determine the physical
location of the chip in the 1-wire bus without needing pre-existing
knowledge of the bus ordering. Support is provided through the sysfs
w1_seq file. The file will contain a single line with an integer value
``w1_seq``. The file will contain a single line with an integer value
representing the device index in the bus starting at 0.
``features`` sysfs entry controls optional driver settings per device.
Insufficient power in parasite mode, line noise and insufficient conversion
time may lead to conversion failure. Original DS18B20 and some clones allow for
detection of invalid conversion. Write bit mask ``1`` to ``features`` to enable
checking the conversion success. If byte 6 of scratchpad memory is 0xC after
conversion and temperature reads 85.00 (powerup value) or 127.94 (insufficient
power), the driver returns a conversion error. Bit mask ``2`` enables poll for
conversion completion (normal power only) by generating read cycles on the bus
after conversion starts. In parasite power mode this feature is not available.
Feature bit masks may be combined (OR). More details in
Documentation/ABI/testing/sysfs-driver-w1_therm
GX20MH01 device shares family number 0x28 with DS18*20. The device is generally
compatible with DS18B20. Added are lowest 2\ :sup:`-5`, 2\ :sup:`-6` temperature
bits in Config register; R2 bit in Config register enabling 13 and 14 bit
resolutions. The device is powered up in 14-bit resolution mode. The conversion
times specified in the datasheet are too low and have to be increased. The
device supports driver features ``1`` and ``2``.

View File

@ -1725,6 +1725,7 @@ ARM/CORESIGHT FRAMEWORK AND DRIVERS
M: Mathieu Poirier <mathieu.poirier@linaro.org>
R: Suzuki K Poulose <suzuki.poulose@arm.com>
R: Mike Leach <mike.leach@linaro.org>
L: coresight@lists.linaro.org (moderated for non-subscribers)
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: Documentation/ABI/testing/sysfs-bus-coresight-devices-*
@ -4101,6 +4102,11 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git
F: drivers/char/
F: drivers/misc/
F: include/linux/miscdevice.h
X: drivers/char/agp/
X: drivers/char/hw_random/
X: drivers/char/ipmi/
X: drivers/char/random.c
X: drivers/char/tpm/
CHECKPATCH
M: Andy Whitcroft <apw@canonical.com>
@ -6828,14 +6834,17 @@ F: drivers/net/ethernet/nvidia/*
FPGA DFL DRIVERS
M: Wu Hao <hao.wu@intel.com>
R: Tom Rix <trix@redhat.com>
L: linux-fpga@vger.kernel.org
S: Maintained
F: Documentation/ABI/testing/sysfs-bus-dfl
F: Documentation/fpga/dfl.rst
F: drivers/fpga/dfl*
F: include/uapi/linux/fpga-dfl.h
FPGA MANAGER FRAMEWORK
M: Moritz Fischer <mdf@kernel.org>
R: Tom Rix <trix@redhat.com>
L: linux-fpga@vger.kernel.org
S: Maintained
W: http://www.rocketboards.org
@ -7885,6 +7894,13 @@ W: http://www.hisilicon.com
F: Documentation/devicetree/bindings/net/hisilicon*.txt
F: drivers/net/ethernet/hisilicon/
HIKEY960 ONBOARD USB GPIO HUB DRIVER
M: John Stultz <john.stultz@linaro.org>
L: linux-kernel@vger.kernel.org
S: Maintained
F: drivers/misc/hisi_hikey_usb.c
F: Documentation/devicetree/bindings/misc/hisilicon-hikey-usb.yaml
HISILICON PMU DRIVER
M: Shaokun Zhang <zhangshaokun@hisilicon.com>
S: Supported
@ -11353,6 +11369,7 @@ M: Hemant Kumar <hemantk@codeaurora.org>
L: linux-arm-msm@vger.kernel.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mani/mhi.git
F: Documentation/ABI/stable/sysfs-bus-mhi
F: Documentation/mhi/
F: drivers/bus/mhi/
F: include/linux/mhi.h
@ -12307,6 +12324,19 @@ S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/lftan/nios2.git
F: arch/nios2/
NITRO ENCLAVES (NE)
M: Andra Paraschiv <andraprs@amazon.com>
M: Alexandru Vasile <lexnv@amazon.com>
M: Alexandru Ciobotaru <alcioa@amazon.com>
L: linux-kernel@vger.kernel.org
S: Supported
W: https://aws.amazon.com/ec2/nitro/nitro-enclaves/
F: Documentation/virt/ne_overview.rst
F: drivers/virt/nitro_enclaves/
F: include/linux/nitro_enclaves.h
F: include/uapi/linux/nitro_enclaves.h
F: samples/nitro_enclaves/
NOHZ, DYNTICKS SUPPORT
M: Frederic Weisbecker <fweisbec@gmail.com>
M: Thomas Gleixner <tglx@linutronix.de>
@ -12469,6 +12499,13 @@ F: drivers/iio/gyro/fxas21002c_core.c
F: drivers/iio/gyro/fxas21002c_i2c.c
F: drivers/iio/gyro/fxas21002c_spi.c
NXP PTN5150A CC LOGIC AND EXTCON DRIVER
M: Krzysztof Kozlowski <krzk@kernel.org>
L: linux-kernel@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/extcon/extcon-ptn5150.yaml
F: drivers/extcon/extcon-ptn5150.c
NXP SGTL5000 DRIVER
M: Fabio Estevam <festevam@gmail.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)

View File

@ -223,7 +223,7 @@ static struct binder_transaction_log_entry *binder_transaction_log_add(
struct binder_work {
struct list_head entry;
enum {
enum binder_work_type {
BINDER_WORK_TRANSACTION = 1,
BINDER_WORK_TRANSACTION_COMPLETE,
BINDER_WORK_RETURN_ERROR,
@ -885,27 +885,6 @@ static struct binder_work *binder_dequeue_work_head_ilocked(
return w;
}
/**
* binder_dequeue_work_head() - Dequeues the item at head of list
* @proc: binder_proc associated with list
* @list: list to dequeue head
*
* Removes the head of the list if there are items on the list
*
* Return: pointer dequeued binder_work, NULL if list was empty
*/
static struct binder_work *binder_dequeue_work_head(
struct binder_proc *proc,
struct list_head *list)
{
struct binder_work *w;
binder_inner_proc_lock(proc);
w = binder_dequeue_work_head_ilocked(list);
binder_inner_proc_unlock(proc);
return w;
}
static void
binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer);
static void binder_free_thread(struct binder_thread *thread);
@ -2344,8 +2323,6 @@ static void binder_transaction_buffer_release(struct binder_proc *proc,
* file is done when the transaction is torn
* down.
*/
WARN_ON(failed_at &&
proc->tsk == current->group_leader);
} break;
case BINDER_TYPE_PTR:
/*
@ -3136,7 +3113,7 @@ static void binder_transaction(struct binder_proc *proc,
t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
tr->offsets_size, extra_buffers_size,
!reply && (t->flags & TF_ONE_WAY));
!reply && (t->flags & TF_ONE_WAY), current->tgid);
if (IS_ERR(t->buffer)) {
/*
* -ESRCH indicates VMA cleared. The target is dying.
@ -4587,13 +4564,17 @@ static void binder_release_work(struct binder_proc *proc,
struct list_head *list)
{
struct binder_work *w;
enum binder_work_type wtype;
while (1) {
w = binder_dequeue_work_head(proc, list);
binder_inner_proc_lock(proc);
w = binder_dequeue_work_head_ilocked(list);
wtype = w ? w->type : 0;
binder_inner_proc_unlock(proc);
if (!w)
return;
switch (w->type) {
switch (wtype) {
case BINDER_WORK_TRANSACTION: {
struct binder_transaction *t;
@ -4627,9 +4608,11 @@ static void binder_release_work(struct binder_proc *proc,
kfree(death);
binder_stats_deleted(BINDER_STAT_DEATH);
} break;
case BINDER_WORK_NODE:
break;
default:
pr_err("unexpected work type, %d, not freed\n",
w->type);
wtype);
break;
}
}
@ -5182,9 +5165,7 @@ static const struct vm_operations_struct binder_vm_ops = {
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
struct binder_proc *proc = filp->private_data;
const char *failure_string;
if (proc->tsk != current->group_leader)
return -EINVAL;
@ -5196,9 +5177,9 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
(unsigned long)pgprot_val(vma->vm_page_prot));
if (vma->vm_flags & FORBIDDEN_MMAP_FLAGS) {
ret = -EPERM;
failure_string = "bad vm_flags";
goto err_bad_arg;
pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
proc->pid, vma->vm_start, vma->vm_end, "bad vm_flags", -EPERM);
return -EPERM;
}
vma->vm_flags |= VM_DONTCOPY | VM_MIXEDMAP;
vma->vm_flags &= ~VM_MAYWRITE;
@ -5206,15 +5187,7 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
vma->vm_ops = &binder_vm_ops;
vma->vm_private_data = proc;
ret = binder_alloc_mmap_handler(&proc->alloc, vma);
if (ret)
return ret;
return 0;
err_bad_arg:
pr_err("%s: %d %lx-%lx %s failed %d\n", __func__,
proc->pid, vma->vm_start, vma->vm_end, failure_string, ret);
return ret;
return binder_alloc_mmap_handler(&proc->alloc, vma);
}
static int binder_open(struct inode *nodp, struct file *filp)

View File

@ -338,12 +338,50 @@ static inline struct vm_area_struct *binder_alloc_get_vma(
return vma;
}
static void debug_low_async_space_locked(struct binder_alloc *alloc, int pid)
{
/*
* Find the amount and size of buffers allocated by the current caller;
* The idea is that once we cross the threshold, whoever is responsible
* for the low async space is likely to try to send another async txn,
* and at some point we'll catch them in the act. This is more efficient
* than keeping a map per pid.
*/
struct rb_node *n;
struct binder_buffer *buffer;
size_t total_alloc_size = 0;
size_t num_buffers = 0;
for (n = rb_first(&alloc->allocated_buffers); n != NULL;
n = rb_next(n)) {
buffer = rb_entry(n, struct binder_buffer, rb_node);
if (buffer->pid != pid)
continue;
if (!buffer->async_transaction)
continue;
total_alloc_size += binder_alloc_buffer_size(alloc, buffer)
+ sizeof(struct binder_buffer);
num_buffers++;
}
/*
* Warn if this pid has more than 50 transactions, or more than 50% of
* async space (which is 25% of total buffer size).
*/
if (num_buffers > 50 || total_alloc_size > alloc->buffer_size / 4) {
binder_alloc_debug(BINDER_DEBUG_USER_ERROR,
"%d: pid %d spamming oneway? %zd buffers allocated for a total size of %zd\n",
alloc->pid, pid, num_buffers, total_alloc_size);
}
}
static struct binder_buffer *binder_alloc_new_buf_locked(
struct binder_alloc *alloc,
size_t data_size,
size_t offsets_size,
size_t extra_buffers_size,
int is_async)
int is_async,
int pid)
{
struct rb_node *n = alloc->free_buffers.rb_node;
struct binder_buffer *buffer;
@ -486,11 +524,20 @@ static struct binder_buffer *binder_alloc_new_buf_locked(
buffer->offsets_size = offsets_size;
buffer->async_transaction = is_async;
buffer->extra_buffers_size = extra_buffers_size;
buffer->pid = pid;
if (is_async) {
alloc->free_async_space -= size + sizeof(struct binder_buffer);
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
"%d: binder_alloc_buf size %zd async free %zd\n",
alloc->pid, size, alloc->free_async_space);
if (alloc->free_async_space < alloc->buffer_size / 10) {
/*
* Start detecting spammers once we have less than 20%
* of async space left (which is less than 10% of total
* buffer size).
*/
debug_low_async_space_locked(alloc, pid);
}
}
return buffer;
@ -508,6 +555,7 @@ err_alloc_buf_struct_failed:
* @offsets_size: user specified buffer offset
* @extra_buffers_size: size of extra space for meta-data (eg, security context)
* @is_async: buffer for async transaction
* @pid: pid to attribute allocation to (used for debugging)
*
* Allocate a new buffer given the requested sizes. Returns
* the kernel version of the buffer pointer. The size allocated
@ -520,13 +568,14 @@ struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
size_t data_size,
size_t offsets_size,
size_t extra_buffers_size,
int is_async)
int is_async,
int pid)
{
struct binder_buffer *buffer;
mutex_lock(&alloc->mutex);
buffer = binder_alloc_new_buf_locked(alloc, data_size, offsets_size,
extra_buffers_size, is_async);
extra_buffers_size, is_async, pid);
mutex_unlock(&alloc->mutex);
return buffer;
}
@ -652,7 +701,7 @@ static void binder_free_buf_locked(struct binder_alloc *alloc,
* @alloc: binder_alloc for this proc
* @buffer: kernel pointer to buffer
*
* Free the buffer allocated via binder_alloc_new_buffer()
* Free the buffer allocated via binder_alloc_new_buf()
*/
void binder_alloc_free_buf(struct binder_alloc *alloc,
struct binder_buffer *buffer)

View File

@ -32,6 +32,7 @@ struct binder_transaction;
* @offsets_size: size of array of offsets
* @extra_buffers_size: size of space for other objects (like sg lists)
* @user_data: user pointer to base of buffer space
* @pid: pid to attribute the buffer to (caller)
*
* Bookkeeping structure for binder transaction buffers
*/
@ -51,6 +52,7 @@ struct binder_buffer {
size_t offsets_size;
size_t extra_buffers_size;
void __user *user_data;
int pid;
};
/**
@ -117,7 +119,8 @@ extern struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
size_t data_size,
size_t offsets_size,
size_t extra_buffers_size,
int is_async);
int is_async,
int pid);
extern void binder_alloc_init(struct binder_alloc *alloc);
extern int binder_alloc_shrinker_init(void);
extern void binder_alloc_vma_close(struct binder_alloc *alloc);

View File

@ -119,7 +119,7 @@ static void binder_selftest_alloc_buf(struct binder_alloc *alloc,
int i;
for (i = 0; i < BUFFER_NUM; i++) {
buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0);
buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0, 0);
if (IS_ERR(buffers[i]) ||
!check_buffer_pages_allocated(alloc, buffers[i],
sizes[i])) {

View File

@ -63,7 +63,7 @@ static const struct constant_table binderfs_param_stats[] = {
{}
};
const struct fs_parameter_spec binderfs_fs_parameters[] = {
static const struct fs_parameter_spec binderfs_fs_parameters[] = {
fsparam_u32("max", Opt_max),
fsparam_enum("stats", Opt_stats_mode, binderfs_param_stats),
{}

View File

@ -272,9 +272,9 @@ static ssize_t firmware_loading_store(struct device *dev,
dev_err(dev, "%s: map pages failed\n",
__func__);
else
rc = security_kernel_post_read_file(NULL,
fw_priv->data, fw_priv->size,
READING_FIRMWARE);
rc = security_kernel_post_load_data(fw_priv->data,
fw_priv->size,
LOADING_FIRMWARE, "blob");
/*
* Same logic as fw_load_abort, only the DONE bit
@ -490,13 +490,11 @@ exit:
/**
* fw_load_sysfs_fallback() - load a firmware via the sysfs fallback mechanism
* @fw_sysfs: firmware sysfs information for the firmware to load
* @opt_flags: flags of options, FW_OPT_*
* @timeout: timeout to wait for the load
*
* In charge of constructing a sysfs fallback interface for firmware loading.
**/
static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs,
u32 opt_flags, long timeout)
static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs, long timeout)
{
int retval = 0;
struct device *f_dev = &fw_sysfs->dev;
@ -518,7 +516,7 @@ static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs,
list_add(&fw_priv->pending_list, &pending_fw_head);
mutex_unlock(&fw_lock);
if (opt_flags & FW_OPT_UEVENT) {
if (fw_priv->opt_flags & FW_OPT_UEVENT) {
fw_priv->need_uevent = true;
dev_set_uevent_suppress(f_dev, false);
dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_name);
@ -580,10 +578,10 @@ static int fw_load_from_user_helper(struct firmware *firmware,
}
fw_sysfs->fw_priv = firmware->priv;
ret = fw_load_sysfs_fallback(fw_sysfs, opt_flags, timeout);
ret = fw_load_sysfs_fallback(fw_sysfs, timeout);
if (!ret)
ret = assign_fw(firmware, device, opt_flags);
ret = assign_fw(firmware, device);
out_unlock:
usermodehelper_read_unlock();
@ -613,7 +611,7 @@ static bool fw_run_sysfs_fallback(u32 opt_flags)
return false;
/* Also permit LSMs and IMA to fail firmware sysfs fallback */
ret = security_kernel_load_data(LOADING_FIRMWARE);
ret = security_kernel_load_data(LOADING_FIRMWARE, true);
if (ret < 0)
return false;
@ -625,7 +623,8 @@ static bool fw_run_sysfs_fallback(u32 opt_flags)
* @fw: pointer to firmware image
* @name: name of firmware file to look for
* @device: device for which firmware is being loaded
* @opt_flags: options to control firmware loading behaviour
* @opt_flags: options to control firmware loading behaviour, as defined by
* &enum fw_opt
* @ret: return value from direct lookup which triggered the fallback mechanism
*
* This function is called if direct lookup for the firmware failed, it enables

View File

@ -67,10 +67,9 @@ static inline void unregister_sysfs_loader(void)
#endif /* CONFIG_FW_LOADER_USER_HELPER */
#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
int firmware_fallback_platform(struct fw_priv *fw_priv, u32 opt_flags);
int firmware_fallback_platform(struct fw_priv *fw_priv);
#else
static inline int firmware_fallback_platform(struct fw_priv *fw_priv,
u32 opt_flags)
static inline int firmware_fallback_platform(struct fw_priv *fw_priv)
{
return -ENOENT;
}

View File

@ -8,16 +8,16 @@
#include "fallback.h"
#include "firmware.h"
int firmware_fallback_platform(struct fw_priv *fw_priv, u32 opt_flags)
int firmware_fallback_platform(struct fw_priv *fw_priv)
{
const u8 *data;
size_t size;
int rc;
if (!(opt_flags & FW_OPT_FALLBACK_PLATFORM))
if (!(fw_priv->opt_flags & FW_OPT_FALLBACK_PLATFORM))
return -ENOENT;
rc = security_kernel_load_data(LOADING_FIRMWARE_EFI_EMBEDDED);
rc = security_kernel_load_data(LOADING_FIRMWARE, true);
if (rc)
return rc;
@ -27,6 +27,12 @@ int firmware_fallback_platform(struct fw_priv *fw_priv, u32 opt_flags)
if (fw_priv->data && size > fw_priv->allocated_size)
return -ENOMEM;
rc = security_kernel_post_load_data((u8 *)data, size, LOADING_FIRMWARE,
"platform");
if (rc)
return rc;
if (!fw_priv->data)
fw_priv->data = vmalloc(size);
if (!fw_priv->data)

View File

@ -32,6 +32,8 @@
* @FW_OPT_FALLBACK_PLATFORM: Enable fallback to device fw copy embedded in
* the platform's main firmware. If both this fallback and the sysfs
* fallback are enabled, then this fallback will be tried first.
* @FW_OPT_PARTIAL: Allow partial read of firmware instead of needing to read
* entire file.
*/
enum fw_opt {
FW_OPT_UEVENT = BIT(0),
@ -41,6 +43,7 @@ enum fw_opt {
FW_OPT_NOCACHE = BIT(4),
FW_OPT_NOFALLBACK_SYSFS = BIT(5),
FW_OPT_FALLBACK_PLATFORM = BIT(6),
FW_OPT_PARTIAL = BIT(7),
};
enum fw_status {
@ -68,6 +71,8 @@ struct fw_priv {
void *data;
size_t size;
size_t allocated_size;
size_t offset;
u32 opt_flags;
#ifdef CONFIG_FW_LOADER_PAGED_BUF
bool is_paged_buf;
struct page **pages;
@ -136,7 +141,7 @@ static inline void fw_state_done(struct fw_priv *fw_priv)
__fw_state_set(fw_priv, FW_STATUS_DONE);
}
int assign_fw(struct firmware *fw, struct device *device, u32 opt_flags);
int assign_fw(struct firmware *fw, struct device *device);
#ifdef CONFIG_FW_LOADER_PAGED_BUF
void fw_free_paged_buf(struct fw_priv *fw_priv);

View File

@ -12,6 +12,7 @@
#include <linux/capability.h>
#include <linux/device.h>
#include <linux/kernel_read_file.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/timer.h>
@ -167,10 +168,21 @@ static int fw_cache_piggyback_on_request(const char *name);
static struct fw_priv *__allocate_fw_priv(const char *fw_name,
struct firmware_cache *fwc,
void *dbuf, size_t size)
void *dbuf,
size_t size,
size_t offset,
u32 opt_flags)
{
struct fw_priv *fw_priv;
/* For a partial read, the buffer must be preallocated. */
if ((opt_flags & FW_OPT_PARTIAL) && !dbuf)
return NULL;
/* Only partial reads are allowed to use an offset. */
if (offset != 0 && !(opt_flags & FW_OPT_PARTIAL))
return NULL;
fw_priv = kzalloc(sizeof(*fw_priv), GFP_ATOMIC);
if (!fw_priv)
return NULL;
@ -185,6 +197,8 @@ static struct fw_priv *__allocate_fw_priv(const char *fw_name,
fw_priv->fwc = fwc;
fw_priv->data = dbuf;
fw_priv->allocated_size = size;
fw_priv->offset = offset;
fw_priv->opt_flags = opt_flags;
fw_state_init(fw_priv);
#ifdef CONFIG_FW_LOADER_USER_HELPER
INIT_LIST_HEAD(&fw_priv->pending_list);
@ -209,13 +223,20 @@ static struct fw_priv *__lookup_fw_priv(const char *fw_name)
/* Returns 1 for batching firmware requests with the same name */
static int alloc_lookup_fw_priv(const char *fw_name,
struct firmware_cache *fwc,
struct fw_priv **fw_priv, void *dbuf,
size_t size, u32 opt_flags)
struct fw_priv **fw_priv,
void *dbuf,
size_t size,
size_t offset,
u32 opt_flags)
{
struct fw_priv *tmp;
spin_lock(&fwc->lock);
if (!(opt_flags & FW_OPT_NOCACHE)) {
/*
* Do not merge requests that are marked to be non-cached or
* are performing partial reads.
*/
if (!(opt_flags & (FW_OPT_NOCACHE | FW_OPT_PARTIAL))) {
tmp = __lookup_fw_priv(fw_name);
if (tmp) {
kref_get(&tmp->ref);
@ -226,7 +247,7 @@ static int alloc_lookup_fw_priv(const char *fw_name,
}
}
tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size);
tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size, offset, opt_flags);
if (tmp) {
INIT_LIST_HEAD(&tmp->list);
if (!(opt_flags & FW_OPT_NOCACHE))
@ -466,18 +487,16 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
size_t in_size,
const void *in_buffer))
{
loff_t size;
size_t size;
int i, len;
int rc = -ENOENT;
char *path;
enum kernel_read_file_id id = READING_FIRMWARE;
size_t msize = INT_MAX;
void *buffer = NULL;
/* Already populated data member means we're loading into a buffer */
if (!decompress && fw_priv->data) {
buffer = fw_priv->data;
id = READING_FIRMWARE_PREALLOC_BUFFER;
msize = fw_priv->allocated_size;
}
@ -486,6 +505,9 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(fw_path); i++) {
size_t file_size = 0;
size_t *file_size_ptr = NULL;
/* skip the unset customized path */
if (!fw_path[i][0])
continue;
@ -499,10 +521,20 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
fw_priv->size = 0;
/*
* The total file size is only examined when doing a partial
* read; the "full read" case needs to fail if the whole
* firmware was not completely loaded.
*/
if ((fw_priv->opt_flags & FW_OPT_PARTIAL) && buffer)
file_size_ptr = &file_size;
/* load firmware files from the mount namespace of init */
rc = kernel_read_file_from_path_initns(path, &buffer,
&size, msize, id);
if (rc) {
rc = kernel_read_file_from_path_initns(path, fw_priv->offset,
&buffer, msize,
file_size_ptr,
READING_FIRMWARE);
if (rc < 0) {
if (rc != -ENOENT)
dev_warn(device, "loading %s failed with error %d\n",
path, rc);
@ -511,6 +543,9 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
path);
continue;
}
size = rc;
rc = 0;
dev_dbg(device, "Loading firmware from %s\n", path);
if (decompress) {
dev_dbg(device, "f/w decompressing %s\n",
@ -637,7 +672,7 @@ static int fw_add_devm_name(struct device *dev, const char *name)
}
#endif
int assign_fw(struct firmware *fw, struct device *device, u32 opt_flags)
int assign_fw(struct firmware *fw, struct device *device)
{
struct fw_priv *fw_priv = fw->priv;
int ret;
@ -656,8 +691,8 @@ int assign_fw(struct firmware *fw, struct device *device, u32 opt_flags)
* should be fixed in devres or driver core.
*/
/* don't cache firmware handled without uevent */
if (device && (opt_flags & FW_OPT_UEVENT) &&
!(opt_flags & FW_OPT_NOCACHE)) {
if (device && (fw_priv->opt_flags & FW_OPT_UEVENT) &&
!(fw_priv->opt_flags & FW_OPT_NOCACHE)) {
ret = fw_add_devm_name(device, fw_priv->fw_name);
if (ret) {
mutex_unlock(&fw_lock);
@ -669,7 +704,7 @@ int assign_fw(struct firmware *fw, struct device *device, u32 opt_flags)
* After caching firmware image is started, let it piggyback
* on request firmware.
*/
if (!(opt_flags & FW_OPT_NOCACHE) &&
if (!(fw_priv->opt_flags & FW_OPT_NOCACHE) &&
fw_priv->fwc->state == FW_LOADER_START_CACHE) {
if (fw_cache_piggyback_on_request(fw_priv->fw_name))
kref_get(&fw_priv->ref);
@ -688,7 +723,7 @@ int assign_fw(struct firmware *fw, struct device *device, u32 opt_flags)
static int
_request_firmware_prepare(struct firmware **firmware_p, const char *name,
struct device *device, void *dbuf, size_t size,
u32 opt_flags)
size_t offset, u32 opt_flags)
{
struct firmware *firmware;
struct fw_priv *fw_priv;
@ -707,7 +742,7 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
}
ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size,
opt_flags);
offset, opt_flags);
/*
* bind with 'priv' now to avoid warning in failure path
@ -754,9 +789,10 @@ static void fw_abort_batch_reqs(struct firmware *fw)
static int
_request_firmware(const struct firmware **firmware_p, const char *name,
struct device *device, void *buf, size_t size,
u32 opt_flags)
size_t offset, u32 opt_flags)
{
struct firmware *fw = NULL;
bool nondirect = false;
int ret;
if (!firmware_p)
@ -768,28 +804,34 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
}
ret = _request_firmware_prepare(&fw, name, device, buf, size,
opt_flags);
offset, opt_flags);
if (ret <= 0) /* error or already assigned */
goto out;
ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL);
/* Only full reads can support decompression, platform, and sysfs. */
if (!(opt_flags & FW_OPT_PARTIAL))
nondirect = true;
#ifdef CONFIG_FW_LOADER_COMPRESS
if (ret == -ENOENT)
if (ret == -ENOENT && nondirect)
ret = fw_get_filesystem_firmware(device, fw->priv, ".xz",
fw_decompress_xz);
#endif
if (ret == -ENOENT)
ret = firmware_fallback_platform(fw->priv, opt_flags);
if (ret == -ENOENT && nondirect)
ret = firmware_fallback_platform(fw->priv);
if (ret) {
if (!(opt_flags & FW_OPT_NO_WARN))
dev_warn(device,
"Direct firmware load for %s failed with error %d\n",
name, ret);
ret = firmware_fallback_sysfs(fw, name, device, opt_flags, ret);
if (nondirect)
ret = firmware_fallback_sysfs(fw, name, device,
opt_flags, ret);
} else
ret = assign_fw(fw, device, opt_flags);
ret = assign_fw(fw, device);
out:
if (ret < 0) {
@ -830,7 +872,7 @@ request_firmware(const struct firmware **firmware_p, const char *name,
/* Need to pin this module until return */
__module_get(THIS_MODULE);
ret = _request_firmware(firmware_p, name, device, NULL, 0,
ret = _request_firmware(firmware_p, name, device, NULL, 0, 0,
FW_OPT_UEVENT);
module_put(THIS_MODULE);
return ret;
@ -857,7 +899,7 @@ int firmware_request_nowarn(const struct firmware **firmware, const char *name,
/* Need to pin this module until return */
__module_get(THIS_MODULE);
ret = _request_firmware(firmware, name, device, NULL, 0,
ret = _request_firmware(firmware, name, device, NULL, 0, 0,
FW_OPT_UEVENT | FW_OPT_NO_WARN);
module_put(THIS_MODULE);
return ret;
@ -881,7 +923,7 @@ int request_firmware_direct(const struct firmware **firmware_p,
int ret;
__module_get(THIS_MODULE);
ret = _request_firmware(firmware_p, name, device, NULL, 0,
ret = _request_firmware(firmware_p, name, device, NULL, 0, 0,
FW_OPT_UEVENT | FW_OPT_NO_WARN |
FW_OPT_NOFALLBACK_SYSFS);
module_put(THIS_MODULE);
@ -906,7 +948,7 @@ int firmware_request_platform(const struct firmware **firmware,
/* Need to pin this module until return */
__module_get(THIS_MODULE);
ret = _request_firmware(firmware, name, device, NULL, 0,
ret = _request_firmware(firmware, name, device, NULL, 0, 0,
FW_OPT_UEVENT | FW_OPT_FALLBACK_PLATFORM);
module_put(THIS_MODULE);
return ret;
@ -962,13 +1004,44 @@ request_firmware_into_buf(const struct firmware **firmware_p, const char *name,
return -EOPNOTSUPP;
__module_get(THIS_MODULE);
ret = _request_firmware(firmware_p, name, device, buf, size,
ret = _request_firmware(firmware_p, name, device, buf, size, 0,
FW_OPT_UEVENT | FW_OPT_NOCACHE);
module_put(THIS_MODULE);
return ret;
}
EXPORT_SYMBOL(request_firmware_into_buf);
/**
* request_partial_firmware_into_buf() - load partial firmware into a previously allocated buffer
* @firmware_p: pointer to firmware image
* @name: name of firmware file
* @device: device for which firmware is being loaded and DMA region allocated
* @buf: address of buffer to load firmware into
* @size: size of buffer
* @offset: offset into file to read
*
* This function works pretty much like request_firmware_into_buf except
* it allows a partial read of the file.
*/
int
request_partial_firmware_into_buf(const struct firmware **firmware_p,
const char *name, struct device *device,
void *buf, size_t size, size_t offset)
{
int ret;
if (fw_cache_is_setup(device, name))
return -EOPNOTSUPP;
__module_get(THIS_MODULE);
ret = _request_firmware(firmware_p, name, device, buf, size, offset,
FW_OPT_UEVENT | FW_OPT_NOCACHE |
FW_OPT_PARTIAL);
module_put(THIS_MODULE);
return ret;
}
EXPORT_SYMBOL(request_partial_firmware_into_buf);
/**
* release_firmware() - release the resource associated with a firmware image
* @fw: firmware resource to release
@ -1001,7 +1074,7 @@ static void request_firmware_work_func(struct work_struct *work)
fw_work = container_of(work, struct firmware_work, work);
_request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0,
_request_firmware(&fw, fw_work->name, fw_work->device, NULL, 0, 0,
fw_work->opt_flags);
fw_work->cont(fw, fw_work->context);
put_device(fw_work->device); /* taken in request_firmware_nowait() */

View File

@ -3,6 +3,7 @@
* Freescale data path resource container (DPRC) driver
*
* Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
* Copyright 2019-2020 NXP
* Author: German Rivera <German.Rivera@freescale.com>
*
*/
@ -80,9 +81,9 @@ static int __fsl_mc_device_remove(struct device *dev, void *data)
* the MC by removing devices that represent MC objects that have
* been dynamically removed in the physical DPRC.
*/
static void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev,
struct fsl_mc_obj_desc *obj_desc_array,
int num_child_objects_in_mc)
void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev,
struct fsl_mc_obj_desc *obj_desc_array,
int num_child_objects_in_mc)
{
if (num_child_objects_in_mc != 0) {
/*
@ -104,6 +105,7 @@ static void dprc_remove_devices(struct fsl_mc_device *mc_bus_dev,
__fsl_mc_device_remove);
}
}
EXPORT_SYMBOL_GPL(dprc_remove_devices);
static int __fsl_mc_device_match(struct device *dev, void *data)
{
@ -220,8 +222,8 @@ static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev,
* dprc_scan_objects - Discover objects in a DPRC
*
* @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object
* @total_irq_count: If argument is provided the function populates the
* total number of IRQs created by objects in the DPRC.
* @alloc_interrupts: if true the function allocates the interrupt pool,
* otherwise the interrupt allocation is delayed
*
* Detects objects added and removed from a DPRC and synchronizes the
* state of the Linux bus driver, MC by adding and removing
@ -236,7 +238,7 @@ static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev,
* of the device drivers for the non-allocatable devices.
*/
static int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev,
unsigned int *total_irq_count)
bool alloc_interrupts)
{
int num_child_objects;
int dprc_get_obj_failures;
@ -317,22 +319,21 @@ static int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev,
* Allocate IRQ's before binding the scanned devices with their
* respective drivers.
*/
if (dev_get_msi_domain(&mc_bus_dev->dev) && !mc_bus->irq_resources) {
if (dev_get_msi_domain(&mc_bus_dev->dev)) {
if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) {
dev_warn(&mc_bus_dev->dev,
"IRQs needed (%u) exceed IRQs preallocated (%u)\n",
irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
}
error = fsl_mc_populate_irq_pool(mc_bus,
FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
if (error < 0)
return error;
if (alloc_interrupts && !mc_bus->irq_resources) {
error = fsl_mc_populate_irq_pool(mc_bus_dev,
FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
if (error < 0)
return error;
}
}
if (total_irq_count)
*total_irq_count = irq_count;
dprc_remove_devices(mc_bus_dev, child_obj_desc_array,
num_child_objects);
@ -354,9 +355,10 @@ static int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev,
* bus driver with the actual state of the MC by adding and removing
* devices as appropriate.
*/
static int dprc_scan_container(struct fsl_mc_device *mc_bus_dev)
int dprc_scan_container(struct fsl_mc_device *mc_bus_dev,
bool alloc_interrupts)
{
int error;
int error = 0;
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
fsl_mc_init_all_resource_pools(mc_bus_dev);
@ -365,16 +367,12 @@ static int dprc_scan_container(struct fsl_mc_device *mc_bus_dev)
* Discover objects in the DPRC:
*/
mutex_lock(&mc_bus->scan_mutex);
error = dprc_scan_objects(mc_bus_dev, NULL);
error = dprc_scan_objects(mc_bus_dev, alloc_interrupts);
mutex_unlock(&mc_bus->scan_mutex);
if (error < 0) {
fsl_mc_cleanup_all_resource_pools(mc_bus_dev);
return error;
}
return 0;
return error;
}
EXPORT_SYMBOL_GPL(dprc_scan_container);
/**
* dprc_irq0_handler - Regular ISR for DPRC interrupt 0
*
@ -434,9 +432,8 @@ static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg)
DPRC_IRQ_EVENT_CONTAINER_DESTROYED |
DPRC_IRQ_EVENT_OBJ_DESTROYED |
DPRC_IRQ_EVENT_OBJ_CREATED)) {
unsigned int irq_count;
error = dprc_scan_objects(mc_dev, &irq_count);
error = dprc_scan_objects(mc_dev, true);
if (error < 0) {
/*
* If the error is -ENXIO, we ignore it, as it indicates
@ -451,12 +448,6 @@ static irqreturn_t dprc_irq0_handler_thread(int irq_num, void *arg)
goto out;
}
if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) {
dev_warn(dev,
"IRQs needed (%u) exceed IRQs preallocated (%u)\n",
irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
}
}
out:
@ -597,25 +588,24 @@ error_free_irqs:
}
/**
* dprc_probe - callback invoked when a DPRC is being bound to this driver
* dprc_setup - opens and creates a mc_io for DPRC
*
* @mc_dev: Pointer to fsl-mc device representing a DPRC
*
* It opens the physical DPRC in the MC.
* It scans the DPRC to discover the MC objects contained in it.
* It creates the interrupt pool for the MC bus associated with the DPRC.
* It configures the interrupts for the DPRC device itself.
* It configures the DPRC portal used to communicate with MC
*/
static int dprc_probe(struct fsl_mc_device *mc_dev)
int dprc_setup(struct fsl_mc_device *mc_dev)
{
int error;
size_t region_size;
struct device *parent_dev = mc_dev->dev.parent;
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
struct irq_domain *mc_msi_domain;
bool mc_io_created = false;
bool msi_domain_set = false;
u16 major_ver, minor_ver;
struct irq_domain *mc_msi_domain;
size_t region_size;
int error;
if (!is_fsl_mc_bus_dprc(mc_dev))
return -EINVAL;
@ -690,23 +680,6 @@ static int dprc_probe(struct fsl_mc_device *mc_dev)
goto error_cleanup_open;
}
mutex_init(&mc_bus->scan_mutex);
/*
* Discover MC objects in DPRC object:
*/
error = dprc_scan_container(mc_dev);
if (error < 0)
goto error_cleanup_open;
/*
* Configure interrupt for the DPRC object associated with this MC bus:
*/
error = dprc_setup_irq(mc_dev);
if (error < 0)
goto error_cleanup_open;
dev_info(&mc_dev->dev, "DPRC device bound to driver");
return 0;
error_cleanup_open:
@ -723,6 +696,49 @@ error_cleanup_msi_domain:
return error;
}
EXPORT_SYMBOL_GPL(dprc_setup);
/**
* dprc_probe - callback invoked when a DPRC is being bound to this driver
*
* @mc_dev: Pointer to fsl-mc device representing a DPRC
*
* It opens the physical DPRC in the MC.
* It scans the DPRC to discover the MC objects contained in it.
* It creates the interrupt pool for the MC bus associated with the DPRC.
* It configures the interrupts for the DPRC device itself.
*/
static int dprc_probe(struct fsl_mc_device *mc_dev)
{
int error;
error = dprc_setup(mc_dev);
if (error < 0)
return error;
/*
* Discover MC objects in DPRC object:
*/
error = dprc_scan_container(mc_dev, true);
if (error < 0)
goto dprc_cleanup;
/*
* Configure interrupt for the DPRC object associated with this MC bus:
*/
error = dprc_setup_irq(mc_dev);
if (error < 0)
goto scan_cleanup;
dev_info(&mc_dev->dev, "DPRC device bound to driver");
return 0;
scan_cleanup:
device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);
dprc_cleanup:
dprc_cleanup(mc_dev);
return error;
}
/*
* Tear down interrupt for a given DPRC object
@ -738,6 +754,53 @@ static void dprc_teardown_irq(struct fsl_mc_device *mc_dev)
fsl_mc_free_irqs(mc_dev);
}
/**
* dprc_cleanup - function that cleanups a DPRC
*
* @mc_dev: Pointer to fsl-mc device representing the DPRC
*
* It closes the DPRC device in the MC.
* It destroys the interrupt pool associated with this MC bus.
*/
int dprc_cleanup(struct fsl_mc_device *mc_dev)
{
int error;
/* this function should be called only for DPRCs, it
* is an error to call it for regular objects
*/
if (!is_fsl_mc_bus_dprc(mc_dev))
return -EINVAL;
if (dev_get_msi_domain(&mc_dev->dev)) {
fsl_mc_cleanup_irq_pool(mc_dev);
dev_set_msi_domain(&mc_dev->dev, NULL);
}
fsl_mc_cleanup_all_resource_pools(mc_dev);
/* if this step fails we cannot go further with cleanup as there is no way of
* communicating with the firmware
*/
if (!mc_dev->mc_io) {
dev_err(&mc_dev->dev, "mc_io is NULL, tear down cannot be performed in firmware\n");
return -EINVAL;
}
error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
if (error < 0)
dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error);
if (!fsl_mc_is_root_dprc(&mc_dev->dev)) {
fsl_destroy_mc_io(mc_dev->mc_io);
mc_dev->mc_io = NULL;
}
return 0;
}
EXPORT_SYMBOL_GPL(dprc_cleanup);
/**
* dprc_remove - callback invoked when a DPRC is being unbound from this driver
*
@ -750,13 +813,10 @@ static void dprc_teardown_irq(struct fsl_mc_device *mc_dev)
*/
static int dprc_remove(struct fsl_mc_device *mc_dev)
{
int error;
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_dev);
if (!is_fsl_mc_bus_dprc(mc_dev))
return -EINVAL;
if (!mc_dev->mc_io)
return -EINVAL;
if (!mc_bus->irq_resources)
return -EINVAL;
@ -766,21 +826,7 @@ static int dprc_remove(struct fsl_mc_device *mc_dev)
device_for_each_child(&mc_dev->dev, NULL, __fsl_mc_device_remove);
if (dev_get_msi_domain(&mc_dev->dev)) {
fsl_mc_cleanup_irq_pool(mc_bus);
dev_set_msi_domain(&mc_dev->dev, NULL);
}
fsl_mc_cleanup_all_resource_pools(mc_dev);
error = dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
if (error < 0)
dev_err(&mc_dev->dev, "dprc_close() failed: %d\n", error);
if (!fsl_mc_is_root_dprc(&mc_dev->dev)) {
fsl_destroy_mc_io(mc_dev->mc_io);
mc_dev->mc_io = NULL;
}
dprc_cleanup(mc_dev);
dev_info(&mc_dev->dev, "DPRC device unbound from driver");
return 0;

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/*
* Copyright 2013-2016 Freescale Semiconductor Inc.
* Copyright 2020 NXP
*
*/
#include <linux/kernel.h>
@ -8,6 +9,13 @@
#include "fsl-mc-private.h"
/*
* cache the DPRC version to reduce the number of commands
* towards the mc firmware
*/
static u16 dprc_major_ver;
static u16 dprc_minor_ver;
/**
* dprc_open() - Open DPRC object for use
* @mc_io: Pointer to MC portal's I/O object
@ -72,6 +80,77 @@ int dprc_close(struct fsl_mc_io *mc_io,
}
EXPORT_SYMBOL_GPL(dprc_close);
/**
* dprc_reset_container - Reset child container.
* @mc_io: Pointer to MC portal's I/O object
* @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
* @token: Token of DPRC object
* @child_container_id: ID of the container to reset
* @options: 32 bit options:
* - 0 (no bits set) - all the objects inside the container are
* reset. The child containers are entered recursively and the
* objects reset. All the objects (including the child containers)
* are closed.
* - bit 0 set - all the objects inside the container are reset.
* However the child containers are not entered recursively.
* This option is supported for API versions >= 6.5
* In case a software context crashes or becomes non-responsive, the parent
* may wish to reset its resources container before the software context is
* restarted.
*
* This routine informs all objects assigned to the child container that the
* container is being reset, so they may perform any cleanup operations that are
* needed. All objects handles that were owned by the child container shall be
* closed.
*
* Note that such request may be submitted even if the child software context
* has not crashed, but the resulting object cleanup operations will not be
* aware of that.
*
* Return: '0' on Success; Error code otherwise.
*/
int dprc_reset_container(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
int child_container_id,
u32 options)
{
struct fsl_mc_command cmd = { 0 };
struct dprc_cmd_reset_container *cmd_params;
u32 cmdid = DPRC_CMDID_RESET_CONT;
int err;
/*
* If the DPRC object version was not yet cached, cache it now.
* Otherwise use the already cached value.
*/
if (!dprc_major_ver && !dprc_minor_ver) {
err = dprc_get_api_version(mc_io, 0,
&dprc_major_ver,
&dprc_minor_ver);
if (err)
return err;
}
/*
* MC API 6.5 introduced a new field in the command used to pass
* some flags.
* Bit 0 indicates that the child containers are not recursively reset.
*/
if (dprc_major_ver > 6 || (dprc_major_ver == 6 && dprc_minor_ver >= 5))
cmdid = DPRC_CMDID_RESET_CONT_V2;
/* prepare command */
cmd.header = mc_encode_cmd_header(cmdid, cmd_flags, token);
cmd_params = (struct dprc_cmd_reset_container *)cmd.params;
cmd_params->child_container_id = cpu_to_le32(child_container_id);
cmd_params->options = cpu_to_le32(options);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
EXPORT_SYMBOL_GPL(dprc_reset_container);
/**
* dprc_set_irq() - Set IRQ information for the DPRC to trigger an interrupt.
* @mc_io: Pointer to MC portal's I/O object
@ -281,7 +360,7 @@ int dprc_get_attributes(struct fsl_mc_io *mc_io,
/* retrieve response parameters */
rsp_params = (struct dprc_rsp_get_attributes *)cmd.params;
attr->container_id = le32_to_cpu(rsp_params->container_id);
attr->icid = le16_to_cpu(rsp_params->icid);
attr->icid = le32_to_cpu(rsp_params->icid);
attr->options = le32_to_cpu(rsp_params->options);
attr->portal_id = le32_to_cpu(rsp_params->portal_id);
@ -443,30 +522,44 @@ int dprc_get_obj_region(struct fsl_mc_io *mc_io,
struct fsl_mc_command cmd = { 0 };
struct dprc_cmd_get_obj_region *cmd_params;
struct dprc_rsp_get_obj_region *rsp_params;
u16 major_ver, minor_ver;
int err;
/* prepare command */
err = dprc_get_api_version(mc_io, 0,
&major_ver,
&minor_ver);
if (err)
return err;
/*
* If the DPRC object version was not yet cached, cache it now.
* Otherwise use the already cached value.
*/
if (!dprc_major_ver && !dprc_minor_ver) {
err = dprc_get_api_version(mc_io, 0,
&dprc_major_ver,
&dprc_minor_ver);
if (err)
return err;
}
/**
* MC API version 6.3 introduced a new field to the region
* descriptor: base_address. If the older API is in use then the base
* address is set to zero to indicate it needs to be obtained elsewhere
* (typically the device tree).
*/
if (major_ver > 6 || (major_ver == 6 && minor_ver >= 3))
cmd.header =
mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG_V2,
cmd_flags, token);
else
cmd.header =
mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG,
cmd_flags, token);
if (dprc_major_ver > 6 || (dprc_major_ver == 6 && dprc_minor_ver >= 6)) {
/*
* MC API version 6.6 changed the size of the MC portals and software
* portals to 64K (as implemented by hardware). If older API is in use the
* size reported is less (64 bytes for mc portals and 4K for software
* portals).
*/
cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG_V3,
cmd_flags, token);
} else if (dprc_major_ver == 6 && dprc_minor_ver >= 3) {
/*
* MC API version 6.3 introduced a new field to the region
* descriptor: base_address. If the older API is in use then the base
* address is set to zero to indicate it needs to be obtained elsewhere
* (typically the device tree).
*/
cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG_V2,
cmd_flags, token);
} else {
cmd.header = mc_encode_cmd_header(DPRC_CMDID_GET_OBJ_REG,
cmd_flags, token);
}
cmd_params = (struct dprc_cmd_get_obj_region *)cmd.params;
cmd_params->obj_id = cpu_to_le32(obj_id);
@ -483,7 +576,7 @@ int dprc_get_obj_region(struct fsl_mc_io *mc_io,
rsp_params = (struct dprc_rsp_get_obj_region *)cmd.params;
region_desc->base_offset = le64_to_cpu(rsp_params->base_offset);
region_desc->size = le32_to_cpu(rsp_params->size);
if (major_ver > 6 || (major_ver == 6 && minor_ver >= 3))
if (dprc_major_ver > 6 || (dprc_major_ver == 6 && dprc_minor_ver >= 3))
region_desc->base_address = le64_to_cpu(rsp_params->base_addr);
else
region_desc->base_address = 0;

View File

@ -344,7 +344,7 @@ EXPORT_SYMBOL_GPL(fsl_mc_object_free);
* Initialize the interrupt pool associated with an fsl-mc bus.
* It allocates a block of IRQs from the GIC-ITS.
*/
int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus,
int fsl_mc_populate_irq_pool(struct fsl_mc_device *mc_bus_dev,
unsigned int irq_count)
{
unsigned int i;
@ -352,10 +352,14 @@ int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus,
struct fsl_mc_device_irq *irq_resources;
struct fsl_mc_device_irq *mc_dev_irq;
int error;
struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
struct fsl_mc_resource_pool *res_pool =
&mc_bus->resource_pools[FSL_MC_POOL_IRQ];
/* do nothing if the IRQ pool is already populated */
if (mc_bus->irq_resources)
return 0;
if (irq_count == 0 ||
irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS)
return -EINVAL;
@ -407,9 +411,9 @@ EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool);
* Teardown the interrupt pool associated with an fsl-mc bus.
* It frees the IRQs that were allocated to the pool, back to the GIC-ITS.
*/
void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus)
void fsl_mc_cleanup_irq_pool(struct fsl_mc_device *mc_bus_dev)
{
struct fsl_mc_device *mc_bus_dev = &mc_bus->mc_dev;
struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev);
struct fsl_mc_resource_pool *res_pool =
&mc_bus->resource_pools[FSL_MC_POOL_IRQ];

View File

@ -3,6 +3,7 @@
* Freescale Management Complex (MC) bus driver
*
* Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
* Copyright 2019-2020 NXP
* Author: German Rivera <German.Rivera@freescale.com>
*
*/
@ -78,6 +79,12 @@ static int fsl_mc_bus_match(struct device *dev, struct device_driver *drv)
struct fsl_mc_driver *mc_drv = to_fsl_mc_driver(drv);
bool found = false;
/* When driver_override is set, only bind to the matching driver */
if (mc_dev->driver_override) {
found = !strcmp(mc_dev->driver_override, mc_drv->driver.name);
goto out;
}
if (!mc_drv->match_id_table)
goto out;
@ -147,8 +154,52 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
}
static DEVICE_ATTR_RO(modalias);
static ssize_t driver_override_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
char *driver_override, *old = mc_dev->driver_override;
char *cp;
if (WARN_ON(dev->bus != &fsl_mc_bus_type))
return -EINVAL;
if (count >= (PAGE_SIZE - 1))
return -EINVAL;
driver_override = kstrndup(buf, count, GFP_KERNEL);
if (!driver_override)
return -ENOMEM;
cp = strchr(driver_override, '\n');
if (cp)
*cp = '\0';
if (strlen(driver_override)) {
mc_dev->driver_override = driver_override;
} else {
kfree(driver_override);
mc_dev->driver_override = NULL;
}
kfree(old);
return count;
}
static ssize_t driver_override_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
return snprintf(buf, PAGE_SIZE, "%s\n", mc_dev->driver_override);
}
static DEVICE_ATTR_RW(driver_override);
static struct attribute *fsl_mc_dev_attrs[] = {
&dev_attr_modalias.attr,
&dev_attr_driver_override.attr,
NULL,
};
@ -452,7 +503,7 @@ common_cleanup:
}
static int get_dprc_icid(struct fsl_mc_io *mc_io,
int container_id, u16 *icid)
int container_id, u32 *icid)
{
struct dprc_attributes attr;
int error;
@ -564,11 +615,8 @@ static int fsl_mc_device_get_mmio_regions(struct fsl_mc_device *mc_dev,
regions[i].end = regions[i].start + region_desc.size - 1;
regions[i].name = "fsl-mc object MMIO region";
regions[i].flags = IORESOURCE_IO;
if (region_desc.flags & DPRC_REGION_CACHEABLE)
regions[i].flags |= IORESOURCE_CACHEABLE;
if (region_desc.flags & DPRC_REGION_SHAREABLE)
regions[i].flags |= IORESOURCE_MEM;
regions[i].flags = region_desc.flags & IORESOURCE_BITS;
regions[i].flags |= IORESOURCE_MEM;
}
mc_dev->regions = regions;
@ -630,6 +678,7 @@ int fsl_mc_device_add(struct fsl_mc_obj_desc *obj_desc,
if (!mc_bus)
return -ENOMEM;
mutex_init(&mc_bus->scan_mutex);
mc_dev = &mc_bus->mc_dev;
} else {
/*
@ -748,6 +797,9 @@ EXPORT_SYMBOL_GPL(fsl_mc_device_add);
*/
void fsl_mc_device_remove(struct fsl_mc_device *mc_dev)
{
kfree(mc_dev->driver_override);
mc_dev->driver_override = NULL;
/*
* The device-specific remove callback will get invoked by device_del()
*/
@ -908,9 +960,6 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
u32 mc_portal_size, mc_stream_id;
struct resource *plat_res;
if (!iommu_present(&fsl_mc_bus_type))
return -EPROBE_DEFER;
mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
if (!mc)
return -ENOMEM;
@ -918,11 +967,11 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mc);
plat_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
mc->fsl_mc_regs = devm_ioremap_resource(&pdev->dev, plat_res);
if (IS_ERR(mc->fsl_mc_regs))
return PTR_ERR(mc->fsl_mc_regs);
if (plat_res)
mc->fsl_mc_regs = devm_ioremap_resource(&pdev->dev, plat_res);
if (IS_ENABLED(CONFIG_ACPI) && !dev_of_node(&pdev->dev)) {
if (mc->fsl_mc_regs && IS_ENABLED(CONFIG_ACPI) &&
!dev_of_node(&pdev->dev)) {
mc_stream_id = readl(mc->fsl_mc_regs + FSL_MC_FAPR);
/*
* HW ORs the PL and BMT bit, places the result in bit 15 of

View File

@ -80,10 +80,12 @@ int dpmcp_reset(struct fsl_mc_io *mc_io,
/* DPRC command versioning */
#define DPRC_CMD_BASE_VERSION 1
#define DPRC_CMD_2ND_VERSION 2
#define DPRC_CMD_3RD_VERSION 3
#define DPRC_CMD_ID_OFFSET 4
#define DPRC_CMD(id) (((id) << DPRC_CMD_ID_OFFSET) | DPRC_CMD_BASE_VERSION)
#define DPRC_CMD_V2(id) (((id) << DPRC_CMD_ID_OFFSET) | DPRC_CMD_2ND_VERSION)
#define DPRC_CMD_V3(id) (((id) << DPRC_CMD_ID_OFFSET) | DPRC_CMD_3RD_VERSION)
/* DPRC command IDs */
#define DPRC_CMDID_CLOSE DPRC_CMD(0x800)
@ -91,6 +93,8 @@ int dpmcp_reset(struct fsl_mc_io *mc_io,
#define DPRC_CMDID_GET_API_VERSION DPRC_CMD(0xa05)
#define DPRC_CMDID_GET_ATTR DPRC_CMD(0x004)
#define DPRC_CMDID_RESET_CONT DPRC_CMD(0x005)
#define DPRC_CMDID_RESET_CONT_V2 DPRC_CMD_V2(0x005)
#define DPRC_CMDID_SET_IRQ DPRC_CMD(0x010)
#define DPRC_CMDID_SET_IRQ_ENABLE DPRC_CMD(0x012)
@ -103,6 +107,7 @@ int dpmcp_reset(struct fsl_mc_io *mc_io,
#define DPRC_CMDID_GET_OBJ DPRC_CMD(0x15A)
#define DPRC_CMDID_GET_OBJ_REG DPRC_CMD(0x15E)
#define DPRC_CMDID_GET_OBJ_REG_V2 DPRC_CMD_V2(0x15E)
#define DPRC_CMDID_GET_OBJ_REG_V3 DPRC_CMD_V3(0x15E)
#define DPRC_CMDID_SET_OBJ_IRQ DPRC_CMD(0x15F)
#define DPRC_CMDID_GET_CONNECTION DPRC_CMD(0x16C)
@ -111,6 +116,11 @@ struct dprc_cmd_open {
__le32 container_id;
};
struct dprc_cmd_reset_container {
__le32 child_container_id;
__le32 options;
};
struct dprc_cmd_set_irq {
/* cmd word 0 */
__le32 irq_val;
@ -152,8 +162,7 @@ struct dprc_cmd_clear_irq_status {
struct dprc_rsp_get_attributes {
/* response word 0 */
__le32 container_id;
__le16 icid;
__le16 pad;
__le32 icid;
/* response word 1 */
__le32 options;
__le32 portal_id;
@ -330,7 +339,7 @@ int dprc_clear_irq_status(struct fsl_mc_io *mc_io,
*/
struct dprc_attributes {
int container_id;
u16 icid;
u32 icid;
int portal_id;
u64 options;
};
@ -358,12 +367,6 @@ int dprc_set_obj_irq(struct fsl_mc_io *mc_io,
int obj_id,
u8 irq_index,
struct dprc_irq_cfg *irq_cfg);
/* Region flags */
/* Cacheable - Indicates that region should be mapped as cacheable */
#define DPRC_REGION_CACHEABLE 0x00000001
#define DPRC_REGION_SHAREABLE 0x00000002
/**
* enum dprc_region_type - Region type
* @DPRC_REGION_TYPE_MC_PORTAL: MC portal region
@ -518,11 +521,6 @@ struct dpcon_cmd_set_notification {
__le64 user_ctx;
};
/**
* Maximum number of total IRQs that can be pre-allocated for an MC bus'
* IRQ pool
*/
#define FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS 256
/**
* struct fsl_mc_resource_pool - Pool of MC resources of a given
@ -597,11 +595,6 @@ void fsl_mc_msi_domain_free_irqs(struct device *dev);
struct irq_domain *fsl_mc_find_msi_domain(struct device *dev);
int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus,
unsigned int irq_count);
void fsl_mc_cleanup_irq_pool(struct fsl_mc_bus *mc_bus);
int __must_check fsl_create_mc_io(struct device *dev,
phys_addr_t mc_portal_phys_addr,
u32 mc_portal_size,

View File

@ -129,7 +129,12 @@ error_destroy_mc_io:
*/
void fsl_destroy_mc_io(struct fsl_mc_io *mc_io)
{
struct fsl_mc_device *dpmcp_dev = mc_io->dpmcp_dev;
struct fsl_mc_device *dpmcp_dev;
if (!mc_io)
return;
dpmcp_dev = mc_io->dpmcp_dev;
if (dpmcp_dev)
fsl_mc_io_unset_dpmcp(mc_io);

View File

@ -6,9 +6,17 @@
#
config MHI_BUS
tristate "Modem Host Interface (MHI) bus"
help
Bus driver for MHI protocol. Modem Host Interface (MHI) is a
communication protocol used by the host processors to control
and communicate with modem devices over a high speed peripheral
bus or shared memory.
tristate "Modem Host Interface (MHI) bus"
help
Bus driver for MHI protocol. Modem Host Interface (MHI) is a
communication protocol used by the host processors to control
and communicate with modem devices over a high speed peripheral
bus or shared memory.
config MHI_BUS_DEBUG
bool "Debugfs support for the MHI bus"
depends on MHI_BUS && DEBUG_FS
help
Enable debugfs support for use with the MHI transport. Allows
reading and/or modifying some values within the MHI controller
for debug and test purposes.

View File

@ -1,3 +1,4 @@
obj-$(CONFIG_MHI_BUS) := mhi.o
obj-$(CONFIG_MHI_BUS) += mhi.o
mhi-y := init.o main.o pm.o boot.o
mhi-$(CONFIG_MHI_BUS_DEBUG) += debugfs.o

View File

@ -392,13 +392,28 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
void *buf;
dma_addr_t dma_addr;
size_t size;
int ret;
int i, ret;
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
dev_err(dev, "Device MHI is not in valid state\n");
return;
}
/* save hardware info from BHI */
ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_SERIALNU,
&mhi_cntrl->serial_number);
if (ret)
dev_err(dev, "Could not capture serial number via BHI\n");
for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++) {
ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_OEMPKHASH(i),
&mhi_cntrl->oem_pk_hash[i]);
if (ret) {
dev_err(dev, "Could not capture OEM PK HASH via BHI\n");
break;
}
}
/* If device is in pass through, do reset to ready state transition */
if (mhi_cntrl->ee == MHI_EE_PTHRU)
goto fw_load_ee_pthru;

View File

@ -0,0 +1,411 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*
*/
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/mhi.h>
#include <linux/module.h>
#include "internal.h"
static int mhi_debugfs_states_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
/* states */
seq_printf(m, "PM state: %s Device: %s MHI state: %s EE: %s wake: %s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
mhi_is_active(mhi_cntrl) ? "Active" : "Inactive",
TO_MHI_STATE_STR(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee),
mhi_cntrl->wake_set ? "true" : "false");
/* counters */
seq_printf(m, "M0: %u M2: %u M3: %u", mhi_cntrl->M0, mhi_cntrl->M2,
mhi_cntrl->M3);
seq_printf(m, " device wake: %u pending packets: %u\n",
atomic_read(&mhi_cntrl->dev_wake),
atomic_read(&mhi_cntrl->pending_pkts));
return 0;
}
static int mhi_debugfs_events_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
struct mhi_event *mhi_event;
struct mhi_event_ctxt *er_ctxt;
int i;
if (!mhi_is_active(mhi_cntrl)) {
seq_puts(m, "Device not ready\n");
return -ENODEV;
}
er_ctxt = mhi_cntrl->mhi_ctxt->er_ctxt;
mhi_event = mhi_cntrl->mhi_event;
for (i = 0; i < mhi_cntrl->total_ev_rings;
i++, er_ctxt++, mhi_event++) {
struct mhi_ring *ring = &mhi_event->ring;
if (mhi_event->offload_ev) {
seq_printf(m, "Index: %d is an offload event ring\n",
i);
continue;
}
seq_printf(m, "Index: %d intmod count: %lu time: %lu",
i, (er_ctxt->intmod & EV_CTX_INTMODC_MASK) >>
EV_CTX_INTMODC_SHIFT,
(er_ctxt->intmod & EV_CTX_INTMODT_MASK) >>
EV_CTX_INTMODT_SHIFT);
seq_printf(m, " base: 0x%0llx len: 0x%llx", er_ctxt->rbase,
er_ctxt->rlen);
seq_printf(m, " rp: 0x%llx wp: 0x%llx", er_ctxt->rp,
er_ctxt->wp);
seq_printf(m, " local rp: 0x%pK db: 0x%pad\n", ring->rp,
&mhi_event->db_cfg.db_val);
}
return 0;
}
static int mhi_debugfs_channels_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
struct mhi_chan *mhi_chan;
struct mhi_chan_ctxt *chan_ctxt;
int i;
if (!mhi_is_active(mhi_cntrl)) {
seq_puts(m, "Device not ready\n");
return -ENODEV;
}
mhi_chan = mhi_cntrl->mhi_chan;
chan_ctxt = mhi_cntrl->mhi_ctxt->chan_ctxt;
for (i = 0; i < mhi_cntrl->max_chan; i++, chan_ctxt++, mhi_chan++) {
struct mhi_ring *ring = &mhi_chan->tre_ring;
if (mhi_chan->offload_ch) {
seq_printf(m, "%s(%u) is an offload channel\n",
mhi_chan->name, mhi_chan->chan);
continue;
}
if (!mhi_chan->mhi_dev)
continue;
seq_printf(m,
"%s(%u) state: 0x%lx brstmode: 0x%lx pollcfg: 0x%lx",
mhi_chan->name, mhi_chan->chan, (chan_ctxt->chcfg &
CHAN_CTX_CHSTATE_MASK) >> CHAN_CTX_CHSTATE_SHIFT,
(chan_ctxt->chcfg & CHAN_CTX_BRSTMODE_MASK) >>
CHAN_CTX_BRSTMODE_SHIFT, (chan_ctxt->chcfg &
CHAN_CTX_POLLCFG_MASK) >> CHAN_CTX_POLLCFG_SHIFT);
seq_printf(m, " type: 0x%x event ring: %u", chan_ctxt->chtype,
chan_ctxt->erindex);
seq_printf(m, " base: 0x%llx len: 0x%llx rp: 0x%llx wp: 0x%llx",
chan_ctxt->rbase, chan_ctxt->rlen, chan_ctxt->rp,
chan_ctxt->wp);
seq_printf(m, " local rp: 0x%pK local wp: 0x%pK db: 0x%pad\n",
ring->rp, ring->wp,
&mhi_chan->db_cfg.db_val);
}
return 0;
}
static int mhi_device_info_show(struct device *dev, void *data)
{
struct mhi_device *mhi_dev;
if (dev->bus != &mhi_bus_type)
return 0;
mhi_dev = to_mhi_device(dev);
seq_printf((struct seq_file *)data, "%s: type: %s dev_wake: %u",
mhi_dev->name, mhi_dev->dev_type ? "Controller" : "Transfer",
mhi_dev->dev_wake);
/* for transfer device types only */
if (mhi_dev->dev_type == MHI_DEVICE_XFER)
seq_printf((struct seq_file *)data, " channels: %u(UL)/%u(DL)",
mhi_dev->ul_chan_id, mhi_dev->dl_chan_id);
seq_puts((struct seq_file *)data, "\n");
return 0;
}
static int mhi_debugfs_devices_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
if (!mhi_is_active(mhi_cntrl)) {
seq_puts(m, "Device not ready\n");
return -ENODEV;
}
device_for_each_child(mhi_cntrl->cntrl_dev, m, mhi_device_info_show);
return 0;
}
static int mhi_debugfs_regdump_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
enum mhi_state state;
enum mhi_ee_type ee;
int i, ret = -EIO;
u32 val;
void __iomem *mhi_base = mhi_cntrl->regs;
void __iomem *bhi_base = mhi_cntrl->bhi;
void __iomem *bhie_base = mhi_cntrl->bhie;
void __iomem *wake_db = mhi_cntrl->wake_db;
struct {
const char *name;
int offset;
void __iomem *base;
} regs[] = {
{ "MHI_REGLEN", MHIREGLEN, mhi_base},
{ "MHI_VER", MHIVER, mhi_base},
{ "MHI_CFG", MHICFG, mhi_base},
{ "MHI_CTRL", MHICTRL, mhi_base},
{ "MHI_STATUS", MHISTATUS, mhi_base},
{ "MHI_WAKE_DB", 0, wake_db},
{ "BHI_EXECENV", BHI_EXECENV, bhi_base},
{ "BHI_STATUS", BHI_STATUS, bhi_base},
{ "BHI_ERRCODE", BHI_ERRCODE, bhi_base},
{ "BHI_ERRDBG1", BHI_ERRDBG1, bhi_base},
{ "BHI_ERRDBG2", BHI_ERRDBG2, bhi_base},
{ "BHI_ERRDBG3", BHI_ERRDBG3, bhi_base},
{ "BHIE_TXVEC_DB", BHIE_TXVECDB_OFFS, bhie_base},
{ "BHIE_TXVEC_STATUS", BHIE_TXVECSTATUS_OFFS, bhie_base},
{ "BHIE_RXVEC_DB", BHIE_RXVECDB_OFFS, bhie_base},
{ "BHIE_RXVEC_STATUS", BHIE_RXVECSTATUS_OFFS, bhie_base},
{ NULL },
};
if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state))
return ret;
seq_printf(m, "Host PM state: %s Device state: %s EE: %s\n",
to_mhi_pm_state_str(mhi_cntrl->pm_state),
TO_MHI_STATE_STR(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee));
state = mhi_get_mhi_state(mhi_cntrl);
ee = mhi_get_exec_env(mhi_cntrl);
seq_printf(m, "Device EE: %s state: %s\n", TO_MHI_EXEC_STR(ee),
TO_MHI_STATE_STR(state));
for (i = 0; regs[i].name; i++) {
if (!regs[i].base)
continue;
ret = mhi_read_reg(mhi_cntrl, regs[i].base, regs[i].offset,
&val);
if (ret)
continue;
seq_printf(m, "%s: 0x%x\n", regs[i].name, val);
}
return 0;
}
static int mhi_debugfs_device_wake_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev;
if (!mhi_is_active(mhi_cntrl)) {
seq_puts(m, "Device not ready\n");
return -ENODEV;
}
seq_printf(m,
"Wake count: %d\n%s\n", mhi_dev->dev_wake,
"Usage: echo get/put > device_wake to vote/unvote for M0");
return 0;
}
static ssize_t mhi_debugfs_device_wake_write(struct file *file,
const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct seq_file *m = file->private_data;
struct mhi_controller *mhi_cntrl = m->private;
struct mhi_device *mhi_dev = mhi_cntrl->mhi_dev;
char buf[16];
int ret = -EINVAL;
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
return -EFAULT;
if (!strncmp(buf, "get", 3)) {
ret = mhi_device_get_sync(mhi_dev);
} else if (!strncmp(buf, "put", 3)) {
mhi_device_put(mhi_dev);
ret = 0;
}
return ret ? ret : count;
}
static int mhi_debugfs_timeout_ms_show(struct seq_file *m, void *d)
{
struct mhi_controller *mhi_cntrl = m->private;
seq_printf(m, "%u ms\n", mhi_cntrl->timeout_ms);
return 0;
}
static ssize_t mhi_debugfs_timeout_ms_write(struct file *file,
const char __user *ubuf,
size_t count, loff_t *ppos)
{
struct seq_file *m = file->private_data;
struct mhi_controller *mhi_cntrl = m->private;
u32 timeout_ms;
if (kstrtou32_from_user(ubuf, count, 0, &timeout_ms))
return -EINVAL;
mhi_cntrl->timeout_ms = timeout_ms;
return count;
}
static int mhi_debugfs_states_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_states_show, inode->i_private);
}
static int mhi_debugfs_events_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_events_show, inode->i_private);
}
static int mhi_debugfs_channels_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_channels_show, inode->i_private);
}
static int mhi_debugfs_devices_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_devices_show, inode->i_private);
}
static int mhi_debugfs_regdump_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_regdump_show, inode->i_private);
}
static int mhi_debugfs_device_wake_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_device_wake_show, inode->i_private);
}
static int mhi_debugfs_timeout_ms_open(struct inode *inode, struct file *fp)
{
return single_open(fp, mhi_debugfs_timeout_ms_show, inode->i_private);
}
static const struct file_operations debugfs_states_fops = {
.open = mhi_debugfs_states_open,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_events_fops = {
.open = mhi_debugfs_events_open,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_channels_fops = {
.open = mhi_debugfs_channels_open,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_devices_fops = {
.open = mhi_debugfs_devices_open,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_regdump_fops = {
.open = mhi_debugfs_regdump_open,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_device_wake_fops = {
.open = mhi_debugfs_device_wake_open,
.write = mhi_debugfs_device_wake_write,
.release = single_release,
.read = seq_read,
};
static const struct file_operations debugfs_timeout_ms_fops = {
.open = mhi_debugfs_timeout_ms_open,
.write = mhi_debugfs_timeout_ms_write,
.release = single_release,
.read = seq_read,
};
static struct dentry *mhi_debugfs_root;
void mhi_create_debugfs(struct mhi_controller *mhi_cntrl)
{
mhi_cntrl->debugfs_dentry =
debugfs_create_dir(dev_name(mhi_cntrl->cntrl_dev),
mhi_debugfs_root);
debugfs_create_file("states", 0444, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_states_fops);
debugfs_create_file("events", 0444, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_events_fops);
debugfs_create_file("channels", 0444, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_channels_fops);
debugfs_create_file("devices", 0444, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_devices_fops);
debugfs_create_file("regdump", 0444, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_regdump_fops);
debugfs_create_file("device_wake", 0644, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_device_wake_fops);
debugfs_create_file("timeout_ms", 0644, mhi_cntrl->debugfs_dentry,
mhi_cntrl, &debugfs_timeout_ms_fops);
}
void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl)
{
debugfs_remove_recursive(mhi_cntrl->debugfs_dentry);
mhi_cntrl->debugfs_dentry = NULL;
}
void mhi_debugfs_init(void)
{
mhi_debugfs_root = debugfs_create_dir(mhi_bus_type.name, NULL);
}
void mhi_debugfs_exit(void)
{
debugfs_remove_recursive(mhi_debugfs_root);
}

View File

@ -4,6 +4,7 @@
*
*/
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/dma-direction.h>
#include <linux/dma-mapping.h>
@ -75,6 +76,42 @@ const char *to_mhi_pm_state_str(enum mhi_pm_state state)
return mhi_pm_state_str[index];
}
static ssize_t serial_number_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct mhi_device *mhi_dev = to_mhi_device(dev);
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
return snprintf(buf, PAGE_SIZE, "Serial Number: %u\n",
mhi_cntrl->serial_number);
}
static DEVICE_ATTR_RO(serial_number);
static ssize_t oem_pk_hash_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct mhi_device *mhi_dev = to_mhi_device(dev);
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
int i, cnt = 0;
for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++)
cnt += snprintf(buf + cnt, PAGE_SIZE - cnt,
"OEMPKHASH[%d]: 0x%x\n", i,
mhi_cntrl->oem_pk_hash[i]);
return cnt;
}
static DEVICE_ATTR_RO(oem_pk_hash);
static struct attribute *mhi_dev_attrs[] = {
&dev_attr_serial_number.attr,
&dev_attr_oem_pk_hash.attr,
NULL,
};
ATTRIBUTE_GROUPS(mhi_dev);
/* MHI protocol requires the transfer ring to be aligned with ring length */
static int mhi_alloc_aligned_ring(struct mhi_controller *mhi_cntrl,
struct mhi_ring *ring,
@ -125,6 +162,13 @@ int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl)
if (mhi_event->offload_ev)
continue;
if (mhi_event->irq >= mhi_cntrl->nr_irqs) {
dev_err(dev, "irq %d not available for event ring\n",
mhi_event->irq);
ret = -EINVAL;
goto error_request;
}
ret = request_irq(mhi_cntrl->irq[mhi_event->irq],
mhi_irq_handler,
IRQF_SHARED | IRQF_NO_SUSPEND,
@ -562,10 +606,10 @@ int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
}
static int parse_ev_cfg(struct mhi_controller *mhi_cntrl,
struct mhi_controller_config *config)
const struct mhi_controller_config *config)
{
struct mhi_event *mhi_event;
struct mhi_event_config *event_cfg;
const struct mhi_event_config *event_cfg;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
int i, num;
@ -636,9 +680,6 @@ static int parse_ev_cfg(struct mhi_controller *mhi_cntrl,
mhi_event++;
}
/* We need IRQ for each event ring + additional one for BHI */
mhi_cntrl->nr_irqs_req = mhi_cntrl->total_ev_rings + 1;
return 0;
error_ev_cfg:
@ -648,9 +689,9 @@ error_ev_cfg:
}
static int parse_ch_cfg(struct mhi_controller *mhi_cntrl,
struct mhi_controller_config *config)
const struct mhi_controller_config *config)
{
struct mhi_channel_config *ch_cfg;
const struct mhi_channel_config *ch_cfg;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
int i;
u32 chan;
@ -766,7 +807,7 @@ error_chan_cfg:
}
static int parse_config(struct mhi_controller *mhi_cntrl,
struct mhi_controller_config *config)
const struct mhi_controller_config *config)
{
int ret;
@ -803,7 +844,7 @@ error_ev_cfg:
}
int mhi_register_controller(struct mhi_controller *mhi_cntrl,
struct mhi_controller_config *config)
const struct mhi_controller_config *config)
{
struct mhi_event *mhi_event;
struct mhi_chan *mhi_chan;
@ -904,6 +945,7 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
mhi_dev->dev_type = MHI_DEVICE_CONTROLLER;
mhi_dev->mhi_cntrl = mhi_cntrl;
dev_set_name(&mhi_dev->dev, "%s", dev_name(mhi_cntrl->cntrl_dev));
mhi_dev->name = dev_name(mhi_cntrl->cntrl_dev);
/* Init wakeup source */
device_init_wakeup(&mhi_dev->dev, true);
@ -914,6 +956,8 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
mhi_cntrl->mhi_dev = mhi_dev;
mhi_create_debugfs(mhi_cntrl);
return 0;
error_add_dev:
@ -936,6 +980,8 @@ void mhi_unregister_controller(struct mhi_controller *mhi_cntrl)
struct mhi_chan *mhi_chan = mhi_cntrl->mhi_chan;
unsigned int i;
mhi_destroy_debugfs(mhi_cntrl);
kfree(mhi_cntrl->mhi_cmd);
kfree(mhi_cntrl->mhi_event);
@ -953,6 +999,22 @@ void mhi_unregister_controller(struct mhi_controller *mhi_cntrl)
}
EXPORT_SYMBOL_GPL(mhi_unregister_controller);
struct mhi_controller *mhi_alloc_controller(void)
{
struct mhi_controller *mhi_cntrl;
mhi_cntrl = kzalloc(sizeof(*mhi_cntrl), GFP_KERNEL);
return mhi_cntrl;
}
EXPORT_SYMBOL_GPL(mhi_alloc_controller);
void mhi_free_controller(struct mhi_controller *mhi_cntrl)
{
kfree(mhi_cntrl);
}
EXPORT_SYMBOL_GPL(mhi_free_controller);
int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl)
{
struct device *dev = &mhi_cntrl->mhi_dev->dev;
@ -1249,7 +1311,7 @@ static int mhi_uevent(struct device *dev, struct kobj_uevent_env *env)
struct mhi_device *mhi_dev = to_mhi_device(dev);
return add_uevent_var(env, "MODALIAS=" MHI_DEVICE_MODALIAS_FMT,
mhi_dev->chan_name);
mhi_dev->name);
}
static int mhi_match(struct device *dev, struct device_driver *drv)
@ -1266,7 +1328,7 @@ static int mhi_match(struct device *dev, struct device_driver *drv)
return 0;
for (id = mhi_drv->id_table; id->chan[0]; id++)
if (!strcmp(mhi_dev->chan_name, id->chan)) {
if (!strcmp(mhi_dev->name, id->chan)) {
mhi_dev->id = id;
return 1;
}
@ -1279,15 +1341,18 @@ struct bus_type mhi_bus_type = {
.dev_name = "mhi",
.match = mhi_match,
.uevent = mhi_uevent,
.dev_groups = mhi_dev_groups,
};
static int __init mhi_init(void)
{
mhi_debugfs_init();
return bus_register(&mhi_bus_type);
}
static void __exit mhi_exit(void)
{
mhi_debugfs_exit();
bus_unregister(&mhi_bus_type);
}

View File

@ -570,6 +570,30 @@ struct mhi_chan {
/* Default MHI timeout */
#define MHI_TIMEOUT_MS (1000)
/* debugfs related functions */
#ifdef CONFIG_MHI_BUS_DEBUG
void mhi_create_debugfs(struct mhi_controller *mhi_cntrl);
void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl);
void mhi_debugfs_init(void);
void mhi_debugfs_exit(void);
#else
static inline void mhi_create_debugfs(struct mhi_controller *mhi_cntrl)
{
}
static inline void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl)
{
}
static inline void mhi_debugfs_init(void)
{
}
static inline void mhi_debugfs_exit(void)
{
}
#endif
struct mhi_device *mhi_alloc_device(struct mhi_controller *mhi_cntrl);
int mhi_destroy_device(struct device *dev, void *data);
@ -592,13 +616,24 @@ void mhi_pm_st_worker(struct work_struct *work);
void mhi_pm_sys_err_handler(struct mhi_controller *mhi_cntrl);
void mhi_fw_load_worker(struct work_struct *work);
int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl);
void mhi_ctrl_ev_task(unsigned long data);
int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl);
void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl);
int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl);
int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl);
int mhi_send_cmd(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
enum mhi_cmd_type cmd);
static inline bool mhi_is_active(struct mhi_controller *mhi_cntrl)
{
return (mhi_cntrl->dev_state >= MHI_STATE_M0 &&
mhi_cntrl->dev_state <= MHI_STATE_M3_FAST);
}
static inline void mhi_trigger_resume(struct mhi_controller *mhi_cntrl)
{
pm_wakeup_event(&mhi_cntrl->mhi_dev->dev, 0);
mhi_cntrl->runtime_get(mhi_cntrl);
mhi_cntrl->runtime_put(mhi_cntrl);
}
/* Register access methods */
void mhi_db_brstmode(struct mhi_controller *mhi_cntrl, struct db_cfg *db_cfg,

View File

@ -249,7 +249,7 @@ int mhi_destroy_device(struct device *dev, void *data)
put_device(&mhi_dev->dl_chan->mhi_dev->dev);
dev_dbg(&mhi_cntrl->mhi_dev->dev, "destroy device for chan:%s\n",
mhi_dev->chan_name);
mhi_dev->name);
/* Notify the client and remove the device from MHI bus */
device_del(dev);
@ -327,10 +327,10 @@ void mhi_create_devices(struct mhi_controller *mhi_cntrl)
}
/* Channel name is same for both UL and DL */
mhi_dev->chan_name = mhi_chan->name;
mhi_dev->name = mhi_chan->name;
dev_set_name(&mhi_dev->dev, "%s_%s",
dev_name(mhi_cntrl->cntrl_dev),
mhi_dev->chan_name);
mhi_dev->name);
/* Init wakeup source if available */
if (mhi_dev->dl_chan && mhi_dev->dl_chan->wake_capable)
@ -909,8 +909,7 @@ void mhi_ctrl_ev_task(unsigned long data)
* process it since we are probably in a suspended state,
* so trigger a resume.
*/
mhi_cntrl->runtime_get(mhi_cntrl);
mhi_cntrl->runtime_put(mhi_cntrl);
mhi_trigger_resume(mhi_cntrl);
return;
}
@ -971,10 +970,8 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
}
/* we're in M3 or transitioning to M3 */
if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) {
mhi_cntrl->runtime_get(mhi_cntrl);
mhi_cntrl->runtime_put(mhi_cntrl);
}
if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
mhi_trigger_resume(mhi_cntrl);
/* Toggle wake to exit out of M2 */
mhi_cntrl->wake_toggle(mhi_cntrl);
@ -1032,10 +1029,8 @@ int mhi_queue_dma(struct mhi_device *mhi_dev, enum dma_data_direction dir,
}
/* we're in M3 or transitioning to M3 */
if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) {
mhi_cntrl->runtime_get(mhi_cntrl);
mhi_cntrl->runtime_put(mhi_cntrl);
}
if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
mhi_trigger_resume(mhi_cntrl);
/* Toggle wake to exit out of M2 */
mhi_cntrl->wake_toggle(mhi_cntrl);
@ -1147,10 +1142,8 @@ int mhi_queue_buf(struct mhi_device *mhi_dev, enum dma_data_direction dir,
read_lock_irqsave(&mhi_cntrl->pm_lock, flags);
/* we're in M3 or transitioning to M3 */
if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) {
mhi_cntrl->runtime_get(mhi_cntrl);
mhi_cntrl->runtime_put(mhi_cntrl);
}
if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
mhi_trigger_resume(mhi_cntrl);
/* Toggle wake to exit out of M2 */
mhi_cntrl->wake_toggle(mhi_cntrl);

View File

@ -256,6 +256,7 @@ int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl)
dev_err(dev, "Unable to transition to M0 state\n");
return -EIO;
}
mhi_cntrl->M0++;
/* Wake up the device */
read_lock_bh(&mhi_cntrl->pm_lock);
@ -326,6 +327,8 @@ void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl)
mhi_cntrl->dev_state = MHI_STATE_M2;
write_unlock_irq(&mhi_cntrl->pm_lock);
mhi_cntrl->M2++;
wake_up_all(&mhi_cntrl->state_event);
/* If there are any pending resources, exit M2 immediately */
@ -362,6 +365,7 @@ int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl)
return -EIO;
}
mhi_cntrl->M3++;
wake_up_all(&mhi_cntrl->state_event);
return 0;
@ -686,7 +690,8 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl)
return -EIO;
/* Return busy if there are any pending resources */
if (atomic_read(&mhi_cntrl->dev_wake))
if (atomic_read(&mhi_cntrl->dev_wake) ||
atomic_read(&mhi_cntrl->pending_pkts))
return -EBUSY;
/* Take MHI out of M2 state */
@ -712,7 +717,8 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl)
write_lock_irq(&mhi_cntrl->pm_lock);
if (atomic_read(&mhi_cntrl->dev_wake)) {
if (atomic_read(&mhi_cntrl->dev_wake) ||
atomic_read(&mhi_cntrl->pending_pkts)) {
write_unlock_irq(&mhi_cntrl->pm_lock);
return -EBUSY;
}
@ -822,11 +828,8 @@ int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl)
/* Wake up the device */
read_lock_bh(&mhi_cntrl->pm_lock);
mhi_cntrl->wake_get(mhi_cntrl, true);
if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) {
pm_wakeup_event(&mhi_cntrl->mhi_dev->dev, 0);
mhi_cntrl->runtime_get(mhi_cntrl);
mhi_cntrl->runtime_put(mhi_cntrl);
}
if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
mhi_trigger_resume(mhi_cntrl);
read_unlock_bh(&mhi_cntrl->pm_lock);
ret = wait_event_timeout(mhi_cntrl->state_event,
@ -915,7 +918,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl)
dev_info(dev, "Requested to power ON\n");
if (mhi_cntrl->nr_irqs < mhi_cntrl->total_ev_rings)
if (mhi_cntrl->nr_irqs < 1)
return -EINVAL;
/* Supply default wake routines if not provided by controller driver */
@ -1113,6 +1116,9 @@ void mhi_device_get(struct mhi_device *mhi_dev)
mhi_dev->dev_wake++;
read_lock_bh(&mhi_cntrl->pm_lock);
if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
mhi_trigger_resume(mhi_cntrl);
mhi_cntrl->wake_get(mhi_cntrl, true);
read_unlock_bh(&mhi_cntrl->pm_lock);
}
@ -1137,10 +1143,8 @@ void mhi_device_put(struct mhi_device *mhi_dev)
mhi_dev->dev_wake--;
read_lock_bh(&mhi_cntrl->pm_lock);
if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) {
mhi_cntrl->runtime_get(mhi_cntrl);
mhi_cntrl->runtime_put(mhi_cntrl);
}
if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state))
mhi_trigger_resume(mhi_cntrl);
mhi_cntrl->wake_put(mhi_cntrl, false);
read_unlock_bh(&mhi_cntrl->pm_lock);

View File

@ -93,8 +93,9 @@ config PPDEV
config VIRTIO_CONSOLE
tristate "Virtio console"
depends on VIRTIO && TTY
depends on TTY
select HVC_DRIVER
select VIRTIO
help
Virtio console for use with hypervisors.

View File

@ -853,8 +853,10 @@ static void lp_console_write(struct console *co, const char *s,
count--;
do {
written = parport_write(port, crlf, i);
if (written > 0)
i -= written, crlf += written;
if (written > 0) {
i -= written;
crlf += written;
}
} while (i > 0 && (CONSOLE_LP_STRICT || written > 0));
}
} while (count > 0 && (CONSOLE_LP_STRICT || written > 0));

View File

@ -726,6 +726,33 @@ static ssize_t read_iter_zero(struct kiocb *iocb, struct iov_iter *iter)
return written;
}
static ssize_t read_zero(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
size_t cleared = 0;
while (count) {
size_t chunk = min_t(size_t, count, PAGE_SIZE);
size_t left;
left = clear_user(buf + cleared, chunk);
if (unlikely(left)) {
cleared += (chunk - left);
if (!cleared)
return -EFAULT;
break;
}
cleared += chunk;
count -= chunk;
if (signal_pending(current))
break;
cond_resched();
}
return cleared;
}
static int mmap_zero(struct file *file, struct vm_area_struct *vma)
{
#ifndef CONFIG_MMU
@ -921,6 +948,7 @@ static const struct file_operations zero_fops = {
.llseek = zero_lseek,
.write = write_zero,
.read_iter = read_iter_zero,
.read = read_zero,
.write_iter = write_iter_zero,
.mmap = mmap_zero,
.get_unmapped_area = get_unmapped_area_zero,

View File

@ -195,10 +195,7 @@ mspec_mmap(struct file *file, struct vm_area_struct *vma,
pages = vma_pages(vma);
vdata_size = sizeof(struct vma_data) + pages * sizeof(long);
if (vdata_size <= PAGE_SIZE)
vdata = kzalloc(vdata_size, GFP_KERNEL);
else
vdata = vzalloc(vdata_size);
vdata = kvzalloc(vdata_size, GFP_KERNEL);
if (!vdata)
return -ENOMEM;

View File

@ -491,18 +491,7 @@ static struct platform_driver axp288_extcon_driver = {
.pm = &axp288_extcon_pm_ops,
},
};
static int __init axp288_extcon_init(void)
{
return platform_driver_register(&axp288_extcon_driver);
}
module_init(axp288_extcon_init);
static void __exit axp288_extcon_exit(void)
{
platform_driver_unregister(&axp288_extcon_driver);
}
module_exit(axp288_extcon_exit);
module_platform_driver(axp288_extcon_driver);
MODULE_AUTHOR("Ramakrishna Pallala <ramakrishna.pallala@intel.com>");
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");

View File

@ -713,7 +713,7 @@ static int max14577_muic_probe(struct platform_device *pdev)
max14577_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
return -ENOMEM;
return PTR_ERR(info->edev);
}
ret = devm_extcon_dev_register(&pdev->dev, info->edev);

View File

@ -1157,7 +1157,7 @@ static int max77693_muic_probe(struct platform_device *pdev)
max77693_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
return -ENOMEM;
return PTR_ERR(info->edev);
}
ret = devm_extcon_dev_register(&pdev->dev, info->edev);

View File

@ -845,7 +845,7 @@ static int max77843_muic_probe(struct platform_device *pdev)
max77843_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "Failed to allocate memory for extcon\n");
ret = -ENODEV;
ret = PTR_ERR(info->edev);
goto err_muic_irq;
}

View File

@ -674,7 +674,7 @@ static int max8997_muic_probe(struct platform_device *pdev)
info->edev = devm_extcon_dev_allocate(&pdev->dev, max8997_extcon_cable);
if (IS_ERR(info->edev)) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
ret = -ENOMEM;
ret = PTR_ERR(info->edev);
goto err_irq;
}

View File

@ -2,7 +2,7 @@
/*
* Palmas USB transceiver driver
*
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
* Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com
* Author: Graeme Gregory <gg@slimlogic.co.uk>
* Author: Kishon Vijay Abraham I <kishon@ti.com>
* Based on twl6030_usb.c
@ -205,21 +205,15 @@ static int palmas_usb_probe(struct platform_device *pdev)
palmas_usb->id_gpiod = devm_gpiod_get_optional(&pdev->dev, "id",
GPIOD_IN);
if (PTR_ERR(palmas_usb->id_gpiod) == -EPROBE_DEFER) {
return -EPROBE_DEFER;
} else if (IS_ERR(palmas_usb->id_gpiod)) {
dev_err(&pdev->dev, "failed to get id gpio\n");
return PTR_ERR(palmas_usb->id_gpiod);
}
if (IS_ERR(palmas_usb->id_gpiod))
return dev_err_probe(&pdev->dev, PTR_ERR(palmas_usb->id_gpiod),
"failed to get id gpio\n");
palmas_usb->vbus_gpiod = devm_gpiod_get_optional(&pdev->dev, "vbus",
GPIOD_IN);
if (PTR_ERR(palmas_usb->vbus_gpiod) == -EPROBE_DEFER) {
return -EPROBE_DEFER;
} else if (IS_ERR(palmas_usb->vbus_gpiod)) {
dev_err(&pdev->dev, "failed to get vbus gpio\n");
return PTR_ERR(palmas_usb->vbus_gpiod);
}
if (IS_ERR(palmas_usb->vbus_gpiod))
return dev_err_probe(&pdev->dev, PTR_ERR(palmas_usb->vbus_gpiod),
"failed to get id gpio\n");
if (palmas_usb->enable_id_detection && palmas_usb->id_gpiod) {
palmas_usb->enable_id_detection = false;

View File

@ -5,7 +5,9 @@
// Based on extcon-sm5502.c driver
// Copyright (c) 2018-2019 by Vijai Kumar K
// Author: Vijai Kumar K <vijaikumar.kanagarajan@gmail.com>
// Copyright (c) 2020 Krzysztof Kozlowski <krzk@kernel.org>
#include <linux/bitfield.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
@ -17,46 +19,28 @@
#include <linux/gpio/consumer.h>
/* PTN5150 registers */
enum ptn5150_reg {
PTN5150_REG_DEVICE_ID = 0x01,
PTN5150_REG_CONTROL,
PTN5150_REG_INT_STATUS,
PTN5150_REG_CC_STATUS,
PTN5150_REG_CON_DET = 0x09,
PTN5150_REG_VCONN_STATUS,
PTN5150_REG_RESET,
PTN5150_REG_INT_MASK = 0x18,
PTN5150_REG_INT_REG_STATUS,
PTN5150_REG_END,
};
#define PTN5150_REG_DEVICE_ID 0x01
#define PTN5150_REG_CONTROL 0x02
#define PTN5150_REG_INT_STATUS 0x03
#define PTN5150_REG_CC_STATUS 0x04
#define PTN5150_REG_CON_DET 0x09
#define PTN5150_REG_VCONN_STATUS 0x0a
#define PTN5150_REG_RESET 0x0b
#define PTN5150_REG_INT_MASK 0x18
#define PTN5150_REG_INT_REG_STATUS 0x19
#define PTN5150_REG_END PTN5150_REG_INT_REG_STATUS
#define PTN5150_DFP_ATTACHED 0x1
#define PTN5150_UFP_ATTACHED 0x2
/* Define PTN5150 MASK/SHIFT constant */
#define PTN5150_REG_DEVICE_ID_VENDOR_SHIFT 0
#define PTN5150_REG_DEVICE_ID_VENDOR_MASK \
(0x3 << PTN5150_REG_DEVICE_ID_VENDOR_SHIFT)
#define PTN5150_REG_DEVICE_ID_VERSION GENMASK(7, 3)
#define PTN5150_REG_DEVICE_ID_VENDOR GENMASK(2, 0)
#define PTN5150_REG_DEVICE_ID_VERSION_SHIFT 3
#define PTN5150_REG_DEVICE_ID_VERSION_MASK \
(0x1f << PTN5150_REG_DEVICE_ID_VERSION_SHIFT)
#define PTN5150_REG_CC_PORT_ATTACHMENT_SHIFT 2
#define PTN5150_REG_CC_PORT_ATTACHMENT_MASK \
(0x7 << PTN5150_REG_CC_PORT_ATTACHMENT_SHIFT)
#define PTN5150_REG_CC_VBUS_DETECTION_SHIFT 7
#define PTN5150_REG_CC_VBUS_DETECTION_MASK \
(0x1 << PTN5150_REG_CC_VBUS_DETECTION_SHIFT)
#define PTN5150_REG_INT_CABLE_ATTACH_SHIFT 0
#define PTN5150_REG_INT_CABLE_ATTACH_MASK \
(0x1 << PTN5150_REG_INT_CABLE_ATTACH_SHIFT)
#define PTN5150_REG_INT_CABLE_DETACH_SHIFT 1
#define PTN5150_REG_INT_CABLE_DETACH_MASK \
(0x1 << PTN5150_REG_CC_CABLE_DETACH_SHIFT)
#define PTN5150_REG_CC_PORT_ATTACHMENT GENMASK(4, 2)
#define PTN5150_REG_CC_VBUS_DETECTION BIT(7)
#define PTN5150_REG_INT_CABLE_ATTACH_MASK BIT(0)
#define PTN5150_REG_INT_CABLE_DETACH_MASK BIT(1)
struct ptn5150_info {
struct device *dev;
@ -83,12 +67,45 @@ static const struct regmap_config ptn5150_regmap_config = {
.max_register = PTN5150_REG_END,
};
static void ptn5150_check_state(struct ptn5150_info *info)
{
unsigned int port_status, reg_data, vbus;
int ret;
ret = regmap_read(info->regmap, PTN5150_REG_CC_STATUS, &reg_data);
if (ret) {
dev_err(info->dev, "failed to read CC STATUS %d\n", ret);
return;
}
port_status = FIELD_GET(PTN5150_REG_CC_PORT_ATTACHMENT, reg_data);
switch (port_status) {
case PTN5150_DFP_ATTACHED:
extcon_set_state_sync(info->edev, EXTCON_USB_HOST, false);
gpiod_set_value_cansleep(info->vbus_gpiod, 0);
extcon_set_state_sync(info->edev, EXTCON_USB, true);
break;
case PTN5150_UFP_ATTACHED:
extcon_set_state_sync(info->edev, EXTCON_USB, false);
vbus = FIELD_GET(PTN5150_REG_CC_VBUS_DETECTION, reg_data);
if (vbus)
gpiod_set_value_cansleep(info->vbus_gpiod, 0);
else
gpiod_set_value_cansleep(info->vbus_gpiod, 1);
extcon_set_state_sync(info->edev, EXTCON_USB_HOST, true);
break;
default:
break;
}
}
static void ptn5150_irq_work(struct work_struct *work)
{
struct ptn5150_info *info = container_of(work,
struct ptn5150_info, irq_work);
int ret = 0;
unsigned int reg_data;
unsigned int int_status;
if (!info->edev)
@ -96,13 +113,6 @@ static void ptn5150_irq_work(struct work_struct *work)
mutex_lock(&info->mutex);
ret = regmap_read(info->regmap, PTN5150_REG_CC_STATUS, &reg_data);
if (ret) {
dev_err(info->dev, "failed to read CC STATUS %d\n", ret);
mutex_unlock(&info->mutex);
return;
}
/* Clear interrupt. Read would clear the register */
ret = regmap_read(info->regmap, PTN5150_REG_INT_STATUS, &int_status);
if (ret) {
@ -116,47 +126,13 @@ static void ptn5150_irq_work(struct work_struct *work)
cable_attach = int_status & PTN5150_REG_INT_CABLE_ATTACH_MASK;
if (cable_attach) {
unsigned int port_status;
unsigned int vbus;
port_status = ((reg_data &
PTN5150_REG_CC_PORT_ATTACHMENT_MASK) >>
PTN5150_REG_CC_PORT_ATTACHMENT_SHIFT);
switch (port_status) {
case PTN5150_DFP_ATTACHED:
extcon_set_state_sync(info->edev,
EXTCON_USB_HOST, false);
gpiod_set_value(info->vbus_gpiod, 0);
extcon_set_state_sync(info->edev, EXTCON_USB,
true);
break;
case PTN5150_UFP_ATTACHED:
extcon_set_state_sync(info->edev, EXTCON_USB,
false);
vbus = ((reg_data &
PTN5150_REG_CC_VBUS_DETECTION_MASK) >>
PTN5150_REG_CC_VBUS_DETECTION_SHIFT);
if (vbus)
gpiod_set_value(info->vbus_gpiod, 0);
else
gpiod_set_value(info->vbus_gpiod, 1);
extcon_set_state_sync(info->edev,
EXTCON_USB_HOST, true);
break;
default:
dev_err(info->dev,
"Unknown Port status : %x\n",
port_status);
break;
}
ptn5150_check_state(info);
} else {
extcon_set_state_sync(info->edev,
EXTCON_USB_HOST, false);
extcon_set_state_sync(info->edev,
EXTCON_USB, false);
gpiod_set_value(info->vbus_gpiod, 0);
gpiod_set_value_cansleep(info->vbus_gpiod, 0);
}
}
@ -194,13 +170,10 @@ static int ptn5150_init_dev_type(struct ptn5150_info *info)
return -EINVAL;
}
vendor_id = ((reg_data & PTN5150_REG_DEVICE_ID_VENDOR_MASK) >>
PTN5150_REG_DEVICE_ID_VENDOR_SHIFT);
version_id = ((reg_data & PTN5150_REG_DEVICE_ID_VERSION_MASK) >>
PTN5150_REG_DEVICE_ID_VERSION_SHIFT);
dev_info(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n",
version_id, vendor_id);
vendor_id = FIELD_GET(PTN5150_REG_DEVICE_ID_VENDOR, reg_data);
version_id = FIELD_GET(PTN5150_REG_DEVICE_ID_VERSION, reg_data);
dev_dbg(info->dev, "Device type: version: 0x%x, vendor: 0x%x\n",
version_id, vendor_id);
/* Clear any existing interrupts */
ret = regmap_read(info->regmap, PTN5150_REG_INT_STATUS, &reg_data);
@ -221,8 +194,7 @@ static int ptn5150_init_dev_type(struct ptn5150_info *info)
return 0;
}
static int ptn5150_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
static int ptn5150_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct device_node *np = i2c->dev.of_node;
@ -239,20 +211,15 @@ static int ptn5150_i2c_probe(struct i2c_client *i2c,
info->dev = &i2c->dev;
info->i2c = i2c;
info->int_gpiod = devm_gpiod_get(&i2c->dev, "int", GPIOD_IN);
if (IS_ERR(info->int_gpiod)) {
dev_err(dev, "failed to get INT GPIO\n");
return PTR_ERR(info->int_gpiod);
}
info->vbus_gpiod = devm_gpiod_get(&i2c->dev, "vbus", GPIOD_IN);
info->vbus_gpiod = devm_gpiod_get(&i2c->dev, "vbus", GPIOD_OUT_LOW);
if (IS_ERR(info->vbus_gpiod)) {
dev_err(dev, "failed to get VBUS GPIO\n");
return PTR_ERR(info->vbus_gpiod);
}
ret = gpiod_direction_output(info->vbus_gpiod, 0);
if (ret) {
dev_err(dev, "failed to set VBUS GPIO direction\n");
return -EINVAL;
ret = PTR_ERR(info->vbus_gpiod);
if (ret == -ENOENT) {
dev_info(dev, "No VBUS GPIO, ignoring VBUS control\n");
info->vbus_gpiod = NULL;
} else {
return dev_err_probe(dev, ret, "failed to get VBUS GPIO\n");
}
}
mutex_init(&info->mutex);
@ -261,28 +228,34 @@ static int ptn5150_i2c_probe(struct i2c_client *i2c,
info->regmap = devm_regmap_init_i2c(i2c, &ptn5150_regmap_config);
if (IS_ERR(info->regmap)) {
ret = PTR_ERR(info->regmap);
dev_err(info->dev, "failed to allocate register map: %d\n",
ret);
return ret;
return dev_err_probe(info->dev, PTR_ERR(info->regmap),
"failed to allocate register map\n");
}
if (info->int_gpiod) {
if (i2c->irq > 0) {
info->irq = i2c->irq;
} else {
info->int_gpiod = devm_gpiod_get(&i2c->dev, "int", GPIOD_IN);
if (IS_ERR(info->int_gpiod)) {
return dev_err_probe(dev, PTR_ERR(info->int_gpiod),
"failed to get INT GPIO\n");
}
info->irq = gpiod_to_irq(info->int_gpiod);
if (info->irq < 0) {
dev_err(dev, "failed to get INTB IRQ\n");
return info->irq;
}
}
ret = devm_request_threaded_irq(dev, info->irq, NULL,
ptn5150_irq_handler,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
i2c->name, info);
if (ret < 0) {
dev_err(dev, "failed to request handler for INTB IRQ\n");
return ret;
}
ret = devm_request_threaded_irq(dev, info->irq, NULL,
ptn5150_irq_handler,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
i2c->name, info);
if (ret < 0) {
dev_err(dev, "failed to request handler for INTB IRQ\n");
return ret;
}
/* Allocate extcon device */
@ -299,11 +272,26 @@ static int ptn5150_i2c_probe(struct i2c_client *i2c,
return ret;
}
extcon_set_property_capability(info->edev, EXTCON_USB,
EXTCON_PROP_USB_VBUS);
extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
EXTCON_PROP_USB_VBUS);
extcon_set_property_capability(info->edev, EXTCON_USB_HOST,
EXTCON_PROP_USB_TYPEC_POLARITY);
/* Initialize PTN5150 device and print vendor id and version id */
ret = ptn5150_init_dev_type(info);
if (ret)
return -EINVAL;
/*
* Update current extcon state if for example OTG connection was there
* before the probe
*/
mutex_lock(&info->mutex);
ptn5150_check_state(info);
mutex_unlock(&info->mutex);
return 0;
}
@ -324,16 +312,12 @@ static struct i2c_driver ptn5150_i2c_driver = {
.name = "ptn5150",
.of_match_table = ptn5150_dt_match,
},
.probe = ptn5150_i2c_probe,
.probe_new = ptn5150_i2c_probe,
.id_table = ptn5150_i2c_id,
};
static int __init ptn5150_i2c_init(void)
{
return i2c_add_driver(&ptn5150_i2c_driver);
}
subsys_initcall(ptn5150_i2c_init);
module_i2c_driver(ptn5150_i2c_driver);
MODULE_DESCRIPTION("NXP PTN5150 CC logic Extcon driver");
MODULE_AUTHOR("Vijai Kumar K <vijaikumar.kanagarajan@gmail.com>");
MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
MODULE_LICENSE("GPL v2");

View File

@ -2,7 +2,7 @@
/**
* drivers/extcon/extcon-usb-gpio.c - USB GPIO extcon driver
*
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
* Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com
* Author: Roger Quadros <rogerq@ti.com>
*/

View File

@ -148,7 +148,7 @@ struct fme_perf_priv {
struct device *dev;
void __iomem *ioaddr;
struct pmu pmu;
u64 id;
u16 id;
u32 fab_users;
u32 fab_port_id;

View File

@ -31,12 +31,12 @@ struct cci_drvdata {
struct dfl_fpga_cdev *cdev; /* container device */
};
static void __iomem *cci_pci_ioremap_bar(struct pci_dev *pcidev, int bar)
static void __iomem *cci_pci_ioremap_bar0(struct pci_dev *pcidev)
{
if (pcim_iomap_regions(pcidev, BIT(bar), DRV_NAME))
if (pcim_iomap_regions(pcidev, BIT(0), DRV_NAME))
return NULL;
return pcim_iomap_table(pcidev)[bar];
return pcim_iomap_table(pcidev)[0];
}
static int cci_pci_alloc_irq(struct pci_dev *pcidev)
@ -156,8 +156,8 @@ static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
goto irq_free_exit;
}
/* start to find Device Feature List from Bar 0 */
base = cci_pci_ioremap_bar(pcidev, 0);
/* start to find Device Feature List in Bar 0 */
base = cci_pci_ioremap_bar0(pcidev);
if (!base) {
ret = -ENOMEM;
goto irq_free_exit;
@ -172,7 +172,7 @@ static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
start = pci_resource_start(pcidev, 0);
len = pci_resource_len(pcidev, 0);
dfl_fpga_enum_info_add_dfl(info, start, len, base);
dfl_fpga_enum_info_add_dfl(info, start, len);
/*
* find more Device Feature Lists (e.g. Ports) per information
@ -196,26 +196,24 @@ static int cci_enumerate_feature_devs(struct pci_dev *pcidev)
*/
bar = FIELD_GET(FME_PORT_OFST_BAR_ID, v);
offset = FIELD_GET(FME_PORT_OFST_DFH_OFST, v);
base = cci_pci_ioremap_bar(pcidev, bar);
if (!base)
continue;
start = pci_resource_start(pcidev, bar) + offset;
len = pci_resource_len(pcidev, bar) - offset;
dfl_fpga_enum_info_add_dfl(info, start, len,
base + offset);
dfl_fpga_enum_info_add_dfl(info, start, len);
}
} else if (dfl_feature_is_port(base)) {
start = pci_resource_start(pcidev, 0);
len = pci_resource_len(pcidev, 0);
dfl_fpga_enum_info_add_dfl(info, start, len, base);
dfl_fpga_enum_info_add_dfl(info, start, len);
} else {
ret = -ENODEV;
goto irq_free_exit;
}
/* release I/O mappings for next step enumeration */
pcim_iounmap_regions(pcidev, BIT(0));
/* start enumeration with prepared enumeration information */
cdev = dfl_fpga_feature_devs_enumerate(info);
if (IS_ERR(cdev)) {

View File

@ -30,12 +30,6 @@ static DEFINE_MUTEX(dfl_id_mutex);
* index to dfl_chardevs table. If no chardev support just set devt_type
* as one invalid index (DFL_FPGA_DEVT_MAX).
*/
enum dfl_id_type {
FME_ID, /* fme id allocation and mapping */
PORT_ID, /* port id allocation and mapping */
DFL_ID_MAX,
};
enum dfl_fpga_devt_type {
DFL_FPGA_DEVT_FME,
DFL_FPGA_DEVT_PORT,
@ -58,7 +52,7 @@ static const char *dfl_pdata_key_strings[DFL_ID_MAX] = {
*/
struct dfl_dev_info {
const char *name;
u32 dfh_id;
u16 dfh_id;
struct idr id;
enum dfl_fpga_devt_type devt_type;
};
@ -134,7 +128,7 @@ static enum dfl_id_type feature_dev_id_type(struct platform_device *pdev)
return DFL_ID_MAX;
}
static enum dfl_id_type dfh_id_to_type(u32 id)
static enum dfl_id_type dfh_id_to_type(u16 id)
{
int i;
@ -250,6 +244,249 @@ int dfl_fpga_check_port_id(struct platform_device *pdev, void *pport_id)
}
EXPORT_SYMBOL_GPL(dfl_fpga_check_port_id);
static DEFINE_IDA(dfl_device_ida);
static const struct dfl_device_id *
dfl_match_one_device(const struct dfl_device_id *id, struct dfl_device *ddev)
{
if (id->type == ddev->type && id->feature_id == ddev->feature_id)
return id;
return NULL;
}
static int dfl_bus_match(struct device *dev, struct device_driver *drv)
{
struct dfl_device *ddev = to_dfl_dev(dev);
struct dfl_driver *ddrv = to_dfl_drv(drv);
const struct dfl_device_id *id_entry;
id_entry = ddrv->id_table;
if (id_entry) {
while (id_entry->feature_id) {
if (dfl_match_one_device(id_entry, ddev)) {
ddev->id_entry = id_entry;
return 1;
}
id_entry++;
}
}
return 0;
}
static int dfl_bus_probe(struct device *dev)
{
struct dfl_driver *ddrv = to_dfl_drv(dev->driver);
struct dfl_device *ddev = to_dfl_dev(dev);
return ddrv->probe(ddev);
}
static int dfl_bus_remove(struct device *dev)
{
struct dfl_driver *ddrv = to_dfl_drv(dev->driver);
struct dfl_device *ddev = to_dfl_dev(dev);
if (ddrv->remove)
ddrv->remove(ddev);
return 0;
}
static int dfl_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct dfl_device *ddev = to_dfl_dev(dev);
/* The type has 4 valid bits and feature_id has 12 valid bits */
return add_uevent_var(env, "MODALIAS=dfl:t%01Xf%03X",
ddev->type, ddev->feature_id);
}
static ssize_t
type_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dfl_device *ddev = to_dfl_dev(dev);
return sprintf(buf, "0x%x\n", ddev->type);
}
static DEVICE_ATTR_RO(type);
static ssize_t
feature_id_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dfl_device *ddev = to_dfl_dev(dev);
return sprintf(buf, "0x%x\n", ddev->feature_id);
}
static DEVICE_ATTR_RO(feature_id);
static struct attribute *dfl_dev_attrs[] = {
&dev_attr_type.attr,
&dev_attr_feature_id.attr,
NULL,
};
ATTRIBUTE_GROUPS(dfl_dev);
static struct bus_type dfl_bus_type = {
.name = "dfl",
.match = dfl_bus_match,
.probe = dfl_bus_probe,
.remove = dfl_bus_remove,
.uevent = dfl_bus_uevent,
.dev_groups = dfl_dev_groups,
};
static void release_dfl_dev(struct device *dev)
{
struct dfl_device *ddev = to_dfl_dev(dev);
if (ddev->mmio_res.parent)
release_resource(&ddev->mmio_res);
ida_simple_remove(&dfl_device_ida, ddev->id);
kfree(ddev->irqs);
kfree(ddev);
}
static struct dfl_device *
dfl_dev_add(struct dfl_feature_platform_data *pdata,
struct dfl_feature *feature)
{
struct platform_device *pdev = pdata->dev;
struct resource *parent_res;
struct dfl_device *ddev;
int id, i, ret;
ddev = kzalloc(sizeof(*ddev), GFP_KERNEL);
if (!ddev)
return ERR_PTR(-ENOMEM);
id = ida_simple_get(&dfl_device_ida, 0, 0, GFP_KERNEL);
if (id < 0) {
dev_err(&pdev->dev, "unable to get id\n");
kfree(ddev);
return ERR_PTR(id);
}
/* freeing resources by put_device() after device_initialize() */
device_initialize(&ddev->dev);
ddev->dev.parent = &pdev->dev;
ddev->dev.bus = &dfl_bus_type;
ddev->dev.release = release_dfl_dev;
ddev->id = id;
ret = dev_set_name(&ddev->dev, "dfl_dev.%d", id);
if (ret)
goto put_dev;
ddev->type = feature_dev_id_type(pdev);
ddev->feature_id = feature->id;
ddev->cdev = pdata->dfl_cdev;
/* add mmio resource */
parent_res = &pdev->resource[feature->resource_index];
ddev->mmio_res.flags = IORESOURCE_MEM;
ddev->mmio_res.start = parent_res->start;
ddev->mmio_res.end = parent_res->end;
ddev->mmio_res.name = dev_name(&ddev->dev);
ret = insert_resource(parent_res, &ddev->mmio_res);
if (ret) {
dev_err(&pdev->dev, "%s failed to claim resource: %pR\n",
dev_name(&ddev->dev), &ddev->mmio_res);
goto put_dev;
}
/* then add irq resource */
if (feature->nr_irqs) {
ddev->irqs = kcalloc(feature->nr_irqs,
sizeof(*ddev->irqs), GFP_KERNEL);
if (!ddev->irqs) {
ret = -ENOMEM;
goto put_dev;
}
for (i = 0; i < feature->nr_irqs; i++)
ddev->irqs[i] = feature->irq_ctx[i].irq;
ddev->num_irqs = feature->nr_irqs;
}
ret = device_add(&ddev->dev);
if (ret)
goto put_dev;
dev_dbg(&pdev->dev, "add dfl_dev: %s\n", dev_name(&ddev->dev));
return ddev;
put_dev:
/* calls release_dfl_dev() which does the clean up */
put_device(&ddev->dev);
return ERR_PTR(ret);
}
static void dfl_devs_remove(struct dfl_feature_platform_data *pdata)
{
struct dfl_feature *feature;
dfl_fpga_dev_for_each_feature(pdata, feature) {
if (feature->ddev) {
device_unregister(&feature->ddev->dev);
feature->ddev = NULL;
}
}
}
static int dfl_devs_add(struct dfl_feature_platform_data *pdata)
{
struct dfl_feature *feature;
struct dfl_device *ddev;
int ret;
dfl_fpga_dev_for_each_feature(pdata, feature) {
if (feature->ioaddr)
continue;
if (feature->ddev) {
ret = -EEXIST;
goto err;
}
ddev = dfl_dev_add(pdata, feature);
if (IS_ERR(ddev)) {
ret = PTR_ERR(ddev);
goto err;
}
feature->ddev = ddev;
}
return 0;
err:
dfl_devs_remove(pdata);
return ret;
}
int __dfl_driver_register(struct dfl_driver *dfl_drv, struct module *owner)
{
if (!dfl_drv || !dfl_drv->probe || !dfl_drv->id_table)
return -EINVAL;
dfl_drv->drv.owner = owner;
dfl_drv->drv.bus = &dfl_bus_type;
return driver_register(&dfl_drv->drv);
}
EXPORT_SYMBOL(__dfl_driver_register);
void dfl_driver_unregister(struct dfl_driver *dfl_drv)
{
driver_unregister(&dfl_drv->drv);
}
EXPORT_SYMBOL(dfl_driver_unregister);
#define is_header_feature(feature) ((feature)->id == FEATURE_ID_FIU_HEADER)
/**
* dfl_fpga_dev_feature_uinit - uinit for sub features of dfl feature device
* @pdev: feature device.
@ -259,12 +496,15 @@ void dfl_fpga_dev_feature_uinit(struct platform_device *pdev)
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
struct dfl_feature *feature;
dfl_fpga_dev_for_each_feature(pdata, feature)
dfl_devs_remove(pdata);
dfl_fpga_dev_for_each_feature(pdata, feature) {
if (feature->ops) {
if (feature->ops->uinit)
feature->ops->uinit(pdev, feature);
feature->ops = NULL;
}
}
}
EXPORT_SYMBOL_GPL(dfl_fpga_dev_feature_uinit);
@ -273,8 +513,22 @@ static int dfl_feature_instance_init(struct platform_device *pdev,
struct dfl_feature *feature,
struct dfl_feature_driver *drv)
{
void __iomem *base;
int ret = 0;
if (!is_header_feature(feature)) {
base = devm_platform_ioremap_resource(pdev,
feature->resource_index);
if (IS_ERR(base)) {
dev_err(&pdev->dev,
"ioremap failed for feature 0x%x!\n",
feature->id);
return PTR_ERR(base);
}
feature->ioaddr = base;
}
if (drv->ops->init) {
ret = drv->ops->init(pdev, feature);
if (ret)
@ -331,6 +585,10 @@ int dfl_fpga_dev_feature_init(struct platform_device *pdev,
drv++;
}
ret = dfl_devs_add(pdata);
if (ret)
goto exit;
return 0;
exit:
dfl_fpga_dev_feature_uinit(pdev);
@ -427,7 +685,9 @@ EXPORT_SYMBOL_GPL(dfl_fpga_dev_ops_unregister);
* @irq_table: Linux IRQ numbers for all irqs, indexed by local irq index of
* this device.
* @feature_dev: current feature device.
* @ioaddr: header register region address of feature device in enumeration.
* @ioaddr: header register region address of current FIU in enumeration.
* @start: register resource start of current FIU.
* @len: max register resource length of current FIU.
* @sub_features: a sub features linked list for feature device in enumeration.
* @feature_num: number of sub features for feature device in enumeration.
*/
@ -439,6 +699,8 @@ struct build_feature_devs_info {
struct platform_device *feature_dev;
void __iomem *ioaddr;
resource_size_t start;
resource_size_t len;
struct list_head sub_features;
int feature_num;
};
@ -454,7 +716,7 @@ struct build_feature_devs_info {
* @nr_irqs: number of irqs of this sub feature.
*/
struct dfl_feature_info {
u64 fid;
u16 fid;
struct resource mmio_res;
void __iomem *ioaddr;
struct list_head node;
@ -484,10 +746,7 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
struct dfl_feature_platform_data *pdata;
struct dfl_feature_info *finfo, *p;
enum dfl_id_type type;
int ret, index = 0;
if (!fdev)
return 0;
int ret, index = 0, res_idx = 0;
type = feature_dev_id_type(fdev);
if (WARN_ON_ONCE(type >= DFL_ID_MAX))
@ -530,16 +789,32 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
/* fill features and resource information for feature dev */
list_for_each_entry_safe(finfo, p, &binfo->sub_features, node) {
struct dfl_feature *feature = &pdata->features[index];
struct dfl_feature *feature = &pdata->features[index++];
struct dfl_feature_irq_ctx *ctx;
unsigned int i;
/* save resource information for each feature */
feature->dev = fdev;
feature->id = finfo->fid;
feature->resource_index = index;
feature->ioaddr = finfo->ioaddr;
fdev->resource[index++] = finfo->mmio_res;
/*
* the FIU header feature has some fundamental functions (sriov
* set, port enable/disable) needed for the dfl bus device and
* other sub features. So its mmio resource should be mapped by
* DFL bus device. And we should not assign it to feature
* devices (dfl-fme/afu) again.
*/
if (is_header_feature(feature)) {
feature->resource_index = -1;
feature->ioaddr =
devm_ioremap_resource(binfo->dev,
&finfo->mmio_res);
if (IS_ERR(feature->ioaddr))
return PTR_ERR(feature->ioaddr);
} else {
feature->resource_index = res_idx;
fdev->resource[res_idx++] = finfo->mmio_res;
}
if (finfo->nr_irqs) {
ctx = devm_kcalloc(binfo->dev, finfo->nr_irqs,
@ -582,19 +857,13 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
static int
build_info_create_dev(struct build_feature_devs_info *binfo,
enum dfl_id_type type, void __iomem *ioaddr)
enum dfl_id_type type)
{
struct platform_device *fdev;
int ret;
if (type >= DFL_ID_MAX)
return -EINVAL;
/* we will create a new device, commit current device first */
ret = build_info_commit_dev(binfo);
if (ret)
return ret;
/*
* we use -ENODEV as the initialization indicator which indicates
* whether the id need to be reclaimed
@ -605,7 +874,7 @@ build_info_create_dev(struct build_feature_devs_info *binfo,
binfo->feature_dev = fdev;
binfo->feature_num = 0;
binfo->ioaddr = ioaddr;
INIT_LIST_HEAD(&binfo->sub_features);
fdev->id = dfl_id_alloc(type, &fdev->dev);
@ -649,7 +918,7 @@ static inline u32 feature_size(void __iomem *start)
return ofst ? ofst : 4096;
}
static u64 feature_id(void __iomem *start)
static u16 feature_id(void __iomem *start)
{
u64 v = readq(start + DFH);
u16 id = FIELD_GET(DFH_ID, v);
@ -667,7 +936,7 @@ static u64 feature_id(void __iomem *start)
}
static int parse_feature_irqs(struct build_feature_devs_info *binfo,
resource_size_t ofst, u64 fid,
resource_size_t ofst, u16 fid,
unsigned int *irq_base, unsigned int *nr_irqs)
{
void __iomem *base = binfo->ioaddr + ofst;
@ -713,12 +982,12 @@ static int parse_feature_irqs(struct build_feature_devs_info *binfo,
return 0;
}
dev_dbg(binfo->dev, "feature: 0x%llx, irq_base: %u, nr_irqs: %u\n",
dev_dbg(binfo->dev, "feature: 0x%x, irq_base: %u, nr_irqs: %u\n",
fid, ibase, inr);
if (ibase + inr > binfo->nr_irqs) {
dev_err(binfo->dev,
"Invalid interrupt number in feature 0x%llx\n", fid);
"Invalid interrupt number in feature 0x%x\n", fid);
return -EINVAL;
}
@ -726,7 +995,7 @@ static int parse_feature_irqs(struct build_feature_devs_info *binfo,
virq = binfo->irq_table[ibase + i];
if (virq < 0 || virq > NR_IRQS) {
dev_err(binfo->dev,
"Invalid irq table entry for feature 0x%llx\n",
"Invalid irq table entry for feature 0x%x\n",
fid);
return -EINVAL;
}
@ -747,18 +1016,17 @@ static int parse_feature_irqs(struct build_feature_devs_info *binfo,
*/
static int
create_feature_instance(struct build_feature_devs_info *binfo,
struct dfl_fpga_enum_dfl *dfl, resource_size_t ofst,
resource_size_t size, u64 fid)
resource_size_t ofst, resource_size_t size, u16 fid)
{
unsigned int irq_base, nr_irqs;
struct dfl_feature_info *finfo;
int ret;
/* read feature size and id if inputs are invalid */
size = size ? size : feature_size(dfl->ioaddr + ofst);
fid = fid ? fid : feature_id(dfl->ioaddr + ofst);
size = size ? size : feature_size(binfo->ioaddr + ofst);
fid = fid ? fid : feature_id(binfo->ioaddr + ofst);
if (dfl->len - ofst < size)
if (binfo->len - ofst < size)
return -EINVAL;
ret = parse_feature_irqs(binfo, ofst, fid, &irq_base, &nr_irqs);
@ -770,12 +1038,11 @@ create_feature_instance(struct build_feature_devs_info *binfo,
return -ENOMEM;
finfo->fid = fid;
finfo->mmio_res.start = dfl->start + ofst;
finfo->mmio_res.start = binfo->start + ofst;
finfo->mmio_res.end = finfo->mmio_res.start + size - 1;
finfo->mmio_res.flags = IORESOURCE_MEM;
finfo->irq_base = irq_base;
finfo->nr_irqs = nr_irqs;
finfo->ioaddr = dfl->ioaddr + ofst;
list_add_tail(&finfo->node, &binfo->sub_features);
binfo->feature_num++;
@ -784,7 +1051,6 @@ create_feature_instance(struct build_feature_devs_info *binfo,
}
static int parse_feature_port_afu(struct build_feature_devs_info *binfo,
struct dfl_fpga_enum_dfl *dfl,
resource_size_t ofst)
{
u64 v = readq(binfo->ioaddr + PORT_HDR_CAP);
@ -792,21 +1058,22 @@ static int parse_feature_port_afu(struct build_feature_devs_info *binfo,
WARN_ON(!size);
return create_feature_instance(binfo, dfl, ofst, size, FEATURE_ID_AFU);
return create_feature_instance(binfo, ofst, size, FEATURE_ID_AFU);
}
#define is_feature_dev_detected(binfo) (!!(binfo)->feature_dev)
static int parse_feature_afu(struct build_feature_devs_info *binfo,
struct dfl_fpga_enum_dfl *dfl,
resource_size_t ofst)
{
if (!binfo->feature_dev) {
if (!is_feature_dev_detected(binfo)) {
dev_err(binfo->dev, "this AFU does not belong to any FIU.\n");
return -EINVAL;
}
switch (feature_dev_id_type(binfo->feature_dev)) {
case PORT_ID:
return parse_feature_port_afu(binfo, dfl, ofst);
return parse_feature_port_afu(binfo, ofst);
default:
dev_info(binfo->dev, "AFU belonging to FIU %s is not supported yet.\n",
binfo->feature_dev->name);
@ -815,35 +1082,79 @@ static int parse_feature_afu(struct build_feature_devs_info *binfo,
return 0;
}
static int build_info_prepare(struct build_feature_devs_info *binfo,
resource_size_t start, resource_size_t len)
{
struct device *dev = binfo->dev;
void __iomem *ioaddr;
if (!devm_request_mem_region(dev, start, len, dev_name(dev))) {
dev_err(dev, "request region fail, start:%pa, len:%pa\n",
&start, &len);
return -EBUSY;
}
ioaddr = devm_ioremap(dev, start, len);
if (!ioaddr) {
dev_err(dev, "ioremap region fail, start:%pa, len:%pa\n",
&start, &len);
return -ENOMEM;
}
binfo->start = start;
binfo->len = len;
binfo->ioaddr = ioaddr;
return 0;
}
static void build_info_complete(struct build_feature_devs_info *binfo)
{
devm_iounmap(binfo->dev, binfo->ioaddr);
devm_release_mem_region(binfo->dev, binfo->start, binfo->len);
}
static int parse_feature_fiu(struct build_feature_devs_info *binfo,
struct dfl_fpga_enum_dfl *dfl,
resource_size_t ofst)
{
u32 id, offset;
u64 v;
int ret = 0;
u32 offset;
u16 id;
u64 v;
v = readq(dfl->ioaddr + ofst + DFH);
if (is_feature_dev_detected(binfo)) {
build_info_complete(binfo);
ret = build_info_commit_dev(binfo);
if (ret)
return ret;
ret = build_info_prepare(binfo, binfo->start + ofst,
binfo->len - ofst);
if (ret)
return ret;
}
v = readq(binfo->ioaddr + DFH);
id = FIELD_GET(DFH_ID, v);
/* create platform device for dfl feature dev */
ret = build_info_create_dev(binfo, dfh_id_to_type(id),
dfl->ioaddr + ofst);
ret = build_info_create_dev(binfo, dfh_id_to_type(id));
if (ret)
return ret;
ret = create_feature_instance(binfo, dfl, ofst, 0, 0);
ret = create_feature_instance(binfo, 0, 0, 0);
if (ret)
return ret;
/*
* find and parse FIU's child AFU via its NEXT_AFU register.
* please note that only Port has valid NEXT_AFU pointer per spec.
*/
v = readq(dfl->ioaddr + ofst + NEXT_AFU);
v = readq(binfo->ioaddr + NEXT_AFU);
offset = FIELD_GET(NEXT_AFU_NEXT_DFH_OFST, v);
if (offset)
return parse_feature_afu(binfo, dfl, ofst + offset);
return parse_feature_afu(binfo, offset);
dev_dbg(binfo->dev, "No AFUs detected on FIU %d\n", id);
@ -851,41 +1162,39 @@ static int parse_feature_fiu(struct build_feature_devs_info *binfo,
}
static int parse_feature_private(struct build_feature_devs_info *binfo,
struct dfl_fpga_enum_dfl *dfl,
resource_size_t ofst)
{
if (!binfo->feature_dev) {
dev_err(binfo->dev, "the private feature %llx does not belong to any AFU.\n",
(unsigned long long)feature_id(dfl->ioaddr + ofst));
if (!is_feature_dev_detected(binfo)) {
dev_err(binfo->dev, "the private feature 0x%x does not belong to any AFU.\n",
feature_id(binfo->ioaddr + ofst));
return -EINVAL;
}
return create_feature_instance(binfo, dfl, ofst, 0, 0);
return create_feature_instance(binfo, ofst, 0, 0);
}
/**
* parse_feature - parse a feature on given device feature list
*
* @binfo: build feature devices information.
* @dfl: device feature list to parse
* @ofst: offset to feature header on this device feature list
* @ofst: offset to current FIU header
*/
static int parse_feature(struct build_feature_devs_info *binfo,
struct dfl_fpga_enum_dfl *dfl, resource_size_t ofst)
resource_size_t ofst)
{
u64 v;
u32 type;
v = readq(dfl->ioaddr + ofst + DFH);
v = readq(binfo->ioaddr + ofst + DFH);
type = FIELD_GET(DFH_TYPE, v);
switch (type) {
case DFH_TYPE_AFU:
return parse_feature_afu(binfo, dfl, ofst);
return parse_feature_afu(binfo, ofst);
case DFH_TYPE_PRIVATE:
return parse_feature_private(binfo, dfl, ofst);
return parse_feature_private(binfo, ofst);
case DFH_TYPE_FIU:
return parse_feature_fiu(binfo, dfl, ofst);
return parse_feature_fiu(binfo, ofst);
default:
dev_info(binfo->dev,
"Feature Type %x is not supported.\n", type);
@ -895,14 +1204,17 @@ static int parse_feature(struct build_feature_devs_info *binfo,
}
static int parse_feature_list(struct build_feature_devs_info *binfo,
struct dfl_fpga_enum_dfl *dfl)
resource_size_t start, resource_size_t len)
{
void __iomem *start = dfl->ioaddr;
void __iomem *end = dfl->ioaddr + dfl->len;
resource_size_t end = start + len;
int ret = 0;
u32 ofst = 0;
u64 v;
ret = build_info_prepare(binfo, start, len);
if (ret)
return ret;
/* walk through the device feature list via DFH's next DFH pointer. */
for (; start < end; start += ofst) {
if (end - start < DFH_SIZE) {
@ -910,11 +1222,11 @@ static int parse_feature_list(struct build_feature_devs_info *binfo,
return -EINVAL;
}
ret = parse_feature(binfo, dfl, start - dfl->ioaddr);
ret = parse_feature(binfo, start - binfo->start);
if (ret)
return ret;
v = readq(start + DFH);
v = readq(binfo->ioaddr + start - binfo->start + DFH);
ofst = FIELD_GET(DFH_NEXT_HDR_OFST, v);
/* stop parsing if EOL(End of List) is set or offset is 0 */
@ -923,7 +1235,12 @@ static int parse_feature_list(struct build_feature_devs_info *binfo,
}
/* commit current feature device when reach the end of list */
return build_info_commit_dev(binfo);
build_info_complete(binfo);
if (is_feature_dev_detected(binfo))
ret = build_info_commit_dev(binfo);
return ret;
}
struct dfl_fpga_enum_info *dfl_fpga_enum_info_alloc(struct device *dev)
@ -976,7 +1293,6 @@ EXPORT_SYMBOL_GPL(dfl_fpga_enum_info_free);
* @info: ptr to dfl_fpga_enum_info
* @start: mmio resource address of the device feature list.
* @len: mmio resource length of the device feature list.
* @ioaddr: mapped mmio resource address of the device feature list.
*
* One FPGA device may have one or more Device Feature Lists (DFLs), use this
* function to add information of each DFL to common data structure for next
@ -985,8 +1301,7 @@ EXPORT_SYMBOL_GPL(dfl_fpga_enum_info_free);
* Return: 0 on success, negative error code otherwise.
*/
int dfl_fpga_enum_info_add_dfl(struct dfl_fpga_enum_info *info,
resource_size_t start, resource_size_t len,
void __iomem *ioaddr)
resource_size_t start, resource_size_t len)
{
struct dfl_fpga_enum_dfl *dfl;
@ -996,7 +1311,6 @@ int dfl_fpga_enum_info_add_dfl(struct dfl_fpga_enum_info *info,
dfl->start = start;
dfl->len = len;
dfl->ioaddr = ioaddr;
list_add_tail(&dfl->node, &info->dfls);
@ -1119,7 +1433,7 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info)
* Lists.
*/
list_for_each_entry(dfl, &info->dfls, node) {
ret = parse_feature_list(binfo, dfl);
ret = parse_feature_list(binfo, dfl->start, dfl->len);
if (ret) {
remove_feature_devs(cdev);
build_info_free(binfo);
@ -1212,11 +1526,17 @@ static int __init dfl_fpga_init(void)
{
int ret;
ret = bus_register(&dfl_bus_type);
if (ret)
return ret;
dfl_ids_init();
ret = dfl_chardev_init();
if (ret)
if (ret) {
dfl_ids_destroy();
bus_unregister(&dfl_bus_type);
}
return ret;
}
@ -1424,7 +1744,7 @@ static int do_set_irq_trigger(struct dfl_feature *feature, unsigned int idx,
return 0;
feature->irq_ctx[idx].name =
kasprintf(GFP_KERNEL, "fpga-irq[%u](%s-%llx)", idx,
kasprintf(GFP_KERNEL, "fpga-irq[%u](%s-%x)", idx,
dev_name(&pdev->dev), feature->id);
if (!feature->irq_ctx[idx].name)
return -ENOMEM;
@ -1554,6 +1874,7 @@ static void __exit dfl_fpga_exit(void)
{
dfl_chardev_uinit();
dfl_ids_destroy();
bus_unregister(&dfl_bus_type);
}
module_init(dfl_fpga_init);

View File

@ -197,7 +197,7 @@ int dfl_fpga_check_port_id(struct platform_device *pdev, void *pport_id);
* @id: unique dfl private feature id.
*/
struct dfl_feature_id {
u64 id;
u16 id;
};
/**
@ -236,16 +236,18 @@ struct dfl_feature_irq_ctx {
* @irq_ctx: interrupt context list.
* @nr_irqs: number of interrupt contexts.
* @ops: ops of this sub feature.
* @ddev: ptr to the dfl device of this sub feature.
* @priv: priv data of this feature.
*/
struct dfl_feature {
struct platform_device *dev;
u64 id;
u16 id;
int resource_index;
void __iomem *ioaddr;
struct dfl_feature_irq_ctx *irq_ctx;
unsigned int nr_irqs;
const struct dfl_feature_ops *ops;
struct dfl_device *ddev;
void *priv;
};
@ -365,7 +367,7 @@ struct platform_device *dfl_fpga_inode_to_feature_dev(struct inode *inode)
(feature) < (pdata)->features + (pdata)->num; (feature)++)
static inline
struct dfl_feature *dfl_get_feature_by_id(struct device *dev, u64 id)
struct dfl_feature *dfl_get_feature_by_id(struct device *dev, u16 id)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
struct dfl_feature *feature;
@ -378,7 +380,7 @@ struct dfl_feature *dfl_get_feature_by_id(struct device *dev, u64 id)
}
static inline
void __iomem *dfl_get_feature_ioaddr_by_id(struct device *dev, u64 id)
void __iomem *dfl_get_feature_ioaddr_by_id(struct device *dev, u16 id)
{
struct dfl_feature *feature = dfl_get_feature_by_id(dev, id);
@ -389,7 +391,7 @@ void __iomem *dfl_get_feature_ioaddr_by_id(struct device *dev, u64 id)
return NULL;
}
static inline bool is_dfl_feature_present(struct device *dev, u64 id)
static inline bool is_dfl_feature_present(struct device *dev, u16 id)
{
return !!dfl_get_feature_ioaddr_by_id(dev, id);
}
@ -441,22 +443,17 @@ struct dfl_fpga_enum_info {
*
* @start: base address of this device feature list.
* @len: size of this device feature list.
* @ioaddr: mapped base address of this device feature list.
* @node: node in list of device feature lists.
*/
struct dfl_fpga_enum_dfl {
resource_size_t start;
resource_size_t len;
void __iomem *ioaddr;
struct list_head node;
};
struct dfl_fpga_enum_info *dfl_fpga_enum_info_alloc(struct device *dev);
int dfl_fpga_enum_info_add_dfl(struct dfl_fpga_enum_info *info,
resource_size_t start, resource_size_t len,
void __iomem *ioaddr);
resource_size_t start, resource_size_t len);
int dfl_fpga_enum_info_add_irq(struct dfl_fpga_enum_info *info,
unsigned int nr_irqs, int *irq_table);
void dfl_fpga_enum_info_free(struct dfl_fpga_enum_info *info);
@ -519,4 +516,88 @@ long dfl_feature_ioctl_set_irq(struct platform_device *pdev,
struct dfl_feature *feature,
unsigned long arg);
/**
* enum dfl_id_type - define the DFL FIU types
*/
enum dfl_id_type {
FME_ID,
PORT_ID,
DFL_ID_MAX,
};
/**
* struct dfl_device_id - dfl device identifier
* @type: contains 4 bits DFL FIU type of the device. See enum dfl_id_type.
* @feature_id: contains 12 bits feature identifier local to its DFL FIU type.
* @driver_data: driver specific data.
*/
struct dfl_device_id {
u8 type;
u16 feature_id;
unsigned long driver_data;
};
/**
* struct dfl_device - represent an dfl device on dfl bus
*
* @dev: generic device interface.
* @id: id of the dfl device.
* @type: type of DFL FIU of the device. See enum dfl_id_type.
* @feature_id: 16 bits feature identifier local to its DFL FIU type.
* @mmio_res: mmio resource of this dfl device.
* @irqs: list of Linux IRQ numbers of this dfl device.
* @num_irqs: number of IRQs supported by this dfl device.
* @cdev: pointer to DFL FPGA container device this dfl device belongs to.
* @id_entry: matched id entry in dfl driver's id table.
*/
struct dfl_device {
struct device dev;
int id;
u8 type;
u16 feature_id;
struct resource mmio_res;
int *irqs;
unsigned int num_irqs;
struct dfl_fpga_cdev *cdev;
const struct dfl_device_id *id_entry;
};
/**
* struct dfl_driver - represent an dfl device driver
*
* @drv: driver model structure.
* @id_table: pointer to table of device IDs the driver is interested in.
* { } member terminated.
* @probe: mandatory callback for device binding.
* @remove: callback for device unbinding.
*/
struct dfl_driver {
struct device_driver drv;
const struct dfl_device_id *id_table;
int (*probe)(struct dfl_device *dfl_dev);
void (*remove)(struct dfl_device *dfl_dev);
};
#define to_dfl_dev(d) container_of(d, struct dfl_device, dev)
#define to_dfl_drv(d) container_of(d, struct dfl_driver, drv)
/*
* use a macro to avoid include chaining to get THIS_MODULE.
*/
#define dfl_driver_register(drv) \
__dfl_driver_register(drv, THIS_MODULE)
int __dfl_driver_register(struct dfl_driver *dfl_drv, struct module *owner);
void dfl_driver_unregister(struct dfl_driver *dfl_drv);
/*
* module_dfl_driver() - Helper macro for drivers that don't do
* anything special in module init/exit. This eliminates a lot of
* boilerplate. Each module may only use this macro once, and
* calling it replaces module_init() and module_exit().
*/
#define module_dfl_driver(__dfl_driver) \
module_driver(__dfl_driver, dfl_driver_register, \
dfl_driver_unregister)
#endif /* __FPGA_DFL_H */

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* FPGA Region - Device Tree support for FPGA programming under Linux
* FPGA Region - Support for FPGA programming under Linux
*
* Copyright (C) 2013-2016 Altera Corporation
* Copyright (C) 2017 Intel Corporation

View File

@ -196,17 +196,13 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
if (ret < 0)
goto init_done;
ret = wait_for_completion_interruptible_timeout(
ret = wait_for_completion_timeout(
&priv->status_return_completion, S10_RECONFIG_TIMEOUT);
if (!ret) {
dev_err(dev, "timeout waiting for RECONFIG_REQUEST\n");
ret = -ETIMEDOUT;
goto init_done;
}
if (ret < 0) {
dev_err(dev, "error (%d) waiting for RECONFIG_REQUEST\n", ret);
goto init_done;
}
ret = 0;
if (!test_and_clear_bit(SVC_STATUS_OK, &priv->status)) {
@ -318,7 +314,7 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
*/
wait_status = 1; /* not timed out */
if (!priv->status)
wait_status = wait_for_completion_interruptible_timeout(
wait_status = wait_for_completion_timeout(
&priv->status_return_completion,
S10_BUFFER_TIMEOUT);
@ -340,13 +336,6 @@ static int s10_ops_write(struct fpga_manager *mgr, const char *buf,
ret = -ETIMEDOUT;
break;
}
if (wait_status < 0) {
ret = wait_status;
dev_err(dev,
"error (%d) waiting for svc layer buffers\n",
ret);
break;
}
}
if (!s10_free_buffers(mgr))
@ -372,7 +361,7 @@ static int s10_ops_write_complete(struct fpga_manager *mgr,
if (ret < 0)
break;
ret = wait_for_completion_interruptible_timeout(
ret = wait_for_completion_timeout(
&priv->status_return_completion, timeout);
if (!ret) {
dev_err(dev,
@ -380,12 +369,6 @@ static int s10_ops_write_complete(struct fpga_manager *mgr,
ret = -ETIMEDOUT;
break;
}
if (ret < 0) {
dev_err(dev,
"error (%d) waiting for RECONFIG_COMPLETED\n",
ret);
break;
}
/* Not error or timeout, so ret is # of jiffies until timeout */
timeout = ret;
ret = 0;

View File

@ -27,11 +27,22 @@ struct xilinx_spi_conf {
struct gpio_desc *done;
};
static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr)
static int get_done_gpio(struct fpga_manager *mgr)
{
struct xilinx_spi_conf *conf = mgr->priv;
int ret;
if (!gpiod_get_value(conf->done))
ret = gpiod_get_value(conf->done);
if (ret < 0)
dev_err(&mgr->dev, "Error reading DONE (%d)\n", ret);
return ret;
}
static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr)
{
if (!get_done_gpio(mgr))
return FPGA_MGR_STATE_RESET;
return FPGA_MGR_STATE_UNKNOWN;
@ -57,11 +68,21 @@ static int wait_for_init_b(struct fpga_manager *mgr, int value,
if (conf->init_b) {
while (time_before(jiffies, timeout)) {
/* dump_state(conf, "wait for init_d .."); */
if (gpiod_get_value(conf->init_b) == value)
int ret = gpiod_get_value(conf->init_b);
if (ret == value)
return 0;
if (ret < 0) {
dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret);
return ret;
}
usleep_range(100, 400);
}
dev_err(&mgr->dev, "Timeout waiting for INIT_B to %s\n",
value ? "assert" : "deassert");
return -ETIMEDOUT;
}
@ -78,7 +99,7 @@ static int xilinx_spi_write_init(struct fpga_manager *mgr,
int err;
if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
dev_err(&mgr->dev, "Partial reconfiguration not supported.\n");
dev_err(&mgr->dev, "Partial reconfiguration not supported\n");
return -EINVAL;
}
@ -86,7 +107,6 @@ static int xilinx_spi_write_init(struct fpga_manager *mgr,
err = wait_for_init_b(mgr, 1, 1); /* min is 500 ns */
if (err) {
dev_err(&mgr->dev, "INIT_B pin did not go low\n");
gpiod_set_value(conf->prog_b, 0);
return err;
}
@ -94,12 +114,10 @@ static int xilinx_spi_write_init(struct fpga_manager *mgr,
gpiod_set_value(conf->prog_b, 0);
err = wait_for_init_b(mgr, 0, 0);
if (err) {
dev_err(&mgr->dev, "INIT_B pin did not go high\n");
if (err)
return err;
}
if (gpiod_get_value(conf->done)) {
if (get_done_gpio(mgr)) {
dev_err(&mgr->dev, "Unexpected DONE pin state...\n");
return -EIO;
}
@ -152,25 +170,46 @@ static int xilinx_spi_write_complete(struct fpga_manager *mgr,
struct fpga_image_info *info)
{
struct xilinx_spi_conf *conf = mgr->priv;
unsigned long timeout;
unsigned long timeout = jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
bool expired = false;
int done;
int ret;
if (gpiod_get_value(conf->done))
return xilinx_spi_apply_cclk_cycles(conf);
/*
* This loop is carefully written such that if the driver is
* scheduled out for more than 'timeout', we still check for DONE
* before giving up and we apply 8 extra CCLK cycles in all cases.
*/
while (!expired) {
expired = time_after(jiffies, timeout);
timeout = jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
while (time_before(jiffies, timeout)) {
done = get_done_gpio(mgr);
if (done < 0)
return done;
ret = xilinx_spi_apply_cclk_cycles(conf);
if (ret)
return ret;
if (gpiod_get_value(conf->done))
return xilinx_spi_apply_cclk_cycles(conf);
if (done)
return 0;
}
if (conf->init_b) {
ret = gpiod_get_value(conf->init_b);
if (ret < 0) {
dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret);
return ret;
}
dev_err(&mgr->dev,
ret ? "CRC error or invalid device\n"
: "Missing sync word or incomplete bitstream\n");
} else {
dev_err(&mgr->dev, "Timeout after config data transfer\n");
}
dev_err(&mgr->dev, "Timeout after config data transfer.\n");
return -ETIMEDOUT;
}

View File

@ -50,6 +50,7 @@ static const int engine_page_size = 0x400;
#define FSI_SMODE 0x0 /* R/W: Mode register */
#define FSI_SISC 0x8 /* R/W: Interrupt condition */
#define FSI_SSTAT 0x14 /* R : Slave status */
#define FSI_SLBUS 0x30 /* W : LBUS Ownership */
#define FSI_LLMODE 0x100 /* R/W: Link layer mode register */
/*
@ -66,6 +67,11 @@ static const int engine_page_size = 0x400;
#define FSI_SMODE_LBCRR_SHIFT 8 /* Clk ratio shift */
#define FSI_SMODE_LBCRR_MASK 0xf /* Clk ratio mask */
/*
* SLBUS fields
*/
#define FSI_SLBUS_FORCE 0x80000000 /* Force LBUS ownership */
/*
* LLMODE fields
*/
@ -981,7 +987,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
uint32_t cfam_id;
struct fsi_slave *slave;
uint8_t crc;
__be32 data, llmode;
__be32 data, llmode, slbus;
int rc;
/* Currently, we only support single slaves on a link, and use the
@ -1052,6 +1058,14 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
}
slbus = cpu_to_be32(FSI_SLBUS_FORCE);
rc = fsi_master_write(master, link, id, FSI_SLAVE_BASE + FSI_SLBUS,
&slbus, sizeof(slbus));
if (rc)
dev_warn(&master->dev,
"can't set slbus on slave:%02x:%02x %d\n", link, id,
rc);
rc = fsi_slave_set_smode(slave);
if (rc) {
dev_warn(&master->dev,
@ -1154,10 +1168,18 @@ static int fsi_master_write(struct fsi_master *master, int link,
return rc;
}
static int fsi_master_link_disable(struct fsi_master *master, int link)
{
if (master->link_enable)
return master->link_enable(master, link, false);
return 0;
}
static int fsi_master_link_enable(struct fsi_master *master, int link)
{
if (master->link_enable)
return master->link_enable(master, link);
return master->link_enable(master, link, true);
return 0;
}
@ -1192,12 +1214,15 @@ static int fsi_master_scan(struct fsi_master *master)
}
rc = fsi_master_break(master, link);
if (rc) {
fsi_master_link_disable(master, link);
dev_dbg(&master->dev,
"break to link %d failed: %d\n", link, rc);
continue;
}
fsi_slave_init(master, link, 0);
rc = fsi_slave_init(master, link, 0);
if (rc)
fsi_master_link_disable(master, link);
}
return 0;

View File

@ -13,6 +13,7 @@
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/iopoll.h>
#include <linux/gpio/consumer.h>
#include "fsi-master.h"
@ -21,6 +22,7 @@ struct fsi_master_aspeed {
struct device *dev;
void __iomem *base;
struct clk *clk;
struct gpio_desc *cfam_reset_gpio;
};
#define to_fsi_master_aspeed(m) \
@ -82,7 +84,12 @@ static const u32 fsi_base = 0xa0000000;
#define FSI_LINK_ENABLE_SETUP_TIME 10 /* in mS */
#define DEFAULT_DIVISOR 14
/* Run the bus at maximum speed by default */
#define FSI_DIVISOR_DEFAULT 1
#define FSI_DIVISOR_CABLED 2
static u16 aspeed_fsi_divisor = FSI_DIVISOR_DEFAULT;
module_param_named(bus_div,aspeed_fsi_divisor, ushort, 0);
#define OPB_POLL_TIMEOUT 10000
static int __opb_write(struct fsi_master_aspeed *aspeed, u32 addr,
@ -241,9 +248,10 @@ static int aspeed_master_read(struct fsi_master *master, int link,
struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master);
int ret;
if (id != 0)
if (id > 0x3)
return -EINVAL;
addr |= id << 21;
addr += link * FSI_HUB_LINK_SIZE;
switch (size) {
@ -273,9 +281,10 @@ static int aspeed_master_write(struct fsi_master *master, int link,
struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master);
int ret;
if (id != 0)
if (id > 0x3)
return -EINVAL;
addr |= id << 21;
addr += link * FSI_HUB_LINK_SIZE;
switch (size) {
@ -299,32 +308,28 @@ static int aspeed_master_write(struct fsi_master *master, int link,
return 0;
}
static int aspeed_master_link_enable(struct fsi_master *master, int link)
static int aspeed_master_link_enable(struct fsi_master *master, int link,
bool enable)
{
struct fsi_master_aspeed *aspeed = to_fsi_master_aspeed(master);
int idx, bit, ret;
__be32 reg, result;
__be32 reg;
idx = link / 32;
bit = link % 32;
reg = cpu_to_be32(0x80000000 >> bit);
if (!enable)
return opb_writel(aspeed, ctrl_base + FSI_MCENP0 + (4 * idx),
reg);
ret = opb_writel(aspeed, ctrl_base + FSI_MSENP0 + (4 * idx), reg);
if (ret)
return ret;
mdelay(FSI_LINK_ENABLE_SETUP_TIME);
ret = opb_readl(aspeed, ctrl_base + FSI_MENP0 + (4 * idx), &result);
if (ret)
return ret;
if (result != reg) {
dev_err(aspeed->dev, "%s failed: %08x\n", __func__, result);
return -EIO;
}
return 0;
}
@ -386,9 +391,11 @@ static int aspeed_master_init(struct fsi_master_aspeed *aspeed)
opb_writel(aspeed, ctrl_base + FSI_MECTRL, reg);
reg = cpu_to_be32(FSI_MMODE_ECRC | FSI_MMODE_EPC | FSI_MMODE_RELA
| fsi_mmode_crs0(DEFAULT_DIVISOR)
| fsi_mmode_crs1(DEFAULT_DIVISOR)
| fsi_mmode_crs0(aspeed_fsi_divisor)
| fsi_mmode_crs1(aspeed_fsi_divisor)
| FSI_MMODE_P8_TO_LSB);
dev_info(aspeed->dev, "mmode set to %08x (divisor %d)\n",
be32_to_cpu(reg), aspeed_fsi_divisor);
opb_writel(aspeed, ctrl_base + FSI_MMODE, reg);
reg = cpu_to_be32(0xffff0000);
@ -419,6 +426,90 @@ static int aspeed_master_init(struct fsi_master_aspeed *aspeed)
return 0;
}
static ssize_t cfam_reset_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct fsi_master_aspeed *aspeed = dev_get_drvdata(dev);
gpiod_set_value(aspeed->cfam_reset_gpio, 1);
usleep_range(900, 1000);
gpiod_set_value(aspeed->cfam_reset_gpio, 0);
return count;
}
static DEVICE_ATTR(cfam_reset, 0200, NULL, cfam_reset_store);
static int setup_cfam_reset(struct fsi_master_aspeed *aspeed)
{
struct device *dev = aspeed->dev;
struct gpio_desc *gpio;
int rc;
gpio = devm_gpiod_get_optional(dev, "cfam-reset", GPIOD_OUT_LOW);
if (IS_ERR(gpio))
return PTR_ERR(gpio);
if (!gpio)
return 0;
aspeed->cfam_reset_gpio = gpio;
rc = device_create_file(dev, &dev_attr_cfam_reset);
if (rc) {
devm_gpiod_put(dev, gpio);
return rc;
}
return 0;
}
static int tacoma_cabled_fsi_fixup(struct device *dev)
{
struct gpio_desc *routing_gpio, *mux_gpio;
int gpio;
/*
* The routing GPIO is a jumper indicating we should mux for the
* externally connected FSI cable.
*/
routing_gpio = devm_gpiod_get_optional(dev, "fsi-routing",
GPIOD_IN | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
if (IS_ERR(routing_gpio))
return PTR_ERR(routing_gpio);
if (!routing_gpio)
return 0;
mux_gpio = devm_gpiod_get_optional(dev, "fsi-mux", GPIOD_ASIS);
if (IS_ERR(mux_gpio))
return PTR_ERR(mux_gpio);
if (!mux_gpio)
return 0;
gpio = gpiod_get_value(routing_gpio);
if (gpio < 0)
return gpio;
/* If the routing GPIO is high we should set the mux to low. */
if (gpio) {
/*
* Cable signal integrity means we should run the bus
* slightly slower. Do not override if a kernel param
* has already overridden.
*/
if (aspeed_fsi_divisor == FSI_DIVISOR_DEFAULT)
aspeed_fsi_divisor = FSI_DIVISOR_CABLED;
gpiod_direction_output(mux_gpio, 0);
dev_info(dev, "FSI configured for external cable\n");
} else {
gpiod_direction_output(mux_gpio, 1);
}
devm_gpiod_put(dev, routing_gpio);
return 0;
}
static int fsi_master_aspeed_probe(struct platform_device *pdev)
{
struct fsi_master_aspeed *aspeed;
@ -426,6 +517,12 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
int rc, links, reg;
__be32 raw;
rc = tacoma_cabled_fsi_fixup(&pdev->dev);
if (rc) {
dev_err(&pdev->dev, "Tacoma FSI cable fixup failed\n");
return rc;
}
aspeed = devm_kzalloc(&pdev->dev, sizeof(*aspeed), GFP_KERNEL);
if (!aspeed)
return -ENOMEM;
@ -448,6 +545,11 @@ static int fsi_master_aspeed_probe(struct platform_device *pdev)
return rc;
}
rc = setup_cfam_reset(aspeed);
if (rc) {
dev_err(&pdev->dev, "CFAM reset GPIO setup failed\n");
}
writel(0x1, aspeed->base + OPB_CLK_SYNC);
writel(OPB1_XFER_ACK_EN | OPB0_XFER_ACK_EN,
aspeed->base + OPB_IRQ_MASK);

View File

@ -838,7 +838,7 @@ static int load_copro_firmware(struct fsi_master_acf *master)
rc = request_firmware(&fw, FW_FILE_NAME, master->dev);
if (rc) {
dev_err(
master->dev, "Error %d to load firwmare '%s' !\n",
master->dev, "Error %d to load firmware '%s' !\n",
rc, FW_FILE_NAME);
return rc;
}
@ -1039,7 +1039,8 @@ static void fsi_master_acf_setup_external(struct fsi_master_acf *master)
gpiod_direction_input(master->gpio_data);
}
static int fsi_master_acf_link_enable(struct fsi_master *_master, int link)
static int fsi_master_acf_link_enable(struct fsi_master *_master, int link,
bool enable)
{
struct fsi_master_acf *master = to_fsi_master_acf(_master);
int rc = -EBUSY;
@ -1049,7 +1050,7 @@ static int fsi_master_acf_link_enable(struct fsi_master *_master, int link)
mutex_lock(&master->lock);
if (!master->external_mode) {
gpiod_set_value(master->gpio_enable, 1);
gpiod_set_value(master->gpio_enable, enable ? 1 : 0);
rc = 0;
}
mutex_unlock(&master->lock);

View File

@ -678,7 +678,8 @@ static void fsi_master_gpio_init_external(struct fsi_master_gpio *master)
gpiod_direction_input(master->gpio_data);
}
static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link,
bool enable)
{
struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
int rc = -EBUSY;
@ -688,7 +689,7 @@ static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
mutex_lock(&master->cmd_lock);
if (!master->external_mode) {
gpiod_set_value(master->gpio_enable, 1);
gpiod_set_value(master->gpio_enable, enable ? 1 : 0);
rc = 0;
}
mutex_unlock(&master->cmd_lock);

View File

@ -77,7 +77,8 @@ static int hub_master_break(struct fsi_master *master, int link)
return hub_master_write(master, link, 0, addr, &cmd, sizeof(cmd));
}
static int hub_master_link_enable(struct fsi_master *master, int link)
static int hub_master_link_enable(struct fsi_master *master, int link,
bool enable)
{
struct fsi_master_hub *hub = to_fsi_master_hub(master);
int idx, bit;
@ -89,13 +90,17 @@ static int hub_master_link_enable(struct fsi_master *master, int link)
reg = cpu_to_be32(0x80000000 >> bit);
if (!enable)
return fsi_device_write(hub->upstream, FSI_MCENP0 + (4 * idx),
&reg, 4);
rc = fsi_device_write(hub->upstream, FSI_MSENP0 + (4 * idx), &reg, 4);
if (rc)
return rc;
mdelay(FSI_LINK_ENABLE_SETUP_TIME);
fsi_device_read(hub->upstream, FSI_MENP0 + (4 * idx), &reg, 4);
return rc;
return 0;
}
static void hub_master_release(struct device *dev)
@ -271,7 +276,7 @@ static int hub_master_remove(struct device *dev)
return 0;
}
static struct fsi_device_id hub_master_ids[] = {
static const struct fsi_device_id hub_master_ids[] = {
{
.engine_type = FSI_ENGID_HUB_MASTER,
.version = FSI_VERSION_ANY,

View File

@ -130,7 +130,8 @@ struct fsi_master {
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);
int (*link_enable)(struct fsi_master *, int link,
bool enable);
int (*link_config)(struct fsi_master *, int link,
u8 t_send_delay, u8 t_echo_delay);
};

View File

@ -555,7 +555,7 @@ static int occ_probe(struct platform_device *pdev)
hwmon_dev_info.id = occ->idx;
hwmon_dev = platform_device_register_full(&hwmon_dev_info);
if (!hwmon_dev)
if (IS_ERR(hwmon_dev))
dev_warn(dev, "failed to create hwmon device\n");
return 0;

View File

@ -1028,7 +1028,7 @@ static int sbefifo_remove(struct device *dev)
return 0;
}
static struct fsi_device_id sbefifo_ids[] = {
static const struct fsi_device_id sbefifo_ids[] = {
{
.engine_type = FSI_ENGID_SBE,
.version = FSI_VERSION_ANY,

View File

@ -627,7 +627,7 @@ static int scom_remove(struct device *dev)
return 0;
}
static struct fsi_device_id scom_ids[] = {
static const struct fsi_device_id scom_ids[] = {
{
.engine_type = FSI_ENGID_SCOM,
.version = FSI_VERSION_ANY,

View File

@ -620,7 +620,7 @@ static struct attribute *interface_common_attrs[] = {
static umode_t interface_unipro_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct device *dev = kobj_to_dev(kobj);
struct gb_interface *intf = to_gb_interface(dev);
switch (intf->type) {
@ -635,7 +635,7 @@ static umode_t interface_unipro_is_visible(struct kobject *kobj,
static umode_t interface_greybus_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct device *dev = kobj_to_dev(kobj);
struct gb_interface *intf = to_gb_interface(dev);
switch (intf->type) {
@ -649,7 +649,7 @@ static umode_t interface_greybus_is_visible(struct kobject *kobj,
static umode_t interface_power_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct device *dev = kobj_to_dev(kobj);
struct gb_interface *intf = to_gb_interface(dev);
switch (intf->type) {

View File

@ -3,7 +3,7 @@
# Coresight configuration
#
menuconfig CORESIGHT
bool "CoreSight Tracing Support"
tristate "CoreSight Tracing Support"
depends on ARM || ARM64
depends on OF || ACPI
select ARM_AMBA
@ -15,17 +15,24 @@ menuconfig CORESIGHT
specification and configure the right series of components when a
trace source gets enabled.
To compile this driver as a module, choose M here: the
module will be called coresight.
if CORESIGHT
config CORESIGHT_LINKS_AND_SINKS
bool "CoreSight Link and Sink drivers"
tristate "CoreSight Link and Sink drivers"
help
This enables support for CoreSight link and sink drivers that are
responsible for transporting and collecting the trace data
respectively. Link and sinks are dynamically aggregated with a trace
entity at run time to form a complete trace path.
To compile these drivers as modules, choose M here: the
modules will be called coresight-funnel and coresight-replicator.
config CORESIGHT_LINK_AND_SINK_TMC
bool "Coresight generic TMC driver"
tristate "Coresight generic TMC driver"
depends on CORESIGHT_LINKS_AND_SINKS
help
This enables support for the Trace Memory Controller driver.
@ -34,8 +41,11 @@ config CORESIGHT_LINK_AND_SINK_TMC
complies with the generic implementation of the component without
special enhancement or added features.
To compile this driver as a module, choose M here: the
module will be called coresight-tmc.
config CORESIGHT_CATU
bool "Coresight Address Translation Unit (CATU) driver"
tristate "Coresight Address Translation Unit (CATU) driver"
depends on CORESIGHT_LINK_AND_SINK_TMC
help
Enable support for the Coresight Address Translation Unit (CATU).
@ -45,8 +55,11 @@ config CORESIGHT_CATU
by looking up the provided table. CATU can also be used in pass-through
mode where the address is not translated.
To compile this driver as a module, choose M here: the
module will be called coresight-catu.
config CORESIGHT_SINK_TPIU
bool "Coresight generic TPIU driver"
tristate "Coresight generic TPIU driver"
depends on CORESIGHT_LINKS_AND_SINKS
help
This enables support for the Trace Port Interface Unit driver,
@ -56,16 +69,22 @@ config CORESIGHT_SINK_TPIU
connected to an external host for use case capturing more traces than
the on-board coresight memory can handle.
To compile this driver as a module, choose M here: the
module will be called coresight-tpiu.
config CORESIGHT_SINK_ETBV10
bool "Coresight ETBv1.0 driver"
tristate "Coresight ETBv1.0 driver"
depends on CORESIGHT_LINKS_AND_SINKS
help
This enables support for the Embedded Trace Buffer version 1.0 driver
that complies with the generic implementation of the component without
special enhancement or added features.
To compile this driver as a module, choose M here: the
module will be called coresight-etb10.
config CORESIGHT_SOURCE_ETM3X
bool "CoreSight Embedded Trace Macrocell 3.x driver"
tristate "CoreSight Embedded Trace Macrocell 3.x driver"
depends on !ARM64
select CORESIGHT_LINKS_AND_SINKS
help
@ -74,8 +93,11 @@ config CORESIGHT_SOURCE_ETM3X
This is primarily useful for instruction level tracing. Depending
the ETM version data tracing may also be available.
To compile this driver as a module, choose M here: the
module will be called coresight-etm3x.
config CORESIGHT_SOURCE_ETM4X
bool "CoreSight Embedded Trace Macrocell 4.x driver"
tristate "CoreSight Embedded Trace Macrocell 4.x driver"
depends on ARM64
select CORESIGHT_LINKS_AND_SINKS
select PID_IN_CONTEXTIDR
@ -85,8 +107,11 @@ config CORESIGHT_SOURCE_ETM4X
for instruction level tracing. Depending on the implemented version
data tracing may also be available.
To compile this driver as a module, choose M here: the
module will be called coresight-etm4x.
config CORESIGHT_STM
bool "CoreSight System Trace Macrocell driver"
tristate "CoreSight System Trace Macrocell driver"
depends on (ARM && !(CPU_32v3 || CPU_32v4 || CPU_32v4T)) || ARM64
select CORESIGHT_LINKS_AND_SINKS
select STM
@ -96,6 +121,9 @@ config CORESIGHT_STM
logging useful software events or data coming from various entities
in the system, possibly running different OSs
To compile this driver as a module, choose M here: the
module will be called coresight-stm.
config CORESIGHT_CPU_DEBUG
tristate "CoreSight CPU Debug driver"
depends on ARM || ARM64
@ -110,8 +138,11 @@ config CORESIGHT_CPU_DEBUG
properly, please refer Documentation/trace/coresight/coresight-cpu-debug.rst
for detailed description and the example for usage.
To compile this driver as a module, choose M here: the
module will be called coresight-cpu-debug.
config CORESIGHT_CTI
bool "CoreSight Cross Trigger Interface (CTI) driver"
tristate "CoreSight Cross Trigger Interface (CTI) driver"
depends on ARM || ARM64
help
This driver provides support for CoreSight CTI and CTM components.
@ -122,6 +153,9 @@ config CORESIGHT_CTI
halt compared to disabling sources and sinks normally in driver
software.
To compile this driver as a module, choose M here: the
module will be called coresight-cti.
config CORESIGHT_CTI_INTEGRATION_REGS
bool "Access CTI CoreSight Integration Registers"
depends on CORESIGHT_CTI

View File

@ -2,22 +2,24 @@
#
# Makefile for CoreSight drivers.
#
obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o \
coresight-platform.o coresight-sysfs.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \
coresight-tmc-etf.o \
coresight-tmc-etr.o
obj-$(CONFIG_CORESIGHT) += coresight.o
coresight-y := coresight-core.o coresight-etm-perf.o coresight-platform.o \
coresight-sysfs.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o
coresight-tmc-y := coresight-tmc-core.o coresight-tmc-etf.o \
coresight-tmc-etr.o
obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o
obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o
obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \
coresight-replicator.o
obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \
coresight-etm3x-sysfs.o
obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \
coresight-etm4x-sysfs.o
obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o
coresight-etm3x-y := coresight-etm3x-core.o coresight-etm-cp14.o \
coresight-etm3x-sysfs.o
obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o
coresight-etm4x-y := coresight-etm4x-core.o coresight-etm4x-sysfs.o
obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o
obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o
obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o
obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o \
coresight-cti-platform.o \
coresight-cti-sysfs.o
obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
coresight-cti-y := coresight-cti-core.o coresight-cti-platform.o \
coresight-cti-sysfs.o

View File

@ -358,7 +358,7 @@ static int catu_alloc_etr_buf(struct tmc_drvdata *tmc_drvdata,
return 0;
}
const struct etr_buf_operations etr_catu_buf_ops = {
static const struct etr_buf_operations etr_catu_buf_ops = {
.alloc = catu_alloc_etr_buf,
.free = catu_free_etr_buf,
.sync = catu_sync_etr_buf,
@ -567,11 +567,21 @@ out:
return ret;
}
static int __exit catu_remove(struct amba_device *adev)
{
struct catu_drvdata *drvdata = dev_get_drvdata(&adev->dev);
coresight_unregister(drvdata->csdev);
return 0;
}
static struct amba_id catu_ids[] = {
CS_AMBA_ID(0x000bb9ee),
{},
};
MODULE_DEVICE_TABLE(amba, catu_ids);
static struct amba_driver catu_driver = {
.drv = {
.name = "coresight-catu",
@ -579,7 +589,30 @@ static struct amba_driver catu_driver = {
.suppress_bind_attrs = true,
},
.probe = catu_probe,
.remove = catu_remove,
.id_table = catu_ids,
};
builtin_amba_driver(catu_driver);
static int __init catu_init(void)
{
int ret;
ret = amba_driver_register(&catu_driver);
if (ret)
pr_info("Error registering catu driver\n");
tmc_etr_set_catu_ops(&etr_catu_buf_ops);
return ret;
}
static void __exit catu_exit(void)
{
tmc_etr_remove_catu_ops();
amba_driver_unregister(&catu_driver);
}
module_init(catu_init);
module_exit(catu_exit);
MODULE_AUTHOR("Suzuki K Poulose <suzuki.poulose@arm.com>");
MODULE_DESCRIPTION("Arm CoreSight Address Translation Unit (CATU) Driver");
MODULE_LICENSE("GPL v2");

View File

@ -108,6 +108,4 @@ static inline bool coresight_is_catu_device(struct coresight_device *csdev)
return true;
}
extern const struct etr_buf_operations etr_catu_buf_ops;
#endif

View File

@ -53,7 +53,22 @@ static struct list_head *stm_path;
* beginning of the data collected in a buffer. That way the decoder knows that
* it needs to look for another sync sequence.
*/
const u32 barrier_pkt[4] = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff};
const u32 coresight_barrier_pkt[4] = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff};
EXPORT_SYMBOL_GPL(coresight_barrier_pkt);
static const struct cti_assoc_op *cti_assoc_ops;
void coresight_set_cti_ops(const struct cti_assoc_op *cti_op)
{
cti_assoc_ops = cti_op;
}
EXPORT_SYMBOL_GPL(coresight_set_cti_ops);
void coresight_remove_cti_ops(void)
{
cti_assoc_ops = NULL;
}
EXPORT_SYMBOL_GPL(coresight_remove_cti_ops);
static int coresight_id_match(struct device *dev, void *data)
{
@ -179,6 +194,7 @@ int coresight_claim_device_unlocked(void __iomem *base)
coresight_clear_claim_tags(base);
return -EBUSY;
}
EXPORT_SYMBOL_GPL(coresight_claim_device_unlocked);
int coresight_claim_device(void __iomem *base)
{
@ -190,6 +206,7 @@ int coresight_claim_device(void __iomem *base)
return rc;
}
EXPORT_SYMBOL_GPL(coresight_claim_device);
/*
* coresight_disclaim_device_unlocked : Clear the claim tags for the device.
@ -208,6 +225,7 @@ void coresight_disclaim_device_unlocked(void __iomem *base)
*/
WARN_ON_ONCE(1);
}
EXPORT_SYMBOL_GPL(coresight_disclaim_device_unlocked);
void coresight_disclaim_device(void __iomem *base)
{
@ -215,6 +233,7 @@ void coresight_disclaim_device(void __iomem *base)
coresight_disclaim_device_unlocked(base);
CS_LOCK(base);
}
EXPORT_SYMBOL_GPL(coresight_disclaim_device);
/* enable or disable an associated CTI device of the supplied CS device */
static int
@ -222,16 +241,32 @@ coresight_control_assoc_ectdev(struct coresight_device *csdev, bool enable)
{
int ect_ret = 0;
struct coresight_device *ect_csdev = csdev->ect_dev;
struct module *mod;
if (!ect_csdev)
return 0;
if ((!ect_ops(ect_csdev)->enable) || (!ect_ops(ect_csdev)->disable))
return 0;
mod = ect_csdev->dev.parent->driver->owner;
if (enable) {
if (ect_ops(ect_csdev)->enable)
if (try_module_get(mod)) {
ect_ret = ect_ops(ect_csdev)->enable(ect_csdev);
if (ect_ret) {
module_put(mod);
} else {
get_device(ect_csdev->dev.parent);
csdev->ect_enabled = true;
}
} else
ect_ret = -ENODEV;
} else {
if (ect_ops(ect_csdev)->disable)
if (csdev->ect_enabled) {
ect_ret = ect_ops(ect_csdev)->disable(ect_csdev);
put_device(ect_csdev->dev.parent);
module_put(mod);
csdev->ect_enabled = false;
}
}
/* output warning if ECT enable is preventing trace operation */
@ -253,6 +288,7 @@ void coresight_set_assoc_ectdev_mutex(struct coresight_device *csdev,
csdev->ect_dev = ect_csdev;
mutex_unlock(&coresight_mutex);
}
EXPORT_SYMBOL_GPL(coresight_set_assoc_ectdev_mutex);
static int coresight_enable_sink(struct coresight_device *csdev,
u32 mode, void *data)
@ -467,6 +503,7 @@ void coresight_disable_path(struct list_head *path)
{
coresight_disable_path_from(path, NULL);
}
EXPORT_SYMBOL_GPL(coresight_disable_path);
int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data)
{
@ -540,50 +577,46 @@ struct coresight_device *coresight_get_sink(struct list_head *path)
return csdev;
}
static int coresight_enabled_sink(struct device *dev, const void *data)
static struct coresight_device *
coresight_find_enabled_sink(struct coresight_device *csdev)
{
const bool *reset = data;
struct coresight_device *csdev = to_coresight_device(dev);
int i;
struct coresight_device *sink;
if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
csdev->activated) {
/*
* Now that we have a handle on the sink for this session,
* disable the sysFS "enable_sink" flag so that possible
* concurrent perf session that wish to use another sink don't
* trip on it. Doing so has no ramification for the current
* session.
*/
if (*reset)
csdev->activated = false;
csdev->activated)
return csdev;
return 1;
/*
* Recursively explore each port found on this element.
*/
for (i = 0; i < csdev->pdata->nr_outport; i++) {
struct coresight_device *child_dev;
child_dev = csdev->pdata->conns[i].child_dev;
if (child_dev)
sink = coresight_find_enabled_sink(child_dev);
if (sink)
return sink;
}
return 0;
return NULL;
}
/**
* coresight_get_enabled_sink - returns the first enabled sink found on the bus
* @deactivate: Whether the 'enable_sink' flag should be reset
* coresight_get_enabled_sink - returns the first enabled sink using
* connection based search starting from the source reference
*
* When operated from perf the deactivate parameter should be set to 'true'.
* That way the "enabled_sink" flag of the sink that was selected can be reset,
* allowing for other concurrent perf sessions to choose a different sink.
*
* When operated from sysFS users have full control and as such the deactivate
* parameter should be set to 'false', hence mandating users to explicitly
* clear the flag.
* @source: Coresight source device reference
*/
struct coresight_device *coresight_get_enabled_sink(bool deactivate)
struct coresight_device *
coresight_get_enabled_sink(struct coresight_device *source)
{
struct device *dev = NULL;
if (!source)
return NULL;
dev = bus_find_device(&coresight_bustype, NULL, &deactivate,
coresight_enabled_sink);
return dev ? to_coresight_device(dev) : NULL;
return coresight_find_enabled_sink(source);
}
static int coresight_sink_by_id(struct device *dev, const void *data)
@ -627,13 +660,45 @@ struct coresight_device *coresight_get_sink_by_id(u32 id)
return dev ? to_coresight_device(dev) : NULL;
}
/**
* coresight_get_ref- Helper function to increase reference count to module
* and device.
* Return true in successful case and power up the device.
* Return false when failed to get reference of module.
*/
static inline bool coresight_get_ref(struct coresight_device *csdev)
{
struct device *dev = csdev->dev.parent;
/* Make sure the driver can't be removed */
if (!try_module_get(dev->driver->owner))
return false;
/* Make sure the device can't go away */
get_device(dev);
pm_runtime_get_sync(dev);
return true;
}
/**
* coresight_put_ref- Helper function to decrease reference count to module
* and device. Power off the device.
*/
static inline void coresight_put_ref(struct coresight_device *csdev)
{
struct device *dev = csdev->dev.parent;
pm_runtime_put(dev);
put_device(dev);
module_put(dev->driver->owner);
}
/*
* coresight_grab_device - Power up this device and any of the helper
* devices connected to it for trace operation. Since the helper devices
* don't appear on the trace path, they should be handled along with the
* the master device.
*/
static void coresight_grab_device(struct coresight_device *csdev)
static int coresight_grab_device(struct coresight_device *csdev)
{
int i;
@ -642,9 +707,20 @@ static void coresight_grab_device(struct coresight_device *csdev)
child = csdev->pdata->conns[i].child_dev;
if (child && child->type == CORESIGHT_DEV_TYPE_HELPER)
pm_runtime_get_sync(child->dev.parent);
if (!coresight_get_ref(child))
goto err;
}
pm_runtime_get_sync(csdev->dev.parent);
if (coresight_get_ref(csdev))
return 0;
err:
for (i--; i >= 0; i--) {
struct coresight_device *child;
child = csdev->pdata->conns[i].child_dev;
if (child && child->type == CORESIGHT_DEV_TYPE_HELPER)
coresight_put_ref(child);
}
return -ENODEV;
}
/*
@ -655,13 +731,13 @@ static void coresight_drop_device(struct coresight_device *csdev)
{
int i;
pm_runtime_put(csdev->dev.parent);
coresight_put_ref(csdev);
for (i = 0; i < csdev->pdata->nr_outport; i++) {
struct coresight_device *child;
child = csdev->pdata->conns[i].child_dev;
if (child && child->type == CORESIGHT_DEV_TYPE_HELPER)
pm_runtime_put(child->dev.parent);
coresight_put_ref(child);
}
}
@ -680,7 +756,7 @@ static int _coresight_build_path(struct coresight_device *csdev,
struct coresight_device *sink,
struct list_head *path)
{
int i;
int i, ret;
bool found = false;
struct coresight_node *node;
@ -710,11 +786,14 @@ out:
* is tell the PM runtime core we need this element and add a node
* for it.
*/
ret = coresight_grab_device(csdev);
if (ret)
return ret;
node = kzalloc(sizeof(struct coresight_node), GFP_KERNEL);
if (!node)
return -ENOMEM;
coresight_grab_device(csdev);
node->csdev = csdev;
list_add(&node->link, path);
@ -988,11 +1067,7 @@ int coresight_enable(struct coresight_device *csdev)
goto out;
}
/*
* Search for a valid sink for this session but don't reset the
* "enable_sink" flag in sysFS. Users get to do that explicitly.
*/
sink = coresight_get_enabled_sink(false);
sink = coresight_get_enabled_sink(csdev);
if (!sink) {
ret = -EINVAL;
goto out;
@ -1188,7 +1263,6 @@ static void coresight_device_release(struct device *dev)
{
struct coresight_device *csdev = to_coresight_device(dev);
cti_remove_assoc_from_csdev(csdev);
fwnode_handle_put(csdev->dev.fwnode);
kfree(csdev->refcnt);
kfree(csdev);
@ -1376,16 +1450,7 @@ int coresight_timeout(void __iomem *addr, u32 offset, int position, int value)
return -EAGAIN;
}
struct bus_type coresight_bustype = {
.name = "coresight",
};
static int __init coresight_init(void)
{
return bus_register(&coresight_bustype);
}
postcore_initcall(coresight_init);
EXPORT_SYMBOL_GPL(coresight_timeout);
/*
* coresight_release_platform_data: Release references to the devices connected
@ -1498,8 +1563,8 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
ret = coresight_fixup_device_conns(csdev);
if (!ret)
ret = coresight_fixup_orphan_conns(csdev);
if (!ret)
cti_add_assoc_to_csdev(csdev);
if (!ret && cti_assoc_ops && cti_assoc_ops->add)
cti_assoc_ops->add(csdev);
mutex_unlock(&coresight_mutex);
if (ret) {
@ -1522,6 +1587,8 @@ void coresight_unregister(struct coresight_device *csdev)
{
etm_perf_del_symlink_sink(csdev);
/* Remove references of that device in the topology */
if (cti_assoc_ops && cti_assoc_ops->remove)
cti_assoc_ops->remove(csdev);
coresight_remove_conns(csdev);
coresight_clear_default_sink(csdev);
coresight_release_platform_data(csdev, csdev->pdata);
@ -1552,6 +1619,7 @@ bool coresight_loses_context_with_cpu(struct device *dev)
return fwnode_property_present(dev_fwnode(dev),
"arm,coresight-loses-context-with-cpu");
}
EXPORT_SYMBOL_GPL(coresight_loses_context_with_cpu);
/*
* coresight_alloc_device_name - Get an index for a given device in the
@ -1592,3 +1660,35 @@ done:
return name;
}
EXPORT_SYMBOL_GPL(coresight_alloc_device_name);
struct bus_type coresight_bustype = {
.name = "coresight",
};
static int __init coresight_init(void)
{
int ret;
ret = bus_register(&coresight_bustype);
if (ret)
return ret;
ret = etm_perf_init();
if (ret)
bus_unregister(&coresight_bustype);
return ret;
}
static void __exit coresight_exit(void)
{
etm_perf_exit();
bus_unregister(&coresight_bustype);
}
module_init(coresight_init);
module_exit(coresight_exit);
MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
MODULE_DESCRIPTION("Arm CoreSight tracer driver");

View File

@ -665,6 +665,8 @@ static const struct amba_id debug_ids[] = {
{},
};
MODULE_DEVICE_TABLE(amba, debug_ids);
static struct amba_driver debug_driver = {
.drv = {
.name = "coresight-cpu-debug",

View File

@ -86,22 +86,16 @@ void cti_write_all_hw_regs(struct cti_drvdata *drvdata)
CS_LOCK(drvdata->base);
}
static void cti_enable_hw_smp_call(void *info)
{
struct cti_drvdata *drvdata = info;
cti_write_all_hw_regs(drvdata);
}
/* write regs to hardware and enable */
static int cti_enable_hw(struct cti_drvdata *drvdata)
{
struct cti_config *config = &drvdata->config;
struct device *dev = &drvdata->csdev->dev;
unsigned long flags;
int rc = 0;
pm_runtime_get_sync(dev->parent);
spin_lock(&drvdata->spinlock);
spin_lock_irqsave(&drvdata->spinlock, flags);
/* no need to do anything if enabled or unpowered*/
if (config->hw_enabled || !config->hw_powered)
@ -112,19 +106,11 @@ static int cti_enable_hw(struct cti_drvdata *drvdata)
if (rc)
goto cti_err_not_enabled;
if (drvdata->ctidev.cpu >= 0) {
rc = smp_call_function_single(drvdata->ctidev.cpu,
cti_enable_hw_smp_call,
drvdata, 1);
if (rc)
goto cti_err_not_enabled;
} else {
cti_write_all_hw_regs(drvdata);
}
cti_write_all_hw_regs(drvdata);
config->hw_enabled = true;
atomic_inc(&drvdata->config.enable_req_count);
spin_unlock(&drvdata->spinlock);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return rc;
cti_state_unchanged:
@ -132,7 +118,7 @@ cti_state_unchanged:
/* cannot enable due to error */
cti_err_not_enabled:
spin_unlock(&drvdata->spinlock);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
pm_runtime_put(dev->parent);
return rc;
}
@ -141,9 +127,7 @@ cti_err_not_enabled:
static void cti_cpuhp_enable_hw(struct cti_drvdata *drvdata)
{
struct cti_config *config = &drvdata->config;
struct device *dev = &drvdata->csdev->dev;
pm_runtime_get_sync(dev->parent);
spin_lock(&drvdata->spinlock);
config->hw_powered = true;
@ -163,7 +147,6 @@ static void cti_cpuhp_enable_hw(struct cti_drvdata *drvdata)
/* did not re-enable due to no claim / no request */
cti_hp_not_enabled:
spin_unlock(&drvdata->spinlock);
pm_runtime_put(dev->parent);
}
/* disable hardware */
@ -511,12 +494,15 @@ static bool cti_add_sysfs_link(struct cti_drvdata *drvdata,
return !link_err;
}
static void cti_remove_sysfs_link(struct cti_trig_con *tc)
static void cti_remove_sysfs_link(struct cti_drvdata *drvdata,
struct cti_trig_con *tc)
{
struct coresight_sysfs_link link_info;
link_info.orig = drvdata->csdev;
link_info.orig_name = tc->con_dev_name;
link_info.target = tc->con_dev;
link_info.target_name = dev_name(&drvdata->csdev->dev);
coresight_remove_sysfs_link(&link_info);
}
@ -556,7 +542,7 @@ cti_match_fixup_csdev(struct cti_device *ctidev, const char *node_name,
* This will set the association if CTI declared before the CS device.
* (called from coresight_register() with coresight_mutex locked).
*/
void cti_add_assoc_to_csdev(struct coresight_device *csdev)
static void cti_add_assoc_to_csdev(struct coresight_device *csdev)
{
struct cti_drvdata *ect_item;
struct cti_device *ctidev;
@ -589,13 +575,12 @@ void cti_add_assoc_to_csdev(struct coresight_device *csdev)
cti_add_done:
mutex_unlock(&ect_mutex);
}
EXPORT_SYMBOL_GPL(cti_add_assoc_to_csdev);
/*
* Removing the associated devices is easier.
* A CTI will not have a value for csdev->ect_dev.
*/
void cti_remove_assoc_from_csdev(struct coresight_device *csdev)
static void cti_remove_assoc_from_csdev(struct coresight_device *csdev)
{
struct cti_drvdata *ctidrv;
struct cti_trig_con *tc;
@ -606,8 +591,8 @@ void cti_remove_assoc_from_csdev(struct coresight_device *csdev)
ctidrv = csdev_to_cti_drvdata(csdev->ect_dev);
ctidev = &ctidrv->ctidev;
list_for_each_entry(tc, &ctidev->trig_cons, node) {
if (tc->con_dev == csdev->ect_dev) {
cti_remove_sysfs_link(tc);
if (tc->con_dev == csdev) {
cti_remove_sysfs_link(ctidrv, tc);
tc->con_dev = NULL;
break;
}
@ -616,7 +601,15 @@ void cti_remove_assoc_from_csdev(struct coresight_device *csdev)
}
mutex_unlock(&ect_mutex);
}
EXPORT_SYMBOL_GPL(cti_remove_assoc_from_csdev);
/*
* Operations to add and remove associated CTI.
* Register to coresight core driver as call back function.
*/
static struct cti_assoc_op cti_assoc_ops = {
.add = cti_add_assoc_to_csdev,
.remove = cti_remove_assoc_from_csdev
};
/*
* Update the cross references where the associated device was found
@ -651,7 +644,7 @@ static void cti_remove_conn_xrefs(struct cti_drvdata *drvdata)
if (tc->con_dev) {
coresight_set_assoc_ectdev_mutex(tc->con_dev,
NULL);
cti_remove_sysfs_link(tc);
cti_remove_sysfs_link(drvdata, tc);
tc->con_dev = NULL;
}
}
@ -742,7 +735,8 @@ static int cti_dying_cpu(unsigned int cpu)
spin_lock(&drvdata->spinlock);
drvdata->config.hw_powered = false;
coresight_disclaim_device(drvdata->base);
if (drvdata->config.hw_enabled)
coresight_disclaim_device(drvdata->base);
spin_unlock(&drvdata->spinlock);
return 0;
}
@ -828,7 +822,6 @@ static void cti_device_release(struct device *dev)
struct cti_drvdata *ect_item, *ect_tmp;
mutex_lock(&ect_mutex);
cti_remove_conn_xrefs(drvdata);
cti_pm_release(drvdata);
/* remove from the list */
@ -843,6 +836,18 @@ static void cti_device_release(struct device *dev)
if (drvdata->csdev_release)
drvdata->csdev_release(dev);
}
static int __exit cti_remove(struct amba_device *adev)
{
struct cti_drvdata *drvdata = dev_get_drvdata(&adev->dev);
mutex_lock(&ect_mutex);
cti_remove_conn_xrefs(drvdata);
mutex_unlock(&ect_mutex);
coresight_unregister(drvdata->csdev);
return 0;
}
static int cti_probe(struct amba_device *adev, const struct amba_id *id)
{
@ -963,6 +968,8 @@ static const struct amba_id cti_ids[] = {
{ 0, 0},
};
MODULE_DEVICE_TABLE(amba, cti_ids);
static struct amba_driver cti_driver = {
.drv = {
.name = "coresight-cti",
@ -970,6 +977,30 @@ static struct amba_driver cti_driver = {
.suppress_bind_attrs = true,
},
.probe = cti_probe,
.remove = cti_remove,
.id_table = cti_ids,
};
builtin_amba_driver(cti_driver);
static int __init cti_init(void)
{
int ret;
ret = amba_driver_register(&cti_driver);
if (ret)
pr_info("Error registering cti driver\n");
coresight_set_cti_ops(&cti_assoc_ops);
return ret;
}
static void __exit cti_exit(void)
{
coresight_remove_cti_ops();
amba_driver_unregister(&cti_driver);
}
module_init(cti_init);
module_exit(cti_exit);
MODULE_AUTHOR("Mike Leach <mike.leach@linaro.org>");
MODULE_DESCRIPTION("Arm CoreSight CTI Driver");
MODULE_LICENSE("GPL v2");

View File

@ -525,7 +525,7 @@ static unsigned long etb_update_buffer(struct coresight_device *csdev,
cur = buf->cur;
offset = buf->offset;
barrier = barrier_pkt;
barrier = coresight_barrier_pkt;
for (i = 0; i < to_read; i += 4) {
buf_ptr = buf->data_pages[cur] + offset;
@ -801,6 +801,21 @@ err_misc_register:
return ret;
}
static int __exit etb_remove(struct amba_device *adev)
{
struct etb_drvdata *drvdata = dev_get_drvdata(&adev->dev);
/*
* Since misc_open() holds a refcount on the f_ops, which is
* etb fops in this case, device is there until last file
* handler to this device is closed.
*/
misc_deregister(&drvdata->miscdev);
coresight_unregister(drvdata->csdev);
return 0;
}
#ifdef CONFIG_PM
static int etb_runtime_suspend(struct device *dev)
{
@ -835,6 +850,8 @@ static const struct amba_id etb_ids[] = {
{ 0, 0},
};
MODULE_DEVICE_TABLE(amba, etb_ids);
static struct amba_driver etb_driver = {
.drv = {
.name = "coresight-etb10",
@ -844,6 +861,13 @@ static struct amba_driver etb_driver = {
},
.probe = etb_probe,
.remove = etb_remove,
.id_table = etb_ids,
};
builtin_amba_driver(etb_driver);
module_amba_driver(etb_driver);
MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
MODULE_DESCRIPTION("Arm CoreSight Embedded Trace Buffer driver");
MODULE_LICENSE("GPL v2");

View File

@ -126,10 +126,10 @@ static void free_sink_buffer(struct etm_event_data *event_data)
cpumask_t *mask = &event_data->mask;
struct coresight_device *sink;
if (WARN_ON(cpumask_empty(mask)))
if (!event_data->snk_config)
return;
if (!event_data->snk_config)
if (WARN_ON(cpumask_empty(mask)))
return;
cpu = cpumask_first(mask);
@ -222,8 +222,6 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
if (event->attr.config2) {
id = (u32)event->attr.config2;
sink = coresight_get_sink_by_id(id);
} else {
sink = coresight_get_enabled_sink(true);
}
mask = &event_data->mask;
@ -321,6 +319,16 @@ static void etm_event_start(struct perf_event *event, int flags)
if (!event_data)
goto fail;
/*
* Check if this ETM is allowed to trace, as decided
* at etm_setup_aux(). This could be due to an unreachable
* sink from this ETM. We can't do much in this case if
* the sink was specified or hinted to the driver. For
* now, simply don't record anything on this ETM.
*/
if (!cpumask_test_cpu(cpu, &event_data->mask))
goto fail_end_stop;
path = etm_event_cpu_path(event_data, cpu);
/* We need a sink, no need to continue without one */
sink = coresight_get_sink(path);
@ -517,6 +525,7 @@ int etm_perf_symlink(struct coresight_device *csdev, bool link)
return 0;
}
EXPORT_SYMBOL_GPL(etm_perf_symlink);
static ssize_t etm_perf_sink_name_show(struct device *dev,
struct device_attribute *dattr,
@ -590,7 +599,7 @@ void etm_perf_del_symlink_sink(struct coresight_device *csdev)
csdev->ea = NULL;
}
static int __init etm_perf_init(void)
int __init etm_perf_init(void)
{
int ret;
@ -617,4 +626,8 @@ static int __init etm_perf_init(void)
return ret;
}
device_initcall(etm_perf_init);
void __exit etm_perf_exit(void)
{
perf_pmu_unregister(&etm_pmu);
}

View File

@ -57,7 +57,7 @@ struct etm_event_data {
struct list_head * __percpu *path;
};
#ifdef CONFIG_CORESIGHT
#if IS_ENABLED(CONFIG_CORESIGHT)
int etm_perf_symlink(struct coresight_device *csdev, bool link);
int etm_perf_add_symlink_sink(struct coresight_device *csdev);
void etm_perf_del_symlink_sink(struct coresight_device *csdev);
@ -82,4 +82,7 @@ static inline void *etm_perf_sink_config(struct perf_output_handle *handle)
#endif /* CONFIG_CORESIGHT */
int __init etm_perf_init(void);
void __exit etm_perf_exit(void);
#endif

View File

@ -40,8 +40,6 @@
static int boot_enable;
module_param_named(boot_enable, boot_enable, int, S_IRUGO);
/* The number of ETM/PTM currently registered */
static int etm_count;
static struct etm_drvdata *etmdrvdata[NR_CPUS];
static enum cpuhp_state hp_online;
@ -782,6 +780,42 @@ static void etm_init_trace_id(struct etm_drvdata *drvdata)
drvdata->traceid = coresight_get_trace_id(drvdata->cpu);
}
static int __init etm_hp_setup(void)
{
int ret;
ret = cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ARM_CORESIGHT_STARTING,
"arm/coresight:starting",
etm_starting_cpu, etm_dying_cpu);
if (ret)
return ret;
ret = cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ONLINE_DYN,
"arm/coresight:online",
etm_online_cpu, NULL);
/* HP dyn state ID returned in ret on success */
if (ret > 0) {
hp_online = ret;
return 0;
}
/* failed dyn state - remove others */
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
return ret;
}
static void etm_hp_clear(void)
{
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
if (hp_online) {
cpuhp_remove_state_nocalls(hp_online);
hp_online = 0;
}
}
static int etm_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret;
@ -823,39 +857,20 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
if (!desc.name)
return -ENOMEM;
cpus_read_lock();
etmdrvdata[drvdata->cpu] = drvdata;
if (smp_call_function_single(drvdata->cpu,
etm_init_arch_data, drvdata, 1))
dev_err(dev, "ETM arch init failed\n");
if (!etm_count++) {
cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ARM_CORESIGHT_STARTING,
"arm/coresight:starting",
etm_starting_cpu, etm_dying_cpu);
ret = cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ONLINE_DYN,
"arm/coresight:online",
etm_online_cpu, NULL);
if (ret < 0)
goto err_arch_supported;
hp_online = ret;
}
cpus_read_unlock();
if (etm_arch_supported(drvdata->arch) == false) {
ret = -EINVAL;
goto err_arch_supported;
}
if (etm_arch_supported(drvdata->arch) == false)
return -EINVAL;
etm_init_trace_id(drvdata);
etm_set_default(&drvdata->config);
pdata = coresight_get_platform_data(dev);
if (IS_ERR(pdata)) {
ret = PTR_ERR(pdata);
goto err_arch_supported;
}
if (IS_ERR(pdata))
return PTR_ERR(pdata);
adev->dev.platform_data = pdata;
desc.type = CORESIGHT_DEV_TYPE_SOURCE;
@ -865,17 +880,17 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
desc.dev = dev;
desc.groups = coresight_etm_groups;
drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
goto err_arch_supported;
}
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
ret = etm_perf_symlink(drvdata->csdev, true);
if (ret) {
coresight_unregister(drvdata->csdev);
goto err_arch_supported;
return ret;
}
etmdrvdata[drvdata->cpu] = drvdata;
pm_runtime_put(&adev->dev);
dev_info(&drvdata->csdev->dev,
"%s initialized\n", (char *)coresight_get_uci_data(id));
@ -885,14 +900,40 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
}
return 0;
}
err_arch_supported:
if (--etm_count == 0) {
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
if (hp_online)
cpuhp_remove_state_nocalls(hp_online);
}
return ret;
static void __exit clear_etmdrvdata(void *info)
{
int cpu = *(int *)info;
etmdrvdata[cpu] = NULL;
}
static int __exit etm_remove(struct amba_device *adev)
{
struct etm_drvdata *drvdata = dev_get_drvdata(&adev->dev);
etm_perf_symlink(drvdata->csdev, false);
/*
* Taking hotplug lock here to avoid racing between etm_remove and
* CPU hotplug call backs.
*/
cpus_read_lock();
/*
* The readers for etmdrvdata[] are CPU hotplug call backs
* and PM notification call backs. Change etmdrvdata[i] on
* CPU i ensures these call backs has consistent view
* inside one call back function.
*/
if (smp_call_function_single(drvdata->cpu, clear_etmdrvdata, &drvdata->cpu, 1))
etmdrvdata[drvdata->cpu] = NULL;
cpus_read_unlock();
coresight_unregister(drvdata->csdev);
return 0;
}
#ifdef CONFIG_PM
@ -937,6 +978,8 @@ static const struct amba_id etm_ids[] = {
{ 0, 0},
};
MODULE_DEVICE_TABLE(amba, etm_ids);
static struct amba_driver etm_driver = {
.drv = {
.name = "coresight-etm3x",
@ -945,6 +988,39 @@ static struct amba_driver etm_driver = {
.suppress_bind_attrs = true,
},
.probe = etm_probe,
.remove = etm_remove,
.id_table = etm_ids,
};
builtin_amba_driver(etm_driver);
static int __init etm_init(void)
{
int ret;
ret = etm_hp_setup();
/* etm_hp_setup() does its own cleanup - exit on error */
if (ret)
return ret;
ret = amba_driver_register(&etm_driver);
if (ret) {
pr_err("Error registering etm3x driver\n");
etm_hp_clear();
}
return ret;
}
static void __exit etm_exit(void)
{
amba_driver_unregister(&etm_driver);
etm_hp_clear();
}
module_init(etm_init);
module_exit(etm_exit);
MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
MODULE_DESCRIPTION("Arm CoreSight Program Flow Trace driver");
MODULE_LICENSE("GPL v2");

View File

@ -48,12 +48,11 @@ module_param(pm_save_enable, int, 0444);
MODULE_PARM_DESC(pm_save_enable,
"Save/restore state on power down: 1 = never, 2 = self-hosted");
/* The number of ETMv4 currently registered */
static int etm4_count;
static struct etmv4_drvdata *etmdrvdata[NR_CPUS];
static void etm4_set_default_config(struct etmv4_config *config);
static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
struct perf_event *event);
static u64 etm4_get_access_type(struct etmv4_config *config);
static enum cpuhp_state hp_online;
@ -743,8 +742,14 @@ static void etm4_init_arch_data(void *info)
* The number of resource pairs conveyed by the HW starts at 0, i.e a
* value of 0x0 indicate 1 resource pair, 0x1 indicate two and so on.
* As such add 1 to the value of NUMRSPAIR for a better representation.
*
* For ETM v4.3 and later, 0x0 means 0, and no pairs are available -
* the default TRUE and FALSE resource selectors are omitted.
* Otherwise for values 0x1 and above the number is N + 1 as per v4.2.
*/
drvdata->nr_resource = BMVAL(etmidr4, 16, 19) + 1;
drvdata->nr_resource = BMVAL(etmidr4, 16, 19);
if ((drvdata->arch < ETM4X_ARCH_4V3) || (drvdata->nr_resource > 0))
drvdata->nr_resource += 1;
/*
* NUMSSCC, bits[23:20] the number of single-shot
* comparator control for tracing. Read any status regs as these
@ -785,6 +790,22 @@ static void etm4_init_arch_data(void *info)
CS_LOCK(drvdata->base);
}
/* Set ELx trace filter access in the TRCVICTLR register */
static void etm4_set_victlr_access(struct etmv4_config *config)
{
u64 access_type;
config->vinst_ctrl &= ~(ETM_EXLEVEL_S_VICTLR_MASK | ETM_EXLEVEL_NS_VICTLR_MASK);
/*
* TRCVICTLR::EXLEVEL_NS:EXLEVELS: Set kernel / user filtering
* bits in vinst_ctrl, same bit pattern as TRCACATRn values returned by
* etm4_get_access_type() but with a relative shift in this register.
*/
access_type = etm4_get_access_type(config) << ETM_EXLEVEL_LSHIFT_TRCVICTLR;
config->vinst_ctrl |= (u32)access_type;
}
static void etm4_set_default_config(struct etmv4_config *config)
{
/* disable all events tracing */
@ -802,6 +823,9 @@ static void etm4_set_default_config(struct etmv4_config *config)
/* TRCVICTLR::EVENT = 0x01, select the always on logic */
config->vinst_ctrl = BIT(0);
/* TRCVICTLR::EXLEVEL_NS:EXLEVELS: Set kernel / user filtering */
etm4_set_victlr_access(config);
}
static u64 etm4_get_ns_access_type(struct etmv4_config *config)
@ -1066,7 +1090,7 @@ out:
void etm4_config_trace_mode(struct etmv4_config *config)
{
u32 addr_acc, mode;
u32 mode;
mode = config->mode;
mode &= (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER);
@ -1078,15 +1102,7 @@ void etm4_config_trace_mode(struct etmv4_config *config)
if (!(mode & ETM_MODE_EXCL_KERN) && !(mode & ETM_MODE_EXCL_USER))
return;
addr_acc = config->addr_acc[ETM_DEFAULT_ADDR_COMP];
/* clear default config */
addr_acc &= ~(ETM_EXLEVEL_NS_APP | ETM_EXLEVEL_NS_OS |
ETM_EXLEVEL_NS_HYP);
addr_acc |= etm4_get_ns_access_type(config);
config->addr_acc[ETM_DEFAULT_ADDR_COMP] = addr_acc;
config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] = addr_acc;
etm4_set_victlr_access(config);
}
static int etm4_online_cpu(unsigned int cpu)
@ -1183,7 +1199,7 @@ static int etm4_cpu_save(struct etmv4_drvdata *drvdata)
state->trcvdsacctlr = readl(drvdata->base + TRCVDSACCTLR);
state->trcvdarcctlr = readl(drvdata->base + TRCVDARCCTLR);
for (i = 0; i < drvdata->nrseqstate; i++)
for (i = 0; i < drvdata->nrseqstate - 1; i++)
state->trcseqevr[i] = readl(drvdata->base + TRCSEQEVRn(i));
state->trcseqrstevr = readl(drvdata->base + TRCSEQRSTEVR);
@ -1227,7 +1243,7 @@ static int etm4_cpu_save(struct etmv4_drvdata *drvdata)
state->trccidcctlr1 = readl(drvdata->base + TRCCIDCCTLR1);
state->trcvmidcctlr0 = readl(drvdata->base + TRCVMIDCCTLR0);
state->trcvmidcctlr0 = readl(drvdata->base + TRCVMIDCCTLR1);
state->trcvmidcctlr1 = readl(drvdata->base + TRCVMIDCCTLR1);
state->trcclaimset = readl(drvdata->base + TRCCLAIMCLR);
@ -1288,7 +1304,7 @@ static void etm4_cpu_restore(struct etmv4_drvdata *drvdata)
writel_relaxed(state->trcvdsacctlr, drvdata->base + TRCVDSACCTLR);
writel_relaxed(state->trcvdarcctlr, drvdata->base + TRCVDARCCTLR);
for (i = 0; i < drvdata->nrseqstate; i++)
for (i = 0; i < drvdata->nrseqstate - 1; i++)
writel_relaxed(state->trcseqevr[i],
drvdata->base + TRCSEQEVRn(i));
@ -1337,7 +1353,7 @@ static void etm4_cpu_restore(struct etmv4_drvdata *drvdata)
writel_relaxed(state->trccidcctlr1, drvdata->base + TRCCIDCCTLR1);
writel_relaxed(state->trcvmidcctlr0, drvdata->base + TRCVMIDCCTLR0);
writel_relaxed(state->trcvmidcctlr0, drvdata->base + TRCVMIDCCTLR1);
writel_relaxed(state->trcvmidcctlr1, drvdata->base + TRCVMIDCCTLR1);
writel_relaxed(state->trcclaimset, drvdata->base + TRCCLAIMSET);
@ -1397,28 +1413,25 @@ static struct notifier_block etm4_cpu_pm_nb = {
.notifier_call = etm4_cpu_pm_notify,
};
/* Setup PM. Called with cpus locked. Deals with error conditions and counts */
static int etm4_pm_setup_cpuslocked(void)
/* Setup PM. Deals with error conditions and counts */
static int __init etm4_pm_setup(void)
{
int ret;
if (etm4_count++)
return 0;
ret = cpu_pm_register_notifier(&etm4_cpu_pm_nb);
if (ret)
goto reduce_count;
return ret;
ret = cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ARM_CORESIGHT_STARTING,
"arm/coresight4:starting",
etm4_starting_cpu, etm4_dying_cpu);
ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING,
"arm/coresight4:starting",
etm4_starting_cpu, etm4_dying_cpu);
if (ret)
goto unregister_notifier;
ret = cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ONLINE_DYN,
"arm/coresight4:online",
etm4_online_cpu, NULL);
ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
"arm/coresight4:online",
etm4_online_cpu, NULL);
/* HP dyn state ID returned in ret on success */
if (ret > 0) {
@ -1427,21 +1440,15 @@ static int etm4_pm_setup_cpuslocked(void)
}
/* failed dyn state - remove others */
cpuhp_remove_state_nocalls_cpuslocked(CPUHP_AP_ARM_CORESIGHT_STARTING);
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
unregister_notifier:
cpu_pm_unregister_notifier(&etm4_cpu_pm_nb);
reduce_count:
--etm4_count;
return ret;
}
static void etm4_pm_clear(void)
{
if (--etm4_count != 0)
return;
cpu_pm_unregister_notifier(&etm4_cpu_pm_nb);
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
if (hp_online) {
@ -1497,35 +1504,20 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
if (!desc.name)
return -ENOMEM;
cpus_read_lock();
etmdrvdata[drvdata->cpu] = drvdata;
if (smp_call_function_single(drvdata->cpu,
etm4_init_arch_data, drvdata, 1))
dev_err(dev, "ETM arch init failed\n");
ret = etm4_pm_setup_cpuslocked();
cpus_read_unlock();
/* etm4_pm_setup_cpuslocked() does its own cleanup - exit on error */
if (ret) {
etmdrvdata[drvdata->cpu] = NULL;
return ret;
}
if (etm4_arch_supported(drvdata->arch) == false) {
ret = -EINVAL;
goto err_arch_supported;
}
if (etm4_arch_supported(drvdata->arch) == false)
return -EINVAL;
etm4_init_trace_id(drvdata);
etm4_set_default(&drvdata->config);
pdata = coresight_get_platform_data(dev);
if (IS_ERR(pdata)) {
ret = PTR_ERR(pdata);
goto err_arch_supported;
}
if (IS_ERR(pdata))
return PTR_ERR(pdata);
adev->dev.platform_data = pdata;
desc.type = CORESIGHT_DEV_TYPE_SOURCE;
@ -1535,17 +1527,17 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
desc.dev = dev;
desc.groups = coresight_etmv4_groups;
drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
goto err_arch_supported;
}
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
ret = etm_perf_symlink(drvdata->csdev, true);
if (ret) {
coresight_unregister(drvdata->csdev);
goto err_arch_supported;
return ret;
}
etmdrvdata[drvdata->cpu] = drvdata;
pm_runtime_put(&adev->dev);
dev_info(&drvdata->csdev->dev, "CPU%d: ETM v%d.%d initialized\n",
drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf);
@ -1556,11 +1548,6 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
}
return 0;
err_arch_supported:
etmdrvdata[drvdata->cpu] = NULL;
etm4_pm_clear();
return ret;
}
static struct amba_cs_uci_id uci_id_etm4[] = {
@ -1572,6 +1559,40 @@ static struct amba_cs_uci_id uci_id_etm4[] = {
}
};
static void __exit clear_etmdrvdata(void *info)
{
int cpu = *(int *)info;
etmdrvdata[cpu] = NULL;
}
static int __exit etm4_remove(struct amba_device *adev)
{
struct etmv4_drvdata *drvdata = dev_get_drvdata(&adev->dev);
etm_perf_symlink(drvdata->csdev, false);
/*
* Taking hotplug lock here to avoid racing between etm4_remove and
* CPU hotplug call backs.
*/
cpus_read_lock();
/*
* The readers for etmdrvdata[] are CPU hotplug call backs
* and PM notification call backs. Change etmdrvdata[i] on
* CPU i ensures these call backs has consistent view
* inside one call back function.
*/
if (smp_call_function_single(drvdata->cpu, clear_etmdrvdata, &drvdata->cpu, 1))
etmdrvdata[drvdata->cpu] = NULL;
cpus_read_unlock();
coresight_unregister(drvdata->csdev);
return 0;
}
static const struct amba_id etm4_ids[] = {
CS_AMBA_ID(0x000bb95d), /* Cortex-A53 */
CS_AMBA_ID(0x000bb95e), /* Cortex-A57 */
@ -1586,15 +1607,53 @@ static const struct amba_id etm4_ids[] = {
CS_AMBA_UCI_ID(0x000bb805, uci_id_etm4),/* Qualcomm Kryo 4XX Cortex-A55 */
CS_AMBA_UCI_ID(0x000bb804, uci_id_etm4),/* Qualcomm Kryo 4XX Cortex-A76 */
CS_AMBA_UCI_ID(0x000cc0af, uci_id_etm4),/* Marvell ThunderX2 */
CS_AMBA_UCI_ID(0x000b6d01, uci_id_etm4),/* HiSilicon-Hip08 */
CS_AMBA_UCI_ID(0x000b6d02, uci_id_etm4),/* HiSilicon-Hip09 */
{},
};
MODULE_DEVICE_TABLE(amba, etm4_ids);
static struct amba_driver etm4x_driver = {
.drv = {
.name = "coresight-etm4x",
.owner = THIS_MODULE,
.suppress_bind_attrs = true,
},
.probe = etm4_probe,
.remove = etm4_remove,
.id_table = etm4_ids,
};
builtin_amba_driver(etm4x_driver);
static int __init etm4x_init(void)
{
int ret;
ret = etm4_pm_setup();
/* etm4_pm_setup() does its own cleanup - exit on error */
if (ret)
return ret;
ret = amba_driver_register(&etm4x_driver);
if (ret) {
pr_err("Error registering etm4x driver\n");
etm4_pm_clear();
}
return ret;
}
static void __exit etm4x_exit(void)
{
amba_driver_unregister(&etm4x_driver);
etm4_pm_clear();
}
module_init(etm4x_init);
module_exit(etm4x_exit);
MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
MODULE_DESCRIPTION("Arm CoreSight Program Flow Trace v4.x driver");
MODULE_LICENSE("GPL v2");

View File

@ -206,7 +206,7 @@ static ssize_t reset_store(struct device *dev,
* each trace run.
*/
config->vinst_ctrl = BIT(0);
if (drvdata->nr_addr_cmp == true) {
if (drvdata->nr_addr_cmp > 0) {
config->mode |= ETM_MODE_VIEWINST_STARTSTOP;
/* SSSTATUS, bit[9] */
config->vinst_ctrl |= BIT(9);
@ -236,7 +236,7 @@ static ssize_t reset_store(struct device *dev,
}
config->res_idx = 0x0;
for (i = 0; i < drvdata->nr_resource; i++)
for (i = 2; i < 2 * drvdata->nr_resource; i++)
config->res_ctrl[i] = 0x0;
config->ss_idx = 0x0;
@ -1663,8 +1663,11 @@ static ssize_t res_idx_store(struct device *dev,
if (kstrtoul(buf, 16, &val))
return -EINVAL;
/* Resource selector pair 0 is always implemented and reserved */
if ((val == 0) || (val >= drvdata->nr_resource))
/*
* Resource selector pair 0 is always implemented and reserved,
* namely an idx with 0 and 1 is illegal.
*/
if ((val < 2) || (val >= 2 * drvdata->nr_resource))
return -EINVAL;
/*

View File

@ -192,11 +192,17 @@
#define ETM_EXLEVEL_NS_HYP BIT(14)
#define ETM_EXLEVEL_NS_NA BIT(15)
/* access level control in TRCVICTLR - same bits as TRCACATRn but shifted */
#define ETM_EXLEVEL_LSHIFT_TRCVICTLR 8
/* secure / non secure masks - TRCVICTLR, IDR3 */
#define ETM_EXLEVEL_S_VICTLR_MASK GENMASK(19, 16)
/* NS MON (EL3) mode never implemented */
#define ETM_EXLEVEL_NS_VICTLR_MASK GENMASK(22, 20)
/* Interpretation of resource numbers change at ETM v4.3 architecture */
#define ETM4X_ARCH_4V3 0x43
/**
* struct etmv4_config - configuration information related to an ETMv4
* @mode: Controls various modes supported by this ETM.

View File

@ -274,6 +274,15 @@ out_disable_clk:
return ret;
}
static int __exit funnel_remove(struct device *dev)
{
struct funnel_drvdata *drvdata = dev_get_drvdata(dev);
coresight_unregister(drvdata->csdev);
return 0;
}
#ifdef CONFIG_PM
static int funnel_runtime_suspend(struct device *dev)
{
@ -319,29 +328,41 @@ static int static_funnel_probe(struct platform_device *pdev)
return ret;
}
static int __exit static_funnel_remove(struct platform_device *pdev)
{
funnel_remove(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
static const struct of_device_id static_funnel_match[] = {
{.compatible = "arm,coresight-static-funnel"},
{}
};
MODULE_DEVICE_TABLE(of, static_funnel_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id static_funnel_ids[] = {
{"ARMHC9FE", 0},
{},
};
MODULE_DEVICE_TABLE(acpi, static_funnel_ids);
#endif
static struct platform_driver static_funnel_driver = {
.probe = static_funnel_probe,
.remove = static_funnel_remove,
.driver = {
.name = "coresight-static-funnel",
.owner = THIS_MODULE,
.of_match_table = static_funnel_match,
.acpi_match_table = ACPI_PTR(static_funnel_ids),
.pm = &funnel_dev_pm_ops,
.suppress_bind_attrs = true,
},
};
builtin_platform_driver(static_funnel_driver);
static int dynamic_funnel_probe(struct amba_device *adev,
const struct amba_id *id)
@ -349,6 +370,11 @@ static int dynamic_funnel_probe(struct amba_device *adev,
return funnel_probe(&adev->dev, &adev->res);
}
static int __exit dynamic_funnel_remove(struct amba_device *adev)
{
return funnel_remove(&adev->dev);
}
static const struct amba_id dynamic_funnel_ids[] = {
{
.id = 0x000bb908,
@ -362,6 +388,8 @@ static const struct amba_id dynamic_funnel_ids[] = {
{ 0, 0},
};
MODULE_DEVICE_TABLE(amba, dynamic_funnel_ids);
static struct amba_driver dynamic_funnel_driver = {
.drv = {
.name = "coresight-dynamic-funnel",
@ -370,6 +398,39 @@ static struct amba_driver dynamic_funnel_driver = {
.suppress_bind_attrs = true,
},
.probe = dynamic_funnel_probe,
.remove = dynamic_funnel_remove,
.id_table = dynamic_funnel_ids,
};
builtin_amba_driver(dynamic_funnel_driver);
static int __init funnel_init(void)
{
int ret;
ret = platform_driver_register(&static_funnel_driver);
if (ret) {
pr_info("Error registering platform driver\n");
return ret;
}
ret = amba_driver_register(&dynamic_funnel_driver);
if (ret) {
pr_info("Error registering amba driver\n");
platform_driver_unregister(&static_funnel_driver);
}
return ret;
}
static void __exit funnel_exit(void)
{
platform_driver_unregister(&static_funnel_driver);
amba_driver_unregister(&dynamic_funnel_driver);
}
module_init(funnel_init);
module_exit(funnel_exit);
MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
MODULE_DESCRIPTION("Arm CoreSight Funnel Driver");
MODULE_LICENSE("GPL v2");

View File

@ -75,6 +75,7 @@ coresight_find_csdev_by_fwnode(struct fwnode_handle *r_fwnode)
}
return csdev;
}
EXPORT_SYMBOL_GPL(coresight_find_csdev_by_fwnode);
#ifdef CONFIG_OF
static inline bool of_coresight_legacy_ep_is_input(struct device_node *ep)
@ -711,11 +712,11 @@ static int acpi_coresight_parse_graph(struct acpi_device *adev,
return dir;
if (dir == ACPI_CORESIGHT_LINK_MASTER) {
if (ptr->outport > pdata->nr_outport)
pdata->nr_outport = ptr->outport;
if (ptr->outport >= pdata->nr_outport)
pdata->nr_outport = ptr->outport + 1;
ptr++;
} else {
WARN_ON(pdata->nr_inport == ptr->child_port);
WARN_ON(pdata->nr_inport == ptr->child_port + 1);
/*
* We do not track input port connections for a device.
* However we need the highest port number described,
@ -723,8 +724,8 @@ static int acpi_coresight_parse_graph(struct acpi_device *adev,
* record for an output connection. Hence, do not move
* the ptr for input connections
*/
if (ptr->child_port > pdata->nr_inport)
pdata->nr_inport = ptr->child_port;
if (ptr->child_port >= pdata->nr_inport)
pdata->nr_inport = ptr->child_port + 1;
}
}

View File

@ -66,8 +66,8 @@ static DEVICE_ATTR_RO(name)
#define coresight_simple_reg64(type, name, lo_off, hi_off) \
__coresight_simple_func(type, NULL, name, lo_off, hi_off)
extern const u32 barrier_pkt[4];
#define CORESIGHT_BARRIER_PKT_SIZE (sizeof(barrier_pkt))
extern const u32 coresight_barrier_pkt[4];
#define CORESIGHT_BARRIER_PKT_SIZE (sizeof(coresight_barrier_pkt))
enum etm_addr_type {
ETM_ADDR_TYPE_NONE,
@ -104,10 +104,9 @@ struct cs_buffers {
static inline void coresight_insert_barrier_packet(void *buf)
{
if (buf)
memcpy(buf, barrier_pkt, CORESIGHT_BARRIER_PKT_SIZE);
memcpy(buf, coresight_barrier_pkt, CORESIGHT_BARRIER_PKT_SIZE);
}
static inline void CS_LOCK(void __iomem *addr)
{
do {
@ -148,7 +147,8 @@ static inline void coresight_write_reg_pair(void __iomem *addr, u64 val,
void coresight_disable_path(struct list_head *path);
int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data);
struct coresight_device *coresight_get_sink(struct list_head *path);
struct coresight_device *coresight_get_enabled_sink(bool reset);
struct coresight_device *
coresight_get_enabled_sink(struct coresight_device *source);
struct coresight_device *coresight_get_sink_by_id(u32 id);
struct coresight_device *
coresight_find_default_sink(struct coresight_device *csdev);
@ -165,7 +165,7 @@ int coresight_make_links(struct coresight_device *orig,
void coresight_remove_links(struct coresight_device *orig,
struct coresight_connection *conn);
#ifdef CONFIG_CORESIGHT_SOURCE_ETM3X
#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM3X)
extern int etm_readl_cp14(u32 off, unsigned int *val);
extern int etm_writel_cp14(u32 off, u32 val);
#else
@ -173,15 +173,13 @@ static inline int etm_readl_cp14(u32 off, unsigned int *val) { return 0; }
static inline int etm_writel_cp14(u32 off, u32 val) { return 0; }
#endif
#ifdef CONFIG_CORESIGHT_CTI
extern void cti_add_assoc_to_csdev(struct coresight_device *csdev);
extern void cti_remove_assoc_from_csdev(struct coresight_device *csdev);
struct cti_assoc_op {
void (*add)(struct coresight_device *csdev);
void (*remove)(struct coresight_device *csdev);
};
#else
static inline void cti_add_assoc_to_csdev(struct coresight_device *csdev) {}
static inline void
cti_remove_assoc_from_csdev(struct coresight_device *csdev) {}
#endif
extern void coresight_set_cti_ops(const struct cti_assoc_op *cti_op);
extern void coresight_remove_cti_ops(void);
/*
* Macros and inline functions to handle CoreSight UCI data and driver

View File

@ -291,6 +291,14 @@ out_disable_clk:
return ret;
}
static int __exit replicator_remove(struct device *dev)
{
struct replicator_drvdata *drvdata = dev_get_drvdata(dev);
coresight_unregister(drvdata->csdev);
return 0;
}
static int static_replicator_probe(struct platform_device *pdev)
{
int ret;
@ -310,6 +318,13 @@ static int static_replicator_probe(struct platform_device *pdev)
return ret;
}
static int __exit static_replicator_remove(struct platform_device *pdev)
{
replicator_remove(&pdev->dev);
pm_runtime_disable(&pdev->dev);
return 0;
}
#ifdef CONFIG_PM
static int replicator_runtime_suspend(struct device *dev)
{
@ -343,24 +358,29 @@ static const struct of_device_id static_replicator_match[] = {
{}
};
MODULE_DEVICE_TABLE(of, static_replicator_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id static_replicator_acpi_ids[] = {
{"ARMHC985", 0}, /* ARM CoreSight Static Replicator */
{}
};
MODULE_DEVICE_TABLE(acpi, static_replicator_acpi_ids);
#endif
static struct platform_driver static_replicator_driver = {
.probe = static_replicator_probe,
.remove = static_replicator_remove,
.driver = {
.name = "coresight-static-replicator",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(static_replicator_match),
.acpi_match_table = ACPI_PTR(static_replicator_acpi_ids),
.pm = &replicator_dev_pm_ops,
.suppress_bind_attrs = true,
},
};
builtin_platform_driver(static_replicator_driver);
static int dynamic_replicator_probe(struct amba_device *adev,
const struct amba_id *id)
@ -368,19 +388,60 @@ static int dynamic_replicator_probe(struct amba_device *adev,
return replicator_probe(&adev->dev, &adev->res);
}
static int __exit dynamic_replicator_remove(struct amba_device *adev)
{
return replicator_remove(&adev->dev);
}
static const struct amba_id dynamic_replicator_ids[] = {
CS_AMBA_ID(0x000bb909),
CS_AMBA_ID(0x000bb9ec), /* Coresight SoC-600 */
{},
};
MODULE_DEVICE_TABLE(amba, dynamic_replicator_ids);
static struct amba_driver dynamic_replicator_driver = {
.drv = {
.name = "coresight-dynamic-replicator",
.pm = &replicator_dev_pm_ops,
.owner = THIS_MODULE,
.suppress_bind_attrs = true,
},
.probe = dynamic_replicator_probe,
.remove = dynamic_replicator_remove,
.id_table = dynamic_replicator_ids,
};
builtin_amba_driver(dynamic_replicator_driver);
static int __init replicator_init(void)
{
int ret;
ret = platform_driver_register(&static_replicator_driver);
if (ret) {
pr_info("Error registering platform driver\n");
return ret;
}
ret = amba_driver_register(&dynamic_replicator_driver);
if (ret) {
pr_info("Error registering amba driver\n");
platform_driver_unregister(&static_replicator_driver);
}
return ret;
}
static void __exit replicator_exit(void)
{
platform_driver_unregister(&static_replicator_driver);
amba_driver_unregister(&dynamic_replicator_driver);
}
module_init(replicator_init);
module_exit(replicator_exit);
MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
MODULE_DESCRIPTION("Arm CoreSight Replicator Driver");
MODULE_LICENSE("GPL v2");

View File

@ -412,6 +412,7 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data,
void __iomem *ch_addr;
struct stm_drvdata *drvdata = container_of(stm_data,
struct stm_drvdata, stm);
unsigned int stm_flags;
if (!(drvdata && local_read(&drvdata->mode)))
return -EACCES;
@ -421,8 +422,9 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data,
ch_addr = stm_channel_addr(drvdata, channel);
flags = (flags == STP_PACKET_TIMESTAMPED) ? STM_FLAG_TIMESTAMPED : 0;
flags |= test_bit(channel, drvdata->chs.guaranteed) ?
stm_flags = (flags & STP_PACKET_TIMESTAMPED) ?
STM_FLAG_TIMESTAMPED : 0;
stm_flags |= test_bit(channel, drvdata->chs.guaranteed) ?
STM_FLAG_GUARANTEED : 0;
if (size > drvdata->write_bytes)
@ -432,7 +434,7 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data,
switch (packet) {
case STP_PACKET_FLAG:
ch_addr += stm_channel_off(STM_PKT_TYPE_FLAG, flags);
ch_addr += stm_channel_off(STM_PKT_TYPE_FLAG, stm_flags);
/*
* The generic STM core sets a size of '0' on flag packets.
@ -444,7 +446,8 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data,
break;
case STP_PACKET_DATA:
ch_addr += stm_channel_off(STM_PKT_TYPE_DATA, flags);
stm_flags |= (flags & STP_PACKET_MARKED) ? STM_FLAG_MARKED : 0;
ch_addr += stm_channel_off(STM_PKT_TYPE_DATA, stm_flags);
stm_send(ch_addr, payload, size,
drvdata->write_bytes);
break;
@ -948,6 +951,17 @@ stm_unregister:
return ret;
}
static int __exit stm_remove(struct amba_device *adev)
{
struct stm_drvdata *drvdata = dev_get_drvdata(&adev->dev);
coresight_unregister(drvdata->csdev);
stm_unregister_device(&drvdata->stm);
return 0;
}
#ifdef CONFIG_PM
static int stm_runtime_suspend(struct device *dev)
{
@ -980,6 +994,8 @@ static const struct amba_id stm_ids[] = {
{ 0, 0},
};
MODULE_DEVICE_TABLE(amba, stm_ids);
static struct amba_driver stm_driver = {
.drv = {
.name = "coresight-stm",
@ -988,7 +1004,12 @@ static struct amba_driver stm_driver = {
.suppress_bind_attrs = true,
},
.probe = stm_probe,
.remove = stm_remove,
.id_table = stm_ids,
};
builtin_amba_driver(stm_driver);
module_amba_driver(stm_driver);
MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
MODULE_DESCRIPTION("Arm CoreSight System Trace Macrocell driver");
MODULE_LICENSE("GPL v2");

View File

@ -102,6 +102,7 @@ int coresight_add_sysfs_link(struct coresight_sysfs_link *info)
return ret;
}
EXPORT_SYMBOL_GPL(coresight_add_sysfs_link);
void coresight_remove_sysfs_link(struct coresight_sysfs_link *info)
{
@ -122,6 +123,7 @@ void coresight_remove_sysfs_link(struct coresight_sysfs_link *info)
info->orig->nr_links--;
info->target->nr_links--;
}
EXPORT_SYMBOL_GPL(coresight_remove_sysfs_link);
/*
* coresight_make_links: Make a link for a connection from a @orig

View File

@ -559,6 +559,21 @@ out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
}
static int __exit tmc_remove(struct amba_device *adev)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(&adev->dev);
/*
* Since misc_open() holds a refcount on the f_ops, which is
* etb fops in this case, device is there until last file
* handler to this device is closed.
*/
misc_deregister(&drvdata->miscdev);
coresight_unregister(drvdata->csdev);
return 0;
}
static const struct amba_id tmc_ids[] = {
CS_AMBA_ID(0x000bb961),
/* Coresight SoC 600 TMC-ETR/ETS */
@ -570,6 +585,8 @@ static const struct amba_id tmc_ids[] = {
{ 0, 0},
};
MODULE_DEVICE_TABLE(amba, tmc_ids);
static struct amba_driver tmc_driver = {
.drv = {
.name = "coresight-tmc",
@ -578,6 +595,12 @@ static struct amba_driver tmc_driver = {
},
.probe = tmc_probe,
.shutdown = tmc_shutdown,
.remove = tmc_remove,
.id_table = tmc_ids,
};
builtin_amba_driver(tmc_driver);
module_amba_driver(tmc_driver);
MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
MODULE_DESCRIPTION("Arm CoreSight Trace Memory Controller driver");
MODULE_LICENSE("GPL v2");

View File

@ -519,7 +519,7 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev,
cur = buf->cur;
offset = buf->offset;
barrier = barrier_pkt;
barrier = coresight_barrier_pkt;
/* for every byte to read */
for (i = 0; i < to_read; i += 4) {

View File

@ -255,6 +255,7 @@ void tmc_free_sg_table(struct tmc_sg_table *sg_table)
tmc_free_table_pages(sg_table);
tmc_free_data_pages(sg_table);
}
EXPORT_SYMBOL_GPL(tmc_free_sg_table);
/*
* Alloc pages for the table. Since this will be used by the device,
@ -340,6 +341,7 @@ struct tmc_sg_table *tmc_alloc_sg_table(struct device *dev,
return sg_table;
}
EXPORT_SYMBOL_GPL(tmc_alloc_sg_table);
/*
* tmc_sg_table_sync_data_range: Sync the data buffer written
@ -360,6 +362,7 @@ void tmc_sg_table_sync_data_range(struct tmc_sg_table *table,
PAGE_SIZE, DMA_FROM_DEVICE);
}
}
EXPORT_SYMBOL_GPL(tmc_sg_table_sync_data_range);
/* tmc_sg_sync_table: Sync the page table */
void tmc_sg_table_sync_table(struct tmc_sg_table *sg_table)
@ -372,6 +375,7 @@ void tmc_sg_table_sync_table(struct tmc_sg_table *sg_table)
dma_sync_single_for_device(real_dev, table_pages->daddrs[i],
PAGE_SIZE, DMA_TO_DEVICE);
}
EXPORT_SYMBOL_GPL(tmc_sg_table_sync_table);
/*
* tmc_sg_table_get_data: Get the buffer pointer for data @offset
@ -401,6 +405,7 @@ ssize_t tmc_sg_table_get_data(struct tmc_sg_table *sg_table,
*bufpp = page_address(data_pages->pages[pg_idx]) + pg_offset;
return len;
}
EXPORT_SYMBOL_GPL(tmc_sg_table_get_data);
#ifdef ETR_SG_DEBUG
/* Map a dma address to virtual address */
@ -766,6 +771,7 @@ tmc_etr_get_catu_device(struct tmc_drvdata *drvdata)
return NULL;
}
EXPORT_SYMBOL_GPL(tmc_etr_get_catu_device);
static inline int tmc_etr_enable_catu(struct tmc_drvdata *drvdata,
struct etr_buf *etr_buf)
@ -788,10 +794,21 @@ static inline void tmc_etr_disable_catu(struct tmc_drvdata *drvdata)
static const struct etr_buf_operations *etr_buf_ops[] = {
[ETR_MODE_FLAT] = &etr_flat_buf_ops,
[ETR_MODE_ETR_SG] = &etr_sg_buf_ops,
[ETR_MODE_CATU] = IS_ENABLED(CONFIG_CORESIGHT_CATU)
? &etr_catu_buf_ops : NULL,
[ETR_MODE_CATU] = NULL,
};
void tmc_etr_set_catu_ops(const struct etr_buf_operations *catu)
{
etr_buf_ops[ETR_MODE_CATU] = catu;
}
EXPORT_SYMBOL_GPL(tmc_etr_set_catu_ops);
void tmc_etr_remove_catu_ops(void)
{
etr_buf_ops[ETR_MODE_CATU] = NULL;
}
EXPORT_SYMBOL_GPL(tmc_etr_remove_catu_ops);
static inline int tmc_etr_mode_alloc_buf(int mode,
struct tmc_drvdata *drvdata,
struct etr_buf *etr_buf, int node,

View File

@ -326,4 +326,7 @@ tmc_sg_table_buf_size(struct tmc_sg_table *sg_table)
struct coresight_device *tmc_etr_get_catu_device(struct tmc_drvdata *drvdata);
void tmc_etr_set_catu_ops(const struct etr_buf_operations *catu);
void tmc_etr_remove_catu_ops(void);
#endif

View File

@ -173,6 +173,15 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
return PTR_ERR(drvdata->csdev);
}
static int __exit tpiu_remove(struct amba_device *adev)
{
struct tpiu_drvdata *drvdata = dev_get_drvdata(&adev->dev);
coresight_unregister(drvdata->csdev);
return 0;
}
#ifdef CONFIG_PM
static int tpiu_runtime_suspend(struct device *dev)
{
@ -216,6 +225,8 @@ static const struct amba_id tpiu_ids[] = {
{ 0, 0},
};
MODULE_DEVICE_TABLE(amba, tpiu_ids);
static struct amba_driver tpiu_driver = {
.drv = {
.name = "coresight-tpiu",
@ -224,6 +235,13 @@ static struct amba_driver tpiu_driver = {
.suppress_bind_attrs = true,
},
.probe = tpiu_probe,
.remove = tpiu_remove,
.id_table = tpiu_ids,
};
builtin_amba_driver(tpiu_driver);
module_amba_driver(tpiu_driver);
MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
MODULE_DESCRIPTION("Arm CoreSight TPIU (Trace Port Interface Unit) driver");
MODULE_LICENSE("GPL v2");

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