diff options
249 files changed, 8886 insertions, 2047 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm3x b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm3x index 651602a61eac..234c33fbdb55 100644 --- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm3x +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm3x @@ -236,7 +236,7 @@ What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/traceid Date: November 2014 KernelVersion: 3.19 Contact: Mathieu Poirier <mathieu.poirier@linaro.org> -Description: (RW) Holds the trace ID that will appear in the trace stream +Description: (RO) Holds the trace ID that will appear in the trace stream coming from this trace entity. What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/trigger_event diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm new file mode 100644 index 000000000000..4a58e649550d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tpdm @@ -0,0 +1,13 @@ +What: /sys/bus/coresight/devices/<tpdm-name>/integration_test +Date: January 2023 +KernelVersion 6.2 +Contact: Jinlong Mao (QUIC) <quic_jinlmao@quicinc.com>, Tao Zhang (QUIC) <quic_taozha@quicinc.com> +Description: + (Write) Run integration test for tpdm. Integration test + will generate test data for tpdm. It can help to make + sure that the trace path is enabled and the link configurations + are fine. + + Accepts only one of the 2 values - 1 or 2. + 1 : Generate 64 bits data + 2 : Generate 32 bits data diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-ultra_smb b/Documentation/ABI/testing/sysfs-bus-coresight-devices-ultra_smb new file mode 100644 index 000000000000..f560918ae738 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-ultra_smb @@ -0,0 +1,31 @@ +What: /sys/bus/coresight/devices/ultra_smb<N>/enable_sink +Date: January 2023 +KernelVersion: 6.3 +Contact: Junhao He <hejunhao3@huawei.com> +Description: (RW) Add/remove a SMB device from a trace path. There can be + multiple sources for a single SMB device. + +What: /sys/bus/coresight/devices/ultra_smb<N>/mgmt/buf_size +Date: January 2023 +KernelVersion: 6.3 +Contact: Junhao He <hejunhao3@huawei.com> +Description: (RO) Shows the buffer size of each UltraSoc SMB device. + +What: /sys/bus/coresight/devices/ultra_smb<N>/mgmt/buf_status +Date: January 2023 +KernelVersion: 6.3 +Contact: Junhao He <hejunhao3@huawei.com> +Description: (RO) Shows the value of UltraSoc SMB status register. + BIT(0) is zero means buffer is empty. + +What: /sys/bus/coresight/devices/ultra_smb<N>/mgmt/read_pos +Date: January 2023 +KernelVersion: 6.3 +Contact: Junhao He <hejunhao3@huawei.com> +Description: (RO) Shows the value of UltraSoc SMB Read Pointer register. + +What: /sys/bus/coresight/devices/ultra_smb<N>/mgmt/write_pos +Date: January 2023 +KernelVersion: 6.3 +Contact: Junhao He <hejunhao3@huawei.com> +Description: (RO) Shows the value of UltraSoc SMB Write Pointer register. diff --git a/Documentation/ABI/testing/sysfs-driver-intel-m10-bmc b/Documentation/ABI/testing/sysfs-driver-intel-m10-bmc index 9773925138af..a8ab58035c95 100644 --- a/Documentation/ABI/testing/sysfs-driver-intel-m10-bmc +++ b/Documentation/ABI/testing/sysfs-driver-intel-m10-bmc @@ -1,4 +1,4 @@ -What: /sys/bus/spi/devices/.../bmc_version +What: /sys/bus/.../drivers/intel-m10-bmc/.../bmc_version Date: June 2020 KernelVersion: 5.10 Contact: Xu Yilun <yilun.xu@intel.com> @@ -6,7 +6,7 @@ Description: Read only. Returns the hardware build version of Intel MAX10 BMC chip. Format: "0x%x". -What: /sys/bus/spi/devices/.../bmcfw_version +What: /sys/bus/.../drivers/intel-m10-bmc/.../bmcfw_version Date: June 2020 KernelVersion: 5.10 Contact: Xu Yilun <yilun.xu@intel.com> @@ -14,7 +14,7 @@ Description: Read only. Returns the firmware version of Intel MAX10 BMC chip. Format: "0x%x". -What: /sys/bus/spi/devices/.../mac_address +What: /sys/bus/.../drivers/intel-m10-bmc/.../mac_address Date: January 2021 KernelVersion: 5.12 Contact: Russ Weight <russell.h.weight@intel.com> @@ -25,7 +25,7 @@ Description: Read only. Returns the first MAC address in a block space. Format: "%02x:%02x:%02x:%02x:%02x:%02x". -What: /sys/bus/spi/devices/.../mac_count +What: /sys/bus/.../drivers/intel-m10-bmc/.../mac_count Date: January 2021 KernelVersion: 5.12 Contact: Russ Weight <russell.h.weight@intel.com> diff --git a/Documentation/ABI/testing/sysfs-driver-uacce b/Documentation/ABI/testing/sysfs-driver-uacce index 08f2591138af..d3f0b8f3c589 100644 --- a/Documentation/ABI/testing/sysfs-driver-uacce +++ b/Documentation/ABI/testing/sysfs-driver-uacce @@ -19,6 +19,24 @@ Contact: linux-accelerators@lists.ozlabs.org Description: Available instances left of the device Return -ENODEV if uacce_ops get_available_instances is not provided +What: /sys/class/uacce/<dev_name>/isolate_strategy +Date: Nov 2022 +KernelVersion: 6.1 +Contact: linux-accelerators@lists.ozlabs.org +Description: (RW) A sysfs node that configure the error threshold for the hardware + isolation strategy. This size is a configured integer value, which is the + number of threshold for hardware errors occurred in one hour. The default is 0. + 0 means never isolate the device. The maximum value is 65535. You can write + a number of threshold based on your hardware. + +What: /sys/class/uacce/<dev_name>/isolate +Date: Nov 2022 +KernelVersion: 6.1 +Contact: linux-accelerators@lists.ozlabs.org +Description: (R) A sysfs node that read the device isolated state. The value 1 + means the device is unavailable. The 0 means the device is + available. + What: /sys/class/uacce/<dev_name>/algorithms Date: Feb 2020 KernelVersion: 5.7 diff --git a/Documentation/ABI/testing/sysfs-driver-xilinx-tmr-manager b/Documentation/ABI/testing/sysfs-driver-xilinx-tmr-manager new file mode 100644 index 000000000000..57b9b68a73ee --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-xilinx-tmr-manager @@ -0,0 +1,16 @@ +What: /sys/devices/platform/amba_pl/<dev>/errcnt +Date: Nov 2022 +Contact: appana.durga.kedareswara.rao@amd.com +Description: This control file provides the fault detection count. + This file cannot be written. + Example: + # cat /sys/devices/platform/amba_pl/44a10000.tmr_manager/errcnt + 1 + +What: /sys/devices/platform/amba_pl/<dev>/dis_block_break +Date: Nov 2022 +Contact: appana.durga.kedareswara.rao@amd.com +Description: Write any value to it, This control file enables the break signal. + This file is write only. + Example: + # echo <any value> > /sys/devices/platform/amba_pl/44a10000.tmr_manager/dis_block_break diff --git a/Documentation/devicetree/bindings/arm/qcom,coresight-tpda.yaml b/Documentation/devicetree/bindings/arm/qcom,coresight-tpda.yaml new file mode 100644 index 000000000000..2ec9b5b24d73 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/qcom,coresight-tpda.yaml @@ -0,0 +1,129 @@ +# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause +# Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/qcom,coresight-tpda.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Trace, Profiling and Diagnostics Aggregator - TPDA + +description: | + TPDAs are responsible for packetization and timestamping of data sets + utilizing the MIPI STPv2 packet protocol. Pulling data sets from one or + more attached TPDM and pushing the resultant (packetized) data out a + master ATB interface. Performing an arbitrated ATB interleaving (funneling) + task for free-flowing data from TPDM (i.e. CMB and DSB data set flows). + + There is no strict binding between TPDM and TPDA. TPDA can have multiple + TPDMs connect to it. But There must be only one TPDA in the path from the + TPDM source to TMC sink. TPDM can directly connect to TPDA's inport or + connect to funnel which will connect to TPDA's inport. + + We can use the commands are similar to the below to validate TPDMs. + Enable coresight sink first. + + echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink + echo 1 > /sys/bus/coresight/devices/tpdm0/enable_source + echo 1 > /sys/bus/coresight/devices/tpdm0/integration_test + echo 2 > /sys/bus/coresight/devices/tpdm0/integration_test + + The test data will be collected in the coresight sink which is enabled. + If rwp register of the sink is keeping updating when do integration_test + (by cat tmc_etf0/mgmt/rwp), it means there is data generated from TPDM + to sink. + +maintainers: + - Mao Jinlong <quic_jinlmao@quicinc.com> + - Tao Zhang <quic_taozha@quicinc.com> + +# Need a custom select here or 'arm,primecell' will match on lots of nodes +select: + properties: + compatible: + contains: + enum: + - qcom,coresight-tpda + required: + - compatible + +properties: + $nodename: + pattern: "^tpda(@[0-9a-f]+)$" + compatible: + items: + - const: qcom,coresight-tpda + - const: arm,primecell + + reg: + minItems: 1 + maxItems: 2 + + clocks: + maxItems: 1 + + clock-names: + items: + - 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 + + properties: + port: + description: + Output connection from the TPDA to legacy CoreSight Trace bus. + $ref: /schemas/graph.yaml#/properties/port + +required: + - compatible + - reg + - clocks + - clock-names + - in-ports + - out-ports + +additionalProperties: false + +examples: + # minimum tpda definition. + - | + tpda@6004000 { + compatible = "qcom,coresight-tpda", "arm,primecell"; + reg = <0x6004000 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + 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>; + }; + }; + }; + + out-ports { + port { + tpda_qdss_out_funnel_in0: endpoint { + remote-endpoint = + <&funnel_in0_in_tpda_qdss>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/arm/qcom,coresight-tpdm.yaml b/Documentation/devicetree/bindings/arm/qcom,coresight-tpdm.yaml new file mode 100644 index 000000000000..5c08342664ea --- /dev/null +++ b/Documentation/devicetree/bindings/arm/qcom,coresight-tpdm.yaml @@ -0,0 +1,93 @@ +# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause +# Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/qcom,coresight-tpdm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Trace, Profiling and Diagnostics Monitor - TPDM + +description: | + The TPDM or Monitor serves as data collection component for various dataset + types specified in the QPMDA spec. It covers Implementation defined ((ImplDef), + Basic Counts (BC), Tenure Counts (TC), Continuous Multi-Bit (CMB), and Discrete + Single Bit (DSB). It performs data collection in the data producing clock + domain and transfers it to the data collection time domain, generally ATB + clock domain. + + The primary use case of the TPDM is to collect data from different data + sources and send it to a TPDA for packetization, timestamping, and funneling. + +maintainers: + - Mao Jinlong <quic_jinlmao@quicinc.com> + - Tao Zhang <quic_taozha@quicinc.com> + +# Need a custom select here or 'arm,primecell' will match on lots of nodes +select: + properties: + compatible: + contains: + enum: + - qcom,coresight-tpdm + required: + - compatible + +properties: + $nodename: + pattern: "^tpdm(@[0-9a-f]+)$" + compatible: + items: + - const: qcom,coresight-tpdm + - const: arm,primecell + + reg: + minItems: 1 + maxItems: 2 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: apb_pclk + + out-ports: + description: | + Output connections from the TPDM to coresight funnel/TPDA. + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port: + description: Output connection from the TPDM to coresight + funnel/TPDA. + $ref: /schemas/graph.yaml#/properties/port + +required: + - compatible + - reg + - clocks + - clock-names + +additionalProperties: false + +examples: + # minimum TPDM definition. TPDM connect to coresight TPDA. + - | + tpdm@684c000 { + compatible = "qcom,coresight-tpdm", "arm,primecell"; + reg = <0x0684c000 0x1000>; + + clocks = <&aoss_qmp>; + clock-names = "apb_pclk"; + + out-ports { + port { + tpdm_prng_out_tpda_qdss: endpoint { + remote-endpoint = + <&tpda_qdss_in_tpdm_prng>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml index 7332442e5661..b6ba7ad1a8d5 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adis16201.yaml @@ -41,7 +41,7 @@ unevaluatedProperties: false examples: - | #include <dt-bindings/interrupt-controller/irq.h> - spi0 { + spi { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adis16240.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adis16240.yaml index f6f97164c2ca..5887021cc90f 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adis16240.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adis16240.yaml @@ -39,7 +39,7 @@ examples: - | #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/interrupt-controller/irq.h> - spi0 { + spi { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml index 185b68ffb536..0c5b64cae965 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml @@ -59,7 +59,7 @@ examples: - | #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/interrupt-controller/irq.h> - i2c0 { + i2c { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml index 346abfb13a3a..07cacc3f6a97 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml @@ -49,7 +49,7 @@ examples: - | #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/interrupt-controller/irq.h> - i2c0 { + i2c { #address-cells = <1>; #size-cells = <0>; @@ -64,7 +64,7 @@ examples: - | #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/interrupt-controller/irq.h> - spi0 { + spi { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml index 6b03c4efbb08..c07261c71013 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml @@ -58,34 +58,34 @@ unevaluatedProperties: false examples: - | - #include <dt-bindings/gpio/gpio.h> - #include <dt-bindings/interrupt-controller/irq.h> - i2c { - #address-cells = <1>; - #size-cells = <0>; + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; - /* Example for a I2C device node */ - accelerometer@1d { - compatible = "adi,adxl355"; - reg = <0x1d>; - interrupt-parent = <&gpio>; - interrupts = <25 IRQ_TYPE_EDGE_RISING>; - interrupt-names = "DRDY"; - }; + /* Example for a I2C device node */ + accelerometer@1d { + compatible = "adi,adxl355"; + reg = <0x1d>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "DRDY"; }; + }; - | - #include <dt-bindings/gpio/gpio.h> - #include <dt-bindings/interrupt-controller/irq.h> - spi { - #address-cells = <1>; - #size-cells = <0>; + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + spi { + #address-cells = <1>; + #size-cells = <0>; - accelerometer@0 { - compatible = "adi,adxl355"; - reg = <0>; - spi-max-frequency = <1000000>; - interrupt-parent = <&gpio>; - interrupts = <25 IRQ_TYPE_EDGE_RISING>; - interrupt-names = "DRDY"; - }; + accelerometer@0 { + compatible = "adi,adxl355"; + reg = <0>; + spi-max-frequency = <1000000>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "DRDY"; }; + }; diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml index 73a5c8f814cc..62465e36a590 100644 --- a/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl372.yaml @@ -37,32 +37,32 @@ unevaluatedProperties: false examples: - | - #include <dt-bindings/gpio/gpio.h> - #include <dt-bindings/interrupt-controller/irq.h> - i2c0 { - #address-cells = <1>; - #size-cells = <0>; + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; - /* Example for a I2C device node */ - accelerometer@53 { - compatible = "adi,adxl372"; - reg = <0x53>; - interrupt-parent = <&gpio>; - interrupts = <25 IRQ_TYPE_EDGE_FALLING>; - }; + /* Example for a I2C device node */ + accelerometer@53 { + compatible = "adi,adxl372"; + reg = <0x53>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_EDGE_FALLING>; }; + }; - | - #include <dt-bindings/gpio/gpio.h> - #include <dt-bindings/interrupt-controller/irq.h> - spi0 { - #address-cells = <1>; - #size-cells = <0>; + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + spi { + #address-cells = <1>; + #size-cells = <0>; - accelerometer@0 { - compatible = "adi,adxl372"; - reg = <0>; - spi-max-frequency = <1000000>; - interrupt-parent = <&gpio>; - interrupts = <25 IRQ_TYPE_EDGE_FALLING>; - }; + accelerometer@0 { + compatible = "adi,adxl372"; + reg = <0>; + spi-max-frequency = <1000000>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_EDGE_FALLING>; }; + }; diff --git a/Documentation/devicetree/bindings/iio/accel/bosch,bma220.yaml b/Documentation/devicetree/bindings/iio/accel/bosch,bma220.yaml index 5dd06f5905b4..ec643de031a3 100644 --- a/Documentation/devicetree/bindings/iio/accel/bosch,bma220.yaml +++ b/Documentation/devicetree/bindings/iio/accel/bosch,bma220.yaml @@ -36,7 +36,7 @@ unevaluatedProperties: false examples: - | #include <dt-bindings/interrupt-controller/irq.h> - spi0 { + spi { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml b/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml index 714e48e613de..6ddb03f61bd9 100644 --- a/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml +++ b/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml @@ -44,7 +44,7 @@ examples: accel@f { compatible = "kionix,kxtf9"; - reg = <0x0F>; + reg = <0xf>; mount-matrix = "0", "1", "0", "1", "0", "0", "0", "0", "1"; diff --git a/Documentation/devicetree/bindings/iio/accel/memsensing,msa311.yaml b/Documentation/devicetree/bindings/iio/accel/memsensing,msa311.yaml index 23528dcaa073..d530ec041fe7 100644 --- a/Documentation/devicetree/bindings/iio/accel/memsensing,msa311.yaml +++ b/Documentation/devicetree/bindings/iio/accel/memsensing,msa311.yaml @@ -1,9 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause - %YAML 1.2 --- -$id: "http://devicetree.org/schemas/iio/accel/memsensing,msa311.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/iio/accel/memsensing,msa311.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: MEMSensing digital 3-Axis accelerometer diff --git a/Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml b/Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml index 65ce8ea14b52..783c7ddfcd90 100644 --- a/Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml +++ b/Documentation/devicetree/bindings/iio/accel/nxp,fxls8962af.yaml @@ -50,7 +50,7 @@ unevaluatedProperties: false examples: - | #include <dt-bindings/interrupt-controller/irq.h> - i2c0 { + i2c { #address-cells = <1>; #size-cells = <0>; @@ -65,7 +65,7 @@ examples: }; - | #include <dt-bindings/interrupt-controller/irq.h> - spi0 { + spi { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml index b97559f23b3a..ce7ba634643c 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7091r5.yaml @@ -44,11 +44,11 @@ examples: #size-cells = <0>; adc@2f { - compatible = "adi,ad7091r5"; - reg = <0x2f>; + compatible = "adi,ad7091r5"; + reg = <0x2f>; - interrupts = <25 IRQ_TYPE_EDGE_FALLING>; - interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml index 75a7184a4735..35ed04350e28 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml @@ -61,7 +61,7 @@ required: patternProperties: "^channel@([0-9]|1[0-5])$": - $ref: "adc.yaml" + $ref: adc.yaml type: object description: | Represents the external channels which are connected to the ADC. diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml index cc347dade4ef..d521d516088b 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml @@ -99,26 +99,26 @@ unevaluatedProperties: false examples: - | - spi0 { - #address-cells = <1>; - #size-cells = <0>; - - adc@0 { - compatible = "adi,ad7192"; - reg = <0>; - spi-max-frequency = <1000000>; - spi-cpol; - spi-cpha; - clocks = <&ad7192_mclk>; - clock-names = "mclk"; - interrupts = <25 0x2>; - interrupt-parent = <&gpio>; - dvdd-supply = <&dvdd>; - avdd-supply = <&avdd>; - - adi,refin2-pins-enable; - adi,rejection-60-Hz-enable; - adi,buffer-enable; - adi,burnout-currents-enable; + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "adi,ad7192"; + reg = <0>; + spi-max-frequency = <1000000>; + spi-cpol; + spi-cpha; + clocks = <&ad7192_mclk>; + clock-names = "mclk"; + interrupts = <25 0x2>; + interrupt-parent = <&gpio>; + dvdd-supply = <&dvdd>; + avdd-supply = <&avdd>; + + adi,refin2-pins-enable; + adi,rejection-60-Hz-enable; + adi,buffer-enable; + adi,burnout-currents-enable; }; }; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml index 1bfbeed6f299..7cc4ddc4e9b7 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7292.yaml @@ -43,7 +43,7 @@ required: patternProperties: "^channel@[0-7]$": - $ref: "adc.yaml" + $ref: adc.yaml type: object description: | Represents the external channels which are connected to the ADC. diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml index ac5a47c8f070..7fa46df1f4fb 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml @@ -112,30 +112,30 @@ examples: - | #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/interrupt-controller/irq.h> - spi0 { + spi { #address-cells = <1>; #size-cells = <0>; adc@0 { - compatible = "adi,ad7606-8"; - reg = <0>; - spi-max-frequency = <1000000>; - spi-cpol; - spi-cpha; - - avcc-supply = <&adc_vref>; - - interrupts = <25 IRQ_TYPE_EDGE_FALLING>; - interrupt-parent = <&gpio>; - - adi,conversion-start-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>; - reset-gpios = <&gpio 27 GPIO_ACTIVE_HIGH>; - adi,first-data-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>; - adi,oversampling-ratio-gpios = <&gpio 18 GPIO_ACTIVE_HIGH>, - <&gpio 23 GPIO_ACTIVE_HIGH>, - <&gpio 26 GPIO_ACTIVE_HIGH>; - standby-gpios = <&gpio 24 GPIO_ACTIVE_LOW>; - adi,sw-mode; + compatible = "adi,ad7606-8"; + reg = <0>; + spi-max-frequency = <1000000>; + spi-cpol; + spi-cpha; + + avcc-supply = <&adc_vref>; + + interrupts = <25 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio>; + + adi,conversion-start-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio 27 GPIO_ACTIVE_HIGH>; + adi,first-data-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>; + adi,oversampling-ratio-gpios = <&gpio 18 GPIO_ACTIVE_HIGH>, + <&gpio 23 GPIO_ACTIVE_HIGH>, + <&gpio 26 GPIO_ACTIVE_HIGH>; + standby-gpios = <&gpio 24 GPIO_ACTIVE_LOW>; + adi,sw-mode; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml index a67ba67dab51..5fcc8dd012f1 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml @@ -72,7 +72,7 @@ additionalProperties: false examples: - | #include <dt-bindings/gpio/gpio.h> - spi0 { + spi { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad799x.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad799x.yaml index 29641ce7175b..433ed2c9295f 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad799x.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad799x.yaml @@ -57,17 +57,17 @@ additionalProperties: false examples: - | i2c { - #address-cells = <1>; - #size-cells = <0>; + #address-cells = <1>; + #size-cells = <0>; - adc1: adc@28 { - reg = <0x28>; - compatible = "adi,ad7991"; - interrupts = <13 2>; - interrupt-parent = <&gpio6>; + adc1: adc@28 { + reg = <0x28>; + compatible = "adi,ad7991"; + interrupts = <13 2>; + interrupt-parent = <&gpio6>; - vcc-supply = <&vcc_3v3>; - vref-supply = <&adc_vref>; + vcc-supply = <&vcc_3v3>; + vref-supply = <&adc_vref>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml index 2d72ff6bcbc0..7aa748d6b7a0 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad9467.yaml @@ -64,10 +64,10 @@ examples: #size-cells = <0>; adc@0 { - compatible = "adi,ad9467"; - reg = <0>; - clocks = <&adc_clk>; - clock-names = "adc-clk"; + compatible = "adi,ad9467"; + reg = <0>; + clocks = <&adc_clk>; + clock-names = "adc-clk"; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml b/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml index 8e25773d69be..9996dd93f84b 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,axi-adc.yaml @@ -51,11 +51,11 @@ additionalProperties: false examples: - | axi-adc@44a00000 { - compatible = "adi,axi-adc-10.0.a"; - reg = <0x44a00000 0x10000>; - dmas = <&rx_dma 0>; - dma-names = "rx"; + compatible = "adi,axi-adc-10.0.a"; + reg = <0x44a00000 0x10000>; + dmas = <&rx_dma 0>; + dma-names = "rx"; - adi,adc-dev = <&spi_adc>; + adi,adc-dev = <&spi_adc>; }; ... diff --git a/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml b/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml index 31f840d59303..4817b840977a 100644 --- a/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml @@ -41,7 +41,7 @@ properties: description: Startup time expressed in ms, it depends on SoC. atmel,trigger-edge-type: - $ref: '/schemas/types.yaml#/definitions/uint32' + $ref: /schemas/types.yaml#/definitions/uint32 description: One of possible edge types for the ADTRG hardware trigger pin. When the specific edge type is detected, the conversion will diff --git a/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml b/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml index 77605f17901c..9c57eb13f892 100644 --- a/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml +++ b/Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/iio/adc/avia-hx711.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/iio/adc/avia-hx711.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: AVIA HX711 ADC chip for weight cells diff --git a/Documentation/devicetree/bindings/iio/adc/cirrus,ep9301-adc.yaml b/Documentation/devicetree/bindings/iio/adc/cirrus,ep9301-adc.yaml new file mode 100644 index 000000000000..6d4fb3e1d2a2 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/cirrus,ep9301-adc.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/cirrus,ep9301-adc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cirrus Logic EP930x internal ADC + +description: | + Cirrus Logic EP9301/EP9302 SoCs' internal ADC block. + + User's manual: + https://cdn.embeddedts.com/resource-attachments/ts-7000_ep9301-ug.pdf + +maintainers: + - Alexander Sverdlin <alexander.sverdlin@gmail.com> + +properties: + compatible: + const: cirrus,ep9301-adc + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + - | + adc: adc@80900000 { + compatible = "cirrus,ep9301-adc"; + reg = <0x80900000 0x28>; + clocks = <&syscon 24>; + interrupt-parent = <&vic1>; + interrupts = <30>; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/ingenic,adc.yaml b/Documentation/devicetree/bindings/iio/adc/ingenic,adc.yaml index 517e8b1fcb73..9cd0fd539782 100644 --- a/Documentation/devicetree/bindings/iio/adc/ingenic,adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ingenic,adc.yaml @@ -2,8 +2,8 @@ # Copyright 2019-2020 Artur Rojek %YAML 1.2 --- -$id: "http://devicetree.org/schemas/iio/adc/ingenic,adc.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/iio/adc/ingenic,adc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Ingenic JZ47xx ADC controller IIO @@ -78,14 +78,14 @@ examples: #include <dt-bindings/iio/adc/ingenic,adc.h> adc@10070000 { - compatible = "ingenic,jz4740-adc"; - #io-channel-cells = <1>; + compatible = "ingenic,jz4740-adc"; + #io-channel-cells = <1>; - reg = <0x10070000 0x30>; + reg = <0x10070000 0x30>; - clocks = <&cgu JZ4740_CLK_ADC>; - clock-names = "adc"; + clocks = <&cgu JZ4740_CLK_ADC>; + clock-names = "adc"; - interrupt-parent = <&intc>; - interrupts = <18>; + interrupt-parent = <&intc>; + interrupts = <18>; }; diff --git a/Documentation/devicetree/bindings/iio/adc/maxim,max1027.yaml b/Documentation/devicetree/bindings/iio/adc/maxim,max1027.yaml index d0a7ed26d9ea..e4b362113509 100644 --- a/Documentation/devicetree/bindings/iio/adc/maxim,max1027.yaml +++ b/Documentation/devicetree/bindings/iio/adc/maxim,max1027.yaml @@ -54,8 +54,8 @@ examples: - | #include <dt-bindings/interrupt-controller/irq.h> spi { - #address-cells = <1>; - #size-cells = <0>; + #address-cells = <1>; + #size-cells = <0>; maxadc: adc@0 { compatible = "maxim,max1027"; reg = <0>; diff --git a/Documentation/devicetree/bindings/iio/adc/maxim,max1238.yaml b/Documentation/devicetree/bindings/iio/adc/maxim,max1238.yaml index 50bcd72ac9d6..60d7b34e3286 100644 --- a/Documentation/devicetree/bindings/iio/adc/maxim,max1238.yaml +++ b/Documentation/devicetree/bindings/iio/adc/maxim,max1238.yaml @@ -10,7 +10,7 @@ maintainers: - Jonathan Cameron <jic23@kernel.org> description: | - Family of simple ADCs with i2c inteface and internal references. + Family of simple ADCs with i2c interface and internal references. properties: compatible: diff --git a/Documentation/devicetree/bindings/iio/adc/maxim,max1241.yaml b/Documentation/devicetree/bindings/iio/adc/maxim,max1241.yaml index 58b12fe8070c..ef8d51e74c08 100644 --- a/Documentation/devicetree/bindings/iio/adc/maxim,max1241.yaml +++ b/Documentation/devicetree/bindings/iio/adc/maxim,max1241.yaml @@ -54,8 +54,8 @@ examples: - | #include <dt-bindings/gpio/gpio.h> spi { - #address-cells = <1>; - #size-cells = <0>; + #address-cells = <1>; + #size-cells = <0>; adc@0 { compatible = "maxim,max1241"; diff --git a/Documentation/devicetree/bindings/iio/adc/maxim,max1363.yaml b/Documentation/devicetree/bindings/iio/adc/maxim,max1363.yaml index e04f09f35601..96f3f535fe34 100644 --- a/Documentation/devicetree/bindings/iio/adc/maxim,max1363.yaml +++ b/Documentation/devicetree/bindings/iio/adc/maxim,max1363.yaml @@ -10,7 +10,7 @@ maintainers: - Jonathan Cameron <jic23@kernel.org> description: | - Family of ADCs with i2c inteface, internal references and threshold + Family of ADCs with i2c interface, internal references and threshold monitoring. properties: diff --git a/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml index 2c93fb41f172..f7b3fde4115a 100644 --- a/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml +++ b/Documentation/devicetree/bindings/iio/adc/microchip,mcp3911.yaml @@ -2,8 +2,8 @@ # Copyright 2019 Marcus Folkesson <marcus.folkesson@gmail.com> %YAML 1.2 --- -$id: "http://devicetree.org/schemas/iio/adc/microchip,mcp3911.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/iio/adc/microchip,mcp3911.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Microchip MCP3911 Dual channel analog front end (ADC) diff --git a/Documentation/devicetree/bindings/iio/adc/nxp,imx93-adc.yaml b/Documentation/devicetree/bindings/iio/adc/nxp,imx93-adc.yaml new file mode 100644 index 000000000000..dacc526dc695 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/nxp,imx93-adc.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/nxp,imx93-adc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP iMX93 ADC + +maintainers: + - Haibo Chen <haibo.chen@nxp.com> + +description: + The ADC on iMX93 is a 8-channel 12-bit 1MS/s ADC with 4 channels + connected to pins. it support normal and inject mode, include + One-Shot and Scan (continuous) conversions. Programmable DMA + enables for each channel Also this ADC contain alternate analog + watchdog thresholds, select threshold through input ports. And + also has Self-test logic and Software-initiated calibration. + +properties: + compatible: + const: nxp,imx93-adc + + reg: + maxItems: 1 + + interrupts: + items: + - description: WDGnL, watchdog threshold interrupt requests. + - description: WDGnH, watchdog threshold interrupt requests. + - description: normal conversion, include EOC (End of Conversion), + ECH (End of Chain), JEOC (End of Injected Conversion) and + JECH (End of injected Chain). + - description: Self-testing Interrupts. + + clocks: + maxItems: 1 + + clock-names: + const: ipg + + vref-supply: + description: + The reference voltage which used to establish channel scaling. + + "#io-channel-cells": + const: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - vref-supply + - "#io-channel-cells" + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/clock/imx93-clock.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + soc { + #address-cells = <1>; + #size-cells = <1>; + adc@44530000 { + compatible = "nxp,imx93-adc"; + reg = <0x44530000 0x10000>; + interrupts = <GIC_SPI 217 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 218 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 219 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 268 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk IMX93_CLK_ADC1_GATE>; + clock-names = "ipg"; + vref-supply = <®_vref_1v8>; + #io-channel-cells = <1>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,pm8018-adc.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,pm8018-adc.yaml index d186b713d6a7..58ea1ca4a5ee 100644 --- a/Documentation/devicetree/bindings/iio/adc/qcom,pm8018-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/qcom,pm8018-adc.yaml @@ -160,7 +160,7 @@ examples: }; ref_muxoff: adc-channel@f { reg = <0x00 0x0f>; - }; + }; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-iadc.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-iadc.yaml index fa855baa368c..73def67fbe01 100644 --- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-iadc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-iadc.yaml @@ -20,6 +20,7 @@ properties: compatible: items: - enum: + - qcom,pm8226-iadc - qcom,pm8941-iadc - const: qcom,spmi-iadc @@ -49,7 +50,7 @@ additionalProperties: false examples: - | #include <dt-bindings/interrupt-controller/irq.h> - spmi_bus { + spmi { #address-cells = <1>; #size-cells = <0>; pmic_iadc: adc@3600 { diff --git a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-rradc.yaml b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-rradc.yaml index c8cbfd3444be..b3a626389870 100644 --- a/Documentation/devicetree/bindings/iio/adc/qcom,spmi-rradc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/qcom,spmi-rradc.yaml @@ -40,12 +40,12 @@ additionalProperties: false examples: - | pmic { - #address-cells = <1>; - #size-cells = <0>; - - pmic_rradc: adc@4500 { - compatible = "qcom,pmi8998-rradc"; - reg = <0x4500>; - #io-channel-cells = <1>; - }; + #address-cells = <1>; + #size-cells = <0>; + + pmic_rradc: adc@4500 { + compatible = "qcom,pmi8998-rradc"; + reg = <0x4500>; + #io-channel-cells = <1>; + }; }; diff --git a/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml index 8b743742a5f9..ba86c7b7d622 100644 --- a/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml @@ -69,7 +69,7 @@ required: patternProperties: "^channel@[0-7]$": - $ref: "adc.yaml" + $ref: adc.yaml type: object description: | Represents the external channels which are connected to the ADC. diff --git a/Documentation/devicetree/bindings/iio/adc/samsung,exynos-adc.yaml b/Documentation/devicetree/bindings/iio/adc/samsung,exynos-adc.yaml index 81c87295912c..582d0a03b814 100644 --- a/Documentation/devicetree/bindings/iio/adc/samsung,exynos-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/samsung,exynos-adc.yaml @@ -52,7 +52,7 @@ properties: vdd-supply: true samsung,syscon-phandle: - $ref: '/schemas/types.yaml#/definitions/phandle' + $ref: /schemas/types.yaml#/definitions/phandle description: Phandle to the PMU system controller node (to access the ADC_PHY register on Exynos3250/4x12/5250/5420/5800). @@ -142,7 +142,7 @@ examples: pullup-ohm = <47000>; pulldown-ohm = <0>; io-channels = <&adc 4>; - }; + }; }; - | @@ -150,7 +150,7 @@ examples: adc@126c0000 { compatible = "samsung,exynos3250-adc"; - reg = <0x126C0000 0x100>; + reg = <0x126c0000 0x100>; interrupts = <0 137 0>; #io-channel-cells = <1>; diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml index 1c340c95df16..995cbf8cefc6 100644 --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: "http://devicetree.org/schemas/iio/adc/st,stm32-adc.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/iio/adc/st,stm32-adc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: STMicroelectronics STM32 ADC @@ -80,7 +80,7 @@ properties: description: Phandle to system configuration controller. It can be used to control the analog circuitry on stm32mp1. - $ref: "/schemas/types.yaml#/definitions/phandle-array" + $ref: /schemas/types.yaml#/definitions/phandle-array interrupt-controller: true @@ -341,7 +341,7 @@ patternProperties: patternProperties: "^channel@([0-9]|1[0-9])$": type: object - $ref: "adc.yaml" + $ref: adc.yaml description: Represents the external channels which are connected to the ADC. properties: diff --git a/Documentation/devicetree/bindings/iio/adc/st,stmpe-adc.yaml b/Documentation/devicetree/bindings/iio/adc/st,stmpe-adc.yaml index 333744a2159c..474e35c49348 100644 --- a/Documentation/devicetree/bindings/iio/adc/st,stmpe-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/st,stmpe-adc.yaml @@ -35,10 +35,8 @@ additionalProperties: false examples: - | - stmpe { - stmpe_adc { - compatible = "st,stmpe-adc"; - st,norequest-mask = <0x0F>; /* dont use ADC CH3-0 */ - }; + adc { + compatible = "st,stmpe-adc"; + st,norequest-mask = <0x0f>; /* dont use ADC CH3-0 */ }; ... diff --git a/Documentation/devicetree/bindings/iio/adc/ti,adc081c.yaml b/Documentation/devicetree/bindings/iio/adc/ti,adc081c.yaml new file mode 100644 index 000000000000..caaad777580c --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ti,adc081c.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/ti,adc081c.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI Single-channel I2C ADCs + +maintainers: + - Jonathan Cameron <jic23@kernel.org> + - Lars-Peter Clausen <lars@metafoo.de> + +description: | + Single-channel ADC supporting 8, 10, or 12-bit samples and high/low alerts. + +properties: + compatible: + enum: + - ti,adc081c + - ti,adc101c + - ti,adc121c + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vref-supply: + description: + Regulator for the combined power supply and voltage reference + + "#io-channel-cells": + const: 1 + +required: + - compatible + - reg + - vref-supply + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + adc@52 { + compatible = "ti,adc081c"; + reg = <0x52>; + vref-supply = <®_2p5v>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml index 2c3c2cf2145c..2127d639a768 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads1015.yaml @@ -104,12 +104,12 @@ examples: #address-cells = <1>; #size-cells = <0>; channel@0 { - reg = <0>; + reg = <0>; }; channel@4 { - reg = <4>; - ti,gain = <3>; - ti,datarate = <5>; + reg = <4>; + ti,gain = <3>; + ti,datarate = <5>; }; }; }; diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml index 55c2c73626f4..890f125d422c 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads131e08.yaml @@ -77,7 +77,7 @@ required: patternProperties: "^channel@([0-7])$": - $ref: "adc.yaml" + $ref: adc.yaml type: object description: | Represents the external channels which are connected to the ADC. diff --git a/Documentation/devicetree/bindings/iio/adc/ti,ads7924.yaml b/Documentation/devicetree/bindings/iio/adc/ti,ads7924.yaml new file mode 100644 index 000000000000..0d8d06afed8b --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ti,ads7924.yaml @@ -0,0 +1,110 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/ti,ads7924.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI ADS7924 4 channels 12 bits I2C analog to digital converter + +maintainers: + - Hugo Villeneuve <hvilleneuve@dimonoff.com> + +description: | + Texas Instruments ADS7924 4 channels 12 bits I2C analog to digital converter + + Specifications: + https://www.ti.com/lit/gpn/ads7924 + +properties: + compatible: + const: ti,ads7924 + + reg: + maxItems: 1 + + vref-supply: + description: + The regulator supply for the ADC reference voltage (AVDD) + + reset-gpios: + maxItems: 1 + + interrupts: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + "#io-channel-cells": + const: 1 + +patternProperties: + "^channel@[0-3]+$": + $ref: adc.yaml + + description: | + Represents the external channels which are connected to the ADC. + + properties: + reg: + description: | + The channel number. It can have up to 4 channels numbered from 0 to 3. + items: + - minimum: 0 + maximum: 3 + + label: true + + required: + - reg + + additionalProperties: false + +additionalProperties: false + +required: + - compatible + - reg + - vref-supply + - "#address-cells" + - "#size-cells" + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/gpio/gpio.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + + adc@48 { + compatible = "ti,ads7924"; + reg = <0x48>; + vref-supply = <&ads7924_reg>; + reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>; + interrupts = <25 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio>; + #address-cells = <1>; + #size-cells = <0>; + channel@0 { + reg = <0>; + label = "CH0"; + }; + channel@1 { + reg = <1>; + label = "CH1"; + }; + channel@2 { + reg = <2>; + label = "CH2"; + }; + channel@3 { + reg = <3>; + label = "CH3"; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/ti,lmp92064.yaml b/Documentation/devicetree/bindings/iio/adc/ti,lmp92064.yaml new file mode 100644 index 000000000000..5fb65bf7749d --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ti,lmp92064.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/ti,lmp92064.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments LMP92064 Precision Current and Voltage Sensor. + +maintainers: + - Leonard Göhrs <l.goehrs@pengutronix.de> + +description: | + The LMP92064 is a two channel ADC intended for combined voltage and current + measurements. + + The device contains two ADCs to allow simultaneous sampling of voltage and + current and thus of instantaneous power consumption. + +properties: + compatible: + enum: + - ti,lmp92064 + + reg: + maxItems: 1 + + vdd-supply: + description: Regulator that provides power to the main part of the chip + + vdig-supply: + description: | + Regulator that provides power to the digital I/O part of the chip + + shunt-resistor-micro-ohms: + description: | + Value of the shunt resistor (in µΩ) connected between INCP and INCN, + across which current is measured. Used to provide correct scaling of the + raw ADC measurement. + + reset-gpios: + maxItems: 1 + +required: + - compatible + - reg + - shunt-resistor-micro-ohms + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + + adc@0 { + compatible = "ti,lmp92064"; + reg = <0>; + vdd-supply = <&vdd>; + vdig-supply = <&vdd>; + spi-max-frequency = <20000000>; + shunt-resistor-micro-ohms = <15000>; + reset-gpios = <&gpio1 16 GPIO_ACTIVE_HIGH>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml b/Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml index bdf3bba2d750..866a05c9db36 100644 --- a/Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml +++ b/Documentation/devicetree/bindings/iio/adc/ti,tsc2046.yaml @@ -41,7 +41,7 @@ required: patternProperties: "^channel@[0-7]$": - $ref: "adc.yaml" + $ref: adc.yaml type: object properties: @@ -83,36 +83,36 @@ examples: #size-cells = <0>; channel@0 { - reg = <0>; + reg = <0>; }; channel@1 { - reg = <1>; - settling-time-us = <700>; - oversampling-ratio = <5>; + reg = <1>; + settling-time-us = <700>; + oversampling-ratio = <5>; }; channel@2 { - reg = <2>; + reg = <2>; }; channel@3 { - reg = <3>; - settling-time-us = <700>; - oversampling-ratio = <5>; + reg = <3>; + settling-time-us = <700>; + oversampling-ratio = <5>; }; channel@4 { - reg = <4>; - settling-time-us = <700>; - oversampling-ratio = <5>; + reg = <4>; + settling-time-us = <700>; + oversampling-ratio = <5>; }; channel@5 { - reg = <5>; - settling-time-us = <700>; - oversampling-ratio = <5>; + reg = <5>; + settling-time-us = <700>; + oversampling-ratio = <5>; }; channel@6 { - reg = <6>; + reg = <6>; }; channel@7 { - reg = <7>; + reg = <7>; }; }; }; diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml index fee0f023a8c8..96340a05754c 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml @@ -192,26 +192,26 @@ additionalProperties: false examples: - | spi { - #address-cells = <1>; - #size-cells = <0>; - ad3552r@0 { - compatible = "adi,ad3552r"; - reg = <0>; - spi-max-frequency = <20000000>; - #address-cells = <1>; - #size-cells = <0>; - channel@0 { - reg = <0>; - adi,output-range-microvolt = <0 10000000>; - }; - channel@1 { - reg = <1>; - custom-output-range-config { - adi,gain-offset = <5>; - adi,gain-scaling-p-inv-log2 = <1>; - adi,gain-scaling-n-inv-log2 = <2>; - adi,rfb-ohms = <1>; - }; + #address-cells = <1>; + #size-cells = <0>; + ad3552r@0 { + compatible = "adi,ad3552r"; + reg = <0>; + spi-max-frequency = <20000000>; + #address-cells = <1>; + #size-cells = <0>; + channel@0 { + reg = <0>; + adi,output-range-microvolt = <0 10000000>; + }; + channel@1 { + reg = <1>; + custom-output-range-config { + adi,gain-offset = <5>; + adi,gain-scaling-p-inv-log2 = <1>; + adi,gain-scaling-n-inv-log2 = <2>; + adi,rfb-ohms = <1>; + }; }; }; }; diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5380.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5380.yaml index ff50c72c62b5..9eb9928500e2 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5380.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5380.yaml @@ -12,6 +12,7 @@ maintainers: description: | DAC devices supporting both SPI and I2C interfaces. + properties: compatible: enum: diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml index 13f214234b8e..b4400c52bec3 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5686.yaml @@ -33,6 +33,7 @@ properties: - description: I2C devices enum: - adi,ad5311r + - adi,ad5337r - adi,ad5338r - adi,ad5671r - adi,ad5675r diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5766.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5766.yaml index 3c8784a54d2c..212c936bab8d 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5766.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5766.yaml @@ -51,15 +51,15 @@ additionalProperties: false examples: - | spi { - #address-cells = <1>; - #size-cells = <0>; + #address-cells = <1>; + #size-cells = <0>; - ad5766@0 { - compatible = "adi,ad5766"; - output-range-microvolts = <(-5000000) 5000000>; - reg = <0>; - spi-cpol; - spi-max-frequency = <1000000>; - reset-gpios = <&gpio 22 0>; - }; - }; + ad5766@0 { + compatible = "adi,ad5766"; + output-range-microvolts = <(-5000000) 5000000>; + reg = <0>; + spi-cpol; + spi-max-frequency = <1000000>; + reset-gpios = <&gpio 22 0>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml index 8e7da0de918f..82b0eed6a7b7 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml @@ -147,49 +147,49 @@ unevaluatedProperties: false examples: - | - spi { - #address-cells = <1>; - #size-cells = <0>; - - ad5770r@0 { - compatible = "adi,ad5770r"; - reg = <0>; - spi-max-frequency = <1000000>; - vref-supply = <&vref>; - adi,external-resistor; - reset-gpios = <&gpio 22 0>; - #address-cells = <1>; - #size-cells = <0>; - - channel@0 { - reg = <0>; - adi,range-microamp = <0 300000>; - }; - - channel@1 { - reg = <1>; - adi,range-microamp = <0 140000>; - }; - - channel@2 { - reg = <2>; - adi,range-microamp = <0 55000>; - }; - - channel@3 { - reg = <3>; - adi,range-microamp = <0 45000>; - }; - - channel@4 { - reg = <4>; - adi,range-microamp = <0 45000>; - }; - - channel@5 { - reg = <5>; - adi,range-microamp = <0 45000>; - }; - }; + spi { + #address-cells = <1>; + #size-cells = <0>; + + ad5770r@0 { + compatible = "adi,ad5770r"; + reg = <0>; + spi-max-frequency = <1000000>; + vref-supply = <&vref>; + adi,external-resistor; + reset-gpios = <&gpio 22 0>; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + adi,range-microamp = <0 300000>; + }; + + channel@1 { + reg = <1>; + adi,range-microamp = <0 140000>; + }; + + channel@2 { + reg = <2>; + adi,range-microamp = <0 55000>; + }; + + channel@3 { + reg = <3>; + adi,range-microamp = <0 45000>; + }; + + channel@4 { + reg = <4>; + adi,range-microamp = <0 45000>; + }; + + channel@5 { + reg = <5>; + adi,range-microamp = <0 45000>; + }; }; + }; ... diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ltc2688.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ltc2688.yaml index 15cc6bf59b13..f22ef710ecde 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ltc2688.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ltc2688.yaml @@ -116,32 +116,32 @@ examples: - | spi { - #address-cells = <1>; - #size-cells = <0>; - ltc2688: ltc2688@0 { - compatible = "adi,ltc2688"; - reg = <0>; - - vcc-supply = <&vcc>; - iovcc-supply = <&vcc>; - vref-supply = <&vref>; - - #address-cells = <1>; - #size-cells = <0>; - channel@0 { - reg = <0>; - adi,toggle-mode; - adi,overrange; - }; - - channel@1 { - reg = <1>; - adi,output-range-microvolt = <0 10000000>; - - clocks = <&clock_tgp3>; - adi,toggle-dither-input = <2>; - }; - }; + #address-cells = <1>; + #size-cells = <0>; + ltc2688: ltc2688@0 { + compatible = "adi,ltc2688"; + reg = <0>; + + vcc-supply = <&vcc>; + iovcc-supply = <&vcc>; + vref-supply = <&vref>; + + #address-cells = <1>; + #size-cells = <0>; + channel@0 { + reg = <0>; + adi,toggle-mode; + adi,overrange; + }; + + channel@1 { + reg = <1>; + adi,output-range-microvolt = <0 10000000>; + + clocks = <&clock_tgp3>; + adi,toggle-dither-input = <2>; + }; + }; }; ... diff --git a/Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml b/Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml index 133b0f867992..c9f51d00fa8f 100644 --- a/Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml +++ b/Documentation/devicetree/bindings/iio/dac/lltc,ltc1660.yaml @@ -2,8 +2,8 @@ # Copyright 2019 Marcus Folkesson <marcus.folkesson@gmail.com> %YAML 1.2 --- -$id: "http://devicetree.org/schemas/iio/dac/lltc,ltc1660.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/iio/dac/lltc,ltc1660.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Linear Technology Micropower octal 8-Bit and 10-Bit DACs diff --git a/Documentation/devicetree/bindings/iio/dac/lltc,ltc2632.yaml b/Documentation/devicetree/bindings/iio/dac/lltc,ltc2632.yaml index b1eb77335d05..733edc7d6d17 100644 --- a/Documentation/devicetree/bindings/iio/dac/lltc,ltc2632.yaml +++ b/Documentation/devicetree/bindings/iio/dac/lltc,ltc2632.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause %YAML 1.2 --- -$id: "http://devicetree.org/schemas/iio/dac/lltc,ltc2632.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/iio/dac/lltc,ltc2632.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Linear Technology LTC263x 12-/10-/8-Bit Rail-to-Rail DAC @@ -64,14 +64,14 @@ examples: }; spi { - #address-cells = <1>; - #size-cells = <0>; + #address-cells = <1>; + #size-cells = <0>; - dac@0 { - compatible = "lltc,ltc2632-l12"; - reg = <0>; /* CS0 */ - spi-max-frequency = <1000000>; - vref-supply = <&vref>; - }; + dac@0 { + compatible = "lltc,ltc2632-l12"; + reg = <0>; /* CS0 */ + spi-max-frequency = <1000000>; + vref-supply = <&vref>; + }; }; ... diff --git a/Documentation/devicetree/bindings/iio/dac/maxim,max5522.yaml b/Documentation/devicetree/bindings/iio/dac/maxim,max5522.yaml new file mode 100644 index 000000000000..24830f56c501 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/maxim,max5522.yaml @@ -0,0 +1,49 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/dac/maxim,max5522.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim Integrated MAX5522 Dual 10-bit Voltage-Output SPI DACs + +maintainers: + - Angelo Dureghello <angelo.dureghello@timesys.com> + - Jonathan Cameron <jic23@kernel.org> + +description: | + Datasheet available at: + https://www.analog.com/en/products/max5522.html + +properties: + compatible: + const: maxim,max5522 + + reg: + maxItems: 1 + + vdd-supply: true + vrefin-supply: true + +required: + - compatible + - reg + - vrefin-supply + +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + dac@0 { + compatible = "maxim,max5522"; + reg = <0>; + vrefin-supply = <&vref>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/dac/st,stm32-dac.yaml b/Documentation/devicetree/bindings/iio/dac/st,stm32-dac.yaml index 0f1bf1110122..04045b932bd2 100644 --- a/Documentation/devicetree/bindings/iio/dac/st,stm32-dac.yaml +++ b/Documentation/devicetree/bindings/iio/dac/st,stm32-dac.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: "http://devicetree.org/schemas/iio/dac/st,stm32-dac.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/iio/dac/st,stm32-dac.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: STMicroelectronics STM32 DAC diff --git a/Documentation/devicetree/bindings/iio/dac/ti,dac5571.yaml b/Documentation/devicetree/bindings/iio/dac/ti,dac5571.yaml index 88298bc43b81..79da0323c327 100644 --- a/Documentation/devicetree/bindings/iio/dac/ti,dac5571.yaml +++ b/Documentation/devicetree/bindings/iio/dac/ti,dac5571.yaml @@ -46,7 +46,7 @@ examples: dac@4c { compatible = "ti,dac5571"; - reg = <0x4C>; + reg = <0x4c>; vref-supply = <&vdd_supply>; }; }; diff --git a/Documentation/devicetree/bindings/iio/frequency/adf4371.yaml b/Documentation/devicetree/bindings/iio/frequency/adf4371.yaml index 0144f74a4768..1cb2adaf66f9 100644 --- a/Documentation/devicetree/bindings/iio/frequency/adf4371.yaml +++ b/Documentation/devicetree/bindings/iio/frequency/adf4371.yaml @@ -53,16 +53,16 @@ unevaluatedProperties: false examples: - | - spi0 { + spi { #address-cells = <1>; #size-cells = <0>; frequency@0 { - compatible = "adi,adf4371"; - reg = <0>; - spi-max-frequency = <1000000>; - clocks = <&adf4371_clkin>; - clock-names = "clkin"; + compatible = "adi,adf4371"; + reg = <0>; + spi-max-frequency = <1000000>; + clocks = <&adf4371_clkin>; + clock-names = "clkin"; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/gyroscope/adi,adxrs290.yaml b/Documentation/devicetree/bindings/iio/gyroscope/adi,adxrs290.yaml index 0ae2464b9bc4..3d94dd4612c4 100644 --- a/Documentation/devicetree/bindings/iio/gyroscope/adi,adxrs290.yaml +++ b/Documentation/devicetree/bindings/iio/gyroscope/adi,adxrs290.yaml @@ -50,13 +50,13 @@ examples: #address-cells = <1>; #size-cells = <0>; gyro@0 { - compatible = "adi,adxrs290"; - reg = <0>; - spi-max-frequency = <5000000>; - spi-cpol; - spi-cpha; - interrupt-parent = <&gpio>; - interrupts = <25 IRQ_TYPE_EDGE_RISING>; + compatible = "adi,adxrs290"; + reg = <0>; + spi-max-frequency = <5000000>; + spi-cpol; + spi-cpha; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_EDGE_RISING>; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/gyroscope/nxp,fxas21002c.yaml b/Documentation/devicetree/bindings/iio/gyroscope/nxp,fxas21002c.yaml index 2c900e9dddc6..297d519d68f2 100644 --- a/Documentation/devicetree/bindings/iio/gyroscope/nxp,fxas21002c.yaml +++ b/Documentation/devicetree/bindings/iio/gyroscope/nxp,fxas21002c.yaml @@ -65,34 +65,34 @@ examples: - | #include <dt-bindings/interrupt-controller/irq.h> - i2c0 { + i2c { #address-cells = <1>; #size-cells = <0>; gyroscope@20 { - compatible = "nxp,fxas21002c"; - reg = <0x20>; + compatible = "nxp,fxas21002c"; + reg = <0x20>; - vdd-supply = <®_peri_3p15v>; - vddio-supply = <®_peri_3p15v>; + vdd-supply = <®_peri_3p15v>; + vddio-supply = <®_peri_3p15v>; - interrupt-parent = <&gpio1>; - interrupts = <7 IRQ_TYPE_EDGE_RISING>; - interrupt-names = "INT1"; + interrupt-parent = <&gpio1>; + interrupts = <7 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "INT1"; }; }; - spi0 { + spi { #address-cells = <1>; #size-cells = <0>; gyroscope@0 { - compatible = "nxp,fxas21002c"; - reg = <0x0>; + compatible = "nxp,fxas21002c"; + reg = <0x0>; - spi-max-frequency = <2000000>; + spi-max-frequency = <2000000>; - interrupt-parent = <&gpio2>; - interrupts = <7 IRQ_TYPE_EDGE_RISING>; - interrupt-names = "INT2"; + interrupt-parent = <&gpio2>; + interrupts = <7 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "INT2"; }; }; diff --git a/Documentation/devicetree/bindings/iio/health/ti,afe4403.yaml b/Documentation/devicetree/bindings/iio/health/ti,afe4403.yaml index 6c5ad426a016..b9b5beac33b2 100644 --- a/Documentation/devicetree/bindings/iio/health/ti,afe4403.yaml +++ b/Documentation/devicetree/bindings/iio/health/ti,afe4403.yaml @@ -42,7 +42,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - heart_mon@0 { + heart-mon@0 { compatible = "ti,afe4403"; reg = <0>; spi-max-frequency = <10000000>; diff --git a/Documentation/devicetree/bindings/iio/health/ti,afe4404.yaml b/Documentation/devicetree/bindings/iio/health/ti,afe4404.yaml index c0e815d9999e..2958c4ca75b4 100644 --- a/Documentation/devicetree/bindings/iio/health/ti,afe4404.yaml +++ b/Documentation/devicetree/bindings/iio/health/ti,afe4404.yaml @@ -39,7 +39,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - heart_mon@58 { + heart-mon@58 { compatible = "ti,afe4404"; reg = <0x58>; tx-supply = <&vbat>; diff --git a/Documentation/devicetree/bindings/iio/humidity/dht11.yaml b/Documentation/devicetree/bindings/iio/humidity/dht11.yaml index 2247481d0203..0103f4238942 100644 --- a/Documentation/devicetree/bindings/iio/humidity/dht11.yaml +++ b/Documentation/devicetree/bindings/iio/humidity/dht11.yaml @@ -34,7 +34,7 @@ additionalProperties: false examples: - | - humidity_sensor { + humidity-sensor { compatible = "dht11"; gpios = <&gpio0 6 0>; }; diff --git a/Documentation/devicetree/bindings/iio/humidity/ti,hdc2010.yaml b/Documentation/devicetree/bindings/iio/humidity/ti,hdc2010.yaml index 88384b69f917..a2bc1fa92da0 100644 --- a/Documentation/devicetree/bindings/iio/humidity/ti,hdc2010.yaml +++ b/Documentation/devicetree/bindings/iio/humidity/ti,hdc2010.yaml @@ -35,12 +35,12 @@ additionalProperties: false examples: - | - i2c0 { - #address-cells = <1>; - #size-cells = <0>; - - humidity@40 { - compatible = "ti,hdc2010"; - reg = <0x40>; - }; + i2c { + #address-cells = <1>; + #size-cells = <0>; + + humidity@40 { + compatible = "ti,hdc2010"; + reg = <0x40>; + }; }; diff --git a/Documentation/devicetree/bindings/iio/imu/adi,adis16460.yaml b/Documentation/devicetree/bindings/iio/imu/adi,adis16460.yaml index d166dbca18c3..4e43c80e5119 100644 --- a/Documentation/devicetree/bindings/iio/imu/adi,adis16460.yaml +++ b/Documentation/devicetree/bindings/iio/imu/adi,adis16460.yaml @@ -42,7 +42,7 @@ examples: - | #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/interrupt-controller/irq.h> - spi0 { + spi { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/iio/imu/adi,adis16475.yaml b/Documentation/devicetree/bindings/iio/imu/adi,adis16475.yaml index 5dbfae80bb28..c73533c54588 100644 --- a/Documentation/devicetree/bindings/iio/imu/adi,adis16475.yaml +++ b/Documentation/devicetree/bindings/iio/imu/adi,adis16475.yaml @@ -114,17 +114,17 @@ examples: - | #include <dt-bindings/interrupt-controller/irq.h> spi { - #address-cells = <1>; - #size-cells = <0>; - - adis16475: adis16475-3@0 { - compatible = "adi,adis16475-3"; - reg = <0>; - spi-cpha; - spi-cpol; - spi-max-frequency = <2000000>; - interrupts = <4 IRQ_TYPE_EDGE_RISING>; - interrupt-parent = <&gpio>; - }; + #address-cells = <1>; + #size-cells = <0>; + + adis16475: adis16475-3@0 { + compatible = "adi,adis16475-3"; + reg = <0>; + spi-cpha; + spi-cpol; + spi-max-frequency = <2000000>; + interrupts = <4 IRQ_TYPE_EDGE_RISING>; + interrupt-parent = <&gpio>; + }; }; ... diff --git a/Documentation/devicetree/bindings/iio/imu/bosch,bmi160.yaml b/Documentation/devicetree/bindings/iio/imu/bosch,bmi160.yaml index a0760382548d..47cfba939ca6 100644 --- a/Documentation/devicetree/bindings/iio/imu/bosch,bmi160.yaml +++ b/Documentation/devicetree/bindings/iio/imu/bosch,bmi160.yaml @@ -64,16 +64,16 @@ examples: #size-cells = <0>; bmi160@68 { - compatible = "bosch,bmi160"; - reg = <0x68>; - vdd-supply = <&pm8916_l17>; - vddio-supply = <&pm8916_l6>; - interrupt-parent = <&gpio4>; - interrupts = <12 IRQ_TYPE_EDGE_RISING>; - interrupt-names = "INT1"; - mount-matrix = "0", "1", "0", - "-1", "0", "0", - "0", "0", "1"; + compatible = "bosch,bmi160"; + reg = <0x68>; + vdd-supply = <&pm8916_l17>; + vddio-supply = <&pm8916_l6>; + interrupt-parent = <&gpio4>; + interrupts = <12 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "INT1"; + mount-matrix = "0", "1", "0", + "-1", "0", "0", + "0", "0", "1"; }; }; - | @@ -84,11 +84,11 @@ examples: #size-cells = <0>; bmi160@0 { - compatible = "bosch,bmi160"; - reg = <0>; - spi-max-frequency = <10000000>; - interrupt-parent = <&gpio2>; - interrupts = <12 IRQ_TYPE_EDGE_RISING>; - interrupt-names = "INT2"; + compatible = "bosch,bmi160"; + reg = <0>; + spi-max-frequency = <10000000>; + interrupt-parent = <&gpio2>; + interrupts = <12 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "INT2"; }; }; diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml index 13c9abdd3131..7cd05bcbee31 100644 --- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml +++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml @@ -65,35 +65,35 @@ examples: - | #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/interrupt-controller/irq.h> - i2c0 { + i2c { #address-cells = <1>; #size-cells = <0>; icm42605@68 { - compatible = "invensense,icm42605"; - reg = <0x68>; - interrupt-parent = <&gpio2>; - interrupts = <7 IRQ_TYPE_EDGE_FALLING>; - vdd-supply = <&vdd>; - vddio-supply = <&vddio>; + compatible = "invensense,icm42605"; + reg = <0x68>; + interrupt-parent = <&gpio2>; + interrupts = <7 IRQ_TYPE_EDGE_FALLING>; + vdd-supply = <&vdd>; + vddio-supply = <&vddio>; }; }; - | #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/interrupt-controller/irq.h> - spi0 { + spi { #address-cells = <1>; #size-cells = <0>; icm42602@0 { - compatible = "invensense,icm42602"; - reg = <0>; - spi-max-frequency = <24000000>; - spi-cpha; - spi-cpol; - interrupt-parent = <&gpio1>; - interrupts = <2 IRQ_TYPE_EDGE_FALLING>; - vdd-supply = <&vdd>; - vddio-supply = <&vddio>; + compatible = "invensense,icm42602"; + reg = <0>; + spi-max-frequency = <24000000>; + spi-cpha; + spi-cpol; + interrupt-parent = <&gpio1>; + interrupts = <2 IRQ_TYPE_EDGE_FALLING>; + vdd-supply = <&vdd>; + vddio-supply = <&vddio>; }; }; diff --git a/Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml b/Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml index 24416b59b782..688100b240bc 100644 --- a/Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml +++ b/Documentation/devicetree/bindings/iio/imu/nxp,fxos8700.yaml @@ -49,33 +49,33 @@ examples: - | #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/interrupt-controller/irq.h> - i2c0 { + i2c { #address-cells = <1>; #size-cells = <0>; fxos8700@1e { - compatible = "nxp,fxos8700"; - reg = <0x1e>; + compatible = "nxp,fxos8700"; + reg = <0x1e>; - interrupt-parent = <&gpio2>; - interrupts = <7 IRQ_TYPE_EDGE_RISING>; - interrupt-names = "INT1"; + interrupt-parent = <&gpio2>; + interrupts = <7 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "INT1"; }; }; - | #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/interrupt-controller/irq.h> - spi0 { + spi { #address-cells = <1>; #size-cells = <0>; fxos8700@0 { - compatible = "nxp,fxos8700"; - reg = <0>; + compatible = "nxp,fxos8700"; + reg = <0>; - spi-max-frequency = <1000000>; - interrupt-parent = <&gpio1>; - interrupts = <7 IRQ_TYPE_EDGE_RISING>; - interrupt-names = "INT2"; + spi-max-frequency = <1000000>; + interrupt-parent = <&gpio1>; + interrupts = <7 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "INT2"; }; }; diff --git a/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml b/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml index 68b481c63318..decf022335d8 100644 --- a/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml +++ b/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml @@ -63,7 +63,7 @@ properties: description: if defined provides VDD IO power to the sensor. st,drdy-int-pin: - $ref: '/schemas/types.yaml#/definitions/uint32' + $ref: /schemas/types.yaml#/definitions/uint32 description: | The pin on the package that will be used to signal data ready enum: diff --git a/Documentation/devicetree/bindings/iio/magnetometer/ti,tmag5273.yaml b/Documentation/devicetree/bindings/iio/magnetometer/ti,tmag5273.yaml new file mode 100644 index 000000000000..121d540b7b6e --- /dev/null +++ b/Documentation/devicetree/bindings/iio/magnetometer/ti,tmag5273.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/magnetometer/ti,tmag5273.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor + +maintainers: + - Gerald Loacker <gerald.loacker@wolfvision.net> + +description: + The TI TMAG5273 is a low-power linear 3D Hall-effect sensor. This device + integrates three independent Hall-effect sensors in the X, Y, and Z axes. + The device has an integrated temperature sensor available. The TMAG5273 + can be configured through the I2C interface to enable any combination of + magnetic axes and temperature measurements. An integrated angle calculation + engine (CORDIC) provides full 360° angular position information for both + on-axis and off-axis angle measurement topologies. The angle calculation is + performed using two user-selected magnetic axes. + +properties: + compatible: + const: ti,tmag5273 + + reg: + maxItems: 1 + + "#io-channel-cells": + const: 1 + + ti,angle-measurement: + $ref: /schemas/types.yaml#/definitions/string + description: + Enables angle measurement in the selected plane. + If not specified, "x-y" will be anables as default. + enum: + - off + - x-y + - y-z + - x-z + + vcc-supply: + description: + A regulator providing 1.7 V to 3.6 V supply voltage on the VCC pin, + typically 3.3 V. + + interrupts: + description: + The low active interrupt can be configured to be fixed width or latched. + Interrupt events can be configured to be generated from magnetic + thresholds or when a conversion is completed. + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + magnetometer@35 { + compatible = "ti,tmag5273"; + reg = <0x35>; + #io-channel-cells = <1>; + ti,angle-measurement = "x-z"; + vcc-supply = <&vcc3v3>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/magnetometer/yamaha,yas530.yaml b/Documentation/devicetree/bindings/iio/magnetometer/yamaha,yas530.yaml index 9438fffaf0ba..877226e9219b 100644 --- a/Documentation/devicetree/bindings/iio/magnetometer/yamaha,yas530.yaml +++ b/Documentation/devicetree/bindings/iio/magnetometer/yamaha,yas530.yaml @@ -91,12 +91,12 @@ examples: #size-cells = <0>; magnetometer@2e { - compatible = "yamaha,yas530"; - reg = <0x2e>; - vdd-supply = <&ldo1_reg>; - iovdd-supply = <&ldo2_reg>; - reset-gpios = <&gpio6 12 GPIO_ACTIVE_LOW>; - interrupts = <13 IRQ_TYPE_EDGE_RISING>; + compatible = "yamaha,yas530"; + reg = <0x2e>; + vdd-supply = <&ldo1_reg>; + iovdd-supply = <&ldo2_reg>; + reset-gpios = <&gpio6 12 GPIO_ACTIVE_LOW>; + interrupts = <13 IRQ_TYPE_EDGE_RISING>; }; }; @@ -105,8 +105,8 @@ examples: #size-cells = <0>; magnetometer@2e { - compatible = "yamaha,yas539"; - reg = <0x2e>; - vdd-supply = <&ldo1_reg>; + compatible = "yamaha,yas539"; + reg = <0x2e>; + vdd-supply = <&ldo1_reg>; }; }; diff --git a/Documentation/devicetree/bindings/iio/potentiometer/adi,ad5272.yaml b/Documentation/devicetree/bindings/iio/potentiometer/adi,ad5272.yaml index 0ebb6725a1af..b8d7083c97f8 100644 --- a/Documentation/devicetree/bindings/iio/potentiometer/adi,ad5272.yaml +++ b/Documentation/devicetree/bindings/iio/potentiometer/adi,ad5272.yaml @@ -44,7 +44,7 @@ examples: potentiometer@2f { compatible = "adi,ad5272-020"; - reg = <0x2F>; + reg = <0x2f>; reset-gpios = <&gpio3 6 GPIO_ACTIVE_LOW>; }; }; diff --git a/Documentation/devicetree/bindings/iio/pressure/asc,dlhl60d.yaml b/Documentation/devicetree/bindings/iio/pressure/asc,dlhl60d.yaml index 1f9fe15b4b3c..9fb8d773efa3 100644 --- a/Documentation/devicetree/bindings/iio/pressure/asc,dlhl60d.yaml +++ b/Documentation/devicetree/bindings/iio/pressure/asc,dlhl60d.yaml @@ -39,7 +39,7 @@ examples: - | #include <dt-bindings/interrupt-controller/irq.h> - i2c0 { + i2c { #address-cells = <1>; #size-cells = <0>; diff --git a/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml b/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml index 72cd2c2d3f17..63885af6a74b 100644 --- a/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml +++ b/Documentation/devicetree/bindings/iio/pressure/bmp085.yaml @@ -60,16 +60,16 @@ examples: - | #include <dt-bindings/gpio/gpio.h> #include <dt-bindings/interrupt-controller/irq.h> - i2c0 { - #address-cells = <1>; - #size-cells = <0>; - pressure@77 { - compatible = "bosch,bmp085"; - reg = <0x77>; - interrupt-parent = <&gpio0>; - interrupts = <25 IRQ_TYPE_EDGE_RISING>; - reset-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>; - vddd-supply = <&foo>; - vdda-supply = <&bar>; - }; + i2c { + #address-cells = <1>; + #size-cells = <0>; + pressure@77 { + compatible = "bosch,bmp085"; + reg = <0x77>; + interrupt-parent = <&gpio0>; + interrupts = <25 IRQ_TYPE_EDGE_RISING>; + reset-gpios = <&gpio0 26 GPIO_ACTIVE_LOW>; + vddd-supply = <&foo>; + vdda-supply = <&bar>; + }; }; diff --git a/Documentation/devicetree/bindings/iio/proximity/ams,as3935.yaml b/Documentation/devicetree/bindings/iio/proximity/ams,as3935.yaml index 710d3b9a86d9..c999994e19e3 100644 --- a/Documentation/devicetree/bindings/iio/proximity/ams,as3935.yaml +++ b/Documentation/devicetree/bindings/iio/proximity/ams,as3935.yaml @@ -60,7 +60,7 @@ examples: #address-cells = <1>; #size-cells = <0>; - lightning@0 { + lightning@0 { compatible = "ams,as3935"; reg = <0>; spi-max-frequency = <400000>; diff --git a/Documentation/devicetree/bindings/iio/proximity/google,cros-ec-mkbp-proximity.yaml b/Documentation/devicetree/bindings/iio/proximity/google,cros-ec-mkbp-proximity.yaml index 00e3b59641d2..d4e09d2dcd21 100644 --- a/Documentation/devicetree/bindings/iio/proximity/google,cros-ec-mkbp-proximity.yaml +++ b/Documentation/devicetree/bindings/iio/proximity/google,cros-ec-mkbp-proximity.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/iio/proximity/google,cros-ec-mkbp-proximity.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/iio/proximity/semtech,sx9360.yaml b/Documentation/devicetree/bindings/iio/proximity/semtech,sx9360.yaml index f088c5d2be99..ad0bb44f41b6 100644 --- a/Documentation/devicetree/bindings/iio/proximity/semtech,sx9360.yaml +++ b/Documentation/devicetree/bindings/iio/proximity/semtech,sx9360.yaml @@ -36,7 +36,7 @@ properties: const: 1 semtech,resolution: - $ref: /schemas/types.yaml#/definitions/uint32-array + $ref: /schemas/types.yaml#/definitions/uint32 enum: [8, 16, 32, 64, 128, 256, 512, 1024] description: Capacitance measurement resolution. For both phases, "reference" and diff --git a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml index 250439b13152..c6201976378f 100644 --- a/Documentation/devicetree/bindings/iio/st,st-sensors.yaml +++ b/Documentation/devicetree/bindings/iio/st,st-sensors.yaml @@ -39,6 +39,7 @@ properties: - st,lis3lv02dl-accel - st,lng2dm-accel - st,lsm303agr-accel + - st,lsm303c-accel - st,lsm303dl-accel - st,lsm303dlh-accel - st,lsm303dlhc-accel @@ -66,6 +67,7 @@ properties: - st,lis2mdl - st,lis3mdl-magn - st,lsm303agr-magn + - st,lsm303c-magn - st,lsm303dlh-magn - st,lsm303dlhc-magn - st,lsm303dlm-magn diff --git a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml index b69813f281da..f44fc32ce87e 100644 --- a/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml @@ -472,75 +472,74 @@ examples: #size-cells = <0>; temperature-sensor@0 { - compatible = "adi,ltc2983"; - reg = <0>; - - #address-cells = <1>; - #size-cells = <0>; - - interrupts = <20 IRQ_TYPE_EDGE_RISING>; - interrupt-parent = <&gpio>; - - thermocouple@18 { - reg = <18>; - adi,sensor-type = <8>; //Type B - adi,sensor-oc-current-microamp = <10>; - adi,cold-junction-handle = <&diode5>; - }; - - diode5: diode@5 { - reg = <5>; - adi,sensor-type = <28>; - }; - - rsense2: rsense@2 { - reg = <2>; - adi,sensor-type = <29>; - adi,rsense-val-milli-ohms = <1200000>; //1.2Kohms - }; - - rtd@14 { - reg = <14>; - adi,sensor-type = <15>; //PT1000 - /*2-wire, internal gnd, no current rotation*/ - adi,number-of-wires = <2>; - adi,rsense-share; - adi,excitation-current-microamp = <500>; - adi,rsense-handle = <&rsense2>; - }; - - adc@10 { - reg = <10>; - adi,sensor-type = <30>; - adi,single-ended; - }; - - thermistor@12 { - reg = <12>; - adi,sensor-type = <26>; //Steinhart - adi,rsense-handle = <&rsense2>; - adi,custom-steinhart = <0x00F371EC 0x12345678 - 0x2C0F8733 0x10018C66 0xA0FEACCD - 0x90021D99>; //6 entries - }; - - thermocouple@20 { - reg = <20>; - adi,sensor-type = <9>; //custom thermocouple - adi,single-ended; - adi,custom-thermocouple = - /bits/ 64 <(-50220000) 0>, - /bits/ 64 <(-30200000) 99100000>, - /bits/ 64 <(-5300000) 135400000>, - /bits/ 64 <0 273150000>, - /bits/ 64 <40200000 361200000>, - /bits/ 64 <55300000 522100000>, - /bits/ 64 <88300000 720300000>, - /bits/ 64 <132200000 811200000>, - /bits/ 64 <188700000 922500000>, - /bits/ 64 <460400000 1000000000>; //10 pairs - }; - + compatible = "adi,ltc2983"; + reg = <0>; + + #address-cells = <1>; + #size-cells = <0>; + + interrupts = <20 IRQ_TYPE_EDGE_RISING>; + interrupt-parent = <&gpio>; + + thermocouple@18 { + reg = <18>; + adi,sensor-type = <8>; //Type B + adi,sensor-oc-current-microamp = <10>; + adi,cold-junction-handle = <&diode5>; + }; + + diode5: diode@5 { + reg = <5>; + adi,sensor-type = <28>; + }; + + rsense2: rsense@2 { + reg = <2>; + adi,sensor-type = <29>; + adi,rsense-val-milli-ohms = <1200000>; //1.2Kohms + }; + + rtd@14 { + reg = <14>; + adi,sensor-type = <15>; //PT1000 + /*2-wire, internal gnd, no current rotation*/ + adi,number-of-wires = <2>; + adi,rsense-share; + adi,excitation-current-microamp = <500>; + adi,rsense-handle = <&rsense2>; + }; + + adc@10 { + reg = <10>; + adi,sensor-type = <30>; + adi,single-ended; + }; + + thermistor@12 { + reg = <12>; + adi,sensor-type = <26>; //Steinhart + adi,rsense-handle = <&rsense2>; + adi,custom-steinhart = <0x00f371ec 0x12345678 + 0x2c0f8733 0x10018c66 0xa0feaccd + 0x90021d99>; //6 entries + }; + + thermocouple@20 { + reg = <20>; + adi,sensor-type = <9>; //custom thermocouple + adi,single-ended; + adi,custom-thermocouple = + /bits/ 64 <(-50220000) 0>, + /bits/ 64 <(-30200000) 99100000>, + /bits/ 64 <(-5300000) 135400000>, + /bits/ 64 <0 273150000>, + /bits/ 64 <40200000 361200000>, + /bits/ 64 <55300000 522100000>, + /bits/ 64 <88300000 720300000>, + /bits/ 64 <132200000 811200000>, + /bits/ 64 <188700000 922500000>, + /bits/ 64 <460400000 1000000000>; //10 pairs + }; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/temperature/maxim,max31865.yaml b/Documentation/devicetree/bindings/iio/temperature/maxim,max31865.yaml index a2823ed6867b..7cc365e0ebc8 100644 --- a/Documentation/devicetree/bindings/iio/temperature/maxim,max31865.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/maxim,max31865.yaml @@ -43,12 +43,12 @@ examples: #address-cells = <1>; #size-cells = <0>; - temp_sensor@0 { - compatible = "maxim,max31865"; - reg = <0>; - spi-max-frequency = <400000>; - spi-cpha; - maxim,3-wire; + temperature-sensor@0 { + compatible = "maxim,max31865"; + reg = <0>; + spi-max-frequency = <400000>; + spi-cpha; + maxim,3-wire; }; }; ... diff --git a/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml b/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml index 347bc16a4671..c4f1c69f9330 100644 --- a/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml +++ b/Documentation/devicetree/bindings/iio/temperature/ti,tmp117.yaml @@ -1,10 +1,10 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: "http://devicetree.org/schemas/iio/temperature/ti,tmp117.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/iio/temperature/ti,tmp117.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# -title: "TI TMP117 - Digital temperature sensor with integrated NV memory" +title: TI TMP117 - Digital temperature sensor with integrated NV memory description: | TI TMP117 - Digital temperature sensor with integrated NV memory that supports diff --git a/Documentation/devicetree/bindings/misc/xlnx,tmr-inject.yaml b/Documentation/devicetree/bindings/misc/xlnx,tmr-inject.yaml new file mode 100644 index 000000000000..1b6020e4ec27 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/xlnx,tmr-inject.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/misc/xlnx,tmr-inject.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Xilinx Triple Modular Redundancy(TMR) Inject IP + +maintainers: + - Appana Durga Kedareswara rao <appana.durga.kedareswara.rao@amd.com> + +description: | + The Triple Modular Redundancy(TMR) Inject core provides functional fault + injection by changing selected MicroBlaze instructions, which provides the + possibility to verify that the TMR subsystem error detection and fault + recovery logic is working properly. + +properties: + compatible: + enum: + - xlnx,tmr-inject-1.0 + + reg: + maxItems: 1 + + xlnx,magic: + minimum: 0 + maximum: 255 + description: | + Magic number, When configured it allows the controller to perform + recovery. + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - xlnx,magic + +additionalProperties: false + +examples: + - | + fault-inject@44a30000 { + compatible = "xlnx,tmr-inject-1.0"; + reg = <0x44a10000 0x10000>; + xlnx,magic = <0x46>; + }; diff --git a/Documentation/devicetree/bindings/misc/xlnx,tmr-manager.yaml b/Documentation/devicetree/bindings/misc/xlnx,tmr-manager.yaml new file mode 100644 index 000000000000..27de12147a52 --- /dev/null +++ b/Documentation/devicetree/bindings/misc/xlnx,tmr-manager.yaml @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/misc/xlnx,tmr-manager.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Xilinx Triple Modular Redundancy(TMR) Manager IP + +maintainers: + - Appana Durga Kedareswara rao <appana.durga.kedareswara.rao@amd.com> + +description: | + The Triple Modular Redundancy(TMR) Manager is responsible for handling the + TMR subsystem state, including fault detection and error recovery. The core + is triplicated in each of the sub-blocks in the TMR subsystem, and provides + majority voting of its internal state. + +properties: + compatible: + enum: + - xlnx,tmr-manager-1.0 + + reg: + maxItems: 1 + + xlnx,magic1: + minimum: 0 + maximum: 255 + description: + Magic byte 1, When configured it allows the controller to perform + recovery. + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - reg + - xlnx,magic1 + +additionalProperties: false + +examples: + - | + tmr-manager@44a10000 { + compatible = "xlnx,tmr-manager-1.0"; + reg = <0x44a10000 0x10000>; + xlnx,magic1 = <0x46>; + }; diff --git a/Documentation/kbuild/kbuild.rst b/Documentation/kbuild/kbuild.rst index 08f575e6236c..5202186728b4 100644 --- a/Documentation/kbuild/kbuild.rst +++ b/Documentation/kbuild/kbuild.rst @@ -278,6 +278,13 @@ To get all available archs you can also specify all. E.g.:: $ make ALLSOURCE_ARCHS=all tags +IGNORE_DIRS +----------- +For tags/TAGS/cscope targets, you can choose which directories won't +be included in the databases, separated by blank space. E.g.:: + + $ make IGNORE_DIRS="drivers/gpu/drm/radeon tools" cscope + KBUILD_BUILD_TIMESTAMP ---------------------- Setting this to a date string overrides the timestamp used in the diff --git a/Documentation/trace/coresight/coresight-tpda.rst b/Documentation/trace/coresight/coresight-tpda.rst new file mode 100644 index 000000000000..a37f387ceaea --- /dev/null +++ b/Documentation/trace/coresight/coresight-tpda.rst @@ -0,0 +1,52 @@ +.. SPDX-License-Identifier: GPL-2.0 + +================================================================= +The trace performance monitoring and diagnostics aggregator(TPDA) +================================================================= + + :Author: Jinlong Mao <quic_jinlmao@quicinc.com> + :Date: January 2023 + +Hardware Description +-------------------- + +TPDA - The trace performance monitoring and diagnostics aggregator or +TPDA in short serves as an arbitration and packetization engine for the +performance monitoring and diagnostics network specification. +The primary use case of the TPDA is to provide packetization, funneling +and timestamping of Monitor data. + + +Sysfs files and directories +--------------------------- +Root: ``/sys/bus/coresight/devices/tpda<N>`` + +Config details +--------------------------- + +The tpdm and tpda nodes should be observed at the coresight path +"/sys/bus/coresight/devices". +e.g. +/sys/bus/coresight/devices # ls -l | grep tpd +tpda0 -> ../../../devices/platform/soc@0/6004000.tpda/tpda0 +tpdm0 -> ../../../devices/platform/soc@0/6c08000.mm.tpdm/tpdm0 + +We can use the commands are similar to the below to validate TPDMs. +Enable coresight sink first. The port of tpda which is connected to +the tpdm will be enabled after commands below. + +echo 1 > /sys/bus/coresight/devices/tmc_etf0/enable_sink +echo 1 > /sys/bus/coresight/devices/tpdm0/enable_source +echo 1 > /sys/bus/coresight/devices/tpdm0/integration_test +echo 2 > /sys/bus/coresight/devices/tpdm0/integration_test + +The test data will be collected in the coresight sink which is enabled. +If rwp register of the sink is keeping updating when do +integration_test (by cat tmc_etf0/mgmt/rwp), it means there is data +generated from TPDM to sink. + +There must be a tpda between tpdm and the sink. When there are some +other trace event hw components in the same HW block with tpdm, tpdm +and these hw components will connect to the coresight funnel. When +there is only tpdm trace hw in the HW block, tpdm will connect to +tpda directly. diff --git a/Documentation/trace/coresight/coresight-tpdm.rst b/Documentation/trace/coresight/coresight-tpdm.rst new file mode 100644 index 000000000000..72fd5c855d45 --- /dev/null +++ b/Documentation/trace/coresight/coresight-tpdm.rst @@ -0,0 +1,45 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================================================== +Trace performance monitoring and diagnostics monitor(TPDM) +========================================================== + + :Author: Jinlong Mao <quic_jinlmao@quicinc.com> + :Date: January 2023 + +Hardware Description +-------------------- +TPDM - The trace performance monitoring and diagnostics monitor or TPDM in +short serves as data collection component for various dataset types. +The primary use case of the TPDM is to collect data from different data +sources and send it to a TPDA for packetization, timestamping and funneling. + +Sysfs files and directories +--------------------------- +Root: ``/sys/bus/coresight/devices/tpdm<N>`` + +---- + +:File: ``enable_source`` (RW) +:Notes: + - > 0 : enable the datasets of TPDM. + + - = 0 : disable the datasets of TPDM. + +:Syntax: + ``echo 1 > enable_source`` + +---- + +:File: ``integration_test`` (wo) +:Notes: + Integration test will generate test data for tpdm. + +:Syntax: + ``echo value > integration_test`` + + value - 1 or 2. + +---- + +.. This text is intentionally added to make Sphinx happy. diff --git a/Documentation/trace/coresight/ultrasoc-smb.rst b/Documentation/trace/coresight/ultrasoc-smb.rst new file mode 100644 index 000000000000..a05488a75ff0 --- /dev/null +++ b/Documentation/trace/coresight/ultrasoc-smb.rst @@ -0,0 +1,83 @@ +.. SPDX-License-Identifier: GPL-2.0 + +====================================== +UltraSoc - HW Assisted Tracing on SoC +====================================== + :Author: Qi Liu <liuqi115@huawei.com> + :Date: January 2023 + +Introduction +------------ + +UltraSoc SMB is a per SCCL (Super CPU Cluster) hardware. It provides a +way to buffer and store CPU trace messages in a region of shared system +memory. The device acts as a coresight sink device and the +corresponding trace generators (ETM) are attached as source devices. + +Sysfs files and directories +--------------------------- + +The SMB devices appear on the existing coresight bus alongside other +devices:: + + $# ls /sys/bus/coresight/devices/ + ultra_smb0 ultra_smb1 ultra_smb2 ultra_smb3 + +The ``ultra_smb<N>`` names SMB device associated with SCCL.:: + + $# ls /sys/bus/coresight/devices/ultra_smb0 + enable_sink mgmt + $# ls /sys/bus/coresight/devices/ultra_smb0/mgmt + buf_size buf_status read_pos write_pos + +Key file items are: + + * ``read_pos``: Shows the value on the read pointer register. + * ``write_pos``: Shows the value on the write pointer register. + * ``buf_status``: Shows the value on the status register. + BIT(0) is zero value which means the buffer is empty. + * ``buf_size``: Shows the buffer size of each device. + +Firmware Bindings +----------------- + +The device is only supported with ACPI. Its binding describes device +identifier, resource information and graph structure. + +The device is identified as ACPI HID "HISI03A1". Device resources are allocated +using the _CRS method. Each device must present two base address; the first one +is the configuration base address of the device, the second one is the 32-bit +base address of shared system memory. + +Example:: + + Device(USMB) { \ + Name(_HID, "HISI03A1") \ + Name(_CRS, ResourceTemplate() { \ + QWordMemory (ResourceConsumer, , MinFixed, MaxFixed, NonCacheable, \ + ReadWrite, 0x0, 0x95100000, 0x951FFFFF, 0x0, 0x100000) \ + QWordMemory (ResourceConsumer, , MinFixed, MaxFixed, Cacheable, \ + ReadWrite, 0x0, 0x50000000, 0x53FFFFFF, 0x0, 0x4000000) \ + }) \ + Name(_DSD, Package() { \ + ToUUID("ab02a46b-74c7-45a2-bd68-f7d344ef2153"), \ + /* Use CoreSight Graph ACPI bindings to describe connections topology */ + Package() { \ + 0, \ + 1, \ + Package() { \ + 1, \ + ToUUID("3ecbc8b6-1d0e-4fb3-8107-e627f805c6cd"), \ + 8, \ + Package() {0x8, 0, \_SB.S00.SL11.CL28.F008, 0}, \ + Package() {0x9, 0, \_SB.S00.SL11.CL29.F009, 0}, \ + Package() {0xa, 0, \_SB.S00.SL11.CL2A.F010, 0}, \ + Package() {0xb, 0, \_SB.S00.SL11.CL2B.F011, 0}, \ + Package() {0xc, 0, \_SB.S00.SL11.CL2C.F012, 0}, \ + Package() {0xd, 0, \_SB.S00.SL11.CL2D.F013, 0}, \ + Package() {0xe, 0, \_SB.S00.SL11.CL2E.F014, 0}, \ + Package() {0xf, 0, \_SB.S00.SL11.CL2F.F015, 0}, \ + } \ + } \ + }) \ + } diff --git a/MAINTAINERS b/MAINTAINERS index fb1471cb5ed3..69d1e8ad52c5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2089,8 +2089,10 @@ M: Hartley Sweeten <hsweeten@visionengravers.com> M: Alexander Sverdlin <alexander.sverdlin@gmail.com> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained +F: Documentation/devicetree/bindings/iio/adc/cirrus,ep9301-adc.yaml F: arch/arm/mach-ep93xx/ F: arch/arm/mach-ep93xx/include/mach/ +F: drivers/iio/adc/ep93xx_adc.c ARM/CLKDEV SUPPORT M: Russell King <linux@armlinux.org.uk> @@ -2122,6 +2124,7 @@ S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/coresight/linux.git F: Documentation/ABI/testing/sysfs-bus-coresight-devices-* F: Documentation/devicetree/bindings/arm/arm,coresight-*.yaml +F: Documentation/devicetree/bindings/arm/qcom,coresight-*.yaml F: Documentation/devicetree/bindings/arm/arm,embedded-trace-extension.yaml F: Documentation/devicetree/bindings/arm/arm,trace-buffer-extension.yaml F: Documentation/trace/coresight/* @@ -9382,11 +9385,15 @@ F: drivers/perf/hisilicon/hns3_pmu.c HISILICON PTT DRIVER 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/trace/hisi-ptt.rst F: drivers/hwtracing/ptt/ +F: tools/perf/arch/arm64/util/hisi-ptt.c +F: tools/perf/util/hisi-ptt* +F: tools/perf/util/hisi-ptt-decoder/* HISILICON QM DRIVER M: Weili Qian <qianweili@huawei.com> @@ -10574,6 +10581,7 @@ F: drivers/watchdog/mei_wdt.c F: include/linux/mei_aux.h F: include/linux/mei_cl_bus.h F: include/uapi/linux/mei.h +F: include/uapi/linux/uuid.h F: samples/mei/* INTEL MAX 10 BMC MFD DRIVER @@ -10583,7 +10591,7 @@ S: Maintained F: Documentation/ABI/testing/sysfs-driver-intel-m10-bmc F: Documentation/hwmon/intel-m10-bmc-hwmon.rst F: drivers/hwmon/intel-m10-bmc-hwmon.c -F: drivers/mfd/intel-m10-bmc.c +F: drivers/mfd/intel-m10-bmc* F: include/linux/mfd/intel-m10-bmc.h INTEL MENLOW THERMAL DRIVER @@ -13625,6 +13633,19 @@ W: http://www.monstr.eu/fdt/ T: git git://git.monstr.eu/linux-2.6-microblaze.git F: arch/microblaze/ +MICROBLAZE TMR MANAGER +M: Appana Durga Kedareswara rao <appana.durga.kedareswara.rao@amd.com> +S: Supported +F: Documentation/ABI/testing/sysfs-driver-xilinx-tmr-manager +F: Documentation/devicetree/bindings/misc/xlnx,tmr-manager.yaml +F: drivers/misc/xilinx_tmr_manager.c + +MICROBLAZE TMR INJECT +M: Appana Durga Kedareswara rao <appana.durga.kedareswara.rao@amd.com> +S: Supported +F: Documentation/devicetree/bindings/misc/xlnx,tmr-inject.yaml +F: drivers/misc/xilinx_tmr_inject.c + MICROCHIP AT91 DMA DRIVERS M: Ludovic Desroches <ludovic.desroches@microchip.com> M: Tudor Ambarus <tudor.ambarus@linaro.org> @@ -15025,14 +15046,16 @@ S: Maintained F: Documentation/devicetree/bindings/iio/adc/nxp,imx8qxp-adc.yaml F: drivers/iio/adc/imx8qxp-adc.c -NXP i.MX 7D/6SX/6UL AND VF610 ADC DRIVER +NXP i.MX 7D/6SX/6UL/93 AND VF610 ADC DRIVER M: Haibo Chen <haibo.chen@nxp.com> L: linux-iio@vger.kernel.org L: linux-imx@nxp.com S: Maintained F: Documentation/devicetree/bindings/iio/adc/fsl,imx7d-adc.yaml F: Documentation/devicetree/bindings/iio/adc/fsl,vf610-adc.yaml +F: Documentation/devicetree/bindings/iio/adc/nxp,imx93-adc.yaml F: drivers/iio/adc/imx7d_adc.c +F: drivers/iio/adc/imx93_adc.c F: drivers/iio/adc/vf610_adc.c NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER @@ -20802,6 +20825,13 @@ M: Robert Richter <rric@kernel.org> S: Odd Fixes F: drivers/gpio/gpio-thunderx.c +TI ADS7924 ADC DRIVER +M: Hugo Villeneuve <hvilleneuve@dimonoff.com> +L: linux-iio@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/iio/adc/ti,ads7924.yaml +F: drivers/iio/adc/ti-ads7924.c + TI AM437X VPFE DRIVER M: "Lad, Prabhakar" <prabhakar.csengg@gmail.com> L: linux-media@vger.kernel.org @@ -20922,6 +20952,14 @@ S: Maintained F: sound/soc/codecs/isabelle* F: sound/soc/codecs/lm49453* +TI LMP92064 ADC DRIVER +M: Leonard Göhrs <l.goehrs@pengutronix.de> +R: kernel@pengutronix.de +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/adc/ti,lmp92064.yaml +F: drivers/iio/adc/ti-lmp92064.c + TI PCM3060 ASoC CODEC DRIVER M: Kirill Marinushkin <kmarinushkin@birdec.com> L: alsa-devel@alsa-project.org (moderated for non-subscribers) @@ -20935,6 +20973,13 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Odd Fixes F: sound/soc/codecs/tas571x* +TI TMAG5273 MAGNETOMETER DRIVER +M: Gerald Loacker <gerald.loacker@wolfvision.net> +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/magnetometer/ti,tmag5273.yaml +F: drivers/iio/magnetometer/tmag5273.c + TI TRF7970A NFC DRIVER M: Mark Greer <mgreer@animalcreek.com> L: linux-wireless@vger.kernel.org @@ -21824,7 +21869,6 @@ L: linux-kernel@vger.kernel.org S: Maintained T: git git://git.infradead.org/users/hch/uuid.git F: include/linux/uuid.h -F: include/uapi/linux/uuid.h F: lib/test_uuid.c F: lib/uuid.c diff --git a/drivers/accessibility/speakup/main.c b/drivers/accessibility/speakup/main.c index 4733fd6334ab..56c073103cbb 100644 --- a/drivers/accessibility/speakup/main.c +++ b/drivers/accessibility/speakup/main.c @@ -2490,7 +2490,7 @@ MODULE_PARM_DESC(punc_level, "Controls the level of punctuation spoken as the sc MODULE_PARM_DESC(reading_punc, "It controls the level of punctuation when reviewing the screen with speakup's screen review commands."); MODULE_PARM_DESC(cursor_time, "This controls cursor delay when using arrow keys."); MODULE_PARM_DESC(say_control, "This controls if speakup speaks shift, alt and control when those keys are pressed or not."); -MODULE_PARM_DESC(say_word_ctl, "Sets thw say_word_ctl on load."); +MODULE_PARM_DESC(say_word_ctl, "Sets the say_word_ctl on load."); MODULE_PARM_DESC(no_interrupt, "Controls if typing interrupts output from speakup."); MODULE_PARM_DESC(key_echo, "Controls if speakup speaks keys when they are typed. One = on zero = off or don't echo keys."); MODULE_PARM_DESC(cur_phonetic, "Controls if speakup speaks letters phonetically during navigation. One = on zero = off or don't speak phonetically."); diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 880224ec6abb..ca3034afd1dc 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -277,11 +277,11 @@ _binder_proc_lock(struct binder_proc *proc, int line) /** * binder_proc_unlock() - Release spinlock for given binder_proc - * @proc: struct binder_proc to acquire + * @proc: struct binder_proc to acquire * * Release lock acquired via binder_proc_lock() */ -#define binder_proc_unlock(_proc) _binder_proc_unlock(_proc, __LINE__) +#define binder_proc_unlock(proc) _binder_proc_unlock(proc, __LINE__) static void _binder_proc_unlock(struct binder_proc *proc, int line) __releases(&proc->outer_lock) @@ -378,7 +378,7 @@ _binder_node_inner_lock(struct binder_node *node, int line) } /** - * binder_node_unlock() - Release node and inner locks + * binder_node_inner_unlock() - Release node and inner locks * @node: struct binder_node to acquire * * Release lock acquired via binder_node_lock() @@ -1194,13 +1194,13 @@ static int binder_inc_ref_olocked(struct binder_ref *ref, int strong, } /** - * binder_dec_ref() - dec the ref for given handle + * binder_dec_ref_olocked() - dec the ref for given handle * @ref: ref to be decremented * @strong: if true, strong decrement, else weak * * Decrement the ref. * - * Return: true if ref is cleaned up and ready to be freed + * Return: %true if ref is cleaned up and ready to be freed. */ static bool binder_dec_ref_olocked(struct binder_ref *ref, int strong) { @@ -2728,7 +2728,10 @@ binder_find_outdated_transaction_ilocked(struct binder_transaction *t, * * Return: 0 if the transaction was successfully queued * BR_DEAD_REPLY if the target process or thread is dead - * BR_FROZEN_REPLY if the target process or thread is frozen + * BR_FROZEN_REPLY if the target process or thread is frozen and + * the sync transaction was rejected + * BR_TRANSACTION_PENDING_FROZEN if the target process is frozen + * and the async transaction was successfully queued */ static int binder_proc_transaction(struct binder_transaction *t, struct binder_proc *proc, @@ -2738,6 +2741,7 @@ static int binder_proc_transaction(struct binder_transaction *t, bool oneway = !!(t->flags & TF_ONE_WAY); bool pending_async = false; struct binder_transaction *t_outdated = NULL; + bool frozen = false; BUG_ON(!node); binder_node_lock(node); @@ -2751,15 +2755,16 @@ static int binder_proc_transaction(struct binder_transaction *t, binder_inner_proc_lock(proc); if (proc->is_frozen) { + frozen = true; proc->sync_recv |= !oneway; proc->async_recv |= oneway; } - if ((proc->is_frozen && !oneway) || proc->is_dead || + if ((frozen && !oneway) || proc->is_dead || (thread && thread->is_dead)) { binder_inner_proc_unlock(proc); binder_node_unlock(node); - return proc->is_frozen ? BR_FROZEN_REPLY : BR_DEAD_REPLY; + return frozen ? BR_FROZEN_REPLY : BR_DEAD_REPLY; } if (!thread && !pending_async) @@ -2770,7 +2775,7 @@ static int binder_proc_transaction(struct binder_transaction *t, } else if (!pending_async) { binder_enqueue_work_ilocked(&t->work, &proc->todo); } else { - if ((t->flags & TF_UPDATE_TXN) && proc->is_frozen) { + if ((t->flags & TF_UPDATE_TXN) && frozen) { t_outdated = binder_find_outdated_transaction_ilocked(t, &node->async_todo); if (t_outdated) { @@ -2807,14 +2812,17 @@ static int binder_proc_transaction(struct binder_transaction *t, binder_stats_deleted(BINDER_STAT_TRANSACTION); } + if (oneway && frozen) + return BR_TRANSACTION_PENDING_FROZEN; + return 0; } /** * binder_get_node_refs_for_txn() - Get required refs on node for txn * @node: struct binder_node for which to get refs - * @proc: returns @node->proc if valid - * @error: if no @proc then returns BR_DEAD_REPLY + * @procp: returns @node->proc if valid + * @error: if no @procp then returns BR_DEAD_REPLY * * User-space normally keeps the node alive when creating a transaction * since it has a reference to the target. The local strong ref keeps it @@ -2828,8 +2836,8 @@ static int binder_proc_transaction(struct binder_transaction *t, * constructing the transaction, so we take that here as well. * * Return: The target_node with refs taken or NULL if no @node->proc is NULL. - * Also sets @proc if valid. If the @node->proc is NULL indicating that the - * target proc has died, @error is set to BR_DEAD_REPLY + * Also sets @procp if valid. If the @node->proc is NULL indicating that the + * target proc has died, @error is set to BR_DEAD_REPLY. */ static struct binder_node *binder_get_node_refs_for_txn( struct binder_node *node, @@ -3607,9 +3615,17 @@ static void binder_transaction(struct binder_proc *proc, } else { BUG_ON(target_node == NULL); BUG_ON(t->buffer->async_transaction != 1); - binder_enqueue_thread_work(thread, tcomplete); return_error = binder_proc_transaction(t, target_proc, NULL); - if (return_error) + /* + * Let the caller know when async transaction reaches a frozen + * process and is put in a pending queue, waiting for the target + * process to be unfrozen. + */ + if (return_error == BR_TRANSACTION_PENDING_FROZEN) + tcomplete->type = BINDER_WORK_TRANSACTION_PENDING; + binder_enqueue_thread_work(thread, tcomplete); + if (return_error && + return_error != BR_TRANSACTION_PENDING_FROZEN) goto err_dead_proc_or_thread; } if (target_thread) @@ -4440,10 +4456,13 @@ retry: binder_stat_br(proc, thread, cmd); } break; case BINDER_WORK_TRANSACTION_COMPLETE: + case BINDER_WORK_TRANSACTION_PENDING: case BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT: { if (proc->oneway_spam_detection_enabled && w->type == BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT) cmd = BR_ONEWAY_SPAM_SUSPECT; + else if (w->type == BINDER_WORK_TRANSACTION_PENDING) + cmd = BR_TRANSACTION_PENDING_FROZEN; else cmd = BR_TRANSACTION_COMPLETE; binder_inner_proc_unlock(proc); @@ -5006,20 +5025,14 @@ static __poll_t binder_poll(struct file *filp, return 0; } -static int binder_ioctl_write_read(struct file *filp, - unsigned int cmd, unsigned long arg, +static int binder_ioctl_write_read(struct file *filp, unsigned long arg, struct binder_thread *thread) { int ret = 0; struct binder_proc *proc = filp->private_data; - unsigned int size = _IOC_SIZE(cmd); void __user *ubuf = (void __user *)arg; struct binder_write_read bwr; - if (size != sizeof(struct binder_write_read)) { - ret = -EINVAL; - goto out; - } if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { ret = -EFAULT; goto out; @@ -5296,7 +5309,6 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) int ret; struct binder_proc *proc = filp->private_data; struct binder_thread *thread; - unsigned int size = _IOC_SIZE(cmd); void __user *ubuf = (void __user *)arg; /*pr_info("binder_ioctl: %d:%d %x %lx\n", @@ -5318,7 +5330,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) switch (cmd) { case BINDER_WRITE_READ: - ret = binder_ioctl_write_read(filp, cmd, arg, thread); + ret = binder_ioctl_write_read(filp, arg, thread); if (ret) goto err; break; @@ -5361,10 +5373,6 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case BINDER_VERSION: { struct binder_version __user *ver = ubuf; - if (size != sizeof(struct binder_version)) { - ret = -EINVAL; - goto err; - } if (put_user(BINDER_CURRENT_PROTOCOL_VERSION, &ver->protocol_version)) { ret = -EINVAL; @@ -6170,6 +6178,7 @@ static const char * const binder_return_strings[] = { "BR_FAILED_REPLY", "BR_FROZEN_REPLY", "BR_ONEWAY_SPAM_SUSPECT", + "BR_TRANSACTION_PENDING_FROZEN" }; static const char * const binder_command_strings[] = { diff --git a/drivers/android/binder_internal.h b/drivers/android/binder_internal.h index abe19d88c6ec..28ef5b3704b1 100644 --- a/drivers/android/binder_internal.h +++ b/drivers/android/binder_internal.h @@ -133,7 +133,7 @@ enum binder_stat_types { }; struct binder_stats { - atomic_t br[_IOC_NR(BR_ONEWAY_SPAM_SUSPECT) + 1]; + atomic_t br[_IOC_NR(BR_TRANSACTION_PENDING_FROZEN) + 1]; atomic_t bc[_IOC_NR(BC_REPLY_SG) + 1]; atomic_t obj_created[BINDER_STAT_COUNT]; atomic_t obj_deleted[BINDER_STAT_COUNT]; @@ -152,6 +152,7 @@ struct binder_work { enum binder_work_type { BINDER_WORK_TRANSACTION = 1, BINDER_WORK_TRANSACTION_COMPLETE, + BINDER_WORK_TRANSACTION_PENDING, BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT, BINDER_WORK_RETURN_ERROR, BINDER_WORK_NODE, diff --git a/drivers/android/binderfs.c b/drivers/android/binderfs.c index 09b2ce7e4c34..abb9c0980afb 100644 --- a/drivers/android/binderfs.c +++ b/drivers/android/binderfs.c @@ -222,14 +222,14 @@ err: } /** - * binderfs_ctl_ioctl - handle binder device node allocation requests + * binder_ctl_ioctl - handle binder device node allocation requests * * The request handler for the binder-control device. All requests operate on * the binderfs mount the binder-control device resides in: * - BINDER_CTL_ADD * Allocate a new binder device. * - * Return: 0 on success, negative errno on failure + * Return: %0 on success, negative errno on failure. */ static long binder_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) diff --git a/drivers/bus/mhi/Makefile b/drivers/bus/mhi/Makefile index 46981331b38f..354204b0ef3a 100644 --- a/drivers/bus/mhi/Makefile +++ b/drivers/bus/mhi/Makefile @@ -1,5 +1,5 @@ # Host MHI stack -obj-y += host/ +obj-$(CONFIG_MHI_BUS) += host/ # Endpoint MHI stack -obj-y += ep/ +obj-$(CONFIG_MHI_BUS_EP) += ep/ diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index 1dc8a3557a46..dffe03658ff9 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -123,6 +123,13 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele int ret; ch_id = MHI_TRE_GET_CMD_CHID(el); + + /* Check if the channel is supported by the controller */ + if ((ch_id >= mhi_cntrl->max_chan) || !mhi_cntrl->mhi_chan[ch_id].name) { + dev_err(dev, "Channel (%u) not supported!\n", ch_id); + return -ENODEV; + } + mhi_chan = &mhi_cntrl->mhi_chan[ch_id]; ch_ring = &mhi_cntrl->mhi_chan[ch_id].ring; @@ -196,9 +203,11 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele mhi_ep_mmio_disable_chdb(mhi_cntrl, ch_id); /* Send channel disconnect status to client drivers */ - result.transaction_status = -ENOTCONN; - result.bytes_xferd = 0; - mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); + if (mhi_chan->xfer_cb) { + result.transaction_status = -ENOTCONN; + result.bytes_xferd = 0; + mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); + } /* Set channel state to STOP */ mhi_chan->state = MHI_CH_STATE_STOP; @@ -217,7 +226,7 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele mutex_unlock(&mhi_chan->lock); break; case MHI_PKT_TYPE_RESET_CHAN_CMD: - dev_dbg(dev, "Received STOP command for channel (%u)\n", ch_id); + dev_dbg(dev, "Received RESET command for channel (%u)\n", ch_id); if (!ch_ring->started) { dev_err(dev, "Channel (%u) not opened\n", ch_id); return -ENODEV; @@ -228,9 +237,11 @@ static int mhi_ep_process_cmd_ring(struct mhi_ep_ring *ring, struct mhi_ring_ele mhi_ep_ring_reset(mhi_cntrl, ch_ring); /* Send channel disconnect status to client driver */ - result.transaction_status = -ENOTCONN; - result.bytes_xferd = 0; - mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); + if (mhi_chan->xfer_cb) { + result.transaction_status = -ENOTCONN; + result.bytes_xferd = 0; + mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); + } /* Set channel state to DISABLED */ mhi_chan->state = MHI_CH_STATE_DISABLED; @@ -719,24 +730,37 @@ static void mhi_ep_ch_ring_worker(struct work_struct *work) list_del(&itr->node); ring = itr->ring; + chan = &mhi_cntrl->mhi_chan[ring->ch_id]; + mutex_lock(&chan->lock); + + /* + * The ring could've stopped while we waited to grab the (chan->lock), so do + * a sanity check before going further. + */ + if (!ring->started) { + mutex_unlock(&chan->lock); + kfree(itr); + continue; + } + /* Update the write offset for the ring */ ret = mhi_ep_update_wr_offset(ring); if (ret) { dev_err(dev, "Error updating write offset for ring\n"); + mutex_unlock(&chan->lock); kfree(itr); continue; } /* Sanity check to make sure there are elements in the ring */ if (ring->rd_offset == ring->wr_offset) { + mutex_unlock(&chan->lock); kfree(itr); continue; } el = &ring->ring_cache[ring->rd_offset]; - chan = &mhi_cntrl->mhi_chan[ring->ch_id]; - mutex_lock(&chan->lock); dev_dbg(dev, "Processing the ring for channel (%u)\n", ring->ch_id); ret = mhi_ep_process_ch_ring(ring, el); if (ret) { @@ -973,44 +997,25 @@ static void mhi_ep_abort_transfer(struct mhi_ep_cntrl *mhi_cntrl) static void mhi_ep_reset_worker(struct work_struct *work) { struct mhi_ep_cntrl *mhi_cntrl = container_of(work, struct mhi_ep_cntrl, reset_work); - struct device *dev = &mhi_cntrl->mhi_dev->dev; enum mhi_state cur_state; - int ret; - mhi_ep_abort_transfer(mhi_cntrl); + mhi_ep_power_down(mhi_cntrl); + + mutex_lock(&mhi_cntrl->state_lock); - spin_lock_bh(&mhi_cntrl->state_lock); /* Reset MMIO to signal host that the MHI_RESET is completed in endpoint */ mhi_ep_mmio_reset(mhi_cntrl); cur_state = mhi_cntrl->mhi_state; - spin_unlock_bh(&mhi_cntrl->state_lock); /* * Only proceed further if the reset is due to SYS_ERR. The host will * issue reset during shutdown also and we don't need to do re-init in * that case. */ - if (cur_state == MHI_STATE_SYS_ERR) { - mhi_ep_mmio_init(mhi_cntrl); - - /* Set AMSS EE before signaling ready state */ - mhi_ep_mmio_set_env(mhi_cntrl, MHI_EE_AMSS); - - /* All set, notify the host that we are ready */ - ret = mhi_ep_set_ready_state(mhi_cntrl); - if (ret) - return; - - dev_dbg(dev, "READY state notification sent to the host\n"); - - ret = mhi_ep_enable(mhi_cntrl); - if (ret) { - dev_err(dev, "Failed to enable MHI endpoint: %d\n", ret); - return; - } + if (cur_state == MHI_STATE_SYS_ERR) + mhi_ep_power_up(mhi_cntrl); - enable_irq(mhi_cntrl->irq); - } + mutex_unlock(&mhi_cntrl->state_lock); } /* @@ -1089,11 +1094,11 @@ EXPORT_SYMBOL_GPL(mhi_ep_power_up); void mhi_ep_power_down(struct mhi_ep_cntrl *mhi_cntrl) { - if (mhi_cntrl->enabled) + if (mhi_cntrl->enabled) { mhi_ep_abort_transfer(mhi_cntrl); - - kfree(mhi_cntrl->mhi_event); - disable_irq(mhi_cntrl->irq); + kfree(mhi_cntrl->mhi_event); + disable_irq(mhi_cntrl->irq); + } } EXPORT_SYMBOL_GPL(mhi_ep_power_down); @@ -1119,6 +1124,7 @@ void mhi_ep_suspend_channels(struct mhi_ep_cntrl *mhi_cntrl) dev_dbg(&mhi_chan->mhi_dev->dev, "Suspending channel\n"); /* Set channel state to SUSPENDED */ + mhi_chan->state = MHI_CH_STATE_SUSPENDED; tmp &= ~CHAN_CTX_CHSTATE_MASK; tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_SUSPENDED); mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp); @@ -1148,6 +1154,7 @@ void mhi_ep_resume_channels(struct mhi_ep_cntrl *mhi_cntrl) dev_dbg(&mhi_chan->mhi_dev->dev, "Resuming channel\n"); /* Set channel state to RUNNING */ + mhi_chan->state = MHI_CH_STATE_RUNNING; tmp &= ~CHAN_CTX_CHSTATE_MASK; tmp |= FIELD_PREP(CHAN_CTX_CHSTATE_MASK, MHI_CH_STATE_RUNNING); mhi_cntrl->ch_ctx_cache[i].chcfg = cpu_to_le32(tmp); @@ -1381,8 +1388,8 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl, INIT_LIST_HEAD(&mhi_cntrl->st_transition_list); INIT_LIST_HEAD(&mhi_cntrl->ch_db_list); - spin_lock_init(&mhi_cntrl->state_lock); spin_lock_init(&mhi_cntrl->list_lock); + mutex_init(&mhi_cntrl->state_lock); mutex_init(&mhi_cntrl->event_lock); /* Set MHI version and AMSS EE before enumeration */ diff --git a/drivers/bus/mhi/ep/sm.c b/drivers/bus/mhi/ep/sm.c index 3655c19e23c7..fd200b2ac0bb 100644 --- a/drivers/bus/mhi/ep/sm.c +++ b/drivers/bus/mhi/ep/sm.c @@ -63,24 +63,23 @@ int mhi_ep_set_m0_state(struct mhi_ep_cntrl *mhi_cntrl) int ret; /* If MHI is in M3, resume suspended channels */ - spin_lock_bh(&mhi_cntrl->state_lock); + mutex_lock(&mhi_cntrl->state_lock); + old_state = mhi_cntrl->mhi_state; if (old_state == MHI_STATE_M3) mhi_ep_resume_channels(mhi_cntrl); ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M0); - spin_unlock_bh(&mhi_cntrl->state_lock); - if (ret) { mhi_ep_handle_syserr(mhi_cntrl); - return ret; + goto err_unlock; } /* Signal host that the device moved to M0 */ ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_M0); if (ret) { dev_err(dev, "Failed sending M0 state change event\n"); - return ret; + goto err_unlock; } if (old_state == MHI_STATE_READY) { @@ -88,11 +87,14 @@ int mhi_ep_set_m0_state(struct mhi_ep_cntrl *mhi_cntrl) ret = mhi_ep_send_ee_event(mhi_cntrl, MHI_EE_AMSS); if (ret) { dev_err(dev, "Failed sending AMSS EE event\n"); - return ret; + goto err_unlock; } } - return 0; +err_unlock: + mutex_unlock(&mhi_cntrl->state_lock); + + return ret; } int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl) @@ -100,13 +102,12 @@ int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl) struct device *dev = &mhi_cntrl->mhi_dev->dev; int ret; - spin_lock_bh(&mhi_cntrl->state_lock); - ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M3); - spin_unlock_bh(&mhi_cntrl->state_lock); + mutex_lock(&mhi_cntrl->state_lock); + ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_M3); if (ret) { mhi_ep_handle_syserr(mhi_cntrl); - return ret; + goto err_unlock; } mhi_ep_suspend_channels(mhi_cntrl); @@ -115,10 +116,13 @@ int mhi_ep_set_m3_state(struct mhi_ep_cntrl *mhi_cntrl) ret = mhi_ep_send_state_change_event(mhi_cntrl, MHI_STATE_M3); if (ret) { dev_err(dev, "Failed sending M3 state change event\n"); - return ret; + goto err_unlock; } - return 0; +err_unlock: + mutex_unlock(&mhi_cntrl->state_lock); + + return ret; } int mhi_ep_set_ready_state(struct mhi_ep_cntrl *mhi_cntrl) @@ -127,22 +131,24 @@ int mhi_ep_set_ready_state(struct mhi_ep_cntrl *mhi_cntrl) enum mhi_state mhi_state; int ret, is_ready; - spin_lock_bh(&mhi_cntrl->state_lock); + mutex_lock(&mhi_cntrl->state_lock); + /* Ensure that the MHISTATUS is set to RESET by host */ mhi_state = mhi_ep_mmio_masked_read(mhi_cntrl, EP_MHISTATUS, MHISTATUS_MHISTATE_MASK); is_ready = mhi_ep_mmio_masked_read(mhi_cntrl, EP_MHISTATUS, MHISTATUS_READY_MASK); if (mhi_state != MHI_STATE_RESET || is_ready) { dev_err(dev, "READY state transition failed. MHI host not in RESET state\n"); - spin_unlock_bh(&mhi_cntrl->state_lock); - return -EIO; + ret = -EIO; + goto err_unlock; } ret = mhi_ep_set_mhi_state(mhi_cntrl, MHI_STATE_READY); - spin_unlock_bh(&mhi_cntrl->state_lock); - if (ret) mhi_ep_handle_syserr(mhi_cntrl); +err_unlock: + mutex_unlock(&mhi_cntrl->state_lock); + return ret; } diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index bf672de35131..7307335c4fd1 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -1449,4 +1449,4 @@ postcore_initcall(mhi_init); module_exit(mhi_exit); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("MHI Host Interface"); +MODULE_DESCRIPTION("Modem Host Interface"); diff --git a/drivers/bus/simple-pm-bus.c b/drivers/bus/simple-pm-bus.c index 6b8d6257ed8a..7afe1947e1c0 100644 --- a/drivers/bus/simple-pm-bus.c +++ b/drivers/bus/simple-pm-bus.c @@ -8,17 +8,24 @@ * for more details. */ +#include <linux/clk.h> #include <linux/module.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +struct simple_pm_bus { + struct clk_bulk_data *clks; + int num_clks; +}; + static int simple_pm_bus_probe(struct platform_device *pdev) { const struct device *dev = &pdev->dev; const struct of_dev_auxdata *lookup = dev_get_platdata(dev); struct device_node *np = dev->of_node; const struct of_device_id *match; + struct simple_pm_bus *bus; /* * Allow user to use driver_override to bind this driver to a @@ -44,6 +51,16 @@ static int simple_pm_bus_probe(struct platform_device *pdev) return -ENODEV; } + bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); + if (!bus) + return -ENOMEM; + + bus->num_clks = devm_clk_bulk_get_all(&pdev->dev, &bus->clks); + if (bus->num_clks < 0) + return dev_err_probe(&pdev->dev, bus->num_clks, "failed to get clocks\n"); + + dev_set_drvdata(&pdev->dev, bus); + dev_dbg(&pdev->dev, "%s\n", __func__); pm_runtime_enable(&pdev->dev); @@ -67,6 +84,34 @@ static int simple_pm_bus_remove(struct platform_device *pdev) return 0; } +static int simple_pm_bus_runtime_suspend(struct device *dev) +{ + struct simple_pm_bus *bus = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(bus->num_clks, bus->clks); + + return 0; +} + +static int simple_pm_bus_runtime_resume(struct device *dev) +{ + struct simple_pm_bus *bus = dev_get_drvdata(dev); + int ret; + + ret = clk_bulk_prepare_enable(bus->num_clks, bus->clks); + if (ret) { + dev_err(dev, "failed to enable clocks: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct dev_pm_ops simple_pm_bus_pm_ops = { + RUNTIME_PM_OPS(simple_pm_bus_runtime_suspend, simple_pm_bus_runtime_resume, NULL) + NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) +}; + #define ONLY_BUS ((void *) 1) /* Match if the device is only a bus. */ static const struct of_device_id simple_pm_bus_of_match[] = { @@ -85,6 +130,7 @@ static struct platform_driver simple_pm_bus_driver = { .driver = { .name = "simple-pm-bus", .of_match_table = simple_pm_bus_of_match, + .pm = pm_ptr(&simple_pm_bus_pm_ops), }, }; diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index 36203d3fa6ea..69314532f38c 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -197,8 +197,10 @@ static int __init applicom_init(void) if (!pci_match_id(applicom_pci_tbl, dev)) continue; - if (pci_enable_device(dev)) + if (pci_enable_device(dev)) { + pci_dev_put(dev); return -EIO; + } RamIO = ioremap(pci_resource_start(dev, 0), LEN_RAM_IO); @@ -207,6 +209,7 @@ static int __init applicom_init(void) "space at 0x%llx\n", (unsigned long long)pci_resource_start(dev, 0)); pci_disable_device(dev); + pci_dev_put(dev); return -EIO; } diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index adaec8fd4b16..e656f42a28ac 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -529,7 +529,8 @@ static int set_protocol(struct cm4000_dev *dev, struct ptsreq *ptsreq) DEBUGP(5, dev, "NumRecBytes is valid\n"); break; } - usleep_range(10000, 11000); + /* can not sleep as this is in atomic context */ + mdelay(10); } if (i == 100) { DEBUGP(5, dev, "Timeout waiting for NumRecBytes getting " @@ -549,7 +550,8 @@ static int set_protocol(struct cm4000_dev *dev, struct ptsreq *ptsreq) } break; } - usleep_range(10000, 11000); + /* can not sleep as this is in atomic context */ + mdelay(10); } /* check whether it is a short PTS reply? */ diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index b2735be81ab2..6d04f2fab63b 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -483,7 +483,7 @@ static void* mgslpc_get_text_ptr(void) return mgslpc_get_text_ptr; } -/** +/* * line discipline callback wrappers * * The wrappers maintain line discipline references @@ -3857,7 +3857,7 @@ static void tx_timeout(struct timer_list *t) #if SYNCLINK_GENERIC_HDLC -/** +/* * called by generic HDLC layer when protocol selected (PPP, frame relay, etc.) * set encoding and frame check sequence (FCS) options * @@ -3910,7 +3910,7 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, return 0; } -/** +/* * called by generic HDLC layer to send frame * * skb socket buffer containing HDLC frame @@ -3955,7 +3955,7 @@ static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } -/** +/* * called by network layer when interface enabled * claim resources and initialize hardware * @@ -4018,7 +4018,7 @@ static int hdlcdev_open(struct net_device *dev) return 0; } -/** +/* * called by network layer when interface is disabled * shutdown hardware and release resources * @@ -4049,7 +4049,7 @@ static int hdlcdev_close(struct net_device *dev) return 0; } -/** +/* * called by network layer to process IOCTL call to network device * * dev pointer to network device structure @@ -4152,7 +4152,7 @@ static int hdlcdev_wan_ioctl(struct net_device *dev, struct if_settings *ifs) } } -/** +/* * called by network layer when transmit timeout is detected * * dev pointer to network device structure @@ -4175,7 +4175,7 @@ static void hdlcdev_tx_timeout(struct net_device *dev, unsigned int txqueue) netif_wake_queue(dev); } -/** +/* * called by device driver when transmit completes * reenable network layer transmit if stopped * @@ -4187,7 +4187,7 @@ static void hdlcdev_tx_done(MGSLPC_INFO *info) netif_wake_queue(info->netdev); } -/** +/* * called by device driver when frame received * pass frame to network layer * @@ -4227,7 +4227,7 @@ static const struct net_device_ops hdlcdev_ops = { .ndo_tx_timeout = hdlcdev_tx_timeout, }; -/** +/* * called by device driver when adding device instance * do generic HDLC initialization * @@ -4275,7 +4275,7 @@ static int hdlcdev_init(MGSLPC_INFO *info) return 0; } -/** +/* * called by device driver when removing device instance * do generic HDLC cleanup * diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 6a821118d553..d5ac4d955bc8 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -1666,9 +1666,8 @@ static void handle_control_message(struct virtio_device *vdev, "Not enough space to store port name\n"); break; } - strncpy(port->name, buf->buf + buf->offset + sizeof(*cpkt), - name_size - 1); - port->name[name_size - 1] = 0; + strscpy(port->name, buf->buf + buf->offset + sizeof(*cpkt), + name_size); /* * Since we only have one sysfs attribute, 'name', diff --git a/drivers/comedi/Kconfig b/drivers/comedi/Kconfig index 3cb61fa2c5c3..9af280735cba 100644 --- a/drivers/comedi/Kconfig +++ b/drivers/comedi/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 -config COMEDI +menuconfig COMEDI tristate "Data acquisition support (comedi)" help Enable support for a wide range of data acquisition devices diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c index e2114bcf815a..b982903aaa46 100644 --- a/drivers/comedi/comedi_fops.c +++ b/drivers/comedi/comedi_fops.c @@ -1215,6 +1215,7 @@ static int check_insn_config_length(struct comedi_insn *insn, case INSN_CONFIG_GET_CLOCK_SRC: case INSN_CONFIG_SET_OTHER_SRC: case INSN_CONFIG_GET_COUNTER_STATUS: + case INSN_CONFIG_GET_PWM_OUTPUT: case INSN_CONFIG_PWM_SET_H_BRIDGE: case INSN_CONFIG_PWM_GET_H_BRIDGE: case INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE: diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index d388bf26f4dc..b5ba8fb02cf7 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -29,6 +29,28 @@ config 104_QUAD_8 array module parameter. The interrupt line numbers for the devices may be configured via the irq array module parameter. +config FTM_QUADDEC + tristate "Flex Timer Module Quadrature decoder driver" + depends on SOC_LS1021A || COMPILE_TEST + depends on HAS_IOMEM && OF + help + Select this option to enable the Flex Timer Quadrature decoder + driver. + + To compile this driver as a module, choose M here: the + module will be called ftm-quaddec. + +config INTEL_QEP + tristate "Intel Quadrature Encoder Peripheral driver" + depends on X86 + depends on PCI + help + Select this option to enable the Intel Quadrature Encoder Peripheral + driver. + + To compile this driver as a module, choose M here: the module + will be called intel-qep. + config INTERRUPT_CNT tristate "Interrupt counter driver" depends on GPIOLIB @@ -39,15 +61,17 @@ config INTERRUPT_CNT To compile this driver as a module, choose M here: the module will be called interrupt-cnt. -config STM32_TIMER_CNT - tristate "STM32 Timer encoder counter driver" - depends on MFD_STM32_TIMERS || COMPILE_TEST +config MICROCHIP_TCB_CAPTURE + tristate "Microchip Timer Counter Capture driver" + depends on SOC_AT91SAM9 || SOC_SAM_V7 || COMPILE_TEST + depends on HAS_IOMEM && OF + select REGMAP_MMIO help - Select this option to enable STM32 Timer quadrature encoder - and counter driver. + Select this option to enable the Microchip Timer Counter Block + capture driver. To compile this driver as a module, choose M here: the - module will be called stm32-timer-cnt. + module will be called microchip-tcb-capture. config STM32_LPTIMER_CNT tristate "STM32 LP Timer encoder counter driver" @@ -59,47 +83,15 @@ config STM32_LPTIMER_CNT To compile this driver as a module, choose M here: the module will be called stm32-lptimer-cnt. -config TI_EQEP - tristate "TI eQEP counter driver" - depends on (SOC_AM33XX || COMPILE_TEST) - select REGMAP_MMIO - help - Select this option to enable the Texas Instruments Enhanced Quadrature - Encoder Pulse (eQEP) counter driver. - - To compile this driver as a module, choose M here: the module will be - called ti-eqep. - -config FTM_QUADDEC - tristate "Flex Timer Module Quadrature decoder driver" - depends on HAS_IOMEM && OF - help - Select this option to enable the Flex Timer Quadrature decoder - driver. - - To compile this driver as a module, choose M here: the - module will be called ftm-quaddec. - -config MICROCHIP_TCB_CAPTURE - tristate "Microchip Timer Counter Capture driver" - depends on HAS_IOMEM && OF - select REGMAP_MMIO +config STM32_TIMER_CNT + tristate "STM32 Timer encoder counter driver" + depends on MFD_STM32_TIMERS || COMPILE_TEST help - Select this option to enable the Microchip Timer Counter Block - capture driver. + Select this option to enable STM32 Timer quadrature encoder + and counter driver. To compile this driver as a module, choose M here: the - module will be called microchip-tcb-capture. - -config INTEL_QEP - tristate "Intel Quadrature Encoder Peripheral driver" - depends on PCI - help - Select this option to enable the Intel Quadrature Encoder Peripheral - driver. - - To compile this driver as a module, choose M here: the module - will be called intel-qep. + module will be called stm32-timer-cnt. config TI_ECAP_CAPTURE tristate "TI eCAP capture driver" @@ -116,4 +108,15 @@ config TI_ECAP_CAPTURE To compile this driver as a module, choose M here: the module will be called ti-ecap-capture. +config TI_EQEP + tristate "TI eQEP counter driver" + depends on (SOC_AM33XX || COMPILE_TEST) + select REGMAP_MMIO + help + Select this option to enable the Texas Instruments Enhanced Quadrature + Encoder Pulse (eQEP) counter driver. + + To compile this driver as a module, choose M here: the module will be + called ti-eqep. + endif # COUNTER diff --git a/drivers/crypto/hisilicon/qm.c b/drivers/crypto/hisilicon/qm.c index 007ac7a69ce7..58d023f6fba3 100644 --- a/drivers/crypto/hisilicon/qm.c +++ b/drivers/crypto/hisilicon/qm.c @@ -367,6 +367,16 @@ struct hisi_qm_resource { struct list_head list; }; +/** + * struct qm_hw_err - Structure describing the device errors + * @list: hardware error list + * @timestamp: timestamp when the error occurred + */ +struct qm_hw_err { + struct list_head list; + unsigned long long timestamp; +}; + struct hisi_qm_hw_ops { int (*get_vft)(struct hisi_qm *qm, u32 *base, u32 *number); void (*qm_db)(struct hisi_qm *qm, u16 qn, @@ -2469,6 +2479,113 @@ static long hisi_qm_uacce_ioctl(struct uacce_queue *q, unsigned int cmd, return -EINVAL; } +/** + * qm_hw_err_isolate() - Try to set the isolation status of the uacce device + * according to user's configuration of error threshold. + * @qm: the uacce device + */ +static int qm_hw_err_isolate(struct hisi_qm *qm) +{ + struct qm_hw_err *err, *tmp, *hw_err; + struct qm_err_isolate *isolate; + u32 count = 0; + + isolate = &qm->isolate_data; + +#define SECONDS_PER_HOUR 3600 + + /* All the hw errs are processed by PF driver */ + if (qm->uacce->is_vf || isolate->is_isolate || !isolate->err_threshold) + return 0; + + hw_err = kzalloc(sizeof(*hw_err), GFP_KERNEL); + if (!hw_err) + return -ENOMEM; + + /* + * Time-stamp every slot AER error. Then check the AER error log when the + * next device AER error occurred. if the device slot AER error count exceeds + * the setting error threshold in one hour, the isolated state will be set + * to true. And the AER error logs that exceed one hour will be cleared. + */ + mutex_lock(&isolate->isolate_lock); + hw_err->timestamp = jiffies; + list_for_each_entry_safe(err, tmp, &isolate->qm_hw_errs, list) { + if ((hw_err->timestamp - err->timestamp) / HZ > + SECONDS_PER_HOUR) { + list_del(&err->list); + kfree(err); + } else { + count++; + } + } + list_add(&hw_err->list, &isolate->qm_hw_errs); + mutex_unlock(&isolate->isolate_lock); + + if (count >= isolate->err_threshold) + isolate->is_isolate = true; + + return 0; +} + +static void qm_hw_err_destroy(struct hisi_qm *qm) +{ + struct qm_hw_err *err, *tmp; + + mutex_lock(&qm->isolate_data.isolate_lock); + list_for_each_entry_safe(err, tmp, &qm->isolate_data.qm_hw_errs, list) { + list_del(&err->list); + kfree(err); + } + mutex_unlock(&qm->isolate_data.isolate_lock); +} + +static enum uacce_dev_state hisi_qm_get_isolate_state(struct uacce_device *uacce) +{ + struct hisi_qm *qm = uacce->priv; + struct hisi_qm *pf_qm; + + if (uacce->is_vf) + pf_qm = pci_get_drvdata(pci_physfn(qm->pdev)); + else + pf_qm = qm; + + return pf_qm->isolate_data.is_isolate ? + UACCE_DEV_ISOLATE : UACCE_DEV_NORMAL; +} + +static int hisi_qm_isolate_threshold_write(struct uacce_device *uacce, u32 num) +{ + struct hisi_qm *qm = uacce->priv; + + /* Must be set by PF */ + if (uacce->is_vf) + return -EPERM; + + if (qm->isolate_data.is_isolate) + return -EPERM; + + qm->isolate_data.err_threshold = num; + + /* After the policy is updated, need to reset the hardware err list */ + qm_hw_err_destroy(qm); + + return 0; +} + +static u32 hisi_qm_isolate_threshold_read(struct uacce_device *uacce) +{ + struct hisi_qm *qm = uacce->priv; + struct hisi_qm *pf_qm; + + if (uacce->is_vf) { + pf_qm = pci_get_drvdata(pci_physfn(qm->pdev)); + return pf_qm->isolate_data.err_threshold; + } + + return qm->isolate_data.err_threshold; +} + static const struct uacce_ops uacce_qm_ops = { .get_available_instances = hisi_qm_get_available_instances, .get_queue = hisi_qm_uacce_get_queue, @@ -2478,8 +2595,22 @@ static const struct uacce_ops uacce_qm_ops = { .mmap = hisi_qm_uacce_mmap, .ioctl = hisi_qm_uacce_ioctl, .is_q_updated = hisi_qm_is_q_updated, + .get_isolate_state = hisi_qm_get_isolate_state, + .isolate_err_threshold_write = hisi_qm_isolate_threshold_write, + .isolate_err_threshold_read = hisi_qm_isolate_threshold_read, }; +static void qm_remove_uacce(struct hisi_qm *qm) +{ + struct uacce_device *uacce = qm->uacce; + + if (qm->use_sva) { + qm_hw_err_destroy(qm); + uacce_remove(uacce); + qm->uacce = NULL; + } +} + static int qm_alloc_uacce(struct hisi_qm *qm) { struct pci_dev *pdev = qm->pdev; @@ -2506,8 +2637,7 @@ static int qm_alloc_uacce(struct hisi_qm *qm) qm->use_sva = true; } else { /* only consider sva case */ - uacce_remove(uacce); - qm->uacce = NULL; + qm_remove_uacce(qm); return -EINVAL; } @@ -2540,6 +2670,8 @@ static int qm_alloc_uacce(struct hisi_qm *qm) uacce->qf_pg_num[UACCE_QFRT_DUS] = dus_page_nr; qm->uacce = uacce; + INIT_LIST_HEAD(&qm->isolate_data.qm_hw_errs); + mutex_init(&qm->isolate_data.isolate_lock); return 0; } @@ -4029,6 +4161,12 @@ static int qm_controller_reset_prepare(struct hisi_qm *qm) return ret; } + if (qm->use_sva) { + ret = qm_hw_err_isolate(qm); + if (ret) + pci_err(pdev, "failed to isolate hw err!\n"); + } + ret = qm_wait_vf_prepare_finish(qm); if (ret) pci_err(pdev, "failed to stop by vfs in soft reset!\n"); @@ -4336,21 +4474,25 @@ static int qm_controller_reset(struct hisi_qm *qm) qm->err_ini->show_last_dfx_regs(qm); ret = qm_soft_reset(qm); - if (ret) { - pci_err(pdev, "Controller reset failed (%d)\n", ret); - qm_reset_bit_clear(qm); - return ret; - } + if (ret) + goto err_reset; ret = qm_controller_reset_done(qm); - if (ret) { - qm_reset_bit_clear(qm); - return ret; - } + if (ret) + goto err_reset; pci_info(pdev, "Controller reset complete\n"); return 0; + +err_reset: + pci_err(pdev, "Controller reset failed (%d)\n", ret); + qm_reset_bit_clear(qm); + + /* if resetting fails, isolate the device */ + if (qm->use_sva) + qm->isolate_data.is_isolate = true; + return ret; } /** @@ -5271,10 +5413,7 @@ int hisi_qm_init(struct hisi_qm *qm) err_free_qm_memory: hisi_qm_memory_uninit(qm); err_alloc_uacce: - if (qm->use_sva) { - uacce_remove(qm->uacce); - qm->uacce = NULL; - } + qm_remove_uacce(qm); err_irq_register: qm_irqs_unregister(qm); err_pci_init: diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index 66727ad3361b..ed5aff0a4204 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c @@ -418,10 +418,10 @@ static ssize_t dmi_sel_raw_read_helper(struct dmi_sysfs_entry *entry, return dmi_sel_raw_read_phys32(entry, &sel, state->buf, state->pos, state->count); case DMI_SEL_ACCESS_METHOD_GPNV: - pr_info("dmi-sysfs: GPNV support missing.\n"); + pr_info_ratelimited("dmi-sysfs: GPNV support missing.\n"); return -EIO; default: - pr_info("dmi-sysfs: Unknown access method %02x\n", + pr_info_ratelimited("dmi-sysfs: Unknown access method %02x\n", sel.access_method); return -EIO; } @@ -603,16 +603,16 @@ static void __init dmi_sysfs_register_handle(const struct dmi_header *dh, *ret = kobject_init_and_add(&entry->kobj, &dmi_sysfs_entry_ktype, NULL, "%d-%d", dh->type, entry->instance); - if (*ret) { - kobject_put(&entry->kobj); - return; - } - /* Thread on the global list for cleanup */ spin_lock(&entry_list_lock); list_add_tail(&entry->list, &entry_list); spin_unlock(&entry_list_lock); + if (*ret) { + kobject_put(&entry->kobj); + return; + } + /* Handle specializations by type */ switch (dh->type) { case DMI_ENTRY_SYSTEM_EVENT_LOG: diff --git a/drivers/firmware/google/framebuffer-coreboot.c b/drivers/firmware/google/framebuffer-coreboot.c index c6dcc1ef93ac..c323a818805c 100644 --- a/drivers/firmware/google/framebuffer-coreboot.c +++ b/drivers/firmware/google/framebuffer-coreboot.c @@ -43,9 +43,7 @@ static int framebuffer_probe(struct coreboot_device *dev) fb->green_mask_pos == formats[i].green.offset && fb->green_mask_size == formats[i].green.length && fb->blue_mask_pos == formats[i].blue.offset && - fb->blue_mask_size == formats[i].blue.length && - fb->reserved_mask_pos == formats[i].transp.offset && - fb->reserved_mask_size == formats[i].transp.length) + fb->blue_mask_size == formats[i].blue.length) pdata.format = formats[i].name; } if (!pdata.format) diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index b4081f4d88a3..bde1f543f529 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -1138,13 +1138,17 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) /* allocate service controller and supporting channel */ controller = devm_kzalloc(dev, sizeof(*controller), GFP_KERNEL); - if (!controller) - return -ENOMEM; + if (!controller) { + ret = -ENOMEM; + goto err_destroy_pool; + } chans = devm_kmalloc_array(dev, SVC_NUM_CHANNEL, sizeof(*chans), GFP_KERNEL | __GFP_ZERO); - if (!chans) - return -ENOMEM; + if (!chans) { + ret = -ENOMEM; + goto err_destroy_pool; + } controller->dev = dev; controller->num_chans = SVC_NUM_CHANNEL; @@ -1159,7 +1163,7 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) ret = kfifo_alloc(&controller->svc_fifo, fifo_size, GFP_KERNEL); if (ret) { dev_err(dev, "failed to allocate FIFO\n"); - return ret; + goto err_destroy_pool; } spin_lock_init(&controller->svc_fifo_lock); @@ -1198,19 +1202,20 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) ret = platform_device_add(svc->stratix10_svc_rsu); if (ret) { platform_device_put(svc->stratix10_svc_rsu); - return ret; + goto err_free_kfifo; } svc->intel_svc_fcs = platform_device_alloc(INTEL_FCS, 1); if (!svc->intel_svc_fcs) { dev_err(dev, "failed to allocate %s device\n", INTEL_FCS); - return -ENOMEM; + ret = -ENOMEM; + goto err_unregister_dev; } ret = platform_device_add(svc->intel_svc_fcs); if (ret) { platform_device_put(svc->intel_svc_fcs); - return ret; + goto err_unregister_dev; } dev_set_drvdata(dev, svc); @@ -1219,8 +1224,12 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) return 0; +err_unregister_dev: + platform_device_unregister(svc->stratix10_svc_rsu); err_free_kfifo: kfifo_free(&controller->svc_fifo); +err_destroy_pool: + gen_pool_destroy(genpool); return ret; } diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 6ce143dafd04..0a00763b9f28 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -246,7 +246,7 @@ config FPGA_MGR_VERSAL_FPGA config FPGA_M10_BMC_SEC_UPDATE tristate "Intel MAX10 BMC Secure Update driver" - depends on MFD_INTEL_M10_BMC + depends on MFD_INTEL_M10_BMC_CORE select FW_LOADER select FW_UPLOAD help diff --git a/drivers/fpga/dfl-afu-region.c b/drivers/fpga/dfl-afu-region.c index 0804b7a0c298..2e7b41629406 100644 --- a/drivers/fpga/dfl-afu-region.c +++ b/drivers/fpga/dfl-afu-region.c @@ -39,6 +39,7 @@ static struct dfl_afu_mmio_region *get_region_by_index(struct dfl_afu *afu, /** * afu_mmio_region_add - add a mmio region to given feature dev. * + * @pdata: afu platform device's pdata. * @region_index: region index. * @region_size: region size. * @phys: region's physical address of this region. diff --git a/drivers/fpga/dfl-afu.h b/drivers/fpga/dfl-afu.h index e5020e2b1f3d..674e9772f0ea 100644 --- a/drivers/fpga/dfl-afu.h +++ b/drivers/fpga/dfl-afu.h @@ -41,7 +41,7 @@ struct dfl_afu_mmio_region { }; /** - * struct fpga_afu_dma_region - afu DMA region data structure + * struct dfl_afu_dma_region - afu DMA region data structure * * @user_addr: region userspace virtual address. * @length: region length. diff --git a/drivers/fpga/dfl-fme-perf.c b/drivers/fpga/dfl-fme-perf.c index 587c82be12f7..7422d2bc6f37 100644 --- a/drivers/fpga/dfl-fme-perf.c +++ b/drivers/fpga/dfl-fme-perf.c @@ -141,7 +141,7 @@ * @fab_port_id: used to indicate current working mode of fabric counters. * @fab_lock: lock to protect fabric counters working mode. * @cpu: active CPU to which the PMU is bound for accesses. - * @cpuhp_node: node for CPU hotplug notifier link. + * @node: node for CPU hotplug notifier link. * @cpuhp_state: state for CPU hotplug notification; */ struct fme_perf_priv { diff --git a/drivers/fpga/dfl-fme-pr.c b/drivers/fpga/dfl-fme-pr.c index d61ce9a18879..cdcf6dea4cc9 100644 --- a/drivers/fpga/dfl-fme-pr.c +++ b/drivers/fpga/dfl-fme-pr.c @@ -164,7 +164,7 @@ free_exit: /** * dfl_fme_create_mgr - create fpga mgr platform device as child device - * + * @feature: sub feature info * @pdata: fme platform_device's pdata * * Return: mgr platform device if successful, and error code otherwise. @@ -273,7 +273,7 @@ static void dfl_fme_destroy_bridge(struct dfl_fme_bridge *fme_br) } /** - * dfl_fme_destroy_bridge - destroy all fpga bridge platform device + * dfl_fme_destroy_bridges - destroy all fpga bridge platform device * @pdata: fme platform device's pdata */ static void dfl_fme_destroy_bridges(struct dfl_feature_platform_data *pdata) diff --git a/drivers/fpga/dfl-fme-pr.h b/drivers/fpga/dfl-fme-pr.h index 096a699089d3..761f80f63312 100644 --- a/drivers/fpga/dfl-fme-pr.h +++ b/drivers/fpga/dfl-fme-pr.h @@ -58,7 +58,7 @@ struct dfl_fme_bridge { }; /** - * struct dfl_fme_bridge_pdata - platform data for FME bridge platform device. + * struct dfl_fme_br_pdata - platform data for FME bridge platform device. * * @cdev: container device. * @port_id: port id. diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index b9aae85ba930..9e59642dd5ba 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -45,7 +45,7 @@ static const char *dfl_pdata_key_strings[DFL_ID_MAX] = { }; /** - * dfl_dev_info - dfl feature device information. + * struct dfl_dev_info - dfl feature device information. * @name: name string of the feature platform device. * @dfh_id: id value in Device Feature Header (DFH) register by DFL spec. * @id: idr id of the feature dev. @@ -67,7 +67,7 @@ static struct dfl_dev_info dfl_devs[] = { }; /** - * dfl_chardev_info - chardev information of dfl feature device + * struct dfl_chardev_info - chardev information of dfl feature device * @name: nmae string of the char device. * @devt: devt of the char device. */ @@ -708,6 +708,7 @@ struct build_feature_devs_info { * struct dfl_feature_info - sub feature info collected during feature dev build * * @fid: id of this sub feature. + * @revision: revision value of this sub feature. * @mmio_res: mmio resource of this sub feature. * @ioaddr: mapped base address of mmio resource. * @node: node in sub_features linked list. diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h index 06cfcd5e84bb..5fc424d505c8 100644 --- a/drivers/fpga/dfl.h +++ b/drivers/fpga/dfl.h @@ -231,6 +231,7 @@ struct dfl_feature_irq_ctx { * * @dev: ptr to pdev of the feature device which has the sub feature. * @id: sub feature id. + * @revision: revision value of this sub feature. * @resource_index: each sub feature has one mmio resource for its registers. * this index is used to find its mmio resource from the * feature dev (platform device)'s resources. diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index 727704431f61..5cd40acab5bf 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c @@ -293,12 +293,15 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fpga_bridge *bridge = to_fpga_bridge(dev); - int enable = 1; + int state = 1; - if (bridge->br_ops && bridge->br_ops->enable_show) - enable = bridge->br_ops->enable_show(bridge); + if (bridge->br_ops && bridge->br_ops->enable_show) { + state = bridge->br_ops->enable_show(bridge); + if (state < 0) + return state; + } - return sprintf(buf, "%s\n", enable ? "enabled" : "disabled"); + return sysfs_emit(buf, "%s\n", state ? "enabled" : "disabled"); } static DEVICE_ATTR_RO(name); diff --git a/drivers/fpga/intel-m10-bmc-sec-update.c b/drivers/fpga/intel-m10-bmc-sec-update.c index 03f1bd81c434..47c0d5c6a4a3 100644 --- a/drivers/fpga/intel-m10-bmc-sec-update.c +++ b/drivers/fpga/intel-m10-bmc-sec-update.c @@ -14,6 +14,12 @@ #include <linux/platform_device.h> #include <linux/slab.h> +struct m10bmc_sec; + +struct m10bmc_sec_ops { + int (*rsu_status)(struct m10bmc_sec *sec); +}; + struct m10bmc_sec { struct device *dev; struct intel_m10bmc *m10bmc; @@ -21,6 +27,7 @@ struct m10bmc_sec { char *fw_name; u32 fw_name_id; bool cancel_request; + const struct m10bmc_sec_ops *ops; }; static DEFINE_XARRAY_ALLOC(fw_upload_xa); @@ -31,6 +38,65 @@ static DEFINE_XARRAY_ALLOC(fw_upload_xa); #define REH_MAGIC GENMASK(15, 0) #define REH_SHA_NUM_BYTES GENMASK(31, 16) +static int m10bmc_sec_write(struct m10bmc_sec *sec, const u8 *buf, u32 offset, u32 size) +{ + struct intel_m10bmc *m10bmc = sec->m10bmc; + unsigned int stride = regmap_get_reg_stride(m10bmc->regmap); + u32 write_count = size / stride; + u32 leftover_offset = write_count * stride; + u32 leftover_size = size - leftover_offset; + u32 leftover_tmp = 0; + int ret; + + if (WARN_ON_ONCE(stride > sizeof(leftover_tmp))) + return -EINVAL; + + ret = regmap_bulk_write(m10bmc->regmap, M10BMC_STAGING_BASE + offset, + buf + offset, write_count); + if (ret) + return ret; + + /* If size is not aligned to stride, handle the remainder bytes with regmap_write() */ + if (leftover_size) { + memcpy(&leftover_tmp, buf + leftover_offset, leftover_size); + ret = regmap_write(m10bmc->regmap, M10BMC_STAGING_BASE + offset + leftover_offset, + leftover_tmp); + if (ret) + return ret; + } + + return 0; +} + +static int m10bmc_sec_read(struct m10bmc_sec *sec, u8 *buf, u32 addr, u32 size) +{ + struct intel_m10bmc *m10bmc = sec->m10bmc; + unsigned int stride = regmap_get_reg_stride(m10bmc->regmap); + u32 read_count = size / stride; + u32 leftover_offset = read_count * stride; + u32 leftover_size = size - leftover_offset; + u32 leftover_tmp; + int ret; + + if (WARN_ON_ONCE(stride > sizeof(leftover_tmp))) + return -EINVAL; + + ret = regmap_bulk_read(m10bmc->regmap, addr, buf, read_count); + if (ret) + return ret; + + /* If size is not aligned to stride, handle the remainder bytes with regmap_read() */ + if (leftover_size) { + ret = regmap_read(m10bmc->regmap, addr + leftover_offset, &leftover_tmp); + if (ret) + return ret; + memcpy(buf + leftover_offset, &leftover_tmp, leftover_size); + } + + return 0; +} + + static ssize_t show_root_entry_hash(struct device *dev, u32 exp_magic, u32 prog_addr, u32 reh_addr, char *buf) @@ -38,11 +104,9 @@ show_root_entry_hash(struct device *dev, u32 exp_magic, struct m10bmc_sec *sec = dev_get_drvdata(dev); int sha_num_bytes, i, ret, cnt = 0; u8 hash[REH_SHA384_SIZE]; - unsigned int stride; u32 magic; - stride = regmap_get_reg_stride(sec->m10bmc->regmap); - ret = m10bmc_raw_read(sec->m10bmc, prog_addr, &magic); + ret = m10bmc_sec_read(sec, (u8 *)&magic, prog_addr, sizeof(magic)); if (ret) return ret; @@ -50,19 +114,16 @@ show_root_entry_hash(struct device *dev, u32 exp_magic, return sysfs_emit(buf, "hash not programmed\n"); sha_num_bytes = FIELD_GET(REH_SHA_NUM_BYTES, magic) / 8; - if ((sha_num_bytes % stride) || - (sha_num_bytes != REH_SHA256_SIZE && - sha_num_bytes != REH_SHA384_SIZE)) { + if (sha_num_bytes != REH_SHA256_SIZE && + sha_num_bytes != REH_SHA384_SIZE) { dev_err(sec->dev, "%s bad sha num bytes %d\n", __func__, sha_num_bytes); return -EINVAL; } - ret = regmap_bulk_read(sec->m10bmc->regmap, reh_addr, - hash, sha_num_bytes / stride); + ret = m10bmc_sec_read(sec, hash, reh_addr, sha_num_bytes); if (ret) { - dev_err(dev, "failed to read root entry hash: %x cnt %x: %d\n", - reh_addr, sha_num_bytes / stride, ret); + dev_err(dev, "failed to read root entry hash\n"); return ret; } @@ -73,16 +134,24 @@ show_root_entry_hash(struct device *dev, u32 exp_magic, return cnt; } -#define DEVICE_ATTR_SEC_REH_RO(_name, _magic, _prog_addr, _reh_addr) \ +#define DEVICE_ATTR_SEC_REH_RO(_name) \ static ssize_t _name##_root_entry_hash_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ -{ return show_root_entry_hash(dev, _magic, _prog_addr, _reh_addr, buf); } \ +{ \ + struct m10bmc_sec *sec = dev_get_drvdata(dev); \ + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; \ + \ + return show_root_entry_hash(dev, csr_map->_name##_magic, \ + csr_map->_name##_prog_addr, \ + csr_map->_name##_reh_addr, \ + buf); \ +} \ static DEVICE_ATTR_RO(_name##_root_entry_hash) -DEVICE_ATTR_SEC_REH_RO(bmc, BMC_PROG_MAGIC, BMC_PROG_ADDR, BMC_REH_ADDR); -DEVICE_ATTR_SEC_REH_RO(sr, SR_PROG_MAGIC, SR_PROG_ADDR, SR_REH_ADDR); -DEVICE_ATTR_SEC_REH_RO(pr, PR_PROG_MAGIC, PR_PROG_ADDR, PR_REH_ADDR); +DEVICE_ATTR_SEC_REH_RO(bmc); +DEVICE_ATTR_SEC_REH_RO(sr); +DEVICE_ATTR_SEC_REH_RO(pr); #define CSK_BIT_LEN 128U #define CSK_32ARRAY_SIZE DIV_ROUND_UP(CSK_BIT_LEN, 32) @@ -90,27 +159,16 @@ DEVICE_ATTR_SEC_REH_RO(pr, PR_PROG_MAGIC, PR_PROG_ADDR, PR_REH_ADDR); static ssize_t show_canceled_csk(struct device *dev, u32 addr, char *buf) { - unsigned int i, stride, size = CSK_32ARRAY_SIZE * sizeof(u32); + unsigned int i, size = CSK_32ARRAY_SIZE * sizeof(u32); struct m10bmc_sec *sec = dev_get_drvdata(dev); DECLARE_BITMAP(csk_map, CSK_BIT_LEN); __le32 csk_le32[CSK_32ARRAY_SIZE]; u32 csk32[CSK_32ARRAY_SIZE]; int ret; - stride = regmap_get_reg_stride(sec->m10bmc->regmap); - if (size % stride) { - dev_err(sec->dev, - "CSK vector size (0x%x) not aligned to stride (0x%x)\n", - size, stride); - WARN_ON_ONCE(1); - return -EINVAL; - } - - ret = regmap_bulk_read(sec->m10bmc->regmap, addr, csk_le32, - size / stride); + ret = m10bmc_sec_read(sec, (u8 *)&csk_le32, addr, size); if (ret) { - dev_err(sec->dev, "failed to read CSK vector: %x cnt %x: %d\n", - addr, size / stride, ret); + dev_err(sec->dev, "failed to read CSK vector\n"); return ret; } @@ -122,18 +180,25 @@ show_canceled_csk(struct device *dev, u32 addr, char *buf) return bitmap_print_to_pagebuf(1, buf, csk_map, CSK_BIT_LEN); } -#define DEVICE_ATTR_SEC_CSK_RO(_name, _addr) \ +#define DEVICE_ATTR_SEC_CSK_RO(_name) \ static ssize_t _name##_canceled_csks_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ -{ return show_canceled_csk(dev, _addr, buf); } \ +{ \ + struct m10bmc_sec *sec = dev_get_drvdata(dev); \ + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; \ + \ + return show_canceled_csk(dev, \ + csr_map->_name##_prog_addr + CSK_VEC_OFFSET, \ + buf); \ +} \ static DEVICE_ATTR_RO(_name##_canceled_csks) #define CSK_VEC_OFFSET 0x34 -DEVICE_ATTR_SEC_CSK_RO(bmc, BMC_PROG_ADDR + CSK_VEC_OFFSET); -DEVICE_ATTR_SEC_CSK_RO(sr, SR_PROG_ADDR + CSK_VEC_OFFSET); -DEVICE_ATTR_SEC_CSK_RO(pr, PR_PROG_ADDR + CSK_VEC_OFFSET); +DEVICE_ATTR_SEC_CSK_RO(bmc); +DEVICE_ATTR_SEC_CSK_RO(sr); +DEVICE_ATTR_SEC_CSK_RO(pr); #define FLASH_COUNT_SIZE 4096 /* count stored as inverted bit vector */ @@ -141,31 +206,21 @@ static ssize_t flash_count_show(struct device *dev, struct device_attribute *attr, char *buf) { struct m10bmc_sec *sec = dev_get_drvdata(dev); - unsigned int stride, num_bits; + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; + unsigned int num_bits; u8 *flash_buf; int cnt, ret; - stride = regmap_get_reg_stride(sec->m10bmc->regmap); num_bits = FLASH_COUNT_SIZE * 8; - if (FLASH_COUNT_SIZE % stride) { - dev_err(sec->dev, - "FLASH_COUNT_SIZE (0x%x) not aligned to stride (0x%x)\n", - FLASH_COUNT_SIZE, stride); - WARN_ON_ONCE(1); - return -EINVAL; - } - flash_buf = kmalloc(FLASH_COUNT_SIZE, GFP_KERNEL); if (!flash_buf) return -ENOMEM; - ret = regmap_bulk_read(sec->m10bmc->regmap, STAGING_FLASH_COUNT, - flash_buf, FLASH_COUNT_SIZE / stride); + ret = m10bmc_sec_read(sec, flash_buf, csr_map->rsu_update_counter, + FLASH_COUNT_SIZE); if (ret) { - dev_err(sec->dev, - "failed to read flash count: %x cnt %x: %d\n", - STAGING_FLASH_COUNT, FLASH_COUNT_SIZE / stride, ret); + dev_err(sec->dev, "failed to read flash count\n"); goto exit_free; } cnt = num_bits - bitmap_weight((unsigned long *)flash_buf, num_bits); @@ -200,25 +255,81 @@ static const struct attribute_group *m10bmc_sec_attr_groups[] = { static void log_error_regs(struct m10bmc_sec *sec, u32 doorbell) { + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; u32 auth_result; - dev_err(sec->dev, "RSU error status: 0x%08x\n", doorbell); + dev_err(sec->dev, "Doorbell: 0x%08x\n", doorbell); - if (!m10bmc_sys_read(sec->m10bmc, M10BMC_AUTH_RESULT, &auth_result)) + if (!m10bmc_sys_read(sec->m10bmc, csr_map->auth_result, &auth_result)) dev_err(sec->dev, "RSU auth result: 0x%08x\n", auth_result); } +static int m10bmc_sec_n3000_rsu_status(struct m10bmc_sec *sec) +{ + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; + u32 doorbell; + int ret; + + ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell); + if (ret) + return ret; + + return FIELD_GET(DRBL_RSU_STATUS, doorbell); +} + +static bool rsu_status_ok(u32 status) +{ + return (status == RSU_STAT_NORMAL || + status == RSU_STAT_NIOS_OK || + status == RSU_STAT_USER_OK || + status == RSU_STAT_FACTORY_OK); +} + +static bool rsu_progress_done(u32 progress) +{ + return (progress == RSU_PROG_IDLE || + progress == RSU_PROG_RSU_DONE); +} + +static bool rsu_progress_busy(u32 progress) +{ + return (progress == RSU_PROG_AUTHENTICATING || + progress == RSU_PROG_COPYING || + progress == RSU_PROG_UPDATE_CANCEL || + progress == RSU_PROG_PROGRAM_KEY_HASH); +} + +static int m10bmc_sec_progress_status(struct m10bmc_sec *sec, u32 *doorbell_reg, + u32 *progress, u32 *status) +{ + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; + int ret; + + ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, doorbell_reg); + if (ret) + return ret; + + ret = sec->ops->rsu_status(sec); + if (ret < 0) + return ret; + + *status = ret; + *progress = rsu_prog(*doorbell_reg); + + return 0; +} + static enum fw_upload_err rsu_check_idle(struct m10bmc_sec *sec) { + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; u32 doorbell; int ret; - ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell); + ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell); if (ret) return FW_UPLOAD_ERR_RW_ERROR; - if (rsu_prog(doorbell) != RSU_PROG_IDLE && - rsu_prog(doorbell) != RSU_PROG_RSU_DONE) { + if (!rsu_progress_done(rsu_prog(doorbell))) { log_error_regs(sec, doorbell); return FW_UPLOAD_ERR_BUSY; } @@ -226,19 +337,15 @@ static enum fw_upload_err rsu_check_idle(struct m10bmc_sec *sec) return FW_UPLOAD_ERR_NONE; } -static inline bool rsu_start_done(u32 doorbell) +static inline bool rsu_start_done(u32 doorbell_reg, u32 progress, u32 status) { - u32 status, progress; - - if (doorbell & DRBL_RSU_REQUEST) + if (doorbell_reg & DRBL_RSU_REQUEST) return false; - status = rsu_stat(doorbell); if (status == RSU_STAT_ERASE_FAIL || status == RSU_STAT_WEAROUT) return true; - progress = rsu_prog(doorbell); - if (progress != RSU_PROG_IDLE && progress != RSU_PROG_RSU_DONE) + if (!rsu_progress_done(progress)) return true; return false; @@ -246,11 +353,12 @@ static inline bool rsu_start_done(u32 doorbell) static enum fw_upload_err rsu_update_init(struct m10bmc_sec *sec) { - u32 doorbell, status; - int ret; + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; + u32 doorbell_reg, progress, status; + int ret, err; ret = regmap_update_bits(sec->m10bmc->regmap, - M10BMC_SYS_BASE + M10BMC_DOORBELL, + csr_map->base + csr_map->doorbell, DRBL_RSU_REQUEST | DRBL_HOST_STATUS, DRBL_RSU_REQUEST | FIELD_PREP(DRBL_HOST_STATUS, @@ -258,26 +366,25 @@ static enum fw_upload_err rsu_update_init(struct m10bmc_sec *sec) if (ret) return FW_UPLOAD_ERR_RW_ERROR; - ret = regmap_read_poll_timeout(sec->m10bmc->regmap, - M10BMC_SYS_BASE + M10BMC_DOORBELL, - doorbell, - rsu_start_done(doorbell), - NIOS_HANDSHAKE_INTERVAL_US, - NIOS_HANDSHAKE_TIMEOUT_US); + ret = read_poll_timeout(m10bmc_sec_progress_status, err, + err < 0 || rsu_start_done(doorbell_reg, progress, status), + NIOS_HANDSHAKE_INTERVAL_US, + NIOS_HANDSHAKE_TIMEOUT_US, + false, + sec, &doorbell_reg, &progress, &status); if (ret == -ETIMEDOUT) { - log_error_regs(sec, doorbell); + log_error_regs(sec, doorbell_reg); return FW_UPLOAD_ERR_TIMEOUT; - } else if (ret) { + } else if (err) { return FW_UPLOAD_ERR_RW_ERROR; } - status = rsu_stat(doorbell); if (status == RSU_STAT_WEAROUT) { dev_warn(sec->dev, "Excessive flash update count detected\n"); return FW_UPLOAD_ERR_WEAROUT; } else if (status == RSU_STAT_ERASE_FAIL) { - log_error_regs(sec, doorbell); + log_error_regs(sec, doorbell_reg); return FW_UPLOAD_ERR_HW_ERROR; } @@ -286,11 +393,12 @@ static enum fw_upload_err rsu_update_init(struct m10bmc_sec *sec) static enum fw_upload_err rsu_prog_ready(struct m10bmc_sec *sec) { + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; unsigned long poll_timeout; u32 doorbell, progress; int ret; - ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell); + ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell); if (ret) return FW_UPLOAD_ERR_RW_ERROR; @@ -300,7 +408,7 @@ static enum fw_upload_err rsu_prog_ready(struct m10bmc_sec *sec) if (time_after(jiffies, poll_timeout)) break; - ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell); + ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell); if (ret) return FW_UPLOAD_ERR_RW_ERROR; } @@ -319,11 +427,12 @@ static enum fw_upload_err rsu_prog_ready(struct m10bmc_sec *sec) static enum fw_upload_err rsu_send_data(struct m10bmc_sec *sec) { - u32 doorbell; + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; + u32 doorbell_reg, status; int ret; ret = regmap_update_bits(sec->m10bmc->regmap, - M10BMC_SYS_BASE + M10BMC_DOORBELL, + csr_map->base + csr_map->doorbell, DRBL_HOST_STATUS, FIELD_PREP(DRBL_HOST_STATUS, HOST_STATUS_WRITE_DONE)); @@ -331,68 +440,58 @@ static enum fw_upload_err rsu_send_data(struct m10bmc_sec *sec) return FW_UPLOAD_ERR_RW_ERROR; ret = regmap_read_poll_timeout(sec->m10bmc->regmap, - M10BMC_SYS_BASE + M10BMC_DOORBELL, - doorbell, - rsu_prog(doorbell) != RSU_PROG_READY, + csr_map->base + csr_map->doorbell, + doorbell_reg, + rsu_prog(doorbell_reg) != RSU_PROG_READY, NIOS_HANDSHAKE_INTERVAL_US, NIOS_HANDSHAKE_TIMEOUT_US); if (ret == -ETIMEDOUT) { - log_error_regs(sec, doorbell); + log_error_regs(sec, doorbell_reg); return FW_UPLOAD_ERR_TIMEOUT; } else if (ret) { return FW_UPLOAD_ERR_RW_ERROR; } - switch (rsu_stat(doorbell)) { - case RSU_STAT_NORMAL: - case RSU_STAT_NIOS_OK: - case RSU_STAT_USER_OK: - case RSU_STAT_FACTORY_OK: - break; - default: - log_error_regs(sec, doorbell); + ret = sec->ops->rsu_status(sec); + if (ret < 0) + return ret; + status = ret; + + if (!rsu_status_ok(status)) { + log_error_regs(sec, doorbell_reg); return FW_UPLOAD_ERR_HW_ERROR; } return FW_UPLOAD_ERR_NONE; } -static int rsu_check_complete(struct m10bmc_sec *sec, u32 *doorbell) +static int rsu_check_complete(struct m10bmc_sec *sec, u32 *doorbell_reg) { - if (m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, doorbell)) + u32 progress, status; + + if (m10bmc_sec_progress_status(sec, doorbell_reg, &progress, &status)) return -EIO; - switch (rsu_stat(*doorbell)) { - case RSU_STAT_NORMAL: - case RSU_STAT_NIOS_OK: - case RSU_STAT_USER_OK: - case RSU_STAT_FACTORY_OK: - break; - default: + if (!rsu_status_ok(status)) return -EINVAL; - } - switch (rsu_prog(*doorbell)) { - case RSU_PROG_IDLE: - case RSU_PROG_RSU_DONE: + if (rsu_progress_done(progress)) return 0; - case RSU_PROG_AUTHENTICATING: - case RSU_PROG_COPYING: - case RSU_PROG_UPDATE_CANCEL: - case RSU_PROG_PROGRAM_KEY_HASH: + + if (rsu_progress_busy(progress)) return -EAGAIN; - default: - return -EINVAL; - } + + return -EINVAL; } static enum fw_upload_err rsu_cancel(struct m10bmc_sec *sec) { + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; u32 doorbell; int ret; - ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell); + ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell); if (ret) return FW_UPLOAD_ERR_RW_ERROR; @@ -400,7 +499,7 @@ static enum fw_upload_err rsu_cancel(struct m10bmc_sec *sec) return FW_UPLOAD_ERR_BUSY; ret = regmap_update_bits(sec->m10bmc->regmap, - M10BMC_SYS_BASE + M10BMC_DOORBELL, + csr_map->base + csr_map->doorbell, DRBL_HOST_STATUS, FIELD_PREP(DRBL_HOST_STATUS, HOST_STATUS_ABORT_RSU)); @@ -441,19 +540,19 @@ static enum fw_upload_err m10bmc_sec_prepare(struct fw_upload *fwl, #define WRITE_BLOCK_SIZE 0x4000 /* Default write-block size is 0x4000 bytes */ -static enum fw_upload_err m10bmc_sec_write(struct fw_upload *fwl, const u8 *data, - u32 offset, u32 size, u32 *written) +static enum fw_upload_err m10bmc_sec_fw_write(struct fw_upload *fwl, const u8 *data, + u32 offset, u32 size, u32 *written) { struct m10bmc_sec *sec = fwl->dd_handle; - u32 blk_size, doorbell, extra_offset; - unsigned int stride, extra = 0; + const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; + struct intel_m10bmc *m10bmc = sec->m10bmc; + u32 blk_size, doorbell; int ret; - stride = regmap_get_reg_stride(sec->m10bmc->regmap); if (sec->cancel_request) return rsu_cancel(sec); - ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell); + ret = m10bmc_sys_read(m10bmc, csr_map->doorbell, &doorbell); if (ret) { return FW_UPLOAD_ERR_RW_ERROR; } else if (rsu_prog(doorbell) != RSU_PROG_READY) { @@ -461,28 +560,12 @@ static enum fw_upload_err m10bmc_sec_write(struct fw_upload *fwl, const u8 *data return FW_UPLOAD_ERR_HW_ERROR; } - WARN_ON_ONCE(WRITE_BLOCK_SIZE % stride); + WARN_ON_ONCE(WRITE_BLOCK_SIZE % regmap_get_reg_stride(m10bmc->regmap)); blk_size = min_t(u32, WRITE_BLOCK_SIZE, size); - ret = regmap_bulk_write(sec->m10bmc->regmap, - M10BMC_STAGING_BASE + offset, - (void *)data + offset, - blk_size / stride); + ret = m10bmc_sec_write(sec, data, offset, blk_size); if (ret) return FW_UPLOAD_ERR_RW_ERROR; - /* - * If blk_size is not aligned to stride, then handle the extra - * bytes with regmap_write. - */ - if (blk_size % stride) { - extra_offset = offset + ALIGN_DOWN(blk_size, stride); - memcpy(&extra, (u8 *)(data + extra_offset), blk_size % stride); - ret = regmap_write(sec->m10bmc->regmap, - M10BMC_STAGING_BASE + extra_offset, extra); - if (ret) - return FW_UPLOAD_ERR_RW_ERROR; - } - *written = blk_size; return FW_UPLOAD_ERR_NONE; } @@ -543,12 +626,16 @@ static void m10bmc_sec_cleanup(struct fw_upload *fwl) static const struct fw_upload_ops m10bmc_ops = { .prepare = m10bmc_sec_prepare, - .write = m10bmc_sec_write, + .write = m10bmc_sec_fw_write, .poll_complete = m10bmc_sec_poll_complete, .cancel = m10bmc_sec_cancel, .cleanup = m10bmc_sec_cleanup, }; +static const struct m10bmc_sec_ops m10sec_n3000_ops = { + .rsu_status = m10bmc_sec_n3000_rsu_status, +}; + #define SEC_UPDATE_LEN_MAX 32 static int m10bmc_sec_probe(struct platform_device *pdev) { @@ -564,6 +651,7 @@ static int m10bmc_sec_probe(struct platform_device *pdev) sec->dev = &pdev->dev; sec->m10bmc = dev_get_drvdata(pdev->dev.parent); + sec->ops = (struct m10bmc_sec_ops *)platform_get_device_id(pdev)->driver_data; dev_set_drvdata(&pdev->dev, sec); ret = xa_alloc(&fw_upload_xa, &sec->fw_name_id, sec, @@ -611,9 +699,11 @@ static int m10bmc_sec_remove(struct platform_device *pdev) static const struct platform_device_id intel_m10bmc_sec_ids[] = { { .name = "n3000bmc-sec-update", + .driver_data = (kernel_ulong_t)&m10sec_n3000_ops, }, { .name = "d5005bmc-sec-update", + .driver_data = (kernel_ulong_t)&m10sec_n3000_ops, }, { } }; diff --git a/drivers/fpga/microchip-spi.c b/drivers/fpga/microchip-spi.c index 7436976ea904..d6070e7f5205 100644 --- a/drivers/fpga/microchip-spi.c +++ b/drivers/fpga/microchip-spi.c @@ -6,6 +6,7 @@ #include <asm/unaligned.h> #include <linux/delay.h> #include <linux/fpga/fpga-mgr.h> +#include <linux/iopoll.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/spi/spi.h> @@ -33,7 +34,7 @@ #define MPF_BITS_PER_COMPONENT_SIZE 22 -#define MPF_STATUS_POLL_RETRIES 10000 +#define MPF_STATUS_POLL_TIMEOUT (2 * USEC_PER_SEC) #define MPF_STATUS_BUSY BIT(0) #define MPF_STATUS_READY BIT(1) #define MPF_STATUS_SPI_VIOLATION BIT(2) @@ -42,46 +43,55 @@ struct mpf_priv { struct spi_device *spi; bool program_mode; + u8 tx __aligned(ARCH_KMALLOC_MINALIGN); + u8 rx; }; -static int mpf_read_status(struct spi_device *spi) +static int mpf_read_status(struct mpf_priv *priv) { - u8 status = 0, status_command = MPF_SPI_READ_STATUS; - struct spi_transfer xfers[2] = { 0 }; - int ret; - /* * HW status is returned on MISO in the first byte after CS went * active. However, first reading can be inadequate, so we submit * two identical SPI transfers and use result of the later one. */ - xfers[0].tx_buf = &status_command; - xfers[1].tx_buf = &status_command; - xfers[0].rx_buf = &status; - xfers[1].rx_buf = &status; - xfers[0].len = 1; - xfers[1].len = 1; - xfers[0].cs_change = 1; + struct spi_transfer xfers[2] = { + { + .tx_buf = &priv->tx, + .rx_buf = &priv->rx, + .len = 1, + .cs_change = 1, + }, { + .tx_buf = &priv->tx, + .rx_buf = &priv->rx, + .len = 1, + }, + }; + u8 status; + int ret; - ret = spi_sync_transfer(spi, xfers, 2); + priv->tx = MPF_SPI_READ_STATUS; + + ret = spi_sync_transfer(priv->spi, xfers, 2); + if (ret) + return ret; + + status = priv->rx; if ((status & MPF_STATUS_SPI_VIOLATION) || (status & MPF_STATUS_SPI_ERROR)) - ret = -EIO; + return -EIO; - return ret ? : status; + return status; } static enum fpga_mgr_states mpf_ops_state(struct fpga_manager *mgr) { struct mpf_priv *priv = mgr->priv; - struct spi_device *spi; bool program_mode; int status; - spi = priv->spi; program_mode = priv->program_mode; - status = mpf_read_status(spi); + status = mpf_read_status(priv); if (!program_mode && !status) return FPGA_MGR_STATE_OPERATING; @@ -185,52 +195,53 @@ static int mpf_ops_parse_header(struct fpga_manager *mgr, return 0; } -/* Poll HW status until busy bit is cleared and mask bits are set. */ -static int mpf_poll_status(struct spi_device *spi, u8 mask) +static int mpf_poll_status(struct mpf_priv *priv, u8 mask) { - int status, retries = MPF_STATUS_POLL_RETRIES; - - while (retries--) { - status = mpf_read_status(spi); - if (status < 0) - return status; + int ret, status; - if (status & MPF_STATUS_BUSY) - continue; - - if (!mask || (status & mask)) - return status; - } + /* + * Busy poll HW status. Polling stops if any of the following + * conditions are met: + * - timeout is reached + * - mpf_read_status() returns an error + * - busy bit is cleared AND mask bits are set + */ + ret = read_poll_timeout(mpf_read_status, status, + (status < 0) || + ((status & (MPF_STATUS_BUSY | mask)) == mask), + 0, MPF_STATUS_POLL_TIMEOUT, false, priv); + if (ret < 0) + return ret; - return -EBUSY; + return status; } -static int mpf_spi_write(struct spi_device *spi, const void *buf, size_t buf_size) +static int mpf_spi_write(struct mpf_priv *priv, const void *buf, size_t buf_size) { - int status = mpf_poll_status(spi, 0); + int status = mpf_poll_status(priv, 0); if (status < 0) return status; - return spi_write(spi, buf, buf_size); + return spi_write_then_read(priv->spi, buf, buf_size, NULL, 0); } -static int mpf_spi_write_then_read(struct spi_device *spi, +static int mpf_spi_write_then_read(struct mpf_priv *priv, const void *txbuf, size_t txbuf_size, void *rxbuf, size_t rxbuf_size) { const u8 read_command[] = { MPF_SPI_READ_DATA }; int ret; - ret = mpf_spi_write(spi, txbuf, txbuf_size); + ret = mpf_spi_write(priv, txbuf, txbuf_size); if (ret) return ret; - ret = mpf_poll_status(spi, MPF_STATUS_READY); + ret = mpf_poll_status(priv, MPF_STATUS_READY); if (ret < 0) return ret; - return spi_write_then_read(spi, read_command, sizeof(read_command), + return spi_write_then_read(priv->spi, read_command, sizeof(read_command), rxbuf, rxbuf_size); } @@ -242,7 +253,6 @@ static int mpf_ops_write_init(struct fpga_manager *mgr, const u8 isc_en_command[] = { MPF_SPI_ISC_ENABLE }; struct mpf_priv *priv = mgr->priv; struct device *dev = &mgr->dev; - struct spi_device *spi; u32 isc_ret = 0; int ret; @@ -251,9 +261,7 @@ static int mpf_ops_write_init(struct fpga_manager *mgr, return -EOPNOTSUPP; } - spi = priv->spi; - - ret = mpf_spi_write_then_read(spi, isc_en_command, sizeof(isc_en_command), + ret = mpf_spi_write_then_read(priv, isc_en_command, sizeof(isc_en_command), &isc_ret, sizeof(isc_ret)); if (ret || isc_ret) { dev_err(dev, "Failed to enable ISC: spi_ret %d, isc_ret %u\n", @@ -261,7 +269,7 @@ static int mpf_ops_write_init(struct fpga_manager *mgr, return -EFAULT; } - ret = mpf_spi_write(spi, program_mode, sizeof(program_mode)); + ret = mpf_spi_write(priv, program_mode, sizeof(program_mode)); if (ret) { dev_err(dev, "Failed to enter program mode: %d\n", ret); return ret; @@ -272,13 +280,32 @@ static int mpf_ops_write_init(struct fpga_manager *mgr, return 0; } +static int mpf_spi_frame_write(struct mpf_priv *priv, const char *buf) +{ + struct spi_transfer xfers[2] = { + { + .tx_buf = &priv->tx, + .len = 1, + }, { + .tx_buf = buf, + .len = MPF_SPI_FRAME_SIZE, + }, + }; + int ret; + + ret = mpf_poll_status(priv, 0); + if (ret < 0) + return ret; + + priv->tx = MPF_SPI_FRAME; + + return spi_sync_transfer(priv->spi, xfers, ARRAY_SIZE(xfers)); +} + static int mpf_ops_write(struct fpga_manager *mgr, const char *buf, size_t count) { - u8 spi_frame_command[] = { MPF_SPI_FRAME }; - struct spi_transfer xfers[2] = { 0 }; struct mpf_priv *priv = mgr->priv; struct device *dev = &mgr->dev; - struct spi_device *spi; int ret, i; if (count % MPF_SPI_FRAME_SIZE) { @@ -287,19 +314,8 @@ static int mpf_ops_write(struct fpga_manager *mgr, const char *buf, size_t count return -EINVAL; } - spi = priv->spi; - - xfers[0].tx_buf = spi_frame_command; - xfers[0].len = sizeof(spi_frame_command); - for (i = 0; i < count / MPF_SPI_FRAME_SIZE; i++) { - xfers[1].tx_buf = buf + i * MPF_SPI_FRAME_SIZE; - xfers[1].len = MPF_SPI_FRAME_SIZE; - - ret = mpf_poll_status(spi, 0); - if (ret >= 0) - ret = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers)); - + ret = mpf_spi_frame_write(priv, buf + i * MPF_SPI_FRAME_SIZE); if (ret) { dev_err(dev, "Failed to write bitstream frame %d/%zu\n", i, count / MPF_SPI_FRAME_SIZE); @@ -317,12 +333,9 @@ static int mpf_ops_write_complete(struct fpga_manager *mgr, const u8 release_command[] = { MPF_SPI_RELEASE }; struct mpf_priv *priv = mgr->priv; struct device *dev = &mgr->dev; - struct spi_device *spi; int ret; - spi = priv->spi; - - ret = mpf_spi_write(spi, isc_dis_command, sizeof(isc_dis_command)); + ret = mpf_spi_write(priv, isc_dis_command, sizeof(isc_dis_command)); if (ret) { dev_err(dev, "Failed to disable ISC: %d\n", ret); return ret; @@ -330,7 +343,7 @@ static int mpf_ops_write_complete(struct fpga_manager *mgr, usleep_range(1000, 2000); - ret = mpf_spi_write(spi, release_command, sizeof(release_command)); + ret = mpf_spi_write(priv, release_command, sizeof(release_command)); if (ret) { dev_err(dev, "Failed to exit program mode: %d\n", ret); return ret; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 3176c33af6c6..42c28e887df3 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2341,7 +2341,7 @@ config SENSORS_XGENE config SENSORS_INTEL_M10_BMC_HWMON tristate "Intel MAX10 BMC Hardware Monitoring" - depends on MFD_INTEL_M10_BMC + depends on MFD_INTEL_M10_BMC_CORE help This driver provides support for the hardware monitoring functionality on Intel MAX10 BMC chip. diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 45c1eb5dfcb7..2b5bbfffbc4f 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -201,4 +201,39 @@ config CORESIGHT_TRBE To compile this driver as a module, choose M here: the module will be called coresight-trbe. + +config ULTRASOC_SMB + tristate "Ultrasoc system memory buffer drivers" + depends on ACPI || COMPILE_TEST + depends on ARM64 && CORESIGHT_LINKS_AND_SINKS + help + This driver provides support for the Ultrasoc system memory buffer (SMB). + SMB is responsible for receiving the trace data from Coresight ETM devices + and storing them to a system buffer. + + To compile this driver as a module, choose M here: the module will be + called ultrasoc-smb. + +config CORESIGHT_TPDM + tristate "CoreSight Trace, Profiling & Diagnostics Monitor driver" + select CORESIGHT_LINKS_AND_SINKS + select CORESIGHT_TPDA + help + This driver provides support for configuring monitor. Monitors are + primarily responsible for data set collection and support the + ability to collect any permutation of data set types. + + To compile this driver as a module, choose M here: the module will be + called coresight-tpdm. + +config CORESIGHT_TPDA + tristate "CoreSight Trace, Profiling & Diagnostics Aggregator driver" + help + This driver provides support for configuring aggregator. This is + primarily useful for pulling the data sets from one or more + attached monitors and pushing the resultant data out. Multiple + monitors are connected on different input ports of TPDA. + + To compile this driver as a module, choose M here: the module will be + called coresight-tpda. endif diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index b6c4a48140ec..33bcc3f7b8ae 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_CORESIGHT) += coresight.o coresight-y := coresight-core.o coresight-etm-perf.o coresight-platform.o \ coresight-sysfs.o coresight-syscfg.o coresight-config.o \ coresight-cfg-preload.o coresight-cfg-afdo.o \ - coresight-syscfg-configfs.o + coresight-syscfg-configfs.o coresight-trace-id.o obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o coresight-tmc-y := coresight-tmc-core.o coresight-tmc-etf.o \ coresight-tmc-etr.o @@ -25,5 +25,8 @@ obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o obj-$(CONFIG_CORESIGHT_TRBE) += coresight-trbe.o +obj-$(CONFIG_CORESIGHT_TPDM) += coresight-tpdm.o +obj-$(CONFIG_CORESIGHT_TPDA) += coresight-tpda.o coresight-cti-y := coresight-cti-core.o coresight-cti-platform.o \ coresight-cti-sysfs.o +obj-$(CONFIG_ULTRASOC_SMB) += ultrasoc-smb.o diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index f3068175ca9d..d3bf82c0de1d 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -8,6 +8,7 @@ #include <linux/types.h> #include <linux/device.h> #include <linux/io.h> +#include <linux/idr.h> #include <linux/err.h> #include <linux/export.h> #include <linux/slab.h> @@ -26,6 +27,13 @@ static DEFINE_MUTEX(coresight_mutex); static DEFINE_PER_CPU(struct coresight_device *, csdev_sink); +/* + * Use IDR to map the hash of the source's device name + * to the pointer of path for the source. The idr is for + * the sources which aren't associated with CPU. + */ +static DEFINE_IDR(path_idr); + /** * struct coresight_node - elements of a path, from source to sink * @csdev: Address of an element. @@ -43,14 +51,6 @@ struct coresight_node { static DEFINE_PER_CPU(struct list_head *, tracer_path); /* - * As of this writing only a single STM can be found in CS topologies. Since - * there is no way to know if we'll ever see more and what kind of - * configuration they will enact, for the time being only define a single path - * for STM. - */ -static struct list_head *stm_path; - -/* * When losing synchronisation a new barrier packet needs to be inserted at the * beginning of the data collected in a buffer. That way the decoder knows that * it needs to look for another sync sequence. @@ -112,45 +112,6 @@ struct coresight_device *coresight_get_percpu_sink(int cpu) } EXPORT_SYMBOL_GPL(coresight_get_percpu_sink); -static int coresight_id_match(struct device *dev, void *data) -{ - int trace_id, i_trace_id; - struct coresight_device *csdev, *i_csdev; - - csdev = data; - i_csdev = to_coresight_device(dev); - - /* - * No need to care about oneself and components that are not - * sources or not enabled - */ - if (i_csdev == csdev || !i_csdev->enable || - i_csdev->type != CORESIGHT_DEV_TYPE_SOURCE) - return 0; - - /* Get the source ID for both components */ - trace_id = source_ops(csdev)->trace_id(csdev); - i_trace_id = source_ops(i_csdev)->trace_id(i_csdev); - - /* All you need is one */ - if (trace_id == i_trace_id) - return 1; - - return 0; -} - -static int coresight_source_is_unique(struct coresight_device *csdev) -{ - int trace_id = source_ops(csdev)->trace_id(csdev); - - /* this shouldn't happen */ - if (trace_id < 0) - return 0; - - return !bus_for_each_dev(&coresight_bustype, NULL, - csdev, coresight_id_match); -} - static int coresight_find_link_inport(struct coresight_device *csdev, struct coresight_device *parent) { @@ -459,12 +420,6 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode) { int ret; - if (!coresight_source_is_unique(csdev)) { - dev_warn(&csdev->dev, "traceID %d not unique\n", - source_ops(csdev)->trace_id(csdev)); - return -EINVAL; - } - if (!csdev->enable) { if (source_ops(csdev)->enable) { ret = coresight_control_assoc_ectdev(csdev, true); @@ -1106,7 +1061,8 @@ static int coresight_validate_source(struct coresight_device *csdev, } if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC && - subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE) { + subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE && + subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS) { dev_err(&csdev->dev, "wrong device subtype in %s\n", function); return -EINVAL; } @@ -1120,6 +1076,7 @@ int coresight_enable(struct coresight_device *csdev) struct coresight_device *sink; struct list_head *path; enum coresight_dev_subtype_source subtype; + u32 hash; subtype = csdev->subtype.source_subtype; @@ -1174,7 +1131,15 @@ int coresight_enable(struct coresight_device *csdev) per_cpu(tracer_path, cpu) = path; break; case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: - stm_path = path; + case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: + /* + * Use the hash of source's device name as ID + * and map the ID to the pointer of the path. + */ + hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); + ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL); + if (ret) + goto err_source; break; default: /* We can't be here */ @@ -1198,6 +1163,7 @@ void coresight_disable(struct coresight_device *csdev) { int cpu, ret; struct list_head *path = NULL; + u32 hash; mutex_lock(&coresight_mutex); @@ -1215,8 +1181,15 @@ void coresight_disable(struct coresight_device *csdev) per_cpu(tracer_path, cpu) = NULL; break; case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: - path = stm_path; - stm_path = NULL; + case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS: + hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev))); + /* Find the path by the hash. */ + path = idr_find(&path_idr, hash); + if (path == NULL) { + pr_err("Path is not found for %s\n", dev_name(&csdev->dev)); + goto out; + } + idr_remove(&path_idr, hash); break; default: /* We can't be here */ diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c index d2cf4f4848e1..277c890a1f1f 100644 --- a/drivers/hwtracing/coresight/coresight-cti-core.c +++ b/drivers/hwtracing/coresight/coresight-cti-core.c @@ -107,12 +107,12 @@ static int cti_enable_hw(struct cti_drvdata *drvdata) cti_write_all_hw_regs(drvdata); config->hw_enabled = true; - atomic_inc(&drvdata->config.enable_req_count); + drvdata->config.enable_req_count++; spin_unlock_irqrestore(&drvdata->spinlock, flags); return rc; cti_state_unchanged: - atomic_inc(&drvdata->config.enable_req_count); + drvdata->config.enable_req_count++; /* cannot enable due to error */ cti_err_not_enabled: @@ -129,7 +129,7 @@ static void cti_cpuhp_enable_hw(struct cti_drvdata *drvdata) config->hw_powered = true; /* no need to do anything if no enable request */ - if (!atomic_read(&drvdata->config.enable_req_count)) + if (!drvdata->config.enable_req_count) goto cti_hp_not_enabled; /* try to claim the device */ @@ -151,11 +151,18 @@ static int cti_disable_hw(struct cti_drvdata *drvdata) { struct cti_config *config = &drvdata->config; struct coresight_device *csdev = drvdata->csdev; + int ret = 0; spin_lock(&drvdata->spinlock); + /* don't allow negative refcounts, return an error */ + if (!drvdata->config.enable_req_count) { + ret = -EINVAL; + goto cti_not_disabled; + } + /* check refcount - disable on 0 */ - if (atomic_dec_return(&drvdata->config.enable_req_count) > 0) + if (--drvdata->config.enable_req_count > 0) goto cti_not_disabled; /* no need to do anything if disabled or cpu unpowered */ @@ -171,12 +178,12 @@ static int cti_disable_hw(struct cti_drvdata *drvdata) coresight_disclaim_device_unlocked(csdev); CS_LOCK(drvdata->base); spin_unlock(&drvdata->spinlock); - return 0; + return ret; /* not disabled this call */ cti_not_disabled: spin_unlock(&drvdata->spinlock); - return 0; + return ret; } void cti_write_single_reg(struct cti_drvdata *drvdata, int offset, u32 value) @@ -232,7 +239,7 @@ static void cti_set_default_config(struct device *dev, /* Most regs default to 0 as zalloc'ed except...*/ config->trig_filter_enable = true; config->ctigate = GENMASK(config->nr_ctm_channels - 1, 0); - atomic_set(&config->enable_req_count, 0); + config->enable_req_count = 0; } /* @@ -689,7 +696,7 @@ static int cti_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd, drvdata->config.hw_enabled = false; /* check enable reference count to enable HW */ - if (atomic_read(&drvdata->config.enable_req_count)) { + if (drvdata->config.enable_req_count) { /* check we can claim the device as we re-power */ if (coresight_claim_device(csdev)) goto cti_notify_exit; diff --git a/drivers/hwtracing/coresight/coresight-cti-sysfs.c b/drivers/hwtracing/coresight/coresight-cti-sysfs.c index 6d59c815ecf5..e528cff9d4e2 100644 --- a/drivers/hwtracing/coresight/coresight-cti-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-cti-sysfs.c @@ -84,8 +84,8 @@ static ssize_t enable_show(struct device *dev, bool enabled, powered; struct cti_drvdata *drvdata = dev_get_drvdata(dev->parent); - enable_req = atomic_read(&drvdata->config.enable_req_count); spin_lock(&drvdata->spinlock); + enable_req = drvdata->config.enable_req_count; powered = drvdata->config.hw_powered; enabled = drvdata->config.hw_enabled; spin_unlock(&drvdata->spinlock); @@ -108,10 +108,19 @@ static ssize_t enable_store(struct device *dev, if (ret) return ret; - if (val) + if (val) { + ret = pm_runtime_resume_and_get(dev->parent); + if (ret) + return ret; ret = cti_enable(drvdata->csdev); - else + if (ret) + pm_runtime_put(dev->parent); + } else { ret = cti_disable(drvdata->csdev); + if (!ret) + pm_runtime_put(dev->parent); + } + if (ret) return ret; return size; diff --git a/drivers/hwtracing/coresight/coresight-cti.h b/drivers/hwtracing/coresight/coresight-cti.h index acf7b545e6b9..8b106b13a244 100644 --- a/drivers/hwtracing/coresight/coresight-cti.h +++ b/drivers/hwtracing/coresight/coresight-cti.h @@ -141,7 +141,7 @@ struct cti_config { int nr_trig_max; /* cti enable control */ - atomic_t enable_req_count; + int enable_req_count; bool hw_enabled; bool hw_powered; diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 43bbd5dc3d3b..a48c97da8165 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -4,6 +4,7 @@ * Author: Mathieu Poirier <mathieu.poirier@linaro.org> */ +#include <linux/bitfield.h> #include <linux/coresight.h> #include <linux/coresight-pmu.h> #include <linux/cpumask.h> @@ -22,6 +23,7 @@ #include "coresight-etm-perf.h" #include "coresight-priv.h" #include "coresight-syscfg.h" +#include "coresight-trace-id.h" static struct pmu etm_pmu; static bool etm_perf_up; @@ -228,8 +230,12 @@ static void free_event_data(struct work_struct *work) if (!(IS_ERR_OR_NULL(*ppath))) coresight_release_path(*ppath); *ppath = NULL; + coresight_trace_id_put_cpu_id(cpu); } + /* mark perf event as done for trace id allocator */ + coresight_trace_id_perf_stop(); + free_percpu(event_data->path); kfree(event_data); } @@ -300,6 +306,7 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, { u32 id, cfg_hash; int cpu = event->cpu; + int trace_id; cpumask_t *mask; struct coresight_device *sink = NULL; struct coresight_device *user_sink = NULL, *last_sink = NULL; @@ -316,6 +323,9 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, sink = user_sink = coresight_get_sink_by_id(id); } + /* tell the trace ID allocator that a perf event is starting up */ + coresight_trace_id_perf_start(); + /* check if user wants a coresight configuration selected */ cfg_hash = (u32)((event->attr.config2 & GENMASK_ULL(63, 32)) >> 32); if (cfg_hash) { @@ -388,6 +398,13 @@ static void *etm_setup_aux(struct perf_event *event, void **pages, continue; } + /* ensure we can allocate a trace ID for this CPU */ + trace_id = coresight_trace_id_get_cpu_id(cpu); + if (!IS_VALID_CS_TRACE_ID(trace_id)) { + cpumask_clear_cpu(cpu, mask); + continue; + } + *etm_event_cpu_path_ptr(event_data, cpu) = path; } @@ -432,6 +449,7 @@ static void etm_event_start(struct perf_event *event, int flags) struct perf_output_handle *handle = &ctxt->handle; struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu); struct list_head *path; + u64 hw_id; if (!csdev) goto fail; @@ -477,6 +495,19 @@ static void etm_event_start(struct perf_event *event, int flags) if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF)) goto fail_disable_path; + /* + * output cpu / trace ID in perf record, once for the lifetime + * of the event. + */ + if (!cpumask_test_cpu(cpu, &event_data->aux_hwid_done)) { + cpumask_set_cpu(cpu, &event_data->aux_hwid_done); + hw_id = FIELD_PREP(CS_AUX_HW_ID_VERSION_MASK, + CS_AUX_HW_ID_CURR_VERSION); + hw_id |= FIELD_PREP(CS_AUX_HW_ID_TRACE_ID_MASK, + coresight_trace_id_read_cpu_id(cpu)); + perf_report_aux_output_id(event, hw_id); + } + out: /* Tell the perf core the event is alive */ event->hw.state = 0; diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h index 468f7799ab4f..bebbadee2ceb 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.h +++ b/drivers/hwtracing/coresight/coresight-etm-perf.h @@ -48,6 +48,7 @@ struct etm_filters { * struct etm_event_data - Coresight specifics associated to an event * @work: Handle to free allocated memory outside IRQ context. * @mask: Hold the CPU(s) this event was set for. + * @aux_hwid_done: Whether a CPU has emitted the TraceID packet or not. * @snk_config: The sink configuration. * @cfg_hash: The hash id of any coresight config selected. * @path: An array of path, each slot for one CPU. @@ -55,6 +56,7 @@ struct etm_filters { struct etm_event_data { struct work_struct work; cpumask_t mask; + cpumask_t aux_hwid_done; void *snk_config; u32 cfg_hash; struct list_head * __percpu *path; diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index f3ab96eaf44e..9a0d08b092ae 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -283,8 +283,9 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) } extern const struct attribute_group *coresight_etm_groups[]; -int etm_get_trace_id(struct etm_drvdata *drvdata); void etm_set_default(struct etm_config *config); void etm_config_trace_mode(struct etm_config *config); struct etm_config *get_etm_config(struct etm_drvdata *drvdata); +int etm_read_alloc_trace_id(struct etm_drvdata *drvdata); +void etm_release_trace_id(struct etm_drvdata *drvdata); #endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c index d0ab9933472b..afc57195ee52 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c @@ -32,6 +32,7 @@ #include "coresight-etm.h" #include "coresight-etm-perf.h" +#include "coresight-trace-id.h" /* * Not really modular but using module_param is the easiest way to @@ -454,52 +455,59 @@ static int etm_cpu_id(struct coresight_device *csdev) return drvdata->cpu; } -int etm_get_trace_id(struct etm_drvdata *drvdata) +int etm_read_alloc_trace_id(struct etm_drvdata *drvdata) { - unsigned long flags; - int trace_id = -1; - struct device *etm_dev; + int trace_id; - if (!drvdata) - goto out; - - etm_dev = drvdata->csdev->dev.parent; - if (!local_read(&drvdata->mode)) - return drvdata->traceid; - - pm_runtime_get_sync(etm_dev); - - spin_lock_irqsave(&drvdata->spinlock, flags); - - CS_UNLOCK(drvdata->base); - trace_id = (etm_readl(drvdata, ETMTRACEIDR) & ETM_TRACEID_MASK); - CS_LOCK(drvdata->base); - - spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(etm_dev); - -out: + /* + * This will allocate a trace ID to the cpu, + * or return the one currently allocated. + * + * trace id function has its own lock + */ + trace_id = coresight_trace_id_get_cpu_id(drvdata->cpu); + if (IS_VALID_CS_TRACE_ID(trace_id)) + drvdata->traceid = (u8)trace_id; + else + dev_err(&drvdata->csdev->dev, + "Failed to allocate trace ID for %s on CPU%d\n", + dev_name(&drvdata->csdev->dev), drvdata->cpu); return trace_id; - } -static int etm_trace_id(struct coresight_device *csdev) +void etm_release_trace_id(struct etm_drvdata *drvdata) { - struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - return etm_get_trace_id(drvdata); + coresight_trace_id_put_cpu_id(drvdata->cpu); } static int etm_enable_perf(struct coresight_device *csdev, struct perf_event *event) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + int trace_id; if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) return -EINVAL; /* Configure the tracer based on the session's specifics */ etm_parse_event_config(drvdata, event); + + /* + * perf allocates cpu ids as part of _setup_aux() - device needs to use + * the allocated ID. This reads the current version without allocation. + * + * This does not use the trace id lock to prevent lock_dep issues + * with perf locks - we know the ID cannot change until perf shuts down + * the session + */ + trace_id = coresight_trace_id_read_cpu_id(drvdata->cpu); + if (!IS_VALID_CS_TRACE_ID(trace_id)) { + dev_err(&drvdata->csdev->dev, "Failed to set trace ID for %s on CPU%d\n", + dev_name(&drvdata->csdev->dev), drvdata->cpu); + return -EINVAL; + } + drvdata->traceid = (u8)trace_id; + /* And enable it */ return etm_enable_hw(drvdata); } @@ -512,6 +520,11 @@ static int etm_enable_sysfs(struct coresight_device *csdev) spin_lock(&drvdata->spinlock); + /* sysfs needs to allocate and set a trace ID */ + ret = etm_read_alloc_trace_id(drvdata); + if (ret < 0) + goto unlock_enable_sysfs; + /* * Configure the ETM only if the CPU is online. If it isn't online * hw configuration will take place on the local CPU during bring up. @@ -528,6 +541,10 @@ static int etm_enable_sysfs(struct coresight_device *csdev) ret = -ENODEV; } + if (ret) + etm_release_trace_id(drvdata); + +unlock_enable_sysfs: spin_unlock(&drvdata->spinlock); if (!ret) @@ -611,6 +628,12 @@ static void etm_disable_perf(struct coresight_device *csdev) coresight_disclaim_device_unlocked(csdev); CS_LOCK(drvdata->base); + + /* + * perf will release trace ids when _free_aux() + * is called at the end of the session + */ + } static void etm_disable_sysfs(struct coresight_device *csdev) @@ -635,6 +658,13 @@ static void etm_disable_sysfs(struct coresight_device *csdev) spin_unlock(&drvdata->spinlock); cpus_read_unlock(); + /* + * we only release trace IDs when resetting sysfs. + * This permits sysfs users to read the trace ID after the trace + * session has completed. This maintains operational behaviour with + * prior trace id allocation method + */ + dev_dbg(&csdev->dev, "ETM tracing disabled\n"); } @@ -671,7 +701,6 @@ static void etm_disable(struct coresight_device *csdev, static const struct coresight_ops_source etm_source_ops = { .cpu_id = etm_cpu_id, - .trace_id = etm_trace_id, .enable = etm_enable, .disable = etm_disable, }; @@ -781,11 +810,6 @@ static void etm_init_arch_data(void *info) CS_LOCK(drvdata->base); } -static void etm_init_trace_id(struct etm_drvdata *drvdata) -{ - drvdata->traceid = coresight_get_trace_id(drvdata->cpu); -} - static int __init etm_hp_setup(void) { int ret; @@ -871,7 +895,6 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) if (etm_arch_supported(drvdata->arch) == false) return -EINVAL; - etm_init_trace_id(drvdata); etm_set_default(&drvdata->config); pdata = coresight_get_platform_data(dev); diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index fd81eca3ec18..2f271b7fb048 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -85,6 +85,7 @@ static ssize_t reset_store(struct device *dev, } etm_set_default(config); + etm_release_trace_id(drvdata); spin_unlock(&drvdata->spinlock); } @@ -1189,30 +1190,16 @@ static DEVICE_ATTR_RO(cpu); static ssize_t traceid_show(struct device *dev, struct device_attribute *attr, char *buf) { - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = etm_get_trace_id(drvdata); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t traceid_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; + int trace_id; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; + trace_id = etm_read_alloc_trace_id(drvdata); + if (trace_id < 0) + return trace_id; - drvdata->traceid = val & ETM_TRACEID_MASK; - return size; + return sysfs_emit(buf, "%#x\n", trace_id); } -static DEVICE_ATTR_RW(traceid); +static DEVICE_ATTR_RO(traceid); static struct attribute *coresight_etm_attrs[] = { &dev_attr_nr_addr_cmp.attr, diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 1cc052979e01..1ea8f173cca0 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -42,6 +42,7 @@ #include "coresight-etm4x-cfg.h" #include "coresight-self-hosted-trace.h" #include "coresight-syscfg.h" +#include "coresight-trace-id.h" static int boot_enable; module_param(boot_enable, int, 0444); @@ -230,11 +231,28 @@ static int etm4_cpu_id(struct coresight_device *csdev) return drvdata->cpu; } -static int etm4_trace_id(struct coresight_device *csdev) +int etm4_read_alloc_trace_id(struct etmv4_drvdata *drvdata) { - struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + int trace_id; + + /* + * This will allocate a trace ID to the cpu, + * or return the one currently allocated. + * The trace id function has its own lock + */ + trace_id = coresight_trace_id_get_cpu_id(drvdata->cpu); + if (IS_VALID_CS_TRACE_ID(trace_id)) + drvdata->trcid = (u8)trace_id; + else + dev_err(&drvdata->csdev->dev, + "Failed to allocate trace ID for %s on CPU%d\n", + dev_name(&drvdata->csdev->dev), drvdata->cpu); + return trace_id; +} - return drvdata->trcid; +void etm4_release_trace_id(struct etmv4_drvdata *drvdata) +{ + coresight_trace_id_put_cpu_id(drvdata->cpu); } struct etm4_enable_arg { @@ -427,8 +445,10 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata) etm4x_relaxed_write32(csa, config->vipcssctlr, TRCVIPCSSCTLR); for (i = 0; i < drvdata->nrseqstate - 1; i++) etm4x_relaxed_write32(csa, config->seq_ctrl[i], TRCSEQEVRn(i)); - etm4x_relaxed_write32(csa, config->seq_rst, TRCSEQRSTEVR); - etm4x_relaxed_write32(csa, config->seq_state, TRCSEQSTR); + if (drvdata->nrseqstate) { + etm4x_relaxed_write32(csa, config->seq_rst, TRCSEQRSTEVR); + etm4x_relaxed_write32(csa, config->seq_state, TRCSEQSTR); + } etm4x_relaxed_write32(csa, config->ext_inp, TRCEXTINSELR); for (i = 0; i < drvdata->nr_cntr; i++) { etm4x_relaxed_write32(csa, config->cntrldvr[i], TRCCNTRLDVRn(i)); @@ -720,7 +740,7 @@ out: static int etm4_enable_perf(struct coresight_device *csdev, struct perf_event *event) { - int ret = 0; + int ret = 0, trace_id; struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) { @@ -732,6 +752,24 @@ static int etm4_enable_perf(struct coresight_device *csdev, ret = etm4_parse_event_config(csdev, event); if (ret) goto out; + + /* + * perf allocates cpu ids as part of _setup_aux() - device needs to use + * the allocated ID. This reads the current version without allocation. + * + * This does not use the trace id lock to prevent lock_dep issues + * with perf locks - we know the ID cannot change until perf shuts down + * the session + */ + trace_id = coresight_trace_id_read_cpu_id(drvdata->cpu); + if (!IS_VALID_CS_TRACE_ID(trace_id)) { + dev_err(&drvdata->csdev->dev, "Failed to set trace ID for %s on CPU%d\n", + dev_name(&drvdata->csdev->dev), drvdata->cpu); + ret = -EINVAL; + goto out; + } + drvdata->trcid = (u8)trace_id; + /* And enable it */ ret = etm4_enable_hw(drvdata); @@ -756,6 +794,11 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) spin_lock(&drvdata->spinlock); + /* sysfs needs to read and allocate a trace ID */ + ret = etm4_read_alloc_trace_id(drvdata); + if (ret < 0) + goto unlock_sysfs_enable; + /* * Executing etm4_enable_hw on the cpu whose ETM is being enabled * ensures that register writes occur when cpu is powered. @@ -767,6 +810,11 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) ret = arg.rc; if (!ret) drvdata->sticky_enable = true; + + if (ret) + etm4_release_trace_id(drvdata); + +unlock_sysfs_enable: spin_unlock(&drvdata->spinlock); if (!ret) @@ -898,6 +946,11 @@ static int etm4_disable_perf(struct coresight_device *csdev, /* TRCVICTLR::SSSTATUS, bit[9] */ filters->ssstatus = (control & BIT(9)); + /* + * perf will release trace ids when _free_aux() is + * called at the end of the session. + */ + return 0; } @@ -923,6 +976,13 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) spin_unlock(&drvdata->spinlock); cpus_read_unlock(); + /* + * we only release trace IDs when resetting sysfs. + * This permits sysfs users to read the trace ID after the trace + * session has completed. This maintains operational behaviour with + * prior trace id allocation method + */ + dev_dbg(&csdev->dev, "ETM tracing disabled\n"); } @@ -956,7 +1016,6 @@ static void etm4_disable(struct coresight_device *csdev, static const struct coresight_ops_source etm4_source_ops = { .cpu_id = etm4_cpu_id, - .trace_id = etm4_trace_id, .enable = etm4_enable, .disable = etm4_disable, }; @@ -1565,11 +1624,6 @@ static int etm4_dying_cpu(unsigned int cpu) return 0; } -static void etm4_init_trace_id(struct etmv4_drvdata *drvdata) -{ - drvdata->trcid = coresight_get_trace_id(drvdata->cpu); -} - static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) { int i, ret = 0; @@ -1634,8 +1688,10 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata) for (i = 0; i < drvdata->nrseqstate - 1; i++) state->trcseqevr[i] = etm4x_read32(csa, TRCSEQEVRn(i)); - state->trcseqrstevr = etm4x_read32(csa, TRCSEQRSTEVR); - state->trcseqstr = etm4x_read32(csa, TRCSEQSTR); + if (drvdata->nrseqstate) { + state->trcseqrstevr = etm4x_read32(csa, TRCSEQRSTEVR); + state->trcseqstr = etm4x_read32(csa, TRCSEQSTR); + } state->trcextinselr = etm4x_read32(csa, TRCEXTINSELR); for (i = 0; i < drvdata->nr_cntr; i++) { @@ -1763,8 +1819,10 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata) for (i = 0; i < drvdata->nrseqstate - 1; i++) etm4x_relaxed_write32(csa, state->trcseqevr[i], TRCSEQEVRn(i)); - etm4x_relaxed_write32(csa, state->trcseqrstevr, TRCSEQRSTEVR); - etm4x_relaxed_write32(csa, state->trcseqstr, TRCSEQSTR); + if (drvdata->nrseqstate) { + etm4x_relaxed_write32(csa, state->trcseqrstevr, TRCSEQRSTEVR); + etm4x_relaxed_write32(csa, state->trcseqstr, TRCSEQSTR); + } etm4x_relaxed_write32(csa, state->trcextinselr, TRCEXTINSELR); for (i = 0; i < drvdata->nr_cntr; i++) { @@ -1946,7 +2004,6 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg) if (!desc.name) return -ENOMEM; - etm4_init_trace_id(drvdata); etm4_set_default(&drvdata->config); pdata = coresight_get_platform_data(dev); diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index 9cac848cffaf..5e62aa40ecd0 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -266,10 +266,11 @@ static ssize_t reset_store(struct device *dev, config->vmid_mask0 = 0x0; config->vmid_mask1 = 0x0; - drvdata->trcid = drvdata->cpu + 1; - spin_unlock(&drvdata->spinlock); + /* for sysfs - only release trace id when resetting */ + etm4_release_trace_id(drvdata); + cscfg_csdev_reset_feats(to_coresight_device(dev)); return size; @@ -2392,6 +2393,26 @@ static struct attribute *coresight_etmv4_attrs[] = { NULL, }; +/* + * Trace ID allocated dynamically on enable - but also allocate on read + * in case sysfs or perf read before enable to ensure consistent metadata + * information for trace decode + */ +static ssize_t trctraceid_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int trace_id; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + trace_id = etm4_read_alloc_trace_id(drvdata); + if (trace_id < 0) + return trace_id; + + return sysfs_emit(buf, "0x%x\n", trace_id); +} +static DEVICE_ATTR_RO(trctraceid); + struct etmv4_reg { struct coresight_device *csdev; u32 offset; @@ -2528,7 +2549,7 @@ static struct attribute *coresight_etmv4_mgmt_attrs[] = { coresight_etm4x_reg(trcpidr3, TRCPIDR3), coresight_etm4x_reg(trcoslsr, TRCOSLSR), coresight_etm4x_reg(trcconfig, TRCCONFIGR), - coresight_etm4x_reg(trctraceid, TRCTRACEIDR), + &dev_attr_trctraceid.attr, coresight_etm4x_reg(trcdevarch, TRCDEVARCH), NULL, }; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 4b21bb79f168..434f4e95ee17 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -1095,4 +1095,7 @@ static inline bool etm4x_is_ete(struct etmv4_drvdata *drvdata) { return drvdata->arch >= ETM_ARCH_ETE; } + +int etm4_read_alloc_trace_id(struct etmv4_drvdata *drvdata); +void etm4_release_trace_id(struct etmv4_drvdata *drvdata); #endif diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 463f449cfb79..66a614c5492c 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -31,6 +31,7 @@ #include <linux/stm.h> #include "coresight-priv.h" +#include "coresight-trace-id.h" #define STMDMASTARTR 0xc04 #define STMDMASTOPR 0xc08 @@ -280,15 +281,7 @@ static void stm_disable(struct coresight_device *csdev, } } -static int stm_trace_id(struct coresight_device *csdev) -{ - struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - return drvdata->traceid; -} - static const struct coresight_ops_source stm_source_ops = { - .trace_id = stm_trace_id, .enable = stm_enable, .disable = stm_disable, }; @@ -615,24 +608,7 @@ static ssize_t traceid_show(struct device *dev, val = drvdata->traceid; return sprintf(buf, "%#lx\n", val); } - -static ssize_t traceid_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - /* traceid field is 7bit wide on STM32 */ - drvdata->traceid = val & 0x7f; - return size; -} -static DEVICE_ATTR_RW(traceid); +static DEVICE_ATTR_RO(traceid); static struct attribute *coresight_stm_attrs[] = { &dev_attr_hwevent_enable.attr, @@ -803,14 +779,6 @@ static void stm_init_default_data(struct stm_drvdata *drvdata) */ drvdata->stmsper = ~0x0; - /* - * The trace ID value for *ETM* tracers start at CPU_ID * 2 + 0x10 and - * anything equal to or higher than 0x70 is reserved. Since 0x00 is - * also reserved the STM trace ID needs to be higher than 0x00 and - * lowner than 0x10. - */ - drvdata->traceid = 0x1; - /* Set invariant transaction timing on all channels */ bitmap_clear(drvdata->chs.guaranteed, 0, drvdata->numsp); } @@ -838,7 +806,7 @@ static void stm_init_generic_data(struct stm_drvdata *drvdata, static int stm_probe(struct amba_device *adev, const struct amba_id *id) { - int ret; + int ret, trace_id; void __iomem *base; struct device *dev = &adev->dev; struct coresight_platform_data *pdata = NULL; @@ -922,12 +890,22 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) goto stm_unregister; } + trace_id = coresight_trace_id_get_system_id(); + if (trace_id < 0) { + ret = trace_id; + goto cs_unregister; + } + drvdata->traceid = (u8)trace_id; + pm_runtime_put(&adev->dev); dev_info(&drvdata->csdev->dev, "%s initialized\n", (char *)coresight_get_uci_data(id)); return 0; +cs_unregister: + coresight_unregister(drvdata->csdev); + stm_unregister: stm_unregister_device(&drvdata->stm); return ret; @@ -937,6 +915,7 @@ static void stm_remove(struct amba_device *adev) { struct stm_drvdata *drvdata = dev_get_drvdata(&adev->dev); + coresight_trace_id_put_system_id(drvdata->traceid); coresight_unregister(drvdata->csdev); stm_unregister_device(&drvdata->stm); diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index 07abf28ad725..c106d142e632 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -31,7 +31,7 @@ DEFINE_CORESIGHT_DEVLIST(etb_devs, "tmc_etb"); DEFINE_CORESIGHT_DEVLIST(etf_devs, "tmc_etf"); DEFINE_CORESIGHT_DEVLIST(etr_devs, "tmc_etr"); -void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) +int tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) { struct coresight_device *csdev = drvdata->csdev; struct csdev_access *csa = &csdev->access; @@ -40,7 +40,9 @@ void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) if (coresight_timeout(csa, TMC_STS, TMC_STS_TMCREADY_BIT, 1)) { dev_err(&csdev->dev, "timeout while waiting for TMC to be Ready\n"); + return -EBUSY; } + return 0; } void tmc_flush_and_stop(struct tmc_drvdata *drvdata) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 4c4cbd1f7258..0ab1f73c2d06 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -16,12 +16,20 @@ static int tmc_set_etf_buffer(struct coresight_device *csdev, struct perf_output_handle *handle); -static void __tmc_etb_enable_hw(struct tmc_drvdata *drvdata) +static int __tmc_etb_enable_hw(struct tmc_drvdata *drvdata) { + int rc = 0; + CS_UNLOCK(drvdata->base); /* Wait for TMCSReady bit to be set */ - tmc_wait_for_tmcready(drvdata); + rc = tmc_wait_for_tmcready(drvdata); + if (rc) { + dev_err(&drvdata->csdev->dev, + "Failed to enable: TMC not ready\n"); + CS_LOCK(drvdata->base); + return rc; + } writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | @@ -33,6 +41,7 @@ static void __tmc_etb_enable_hw(struct tmc_drvdata *drvdata) tmc_enable_hw(drvdata); CS_LOCK(drvdata->base); + return rc; } static int tmc_etb_enable_hw(struct tmc_drvdata *drvdata) @@ -42,8 +51,10 @@ static int tmc_etb_enable_hw(struct tmc_drvdata *drvdata) if (rc) return rc; - __tmc_etb_enable_hw(drvdata); - return 0; + rc = __tmc_etb_enable_hw(drvdata); + if (rc) + coresight_disclaim_device(drvdata->csdev); + return rc; } static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) @@ -91,12 +102,20 @@ static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) coresight_disclaim_device(drvdata->csdev); } -static void __tmc_etf_enable_hw(struct tmc_drvdata *drvdata) +static int __tmc_etf_enable_hw(struct tmc_drvdata *drvdata) { + int rc = 0; + CS_UNLOCK(drvdata->base); /* Wait for TMCSReady bit to be set */ - tmc_wait_for_tmcready(drvdata); + rc = tmc_wait_for_tmcready(drvdata); + if (rc) { + dev_err(&drvdata->csdev->dev, + "Failed to enable : TMC is not ready\n"); + CS_LOCK(drvdata->base); + return rc; + } writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE); writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI, @@ -105,6 +124,7 @@ static void __tmc_etf_enable_hw(struct tmc_drvdata *drvdata) tmc_enable_hw(drvdata); CS_LOCK(drvdata->base); + return rc; } static int tmc_etf_enable_hw(struct tmc_drvdata *drvdata) @@ -114,8 +134,10 @@ static int tmc_etf_enable_hw(struct tmc_drvdata *drvdata) if (rc) return rc; - __tmc_etf_enable_hw(drvdata); - return 0; + rc = __tmc_etf_enable_hw(drvdata); + if (rc) + coresight_disclaim_device(drvdata->csdev); + return rc; } static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata) @@ -639,6 +661,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) char *buf = NULL; enum tmc_mode mode; unsigned long flags; + int rc = 0; /* config types are set a boot time and never change */ if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETB && @@ -664,7 +687,11 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) * can't be NULL. */ memset(drvdata->buf, 0, drvdata->size); - __tmc_etb_enable_hw(drvdata); + rc = __tmc_etb_enable_hw(drvdata); + if (rc) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return rc; + } } else { /* * The ETB/ETF is not tracing and the buffer was just read. diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 867ad8bb9b0c..918d461fcf4a 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -983,15 +983,22 @@ static void tmc_sync_etr_buf(struct tmc_drvdata *drvdata) etr_buf->ops->sync(etr_buf, rrp, rwp); } -static void __tmc_etr_enable_hw(struct tmc_drvdata *drvdata) +static int __tmc_etr_enable_hw(struct tmc_drvdata *drvdata) { u32 axictl, sts; struct etr_buf *etr_buf = drvdata->etr_buf; + int rc = 0; CS_UNLOCK(drvdata->base); /* Wait for TMCSReady bit to be set */ - tmc_wait_for_tmcready(drvdata); + rc = tmc_wait_for_tmcready(drvdata); + if (rc) { + dev_err(&drvdata->csdev->dev, + "Failed to enable : TMC not ready\n"); + CS_LOCK(drvdata->base); + return rc; + } writel_relaxed(etr_buf->size / 4, drvdata->base + TMC_RSZ); writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); @@ -1032,6 +1039,7 @@ static void __tmc_etr_enable_hw(struct tmc_drvdata *drvdata) tmc_enable_hw(drvdata); CS_LOCK(drvdata->base); + return rc; } static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata, @@ -1060,7 +1068,12 @@ static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata, rc = coresight_claim_device(drvdata->csdev); if (!rc) { drvdata->etr_buf = etr_buf; - __tmc_etr_enable_hw(drvdata); + rc = __tmc_etr_enable_hw(drvdata); + if (rc) { + drvdata->etr_buf = NULL; + coresight_disclaim_device(drvdata->csdev); + tmc_etr_disable_catu(drvdata); + } } return rc; diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 66959557cf39..01c0382a29c0 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -255,7 +255,7 @@ struct tmc_sg_table { }; /* Generic functions */ -void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata); +int tmc_wait_for_tmcready(struct tmc_drvdata *drvdata); void tmc_flush_and_stop(struct tmc_drvdata *drvdata); void tmc_enable_hw(struct tmc_drvdata *drvdata); void tmc_disable_hw(struct tmc_drvdata *drvdata); diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c new file mode 100644 index 000000000000..f712e112ecff --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-tpda.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/amba/bus.h> +#include <linux/bitfield.h> +#include <linux/coresight.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include "coresight-priv.h" +#include "coresight-tpda.h" +#include "coresight-trace-id.h" + +DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda"); + +/* Settings pre enabling port control register */ +static void tpda_enable_pre_port(struct tpda_drvdata *drvdata) +{ + u32 val; + + val = readl_relaxed(drvdata->base + TPDA_CR); + val &= ~TPDA_CR_ATID; + val |= FIELD_PREP(TPDA_CR_ATID, drvdata->atid); + writel_relaxed(val, drvdata->base + TPDA_CR); +} + +static void tpda_enable_port(struct tpda_drvdata *drvdata, int port) +{ + u32 val; + + val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port)); + /* Enable the port */ + val |= TPDA_Pn_CR_ENA; + writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port)); +} + +static void __tpda_enable(struct tpda_drvdata *drvdata, int port) +{ + CS_UNLOCK(drvdata->base); + + if (!drvdata->csdev->enable) + tpda_enable_pre_port(drvdata); + + tpda_enable_port(drvdata, port); + + CS_LOCK(drvdata->base); +} + +static int tpda_enable(struct coresight_device *csdev, int inport, int outport) +{ + struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock(&drvdata->spinlock); + if (atomic_read(&csdev->refcnt[inport]) == 0) + __tpda_enable(drvdata, inport); + + atomic_inc(&csdev->refcnt[inport]); + spin_unlock(&drvdata->spinlock); + + dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", inport); + return 0; +} + +static void __tpda_disable(struct tpda_drvdata *drvdata, int port) +{ + u32 val; + + CS_UNLOCK(drvdata->base); + + val = readl_relaxed(drvdata->base + TPDA_Pn_CR(port)); + val &= ~TPDA_Pn_CR_ENA; + writel_relaxed(val, drvdata->base + TPDA_Pn_CR(port)); + + CS_LOCK(drvdata->base); +} + +static void tpda_disable(struct coresight_device *csdev, int inport, + int outport) +{ + struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock(&drvdata->spinlock); + if (atomic_dec_return(&csdev->refcnt[inport]) == 0) + __tpda_disable(drvdata, inport); + + spin_unlock(&drvdata->spinlock); + + dev_dbg(drvdata->dev, "TPDA inport %d disabled\n", inport); +} + +static const struct coresight_ops_link tpda_link_ops = { + .enable = tpda_enable, + .disable = tpda_disable, +}; + +static const struct coresight_ops tpda_cs_ops = { + .link_ops = &tpda_link_ops, +}; + +static int tpda_init_default_data(struct tpda_drvdata *drvdata) +{ + int atid; + /* + * TPDA must has a unique atid. This atid can uniquely + * identify the TPDM trace source connected to the TPDA. + * The TPDMs which are connected to same TPDA share the + * same trace-id. When TPDA does packetization, different + * port will have unique channel number for decoding. + */ + atid = coresight_trace_id_get_system_id(); + if (atid < 0) + return atid; + + drvdata->atid = atid; + return 0; +} + +static int tpda_probe(struct amba_device *adev, const struct amba_id *id) +{ + int ret; + struct device *dev = &adev->dev; + struct coresight_platform_data *pdata; + struct tpda_drvdata *drvdata; + struct coresight_desc desc = { 0 }; + void __iomem *base; + + pdata = coresight_get_platform_data(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + adev->dev.platform_data = pdata; + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->dev = &adev->dev; + dev_set_drvdata(dev, drvdata); + + base = devm_ioremap_resource(dev, &adev->res); + if (IS_ERR(base)) + return PTR_ERR(base); + drvdata->base = base; + + spin_lock_init(&drvdata->spinlock); + + ret = tpda_init_default_data(drvdata); + if (ret) + return ret; + + desc.name = coresight_alloc_device_name(&tpda_devs, dev); + if (!desc.name) + return -ENOMEM; + desc.type = CORESIGHT_DEV_TYPE_LINK; + desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_MERG; + desc.ops = &tpda_cs_ops; + desc.pdata = adev->dev.platform_data; + desc.dev = &adev->dev; + desc.access = CSDEV_ACCESS_IOMEM(base); + drvdata->csdev = coresight_register(&desc); + if (IS_ERR(drvdata->csdev)) + return PTR_ERR(drvdata->csdev); + + pm_runtime_put(&adev->dev); + + dev_dbg(drvdata->dev, "TPDA initialized\n"); + return 0; +} + +static void tpda_remove(struct amba_device *adev) +{ + struct tpda_drvdata *drvdata = dev_get_drvdata(&adev->dev); + + coresight_trace_id_put_system_id(drvdata->atid); + coresight_unregister(drvdata->csdev); +} + +/* + * Different TPDA has different periph id. + * The difference is 0-7 bits' value. So ignore 0-7 bits. + */ +static struct amba_id tpda_ids[] = { + { + .id = 0x000f0f00, + .mask = 0x000fff00, + }, + { 0, 0}, +}; + +static struct amba_driver tpda_driver = { + .drv = { + .name = "coresight-tpda", + .owner = THIS_MODULE, + .suppress_bind_attrs = true, + }, + .probe = tpda_probe, + .remove = tpda_remove, + .id_table = tpda_ids, +}; + +module_amba_driver(tpda_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Aggregator driver"); diff --git a/drivers/hwtracing/coresight/coresight-tpda.h b/drivers/hwtracing/coresight/coresight-tpda.h new file mode 100644 index 000000000000..0399678df312 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-tpda.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _CORESIGHT_CORESIGHT_TPDA_H +#define _CORESIGHT_CORESIGHT_TPDA_H + +#define TPDA_CR (0x000) +#define TPDA_Pn_CR(n) (0x004 + (n * 4)) +/* Aggregator port enable bit */ +#define TPDA_Pn_CR_ENA BIT(0) + +#define TPDA_MAX_INPORTS 32 + +/* Bits 6 ~ 12 is for atid value */ +#define TPDA_CR_ATID GENMASK(12, 6) + +/** + * struct tpda_drvdata - specifics associated to an TPDA component + * @base: memory mapped base address for this component. + * @dev: The device entity associated to this component. + * @csdev: component vitals needed by the framework. + * @spinlock: lock for the drvdata value. + * @enable: enable status of the component. + */ +struct tpda_drvdata { + void __iomem *base; + struct device *dev; + struct coresight_device *csdev; + spinlock_t spinlock; + u8 atid; +}; + +#endif /* _CORESIGHT_CORESIGHT_TPDA_H */ diff --git a/drivers/hwtracing/coresight/coresight-tpdm.c b/drivers/hwtracing/coresight/coresight-tpdm.c new file mode 100644 index 000000000000..9479a5e8c672 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-tpdm.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/amba/bus.h> +#include <linux/bitmap.h> +#include <linux/coresight.h> +#include <linux/coresight-pmu.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> + +#include "coresight-priv.h" +#include "coresight-tpdm.h" + +DEFINE_CORESIGHT_DEVLIST(tpdm_devs, "tpdm"); + +static void tpdm_enable_dsb(struct tpdm_drvdata *drvdata) +{ + u32 val; + + /* Set the enable bit of DSB control register to 1 */ + val = readl_relaxed(drvdata->base + TPDM_DSB_CR); + val |= TPDM_DSB_CR_ENA; + writel_relaxed(val, drvdata->base + TPDM_DSB_CR); +} + +/* TPDM enable operations */ +static void __tpdm_enable(struct tpdm_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + /* Check if DSB datasets is present for TPDM. */ + if (drvdata->datasets & TPDM_PIDR0_DS_DSB) + tpdm_enable_dsb(drvdata); + + CS_LOCK(drvdata->base); +} + +static int tpdm_enable(struct coresight_device *csdev, + struct perf_event *event, u32 mode) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock(&drvdata->spinlock); + if (drvdata->enable) { + spin_unlock(&drvdata->spinlock); + return -EBUSY; + } + + __tpdm_enable(drvdata); + drvdata->enable = true; + spin_unlock(&drvdata->spinlock); + + dev_dbg(drvdata->dev, "TPDM tracing enabled\n"); + return 0; +} + +static void tpdm_disable_dsb(struct tpdm_drvdata *drvdata) +{ + u32 val; + + /* Set the enable bit of DSB control register to 0 */ + val = readl_relaxed(drvdata->base + TPDM_DSB_CR); + val &= ~TPDM_DSB_CR_ENA; + writel_relaxed(val, drvdata->base + TPDM_DSB_CR); +} + +/* TPDM disable operations */ +static void __tpdm_disable(struct tpdm_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + /* Check if DSB datasets is present for TPDM. */ + if (drvdata->datasets & TPDM_PIDR0_DS_DSB) + tpdm_disable_dsb(drvdata); + + CS_LOCK(drvdata->base); +} + +static void tpdm_disable(struct coresight_device *csdev, + struct perf_event *event) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock(&drvdata->spinlock); + if (!drvdata->enable) { + spin_unlock(&drvdata->spinlock); + return; + } + + __tpdm_disable(drvdata); + drvdata->enable = false; + spin_unlock(&drvdata->spinlock); + + dev_dbg(drvdata->dev, "TPDM tracing disabled\n"); +} + +static const struct coresight_ops_source tpdm_source_ops = { + .enable = tpdm_enable, + .disable = tpdm_disable, +}; + +static const struct coresight_ops tpdm_cs_ops = { + .source_ops = &tpdm_source_ops, +}; + +static void tpdm_init_default_data(struct tpdm_drvdata *drvdata) +{ + u32 pidr; + + CS_UNLOCK(drvdata->base); + /* Get the datasets present on the TPDM. */ + pidr = readl_relaxed(drvdata->base + CORESIGHT_PERIPHIDR0); + drvdata->datasets |= pidr & GENMASK(TPDM_DATASETS - 1, 0); + CS_LOCK(drvdata->base); +} + +/* + * value 1: 64 bits test data + * value 2: 32 bits test data + */ +static ssize_t integration_test_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t size) +{ + int i, ret = 0; + unsigned long val; + struct tpdm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + if (val != 1 && val != 2) + return -EINVAL; + + if (!drvdata->enable) + return -EINVAL; + + if (val == 1) + val = ATBCNTRL_VAL_64; + else + val = ATBCNTRL_VAL_32; + CS_UNLOCK(drvdata->base); + writel_relaxed(0x1, drvdata->base + TPDM_ITCNTRL); + + for (i = 0; i < INTEGRATION_TEST_CYCLE; i++) + writel_relaxed(val, drvdata->base + TPDM_ITATBCNTRL); + + writel_relaxed(0, drvdata->base + TPDM_ITCNTRL); + CS_LOCK(drvdata->base); + return size; +} +static DEVICE_ATTR_WO(integration_test); + +static struct attribute *tpdm_attrs[] = { + &dev_attr_integration_test.attr, + NULL, +}; + +static struct attribute_group tpdm_attr_grp = { + .attrs = tpdm_attrs, +}; + +static const struct attribute_group *tpdm_attr_grps[] = { + &tpdm_attr_grp, + NULL, +}; + +static int tpdm_probe(struct amba_device *adev, const struct amba_id *id) +{ + void __iomem *base; + struct device *dev = &adev->dev; + struct coresight_platform_data *pdata; + struct tpdm_drvdata *drvdata; + struct coresight_desc desc = { 0 }; + + pdata = coresight_get_platform_data(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + adev->dev.platform_data = pdata; + + /* driver data*/ + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + drvdata->dev = &adev->dev; + dev_set_drvdata(dev, drvdata); + + base = devm_ioremap_resource(dev, &adev->res); + if (IS_ERR(base)) + return PTR_ERR(base); + + drvdata->base = base; + + /* Set up coresight component description */ + desc.name = coresight_alloc_device_name(&tpdm_devs, dev); + if (!desc.name) + return -ENOMEM; + desc.type = CORESIGHT_DEV_TYPE_SOURCE; + desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS; + desc.ops = &tpdm_cs_ops; + desc.pdata = adev->dev.platform_data; + desc.dev = &adev->dev; + desc.access = CSDEV_ACCESS_IOMEM(base); + desc.groups = tpdm_attr_grps; + drvdata->csdev = coresight_register(&desc); + if (IS_ERR(drvdata->csdev)) + return PTR_ERR(drvdata->csdev); + + spin_lock_init(&drvdata->spinlock); + tpdm_init_default_data(drvdata); + /* Decrease pm refcount when probe is done.*/ + pm_runtime_put(&adev->dev); + + return 0; +} + +static void tpdm_remove(struct amba_device *adev) +{ + struct tpdm_drvdata *drvdata = dev_get_drvdata(&adev->dev); + + coresight_unregister(drvdata->csdev); +} + +/* + * Different TPDM has different periph id. + * The difference is 0-7 bits' value. So ignore 0-7 bits. + */ +static struct amba_id tpdm_ids[] = { + { + .id = 0x000f0e00, + .mask = 0x000fff00, + }, + { 0, 0}, +}; + +static struct amba_driver tpdm_driver = { + .drv = { + .name = "coresight-tpdm", + .owner = THIS_MODULE, + .suppress_bind_attrs = true, + }, + .probe = tpdm_probe, + .id_table = tpdm_ids, + .remove = tpdm_remove, +}; + +module_amba_driver(tpdm_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Trace, Profiling & Diagnostic Monitor driver"); diff --git a/drivers/hwtracing/coresight/coresight-tpdm.h b/drivers/hwtracing/coresight/coresight-tpdm.h new file mode 100644 index 000000000000..543854043a2d --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-tpdm.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _CORESIGHT_CORESIGHT_TPDM_H +#define _CORESIGHT_CORESIGHT_TPDM_H + +/* The max number of the datasets that TPDM supports */ +#define TPDM_DATASETS 7 + +/* DSB Subunit Registers */ +#define TPDM_DSB_CR (0x780) +/* Enable bit for DSB subunit */ +#define TPDM_DSB_CR_ENA BIT(0) + +/* TPDM integration test registers */ +#define TPDM_ITATBCNTRL (0xEF0) +#define TPDM_ITCNTRL (0xF00) + +/* Register value for integration test */ +#define ATBCNTRL_VAL_32 0xC00F1409 +#define ATBCNTRL_VAL_64 0xC01F1409 + +/* + * Number of cycles to write value when + * integration test. + */ +#define INTEGRATION_TEST_CYCLE 10 + +/** + * The bits of PERIPHIDR0 register. + * The fields [6:0] of PERIPHIDR0 are used to determine what + * interfaces and subunits are present on a given TPDM. + * + * PERIPHIDR0[0] : Fix to 1 if ImplDef subunit present, else 0 + * PERIPHIDR0[1] : Fix to 1 if DSB subunit present, else 0 + */ + +#define TPDM_PIDR0_DS_IMPDEF BIT(0) +#define TPDM_PIDR0_DS_DSB BIT(1) + +/** + * struct tpdm_drvdata - specifics associated to an TPDM component + * @base: memory mapped base address for this component. + * @dev: The device entity associated to this component. + * @csdev: component vitals needed by the framework. + * @spinlock: lock for the drvdata value. + * @enable: enable status of the component. + * @datasets: The datasets types present of the TPDM. + */ + +struct tpdm_drvdata { + void __iomem *base; + struct device *dev; + struct coresight_device *csdev; + spinlock_t spinlock; + bool enable; + unsigned long datasets; +}; + +#endif /* _CORESIGHT_CORESIGHT_TPDM_H */ diff --git a/drivers/hwtracing/coresight/coresight-trace-id.c b/drivers/hwtracing/coresight/coresight-trace-id.c new file mode 100644 index 000000000000..af5b4ef59cea --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-trace-id.c @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022, Linaro Limited, All rights reserved. + * Author: Mike Leach <mike.leach@linaro.org> + */ +#include <linux/coresight-pmu.h> +#include <linux/cpumask.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include "coresight-trace-id.h" + +/* Default trace ID map. Used on systems that don't require per sink mappings */ +static struct coresight_trace_id_map id_map_default; + +/* maintain a record of the mapping of IDs and pending releases per cpu */ +static DEFINE_PER_CPU(atomic_t, cpu_id) = ATOMIC_INIT(0); +static cpumask_t cpu_id_release_pending; + +/* perf session active counter */ +static atomic_t perf_cs_etm_session_active = ATOMIC_INIT(0); + +/* lock to protect id_map and cpu data */ +static DEFINE_SPINLOCK(id_map_lock); + +/* #define TRACE_ID_DEBUG 1 */ +#if defined(TRACE_ID_DEBUG) || defined(CONFIG_COMPILE_TEST) + +static void coresight_trace_id_dump_table(struct coresight_trace_id_map *id_map, + const char *func_name) +{ + pr_debug("%s id_map::\n", func_name); + pr_debug("Used = %*pb\n", CORESIGHT_TRACE_IDS_MAX, id_map->used_ids); + pr_debug("Pend = %*pb\n", CORESIGHT_TRACE_IDS_MAX, id_map->pend_rel_ids); +} +#define DUMP_ID_MAP(map) coresight_trace_id_dump_table(map, __func__) +#define DUMP_ID_CPU(cpu, id) pr_debug("%s called; cpu=%d, id=%d\n", __func__, cpu, id) +#define DUMP_ID(id) pr_debug("%s called; id=%d\n", __func__, id) +#define PERF_SESSION(n) pr_debug("%s perf count %d\n", __func__, n) +#else +#define DUMP_ID_MAP(map) +#define DUMP_ID(id) +#define DUMP_ID_CPU(cpu, id) +#define PERF_SESSION(n) +#endif + +/* unlocked read of current trace ID value for given CPU */ +static int _coresight_trace_id_read_cpu_id(int cpu) +{ + return atomic_read(&per_cpu(cpu_id, cpu)); +} + +/* look for next available odd ID, return 0 if none found */ +static int coresight_trace_id_find_odd_id(struct coresight_trace_id_map *id_map) +{ + int found_id = 0, bit = 1, next_id; + + while ((bit < CORESIGHT_TRACE_ID_RES_TOP) && !found_id) { + /* + * bitmap length of CORESIGHT_TRACE_ID_RES_TOP, + * search from offset `bit`. + */ + next_id = find_next_zero_bit(id_map->used_ids, + CORESIGHT_TRACE_ID_RES_TOP, bit); + if ((next_id < CORESIGHT_TRACE_ID_RES_TOP) && (next_id & 0x1)) + found_id = next_id; + else + bit = next_id + 1; + } + return found_id; +} + +/* + * Allocate new ID and set in use + * + * if @preferred_id is a valid id then try to use that value if available. + * if @preferred_id is not valid and @prefer_odd_id is true, try for odd id. + * + * Otherwise allocate next available ID. + */ +static int coresight_trace_id_alloc_new_id(struct coresight_trace_id_map *id_map, + int preferred_id, bool prefer_odd_id) +{ + int id = 0; + + /* for backwards compatibility, cpu IDs may use preferred value */ + if (IS_VALID_CS_TRACE_ID(preferred_id) && + !test_bit(preferred_id, id_map->used_ids)) { + id = preferred_id; + goto trace_id_allocated; + } else if (prefer_odd_id) { + /* may use odd ids to avoid preferred legacy cpu IDs */ + id = coresight_trace_id_find_odd_id(id_map); + if (id) + goto trace_id_allocated; + } + + /* + * skip reserved bit 0, look at bitmap length of + * CORESIGHT_TRACE_ID_RES_TOP from offset of bit 1. + */ + id = find_next_zero_bit(id_map->used_ids, CORESIGHT_TRACE_ID_RES_TOP, 1); + if (id >= CORESIGHT_TRACE_ID_RES_TOP) + return -EINVAL; + + /* mark as used */ +trace_id_allocated: + set_bit(id, id_map->used_ids); + return id; +} + +static void coresight_trace_id_free(int id, struct coresight_trace_id_map *id_map) +{ + if (WARN(!IS_VALID_CS_TRACE_ID(id), "Invalid Trace ID %d\n", id)) + return; + if (WARN(!test_bit(id, id_map->used_ids), "Freeing unused ID %d\n", id)) + return; + clear_bit(id, id_map->used_ids); +} + +static void coresight_trace_id_set_pend_rel(int id, struct coresight_trace_id_map *id_map) +{ + if (WARN(!IS_VALID_CS_TRACE_ID(id), "Invalid Trace ID %d\n", id)) + return; + set_bit(id, id_map->pend_rel_ids); +} + +/* + * release all pending IDs for all current maps & clear CPU associations + * + * This currently operates on the default id map, but may be extended to + * operate on all registered id maps if per sink id maps are used. + */ +static void coresight_trace_id_release_all_pending(void) +{ + struct coresight_trace_id_map *id_map = &id_map_default; + unsigned long flags; + int cpu, bit; + + spin_lock_irqsave(&id_map_lock, flags); + for_each_set_bit(bit, id_map->pend_rel_ids, CORESIGHT_TRACE_ID_RES_TOP) { + clear_bit(bit, id_map->used_ids); + clear_bit(bit, id_map->pend_rel_ids); + } + for_each_cpu(cpu, &cpu_id_release_pending) { + atomic_set(&per_cpu(cpu_id, cpu), 0); + cpumask_clear_cpu(cpu, &cpu_id_release_pending); + } + spin_unlock_irqrestore(&id_map_lock, flags); + DUMP_ID_MAP(id_map); +} + +static int coresight_trace_id_map_get_cpu_id(int cpu, struct coresight_trace_id_map *id_map) +{ + unsigned long flags; + int id; + + spin_lock_irqsave(&id_map_lock, flags); + + /* check for existing allocation for this CPU */ + id = _coresight_trace_id_read_cpu_id(cpu); + if (id) + goto get_cpu_id_clr_pend; + + /* + * Find a new ID. + * + * Use legacy values where possible in the dynamic trace ID allocator to + * allow older tools to continue working if they are not upgraded at the + * same time as the kernel drivers. + * + * If the generated legacy ID is invalid, or not available then the next + * available dynamic ID will be used. + */ + id = coresight_trace_id_alloc_new_id(id_map, + CORESIGHT_LEGACY_CPU_TRACE_ID(cpu), + false); + if (!IS_VALID_CS_TRACE_ID(id)) + goto get_cpu_id_out_unlock; + + /* allocate the new id to the cpu */ + atomic_set(&per_cpu(cpu_id, cpu), id); + +get_cpu_id_clr_pend: + /* we are (re)using this ID - so ensure it is not marked for release */ + cpumask_clear_cpu(cpu, &cpu_id_release_pending); + clear_bit(id, id_map->pend_rel_ids); + +get_cpu_id_out_unlock: + spin_unlock_irqrestore(&id_map_lock, flags); + + DUMP_ID_CPU(cpu, id); + DUMP_ID_MAP(id_map); + return id; +} + +static void coresight_trace_id_map_put_cpu_id(int cpu, struct coresight_trace_id_map *id_map) +{ + unsigned long flags; + int id; + + /* check for existing allocation for this CPU */ + id = _coresight_trace_id_read_cpu_id(cpu); + if (!id) + return; + + spin_lock_irqsave(&id_map_lock, flags); + + if (atomic_read(&perf_cs_etm_session_active)) { + /* set release at pending if perf still active */ + coresight_trace_id_set_pend_rel(id, id_map); + cpumask_set_cpu(cpu, &cpu_id_release_pending); + } else { + /* otherwise clear id */ + coresight_trace_id_free(id, id_map); + atomic_set(&per_cpu(cpu_id, cpu), 0); + } + + spin_unlock_irqrestore(&id_map_lock, flags); + DUMP_ID_CPU(cpu, id); + DUMP_ID_MAP(id_map); +} + +static int coresight_trace_id_map_get_system_id(struct coresight_trace_id_map *id_map) +{ + unsigned long flags; + int id; + + spin_lock_irqsave(&id_map_lock, flags); + /* prefer odd IDs for system components to avoid legacy CPU IDS */ + id = coresight_trace_id_alloc_new_id(id_map, 0, true); + spin_unlock_irqrestore(&id_map_lock, flags); + + DUMP_ID(id); + DUMP_ID_MAP(id_map); + return id; +} + +static void coresight_trace_id_map_put_system_id(struct coresight_trace_id_map *id_map, int id) +{ + unsigned long flags; + + spin_lock_irqsave(&id_map_lock, flags); + coresight_trace_id_free(id, id_map); + spin_unlock_irqrestore(&id_map_lock, flags); + + DUMP_ID(id); + DUMP_ID_MAP(id_map); +} + +/* API functions */ + +int coresight_trace_id_get_cpu_id(int cpu) +{ + return coresight_trace_id_map_get_cpu_id(cpu, &id_map_default); +} +EXPORT_SYMBOL_GPL(coresight_trace_id_get_cpu_id); + +void coresight_trace_id_put_cpu_id(int cpu) +{ + coresight_trace_id_map_put_cpu_id(cpu, &id_map_default); +} +EXPORT_SYMBOL_GPL(coresight_trace_id_put_cpu_id); + +int coresight_trace_id_read_cpu_id(int cpu) +{ + return _coresight_trace_id_read_cpu_id(cpu); +} +EXPORT_SYMBOL_GPL(coresight_trace_id_read_cpu_id); + +int coresight_trace_id_get_system_id(void) +{ + return coresight_trace_id_map_get_system_id(&id_map_default); +} +EXPORT_SYMBOL_GPL(coresight_trace_id_get_system_id); + +void coresight_trace_id_put_system_id(int id) +{ + coresight_trace_id_map_put_system_id(&id_map_default, id); +} +EXPORT_SYMBOL_GPL(coresight_trace_id_put_system_id); + +void coresight_trace_id_perf_start(void) +{ + atomic_inc(&perf_cs_etm_session_active); + PERF_SESSION(atomic_read(&perf_cs_etm_session_active)); +} +EXPORT_SYMBOL_GPL(coresight_trace_id_perf_start); + +void coresight_trace_id_perf_stop(void) +{ + if (!atomic_dec_return(&perf_cs_etm_session_active)) + coresight_trace_id_release_all_pending(); + PERF_SESSION(atomic_read(&perf_cs_etm_session_active)); +} +EXPORT_SYMBOL_GPL(coresight_trace_id_perf_stop); diff --git a/drivers/hwtracing/coresight/coresight-trace-id.h b/drivers/hwtracing/coresight/coresight-trace-id.h new file mode 100644 index 000000000000..3797777d367e --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-trace-id.h @@ -0,0 +1,156 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(C) 2022 Linaro Limited. All rights reserved. + * Author: Mike Leach <mike.leach@linaro.org> + */ + +#ifndef _CORESIGHT_TRACE_ID_H +#define _CORESIGHT_TRACE_ID_H + +/* + * Coresight trace ID allocation API + * + * With multi cpu systems, and more additional trace sources a scalable + * trace ID reservation system is required. + * + * The system will allocate Ids on a demand basis, and allow them to be + * released when done. + * + * In order to ensure that a consistent cpu / ID matching is maintained + * throughout a perf cs_etm event session - a session in progress flag will + * be maintained, and released IDs not cleared until the perf session is + * complete. This allows the same CPU to be re-allocated its prior ID. + * + * + * Trace ID maps will be created and initialised to prevent architecturally + * reserved IDs from being allocated. + * + * API permits multiple maps to be maintained - for large systems where + * different sets of cpus trace into different independent sinks. + */ + +#include <linux/bitops.h> +#include <linux/types.h> + + +/* architecturally we have 128 IDs some of which are reserved */ +#define CORESIGHT_TRACE_IDS_MAX 128 + +/* ID 0 is reserved */ +#define CORESIGHT_TRACE_ID_RES_0 0 + +/* ID 0x70 onwards are reserved */ +#define CORESIGHT_TRACE_ID_RES_TOP 0x70 + +/* check an ID is in the valid range */ +#define IS_VALID_CS_TRACE_ID(id) \ + ((id > CORESIGHT_TRACE_ID_RES_0) && (id < CORESIGHT_TRACE_ID_RES_TOP)) + +/** + * Trace ID map. + * + * @used_ids: Bitmap to register available (bit = 0) and in use (bit = 1) IDs. + * Initialised so that the reserved IDs are permanently marked as + * in use. + * @pend_rel_ids: CPU IDs that have been released by the trace source but not + * yet marked as available, to allow re-allocation to the same + * CPU during a perf session. + */ +struct coresight_trace_id_map { + DECLARE_BITMAP(used_ids, CORESIGHT_TRACE_IDS_MAX); + DECLARE_BITMAP(pend_rel_ids, CORESIGHT_TRACE_IDS_MAX); +}; + +/* Allocate and release IDs for a single default trace ID map */ + +/** + * Read and optionally allocate a CoreSight trace ID and associate with a CPU. + * + * Function will read the current trace ID for the associated CPU, + * allocating an new ID if one is not currently allocated. + * + * Numeric ID values allocated use legacy allocation algorithm if possible, + * otherwise any available ID is used. + * + * @cpu: The CPU index to allocate for. + * + * return: CoreSight trace ID or -EINVAL if allocation impossible. + */ +int coresight_trace_id_get_cpu_id(int cpu); + +/** + * Release an allocated trace ID associated with the CPU. + * + * This will release the CoreSight trace ID associated with the CPU, + * unless a perf session is in operation. + * + * If a perf session is in operation then the ID will be marked as pending + * release. + * + * @cpu: The CPU index to release the associated trace ID. + */ +void coresight_trace_id_put_cpu_id(int cpu); + +/** + * Read the current allocated CoreSight Trace ID value for the CPU. + * + * Fast read of the current value that does not allocate if no ID allocated + * for the CPU. + * + * Used in perf context where it is known that the value for the CPU will not + * be changing, when perf starts and event on a core and outputs the Trace ID + * for the CPU as a packet in the data file. IDs cannot change during a perf + * session. + * + * This function does not take the lock protecting the ID lists, avoiding + * locking dependency issues with perf locks. + * + * @cpu: The CPU index to read. + * + * return: current value, will be 0 if unallocated. + */ +int coresight_trace_id_read_cpu_id(int cpu); + +/** + * Allocate a CoreSight trace ID for a system component. + * + * Unconditionally allocates a Trace ID, without associating the ID with a CPU. + * + * Used to allocate IDs for system trace sources such as STM. + * + * return: Trace ID or -EINVAL if allocation is impossible. + */ +int coresight_trace_id_get_system_id(void); + +/** + * Release an allocated system trace ID. + * + * Unconditionally release a trace ID allocated to a system component. + * + * @id: value of trace ID allocated. + */ +void coresight_trace_id_put_system_id(int id); + +/* notifiers for perf session start and stop */ + +/** + * Notify the Trace ID allocator that a perf session is starting. + * + * Increase the perf session reference count - called by perf when setting up + * a trace event. + * + * This reference count is used by the ID allocator to ensure that trace IDs + * associated with a CPU cannot change or be released during a perf session. + */ +void coresight_trace_id_perf_start(void); + +/** + * Notify the ID allocator that a perf session is stopping. + * + * Decrease the perf session reference count. + * if this causes the count to go to zero, then all Trace IDs marked as pending + * release, will be released. + */ +void coresight_trace_id_perf_stop(void); + +#endif /* _CORESIGHT_TRACE_ID_H */ diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.c b/drivers/hwtracing/coresight/ultrasoc-smb.c new file mode 100644 index 000000000000..b317342c7ce5 --- /dev/null +++ b/drivers/hwtracing/coresight/ultrasoc-smb.c @@ -0,0 +1,648 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Siemens System Memory Buffer driver. + * Copyright(c) 2022, HiSilicon Limited. + */ + +#include <linux/atomic.h> +#include <linux/acpi.h> +#include <linux/circ_buf.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> + +#include "coresight-etm-perf.h" +#include "coresight-priv.h" +#include "ultrasoc-smb.h" + +DEFINE_CORESIGHT_DEVLIST(sink_devs, "ultra_smb"); + +#define ULTRASOC_SMB_DSM_UUID "82ae1283-7f6a-4cbe-aa06-53e8fb24db18" + +static bool smb_buffer_not_empty(struct smb_drv_data *drvdata) +{ + u32 buf_status = readl(drvdata->base + SMB_LB_INT_STS_REG); + + return FIELD_GET(SMB_LB_INT_STS_NOT_EMPTY_MSK, buf_status); +} + +static void smb_update_data_size(struct smb_drv_data *drvdata) +{ + struct smb_data_buffer *sdb = &drvdata->sdb; + u32 buf_wrptr; + + buf_wrptr = readl(drvdata->base + SMB_LB_WR_ADDR_REG) - + sdb->buf_hw_base; + + /* Buffer is full */ + if (buf_wrptr == sdb->buf_rdptr && smb_buffer_not_empty(drvdata)) { + sdb->data_size = sdb->buf_size; + return; + } + + /* The buffer mode is circular buffer mode */ + sdb->data_size = CIRC_CNT(buf_wrptr, sdb->buf_rdptr, + sdb->buf_size); +} + +/* + * The read pointer adds @nbytes bytes (may round up to the beginning) + * after the data is read or discarded, while needing to update the + * available data size. + */ +static void smb_update_read_ptr(struct smb_drv_data *drvdata, u32 nbytes) +{ + struct smb_data_buffer *sdb = &drvdata->sdb; + + sdb->buf_rdptr += nbytes; + sdb->buf_rdptr %= sdb->buf_size; + writel(sdb->buf_hw_base + sdb->buf_rdptr, + drvdata->base + SMB_LB_RD_ADDR_REG); + + sdb->data_size -= nbytes; +} + +static void smb_reset_buffer(struct smb_drv_data *drvdata) +{ + struct smb_data_buffer *sdb = &drvdata->sdb; + u32 write_ptr; + + /* + * We must flush and discard any data left in hardware path + * to avoid corrupting the next session. + * Note: The write pointer will never exceed the read pointer. + */ + writel(SMB_LB_PURGE_PURGED, drvdata->base + SMB_LB_PURGE_REG); + + /* Reset SMB logical buffer status flags */ + writel(SMB_LB_INT_STS_RESET, drvdata->base + SMB_LB_INT_STS_REG); + + write_ptr = readl(drvdata->base + SMB_LB_WR_ADDR_REG); + + /* Do nothing, not data left in hardware path */ + if (!write_ptr || write_ptr == sdb->buf_rdptr + sdb->buf_hw_base) + return; + + /* + * The SMB_LB_WR_ADDR_REG register is read-only, + * Synchronize the read pointer to write pointer. + */ + writel(write_ptr, drvdata->base + SMB_LB_RD_ADDR_REG); + sdb->buf_rdptr = write_ptr - sdb->buf_hw_base; +} + +static int smb_open(struct inode *inode, struct file *file) +{ + struct smb_drv_data *drvdata = container_of(file->private_data, + struct smb_drv_data, miscdev); + int ret = 0; + + mutex_lock(&drvdata->mutex); + + if (drvdata->reading) { + ret = -EBUSY; + goto out; + } + + if (atomic_read(drvdata->csdev->refcnt)) { + ret = -EBUSY; + goto out; + } + + smb_update_data_size(drvdata); + + drvdata->reading = true; +out: + mutex_unlock(&drvdata->mutex); + + return ret; +} + +static ssize_t smb_read(struct file *file, char __user *data, size_t len, + loff_t *ppos) +{ + struct smb_drv_data *drvdata = container_of(file->private_data, + struct smb_drv_data, miscdev); + struct smb_data_buffer *sdb = &drvdata->sdb; + struct device *dev = &drvdata->csdev->dev; + ssize_t to_copy = 0; + + if (!len) + return 0; + + mutex_lock(&drvdata->mutex); + + if (!sdb->data_size) + goto out; + + to_copy = min(sdb->data_size, len); + + /* Copy parts of trace data when read pointer wrap around SMB buffer */ + if (sdb->buf_rdptr + to_copy > sdb->buf_size) + to_copy = sdb->buf_size - sdb->buf_rdptr; + + if (copy_to_user(data, sdb->buf_base + sdb->buf_rdptr, to_copy)) { + dev_dbg(dev, "Failed to copy data to user\n"); + to_copy = -EFAULT; + goto out; + } + + *ppos += to_copy; + + smb_update_read_ptr(drvdata, to_copy); + + dev_dbg(dev, "%zu bytes copied\n", to_copy); +out: + if (!sdb->data_size) + smb_reset_buffer(drvdata); + mutex_unlock(&drvdata->mutex); + + return to_copy; +} + +static int smb_release(struct inode *inode, struct file *file) +{ + struct smb_drv_data *drvdata = container_of(file->private_data, + struct smb_drv_data, miscdev); + + mutex_lock(&drvdata->mutex); + drvdata->reading = false; + mutex_unlock(&drvdata->mutex); + + return 0; +} + +static const struct file_operations smb_fops = { + .owner = THIS_MODULE, + .open = smb_open, + .read = smb_read, + .release = smb_release, + .llseek = no_llseek, +}; + +static ssize_t buf_size_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct smb_drv_data *drvdata = dev_get_drvdata(dev->parent); + + return sysfs_emit(buf, "0x%lx\n", drvdata->sdb.buf_size); +} +static DEVICE_ATTR_RO(buf_size); + +static struct attribute *smb_sink_attrs[] = { + coresight_simple_reg32(read_pos, SMB_LB_RD_ADDR_REG), + coresight_simple_reg32(write_pos, SMB_LB_WR_ADDR_REG), + coresight_simple_reg32(buf_status, SMB_LB_INT_STS_REG), + &dev_attr_buf_size.attr, + NULL +}; + +static const struct attribute_group smb_sink_group = { + .attrs = smb_sink_attrs, + .name = "mgmt", +}; + +static const struct attribute_group *smb_sink_groups[] = { + &smb_sink_group, + NULL +}; + +static void smb_enable_hw(struct smb_drv_data *drvdata) +{ + writel(SMB_GLB_EN_HW_ENABLE, drvdata->base + SMB_GLB_EN_REG); +} + +static void smb_disable_hw(struct smb_drv_data *drvdata) +{ + writel(0x0, drvdata->base + SMB_GLB_EN_REG); +} + +static void smb_enable_sysfs(struct coresight_device *csdev) +{ + struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (drvdata->mode != CS_MODE_DISABLED) + return; + + smb_enable_hw(drvdata); + drvdata->mode = CS_MODE_SYSFS; +} + +static int smb_enable_perf(struct coresight_device *csdev, void *data) +{ + struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); + struct perf_output_handle *handle = data; + struct cs_buffers *buf = etm_perf_sink_config(handle); + pid_t pid; + + if (!buf) + return -EINVAL; + + /* Get a handle on the pid of the target process */ + pid = buf->pid; + + /* Device is already in used by other session */ + if (drvdata->pid != -1 && drvdata->pid != pid) + return -EBUSY; + + if (drvdata->pid == -1) { + smb_enable_hw(drvdata); + drvdata->pid = pid; + drvdata->mode = CS_MODE_PERF; + } + + return 0; +} + +static int smb_enable(struct coresight_device *csdev, u32 mode, void *data) +{ + struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); + int ret = 0; + + mutex_lock(&drvdata->mutex); + + /* Do nothing, the trace data is reading by other interface now */ + if (drvdata->reading) { + ret = -EBUSY; + goto out; + } + + /* Do nothing, the SMB is already enabled as other mode */ + if (drvdata->mode != CS_MODE_DISABLED && drvdata->mode != mode) { + ret = -EBUSY; + goto out; + } + + switch (mode) { + case CS_MODE_SYSFS: + smb_enable_sysfs(csdev); + break; + case CS_MODE_PERF: + ret = smb_enable_perf(csdev, data); + break; + default: + ret = -EINVAL; + } + + if (ret) + goto out; + + atomic_inc(csdev->refcnt); + + dev_dbg(&csdev->dev, "Ultrasoc SMB enabled\n"); +out: + mutex_unlock(&drvdata->mutex); + + return ret; +} + +static int smb_disable(struct coresight_device *csdev) +{ + struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); + int ret = 0; + + mutex_lock(&drvdata->mutex); + + if (drvdata->reading) { + ret = -EBUSY; + goto out; + } + + if (atomic_dec_return(csdev->refcnt)) { + ret = -EBUSY; + goto out; + } + + /* Complain if we (somehow) got out of sync */ + WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED); + + smb_disable_hw(drvdata); + + /* Dissociate from the target process. */ + drvdata->pid = -1; + drvdata->mode = CS_MODE_DISABLED; + + dev_dbg(&csdev->dev, "Ultrasoc SMB disabled\n"); +out: + mutex_unlock(&drvdata->mutex); + + return ret; +} + +static void *smb_alloc_buffer(struct coresight_device *csdev, + struct perf_event *event, void **pages, + int nr_pages, bool overwrite) +{ + struct cs_buffers *buf; + int node; + + node = (event->cpu == -1) ? NUMA_NO_NODE : cpu_to_node(event->cpu); + buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node); + if (!buf) + return NULL; + + buf->snapshot = overwrite; + buf->nr_pages = nr_pages; + buf->data_pages = pages; + buf->pid = task_pid_nr(event->owner); + + return buf; +} + +static void smb_free_buffer(void *config) +{ + struct cs_buffers *buf = config; + + kfree(buf); +} + +static void smb_sync_perf_buffer(struct smb_drv_data *drvdata, + struct cs_buffers *buf, + unsigned long head) +{ + struct smb_data_buffer *sdb = &drvdata->sdb; + char **dst_pages = (char **)buf->data_pages; + unsigned long to_copy; + long pg_idx, pg_offset; + + pg_idx = head >> PAGE_SHIFT; + pg_offset = head & (PAGE_SIZE - 1); + + while (sdb->data_size) { + unsigned long pg_space = PAGE_SIZE - pg_offset; + + to_copy = min(sdb->data_size, pg_space); + + /* Copy parts of trace data when read pointer wrap around */ + if (sdb->buf_rdptr + to_copy > sdb->buf_size) + to_copy = sdb->buf_size - sdb->buf_rdptr; + + memcpy(dst_pages[pg_idx] + pg_offset, + sdb->buf_base + sdb->buf_rdptr, to_copy); + + pg_offset += to_copy; + if (pg_offset >= PAGE_SIZE) { + pg_offset = 0; + pg_idx++; + pg_idx %= buf->nr_pages; + } + smb_update_read_ptr(drvdata, to_copy); + } + + smb_reset_buffer(drvdata); +} + +static unsigned long smb_update_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config) +{ + struct smb_drv_data *drvdata = dev_get_drvdata(csdev->dev.parent); + struct smb_data_buffer *sdb = &drvdata->sdb; + struct cs_buffers *buf = sink_config; + unsigned long data_size = 0; + bool lost = false; + + if (!buf) + return 0; + + mutex_lock(&drvdata->mutex); + + /* Don't do anything if another tracer is using this sink. */ + if (atomic_read(csdev->refcnt) != 1) + goto out; + + smb_disable_hw(drvdata); + smb_update_data_size(drvdata); + + /* + * The SMB buffer may be bigger than the space available in the + * perf ring buffer (handle->size). If so advance the offset so + * that we get the latest trace data. + */ + if (sdb->data_size > handle->size) { + smb_update_read_ptr(drvdata, sdb->data_size - handle->size); + lost = true; + } + + data_size = sdb->data_size; + smb_sync_perf_buffer(drvdata, buf, handle->head); + if (!buf->snapshot && lost) + perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); +out: + mutex_unlock(&drvdata->mutex); + + return data_size; +} + +static const struct coresight_ops_sink smb_cs_ops = { + .enable = smb_enable, + .disable = smb_disable, + .alloc_buffer = smb_alloc_buffer, + .free_buffer = smb_free_buffer, + .update_buffer = smb_update_buffer, +}; + +static const struct coresight_ops cs_ops = { + .sink_ops = &smb_cs_ops, +}; + +static int smb_init_data_buffer(struct platform_device *pdev, + struct smb_data_buffer *sdb) +{ + struct resource *res; + void *base; + + res = platform_get_resource(pdev, IORESOURCE_MEM, SMB_BUF_ADDR_RES); + if (!res) { + dev_err(&pdev->dev, "SMB device failed to get resource\n"); + return -EINVAL; + } + + sdb->buf_rdptr = 0; + sdb->buf_hw_base = FIELD_GET(SMB_BUF_ADDR_LO_MSK, res->start); + sdb->buf_size = resource_size(res); + if (sdb->buf_size == 0) + return -EINVAL; + + /* + * This is a chunk of memory, use classic mapping with better + * performance. + */ + base = devm_memremap(&pdev->dev, sdb->buf_hw_base, sdb->buf_size, + MEMREMAP_WB); + if (IS_ERR(base)) + return PTR_ERR(base); + + sdb->buf_base = base; + + return 0; +} + +static void smb_init_hw(struct smb_drv_data *drvdata) +{ + smb_disable_hw(drvdata); + smb_reset_buffer(drvdata); + + writel(SMB_LB_CFG_LO_DEFAULT, drvdata->base + SMB_LB_CFG_LO_REG); + writel(SMB_LB_CFG_HI_DEFAULT, drvdata->base + SMB_LB_CFG_HI_REG); + writel(SMB_GLB_CFG_DEFAULT, drvdata->base + SMB_GLB_CFG_REG); + writel(SMB_GLB_INT_CFG, drvdata->base + SMB_GLB_INT_REG); + writel(SMB_LB_INT_CTRL_CFG, drvdata->base + SMB_LB_INT_CTRL_REG); +} + +static int smb_register_sink(struct platform_device *pdev, + struct smb_drv_data *drvdata) +{ + struct coresight_platform_data *pdata = NULL; + struct coresight_desc desc = { 0 }; + int ret; + + pdata = coresight_get_platform_data(&pdev->dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + + desc.type = CORESIGHT_DEV_TYPE_SINK; + desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; + desc.ops = &cs_ops; + desc.pdata = pdata; + desc.dev = &pdev->dev; + desc.groups = smb_sink_groups; + desc.name = coresight_alloc_device_name(&sink_devs, &pdev->dev); + if (!desc.name) { + dev_err(&pdev->dev, "Failed to alloc coresight device name"); + return -ENOMEM; + } + desc.access = CSDEV_ACCESS_IOMEM(drvdata->base); + + drvdata->csdev = coresight_register(&desc); + if (IS_ERR(drvdata->csdev)) + return PTR_ERR(drvdata->csdev); + + drvdata->miscdev.name = desc.name; + drvdata->miscdev.minor = MISC_DYNAMIC_MINOR; + drvdata->miscdev.fops = &smb_fops; + ret = misc_register(&drvdata->miscdev); + if (ret) { + coresight_unregister(drvdata->csdev); + dev_err(&pdev->dev, "Failed to register misc, ret=%d\n", ret); + } + + return ret; +} + +static void smb_unregister_sink(struct smb_drv_data *drvdata) +{ + misc_deregister(&drvdata->miscdev); + coresight_unregister(drvdata->csdev); +} + +static int smb_config_inport(struct device *dev, bool enable) +{ + u64 func = enable ? 1 : 0; + union acpi_object *obj; + guid_t guid; + u64 rev = 0; + + /* + * Using DSM calls to enable/disable ultrasoc hardwares on + * tracing path, to prevent ultrasoc packet format being exposed. + */ + if (guid_parse(ULTRASOC_SMB_DSM_UUID, &guid)) { + dev_err(dev, "Get GUID failed\n"); + return -EINVAL; + } + + obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), &guid, rev, func, NULL); + if (!obj) { + dev_err(dev, "ACPI handle failed\n"); + return -ENODEV; + } + + ACPI_FREE(obj); + + return 0; +} + +static int smb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct smb_drv_data *drvdata; + int ret; + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->base = devm_platform_ioremap_resource(pdev, SMB_REG_ADDR_RES); + if (IS_ERR(drvdata->base)) { + dev_err(dev, "Failed to ioremap resource\n"); + return PTR_ERR(drvdata->base); + } + + smb_init_hw(drvdata); + + ret = smb_init_data_buffer(pdev, &drvdata->sdb); + if (ret) { + dev_err(dev, "Failed to init buffer, ret = %d\n", ret); + return ret; + } + + mutex_init(&drvdata->mutex); + drvdata->pid = -1; + + ret = smb_register_sink(pdev, drvdata); + if (ret) { + dev_err(dev, "Failed to register SMB sink\n"); + return ret; + } + + ret = smb_config_inport(dev, true); + if (ret) { + smb_unregister_sink(drvdata); + return ret; + } + + platform_set_drvdata(pdev, drvdata); + + return 0; +} + +static int smb_remove(struct platform_device *pdev) +{ + struct smb_drv_data *drvdata = platform_get_drvdata(pdev); + int ret; + + ret = smb_config_inport(&pdev->dev, false); + if (ret) + return ret; + + smb_unregister_sink(drvdata); + + return 0; +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id ultrasoc_smb_acpi_match[] = { + {"HISI03A1", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, ultrasoc_smb_acpi_match); +#endif + +static struct platform_driver smb_driver = { + .driver = { + .name = "ultrasoc-smb", + .acpi_match_table = ACPI_PTR(ultrasoc_smb_acpi_match), + .suppress_bind_attrs = true, + }, + .probe = smb_probe, + .remove = smb_remove, +}; +module_platform_driver(smb_driver); + +MODULE_DESCRIPTION("UltraSoc SMB CoreSight driver"); +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("Jonathan Zhou <jonathan.zhouwen@huawei.com>"); +MODULE_AUTHOR("Qi Liu <liuqi115@huawei.com>"); diff --git a/drivers/hwtracing/coresight/ultrasoc-smb.h b/drivers/hwtracing/coresight/ultrasoc-smb.h new file mode 100644 index 000000000000..7dfbe42e37a0 --- /dev/null +++ b/drivers/hwtracing/coresight/ultrasoc-smb.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Siemens System Memory Buffer driver. + * Copyright(c) 2022, HiSilicon Limited. + */ + +#ifndef _ULTRASOC_SMB_H +#define _ULTRASOC_SMB_H + +#include <linux/miscdevice.h> +#include <linux/mutex.h> + +/* Offset of SMB global registers */ +#define SMB_GLB_CFG_REG 0x00 +#define SMB_GLB_EN_REG 0x04 +#define SMB_GLB_INT_REG 0x08 + +/* Offset of SMB logical buffer registers */ +#define SMB_LB_CFG_LO_REG 0x40 +#define SMB_LB_CFG_HI_REG 0x44 +#define SMB_LB_INT_CTRL_REG 0x48 +#define SMB_LB_INT_STS_REG 0x4c +#define SMB_LB_RD_ADDR_REG 0x5c +#define SMB_LB_WR_ADDR_REG 0x60 +#define SMB_LB_PURGE_REG 0x64 + +/* Set global config register */ +#define SMB_GLB_CFG_BURST_LEN_MSK GENMASK(11, 4) +#define SMB_GLB_CFG_IDLE_PRD_MSK GENMASK(15, 12) +#define SMB_GLB_CFG_MEM_WR_MSK GENMASK(21, 16) +#define SMB_GLB_CFG_MEM_RD_MSK GENMASK(27, 22) +#define SMB_GLB_CFG_DEFAULT (FIELD_PREP(SMB_GLB_CFG_BURST_LEN_MSK, 0xf) | \ + FIELD_PREP(SMB_GLB_CFG_IDLE_PRD_MSK, 0xf) | \ + FIELD_PREP(SMB_GLB_CFG_MEM_WR_MSK, 0x3) | \ + FIELD_PREP(SMB_GLB_CFG_MEM_RD_MSK, 0x1b)) + +#define SMB_GLB_EN_HW_ENABLE BIT(0) + +/* Set global interrupt control register */ +#define SMB_GLB_INT_EN BIT(0) +#define SMB_GLB_INT_PULSE BIT(1) /* Interrupt type: 1 - Pulse */ +#define SMB_GLB_INT_ACT_H BIT(2) /* Interrupt polarity: 1 - Active high */ +#define SMB_GLB_INT_CFG (SMB_GLB_INT_EN | SMB_GLB_INT_PULSE | \ + SMB_GLB_INT_ACT_H) + +/* Set logical buffer config register lower 32 bits */ +#define SMB_LB_CFG_LO_EN BIT(0) +#define SMB_LB_CFG_LO_SINGLE_END BIT(1) +#define SMB_LB_CFG_LO_INIT BIT(8) +#define SMB_LB_CFG_LO_CONT BIT(11) +#define SMB_LB_CFG_LO_FLOW_MSK GENMASK(19, 16) +#define SMB_LB_CFG_LO_DEFAULT (SMB_LB_CFG_LO_EN | SMB_LB_CFG_LO_SINGLE_END | \ + SMB_LB_CFG_LO_INIT | SMB_LB_CFG_LO_CONT | \ + FIELD_PREP(SMB_LB_CFG_LO_FLOW_MSK, 0xf)) + +/* Set logical buffer config register upper 32 bits */ +#define SMB_LB_CFG_HI_RANGE_UP_MSK GENMASK(15, 8) +#define SMB_LB_CFG_HI_DEFAULT FIELD_PREP(SMB_LB_CFG_HI_RANGE_UP_MSK, 0xff) + +/* + * Set logical buffer interrupt control register. + * The register control the validity of both real-time events and + * interrupts. When logical buffer status changes causes to issue + * an interrupt at the same time as it issues a real-time event. + * Real-time events are used in SMB driver, which needs to get the buffer + * status. Interrupts are used in debugger mode. + * SMB_LB_INT_CTRL_BUF_NOTE_MASK control which events flags or interrupts + * are valid. + */ +#define SMB_LB_INT_CTRL_EN BIT(0) +#define SMB_LB_INT_CTRL_BUF_NOTE_MSK GENMASK(11, 8) +#define SMB_LB_INT_CTRL_CFG (SMB_LB_INT_CTRL_EN | \ + FIELD_PREP(SMB_LB_INT_CTRL_BUF_NOTE_MSK, 0xf)) + +/* Set logical buffer interrupt status register */ +#define SMB_LB_INT_STS_NOT_EMPTY_MSK BIT(0) +#define SMB_LB_INT_STS_BUF_RESET_MSK GENMASK(3, 0) +#define SMB_LB_INT_STS_RESET FIELD_PREP(SMB_LB_INT_STS_BUF_RESET_MSK, 0xf) + +#define SMB_LB_PURGE_PURGED BIT(0) + +#define SMB_REG_ADDR_RES 0 +#define SMB_BUF_ADDR_RES 1 +#define SMB_BUF_ADDR_LO_MSK GENMASK(31, 0) + +/** + * struct smb_data_buffer - Details of the buffer used by SMB + * @buf_base: Memory mapped base address of SMB. + * @buf_hw_base: SMB buffer start Physical base address, only used 32bits. + * @buf_size: Size of the buffer. + * @data_size: Size of the available trace data for SMB. + * @buf_rdptr: Current read position (index) within the buffer. + */ +struct smb_data_buffer { + void *buf_base; + u32 buf_hw_base; + unsigned long buf_size; + unsigned long data_size; + unsigned long buf_rdptr; +}; + +/** + * struct smb_drv_data - specifics associated to an SMB component + * @base: Memory mapped base address for SMB component. + * @csdev: Component vitals needed by the framework. + * @sdb: Data buffer for SMB. + * @miscdev: Specifics to handle "/dev/xyz.smb" entry. + * @mutex: Control data access to one at a time. + * @reading: Synchronise user space access to SMB buffer. + * @pid: Process ID of the process being monitored by the + * session that is using this component. + * @mode: How this SMB is being used, perf mode or sysfs mode. + */ +struct smb_drv_data { + void __iomem *base; + struct coresight_device *csdev; + struct smb_data_buffer sdb; + struct miscdevice miscdev; + struct mutex mutex; + bool reading; + pid_t pid; + u32 mode; +}; + +#endif diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index 5d5526aa60c4..30f1525639b5 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -356,8 +356,18 @@ static int hisi_ptt_register_irq(struct hisi_ptt *hisi_ptt) static int hisi_ptt_init_filters(struct pci_dev *pdev, void *data) { + struct pci_dev *root_port = pcie_find_root_port(pdev); struct hisi_ptt_filter_desc *filter; struct hisi_ptt *hisi_ptt = data; + u32 port_devid; + + if (!root_port) + return 0; + + port_devid = PCI_DEVID(root_port->bus->number, root_port->devfn); + if (port_devid < hisi_ptt->lower_bdf || + port_devid > hisi_ptt->upper_bdf) + return 0; /* * We won't fail the probe if filter allocation failed here. The filters diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 03ac410c162e..b6b45d359f28 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -380,7 +380,7 @@ config IIO_ST_ACCEL_3AXIS select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) help Say yes here to build support for STMicroelectronics accelerometers: - LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, + LSM303C, LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL, LNG2DM, LIS3DE, LIS2DE12, LIS2HH12 diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h index 36edbaff4f7f..932358b45f17 100644 --- a/drivers/iio/accel/bma400.h +++ b/drivers/iio/accel/bma400.h @@ -141,10 +141,6 @@ #define BMA400_SCALE_MIN 9577 #define BMA400_SCALE_MAX 76617 -#define BMA400_NUM_REGULATORS 2 -#define BMA400_VDD_REGULATOR 0 -#define BMA400_VDDIO_REGULATOR 1 - extern const struct regmap_config bma400_regmap_config; int bma400_probe(struct device *dev, struct regmap *regmap, int irq, diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index b612d0146d4d..623f37cbaf50 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -98,7 +98,6 @@ enum bma400_activity { struct bma400_data { struct device *dev; struct regmap *regmap; - struct regulator_bulk_data regulators[BMA400_NUM_REGULATORS]; struct mutex mutex; /* data register lock */ struct iio_mount_matrix orientation; enum bma400_power_mode power_mode; @@ -832,13 +831,6 @@ static void bma400_init_tables(void) } } -static void bma400_regulators_disable(void *data_ptr) -{ - struct bma400_data *data = data_ptr; - - regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); -} - static void bma400_power_disable(void *data_ptr) { struct bma400_data *data = data_ptr; @@ -868,30 +860,17 @@ static enum iio_modifier bma400_act_to_mod(enum bma400_activity activity) static int bma400_init(struct bma400_data *data) { + static const char * const regulator_names[] = { "vdd", "vddio" }; unsigned int val; int ret; - data->regulators[BMA400_VDD_REGULATOR].supply = "vdd"; - data->regulators[BMA400_VDDIO_REGULATOR].supply = "vddio"; - ret = devm_regulator_bulk_get(data->dev, - ARRAY_SIZE(data->regulators), - data->regulators); + ret = devm_regulator_bulk_get_enable(data->dev, + ARRAY_SIZE(regulator_names), + regulator_names); if (ret) return dev_err_probe(data->dev, ret, "Failed to get regulators: %d\n", ret); - ret = regulator_bulk_enable(ARRAY_SIZE(data->regulators), - data->regulators); - if (ret) { - dev_err(data->dev, "Failed to enable regulators: %d\n", - ret); - return ret; - } - - ret = devm_add_action_or_reset(data->dev, bma400_regulators_disable, data); - if (ret) - return ret; - /* Try to read chip_id register. It must return 0x90. */ ret = regmap_read(data->regmap, BMA400_CHIP_ID_REG, &val); if (ret) { diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c index 64ca7d7a9673..b898f865fb87 100644 --- a/drivers/iio/accel/mma9551_core.c +++ b/drivers/iio/accel/mma9551_core.c @@ -296,9 +296,12 @@ int mma9551_read_config_word(struct i2c_client *client, u8 app_id, ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG, reg, NULL, 0, (u8 *)&v, 2); + if (ret < 0) + return ret; + *val = be16_to_cpu(v); - return ret; + return 0; } EXPORT_SYMBOL_NS(mma9551_read_config_word, IIO_MMA9551); @@ -354,9 +357,12 @@ int mma9551_read_status_word(struct i2c_client *client, u8 app_id, ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS, reg, NULL, 0, (u8 *)&v, 2); + if (ret < 0) + return ret; + *val = be16_to_cpu(v); - return ret; + return 0; } EXPORT_SYMBOL_NS(mma9551_read_status_word, IIO_MMA9551); diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 5b0f54e33d9e..56ed0c776d4a 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -37,6 +37,7 @@ #define LIS2DE12_ACCEL_DEV_NAME "lis2de12" #define LIS2HH12_ACCEL_DEV_NAME "lis2hh12" #define LIS302DL_ACCEL_DEV_NAME "lis302dl" +#define LSM303C_ACCEL_DEV_NAME "lsm303c_accel" #define SC7A20_ACCEL_DEV_NAME "sc7a20" diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index c8c8eb15c34e..6b8562f684d5 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -929,6 +929,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, .sensors_supported = { [0] = LIS2HH12_ACCEL_DEV_NAME, + [1] = LSM303C_ACCEL_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_accel_16bit_channels, .odr = { diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 45ee0ddc133c..3f02fd5d5946 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -112,6 +112,10 @@ static const struct of_device_id st_accel_of_match[] = { .data = LIS302DL_ACCEL_DEV_NAME, }, { + .compatible = "st,lsm303c-accel", + .data = LSM303C_ACCEL_DEV_NAME, + }, + { .compatible = "silan,sc7a20", .data = SC7A20_ACCEL_DEV_NAME, }, @@ -151,6 +155,7 @@ static const struct i2c_device_id st_accel_id_table[] = { { LIS2DE12_ACCEL_DEV_NAME }, { LIS2HH12_ACCEL_DEV_NAME }, { LIS302DL_ACCEL_DEV_NAME }, + { LSM303C_ACCEL_DEV_NAME }, { SC7A20_ACCEL_DEV_NAME }, {}, }; diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 6c0917750288..5740dc1820bd 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -96,6 +96,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,lis302dl", .data = LIS302DL_ACCEL_DEV_NAME, }, + { + .compatible = "st,lsm303c-accel", + .data = LSM303C_ACCEL_DEV_NAME, + }, {} }; MODULE_DEVICE_TABLE(of, st_accel_of_match); @@ -152,6 +156,7 @@ static const struct spi_device_id st_accel_id_table[] = { { LIS3DHH_ACCEL_DEV_NAME }, { LIS3DE_ACCEL_DEV_NAME }, { LIS302DL_ACCEL_DEV_NAME }, + { LSM303C_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_accel_id_table); diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 63f80d747cbd..4a95c843a91b 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -441,7 +441,8 @@ config ENVELOPE_DETECTOR config EP93XX_ADC tristate "Cirrus Logic EP93XX ADC driver" - depends on ARCH_EP93XX + depends on ARCH_EP93XX || COMPILE_TEST + depends on HAS_IOMEM help Driver for the ADC module on the EP93XX series of SoC from Cirrus Logic. It's recommended to switch on CONFIG_HIGH_RES_TIMERS option, in this @@ -565,6 +566,16 @@ config IMX8QXP_ADC This driver can also be built as a module. If so, the module will be called imx8qxp-adc. +config IMX93_ADC + tristate "IMX93 ADC driver" + depends on ARCH_MXC || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for IMX93 ADC. + + This driver can also be built as a module. If so, the module will be + called imx93_adc. + config LP8788_ADC tristate "LP8788 ADC driver" depends on MFD_LP8788 @@ -1207,6 +1218,17 @@ config TI_ADS1015 This driver can also be built as a module. If so, the module will be called ti-ads1015. +config TI_ADS7924 + tristate "Texas Instruments ADS7924 ADC" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for Texas Instruments ADS7924 + 4 channels, 12-bit I2C ADC chip. + + This driver can also be built as a module. If so, the module will be + called ti-ads7924. + config TI_ADS7950 tristate "Texas Instruments ADS7950 ADC driver" depends on SPI && GPIOLIB @@ -1274,6 +1296,16 @@ config TI_AM335X_ADC To compile this driver as a module, choose M here: the module will be called ti_am335x_adc. +config TI_LMP92064 + tristate "Texas Instruments LMP92064 ADC driver" + depends on SPI + help + Say yes here to build support for the LMP92064 Precision Current and Voltage + sensor. + + This driver can also be built as a module. If so, the module will be called + ti-lmp92064. + config TI_TLC4541 tristate "Texas Instruments TLC4541 ADC driver" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 4ef41a7dfac6..36c18177322a 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -49,6 +49,7 @@ obj-$(CONFIG_HI8435) += hi8435.o obj-$(CONFIG_HX711) += hx711.o obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o obj-$(CONFIG_IMX8QXP_ADC) += imx8qxp-adc.o +obj-$(CONFIG_IMX93_ADC) += imx93_adc.o obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o obj-$(CONFIG_INTEL_MRFLD_ADC) += intel_mrfld_adc.o @@ -107,12 +108,14 @@ obj-$(CONFIG_TI_ADC108S102) += ti-adc108s102.o obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o +obj-$(CONFIG_TI_ADS7924) += ti-ads7924.o obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o +obj-$(CONFIG_TI_LMP92064) += ti-lmp92064.o obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o obj-$(CONFIG_TI_TSC2046) += ti-tsc2046.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c index 3dd0105f63d7..f9ee189925de 100644 --- a/drivers/iio/adc/ad7291.c +++ b/drivers/iio/adc/ad7291.c @@ -179,7 +179,7 @@ static unsigned int ad7291_threshold_reg(const struct iio_chan_spec *chan, offset = AD7291_VOLTAGE_OFFSET; break; default: - return 0; + return 0; } switch (info) { diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index ed4f8501bda8..50d02e5fc6fc 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -2181,7 +2181,7 @@ static ssize_t at91_adc_get_fifo_state(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct at91_adc_state *st = iio_priv(indio_dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", !!st->dma_st.dma_chan); + return sysfs_emit(buf, "%d\n", !!st->dma_st.dma_chan); } static ssize_t at91_adc_get_watermark(struct device *dev, @@ -2190,7 +2190,7 @@ static ssize_t at91_adc_get_watermark(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct at91_adc_state *st = iio_priv(indio_dev); - return scnprintf(buf, PAGE_SIZE, "%d\n", st->dma_st.watermark); + return sysfs_emit(buf, "%d\n", st->dma_st.watermark); } static IIO_DEVICE_ATTR(hwfifo_enabled, 0444, diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c index fd5a9404c8dc..a35e6cead67d 100644 --- a/drivers/iio/adc/ep93xx_adc.c +++ b/drivers/iio/adc/ep93xx_adc.c @@ -21,6 +21,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/platform_device.h> +#include <linux/of.h> /* * This code could benefit from real HR Timers, but jiffy granularity would @@ -227,9 +228,16 @@ static int ep93xx_adc_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id ep93xx_adc_of_ids[] = { + { .compatible = "cirrus,ep9301-adc" }, + {} +}; +MODULE_DEVICE_TABLE(of, ep93xx_adc_of_ids); + static struct platform_driver ep93xx_adc_driver = { .driver = { .name = "ep93xx-adc", + .of_match_table = ep93xx_adc_of_ids, }, .probe = ep93xx_adc_probe, .remove = ep93xx_adc_remove, diff --git a/drivers/iio/adc/imx93_adc.c b/drivers/iio/adc/imx93_adc.c new file mode 100644 index 000000000000..a775d2e40567 --- /dev/null +++ b/drivers/iio/adc/imx93_adc.c @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NXP i.MX93 ADC driver + * + * Copyright 2023 NXP + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/err.h> +#include <linux/iio/iio.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> + +#define IMX93_ADC_DRIVER_NAME "imx93-adc" + +/* Register map definition */ +#define IMX93_ADC_MCR 0x00 +#define IMX93_ADC_MSR 0x04 +#define IMX93_ADC_ISR 0x10 +#define IMX93_ADC_IMR 0x20 +#define IMX93_ADC_CIMR0 0x24 +#define IMX93_ADC_CTR0 0x94 +#define IMX93_ADC_NCMR0 0xA4 +#define IMX93_ADC_PCDR0 0x100 +#define IMX93_ADC_PCDR1 0x104 +#define IMX93_ADC_PCDR2 0x108 +#define IMX93_ADC_PCDR3 0x10c +#define IMX93_ADC_PCDR4 0x110 +#define IMX93_ADC_PCDR5 0x114 +#define IMX93_ADC_PCDR6 0x118 +#define IMX93_ADC_PCDR7 0x11c +#define IMX93_ADC_CALSTAT 0x39C + +/* ADC bit shift */ +#define IMX93_ADC_MCR_MODE_MASK BIT(29) +#define IMX93_ADC_MCR_NSTART_MASK BIT(24) +#define IMX93_ADC_MCR_CALSTART_MASK BIT(14) +#define IMX93_ADC_MCR_ADCLKSE_MASK BIT(8) +#define IMX93_ADC_MCR_PWDN_MASK BIT(0) +#define IMX93_ADC_MSR_CALFAIL_MASK BIT(30) +#define IMX93_ADC_MSR_CALBUSY_MASK BIT(29) +#define IMX93_ADC_MSR_ADCSTATUS_MASK GENMASK(2, 0) +#define IMX93_ADC_ISR_ECH_MASK BIT(0) +#define IMX93_ADC_ISR_EOC_MASK BIT(1) +#define IMX93_ADC_ISR_EOC_ECH_MASK (IMX93_ADC_ISR_EOC_MASK | \ + IMX93_ADC_ISR_ECH_MASK) +#define IMX93_ADC_IMR_JEOC_MASK BIT(3) +#define IMX93_ADC_IMR_JECH_MASK BIT(2) +#define IMX93_ADC_IMR_EOC_MASK BIT(1) +#define IMX93_ADC_IMR_ECH_MASK BIT(0) +#define IMX93_ADC_PCDR_CDATA_MASK GENMASK(11, 0) + +/* ADC status */ +#define IMX93_ADC_MSR_ADCSTATUS_IDLE 0 +#define IMX93_ADC_MSR_ADCSTATUS_POWER_DOWN 1 +#define IMX93_ADC_MSR_ADCSTATUS_WAIT_STATE 2 +#define IMX93_ADC_MSR_ADCSTATUS_BUSY_IN_CALIBRATION 3 +#define IMX93_ADC_MSR_ADCSTATUS_SAMPLE 4 +#define IMX93_ADC_MSR_ADCSTATUS_CONVERSION 6 + +#define IMX93_ADC_TIMEOUT msecs_to_jiffies(100) + +struct imx93_adc { + struct device *dev; + void __iomem *regs; + struct clk *ipg_clk; + int irq; + struct regulator *vref; + /* lock to protect against multiple access to the device */ + struct mutex lock; + struct completion completion; +}; + +#define IMX93_ADC_CHAN(_idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} + +static const struct iio_chan_spec imx93_adc_iio_channels[] = { + IMX93_ADC_CHAN(0), + IMX93_ADC_CHAN(1), + IMX93_ADC_CHAN(2), + IMX93_ADC_CHAN(3), +}; + +static void imx93_adc_power_down(struct imx93_adc *adc) +{ + u32 mcr, msr; + int ret; + + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr |= FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr, + ((msr & IMX93_ADC_MSR_ADCSTATUS_MASK) == + IMX93_ADC_MSR_ADCSTATUS_POWER_DOWN), + 1, 50); + if (ret == -ETIMEDOUT) + dev_warn(adc->dev, + "ADC do not in power down mode, current MSR is %x\n", + msr); +} + +static void imx93_adc_power_up(struct imx93_adc *adc) +{ + u32 mcr; + + /* bring ADC out of power down state, in idle state */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr &= ~FIELD_PREP(IMX93_ADC_MCR_PWDN_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); +} + +static void imx93_adc_config_ad_clk(struct imx93_adc *adc) +{ + u32 mcr; + + /* put adc in power down mode */ + imx93_adc_power_down(adc); + + /* config the AD_CLK equal to bus clock */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr |= FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + imx93_adc_power_up(adc); +} + +static int imx93_adc_calibration(struct imx93_adc *adc) +{ + u32 mcr, msr; + int ret; + + /* make sure ADC in power down mode */ + imx93_adc_power_down(adc); + + /* config SAR controller operating clock */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr &= ~FIELD_PREP(IMX93_ADC_MCR_ADCLKSE_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + imx93_adc_power_up(adc); + + /* + * TODO: we use the default TSAMP/NRSMPL/AVGEN in MCR, + * can add the setting of these bit if need in future. + */ + + /* run calibration */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr |= FIELD_PREP(IMX93_ADC_MCR_CALSTART_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + /* wait calibration to be finished */ + ret = readl_poll_timeout(adc->regs + IMX93_ADC_MSR, msr, + !(msr & IMX93_ADC_MSR_CALBUSY_MASK), 1000, 2000000); + if (ret == -ETIMEDOUT) { + dev_warn(adc->dev, "ADC do not finish calibration in 2 min!\n"); + imx93_adc_power_down(adc); + return ret; + } + + /* check whether calbration is success or not */ + msr = readl(adc->regs + IMX93_ADC_MSR); + if (msr & IMX93_ADC_MSR_CALFAIL_MASK) { + dev_warn(adc->dev, "ADC calibration failed!\n"); + imx93_adc_power_down(adc); + return -EAGAIN; + } + + return 0; +} + +static int imx93_adc_read_channel_conversion(struct imx93_adc *adc, + int channel_number, + int *result) +{ + u32 channel; + u32 imr, mcr, pcda; + long ret; + + reinit_completion(&adc->completion); + + /* config channel mask register */ + channel = 1 << channel_number; + writel(channel, adc->regs + IMX93_ADC_NCMR0); + + /* TODO: can config desired sample time in CTRn if need */ + + /* config interrupt mask */ + imr = FIELD_PREP(IMX93_ADC_IMR_EOC_MASK, 1); + writel(imr, adc->regs + IMX93_ADC_IMR); + writel(channel, adc->regs + IMX93_ADC_CIMR0); + + /* config one-shot mode */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr &= ~FIELD_PREP(IMX93_ADC_MCR_MODE_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + /* start normal conversion */ + mcr = readl(adc->regs + IMX93_ADC_MCR); + mcr |= FIELD_PREP(IMX93_ADC_MCR_NSTART_MASK, 1); + writel(mcr, adc->regs + IMX93_ADC_MCR); + + ret = wait_for_completion_interruptible_timeout(&adc->completion, + IMX93_ADC_TIMEOUT); + if (ret == 0) + return -ETIMEDOUT; + + if (ret < 0) + return ret; + + pcda = readl(adc->regs + IMX93_ADC_PCDR0 + channel_number * 4); + + *result = FIELD_GET(IMX93_ADC_PCDR_CDATA_MASK, pcda); + + return ret; +} + +static int imx93_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct imx93_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + long ret; + u32 vref_uv; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + pm_runtime_get_sync(dev); + mutex_lock(&adc->lock); + ret = imx93_adc_read_channel_conversion(adc, chan->channel, val); + mutex_unlock(&adc->lock); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_sync_autosuspend(dev); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret = vref_uv = regulator_get_voltage(adc->vref); + if (ret < 0) + return ret; + *val = vref_uv / 1000; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = clk_get_rate(adc->ipg_clk); + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static irqreturn_t imx93_adc_isr(int irq, void *dev_id) +{ + struct imx93_adc *adc = dev_id; + u32 isr, eoc, unexpected; + + isr = readl(adc->regs + IMX93_ADC_ISR); + + if (FIELD_GET(IMX93_ADC_ISR_EOC_ECH_MASK, isr)) { + eoc = isr & IMX93_ADC_ISR_EOC_ECH_MASK; + writel(eoc, adc->regs + IMX93_ADC_ISR); + complete(&adc->completion); + } + + unexpected = isr & ~IMX93_ADC_ISR_EOC_ECH_MASK; + if (unexpected) { + writel(unexpected, adc->regs + IMX93_ADC_ISR); + dev_err(adc->dev, "Unexpected interrupt 0x%08x.\n", unexpected); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static const struct iio_info imx93_adc_iio_info = { + .read_raw = &imx93_adc_read_raw, +}; + +static int imx93_adc_probe(struct platform_device *pdev) +{ + struct imx93_adc *adc; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return dev_err_probe(dev, -ENOMEM, + "Failed allocating iio device\n"); + + adc = iio_priv(indio_dev); + adc->dev = dev; + + mutex_init(&adc->lock); + adc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adc->regs)) + return dev_err_probe(dev, PTR_ERR(adc->regs), + "Failed getting ioremap resource\n"); + + /* The third irq is for ADC conversion usage */ + adc->irq = platform_get_irq(pdev, 2); + if (adc->irq < 0) + return adc->irq; + + adc->ipg_clk = devm_clk_get(dev, "ipg"); + if (IS_ERR(adc->ipg_clk)) + return dev_err_probe(dev, PTR_ERR(adc->ipg_clk), + "Failed getting clock.\n"); + + adc->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(adc->vref)) + return dev_err_probe(dev, PTR_ERR(adc->vref), + "Failed getting reference voltage.\n"); + + ret = regulator_enable(adc->vref); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable reference voltage.\n"); + + platform_set_drvdata(pdev, indio_dev); + + init_completion(&adc->completion); + + indio_dev->name = "imx93-adc"; + indio_dev->info = &imx93_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = imx93_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(imx93_adc_iio_channels); + + ret = clk_prepare_enable(adc->ipg_clk); + if (ret) { + dev_err_probe(dev, ret, + "Failed to enable ipg clock.\n"); + goto error_regulator_disable; + } + + ret = request_irq(adc->irq, imx93_adc_isr, 0, IMX93_ADC_DRIVER_NAME, adc); + if (ret < 0) { + dev_err_probe(dev, ret, + "Failed requesting irq, irq = %d\n", adc->irq); + goto error_ipg_clk_disable; + } + + ret = imx93_adc_calibration(adc); + if (ret < 0) + goto error_free_adc_irq; + + imx93_adc_config_ad_clk(adc); + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err_probe(dev, ret, + "Failed to register this iio device.\n"); + goto error_adc_power_down; + } + + pm_runtime_set_active(dev); + pm_runtime_set_autosuspend_delay(dev, 50); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + return 0; + +error_adc_power_down: + imx93_adc_power_down(adc); +error_free_adc_irq: + free_irq(adc->irq, adc); +error_ipg_clk_disable: + clk_disable_unprepare(adc->ipg_clk); +error_regulator_disable: + regulator_disable(adc->vref); + + return ret; +} + +static int imx93_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct imx93_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + + /* adc power down need clock on */ + pm_runtime_get_sync(dev); + + pm_runtime_disable(dev); + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_put_noidle(dev); + + iio_device_unregister(indio_dev); + imx93_adc_power_down(adc); + free_irq(adc->irq, adc); + clk_disable_unprepare(adc->ipg_clk); + regulator_disable(adc->vref); + + return 0; +} + +static int imx93_adc_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct imx93_adc *adc = iio_priv(indio_dev); + + imx93_adc_power_down(adc); + clk_disable_unprepare(adc->ipg_clk); + regulator_disable(adc->vref); + + return 0; +} + +static int imx93_adc_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct imx93_adc *adc = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(adc->vref); + if (ret) { + dev_err(dev, + "Can't enable adc reference top voltage, err = %d\n", + ret); + return ret; + } + + ret = clk_prepare_enable(adc->ipg_clk); + if (ret) { + dev_err(dev, "Could not prepare or enable clock.\n"); + goto err_disable_reg; + } + + imx93_adc_power_up(adc); + + return 0; + +err_disable_reg: + regulator_disable(adc->vref); + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(imx93_adc_pm_ops, + imx93_adc_runtime_suspend, + imx93_adc_runtime_resume, NULL); + +static const struct of_device_id imx93_adc_match[] = { + { .compatible = "nxp,imx93-adc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx93_adc_match); + +static struct platform_driver imx93_adc_driver = { + .probe = imx93_adc_probe, + .remove = imx93_adc_remove, + .driver = { + .name = IMX93_ADC_DRIVER_NAME, + .of_match_table = imx93_adc_match, + .pm = pm_ptr(&imx93_adc_pm_ops), + }, +}; + +module_platform_driver(imx93_adc_driver); + +MODULE_DESCRIPTION("NXP i.MX93 ADC driver"); +MODULE_AUTHOR("Haibo Chen <haibo.chen@nxp.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c index fdc9f03135b5..b74b689ee7de 100644 --- a/drivers/iio/adc/max11410.c +++ b/drivers/iio/adc/max11410.c @@ -4,7 +4,6 @@ * * Copyright 2022 Analog Devices Inc. */ -#include <asm-generic/unaligned.h> #include <linux/bitfield.h> #include <linux/delay.h> #include <linux/device.h> @@ -16,6 +15,8 @@ #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> +#include <asm/unaligned.h> + #include <linux/iio/buffer.h> #include <linux/iio/sysfs.h> #include <linux/iio/trigger.h> diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index 821fee60a765..e90c299c913a 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -543,6 +543,8 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = { SCALE_HW_CALIB_DEFAULT) [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm", 0, SCALE_HW_CALIB_XOTHERM) + [ADC5_BAT_ID_100K_PU] = ADC5_CHAN_TEMP("bat_id", 0, + SCALE_HW_CALIB_DEFAULT) [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 0, SCALE_HW_CALIB_THERM_100K_PULLUP) [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 0, @@ -894,10 +896,8 @@ static int adc5_probe(struct platform_device *pdev) mutex_init(&adc->lock); ret = adc5_get_fw_data(adc); - if (ret) { - dev_err(dev, "adc get dt data failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "adc get dt data failed\n"); irq_eoc = platform_get_irq(pdev, 0); if (irq_eoc < 0) { diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c index a3d4de6ba4c2..0362df285a57 100644 --- a/drivers/iio/adc/stm32-dfsdm-core.c +++ b/drivers/iio/adc/stm32-dfsdm-core.c @@ -6,6 +6,7 @@ * Author(s): Arnaud Pouliquen <arnaud.pouliquen@st.com> for STMicroelectronics. */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -19,7 +20,15 @@ #include "stm32-dfsdm.h" +/** + * struct stm32_dfsdm_dev_data - DFSDM compatible configuration data + * @ipid: DFSDM identification number. Used only if hardware provides identification registers + * @num_filters: DFSDM number of filters. Unused if identification registers are available + * @num_channels: DFSDM number of channels. Unused if identification registers are available + * @regmap_cfg: SAI register map configuration pointer + */ struct stm32_dfsdm_dev_data { + u32 ipid; unsigned int num_filters; unsigned int num_channels; const struct regmap_config *regmap_cfg; @@ -27,8 +36,6 @@ struct stm32_dfsdm_dev_data { #define STM32H7_DFSDM_NUM_FILTERS 4 #define STM32H7_DFSDM_NUM_CHANNELS 8 -#define STM32MP1_DFSDM_NUM_FILTERS 6 -#define STM32MP1_DFSDM_NUM_CHANNELS 8 static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg) { @@ -75,8 +82,7 @@ static const struct regmap_config stm32mp1_dfsdm_regmap_cfg = { }; static const struct stm32_dfsdm_dev_data stm32mp1_dfsdm_data = { - .num_filters = STM32MP1_DFSDM_NUM_FILTERS, - .num_channels = STM32MP1_DFSDM_NUM_CHANNELS, + .ipid = STM32MP15_IPIDR_NUMBER, .regmap_cfg = &stm32mp1_dfsdm_regmap_cfg, }; @@ -295,6 +301,65 @@ static const struct of_device_id stm32_dfsdm_of_match[] = { }; MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match); +static int stm32_dfsdm_probe_identification(struct platform_device *pdev, + struct dfsdm_priv *priv, + const struct stm32_dfsdm_dev_data *dev_data) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *child; + struct stm32_dfsdm *dfsdm = &priv->dfsdm; + const char *compat; + int ret, count = 0; + u32 id, val; + + if (!dev_data->ipid) { + dfsdm->num_fls = dev_data->num_filters; + dfsdm->num_chs = dev_data->num_channels; + return 0; + } + + ret = regmap_read(dfsdm->regmap, DFSDM_IPIDR, &id); + if (ret) + return ret; + + if (id != dev_data->ipid) { + dev_err(&pdev->dev, "Unexpected IP version: 0x%x", id); + return -EINVAL; + } + + for_each_child_of_node(np, child) { + ret = of_property_read_string(child, "compatible", &compat); + if (ret) + continue; + /* Count only child nodes with dfsdm compatible */ + if (strstr(compat, "dfsdm")) + count++; + } + + ret = regmap_read(dfsdm->regmap, DFSDM_HWCFGR, &val); + if (ret) + return ret; + + dfsdm->num_fls = FIELD_GET(DFSDM_HWCFGR_NBF_MASK, val); + dfsdm->num_chs = FIELD_GET(DFSDM_HWCFGR_NBT_MASK, val); + + if (count > dfsdm->num_fls) { + dev_err(&pdev->dev, "Unexpected child number: %d", count); + return -EINVAL; + } + + ret = regmap_read(dfsdm->regmap, DFSDM_VERR, &val); + if (ret) + return ret; + + dev_dbg(&pdev->dev, "DFSDM version: %lu.%lu. %d channels/%d filters\n", + FIELD_GET(DFSDM_VERR_MAJREV_MASK, val), + FIELD_GET(DFSDM_VERR_MINREV_MASK, val), + dfsdm->num_chs, dfsdm->num_fls); + + return 0; +} + static int stm32_dfsdm_probe(struct platform_device *pdev) { struct dfsdm_priv *priv; @@ -311,18 +376,6 @@ static int stm32_dfsdm_probe(struct platform_device *pdev) dev_data = of_device_get_match_data(&pdev->dev); dfsdm = &priv->dfsdm; - dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters, - sizeof(*dfsdm->fl_list), GFP_KERNEL); - if (!dfsdm->fl_list) - return -ENOMEM; - - dfsdm->num_fls = dev_data->num_filters; - dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels, - sizeof(*dfsdm->ch_list), - GFP_KERNEL); - if (!dfsdm->ch_list) - return -ENOMEM; - dfsdm->num_chs = dev_data->num_channels; ret = stm32_dfsdm_parse_of(pdev, priv); if (ret < 0) @@ -338,6 +391,20 @@ static int stm32_dfsdm_probe(struct platform_device *pdev) return ret; } + ret = stm32_dfsdm_probe_identification(pdev, priv, dev_data); + if (ret < 0) + return ret; + + dfsdm->fl_list = devm_kcalloc(&pdev->dev, dfsdm->num_fls, + sizeof(*dfsdm->fl_list), GFP_KERNEL); + if (!dfsdm->fl_list) + return -ENOMEM; + + dfsdm->ch_list = devm_kcalloc(&pdev->dev, dfsdm->num_chs, + sizeof(*dfsdm->ch_list), GFP_KERNEL); + if (!dfsdm->ch_list) + return -ENOMEM; + platform_set_drvdata(pdev, dfsdm); ret = stm32_dfsdm_clk_prepare_enable(dfsdm); diff --git a/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h index 4afc1f528b78..570a1552aec4 100644 --- a/drivers/iio/adc/stm32-dfsdm.h +++ b/drivers/iio/adc/stm32-dfsdm.h @@ -13,25 +13,29 @@ /* * STM32 DFSDM - global register map - * ________________________________________________________ - * | Offset | Registers block | - * -------------------------------------------------------- - * | 0x000 | CHANNEL 0 + COMMON CHANNEL FIELDS | - * -------------------------------------------------------- - * | 0x020 | CHANNEL 1 | - * -------------------------------------------------------- - * | ... | ..... | - * -------------------------------------------------------- - * | 0x0E0 | CHANNEL 7 | - * -------------------------------------------------------- - * | 0x100 | FILTER 0 + COMMON FILTER FIELDs | - * -------------------------------------------------------- - * | 0x200 | FILTER 1 | - * -------------------------------------------------------- - * | 0x300 | FILTER 2 | - * -------------------------------------------------------- - * | 0x400 | FILTER 3 | - * -------------------------------------------------------- + * __________________________________________________________ + * | Offset | Registers block | + * ---------------------------------------------------------- + * | 0x000 | CHANNEL 0 + COMMON CHANNEL FIELDS | + * ---------------------------------------------------------- + * | 0x020 | CHANNEL 1 | + * ---------------------------------------------------------- + * | ... | ..... | + * ---------------------------------------------------------- + * | 0x20 x n | CHANNEL n | + * ---------------------------------------------------------- + * | 0x100 | FILTER 0 + COMMON FILTER FIELDs | + * ---------------------------------------------------------- + * | 0x200 | FILTER 1 | + * ---------------------------------------------------------- + * | | ..... | + * ---------------------------------------------------------- + * | 0x100 x m | FILTER m | + * ---------------------------------------------------------- + * | | ..... | + * ---------------------------------------------------------- + * | 0x7F0-7FC | Identification registers | + * ---------------------------------------------------------- */ /* @@ -231,6 +235,24 @@ #define DFSDM_AWCFR_AWHTF_MASK GENMASK(15, 8) #define DFSDM_AWCFR_AWHTF(v) FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v) +/* + * Identification register definitions + */ +#define DFSDM_HWCFGR 0x7F0 +#define DFSDM_VERR 0x7F4 +#define DFSDM_IPIDR 0x7F8 +#define DFSDM_SIDR 0x7FC + +/* HWCFGR: Hardware configuration register */ +#define DFSDM_HWCFGR_NBT_MASK GENMASK(7, 0) +#define DFSDM_HWCFGR_NBF_MASK GENMASK(15, 8) + +/* VERR: Version register */ +#define DFSDM_VERR_MINREV_MASK GENMASK(3, 0) +#define DFSDM_VERR_MAJREV_MASK GENMASK(7, 4) + +#define STM32MP15_IPIDR_NUMBER 0x00110031 + /* DFSDM filter order */ enum stm32_dfsdm_sinc_order { DFSDM_FASTSINC_ORDER, /* FastSinc filter type */ diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c index b3d5b9b7255b..a456ea78462f 100644 --- a/drivers/iio/adc/ti-adc128s052.c +++ b/drivers/iio/adc/ti-adc128s052.c @@ -9,14 +9,13 @@ * https://www.ti.com/lit/ds/symlink/adc124s021.pdf */ -#include <linux/acpi.h> #include <linux/err.h> -#include <linux/spi/spi.h> -#include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/iio/iio.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> #include <linux/property.h> #include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> struct adc128_configuration { const struct iio_chan_spec *channels; @@ -139,16 +138,11 @@ static void adc128_disable_regulator(void *reg) static int adc128_probe(struct spi_device *spi) { + const struct adc128_configuration *config; struct iio_dev *indio_dev; - unsigned int config; struct adc128 *adc; int ret; - if (dev_fwnode(&spi->dev)) - config = (unsigned long) device_get_match_data(&spi->dev); - else - config = spi_get_device_id(spi)->driver_data; - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc)); if (!indio_dev) return -ENOMEM; @@ -160,8 +154,10 @@ static int adc128_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &adc128_info; - indio_dev->channels = adc128_config[config].channels; - indio_dev->num_channels = adc128_config[config].num_channels; + config = spi_get_device_match_data(spi); + + indio_dev->channels = config->channels; + indio_dev->num_channels = config->num_channels; adc->reg = devm_regulator_get(&spi->dev, "vref"); if (IS_ERR(adc->reg)) @@ -181,42 +177,40 @@ static int adc128_probe(struct spi_device *spi) } static const struct of_device_id adc128_of_match[] = { - { .compatible = "ti,adc128s052", .data = (void*)0L, }, - { .compatible = "ti,adc122s021", .data = (void*)1L, }, - { .compatible = "ti,adc122s051", .data = (void*)1L, }, - { .compatible = "ti,adc122s101", .data = (void*)1L, }, - { .compatible = "ti,adc124s021", .data = (void*)2L, }, - { .compatible = "ti,adc124s051", .data = (void*)2L, }, - { .compatible = "ti,adc124s101", .data = (void*)2L, }, + { .compatible = "ti,adc128s052", .data = &adc128_config[0] }, + { .compatible = "ti,adc122s021", .data = &adc128_config[1] }, + { .compatible = "ti,adc122s051", .data = &adc128_config[1] }, + { .compatible = "ti,adc122s101", .data = &adc128_config[1] }, + { .compatible = "ti,adc124s021", .data = &adc128_config[2] }, + { .compatible = "ti,adc124s051", .data = &adc128_config[2] }, + { .compatible = "ti,adc124s101", .data = &adc128_config[2] }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, adc128_of_match); static const struct spi_device_id adc128_id[] = { - { "adc128s052", 0 }, /* index into adc128_config */ - { "adc122s021", 1 }, - { "adc122s051", 1 }, - { "adc122s101", 1 }, - { "adc124s021", 2 }, - { "adc124s051", 2 }, - { "adc124s101", 2 }, + { "adc128s052", (kernel_ulong_t)&adc128_config[0] }, + { "adc122s021", (kernel_ulong_t)&adc128_config[1] }, + { "adc122s051", (kernel_ulong_t)&adc128_config[1] }, + { "adc122s101", (kernel_ulong_t)&adc128_config[1] }, + { "adc124s021", (kernel_ulong_t)&adc128_config[2] }, + { "adc124s051", (kernel_ulong_t)&adc128_config[2] }, + { "adc124s101", (kernel_ulong_t)&adc128_config[2] }, { } }; MODULE_DEVICE_TABLE(spi, adc128_id); -#ifdef CONFIG_ACPI static const struct acpi_device_id adc128_acpi_match[] = { - { "AANT1280", 2 }, /* ADC124S021 compatible ACPI ID */ + { "AANT1280", (kernel_ulong_t)&adc128_config[2] }, { } }; MODULE_DEVICE_TABLE(acpi, adc128_acpi_match); -#endif static struct spi_driver adc128_driver = { .driver = { .name = "adc128s052", .of_match_table = adc128_of_match, - .acpi_match_table = ACPI_PTR(adc128_acpi_match), + .acpi_match_table = adc128_acpi_match, }, .probe = adc128_probe, .id_table = adc128_id, diff --git a/drivers/iio/adc/ti-ads7924.c b/drivers/iio/adc/ti-ads7924.c new file mode 100644 index 000000000000..b02abb026966 --- /dev/null +++ b/drivers/iio/adc/ti-ads7924.c @@ -0,0 +1,474 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * IIO driver for Texas Instruments ADS7924 ADC, 12-bit, 4-Channels, I2C + * + * Author: Hugo Villeneuve <hvilleneuve@dimonoff.com> + * Copyright 2022 DimOnOff + * + * based on iio/adc/ti-ads1015.c + * Copyright (c) 2016, Intel Corporation. + * + * Datasheet: https://www.ti.com/lit/gpn/ads7924 + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#include <linux/iio/iio.h> +#include <linux/iio/types.h> + +#define ADS7924_CHANNELS 4 +#define ADS7924_BITS 12 +#define ADS7924_DATA_SHIFT 4 + +/* Registers. */ +#define ADS7924_MODECNTRL_REG 0x00 +#define ADS7924_INTCNTRL_REG 0x01 +#define ADS7924_DATA0_U_REG 0x02 +#define ADS7924_DATA0_L_REG 0x03 +#define ADS7924_DATA1_U_REG 0x04 +#define ADS7924_DATA1_L_REG 0x05 +#define ADS7924_DATA2_U_REG 0x06 +#define ADS7924_DATA2_L_REG 0x07 +#define ADS7924_DATA3_U_REG 0x08 +#define ADS7924_DATA3_L_REG 0x09 +#define ADS7924_ULR0_REG 0x0A +#define ADS7924_LLR0_REG 0x0B +#define ADS7924_ULR1_REG 0x0C +#define ADS7924_LLR1_REG 0x0D +#define ADS7924_ULR2_REG 0x0E +#define ADS7924_LLR2_REG 0x0F +#define ADS7924_ULR3_REG 0x10 +#define ADS7924_LLR3_REG 0x11 +#define ADS7924_INTCONFIG_REG 0x12 +#define ADS7924_SLPCONFIG_REG 0x13 +#define ADS7924_ACQCONFIG_REG 0x14 +#define ADS7924_PWRCONFIG_REG 0x15 +#define ADS7924_RESET_REG 0x16 + +/* + * Register address INC bit: when set to '1', the register address is + * automatically incremented after every register read which allows convenient + * reading of multiple registers. Set INC to '0' when reading a single register. + */ +#define ADS7924_AUTO_INCREMENT_BIT BIT(7) + +#define ADS7924_MODECNTRL_MODE_MASK GENMASK(7, 2) + +#define ADS7924_MODECNTRL_SEL_MASK GENMASK(1, 0) + +#define ADS7924_CFG_INTPOL_BIT 1 +#define ADS7924_CFG_INTTRIG_BIT 0 + +#define ADS7924_CFG_INTPOL_MASK BIT(ADS7924_CFG_INTPOL_BIT) +#define ADS7924_CFG_INTTRIG_MASK BIT(ADS7924_CFG_INTTRIG_BIT) + +/* Interrupt pin polarity */ +#define ADS7924_CFG_INTPOL_LOW 0 +#define ADS7924_CFG_INTPOL_HIGH 1 + +/* Interrupt pin signaling */ +#define ADS7924_CFG_INTTRIG_LEVEL 0 +#define ADS7924_CFG_INTTRIG_EDGE 1 + +/* Mode control values */ +#define ADS7924_MODECNTRL_IDLE 0x00 +#define ADS7924_MODECNTRL_AWAKE 0x20 +#define ADS7924_MODECNTRL_MANUAL_SINGLE 0x30 +#define ADS7924_MODECNTRL_MANUAL_SCAN 0x32 +#define ADS7924_MODECNTRL_AUTO_SINGLE 0x31 +#define ADS7924_MODECNTRL_AUTO_SCAN 0x33 +#define ADS7924_MODECNTRL_AUTO_SINGLE_SLEEP 0x39 +#define ADS7924_MODECNTRL_AUTO_SCAN_SLEEP 0x3B +#define ADS7924_MODECNTRL_AUTO_BURST_SLEEP 0x3F + +#define ADS7924_ACQTIME_MASK GENMASK(4, 0) + +#define ADS7924_PWRUPTIME_MASK GENMASK(4, 0) + +/* + * The power-up time is allowed to elapse whenever the device has been shutdown + * in idle mode. Power-up time can allow external circuits, such as an + * operational amplifier, between the MUXOUT and ADCIN pins to turn on. + * The nominal time programmed by the PUTIME[4:0] register bits is given by: + * t PU = PWRUPTIME[4:0] × 2 μs + * If a power-up time is not required, set the bits to '0' to effectively bypass. + */ +#define ADS7924_PWRUPTIME_US 0 /* Bypass (0us). */ + +/* + * Acquisition Time according to ACQTIME[4:0] register bits. + * The Acquisition Time is given by: + * t ACQ = (ACQTIME[4:0] × 2 μs) + 6 μs + * Using default value of 0 for ACQTIME[4:0] results in a minimum acquisition + * time of 6us. + */ +#define ADS7924_ACQTIME_US 6 + +/* The conversion time is always 4μs and cannot be programmed by the user. */ +#define ADS7924_CONVTIME_US 4 + +#define ADS7924_TOTAL_CONVTIME_US (ADS7924_PWRUPTIME_US + ADS7924_ACQTIME_US + \ + ADS7924_CONVTIME_US) + +#define ADS7924_V_CHAN(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = _chan, \ + .address = _addr, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .datasheet_name = "AIN"#_chan, \ +} + +struct ads7924_data { + struct device *dev; + struct regmap *regmap; + struct regulator *vref_reg; + + /* GPIO descriptor for device hard-reset pin. */ + struct gpio_desc *reset_gpio; + + /* + * Protects ADC ops, e.g: concurrent sysfs/buffered + * data reads, configuration updates + */ + struct mutex lock; + + /* + * Set to true when the ADC is switched to the continuous-conversion + * mode and exits from a power-down state. This flag is used to avoid + * getting the stale result from the conversion register. + */ + bool conv_invalid; +}; + +static bool ads7924_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADS7924_MODECNTRL_REG: + case ADS7924_INTCNTRL_REG: + case ADS7924_ULR0_REG: + case ADS7924_LLR0_REG: + case ADS7924_ULR1_REG: + case ADS7924_LLR1_REG: + case ADS7924_ULR2_REG: + case ADS7924_LLR2_REG: + case ADS7924_ULR3_REG: + case ADS7924_LLR3_REG: + case ADS7924_INTCONFIG_REG: + case ADS7924_SLPCONFIG_REG: + case ADS7924_ACQCONFIG_REG: + case ADS7924_PWRCONFIG_REG: + case ADS7924_RESET_REG: + return true; + default: + return false; + } +} + +static const struct regmap_config ads7924_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = ADS7924_RESET_REG, + .writeable_reg = ads7924_is_writeable_reg, +}; + +static const struct iio_chan_spec ads7924_channels[] = { + ADS7924_V_CHAN(0, ADS7924_DATA0_U_REG), + ADS7924_V_CHAN(1, ADS7924_DATA1_U_REG), + ADS7924_V_CHAN(2, ADS7924_DATA2_U_REG), + ADS7924_V_CHAN(3, ADS7924_DATA3_U_REG), +}; + +static int ads7924_get_adc_result(struct ads7924_data *data, + struct iio_chan_spec const *chan, int *val) +{ + int ret; + __be16 be_val; + + if (chan->channel < 0 || chan->channel >= ADS7924_CHANNELS) + return -EINVAL; + + if (data->conv_invalid) { + int conv_time; + + conv_time = ADS7924_TOTAL_CONVTIME_US; + /* Allow 10% for internal clock inaccuracy. */ + conv_time += conv_time / 10; + usleep_range(conv_time, conv_time + 1); + data->conv_invalid = false; + } + + ret = regmap_raw_read(data->regmap, ADS7924_AUTO_INCREMENT_BIT | + chan->address, &be_val, sizeof(be_val)); + if (ret) + return ret; + + *val = be16_to_cpu(be_val) >> ADS7924_DATA_SHIFT; + + return 0; +} + +static int ads7924_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + int ret, vref_uv; + struct ads7924_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->lock); + ret = ads7924_get_adc_result(data, chan, val); + mutex_unlock(&data->lock); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + vref_uv = regulator_get_voltage(data->vref_reg); + if (vref_uv < 0) + return vref_uv; + + *val = vref_uv / 1000; /* Convert reg voltage to mV */ + *val2 = ADS7924_BITS; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } +} + +static const struct iio_info ads7924_info = { + .read_raw = ads7924_read_raw, +}; + +static int ads7924_get_channels_config(struct i2c_client *client, + struct iio_dev *indio_dev) +{ + struct ads7924_data *priv = iio_priv(indio_dev); + struct device *dev = priv->dev; + struct fwnode_handle *node; + int num_channels = 0; + + device_for_each_child_node(dev, node) { + u32 pval; + unsigned int channel; + + if (fwnode_property_read_u32(node, "reg", &pval)) { + dev_err(dev, "invalid reg on %pfw\n", node); + continue; + } + + channel = pval; + if (channel >= ADS7924_CHANNELS) { + dev_err(dev, "invalid channel index %d on %pfw\n", + channel, node); + continue; + } + + num_channels++; + } + + if (!num_channels) + return -EINVAL; + + return 0; +} + +static int ads7924_set_conv_mode(struct ads7924_data *data, int mode) +{ + int ret; + unsigned int mode_field; + struct device *dev = data->dev; + + /* + * When switching between modes, be sure to first select the Awake mode + * and then switch to the desired mode. This procedure ensures the + * internal control logic is properly synchronized. + */ + if (mode != ADS7924_MODECNTRL_IDLE) { + mode_field = FIELD_PREP(ADS7924_MODECNTRL_MODE_MASK, + ADS7924_MODECNTRL_AWAKE); + + ret = regmap_update_bits(data->regmap, ADS7924_MODECNTRL_REG, + ADS7924_MODECNTRL_MODE_MASK, + mode_field); + if (ret) { + dev_err(dev, "failed to set awake mode (%pe)\n", + ERR_PTR(ret)); + return ret; + } + } + + mode_field = FIELD_PREP(ADS7924_MODECNTRL_MODE_MASK, mode); + + ret = regmap_update_bits(data->regmap, ADS7924_MODECNTRL_REG, + ADS7924_MODECNTRL_MODE_MASK, mode_field); + if (ret) + dev_err(dev, "failed to set mode %d (%pe)\n", mode, + ERR_PTR(ret)); + + return ret; +} + +static int ads7924_reset(struct iio_dev *indio_dev) +{ + struct ads7924_data *data = iio_priv(indio_dev); + + if (data->reset_gpio) { + gpiod_set_value(data->reset_gpio, 1); /* Assert. */ + /* Educated guess: assert time not specified in datasheet... */ + mdelay(100); + gpiod_set_value(data->reset_gpio, 0); /* Deassert. */ + return 0; + } + + /* + * A write of 10101010 to this register will generate a + * software reset of the ADS7924. + */ + return regmap_write(data->regmap, ADS7924_RESET_REG, 0b10101010); +}; + +static void ads7924_reg_disable(void *data) +{ + regulator_disable(data); +} + +static void ads7924_set_idle_mode(void *data) +{ + ads7924_set_conv_mode(data, ADS7924_MODECNTRL_IDLE); +} + +static int ads7924_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct ads7924_data *data; + struct device *dev = &client->dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return dev_err_probe(dev, -ENOMEM, + "failed to allocate iio device\n"); + + data = iio_priv(indio_dev); + + data->dev = dev; + + /* Initialize the reset GPIO as output with an initial value of 0. */ + data->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(data->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(data->reset_gpio), + "failed to get request reset GPIO\n"); + + mutex_init(&data->lock); + + indio_dev->name = "ads7924"; + indio_dev->modes = INDIO_DIRECT_MODE; + + indio_dev->channels = ads7924_channels; + indio_dev->num_channels = ARRAY_SIZE(ads7924_channels); + indio_dev->info = &ads7924_info; + + ret = ads7924_get_channels_config(client, indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to get channels configuration\n"); + + data->regmap = devm_regmap_init_i2c(client, &ads7924_regmap_config); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "failed to init regmap\n"); + + data->vref_reg = devm_regulator_get(dev, "vref"); + if (IS_ERR(data->vref_reg)) + return dev_err_probe(dev, PTR_ERR(data->vref_reg), + "failed to get vref regulator\n"); + + ret = regulator_enable(data->vref_reg); + if (ret) + return dev_err_probe(dev, ret, + "failed to enable regulator\n"); + + ret = devm_add_action_or_reset(dev, ads7924_reg_disable, data->vref_reg); + if (ret) + return dev_err_probe(dev, ret, + "failed to add regulator disable action\n"); + + ret = ads7924_reset(indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to reset device\n"); + + ret = ads7924_set_conv_mode(data, ADS7924_MODECNTRL_AUTO_SCAN); + if (ret) + return dev_err_probe(dev, ret, + "failed to set conversion mode\n"); + + ret = devm_add_action_or_reset(dev, ads7924_set_idle_mode, data); + if (ret) + return dev_err_probe(dev, ret, + "failed to add idle mode action\n"); + + /* Use minimum signal acquire time. */ + ret = regmap_update_bits(data->regmap, ADS7924_ACQCONFIG_REG, + ADS7924_ACQTIME_MASK, + FIELD_PREP(ADS7924_ACQTIME_MASK, 0)); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to configure signal acquire time\n"); + + /* Disable power-up time. */ + ret = regmap_update_bits(data->regmap, ADS7924_PWRCONFIG_REG, + ADS7924_PWRUPTIME_MASK, + FIELD_PREP(ADS7924_PWRUPTIME_MASK, 0)); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to configure power-up time\n"); + + data->conv_invalid = true; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to register IIO device\n"); + + return 0; +} + +static const struct i2c_device_id ads7924_id[] = { + { "ads7924", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ads7924_id); + +static const struct of_device_id ads7924_of_match[] = { + { .compatible = "ti,ads7924", }, + {} +}; +MODULE_DEVICE_TABLE(of, ads7924_of_match); + +static struct i2c_driver ads7924_driver = { + .driver = { + .name = "ads7924", + .of_match_table = ads7924_of_match, + }, + .probe_new = ads7924_probe, + .id_table = ads7924_id, +}; + +module_i2c_driver(ads7924_driver); + +MODULE_AUTHOR("Hugo Villeneuve <hvilleneuve@dimonoff.com>"); +MODULE_DESCRIPTION("Texas Instruments ADS7924 ADC I2C driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ti-lmp92064.c b/drivers/iio/adc/ti-lmp92064.c new file mode 100644 index 000000000000..c30ed824924f --- /dev/null +++ b/drivers/iio/adc/ti-lmp92064.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments LMP92064 SPI ADC driver + * + * Copyright (c) 2022 Leonard Göhrs <kernel@pengutronix.de>, Pengutronix + * + * Based on linux/drivers/iio/adc/ti-tsc2046.c + * Copyright (c) 2021 Oleksij Rempel <kernel@pengutronix.de>, Pengutronix + */ + +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#include <linux/iio/iio.h> +#include <linux/iio/driver.h> + +#define TI_LMP92064_REG_CONFIG_A 0x0000 +#define TI_LMP92064_REG_CONFIG_B 0x0001 +#define TI_LMP92064_REG_CHIP_REV 0x0006 + +#define TI_LMP92064_REG_MFR_ID1 0x000C +#define TI_LMP92064_REG_MFR_ID2 0x000D + +#define TI_LMP92064_REG_REG_UPDATE 0x000F +#define TI_LMP92064_REG_CONFIG_REG 0x0100 +#define TI_LMP92064_REG_STATUS 0x0103 + +#define TI_LMP92064_REG_DATA_VOUT_LSB 0x0200 +#define TI_LMP92064_REG_DATA_VOUT_MSB 0x0201 +#define TI_LMP92064_REG_DATA_COUT_LSB 0x0202 +#define TI_LMP92064_REG_DATA_COUT_MSB 0x0203 + +#define TI_LMP92064_VAL_CONFIG_A 0x99 +#define TI_LMP92064_VAL_CONFIG_B 0x00 +#define TI_LMP92064_VAL_STATUS_OK 0x01 + +/* + * Channel number definitions for the two channels of the device + * - IN Current (INC) + * - IN Voltage (INV) + */ +#define TI_LMP92064_CHAN_INC 0 +#define TI_LMP92064_CHAN_INV 1 + +static const struct regmap_range lmp92064_readable_reg_ranges[] = { + regmap_reg_range(TI_LMP92064_REG_CONFIG_A, TI_LMP92064_REG_CHIP_REV), + regmap_reg_range(TI_LMP92064_REG_MFR_ID1, TI_LMP92064_REG_MFR_ID2), + regmap_reg_range(TI_LMP92064_REG_REG_UPDATE, TI_LMP92064_REG_REG_UPDATE), + regmap_reg_range(TI_LMP92064_REG_CONFIG_REG, TI_LMP92064_REG_CONFIG_REG), + regmap_reg_range(TI_LMP92064_REG_STATUS, TI_LMP92064_REG_STATUS), + regmap_reg_range(TI_LMP92064_REG_DATA_VOUT_LSB, TI_LMP92064_REG_DATA_COUT_MSB), +}; + +static const struct regmap_access_table lmp92064_readable_regs = { + .yes_ranges = lmp92064_readable_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(lmp92064_readable_reg_ranges), +}; + +static const struct regmap_range lmp92064_writable_reg_ranges[] = { + regmap_reg_range(TI_LMP92064_REG_CONFIG_A, TI_LMP92064_REG_CONFIG_B), + regmap_reg_range(TI_LMP92064_REG_REG_UPDATE, TI_LMP92064_REG_REG_UPDATE), + regmap_reg_range(TI_LMP92064_REG_CONFIG_REG, TI_LMP92064_REG_CONFIG_REG), +}; + +static const struct regmap_access_table lmp92064_writable_regs = { + .yes_ranges = lmp92064_writable_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(lmp92064_writable_reg_ranges), +}; + +static const struct regmap_config lmp92064_spi_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = TI_LMP92064_REG_DATA_COUT_MSB, + .rd_table = &lmp92064_readable_regs, + .wr_table = &lmp92064_writable_regs, +}; + +struct lmp92064_adc_priv { + int shunt_resistor_uohm; + struct spi_device *spi; + struct regmap *regmap; +}; + +static const struct iio_chan_spec lmp92064_adc_channels[] = { + { + .type = IIO_CURRENT, + .address = TI_LMP92064_CHAN_INC, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .datasheet_name = "INC", + }, + { + .type = IIO_VOLTAGE, + .address = TI_LMP92064_CHAN_INV, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .datasheet_name = "INV", + }, +}; + +static int lmp92064_read_meas(struct lmp92064_adc_priv *priv, u16 *res) +{ + __be16 raw[2]; + int ret; + + /* + * The ADC only latches in new samples if all DATA registers are read + * in descending sequential order. + * The ADC auto-decrements the register index with each clocked byte. + * Read both channels in single SPI transfer by selecting the highest + * register using the command below and clocking out all four data + * bytes. + */ + + ret = regmap_bulk_read(priv->regmap, TI_LMP92064_REG_DATA_COUT_MSB, + &raw, sizeof(raw)); + + if (ret) { + dev_err(&priv->spi->dev, "regmap_bulk_read failed: %pe\n", + ERR_PTR(ret)); + return ret; + } + + res[0] = be16_to_cpu(raw[0]); + res[1] = be16_to_cpu(raw[1]); + + return 0; +} + +static int lmp92064_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct lmp92064_adc_priv *priv = iio_priv(indio_dev); + u16 raw[2]; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = lmp92064_read_meas(priv, raw); + if (ret < 0) + return ret; + + *val = (chan->address == TI_LMP92064_CHAN_INC) ? raw[0] : raw[1]; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (chan->address == TI_LMP92064_CHAN_INC) { + /* + * processed (mA) = raw * current_lsb (mA) + * current_lsb (mA) = shunt_voltage_lsb (nV) / shunt_resistor (uOhm) + * shunt_voltage_lsb (nV) = 81920000 / 4096 = 20000 + */ + *val = 20000; + *val2 = priv->shunt_resistor_uohm; + } else { + /* + * processed (mV) = raw * voltage_lsb (mV) + * voltage_lsb (mV) = 2048 / 4096 + */ + *val = 2048; + *val2 = 4096; + } + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static int lmp92064_reset(struct lmp92064_adc_priv *priv, + struct gpio_desc *gpio_reset) +{ + unsigned int status; + int ret, i; + + if (gpio_reset) { + /* + * Perform a hard reset if gpio_reset is available. + * The datasheet specifies a very low 3.5ns reset pulse duration and does not + * specify how long to wait after a reset to access the device. + * Use more conservative pulse lengths to allow analog RC filtering of the + * reset line at the board level (as recommended in the datasheet). + */ + gpiod_set_value_cansleep(gpio_reset, 1); + usleep_range(1, 10); + gpiod_set_value_cansleep(gpio_reset, 0); + usleep_range(500, 750); + } else { + /* + * Perform a soft-reset if not. + * Also write default values to the config registers that are not + * affected by soft reset. + */ + ret = regmap_write(priv->regmap, TI_LMP92064_REG_CONFIG_A, + TI_LMP92064_VAL_CONFIG_A); + if (ret < 0) + return ret; + + ret = regmap_write(priv->regmap, TI_LMP92064_REG_CONFIG_B, + TI_LMP92064_VAL_CONFIG_B); + if (ret < 0) + return ret; + } + + /* + * Wait for the device to signal readiness to prevent reading bogus data + * and make sure device is actually connected. + * The datasheet does not specify how long this takes but usually it is + * not more than 3-4 iterations of this loop. + */ + for (i = 0; i < 10; i++) { + ret = regmap_read(priv->regmap, TI_LMP92064_REG_STATUS, &status); + if (ret < 0) + return ret; + + if (status == TI_LMP92064_VAL_STATUS_OK) + return 0; + + usleep_range(1000, 2000); + } + + /* + * No (correct) response received. + * Device is mostly likely not connected to the bus. + */ + return -ENXIO; +} + +static const struct iio_info lmp92064_adc_info = { + .read_raw = lmp92064_read_raw, +}; + +static int lmp92064_adc_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct lmp92064_adc_priv *priv; + struct gpio_desc *gpio_reset; + struct iio_dev *indio_dev; + u32 shunt_resistor_uohm; + struct regmap *regmap; + int ret; + + ret = spi_setup(spi); + if (ret < 0) + return dev_err_probe(dev, ret, "Error in SPI setup\n"); + + regmap = devm_regmap_init_spi(spi, &lmp92064_spi_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to set up SPI regmap\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + + priv->spi = spi; + priv->regmap = regmap; + + ret = device_property_read_u32(dev, "shunt-resistor-micro-ohms", + &shunt_resistor_uohm); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to get shunt-resistor value\n"); + + /* + * The shunt resistance is passed to userspace as the denominator of an iio + * fraction. Make sure it is in range for that. + */ + if (shunt_resistor_uohm == 0 || shunt_resistor_uohm > INT_MAX) { + dev_err(dev, "Shunt resistance is out of range\n"); + return -EINVAL; + } + + priv->shunt_resistor_uohm = shunt_resistor_uohm; + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return ret; + + ret = devm_regulator_get_enable(dev, "vdig"); + if (ret) + return ret; + + gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gpio_reset)) + return dev_err_probe(dev, PTR_ERR(gpio_reset), + "Failed to get GPIO reset pin\n"); + + ret = lmp92064_reset(priv, gpio_reset); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to reset device\n"); + + indio_dev->name = "lmp92064"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = lmp92064_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(lmp92064_adc_channels); + indio_dev->info = &lmp92064_adc_info; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct spi_device_id lmp92064_id_table[] = { + { "lmp92064" }, + {} +}; +MODULE_DEVICE_TABLE(spi, lmp92064_id_table); + +static const struct of_device_id lmp92064_of_table[] = { + { .compatible = "ti,lmp92064" }, + {} +}; +MODULE_DEVICE_TABLE(of, lmp92064_of_table); + +static struct spi_driver lmp92064_adc_driver = { + .driver = { + .name = "lmp92064", + .of_match_table = lmp92064_of_table, + }, + .probe = lmp92064_adc_probe, + .id_table = lmp92064_id_table, +}; +module_spi_driver(lmp92064_adc_driver); + +MODULE_AUTHOR("Leonard Göhrs <kernel@pengutronix.de>"); +MODULE_DESCRIPTION("TI LMP92064 ADC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c index a507d2e17079..34cf336b3490 100644 --- a/drivers/iio/adc/xilinx-ams.c +++ b/drivers/iio/adc/xilinx-ams.c @@ -1220,8 +1220,7 @@ static int ams_init_module(struct iio_dev *indio_dev, int num_channels = 0; int ret; - if (fwnode_property_match_string(fwnode, "compatible", - "xlnx,zynqmp-ams-ps") == 0) { + if (fwnode_device_is_compatible(fwnode, "xlnx,zynqmp-ams-ps")) { ams->ps_base = fwnode_iomap(fwnode, 0); if (!ams->ps_base) return -ENXIO; @@ -1232,8 +1231,7 @@ static int ams_init_module(struct iio_dev *indio_dev, /* add PS channels to iio device channels */ memcpy(channels, ams_ps_channels, sizeof(ams_ps_channels)); num_channels = ARRAY_SIZE(ams_ps_channels); - } else if (fwnode_property_match_string(fwnode, "compatible", - "xlnx,zynqmp-ams-pl") == 0) { + } else if (fwnode_device_is_compatible(fwnode, "xlnx,zynqmp-ams-pl")) { ams->pl_base = fwnode_iomap(fwnode, 0); if (!ams->pl_base) return -ENXIO; @@ -1247,8 +1245,7 @@ static int ams_init_module(struct iio_dev *indio_dev, num_channels += AMS_PL_MAX_FIXED_CHANNEL; num_channels = ams_get_ext_chan(fwnode, channels, num_channels); - } else if (fwnode_property_match_string(fwnode, "compatible", - "xlnx,zynqmp-ams") == 0) { + } else if (fwnode_device_is_compatible(fwnode, "xlnx,zynqmp-ams")) { /* add AMS channels to iio device channels */ memcpy(channels, ams_ctrl_channels, sizeof(ams_ctrl_channels)); num_channels += ARRAY_SIZE(ams_ctrl_channels); diff --git a/drivers/iio/cdc/ad7746.c b/drivers/iio/cdc/ad7746.c index 6f68651ce1d5..a1db5469f2d1 100644 --- a/drivers/iio/cdc/ad7746.c +++ b/drivers/iio/cdc/ad7746.c @@ -285,8 +285,7 @@ static int ad7746_select_channel(struct iio_dev *indio_dev, if (ret < 0) return ret; - if (chip->capdac_set != chan->channel) - chip->capdac_set = chan->channel; + chip->capdac_set = chan->channel; break; case IIO_VOLTAGE: case IIO_TEMP: diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index 682fca39d14d..7be5a45cf71a 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -354,7 +354,7 @@ static ssize_t sampling_frequency_available_show(struct device *dev, struct devi ssize_t len = 0; do { - len += scnprintf(buf + len, PAGE_SIZE - len, "0.%09u ", 1000000000 / i); + len += sysfs_emit_at(buf, len, "0.%09u ", 1000000000 / i); /* * Not all values fit PAGE_SIZE buffer hence print every 6th * (each frequency differs by 6s in time domain from the @@ -380,7 +380,7 @@ static ssize_t calibration_auto_enable_show(struct device *dev, struct device_at ret = scd30_command_read(state, CMD_ASC, &val); mutex_unlock(&state->lock); - return ret ?: sprintf(buf, "%d\n", val); + return ret ?: sysfs_emit(buf, "%d\n", val); } static ssize_t calibration_auto_enable_store(struct device *dev, struct device_attribute *attr, @@ -414,7 +414,7 @@ static ssize_t calibration_forced_value_show(struct device *dev, struct device_a ret = scd30_command_read(state, CMD_FRC, &val); mutex_unlock(&state->lock); - return ret ?: sprintf(buf, "%d\n", val); + return ret ?: sysfs_emit(buf, "%d\n", val); } static ssize_t calibration_forced_value_store(struct device *dev, struct device_attribute *attr, @@ -642,10 +642,8 @@ static int scd30_setup_trigger(struct iio_dev *indio_dev) trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, iio_device_id(indio_dev)); - if (!trig) { - dev_err(dev, "failed to allocate trigger\n"); - return -ENOMEM; - } + if (!trig) + return dev_err_probe(dev, -ENOMEM, "failed to allocate trigger\n"); trig->ops = &scd30_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); @@ -667,9 +665,9 @@ static int scd30_setup_trigger(struct iio_dev *indio_dev) IRQF_NO_AUTOEN, indio_dev->name, indio_dev); if (ret) - dev_err(dev, "failed to request irq\n"); + return dev_err_probe(dev, ret, "failed to request irq\n"); - return ret; + return 0; } int scd30_probe(struct device *dev, int irq, const char *name, void *priv, @@ -717,17 +715,13 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv, return ret; ret = scd30_reset(state); - if (ret) { - dev_err(dev, "failed to reset device: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to reset device\n"); if (state->irq > 0) { ret = scd30_setup_trigger(indio_dev); - if (ret) { - dev_err(dev, "failed to setup trigger: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to setup trigger\n"); } ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, scd30_trigger_handler, NULL); @@ -735,23 +729,17 @@ int scd30_probe(struct device *dev, int irq, const char *name, void *priv, return ret; ret = scd30_command_read(state, CMD_FW_VERSION, &val); - if (ret) { - dev_err(dev, "failed to read firmware version: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to read firmware version\n"); dev_info(dev, "firmware version: %d.%d\n", val >> 8, (char)val); ret = scd30_command_write(state, CMD_MEAS_INTERVAL, state->meas_interval); - if (ret) { - dev_err(dev, "failed to set measurement interval: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to set measurement interval\n"); ret = scd30_command_write(state, CMD_START_MEAS, state->pressure_comp); - if (ret) { - dev_err(dev, "failed to start measurement: %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "failed to start measurement\n"); ret = devm_add_action_or_reset(dev, scd30_stop_meas, state); if (ret) diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c index d92f7f651f7b..0c2caf3570db 100644 --- a/drivers/iio/common/scmi_sensors/scmi_iio.c +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c @@ -400,12 +400,12 @@ static ssize_t scmi_iio_get_raw_available(struct iio_dev *iio_dev, rem = do_div(resolution, int_pow(10, abs(exponent)) ); - len = scnprintf(buf, PAGE_SIZE, + len = sysfs_emit(buf, "[%lld %llu.%llu %lld]\n", min_range, resolution, rem, max_range); } else { resolution = resolution * int_pow(10, exponent); - len = scnprintf(buf, PAGE_SIZE, "[%lld %llu %lld]\n", + len = sysfs_emit(buf, "[%lld %llu %lld]\n", min_range, resolution, max_range); } } diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 80521bd28d0f..d3f90cf86143 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -162,10 +162,10 @@ config AD5696_I2C depends on I2C select AD5686 help - Say yes here to build support for Analog Devices AD5311R, AD5338R, - AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693, AD5693R, - AD5694, AD5694R, AD5695R, AD5696, and AD5696R Digital to Analog - converters. + Say yes here to build support for Analog Devices AD5311R, AD5337, + AD5338R, AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693, + AD5693R, AD5694, AD5694R, AD5695R, AD5696, and AD5696R Digital to + Analog converters. To compile this driver as a module, choose M here: the module will be called ad5696. @@ -357,6 +357,19 @@ config MAX517 This driver can also be built as a module. If so, the module will be called max517. +config MAX5522 + tristate "Maxim MAX5522 DAC driver" + depends on SPI_MASTER + select REGMAP_SPI + help + Say Y here if you want to build a driver for the Maxim MAX5522. + + MAX5522 is a dual, ultra-low-power, 10-Bit, voltage-output + digital to analog converter (DAC) offering rail-to-rail buffered + voltage outputs. + + If compiled as a module, it will be called max5522. + config MAX5821 tristate "Maxim MAX5821 DAC driver" depends on I2C diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index ec3e42713f00..6c74fea21736 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_LTC2632) += ltc2632.o obj-$(CONFIG_LTC2688) += ltc2688.o obj-$(CONFIG_M62332) += m62332.o obj-$(CONFIG_MAX517) += max517.o +obj-$(CONFIG_MAX5522) += max5522.o obj-$(CONFIG_MAX5821) += max5821.o obj-$(CONFIG_MCP4725) += mcp4725.o obj-$(CONFIG_MCP4922) += mcp4922.o diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 15361d8bbf94..57cc0f0eedc6 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -258,6 +258,7 @@ static const struct iio_chan_spec name[] = { \ DECLARE_AD5693_CHANNELS(ad5310r_channels, 10, 2); DECLARE_AD5693_CHANNELS(ad5311r_channels, 10, 6); +DECLARE_AD5338_CHANNELS(ad5337r_channels, 8, 8); DECLARE_AD5338_CHANNELS(ad5338r_channels, 10, 6); DECLARE_AD5676_CHANNELS(ad5672_channels, 12, 4); DECLARE_AD5679_CHANNELS(ad5674r_channels, 12, 4); @@ -283,6 +284,12 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { .num_channels = 1, .regmap_type = AD5693_REGMAP, }, + [ID_AD5337R] = { + .channels = ad5337r_channels, + .int_vref_mv = 2500, + .num_channels = 2, + .regmap_type = AD5686_REGMAP, + }, [ID_AD5338R] = { .channels = ad5338r_channels, .int_vref_mv = 2500, diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index b7ade3a6b9b6..760f852911df 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -54,6 +54,7 @@ enum ad5686_supported_device_ids { ID_AD5310R, ID_AD5311R, + ID_AD5337R, ID_AD5338R, ID_AD5671R, ID_AD5672R, diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index 160e80cf9135..8a95f0278018 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -72,6 +72,7 @@ static void ad5686_i2c_remove(struct i2c_client *i2c) static const struct i2c_device_id ad5686_i2c_id[] = { {"ad5311r", ID_AD5311R}, + {"ad5337r", ID_AD5337R}, {"ad5338r", ID_AD5338R}, {"ad5671r", ID_AD5671R}, {"ad5673r", ID_AD5673R}, @@ -92,6 +93,7 @@ MODULE_DEVICE_TABLE(i2c, ad5686_i2c_id); static const struct of_device_id ad5686_of_match[] = { { .compatible = "adi,ad5311r" }, + { .compatible = "adi,ad5337r" }, { .compatible = "adi,ad5338r" }, { .compatible = "adi,ad5671r" }, { .compatible = "adi,ad5675r" }, diff --git a/drivers/iio/dac/max5522.c b/drivers/iio/dac/max5522.c new file mode 100644 index 000000000000..00ba4e98fb9c --- /dev/null +++ b/drivers/iio/dac/max5522.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Maxim MAX5522 + * Dual, Ultra-Low-Power 10-Bit, Voltage-Output DACs + * + * Copyright 2022 Timesys Corp. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> + +#include <linux/iio/iio.h> + +#define MAX5522_MAX_ADDR 15 +#define MAX5522_CTRL_NONE 0 +#define MAX5522_CTRL_LOAD_IN_A 9 +#define MAX5522_CTRL_LOAD_IN_B 10 + +#define MAX5522_REG_DATA(x) ((x) + MAX5522_CTRL_LOAD_IN_A) + +struct max5522_chip_info { + const char *name; + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct max5522_state { + struct regmap *regmap; + const struct max5522_chip_info *chip_info; + unsigned short dac_cache[2]; + struct regulator *vrefin_reg; +}; + +#define MAX5522_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .output = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 10, \ + .storagebits = 16, \ + .shift = 2, \ + } \ +} + +const struct iio_chan_spec max5522_channels[] = { + MAX5522_CHANNEL(0), + MAX5522_CHANNEL(1), +}; + +enum max5522_type { + ID_MAX5522, +}; + +static const struct max5522_chip_info max5522_chip_info_tbl[] = { + [ID_MAX5522] = { + .name = "max5522", + .channels = max5522_channels, + .num_channels = 2, + }, +}; + +static inline int max5522_info_to_reg(struct iio_chan_spec const *chan) +{ + return MAX5522_REG_DATA(chan->channel); +} + +static int max5522_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct max5522_state *state = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + *val = state->dac_cache[chan->channel]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(state->vrefin_reg); + if (ret < 0) + return -EINVAL; + *val = ret / 1000; + *val2 = 10; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + + return -EINVAL; +} + +static int max5522_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct max5522_state *state = iio_priv(indio_dev); + int rval; + + if (val > 1023 || val < 0) + return -EINVAL; + + rval = regmap_write(state->regmap, max5522_info_to_reg(chan), + val << chan->scan_type.shift); + if (rval < 0) + return rval; + + state->dac_cache[chan->channel] = val; + + return 0; +} + +static const struct iio_info max5522_info = { + .read_raw = max5522_read_raw, + .write_raw = max5522_write_raw, +}; + +static const struct regmap_config max5522_regmap_config = { + .reg_bits = 4, + .val_bits = 12, + .max_register = MAX5522_MAX_ADDR, +}; + +static int max5522_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct iio_dev *indio_dev; + struct max5522_state *state; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); + if (indio_dev == NULL) { + dev_err(&spi->dev, "failed to allocate iio device\n"); + return -ENOMEM; + } + + state = iio_priv(indio_dev); + state->chip_info = device_get_match_data(&spi->dev); + if (!state->chip_info) { + state->chip_info = + (struct max5522_chip_info *)(id->driver_data); + if (!state->chip_info) + return -EINVAL; + } + + state->vrefin_reg = devm_regulator_get(&spi->dev, "vrefin"); + if (IS_ERR(state->vrefin_reg)) + return dev_err_probe(&spi->dev, PTR_ERR(state->vrefin_reg), + "Vrefin regulator not specified\n"); + + ret = regulator_enable(state->vrefin_reg); + if (ret) { + return dev_err_probe(&spi->dev, ret, + "Failed to enable vref regulators\n"); + } + + state->regmap = devm_regmap_init_spi(spi, &max5522_regmap_config); + + if (IS_ERR(state->regmap)) + return PTR_ERR(state->regmap); + + indio_dev->info = &max5522_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max5522_channels; + indio_dev->num_channels = ARRAY_SIZE(max5522_channels); + indio_dev->name = max5522_chip_info_tbl[ID_MAX5522].name; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id max5522_ids[] = { + { "max5522", (kernel_ulong_t)&max5522_chip_info_tbl[ID_MAX5522] }, + {} +}; +MODULE_DEVICE_TABLE(spi, max5522_ids); + +static const struct of_device_id max5522_of_match[] = { + { + .compatible = "maxim,max5522", + .data = &max5522_chip_info_tbl[ID_MAX5522], + }, + {} +}; +MODULE_DEVICE_TABLE(of, max5522_of_match); + +static struct spi_driver max5522_spi_driver = { + .driver = { + .name = "max5522", + .of_match_table = max5522_of_match, + }, + .probe = max5522_spi_probe, + .id_table = max5522_ids, +}; +module_spi_driver(max5522_spi_driver); + +MODULE_AUTHOR("Angelo Dureghello <angelo.dureghello@timesys.com"); +MODULE_DESCRIPTION("MAX5522 DAC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/imu/bno055/bno055_ser_trace.c b/drivers/iio/imu/bno055/bno055_ser_trace.c index 48397b66daef..ab564186d19c 100644 --- a/drivers/iio/imu/bno055/bno055_ser_trace.c +++ b/drivers/iio/imu/bno055/bno055_ser_trace.c @@ -1,4 +1,4 @@ -//SPDX-License-Identifier: GPL-2.0 +// SPDX-License-Identifier: GPL-2.0 /* * bno055_ser Trace Support diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index e692dfeeda44..53ba020fa5d0 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -649,7 +649,7 @@ static int kmx61_chip_update_thresholds(struct kmx61_data *data) KMX61_REG_WUF_TIMER, data->wake_duration); if (ret < 0) { - dev_err(&data->client->dev, "Errow writing reg_wuf_timer\n"); + dev_err(&data->client->dev, "Error writing reg_wuf_timer\n"); return ret; } diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 5b6f195748fc..499fcf8875b4 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -93,7 +93,7 @@ enum st_lsm6dsx_hw_id { .endianness = IIO_LE, \ }, \ .event_spec = &st_lsm6dsx_event, \ - .ext_info = st_lsm6dsx_accel_ext_info, \ + .ext_info = st_lsm6dsx_ext_info, \ .num_event_specs = 1, \ } @@ -113,6 +113,7 @@ enum st_lsm6dsx_hw_id { .storagebits = 16, \ .endianness = IIO_LE, \ }, \ + .ext_info = st_lsm6dsx_ext_info, \ } struct st_lsm6dsx_reg { @@ -528,7 +529,7 @@ st_lsm6dsx_device_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable) } static const -struct iio_chan_spec_ext_info __maybe_unused st_lsm6dsx_accel_ext_info[] = { +struct iio_chan_spec_ext_info __maybe_unused st_lsm6dsx_ext_info[] = { IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, st_lsm6dsx_get_mount_matrix), { } }; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c index f2b64b4956a3..c1b444520d2a 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c @@ -704,18 +704,18 @@ static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev, static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_shub_sampling_freq_avail); static IIO_DEVICE_ATTR(in_scale_available, 0444, st_lsm6dsx_shub_scale_avail, NULL, 0); -static struct attribute *st_lsm6dsx_ext_attributes[] = { +static struct attribute *st_lsm6dsx_shub_attributes[] = { &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_scale_available.dev_attr.attr, NULL, }; -static const struct attribute_group st_lsm6dsx_ext_attribute_group = { - .attrs = st_lsm6dsx_ext_attributes, +static const struct attribute_group st_lsm6dsx_shub_attribute_group = { + .attrs = st_lsm6dsx_shub_attributes, }; -static const struct iio_info st_lsm6dsx_ext_info = { - .attrs = &st_lsm6dsx_ext_attribute_group, +static const struct iio_info st_lsm6dsx_shub_info = { + .attrs = &st_lsm6dsx_shub_attribute_group, .read_raw = st_lsm6dsx_shub_read_raw, .write_raw = st_lsm6dsx_shub_write_raw, .hwfifo_set_watermark = st_lsm6dsx_set_watermark, @@ -737,7 +737,7 @@ st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw, return NULL; iio_dev->modes = INDIO_DIRECT_MODE; - iio_dev->info = &st_lsm6dsx_ext_info; + iio_dev->info = &st_lsm6dsx_shub_info; sensor = iio_priv(iio_dev); sensor->id = id; diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 52e690f031cb..c117f50d0cf3 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -8,30 +8,32 @@ #define pr_fmt(fmt) "iio-core: " fmt -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/idr.h> -#include <linux/kdev_t.h> -#include <linux/err.h> +#include <linux/anon_inodes.h> +#include <linux/cdev.h> +#include <linux/debugfs.h> #include <linux/device.h> +#include <linux/err.h> #include <linux/fs.h> +#include <linux/idr.h> +#include <linux/kdev_t.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> #include <linux/poll.h> #include <linux/property.h> #include <linux/sched.h> -#include <linux/wait.h> -#include <linux/cdev.h> #include <linux/slab.h> -#include <linux/anon_inodes.h> -#include <linux/debugfs.h> -#include <linux/mutex.h> -#include <linux/iio/iio.h> +#include <linux/wait.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/buffer_impl.h> +#include <linux/iio/events.h> #include <linux/iio/iio-opaque.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + #include "iio_core.h" #include "iio_core_trigger.h" -#include <linux/iio/sysfs.h> -#include <linux/iio/events.h> -#include <linux/iio/buffer.h> -#include <linux/iio/buffer_impl.h> /* IDA to assign each registered device a unique id */ static DEFINE_IDA(iio_ida); @@ -205,36 +207,6 @@ bool iio_buffer_enabled(struct iio_dev *indio_dev) } EXPORT_SYMBOL_GPL(iio_buffer_enabled); -/** - * iio_sysfs_match_string_with_gaps - matches given string in an array with gaps - * @array: array of strings - * @n: number of strings in the array - * @str: string to match with - * - * Returns index of @str in the @array or -EINVAL, similar to match_string(). - * Uses sysfs_streq instead of strcmp for matching. - * - * This routine will look for a string in an array of strings. - * The search will continue until the element is found or the n-th element - * is reached, regardless of any NULL elements in the array. - */ -static int iio_sysfs_match_string_with_gaps(const char * const *array, size_t n, - const char *str) -{ - const char *item; - int index; - - for (index = 0; index < n; index++) { - item = array[index]; - if (!item) - continue; - if (sysfs_streq(item, str)) - return index; - } - - return -EINVAL; -} - #if defined(CONFIG_DEBUG_FS) /* * There's also a CONFIG_DEBUG_FS guard in include/linux/iio/iio.h for @@ -569,7 +541,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, if (!e->set) return -EINVAL; - ret = iio_sysfs_match_string_with_gaps(e->items, e->num_items, buf); + ret = __sysfs_match_string(e->items, e->num_items, buf); if (ret < 0) return ret; diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 6f23817fae6f..d74d2b5ff14c 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -39,7 +39,6 @@ obj-$(CONFIG_NOA1305) += noa1305.o obj-$(CONFIG_OPT3001) += opt3001.o obj-$(CONFIG_PA12203001) += pa12203001.o obj-$(CONFIG_RPR0521) += rpr0521.o -obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o obj-$(CONFIG_SI1133) += si1133.o obj-$(CONFIG_SI1145) += si1145.o obj-$(CONFIG_STK3310) += stk3310.o @@ -48,6 +47,7 @@ obj-$(CONFIG_ST_UVIS25_I2C) += st_uvis25_i2c.o obj-$(CONFIG_ST_UVIS25_SPI) += st_uvis25_spi.o obj-$(CONFIG_TCS3414) += tcs3414.o obj-$(CONFIG_TCS3472) += tcs3472.o +obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o obj-$(CONFIG_TSL2583) += tsl2583.o obj-$(CONFIG_TSL2591) += tsl2591.o obj-$(CONFIG_TSL2772) += tsl2772.o diff --git a/drivers/iio/light/max44009.c b/drivers/iio/light/max44009.c index 801e5a0ad496..3dadace09fe2 100644 --- a/drivers/iio/light/max44009.c +++ b/drivers/iio/light/max44009.c @@ -487,8 +487,7 @@ static irqreturn_t max44009_threaded_irq_handler(int irq, void *p) return IRQ_NONE; } -static int max44009_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int max44009_probe(struct i2c_client *client) { struct max44009_data *data; struct iio_dev *indio_dev; @@ -538,7 +537,7 @@ static struct i2c_driver max44009_driver = { .driver = { .name = MAX44009_DRV_NAME, }, - .probe = max44009_probe, + .probe_new = max44009_probe, .id_table = max44009_id, }; module_i2c_driver(max44009_driver); diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index d0e42b73203a..f2f55239a072 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -11,69 +11,63 @@ * Amit Kucheria <amit.kucheria@verdurent.com> */ -#include <linux/module.h> -#include <linux/mod_devicetable.h> -#include <linux/property.h> +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/err.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/irq.h> -#include <linux/sched.h> +#include <linux/math.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> #include <linux/mutex.h> -#include <linux/delay.h> #include <linux/pm.h> -#include <linux/err.h> +#include <linux/property.h> +#include <linux/sched.h> #include <linux/slab.h> +#include <linux/iio/events.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#include <linux/iio/events.h> -#include <linux/platform_data/tsl2563.h> /* Use this many bits for fraction part. */ #define ADC_FRAC_BITS 14 /* Given number of 1/10000's in ADC_FRAC_BITS precision. */ -#define FRAC10K(f) (((f) * (1L << (ADC_FRAC_BITS))) / (10000)) +#define FRAC10K(f) (((f) * BIT(ADC_FRAC_BITS)) / (10000)) /* Bits used for fraction in calibration coefficients.*/ #define CALIB_FRAC_BITS 10 -/* 0.5 in CALIB_FRAC_BITS precision */ -#define CALIB_FRAC_HALF (1 << (CALIB_FRAC_BITS - 1)) -/* Make a fraction from a number n that was multiplied with b. */ -#define CALIB_FRAC(n, b) (((n) << CALIB_FRAC_BITS) / (b)) /* Decimal 10^(digits in sysfs presentation) */ #define CALIB_BASE_SYSFS 1000 -#define TSL2563_CMD 0x80 -#define TSL2563_CLEARINT 0x40 +#define TSL2563_CMD BIT(7) +#define TSL2563_CLEARINT BIT(6) #define TSL2563_REG_CTRL 0x00 #define TSL2563_REG_TIMING 0x01 -#define TSL2563_REG_LOWLOW 0x02 /* data0 low threshold, 2 bytes */ -#define TSL2563_REG_LOWHIGH 0x03 -#define TSL2563_REG_HIGHLOW 0x04 /* data0 high threshold, 2 bytes */ -#define TSL2563_REG_HIGHHIGH 0x05 +#define TSL2563_REG_LOW 0x02 /* data0 low threshold, 2 bytes */ +#define TSL2563_REG_HIGH 0x04 /* data0 high threshold, 2 bytes */ #define TSL2563_REG_INT 0x06 #define TSL2563_REG_ID 0x0a -#define TSL2563_REG_DATA0LOW 0x0c /* broadband sensor value, 2 bytes */ -#define TSL2563_REG_DATA0HIGH 0x0d -#define TSL2563_REG_DATA1LOW 0x0e /* infrared sensor value, 2 bytes */ -#define TSL2563_REG_DATA1HIGH 0x0f +#define TSL2563_REG_DATA0 0x0c /* broadband sensor value, 2 bytes */ +#define TSL2563_REG_DATA1 0x0e /* infrared sensor value, 2 bytes */ #define TSL2563_CMD_POWER_ON 0x03 #define TSL2563_CMD_POWER_OFF 0x00 -#define TSL2563_CTRL_POWER_MASK 0x03 +#define TSL2563_CTRL_POWER_MASK GENMASK(1, 0) #define TSL2563_TIMING_13MS 0x00 #define TSL2563_TIMING_100MS 0x01 #define TSL2563_TIMING_400MS 0x02 -#define TSL2563_TIMING_MASK 0x03 +#define TSL2563_TIMING_MASK GENMASK(1, 0) #define TSL2563_TIMING_GAIN16 0x10 #define TSL2563_TIMING_GAIN1 0x00 #define TSL2563_INT_DISABLED 0x00 #define TSL2563_INT_LEVEL 0x10 -#define TSL2563_INT_PERSIST(n) ((n) & 0x0F) +#define TSL2563_INT_MASK GENMASK(5, 4) +#define TSL2563_INT_PERSIST(n) ((n) & GENMASK(3, 0)) struct tsl2563_gainlevel_coeff { u8 gaintime; @@ -161,24 +155,16 @@ static int tsl2563_configure(struct tsl2563_chip *chip) chip->gainlevel->gaintime); if (ret) goto error_ret; - ret = i2c_smbus_write_byte_data(chip->client, - TSL2563_CMD | TSL2563_REG_HIGHLOW, - chip->high_thres & 0xFF); - if (ret) - goto error_ret; - ret = i2c_smbus_write_byte_data(chip->client, - TSL2563_CMD | TSL2563_REG_HIGHHIGH, - (chip->high_thres >> 8) & 0xFF); + ret = i2c_smbus_write_word_data(chip->client, + TSL2563_CMD | TSL2563_REG_HIGH, + chip->high_thres); if (ret) goto error_ret; - ret = i2c_smbus_write_byte_data(chip->client, - TSL2563_CMD | TSL2563_REG_LOWLOW, - chip->low_thres & 0xFF); + ret = i2c_smbus_write_word_data(chip->client, + TSL2563_CMD | TSL2563_REG_LOW, + chip->low_thres); if (ret) goto error_ret; - ret = i2c_smbus_write_byte_data(chip->client, - TSL2563_CMD | TSL2563_REG_LOWHIGH, - (chip->low_thres >> 8) & 0xFF); /* * Interrupt register is automatically written anyway if it is relevant * so is not here. @@ -223,6 +209,24 @@ static int tsl2563_read_id(struct tsl2563_chip *chip, u8 *id) return 0; } +static int tsl2563_configure_irq(struct tsl2563_chip *chip, bool enable) +{ + int ret; + + chip->intr &= ~TSL2563_INT_MASK; + if (enable) + chip->intr |= TSL2563_INT_LEVEL; + + ret = i2c_smbus_write_byte_data(chip->client, + TSL2563_CMD | TSL2563_REG_INT, + chip->intr); + if (ret < 0) + return ret; + + chip->int_enabled = enable; + return 0; +} + /* * "Normalized" ADC value is one obtained with 400ms of integration time and * 16x gain. This function returns the number of bits of shift needed to @@ -325,13 +329,13 @@ static int tsl2563_get_adc(struct tsl2563_chip *chip) while (retry) { ret = i2c_smbus_read_word_data(client, - TSL2563_CMD | TSL2563_REG_DATA0LOW); + TSL2563_CMD | TSL2563_REG_DATA0); if (ret < 0) goto out; adc0 = ret; ret = i2c_smbus_read_word_data(client, - TSL2563_CMD | TSL2563_REG_DATA1LOW); + TSL2563_CMD | TSL2563_REG_DATA1); if (ret < 0) goto out; adc1 = ret; @@ -352,12 +356,12 @@ out: static inline int tsl2563_calib_to_sysfs(u32 calib) { - return (int) (((calib * CALIB_BASE_SYSFS) + - CALIB_FRAC_HALF) >> CALIB_FRAC_BITS); + return (int)DIV_ROUND_CLOSEST(calib * CALIB_BASE_SYSFS, BIT(CALIB_FRAC_BITS)); } static inline u32 tsl2563_calib_from_sysfs(int value) { + /* Make a fraction from a number n that was multiplied with b. */ return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS; } @@ -584,20 +588,18 @@ static int tsl2563_write_thresh(struct iio_dev *indio_dev, { struct tsl2563_chip *chip = iio_priv(indio_dev); int ret; - u8 address; + + mutex_lock(&chip->lock); if (dir == IIO_EV_DIR_RISING) - address = TSL2563_REG_HIGHLOW; + ret = i2c_smbus_write_word_data(chip->client, + TSL2563_CMD | TSL2563_REG_HIGH, val); else - address = TSL2563_REG_LOWLOW; - mutex_lock(&chip->lock); - ret = i2c_smbus_write_byte_data(chip->client, TSL2563_CMD | address, - val & 0xFF); + ret = i2c_smbus_write_word_data(chip->client, + TSL2563_CMD | TSL2563_REG_LOW, val); if (ret) goto error_ret; - ret = i2c_smbus_write_byte_data(chip->client, - TSL2563_CMD | (address + 1), - (val >> 8) & 0xFF); + if (dir == IIO_EV_DIR_RISING) chip->high_thres = val; else @@ -634,9 +636,7 @@ static int tsl2563_write_interrupt_config(struct iio_dev *indio_dev, int ret = 0; mutex_lock(&chip->lock); - if (state && !(chip->intr & 0x30)) { - chip->intr &= ~0x30; - chip->intr |= 0x10; + if (state && !(chip->intr & TSL2563_INT_MASK)) { /* ensure the chip is actually on */ cancel_delayed_work_sync(&chip->poweroff_work); if (!tsl2563_get_power(chip)) { @@ -647,18 +647,11 @@ static int tsl2563_write_interrupt_config(struct iio_dev *indio_dev, if (ret) goto out; } - ret = i2c_smbus_write_byte_data(chip->client, - TSL2563_CMD | TSL2563_REG_INT, - chip->intr); - chip->int_enabled = true; + ret = tsl2563_configure_irq(chip, true); } - if (!state && (chip->intr & 0x30)) { - chip->intr &= ~0x30; - ret = i2c_smbus_write_byte_data(chip->client, - TSL2563_CMD | TSL2563_REG_INT, - chip->intr); - chip->int_enabled = false; + if (!state && (chip->intr & TSL2563_INT_MASK)) { + ret = tsl2563_configure_irq(chip, false); /* now the interrupt is not enabled, we can go to sleep */ schedule_delayed_work(&chip->poweroff_work, 5 * HZ); } @@ -682,7 +675,7 @@ static int tsl2563_read_interrupt_config(struct iio_dev *indio_dev, if (ret < 0) return ret; - return !!(ret & 0x30); + return !!(ret & TSL2563_INT_MASK); } static const struct iio_info tsl2563_info_no_irq = { @@ -701,13 +694,14 @@ static const struct iio_info tsl2563_info = { static int tsl2563_probe(struct i2c_client *client) { + struct device *dev = &client->dev; struct iio_dev *indio_dev; struct tsl2563_chip *chip; - struct tsl2563_platform_data *pdata = client->dev.platform_data; - int err = 0; + unsigned long irq_flags; u8 id = 0; + int err; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*chip)); if (!indio_dev) return -ENOMEM; @@ -717,16 +711,12 @@ static int tsl2563_probe(struct i2c_client *client) chip->client = client; err = tsl2563_detect(chip); - if (err) { - dev_err(&client->dev, "detect error %d\n", -err); - return err; - } + if (err) + return dev_err_probe(dev, err, "detect error\n"); err = tsl2563_read_id(chip, &id); - if (err) { - dev_err(&client->dev, "read id error %d\n", -err); - return err; - } + if (err) + return dev_err_probe(dev, err, "read id error\n"); mutex_init(&chip->lock); @@ -738,16 +728,10 @@ static int tsl2563_probe(struct i2c_client *client) chip->calib0 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS); chip->calib1 = tsl2563_calib_from_sysfs(CALIB_BASE_SYSFS); - if (pdata) { - chip->cover_comp_gain = pdata->cover_comp_gain; - } else { - err = device_property_read_u32(&client->dev, "amstaos,cover-comp-gain", - &chip->cover_comp_gain); - if (err) - chip->cover_comp_gain = 1; - } + chip->cover_comp_gain = 1; + device_property_read_u32(dev, "amstaos,cover-comp-gain", &chip->cover_comp_gain); - dev_info(&client->dev, "model %d, rev. %d\n", id >> 4, id & 0x0f); + dev_info(dev, "model %d, rev. %d\n", id >> 4, id & 0x0f); indio_dev->name = client->name; indio_dev->channels = tsl2563_channels; indio_dev->num_channels = ARRAY_SIZE(tsl2563_channels); @@ -759,23 +743,24 @@ static int tsl2563_probe(struct i2c_client *client) indio_dev->info = &tsl2563_info_no_irq; if (client->irq) { - err = devm_request_threaded_irq(&client->dev, client->irq, + irq_flags = irq_get_trigger_type(client->irq); + if (irq_flags == IRQF_TRIGGER_NONE) + irq_flags = IRQF_TRIGGER_RISING; + irq_flags |= IRQF_ONESHOT; + + err = devm_request_threaded_irq(dev, client->irq, NULL, &tsl2563_event_handler, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, + irq_flags, "tsl2563_event", indio_dev); - if (err) { - dev_err(&client->dev, "irq request error %d\n", -err); - return err; - } + if (err) + return dev_err_probe(dev, err, "irq request error\n"); } err = tsl2563_configure(chip); - if (err) { - dev_err(&client->dev, "configure error %d\n", -err); - return err; - } + if (err) + return dev_err_probe(dev, err, "configure error\n"); INIT_DELAYED_WORK(&chip->poweroff_work, tsl2563_poweroff_work); @@ -784,7 +769,7 @@ static int tsl2563_probe(struct i2c_client *client) err = iio_device_register(indio_dev); if (err) { - dev_err(&client->dev, "iio registration error %d\n", -err); + dev_err_probe(dev, err, "iio registration error\n"); goto fail; } @@ -804,15 +789,13 @@ static void tsl2563_remove(struct i2c_client *client) if (!chip->int_enabled) cancel_delayed_work_sync(&chip->poweroff_work); /* Ensure that interrupts are disabled - then flush any bottom halves */ - chip->intr &= ~0x30; - i2c_smbus_write_byte_data(chip->client, TSL2563_CMD | TSL2563_REG_INT, - chip->intr); + tsl2563_configure_irq(chip, false); tsl2563_set_power(chip, 0); } static int tsl2563_suspend(struct device *dev) { - struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct tsl2563_chip *chip = iio_priv(indio_dev); int ret; @@ -831,7 +814,7 @@ out: static int tsl2563_resume(struct device *dev) { - struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct tsl2563_chip *chip = iio_priv(indio_dev); int ret; diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index cc1a2062e76d..6bdfce9747f9 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -60,8 +60,11 @@ #define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */ #define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */ +#define VCNL4040_PS_THDL_LM 0x06 /* Proximity threshold low */ +#define VCNL4040_PS_THDH_LM 0x07 /* Proximity threshold high */ #define VCNL4200_PS_DATA 0x08 /* Proximity data */ #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ +#define VCNL4040_INT_FLAGS 0x0b /* Interrupt register */ #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ @@ -78,6 +81,9 @@ #define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0) #define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0) #define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */ +#define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */ +#define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */ +#define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */ /* Bit masks for interrupt registers. */ #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ @@ -138,6 +144,7 @@ struct vcnl4000_data { enum vcnl4000_device_ids id; int rev; int al_scale; + u8 ps_int; /* proximity interrupt mode */ const struct vcnl4000_chip_spec *chip_spec; struct mutex vcnl4000_lock; struct vcnl4200_channel vcnl4200_al; @@ -150,11 +157,13 @@ struct vcnl4000_chip_spec { struct iio_chan_spec const *channels; const int num_channels; const struct iio_info *info; - bool irq_support; + const struct iio_buffer_setup_ops *buffer_setup_ops; int (*init)(struct vcnl4000_data *data); int (*measure_light)(struct vcnl4000_data *data, int *val); int (*measure_proximity)(struct vcnl4000_data *data, int *val); int (*set_power_state)(struct vcnl4000_data *data, bool on); + irqreturn_t (*irq_thread)(int irq, void *priv); + irqreturn_t (*trig_buffer_func)(int irq, void *priv); }; static const struct i2c_device_id vcnl4000_id[] = { @@ -254,6 +263,10 @@ static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) { int ret; + /* Do not power down if interrupts are enabled */ + if (!on && data->ps_int) + return 0; + ret = vcnl4000_write_als_enable(data, on); if (ret < 0) return ret; @@ -295,6 +308,7 @@ static int vcnl4200_init(struct vcnl4000_data *data) dev_dbg(&data->client->dev, "device id 0x%x", id); data->rev = (ret >> 8) & 0xf; + data->ps_int = 0; data->vcnl4200_al.reg = VCNL4200_AL_DATA; data->vcnl4200_ps.reg = VCNL4200_PS_DATA; @@ -795,6 +809,64 @@ static int vcnl4010_write_event(struct iio_dev *indio_dev, } } +static int vcnl4040_read_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + int ret; + struct vcnl4000_data *data = iio_priv(indio_dev); + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = i2c_smbus_read_word_data(data->client, + VCNL4040_PS_THDH_LM); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + ret = i2c_smbus_read_word_data(data->client, + VCNL4040_PS_THDL_LM); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int vcnl4040_write_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + int ret; + struct vcnl4000_data *data = iio_priv(indio_dev); + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = i2c_smbus_write_word_data(data->client, + VCNL4040_PS_THDH_LM, val); + if (ret < 0) + return ret; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + ret = i2c_smbus_write_word_data(data->client, + VCNL4040_PS_THDL_LM, val); + if (ret < 0) + return ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) { int ret; @@ -877,6 +949,86 @@ static int vcnl4010_write_event_config(struct iio_dev *indio_dev, } } +static int vcnl4040_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + int ret; + struct vcnl4000_data *data = iio_priv(indio_dev); + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); + if (ret < 0) + return ret; + + data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret); + + return (dir == IIO_EV_DIR_RISING) ? + FIELD_GET(VCNL4040_PS_IF_AWAY, ret) : + FIELD_GET(VCNL4040_PS_IF_CLOSE, ret); +} + +static int vcnl4040_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + int ret; + u16 val, mask; + struct vcnl4000_data *data = iio_priv(indio_dev); + + mutex_lock(&data->vcnl4000_lock); + + ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); + if (ret < 0) + goto out; + + if (dir == IIO_EV_DIR_RISING) + mask = VCNL4040_PS_IF_AWAY; + else + mask = VCNL4040_PS_IF_CLOSE; + + val = state ? (ret | mask) : (ret & ~mask); + + data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); + ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val); + +out: + mutex_unlock(&data->vcnl4000_lock); + data->chip_spec->set_power_state(data, data->ps_int != 0); + + return ret; +} + +static irqreturn_t vcnl4040_irq_thread(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct vcnl4000_data *data = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_read_word_data(data->client, VCNL4040_INT_FLAGS); + if (ret < 0) + return IRQ_HANDLED; + + if (ret & VCNL4040_PS_IF_CLOSE) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + } + + if (ret & VCNL4040_PS_IF_AWAY) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(indio_dev)); + } + + return IRQ_HANDLED; +} + static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, uintptr_t priv, const struct iio_chan_spec *chan, @@ -887,6 +1039,134 @@ static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, return sprintf(buf, "%u\n", data->near_level); } +static irqreturn_t vcnl4010_irq_thread(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct vcnl4000_data *data = iio_priv(indio_dev); + unsigned long isr; + int ret; + + ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); + if (ret < 0) + goto end; + + isr = ret; + + if (isr & VCNL4010_INT_THR) { + if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE( + IIO_PROXIMITY, + 1, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns(indio_dev)); + } + + if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE( + IIO_PROXIMITY, + 1, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + } + + i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, + isr & VCNL4010_INT_THR); + } + + if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) + iio_trigger_poll_chained(indio_dev->trig); + +end: + return IRQ_HANDLED; +} + +static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct vcnl4000_data *data = iio_priv(indio_dev); + const unsigned long *active_scan_mask = indio_dev->active_scan_mask; + u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ + bool data_read = false; + unsigned long isr; + int val = 0; + int ret; + + ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); + if (ret < 0) + goto end; + + isr = ret; + + if (test_bit(0, active_scan_mask)) { + if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { + ret = vcnl4000_read_data(data, + VCNL4000_PS_RESULT_HI, + &val); + if (ret < 0) + goto end; + + buffer[0] = val; + data_read = true; + } + } + + ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, + isr & VCNL4010_INT_DRDY); + if (ret < 0) + goto end; + + if (!data_read) + goto end; + + iio_push_to_buffers_with_timestamp(indio_dev, buffer, + iio_get_time_ns(indio_dev)); + +end: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) +{ + struct vcnl4000_data *data = iio_priv(indio_dev); + int ret; + int cmd; + + /* Do not enable the buffer if we are already capturing events. */ + if (vcnl4010_is_in_periodic_mode(data)) + return -EBUSY; + + ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, + VCNL4010_INT_PROX_EN); + if (ret < 0) + return ret; + + cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; + return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); +} + +static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) +{ + struct vcnl4000_data *data = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); +} + +static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { + .postenable = &vcnl4010_buffer_postenable, + .predisable = &vcnl4010_buffer_predisable, +}; + static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { { .name = "nearlevel", @@ -912,6 +1192,18 @@ static const struct iio_event_spec vcnl4000_event_spec[] = { } }; +static const struct iio_event_spec vcnl4040_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), + }, +}; + static const struct iio_chan_spec vcnl4000_channels[] = { { .type = IIO_LIGHT, @@ -960,6 +1252,8 @@ static const struct iio_chan_spec vcnl4040_channels[] = { BIT(IIO_CHAN_INFO_INT_TIME), .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), .ext_info = vcnl4000_ext_info, + .event_spec = vcnl4040_event_spec, + .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec), } }; @@ -980,6 +1274,10 @@ static const struct iio_info vcnl4010_info = { static const struct iio_info vcnl4040_info = { .read_raw = vcnl4000_read_raw, .write_raw = vcnl4040_write_raw, + .read_event_value = vcnl4040_read_event, + .write_event_value = vcnl4040_write_event, + .read_event_config = vcnl4040_read_event_config, + .write_event_config = vcnl4040_write_event_config, .read_avail = vcnl4040_read_avail, }; @@ -993,7 +1291,6 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .channels = vcnl4000_channels, .num_channels = ARRAY_SIZE(vcnl4000_channels), .info = &vcnl4000_info, - .irq_support = false, }, [VCNL4010] = { .prod = "VCNL4010/4020", @@ -1004,7 +1301,9 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .channels = vcnl4010_channels, .num_channels = ARRAY_SIZE(vcnl4010_channels), .info = &vcnl4010_info, - .irq_support = true, + .irq_thread = vcnl4010_irq_thread, + .trig_buffer_func = vcnl4010_trigger_handler, + .buffer_setup_ops = &vcnl4010_buffer_ops, }, [VCNL4040] = { .prod = "VCNL4040", @@ -1015,7 +1314,7 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .channels = vcnl4040_channels, .num_channels = ARRAY_SIZE(vcnl4040_channels), .info = &vcnl4040_info, - .irq_support = false, + .irq_thread = vcnl4040_irq_thread, }, [VCNL4200] = { .prod = "VCNL4200", @@ -1026,138 +1325,9 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { .channels = vcnl4000_channels, .num_channels = ARRAY_SIZE(vcnl4000_channels), .info = &vcnl4000_info, - .irq_support = false, }, }; -static irqreturn_t vcnl4010_irq_thread(int irq, void *p) -{ - struct iio_dev *indio_dev = p; - struct vcnl4000_data *data = iio_priv(indio_dev); - unsigned long isr; - int ret; - - ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); - if (ret < 0) - goto end; - - isr = ret; - - if (isr & VCNL4010_INT_THR) { - if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE( - IIO_PROXIMITY, - 1, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_FALLING), - iio_get_time_ns(indio_dev)); - } - - if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE( - IIO_PROXIMITY, - 1, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_RISING), - iio_get_time_ns(indio_dev)); - } - - i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, - isr & VCNL4010_INT_THR); - } - - if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) - iio_trigger_poll_chained(indio_dev->trig); - -end: - return IRQ_HANDLED; -} - -static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) -{ - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; - struct vcnl4000_data *data = iio_priv(indio_dev); - const unsigned long *active_scan_mask = indio_dev->active_scan_mask; - u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ - bool data_read = false; - unsigned long isr; - int val = 0; - int ret; - - ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); - if (ret < 0) - goto end; - - isr = ret; - - if (test_bit(0, active_scan_mask)) { - if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { - ret = vcnl4000_read_data(data, - VCNL4000_PS_RESULT_HI, - &val); - if (ret < 0) - goto end; - - buffer[0] = val; - data_read = true; - } - } - - ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, - isr & VCNL4010_INT_DRDY); - if (ret < 0) - goto end; - - if (!data_read) - goto end; - - iio_push_to_buffers_with_timestamp(indio_dev, buffer, - iio_get_time_ns(indio_dev)); - -end: - iio_trigger_notify_done(indio_dev->trig); - return IRQ_HANDLED; -} - -static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) -{ - struct vcnl4000_data *data = iio_priv(indio_dev); - int ret; - int cmd; - - /* Do not enable the buffer if we are already capturing events. */ - if (vcnl4010_is_in_periodic_mode(data)) - return -EBUSY; - - ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, - VCNL4010_INT_PROX_EN); - if (ret < 0) - return ret; - - cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; - return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); -} - -static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) -{ - struct vcnl4000_data *data = iio_priv(indio_dev); - int ret; - - ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); - if (ret < 0) - return ret; - - return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); -} - -static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { - .postenable = &vcnl4010_buffer_postenable, - .predisable = &vcnl4010_buffer_predisable, -}; - static const struct iio_trigger_ops vcnl4010_trigger_ops = { .validate_device = iio_trigger_validate_own_device, }; @@ -1214,22 +1384,25 @@ static int vcnl4000_probe(struct i2c_client *client) indio_dev->name = VCNL4000_DRV_NAME; indio_dev->modes = INDIO_DIRECT_MODE; - if (client->irq && data->chip_spec->irq_support) { + if (data->chip_spec->trig_buffer_func && + data->chip_spec->buffer_setup_ops) { ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, - vcnl4010_trigger_handler, - &vcnl4010_buffer_ops); + data->chip_spec->trig_buffer_func, + data->chip_spec->buffer_setup_ops); if (ret < 0) { dev_err(&client->dev, "unable to setup iio triggered buffer\n"); return ret; } + } + if (client->irq && data->chip_spec->irq_thread) { ret = devm_request_threaded_irq(&client->dev, client->irq, - NULL, vcnl4010_irq_thread, + NULL, data->chip_spec->irq_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "vcnl4010_irq", + "vcnl4000_irq", indio_dev); if (ret < 0) { dev_err(&client->dev, "irq request failed\n"); diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index b91fc5e6a26e..38532d840f2a 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -119,7 +119,7 @@ config IIO_ST_MAGN_3AXIS select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) help Say yes here to build support for STMicroelectronics magnetometers: - LSM303DLHC, LSM303DLM, LIS3MDL. + LSM303C, LSM303DLHC, LSM303DLM, LIS3MDL. Also need to enable at least one of I2C and SPI interface drivers below. @@ -208,6 +208,18 @@ config SENSORS_RM3100_SPI To compile this driver as a module, choose M here: the module will be called rm3100-spi. +config TI_TMAG5273 + tristate "TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor" + depends on I2C + select REGMAP_I2C + help + Say Y here to add support for the TI TMAG5273 Low-Power + Linear 3D Hall-Effect Sensor. + + This driver can also be compiled as a module. + To compile this driver as a module, choose M here: the module + will be called tmag5273. + config YAMAHA_YAS530 tristate "Yamaha YAS530 family of 3-Axis Magnetometers (I2C)" depends on I2C diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index b9f45b7fafc3..b1c784ea71c8 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -29,4 +29,6 @@ obj-$(CONFIG_SENSORS_RM3100) += rm3100-core.o obj-$(CONFIG_SENSORS_RM3100_I2C) += rm3100-i2c.o obj-$(CONFIG_SENSORS_RM3100_SPI) += rm3100-spi.o +obj-$(CONFIG_TI_TMAG5273) += tmag5273.o + obj-$(CONFIG_YAMAHA_YAS530) += yamaha-yas530.o diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h index 785b7f7b8b06..89945984d966 100644 --- a/drivers/iio/magnetometer/st_magn.h +++ b/drivers/iio/magnetometer/st_magn.h @@ -22,6 +22,7 @@ #define LIS2MDL_MAGN_DEV_NAME "lis2mdl" #define LSM9DS1_MAGN_DEV_NAME "lsm9ds1_magn" #define IIS2MDC_MAGN_DEV_NAME "iis2mdc" +#define LSM303C_MAGN_DEV_NAME "lsm303c_magn" #ifdef CONFIG_IIO_BUFFER int st_magn_allocate_ring(struct iio_dev *indio_dev); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index e2fd233b3626..8faa7409d9e1 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -305,6 +305,7 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { .sensors_supported = { [0] = LIS3MDL_MAGN_DEV_NAME, [1] = LSM9DS1_MAGN_DEV_NAME, + [2] = LSM303C_MAGN_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_magn_2_16bit_channels, .odr = { diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index b4098d3b3813..cc0e0e94b129 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -50,6 +50,10 @@ static const struct of_device_id st_magn_of_match[] = { .compatible = "st,iis2mdc", .data = IIS2MDC_MAGN_DEV_NAME, }, + { + .compatible = "st,lsm303c-magn", + .data = LSM303C_MAGN_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_magn_of_match); @@ -97,6 +101,7 @@ static const struct i2c_device_id st_magn_id_table[] = { { LIS2MDL_MAGN_DEV_NAME }, { LSM9DS1_MAGN_DEV_NAME }, { IIS2MDC_MAGN_DEV_NAME }, + { LSM303C_MAGN_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(i2c, st_magn_id_table); diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index 6ddc4318564a..f203e1f87eec 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -45,6 +45,10 @@ static const struct of_device_id st_magn_of_match[] = { .compatible = "st,iis2mdc", .data = IIS2MDC_MAGN_DEV_NAME, }, + { + .compatible = "st,lsm303c-magn", + .data = LSM303C_MAGN_DEV_NAME, + }, {} }; MODULE_DEVICE_TABLE(of, st_magn_of_match); @@ -89,6 +93,7 @@ static const struct spi_device_id st_magn_id_table[] = { { LIS2MDL_MAGN_DEV_NAME }, { LSM9DS1_MAGN_DEV_NAME }, { IIS2MDC_MAGN_DEV_NAME }, + { LSM303C_MAGN_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_magn_id_table); diff --git a/drivers/iio/magnetometer/tmag5273.c b/drivers/iio/magnetometer/tmag5273.c new file mode 100644 index 000000000000..28bb7efe8df8 --- /dev/null +++ b/drivers/iio/magnetometer/tmag5273.c @@ -0,0 +1,743 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for the TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor + * + * Copyright (C) 2022 WolfVision GmbH + * + * Author: Gerald Loacker <gerald.loacker@wolfvision.net> + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/pm_runtime.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define TMAG5273_DEVICE_CONFIG_1 0x00 +#define TMAG5273_DEVICE_CONFIG_2 0x01 +#define TMAG5273_SENSOR_CONFIG_1 0x02 +#define TMAG5273_SENSOR_CONFIG_2 0x03 +#define TMAG5273_X_THR_CONFIG 0x04 +#define TMAG5273_Y_THR_CONFIG 0x05 +#define TMAG5273_Z_THR_CONFIG 0x06 +#define TMAG5273_T_CONFIG 0x07 +#define TMAG5273_INT_CONFIG_1 0x08 +#define TMAG5273_MAG_GAIN_CONFIG 0x09 +#define TMAG5273_MAG_OFFSET_CONFIG_1 0x0A +#define TMAG5273_MAG_OFFSET_CONFIG_2 0x0B +#define TMAG5273_I2C_ADDRESS 0x0C +#define TMAG5273_DEVICE_ID 0x0D +#define TMAG5273_MANUFACTURER_ID_LSB 0x0E +#define TMAG5273_MANUFACTURER_ID_MSB 0x0F +#define TMAG5273_T_MSB_RESULT 0x10 +#define TMAG5273_T_LSB_RESULT 0x11 +#define TMAG5273_X_MSB_RESULT 0x12 +#define TMAG5273_X_LSB_RESULT 0x13 +#define TMAG5273_Y_MSB_RESULT 0x14 +#define TMAG5273_Y_LSB_RESULT 0x15 +#define TMAG5273_Z_MSB_RESULT 0x16 +#define TMAG5273_Z_LSB_RESULT 0x17 +#define TMAG5273_CONV_STATUS 0x18 +#define TMAG5273_ANGLE_RESULT_MSB 0x19 +#define TMAG5273_ANGLE_RESULT_LSB 0x1A +#define TMAG5273_MAGNITUDE_RESULT 0x1B +#define TMAG5273_DEVICE_STATUS 0x1C +#define TMAG5273_MAX_REG TMAG5273_DEVICE_STATUS + +#define TMAG5273_AUTOSLEEP_DELAY_MS 5000 +#define TMAG5273_MAX_AVERAGE 32 + +/* + * bits in the TMAG5273_MANUFACTURER_ID_LSB / MSB register + * 16-bit unique manufacturer ID 0x49 / 0x54 = "TI" + */ +#define TMAG5273_MANUFACTURER_ID 0x5449 + +/* bits in the TMAG5273_DEVICE_CONFIG_1 register */ +#define TMAG5273_AVG_MODE_MASK GENMASK(4, 2) +#define TMAG5273_AVG_1_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 0) +#define TMAG5273_AVG_2_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 1) +#define TMAG5273_AVG_4_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 2) +#define TMAG5273_AVG_8_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 3) +#define TMAG5273_AVG_16_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 4) +#define TMAG5273_AVG_32_MODE FIELD_PREP(TMAG5273_AVG_MODE_MASK, 5) + +/* bits in the TMAG5273_DEVICE_CONFIG_2 register */ +#define TMAG5273_OP_MODE_MASK GENMASK(1, 0) +#define TMAG5273_OP_MODE_STANDBY FIELD_PREP(TMAG5273_OP_MODE_MASK, 0) +#define TMAG5273_OP_MODE_SLEEP FIELD_PREP(TMAG5273_OP_MODE_MASK, 1) +#define TMAG5273_OP_MODE_CONT FIELD_PREP(TMAG5273_OP_MODE_MASK, 2) +#define TMAG5273_OP_MODE_WAKEUP FIELD_PREP(TMAG5273_OP_MODE_MASK, 3) + +/* bits in the TMAG5273_SENSOR_CONFIG_1 register */ +#define TMAG5273_MAG_CH_EN_MASK GENMASK(7, 4) +#define TMAG5273_MAG_CH_EN_X_Y_Z 7 + +/* bits in the TMAG5273_SENSOR_CONFIG_2 register */ +#define TMAG5273_Z_RANGE_MASK BIT(0) +#define TMAG5273_X_Y_RANGE_MASK BIT(1) +#define TMAG5273_ANGLE_EN_MASK GENMASK(3, 2) +#define TMAG5273_ANGLE_EN_OFF 0 +#define TMAG5273_ANGLE_EN_X_Y 1 +#define TMAG5273_ANGLE_EN_Y_Z 2 +#define TMAG5273_ANGLE_EN_X_Z 3 + +/* bits in the TMAG5273_T_CONFIG register */ +#define TMAG5273_T_CH_EN BIT(0) + +/* bits in the TMAG5273_DEVICE_ID register */ +#define TMAG5273_VERSION_MASK GENMASK(1, 0) + +/* bits in the TMAG5273_CONV_STATUS register */ +#define TMAG5273_CONV_STATUS_COMPLETE BIT(0) + +enum tmag5273_channels { + TEMPERATURE = 0, + AXIS_X, + AXIS_Y, + AXIS_Z, + ANGLE, + MAGNITUDE, +}; + +enum tmag5273_scale_index { + MAGN_RANGE_LOW = 0, + MAGN_RANGE_HIGH, + MAGN_RANGE_NUM +}; + +/* state container for the TMAG5273 driver */ +struct tmag5273_data { + struct device *dev; + unsigned int devid; + unsigned int version; + char name[16]; + unsigned int conv_avg; + unsigned int scale; + enum tmag5273_scale_index scale_index; + unsigned int angle_measurement; + struct regmap *map; + struct regulator *vcc; + + /* + * Locks the sensor for exclusive use during a measurement (which + * involves several register transactions so the regmap lock is not + * enough) so that measurements get serialized in a + * first-come-first-serve manner. + */ + struct mutex lock; +}; + +static const char *const tmag5273_angle_names[] = { "off", "x-y", "y-z", "x-z" }; + +/* + * Averaging enables additional sampling of the sensor data to reduce the noise + * effect, but also increases conversion time. + */ +static const unsigned int tmag5273_avg_table[] = { + 1, 2, 4, 8, 16, 32, +}; + +/* + * Magnetic resolution in Gauss for different TMAG5273 versions. + * Scale[Gauss] = Range[mT] * 1000 / 2^15 * 10, (1 mT = 10 Gauss) + * Only version 1 and 2 are valid, version 0 and 3 are reserved. + */ +static const struct iio_val_int_plus_micro tmag5273_scale[][MAGN_RANGE_NUM] = { + { { 0, 0 }, { 0, 0 } }, + { { 0, 12200 }, { 0, 24400 } }, + { { 0, 40600 }, { 0, 81200 } }, + { { 0, 0 }, { 0, 0 } }, +}; + +static int tmag5273_get_measure(struct tmag5273_data *data, s16 *t, s16 *x, + s16 *y, s16 *z, u16 *angle, u16 *magnitude) +{ + unsigned int status, val; + __be16 reg_data[4]; + int ret; + + mutex_lock(&data->lock); + + /* + * Max. conversion time is 2425 us in 32x averaging mode for all three + * channels. Since we are in continuous measurement mode, a measurement + * may already be there, so poll for completed measurement with + * timeout. + */ + ret = regmap_read_poll_timeout(data->map, TMAG5273_CONV_STATUS, status, + status & TMAG5273_CONV_STATUS_COMPLETE, + 100, 10000); + if (ret) { + dev_err(data->dev, "timeout waiting for measurement\n"); + goto out_unlock; + } + + ret = regmap_bulk_read(data->map, TMAG5273_T_MSB_RESULT, reg_data, + sizeof(reg_data)); + if (ret) + goto out_unlock; + *t = be16_to_cpu(reg_data[0]); + *x = be16_to_cpu(reg_data[1]); + *y = be16_to_cpu(reg_data[2]); + *z = be16_to_cpu(reg_data[3]); + + ret = regmap_bulk_read(data->map, TMAG5273_ANGLE_RESULT_MSB, + ®_data[0], sizeof(reg_data[0])); + if (ret) + goto out_unlock; + /* + * angle has 9 bits integer value and 4 bits fractional part + * 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + * 0 0 0 a a a a a a a a a f f f f + */ + *angle = be16_to_cpu(reg_data[0]); + + ret = regmap_read(data->map, TMAG5273_MAGNITUDE_RESULT, &val); + if (ret < 0) + goto out_unlock; + *magnitude = val; + +out_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int tmag5273_write_osr(struct tmag5273_data *data, int val) +{ + int i; + + if (val == data->conv_avg) + return 0; + + for (i = 0; i < ARRAY_SIZE(tmag5273_avg_table); i++) { + if (tmag5273_avg_table[i] == val) + break; + } + if (i == ARRAY_SIZE(tmag5273_avg_table)) + return -EINVAL; + data->conv_avg = val; + + return regmap_update_bits(data->map, TMAG5273_DEVICE_CONFIG_1, + TMAG5273_AVG_MODE_MASK, + FIELD_PREP(TMAG5273_AVG_MODE_MASK, i)); +} + +static int tmag5273_write_scale(struct tmag5273_data *data, int scale_micro) +{ + u32 value; + int i; + + for (i = 0; i < ARRAY_SIZE(tmag5273_scale[0]); i++) { + if (tmag5273_scale[data->version][i].micro == scale_micro) + break; + } + if (i == ARRAY_SIZE(tmag5273_scale[0])) + return -EINVAL; + data->scale_index = i; + + if (data->scale_index == MAGN_RANGE_LOW) + value = 0; + else + value = TMAG5273_Z_RANGE_MASK | TMAG5273_X_Y_RANGE_MASK; + + return regmap_update_bits(data->map, TMAG5273_SENSOR_CONFIG_2, + TMAG5273_Z_RANGE_MASK | TMAG5273_X_Y_RANGE_MASK, value); +} + +static int tmag5273_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct tmag5273_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals = tmag5273_avg_table; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(tmag5273_avg_table); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_MAGN: + *type = IIO_VAL_INT_PLUS_MICRO; + *vals = (int *)tmag5273_scale[data->version]; + *length = ARRAY_SIZE(tmag5273_scale[data->version]) * + MAGN_RANGE_NUM; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int tmag5273_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, + int *val2, long mask) +{ + struct tmag5273_data *data = iio_priv(indio_dev); + s16 t, x, y, z; + u16 angle, magnitude; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + case IIO_CHAN_INFO_RAW: + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + + ret = tmag5273_get_measure(data, &t, &x, &y, &z, &angle, &magnitude); + if (ret) + return ret; + + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + + switch (chan->address) { + case TEMPERATURE: + *val = t; + return IIO_VAL_INT; + case AXIS_X: + *val = x; + return IIO_VAL_INT; + case AXIS_Y: + *val = y; + return IIO_VAL_INT; + case AXIS_Z: + *val = z; + return IIO_VAL_INT; + case ANGLE: + *val = angle; + return IIO_VAL_INT; + case MAGNITUDE: + *val = magnitude; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + /* + * Convert device specific value to millicelsius. + * Resolution from the sensor is 60.1 LSB/celsius and + * the reference value at 25 celsius is 17508 LSBs. + */ + *val = 10000; + *val2 = 601; + return IIO_VAL_FRACTIONAL; + case IIO_MAGN: + /* Magnetic resolution in uT */ + *val = 0; + *val2 = tmag5273_scale[data->version] + [data->scale_index].micro; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_ANGL: + /* + * Angle is in degrees and has four fractional bits, + * therefore use 1/16 * pi/180 to convert to radians. + */ + *val = 1000; + *val2 = 916732; + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + *val = -266314; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *val = data->conv_avg; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int tmag5273_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct tmag5273_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return tmag5273_write_osr(data, val); + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_MAGN: + if (val) + return -EINVAL; + return tmag5273_write_scale(data, val2); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +#define TMAG5273_AXIS_CHANNEL(axis, index) \ + { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .info_mask_shared_by_all_available = \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ + } + +static const struct iio_chan_spec tmag5273_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .address = TEMPERATURE, + .scan_index = TEMPERATURE, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + TMAG5273_AXIS_CHANNEL(X, AXIS_X), + TMAG5273_AXIS_CHANNEL(Y, AXIS_Y), + TMAG5273_AXIS_CHANNEL(Z, AXIS_Z), + { + .type = IIO_ANGL, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .address = ANGLE, + .scan_index = ANGLE, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + { + .type = IIO_DISTANCE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_all_available = + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .address = MAGNITUDE, + .scan_index = MAGNITUDE, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(6), +}; + +static const struct iio_info tmag5273_info = { + .read_avail = tmag5273_read_avail, + .read_raw = tmag5273_read_raw, + .write_raw = tmag5273_write_raw, +}; + +static bool tmag5273_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg >= TMAG5273_T_MSB_RESULT && reg <= TMAG5273_MAGNITUDE_RESULT; +} + +static const struct regmap_config tmag5273_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = TMAG5273_MAX_REG, + .volatile_reg = tmag5273_volatile_reg, +}; + +static int tmag5273_set_operating_mode(struct tmag5273_data *data, + unsigned int val) +{ + return regmap_write(data->map, TMAG5273_DEVICE_CONFIG_2, val); +} + +static void tmag5273_read_device_property(struct tmag5273_data *data) +{ + struct device *dev = data->dev; + const char *str; + int ret; + + data->angle_measurement = TMAG5273_ANGLE_EN_X_Y; + + ret = device_property_read_string(dev, "ti,angle-measurement", &str); + if (ret) + return; + + ret = match_string(tmag5273_angle_names, + ARRAY_SIZE(tmag5273_angle_names), str); + if (ret >= 0) + data->angle_measurement = ret; +} + +static void tmag5273_wake_up(struct tmag5273_data *data) +{ + int val; + + /* Wake up the chip by sending a dummy I2C command */ + regmap_read(data->map, TMAG5273_DEVICE_ID, &val); + /* + * Time to go to stand-by mode from sleep mode is 50us + * typically, during this time no I2C access is possible. + */ + usleep_range(80, 200); +} + +static int tmag5273_chip_init(struct tmag5273_data *data) +{ + int ret; + + ret = regmap_write(data->map, TMAG5273_DEVICE_CONFIG_1, + TMAG5273_AVG_32_MODE); + if (ret) + return ret; + data->conv_avg = 32; + + ret = regmap_write(data->map, TMAG5273_DEVICE_CONFIG_2, + TMAG5273_OP_MODE_CONT); + if (ret) + return ret; + + ret = regmap_write(data->map, TMAG5273_SENSOR_CONFIG_1, + FIELD_PREP(TMAG5273_MAG_CH_EN_MASK, + TMAG5273_MAG_CH_EN_X_Y_Z)); + if (ret) + return ret; + + ret = regmap_write(data->map, TMAG5273_SENSOR_CONFIG_2, + FIELD_PREP(TMAG5273_ANGLE_EN_MASK, + data->angle_measurement)); + if (ret) + return ret; + data->scale_index = MAGN_RANGE_LOW; + + return regmap_write(data->map, TMAG5273_T_CONFIG, TMAG5273_T_CH_EN); +} + +static int tmag5273_check_device_id(struct tmag5273_data *data) +{ + __le16 devid; + int val, ret; + + ret = regmap_read(data->map, TMAG5273_DEVICE_ID, &val); + if (ret) + return dev_err_probe(data->dev, ret, "failed to power on device\n"); + data->version = FIELD_PREP(TMAG5273_VERSION_MASK, val); + + ret = regmap_bulk_read(data->map, TMAG5273_MANUFACTURER_ID_LSB, &devid, + sizeof(devid)); + if (ret) + return dev_err_probe(data->dev, ret, "failed to read device ID\n"); + data->devid = le16_to_cpu(devid); + + switch (data->devid) { + case TMAG5273_MANUFACTURER_ID: + /* + * The device name matches the orderable part number. 'x' stands + * for A, B, C or D devices, which have different I2C addresses. + * Versions 1 or 2 (0 and 3 is reserved) stands for different + * magnetic strengths. + */ + snprintf(data->name, sizeof(data->name), "tmag5273x%1u", data->version); + if (data->version < 1 || data->version > 2) + dev_warn(data->dev, "Unsupported device %s\n", data->name); + return 0; + default: + /* + * Only print warning in case of unknown device ID to allow + * fallback compatible in device tree. + */ + dev_warn(data->dev, "Unknown device ID 0x%x\n", data->devid); + return 0; + } +} + +static void tmag5273_power_down(void *data) +{ + tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_SLEEP); +} + +static int tmag5273_probe(struct i2c_client *i2c) +{ + struct device *dev = &i2c->dev; + struct tmag5273_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->dev = dev; + i2c_set_clientdata(i2c, indio_dev); + + data->map = devm_regmap_init_i2c(i2c, &tmag5273_regmap_config); + if (IS_ERR(data->map)) + return dev_err_probe(dev, PTR_ERR(data->map), + "failed to allocate register map\n"); + + mutex_init(&data->lock); + + ret = devm_regulator_get_enable(dev, "vcc"); + if (ret) + return dev_err_probe(dev, ret, "failed to enable regulator\n"); + + tmag5273_wake_up(data); + + ret = tmag5273_check_device_id(data); + if (ret) + return ret; + + ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_CONT); + if (ret) + return dev_err_probe(dev, ret, "failed to power on device\n"); + + /* + * Register powerdown deferred callback which suspends the chip + * after module unloaded. + * + * TMAG5273 should be in SUSPEND mode in the two cases: + * 1) When driver is loaded, but we do not have any data or + * configuration requests to it (we are solving it using + * autosuspend feature). + * 2) When driver is unloaded and device is not used (devm action is + * used in this case). + */ + ret = devm_add_action_or_reset(dev, tmag5273_power_down, data); + if (ret) + return dev_err_probe(dev, ret, "failed to add powerdown action\n"); + + ret = pm_runtime_set_active(dev); + if (ret < 0) + return ret; + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + pm_runtime_get_noresume(dev); + pm_runtime_set_autosuspend_delay(dev, TMAG5273_AUTOSLEEP_DELAY_MS); + pm_runtime_use_autosuspend(dev); + + tmag5273_read_device_property(data); + + ret = tmag5273_chip_init(data); + if (ret) + return dev_err_probe(dev, ret, "failed to init device\n"); + + indio_dev->info = &tmag5273_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->name = data->name; + indio_dev->channels = tmag5273_channels; + indio_dev->num_channels = ARRAY_SIZE(tmag5273_channels); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "device register failed\n"); + + return 0; +} + +static int tmag5273_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tmag5273_data *data = iio_priv(indio_dev); + int ret; + + ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_SLEEP); + if (ret) + dev_err(dev, "failed to power off device (%pe)\n", ERR_PTR(ret)); + + return ret; +} + +static int tmag5273_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tmag5273_data *data = iio_priv(indio_dev); + int ret; + + tmag5273_wake_up(data); + + ret = tmag5273_set_operating_mode(data, TMAG5273_OP_MODE_CONT); + if (ret) + dev_err(dev, "failed to power on device (%pe)\n", ERR_PTR(ret)); + + return ret; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(tmag5273_pm_ops, + tmag5273_runtime_suspend, tmag5273_runtime_resume, + NULL); + +static const struct i2c_device_id tmag5273_id[] = { + { "tmag5273" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(i2c, tmag5273_id); + +static const struct of_device_id tmag5273_of_match[] = { + { .compatible = "ti,tmag5273" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tmag5273_of_match); + +static struct i2c_driver tmag5273_driver = { + .driver = { + .name = "tmag5273", + .of_match_table = tmag5273_of_match, + .pm = pm_ptr(&tmag5273_pm_ops), + }, + .probe_new = tmag5273_probe, + .id_table = tmag5273_id, +}; +module_i2c_driver(tmag5273_driver); + +MODULE_DESCRIPTION("TI TMAG5273 Low-Power Linear 3D Hall-Effect Sensor driver"); +MODULE_AUTHOR("Gerald Loacker <gerald.loacker@wolfvision.net>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h index 550b75b7186f..a78acd3fb18f 100644 --- a/drivers/iio/pressure/ms5611.h +++ b/drivers/iio/pressure/ms5611.h @@ -13,8 +13,6 @@ #include <linux/iio/iio.h> #include <linux/mutex.h> -struct regulator; - #define MS5611_RESET 0x1e #define MS5611_READ_ADC 0x00 #define MS5611_READ_PROM_WORD 0xA0 @@ -52,11 +50,9 @@ struct ms5611_state { int (*compensate_temp_and_pressure)(struct ms5611_state *st, s32 *temp, s32 *pressure); - struct regulator *vdd; }; int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, const char *name, int type); -void ms5611_remove(struct iio_dev *indio_dev); #endif /* _MS5611_H */ diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index c564a1d6cafe..627497e61a63 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -380,40 +380,21 @@ static const struct iio_info ms5611_info = { static int ms5611_init(struct iio_dev *indio_dev) { int ret; - struct ms5611_state *st = iio_priv(indio_dev); /* Enable attached regulator if any. */ - st->vdd = devm_regulator_get(indio_dev->dev.parent, "vdd"); - if (IS_ERR(st->vdd)) - return PTR_ERR(st->vdd); - - ret = regulator_enable(st->vdd); - if (ret) { - dev_err(indio_dev->dev.parent, - "failed to enable Vdd supply: %d\n", ret); + ret = devm_regulator_get_enable(indio_dev->dev.parent, "vdd"); + if (ret) return ret; - } ret = ms5611_reset(indio_dev); if (ret < 0) - goto err_regulator_disable; + return ret; ret = ms5611_read_prom(indio_dev); if (ret < 0) - goto err_regulator_disable; + return ret; return 0; - -err_regulator_disable: - regulator_disable(st->vdd); - return ret; -} - -static void ms5611_fini(const struct iio_dev *indio_dev) -{ - const struct ms5611_state *st = iio_priv(indio_dev); - - regulator_disable(st->vdd); } int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, @@ -453,37 +434,23 @@ int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, if (ret < 0) return ret; - ret = iio_triggered_buffer_setup(indio_dev, NULL, + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, ms5611_trigger_handler, NULL); if (ret < 0) { dev_err(dev, "iio triggered buffer setup failed\n"); - goto err_fini; + return ret; } - ret = iio_device_register(indio_dev); + ret = devm_iio_device_register(dev, indio_dev); if (ret < 0) { dev_err(dev, "unable to register iio device\n"); - goto err_buffer_cleanup; + return ret; } return 0; - -err_buffer_cleanup: - iio_triggered_buffer_cleanup(indio_dev); -err_fini: - ms5611_fini(indio_dev); - return ret; } EXPORT_SYMBOL_NS(ms5611_probe, IIO_MS5611); -void ms5611_remove(struct iio_dev *indio_dev) -{ - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - ms5611_fini(indio_dev); -} -EXPORT_SYMBOL_NS(ms5611_remove, IIO_MS5611); - MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("MS5611 core driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c index caf882497656..e3c68a3ed76a 100644 --- a/drivers/iio/pressure/ms5611_i2c.c +++ b/drivers/iio/pressure/ms5611_i2c.c @@ -105,11 +105,6 @@ static int ms5611_i2c_probe(struct i2c_client *client) return ms5611_probe(indio_dev, &client->dev, id->name, id->driver_data); } -static void ms5611_i2c_remove(struct i2c_client *client) -{ - ms5611_remove(i2c_get_clientdata(client)); -} - static const struct of_device_id ms5611_i2c_matches[] = { { .compatible = "meas,ms5611" }, { .compatible = "meas,ms5607" }, @@ -131,7 +126,6 @@ static struct i2c_driver ms5611_driver = { }, .id_table = ms5611_id, .probe_new = ms5611_i2c_probe, - .remove = ms5611_i2c_remove, }; module_i2c_driver(ms5611_driver); diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c index a0a7205c9c3a..cc9d1f68c53c 100644 --- a/drivers/iio/pressure/ms5611_spi.c +++ b/drivers/iio/pressure/ms5611_spi.c @@ -107,11 +107,6 @@ static int ms5611_spi_probe(struct spi_device *spi) spi_get_device_id(spi)->driver_data); } -static void ms5611_spi_remove(struct spi_device *spi) -{ - ms5611_remove(spi_get_drvdata(spi)); -} - static const struct of_device_id ms5611_spi_matches[] = { { .compatible = "meas,ms5611" }, { .compatible = "meas,ms5607" }, @@ -133,7 +128,6 @@ static struct spi_driver ms5611_driver = { }, .id_table = ms5611_id, .probe = ms5611_spi_probe, - .remove = ms5611_spi_remove, }; module_spi_driver(ms5611_driver); diff --git a/drivers/ipack/devices/ipoctal.c b/drivers/ipack/devices/ipoctal.c index fc00274070b6..39f0852e5ca7 100644 --- a/drivers/ipack/devices/ipoctal.c +++ b/drivers/ipack/devices/ipoctal.c @@ -253,7 +253,7 @@ static void ipoctal_irq_channel(struct ipoctal_channel *channel) static irqreturn_t ipoctal_irq_handler(void *arg) { unsigned int i; - struct ipoctal *ipoctal = (struct ipoctal *) arg; + struct ipoctal *ipoctal = arg; /* Clear the IPack device interrupt */ readw(ipoctal->int_space + ACK_INT_REQ0); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 30db49f31866..2632ca2162de 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2224,14 +2224,32 @@ config SGI_MFD_IOC3 If you have an SGI Origin, Octane, or a PCI IOC3 card, then say Y. Otherwise say N. -config MFD_INTEL_M10_BMC - tristate "Intel MAX 10 Board Management Controller" - depends on SPI_MASTER - select REGMAP_SPI_AVMM - select MFD_CORE +config MFD_INTEL_M10_BMC_CORE + tristate + select MFD_CORE + select REGMAP + default n + +config MFD_INTEL_M10_BMC_SPI + tristate "Intel MAX 10 Board Management Controller with SPI" + depends on SPI_MASTER + select MFD_INTEL_M10_BMC_CORE + select REGMAP_SPI_AVMM + help + Support for the Intel MAX 10 board management controller using the + SPI interface. + + This driver provides common support for accessing the device, + additional drivers must be enabled in order to use the functionality + of the device. + +config MFD_INTEL_M10_BMC_PMCI + tristate "Intel MAX 10 Board Management Controller with PMCI" + depends on FPGA_DFL + select MFD_INTEL_M10_BMC_CORE + select REGMAP help - Support for the Intel MAX 10 board management controller using the - SPI interface. + Support for the Intel MAX 10 board management controller via PMCI. This driver provides common support for accessing the device, additional drivers must be enabled in order to use the functionality diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 457471478a93..81c30d7cf5d4 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -269,7 +269,10 @@ obj-$(CONFIG_MFD_QCOM_PM8008) += qcom-pm8008.o obj-$(CONFIG_SGI_MFD_IOC3) += ioc3.o obj-$(CONFIG_MFD_SIMPLE_MFD_I2C) += simple-mfd-i2c.o obj-$(CONFIG_MFD_SMPRO) += smpro-core.o -obj-$(CONFIG_MFD_INTEL_M10_BMC) += intel-m10-bmc.o + +obj-$(CONFIG_MFD_INTEL_M10_BMC_CORE) += intel-m10-bmc-core.o +obj-$(CONFIG_MFD_INTEL_M10_BMC_SPI) += intel-m10-bmc-spi.o +obj-$(CONFIG_MFD_INTEL_M10_BMC_PMCI) += intel-m10-bmc-pmci.o obj-$(CONFIG_MFD_ATC260X) += atc260x-core.o obj-$(CONFIG_MFD_ATC260X_I2C) += atc260x-i2c.o diff --git a/drivers/mfd/intel-m10-bmc-core.c b/drivers/mfd/intel-m10-bmc-core.c new file mode 100644 index 000000000000..dac9cf7bcb4a --- /dev/null +++ b/drivers/mfd/intel-m10-bmc-core.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel MAX 10 Board Management Controller chip - common code + * + * Copyright (C) 2018-2020 Intel Corporation. All rights reserved. + */ + +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/dev_printk.h> +#include <linux/mfd/core.h> +#include <linux/mfd/intel-m10-bmc.h> +#include <linux/module.h> + +static ssize_t bmc_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct intel_m10bmc *ddata = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = m10bmc_sys_read(ddata, ddata->info->csr_map->build_version, &val); + if (ret) + return ret; + + return sprintf(buf, "0x%x\n", val); +} +static DEVICE_ATTR_RO(bmc_version); + +static ssize_t bmcfw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct intel_m10bmc *ddata = dev_get_drvdata(dev); + unsigned int val; + int ret; + + ret = m10bmc_sys_read(ddata, ddata->info->csr_map->fw_version, &val); + if (ret) + return ret; + + return sprintf(buf, "0x%x\n", val); +} +static DEVICE_ATTR_RO(bmcfw_version); + +static ssize_t mac_address_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct intel_m10bmc *ddata = dev_get_drvdata(dev); + unsigned int macaddr_low, macaddr_high; + int ret; + + ret = m10bmc_sys_read(ddata, ddata->info->csr_map->mac_low, &macaddr_low); + if (ret) + return ret; + + ret = m10bmc_sys_read(ddata, ddata->info->csr_map->mac_high, &macaddr_high); + if (ret) + return ret; + + return sysfs_emit(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", + (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE1, macaddr_low), + (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE2, macaddr_low), + (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE3, macaddr_low), + (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE4, macaddr_low), + (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE5, macaddr_high), + (u8)FIELD_GET(M10BMC_N3000_MAC_BYTE6, macaddr_high)); +} +static DEVICE_ATTR_RO(mac_address); + +static ssize_t mac_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct intel_m10bmc *ddata = dev_get_drvdata(dev); + unsigned int macaddr_high; + int ret; + + ret = m10bmc_sys_read(ddata, ddata->info->csr_map->mac_high, &macaddr_high); + if (ret) + return ret; + + return sysfs_emit(buf, "%u\n", (u8)FIELD_GET(M10BMC_N3000_MAC_COUNT, macaddr_high)); +} +static DEVICE_ATTR_RO(mac_count); + +static struct attribute *m10bmc_attrs[] = { + &dev_attr_bmc_version.attr, + &dev_attr_bmcfw_version.attr, + &dev_attr_mac_address.attr, + &dev_attr_mac_count.attr, + NULL, +}; + +static const struct attribute_group m10bmc_group = { + .attrs = m10bmc_attrs, +}; + +const struct attribute_group *m10bmc_dev_groups[] = { + &m10bmc_group, + NULL, +}; +EXPORT_SYMBOL_GPL(m10bmc_dev_groups); + +int m10bmc_dev_init(struct intel_m10bmc *m10bmc, const struct intel_m10bmc_platform_info *info) +{ + int ret; + + m10bmc->info = info; + dev_set_drvdata(m10bmc->dev, m10bmc); + + ret = devm_mfd_add_devices(m10bmc->dev, PLATFORM_DEVID_AUTO, + info->cells, info->n_cells, + NULL, 0, NULL); + if (ret) + dev_err(m10bmc->dev, "Failed to register sub-devices: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(m10bmc_dev_init); + +MODULE_DESCRIPTION("Intel MAX 10 BMC core driver"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/intel-m10-bmc-pmci.c b/drivers/mfd/intel-m10-bmc-pmci.c new file mode 100644 index 000000000000..975019f644b5 --- /dev/null +++ b/drivers/mfd/intel-m10-bmc-pmci.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MAX10 BMC Platform Management Component Interface (PMCI) based + * interface. + * + * Copyright (C) 2020-2023 Intel Corporation. + */ + +#include <linux/device.h> +#include <linux/dfl.h> +#include <linux/mfd/core.h> +#include <linux/mfd/intel-m10-bmc.h> +#include <linux/module.h> +#include <linux/regmap.h> + +struct m10bmc_pmci_device { + void __iomem *base; + struct intel_m10bmc m10bmc; +}; + +/* + * Intel FGPA indirect register access via hardware controller/bridge. + */ +#define INDIRECT_CMD_OFF 0 +#define INDIRECT_CMD_CLR 0 +#define INDIRECT_CMD_RD BIT(0) +#define INDIRECT_CMD_WR BIT(1) +#define INDIRECT_CMD_ACK BIT(2) + +#define INDIRECT_ADDR_OFF 0x4 +#define INDIRECT_RD_OFF 0x8 +#define INDIRECT_WR_OFF 0xc + +#define INDIRECT_INT_US 1 +#define INDIRECT_TIMEOUT_US 10000 + +struct indirect_ctx { + void __iomem *base; + struct device *dev; +}; + +static int indirect_clear_cmd(struct indirect_ctx *ctx) +{ + unsigned int cmd; + int ret; + + writel(INDIRECT_CMD_CLR, ctx->base + INDIRECT_CMD_OFF); + + ret = readl_poll_timeout(ctx->base + INDIRECT_CMD_OFF, cmd, + cmd == INDIRECT_CMD_CLR, + INDIRECT_INT_US, INDIRECT_TIMEOUT_US); + if (ret) + dev_err(ctx->dev, "timed out waiting clear cmd (residual cmd=0x%x)\n", cmd); + + return ret; +} + +static int indirect_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct indirect_ctx *ctx = context; + unsigned int cmd, ack, tmpval; + int ret, ret2; + + cmd = readl(ctx->base + INDIRECT_CMD_OFF); + if (cmd != INDIRECT_CMD_CLR) + dev_warn(ctx->dev, "residual cmd 0x%x on read entry\n", cmd); + + writel(reg, ctx->base + INDIRECT_ADDR_OFF); + writel(INDIRECT_CMD_RD, ctx->base + INDIRECT_CMD_OFF); + + ret = readl_poll_timeout(ctx->base + INDIRECT_CMD_OFF, ack, + (ack & INDIRECT_CMD_ACK) == INDIRECT_CMD_ACK, + INDIRECT_INT_US, INDIRECT_TIMEOUT_US); + if (ret) + dev_err(ctx->dev, "read timed out on reg 0x%x ack 0x%x\n", reg, ack); + else + tmpval = readl(ctx->base + INDIRECT_RD_OFF); + + ret2 = indirect_clear_cmd(ctx); + + if (ret) + return ret; + if (ret2) + return ret2; + + *val = tmpval; + return 0; +} + +static int indirect_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct indirect_ctx *ctx = context; + unsigned int cmd, ack; + int ret, ret2; + + cmd = readl(ctx->base + INDIRECT_CMD_OFF); + if (cmd != INDIRECT_CMD_CLR) + dev_warn(ctx->dev, "residual cmd 0x%x on write entry\n", cmd); + + writel(val, ctx->base + INDIRECT_WR_OFF); + writel(reg, ctx->base + INDIRECT_ADDR_OFF); + writel(INDIRECT_CMD_WR, ctx->base + INDIRECT_CMD_OFF); + + ret = readl_poll_timeout(ctx->base + INDIRECT_CMD_OFF, ack, + (ack & INDIRECT_CMD_ACK) == INDIRECT_CMD_ACK, + INDIRECT_INT_US, INDIRECT_TIMEOUT_US); + if (ret) + dev_err(ctx->dev, "write timed out on reg 0x%x ack 0x%x\n", reg, ack); + + ret2 = indirect_clear_cmd(ctx); + + if (ret) + return ret; + return ret2; +} + +static const struct regmap_range m10bmc_pmci_regmap_range[] = { + regmap_reg_range(M10BMC_N6000_SYS_BASE, M10BMC_N6000_SYS_END), +}; + +static const struct regmap_access_table m10bmc_pmci_access_table = { + .yes_ranges = m10bmc_pmci_regmap_range, + .n_yes_ranges = ARRAY_SIZE(m10bmc_pmci_regmap_range), +}; + +static struct regmap_config m10bmc_pmci_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .wr_table = &m10bmc_pmci_access_table, + .rd_table = &m10bmc_pmci_access_table, + .reg_read = &indirect_reg_read, + .reg_write = &indirect_reg_write, + .max_register = M10BMC_N6000_SYS_END, +}; + +static struct mfd_cell m10bmc_pmci_n6000_bmc_subdevs[] = { + { .name = "n6000bmc-hwmon" }, +}; + +static const struct m10bmc_csr_map m10bmc_n6000_csr_map = { + .base = M10BMC_N6000_SYS_BASE, + .build_version = M10BMC_N6000_BUILD_VER, + .fw_version = NIOS2_N6000_FW_VERSION, + .mac_low = M10BMC_N6000_MAC_LOW, + .mac_high = M10BMC_N6000_MAC_HIGH, + .doorbell = M10BMC_N6000_DOORBELL, + .auth_result = M10BMC_N6000_AUTH_RESULT, + .bmc_prog_addr = M10BMC_N6000_BMC_PROG_ADDR, + .bmc_reh_addr = M10BMC_N6000_BMC_REH_ADDR, + .bmc_magic = M10BMC_N6000_BMC_PROG_MAGIC, + .sr_prog_addr = M10BMC_N6000_SR_PROG_ADDR, + .sr_reh_addr = M10BMC_N6000_SR_REH_ADDR, + .sr_magic = M10BMC_N6000_SR_PROG_MAGIC, + .pr_prog_addr = M10BMC_N6000_PR_PROG_ADDR, + .pr_reh_addr = M10BMC_N6000_PR_REH_ADDR, + .pr_magic = M10BMC_N6000_PR_PROG_MAGIC, + .rsu_update_counter = M10BMC_N6000_STAGING_FLASH_COUNT, +}; + +static const struct intel_m10bmc_platform_info m10bmc_pmci_n6000 = { + .cells = m10bmc_pmci_n6000_bmc_subdevs, + .n_cells = ARRAY_SIZE(m10bmc_pmci_n6000_bmc_subdevs), + .csr_map = &m10bmc_n6000_csr_map, +}; + +static int m10bmc_pmci_probe(struct dfl_device *ddev) +{ + struct device *dev = &ddev->dev; + struct m10bmc_pmci_device *pmci; + struct indirect_ctx *ctx; + + pmci = devm_kzalloc(dev, sizeof(*pmci), GFP_KERNEL); + if (!pmci) + return -ENOMEM; + + pmci->m10bmc.dev = dev; + + pmci->base = devm_ioremap_resource(dev, &ddev->mmio_res); + if (IS_ERR(pmci->base)) + return PTR_ERR(pmci->base); + + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->base = pmci->base + M10BMC_N6000_INDIRECT_BASE; + ctx->dev = dev; + indirect_clear_cmd(ctx); + pmci->m10bmc.regmap = devm_regmap_init(dev, NULL, ctx, &m10bmc_pmci_regmap_config); + + if (IS_ERR(pmci->m10bmc.regmap)) + return PTR_ERR(pmci->m10bmc.regmap); + + return m10bmc_dev_init(&pmci->m10bmc, &m10bmc_pmci_n6000); +} + +#define FME_FEATURE_ID_M10BMC_PMCI 0x12 + +static const struct dfl_device_id m10bmc_pmci_ids[] = { + { FME_ID, FME_FEATURE_ID_M10BMC_PMCI }, + { } +}; +MODULE_DEVICE_TABLE(dfl, m10bmc_pmci_ids); + +static struct dfl_driver m10bmc_pmci_driver = { + .drv = { + .name = "intel-m10-bmc", + .dev_groups = m10bmc_dev_groups, + }, + .id_table = m10bmc_pmci_ids, + .probe = m10bmc_pmci_probe, +}; + +module_dfl_driver(m10bmc_pmci_driver); + +MODULE_DESCRIPTION("MAX10 BMC PMCI-based interface"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/intel-m10-bmc-spi.c b/drivers/mfd/intel-m10-bmc-spi.c new file mode 100644 index 000000000000..957200e17fed --- /dev/null +++ b/drivers/mfd/intel-m10-bmc-spi.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel MAX 10 Board Management Controller chip + * + * Copyright (C) 2018-2020 Intel Corporation. All rights reserved. + */ +#include <linux/bitfield.h> +#include <linux/dev_printk.h> +#include <linux/init.h> +#include <linux/mfd/core.h> +#include <linux/mfd/intel-m10-bmc.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +static const struct regmap_range m10bmc_regmap_range[] = { + regmap_reg_range(M10BMC_N3000_LEGACY_BUILD_VER, M10BMC_N3000_LEGACY_BUILD_VER), + regmap_reg_range(M10BMC_N3000_SYS_BASE, M10BMC_N3000_SYS_END), + regmap_reg_range(M10BMC_N3000_FLASH_BASE, M10BMC_N3000_FLASH_END), +}; + +static const struct regmap_access_table m10bmc_access_table = { + .yes_ranges = m10bmc_regmap_range, + .n_yes_ranges = ARRAY_SIZE(m10bmc_regmap_range), +}; + +static struct regmap_config intel_m10bmc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .wr_table = &m10bmc_access_table, + .rd_table = &m10bmc_access_table, + .max_register = M10BMC_N3000_MEM_END, +}; + +static int check_m10bmc_version(struct intel_m10bmc *ddata) +{ + unsigned int v; + int ret; + + /* + * This check is to filter out the very old legacy BMC versions. In the + * old BMC chips, the BMC version info is stored in the old version + * register (M10BMC_N3000_LEGACY_BUILD_VER), so its read out value would have + * not been M10BMC_N3000_VER_LEGACY_INVALID (0xffffffff). But in new BMC + * chips that the driver supports, the value of this register should be + * M10BMC_N3000_VER_LEGACY_INVALID. + */ + ret = m10bmc_raw_read(ddata, M10BMC_N3000_LEGACY_BUILD_VER, &v); + if (ret) + return -ENODEV; + + if (v != M10BMC_N3000_VER_LEGACY_INVALID) { + dev_err(ddata->dev, "bad version M10BMC detected\n"); + return -ENODEV; + } + + return 0; +} + +static int intel_m10_bmc_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + const struct intel_m10bmc_platform_info *info; + struct device *dev = &spi->dev; + struct intel_m10bmc *ddata; + int ret; + + ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + info = (struct intel_m10bmc_platform_info *)id->driver_data; + ddata->dev = dev; + + ddata->regmap = devm_regmap_init_spi_avmm(spi, &intel_m10bmc_regmap_config); + if (IS_ERR(ddata->regmap)) { + ret = PTR_ERR(ddata->regmap); + dev_err(dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + spi_set_drvdata(spi, ddata); + + ret = check_m10bmc_version(ddata); + if (ret) { + dev_err(dev, "Failed to identify m10bmc hardware\n"); + return ret; + } + + return m10bmc_dev_init(ddata, info); +} + +static const struct m10bmc_csr_map m10bmc_n3000_csr_map = { + .base = M10BMC_N3000_SYS_BASE, + .build_version = M10BMC_N3000_BUILD_VER, + .fw_version = NIOS2_N3000_FW_VERSION, + .mac_low = M10BMC_N3000_MAC_LOW, + .mac_high = M10BMC_N3000_MAC_HIGH, + .doorbell = M10BMC_N3000_DOORBELL, + .auth_result = M10BMC_N3000_AUTH_RESULT, + .bmc_prog_addr = M10BMC_N3000_BMC_PROG_ADDR, + .bmc_reh_addr = M10BMC_N3000_BMC_REH_ADDR, + .bmc_magic = M10BMC_N3000_BMC_PROG_MAGIC, + .sr_prog_addr = M10BMC_N3000_SR_PROG_ADDR, + .sr_reh_addr = M10BMC_N3000_SR_REH_ADDR, + .sr_magic = M10BMC_N3000_SR_PROG_MAGIC, + .pr_prog_addr = M10BMC_N3000_PR_PROG_ADDR, + .pr_reh_addr = M10BMC_N3000_PR_REH_ADDR, + .pr_magic = M10BMC_N3000_PR_PROG_MAGIC, + .rsu_update_counter = M10BMC_N3000_STAGING_FLASH_COUNT, +}; + +static struct mfd_cell m10bmc_d5005_subdevs[] = { + { .name = "d5005bmc-hwmon" }, + { .name = "d5005bmc-sec-update" }, +}; + +static struct mfd_cell m10bmc_pacn3000_subdevs[] = { + { .name = "n3000bmc-hwmon" }, + { .name = "n3000bmc-retimer" }, + { .name = "n3000bmc-sec-update" }, +}; + +static struct mfd_cell m10bmc_n5010_subdevs[] = { + { .name = "n5010bmc-hwmon" }, +}; + +static const struct intel_m10bmc_platform_info m10bmc_spi_n3000 = { + .cells = m10bmc_pacn3000_subdevs, + .n_cells = ARRAY_SIZE(m10bmc_pacn3000_subdevs), + .csr_map = &m10bmc_n3000_csr_map, +}; + +static const struct intel_m10bmc_platform_info m10bmc_spi_d5005 = { + .cells = m10bmc_d5005_subdevs, + .n_cells = ARRAY_SIZE(m10bmc_d5005_subdevs), + .csr_map = &m10bmc_n3000_csr_map, +}; + +static const struct intel_m10bmc_platform_info m10bmc_spi_n5010 = { + .cells = m10bmc_n5010_subdevs, + .n_cells = ARRAY_SIZE(m10bmc_n5010_subdevs), + .csr_map = &m10bmc_n3000_csr_map, +}; + +static const struct spi_device_id m10bmc_spi_id[] = { + { "m10-n3000", (kernel_ulong_t)&m10bmc_spi_n3000 }, + { "m10-d5005", (kernel_ulong_t)&m10bmc_spi_d5005 }, + { "m10-n5010", (kernel_ulong_t)&m10bmc_spi_n5010 }, + { } +}; +MODULE_DEVICE_TABLE(spi, m10bmc_spi_id); + +static struct spi_driver intel_m10bmc_spi_driver = { + .driver = { + .name = "intel-m10-bmc", + .dev_groups = m10bmc_dev_groups, + }, + .probe = intel_m10_bmc_spi_probe, + .id_table = m10bmc_spi_id, +}; +module_spi_driver(intel_m10bmc_spi_driver); + +MODULE_DESCRIPTION("Intel MAX 10 BMC SPI bus interface"); +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("spi:intel-m10-bmc"); diff --git a/drivers/mfd/intel-m10-bmc.c b/drivers/mfd/intel-m10-bmc.c deleted file mode 100644 index 7e3319e5b22f..000000000000 --- a/drivers/mfd/intel-m10-bmc.c +++ /dev/null @@ -1,238 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Intel MAX 10 Board Management Controller chip - * - * Copyright (C) 2018-2020 Intel Corporation. All rights reserved. - */ -#include <linux/bitfield.h> -#include <linux/init.h> -#include <linux/mfd/core.h> -#include <linux/mfd/intel-m10-bmc.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/regmap.h> -#include <linux/spi/spi.h> - -enum m10bmc_type { - M10_N3000, - M10_D5005, - M10_N5010, -}; - -static struct mfd_cell m10bmc_d5005_subdevs[] = { - { .name = "d5005bmc-hwmon" }, - { .name = "d5005bmc-sec-update" } -}; - -static struct mfd_cell m10bmc_pacn3000_subdevs[] = { - { .name = "n3000bmc-hwmon" }, - { .name = "n3000bmc-retimer" }, - { .name = "n3000bmc-sec-update" }, -}; - -static struct mfd_cell m10bmc_n5010_subdevs[] = { - { .name = "n5010bmc-hwmon" }, -}; - -static const struct regmap_range m10bmc_regmap_range[] = { - regmap_reg_range(M10BMC_LEGACY_BUILD_VER, M10BMC_LEGACY_BUILD_VER), - regmap_reg_range(M10BMC_SYS_BASE, M10BMC_SYS_END), - regmap_reg_range(M10BMC_FLASH_BASE, M10BMC_FLASH_END), -}; - -static const struct regmap_access_table m10bmc_access_table = { - .yes_ranges = m10bmc_regmap_range, - .n_yes_ranges = ARRAY_SIZE(m10bmc_regmap_range), -}; - -static struct regmap_config intel_m10bmc_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .wr_table = &m10bmc_access_table, - .rd_table = &m10bmc_access_table, - .max_register = M10BMC_MEM_END, -}; - -static ssize_t bmc_version_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct intel_m10bmc *ddata = dev_get_drvdata(dev); - unsigned int val; - int ret; - - ret = m10bmc_sys_read(ddata, M10BMC_BUILD_VER, &val); - if (ret) - return ret; - - return sprintf(buf, "0x%x\n", val); -} -static DEVICE_ATTR_RO(bmc_version); - -static ssize_t bmcfw_version_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct intel_m10bmc *ddata = dev_get_drvdata(dev); - unsigned int val; - int ret; - - ret = m10bmc_sys_read(ddata, NIOS2_FW_VERSION, &val); - if (ret) - return ret; - - return sprintf(buf, "0x%x\n", val); -} -static DEVICE_ATTR_RO(bmcfw_version); - -static ssize_t mac_address_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct intel_m10bmc *max10 = dev_get_drvdata(dev); - unsigned int macaddr_low, macaddr_high; - int ret; - - ret = m10bmc_sys_read(max10, M10BMC_MAC_LOW, &macaddr_low); - if (ret) - return ret; - - ret = m10bmc_sys_read(max10, M10BMC_MAC_HIGH, &macaddr_high); - if (ret) - return ret; - - return sysfs_emit(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n", - (u8)FIELD_GET(M10BMC_MAC_BYTE1, macaddr_low), - (u8)FIELD_GET(M10BMC_MAC_BYTE2, macaddr_low), - (u8)FIELD_GET(M10BMC_MAC_BYTE3, macaddr_low), - (u8)FIELD_GET(M10BMC_MAC_BYTE4, macaddr_low), - (u8)FIELD_GET(M10BMC_MAC_BYTE5, macaddr_high), - (u8)FIELD_GET(M10BMC_MAC_BYTE6, macaddr_high)); -} -static DEVICE_ATTR_RO(mac_address); - -static ssize_t mac_count_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct intel_m10bmc *max10 = dev_get_drvdata(dev); - unsigned int macaddr_high; - int ret; - - ret = m10bmc_sys_read(max10, M10BMC_MAC_HIGH, &macaddr_high); - if (ret) - return ret; - - return sysfs_emit(buf, "%u\n", - (u8)FIELD_GET(M10BMC_MAC_COUNT, macaddr_high)); -} -static DEVICE_ATTR_RO(mac_count); - -static struct attribute *m10bmc_attrs[] = { - &dev_attr_bmc_version.attr, - &dev_attr_bmcfw_version.attr, - &dev_attr_mac_address.attr, - &dev_attr_mac_count.attr, - NULL, -}; -ATTRIBUTE_GROUPS(m10bmc); - -static int check_m10bmc_version(struct intel_m10bmc *ddata) -{ - unsigned int v; - int ret; - - /* - * This check is to filter out the very old legacy BMC versions. In the - * old BMC chips, the BMC version info is stored in the old version - * register (M10BMC_LEGACY_BUILD_VER), so its read out value would have - * not been M10BMC_VER_LEGACY_INVALID (0xffffffff). But in new BMC - * chips that the driver supports, the value of this register should be - * M10BMC_VER_LEGACY_INVALID. - */ - ret = m10bmc_raw_read(ddata, M10BMC_LEGACY_BUILD_VER, &v); - if (ret) - return -ENODEV; - - if (v != M10BMC_VER_LEGACY_INVALID) { - dev_err(ddata->dev, "bad version M10BMC detected\n"); - return -ENODEV; - } - - return 0; -} - -static int intel_m10_bmc_spi_probe(struct spi_device *spi) -{ - const struct spi_device_id *id = spi_get_device_id(spi); - struct device *dev = &spi->dev; - struct mfd_cell *cells; - struct intel_m10bmc *ddata; - int ret, n_cell; - - ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); - if (!ddata) - return -ENOMEM; - - ddata->dev = dev; - - ddata->regmap = - devm_regmap_init_spi_avmm(spi, &intel_m10bmc_regmap_config); - if (IS_ERR(ddata->regmap)) { - ret = PTR_ERR(ddata->regmap); - dev_err(dev, "Failed to allocate regmap: %d\n", ret); - return ret; - } - - spi_set_drvdata(spi, ddata); - - ret = check_m10bmc_version(ddata); - if (ret) { - dev_err(dev, "Failed to identify m10bmc hardware\n"); - return ret; - } - - switch (id->driver_data) { - case M10_N3000: - cells = m10bmc_pacn3000_subdevs; - n_cell = ARRAY_SIZE(m10bmc_pacn3000_subdevs); - break; - case M10_D5005: - cells = m10bmc_d5005_subdevs; - n_cell = ARRAY_SIZE(m10bmc_d5005_subdevs); - break; - case M10_N5010: - cells = m10bmc_n5010_subdevs; - n_cell = ARRAY_SIZE(m10bmc_n5010_subdevs); - break; - default: - return -ENODEV; - } - - ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cells, n_cell, - NULL, 0, NULL); - if (ret) - dev_err(dev, "Failed to register sub-devices: %d\n", ret); - - return ret; -} - -static const struct spi_device_id m10bmc_spi_id[] = { - { "m10-n3000", M10_N3000 }, - { "m10-d5005", M10_D5005 }, - { "m10-n5010", M10_N5010 }, - { } -}; -MODULE_DEVICE_TABLE(spi, m10bmc_spi_id); - -static struct spi_driver intel_m10bmc_spi_driver = { - .driver = { - .name = "intel-m10-bmc", - .dev_groups = m10bmc_groups, - }, - .probe = intel_m10_bmc_spi_probe, - .id_table = m10bmc_spi_id, -}; -module_spi_driver(intel_m10bmc_spi_driver); - -MODULE_DESCRIPTION("Intel MAX 10 BMC Device Driver"); -MODULE_AUTHOR("Intel Corporation"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("spi:intel-m10-bmc"); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 9947b7892bd5..6a514fb7a1f2 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -518,6 +518,26 @@ config VCPU_STALL_DETECTOR If you do not intend to run this kernel as a guest, say N. +config TMR_MANAGER + tristate "Select TMR Manager" + depends on MICROBLAZE && MB_MANAGER + help + This option enables the driver developed for TMR Manager. + The Triple Modular Redundancy(TMR) manager provides support for + fault detection. + + Say N here unless you know what you are doing. + +config TMR_INJECT + tristate "Select TMR Inject" + depends on TMR_MANAGER && FAULT_INJECTION_DEBUG_FS + help + This option enables the driver developed for TMR Inject. + The Triple Modular Redundancy(TMR) Inject provides + fault injection. + + Say N here unless you know what you are doing. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 87b54a4a4422..172e2076470c 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -64,3 +64,5 @@ obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o obj-$(CONFIG_OPEN_DICE) += open-dice.o obj-$(CONFIG_GP_PCI1XXXX) += mchp_pci1xxxx/ obj-$(CONFIG_VCPU_STALL_DETECTOR) += vcpu_stall_detector.o +obj-$(CONFIG_TMR_MANAGER) += xilinx_tmr_manager.o +obj-$(CONFIG_TMR_INJECT) += xilinx_tmr_inject.o diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index bdffc6543f6f..65d49a6de1a7 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -437,12 +437,6 @@ static int at25_probe(struct spi_device *spi) struct spi_eeprom *pdata; bool is_fram; - err = device_property_match_string(&spi->dev, "compatible", "cypress,fm25"); - if (err >= 0) - is_fram = true; - else - is_fram = false; - /* * Ping the chip ... the status register is pretty portable, * unlike probing manufacturer IDs. We do expect that system @@ -462,6 +456,8 @@ static int at25_probe(struct spi_device *spi) at25->spi = spi; spi_set_drvdata(spi, at25); + is_fram = fwnode_device_is_compatible(dev_fwnode(&spi->dev), "cypress,fm25"); + /* Chip description */ pdata = dev_get_platdata(&spi->dev); if (pdata) { diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index 4e07ee9cb500..7075d0b37881 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1566,12 +1566,20 @@ static struct i2c_driver idt_driver = { */ static int __init idt_init(void) { + int ret; + /* Create Debugfs directory first */ if (debugfs_initialized()) csr_dbgdir = debugfs_create_dir("idt_csr", NULL); /* Add new i2c-device driver */ - return i2c_add_driver(&idt_driver); + ret = i2c_add_driver(&idt_driver); + if (ret) { + debugfs_remove_recursive(csr_dbgdir); + return ret; + } + + return 0; } module_init(idt_init); diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index 1b010d9267c9..4ba966529458 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -32,7 +32,7 @@ static struct class enclosure_class; * found. @start can be used as a starting point to obtain multiple * enclosures per parent (should begin with NULL and then be set to * each returned enclosure device). Obtains a reference to the - * enclosure class device which must be released with device_put(). + * enclosure class device which must be released with put_device(). * If @start is not NULL, a reference must be taken on it which is * released before returning (this allows a loop through all * enclosures to exit with only the reference on the enclosure of diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c index 5310606113fe..7ccaca1b7cb8 100644 --- a/drivers/misc/fastrpc.c +++ b/drivers/misc/fastrpc.c @@ -2315,7 +2315,18 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev) data->domain_id = domain_id; data->rpdev = rpdev; - return of_platform_populate(rdev->of_node, NULL, NULL, rdev); + err = of_platform_populate(rdev->of_node, NULL, NULL, rdev); + if (err) + goto populate_error; + + return 0; + +populate_error: + if (data->fdevice) + misc_deregister(&data->fdevice->miscdev); + if (data->secure_fdevice) + misc_deregister(&data->secure_fdevice->miscdev); + fdev_error: kfree(data); return err; diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index 1167463f26fb..f778e11237a6 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -151,6 +151,9 @@ int genwqe_read_app_id(struct genwqe_dev *cd, char *app_name, int len) return i; } +#define CRC32_POLYNOMIAL 0x20044009 +static u32 crc32_tab[256]; /* crc32 lookup table */ + /** * genwqe_init_crc32() - Prepare a lookup table for fast crc32 calculations * @@ -159,9 +162,6 @@ int genwqe_read_app_id(struct genwqe_dev *cd, char *app_name, int len) * * Genwqe's Polynomial = 0x20044009 */ -#define CRC32_POLYNOMIAL 0x20044009 -static u32 crc32_tab[256]; /* crc32 lookup table */ - void genwqe_init_crc32(void) { int i, j; diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c index aeda2fa89e61..147b58f7968d 100644 --- a/drivers/misc/isl29003.c +++ b/drivers/misc/isl29003.c @@ -186,7 +186,7 @@ static ssize_t isl29003_show_range(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); - return sprintf(buf, "%i\n", isl29003_get_range(client)); + return sysfs_emit(buf, "%i\n", isl29003_get_range(client)); } static ssize_t isl29003_store_range(struct device *dev, @@ -222,7 +222,7 @@ static ssize_t isl29003_show_resolution(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); - return sprintf(buf, "%d\n", isl29003_get_resolution(client)); + return sysfs_emit(buf, "%d\n", isl29003_get_resolution(client)); } static ssize_t isl29003_store_resolution(struct device *dev, @@ -256,7 +256,7 @@ static ssize_t isl29003_show_mode(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); - return sprintf(buf, "%d\n", isl29003_get_mode(client)); + return sysfs_emit(buf, "%d\n", isl29003_get_mode(client)); } static ssize_t isl29003_store_mode(struct device *dev, @@ -291,7 +291,7 @@ static ssize_t isl29003_show_power_state(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); - return sprintf(buf, "%d\n", isl29003_get_power_state(client)); + return sysfs_emit(buf, "%d\n", isl29003_get_power_state(client)); } static ssize_t isl29003_store_power_state(struct device *dev, @@ -327,7 +327,7 @@ static ssize_t isl29003_show_lux(struct device *dev, if (!isl29003_get_power_state(client)) return -EBUSY; - return sprintf(buf, "%d\n", isl29003_get_adc_value(client)); + return sysfs_emit(buf, "%d\n", isl29003_get_adc_value(client)); } static DEVICE_ATTR(lux, S_IRUGO, isl29003_show_lux, NULL); diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index 6df7679d9739..92c0930cc742 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -151,7 +151,7 @@ static int mei_fwver(struct mei_cl_device *cldev) ret = __mei_cl_send(cldev->cl, (u8 *)&req, sizeof(req), 0, MEI_CL_IO_TX_BLOCKING); if (ret < 0) { - dev_err(&cldev->dev, "Could not send ReqFWVersion cmd\n"); + dev_err(&cldev->dev, "Could not send ReqFWVersion cmd ret = %d\n", ret); return ret; } @@ -163,7 +163,7 @@ static int mei_fwver(struct mei_cl_device *cldev) * Should be at least one version block, * error out if nothing found */ - dev_err(&cldev->dev, "Could not read FW version\n"); + dev_err(&cldev->dev, "Could not read FW version ret = %d\n", bytes_recv); return -EIO; } @@ -380,7 +380,7 @@ static int mei_nfc_if_version(struct mei_cl *cl, ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(cmd), 0, MEI_CL_IO_TX_BLOCKING); if (ret < 0) { - dev_err(bus->dev, "Could not send IF version cmd\n"); + dev_err(bus->dev, "Could not send IF version cmd ret = %d\n", ret); return ret; } @@ -395,7 +395,7 @@ static int mei_nfc_if_version(struct mei_cl *cl, bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length, &vtag, 0, 0); if (bytes_recv < 0 || (size_t)bytes_recv < if_version_length) { - dev_err(bus->dev, "Could not read IF version\n"); + dev_err(bus->dev, "Could not read IF version ret = %d\n", bytes_recv); ret = -EIO; goto err; } diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index e889a8bd7ac8..e0dcd5c114db 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -859,8 +859,8 @@ static void mei_hdcp_remove(struct mei_cl_device *cldev) dev_warn(&cldev->dev, "mei_cldev_disable() failed\n"); } -#define MEI_UUID_HDCP GUID_INIT(0xB638AB7E, 0x94E2, 0x4EA2, 0xA5, \ - 0x52, 0xD1, 0xC5, 0x4B, 0x62, 0x7F, 0x04) +#define MEI_UUID_HDCP UUID_LE(0xB638AB7E, 0x94E2, 0x4EA2, 0xA5, \ + 0x52, 0xD1, 0xC5, 0x4B, 0x62, 0x7F, 0x04) static const struct mei_cl_device_id mei_hdcp_tbl[] = { { .uuid = MEI_UUID_HDCP, .version = MEI_CL_VERSION_ANY }, diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 996b70a988be..895011b7a0bf 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -13,6 +13,11 @@ #include <linux/mei.h> #include <linux/mei_cl_bus.h> +static inline int uuid_le_cmp(const uuid_le u1, const uuid_le u2) +{ + return memcmp(&u1, &u2, sizeof(uuid_le)); +} + #include "hw.h" #include "hbm.h" diff --git a/drivers/misc/mei/pxp/mei_pxp.c b/drivers/misc/mei/pxp/mei_pxp.c index 8dd09b1722eb..7ee1fa7b1cb3 100644 --- a/drivers/misc/mei/pxp/mei_pxp.c +++ b/drivers/misc/mei/pxp/mei_pxp.c @@ -238,8 +238,8 @@ static void mei_pxp_remove(struct mei_cl_device *cldev) } /* fbf6fcf1-96cf-4e2e-a6a6-1bab8cbe36b1 : PAVP GUID*/ -#define MEI_GUID_PXP GUID_INIT(0xfbf6fcf1, 0x96cf, 0x4e2e, 0xA6, \ - 0xa6, 0x1b, 0xab, 0x8c, 0xbe, 0x36, 0xb1) +#define MEI_GUID_PXP UUID_LE(0xfbf6fcf1, 0x96cf, 0x4e2e, 0xA6, \ + 0xa6, 0x1b, 0xab, 0x8c, 0xbe, 0x36, 0xb1) static struct mei_cl_device_id mei_pxp_tbl[] = { { .uuid = MEI_GUID_PXP, .version = MEI_CL_VERSION_ANY }, diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c index fa1f5a632e7f..37e804bbb1f2 100644 --- a/drivers/misc/sgi-gru/grukservices.c +++ b/drivers/misc/sgi-gru/grukservices.c @@ -425,7 +425,7 @@ int gru_get_cb_exception_detail(void *cb, static char *gru_get_cb_exception_detail_str(int ret, void *cb, char *buf, int size) { - struct gru_control_block_status *gen = (void *)cb; + struct gru_control_block_status *gen = cb; struct control_block_extended_exc_detail excdet; if (ret > 0 && gen->istatus == CBS_EXCEPTION) { @@ -452,7 +452,7 @@ static int gru_wait_idle_or_exception(struct gru_control_block_status *gen) static int gru_retry_exception(void *cb) { - struct gru_control_block_status *gen = (void *)cb; + struct gru_control_block_status *gen = cb; struct control_block_extended_exc_detail excdet; int retry = EXCEPTION_RETRY_LIMIT; @@ -475,7 +475,7 @@ static int gru_retry_exception(void *cb) int gru_check_status_proc(void *cb) { - struct gru_control_block_status *gen = (void *)cb; + struct gru_control_block_status *gen = cb; int ret; ret = gen->istatus; @@ -488,7 +488,7 @@ int gru_check_status_proc(void *cb) int gru_wait_proc(void *cb) { - struct gru_control_block_status *gen = (void *)cb; + struct gru_control_block_status *gen = cb; int ret; ret = gru_wait_idle_or_exception(gen); diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c index 7f6976a9f508..01d2257deea4 100644 --- a/drivers/misc/ti-st/st_core.c +++ b/drivers/misc/ti-st/st_core.c @@ -338,7 +338,7 @@ void st_int_recv(void *disc_data, ptr++; count--; continue; - /* Unknow packet? */ + /* Unknown packet? */ default: type = *ptr; diff --git a/drivers/misc/uacce/uacce.c b/drivers/misc/uacce/uacce.c index 905eff1f840e..d3a217929a24 100644 --- a/drivers/misc/uacce/uacce.c +++ b/drivers/misc/uacce/uacce.c @@ -363,12 +363,52 @@ static ssize_t region_dus_size_show(struct device *dev, uacce->qf_pg_num[UACCE_QFRT_DUS] << PAGE_SHIFT); } +static ssize_t isolate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uacce_device *uacce = to_uacce_device(dev); + + return sysfs_emit(buf, "%d\n", uacce->ops->get_isolate_state(uacce)); +} + +static ssize_t isolate_strategy_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct uacce_device *uacce = to_uacce_device(dev); + u32 val; + + val = uacce->ops->isolate_err_threshold_read(uacce); + + return sysfs_emit(buf, "%u\n", val); +} + +static ssize_t isolate_strategy_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct uacce_device *uacce = to_uacce_device(dev); + unsigned long val; + int ret; + + if (kstrtoul(buf, 0, &val) < 0) + return -EINVAL; + + if (val > UACCE_MAX_ERR_THRESHOLD) + return -EINVAL; + + ret = uacce->ops->isolate_err_threshold_write(uacce, val); + if (ret) + return ret; + + return count; +} + static DEVICE_ATTR_RO(api); static DEVICE_ATTR_RO(flags); static DEVICE_ATTR_RO(available_instances); static DEVICE_ATTR_RO(algorithms); static DEVICE_ATTR_RO(region_mmio_size); static DEVICE_ATTR_RO(region_dus_size); +static DEVICE_ATTR_RO(isolate); +static DEVICE_ATTR_RW(isolate_strategy); static struct attribute *uacce_dev_attrs[] = { &dev_attr_api.attr, @@ -377,6 +417,8 @@ static struct attribute *uacce_dev_attrs[] = { &dev_attr_algorithms.attr, &dev_attr_region_mmio_size.attr, &dev_attr_region_dus_size.attr, + &dev_attr_isolate.attr, + &dev_attr_isolate_strategy.attr, NULL, }; @@ -392,6 +434,14 @@ static umode_t uacce_dev_is_visible(struct kobject *kobj, (!uacce->qf_pg_num[UACCE_QFRT_DUS]))) return 0; + if (attr == &dev_attr_isolate_strategy.attr && + (!uacce->ops->isolate_err_threshold_read && + !uacce->ops->isolate_err_threshold_write)) + return 0; + + if (attr == &dev_attr_isolate.attr && !uacce->ops->get_isolate_state) + return 0; + return attr->mode; } diff --git a/drivers/misc/vmw_vmci/vmci_host.c b/drivers/misc/vmw_vmci/vmci_host.c index da1e2a773823..857b9851402a 100644 --- a/drivers/misc/vmw_vmci/vmci_host.c +++ b/drivers/misc/vmw_vmci/vmci_host.c @@ -242,6 +242,8 @@ static int vmci_host_setup_notify(struct vmci_ctx *context, context->notify_page = NULL; return VMCI_ERROR_GENERIC; } + if (context->notify_page == NULL) + return VMCI_ERROR_UNAVAILABLE; /* * Map the locked page and set up notify pointer. diff --git a/drivers/misc/xilinx_tmr_inject.c b/drivers/misc/xilinx_tmr_inject.c new file mode 100644 index 000000000000..d96f6d7cd109 --- /dev/null +++ b/drivers/misc/xilinx_tmr_inject.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Xilinx TMR Inject IP. + * + * Copyright (C) 2022 Advanced Micro Devices, Inc. + * + * Description: + * This driver is developed for TMR Inject IP,The Triple Modular Redundancy(TMR) + * Inject provides fault injection. + */ + +#include <asm/xilinx_mb_manager.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/fault-inject.h> + +/* TMR Inject Register offsets */ +#define XTMR_INJECT_CR_OFFSET 0x0 +#define XTMR_INJECT_AIR_OFFSET 0x4 +#define XTMR_INJECT_IIR_OFFSET 0xC +#define XTMR_INJECT_EAIR_OFFSET 0x10 +#define XTMR_INJECT_ERR_OFFSET 0x204 + +/* Register Bitmasks/shifts */ +#define XTMR_INJECT_CR_CPUID_SHIFT 8 +#define XTMR_INJECT_CR_IE_SHIFT 10 +#define XTMR_INJECT_IIR_ADDR_MASK GENMASK(31, 16) + +#define XTMR_INJECT_MAGIC_MAX_VAL 255 + +/** + * struct xtmr_inject_dev - Driver data for TMR Inject + * @regs: device physical base address + * @magic: Magic hardware configuration value + */ +struct xtmr_inject_dev { + void __iomem *regs; + u32 magic; +}; + +static DECLARE_FAULT_ATTR(inject_fault); +static char *inject_request; +module_param(inject_request, charp, 0); +MODULE_PARM_DESC(inject_request, "default fault injection attributes"); +static struct dentry *dbgfs_root; + +/* IO accessors */ +static inline void xtmr_inject_write(struct xtmr_inject_dev *xtmr_inject, + u32 addr, u32 value) +{ + iowrite32(value, xtmr_inject->regs + addr); +} + +static inline u32 xtmr_inject_read(struct xtmr_inject_dev *xtmr_inject, + u32 addr) +{ + return ioread32(xtmr_inject->regs + addr); +} + +static int xtmr_inject_set(void *data, u64 val) +{ + if (val != 1) + return -EINVAL; + + xmb_inject_err(); + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(xtmr_inject_fops, NULL, xtmr_inject_set, "%llu\n"); + +static void xtmr_init_debugfs(struct xtmr_inject_dev *xtmr_inject) +{ + struct dentry *dir; + + dbgfs_root = debugfs_create_dir("xtmr_inject", NULL); + dir = fault_create_debugfs_attr("inject_fault", dbgfs_root, + &inject_fault); + debugfs_create_file("inject_fault", 0200, dir, NULL, + &xtmr_inject_fops); +} + +static void xtmr_inject_init(struct xtmr_inject_dev *xtmr_inject) +{ + u32 cr_val; + + if (inject_request) + setup_fault_attr(&inject_fault, inject_request); + /* Allow fault injection */ + cr_val = xtmr_inject->magic | + (1 << XTMR_INJECT_CR_IE_SHIFT) | + (1 << XTMR_INJECT_CR_CPUID_SHIFT); + xtmr_inject_write(xtmr_inject, XTMR_INJECT_CR_OFFSET, + cr_val); + /* Initialize the address inject and instruction inject registers */ + xtmr_inject_write(xtmr_inject, XTMR_INJECT_AIR_OFFSET, + XMB_INJECT_ERR_OFFSET); + xtmr_inject_write(xtmr_inject, XTMR_INJECT_IIR_OFFSET, + XMB_INJECT_ERR_OFFSET & XTMR_INJECT_IIR_ADDR_MASK); +} + +/** + * xtmr_inject_probe - Driver probe function + * @pdev: Pointer to the platform_device structure + * + * This is the driver probe routine. It does all the memory + * allocation for the device. + * + * Return: 0 on success and failure value on error + */ +static int xtmr_inject_probe(struct platform_device *pdev) +{ + struct xtmr_inject_dev *xtmr_inject; + int err; + + xtmr_inject = devm_kzalloc(&pdev->dev, sizeof(*xtmr_inject), + GFP_KERNEL); + if (!xtmr_inject) + return -ENOMEM; + + xtmr_inject->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(xtmr_inject->regs)) + return PTR_ERR(xtmr_inject->regs); + + err = of_property_read_u32(pdev->dev.of_node, "xlnx,magic", + &xtmr_inject->magic); + if (err < 0) { + dev_err(&pdev->dev, "unable to read xlnx,magic property"); + return err; + } + + if (xtmr_inject->magic > XTMR_INJECT_MAGIC_MAX_VAL) { + dev_err(&pdev->dev, "invalid xlnx,magic property value"); + return -EINVAL; + } + + /* Initialize TMR Inject */ + xtmr_inject_init(xtmr_inject); + + xtmr_init_debugfs(xtmr_inject); + + platform_set_drvdata(pdev, xtmr_inject); + + return 0; +} + +static int xtmr_inject_remove(struct platform_device *pdev) +{ + debugfs_remove_recursive(dbgfs_root); + dbgfs_root = NULL; + return 0; +} + +static const struct of_device_id xtmr_inject_of_match[] = { + { + .compatible = "xlnx,tmr-inject-1.0", + }, + { /* end of table */ } +}; +MODULE_DEVICE_TABLE(of, xtmr_inject_of_match); + +static struct platform_driver xtmr_inject_driver = { + .driver = { + .name = "xilinx-tmr_inject", + .of_match_table = xtmr_inject_of_match, + }, + .probe = xtmr_inject_probe, + .remove = xtmr_inject_remove, +}; +module_platform_driver(xtmr_inject_driver); +MODULE_AUTHOR("Advanced Micro Devices, Inc"); +MODULE_DESCRIPTION("Xilinx TMR Inject Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/xilinx_tmr_manager.c b/drivers/misc/xilinx_tmr_manager.c new file mode 100644 index 000000000000..0ef55e06d3a0 --- /dev/null +++ b/drivers/misc/xilinx_tmr_manager.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Xilinx TMR Manager IP. + * + * Copyright (C) 2022 Advanced Micro Devices, Inc. + * + * Description: + * This driver is developed for TMR Manager,The Triple Modular Redundancy(TMR) + * Manager is responsible for handling the TMR subsystem state, including + * fault detection and error recovery. The core is triplicated in each of + * the sub-blocks in the TMR subsystem, and provides majority voting of + * its internal state provides soft error detection, correction and + * recovery. + */ + +#include <asm/xilinx_mb_manager.h> +#include <linux/module.h> +#include <linux/of_device.h> + +/* TMR Manager Register offsets */ +#define XTMR_MANAGER_CR_OFFSET 0x0 +#define XTMR_MANAGER_FFR_OFFSET 0x4 +#define XTMR_MANAGER_CMR0_OFFSET 0x8 +#define XTMR_MANAGER_CMR1_OFFSET 0xC +#define XTMR_MANAGER_BDIR_OFFSET 0x10 +#define XTMR_MANAGER_SEMIMR_OFFSET 0x1C + +/* Register Bitmasks/shifts */ +#define XTMR_MANAGER_CR_MAGIC1_MASK GENMASK(7, 0) +#define XTMR_MANAGER_CR_MAGIC2_MASK GENMASK(15, 8) +#define XTMR_MANAGER_CR_RIR_MASK BIT(16) +#define XTMR_MANAGER_FFR_LM12_MASK BIT(0) +#define XTMR_MANAGER_FFR_LM13_MASK BIT(1) +#define XTMR_MANAGER_FFR_LM23_MASK BIT(2) + +#define XTMR_MANAGER_CR_MAGIC2_SHIFT 4 +#define XTMR_MANAGER_CR_RIR_SHIFT 16 +#define XTMR_MANAGER_CR_BB_SHIFT 18 + +#define XTMR_MANAGER_MAGIC1_MAX_VAL 255 + +/** + * struct xtmr_manager_dev - Driver data for TMR Manager + * @regs: device physical base address + * @cr_val: control register value + * @magic1: Magic 1 hardware configuration value + * @err_cnt: error statistics count + * @phys_baseaddr: Physical base address + */ +struct xtmr_manager_dev { + void __iomem *regs; + u32 cr_val; + u32 magic1; + u32 err_cnt; + resource_size_t phys_baseaddr; +}; + +/* IO accessors */ +static inline void xtmr_manager_write(struct xtmr_manager_dev *xtmr_manager, + u32 addr, u32 value) +{ + iowrite32(value, xtmr_manager->regs + addr); +} + +static inline u32 xtmr_manager_read(struct xtmr_manager_dev *xtmr_manager, + u32 addr) +{ + return ioread32(xtmr_manager->regs + addr); +} + +static void xmb_manager_reset_handler(struct xtmr_manager_dev *xtmr_manager) +{ + /* Clear the FFR Register contents as a part of recovery process. */ + xtmr_manager_write(xtmr_manager, XTMR_MANAGER_FFR_OFFSET, 0); +} + +static void xmb_manager_update_errcnt(struct xtmr_manager_dev *xtmr_manager) +{ + xtmr_manager->err_cnt++; +} + +static ssize_t errcnt_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct xtmr_manager_dev *xtmr_manager = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%x\n", xtmr_manager->err_cnt); +} +static DEVICE_ATTR_RO(errcnt); + +static ssize_t dis_block_break_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct xtmr_manager_dev *xtmr_manager = dev_get_drvdata(dev); + int ret; + long value; + + ret = kstrtoul(buf, 16, &value); + if (ret) + return ret; + + /* unblock the break signal*/ + xtmr_manager->cr_val &= ~(1 << XTMR_MANAGER_CR_BB_SHIFT); + xtmr_manager_write(xtmr_manager, XTMR_MANAGER_CR_OFFSET, + xtmr_manager->cr_val); + return size; +} +static DEVICE_ATTR_WO(dis_block_break); + +static struct attribute *xtmr_manager_dev_attrs[] = { + &dev_attr_dis_block_break.attr, + &dev_attr_errcnt.attr, + NULL, +}; +ATTRIBUTE_GROUPS(xtmr_manager_dev); + +static void xtmr_manager_init(struct xtmr_manager_dev *xtmr_manager) +{ + /* Clear the SEM interrupt mask register to disable the interrupt */ + xtmr_manager_write(xtmr_manager, XTMR_MANAGER_SEMIMR_OFFSET, 0); + + /* Allow recovery reset by default */ + xtmr_manager->cr_val = (1 << XTMR_MANAGER_CR_RIR_SHIFT) | + xtmr_manager->magic1; + xtmr_manager_write(xtmr_manager, XTMR_MANAGER_CR_OFFSET, + xtmr_manager->cr_val); + /* + * Configure Break Delay Initialization Register to zero so that + * break occurs immediately + */ + xtmr_manager_write(xtmr_manager, XTMR_MANAGER_BDIR_OFFSET, 0); + + /* + * To come out of break handler need to block the break signal + * in the tmr manager, update the xtmr_manager cr_val for the same + */ + xtmr_manager->cr_val |= (1 << XTMR_MANAGER_CR_BB_SHIFT); + + /* + * When the break vector gets asserted because of error injection, + * the break signal must be blocked before exiting from the + * break handler, Below api updates the TMR manager address and + * control register and error counter callback arguments, + * which will be used by the break handler to block the + * break and call the callback function. + */ + xmb_manager_register(xtmr_manager->phys_baseaddr, xtmr_manager->cr_val, + (void *)xmb_manager_update_errcnt, + xtmr_manager, (void *)xmb_manager_reset_handler); +} + +/** + * xtmr_manager_probe - Driver probe function + * @pdev: Pointer to the platform_device structure + * + * This is the driver probe routine. It does all the memory + * allocation for the device. + * + * Return: 0 on success and failure value on error + */ +static int xtmr_manager_probe(struct platform_device *pdev) +{ + struct xtmr_manager_dev *xtmr_manager; + struct resource *res; + int err; + + xtmr_manager = devm_kzalloc(&pdev->dev, sizeof(*xtmr_manager), + GFP_KERNEL); + if (!xtmr_manager) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + xtmr_manager->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xtmr_manager->regs)) + return PTR_ERR(xtmr_manager->regs); + + xtmr_manager->phys_baseaddr = res->start; + + err = of_property_read_u32(pdev->dev.of_node, "xlnx,magic1", + &xtmr_manager->magic1); + if (err < 0) { + dev_err(&pdev->dev, "unable to read xlnx,magic1 property"); + return err; + } + + if (xtmr_manager->magic1 > XTMR_MANAGER_MAGIC1_MAX_VAL) { + dev_err(&pdev->dev, "invalid xlnx,magic1 property value"); + return -EINVAL; + } + + /* Initialize TMR Manager */ + xtmr_manager_init(xtmr_manager); + + platform_set_drvdata(pdev, xtmr_manager); + + return 0; +} + +static const struct of_device_id xtmr_manager_of_match[] = { + { + .compatible = "xlnx,tmr-manager-1.0", + }, + { /* end of table */ } +}; +MODULE_DEVICE_TABLE(of, xtmr_manager_of_match); + +static struct platform_driver xtmr_manager_driver = { + .driver = { + .name = "xilinx-tmr_manager", + .of_match_table = xtmr_manager_of_match, + .dev_groups = xtmr_manager_dev_groups, + }, + .probe = xtmr_manager_probe, +}; +module_platform_driver(xtmr_manager_driver); + +MODULE_AUTHOR("Advanced Micro Devices, Inc"); +MODULE_DESCRIPTION("Xilinx TMR Manager Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/most/Kconfig b/drivers/most/Kconfig index 4b8145b9e7ad..69c9e728b7e6 100644 --- a/drivers/most/Kconfig +++ b/drivers/most/Kconfig @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 menuconfig MOST - tristate "MOST support" + tristate "MOST (Media Oriented Systems Transport) support" depends on HAS_DMA && CONFIGFS_FS default n help diff --git a/drivers/most/most_cdev.c b/drivers/most/most_cdev.c index 3722f9abd7b9..4ee536980f71 100644 --- a/drivers/most/most_cdev.c +++ b/drivers/most/most_cdev.c @@ -297,7 +297,7 @@ static __poll_t comp_poll(struct file *filp, poll_table *wait) return mask; } -/** +/* * Initialization of struct file_operations */ static const struct file_operations channel_fops = { @@ -404,8 +404,9 @@ static int comp_tx_completion(struct most_interface *iface, int channel_id) * @channel_id: channel index/ID * @cfg: pointer to actual channel configuration * @name: name of the device to be created + * @args: pointer to array of component parameters (from configfs) * - * This allocates achannel object and creates the device node in /dev + * This allocates a channel object and creates the device node in /dev * * Returns 0 on success or error code otherwise. */ diff --git a/drivers/most/most_snd.c b/drivers/most/most_snd.c index c87f6a037874..45d762804c5e 100644 --- a/drivers/most/most_snd.c +++ b/drivers/most/most_snd.c @@ -27,6 +27,7 @@ static struct most_component comp; /** * struct channel - private structure to keep channel specific data * @substream: stores the substream structure + * @pcm_hardware: low-level hardware description * @iface: interface for which the channel belongs to * @cfg: channel configuration * @card: registered sound card @@ -38,6 +39,7 @@ static struct most_component comp; * @opened: set when the stream is opened * @playback_task: playback thread * @playback_waitq: waitq used by playback thread + * @copy_fn: copy function for PCM-specific format and width */ struct channel { struct snd_pcm_substream *substream; @@ -400,7 +402,7 @@ static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream) return channel->buffer_pos; } -/** +/* * Initialization of struct snd_pcm_ops */ static const struct snd_pcm_ops pcm_ops = { @@ -501,8 +503,8 @@ static void release_adapter(struct sound_adapter *adpt) * @iface: pointer to interface instance * @channel_id: channel index/ID * @cfg: pointer to actual channel configuration - * @arg_list: string that provides the name of the device to be created in /dev - * plus the desired audio resolution + * @device_name: name of the device to be created in /dev + * @arg_list: string that provides the desired audio resolution * * Creates sound card, pcm device, sets pcm ops and registers sound card. * @@ -699,7 +701,7 @@ static int audio_tx_completion(struct most_interface *iface, int channel_id) return 0; } -/** +/* * Initialization of the struct most_component */ static struct most_component comp = { diff --git a/drivers/most/most_usb.c b/drivers/most/most_usb.c index 73258b24fea7..485d5ca39951 100644 --- a/drivers/most/most_usb.c +++ b/drivers/most/most_usb.c @@ -660,7 +660,7 @@ static void hdm_request_netinfo(struct most_interface *iface, int channel, /** * link_stat_timer_handler - schedule work obtaining mac address and link status - * @data: pointer to USB device instance + * @t: pointer to timer_list which holds a pointer to the USB device instance * * The handler runs in interrupt context. That's why we need to defer the * tasks to a work queue. @@ -763,14 +763,14 @@ static void wq_clear_halt(struct work_struct *wq_obj) mutex_unlock(&mdev->io_mutex); } -/** +/* * hdm_usb_fops - file operation table for USB driver */ static const struct file_operations hdm_usb_fops = { .owner = THIS_MODULE, }; -/** +/* * usb_device_id - ID table for HCD device probing */ static const struct usb_device_id usbid[] = { diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index 5784dc20fb38..88e125e36230 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -106,15 +106,22 @@ static int pnp_registered_parport; static void frob_econtrol(struct parport *pb, unsigned char m, unsigned char v) { + const struct parport_pc_private *priv = pb->physport->private_data; + unsigned char ecr_writable = priv->ecr_writable; unsigned char ectr = 0; + unsigned char new; if (m != 0xff) ectr = inb(ECONTROL(pb)); - pr_debug("frob_econtrol(%02x,%02x): %02x -> %02x\n", - m, v, ectr, (ectr & ~m) ^ v); + new = (ectr & ~m) ^ v; + if (ecr_writable) + /* All known users of the ECR mask require bit 0 to be set. */ + new = (new & ecr_writable) | 1; - outb((ectr & ~m) ^ v, ECONTROL(pb)); + pr_debug("frob_econtrol(%02x,%02x): %02x -> %02x\n", m, v, ectr, new); + + outb(new, ECONTROL(pb)); } static inline void frob_set_mode(struct parport *p, int mode) @@ -1479,21 +1486,24 @@ static int parport_ECR_present(struct parport *pb) struct parport_pc_private *priv = pb->private_data; unsigned char r = 0xc; - outb(r, CONTROL(pb)); - if ((inb(ECONTROL(pb)) & 0x3) == (r & 0x3)) { - outb(r ^ 0x2, CONTROL(pb)); /* Toggle bit 1 */ + if (!priv->ecr_writable) { + outb(r, CONTROL(pb)); + if ((inb(ECONTROL(pb)) & 0x3) == (r & 0x3)) { + outb(r ^ 0x2, CONTROL(pb)); /* Toggle bit 1 */ - r = inb(CONTROL(pb)); - if ((inb(ECONTROL(pb)) & 0x2) == (r & 0x2)) - goto no_reg; /* Sure that no ECR register exists */ - } + r = inb(CONTROL(pb)); + if ((inb(ECONTROL(pb)) & 0x2) == (r & 0x2)) + /* Sure that no ECR register exists */ + goto no_reg; + } - if ((inb(ECONTROL(pb)) & 0x3) != 0x1) - goto no_reg; + if ((inb(ECONTROL(pb)) & 0x3) != 0x1) + goto no_reg; - ECR_WRITE(pb, 0x34); - if (inb(ECONTROL(pb)) != 0x35) - goto no_reg; + ECR_WRITE(pb, 0x34); + if (inb(ECONTROL(pb)) != 0x35) + goto no_reg; + } priv->ecr = 1; outb(0xc, CONTROL(pb)); @@ -2000,11 +2010,13 @@ static int parport_dma_probe(struct parport *p) static LIST_HEAD(ports_list); static DEFINE_SPINLOCK(ports_lock); -struct parport *parport_pc_probe_port(unsigned long int base, - unsigned long int base_hi, - int irq, int dma, - struct device *dev, - int irqflags) +static struct parport *__parport_pc_probe_port(unsigned long int base, + unsigned long int base_hi, + int irq, int dma, + struct device *dev, + int irqflags, + unsigned int mode_mask, + unsigned char ecr_writable) { struct parport_pc_private *priv; struct parport_operations *ops; @@ -2053,6 +2065,7 @@ struct parport *parport_pc_probe_port(unsigned long int base, priv->ctr = 0xc; priv->ctr_writable = ~0x10; priv->ecr = 0; + priv->ecr_writable = ecr_writable; priv->fifo_depth = 0; priv->dma_buf = NULL; priv->dma_handle = 0; @@ -2116,20 +2129,28 @@ struct parport *parport_pc_probe_port(unsigned long int base, p->dma != PARPORT_DMA_NOFIFO && priv->fifo_depth > 0 && p->irq != PARPORT_IRQ_NONE) { p->modes |= PARPORT_MODE_ECP | PARPORT_MODE_COMPAT; + if (p->dma != PARPORT_DMA_NONE) + p->modes |= PARPORT_MODE_DMA; + } else + /* We can't use the DMA channel after all. */ + p->dma = PARPORT_DMA_NONE; +#endif /* Allowed to use FIFO/DMA */ + + p->modes &= ~mode_mask; + +#ifdef CONFIG_PARPORT_PC_FIFO + if ((p->modes & PARPORT_MODE_COMPAT) != 0) p->ops->compat_write_data = parport_pc_compat_write_block_pio; #ifdef CONFIG_PARPORT_1284 + if ((p->modes & PARPORT_MODE_ECP) != 0) p->ops->ecp_write_data = parport_pc_ecp_write_block_pio; - /* currently broken, but working on it.. (FB) */ - /* p->ops->ecp_read_data = parport_pc_ecp_read_block_pio; */ -#endif /* IEEE 1284 support */ - if (p->dma != PARPORT_DMA_NONE) { +#endif + if ((p->modes & (PARPORT_MODE_ECP | PARPORT_MODE_COMPAT)) != 0) { + if ((p->modes & PARPORT_MODE_DMA) != 0) pr_cont(", dma %d", p->dma); - p->modes |= PARPORT_MODE_DMA; - } else + else pr_cont(", using FIFO"); - } else - /* We can't use the DMA channel after all. */ - p->dma = PARPORT_DMA_NONE; + } #endif /* Allowed to use FIFO/DMA */ pr_cont(" ["); @@ -2239,6 +2260,16 @@ out1: platform_device_unregister(pdev); return NULL; } + +struct parport *parport_pc_probe_port(unsigned long int base, + unsigned long int base_hi, + int irq, int dma, + struct device *dev, + int irqflags) +{ + return __parport_pc_probe_port(base, base_hi, irq, dma, + dev, irqflags, 0, 0); +} EXPORT_SYMBOL(parport_pc_probe_port); void parport_pc_unregister_port(struct parport *p) @@ -2626,7 +2657,14 @@ static struct parport_pc_pci { int lo; int hi; /* -1 if not there, >6 for offset-method (max BAR is 6) */ - } addr[4]; + } addr[2]; + + /* Bit field of parport modes to exclude. */ + unsigned int mode_mask; + + /* If non-zero, sets the bitmask of writable ECR bits. In that + * case additionally bit 0 will be forcibly set on writes. */ + unsigned char ecr_writable; /* If set, this is called immediately after pci_enable_device. * If it returns non-zero, no probing will take place and the @@ -2658,12 +2696,19 @@ static struct parport_pc_pci { /* titan_010l */ { 1, { { 3, -1 }, } }, /* avlab_1p */ { 1, { { 0, 1}, } }, /* avlab_2p */ { 2, { { 0, 1}, { 2, 3 },} }, - /* The Oxford Semi cards are unusual: 954 doesn't support ECP, - * and 840 locks up if you write 1 to bit 2! */ - /* oxsemi_952 */ { 1, { { 0, 1 }, } }, - /* oxsemi_954 */ { 1, { { 0, -1 }, } }, - /* oxsemi_840 */ { 1, { { 0, 1 }, } }, - /* oxsemi_pcie_pport */ { 1, { { 0, 1 }, } }, + /* The Oxford Semi cards are unusual: older variants of 954 don't + * support ECP, and 840 locks up if you write 1 to bit 2! None + * implement nFault or service interrupts and all require 00001 + * bit pattern to be used for bits 4:0 with ECR writes. */ + /* oxsemi_952 */ { 1, { { 0, 1 }, }, + PARPORT_MODE_COMPAT, ECR_MODE_MASK }, + /* oxsemi_954 */ { 1, { { 0, 1 }, }, + PARPORT_MODE_ECP | + PARPORT_MODE_COMPAT, ECR_MODE_MASK }, + /* oxsemi_840 */ { 1, { { 0, 1 }, }, + PARPORT_MODE_COMPAT, ECR_MODE_MASK }, + /* oxsemi_pcie_pport */ { 1, { { 0, 1 }, }, + PARPORT_MODE_COMPAT, ECR_MODE_MASK }, /* aks_0100 */ { 1, { { 0, -1 }, } }, /* mobility_pp */ { 1, { { 0, 1 }, } }, /* netmos_9900 */ { 1, { { 0, -1 }, } }, @@ -2831,9 +2876,11 @@ static int parport_pc_pci_probe(struct pci_dev *dev, id->vendor, id->device, io_lo, io_hi, irq); } data->ports[count] = - parport_pc_probe_port(io_lo, io_hi, irq, - PARPORT_DMA_NONE, &dev->dev, - IRQF_SHARED); + __parport_pc_probe_port(io_lo, io_hi, irq, + PARPORT_DMA_NONE, &dev->dev, + IRQF_SHARED, + cards[i].mode_mask, + cards[i].ecr_writable); if (data->ports[count]) count++; } diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h index 6c2fd6cc5a98..51ac441a37c3 100644 --- a/include/linux/coresight-pmu.h +++ b/include/linux/coresight-pmu.h @@ -7,8 +7,19 @@ #ifndef _LINUX_CORESIGHT_PMU_H #define _LINUX_CORESIGHT_PMU_H +#include <linux/bits.h> + #define CORESIGHT_ETM_PMU_NAME "cs_etm" -#define CORESIGHT_ETM_PMU_SEED 0x10 + +/* + * The legacy Trace ID system based on fixed calculation from the cpu + * number. This has been replaced by drivers using a dynamic allocation + * system - but need to retain the legacy algorithm for backward comparibility + * in certain situations:- + * a) new perf running on older systems that generate the legacy mapping + * b) older tools that may not update at the same time as the kernel. + */ +#define CORESIGHT_LEGACY_CPU_TRACE_ID(cpu) (0x10 + (cpu * 2)) /* * Below are the definition of bit offsets for perf option, and works as @@ -34,15 +45,16 @@ #define ETM4_CFG_BIT_RETSTK 12 #define ETM4_CFG_BIT_VMID_OPT 15 -static inline int coresight_get_trace_id(int cpu) -{ - /* - * A trace ID of value 0 is invalid, so let's start at some - * random value that fits in 7 bits and go from there. Since - * the common convention is to have data trace IDs be I(N) + 1, - * set instruction trace IDs as a function of the CPU number. - */ - return (CORESIGHT_ETM_PMU_SEED + (cpu * 2)); -} +/* + * Interpretation of the PERF_RECORD_AUX_OUTPUT_HW_ID payload. + * Used to associate a CPU with the CoreSight Trace ID. + * [07:00] - Trace ID - uses 8 bits to make value easy to read in file. + * [59:08] - Unused (SBZ) + * [63:60] - Version + */ +#define CS_AUX_HW_ID_TRACE_ID_MASK GENMASK_ULL(7, 0) +#define CS_AUX_HW_ID_VERSION_MASK GENMASK_ULL(63, 60) + +#define CS_AUX_HW_ID_CURR_VERSION 0 #endif diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 1554021231f9..f19a47b9bb5a 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -61,6 +61,7 @@ enum coresight_dev_subtype_source { CORESIGHT_DEV_SUBTYPE_SOURCE_PROC, CORESIGHT_DEV_SUBTYPE_SOURCE_BUS, CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE, + CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS, }; enum coresight_dev_subtype_helper { @@ -314,14 +315,11 @@ struct coresight_ops_link { * Operations available for sources. * @cpu_id: returns the value of the CPU number this component * is associated to. - * @trace_id: returns the value of the component's trace ID as known - * to the HW. * @enable: enables tracing for a source. * @disable: disables tracing for a source. */ struct coresight_ops_source { int (*cpu_id)(struct coresight_device *csdev); - int (*trace_id)(struct coresight_device *csdev); int (*enable)(struct coresight_device *csdev, struct perf_event *event, u32 mode); void (*disable)(struct coresight_device *csdev, diff --git a/include/linux/hisi_acc_qm.h b/include/linux/hisi_acc_qm.h index be3aedaa96dc..d08bff0f87f9 100644 --- a/include/linux/hisi_acc_qm.h +++ b/include/linux/hisi_acc_qm.h @@ -272,6 +272,20 @@ struct hisi_qm_poll_data { u16 *qp_finish_id; }; +/** + * struct qm_err_isolate + * @isolate_lock: protects device error log + * @err_threshold: user config error threshold which triggers isolation + * @is_isolate: device isolation state + * @uacce_hw_errs: index into qm device error list + */ +struct qm_err_isolate { + struct mutex isolate_lock; + u32 err_threshold; + bool is_isolate; + struct list_head qm_hw_errs; +}; + struct hisi_qm { enum qm_hw_ver ver; enum qm_fun_type fun_type; @@ -341,6 +355,7 @@ struct hisi_qm { struct qm_shaper_factor *factor; u32 mb_qos; u32 type_rate; + struct qm_err_isolate isolate_data; }; struct hisi_qp_status { diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 8e0afaaa3f75..81413cd3a3e7 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -381,6 +381,11 @@ s64 iio_get_time_ns(const struct iio_dev *indio_dev); #define INDIO_MAX_RAW_ELEMENTS 4 +struct iio_val_int_plus_micro { + int integer; + int micro; +}; + struct iio_trigger; /* forward declaration */ /** diff --git a/include/linux/mfd/intel-m10-bmc.h b/include/linux/mfd/intel-m10-bmc.h index f0044b14136e..810534b1bd12 100644 --- a/include/linux/mfd/intel-m10-bmc.h +++ b/include/linux/mfd/intel-m10-bmc.h @@ -7,40 +7,43 @@ #ifndef __MFD_INTEL_M10_BMC_H #define __MFD_INTEL_M10_BMC_H +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/dev_printk.h> #include <linux/regmap.h> -#define M10BMC_LEGACY_BUILD_VER 0x300468 -#define M10BMC_SYS_BASE 0x300800 -#define M10BMC_SYS_END 0x300fff -#define M10BMC_FLASH_BASE 0x10000000 -#define M10BMC_FLASH_END 0x1fffffff -#define M10BMC_MEM_END M10BMC_FLASH_END +#define M10BMC_N3000_LEGACY_BUILD_VER 0x300468 +#define M10BMC_N3000_SYS_BASE 0x300800 +#define M10BMC_N3000_SYS_END 0x300fff +#define M10BMC_N3000_FLASH_BASE 0x10000000 +#define M10BMC_N3000_FLASH_END 0x1fffffff +#define M10BMC_N3000_MEM_END M10BMC_N3000_FLASH_END #define M10BMC_STAGING_BASE 0x18000000 #define M10BMC_STAGING_SIZE 0x3800000 /* Register offset of system registers */ -#define NIOS2_FW_VERSION 0x0 -#define M10BMC_MAC_LOW 0x10 -#define M10BMC_MAC_BYTE4 GENMASK(7, 0) -#define M10BMC_MAC_BYTE3 GENMASK(15, 8) -#define M10BMC_MAC_BYTE2 GENMASK(23, 16) -#define M10BMC_MAC_BYTE1 GENMASK(31, 24) -#define M10BMC_MAC_HIGH 0x14 -#define M10BMC_MAC_BYTE6 GENMASK(7, 0) -#define M10BMC_MAC_BYTE5 GENMASK(15, 8) -#define M10BMC_MAC_COUNT GENMASK(23, 16) -#define M10BMC_TEST_REG 0x3c -#define M10BMC_BUILD_VER 0x68 -#define M10BMC_VER_MAJOR_MSK GENMASK(23, 16) -#define M10BMC_VER_PCB_INFO_MSK GENMASK(31, 24) -#define M10BMC_VER_LEGACY_INVALID 0xffffffff +#define NIOS2_N3000_FW_VERSION 0x0 +#define M10BMC_N3000_MAC_LOW 0x10 +#define M10BMC_N3000_MAC_BYTE4 GENMASK(7, 0) +#define M10BMC_N3000_MAC_BYTE3 GENMASK(15, 8) +#define M10BMC_N3000_MAC_BYTE2 GENMASK(23, 16) +#define M10BMC_N3000_MAC_BYTE1 GENMASK(31, 24) +#define M10BMC_N3000_MAC_HIGH 0x14 +#define M10BMC_N3000_MAC_BYTE6 GENMASK(7, 0) +#define M10BMC_N3000_MAC_BYTE5 GENMASK(15, 8) +#define M10BMC_N3000_MAC_COUNT GENMASK(23, 16) +#define M10BMC_N3000_TEST_REG 0x3c +#define M10BMC_N3000_BUILD_VER 0x68 +#define M10BMC_N3000_VER_MAJOR_MSK GENMASK(23, 16) +#define M10BMC_N3000_VER_PCB_INFO_MSK GENMASK(31, 24) +#define M10BMC_N3000_VER_LEGACY_INVALID 0xffffffff /* Secure update doorbell register, in system register region */ -#define M10BMC_DOORBELL 0x400 +#define M10BMC_N3000_DOORBELL 0x400 /* Authorization Result register, in system register region */ -#define M10BMC_AUTH_RESULT 0x404 +#define M10BMC_N3000_AUTH_RESULT 0x404 /* Doorbell register fields */ #define DRBL_RSU_REQUEST BIT(0) @@ -88,7 +91,6 @@ #define HOST_STATUS_ABORT_RSU 0x2 #define rsu_prog(doorbell) FIELD_GET(DRBL_RSU_PROGRESS, doorbell) -#define rsu_stat(doorbell) FIELD_GET(DRBL_RSU_STATUS, doorbell) /* interval 100ms and timeout 5s */ #define NIOS_HANDSHAKE_INTERVAL_US (100 * 1000) @@ -103,29 +105,94 @@ #define RSU_COMPLETE_TIMEOUT_MS (40 * 60 * 1000) /* Addresses for security related data in FLASH */ -#define BMC_REH_ADDR 0x17ffc004 -#define BMC_PROG_ADDR 0x17ffc000 -#define BMC_PROG_MAGIC 0x5746 +#define M10BMC_N3000_BMC_REH_ADDR 0x17ffc004 +#define M10BMC_N3000_BMC_PROG_ADDR 0x17ffc000 +#define M10BMC_N3000_BMC_PROG_MAGIC 0x5746 -#define SR_REH_ADDR 0x17ffd004 -#define SR_PROG_ADDR 0x17ffd000 -#define SR_PROG_MAGIC 0x5253 +#define M10BMC_N3000_SR_REH_ADDR 0x17ffd004 +#define M10BMC_N3000_SR_PROG_ADDR 0x17ffd000 +#define M10BMC_N3000_SR_PROG_MAGIC 0x5253 -#define PR_REH_ADDR 0x17ffe004 -#define PR_PROG_ADDR 0x17ffe000 -#define PR_PROG_MAGIC 0x5250 +#define M10BMC_N3000_PR_REH_ADDR 0x17ffe004 +#define M10BMC_N3000_PR_PROG_ADDR 0x17ffe000 +#define M10BMC_N3000_PR_PROG_MAGIC 0x5250 /* Address of 4KB inverted bit vector containing staging area FLASH count */ -#define STAGING_FLASH_COUNT 0x17ffb000 +#define M10BMC_N3000_STAGING_FLASH_COUNT 0x17ffb000 + +#define M10BMC_N6000_INDIRECT_BASE 0x400 + +#define M10BMC_N6000_SYS_BASE 0x0 +#define M10BMC_N6000_SYS_END 0xfff + +#define M10BMC_N6000_DOORBELL 0x1c0 +#define M10BMC_N6000_AUTH_RESULT 0x1c4 + +#define M10BMC_N6000_BUILD_VER 0x0 +#define NIOS2_N6000_FW_VERSION 0x4 +#define M10BMC_N6000_MAC_LOW 0x20 +#define M10BMC_N6000_MAC_HIGH (M10BMC_N6000_MAC_LOW + 4) + +/* Addresses for security related data in FLASH */ +#define M10BMC_N6000_BMC_REH_ADDR 0x7ffc004 +#define M10BMC_N6000_BMC_PROG_ADDR 0x7ffc000 +#define M10BMC_N6000_BMC_PROG_MAGIC 0x5746 + +#define M10BMC_N6000_SR_REH_ADDR 0x7ffd004 +#define M10BMC_N6000_SR_PROG_ADDR 0x7ffd000 +#define M10BMC_N6000_SR_PROG_MAGIC 0x5253 + +#define M10BMC_N6000_PR_REH_ADDR 0x7ffe004 +#define M10BMC_N6000_PR_PROG_ADDR 0x7ffe000 +#define M10BMC_N6000_PR_PROG_MAGIC 0x5250 + +#define M10BMC_N6000_STAGING_FLASH_COUNT 0x7ff5000 + +/** + * struct m10bmc_csr_map - Intel MAX 10 BMC CSR register map + */ +struct m10bmc_csr_map { + unsigned int base; + unsigned int build_version; + unsigned int fw_version; + unsigned int mac_low; + unsigned int mac_high; + unsigned int doorbell; + unsigned int auth_result; + unsigned int bmc_prog_addr; + unsigned int bmc_reh_addr; + unsigned int bmc_magic; + unsigned int sr_prog_addr; + unsigned int sr_reh_addr; + unsigned int sr_magic; + unsigned int pr_prog_addr; + unsigned int pr_reh_addr; + unsigned int pr_magic; + unsigned int rsu_update_counter; +}; + +/** + * struct intel_m10bmc_platform_info - Intel MAX 10 BMC platform specific information + * @cells: MFD cells + * @n_cells: MFD cells ARRAY_SIZE() + * @csr_map: the mappings for register definition of MAX10 BMC + */ +struct intel_m10bmc_platform_info { + struct mfd_cell *cells; + int n_cells; + const struct m10bmc_csr_map *csr_map; +}; /** * struct intel_m10bmc - Intel MAX 10 BMC parent driver data structure * @dev: this device * @regmap: the regmap used to access registers by m10bmc itself + * @info: the platform information for MAX10 BMC */ struct intel_m10bmc { struct device *dev; struct regmap *regmap; + const struct intel_m10bmc_platform_info *info; }; /* @@ -152,11 +219,22 @@ m10bmc_raw_read(struct intel_m10bmc *m10bmc, unsigned int addr, * The base of the system registers could be configured by HW developers, and * in HW SPEC, the base is not added to the addresses of the system registers. * - * This macro helps to simplify the accessing of the system registers. And if + * This function helps to simplify the accessing of the system registers. And if * the base is reconfigured in HW, SW developers could simply change the - * M10BMC_SYS_BASE accordingly. + * csr_map's base accordingly. + */ +static inline int m10bmc_sys_read(struct intel_m10bmc *m10bmc, unsigned int offset, + unsigned int *val) +{ + const struct m10bmc_csr_map *csr_map = m10bmc->info->csr_map; + + return m10bmc_raw_read(m10bmc, csr_map->base + offset, val); +} + +/* + * MAX10 BMC Core support */ -#define m10bmc_sys_read(m10bmc, offset, val) \ - m10bmc_raw_read(m10bmc, M10BMC_SYS_BASE + (offset), val) +int m10bmc_dev_init(struct intel_m10bmc *m10bmc, const struct intel_m10bmc_platform_info *info); +extern const struct attribute_group *m10bmc_dev_groups[]; #endif /* __MFD_INTEL_M10_BMC_H */ diff --git a/include/linux/mhi_ep.h b/include/linux/mhi_ep.h index 478aece17046..f198a8ac7ee7 100644 --- a/include/linux/mhi_ep.h +++ b/include/linux/mhi_ep.h @@ -70,8 +70,8 @@ struct mhi_ep_db_info { * @cmd_ctx_cache_phys: Physical address of the host command context cache * @chdb: Array of channel doorbell interrupt info * @event_lock: Lock for protecting event rings - * @list_lock: Lock for protecting state transition and channel doorbell lists * @state_lock: Lock for protecting state transitions + * @list_lock: Lock for protecting state transition and channel doorbell lists * @st_transition_list: List of state transitions * @ch_db_list: List of queued channel doorbells * @wq: Dedicated workqueue for handling rings and state changes @@ -117,8 +117,8 @@ struct mhi_ep_cntrl { struct mhi_ep_db_info chdb[4]; struct mutex event_lock; + struct mutex state_lock; spinlock_t list_lock; - spinlock_t state_lock; struct list_head st_transition_list; struct list_head ch_db_list; diff --git a/include/linux/parport_pc.h b/include/linux/parport_pc.h index 3d6fc576d6a1..f1ec5c10c3b3 100644 --- a/include/linux/parport_pc.h +++ b/include/linux/parport_pc.h @@ -26,6 +26,9 @@ struct parport_pc_private { /* Whether or not there's an ECR. */ int ecr; + /* Bitmask of writable ECR bits. */ + unsigned char ecr_writable; + /* Number of PWords that FIFO will hold. */ int fifo_depth; diff --git a/include/linux/platform_data/tsl2563.h b/include/linux/platform_data/tsl2563.h deleted file mode 100644 index 9cf9309c3f24..000000000000 --- a/include/linux/platform_data/tsl2563.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __LINUX_TSL2563_H -#define __LINUX_TSL2563_H - -struct tsl2563_platform_data { - int cover_comp_gain; -}; - -#endif /* __LINUX_TSL2563_H */ diff --git a/include/linux/uacce.h b/include/linux/uacce.h index 9ce88c28b0a8..0a81c3dfd26c 100644 --- a/include/linux/uacce.h +++ b/include/linux/uacce.h @@ -8,6 +8,7 @@ #define UACCE_NAME "uacce" #define UACCE_MAX_REGION 2 #define UACCE_MAX_NAME_SIZE 64 +#define UACCE_MAX_ERR_THRESHOLD 65535 struct uacce_queue; struct uacce_device; @@ -30,6 +31,9 @@ struct uacce_qfile_region { * @is_q_updated: check whether the task is finished * @mmap: mmap addresses of queue to user space * @ioctl: ioctl for user space users of the queue + * @get_isolate_state: get the device state after set the isolate strategy + * @isolate_err_threshold_write: stored the isolate error threshold to the device + * @isolate_err_threshold_read: read the isolate error threshold value from the device */ struct uacce_ops { int (*get_available_instances)(struct uacce_device *uacce); @@ -43,6 +47,9 @@ struct uacce_ops { struct uacce_qfile_region *qfr); long (*ioctl)(struct uacce_queue *q, unsigned int cmd, unsigned long arg); + enum uacce_dev_state (*get_isolate_state)(struct uacce_device *uacce); + int (*isolate_err_threshold_write)(struct uacce_device *uacce, u32 num); + u32 (*isolate_err_threshold_read)(struct uacce_device *uacce); }; /** @@ -57,6 +64,11 @@ struct uacce_interface { const struct uacce_ops *ops; }; +enum uacce_dev_state { + UACCE_DEV_NORMAL, + UACCE_DEV_ISOLATE, +}; + enum uacce_q_state { UACCE_Q_ZOMBIE = 0, UACCE_Q_INIT, diff --git a/include/linux/uuid.h b/include/linux/uuid.h index 8cdc0d3567cd..6b1a3efa1e0b 100644 --- a/include/linux/uuid.h +++ b/include/linux/uuid.h @@ -8,15 +8,25 @@ #ifndef _LINUX_UUID_H_ #define _LINUX_UUID_H_ -#include <uapi/linux/uuid.h> #include <linux/string.h> #define UUID_SIZE 16 typedef struct { __u8 b[UUID_SIZE]; +} guid_t; + +typedef struct { + __u8 b[UUID_SIZE]; } uuid_t; +#define GUID_INIT(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ +((guid_t) \ +{{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ + (b) & 0xff, ((b) >> 8) & 0xff, \ + (c) & 0xff, ((c) >> 8) & 0xff, \ + (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}) + #define UUID_INIT(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ ((uuid_t) \ {{ ((a) >> 24) & 0xff, ((a) >> 16) & 0xff, ((a) >> 8) & 0xff, (a) & 0xff, \ @@ -97,10 +107,7 @@ extern const u8 uuid_index[16]; int guid_parse(const char *uuid, guid_t *u); int uuid_parse(const char *uuid, uuid_t *u); -/* backwards compatibility, don't use in new code */ -static inline int uuid_le_cmp(const guid_t u1, const guid_t u2) -{ - return memcmp(&u1, &u2, sizeof(guid_t)); -} +/* MEI UUID type, don't use anywhere else */ +#include <uapi/linux/uuid.h> #endif diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index e72e4de8f452..5f636b5afcd7 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -450,7 +450,7 @@ enum binder_driver_return_protocol { BR_FROZEN_REPLY = _IO('r', 18), /* - * The target of the last transaction (either a bcTRANSACTION or + * The target of the last sync transaction (either a bcTRANSACTION or * a bcATTEMPT_ACQUIRE) is frozen. No parameters. */ @@ -460,6 +460,11 @@ enum binder_driver_return_protocol { * asynchronous transaction makes the allocated async buffer size exceed * detection threshold. No parameters. */ + + BR_TRANSACTION_PENDING_FROZEN = _IO('r', 20), + /* + * The target of the last async transaction is frozen. No parameters. + */ }; enum binder_driver_command_protocol { diff --git a/include/uapi/linux/uuid.h b/include/uapi/linux/uuid.h index c0f4bd9b040e..96ac684a4b2f 100644 --- a/include/uapi/linux/uuid.h +++ b/include/uapi/linux/uuid.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ /* DO NOT USE in new code! This is solely for MEI due to legacy reasons */ /* - * UUID/GUID definition + * MEI UUID definition * * Copyright (C) 2010, Intel Corp. * Huang Ying <ying.huang@intel.com> @@ -14,19 +14,15 @@ typedef struct { __u8 b[16]; -} guid_t; +} uuid_le; -#define GUID_INIT(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ -((guid_t) \ +#define UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ +((uuid_le) \ {{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ (b) & 0xff, ((b) >> 8) & 0xff, \ (c) & 0xff, ((c) >> 8) & 0xff, \ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}) -/* backwards compatibility, don't use in new code */ -typedef guid_t uuid_le; -#define UUID_LE(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ - GUID_INIT(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) #define NULL_UUID_LE \ UUID_LE(0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00) diff --git a/kernel/events/core.c b/kernel/events/core.c index c4be13e50547..d0cce1dd53b4 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -9394,6 +9394,7 @@ void perf_report_aux_output_id(struct perf_event *event, u64 hw_id) perf_output_end(&handle); } +EXPORT_SYMBOL_GPL(perf_report_aux_output_id); static int __perf_event_account_interrupt(struct perf_event *event, int throttle) diff --git a/scripts/tags.sh b/scripts/tags.sh index e137cf15aae9..1ad45f17179a 100755 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -17,6 +17,13 @@ ignore="$(echo "$RCS_FIND_IGNORE" | sed 's|\\||g' )" # tags and cscope files should also ignore MODVERSION *.mod.c files ignore="$ignore ( -name *.mod.c ) -prune -o" +# ignore arbitrary directories +if [ -n "${IGNORE_DIRS}" ]; then + for i in ${IGNORE_DIRS}; do + ignore="${ignore} ( -path $i ) -prune -o" + done +fi + # Use make KBUILD_ABS_SRCTREE=1 {tags|cscope} # to force full paths for a non-O= build if [ "${srctree}" = "." -o -z "${srctree}" ]; then diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index 8d35893b2fa8..6a00a6eecaef 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -264,6 +264,7 @@ int iioutils_get_param_float(float *output, const char *param_name, if (fscanf(sysfsfp, "%f", output) != 1) ret = errno ? -errno : -ENODATA; + fclose(sysfsfp); break; } error_free_filename: @@ -345,9 +346,9 @@ int build_channel_array(const char *device_dir, int buffer_idx, } sysfsfp = fopen(filename, "r"); + free(filename); if (!sysfsfp) { ret = -errno; - free(filename); goto error_close_dir; } @@ -357,7 +358,6 @@ int build_channel_array(const char *device_dir, int buffer_idx, if (fclose(sysfsfp)) perror("build_channel_array(): Failed to close file"); - free(filename); goto error_close_dir; } if (ret == 1) @@ -365,11 +365,9 @@ int build_channel_array(const char *device_dir, int buffer_idx, if (fclose(sysfsfp)) { ret = -errno; - free(filename); goto error_close_dir; } - free(filename); } *ci_array = malloc(sizeof(**ci_array) * (*counter)); @@ -395,9 +393,9 @@ int build_channel_array(const char *device_dir, int buffer_idx, } sysfsfp = fopen(filename, "r"); + free(filename); if (!sysfsfp) { ret = -errno; - free(filename); count--; goto error_cleanup_array; } @@ -405,20 +403,17 @@ int build_channel_array(const char *device_dir, int buffer_idx, errno = 0; if (fscanf(sysfsfp, "%i", ¤t_enabled) != 1) { ret = errno ? -errno : -ENODATA; - free(filename); count--; goto error_cleanup_array; } if (fclose(sysfsfp)) { ret = -errno; - free(filename); count--; goto error_cleanup_array; } if (!current_enabled) { - free(filename); count--; continue; } @@ -429,7 +424,6 @@ int build_channel_array(const char *device_dir, int buffer_idx, strlen(ent->d_name) - strlen("_en")); if (!current->name) { - free(filename); ret = -ENOMEM; count--; goto error_cleanup_array; @@ -439,7 +433,6 @@ int build_channel_array(const char *device_dir, int buffer_idx, ret = iioutils_break_up_name(current->name, ¤t->generic_name); if (ret) { - free(filename); free(current->name); count--; goto error_cleanup_array; @@ -450,17 +443,16 @@ int build_channel_array(const char *device_dir, int buffer_idx, scan_el_dir, current->name); if (ret < 0) { - free(filename); ret = -ENOMEM; goto error_cleanup_array; } sysfsfp = fopen(filename, "r"); + free(filename); if (!sysfsfp) { ret = -errno; - fprintf(stderr, "failed to open %s\n", - filename); - free(filename); + fprintf(stderr, "failed to open %s/%s_index\n", + scan_el_dir, current->name); goto error_cleanup_array; } @@ -470,17 +462,14 @@ int build_channel_array(const char *device_dir, int buffer_idx, if (fclose(sysfsfp)) perror("build_channel_array(): Failed to close file"); - free(filename); goto error_cleanup_array; } if (fclose(sysfsfp)) { ret = -errno; - free(filename); goto error_cleanup_array; } - free(filename); /* Find the scale */ ret = iioutils_get_param_float(¤t->scale, "scale", |