Char/Misc and other driver subsystem updates for 6.9-rc1

Here is the big set of char/misc and a number of other driver subsystem
 updates for 6.9-rc1.  Included in here are:
   - IIO driver updates, loads of new ones and evolution of existing ones
   - coresight driver updates
   - const cleanups for many driver subsystems
   - speakup driver additions
   - platform remove callback void cleanups
   - mei driver updates
   - mhi driver updates
   - cdx driver updates for MSI interrupt handling
   - nvmem driver updates
   - other smaller driver updates and cleanups, full details in the
     shortlog
 
 All of these have been in linux-next for a long time with no reported
 issue, other than a build warning with some older versions of gcc for a
 speakup driver, fix for that will come in a few days when I catch up
 with my pending patch queues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZfwuLg8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ynKVACgjvR1cD8NYk9PcGWc9ZaXAZ6zSnwAn260kMoe
 lLFtwszo7m0N6ZULBWBd
 =y3yz
 -----END PGP SIGNATURE-----

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

Pull char/misc and other driver subsystem updates from Greg KH:
 "Here is the big set of char/misc and a number of other driver
  subsystem updates for 6.9-rc1. Included in here are:

   - IIO driver updates, loads of new ones and evolution of existing ones

   - coresight driver updates

   - const cleanups for many driver subsystems

   - speakup driver additions

   - platform remove callback void cleanups

   - mei driver updates

   - mhi driver updates

   - cdx driver updates for MSI interrupt handling

   - nvmem driver updates

   - other smaller driver updates and cleanups, full details in the
    shortlog

  All of these have been in linux-next for a long time with no reported
  issue, other than a build warning for the speakup driver"

The build warning hits clang and is a gcc (and C23) extension, and is
fixed up in the merge.

Link: https://lore.kernel.org/all/20240321134831.GA2762840@dev-arch.thelio-3990X/

* tag 'char-misc-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (279 commits)
  binder: remove redundant variable page_addr
  uio_dmem_genirq: UIO_MEM_DMA_COHERENT conversion
  uio_pruss: UIO_MEM_DMA_COHERENT conversion
  cnic,bnx2,bnx2x: use UIO_MEM_DMA_COHERENT
  uio: introduce UIO_MEM_DMA_COHERENT type
  cdx: add MSI support for CDX bus
  pps: use cflags-y instead of EXTRA_CFLAGS
  speakup: Add /dev/synthu device
  speakup: Fix 8bit characters from direct synth
  parport: sunbpp: Convert to platform remove callback returning void
  parport: amiga: Convert to platform remove callback returning void
  char: xillybus: Convert to platform remove callback returning void
  vmw_balloon: change maintainership
  MAINTAINERS: change the maintainer for hpilo driver
  char: xilinx_hwicap: Fix NULL vs IS_ERR() bug
  hpet: remove hpets::hp_clocksource
  platform: goldfish: move the separate 'default' propery for CONFIG_GOLDFISH
  char: xilinx_hwicap: drop casting to void in dev_set_drvdata
  greybus: move is_gb_* functions out of greybus.h
  greybus: Remove usage of the deprecated ida_simple_xx() API
  ...
This commit is contained in:
Linus Torvalds 2024-03-21 13:21:31 -07:00
commit bb41fe35dc
305 changed files with 15181 additions and 4508 deletions

View File

@ -439,6 +439,8 @@ Mukesh Ojha <quic_mojha@quicinc.com> <mojha@codeaurora.org>
Muna Sinada <quic_msinada@quicinc.com> <msinada@codeaurora.org>
Murali Nalajala <quic_mnalajal@quicinc.com> <mnalajal@codeaurora.org>
Mythri P K <mythripk@ti.com>
Nadav Amit <nadav.amit@gmail.com> <namit@vmware.com>
Nadav Amit <nadav.amit@gmail.com> <namit@cs.technion.ac.il>
Nadia Yvette Chambers <nyc@holomorphy.com> William Lee Irwin III <wli@holomorphy.com>
Naoya Horiguchi <naoya.horiguchi@nec.com> <n-horiguchi@ah.jp.nec.com>
Nathan Chancellor <nathan@kernel.org> <natechancellor@gmail.com>

View File

@ -170,3 +170,90 @@ Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_t
Description:
(RW) Set/Get the MSR(mux select register) for the DSB subunit
TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/cmb_mode
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description: (Write) Set the data collection mode of CMB tpdm. Continuous
change creates CMB data set elements on every CMBCLK edge.
Trace-on-change creates CMB data set elements only when a new
data set element differs in value from the previous element
in a CMB data set.
Accepts only one of the 2 values - 0 or 1.
0 : Continuous CMB collection mode.
1 : Trace-on-change CMB collection mode.
What: /sys/bus/coresight/devices/<tpdm-name>/cmb_trig_patt/xpr[0:1]
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the value of the trigger pattern for the CMB
subunit TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/cmb_trig_patt/xpmr[0:1]
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the mask of the trigger pattern for the CMB
subunit TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_patt/tpr[0:1]
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the value of the pattern for the CMB subunit TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/dsb_patt/tpmr[0:1]
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the mask of the pattern for the CMB subunit TPDM.
What: /sys/bus/coresight/devices/<tpdm-name>/cmb_patt/enable_ts
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(Write) Set the pattern timestamp of CMB tpdm. Read
the pattern timestamp of CMB tpdm.
Accepts only one of the 2 values - 0 or 1.
0 : Disable CMB pattern timestamp.
1 : Enable CMB pattern timestamp.
What: /sys/bus/coresight/devices/<tpdm-name>/cmb_trig_ts
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the trigger timestamp of the CMB for tpdm.
Accepts only one of the 2 values - 0 or 1.
0 : Set the CMB trigger type to false
1 : Set the CMB trigger type to true
What: /sys/bus/coresight/devices/<tpdm-name>/cmb_ts_all
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Read or write the status of timestamp upon all interface.
Only value 0 and 1 can be written to this node. Set this node to 1 to requeset
timestamp to all trace packet.
Accepts only one of the 2 values - 0 or 1.
0 : Disable the timestamp of all trace packets.
1 : Enable the timestamp of all trace packets.
What: /sys/bus/coresight/devices/<tpdm-name>/cmb_msr/msr[0:31]
Date: January 2024
KernelVersion 6.9
Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com>
Description:
(RW) Set/Get the MSR(mux select register) for the CMB subunit
TPDM.

View File

@ -0,0 +1,9 @@
What: /sys/bus/iio/devices/iio:deviceX/in_shunt_resistorY
KernelVersion: 6.7
Contact: linux-iio@vger.kernel.org
Description:
The value of the shunt resistor may be known only at runtime
and set by a client application. This attribute allows to
set its value in micro-ohms. X is the IIO index of the device.
Y is the channel number. The value is used to calculate
current, power and accumulated energy.

View File

@ -44,14 +44,21 @@ properties:
minItems: 1
maxItems: 2
qcom,dsb-element-size:
qcom,dsb-element-bits:
description:
Specifies the DSB(Discrete Single Bit) element size supported by
the monitor. The associated aggregator will read this size before it
is enabled. DSB element size currently only supports 32-bit and 64-bit.
$ref: /schemas/types.yaml#/definitions/uint8
enum: [32, 64]
qcom,cmb-element-bits:
description:
Specifies the CMB(Continuous Multi-Bit) element size supported by
the monitor. The associated aggregator will read this size before it
is enabled. CMB element size currently only supports 8-bit, 32-bit
and 64-bit.
enum: [8, 32, 64]
qcom,dsb-msrs-num:
description:
Specifies the number of DSB(Discrete Single Bit) MSR(mux select register)
@ -61,6 +68,15 @@ properties:
minimum: 0
maximum: 32
qcom,cmb-msrs-num:
description:
Specifies the number of CMB MSR(mux select register) registers supported
by the monitor. If this property is not configured or set to 0, it means
this TPDM doesn't support CMB MSR.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 32
clocks:
maxItems: 1
@ -94,7 +110,7 @@ examples:
compatible = "qcom,coresight-tpdm", "arm,primecell";
reg = <0x0684c000 0x1000>;
qcom,dsb-element-size = /bits/ 8 <32>;
qcom,dsb-element-bits = <32>;
qcom,dsb-msrs-num = <16>;
clocks = <&aoss_qmp>;
@ -110,4 +126,22 @@ examples:
};
};
tpdm@6c29000 {
compatible = "qcom,coresight-tpdm", "arm,primecell";
reg = <0x06c29000 0x1000>;
qcom,cmb-element-bits = <64>;
qcom,cmb-msrs-num = <32>;
clocks = <&aoss_qmp>;
clock-names = "apb_pclk";
out-ports {
port {
tpdm_ipcc_out_funnel_center: endpoint {
remote-endpoint = <&funnel_center_in_tpdm_ipcc>;
};
};
};
};
...

View File

@ -22,7 +22,6 @@ properties:
maxItems: 1
label:
$ref: /schemas/types.yaml#/definitions/string
description: Unique name to identify which channel this is.
bipolar:

View File

@ -44,6 +44,9 @@ properties:
Pin that controls the powerdown mode of the device.
maxItems: 1
io-backends:
maxItems: 1
reset-gpios:
description:
Reset pin for the device.
@ -68,6 +71,7 @@ examples:
reg = <0>;
clocks = <&adc_clk>;
clock-names = "adc-clk";
io-backends = <&iio_backend>;
};
};
...

View File

@ -39,12 +39,15 @@ properties:
$ref: /schemas/types.yaml#/definitions/phandle
description:
A reference to a the actual ADC to which this FPGA ADC interfaces to.
deprecated: true
'#io-backend-cells':
const: 0
required:
- compatible
- dmas
- reg
- adi,adc-dev
additionalProperties: false
@ -55,7 +58,6 @@ examples:
reg = <0x44a00000 0x10000>;
dmas = <&rx_dma 0>;
dma-names = "rx";
adi,adc-dev = <&spi_adc>;
#io-backend-cells = <0>;
};
...

View File

@ -0,0 +1,120 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/microchip,pac1934.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip PAC1934 Power Monitors with Accumulator
maintainers:
- Marius Cristea <marius.cristea@microchip.com>
description: |
This device is part of the Microchip family of Power Monitors with
Accumulator.
The datasheet for PAC1931, PAC1932, PAC1933 and PAC1934 can be found here:
https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/PAC1931-Family-Data-Sheet-DS20005850E.pdf
properties:
compatible:
enum:
- microchip,pac1931
- microchip,pac1932
- microchip,pac1933
- microchip,pac1934
reg:
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 0
interrupts:
maxItems: 1
slow-io-gpios:
description:
A GPIO used to trigger a change is sampling rate (lowering the chip power
consumption). If configured in SLOW mode, if this pin is forced high,
sampling rate is forced to eight samples/second. When it is forced low,
the sampling rate is 1024 samples/second unless a different sample rate
has been programmed.
patternProperties:
"^channel@[1-4]+$":
type: object
$ref: adc.yaml
description:
Represents the external channels which are connected to the ADC.
properties:
reg:
items:
minimum: 1
maximum: 4
shunt-resistor-micro-ohms:
description:
Value in micro Ohms of the shunt resistor connected between
the SENSE+ and SENSE- inputs, across which the current is measured.
Value is needed to compute the scaling of the measured current.
required:
- reg
- shunt-resistor-micro-ohms
unevaluatedProperties: false
required:
- compatible
- reg
- "#address-cells"
- "#size-cells"
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
power-monitor@10 {
compatible = "microchip,pac1934";
reg = <0x10>;
#address-cells = <1>;
#size-cells = <0>;
channel@1 {
reg = <0x1>;
shunt-resistor-micro-ohms = <24900000>;
label = "CPU";
};
channel@2 {
reg = <0x2>;
shunt-resistor-micro-ohms = <49900000>;
label = "GPU";
};
channel@3 {
reg = <0x3>;
shunt-resistor-micro-ohms = <75000000>;
label = "MEM";
bipolar;
};
channel@4 {
reg = <0x4>;
shunt-resistor-micro-ohms = <100000000>;
label = "NET";
bipolar;
};
};
};
...

View File

@ -31,7 +31,6 @@ properties:
- description: normal conversion, include EOC (End of Conversion),
ECH (End of Chain), JEOC (End of Injected Conversion) and
JECH (End of injected Chain).
- description: Self-testing Interrupts.
clocks:
maxItems: 1
@ -70,8 +69,7 @@ examples:
reg = <0x44530000 0x10000>;
interrupts = <GIC_SPI 217 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 218 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 219 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>;
<GIC_SPI 219 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk IMX93_CLK_ADC1_GATE>;
clock-names = "ipg";
vref-supply = <&reg_vref_1v8>;

View File

@ -75,7 +75,6 @@ patternProperties:
in the PMIC-specific files in include/dt-bindings/iio/.
label:
$ref: /schemas/types.yaml#/definitions/string
description: |
ADC input of the platform as seen in the schematics.
For thermistor inputs connected to generic AMUX or GPIO inputs

View File

@ -25,7 +25,14 @@ description: |
properties:
compatible:
const: richtek,rtq6056
oneOf:
- enum:
- richtek,rtq6056
- richtek,rtq6059
- items:
- enum:
- richtek,rtq6053
- const: richtek,rtq6056
reg:
maxItems: 1

View File

@ -0,0 +1,80 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/ti,ads1298.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments' ads1298 medical ADC chips
description: |
Datasheet at: https://www.ti.com/product/ADS1298
Bindings for this chip aren't complete.
maintainers:
- Mike Looijmans <mike.looijmans@topic.nl>
properties:
compatible:
enum:
- ti,ads1298
reg:
maxItems: 1
spi-cpha: true
reset-gpios:
maxItems: 1
avdd-supply:
description:
Analog power supply, voltage between AVDD and AVSS. When providing a
symmetric +/- 2.5V, the regulator should report 5V.
vref-supply:
description:
Optional reference voltage. If omitted, internal reference is used,
which is 2.4V when analog supply is below 4.4V, 4V otherwise.
clocks:
description: Optional 2.048 MHz external source clock on CLK pin
maxItems: 1
interrupts:
description: Interrupt on DRDY pin, triggers on falling edge
maxItems: 1
label: true
required:
- compatible
- reg
- avdd-supply
- interrupts
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@1 {
reg = <1>;
compatible = "ti,ads1298";
label = "ads1298-1-ecg";
avdd-supply = <&reg_iso_5v_a>;
clocks = <&clk_ads1298>;
interrupt-parent = <&gpio0>;
interrupts = <78 IRQ_TYPE_EDGE_FALLING>;
spi-max-frequency = <20000000>;
spi-cpha;
};
};
...

View File

@ -39,6 +39,17 @@ properties:
description: |
Channel node of a voltage io-channel.
'#io-channel-cells':
description:
In addition to consuming the measurement services of a voltage
output channel, the voltage divider can act as a provider of
measurement services to other devices. This is particularly
useful in scenarios wherein an ADC has an analog frontend,
such as a voltage divider, and then consuming its raw value
isn't interesting. In this case, the voltage before the divider
is desired.
const: 1
output-ohms:
description:
Resistance Rout over which the output voltage is measured. See full-ohms.

View File

@ -21,6 +21,8 @@ description: |
HMC540S 1 dB LSB Silicon MMIC 4-Bit Digital Positive Control Attenuator, 0.1 - 8 GHz
https://www.analog.com/media/en/technical-documentation/data-sheets/hmc540s.pdf
LTC6373 is a 3-Bit precision instrumentation amplifier with fully differential outputs
https://www.analog.com/media/en/technical-documentation/data-sheets/ltc6373.pdf
properties:
compatible:
@ -28,16 +30,55 @@ properties:
- adi,adrf5740
- adi,hmc425a
- adi,hmc540s
- adi,ltc6373
vcc-supply: true
ctrl-gpios:
description:
Must contain an array of 6 GPIO specifiers, referring to the GPIO pins
connected to the control pins V1-V6.
minItems: 6
Must contain an array of GPIO specifiers, referring to the GPIO pins
connected to the control pins.
ADRF5740 - 4 GPIO connected to D2-D5
HMC540S - 4 GPIO connected to V1-V4
HMC425A - 6 GPIO connected to V1-V6
LTC6373 - 3 GPIO connected to A0-A2
minItems: 1
maxItems: 6
allOf:
- if:
properties:
compatible:
contains:
const: adi,hmc425a
then:
properties:
ctrl-gpios:
minItems: 6
maxItems: 6
- if:
properties:
compatible:
contains:
anyOf:
- const: adi,adrf5740
- const: adi,hmc540s
then:
properties:
ctrl-gpios:
minItems: 4
maxItems: 4
- if:
properties:
compatible:
contains:
const: adi,ltc6373
then:
properties:
ctrl-gpios:
minItems: 3
maxItems: 3
required:
- compatible
- ctrl-gpios

View File

@ -0,0 +1,127 @@
# 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/frequency/adi,admfm2000.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ADMFM2000 Dual Microwave Down Converter
maintainers:
- Kim Seer Paller <kimseer.paller@analog.com>
description:
Dual microwave down converter module with input RF and LO frequency ranges
from 0.5 to 32 GHz and an output IF frequency range from 0.1 to 8 GHz.
It consists of a LNA, mixer, IF filter, DSA, and IF amplifier for each down
conversion path.
properties:
compatible:
enum:
- adi,admfm2000
'#address-cells':
const: 1
'#size-cells':
const: 0
patternProperties:
"^channel@[0-1]$":
type: object
description: Represents a channel of the device.
additionalProperties: false
properties:
reg:
description:
The channel number.
minimum: 0
maximum: 1
adi,mixer-mode:
description:
Enable mixer mode for the channel. It downconverts RF between 5 GHz
and 32 GHz to IF between 0.5 GHz and 8 GHz. If not present, the channel
is in direct IF mode which bypasses the mixer and downconverts RF
between 2 GHz and 8 GHz to IF between 0.5 GHz and 8 GHz.
type: boolean
switch-gpios:
description: |
GPIOs to select the RF path for the channel. The same state of CTRL-A
and CTRL-B GPIOs is not permitted.
CTRL-A CTRL-B CH1 Status CH2 Status
1 0 Direct IF mode Mixer mode
0 1 Mixer mode Direct IF mode
items:
- description: CTRL-A GPIO
- description: CTRL-B GPIO
attenuation-gpios:
description: |
Choice of attenuation:
DSA-V4 DSA-V3 DSA-V2 DSA-V1 DSA-V0
1 1 1 1 1 0 dB
1 1 1 1 0 -1 dB
1 1 1 0 1 -2 dB
1 1 0 1 1 -4 dB
1 0 1 1 1 -8 dB
0 1 1 1 1 -16 dB
0 0 0 0 0 -31 dB
items:
- description: DSA-V0 GPIO
- description: DSA-V1 GPIO
- description: DSA-V2 GPIO
- description: DSA-V3 GPIO
- description: DSA-V4 GPIO
required:
- reg
- switch-gpios
- attenuation-gpios
required:
- compatible
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
converter {
compatible = "adi,admfm2000";
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
switch-gpios = <&gpio 1 GPIO_ACTIVE_LOW>,
<&gpio 2 GPIO_ACTIVE_HIGH>;
attenuation-gpios = <&gpio 17 GPIO_ACTIVE_LOW>,
<&gpio 22 GPIO_ACTIVE_LOW>,
<&gpio 23 GPIO_ACTIVE_LOW>,
<&gpio 24 GPIO_ACTIVE_LOW>,
<&gpio 25 GPIO_ACTIVE_LOW>;
};
channel@1 {
reg = <1>;
adi,mixer-mode;
switch-gpios = <&gpio 3 GPIO_ACTIVE_LOW>,
<&gpio 4 GPIO_ACTIVE_HIGH>;
attenuation-gpios = <&gpio 0 GPIO_ACTIVE_LOW>,
<&gpio 5 GPIO_ACTIVE_LOW>,
<&gpio 6 GPIO_ACTIVE_LOW>,
<&gpio 16 GPIO_ACTIVE_LOW>,
<&gpio 26 GPIO_ACTIVE_LOW>;
};
};
...

View File

@ -22,6 +22,9 @@ properties:
vdd-supply: true
vddio-supply: true
spi-max-frequency:
maximum: 10000000
interrupts:
minItems: 1
maxItems: 2
@ -33,7 +36,10 @@ required:
- compatible
- reg
additionalProperties: false
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
unevaluatedProperties: false
examples:
- |

View File

@ -27,6 +27,9 @@ properties:
reg:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg

View File

@ -43,6 +43,7 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
@ -51,5 +52,7 @@ examples:
compatible = "ti,hdc3021", "ti,hdc3020";
reg = <0x47>;
vdd-supply = <&vcc_3v3>;
interrupt-parent = <&gpio3>;
interrupts = <23 IRQ_TYPE_EDGE_RISING>;
};
};

View File

@ -35,7 +35,9 @@ properties:
- st,lsm6dsv
- st,lsm6dso16is
- items:
- const: st,asm330lhhx
- enum:
- st,asm330lhhx
- st,asm330lhhxg1
- const: st,lsm6dsr
- items:
- const: st,lsm6dstx

View File

@ -4,19 +4,22 @@
$id: http://devicetree.org/schemas/iio/light/ams,as73211.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: AMS AS73211 JENCOLOR(R) Digital XYZ Sensor
title: AMS AS73211 JENCOLOR(R) Digital XYZ Sensor and AMS AS7331 UV Sensor
maintainers:
- Christian Eggers <ceggers@arri.de>
description: |
XYZ True Color Sensor with I2C Interface
AMS AS73211 XYZ True Color Sensor with I2C Interface
https://ams.com/documents/20143/36005/AS73211_DS000556_3-01.pdf/a65474c0-b302-c2fd-e30a-c98df87616df
AMS AS7331 UVA, UVB and UVC Sensor with I2C Interface
https://ams.com/documents/20143/9106314/AS7331_DS001047_4-00.pdf
properties:
compatible:
enum:
- ams,as73211
- ams,as7331
reg:
description:

View File

@ -21,6 +21,7 @@ properties:
required:
- compatible
- reg
- vdd-supply
additionalProperties: false

View File

@ -0,0 +1,60 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/magnetometer/voltafield,af8133j.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Voltafield AF8133J magnetometer sensor
maintainers:
- Ondřej Jirman <megi@xff.cz>
properties:
compatible:
const: voltafield,af8133j
reg:
maxItems: 1
reset-gpios:
description:
A signal for active low reset input of the sensor. (optional; if not
used, software reset over I2C will be used instead)
avdd-supply:
description:
A regulator that provides AVDD power (Working power, usually 3.3V) to
the sensor.
dvdd-supply:
description:
A regulator that provides DVDD power (Digital IO power, 1.8V - AVDD)
to the sensor.
mount-matrix:
description: An optional 3x3 mounting rotation matrix.
required:
- compatible
- reg
- avdd-supply
- dvdd-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
magnetometer@1c {
compatible = "voltafield,af8133j";
reg = <0x1c>;
avdd-supply = <&reg_dldo1>;
dvdd-supply = <&reg_dldo1>;
reset-gpios = <&pio 1 1 GPIO_ACTIVE_LOW>;
};
};

View File

@ -99,6 +99,9 @@ required:
- honeywell,transfer-function
- honeywell,pressure-triplet
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml
additionalProperties: false
dependentSchemas:

View File

@ -8,25 +8,28 @@ title: Honeywell mprls0025pa pressure sensor
maintainers:
- Andreas Klinger <ak@it-klinger.de>
- Petre Rodan <petre.rodan@subdimension.ro>
description: |
Honeywell pressure sensor of model mprls0025pa.
This sensor has an I2C and SPI interface. Only the I2C interface is
implemented.
This sensor has an I2C and SPI interface.
There are many models with different pressure ranges available. The vendor
calls them "mpr series". All of them have the identical programming model and
differ in the pressure range, unit and transfer function.
To support different models one need to specify the pressure range as well as
the transfer function. Pressure range needs to be converted from its unit to
pascal.
To support different models one need to specify its pressure triplet as well
as the transfer function.
For custom silicon chips not covered by the Honeywell MPR series datasheet,
the pressure values can be specified manually via honeywell,pmin-pascal and
honeywell,pmax-pascal.
The minimal range value stands for the minimum pressure and the maximum value
also for the maximum pressure with linear relation inside the range.
The transfer function defines the ranges of numerical values delivered by the
sensor. The minimal range value stands for the minimum pressure and the
maximum value also for the maximum pressure with linear relation inside the
range.
sensor.
Specifications about the devices can be found at:
https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/
@ -42,6 +45,10 @@ properties:
maxItems: 1
interrupts:
description:
Optional interrupt for indicating End-of-conversion.
If not present, the driver loops for a while until the received status
byte indicates correct measurement.
maxItems: 1
reset-gpios:
@ -50,6 +57,27 @@ properties:
If not present the device is not reset during the probe.
maxItems: 1
honeywell,transfer-function:
description: |
Transfer function which defines the range of valid values delivered by the
sensor.
1 - A, 10% to 90% of 2^24 (1677722 .. 15099494)
2 - B, 2.5% to 22.5% of 2^24 (419430 .. 3774874)
3 - C, 20% to 80% of 2^24 (3355443 .. 13421773)
enum: [1, 2, 3]
$ref: /schemas/types.yaml#/definitions/uint32
honeywell,pressure-triplet:
description: |
Case-sensitive five character string that defines pressure range, unit
and type as part of the device nomenclature. In the unlikely case of a
custom chip, unset and provide pmin-pascal and pmax-pascal instead.
enum: [0001BA, 01.6BA, 02.5BA, 0060MG, 0100MG, 0160MG, 0250MG, 0400MG,
0600MG, 0001BG, 01.6BG, 02.5BG, 0100KA, 0160KA, 0250KA, 0006KG,
0010KG, 0016KG, 0025KG, 0040KG, 0060KG, 0100KG, 0160KG, 0250KG,
0015PA, 0025PA, 0030PA, 0001PG, 0005PG, 0015PG, 0030PG, 0300YG]
$ref: /schemas/types.yaml#/definitions/string
honeywell,pmin-pascal:
description:
Minimum pressure value the sensor can measure in pascal.
@ -58,14 +86,8 @@ properties:
description:
Maximum pressure value the sensor can measure in pascal.
honeywell,transfer-function:
description: |
Transfer function which defines the range of valid values delivered by the
sensor.
1 - A, 10% to 90% of 2^24 (1677722 .. 15099494)
2 - B, 2.5% to 22.5% of 2^24 (419430 .. 3774874)
3 - C, 20% to 80% of 2^24 (3355443 .. 13421773)
$ref: /schemas/types.yaml#/definitions/uint32
spi-max-frequency:
maximum: 800000
vdd-supply:
description: provide VDD power to the sensor.
@ -73,11 +95,26 @@ properties:
required:
- compatible
- reg
- honeywell,pmin-pascal
- honeywell,pmax-pascal
- honeywell,transfer-function
- vdd-supply
oneOf:
- required:
- honeywell,pressure-triplet
- required:
- honeywell,pmin-pascal
- honeywell,pmax-pascal
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml
- if:
required:
- honeywell,pressure-triplet
then:
properties:
honeywell,pmin-pascal: false
honeywell,pmax-pascal: false
additionalProperties: false
examples:
@ -93,10 +130,29 @@ examples:
reg = <0x18>;
reset-gpios = <&gpio3 19 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&gpio3>;
interrupts = <21 IRQ_TYPE_EDGE_FALLING>;
honeywell,pmin-pascal = <0>;
honeywell,pmax-pascal = <172369>;
interrupts = <21 IRQ_TYPE_EDGE_RISING>;
honeywell,pressure-triplet = "0025PA";
honeywell,transfer-function = <1>;
vdd-supply = <&vcc_3v3>;
};
};
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
pressure@0 {
compatible = "honeywell,mprls0025pa";
reg = <0>;
spi-max-frequency = <800000>;
reset-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&gpio0>;
interrupts = <30 IRQ_TYPE_EDGE_RISING>;
honeywell,pressure-triplet = "0015PA";
honeywell,transfer-function = <1>;
vdd-supply = <&vcc_3v3>;
};
};
...

