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:
Linus Torvalds 2025-04-01 11:26:08 -07:00
commit 25601e8544
265 changed files with 19367 additions and 4211 deletions

View file

@ -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.

View file

@ -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

View file

@ -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

View 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.

View file

@ -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

View file

@ -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".

View 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.

View file

@ -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";

View file

@ -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>;
};
};
};
};

View file

@ -55,8 +55,7 @@ properties:
- const: arm,primecell
reg:
minItems: 1
maxItems: 2
maxItems: 1
clocks:
maxItems: 1

View file

@ -41,8 +41,7 @@ properties:
- const: arm,primecell
reg:
minItems: 1
maxItems: 2
maxItems: 1
qcom,dsb-element-bits:
description:

View 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>;
};
};

View file

@ -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

View 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>;
};
};
};
...

View 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>;
};
};

View file

@ -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

View file

@ -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 = <&parallel_bus_controller>;
};
};
...

View file

@ -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

View file

@ -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

View 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 = <&reg_stb_3v3>;
interrupt-parent = <&gpio2>;
interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
};
};
...

View file

@ -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";
};
};
...

View file

@ -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

View file

@ -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>;
};
};
...

View 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>;
};
};

View file

@ -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>;
};
};
...

View file

@ -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

View file

@ -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>;
};
};

View file

@ -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;
};
};
...

View file

@ -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>;
};
};

View file

@ -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

View file

@ -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

View 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

View file

@ -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).

View 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.

View file

@ -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
==============

View file

@ -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
==============

View 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.

View file

@ -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:

View 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>;
};
};

View file

@ -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

View file

@ -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 ]

View 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.

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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,

View file

@ -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),

View file

@ -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;

View file

@ -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");

View file

@ -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;

View file

@ -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");

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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),
{},
};

View file

@ -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
};

View file

@ -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

View 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) */

View file

@ -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");
}

View file

@ -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;

View file

@ -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>");

View 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");

View 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

View file

@ -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);

View file

@ -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)

View file

@ -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);

View file

@ -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 = {

View file

@ -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);

View file

@ -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,

View file

@ -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);

View file

@ -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

View file

@ -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,
};

View file

@ -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;

View file

@ -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)

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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)

View file

@ -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);

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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,

View file

@ -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,

View file

@ -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 */

View file

@ -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);

View file

@ -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);

View file

@ -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;
};

View file

@ -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) \

View file

@ -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, &regval);
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,
};

View file

@ -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,

View file

@ -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