mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-29 17:22:07 +00:00
Char/Misc and other driver subsystem changes for 6.10-rc1
Here is the big set of char/misc and other driver subsystem updates for 6.10-rc1. Nothing major here, just lots of new drivers and updates for apis and new hardware types. Included in here are: - big IIO driver updates with more devices and drivers added - fpga driver updates - hyper-v driver updates - uio_pruss driver removal, no one uses it, other drivers control the same hardware now - binder minor updates - mhi driver updates - excon driver updates - counter driver updates - accessability driver updates - coresight driver updates - other hwtracing driver updates - nvmem driver updates - slimbus driver updates - spmi driver updates - other smaller misc and char driver updates All of these have been in linux-next for a while with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZk3lTg8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ynhZQCfSWyK0lHsys2LhEBmufrB3RCgnZwAn3Lm2eJY WVk7h01A0lHyacrzm5LN =s95M -----END PGP SIGNATURE----- Merge tag 'char-misc-6.10-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 other driver subsystem updates for 6.10-rc1. Nothing major here, just lots of new drivers and updates for apis and new hardware types. Included in here are: - big IIO driver updates with more devices and drivers added - fpga driver updates - hyper-v driver updates - uio_pruss driver removal, no one uses it, other drivers control the same hardware now - binder minor updates - mhi driver updates - excon driver updates - counter driver updates - accessability driver updates - coresight driver updates - other hwtracing driver updates - nvmem driver updates - slimbus driver updates - spmi driver updates - other smaller misc and char driver updates All of these have been in linux-next for a while with no reported issues" * tag 'char-misc-6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (319 commits) misc: ntsync: mark driver as "broken" to prevent from building spmi: pmic-arb: Add multi bus support spmi: pmic-arb: Register controller for bus instead of arbiter spmi: pmic-arb: Make core resources acquiring a version operation spmi: pmic-arb: Make the APID init a version operation spmi: pmic-arb: Fix some compile warnings about members not being described dt-bindings: spmi: Deprecate qcom,bus-id dt-bindings: spmi: Add X1E80100 SPMI PMIC ARB schema spmi: pmic-arb: Replace three IS_ERR() calls by null pointer checks in spmi_pmic_arb_probe() spmi: hisi-spmi-controller: Do not override device identifier dt-bindings: spmi: hisilicon,hisi-spmi-controller: clean up example dt-bindings: spmi: hisilicon,hisi-spmi-controller: fix binding references spmi: make spmi_bus_type const extcon: adc-jack: Document missing struct members extcon: realtek: Remove unused of_gpio.h extcon: usbc-cros-ec: Convert to platform remove callback returning void extcon: usb-gpio: Convert to platform remove callback returning void extcon: max77843: Convert to platform remove callback returning void extcon: max3355: Convert to platform remove callback returning void extcon: intel-mrfld: Convert to platform remove callback returning void ...
This commit is contained in:
commit
5f16eb0549
@ -29,3 +29,16 @@ Description: Initiates a SoC reset on the MHI controller. A SoC reset is
|
||||
This can be useful as a method of recovery if the device is
|
||||
non-responsive, or as a means of loading new firmware as a
|
||||
system administration task.
|
||||
|
||||
What: /sys/bus/mhi/devices/.../trigger_edl
|
||||
Date: April 2024
|
||||
KernelVersion: 6.10
|
||||
Contact: mhi@lists.linux.dev
|
||||
Description: Writing a non-zero value to this file will force devices to
|
||||
enter EDL (Emergency Download) mode. This entry only exists for
|
||||
devices capable of entering the EDL mode using the standard EDL
|
||||
triggering mechanism defined in the MHI spec v1.2. Once in EDL
|
||||
mode, the flash programmer image can be downloaded to the
|
||||
device to enter the flash programmer execution environment.
|
||||
This can be useful if user wants to use QDL (Qualcomm Download,
|
||||
which is used to download firmware over EDL) to update firmware.
|
||||
|
@ -22,7 +22,7 @@ Contact: Mathieu Poirier <mathieu.poirier@linaro.org>
|
||||
Description: (RW) Used in conjunction with @addr_idx. Specifies
|
||||
characteristics about the address comparator being configure,
|
||||
for example the access type, the kind of instruction to trace,
|
||||
processor contect ID to trigger on, etc. Individual fields in
|
||||
processor context ID to trigger on, etc. Individual fields in
|
||||
the access type register may vary on the version of the trace
|
||||
entity.
|
||||
|
||||
|
@ -97,7 +97,7 @@ Date: August 2023
|
||||
KernelVersion: 6.7
|
||||
Contact: Anshuman Khandual <anshuman.khandual@arm.com>
|
||||
Description: (Read) Shows all supported Coresight TMC-ETR buffer modes available
|
||||
for the users to configure explicitly. This file is avaialble only
|
||||
for the users to configure explicitly. This file is available only
|
||||
for TMC ETR devices.
|
||||
|
||||
What: /sys/bus/coresight/devices/<memory_map>.tmc/buf_mode_preferred
|
||||
|
@ -244,7 +244,7 @@ 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
|
||||
Only value 0 and 1 can be written to this node. Set this node to 1 to request
|
||||
timestamp to all trace packet.
|
||||
Accepts only one of the 2 values - 0 or 1.
|
||||
0 : Disable the timestamp of all trace packets.
|
||||
|
@ -1,4 +1,4 @@
|
||||
What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/tune
|
||||
What: /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>/tune
|
||||
Date: October 2022
|
||||
KernelVersion: 6.1
|
||||
Contact: Yicong Yang <yangyicong@hisilicon.com>
|
||||
@ -8,7 +8,7 @@ Description: This directory contains files for tuning the PCIe link
|
||||
|
||||
See Documentation/trace/hisi-ptt.rst for more information.
|
||||
|
||||
What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/tune/qos_tx_cpl
|
||||
What: /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>/tune/qos_tx_cpl
|
||||
Date: October 2022
|
||||
KernelVersion: 6.1
|
||||
Contact: Yicong Yang <yangyicong@hisilicon.com>
|
||||
@ -18,7 +18,7 @@ Description: (RW) Controls the weight of Tx completion TLPs, which influence
|
||||
will return an error, and out of range values will be converted
|
||||
to 2. The value indicates a probable level of the event.
|
||||
|
||||
What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/tune/qos_tx_np
|
||||
What: /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>/tune/qos_tx_np
|
||||
Date: October 2022
|
||||
KernelVersion: 6.1
|
||||
Contact: Yicong Yang <yangyicong@hisilicon.com>
|
||||
@ -28,7 +28,7 @@ Description: (RW) Controls the weight of Tx non-posted TLPs, which influence
|
||||
will return an error, and out of range values will be converted
|
||||
to 2. The value indicates a probable level of the event.
|
||||
|
||||
What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/tune/qos_tx_p
|
||||
What: /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>/tune/qos_tx_p
|
||||
Date: October 2022
|
||||
KernelVersion: 6.1
|
||||
Contact: Yicong Yang <yangyicong@hisilicon.com>
|
||||
@ -38,7 +38,7 @@ Description: (RW) Controls the weight of Tx posted TLPs, which influence the
|
||||
will return an error, and out of range values will be converted
|
||||
to 2. The value indicates a probable level of the event.
|
||||
|
||||
What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/tune/rx_alloc_buf_level
|
||||
What: /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>/tune/rx_alloc_buf_level
|
||||
Date: October 2022
|
||||
KernelVersion: 6.1
|
||||
Contact: Yicong Yang <yangyicong@hisilicon.com>
|
||||
@ -49,7 +49,7 @@ Description: (RW) Control the allocated buffer watermark for inbound packets.
|
||||
will return an error, and out of range values will be converted
|
||||
to 2. The value indicates a probable level of the event.
|
||||
|
||||
What: /sys/devices/hisi_ptt<sicl_id>_<core_id>/tune/tx_alloc_buf_level
|
||||
What: /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>/tune/tx_alloc_buf_level
|
||||
Date: October 2022
|
||||
KernelVersion: 6.1
|
||||
Contact: Yicong Yang <yangyicong@hisilicon.com>
|
@ -243,7 +243,8 @@ Description:
|
||||
less measurements. Units after application of scale and offset
|
||||
are milli degrees Celsius.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_tempX_input
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_tempY_input
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_temp_input
|
||||
KernelVersion: 2.6.38
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
19
Documentation/ABI/testing/sysfs-bus-iio-ad9739a
Normal file
19
Documentation/ABI/testing/sysfs-bus-iio-ad9739a
Normal file
@ -0,0 +1,19 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode
|
||||
KernelVersion: 6.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
DAC operating mode. One of the following modes can be selected:
|
||||
|
||||
* normal: This is DAC normal mode.
|
||||
* mixed-mode: In this mode the output is effectively chopped at
|
||||
the DAC sample rate. This has the effect of
|
||||
reducing the power of the fundamental signal while
|
||||
increasing the power of the images centered around
|
||||
the DAC sample rate, thus improving the output
|
||||
power of these images.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode_available
|
||||
KernelVersion: 6.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Available operating modes.
|
@ -66,13 +66,11 @@ properties:
|
||||
- const: apb_pclk
|
||||
|
||||
in-ports:
|
||||
type: object
|
||||
description: |
|
||||
Input connections from TPDM to TPDA
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
|
||||
out-ports:
|
||||
type: object
|
||||
description: |
|
||||
Output connections from the TPDA to legacy CoreSight trace bus.
|
||||
$ref: /schemas/graph.yaml#/properties/ports
|
||||
@ -97,33 +95,31 @@ examples:
|
||||
# minimum tpda definition.
|
||||
- |
|
||||
tpda@6004000 {
|
||||
compatible = "qcom,coresight-tpda", "arm,primecell";
|
||||
reg = <0x6004000 0x1000>;
|
||||
compatible = "qcom,coresight-tpda", "arm,primecell";
|
||||
reg = <0x6004000 0x1000>;
|
||||
|
||||
clocks = <&aoss_qmp>;
|
||||
clock-names = "apb_pclk";
|
||||
clocks = <&aoss_qmp>;
|
||||
clock-names = "apb_pclk";
|
||||
|
||||
in-ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
in-ports {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
port@0 {
|
||||
reg = <0>;
|
||||
tpda_qdss_0_in_tpdm_dcc: endpoint {
|
||||
remote-endpoint =
|
||||
<&tpdm_dcc_out_tpda_qdss_0>;
|
||||
};
|
||||
remote-endpoint = <&tpdm_dcc_out_tpda_qdss_0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
out-ports {
|
||||
port {
|
||||
tpda_qdss_out_funnel_in0: endpoint {
|
||||
remote-endpoint =
|
||||
<&funnel_in0_in_tpda_qdss>;
|
||||
};
|
||||
out-ports {
|
||||
port {
|
||||
tpda_qdss_out_funnel_in0: endpoint {
|
||||
remote-endpoint = <&funnel_in0_in_tpda_qdss>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
@ -0,0 +1,86 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/fpga/xlnx,fpga-selectmap.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Xilinx SelectMAP FPGA interface
|
||||
|
||||
maintainers:
|
||||
- Charles Perry <charles.perry@savoirfairelinux.com>
|
||||
|
||||
description: |
|
||||
Xilinx 7 Series FPGAs support a method of loading the bitstream over a
|
||||
parallel port named the SelectMAP interface in the documentation. Only
|
||||
the x8 mode is supported where data is loaded at one byte per rising edge of
|
||||
the clock, with the MSB of each byte presented to the D0 pin.
|
||||
|
||||
Datasheets:
|
||||
https://www.xilinx.com/support/documentation/user_guides/ug470_7Series_Config.pdf
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/memory-controllers/mc-peripheral-props.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- xlnx,fpga-xc7s-selectmap
|
||||
- xlnx,fpga-xc7a-selectmap
|
||||
- xlnx,fpga-xc7k-selectmap
|
||||
- xlnx,fpga-xc7v-selectmap
|
||||
|
||||
reg:
|
||||
description:
|
||||
At least 1 byte of memory mapped IO
|
||||
maxItems: 1
|
||||
|
||||
prog-gpios:
|
||||
description:
|
||||
config pin (referred to as PROGRAM_B in the manual)
|
||||
maxItems: 1
|
||||
|
||||
done-gpios:
|
||||
description:
|
||||
config status pin (referred to as DONE in the manual)
|
||||
maxItems: 1
|
||||
|
||||
init-gpios:
|
||||
description:
|
||||
initialization status and configuration error pin
|
||||
(referred to as INIT_B in the manual)
|
||||
maxItems: 1
|
||||
|
||||
csi-gpios:
|
||||
description:
|
||||
chip select pin (referred to as CSI_B in the manual)
|
||||
Optional gpio for if the bus controller does not provide a chip select.
|
||||
maxItems: 1
|
||||
|
||||
rdwr-gpios:
|
||||
description:
|
||||
read/write select pin (referred to as RDWR_B in the manual)
|
||||
Optional gpio for if the bus controller does not provide this pin.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- prog-gpios
|
||||
- done-gpios
|
||||
- init-gpios
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
fpga-mgr@8000000 {
|
||||
compatible = "xlnx,fpga-xc7s-selectmap";
|
||||
reg = <0x8000000 0x4>;
|
||||
prog-gpios = <&gpio5 5 GPIO_ACTIVE_LOW>;
|
||||
init-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
|
||||
done-gpios = <&gpio2 30 GPIO_ACTIVE_HIGH>;
|
||||
csi-gpios = <&gpio3 19 GPIO_ACTIVE_LOW>;
|
||||
rdwr-gpios = <&gpio3 10 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
...
|
@ -32,6 +32,8 @@ properties:
|
||||
|
||||
spi-cpol: true
|
||||
|
||||
spi-3wire: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
|
279
Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml
Normal file
279
Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml
Normal file
@ -0,0 +1,279 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2023 Analog Devices Inc.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/adi,ad7173.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD7173 ADC
|
||||
|
||||
maintainers:
|
||||
- Ceclan Dumitru <dumitru.ceclan@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices AD717x ADC's:
|
||||
The AD717x family offer a complete integrated Sigma-Delta ADC solution which
|
||||
can be used in high precision, low noise single channel applications
|
||||
(Life Science measurements) or higher speed multiplexed applications
|
||||
(Factory Automation PLC Input modules). The Sigma-Delta ADC is intended
|
||||
primarily for measurement of signals close to DC but also delivers
|
||||
outstanding performance with input bandwidths out to ~10kHz.
|
||||
|
||||
Datasheets for supported chips:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7172-2.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7172-4.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7173-8.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7175-2.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7175-8.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7176-2.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7177-2.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7172-2
|
||||
- adi,ad7172-4
|
||||
- adi,ad7173-8
|
||||
- adi,ad7175-2
|
||||
- adi,ad7175-8
|
||||
- adi,ad7176-2
|
||||
- adi,ad7177-2
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: |
|
||||
Ready: multiplexed with SPI data out. While SPI CS is low,
|
||||
can be used to indicate the completion of a conversion.
|
||||
|
||||
- description: |
|
||||
Error: The three error bits in the status register (ADC_ERROR, CRC_ERROR,
|
||||
and REG_ERROR) are OR'ed, inverted, and mapped to the ERROR pin.
|
||||
Therefore, the ERROR pin indicates that an error has occurred.
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: rdy
|
||||
- const: err
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 20000000
|
||||
|
||||
gpio-controller:
|
||||
description: Marks the device node as a GPIO controller.
|
||||
|
||||
'#gpio-cells':
|
||||
const: 2
|
||||
description:
|
||||
The first cell is the GPIO number and the second cell specifies
|
||||
GPIO flags, as defined in <dt-bindings/gpio/gpio.h>.
|
||||
|
||||
vref-supply:
|
||||
description: |
|
||||
Differential external reference supply used for conversion. The reference
|
||||
voltage (Vref) specified here must be the voltage difference between the
|
||||
REF+ and REF- pins: Vref = (REF+) - (REF-).
|
||||
|
||||
vref2-supply:
|
||||
description: |
|
||||
Differential external reference supply used for conversion. The reference
|
||||
voltage (Vref2) specified here must be the voltage difference between the
|
||||
REF2+ and REF2- pins: Vref2 = (REF2+) - (REF2-).
|
||||
|
||||
avdd-supply:
|
||||
description: Avdd supply, can be used as reference for conversion.
|
||||
This supply is referenced to AVSS, voltage specified here
|
||||
represents (AVDD1 - AVSS).
|
||||
|
||||
avdd2-supply:
|
||||
description: Avdd2 supply, used as the input to the internal voltage regulator.
|
||||
This supply is referenced to AVSS, voltage specified here
|
||||
represents (AVDD2 - AVSS).
|
||||
|
||||
iovdd-supply:
|
||||
description: iovdd supply, used for the chip digital interface.
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: |
|
||||
Optional external clock source. Can include one clock source: external
|
||||
clock or external crystal.
|
||||
|
||||
clock-names:
|
||||
enum:
|
||||
- ext-clk
|
||||
- xtal
|
||||
|
||||
'#clock-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^channel@[0-9a-f]$":
|
||||
type: object
|
||||
$ref: adc.yaml
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 15
|
||||
|
||||
diff-channels:
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 31
|
||||
|
||||
adi,reference-select:
|
||||
description: |
|
||||
Select the reference source to use when converting on
|
||||
the specific channel. Valid values are:
|
||||
vref : REF+ /REF−
|
||||
vref2 : REF2+ /REF2−
|
||||
refout-avss: REFOUT/AVSS (Internal reference)
|
||||
avdd : AVDD /AVSS
|
||||
|
||||
External reference ref2 only available on ad7173-8 and ad7172-4.
|
||||
Internal reference refout-avss not available on ad7172-4.
|
||||
|
||||
If not specified, internal reference used (if available).
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum:
|
||||
- vref
|
||||
- vref2
|
||||
- refout-avss
|
||||
- avdd
|
||||
default: refout-avss
|
||||
|
||||
required:
|
||||
- reg
|
||||
- diff-channels
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
# Only ad7172-4, ad7173-8 and ad7175-8 support vref2
|
||||
# Other models have [0-3] channel registers
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
not:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad7172-4
|
||||
- adi,ad7173-8
|
||||
- adi,ad7175-8
|
||||
then:
|
||||
properties:
|
||||
vref2-supply: false
|
||||
patternProperties:
|
||||
"^channel@[0-9a-f]$":
|
||||
properties:
|
||||
adi,reference-select:
|
||||
enum:
|
||||
- vref
|
||||
- refout-avss
|
||||
- avdd
|
||||
reg:
|
||||
maximum: 3
|
||||
|
||||
# Model ad7172-4 does not support internal reference
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: adi,ad7172-4
|
||||
then:
|
||||
patternProperties:
|
||||
"^channel@[0-9a-f]$":
|
||||
properties:
|
||||
reg:
|
||||
maximum: 7
|
||||
adi,reference-select:
|
||||
enum:
|
||||
- vref
|
||||
- vref2
|
||||
- avdd
|
||||
required:
|
||||
- adi,reference-select
|
||||
|
||||
- if:
|
||||
anyOf:
|
||||
- required: [clock-names]
|
||||
- required: [clocks]
|
||||
then:
|
||||
properties:
|
||||
'#clock-cells': false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@0 {
|
||||
compatible = "adi,ad7173-8";
|
||||
reg = <0>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-names = "rdy";
|
||||
interrupt-parent = <&gpio>;
|
||||
spi-max-frequency = <5000000>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
#clock-cells = <0>;
|
||||
|
||||
vref-supply = <&dummy_regulator>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
bipolar;
|
||||
diff-channels = <0 1>;
|
||||
adi,reference-select = "vref";
|
||||
};
|
||||
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
diff-channels = <2 3>;
|
||||
};
|
||||
|
||||
channel@2 {
|
||||
reg = <2>;
|
||||
bipolar;
|
||||
diff-channels = <4 5>;
|
||||
};
|
||||
|
||||
channel@3 {
|
||||
reg = <3>;
|
||||
bipolar;
|
||||
diff-channels = <6 7>;
|
||||
};
|
||||
|
||||
channel@4 {
|
||||
reg = <4>;
|
||||
diff-channels = <8 9>;
|
||||
adi,reference-select = "avdd";
|
||||
};
|
||||
};
|
||||
};
|
213
Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml
Normal file
213
Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml
Normal file
@ -0,0 +1,213 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/adi,ad7944.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices PulSAR LFCSP Analog to Digital Converters
|
||||
|
||||
maintainers:
|
||||
- Michael Hennerich <Michael.Hennerich@analog.com>
|
||||
- Nuno Sá <nuno.sa@analog.com>
|
||||
|
||||
description: |
|
||||
A family of pin-compatible single channel differential analog to digital
|
||||
converters with SPI support in a LFCSP package.
|
||||
|
||||
* https://www.analog.com/en/products/ad7944.html
|
||||
* https://www.analog.com/en/products/ad7985.html
|
||||
* https://www.analog.com/en/products/ad7986.html
|
||||
|
||||
$ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7944
|
||||
- adi,ad7985
|
||||
- adi,ad7986
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 111111111
|
||||
|
||||
spi-cpol: true
|
||||
spi-cpha: true
|
||||
|
||||
adi,spi-mode:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [ single, chain ]
|
||||
description: |
|
||||
This property indicates the SPI wiring configuration.
|
||||
|
||||
When this property is omitted, it is assumed that the device is using what
|
||||
the datasheet calls "4-wire mode". This is the conventional SPI mode used
|
||||
when there are multiple devices on the same bus. In this mode, the CNV
|
||||
line is used to initiate the conversion and the SDI line is connected to
|
||||
CS on the SPI controller.
|
||||
|
||||
When this property is present, it indicates that the device is using one
|
||||
of the following alternative wiring configurations:
|
||||
|
||||
* single: The datasheet calls this "3-wire mode". (NOTE: The datasheet's
|
||||
definition of 3-wire mode is NOT at all related to the standard
|
||||
spi-3wire property!) This mode is often used when the ADC is the only
|
||||
device on the bus. In this mode, SDI is tied to VIO, and the CNV line
|
||||
can be connected to the CS line of the SPI controller or to a GPIO, in
|
||||
which case the CS line of the controller is unused.
|
||||
* chain: The datasheet calls this "chain mode". This mode is used to save
|
||||
on wiring when multiple ADCs are used. In this mode, the SDI line of
|
||||
one chip is tied to the SDO of the next chip in the chain and the SDI of
|
||||
the last chip in the chain is tied to GND. Only the first chip in the
|
||||
chain is connected to the SPI bus. The CNV line of all chips are tied
|
||||
together. The CS line of the SPI controller can be used as the CNV line
|
||||
only if it is active high.
|
||||
|
||||
'#daisy-chained-devices': true
|
||||
|
||||
avdd-supply:
|
||||
description: A 2.5V supply that powers the analog circuitry.
|
||||
|
||||
dvdd-supply:
|
||||
description: A 2.5V supply that powers the digital circuitry.
|
||||
|
||||
vio-supply:
|
||||
description:
|
||||
A 1.8V to 2.7V supply for the digital inputs and outputs.
|
||||
|
||||
bvdd-supply:
|
||||
description:
|
||||
A voltage supply for the buffered power. When using an external reference
|
||||
without an internal buffer (PDREF high, REFIN low), this should be
|
||||
connected to the same supply as ref-supply. Otherwise, when using an
|
||||
internal reference or an external reference with an internal buffer, this
|
||||
is connected to a 5V supply.
|
||||
|
||||
ref-supply:
|
||||
description:
|
||||
Voltage regulator for the external reference voltage (REF). This property
|
||||
is omitted when using an internal reference.
|
||||
|
||||
refin-supply:
|
||||
description:
|
||||
Voltage regulator for the reference buffer input (REFIN). When using an
|
||||
external buffer with internal reference, this should be connected to a
|
||||
1.2V external reference voltage supply. Otherwise, this property is
|
||||
omitted.
|
||||
|
||||
cnv-gpios:
|
||||
description:
|
||||
The Convert Input (CNV). This input has multiple functions. It initiates
|
||||
the conversions and selects the SPI mode of the device (chain or CS). In
|
||||
'single' mode, this property is omitted if the CNV pin is connected to the
|
||||
CS line of the SPI controller.
|
||||
maxItems: 1
|
||||
|
||||
turbo-gpios:
|
||||
description:
|
||||
GPIO connected to the TURBO line. If omitted, it is assumed that the TURBO
|
||||
line is hard-wired and the state is determined by the adi,always-turbo
|
||||
property.
|
||||
maxItems: 1
|
||||
|
||||
adi,always-turbo:
|
||||
type: boolean
|
||||
description:
|
||||
When present, this property indicates that the TURBO line is hard-wired
|
||||
and the state is always high. If neither this property nor turbo-gpios is
|
||||
present, the TURBO line is assumed to be hard-wired and the state is
|
||||
always low.
|
||||
|
||||
interrupts:
|
||||
description:
|
||||
The SDO pin can also function as a busy indicator. This node should be
|
||||
connected to an interrupt that is triggered when the SDO line goes low
|
||||
while the SDI line is high and the CNV line is low ('single' mode) or the
|
||||
SDI line is low and the CNV line is high ('multi' mode); or when the SDO
|
||||
line goes high while the SDI and CNV lines are high (chain mode),
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- avdd-supply
|
||||
- dvdd-supply
|
||||
- vio-supply
|
||||
- bvdd-supply
|
||||
|
||||
allOf:
|
||||
# ref-supply and refin-supply are mutually exclusive (neither is also valid)
|
||||
- if:
|
||||
required:
|
||||
- ref-supply
|
||||
then:
|
||||
properties:
|
||||
refin-supply: false
|
||||
- if:
|
||||
required:
|
||||
- refin-supply
|
||||
then:
|
||||
properties:
|
||||
ref-supply: false
|
||||
# in '4-wire' mode, cnv-gpios is required, for other modes it is optional
|
||||
- if:
|
||||
not:
|
||||
required:
|
||||
- adi,spi-mode
|
||||
then:
|
||||
required:
|
||||
- cnv-gpios
|
||||
# chain mode has lower SCLK max rate and doesn't work when TURBO is enabled
|
||||
- if:
|
||||
required:
|
||||
- adi,spi-mode
|
||||
properties:
|
||||
adi,spi-mode:
|
||||
const: chain
|
||||
then:
|
||||
properties:
|
||||
spi-max-frequency:
|
||||
maximum: 90909090
|
||||
adi,always-turbo: false
|
||||
required:
|
||||
- '#daisy-chained-devices'
|
||||
else:
|
||||
properties:
|
||||
'#daisy-chained-devices': false
|
||||
# turbo-gpios and adi,always-turbo are mutually exclusive
|
||||
- if:
|
||||
required:
|
||||
- turbo-gpios
|
||||
then:
|
||||
properties:
|
||||
adi,always-turbo: false
|
||||
- if:
|
||||
required:
|
||||
- adi,always-turbo
|
||||
then:
|
||||
properties:
|
||||
turbo-gpios: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
adc@0 {
|
||||
compatible = "adi,ad7944";
|
||||
reg = <0>;
|
||||
spi-cpha;
|
||||
spi-max-frequency = <111111111>;
|
||||
avdd-supply = <&supply_2_5V>;
|
||||
dvdd-supply = <&supply_2_5V>;
|
||||
vio-supply = <&supply_1_8V>;
|
||||
bvdd-supply = <&supply_5V>;
|
||||
cnv-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
|
||||
turbo-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
@ -28,6 +28,9 @@ properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
maxItems: 1
|
||||
|
||||
@ -48,6 +51,7 @@ required:
|
||||
- compatible
|
||||
- dmas
|
||||
- reg
|
||||
- clocks
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
@ -58,6 +62,7 @@ examples:
|
||||
reg = <0x44a00000 0x10000>;
|
||||
dmas = <&rx_dma 0>;
|
||||
dma-names = "rx";
|
||||
clocks = <&axi_clk>;
|
||||
#io-backend-cells = <0>;
|
||||
};
|
||||
...
|
||||
|
@ -11,8 +11,13 @@ maintainers:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- allwinner,sun20i-d1-gpadc
|
||||
oneOf:
|
||||
- enum:
|
||||
- allwinner,sun20i-d1-gpadc
|
||||
- items:
|
||||
- enum:
|
||||
- allwinner,sun50i-h616-gpadc
|
||||
- const: allwinner,sun20i-d1-gpadc
|
||||
|
||||
"#io-channel-cells":
|
||||
const: 1
|
||||
|
95
Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
Normal file
95
Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
Normal file
@ -0,0 +1,95 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/dac/adi,ad9739a.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD9739A RF DAC
|
||||
|
||||
maintainers:
|
||||
- Dragos Bogdan <dragos.bogdan@analog.com>
|
||||
- Nuno Sa <nuno.sa@analog.com>
|
||||
|
||||
description: |
|
||||
The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
|
||||
of synthesizing wideband signals from dc up to 3 GHz.
|
||||
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad9737a_9739a.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad9739a
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-3p3-supply:
|
||||
description: 3.3V Digital input supply.
|
||||
|
||||
vdd-supply:
|
||||
description: 1.8V Digital input supply.
|
||||
|
||||
vdda-supply:
|
||||
description: 3.3V Analog input supply.
|
||||
|
||||
vddc-supply:
|
||||
description: 1.8V Clock input supply.
|
||||
|
||||
vref-supply:
|
||||
description: Input/Output reference supply.
|
||||
|
||||
io-backends:
|
||||
maxItems: 1
|
||||
|
||||
adi,full-scale-microamp:
|
||||
description: This property represents the DAC full scale current.
|
||||
minimum: 8580
|
||||
maximum: 31700
|
||||
default: 20000
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- io-backends
|
||||
- vdd-3p3-supply
|
||||
- vdd-supply
|
||||
- vdda-supply
|
||||
- vddc-supply
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
dac@0 {
|
||||
compatible = "adi,ad9739a";
|
||||
reg = <0>;
|
||||
|
||||
clocks = <&dac_clk>;
|
||||
|
||||
io-backends = <&iio_backend>;
|
||||
|
||||
vdd-3p3-supply = <&vdd_3_3>;
|
||||
vdd-supply = <&vdd>;
|
||||
vdda-supply = <&vdd_3_3>;
|
||||
vddc-supply = <&vdd>;
|
||||
};
|
||||
};
|
||||
...
|
62
Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
Normal file
62
Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
Normal file
@ -0,0 +1,62 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/dac/adi,axi-dac.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AXI DAC IP core
|
||||
|
||||
maintainers:
|
||||
- Nuno Sa <nuno.sa@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices Generic AXI DAC IP core for interfacing a DAC device
|
||||
with a high speed serial (JESD204B/C) or source synchronous parallel
|
||||
interface (LVDS/CMOS).
|
||||
Usually, some other interface type (i.e SPI) is used as a control
|
||||
interface for the actual DAC, while this IP core will interface
|
||||
to the data-lines of the DAC and handle the streaming of data from
|
||||
memory via DMA into the DAC.
|
||||
|
||||
https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,axi-dac-9.1.b
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
maxItems: 1
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: tx
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
'#io-backend-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- dmas
|
||||
- reg
|
||||
- clocks
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
dac@44a00000 {
|
||||
compatible = "adi,axi-dac-9.1.b";
|
||||
reg = <0x44a00000 0x10000>;
|
||||
dmas = <&tx_dma 0>;
|
||||
dma-names = "tx";
|
||||
#io-backend-cells = <0>;
|
||||
clocks = <&axi_clk>;
|
||||
};
|
||||
...
|
@ -21,6 +21,7 @@ properties:
|
||||
- ti,dac5573
|
||||
- ti,dac6573
|
||||
- ti,dac7573
|
||||
- ti,dac081c081
|
||||
- ti,dac121c081
|
||||
|
||||
reg:
|
||||
|
@ -4,16 +4,20 @@
|
||||
$id: http://devicetree.org/schemas/iio/health/maxim,max30102.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Maxim MAX30102 heart rate and pulse oximeter and MAX30105 particle-sensor
|
||||
title: Maxim MAX30101/2 heart rate and pulse oximeter and MAX30105 particle-sensor
|
||||
|
||||
maintainers:
|
||||
- Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- maxim,max30102
|
||||
- maxim,max30105
|
||||
oneOf:
|
||||
- enum:
|
||||
- maxim,max30102
|
||||
- maxim,max30105
|
||||
- items:
|
||||
- const: maxim,max30101
|
||||
- const: maxim,max30105
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -34,6 +34,9 @@ properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@ -43,6 +46,7 @@ additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
@ -54,5 +58,6 @@ examples:
|
||||
vdd-supply = <&vcc_3v3>;
|
||||
interrupt-parent = <&gpio3>;
|
||||
interrupts = <23 IRQ_TYPE_EDGE_RISING>;
|
||||
reset-gpios = <&gpio3 27 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
|
@ -32,6 +32,8 @@ properties:
|
||||
- invensense,icm42605
|
||||
- invensense,icm42622
|
||||
- invensense,icm42631
|
||||
- invensense,icm42686
|
||||
- invensense,icm42688
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -62,14 +62,15 @@ properties:
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- invensense,mpu9150
|
||||
- invensense,mpu9250
|
||||
- invensense,mpu9255
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- invensense,iam20680
|
||||
- invensense,icm20602
|
||||
- invensense,icm20608
|
||||
- invensense,icm20609
|
||||
- invensense,icm20689
|
||||
then:
|
||||
properties:
|
||||
i2c-gate: false
|
||||
|
@ -4,17 +4,22 @@
|
||||
$id: http://devicetree.org/schemas/iio/light/avago,apds9300.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Avago APDS9300 ambient light sensor
|
||||
title: Avago Gesture/RGB/ALS/Proximity sensors
|
||||
|
||||
maintainers:
|
||||
- Jonathan Cameron <jic23@kernel.org>
|
||||
- Subhajit Ghosh <subhajit.ghosh@tweaklogic.com>
|
||||
|
||||
description: |
|
||||
Datasheet at https://www.avagotech.com/docs/AV02-1077EN
|
||||
Datasheet: https://www.avagotech.com/docs/AV02-1077EN
|
||||
Datasheet: https://www.avagotech.com/docs/AV02-4191EN
|
||||
Datasheet: https://www.avagotech.com/docs/AV02-4755EN
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: avago,apds9300
|
||||
enum:
|
||||
- avago,apds9300
|
||||
- avago,apds9306
|
||||
- avago,apds9960
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -22,6 +27,8 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
@ -30,6 +37,8 @@ required:
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
@ -38,7 +47,8 @@ examples:
|
||||
compatible = "avago,apds9300";
|
||||
reg = <0x39>;
|
||||
interrupt-parent = <&gpio2>;
|
||||
interrupts = <29 8>;
|
||||
interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
|
||||
vdd-supply = <®ulator_3v3>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
@ -1,44 +0,0 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/avago,apds9960.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Avago APDS9960 gesture/RGB/ALS/proximity sensor
|
||||
|
||||
maintainers:
|
||||
- Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
|
||||
description: |
|
||||
Datasheet at https://www.avagotech.com/docs/AV02-4191EN
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: avago,apds9960
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
light-sensor@39 {
|
||||
compatible = "avago,apds9960";
|
||||
reg = <0x39>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <16 1>;
|
||||
};
|
||||
};
|
||||
...
|
@ -57,6 +57,8 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
adi,mux-delay-config-us:
|
||||
description: |
|
||||
Extra delay prior to each conversion, in addition to the internal 1ms
|
||||
@ -460,6 +462,7 @@ required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
@ -489,6 +492,7 @@ examples:
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
vdd-supply = <&supply>;
|
||||
interrupts = <20 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-parent = <&gpio>;
|
||||
|
||||
|
@ -34,6 +34,7 @@ properties:
|
||||
- qcom,qcs404-qfprom
|
||||
- qcom,sc7180-qfprom
|
||||
- qcom,sc7280-qfprom
|
||||
- qcom,sc8280xp-qfprom
|
||||
- qcom,sdm630-qfprom
|
||||
- qcom,sdm670-qfprom
|
||||
- qcom,sdm845-qfprom
|
||||
@ -42,6 +43,9 @@ properties:
|
||||
- qcom,sm6375-qfprom
|
||||
- qcom,sm8150-qfprom
|
||||
- qcom,sm8250-qfprom
|
||||
- qcom,sm8450-qfprom
|
||||
- qcom,sm8550-qfprom
|
||||
- qcom,sm8650-qfprom
|
||||
- const: qcom,qfprom
|
||||
|
||||
reg:
|
||||
|
@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
title: Qualcomm Technologies, Inc. SPMI SDAM
|
||||
|
||||
maintainers:
|
||||
- Shyam Kumar Thella <sthella@codeaurora.org>
|
||||
- David Collins <quic_collinsd@quicinc.com>
|
||||
|
||||
description: |
|
||||
The SDAM provides scratch register space for the PMIC clients. This
|
||||
|
@ -14,7 +14,7 @@ description: |
|
||||
It is a MIPI System Power Management (SPMI) controller.
|
||||
|
||||
The PMIC part is provided by
|
||||
./Documentation/devicetree/bindings/mfd/hisilicon,hi6421-spmi-pmic.yaml.
|
||||
Documentation/devicetree/bindings/mfd/hisilicon,hi6421-spmi-pmic.yaml.
|
||||
|
||||
allOf:
|
||||
- $ref: spmi.yaml#
|
||||
@ -48,26 +48,23 @@ patternProperties:
|
||||
PMIC properties, which are specific to the used SPMI PMIC device(s).
|
||||
When used in combination with HiSilicon 6421v600, the properties
|
||||
are documented at
|
||||
drivers/staging/hikey9xx/hisilicon,hi6421-spmi-pmic.yaml.
|
||||
Documentation/devicetree/bindings/mfd/hisilicon,hi6421-spmi-pmic.yaml
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
bus {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
#include <dt-bindings/spmi/spmi.h>
|
||||
|
||||
spmi: spmi@fff24000 {
|
||||
spmi@fff24000 {
|
||||
compatible = "hisilicon,kirin970-spmi-controller";
|
||||
reg = <0xfff24000 0x1000>;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x0 0xfff24000 0x0 0x1000>;
|
||||
hisilicon,spmi-channel = <2>;
|
||||
|
||||
pmic@0 {
|
||||
reg = <0 0>;
|
||||
/* pmic properties */
|
||||
reg = <0 SPMI_USID>;
|
||||
/* pmic properties */
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -92,6 +92,7 @@ properties:
|
||||
description: >
|
||||
SPMI bus instance. only applicable to PMIC arbiter version 7 and beyond.
|
||||
Supported values, 0 = primary bus, 1 = secondary bus
|
||||
deprecated: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
@ -0,0 +1,136 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/spmi/qcom,x1e80100-spmi-pmic-arb.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Qualcomm X1E80100 SPMI Controller (PMIC Arbiter v7)
|
||||
|
||||
maintainers:
|
||||
- Stephen Boyd <sboyd@kernel.org>
|
||||
|
||||
description: |
|
||||
The X1E80100 SPMI PMIC Arbiter implements HW version 7 and it's an SPMI
|
||||
controller with wrapping arbitration logic to allow for multiple on-chip
|
||||
devices to control up to 2 SPMI separate buses.
|
||||
|
||||
The PMIC Arbiter can also act as an interrupt controller, providing interrupts
|
||||
to slave devices.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: qcom,x1e80100-spmi-pmic-arb
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: core registers
|
||||
- description: tx-channel per virtual slave registers
|
||||
- description: rx-channel (called observer) per virtual slave registers
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: chnls
|
||||
- const: obsrvr
|
||||
|
||||
ranges: true
|
||||
|
||||
'#address-cells':
|
||||
const: 2
|
||||
|
||||
'#size-cells':
|
||||
const: 2
|
||||
|
||||
qcom,ee:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 5
|
||||
description: >
|
||||
indicates the active Execution Environment identifier
|
||||
|
||||
qcom,channel:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 5
|
||||
description: >
|
||||
which of the PMIC Arb provided channels to use for accesses
|
||||
|
||||
patternProperties:
|
||||
"^spmi@[a-f0-9]+$":
|
||||
type: object
|
||||
$ref: /schemas/spmi/spmi.yaml
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: configuration registers
|
||||
- description: interrupt controller registers
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: cnfg
|
||||
- const: intr
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
interrupt-names:
|
||||
const: periph_irq
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
'#interrupt-cells':
|
||||
const: 4
|
||||
description: |
|
||||
cell 1: slave ID for the requested interrupt (0-15)
|
||||
cell 2: peripheral ID for requested interrupt (0-255)
|
||||
cell 3: the requested peripheral interrupt (0-7)
|
||||
cell 4: interrupt flags indicating level-sense information,
|
||||
as defined in dt-bindings/interrupt-controller/irq.h
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg-names
|
||||
- qcom,ee
|
||||
- qcom,channel
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
spmi: arbiter@c400000 {
|
||||
compatible = "qcom,x1e80100-spmi-pmic-arb";
|
||||
reg = <0 0x0c400000 0 0x3000>,
|
||||
<0 0x0c500000 0 0x4000000>,
|
||||
<0 0x0c440000 0 0x80000>;
|
||||
reg-names = "core", "chnls", "obsrvr";
|
||||
|
||||
qcom,ee = <0>;
|
||||
qcom,channel = <0>;
|
||||
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
ranges;
|
||||
|
||||
spmi_bus0: spmi@c42d000 {
|
||||
reg = <0 0x0c42d000 0 0x4000>,
|
||||
<0 0x0c4c0000 0 0x10000>;
|
||||
reg-names = "cnfg", "intr";
|
||||
|
||||
interrupt-names = "periph_irq";
|
||||
interrupts-extended = <&pdc 1 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <4>;
|
||||
|
||||
#address-cells = <2>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
};
|
||||
};
|
@ -6,9 +6,12 @@ API to implement a new FPGA bridge
|
||||
|
||||
* struct fpga_bridge - The FPGA Bridge structure
|
||||
* struct fpga_bridge_ops - Low level Bridge driver ops
|
||||
* fpga_bridge_register() - Create and register a bridge
|
||||
* __fpga_bridge_register() - Create and register a bridge
|
||||
* fpga_bridge_unregister() - Unregister a bridge
|
||||
|
||||
The helper macro ``fpga_bridge_register()`` automatically sets
|
||||
the module that registers the FPGA bridge as the owner.
|
||||
|
||||
.. kernel-doc:: include/linux/fpga/fpga-bridge.h
|
||||
:functions: fpga_bridge
|
||||
|
||||
@ -16,7 +19,7 @@ API to implement a new FPGA bridge
|
||||
:functions: fpga_bridge_ops
|
||||
|
||||
.. kernel-doc:: drivers/fpga/fpga-bridge.c
|
||||
:functions: fpga_bridge_register
|
||||
:functions: __fpga_bridge_register
|
||||
|
||||
.. kernel-doc:: drivers/fpga/fpga-bridge.c
|
||||
:functions: fpga_bridge_unregister
|
||||
|
@ -24,7 +24,8 @@ How to support a new FPGA device
|
||||
--------------------------------
|
||||
|
||||
To add another FPGA manager, write a driver that implements a set of ops. The
|
||||
probe function calls fpga_mgr_register() or fpga_mgr_register_full(), such as::
|
||||
probe function calls ``fpga_mgr_register()`` or ``fpga_mgr_register_full()``,
|
||||
such as::
|
||||
|
||||
static const struct fpga_manager_ops socfpga_fpga_ops = {
|
||||
.write_init = socfpga_fpga_ops_configure_init,
|
||||
@ -69,10 +70,11 @@ probe function calls fpga_mgr_register() or fpga_mgr_register_full(), such as::
|
||||
}
|
||||
|
||||
Alternatively, the probe function could call one of the resource managed
|
||||
register functions, devm_fpga_mgr_register() or devm_fpga_mgr_register_full().
|
||||
When these functions are used, the parameter syntax is the same, but the call
|
||||
to fpga_mgr_unregister() should be removed. In the above example, the
|
||||
socfpga_fpga_remove() function would not be required.
|
||||
register functions, ``devm_fpga_mgr_register()`` or
|
||||
``devm_fpga_mgr_register_full()``. When these functions are used, the
|
||||
parameter syntax is the same, but the call to ``fpga_mgr_unregister()`` should be
|
||||
removed. In the above example, the ``socfpga_fpga_remove()`` function would not be
|
||||
required.
|
||||
|
||||
The ops will implement whatever device specific register writes are needed to
|
||||
do the programming sequence for this particular FPGA. These ops return 0 for
|
||||
@ -125,15 +127,19 @@ API for implementing a new FPGA Manager driver
|
||||
* struct fpga_manager - the FPGA manager struct
|
||||
* struct fpga_manager_ops - Low level FPGA manager driver ops
|
||||
* struct fpga_manager_info - Parameter structure for fpga_mgr_register_full()
|
||||
* fpga_mgr_register_full() - Create and register an FPGA manager using the
|
||||
* __fpga_mgr_register_full() - Create and register an FPGA manager using the
|
||||
fpga_mgr_info structure to provide the full flexibility of options
|
||||
* fpga_mgr_register() - Create and register an FPGA manager using standard
|
||||
* __fpga_mgr_register() - Create and register an FPGA manager using standard
|
||||
arguments
|
||||
* devm_fpga_mgr_register_full() - Resource managed version of
|
||||
fpga_mgr_register_full()
|
||||
* devm_fpga_mgr_register() - Resource managed version of fpga_mgr_register()
|
||||
* __devm_fpga_mgr_register_full() - Resource managed version of
|
||||
__fpga_mgr_register_full()
|
||||
* __devm_fpga_mgr_register() - Resource managed version of __fpga_mgr_register()
|
||||
* fpga_mgr_unregister() - Unregister an FPGA manager
|
||||
|
||||
Helper macros ``fpga_mgr_register_full()``, ``fpga_mgr_register()``,
|
||||
``devm_fpga_mgr_register_full()``, and ``devm_fpga_mgr_register()`` are available
|
||||
to ease the registration.
|
||||
|
||||
.. kernel-doc:: include/linux/fpga/fpga-mgr.h
|
||||
:functions: fpga_mgr_states
|
||||
|
||||
@ -147,16 +153,16 @@ API for implementing a new FPGA Manager driver
|
||||
:functions: fpga_manager_info
|
||||
|
||||
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||
:functions: fpga_mgr_register_full
|
||||
:functions: __fpga_mgr_register_full
|
||||
|
||||
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||
:functions: fpga_mgr_register
|
||||
:functions: __fpga_mgr_register
|
||||
|
||||
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||
:functions: devm_fpga_mgr_register_full
|
||||
:functions: __devm_fpga_mgr_register_full
|
||||
|
||||
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||
:functions: devm_fpga_mgr_register
|
||||
:functions: __devm_fpga_mgr_register
|
||||
|
||||
.. kernel-doc:: drivers/fpga/fpga-mgr.c
|
||||
:functions: fpga_mgr_unregister
|
||||
|
@ -46,13 +46,16 @@ API to add a new FPGA region
|
||||
----------------------------
|
||||
|
||||
* struct fpga_region - The FPGA region struct
|
||||
* struct fpga_region_info - Parameter structure for fpga_region_register_full()
|
||||
* fpga_region_register_full() - Create and register an FPGA region using the
|
||||
* struct fpga_region_info - Parameter structure for __fpga_region_register_full()
|
||||
* __fpga_region_register_full() - Create and register an FPGA region using the
|
||||
fpga_region_info structure to provide the full flexibility of options
|
||||
* fpga_region_register() - Create and register an FPGA region using standard
|
||||
* __fpga_region_register() - Create and register an FPGA region using standard
|
||||
arguments
|
||||
* fpga_region_unregister() - Unregister an FPGA region
|
||||
|
||||
Helper macros ``fpga_region_register()`` and ``fpga_region_register_full()``
|
||||
automatically set the module that registers the FPGA region as the owner.
|
||||
|
||||
The FPGA region's probe function will need to get a reference to the FPGA
|
||||
Manager it will be using to do the programming. This usually would happen
|
||||
during the region's probe function.
|
||||
@ -82,10 +85,10 @@ following APIs to handle building or tearing down that list.
|
||||
:functions: fpga_region_info
|
||||
|
||||
.. kernel-doc:: drivers/fpga/fpga-region.c
|
||||
:functions: fpga_region_register_full
|
||||
:functions: __fpga_region_register_full
|
||||
|
||||
.. kernel-doc:: drivers/fpga/fpga-region.c
|
||||
:functions: fpga_region_register
|
||||
:functions: __fpga_region_register
|
||||
|
||||
.. kernel-doc:: drivers/fpga/fpga-region.c
|
||||
:functions: fpga_region_unregister
|
||||
|
156
Documentation/iio/ad7944.rst
Normal file
156
Documentation/iio/ad7944.rst
Normal file
@ -0,0 +1,156 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
=============
|
||||
AD7944 driver
|
||||
=============
|
||||
|
||||
ADC driver for Analog Devices Inc. AD7944 and similar devices. The module name
|
||||
is ``ad7944``.
|
||||
|
||||
|
||||
Supported devices
|
||||
=================
|
||||
|
||||
The following chips are supported by this driver:
|
||||
|
||||
* `AD7944 <https://www.analog.com/AD7944>`_
|
||||
* `AD7985 <https://www.analog.com/AD7985>`_
|
||||
* `AD7986 <https://www.analog.com/AD7986>`_
|
||||
|
||||
|
||||
Supported features
|
||||
==================
|
||||
|
||||
SPI wiring modes
|
||||
----------------
|
||||
|
||||
The driver currently supports three of the many possible SPI wiring configurations.
|
||||
|
||||
CS mode, 3-wire, without busy indicator
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block::
|
||||
|
||||
+-------------+
|
||||
+--------------------| CS |
|
||||
v | |
|
||||
VIO +--------------------+ | HOST |
|
||||
| | CNV | | |
|
||||
+--->| SDI AD7944 SDO |-------->| SDI |
|
||||
| SCK | | |
|
||||
+--------------------+ | |
|
||||
^ | |
|
||||
+--------------------| SCLK |
|
||||
+-------------+
|
||||
|
||||
To select this mode in the device tree, set the ``adi,spi-mode`` property to
|
||||
``"single"`` and omit the ``cnv-gpios`` property.
|
||||
|
||||
CS mode, 4-wire, without busy indicator
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block::
|
||||
|
||||
+-------------+
|
||||
+-----------------------------------| CS |
|
||||
| | |
|
||||
| +--------------------| GPIO |
|
||||
| v | |
|
||||
| +--------------------+ | HOST |
|
||||
| | CNV | | |
|
||||
+--->| SDI AD7944 SDO |-------->| SDI |
|
||||
| SCK | | |
|
||||
+--------------------+ | |
|
||||
^ | |
|
||||
+--------------------| SCLK |
|
||||
+-------------+
|
||||
|
||||
To select this mode in the device tree, omit the ``adi,spi-mode`` property and
|
||||
provide the ``cnv-gpios`` property.
|
||||
|
||||
Chain mode, without busy indicator
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block::
|
||||
|
||||
+-------------+
|
||||
+-------------------------+--------------------| CS |
|
||||
v v | |
|
||||
+--------------------+ +--------------------+ | HOST |
|
||||
| CNV | | CNV | | |
|
||||
+--->| SDI AD7944 SDO |--->| SDI AD7944 SDO |-------->| SDI |
|
||||
| | SCK | | SCK | | |
|
||||
GND +--------------------+ +--------------------+ | |
|
||||
^ ^ | |
|
||||
+-------------------------+--------------------| SCLK |
|
||||
+-------------+
|
||||
|
||||
To select this mode in the device tree, set the ``adi,spi-mode`` property to
|
||||
``"chain"``, add the ``spi-cs-high`` flag, add the ``#daisy-chained-devices``
|
||||
property, and omit the ``cnv-gpios`` property.
|
||||
|
||||
Reference voltage
|
||||
-----------------
|
||||
|
||||
All 3 possible reference voltage sources are supported:
|
||||
|
||||
- Internal reference
|
||||
- External 1.2V reference and internal buffer
|
||||
- External reference
|
||||
|
||||
The source is determined by the device tree. If ``ref-supply`` is present, then
|
||||
the external reference is used. If ``refin-supply`` is present, then the internal
|
||||
buffer is used. If neither is present, then the internal reference is used.
|
||||
|
||||
Unimplemented features
|
||||
----------------------
|
||||
|
||||
- ``BUSY`` indication
|
||||
- ``TURBO`` mode
|
||||
|
||||
|
||||
Device attributes
|
||||
=================
|
||||
|
||||
There are two types of ADCs in this family, pseudo-differential and fully
|
||||
differential. The channel name is different depending on the type of ADC.
|
||||
|
||||
Pseudo-differential ADCs
|
||||
------------------------
|
||||
|
||||
AD7944 and AD7985 are pseudo-differential ADCs and have the following attributes:
|
||||
|
||||
+---------------------------------------+--------------------------------------------------------------+
|
||||
| Attribute | Description |
|
||||
+=======================================+==============================================================+
|
||||
| ``in_voltage0_raw`` | Raw ADC voltage value (*IN+* referenced to ground sense). |
|
||||
+---------------------------------------+--------------------------------------------------------------+
|
||||
| ``in_voltage0_scale`` | Scale factor to convert raw value to mV. |
|
||||
+---------------------------------------+--------------------------------------------------------------+
|
||||
|
||||
In "chain" mode, additional chips will appear as additional voltage input
|
||||
channels, e.g. ``in_voltage1_raw``.
|
||||
|
||||
Fully-differential ADCs
|
||||
-----------------------
|
||||
|
||||
AD7986 is a fully-differential ADC and has the following attributes:
|
||||
|
||||
+---------------------------------------+--------------------------------------------------------------+
|
||||
| Attribute | Description |
|
||||
+=======================================+==============================================================+
|
||||
| ``in_voltage0-voltage1_raw`` | Raw ADC voltage value (*IN+* - *IN-*). |
|
||||
+---------------------------------------+--------------------------------------------------------------+
|
||||
| ``in_voltage0-voltage1_scale`` | Scale factor to convert raw value to mV. |
|
||||
+---------------------------------------+--------------------------------------------------------------+
|
||||
|
||||
In "chain" mode, additional chips will appear as additional voltage input
|
||||
channels, e.g. ``in_voltage2-voltage3_raw``.
|
||||
|
||||
|
||||
Device buffers
|
||||
==============
|
||||
|
||||
This driver supports IIO triggered buffers.
|
||||
|
||||
See :doc:`iio_devbuf` for more information.
|
@ -66,11 +66,9 @@ specific device folder path ``/sys/bus/iio/devices/iio:deviceX``.
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| 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_calibbias | Calibration offset for the Y-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_y_raw | Raw Y-axis accelerometer channel value. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
@ -94,11 +92,9 @@ specific device folder path ``/sys/bus/iio/devices/iio:deviceX``.
|
||||
+---------------------------------------+------------------------------------------------------+
|
||||
| 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_calibbias | Calibration offset for the Y-axis gyroscope channel. |
|
||||
+---------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_y_raw | Raw Y-axis gyroscope channel value. |
|
||||
+---------------------------------------+------------------------------------------------------+
|
||||
|
@ -16,6 +16,7 @@ Industrial I/O Kernel Drivers
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
ad7944
|
||||
adis16475
|
||||
bno055
|
||||
ep93xx_adc
|
||||
|
@ -40,7 +40,7 @@ IO dies (SICL, Super I/O Cluster), where there's one PCIe Root
|
||||
Complex for each SICL.
|
||||
::
|
||||
|
||||
/sys/devices/hisi_ptt<sicl_id>_<core_id>
|
||||
/sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>
|
||||
|
||||
Tune
|
||||
====
|
||||
@ -53,7 +53,7 @@ Each event is presented as a file under $(PTT PMU dir)/tune, and
|
||||
a simple open/read/write/close cycle will be used to tune the event.
|
||||
::
|
||||
|
||||
$ cd /sys/devices/hisi_ptt<sicl_id>_<core_id>/tune
|
||||
$ cd /sys/bus/event_source/devices/hisi_ptt<sicl_id>_<core_id>/tune
|
||||
$ ls
|
||||
qos_tx_cpl qos_tx_np qos_tx_p
|
||||
tx_path_rx_req_alloc_buf_level
|
||||
|
@ -174,6 +174,8 @@ Code Seq# Include File Comments
|
||||
'M' 00-0F drivers/video/fsl-diu-fb.h conflict!
|
||||
'N' 00-1F drivers/usb/scanner.h
|
||||
'N' 40-7F drivers/block/nvme.c
|
||||
'N' 80-8F uapi/linux/ntsync.h NT synchronization primitives
|
||||
<mailto:wine-devel@winehq.org>
|
||||
'O' 00-06 mtd/ubi-user.h UBI
|
||||
'P' all linux/soundcard.h conflict!
|
||||
'P' 60-6F sound/sscape_ioctl.h conflict!
|
||||
|
60
MAINTAINERS
60
MAINTAINERS
@ -210,44 +210,44 @@ S: Maintained
|
||||
F: drivers/hwmon/abituguru3.c
|
||||
|
||||
ACCES 104-DIO-48E GPIO DRIVER
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
M: William Breathitt Gray <wbg@kernel.org>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-104-dio-48e.c
|
||||
|
||||
ACCES 104-IDI-48 GPIO DRIVER
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
M: William Breathitt Gray <wbg@kernel.org>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-104-idi-48.c
|
||||
|
||||
ACCES 104-IDIO-16 GPIO DRIVER
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
M: William Breathitt Gray <wbg@kernel.org>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-104-idio-16.c
|
||||
|
||||
ACCES 104-QUAD-8 DRIVER
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
M: William Breathitt Gray <wbg@kernel.org>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/counter/104-quad-8.c
|
||||
|
||||
ACCES IDIO-16 GPIO LIBRARY
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
M: William Breathitt Gray <wbg@kernel.org>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-idio-16.c
|
||||
F: drivers/gpio/gpio-idio-16.h
|
||||
|
||||
ACCES PCI-IDIO-16 GPIO DRIVER
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
M: William Breathitt Gray <wbg@kernel.org>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-pci-idio-16.c
|
||||
|
||||
ACCES PCIe-IDIO-24 GPIO DRIVER
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
M: William Breathitt Gray <wbg@kernel.org>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-pcie-idio-24.c
|
||||
@ -453,6 +453,16 @@ W: http://wiki.analog.com/AD7879
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: drivers/input/touchscreen/ad7879.c
|
||||
|
||||
AD7944 ADC DRIVER (AD7944/AD7985/AD7986)
|
||||
M: Michael Hennerich <michael.hennerich@analog.com>
|
||||
M: Nuno Sá <nuno.sa@analog.com>
|
||||
R: David Lechner <dlechner@baylibre.com>
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml
|
||||
F: Documentation/iio/ad7944.rst
|
||||
F: drivers/iio/adc/ad7944.c
|
||||
|
||||
ADAFRUIT MINI I2C GAMEPAD
|
||||
M: Anshul Dalal <anshulusr@gmail.com>
|
||||
L: linux-input@vger.kernel.org
|
||||
@ -1255,6 +1265,15 @@ W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml
|
||||
F: drivers/iio/adc/ad7780.c
|
||||
|
||||
ANALOG DEVICES INC AD9739a DRIVER
|
||||
M: Nuno Sa <nuno.sa@analog.com>
|
||||
M: Dragos Bogdan <dragos.bogdan@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
|
||||
F: drivers/iio/dac/ad9739a.c
|
||||
|
||||
ANALOG DEVICES INC ADA4250 DRIVER
|
||||
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
@ -1420,6 +1439,14 @@ F: sound/soc/codecs/adav*
|
||||
F: sound/soc/codecs/sigmadsp.*
|
||||
F: sound/soc/codecs/ssm*
|
||||
|
||||
ANALOG DEVICES INC AXI DAC DRIVER
|
||||
M: Nuno Sa <nuno.sa@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
|
||||
F: drivers/iio/dac/adi-axi-dac.c
|
||||
|
||||
ANALOG DEVICES INC DMA DRIVERS
|
||||
M: Lars-Peter Clausen <lars@metafoo.de>
|
||||
S: Supported
|
||||
@ -1484,7 +1511,7 @@ S: Maintained
|
||||
F: sound/aoa/
|
||||
|
||||
APEX EMBEDDED SYSTEMS STX104 IIO DRIVER
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
M: William Breathitt Gray <wbg@kernel.org>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/addac/stx104.c
|
||||
@ -5585,7 +5612,7 @@ F: Documentation/hwmon/corsair-psu.rst
|
||||
F: drivers/hwmon/corsair-psu.c
|
||||
|
||||
COUNTER SUBSYSTEM
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
M: William Breathitt Gray <wbg@kernel.org>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/wbg/counter.git
|
||||
@ -6371,7 +6398,7 @@ F: include/sound/da[79]*.h
|
||||
F: sound/soc/codecs/da[79]*.[ch]
|
||||
|
||||
DIAMOND SYSTEMS GPIO-MM GPIO DRIVER
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
M: William Breathitt Gray <wbg@kernel.org>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-gpio-mm.c
|
||||
@ -9943,7 +9970,7 @@ M: Yicong Yang <yangyicong@hisilicon.com>
|
||||
M: Jonathan Cameron <jonathan.cameron@huawei.com>
|
||||
L: linux-kernel@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/ABI/testing/sysfs-devices-hisi_ptt
|
||||
F: Documentation/ABI/testing/sysfs-bus-event_source-devices-hisi_ptt
|
||||
F: Documentation/trace/hisi-ptt.rst
|
||||
F: drivers/hwtracing/ptt/
|
||||
F: tools/perf/arch/arm64/util/hisi-ptt.c
|
||||
@ -10704,6 +10731,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git
|
||||
F: Documentation/ABI/testing/configfs-iio*
|
||||
F: Documentation/ABI/testing/sysfs-bus-iio*
|
||||
F: Documentation/devicetree/bindings/iio/
|
||||
F: Documentation/iio/
|
||||
F: drivers/iio/
|
||||
F: drivers/staging/iio/
|
||||
F: include/dt-bindings/iio/
|
||||
@ -10911,14 +10939,14 @@ S: Maintained
|
||||
F: drivers/video/fbdev/i810/
|
||||
|
||||
INTEL 8254 COUNTER DRIVER
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
M: William Breathitt Gray <wbg@kernel.org>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/counter/i8254.c
|
||||
F: include/linux/i8254.h
|
||||
|
||||
INTEL 8255 GPIO DRIVER
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
M: William Breathitt Gray <wbg@kernel.org>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-i8255.c
|
||||
@ -11632,7 +11660,7 @@ F: drivers/irqchip/
|
||||
F: include/linux/irqchip.h
|
||||
|
||||
ISA
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
M: William Breathitt Gray <wbg@kernel.org>
|
||||
S: Maintained
|
||||
F: Documentation/driver-api/isa.rst
|
||||
F: drivers/base/isa.c
|
||||
@ -13686,7 +13714,7 @@ F: drivers/net/mdio/mdio-regmap.c
|
||||
F: include/linux/mdio/mdio-regmap.h
|
||||
|
||||
MEASUREMENT COMPUTING CIO-DAC IIO DRIVER
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
M: William Breathitt Gray <wbg@kernel.org>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/iio/dac/cio-dac.c
|
||||
@ -24202,7 +24230,7 @@ S: Orphan
|
||||
F: drivers/watchdog/ebc-c384_wdt.c
|
||||
|
||||
WINSYSTEMS WS16C48 GPIO DRIVER
|
||||
M: William Breathitt Gray <william.gray@linaro.org>
|
||||
M: William Breathitt Gray <wbg@kernel.org>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/gpio/gpio-ws16c48.c
|
||||
|
@ -39,13 +39,13 @@ static ssize_t speakup_file_write(struct file *fp, const char __user *buffer,
|
||||
static ssize_t speakup_file_writeu(struct file *fp, const char __user *buffer,
|
||||
size_t nbytes, loff_t *ppos)
|
||||
{
|
||||
size_t count = nbytes, want;
|
||||
size_t count = nbytes, consumed, want;
|
||||
const char __user *ptr = buffer;
|
||||
size_t bytes;
|
||||
unsigned long flags;
|
||||
unsigned char buf[256];
|
||||
u16 ubuf[256];
|
||||
size_t in, in2, out;
|
||||
size_t in, out;
|
||||
|
||||
if (!synth)
|
||||
return -ENODEV;
|
||||
@ -58,57 +58,24 @@ static ssize_t speakup_file_writeu(struct file *fp, const char __user *buffer,
|
||||
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;
|
||||
for (in = 0, out = 0; in < bytes; in += consumed) {
|
||||
s32 value;
|
||||
|
||||
switch (nbytes) {
|
||||
case 8: /* 0xff */
|
||||
case 7: /* 0xfe */
|
||||
case 1: /* 0x80 */
|
||||
/* Invalid, drop */
|
||||
goto drop;
|
||||
value = synth_utf8_get(buf + in, bytes - in, &consumed, &want);
|
||||
if (value == -1) {
|
||||
/* Invalid or incomplete */
|
||||
|
||||
case 0:
|
||||
/* ASCII, copy */
|
||||
ubuf[out++] = c;
|
||||
continue;
|
||||
|
||||
default:
|
||||
/* 2..6-byte UTF-8 */
|
||||
|
||||
if (bytes - in < nbytes) {
|
||||
if (want > bytes - in)
|
||||
/* 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;
|
||||
continue;
|
||||
}
|
||||
drop:
|
||||
/* empty statement */;
|
||||
|
||||
if (value < 0x10000)
|
||||
ubuf[out++] = value;
|
||||
}
|
||||
|
||||
count -= bytes;
|
||||
|
@ -76,7 +76,9 @@ int speakup_paste_selection(struct tty_struct *tty);
|
||||
void speakup_cancel_paste(void);
|
||||
void speakup_register_devsynth(void);
|
||||
void speakup_unregister_devsynth(void);
|
||||
s32 synth_utf8_get(const char *buf, size_t count, size_t *consumed, size_t *want);
|
||||
void synth_write(const char *buf, size_t count);
|
||||
void synth_writeu(const char *buf, size_t count);
|
||||
int synth_supports_indexing(void);
|
||||
|
||||
extern struct vc_data *spk_sel_cons;
|
||||
|
@ -217,10 +217,95 @@ void synth_write(const char *_buf, size_t count)
|
||||
synth_start();
|
||||
}
|
||||
|
||||
/* Consume one utf-8 character from buf (that contains up to count bytes),
|
||||
* returns the unicode codepoint if valid, -1 otherwise.
|
||||
* In all cases, returns the number of consumed bytes in *consumed,
|
||||
* and the minimum number of bytes that would be needed for the next character
|
||||
* in *want.
|
||||
*/
|
||||
s32 synth_utf8_get(const char *buf, size_t count, size_t *consumed, size_t *want)
|
||||
{
|
||||
unsigned char c = buf[0];
|
||||
int nbytes = 8 - fls(c ^ 0xff);
|
||||
u32 value;
|
||||
size_t i;
|
||||
|
||||
switch (nbytes) {
|
||||
case 8: /* 0xff */
|
||||
case 7: /* 0xfe */
|
||||
case 1: /* 0x80 */
|
||||
/* Invalid, drop */
|
||||
*consumed = 1;
|
||||
*want = 1;
|
||||
return -1;
|
||||
|
||||
case 0:
|
||||
/* ASCII, take as such */
|
||||
*consumed = 1;
|
||||
*want = 1;
|
||||
return c;
|
||||
|
||||
default:
|
||||
/* 2..6-byte UTF-8 */
|
||||
|
||||
if (count < nbytes) {
|
||||
/* We don't have it all */
|
||||
*consumed = 0;
|
||||
*want = nbytes;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* First byte */
|
||||
value = c & ((1u << (7 - nbytes)) - 1);
|
||||
|
||||
/* Other bytes */
|
||||
for (i = 1; i < nbytes; i++) {
|
||||
c = buf[i];
|
||||
if ((c & 0xc0) != 0x80) {
|
||||
/* Invalid, drop the head */
|
||||
*consumed = i;
|
||||
*want = 1;
|
||||
return -1;
|
||||
}
|
||||
value = (value << 6) | (c & 0x3f);
|
||||
}
|
||||
|
||||
*consumed = nbytes;
|
||||
*want = 1;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
void synth_writeu(const char *buf, size_t count)
|
||||
{
|
||||
size_t i, consumed, want;
|
||||
|
||||
/* Convert to u16 */
|
||||
for (i = 0; i < count; i++) {
|
||||
s32 value;
|
||||
|
||||
value = synth_utf8_get(buf + i, count - i, &consumed, &want);
|
||||
if (value == -1) {
|
||||
/* Invalid or incomplete */
|
||||
|
||||
if (want > count - i)
|
||||
/* We don't have it all, stop */
|
||||
count = i;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value < 0x10000)
|
||||
synth_buffer_add(value);
|
||||
}
|
||||
|
||||
synth_start();
|
||||
}
|
||||
|
||||
void synth_printf(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
unsigned char buf[160], *p;
|
||||
unsigned char buf[160];
|
||||
int r;
|
||||
|
||||
va_start(args, fmt);
|
||||
@ -229,10 +314,7 @@ void synth_printf(const char *fmt, ...)
|
||||
if (r > sizeof(buf) - 1)
|
||||
r = sizeof(buf) - 1;
|
||||
|
||||
p = buf;
|
||||
while (r--)
|
||||
synth_buffer_add(*p++);
|
||||
synth_start();
|
||||
synth_writeu(buf, r);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(synth_printf);
|
||||
|
||||
|
@ -22,14 +22,6 @@
|
||||
static const struct acpi_device_id amba_id_list[] = {
|
||||
{"ARMH0061", 0}, /* PL061 GPIO Device */
|
||||
{"ARMH0330", 0}, /* ARM DMA Controller DMA-330 */
|
||||
{"ARMHC501", 0}, /* ARM CoreSight ETR */
|
||||
{"ARMHC502", 0}, /* ARM CoreSight STM */
|
||||
{"ARMHC503", 0}, /* ARM CoreSight Debug */
|
||||
{"ARMHC979", 0}, /* ARM CoreSight TPIU */
|
||||
{"ARMHC97C", 0}, /* ARM CoreSight SoC-400 TMC, SoC-600 ETF/ETB */
|
||||
{"ARMHC98D", 0}, /* ARM CoreSight Dynamic Replicator */
|
||||
{"ARMHC9CA", 0}, /* ARM CoreSight CATU */
|
||||
{"ARMHC9FF", 0}, /* ARM CoreSight Dynamic Funnel */
|
||||
{"", 0},
|
||||
};
|
||||
|
||||
|
@ -5367,7 +5367,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
goto err;
|
||||
break;
|
||||
case BINDER_SET_MAX_THREADS: {
|
||||
int max_threads;
|
||||
u32 max_threads;
|
||||
|
||||
if (copy_from_user(&max_threads, ubuf,
|
||||
sizeof(max_threads))) {
|
||||
|
@ -421,7 +421,7 @@ struct binder_proc {
|
||||
struct list_head todo;
|
||||
struct binder_stats stats;
|
||||
struct list_head delivered_death;
|
||||
int max_threads;
|
||||
u32 max_threads;
|
||||
int requested_threads;
|
||||
int requested_threads_started;
|
||||
int tmp_ref;
|
||||
|
@ -868,20 +868,6 @@ struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_handle_get);
|
||||
|
||||
/**
|
||||
* fwnode_handle_put - Drop reference to a device node
|
||||
* @fwnode: Pointer to the device node to drop the reference to.
|
||||
*
|
||||
* This has to be used when terminating device_for_each_child_node() iteration
|
||||
* with break or return to prevent stale device node references from being left
|
||||
* behind.
|
||||
*/
|
||||
void fwnode_handle_put(struct fwnode_handle *fwnode)
|
||||
{
|
||||
fwnode_call_void_op(fwnode, put);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_handle_put);
|
||||
|
||||
/**
|
||||
* fwnode_device_is_available - check if a device is available for use
|
||||
* @fwnode: Pointer to the fwnode of the device.
|
||||
|
@ -127,6 +127,30 @@ static ssize_t soc_reset_store(struct device *dev,
|
||||
}
|
||||
static DEVICE_ATTR_WO(soc_reset);
|
||||
|
||||
static ssize_t trigger_edl_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct mhi_device *mhi_dev = to_mhi_device(dev);
|
||||
struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl;
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
|
||||
ret = mhi_cntrl->edl_trigger(mhi_cntrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_WO(trigger_edl);
|
||||
|
||||
static struct attribute *mhi_dev_attrs[] = {
|
||||
&dev_attr_serial_number.attr,
|
||||
&dev_attr_oem_pk_hash.attr,
|
||||
@ -517,11 +541,9 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl)
|
||||
dev_dbg(dev, "Initializing MHI registers\n");
|
||||
|
||||
/* Read channel db offset */
|
||||
ret = mhi_read_reg(mhi_cntrl, base, CHDBOFF, &val);
|
||||
if (ret) {
|
||||
dev_err(dev, "Unable to read CHDBOFF register\n");
|
||||
return -EIO;
|
||||
}
|
||||
ret = mhi_get_channel_doorbell_offset(mhi_cntrl, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val >= mhi_cntrl->reg_len - (8 * MHI_DEV_WAKE_DB)) {
|
||||
dev_err(dev, "CHDB offset: 0x%x is out of range: 0x%zx\n",
|
||||
@ -1018,6 +1040,12 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl,
|
||||
if (ret)
|
||||
goto err_release_dev;
|
||||
|
||||
if (mhi_cntrl->edl_trigger) {
|
||||
ret = sysfs_create_file(&mhi_dev->dev.kobj, &dev_attr_trigger_edl.attr);
|
||||
if (ret)
|
||||
goto err_release_dev;
|
||||
}
|
||||
|
||||
mhi_cntrl->mhi_dev = mhi_dev;
|
||||
|
||||
mhi_create_debugfs(mhi_cntrl);
|
||||
@ -1051,6 +1079,9 @@ void mhi_unregister_controller(struct mhi_controller *mhi_cntrl)
|
||||
mhi_deinit_free_irq(mhi_cntrl);
|
||||
mhi_destroy_debugfs(mhi_cntrl);
|
||||
|
||||
if (mhi_cntrl->edl_trigger)
|
||||
sysfs_remove_file(&mhi_dev->dev.kobj, &dev_attr_trigger_edl.attr);
|
||||
|
||||
destroy_workqueue(mhi_cntrl->hiprio_wq);
|
||||
kfree(mhi_cntrl->mhi_cmd);
|
||||
kfree(mhi_cntrl->mhi_event);
|
||||
|
@ -1691,3 +1691,19 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev)
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer);
|
||||
|
||||
int mhi_get_channel_doorbell_offset(struct mhi_controller *mhi_cntrl, u32 *chdb_offset)
|
||||
{
|
||||
struct device *dev = &mhi_cntrl->mhi_dev->dev;
|
||||
void __iomem *base = mhi_cntrl->regs;
|
||||
int ret;
|
||||
|
||||
ret = mhi_read_reg(mhi_cntrl, base, CHDBOFF, chdb_offset);
|
||||
if (ret) {
|
||||
dev_err(dev, "Unable to read CHDBOFF register\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mhi_get_channel_doorbell_offset);
|
||||
|
@ -27,12 +27,16 @@
|
||||
#define PCI_VENDOR_ID_THALES 0x1269
|
||||
#define PCI_VENDOR_ID_QUECTEL 0x1eac
|
||||
|
||||
#define MHI_EDL_DB 91
|
||||
#define MHI_EDL_COOKIE 0xEDEDEDED
|
||||
|
||||
/**
|
||||
* struct mhi_pci_dev_info - MHI PCI device specific information
|
||||
* @config: MHI controller configuration
|
||||
* @name: name of the PCI module
|
||||
* @fw: firmware path (if any)
|
||||
* @edl: emergency download mode firmware path (if any)
|
||||
* @edl_trigger: capable of triggering EDL mode in the device (if supported)
|
||||
* @bar_num: PCI base address register to use for MHI MMIO register space
|
||||
* @dma_data_width: DMA transfer word size (32 or 64 bits)
|
||||
* @mru_default: default MRU size for MBIM network packets
|
||||
@ -44,6 +48,7 @@ struct mhi_pci_dev_info {
|
||||
const char *name;
|
||||
const char *fw;
|
||||
const char *edl;
|
||||
bool edl_trigger;
|
||||
unsigned int bar_num;
|
||||
unsigned int dma_data_width;
|
||||
unsigned int mru_default;
|
||||
@ -292,6 +297,7 @@ static const struct mhi_pci_dev_info mhi_qcom_sdx75_info = {
|
||||
.name = "qcom-sdx75m",
|
||||
.fw = "qcom/sdx75m/xbl.elf",
|
||||
.edl = "qcom/sdx75m/edl.mbn",
|
||||
.edl_trigger = true,
|
||||
.config = &modem_qcom_v2_mhiv_config,
|
||||
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
|
||||
.dma_data_width = 32,
|
||||
@ -302,6 +308,7 @@ static const struct mhi_pci_dev_info mhi_qcom_sdx65_info = {
|
||||
.name = "qcom-sdx65m",
|
||||
.fw = "qcom/sdx65m/xbl.elf",
|
||||
.edl = "qcom/sdx65m/edl.mbn",
|
||||
.edl_trigger = true,
|
||||
.config = &modem_qcom_v1_mhiv_config,
|
||||
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
|
||||
.dma_data_width = 32,
|
||||
@ -312,6 +319,7 @@ static const struct mhi_pci_dev_info mhi_qcom_sdx55_info = {
|
||||
.name = "qcom-sdx55m",
|
||||
.fw = "qcom/sdx55m/sbl1.mbn",
|
||||
.edl = "qcom/sdx55m/edl.mbn",
|
||||
.edl_trigger = true,
|
||||
.config = &modem_qcom_v1_mhiv_config,
|
||||
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
|
||||
.dma_data_width = 32,
|
||||
@ -928,6 +936,40 @@ static void health_check(struct timer_list *t)
|
||||
mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
|
||||
}
|
||||
|
||||
static int mhi_pci_generic_edl_trigger(struct mhi_controller *mhi_cntrl)
|
||||
{
|
||||
void __iomem *base = mhi_cntrl->regs;
|
||||
void __iomem *edl_db;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = mhi_device_get_sync(mhi_cntrl->mhi_dev);
|
||||
if (ret) {
|
||||
dev_err(mhi_cntrl->cntrl_dev, "Failed to wakeup the device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_wakeup_event(&mhi_cntrl->mhi_dev->dev, 0);
|
||||
mhi_cntrl->runtime_get(mhi_cntrl);
|
||||
|
||||
ret = mhi_get_channel_doorbell_offset(mhi_cntrl, &val);
|
||||
if (ret)
|
||||
goto err_get_chdb;
|
||||
|
||||
edl_db = base + val + (8 * MHI_EDL_DB);
|
||||
|
||||
mhi_cntrl->write_reg(mhi_cntrl, edl_db + 4, upper_32_bits(MHI_EDL_COOKIE));
|
||||
mhi_cntrl->write_reg(mhi_cntrl, edl_db, lower_32_bits(MHI_EDL_COOKIE));
|
||||
|
||||
mhi_soc_reset(mhi_cntrl);
|
||||
|
||||
err_get_chdb:
|
||||
mhi_cntrl->runtime_put(mhi_cntrl);
|
||||
mhi_device_put(mhi_cntrl->mhi_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
const struct mhi_pci_dev_info *info = (struct mhi_pci_dev_info *) id->driver_data;
|
||||
@ -962,6 +1004,9 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
mhi_cntrl->runtime_put = mhi_pci_runtime_put;
|
||||
mhi_cntrl->mru = info->mru_default;
|
||||
|
||||
if (info->edl_trigger)
|
||||
mhi_cntrl->edl_trigger = mhi_pci_generic_edl_trigger;
|
||||
|
||||
if (info->sideband_wake) {
|
||||
mhi_cntrl->wake_get = mhi_pci_wake_get_nop;
|
||||
mhi_cntrl->wake_put = mhi_pci_wake_put_nop;
|
||||
|
@ -222,7 +222,7 @@ static int xlnx_cdx_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xlnx_cdx_remove(struct platform_device *pdev)
|
||||
static void xlnx_cdx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cdx_controller *cdx = platform_get_drvdata(pdev);
|
||||
struct cdx_mcdi *cdx_mcdi = cdx->priv;
|
||||
@ -234,8 +234,6 @@ static int xlnx_cdx_remove(struct platform_device *pdev)
|
||||
|
||||
cdx_mcdi_finish(cdx_mcdi);
|
||||
kfree(cdx_mcdi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id cdx_match_table[] = {
|
||||
@ -252,7 +250,7 @@ static struct platform_driver cdx_pdriver = {
|
||||
.of_match_table = cdx_match_table,
|
||||
},
|
||||
.probe = xlnx_cdx_probe,
|
||||
.remove = xlnx_cdx_remove,
|
||||
.remove_new = xlnx_cdx_remove,
|
||||
};
|
||||
|
||||
static int __init cdx_controller_init(void)
|
||||
|
@ -383,6 +383,7 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEVPORT
|
||||
static ssize_t read_port(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
@ -424,6 +425,7 @@ static ssize_t write_port(struct file *file, const char __user *buf,
|
||||
*ppos = i;
|
||||
return tmp-buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
static ssize_t read_null(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
@ -653,12 +655,14 @@ static const struct file_operations null_fops = {
|
||||
.uring_cmd = uring_cmd_null,
|
||||
};
|
||||
|
||||
static const struct file_operations __maybe_unused port_fops = {
|
||||
#ifdef CONFIG_DEVPORT
|
||||
static const struct file_operations port_fops = {
|
||||
.llseek = memory_lseek,
|
||||
.read = read_port,
|
||||
.write = write_port,
|
||||
.open = open_port,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct file_operations zero_fops = {
|
||||
.llseek = zero_lseek,
|
||||
|
@ -195,12 +195,11 @@ static int oppanel_probe(struct platform_device *pdev)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int oppanel_remove(struct platform_device *pdev)
|
||||
static void oppanel_remove(struct platform_device *pdev)
|
||||
{
|
||||
misc_deregister(&oppanel_dev);
|
||||
kfree(oppanel_lines);
|
||||
kfree(oppanel_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id oppanel_match[] = {
|
||||
@ -214,7 +213,7 @@ static struct platform_driver oppanel_driver = {
|
||||
.of_match_table = oppanel_match,
|
||||
},
|
||||
.probe = oppanel_probe,
|
||||
.remove = oppanel_remove,
|
||||
.remove_new = oppanel_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(oppanel_driver);
|
||||
|
@ -296,28 +296,35 @@ static int register_device(int minor, struct pp_struct *pp)
|
||||
if (!port) {
|
||||
pr_warn("%s: no associated port!\n", name);
|
||||
rc = -ENXIO;
|
||||
goto err;
|
||||
goto err_free_name;
|
||||
}
|
||||
|
||||
index = ida_alloc(&ida_index, GFP_KERNEL);
|
||||
if (index < 0) {
|
||||
pr_warn("%s: failed to get index!\n", name);
|
||||
rc = index;
|
||||
goto err_put_port;
|
||||
}
|
||||
|
||||
memset(&ppdev_cb, 0, sizeof(ppdev_cb));
|
||||
ppdev_cb.irq_func = pp_irq;
|
||||
ppdev_cb.flags = (pp->flags & PP_EXCL) ? PARPORT_FLAG_EXCL : 0;
|
||||
ppdev_cb.private = pp;
|
||||
pdev = parport_register_dev_model(port, name, &ppdev_cb, index);
|
||||
parport_put_port(port);
|
||||
|
||||
if (!pdev) {
|
||||
pr_warn("%s: failed to register device!\n", name);
|
||||
rc = -ENXIO;
|
||||
ida_free(&ida_index, index);
|
||||
goto err;
|
||||
goto err_put_port;
|
||||
}
|
||||
|
||||
pp->pdev = pdev;
|
||||
pp->index = index;
|
||||
dev_dbg(&pdev->dev, "registered pardevice\n");
|
||||
err:
|
||||
err_put_port:
|
||||
parport_put_port(port);
|
||||
err_free_name:
|
||||
kfree(name);
|
||||
return rc;
|
||||
}
|
||||
|
@ -1408,7 +1408,7 @@ static int sonypi_probe(struct platform_device *dev)
|
||||
return error;
|
||||
}
|
||||
|
||||
static int sonypi_remove(struct platform_device *dev)
|
||||
static void sonypi_remove(struct platform_device *dev)
|
||||
{
|
||||
sonypi_disable();
|
||||
|
||||
@ -1432,8 +1432,6 @@ static int sonypi_remove(struct platform_device *dev)
|
||||
}
|
||||
|
||||
kfifo_free(&sonypi_device.fifo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
@ -1470,7 +1468,7 @@ static struct platform_driver sonypi_driver = {
|
||||
.pm = SONYPI_PM,
|
||||
},
|
||||
.probe = sonypi_probe,
|
||||
.remove = sonypi_remove,
|
||||
.remove_new = sonypi_remove,
|
||||
.shutdown = sonypi_shutdown,
|
||||
};
|
||||
|
||||
|
@ -374,11 +374,6 @@ static inline u16 pipe_full_bits(u16 hw_status_bits)
|
||||
return (hw_status_bits >> 10) & 0x3;
|
||||
};
|
||||
|
||||
static inline unsigned int dma_chain_flag_bits(u16 prepost_bits)
|
||||
{
|
||||
return (prepost_bits >> 6) & 0x3;
|
||||
}
|
||||
|
||||
static inline unsigned int adc_upper_read_ptr_code(u16 prepost_bits)
|
||||
{
|
||||
return (prepost_bits >> 12) & 0x3;
|
||||
|
@ -49,12 +49,12 @@ static void counter_device_release(struct device *dev)
|
||||
kfree(container_of(counter, struct counter_device_allochelper, counter));
|
||||
}
|
||||
|
||||
static struct device_type counter_device_type = {
|
||||
static const struct device_type counter_device_type = {
|
||||
.name = "counter_device",
|
||||
.release = counter_device_release,
|
||||
};
|
||||
|
||||
static struct bus_type counter_bus_type = {
|
||||
static const struct bus_type counter_bus_type = {
|
||||
.name = "counter",
|
||||
.dev_name = "counter",
|
||||
};
|
||||
|
@ -8,9 +8,11 @@
|
||||
*
|
||||
*/
|
||||
#include <linux/counter.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/stm32-timers.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/types.h>
|
||||
@ -21,6 +23,12 @@
|
||||
#define TIM_CCER_MASK (TIM_CCER_CC1P | TIM_CCER_CC1NP | \
|
||||
TIM_CCER_CC2P | TIM_CCER_CC2NP)
|
||||
|
||||
#define STM32_CH1_SIG 0
|
||||
#define STM32_CH2_SIG 1
|
||||
#define STM32_CLOCK_SIG 2
|
||||
#define STM32_CH3_SIG 3
|
||||
#define STM32_CH4_SIG 4
|
||||
|
||||
struct stm32_timer_regs {
|
||||
u32 cr1;
|
||||
u32 cnt;
|
||||
@ -34,6 +42,11 @@ struct stm32_timer_cnt {
|
||||
u32 max_arr;
|
||||
bool enabled;
|
||||
struct stm32_timer_regs bak;
|
||||
bool has_encoder;
|
||||
unsigned int nchannels;
|
||||
unsigned int nr_irqs;
|
||||
spinlock_t lock; /* protects nb_ovf */
|
||||
u64 nb_ovf;
|
||||
};
|
||||
|
||||
static const enum counter_function stm32_count_functions[] = {
|
||||
@ -107,12 +120,18 @@ static int stm32_count_function_write(struct counter_device *counter,
|
||||
sms = TIM_SMCR_SMS_SLAVE_MODE_DISABLED;
|
||||
break;
|
||||
case COUNTER_FUNCTION_QUADRATURE_X2_A:
|
||||
if (!priv->has_encoder)
|
||||
return -EOPNOTSUPP;
|
||||
sms = TIM_SMCR_SMS_ENCODER_MODE_1;
|
||||
break;
|
||||
case COUNTER_FUNCTION_QUADRATURE_X2_B:
|
||||
if (!priv->has_encoder)
|
||||
return -EOPNOTSUPP;
|
||||
sms = TIM_SMCR_SMS_ENCODER_MODE_2;
|
||||
break;
|
||||
case COUNTER_FUNCTION_QUADRATURE_X4:
|
||||
if (!priv->has_encoder)
|
||||
return -EOPNOTSUPP;
|
||||
sms = TIM_SMCR_SMS_ENCODER_MODE_3;
|
||||
break;
|
||||
default:
|
||||
@ -216,11 +235,108 @@ static int stm32_count_enable_write(struct counter_device *counter,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_count_prescaler_read(struct counter_device *counter,
|
||||
struct counter_count *count, u64 *prescaler)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter_priv(counter);
|
||||
u32 psc;
|
||||
|
||||
regmap_read(priv->regmap, TIM_PSC, &psc);
|
||||
|
||||
*prescaler = psc + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_count_prescaler_write(struct counter_device *counter,
|
||||
struct counter_count *count, u64 prescaler)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter_priv(counter);
|
||||
u32 psc;
|
||||
|
||||
if (!prescaler || prescaler > MAX_TIM_PSC + 1)
|
||||
return -ERANGE;
|
||||
|
||||
psc = prescaler - 1;
|
||||
|
||||
return regmap_write(priv->regmap, TIM_PSC, psc);
|
||||
}
|
||||
|
||||
static int stm32_count_cap_read(struct counter_device *counter,
|
||||
struct counter_count *count,
|
||||
size_t ch, u64 *cap)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter_priv(counter);
|
||||
u32 ccrx;
|
||||
|
||||
if (ch >= priv->nchannels)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (ch) {
|
||||
case 0:
|
||||
regmap_read(priv->regmap, TIM_CCR1, &ccrx);
|
||||
break;
|
||||
case 1:
|
||||
regmap_read(priv->regmap, TIM_CCR2, &ccrx);
|
||||
break;
|
||||
case 2:
|
||||
regmap_read(priv->regmap, TIM_CCR3, &ccrx);
|
||||
break;
|
||||
case 3:
|
||||
regmap_read(priv->regmap, TIM_CCR4, &ccrx);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(counter->parent, "CCR%zu: 0x%08x\n", ch + 1, ccrx);
|
||||
|
||||
*cap = ccrx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_count_nb_ovf_read(struct counter_device *counter,
|
||||
struct counter_count *count, u64 *val)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter_priv(counter);
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, irqflags);
|
||||
*val = priv->nb_ovf;
|
||||
spin_unlock_irqrestore(&priv->lock, irqflags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_count_nb_ovf_write(struct counter_device *counter,
|
||||
struct counter_count *count, u64 val)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter_priv(counter);
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, irqflags);
|
||||
priv->nb_ovf = val;
|
||||
spin_unlock_irqrestore(&priv->lock, irqflags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DEFINE_COUNTER_ARRAY_CAPTURE(stm32_count_cap_array, 4);
|
||||
|
||||
static struct counter_comp stm32_count_ext[] = {
|
||||
COUNTER_COMP_DIRECTION(stm32_count_direction_read),
|
||||
COUNTER_COMP_ENABLE(stm32_count_enable_read, stm32_count_enable_write),
|
||||
COUNTER_COMP_CEILING(stm32_count_ceiling_read,
|
||||
stm32_count_ceiling_write),
|
||||
COUNTER_COMP_COUNT_U64("prescaler", stm32_count_prescaler_read,
|
||||
stm32_count_prescaler_write),
|
||||
COUNTER_COMP_ARRAY_CAPTURE(stm32_count_cap_read, NULL, stm32_count_cap_array),
|
||||
COUNTER_COMP_COUNT_U64("num_overflows", stm32_count_nb_ovf_read, stm32_count_nb_ovf_write),
|
||||
};
|
||||
|
||||
static const enum counter_synapse_action stm32_clock_synapse_actions[] = {
|
||||
COUNTER_SYNAPSE_ACTION_RISING_EDGE,
|
||||
};
|
||||
|
||||
static const enum counter_synapse_action stm32_synapse_actions[] = {
|
||||
@ -243,25 +359,152 @@ static int stm32_action_read(struct counter_device *counter,
|
||||
switch (function) {
|
||||
case COUNTER_FUNCTION_INCREASE:
|
||||
/* counts on internal clock when CEN=1 */
|
||||
*action = COUNTER_SYNAPSE_ACTION_NONE;
|
||||
if (synapse->signal->id == STM32_CLOCK_SIG)
|
||||
*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
|
||||
else
|
||||
*action = COUNTER_SYNAPSE_ACTION_NONE;
|
||||
return 0;
|
||||
case COUNTER_FUNCTION_QUADRATURE_X2_A:
|
||||
/* counts up/down on TI1FP1 edge depending on TI2FP2 level */
|
||||
if (synapse->signal->id == count->synapses[0].signal->id)
|
||||
if (synapse->signal->id == STM32_CH1_SIG)
|
||||
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
else
|
||||
*action = COUNTER_SYNAPSE_ACTION_NONE;
|
||||
return 0;
|
||||
case COUNTER_FUNCTION_QUADRATURE_X2_B:
|
||||
/* counts up/down on TI2FP2 edge depending on TI1FP1 level */
|
||||
if (synapse->signal->id == count->synapses[1].signal->id)
|
||||
if (synapse->signal->id == STM32_CH2_SIG)
|
||||
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
else
|
||||
*action = COUNTER_SYNAPSE_ACTION_NONE;
|
||||
return 0;
|
||||
case COUNTER_FUNCTION_QUADRATURE_X4:
|
||||
/* counts up/down on both TI1FP1 and TI2FP2 edges */
|
||||
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
if (synapse->signal->id == STM32_CH1_SIG || synapse->signal->id == STM32_CH2_SIG)
|
||||
*action = COUNTER_SYNAPSE_ACTION_BOTH_EDGES;
|
||||
else
|
||||
*action = COUNTER_SYNAPSE_ACTION_NONE;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
struct stm32_count_cc_regs {
|
||||
u32 ccmr_reg;
|
||||
u32 ccmr_mask;
|
||||
u32 ccmr_bits;
|
||||
u32 ccer_bits;
|
||||
};
|
||||
|
||||
static const struct stm32_count_cc_regs stm32_cc[] = {
|
||||
{ TIM_CCMR1, TIM_CCMR_CC1S, TIM_CCMR_CC1S_TI1,
|
||||
TIM_CCER_CC1E | TIM_CCER_CC1P | TIM_CCER_CC1NP },
|
||||
{ TIM_CCMR1, TIM_CCMR_CC2S, TIM_CCMR_CC2S_TI2,
|
||||
TIM_CCER_CC2E | TIM_CCER_CC2P | TIM_CCER_CC2NP },
|
||||
{ TIM_CCMR2, TIM_CCMR_CC3S, TIM_CCMR_CC3S_TI3,
|
||||
TIM_CCER_CC3E | TIM_CCER_CC3P | TIM_CCER_CC3NP },
|
||||
{ TIM_CCMR2, TIM_CCMR_CC4S, TIM_CCMR_CC4S_TI4,
|
||||
TIM_CCER_CC4E | TIM_CCER_CC4P | TIM_CCER_CC4NP },
|
||||
};
|
||||
|
||||
static int stm32_count_capture_configure(struct counter_device *counter, unsigned int ch,
|
||||
bool enable)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter_priv(counter);
|
||||
const struct stm32_count_cc_regs *cc;
|
||||
u32 ccmr, ccer;
|
||||
|
||||
if (ch >= ARRAY_SIZE(stm32_cc) || ch >= priv->nchannels) {
|
||||
dev_err(counter->parent, "invalid ch: %d\n", ch);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cc = &stm32_cc[ch];
|
||||
|
||||
/*
|
||||
* configure channel in input capture mode, map channel 1 on TI1, channel2 on TI2...
|
||||
* Select both edges / non-inverted to trigger a capture.
|
||||
*/
|
||||
if (enable) {
|
||||
/* first clear possibly latched capture flag upon enabling */
|
||||
if (!regmap_test_bits(priv->regmap, TIM_CCER, cc->ccer_bits))
|
||||
regmap_write(priv->regmap, TIM_SR, ~TIM_SR_CC_IF(ch));
|
||||
regmap_update_bits(priv->regmap, cc->ccmr_reg, cc->ccmr_mask,
|
||||
cc->ccmr_bits);
|
||||
regmap_set_bits(priv->regmap, TIM_CCER, cc->ccer_bits);
|
||||
} else {
|
||||
regmap_clear_bits(priv->regmap, TIM_CCER, cc->ccer_bits);
|
||||
regmap_clear_bits(priv->regmap, cc->ccmr_reg, cc->ccmr_mask);
|
||||
}
|
||||
|
||||
regmap_read(priv->regmap, cc->ccmr_reg, &ccmr);
|
||||
regmap_read(priv->regmap, TIM_CCER, &ccer);
|
||||
dev_dbg(counter->parent, "%s(%s) ch%d 0x%08x 0x%08x\n", __func__, enable ? "ena" : "dis",
|
||||
ch, ccmr, ccer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_count_events_configure(struct counter_device *counter)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter_priv(counter);
|
||||
struct counter_event_node *event_node;
|
||||
u32 dier = 0;
|
||||
int i, ret;
|
||||
|
||||
list_for_each_entry(event_node, &counter->events_list, l) {
|
||||
switch (event_node->event) {
|
||||
case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
|
||||
/* first clear possibly latched UIF before enabling */
|
||||
if (!regmap_test_bits(priv->regmap, TIM_DIER, TIM_DIER_UIE))
|
||||
regmap_write(priv->regmap, TIM_SR, (u32)~TIM_SR_UIF);
|
||||
dier |= TIM_DIER_UIE;
|
||||
break;
|
||||
case COUNTER_EVENT_CAPTURE:
|
||||
ret = stm32_count_capture_configure(counter, event_node->channel, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
dier |= TIM_DIER_CC_IE(event_node->channel);
|
||||
break;
|
||||
default:
|
||||
/* should never reach this path */
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable / disable all events at once, from events_list, so write all DIER bits */
|
||||
regmap_write(priv->regmap, TIM_DIER, dier);
|
||||
|
||||
/* check for disabled capture events */
|
||||
for (i = 0 ; i < priv->nchannels; i++) {
|
||||
if (!(dier & TIM_DIER_CC_IE(i))) {
|
||||
ret = stm32_count_capture_configure(counter, i, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_count_watch_validate(struct counter_device *counter,
|
||||
const struct counter_watch *watch)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter_priv(counter);
|
||||
|
||||
/* Interrupts are optional */
|
||||
if (!priv->nr_irqs)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (watch->event) {
|
||||
case COUNTER_EVENT_CAPTURE:
|
||||
if (watch->channel >= priv->nchannels) {
|
||||
dev_err(counter->parent, "Invalid channel %d\n", watch->channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
case COUNTER_EVENT_OVERFLOW_UNDERFLOW:
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -274,35 +517,89 @@ static const struct counter_ops stm32_timer_cnt_ops = {
|
||||
.function_read = stm32_count_function_read,
|
||||
.function_write = stm32_count_function_write,
|
||||
.action_read = stm32_action_read,
|
||||
.events_configure = stm32_count_events_configure,
|
||||
.watch_validate = stm32_count_watch_validate,
|
||||
};
|
||||
|
||||
static int stm32_count_clk_get_freq(struct counter_device *counter,
|
||||
struct counter_signal *signal, u64 *freq)
|
||||
{
|
||||
struct stm32_timer_cnt *const priv = counter_priv(counter);
|
||||
|
||||
*freq = clk_get_rate(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct counter_comp stm32_count_clock_ext[] = {
|
||||
COUNTER_COMP_FREQUENCY(stm32_count_clk_get_freq),
|
||||
};
|
||||
|
||||
static struct counter_signal stm32_signals[] = {
|
||||
/*
|
||||
* Need to declare all the signals as a static array, and keep the signals order here,
|
||||
* even if they're unused or unexisting on some timer instances. It's an abstraction,
|
||||
* e.g. high level view of the counter features.
|
||||
*
|
||||
* Userspace programs may rely on signal0 to be "Channel 1", signal1 to be "Channel 2",
|
||||
* and so on. When a signal is unexisting, the COUNTER_SYNAPSE_ACTION_NONE can be used,
|
||||
* to indicate that a signal doesn't affect the counter.
|
||||
*/
|
||||
{
|
||||
.id = 0,
|
||||
.name = "Channel 1 Quadrature A"
|
||||
.id = STM32_CH1_SIG,
|
||||
.name = "Channel 1"
|
||||
},
|
||||
{
|
||||
.id = 1,
|
||||
.name = "Channel 1 Quadrature B"
|
||||
}
|
||||
.id = STM32_CH2_SIG,
|
||||
.name = "Channel 2"
|
||||
},
|
||||
{
|
||||
.id = STM32_CLOCK_SIG,
|
||||
.name = "Clock",
|
||||
.ext = stm32_count_clock_ext,
|
||||
.num_ext = ARRAY_SIZE(stm32_count_clock_ext),
|
||||
},
|
||||
{
|
||||
.id = STM32_CH3_SIG,
|
||||
.name = "Channel 3"
|
||||
},
|
||||
{
|
||||
.id = STM32_CH4_SIG,
|
||||
.name = "Channel 4"
|
||||
},
|
||||
};
|
||||
|
||||
static struct counter_synapse stm32_count_synapses[] = {
|
||||
{
|
||||
.actions_list = stm32_synapse_actions,
|
||||
.num_actions = ARRAY_SIZE(stm32_synapse_actions),
|
||||
.signal = &stm32_signals[0]
|
||||
.signal = &stm32_signals[STM32_CH1_SIG]
|
||||
},
|
||||
{
|
||||
.actions_list = stm32_synapse_actions,
|
||||
.num_actions = ARRAY_SIZE(stm32_synapse_actions),
|
||||
.signal = &stm32_signals[1]
|
||||
}
|
||||
.signal = &stm32_signals[STM32_CH2_SIG]
|
||||
},
|
||||
{
|
||||
.actions_list = stm32_clock_synapse_actions,
|
||||
.num_actions = ARRAY_SIZE(stm32_clock_synapse_actions),
|
||||
.signal = &stm32_signals[STM32_CLOCK_SIG]
|
||||
},
|
||||
{
|
||||
.actions_list = stm32_synapse_actions,
|
||||
.num_actions = ARRAY_SIZE(stm32_synapse_actions),
|
||||
.signal = &stm32_signals[STM32_CH3_SIG]
|
||||
},
|
||||
{
|
||||
.actions_list = stm32_synapse_actions,
|
||||
.num_actions = ARRAY_SIZE(stm32_synapse_actions),
|
||||
.signal = &stm32_signals[STM32_CH4_SIG]
|
||||
},
|
||||
};
|
||||
|
||||
static struct counter_count stm32_counts = {
|
||||
.id = 0,
|
||||
.name = "Channel 1 Count",
|
||||
.name = "STM32 Timer Counter",
|
||||
.functions_list = stm32_count_functions,
|
||||
.num_functions = ARRAY_SIZE(stm32_count_functions),
|
||||
.synapses = stm32_count_synapses,
|
||||
@ -311,13 +608,111 @@ static struct counter_count stm32_counts = {
|
||||
.num_ext = ARRAY_SIZE(stm32_count_ext)
|
||||
};
|
||||
|
||||
static irqreturn_t stm32_timer_cnt_isr(int irq, void *ptr)
|
||||
{
|
||||
struct counter_device *counter = ptr;
|
||||
struct stm32_timer_cnt *const priv = counter_priv(counter);
|
||||
u32 clr = GENMASK(31, 0); /* SR flags can be cleared by writing 0 (wr 1 has no effect) */
|
||||
u32 sr, dier;
|
||||
int i;
|
||||
|
||||
regmap_read(priv->regmap, TIM_SR, &sr);
|
||||
regmap_read(priv->regmap, TIM_DIER, &dier);
|
||||
/*
|
||||
* Some status bits in SR don't match with the enable bits in DIER. Only take care of
|
||||
* the possibly enabled bits in DIER (that matches in between SR and DIER).
|
||||
*/
|
||||
dier &= (TIM_DIER_UIE | TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE | TIM_DIER_CC4IE);
|
||||
sr &= dier;
|
||||
|
||||
if (sr & TIM_SR_UIF) {
|
||||
spin_lock(&priv->lock);
|
||||
priv->nb_ovf++;
|
||||
spin_unlock(&priv->lock);
|
||||
counter_push_event(counter, COUNTER_EVENT_OVERFLOW_UNDERFLOW, 0);
|
||||
dev_dbg(counter->parent, "COUNTER_EVENT_OVERFLOW_UNDERFLOW\n");
|
||||
/* SR flags can be cleared by writing 0, only clear relevant flag */
|
||||
clr &= ~TIM_SR_UIF;
|
||||
}
|
||||
|
||||
/* Check capture events */
|
||||
for (i = 0 ; i < priv->nchannels; i++) {
|
||||
if (sr & TIM_SR_CC_IF(i)) {
|
||||
counter_push_event(counter, COUNTER_EVENT_CAPTURE, i);
|
||||
clr &= ~TIM_SR_CC_IF(i);
|
||||
dev_dbg(counter->parent, "COUNTER_EVENT_CAPTURE, %d\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
regmap_write(priv->regmap, TIM_SR, clr);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
};
|
||||
|
||||
static void stm32_timer_cnt_detect_channels(struct device *dev,
|
||||
struct stm32_timer_cnt *priv)
|
||||
{
|
||||
u32 ccer, ccer_backup;
|
||||
|
||||
regmap_read(priv->regmap, TIM_CCER, &ccer_backup);
|
||||
regmap_set_bits(priv->regmap, TIM_CCER, TIM_CCER_CCXE);
|
||||
regmap_read(priv->regmap, TIM_CCER, &ccer);
|
||||
regmap_write(priv->regmap, TIM_CCER, ccer_backup);
|
||||
priv->nchannels = hweight32(ccer & TIM_CCER_CCXE);
|
||||
|
||||
dev_dbg(dev, "has %d cc channels\n", priv->nchannels);
|
||||
}
|
||||
|
||||
/* encoder supported on TIM1 TIM2 TIM3 TIM4 TIM5 TIM8 */
|
||||
#define STM32_TIM_ENCODER_SUPPORTED (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(7))
|
||||
|
||||
static const char * const stm32_timer_trigger_compat[] = {
|
||||
"st,stm32-timer-trigger",
|
||||
"st,stm32h7-timer-trigger",
|
||||
};
|
||||
|
||||
static int stm32_timer_cnt_probe_encoder(struct device *dev,
|
||||
struct stm32_timer_cnt *priv)
|
||||
{
|
||||
struct device *parent = dev->parent;
|
||||
struct device_node *tnode = NULL, *pnode = parent->of_node;
|
||||
int i, ret;
|
||||
u32 idx;
|
||||
|
||||
/*
|
||||
* Need to retrieve the trigger node index from DT, to be able
|
||||
* to determine if the counter supports encoder mode. It also
|
||||
* enforce backward compatibility, and allow to support other
|
||||
* counter modes in this driver (when the timer doesn't support
|
||||
* encoder).
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(stm32_timer_trigger_compat) && !tnode; i++)
|
||||
tnode = of_get_compatible_child(pnode, stm32_timer_trigger_compat[i]);
|
||||
if (!tnode) {
|
||||
dev_err(dev, "Can't find trigger node\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(tnode, "reg", &idx);
|
||||
if (ret) {
|
||||
dev_err(dev, "Can't get index (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->has_encoder = !!(STM32_TIM_ENCODER_SUPPORTED & BIT(idx));
|
||||
|
||||
dev_dbg(dev, "encoder support: %s\n", priv->has_encoder ? "yes" : "no");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_timer_cnt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct stm32_timer_cnt *priv;
|
||||
struct counter_device *counter;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
if (IS_ERR_OR_NULL(ddata))
|
||||
return -EINVAL;
|
||||
@ -331,6 +726,13 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev)
|
||||
priv->regmap = ddata->regmap;
|
||||
priv->clk = ddata->clk;
|
||||
priv->max_arr = ddata->max_arr;
|
||||
priv->nr_irqs = ddata->nr_irqs;
|
||||
|
||||
ret = stm32_timer_cnt_probe_encoder(dev, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
stm32_timer_cnt_detect_channels(dev, priv);
|
||||
|
||||
counter->name = dev_name(dev);
|
||||
counter->parent = dev;
|
||||
@ -340,8 +742,39 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev)
|
||||
counter->signals = stm32_signals;
|
||||
counter->num_signals = ARRAY_SIZE(stm32_signals);
|
||||
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
/* STM32 Timers can have either 1 global, or 4 dedicated interrupts (optional) */
|
||||
if (priv->nr_irqs == 1) {
|
||||
/* All events reported through the global interrupt */
|
||||
ret = devm_request_irq(&pdev->dev, ddata->irq[0], stm32_timer_cnt_isr,
|
||||
0, dev_name(dev), counter);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to request irq %d (err %d)\n",
|
||||
ddata->irq[0], ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < priv->nr_irqs; i++) {
|
||||
/*
|
||||
* Only take care of update IRQ for overflow events, and cc for
|
||||
* capture events.
|
||||
*/
|
||||
if (i != STM32_TIMERS_IRQ_UP && i != STM32_TIMERS_IRQ_CC)
|
||||
continue;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, ddata->irq[i], stm32_timer_cnt_isr,
|
||||
0, dev_name(dev), counter);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to request irq %d (err %d)\n",
|
||||
ddata->irq[i], ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset input selector to its default input */
|
||||
regmap_write(priv->regmap, TIM_TISEL, 0x0);
|
||||
|
||||
|
@ -369,7 +369,7 @@ static const enum counter_synapse_action ecap_cnt_input_actions[] = {
|
||||
};
|
||||
|
||||
static struct counter_comp ecap_cnt_clock_ext[] = {
|
||||
COUNTER_COMP_SIGNAL_U64("frequency", ecap_cnt_clk_get_freq, NULL),
|
||||
COUNTER_COMP_FREQUENCY(ecap_cnt_clk_get_freq),
|
||||
};
|
||||
|
||||
static const enum counter_signal_polarity ecap_cnt_pol_avail[] = {
|
||||
@ -537,15 +537,13 @@ static int ecap_cnt_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ecap_cnt_remove(struct platform_device *pdev)
|
||||
static void ecap_cnt_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct counter_device *counter_dev = platform_get_drvdata(pdev);
|
||||
struct ecap_cnt_dev *ecap_dev = counter_priv(counter_dev);
|
||||
|
||||
if (ecap_dev->enabled)
|
||||
ecap_cnt_capture_disable(counter_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ecap_cnt_suspend(struct device *dev)
|
||||
@ -600,7 +598,7 @@ MODULE_DEVICE_TABLE(of, ecap_cnt_of_match);
|
||||
|
||||
static struct platform_driver ecap_cnt_driver = {
|
||||
.probe = ecap_cnt_probe,
|
||||
.remove = ecap_cnt_remove,
|
||||
.remove_new = ecap_cnt_remove,
|
||||
.driver = {
|
||||
.name = "ecap-capture",
|
||||
.of_match_table = ecap_cnt_of_match,
|
||||
|
@ -425,7 +425,7 @@ static int ti_eqep_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_eqep_remove(struct platform_device *pdev)
|
||||
static void ti_eqep_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct counter_device *counter = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -433,8 +433,6 @@ static int ti_eqep_remove(struct platform_device *pdev)
|
||||
counter_unregister(counter);
|
||||
pm_runtime_put_sync(dev);
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ti_eqep_of_match[] = {
|
||||
@ -445,7 +443,7 @@ MODULE_DEVICE_TABLE(of, ti_eqep_of_match);
|
||||
|
||||
static struct platform_driver ti_eqep_driver = {
|
||||
.probe = ti_eqep_probe,
|
||||
.remove = ti_eqep_remove,
|
||||
.remove_new = ti_eqep_remove,
|
||||
.driver = {
|
||||
.name = "ti-eqep-cnt",
|
||||
.of_match_table = ti_eqep_of_match,
|
||||
|
@ -116,7 +116,8 @@ config EXTCON_MAX77843
|
||||
|
||||
config EXTCON_MAX8997
|
||||
tristate "Maxim MAX8997 EXTCON Support"
|
||||
depends on MFD_MAX8997 && IRQ_DOMAIN
|
||||
depends on MFD_MAX8997
|
||||
select IRQ_DOMAIN
|
||||
help
|
||||
If you say yes here you get support for the MUIC device of
|
||||
Maxim MAX8997 PMIC. The MAX8997 MUIC is a USB port accessory
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
/**
|
||||
* struct adc_jack_data - internal data for adc_jack device driver
|
||||
* @dev: The device structure associated with the adc_jack.
|
||||
* @edev: extcon device.
|
||||
* @cable_names: list of supported cables.
|
||||
* @adc_conditions: list of adc value conditions.
|
||||
@ -35,6 +36,7 @@
|
||||
* handling at handling_delay jiffies.
|
||||
* @handler: extcon event handler called by interrupt handler.
|
||||
* @chan: iio channel being queried.
|
||||
* @wakeup_source: Indicates if the device can wake up the system.
|
||||
*/
|
||||
struct adc_jack_data {
|
||||
struct device *dev;
|
||||
@ -158,14 +160,12 @@ static int adc_jack_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_jack_remove(struct platform_device *pdev)
|
||||
static void adc_jack_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct adc_jack_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(data->irq, data);
|
||||
cancel_work_sync(&data->handler.work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
@ -196,7 +196,7 @@ static SIMPLE_DEV_PM_OPS(adc_jack_pm_ops,
|
||||
|
||||
static struct platform_driver adc_jack_driver = {
|
||||
.probe = adc_jack_probe,
|
||||
.remove = adc_jack_remove,
|
||||
.remove_new = adc_jack_remove,
|
||||
.driver = {
|
||||
.name = "adc-jack",
|
||||
.pm = &adc_jack_pm_ops,
|
||||
|
@ -617,13 +617,11 @@ static int cht_wc_extcon_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cht_wc_extcon_remove(struct platform_device *pdev)
|
||||
static void cht_wc_extcon_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cht_wc_extcon_data *ext = platform_get_drvdata(pdev);
|
||||
|
||||
cht_wc_extcon_sw_control(ext, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id cht_wc_extcon_table[] = {
|
||||
@ -634,7 +632,7 @@ MODULE_DEVICE_TABLE(platform, cht_wc_extcon_table);
|
||||
|
||||
static struct platform_driver cht_wc_extcon_driver = {
|
||||
.probe = cht_wc_extcon_probe,
|
||||
.remove = cht_wc_extcon_remove,
|
||||
.remove_new = cht_wc_extcon_remove,
|
||||
.id_table = cht_wc_extcon_table,
|
||||
.driver = {
|
||||
.name = "cht_wcove_pwrsrc",
|
||||
|
@ -214,27 +214,21 @@ static int mrfld_extcon_probe(struct platform_device *pdev)
|
||||
|
||||
data->edev = devm_extcon_dev_allocate(dev, mrfld_extcon_cable);
|
||||
if (IS_ERR(data->edev))
|
||||
return -ENOMEM;
|
||||
return PTR_ERR(data->edev);
|
||||
|
||||
ret = devm_extcon_dev_register(dev, data->edev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "can't register extcon device: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "can't register extcon device\n");
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL, mrfld_extcon_interrupt,
|
||||
IRQF_ONESHOT | IRQF_SHARED, pdev->name,
|
||||
data);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't register IRQ handler: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "can't register IRQ handler\n");
|
||||
|
||||
ret = regmap_read(regmap, BCOVE_ID, &id);
|
||||
if (ret) {
|
||||
dev_err(dev, "can't read PMIC ID: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "can't read PMIC ID\n");
|
||||
|
||||
data->id = id;
|
||||
|
||||
@ -263,13 +257,11 @@ static int mrfld_extcon_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrfld_extcon_remove(struct platform_device *pdev)
|
||||
static void mrfld_extcon_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mrfld_extcon_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
mrfld_extcon_sw_control(data, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id mrfld_extcon_id_table[] = {
|
||||
@ -283,7 +275,7 @@ static struct platform_driver mrfld_extcon_driver = {
|
||||
.name = "mrfld_bcove_pwrsrc",
|
||||
},
|
||||
.probe = mrfld_extcon_probe,
|
||||
.remove = mrfld_extcon_remove,
|
||||
.remove_new = mrfld_extcon_remove,
|
||||
.id_table = mrfld_extcon_id_table,
|
||||
};
|
||||
module_platform_driver(mrfld_extcon_driver);
|
||||
|
@ -112,13 +112,11 @@ static int max3355_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max3355_remove(struct platform_device *pdev)
|
||||
static void max3355_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct max3355_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
gpiod_set_value_cansleep(data->shdn_gpiod, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id max3355_match_table[] = {
|
||||
@ -129,7 +127,7 @@ MODULE_DEVICE_TABLE(of, max3355_match_table);
|
||||
|
||||
static struct platform_driver max3355_driver = {
|
||||
.probe = max3355_probe,
|
||||
.remove = max3355_remove,
|
||||
.remove_new = max3355_remove,
|
||||
.driver = {
|
||||
.name = "extcon-max3355",
|
||||
.of_match_table = max3355_match_table,
|
||||
|
@ -928,7 +928,7 @@ static int max77843_muic_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max77843_muic_remove(struct platform_device *pdev)
|
||||
static void max77843_muic_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct max77843_muic_info *info = platform_get_drvdata(pdev);
|
||||
struct max77693_dev *max77843 = info->max77843;
|
||||
@ -936,8 +936,6 @@ static int max77843_muic_remove(struct platform_device *pdev)
|
||||
cancel_work_sync(&info->irq_work);
|
||||
regmap_del_irq_chip(max77843->irq, max77843->irq_data_muic);
|
||||
i2c_unregister_device(max77843->i2c_muic);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id max77843_muic_id[] = {
|
||||
@ -958,7 +956,7 @@ static struct platform_driver max77843_muic_driver = {
|
||||
.of_match_table = of_max77843_muic_dt_match,
|
||||
},
|
||||
.probe = max77843_muic_probe,
|
||||
.remove = max77843_muic_remove,
|
||||
.remove_new = max77843_muic_remove,
|
||||
.id_table = max77843_muic_id,
|
||||
};
|
||||
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/syscalls.h>
|
||||
|
@ -193,14 +193,12 @@ static int usb_extcon_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usb_extcon_remove(struct platform_device *pdev)
|
||||
static void usb_extcon_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_extcon_info *info = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_delayed_work_sync(&info->wq_detcable);
|
||||
device_init_wakeup(&pdev->dev, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
@ -281,7 +279,7 @@ MODULE_DEVICE_TABLE(platform, usb_extcon_platform_ids);
|
||||
|
||||
static struct platform_driver usb_extcon_driver = {
|
||||
.probe = usb_extcon_probe,
|
||||
.remove = usb_extcon_remove,
|
||||
.remove_new = usb_extcon_remove,
|
||||
.driver = {
|
||||
.name = "extcon-usb-gpio",
|
||||
.pm = &usb_extcon_pm_ops,
|
||||
|
@ -480,14 +480,12 @@ static int extcon_cros_ec_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int extcon_cros_ec_remove(struct platform_device *pdev)
|
||||
static void extcon_cros_ec_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cros_ec_extcon_info *info = platform_get_drvdata(pdev);
|
||||
|
||||
blocking_notifier_chain_unregister(&info->ec->event_notifier,
|
||||
&info->notifier);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
@ -531,7 +529,7 @@ static struct platform_driver extcon_cros_ec_driver = {
|
||||
.of_match_table = of_match_ptr(extcon_cros_ec_of_match),
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
.remove = extcon_cros_ec_remove,
|
||||
.remove_new = extcon_cros_ec_remove,
|
||||
.probe = extcon_cros_ec_probe,
|
||||
};
|
||||
|
||||
|
@ -64,9 +64,21 @@ config FPGA_MGR_STRATIX10_SOC
|
||||
help
|
||||
FPGA manager driver support for the Intel Stratix10 SoC.
|
||||
|
||||
config FPGA_MGR_XILINX_CORE
|
||||
tristate
|
||||
|
||||
config FPGA_MGR_XILINX_SELECTMAP
|
||||
tristate "Xilinx Configuration over SelectMAP"
|
||||
depends on HAS_IOMEM
|
||||
select FPGA_MGR_XILINX_CORE
|
||||
help
|
||||
FPGA manager driver support for Xilinx FPGA configuration
|
||||
over SelectMAP interface.
|
||||
|
||||
config FPGA_MGR_XILINX_SPI
|
||||
tristate "Xilinx Configuration over Slave Serial (SPI)"
|
||||
depends on SPI
|
||||
select FPGA_MGR_XILINX_CORE
|
||||
help
|
||||
FPGA manager driver support for Xilinx FPGA configuration
|
||||
over slave serial interface.
|
||||
|
@ -15,6 +15,8 @@ obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o
|
||||
obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o
|
||||
obj-$(CONFIG_FPGA_MGR_STRATIX10_SOC) += stratix10-soc.o
|
||||
obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o
|
||||
obj-$(CONFIG_FPGA_MGR_XILINX_CORE) += xilinx-core.o
|
||||
obj-$(CONFIG_FPGA_MGR_XILINX_SELECTMAP) += xilinx-selectmap.o
|
||||
obj-$(CONFIG_FPGA_MGR_XILINX_SPI) += xilinx-spi.o
|
||||
obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o
|
||||
obj-$(CONFIG_FPGA_MGR_ZYNQMP_FPGA) += zynqmp-fpga.o
|
||||
|
@ -72,7 +72,6 @@ static bool altera_cvp_chkcfg;
|
||||
struct cvp_priv;
|
||||
|
||||
struct altera_cvp_conf {
|
||||
struct fpga_manager *mgr;
|
||||
struct pci_dev *pci_dev;
|
||||
void __iomem *map;
|
||||
void (*write_data)(struct altera_cvp_conf *conf,
|
||||
|
@ -284,7 +284,6 @@ MODULE_DEVICE_TABLE(spi, altera_ps_spi_ids);
|
||||
static struct spi_driver altera_ps_driver = {
|
||||
.driver = {
|
||||
.name = "altera-ps-spi",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_ef_match,
|
||||
},
|
||||
.id_table = altera_ps_spi_ids,
|
||||
|
@ -858,8 +858,6 @@ static int afu_dev_init(struct platform_device *pdev)
|
||||
if (!afu)
|
||||
return -ENOMEM;
|
||||
|
||||
afu->pdata = pdata;
|
||||
|
||||
mutex_lock(&pdata->lock);
|
||||
dfl_fpga_pdata_set_private(pdata, afu);
|
||||
afu_mmio_region_init(pdata);
|
||||
|
@ -67,7 +67,6 @@ struct dfl_afu_dma_region {
|
||||
* @regions: the mmio region linked list of this afu feature device.
|
||||
* @dma_regions: root of dma regions rb tree.
|
||||
* @num_umsgs: num of umsgs.
|
||||
* @pdata: afu platform device's pdata.
|
||||
*/
|
||||
struct dfl_afu {
|
||||
u64 region_cur_offset;
|
||||
@ -75,8 +74,6 @@ struct dfl_afu {
|
||||
u8 num_umsgs;
|
||||
struct list_head regions;
|
||||
struct rb_root dma_regions;
|
||||
|
||||
struct dfl_feature_platform_data *pdata;
|
||||
};
|
||||
|
||||
/* hold pdata->lock when call __afu_port_enable/disable */
|
||||
|
@ -679,8 +679,6 @@ static int fme_dev_init(struct platform_device *pdev)
|
||||
if (!fme)
|
||||
return -ENOMEM;
|
||||
|
||||
fme->pdata = pdata;
|
||||
|
||||
mutex_lock(&pdata->lock);
|
||||
dfl_fpga_pdata_set_private(pdata, fme);
|
||||
mutex_unlock(&pdata->lock);
|
||||
|
@ -24,13 +24,11 @@
|
||||
* @mgr: FME's FPGA manager platform device.
|
||||
* @region_list: linked list of FME's FPGA regions.
|
||||
* @bridge_list: linked list of FME's FPGA bridges.
|
||||
* @pdata: fme platform device's pdata.
|
||||
*/
|
||||
struct dfl_fme {
|
||||
struct platform_device *mgr;
|
||||
struct list_head region_list;
|
||||
struct list_head bridge_list;
|
||||
struct dfl_feature_platform_data *pdata;
|
||||
};
|
||||
|
||||
extern const struct dfl_feature_ops fme_pr_mgmt_ops;
|
||||
|
@ -437,11 +437,6 @@ void __iomem *dfl_get_feature_ioaddr_by_id(struct device *dev, u16 id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool is_dfl_feature_present(struct device *dev, u16 id)
|
||||
{
|
||||
return !!dfl_get_feature_ioaddr_by_id(dev, id);
|
||||
}
|
||||
|
||||
static inline
|
||||
struct device *dfl_fpga_pdata_to_parent(struct dfl_feature_platform_data *pdata)
|
||||
{
|
||||
|
@ -55,33 +55,26 @@ int fpga_bridge_disable(struct fpga_bridge *bridge)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fpga_bridge_disable);
|
||||
|
||||
static struct fpga_bridge *__fpga_bridge_get(struct device *dev,
|
||||
static struct fpga_bridge *__fpga_bridge_get(struct device *bridge_dev,
|
||||
struct fpga_image_info *info)
|
||||
{
|
||||
struct fpga_bridge *bridge;
|
||||
int ret = -ENODEV;
|
||||
|
||||
bridge = to_fpga_bridge(dev);
|
||||
bridge = to_fpga_bridge(bridge_dev);
|
||||
|
||||
bridge->info = info;
|
||||
|
||||
if (!mutex_trylock(&bridge->mutex)) {
|
||||
ret = -EBUSY;
|
||||
goto err_dev;
|
||||
}
|
||||
if (!mutex_trylock(&bridge->mutex))
|
||||
return ERR_PTR(-EBUSY);
|
||||
|
||||
if (!try_module_get(dev->parent->driver->owner))
|
||||
goto err_ll_mod;
|
||||
if (!try_module_get(bridge->br_ops_owner)) {
|
||||
mutex_unlock(&bridge->mutex);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
dev_dbg(&bridge->dev, "get\n");
|
||||
|
||||
return bridge;
|
||||
|
||||
err_ll_mod:
|
||||
mutex_unlock(&bridge->mutex);
|
||||
err_dev:
|
||||
put_device(dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,13 +91,18 @@ static struct fpga_bridge *__fpga_bridge_get(struct device *dev,
|
||||
struct fpga_bridge *of_fpga_bridge_get(struct device_node *np,
|
||||
struct fpga_image_info *info)
|
||||
{
|
||||
struct device *dev;
|
||||
struct fpga_bridge *bridge;
|
||||
struct device *bridge_dev;
|
||||
|
||||
dev = class_find_device_by_of_node(&fpga_bridge_class, np);
|
||||
if (!dev)
|
||||
bridge_dev = class_find_device_by_of_node(&fpga_bridge_class, np);
|
||||
if (!bridge_dev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return __fpga_bridge_get(dev, info);
|
||||
bridge = __fpga_bridge_get(bridge_dev, info);
|
||||
if (IS_ERR(bridge))
|
||||
put_device(bridge_dev);
|
||||
|
||||
return bridge;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_fpga_bridge_get);
|
||||
|
||||
@ -125,6 +123,7 @@ static int fpga_bridge_dev_match(struct device *dev, const void *data)
|
||||
struct fpga_bridge *fpga_bridge_get(struct device *dev,
|
||||
struct fpga_image_info *info)
|
||||
{
|
||||
struct fpga_bridge *bridge;
|
||||
struct device *bridge_dev;
|
||||
|
||||
bridge_dev = class_find_device(&fpga_bridge_class, NULL, dev,
|
||||
@ -132,7 +131,11 @@ struct fpga_bridge *fpga_bridge_get(struct device *dev,
|
||||
if (!bridge_dev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return __fpga_bridge_get(bridge_dev, info);
|
||||
bridge = __fpga_bridge_get(bridge_dev, info);
|
||||
if (IS_ERR(bridge))
|
||||
put_device(bridge_dev);
|
||||
|
||||
return bridge;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fpga_bridge_get);
|
||||
|
||||
@ -146,7 +149,7 @@ void fpga_bridge_put(struct fpga_bridge *bridge)
|
||||
dev_dbg(&bridge->dev, "put\n");
|
||||
|
||||
bridge->info = NULL;
|
||||
module_put(bridge->dev.parent->driver->owner);
|
||||
module_put(bridge->br_ops_owner);
|
||||
mutex_unlock(&bridge->mutex);
|
||||
put_device(&bridge->dev);
|
||||
}
|
||||
@ -316,18 +319,19 @@ static struct attribute *fpga_bridge_attrs[] = {
|
||||
ATTRIBUTE_GROUPS(fpga_bridge);
|
||||
|
||||
/**
|
||||
* fpga_bridge_register - create and register an FPGA Bridge device
|
||||
* __fpga_bridge_register - create and register an FPGA Bridge device
|
||||
* @parent: FPGA bridge device from pdev
|
||||
* @name: FPGA bridge name
|
||||
* @br_ops: pointer to structure of fpga bridge ops
|
||||
* @priv: FPGA bridge private data
|
||||
* @owner: owner module containing the br_ops
|
||||
*
|
||||
* Return: struct fpga_bridge pointer or ERR_PTR()
|
||||
*/
|
||||
struct fpga_bridge *
|
||||
fpga_bridge_register(struct device *parent, const char *name,
|
||||
const struct fpga_bridge_ops *br_ops,
|
||||
void *priv)
|
||||
__fpga_bridge_register(struct device *parent, const char *name,
|
||||
const struct fpga_bridge_ops *br_ops,
|
||||
void *priv, struct module *owner)
|
||||
{
|
||||
struct fpga_bridge *bridge;
|
||||
int id, ret;
|
||||
@ -357,6 +361,7 @@ fpga_bridge_register(struct device *parent, const char *name,
|
||||
|
||||
bridge->name = name;
|
||||
bridge->br_ops = br_ops;
|
||||
bridge->br_ops_owner = owner;
|
||||
bridge->priv = priv;
|
||||
|
||||
bridge->dev.groups = br_ops->groups;
|
||||
@ -386,7 +391,7 @@ fpga_bridge_register(struct device *parent, const char *name,
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fpga_bridge_register);
|
||||
EXPORT_SYMBOL_GPL(__fpga_bridge_register);
|
||||
|
||||
/**
|
||||
* fpga_bridge_unregister - unregister an FPGA bridge
|
||||
|
@ -664,20 +664,16 @@ static struct attribute *fpga_mgr_attrs[] = {
|
||||
};
|
||||
ATTRIBUTE_GROUPS(fpga_mgr);
|
||||
|
||||
static struct fpga_manager *__fpga_mgr_get(struct device *dev)
|
||||
static struct fpga_manager *__fpga_mgr_get(struct device *mgr_dev)
|
||||
{
|
||||
struct fpga_manager *mgr;
|
||||
|
||||
mgr = to_fpga_manager(dev);
|
||||
mgr = to_fpga_manager(mgr_dev);
|
||||
|
||||
if (!try_module_get(dev->parent->driver->owner))
|
||||
goto err_dev;
|
||||
if (!try_module_get(mgr->mops_owner))
|
||||
mgr = ERR_PTR(-ENODEV);
|
||||
|
||||
return mgr;
|
||||
|
||||
err_dev:
|
||||
put_device(dev);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static int fpga_mgr_dev_match(struct device *dev, const void *data)
|
||||
@ -693,12 +689,18 @@ static int fpga_mgr_dev_match(struct device *dev, const void *data)
|
||||
*/
|
||||
struct fpga_manager *fpga_mgr_get(struct device *dev)
|
||||
{
|
||||
struct device *mgr_dev = class_find_device(&fpga_mgr_class, NULL, dev,
|
||||
fpga_mgr_dev_match);
|
||||
struct fpga_manager *mgr;
|
||||
struct device *mgr_dev;
|
||||
|
||||
mgr_dev = class_find_device(&fpga_mgr_class, NULL, dev, fpga_mgr_dev_match);
|
||||
if (!mgr_dev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return __fpga_mgr_get(mgr_dev);
|
||||
mgr = __fpga_mgr_get(mgr_dev);
|
||||
if (IS_ERR(mgr))
|
||||
put_device(mgr_dev);
|
||||
|
||||
return mgr;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fpga_mgr_get);
|
||||
|
||||
@ -711,13 +713,18 @@ EXPORT_SYMBOL_GPL(fpga_mgr_get);
|
||||
*/
|
||||
struct fpga_manager *of_fpga_mgr_get(struct device_node *node)
|
||||
{
|
||||
struct device *dev;
|
||||
struct fpga_manager *mgr;
|
||||
struct device *mgr_dev;
|
||||
|
||||
dev = class_find_device_by_of_node(&fpga_mgr_class, node);
|
||||
if (!dev)
|
||||
mgr_dev = class_find_device_by_of_node(&fpga_mgr_class, node);
|
||||
if (!mgr_dev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
return __fpga_mgr_get(dev);
|
||||
mgr = __fpga_mgr_get(mgr_dev);
|
||||
if (IS_ERR(mgr))
|
||||
put_device(mgr_dev);
|
||||
|
||||
return mgr;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_fpga_mgr_get);
|
||||
|
||||
@ -727,7 +734,7 @@ EXPORT_SYMBOL_GPL(of_fpga_mgr_get);
|
||||
*/
|
||||
void fpga_mgr_put(struct fpga_manager *mgr)
|
||||
{
|
||||
module_put(mgr->dev.parent->driver->owner);
|
||||
module_put(mgr->mops_owner);
|
||||
put_device(&mgr->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fpga_mgr_put);
|
||||
@ -766,9 +773,10 @@ void fpga_mgr_unlock(struct fpga_manager *mgr)
|
||||
EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
|
||||
|
||||
/**
|
||||
* fpga_mgr_register_full - create and register an FPGA Manager device
|
||||
* __fpga_mgr_register_full - create and register an FPGA Manager device
|
||||
* @parent: fpga manager device from pdev
|
||||
* @info: parameters for fpga manager
|
||||
* @owner: owner module containing the ops
|
||||
*
|
||||
* The caller of this function is responsible for calling fpga_mgr_unregister().
|
||||
* Using devm_fpga_mgr_register_full() instead is recommended.
|
||||
@ -776,7 +784,8 @@ EXPORT_SYMBOL_GPL(fpga_mgr_unlock);
|
||||
* Return: pointer to struct fpga_manager pointer or ERR_PTR()
|
||||
*/
|
||||
struct fpga_manager *
|
||||
fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info)
|
||||
__fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
|
||||
struct module *owner)
|
||||
{
|
||||
const struct fpga_manager_ops *mops = info->mops;
|
||||
struct fpga_manager *mgr;
|
||||
@ -804,6 +813,8 @@ fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *in
|
||||
|
||||
mutex_init(&mgr->ref_mutex);
|
||||
|
||||
mgr->mops_owner = owner;
|
||||
|
||||
mgr->name = info->name;
|
||||
mgr->mops = info->mops;
|
||||
mgr->priv = info->priv;
|
||||
@ -841,14 +852,15 @@ fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *in
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fpga_mgr_register_full);
|
||||
EXPORT_SYMBOL_GPL(__fpga_mgr_register_full);
|
||||
|
||||
/**
|
||||
* fpga_mgr_register - create and register an FPGA Manager device
|
||||
* __fpga_mgr_register - create and register an FPGA Manager device
|
||||
* @parent: fpga manager device from pdev
|
||||
* @name: fpga manager name
|
||||
* @mops: pointer to structure of fpga manager ops
|
||||
* @priv: fpga manager private data
|
||||
* @owner: owner module containing the ops
|
||||
*
|
||||
* The caller of this function is responsible for calling fpga_mgr_unregister().
|
||||
* Using devm_fpga_mgr_register() instead is recommended. This simple
|
||||
@ -859,8 +871,8 @@ EXPORT_SYMBOL_GPL(fpga_mgr_register_full);
|
||||
* Return: pointer to struct fpga_manager pointer or ERR_PTR()
|
||||
*/
|
||||
struct fpga_manager *
|
||||
fpga_mgr_register(struct device *parent, const char *name,
|
||||
const struct fpga_manager_ops *mops, void *priv)
|
||||
__fpga_mgr_register(struct device *parent, const char *name,
|
||||
const struct fpga_manager_ops *mops, void *priv, struct module *owner)
|
||||
{
|
||||
struct fpga_manager_info info = { 0 };
|
||||
|
||||
@ -868,9 +880,9 @@ fpga_mgr_register(struct device *parent, const char *name,
|
||||
info.mops = mops;
|
||||
info.priv = priv;
|
||||
|
||||
return fpga_mgr_register_full(parent, &info);
|
||||
return __fpga_mgr_register_full(parent, &info, owner);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fpga_mgr_register);
|
||||
EXPORT_SYMBOL_GPL(__fpga_mgr_register);
|
||||
|
||||
/**
|
||||
* fpga_mgr_unregister - unregister an FPGA manager
|
||||
@ -900,9 +912,10 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_fpga_mgr_register_full - resource managed variant of fpga_mgr_register()
|
||||
* __devm_fpga_mgr_register_full - resource managed variant of fpga_mgr_register()
|
||||
* @parent: fpga manager device from pdev
|
||||
* @info: parameters for fpga manager
|
||||
* @owner: owner module containing the ops
|
||||
*
|
||||
* Return: fpga manager pointer on success, negative error code otherwise.
|
||||
*
|
||||
@ -910,7 +923,8 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res)
|
||||
* function will be called automatically when the managing device is detached.
|
||||
*/
|
||||
struct fpga_manager *
|
||||
devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info)
|
||||
__devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
|
||||
struct module *owner)
|
||||
{
|
||||
struct fpga_mgr_devres *dr;
|
||||
struct fpga_manager *mgr;
|
||||
@ -919,7 +933,7 @@ devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_inf
|
||||
if (!dr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mgr = fpga_mgr_register_full(parent, info);
|
||||
mgr = __fpga_mgr_register_full(parent, info, owner);
|
||||
if (IS_ERR(mgr)) {
|
||||
devres_free(dr);
|
||||
return mgr;
|
||||
@ -930,14 +944,15 @@ devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_inf
|
||||
|
||||
return mgr;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_fpga_mgr_register_full);
|
||||
EXPORT_SYMBOL_GPL(__devm_fpga_mgr_register_full);
|
||||
|
||||
/**
|
||||
* devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
|
||||
* __devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
|
||||
* @parent: fpga manager device from pdev
|
||||
* @name: fpga manager name
|
||||
* @mops: pointer to structure of fpga manager ops
|
||||
* @priv: fpga manager private data
|
||||
* @owner: owner module containing the ops
|
||||
*
|
||||
* Return: fpga manager pointer on success, negative error code otherwise.
|
||||
*
|
||||
@ -946,8 +961,9 @@ EXPORT_SYMBOL_GPL(devm_fpga_mgr_register_full);
|
||||
* device is detached.
|
||||
*/
|
||||
struct fpga_manager *
|
||||
devm_fpga_mgr_register(struct device *parent, const char *name,
|
||||
const struct fpga_manager_ops *mops, void *priv)
|
||||
__devm_fpga_mgr_register(struct device *parent, const char *name,
|
||||
const struct fpga_manager_ops *mops, void *priv,
|
||||
struct module *owner)
|
||||
{
|
||||
struct fpga_manager_info info = { 0 };
|
||||
|
||||
@ -955,9 +971,9 @@ devm_fpga_mgr_register(struct device *parent, const char *name,
|
||||
info.mops = mops;
|
||||
info.priv = priv;
|
||||
|
||||
return devm_fpga_mgr_register_full(parent, &info);
|
||||
return __devm_fpga_mgr_register_full(parent, &info, owner);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_fpga_mgr_register);
|
||||
EXPORT_SYMBOL_GPL(__devm_fpga_mgr_register);
|
||||
|
||||
static void fpga_mgr_dev_release(struct device *dev)
|
||||
{
|
||||
|
@ -53,7 +53,7 @@ static struct fpga_region *fpga_region_get(struct fpga_region *region)
|
||||
}
|
||||
|
||||
get_device(dev);
|
||||
if (!try_module_get(dev->parent->driver->owner)) {
|
||||
if (!try_module_get(region->ops_owner)) {
|
||||
put_device(dev);
|
||||
mutex_unlock(®ion->mutex);
|
||||
return ERR_PTR(-ENODEV);
|
||||
@ -75,7 +75,7 @@ static void fpga_region_put(struct fpga_region *region)
|
||||
|
||||
dev_dbg(dev, "put\n");
|
||||
|
||||
module_put(dev->parent->driver->owner);
|
||||
module_put(region->ops_owner);
|
||||
put_device(dev);
|
||||
mutex_unlock(®ion->mutex);
|
||||
}
|
||||
@ -181,14 +181,16 @@ static struct attribute *fpga_region_attrs[] = {
|
||||
ATTRIBUTE_GROUPS(fpga_region);
|
||||
|
||||
/**
|
||||
* fpga_region_register_full - create and register an FPGA Region device
|
||||
* __fpga_region_register_full - create and register an FPGA Region device
|
||||
* @parent: device parent
|
||||
* @info: parameters for FPGA Region
|
||||
* @owner: module containing the get_bridges function
|
||||
*
|
||||
* Return: struct fpga_region or ERR_PTR()
|
||||
*/
|
||||
struct fpga_region *
|
||||
fpga_region_register_full(struct device *parent, const struct fpga_region_info *info)
|
||||
__fpga_region_register_full(struct device *parent, const struct fpga_region_info *info,
|
||||
struct module *owner)
|
||||
{
|
||||
struct fpga_region *region;
|
||||
int id, ret = 0;
|
||||
@ -213,6 +215,7 @@ fpga_region_register_full(struct device *parent, const struct fpga_region_info *
|
||||
region->compat_id = info->compat_id;
|
||||
region->priv = info->priv;
|
||||
region->get_bridges = info->get_bridges;
|
||||
region->ops_owner = owner;
|
||||
|
||||
mutex_init(®ion->mutex);
|
||||
INIT_LIST_HEAD(®ion->bridge_list);
|
||||
@ -241,13 +244,14 @@ fpga_region_register_full(struct device *parent, const struct fpga_region_info *
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fpga_region_register_full);
|
||||
EXPORT_SYMBOL_GPL(__fpga_region_register_full);
|
||||
|
||||
/**
|
||||
* fpga_region_register - create and register an FPGA Region device
|
||||
* __fpga_region_register - create and register an FPGA Region device
|
||||
* @parent: device parent
|
||||
* @mgr: manager that programs this region
|
||||
* @get_bridges: optional function to get bridges to a list
|
||||
* @owner: module containing the get_bridges function
|
||||
*
|
||||
* This simple version of the register function should be sufficient for most users.
|
||||
* The fpga_region_register_full() function is available for users that need to
|
||||
@ -256,17 +260,17 @@ EXPORT_SYMBOL_GPL(fpga_region_register_full);
|
||||
* Return: struct fpga_region or ERR_PTR()
|
||||
*/
|
||||
struct fpga_region *
|
||||
fpga_region_register(struct device *parent, struct fpga_manager *mgr,
|
||||
int (*get_bridges)(struct fpga_region *))
|
||||
__fpga_region_register(struct device *parent, struct fpga_manager *mgr,
|
||||
int (*get_bridges)(struct fpga_region *), struct module *owner)
|
||||
{
|
||||
struct fpga_region_info info = { 0 };
|
||||
|
||||
info.mgr = mgr;
|
||||
info.get_bridges = get_bridges;
|
||||
|
||||
return fpga_region_register_full(parent, &info);
|
||||
return __fpga_region_register_full(parent, &info, owner);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fpga_region_register);
|
||||
EXPORT_SYMBOL_GPL(__fpga_region_register);
|
||||
|
||||
/**
|
||||
* fpga_region_unregister - unregister an FPGA region
|
||||
|
@ -10,8 +10,8 @@
|
||||
|
||||
#include <linux/fpga/fpga-mgr.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/stringify.h>
|
||||
|
||||
@ -199,7 +199,7 @@ static struct spi_driver ice40_fpga_driver = {
|
||||
.probe = ice40_fpga_probe,
|
||||
.driver = {
|
||||
.name = "ice40spi",
|
||||
.of_match_table = of_match_ptr(ice40_fpga_of_match),
|
||||
.of_match_table = ice40_fpga_of_match,
|
||||
},
|
||||
.id_table = ice40_fpga_spi_ids,
|
||||
};
|
||||
|
@ -7,8 +7,8 @@
|
||||
* Author: Marco Pagani <marpagan@redhat.com>
|
||||
*/
|
||||
|
||||
#include <kunit/device.h>
|
||||
#include <kunit/test.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fpga/fpga-bridge.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
@ -19,7 +19,7 @@ struct bridge_stats {
|
||||
|
||||
struct bridge_ctx {
|
||||
struct fpga_bridge *bridge;
|
||||
struct platform_device *pdev;
|
||||
struct device *dev;
|
||||
struct bridge_stats stats;
|
||||
};
|
||||
|
||||
@ -43,30 +43,31 @@ static const struct fpga_bridge_ops fake_bridge_ops = {
|
||||
/**
|
||||
* register_test_bridge() - Register a fake FPGA bridge for testing.
|
||||
* @test: KUnit test context object.
|
||||
* @dev_name: name of the kunit device to be registered
|
||||
*
|
||||
* Return: Context of the newly registered FPGA bridge.
|
||||
*/
|
||||
static struct bridge_ctx *register_test_bridge(struct kunit *test)
|
||||
static struct bridge_ctx *register_test_bridge(struct kunit *test, const char *dev_name)
|
||||
{
|
||||
struct bridge_ctx *ctx;
|
||||
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
|
||||
ctx->pdev = platform_device_register_simple("bridge_pdev", PLATFORM_DEVID_AUTO, NULL, 0);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->pdev);
|
||||
ctx->dev = kunit_device_register(test, dev_name);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->dev);
|
||||
|
||||
ctx->bridge = fpga_bridge_register(&ctx->pdev->dev, "Fake FPGA bridge", &fake_bridge_ops,
|
||||
ctx->bridge = fpga_bridge_register(ctx->dev, "Fake FPGA bridge", &fake_bridge_ops,
|
||||
&ctx->stats);
|
||||
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->bridge));
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
static void unregister_test_bridge(struct bridge_ctx *ctx)
|
||||
static void unregister_test_bridge(struct kunit *test, struct bridge_ctx *ctx)
|
||||
{
|
||||
fpga_bridge_unregister(ctx->bridge);
|
||||
platform_device_unregister(ctx->pdev);
|
||||
kunit_device_unregister(test, ctx->dev);
|
||||
}
|
||||
|
||||
static void fpga_bridge_test_get(struct kunit *test)
|
||||
@ -74,10 +75,10 @@ static void fpga_bridge_test_get(struct kunit *test)
|
||||
struct bridge_ctx *ctx = test->priv;
|
||||
struct fpga_bridge *bridge;
|
||||
|
||||
bridge = fpga_bridge_get(&ctx->pdev->dev, NULL);
|
||||
bridge = fpga_bridge_get(ctx->dev, NULL);
|
||||
KUNIT_EXPECT_PTR_EQ(test, bridge, ctx->bridge);
|
||||
|
||||
bridge = fpga_bridge_get(&ctx->pdev->dev, NULL);
|
||||
bridge = fpga_bridge_get(ctx->dev, NULL);
|
||||
KUNIT_EXPECT_EQ(test, PTR_ERR(bridge), -EBUSY);
|
||||
|
||||
fpga_bridge_put(ctx->bridge);
|
||||
@ -105,19 +106,19 @@ static void fpga_bridge_test_get_put_list(struct kunit *test)
|
||||
int ret;
|
||||
|
||||
ctx_0 = test->priv;
|
||||
ctx_1 = register_test_bridge(test);
|
||||
ctx_1 = register_test_bridge(test, "fpga-bridge-test-dev-1");
|
||||
|
||||
INIT_LIST_HEAD(&bridge_list);
|
||||
|
||||
/* Get bridge 0 and add it to the list */
|
||||
ret = fpga_bridge_get_to_list(&ctx_0->pdev->dev, NULL, &bridge_list);
|
||||
ret = fpga_bridge_get_to_list(ctx_0->dev, NULL, &bridge_list);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
|
||||
KUNIT_EXPECT_PTR_EQ(test, ctx_0->bridge,
|
||||
list_first_entry_or_null(&bridge_list, struct fpga_bridge, node));
|
||||
|
||||
/* Get bridge 1 and add it to the list */
|
||||
ret = fpga_bridge_get_to_list(&ctx_1->pdev->dev, NULL, &bridge_list);
|
||||
ret = fpga_bridge_get_to_list(ctx_1->dev, NULL, &bridge_list);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
|
||||
KUNIT_EXPECT_PTR_EQ(test, ctx_1->bridge,
|
||||
@ -141,19 +142,19 @@ static void fpga_bridge_test_get_put_list(struct kunit *test)
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, list_empty(&bridge_list));
|
||||
|
||||
unregister_test_bridge(ctx_1);
|
||||
unregister_test_bridge(test, ctx_1);
|
||||
}
|
||||
|
||||
static int fpga_bridge_test_init(struct kunit *test)
|
||||
{
|
||||
test->priv = register_test_bridge(test);
|
||||
test->priv = register_test_bridge(test, "fpga-bridge-test-dev-0");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fpga_bridge_test_exit(struct kunit *test)
|
||||
{
|
||||
unregister_test_bridge(test->priv);
|
||||
unregister_test_bridge(test, test->priv);
|
||||
}
|
||||
|
||||
static struct kunit_case fpga_bridge_test_cases[] = {
|
||||
|
@ -7,8 +7,8 @@
|
||||
* Author: Marco Pagani <marpagan@redhat.com>
|
||||
*/
|
||||
|
||||
#include <kunit/device.h>
|
||||
#include <kunit/test.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fpga/fpga-mgr.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/scatterlist.h>
|
||||
@ -40,7 +40,7 @@ struct mgr_stats {
|
||||
struct mgr_ctx {
|
||||
struct fpga_image_info *img_info;
|
||||
struct fpga_manager *mgr;
|
||||
struct platform_device *pdev;
|
||||
struct device *dev;
|
||||
struct mgr_stats stats;
|
||||
};
|
||||
|
||||
@ -194,7 +194,7 @@ static void fpga_mgr_test_get(struct kunit *test)
|
||||
struct mgr_ctx *ctx = test->priv;
|
||||
struct fpga_manager *mgr;
|
||||
|
||||
mgr = fpga_mgr_get(&ctx->pdev->dev);
|
||||
mgr = fpga_mgr_get(ctx->dev);
|
||||
KUNIT_EXPECT_PTR_EQ(test, mgr, ctx->mgr);
|
||||
|
||||
fpga_mgr_put(ctx->mgr);
|
||||
@ -284,14 +284,14 @@ static int fpga_mgr_test_init(struct kunit *test)
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
|
||||
ctx->pdev = platform_device_register_simple("mgr_pdev", PLATFORM_DEVID_AUTO, NULL, 0);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->pdev);
|
||||
ctx->dev = kunit_device_register(test, "fpga-manager-test-dev");
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->dev);
|
||||
|
||||
ctx->mgr = devm_fpga_mgr_register(&ctx->pdev->dev, "Fake FPGA Manager", &fake_mgr_ops,
|
||||
ctx->mgr = devm_fpga_mgr_register(ctx->dev, "Fake FPGA Manager", &fake_mgr_ops,
|
||||
&ctx->stats);
|
||||
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->mgr));
|
||||
|
||||
ctx->img_info = fpga_image_info_alloc(&ctx->pdev->dev);
|
||||
ctx->img_info = fpga_image_info_alloc(ctx->dev);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->img_info);
|
||||
|
||||
test->priv = ctx;
|
||||
@ -304,7 +304,7 @@ static void fpga_mgr_test_exit(struct kunit *test)
|
||||
struct mgr_ctx *ctx = test->priv;
|
||||
|
||||
fpga_image_info_free(ctx->img_info);
|
||||
platform_device_unregister(ctx->pdev);
|
||||
kunit_device_unregister(test, ctx->dev);
|
||||
}
|
||||
|
||||
static struct kunit_case fpga_mgr_test_cases[] = {
|
||||
|
@ -7,12 +7,12 @@
|
||||
* Author: Marco Pagani <marpagan@redhat.com>
|
||||
*/
|
||||
|
||||
#include <kunit/device.h>
|
||||
#include <kunit/test.h>
|
||||
#include <linux/fpga/fpga-bridge.h>
|
||||
#include <linux/fpga/fpga-mgr.h>
|
||||
#include <linux/fpga/fpga-region.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct mgr_stats {
|
||||
@ -26,11 +26,11 @@ struct bridge_stats {
|
||||
|
||||
struct test_ctx {
|
||||
struct fpga_manager *mgr;
|
||||
struct platform_device *mgr_pdev;
|
||||
struct device *mgr_dev;
|
||||
struct fpga_bridge *bridge;
|
||||
struct platform_device *bridge_pdev;
|
||||
struct device *bridge_dev;
|
||||
struct fpga_region *region;
|
||||
struct platform_device *region_pdev;
|
||||
struct device *region_dev;
|
||||
struct bridge_stats bridge_stats;
|
||||
struct mgr_stats mgr_stats;
|
||||
};
|
||||
@ -91,7 +91,7 @@ static void fpga_region_test_class_find(struct kunit *test)
|
||||
struct test_ctx *ctx = test->priv;
|
||||
struct fpga_region *region;
|
||||
|
||||
region = fpga_region_class_find(NULL, &ctx->region_pdev->dev, fake_region_match);
|
||||
region = fpga_region_class_find(NULL, ctx->region_dev, fake_region_match);
|
||||
KUNIT_EXPECT_PTR_EQ(test, region, ctx->region);
|
||||
|
||||
put_device(®ion->dev);
|
||||
@ -108,7 +108,7 @@ static void fpga_region_test_program_fpga(struct kunit *test)
|
||||
char img_buf[4];
|
||||
int ret;
|
||||
|
||||
img_info = fpga_image_info_alloc(&ctx->mgr_pdev->dev);
|
||||
img_info = fpga_image_info_alloc(ctx->mgr_dev);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, img_info);
|
||||
|
||||
img_info->buf = img_buf;
|
||||
@ -148,32 +148,30 @@ static int fpga_region_test_init(struct kunit *test)
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
|
||||
ctx->mgr_pdev = platform_device_register_simple("mgr_pdev", PLATFORM_DEVID_AUTO, NULL, 0);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->mgr_pdev);
|
||||
ctx->mgr_dev = kunit_device_register(test, "fpga-manager-test-dev");
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->mgr_dev);
|
||||
|
||||
ctx->mgr = devm_fpga_mgr_register(&ctx->mgr_pdev->dev, "Fake FPGA Manager", &fake_mgr_ops,
|
||||
&ctx->mgr_stats);
|
||||
ctx->mgr = devm_fpga_mgr_register(ctx->mgr_dev, "Fake FPGA Manager",
|
||||
&fake_mgr_ops, &ctx->mgr_stats);
|
||||
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->mgr));
|
||||
|
||||
ctx->bridge_pdev = platform_device_register_simple("bridge_pdev", PLATFORM_DEVID_AUTO,
|
||||
NULL, 0);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->bridge_pdev);
|
||||
ctx->bridge_dev = kunit_device_register(test, "fpga-bridge-test-dev");
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->bridge_dev);
|
||||
|
||||
ctx->bridge = fpga_bridge_register(&ctx->bridge_pdev->dev, "Fake FPGA Bridge",
|
||||
ctx->bridge = fpga_bridge_register(ctx->bridge_dev, "Fake FPGA Bridge",
|
||||
&fake_bridge_ops, &ctx->bridge_stats);
|
||||
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->bridge));
|
||||
|
||||
ctx->bridge_stats.enable = true;
|
||||
|
||||
ctx->region_pdev = platform_device_register_simple("region_pdev", PLATFORM_DEVID_AUTO,
|
||||
NULL, 0);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->region_pdev);
|
||||
ctx->region_dev = kunit_device_register(test, "fpga-region-test-dev");
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->region_dev);
|
||||
|
||||
region_info.mgr = ctx->mgr;
|
||||
region_info.priv = ctx->bridge;
|
||||
region_info.get_bridges = fake_region_get_bridges;
|
||||
|
||||
ctx->region = fpga_region_register_full(&ctx->region_pdev->dev, ®ion_info);
|
||||
ctx->region = fpga_region_register_full(ctx->region_dev, ®ion_info);
|
||||
KUNIT_ASSERT_FALSE(test, IS_ERR_OR_NULL(ctx->region));
|
||||
|
||||
test->priv = ctx;
|
||||
@ -186,18 +184,17 @@ static void fpga_region_test_exit(struct kunit *test)
|
||||
struct test_ctx *ctx = test->priv;
|
||||
|
||||
fpga_region_unregister(ctx->region);
|
||||
platform_device_unregister(ctx->region_pdev);
|
||||
kunit_device_unregister(test, ctx->region_dev);
|
||||
|
||||
fpga_bridge_unregister(ctx->bridge);
|
||||
platform_device_unregister(ctx->bridge_pdev);
|
||||
kunit_device_unregister(test, ctx->bridge_dev);
|
||||
|
||||
platform_device_unregister(ctx->mgr_pdev);
|
||||
kunit_device_unregister(test, ctx->mgr_dev);
|
||||
}
|
||||
|
||||
static struct kunit_case fpga_region_test_cases[] = {
|
||||
KUNIT_CASE(fpga_region_test_class_find),
|
||||
KUNIT_CASE(fpga_region_test_program_fpga),
|
||||
|
||||
{}
|
||||
};
|
||||
|
||||
|
229
drivers/fpga/xilinx-core.c
Normal file
229
drivers/fpga/xilinx-core.c
Normal file
@ -0,0 +1,229 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Common parts of the Xilinx Spartan6 and 7 Series FPGA manager drivers.
|
||||
*
|
||||
* Copyright (C) 2017 DENX Software Engineering
|
||||
*
|
||||
* Anatolij Gustschin <agust@denx.de>
|
||||
*/
|
||||
|
||||
#include "xilinx-core.h"
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fpga/fpga-mgr.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
static int get_done_gpio(struct fpga_manager *mgr)
|
||||
{
|
||||
struct xilinx_fpga_core *core = mgr->priv;
|
||||
int ret;
|
||||
|
||||
ret = gpiod_get_value(core->done);
|
||||
if (ret < 0)
|
||||
dev_err(&mgr->dev, "Error reading DONE (%d)\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum fpga_mgr_states xilinx_core_state(struct fpga_manager *mgr)
|
||||
{
|
||||
if (!get_done_gpio(mgr))
|
||||
return FPGA_MGR_STATE_RESET;
|
||||
|
||||
return FPGA_MGR_STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* wait_for_init_b - wait for the INIT_B pin to have a given state, or wait
|
||||
* a given delay if the pin is unavailable
|
||||
*
|
||||
* @mgr: The FPGA manager object
|
||||
* @value: Value INIT_B to wait for (1 = asserted = low)
|
||||
* @alt_udelay: Delay to wait if the INIT_B GPIO is not available
|
||||
*
|
||||
* Returns 0 when the INIT_B GPIO reached the given state or -ETIMEDOUT if
|
||||
* too much time passed waiting for that. If no INIT_B GPIO is available
|
||||
* then always return 0.
|
||||
*/
|
||||
static int wait_for_init_b(struct fpga_manager *mgr, int value,
|
||||
unsigned long alt_udelay)
|
||||
{
|
||||
struct xilinx_fpga_core *core = mgr->priv;
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
|
||||
|
||||
if (core->init_b) {
|
||||
while (time_before(jiffies, timeout)) {
|
||||
int ret = gpiod_get_value(core->init_b);
|
||||
|
||||
if (ret == value)
|
||||
return 0;
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&mgr->dev,
|
||||
"Error reading INIT_B (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usleep_range(100, 400);
|
||||
}
|
||||
|
||||
dev_err(&mgr->dev, "Timeout waiting for INIT_B to %s\n",
|
||||
value ? "assert" : "deassert");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
udelay(alt_udelay);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xilinx_core_write_init(struct fpga_manager *mgr,
|
||||
struct fpga_image_info *info, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct xilinx_fpga_core *core = mgr->priv;
|
||||
int err;
|
||||
|
||||
if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
|
||||
dev_err(&mgr->dev, "Partial reconfiguration not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gpiod_set_value(core->prog_b, 1);
|
||||
|
||||
err = wait_for_init_b(mgr, 1, 1); /* min is 500 ns */
|
||||
if (err) {
|
||||
gpiod_set_value(core->prog_b, 0);
|
||||
return err;
|
||||
}
|
||||
|
||||
gpiod_set_value(core->prog_b, 0);
|
||||
|
||||
err = wait_for_init_b(mgr, 0, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (get_done_gpio(mgr)) {
|
||||
dev_err(&mgr->dev, "Unexpected DONE pin state...\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* program latency */
|
||||
usleep_range(7500, 7600);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xilinx_core_write(struct fpga_manager *mgr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct xilinx_fpga_core *core = mgr->priv;
|
||||
|
||||
return core->write(core, buf, count);
|
||||
}
|
||||
|
||||
static int xilinx_core_write_complete(struct fpga_manager *mgr,
|
||||
struct fpga_image_info *info)
|
||||
{
|
||||
struct xilinx_fpga_core *core = mgr->priv;
|
||||
unsigned long timeout =
|
||||
jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
|
||||
bool expired = false;
|
||||
int done;
|
||||
int ret;
|
||||
const char padding[1] = { 0xff };
|
||||
|
||||
/*
|
||||
* This loop is carefully written such that if the driver is
|
||||
* scheduled out for more than 'timeout', we still check for DONE
|
||||
* before giving up and we apply 8 extra CCLK cycles in all cases.
|
||||
*/
|
||||
while (!expired) {
|
||||
expired = time_after(jiffies, timeout);
|
||||
|
||||
done = get_done_gpio(mgr);
|
||||
if (done < 0)
|
||||
return done;
|
||||
|
||||
ret = core->write(core, padding, sizeof(padding));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (done)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (core->init_b) {
|
||||
ret = gpiod_get_value(core->init_b);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_err(&mgr->dev,
|
||||
ret ? "CRC error or invalid device\n" :
|
||||
"Missing sync word or incomplete bitstream\n");
|
||||
} else {
|
||||
dev_err(&mgr->dev, "Timeout after config data transfer\n");
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static inline struct gpio_desc *
|
||||
xilinx_core_devm_gpiod_get(struct device *dev, const char *con_id,
|
||||
const char *legacy_con_id, enum gpiod_flags flags)
|
||||
{
|
||||
struct gpio_desc *desc;
|
||||
|
||||
desc = devm_gpiod_get(dev, con_id, flags);
|
||||
if (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT &&
|
||||
of_device_is_compatible(dev->of_node, "xlnx,fpga-slave-serial"))
|
||||
desc = devm_gpiod_get(dev, legacy_con_id, flags);
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
static const struct fpga_manager_ops xilinx_core_ops = {
|
||||
.state = xilinx_core_state,
|
||||
.write_init = xilinx_core_write_init,
|
||||
.write = xilinx_core_write,
|
||||
.write_complete = xilinx_core_write_complete,
|
||||
};
|
||||
|
||||
int xilinx_core_probe(struct xilinx_fpga_core *core)
|
||||
{
|
||||
struct fpga_manager *mgr;
|
||||
|
||||
if (!core || !core->dev || !core->write)
|
||||
return -EINVAL;
|
||||
|
||||
/* PROGRAM_B is active low */
|
||||
core->prog_b = xilinx_core_devm_gpiod_get(core->dev, "prog", "prog_b",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(core->prog_b))
|
||||
return dev_err_probe(core->dev, PTR_ERR(core->prog_b),
|
||||
"Failed to get PROGRAM_B gpio\n");
|
||||
|
||||
core->init_b = xilinx_core_devm_gpiod_get(core->dev, "init", "init-b",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(core->init_b))
|
||||
return dev_err_probe(core->dev, PTR_ERR(core->init_b),
|
||||
"Failed to get INIT_B gpio\n");
|
||||
|
||||
core->done = devm_gpiod_get(core->dev, "done", GPIOD_IN);
|
||||
if (IS_ERR(core->done))
|
||||
return dev_err_probe(core->dev, PTR_ERR(core->done),
|
||||
"Failed to get DONE gpio\n");
|
||||
|
||||
mgr = devm_fpga_mgr_register(core->dev,
|
||||
"Xilinx Slave Serial FPGA Manager",
|
||||
&xilinx_core_ops, core);
|
||||
return PTR_ERR_OR_ZERO(mgr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xilinx_core_probe);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
|
||||
MODULE_DESCRIPTION("Xilinx 7 Series FPGA manager core");
|
27
drivers/fpga/xilinx-core.h
Normal file
27
drivers/fpga/xilinx-core.h
Normal file
@ -0,0 +1,27 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef __XILINX_CORE_H
|
||||
#define __XILINX_CORE_H
|
||||
|
||||
#include <linux/device.h>
|
||||
|
||||
/**
|
||||
* struct xilinx_fpga_core - interface between the driver and the core manager
|
||||
* of Xilinx 7 Series FPGA manager
|
||||
* @dev: device node
|
||||
* @write: write callback of the driver
|
||||
*/
|
||||
struct xilinx_fpga_core {
|
||||
/* public: */
|
||||
struct device *dev;
|
||||
int (*write)(struct xilinx_fpga_core *core, const char *buf,
|
||||
size_t count);
|
||||
/* private: handled by xilinx-core */
|
||||
struct gpio_desc *prog_b;
|
||||
struct gpio_desc *init_b;
|
||||
struct gpio_desc *done;
|
||||
};
|
||||
|
||||
int xilinx_core_probe(struct xilinx_fpga_core *core);
|
||||
|
||||
#endif /* __XILINX_CORE_H */
|
95
drivers/fpga/xilinx-selectmap.c
Normal file
95
drivers/fpga/xilinx-selectmap.c
Normal file
@ -0,0 +1,95 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Xilinx Spartan6 and 7 Series SelectMAP interface driver
|
||||
*
|
||||
* (C) 2024 Charles Perry <charles.perry@savoirfairelinux.com>
|
||||
*
|
||||
* Manage Xilinx FPGA firmware loaded over the SelectMAP configuration
|
||||
* interface.
|
||||
*/
|
||||
|
||||
#include "xilinx-core.h"
|
||||
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct xilinx_selectmap_conf {
|
||||
struct xilinx_fpga_core core;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
#define to_xilinx_selectmap_conf(obj) \
|
||||
container_of(obj, struct xilinx_selectmap_conf, core)
|
||||
|
||||
static int xilinx_selectmap_write(struct xilinx_fpga_core *core,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct xilinx_selectmap_conf *conf = to_xilinx_selectmap_conf(core);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
writeb(buf[i], conf->base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xilinx_selectmap_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct xilinx_selectmap_conf *conf;
|
||||
struct gpio_desc *gpio;
|
||||
void __iomem *base;
|
||||
|
||||
conf = devm_kzalloc(&pdev->dev, sizeof(*conf), GFP_KERNEL);
|
||||
if (!conf)
|
||||
return -ENOMEM;
|
||||
|
||||
conf->core.dev = &pdev->dev;
|
||||
conf->core.write = xilinx_selectmap_write;
|
||||
|
||||
base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
|
||||
if (IS_ERR(base))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(base),
|
||||
"ioremap error\n");
|
||||
conf->base = base;
|
||||
|
||||
/* CSI_B is active low */
|
||||
gpio = devm_gpiod_get_optional(&pdev->dev, "csi", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gpio))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(gpio),
|
||||
"Failed to get CSI_B gpio\n");
|
||||
|
||||
/* RDWR_B is active low */
|
||||
gpio = devm_gpiod_get_optional(&pdev->dev, "rdwr", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gpio))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(gpio),
|
||||
"Failed to get RDWR_B gpio\n");
|
||||
|
||||
return xilinx_core_probe(&conf->core);
|
||||
}
|
||||
|
||||
static const struct of_device_id xlnx_selectmap_of_match[] = {
|
||||
{ .compatible = "xlnx,fpga-xc7s-selectmap", }, // Spartan-7
|
||||
{ .compatible = "xlnx,fpga-xc7a-selectmap", }, // Artix-7
|
||||
{ .compatible = "xlnx,fpga-xc7k-selectmap", }, // Kintex-7
|
||||
{ .compatible = "xlnx,fpga-xc7v-selectmap", }, // Virtex-7
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, xlnx_selectmap_of_match);
|
||||
|
||||
static struct platform_driver xilinx_selectmap_driver = {
|
||||
.driver = {
|
||||
.name = "xilinx-selectmap",
|
||||
.of_match_table = xlnx_selectmap_of_match,
|
||||
},
|
||||
.probe = xilinx_selectmap_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(xilinx_selectmap_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Charles Perry <charles.perry@savoirfairelinux.com>");
|
||||
MODULE_DESCRIPTION("Load Xilinx FPGA firmware over SelectMap");
|
@ -10,127 +10,17 @@
|
||||
* the slave serial configuration interface.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/fpga/fpga-mgr.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include "xilinx-core.h"
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
struct xilinx_spi_conf {
|
||||
struct spi_device *spi;
|
||||
struct gpio_desc *prog_b;
|
||||
struct gpio_desc *init_b;
|
||||
struct gpio_desc *done;
|
||||
};
|
||||
|
||||
static int get_done_gpio(struct fpga_manager *mgr)
|
||||
{
|
||||
struct xilinx_spi_conf *conf = mgr->priv;
|
||||
int ret;
|
||||
|
||||
ret = gpiod_get_value(conf->done);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(&mgr->dev, "Error reading DONE (%d)\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum fpga_mgr_states xilinx_spi_state(struct fpga_manager *mgr)
|
||||
{
|
||||
if (!get_done_gpio(mgr))
|
||||
return FPGA_MGR_STATE_RESET;
|
||||
|
||||
return FPGA_MGR_STATE_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* wait_for_init_b - wait for the INIT_B pin to have a given state, or wait
|
||||
* a given delay if the pin is unavailable
|
||||
*
|
||||
* @mgr: The FPGA manager object
|
||||
* @value: Value INIT_B to wait for (1 = asserted = low)
|
||||
* @alt_udelay: Delay to wait if the INIT_B GPIO is not available
|
||||
*
|
||||
* Returns 0 when the INIT_B GPIO reached the given state or -ETIMEDOUT if
|
||||
* too much time passed waiting for that. If no INIT_B GPIO is available
|
||||
* then always return 0.
|
||||
*/
|
||||
static int wait_for_init_b(struct fpga_manager *mgr, int value,
|
||||
unsigned long alt_udelay)
|
||||
{
|
||||
struct xilinx_spi_conf *conf = mgr->priv;
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
|
||||
|
||||
if (conf->init_b) {
|
||||
while (time_before(jiffies, timeout)) {
|
||||
int ret = gpiod_get_value(conf->init_b);
|
||||
|
||||
if (ret == value)
|
||||
return 0;
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
usleep_range(100, 400);
|
||||
}
|
||||
|
||||
dev_err(&mgr->dev, "Timeout waiting for INIT_B to %s\n",
|
||||
value ? "assert" : "deassert");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
udelay(alt_udelay);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xilinx_spi_write_init(struct fpga_manager *mgr,
|
||||
struct fpga_image_info *info,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct xilinx_spi_conf *conf = mgr->priv;
|
||||
int err;
|
||||
|
||||
if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) {
|
||||
dev_err(&mgr->dev, "Partial reconfiguration not supported\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gpiod_set_value(conf->prog_b, 1);
|
||||
|
||||
err = wait_for_init_b(mgr, 1, 1); /* min is 500 ns */
|
||||
if (err) {
|
||||
gpiod_set_value(conf->prog_b, 0);
|
||||
return err;
|
||||
}
|
||||
|
||||
gpiod_set_value(conf->prog_b, 0);
|
||||
|
||||
err = wait_for_init_b(mgr, 0, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (get_done_gpio(mgr)) {
|
||||
dev_err(&mgr->dev, "Unexpected DONE pin state...\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* program latency */
|
||||
usleep_range(7500, 7600);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf,
|
||||
static int xilinx_spi_write(struct xilinx_fpga_core *core, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct xilinx_spi_conf *conf = mgr->priv;
|
||||
struct spi_device *spi = to_spi_device(core->dev);
|
||||
const char *fw_data = buf;
|
||||
const char *fw_data_end = fw_data + count;
|
||||
|
||||
@ -141,9 +31,9 @@ static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf,
|
||||
remaining = fw_data_end - fw_data;
|
||||
stride = min_t(size_t, remaining, SZ_4K);
|
||||
|
||||
ret = spi_write(conf->spi, fw_data, stride);
|
||||
ret = spi_write(spi, fw_data, stride);
|
||||
if (ret) {
|
||||
dev_err(&mgr->dev, "SPI error in firmware write: %d\n",
|
||||
dev_err(core->dev, "SPI error in firmware write: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
@ -153,109 +43,25 @@ static int xilinx_spi_write(struct fpga_manager *mgr, const char *buf,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xilinx_spi_apply_cclk_cycles(struct xilinx_spi_conf *conf)
|
||||
{
|
||||
struct spi_device *spi = conf->spi;
|
||||
const u8 din_data[1] = { 0xff };
|
||||
int ret;
|
||||
|
||||
ret = spi_write(conf->spi, din_data, sizeof(din_data));
|
||||
if (ret)
|
||||
dev_err(&spi->dev, "applying CCLK cycles failed: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xilinx_spi_write_complete(struct fpga_manager *mgr,
|
||||
struct fpga_image_info *info)
|
||||
{
|
||||
struct xilinx_spi_conf *conf = mgr->priv;
|
||||
unsigned long timeout = jiffies + usecs_to_jiffies(info->config_complete_timeout_us);
|
||||
bool expired = false;
|
||||
int done;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* This loop is carefully written such that if the driver is
|
||||
* scheduled out for more than 'timeout', we still check for DONE
|
||||
* before giving up and we apply 8 extra CCLK cycles in all cases.
|
||||
*/
|
||||
while (!expired) {
|
||||
expired = time_after(jiffies, timeout);
|
||||
|
||||
done = get_done_gpio(mgr);
|
||||
if (done < 0)
|
||||
return done;
|
||||
|
||||
ret = xilinx_spi_apply_cclk_cycles(conf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (done)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (conf->init_b) {
|
||||
ret = gpiod_get_value(conf->init_b);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&mgr->dev, "Error reading INIT_B (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_err(&mgr->dev,
|
||||
ret ? "CRC error or invalid device\n"
|
||||
: "Missing sync word or incomplete bitstream\n");
|
||||
} else {
|
||||
dev_err(&mgr->dev, "Timeout after config data transfer\n");
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static const struct fpga_manager_ops xilinx_spi_ops = {
|
||||
.state = xilinx_spi_state,
|
||||
.write_init = xilinx_spi_write_init,
|
||||
.write = xilinx_spi_write,
|
||||
.write_complete = xilinx_spi_write_complete,
|
||||
};
|
||||
|
||||
static int xilinx_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct xilinx_spi_conf *conf;
|
||||
struct fpga_manager *mgr;
|
||||
struct xilinx_fpga_core *core;
|
||||
|
||||
conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL);
|
||||
if (!conf)
|
||||
core = devm_kzalloc(&spi->dev, sizeof(*core), GFP_KERNEL);
|
||||
if (!core)
|
||||
return -ENOMEM;
|
||||
|
||||
conf->spi = spi;
|
||||
core->dev = &spi->dev;
|
||||
core->write = xilinx_spi_write;
|
||||
|
||||
/* PROGRAM_B is active low */
|
||||
conf->prog_b = devm_gpiod_get(&spi->dev, "prog_b", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(conf->prog_b))
|
||||
return dev_err_probe(&spi->dev, PTR_ERR(conf->prog_b),
|
||||
"Failed to get PROGRAM_B gpio\n");
|
||||
|
||||
conf->init_b = devm_gpiod_get_optional(&spi->dev, "init-b", GPIOD_IN);
|
||||
if (IS_ERR(conf->init_b))
|
||||
return dev_err_probe(&spi->dev, PTR_ERR(conf->init_b),
|
||||
"Failed to get INIT_B gpio\n");
|
||||
|
||||
conf->done = devm_gpiod_get(&spi->dev, "done", GPIOD_IN);
|
||||
if (IS_ERR(conf->done))
|
||||
return dev_err_probe(&spi->dev, PTR_ERR(conf->done),
|
||||
"Failed to get DONE gpio\n");
|
||||
|
||||
mgr = devm_fpga_mgr_register(&spi->dev,
|
||||
"Xilinx Slave Serial FPGA Manager",
|
||||
&xilinx_spi_ops, conf);
|
||||
return PTR_ERR_OR_ZERO(mgr);
|
||||
return xilinx_core_probe(core);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id xlnx_spi_of_match[] = {
|
||||
{ .compatible = "xlnx,fpga-slave-serial", },
|
||||
{
|
||||
.compatible = "xlnx,fpga-slave-serial",
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, xlnx_spi_of_match);
|
||||
|
@ -693,6 +693,7 @@ static void gb_interface_release(struct device *dev)
|
||||
|
||||
trace_gb_interface_release(intf);
|
||||
|
||||
cancel_work_sync(&intf->mode_switch_work);
|
||||
kfree(intf);
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ hv_vmbus-y := vmbus_drv.o \
|
||||
hv.o connection.o channel.o \
|
||||
channel_mgmt.o ring_buffer.o hv_trace.o
|
||||
hv_vmbus-$(CONFIG_HYPERV_TESTING) += hv_debugfs.o
|
||||
hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o hv_utils_transport.o
|
||||
hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_utils_transport.o
|
||||
|
||||
# Code that must be built-in
|
||||
obj-$(subst m,y,$(CONFIG_HYPERV)) += hv_common.o
|
||||
|
@ -120,7 +120,9 @@ const struct vmbus_device vmbus_devs[] = {
|
||||
},
|
||||
|
||||
/* File copy */
|
||||
{ .dev_type = HV_FCOPY,
|
||||
/* fcopy always uses 16KB ring buffer size and is working well for last many years */
|
||||
{ .pref_ring_size = 0x4000,
|
||||
.dev_type = HV_FCOPY,
|
||||
HV_FCOPY_GUID,
|
||||
.perf_device = false,
|
||||
.allowed_in_isolated = false,
|
||||
@ -140,12 +142,19 @@ const struct vmbus_device vmbus_devs[] = {
|
||||
.allowed_in_isolated = false,
|
||||
},
|
||||
|
||||
/* Unknown GUID */
|
||||
{ .dev_type = HV_UNKNOWN,
|
||||
/*
|
||||
* Unknown GUID
|
||||
* 64 KB ring buffer + 4 KB header should be sufficient size for any Hyper-V device apart
|
||||
* from HV_NIC and HV_SCSI. This case avoid the fallback for unknown devices to allocate
|
||||
* much bigger (2 MB) of ring size.
|
||||
*/
|
||||
{ .pref_ring_size = 0x11000,
|
||||
.dev_type = HV_UNKNOWN,
|
||||
.perf_device = false,
|
||||
.allowed_in_isolated = false,
|
||||
},
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(vmbus_devs);
|
||||
|
||||
static const struct {
|
||||
guid_t guid;
|
||||
|
@ -1,427 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* An implementation of file copy service.
|
||||
*
|
||||
* Copyright (C) 2014, Microsoft, Inc.
|
||||
*
|
||||
* Author : K. Y. Srinivasan <ksrinivasan@novell.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/nls.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/hyperv.h>
|
||||
#include <linux/sched.h>
|
||||
#include <asm/hyperv-tlfs.h>
|
||||
|
||||
#include "hyperv_vmbus.h"
|
||||
#include "hv_utils_transport.h"
|
||||
|
||||
#define WIN8_SRV_MAJOR 1
|
||||
#define WIN8_SRV_MINOR 1
|
||||
#define WIN8_SRV_VERSION (WIN8_SRV_MAJOR << 16 | WIN8_SRV_MINOR)
|
||||
|
||||
#define FCOPY_VER_COUNT 1
|
||||
static const int fcopy_versions[] = {
|
||||
WIN8_SRV_VERSION
|
||||
};
|
||||
|
||||
#define FW_VER_COUNT 1
|
||||
static const int fw_versions[] = {
|
||||
UTIL_FW_VERSION
|
||||
};
|
||||
|
||||
/*
|
||||
* Global state maintained for transaction that is being processed.
|
||||
* For a class of integration services, including the "file copy service",
|
||||
* the specified protocol is a "request/response" protocol which means that
|
||||
* there can only be single outstanding transaction from the host at any
|
||||
* given point in time. We use this to simplify memory management in this
|
||||
* driver - we cache and process only one message at a time.
|
||||
*
|
||||
* While the request/response protocol is guaranteed by the host, we further
|
||||
* ensure this by serializing packet processing in this driver - we do not
|
||||
* read additional packets from the VMBUs until the current packet is fully
|
||||
* handled.
|
||||
*/
|
||||
|
||||
static struct {
|
||||
int state; /* hvutil_device_state */
|
||||
int recv_len; /* number of bytes received. */
|
||||
struct hv_fcopy_hdr *fcopy_msg; /* current message */
|
||||
struct vmbus_channel *recv_channel; /* chn we got the request */
|
||||
u64 recv_req_id; /* request ID. */
|
||||
} fcopy_transaction;
|
||||
|
||||
static void fcopy_respond_to_host(int error);
|
||||
static void fcopy_send_data(struct work_struct *dummy);
|
||||
static void fcopy_timeout_func(struct work_struct *dummy);
|
||||
static DECLARE_DELAYED_WORK(fcopy_timeout_work, fcopy_timeout_func);
|
||||
static DECLARE_WORK(fcopy_send_work, fcopy_send_data);
|
||||
static const char fcopy_devname[] = "vmbus/hv_fcopy";
|
||||
static u8 *recv_buffer;
|
||||
static struct hvutil_transport *hvt;
|
||||
/*
|
||||
* This state maintains the version number registered by the daemon.
|
||||
*/
|
||||
static int dm_reg_value;
|
||||
|
||||
static void fcopy_poll_wrapper(void *channel)
|
||||
{
|
||||
/* Transaction is finished, reset the state here to avoid races. */
|
||||
fcopy_transaction.state = HVUTIL_READY;
|
||||
tasklet_schedule(&((struct vmbus_channel *)channel)->callback_event);
|
||||
}
|
||||
|
||||
static void fcopy_timeout_func(struct work_struct *dummy)
|
||||
{
|
||||
/*
|
||||
* If the timer fires, the user-mode component has not responded;
|
||||
* process the pending transaction.
|
||||
*/
|
||||
fcopy_respond_to_host(HV_E_FAIL);
|
||||
hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
|
||||
}
|
||||
|
||||
static void fcopy_register_done(void)
|
||||
{
|
||||
pr_debug("FCP: userspace daemon registered\n");
|
||||
hv_poll_channel(fcopy_transaction.recv_channel, fcopy_poll_wrapper);
|
||||
}
|
||||
|
||||
static int fcopy_handle_handshake(u32 version)
|
||||
{
|
||||
u32 our_ver = FCOPY_CURRENT_VERSION;
|
||||
|
||||
switch (version) {
|
||||
case FCOPY_VERSION_0:
|
||||
/* Daemon doesn't expect us to reply */
|
||||
dm_reg_value = version;
|
||||
break;
|
||||
case FCOPY_VERSION_1:
|
||||
/* Daemon expects us to reply with our own version */
|
||||
if (hvutil_transport_send(hvt, &our_ver, sizeof(our_ver),
|
||||
fcopy_register_done))
|
||||
return -EFAULT;
|
||||
dm_reg_value = version;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* For now we will fail the registration.
|
||||
* If and when we have multiple versions to
|
||||
* deal with, we will be backward compatible.
|
||||
* We will add this code when needed.
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
pr_debug("FCP: userspace daemon ver. %d connected\n", version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fcopy_send_data(struct work_struct *dummy)
|
||||
{
|
||||
struct hv_start_fcopy *smsg_out = NULL;
|
||||
int operation = fcopy_transaction.fcopy_msg->operation;
|
||||
struct hv_start_fcopy *smsg_in;
|
||||
void *out_src;
|
||||
int rc, out_len;
|
||||
|
||||
/*
|
||||
* The strings sent from the host are encoded in
|
||||
* utf16; convert it to utf8 strings.
|
||||
* The host assures us that the utf16 strings will not exceed
|
||||
* the max lengths specified. We will however, reserve room
|
||||
* for the string terminating character - in the utf16s_utf8s()
|
||||
* function we limit the size of the buffer where the converted
|
||||
* string is placed to W_MAX_PATH -1 to guarantee
|
||||
* that the strings can be properly terminated!
|
||||
*/
|
||||
|
||||
switch (operation) {
|
||||
case START_FILE_COPY:
|
||||
out_len = sizeof(struct hv_start_fcopy);
|
||||
smsg_out = kzalloc(sizeof(*smsg_out), GFP_KERNEL);
|
||||
if (!smsg_out)
|
||||
return;
|
||||
|
||||
smsg_out->hdr.operation = operation;
|
||||
smsg_in = (struct hv_start_fcopy *)fcopy_transaction.fcopy_msg;
|
||||
|
||||
utf16s_to_utf8s((wchar_t *)smsg_in->file_name, W_MAX_PATH,
|
||||
UTF16_LITTLE_ENDIAN,
|
||||
(__u8 *)&smsg_out->file_name, W_MAX_PATH - 1);
|
||||
|
||||
utf16s_to_utf8s((wchar_t *)smsg_in->path_name, W_MAX_PATH,
|
||||
UTF16_LITTLE_ENDIAN,
|
||||
(__u8 *)&smsg_out->path_name, W_MAX_PATH - 1);
|
||||
|
||||
smsg_out->copy_flags = smsg_in->copy_flags;
|
||||
smsg_out->file_size = smsg_in->file_size;
|
||||
out_src = smsg_out;
|
||||
break;
|
||||
|
||||
case WRITE_TO_FILE:
|
||||
out_src = fcopy_transaction.fcopy_msg;
|
||||
out_len = sizeof(struct hv_do_fcopy);
|
||||
break;
|
||||
default:
|
||||
out_src = fcopy_transaction.fcopy_msg;
|
||||
out_len = fcopy_transaction.recv_len;
|
||||
break;
|
||||
}
|
||||
|
||||
fcopy_transaction.state = HVUTIL_USERSPACE_REQ;
|
||||
rc = hvutil_transport_send(hvt, out_src, out_len, NULL);
|
||||
if (rc) {
|
||||
pr_debug("FCP: failed to communicate to the daemon: %d\n", rc);
|
||||
if (cancel_delayed_work_sync(&fcopy_timeout_work)) {
|
||||
fcopy_respond_to_host(HV_E_FAIL);
|
||||
fcopy_transaction.state = HVUTIL_READY;
|
||||
}
|
||||
}
|
||||
kfree(smsg_out);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a response back to the host.
|
||||
*/
|
||||
|
||||
static void
|
||||
fcopy_respond_to_host(int error)
|
||||
{
|
||||
struct icmsg_hdr *icmsghdr;
|
||||
u32 buf_len;
|
||||
struct vmbus_channel *channel;
|
||||
u64 req_id;
|
||||
|
||||
/*
|
||||
* Copy the global state for completing the transaction. Note that
|
||||
* only one transaction can be active at a time. This is guaranteed
|
||||
* by the file copy protocol implemented by the host. Furthermore,
|
||||
* the "transaction active" state we maintain ensures that there can
|
||||
* only be one active transaction at a time.
|
||||
*/
|
||||
|
||||
buf_len = fcopy_transaction.recv_len;
|
||||
channel = fcopy_transaction.recv_channel;
|
||||
req_id = fcopy_transaction.recv_req_id;
|
||||
|
||||
icmsghdr = (struct icmsg_hdr *)
|
||||
&recv_buffer[sizeof(struct vmbuspipe_hdr)];
|
||||
|
||||
if (channel->onchannel_callback == NULL)
|
||||
/*
|
||||
* We have raced with util driver being unloaded;
|
||||
* silently return.
|
||||
*/
|
||||
return;
|
||||
|
||||
icmsghdr->status = error;
|
||||
icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
|
||||
vmbus_sendpacket(channel, recv_buffer, buf_len, req_id,
|
||||
VM_PKT_DATA_INBAND, 0);
|
||||
}
|
||||
|
||||
void hv_fcopy_onchannelcallback(void *context)
|
||||
{
|
||||
struct vmbus_channel *channel = context;
|
||||
u32 recvlen;
|
||||
u64 requestid;
|
||||
struct hv_fcopy_hdr *fcopy_msg;
|
||||
struct icmsg_hdr *icmsghdr;
|
||||
int fcopy_srv_version;
|
||||
|
||||
if (fcopy_transaction.state > HVUTIL_READY)
|
||||
return;
|
||||
|
||||
if (vmbus_recvpacket(channel, recv_buffer, HV_HYP_PAGE_SIZE * 2, &recvlen, &requestid)) {
|
||||
pr_err_ratelimited("Fcopy request received. Could not read into recv buf\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!recvlen)
|
||||
return;
|
||||
|
||||
/* Ensure recvlen is big enough to read header data */
|
||||
if (recvlen < ICMSG_HDR) {
|
||||
pr_err_ratelimited("Fcopy request received. Packet length too small: %d\n",
|
||||
recvlen);
|
||||
return;
|
||||
}
|
||||
|
||||
icmsghdr = (struct icmsg_hdr *)&recv_buffer[
|
||||
sizeof(struct vmbuspipe_hdr)];
|
||||
|
||||
if (icmsghdr->icmsgtype == ICMSGTYPE_NEGOTIATE) {
|
||||
if (vmbus_prep_negotiate_resp(icmsghdr,
|
||||
recv_buffer, recvlen,
|
||||
fw_versions, FW_VER_COUNT,
|
||||
fcopy_versions, FCOPY_VER_COUNT,
|
||||
NULL, &fcopy_srv_version)) {
|
||||
|
||||
pr_info("FCopy IC version %d.%d\n",
|
||||
fcopy_srv_version >> 16,
|
||||
fcopy_srv_version & 0xFFFF);
|
||||
}
|
||||
} else if (icmsghdr->icmsgtype == ICMSGTYPE_FCOPY) {
|
||||
/* Ensure recvlen is big enough to contain hv_fcopy_hdr */
|
||||
if (recvlen < ICMSG_HDR + sizeof(struct hv_fcopy_hdr)) {
|
||||
pr_err_ratelimited("Invalid Fcopy hdr. Packet length too small: %u\n",
|
||||
recvlen);
|
||||
return;
|
||||
}
|
||||
fcopy_msg = (struct hv_fcopy_hdr *)&recv_buffer[ICMSG_HDR];
|
||||
|
||||
/*
|
||||
* Stash away this global state for completing the
|
||||
* transaction; note transactions are serialized.
|
||||
*/
|
||||
|
||||
fcopy_transaction.recv_len = recvlen;
|
||||
fcopy_transaction.recv_req_id = requestid;
|
||||
fcopy_transaction.fcopy_msg = fcopy_msg;
|
||||
|
||||
if (fcopy_transaction.state < HVUTIL_READY) {
|
||||
/* Userspace is not registered yet */
|
||||
fcopy_respond_to_host(HV_E_FAIL);
|
||||
return;
|
||||
}
|
||||
fcopy_transaction.state = HVUTIL_HOSTMSG_RECEIVED;
|
||||
|
||||
/*
|
||||
* Send the information to the user-level daemon.
|
||||
*/
|
||||
schedule_work(&fcopy_send_work);
|
||||
schedule_delayed_work(&fcopy_timeout_work,
|
||||
HV_UTIL_TIMEOUT * HZ);
|
||||
return;
|
||||
} else {
|
||||
pr_err_ratelimited("Fcopy request received. Invalid msg type: %d\n",
|
||||
icmsghdr->icmsgtype);
|
||||
return;
|
||||
}
|
||||
icmsghdr->icflags = ICMSGHDRFLAG_TRANSACTION | ICMSGHDRFLAG_RESPONSE;
|
||||
vmbus_sendpacket(channel, recv_buffer, recvlen, requestid,
|
||||
VM_PKT_DATA_INBAND, 0);
|
||||
}
|
||||
|
||||
/* Callback when data is received from userspace */
|
||||
static int fcopy_on_msg(void *msg, int len)
|
||||
{
|
||||
int *val = (int *)msg;
|
||||
|
||||
if (len != sizeof(int))
|
||||
return -EINVAL;
|
||||
|
||||
if (fcopy_transaction.state == HVUTIL_DEVICE_INIT)
|
||||
return fcopy_handle_handshake(*val);
|
||||
|
||||
if (fcopy_transaction.state != HVUTIL_USERSPACE_REQ)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Complete the transaction by forwarding the result
|
||||
* to the host. But first, cancel the timeout.
|
||||
*/
|
||||
if (cancel_delayed_work_sync(&fcopy_timeout_work)) {
|
||||
fcopy_transaction.state = HVUTIL_USERSPACE_RECV;
|
||||
fcopy_respond_to_host(*val);
|
||||
hv_poll_channel(fcopy_transaction.recv_channel,
|
||||
fcopy_poll_wrapper);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fcopy_on_reset(void)
|
||||
{
|
||||
/*
|
||||
* The daemon has exited; reset the state.
|
||||
*/
|
||||
fcopy_transaction.state = HVUTIL_DEVICE_INIT;
|
||||
|
||||
if (cancel_delayed_work_sync(&fcopy_timeout_work))
|
||||
fcopy_respond_to_host(HV_E_FAIL);
|
||||
}
|
||||
|
||||
int hv_fcopy_init(struct hv_util_service *srv)
|
||||
{
|
||||
recv_buffer = srv->recv_buffer;
|
||||
fcopy_transaction.recv_channel = srv->channel;
|
||||
fcopy_transaction.recv_channel->max_pkt_size = HV_HYP_PAGE_SIZE * 2;
|
||||
|
||||
/*
|
||||
* When this driver loads, the user level daemon that
|
||||
* processes the host requests may not yet be running.
|
||||
* Defer processing channel callbacks until the daemon
|
||||
* has registered.
|
||||
*/
|
||||
fcopy_transaction.state = HVUTIL_DEVICE_INIT;
|
||||
|
||||
hvt = hvutil_transport_init(fcopy_devname, 0, 0,
|
||||
fcopy_on_msg, fcopy_on_reset);
|
||||
if (!hvt)
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hv_fcopy_cancel_work(void)
|
||||
{
|
||||
cancel_delayed_work_sync(&fcopy_timeout_work);
|
||||
cancel_work_sync(&fcopy_send_work);
|
||||
}
|
||||
|
||||
int hv_fcopy_pre_suspend(void)
|
||||
{
|
||||
struct vmbus_channel *channel = fcopy_transaction.recv_channel;
|
||||
struct hv_fcopy_hdr *fcopy_msg;
|
||||
|
||||
/*
|
||||
* Fake a CANCEL_FCOPY message for the user space daemon in case the
|
||||
* daemon is in the middle of copying some file. It doesn't matter if
|
||||
* there is already a message pending to be delivered to the user
|
||||
* space since we force fcopy_transaction.state to be HVUTIL_READY, so
|
||||
* the user space daemon's write() will fail with EINVAL (see
|
||||
* fcopy_on_msg()), and the daemon will reset the device by closing
|
||||
* and re-opening it.
|
||||
*/
|
||||
fcopy_msg = kzalloc(sizeof(*fcopy_msg), GFP_KERNEL);
|
||||
if (!fcopy_msg)
|
||||
return -ENOMEM;
|
||||
|
||||
tasklet_disable(&channel->callback_event);
|
||||
|
||||
fcopy_msg->operation = CANCEL_FCOPY;
|
||||
|
||||
hv_fcopy_cancel_work();
|
||||
|
||||
/* We don't care about the return value. */
|
||||
hvutil_transport_send(hvt, fcopy_msg, sizeof(*fcopy_msg), NULL);
|
||||
|
||||
kfree(fcopy_msg);
|
||||
|
||||
fcopy_transaction.state = HVUTIL_READY;
|
||||
|
||||
/* tasklet_enable() will be called in hv_fcopy_pre_resume(). */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hv_fcopy_pre_resume(void)
|
||||
{
|
||||
struct vmbus_channel *channel = fcopy_transaction.recv_channel;
|
||||
|
||||
tasklet_enable(&channel->callback_event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hv_fcopy_deinit(void)
|
||||
{
|
||||
fcopy_transaction.state = HVUTIL_DEVICE_DYING;
|
||||
|
||||
hv_fcopy_cancel_work();
|
||||
|
||||
hvutil_transport_destroy(hvt);
|
||||
}
|
@ -154,14 +154,6 @@ static struct hv_util_service util_vss = {
|
||||
.util_deinit = hv_vss_deinit,
|
||||
};
|
||||
|
||||
static struct hv_util_service util_fcopy = {
|
||||
.util_cb = hv_fcopy_onchannelcallback,
|
||||
.util_init = hv_fcopy_init,
|
||||
.util_pre_suspend = hv_fcopy_pre_suspend,
|
||||
.util_pre_resume = hv_fcopy_pre_resume,
|
||||
.util_deinit = hv_fcopy_deinit,
|
||||
};
|
||||
|
||||
static void perform_shutdown(struct work_struct *dummy)
|
||||
{
|
||||
orderly_poweroff(true);
|
||||
@ -700,10 +692,6 @@ static const struct hv_vmbus_device_id id_table[] = {
|
||||
{ HV_VSS_GUID,
|
||||
.driver_data = (unsigned long)&util_vss
|
||||
},
|
||||
/* File copy GUID */
|
||||
{ HV_FCOPY_GUID,
|
||||
.driver_data = (unsigned long)&util_fcopy
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
|
@ -417,6 +417,11 @@ static inline bool hv_is_perf_channel(struct vmbus_channel *channel)
|
||||
return vmbus_devs[channel->device_id].perf_device;
|
||||
}
|
||||
|
||||
static inline size_t hv_dev_ring_size(struct vmbus_channel *channel)
|
||||
{
|
||||
return vmbus_devs[channel->device_id].pref_ring_size;
|
||||
}
|
||||
|
||||
static inline bool hv_is_allocated_cpu(unsigned int cpu)
|
||||
{
|
||||
struct vmbus_channel *channel, *sc;
|
||||
|
@ -7,11 +7,13 @@
|
||||
* Author: Suzuki K Poulose <suzuki.poulose@arm.com>
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "coresight-catu.h"
|
||||
@ -502,28 +504,20 @@ static const struct coresight_ops catu_ops = {
|
||||
.helper_ops = &catu_helper_ops,
|
||||
};
|
||||
|
||||
static int catu_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
static int __catu_probe(struct device *dev, struct resource *res)
|
||||
{
|
||||
int ret = 0;
|
||||
u32 dma_mask;
|
||||
struct catu_drvdata *drvdata;
|
||||
struct catu_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
struct coresight_desc catu_desc;
|
||||
struct coresight_platform_data *pdata = NULL;
|
||||
struct device *dev = &adev->dev;
|
||||
void __iomem *base;
|
||||
|
||||
catu_desc.name = coresight_alloc_device_name(&catu_devs, dev);
|
||||
if (!catu_desc.name)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, drvdata);
|
||||
base = devm_ioremap_resource(dev, &adev->res);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base)) {
|
||||
ret = PTR_ERR(base);
|
||||
goto out;
|
||||
@ -567,19 +561,39 @@ static int catu_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
drvdata->csdev = coresight_register(&catu_desc);
|
||||
if (IS_ERR(drvdata->csdev))
|
||||
ret = PTR_ERR(drvdata->csdev);
|
||||
else
|
||||
pm_runtime_put(&adev->dev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void catu_remove(struct amba_device *adev)
|
||||
static int catu_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
struct catu_drvdata *drvdata = dev_get_drvdata(&adev->dev);
|
||||
struct catu_drvdata *drvdata;
|
||||
int ret;
|
||||
|
||||
drvdata = devm_kzalloc(&adev->dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
amba_set_drvdata(adev, drvdata);
|
||||
ret = __catu_probe(&adev->dev, &adev->res);
|
||||
if (!ret)
|
||||
pm_runtime_put(&adev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __catu_remove(struct device *dev)
|
||||
{
|
||||
struct catu_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
coresight_unregister(drvdata->csdev);
|
||||
}
|
||||
|
||||
static void catu_remove(struct amba_device *adev)
|
||||
{
|
||||
__catu_remove(&adev->dev);
|
||||
}
|
||||
|
||||
static struct amba_id catu_ids[] = {
|
||||
CS_AMBA_ID(0x000bb9ee),
|
||||
{},
|
||||
@ -597,13 +611,98 @@ static struct amba_driver catu_driver = {
|
||||
.id_table = catu_ids,
|
||||
};
|
||||
|
||||
static int catu_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
struct catu_drvdata *drvdata;
|
||||
int ret = 0;
|
||||
|
||||
drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->pclk = coresight_get_enable_apb_pclk(&pdev->dev);
|
||||
if (IS_ERR(drvdata->pclk))
|
||||
return -ENODEV;
|
||||
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
dev_set_drvdata(&pdev->dev, drvdata);
|
||||
ret = __catu_probe(&pdev->dev, res);
|
||||
pm_runtime_put(&pdev->dev);
|
||||
if (ret) {
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!IS_ERR_OR_NULL(drvdata->pclk))
|
||||
clk_put(drvdata->pclk);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void catu_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct catu_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (WARN_ON(!drvdata))
|
||||
return;
|
||||
|
||||
__catu_remove(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!IS_ERR_OR_NULL(drvdata->pclk))
|
||||
clk_put(drvdata->pclk);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int catu_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct catu_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
|
||||
clk_disable_unprepare(drvdata->pclk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int catu_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct catu_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
|
||||
clk_prepare_enable(drvdata->pclk);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops catu_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(catu_runtime_suspend, catu_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id catu_acpi_ids[] = {
|
||||
{"ARMHC9CA", 0, 0, 0}, /* ARM CoreSight CATU */
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, catu_acpi_ids);
|
||||
#endif
|
||||
|
||||
static struct platform_driver catu_platform_driver = {
|
||||
.probe = catu_platform_probe,
|
||||
.remove_new = catu_platform_remove,
|
||||
.driver = {
|
||||
.name = "coresight-catu-platform",
|
||||
.acpi_match_table = ACPI_PTR(catu_acpi_ids),
|
||||
.suppress_bind_attrs = true,
|
||||
.pm = &catu_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init catu_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = amba_driver_register(&catu_driver);
|
||||
if (ret)
|
||||
pr_info("Error registering catu driver\n");
|
||||
ret = coresight_init_driver("catu", &catu_driver, &catu_platform_driver);
|
||||
tmc_etr_set_catu_ops(&etr_catu_buf_ops);
|
||||
return ret;
|
||||
}
|
||||
@ -611,7 +710,7 @@ static int __init catu_init(void)
|
||||
static void __exit catu_exit(void)
|
||||
{
|
||||
tmc_etr_remove_catu_ops();
|
||||
amba_driver_unregister(&catu_driver);
|
||||
coresight_remove_driver(&catu_driver, &catu_platform_driver);
|
||||
}
|
||||
|
||||
module_init(catu_init);
|
||||
|
@ -61,6 +61,7 @@
|
||||
#define CATU_IRQEN_OFF 0x0
|
||||
|
||||
struct catu_drvdata {
|
||||
struct clk *pclk;
|
||||
void __iomem *base;
|
||||
struct coresight_device *csdev;
|
||||
int irq;
|
||||
|
@ -1398,6 +1398,35 @@ static void __exit coresight_exit(void)
|
||||
module_init(coresight_init);
|
||||
module_exit(coresight_exit);
|
||||
|
||||
int coresight_init_driver(const char *drv, struct amba_driver *amba_drv,
|
||||
struct platform_driver *pdev_drv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = amba_driver_register(amba_drv);
|
||||
if (ret) {
|
||||
pr_err("%s: error registering AMBA driver\n", drv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = platform_driver_register(pdev_drv);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
pr_err("%s: error registering platform driver\n", drv);
|
||||
amba_driver_unregister(amba_drv);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_init_driver);
|
||||
|
||||
void coresight_remove_driver(struct amba_driver *amba_drv,
|
||||
struct platform_driver *pdev_drv)
|
||||
{
|
||||
amba_driver_unregister(amba_drv);
|
||||
platform_driver_unregister(pdev_drv);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(coresight_remove_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
|
||||
MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
|
||||
|
@ -4,6 +4,7 @@
|
||||
*
|
||||
* Author: Leo Yan <leo.yan@linaro.org>
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/amba/bus.h>
|
||||
#include <linux/coresight.h>
|
||||
#include <linux/cpu.h>
|
||||
@ -18,6 +19,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/panic_notifier.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/smp.h>
|
||||
@ -84,6 +86,7 @@
|
||||
#define DEBUG_WAIT_TIMEOUT 32000
|
||||
|
||||
struct debug_drvdata {
|
||||
struct clk *pclk;
|
||||
void __iomem *base;
|
||||
struct device *dev;
|
||||
int cpu;
|
||||
@ -557,18 +560,12 @@ static void debug_func_exit(void)
|
||||
debugfs_remove_recursive(debug_debugfs_dir);
|
||||
}
|
||||
|
||||
static int debug_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
static int __debug_probe(struct device *dev, struct resource *res)
|
||||
{
|
||||
struct debug_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
void __iomem *base;
|
||||
struct device *dev = &adev->dev;
|
||||
struct debug_drvdata *drvdata;
|
||||
struct resource *res = &adev->res;
|
||||
int ret;
|
||||
|
||||
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->cpu = coresight_get_cpu(dev);
|
||||
if (drvdata->cpu < 0)
|
||||
return drvdata->cpu;
|
||||
@ -579,10 +576,7 @@ static int debug_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
drvdata->dev = &adev->dev;
|
||||
amba_set_drvdata(adev, drvdata);
|
||||
|
||||
/* Validity for the resource is already checked by the AMBA core */
|
||||
drvdata->dev = dev;
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
@ -629,10 +623,21 @@ static int debug_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void debug_remove(struct amba_device *adev)
|
||||
static int debug_probe(struct amba_device *adev, const struct amba_id *id)
|
||||
{
|
||||
struct device *dev = &adev->dev;
|
||||
struct debug_drvdata *drvdata = amba_get_drvdata(adev);
|
||||
struct debug_drvdata *drvdata;
|
||||
|
||||
drvdata = devm_kzalloc(&adev->dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
amba_set_drvdata(adev, drvdata);
|
||||
return __debug_probe(&adev->dev, &adev->res);
|
||||
}
|
||||
|
||||
static void __debug_remove(struct device *dev)
|
||||
{
|
||||
struct debug_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
per_cpu(debug_drvdata, drvdata->cpu) = NULL;
|
||||
|
||||
@ -646,6 +651,11 @@ static void debug_remove(struct amba_device *adev)
|
||||
debug_func_exit();
|
||||
}
|
||||
|
||||
static void debug_remove(struct amba_device *adev)
|
||||
{
|
||||
__debug_remove(&adev->dev);
|
||||
}
|
||||
|
||||
static const struct amba_cs_uci_id uci_id_debug[] = {
|
||||
{
|
||||
/* CPU Debug UCI data */
|
||||
@ -677,7 +687,102 @@ static struct amba_driver debug_driver = {
|
||||
.id_table = debug_ids,
|
||||
};
|
||||
|
||||
module_amba_driver(debug_driver);
|
||||
static int debug_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
struct debug_drvdata *drvdata;
|
||||
int ret = 0;
|
||||
|
||||
drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
|
||||
if (!drvdata)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->pclk = coresight_get_enable_apb_pclk(&pdev->dev);
|
||||
if (IS_ERR(drvdata->pclk))
|
||||
return -ENODEV;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, drvdata);
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
ret = __debug_probe(&pdev->dev, res);
|
||||
if (ret) {
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!IS_ERR_OR_NULL(drvdata->pclk))
|
||||
clk_put(drvdata->pclk);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void debug_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct debug_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (WARN_ON(!drvdata))
|
||||
return;
|
||||
|
||||
__debug_remove(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!IS_ERR_OR_NULL(drvdata->pclk))
|
||||
clk_put(drvdata->pclk);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id debug_platform_ids[] = {
|
||||
{"ARMHC503", 0, 0, 0}, /* ARM CoreSight Debug */
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, debug_platform_ids);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int debug_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct debug_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
|
||||
clk_disable_unprepare(drvdata->pclk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int debug_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct debug_drvdata *drvdata = dev_get_drvdata(dev);
|
||||
|
||||
if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
|
||||
clk_prepare_enable(drvdata->pclk);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops debug_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(debug_runtime_suspend, debug_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver debug_platform_driver = {
|
||||
.probe = debug_platform_probe,
|
||||
.remove_new = debug_platform_remove,
|
||||
.driver = {
|
||||
.name = "coresight-debug-platform",
|
||||
.acpi_match_table = ACPI_PTR(debug_platform_ids),
|
||||
.suppress_bind_attrs = true,
|
||||
.pm = &debug_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init debug_init(void)
|
||||
{
|
||||
return coresight_init_driver("debug", &debug_driver, &debug_platform_driver);
|
||||
}
|
||||
|
||||
static void __exit debug_exit(void)
|
||||
{
|
||||
coresight_remove_driver(&debug_driver, &debug_platform_driver);
|
||||
}
|
||||
module_init(debug_init);
|
||||
module_exit(debug_exit);
|
||||
|
||||
MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");
|
||||
MODULE_DESCRIPTION("ARM Coresight CPU Debug Driver");
|
||||
|
@ -1240,6 +1240,8 @@ static void etm4_init_arch_data(void *info)
|
||||
drvdata->nr_event = FIELD_GET(TRCIDR0_NUMEVENT_MASK, etmidr0);
|
||||
/* QSUPP, bits[16:15] Q element support field */
|
||||
drvdata->q_support = FIELD_GET(TRCIDR0_QSUPP_MASK, etmidr0);
|
||||
if (drvdata->q_support)
|
||||
drvdata->q_filt = !!(etmidr0 & TRCIDR0_QFILT);
|
||||
/* TSSIZE, bits[28:24] Global timestamp size field */
|
||||
drvdata->ts_size = FIELD_GET(TRCIDR0_TSSIZE_MASK, etmidr0);
|
||||
|
||||
@ -1732,16 +1734,14 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
|
||||
state->trcccctlr = etm4x_read32(csa, TRCCCCTLR);
|
||||
state->trcbbctlr = etm4x_read32(csa, TRCBBCTLR);
|
||||
state->trctraceidr = etm4x_read32(csa, TRCTRACEIDR);
|
||||
state->trcqctlr = etm4x_read32(csa, TRCQCTLR);
|
||||
if (drvdata->q_filt)
|
||||
state->trcqctlr = etm4x_read32(csa, TRCQCTLR);
|
||||
|
||||
state->trcvictlr = etm4x_read32(csa, TRCVICTLR);
|
||||
state->trcviiectlr = etm4x_read32(csa, TRCVIIECTLR);
|
||||
state->trcvissctlr = etm4x_read32(csa, TRCVISSCTLR);
|
||||
if (drvdata->nr_pe_cmp)
|
||||
state->trcvipcssctlr = etm4x_read32(csa, TRCVIPCSSCTLR);
|
||||
state->trcvdctlr = etm4x_read32(csa, TRCVDCTLR);
|
||||
state->trcvdsacctlr = etm4x_read32(csa, TRCVDSACCTLR);
|
||||
state->trcvdarcctlr = etm4x_read32(csa, TRCVDARCCTLR);
|
||||
|
||||
for (i = 0; i < drvdata->nrseqstate - 1; i++)
|
||||
state->trcseqevr[i] = etm4x_read32(csa, TRCSEQEVRn(i));
|
||||
@ -1758,7 +1758,8 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
|
||||
state->trccntvr[i] = etm4x_read32(csa, TRCCNTVRn(i));
|
||||
}
|
||||
|
||||
for (i = 0; i < drvdata->nr_resource * 2; i++)
|
||||
/* Resource selector pair 0 is reserved */
|
||||
for (i = 2; i < drvdata->nr_resource * 2; i++)
|
||||
state->trcrsctlr[i] = etm4x_read32(csa, TRCRSCTLRn(i));
|
||||
|
||||
for (i = 0; i < drvdata->nr_ss_cmp; i++) {
|
||||
@ -1843,8 +1844,10 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
|
||||
{
|
||||
int i;
|
||||
struct etmv4_save_state *state = drvdata->save_state;
|
||||
struct csdev_access tmp_csa = CSDEV_ACCESS_IOMEM(drvdata->base);
|
||||
struct csdev_access *csa = &tmp_csa;
|
||||
struct csdev_access *csa = &drvdata->csdev->access;
|
||||
|
||||
if (WARN_ON(!drvdata->csdev))
|
||||
return;
|
||||
|
||||
etm4_cs_unlock(drvdata, csa);
|
||||
etm4x_relaxed_write32(csa, state->trcclaimset, TRCCLAIMSET);
|
||||
@ -1863,16 +1866,14 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
|
||||
etm4x_relaxed_write32(csa, state->trcccctlr, TRCCCCTLR);
|
||||
etm4x_relaxed_write32(csa, state->trcbbctlr, TRCBBCTLR);
|
||||
etm4x_relaxed_write32(csa, state->trctraceidr, TRCTRACEIDR);
|
||||
etm4x_relaxed_write32(csa, state->trcqctlr, TRCQCTLR);
|
||||
if (drvdata->q_filt)
|
||||
etm4x_relaxed_write32(csa, state->trcqctlr, TRCQCTLR);
|
||||
|
||||
etm4x_relaxed_write32(csa, state->trcvictlr, TRCVICTLR);
|
||||
etm4x_relaxed_write32(csa, state->trcviiectlr, TRCVIIECTLR);
|
||||
etm4x_relaxed_write32(csa, state->trcvissctlr, TRCVISSCTLR);
|
||||
if (drvdata->nr_pe_cmp)
|
||||
etm4x_relaxed_write32(csa, state->trcvipcssctlr, TRCVIPCSSCTLR);
|
||||
etm4x_relaxed_write32(csa, state->trcvdctlr, TRCVDCTLR);
|
||||
etm4x_relaxed_write32(csa, state->trcvdsacctlr, TRCVDSACCTLR);
|
||||
etm4x_relaxed_write32(csa, state->trcvdarcctlr, TRCVDARCCTLR);
|
||||
|
||||
for (i = 0; i < drvdata->nrseqstate - 1; i++)
|
||||
etm4x_relaxed_write32(csa, state->trcseqevr[i], TRCSEQEVRn(i));
|
||||
@ -1889,7 +1890,8 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
|
||||
etm4x_relaxed_write32(csa, state->trccntvr[i], TRCCNTVRn(i));
|
||||
}
|
||||
|
||||
for (i = 0; i < drvdata->nr_resource * 2; i++)
|
||||
/* Resource selector pair 0 is reserved */
|
||||
for (i = 2; i < drvdata->nr_resource * 2; i++)
|
||||
etm4x_relaxed_write32(csa, state->trcrsctlr[i], TRCRSCTLRn(i));
|
||||
|
||||
for (i = 0; i < drvdata->nr_ss_cmp; i++) {
|
||||
@ -2213,6 +2215,9 @@ static int etm4_probe_platform_dev(struct platform_device *pdev)
|
||||
ret = etm4_probe(&pdev->dev);
|
||||
|
||||
pm_runtime_put(&pdev->dev);
|
||||
if (ret)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -43,9 +43,6 @@
|
||||
#define TRCVIIECTLR 0x084
|
||||
#define TRCVISSCTLR 0x088
|
||||
#define TRCVIPCSSCTLR 0x08C
|
||||
#define TRCVDCTLR 0x0A0
|
||||
#define TRCVDSACCTLR 0x0A4
|
||||
#define TRCVDARCCTLR 0x0A8
|
||||
/* Derived resources registers */
|
||||
#define TRCSEQEVRn(n) (0x100 + (n * 4)) /* n = 0-2 */
|
||||
#define TRCSEQRSTEVR 0x118
|
||||
@ -90,9 +87,6 @@
|
||||
/* Address Comparator registers n = 0-15 */
|
||||
#define TRCACVRn(n) (0x400 + (n * 8))
|
||||
#define TRCACATRn(n) (0x480 + (n * 8))
|
||||
/* Data Value Comparator Value registers, n = 0-7 */
|
||||
#define TRCDVCVRn(n) (0x500 + (n * 16))
|
||||
#define TRCDVCMRn(n) (0x580 + (n * 16))
|
||||
/* ContextID/Virtual ContextID comparators, n = 0-7 */
|
||||
#define TRCCIDCVRn(n) (0x600 + (n * 8))
|
||||
#define TRCVMIDCVRn(n) (0x640 + (n * 8))
|
||||
@ -141,6 +135,7 @@
|
||||
#define TRCIDR0_TRCCCI BIT(7)
|
||||
#define TRCIDR0_RETSTACK BIT(9)
|
||||
#define TRCIDR0_NUMEVENT_MASK GENMASK(11, 10)
|
||||
#define TRCIDR0_QFILT BIT(14)
|
||||
#define TRCIDR0_QSUPP_MASK GENMASK(16, 15)
|
||||
#define TRCIDR0_TSSIZE_MASK GENMASK(28, 24)
|
||||
|
||||
@ -272,9 +267,6 @@
|
||||
/* List of registers accessible via System instructions */
|
||||
#define ETM4x_ONLY_SYSREG_LIST(op, val) \
|
||||
CASE_##op((val), TRCPROCSELR) \
|
||||
CASE_##op((val), TRCVDCTLR) \
|
||||
CASE_##op((val), TRCVDSACCTLR) \
|
||||
CASE_##op((val), TRCVDARCCTLR) \
|
||||
CASE_##op((val), TRCOSLAR)
|
||||
|
||||
#define ETM_COMMON_SYSREG_LIST(op, val) \
|
||||
@ -422,22 +414,6 @@
|
||||
CASE_##op((val), TRCACATRn(13)) \
|
||||
CASE_##op((val), TRCACATRn(14)) \
|
||||
CASE_##op((val), TRCACATRn(15)) \
|
||||
CASE_##op((val), TRCDVCVRn(0)) \
|
||||
CASE_##op((val), TRCDVCVRn(1)) \
|
||||
CASE_##op((val), TRCDVCVRn(2)) \
|
||||
CASE_##op((val), TRCDVCVRn(3)) \
|
||||
CASE_##op((val), TRCDVCVRn(4)) \
|
||||
CASE_##op((val), TRCDVCVRn(5)) \
|
||||
CASE_##op((val), TRCDVCVRn(6)) \
|
||||
CASE_##op((val), TRCDVCVRn(7)) \
|
||||
CASE_##op((val), TRCDVCMRn(0)) \
|
||||
CASE_##op((val), TRCDVCMRn(1)) \
|
||||
CASE_##op((val), TRCDVCMRn(2)) \
|
||||
CASE_##op((val), TRCDVCMRn(3)) \
|
||||
CASE_##op((val), TRCDVCMRn(4)) \
|
||||
CASE_##op((val), TRCDVCMRn(5)) \
|
||||
CASE_##op((val), TRCDVCMRn(6)) \
|
||||
CASE_##op((val), TRCDVCMRn(7)) \
|
||||
CASE_##op((val), TRCCIDCVRn(0)) \
|
||||
CASE_##op((val), TRCCIDCVRn(1)) \
|
||||
CASE_##op((val), TRCCIDCVRn(2)) \
|
||||
@ -907,9 +883,6 @@ struct etmv4_save_state {
|
||||
u32 trcviiectlr;
|
||||
u32 trcvissctlr;
|
||||
u32 trcvipcssctlr;
|
||||
u32 trcvdctlr;
|
||||
u32 trcvdsacctlr;
|
||||
u32 trcvdarcctlr;
|
||||
|
||||
u32 trcseqevr[ETM_MAX_SEQ_STATES];
|
||||
u32 trcseqrstevr;
|
||||
@ -982,6 +955,7 @@ struct etmv4_save_state {
|
||||
* @os_unlock: True if access to management registers is allowed.
|
||||
* @instrp0: Tracing of load and store instructions
|
||||
* as P0 elements is supported.
|
||||
* @q_filt: Q element filtering support, if Q elements are supported.
|
||||
* @trcbb: Indicates if the trace unit supports branch broadcast tracing.
|
||||
* @trccond: If the trace unit supports conditional
|
||||
* instruction tracing.
|
||||
@ -1044,6 +1018,7 @@ struct etmv4_drvdata {
|
||||
bool boot_enable;
|
||||
bool os_unlock;
|
||||
bool instrp0;
|
||||
bool q_filt;
|
||||
bool trcbb;
|
||||
bool trccond;
|
||||
bool retstack;
|
||||
|
@ -36,6 +36,7 @@ DEFINE_CORESIGHT_DEVLIST(funnel_devs, "funnel");
|
||||
* struct funnel_drvdata - specifics associated to a funnel component
|
||||
* @base: memory mapped base address for this component.
|
||||
* @atclk: optional clock for the core parts of the funnel.
|
||||
* @pclk: APB clock if present, otherwise NULL
|
||||
* @csdev: component vitals needed by the framework.
|
||||
* @priority: port selection order.
|
||||
* @spinlock: serialize enable/disable operations.
|
||||
@ -43,6 +44,7 @@ DEFINE_CORESIGHT_DEVLIST(funnel_devs, "funnel");
|
||||
struct funnel_drvdata {
|
||||
void __iomem *base;
|
||||
struct clk *atclk;
|
||||
struct clk *pclk;
|
||||
struct coresight_device *csdev;
|
||||
unsigned long priority;
|
||||
spinlock_t spinlock;
|
||||
@ -236,6 +238,10 @@ static int funnel_probe(struct device *dev, struct resource *res)
|
||||
return ret;
|
||||
}
|
||||
|
||||
drvdata->pclk = coresight_get_enable_apb_pclk(dev);
|
||||
if (IS_ERR(drvdata->pclk))
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Map the device base for dynamic-funnel, which has been
|
||||
* validated by AMBA core.
|
||||
@ -272,12 +278,13 @@ static int funnel_probe(struct device *dev, struct resource *res)
|
||||
goto out_disable_clk;
|
||||
}
|
||||
|
||||
pm_runtime_put(dev);
|
||||
ret = 0;
|
||||
|
||||
out_disable_clk:
|
||||
if (ret && !IS_ERR_OR_NULL(drvdata->atclk))
|
||||
clk_disable_unprepare(drvdata->atclk);
|
||||
if (ret && !IS_ERR_OR_NULL(drvdata->pclk))
|
||||
clk_disable_unprepare(drvdata->pclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -298,6 +305,9 @@ static int funnel_runtime_suspend(struct device *dev)
|
||||
if (drvdata && !IS_ERR(drvdata->atclk))
|
||||
clk_disable_unprepare(drvdata->atclk);
|
||||
|
||||
if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
|
||||
clk_disable_unprepare(drvdata->pclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -308,6 +318,8 @@ static int funnel_runtime_resume(struct device *dev)
|
||||
if (drvdata && !IS_ERR(drvdata->atclk))
|
||||
clk_prepare_enable(drvdata->atclk);
|
||||
|
||||
if (drvdata && !IS_ERR_OR_NULL(drvdata->pclk))
|
||||
clk_prepare_enable(drvdata->pclk);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@ -316,55 +328,61 @@ static const struct dev_pm_ops funnel_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static int static_funnel_probe(struct platform_device *pdev)
|
||||
static int funnel_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
int ret;
|
||||
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
/* Static funnel do not have programming base */
|
||||
ret = funnel_probe(&pdev->dev, NULL);
|
||||
|
||||
if (ret) {
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
ret = funnel_probe(&pdev->dev, res);
|
||||
pm_runtime_put(&pdev->dev);
|
||||
if (ret)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void static_funnel_remove(struct platform_device *pdev)
|
||||
static void funnel_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct funnel_drvdata *drvdata = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (WARN_ON(!drvdata))
|
||||
return;
|
||||
|
||||
funnel_remove(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
if (!IS_ERR_OR_NULL(drvdata->pclk))
|
||||
clk_put(drvdata->pclk);
|
||||
}
|
||||
|
||||
static const struct of_device_id static_funnel_match[] = {
|
||||
static const struct of_device_id funnel_match[] = {
|
||||
{.compatible = "arm,coresight-static-funnel"},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, static_funnel_match);
|
||||
MODULE_DEVICE_TABLE(of, funnel_match);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id static_funnel_ids[] = {
|
||||
{"ARMHC9FE", 0, 0, 0},
|
||||
static const struct acpi_device_id funnel_acpi_ids[] = {
|
||||
{"ARMHC9FE", 0, 0, 0}, /* ARM Coresight Static Funnel */
|
||||
{"ARMHC9FF", 0, 0, 0}, /* ARM CoreSight Dynamic Funnel */
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(acpi, static_funnel_ids);
|
||||
MODULE_DEVICE_TABLE(acpi, funnel_acpi_ids);
|
||||
#endif
|
||||
|
||||
static struct platform_driver static_funnel_driver = {
|
||||
.probe = static_funnel_probe,
|
||||
.remove_new = static_funnel_remove,
|
||||
.driver = {
|
||||
.name = "coresight-static-funnel",
|
||||
static struct platform_driver funnel_driver = {
|
||||
.probe = funnel_platform_probe,
|
||||
.remove_new = funnel_platform_remove,
|
||||
.driver = {
|
||||
.name = "coresight-funnel",
|
||||
/* THIS_MODULE is taken care of by platform_driver_register() */
|
||||
.of_match_table = static_funnel_match,
|
||||
.acpi_match_table = ACPI_PTR(static_funnel_ids),
|
||||
.of_match_table = funnel_match,
|
||||
.acpi_match_table = ACPI_PTR(funnel_acpi_ids),
|
||||
.pm = &funnel_dev_pm_ops,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
@ -373,7 +391,13 @@ static struct platform_driver static_funnel_driver = {
|
||||
static int dynamic_funnel_probe(struct amba_device *adev,
|
||||
const struct amba_id *id)
|
||||
{
|
||||
return funnel_probe(&adev->dev, &adev->res);
|
||||
int ret;
|
||||
|
||||
ret = funnel_probe(&adev->dev, &adev->res);
|
||||
if (!ret)
|
||||
pm_runtime_put(&adev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dynamic_funnel_remove(struct amba_device *adev)
|
||||
@ -409,27 +433,12 @@ static struct amba_driver dynamic_funnel_driver = {
|
||||
|
||||
static int __init funnel_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&static_funnel_driver);
|
||||
if (ret) {
|
||||
pr_info("Error registering platform driver\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = amba_driver_register(&dynamic_funnel_driver);
|
||||
if (ret) {
|
||||
pr_info("Error registering amba driver\n");
|
||||
platform_driver_unregister(&static_funnel_driver);
|
||||
}
|
||||
|
||||
return ret;
|
||||
return coresight_init_driver("funnel", &dynamic_funnel_driver, &funnel_driver);
|
||||
}
|
||||
|
||||
static void __exit funnel_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&static_funnel_driver);
|
||||
amba_driver_unregister(&dynamic_funnel_driver);
|
||||
coresight_remove_driver(&dynamic_funnel_driver, &funnel_driver);
|
||||
}
|
||||
|
||||
module_init(funnel_init);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user