View File

@ -24,9 +24,16 @@ properties:
reg:
maxItems: 1
vcc-supply:
description: provide VCC power to the sensor.
label:
description: Unique name to identify which device this is.
required:
- compatible
- reg
- vcc-supply
additionalProperties: false
@ -39,5 +46,6 @@ examples:
tmp117@48 {
compatible = "ti,tmp117";
reg = <0x48>;
vcc-supply = <&pmic_reg_3v3>;
};
};

View File

@ -23,6 +23,9 @@ properties:
compatible:
enum:
- qcom,msm8909-bimc
- qcom,msm8909-pcnoc
- qcom,msm8909-snoc
- qcom,msm8916-bimc
- qcom,msm8916-pcnoc
- qcom,msm8916-snoc

View File

@ -8,7 +8,7 @@ title: Qualcomm RPMh Network-On-Chip Interconnect
maintainers:
- Georgi Djakov <georgi.djakov@linaro.org>
- Odelu Kukatla <okukatla@codeaurora.org>
- Odelu Kukatla <quic_okukatla@quicinc.com>
description: |
RPMh interconnect providers support system bandwidth requirements through

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/interconnect/qcom,sm7150-rpmh.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm RPMh Network-On-Chip Interconnect on SM7150
maintainers:
- Danila Tikhonov <danila@jiaxyga.com>
description: |
RPMh interconnect providers support system bandwidth requirements through
RPMh hardware accelerators known as Bus Clock Manager (BCM).
See also:: include/dt-bindings/interconnect/qcom,sm7150-rpmh.h
allOf:
- $ref: qcom,rpmh-common.yaml#
properties:
compatible:
enum:
- qcom,sm7150-aggre1-noc
- qcom,sm7150-aggre2-noc
- qcom,sm7150-compute-noc
- qcom,sm7150-config-noc
- qcom,sm7150-dc-noc
- qcom,sm7150-gem-noc
- qcom,sm7150-mc-virt
- qcom,sm7150-mmss-noc
- qcom,sm7150-system-noc
reg:
maxItems: 1
# Child node's properties
patternProperties:
'^interconnect-[0-9]+$':
type: object
description:
The interconnect providers do not have a separate QoS register space,
but share parent's space.
allOf:
- $ref: qcom,rpmh-common.yaml#
properties:
compatible:
enum:
- qcom,sm7150-camnoc-virt
required:
- compatible
unevaluatedProperties: false
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
mc_virt: interconnect@1380000 {
compatible = "qcom,sm7150-mc-virt";
reg = <0x01380000 0x40000>;
#interconnect-cells = <2>;
qcom,bcm-voters = <&apps_bcm_voter>;
};
system_noc: interconnect@1620000 {
compatible = "qcom,sm7150-system-noc";
reg = <0x01620000 0x40000>;
#interconnect-cells = <2>;
qcom,bcm-voters = <&apps_bcm_voter>;
camnoc_virt: interconnect-0 {
compatible = "qcom,sm7150-camnoc-virt";
#interconnect-cells = <2>;
qcom,bcm-voters = <&apps_bcm_voter>;
};
};

View File

@ -36,20 +36,18 @@ properties:
allOf:
- if:
properties:
compatible:
contains:
const: mac-base
required: [ compatible ]
then:
if:
properties:
compatible:
contains:
const: mac-base
then:
properties:
"#nvmem-cell-cells":
description: The first argument is a MAC address offset.
const: 1
required:
- "#nvmem-cell-cells"
properties:
"#nvmem-cell-cells":
description: The first argument is a MAC address offset.
const: 1
required:
- "#nvmem-cell-cells"
required:
- reg

View File

@ -0,0 +1,18 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/nvmem/nvmem-provider.yaml#
$schema: http://devicetree.org/meta-schemas/base.yaml#
title: NVMEM (Non Volatile Memory) Provider
maintainers:
- Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
select: true
properties:
'#nvmem-cell-cells':
enum: [0, 1]
additionalProperties: true

View File

@ -1,46 +0,0 @@
--------------------------------------------------------------------------
= Zynq UltraScale+ MPSoC nvmem firmware driver binding =
--------------------------------------------------------------------------
The nvmem_firmware node provides access to the hardware related data
like soc revision, IDCODE... etc, By using the firmware interface.
Required properties:
- compatible: should be "xlnx,zynqmp-nvmem-fw"
= Data cells =
Are child nodes of silicon id, bindings of which as described in
bindings/nvmem/nvmem.txt
-------
Example
-------
firmware {
zynqmp_firmware: zynqmp-firmware {
compatible = "xlnx,zynqmp-firmware";
method = "smc";
nvmem_firmware {
compatible = "xlnx,zynqmp-nvmem-fw";
#address-cells = <1>;
#size-cells = <1>;
/* Data cells */
soc_revision: soc_revision {
reg = <0x0 0x4>;
};
};
};
};
= Data consumers =
Are device nodes which consume nvmem data cells.
For example:
pcap {
...
nvmem-cells = <&soc_revision>;
nvmem-cell-names = "soc_revision";
...
};

View File

@ -0,0 +1,42 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/nvmem/xlnx,zynqmp-nvmem.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Zynq UltraScale+ MPSoC Non Volatile Memory interface
description: |
The ZynqMP MPSoC provides access to the hardware related data
like SOC revision, IDCODE and specific purpose efuses.
maintainers:
- Kalyani Akula <kalyani.akula@amd.com>
- Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
allOf:
- $ref: nvmem.yaml#
properties:
compatible:
const: xlnx,zynqmp-nvmem-fw
required:
- compatible
unevaluatedProperties: false
examples:
- |
nvmem {
compatible = "xlnx,zynqmp-nvmem-fw";
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
soc_revision: soc-revision@0 {
reg = <0x0 0x4>;
};
};
};

View File

@ -1573,6 +1573,8 @@ patternProperties:
description: VoCore Studio
"^voipac,.*":
description: Voipac Technologies s.r.o.
"^voltafield,.*":
description: Voltafield Technology Corp.
"^vot,.*":
description: Vision Optical Technology Co., Ltd.
"^vscom,.*":

View File

@ -0,0 +1,407 @@
.. SPDX-License-Identifier: GPL-2.0
================
ADIS16475 driver
================
This driver supports Analog Device's IMUs on SPI bus.
1. Supported devices
====================
* `ADIS16465 <https://www.analog.com/ADIS16465>`_
* `ADIS16467 <https://www.analog.com/ADIS16467>`_
* `ADIS16470 <https://www.analog.com/ADIS16470>`_
* `ADIS16475 <https://www.analog.com/ADIS16475>`_
* `ADIS16477 <https://www.analog.com/ADIS16477>`_
* `ADIS16500 <https://www.analog.com/ADIS16500>`_
* `ADIS16505 <https://www.analog.com/ADIS16505>`_
* `ADIS16507 <https://www.analog.com/ADIS16507>`_
Each supported device is a precision, miniature microelectromechanical system
(MEMS) inertial measurement unit (IMU) that includes a triaxial gyroscope and a
triaxial accelerometer. Each inertial sensor in the IMU device combines with
signal conditioning that optimizes dynamic performance. The factory calibration
characterizes each sensor for sensitivity, bias, alignment, linear acceleration
(gyroscope bias), and point of percussion (accelerometer location). As a result,
each sensor has dynamic compensation formulas that provide accurate sensor
measurements over a broad set of conditions.
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.
The signal chain of each inertial sensor (accelerometers and gyroscopes)
includes the application of unique correction formulas, which are derived from
extensive characterization of bias, sensitivity, alignment, response to linear
acceleration (gyroscopes), and point of percussion (accelerometer location)
over a temperature range of 40°C to +85°C, for each ADIS device. These
correction formulas are not accessible, but users do have the opportunity to
adjust the bias for each sensor individually through the calibbias attribute.
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 adis16475 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_x_calibbias | Calibration offset for the X-axis accelerometer channel. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_calibbias_x | x-axis acceleration offset correction |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_x_raw | Raw X-axis accelerometer channel value. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_calibbias_y | y-axis acceleration offset correction |
+-------------------------------------------+----------------------------------------------------------+
| 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_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_x_calibbias | Calibration offset for the X-axis gyroscope channel. |
+---------------------------------------+------------------------------------------------------+
| in_anglvel_calibbias_x | x-axis gyroscope offset correction |
+---------------------------------------+------------------------------------------------------+
| in_anglvel_x_raw | Raw X-axis gyroscope channel value. |
+---------------------------------------+------------------------------------------------------+
| in_anglvel_calibbias_y | y-axis gyroscope offset correction |
+---------------------------------------+------------------------------------------------------+
| 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_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_scale | Scale for the temperature sensor channel. |
+----------------------------------+-------------------------------------------+
+-------------------------------+---------------------------------------------------------+
| Miscellaneous device files | Description |
+-------------------------------+---------------------------------------------------------+
| name | Name of the IIO device. |
+-------------------------------+---------------------------------------------------------+
| sampling_frequency | Currently selected sample rate. |
+-------------------------------+---------------------------------------------------------+
| filter_low_pass_3db_frequency | Bandwidth for the accelerometer and gyroscope channels. |
+-------------------------------+---------------------------------------------------------+
The following table shows the adis16475 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 (e.g. 16475, 16500, 16505, etc.). |
+----------------------+-------------------------------------------------------------------------+
| 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 adis16475 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
adis16505-2
Show accelerometer channels value:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_raw
-275924
root:/sys/bus/iio/devices/iio:device0> cat in_accel_y_raw
-30142222
root:/sys/bus/iio/devices/iio:device0> cat in_accel_z_raw
261265769
root:/sys/bus/iio/devices/iio:device0> cat in_accel_scale
0.000000037
- X-axis acceleration = in_accel_x_raw * in_accel_scale = 0.010209188 m/s^2
- Y-axis acceleration = in_accel_y_raw * in_accel_scale = 1.115262214 m/s^2
- Z-axis acceleration = in_accel_z_raw * in_accel_scale = 9.666833453 m/s^2
Show gyroscope channels value:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_x_raw
-3324626
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_raw
1336980
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_z_raw
-602983
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_scale
0.000000006
- X-axis angular velocity = in_anglvel_x_raw * in_anglvel_scale = 0.019947756 rad/s
- Y-axis angular velocity = in_anglvel_y_raw * in_anglvel_scale = 0.00802188 rad/s
- Z-axis angular velocity = in_anglvel_z_raw * in_anglvel_scale = 0.003617898 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
2000.000000
root:/sys/bus/iio/devices/iio:device0> echo 1000 > sampling_frequency
1000.000000
Set bandwidth for accelerometer and gyroscope:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat filter_low_pass_3db_frequency
720
root:/sys/bus/iio/devices/iio:device0> echo 360 > filter_low_pass_3db_frequency
root:/sys/bus/iio/devices/iio:device0> cat filter_low_pass_3db_frequency
360
Show serial number:
.. code-block:: bash
root:/sys/kernel/debug/iio/iio:device0> cat serial_number
0x04f9
Show product id:
.. code-block:: bash
root:/sys/kernel/debug/iio/iio:device0> cat product_id
16505
Show flash count:
.. code-block:: bash
root:/sys/kernel/debug/iio/iio:device0> cat flash_count
150
Show firmware revision:
.. code-block:: bash
root:/sys/kernel/debug/iio/iio:device0> cat firmware_revision
1.6
Show firmware date:
.. code-block:: bash
root:/sys/kernel/debug/iio/iio:device0> cat firmware_date
06-27-2019
3. Device buffers
=================
This driver supports IIO buffers.
All devices support retrieving the raw acceleration, gyroscope and temperature
measurements using buffers.
The following device families also support retrieving the delta velocity, delta
angle and temperature measurements using buffers:
- ADIS16477
- ADIS16500
- ADIS16505
- ADIS16507
However, when retrieving acceleration or gyroscope data using buffers, delta
readings will not be available and vice versa.
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 adis16505-2-dev0 > trigger/current_trigger
root:/sys/bus/iio/devices/iio:device0> cat trigger/current_trigger
adis16505-2-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
...
00001680 01 1f 00 00 ff ff fe ef 00 00 47 bf 00 03 35 55 |..........G...5U|
00001690 01 1f 00 00 ff ff ff d9 00 00 46 f1 00 03 35 35 |..........F...55|
000016a0 01 1f 00 00 ff ff fe fc 00 00 46 cb 00 03 35 7b |..........F...5{|
000016b0 01 1f 00 00 ff ff fe 41 00 00 47 0d 00 03 35 8b |.......A..G...5.|
000016c0 01 1f 00 00 ff ff fe 37 00 00 46 b4 00 03 35 90 |.......7..F...5.|
000016d0 01 1d 00 00 ff ff fe 5a 00 00 45 d7 00 03 36 08 |.......Z..E...6.|
000016e0 01 1b 00 00 ff ff fe fb 00 00 45 e7 00 03 36 60 |..........E...6`|
000016f0 01 1a 00 00 ff ff ff 17 00 00 46 bc 00 03 36 de |..........F...6.|
00001700 01 1a 00 00 ff ff fe 59 00 00 46 d7 00 03 37 b8 |.......Y..F...7.|
00001710 01 1a 00 00 ff ff fe ae 00 00 46 95 00 03 37 ba |..........F...7.|
00001720 01 1a 00 00 ff ff fe c5 00 00 46 63 00 03 37 9f |..........Fc..7.|
00001730 01 1a 00 00 ff ff fe 55 00 00 46 89 00 03 37 c1 |.......U..F...7.|
00001740 01 1a 00 00 ff ff fe 31 00 00 46 aa 00 03 37 f7 |.......1..F...7.|
...
See ``Documentation/iio/iio_devbuf.rst`` for more information about how buffered
data is structured.
4. IIO Interfacing Tools
========================
Linux Kernel Tools
------------------
Linux Kernel provides some userspace tools that can be used to retrieve data
from IIO sysfs:
* lsiio: example application that provides a list of IIO devices and triggers
* iio_event_monitor: example application that reads events from an IIO device
and prints them
* iio_generic_buffer: example application that reads data from buffer
* iio_utils: set of APIs, typically used to access sysfs files.
LibIIO
------
LibIIO is a C/C++ library that provides generic access to IIO devices. The
library abstracts the low-level details of the hardware, and provides a simple
yet complete programming interface that can be used for advanced projects.
For more information about LibIIO, please see:
https://github.com/analogdevicesinc/libiio

View File

@ -0,0 +1,152 @@
.. SPDX-License-Identifier: GPL-2.0
=============================
Industrial IIO device buffers
=============================
1. Overview
===========
The Industrial I/O core offers a way for continuous data capture based on a
trigger source. Multiple data channels can be read at once from
``/dev/iio:deviceX`` character device node, thus reducing the CPU load.
Devices with buffer support feature an additional sub-directory in the
``/sys/bus/iio/devices/iio:deviceX/`` directory hierarchy, called bufferY, where
Y defaults to 0, for devices with a single buffer.
2. Buffer attributes
====================
An IIO buffer has an associated attributes directory under
``/sys/bus/iio/iio:deviceX/bufferY/``. The attributes are described below.
``length``
----------
Read / Write attribute which states the total number of data samples (capacity)
that can be stored by the buffer.
``enable``
----------
Read / Write attribute which starts / stops the buffer capture. This file should
be written last, after length and selection of scan elements. Writing a non-zero
value may result in an error, such as EINVAL, if, for example, an unsupported
combination of channels is given.
``watermark``
-------------
Read / Write positive integer attribute specifying the maximum number of scan
elements to wait for.
Poll will block until the watermark is reached.
Blocking read will wait until the minimum between the requested read amount or
the low watermark is available.
Non-blocking read will retrieve the available samples from the buffer even if
there are less samples than the watermark level. This allows the application to
block on poll with a timeout and read the available samples after the timeout
expires and thus have a maximum delay guarantee.
Data available
--------------
Read-only attribute indicating the bytes of data available in the buffer. In the
case of an output buffer, this indicates the amount of empty space available to
write data to. In the case of an input buffer, this indicates the amount of data
available for reading.
Scan elements
-------------
The meta information associated with a channel data placed in a buffer is called
a scan element. The scan elements attributes are presented below.
**_en**
Read / Write attribute used for enabling a channel. If and only if its value
is non-zero, then a triggered capture will contain data samples for this
channel.
**_index**
Read-only unsigned integer attribute specifying the position of the channel in
the buffer. Note these are not dependent on what is enabled and may not be
contiguous. Thus for userspace to establish the full layout these must be used
in conjunction with all _en attributes to establish which channels are present,
and the relevant _type attributes to establish the data storage format.
**_type**
Read-only attribute containing the description of the scan element data storage
within the buffer and hence the form in which it is read from userspace. Format
is [be|le]:[s|u]bits/storagebits[Xrepeat][>>shift], where:
- **be** or **le** specifies big or little-endian.
- **s** or **u** specifies if signed (2's complement) or unsigned.
- **bits** is the number of valid data bits.
- **storagebits** is the number of bits (after padding) that it occupies in the
buffer.
- **repeat** specifies the number of bits/storagebits repetitions. When the
repeat element is 0 or 1, then the repeat value is omitted.
- **shift** if specified, is the shift that needs to be applied prior to
masking out unused bits.
For example, a driver for a 3-axis accelerometer with 12-bit resolution where
data is stored in two 8-bit registers is as follows::
7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
|D3 |D2 |D1 |D0 | X | X | X | X | (LOW byte, address 0x06)
+---+---+---+---+---+---+---+---+
7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
|D11|D10|D9 |D8 |D7 |D6 |D5 |D4 | (HIGH byte, address 0x07)
+---+---+---+---+---+---+---+---+
will have the following scan element type for each axis:
.. code-block:: bash
$ cat /sys/bus/iio/devices/iio:device0/buffer0/in_accel_y_type
le:s12/16>>4
A userspace application will interpret data samples read from the buffer as
two-byte little-endian signed data, that needs a 4 bits right shift before
masking out the 12 valid bits of data.
It is also worth mentioning that the data in the buffer will be naturally
aligned, so the userspace application has to handle the buffers accordingly.
Take for example, a driver with four channels with the following description:
- channel0: index: 0, type: be:u16/16>>0
- channel1: index: 1, type: be:u32/32>>0
- channel2: index: 2, type: be:u32/32>>0
- channel3: index: 3, type: be:u64/64>>0
If all channels are enabled, the data will be aligned in the buffer as follows::
0-1 2 3 4-7 8-11 12 13 14 15 16-23 -> buffer byte number
+-----+---+---+-----+-----+---+---+---+---+-----+
|CHN_0|PAD|PAD|CHN_1|CHN_2|PAD|PAD|PAD|PAD|CHN_3| -> buffer content
+-----+---+---+-----+-----+---+---+---+---+-----+
If only channel0 and channel3 are enabled, the data will be aligned in the
buffer as follows::
0-1 2 3 4 5 6 7 8-15 -> buffer byte number
+-----+---+---+---+---+---+---+-----+
|CHN_0|PAD|PAD|PAD|PAD|PAD|PAD|CHN_3| -> buffer content
+-----+---+---+---+---+---+---+-----+
Typically the buffered data is found in raw format (unscaled with no offset
applied), however there are corner cases in which the buffered data may be found
in a processed form. Please note that these corner cases are not addressed by
this documentation.
Please see ``Documentation/ABI/testing/sysfs-bus-iio`` for a complete
description of the attributes.

View File

@ -8,7 +8,14 @@ Industrial I/O
:maxdepth: 1
iio_configfs
iio_devbuf
ep93xx_adc
Industrial I/O Kernel Drivers
=============================
.. toctree::
:maxdepth: 1
adis16475
bno055
ep93xx_adc

View File

@ -579,6 +579,12 @@ F: drivers/iio/accel/adxl372.c
F: drivers/iio/accel/adxl372_i2c.c
F: drivers/iio/accel/adxl372_spi.c
AF8133J THREE-AXIS MAGNETOMETER DRIVER
M: Ondřej Jirman <megi@xff.cz>
S: Maintained
F: Documentation/devicetree/bindings/iio/magnetometer/voltafield,af8133j.yaml
F: drivers/iio/magnetometer/af8133j.c
AF9013 MEDIA DRIVER
L: linux-media@vger.kernel.org
S: Orphan
@ -1158,7 +1164,7 @@ L: linux-iio@vger.kernel.org
S: Supported
W: http://ez.analog.com/community/linux-device-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7091r*
F: drivers/iio/adc/drivers/iio/adc/ad7091r*
F: drivers/iio/adc/ad7091r*
ANALOG DEVICES INC AD7192 DRIVER
M: Alexandru Tachici <alexandru.tachici@analog.com>
@ -1281,6 +1287,14 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml
F: drivers/hwmon/adm1177.c
ANALOG DEVICES INC ADMFM2000 DRIVER
M: Kim Seer Paller <kimseer.paller@analog.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/frequency/adi,admfm2000.yaml
F: drivers/iio/frequency/admfm2000.c
ANALOG DEVICES INC ADMV1013 DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
@ -9509,7 +9523,7 @@ T: git git://linuxtv.org/media_tree.git
F: drivers/media/usb/hdpvr/
HEWLETT PACKARD ENTERPRISE ILO CHIF DRIVER
M: Matt Hsiao <matt.hsiao@hpe.com>
M: Keng-Yu Lin <keng-yu.lin@hpe.com>
S: Supported
F: drivers/misc/hpilo.[ch]
@ -9879,10 +9893,11 @@ F: drivers/iio/pressure/hsc030pa*
HONEYWELL MPRLS0025PA PRESSURE SENSOR SERIES IIO DRIVER
M: Andreas Klinger <ak@it-klinger.de>
M: Petre Rodan <petre.rodan@subdimension.ro>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/pressure/honeywell,mprls0025pa.yaml
F: drivers/iio/pressure/mprls0025pa.c
F: drivers/iio/pressure/mprls0025pa*
HP BIOSCFG DRIVER
M: Jorge Lopez <jorge.lopez2@hp.com>
@ -10475,6 +10490,14 @@ L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/rc/iguanair.c
IIO BACKEND FRAMEWORK
M: Nuno Sa <nuno.sa@analog.com>
R: Olivier Moysan <olivier.moysan@foss.st.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/industrialio-backend.c
F: include/linux/iio/backend.h
IIO DIGITAL POTENTIOMETER DAC
M: Peter Rosin <peda@axentia.se>
L: linux-iio@vger.kernel.org
@ -10497,6 +10520,7 @@ L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/industrialio-gts-helper.c
F: include/linux/iio/iio-gts-helper.h
F: drivers/iio/test/iio-test-gts.c
IIO MULTIPLEXER
M: Peter Rosin <peda@axentia.se>
@ -14487,6 +14511,13 @@ F: Documentation/devicetree/bindings/nvmem/microchip,sama7g5-otpc.yaml
F: drivers/nvmem/microchip-otpc.c
F: include/dt-bindings/nvmem/microchip,sama7g5-otpc.h
MICROCHIP PAC1934 POWER/ENERGY MONITOR DRIVER
M: Marius Cristea <marius.cristea@microchip.com>
L: linux-iio@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/iio/adc/microchip,pac1934.yaml
F: drivers/iio/adc/pac1934.c
MICROCHIP PCI1XXXX GP DRIVER
M: Vaibhaav Ram T.L <vaibhaavram.tl@microchip.com>
M: Kumaravel Thiagarajan <kumaravel.thiagarajan@microchip.com>
@ -23566,8 +23597,8 @@ F: Documentation/driver-api/vme.rst
F: drivers/staging/vme_user/
VMWARE BALLOON DRIVER
M: Nadav Amit <namit@vmware.com>
R: VMware PV-Drivers Reviewers <pv-drivers@vmware.com>
M: Jerrin Shaji George <jerrin.shaji-george@broadcom.com>
R: Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com>
L: linux-kernel@vger.kernel.org
S: Supported
F: drivers/misc/vmw_balloon.c
@ -24344,6 +24375,14 @@ M: Harsha <harsha.harsha@amd.com>
S: Maintained
F: drivers/crypto/xilinx/zynqmp-sha.c
XILINX ZYNQMP NVMEM DRIVER
M: Praveen Teja Kundanala <praveen.teja.kundanala@amd.com>
M: Kalyani Akula <kalyani.akula@amd.com>
R: Michal Simek <michal.simek@amd.com>
S: Maintained
F: Documentation/devicetree/bindings/nvmem/xlnx,zynqmp-nvmem.yaml
F: drivers/nvmem/zynqmp_nvmem.c
XILLYBUS DRIVER
M: Eli Billauer <eli.billauer@gmail.com>
L: linux-kernel@vger.kernel.org

View File

@ -472,10 +472,6 @@ config X86_MPPARSE
For old smp systems that do not have proper acpi support. Newer systems
(esp with 64bit cpus) with acpi support, MADT and DSDT will override it
config GOLDFISH
def_bool y
depends on X86_GOLDFISH
config X86_CPU_RESCTRL
bool "x86 CPU resource control support"
depends on X86 && (CPU_SUP_INTEL || CPU_SUP_AMD)

View File

@ -7,9 +7,10 @@
#include "speakup.h"
#include "spk_priv.h"
static int misc_registered;
static int synth_registered, synthu_registered;
static int dev_opened;
/* Latin1 version */
static ssize_t speakup_file_write(struct file *fp, const char __user *buffer,
size_t nbytes, loff_t *ppos)
{
@ -34,6 +35,98 @@ static ssize_t speakup_file_write(struct file *fp, const char __user *buffer,
return (ssize_t)nbytes;
}
/* UTF-8 version */
static ssize_t speakup_file_writeu(struct file *fp, const char __user *buffer,
size_t nbytes, loff_t *ppos)
{
size_t count = nbytes, want;
const char __user *ptr = buffer;
size_t bytes;
unsigned long flags;
unsigned char buf[256];
u16 ubuf[256];
size_t in, in2, out;
if (!synth)
return -ENODEV;
want = 1;
while (count >= want) {
/* Copy some UTF-8 piece from userland */
bytes = min(count, sizeof(buf));
if (copy_from_user(buf, ptr, bytes))
return -EFAULT;
/* Convert to u16 */
for (in = 0, out = 0; in < bytes; in++) {
unsigned char c = buf[in];
int nbytes = 8 - fls(c ^ 0xff);
u32 value;
switch (nbytes) {
case 8: /* 0xff */
case 7: /* 0xfe */
case 1: /* 0x80 */
/* Invalid, drop */
goto drop;
case 0:
/* ASCII, copy */
ubuf[out++] = c;
continue;
default:
/* 2..6-byte UTF-8 */
if (bytes - in < nbytes) {
/* We don't have it all yet, stop here
* and wait for the rest
*/
bytes = in;
want = nbytes;
continue;
}
/* First byte */
value = c & ((1u << (7 - nbytes)) - 1);
/* Other bytes */
for (in2 = 2; in2 <= nbytes; in2++) {
c = buf[in + 1];
if ((c & 0xc0) != 0x80) {
/* Invalid, drop the head */
want = 1;
goto drop;
}
value = (value << 6) | (c & 0x3f);
in++;
}
if (value < 0x10000)
ubuf[out++] = value;
want = 1;
break;
}
drop:
/* empty statement */;
}
count -= bytes;
ptr += bytes;
/* And speak this up */
if (out) {
spin_lock_irqsave(&speakup_info.spinlock, flags);
for (in = 0; in < out; in++)
synth_buffer_add(ubuf[in]);
synth_start();
spin_unlock_irqrestore(&speakup_info.spinlock, flags);
}
}
return (ssize_t)(nbytes - count);
}
static ssize_t speakup_file_read(struct file *fp, char __user *buf,
size_t nbytes, loff_t *ppos)
{
@ -62,31 +155,57 @@ static const struct file_operations synth_fops = {
.release = speakup_file_release,
};
static const struct file_operations synthu_fops = {
.read = speakup_file_read,
.write = speakup_file_writeu,
.open = speakup_file_open,
.release = speakup_file_release,
};
static struct miscdevice synth_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "synth",
.fops = &synth_fops,
};
static struct miscdevice synthu_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "synthu",
.fops = &synthu_fops,
};
void speakup_register_devsynth(void)
{
if (misc_registered != 0)
return;
/* zero it so if register fails, deregister will not ref invalid ptrs */
if (misc_register(&synth_device)) {
pr_warn("Couldn't initialize miscdevice /dev/synth.\n");
} else {
pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n",
MISC_MAJOR, synth_device.minor);
misc_registered = 1;
if (!synth_registered) {
if (misc_register(&synth_device)) {
pr_warn("Couldn't initialize miscdevice /dev/synth.\n");
} else {
pr_info("initialized device: /dev/synth, node (MAJOR %d, MINOR %d)\n",
MISC_MAJOR, synth_device.minor);
synth_registered = 1;
}
}
if (!synthu_registered) {
if (misc_register(&synthu_device)) {
pr_warn("Couldn't initialize miscdevice /dev/synthu.\n");
} else {
pr_info("initialized device: /dev/synthu, node (MAJOR %d, MINOR %d)\n",
MISC_MAJOR, synthu_device.minor);
synthu_registered = 1;
}
}
}
void speakup_unregister_devsynth(void)
{
if (!misc_registered)
return;
pr_info("speakup: unregistering synth device /dev/synth\n");
misc_deregister(&synth_device);
misc_registered = 0;
if (synth_registered) {
pr_info("speakup: unregistering synth device /dev/synth\n");
misc_deregister(&synth_device);
synth_registered = 0;
}
if (synthu_registered) {
pr_info("speakup: unregistering synth device /dev/synthu\n");
misc_deregister(&synthu_device);
synthu_registered = 0;
}
}

View File

@ -208,8 +208,10 @@ void spk_do_flush(void)
wake_up_process(speakup_task);
}
void synth_write(const char *buf, size_t count)
void synth_write(const char *_buf, size_t count)
{
const unsigned char *buf = (const unsigned char *) _buf;
while (count--)
synth_buffer_add(*buf++);
synth_start();

View File

@ -925,7 +925,6 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)
int i;
for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
unsigned long page_addr;
bool on_lru;
if (!alloc->pages[i].page_ptr)
@ -933,7 +932,6 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)
on_lru = list_lru_del_obj(&binder_freelist,
&alloc->pages[i].lru);
page_addr = alloc->buffer + i * PAGE_SIZE;
binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
"%s: %d: page %d %s\n",
__func__, alloc->pid, i,

View File

@ -297,30 +297,30 @@ struct mhi_ring_element {
__le32 dword[2];
};
#define MHI_STATE_LIST \
mhi_state(RESET, "RESET") \
mhi_state(READY, "READY") \
mhi_state(M0, "M0") \
mhi_state(M1, "M1") \
mhi_state(M2, "M2") \
mhi_state(M3, "M3") \
mhi_state(M3_FAST, "M3_FAST") \
mhi_state(BHI, "BHI") \
mhi_state_end(SYS_ERR, "SYS ERROR")
#undef mhi_state
#undef mhi_state_end
#define mhi_state(a, b) case MHI_STATE_##a: return b;
#define mhi_state_end(a, b) case MHI_STATE_##a: return b;
static inline const char *mhi_state_str(enum mhi_state state)
{
switch (state) {
case MHI_STATE_RESET:
return "RESET";
case MHI_STATE_READY:
return "READY";
case MHI_STATE_M0:
return "M0";
case MHI_STATE_M1:
return "M1";
case MHI_STATE_M2:
return "M2";
case MHI_STATE_M3:
return "M3";
case MHI_STATE_M3_FAST:
return "M3 FAST";
case MHI_STATE_BHI:
return "BHI";
case MHI_STATE_SYS_ERR:
return "SYS ERROR";
MHI_STATE_LIST
default:
return "Unknown state";
}
};
}
#endif /* _MHI_COMMON_H */

View File

@ -1149,8 +1149,9 @@ int mhi_ep_power_up(struct mhi_ep_cntrl *mhi_cntrl)
mhi_ep_mmio_mask_interrupts(mhi_cntrl);
mhi_ep_mmio_init(mhi_cntrl);
mhi_cntrl->mhi_event = kzalloc(mhi_cntrl->event_rings * (sizeof(*mhi_cntrl->mhi_event)),
GFP_KERNEL);
mhi_cntrl->mhi_event = kcalloc(mhi_cntrl->event_rings,
sizeof(*mhi_cntrl->mhi_event),
GFP_KERNEL);
if (!mhi_cntrl->mhi_event)
return -ENOMEM;
@ -1496,7 +1497,7 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl,
mhi_cntrl->ring_item_cache = kmem_cache_create("mhi_ep_ring_item",
sizeof(struct mhi_ep_ring_item), 0,
0, NULL);
if (!mhi_cntrl->ev_ring_el_cache) {
if (!mhi_cntrl->ring_item_cache) {
ret = -ENOMEM;
goto err_destroy_tre_buf_cache;
}

View File

@ -395,7 +395,7 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
void *buf;
dma_addr_t dma_addr;
size_t size, fw_sz;
int i, ret;
int ret;
if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) {
dev_err(dev, "Device MHI is not in valid state\n");
@ -408,15 +408,6 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl)
if (ret)
dev_err(dev, "Could not capture serial number via BHI\n");
for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++) {
ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_OEMPKHASH(i),
&mhi_cntrl->oem_pk_hash[i]);
if (ret) {
dev_err(dev, "Could not capture OEM PK HASH via BHI\n");
break;
}
}
/* wait for ready on pass through or any other execution environment */
if (!MHI_FW_LOAD_CAPABLE(mhi_cntrl->ee))
goto fw_load_ready_state;

View File

@ -20,50 +20,49 @@
#include <linux/wait.h>
#include "internal.h"
#define CREATE_TRACE_POINTS
#include "trace.h"
static DEFINE_IDA(mhi_controller_ida);
#undef mhi_ee
#undef mhi_ee_end
#define mhi_ee(a, b) [MHI_EE_##a] = b,
#define mhi_ee_end(a, b) [MHI_EE_##a] = b,
const char * const mhi_ee_str[MHI_EE_MAX] = {
[MHI_EE_PBL] = "PRIMARY BOOTLOADER",
[MHI_EE_SBL] = "SECONDARY BOOTLOADER",
[MHI_EE_AMSS] = "MISSION MODE",
[MHI_EE_RDDM] = "RAMDUMP DOWNLOAD MODE",
[MHI_EE_WFW] = "WLAN FIRMWARE",
[MHI_EE_PTHRU] = "PASS THROUGH",
[MHI_EE_EDL] = "EMERGENCY DOWNLOAD",
[MHI_EE_FP] = "FLASH PROGRAMMER",
[MHI_EE_DISABLE_TRANSITION] = "DISABLE",
[MHI_EE_NOT_SUPPORTED] = "NOT SUPPORTED",
MHI_EE_LIST
};
#undef dev_st_trans
#undef dev_st_trans_end
#define dev_st_trans(a, b) [DEV_ST_TRANSITION_##a] = b,
#define dev_st_trans_end(a, b) [DEV_ST_TRANSITION_##a] = b,
const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX] = {
[DEV_ST_TRANSITION_PBL] = "PBL",
[DEV_ST_TRANSITION_READY] = "READY",
[DEV_ST_TRANSITION_SBL] = "SBL",
[DEV_ST_TRANSITION_MISSION_MODE] = "MISSION MODE",
[DEV_ST_TRANSITION_FP] = "FLASH PROGRAMMER",
[DEV_ST_TRANSITION_SYS_ERR] = "SYS ERROR",
[DEV_ST_TRANSITION_DISABLE] = "DISABLE",
DEV_ST_TRANSITION_LIST
};
#undef ch_state_type
#undef ch_state_type_end
#define ch_state_type(a, b) [MHI_CH_STATE_TYPE_##a] = b,
#define ch_state_type_end(a, b) [MHI_CH_STATE_TYPE_##a] = b,
const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = {
[MHI_CH_STATE_TYPE_RESET] = "RESET",
[MHI_CH_STATE_TYPE_STOP] = "STOP",
[MHI_CH_STATE_TYPE_START] = "START",
MHI_CH_STATE_TYPE_LIST
};
#undef mhi_pm_state
#undef mhi_pm_state_end
#define mhi_pm_state(a, b) [MHI_PM_STATE_##a] = b,
#define mhi_pm_state_end(a, b) [MHI_PM_STATE_##a] = b,
static const char * const mhi_pm_state_str[] = {
[MHI_PM_STATE_DISABLE] = "DISABLE",
[MHI_PM_STATE_POR] = "POWER ON RESET",
[MHI_PM_STATE_M0] = "M0",
[MHI_PM_STATE_M2] = "M2",
[MHI_PM_STATE_M3_ENTER] = "M?->M3",
[MHI_PM_STATE_M3] = "M3",
[MHI_PM_STATE_M3_EXIT] = "M3->M0",
[MHI_PM_STATE_FW_DL_ERR] = "Firmware Download Error",
[MHI_PM_STATE_SYS_ERR_DETECT] = "SYS ERROR Detect",
[MHI_PM_STATE_SYS_ERR_PROCESS] = "SYS ERROR Process",
[MHI_PM_STATE_SHUTDOWN_PROCESS] = "SHUTDOWN Process",
[MHI_PM_STATE_LD_ERR_FATAL_DETECT] = "Linkdown or Error Fatal Detect",
MHI_PM_STATE_LIST
};
const char *to_mhi_pm_state_str(u32 state)
@ -97,11 +96,19 @@ static ssize_t oem_pk_hash_show(struct device *dev,
{
struct mhi_device *mhi_dev = to_mhi_device(dev);
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
int i, cnt = 0;
u32 hash_segment[MHI_MAX_OEM_PK_HASH_SEGMENTS];
int i, cnt = 0, ret;
for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++)
cnt += sysfs_emit_at(buf, cnt, "OEMPKHASH[%d]: 0x%x\n",
i, mhi_cntrl->oem_pk_hash[i]);
for (i = 0; i < MHI_MAX_OEM_PK_HASH_SEGMENTS; i++) {
ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_OEMPKHASH(i), &hash_segment[i]);
if (ret) {
dev_err(dev, "Could not capture OEM PK HASH\n");
return ret;
}
}
for (i = 0; i < MHI_MAX_OEM_PK_HASH_SEGMENTS; i++)
cnt += sysfs_emit_at(buf, cnt, "OEMPKHASH[%d]: 0x%x\n", i, hash_segment[i]);
return cnt;
}
@ -907,7 +914,6 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan;
struct mhi_cmd *mhi_cmd;
struct mhi_device *mhi_dev;
u32 soc_info;
int ret, i;
if (!mhi_cntrl || !mhi_cntrl->cntrl_dev || !mhi_cntrl->regs ||
@ -982,17 +988,6 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
mhi_cntrl->unmap_single = mhi_unmap_single_no_bb;
}
/* Read the MHI device info */
ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs,
SOC_HW_VERSION_OFFS, &soc_info);
if (ret)
goto err_destroy_wq;
mhi_cntrl->family_number = FIELD_GET(SOC_HW_VERSION_FAM_NUM_BMSK, soc_info);
mhi_cntrl->device_number = FIELD_GET(SOC_HW_VERSION_DEV_NUM_BMSK, soc_info);
mhi_cntrl->major_version = FIELD_GET(SOC_HW_VERSION_MAJOR_VER_BMSK, soc_info);
mhi_cntrl->minor_version = FIELD_GET(SOC_HW_VERSION_MINOR_VER_BMSK, soc_info);
mhi_cntrl->index = ida_alloc(&mhi_controller_ida, GFP_KERNEL);
if (mhi_cntrl->index < 0) {
ret = mhi_cntrl->index;

View File

@ -15,12 +15,6 @@ extern struct bus_type mhi_bus_type;
#define MHI_SOC_RESET_REQ_OFFSET 0xb0
#define MHI_SOC_RESET_REQ BIT(0)
#define SOC_HW_VERSION_OFFS 0x224
#define SOC_HW_VERSION_FAM_NUM_BMSK GENMASK(31, 28)
#define SOC_HW_VERSION_DEV_NUM_BMSK GENMASK(27, 16)
#define SOC_HW_VERSION_MAJOR_VER_BMSK GENMASK(15, 8)
#define SOC_HW_VERSION_MINOR_VER_BMSK GENMASK(7, 0)
struct mhi_ctxt {
struct mhi_event_ctxt *er_ctxt;
struct mhi_chan_ctxt *chan_ctxt;
@ -42,6 +36,11 @@ enum mhi_ch_state_type {
MHI_CH_STATE_TYPE_MAX,
};
#define MHI_CH_STATE_TYPE_LIST \
ch_state_type(RESET, "RESET") \
ch_state_type(STOP, "STOP") \
ch_state_type_end(START, "START")
extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX];
#define TO_CH_STATE_TYPE_STR(state) (((state) >= MHI_CH_STATE_TYPE_MAX) ? \
"INVALID_STATE" : \
@ -50,6 +49,18 @@ extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX];
#define MHI_INVALID_BRSTMODE(mode) (mode != MHI_DB_BRST_DISABLE && \
mode != MHI_DB_BRST_ENABLE)
#define MHI_EE_LIST \
mhi_ee(PBL, "PRIMARY BOOTLOADER") \
mhi_ee(SBL, "SECONDARY BOOTLOADER") \
mhi_ee(AMSS, "MISSION MODE") \
mhi_ee(RDDM, "RAMDUMP DOWNLOAD MODE")\
mhi_ee(WFW, "WLAN FIRMWARE") \
mhi_ee(PTHRU, "PASS THROUGH") \
mhi_ee(EDL, "EMERGENCY DOWNLOAD") \
mhi_ee(FP, "FLASH PROGRAMMER") \
mhi_ee(DISABLE_TRANSITION, "DISABLE") \
mhi_ee_end(NOT_SUPPORTED, "NOT SUPPORTED")
extern const char * const mhi_ee_str[MHI_EE_MAX];
#define TO_MHI_EXEC_STR(ee) (((ee) >= MHI_EE_MAX) ? \
"INVALID_EE" : mhi_ee_str[ee])
@ -72,6 +83,15 @@ enum dev_st_transition {
DEV_ST_TRANSITION_MAX,
};
#define DEV_ST_TRANSITION_LIST \
dev_st_trans(PBL, "PBL") \
dev_st_trans(READY, "READY") \
dev_st_trans(SBL, "SBL") \
dev_st_trans(MISSION_MODE, "MISSION MODE") \
dev_st_trans(FP, "FLASH PROGRAMMER") \
dev_st_trans(SYS_ERR, "SYS ERROR") \
dev_st_trans_end(DISABLE, "DISABLE")
extern const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX];
#define TO_DEV_STATE_TRANS_STR(state) (((state) >= DEV_ST_TRANSITION_MAX) ? \
"INVALID_STATE" : dev_state_tran_str[state])
@ -88,11 +108,27 @@ enum mhi_pm_state {
MHI_PM_STATE_FW_DL_ERR,
MHI_PM_STATE_SYS_ERR_DETECT,
MHI_PM_STATE_SYS_ERR_PROCESS,
MHI_PM_STATE_SYS_ERR_FAIL,
MHI_PM_STATE_SHUTDOWN_PROCESS,
MHI_PM_STATE_LD_ERR_FATAL_DETECT,
MHI_PM_STATE_MAX
};
#define MHI_PM_STATE_LIST \
mhi_pm_state(DISABLE, "DISABLE") \
mhi_pm_state(POR, "POWER ON RESET") \
mhi_pm_state(M0, "M0") \
mhi_pm_state(M2, "M2") \
mhi_pm_state(M3_ENTER, "M?->M3") \
mhi_pm_state(M3, "M3") \
mhi_pm_state(M3_EXIT, "M3->M0") \
mhi_pm_state(FW_DL_ERR, "Firmware Download Error") \
mhi_pm_state(SYS_ERR_DETECT, "SYS ERROR Detect") \
mhi_pm_state(SYS_ERR_PROCESS, "SYS ERROR Process") \
mhi_pm_state(SYS_ERR_FAIL, "SYS ERROR Failure") \
mhi_pm_state(SHUTDOWN_PROCESS, "SHUTDOWN Process") \
mhi_pm_state_end(LD_ERR_FATAL_DETECT, "Linkdown or Error Fatal Detect")
#define MHI_PM_DISABLE BIT(0)
#define MHI_PM_POR BIT(1)
#define MHI_PM_M0 BIT(2)
@ -104,14 +140,16 @@ enum mhi_pm_state {
#define MHI_PM_FW_DL_ERR BIT(7)
#define MHI_PM_SYS_ERR_DETECT BIT(8)
#define MHI_PM_SYS_ERR_PROCESS BIT(9)
#define MHI_PM_SHUTDOWN_PROCESS BIT(10)
#define MHI_PM_SYS_ERR_FAIL BIT(10)
#define MHI_PM_SHUTDOWN_PROCESS BIT(11)
/* link not accessible */
#define MHI_PM_LD_ERR_FATAL_DETECT BIT(11)
#define MHI_PM_LD_ERR_FATAL_DETECT BIT(12)
#define MHI_REG_ACCESS_VALID(pm_state) ((pm_state & (MHI_PM_POR | MHI_PM_M0 | \
MHI_PM_M2 | MHI_PM_M3_ENTER | MHI_PM_M3_EXIT | \
MHI_PM_SYS_ERR_DETECT | MHI_PM_SYS_ERR_PROCESS | \
MHI_PM_SHUTDOWN_PROCESS | MHI_PM_FW_DL_ERR)))
MHI_PM_SYS_ERR_FAIL | MHI_PM_SHUTDOWN_PROCESS | \
MHI_PM_FW_DL_ERR)))
#define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR)
#define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state == MHI_PM_LD_ERR_FATAL_DETECT)
#define MHI_DB_ACCESS_VALID(mhi_cntrl) (mhi_cntrl->pm_state & mhi_cntrl->db_access)

View File

@ -15,6 +15,7 @@
#include <linux/skbuff.h>
#include <linux/slab.h>
#include "internal.h"
#include "trace.h"
int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl,
void __iomem *base, u32 offset, u32 *out)
@ -493,11 +494,8 @@ irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv)
state = mhi_get_mhi_state(mhi_cntrl);
ee = mhi_get_exec_env(mhi_cntrl);
dev_dbg(dev, "local ee: %s state: %s device ee: %s state: %s\n",
TO_MHI_EXEC_STR(mhi_cntrl->ee),
mhi_state_str(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(ee), mhi_state_str(state));
trace_mhi_intvec_states(mhi_cntrl, ee, state);
if (state == MHI_STATE_SYS_ERR) {
dev_dbg(dev, "System error detected\n");
pm_state = mhi_tryset_pm_state(mhi_cntrl,
@ -838,6 +836,8 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl,
while (dev_rp != local_rp) {
enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp);
trace_mhi_ctrl_event(mhi_cntrl, local_rp);
switch (type) {
case MHI_PKT_TYPE_BW_REQ_EVENT:
{
@ -1003,6 +1003,8 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl,
while (dev_rp != local_rp && event_quota > 0) {
enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp);
trace_mhi_data_event(mhi_cntrl, local_rp);
chan = MHI_TRE_GET_EV_CHID(local_rp);
WARN_ON(chan >= mhi_cntrl->max_chan);
@ -1243,6 +1245,7 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(info->len);
mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(bei, eot, eob, chain);
trace_mhi_gen_tre(mhi_cntrl, mhi_chan, mhi_tre);
/* increment WP */
mhi_add_ring_element(mhi_cntrl, tre_ring);
mhi_add_ring_element(mhi_cntrl, buf_ring);
@ -1337,9 +1340,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl,
enum mhi_cmd_type cmd = MHI_CMD_NOP;
int ret;
dev_dbg(dev, "%d: Updating channel state to: %s\n", mhi_chan->chan,
TO_CH_STATE_TYPE_STR(to_state));
trace_mhi_channel_command_start(mhi_cntrl, mhi_chan, to_state, TPS("Updating"));
switch (to_state) {
case MHI_CH_STATE_TYPE_RESET:
write_lock_irq(&mhi_chan->lock);
@ -1406,9 +1407,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl,
write_unlock_irq(&mhi_chan->lock);
}
dev_dbg(dev, "%d: Channel state change to %s successful\n",
mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state));
trace_mhi_channel_command_end(mhi_cntrl, mhi_chan, to_state, TPS("Updated"));
exit_channel_update:
mhi_cntrl->runtime_put(mhi_cntrl);
mhi_device_put(mhi_cntrl->mhi_dev);

View File

@ -538,7 +538,7 @@ static struct mhi_event_config mhi_telit_fn980_hw_v1_events[] = {
MHI_EVENT_CONFIG_HW_DATA(2, 2048, 101)
};
static struct mhi_controller_config modem_telit_fn980_hw_v1_config = {
static const struct mhi_controller_config modem_telit_fn980_hw_v1_config = {
.max_channels = 128,
.timeout_ms = 20000,
.num_channels = ARRAY_SIZE(mhi_telit_fn980_hw_v1_channels),

View File

@ -15,6 +15,7 @@
#include <linux/slab.h>
#include <linux/wait.h>
#include "internal.h"
#include "trace.h"
/*
* Not all MHI state transitions are synchronous. Transitions like Linkdown,
@ -36,7 +37,10 @@
* M0 <--> M0
* M0 -> FW_DL_ERR
* M0 -> M3_ENTER -> M3 -> M3_EXIT --> M0
* L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS --> POR
* L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS
* SYS_ERR_PROCESS -> SYS_ERR_FAIL
* SYS_ERR_FAIL -> SYS_ERR_DETECT
* SYS_ERR_PROCESS --> POR
* L2: SHUTDOWN_PROCESS -> LD_ERR_FATAL_DETECT
* SHUTDOWN_PROCESS -> DISABLE
* L3: LD_ERR_FATAL_DETECT <--> LD_ERR_FATAL_DETECT
@ -93,7 +97,12 @@ static const struct mhi_pm_transitions dev_state_transitions[] = {
},
{
MHI_PM_SYS_ERR_PROCESS,
MHI_PM_POR | MHI_PM_SHUTDOWN_PROCESS |
MHI_PM_POR | MHI_PM_SYS_ERR_FAIL | MHI_PM_SHUTDOWN_PROCESS |
MHI_PM_LD_ERR_FATAL_DETECT
},
{
MHI_PM_SYS_ERR_FAIL,
MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS |
MHI_PM_LD_ERR_FATAL_DETECT
},
/* L2 States */
@ -123,6 +132,7 @@ enum mhi_pm_state __must_check mhi_tryset_pm_state(struct mhi_controller *mhi_cn
if (unlikely(!(dev_state_transitions[index].to_states & state)))
return cur_state;
trace_mhi_tryset_pm_state(mhi_cntrl, state);
mhi_cntrl->pm_state = state;
return mhi_cntrl->pm_state;
}
@ -629,7 +639,13 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl)
!in_reset, timeout);
if (!ret || in_reset) {
dev_err(dev, "Device failed to exit MHI Reset state\n");
goto exit_sys_error_transition;
write_lock_irq(&mhi_cntrl->pm_lock);
cur_state = mhi_tryset_pm_state(mhi_cntrl,
MHI_PM_SYS_ERR_FAIL);
write_unlock_irq(&mhi_cntrl->pm_lock);
/* Shutdown may have occurred, otherwise cleanup now */
if (cur_state != MHI_PM_SYS_ERR_FAIL)
goto exit_sys_error_transition;
}
/*
@ -758,7 +774,6 @@ void mhi_pm_st_worker(struct work_struct *work)
struct mhi_controller *mhi_cntrl = container_of(work,
struct mhi_controller,
st_worker);
struct device *dev = &mhi_cntrl->mhi_dev->dev;
spin_lock_irq(&mhi_cntrl->transition_lock);
list_splice_tail_init(&mhi_cntrl->transition_list, &head);
@ -766,8 +781,8 @@ void mhi_pm_st_worker(struct work_struct *work)
list_for_each_entry_safe(itr, tmp, &head, node) {
list_del(&itr->node);
dev_dbg(dev, "Handling state transition: %s\n",
TO_DEV_STATE_TRANS_STR(itr->state));
trace_mhi_pm_st_transition(mhi_cntrl, itr->state);
switch (itr->state) {
case DEV_ST_TRANSITION_PBL:

View File

@ -0,0 +1,282 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM mhi_host
#if !defined(_TRACE_EVENT_MHI_HOST_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_EVENT_MHI_HOST_H
#include <linux/tracepoint.h>
#include <linux/trace_seq.h>
#include "../common.h"
#include "internal.h"
#undef mhi_state
#undef mhi_state_end
#define mhi_state(a, b) TRACE_DEFINE_ENUM(MHI_STATE_##a);
#define mhi_state_end(a, b) TRACE_DEFINE_ENUM(MHI_STATE_##a);
MHI_STATE_LIST
#undef mhi_state
#undef mhi_state_end
#define mhi_state(a, b) { MHI_STATE_##a, b },
#define mhi_state_end(a, b) { MHI_STATE_##a, b }
#undef mhi_pm_state
#undef mhi_pm_state_end
#define mhi_pm_state(a, b) TRACE_DEFINE_ENUM(MHI_PM_STATE_##a);
#define mhi_pm_state_end(a, b) TRACE_DEFINE_ENUM(MHI_PM_STATE_##a);
MHI_PM_STATE_LIST
#undef mhi_pm_state
#undef mhi_pm_state_end
#define mhi_pm_state(a, b) { MHI_PM_STATE_##a, b },
#define mhi_pm_state_end(a, b) { MHI_PM_STATE_##a, b }
#undef mhi_ee
#undef mhi_ee_end
#define mhi_ee(a, b) TRACE_DEFINE_ENUM(MHI_EE_##a);
#define mhi_ee_end(a, b) TRACE_DEFINE_ENUM(MHI_EE_##a);
MHI_EE_LIST
#undef mhi_ee
#undef mhi_ee_end
#define mhi_ee(a, b) { MHI_EE_##a, b },
#define mhi_ee_end(a, b) { MHI_EE_##a, b }
#undef ch_state_type
#undef ch_state_type_end
#define ch_state_type(a, b) TRACE_DEFINE_ENUM(MHI_CH_STATE_TYPE_##a);
#define ch_state_type_end(a, b) TRACE_DEFINE_ENUM(MHI_CH_STATE_TYPE_##a);
MHI_CH_STATE_TYPE_LIST
#undef ch_state_type
#undef ch_state_type_end
#define ch_state_type(a, b) { MHI_CH_STATE_TYPE_##a, b },
#define ch_state_type_end(a, b) { MHI_CH_STATE_TYPE_##a, b }
#undef dev_st_trans
#undef dev_st_trans_end
#define dev_st_trans(a, b) TRACE_DEFINE_ENUM(DEV_ST_TRANSITION_##a);
#define dev_st_trans_end(a, b) TRACE_DEFINE_ENUM(DEV_ST_TRANSITION_##a);
DEV_ST_TRANSITION_LIST
#undef dev_st_trans
#undef dev_st_trans_end
#define dev_st_trans(a, b) { DEV_ST_TRANSITION_##a, b },
#define dev_st_trans_end(a, b) { DEV_ST_TRANSITION_##a, b }
#define TPS(x) tracepoint_string(x)
TRACE_EVENT(mhi_gen_tre,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan,
struct mhi_ring_element *mhi_tre),
TP_ARGS(mhi_cntrl, mhi_chan, mhi_tre),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(int, ch_num)
__field(void *, wp)
__field(__le64, tre_ptr)
__field(__le32, dword0)
__field(__le32, dword1)
),
TP_fast_assign(
__assign_str(name, mhi_cntrl->mhi_dev->name);
__entry->ch_num = mhi_chan->chan;
__entry->wp = mhi_tre;
__entry->tre_ptr = mhi_tre->ptr;
__entry->dword0 = mhi_tre->dword[0];
__entry->dword1 = mhi_tre->dword[1];
),
TP_printk("%s: Chan: %d TRE: 0x%p TRE buf: 0x%llx DWORD0: 0x%08x DWORD1: 0x%08x\n",
__get_str(name), __entry->ch_num, __entry->wp, __entry->tre_ptr,
__entry->dword0, __entry->dword1)
);
TRACE_EVENT(mhi_intvec_states,
TP_PROTO(struct mhi_controller *mhi_cntrl, int dev_ee, int dev_state),
TP_ARGS(mhi_cntrl, dev_ee, dev_state),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(int, local_ee)
__field(int, state)
__field(int, dev_ee)
__field(int, dev_state)
),
TP_fast_assign(
__assign_str(name, mhi_cntrl->mhi_dev->name);
__entry->local_ee = mhi_cntrl->ee;
__entry->state = mhi_cntrl->dev_state;
__entry->dev_ee = dev_ee;
__entry->dev_state = dev_state;
),
TP_printk("%s: Local EE: %s State: %s Device EE: %s Dev State: %s\n",
__get_str(name),
__print_symbolic(__entry->local_ee, MHI_EE_LIST),
__print_symbolic(__entry->state, MHI_STATE_LIST),
__print_symbolic(__entry->dev_ee, MHI_EE_LIST),
__print_symbolic(__entry->dev_state, MHI_STATE_LIST))
);
TRACE_EVENT(mhi_tryset_pm_state,
TP_PROTO(struct mhi_controller *mhi_cntrl, int pm_state),
TP_ARGS(mhi_cntrl, pm_state),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(int, pm_state)
),
TP_fast_assign(
__assign_str(name, mhi_cntrl->mhi_dev->name);
if (pm_state)
pm_state = __fls(pm_state);
__entry->pm_state = pm_state;
),
TP_printk("%s: PM state: %s\n", __get_str(name),
__print_symbolic(__entry->pm_state, MHI_PM_STATE_LIST))
);
DECLARE_EVENT_CLASS(mhi_process_event_ring,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp),
TP_ARGS(mhi_cntrl, rp),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(__le32, dword0)
__field(__le32, dword1)
__field(int, state)
__field(__le64, ptr)
__field(void *, rp)
),
TP_fast_assign(
__assign_str(name, mhi_cntrl->mhi_dev->name);
__entry->rp = rp;
__entry->ptr = rp->ptr;
__entry->dword0 = rp->dword[0];
__entry->dword1 = rp->dword[1];
__entry->state = MHI_TRE_GET_EV_STATE(rp);
),
TP_printk("%s: TRE: 0x%p TRE buf: 0x%llx DWORD0: 0x%08x DWORD1: 0x%08x State: %s\n",
__get_str(name), __entry->rp, __entry->ptr, __entry->dword0,
__entry->dword1, __print_symbolic(__entry->state, MHI_STATE_LIST))
);
DEFINE_EVENT(mhi_process_event_ring, mhi_data_event,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp),
TP_ARGS(mhi_cntrl, rp)
);
DEFINE_EVENT(mhi_process_event_ring, mhi_ctrl_event,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp),
TP_ARGS(mhi_cntrl, rp)
);
DECLARE_EVENT_CLASS(mhi_update_channel_state,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state,
const char *reason),
TP_ARGS(mhi_cntrl, mhi_chan, state, reason),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(int, ch_num)
__field(int, state)
__field(const char *, reason)
),
TP_fast_assign(
__assign_str(name, mhi_cntrl->mhi_dev->name);
__entry->ch_num = mhi_chan->chan;
__entry->state = state;
__entry->reason = reason;
),
TP_printk("%s: chan%d: %s state to: %s\n",
__get_str(name), __entry->ch_num, __entry->reason,
__print_symbolic(__entry->state, MHI_CH_STATE_TYPE_LIST))
);
DEFINE_EVENT(mhi_update_channel_state, mhi_channel_command_start,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state,
const char *reason),
TP_ARGS(mhi_cntrl, mhi_chan, state, reason)
);
DEFINE_EVENT(mhi_update_channel_state, mhi_channel_command_end,
TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state,
const char *reason),
TP_ARGS(mhi_cntrl, mhi_chan, state, reason)
);
TRACE_EVENT(mhi_pm_st_transition,
TP_PROTO(struct mhi_controller *mhi_cntrl, int state),
TP_ARGS(mhi_cntrl, state),
TP_STRUCT__entry(
__string(name, mhi_cntrl->mhi_dev->name)
__field(int, state)
),
TP_fast_assign(
__assign_str(name, mhi_cntrl->mhi_dev->name);
__entry->state = state;
),
TP_printk("%s: Handling state transition: %s\n", __get_str(name),
__print_symbolic(__entry->state, DEV_ST_TRANSITION_LIST))
);
#endif
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH ../../drivers/bus/mhi/host
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
#include <trace/define_trace.h>

View File

@ -8,3 +8,7 @@
ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE=CDX_BUS
obj-$(CONFIG_CDX_BUS) += cdx.o controller/
ifdef CONFIG_GENERIC_MSI_IRQ
obj-$(CONFIG_CDX_BUS) += cdx_msi.o
endif

View File

@ -56,6 +56,7 @@
*/
#include <linux/init.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_device.h>
@ -302,8 +303,19 @@ static int cdx_probe(struct device *dev)
{
struct cdx_driver *cdx_drv = to_cdx_driver(dev->driver);
struct cdx_device *cdx_dev = to_cdx_device(dev);
struct cdx_controller *cdx = cdx_dev->cdx;
int error;
/*
* Setup MSI device data so that generic MSI alloc/free can
* be used by the device driver.
*/
if (cdx->msi_domain) {
error = msi_setup_device_data(&cdx_dev->dev);
if (error)
return error;
}
error = cdx_drv->probe(cdx_dev);
if (error) {
dev_err_probe(dev, error, "%s failed\n", __func__);
@ -787,6 +799,7 @@ int cdx_device_add(struct cdx_dev_params *dev_params)
/* Populate CDX dev params */
cdx_dev->req_id = dev_params->req_id;
cdx_dev->msi_dev_id = dev_params->msi_dev_id;
cdx_dev->vendor = dev_params->vendor;
cdx_dev->device = dev_params->device;
cdx_dev->subsystem_vendor = dev_params->subsys_vendor;
@ -804,12 +817,19 @@ int cdx_device_add(struct cdx_dev_params *dev_params)
cdx_dev->dev.bus = &cdx_bus_type;
cdx_dev->dev.dma_mask = &cdx_dev->dma_mask;
cdx_dev->dev.release = cdx_device_release;
cdx_dev->msi_write_pending = false;
mutex_init(&cdx_dev->irqchip_lock);
/* Set Name */
dev_set_name(&cdx_dev->dev, "cdx-%02x:%02x",
((cdx->id << CDX_CONTROLLER_ID_SHIFT) | (cdx_dev->bus_num & CDX_BUS_NUM_MASK)),
cdx_dev->dev_num);
if (cdx->msi_domain) {
cdx_dev->num_msi = dev_params->num_msi;
dev_set_msi_domain(&cdx_dev->dev, cdx->msi_domain);
}
ret = device_add(&cdx_dev->dev);
if (ret) {
dev_err(&cdx_dev->dev,

View File

@ -25,6 +25,8 @@
* @req_id: Requestor ID associated with CDX device
* @class: Class of the CDX Device
* @revision: Revision of the CDX device
* @msi_dev_id: MSI device ID associated with CDX device
* @num_msi: Number of MSI's supported by the device
*/
struct cdx_dev_params {
struct cdx_controller *cdx;
@ -40,6 +42,8 @@ struct cdx_dev_params {
u32 req_id;
u32 class;
u8 revision;
u32 msi_dev_id;
u32 num_msi;
};
/**
@ -79,4 +83,12 @@ int cdx_device_add(struct cdx_dev_params *dev_params);
*/
struct device *cdx_bus_add(struct cdx_controller *cdx, u8 bus_num);
/**
* cdx_msi_domain_init - Init the CDX bus MSI domain.
* @dev: Device of the CDX bus controller
*
* Return: CDX MSI domain, NULL on failure
*/
struct irq_domain *cdx_msi_domain_init(struct device *dev);
#endif /* _CDX_H_ */

192
drivers/cdx/cdx_msi.c Normal file
View File

@ -0,0 +1,192 @@
// SPDX-License-Identifier: GPL-2.0
/*
* AMD CDX bus driver MSI support
*
* Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
*/
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/msi.h>
#include <linux/cdx/cdx_bus.h>
#include "cdx.h"
static void cdx_msi_write_msg(struct irq_data *irq_data, struct msi_msg *msg)
{
struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data);
struct cdx_device *cdx_dev = to_cdx_device(msi_desc->dev);
/* We would not operate on msg here rather we wait for irq_bus_sync_unlock()
* to be called from preemptible task context.
*/
msi_desc->msg = *msg;
cdx_dev->msi_write_pending = true;
}
static void cdx_msi_write_irq_lock(struct irq_data *irq_data)
{
struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data);
struct cdx_device *cdx_dev = to_cdx_device(msi_desc->dev);
mutex_lock(&cdx_dev->irqchip_lock);
}
static void cdx_msi_write_irq_unlock(struct irq_data *irq_data)
{
struct msi_desc *msi_desc = irq_data_get_msi_desc(irq_data);
struct cdx_device *cdx_dev = to_cdx_device(msi_desc->dev);
struct cdx_controller *cdx = cdx_dev->cdx;
struct cdx_device_config dev_config;
if (!cdx_dev->msi_write_pending) {
mutex_unlock(&cdx_dev->irqchip_lock);
return;
}
cdx_dev->msi_write_pending = false;
mutex_unlock(&cdx_dev->irqchip_lock);
dev_config.msi.msi_index = msi_desc->msi_index;
dev_config.msi.data = msi_desc->msg.data;
dev_config.msi.addr = ((u64)(msi_desc->msg.address_hi) << 32) | msi_desc->msg.address_lo;
/*
* dev_configure() is a controller callback which can interact with
* Firmware or other entities, and can sleep, so invoke this function
* outside of the mutex held region.
*/
dev_config.type = CDX_DEV_MSI_CONF;
if (cdx->ops->dev_configure)
cdx->ops->dev_configure(cdx, cdx_dev->bus_num, cdx_dev->dev_num, &dev_config);
}
int cdx_enable_msi(struct cdx_device *cdx_dev)
{
struct cdx_controller *cdx = cdx_dev->cdx;
struct cdx_device_config dev_config;
dev_config.type = CDX_DEV_MSI_ENABLE;
dev_config.msi_enable = true;
if (cdx->ops->dev_configure) {
return cdx->ops->dev_configure(cdx, cdx_dev->bus_num, cdx_dev->dev_num,
&dev_config);
}
return -EOPNOTSUPP;
}
EXPORT_SYMBOL_GPL(cdx_enable_msi);
void cdx_disable_msi(struct cdx_device *cdx_dev)
{
struct cdx_controller *cdx = cdx_dev->cdx;
struct cdx_device_config dev_config;
dev_config.type = CDX_DEV_MSI_ENABLE;
dev_config.msi_enable = false;
if (cdx->ops->dev_configure)
cdx->ops->dev_configure(cdx, cdx_dev->bus_num, cdx_dev->dev_num, &dev_config);
}
EXPORT_SYMBOL_GPL(cdx_disable_msi);
static struct irq_chip cdx_msi_irq_chip = {
.name = "CDX-MSI",
.irq_mask = irq_chip_mask_parent,
.irq_unmask = irq_chip_unmask_parent,
.irq_eoi = irq_chip_eoi_parent,
.irq_set_affinity = msi_domain_set_affinity,
.irq_write_msi_msg = cdx_msi_write_msg,
.irq_bus_lock = cdx_msi_write_irq_lock,
.irq_bus_sync_unlock = cdx_msi_write_irq_unlock
};
/* Convert an msi_desc to a unique identifier within the domain. */
static irq_hw_number_t cdx_domain_calc_hwirq(struct cdx_device *dev,
struct msi_desc *desc)
{
return ((irq_hw_number_t)dev->msi_dev_id << 10) | desc->msi_index;
}
static void cdx_msi_set_desc(msi_alloc_info_t *arg, struct msi_desc *desc)
{
arg->desc = desc;
arg->hwirq = cdx_domain_calc_hwirq(to_cdx_device(desc->dev), desc);
}
static int cdx_msi_prepare(struct irq_domain *msi_domain,
struct device *dev,
int nvec, msi_alloc_info_t *info)
{
struct cdx_device *cdx_dev = to_cdx_device(dev);
struct device *parent = cdx_dev->cdx->dev;
struct msi_domain_info *msi_info;
u32 dev_id;
int ret;
/* Retrieve device ID from requestor ID using parent device */
ret = of_map_id(parent->of_node, cdx_dev->msi_dev_id, "msi-map", "msi-map-mask",
NULL, &dev_id);
if (ret) {
dev_err(dev, "of_map_id failed for MSI: %d\n", ret);
return ret;
}
#ifdef GENERIC_MSI_DOMAIN_OPS
/* Set the device Id to be passed to the GIC-ITS */
info->scratchpad[0].ul = dev_id;
#endif
msi_info = msi_get_domain_info(msi_domain->parent);
return msi_info->ops->msi_prepare(msi_domain->parent, dev, nvec, info);
}
static struct msi_domain_ops cdx_msi_ops = {
.msi_prepare = cdx_msi_prepare,
.set_desc = cdx_msi_set_desc
};
static struct msi_domain_info cdx_msi_domain_info = {
.ops = &cdx_msi_ops,
.chip = &cdx_msi_irq_chip,
.flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
MSI_FLAG_ALLOC_SIMPLE_MSI_DESCS | MSI_FLAG_FREE_MSI_DESCS
};
struct irq_domain *cdx_msi_domain_init(struct device *dev)
{
struct device_node *np = dev->of_node;
struct fwnode_handle *fwnode_handle;
struct irq_domain *cdx_msi_domain;
struct device_node *parent_node;
struct irq_domain *parent;
fwnode_handle = of_node_to_fwnode(np);
parent_node = of_parse_phandle(np, "msi-map", 1);
if (!parent_node) {
dev_err(dev, "msi-map not present on cdx controller\n");
return NULL;
}
parent = irq_find_matching_fwnode(of_node_to_fwnode(parent_node), DOMAIN_BUS_NEXUS);
if (!parent || !msi_get_domain_info(parent)) {
dev_err(dev, "unable to locate ITS domain\n");
return NULL;
}
cdx_msi_domain = msi_create_irq_domain(fwnode_handle, &cdx_msi_domain_info, parent);
if (!cdx_msi_domain) {
dev_err(dev, "unable to create CDX-MSI domain\n");
return NULL;
}
dev_dbg(dev, "CDX-MSI domain created\n");
return cdx_msi_domain;
}
EXPORT_SYMBOL_NS_GPL(cdx_msi_domain_init, CDX_BUS_CONTROLLER);

View File

@ -9,6 +9,7 @@ if CDX_BUS
config CDX_CONTROLLER
tristate "CDX bus controller"
select GENERIC_MSI_IRQ
select REMOTEPROC
select RPMSG
help

View File

@ -9,6 +9,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/cdx/cdx_bus.h>
#include <linux/irqdomain.h>
#include "cdx_controller.h"
#include "../cdx.h"
@ -60,9 +61,19 @@ static int cdx_configure_device(struct cdx_controller *cdx,
u8 bus_num, u8 dev_num,
struct cdx_device_config *dev_config)
{
u16 msi_index;
int ret = 0;
u32 data;
u64 addr;
switch (dev_config->type) {
case CDX_DEV_MSI_CONF:
msi_index = dev_config->msi.msi_index;
data = dev_config->msi.data;
addr = dev_config->msi.addr;
ret = cdx_mcdi_write_msi(cdx->priv, bus_num, dev_num, msi_index, addr, data);
break;
case CDX_DEV_RESET_CONF:
ret = cdx_mcdi_reset_device(cdx->priv, bus_num, dev_num);
break;
@ -70,6 +81,9 @@ static int cdx_configure_device(struct cdx_controller *cdx,
ret = cdx_mcdi_bus_master_enable(cdx->priv, bus_num, dev_num,
dev_config->bus_master_enable);
break;
case CDX_DEV_MSI_ENABLE:
ret = cdx_mcdi_msi_enable(cdx->priv, bus_num, dev_num, dev_config->msi_enable);
break;
default:
ret = -EINVAL;
}
@ -178,6 +192,14 @@ static int xlnx_cdx_probe(struct platform_device *pdev)
cdx->priv = cdx_mcdi;
cdx->ops = &cdx_ops;
/* Create MSI domain */
cdx->msi_domain = cdx_msi_domain_init(&pdev->dev);
if (!cdx->msi_domain) {
dev_err(&pdev->dev, "cdx_msi_domain_init() failed");
ret = -ENODEV;
goto cdx_msi_fail;
}
ret = cdx_setup_rpmsg(pdev);
if (ret) {
if (ret != -EPROBE_DEFER)
@ -189,6 +211,8 @@ static int xlnx_cdx_probe(struct platform_device *pdev)
return 0;
cdx_rpmsg_fail:
irq_domain_remove(cdx->msi_domain);
cdx_msi_fail:
kfree(cdx);
cdx_alloc_fail:
cdx_mcdi_finish(cdx_mcdi);
@ -205,6 +229,7 @@ static int xlnx_cdx_remove(struct platform_device *pdev)
cdx_destroy_rpmsg(pdev);
irq_domain_remove(cdx->msi_domain);
kfree(cdx);
cdx_mcdi_finish(cdx_mcdi);

View File

@ -455,6 +455,12 @@
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID_OFST 84
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID_LEN 4
/* MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2 msgresponse */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_LEN 92
/* Requester ID used by device for GIC ITS DeviceID */
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_REQUESTER_DEVICE_ID_OFST 88
#define MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_REQUESTER_DEVICE_ID_LEN 4
/***********************************/
/*
* MC_CMD_CDX_BUS_DOWN
@ -616,6 +622,64 @@
#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MMIO_REGIONS_ENABLE_LBN 2
#define MC_CMD_CDX_DEVICE_CONTROL_GET_OUT_MMIO_REGIONS_ENABLE_WIDTH 1
/***********************************/
/*
* MC_CMD_CDX_DEVICE_WRITE_MSI_MSG
* Populates the MSI message to be used by the hardware to raise the specified
* interrupt vector. Versal-net implementation specific limitations are that
* only 4 CDX devices with MSI interrupt capability are supported and all
* vectors within a device must use the same write address. The command will
* return EINVAL if any of these limitations is violated.
*/
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG 0x9
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_MSGSET 0x9
#undef MC_CMD_0x9_PRIVILEGE_CTG
#define MC_CMD_0x9_PRIVILEGE_CTG SRIOV_CTG_ADMIN
/* MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN msgrequest */
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_LEN 28
/* Device bus number, in range 0 to BUS_COUNT-1 */
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_BUS_OFST 0
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_BUS_LEN 4
/* Device number relative to the bus, in range 0 to DEVICE_COUNT-1 for that bus */
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_DEVICE_OFST 4
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_DEVICE_LEN 4
/*
* Device-relative MSI vector number. Must be < MSI_COUNT reported for the
* device.
*/
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_VECTOR_OFST 8
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_VECTOR_LEN 4
/* Reserved (alignment) */
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_RESERVED_OFST 12
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_RESERVED_LEN 4
/*
* MSI address to be used by the hardware. Typically, on ARM systems this
* address is translated by the IOMMU (if enabled) and it is the responsibility
* of the entity managing the IOMMU (APU kernel) to supply the correct IOVA
* here.
*/
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_OFST 16
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LEN 8
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_OFST 16
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_LEN 4
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_LBN 128
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_LO_WIDTH 32
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_OFST 20
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_LEN 4
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_LBN 160
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS_HI_WIDTH 32
/*
* MSI data to be used by the hardware. On versal-net, only the lower 16-bits
* are used, the remaining bits are ignored and should be set to zero.
*/
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_DATA_OFST 24
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_DATA_LEN 4
/* MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_OUT msgresponse */
#define MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_OUT_LEN 0
/***********************************/
/* MC_CMD_V2_EXTN - Encapsulation for a v2 extended command */
#define MC_CMD_V2_EXTN 0x7f

View File

@ -49,7 +49,7 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx,
u8 bus_num, u8 dev_num,
struct cdx_dev_params *dev_params)
{
MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_LEN);
MCDI_DECLARE_BUF(outbuf, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_LEN);
MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_IN_LEN);
struct resource *res = &dev_params->res[0];
size_t outlen;
@ -64,7 +64,7 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx,
if (ret)
return ret;
if (outlen != MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_LEN)
if (outlen != MC_CMD_CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_LEN)
return -EIO;
dev_params->bus_num = bus_num;
@ -73,6 +73,9 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx,
req_id = MCDI_DWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_REQUESTER_ID);
dev_params->req_id = req_id;
dev_params->msi_dev_id = MCDI_DWORD(outbuf,
CDX_BUS_GET_DEVICE_CONFIG_OUT_V2_REQUESTER_DEVICE_ID);
dev_params->res_count = 0;
if (MCDI_QWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MMIO_REGION0_SIZE) != 0) {
res[dev_params->res_count].start =
@ -127,6 +130,7 @@ int cdx_mcdi_get_dev_config(struct cdx_mcdi *cdx,
dev_params->class = MCDI_DWORD(outbuf,
CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_CLASS) & 0xFFFFFF;
dev_params->revision = MCDI_BYTE(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_DEVICE_REVISION);
dev_params->num_msi = MCDI_DWORD(outbuf, CDX_BUS_GET_DEVICE_CONFIG_OUT_MSI_COUNT);
return 0;
}
@ -155,6 +159,24 @@ int cdx_mcdi_bus_disable(struct cdx_mcdi *cdx, u8 bus_num)
return ret;
}
int cdx_mcdi_write_msi(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num,
u32 msi_vector, u64 msi_address, u32 msi_data)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_DEVICE_WRITE_MSI_MSG_IN_LEN);
int ret;
MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_BUS, bus_num);
MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_DEVICE, dev_num);
MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_VECTOR, msi_vector);
MCDI_SET_QWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_ADDRESS, msi_address);
MCDI_SET_DWORD(inbuf, CDX_DEVICE_WRITE_MSI_MSG_IN_MSI_DATA, msi_data);
ret = cdx_mcdi_rpc(cdx, MC_CMD_CDX_DEVICE_WRITE_MSI_MSG, inbuf, sizeof(inbuf),
NULL, 0, NULL);
return ret;
}
int cdx_mcdi_reset_device(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num)
{
MCDI_DECLARE_BUF(inbuf, MC_CMD_CDX_DEVICE_RESET_IN_LEN);
@ -226,3 +248,10 @@ int cdx_mcdi_bus_master_enable(struct cdx_mcdi *cdx, u8 bus_num,
return cdx_mcdi_ctrl_flag_set(cdx, bus_num, dev_num, enable,
MC_CMD_CDX_DEVICE_CONTROL_SET_IN_BUS_MASTER_ENABLE_LBN);
}
int cdx_mcdi_msi_enable(struct cdx_mcdi *cdx, u8 bus_num,
u8 dev_num, bool enable)
{
return cdx_mcdi_ctrl_flag_set(cdx, bus_num, dev_num, enable,
MC_CMD_CDX_DEVICE_CONTROL_SET_IN_MSI_ENABLE_LBN);
}

View File

@ -65,6 +65,26 @@ int cdx_mcdi_bus_enable(struct cdx_mcdi *cdx, u8 bus_num);
*/
int cdx_mcdi_bus_disable(struct cdx_mcdi *cdx, u8 bus_num);
/**
* cdx_mcdi_write_msi - Write MSI configuration for CDX device
* @cdx: pointer to MCDI interface.
* @bus_num: Bus number.
* @dev_num: Device number.
* @msi_vector: Device-relative MSI vector number.
* Must be < MSI_COUNT reported for the device.
* @msi_address: MSI address to be used by the hardware. Typically, on ARM
* systems this address is translated by the IOMMU (if enabled) and
* it is the responsibility of the entity managing the IOMMU (APU kernel)
* to supply the correct IOVA here.
* @msi_data: MSI data to be used by the hardware. On versal-net, only the
* lower 16-bits are used, the remaining bits are ignored and should be
* set to zero.
*
* Return: 0 on success, <0 on failure
*/
int cdx_mcdi_write_msi(struct cdx_mcdi *cdx, u8 bus_num, u8 dev_num,
u32 msi_vector, u64 msi_address, u32 msi_data);
/**
* cdx_mcdi_reset_device - Reset cdx device represented by bus_num:dev_num
* @cdx: pointer to MCDI interface.
@ -89,4 +109,17 @@ int cdx_mcdi_reset_device(struct cdx_mcdi *cdx,
int cdx_mcdi_bus_master_enable(struct cdx_mcdi *cdx, u8 bus_num,
u8 dev_num, bool enable);
/**
* cdx_mcdi_msi_enable - Enable/Disable MSIs for cdx device represented
* by bus_num:dev_num
* @cdx: pointer to MCDI interface.
* @bus_num: Bus number.
* @dev_num: Device number.
* @enable: Enable msi's if set, disable otherwise.
*
* Return: 0 on success, <0 on failure
*/
int cdx_mcdi_msi_enable(struct cdx_mcdi *cdx, u8 bus_num,
u8 dev_num, bool enable);
#endif /* CDX_MCDI_FUNCTIONS_H */

View File

@ -87,7 +87,6 @@ struct hpets {
struct hpets *hp_next;
struct hpet __iomem *hp_hpet;
unsigned long hp_hpet_phys;
struct clocksource *hp_clocksource;
unsigned long long hp_tick_freq;
unsigned long hp_delta;
unsigned int hp_ntimer;

View File

@ -636,11 +636,11 @@ static int hwicap_setup(struct platform_device *pdev, int id,
retval = -ENOMEM;
goto failed;
}
dev_set_drvdata(dev, (void *)drvdata);
dev_set_drvdata(dev, drvdata);
drvdata->base_address = devm_platform_ioremap_resource(pdev, 0);
if (!drvdata->base_address) {
retval = -ENODEV;
if (IS_ERR(drvdata->base_address)) {
retval = PTR_ERR(drvdata->base_address);
goto failed;
}

View File

@ -64,19 +64,17 @@ static int xilly_drv_probe(struct platform_device *op)
return xillybus_endpoint_discovery(endpoint);
}
static int xilly_drv_remove(struct platform_device *op)
static void xilly_drv_remove(struct platform_device *op)
{
struct device *dev = &op->dev;
struct xilly_endpoint *endpoint = dev_get_drvdata(dev);
xillybus_endpoint_remove(endpoint);
return 0;
}
static struct platform_driver xillybus_platform_driver = {
.probe = xilly_drv_probe,
.remove = xilly_drv_remove,
.remove_new = xilly_drv_remove,
.driver = {
.name = xillyname,
.of_match_table = xillybus_of_match,

View File

@ -177,7 +177,6 @@ static int das08_ai_insn_read(struct comedi_device *dev,
int ret;
chan = CR_CHAN(insn->chanspec);
range = CR_RANGE(insn->chanspec);
/* clear crap */
inb(dev->iobase + DAS08_AI_LSB_REG);

View File

@ -123,7 +123,7 @@ static int dio_bus_match(struct device *dev, struct device_driver *drv)
}
struct bus_type dio_bus_type = {
const struct bus_type dio_bus_type = {
.name = "dio",
.match = dio_bus_match,
.probe = dio_device_probe,

View File

@ -3,6 +3,7 @@
* Xilinx Zynq MPSoC Firmware layer
*
* Copyright (C) 2014-2022 Xilinx, Inc.
* Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc.
*
* Michal Simek <michal.simek@amd.com>
* Davorin Mista <davorin.mista@aggios.com>
@ -1384,6 +1385,30 @@ int zynqmp_pm_aes_engine(const u64 address, u32 *out)
}
EXPORT_SYMBOL_GPL(zynqmp_pm_aes_engine);
/**
* zynqmp_pm_efuse_access - Provides access to efuse memory.
* @address: Address of the efuse params structure
* @out: Returned output value
*
* Return: Returns status, either success or error code.
*/
int zynqmp_pm_efuse_access(const u64 address, u32 *out)
{
u32 ret_payload[PAYLOAD_ARG_CNT];
int ret;
if (!out)
return -EINVAL;
ret = zynqmp_pm_invoke_fn(PM_EFUSE_ACCESS, ret_payload, 2,
upper_32_bits(address),
lower_32_bits(address));
*out = ret_payload[1];
return ret;
}
EXPORT_SYMBOL_GPL(zynqmp_pm_efuse_access);
/**
* zynqmp_pm_sha_hash - Access the SHA engine to calculate the hash
* @address: Address of the data/ Address of output buffer where

View File

@ -327,7 +327,7 @@ static struct attribute *dfl_dev_attrs[] = {
};
ATTRIBUTE_GROUPS(dfl_dev);
static struct bus_type dfl_bus_type = {
static const struct bus_type dfl_bus_type = {
.name = "dfl",
.match = dfl_bus_match,
.probe = dfl_bus_probe,

View File

@ -30,7 +30,7 @@ int fpga_bridge_enable(struct fpga_bridge *bridge)
{
dev_dbg(&bridge->dev, "enable\n");
if (bridge->br_ops && bridge->br_ops->enable_set)
if (bridge->br_ops->enable_set)
return bridge->br_ops->enable_set(bridge, 1);
return 0;
@ -48,7 +48,7 @@ int fpga_bridge_disable(struct fpga_bridge *bridge)
{
dev_dbg(&bridge->dev, "disable\n");
if (bridge->br_ops && bridge->br_ops->enable_set)
if (bridge->br_ops->enable_set)
return bridge->br_ops->enable_set(bridge, 0);
return 0;
@ -296,7 +296,7 @@ static ssize_t state_show(struct device *dev,
struct fpga_bridge *bridge = to_fpga_bridge(dev);
int state = 1;
if (bridge->br_ops && bridge->br_ops->enable_show) {
if (bridge->br_ops->enable_show) {
state = bridge->br_ops->enable_show(bridge);
if (state < 0)
return state;
@ -401,7 +401,7 @@ void fpga_bridge_unregister(struct fpga_bridge *bridge)
* If the low level driver provides a method for putting bridge into
* a desired state upon unregister, do it.
*/
if (bridge->br_ops && bridge->br_ops->fpga_bridge_remove)
if (bridge->br_ops->fpga_bridge_remove)
bridge->br_ops->fpga_bridge_remove(bridge);
device_unregister(&bridge->dev);

View File

@ -166,7 +166,7 @@ static const struct dev_pm_ops gb_bundle_pm_ops = {
SET_RUNTIME_PM_OPS(gb_bundle_suspend, gb_bundle_resume, gb_bundle_idle)
};
struct device_type greybus_bundle_type = {
const struct device_type greybus_bundle_type = {
.name = "greybus_bundle",
.release = gb_bundle_release,
.pm = &gb_bundle_pm_ops,

View File

@ -436,7 +436,7 @@ static void gb_control_release(struct device *dev)
kfree(control);
}
struct device_type greybus_control_type = {
const struct device_type greybus_control_type = {
.name = "greybus_control",
.release = gb_control_release,
};

View File

@ -27,6 +27,36 @@ int greybus_disabled(void)
}
EXPORT_SYMBOL_GPL(greybus_disabled);
static int is_gb_host_device(const struct device *dev)
{
return dev->type == &greybus_hd_type;
}
static int is_gb_module(const struct device *dev)
{
return dev->type == &greybus_module_type;
}
static int is_gb_interface(const struct device *dev)
{
return dev->type == &greybus_interface_type;
}
static int is_gb_control(const struct device *dev)
{
return dev->type == &greybus_control_type;
}
static int is_gb_bundle(const struct device *dev)
{
return dev->type == &greybus_bundle_type;
}
static int is_gb_svc(const struct device *dev)
{
return dev->type == &greybus_svc_type;
}
static bool greybus_match_one_id(struct gb_bundle *bundle,
const struct greybus_bundle_id *id)
{
@ -155,7 +185,7 @@ static void greybus_shutdown(struct device *dev)
}
}
struct bus_type greybus_bus_type = {
const struct bus_type greybus_bus_type = {
.name = "greybus",
.match = greybus_match_device,
.uevent = greybus_uevent,

View File

@ -513,16 +513,16 @@ static int es2_cport_allocate(struct gb_host_device *hd, int cport_id,
if (cport_id < 0) {
ida_start = 0;
ida_end = hd->num_cports;
ida_end = hd->num_cports - 1;
} else if (cport_id < hd->num_cports) {
ida_start = cport_id;
ida_end = cport_id + 1;
ida_end = cport_id;
} else {
dev_err(&hd->dev, "cport %d not available\n", cport_id);
return -EINVAL;
}
return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL);
return ida_alloc_range(id_map, ida_start, ida_end, GFP_KERNEL);
}
static void es2_cport_release(struct gb_host_device *hd, u16 cport_id)
@ -535,7 +535,7 @@ static void es2_cport_release(struct gb_host_device *hd, u16 cport_id)
return;
}
ida_simple_remove(&hd->cport_id_map, cport_id);
ida_free(&hd->cport_id_map, cport_id);
}
static int cport_enable(struct gb_host_device *hd, u16 cport_id,

View File

@ -50,7 +50,7 @@ int gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id)
struct ida *id_map = &hd->cport_id_map;
int ret;
ret = ida_simple_get(id_map, cport_id, cport_id + 1, GFP_KERNEL);
ret = ida_alloc_range(id_map, cport_id, cport_id, GFP_KERNEL);
if (ret < 0) {
dev_err(&hd->dev, "failed to reserve cport %u\n", cport_id);
return ret;
@ -64,7 +64,7 @@ void gb_hd_cport_release_reserved(struct gb_host_device *hd, u16 cport_id)
{
struct ida *id_map = &hd->cport_id_map;
ida_simple_remove(id_map, cport_id);
ida_free(id_map, cport_id);
}
EXPORT_SYMBOL_GPL(gb_hd_cport_release_reserved);
@ -80,16 +80,16 @@ int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id,
if (cport_id < 0) {
ida_start = 0;
ida_end = hd->num_cports;
ida_end = hd->num_cports - 1;
} else if (cport_id < hd->num_cports) {
ida_start = cport_id;
ida_end = cport_id + 1;
ida_end = cport_id;
} else {
dev_err(&hd->dev, "cport %d not available\n", cport_id);
return -EINVAL;
}
return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL);
return ida_alloc_range(id_map, ida_start, ida_end, GFP_KERNEL);
}
/* Locking: Caller guarantees serialisation */
@ -100,7 +100,7 @@ void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id)
return;
}
ida_simple_remove(&hd->cport_id_map, cport_id);
ida_free(&hd->cport_id_map, cport_id);
}
static void gb_hd_release(struct device *dev)
@ -111,12 +111,12 @@ static void gb_hd_release(struct device *dev)
if (hd->svc)
gb_svc_put(hd->svc);
ida_simple_remove(&gb_hd_bus_id_map, hd->bus_id);
ida_free(&gb_hd_bus_id_map, hd->bus_id);
ida_destroy(&hd->cport_id_map);
kfree(hd);
}
struct device_type greybus_hd_type = {
const struct device_type greybus_hd_type = {
.name = "greybus_host_device",
.release = gb_hd_release,
};
@ -162,7 +162,7 @@ struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver,
if (!hd)
return ERR_PTR(-ENOMEM);
ret = ida_simple_get(&gb_hd_bus_id_map, 1, 0, GFP_KERNEL);
ret = ida_alloc_min(&gb_hd_bus_id_map, 1, GFP_KERNEL);
if (ret < 0) {
kfree(hd);
return ERR_PTR(ret);

View File

@ -131,9 +131,8 @@ static int gb_interface_route_create(struct gb_interface *intf)
int ret;
/* Allocate an interface device id. */
ret = ida_simple_get(&svc->device_id_map,
GB_SVC_DEVICE_ID_MIN, GB_SVC_DEVICE_ID_MAX + 1,
GFP_KERNEL);
ret = ida_alloc_range(&svc->device_id_map, GB_SVC_DEVICE_ID_MIN,
GB_SVC_DEVICE_ID_MAX, GFP_KERNEL);
if (ret < 0) {
dev_err(&intf->dev, "failed to allocate device id: %d\n", ret);
return ret;
@ -165,7 +164,7 @@ static int gb_interface_route_create(struct gb_interface *intf)
* XXX anymore.
*/
err_ida_remove:
ida_simple_remove(&svc->device_id_map, device_id);
ida_free(&svc->device_id_map, device_id);
return ret;
}
@ -178,7 +177,7 @@ static void gb_interface_route_destroy(struct gb_interface *intf)
return;
gb_svc_route_destroy(svc, svc->ap_intf_id, intf->interface_id);
ida_simple_remove(&svc->device_id_map, intf->device_id);
ida_free(&svc->device_id_map, intf->device_id);
intf->device_id = GB_INTERFACE_DEVICE_ID_BAD;
}
@ -765,7 +764,7 @@ static const struct dev_pm_ops gb_interface_pm_ops = {
gb_interface_runtime_idle)
};
struct device_type greybus_interface_type = {
const struct device_type greybus_interface_type = {
.name = "greybus_interface",
.release = gb_interface_release,
.pm = &gb_interface_pm_ops,

View File

@ -81,7 +81,7 @@ static void gb_module_release(struct device *dev)
kfree(module);
}
struct device_type greybus_module_type = {
const struct device_type greybus_module_type = {
.name = "greybus_module",
.release = gb_module_release,
};

View File

@ -1305,7 +1305,7 @@ static void gb_svc_release(struct device *dev)
kfree(svc);
}
struct device_type greybus_svc_type = {
const struct device_type greybus_svc_type = {
.name = "greybus_svc",
.release = gb_svc_release,
};

View File

@ -2,6 +2,26 @@
#
# Makefile for CoreSight drivers.
#
# Current W=1 warnings
subdir-ccflags-y += -Wextra -Wunused -Wno-unused-parameter
subdir-ccflags-y += -Wmissing-declarations
subdir-ccflags-y += -Wmissing-format-attribute
subdir-ccflags-y += -Wmissing-prototypes
subdir-ccflags-y += -Wold-style-definition
subdir-ccflags-y += -Wmissing-include-dirs
subdir-ccflags-y += -Wno-sign-compare
condflags := \
$(call cc-option, -Wrestrict) \
$(call cc-option, -Wunused-but-set-variable) \
$(call cc-option, -Wunused-const-variable) \
$(call cc-option, -Wpacked-not-aligned) \
$(call cc-option, -Wformat-overflow) \
$(call cc-option, -Wformat-truncation) \
$(call cc-option, -Wstringop-overflow) \
$(call cc-option, -Wstringop-truncation)
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 \

View File

@ -9,6 +9,7 @@
/* ETMv4 includes and features */
#if IS_ENABLED(CONFIG_CORESIGHT_SOURCE_ETM4X)
#include "coresight-etm4x-cfg.h"
#include "coresight-cfg-preload.h"
/* preload configurations and features */

View File

@ -9,7 +9,6 @@
#include <linux/types.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/idr.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/slab.h>
@ -25,15 +24,12 @@
#include "coresight-priv.h"
#include "coresight-syscfg.h"
static DEFINE_MUTEX(coresight_mutex);
static DEFINE_PER_CPU(struct coresight_device *, csdev_sink);
/*
* Use IDR to map the hash of the source's device name
* to the pointer of path for the source. The idr is for
* the sources which aren't associated with CPU.
* Mutex used to lock all sysfs enable and disable actions and loading and
* unloading devices by the Coresight core.
*/
static DEFINE_IDR(path_idr);
DEFINE_MUTEX(coresight_mutex);
static DEFINE_PER_CPU(struct coresight_device *, csdev_sink);
/**
* struct coresight_node - elements of a path, from source to sink
@ -45,12 +41,6 @@ struct coresight_node {
struct list_head link;
};
/*
* 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);
/*
* When losing synchronisation a new barrier packet needs to be inserted at the
* beginning of the data collected in a buffer. That way the decoder knows that
@ -61,34 +51,6 @@ EXPORT_SYMBOL_GPL(coresight_barrier_pkt);
static const struct cti_assoc_op *cti_assoc_ops;
ssize_t coresight_simple_show_pair(struct device *_dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev);
struct cs_pair_attribute *cs_attr = container_of(attr, struct cs_pair_attribute, attr);
u64 val;
pm_runtime_get_sync(_dev->parent);
val = csdev_access_relaxed_read_pair(&csdev->access, cs_attr->lo_off, cs_attr->hi_off);
pm_runtime_put_sync(_dev->parent);
return sysfs_emit(buf, "0x%llx\n", val);
}
EXPORT_SYMBOL_GPL(coresight_simple_show_pair);
ssize_t coresight_simple_show32(struct device *_dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev);
struct cs_off_attribute *cs_attr = container_of(attr, struct cs_off_attribute, attr);
u64 val;
pm_runtime_get_sync(_dev->parent);
val = csdev_access_relaxed_read32(&csdev->access, cs_attr->off);
pm_runtime_put_sync(_dev->parent);
return sysfs_emit(buf, "0x%llx\n", val);
}
EXPORT_SYMBOL_GPL(coresight_simple_show32);
void coresight_set_cti_ops(const struct cti_assoc_op *cti_op)
{
cti_assoc_ops = cti_op;
@ -279,42 +241,18 @@ EXPORT_SYMBOL_GPL(coresight_add_helper);
static int coresight_enable_sink(struct coresight_device *csdev,
enum cs_mode mode, void *data)
{
int ret;
/*
* We need to make sure the "new" session is compatible with the
* existing "mode" of operation.
*/
if (!sink_ops(csdev)->enable)
return -EINVAL;
ret = sink_ops(csdev)->enable(csdev, mode, data);
if (ret)
return ret;
csdev->enable = true;
return 0;
return sink_ops(csdev)->enable(csdev, mode, data);
}
static void coresight_disable_sink(struct coresight_device *csdev)
{
int ret;
if (!sink_ops(csdev)->disable)
return;
ret = sink_ops(csdev)->disable(csdev);
if (ret)
return;
csdev->enable = false;
sink_ops(csdev)->disable(csdev);
}
static int coresight_enable_link(struct coresight_device *csdev,
struct coresight_device *parent,
struct coresight_device *child)
{
int ret = 0;
int link_subtype;
struct coresight_connection *inconn, *outconn;
@ -330,21 +268,13 @@ static int coresight_enable_link(struct coresight_device *csdev,
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT && IS_ERR(outconn))
return PTR_ERR(outconn);
if (link_ops(csdev)->enable) {
ret = link_ops(csdev)->enable(csdev, inconn, outconn);
if (!ret)
csdev->enable = true;
}
return ret;
return link_ops(csdev)->enable(csdev, inconn, outconn);
}
static void coresight_disable_link(struct coresight_device *csdev,
struct coresight_device *parent,
struct coresight_device *child)
{
int i;
int link_subtype;
struct coresight_connection *inconn, *outconn;
if (!parent || !child)
@ -352,50 +282,10 @@ static void coresight_disable_link(struct coresight_device *csdev,
inconn = coresight_find_out_connection(parent, csdev);
outconn = coresight_find_out_connection(csdev, child);
link_subtype = csdev->subtype.link_subtype;
if (link_ops(csdev)->disable) {
link_ops(csdev)->disable(csdev, inconn, outconn);
}
if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) {
for (i = 0; i < csdev->pdata->nr_inconns; i++)
if (atomic_read(&csdev->pdata->in_conns[i]->dest_refcnt) !=
0)
return;
} else if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_SPLIT) {
for (i = 0; i < csdev->pdata->nr_outconns; i++)
if (atomic_read(&csdev->pdata->out_conns[i]->src_refcnt) !=
0)
return;
} else {
if (atomic_read(&csdev->refcnt) != 0)
return;
}
csdev->enable = false;
link_ops(csdev)->disable(csdev, inconn, outconn);
}
int coresight_enable_source(struct coresight_device *csdev, enum cs_mode mode,
void *data)
{
int ret;
if (!csdev->enable) {
if (source_ops(csdev)->enable) {
ret = source_ops(csdev)->enable(csdev, data, mode);
if (ret)
return ret;
}
csdev->enable = true;
}
atomic_inc(&csdev->refcnt);
return 0;
}
EXPORT_SYMBOL_GPL(coresight_enable_source);
static bool coresight_is_helper(struct coresight_device *csdev)
{
return csdev->type == CORESIGHT_DEV_TYPE_HELPER;
@ -404,29 +294,12 @@ static bool coresight_is_helper(struct coresight_device *csdev)
static int coresight_enable_helper(struct coresight_device *csdev,
enum cs_mode mode, void *data)
{
int ret;
if (!helper_ops(csdev)->enable)
return 0;
ret = helper_ops(csdev)->enable(csdev, mode, data);
if (ret)
return ret;
csdev->enable = true;
return 0;
return helper_ops(csdev)->enable(csdev, mode, data);
}
static void coresight_disable_helper(struct coresight_device *csdev)
{
int ret;
if (!helper_ops(csdev)->disable)
return;
ret = helper_ops(csdev)->disable(csdev, NULL);
if (ret)
return;
csdev->enable = false;
helper_ops(csdev)->disable(csdev, NULL);
}
static void coresight_disable_helpers(struct coresight_device *csdev)
@ -441,25 +314,20 @@ static void coresight_disable_helpers(struct coresight_device *csdev)
}
}
/**
* coresight_disable_source - Drop the reference count by 1 and disable
* the device if there are no users left.
/*
* Helper function to call source_ops(csdev)->disable and also disable the
* helpers.
*
* @csdev: The coresight device to disable
* @data: Opaque data to pass on to the disable function of the source device.
* For example in perf mode this is a pointer to the struct perf_event.
*
* Returns true if the device has been disabled.
* There is an imbalance between coresight_enable_path() and
* coresight_disable_path(). Enabling also enables the source's helpers as part
* of the path, but disabling always skips the first item in the path (which is
* the source), so sources and their helpers don't get disabled as part of that
* function and we need the extra step here.
*/
bool coresight_disable_source(struct coresight_device *csdev, void *data)
void coresight_disable_source(struct coresight_device *csdev, void *data)
{
if (atomic_dec_return(&csdev->refcnt) == 0) {
if (source_ops(csdev)->disable)
source_ops(csdev)->disable(csdev, data);
coresight_disable_helpers(csdev);
csdev->enable = false;
}
return !csdev->enable;
source_ops(csdev)->disable(csdev, data);
coresight_disable_helpers(csdev);
}
EXPORT_SYMBOL_GPL(coresight_disable_source);
@ -484,7 +352,7 @@ static void coresight_disable_path_from(struct list_head *path,
/*
* ETF devices are tricky... They can be a link or a sink,
* depending on how they are configured. If an ETF has been
* "activated" it will be configured as a sink, otherwise
* selected as a sink it will be configured as a sink, otherwise
* go ahead with the link configuration.
*/
if (type == CORESIGHT_DEV_TYPE_LINKSINK)
@ -562,7 +430,7 @@ int coresight_enable_path(struct list_head *path, enum cs_mode mode,
/*
* ETF devices are tricky... They can be a link or a sink,
* depending on how they are configured. If an ETF has been
* "activated" it will be configured as a sink, otherwise
* selected as a sink it will be configured as a sink, otherwise
* go ahead with the link configuration.
*/
if (type == CORESIGHT_DEV_TYPE_LINKSINK)
@ -619,48 +487,6 @@ struct coresight_device *coresight_get_sink(struct list_head *path)
return csdev;
}
static struct coresight_device *
coresight_find_enabled_sink(struct coresight_device *csdev)
{
int i;
struct coresight_device *sink = NULL;
if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
csdev->activated)
return csdev;
/*
* Recursively explore each port found on this element.
*/
for (i = 0; i < csdev->pdata->nr_outconns; i++) {
struct coresight_device *child_dev;
child_dev = csdev->pdata->out_conns[i]->dest_dev;
if (child_dev)
sink = coresight_find_enabled_sink(child_dev);
if (sink)
return sink;
}
return NULL;
}
/**
* coresight_get_enabled_sink - returns the first enabled sink using
* connection based search starting from the source reference
*
* @source: Coresight source device reference
*/
struct coresight_device *
coresight_get_enabled_sink(struct coresight_device *source)
{
if (!source)
return NULL;
return coresight_find_enabled_sink(source);
}
static int coresight_sink_by_id(struct device *dev, const void *data)
{
struct coresight_device *csdev = to_coresight_device(dev);
@ -794,11 +620,10 @@ static void coresight_drop_device(struct coresight_device *csdev)
* @sink: The final sink we want in this path.
* @path: The list to add devices to.
*
* The tree of Coresight device is traversed until an activated sink is
* found. From there the sink is added to the list along with all the
* devices that led to that point - the end result is a list from source
* to sink. In that list the source is the first device and the sink the
* last one.
* The tree of Coresight device is traversed until @sink is found.
* From there the sink is added to the list along with all the devices that led
* to that point - the end result is a list from source to sink. In that list
* the source is the first device and the sink the last one.
*/
static int _coresight_build_path(struct coresight_device *csdev,
struct coresight_device *sink,
@ -808,7 +633,7 @@ static int _coresight_build_path(struct coresight_device *csdev,
bool found = false;
struct coresight_node *node;
/* An activated sink has been found. Enqueue the element */
/* The sink has been found. Enqueue the element */
if (csdev == sink)
goto out;
@ -1072,269 +897,6 @@ static void coresight_clear_default_sink(struct coresight_device *csdev)
}
}
/** coresight_validate_source - make sure a source has the right credentials
* @csdev: the device structure for a source.
* @function: the function this was called from.
*
* Assumes the coresight_mutex is held.
*/
static int coresight_validate_source(struct coresight_device *csdev,
const char *function)
{
u32 type, subtype;
type = csdev->type;
subtype = csdev->subtype.source_subtype;
if (type != CORESIGHT_DEV_TYPE_SOURCE) {
dev_err(&csdev->dev, "wrong device type in %s\n", function);
return -EINVAL;
}
if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS) {
dev_err(&csdev->dev, "wrong device subtype in %s\n", function);
return -EINVAL;
}
return 0;
}
int coresight_enable(struct coresight_device *csdev)
{
int cpu, ret = 0;
struct coresight_device *sink;
struct list_head *path;
enum coresight_dev_subtype_source subtype;
u32 hash;
subtype = csdev->subtype.source_subtype;
mutex_lock(&coresight_mutex);
ret = coresight_validate_source(csdev, __func__);
if (ret)
goto out;
if (csdev->enable) {
/*
* There could be multiple applications driving the software
* source. So keep the refcount for each such user when the
* source is already enabled.
*/
if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE)
atomic_inc(&csdev->refcnt);
goto out;
}
sink = coresight_get_enabled_sink(csdev);
if (!sink) {
ret = -EINVAL;
goto out;
}
path = coresight_build_path(csdev, sink);
if (IS_ERR(path)) {
pr_err("building path(s) failed\n");
ret = PTR_ERR(path);
goto out;
}
ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL);
if (ret)
goto err_path;
ret = coresight_enable_source(csdev, CS_MODE_SYSFS, NULL);
if (ret)
goto err_source;
switch (subtype) {
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
/*
* When working from sysFS it is important to keep track
* of the paths that were created so that they can be
* undone in 'coresight_disable()'. Since there can only
* be a single session per tracer (when working from sysFS)
* a per-cpu variable will do just fine.
*/
cpu = source_ops(csdev)->cpu_id(csdev);
per_cpu(tracer_path, cpu) = path;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
/*
* Use the hash of source's device name as ID
* and map the ID to the pointer of the path.
*/
hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL);
if (ret)
goto err_source;
break;
default:
/* We can't be here */
break;
}
out:
mutex_unlock(&coresight_mutex);
return ret;
err_source:
coresight_disable_path(path);
err_path:
coresight_release_path(path);
goto out;
}
EXPORT_SYMBOL_GPL(coresight_enable);
void coresight_disable(struct coresight_device *csdev)
{
int cpu, ret;
struct list_head *path = NULL;
u32 hash;
mutex_lock(&coresight_mutex);
ret = coresight_validate_source(csdev, __func__);
if (ret)
goto out;
if (!csdev->enable || !coresight_disable_source(csdev, NULL))
goto out;
switch (csdev->subtype.source_subtype) {
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
cpu = source_ops(csdev)->cpu_id(csdev);
path = per_cpu(tracer_path, cpu);
per_cpu(tracer_path, cpu) = NULL;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
/* Find the path by the hash. */
path = idr_find(&path_idr, hash);
if (path == NULL) {
pr_err("Path is not found for %s\n", dev_name(&csdev->dev));
goto out;
}
idr_remove(&path_idr, hash);
break;
default:
/* We can't be here */
break;
}
coresight_disable_path(path);
coresight_release_path(path);
out:
mutex_unlock(&coresight_mutex);
}
EXPORT_SYMBOL_GPL(coresight_disable);
static ssize_t enable_sink_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = to_coresight_device(dev);
return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->activated);
}
static ssize_t enable_sink_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
unsigned long val;
struct coresight_device *csdev = to_coresight_device(dev);
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (val)
csdev->activated = true;
else
csdev->activated = false;
return size;
}
static DEVICE_ATTR_RW(enable_sink);
static ssize_t enable_source_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = to_coresight_device(dev);
return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->enable);
}
static ssize_t enable_source_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret = 0;
unsigned long val;
struct coresight_device *csdev = to_coresight_device(dev);
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (val) {
ret = coresight_enable(csdev);
if (ret)
return ret;
} else {
coresight_disable(csdev);
}
return size;
}
static DEVICE_ATTR_RW(enable_source);
static struct attribute *coresight_sink_attrs[] = {
&dev_attr_enable_sink.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_sink);
static struct attribute *coresight_source_attrs[] = {
&dev_attr_enable_source.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_source);
static struct device_type coresight_dev_type[] = {
{
.name = "sink",
.groups = coresight_sink_groups,
},
{
.name = "link",
},
{
.name = "linksink",
.groups = coresight_sink_groups,
},
{
.name = "source",
.groups = coresight_source_groups,
},
{
.name = "helper",
}
};
/* Ensure the enum matches the names and groups */
static_assert(ARRAY_SIZE(coresight_dev_type) == CORESIGHT_DEV_TYPE_MAX);
static void coresight_device_release(struct device *dev)
{
struct coresight_device *csdev = to_coresight_device(dev);
@ -1799,7 +1361,7 @@ char *coresight_alloc_device_name(struct coresight_dev_list *dict,
}
EXPORT_SYMBOL_GPL(coresight_alloc_device_name);
struct bus_type coresight_bustype = {
const struct bus_type coresight_bustype = {
.name = "coresight",
};

View File

@ -974,7 +974,7 @@ static const struct amba_id cti_ids[] = {
CS_AMBA_ID(0x000bb9aa), /* CTI - C-A73 */
CS_AMBA_UCI_ID(0x000bb9da, uci_id_cti), /* CTI - C-A35 */
CS_AMBA_UCI_ID(0x000bb9ed, uci_id_cti), /* Coresight CTI (SoC 600) */
{ 0, 0},
{ 0, 0, NULL },
};
MODULE_DEVICE_TABLE(amba, cti_ids);

View File

@ -76,7 +76,6 @@ DEFINE_CORESIGHT_DEVLIST(etb_devs, "etb");
* @pid: Process ID of the process being monitored by the session
* that is using this component.
* @buf: area of memory where ETB buffer content gets sent.
* @mode: this ETB is being used.
* @buffer_depth: size of @buf.
* @trigger_cntr: amount of words to store after a trigger.
*/
@ -89,7 +88,6 @@ struct etb_drvdata {
local_t reading;
pid_t pid;
u8 *buf;
u32 mode;
u32 buffer_depth;
u32 trigger_cntr;
};
@ -150,20 +148,20 @@ static int etb_enable_sysfs(struct coresight_device *csdev)
spin_lock_irqsave(&drvdata->spinlock, flags);
/* Don't messup with perf sessions. */
if (drvdata->mode == CS_MODE_PERF) {
if (coresight_get_mode(csdev) == CS_MODE_PERF) {
ret = -EBUSY;
goto out;
}
if (drvdata->mode == CS_MODE_DISABLED) {
if (coresight_get_mode(csdev) == CS_MODE_DISABLED) {
ret = etb_enable_hw(drvdata);
if (ret)
goto out;
drvdata->mode = CS_MODE_SYSFS;
coresight_set_mode(csdev, CS_MODE_SYSFS);
}
atomic_inc(&csdev->refcnt);
csdev->refcnt++;
out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return ret;
@ -181,7 +179,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data)
spin_lock_irqsave(&drvdata->spinlock, flags);
/* No need to continue if the component is already in used by sysFS. */
if (drvdata->mode == CS_MODE_SYSFS) {
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
ret = -EBUSY;
goto out;
}
@ -199,7 +197,7 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data)
* use for this session.
*/
if (drvdata->pid == pid) {
atomic_inc(&csdev->refcnt);
csdev->refcnt++;
goto out;
}
@ -216,8 +214,8 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data)
if (!ret) {
/* Associate with monitored process. */
drvdata->pid = pid;
drvdata->mode = CS_MODE_PERF;
atomic_inc(&csdev->refcnt);
coresight_set_mode(drvdata->csdev, CS_MODE_PERF);
csdev->refcnt++;
}
out:
@ -356,17 +354,18 @@ static int etb_disable(struct coresight_device *csdev)
spin_lock_irqsave(&drvdata->spinlock, flags);
if (atomic_dec_return(&csdev->refcnt)) {
csdev->refcnt--;
if (csdev->refcnt) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return -EBUSY;
}
/* Complain if we (somehow) got out of sync */
WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED);
WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED);
etb_disable_hw(drvdata);
/* Dissociate from monitored process. */
drvdata->pid = -1;
drvdata->mode = CS_MODE_DISABLED;
coresight_set_mode(csdev, CS_MODE_DISABLED);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_dbg(&csdev->dev, "ETB disabled\n");
@ -447,7 +446,7 @@ static unsigned long etb_update_buffer(struct coresight_device *csdev,
spin_lock_irqsave(&drvdata->spinlock, flags);
/* Don't do anything if another tracer is using this sink */
if (atomic_read(&csdev->refcnt) != 1)
if (csdev->refcnt != 1)
goto out;
__etb_disable_hw(drvdata);
@ -589,7 +588,7 @@ static void etb_dump(struct etb_drvdata *drvdata)
unsigned long flags;
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->mode == CS_MODE_SYSFS) {
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
__etb_disable_hw(drvdata);
etb_dump_hw(drvdata);
__etb_enable_hw(drvdata);
@ -837,7 +836,7 @@ static const struct amba_id etb_ids[] = {
.id = 0x000bb907,
.mask = 0x000fffff,
},
{ 0, 0},
{ 0, 0, NULL },
};
MODULE_DEVICE_TABLE(amba, etb_ids);

View File

@ -589,7 +589,7 @@ static void etm_event_stop(struct perf_event *event, int mode)
return;
/* stop tracer */
source_ops(csdev)->disable(csdev, event);
coresight_disable_source(csdev, event);
/* tell the core */
event->hw.state = PERF_HES_STOPPED;

View File

@ -215,7 +215,6 @@ struct etm_config {
* @port_size: port size as reported by ETMCR bit 4-6 and 21.
* @arch: ETM/PTM version number.
* @use_cpu14: true if management registers need to be accessed via CP14.
* @mode: this tracer's mode, i.e sysFS, Perf or disabled.
* @sticky_enable: true if ETM base configuration has been done.
* @boot_enable:true if we should start tracing at boot time.
* @os_unlock: true if access to management registers is allowed.
@ -238,7 +237,6 @@ struct etm_drvdata {
int port_size;
u8 arch;
bool use_cp14;
local_t mode;
bool sticky_enable;
bool boot_enable;
bool os_unlock;

View File

@ -115,7 +115,7 @@ static void etm_clr_pwrup(struct etm_drvdata *drvdata)
*
* Basically the same as @coresight_timeout except for the register access
* method where we have to account for CP14 configurations.
*
* Return: 0 as soon as the bit has taken the desired state or -EAGAIN if
* TIMEOUT_US has elapsed, which ever happens first.
*/
@ -556,14 +556,12 @@ static int etm_enable(struct coresight_device *csdev, struct perf_event *event,
enum cs_mode mode)
{
int ret;
u32 val;
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
/* Someone is already using the tracer */
if (val)
if (!coresight_take_mode(csdev, mode)) {
/* Someone is already using the tracer */
return -EBUSY;
}
switch (mode) {
case CS_MODE_SYSFS:
@ -578,7 +576,7 @@ static int etm_enable(struct coresight_device *csdev, struct perf_event *event,
/* The tracer didn't start */
if (ret)
local_set(&drvdata->mode, CS_MODE_DISABLED);
coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
return ret;
}
@ -672,14 +670,13 @@ static void etm_disable(struct coresight_device *csdev,
struct perf_event *event)
{
enum cs_mode mode;
struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
/*
* For as long as the tracer isn't disabled another entity can't
* change its status. As such we can read the status here without
* fearing it will change under us.
*/
mode = local_read(&drvdata->mode);
mode = coresight_get_mode(csdev);
switch (mode) {
case CS_MODE_DISABLED:
@ -696,7 +693,7 @@ static void etm_disable(struct coresight_device *csdev,
}
if (mode)
local_set(&drvdata->mode, CS_MODE_DISABLED);
coresight_set_mode(csdev, CS_MODE_DISABLED);
}
static const struct coresight_ops_source etm_source_ops = {
@ -715,7 +712,7 @@ static int etm_online_cpu(unsigned int cpu)
return 0;
if (etmdrvdata[cpu]->boot_enable && !etmdrvdata[cpu]->sticky_enable)
coresight_enable(etmdrvdata[cpu]->csdev);
coresight_enable_sysfs(etmdrvdata[cpu]->csdev);
return 0;
}
@ -730,7 +727,7 @@ static int etm_starting_cpu(unsigned int cpu)
etmdrvdata[cpu]->os_unlock = true;
}
if (local_read(&etmdrvdata[cpu]->mode))
if (coresight_get_mode(etmdrvdata[cpu]->csdev))
etm_enable_hw(etmdrvdata[cpu]);
spin_unlock(&etmdrvdata[cpu]->spinlock);
return 0;
@ -742,7 +739,7 @@ static int etm_dying_cpu(unsigned int cpu)
return 0;
spin_lock(&etmdrvdata[cpu]->spinlock);
if (local_read(&etmdrvdata[cpu]->mode))
if (coresight_get_mode(etmdrvdata[cpu]->csdev))
etm_disable_hw(etmdrvdata[cpu]);
spin_unlock(&etmdrvdata[cpu]->spinlock);
return 0;
@ -925,7 +922,7 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id)
dev_info(&drvdata->csdev->dev,
"%s initialized\n", (char *)coresight_get_uci_data(id));
if (boot_enable) {
coresight_enable(drvdata->csdev);
coresight_enable_sysfs(drvdata->csdev);
drvdata->boot_enable = true;
}
@ -1003,7 +1000,7 @@ static const struct amba_id etm_ids[] = {
CS_AMBA_ID_DATA(0x000bb95f, "PTM 1.1"),
/* PTM 1.1 Qualcomm */
CS_AMBA_ID_DATA(0x000b006f, "PTM 1.1"),
{ 0, 0},
{ 0, 0, NULL},
};
MODULE_DEVICE_TABLE(amba, etm_ids);

View File

@ -722,7 +722,7 @@ static ssize_t cntr_val_show(struct device *dev,
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etm_config *config = &drvdata->config;
if (!local_read(&drvdata->mode)) {
if (!coresight_get_mode(drvdata->csdev)) {
spin_lock(&drvdata->spinlock);
for (i = 0; i < drvdata->nr_cntr; i++)
ret += sprintf(buf, "counter %d: %x\n",
@ -941,7 +941,7 @@ static ssize_t seq_curr_state_show(struct device *dev,
struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct etm_config *config = &drvdata->config;
if (!local_read(&drvdata->mode)) {
if (!coresight_get_mode(drvdata->csdev)) {
val = config->seq_curr_state;
goto out;
}

View File

@ -840,14 +840,11 @@ static int etm4_enable(struct coresight_device *csdev, struct perf_event *event,
enum cs_mode mode)
{
int ret;
u32 val;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
/* Someone is already using the tracer */
if (val)
if (!coresight_take_mode(csdev, mode)) {
/* Someone is already using the tracer */
return -EBUSY;
}
switch (mode) {
case CS_MODE_SYSFS:
@ -862,7 +859,7 @@ static int etm4_enable(struct coresight_device *csdev, struct perf_event *event,
/* The tracer didn't start */
if (ret)
local_set(&drvdata->mode, CS_MODE_DISABLED);
coresight_set_mode(csdev, CS_MODE_DISABLED);
return ret;
}
@ -1004,14 +1001,13 @@ static void etm4_disable(struct coresight_device *csdev,
struct perf_event *event)
{
enum cs_mode mode;
struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
/*
* For as long as the tracer isn't disabled another entity can't
* change its status. As such we can read the status here without
* fearing it will change under us.
*/
mode = local_read(&drvdata->mode);
mode = coresight_get_mode(csdev);
switch (mode) {
case CS_MODE_DISABLED:
@ -1025,7 +1021,7 @@ static void etm4_disable(struct coresight_device *csdev,
}
if (mode)
local_set(&drvdata->mode, CS_MODE_DISABLED);
coresight_set_mode(csdev, CS_MODE_DISABLED);
}
static const struct coresight_ops_source etm4_source_ops = {
@ -1200,6 +1196,7 @@ static void etm4_init_arch_data(void *info)
struct etm4_init_arg *init_arg = info;
struct etmv4_drvdata *drvdata;
struct csdev_access *csa;
struct device *dev = init_arg->dev;
int i;
drvdata = dev_get_drvdata(init_arg->dev);
@ -1213,6 +1210,10 @@ static void etm4_init_arch_data(void *info)
if (!etm4_init_csdev_access(drvdata, csa))
return;
if (!csa->io_mem ||
fwnode_property_present(dev_fwnode(dev), "qcom,skip-power-up"))
drvdata->skip_power_up = true;
/* Detect the support for OS Lock before we actually use it */
etm_detect_os_lock(drvdata, csa);
@ -1650,7 +1651,7 @@ static int etm4_online_cpu(unsigned int cpu)
return etm4_probe_cpu(cpu);
if (etmdrvdata[cpu]->boot_enable && !etmdrvdata[cpu]->sticky_enable)
coresight_enable(etmdrvdata[cpu]->csdev);
coresight_enable_sysfs(etmdrvdata[cpu]->csdev);
return 0;
}
@ -1663,7 +1664,7 @@ static int etm4_starting_cpu(unsigned int cpu)
if (!etmdrvdata[cpu]->os_unlock)
etm4_os_unlock(etmdrvdata[cpu]);
if (local_read(&etmdrvdata[cpu]->mode))
if (coresight_get_mode(etmdrvdata[cpu]->csdev))
etm4_enable_hw(etmdrvdata[cpu]);
spin_unlock(&etmdrvdata[cpu]->spinlock);
return 0;
@ -1675,7 +1676,7 @@ static int etm4_dying_cpu(unsigned int cpu)
return 0;
spin_lock(&etmdrvdata[cpu]->spinlock);
if (local_read(&etmdrvdata[cpu]->mode))
if (coresight_get_mode(etmdrvdata[cpu]->csdev))
etm4_disable_hw(etmdrvdata[cpu]);
spin_unlock(&etmdrvdata[cpu]->spinlock);
return 0;
@ -1833,7 +1834,7 @@ static int etm4_cpu_save(struct etmv4_drvdata *drvdata)
* Save and restore the ETM Trace registers only if
* the ETM is active.
*/
if (local_read(&drvdata->mode) && drvdata->save_state)
if (coresight_get_mode(drvdata->csdev) && drvdata->save_state)
ret = __etm4_cpu_save(drvdata);
return ret;
}
@ -2040,11 +2041,6 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
if (!drvdata->arch)
return -EINVAL;
/* TRCPDCR is not accessible with system instructions. */
if (!desc.access.io_mem ||
fwnode_property_present(dev_fwnode(dev), "qcom,skip-power-up"))
drvdata->skip_power_up = true;
major = ETM_ARCH_MAJOR_VERSION(drvdata->arch);
minor = ETM_ARCH_MINOR_VERSION(drvdata->arch);
@ -2098,7 +2094,7 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
drvdata->cpu, type_name, major, minor);
if (boot_enable) {
coresight_enable(drvdata->csdev);
coresight_enable_sysfs(drvdata->csdev);
drvdata->boot_enable = true;
}
@ -2390,7 +2386,7 @@ static const struct of_device_id etm4_sysreg_match[] = {
#ifdef CONFIG_ACPI
static const struct acpi_device_id etm4x_acpi_ids[] = {
{"ARMHC500", 0}, /* ARM CoreSight ETM4x */
{"ARMHC500", 0, 0, 0}, /* ARM CoreSight ETM4x */
{}
};
MODULE_DEVICE_TABLE(acpi, etm4x_acpi_ids);

View File

@ -1016,7 +1016,6 @@ struct etmv4_drvdata {
void __iomem *base;
struct coresight_device *csdev;
spinlock_t spinlock;
local_t mode;
int cpu;
u8 arch;
u8 nr_pe;

View File

@ -350,7 +350,7 @@ MODULE_DEVICE_TABLE(of, static_funnel_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id static_funnel_ids[] = {
{"ARMHC9FE", 0},
{"ARMHC9FE", 0, 0, 0},
{},
};
@ -391,7 +391,7 @@ static const struct amba_id dynamic_funnel_ids[] = {
.id = 0x000bb9eb,
.mask = 0x000fffff,
},
{ 0, 0},
{ 0, 0, NULL },
};
MODULE_DEVICE_TABLE(amba, dynamic_funnel_ids);

View File

@ -12,6 +12,9 @@
#include <linux/coresight.h>
#include <linux/pm_runtime.h>
extern struct mutex coresight_mutex;
extern struct device_type coresight_dev_type[];
/*
* Coresight management registers (0xf00-0xfcc)
* 0xfa0 - 0xfa4: Management registers in PFTv1.0
@ -130,8 +133,6 @@ void coresight_disable_path(struct list_head *path);
int coresight_enable_path(struct list_head *path, enum cs_mode mode,
void *sink_data);
struct coresight_device *coresight_get_sink(struct list_head *path);
struct coresight_device *
coresight_get_enabled_sink(struct coresight_device *source);
struct coresight_device *coresight_get_sink_by_id(u32 id);
struct coresight_device *
coresight_find_default_sink(struct coresight_device *csdev);
@ -231,8 +232,6 @@ void coresight_add_helper(struct coresight_device *csdev,
void coresight_set_percpu_sink(int cpu, struct coresight_device *csdev);
struct coresight_device *coresight_get_percpu_sink(int cpu);
int coresight_enable_source(struct coresight_device *csdev, enum cs_mode mode,
void *data);
bool coresight_disable_source(struct coresight_device *csdev, void *data);
void coresight_disable_source(struct coresight_device *csdev, void *data);
#endif

View File

@ -363,7 +363,7 @@ MODULE_DEVICE_TABLE(of, static_replicator_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id static_replicator_acpi_ids[] = {
{"ARMHC985", 0}, /* ARM CoreSight Static Replicator */
{"ARMHC985", 0, 0, 0}, /* ARM CoreSight Static Replicator */
{}
};

View File

@ -119,7 +119,6 @@ DEFINE_CORESIGHT_DEVLIST(stm_devs, "stm");
* @spinlock: only one at a time pls.
* @chs: the channels accociated to this STM.
* @stm: structure associated to the generic STM interface.
* @mode: this tracer's mode (enum cs_mode), i.e sysFS, or disabled.
* @traceid: value of the current ID for this component.
* @write_bytes: Maximus bytes this STM can write at a time.
* @stmsper: settings for register STMSPER.
@ -136,7 +135,6 @@ struct stm_drvdata {
spinlock_t spinlock;
struct channel_space chs;
struct stm_data stm;
local_t mode;
u8 traceid;
u32 write_bytes;
u32 stmsper;
@ -195,17 +193,15 @@ 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)
{
u32 val;
struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
if (mode != CS_MODE_SYSFS)
return -EINVAL;
val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
/* Someone is already using the tracer */
if (val)
if (!coresight_take_mode(csdev, mode)) {
/* Someone is already using the tracer */
return -EBUSY;
}
pm_runtime_get_sync(csdev->dev.parent);
@ -266,7 +262,7 @@ static void stm_disable(struct coresight_device *csdev,
* change its status. As such we can read the status here without
* fearing it will change under us.
*/
if (local_read(&drvdata->mode) == CS_MODE_SYSFS) {
if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
spin_lock(&drvdata->spinlock);
stm_disable_hw(drvdata);
spin_unlock(&drvdata->spinlock);
@ -276,7 +272,7 @@ static void stm_disable(struct coresight_device *csdev,
pm_runtime_put(csdev->dev.parent);
local_set(&drvdata->mode, CS_MODE_DISABLED);
coresight_set_mode(csdev, CS_MODE_DISABLED);
dev_dbg(&csdev->dev, "STM tracing disabled\n");
}
}
@ -334,7 +330,7 @@ static int stm_generic_link(struct stm_data *stm_data,
if (!drvdata || !drvdata->csdev)
return -EINVAL;
return coresight_enable(drvdata->csdev);
return coresight_enable_sysfs(drvdata->csdev);
}
static void stm_generic_unlink(struct stm_data *stm_data,
@ -345,7 +341,7 @@ static void stm_generic_unlink(struct stm_data *stm_data,
if (!drvdata || !drvdata->csdev)
return;
coresight_disable(drvdata->csdev);
coresight_disable_sysfs(drvdata->csdev);
}
static phys_addr_t
@ -373,7 +369,7 @@ static long stm_generic_set_options(struct stm_data *stm_data,
{
struct stm_drvdata *drvdata = container_of(stm_data,
struct stm_drvdata, stm);
if (!(drvdata && local_read(&drvdata->mode)))
if (!(drvdata && coresight_get_mode(drvdata->csdev)))
return -EINVAL;
if (channel >= drvdata->numsp)
@ -408,7 +404,7 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data,
struct stm_drvdata, stm);
unsigned int stm_flags;
if (!(drvdata && local_read(&drvdata->mode)))
if (!(drvdata && coresight_get_mode(drvdata->csdev)))
return -EACCES;
if (channel >= drvdata->numsp)
@ -515,7 +511,7 @@ static ssize_t port_select_show(struct device *dev,
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if (!local_read(&drvdata->mode)) {
if (!coresight_get_mode(drvdata->csdev)) {
val = drvdata->stmspscr;
} else {
spin_lock(&drvdata->spinlock);
@ -541,7 +537,7 @@ static ssize_t port_select_store(struct device *dev,
spin_lock(&drvdata->spinlock);
drvdata->stmspscr = val;
if (local_read(&drvdata->mode)) {
if (coresight_get_mode(drvdata->csdev)) {
CS_UNLOCK(drvdata->base);
/* Process as per ARM's TRM recommendation */
stmsper = readl_relaxed(drvdata->base + STMSPER);
@ -562,7 +558,7 @@ static ssize_t port_enable_show(struct device *dev,
struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
if (!local_read(&drvdata->mode)) {
if (!coresight_get_mode(drvdata->csdev)) {
val = drvdata->stmsper;
} else {
spin_lock(&drvdata->spinlock);
@ -588,7 +584,7 @@ static ssize_t port_enable_store(struct device *dev,
spin_lock(&drvdata->spinlock);
drvdata->stmsper = val;
if (local_read(&drvdata->mode)) {
if (coresight_get_mode(drvdata->csdev)) {
CS_UNLOCK(drvdata->base);
writel_relaxed(drvdata->stmsper, drvdata->base + STMSPER);
CS_LOCK(drvdata->base);
@ -950,7 +946,7 @@ static const struct dev_pm_ops stm_dev_pm_ops = {
static const struct amba_id stm_ids[] = {
CS_AMBA_ID_DATA(0x000bb962, "STM32"),
CS_AMBA_ID_DATA(0x000bb963, "STM500"),
{ 0, 0},
{ 0, 0, NULL },
};
MODULE_DEVICE_TABLE(amba, stm_ids);

View File

@ -5,10 +5,401 @@
*/
#include <linux/device.h>
#include <linux/idr.h>
#include <linux/kernel.h>
#include "coresight-priv.h"
/*
* Use IDR to map the hash of the source's device name
* to the pointer of path for the source. The idr is for
* the sources which aren't associated with CPU.
*/
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);
ssize_t coresight_simple_show_pair(struct device *_dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev);
struct cs_pair_attribute *cs_attr = container_of(attr, struct cs_pair_attribute, attr);
u64 val;
pm_runtime_get_sync(_dev->parent);
val = csdev_access_relaxed_read_pair(&csdev->access, cs_attr->lo_off, cs_attr->hi_off);
pm_runtime_put_sync(_dev->parent);
return sysfs_emit(buf, "0x%llx\n", val);
}
EXPORT_SYMBOL_GPL(coresight_simple_show_pair);
ssize_t coresight_simple_show32(struct device *_dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = container_of(_dev, struct coresight_device, dev);
struct cs_off_attribute *cs_attr = container_of(attr, struct cs_off_attribute, attr);
u64 val;
pm_runtime_get_sync(_dev->parent);
val = csdev_access_relaxed_read32(&csdev->access, cs_attr->off);
pm_runtime_put_sync(_dev->parent);
return sysfs_emit(buf, "0x%llx\n", val);
}
EXPORT_SYMBOL_GPL(coresight_simple_show32);
static int coresight_enable_source_sysfs(struct coresight_device *csdev,
enum cs_mode mode, void *data)
{
int ret;
/*
* Comparison with CS_MODE_SYSFS works without taking any device
* specific spinlock because the truthyness of that comparison can only
* change with coresight_mutex held, which we already have here.
*/
lockdep_assert_held(&coresight_mutex);
if (coresight_get_mode(csdev) != CS_MODE_SYSFS) {
ret = source_ops(csdev)->enable(csdev, data, mode);
if (ret)
return ret;
}
csdev->refcnt++;
return 0;
}
/**
* coresight_disable_source_sysfs - Drop the reference count by 1 and disable
* the device if there are no users left.
*
* @csdev: The coresight device to disable
* @data: Opaque data to pass on to the disable function of the source device.
* For example in perf mode this is a pointer to the struct perf_event.
*
* Returns true if the device has been disabled.
*/
static bool coresight_disable_source_sysfs(struct coresight_device *csdev,
void *data)
{
lockdep_assert_held(&coresight_mutex);
if (coresight_get_mode(csdev) != CS_MODE_SYSFS)
return false;
csdev->refcnt--;
if (csdev->refcnt == 0) {
coresight_disable_source(csdev, data);
return true;
}
return false;
}
/**
* coresight_find_activated_sysfs_sink - returns the first sink activated via
* sysfs using connection based search starting from the source reference.
*
* @csdev: Coresight source device reference
*/
static struct coresight_device *
coresight_find_activated_sysfs_sink(struct coresight_device *csdev)
{
int i;
struct coresight_device *sink = NULL;
if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
csdev->sysfs_sink_activated)
return csdev;
/*
* Recursively explore each port found on this element.
*/
for (i = 0; i < csdev->pdata->nr_outconns; i++) {
struct coresight_device *child_dev;
child_dev = csdev->pdata->out_conns[i]->dest_dev;
if (child_dev)
sink = coresight_find_activated_sysfs_sink(child_dev);
if (sink)
return sink;
}
return NULL;
}
/** coresight_validate_source - make sure a source has the right credentials to
* be used via sysfs.
* @csdev: the device structure for a source.
* @function: the function this was called from.
*
* Assumes the coresight_mutex is held.
*/
static int coresight_validate_source_sysfs(struct coresight_device *csdev,
const char *function)
{
u32 type, subtype;
type = csdev->type;
subtype = csdev->subtype.source_subtype;
if (type != CORESIGHT_DEV_TYPE_SOURCE) {
dev_err(&csdev->dev, "wrong device type in %s\n", function);
return -EINVAL;
}
if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM &&
subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS) {
dev_err(&csdev->dev, "wrong device subtype in %s\n", function);
return -EINVAL;
}
return 0;
}
int coresight_enable_sysfs(struct coresight_device *csdev)
{
int cpu, ret = 0;
struct coresight_device *sink;
struct list_head *path;
enum coresight_dev_subtype_source subtype;
u32 hash;
subtype = csdev->subtype.source_subtype;
mutex_lock(&coresight_mutex);
ret = coresight_validate_source_sysfs(csdev, __func__);
if (ret)
goto out;
/*
* mode == SYSFS implies that it's already enabled. Don't look at the
* refcount to determine this because we don't claim the source until
* coresight_enable_source() so can still race with Perf mode which
* doesn't hold coresight_mutex.
*/
if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
/*
* There could be multiple applications driving the software
* source. So keep the refcount for each such user when the
* source is already enabled.
*/
if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE)
csdev->refcnt++;
goto out;
}
sink = coresight_find_activated_sysfs_sink(csdev);
if (!sink) {
ret = -EINVAL;
goto out;
}
path = coresight_build_path(csdev, sink);
if (IS_ERR(path)) {
pr_err("building path(s) failed\n");
ret = PTR_ERR(path);
goto out;
}
ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL);
if (ret)
goto err_path;
ret = coresight_enable_source_sysfs(csdev, CS_MODE_SYSFS, NULL);
if (ret)
goto err_source;
switch (subtype) {
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
/*
* When working from sysFS it is important to keep track
* of the paths that were created so that they can be
* undone in 'coresight_disable()'. Since there can only
* be a single session per tracer (when working from sysFS)
* a per-cpu variable will do just fine.
*/
cpu = source_ops(csdev)->cpu_id(csdev);
per_cpu(tracer_path, cpu) = path;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
/*
* Use the hash of source's device name as ID
* and map the ID to the pointer of the path.
*/
hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL);
if (ret)
goto err_source;
break;
default:
/* We can't be here */
break;
}
out:
mutex_unlock(&coresight_mutex);
return ret;
err_source:
coresight_disable_path(path);
err_path:
coresight_release_path(path);
goto out;
}
EXPORT_SYMBOL_GPL(coresight_enable_sysfs);
void coresight_disable_sysfs(struct coresight_device *csdev)
{
int cpu, ret;
struct list_head *path = NULL;
u32 hash;
mutex_lock(&coresight_mutex);
ret = coresight_validate_source_sysfs(csdev, __func__);
if (ret)
goto out;
if (!coresight_disable_source_sysfs(csdev, NULL))
goto out;
switch (csdev->subtype.source_subtype) {
case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
cpu = source_ops(csdev)->cpu_id(csdev);
path = per_cpu(tracer_path, cpu);
per_cpu(tracer_path, cpu) = NULL;
break;
case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
/* Find the path by the hash. */
path = idr_find(&path_idr, hash);
if (path == NULL) {
pr_err("Path is not found for %s\n", dev_name(&csdev->dev));
goto out;
}
idr_remove(&path_idr, hash);
break;
default:
/* We can't be here */
break;
}
coresight_disable_path(path);
coresight_release_path(path);
out:
mutex_unlock(&coresight_mutex);
}
EXPORT_SYMBOL_GPL(coresight_disable_sysfs);
static ssize_t enable_sink_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = to_coresight_device(dev);
return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->sysfs_sink_activated);
}
static ssize_t enable_sink_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret;
unsigned long val;
struct coresight_device *csdev = to_coresight_device(dev);
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
csdev->sysfs_sink_activated = !!val;
return size;
}
static DEVICE_ATTR_RW(enable_sink);
static ssize_t enable_source_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = to_coresight_device(dev);
guard(mutex)(&coresight_mutex);
return scnprintf(buf, PAGE_SIZE, "%u\n",
coresight_get_mode(csdev) == CS_MODE_SYSFS);
}
static ssize_t enable_source_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
int ret = 0;
unsigned long val;
struct coresight_device *csdev = to_coresight_device(dev);
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (val) {
ret = coresight_enable_sysfs(csdev);
if (ret)
return ret;
} else {
coresight_disable_sysfs(csdev);
}
return size;
}
static DEVICE_ATTR_RW(enable_source);
static struct attribute *coresight_sink_attrs[] = {
&dev_attr_enable_sink.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_sink);
static struct attribute *coresight_source_attrs[] = {
&dev_attr_enable_source.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_source);
struct device_type coresight_dev_type[] = {
[CORESIGHT_DEV_TYPE_SINK] = {
.name = "sink",
.groups = coresight_sink_groups,
},
[CORESIGHT_DEV_TYPE_LINK] = {
.name = "link",
},
[CORESIGHT_DEV_TYPE_LINKSINK] = {
.name = "linksink",
.groups = coresight_sink_groups,
},
[CORESIGHT_DEV_TYPE_SOURCE] = {
.name = "source",
.groups = coresight_source_groups,
},
[CORESIGHT_DEV_TYPE_HELPER] = {
.name = "helper",
}
};
/* Ensure the enum matches the names and groups */
static_assert(ARRAY_SIZE(coresight_dev_type) == CORESIGHT_DEV_TYPE_MAX);
/*
* Connections group - links attribute.
* Count of created links between coresight components in the group.

View File

@ -558,7 +558,7 @@ static void tmc_shutdown(struct amba_device *adev)
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->mode == CS_MODE_DISABLED)
if (coresight_get_mode(drvdata->csdev) == CS_MODE_DISABLED)
goto out;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR)
@ -594,7 +594,7 @@ static const struct amba_id tmc_ids[] = {
CS_AMBA_ID(0x000bb9e9),
/* Coresight SoC 600 TMC-ETF */
CS_AMBA_ID(0x000bb9ea),
{ 0, 0},
{ 0, 0, NULL },
};
MODULE_DEVICE_TABLE(amba, tmc_ids);

View File

@ -89,7 +89,7 @@ static void __tmc_etb_disable_hw(struct tmc_drvdata *drvdata)
* When operating in sysFS mode the content of the buffer needs to be
* read before the TMC is disabled.
*/
if (drvdata->mode == CS_MODE_SYSFS)
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS)
tmc_etb_dump_hw(drvdata);
tmc_disable_hw(drvdata);
@ -205,8 +205,8 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
* sink is already enabled no memory is needed and the HW need not be
* touched.
*/
if (drvdata->mode == CS_MODE_SYSFS) {
atomic_inc(&csdev->refcnt);
if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
csdev->refcnt++;
goto out;
}
@ -228,8 +228,8 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev)
ret = tmc_etb_enable_hw(drvdata);
if (!ret) {
drvdata->mode = CS_MODE_SYSFS;
atomic_inc(&csdev->refcnt);
coresight_set_mode(csdev, CS_MODE_SYSFS);
csdev->refcnt++;
} else {
/* Free up the buffer if we failed to enable */
used = false;
@ -262,7 +262,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data)
* No need to continue if the ETB/ETF is already operated
* from sysFS.
*/
if (drvdata->mode == CS_MODE_SYSFS) {
if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
ret = -EBUSY;
break;
}
@ -284,7 +284,7 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data)
* use for this session.
*/
if (drvdata->pid == pid) {
atomic_inc(&csdev->refcnt);
csdev->refcnt++;
break;
}
@ -292,8 +292,8 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data)
if (!ret) {
/* Associate with monitored process. */
drvdata->pid = pid;
drvdata->mode = CS_MODE_PERF;
atomic_inc(&csdev->refcnt);
coresight_set_mode(csdev, CS_MODE_PERF);
csdev->refcnt++;
}
} while (0);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
@ -338,17 +338,18 @@ static int tmc_disable_etf_sink(struct coresight_device *csdev)
return -EBUSY;
}
if (atomic_dec_return(&csdev->refcnt)) {
csdev->refcnt--;
if (csdev->refcnt) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return -EBUSY;
}
/* Complain if we (somehow) got out of sync */
WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED);
WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED);
tmc_etb_disable_hw(drvdata);
/* Dissociate from monitored process. */
drvdata->pid = -1;
drvdata->mode = CS_MODE_DISABLED;
coresight_set_mode(csdev, CS_MODE_DISABLED);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
@ -371,15 +372,15 @@ static int tmc_enable_etf_link(struct coresight_device *csdev,
return -EBUSY;
}
if (atomic_read(&csdev->refcnt) == 0) {
if (csdev->refcnt == 0) {
ret = tmc_etf_enable_hw(drvdata);
if (!ret) {
drvdata->mode = CS_MODE_SYSFS;
coresight_set_mode(csdev, CS_MODE_SYSFS);
first_enable = true;
}
}
if (!ret)
atomic_inc(&csdev->refcnt);
csdev->refcnt++;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
if (first_enable)
@ -401,9 +402,10 @@ static void tmc_disable_etf_link(struct coresight_device *csdev,
return;
}
if (atomic_dec_return(&csdev->refcnt) == 0) {
csdev->refcnt--;
if (csdev->refcnt == 0) {
tmc_etf_disable_hw(drvdata);
drvdata->mode = CS_MODE_DISABLED;
coresight_set_mode(csdev, CS_MODE_DISABLED);
last_disable = true;
}
spin_unlock_irqrestore(&drvdata->spinlock, flags);
@ -483,13 +485,13 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev,
return 0;
/* This shouldn't happen */
if (WARN_ON_ONCE(drvdata->mode != CS_MODE_PERF))
if (WARN_ON_ONCE(coresight_get_mode(csdev) != CS_MODE_PERF))
return 0;
spin_lock_irqsave(&drvdata->spinlock, flags);
/* Don't do anything if another tracer is using this sink */
if (atomic_read(&csdev->refcnt) != 1)
if (csdev->refcnt != 1)
goto out;
CS_UNLOCK(drvdata->base);
@ -629,7 +631,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
}
/* Don't interfere if operated from Perf */
if (drvdata->mode == CS_MODE_PERF) {
if (coresight_get_mode(drvdata->csdev) == CS_MODE_PERF) {
ret = -EINVAL;
goto out;
}
@ -641,7 +643,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
}
/* Disable the TMC if need be */
if (drvdata->mode == CS_MODE_SYSFS) {
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) {
@ -673,7 +675,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
spin_lock_irqsave(&drvdata->spinlock, flags);
/* Re-enable the TMC if need be */
if (drvdata->mode == CS_MODE_SYSFS) {
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) {

View File

@ -1143,7 +1143,7 @@ static void __tmc_etr_disable_hw(struct tmc_drvdata *drvdata)
* When operating in sysFS mode the content of the buffer needs to be
* read before the TMC is disabled.
*/
if (drvdata->mode == CS_MODE_SYSFS)
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS)
tmc_etr_sync_sysfs_buf(drvdata);
tmc_disable_hw(drvdata);
@ -1189,7 +1189,7 @@ static struct etr_buf *tmc_etr_get_sysfs_buffer(struct coresight_device *csdev)
spin_lock_irqsave(&drvdata->spinlock, flags);
}
if (drvdata->reading || drvdata->mode == CS_MODE_PERF) {
if (drvdata->reading || coresight_get_mode(csdev) == CS_MODE_PERF) {
ret = -EBUSY;
goto out;
}
@ -1230,15 +1230,15 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
* sink is already enabled no memory is needed and the HW need not be
* touched, even if the buffer size has changed.
*/
if (drvdata->mode == CS_MODE_SYSFS) {
atomic_inc(&csdev->refcnt);
if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
csdev->refcnt++;
goto out;
}
ret = tmc_etr_enable_hw(drvdata, sysfs_buf);
if (!ret) {
drvdata->mode = CS_MODE_SYSFS;
atomic_inc(&csdev->refcnt);
coresight_set_mode(csdev, CS_MODE_SYSFS);
csdev->refcnt++;
}
out:
@ -1564,7 +1564,7 @@ tmc_update_etr_buffer(struct coresight_device *csdev,
spin_lock_irqsave(&drvdata->spinlock, flags);
/* Don't do anything if another tracer is using this sink */
if (atomic_read(&csdev->refcnt) != 1) {
if (csdev->refcnt != 1) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
goto out;
}
@ -1652,7 +1652,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
spin_lock_irqsave(&drvdata->spinlock, flags);
/* Don't use this sink if it is already claimed by sysFS */
if (drvdata->mode == CS_MODE_SYSFS) {
if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
rc = -EBUSY;
goto unlock_out;
}
@ -1676,7 +1676,7 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
* use for this session.
*/
if (drvdata->pid == pid) {
atomic_inc(&csdev->refcnt);
csdev->refcnt++;
goto unlock_out;
}
@ -1684,9 +1684,9 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
if (!rc) {
/* Associate with monitored process. */
drvdata->pid = pid;
drvdata->mode = CS_MODE_PERF;
coresight_set_mode(csdev, CS_MODE_PERF);
drvdata->perf_buf = etr_perf->etr_buf;
atomic_inc(&csdev->refcnt);
csdev->refcnt++;
}
unlock_out:
@ -1719,17 +1719,18 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev)
return -EBUSY;
}
if (atomic_dec_return(&csdev->refcnt)) {
csdev->refcnt--;
if (csdev->refcnt) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return -EBUSY;
}
/* Complain if we (somehow) got out of sync */
WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED);
WARN_ON_ONCE(coresight_get_mode(csdev) == CS_MODE_DISABLED);
tmc_etr_disable_hw(drvdata);
/* Dissociate from monitored process. */
drvdata->pid = -1;
drvdata->mode = CS_MODE_DISABLED;
coresight_set_mode(csdev, CS_MODE_DISABLED);
/* Reset perf specific data */
drvdata->perf_buf = NULL;
@ -1777,7 +1778,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
}
/* Disable the TMC if we are trying to read from a running session. */
if (drvdata->mode == CS_MODE_SYSFS)
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS)
__tmc_etr_disable_hw(drvdata);
drvdata->reading = true;
@ -1799,7 +1800,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
spin_lock_irqsave(&drvdata->spinlock, flags);
/* RE-enable the TMC if need be */
if (drvdata->mode == CS_MODE_SYSFS) {
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
/*
* The trace run will continue with the same allocated trace
* buffer. Since the tracer is still enabled drvdata::buf can't

View File

@ -178,7 +178,6 @@ struct etr_buf {
* @size: trace buffer size for this TMC (common for all modes).
* @max_burst_size: The maximum burst size that can be initiated by
* TMC-ETR on AXI bus.
* @mode: how this TMC is being used.
* @config_type: TMC variant, must be of type @tmc_config_type.
* @memwidth: width of the memory interface databus, in bytes.
* @trigger_cntr: amount of words to store after a trigger.
@ -203,7 +202,6 @@ struct tmc_drvdata {
u32 len;
u32 size;
u32 max_burst_size;
u32 mode;
enum tmc_config_type config_type;
enum tmc_mem_intf_width memwidth;
u32 trigger_cntr;

View File

@ -18,6 +18,7 @@
#include "coresight-priv.h"
#include "coresight-tpda.h"
#include "coresight-trace-id.h"
#include "coresight-tpdm.h"
DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda");
@ -28,24 +29,59 @@ static bool coresight_device_is_tpdm(struct coresight_device *csdev)
CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM);
}
/*
* Read the DSB element size from the TPDM device
* Returns
* The dsb element size read from the devicetree if available.
* 0 - Otherwise, with a warning once.
*/
static int tpdm_read_dsb_element_size(struct coresight_device *csdev)
static void tpda_clear_element_size(struct coresight_device *csdev)
{
int rc = 0;
u8 size = 0;
struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
drvdata->dsb_esize = 0;
drvdata->cmb_esize = 0;
}
static void tpda_set_element_size(struct tpda_drvdata *drvdata, u32 *val)
{
/* Clear all relevant fields */
*val &= ~(TPDA_Pn_CR_DSBSIZE | TPDA_Pn_CR_CMBSIZE);
if (drvdata->dsb_esize == 64)
*val |= TPDA_Pn_CR_DSBSIZE;
else if (drvdata->dsb_esize == 32)
*val &= ~TPDA_Pn_CR_DSBSIZE;
if (drvdata->cmb_esize == 64)
*val |= FIELD_PREP(TPDA_Pn_CR_CMBSIZE, 0x2);
else if (drvdata->cmb_esize == 32)
*val |= FIELD_PREP(TPDA_Pn_CR_CMBSIZE, 0x1);
else if (drvdata->cmb_esize == 8)
*val &= ~TPDA_Pn_CR_CMBSIZE;
}
/*
* Read the element size from the TPDM device. One TPDM must have at least one of the
* element size property.
* Returns
* 0 - The element size property is read
* Others - Cannot read the property of the element size
*/
static int tpdm_read_element_size(struct tpda_drvdata *drvdata,
struct coresight_device *csdev)
{
int rc = -EINVAL;
struct tpdm_drvdata *tpdm_data = dev_get_drvdata(csdev->dev.parent);
if (tpdm_has_dsb_dataset(tpdm_data)) {
rc = fwnode_property_read_u32(dev_fwnode(csdev->dev.parent),
"qcom,dsb-element-bits", &drvdata->dsb_esize);
}
if (tpdm_has_cmb_dataset(tpdm_data)) {
rc = fwnode_property_read_u32(dev_fwnode(csdev->dev.parent),
"qcom,cmb-element-bits", &drvdata->cmb_esize);
}
rc = fwnode_property_read_u8(dev_fwnode(csdev->dev.parent),
"qcom,dsb-element-size", &size);
if (rc)
dev_warn_once(&csdev->dev,
"Failed to read TPDM DSB Element size: %d\n", rc);
"Failed to read TPDM Element size: %d\n", rc);
return size;
return rc;
}
/*
@ -56,11 +92,12 @@ static int tpdm_read_dsb_element_size(struct coresight_device *csdev)
* Parameter "inport" is used to pass in the input port number
* of TPDA, and it is set to -1 in the recursize call.
*/
static int tpda_get_element_size(struct coresight_device *csdev,
static int tpda_get_element_size(struct tpda_drvdata *drvdata,
struct coresight_device *csdev,
int inport)
{
int dsb_size = -ENOENT;
int i, size;
int rc = 0;
int i;
struct coresight_device *in;
for (i = 0; i < csdev->pdata->nr_inconns; i++) {
@ -69,30 +106,26 @@ static int tpda_get_element_size(struct coresight_device *csdev,
continue;
/* Ignore the paths that do not match port */
if (inport > 0 &&
if (inport >= 0 &&
csdev->pdata->in_conns[i]->dest_port != inport)
continue;
if (coresight_device_is_tpdm(in)) {
size = tpdm_read_dsb_element_size(in);
if (drvdata->dsb_esize || drvdata->cmb_esize)
return -EEXIST;
rc = tpdm_read_element_size(drvdata, in);
if (rc)
return rc;
} else {
/* Recurse down the path */
size = tpda_get_element_size(in, -1);
}
if (size < 0)
return size;
if (dsb_size < 0) {
/* Found a size, save it. */
dsb_size = size;
} else {
/* Found duplicate TPDMs */
return -EEXIST;
rc = tpda_get_element_size(drvdata, in, -1);
if (rc)
return rc;
}
}
return dsb_size;
return rc;
}
/* Settings pre enabling port control register */
@ -109,37 +142,24 @@ static void tpda_enable_pre_port(struct tpda_drvdata *drvdata)
static int tpda_enable_port(struct tpda_drvdata *drvdata, int port)
{
u32 val;
int size;
int rc;
val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port));
/*
* Configure aggregator port n DSB data set element size
* Set the bit to 0 if the size is 32
* Set the bit to 1 if the size is 64
*/
size = tpda_get_element_size(drvdata->csdev, port);
switch (size) {
case 32:
val &= ~TPDA_Pn_CR_DSBSIZE;
break;
case 64:
val |= TPDA_Pn_CR_DSBSIZE;
break;
case 0:
return -EEXIST;
case -EEXIST:
tpda_clear_element_size(drvdata->csdev);
rc = tpda_get_element_size(drvdata, drvdata->csdev, port);
if (!rc && (drvdata->dsb_esize || drvdata->cmb_esize)) {
tpda_set_element_size(drvdata, &val);
/* Enable the port */
val |= TPDA_Pn_CR_ENA;
writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port));
} else if (rc == -EEXIST)
dev_warn_once(&drvdata->csdev->dev,
"Detected multiple TPDMs on port %d", -EEXIST);
return -EEXIST;
default:
return -EINVAL;
}
"Detected multiple TPDMs on port %d", port);
else
dev_warn_once(&drvdata->csdev->dev,
"Didn't find TPDM element size");
/* Enable the port */
val |= TPDA_Pn_CR_ENA;
writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port));
return 0;
return rc;
}
static int __tpda_enable(struct tpda_drvdata *drvdata, int port)
@ -148,7 +168,12 @@ static int __tpda_enable(struct tpda_drvdata *drvdata, int port)
CS_UNLOCK(drvdata->base);
if (!drvdata->csdev->enable)
/*
* Only do pre-port enable for first port that calls enable when the
* device's main refcount is still 0
*/
lockdep_assert_held(&drvdata->spinlock);
if (!drvdata->csdev->refcnt)
tpda_enable_pre_port(drvdata);
ret = tpda_enable_port(drvdata, port);
@ -169,6 +194,7 @@ static int tpda_enable(struct coresight_device *csdev,
ret = __tpda_enable(drvdata, in->dest_port);
if (!ret) {
atomic_inc(&in->dest_refcnt);
csdev->refcnt++;
dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port);
}
}
@ -197,9 +223,10 @@ static void tpda_disable(struct coresight_device *csdev,
struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
spin_lock(&drvdata->spinlock);
if (atomic_dec_return(&in->dest_refcnt) == 0)
if (atomic_dec_return(&in->dest_refcnt) == 0) {
__tpda_disable(drvdata, in->dest_port);
csdev->refcnt--;
}
spin_unlock(&drvdata->spinlock);
dev_dbg(drvdata->dev, "TPDA inport %d disabled\n", in->dest_port);
@ -300,7 +327,7 @@ static struct amba_id tpda_ids[] = {
.id = 0x000f0f00,
.mask = 0x000fff00,
},
{ 0, 0},
{ 0, 0, NULL },
};
static struct amba_driver tpda_driver = {

View File

@ -10,6 +10,8 @@
#define TPDA_Pn_CR(n) (0x004 + (n * 4))
/* Aggregator port enable bit */
#define TPDA_Pn_CR_ENA BIT(0)
/* Aggregator port CMB data set element size bit */
#define TPDA_Pn_CR_CMBSIZE GENMASK(7, 6)
/* Aggregator port DSB data set element size bit */
#define TPDA_Pn_CR_DSBSIZE BIT(8)
@ -25,6 +27,8 @@
* @csdev: component vitals needed by the framework.
* @spinlock: lock for the drvdata value.
* @enable: enable status of the component.
* @dsb_esize Record the DSB element size.
* @cmb_esize Record the CMB element size.
*/
struct tpda_drvdata {
void __iomem *base;
@ -32,6 +36,8 @@ struct tpda_drvdata {
struct coresight_device *csdev;
spinlock_t spinlock;
u8 atid;
u32 dsb_esize;
u32 cmb_esize;
};
#endif /* _CORESIGHT_CORESIGHT_TPDA_H */

View File

@ -66,6 +66,31 @@ static ssize_t tpdm_simple_dataset_show(struct device *dev,
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->dsb->msr[tpdm_attr->idx]);
case CMB_TRIG_PATT:
if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->cmb->trig_patt[tpdm_attr->idx]);
case CMB_TRIG_PATT_MASK:
if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->cmb->trig_patt_mask[tpdm_attr->idx]);
case CMB_PATT:
if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->cmb->patt_val[tpdm_attr->idx]);
case CMB_PATT_MASK:
if (tpdm_attr->idx >= TPDM_CMB_MAX_PATT)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->cmb->patt_mask[tpdm_attr->idx]);
case CMB_MSR:
if (tpdm_attr->idx >= drvdata->cmb_msr_num)
return -EINVAL;
return sysfs_emit(buf, "0x%x\n",
drvdata->cmb->msr[tpdm_attr->idx]);
}
return -EINVAL;
}
@ -77,60 +102,84 @@ static ssize_t tpdm_simple_dataset_store(struct device *dev,
size_t size)
{
unsigned long val;
ssize_t ret = size;
ssize_t ret = -EINVAL;
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct tpdm_dataset_attribute *tpdm_attr =
container_of(attr, struct tpdm_dataset_attribute, attr);
if (kstrtoul(buf, 0, &val))
return -EINVAL;
return ret;
spin_lock(&drvdata->spinlock);
guard(spinlock)(&drvdata->spinlock);
switch (tpdm_attr->mem) {
case DSB_TRIG_PATT:
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT)
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) {
drvdata->dsb->trig_patt[tpdm_attr->idx] = val;
else
ret = -EINVAL;
ret = size;
}
break;
case DSB_TRIG_PATT_MASK:
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT)
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) {
drvdata->dsb->trig_patt_mask[tpdm_attr->idx] = val;
else
ret = -EINVAL;
ret = size;
}
break;
case DSB_PATT:
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT)
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) {
drvdata->dsb->patt_val[tpdm_attr->idx] = val;
else
ret = -EINVAL;
ret = size;
}
break;
case DSB_PATT_MASK:
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT)
if (tpdm_attr->idx < TPDM_DSB_MAX_PATT) {
drvdata->dsb->patt_mask[tpdm_attr->idx] = val;
else
ret = -EINVAL;
ret = size;
}
break;
case DSB_MSR:
if (tpdm_attr->idx < drvdata->dsb_msr_num)
if (tpdm_attr->idx < drvdata->dsb_msr_num) {
drvdata->dsb->msr[tpdm_attr->idx] = val;
else
ret = -EINVAL;
ret = size;
}
break;
case CMB_TRIG_PATT:
if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) {
drvdata->cmb->trig_patt[tpdm_attr->idx] = val;
ret = size;
}
break;
case CMB_TRIG_PATT_MASK:
if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) {
drvdata->cmb->trig_patt_mask[tpdm_attr->idx] = val;
ret = size;
}
break;
case CMB_PATT:
if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) {
drvdata->cmb->patt_val[tpdm_attr->idx] = val;
ret = size;
}
break;
case CMB_PATT_MASK:
if (tpdm_attr->idx < TPDM_CMB_MAX_PATT) {
drvdata->cmb->patt_mask[tpdm_attr->idx] = val;
ret = size;
}
break;
case CMB_MSR:
if (tpdm_attr->idx < drvdata->cmb_msr_num) {
drvdata->cmb->msr[tpdm_attr->idx] = val;
ret = size;
}
break;
default:
ret = -EINVAL;
break;
}
spin_unlock(&drvdata->spinlock);
return ret;
}
static bool tpdm_has_dsb_dataset(struct tpdm_drvdata *drvdata)
{
return (drvdata->datasets & TPDM_PIDR0_DS_DSB);
}
static umode_t tpdm_dsb_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
@ -143,6 +192,18 @@ static umode_t tpdm_dsb_is_visible(struct kobject *kobj,
return 0;
}
static umode_t tpdm_cmb_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_cmb_dataset(drvdata))
return attr->mode;
return 0;
}
static umode_t tpdm_dsb_msr_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
@ -159,6 +220,23 @@ static umode_t tpdm_dsb_msr_is_visible(struct kobject *kobj,
return 0;
}
static umode_t tpdm_cmb_msr_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);
struct device_attribute *dev_attr =
container_of(attr, struct device_attribute, attr);
struct tpdm_dataset_attribute *tpdm_attr =
container_of(dev_attr, struct tpdm_dataset_attribute, attr);
if (tpdm_attr->idx < drvdata->cmb_msr_num)
return attr->mode;
return 0;
}
static void tpdm_reset_datasets(struct tpdm_drvdata *drvdata)
{
if (tpdm_has_dsb_dataset(drvdata)) {
@ -167,6 +245,9 @@ static void tpdm_reset_datasets(struct tpdm_drvdata *drvdata)
drvdata->dsb->trig_ts = true;
drvdata->dsb->trig_type = false;
}
if (drvdata->cmb)
memset(drvdata->cmb, 0, sizeof(struct cmb_dataset));
}
static void set_dsb_mode(struct tpdm_drvdata *drvdata, u32 *val)
@ -233,25 +314,27 @@ static void tpdm_enable_dsb(struct tpdm_drvdata *drvdata)
{
u32 val, i;
if (!tpdm_has_dsb_dataset(drvdata))
return;
for (i = 0; i < TPDM_DSB_MAX_EDCR; i++)
writel_relaxed(drvdata->dsb->edge_ctrl[i],
drvdata->base + TPDM_DSB_EDCR(i));
drvdata->base + TPDM_DSB_EDCR(i));
for (i = 0; i < TPDM_DSB_MAX_EDCMR; i++)
writel_relaxed(drvdata->dsb->edge_ctrl_mask[i],
drvdata->base + TPDM_DSB_EDCMR(i));
drvdata->base + TPDM_DSB_EDCMR(i));
for (i = 0; i < TPDM_DSB_MAX_PATT; i++) {
writel_relaxed(drvdata->dsb->patt_val[i],
drvdata->base + TPDM_DSB_TPR(i));
drvdata->base + TPDM_DSB_TPR(i));
writel_relaxed(drvdata->dsb->patt_mask[i],
drvdata->base + TPDM_DSB_TPMR(i));
drvdata->base + TPDM_DSB_TPMR(i));
writel_relaxed(drvdata->dsb->trig_patt[i],
drvdata->base + TPDM_DSB_XPR(i));
drvdata->base + TPDM_DSB_XPR(i));
writel_relaxed(drvdata->dsb->trig_patt_mask[i],
drvdata->base + TPDM_DSB_XPMR(i));
drvdata->base + TPDM_DSB_XPMR(i));
}
set_dsb_tier(drvdata);
set_dsb_msr(drvdata);
val = readl_relaxed(drvdata->base + TPDM_DSB_CR);
@ -267,6 +350,76 @@ static void tpdm_enable_dsb(struct tpdm_drvdata *drvdata)
writel_relaxed(val, drvdata->base + TPDM_DSB_CR);
}
static void set_cmb_tier(struct tpdm_drvdata *drvdata)
{
u32 val;
val = readl_relaxed(drvdata->base + TPDM_CMB_TIER);
/* Clear all relevant fields */
val &= ~(TPDM_CMB_TIER_PATT_TSENAB | TPDM_CMB_TIER_TS_ALL |
TPDM_CMB_TIER_XTRIG_TSENAB);
/* Set pattern timestamp type and enablement */
if (drvdata->cmb->patt_ts)
val |= TPDM_CMB_TIER_PATT_TSENAB;
/* Set trigger timestamp */
if (drvdata->cmb->trig_ts)
val |= TPDM_CMB_TIER_XTRIG_TSENAB;
/* Set all timestamp enablement*/
if (drvdata->cmb->ts_all)
val |= TPDM_CMB_TIER_TS_ALL;
writel_relaxed(val, drvdata->base + TPDM_CMB_TIER);
}
static void set_cmb_msr(struct tpdm_drvdata *drvdata)
{
int i;
for (i = 0; i < drvdata->cmb_msr_num; i++)
writel_relaxed(drvdata->cmb->msr[i],
drvdata->base + TPDM_CMB_MSR(i));
}
static void tpdm_enable_cmb(struct tpdm_drvdata *drvdata)
{
u32 val, i;
if (!tpdm_has_cmb_dataset(drvdata))
return;
/* Configure pattern registers */
for (i = 0; i < TPDM_CMB_MAX_PATT; i++) {
writel_relaxed(drvdata->cmb->patt_val[i],
drvdata->base + TPDM_CMB_TPR(i));
writel_relaxed(drvdata->cmb->patt_mask[i],
drvdata->base + TPDM_CMB_TPMR(i));
writel_relaxed(drvdata->cmb->trig_patt[i],
drvdata->base + TPDM_CMB_XPR(i));
writel_relaxed(drvdata->cmb->trig_patt_mask[i],
drvdata->base + TPDM_CMB_XPMR(i));
}
set_cmb_tier(drvdata);
set_cmb_msr(drvdata);
val = readl_relaxed(drvdata->base + TPDM_CMB_CR);
/*
* Set to 0 for continuous CMB collection mode,
* 1 for trace-on-change CMB collection mode.
*/
if (drvdata->cmb->trace_mode)
val |= TPDM_CMB_CR_MODE;
else
val &= ~TPDM_CMB_CR_MODE;
/* Set the enable bit of CMB control register to 1 */
val |= TPDM_CMB_CR_ENA;
writel_relaxed(val, drvdata->base + TPDM_CMB_CR);
}
/*
* TPDM enable operations
* The TPDM or Monitor serves as data collection component for various
@ -279,8 +432,8 @@ static void __tpdm_enable(struct tpdm_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
if (tpdm_has_dsb_dataset(drvdata))
tpdm_enable_dsb(drvdata);
tpdm_enable_dsb(drvdata);
tpdm_enable_cmb(drvdata);
CS_LOCK(drvdata->base);
}
@ -308,19 +461,35 @@ static void tpdm_disable_dsb(struct tpdm_drvdata *drvdata)
{
u32 val;
if (!tpdm_has_dsb_dataset(drvdata))
return;
/* Set the enable bit of DSB control register to 0 */
val = readl_relaxed(drvdata->base + TPDM_DSB_CR);
val &= ~TPDM_DSB_CR_ENA;
writel_relaxed(val, drvdata->base + TPDM_DSB_CR);
}
static void tpdm_disable_cmb(struct tpdm_drvdata *drvdata)
{
u32 val;
if (!tpdm_has_cmb_dataset(drvdata))
return;
val = readl_relaxed(drvdata->base + TPDM_CMB_CR);
/* Set the enable bit of CMB control register to 0 */
val &= ~TPDM_CMB_CR_ENA;
writel_relaxed(val, drvdata->base + TPDM_CMB_CR);
}
/* TPDM disable operations */
static void __tpdm_disable(struct tpdm_drvdata *drvdata)
{
CS_UNLOCK(drvdata->base);
if (tpdm_has_dsb_dataset(drvdata))
tpdm_disable_dsb(drvdata);
tpdm_disable_dsb(drvdata);
tpdm_disable_cmb(drvdata);
CS_LOCK(drvdata->base);
}
@ -366,6 +535,12 @@ static int tpdm_datasets_setup(struct tpdm_drvdata *drvdata)
if (!drvdata->dsb)
return -ENOMEM;
}
if (tpdm_has_cmb_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;
@ -577,9 +752,18 @@ static ssize_t enable_ts_show(struct device *dev,
char *buf)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct tpdm_dataset_attribute *tpdm_attr =
container_of(attr, struct tpdm_dataset_attribute, attr);
ssize_t size = -EINVAL;
return sysfs_emit(buf, "%u\n",
(unsigned int)drvdata->dsb->patt_ts);
if (tpdm_attr->mem == DSB_PATT)
size = sysfs_emit(buf, "%u\n",
(unsigned int)drvdata->dsb->patt_ts);
else if (tpdm_attr->mem == CMB_PATT)
size = sysfs_emit(buf, "%u\n",
(unsigned int)drvdata->cmb->patt_ts);
return size;
}
/*
@ -591,17 +775,23 @@ static ssize_t enable_ts_store(struct device *dev,
size_t size)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
struct tpdm_dataset_attribute *tpdm_attr =
container_of(attr, struct tpdm_dataset_attribute, attr);
unsigned long val;
if ((kstrtoul(buf, 0, &val)) || (val & ~1UL))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->dsb->patt_ts = !!val;
spin_unlock(&drvdata->spinlock);
guard(spinlock)(&drvdata->spinlock);
if (tpdm_attr->mem == DSB_PATT)
drvdata->dsb->patt_ts = !!val;
else if (tpdm_attr->mem == CMB_PATT)
drvdata->cmb->patt_ts = !!val;
else
return -EINVAL;
return size;
}
static DEVICE_ATTR_RW(enable_ts);
static ssize_t set_type_show(struct device *dev,
struct device_attribute *attr,
@ -704,6 +894,96 @@ static ssize_t dsb_trig_ts_store(struct device *dev,
}
static DEVICE_ATTR_RW(dsb_trig_ts);
static ssize_t cmb_mode_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent);
return sysfs_emit(buf, "%x\n", drvdata->cmb->trace_mode);
}
static ssize_t cmb_mode_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 trace_mode;
if (kstrtoul(buf, 0, &trace_mode) || (trace_mode & ~1UL))
return -EINVAL;
spin_lock(&drvdata->spinlock);
drvdata->cmb->trace_mode = trace_mode;
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_RW(cmb_mode);
static ssize_t cmb_ts_all_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->ts_all);
}
static ssize_t cmb_ts_all_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 & ~1UL))
return -EINVAL;
guard(spinlock)(&drvdata->spinlock);
if (val)
drvdata->cmb->ts_all = true;
else
drvdata->cmb->ts_all = false;
return size;
}
static DEVICE_ATTR_RW(cmb_ts_all);
static ssize_t cmb_trig_ts_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->trig_ts);
}
static ssize_t cmb_trig_ts_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 & ~1UL))
return -EINVAL;
guard(spinlock)(&drvdata->spinlock);
if (val)
drvdata->cmb->trig_ts = true;
else
drvdata->cmb->trig_ts = false;
return size;
}
static DEVICE_ATTR_RW(cmb_trig_ts);
static struct attribute *tpdm_dsb_edge_attrs[] = {
&dev_attr_ctrl_idx.attr,
&dev_attr_ctrl_val.attr,
@ -772,7 +1052,7 @@ static struct attribute *tpdm_dsb_patt_attrs[] = {
DSB_PATT_MASK_ATTR(5),
DSB_PATT_MASK_ATTR(6),
DSB_PATT_MASK_ATTR(7),
&dev_attr_enable_ts.attr,
DSB_PATT_ENABLE_TS,
&dev_attr_set_type.attr,
NULL,
};
@ -813,6 +1093,59 @@ static struct attribute *tpdm_dsb_msr_attrs[] = {
NULL,
};
static struct attribute *tpdm_cmb_trig_patt_attrs[] = {
CMB_TRIG_PATT_ATTR(0),
CMB_TRIG_PATT_ATTR(1),
CMB_TRIG_PATT_MASK_ATTR(0),
CMB_TRIG_PATT_MASK_ATTR(1),
NULL,
};
static struct attribute *tpdm_cmb_patt_attrs[] = {
CMB_PATT_ATTR(0),
CMB_PATT_ATTR(1),
CMB_PATT_MASK_ATTR(0),
CMB_PATT_MASK_ATTR(1),
CMB_PATT_ENABLE_TS,
NULL,
};
static struct attribute *tpdm_cmb_msr_attrs[] = {
CMB_MSR_ATTR(0),
CMB_MSR_ATTR(1),
CMB_MSR_ATTR(2),
CMB_MSR_ATTR(3),
CMB_MSR_ATTR(4),
CMB_MSR_ATTR(5),
CMB_MSR_ATTR(6),
CMB_MSR_ATTR(7),
CMB_MSR_ATTR(8),
CMB_MSR_ATTR(9),
CMB_MSR_ATTR(10),
CMB_MSR_ATTR(11),
CMB_MSR_ATTR(12),
CMB_MSR_ATTR(13),
CMB_MSR_ATTR(14),
CMB_MSR_ATTR(15),
CMB_MSR_ATTR(16),
CMB_MSR_ATTR(17),
CMB_MSR_ATTR(18),
CMB_MSR_ATTR(19),
CMB_MSR_ATTR(20),
CMB_MSR_ATTR(21),
CMB_MSR_ATTR(22),
CMB_MSR_ATTR(23),
CMB_MSR_ATTR(24),
CMB_MSR_ATTR(25),
CMB_MSR_ATTR(26),
CMB_MSR_ATTR(27),
CMB_MSR_ATTR(28),
CMB_MSR_ATTR(29),
CMB_MSR_ATTR(30),
CMB_MSR_ATTR(31),
NULL,
};
static struct attribute *tpdm_dsb_attrs[] = {
&dev_attr_dsb_mode.attr,
&dev_attr_dsb_trig_ts.attr,
@ -820,6 +1153,13 @@ static struct attribute *tpdm_dsb_attrs[] = {
NULL,
};
static struct attribute *tpdm_cmb_attrs[] = {
&dev_attr_cmb_mode.attr,
&dev_attr_cmb_ts_all.attr,
&dev_attr_cmb_trig_ts.attr,
NULL,
};
static struct attribute_group tpdm_dsb_attr_grp = {
.attrs = tpdm_dsb_attrs,
.is_visible = tpdm_dsb_is_visible,
@ -849,6 +1189,29 @@ static struct attribute_group tpdm_dsb_msr_grp = {
.name = "dsb_msr",
};
static struct attribute_group tpdm_cmb_attr_grp = {
.attrs = tpdm_cmb_attrs,
.is_visible = tpdm_cmb_is_visible,
};
static struct attribute_group tpdm_cmb_trig_patt_grp = {
.attrs = tpdm_cmb_trig_patt_attrs,
.is_visible = tpdm_cmb_is_visible,
.name = "cmb_trig_patt",
};
static struct attribute_group tpdm_cmb_patt_grp = {
.attrs = tpdm_cmb_patt_attrs,
.is_visible = tpdm_cmb_is_visible,
.name = "cmb_patt",
};
static struct attribute_group tpdm_cmb_msr_grp = {
.attrs = tpdm_cmb_msr_attrs,
.is_visible = tpdm_cmb_msr_is_visible,
.name = "cmb_msr",
};
static const struct attribute_group *tpdm_attr_grps[] = {
&tpdm_attr_grp,
&tpdm_dsb_attr_grp,
@ -856,6 +1219,10 @@ static const struct attribute_group *tpdm_attr_grps[] = {
&tpdm_dsb_trig_patt_grp,
&tpdm_dsb_patt_grp,
&tpdm_dsb_msr_grp,
&tpdm_cmb_attr_grp,
&tpdm_cmb_trig_patt_grp,
&tpdm_cmb_patt_grp,
&tpdm_cmb_msr_grp,
NULL,
};
@ -894,6 +1261,10 @@ static int tpdm_probe(struct amba_device *adev, const struct amba_id *id)
of_property_read_u32(drvdata->dev->of_node,
"qcom,dsb-msrs-num", &drvdata->dsb_msr_num);
if (drvdata && tpdm_has_cmb_dataset(drvdata))
of_property_read_u32(drvdata->dev->of_node,
"qcom,cmb-msrs-num", &drvdata->cmb_msr_num);
/* Set up coresight component description */
desc.name = coresight_alloc_device_name(&tpdm_devs, dev);
if (!desc.name)
@ -933,7 +1304,7 @@ static struct amba_id tpdm_ids[] = {
.id = 0x000f0e00,
.mask = 0x000fff00,
},
{ 0, 0},
{ 0, 0, NULL },
};
static struct amba_driver tpdm_driver = {

View File

@ -9,6 +9,38 @@
/* The max number of the datasets that TPDM supports */
#define TPDM_DATASETS 7
/* CMB Subunit Registers */
#define TPDM_CMB_CR (0xA00)
/* CMB subunit timestamp insertion enable register */
#define TPDM_CMB_TIER (0xA04)
/* CMB subunit timestamp pattern registers */
#define TPDM_CMB_TPR(n) (0xA08 + (n * 4))
/* CMB subunit timestamp pattern mask registers */
#define TPDM_CMB_TPMR(n) (0xA10 + (n * 4))
/* CMB subunit trigger pattern registers */
#define TPDM_CMB_XPR(n) (0xA18 + (n * 4))
/* CMB subunit trigger pattern mask registers */
#define TPDM_CMB_XPMR(n) (0xA20 + (n * 4))
/* CMB MSR register */
#define TPDM_CMB_MSR(n) (0xA80 + (n * 4))
/* Enable bit for CMB subunit */
#define TPDM_CMB_CR_ENA BIT(0)
/* Trace collection mode for CMB subunit */
#define TPDM_CMB_CR_MODE BIT(1)
/* Timestamp control for pattern match */
#define TPDM_CMB_TIER_PATT_TSENAB BIT(0)
/* CMB CTI timestamp request */
#define TPDM_CMB_TIER_XTRIG_TSENAB BIT(1)
/* For timestamp fo all trace */
#define TPDM_CMB_TIER_TS_ALL BIT(2)
/* Patten register number */
#define TPDM_CMB_MAX_PATT 2
/* MAX number of DSB MSR */
#define TPDM_CMB_MAX_MSR 32
/* DSB Subunit Registers */
#define TPDM_DSB_CR (0x780)
#define TPDM_DSB_TIER (0x784)
@ -79,10 +111,12 @@
*
* 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
*/
#define TPDM_PIDR0_DS_IMPDEF BIT(0)
#define TPDM_PIDR0_DS_DSB BIT(1)
#define TPDM_PIDR0_DS_CMB BIT(2)
#define TPDM_DSB_MAX_LINES 256
/* MAX number of EDCR registers */
@ -113,6 +147,16 @@
} \
})[0].attr.attr)
#define tpdm_patt_enable_ts(name, mem) \
(&((struct tpdm_dataset_attribute[]) { \
{ \
__ATTR(name, 0644, enable_ts_show, \
enable_ts_store), \
mem, \
0, \
} \
})[0].attr.attr)
#define DSB_EDGE_CTRL_ATTR(nr) \
tpdm_simple_dataset_ro(edcr##nr, \
DSB_EDGE_CTRL, nr)
@ -137,10 +181,38 @@
tpdm_simple_dataset_rw(tpmr##nr, \
DSB_PATT_MASK, nr)
#define DSB_PATT_ENABLE_TS \
tpdm_patt_enable_ts(enable_ts, \
DSB_PATT)
#define DSB_MSR_ATTR(nr) \
tpdm_simple_dataset_rw(msr##nr, \
DSB_MSR, nr)
#define CMB_TRIG_PATT_ATTR(nr) \
tpdm_simple_dataset_rw(xpr##nr, \
CMB_TRIG_PATT, nr)
#define CMB_TRIG_PATT_MASK_ATTR(nr) \
tpdm_simple_dataset_rw(xpmr##nr, \
CMB_TRIG_PATT_MASK, nr)
#define CMB_PATT_ATTR(nr) \
tpdm_simple_dataset_rw(tpr##nr, \
CMB_PATT, nr)
#define CMB_PATT_MASK_ATTR(nr) \
tpdm_simple_dataset_rw(tpmr##nr, \
CMB_PATT_MASK, nr)
#define CMB_PATT_ENABLE_TS \
tpdm_patt_enable_ts(enable_ts, \
CMB_PATT)
#define CMB_MSR_ATTR(nr) \
tpdm_simple_dataset_rw(msr##nr, \
CMB_MSR, nr)
/**
* struct dsb_dataset - specifics associated to dsb dataset
* @mode: DSB programming mode
@ -173,6 +245,30 @@ struct dsb_dataset {
bool trig_type;
};
/**
* struct cmb_dataset
* @trace_mode: Dataset collection mode
* @patt_val: Save value for pattern
* @patt_mask: Save value for pattern mask
* @trig_patt: Save value for trigger pattern
* @trig_patt_mask: Save value for trigger pattern mask
* @msr Save value for MSR
* @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 cmb_dataset {
u32 trace_mode;
u32 patt_val[TPDM_CMB_MAX_PATT];
u32 patt_mask[TPDM_CMB_MAX_PATT];
u32 trig_patt[TPDM_CMB_MAX_PATT];
u32 trig_patt_mask[TPDM_CMB_MAX_PATT];
u32 msr[TPDM_CMB_MAX_MSR];
bool patt_ts;
bool trig_ts;
bool ts_all;
};
/**
* struct tpdm_drvdata - specifics associated to an TPDM component
* @base: memory mapped base address for this component.
@ -182,7 +278,9 @@ struct dsb_dataset {
* @enable: enable status of the component.
* @datasets: The datasets types present of the TPDM.
* @dsb Specifics associated to TPDM DSB.
* @cmb Specifics associated to TPDM CMB.
* @dsb_msr_num Number of MSR supported by DSB TPDM
* @cmb_msr_num Number of MSR supported by CMB TPDM
*/
struct tpdm_drvdata {
@ -193,7 +291,9 @@ struct tpdm_drvdata {
bool enable;
unsigned long datasets;
struct dsb_dataset *dsb;
struct cmb_dataset *cmb;
u32 dsb_msr_num;
u32 cmb_msr_num;
};
/* Enumerate members of various datasets */
@ -205,6 +305,11 @@ enum dataset_mem {
DSB_PATT,
DSB_PATT_MASK,
DSB_MSR,
CMB_TRIG_PATT,
CMB_TRIG_PATT_MASK,
CMB_PATT,
CMB_PATT_MASK,
CMB_MSR
};
/**
@ -220,4 +325,13 @@ struct tpdm_dataset_attribute {
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

@ -58,6 +58,7 @@ struct tpiu_drvdata {
void __iomem *base;
struct clk *atclk;
struct coresight_device *csdev;
spinlock_t spinlock;
};
static void tpiu_enable_hw(struct csdev_access *csa)
@ -72,8 +73,11 @@ static void tpiu_enable_hw(struct csdev_access *csa)
static int tpiu_enable(struct coresight_device *csdev, enum cs_mode mode,
void *__unused)
{
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
guard(spinlock)(&drvdata->spinlock);
tpiu_enable_hw(&csdev->access);
atomic_inc(&csdev->refcnt);
csdev->refcnt++;
dev_dbg(&csdev->dev, "TPIU enabled\n");
return 0;
}
@ -96,7 +100,11 @@ static void tpiu_disable_hw(struct csdev_access *csa)
static int tpiu_disable(struct coresight_device *csdev)
{
if (atomic_dec_return(&csdev->refcnt))
struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
guard(spinlock)(&drvdata->spinlock);
csdev->refcnt--;
if (csdev->refcnt)
return -EBUSY;
tpiu_disable_hw(&csdev->access);
@ -132,6 +140,8 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id)
if (!drvdata)
return -ENOMEM;
spin_lock_init(&drvdata->spinlock);
drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */
if (!IS_ERR(drvdata->atclk)) {
ret = clk_prepare_enable(drvdata->atclk);
@ -218,7 +228,7 @@ static const struct amba_id tpiu_ids[] = {
.id = 0x000bb9e7,
.mask = 0x000fffff,
},
{ 0, 0},
{ 0, 0, NULL },
};
MODULE_DEVICE_TABLE(amba, tpiu_ids);

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