mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-04-13 09:59:31 +00:00
Char/Misc/IIO driver updates for 6.15-rc1
Here is the big set of char, misc, iio, and other smaller driver subsystems for 6.15-rc1. Lots of stuff in here, including: - loads of IIO changes and driver updates - counter driver updates - w1 driver updates - faux conversions for some drivers that were abusing the platform bus interface - coresight driver updates - rust miscdevice binding updates based on real-world-use - other minor driver updates All of these have been in linux-next with no reported issues for quite a while. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZ+mNdQ8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ylktACfYJix41jCCDbiFjnu7Hz4OIdcrUsAnRyF164M 1n5MhEhsEmvQj7WBwQLE =AmmW -----END PGP SIGNATURE----- Merge tag 'char-misc-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc Pull char / misc / IIO driver updates from Greg KH: "Here is the big set of char, misc, iio, and other smaller driver subsystems for 6.15-rc1. Lots of stuff in here, including: - loads of IIO changes and driver updates - counter driver updates - w1 driver updates - faux conversions for some drivers that were abusing the platform bus interface - coresight driver updates - rust miscdevice binding updates based on real-world-use - other minor driver updates All of these have been in linux-next with no reported issues for quite a while" * tag 'char-misc-6.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (292 commits) samples: rust_misc_device: fix markup in top-level docs Coresight: Fix a NULL vs IS_ERR() bug in probe misc: lis3lv02d: convert to use faux_device tlclk: convert to use faux_device regulator: dummy: convert to use the faux device interface bus: mhi: host: Fix race between unprepare and queue_buf coresight: configfs: Constify struct config_item_type doc: iio: ad7380: describe offload support iio: ad7380: add support for SPI offload iio: light: Add check for array bounds in veml6075_read_int_time_ms iio: adc: ti-ads7924 Drop unnecessary function parameters staging: iio: ad9834: Use devm_regulator_get_enable() staging: iio: ad9832: Use devm_regulator_get_enable() iio: gyro: bmg160_spi: add of_match_table dt-bindings: iio: adc: Add i.MX94 and i.MX95 support iio: adc: ad7768-1: remove unnecessary locking Documentation: ABI: add wideband filter type to sysfs-bus-iio iio: adc: ad7768-1: set MOSI idle state to prevent accidental reset iio: adc: ad7768-1: Fix conversion result sign iio: adc: ad7124: Benefit of dev = indio_dev->dev.parent in ad7124_parse_channel_config() ...
This commit is contained in:
commit
25601e8544
265 changed files with 19367 additions and 4211 deletions
|
@ -257,3 +257,18 @@ Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_t
|
|||
Description:
|
||||
(RW) Set/Get the MSR(mux select register) for the CMB subunit
|
||||
TPDM.
|
||||
|
||||
What: /sys/bus/coresight/devices/<tpdm-name>/mcmb_trig_lane
|
||||
Date: Feb 2025
|
||||
KernelVersion 6.15
|
||||
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
|
||||
Description:
|
||||
(RW) Set/Get which lane participates in the output pattern
|
||||
match cross trigger mechanism for the MCMB subunit TPDM.
|
||||
|
||||
What: /sys/bus/coresight/devices/<tpdm-name>/mcmb_lanes_select
|
||||
Date: Feb 2025
|
||||
KernelVersion 6.15
|
||||
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
|
||||
Description:
|
||||
(RW) Set/Get the enablement of the individual lane.
|
||||
|
|
|
@ -34,6 +34,14 @@ Contact: linux-iio@vger.kernel.org
|
|||
Description:
|
||||
Count data of Count Y represented as a string.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/compare
|
||||
KernelVersion: 6.15
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
If the counter device supports compare registers -- registers
|
||||
used to compare counter channels against a particular count --
|
||||
the compare count for channel Y is provided by this attribute.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/countY/capture
|
||||
KernelVersion: 6.1
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
|
@ -301,6 +309,7 @@ Description:
|
|||
|
||||
What: /sys/bus/counter/devices/counterX/cascade_counts_enable_component_id
|
||||
What: /sys/bus/counter/devices/counterX/external_input_phase_clock_select_component_id
|
||||
What: /sys/bus/counter/devices/counterX/countY/compare_component_id
|
||||
What: /sys/bus/counter/devices/counterX/countY/capture_component_id
|
||||
What: /sys/bus/counter/devices/counterX/countY/ceiling_component_id
|
||||
What: /sys/bus/counter/devices/counterX/countY/floor_component_id
|
||||
|
|
|
@ -2268,7 +2268,7 @@ Description:
|
|||
representing the sensor unique ID number.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/filter_type_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_filter_mode_available
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_filter_type_available
|
||||
KernelVersion: 6.1
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
@ -2290,6 +2290,16 @@ Description:
|
|||
* "sinc3+pf2" - Sinc3 + device specific Post Filter 2.
|
||||
* "sinc3+pf3" - Sinc3 + device specific Post Filter 3.
|
||||
* "sinc3+pf4" - Sinc3 + device specific Post Filter 4.
|
||||
* "wideband" - filter with wideband low ripple passband
|
||||
and sharp transition band.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/filter_type
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_filter_type
|
||||
KernelVersion: 6.1
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Specifies which filter type apply to the channel. The possible
|
||||
values are given by the filter_type_available attribute.
|
||||
|
||||
What: /sys/.../events/in_proximity_thresh_either_runningperiod
|
||||
KernelVersion: 6.6
|
||||
|
|
20
Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130
Normal file
20
Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130
Normal file
|
@ -0,0 +1,20 @@
|
|||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_filter_mode_available
|
||||
KernelVersion: 6.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Reading returns a list with the possible filter modes.
|
||||
|
||||
This ABI is only kept for backwards compatibility and the values
|
||||
returned are identical to filter_type_available attribute
|
||||
documented in Documentation/ABI/testing/sysfs-bus-iio. Please,
|
||||
use filter_type_available like ABI to provide filter options for
|
||||
new drivers.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_filter_mode
|
||||
KernelVersion: 6.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This ABI is only kept for backwards compatibility and the values
|
||||
returned are identical to in_voltageY-voltageZ_filter_type
|
||||
attribute documented in Documentation/ABI/testing/sysfs-bus-iio.
|
||||
Please, use in_voltageY-voltageZ_filter_type for new drivers.
|
|
@ -17,7 +17,7 @@ Description: Read only. Returns the firmware version of Intel MAX10
|
|||
What: /sys/bus/.../drivers/intel-m10-bmc/.../mac_address
|
||||
Date: January 2021
|
||||
KernelVersion: 5.12
|
||||
Contact: Peter Colberg <peter.colberg@intel.com>
|
||||
Contact: Peter Colberg <peter.colberg@altera.com>
|
||||
Description: Read only. Returns the first MAC address in a block
|
||||
of sequential MAC addresses assigned to the board
|
||||
that is managed by the Intel MAX10 BMC. It is stored in
|
||||
|
@ -28,7 +28,7 @@ Description: Read only. Returns the first MAC address in a block
|
|||
What: /sys/bus/.../drivers/intel-m10-bmc/.../mac_count
|
||||
Date: January 2021
|
||||
KernelVersion: 5.12
|
||||
Contact: Peter Colberg <peter.colberg@intel.com>
|
||||
Contact: Peter Colberg <peter.colberg@altera.com>
|
||||
Description: Read only. Returns the number of sequential MAC
|
||||
addresses assigned to the board managed by the Intel
|
||||
MAX10 BMC. This value is stored in FLASH and is mirrored
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
What: /sys/bus/platform/drivers/intel-m10bmc-sec-update/.../security/sr_root_entry_hash
|
||||
Date: Sep 2022
|
||||
KernelVersion: 5.20
|
||||
Contact: Peter Colberg <peter.colberg@intel.com>
|
||||
Contact: Peter Colberg <peter.colberg@altera.com>
|
||||
Description: Read only. Returns the root entry hash for the static
|
||||
region if one is programmed, else it returns the
|
||||
string: "hash not programmed". This file is only
|
||||
|
@ -11,7 +11,7 @@ Description: Read only. Returns the root entry hash for the static
|
|||
What: /sys/bus/platform/drivers/intel-m10bmc-sec-update/.../security/pr_root_entry_hash
|
||||
Date: Sep 2022
|
||||
KernelVersion: 5.20
|
||||
Contact: Peter Colberg <peter.colberg@intel.com>
|
||||
Contact: Peter Colberg <peter.colberg@altera.com>
|
||||
Description: Read only. Returns the root entry hash for the partial
|
||||
reconfiguration region if one is programmed, else it
|
||||
returns the string: "hash not programmed". This file
|
||||
|
@ -21,7 +21,7 @@ Description: Read only. Returns the root entry hash for the partial
|
|||
What: /sys/bus/platform/drivers/intel-m10bmc-sec-update/.../security/bmc_root_entry_hash
|
||||
Date: Sep 2022
|
||||
KernelVersion: 5.20
|
||||
Contact: Peter Colberg <peter.colberg@intel.com>
|
||||
Contact: Peter Colberg <peter.colberg@altera.com>
|
||||
Description: Read only. Returns the root entry hash for the BMC image
|
||||
if one is programmed, else it returns the string:
|
||||
"hash not programmed". This file is only visible if the
|
||||
|
@ -31,7 +31,7 @@ Description: Read only. Returns the root entry hash for the BMC image
|
|||
What: /sys/bus/platform/drivers/intel-m10bmc-sec-update/.../security/sr_canceled_csks
|
||||
Date: Sep 2022
|
||||
KernelVersion: 5.20
|
||||
Contact: Peter Colberg <peter.colberg@intel.com>
|
||||
Contact: Peter Colberg <peter.colberg@altera.com>
|
||||
Description: Read only. Returns a list of indices for canceled code
|
||||
signing keys for the static region. The standard bitmap
|
||||
list format is used (e.g. "1,2-6,9").
|
||||
|
@ -39,7 +39,7 @@ Description: Read only. Returns a list of indices for canceled code
|
|||
What: /sys/bus/platform/drivers/intel-m10bmc-sec-update/.../security/pr_canceled_csks
|
||||
Date: Sep 2022
|
||||
KernelVersion: 5.20
|
||||
Contact: Peter Colberg <peter.colberg@intel.com>
|
||||
Contact: Peter Colberg <peter.colberg@altera.com>
|
||||
Description: Read only. Returns a list of indices for canceled code
|
||||
signing keys for the partial reconfiguration region. The
|
||||
standard bitmap list format is used (e.g. "1,2-6,9").
|
||||
|
@ -47,7 +47,7 @@ Description: Read only. Returns a list of indices for canceled code
|
|||
What: /sys/bus/platform/drivers/intel-m10bmc-sec-update/.../security/bmc_canceled_csks
|
||||
Date: Sep 2022
|
||||
KernelVersion: 5.20
|
||||
Contact: Peter Colberg <peter.colberg@intel.com>
|
||||
Contact: Peter Colberg <peter.colberg@altera.com>
|
||||
Description: Read only. Returns a list of indices for canceled code
|
||||
signing keys for the BMC. The standard bitmap list format
|
||||
is used (e.g. "1,2-6,9").
|
||||
|
@ -55,7 +55,7 @@ Description: Read only. Returns a list of indices for canceled code
|
|||
What: /sys/bus/platform/drivers/intel-m10bmc-sec-update/.../security/flash_count
|
||||
Date: Sep 2022
|
||||
KernelVersion: 5.20
|
||||
Contact: Peter Colberg <peter.colberg@intel.com>
|
||||
Contact: Peter Colberg <peter.colberg@altera.com>
|
||||
Description: Read only. Returns number of times the secure update
|
||||
staging area has been flashed.
|
||||
Format: "%u".
|
||||
|
|
6
Documentation/ABI/testing/sysfs-pps-gen-tio
Normal file
6
Documentation/ABI/testing/sysfs-pps-gen-tio
Normal file
|
@ -0,0 +1,6 @@
|
|||
What: /sys/class/pps-gen/pps-genx/enable
|
||||
Date: April 2025
|
||||
KernelVersion: 6.15
|
||||
Contact: Subramanian Mohan<subramanian.mohan@intel.com>
|
||||
Description:
|
||||
Enable or disable PPS TIO generator output.
|
|
@ -101,6 +101,29 @@ properties:
|
|||
and ETF configurations.
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
|
||||
memory-region:
|
||||
items:
|
||||
- description: Reserved trace buffer memory for ETR and ETF sinks.
|
||||
For ETR, this reserved memory region is used for trace data capture.
|
||||
Same region is used for trace data retention as well after a panic
|
||||
or watchdog reset.
|
||||
This reserved memory region is used as trace buffer or used for trace
|
||||
data retention only if specifically selected by the user in sysfs
|
||||
interface.
|
||||
The default memory usage models for ETR in sysfs/perf modes are
|
||||
otherwise unaltered.
|
||||
|
||||
For ETF, this reserved memory region is used by default for
|
||||
retention of trace data synced from internal SRAM after a panic
|
||||
or watchdog reset.
|
||||
- description: Reserved meta data memory. Used for ETR and ETF sinks
|
||||
for storing metadata.
|
||||
|
||||
memory-region-names:
|
||||
items:
|
||||
- const: tracedata
|
||||
- const: metadata
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -115,6 +138,9 @@ examples:
|
|||
etr@20070000 {
|
||||
compatible = "arm,coresight-tmc", "arm,primecell";
|
||||
reg = <0x20070000 0x1000>;
|
||||
memory-region = <&etr_trace_mem_reserved>,
|
||||
<&etr_mdata_mem_reserved>;
|
||||
memory-region-names = "tracedata", "metadata";
|
||||
|
||||
clocks = <&oscclk6a>;
|
||||
clock-names = "apb_pclk";
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/arm/qcom,coresight-ctcu.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: CoreSight TMC Control Unit
|
||||
|
||||
maintainers:
|
||||
- Yuanfang Zhang <quic_yuanfang@quicinc.com>
|
||||
- Mao Jinlong <quic_jinlmao@quicinc.com>
|
||||
- Jie Gan <quic_jiegan@quicinc.com>
|
||||
|
||||
description: |
|
||||
The Trace Memory Controller(TMC) is used for Embedded Trace Buffer(ETB),
|
||||
Embedded Trace FIFO(ETF) and Embedded Trace Router(ETR) configurations.
|
||||
The configuration mode (ETB, ETF, ETR) is discovered at boot time when
|
||||
the device is probed.
|
||||
|
||||
The Coresight TMC Control unit controls various Coresight behaviors.
|
||||
It works as a helper device when connected to TMC ETR device.
|
||||
It is responsible for controlling the data filter function based on
|
||||
the source device's Trace ID for TMC ETR device. The trace data with
|
||||
that Trace id can get into ETR's buffer while other trace data gets
|
||||
ignored.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- qcom,sa8775p-ctcu
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: apb
|
||||
|
||||
in-ports:
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
patternProperties:
|
||||
'^port(@[0-1])?$':
|
||||
description: Input connections from CoreSight Trace bus
|
||||
$ref: /schemas/graph.yaml#/properties/port
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- in-ports
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
ctcu@1001000 {
|
||||
compatible = "qcom,sa8775p-ctcu";
|
||||
reg = <0x1001000 0x1000>;
|
||||
|
||||
clocks = <&aoss_qmp>;
|
||||
clock-names = "apb";
|
||||
|
||||
in-ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
ctcu_in_port0: endpoint {
|
||||
remote-endpoint = <&etr0_out_port>;
|
||||
};
|
||||
};
|
||||
|
||||
port@1 {
|
||||
reg = <1>;
|
||||
ctcu_in_port1: endpoint {
|
||||
remote-endpoint = <&etr1_out_port>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
|
@ -55,8 +55,7 @@ properties:
|
|||
- const: arm,primecell
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
|
|
@ -41,8 +41,7 @@ properties:
|
|||
- const: arm,primecell
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
maxItems: 1
|
||||
|
||||
qcom,dsb-element-bits:
|
||||
description:
|
||||
|
|
110
Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml
Normal file
110
Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml
Normal file
|
@ -0,0 +1,110 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright 2024 Analog Devices Inc.
|
||||
# Copyright 2024 BayLibre, SAS.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/adi,ad4030.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD4030 and AD4630 ADC families
|
||||
|
||||
maintainers:
|
||||
- Michael Hennerich <michael.hennerich@analog.com>
|
||||
- Nuno Sa <nuno.sa@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices AD4030 single channel and AD4630/AD4632 dual channel precision
|
||||
SAR ADC families
|
||||
|
||||
* https://www.analog.com/media/en/technical-documentation/data-sheets/ad4030-24-4032-24.pdf
|
||||
* https://www.analog.com/media/en/technical-documentation/data-sheets/ad4630-24_ad4632-24.pdf
|
||||
* https://www.analog.com/media/en/technical-documentation/data-sheets/ad4630-16-4632-16.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad4030-24
|
||||
- adi,ad4032-24
|
||||
- adi,ad4630-16
|
||||
- adi,ad4630-24
|
||||
- adi,ad4632-16
|
||||
- adi,ad4632-24
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 102040816
|
||||
|
||||
spi-rx-bus-width:
|
||||
enum: [1, 2, 4]
|
||||
|
||||
vdd-5v-supply: true
|
||||
vdd-1v8-supply: true
|
||||
vio-supply: true
|
||||
|
||||
ref-supply:
|
||||
description:
|
||||
Optional External unbuffered reference. Used when refin-supply is not
|
||||
connected.
|
||||
|
||||
refin-supply:
|
||||
description:
|
||||
Internal buffered Reference. Used when ref-supply is not connected.
|
||||
|
||||
cnv-gpios:
|
||||
description:
|
||||
The Convert Input (CNV). It initiates the sampling conversions.
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
description:
|
||||
The Reset Input (/RST). Used for asynchronous device reset.
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
description:
|
||||
The BUSY pin is used to signal that the conversions results are available
|
||||
to be transferred when in SPI Clocking Mode. This nodes should be
|
||||
connected to an interrupt that is triggered when the BUSY line goes low.
|
||||
maxItems: 1
|
||||
|
||||
interrupt-names:
|
||||
const: busy
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-5v-supply
|
||||
- vdd-1v8-supply
|
||||
- vio-supply
|
||||
- cnv-gpios
|
||||
|
||||
oneOf:
|
||||
- required:
|
||||
- ref-supply
|
||||
- required:
|
||||
- refin-supply
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@0 {
|
||||
compatible = "adi,ad4030-24";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <80000000>;
|
||||
vdd-5v-supply = <&supply_5V>;
|
||||
vdd-1v8-supply = <&supply_1_8V>;
|
||||
vio-supply = <&supply_1_8V>;
|
||||
ref-supply = <&supply_5V>;
|
||||
cnv-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
|
||||
reset-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
|
@ -84,6 +84,10 @@ properties:
|
|||
description: The Reset Input (RESET). Should be configured GPIO_ACTIVE_LOW.
|
||||
maxItems: 1
|
||||
|
||||
pwms:
|
||||
description: PWM signal connected to the CNV pin.
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
items:
|
||||
|
@ -106,6 +110,15 @@ properties:
|
|||
The first cell is the GPn number: 0 to 3.
|
||||
The second cell takes standard GPIO flags.
|
||||
|
||||
'#trigger-source-cells':
|
||||
description: |
|
||||
First cell indicates the output signal: 0 = BUSY, 1 = ALERT.
|
||||
Second cell indicates which GPn pin is used: 0, 2 or 3.
|
||||
|
||||
For convenience, macros for these values are available in
|
||||
dt-bindings/iio/adc/adi,ad4695.h.
|
||||
const: 2
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
|
|
153
Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml
Normal file
153
Documentation/devicetree/bindings/iio/adc/adi,ad4851.yaml
Normal file
|
@ -0,0 +1,153 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright 2024 Analog Devices Inc.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/adi,ad4851.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD485X family
|
||||
|
||||
maintainers:
|
||||
- Sergiu Cuciurean <sergiu.cuciurean@analog.com>
|
||||
- Dragos Bogdan <dragos.bogdan@analog.com>
|
||||
- Antoniu Miclaus <antoniu.miclaus@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices AD485X fully buffered, 8-channel simultaneous sampling,
|
||||
16/20-bit, 1 MSPS data acquisition system (DAS) with differential, wide
|
||||
common-mode range inputs.
|
||||
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4855.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4856.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4857.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4858.pdf
|
||||
|
||||
$ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad4851
|
||||
- adi,ad4852
|
||||
- adi,ad4853
|
||||
- adi,ad4854
|
||||
- adi,ad4855
|
||||
- adi,ad4856
|
||||
- adi,ad4857
|
||||
- adi,ad4858
|
||||
- adi,ad4858i
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vcc-supply: true
|
||||
|
||||
vee-supply: true
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
vddh-supply: true
|
||||
|
||||
vddl-supply: true
|
||||
|
||||
vio-supply: true
|
||||
|
||||
vrefbuf-supply: true
|
||||
|
||||
vrefio-supply: true
|
||||
|
||||
pwms:
|
||||
description: PWM connected to the CNV pin.
|
||||
maxItems: 1
|
||||
|
||||
io-backends:
|
||||
maxItems: 1
|
||||
|
||||
pd-gpios:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 25000000
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^channel(@[0-7])?$":
|
||||
$ref: adc.yaml
|
||||
type: object
|
||||
description: Represents the channels which are connected to the ADC.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description:
|
||||
The channel number, as specified in the datasheet (from 0 to 7).
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
|
||||
diff-channels:
|
||||
description:
|
||||
Each channel can be configured as a bipolar differential channel.
|
||||
The ADC uses the same positive and negative inputs for this.
|
||||
This property must be specified as 'reg' (or the channel number) for
|
||||
both positive and negative inputs (i.e. diff-channels = <reg reg>).
|
||||
Since the configuration is bipolar differential, the 'bipolar'
|
||||
property is required.
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
|
||||
bipolar: true
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vcc-supply
|
||||
- vee-supply
|
||||
- vdd-supply
|
||||
- vio-supply
|
||||
- pwms
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@0{
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "adi,ad4858";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <10000000>;
|
||||
vcc-supply = <&vcc>;
|
||||
vdd-supply = <&vdd>;
|
||||
vee-supply = <&vee>;
|
||||
vddh-supply = <&vddh>;
|
||||
vddl-supply = <&vddl>;
|
||||
vio-supply = <&vio>;
|
||||
pwms = <&pwm_gen 0 0>;
|
||||
io-backends = <&iio_backend>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
diff-channels = <0 0>;
|
||||
bipolar;
|
||||
};
|
||||
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
149
Documentation/devicetree/bindings/iio/adc/adi,ad7191.yaml
Normal file
149
Documentation/devicetree/bindings/iio/adc/adi,ad7191.yaml
Normal file
|
@ -0,0 +1,149 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2025 Analog Devices Inc.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/adi,ad7191.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD7191 ADC
|
||||
|
||||
maintainers:
|
||||
- Alisa-Dariana Roman <alisa.roman@analog.com>
|
||||
|
||||
description: |
|
||||
Bindings for the Analog Devices AD7191 ADC device. Datasheet can be
|
||||
found here:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7191.pdf
|
||||
The device's PDOWN pin must be connected to the SPI controller's chip select
|
||||
pin.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7191
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-cpol: true
|
||||
|
||||
spi-cpha: true
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description:
|
||||
Must be present when CLKSEL pin is tied HIGH to select external clock
|
||||
source (either a crystal between MCLK1 and MCLK2 pins, or a
|
||||
CMOS-compatible clock driving MCLK2 pin). Must be absent when CLKSEL pin
|
||||
is tied LOW to use the internal 4.92MHz clock.
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
avdd-supply:
|
||||
description: AVdd voltage supply
|
||||
|
||||
dvdd-supply:
|
||||
description: DVdd voltage supply
|
||||
|
||||
vref-supply:
|
||||
description: Vref voltage supply
|
||||
|
||||
odr-gpios:
|
||||
description:
|
||||
ODR1 and ODR2 pins for output data rate selection. Should be defined if
|
||||
adi,odr-value is absent.
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
adi,odr-value:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: |
|
||||
Should be present if ODR pins are pin-strapped. Possible values:
|
||||
120 Hz (ODR1=0, ODR2=0)
|
||||
60 Hz (ODR1=0, ODR2=1)
|
||||
50 Hz (ODR1=1, ODR2=0)
|
||||
10 Hz (ODR1=1, ODR2=1)
|
||||
If defined, odr-gpios must be absent.
|
||||
enum: [120, 60, 50, 10]
|
||||
|
||||
pga-gpios:
|
||||
description:
|
||||
PGA1 and PGA2 pins for gain selection. Should be defined if adi,pga-value
|
||||
is absent.
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
adi,pga-value:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: |
|
||||
Should be present if PGA pins are pin-strapped. Possible values:
|
||||
Gain 1 (PGA1=0, PGA2=0)
|
||||
Gain 8 (PGA1=0, PGA2=1)
|
||||
Gain 64 (PGA1=1, PGA2=0)
|
||||
Gain 128 (PGA1=1, PGA2=1)
|
||||
If defined, pga-gpios must be absent.
|
||||
enum: [1, 8, 64, 128]
|
||||
|
||||
temp-gpios:
|
||||
description: TEMP pin for temperature sensor enable.
|
||||
maxItems: 1
|
||||
|
||||
chan-gpios:
|
||||
description: CHAN pin for input channel selection.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- avdd-supply
|
||||
- dvdd-supply
|
||||
- vref-supply
|
||||
- spi-cpol
|
||||
- spi-cpha
|
||||
- temp-gpios
|
||||
- chan-gpios
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
- oneOf:
|
||||
- required:
|
||||
- adi,odr-value
|
||||
- required:
|
||||
- odr-gpios
|
||||
- oneOf:
|
||||
- required:
|
||||
- adi,pga-value
|
||||
- required:
|
||||
- pga-gpios
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@0 {
|
||||
compatible = "adi,ad7191";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
clocks = <&ad7191_mclk>;
|
||||
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpio>;
|
||||
avdd-supply = <&avdd>;
|
||||
dvdd-supply = <&dvdd>;
|
||||
vref-supply = <&vref>;
|
||||
adi,pga-value = <1>;
|
||||
odr-gpios = <&gpio 23 GPIO_ACTIVE_HIGH>, <&gpio 24 GPIO_ACTIVE_HIGH>;
|
||||
temp-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>;
|
||||
chan-gpios = <&gpio 27 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
|
@ -27,6 +27,7 @@ description: |
|
|||
* https://www.analog.com/en/products/ad7388-4.html
|
||||
* https://www.analog.com/en/products/adaq4370-4.html
|
||||
* https://www.analog.com/en/products/adaq4380-4.html
|
||||
* https://www.analog.com/en/products/adaq4381-4.html
|
||||
|
||||
|
||||
$ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
@ -50,6 +51,7 @@ properties:
|
|||
- adi,ad7388-4
|
||||
- adi,adaq4370-4
|
||||
- adi,adaq4380-4
|
||||
- adi,adaq4381-4
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -201,6 +203,7 @@ allOf:
|
|||
- adi,ad7380-4
|
||||
- adi,adaq4370-4
|
||||
- adi,adaq4380-4
|
||||
- adi,adaq4381-4
|
||||
then:
|
||||
properties:
|
||||
refio-supply: false
|
||||
|
@ -218,6 +221,7 @@ allOf:
|
|||
enum:
|
||||
- adi,adaq4370-4
|
||||
- adi,adaq4380-4
|
||||
- adi,adaq4381-4
|
||||
then:
|
||||
required:
|
||||
- vs-p-supply
|
||||
|
|
|
@ -17,13 +17,25 @@ description: |
|
|||
interface for the actual ADC, while this IP core will interface
|
||||
to the data-lines of the ADC and handle the streaming of data into
|
||||
memory via DMA.
|
||||
In some cases, the AXI ADC interface is used to perform specialized
|
||||
operation to a particular ADC, e.g access the physical bus through
|
||||
specific registers to write ADC registers.
|
||||
In this case, we use a different compatible which indicates the target
|
||||
IP core's name.
|
||||
The following IP is currently supported:
|
||||
- AXI AD7606x: specialized version of the IP core for all the chips from
|
||||
the ad7606 family.
|
||||
|
||||
https://wiki.analog.com/resources/fpga/docs/axi_adc_ip
|
||||
https://analogdevicesinc.github.io/hdl/library/axi_ad485x/index.html
|
||||
http://analogdevicesinc.github.io/hdl/library/axi_ad7606x/index.html
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,axi-adc-10.0.a
|
||||
- adi,axi-ad7606x
|
||||
- adi,axi-ad485x
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -47,17 +59,48 @@ properties:
|
|||
'#io-backend-cells':
|
||||
const: 0
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^adc@[0-9a-f]+$":
|
||||
type: object
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
additionalProperties: true
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- dmas
|
||||
- reg
|
||||
- clocks
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
not:
|
||||
contains:
|
||||
const: adi,axi-ad7606x
|
||||
then:
|
||||
properties:
|
||||
'#address-cells': false
|
||||
'#size-cells': false
|
||||
patternProperties:
|
||||
"^adc@[0-9a-f]+$": false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
axi-adc@44a00000 {
|
||||
adc@44a00000 {
|
||||
compatible = "adi,axi-adc-10.0.a";
|
||||
reg = <0x44a00000 0x10000>;
|
||||
dmas = <&rx_dma 0>;
|
||||
|
@ -65,4 +108,31 @@ examples:
|
|||
clocks = <&axi_clk>;
|
||||
#io-backend-cells = <0>;
|
||||
};
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
parallel_bus_controller@44a00000 {
|
||||
compatible = "adi,axi-ad7606x";
|
||||
reg = <0x44a00000 0x10000>;
|
||||
dmas = <&rx_dma 0>;
|
||||
dma-names = "rx";
|
||||
clocks = <&ext_clk>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@0 {
|
||||
compatible = "adi,ad7606b";
|
||||
reg = <0>;
|
||||
pwms = <&axi_pwm_gen 0 0>;
|
||||
pwm-names = "convst1";
|
||||
avcc-supply = <&adc_vref>;
|
||||
vdrive-supply = <&vdd_supply>;
|
||||
reset-gpios = <&gpio0 91 GPIO_ACTIVE_HIGH>;
|
||||
standby-gpios = <&gpio0 90 GPIO_ACTIVE_LOW>;
|
||||
adi,range-gpios = <&gpio0 89 GPIO_ACTIVE_HIGH>;
|
||||
adi,oversampling-ratio-gpios = <&gpio0 88 GPIO_ACTIVE_HIGH
|
||||
&gpio0 87 GPIO_ACTIVE_HIGH
|
||||
&gpio0 86 GPIO_ACTIVE_HIGH>;
|
||||
io-backends = <¶llel_bus_controller>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
@ -19,7 +19,14 @@ description:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
const: nxp,imx93-adc
|
||||
oneOf:
|
||||
- enum:
|
||||
- nxp,imx93-adc
|
||||
- items:
|
||||
- enum:
|
||||
- nxp,imx94-adc
|
||||
- nxp,imx95-adc
|
||||
- const: nxp,imx93-adc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
@ -15,6 +15,8 @@ properties:
|
|||
- const: rockchip,saradc
|
||||
- const: rockchip,rk3066-tsadc
|
||||
- const: rockchip,rk3399-saradc
|
||||
- const: rockchip,rk3528-saradc
|
||||
- const: rockchip,rk3562-saradc
|
||||
- const: rockchip,rk3588-saradc
|
||||
- items:
|
||||
- const: rockchip,rk3576-saradc
|
||||
|
|
63
Documentation/devicetree/bindings/iio/adc/ti,ads7138.yaml
Normal file
63
Documentation/devicetree/bindings/iio/adc/ti,ads7138.yaml
Normal file
|
@ -0,0 +1,63 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/ti,ads7138.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Texas Instruments ADS7128/ADS7138 analog-to-digital converter (ADC)
|
||||
|
||||
maintainers:
|
||||
- Tobias Sperling <tobias.sperling@softing.com>
|
||||
|
||||
description: |
|
||||
The ADS7128 and ADS7138 chips are 12-bit, 8 channel analog-to-digital
|
||||
converters (ADC) with build-in digital window comparator (DWC), using the
|
||||
I2C interface.
|
||||
ADS7128 differs in the addition of further hardware features, like a
|
||||
root-mean-square (RMS) and a zero-crossing-detect (ZCD) module.
|
||||
|
||||
Datasheets:
|
||||
https://www.ti.com/product/ADS7128
|
||||
https://www.ti.com/product/ADS7138
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,ads7128
|
||||
- ti,ads7138
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
avdd-supply:
|
||||
description:
|
||||
The regulator used as analog supply voltage as well as reference voltage.
|
||||
|
||||
interrupts:
|
||||
description:
|
||||
Interrupt on ALERT pin, triggers on low level.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- avdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@10 {
|
||||
compatible = "ti,ads7138";
|
||||
reg = <0x10>;
|
||||
avdd-supply = <®_stb_3v3>;
|
||||
interrupt-parent = <&gpio2>;
|
||||
interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -55,18 +55,18 @@ examples:
|
|||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
dac@0 {
|
||||
reg = <0>;
|
||||
compatible = "adi,ad5390-5";
|
||||
vref-supply = <&dacvref>;
|
||||
reg = <0>;
|
||||
compatible = "adi,ad5390-5";
|
||||
vref-supply = <&dacvref>;
|
||||
};
|
||||
};
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
dac@42 {
|
||||
reg = <0x42>;
|
||||
compatible = "adi,ad5380-3";
|
||||
};
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
dac@42 {
|
||||
reg = <0x42>;
|
||||
compatible = "adi,ad5380-3";
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
@ -30,8 +30,9 @@ properties:
|
|||
|
||||
clock-names:
|
||||
description:
|
||||
Must be "clkin"
|
||||
maxItems: 1
|
||||
Must be "clkin" if the input reference is single ended or "clkin-diff"
|
||||
if the input reference is differential.
|
||||
enum: [clkin, clkin-diff]
|
||||
|
||||
adi,mute-till-lock-en:
|
||||
type: boolean
|
||||
|
|
|
@ -43,13 +43,13 @@ additionalProperties: false
|
|||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
temperature-sensor@43 {
|
||||
compatible = "sciosense,ens210";
|
||||
reg = <0x43>;
|
||||
};
|
||||
temperature-sensor@43 {
|
||||
compatible = "sciosense,ens210";
|
||||
reg = <0x43>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
||||
|
|
74
Documentation/devicetree/bindings/iio/imu/adi,adis16550.yaml
Normal file
74
Documentation/devicetree/bindings/iio/imu/adi,adis16550.yaml
Normal file
|
@ -0,0 +1,74 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/imu/adi,adis16550.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices ADIS16550 and similar IMUs
|
||||
|
||||
maintainers:
|
||||
- Nuno Sa <nuno.sa@analog.com>
|
||||
- Ramona Gradinariu <ramona.gradinariu@analog.com>
|
||||
- Antoniu Miclaus <antoniu.miclaus@analog.com>
|
||||
- Robert Budai <robert.budai@analog.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,adis16550
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-cpha: true
|
||||
|
||||
spi-cpol: true
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 15000000
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
description:
|
||||
Active low RESET pin.
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
description: If not provided, then the internal clock is used.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- spi-cpha
|
||||
- spi-cpol
|
||||
- spi-max-frequency
|
||||
- vdd-supply
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
imu@0 {
|
||||
compatible = "adi,adis16550";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <15000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
vdd-supply = <&vdd>;
|
||||
interrupts = <4 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpio>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,78 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/brcm,apds9160.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Broadcom Combined Proximity & Ambient light sensor
|
||||
|
||||
maintainers:
|
||||
- Mikael Gonella-Bolduc <m.gonella.bolduc@gmail.com>
|
||||
|
||||
description: |
|
||||
Datasheet: https://docs.broadcom.com/docs/APDS-9160-003-DS
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- brcm,apds9160
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
ps-cancellation-duration:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
Proximity sensor cancellation pulse duration in half clock cycles.
|
||||
This parameter determines a cancellation pulse duration.
|
||||
The cancellation is applied in the integration phase to cancel out
|
||||
unwanted reflected light from very near objects such as tempered glass
|
||||
in front of the sensor.
|
||||
default: 0
|
||||
maximum: 63
|
||||
|
||||
ps-cancellation-current-picoamp:
|
||||
description:
|
||||
Proximity sensor crosstalk cancellation current in picoampere.
|
||||
This parameter adjusts the current in steps of 2400 pA up to 276000 pA.
|
||||
The provided value must be a multiple of 2400 and in one of these ranges
|
||||
[60000 - 96000]
|
||||
[120000 - 156000]
|
||||
[180000 - 216000]
|
||||
[240000 - 276000]
|
||||
This parameter is used in conjunction with the cancellation duration.
|
||||
minimum: 60000
|
||||
maximum: 276000
|
||||
multipleOf: 2400
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
light-sensor@53 {
|
||||
compatible = "brcm,apds9160";
|
||||
reg = <0x53>;
|
||||
vdd-supply = <&vdd_reg>;
|
||||
interrupts = <29 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&pinctrl>;
|
||||
ps-cancellation-duration = <10>;
|
||||
ps-cancellation-current-picoamp = <62400>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -4,14 +4,16 @@
|
|||
$id: http://devicetree.org/schemas/iio/light/dynaimage,al3010.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Dyna-Image AL3010 sensor
|
||||
title: Dyna-Image AL3000a/AL3010 sensor
|
||||
|
||||
maintainers:
|
||||
- David Heidelberg <david@ixit.cz>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: dynaimage,al3010
|
||||
enum:
|
||||
- dynaimage,al3000a
|
||||
- dynaimage,al3010
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/magnetometer/silabs,si7210.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Si7210 magnetic position and temperature sensor
|
||||
|
||||
maintainers:
|
||||
- Antoni Pokusinski <apokusinski01@gmail.com>
|
||||
|
||||
description: |
|
||||
Silabs Si7210 I2C Hall effect magnetic position and temperature sensor.
|
||||
https://www.silabs.com/documents/public/data-sheets/si7210-datasheet.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: silabs,si7210
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply:
|
||||
description: Regulator that provides power to the sensor
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
magnetometer@30 {
|
||||
compatible = "silabs,si7210";
|
||||
reg = <0x30>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <4 IRQ_TYPE_EDGE_FALLING>;
|
||||
vdd-supply = <&vdd_3v3_reg>;
|
||||
};
|
||||
};
|
|
@ -40,15 +40,15 @@ unevaluatedProperties: false
|
|||
examples:
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
temperature-sensor@0 {
|
||||
compatible = "maxim,max31865";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <400000>;
|
||||
spi-cpha;
|
||||
maxim,3-wire;
|
||||
};
|
||||
temperature-sensor@0 {
|
||||
compatible = "maxim,max31865";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <400000>;
|
||||
spi-cpha;
|
||||
maxim,3-wire;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
@ -44,8 +44,8 @@ examples:
|
|||
#size-cells = <0>;
|
||||
|
||||
tmp117@48 {
|
||||
compatible = "ti,tmp117";
|
||||
reg = <0x48>;
|
||||
vcc-supply = <&pmic_reg_3v3>;
|
||||
compatible = "ti,tmp117";
|
||||
reg = <0x48>;
|
||||
vcc-supply = <&pmic_reg_3v3>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -193,6 +193,8 @@ properties:
|
|||
- maxim,max20751
|
||||
# mCube 3-axis 8-bit digital accelerometer
|
||||
- mcube,mc3230
|
||||
# mCube 3-axis 8-bit digital accelerometer
|
||||
- mcube,mc3510c
|
||||
# Measurement Specialities I2C temperature and humidity sensor
|
||||
- meas,htu21
|
||||
# Measurement Specialities I2C temperature and humidity sensor
|
||||
|
|
|
@ -206,8 +206,7 @@ To do so the class pps-gen has been added. PPS generators can be
|
|||
registered in the kernel by defining a struct pps_gen_source_info as
|
||||
follows::
|
||||
|
||||
static struct pps_gen_source_info pps_gen_dummy_info = {
|
||||
.name = "dummy",
|
||||
static const struct pps_gen_source_info pps_gen_dummy_info = {
|
||||
.use_system_clock = true,
|
||||
.get_time = pps_gen_dummy_get_time,
|
||||
.enable = pps_gen_dummy_enable,
|
||||
|
@ -286,3 +285,27 @@ delay between assert and clear edge as small as possible to reduce system
|
|||
latencies. But if it is too small slave won't be able to capture clear edge
|
||||
transition. The default of 30us should be good enough in most situations.
|
||||
The delay can be selected using 'delay' pps_gen_parport module parameter.
|
||||
|
||||
|
||||
Intel Timed I/O PPS signal generator
|
||||
------------------------------------
|
||||
|
||||
Intel Timed I/O is a high precision device, present on 2019 and newer Intel
|
||||
CPUs, that can generate PPS signals.
|
||||
|
||||
Timed I/O and system time are both driven by same hardware clock. The signal
|
||||
is generated with a precision of ~20 nanoseconds. The generated PPS signal
|
||||
is used to synchronize an external device with system clock. For example,
|
||||
it can be used to share your clock with a device that receives PPS signal,
|
||||
generated by Timed I/O device. There are dedicated Timed I/O pins to deliver
|
||||
the PPS signal to an external device.
|
||||
|
||||
Usage of Intel Timed I/O as PPS generator:
|
||||
|
||||
Start generating PPS signal::
|
||||
|
||||
$echo 1 > /sys/class/pps-gen/pps-genx/enable
|
||||
|
||||
Stop generating PPS signal::
|
||||
|
||||
$echo 0 > /sys/class/pps-gen/pps-genx/enable
|
||||
|
|
180
Documentation/iio/ad4030.rst
Normal file
180
Documentation/iio/ad4030.rst
Normal file
|
@ -0,0 +1,180 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
=============
|
||||
AD4030 driver
|
||||
=============
|
||||
|
||||
ADC driver for Analog Devices Inc. AD4030 and similar devices. The module name
|
||||
is ``ad4030``.
|
||||
|
||||
|
||||
Supported devices
|
||||
=================
|
||||
|
||||
The following chips are supported by this driver:
|
||||
|
||||
* `AD4030-24 <https://www.analog.com/AD4030-24>`_
|
||||
* `AD4032-24 <https://www.analog.com/AD4032-24>`_
|
||||
* `AD4630-16 <https://www.analog.com/AD4630-16>`_
|
||||
* `AD4630-24 <https://www.analog.com/AD4630-24>`_
|
||||
* `AD4632-16 <https://www.analog.com/AD4632-16>`_
|
||||
* `AD4632-24 <https://www.analog.com/AD4632-24>`_
|
||||
|
||||
IIO channels
|
||||
============
|
||||
|
||||
Each "hardware" channel as described in the datasheet is split in 2 IIO
|
||||
channels:
|
||||
|
||||
- One channel for the differential data
|
||||
- One channel for the common byte.
|
||||
|
||||
The possible IIO channels depending on the numbers of "hardware" channel are:
|
||||
|
||||
+------------------------------------+------------------------------------+
|
||||
| 1 channel ADC | 2 channels ADC |
|
||||
+====================================+====================================+
|
||||
| - voltage0-voltage1 (differential) | - voltage0-voltage1 (differential) |
|
||||
| - voltage2 (common-mode) | - voltage2-voltage3 (differential) |
|
||||
| | - voltage4 (common-mode) |
|
||||
| | - voltage5 (common-mode) |
|
||||
+------------------------------------+------------------------------------+
|
||||
|
||||
Labels
|
||||
------
|
||||
|
||||
For ease of use, the IIO channels provide a label. For a differential channel,
|
||||
the label is ``differentialN`` where ``N`` is the "hardware" channel id. For a
|
||||
common-mode channel, the label is ``common-modeN`` where ``N`` is the
|
||||
"hardware" channel id.
|
||||
|
||||
The possible labels are:
|
||||
|
||||
+-----------------+-----------------+
|
||||
| 1 channel ADC | 2 channels ADC |
|
||||
+=================+=================+
|
||||
| - differential0 | - differential0 |
|
||||
| - common-mode0 | - differential1 |
|
||||
| | - common-mode0 |
|
||||
| | - common-mode1 |
|
||||
+-----------------+-----------------+
|
||||
|
||||
Supported features
|
||||
==================
|
||||
|
||||
SPI wiring modes
|
||||
----------------
|
||||
|
||||
The driver currently supports the following SPI wiring configurations:
|
||||
|
||||
One lane mode
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
In this mode, each channel has its own SDO line to send the conversion results.
|
||||
At the moment this mode can only be used on AD4030 which has one channel so only
|
||||
one SDO line is used.
|
||||
|
||||
.. code-block::
|
||||
|
||||
+-------------+ +-------------+
|
||||
| ADC | | HOST |
|
||||
| | | |
|
||||
| CNV |<--------| CNV |
|
||||
| CS |<--------| CS |
|
||||
| SDI |<--------| SDO |
|
||||
| SDO0 |-------->| SDI |
|
||||
| SCLK |<--------| SCLK |
|
||||
+-------------+ +-------------+
|
||||
|
||||
Interleaved mode
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
In this mode, both channels conversion results are bit interleaved one SDO line.
|
||||
As such the wiring is the same as `One lane mode`_.
|
||||
|
||||
SPI Clock mode
|
||||
--------------
|
||||
|
||||
Only the SPI clocking mode is supported.
|
||||
|
||||
Output modes
|
||||
------------
|
||||
|
||||
There are more exposed IIO channels than channels as describe in the devices
|
||||
datasheet. This is due to the `Differential data + common-mode`_ encoding
|
||||
2 types of information in one conversion result. As such a "device" channel
|
||||
provides 2 IIO channels, one for the differential data and one for the common
|
||||
byte.
|
||||
|
||||
Differential data
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
This mode is selected when:
|
||||
|
||||
- Only differential channels are enabled in a buffered read
|
||||
- Oversampling attribute is set to 1
|
||||
|
||||
Differential data + common-mode
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This mode is selected when:
|
||||
|
||||
- Differential and common-mode channels are enabled in a buffered read
|
||||
- Oversampling attribute is set to 1
|
||||
|
||||
For the 24-bits chips, this mode is also available with 16-bits differential
|
||||
data but is not selectable yet.
|
||||
|
||||
Averaged differential data
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This mode is selected when:
|
||||
|
||||
- Only differential channels are selected enabled in a buffered read
|
||||
- Oversampling attribute is greater than 1
|
||||
|
||||
Digital Gain and Offset
|
||||
-----------------------
|
||||
|
||||
Each differential data channel has a 16-bits unsigned configurable hardware
|
||||
gain applied to it. By default it's equal to 1. Note that applying gain can
|
||||
cause numerical saturation.
|
||||
|
||||
Each differential data channel has a signed configurable hardware offset.
|
||||
For the ADCs ending in ``-24``, the gain is encoded on 24-bits.
|
||||
Likewise, the ADCs ending in ``-16`` have a gain encoded on 16-bits. Note that
|
||||
applying an offset can cause numerical saturation.
|
||||
|
||||
The final differential data returned by the ADC is computed by first applying
|
||||
the gain, then the offset.
|
||||
|
||||
The gain is controlled by the ``calibscale`` IIO attribute while the offset is
|
||||
controlled by the ``calibbias`` attribute.
|
||||
|
||||
Reference voltage
|
||||
-----------------
|
||||
|
||||
The chip supports an external reference voltage via the ``REF`` input or an
|
||||
internal buffered reference voltage via the ``REFIN`` input. The driver looks
|
||||
at the device tree to determine which is being used. If ``ref-supply`` is
|
||||
present, then the external reference voltage is used and the internal buffer is
|
||||
disabled. If ``refin-supply`` is present, then the internal buffered reference
|
||||
voltage is used.
|
||||
|
||||
Reset
|
||||
-----
|
||||
|
||||
Both hardware and software reset are supported. The driver looks first at the
|
||||
device tree to see if the ``reset-gpio`` is populated.
|
||||
If not present, the driver will fallback to a software reset by wiring to the
|
||||
device's registers.
|
||||
|
||||
Unimplemented features
|
||||
----------------------
|
||||
|
||||
- ``BUSY`` indication
|
||||
- Additional wiring modes
|
||||
- Additional clock modes
|
||||
- Differential data 16-bits + common-mode for 24-bits chips
|
||||
- Overrange events
|
||||
- Test patterns
|
|
@ -47,6 +47,36 @@ In this mode, CNV and CS are tied together and there is a single SDO line.
|
|||
To use this mode, in the device tree, omit the ``cnv-gpios`` and
|
||||
``spi-rx-bus-width`` properties.
|
||||
|
||||
SPI offload wiring
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When used with a SPI offload, the supported wiring configuration is:
|
||||
|
||||
.. code-block::
|
||||
|
||||
+-------------+ +-------------+
|
||||
| GP0/BUSY |-------->| TRIGGER |
|
||||
| CS |<--------| CS |
|
||||
| | | |
|
||||
| ADC | | SPI |
|
||||
| | | |
|
||||
| SDI |<--------| SDO |
|
||||
| SDO |-------->| SDI |
|
||||
| SCLK |<--------| SCLK |
|
||||
| | | |
|
||||
| | +-------------+
|
||||
| CNV |<-----+--| PWM |
|
||||
| | +--| GPIO |
|
||||
+-------------+ +-------------+
|
||||
|
||||
In this case, both the ``cnv-gpios`` and ``pwms`` properties are required.
|
||||
The ``#trigger-source-cells = <2>`` property is also required to connect back
|
||||
to the SPI offload. The SPI offload will have ``trigger-sources`` property
|
||||
with cells to indicate the busy signal and which GPx pin is used, e.g
|
||||
``<&ad4695 AD4695_TRIGGER_EVENT_BUSY AD4695_TRIGGER_PIN_GP0>``.
|
||||
|
||||
.. seealso:: `SPI offload support`_
|
||||
|
||||
Channel configuration
|
||||
---------------------
|
||||
|
||||
|
@ -149,15 +179,62 @@ Gain/offset calibration
|
|||
System calibration is supported using the channel gain and offset registers via
|
||||
the ``calibscale`` and ``calibbias`` attributes respectively.
|
||||
|
||||
Oversampling
|
||||
------------
|
||||
|
||||
The chip supports per-channel oversampling when SPI offload is being used, with
|
||||
available oversampling ratios (OSR) of 1 (default), 4, 16, and 64. Enabling
|
||||
oversampling on a channel raises the effective number of bits of sampled data to
|
||||
17 (OSR == 4), 18 (16), or 19 (64), respectively. This can be set via the
|
||||
``oversampling_ratio`` attribute.
|
||||
|
||||
Setting the oversampling ratio for a channel also changes the sample rate for
|
||||
that channel, since it requires multiple conversions per 1 sample. Specifically,
|
||||
the new sampling frequency is the PWM sampling frequency divided by the
|
||||
particular OSR. This is set automatically by the driver when setting the
|
||||
``oversampling_ratio`` attribute. For example, if the device's current
|
||||
``sampling_frequency`` is 10000 and an OSR of 4 is set on channel ``voltage0``,
|
||||
the new reported sampling rate for that channel will be 2500 (ignoring PWM API
|
||||
rounding), while all others will remain at 10000. Subsequently setting the
|
||||
sampling frequency to a higher value on that channel will adjust the CNV trigger
|
||||
period for all channels, e.g. if ``voltage0``'s sampling frequency is adjusted
|
||||
from 2500 (with an OSR of 4) to 10000, the value reported by
|
||||
``in_voltage0_sampling_frequency`` will be 10000, but all other channels will
|
||||
now report 40000.
|
||||
|
||||
For simplicity, the sampling frequency of the device should be set (considering
|
||||
the highest desired OSR value to be used) first, before configuring oversampling
|
||||
for specific channels.
|
||||
|
||||
Unimplemented features
|
||||
----------------------
|
||||
|
||||
- Additional wiring modes
|
||||
- Threshold events
|
||||
- Oversampling
|
||||
- GPIO support
|
||||
- CRC support
|
||||
|
||||
SPI offload support
|
||||
===================
|
||||
|
||||
To be able to achieve the maximum sample rate, the driver can be used with the
|
||||
`AXI SPI Engine`_ to provide SPI offload support.
|
||||
|
||||
.. _AXI SPI Engine: http://analogdevicesinc.github.io/hdl/projects/ad469x_fmc/index.html
|
||||
|
||||
.. seealso:: `SPI offload wiring`_
|
||||
|
||||
When SPI offload is being used, some attributes will be different.
|
||||
|
||||
* ``trigger`` directory is removed.
|
||||
* ``in_voltage0_sampling_frequency`` attributes are added for setting the sample
|
||||
rate.
|
||||
* ``in_voltage0_sampling_frequency_available`` attributes are added for querying
|
||||
the max sample rate.
|
||||
* ``timestamp`` channel is removed.
|
||||
* Buffer data format may be different compared to when offload is not used,
|
||||
e.g. the ``buffer0/in_voltage0_type`` attribute.
|
||||
|
||||
Device buffers
|
||||
==============
|
||||
|
||||
|
@ -165,3 +242,28 @@ This driver supports hardware triggered buffers. This uses the "advanced
|
|||
sequencer" feature of the chip to trigger a burst of conversions.
|
||||
|
||||
Also see :doc:`iio_devbuf` for more general information.
|
||||
|
||||
Effective sample rate for buffered reads
|
||||
----------------------------------------
|
||||
|
||||
When SPI offload is not used, the sample rate is determined by the trigger that
|
||||
is manually configured in userspace. All enabled channels will be read in a
|
||||
burst when the trigger is received.
|
||||
|
||||
When SPI offload is used, the sample rate is configured per channel. All
|
||||
channels will have the same rate, so only one ``in_voltageY_sampling_frequency``
|
||||
attribute needs to be set. Since this rate determines the delay between each
|
||||
individual conversion, the effective sample rate for each sample is actually
|
||||
the sum of the periods of each enabled channel in a buffered read. In other
|
||||
words, it is the value of the ``in_voltageY_sampling_frequency`` attribute
|
||||
divided by the number of enabled channels. So if 4 channels are enabled, with
|
||||
the ``in_voltageY_sampling_frequency`` attributes set to 1 MHz, the effective
|
||||
sample rate is 250 kHz.
|
||||
|
||||
With oversampling enabled, the effective sample rate also depends on the OSR
|
||||
assigned to each channel. For example, if one of the 4 channels mentioned in the
|
||||
previous case is configured with an OSR of 4, the effective sample rate for that
|
||||
channel becomes (1 MHz / 4 ) = 250 kHz. The effective sample rate for all
|
||||
four channels is then 1 / ( (3 / 1 MHz) + ( 1 / 250 kHz) ) ~= 142.9 kHz. Note
|
||||
that in this case "sample" refers to one read of all enabled channels (i.e. one
|
||||
full cycle through the auto-sequencer).
|
||||
|
|
119
Documentation/iio/ad7191.rst
Normal file
119
Documentation/iio/ad7191.rst
Normal file
|
@ -0,0 +1,119 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
=============
|
||||
AD7191 driver
|
||||
=============
|
||||
|
||||
Device driver for Analog Devices AD7191 ADC.
|
||||
|
||||
Supported devices
|
||||
=================
|
||||
|
||||
* `AD7191 <https://www.analog.com/AD7191>`_
|
||||
|
||||
The AD7191 is a high precision, low noise, 24-bit Σ-Δ ADC with integrated PGA.
|
||||
It features two differential input channels, an internal temperature sensor, and
|
||||
configurable sampling rates.
|
||||
|
||||
Devicetree
|
||||
==========
|
||||
|
||||
Pin Configuration
|
||||
-----------------
|
||||
|
||||
The driver supports both pin-strapped and GPIO-controlled configurations for ODR
|
||||
(Output Data Rate) and PGA (Programmable Gain Amplifier) settings. These
|
||||
configurations are mutually exclusive - you must use either pin-strapped or GPIO
|
||||
control for each setting, not both.
|
||||
|
||||
ODR Configuration
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The ODR can be configured either through GPIO control or pin-strapping:
|
||||
|
||||
- When using GPIO control, specify the "odr-gpios" property in the device tree
|
||||
- For pin-strapped configuration, specify the "adi,odr-value" property in the
|
||||
device tree
|
||||
|
||||
Available ODR settings:
|
||||
|
||||
- 120 Hz (ODR1=0, ODR2=0)
|
||||
- 60 Hz (ODR1=0, ODR2=1)
|
||||
- 50 Hz (ODR1=1, ODR2=0)
|
||||
- 10 Hz (ODR1=1, ODR2=1)
|
||||
|
||||
PGA Configuration
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The PGA can be configured either through GPIO control or pin-strapping:
|
||||
|
||||
- When using GPIO control, specify the "pga-gpios" property in the device tree
|
||||
- For pin-strapped configuration, specify the "adi,pga-value" property in the
|
||||
device tree
|
||||
|
||||
Available PGA gain settings:
|
||||
|
||||
- 1x (PGA1=0, PGA2=0)
|
||||
- 8x (PGA1=0, PGA2=1)
|
||||
- 64x (PGA1=1, PGA2=0)
|
||||
- 128x (PGA1=1, PGA2=1)
|
||||
|
||||
Clock Configuration
|
||||
-------------------
|
||||
|
||||
The AD7191 supports both internal and external clock sources:
|
||||
|
||||
- When CLKSEL pin is tied LOW: Uses internal 4.92MHz clock (no clock property
|
||||
needed)
|
||||
- When CLKSEL pin is tied HIGH: Requires external clock source
|
||||
- Can be a crystal between MCLK1 and MCLK2 pins
|
||||
- Or a CMOS-compatible clock driving MCLK2 pin
|
||||
- Must specify the "clocks" property in device tree when using external clock
|
||||
|
||||
SPI Interface Requirements
|
||||
--------------------------
|
||||
|
||||
The AD7191 has specific SPI interface requirements:
|
||||
|
||||
- The DOUT/RDY output is dual-purpose and requires SPI bus locking
|
||||
- DOUT/RDY must be connected to an interrupt-capable GPIO
|
||||
- The SPI controller's chip select must be connected to the PDOWN pin of the ADC
|
||||
- When CS (PDOWN) is high, the device powers down and resets internal circuitry
|
||||
- SPI mode 3 operation (CPOL=1, CPHA=1) is required
|
||||
|
||||
Power Supply Requirements
|
||||
-------------------------
|
||||
|
||||
The device requires the following power supplies:
|
||||
|
||||
- AVdd: Analog power supply
|
||||
- DVdd: Digital power supply
|
||||
- Vref: Reference voltage supply (external)
|
||||
|
||||
All power supplies must be specified in the device tree.
|
||||
|
||||
Channel Configuration
|
||||
=====================
|
||||
|
||||
The device provides three channels:
|
||||
|
||||
1. Temperature Sensor
|
||||
- 24-bit unsigned
|
||||
- Internal temperature measurement
|
||||
- Temperature in millidegrees Celsius
|
||||
|
||||
2. Differential Input (AIN1-AIN2)
|
||||
- 24-bit unsigned
|
||||
- Differential voltage measurement
|
||||
- Configurable gain via PGA
|
||||
|
||||
3. Differential Input (AIN3-AIN4)
|
||||
- 24-bit unsigned
|
||||
- Differential voltage measurement
|
||||
- Configurable gain via PGA
|
||||
|
||||
Buffer Support
|
||||
==============
|
||||
|
||||
This driver supports IIO triggered buffers. See Documentation/iio/iio_devbuf.rst
|
||||
for more information about IIO triggered buffers.
|
|
@ -29,6 +29,7 @@ The following chips are supported by this driver:
|
|||
* `AD7388-4 <https://www.analog.com/en/products/ad7388-4.html>`_
|
||||
* `ADAQ4370-4 <https://www.analog.com/en/products/adaq4370-4.html>`_
|
||||
* `ADAQ4380-4 <https://www.analog.com/en/products/adaq4380-4.html>`_
|
||||
* `ADAQ4381-4 <https://www.analog.com/en/products/adaq4381-4.html>`_
|
||||
|
||||
|
||||
Supported features
|
||||
|
@ -52,8 +53,8 @@ declared in the device tree as ``refin-supply``.
|
|||
ADAQ devices
|
||||
~~~~~~~~~~~~
|
||||
|
||||
adaq4370-4 and adaq4380-4 don't have an external reference, but use a 3.3V
|
||||
internal reference derived from one of its supplies (``refin-supply``)
|
||||
ADAQ devices don't have an external reference, but use a 3.3V internal reference
|
||||
derived from one of its supplies (``refin-supply``)
|
||||
|
||||
All other devices from ad738x family
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -92,6 +93,38 @@ must restart iiod using the following command:
|
|||
|
||||
root:~# systemctl restart iiod
|
||||
|
||||
Alert
|
||||
-----
|
||||
|
||||
2 channels variants of the ad738x family, can use the SDOB line as an alert pin
|
||||
when configured in 1 SDO line mode. 4 channels variants, can use SDOD as an
|
||||
alert pin when configured in 1 or 2 SDO line(s) mode, although only 1 SDO line
|
||||
mode is currently supported by the driver (see `SPI wiring modes`_).
|
||||
|
||||
At the end of a conversion the active-low alert pin gets asserted if the
|
||||
conversion result exceeds the alert high limit or falls below the alert low
|
||||
limit. It is cleared, on a falling edge of CS. The alert pin is common to all
|
||||
channels.
|
||||
|
||||
User can enable alert using the regular iio events attribute:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
events/thresh_either_en
|
||||
|
||||
The high and low thresholds are common to all channels and can also be set using
|
||||
regular iio events attributes:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
events/in_thresh_falling_value
|
||||
events/in_thresh_rising_value
|
||||
|
||||
If debugfs is available, user can read the ALERT register to determine the
|
||||
faulty channel and direction.
|
||||
|
||||
In most use cases, user will hardwire the alert pin to trigger a shutdown.
|
||||
|
||||
Channel selection and sequencer (single-end chips only)
|
||||
-------------------------------------------------------
|
||||
|
||||
|
@ -144,8 +177,25 @@ Unimplemented features
|
|||
- Rolling average oversampling
|
||||
- Power down mode
|
||||
- CRC indication
|
||||
- Alert
|
||||
|
||||
SPI offload support
|
||||
===================
|
||||
|
||||
To be able to achieve the maximum sample rate, the driver can be used with the
|
||||
`AXI SPI Engine`_ to provide SPI offload support.
|
||||
|
||||
.. _AXI SPI Engine: http://analogdevicesinc.github.io/hdl/projects/pulsar_adc/index.html
|
||||
|
||||
When SPI offload is being used, some attributes will be different.
|
||||
|
||||
* ``trigger`` directory is removed.
|
||||
* ``in_voltage0_sampling_frequency`` attribute is added for setting the sample
|
||||
rate.
|
||||
* ``in_voltage0_sampling_frequency_available`` attribute is added for querying
|
||||
the max sample rate.
|
||||
* ``timestamp`` channel is removed.
|
||||
* Buffer data format may be different compared to when offload is not used,
|
||||
e.g. the ``in_voltage0_type`` attribute.
|
||||
|
||||
Device buffers
|
||||
==============
|
||||
|
|
|
@ -46,6 +46,8 @@ CS mode, 3-wire, without busy indicator
|
|||
To select this mode in the device tree, set the ``adi,spi-mode`` property to
|
||||
``"single"`` and omit the ``cnv-gpios`` property.
|
||||
|
||||
This is the only wiring configuration supported when using `SPI offload support`_.
|
||||
|
||||
CS mode, 4-wire, without busy indicator
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -106,7 +108,6 @@ Unimplemented features
|
|||
----------------------
|
||||
|
||||
- ``BUSY`` indication
|
||||
- ``TURBO`` mode
|
||||
|
||||
|
||||
Device attributes
|
||||
|
@ -147,6 +148,27 @@ AD7986 is a fully-differential ADC and has the following attributes:
|
|||
In "chain" mode, additional chips will appear as additional voltage input
|
||||
channels, e.g. ``in_voltage2-voltage3_raw``.
|
||||
|
||||
SPI offload support
|
||||
===================
|
||||
|
||||
To be able to achieve the maximum sample rate, the driver can be used with the
|
||||
`AXI SPI Engine`_ to provide SPI offload support.
|
||||
|
||||
.. _AXI SPI Engine: http://analogdevicesinc.github.io/hdl/projects/pulsar_adc/index.html
|
||||
|
||||
When SPI offload is being used, some attributes will be different.
|
||||
|
||||
* ``trigger`` directory is removed.
|
||||
* ``in_voltage0_sampling_frequency`` attribute is added for setting the sample
|
||||
rate.
|
||||
* ``in_voltage0_sampling_frequency_available`` attribute is added for querying
|
||||
the max sample rate.
|
||||
* ``timestamp`` channel is removed.
|
||||
* Buffer data format may be different compared to when offload is not used,
|
||||
e.g. the ``in_voltage0_type`` attribute.
|
||||
|
||||
If the ``turbo-gpios`` property is present in the device tree, the driver will
|
||||
turn on TURBO during buffered reads and turn it off otherwise.
|
||||
|
||||
Device buffers
|
||||
==============
|
||||
|
|
376
Documentation/iio/adis16550.rst
Normal file
376
Documentation/iio/adis16550.rst
Normal file
|
@ -0,0 +1,376 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
================
|
||||
ADIS16550 driver
|
||||
================
|
||||
|
||||
This driver supports Analog Device's IMUs on SPI bus.
|
||||
|
||||
1. Supported devices
|
||||
====================
|
||||
|
||||
* `ADIS16550 <https://www.analog.com/ADIS16550>`_
|
||||
|
||||
The ADIS16550 is a complete inertial system that includes a triaxis gyroscope
|
||||
and a triaxis accelerometer. The factory calibration characterizes each sensor for
|
||||
sensitivity, bias, and alignment. As a result, each sensor has its own dynamic
|
||||
compensation formulas that provide accurate sensor measurements.
|
||||
|
||||
2. Device attributes
|
||||
====================
|
||||
|
||||
Accelerometer, gyroscope measurements are always provided. Furthermore, the
|
||||
driver offers the capability to retrieve the delta angle and the delta velocity
|
||||
measurements computed by the device.
|
||||
|
||||
The delta angle measurements represent a calculation of angular displacement
|
||||
between each sample update, while the delta velocity measurements represent a
|
||||
calculation of linear velocity change between each sample update.
|
||||
|
||||
Finally, temperature data are provided which show a coarse measurement of
|
||||
the temperature inside of the IMU device. This data is most useful for
|
||||
monitoring relative changes in the thermal environment.
|
||||
|
||||
Each IIO device, has a device folder under ``/sys/bus/iio/devices/iio:deviceX``,
|
||||
where X is the IIO index of the device. Under these folders reside a set of
|
||||
device files, depending on the characteristics and features of the hardware
|
||||
device in questions. These files are consistently generalized and documented in
|
||||
the IIO ABI documentation.
|
||||
|
||||
The following tables show the adis16550 related device files, found in the
|
||||
specific device folder path ``/sys/bus/iio/devices/iio:deviceX``.
|
||||
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| 3-Axis Accelerometer related device files | Description |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_scale | Scale for the accelerometer channels. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_filter_low_pass_3db_frequency | Bandwidth for the accelerometer channels. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_x_calibbias | Calibration offset for the X-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_x_calibscale | Calibration scale for the X-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_x_raw | Raw X-axis accelerometer channel value. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_y_calibbias | Calibration offset for the Y-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_y_calibscale | Calibration scale for the Y-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_y_raw | Raw Y-axis accelerometer channel value. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_z_calibbias | Calibration offset for the Z-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_z_calibscale | Calibration scale for the Z-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_z_raw | Raw Z-axis accelerometer channel value. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_deltavelocity_scale | Scale for delta velocity channels. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_deltavelocity_x_raw | Raw X-axis delta velocity channel value. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_deltavelocity_y_raw | Raw Y-axis delta velocity channel value. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_deltavelocity_z_raw | Raw Z-axis delta velocity channel value. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| 3-Axis Gyroscope related device files | Description |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_scale | Scale for the gyroscope channels. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_filter_low_pass_3db_frequency | Scale for the gyroscope channels. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_x_calibbias | Calibration offset for the X-axis gyroscope channel. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_x_calibscale | Calibration scale for the X-axis gyroscope channel. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_x_raw | Raw X-axis gyroscope channel value. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_y_calibbias | Calibration offset for the Y-axis gyroscope channel. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_y_calibscale | Calibration scale for the Y-axis gyroscope channel. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_y_raw | Raw Y-axis gyroscope channel value. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_z_calibbias | Calibration offset for the Z-axis gyroscope channel. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_z_calibscale | Calibration scale for the Z-axis gyroscope channel. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_z_raw | Raw Z-axis gyroscope channel value. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_deltaangl_scale | Scale for delta angle channels. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_deltaangl_x_raw | Raw X-axis delta angle channel value. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_deltaangl_y_raw | Raw Y-axis delta angle channel value. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_deltaangl_z_raw | Raw Z-axis delta angle channel value. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
|
||||
+----------------------------------+-------------------------------------------+
|
||||
| Temperature sensor related files | Description |
|
||||
+----------------------------------+-------------------------------------------+
|
||||
| in_temp0_raw | Raw temperature channel value. |
|
||||
+----------------------------------+-------------------------------------------+
|
||||
| in_temp0_offset | Offset for the temperature sensor channel.|
|
||||
+----------------------------------+-------------------------------------------+
|
||||
| in_temp0_scale | Scale for the temperature sensor channel. |
|
||||
+----------------------------------+-------------------------------------------+
|
||||
|
||||
+----------------------------+--------------------------------------------------------------------------------+
|
||||
| Miscellaneous device files | Description |
|
||||
+----------------------------+--------------------------------------------------------------------------------+
|
||||
| name | Name of the IIO device. |
|
||||
+----------------------------+--------------------------------------------------------------------------------+
|
||||
| sampling_frequency | Currently selected sample rate. |
|
||||
+----------------------------+--------------------------------------------------------------------------------+
|
||||
|
||||
The following table shows the adis16550 related device debug files, found in the
|
||||
specific device debug folder path ``/sys/kernel/debug/iio/iio:deviceX``.
|
||||
|
||||
+----------------------+-------------------------------------------------------------------------+
|
||||
| Debugfs device files | Description |
|
||||
+----------------------+-------------------------------------------------------------------------+
|
||||
| serial_number | The serial number of the chip in hexadecimal format. |
|
||||
+----------------------+-------------------------------------------------------------------------+
|
||||
| product_id | Chip specific product id (16550). |
|
||||
+----------------------+-------------------------------------------------------------------------+
|
||||
| flash_count | The number of flash writes performed on the device. |
|
||||
+----------------------+-------------------------------------------------------------------------+
|
||||
| firmware_revision | String containing the firmware revision in the following format ##.##. |
|
||||
+----------------------+-------------------------------------------------------------------------+
|
||||
| firmware_date | String containing the firmware date in the following format mm-dd-yyyy. |
|
||||
+----------------------+-------------------------------------------------------------------------+
|
||||
|
||||
Channels processed values
|
||||
-------------------------
|
||||
|
||||
A channel value can be read from its _raw attribute. The value returned is the
|
||||
raw value as reported by the devices. To get the processed value of the channel,
|
||||
apply the following formula:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
processed value = (_raw + _offset) * _scale
|
||||
|
||||
Where _offset and _scale are device attributes. If no _offset attribute is
|
||||
present, simply assume its value is 0.
|
||||
|
||||
The adis16550 driver offers data for 5 types of channels, the table below shows
|
||||
the measurement units for the processed value, which are defined by the IIO
|
||||
framework:
|
||||
|
||||
+--------------------------------------+---------------------------+
|
||||
| Channel type | Measurement unit |
|
||||
+--------------------------------------+---------------------------+
|
||||
| Acceleration on X, Y, and Z axis | Meters per Second squared |
|
||||
+--------------------------------------+---------------------------+
|
||||
| Angular velocity on X, Y and Z axis | Radians per second |
|
||||
+--------------------------------------+---------------------------+
|
||||
| Delta velocity on X. Y, and Z axis | Meters per Second |
|
||||
+--------------------------------------+---------------------------+
|
||||
| Delta angle on X, Y, and Z axis | Radians |
|
||||
+--------------------------------------+---------------------------+
|
||||
| Temperature | Millidegrees Celsius |
|
||||
+--------------------------------------+---------------------------+
|
||||
|
||||
Usage examples
|
||||
--------------
|
||||
|
||||
Show device name:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat name
|
||||
adis16550
|
||||
|
||||
Show accelerometer channels value:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_raw
|
||||
6903851
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_y_raw
|
||||
5650550
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_z_raw
|
||||
104873530
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_scale
|
||||
0.000000095
|
||||
|
||||
- X-axis acceleration = in_accel_x_raw * in_accel_scale = 0.655865845 m/s^2
|
||||
- Y-axis acceleration = in_accel_y_raw * in_accel_scale = 0.53680225 m/s^2
|
||||
- Z-axis acceleration = in_accel_z_raw * in_accel_scale = 9.96298535 m/s^2
|
||||
|
||||
Show gyroscope channels value:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_x_raw
|
||||
193309
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_raw
|
||||
-763676
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_z_raw
|
||||
-358108
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_scale
|
||||
0.000000003
|
||||
|
||||
- X-axis angular velocity = in_anglvel_x_raw * in_anglvel_scale = 0.000579927 rad/s
|
||||
- Y-axis angular velocity = in_anglvel_y_raw * in_anglvel_scale = −0.002291028 rad/s
|
||||
- Z-axis angular velocity = in_anglvel_z_raw * in_anglvel_scale = −0.001074324 rad/s
|
||||
|
||||
Set calibration offset for accelerometer channels:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias
|
||||
0
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 5000 > in_accel_x_calibbias
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias
|
||||
5000
|
||||
|
||||
Set calibration offset for gyroscope channels:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_calibbias
|
||||
0
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo -5000 > in_anglvel_y_calibbias
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_calibbias
|
||||
-5000
|
||||
|
||||
Set sampling frequency:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat sampling_frequency
|
||||
4000.000000
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 1000 > sampling_frequency
|
||||
1000.000000
|
||||
|
||||
Set bandwidth for accelerometer channels:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_filter_low_pass_3db_frequency
|
||||
0
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 100 > in_accel_filter_low_pass_3db_frequency
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_filter_low_pass_3db_frequency
|
||||
100
|
||||
|
||||
Show serial number:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/kernel/debug/iio/iio:device0> cat serial_number
|
||||
0x000000b6
|
||||
|
||||
Show product id:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/kernel/debug/iio/iio:device0> cat product_id
|
||||
16550
|
||||
|
||||
Show flash count:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/kernel/debug/iio/iio:device0> cat flash_count
|
||||
13
|
||||
|
||||
Show firmware revision:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/kernel/debug/iio/iio:device0> cat firmware_revision
|
||||
1.5
|
||||
|
||||
Show firmware date:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/kernel/debug/iio/iio:device0> cat firmware_date
|
||||
28-04-2021
|
||||
|
||||
3. Device buffers
|
||||
=================
|
||||
|
||||
This driver supports IIO buffers.
|
||||
|
||||
The device supports retrieving the raw acceleration, gyroscope, delta velocity,
|
||||
delta angle and temperature measurements using buffers.
|
||||
|
||||
However, when retrieving acceleration or gyroscope data using buffers, delta
|
||||
readings will not be available and vice versa. This is because the device only
|
||||
allows to read either acceleration and gyroscope data or delta velocity and
|
||||
delta angle data at a time and switching between these two burst data selection
|
||||
modes is time consuming.
|
||||
|
||||
Usage examples
|
||||
--------------
|
||||
|
||||
Set device trigger in current_trigger, if not already set:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat trigger/current_trigger
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo adis16550-dev0 > trigger/current_trigger
|
||||
root:/sys/bus/iio/devices/iio:device0> cat trigger/current_trigger
|
||||
adis16550-dev0
|
||||
|
||||
Select channels for buffer read:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_deltavelocity_x_en
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_deltavelocity_y_en
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_deltavelocity_z_en
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_temp0_en
|
||||
|
||||
Set the number of samples to be stored in the buffer:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 10 > buffer/length
|
||||
|
||||
Enable buffer readings:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 1 > buffer/enable
|
||||
|
||||
Obtain buffered data:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> hexdump -C /dev/iio\:device0
|
||||
...
|
||||
0000cdf0 00 00 0d 2f 00 00 08 43 00 00 09 09 00 00 a4 5f |.../...C......._|
|
||||
0000ce00 00 00 0d 2f 00 00 07 de 00 00 08 db 00 00 a4 4b |.../...........K|
|
||||
0000ce10 00 00 0d 2f 00 00 07 58 00 00 08 a3 00 00 a4 55 |.../...X.......U|
|
||||
0000ce20 00 00 0d 2f 00 00 06 d6 00 00 08 5c 00 00 a4 62 |.../.......\...b|
|
||||
0000ce30 00 00 0d 2f 00 00 06 45 00 00 08 37 00 00 a4 47 |.../...E...7...G|
|
||||
0000ce40 00 00 0d 2f 00 00 05 d4 00 00 08 30 00 00 a3 fa |.../.......0....|
|
||||
0000ce50 00 00 0d 2f 00 00 05 d0 00 00 08 12 00 00 a3 d3 |.../............|
|
||||
0000ce60 00 00 0d 2f 00 00 05 dd 00 00 08 2e 00 00 a3 e9 |.../............|
|
||||
0000ce70 00 00 0d 2f 00 00 05 cc 00 00 08 51 00 00 a3 d5 |.../.......Q....|
|
||||
0000ce80 00 00 0d 2f 00 00 05 ba 00 00 08 22 00 00 a3 9a |.../......."....|
|
||||
0000ce90 00 00 0d 2f 00 00 05 9c 00 00 07 d9 00 00 a3 40 |.../...........@|
|
||||
0000cea0 00 00 0d 2f 00 00 05 68 00 00 07 94 00 00 a2 e4 |.../...h........|
|
||||
0000ceb0 00 00 0d 2f 00 00 05 25 00 00 07 8d 00 00 a2 ce |.../...%........|
|
||||
...
|
||||
|
||||
See ``Documentation/iio/iio_devbuf.rst`` for more information about how buffered
|
||||
data is structured.
|
||||
|
||||
4. IIO Interfacing Tools
|
||||
========================
|
||||
|
||||
See ``Documentation/iio/iio_tools.rst`` for the description of the available IIO
|
||||
interfacing tools.
|
|
@ -94,7 +94,7 @@ apply the following formula:
|
|||
Where _offset and _scale are device attributes. If no _offset attribute is
|
||||
present, simply assume its value is 0.
|
||||
|
||||
The adis16475 driver offers data for 2 types of channels, the table below shows
|
||||
The ADXL380 driver offers data for 2 types of channels, the table below shows
|
||||
the measurement units for the processed value, which are defined by the IIO
|
||||
framework:
|
||||
|
||||
|
|
305
Documentation/iio/iio_adc.rst
Normal file
305
Documentation/iio/iio_adc.rst
Normal file
|
@ -0,0 +1,305 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
=========================
|
||||
IIO Abstractions for ADCs
|
||||
=========================
|
||||
|
||||
1. Overview
|
||||
===========
|
||||
|
||||
The IIO subsystem supports many Analog to Digital Converters (ADCs). Some ADCs
|
||||
have features and characteristics that are supported in specific ways by IIO
|
||||
device drivers. This documentation describes common ADC features and explains
|
||||
how they are supported by the IIO subsystem.
|
||||
|
||||
1. ADC Channel Types
|
||||
====================
|
||||
|
||||
ADCs can have distinct types of inputs, each of them measuring analog voltages
|
||||
in a slightly different way. An ADC digitizes the analog input voltage over a
|
||||
span that is often given by the provided voltage reference, the input type, and
|
||||
the input polarity. The input range allowed to an ADC channel is needed to
|
||||
determine the scale factor and offset needed to obtain the measured value in
|
||||
real-world units (millivolts for voltage measurement, milliamps for current
|
||||
measurement, etc.).
|
||||
|
||||
Elaborate designs may have nonlinear characteristics or integrated components
|
||||
(such as amplifiers and reference buffers) that might also have to be considered
|
||||
to derive the allowed input range for an ADC. For clarity, the sections below
|
||||
assume the input range only depends on the provided voltage references, input
|
||||
type, and input polarity.
|
||||
|
||||
There are three general types of ADC inputs (single-ended, differential,
|
||||
pseudo-differential) and two possible polarities (unipolar, bipolar). The input
|
||||
type (single-ended, differential, pseudo-differential) is one channel
|
||||
characteristic, and is completely independent of the polarity (unipolar,
|
||||
bipolar) aspect. A comprehensive article about ADC input types (on which this
|
||||
doc is heavily based on) can be found at
|
||||
https://www.analog.com/en/resources/technical-articles/sar-adc-input-types.html.
|
||||
|
||||
1.1 Single-ended channels
|
||||
-------------------------
|
||||
|
||||
Single-ended channels digitize the analog input voltage relative to ground and
|
||||
can be either unipolar or bipolar.
|
||||
|
||||
1.1.1 Single-ended Unipolar Channels
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
::
|
||||
|
||||
---------- VREF -------------
|
||||
´ ` ´ ` _____________
|
||||
/ \ / \ / |
|
||||
/ \ / \ --- < IN ADC |
|
||||
\ / \ / \ |
|
||||
`-´ `-´ \ VREF |
|
||||
-------- GND (0V) ----------- +-----------+
|
||||
^
|
||||
|
|
||||
External VREF
|
||||
|
||||
The input voltage to a **single-ended unipolar** channel is allowed to swing
|
||||
from GND to VREF (where VREF is a voltage reference with electrical potential
|
||||
higher than system ground). The maximum input voltage is also called VFS
|
||||
(Voltage input Full-Scale), with VFS being determined by VREF. The voltage
|
||||
reference may be provided from an external supply or derived from the chip power
|
||||
source.
|
||||
|
||||
A single-ended unipolar channel could be described in device tree like the
|
||||
following example::
|
||||
|
||||
adc@0 {
|
||||
...
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
One is always allowed to include ADC channel nodes in the device tree. Though,
|
||||
if the device has a uniform set of inputs (e.g. all inputs are single-ended),
|
||||
then declaring the channel nodes is optional.
|
||||
|
||||
One caveat for devices that support mixed single-ended and differential channels
|
||||
is that single-ended channel nodes also need to provide a ``single-channel``
|
||||
property when ``reg`` is an arbitrary number that doesn't match the input pin
|
||||
number.
|
||||
|
||||
See ``Documentation/devicetree/bindings/iio/adc/adc.yaml`` for the complete
|
||||
documentation of ADC specific device tree properties.
|
||||
|
||||
|
||||
1.1.2 Single-ended Bipolar Channels
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
::
|
||||
|
||||
---------- +VREF ------------
|
||||
´ ` ´ ` _____________________
|
||||
/ \ / \ / |
|
||||
/ \ / \ --- < IN ADC |
|
||||
\ / \ / \ |
|
||||
`-´ `-´ \ +VREF -VREF |
|
||||
---------- -VREF ------------ +-------------------+
|
||||
^ ^
|
||||
| |
|
||||
External +VREF ------+ External -VREF
|
||||
|
||||
For a **single-ended bipolar** channel, the analog voltage input can go from
|
||||
-VREF to +VREF (where -VREF is the voltage reference that has the lower
|
||||
electrical potential while +VREF is the reference with the higher one). Some ADC
|
||||
chips derive the lower reference from +VREF, others get it from a separate
|
||||
input. Often, +VREF and -VREF are symmetric but they don't need to be so. When
|
||||
-VREF is lower than system ground, these inputs are also called single-ended
|
||||
true bipolar. Also, while there is a relevant difference between bipolar and
|
||||
true bipolar from the electrical perspective, IIO makes no explicit distinction
|
||||
between them.
|
||||
|
||||
Here's an example device tree description of a single-ended bipolar channel::
|
||||
|
||||
adc@0 {
|
||||
...
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
bipolar;
|
||||
};
|
||||
};
|
||||
|
||||
1.2 Differential channels
|
||||
-------------------------
|
||||
|
||||
A differential voltage measurement digitizes the voltage level at the positive
|
||||
input (IN+) relative to the negative input (IN-) over the -VREF to +VREF span.
|
||||
In other words, a differential channel measures the potential difference between
|
||||
IN+ and IN-, which is often denoted by the IN+ - IN- formula.
|
||||
|
||||
1.2.1 Differential Bipolar Channels
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
::
|
||||
|
||||
-------- +VREF ------ +-------------------+
|
||||
´ ` ´ ` / |
|
||||
/ \ / \ / --- < IN+ |
|
||||
`-´ `-´ | |
|
||||
-------- -VREF ------ | |
|
||||
| ADC |
|
||||
-------- +VREF ------ | |
|
||||
´ ` ´ ` | |
|
||||
\ / \ / \ --- < IN- |
|
||||
`-´ `-´ \ +VREF -VREF |
|
||||
-------- -VREF ------ +-------------------+
|
||||
^ ^
|
||||
| +---- External -VREF
|
||||
External +VREF
|
||||
|
||||
The analog signals to **differential bipolar** inputs are also allowed to swing
|
||||
from -VREF to +VREF. The bipolar part of the name means that the resulting value
|
||||
of the difference (IN+ - IN-) can be positive or negative. If -VREF is below
|
||||
system GND, these are also called differential true bipolar inputs.
|
||||
|
||||
Device tree example of a differential bipolar channel::
|
||||
|
||||
adc@0 {
|
||||
...
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
bipolar;
|
||||
diff-channels = <0 1>;
|
||||
};
|
||||
};
|
||||
|
||||
In the ADC driver, ``differential = 1`` is set into ``struct iio_chan_spec`` for
|
||||
the channel. Even though, there are three general input types, ``differential``
|
||||
is only used to distinguish between differential and non-differential (either
|
||||
single-ended or pseudo-differential) input types. See
|
||||
``include/linux/iio/iio.h`` for more information.
|
||||
|
||||
1.2.2 Differential Unipolar Channels
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For **differential unipolar** channels, the analog voltage at the positive input
|
||||
must also be higher than the voltage at the negative input. Thus, the actual
|
||||
input range allowed to a differential unipolar channel is IN- to +VREF. Because
|
||||
IN+ is allowed to swing with the measured analog signal and the input setup must
|
||||
guarantee IN+ will not go below IN- (nor IN- will raise above IN+), most
|
||||
differential unipolar channel setups have IN- fixed to a known voltage that does
|
||||
not fall within the voltage range expected for the measured signal. That leads
|
||||
to a setup that is equivalent to a pseudo-differential channel. Thus,
|
||||
differential unipolar setups can often be supported as pseudo-differential
|
||||
unipolar channels.
|
||||
|
||||
1.3 Pseudo-differential Channels
|
||||
--------------------------------
|
||||
|
||||
There is a third ADC input type which is called pseudo-differential or
|
||||
single-ended to differential configuration. A pseudo-differential channel is
|
||||
similar to a differential channel in that it also measures IN+ relative to IN-.
|
||||
However, unlike bipolar differential channels, the negative input is limited to
|
||||
a narrow voltage range (taken as a constant voltage) while only IN+ is allowed
|
||||
to swing. A pseudo-differential channel can be made out from a differential pair
|
||||
of inputs by restricting the negative input to a known voltage while allowing
|
||||
only the positive input to swing. Sometimes, the input provided to IN- is called
|
||||
common-mode voltage. Besides, some parts have a COM pin that allows single-ended
|
||||
inputs to be referenced to a common-mode voltage, making them
|
||||
pseudo-differential channels. Often, the common mode input voltage can be
|
||||
described in the device tree as a voltage regulator (e.g. ``com-supply``) since
|
||||
it is basically a constant voltage source.
|
||||
|
||||
1.3.1 Pseudo-differential Unipolar Channels
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
::
|
||||
|
||||
-------- +VREF ------ +-------------------+
|
||||
´ ` ´ ` / |
|
||||
/ \ / \ / --- < IN+ |
|
||||
`-´ `-´ | |
|
||||
--------- IN- ------- | ADC |
|
||||
| |
|
||||
Common-mode voltage --> --- < IN- |
|
||||
\ +VREF -VREF |
|
||||
+-------------------+
|
||||
^ ^
|
||||
| +---- External -VREF
|
||||
External +VREF
|
||||
|
||||
A **pseudo-differential unipolar** input has the limitations a differential
|
||||
unipolar channel would have, meaning the analog voltage to the positive input
|
||||
IN+ must stay within IN- to +VREF. The fixed voltage to IN- is often called
|
||||
common-mode voltage and it must be within -VREF to +VREF as would be expected
|
||||
from the signal to any differential channel negative input.
|
||||
|
||||
The voltage measured from IN+ is relative to IN- but, unlike differential
|
||||
channels, pseudo-differential setups are intended to gauge single-ended input
|
||||
signals. To enable applications to calculate IN+ voltage with respect to system
|
||||
ground, the IIO channel may provide an ``_offset`` sysfs attribute to be added
|
||||
to ADC output when converting raw data to voltage units. In many setups, the
|
||||
common-mode voltage input is at GND level and the ``_offset`` attribute is
|
||||
omitted due to being always zero.
|
||||
|
||||
Device tree example for pseudo-differential unipolar channel::
|
||||
|
||||
adc@0 {
|
||||
...
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
single-channel = <0>;
|
||||
common-mode-channel = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
Do not set ``differential`` in the channel ``iio_chan_spec`` struct of
|
||||
pseudo-differential channels.
|
||||
|
||||
1.3.2 Pseudo-differential Bipolar Channels
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
::
|
||||
|
||||
-------- +VREF ------ +-------------------+
|
||||
´ ` ´ ` / |
|
||||
/ \ / \ / --- < IN+ |
|
||||
`-´ `-´ | |
|
||||
-------- -VREF ------ | ADC |
|
||||
| |
|
||||
Common-mode voltage --> --- < IN- |
|
||||
\ +VREF -VREF |
|
||||
+-------------------+
|
||||
^ ^
|
||||
| +---- External -VREF
|
||||
External +VREF
|
||||
|
||||
A **pseudo-differential bipolar** input is not limited by the level at IN- but
|
||||
it will be limited to -VREF or to GND on the lower end of the input range
|
||||
depending on the particular ADC. Similar to their unipolar counter parts,
|
||||
pseudo-differential bipolar channels ought to declare an ``_offset`` attribute
|
||||
to enable the conversion of raw ADC data to voltage units. For the setup with
|
||||
IN- connected to GND, ``_offset`` is often omitted.
|
||||
|
||||
Device tree example for pseudo-differential bipolar channel::
|
||||
|
||||
adc@0 {
|
||||
...
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
bipolar;
|
||||
single-channel = <0>;
|
||||
common-mode-channel = <1>;
|
||||
};
|
||||
};
|
|
@ -7,6 +7,7 @@ Industrial I/O
|
|||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
iio_adc
|
||||
iio_configfs
|
||||
iio_devbuf
|
||||
iio_dmabuf_api
|
||||
|
@ -19,13 +20,16 @@ Industrial I/O Kernel Drivers
|
|||
:maxdepth: 1
|
||||
|
||||
ad4000
|
||||
ad4030
|
||||
ad4695
|
||||
ad7191
|
||||
ad7380
|
||||
ad7606
|
||||
ad7625
|
||||
ad7944
|
||||
adis16475
|
||||
adis16480
|
||||
adis16550
|
||||
adxl380
|
||||
bno055
|
||||
ep93xx_adc
|
||||
|
|
|
@ -462,44 +462,35 @@ queried by the perf command line tool:
|
|||
|
||||
cs_etm// [Kernel PMU event]
|
||||
|
||||
linaro@linaro-nano:~$
|
||||
|
||||
Regardless of the number of tracers available in a system (usually equal to the
|
||||
amount of processor cores), the "cs_etm" PMU will be listed only once.
|
||||
|
||||
A Coresight PMU works the same way as any other PMU, i.e the name of the PMU is
|
||||
listed along with configuration options within forward slashes '/'. Since a
|
||||
Coresight system will typically have more than one sink, the name of the sink to
|
||||
work with needs to be specified as an event option.
|
||||
On newer kernels the available sinks are listed in sysFS under
|
||||
provided along with configuration options within forward slashes '/' (see
|
||||
`Config option formats`_).
|
||||
|
||||
Advanced Perf framework usage
|
||||
-----------------------------
|
||||
|
||||
Sink selection
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
An appropriate sink will be selected automatically for use with Perf, but since
|
||||
there will typically be more than one sink, the name of the sink to use may be
|
||||
specified as a special config option prefixed with '@'.
|
||||
|
||||
The available sinks are listed in sysFS under
|
||||
($SYSFS)/bus/event_source/devices/cs_etm/sinks/::
|
||||
|
||||
root@localhost:/sys/bus/event_source/devices/cs_etm/sinks# ls
|
||||
tmc_etf0 tmc_etr0 tpiu0
|
||||
|
||||
On older kernels, this may need to be found from the list of coresight devices,
|
||||
available under ($SYSFS)/bus/coresight/devices/::
|
||||
|
||||
root:~# ls /sys/bus/coresight/devices/
|
||||
etm0 etm1 etm2 etm3 etm4 etm5 funnel0
|
||||
funnel1 funnel2 replicator0 stm0 tmc_etf0 tmc_etr0 tpiu0
|
||||
root@linaro-nano:~# perf record -e cs_etm/@tmc_etr0/u --per-thread program
|
||||
|
||||
As mentioned above in section "Device Naming scheme", the names of the devices could
|
||||
look different from what is used in the example above. One must use the device names
|
||||
as it appears under the sysFS.
|
||||
|
||||
The syntax within the forward slashes '/' is important. The '@' character
|
||||
tells the parser that a sink is about to be specified and that this is the sink
|
||||
to use for the trace session.
|
||||
|
||||
More information on the above and other example on how to use Coresight with
|
||||
the perf tools can be found in the "HOWTO.md" file of the openCSD gitHub
|
||||
repository [#third]_.
|
||||
|
||||
Advanced perf framework usage
|
||||
-----------------------------
|
||||
|
||||
AutoFDO analysis using the perf tools
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -508,7 +499,7 @@ perf can be used to record and analyze trace of programs.
|
|||
Execution can be recorded using 'perf record' with the cs_etm event,
|
||||
specifying the name of the sink to record to, e.g::
|
||||
|
||||
perf record -e cs_etm/@tmc_etr0/u --per-thread
|
||||
perf record -e cs_etm//u --per-thread
|
||||
|
||||
The 'perf report' and 'perf script' commands can be used to analyze execution,
|
||||
synthesizing instruction and branch events from the instruction trace.
|
||||
|
@ -572,7 +563,7 @@ sort example is from the AutoFDO tutorial (https://gcc.gnu.org/wiki/AutoFDO/Tuto
|
|||
Bubble sorting array of 30000 elements
|
||||
5910 ms
|
||||
|
||||
$ perf record -e cs_etm/@tmc_etr0/u --per-thread taskset -c 2 ./sort
|
||||
$ perf record -e cs_etm//u --per-thread taskset -c 2 ./sort
|
||||
Bubble sorting array of 30000 elements
|
||||
12543 ms
|
||||
[ perf record: Woken up 35 times to write data ]
|
||||
|
|
362
Documentation/trace/coresight/panic.rst
Normal file
362
Documentation/trace/coresight/panic.rst
Normal file
|
@ -0,0 +1,362 @@
|
|||
===================================================
|
||||
Using Coresight for Kernel panic and Watchdog reset
|
||||
===================================================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
This documentation is about using Linux coresight trace support to
|
||||
debug kernel panic and watchdog reset scenarios.
|
||||
|
||||
Coresight trace during Kernel panic
|
||||
-----------------------------------
|
||||
From the coresight driver point of view, addressing the kernel panic
|
||||
situation has four main requirements.
|
||||
|
||||
a. Support for allocation of trace buffer pages from reserved memory area.
|
||||
Platform can advertise this using a new device tree property added to
|
||||
relevant coresight nodes.
|
||||
|
||||
b. Support for stopping coresight blocks at the time of panic
|
||||
|
||||
c. Saving required metadata in the specified format
|
||||
|
||||
d. Support for reading trace data captured at the time of panic
|
||||
|
||||
Allocation of trace buffer pages from reserved RAM
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
A new optional device tree property "memory-region" is added to the
|
||||
Coresight TMC device nodes, that would give the base address and size of trace
|
||||
buffer.
|
||||
|
||||
Static allocation of trace buffers would ensure that both IOMMU enabled
|
||||
and disabled cases are handled. Also, platforms that support persistent
|
||||
RAM will allow users to read trace data in the subsequent boot without
|
||||
booting the crashdump kernel.
|
||||
|
||||
Note:
|
||||
For ETR sink devices, this reserved region will be used for both trace
|
||||
capture and trace data retrieval.
|
||||
For ETF sink devices, internal SRAM would be used for trace capture,
|
||||
and they would be synced to reserved region for retrieval.
|
||||
|
||||
|
||||
Disabling coresight blocks at the time of panic
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
In order to avoid the situation of losing relevant trace data after a
|
||||
kernel panic, it would be desirable to stop the coresight blocks at the
|
||||
time of panic.
|
||||
|
||||
This can be achieved by configuring the comparator, CTI and sink
|
||||
devices as below::
|
||||
|
||||
Trigger on panic
|
||||
Comparator --->External out --->CTI -->External In---->ETR/ETF stop
|
||||
|
||||
Saving metadata at the time of kernel panic
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Coresight metadata involves all additional data that are required for a
|
||||
successful trace decode in addition to the trace data. This involves
|
||||
ETR/ETF/ETB register snapshot etc.
|
||||
|
||||
A new optional device property "memory-region" is added to
|
||||
the ETR/ETF/ETB device nodes for this.
|
||||
|
||||
Reading trace data captured at the time of panic
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Trace data captured at the time of panic, can be read from rebooted kernel
|
||||
or from crashdump kernel using a special device file /dev/crash_tmc_xxx.
|
||||
This device file is created only when there is a valid crashdata available.
|
||||
|
||||
General flow of trace capture and decode incase of kernel panic
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
1. Enable source and sink on all the cores using the sysfs interface.
|
||||
ETR sinks should have trace buffers allocated from reserved memory,
|
||||
by selecting "resrv" buffer mode from sysfs.
|
||||
|
||||
2. Run relevant tests.
|
||||
|
||||
3. On a kernel panic, all coresight blocks are disabled, necessary
|
||||
metadata is synced by kernel panic handler.
|
||||
|
||||
System would eventually reboot or boot a crashdump kernel.
|
||||
|
||||
4. For platforms that supports crashdump kernel, raw trace data can be
|
||||
dumped using the coresight sysfs interface from the crashdump kernel
|
||||
itself. Persistent RAM is not a requirement in this case.
|
||||
|
||||
5. For platforms that supports persistent RAM, trace data can be dumped
|
||||
using the coresight sysfs interface in the subsequent Linux boot.
|
||||
Crashdump kernel is not a requirement in this case. Persistent RAM
|
||||
ensures that trace data is intact across reboot.
|
||||
|
||||
Coresight trace during Watchdog reset
|
||||
-------------------------------------
|
||||
The main difference between addressing the watchdog reset and kernel panic
|
||||
case are below,
|
||||
|
||||
a. Saving coresight metadata need to be taken care by the
|
||||
SCP(system control processor) firmware in the specified format,
|
||||
instead of kernel.
|
||||
|
||||
b. Reserved memory region given by firmware for trace buffer and metadata
|
||||
has to be in persistent RAM.
|
||||
Note: This is a requirement for watchdog reset case but optional
|
||||
in kernel panic case.
|
||||
|
||||
Watchdog reset can be supported only on platforms that meet the above
|
||||
two requirements.
|
||||
|
||||
Sample commands for testing a Kernel panic case with ETR sink
|
||||
-------------------------------------------------------------
|
||||
|
||||
1. Boot Linux kernel with "crash_kexec_post_notifiers" added to the kernel
|
||||
bootargs. This is mandatory if the user would like to read the tracedata
|
||||
from the crashdump kernel.
|
||||
|
||||
2. Enable the preloaded ETM configuration::
|
||||
|
||||
#echo 1 > /sys/kernel/config/cs-syscfg/configurations/panicstop/enable
|
||||
|
||||
3. Configure CTI using sysfs interface::
|
||||
|
||||
#./cti_setup.sh
|
||||
|
||||
#cat cti_setup.sh
|
||||
|
||||
|
||||
cd /sys/bus/coresight/devices/
|
||||
|
||||
ap_cti_config () {
|
||||
#ETM trig out[0] trigger to Channel 0
|
||||
echo 0 4 > channels/trigin_attach
|
||||
}
|
||||
|
||||
etf_cti_config () {
|
||||
#ETF Flush in trigger from Channel 0
|
||||
echo 0 1 > channels/trigout_attach
|
||||
echo 1 > channels/trig_filter_enable
|
||||
}
|
||||
|
||||
etr_cti_config () {
|
||||
#ETR Flush in from Channel 0
|
||||
echo 0 1 > channels/trigout_attach
|
||||
echo 1 > channels/trig_filter_enable
|
||||
}
|
||||
|
||||
ctidevs=`find . -name "cti*"`
|
||||
|
||||
for i in $ctidevs
|
||||
do
|
||||
cd $i
|
||||
|
||||
connection=`find . -name "ete*"`
|
||||
if [ ! -z "$connection" ]
|
||||
then
|
||||
echo "AP CTI config for $i"
|
||||
ap_cti_config
|
||||
fi
|
||||
|
||||
connection=`find . -name "tmc_etf*"`
|
||||
if [ ! -z "$connection" ]
|
||||
then
|
||||
echo "ETF CTI config for $i"
|
||||
etf_cti_config
|
||||
fi
|
||||
|
||||
connection=`find . -name "tmc_etr*"`
|
||||
if [ ! -z "$connection" ]
|
||||
then
|
||||
echo "ETR CTI config for $i"
|
||||
etr_cti_config
|
||||
fi
|
||||
|
||||
cd ..
|
||||
done
|
||||
|
||||
Note: CTI connections are SOC specific and hence the above script is
|
||||
added just for reference.
|
||||
|
||||
4. Choose reserved buffer mode for ETR buffer::
|
||||
|
||||
#echo "resrv" > /sys/bus/coresight/devices/tmc_etr0/buf_mode_preferred
|
||||
|
||||
5. Enable stop on flush trigger configuration::
|
||||
|
||||
#echo 1 > /sys/bus/coresight/devices/tmc_etr0/stop_on_flush
|
||||
|
||||
6. Start Coresight tracing on cores 1 and 2 using sysfs interface
|
||||
|
||||
7. Run some application on core 1::
|
||||
|
||||
#taskset -c 1 dd if=/dev/urandom of=/dev/null &
|
||||
|
||||
8. Invoke kernel panic on core 2::
|
||||
|
||||
#echo 1 > /proc/sys/kernel/panic
|
||||
#taskset -c 2 echo c > /proc/sysrq-trigger
|
||||
|
||||
9. From rebooted kernel or crashdump kernel, read crashdata::
|
||||
|
||||
#dd if=/dev/crash_tmc_etr0 of=/trace/cstrace.bin
|
||||
|
||||
10. Run opencsd decoder tools/scripts to generate the instruction trace.
|
||||
|
||||
Sample instruction trace dump
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Core1 dump::
|
||||
|
||||
A etm4_enable_hw: ffff800008ae1dd4
|
||||
CONTEXT EL2 etm4_enable_hw: ffff800008ae1dd4
|
||||
I etm4_enable_hw: ffff800008ae1dd4:
|
||||
d503201f nop
|
||||
I etm4_enable_hw: ffff800008ae1dd8:
|
||||
d503201f nop
|
||||
I etm4_enable_hw: ffff800008ae1ddc:
|
||||
d503201f nop
|
||||
I etm4_enable_hw: ffff800008ae1de0:
|
||||
d503201f nop
|
||||
I etm4_enable_hw: ffff800008ae1de4:
|
||||
d503201f nop
|
||||
I etm4_enable_hw: ffff800008ae1de8:
|
||||
d503233f paciasp
|
||||
I etm4_enable_hw: ffff800008ae1dec:
|
||||
a9be7bfd stp x29, x30, [sp, #-32]!
|
||||
I etm4_enable_hw: ffff800008ae1df0:
|
||||
910003fd mov x29, sp
|
||||
I etm4_enable_hw: ffff800008ae1df4:
|
||||
a90153f3 stp x19, x20, [sp, #16]
|
||||
I etm4_enable_hw: ffff800008ae1df8:
|
||||
2a0003f4 mov w20, w0
|
||||
I etm4_enable_hw: ffff800008ae1dfc:
|
||||
900085b3 adrp x19, ffff800009b95000 <reserved_mem+0xc48>
|
||||
I etm4_enable_hw: ffff800008ae1e00:
|
||||
910f4273 add x19, x19, #0x3d0
|
||||
I etm4_enable_hw: ffff800008ae1e04:
|
||||
f8747a60 ldr x0, [x19, x20, lsl #3]
|
||||
E etm4_enable_hw: ffff800008ae1e08:
|
||||
b4000140 cbz x0, ffff800008ae1e30 <etm4_starting_cpu+0x50>
|
||||
I 149.039572921 etm4_enable_hw: ffff800008ae1e30:
|
||||
a94153f3 ldp x19, x20, [sp, #16]
|
||||
I 149.039572921 etm4_enable_hw: ffff800008ae1e34:
|
||||
52800000 mov w0, #0x0 // #0
|
||||
I 149.039572921 etm4_enable_hw: ffff800008ae1e38:
|
||||
a8c27bfd ldp x29, x30, [sp], #32
|
||||
|
||||
..snip
|
||||
|
||||
149.052324811 chacha_block_generic: ffff800008642d80:
|
||||
9100a3e0 add x0,
|
||||
I 149.052324811 chacha_block_generic: ffff800008642d84:
|
||||
b86178a2 ldr w2, [x5, x1, lsl #2]
|
||||
I 149.052324811 chacha_block_generic: ffff800008642d88:
|
||||
8b010803 add x3, x0, x1, lsl #2
|
||||
I 149.052324811 chacha_block_generic: ffff800008642d8c:
|
||||
b85fc063 ldur w3, [x3, #-4]
|
||||
I 149.052324811 chacha_block_generic: ffff800008642d90:
|
||||
0b030042 add w2, w2, w3
|
||||
I 149.052324811 chacha_block_generic: ffff800008642d94:
|
||||
b8217882 str w2, [x4, x1, lsl #2]
|
||||
I 149.052324811 chacha_block_generic: ffff800008642d98:
|
||||
91000421 add x1, x1, #0x1
|
||||
I 149.052324811 chacha_block_generic: ffff800008642d9c:
|
||||
f100443f cmp x1, #0x11
|
||||
|
||||
|
||||
Core 2 dump::
|
||||
|
||||
A etm4_enable_hw: ffff800008ae1dd4
|
||||
CONTEXT EL2 etm4_enable_hw: ffff800008ae1dd4
|
||||
I etm4_enable_hw: ffff800008ae1dd4:
|
||||
d503201f nop
|
||||
I etm4_enable_hw: ffff800008ae1dd8:
|
||||
d503201f nop
|
||||
I etm4_enable_hw: ffff800008ae1ddc:
|
||||
d503201f nop
|
||||
I etm4_enable_hw: ffff800008ae1de0:
|
||||
d503201f nop
|
||||
I etm4_enable_hw: ffff800008ae1de4:
|
||||
d503201f nop
|
||||
I etm4_enable_hw: ffff800008ae1de8:
|
||||
d503233f paciasp
|
||||
I etm4_enable_hw: ffff800008ae1dec:
|
||||
a9be7bfd stp x29, x30, [sp, #-32]!
|
||||
I etm4_enable_hw: ffff800008ae1df0:
|
||||
910003fd mov x29, sp
|
||||
I etm4_enable_hw: ffff800008ae1df4:
|
||||
a90153f3 stp x19, x20, [sp, #16]
|
||||
I etm4_enable_hw: ffff800008ae1df8:
|
||||
2a0003f4 mov w20, w0
|
||||
I etm4_enable_hw: ffff800008ae1dfc:
|
||||
900085b3 adrp x19, ffff800009b95000 <reserved_mem+0xc48>
|
||||
I etm4_enable_hw: ffff800008ae1e00:
|
||||
910f4273 add x19, x19, #0x3d0
|
||||
I etm4_enable_hw: ffff800008ae1e04:
|
||||
f8747a60 ldr x0, [x19, x20, lsl #3]
|
||||
E etm4_enable_hw: ffff800008ae1e08:
|
||||
b4000140 cbz x0, ffff800008ae1e30 <etm4_starting_cpu+0x50>
|
||||
I 149.046243445 etm4_enable_hw: ffff800008ae1e30:
|
||||
a94153f3 ldp x19, x20, [sp, #16]
|
||||
I 149.046243445 etm4_enable_hw: ffff800008ae1e34:
|
||||
52800000 mov w0, #0x0 // #0
|
||||
I 149.046243445 etm4_enable_hw: ffff800008ae1e38:
|
||||
a8c27bfd ldp x29, x30, [sp], #32
|
||||
I 149.046243445 etm4_enable_hw: ffff800008ae1e3c:
|
||||
d50323bf autiasp
|
||||
E 149.046243445 etm4_enable_hw: ffff800008ae1e40:
|
||||
d65f03c0 ret
|
||||
A ete_sysreg_write: ffff800008adfa18
|
||||
|
||||
..snip
|
||||
|
||||
I 149.05422547 panic: ffff800008096300:
|
||||
a90363f7 stp x23, x24, [sp, #48]
|
||||
I 149.05422547 panic: ffff800008096304:
|
||||
6b00003f cmp w1, w0
|
||||
I 149.05422547 panic: ffff800008096308:
|
||||
3a411804 ccmn w0, #0x1, #0x4, ne // ne = any
|
||||
N 149.05422547 panic: ffff80000809630c:
|
||||
540001e0 b.eq ffff800008096348 <panic+0xe0> // b.none
|
||||
I 149.05422547 panic: ffff800008096310:
|
||||
f90023f9 str x25, [sp, #64]
|
||||
E 149.05422547 panic: ffff800008096314:
|
||||
97fe44ef bl ffff8000080276d0 <panic_smp_self_stop>
|
||||
A panic: ffff80000809634c
|
||||
I 149.05422547 panic: ffff80000809634c:
|
||||
910102d5 add x21, x22, #0x40
|
||||
I 149.05422547 panic: ffff800008096350:
|
||||
52800020 mov w0, #0x1 // #1
|
||||
E 149.05422547 panic: ffff800008096354:
|
||||
94166b8b bl ffff800008631180 <bust_spinlocks>
|
||||
N 149.054225518 bust_spinlocks: ffff800008631180:
|
||||
340000c0 cbz w0, ffff800008631198 <bust_spinlocks+0x18>
|
||||
I 149.054225518 bust_spinlocks: ffff800008631184:
|
||||
f000a321 adrp x1, ffff800009a98000 <pbufs.0+0xbb8>
|
||||
I 149.054225518 bust_spinlocks: ffff800008631188:
|
||||
b9405c20 ldr w0, [x1, #92]
|
||||
I 149.054225518 bust_spinlocks: ffff80000863118c:
|
||||
11000400 add w0, w0, #0x1
|
||||
I 149.054225518 bust_spinlocks: ffff800008631190:
|
||||
b9005c20 str w0, [x1, #92]
|
||||
E 149.054225518 bust_spinlocks: ffff800008631194:
|
||||
d65f03c0 ret
|
||||
A panic: ffff800008096358
|
||||
|
||||
Perf based testing
|
||||
------------------
|
||||
|
||||
Starting perf session
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
ETF::
|
||||
|
||||
perf record -e cs_etm/panicstop,@tmc_etf1/ -C 1
|
||||
perf record -e cs_etm/panicstop,@tmc_etf2/ -C 2
|
||||
|
||||
ETR::
|
||||
|
||||
perf record -e cs_etm/panicstop,@tmc_etr0/ -C 1,2
|
||||
|
||||
Reading trace data after panic
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Same sysfs based method explained above can be used to retrieve and
|
||||
decode the trace data after the reboot on kernel panic.
|
|
@ -627,7 +627,7 @@ regular ring buffer.
|
|||
AUX events and AUX trace data are two different things. Let's see an
|
||||
example::
|
||||
|
||||
perf record -a -e cycles -e cs_etm/@tmc_etr0/ -- sleep 2
|
||||
perf record -a -e cycles -e cs_etm// -- sleep 2
|
||||
|
||||
The above command enables two events: one is the event *cycles* from PMU
|
||||
and another is the AUX event *cs_etm* from Arm CoreSight, both are saved
|
||||
|
@ -766,7 +766,7 @@ only record AUX trace data at a specific time point which users are
|
|||
interested in. E.g. below gives an example of how to take snapshots
|
||||
with 1 second interval with Arm CoreSight::
|
||||
|
||||
perf record -e cs_etm/@tmc_etr0/u -S -a program &
|
||||
perf record -e cs_etm//u -S -a program &
|
||||
PERFPID=$!
|
||||
while true; do
|
||||
kill -USR2 $PERFPID
|
||||
|
|
43
MAINTAINERS
43
MAINTAINERS
|
@ -1317,11 +1317,23 @@ F: Documentation/devicetree/bindings/iio/adc/adi,ad4000.yaml
|
|||
F: Documentation/iio/ad4000.rst
|
||||
F: drivers/iio/adc/ad4000.c
|
||||
|
||||
AD4030 ADC DRIVER (AD4030-24/AD4630-16/AD4630-24/AD4632-16/AD4632-24)
|
||||
M: Michael Hennerich <michael.hennerich@analog.com>
|
||||
M: Nuno Sá <nuno.sa@analog.com>
|
||||
R: Esteban Blanc <eblanc@baylibre.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml
|
||||
F: Documentation/iio/ad4030.rst
|
||||
F: drivers/iio/adc/ad4030.c
|
||||
|
||||
ANALOG DEVICES INC AD4130 DRIVER
|
||||
M: Cosmin Tanislav <cosmin.tanislav@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml
|
||||
F: drivers/iio/adc/ad4130.c
|
||||
|
||||
|
@ -1345,6 +1357,15 @@ W: http://ez.analog.com/community/linux-device-drivers
|
|||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7091r*
|
||||
F: drivers/iio/adc/ad7091r*
|
||||
|
||||
ANALOG DEVICES INC AD7191 DRIVER
|
||||
M: Alisa-Dariana Roman <alisa.roman@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7191.yaml
|
||||
F: Documentation/iio/ad7191.rst
|
||||
F: drivers/iio/adc/ad7191.c
|
||||
|
||||
ANALOG DEVICES INC AD7192 DRIVER
|
||||
M: Alisa-Dariana Roman <alisa.roman@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
|
@ -1496,6 +1517,16 @@ W: https://ez.analog.com/linux-software-drivers
|
|||
F: Documentation/devicetree/bindings/iio/imu/adi,adis16475.yaml
|
||||
F: drivers/iio/imu/adis16475.c
|
||||
|
||||
ANALOG DEVICES INC ADIS16550 DRIVER
|
||||
M: Nuno Sa <nuno.sa@analog.com>
|
||||
M: Ramona Gradinariu <ramona.gradinariu@analog.com>
|
||||
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
|
||||
M: Robert Budai <robert.budai@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/imu/adi,adis16550.yaml
|
||||
|
||||
ANALOG DEVICES INC ADM1177 DRIVER
|
||||
M: Michael Hennerich <Michael.Hennerich@analog.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
|
@ -4485,6 +4516,13 @@ F: kernel/bpf/stackmap.c
|
|||
F: kernel/trace/bpf_trace.c
|
||||
F: lib/buildid.c
|
||||
|
||||
BROADCOM APDS9160 AMBIENT LIGHT SENSOR AND PROXIMITY DRIVER
|
||||
M: Mikael Gonella-Bolduc <m.gonella.bolduc@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/iio/light/brcm,apds9160.yaml
|
||||
F: drivers/iio/light/apds9160.c
|
||||
|
||||
BROADCOM ASP 2.0 ETHERNET DRIVER
|
||||
M: Justin Chen <justin.chen@broadcom.com>
|
||||
M: Florian Fainelli <florian.fainelli@broadcom.com>
|
||||
|
@ -12079,7 +12117,7 @@ F: drivers/mfd/intel-m10-bmc*
|
|||
F: include/linux/mfd/intel-m10-bmc.h
|
||||
|
||||
INTEL MAX10 BMC SECURE UPDATES
|
||||
M: Peter Colberg <peter.colberg@intel.com>
|
||||
M: Peter Colberg <peter.colberg@altera.com>
|
||||
L: linux-fpga@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-driver-intel-m10-bmc-sec-update
|
||||
|
@ -15905,6 +15943,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
|||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/counter/microchip-tcb-capture.c
|
||||
F: include/uapi/linux/counter/microchip-tcb-capture.h
|
||||
|
||||
MICROCHIP USB251XB DRIVER
|
||||
M: Richard Leitner <richard.leitner@skidata.com>
|
||||
|
@ -19218,6 +19257,7 @@ S: Maintained
|
|||
W: http://wiki.enneenne.com/index.php/LinuxPPS_support
|
||||
F: Documentation/ABI/testing/sysfs-pps
|
||||
F: Documentation/ABI/testing/sysfs-pps-gen
|
||||
F: Documentation/ABI/testing/sysfs-pps-gen-tio
|
||||
F: Documentation/devicetree/bindings/pps/pps-gpio.yaml
|
||||
F: Documentation/driver-api/pps.rst
|
||||
F: drivers/pps/
|
||||
|
@ -22857,7 +22897,6 @@ STAGING - SEPS525 LCD CONTROLLER DRIVERS
|
|||
M: Michael Hennerich <michael.hennerich@analog.com>
|
||||
L: linux-fbdev@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml
|
||||
F: drivers/staging/fbtft/fb_seps525.c
|
||||
|
||||
STAGING - SILICON MOTION SM750 FRAME BUFFER DRIVER
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#ifndef _LINUX_BINDER_INTERNAL_H
|
||||
#define _LINUX_BINDER_INTERNAL_H
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/miscdevice.h>
|
||||
|
|
|
@ -1181,25 +1181,6 @@ int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(mhi_queue_skb);
|
||||
|
||||
int mhi_queue_dma(struct mhi_device *mhi_dev, enum dma_data_direction dir,
|
||||
struct mhi_buf *mhi_buf, size_t len, enum mhi_flags mflags)
|
||||
{
|
||||
struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan :
|
||||
mhi_dev->dl_chan;
|
||||
struct mhi_buf_info buf_info = { };
|
||||
|
||||
buf_info.p_addr = mhi_buf->dma_addr;
|
||||
buf_info.cb_buf = mhi_buf;
|
||||
buf_info.pre_mapped = true;
|
||||
buf_info.len = len;
|
||||
|
||||
if (unlikely(mhi_chan->pre_alloc))
|
||||
return -EINVAL;
|
||||
|
||||
return mhi_queue(mhi_dev, &buf_info, dir, mflags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mhi_queue_dma);
|
||||
|
||||
int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
|
||||
struct mhi_buf_info *info, enum mhi_flags flags)
|
||||
{
|
||||
|
@ -1207,11 +1188,16 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
|
|||
struct mhi_ring_element *mhi_tre;
|
||||
struct mhi_buf_info *buf_info;
|
||||
int eot, eob, chain, bei;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
/* Protect accesses for reading and incrementing WP */
|
||||
write_lock_bh(&mhi_chan->lock);
|
||||
|
||||
if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buf_ring = &mhi_chan->buf_ring;
|
||||
tre_ring = &mhi_chan->tre_ring;
|
||||
|
||||
|
@ -1229,10 +1215,8 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
|
|||
|
||||
if (!info->pre_mapped) {
|
||||
ret = mhi_cntrl->map_single(mhi_cntrl, buf_info);
|
||||
if (ret) {
|
||||
write_unlock_bh(&mhi_chan->lock);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
eob = !!(flags & MHI_EOB);
|
||||
|
@ -1250,9 +1234,10 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
|
|||
mhi_add_ring_element(mhi_cntrl, tre_ring);
|
||||
mhi_add_ring_element(mhi_cntrl, buf_ring);
|
||||
|
||||
out:
|
||||
write_unlock_bh(&mhi_chan->lock);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mhi_queue_buf(struct mhi_device *mhi_dev, enum dma_data_direction dir,
|
||||
|
|
|
@ -297,6 +297,19 @@ static const struct mhi_pci_dev_info mhi_qcom_qdu100_info = {
|
|||
.sideband_wake = false,
|
||||
};
|
||||
|
||||
static const struct mhi_channel_config mhi_qcom_sa8775p_channels[] = {
|
||||
MHI_CHANNEL_CONFIG_UL(46, "IP_SW0", 2048, 1),
|
||||
MHI_CHANNEL_CONFIG_DL(47, "IP_SW0", 2048, 2),
|
||||
};
|
||||
|
||||
static struct mhi_event_config mhi_qcom_sa8775p_events[] = {
|
||||
/* first ring is control+data ring */
|
||||
MHI_EVENT_CONFIG_CTRL(0, 64),
|
||||
/* Software channels dedicated event ring */
|
||||
MHI_EVENT_CONFIG_SW_DATA(1, 64),
|
||||
MHI_EVENT_CONFIG_SW_DATA(2, 64),
|
||||
};
|
||||
|
||||
static const struct mhi_channel_config modem_qcom_v1_mhi_channels[] = {
|
||||
MHI_CHANNEL_CONFIG_UL(4, "DIAG", 16, 1),
|
||||
MHI_CHANNEL_CONFIG_DL(5, "DIAG", 16, 1),
|
||||
|
@ -327,6 +340,15 @@ static struct mhi_event_config modem_qcom_v1_mhi_events[] = {
|
|||
MHI_EVENT_CONFIG_HW_DATA(5, 2048, 101)
|
||||
};
|
||||
|
||||
static const struct mhi_controller_config mhi_qcom_sa8775p_config = {
|
||||
.max_channels = 128,
|
||||
.timeout_ms = 8000,
|
||||
.num_channels = ARRAY_SIZE(mhi_qcom_sa8775p_channels),
|
||||
.ch_cfg = mhi_qcom_sa8775p_channels,
|
||||
.num_events = ARRAY_SIZE(mhi_qcom_sa8775p_events),
|
||||
.event_cfg = mhi_qcom_sa8775p_events,
|
||||
};
|
||||
|
||||
static const struct mhi_controller_config modem_qcom_v2_mhiv_config = {
|
||||
.max_channels = 128,
|
||||
.timeout_ms = 8000,
|
||||
|
@ -346,6 +368,16 @@ static const struct mhi_controller_config modem_qcom_v1_mhiv_config = {
|
|||
.event_cfg = modem_qcom_v1_mhi_events,
|
||||
};
|
||||
|
||||
static const struct mhi_pci_dev_info mhi_qcom_sa8775p_info = {
|
||||
.name = "qcom-sa8775p",
|
||||
.edl_trigger = false,
|
||||
.config = &mhi_qcom_sa8775p_config,
|
||||
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
|
||||
.dma_data_width = 32,
|
||||
.mru_default = 32768,
|
||||
.sideband_wake = false,
|
||||
};
|
||||
|
||||
static const struct mhi_pci_dev_info mhi_qcom_sdx75_info = {
|
||||
.name = "qcom-sdx75m",
|
||||
.fw = "qcom/sdx75m/xbl.elf",
|
||||
|
@ -772,6 +804,8 @@ static const struct mhi_pci_dev_info mhi_netprisma_fcun69_info = {
|
|||
|
||||
/* Keep the list sorted based on the PID. New VID should be added as the last entry */
|
||||
static const struct pci_device_id mhi_pci_id_table[] = {
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0116),
|
||||
.driver_data = (kernel_ulong_t) &mhi_qcom_sa8775p_info },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0304),
|
||||
.driver_data = (kernel_ulong_t) &mhi_qcom_sdx24_info },
|
||||
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, PCI_VENDOR_ID_QCOM, 0x010c),
|
||||
|
|
|
@ -1296,20 +1296,6 @@ int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(mhi_force_rddm_mode);
|
||||
|
||||
void mhi_device_get(struct mhi_device *mhi_dev)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
|
||||
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);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mhi_device_get);
|
||||
|
||||
int mhi_device_get_sync(struct mhi_device *mhi_dev)
|
||||
{
|
||||
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
#include <linux/sysfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/device/faux.h>
|
||||
#include <asm/io.h> /* inb/outb */
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
|
@ -742,7 +742,7 @@ static ssize_t store_reset (struct device *d,
|
|||
|
||||
static DEVICE_ATTR(reset, (S_IWUSR|S_IWGRP), NULL, store_reset);
|
||||
|
||||
static struct attribute *tlclk_sysfs_entries[] = {
|
||||
static struct attribute *tlclk_attrs[] = {
|
||||
&dev_attr_current_ref.attr,
|
||||
&dev_attr_telclock_version.attr,
|
||||
&dev_attr_alarms.attr,
|
||||
|
@ -766,13 +766,9 @@ static struct attribute *tlclk_sysfs_entries[] = {
|
|||
&dev_attr_reset.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(tlclk);
|
||||
|
||||
static const struct attribute_group tlclk_attribute_group = {
|
||||
.name = NULL, /* put in device directory */
|
||||
.attrs = tlclk_sysfs_entries,
|
||||
};
|
||||
|
||||
static struct platform_device *tlclk_device;
|
||||
static struct faux_device *tlclk_device;
|
||||
|
||||
static int __init tlclk_init(void)
|
||||
{
|
||||
|
@ -817,24 +813,13 @@ static int __init tlclk_init(void)
|
|||
goto out3;
|
||||
}
|
||||
|
||||
tlclk_device = platform_device_register_simple("telco_clock",
|
||||
-1, NULL, 0);
|
||||
if (IS_ERR(tlclk_device)) {
|
||||
printk(KERN_ERR "tlclk: platform_device_register failed.\n");
|
||||
ret = PTR_ERR(tlclk_device);
|
||||
tlclk_device = faux_device_create_with_groups("telco_clock", NULL, NULL, tlclk_groups);
|
||||
if (!tlclk_device) {
|
||||
ret = -ENODEV;
|
||||
goto out4;
|
||||
}
|
||||
|
||||
ret = sysfs_create_group(&tlclk_device->dev.kobj,
|
||||
&tlclk_attribute_group);
|
||||
if (ret) {
|
||||
printk(KERN_ERR "tlclk: failed to create sysfs device attributes.\n");
|
||||
goto out5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out5:
|
||||
platform_device_unregister(tlclk_device);
|
||||
out4:
|
||||
misc_deregister(&tlclk_miscdev);
|
||||
out3:
|
||||
|
@ -848,8 +833,7 @@ out1:
|
|||
|
||||
static void __exit tlclk_cleanup(void)
|
||||
{
|
||||
sysfs_remove_group(&tlclk_device->dev.kobj, &tlclk_attribute_group);
|
||||
platform_device_unregister(tlclk_device);
|
||||
faux_device_destroy(tlclk_device);
|
||||
misc_deregister(&tlclk_miscdev);
|
||||
unregister_chrdev(tlclk_major, "telco_clock");
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/workqueue.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/string_choices.h>
|
||||
#include "../tty/hvc/hvc_console.h"
|
||||
|
||||
#define is_rproc_enabled IS_ENABLED(CONFIG_REMOTEPROC)
|
||||
|
@ -1269,8 +1270,7 @@ static int port_debugfs_show(struct seq_file *s, void *data)
|
|||
seq_printf(s, "bytes_sent: %lu\n", port->stats.bytes_sent);
|
||||
seq_printf(s, "bytes_received: %lu\n", port->stats.bytes_received);
|
||||
seq_printf(s, "bytes_discarded: %lu\n", port->stats.bytes_discarded);
|
||||
seq_printf(s, "is_console: %s\n",
|
||||
is_console_port(port) ? "yes" : "no");
|
||||
seq_printf(s, "is_console: %s\n", str_yes_no(is_console_port(port)));
|
||||
seq_printf(s, "console_vtermno: %u\n", port->cons.vtermno);
|
||||
|
||||
return 0;
|
||||
|
@ -1321,7 +1321,6 @@ static void send_sigio_to_port(struct port *port)
|
|||
|
||||
static int add_port(struct ports_device *portdev, u32 id)
|
||||
{
|
||||
char debugfs_name[16];
|
||||
struct port *port;
|
||||
dev_t devt;
|
||||
int err;
|
||||
|
@ -1424,9 +1423,7 @@ static int add_port(struct ports_device *portdev, u32 id)
|
|||
* Finally, create the debugfs file that we can use to
|
||||
* inspect a port's state at any time
|
||||
*/
|
||||
snprintf(debugfs_name, sizeof(debugfs_name), "vport%up%u",
|
||||
port->portdev->vdev->index, id);
|
||||
port->debugfs_file = debugfs_create_file(debugfs_name, 0444,
|
||||
port->debugfs_file = debugfs_create_file(dev_name(port->dev), 0444,
|
||||
pdrvdata.debugfs_dir,
|
||||
port, &port_debugfs_fops);
|
||||
return 0;
|
||||
|
|
|
@ -6,18 +6,24 @@
|
|||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/counter.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <uapi/linux/counter/microchip-tcb-capture.h>
|
||||
#include <soc/at91/atmel_tcb.h>
|
||||
|
||||
#define ATMEL_TC_CMR_MASK (ATMEL_TC_LDRA_RISING | ATMEL_TC_LDRB_FALLING | \
|
||||
ATMEL_TC_ETRGEDG_RISING | ATMEL_TC_LDBDIS | \
|
||||
ATMEL_TC_LDBSTOP)
|
||||
|
||||
#define ATMEL_TC_DEF_IRQS (ATMEL_TC_ETRGS | ATMEL_TC_COVFS | \
|
||||
ATMEL_TC_LDRAS | ATMEL_TC_LDRBS | ATMEL_TC_CPCS)
|
||||
|
||||
#define ATMEL_TC_QDEN BIT(8)
|
||||
#define ATMEL_TC_POSEN BIT(9)
|
||||
|
||||
|
@ -247,6 +253,90 @@ static int mchp_tc_count_read(struct counter_device *counter,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_tc_count_cap_read(struct counter_device *counter,
|
||||
struct counter_count *count, size_t idx, u64 *val)
|
||||
{
|
||||
struct mchp_tc_data *const priv = counter_priv(counter);
|
||||
u32 cnt;
|
||||
int ret;
|
||||
|
||||
switch (idx) {
|
||||
case COUNTER_MCHP_EXCAP_RA:
|
||||
ret = regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], RA), &cnt);
|
||||
break;
|
||||
case COUNTER_MCHP_EXCAP_RB:
|
||||
ret = regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], RB), &cnt);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = cnt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_tc_count_cap_write(struct counter_device *counter,
|
||||
struct counter_count *count, size_t idx, u64 val)
|
||||
{
|
||||
struct mchp_tc_data *const priv = counter_priv(counter);
|
||||
int ret;
|
||||
|
||||
if (val > U32_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
switch (idx) {
|
||||
case COUNTER_MCHP_EXCAP_RA:
|
||||
ret = regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], RA), val);
|
||||
break;
|
||||
case COUNTER_MCHP_EXCAP_RB:
|
||||
ret = regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], RB), val);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mchp_tc_count_compare_read(struct counter_device *counter, struct counter_count *count,
|
||||
u64 *val)
|
||||
{
|
||||
struct mchp_tc_data *const priv = counter_priv(counter);
|
||||
u32 cnt;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], RC), &cnt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = cnt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_tc_count_compare_write(struct counter_device *counter, struct counter_count *count,
|
||||
u64 val)
|
||||
{
|
||||
struct mchp_tc_data *const priv = counter_priv(counter);
|
||||
|
||||
if (val > U32_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
return regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], RC), val);
|
||||
}
|
||||
|
||||
static DEFINE_COUNTER_ARRAY_CAPTURE(mchp_tc_cnt_cap_array, 2);
|
||||
|
||||
static struct counter_comp mchp_tc_count_ext[] = {
|
||||
COUNTER_COMP_ARRAY_CAPTURE(mchp_tc_count_cap_read, mchp_tc_count_cap_write,
|
||||
mchp_tc_cnt_cap_array),
|
||||
COUNTER_COMP_COMPARE(mchp_tc_count_compare_read, mchp_tc_count_compare_write),
|
||||
};
|
||||
|
||||
static struct counter_count mchp_tc_counts[] = {
|
||||
{
|
||||
.id = 0,
|
||||
|
@ -255,6 +345,8 @@ static struct counter_count mchp_tc_counts[] = {
|
|||
.num_functions = ARRAY_SIZE(mchp_tc_count_functions),
|
||||
.synapses = mchp_tc_count_synapses,
|
||||
.num_synapses = ARRAY_SIZE(mchp_tc_count_synapses),
|
||||
.ext = mchp_tc_count_ext,
|
||||
.num_ext = ARRAY_SIZE(mchp_tc_count_ext),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -294,6 +386,65 @@ static const struct of_device_id atmel_tc_of_match[] = {
|
|||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static irqreturn_t mchp_tc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct counter_device *const counter = dev_id;
|
||||
struct mchp_tc_data *const priv = counter_priv(counter);
|
||||
u32 sr, mask;
|
||||
|
||||
regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], SR), &sr);
|
||||
regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], IMR), &mask);
|
||||
|
||||
sr &= mask;
|
||||
if (!(sr & ATMEL_TC_ALL_IRQ))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (sr & ATMEL_TC_ETRGS)
|
||||
counter_push_event(counter, COUNTER_EVENT_CHANGE_OF_STATE,
|
||||
COUNTER_MCHP_EVCHN_CV);
|
||||
if (sr & ATMEL_TC_LDRAS)
|
||||
counter_push_event(counter, COUNTER_EVENT_CAPTURE,
|
||||
COUNTER_MCHP_EVCHN_RA);
|
||||
if (sr & ATMEL_TC_LDRBS)
|
||||
counter_push_event(counter, COUNTER_EVENT_CAPTURE,
|
||||
COUNTER_MCHP_EVCHN_RB);
|
||||
if (sr & ATMEL_TC_CPCS)
|
||||
counter_push_event(counter, COUNTER_EVENT_THRESHOLD,
|
||||
COUNTER_MCHP_EVCHN_RC);
|
||||
if (sr & ATMEL_TC_COVFS)
|
||||
counter_push_event(counter, COUNTER_EVENT_OVERFLOW,
|
||||
COUNTER_MCHP_EVCHN_CV);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void mchp_tc_irq_remove(void *ptr)
|
||||
{
|
||||
struct mchp_tc_data *priv = ptr;
|
||||
|
||||
regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], IDR), ATMEL_TC_DEF_IRQS);
|
||||
}
|
||||
|
||||
static int mchp_tc_irq_enable(struct counter_device *const counter, int irq)
|
||||
{
|
||||
struct mchp_tc_data *const priv = counter_priv(counter);
|
||||
int ret = devm_request_irq(counter->parent, irq, mchp_tc_isr, 0,
|
||||
dev_name(counter->parent), counter);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(priv->regmap, ATMEL_TC_REG(priv->channel[0], IER), ATMEL_TC_DEF_IRQS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(counter->parent, mchp_tc_irq_remove, priv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mchp_tc_clk_remove(void *ptr)
|
||||
{
|
||||
clk_disable_unprepare((struct clk *)ptr);
|
||||
|
@ -378,6 +529,15 @@ static int mchp_tc_probe(struct platform_device *pdev)
|
|||
counter->num_signals = ARRAY_SIZE(mchp_tc_count_signals);
|
||||
counter->signals = mchp_tc_count_signals;
|
||||
|
||||
i = of_irq_get(np->parent, 0);
|
||||
if (i == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
if (i > 0) {
|
||||
ret = mchp_tc_irq_enable(counter, i);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret, "Failed to set up IRQ");
|
||||
}
|
||||
|
||||
ret = devm_counter_add(&pdev->dev, counter);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n");
|
||||
|
|
|
@ -107,6 +107,15 @@
|
|||
#define QCLR_PCE BIT(1)
|
||||
#define QCLR_INT BIT(0)
|
||||
|
||||
#define QEPSTS_UPEVNT BIT(7)
|
||||
#define QEPSTS_FDF BIT(6)
|
||||
#define QEPSTS_QDF BIT(5)
|
||||
#define QEPSTS_QDLF BIT(4)
|
||||
#define QEPSTS_COEF BIT(3)
|
||||
#define QEPSTS_CDEF BIT(2)
|
||||
#define QEPSTS_FIMF BIT(1)
|
||||
#define QEPSTS_PCEF BIT(0)
|
||||
|
||||
/* EQEP Inputs */
|
||||
enum {
|
||||
TI_EQEP_SIGNAL_QEPA, /* QEPA/XCLK */
|
||||
|
@ -286,6 +295,9 @@ static int ti_eqep_events_configure(struct counter_device *counter)
|
|||
case COUNTER_EVENT_UNDERFLOW:
|
||||
qeint |= QEINT_PCU;
|
||||
break;
|
||||
case COUNTER_EVENT_DIRECTION_CHANGE:
|
||||
qeint |= QEINT_QDC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -298,6 +310,7 @@ static int ti_eqep_watch_validate(struct counter_device *counter,
|
|||
switch (watch->event) {
|
||||
case COUNTER_EVENT_OVERFLOW:
|
||||
case COUNTER_EVENT_UNDERFLOW:
|
||||
case COUNTER_EVENT_DIRECTION_CHANGE:
|
||||
if (watch->channel != 0)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -368,11 +381,27 @@ static int ti_eqep_position_enable_write(struct counter_device *counter,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ti_eqep_direction_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
enum counter_count_direction *direction)
|
||||
{
|
||||
struct ti_eqep_cnt *priv = counter_priv(counter);
|
||||
u32 qepsts;
|
||||
|
||||
regmap_read(priv->regmap16, QEPSTS, &qepsts);
|
||||
|
||||
*direction = (qepsts & QEPSTS_QDF) ? COUNTER_COUNT_DIRECTION_FORWARD
|
||||
: COUNTER_COUNT_DIRECTION_BACKWARD;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct counter_comp ti_eqep_position_ext[] = {
|
||||
COUNTER_COMP_CEILING(ti_eqep_position_ceiling_read,
|
||||
ti_eqep_position_ceiling_write),
|
||||
COUNTER_COMP_ENABLE(ti_eqep_position_enable_read,
|
||||
ti_eqep_position_enable_write),
|
||||
COUNTER_COMP_DIRECTION(ti_eqep_direction_read),
|
||||
};
|
||||
|
||||
static struct counter_signal ti_eqep_signals[] = {
|
||||
|
@ -439,6 +468,9 @@ static irqreturn_t ti_eqep_irq_handler(int irq, void *dev_id)
|
|||
if (qflg & QFLG_PCU)
|
||||
counter_push_event(counter, COUNTER_EVENT_UNDERFLOW, 0);
|
||||
|
||||
if (qflg & QFLG_QDC)
|
||||
counter_push_event(counter, COUNTER_EVENT_DIRECTION_CHANGE, 0);
|
||||
|
||||
regmap_write(priv->regmap16, QCLR, qflg);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
/* V2 Defines */
|
||||
#define VSE_CVP_TX_CREDITS 0x49 /* 8bit */
|
||||
|
||||
#define V2_CREDIT_TIMEOUT_US 20000
|
||||
#define V2_CREDIT_TIMEOUT_US 40000
|
||||
#define V2_CHECK_CREDIT_US 10
|
||||
#define V2_POLL_TIMEOUT_US 1000000
|
||||
#define V2_USER_TIMEOUT_US 500000
|
||||
|
|
|
@ -69,7 +69,7 @@ static struct platform_driver versal_fpga_driver = {
|
|||
.probe = versal_fpga_probe,
|
||||
.driver = {
|
||||
.name = "versal_fpga_manager",
|
||||
.of_match_table = of_match_ptr(versal_fpga_of_match),
|
||||
.of_match_table = versal_fpga_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(versal_fpga_driver);
|
||||
|
|
|
@ -912,7 +912,9 @@ static enum fw_upload_err cc1352_prepare(struct fw_upload *fw_upload,
|
|||
cc1352_bootloader_reset(bg);
|
||||
WRITE_ONCE(bg->flashing_mode, false);
|
||||
msleep(200);
|
||||
gb_greybus_init(bg);
|
||||
if (gb_greybus_init(bg) < 0)
|
||||
return dev_err_probe(&bg->sd->dev, FW_UPLOAD_ERR_RW_ERROR,
|
||||
"Failed to initialize greybus");
|
||||
gb_beagleplay_start_svc(bg);
|
||||
return FW_UPLOAD_ERR_FW_INVALID;
|
||||
}
|
||||
|
|
|
@ -133,6 +133,18 @@ config CORESIGHT_STM
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called coresight-stm.
|
||||
|
||||
config CORESIGHT_CTCU
|
||||
tristate "CoreSight TMC Control Unit driver"
|
||||
depends on CORESIGHT_LINK_AND_SINK_TMC
|
||||
help
|
||||
This driver provides support for CoreSight TMC Control Unit
|
||||
that hosts miscellaneous configuration registers. This is
|
||||
primarily used for controlling the behaviors of the TMC
|
||||
ETR device.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called coresight-ctcu.
|
||||
|
||||
config CORESIGHT_CPU_DEBUG
|
||||
tristate "CoreSight CPU Debug driver"
|
||||
depends on ARM || ARM64
|
||||
|
|
|
@ -25,7 +25,7 @@ subdir-ccflags-y += $(condflags)
|
|||
obj-$(CONFIG_CORESIGHT) += coresight.o
|
||||
coresight-y := coresight-core.o coresight-etm-perf.o coresight-platform.o \
|
||||
coresight-sysfs.o coresight-syscfg.o coresight-config.o \
|
||||
coresight-cfg-preload.o coresight-cfg-afdo.o \
|
||||
coresight-cfg-preload.o coresight-cfg-afdo.o coresight-cfg-pstop.o \
|
||||
coresight-syscfg-configfs.o coresight-trace-id.o
|
||||
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o
|
||||
coresight-tmc-y := coresight-tmc-core.o coresight-tmc-etf.o \
|
||||
|
@ -51,3 +51,5 @@ coresight-cti-y := coresight-cti-core.o coresight-cti-platform.o \
|
|||
coresight-cti-sysfs.o
|
||||
obj-$(CONFIG_ULTRASOC_SMB) += ultrasoc-smb.o
|
||||
obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o
|
||||
obj-$(CONFIG_CORESIGHT_CTCU) += coresight-ctcu.o
|
||||
coresight-ctcu-y := coresight-ctcu-core.o
|
||||
|
|
|
@ -269,7 +269,7 @@ catu_init_sg_table(struct device *catu_dev, int node,
|
|||
* Each table can address upto 1MB and we can have
|
||||
* CATU_PAGES_PER_SYSPAGE tables in a system page.
|
||||
*/
|
||||
nr_tpages = DIV_ROUND_UP(size, SZ_1M) / CATU_PAGES_PER_SYSPAGE;
|
||||
nr_tpages = DIV_ROUND_UP(size, CATU_PAGES_PER_SYSPAGE * SZ_1M);
|
||||
catu_table = tmc_alloc_sg_table(catu_dev, node, nr_tpages,
|
||||
size >> PAGE_SHIFT, pages);
|
||||
if (IS_ERR(catu_table))
|
||||
|
@ -594,7 +594,7 @@ static void catu_remove(struct amba_device *adev)
|
|||
__catu_remove(&adev->dev);
|
||||
}
|
||||
|
||||
static struct amba_id catu_ids[] = {
|
||||
static const struct amba_id catu_ids[] = {
|
||||
CS_AMBA_ID(0x000bb9ee),
|
||||
{},
|
||||
};
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
static struct cscfg_feature_desc *preload_feats[] = {
|
||||
#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
|
||||
&strobe_etm4x,
|
||||
&gen_etrig_etm4x,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
@ -20,6 +21,7 @@ static struct cscfg_feature_desc *preload_feats[] = {
|
|||
static struct cscfg_config_desc *preload_cfgs[] = {
|
||||
#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
|
||||
&afdo_etm4x,
|
||||
&pstop_etm4x,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
|
|
@ -10,4 +10,6 @@
|
|||
#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
|
||||
extern struct cscfg_feature_desc strobe_etm4x;
|
||||
extern struct cscfg_config_desc afdo_etm4x;
|
||||
extern struct cscfg_feature_desc gen_etrig_etm4x;
|
||||
extern struct cscfg_config_desc pstop_etm4x;
|
||||
#endif
|
||||
|
|
83
drivers/hwtracing/coresight/coresight-cfg-pstop.c
Normal file
83
drivers/hwtracing/coresight/coresight-cfg-pstop.c
Normal file
|
@ -0,0 +1,83 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright(C) 2023 Marvell.
|
||||
* Based on coresight-cfg-afdo.c
|
||||
*/
|
||||
|
||||
#include "coresight-config.h"
|
||||
|
||||
/* ETMv4 includes and features */
|
||||
#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
|
||||
#include "coresight-etm4x-cfg.h"
|
||||
|
||||
/* preload configurations and features */
|
||||
|
||||
/* preload in features for ETMv4 */
|
||||
|
||||
/* panic_stop feature */
|
||||
static struct cscfg_parameter_desc gen_etrig_params[] = {
|
||||
{
|
||||
.name = "address",
|
||||
.value = (u64)panic,
|
||||
},
|
||||
};
|
||||
|
||||
static struct cscfg_regval_desc gen_etrig_regs[] = {
|
||||
/* resource selector */
|
||||
{
|
||||
.type = CS_CFG_REG_TYPE_RESOURCE,
|
||||
.offset = TRCRSCTLRn(2),
|
||||
.hw_info = ETM4_CFG_RES_SEL,
|
||||
.val32 = 0x40001,
|
||||
},
|
||||
/* single address comparator */
|
||||
{
|
||||
.type = CS_CFG_REG_TYPE_RESOURCE | CS_CFG_REG_TYPE_VAL_64BIT |
|
||||
CS_CFG_REG_TYPE_VAL_PARAM,
|
||||
.offset = TRCACVRn(0),
|
||||
.val32 = 0x0,
|
||||
},
|
||||
{
|
||||
.type = CS_CFG_REG_TYPE_RESOURCE,
|
||||
.offset = TRCACATRn(0),
|
||||
.val64 = 0xf00,
|
||||
},
|
||||
/* Driver external output[0] with comparator out */
|
||||
{
|
||||
.type = CS_CFG_REG_TYPE_RESOURCE,
|
||||
.offset = TRCEVENTCTL0R,
|
||||
.val32 = 0x2,
|
||||
},
|
||||
/* end of regs */
|
||||
};
|
||||
|
||||
struct cscfg_feature_desc gen_etrig_etm4x = {
|
||||
.name = "gen_etrig",
|
||||
.description = "Generate external trigger on address match\n"
|
||||
"parameter \'address\': address of kernel address\n",
|
||||
.match_flags = CS_CFG_MATCH_CLASS_SRC_ETM4,
|
||||
.nr_params = ARRAY_SIZE(gen_etrig_params),
|
||||
.params_desc = gen_etrig_params,
|
||||
.nr_regs = ARRAY_SIZE(gen_etrig_regs),
|
||||
.regs_desc = gen_etrig_regs,
|
||||
};
|
||||
|
||||
/* create a panic stop configuration */
|
||||
|
||||
/* the total number of parameters in used features */
|
||||
#define PSTOP_NR_PARAMS ARRAY_SIZE(gen_etrig_params)
|
||||
|
||||
static const char *pstop_ref_names[] = {
|
||||
"gen_etrig",
|
||||
};
|
||||
|
||||
struct cscfg_config_desc pstop_etm4x = {
|
||||
.name = "panicstop",
|
||||
.description = "Stop ETM on kernel panic\n",
|
||||
.nr_feat_refs = ARRAY_SIZE(pstop_ref_names),
|
||||
.feat_ref_names = pstop_ref_names,
|
||||
.nr_total_params = PSTOP_NR_PARAMS,
|
||||
};
|
||||
|
||||
/* end of ETM4x configurations */
|
||||
#endif /* IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X) */
|
|
@ -76,10 +76,10 @@ static int cscfg_set_on_enable(struct cscfg_feature_csdev *feat_csdev)
|
|||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(feat_csdev->drv_spinlock, flags);
|
||||
raw_spin_lock_irqsave(feat_csdev->drv_spinlock, flags);
|
||||
for (i = 0; i < feat_csdev->nr_regs; i++)
|
||||
cscfg_set_reg(&feat_csdev->regs_csdev[i]);
|
||||
spin_unlock_irqrestore(feat_csdev->drv_spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(feat_csdev->drv_spinlock, flags);
|
||||
dev_dbg(&feat_csdev->csdev->dev, "Feature %s: %s",
|
||||
feat_csdev->feat_desc->name, "set on enable");
|
||||
return 0;
|
||||
|
@ -91,10 +91,10 @@ static void cscfg_save_on_disable(struct cscfg_feature_csdev *feat_csdev)
|
|||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(feat_csdev->drv_spinlock, flags);
|
||||
raw_spin_lock_irqsave(feat_csdev->drv_spinlock, flags);
|
||||
for (i = 0; i < feat_csdev->nr_regs; i++)
|
||||
cscfg_save_reg(&feat_csdev->regs_csdev[i]);
|
||||
spin_unlock_irqrestore(feat_csdev->drv_spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(feat_csdev->drv_spinlock, flags);
|
||||
dev_dbg(&feat_csdev->csdev->dev, "Feature %s: %s",
|
||||
feat_csdev->feat_desc->name, "save on disable");
|
||||
}
|
||||
|
|
|
@ -206,7 +206,7 @@ struct cscfg_feature_csdev {
|
|||
const struct cscfg_feature_desc *feat_desc;
|
||||
struct coresight_device *csdev;
|
||||
struct list_head node;
|
||||
spinlock_t *drv_spinlock;
|
||||
raw_spinlock_t *drv_spinlock;
|
||||
int nr_params;
|
||||
struct cscfg_parameter_csdev *params_csdev;
|
||||
int nr_regs;
|
||||
|
|
|
@ -19,10 +19,12 @@
|
|||
#include <linux/property.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/panic_notifier.h>
|
||||
|
||||
#include "coresight-etm-perf.h"
|
||||
#include "coresight-priv.h"
|
||||
#include "coresight-syscfg.h"
|
||||
#include "coresight-trace-id.h"
|
||||
|
||||
/*
|
||||
* Mutex used to lock all sysfs enable and disable actions and loading and
|
||||
|
@ -75,14 +77,14 @@ struct coresight_device *coresight_get_percpu_sink(int cpu)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_get_percpu_sink);
|
||||
|
||||
static struct coresight_device *coresight_get_source(struct list_head *path)
|
||||
static struct coresight_device *coresight_get_source(struct coresight_path *path)
|
||||
{
|
||||
struct coresight_device *csdev;
|
||||
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
csdev = list_first_entry(path, struct coresight_node, link)->csdev;
|
||||
csdev = list_first_entry(&path->path_list, struct coresight_node, link)->csdev;
|
||||
if (!coresight_is_device_source(csdev))
|
||||
return NULL;
|
||||
|
||||
|
@ -331,12 +333,12 @@ static int coresight_enable_helper(struct coresight_device *csdev,
|
|||
return helper_ops(csdev)->enable(csdev, mode, data);
|
||||
}
|
||||
|
||||
static void coresight_disable_helper(struct coresight_device *csdev)
|
||||
static void coresight_disable_helper(struct coresight_device *csdev, void *data)
|
||||
{
|
||||
helper_ops(csdev)->disable(csdev, NULL);
|
||||
helper_ops(csdev)->disable(csdev, data);
|
||||
}
|
||||
|
||||
static void coresight_disable_helpers(struct coresight_device *csdev)
|
||||
static void coresight_disable_helpers(struct coresight_device *csdev, void *data)
|
||||
{
|
||||
int i;
|
||||
struct coresight_device *helper;
|
||||
|
@ -344,7 +346,7 @@ static void coresight_disable_helpers(struct coresight_device *csdev)
|
|||
for (i = 0; i < csdev->pdata->nr_outconns; ++i) {
|
||||
helper = csdev->pdata->out_conns[i]->dest_dev;
|
||||
if (helper && coresight_is_helper(helper))
|
||||
coresight_disable_helper(helper);
|
||||
coresight_disable_helper(helper, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -361,7 +363,7 @@ static void coresight_disable_helpers(struct coresight_device *csdev)
|
|||
void coresight_disable_source(struct coresight_device *csdev, void *data)
|
||||
{
|
||||
source_ops(csdev)->disable(csdev, data);
|
||||
coresight_disable_helpers(csdev);
|
||||
coresight_disable_helpers(csdev, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_disable_source);
|
||||
|
||||
|
@ -370,16 +372,16 @@ EXPORT_SYMBOL_GPL(coresight_disable_source);
|
|||
* @nd in the list. If @nd is NULL, all the components, except the SOURCE are
|
||||
* disabled.
|
||||
*/
|
||||
static void coresight_disable_path_from(struct list_head *path,
|
||||
static void coresight_disable_path_from(struct coresight_path *path,
|
||||
struct coresight_node *nd)
|
||||
{
|
||||
u32 type;
|
||||
struct coresight_device *csdev, *parent, *child;
|
||||
|
||||
if (!nd)
|
||||
nd = list_first_entry(path, struct coresight_node, link);
|
||||
nd = list_first_entry(&path->path_list, struct coresight_node, link);
|
||||
|
||||
list_for_each_entry_continue(nd, path, link) {
|
||||
list_for_each_entry_continue(nd, &path->path_list, link) {
|
||||
csdev = nd->csdev;
|
||||
type = csdev->type;
|
||||
|
||||
|
@ -417,11 +419,11 @@ static void coresight_disable_path_from(struct list_head *path,
|
|||
}
|
||||
|
||||
/* Disable all helpers adjacent along the path last */
|
||||
coresight_disable_helpers(csdev);
|
||||
coresight_disable_helpers(csdev, path);
|
||||
}
|
||||
}
|
||||
|
||||
void coresight_disable_path(struct list_head *path)
|
||||
void coresight_disable_path(struct coresight_path *path)
|
||||
{
|
||||
coresight_disable_path_from(path, NULL);
|
||||
}
|
||||
|
@ -446,7 +448,7 @@ static int coresight_enable_helpers(struct coresight_device *csdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int coresight_enable_path(struct list_head *path, enum cs_mode mode,
|
||||
int coresight_enable_path(struct coresight_path *path, enum cs_mode mode,
|
||||
void *sink_data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -456,12 +458,12 @@ int coresight_enable_path(struct list_head *path, enum cs_mode mode,
|
|||
struct coresight_device *source;
|
||||
|
||||
source = coresight_get_source(path);
|
||||
list_for_each_entry_reverse(nd, path, link) {
|
||||
list_for_each_entry_reverse(nd, &path->path_list, link) {
|
||||
csdev = nd->csdev;
|
||||
type = csdev->type;
|
||||
|
||||
/* Enable all helpers adjacent to the path first */
|
||||
ret = coresight_enable_helpers(csdev, mode, sink_data);
|
||||
ret = coresight_enable_helpers(csdev, mode, path);
|
||||
if (ret)
|
||||
goto err;
|
||||
/*
|
||||
|
@ -509,20 +511,21 @@ err:
|
|||
goto out;
|
||||
}
|
||||
|
||||
struct coresight_device *coresight_get_sink(struct list_head *path)
|
||||
struct coresight_device *coresight_get_sink(struct coresight_path *path)
|
||||
{
|
||||
struct coresight_device *csdev;
|
||||
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
csdev = list_last_entry(path, struct coresight_node, link)->csdev;
|
||||
csdev = list_last_entry(&path->path_list, struct coresight_node, link)->csdev;
|
||||
if (csdev->type != CORESIGHT_DEV_TYPE_SINK &&
|
||||
csdev->type != CORESIGHT_DEV_TYPE_LINKSINK)
|
||||
return NULL;
|
||||
|
||||
return csdev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_get_sink);
|
||||
|
||||
u32 coresight_get_sink_id(struct coresight_device *csdev)
|
||||
{
|
||||
|
@ -653,6 +656,50 @@ static void coresight_drop_device(struct coresight_device *csdev)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* coresight device will read their existing or alloc a trace ID, if their trace_id
|
||||
* callback is set.
|
||||
*
|
||||
* Return 0 if the trace_id callback is not set.
|
||||
* Return the result of the trace_id callback if it is set. The return value
|
||||
* will be the trace_id if successful, and an error number if it fails.
|
||||
*/
|
||||
static int coresight_get_trace_id(struct coresight_device *csdev,
|
||||
enum cs_mode mode,
|
||||
struct coresight_device *sink)
|
||||
{
|
||||
if (coresight_ops(csdev)->trace_id)
|
||||
return coresight_ops(csdev)->trace_id(csdev, mode, sink);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Call this after creating the path and before enabling it. This leaves
|
||||
* the trace ID set on the path, or it remains 0 if it couldn't be assigned.
|
||||
*/
|
||||
void coresight_path_assign_trace_id(struct coresight_path *path,
|
||||
enum cs_mode mode)
|
||||
{
|
||||
struct coresight_device *sink = coresight_get_sink(path);
|
||||
struct coresight_node *nd;
|
||||
int trace_id;
|
||||
|
||||
list_for_each_entry(nd, &path->path_list, link) {
|
||||
/* Assign a trace ID to the path for the first device that wants to do it */
|
||||
trace_id = coresight_get_trace_id(nd->csdev, mode, sink);
|
||||
|
||||
/*
|
||||
* 0 in this context is that it didn't want to assign so keep searching.
|
||||
* Non 0 is either success or fail.
|
||||
*/
|
||||
if (trace_id != 0) {
|
||||
path->trace_id = trace_id;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* _coresight_build_path - recursively build a path from a @csdev to a sink.
|
||||
* @csdev: The device to start from.
|
||||
|
@ -668,7 +715,7 @@ static void coresight_drop_device(struct coresight_device *csdev)
|
|||
static int _coresight_build_path(struct coresight_device *csdev,
|
||||
struct coresight_device *source,
|
||||
struct coresight_device *sink,
|
||||
struct list_head *path)
|
||||
struct coresight_path *path)
|
||||
{
|
||||
int i, ret;
|
||||
bool found = false;
|
||||
|
@ -721,25 +768,25 @@ out:
|
|||
return -ENOMEM;
|
||||
|
||||
node->csdev = csdev;
|
||||
list_add(&node->link, path);
|
||||
list_add(&node->link, &path->path_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct list_head *coresight_build_path(struct coresight_device *source,
|
||||
struct coresight_path *coresight_build_path(struct coresight_device *source,
|
||||
struct coresight_device *sink)
|
||||
{
|
||||
struct list_head *path;
|
||||
struct coresight_path *path;
|
||||
int rc;
|
||||
|
||||
if (!sink)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
path = kzalloc(sizeof(struct list_head), GFP_KERNEL);
|
||||
path = kzalloc(sizeof(struct coresight_path), GFP_KERNEL);
|
||||
if (!path)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
INIT_LIST_HEAD(path);
|
||||
INIT_LIST_HEAD(&path->path_list);
|
||||
|
||||
rc = _coresight_build_path(source, source, sink, path);
|
||||
if (rc) {
|
||||
|
@ -757,12 +804,12 @@ struct list_head *coresight_build_path(struct coresight_device *source,
|
|||
* Go through all the elements of a path and 1) removed it from the list and
|
||||
* 2) free the memory allocated for each node.
|
||||
*/
|
||||
void coresight_release_path(struct list_head *path)
|
||||
void coresight_release_path(struct coresight_path *path)
|
||||
{
|
||||
struct coresight_device *csdev;
|
||||
struct coresight_node *nd, *next;
|
||||
|
||||
list_for_each_entry_safe(nd, next, path, link) {
|
||||
list_for_each_entry_safe(nd, next, &path->path_list, link) {
|
||||
csdev = nd->csdev;
|
||||
|
||||
coresight_drop_device(csdev);
|
||||
|
@ -1092,18 +1139,20 @@ static void coresight_remove_conns(struct coresight_device *csdev)
|
|||
}
|
||||
|
||||
/**
|
||||
* coresight_timeout - loop until a bit has changed to a specific register
|
||||
* state.
|
||||
* coresight_timeout_action - loop until a bit has changed to a specific register
|
||||
* state, with a callback after every trial.
|
||||
* @csa: coresight device access for the device
|
||||
* @offset: Offset of the register from the base of the device.
|
||||
* @position: the position of the bit of interest.
|
||||
* @value: the value the bit should have.
|
||||
* @cb: Call back after each trial.
|
||||
*
|
||||
* Return: 0 as soon as the bit has taken the desired state or -EAGAIN if
|
||||
* TIMEOUT_US has elapsed, which ever happens first.
|
||||
*/
|
||||
int coresight_timeout(struct csdev_access *csa, u32 offset,
|
||||
int position, int value)
|
||||
int coresight_timeout_action(struct csdev_access *csa, u32 offset,
|
||||
int position, int value,
|
||||
coresight_timeout_cb_t cb)
|
||||
{
|
||||
int i;
|
||||
u32 val;
|
||||
|
@ -1119,7 +1168,8 @@ int coresight_timeout(struct csdev_access *csa, u32 offset,
|
|||
if (!(val & BIT(position)))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cb)
|
||||
cb(csa, offset, position, value);
|
||||
/*
|
||||
* Delay is arbitrary - the specification doesn't say how long
|
||||
* we are expected to wait. Extra check required to make sure
|
||||
|
@ -1131,6 +1181,13 @@ int coresight_timeout(struct csdev_access *csa, u32 offset,
|
|||
|
||||
return -EAGAIN;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_timeout_action);
|
||||
|
||||
int coresight_timeout(struct csdev_access *csa, u32 offset,
|
||||
int position, int value)
|
||||
{
|
||||
return coresight_timeout_action(csa, offset, position, value, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_timeout);
|
||||
|
||||
u32 coresight_relaxed_read32(struct coresight_device *csdev, u32 offset)
|
||||
|
@ -1239,7 +1296,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
|
|||
|
||||
if (csdev->type == CORESIGHT_DEV_TYPE_SINK ||
|
||||
csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) {
|
||||
spin_lock_init(&csdev->perf_sink_id_map.lock);
|
||||
raw_spin_lock_init(&csdev->perf_sink_id_map.lock);
|
||||
csdev->perf_sink_id_map.cpu_map = alloc_percpu(atomic_t);
|
||||
if (!csdev->perf_sink_id_map.cpu_map) {
|
||||
kfree(csdev);
|
||||
|
@ -1453,6 +1510,36 @@ const struct bus_type coresight_bustype = {
|
|||
.name = "coresight",
|
||||
};
|
||||
|
||||
static int coresight_panic_sync(struct device *dev, void *data)
|
||||
{
|
||||
int mode;
|
||||
struct coresight_device *csdev;
|
||||
|
||||
/* Run through panic sync handlers for all enabled devices */
|
||||
csdev = container_of(dev, struct coresight_device, dev);
|
||||
mode = coresight_get_mode(csdev);
|
||||
|
||||
if ((mode == CS_MODE_SYSFS) || (mode == CS_MODE_PERF)) {
|
||||
if (panic_ops(csdev))
|
||||
panic_ops(csdev)->sync(csdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int coresight_panic_cb(struct notifier_block *self,
|
||||
unsigned long v, void *p)
|
||||
{
|
||||
bus_for_each_dev(&coresight_bustype, NULL, NULL,
|
||||
coresight_panic_sync);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct notifier_block coresight_notifier = {
|
||||
.notifier_call = coresight_panic_cb,
|
||||
};
|
||||
|
||||
static int __init coresight_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
@ -1465,11 +1552,20 @@ static int __init coresight_init(void)
|
|||
if (ret)
|
||||
goto exit_bus_unregister;
|
||||
|
||||
/* Register function to be called for panic */
|
||||
ret = atomic_notifier_chain_register(&panic_notifier_list,
|
||||
&coresight_notifier);
|
||||
if (ret)
|
||||
goto exit_perf;
|
||||
|
||||
/* initialise the coresight syscfg API */
|
||||
ret = cscfg_init();
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||
&coresight_notifier);
|
||||
exit_perf:
|
||||
etm_perf_exit();
|
||||
exit_bus_unregister:
|
||||
bus_unregister(&coresight_bustype);
|
||||
|
@ -1479,6 +1575,8 @@ exit_bus_unregister:
|
|||
static void __exit coresight_exit(void)
|
||||
{
|
||||
cscfg_exit();
|
||||
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||
&coresight_notifier);
|
||||
etm_perf_exit();
|
||||
bus_unregister(&coresight_bustype);
|
||||
}
|
||||
|
@ -1515,6 +1613,38 @@ void coresight_remove_driver(struct amba_driver *amba_drv,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_remove_driver);
|
||||
|
||||
int coresight_etm_get_trace_id(struct coresight_device *csdev, enum cs_mode mode,
|
||||
struct coresight_device *sink)
|
||||
{
|
||||
int cpu, trace_id;
|
||||
|
||||
if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE || !source_ops(csdev)->cpu_id)
|
||||
return -EINVAL;
|
||||
|
||||
cpu = source_ops(csdev)->cpu_id(csdev);
|
||||
switch (mode) {
|
||||
case CS_MODE_SYSFS:
|
||||
trace_id = coresight_trace_id_get_cpu_id(cpu);
|
||||
break;
|
||||
case CS_MODE_PERF:
|
||||
if (WARN_ON(!sink))
|
||||
return -EINVAL;
|
||||
|
||||
trace_id = coresight_trace_id_get_cpu_id_map(cpu, &sink->perf_sink_id_map);
|
||||
break;
|
||||
default:
|
||||
trace_id = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!IS_VALID_CS_TRACE_ID(trace_id))
|
||||
dev_err(&csdev->dev,
|
||||
"Failed to allocate trace ID on CPU%d\n", cpu);
|
||||
|
||||
return trace_id;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_etm_get_trace_id);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
|
||||
MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
|
||||
|
|
326
drivers/hwtracing/coresight/coresight-ctcu-core.c
Normal file
326
drivers/hwtracing/coresight/coresight-ctcu-core.c
Normal file
|
@ -0,0 +1,326 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "coresight-ctcu.h"
|
||||
#include "coresight-priv.h"
|
||||
|
||||
DEFINE_CORESIGHT_DEVLIST(ctcu_devs, "ctcu");
|
||||
|
||||
#define ctcu_writel(drvdata, val, offset) __raw_writel((val), drvdata->base + offset)
|
||||
#define ctcu_readl(drvdata, offset) __raw_readl(drvdata->base + offset)
|
||||
|
||||
/*
|
||||
* The TMC Coresight Control Unit utilizes four ATID registers to control the data
|
||||
* filter function based on the trace ID for each TMC ETR sink. The length of each
|
||||
* ATID register is 32 bits. Therefore, an ETR device has a 128-bit long field
|
||||
* in CTCU. Each trace ID is represented by one bit in that filed.
|
||||
* e.g. ETR0ATID0 layout, set bit 5 for traceid 5
|
||||
* bit5
|
||||
* ------------------------------------------------------
|
||||
* | |28| |24| |20| |16| |12| |8| 1|4| |0|
|
||||
* ------------------------------------------------------
|
||||
*
|
||||
* e.g. ETR0:
|
||||
* 127 0 from ATID_offset for ETR0ATID0
|
||||
* -------------------------
|
||||
* |ATID3|ATID2|ATID1|ATID0|
|
||||
*/
|
||||
#define CTCU_ATID_REG_OFFSET(traceid, atid_offset) \
|
||||
((traceid / 32) * 4 + atid_offset)
|
||||
|
||||
#define CTCU_ATID_REG_BIT(traceid) (traceid % 32)
|
||||
#define CTCU_ATID_REG_SIZE 0x10
|
||||
#define CTCU_ETR0_ATID0 0xf8
|
||||
#define CTCU_ETR1_ATID0 0x108
|
||||
|
||||
static const struct ctcu_etr_config sa8775p_etr_cfgs[] = {
|
||||
{
|
||||
.atid_offset = CTCU_ETR0_ATID0,
|
||||
.port_num = 0,
|
||||
},
|
||||
{
|
||||
.atid_offset = CTCU_ETR1_ATID0,
|
||||
.port_num = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct ctcu_config sa8775p_cfgs = {
|
||||
.etr_cfgs = sa8775p_etr_cfgs,
|
||||
.num_etr_config = ARRAY_SIZE(sa8775p_etr_cfgs),
|
||||
};
|
||||
|
||||
static void ctcu_program_atid_register(struct ctcu_drvdata *drvdata, u32 reg_offset,
|
||||
u8 bit, bool enable)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
val = ctcu_readl(drvdata, reg_offset);
|
||||
if (enable)
|
||||
val |= BIT(bit);
|
||||
else
|
||||
val &= ~BIT(bit);
|
||||
|
||||
ctcu_writel(drvdata, val, reg_offset);
|
||||
CS_LOCK(drvdata->base);
|
||||
}
|
||||
|
||||
/*
|
||||
* __ctcu_set_etr_traceid: Set bit in the ATID register based on trace ID when enable is true.
|
||||
* Reset the bit of the ATID register based on trace ID when enable is false.
|
||||
*
|
||||
* @csdev: coresight_device of CTCU.
|
||||
* @traceid: trace ID of the source tracer.
|
||||
* @port_num: port number connected to TMC ETR sink.
|
||||
* @enable: True for set bit and false for reset bit.
|
||||
*
|
||||
* Returns 0 indicates success. Non-zero result means failure.
|
||||
*/
|
||||
static int __ctcu_set_etr_traceid(struct coresight_device *csdev, u8 traceid, int port_num,
|
||||
bool enable)
|
||||
{
|
||||
struct ctcu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
u32 atid_offset, reg_offset;
|
||||
u8 refcnt, bit;
|
||||
|
||||
atid_offset = drvdata->atid_offset[port_num];
|
||||
if (atid_offset == 0)
|
||||
return -EINVAL;
|
||||
|
||||
bit = CTCU_ATID_REG_BIT(traceid);
|
||||
reg_offset = CTCU_ATID_REG_OFFSET(traceid, atid_offset);
|
||||
if (reg_offset - atid_offset > CTCU_ATID_REG_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
guard(raw_spinlock_irqsave)(&drvdata->spin_lock);
|
||||
refcnt = drvdata->traceid_refcnt[port_num][traceid];
|
||||
/* Only program the atid register when the refcnt value is 1 or 0 */
|
||||
if ((enable && !refcnt++) || (!enable && !--refcnt))
|
||||
ctcu_program_atid_register(drvdata, reg_offset, bit, enable);
|
||||
|
||||
drvdata->traceid_refcnt[port_num][traceid] = refcnt;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Searching the sink device from helper's view in case there are multiple helper devices
|
||||
* connected to the sink device.
|
||||
*/
|
||||
static int ctcu_get_active_port(struct coresight_device *sink, struct coresight_device *helper)
|
||||
{
|
||||
struct coresight_platform_data *pdata = helper->pdata;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pdata->nr_inconns; ++i) {
|
||||
if (pdata->in_conns[i]->src_dev == sink)
|
||||
return pdata->in_conns[i]->dest_port;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ctcu_set_etr_traceid(struct coresight_device *csdev, struct coresight_path *path,
|
||||
bool enable)
|
||||
{
|
||||
struct coresight_device *sink = coresight_get_sink(path);
|
||||
u8 traceid = path->trace_id;
|
||||
int port_num;
|
||||
|
||||
if ((sink == NULL) || !IS_VALID_CS_TRACE_ID(traceid)) {
|
||||
dev_err(&csdev->dev, "Invalid sink device or trace ID\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
port_num = ctcu_get_active_port(sink, csdev);
|
||||
if (port_num < 0)
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(&csdev->dev, "traceid is %d\n", traceid);
|
||||
|
||||
return __ctcu_set_etr_traceid(csdev, traceid, port_num, enable);
|
||||
}
|
||||
|
||||
static int ctcu_enable(struct coresight_device *csdev, enum cs_mode mode, void *data)
|
||||
{
|
||||
struct coresight_path *path = (struct coresight_path *)data;
|
||||
|
||||
return ctcu_set_etr_traceid(csdev, path, true);
|
||||
}
|
||||
|
||||
static int ctcu_disable(struct coresight_device *csdev, void *data)
|
||||
{
|
||||
struct coresight_path *path = (struct coresight_path *)data;
|
||||
|
||||
return ctcu_set_etr_traceid(csdev, path, false);
|
||||
}
|
||||
|
||||
static const struct coresight_ops_helper ctcu_helper_ops = {
|
||||
.enable = ctcu_enable,
|
||||
.disable = ctcu_disable,
|
||||
};
|
||||
|
||||
static const struct coresight_ops ctcu_ops = {
|
||||
.helper_ops = &ctcu_helper_ops,
|
||||
};
|
||||
|
||||
static int ctcu_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct ctcu_etr_config *etr_cfg;
|
||||
struct coresight_platform_data *pdata;
|
||||
struct coresight_desc desc = { 0 };
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct ctcu_config *cfgs;
|
||||
struct ctcu_drvdata *drvdata;
|
||||
void __iomem *base;
|
||||
int i;
|
||||
|
||||
desc.name = coresight_alloc_device_name(&ctcu_devs, dev);
|
||||
if (!desc.name)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata = coresight_get_platform_data(dev);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
dev->platform_data = pdata;
|
||||
|
||||
base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
drvdata->apb_clk = coresight_get_enable_apb_pclk(dev);
|
||||
if (IS_ERR(drvdata->apb_clk))
|
||||
return -ENODEV;
|
||||
|
||||
cfgs = of_device_get_match_data(dev);
|
||||
if (cfgs) {
|
||||
if (cfgs->num_etr_config <= ETR_MAX_NUM) {
|
||||
for (i = 0; i < cfgs->num_etr_config; i++) {
|
||||
etr_cfg = &cfgs->etr_cfgs[i];
|
||||
drvdata->atid_offset[i] = etr_cfg->atid_offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drvdata->base = base;
|
||||
drvdata->dev = dev;
|
||||
platform_set_drvdata(pdev, drvdata);
|
||||
|
||||
desc.type = CORESIGHT_DEV_TYPE_HELPER;
|
||||
desc.subtype.helper_subtype = CORESIGHT_DEV_SUBTYPE_HELPER_CTCU;
|
||||
desc.pdata = pdata;
|
||||
desc.dev = dev;
|
||||
desc.ops = &ctcu_ops;
|
||||
desc.access = CSDEV_ACCESS_IOMEM(base);
|
||||
|
||||
drvdata->csdev = coresight_register(&desc);
|
||||
if (IS_ERR(drvdata->csdev)) {
|
||||
if (!IS_ERR_OR_NULL(drvdata->apb_clk))
|
||||
clk_put(drvdata->apb_clk);
|
||||
|
||||
return PTR_ERR(drvdata->csdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ctcu_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ctcu_drvdata *drvdata = platform_get_drvdata(pdev);
|
||||
|
||||
coresight_unregister(drvdata->csdev);
|
||||
}
|
||||
|
||||
static int ctcu_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = ctcu_probe(pdev);
|
||||
pm_runtime_put(&pdev->dev);
|
||||
if (ret)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ctcu_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ctcu_drvdata *drvdata = platform_get_drvdata(pdev);
|
||||
|
||||
if (WARN_ON(!drvdata))
|
||||
return;
|
||||
|
||||
ctcu_remove(pdev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!IS_ERR_OR_NULL(drvdata->apb_clk))
|
||||
clk_put(drvdata->apb_clk);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ctcu_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct ctcu_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR_OR_NULL(drvdata->apb_clk))
|
||||
clk_disable_unprepare(drvdata->apb_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ctcu_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct ctcu_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR_OR_NULL(drvdata->apb_clk))
|
||||
clk_prepare_enable(drvdata->apb_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops ctcu_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(ctcu_runtime_suspend, ctcu_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id ctcu_match[] = {
|
||||
{.compatible = "qcom,sa8775p-ctcu", .data = &sa8775p_cfgs},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver ctcu_driver = {
|
||||
.probe = ctcu_platform_probe,
|
||||
.remove = ctcu_platform_remove,
|
||||
.driver = {
|
||||
.name = "coresight-ctcu",
|
||||
.of_match_table = ctcu_match,
|
||||
.pm = &ctcu_dev_pm_ops,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ctcu_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("CoreSight TMC Control Unit driver");
|
39
drivers/hwtracing/coresight/coresight-ctcu.h
Normal file
39
drivers/hwtracing/coresight/coresight-ctcu.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _CORESIGHT_CTCU_H
|
||||
#define _CORESIGHT_CTCU_H
|
||||
#include "coresight-trace-id.h"
|
||||
|
||||
/* Maximum number of supported ETR devices for a single CTCU. */
|
||||
#define ETR_MAX_NUM 2
|
||||
|
||||
/**
|
||||
* struct ctcu_etr_config
|
||||
* @atid_offset: offset to the ATID0 Register.
|
||||
* @port_num: in-port number of CTCU device that connected to ETR.
|
||||
*/
|
||||
struct ctcu_etr_config {
|
||||
const u32 atid_offset;
|
||||
const u32 port_num;
|
||||
};
|
||||
|
||||
struct ctcu_config {
|
||||
const struct ctcu_etr_config *etr_cfgs;
|
||||
int num_etr_config;
|
||||
};
|
||||
|
||||
struct ctcu_drvdata {
|
||||
void __iomem *base;
|
||||
struct clk *apb_clk;
|
||||
struct device *dev;
|
||||
struct coresight_device *csdev;
|
||||
raw_spinlock_t spin_lock;
|
||||
u32 atid_offset[ETR_MAX_NUM];
|
||||
/* refcnt for each traceid of each sink */
|
||||
u8 traceid_refcnt[ETR_MAX_NUM][CORESIGHT_TRACE_ID_RES_TOP];
|
||||
};
|
||||
|
||||
#endif
|
|
@ -93,7 +93,7 @@ static int cti_enable_hw(struct cti_drvdata *drvdata)
|
|||
unsigned long flags;
|
||||
int rc = 0;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
/* no need to do anything if enabled or unpowered*/
|
||||
if (config->hw_enabled || !config->hw_powered)
|
||||
|
@ -108,7 +108,7 @@ static int cti_enable_hw(struct cti_drvdata *drvdata)
|
|||
|
||||
config->hw_enabled = true;
|
||||
drvdata->config.enable_req_count++;
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return rc;
|
||||
|
||||
cti_state_unchanged:
|
||||
|
@ -116,7 +116,7 @@ cti_state_unchanged:
|
|||
|
||||
/* cannot enable due to error */
|
||||
cti_err_not_enabled:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@ static void cti_cpuhp_enable_hw(struct cti_drvdata *drvdata)
|
|||
{
|
||||
struct cti_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
config->hw_powered = true;
|
||||
|
||||
/* no need to do anything if no enable request */
|
||||
|
@ -138,12 +138,12 @@ static void cti_cpuhp_enable_hw(struct cti_drvdata *drvdata)
|
|||
|
||||
cti_write_all_hw_regs(drvdata);
|
||||
config->hw_enabled = true;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return;
|
||||
|
||||
/* did not re-enable due to no claim / no request */
|
||||
cti_hp_not_enabled:
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
}
|
||||
|
||||
/* disable hardware */
|
||||
|
@ -153,7 +153,7 @@ static int cti_disable_hw(struct cti_drvdata *drvdata)
|
|||
struct coresight_device *csdev = drvdata->csdev;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
|
||||
/* don't allow negative refcounts, return an error */
|
||||
if (!drvdata->config.enable_req_count) {
|
||||
|
@ -177,12 +177,12 @@ static int cti_disable_hw(struct cti_drvdata *drvdata)
|
|||
|
||||
coresight_disclaim_device_unlocked(csdev);
|
||||
CS_LOCK(drvdata->base);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return ret;
|
||||
|
||||
/* not disabled this call */
|
||||
cti_not_disabled:
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -198,11 +198,11 @@ void cti_write_intack(struct device *dev, u32 ackval)
|
|||
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct cti_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
/* write if enabled */
|
||||
if (cti_active(config))
|
||||
cti_write_single_reg(drvdata, CTIINTACK, ackval);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -369,7 +369,7 @@ int cti_channel_trig_op(struct device *dev, enum cti_chan_op op,
|
|||
reg_offset = (direction == CTI_TRIG_IN ? CTIINEN(trigger_idx) :
|
||||
CTIOUTEN(trigger_idx));
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
|
||||
/* read - modify write - the trigger / channel enable value */
|
||||
reg_value = direction == CTI_TRIG_IN ? config->ctiinen[trigger_idx] :
|
||||
|
@ -388,7 +388,7 @@ int cti_channel_trig_op(struct device *dev, enum cti_chan_op op,
|
|||
/* write through if enabled */
|
||||
if (cti_active(config))
|
||||
cti_write_single_reg(drvdata, reg_offset, reg_value);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -406,7 +406,7 @@ int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op,
|
|||
|
||||
chan_bitmask = BIT(channel_idx);
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
reg_value = config->ctigate;
|
||||
switch (op) {
|
||||
case CTI_GATE_CHAN_ENABLE:
|
||||
|
@ -426,7 +426,7 @@ int cti_channel_gate_op(struct device *dev, enum cti_chan_gate_op op,
|
|||
if (cti_active(config))
|
||||
cti_write_single_reg(drvdata, CTIGATE, reg_value);
|
||||
}
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -445,7 +445,7 @@ int cti_channel_setop(struct device *dev, enum cti_chan_set_op op,
|
|||
|
||||
chan_bitmask = BIT(channel_idx);
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
reg_value = config->ctiappset;
|
||||
switch (op) {
|
||||
case CTI_CHAN_SET:
|
||||
|
@ -473,7 +473,7 @@ int cti_channel_setop(struct device *dev, enum cti_chan_set_op op,
|
|||
|
||||
if ((err == 0) && cti_active(config))
|
||||
cti_write_single_reg(drvdata, reg_offset, reg_value);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -676,7 +676,7 @@ static int cti_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
|
|||
if (WARN_ON_ONCE(drvdata->ctidev.cpu != cpu))
|
||||
return NOTIFY_BAD;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
|
||||
switch (cmd) {
|
||||
case CPU_PM_ENTER:
|
||||
|
@ -716,7 +716,7 @@ static int cti_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
|
|||
}
|
||||
|
||||
cti_notify_exit:
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return notify_res;
|
||||
}
|
||||
|
||||
|
@ -743,11 +743,11 @@ static int cti_dying_cpu(unsigned int cpu)
|
|||
if (!drvdata)
|
||||
return 0;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
drvdata->config.hw_powered = false;
|
||||
if (drvdata->config.hw_enabled)
|
||||
coresight_disclaim_device(drvdata->csdev);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -888,7 +888,7 @@ static int cti_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
drvdata->ctidev.ctm_id = 0;
|
||||
INIT_LIST_HEAD(&drvdata->ctidev.trig_cons);
|
||||
|
||||
spin_lock_init(&drvdata->spinlock);
|
||||
raw_spin_lock_init(&drvdata->spinlock);
|
||||
|
||||
/* initialise CTI driver config values */
|
||||
cti_set_default_config(dev, drvdata);
|
||||
|
|
|
@ -84,11 +84,11 @@ static ssize_t enable_show(struct device *dev,
|
|||
bool enabled, powered;
|
||||
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
enable_req = drvdata->config.enable_req_count;
|
||||
powered = drvdata->config.hw_powered;
|
||||
enabled = drvdata->config.hw_enabled;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
|
||||
if (powered)
|
||||
return sprintf(buf, "%d\n", enabled);
|
||||
|
@ -134,9 +134,9 @@ static ssize_t powered_show(struct device *dev,
|
|||
bool powered;
|
||||
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
powered = drvdata->config.hw_powered;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
|
||||
return sprintf(buf, "%d\n", powered);
|
||||
}
|
||||
|
@ -181,10 +181,10 @@ static ssize_t coresight_cti_reg_show(struct device *dev,
|
|||
u32 val = 0;
|
||||
|
||||
pm_runtime_get_sync(dev->parent);
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
if (drvdata->config.hw_powered)
|
||||
val = readl_relaxed(drvdata->base + cti_attr->off);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
pm_runtime_put_sync(dev->parent);
|
||||
return sysfs_emit(buf, "0x%x\n", val);
|
||||
}
|
||||
|
@ -202,10 +202,10 @@ static __maybe_unused ssize_t coresight_cti_reg_store(struct device *dev,
|
|||
return -EINVAL;
|
||||
|
||||
pm_runtime_get_sync(dev->parent);
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
if (drvdata->config.hw_powered)
|
||||
cti_write_single_reg(drvdata, cti_attr->off, val);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
pm_runtime_put_sync(dev->parent);
|
||||
return size;
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ static ssize_t cti_reg32_show(struct device *dev, char *buf,
|
|||
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct cti_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
if ((reg_offset >= 0) && cti_active(config)) {
|
||||
CS_UNLOCK(drvdata->base);
|
||||
val = readl_relaxed(drvdata->base + reg_offset);
|
||||
|
@ -274,7 +274,7 @@ static ssize_t cti_reg32_show(struct device *dev, char *buf,
|
|||
} else if (pcached_val) {
|
||||
val = *pcached_val;
|
||||
}
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return sprintf(buf, "%#x\n", val);
|
||||
}
|
||||
|
||||
|
@ -293,7 +293,7 @@ static ssize_t cti_reg32_store(struct device *dev, const char *buf,
|
|||
if (kstrtoul(buf, 0, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
/* local store */
|
||||
if (pcached_val)
|
||||
*pcached_val = (u32)val;
|
||||
|
@ -301,7 +301,7 @@ static ssize_t cti_reg32_store(struct device *dev, const char *buf,
|
|||
/* write through if offset and enabled */
|
||||
if ((reg_offset >= 0) && cti_active(config))
|
||||
cti_write_single_reg(drvdata, reg_offset, val);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
@ -349,9 +349,9 @@ static ssize_t inout_sel_store(struct device *dev,
|
|||
if (val > (CTIINOUTEN_MAX - 1))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
drvdata->config.ctiinout_sel = val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(inout_sel);
|
||||
|
@ -364,10 +364,10 @@ static ssize_t inen_show(struct device *dev,
|
|||
int index;
|
||||
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
index = drvdata->config.ctiinout_sel;
|
||||
val = drvdata->config.ctiinen[index];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return sprintf(buf, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -383,14 +383,14 @@ static ssize_t inen_store(struct device *dev,
|
|||
if (kstrtoul(buf, 0, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
index = config->ctiinout_sel;
|
||||
config->ctiinen[index] = val;
|
||||
|
||||
/* write through if enabled */
|
||||
if (cti_active(config))
|
||||
cti_write_single_reg(drvdata, CTIINEN(index), val);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(inen);
|
||||
|
@ -403,10 +403,10 @@ static ssize_t outen_show(struct device *dev,
|
|||
int index;
|
||||
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
index = drvdata->config.ctiinout_sel;
|
||||
val = drvdata->config.ctiouten[index];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return sprintf(buf, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -422,14 +422,14 @@ static ssize_t outen_store(struct device *dev,
|
|||
if (kstrtoul(buf, 0, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
index = config->ctiinout_sel;
|
||||
config->ctiouten[index] = val;
|
||||
|
||||
/* write through if enabled */
|
||||
if (cti_active(config))
|
||||
cti_write_single_reg(drvdata, CTIOUTEN(index), val);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(outen);
|
||||
|
@ -463,7 +463,7 @@ static ssize_t appclear_store(struct device *dev,
|
|||
if (kstrtoul(buf, 0, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
|
||||
/* a 1'b1 in appclr clears down the same bit in appset*/
|
||||
config->ctiappset &= ~val;
|
||||
|
@ -471,7 +471,7 @@ static ssize_t appclear_store(struct device *dev,
|
|||
/* write through if enabled */
|
||||
if (cti_active(config))
|
||||
cti_write_single_reg(drvdata, CTIAPPCLEAR, val);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_WO(appclear);
|
||||
|
@ -487,12 +487,12 @@ static ssize_t apppulse_store(struct device *dev,
|
|||
if (kstrtoul(buf, 0, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
|
||||
/* write through if enabled */
|
||||
if (cti_active(config))
|
||||
cti_write_single_reg(drvdata, CTIAPPPULSE, val);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_WO(apppulse);
|
||||
|
@ -681,9 +681,9 @@ static ssize_t trig_filter_enable_show(struct device *dev,
|
|||
u32 val;
|
||||
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
val = drvdata->config.trig_filter_enable;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return sprintf(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
|
@ -697,9 +697,9 @@ static ssize_t trig_filter_enable_store(struct device *dev,
|
|||
if (kstrtoul(buf, 0, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
drvdata->config.trig_filter_enable = !!val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(trig_filter_enable);
|
||||
|
@ -728,7 +728,7 @@ static ssize_t chan_xtrigs_reset_store(struct device *dev,
|
|||
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct cti_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
|
||||
/* clear the CTI trigger / channel programming registers */
|
||||
for (i = 0; i < config->nr_trig_max; i++) {
|
||||
|
@ -747,7 +747,7 @@ static ssize_t chan_xtrigs_reset_store(struct device *dev,
|
|||
if (cti_active(config))
|
||||
cti_write_all_hw_regs(drvdata);
|
||||
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_WO(chan_xtrigs_reset);
|
||||
|
@ -768,9 +768,9 @@ static ssize_t chan_xtrigs_sel_store(struct device *dev,
|
|||
if (val > (drvdata->config.nr_ctm_channels - 1))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
drvdata->config.xtrig_rchan_sel = val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
@ -781,9 +781,9 @@ static ssize_t chan_xtrigs_sel_show(struct device *dev,
|
|||
unsigned long val;
|
||||
struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
val = drvdata->config.xtrig_rchan_sel;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
|
||||
return sprintf(buf, "%ld\n", val);
|
||||
}
|
||||
|
@ -838,12 +838,12 @@ static ssize_t print_chan_list(struct device *dev,
|
|||
unsigned long inuse_bits = 0, chan_mask;
|
||||
|
||||
/* scan regs to get bitmap of channels in use. */
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
for (i = 0; i < config->nr_trig_max; i++) {
|
||||
inuse_bits |= config->ctiinen[i];
|
||||
inuse_bits |= config->ctiouten[i];
|
||||
}
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
|
||||
/* inverse bits if printing free channels */
|
||||
if (!inuse)
|
||||
|
|
|
@ -175,7 +175,7 @@ struct cti_drvdata {
|
|||
void __iomem *base;
|
||||
struct coresight_device *csdev;
|
||||
struct cti_device ctidev;
|
||||
spinlock_t spinlock;
|
||||
raw_spinlock_t spinlock;
|
||||
struct cti_config config;
|
||||
struct list_head node;
|
||||
void (*csdev_release)(struct device *dev);
|
||||
|
|
|
@ -24,7 +24,7 @@ DEFINE_CORESIGHT_DEVLIST(sink_devs, "dummy_sink");
|
|||
|
||||
static int dummy_source_enable(struct coresight_device *csdev,
|
||||
struct perf_event *event, enum cs_mode mode,
|
||||
__maybe_unused struct coresight_trace_id_map *id_map)
|
||||
__maybe_unused struct coresight_path *path)
|
||||
{
|
||||
if (!coresight_take_mode(csdev, mode))
|
||||
return -EBUSY;
|
||||
|
@ -41,6 +41,16 @@ static void dummy_source_disable(struct coresight_device *csdev,
|
|||
dev_dbg(csdev->dev.parent, "Dummy source disabled\n");
|
||||
}
|
||||
|
||||
static int dummy_source_trace_id(struct coresight_device *csdev, __maybe_unused enum cs_mode mode,
|
||||
__maybe_unused struct coresight_device *sink)
|
||||
{
|
||||
struct dummy_drvdata *drvdata;
|
||||
|
||||
drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
return drvdata->traceid;
|
||||
}
|
||||
|
||||
static int dummy_sink_enable(struct coresight_device *csdev, enum cs_mode mode,
|
||||
void *data)
|
||||
{
|
||||
|
@ -62,7 +72,8 @@ static const struct coresight_ops_source dummy_source_ops = {
|
|||
};
|
||||
|
||||
static const struct coresight_ops dummy_source_cs_ops = {
|
||||
.source_ops = &dummy_source_ops,
|
||||
.trace_id = dummy_source_trace_id,
|
||||
.source_ops = &dummy_source_ops,
|
||||
};
|
||||
|
||||
static const struct coresight_ops_sink dummy_sink_ops = {
|
||||
|
|
|
@ -84,7 +84,7 @@ struct etb_drvdata {
|
|||
struct clk *atclk;
|
||||
struct coresight_device *csdev;
|
||||
struct miscdevice miscdev;
|
||||
spinlock_t spinlock;
|
||||
raw_spinlock_t spinlock;
|
||||
local_t reading;
|
||||
pid_t pid;
|
||||
u8 *buf;
|
||||
|
@ -145,7 +145,7 @@ static int etb_enable_sysfs(struct coresight_device *csdev)
|
|||
unsigned long flags;
|
||||
struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
/* Don't messup with perf sessions. */
|
||||
if (coresight_get_mode(csdev) == CS_MODE_PERF) {
|
||||
|
@ -163,7 +163,7 @@ static int etb_enable_sysfs(struct coresight_device *csdev)
|
|||
|
||||
csdev->refcnt++;
|
||||
out:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -176,7 +176,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data)
|
|||
struct perf_output_handle *handle = data;
|
||||
struct cs_buffers *buf = etm_perf_sink_config(handle);
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
/* No need to continue if the component is already in used by sysFS. */
|
||||
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
|
||||
|
@ -219,7 +219,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data)
|
|||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -352,11 +352,11 @@ static int etb_disable(struct coresight_device *csdev)
|
|||
struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
csdev->refcnt--;
|
||||
if (csdev->refcnt) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
@ -366,7 +366,7 @@ static int etb_disable(struct coresight_device *csdev)
|
|||
/* Dissociate from monitored process. */
|
||||
drvdata->pid = -1;
|
||||
coresight_set_mode(csdev, CS_MODE_DISABLED);
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
dev_dbg(&csdev->dev, "ETB disabled\n");
|
||||
return 0;
|
||||
|
@ -443,7 +443,7 @@ static unsigned long etb_update_buffer(struct coresight_device *csdev,
|
|||
|
||||
capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
/* Don't do anything if another tracer is using this sink */
|
||||
if (csdev->refcnt != 1)
|
||||
|
@ -566,7 +566,7 @@ static unsigned long etb_update_buffer(struct coresight_device *csdev,
|
|||
__etb_enable_hw(drvdata);
|
||||
CS_LOCK(drvdata->base);
|
||||
out:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
return to_read;
|
||||
}
|
||||
|
@ -587,13 +587,13 @@ static void etb_dump(struct etb_drvdata *drvdata)
|
|||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
|
||||
__etb_disable_hw(drvdata);
|
||||
etb_dump_hw(drvdata);
|
||||
__etb_enable_hw(drvdata);
|
||||
}
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
dev_dbg(&drvdata->csdev->dev, "ETB dumped\n");
|
||||
}
|
||||
|
@ -746,7 +746,7 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
drvdata->base = base;
|
||||
desc.access = CSDEV_ACCESS_IOMEM(base);
|
||||
|
||||
spin_lock_init(&drvdata->spinlock);
|
||||
raw_spin_lock_init(&drvdata->spinlock);
|
||||
|
||||
drvdata->buffer_depth = etb_get_buffer_depth(drvdata);
|
||||
|
||||
|
|
|
@ -136,13 +136,13 @@ static const struct attribute_group *etm_pmu_attr_groups[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static inline struct list_head **
|
||||
static inline struct coresight_path **
|
||||
etm_event_cpu_path_ptr(struct etm_event_data *data, int cpu)
|
||||
{
|
||||
return per_cpu_ptr(data->path, cpu);
|
||||
}
|
||||
|
||||
static inline struct list_head *
|
||||
static inline struct coresight_path *
|
||||
etm_event_cpu_path(struct etm_event_data *data, int cpu)
|
||||
{
|
||||
return *etm_event_cpu_path_ptr(data, cpu);
|
||||
|
@ -226,7 +226,7 @@ static void free_event_data(struct work_struct *work)
|
|||
cscfg_deactivate_config(event_data->cfg_hash);
|
||||
|
||||
for_each_cpu(cpu, mask) {
|
||||
struct list_head **ppath;
|
||||
struct coresight_path **ppath;
|
||||
|
||||
ppath = etm_event_cpu_path_ptr(event_data, cpu);
|
||||
if (!(IS_ERR_OR_NULL(*ppath))) {
|
||||
|
@ -276,7 +276,7 @@ static void *alloc_event_data(int cpu)
|
|||
* unused memory when dealing with single CPU trace scenarios is small
|
||||
* compared to the cost of searching through an optimized array.
|
||||
*/
|
||||
event_data->path = alloc_percpu(struct list_head *);
|
||||
event_data->path = alloc_percpu(struct coresight_path *);
|
||||
|
||||
if (!event_data->path) {
|
||||
kfree(event_data);
|
||||
|
@ -317,7 +317,6 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
|
|||
{
|
||||
u32 id, cfg_hash;
|
||||
int cpu = event->cpu;
|
||||
int trace_id;
|
||||
cpumask_t *mask;
|
||||
struct coresight_device *sink = NULL;
|
||||
struct coresight_device *user_sink = NULL, *last_sink = NULL;
|
||||
|
@ -352,7 +351,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
|
|||
* CPUs, we can handle it and fail the session.
|
||||
*/
|
||||
for_each_cpu(cpu, mask) {
|
||||
struct list_head *path;
|
||||
struct coresight_path *path;
|
||||
struct coresight_device *csdev;
|
||||
|
||||
csdev = per_cpu(csdev_src, cpu);
|
||||
|
@ -407,8 +406,8 @@ static void *etm_setup_aux(struct perf_event *event, void **pages,
|
|||
}
|
||||
|
||||
/* ensure we can allocate a trace ID for this CPU */
|
||||
trace_id = coresight_trace_id_get_cpu_id_map(cpu, &sink->perf_sink_id_map);
|
||||
if (!IS_VALID_CS_TRACE_ID(trace_id)) {
|
||||
coresight_path_assign_trace_id(path, CS_MODE_PERF);
|
||||
if (!IS_VALID_CS_TRACE_ID(path->trace_id)) {
|
||||
cpumask_clear_cpu(cpu, mask);
|
||||
coresight_release_path(path);
|
||||
continue;
|
||||
|
@ -458,9 +457,8 @@ static void etm_event_start(struct perf_event *event, int flags)
|
|||
struct etm_ctxt *ctxt = this_cpu_ptr(&etm_ctxt);
|
||||
struct perf_output_handle *handle = &ctxt->handle;
|
||||
struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu);
|
||||
struct list_head *path;
|
||||
struct coresight_path *path;
|
||||
u64 hw_id;
|
||||
u8 trace_id;
|
||||
|
||||
if (!csdev)
|
||||
goto fail;
|
||||
|
@ -503,8 +501,7 @@ static void etm_event_start(struct perf_event *event, int flags)
|
|||
goto fail_end_stop;
|
||||
|
||||
/* Finally enable the tracer */
|
||||
if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF,
|
||||
&sink->perf_sink_id_map))
|
||||
if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF, path))
|
||||
goto fail_disable_path;
|
||||
|
||||
/*
|
||||
|
@ -514,13 +511,11 @@ static void etm_event_start(struct perf_event *event, int flags)
|
|||
if (!cpumask_test_cpu(cpu, &event_data->aux_hwid_done)) {
|
||||
cpumask_set_cpu(cpu, &event_data->aux_hwid_done);
|
||||
|
||||
trace_id = coresight_trace_id_read_cpu_id_map(cpu, &sink->perf_sink_id_map);
|
||||
|
||||
hw_id = FIELD_PREP(CS_AUX_HW_ID_MAJOR_VERSION_MASK,
|
||||
CS_AUX_HW_ID_MAJOR_VERSION);
|
||||
hw_id |= FIELD_PREP(CS_AUX_HW_ID_MINOR_VERSION_MASK,
|
||||
CS_AUX_HW_ID_MINOR_VERSION);
|
||||
hw_id |= FIELD_PREP(CS_AUX_HW_ID_TRACE_ID_MASK, trace_id);
|
||||
hw_id |= FIELD_PREP(CS_AUX_HW_ID_TRACE_ID_MASK, path->trace_id);
|
||||
hw_id |= FIELD_PREP(CS_AUX_HW_ID_SINK_ID_MASK, coresight_get_sink_id(sink));
|
||||
|
||||
perf_report_aux_output_id(event, hw_id);
|
||||
|
@ -558,7 +553,7 @@ static void etm_event_stop(struct perf_event *event, int mode)
|
|||
struct etm_ctxt *ctxt = this_cpu_ptr(&etm_ctxt);
|
||||
struct perf_output_handle *handle = &ctxt->handle;
|
||||
struct etm_event_data *event_data;
|
||||
struct list_head *path;
|
||||
struct coresight_path *path;
|
||||
|
||||
/*
|
||||
* If we still have access to the event_data via handle,
|
||||
|
|
|
@ -59,7 +59,7 @@ struct etm_event_data {
|
|||
cpumask_t aux_hwid_done;
|
||||
void *snk_config;
|
||||
u32 cfg_hash;
|
||||
struct list_head * __percpu *path;
|
||||
struct coresight_path * __percpu *path;
|
||||
};
|
||||
|
||||
int etm_perf_symlink(struct coresight_device *csdev, bool link);
|
||||
|
|
|
@ -284,6 +284,5 @@ extern const struct attribute_group *coresight_etm_groups[];
|
|||
void etm_set_default(struct etm_config *config);
|
||||
void etm_config_trace_mode(struct etm_config *config);
|
||||
struct etm_config *get_etm_config(struct etm_drvdata *drvdata);
|
||||
int etm_read_alloc_trace_id(struct etm_drvdata *drvdata);
|
||||
void etm_release_trace_id(struct etm_drvdata *drvdata);
|
||||
#endif
|
||||
|
|
|
@ -455,26 +455,6 @@ static int etm_cpu_id(struct coresight_device *csdev)
|
|||
return drvdata->cpu;
|
||||
}
|
||||
|
||||
int etm_read_alloc_trace_id(struct etm_drvdata *drvdata)
|
||||
{
|
||||
int trace_id;
|
||||
|
||||
/*
|
||||
* This will allocate a trace ID to the cpu,
|
||||
* or return the one currently allocated.
|
||||
*
|
||||
* trace id function has its own lock
|
||||
*/
|
||||
trace_id = coresight_trace_id_get_cpu_id(drvdata->cpu);
|
||||
if (IS_VALID_CS_TRACE_ID(trace_id))
|
||||
drvdata->traceid = (u8)trace_id;
|
||||
else
|
||||
dev_err(&drvdata->csdev->dev,
|
||||
"Failed to allocate trace ID for %s on CPU%d\n",
|
||||
dev_name(&drvdata->csdev->dev), drvdata->cpu);
|
||||
return trace_id;
|
||||
}
|
||||
|
||||
void etm_release_trace_id(struct etm_drvdata *drvdata)
|
||||
{
|
||||
coresight_trace_id_put_cpu_id(drvdata->cpu);
|
||||
|
@ -482,38 +462,22 @@ void etm_release_trace_id(struct etm_drvdata *drvdata)
|
|||
|
||||
static int etm_enable_perf(struct coresight_device *csdev,
|
||||
struct perf_event *event,
|
||||
struct coresight_trace_id_map *id_map)
|
||||
struct coresight_path *path)
|
||||
{
|
||||
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
int trace_id;
|
||||
|
||||
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id()))
|
||||
return -EINVAL;
|
||||
|
||||
/* Configure the tracer based on the session's specifics */
|
||||
etm_parse_event_config(drvdata, event);
|
||||
|
||||
/*
|
||||
* perf allocates cpu ids as part of _setup_aux() - device needs to use
|
||||
* the allocated ID. This reads the current version without allocation.
|
||||
*
|
||||
* This does not use the trace id lock to prevent lock_dep issues
|
||||
* with perf locks - we know the ID cannot change until perf shuts down
|
||||
* the session
|
||||
*/
|
||||
trace_id = coresight_trace_id_read_cpu_id_map(drvdata->cpu, id_map);
|
||||
if (!IS_VALID_CS_TRACE_ID(trace_id)) {
|
||||
dev_err(&drvdata->csdev->dev, "Failed to set trace ID for %s on CPU%d\n",
|
||||
dev_name(&drvdata->csdev->dev), drvdata->cpu);
|
||||
return -EINVAL;
|
||||
}
|
||||
drvdata->traceid = (u8)trace_id;
|
||||
drvdata->traceid = path->trace_id;
|
||||
|
||||
/* And enable it */
|
||||
return etm_enable_hw(drvdata);
|
||||
}
|
||||
|
||||
static int etm_enable_sysfs(struct coresight_device *csdev)
|
||||
static int etm_enable_sysfs(struct coresight_device *csdev, struct coresight_path *path)
|
||||
{
|
||||
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
struct etm_enable_arg arg = { };
|
||||
|
@ -521,10 +485,7 @@ static int etm_enable_sysfs(struct coresight_device *csdev)
|
|||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
|
||||
/* sysfs needs to allocate and set a trace ID */
|
||||
ret = etm_read_alloc_trace_id(drvdata);
|
||||
if (ret < 0)
|
||||
goto unlock_enable_sysfs;
|
||||
drvdata->traceid = path->trace_id;
|
||||
|
||||
/*
|
||||
* Configure the ETM only if the CPU is online. If it isn't online
|
||||
|
@ -545,7 +506,6 @@ static int etm_enable_sysfs(struct coresight_device *csdev)
|
|||
if (ret)
|
||||
etm_release_trace_id(drvdata);
|
||||
|
||||
unlock_enable_sysfs:
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
|
||||
if (!ret)
|
||||
|
@ -554,7 +514,7 @@ unlock_enable_sysfs:
|
|||
}
|
||||
|
||||
static int etm_enable(struct coresight_device *csdev, struct perf_event *event,
|
||||
enum cs_mode mode, struct coresight_trace_id_map *id_map)
|
||||
enum cs_mode mode, struct coresight_path *path)
|
||||
{
|
||||
int ret;
|
||||
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
@ -566,10 +526,10 @@ static int etm_enable(struct coresight_device *csdev, struct perf_event *event,
|
|||
|
||||
switch (mode) {
|
||||
case CS_MODE_SYSFS:
|
||||
ret = etm_enable_sysfs(csdev);
|
||||
ret = etm_enable_sysfs(csdev, path);
|
||||
break;
|
||||
case CS_MODE_PERF:
|
||||
ret = etm_enable_perf(csdev, event, id_map);
|
||||
ret = etm_enable_perf(csdev, event, path);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
@ -704,6 +664,7 @@ static const struct coresight_ops_source etm_source_ops = {
|
|||
};
|
||||
|
||||
static const struct coresight_ops etm_cs_ops = {
|
||||
.trace_id = coresight_etm_get_trace_id,
|
||||
.source_ops = &etm_source_ops,
|
||||
};
|
||||
|
||||
|
|
|
@ -1190,10 +1190,9 @@ static DEVICE_ATTR_RO(cpu);
|
|||
static ssize_t traceid_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int trace_id;
|
||||
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
int trace_id = coresight_etm_get_trace_id(drvdata->csdev, CS_MODE_SYSFS, NULL);
|
||||
|
||||
trace_id = etm_read_alloc_trace_id(drvdata);
|
||||
if (trace_id < 0)
|
||||
return trace_id;
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <linux/cpu_pm.h>
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/coresight-pmu.h>
|
||||
#include <linux/pm_wakeup.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
@ -233,25 +232,6 @@ static int etm4_cpu_id(struct coresight_device *csdev)
|
|||
return drvdata->cpu;
|
||||
}
|
||||
|
||||
int etm4_read_alloc_trace_id(struct etmv4_drvdata *drvdata)
|
||||
{
|
||||
int trace_id;
|
||||
|
||||
/*
|
||||
* This will allocate a trace ID to the cpu,
|
||||
* or return the one currently allocated.
|
||||
* The trace id function has its own lock
|
||||
*/
|
||||
trace_id = coresight_trace_id_get_cpu_id(drvdata->cpu);
|
||||
if (IS_VALID_CS_TRACE_ID(trace_id))
|
||||
drvdata->trcid = (u8)trace_id;
|
||||
else
|
||||
dev_err(&drvdata->csdev->dev,
|
||||
"Failed to allocate trace ID for %s on CPU%d\n",
|
||||
dev_name(&drvdata->csdev->dev), drvdata->cpu);
|
||||
return trace_id;
|
||||
}
|
||||
|
||||
void etm4_release_trace_id(struct etmv4_drvdata *drvdata)
|
||||
{
|
||||
coresight_trace_id_put_cpu_id(drvdata->cpu);
|
||||
|
@ -428,6 +408,29 @@ static void etm4_check_arch_features(struct etmv4_drvdata *drvdata,
|
|||
}
|
||||
#endif /* CONFIG_ETM4X_IMPDEF_FEATURE */
|
||||
|
||||
static void etm4x_sys_ins_barrier(struct csdev_access *csa, u32 offset, int pos, int val)
|
||||
{
|
||||
if (!csa->io_mem)
|
||||
isb();
|
||||
}
|
||||
|
||||
/*
|
||||
* etm4x_wait_status: Poll for TRCSTATR.<pos> == <val>. While using system
|
||||
* instruction to access the trace unit, each access must be separated by a
|
||||
* synchronization barrier. See ARM IHI0064H.b section "4.3.7 Synchronization of
|
||||
* register updates", for system instructions section, in "Notes":
|
||||
*
|
||||
* "In particular, whenever disabling or enabling the trace unit, a poll of
|
||||
* TRCSTATR needs explicit synchronization between each read of TRCSTATR"
|
||||
*/
|
||||
static int etm4x_wait_status(struct csdev_access *csa, int pos, int val)
|
||||
{
|
||||
if (!csa->io_mem)
|
||||
return coresight_timeout_action(csa, TRCSTATR, pos, val,
|
||||
etm4x_sys_ins_barrier);
|
||||
return coresight_timeout(csa, TRCSTATR, pos, val);
|
||||
}
|
||||
|
||||
static int etm4_enable_hw(struct etmv4_drvdata *drvdata)
|
||||
{
|
||||
int i, rc;
|
||||
|
@ -459,7 +462,7 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata)
|
|||
isb();
|
||||
|
||||
/* wait for TRCSTATR.IDLE to go up */
|
||||
if (coresight_timeout(csa, TRCSTATR, TRCSTATR_IDLE_BIT, 1))
|
||||
if (etm4x_wait_status(csa, TRCSTATR_IDLE_BIT, 1))
|
||||
dev_err(etm_dev,
|
||||
"timeout while waiting for Idle Trace Status\n");
|
||||
if (drvdata->nr_pe)
|
||||
|
@ -552,7 +555,7 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata)
|
|||
isb();
|
||||
|
||||
/* wait for TRCSTATR.IDLE to go back down to '0' */
|
||||
if (coresight_timeout(csa, TRCSTATR, TRCSTATR_IDLE_BIT, 0))
|
||||
if (etm4x_wait_status(csa, TRCSTATR_IDLE_BIT, 0))
|
||||
dev_err(etm_dev,
|
||||
"timeout while waiting for Idle Trace Status\n");
|
||||
|
||||
|
@ -788,9 +791,9 @@ out:
|
|||
|
||||
static int etm4_enable_perf(struct coresight_device *csdev,
|
||||
struct perf_event *event,
|
||||
struct coresight_trace_id_map *id_map)
|
||||
struct coresight_path *path)
|
||||
{
|
||||
int ret = 0, trace_id;
|
||||
int ret = 0;
|
||||
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) {
|
||||
|
@ -803,22 +806,7 @@ static int etm4_enable_perf(struct coresight_device *csdev,
|
|||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* perf allocates cpu ids as part of _setup_aux() - device needs to use
|
||||
* the allocated ID. This reads the current version without allocation.
|
||||
*
|
||||
* This does not use the trace id lock to prevent lock_dep issues
|
||||
* with perf locks - we know the ID cannot change until perf shuts down
|
||||
* the session
|
||||
*/
|
||||
trace_id = coresight_trace_id_read_cpu_id_map(drvdata->cpu, id_map);
|
||||
if (!IS_VALID_CS_TRACE_ID(trace_id)) {
|
||||
dev_err(&drvdata->csdev->dev, "Failed to set trace ID for %s on CPU%d\n",
|
||||
dev_name(&drvdata->csdev->dev), drvdata->cpu);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
drvdata->trcid = (u8)trace_id;
|
||||
drvdata->trcid = path->trace_id;
|
||||
|
||||
/* And enable it */
|
||||
ret = etm4_enable_hw(drvdata);
|
||||
|
@ -827,7 +815,7 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int etm4_enable_sysfs(struct coresight_device *csdev)
|
||||
static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_path *path)
|
||||
{
|
||||
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
struct etm4_enable_arg arg = { };
|
||||
|
@ -842,12 +830,9 @@ static int etm4_enable_sysfs(struct coresight_device *csdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
|
||||
/* sysfs needs to read and allocate a trace ID */
|
||||
ret = etm4_read_alloc_trace_id(drvdata);
|
||||
if (ret < 0)
|
||||
goto unlock_sysfs_enable;
|
||||
drvdata->trcid = path->trace_id;
|
||||
|
||||
/*
|
||||
* Executing etm4_enable_hw on the cpu whose ETM is being enabled
|
||||
|
@ -864,8 +849,7 @@ static int etm4_enable_sysfs(struct coresight_device *csdev)
|
|||
if (ret)
|
||||
etm4_release_trace_id(drvdata);
|
||||
|
||||
unlock_sysfs_enable:
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
|
||||
if (!ret)
|
||||
dev_dbg(&csdev->dev, "ETM tracing enabled\n");
|
||||
|
@ -873,7 +857,7 @@ unlock_sysfs_enable:
|
|||
}
|
||||
|
||||
static int etm4_enable(struct coresight_device *csdev, struct perf_event *event,
|
||||
enum cs_mode mode, struct coresight_trace_id_map *id_map)
|
||||
enum cs_mode mode, struct coresight_path *path)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -884,10 +868,10 @@ static int etm4_enable(struct coresight_device *csdev, struct perf_event *event,
|
|||
|
||||
switch (mode) {
|
||||
case CS_MODE_SYSFS:
|
||||
ret = etm4_enable_sysfs(csdev);
|
||||
ret = etm4_enable_sysfs(csdev, path);
|
||||
break;
|
||||
case CS_MODE_PERF:
|
||||
ret = etm4_enable_perf(csdev, event, id_map);
|
||||
ret = etm4_enable_perf(csdev, event, path);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
@ -941,10 +925,25 @@ static void etm4_disable_hw(void *info)
|
|||
tsb_csync();
|
||||
etm4x_relaxed_write32(csa, control, TRCPRGCTLR);
|
||||
|
||||
/*
|
||||
* As recommended by section 4.3.7 ("Synchronization when using system
|
||||
* instructions to progrom the trace unit") of ARM IHI 0064H.b, the
|
||||
* self-hosted trace analyzer must perform a Context synchronization
|
||||
* event between writing to the TRCPRGCTLR and reading the TRCSTATR.
|
||||
*/
|
||||
if (!csa->io_mem)
|
||||
isb();
|
||||
|
||||
/* wait for TRCSTATR.PMSTABLE to go to '1' */
|
||||
if (coresight_timeout(csa, TRCSTATR, TRCSTATR_PMSTABLE_BIT, 1))
|
||||
if (etm4x_wait_status(csa, TRCSTATR_PMSTABLE_BIT, 1))
|
||||
dev_err(etm_dev,
|
||||
"timeout while waiting for PM stable Trace Status\n");
|
||||
/*
|
||||
* As recommended by section 4.3.7 (Synchronization of register updates)
|
||||
* of ARM IHI 0064H.b.
|
||||
*/
|
||||
isb();
|
||||
|
||||
/* read the status of the single shot comparators */
|
||||
for (i = 0; i < drvdata->nr_ss_cmp; i++) {
|
||||
config->ss_status[i] =
|
||||
|
@ -1012,7 +1011,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
|
|||
* DYING hotplug callback is serviced by the ETM driver.
|
||||
*/
|
||||
cpus_read_lock();
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
|
||||
/*
|
||||
* Executing etm4_disable_hw on the cpu whose ETM is being disabled
|
||||
|
@ -1020,7 +1019,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev)
|
|||
*/
|
||||
smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1);
|
||||
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
cpus_read_unlock();
|
||||
|
||||
/*
|
||||
|
@ -1067,6 +1066,7 @@ static const struct coresight_ops_source etm4_source_ops = {
|
|||
};
|
||||
|
||||
static const struct coresight_ops etm4_cs_ops = {
|
||||
.trace_id = coresight_etm_get_trace_id,
|
||||
.source_ops = &etm4_source_ops,
|
||||
};
|
||||
|
||||
|
@ -1698,13 +1698,13 @@ static int etm4_starting_cpu(unsigned int cpu)
|
|||
if (!etmdrvdata[cpu])
|
||||
return 0;
|
||||
|
||||
spin_lock(&etmdrvdata[cpu]->spinlock);
|
||||
raw_spin_lock(&etmdrvdata[cpu]->spinlock);
|
||||
if (!etmdrvdata[cpu]->os_unlock)
|
||||
etm4_os_unlock(etmdrvdata[cpu]);
|
||||
|
||||
if (coresight_get_mode(etmdrvdata[cpu]->csdev))
|
||||
etm4_enable_hw(etmdrvdata[cpu]);
|
||||
spin_unlock(&etmdrvdata[cpu]->spinlock);
|
||||
raw_spin_unlock(&etmdrvdata[cpu]->spinlock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1713,10 +1713,10 @@ static int etm4_dying_cpu(unsigned int cpu)
|
|||
if (!etmdrvdata[cpu])
|
||||
return 0;
|
||||
|
||||
spin_lock(&etmdrvdata[cpu]->spinlock);
|
||||
raw_spin_lock(&etmdrvdata[cpu]->spinlock);
|
||||
if (coresight_get_mode(etmdrvdata[cpu]->csdev))
|
||||
etm4_disable_hw(etmdrvdata[cpu]);
|
||||
spin_unlock(&etmdrvdata[cpu]->spinlock);
|
||||
raw_spin_unlock(&etmdrvdata[cpu]->spinlock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1746,7 +1746,7 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
|
|||
etm4_os_lock(drvdata);
|
||||
|
||||
/* wait for TRCSTATR.PMSTABLE to go up */
|
||||
if (coresight_timeout(csa, TRCSTATR, TRCSTATR_PMSTABLE_BIT, 1)) {
|
||||
if (etm4x_wait_status(csa, TRCSTATR_PMSTABLE_BIT, 1)) {
|
||||
dev_err(etm_dev,
|
||||
"timeout while waiting for PM Stable Status\n");
|
||||
etm4_os_unlock(drvdata);
|
||||
|
@ -1837,7 +1837,7 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
|
|||
state->trcpdcr = etm4x_read32(csa, TRCPDCR);
|
||||
|
||||
/* wait for TRCSTATR.IDLE to go up */
|
||||
if (coresight_timeout(csa, TRCSTATR, TRCSTATR_IDLE_BIT, 1)) {
|
||||
if (etm4x_wait_status(csa, TRCSTATR_PMSTABLE_BIT, 1)) {
|
||||
dev_err(etm_dev,
|
||||
"timeout while waiting for Idle Trace Status\n");
|
||||
etm4_os_unlock(drvdata);
|
||||
|
@ -2160,7 +2160,7 @@ static int etm4_probe(struct device *dev)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_init(&drvdata->spinlock);
|
||||
raw_spin_lock_init(&drvdata->spinlock);
|
||||
|
||||
drvdata->cpu = coresight_get_cpu(dev);
|
||||
if (drvdata->cpu < 0)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* Author: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
*/
|
||||
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/pid_namespace.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
@ -174,7 +175,7 @@ static ssize_t reset_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
if (val)
|
||||
config->mode = 0x0;
|
||||
|
||||
|
@ -266,7 +267,7 @@ static ssize_t reset_store(struct device *dev,
|
|||
config->vmid_mask0 = 0x0;
|
||||
config->vmid_mask1 = 0x0;
|
||||
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
|
||||
/* for sysfs - only release trace id when resetting */
|
||||
etm4_release_trace_id(drvdata);
|
||||
|
@ -300,7 +301,7 @@ static ssize_t mode_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
config->mode = val & ETMv4_MODE_ALL;
|
||||
|
||||
if (drvdata->instrp0 == true) {
|
||||
|
@ -437,7 +438,7 @@ static ssize_t mode_store(struct device *dev,
|
|||
if (config->mode & (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER))
|
||||
etm4_config_trace_mode(config);
|
||||
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
@ -466,14 +467,14 @@ static ssize_t pe_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
if (val > drvdata->nr_pe) {
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
config->pe_sel = val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(pe);
|
||||
|
@ -501,7 +502,7 @@ static ssize_t event_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
switch (drvdata->nr_event) {
|
||||
case 0x0:
|
||||
/* EVENT0, bits[7:0] */
|
||||
|
@ -522,7 +523,7 @@ static ssize_t event_store(struct device *dev,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(event);
|
||||
|
@ -550,7 +551,7 @@ static ssize_t event_instren_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
/* start by clearing all instruction event enable bits */
|
||||
config->eventctrl1 &= ~TRCEVENTCTL1R_INSTEN_MASK;
|
||||
switch (drvdata->nr_event) {
|
||||
|
@ -578,7 +579,7 @@ static ssize_t event_instren_store(struct device *dev,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(event_instren);
|
||||
|
@ -739,11 +740,11 @@ static ssize_t event_vinst_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
val &= TRCVICTLR_EVENT_MASK >> __bf_shf(TRCVICTLR_EVENT_MASK);
|
||||
config->vinst_ctrl &= ~TRCVICTLR_EVENT_MASK;
|
||||
config->vinst_ctrl |= FIELD_PREP(TRCVICTLR_EVENT_MASK, val);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(event_vinst);
|
||||
|
@ -771,13 +772,13 @@ static ssize_t s_exlevel_vinst_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
/* clear all EXLEVEL_S bits */
|
||||
config->vinst_ctrl &= ~TRCVICTLR_EXLEVEL_S_MASK;
|
||||
/* enable instruction tracing for corresponding exception level */
|
||||
val &= drvdata->s_ex_level;
|
||||
config->vinst_ctrl |= val << __bf_shf(TRCVICTLR_EXLEVEL_S_MASK);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(s_exlevel_vinst);
|
||||
|
@ -806,13 +807,13 @@ static ssize_t ns_exlevel_vinst_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
/* clear EXLEVEL_NS bits */
|
||||
config->vinst_ctrl &= ~TRCVICTLR_EXLEVEL_NS_MASK;
|
||||
/* enable instruction tracing for corresponding exception level */
|
||||
val &= drvdata->ns_ex_level;
|
||||
config->vinst_ctrl |= val << __bf_shf(TRCVICTLR_EXLEVEL_NS_MASK);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(ns_exlevel_vinst);
|
||||
|
@ -846,9 +847,9 @@ static ssize_t addr_idx_store(struct device *dev,
|
|||
* Use spinlock to ensure index doesn't change while it gets
|
||||
* dereferenced multiple times within a spinlock block elsewhere.
|
||||
*/
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
config->addr_idx = val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(addr_idx);
|
||||
|
@ -862,7 +863,7 @@ static ssize_t addr_instdatatype_show(struct device *dev,
|
|||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
val = FIELD_GET(TRCACATRn_TYPE_MASK, config->addr_acc[idx]);
|
||||
len = scnprintf(buf, PAGE_SIZE, "%s\n",
|
||||
|
@ -870,7 +871,7 @@ static ssize_t addr_instdatatype_show(struct device *dev,
|
|||
(val == TRCACATRn_TYPE_DATA_LOAD_ADDR ? "data_load" :
|
||||
(val == TRCACATRn_TYPE_DATA_STORE_ADDR ? "data_store" :
|
||||
"data_load_store")));
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
@ -888,13 +889,13 @@ static ssize_t addr_instdatatype_store(struct device *dev,
|
|||
if (sscanf(buf, "%s", str) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
if (!strcmp(str, "instr"))
|
||||
/* TYPE, bits[1:0] */
|
||||
config->addr_acc[idx] &= ~TRCACATRn_TYPE_MASK;
|
||||
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(addr_instdatatype);
|
||||
|
@ -909,14 +910,14 @@ static ssize_t addr_single_show(struct device *dev,
|
|||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
idx = config->addr_idx;
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
|
||||
config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return -EPERM;
|
||||
}
|
||||
val = (unsigned long)config->addr_val[idx];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -932,17 +933,17 @@ static ssize_t addr_single_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
|
||||
config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) {
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
config->addr_val[idx] = (u64)val;
|
||||
config->addr_type[idx] = ETM_ADDR_TYPE_SINGLE;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(addr_single);
|
||||
|
@ -956,23 +957,23 @@ static ssize_t addr_range_show(struct device *dev,
|
|||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
if (idx % 2 != 0) {
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return -EPERM;
|
||||
}
|
||||
if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE &&
|
||||
config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
|
||||
(config->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
|
||||
config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
val1 = (unsigned long)config->addr_val[idx];
|
||||
val2 = (unsigned long)config->addr_val[idx + 1];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2);
|
||||
}
|
||||
|
||||
|
@ -995,10 +996,10 @@ static ssize_t addr_range_store(struct device *dev,
|
|||
if (val1 > val2)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
if (idx % 2 != 0) {
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
|
@ -1006,7 +1007,7 @@ static ssize_t addr_range_store(struct device *dev,
|
|||
config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) ||
|
||||
(config->addr_type[idx] == ETM_ADDR_TYPE_RANGE &&
|
||||
config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) {
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
|
@ -1023,7 +1024,7 @@ static ssize_t addr_range_store(struct device *dev,
|
|||
exclude = config->mode & ETM_MODE_EXCLUDE;
|
||||
etm4_set_mode_exclude(drvdata, exclude ? true : false);
|
||||
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(addr_range);
|
||||
|
@ -1037,17 +1038,17 @@ static ssize_t addr_start_show(struct device *dev,
|
|||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
|
||||
if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
|
||||
config->addr_type[idx] == ETM_ADDR_TYPE_START)) {
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
val = (unsigned long)config->addr_val[idx];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -1063,22 +1064,22 @@ static ssize_t addr_start_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
if (!drvdata->nr_addr_cmp) {
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
|
||||
config->addr_type[idx] == ETM_ADDR_TYPE_START)) {
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
config->addr_val[idx] = (u64)val;
|
||||
config->addr_type[idx] = ETM_ADDR_TYPE_START;
|
||||
config->vissctlr |= BIT(idx);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(addr_start);
|
||||
|
@ -1092,17 +1093,17 @@ static ssize_t addr_stop_show(struct device *dev,
|
|||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
|
||||
if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
|
||||
config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
val = (unsigned long)config->addr_val[idx];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -1118,22 +1119,22 @@ static ssize_t addr_stop_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
if (!drvdata->nr_addr_cmp) {
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE ||
|
||||
config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) {
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
config->addr_val[idx] = (u64)val;
|
||||
config->addr_type[idx] = ETM_ADDR_TYPE_STOP;
|
||||
config->vissctlr |= BIT(idx + 16);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(addr_stop);
|
||||
|
@ -1147,14 +1148,14 @@ static ssize_t addr_ctxtype_show(struct device *dev,
|
|||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
/* CONTEXTTYPE, bits[3:2] */
|
||||
val = FIELD_GET(TRCACATRn_CONTEXTTYPE_MASK, config->addr_acc[idx]);
|
||||
len = scnprintf(buf, PAGE_SIZE, "%s\n", val == ETM_CTX_NONE ? "none" :
|
||||
(val == ETM_CTX_CTXID ? "ctxid" :
|
||||
(val == ETM_CTX_VMID ? "vmid" : "all")));
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
@ -1172,7 +1173,7 @@ static ssize_t addr_ctxtype_store(struct device *dev,
|
|||
if (sscanf(buf, "%s", str) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
if (!strcmp(str, "none"))
|
||||
/* start by clearing context type bits */
|
||||
|
@ -1199,7 +1200,7 @@ static ssize_t addr_ctxtype_store(struct device *dev,
|
|||
if (drvdata->numvmidc)
|
||||
config->addr_acc[idx] |= TRCACATRn_CONTEXTTYPE_VMID;
|
||||
}
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(addr_ctxtype);
|
||||
|
@ -1213,11 +1214,11 @@ static ssize_t addr_context_show(struct device *dev,
|
|||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
/* context ID comparator bits[6:4] */
|
||||
val = FIELD_GET(TRCACATRn_CONTEXT_MASK, config->addr_acc[idx]);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -1238,12 +1239,12 @@ static ssize_t addr_context_store(struct device *dev,
|
|||
drvdata->numcidc : drvdata->numvmidc))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
/* clear context ID comparator bits[6:4] */
|
||||
config->addr_acc[idx] &= ~TRCACATRn_CONTEXT_MASK;
|
||||
config->addr_acc[idx] |= val << __bf_shf(TRCACATRn_CONTEXT_MASK);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(addr_context);
|
||||
|
@ -1257,10 +1258,10 @@ static ssize_t addr_exlevel_s_ns_show(struct device *dev,
|
|||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
val = FIELD_GET(TRCACATRn_EXLEVEL_MASK, config->addr_acc[idx]);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -1279,12 +1280,12 @@ static ssize_t addr_exlevel_s_ns_store(struct device *dev,
|
|||
if (val & ~(TRCACATRn_EXLEVEL_MASK >> __bf_shf(TRCACATRn_EXLEVEL_MASK)))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
/* clear Exlevel_ns & Exlevel_s bits[14:12, 11:8], bit[15] is res0 */
|
||||
config->addr_acc[idx] &= ~TRCACATRn_EXLEVEL_MASK;
|
||||
config->addr_acc[idx] |= val << __bf_shf(TRCACATRn_EXLEVEL_MASK);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(addr_exlevel_s_ns);
|
||||
|
@ -1307,7 +1308,7 @@ static ssize_t addr_cmp_view_show(struct device *dev,
|
|||
int size = 0;
|
||||
bool exclude = false;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->addr_idx;
|
||||
addr_v = config->addr_val[idx];
|
||||
addr_ctrl = config->addr_acc[idx];
|
||||
|
@ -1322,7 +1323,7 @@ static ssize_t addr_cmp_view_show(struct device *dev,
|
|||
}
|
||||
exclude = config->viiectlr & BIT(idx / 2 + 16);
|
||||
}
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
if (addr_type) {
|
||||
size = scnprintf(buf, PAGE_SIZE, "addr_cmp[%i] %s %#lx", idx,
|
||||
addr_type_names[addr_type], addr_v);
|
||||
|
@ -1366,9 +1367,9 @@ static ssize_t vinst_pe_cmp_start_stop_store(struct device *dev,
|
|||
if (!drvdata->nr_pe_cmp)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
config->vipcssctlr = val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(vinst_pe_cmp_start_stop);
|
||||
|
@ -1402,9 +1403,9 @@ static ssize_t seq_idx_store(struct device *dev,
|
|||
* Use spinlock to ensure index doesn't change while it gets
|
||||
* dereferenced multiple times within a spinlock block elsewhere.
|
||||
*/
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
config->seq_idx = val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(seq_idx);
|
||||
|
@ -1448,10 +1449,10 @@ static ssize_t seq_event_show(struct device *dev,
|
|||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->seq_idx;
|
||||
val = config->seq_ctrl[idx];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -1467,11 +1468,11 @@ static ssize_t seq_event_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->seq_idx;
|
||||
/* Seq control has two masks B[15:8] F[7:0] */
|
||||
config->seq_ctrl[idx] = val & 0xFFFF;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(seq_event);
|
||||
|
@ -1535,9 +1536,9 @@ static ssize_t cntr_idx_store(struct device *dev,
|
|||
* Use spinlock to ensure index doesn't change while it gets
|
||||
* dereferenced multiple times within a spinlock block elsewhere.
|
||||
*/
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
config->cntr_idx = val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(cntr_idx);
|
||||
|
@ -1551,10 +1552,10 @@ static ssize_t cntrldvr_show(struct device *dev,
|
|||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->cntr_idx;
|
||||
val = config->cntrldvr[idx];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -1572,10 +1573,10 @@ static ssize_t cntrldvr_store(struct device *dev,
|
|||
if (val > ETM_CNTR_MAX_VAL)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->cntr_idx;
|
||||
config->cntrldvr[idx] = val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(cntrldvr);
|
||||
|
@ -1589,10 +1590,10 @@ static ssize_t cntr_val_show(struct device *dev,
|
|||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->cntr_idx;
|
||||
val = config->cntr_val[idx];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -1610,10 +1611,10 @@ static ssize_t cntr_val_store(struct device *dev,
|
|||
if (val > ETM_CNTR_MAX_VAL)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->cntr_idx;
|
||||
config->cntr_val[idx] = val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(cntr_val);
|
||||
|
@ -1627,10 +1628,10 @@ static ssize_t cntr_ctrl_show(struct device *dev,
|
|||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->cntr_idx;
|
||||
val = config->cntr_ctrl[idx];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -1646,10 +1647,10 @@ static ssize_t cntr_ctrl_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->cntr_idx;
|
||||
config->cntr_ctrl[idx] = val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(cntr_ctrl);
|
||||
|
@ -1687,9 +1688,9 @@ static ssize_t res_idx_store(struct device *dev,
|
|||
* Use spinlock to ensure index doesn't change while it gets
|
||||
* dereferenced multiple times within a spinlock block elsewhere.
|
||||
*/
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
config->res_idx = val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(res_idx);
|
||||
|
@ -1703,10 +1704,10 @@ static ssize_t res_ctrl_show(struct device *dev,
|
|||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->res_idx;
|
||||
val = config->res_ctrl[idx];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -1722,7 +1723,7 @@ static ssize_t res_ctrl_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->res_idx;
|
||||
/* For odd idx pair inversal bit is RES0 */
|
||||
if (idx % 2 != 0)
|
||||
|
@ -1732,7 +1733,7 @@ static ssize_t res_ctrl_store(struct device *dev,
|
|||
TRCRSCTLRn_INV |
|
||||
TRCRSCTLRn_GROUP_MASK |
|
||||
TRCRSCTLRn_SELECT_MASK);
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(res_ctrl);
|
||||
|
@ -1761,9 +1762,9 @@ static ssize_t sshot_idx_store(struct device *dev,
|
|||
if (val >= drvdata->nr_ss_cmp)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
config->ss_idx = val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(sshot_idx);
|
||||
|
@ -1776,9 +1777,9 @@ static ssize_t sshot_ctrl_show(struct device *dev,
|
|||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
val = config->ss_ctrl[config->ss_idx];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -1794,12 +1795,12 @@ static ssize_t sshot_ctrl_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->ss_idx;
|
||||
config->ss_ctrl[idx] = FIELD_PREP(TRCSSCCRn_SAC_ARC_RST_MASK, val);
|
||||
/* must clear bit 31 in related status register on programming */
|
||||
config->ss_status[idx] &= ~TRCSSCSRn_STATUS;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(sshot_ctrl);
|
||||
|
@ -1811,9 +1812,9 @@ static ssize_t sshot_status_show(struct device *dev,
|
|||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
val = config->ss_status[config->ss_idx];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
|
||||
}
|
||||
static DEVICE_ATTR_RO(sshot_status);
|
||||
|
@ -1826,9 +1827,9 @@ static ssize_t sshot_pe_ctrl_show(struct device *dev,
|
|||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
struct etmv4_config *config = &drvdata->config;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
val = config->ss_pe_cmp[config->ss_idx];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -1844,12 +1845,12 @@ static ssize_t sshot_pe_ctrl_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->ss_idx;
|
||||
config->ss_pe_cmp[idx] = FIELD_PREP(TRCSSPCICRn_PC_MASK, val);
|
||||
/* must clear bit 31 in related status register on programming */
|
||||
config->ss_status[idx] &= ~TRCSSCSRn_STATUS;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(sshot_pe_ctrl);
|
||||
|
@ -1883,9 +1884,9 @@ static ssize_t ctxid_idx_store(struct device *dev,
|
|||
* Use spinlock to ensure index doesn't change while it gets
|
||||
* dereferenced multiple times within a spinlock block elsewhere.
|
||||
*/
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
config->ctxid_idx = val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(ctxid_idx);
|
||||
|
@ -1906,10 +1907,10 @@ static ssize_t ctxid_pid_show(struct device *dev,
|
|||
if (task_active_pid_ns(current) != &init_pid_ns)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->ctxid_idx;
|
||||
val = (unsigned long)config->ctxid_pid[idx];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -1944,10 +1945,10 @@ static ssize_t ctxid_pid_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &pid))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
idx = config->ctxid_idx;
|
||||
config->ctxid_pid[idx] = (u64)pid;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(ctxid_pid);
|
||||
|
@ -1967,10 +1968,10 @@ static ssize_t ctxid_masks_show(struct device *dev,
|
|||
if (task_active_pid_ns(current) != &init_pid_ns)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
val1 = config->ctxid_mask0;
|
||||
val2 = config->ctxid_mask1;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2);
|
||||
}
|
||||
|
||||
|
@ -2003,7 +2004,7 @@ static ssize_t ctxid_masks_store(struct device *dev,
|
|||
if ((drvdata->numcidc > 4) && (nr_inputs != 2))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
/*
|
||||
* each byte[0..3] controls mask value applied to ctxid
|
||||
* comparator[0..3]
|
||||
|
@ -2075,7 +2076,7 @@ static ssize_t ctxid_masks_store(struct device *dev,
|
|||
mask >>= 0x8;
|
||||
}
|
||||
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(ctxid_masks);
|
||||
|
@ -2109,9 +2110,9 @@ static ssize_t vmid_idx_store(struct device *dev,
|
|||
* Use spinlock to ensure index doesn't change while it gets
|
||||
* dereferenced multiple times within a spinlock block elsewhere.
|
||||
*/
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
config->vmid_idx = val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(vmid_idx);
|
||||
|
@ -2131,9 +2132,9 @@ static ssize_t vmid_val_show(struct device *dev,
|
|||
if (!task_is_in_init_pid_ns(current))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
val = (unsigned long)config->vmid_val[config->vmid_idx];
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
|
||||
}
|
||||
|
||||
|
@ -2161,9 +2162,9 @@ static ssize_t vmid_val_store(struct device *dev,
|
|||
if (kstrtoul(buf, 16, &val))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
config->vmid_val[config->vmid_idx] = (u64)val;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(vmid_val);
|
||||
|
@ -2182,10 +2183,10 @@ static ssize_t vmid_masks_show(struct device *dev,
|
|||
if (!task_is_in_init_pid_ns(current))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
val1 = config->vmid_mask0;
|
||||
val2 = config->vmid_mask1;
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2);
|
||||
}
|
||||
|
||||
|
@ -2217,7 +2218,7 @@ static ssize_t vmid_masks_store(struct device *dev,
|
|||
if ((drvdata->numvmidc > 4) && (nr_inputs != 2))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock(&drvdata->spinlock);
|
||||
raw_spin_lock(&drvdata->spinlock);
|
||||
|
||||
/*
|
||||
* each byte[0..3] controls mask value applied to vmid
|
||||
|
@ -2290,7 +2291,7 @@ static ssize_t vmid_masks_store(struct device *dev,
|
|||
else
|
||||
mask >>= 0x8;
|
||||
}
|
||||
spin_unlock(&drvdata->spinlock);
|
||||
raw_spin_unlock(&drvdata->spinlock);
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(vmid_masks);
|
||||
|
@ -2402,10 +2403,9 @@ static ssize_t trctraceid_show(struct device *dev,
|
|||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int trace_id;
|
||||
struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
int trace_id = coresight_etm_get_trace_id(drvdata->csdev, CS_MODE_SYSFS, NULL);
|
||||
|
||||
trace_id = etm4_read_alloc_trace_id(drvdata);
|
||||
if (trace_id < 0)
|
||||
return trace_id;
|
||||
|
||||
|
|
|
@ -989,7 +989,7 @@ struct etmv4_drvdata {
|
|||
struct clk *pclk;
|
||||
void __iomem *base;
|
||||
struct coresight_device *csdev;
|
||||
spinlock_t spinlock;
|
||||
raw_spinlock_t spinlock;
|
||||
int cpu;
|
||||
u8 arch;
|
||||
u8 nr_pe;
|
||||
|
@ -1066,6 +1066,5 @@ static inline bool etm4x_is_ete(struct etmv4_drvdata *drvdata)
|
|||
return drvdata->arch >= ETM_ARCH_ETE;
|
||||
}
|
||||
|
||||
int etm4_read_alloc_trace_id(struct etmv4_drvdata *drvdata);
|
||||
void etm4_release_trace_id(struct etmv4_drvdata *drvdata);
|
||||
#endif
|
||||
|
|
|
@ -47,7 +47,7 @@ struct funnel_drvdata {
|
|||
struct clk *pclk;
|
||||
struct coresight_device *csdev;
|
||||
unsigned long priority;
|
||||
spinlock_t spinlock;
|
||||
raw_spinlock_t spinlock;
|
||||
};
|
||||
|
||||
static int dynamic_funnel_enable_hw(struct funnel_drvdata *drvdata, int port)
|
||||
|
@ -85,7 +85,7 @@ static int funnel_enable(struct coresight_device *csdev,
|
|||
unsigned long flags;
|
||||
bool first_enable = false;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (in->dest_refcnt == 0) {
|
||||
if (drvdata->base)
|
||||
rc = dynamic_funnel_enable_hw(drvdata, in->dest_port);
|
||||
|
@ -94,7 +94,7 @@ static int funnel_enable(struct coresight_device *csdev,
|
|||
}
|
||||
if (!rc)
|
||||
in->dest_refcnt++;
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
if (first_enable)
|
||||
dev_dbg(&csdev->dev, "FUNNEL inport %d enabled\n",
|
||||
|
@ -129,13 +129,13 @@ static void funnel_disable(struct coresight_device *csdev,
|
|||
unsigned long flags;
|
||||
bool last_disable = false;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (--in->dest_refcnt == 0) {
|
||||
if (drvdata->base)
|
||||
dynamic_funnel_disable_hw(drvdata, in->dest_port);
|
||||
last_disable = true;
|
||||
}
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
if (last_disable)
|
||||
dev_dbg(&csdev->dev, "FUNNEL inport %d disabled\n",
|
||||
|
@ -266,7 +266,7 @@ static int funnel_probe(struct device *dev, struct resource *res)
|
|||
}
|
||||
dev->platform_data = pdata;
|
||||
|
||||
spin_lock_init(&drvdata->spinlock);
|
||||
raw_spin_lock_init(&drvdata->spinlock);
|
||||
desc.type = CORESIGHT_DEV_TYPE_LINK;
|
||||
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG;
|
||||
desc.ops = &funnel_cs_ops;
|
||||
|
|
|
@ -132,16 +132,16 @@ static inline void CS_UNLOCK(void __iomem *addr)
|
|||
} while (0);
|
||||
}
|
||||
|
||||
void coresight_disable_path(struct list_head *path);
|
||||
int coresight_enable_path(struct list_head *path, enum cs_mode mode,
|
||||
void coresight_disable_path(struct coresight_path *path);
|
||||
int coresight_enable_path(struct coresight_path *path, enum cs_mode mode,
|
||||
void *sink_data);
|
||||
struct coresight_device *coresight_get_sink(struct list_head *path);
|
||||
struct coresight_device *coresight_get_sink(struct coresight_path *path);
|
||||
struct coresight_device *coresight_get_sink_by_id(u32 id);
|
||||
struct coresight_device *
|
||||
coresight_find_default_sink(struct coresight_device *csdev);
|
||||
struct list_head *coresight_build_path(struct coresight_device *csdev,
|
||||
struct coresight_device *sink);
|
||||
void coresight_release_path(struct list_head *path);
|
||||
struct coresight_path *coresight_build_path(struct coresight_device *csdev,
|
||||
struct coresight_device *sink);
|
||||
void coresight_release_path(struct coresight_path *path);
|
||||
int coresight_add_sysfs_link(struct coresight_sysfs_link *info);
|
||||
void coresight_remove_sysfs_link(struct coresight_sysfs_link *info);
|
||||
int coresight_create_conns_sysfs_group(struct coresight_device *csdev);
|
||||
|
@ -152,6 +152,8 @@ int coresight_make_links(struct coresight_device *orig,
|
|||
void coresight_remove_links(struct coresight_device *orig,
|
||||
struct coresight_connection *conn);
|
||||
u32 coresight_get_sink_id(struct coresight_device *csdev);
|
||||
void coresight_path_assign_trace_id(struct coresight_path *path,
|
||||
enum cs_mode mode);
|
||||
|
||||
#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM3X)
|
||||
extern int etm_readl_cp14(u32 off, unsigned int *val);
|
||||
|
|
|
@ -41,7 +41,7 @@ struct replicator_drvdata {
|
|||
struct clk *atclk;
|
||||
struct clk *pclk;
|
||||
struct coresight_device *csdev;
|
||||
spinlock_t spinlock;
|
||||
raw_spinlock_t spinlock;
|
||||
bool check_idfilter_val;
|
||||
};
|
||||
|
||||
|
@ -125,7 +125,7 @@ static int replicator_enable(struct coresight_device *csdev,
|
|||
unsigned long flags;
|
||||
bool first_enable = false;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (out->src_refcnt == 0) {
|
||||
if (drvdata->base)
|
||||
rc = dynamic_replicator_enable(drvdata, in->dest_port,
|
||||
|
@ -135,7 +135,7 @@ static int replicator_enable(struct coresight_device *csdev,
|
|||
}
|
||||
if (!rc)
|
||||
out->src_refcnt++;
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
if (first_enable)
|
||||
dev_dbg(&csdev->dev, "REPLICATOR enabled\n");
|
||||
|
@ -179,14 +179,14 @@ static void replicator_disable(struct coresight_device *csdev,
|
|||
unsigned long flags;
|
||||
bool last_disable = false;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (--out->src_refcnt == 0) {
|
||||
if (drvdata->base)
|
||||
dynamic_replicator_disable(drvdata, in->dest_port,
|
||||
out->src_port);
|
||||
last_disable = true;
|
||||
}
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
if (last_disable)
|
||||
dev_dbg(&csdev->dev, "REPLICATOR disabled\n");
|
||||
|
@ -277,7 +277,7 @@ static int replicator_probe(struct device *dev, struct resource *res)
|
|||
}
|
||||
dev->platform_data = pdata;
|
||||
|
||||
spin_lock_init(&drvdata->spinlock);
|
||||
raw_spin_lock_init(&drvdata->spinlock);
|
||||
desc.type = CORESIGHT_DEV_TYPE_LINK;
|
||||
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_SPLIT;
|
||||
desc.ops = &replicator_cs_ops;
|
||||
|
|
|
@ -195,7 +195,7 @@ static void stm_enable_hw(struct stm_drvdata *drvdata)
|
|||
|
||||
static int stm_enable(struct coresight_device *csdev, struct perf_event *event,
|
||||
enum cs_mode mode,
|
||||
__maybe_unused struct coresight_trace_id_map *trace_id)
|
||||
__maybe_unused struct coresight_path *path)
|
||||
{
|
||||
struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
|
@ -281,12 +281,23 @@ static void stm_disable(struct coresight_device *csdev,
|
|||
}
|
||||
}
|
||||
|
||||
static int stm_trace_id(struct coresight_device *csdev, __maybe_unused enum cs_mode mode,
|
||||
__maybe_unused struct coresight_device *sink)
|
||||
{
|
||||
struct stm_drvdata *drvdata;
|
||||
|
||||
drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
return drvdata->traceid;
|
||||
}
|
||||
|
||||
static const struct coresight_ops_source stm_source_ops = {
|
||||
.enable = stm_enable,
|
||||
.disable = stm_disable,
|
||||
};
|
||||
|
||||
static const struct coresight_ops stm_cs_ops = {
|
||||
.trace_id = stm_trace_id,
|
||||
.source_ops = &stm_source_ops,
|
||||
};
|
||||
|
||||
|
|
|
@ -160,7 +160,7 @@ static struct configfs_attribute *cscfg_config_view_attrs[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type cscfg_config_view_type = {
|
||||
static const struct config_item_type cscfg_config_view_type = {
|
||||
.ct_owner = THIS_MODULE,
|
||||
.ct_attrs = cscfg_config_view_attrs,
|
||||
};
|
||||
|
@ -170,7 +170,7 @@ static struct configfs_attribute *cscfg_config_preset_attrs[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type cscfg_config_preset_type = {
|
||||
static const struct config_item_type cscfg_config_preset_type = {
|
||||
.ct_owner = THIS_MODULE,
|
||||
.ct_attrs = cscfg_config_preset_attrs,
|
||||
};
|
||||
|
@ -272,7 +272,7 @@ static struct configfs_attribute *cscfg_feature_view_attrs[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type cscfg_feature_view_type = {
|
||||
static const struct config_item_type cscfg_feature_view_type = {
|
||||
.ct_owner = THIS_MODULE,
|
||||
.ct_attrs = cscfg_feature_view_attrs,
|
||||
};
|
||||
|
@ -309,7 +309,7 @@ static struct configfs_attribute *cscfg_param_view_attrs[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct config_item_type cscfg_param_view_type = {
|
||||
static const struct config_item_type cscfg_param_view_type = {
|
||||
.ct_owner = THIS_MODULE,
|
||||
.ct_attrs = cscfg_param_view_attrs,
|
||||
};
|
||||
|
@ -380,7 +380,7 @@ static struct config_group *cscfg_create_feature_group(struct cscfg_feature_desc
|
|||
return &feat_view->group;
|
||||
}
|
||||
|
||||
static struct config_item_type cscfg_configs_type = {
|
||||
static const struct config_item_type cscfg_configs_type = {
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
@ -414,7 +414,7 @@ void cscfg_configfs_del_config(struct cscfg_config_desc *config_desc)
|
|||
}
|
||||
}
|
||||
|
||||
static struct config_item_type cscfg_features_type = {
|
||||
static const struct config_item_type cscfg_features_type = {
|
||||
.ct_owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
|
|
|
@ -89,9 +89,9 @@ static int cscfg_add_csdev_cfg(struct coresight_device *csdev,
|
|||
}
|
||||
/* if matched features, add config to device.*/
|
||||
if (config_csdev) {
|
||||
spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
|
||||
raw_spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
|
||||
list_add(&config_csdev->node, &csdev->config_csdev_list);
|
||||
spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
|
||||
raw_spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -194,9 +194,9 @@ static int cscfg_load_feat_csdev(struct coresight_device *csdev,
|
|||
|
||||
/* add to internal csdev feature list & initialise using reset call */
|
||||
cscfg_reset_feat(feat_csdev);
|
||||
spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
|
||||
raw_spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
|
||||
list_add(&feat_csdev->node, &csdev->feature_csdev_list);
|
||||
spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
|
||||
raw_spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -765,7 +765,7 @@ static int cscfg_list_add_csdev(struct coresight_device *csdev,
|
|||
|
||||
INIT_LIST_HEAD(&csdev->feature_csdev_list);
|
||||
INIT_LIST_HEAD(&csdev->config_csdev_list);
|
||||
spin_lock_init(&csdev->cscfg_csdev_lock);
|
||||
raw_spin_lock_init(&csdev->cscfg_csdev_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -855,7 +855,7 @@ void cscfg_csdev_reset_feats(struct coresight_device *csdev)
|
|||
struct cscfg_feature_csdev *feat_csdev;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
|
||||
raw_spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
|
||||
if (list_empty(&csdev->feature_csdev_list))
|
||||
goto unlock_exit;
|
||||
|
||||
|
@ -863,7 +863,7 @@ void cscfg_csdev_reset_feats(struct coresight_device *csdev)
|
|||
cscfg_reset_feat(feat_csdev);
|
||||
|
||||
unlock_exit:
|
||||
spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
|
||||
raw_spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cscfg_csdev_reset_feats);
|
||||
|
||||
|
@ -1059,7 +1059,7 @@ int cscfg_csdev_enable_active_config(struct coresight_device *csdev,
|
|||
* Look for matching configuration - set the active configuration
|
||||
* context if found.
|
||||
*/
|
||||
spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
|
||||
raw_spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
|
||||
list_for_each_entry(config_csdev_item, &csdev->config_csdev_list, node) {
|
||||
config_desc = config_csdev_item->config_desc;
|
||||
if ((atomic_read(&config_desc->active_cnt)) &&
|
||||
|
@ -1069,7 +1069,7 @@ int cscfg_csdev_enable_active_config(struct coresight_device *csdev,
|
|||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
|
||||
raw_spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
|
||||
|
||||
/*
|
||||
* If found, attempt to enable
|
||||
|
@ -1090,12 +1090,12 @@ int cscfg_csdev_enable_active_config(struct coresight_device *csdev,
|
|||
*
|
||||
* Set enabled if OK, err if not.
|
||||
*/
|
||||
spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
|
||||
raw_spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
|
||||
if (csdev->active_cscfg_ctxt)
|
||||
config_csdev_active->enabled = true;
|
||||
else
|
||||
err = -EBUSY;
|
||||
spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
|
||||
raw_spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
|
@ -1124,7 +1124,7 @@ void cscfg_csdev_disable_active_config(struct coresight_device *csdev)
|
|||
* If it was not enabled, we have no work to do, otherwise mark as disabled.
|
||||
* Clear the active config pointer.
|
||||
*/
|
||||
spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
|
||||
raw_spin_lock_irqsave(&csdev->cscfg_csdev_lock, flags);
|
||||
config_csdev = (struct cscfg_config_csdev *)csdev->active_cscfg_ctxt;
|
||||
if (config_csdev) {
|
||||
if (!config_csdev->enabled)
|
||||
|
@ -1133,7 +1133,7 @@ void cscfg_csdev_disable_active_config(struct coresight_device *csdev)
|
|||
config_csdev->enabled = false;
|
||||
}
|
||||
csdev->active_cscfg_ctxt = NULL;
|
||||
spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
|
||||
raw_spin_unlock_irqrestore(&csdev->cscfg_csdev_lock, flags);
|
||||
|
||||
/* true if there was an enabled active config */
|
||||
if (config_csdev)
|
||||
|
|
|
@ -22,7 +22,7 @@ static DEFINE_IDR(path_idr);
|
|||
* When operating Coresight drivers from the sysFS interface, only a single
|
||||
* path can exist from a tracer (associated to a CPU) to a sink.
|
||||
*/
|
||||
static DEFINE_PER_CPU(struct list_head *, tracer_path);
|
||||
static DEFINE_PER_CPU(struct coresight_path *, tracer_path);
|
||||
|
||||
ssize_t coresight_simple_show_pair(struct device *_dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
|
@ -53,7 +53,8 @@ ssize_t coresight_simple_show32(struct device *_dev,
|
|||
EXPORT_SYMBOL_GPL(coresight_simple_show32);
|
||||
|
||||
static int coresight_enable_source_sysfs(struct coresight_device *csdev,
|
||||
enum cs_mode mode, void *data)
|
||||
enum cs_mode mode,
|
||||
struct coresight_path *path)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -64,7 +65,7 @@ static int coresight_enable_source_sysfs(struct coresight_device *csdev,
|
|||
*/
|
||||
lockdep_assert_held(&coresight_mutex);
|
||||
if (coresight_get_mode(csdev) != CS_MODE_SYSFS) {
|
||||
ret = source_ops(csdev)->enable(csdev, data, mode, NULL);
|
||||
ret = source_ops(csdev)->enable(csdev, NULL, mode, path);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
@ -167,7 +168,7 @@ int coresight_enable_sysfs(struct coresight_device *csdev)
|
|||
{
|
||||
int cpu, ret = 0;
|
||||
struct coresight_device *sink;
|
||||
struct list_head *path;
|
||||
struct coresight_path *path;
|
||||
enum coresight_dev_subtype_source subtype;
|
||||
u32 hash;
|
||||
|
||||
|
@ -209,11 +210,15 @@ int coresight_enable_sysfs(struct coresight_device *csdev)
|
|||
goto out;
|
||||
}
|
||||
|
||||
coresight_path_assign_trace_id(path, CS_MODE_SYSFS);
|
||||
if (!IS_VALID_CS_TRACE_ID(path->trace_id))
|
||||
goto err_path;
|
||||
|
||||
ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL);
|
||||
if (ret)
|
||||
goto err_path;
|
||||
|
||||
ret = coresight_enable_source_sysfs(csdev, CS_MODE_SYSFS, NULL);
|
||||
ret = coresight_enable_source_sysfs(csdev, CS_MODE_SYSFS, path);
|
||||
if (ret)
|
||||
goto err_source;
|
||||
|
||||
|
@ -262,7 +267,7 @@ EXPORT_SYMBOL_GPL(coresight_enable_sysfs);
|
|||
void coresight_disable_sysfs(struct coresight_device *csdev)
|
||||
{
|
||||
int cpu, ret;
|
||||
struct list_head *path = NULL;
|
||||
struct coresight_path *path = NULL;
|
||||
u32 hash;
|
||||
|
||||
mutex_lock(&coresight_mutex);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -104,6 +105,128 @@ u32 tmc_get_memwidth_mask(struct tmc_drvdata *drvdata)
|
|||
return mask;
|
||||
}
|
||||
|
||||
static bool is_tmc_crashdata_valid(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
struct tmc_crash_metadata *mdata;
|
||||
|
||||
if (!tmc_has_reserved_buffer(drvdata) ||
|
||||
!tmc_has_crash_mdata_buffer(drvdata))
|
||||
return false;
|
||||
|
||||
mdata = drvdata->crash_mdata.vaddr;
|
||||
|
||||
/* Check version match */
|
||||
if (mdata->version != CS_CRASHDATA_VERSION)
|
||||
return false;
|
||||
|
||||
/* Check for valid metadata */
|
||||
if (!mdata->valid) {
|
||||
dev_dbg(&drvdata->csdev->dev,
|
||||
"Data invalid in tmc crash metadata\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Buffer address given by metadata for retrieval of trace data
|
||||
* from previous boot is expected to be same as the reserved
|
||||
* trace buffer memory region provided through DTS
|
||||
*/
|
||||
if (drvdata->resrv_buf.paddr != mdata->trace_paddr) {
|
||||
dev_dbg(&drvdata->csdev->dev,
|
||||
"Trace buffer address of previous boot invalid\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check data integrity of metadata */
|
||||
if (mdata->crc32_mdata != find_crash_metadata_crc(mdata)) {
|
||||
dev_err(&drvdata->csdev->dev,
|
||||
"CRC mismatch in tmc crash metadata\n");
|
||||
return false;
|
||||
}
|
||||
/* Check data integrity of tracedata */
|
||||
if (mdata->crc32_tdata != find_crash_tracedata_crc(drvdata, mdata)) {
|
||||
dev_err(&drvdata->csdev->dev,
|
||||
"CRC mismatch in tmc crash tracedata\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline ssize_t tmc_get_resvbuf_trace(struct tmc_drvdata *drvdata,
|
||||
loff_t pos, size_t len, char **bufpp)
|
||||
{
|
||||
s64 offset;
|
||||
ssize_t actual = len;
|
||||
struct tmc_resrv_buf *rbuf = &drvdata->resrv_buf;
|
||||
|
||||
if (pos + actual > rbuf->len)
|
||||
actual = rbuf->len - pos;
|
||||
if (actual <= 0)
|
||||
return 0;
|
||||
|
||||
/* Compute the offset from which we read the data */
|
||||
offset = rbuf->offset + pos;
|
||||
if (offset >= rbuf->size)
|
||||
offset -= rbuf->size;
|
||||
|
||||
/* Adjust the length to limit this transaction to end of buffer */
|
||||
actual = (actual < (rbuf->size - offset)) ?
|
||||
actual : rbuf->size - offset;
|
||||
|
||||
*bufpp = (char *)rbuf->vaddr + offset;
|
||||
|
||||
return actual;
|
||||
}
|
||||
|
||||
static int tmc_prepare_crashdata(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
char *bufp;
|
||||
ssize_t len;
|
||||
u32 status, size;
|
||||
u64 rrp, rwp, dba;
|
||||
struct tmc_resrv_buf *rbuf;
|
||||
struct tmc_crash_metadata *mdata;
|
||||
|
||||
mdata = drvdata->crash_mdata.vaddr;
|
||||
rbuf = &drvdata->resrv_buf;
|
||||
|
||||
rrp = mdata->tmc_rrp;
|
||||
rwp = mdata->tmc_rwp;
|
||||
dba = mdata->tmc_dba;
|
||||
status = mdata->tmc_sts;
|
||||
size = mdata->tmc_ram_size << 2;
|
||||
|
||||
/* Sync the buffer pointers */
|
||||
rbuf->offset = rrp - dba;
|
||||
if (status & TMC_STS_FULL)
|
||||
rbuf->len = size;
|
||||
else
|
||||
rbuf->len = rwp - rrp;
|
||||
|
||||
/* Additional sanity checks for validating metadata */
|
||||
if ((rbuf->offset > size) ||
|
||||
(rbuf->len > size)) {
|
||||
dev_dbg(&drvdata->csdev->dev,
|
||||
"Offset and length invalid in tmc crash metadata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (status & TMC_STS_FULL) {
|
||||
len = tmc_get_resvbuf_trace(drvdata, 0x0,
|
||||
CORESIGHT_BARRIER_PKT_SIZE, &bufp);
|
||||
if (len >= CORESIGHT_BARRIER_PKT_SIZE) {
|
||||
coresight_insert_barrier_packet(bufp);
|
||||
/* Recalculate crc */
|
||||
mdata->crc32_tdata = find_crash_tracedata_crc(drvdata,
|
||||
mdata);
|
||||
mdata->crc32_mdata = find_crash_metadata_crc(mdata);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc_read_prepare(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -222,6 +345,84 @@ static const struct file_operations tmc_fops = {
|
|||
.release = tmc_release,
|
||||
};
|
||||
|
||||
static int tmc_crashdata_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int err = 0;
|
||||
unsigned long flags;
|
||||
struct tmc_resrv_buf *rbuf;
|
||||
struct tmc_crash_metadata *mdata;
|
||||
struct tmc_drvdata *drvdata = container_of(file->private_data,
|
||||
struct tmc_drvdata,
|
||||
crashdev);
|
||||
|
||||
mdata = drvdata->crash_mdata.vaddr;
|
||||
rbuf = &drvdata->resrv_buf;
|
||||
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (mdata->valid)
|
||||
rbuf->reading = true;
|
||||
else
|
||||
err = -ENOENT;
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
nonseekable_open(inode, file);
|
||||
dev_dbg(&drvdata->csdev->dev, "%s: successfully opened\n", __func__);
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t tmc_crashdata_read(struct file *file, char __user *data,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
char *bufp;
|
||||
ssize_t actual;
|
||||
struct tmc_drvdata *drvdata = container_of(file->private_data,
|
||||
struct tmc_drvdata,
|
||||
crashdev);
|
||||
|
||||
actual = tmc_get_resvbuf_trace(drvdata, *ppos, len, &bufp);
|
||||
if (actual <= 0)
|
||||
return 0;
|
||||
|
||||
if (copy_to_user(data, bufp, actual)) {
|
||||
dev_dbg(&drvdata->csdev->dev,
|
||||
"%s: copy_to_user failed\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
*ppos += actual;
|
||||
dev_dbg(&drvdata->csdev->dev, "%zu bytes copied\n", actual);
|
||||
|
||||
return actual;
|
||||
}
|
||||
|
||||
static int tmc_crashdata_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
struct tmc_resrv_buf *rbuf;
|
||||
struct tmc_drvdata *drvdata = container_of(file->private_data,
|
||||
struct tmc_drvdata,
|
||||
crashdev);
|
||||
|
||||
rbuf = &drvdata->resrv_buf;
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
rbuf->reading = false;
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
dev_dbg(&drvdata->csdev->dev, "%s: released\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations tmc_crashdata_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = tmc_crashdata_open,
|
||||
.read = tmc_crashdata_read,
|
||||
.release = tmc_crashdata_release,
|
||||
};
|
||||
|
||||
static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid)
|
||||
{
|
||||
enum tmc_mem_intf_width memwidth;
|
||||
|
@ -331,9 +532,40 @@ static ssize_t buffer_size_store(struct device *dev,
|
|||
|
||||
static DEVICE_ATTR_RW(buffer_size);
|
||||
|
||||
static ssize_t stop_on_flush_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
return sprintf(buf, "%#x\n", drvdata->stop_on_flush);
|
||||
}
|
||||
|
||||
static ssize_t stop_on_flush_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
{
|
||||
int ret;
|
||||
u8 val;
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
ret = kstrtou8(buf, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (val)
|
||||
drvdata->stop_on_flush = true;
|
||||
else
|
||||
drvdata->stop_on_flush = false;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(stop_on_flush);
|
||||
|
||||
|
||||
static struct attribute *coresight_tmc_attrs[] = {
|
||||
&dev_attr_trigger_cntr.attr,
|
||||
&dev_attr_buffer_size.attr,
|
||||
&dev_attr_stop_on_flush.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -398,6 +630,67 @@ static inline bool tmc_etr_has_non_secure_access(struct tmc_drvdata *drvdata)
|
|||
|
||||
static const struct amba_id tmc_ids[];
|
||||
|
||||
static int of_tmc_get_reserved_resource_by_name(struct device *dev,
|
||||
const char *name,
|
||||
struct resource *res)
|
||||
{
|
||||
int index, rc = -ENODEV;
|
||||
struct device_node *node;
|
||||
|
||||
if (!is_of_node(dev->fwnode))
|
||||
return -ENODEV;
|
||||
|
||||
index = of_property_match_string(dev->of_node, "memory-region-names",
|
||||
name);
|
||||
if (index < 0)
|
||||
return rc;
|
||||
|
||||
node = of_parse_phandle(dev->of_node, "memory-region", index);
|
||||
if (!node)
|
||||
return rc;
|
||||
|
||||
if (!of_address_to_resource(node, 0, res) &&
|
||||
res->start != 0 && resource_size(res) != 0)
|
||||
rc = 0;
|
||||
of_node_put(node);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void tmc_get_reserved_region(struct device *parent)
|
||||
{
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(parent);
|
||||
struct resource res;
|
||||
|
||||
if (of_tmc_get_reserved_resource_by_name(parent, "tracedata", &res))
|
||||
return;
|
||||
|
||||
drvdata->resrv_buf.vaddr = memremap(res.start,
|
||||
resource_size(&res),
|
||||
MEMREMAP_WC);
|
||||
if (IS_ERR_OR_NULL(drvdata->resrv_buf.vaddr)) {
|
||||
dev_err(parent, "Reserved trace buffer mapping failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
drvdata->resrv_buf.paddr = res.start;
|
||||
drvdata->resrv_buf.size = resource_size(&res);
|
||||
|
||||
if (of_tmc_get_reserved_resource_by_name(parent, "metadata", &res))
|
||||
return;
|
||||
|
||||
drvdata->crash_mdata.vaddr = memremap(res.start,
|
||||
resource_size(&res),
|
||||
MEMREMAP_WC);
|
||||
if (IS_ERR_OR_NULL(drvdata->crash_mdata.vaddr)) {
|
||||
dev_err(parent, "Metadata memory mapping failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
drvdata->crash_mdata.paddr = res.start;
|
||||
drvdata->crash_mdata.size = resource_size(&res);
|
||||
}
|
||||
|
||||
/* Detect and initialise the capabilities of a TMC ETR */
|
||||
static int tmc_etr_setup_caps(struct device *parent, u32 devid,
|
||||
struct csdev_access *access)
|
||||
|
@ -470,6 +763,22 @@ static u32 tmc_etr_get_max_burst_size(struct device *dev)
|
|||
return burst_size;
|
||||
}
|
||||
|
||||
static void register_crash_dev_interface(struct tmc_drvdata *drvdata,
|
||||
const char *name)
|
||||
{
|
||||
drvdata->crashdev.name =
|
||||
devm_kasprintf(&drvdata->csdev->dev, GFP_KERNEL, "%s_%s", "crash", name);
|
||||
drvdata->crashdev.minor = MISC_DYNAMIC_MINOR;
|
||||
drvdata->crashdev.fops = &tmc_crashdata_fops;
|
||||
if (misc_register(&drvdata->crashdev)) {
|
||||
dev_dbg(&drvdata->csdev->dev,
|
||||
"Failed to setup user interface for crashdata\n");
|
||||
drvdata->crashdev.fops = NULL;
|
||||
} else
|
||||
dev_info(&drvdata->csdev->dev,
|
||||
"Valid crash tracedata found\n");
|
||||
}
|
||||
|
||||
static int __tmc_probe(struct device *dev, struct resource *res)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -492,7 +801,7 @@ static int __tmc_probe(struct device *dev, struct resource *res)
|
|||
drvdata->base = base;
|
||||
desc.access = CSDEV_ACCESS_IOMEM(base);
|
||||
|
||||
spin_lock_init(&drvdata->spinlock);
|
||||
raw_spin_lock_init(&drvdata->spinlock);
|
||||
|
||||
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
|
||||
drvdata->config_type = BMVAL(devid, 6, 7);
|
||||
|
@ -508,6 +817,8 @@ static int __tmc_probe(struct device *dev, struct resource *res)
|
|||
drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4;
|
||||
}
|
||||
|
||||
tmc_get_reserved_region(dev);
|
||||
|
||||
desc.dev = dev;
|
||||
|
||||
switch (drvdata->config_type) {
|
||||
|
@ -568,9 +879,15 @@ static int __tmc_probe(struct device *dev, struct resource *res)
|
|||
drvdata->miscdev.minor = MISC_DYNAMIC_MINOR;
|
||||
drvdata->miscdev.fops = &tmc_fops;
|
||||
ret = misc_register(&drvdata->miscdev);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
coresight_unregister(drvdata->csdev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (is_tmc_crashdata_valid(drvdata) &&
|
||||
!tmc_prepare_crashdata(drvdata))
|
||||
register_crash_dev_interface(drvdata, desc.name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -596,7 +913,7 @@ static void tmc_shutdown(struct amba_device *adev)
|
|||
unsigned long flags;
|
||||
struct tmc_drvdata *drvdata = amba_get_drvdata(adev);
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
if (coresight_get_mode(drvdata->csdev) == CS_MODE_DISABLED)
|
||||
goto out;
|
||||
|
@ -610,7 +927,7 @@ static void tmc_shutdown(struct amba_device *adev)
|
|||
* the system is going down after this.
|
||||
*/
|
||||
out:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
}
|
||||
|
||||
static void __tmc_remove(struct device *dev)
|
||||
|
@ -623,6 +940,8 @@ static void __tmc_remove(struct device *dev)
|
|||
* handler to this device is closed.
|
||||
*/
|
||||
misc_deregister(&drvdata->miscdev);
|
||||
if (drvdata->crashdev.fops)
|
||||
misc_deregister(&drvdata->crashdev);
|
||||
coresight_unregister(drvdata->csdev);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ static int tmc_set_etf_buffer(struct coresight_device *csdev,
|
|||
static int __tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
int rc = 0;
|
||||
u32 ffcr;
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
|
@ -32,10 +33,12 @@ static int __tmc_etb_enable_hw(struct tmc_drvdata *drvdata)
|
|||
}
|
||||
|
||||
writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE);
|
||||
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
|
||||
TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
|
||||
TMC_FFCR_TRIGON_TRIGIN,
|
||||
drvdata->base + TMC_FFCR);
|
||||
|
||||
ffcr = TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | TMC_FFCR_FON_FLIN |
|
||||
TMC_FFCR_FON_TRIG_EVT | TMC_FFCR_TRIGON_TRIGIN;
|
||||
if (drvdata->stop_on_flush)
|
||||
ffcr |= TMC_FFCR_STOP_ON_FLUSH;
|
||||
writel_relaxed(ffcr, drvdata->base + TMC_FFCR);
|
||||
|
||||
writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
|
||||
tmc_enable_hw(drvdata);
|
||||
|
@ -182,9 +185,9 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
|
|||
* If we don't have a buffer release the lock and allocate memory.
|
||||
* Otherwise keep the lock and move along.
|
||||
*/
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (!drvdata->buf) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
/* Allocating the memory here while outside of the spinlock */
|
||||
buf = kzalloc(drvdata->size, GFP_KERNEL);
|
||||
|
@ -192,7 +195,7 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
|
|||
return -ENOMEM;
|
||||
|
||||
/* Let's try again */
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
}
|
||||
|
||||
if (drvdata->reading) {
|
||||
|
@ -225,7 +228,6 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
|
|||
used = true;
|
||||
drvdata->buf = buf;
|
||||
}
|
||||
|
||||
ret = tmc_etb_enable_hw(drvdata);
|
||||
if (!ret) {
|
||||
coresight_set_mode(csdev, CS_MODE_SYSFS);
|
||||
|
@ -235,7 +237,7 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
|
|||
used = false;
|
||||
}
|
||||
out:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
/* Free memory outside the spinlock if need be */
|
||||
if (!used)
|
||||
|
@ -253,7 +255,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data)
|
|||
struct perf_output_handle *handle = data;
|
||||
struct cs_buffers *buf = etm_perf_sink_config(handle);
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
do {
|
||||
ret = -EINVAL;
|
||||
if (drvdata->reading)
|
||||
|
@ -296,7 +298,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data)
|
|||
csdev->refcnt++;
|
||||
}
|
||||
} while (0);
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -331,16 +333,16 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev)
|
|||
unsigned long flags;
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
if (drvdata->reading) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
csdev->refcnt--;
|
||||
if (csdev->refcnt) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
@ -351,7 +353,7 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev)
|
|||
drvdata->pid = -1;
|
||||
coresight_set_mode(csdev, CS_MODE_DISABLED);
|
||||
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
dev_dbg(&csdev->dev, "TMC-ETB/ETF disabled\n");
|
||||
return 0;
|
||||
|
@ -366,9 +368,9 @@ static int tmc_enable_etf_link(struct coresight_device *csdev,
|
|||
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
bool first_enable = false;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (drvdata->reading) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
@ -381,7 +383,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev,
|
|||
}
|
||||
if (!ret)
|
||||
csdev->refcnt++;
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
if (first_enable)
|
||||
dev_dbg(&csdev->dev, "TMC-ETF enabled\n");
|
||||
|
@ -396,9 +398,9 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
|
|||
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
bool last_disable = false;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (drvdata->reading) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -408,7 +410,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
|
|||
coresight_set_mode(csdev, CS_MODE_DISABLED);
|
||||
last_disable = true;
|
||||
}
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
if (last_disable)
|
||||
dev_dbg(&csdev->dev, "TMC-ETF disabled\n");
|
||||
|
@ -488,7 +490,7 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev,
|
|||
if (WARN_ON_ONCE(coresight_get_mode(csdev) != CS_MODE_PERF))
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
/* Don't do anything if another tracer is using this sink */
|
||||
if (csdev->refcnt != 1)
|
||||
|
@ -585,11 +587,86 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev,
|
|||
*/
|
||||
CS_LOCK(drvdata->base);
|
||||
out:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
return to_read;
|
||||
}
|
||||
|
||||
static int tmc_panic_sync_etf(struct coresight_device *csdev)
|
||||
{
|
||||
u32 val;
|
||||
struct tmc_crash_metadata *mdata;
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
mdata = (struct tmc_crash_metadata *)drvdata->crash_mdata.vaddr;
|
||||
|
||||
/* Make sure we have valid reserved memory */
|
||||
if (!tmc_has_reserved_buffer(drvdata) ||
|
||||
!tmc_has_crash_mdata_buffer(drvdata))
|
||||
return 0;
|
||||
|
||||
tmc_crashdata_set_invalid(drvdata);
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
/* Proceed only if ETF is enabled or configured as sink */
|
||||
val = readl(drvdata->base + TMC_CTL);
|
||||
if (!(val & TMC_CTL_CAPT_EN))
|
||||
goto out;
|
||||
val = readl(drvdata->base + TMC_MODE);
|
||||
if (val != TMC_MODE_CIRCULAR_BUFFER)
|
||||
goto out;
|
||||
|
||||
val = readl(drvdata->base + TMC_FFSR);
|
||||
/* Do manual flush and stop only if its not auto-stopped */
|
||||
if (!(val & TMC_FFSR_FT_STOPPED)) {
|
||||
dev_dbg(&csdev->dev,
|
||||
"%s: Triggering manual flush\n", __func__);
|
||||
tmc_flush_and_stop(drvdata);
|
||||
} else
|
||||
tmc_wait_for_tmcready(drvdata);
|
||||
|
||||
/* Sync registers from hardware to metadata region */
|
||||
mdata->tmc_sts = readl(drvdata->base + TMC_STS);
|
||||
mdata->tmc_mode = readl(drvdata->base + TMC_MODE);
|
||||
mdata->tmc_ffcr = readl(drvdata->base + TMC_FFCR);
|
||||
mdata->tmc_ffsr = readl(drvdata->base + TMC_FFSR);
|
||||
|
||||
/* Sync Internal SRAM to reserved trace buffer region */
|
||||
drvdata->buf = drvdata->resrv_buf.vaddr;
|
||||
tmc_etb_dump_hw(drvdata);
|
||||
/* Store as per RSZ register convention */
|
||||
mdata->tmc_ram_size = drvdata->len >> 2;
|
||||
|
||||
/* Other fields for processing trace buffer reads */
|
||||
mdata->tmc_rrp = 0;
|
||||
mdata->tmc_dba = 0;
|
||||
mdata->tmc_rwp = drvdata->len;
|
||||
mdata->trace_paddr = drvdata->resrv_buf.paddr;
|
||||
|
||||
mdata->version = CS_CRASHDATA_VERSION;
|
||||
|
||||
/*
|
||||
* Make sure all previous writes are ordered,
|
||||
* before we mark valid
|
||||
*/
|
||||
dmb(sy);
|
||||
mdata->valid = true;
|
||||
/*
|
||||
* Below order need to maintained, since crc of metadata
|
||||
* is dependent on first
|
||||
*/
|
||||
mdata->crc32_tdata = find_crash_tracedata_crc(drvdata, mdata);
|
||||
mdata->crc32_mdata = find_crash_metadata_crc(mdata);
|
||||
|
||||
tmc_disable_hw(drvdata);
|
||||
|
||||
dev_dbg(&csdev->dev, "%s: success\n", __func__);
|
||||
out:
|
||||
CS_UNLOCK(drvdata->base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct coresight_ops_sink tmc_etf_sink_ops = {
|
||||
.enable = tmc_enable_etf_sink,
|
||||
.disable = tmc_disable_etf_sink,
|
||||
|
@ -603,6 +680,10 @@ static const struct coresight_ops_link tmc_etf_link_ops = {
|
|||
.disable = tmc_disable_etf_link,
|
||||
};
|
||||
|
||||
static const struct coresight_ops_panic tmc_etf_sync_ops = {
|
||||
.sync = tmc_panic_sync_etf,
|
||||
};
|
||||
|
||||
const struct coresight_ops tmc_etb_cs_ops = {
|
||||
.sink_ops = &tmc_etf_sink_ops,
|
||||
};
|
||||
|
@ -610,6 +691,7 @@ const struct coresight_ops tmc_etb_cs_ops = {
|
|||
const struct coresight_ops tmc_etf_cs_ops = {
|
||||
.sink_ops = &tmc_etf_sink_ops,
|
||||
.link_ops = &tmc_etf_link_ops,
|
||||
.panic_ops = &tmc_etf_sync_ops,
|
||||
};
|
||||
|
||||
int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
|
||||
|
@ -623,7 +705,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
|
|||
drvdata->config_type != TMC_CONFIG_TYPE_ETF))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
if (drvdata->reading) {
|
||||
ret = -EBUSY;
|
||||
|
@ -655,7 +737,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
|
|||
|
||||
drvdata->reading = true;
|
||||
out:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -672,14 +754,14 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
|
|||
drvdata->config_type != TMC_CONFIG_TYPE_ETF))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
/* Re-enable the TMC if need be */
|
||||
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
|
||||
/* There is no point in reading a TMC in HW FIFO mode */
|
||||
mode = readl_relaxed(drvdata->base + TMC_MODE);
|
||||
if (mode != TMC_MODE_CIRCULAR_BUFFER) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
|
@ -693,7 +775,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
|
|||
memset(drvdata->buf, 0, drvdata->size);
|
||||
rc = __tmc_etb_enable_hw(drvdata);
|
||||
if (rc) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
|
@ -706,7 +788,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
|
|||
}
|
||||
|
||||
drvdata->reading = false;
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
/*
|
||||
* Free allocated memory outside of the spinlock. There is no need
|
||||
|
|
|
@ -30,6 +30,7 @@ struct etr_buf_hw {
|
|||
bool has_iommu;
|
||||
bool has_etr_sg;
|
||||
bool has_catu;
|
||||
bool has_resrv;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -695,6 +696,75 @@ static const struct etr_buf_operations etr_flat_buf_ops = {
|
|||
.get_data = tmc_etr_get_data_flat_buf,
|
||||
};
|
||||
|
||||
/*
|
||||
* tmc_etr_alloc_resrv_buf: Allocate a contiguous DMA buffer from reserved region.
|
||||
*/
|
||||
static int tmc_etr_alloc_resrv_buf(struct tmc_drvdata *drvdata,
|
||||
struct etr_buf *etr_buf, int node,
|
||||
void **pages)
|
||||
{
|
||||
struct etr_flat_buf *resrv_buf;
|
||||
struct device *real_dev = drvdata->csdev->dev.parent;
|
||||
|
||||
/* We cannot reuse existing pages for resrv buf */
|
||||
if (pages)
|
||||
return -EINVAL;
|
||||
|
||||
resrv_buf = kzalloc(sizeof(*resrv_buf), GFP_KERNEL);
|
||||
if (!resrv_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
resrv_buf->daddr = dma_map_resource(real_dev, drvdata->resrv_buf.paddr,
|
||||
drvdata->resrv_buf.size,
|
||||
DMA_FROM_DEVICE, 0);
|
||||
if (dma_mapping_error(real_dev, resrv_buf->daddr)) {
|
||||
dev_err(real_dev, "failed to map source buffer address\n");
|
||||
kfree(resrv_buf);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
resrv_buf->vaddr = drvdata->resrv_buf.vaddr;
|
||||
resrv_buf->size = etr_buf->size = drvdata->resrv_buf.size;
|
||||
resrv_buf->dev = &drvdata->csdev->dev;
|
||||
etr_buf->hwaddr = resrv_buf->daddr;
|
||||
etr_buf->mode = ETR_MODE_RESRV;
|
||||
etr_buf->private = resrv_buf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tmc_etr_free_resrv_buf(struct etr_buf *etr_buf)
|
||||
{
|
||||
struct etr_flat_buf *resrv_buf = etr_buf->private;
|
||||
|
||||
if (resrv_buf && resrv_buf->daddr) {
|
||||
struct device *real_dev = resrv_buf->dev->parent;
|
||||
|
||||
dma_unmap_resource(real_dev, resrv_buf->daddr,
|
||||
resrv_buf->size, DMA_FROM_DEVICE, 0);
|
||||
}
|
||||
kfree(resrv_buf);
|
||||
}
|
||||
|
||||
static void tmc_etr_sync_resrv_buf(struct etr_buf *etr_buf, u64 rrp, u64 rwp)
|
||||
{
|
||||
/*
|
||||
* Adjust the buffer to point to the beginning of the trace data
|
||||
* and update the available trace data.
|
||||
*/
|
||||
etr_buf->offset = rrp - etr_buf->hwaddr;
|
||||
if (etr_buf->full)
|
||||
etr_buf->len = etr_buf->size;
|
||||
else
|
||||
etr_buf->len = rwp - rrp;
|
||||
}
|
||||
|
||||
static const struct etr_buf_operations etr_resrv_buf_ops = {
|
||||
.alloc = tmc_etr_alloc_resrv_buf,
|
||||
.free = tmc_etr_free_resrv_buf,
|
||||
.sync = tmc_etr_sync_resrv_buf,
|
||||
.get_data = tmc_etr_get_data_flat_buf,
|
||||
};
|
||||
|
||||
/*
|
||||
* tmc_etr_alloc_sg_buf: Allocate an SG buf @etr_buf. Setup the parameters
|
||||
* appropriately.
|
||||
|
@ -801,6 +871,7 @@ 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] = NULL,
|
||||
[ETR_MODE_RESRV] = &etr_resrv_buf_ops
|
||||
};
|
||||
|
||||
void tmc_etr_set_catu_ops(const struct etr_buf_operations *catu)
|
||||
|
@ -826,6 +897,7 @@ static inline int tmc_etr_mode_alloc_buf(int mode,
|
|||
case ETR_MODE_FLAT:
|
||||
case ETR_MODE_ETR_SG:
|
||||
case ETR_MODE_CATU:
|
||||
case ETR_MODE_RESRV:
|
||||
if (etr_buf_ops[mode] && etr_buf_ops[mode]->alloc)
|
||||
rc = etr_buf_ops[mode]->alloc(drvdata, etr_buf,
|
||||
node, pages);
|
||||
|
@ -844,6 +916,7 @@ static void get_etr_buf_hw(struct device *dev, struct etr_buf_hw *buf_hw)
|
|||
buf_hw->has_iommu = iommu_get_domain_for_dev(dev->parent);
|
||||
buf_hw->has_etr_sg = tmc_etr_has_cap(drvdata, TMC_ETR_SG);
|
||||
buf_hw->has_catu = !!tmc_etr_get_catu_device(drvdata);
|
||||
buf_hw->has_resrv = tmc_has_reserved_buffer(drvdata);
|
||||
}
|
||||
|
||||
static bool etr_can_use_flat_mode(struct etr_buf_hw *buf_hw, ssize_t etr_buf_size)
|
||||
|
@ -987,7 +1060,7 @@ static void tmc_sync_etr_buf(struct tmc_drvdata *drvdata)
|
|||
|
||||
static int __tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
u32 axictl, sts;
|
||||
u32 axictl, sts, ffcr;
|
||||
struct etr_buf *etr_buf = drvdata->etr_buf;
|
||||
int rc = 0;
|
||||
|
||||
|
@ -1033,10 +1106,12 @@ static int __tmc_etr_enable_hw(struct tmc_drvdata *drvdata)
|
|||
writel_relaxed(sts, drvdata->base + TMC_STS);
|
||||
}
|
||||
|
||||
writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI |
|
||||
TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT |
|
||||
TMC_FFCR_TRIGON_TRIGIN,
|
||||
drvdata->base + TMC_FFCR);
|
||||
ffcr = TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | TMC_FFCR_FON_FLIN |
|
||||
TMC_FFCR_FON_TRIG_EVT | TMC_FFCR_TRIGON_TRIGIN;
|
||||
if (drvdata->stop_on_flush)
|
||||
ffcr |= TMC_FFCR_STOP_ON_FLUSH;
|
||||
writel_relaxed(ffcr, drvdata->base + TMC_FFCR);
|
||||
|
||||
writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG);
|
||||
tmc_enable_hw(drvdata);
|
||||
|
||||
|
@ -1176,10 +1251,10 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
|
|||
* buffer, provided the size matches. Any allocation has to be done
|
||||
* with the lock released.
|
||||
*/
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
sysfs_buf = READ_ONCE(drvdata->sysfs_buf);
|
||||
if (!sysfs_buf || (sysfs_buf->size != drvdata->size)) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
/* Allocate memory with the locks released */
|
||||
free_buf = new_buf = tmc_etr_setup_sysfs_buf(drvdata);
|
||||
|
@ -1187,7 +1262,7 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
|
|||
return new_buf;
|
||||
|
||||
/* Let's try again */
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
}
|
||||
|
||||
if (drvdata->reading || coresight_get_mode(csdev) == CS_MODE_PERF) {
|
||||
|
@ -1206,7 +1281,7 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
|
|||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
/* Free memory outside the spinlock if need be */
|
||||
if (free_buf)
|
||||
|
@ -1224,7 +1299,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
|
|||
if (IS_ERR(sysfs_buf))
|
||||
return PTR_ERR(sysfs_buf);
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
/*
|
||||
* In sysFS mode we can have multiple writers per sink. Since this
|
||||
|
@ -1243,7 +1318,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
|
|||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
if (!ret)
|
||||
dev_dbg(&csdev->dev, "TMC-ETR enabled\n");
|
||||
|
@ -1562,17 +1637,17 @@ tmc_update_etr_buffer(struct coresight_device *csdev,
|
|||
struct etr_perf_buffer *etr_perf = config;
|
||||
struct etr_buf *etr_buf = etr_perf->etr_buf;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
/* Don't do anything if another tracer is using this sink */
|
||||
if (csdev->refcnt != 1) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WARN_ON(drvdata->perf_buf != etr_buf)) {
|
||||
lost = true;
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -1582,7 +1657,7 @@ tmc_update_etr_buffer(struct coresight_device *csdev,
|
|||
tmc_sync_etr_buf(drvdata);
|
||||
|
||||
CS_LOCK(drvdata->base);
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
lost = etr_buf->full;
|
||||
offset = etr_buf->offset;
|
||||
|
@ -1651,7 +1726,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
|
|||
struct perf_output_handle *handle = data;
|
||||
struct etr_perf_buffer *etr_perf = etm_perf_sink_config(handle);
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
/* Don't use this sink if it is already claimed by sysFS */
|
||||
if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
|
||||
rc = -EBUSY;
|
||||
|
@ -1691,7 +1766,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
|
|||
}
|
||||
|
||||
unlock_out:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1713,16 +1788,16 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev)
|
|||
unsigned long flags;
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
if (drvdata->reading) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
csdev->refcnt--;
|
||||
if (csdev->refcnt) {
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
|
@ -1735,12 +1810,80 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev)
|
|||
/* Reset perf specific data */
|
||||
drvdata->perf_buf = NULL;
|
||||
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
dev_dbg(&csdev->dev, "TMC-ETR disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmc_panic_sync_etr(struct coresight_device *csdev)
|
||||
{
|
||||
u32 val;
|
||||
struct tmc_crash_metadata *mdata;
|
||||
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
mdata = (struct tmc_crash_metadata *)drvdata->crash_mdata.vaddr;
|
||||
|
||||
if (!drvdata->etr_buf)
|
||||
return 0;
|
||||
|
||||
/* Being in RESRV mode implies valid reserved memory as well */
|
||||
if (drvdata->etr_buf->mode != ETR_MODE_RESRV)
|
||||
return 0;
|
||||
|
||||
if (!tmc_has_crash_mdata_buffer(drvdata))
|
||||
return 0;
|
||||
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
/* Proceed only if ETR is enabled */
|
||||
val = readl(drvdata->base + TMC_CTL);
|
||||
if (!(val & TMC_CTL_CAPT_EN))
|
||||
goto out;
|
||||
|
||||
val = readl(drvdata->base + TMC_FFSR);
|
||||
/* Do manual flush and stop only if its not auto-stopped */
|
||||
if (!(val & TMC_FFSR_FT_STOPPED)) {
|
||||
dev_dbg(&csdev->dev,
|
||||
"%s: Triggering manual flush\n", __func__);
|
||||
tmc_flush_and_stop(drvdata);
|
||||
} else
|
||||
tmc_wait_for_tmcready(drvdata);
|
||||
|
||||
/* Sync registers from hardware to metadata region */
|
||||
mdata->tmc_ram_size = readl(drvdata->base + TMC_RSZ);
|
||||
mdata->tmc_sts = readl(drvdata->base + TMC_STS);
|
||||
mdata->tmc_mode = readl(drvdata->base + TMC_MODE);
|
||||
mdata->tmc_ffcr = readl(drvdata->base + TMC_FFCR);
|
||||
mdata->tmc_ffsr = readl(drvdata->base + TMC_FFSR);
|
||||
mdata->tmc_rrp = tmc_read_rrp(drvdata);
|
||||
mdata->tmc_rwp = tmc_read_rwp(drvdata);
|
||||
mdata->tmc_dba = tmc_read_dba(drvdata);
|
||||
mdata->trace_paddr = drvdata->resrv_buf.paddr;
|
||||
mdata->version = CS_CRASHDATA_VERSION;
|
||||
|
||||
/*
|
||||
* Make sure all previous writes are ordered,
|
||||
* before we mark valid
|
||||
*/
|
||||
dmb(sy);
|
||||
mdata->valid = true;
|
||||
/*
|
||||
* Below order need to maintained, since crc of metadata
|
||||
* is dependent on first
|
||||
*/
|
||||
mdata->crc32_tdata = find_crash_tracedata_crc(drvdata, mdata);
|
||||
mdata->crc32_mdata = find_crash_metadata_crc(mdata);
|
||||
|
||||
tmc_disable_hw(drvdata);
|
||||
|
||||
dev_dbg(&csdev->dev, "%s: success\n", __func__);
|
||||
out:
|
||||
CS_UNLOCK(drvdata->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct coresight_ops_sink tmc_etr_sink_ops = {
|
||||
.enable = tmc_enable_etr_sink,
|
||||
.disable = tmc_disable_etr_sink,
|
||||
|
@ -1749,8 +1892,13 @@ static const struct coresight_ops_sink tmc_etr_sink_ops = {
|
|||
.free_buffer = tmc_free_etr_buffer,
|
||||
};
|
||||
|
||||
static const struct coresight_ops_panic tmc_etr_sync_ops = {
|
||||
.sync = tmc_panic_sync_etr,
|
||||
};
|
||||
|
||||
const struct coresight_ops tmc_etr_cs_ops = {
|
||||
.sink_ops = &tmc_etr_sink_ops,
|
||||
.panic_ops = &tmc_etr_sync_ops,
|
||||
};
|
||||
|
||||
int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
|
||||
|
@ -1762,7 +1910,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
|
|||
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (drvdata->reading) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
|
@ -1784,7 +1932,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
|
|||
|
||||
drvdata->reading = true;
|
||||
out:
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1798,7 +1946,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
|
|||
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
|
||||
/* RE-enable the TMC if need be */
|
||||
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
|
||||
|
@ -1818,7 +1966,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
|
|||
}
|
||||
|
||||
drvdata->reading = false;
|
||||
spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
|
||||
/* Free allocated memory out side of the spinlock */
|
||||
if (sysfs_buf)
|
||||
|
@ -1831,6 +1979,7 @@ static const char *const buf_modes_str[] = {
|
|||
[ETR_MODE_FLAT] = "flat",
|
||||
[ETR_MODE_ETR_SG] = "tmc-sg",
|
||||
[ETR_MODE_CATU] = "catu",
|
||||
[ETR_MODE_RESRV] = "resrv",
|
||||
[ETR_MODE_AUTO] = "auto",
|
||||
};
|
||||
|
||||
|
@ -1849,6 +1998,9 @@ static ssize_t buf_modes_available_show(struct device *dev,
|
|||
if (buf_hw.has_catu)
|
||||
size += sysfs_emit_at(buf, size, "%s ", buf_modes_str[ETR_MODE_CATU]);
|
||||
|
||||
if (buf_hw.has_resrv)
|
||||
size += sysfs_emit_at(buf, size, "%s ", buf_modes_str[ETR_MODE_RESRV]);
|
||||
|
||||
size += sysfs_emit_at(buf, size, "\n");
|
||||
return size;
|
||||
}
|
||||
|
@ -1862,6 +2014,26 @@ static ssize_t buf_mode_preferred_show(struct device *dev,
|
|||
return sysfs_emit(buf, "%s\n", buf_modes_str[drvdata->etr_mode]);
|
||||
}
|
||||
|
||||
static int buf_mode_set_resrv(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
int err = -EBUSY;
|
||||
unsigned long flags;
|
||||
struct tmc_resrv_buf *rbuf;
|
||||
|
||||
rbuf = &drvdata->resrv_buf;
|
||||
|
||||
/* Ensure there are no active crashdata read sessions */
|
||||
raw_spin_lock_irqsave(&drvdata->spinlock, flags);
|
||||
if (!rbuf->reading) {
|
||||
tmc_crashdata_set_invalid(drvdata);
|
||||
rbuf->len = 0;
|
||||
drvdata->etr_mode = ETR_MODE_RESRV;
|
||||
err = 0;
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&drvdata->spinlock, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t buf_mode_preferred_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t size)
|
||||
|
@ -1876,6 +2048,8 @@ static ssize_t buf_mode_preferred_store(struct device *dev,
|
|||
drvdata->etr_mode = ETR_MODE_ETR_SG;
|
||||
else if (sysfs_streq(buf, buf_modes_str[ETR_MODE_CATU]) && buf_hw.has_catu)
|
||||
drvdata->etr_mode = ETR_MODE_CATU;
|
||||
else if (sysfs_streq(buf, buf_modes_str[ETR_MODE_RESRV]) && buf_hw.has_resrv)
|
||||
return buf_mode_set_resrv(drvdata) ? : size;
|
||||
else if (sysfs_streq(buf, buf_modes_str[ETR_MODE_AUTO]))
|
||||
drvdata->etr_mode = ETR_MODE_AUTO;
|
||||
else
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/miscdevice.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/crc32.h>
|
||||
|
||||
#define TMC_RSZ 0x004
|
||||
#define TMC_STS 0x00c
|
||||
|
@ -76,6 +77,9 @@
|
|||
#define TMC_AXICTL_AXCACHE_OS (0xf << 2)
|
||||
#define TMC_AXICTL_ARCACHE_OS (0xf << 16)
|
||||
|
||||
/* TMC_FFSR - 0x300 */
|
||||
#define TMC_FFSR_FT_STOPPED BIT(1)
|
||||
|
||||
/* TMC_FFCR - 0x304 */
|
||||
#define TMC_FFCR_FLUSHMAN_BIT 6
|
||||
#define TMC_FFCR_EN_FMT BIT(0)
|
||||
|
@ -94,6 +98,9 @@
|
|||
|
||||
#define TMC_AUTH_NSID_MASK GENMASK(1, 0)
|
||||
|
||||
/* Major version 1 Minor version 0 */
|
||||
#define CS_CRASHDATA_VERSION (1 << 16)
|
||||
|
||||
enum tmc_config_type {
|
||||
TMC_CONFIG_TYPE_ETB,
|
||||
TMC_CONFIG_TYPE_ETR,
|
||||
|
@ -131,10 +138,30 @@ enum tmc_mem_intf_width {
|
|||
#define CORESIGHT_SOC_600_ETR_CAPS \
|
||||
(TMC_ETR_SAVE_RESTORE | TMC_ETR_AXI_ARCACHE)
|
||||
|
||||
/* TMC metadata region for ETR and ETF configurations */
|
||||
struct tmc_crash_metadata {
|
||||
uint32_t crc32_mdata; /* crc of metadata */
|
||||
uint32_t crc32_tdata; /* crc of tracedata */
|
||||
uint32_t version; /* 31:16 Major version, 15:0 Minor version */
|
||||
uint32_t valid; /* Indicate if this ETF/ETR was enabled */
|
||||
uint32_t tmc_ram_size; /* Ram Size register */
|
||||
uint32_t tmc_sts; /* Status register */
|
||||
uint32_t tmc_mode; /* Mode register */
|
||||
uint32_t tmc_ffcr; /* Formatter and flush control register */
|
||||
uint32_t tmc_ffsr; /* Formatter and flush status register */
|
||||
uint32_t reserved32;
|
||||
uint64_t tmc_rrp; /* Ram Read pointer register */
|
||||
uint64_t tmc_rwp; /* Ram Write pointer register */
|
||||
uint64_t tmc_dba; /* Data buffer address register */
|
||||
uint64_t trace_paddr; /* Phys address of trace buffer */
|
||||
uint64_t reserved64[3];
|
||||
};
|
||||
|
||||
enum etr_mode {
|
||||
ETR_MODE_FLAT, /* Uses contiguous flat buffer */
|
||||
ETR_MODE_ETR_SG, /* Uses in-built TMC ETR SG mechanism */
|
||||
ETR_MODE_CATU, /* Use SG mechanism in CATU */
|
||||
ETR_MODE_RESRV, /* Use reserved region contiguous buffer */
|
||||
ETR_MODE_AUTO, /* Use the default mechanism */
|
||||
};
|
||||
|
||||
|
@ -164,16 +191,36 @@ struct etr_buf {
|
|||
void *private;
|
||||
};
|
||||
|
||||
/**
|
||||
* @paddr : Start address of reserved memory region.
|
||||
* @vaddr : Corresponding CPU virtual address.
|
||||
* @size : Size of reserved memory region.
|
||||
* @offset : Offset of the trace data in the buffer for consumption.
|
||||
* @reading : Flag to indicate if reading is active
|
||||
* @len : Available trace data @buf (may round up to the beginning).
|
||||
*/
|
||||
struct tmc_resrv_buf {
|
||||
phys_addr_t paddr;
|
||||
void *vaddr;
|
||||
size_t size;
|
||||
unsigned long offset;
|
||||
bool reading;
|
||||
s64 len;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tmc_drvdata - specifics associated to an TMC component
|
||||
* @pclk: APB clock if present, otherwise NULL
|
||||
* @base: memory mapped base address for this component.
|
||||
* @csdev: component vitals needed by the framework.
|
||||
* @miscdev: specifics to handle "/dev/xyz.tmc" entry.
|
||||
* @crashdev: specifics to handle "/dev/crash_tmc_xyz" entry for reading
|
||||
* crash tracedata.
|
||||
* @spinlock: only one at a time pls.
|
||||
* @pid: Process ID of the process that owns the session that is using
|
||||
* this component. For example this would be the pid of the Perf
|
||||
* process.
|
||||
* @stop_on_flush: Stop on flush trigger user configuration.
|
||||
* @buf: Snapshot of the trace data for ETF/ETB.
|
||||
* @etr_buf: details of buffer used in TMC-ETR
|
||||
* @len: size of the available trace for ETF/ETB.
|
||||
|
@ -189,15 +236,23 @@ struct etr_buf {
|
|||
* @idr_mutex: Access serialisation for idr.
|
||||
* @sysfs_buf: SYSFS buffer for ETR.
|
||||
* @perf_buf: PERF buffer for ETR.
|
||||
* @resrv_buf: Used by ETR as hardware trace buffer and for trace data
|
||||
* retention (after crash) only when ETR_MODE_RESRV buffer
|
||||
* mode is enabled. Used by ETF for trace data retention
|
||||
* (after crash) by default.
|
||||
* @crash_mdata: Reserved memory for storing tmc crash metadata.
|
||||
* Used by ETR/ETF.
|
||||
*/
|
||||
struct tmc_drvdata {
|
||||
struct clk *pclk;
|
||||
void __iomem *base;
|
||||
struct coresight_device *csdev;
|
||||
struct miscdevice miscdev;
|
||||
spinlock_t spinlock;
|
||||
struct miscdevice crashdev;
|
||||
raw_spinlock_t spinlock;
|
||||
pid_t pid;
|
||||
bool reading;
|
||||
bool stop_on_flush;
|
||||
union {
|
||||
char *buf; /* TMC ETB */
|
||||
struct etr_buf *etr_buf; /* TMC ETR */
|
||||
|
@ -214,6 +269,8 @@ struct tmc_drvdata {
|
|||
struct mutex idr_mutex;
|
||||
struct etr_buf *sysfs_buf;
|
||||
struct etr_buf *perf_buf;
|
||||
struct tmc_resrv_buf resrv_buf;
|
||||
struct tmc_resrv_buf crash_mdata;
|
||||
};
|
||||
|
||||
struct etr_buf_operations {
|
||||
|
@ -263,6 +320,7 @@ void tmc_flush_and_stop(struct tmc_drvdata *drvdata);
|
|||
void tmc_enable_hw(struct tmc_drvdata *drvdata);
|
||||
void tmc_disable_hw(struct tmc_drvdata *drvdata);
|
||||
u32 tmc_get_memwidth_mask(struct tmc_drvdata *drvdata);
|
||||
int tmc_read_prepare_crashdata(struct tmc_drvdata *drvdata);
|
||||
|
||||
/* ETB/ETF functions */
|
||||
int tmc_read_prepare_etb(struct tmc_drvdata *drvdata);
|
||||
|
@ -325,12 +383,58 @@ void tmc_sg_table_sync_data_range(struct tmc_sg_table *table,
|
|||
u64 offset, u64 size);
|
||||
ssize_t tmc_sg_table_get_data(struct tmc_sg_table *sg_table,
|
||||
u64 offset, size_t len, char **bufpp);
|
||||
|
||||
static inline unsigned long
|
||||
tmc_sg_table_buf_size(struct tmc_sg_table *sg_table)
|
||||
{
|
||||
return (unsigned long)sg_table->data_pages.nr_pages << PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static inline bool tmc_has_reserved_buffer(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
if (drvdata->resrv_buf.vaddr &&
|
||||
drvdata->resrv_buf.size)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool tmc_has_crash_mdata_buffer(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
if (drvdata->crash_mdata.vaddr &&
|
||||
drvdata->crash_mdata.size)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void tmc_crashdata_set_invalid(struct tmc_drvdata *drvdata)
|
||||
{
|
||||
struct tmc_crash_metadata *mdata;
|
||||
|
||||
mdata = (struct tmc_crash_metadata *)drvdata->crash_mdata.vaddr;
|
||||
|
||||
if (tmc_has_crash_mdata_buffer(drvdata))
|
||||
mdata->valid = false;
|
||||
}
|
||||
|
||||
static inline uint32_t find_crash_metadata_crc(struct tmc_crash_metadata *md)
|
||||
{
|
||||
unsigned long crc_size;
|
||||
|
||||
crc_size = sizeof(struct tmc_crash_metadata) -
|
||||
offsetof(struct tmc_crash_metadata, crc32_tdata);
|
||||
return crc32_le(0, (void *)&md->crc32_tdata, crc_size);
|
||||
}
|
||||
|
||||
static inline uint32_t find_crash_tracedata_crc(struct tmc_drvdata *drvdata,
|
||||
struct tmc_crash_metadata *md)
|
||||
{
|
||||
unsigned long crc_size;
|
||||
|
||||
/* Take CRC of configured buffer size to keep it simple */
|
||||
crc_size = md->tmc_ram_size << 2;
|
||||
return crc32_le(0, (void *)drvdata->resrv_buf.vaddr, crc_size);
|
||||
}
|
||||
|
||||
struct coresight_device *tmc_etr_get_catu_device(struct tmc_drvdata *drvdata);
|
||||
|
||||
void tmc_etr_set_catu_ops(const struct etr_buf_operations *catu);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/amba/bus.h>
|
||||
|
@ -68,11 +68,12 @@ static int tpdm_read_element_size(struct tpda_drvdata *drvdata,
|
|||
int rc = -EINVAL;
|
||||
struct tpdm_drvdata *tpdm_data = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
if (tpdm_has_dsb_dataset(tpdm_data)) {
|
||||
if (tpdm_data->dsb) {
|
||||
rc = fwnode_property_read_u32(dev_fwnode(csdev->dev.parent),
|
||||
"qcom,dsb-element-bits", &drvdata->dsb_esize);
|
||||
}
|
||||
if (tpdm_has_cmb_dataset(tpdm_data)) {
|
||||
|
||||
if (tpdm_data->cmb) {
|
||||
rc = fwnode_property_read_u32(dev_fwnode(csdev->dev.parent),
|
||||
"qcom,cmb-element-bits", &drvdata->cmb_esize);
|
||||
}
|
||||
|
@ -241,12 +242,23 @@ static void tpda_disable(struct coresight_device *csdev,
|
|||
dev_dbg(drvdata->dev, "TPDA inport %d disabled\n", in->dest_port);
|
||||
}
|
||||
|
||||
static int tpda_trace_id(struct coresight_device *csdev, __maybe_unused enum cs_mode mode,
|
||||
__maybe_unused struct coresight_device *sink)
|
||||
{
|
||||
struct tpda_drvdata *drvdata;
|
||||
|
||||
drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
return drvdata->atid;
|
||||
}
|
||||
|
||||
static const struct coresight_ops_link tpda_link_ops = {
|
||||
.enable = tpda_enable,
|
||||
.disable = tpda_disable,
|
||||
};
|
||||
|
||||
static const struct coresight_ops tpda_cs_ops = {
|
||||
.trace_id = tpda_trace_id,
|
||||
.link_ops = &tpda_link_ops,
|
||||
};
|
||||
|
||||
|
@ -331,7 +343,7 @@ static void tpda_remove(struct amba_device *adev)
|
|||
* Different TPDA has different periph id.
|
||||
* The difference is 0-7 bits' value. So ignore 0-7 bits.
|
||||
*/
|
||||
static struct amba_id tpda_ids[] = {
|
||||
static const struct amba_id tpda_ids[] = {
|
||||
{
|
||||
.id = 0x000f0f00,
|
||||
.mask = 0x000fff00,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/amba/bus.h>
|
||||
|
@ -21,6 +21,21 @@
|
|||
|
||||
DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm");
|
||||
|
||||
static bool tpdm_has_dsb_dataset(struct tpdm_drvdata *drvdata)
|
||||
{
|
||||
return (drvdata->datasets & TPDM_PIDR0_DS_DSB);
|
||||
}
|
||||
|
||||
static bool tpdm_has_cmb_dataset(struct tpdm_drvdata *drvdata)
|
||||
{
|
||||
return (drvdata->datasets & TPDM_PIDR0_DS_CMB);
|
||||
}
|
||||
|
||||
static bool tpdm_has_mcmb_dataset(struct tpdm_drvdata *drvdata)
|
||||
{
|
||||
return (drvdata->datasets & TPDM_PIDR0_DS_MCMB);
|
||||
}
|
||||
|
||||
/* Read dataset array member with the index number */
|
||||
static ssize_t tpdm_simple_dataset_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
|
@ -198,7 +213,7 @@ static umode_t tpdm_cmb_is_visible(struct kobject *kobj,
|
|||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
if (drvdata && tpdm_has_cmb_dataset(drvdata))
|
||||
if (drvdata && drvdata->cmb)
|
||||
return attr->mode;
|
||||
|
||||
return 0;
|
||||
|
@ -237,6 +252,18 @@ static umode_t tpdm_cmb_msr_is_visible(struct kobject *kobj,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static umode_t tpdm_mcmb_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
if (drvdata && tpdm_has_mcmb_dataset(drvdata))
|
||||
return attr->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tpdm_reset_datasets(struct tpdm_drvdata *drvdata)
|
||||
{
|
||||
if (tpdm_has_dsb_dataset(drvdata)) {
|
||||
|
@ -388,7 +415,7 @@ static void tpdm_enable_cmb(struct tpdm_drvdata *drvdata)
|
|||
{
|
||||
u32 val, i;
|
||||
|
||||
if (!tpdm_has_cmb_dataset(drvdata))
|
||||
if (!drvdata->cmb)
|
||||
return;
|
||||
|
||||
/* Configure pattern registers */
|
||||
|
@ -415,6 +442,19 @@ static void tpdm_enable_cmb(struct tpdm_drvdata *drvdata)
|
|||
val |= TPDM_CMB_CR_MODE;
|
||||
else
|
||||
val &= ~TPDM_CMB_CR_MODE;
|
||||
|
||||
if (tpdm_has_mcmb_dataset(drvdata)) {
|
||||
val &= ~TPDM_CMB_CR_XTRIG_LNSEL;
|
||||
/* Set the lane participates in the output pattern */
|
||||
val |= FIELD_PREP(TPDM_CMB_CR_XTRIG_LNSEL,
|
||||
drvdata->cmb->mcmb.trig_lane);
|
||||
|
||||
/* Set the enablement of the lane */
|
||||
val &= ~TPDM_CMB_CR_E_LN;
|
||||
val |= FIELD_PREP(TPDM_CMB_CR_E_LN,
|
||||
drvdata->cmb->mcmb.lane_select);
|
||||
}
|
||||
|
||||
/* Set the enable bit of CMB control register to 1 */
|
||||
val |= TPDM_CMB_CR_ENA;
|
||||
writel_relaxed(val, drvdata->base + TPDM_CMB_CR);
|
||||
|
@ -440,7 +480,7 @@ static void __tpdm_enable(struct tpdm_drvdata *drvdata)
|
|||
|
||||
static int tpdm_enable(struct coresight_device *csdev, struct perf_event *event,
|
||||
enum cs_mode mode,
|
||||
__maybe_unused struct coresight_trace_id_map *id_map)
|
||||
__maybe_unused struct coresight_path *path)
|
||||
{
|
||||
struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
|
@ -480,7 +520,7 @@ static void tpdm_disable_cmb(struct tpdm_drvdata *drvdata)
|
|||
{
|
||||
u32 val;
|
||||
|
||||
if (!tpdm_has_cmb_dataset(drvdata))
|
||||
if (!drvdata->cmb)
|
||||
return;
|
||||
|
||||
val = readl_relaxed(drvdata->base + TPDM_CMB_CR);
|
||||
|
@ -542,12 +582,14 @@ static int tpdm_datasets_setup(struct tpdm_drvdata *drvdata)
|
|||
if (!drvdata->dsb)
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (tpdm_has_cmb_dataset(drvdata) && (!drvdata->cmb)) {
|
||||
if ((tpdm_has_cmb_dataset(drvdata) || tpdm_has_mcmb_dataset(drvdata))
|
||||
&& (!drvdata->cmb)) {
|
||||
drvdata->cmb = devm_kzalloc(drvdata->dev,
|
||||
sizeof(*drvdata->cmb), GFP_KERNEL);
|
||||
if (!drvdata->cmb)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
tpdm_reset_datasets(drvdata);
|
||||
|
||||
return 0;
|
||||
|
@ -990,6 +1032,62 @@ static ssize_t cmb_trig_ts_store(struct device *dev,
|
|||
}
|
||||
static DEVICE_ATTR_RW(cmb_trig_ts);
|
||||
|
||||
static ssize_t mcmb_trig_lane_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
return sysfs_emit(buf, "%u\n",
|
||||
(unsigned int)drvdata->cmb->mcmb.trig_lane);
|
||||
}
|
||||
|
||||
static ssize_t mcmb_trig_lane_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t size)
|
||||
{
|
||||
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
unsigned long val;
|
||||
|
||||
if ((kstrtoul(buf, 0, &val)) || (val >= TPDM_MCMB_MAX_LANES))
|
||||
return -EINVAL;
|
||||
|
||||
guard(spinlock)(&drvdata->spinlock);
|
||||
drvdata->cmb->mcmb.trig_lane = val;
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(mcmb_trig_lane);
|
||||
|
||||
static ssize_t mcmb_lanes_select_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
|
||||
return sysfs_emit(buf, "%u\n",
|
||||
(unsigned int)drvdata->cmb->mcmb.lane_select);
|
||||
}
|
||||
|
||||
static ssize_t mcmb_lanes_select_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t size)
|
||||
{
|
||||
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
|
||||
unsigned long val;
|
||||
|
||||
if (kstrtoul(buf, 0, &val) || (val & ~TPDM_MCMB_E_LN_MASK))
|
||||
return -EINVAL;
|
||||
|
||||
guard(spinlock)(&drvdata->spinlock);
|
||||
drvdata->cmb->mcmb.lane_select = val & TPDM_MCMB_E_LN_MASK;
|
||||
|
||||
return size;
|
||||
}
|
||||
static DEVICE_ATTR_RW(mcmb_lanes_select);
|
||||
|
||||
static struct attribute *tpdm_dsb_edge_attrs[] = {
|
||||
&dev_attr_ctrl_idx.attr,
|
||||
&dev_attr_ctrl_val.attr,
|
||||
|
@ -1152,6 +1250,12 @@ static struct attribute *tpdm_cmb_msr_attrs[] = {
|
|||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute *tpdm_mcmb_attrs[] = {
|
||||
&dev_attr_mcmb_trig_lane.attr,
|
||||
&dev_attr_mcmb_lanes_select.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute *tpdm_dsb_attrs[] = {
|
||||
&dev_attr_dsb_mode.attr,
|
||||
&dev_attr_dsb_trig_ts.attr,
|
||||
|
@ -1218,6 +1322,11 @@ static struct attribute_group tpdm_cmb_msr_grp = {
|
|||
.name = "cmb_msr",
|
||||
};
|
||||
|
||||
static struct attribute_group tpdm_mcmb_attr_grp = {
|
||||
.attrs = tpdm_mcmb_attrs,
|
||||
.is_visible = tpdm_mcmb_is_visible,
|
||||
};
|
||||
|
||||
static const struct attribute_group *tpdm_attr_grps[] = {
|
||||
&tpdm_attr_grp,
|
||||
&tpdm_dsb_attr_grp,
|
||||
|
@ -1229,6 +1338,7 @@ static const struct attribute_group *tpdm_attr_grps[] = {
|
|||
&tpdm_cmb_trig_patt_grp,
|
||||
&tpdm_cmb_patt_grp,
|
||||
&tpdm_cmb_msr_grp,
|
||||
&tpdm_mcmb_attr_grp,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -1305,7 +1415,7 @@ static void tpdm_remove(struct amba_device *adev)
|
|||
* Different TPDM has different periph id.
|
||||
* The difference is 0-7 bits' value. So ignore 0-7 bits.
|
||||
*/
|
||||
static struct amba_id tpdm_ids[] = {
|
||||
static const struct amba_id tpdm_ids[] = {
|
||||
{
|
||||
.id = 0x001f0e00,
|
||||
.mask = 0x00ffff00,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _CORESIGHT_CORESIGHT_TPDM_H
|
||||
|
@ -9,7 +9,7 @@
|
|||
/* The max number of the datasets that TPDM supports */
|
||||
#define TPDM_DATASETS 7
|
||||
|
||||
/* CMB Subunit Registers */
|
||||
/* CMB/MCMB Subunit Registers */
|
||||
#define TPDM_CMB_CR (0xA00)
|
||||
/* CMB subunit timestamp insertion enable register */
|
||||
#define TPDM_CMB_TIER (0xA04)
|
||||
|
@ -28,6 +28,10 @@
|
|||
#define TPDM_CMB_CR_ENA BIT(0)
|
||||
/* Trace collection mode for CMB subunit */
|
||||
#define TPDM_CMB_CR_MODE BIT(1)
|
||||
/* MCMB trigger lane select */
|
||||
#define TPDM_CMB_CR_XTRIG_LNSEL GENMASK(20, 18)
|
||||
/* MCMB lane enablement */
|
||||
#define TPDM_CMB_CR_E_LN GENMASK(17, 10)
|
||||
/* Timestamp control for pattern match */
|
||||
#define TPDM_CMB_TIER_PATT_TSENAB BIT(0)
|
||||
/* CMB CTI timestamp request */
|
||||
|
@ -41,6 +45,12 @@
|
|||
/* MAX number of DSB MSR */
|
||||
#define TPDM_CMB_MAX_MSR 32
|
||||
|
||||
/* MAX lanes in the output pattern for MCMB configurations*/
|
||||
#define TPDM_MCMB_MAX_LANES 8
|
||||
|
||||
/* Filter bit 0~7 from the value for CR_E_LN */
|
||||
#define TPDM_MCMB_E_LN_MASK GENMASK(7, 0)
|
||||
|
||||
/* DSB Subunit Registers */
|
||||
#define TPDM_DSB_CR (0x780)
|
||||
#define TPDM_DSB_TIER (0x784)
|
||||
|
@ -112,11 +122,13 @@
|
|||
* PERIPHIDR0[0] : Fix to 1 if ImplDef subunit present, else 0
|
||||
* PERIPHIDR0[1] : Fix to 1 if DSB subunit present, else 0
|
||||
* PERIPHIDR0[2] : Fix to 1 if CMB subunit present, else 0
|
||||
* PERIPHIDR0[6] : Fix to 1 if MCMB subunit present, else 0
|
||||
*/
|
||||
|
||||
#define TPDM_PIDR0_DS_IMPDEF BIT(0)
|
||||
#define TPDM_PIDR0_DS_DSB BIT(1)
|
||||
#define TPDM_PIDR0_DS_CMB BIT(2)
|
||||
#define TPDM_PIDR0_DS_MCMB BIT(6)
|
||||
|
||||
#define TPDM_DSB_MAX_LINES 256
|
||||
/* MAX number of EDCR registers */
|
||||
|
@ -256,6 +268,9 @@ struct dsb_dataset {
|
|||
* @patt_ts: Indicates if pattern match for timestamp is enabled.
|
||||
* @trig_ts: Indicates if CTI trigger for timestamp is enabled.
|
||||
* @ts_all: Indicates if timestamp is enabled for all packets.
|
||||
* struct mcmb_dataset
|
||||
* @mcmb_trig_lane: Save data for trigger lane
|
||||
* @mcmb_lane_select: Save data for lane enablement
|
||||
*/
|
||||
struct cmb_dataset {
|
||||
u32 trace_mode;
|
||||
|
@ -267,6 +282,10 @@ struct cmb_dataset {
|
|||
bool patt_ts;
|
||||
bool trig_ts;
|
||||
bool ts_all;
|
||||
struct {
|
||||
u8 trig_lane;
|
||||
u8 lane_select;
|
||||
} mcmb;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -324,14 +343,4 @@ struct tpdm_dataset_attribute {
|
|||
enum dataset_mem mem;
|
||||
u32 idx;
|
||||
};
|
||||
|
||||
static bool tpdm_has_dsb_dataset(struct tpdm_drvdata *drvdata)
|
||||
{
|
||||
return (drvdata->datasets & TPDM_PIDR0_DS_DSB);
|
||||
}
|
||||
|
||||
static bool tpdm_has_cmb_dataset(struct tpdm_drvdata *drvdata)
|
||||
{
|
||||
return (drvdata->datasets & TPDM_PIDR0_DS_CMB);
|
||||
}
|
||||
#endif /* _CORESIGHT_CORESIGHT_TPDM_H */
|
||||
|
|
|
@ -22,7 +22,7 @@ enum trace_id_flags {
|
|||
static DEFINE_PER_CPU(atomic_t, id_map_default_cpu_ids) = ATOMIC_INIT(0);
|
||||
static struct coresight_trace_id_map id_map_default = {
|
||||
.cpu_map = &id_map_default_cpu_ids,
|
||||
.lock = __SPIN_LOCK_UNLOCKED(id_map_default.lock)
|
||||
.lock = __RAW_SPIN_LOCK_UNLOCKED(id_map_default.lock)
|
||||
};
|
||||
|
||||
/* #define TRACE_ID_DEBUG 1 */
|
||||
|
@ -131,11 +131,11 @@ static void coresight_trace_id_release_all(struct coresight_trace_id_map *id_map
|
|||
unsigned long flags;
|
||||
int cpu;
|
||||
|
||||
spin_lock_irqsave(&id_map->lock, flags);
|
||||
raw_spin_lock_irqsave(&id_map->lock, flags);
|
||||
bitmap_zero(id_map->used_ids, CORESIGHT_TRACE_IDS_MAX);
|
||||
for_each_possible_cpu(cpu)
|
||||
atomic_set(per_cpu_ptr(id_map->cpu_map, cpu), 0);
|
||||
spin_unlock_irqrestore(&id_map->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&id_map->lock, flags);
|
||||
DUMP_ID_MAP(id_map);
|
||||
}
|
||||
|
||||
|
@ -144,7 +144,7 @@ static int _coresight_trace_id_get_cpu_id(int cpu, struct coresight_trace_id_map
|
|||
unsigned long flags;
|
||||
int id;
|
||||
|
||||
spin_lock_irqsave(&id_map->lock, flags);
|
||||
raw_spin_lock_irqsave(&id_map->lock, flags);
|
||||
|
||||
/* check for existing allocation for this CPU */
|
||||
id = _coresight_trace_id_read_cpu_id(cpu, id_map);
|
||||
|
@ -171,7 +171,7 @@ static int _coresight_trace_id_get_cpu_id(int cpu, struct coresight_trace_id_map
|
|||
atomic_set(per_cpu_ptr(id_map->cpu_map, cpu), id);
|
||||
|
||||
get_cpu_id_out_unlock:
|
||||
spin_unlock_irqrestore(&id_map->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&id_map->lock, flags);
|
||||
|
||||
DUMP_ID_CPU(cpu, id);
|
||||
DUMP_ID_MAP(id_map);
|
||||
|
@ -188,12 +188,12 @@ static void _coresight_trace_id_put_cpu_id(int cpu, struct coresight_trace_id_ma
|
|||
if (!id)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&id_map->lock, flags);
|
||||
raw_spin_lock_irqsave(&id_map->lock, flags);
|
||||
|
||||
coresight_trace_id_free(id, id_map);
|
||||
atomic_set(per_cpu_ptr(id_map->cpu_map, cpu), 0);
|
||||
|
||||
spin_unlock_irqrestore(&id_map->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&id_map->lock, flags);
|
||||
DUMP_ID_CPU(cpu, id);
|
||||
DUMP_ID_MAP(id_map);
|
||||
}
|
||||
|
@ -204,9 +204,9 @@ static int coresight_trace_id_map_get_system_id(struct coresight_trace_id_map *i
|
|||
unsigned long flags;
|
||||
int id;
|
||||
|
||||
spin_lock_irqsave(&id_map->lock, flags);
|
||||
raw_spin_lock_irqsave(&id_map->lock, flags);
|
||||
id = coresight_trace_id_alloc_new_id(id_map, preferred_id, traceid_flags);
|
||||
spin_unlock_irqrestore(&id_map->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&id_map->lock, flags);
|
||||
|
||||
DUMP_ID(id);
|
||||
DUMP_ID_MAP(id_map);
|
||||
|
@ -217,9 +217,9 @@ static void coresight_trace_id_map_put_system_id(struct coresight_trace_id_map *
|
|||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&id_map->lock, flags);
|
||||
raw_spin_lock_irqsave(&id_map->lock, flags);
|
||||
coresight_trace_id_free(id, id_map);
|
||||
spin_unlock_irqrestore(&id_map->lock, flags);
|
||||
raw_spin_unlock_irqrestore(&id_map->lock, flags);
|
||||
|
||||
DUMP_ID(id);
|
||||
DUMP_ID_MAP(id_map);
|
||||
|
|
|
@ -98,7 +98,7 @@ static int smb_open(struct inode *inode, struct file *file)
|
|||
struct smb_drv_data *drvdata = container_of(file->private_data,
|
||||
struct smb_drv_data, miscdev);
|
||||
|
||||
guard(spinlock)(&drvdata->spinlock);
|
||||
guard(raw_spinlock)(&drvdata->spinlock);
|
||||
|
||||
if (drvdata->reading)
|
||||
return -EBUSY;
|
||||
|
@ -152,7 +152,7 @@ static int smb_release(struct inode *inode, struct file *file)
|
|||
struct smb_drv_data *drvdata = container_of(file->private_data,
|
||||
struct smb_drv_data, miscdev);
|
||||
|
||||
guard(spinlock)(&drvdata->spinlock);
|
||||
guard(raw_spinlock)(&drvdata->spinlock);
|
||||
drvdata->reading = false;
|
||||
|
||||
return 0;
|
||||
|
@ -245,7 +245,7 @@ static int smb_enable(struct coresight_device *csdev, enum cs_mode mode,
|
|||
struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
int ret = 0;
|
||||
|
||||
guard(spinlock)(&drvdata->spinlock);
|
||||
guard(raw_spinlock)(&drvdata->spinlock);
|
||||
|
||||
/* Do nothing, the trace data is reading by other interface now */
|
||||
if (drvdata->reading)
|
||||
|
@ -280,7 +280,7 @@ static int smb_disable(struct coresight_device *csdev)
|
|||
{
|
||||
struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent);
|
||||
|
||||
guard(spinlock)(&drvdata->spinlock);
|
||||
guard(raw_spinlock)(&drvdata->spinlock);
|
||||
|
||||
if (drvdata->reading)
|
||||
return -EBUSY;
|
||||
|
@ -378,7 +378,7 @@ static unsigned long smb_update_buffer(struct coresight_device *csdev,
|
|||
if (!buf)
|
||||
return 0;
|
||||
|
||||
guard(spinlock)(&drvdata->spinlock);
|
||||
guard(raw_spinlock)(&drvdata->spinlock);
|
||||
|
||||
/* Don't do anything if another tracer is using this sink. */
|
||||
if (csdev->refcnt != 1)
|
||||
|
@ -563,7 +563,7 @@ static int smb_probe(struct platform_device *pdev)
|
|||
|
||||
smb_reset_buffer(drvdata);
|
||||
platform_set_drvdata(pdev, drvdata);
|
||||
spin_lock_init(&drvdata->spinlock);
|
||||
raw_spin_lock_init(&drvdata->spinlock);
|
||||
drvdata->pid = -1;
|
||||
|
||||
ret = smb_register_sink(pdev, drvdata);
|
||||
|
|
|
@ -115,7 +115,7 @@ struct smb_drv_data {
|
|||
struct coresight_device *csdev;
|
||||
struct smb_data_buffer sdb;
|
||||
struct miscdevice miscdev;
|
||||
spinlock_t spinlock;
|
||||
raw_spinlock_t spinlock;
|
||||
bool reading;
|
||||
pid_t pid;
|
||||
};
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
#define ADXL345_REG_INT_ENABLE 0x2E
|
||||
#define ADXL345_REG_INT_MAP 0x2F
|
||||
#define ADXL345_REG_INT_SOURCE 0x30
|
||||
#define ADXL345_REG_INT_SOURCE_MSK 0xFF
|
||||
#define ADXL345_REG_DATA_FORMAT 0x31
|
||||
#define ADXL345_REG_XYZ_BASE 0x32
|
||||
#define ADXL345_REG_DATA_AXIS(index) \
|
||||
|
|
|
@ -76,6 +76,26 @@ static const unsigned long adxl345_scan_masks[] = {
|
|||
0
|
||||
};
|
||||
|
||||
/**
|
||||
* adxl345_set_measure_en() - Enable and disable measuring.
|
||||
*
|
||||
* @st: The device data.
|
||||
* @en: Enable measurements, else standby mode.
|
||||
*
|
||||
* For lowest power operation, standby mode can be used. In standby mode,
|
||||
* current consumption is supposed to be reduced to 0.1uA (typical). In this
|
||||
* mode no measurements are made. Placing the device into standby mode
|
||||
* preserves the contents of FIFO.
|
||||
*
|
||||
* Return: Returns 0 if successful, or a negative error value.
|
||||
*/
|
||||
static int adxl345_set_measure_en(struct adxl345_state *st, bool en)
|
||||
{
|
||||
unsigned int val = en ? ADXL345_POWER_CTL_MEASURE : ADXL345_POWER_CTL_STANDBY;
|
||||
|
||||
return regmap_write(st->regmap, ADXL345_REG_POWER_CTL, val);
|
||||
}
|
||||
|
||||
static int adxl345_set_interrupts(struct adxl345_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
@ -87,8 +107,7 @@ static int adxl345_set_interrupts(struct adxl345_state *st)
|
|||
* interrupts to the INT1 pin, whereas bits set to 1 send their respective
|
||||
* interrupts to the INT2 pin. The intio shall convert this accordingly.
|
||||
*/
|
||||
int_map = FIELD_GET(ADXL345_REG_INT_SOURCE_MSK,
|
||||
st->intio ? st->int_map : ~st->int_map);
|
||||
int_map = st->intio ? st->int_map : ~st->int_map;
|
||||
|
||||
ret = regmap_write(st->regmap, ADXL345_REG_INT_MAP, int_map);
|
||||
if (ret)
|
||||
|
@ -182,6 +201,16 @@ static int adxl345_write_raw(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adxl345_reg_access(struct iio_dev *indio_dev, unsigned int reg,
|
||||
unsigned int writeval, unsigned int *readval)
|
||||
{
|
||||
struct adxl345_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (readval)
|
||||
return regmap_read(st->regmap, reg, readval);
|
||||
return regmap_write(st->regmap, reg, writeval);
|
||||
}
|
||||
|
||||
static int adxl345_set_watermark(struct iio_dev *indio_dev, unsigned int value)
|
||||
{
|
||||
struct adxl345_state *st = iio_priv(indio_dev);
|
||||
|
@ -214,26 +243,6 @@ static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* adxl345_set_measure_en() - Enable and disable measuring.
|
||||
*
|
||||
* @st: The device data.
|
||||
* @en: Enable measurements, else standby mode.
|
||||
*
|
||||
* For lowest power operation, standby mode can be used. In standby mode,
|
||||
* current consumption is supposed to be reduced to 0.1uA (typical). In this
|
||||
* mode no measurements are made. Placing the device into standby mode
|
||||
* preserves the contents of FIFO.
|
||||
*
|
||||
* Return: Returns 0 if successful, or a negative error value.
|
||||
*/
|
||||
static int adxl345_set_measure_en(struct adxl345_state *st, bool en)
|
||||
{
|
||||
unsigned int val = en ? ADXL345_POWER_CTL_MEASURE : ADXL345_POWER_CTL_STANDBY;
|
||||
|
||||
return regmap_write(st->regmap, ADXL345_REG_POWER_CTL, val);
|
||||
}
|
||||
|
||||
static void adxl345_powerdown(void *ptr)
|
||||
{
|
||||
struct adxl345_state *st = ptr;
|
||||
|
@ -394,18 +403,6 @@ static const struct iio_buffer_setup_ops adxl345_buffer_ops = {
|
|||
.predisable = adxl345_buffer_predisable,
|
||||
};
|
||||
|
||||
static int adxl345_get_status(struct adxl345_state *st)
|
||||
{
|
||||
int ret;
|
||||
unsigned int regval;
|
||||
|
||||
ret = regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return FIELD_GET(ADXL345_REG_INT_SOURCE_MSK, regval);
|
||||
}
|
||||
|
||||
static int adxl345_fifo_push(struct iio_dev *indio_dev,
|
||||
int samples)
|
||||
{
|
||||
|
@ -439,14 +436,10 @@ static irqreturn_t adxl345_irq_handler(int irq, void *p)
|
|||
int int_stat;
|
||||
int samples;
|
||||
|
||||
int_stat = adxl345_get_status(st);
|
||||
if (int_stat <= 0)
|
||||
if (regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, &int_stat))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (int_stat & ADXL345_INT_OVERRUN)
|
||||
goto err;
|
||||
|
||||
if (int_stat & ADXL345_INT_WATERMARK) {
|
||||
if (FIELD_GET(ADXL345_INT_WATERMARK, int_stat)) {
|
||||
samples = adxl345_get_samples(st);
|
||||
if (samples < 0)
|
||||
goto err;
|
||||
|
@ -454,6 +447,10 @@ static irqreturn_t adxl345_irq_handler(int irq, void *p)
|
|||
if (adxl345_fifo_push(indio_dev, samples) < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (FIELD_GET(ADXL345_INT_OVERRUN, int_stat))
|
||||
goto err;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
||||
err:
|
||||
|
@ -467,6 +464,7 @@ static const struct iio_info adxl345_info = {
|
|||
.read_raw = adxl345_read_raw,
|
||||
.write_raw = adxl345_write_raw,
|
||||
.write_raw_get_fmt = adxl345_write_raw_get_fmt,
|
||||
.debugfs_reg_access = &adxl345_reg_access,
|
||||
.hwfifo_set_watermark = adxl345_set_watermark,
|
||||
};
|
||||
|
||||
|
|
|
@ -477,45 +477,42 @@ static int adxl367_set_fifo_watermark(struct adxl367_state *st,
|
|||
static int adxl367_set_range(struct iio_dev *indio_dev,
|
||||
enum adxl367_range range)
|
||||
{
|
||||
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
||||
struct adxl367_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct adxl367_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
guard(mutex)(&st->lock);
|
||||
|
||||
ret = adxl367_set_measure_en(st, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = adxl367_set_measure_en(st, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL,
|
||||
ADXL367_FILTER_CTL_RANGE_MASK,
|
||||
FIELD_PREP(ADXL367_FILTER_CTL_RANGE_MASK,
|
||||
range));
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL,
|
||||
ADXL367_FILTER_CTL_RANGE_MASK,
|
||||
FIELD_PREP(ADXL367_FILTER_CTL_RANGE_MASK,
|
||||
range));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adxl367_scale_act_thresholds(st, st->range, range);
|
||||
adxl367_scale_act_thresholds(st, st->range, range);
|
||||
|
||||
/* Activity thresholds depend on range */
|
||||
ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY,
|
||||
st->act_threshold);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* Activity thresholds depend on range */
|
||||
ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY,
|
||||
st->act_threshold);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY,
|
||||
st->inact_threshold);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY,
|
||||
st->inact_threshold);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adxl367_set_measure_en(st, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = adxl367_set_measure_en(st, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->range = range;
|
||||
st->range = range;
|
||||
|
||||
return 0;
|
||||
}
|
||||
unreachable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adxl367_time_ms_to_samples(struct adxl367_state *st, unsigned int ms)
|
||||
|
@ -620,23 +617,20 @@ static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr)
|
|||
|
||||
static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr)
|
||||
{
|
||||
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
||||
struct adxl367_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct adxl367_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
guard(mutex)(&st->lock);
|
||||
|
||||
ret = adxl367_set_measure_en(st, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = adxl367_set_measure_en(st, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = _adxl367_set_odr(st, odr);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = _adxl367_set_odr(st, odr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return adxl367_set_measure_en(st, true);
|
||||
}
|
||||
unreachable();
|
||||
return adxl367_set_measure_en(st, true);
|
||||
}
|
||||
|
||||
static int adxl367_set_temp_adc_en(struct adxl367_state *st, unsigned int reg,
|
||||
|
@ -725,32 +719,29 @@ static int adxl367_read_sample(struct iio_dev *indio_dev,
|
|||
struct iio_chan_spec const *chan,
|
||||
int *val)
|
||||
{
|
||||
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
||||
struct adxl367_state *st = iio_priv(indio_dev);
|
||||
u16 sample;
|
||||
int ret;
|
||||
struct adxl367_state *st = iio_priv(indio_dev);
|
||||
u16 sample;
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
guard(mutex)(&st->lock);
|
||||
|
||||
ret = adxl367_set_temp_adc_reg_en(st, chan->address, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = adxl367_set_temp_adc_reg_en(st, chan->address, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf,
|
||||
sizeof(st->sample_buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf,
|
||||
sizeof(st->sample_buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sample = FIELD_GET(ADXL367_DATA_MASK, be16_to_cpu(st->sample_buf));
|
||||
*val = sign_extend32(sample, chan->scan_type.realbits - 1);
|
||||
sample = FIELD_GET(ADXL367_DATA_MASK, be16_to_cpu(st->sample_buf));
|
||||
*val = sign_extend32(sample, chan->scan_type.realbits - 1);
|
||||
|
||||
ret = adxl367_set_temp_adc_reg_en(st, chan->address, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = adxl367_set_temp_adc_reg_en(st, chan->address, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
unreachable();
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int adxl367_get_status(struct adxl367_state *st, u8 *status,
|
||||
|
@ -852,10 +843,15 @@ static int adxl367_read_raw(struct iio_dev *indio_dev,
|
|||
int *val, int *val2, long info)
|
||||
{
|
||||
struct adxl367_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return adxl367_read_sample(indio_dev, chan, val);
|
||||
if (!iio_device_claim_direct(indio_dev))
|
||||
return -EBUSY;
|
||||
ret = adxl367_read_sample(indio_dev, chan, val);
|
||||
iio_device_release_direct(indio_dev);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ACCEL: {
|
||||
|
@ -912,7 +908,12 @@ static int adxl367_write_raw(struct iio_dev *indio_dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return adxl367_set_odr(indio_dev, odr);
|
||||
if (!iio_device_claim_direct(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
ret = adxl367_set_odr(indio_dev, odr);
|
||||
iio_device_release_direct(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE: {
|
||||
enum adxl367_range range;
|
||||
|
@ -921,7 +922,12 @@ static int adxl367_write_raw(struct iio_dev *indio_dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return adxl367_set_range(indio_dev, range);
|
||||
if (!iio_device_claim_direct(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
ret = adxl367_set_range(indio_dev, range);
|
||||
iio_device_release_direct(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -1069,13 +1075,15 @@ static int adxl367_read_event_config(struct iio_dev *indio_dev,
|
|||
}
|
||||
}
|
||||
|
||||
static int adxl367_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
bool state)
|
||||
static int __adxl367_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
bool state)
|
||||
{
|
||||
struct adxl367_state *st = iio_priv(indio_dev);
|
||||
enum adxl367_activity_type act;
|
||||
int ret;
|
||||
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
|
@ -1088,28 +1096,38 @@ static int adxl367_write_event_config(struct iio_dev *indio_dev,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
||||
struct adxl367_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
guard(mutex)(&st->lock);
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
ret = adxl367_set_measure_en(st, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adxl367_set_measure_en(st, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = adxl367_set_act_interrupt_en(st, act, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adxl367_set_act_interrupt_en(st, act, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED
|
||||
: ADXL367_ACT_DISABLED);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED
|
||||
: ADXL367_ACT_DISABLED);
|
||||
if (ret)
|
||||
return ret;
|
||||
return adxl367_set_measure_en(st, true);
|
||||
}
|
||||
|
||||
return adxl367_set_measure_en(st, true);
|
||||
}
|
||||
unreachable();
|
||||
static int adxl367_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
bool state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!iio_device_claim_direct(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
ret = __adxl367_write_event_config(indio_dev, chan, type, dir, state);
|
||||
iio_device_release_direct(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t adxl367_get_fifo_enabled(struct device *dev,
|
||||
|
|
|
@ -763,12 +763,11 @@ static int adxl372_read_raw(struct iio_dev *indio_dev,
|
|||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!iio_device_claim_direct(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
ret = adxl372_read_axis(st, chan->address);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
iio_device_release_direct(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue