mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
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:
commit
bb41fe35dc
2
.mailmap
2
.mailmap
@ -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>
|
||||
|
@ -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.
|
||||
|
9
Documentation/ABI/testing/sysfs-bus-iio-adc-pac1934
Normal file
9
Documentation/ABI/testing/sysfs-bus-iio-adc-pac1934
Normal 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.
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
|
@ -22,7 +22,6 @@ properties:
|
||||
maxItems: 1
|
||||
|
||||
label:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description: Unique name to identify which channel this is.
|
||||
|
||||
bipolar:
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
@ -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>;
|
||||
};
|
||||
...
|
||||
|
120
Documentation/devicetree/bindings/iio/adc/microchip,pac1934.yaml
Normal file
120
Documentation/devicetree/bindings/iio/adc/microchip,pac1934.yaml
Normal 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;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -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 = <®_vref_1v8>;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
80
Documentation/devicetree/bindings/iio/adc/ti,ads1298.yaml
Normal file
80
Documentation/devicetree/bindings/iio/adc/ti,ads1298.yaml
Normal 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 = <®_iso_5v_a>;
|
||||
clocks = <&clk_ads1298>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <78 IRQ_TYPE_EDGE_FALLING>;
|
||||
spi-max-frequency = <20000000>;
|
||||
spi-cpha;
|
||||
};
|
||||
};
|
||||
...
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
@ -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:
|
||||
- |
|
||||
|
@ -27,6 +27,9 @@ properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -21,6 +21,7 @@ properties:
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
|
@ -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 = <®_dldo1>;
|
||||
dvdd-supply = <®_dldo1>;
|
||||
reset-gpios = <&pio 1 1 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
@ -99,6 +99,9 @@ required:
|
||||
- honeywell,transfer-function
|
||||
- honeywell,pressure-triplet
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
dependentSchemas:
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
@ -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
|
||||
|
18
Documentation/devicetree/bindings/nvmem/nvmem-provider.yaml
Normal file
18
Documentation/devicetree/bindings/nvmem/nvmem-provider.yaml
Normal 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
|
@ -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";
|
||||
|
||||
...
|
||||
};
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
@ -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,.*":
|
||||
|
407
Documentation/iio/adis16475.rst
Normal file
407
Documentation/iio/adis16475.rst
Normal 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
|
152
Documentation/iio/iio_devbuf.rst
Normal file
152
Documentation/iio/iio_devbuf.rst
Normal 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.
|
@ -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
|
||||
|
49
MAINTAINERS
49
MAINTAINERS
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
|
@ -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:
|
||||
|
282
drivers/bus/mhi/host/trace.h
Normal file
282
drivers/bus/mhi/host/trace.h
Normal 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>
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
192
drivers/cdx/cdx_msi.c
Normal 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);
|
@ -9,6 +9,7 @@ if CDX_BUS
|
||||
|
||||
config CDX_CONTROLLER
|
||||
tristate "CDX bus controller"
|
||||
select GENERIC_MSI_IRQ
|
||||
select REMOTEPROC
|
||||
select RPMSG
|
||||
help
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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 @@ err_svc_id_free:
|
||||
* 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,
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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 \
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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 @@ done:
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_alloc_device_name);
|
||||
|
||||
struct bus_type coresight_bustype = {
|
||||
const struct bus_type coresight_bustype = {
|
||||
.name = "coresight",
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 = {
|
||||
|
@ -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 */
|
||||
|
@ -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 = {
|
||||
|
@ -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 */
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user