diff options
author | Linaro CI <ci_notify@linaro.org> | 2020-09-19 21:59:09 +0000 |
---|---|---|
committer | Linaro CI <ci_notify@linaro.org> | 2020-09-19 21:59:09 +0000 |
commit | dba97c7eeabad4fd8cef356f689a28d6a2bc53fc (patch) | |
tree | 9f2450dd4ded35d0feb609a516a46f806ad5415a | |
parent | cfbb440280f1dc7be488069b1e8aa67720c73a81 (diff) | |
parent | 6c73ce02692d9e32e34885368180a744079fa9f9 (diff) |
Merge remote-tracking branch 'sm8250/tracking-qcomlt-sm8250' into integration-linux-qcomltintegration-linux-qcomlt-20200920-093824-v5.9-rc5-435-gdba97c7eeaba
245 files changed, 24873 insertions, 1860 deletions
diff --git a/Documentation/devicetree/bindings/arm/qcom.yaml b/Documentation/devicetree/bindings/arm/qcom.yaml index 6031aee0f5a8..1adc8a33a3e4 100644 --- a/Documentation/devicetree/bindings/arm/qcom.yaml +++ b/Documentation/devicetree/bindings/arm/qcom.yaml @@ -40,6 +40,7 @@ description: | sdm630 sdm660 sdm845 + sm8250 The 'board' element must be one of the following strings: @@ -165,4 +166,10 @@ properties: - qcom,ipq6018-cp01-c1 - const: qcom,ipq6018 + - items: + - enum: + - qcom,qrb5165-rb5 + - qcom,sm8250-mtp + - const: qcom,sm8250 + ... diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.txt b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.txt index 33856947c561..aea4ddb2b9e8 100644 --- a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.txt +++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.txt @@ -8,7 +8,7 @@ Properties: - compatible Usage: required Value type: <string> - Definition: must be "qcom,cpufreq-hw". + Definition: must be "qcom,cpufreq-hw" or "qcom,sm8250-epss". - clocks Usage: required diff --git a/Documentation/devicetree/bindings/dma/qcom,gpi.yaml b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml new file mode 100644 index 000000000000..82f404bc8745 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/qcom,gpi.yaml @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/qcom,gpi.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies Inc GPI DMA controller + +maintainers: + - Vinod Koul <vkoul@kernel.org> + +description: | + QCOM GPI DMA controller provides DMA capabilities for + peripheral buses such as I2C, UART, and SPI. + +allOf: + - $ref: "dma-controller.yaml#" + +properties: + compatible: + enum: + - qcom,gpi-dma + + reg: + maxItems: 1 + + interrupts: + description: + Interrupt lines for each GPII instance + maxItems: 13 + + "#dma-cells": + const: 3 + description: > + DMA clients must use the format described in dma.txt, giving a phandle + to the DMA controller plus the following 3 integer cells: + - channel: if set to 0xffffffff, any available channel will be allocated + for the client. Otherwise, the exact channel specified will be used. + - seid: serial id of the client as defined in the SoC documentation. + - client: type of the client as defined in dt-bindings/dma/qcom-gpi.h + + iommus: + maxItems: 1 + + dma-channels: + maxItems: 1 + + dma-channel-mask: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - "#dma-cells" + - iommus + - dma-channels + - dma-channel-mask + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/dma/qcom-gpi.h> + gpi_dma0: dma-controller@800000 { + compatible = "qcom,gpi-dma"; + #dma-cells = <3>; + reg = <0x00800000 0x60000>; + iommus = <&apps_smmu 0x0016 0x0>; + dma-channels = <13>; + dma-channel-mask = <0xfa>; + interrupts = <GIC_SPI 244 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 245 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 246 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 247 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 248 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 249 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 250 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 251 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 252 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 253 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 254 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 255 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 256 IRQ_TYPE_LEVEL_HIGH>; + }; + +... diff --git a/Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml b/Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml new file mode 100644 index 000000000000..7b87ec328515 --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml @@ -0,0 +1,82 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/can/microchip,mcp25xxfd.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip MCP25XXFD stand-alone CAN controller binding + +maintainers: + - Martin Sperl <kernel@martin.sperl.org> + - Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> + +properties: + compatible: + const: microchip,mcp2517fd + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + + vdd-supply: + description: Regulator that powers the CAN controller + + xceiver-supply: + description: Regulator that powers the CAN transceiver + + microchip,clock-out-div: + description: Clock output pin divider + allOf: + - $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 4, 10] + default: 10 + + microchip,clock-div2: + description: Divide the internal clock by 2 + type: boolean + + microchip,gpio-open-drain: + description: Enable open-drain for all pins + type: boolean + +required: + - compatible + - reg + - clocks + - interrupts + - gpio-controller + - vdd-supply + - xceiver-supply + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + can0: can@1 { + compatible = "microchip,mcp2517fd"; + reg = <1>; + clocks = <&clk24m>; + interrupt-parent = <&gpio4>; + interrupts = <13 0x8>; + vdd-supply = <®5v0>; + xceiver-supply = <®5v0>; + gpio-controller; + #gpio-cells = <2>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/pci/qcom,pcie.txt b/Documentation/devicetree/bindings/pci/qcom,pcie.txt index 02bc81bb8b2d..7fc328c54c39 100644 --- a/Documentation/devicetree/bindings/pci/qcom,pcie.txt +++ b/Documentation/devicetree/bindings/pci/qcom,pcie.txt @@ -13,6 +13,7 @@ - "qcom,pcie-ipq8074" for ipq8074 - "qcom,pcie-qcs404" for qcs404 - "qcom,pcie-sdm845" for sdm845 + - "qcom,pcie-sm8250" for sm8250 - reg: Usage: required @@ -131,7 +132,7 @@ - "slave_bus" AXI Slave clock -clock-names: - Usage: required for sdm845 + Usage: required for sdm845 and sm8250 Value type: <stringlist> Definition: Should contain the following entries - "aux" Auxiliary clock @@ -206,7 +207,7 @@ - "ahb" AHB reset - reset-names: - Usage: required for sdm845 + Usage: required for sdm845 and sm8250 Value type: <stringlist> Definition: Should contain the following entries - "pci" PCIe core reset diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml index 185cdea9cf81..0aaa738bbbb2 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml @@ -31,6 +31,8 @@ properties: - qcom,sdm845-qmp-usb3-uni-phy - qcom,sm8150-qmp-ufs-phy - qcom,sm8250-qmp-ufs-phy + - qcom,sm8250-qmp-pcie-phy + - qcom,sm8250-qhp-pcie-phy reg: items: @@ -259,6 +261,8 @@ allOf: enum: - qcom,sdm845-qhp-pcie-phy - qcom,sdm845-qmp-pcie-phy + - qcom,sm8250-qhp-pcie-phy + - qcom,sm8250-qmp-pcie-phy then: properties: clocks: diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,wcnss-pil.txt b/Documentation/devicetree/bindings/remoteproc/qcom,wcnss-pil.txt index d420f84ddfb0..00844a5d2ccf 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,wcnss-pil.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,wcnss-pil.txt @@ -34,6 +34,12 @@ on the Qualcomm WCNSS core. Definition: should be "wdog", "fatal", optionally followed by "ready", "handover", "stop-ack" +- firmware-name: + Usage: optional + Value type: <string> + Definition: must list the relative firmware image path for the + WCNSS core. + - vddmx-supply: - vddcx-supply: - vddpx-supply: diff --git a/Documentation/devicetree/bindings/thermal/qcom-spmi-adc-tm5.yaml b/Documentation/devicetree/bindings/thermal/qcom-spmi-adc-tm5.yaml new file mode 100644 index 000000000000..94aaf3720b9f --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/qcom-spmi-adc-tm5.yaml @@ -0,0 +1,142 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/thermal/qcom-spmi-adc-tm5.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm's SPMI PMIC ADC-TM +maintainers: + - Dmitry Baryshkov <dmitry.baryshkov@linaro.org> + +properties: + compatible: + const: qcom,spmi-adc-tm5 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + "#thermal-sensor-cells": + const: 1 + description: + Number of cells required to uniquely identify the thermal sensors. Since + we have multiple sensors this is set to 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + io-channels: + description: + From common IIO binding. Used to pipe PMIC ADC channel to thermal monitor + + io-channel-names: + description: + From common IIO binding. Names each of IIO channels. The name should + be equal to the sensor's subnode name. + + qcom,avg-samples: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of samples to be used for measurement. + enum: + - 1 + - 2 + - 4 + - 8 + - 16 + default: 1 + + qcom,decimation: + $ref: /schemas/types.yaml#/definitions/uint32 + description: This parameter is used to decrease ADC sampling rate. + enum: + - 250 + - 420 + - 840 + default: 840 + +patternProperties: + "^([-a-z0-9]*)@[0-9]+$": + type: object + description: + Represent one thermal sensor. + + properties: + reg: + description: Specify the sensor channel. + maxItems: 1 + + qcom,adc-channel: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Corresponding ADC channel ID. + + qcom,ratiometric: + $ref: /schemas/types.yaml#/definitions/flag + description: + Channel calibration type. + If this property is specified VADC will use the VDD reference + (1.875V) and GND for channel calibration. If property is not found, + channel will be calibrated with 0V and 1.25V reference channels, + also known as absolute calibration. + + qcom,hw-settle-time: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Time between AMUX getting configured and the ADC starting conversion. + + qcom,pre-scaling: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Used for scaling the channel input signal before the signal is fed to VADC. See qcom,spi-vadc specification for the list of possible values. + minItems: 2 + maxItems: 2 + + required: + - reg + - qcom,adc-channel + + additionalProperties: + false + +required: + - compatible + - reg + - interrupts + - "#address-cells" + - "#size-cells" + - "#thermal-sensor-cells" + +additionalProperties: false + +examples: + - | + pm8150b_adc: adc@3100 { + compatible = "qcom,spmi-adc5"; + /* Other propreties are omitted */ + conn-therm@4f { + reg = <ADC5_AMUX_THM3_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + }; + + pm8150b_adc_tm: adc-tm@3500 { + compatible = "qcom,spmi-adc-tm5"; + reg = <0x3500>; + interrupts = <0x2 0x35 0x0 IRQ_TYPE_EDGE_RISING>; + #thermal-sensor-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + io-channels = <&pm8150b_adc ADC5_AMUX_THM3_100K_PU>; + io-channel-names = "conn-therm"; + + conn-therm@0 { + reg = <0>; + qcom,adc-channel = <ADC5_AMUX_THM3_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml b/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml new file mode 100644 index 000000000000..d5173f88d429 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/usb/qcom,pmic-typec.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm PMIC based USB type C Detection Driver + +maintainers: + - Wesley Cheng <wcheng@codeaurora.org> + +description: | + Qualcomm PMIC Type C Detect + +properties: + compatible: + enum: + - qcom,pm8150b-usb-typec + + reg: + maxItems: 1 + description: Type C base address + + interrupts: + maxItems: 1 + description: CC change interrupt from PMIC + + connector: + description: Connector type for remote endpoints + type: object + + properties: + compatible: + $ref: /connector/usb-connector.yaml#/properties/compatible + enum: + - usb-c-connector + + power-role: true + data-role: true + + ports: + description: Remote endpoint connections + type: object + $ref: /connector/usb-connector.yaml#/properties/ports + + properties: + port@0: + description: Remote endpoints for the High Speed path + type: object + + port@1: + description: Remote endpoints for the Super Speed path + type: object + + properties: + endpoint@0: + description: Connection to USB type C mux node + type: object + + endpoint@1: + description: Connection to role switch node + type: object + + required: + - compatible + +required: + - compatible + - interrupts + - connector + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + pm8150b { + #address-cells = <1>; + #size-cells = <0>; + pm8150b_typec: typec@1500 { + compatible = "qcom,pm8150b-usb-typec"; + reg = <0x1500>; + interrupts = <0x2 0x15 0x5 IRQ_TYPE_EDGE_RISING>; + + connector { + compatible = "usb-c-connector"; + power-role = "dual"; + data-role = "dual"; + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + }; + port@1 { + reg = <1>; + #address-cells = <1>; + #size-cells = <0>; + usb3_data_ss: endpoint@0 { + reg = <0>; + remote-endpoint = <&qmp_ss_mux>; + }; + usb3_role: endpoint@1 { + reg = <1>; + remote-endpoint = <&dwc3_drd_switch>; + }; + }; + }; + }; + }; + }; +... diff --git a/MAINTAINERS b/MAINTAINERS index 0d0862b19ce5..4c5872663feb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10660,6 +10660,14 @@ L: linux-input@vger.kernel.org S: Maintained F: drivers/hid/hid-mcp2221.c +MCP25XXFD CAN NETWORK DRIVER +M: Martin Sperl <kernel@martin.sperl.org> +M: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> +L: linux-can@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml +F: drivers/net/can/mcp25xxfd/ + MCP4018 AND MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVERS M: Peter Rosin <peda@axentia.se> L: linux-iio@vger.kernel.org diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile index d8f1466e6758..05d21ba311f9 100644 --- a/arch/arm64/boot/dts/qcom/Makefile +++ b/arch/arm64/boot/dts/qcom/Makefile @@ -35,3 +35,4 @@ dtb-$(CONFIG_ARCH_QCOM) += sm8150-mtp.dtb dtb-$(CONFIG_ARCH_QCOM) += sm8250-mtp.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs404-evb-1000.dtb dtb-$(CONFIG_ARCH_QCOM) += qcs404-evb-4000.dtb +dtb-$(CONFIG_ARCH_QCOM) += qrb5165-rb5.dtb diff --git a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi index 194343510dcb..6a19e8815f35 100644 --- a/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi +++ b/arch/arm64/boot/dts/qcom/apq8016-sbc.dtsi @@ -519,6 +519,7 @@ wcnss@a21b000 { status = "okay"; + firmware-name = "qcom/msm8916/wcnss.mdt"; }; tpiu@820000 { status = "okay"; }; @@ -636,6 +637,11 @@ }; }; +&mpss { + status = "okay"; + firmware-name = "qcom/msm8916/mba.mbn", "qcom/msm8916/modem.mdt"; +}; + &spmi_bus { pm8916_0: pm8916@0 { pon@800 { diff --git a/arch/arm64/boot/dts/qcom/msm8916.dtsi b/arch/arm64/boot/dts/qcom/msm8916.dtsi index 67cae5f9e47e..0e281d572d2e 100644 --- a/arch/arm64/boot/dts/qcom/msm8916.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8916.dtsi @@ -6,6 +6,7 @@ #include <dt-bindings/arm/coresight-cti-dt.h> #include <dt-bindings/interconnect/qcom,msm8916.h> #include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/interconnect/qcom,msm8916.h> #include <dt-bindings/clock/qcom,gcc-msm8916.h> #include <dt-bindings/reset/qcom,gcc-msm8916.h> #include <dt-bindings/clock/qcom,rpmcc.h> @@ -1144,7 +1145,7 @@ }; - hexagon@4080000 { + mpss: remoteproc@4080000 { compatible = "qcom,q6v5-pil"; reg = <0x04080000 0x100>, <0x04020000 0x040>; diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi index 56f02d6f5c5c..ed9bf4ec525d 100644 --- a/arch/arm64/boot/dts/qcom/msm8996.dtsi +++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi @@ -1004,7 +1004,8 @@ "ispif", "vfe0", "vfe1"; - power-domains = <&mmcc VFE0_GDSC>; + power-domains = <&mmcc VFE0_GDSC>, + <&mmcc VFE1_GDSC>; clocks = <&mmcc CAMSS_TOP_AHB_CLK>, <&mmcc CAMSS_ISPIF_AHB_CLK>, <&mmcc CAMSS_CSI0PHYTIMER_CLK>, diff --git a/arch/arm64/boot/dts/qcom/pm8150.dtsi b/arch/arm64/boot/dts/qcom/pm8150.dtsi index 1b6406927509..15e87153a194 100644 --- a/arch/arm64/boot/dts/qcom/pm8150.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8150.dtsi @@ -97,7 +97,17 @@ }; }; - rtc@6000 { + pm8150_adc_tm: adc-tm@3500 { + compatible = "qcom,spmi-adc-tm5"; + reg = <0x3500>; + interrupts = <0x0 0x35 0x0 IRQ_TYPE_EDGE_RISING>; + #thermal-sensor-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + pm8150_rtc: rtc@6000 { compatible = "qcom,pm8941-rtc"; reg = <0x6000>; reg-names = "rtc", "alarm"; diff --git a/arch/arm64/boot/dts/qcom/pm8150b.dtsi b/arch/arm64/boot/dts/qcom/pm8150b.dtsi index e112e8876db6..d6e075f92b1e 100644 --- a/arch/arm64/boot/dts/qcom/pm8150b.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8150b.dtsi @@ -53,6 +53,19 @@ status = "disabled"; }; + pm8150b_vbus: dcdc@1100 { + compatible = "qcom,pm8150b-vbus-reg"; + status = "disabled"; + reg = <0x1100>; + }; + + pm8150b_typec: typec@1500 { + compatible = "qcom,pm8150b-usb-typec"; + status = "disabled"; + reg = <0x1500>; + interrupts = <0x2 0x15 0x5 IRQ_TYPE_EDGE_RISING>; + }; + pm8150b_temp: temp-alarm@2400 { compatible = "qcom,spmi-temp-alarm"; reg = <0x2400>; @@ -95,6 +108,16 @@ }; }; + pm8150b_adc_tm: adc-tm@3500 { + compatible = "qcom,spmi-adc-tm5"; + reg = <0x3500>; + interrupts = <0x2 0x35 0x0 IRQ_TYPE_EDGE_RISING>; + #thermal-sensor-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + pm8150b_gpios: gpio@c000 { compatible = "qcom,pm8150b-gpio"; reg = <0xc000>; diff --git a/arch/arm64/boot/dts/qcom/pm8150l.dtsi b/arch/arm64/boot/dts/qcom/pm8150l.dtsi index 62139538b7d9..9f214ceec2b7 100644 --- a/arch/arm64/boot/dts/qcom/pm8150l.dtsi +++ b/arch/arm64/boot/dts/qcom/pm8150l.dtsi @@ -89,6 +89,16 @@ }; }; + pm8150l_adc_tm: adc-tm@3500 { + compatible = "qcom,spmi-adc-tm5"; + reg = <0x3500>; + interrupts = <0x4 0x35 0x0 IRQ_TYPE_EDGE_RISING>; + #thermal-sensor-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + pm8150l_gpios: gpio@c000 { compatible = "qcom,pm8150l-gpio"; reg = <0xc000>; diff --git a/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts new file mode 100644 index 000000000000..f0dad4dd34ec --- /dev/null +++ b/arch/arm64/boot/dts/qcom/qrb5165-rb5.dts @@ -0,0 +1,1394 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2020, Linaro Ltd. + */ + +/dts-v1/; + +#include <dt-bindings/gpio/gpio.h> +#include <dt-bindings/regulator/qcom,rpmh-regulator.h> +#include <dt-bindings/sound/qcom,q6afe.h> +#include <dt-bindings/sound/qcom,q6asm.h> +#include "sm8250.dtsi" +#include "pm8150.dtsi" +#include "pm8150b.dtsi" +#include "pm8150l.dtsi" + +/ { + model = "Qualcomm Technologies, Inc. Robotics RB5"; + compatible = "qcom,qrb5165-rb5", "qcom,sm8250"; + + aliases { + serial0 = &uart12; + hsuart0 = &uart6; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + dc12v: dc12v-regulator { + compatible = "regulator-fixed"; + regulator-name = "DC12V"; + regulator-min-microvolt = <12000000>; + regulator-max-microvolt = <12000000>; + regulator-always-on; + }; + + hdmi-out { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_con: endpoint { + remote-endpoint = <<9611_out>; + }; + }; + }; + + leds { + compatible = "gpio-leds"; + + user4 { + label = "green:user4"; + gpios = <&pm8150_gpios 10 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "panic-indicator"; + default-state = "off"; + }; + + wlan { + label = "yellow:wlan"; + gpios = <&pm8150_gpios 9 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "phy0tx"; + default-state = "off"; + }; + + bt { + label = "blue:bt"; + gpios = <&pm8150_gpios 7 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "bluetooth-power"; + default-state = "off"; + }; + + }; + + lt9611_1v2: lt9611-vdd12-regulator { + compatible = "regulator-fixed"; + regulator-name = "LT9611_1V2"; + + vin-supply = <&vdc_3v3>; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + + /* not connected on the board + gpio = <&pm8150l_gpios 7 GPIO_ACTIVE_HIGH>; + enable-active-high; + */ + regulator-boot-on; + regulator-always-on; + }; + + lt9611_3v3: lt9611-3v3 { + compatible = "regulator-fixed"; + regulator-name = "LT9611_3V3"; + + vin-supply = <&vdc_3v3>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + + // TODO: make it possible to drive same GPIO from two clients + // gpio = <&pm8150l_gpios 7 GPIO_ACTIVE_HIGH>; + // enable-active-high; + }; + + thermal-zones { + xo-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8150_adc_tm 0>; + trips { + active-config0 { + temperature = <50000>; + hysteresis = <4000>; + type = "passive"; + }; + }; + }; + + wifi-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8150_adc_tm 1>; + trips { + active-config0 { + temperature = <52000>; + hysteresis = <4000>; + type = "passive"; + }; + }; + }; + + conn-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8150b_adc_tm 0>; + + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + }; + + skin-msm-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8150l_adc_tm 0>; + + trips { + active-config0 { + temperature = <50000>; + hysteresis = <4000>; + type = "passive"; + }; + }; + }; + + pm8150l-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8150l_adc_tm 1>; + + trips { + active-config0 { + temperature = <50000>; + hysteresis = <4000>; + type = "passive"; + }; + }; + }; + + }; + + vbat: vbat-regulator { + compatible = "regulator-fixed"; + regulator-name = "VBAT"; + vin-supply = <&vreg_l11c_3p3>; + regulator-min-microvolt = <4200000>; + regulator-max-microvolt = <4200000>; + regulator-always-on; + }; + + vbat_som: vbat-som-regulator { + compatible = "regulator-fixed"; + regulator-name = "VBAT_SOM"; + vin-supply = <&dc12v>; + regulator-min-microvolt = <4200000>; + regulator-max-microvolt = <4200000>; + regulator-always-on; + }; + + vdc_3v3: vdc-3v3-regulator { + compatible = "regulator-fixed"; + regulator-name = "VDC_3V3"; + vin-supply = <&vreg_l11c_3p3>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; + + vdc_5v: vdc-5v-regulator { + compatible = "regulator-fixed"; + regulator-name = "VDC_5V"; + + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-always-on; + vin-supply = <&vreg_l11c_3p3>; + }; + + vph_pwr: vph-pwr-regulator { + compatible = "regulator-fixed"; + regulator-name = "vph_pwr"; + regulator-min-microvolt = <3700000>; + regulator-max-microvolt = <3700000>; + regulator-always-on; + }; + + vreg_s4a_1p8: vreg-s4a-1p8 { + compatible = "regulator-fixed"; + regulator-name = "vreg_s4a_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + }; + + dummy { + compatible = "qcom-dummy"; + vddaon-supply = <&vreg_s6a_0p95>; + vddpmu-supply = <&vreg_s2f_0p95>; + vddrfa1-supply = <&vreg_s2f_0p95>; + vddrfa2-supply = <&vreg_s8c_1p3>; + vddrfa3-supply = <&vreg_s5a_1p9>; + vddpcie1-supply = <&vreg_s8c_1p3>; + vddpcie2-supply = <&vreg_s5a_1p9>; + vddio-supply = <&vreg_s4a_1p8>; + pinctrl-names = "wlan_en_active", "bt_en_active"; + pinctrl-0 = <&wlan_default_state>; + pinctrl-1 = <&bt_default_state>; + }; +}; + +&apps_rsc { + pm8009-rpmh-regulators { + compatible = "qcom,pm8009-rpmh-regulators"; + qcom,pmic-id = "f"; + + vdd-s1-supply = <&vph_pwr>; + vdd-s2-supply = <&vph_pwr>; + vdd-l2-supply = <&vreg_s8c_1p3>; + vdd-l5-l6-supply = <&vreg_bob>; + vdd-l7-supply = <&vreg_s4a_1p8>; + + vreg_s2f_0p95: smps2 { + regulator-name = "vreg_s2f_0p95"; + regulator-min-microvolt = <512000>; + regulator-max-microvolt = <1100000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_AUTO>; + }; + + vreg_l1f_1p1: ldo1 { + regulator-name = "vreg_l1f_1p1"; + regulator-min-microvolt = <1104000>; + regulator-max-microvolt = <1104000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l2f_1p2: ldo2 { + regulator-name = "vreg_l2f_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l6f_2p8: ldo6 { + regulator-name = "vreg_l6f_2p8"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l7f_1p8: ldo7 { + regulator-name = "vreg_l7f_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + }; + + pm8150-rpmh-regulators { + compatible = "qcom,pm8150-rpmh-regulators"; + qcom,pmic-id = "a"; + + vdd-s1-supply = <&vph_pwr>; + vdd-s2-supply = <&vph_pwr>; + vdd-s3-supply = <&vph_pwr>; + vdd-s4-supply = <&vph_pwr>; + vdd-s5-supply = <&vph_pwr>; + vdd-s6-supply = <&vph_pwr>; + vdd-s7-supply = <&vph_pwr>; + vdd-s8-supply = <&vph_pwr>; + vdd-s9-supply = <&vph_pwr>; + vdd-s10-supply = <&vph_pwr>; + vdd-l2-l10-supply = <&vreg_bob>; + vdd-l3-l4-l5-l18-supply = <&vreg_s6a_0p95>; + vdd-l6-l9-supply = <&vreg_s8c_1p3>; + vdd-l7-l12-l14-l15-supply = <&vreg_s5a_1p9>; + vdd-l13-l16-l17-supply = <&vreg_bob>; + + vreg_l2a_3p1: ldo2 { + regulator-name = "vreg_l2a_3p1"; + regulator-min-microvolt = <3072000>; + regulator-max-microvolt = <3072000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l3a_0p9: ldo3 { + regulator-name = "vreg_l3a_0p9"; + regulator-min-microvolt = <928000>; + regulator-max-microvolt = <932000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l5a_0p88: ldo5 { + regulator-name = "vreg_l5a_0p88"; + regulator-min-microvolt = <880000>; + regulator-max-microvolt = <880000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l6a_1p2: ldo6 { + regulator-name = "vreg_l6a_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l7a_1p7: ldo7 { + regulator-name = "vreg_l7a_1p7"; + regulator-min-microvolt = <1704000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l9a_1p2: ldo9 { + regulator-name = "vreg_l9a_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l10a_1p8: ldo10 { + regulator-name = "vreg_l10a_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l12a_1p8: ldo12 { + regulator-name = "vreg_l12a_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l13a_ts_3p0: ldo13 { + regulator-name = "vreg_l13a_ts_3p0"; + regulator-min-microvolt = <3008000>; + regulator-max-microvolt = <3008000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l14a_1p8: ldo14 { + regulator-name = "vreg_l14a_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1880000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l15a_1p8: ldo15 { + regulator-name = "vreg_l15a_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l16a_2p7: ldo16 { + regulator-name = "vreg_l16a_2p7"; + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <2960000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l17a_3p0: ldo17 { + regulator-name = "vreg_l17a_3p0"; + regulator-min-microvolt = <2856000>; + regulator-max-microvolt = <3008000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l18a_0p92: ldo18 { + regulator-name = "vreg_l18a_0p92"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_s5a_1p9: smps5 { + regulator-name = "vreg_s5a_1p9"; + regulator-min-microvolt = <1904000>; + regulator-max-microvolt = <2000000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_s6a_0p95: smps6 { + regulator-name = "vreg_s6a_0p95"; + regulator-min-microvolt = <920000>; + regulator-max-microvolt = <1128000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + }; + + pm8150l-rpmh-regulators { + compatible = "qcom,pm8150l-rpmh-regulators"; + qcom,pmic-id = "c"; + + vdd-s1-supply = <&vph_pwr>; + vdd-s2-supply = <&vph_pwr>; + vdd-s3-supply = <&vph_pwr>; + vdd-s4-supply = <&vph_pwr>; + vdd-s5-supply = <&vph_pwr>; + vdd-s6-supply = <&vph_pwr>; + vdd-s7-supply = <&vph_pwr>; + vdd-s8-supply = <&vph_pwr>; + vdd-l1-l8-supply = <&vreg_s4a_1p8>; + vdd-l2-l3-supply = <&vreg_s8c_1p3>; + vdd-l4-l5-l6-supply = <&vreg_bob>; + vdd-l7-l11-supply = <&vreg_bob>; + vdd-l9-l10-supply = <&vreg_bob>; + vdd-bob-supply = <&vph_pwr>; + + vreg_bob: bob { + regulator-name = "vreg_bob"; + regulator-min-microvolt = <3008000>; + regulator-max-microvolt = <4000000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_AUTO>; + }; + + vreg_l1c_1p8: ldo1 { + regulator-name = "vreg_l1c_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l2c_1p2: ldo2 { + regulator-name = "vreg_l2c_1p2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l3c_0p8: ldo3 { + regulator-name = "vreg_l3c_0p8"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <800000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l4c_1p7: ldo4 { + regulator-name = "vreg_l4c_1p7"; + regulator-min-microvolt = <1704000>; + regulator-max-microvolt = <2928000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l5c_1p8: ldo5 { + regulator-name = "vreg_l5c_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2928000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l6c_2p96: ldo6 { + regulator-name = "vreg_l6c_2p96"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2960000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l7c_cam_vcm0_2p85: ldo7 { + regulator-name = "vreg_l7c_cam_vcm0_2p85"; + regulator-min-microvolt = <2856000>; + regulator-max-microvolt = <3104000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l8c_1p8: ldo8 { + regulator-name = "vreg_l8c_1p8"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l9c_2p96: ldo9 { + regulator-name = "vreg_l9c_2p96"; + regulator-min-microvolt = <2704000>; + regulator-max-microvolt = <2960000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l10c_3p0: ldo10 { + regulator-name = "vreg_l10c_3p0"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + + vreg_l11c_3p3: ldo11 { + regulator-name = "vreg_l11c_3p3"; + regulator-min-microvolt = <3296000>; + regulator-max-microvolt = <3296000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + regulator-always-on; + }; + + vreg_s8c_1p3: smps8 { + regulator-name = "vreg_s8c_1p3"; + regulator-min-microvolt = <1352000>; + regulator-max-microvolt = <1352000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; + }; +}; + +&adsp { + status = "okay"; + firmware-name = "qcom/sm8250/adsp.mdt"; +}; + +&q6afedai { + qi2s@16 { + reg = <16>; + qcom,sd-lines = <0 1 2 3>; + }; +}; + +/* TERT I2S Uses 1 I2S SD Lines for audio on LT9611 HDMI Bridge */ +&q6afedai { + qi2s@20 { + reg = <20>; + qcom,sd-lines = <0>; + }; +}; + +&q6asmdai { + dai@0 { + reg = <0>; + }; + + dai@1 { + reg = <1>; + }; +}; + +&swr0 { + + left_spkr: wsa8810-left{ + compatible = "sdw10217211000"; + reg = <0 3>; + powerdown-gpios = <&tlmm 130 GPIO_ACTIVE_HIGH>; + #thermal-sensor-cells = <0>; + sound-name-prefix = "SpkrLeft"; + #sound-dai-cells = <0>; + }; + + + right_spkr: wsa8810-right{ + compatible = "sdw10217211000"; + reg = <0 4>; + powerdown-gpios = <&tlmm 130 GPIO_ACTIVE_HIGH>; + #thermal-sensor-cells = <0>; + sound-name-prefix = "SpkrRight"; + #sound-dai-cells = <0>; + }; +}; + +&sound { + compatible = "qcom,sm8250-sndcard"; + pinctrl-0 = <&tert_mi2s_sck_active + &tert_mi2s_sd0_active + &tert_mi2s_ws_active>; + pinctrl-names = "default"; + model = "RB5"; + audio-routing = + "SpkrLeft IN", "WSA_SPK1 OUT", + "SpkrRight IN", "WSA_SPK2 OUT", + "MM_DL1", "MultiMedia1 Playback", + "MM_DL2", "MultiMedia2 Playback"; + + mm1-dai-link { + link-name = "MultiMedia1"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; + }; + }; + + mm2-dai-link { + link-name = "MultiMedia2"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA2>; + }; + }; + + hdmi-dai-link { + link-name = "HDMI Playback"; + cpu { + sound-dai = <&q6afedai TERTIARY_MI2S_RX>; + }; + + platform { + sound-dai = <&q6routing>; + }; + + codec { + sound-dai = <<9611_codec 0>; + }; + }; + + dma-dai-link { + link-name = "WSA Playback"; + cpu { + sound-dai = <&q6afedai WSA_CODEC_DMA_RX_0>; + }; + + platform { + sound-dai = <&q6routing>; + }; + + codec { + sound-dai = <&left_spkr>, <&right_spkr>, <&swr0 0>, <&wsamacro>; + }; + }; +}; + +&cdsp { + status = "okay"; + firmware-name = "qcom/sm8250/cdsp.mdt"; +}; + +&dsi0 { + status = "okay"; + vdda-supply = <&vreg_l9a_1p2>; + +#if 0 + qcom,dual-dsi-mode; + qcom,master-dsi; +#endif + + ports { + port@1 { + endpoint { + remote-endpoint = <<9611_a>; + data-lanes = <0 1 2 3>; + }; + }; + }; +}; + +&dsi0_phy { + status = "okay"; + vdds-supply = <&vreg_l5a_0p88>; +}; + +&gpu { + zap-shader { + memory-region = <&gpu_mem>; + firmware-name = "qcom/a650_zap.mdt"; + }; +}; + +/* LS-I2C0 */ +&i2c4 { + status = "okay"; +}; + +&i2c5 { + status = "okay"; + clock-frequency = <400000>; + + lt9611_codec: hdmi-bridge@2b { + compatible = "lontium,lt9611uxc"; + reg = <0x2b>; + #sound-dai-cells = <1>; + + interrupts-extended = <&tlmm 63 IRQ_TYPE_EDGE_FALLING>; + + reset-gpios = <&pm8150l_gpios 5 GPIO_ACTIVE_HIGH>; + + vdd-supply = <<9611_1v2>; + vcc-supply = <<9611_3v3>; + + pinctrl-names = "default"; + pinctrl-0 = <<9611_irq_pin <9611_rst_pin>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + lt9611_out: endpoint { + remote-endpoint = <&hdmi_con>; + }; + }; + + port@1 { + reg = <1>; + + lt9611_a: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + +#if 0 + port@2 { + reg = <2>; + + lt9611_b: endpoint { + remote-endpoint = <&dsi1_out>; + }; + }; +#endif + }; + }; +}; + +/* LS-I2C1 */ +&i2c15 { + status = "okay"; +}; + +&mdss { + status = "okay"; +}; + +&mdss_mdp { + status = "okay"; +}; + +&pcie0 { + status = "okay"; + perst-gpio = <&tlmm 79 GPIO_ACTIVE_LOW>; + wake-gpio = <&tlmm 81 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pcie0_default_state>; +}; + +&pcie0_phy { + status = "okay"; + vdda-phy-supply = <&vreg_l5a_0p88>; + vdda-pll-supply = <&vreg_l9a_1p2>; +}; + +&pcie1 { + status = "okay"; + perst-gpio = <&tlmm 82 GPIO_ACTIVE_LOW>; + wake-gpio = <&tlmm 84 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pcie1_default_state>; +}; + +&pcie1_phy { + status = "okay"; + vdda-phy-supply = <&vreg_l5a_0p88>; + vdda-pll-supply = <&vreg_l9a_1p2>; +}; + +&pcie2 { + status = "okay"; + perst-gpio = <&tlmm 85 GPIO_ACTIVE_LOW>; + wake-gpio = <&tlmm 87 GPIO_ACTIVE_HIGH>; + pinctrl-names = "default"; + pinctrl-0 = <&pcie2_default_state>; +}; + +&pcie2_phy { + status = "okay"; + vdda-phy-supply = <&vreg_l5a_0p88>; + vdda-pll-supply = <&vreg_l9a_1p2>; +}; + +&pm8150_adc { + xo-therm@4c { + reg = <ADC5_XO_THERM_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + wifi-therm@4e { + reg = <ADC5_AMUX_THM2_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm8150b_adc { + conn-therm@4f { + reg = <ADC5_AMUX_THM3_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm8150l_adc { + skin-msm-therm@4e { + reg = <ADC5_AMUX_THM2_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + pm8150l-therm@4f { + reg = <ADC5_AMUX_THM3_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm8150_adc_tm { + status = "okay"; + io-channels = <&pm8150_adc ADC5_XO_THERM_100K_PU>, + <&pm8150_adc ADC5_AMUX_THM2_100K_PU>; + io-channel-names = "xo-therm", "wifi-therm"; + + xo-therm@0 { + reg = <0>; + qcom,adc-channel = <ADC5_XO_THERM_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + wifi-therm@1 { + reg = <1>; + qcom,adc-channel = <ADC5_AMUX_THM2_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm8150b_adc_tm { + status = "okay"; + io-channels = <&pm8150b_adc ADC5_AMUX_THM3_100K_PU>; + io-channel-names = "conn-therm"; + + conn-therm@0 { + reg = <0>; + qcom,adc-channel = <ADC5_AMUX_THM3_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm8150l_adc_tm { + status = "okay"; + io-channels = <&pm8150l_adc ADC5_AMUX_THM2_100K_PU>, + <&pm8150l_adc ADC5_AMUX_THM3_100K_PU>; + io-channel-names = "skin-msm-therm", "pm8150l-therm"; + + skin-msm-therm@0 { + reg = <0>; + qcom,adc-channel = <ADC5_AMUX_THM2_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + pm8150l-therm@1 { + reg = <1>; + qcom,adc-channel = <ADC5_AMUX_THM3_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm8150_gpios { + gpio-reserved-ranges = <1 1>, <3 2>, <7 1>; + gpio-line-names = + "NC", + "OPTION2", + "PM_GPIO-F", + "PM_SLP_CLK_IN", + "OPTION1", + "VOL_UP_N", + "PM8250_GPIO7", /* Blue LED */ + "SP_ARI_PWR_ALARM", + "GPIO_9_P", /* Yellow LED */ + "GPIO_10_P"; /* Green LED */ +}; + +&pm8150b_gpios { + gpio-line-names = + "NC", + "NC", + "NC", + "NC", + "HAP_BOOST_EN", /* SOM */ + "SMB_STAT", /* SOM */ + "NC", + "NC", + "SDM_FORCE_USB_BOOT", + "NC", + "NC", + "NC"; +}; + +&pm8150b_typec { + status = "okay"; + connector { + compatible = "usb-c-connector"; + power-role = "dual"; + data-role = "dual"; + ports { + #address-cells = <1>; + #size-cells = <0>; + port@1 { + reg = <1>; + usb3_data_ss: endpoint@0 { + remote-endpoint = <&qmp_ss_mux>; + }; + usb3_role: endpoint@1 { + remote-endpoint = <&dwc3_drd_switch>; + }; + }; + }; + }; +}; + +&pm8150b_vbus { + status = "okay"; +}; + +&pm8150l_gpios { + gpio-line-names = + "NC", + "PM3003A_EN", + "NC", + "NC", + "PM_GPIO5", /* HDMI RST_N */ + "PM_GPIO-A", /* PWM */ + "PM_GPIO7", + "NC", + "NC", + "PM_GPIO-B", + "NC", + "PM3003A_MODE"; + + lt9611_rst_pin: lt9611-rst-pin { + pins = "gpio5"; + function = "normal"; + + output-high; + input-disable; + power-source = <0>; + }; +}; + +&pm8150_rtc { + status = "okay"; +}; + +&qupv3_id_0 { + status = "okay"; +}; + +&qupv3_id_1 { + status = "okay"; +}; + +&qupv3_id_2 { + status = "okay"; +}; + +&qup_uart6_default { + ctsrx { + pins = "gpio16", "gpio19"; + drive-strength = <2>; + bias-disable; + }; + + rts { + pins = "gpio17"; + drive-strength = <2>; + bias-disable; + }; + + tx { + pins = "gpio18"; + drive-strength = <2>; + bias-pull-up; + }; +}; + + +&slpi { + status = "okay"; + firmware-name = "qcom/sm8250/slpi.mdt"; +}; + +/* CAN */ +&spi0 { + status = "okay"; +}; + +&tlmm { + gpio-reserved-ranges = <40 4>; + gpio-line-names = + "GPIO-MM", + "GPIO-NN", + "GPIO-OO", + "GPIO-PP", + "GPIO-A", + "GPIO-C", + "GPIO-E", + "GPIO-D", + "I2C0-SDA", + "I2C0-SCL", + "GPIO-TT", /* GPIO_10 */ + "NC", + "GPIO_12_I2C_SDA", + "GPIO_13_I2C_SCL", + "GPIO-X", + "GPIO_15_RGMII_INT", + "HST_BT_UART_CTS", + "HST_BT_UART_RFR", + "HST_BT_UART_TX", + "HST_BT_UART_RX", + "HST_WLAN_EN", /* GPIO_20 */ + "HST_BT_EN", + "GPIO-AAA", + "GPIO-BBB", + "GPIO-CCC", + "GPIO-Z", + "GPIO-DDD", + "GPIO-BB", + "GPIO_28_CAN_SPI_MISO", + "GPIO_29_CAN_SPI_MOSI", + "GPIO_30_CAN_SPI_CLK", /* GPIO_30 */ + "GPIO_31_CAN_SPI_CS", + "GPIO-UU", + "NC", + "UART1_TXD_SOM", + "UART1_RXD_SOM", + "UART0_CTS", + "UART0_RTS", + "UART0_TXD", + "UART0_RXD", + "SPI1_MISO", /* GPIO_40 */ + "SPI1_MOSI", + "SPI1_CLK", + "SPI1_CS", + "I2C1_SDA", + "I2C1_SCL", + "GPIO-F", + "GPIO-JJ", + "Board_ID1", + "Board_ID2", + "NC", /* GPIO_50 */ + "NC", + "SPI0_MISO", + "SPI0_MOSI", + "SPI0_SCLK", + "SPI0_CS", + "GPIO-QQ", + "GPIO-RR", + "USB2LAN_RESET", + "USB2LAN_EXTWAKE", + "NC", /* GPIO_60 */ + "NC", + "NC", + "LT9611_INT", + "GPIO-AA", + "USB_CC_DIR", + "GPIO-G", + "GPIO-LL", + "USB_DP_HPD_1P8", + "NC", + "NC", /* GPIO_70 */ + "SD_CMD", + "SD_DAT3", + "SD_SCLK", + "SD_DAT2", + "SD_DAT1", + "SD_DAT0", /* BOOT_CFG3 */ + "SD_UFS_CARD_DET_N", + "GPIO-II", + "PCIE0_RST_N", + "PCIE0_CLK_REQ_N", /* GPIO_80 */ + "PCIE0_WAKE_N", + "GPIO-CC", + "GPIO-DD", + "GPIO-EE", + "GPIO-FF", + "GPIO-GG", + "GPIO-HH", + "GPIO-VV", + "GPIO-WW", + "NC", /* GPIO_90 */ + "NC", + "GPIO-K", + "GPIO-I", + "CSI0_MCLK", + "CSI1_MCLK", + "CSI2_MCLK", + "CSI3_MCLK", + "GPIO-AA", /* CSI4_MCLK */ + "GPIO-BB", /* CSI5_MCLK */ + "GPIO-KK", /* GPIO_100 */ + "CCI_I2C_SDA0", + "CCI_I2C_SCL0", + "CCI_I2C_SDA1", + "CCI_I2C_SCL1", + "CCI_I2C_SDA2", + "CCI_I2C_SCL2", + "CCI_I2C_SDA3", + "CCI_I2C_SCL3", + "GPIO-L", + "NC", /* GPIO_110 */ + "NC", + "ACCEL_INT", + "GYRO_INT", + "GPIO-J", + "GPIO-YY", + "GPIO-H", + "GPIO-ZZ", + "NC", + "NC", + "NC", /* GPIO_120 */ + "NC", + "MAG_INT", + "MAG_DRDY_INT", + "HST_SW_CTRL", + "GPIO-M", + "GPIO-N", + "GPIO-O", + "GPIO-P", + "PS_INT", + "WSA1_EN", /* GPIO_130 */ + "USB_HUB_RESET", + "SDM_FORCE_USB_BOOT", + "I2S1_CLK_HDMI", + "I2S1_DATA0_HDMI", + "I2S1_WS_HDMI", + "GPIO-B", + "GPIO_137", /* To LT9611_I2S_MCLK_3V3 */ + "PCM_CLK", + "PCM_DI", + "PCM_DO", /* GPIO_140 */ + "PCM_FS", + "HST_SLIM_CLK", + "HST_SLIM_DATA", + "GPIO-U", + "GPIO-Y", + "GPIO-R", + "GPIO-Q", + "GPIO-S", + "GPIO-T", + "GPIO-V", /* GPIO_150 */ + "GPIO-W", + "DMIC_CLK1", + "DMIC_DATA1", + "DMIC_CLK2", + "DMIC_DATA2", + "WSA_SWR_CLK", + "WSA_SWR_DATA", + "DMIC_CLK3", + "DMIC_DATA3", + "I2C4_SDA", /* GPIO_160 */ + "I2C4_SCL", + "SPI3_CS1", + "SPI3_CS2", + "SPI2_MISO_LS3", + "SPI2_MOSI_LS3", + "SPI2_CLK_LS3", + "SPI2_ACCEL_CS_LS3", + "SPI2_CS1", + "NC", + "GPIO-SS", /* GPIO_170 */ + "GPIO-XX", + "SPI3_MISO", + "SPI3_MOSI", + "SPI3_CLK", + "SPI3_CS", + "HST_BLE_SNS_UART_TX", + "HST_BLE_SNS_UART_RX", + "HST_WLAN_UART_TX", + "HST_WLAN_UART_RX"; + + lt9611_irq_pin: lt9611-irq { + pins = "gpio63"; + function = "gpio"; + bias-disable; + }; + + pcie0_default_state: pcie0-default { + clkreq { + pins = "gpio80"; + function = "pci_e0"; + bias-pull-up; + }; + + reset-n { + pins = "gpio79"; + function = "gpio"; + + drive-strength = <2>; + output-low; + bias-pull-down; + }; + + wake-n { + pins = "gpio81"; + function = "gpio"; + + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie1_default_state: pcie1-default { + clkreq { + pins = "gpio83"; + function = "pci_e1"; + bias-pull-up; + }; + + reset-n { + pins = "gpio82"; + function = "gpio"; + + drive-strength = <2>; + output-low; + bias-pull-down; + }; + + wake-n { + pins = "gpio84"; + function = "gpio"; + + drive-strength = <2>; + bias-pull-up; + }; + }; + + pcie2_default_state: pcie2-default { + clkreq { + pins = "gpio86"; + function = "pci_e2"; + bias-pull-up; + }; + + reset-n { + pins = "gpio85"; + function = "gpio"; + + drive-strength = <2>; + output-low; + bias-pull-down; + }; + + wake-n { + pins = "gpio87"; + function = "gpio"; + + drive-strength = <2>; + bias-pull-up; + }; + }; + + wlan_default_state: wlan-default-state { + wlan-en { + pins = "gpio20"; + function = "gpio"; + + drive-strength = <16>; + output-high; + bias-pull-up; + }; + }; + + bt_default_state: bt-default-state { + bt-en { + pins = "gpio21"; + function = "gpio"; + + drive-strength = <16>; + output-high; + bias-pull-up; + }; + }; + +}; + +&uart6 { + status = "okay"; + bluetooth { + compatible = "qcom,qca6391-bt"; + }; +}; + +&uart12 { + status = "okay"; +}; + +&ufs_mem_hc { + status = "okay"; + + vcc-supply = <&vreg_l17a_3p0>; + vcc-max-microamp = <800000>; + vccq-supply = <&vreg_l6a_1p2>; + vccq-max-microamp = <800000>; + vccq2-supply = <&vreg_s4a_1p8>; + vccq2-max-microamp = <800000>; +}; + +&ufs_mem_phy { + status = "okay"; + + vdda-phy-supply = <&vreg_l5a_0p88>; + vdda-max-microamp = <89900>; + vdda-pll-supply = <&vreg_l9a_1p2>; + vdda-pll-max-microamp = <18800>; +}; + +&usb_1 { + status = "okay"; + usb-role-switch; + port { + dwc3_drd_switch: endpoint@0 { + remote-endpoint = <&usb3_role>; + }; + }; +}; + +&usb_1_dwc3 { + dr_mode = "otg"; + usb-role-switch; +}; + +&usb_1_hsphy { + status = "okay"; + + vdda-pll-supply = <&vreg_l5a_0p88>; + vdda33-supply = <&vreg_l2a_3p1>; + vdda18-supply = <&vreg_l12a_1p8>; +}; + +&usb_1_qmpphy { + status = "okay"; + + vdda-phy-supply = <&vreg_l9a_1p2>; + vdda-pll-supply = <&vreg_l18a_0p92>; + orientation-switch; + port { + qmp_ss_mux: endpoint@0 { + remote-endpoint = <&usb3_data_ss>; + }; + }; +}; + +&usb_2 { + status = "okay"; +}; + +&usb_2_dwc3 { + dr_mode = "host"; +}; + +&usb_2_hsphy { + status = "okay"; + + vdda-pll-supply = <&vreg_l5a_0p88>; + vdda33-supply = <&vreg_l2a_3p1>; + vdda18-supply = <&vreg_l12a_1p8>; +}; + +&usb_2_qmpphy { + status = "okay"; + + vdda-phy-supply = <&vreg_l9a_1p2>; + vdda-pll-supply = <&vreg_l18a_0p92>; +}; diff --git a/arch/arm64/boot/dts/qcom/sm8150-mtp.dts b/arch/arm64/boot/dts/qcom/sm8150-mtp.dts index 6c6325c3af59..c7d5aab69b56 100644 --- a/arch/arm64/boot/dts/qcom/sm8150-mtp.dts +++ b/arch/arm64/boot/dts/qcom/sm8150-mtp.dts @@ -409,6 +409,32 @@ vdda-pll-max-microamp = <19000>; }; +&pm8150b_vbus { + status = "okay"; +}; + +&pm8150b_typec { + status = "okay"; + connector { + compatible = "usb-c-connector"; + power-role = "dual"; + data-role = "dual"; + ports { + #address-cells = <1>; + #size-cells = <0>; + port@1 { + reg = <1>; + usb3_data_ss: endpoint@0 { + remote-endpoint = <&qmp_ss_mux>; + }; + usb3_role: endpoint@1 { + remote-endpoint = <&dwc3_drd_switch>; + }; + }; + }; + }; +}; + &usb_1_hsphy { status = "okay"; vdda-pll-supply = <&vdd_usb_hs_core>; @@ -420,12 +446,25 @@ status = "okay"; vdda-phy-supply = <&vreg_l3c_1p2>; vdda-pll-supply = <&vdda_usb_ss_dp_core_1>; + orientation-switch; + port { + qmp_ss_mux: endpoint@0 { + remote-endpoint = <&usb3_data_ss>; + }; + }; }; &usb_1 { status = "okay"; + usb-role-switch; + port { + dwc3_drd_switch: endpoint@0 { + remote-endpoint = <&usb3_role>; + }; + }; }; &usb_1_dwc3 { - dr_mode = "peripheral"; + dr_mode = "otg"; + usb-role-switch; }; diff --git a/arch/arm64/boot/dts/qcom/sm8150.dtsi b/arch/arm64/boot/dts/qcom/sm8150.dtsi index b86a7ead3006..9405ef47df74 100644 --- a/arch/arm64/boot/dts/qcom/sm8150.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8150.dtsi @@ -10,6 +10,7 @@ #include <dt-bindings/soc/qcom,rpmh-rsc.h> #include <dt-bindings/clock/qcom,rpmh.h> #include <dt-bindings/clock/qcom,gcc-sm8150.h> +#include <dt-bindings/interconnect/qcom,sm8150.h> #include <dt-bindings/thermal/thermal.h> / { @@ -439,6 +440,55 @@ }; }; + config_noc: interconnect@1500000 { + compatible = "qcom,sm8150-config-noc"; + reg = <0 0x01500000 0 0x7400>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + system_noc: interconnect@1620000 { + compatible = "qcom,sm8150-system-noc"; + reg = <0 0x01620000 0 0x19400>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + mc_virt: interconnect@163a000 { + compatible = "qcom,sm8150-mc-virt"; + reg = <0 0x0163a000 0 0x1000>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + aggre1_noc: interconnect@16e0000 { + compatible = "qcom,sm8150-aggre1-noc"; + reg = <0 0x016e0000 0 0xd080>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + aggre2_noc: interconnect@1700000 { + compatible = "qcom,sm8150-aggre2-noc"; + reg = <0 0x01700000 0 0x20000>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + compute_noc: interconnect@1720000 { + compatible = "qcom,sm8150-compute-noc"; + reg = <0 0x01720000 0 0x7000>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + mmss_noc: interconnect@1740000 { + compatible = "qcom,sm8150-mmss-noc"; + reg = <0 0x01740000 0 0x1c100>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + ufs_mem_hc: ufshc@1d84000 { compatible = "qcom,sm8150-ufshc", "qcom,ufshc", "jedec,ufs-2.0"; @@ -507,6 +557,13 @@ }; }; + ipa_virt: interconnect@1e00000 { + compatible = "qcom,sm8150-ipa-virt"; + reg = <0 0x01e00000 0 0x1000>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + tcsr_mutex_regs: syscon@1f40000 { compatible = "syscon"; reg = <0x0 0x01f40000 0x0 0x40000>; @@ -813,6 +870,20 @@ }; }; + dc_noc: interconnect@9160000 { + compatible = "qcom,sm8150-dc-noc"; + reg = <0 0x09160000 0 0x3200>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + gem_noc: interconnect@9680000 { + compatible = "qcom,sm8150-gem-noc"; + reg = <0 0x09680000 0 0x3e200>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + usb_1: usb@a6f8800 { compatible = "qcom,sm8150-dwc3", "qcom,dwc3"; reg = <0 0x0a6f8800 0 0x400>; @@ -857,6 +928,13 @@ }; }; + camnoc_virt: interconnect@ac00000 { + compatible = "qcom,sm8150-camnoc-virt"; + reg = <0 0x0ac00000 0 0x1000>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + aoss_qmp: power-controller@c300000 { compatible = "qcom,sm8150-aoss-qmp"; reg = <0x0 0x0c300000 0x0 0x100000>; @@ -1099,6 +1177,10 @@ }; }; }; + + apps_bcm_voter: bcm_voter { + compatible = "qcom,bcm-voter"; + }; }; cpufreq_hw: cpufreq@18323000 { diff --git a/arch/arm64/boot/dts/qcom/sm8250-mtp.dts b/arch/arm64/boot/dts/qcom/sm8250-mtp.dts index 6894f8490dae..caf92e91f570 100644 --- a/arch/arm64/boot/dts/qcom/sm8250-mtp.dts +++ b/arch/arm64/boot/dts/qcom/sm8250-mtp.dts @@ -6,6 +6,9 @@ /dts-v1/; #include <dt-bindings/regulator/qcom,rpmh-regulator.h> +#include <dt-bindings/sound/qcom,q6afe.h> +#include <dt-bindings/sound/qcom,q6asm.h> +#include <dt-bindings/gpio/gpio.h> #include "sm8250.dtsi" #include "pm8150.dtsi" #include "pm8150b.dtsi" @@ -17,13 +20,111 @@ compatible = "qcom,sm8250-mtp"; aliases { - serial0 = &uart2; + serial0 = &uart12; }; chosen { stdout-path = "serial0:115200n8"; }; + thermal-zones { + xo-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8150_adc_tm 0>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + skin-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8150_adc_tm 1>; + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + mmw-pa1 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8150_adc_tm 2>; + + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + conn-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8150b_adc_tm 0>; + + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + camera-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8150l_adc_tm 0>; + + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + skin-msm-therm { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8150l_adc_tm 1>; + + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + + mmw-pa2 { + polling-delay-passive = <0>; + polling-delay = <0>; + thermal-sensors = <&pm8150l_adc_tm 2>; + + trips { + active-config0 { + temperature = <125000>; + hysteresis = <1000>; + type = "passive"; + }; + }; + }; + }; + vph_pwr: vph-pwr-regulator { compatible = "regulator-fixed"; regulator-name = "vph_pwr"; @@ -53,11 +154,25 @@ regulator-always-on; vin-supply = <&vph_pwr>; }; + + display_panel_avdd: display_gpio_regulator@1 { + compatible = "regulator-fixed"; + regulator-name = "display_panel_avdd"; + regulator-min-microvolt = <5500000>; + regulator-max-microvolt = <5500000>; + regulator-enable-ramp-delay = <233>; + gpio = <&tlmm 61 0>; + enable-active-high; + regulator-boot-on; + pinctrl-names = "default"; + pinctrl-0 = <&display_panel_avdd_default>; + }; + }; &adsp { status = "okay"; - firmware-name = "qcom/sm8250/adsp.mbn"; + firmware-name = "qcom/sm8250/adsp.mdt"; }; &apps_rsc { @@ -186,6 +301,13 @@ regulator-max-microvolt = <3008000>; regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; }; + + vreg_l18a_0p9: ldo18 { + regulator-name = "vreg_l18a_0p9"; + regulator-min-microvolt = <912000>; + regulator-max-microvolt = <912000>; + regulator-initial-mode = <RPMH_REGULATOR_MODE_HPM>; + }; }; pm8150l-rpmh-regulators { @@ -355,23 +477,253 @@ &cdsp { status = "okay"; - firmware-name = "qcom/sm8250/cdsp.mbn"; + firmware-name = "qcom/sm8250/cdsp.mdt"; +}; + +&dispcc { + dpu-disable-interfaces; +}; + +#if 0 +&dsi0 { + status = "okay"; + vdda-supply = <&vreg_l9a_1p2>; + + #address-cells = <1>; + #size-cells = <0>; + + ports { + port@1 { + endpoint { + data-lanes = <0 1 2 3>; + }; + }; + }; +}; + +&dsi0_phy { + status = "okay"; + vdds-supply = <&vreg_l5a_0p875>; +}; +#endif + +#if 0 +&dsi1 { + status = "okay"; + vdda-supply = <&vreg_l9a_1p2>; + + ports { + port@1 { + endpoint { + data-lanes = <0 1 2 3>; + }; + }; + }; +}; + +&dsi1_phy { + status = "okay"; + vdds-supply = <&vreg_l5a_0p875>; +}; +#endif + +&gpu { + zap-shader { + memory-region = <&gpu_mem>; + firmware-name = "qcom/a650_zap.mdt"; + }; +}; + +&i2c1 { + status = "okay"; + clock-frequency = <1000000>; + + /* NQ NFC chip @28 */ +}; + +&i2c13 { + status = "okay"; + + /* st,stmfts @ 49 */ +}; + +&i2c15 { + status = "okay"; + + /* smb1390 @ 10 */ + /* rtc6226 @ 64 */ +}; + +#if 0 +&mdss { + status = "okay"; +}; + +&mdss_mdp { + status = "okay"; +}; +#endif + +&pm8150_adc { + xo-therm@4c { + reg = <ADC5_XO_THERM_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + skin-therm@4d { + reg = <ADC5_AMUX_THM1_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + pa-therm1@4e { + reg = <ADC5_AMUX_THM2_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm8150b_adc { + conn-therm@4f { + reg = <ADC5_AMUX_THM3_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm8150l_adc { + camera-flash-therm@4d { + reg = <ADC5_AMUX_THM1_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + skin-msm-therm@4e { + reg = <ADC5_AMUX_THM2_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + pa-therm2@4f { + reg = <ADC5_AMUX_THM3_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm8150_adc_tm { + status = "okay"; + io-channels = <&pm8150_adc ADC5_XO_THERM_100K_PU>, + <&pm8150_adc ADC5_AMUX_THM1_100K_PU>, + <&pm8150_adc ADC5_AMUX_THM2_100K_PU>; + io-channel-names = "xo-therm", "skin-therm", "pa-therm1"; + + xo-therm@0 { + reg = <0>; + qcom,adc-channel = <ADC5_XO_THERM_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + skin-therm@1 { + reg = <1>; + qcom,adc-channel = <ADC5_AMUX_THM1_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + pa-therm1@2 { + reg = <2>; + qcom,adc-channel = <ADC5_AMUX_THM2_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm8150b_adc_tm { + status = "okay"; + io-channels = <&pm8150b_adc ADC5_AMUX_THM3_100K_PU>; + io-channel-names = "conn-therm"; + + conn-therm@0 { + reg = <0>; + qcom,adc-channel = <ADC5_AMUX_THM3_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm8150l_adc_tm { + status = "okay"; + io-channels = <&pm8150l_adc ADC5_AMUX_THM1_100K_PU>, + <&pm8150l_adc ADC5_AMUX_THM2_100K_PU>, + <&pm8150l_adc ADC5_AMUX_THM3_100K_PU>; + io-channel-names = "camera-flash-therm", "skin-msm-therm", "pa-therm2"; + + camera-flash-therm@0 { + reg = <0>; + qcom,adc-channel = <ADC5_AMUX_THM1_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + skin-msm-therm@1 { + reg = <1>; + qcom,adc-channel = <ADC5_AMUX_THM2_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; + + pa-therm2@2 { + reg = <2>; + qcom,adc-channel = <ADC5_AMUX_THM3_100K_PU>; + qcom,ratiometric; + qcom,hw-settle-time = <200>; + }; +}; + +&pm8150_rtc { + status = "okay"; +}; + +&qupv3_id_0 { + status = "okay"; }; &qupv3_id_1 { status = "okay"; }; +&qupv3_id_2 { + status = "okay"; +}; + &slpi { status = "okay"; - firmware-name = "qcom/sm8250/slpi.mbn"; + firmware-name = "qcom/sm8250/slpi.mdt"; }; &tlmm { gpio-reserved-ranges = <28 4>, <40 4>; + + display_panel_avdd_default: display_panel_avdd_default { + mux { + pins = "gpio61"; + function = "gpio"; + }; + + config { + pins = "gpio61"; + drive-strength = <8>; + bias-disable = <0>; + output-high; + }; + }; + }; -&uart2 { +&uart12 { status = "okay"; }; @@ -394,3 +746,107 @@ vdda-pll-supply = <&vreg_l9a_1p2>; vdda-pll-max-microamp = <19000>; }; + +&usb_1 { + status = "okay"; +}; + +&usb_1_dwc3 { + dr_mode = "host"; +}; + +&usb_1_hsphy { + status = "okay"; + + vdda-pll-supply = <&vreg_l5a_0p875>; + vdda18-supply = <&vreg_l12a_1p8>; + vdda33-supply = <&vreg_l2a_3p1>; +}; + +&usb_1_qmpphy { + status = "okay"; + + vdda-phy-supply = <&vreg_l9a_1p2>; + vdda-pll-supply = <&vreg_l18a_0p9>; +}; + +&usb_2 { + status = "okay"; +}; + +&usb_2_dwc3 { + dr_mode = "host"; +}; + +&usb_2_hsphy { + status = "okay"; + + vdda-pll-supply = <&vreg_l5a_0p875>; + vdda18-supply = <&vreg_l12a_1p8>; + vdda33-supply = <&vreg_l2a_3p1>; +}; + +&usb_2_qmpphy { + status = "okay"; + + vdda-phy-supply = <&vreg_l9a_1p2>; + vdda-pll-supply = <&vreg_l18a_0p9>; +}; + +&swr0 { + left_right: wsa8810-right{ + compatible = "sdw10217211000"; + reg = <0 2>; + powerdown-gpios = <&tlmm 127 GPIO_ACTIVE_HIGH>; + #thermal-sensor-cells = <0>; + sound-name-prefix = "SpkrRight"; + #sound-dai-cells = <0>; + }; + + left_spkr: wsa8810-left{ + compatible = "sdw10217211000"; + reg = <0 1>; + powerdown-gpios = <&tlmm 26 GPIO_ACTIVE_HIGH>; + #thermal-sensor-cells = <0>; + sound-name-prefix = "SpkrLeft"; + #sound-dai-cells = <0>; + }; +}; + +&q6asmdai { + dai@0 { + reg = <0>; + direction = <2>; + }; +}; + +&sound { + compatible = "qcom,sm8250-sndcard"; + model = "SM8250"; + audio-routing = + "SpkrLeft IN", "WSA_SPK1 OUT", + "MM_DL1", "MultiMedia1 Playback"; + + mm1-dai-link { + link-name = "MultiMedia1"; + cpu { + sound-dai = <&q6asmdai MSM_FRONTEND_DAI_MULTIMEDIA1>; + }; + }; + + dma-dai-link { + link-name = "WSA Playback"; + cpu { + sound-dai = <&q6afedai WSA_CODEC_DMA_RX_0>; + }; + + platform { + sound-dai = <&q6routing>; + }; + + codec { + sound-dai = <&left_spkr>, <&swr0 0>, <&wsamacro>; + }; + }; +}; + diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi index 377172e8967b..d4fd827de773 100644 --- a/arch/arm64/boot/dts/qcom/sm8250.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi @@ -4,12 +4,19 @@ */ #include <dt-bindings/interrupt-controller/arm-gic.h> +#include <dt-bindings/clock/qcom,dispcc-sm8250.h> #include <dt-bindings/clock/qcom,gcc-sm8250.h> +#include <dt-bindings/clock/qcom,gpucc-sm8250.h> +#include <dt-bindings/clock/qcom,audiocc-sm8250.h> #include <dt-bindings/clock/qcom,rpmh.h> +#include <dt-bindings/interconnect/qcom,sm8250.h> #include <dt-bindings/mailbox/qcom-ipcc.h> #include <dt-bindings/power/qcom-aoss-qmp.h> #include <dt-bindings/power/qcom-rpmpd.h> +#include <dt-bindings/soc/qcom,apr.h> +#include <dt-bindings/sound/qcom,q6afe.h> #include <dt-bindings/soc/qcom,rpmh-rsc.h> +#include <dt-bindings/thermal/thermal.h> / { interrupt-parent = <&intc>; @@ -87,6 +94,8 @@ reg = <0x0 0x0>; enable-method = "psci"; next-level-cache = <&L2_0>; + qcom,freq-domain = <&cpufreq_hw 0>; + #cooling-cells = <2>; L2_0: l2-cache { compatible = "cache"; next-level-cache = <&L3_0>; @@ -102,6 +111,8 @@ reg = <0x0 0x100>; enable-method = "psci"; next-level-cache = <&L2_100>; + qcom,freq-domain = <&cpufreq_hw 0>; + #cooling-cells = <2>; L2_100: l2-cache { compatible = "cache"; next-level-cache = <&L3_0>; @@ -114,6 +125,8 @@ reg = <0x0 0x200>; enable-method = "psci"; next-level-cache = <&L2_200>; + qcom,freq-domain = <&cpufreq_hw 0>; + #cooling-cells = <2>; L2_200: l2-cache { compatible = "cache"; next-level-cache = <&L3_0>; @@ -126,6 +139,8 @@ reg = <0x0 0x300>; enable-method = "psci"; next-level-cache = <&L2_300>; + qcom,freq-domain = <&cpufreq_hw 0>; + #cooling-cells = <2>; L2_300: l2-cache { compatible = "cache"; next-level-cache = <&L3_0>; @@ -138,6 +153,8 @@ reg = <0x0 0x400>; enable-method = "psci"; next-level-cache = <&L2_400>; + qcom,freq-domain = <&cpufreq_hw 1>; + #cooling-cells = <2>; L2_400: l2-cache { compatible = "cache"; next-level-cache = <&L3_0>; @@ -150,6 +167,8 @@ reg = <0x0 0x500>; enable-method = "psci"; next-level-cache = <&L2_500>; + qcom,freq-domain = <&cpufreq_hw 1>; + #cooling-cells = <2>; L2_500: l2-cache { compatible = "cache"; next-level-cache = <&L3_0>; @@ -163,6 +182,8 @@ reg = <0x0 0x600>; enable-method = "psci"; next-level-cache = <&L2_600>; + qcom,freq-domain = <&cpufreq_hw 1>; + #cooling-cells = <2>; L2_600: l2-cache { compatible = "cache"; next-level-cache = <&L3_0>; @@ -175,6 +196,8 @@ reg = <0x0 0x700>; enable-method = "psci"; next-level-cache = <&L2_700>; + qcom,freq-domain = <&cpufreq_hw 2>; + #cooling-cells = <2>; L2_700: l2-cache { compatible = "cache"; next-level-cache = <&L3_0>; @@ -393,8 +416,12 @@ #clock-cells = <1>; #reset-cells = <1>; #power-domain-cells = <1>; - clock-names = "bi_tcxo", "sleep_clk"; - clocks = <&rpmhcc RPMH_CXO_CLK>, <&sleep_clk>; + clock-names = "bi_tcxo", + "bi_tcxo_ao", + "sleep_clk"; + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&rpmhcc RPMH_CXO_CLK_A>, + <&sleep_clk>; }; ipcc: mailbox@408000 { @@ -406,6 +433,32 @@ #mbox-cells = <2>; }; + rng: rng@793000 { + compatible = "qcom,prng-ee"; + reg = <0 0x00793000 0 0x1000>; + clocks = <&gcc GCC_PRNG_AHB_CLK>; + clock-names = "core"; + }; + + qup_opp_table: qup-opp-table { + compatible = "operating-points-v2"; + + opp-50000000 { + opp-hz = /bits/ 64 <50000000>; + required-opps = <&rpmhpd_opp_min_svs>; + }; + + opp-75000000 { + opp-hz = /bits/ 64 <75000000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-120000000 { + opp-hz = /bits/ 64 <100000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + }; + qupv3_id_2: geniqup@8c0000 { compatible = "qcom,geni-se-qup"; reg = <0x0 0x008c0000 0x0 0x6000>; @@ -440,6 +493,8 @@ interrupts = <GIC_SPI 373 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -466,6 +521,8 @@ interrupts = <GIC_SPI 583 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -492,6 +549,8 @@ interrupts = <GIC_SPI 584 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -518,6 +577,21 @@ interrupts = <GIC_SPI 585 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; + status = "disabled"; + }; + + uart17: serial@88c000 { + compatible = "qcom,geni-uart"; + reg = <0 0x0088c000 0 0x4000>; + clock-names = "se"; + clocks = <&gcc GCC_QUPV3_WRAP2_S3_CLK>; + pinctrl-names = "default"; + pinctrl-0 = <&qup_uart17_default>; + interrupts = <GIC_SPI 585 IRQ_TYPE_LEVEL_HIGH>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -544,6 +618,21 @@ interrupts = <GIC_SPI 586 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; + status = "disabled"; + }; + + uart18: serial@890000 { + compatible = "qcom,geni-uart"; + reg = <0 0x00890000 0 0x4000>; + clock-names = "se"; + clocks = <&gcc GCC_QUPV3_WRAP2_S4_CLK>; + pinctrl-names = "default"; + pinctrl-0 = <&qup_uart18_default>; + interrupts = <GIC_SPI 586 IRQ_TYPE_LEVEL_HIGH>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -570,6 +659,8 @@ interrupts = <GIC_SPI 587 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; }; @@ -608,6 +699,8 @@ interrupts = <GIC_SPI 601 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -634,6 +727,8 @@ interrupts = <GIC_SPI 602 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -660,6 +755,21 @@ interrupts = <GIC_SPI 603 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; + status = "disabled"; + }; + + uart2: serial@988000 { + compatible = "qcom,geni-debug-uart"; + reg = <0 0x00988000 0 0x4000>; + clock-names = "se"; + clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>; + pinctrl-names = "default"; + pinctrl-0 = <&qup_uart2_default>; + interrupts = <GIC_SPI 603 IRQ_TYPE_LEVEL_HIGH>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -686,6 +796,8 @@ interrupts = <GIC_SPI 604 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -712,6 +824,8 @@ interrupts = <GIC_SPI 605 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -738,6 +852,8 @@ interrupts = <GIC_SPI 606 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -764,6 +880,21 @@ interrupts = <GIC_SPI 607 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; + status = "disabled"; + }; + + uart6: serial@998000 { + compatible = "qcom,geni-uart"; + reg = <0 0x00998000 0 0x4000>; + clock-names = "se"; + clocks = <&gcc GCC_QUPV3_WRAP0_S6_CLK>; + pinctrl-names = "default"; + pinctrl-0 = <&qup_uart6_default>; + interrupts = <GIC_SPI 607 IRQ_TYPE_LEVEL_HIGH>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -790,6 +921,8 @@ interrupts = <GIC_SPI 608 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; }; @@ -828,6 +961,8 @@ interrupts = <GIC_SPI 353 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -854,6 +989,8 @@ interrupts = <GIC_SPI 354 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -880,6 +1017,8 @@ interrupts = <GIC_SPI 355 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -906,6 +1045,8 @@ interrupts = <GIC_SPI 356 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -932,15 +1073,21 @@ interrupts = <GIC_SPI 357 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; - uart2: serial@a90000 { + uart12: serial@a90000 { compatible = "qcom,geni-debug-uart"; reg = <0x0 0x00a90000 0x0 0x4000>; clock-names = "se"; clocks = <&gcc GCC_QUPV3_WRAP1_S4_CLK>; + pinctrl-names = "default"; + pinctrl-0 = <&qup_uart12_default>; interrupts = <GIC_SPI 357 IRQ_TYPE_LEVEL_HIGH>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; @@ -967,10 +1114,348 @@ interrupts = <GIC_SPI 358 IRQ_TYPE_LEVEL_HIGH>; #address-cells = <1>; #size-cells = <0>; + power-domains = <&rpmhpd SM8250_CX>; + operating-points-v2 = <&qup_opp_table>; status = "disabled"; }; }; + config_noc: interconnect@1500000 { + compatible = "qcom,sm8250-config-noc"; + reg = <0 0x01500000 0 0xa580>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + system_noc: interconnect@1620000 { + compatible = "qcom,sm8250-system-noc"; + reg = <0 0x01620000 0 0x1c200>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + mc_virt: interconnect@163d000 { + compatible = "qcom,sm8250-mc-virt"; + reg = <0 0x0163d000 0 0x1000>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + aggre1_noc: interconnect@16e0000 { + compatible = "qcom,sm8250-aggre1-noc"; + reg = <0 0x016e0000 0 0x1f180>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + aggre2_noc: interconnect@1700000 { + compatible = "qcom,sm8250-aggre2-noc"; + reg = <0 0x01700000 0 0x33000>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + compute_noc: interconnect@1733000 { + compatible = "qcom,sm8250-compute-noc"; + reg = <0 0x01733000 0 0xa180>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + mmss_noc: interconnect@1740000 { + compatible = "qcom,sm8250-mmss-noc"; + reg = <0 0x01740000 0 0x1f080>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + pcie0: pci@1c00000 { + compatible = "qcom,pcie-sm8250", "snps,dw-pcie"; + reg = <0 0x01c00000 0 0x3000>, + <0 0x60000000 0 0xf1d>, + <0 0x60000f20 0 0xa8>, + <0 0x60001000 0 0x1000>, + <0 0x60100000 0 0x100000>; + reg-names = "parf", "dbi", "elbi", "atu", "config"; + device_type = "pci"; + linux,pci-domain = <0>; + bus-range = <0x00 0xff>; + num-lanes = <1>; + + #address-cells = <3>; + #size-cells = <2>; + + ranges = <0x01000000 0x0 0x60200000 0 0x60200000 0x0 0x100000>, + <0x02000000 0x0 0x60300000 0 0x60300000 0x0 0x3d00000>; + + interrupts = <GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "msi"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 149 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ + <0 0 0 2 &intc 0 150 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ + <0 0 0 3 &intc 0 151 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <0 0 0 4 &intc 0 152 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ + + clocks = <&gcc GCC_PCIE_0_PIPE_CLK>, + <&gcc GCC_PCIE_0_AUX_CLK>, + <&gcc GCC_PCIE_0_CFG_AHB_CLK>, + <&gcc GCC_PCIE_0_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_0_SLV_AXI_CLK>, + <&gcc GCC_PCIE_0_SLV_Q2A_AXI_CLK>, + <&gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>; + clock-names = "pipe", + "aux", + "cfg", + "bus_master", + "bus_slave", + "slave_q2a", + "tbu"; + + iommus = <&apps_smmu 0x1c00 0x7f>; + iommu-map = <0x0 &apps_smmu 0x1c00 0x1>, + <0x100 &apps_smmu 0x1c01 0x1>; + + resets = <&gcc GCC_PCIE_0_BCR>; + reset-names = "pci"; + + power-domains = <&gcc PCIE_0_GDSC>; + + phys = <&pcie0_lane>; + phy-names = "pciephy"; + + status = "disabled"; + }; + + pcie0_phy: phy@1c06000 { + compatible = "qcom,sm8250-qmp-pcie-phy"; + reg = <0 0x01c06000 0 0x1c0>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>, + <&gcc GCC_PCIE_0_CFG_AHB_CLK>, + <&gcc GCC_PCIE_WIFI_CLKREF_EN>, + <&gcc GCC_PCIE0_PHY_REFGEN_CLK>; + clock-names = "aux", "cfg_ahb", "ref", "refgen"; + + resets = <&gcc GCC_PCIE_0_PHY_BCR>; + reset-names = "phy"; + + assigned-clocks = <&gcc GCC_PCIE0_PHY_REFGEN_CLK>; + assigned-clock-rates = <100000000>; + + status = "disabled"; + + pcie0_lane: lanes@1c06200 { + reg = <0 0x1c06200 0 0x170>, /* tx */ + <0 0x1c06400 0 0x200>, /* rx */ + <0 0x1c06800 0 0x1f0>, /* pcs */ + <0 0x1c06c00 0 0xf4>; /* "pcs_lane" same as pcs_misc? */ + clocks = <&gcc GCC_PCIE_0_PIPE_CLK>; + clock-names = "pipe0"; + + #phy-cells = <0>; + clock-output-names = "pcie_0_pipe_clk"; + }; + }; + + pcie1: pci@1c08000 { + compatible = "qcom,pcie-sm8250", "snps,dw-pcie"; + reg = <0 0x01c08000 0 0x3000>, + <0 0x40000000 0 0xf1d>, + <0 0x40000f20 0 0xa8>, + <0 0x40001000 0 0x1000>, + <0 0x40100000 0 0x100000>; + reg-names = "parf", "dbi", "elbi", "atu", "config"; + device_type = "pci"; + linux,pci-domain = <1>; + bus-range = <0x00 0xff>; + num-lanes = <2>; + + #address-cells = <3>; + #size-cells = <2>; + + ranges = <0x01000000 0x0 0x40200000 0x0 0x40200000 0x0 0x100000>, + <0x02000000 0x0 0x40300000 0x0 0x40300000 0x0 0x1fd00000>; + + interrupts = <GIC_SPI 306 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "msi"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 434 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ + <0 0 0 2 &intc 0 435 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ + <0 0 0 3 &intc 0 438 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <0 0 0 4 &intc 0 439 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ + + clocks = <&gcc GCC_PCIE_1_PIPE_CLK>, + <&gcc GCC_PCIE_1_AUX_CLK>, + <&gcc GCC_PCIE_1_CFG_AHB_CLK>, + <&gcc GCC_PCIE_1_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_1_SLV_AXI_CLK>, + <&gcc GCC_PCIE_1_SLV_Q2A_AXI_CLK>, + <&gcc GCC_PCIE_WIGIG_CLKREF_EN>, + <&gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>; + clock-names = "pipe", + "aux", + "cfg", + "bus_master", + "bus_slave", + "slave_q2a", + "ref", + "tbu"; + + assigned-clocks = <&gcc GCC_PCIE_1_AUX_CLK>; + assigned-clock-rates = <19200000>; + + iommus = <&apps_smmu 0x1c80 0x7f>; + iommu-map = <0x0 &apps_smmu 0x1c80 0x1>, + <0x100 &apps_smmu 0x1c81 0x1>; + + resets = <&gcc GCC_PCIE_1_BCR>; + reset-names = "pci"; + + power-domains = <&gcc PCIE_1_GDSC>; + + phys = <&pcie1_lane>; + phy-names = "pciephy"; + + status = "disabled"; + }; + + pcie1_phy: phy@1c0e000 { + compatible = "qcom,sm8250-qhp-pcie-phy"; + reg = <0 0x01c0e000 0 0x1c0>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>, + <&gcc GCC_PCIE_1_CFG_AHB_CLK>, + <&gcc GCC_PCIE_WIGIG_CLKREF_EN>, + <&gcc GCC_PCIE1_PHY_REFGEN_CLK>; + clock-names = "aux", "cfg_ahb", "ref", "refgen"; + + resets = <&gcc GCC_PCIE_1_PHY_BCR>; + reset-names = "phy"; + + assigned-clocks = <&gcc GCC_PCIE1_PHY_REFGEN_CLK>; + assigned-clock-rates = <100000000>; + + status = "disabled"; + + pcie1_lane: lanes@1c0e200 { + reg = <0 0x1c0e200 0 0x170>, /* tx0 */ + <0 0x1c0e400 0 0x200>, /* rx0 */ + <0 0x1c0ea00 0 0x1f0>, /* pcs */ + <0 0x1c0e600 0 0x170>, /* tx1 */ + <0 0x1c0e800 0 0x200>, /* rx1 */ + <0 0x1c0ee00 0 0xf4>; /* "pcs_com" same as pcs_misc? */ + clocks = <&gcc GCC_PCIE_1_PIPE_CLK>; + clock-names = "pipe0"; + + #phy-cells = <0>; + clock-output-names = "pcie_1_pipe_clk"; + }; + }; + + pcie2: pci@1c10000 { + compatible = "qcom,pcie-sm8250", "snps,dw-pcie"; + reg = <0 0x01c10000 0 0x3000>, + <0 0x64000000 0 0xf1d>, + <0 0x64000f20 0 0xa8>, + <0 0x64001000 0 0x1000>, + <0 0x64100000 0 0x100000>; + reg-names = "parf", "dbi", "elbi", "atu", "config"; + device_type = "pci"; + linux,pci-domain = <2>; + bus-range = <0x00 0xff>; + num-lanes = <2>; + + #address-cells = <3>; + #size-cells = <2>; + + ranges = <0x01000000 0x0 0x64200000 0x0 0x64200000 0x0 0x100000>, + <0x02000000 0x0 0x64300000 0x0 0x64300000 0x0 0x3d00000>; + + interrupts = <GIC_SPI 236 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "msi"; + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 0x7>; + interrupt-map = <0 0 0 1 &intc 0 290 IRQ_TYPE_LEVEL_HIGH>, /* int_a */ + <0 0 0 2 &intc 0 415 IRQ_TYPE_LEVEL_HIGH>, /* int_b */ + <0 0 0 3 &intc 0 416 IRQ_TYPE_LEVEL_HIGH>, /* int_c */ + <0 0 0 4 &intc 0 417 IRQ_TYPE_LEVEL_HIGH>; /* int_d */ + + clocks = <&gcc GCC_PCIE_2_PIPE_CLK>, + <&gcc GCC_PCIE_2_AUX_CLK>, + <&gcc GCC_PCIE_2_CFG_AHB_CLK>, + <&gcc GCC_PCIE_2_MSTR_AXI_CLK>, + <&gcc GCC_PCIE_2_SLV_AXI_CLK>, + <&gcc GCC_PCIE_2_SLV_Q2A_AXI_CLK>, + <&gcc GCC_PCIE_MDM_CLKREF_EN>, + <&gcc GCC_AGGRE_NOC_PCIE_TBU_CLK>; + clock-names = "pipe", + "aux", + "cfg", + "bus_master", + "bus_slave", + "slave_q2a", + "ref", + "tbu"; + + assigned-clocks = <&gcc GCC_PCIE_2_AUX_CLK>; + assigned-clock-rates = <19200000>; + + iommus = <&apps_smmu 0x1d00 0x7f>; + iommu-map = <0x0 &apps_smmu 0x1d00 0x1>, + <0x100 &apps_smmu 0x1d01 0x1>; + + resets = <&gcc GCC_PCIE_2_BCR>; + reset-names = "pci"; + + power-domains = <&gcc PCIE_2_GDSC>; + + phys = <&pcie2_lane>; + phy-names = "pciephy"; + + status = "disabled"; + }; + + pcie2_phy: phy@1c16000 { + compatible = "qcom,sm8250-qhp-pcie-phy"; + reg = <0 0x1c16000 0 0x1c0>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>, + <&gcc GCC_PCIE_2_CFG_AHB_CLK>, + <&gcc GCC_PCIE_MDM_CLKREF_EN>, + <&gcc GCC_PCIE2_PHY_REFGEN_CLK>; + clock-names = "aux", "cfg_ahb", "ref", "refgen"; + + resets = <&gcc GCC_PCIE_2_PHY_BCR>; + reset-names = "phy"; + + assigned-clocks = <&gcc GCC_PCIE2_PHY_REFGEN_CLK>; + assigned-clock-rates = <100000000>; + + status = "disabled"; + + pcie2_lane: lanes@1c0e200 { + reg = <0 0x1c16200 0 0x170>, /* tx0 */ + <0 0x1c16400 0 0x200>, /* rx0 */ + <0 0x1c16a00 0 0x1f0>, /* pcs */ + <0 0x1c16600 0 0x170>, /* tx1 */ + <0 0x1c16800 0 0x200>, /* rx1 */ + <0 0x1c16e00 0 0xf4>; /* "pcs_com" same as pcs_misc? */ + clocks = <&gcc GCC_PCIE_2_PIPE_CLK>; + clock-names = "pipe0"; + + #phy-cells = <0>; + clock-output-names = "pcie_2_pipe_clk"; + }; + }; + ufs_mem_hc: ufshc@1d84000 { compatible = "qcom,sm8250-ufshc", "qcom,ufshc", "jedec,ufs-2.0"; @@ -985,6 +1470,8 @@ power-domains = <&gcc UFS_PHY_GDSC>; + iommus = <&apps_smmu 0x0e0 0>, <&apps_smmu 0x4e0 0>; + clock-names = "core_clk", "bus_aggr_clk", @@ -1041,21 +1528,57 @@ }; }; + ipa_virt: interconnect@1e00000 { + compatible = "qcom,sm8250-ipa-virt"; + reg = <0 0x01e00000 0 0x1000>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + tcsr_mutex: hwlock@1f40000 { compatible = "qcom,tcsr-mutex"; reg = <0x0 0x01f40000 0x0 0x40000>; #hwlock-cells = <1>; }; + slimbam: dma@3a84000 { + compatible = "qcom,bam-v1.7.0"; + qcom,controlled-remotely; + reg = <0 0x3a84000 0 0x2a000>; + num-channels = <31>; + interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>; + #dma-cells = <1>; + qcom,ee = <1>; + qcom,num-ees = <2>; + iommus = <&apps_smmu 0x1826 0x0>; + }; + + slim: slim@3ac0000 { + compatible = "qcom,slim-ngd-v2.2.0"; + reg = <0 0x3ac0000 0 0x2c000>; + interrupts = <GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>; + + qcom,apps-ch-pipes = <0x780000>; + qcom,ea-pc = <0x270>; + status = "okay"; + dmas = <&slimbam 3>, <&slimbam 4>, + <&slimbam 5>, <&slimbam 6>; + dma-names = "rx", "tx", "tx2", "rx2"; + + iommus = <&apps_smmu 0x1826 0x0>; + #address-cells = <1>; + #size-cells = <0>; + + ngd@1 { + reg = <1>; + #address-cells = <2>; + #size-cells = <0>; + }; + }; + gpu: gpu@3d00000 { - /* - * note: the amd,imageon compatible makes it possible - * to use the drm/msm driver without the display node, - * make sure to remove it when display node is added - */ compatible = "qcom,adreno-650.2", - "qcom,adreno", - "amd,imageon"; + "qcom,adreno"; #stream-id-cells = <16>; reg = <0 0x03d00000 0 0x40000>; @@ -1127,15 +1650,15 @@ <GIC_SPI 305 IRQ_TYPE_LEVEL_HIGH>; interrupt-names = "hfi", "gmu"; - clocks = <&gpucc 0>, - <&gpucc 3>, - <&gpucc 6>, + clocks = <&gpucc GPU_CC_AHB_CLK>, + <&gpucc GPU_CC_CX_GMU_CLK>, + <&gpucc GPU_CC_CXO_CLK>, <&gcc GCC_DDRSS_GPU_AXI_CLK>, <&gcc GCC_GPU_MEMNOC_GFX_CLK>; clock-names = "ahb", "gmu", "cxo", "axi", "memnoc"; - power-domains = <&gpucc 0>, - <&gpucc 1>; + power-domains = <&gpucc GPU_CX_GDSC>, + <&gpucc GPU_GX_GDSC>; power-domain-names = "cx", "gx"; iommus = <&adreno_smmu 5 0x400>; @@ -1181,19 +1704,19 @@ <GIC_SPI 683 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 684 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 685 IRQ_TYPE_LEVEL_HIGH>; - clocks = <&gpucc 0>, + clocks = <&gpucc GPU_CC_AHB_CLK>, <&gcc GCC_GPU_MEMNOC_GFX_CLK>, <&gcc GCC_GPU_SNOC_DVM_GFX_CLK>; clock-names = "ahb", "bus", "iface"; - power-domains = <&gpucc 0>; + power-domains = <&gpucc GPU_CX_GDSC>; }; slpi: remoteproc@5c00000 { compatible = "qcom,sm8250-slpi-pas"; reg = <0 0x05c00000 0 0x4000>; - interrupts-extended = <&pdc 9 IRQ_TYPE_LEVEL_HIGH>, + interrupts-extended = <&pdc 9 IRQ_TYPE_EDGE_RISING>, <&smp2p_slpi_in 0 IRQ_TYPE_EDGE_RISING>, <&smp2p_slpi_in 1 IRQ_TYPE_EDGE_RISING>, <&smp2p_slpi_in 2 IRQ_TYPE_EDGE_RISING>, @@ -1232,7 +1755,7 @@ compatible = "qcom,sm8250-cdsp-pas"; reg = <0 0x08300000 0 0x10000>; - interrupts-extended = <&intc GIC_SPI 578 IRQ_TYPE_LEVEL_HIGH>, + interrupts-extended = <&intc GIC_SPI 578 IRQ_TYPE_EDGE_RISING>, <&smp2p_cdsp_in 0 IRQ_TYPE_EDGE_RISING>, <&smp2p_cdsp_in 1 IRQ_TYPE_EDGE_RISING>, <&smp2p_cdsp_in 2 IRQ_TYPE_EDGE_RISING>, @@ -1266,6 +1789,530 @@ }; }; + sdhc_2: sdhci@8804000 { + compatible = "qcom,sm8250-sdhci", "qcom,sdhci-msm-v5"; + reg = <0 0x08804000 0 0x1000>; + + interrupts = <GIC_SPI 204 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 222 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "hc_irq", "pwr_irq"; + + clocks = <&gcc GCC_SDCC2_AHB_CLK>, + <&gcc GCC_SDCC2_APPS_CLK>, + <&xo_board>; + clock-names = "iface", "core", "xo"; + iommus = <&apps_smmu 0xa0 0xf>; + qcom,dll-config = <0x0007642c>; + qcom,ddr-config = <0x80040868>; + power-domains = <&rpmhpd SM8250_CX>; + + status = "disabled"; + }; + + sound: sound { + }; + + usb_1_hsphy: phy@88e3000 { + compatible = "qcom,sm8250-usb-hs-phy", + "qcom,usb-snps-hs-7nm-phy"; + reg = <0 0x088e3000 0 0x400>; + status = "disabled"; + #phy-cells = <0>; + + clocks = <&rpmhcc RPMH_CXO_CLK>; + clock-names = "ref"; + + resets = <&gcc GCC_QUSB2PHY_PRIM_BCR>; + }; + + usb_2_hsphy: phy@88e4000 { + compatible = "qcom,sm8250-usb-hs-phy", + "qcom,usb-snps-hs-7nm-phy"; + reg = <0 0x088e4000 0 0x400>; + status = "disabled"; + #phy-cells = <0>; + + clocks = <&rpmhcc RPMH_CXO_CLK>; + clock-names = "ref"; + + resets = <&gcc GCC_QUSB2PHY_SEC_BCR>; + }; + + usb_1_qmpphy: phy@88e9000 { + compatible = "qcom,sm8250-qmp-usb3-phy"; + reg = <0 0x088e9000 0 0x200>, + <0 0x088e8000 0 0x20>; + reg-names = "reg-base", "dp_com"; + status = "disabled"; + #clock-cells = <1>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + clocks = <&gcc GCC_USB3_PRIM_PHY_AUX_CLK>, + <&rpmhcc RPMH_CXO_CLK>, + <&gcc GCC_USB3_PRIM_PHY_COM_AUX_CLK>; + clock-names = "aux", "ref_clk_src", "com_aux"; + + resets = <&gcc GCC_USB3_DP_PHY_PRIM_BCR>, + <&gcc GCC_USB3_PHY_PRIM_BCR>; + reset-names = "phy", "common"; + + usb_1_ssphy: lanes@88e9200 { + reg = <0 0x088e9200 0 0x200>, + <0 0x088e9400 0 0x200>, + <0 0x088e9c00 0 0x400>, + <0 0x088e9600 0 0x200>, + <0 0x088e9800 0 0x200>, + <0 0x088e9a00 0 0x100>; + #phy-cells = <0>; + clocks = <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>; + clock-names = "pipe0"; + clock-output-names = "usb3_phy_pipe_clk_src"; + }; + }; + + usb_2_qmpphy: phy@88eb000 { + compatible = "qcom,sm8250-qmp-usb3-uni-phy"; + reg = <0 0x088eb000 0 0x200>; + status = "disabled"; + #clock-cells = <1>; + #address-cells = <2>; + #size-cells = <2>; + ranges; + + clocks = <&gcc GCC_USB3_SEC_PHY_AUX_CLK>, + <&rpmhcc RPMH_CXO_CLK>, + <&gcc GCC_USB3_SEC_CLKREF_EN>, + <&gcc GCC_USB3_SEC_PHY_COM_AUX_CLK>; + clock-names = "aux", "ref_clk_src", "ref", "com_aux"; + + resets = <&gcc GCC_USB3PHY_PHY_SEC_BCR>, + <&gcc GCC_USB3_PHY_SEC_BCR>; + reset-names = "phy", "common"; + + usb_2_ssphy: lanes@88eb200 { + reg = <0 0x088eb200 0 0x200>, + <0 0x088eb400 0 0x200>, + <0 0x088eb800 0 0x800>; + #phy-cells = <0>; + clocks = <&gcc GCC_USB3_SEC_PHY_PIPE_CLK>; + clock-names = "pipe0"; + clock-output-names = "usb3_uni_phy_pipe_clk_src"; + }; + }; + + dc_noc: interconnect@90c0000 { + compatible = "qcom,sm8250-dc-noc"; + reg = <0 0x090c0000 0 0x4200>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + gem_noc: interconnect@9100000 { + compatible = "qcom,sm8250-gem-noc"; + reg = <0 0x09100000 0 0xb4000>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + npu_noc: interconnect@9990000 { + compatible = "qcom,sm8250-npu-noc"; + reg = <0 0x09990000 0 0x1600>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + usb_1: usb@a6f8800 { + compatible = "qcom,sm8250-dwc3", "qcom,dwc3"; + reg = <0 0x0a6f8800 0 0x400>; + status = "disabled"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + dma-ranges; + + clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>, + <&gcc GCC_USB30_PRIM_MASTER_CLK>, + <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>, + <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, + <&gcc GCC_USB30_PRIM_SLEEP_CLK>, + <&gcc GCC_USB3_SEC_CLKREF_EN>; + clock-names = "cfg_noc", "core", "iface", "mock_utmi", + "sleep", "xo"; + + assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>, + <&gcc GCC_USB30_PRIM_MASTER_CLK>; + assigned-clock-rates = <19200000>, <200000000>; + + interrupts-extended = <&intc GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 14 IRQ_TYPE_EDGE_BOTH>, + <&pdc 15 IRQ_TYPE_EDGE_BOTH>, + <&pdc 17 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "hs_phy_irq", "dp_hs_phy_irq", + "dm_hs_phy_irq", "ss_phy_irq"; + + power-domains = <&gcc USB30_PRIM_GDSC>; + + resets = <&gcc GCC_USB30_PRIM_BCR>; + + usb_1_dwc3: dwc3@a600000 { + compatible = "snps,dwc3"; + reg = <0 0x0a600000 0 0xcd00>; + interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>; + iommus = <&apps_smmu 0x0 0x0>; + snps,dis_u2_susphy_quirk; + snps,dis_enblslpm_quirk; + phys = <&usb_1_hsphy>, <&usb_1_ssphy>; + phy-names = "usb2-phy", "usb3-phy"; + }; + }; + + system-cache-controller@9200000 { + compatible = "qcom,sm8250-llcc"; + reg = <0 0x09200000 0 0x1d0000>, <0 0x09600000 0 0x50000>; + reg-names = "llcc_base", "llcc_broadcast_base"; + }; + + usb_2: usb@a8f8800 { + compatible = "qcom,sm8250-dwc3", "qcom,dwc3"; + reg = <0 0x0a8f8800 0 0x400>; + status = "disabled"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + dma-ranges; + + clocks = <&gcc GCC_CFG_NOC_USB3_SEC_AXI_CLK>, + <&gcc GCC_USB30_SEC_MASTER_CLK>, + <&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>, + <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, + <&gcc GCC_USB30_SEC_SLEEP_CLK>, + <&gcc GCC_USB3_SEC_CLKREF_EN>; + clock-names = "cfg_noc", "core", "iface", "mock_utmi", + "sleep", "xo"; + + assigned-clocks = <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>, + <&gcc GCC_USB30_SEC_MASTER_CLK>; + assigned-clock-rates = <19200000>, <200000000>; + + interrupts-extended = <&intc GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 12 IRQ_TYPE_EDGE_BOTH>, + <&pdc 13 IRQ_TYPE_EDGE_BOTH>, + <&pdc 16 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "hs_phy_irq", "dp_hs_phy_irq", + "dm_hs_phy_irq", "ss_phy_irq"; + + power-domains = <&gcc USB30_SEC_GDSC>; + + resets = <&gcc GCC_USB30_SEC_BCR>; + + usb_2_dwc3: dwc3@a800000 { + compatible = "snps,dwc3"; + reg = <0 0x0a800000 0 0xcd00>; + interrupts = <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>; + iommus = <&apps_smmu 0x20 0>; + snps,dis_u2_susphy_quirk; + snps,dis_enblslpm_quirk; + phys = <&usb_2_hsphy>, <&usb_2_ssphy>; + phy-names = "usb2-phy", "usb3-phy"; + }; + }; + + mdss: mdss@ae00000 { + compatible = "qcom,sdm845-mdss"; + reg = <0 0x0ae00000 0 0x1000>; + reg-names = "mdss"; + + interconnects = <&gem_noc MASTER_AMPSS_M0 &config_noc SLAVE_DISPLAY_CFG>, + <&mmss_noc MASTER_MDP_PORT0 &mc_virt SLAVE_EBI_CH0>, + <&mmss_noc MASTER_MDP_PORT1 &mc_virt SLAVE_EBI_CH0>; + interconnect-names = "notused", "mdp0-mem", "mdp1-mem"; + + power-domains = <&dispcc MDSS_GDSC>; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>, + <&gcc GCC_DISP_SF_AXI_CLK>, + <&dispcc DISP_CC_MDSS_MDP_CLK>; + clock-names = "iface", "bus", "nrt_bus", "core"; + + assigned-clocks = <&dispcc DISP_CC_MDSS_MDP_CLK>; + assigned-clock-rates = <460000000>; + + interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>; + interrupt-controller; + #interrupt-cells = <1>; + + iommus = <&apps_smmu 0x820 0x402>; + + status = "disabled"; + + #address-cells = <2>; + #size-cells = <2>; + ranges; + + mdss_mdp: mdp@ae01000 { + compatible = "qcom,sdm845-dpu"; + reg = <0 0x0ae01000 0 0x8f000>, + <0 0x0aeb0000 0 0x2008>; + reg-names = "mdp", "vbif"; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>, + <&dispcc DISP_CC_MDSS_MDP_CLK>, + <&dispcc DISP_CC_MDSS_VSYNC_CLK>; + clock-names = "iface", "bus", "core", "vsync"; + + assigned-clocks = <&dispcc DISP_CC_MDSS_MDP_CLK>, + <&dispcc DISP_CC_MDSS_VSYNC_CLK>; + assigned-clock-rates = <460000000>, + <19200000>; + + operating-points-v2 = <&mdp_opp_table>; + power-domains = <&rpmhpd SM8250_MMCX>; + + interrupt-parent = <&mdss>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dpu_intf1_out: endpoint { + remote-endpoint = <&dsi0_in>; + }; + }; + + port@1 { + reg = <1>; + dpu_intf2_out: endpoint { + remote-endpoint = <&dsi1_in>; + }; + }; + }; + + mdp_opp_table: mdp-opp-table { + compatible = "operating-points-v2"; + + opp-200000000 { + opp-hz = /bits/ 64 <200000000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-345000000 { + opp-hz = /bits/ 64 <345000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + + opp-460000000 { + opp-hz = /bits/ 64 <460000000>; + required-opps = <&rpmhpd_opp_nom>; + }; + }; + }; + + dsi0: dsi@ae94000 { + compatible = "qcom,mdss-dsi-ctrl"; + reg = <0 0x0ae94000 0 0x400>; + reg-names = "dsi_ctrl"; + + interrupt-parent = <&mdss>; + interrupts = <4 IRQ_TYPE_LEVEL_HIGH>; + + clocks = <&dispcc DISP_CC_MDSS_BYTE0_CLK>, + <&dispcc DISP_CC_MDSS_BYTE0_INTF_CLK>, + <&dispcc DISP_CC_MDSS_PCLK0_CLK>, + <&dispcc DISP_CC_MDSS_ESC0_CLK>, + <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>; + clock-names = "byte", + "byte_intf", + "pixel", + "core", + "iface", + "bus"; + + operating-points-v2 = <&dsi_opp_table>; + power-domains = <&rpmhpd SM8250_MMCX>; + + phys = <&dsi0_phy>; + phy-names = "dsi"; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dsi0_in: endpoint { + remote-endpoint = <&dpu_intf1_out>; + }; + }; + + port@1 { + reg = <1>; + dsi0_out: endpoint { + }; + }; + }; + }; + + dsi0_phy: dsi-phy@ae94400 { + compatible = "qcom,dsi-phy-7nm"; + reg = <0 0x0ae94400 0 0x200>, + <0 0x0ae94600 0 0x280>, + <0 0x0ae94900 0 0x260>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + #clock-cells = <1>; + #phy-cells = <0>; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>; + clock-names = "iface", "ref"; + + status = "disabled"; + }; + + dsi1: dsi@ae96000 { + compatible = "qcom,mdss-dsi-ctrl"; + reg = <0 0x0ae96000 0 0x400>; + reg-names = "dsi_ctrl"; + + interrupt-parent = <&mdss>; + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; + + clocks = <&dispcc DISP_CC_MDSS_BYTE1_CLK>, + <&dispcc DISP_CC_MDSS_BYTE1_INTF_CLK>, + <&dispcc DISP_CC_MDSS_PCLK1_CLK>, + <&dispcc DISP_CC_MDSS_ESC1_CLK>, + <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&gcc GCC_DISP_HF_AXI_CLK>; + clock-names = "byte", + "byte_intf", + "pixel", + "core", + "iface", + "bus"; + + operating-points-v2 = <&dsi_opp_table>; + power-domains = <&rpmhpd SM8250_MMCX>; + + phys = <&dsi1_phy>; + phy-names = "dsi"; + + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dsi1_in: endpoint { + remote-endpoint = <&dpu_intf2_out>; + }; + }; + + port@1 { + reg = <1>; + dsi1_out: endpoint { + }; + }; + }; + }; + + dsi1_phy: dsi-phy@ae96400 { + compatible = "qcom,dsi-phy-7nm"; + reg = <0 0x0ae96400 0 0x200>, + <0 0x0ae96600 0 0x280>, + <0 0x0ae96900 0 0x260>; + reg-names = "dsi_phy", + "dsi_phy_lane", + "dsi_pll"; + + #clock-cells = <1>; + #phy-cells = <0>; + + clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, + <&rpmhcc RPMH_CXO_CLK>; + clock-names = "iface", "ref"; + + status = "disabled"; + + dsi_opp_table: dsi-opp-table { + compatible = "operating-points-v2"; + + opp-187500000 { + opp-hz = /bits/ 64 <187500000>; + required-opps = <&rpmhpd_opp_low_svs>; + }; + + opp-300000000 { + opp-hz = /bits/ 64 <300000000>; + required-opps = <&rpmhpd_opp_svs>; + }; + + opp-358000000 { + opp-hz = /bits/ 64 <358000000>; + required-opps = <&rpmhpd_opp_svs_l1>; + }; + }; + }; + }; + + dispcc: clock-controller@af00000 { + compatible = "qcom,sm8250-dispcc"; + reg = <0 0x0af00000 0 0x20000>; + power-domains = <&rpmhpd SM8250_MMCX>; + required-opps = <&rpmhpd_opp_low_svs>; + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&dsi0_phy 0>, + <&dsi0_phy 1>, + <&dsi1_phy 0>, + <&dsi1_phy 1>, + <0>, + <0>, + <0>, + <0>, + <0>, + <0>, + <0>, + <0>, + <&sleep_clk>; + clock-names = "bi_tcxo", + "dsi0_phy_pll_out_byteclk", + "dsi0_phy_pll_out_dsiclk", + "dsi1_phy_pll_out_byteclk", + "dsi1_phy_pll_out_dsiclk", + "dp_link_clk_divsel_ten", + "dp_vco_divided_clk_src_mux", + "dptx1_phy_pll_link_clk", + "dptx1_phy_pll_vco_div_clk", + "dptx2_phy_pll_link_clk", + "dptx2_phy_pll_vco_div_clk", + "edp_phy_pll_link_clk", + "edp_phy_pll_vco_div_clk", + "sleep_clk"; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; + pdc: interrupt-controller@b220000 { compatible = "qcom,sm8250-pdc", "qcom,pdc"; reg = <0 0x0b220000 0 0x30000>, <0 0x17c000f0 0 0x60>; @@ -1276,6 +2323,28 @@ interrupt-controller; }; + tsens0: thermal-sensor@c263000 { + compatible = "qcom,sm8250-tsens", "qcom,tsens-v2"; + reg = <0 0x0c263000 0 0x1ff>, /* TM */ + <0 0x0c222000 0 0x1ff>; /* SROT */ + #qcom,sensors = <16>; + interrupts = <GIC_SPI 506 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 508 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "uplow", "critical"; + #thermal-sensor-cells = <1>; + }; + + tsens1: thermal-sensor@c265000 { + compatible = "qcom,sm8250-tsens", "qcom,tsens-v2"; + reg = <0 0x0c265000 0 0x1ff>, /* TM */ + <0 0x0c223000 0 0x1ff>; /* SROT */ + #qcom,sensors = <9>; + interrupts = <GIC_SPI 507 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 509 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "uplow", "critical"; + #thermal-sensor-cells = <1>; + }; + aoss_qmp: qmp@c300000 { compatible = "qcom,sm8250-aoss-qmp"; reg = <0 0x0c300000 0 0x100000>; @@ -1880,13 +2949,385 @@ bias-disable; }; }; + + qup_uart2_default: qup-uart2-default { + mux { + pins = "gpio117", "gpio118"; + function = "qup2"; + }; + }; + + qup_uart6_default: qup-uart6-default { + mux { + pins = "gpio16", "gpio17", + "gpio18", "gpio19"; + function = "qup6"; + }; + }; + + qup_uart12_default: qup-uart12-default { + mux { + pins = "gpio34", "gpio35"; + function = "qup12"; + }; + }; + + qup_uart17_default: qup-uart17-default { + mux { + pins = "gpio52", "gpio53", + "gpio54", "gpio55"; + function = "qup17"; + }; + }; + + qup_uart18_default: qup-uart18-default { + mux { + pins = "gpio58", "gpio59"; + function = "qup18"; + }; + }; + + pri_mi2s_sck_active: pri-mi2s-sck-active { + mux { + pins = "gpio138"; + function = "mi2s0_sck"; + }; + + config { + pins = "gpio138"; + drive-strength = <8>; + bias-disable; + output-high; + }; + }; + + pri_mi2s_ws_active: pri-mi2s-ws-active { + mux { + pins = "gpio141"; + function = "mi2s0_ws"; + }; + + config { + pins = "gpio141"; + drive-strength = <8>; + output-high; + }; + }; + + pri_mi2s_sd0_active: pri-mi2s-sd0-active { + mux { + pins = "gpio139"; + function = "mi2s0_data0"; + }; + + config { + pins = "gpio139"; + drive-strength = <8>; + bias-disable; + output-high; + }; + }; + + pri_mi2s_sd1_active: pri-mi2s-sd1-active { + mux { + pins = "gpio140"; + function = "mi2s0_data1"; + }; + + config { + pins = "gpio140"; + drive-strength = <8>; + output-high; + }; + }; + + tert_mi2s_sck_active: tert-mi2s-sck-active { + mux { + pins = "gpio133"; + function = "mi2s2_sck"; + }; + + config { + pins = "gpio133"; + drive-strength = <8>; + bias-disable; + output-high; + }; + }; + + tert_mi2s_sd0_active: tert-mi2s-sd0-active { + mux { + pins = "gpio134"; + function = "mi2s2_data0"; + }; + + config { + pins = "gpio134"; + drive-strength = <8>; + bias-disable; + output-high; + }; + }; + + tert_mi2s_ws_active: tert-mi2s-ws-active { + mux { + pins = "gpio135"; + function = "mi2s2_ws"; + }; + + config { + pins = "gpio135"; + drive-strength = <8>; + output-high; + }; + }; + }; + + apps_smmu: iommu@15000000 { + compatible = "qcom,sm8250-smmu-500", "arm,mmu-500"; + reg = <0 0x15000000 0 0x100000>; + #iommu-cells = <2>; + #global-interrupts = <2>; + interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 103 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 105 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 107 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 181 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 182 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 183 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 184 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 185 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 186 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 187 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 188 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 189 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 190 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 191 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 192 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 315 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 316 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 317 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 318 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 319 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 320 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 321 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 322 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 323 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 324 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 325 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 326 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 327 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 328 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 329 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 330 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 331 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 332 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 333 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 334 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 335 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 336 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 337 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 338 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 339 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 340 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 341 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 342 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 343 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 344 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 345 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 395 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 396 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 397 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 398 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 399 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 400 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 401 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 402 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 403 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 404 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 405 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 406 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 407 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 408 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 409 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 412 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 418 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 419 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 421 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 423 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 424 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 425 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 690 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 691 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 692 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 693 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 694 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 695 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 696 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 697 IRQ_TYPE_LEVEL_HIGH>, + <GIC_SPI 707 IRQ_TYPE_LEVEL_HIGH>; }; + +// lpass@2c00000 { +// compatible = "qcom,sm8250-lpass"; + + lpass_tlmm: pinctrl@33c0000{ + compatible = "qcom,sm8250-lpass-pinctrl"; + reg = <0 0x33c0000 0x0 0x20000>, + <0 0x355a000 0x0 0x1000>; + gpio-controller; + #gpio-cells = <2>; + + clocks = <&q6afecc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6afecc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6afecc LPASS_CLK_ID_WSA_CORE_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6afecc LPASS_CLK_ID_WSA_CORE_NPL_MCLK LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "core", "audio","iface", "npl"; + + + wsa_swr_clk_pin { + wsa_swr_clk_sleep: wsa_swr_clk_sleep { + mux { + pins = "gpio10"; + function = "func2"; + }; + + config { + pins = "gpio10"; + drive-strength = <2>; + input-enable; + bias-pull-down; + }; + }; + + wsa_swr_clk_active: wsa_swr_clk_active { + mux { + pins = "gpio10"; + function = "func2"; + }; + + config { + pins = "gpio10"; + drive-strength = <2>; + slew-rate = <1>; + bias-disable; + }; + }; + }; + + wsa_swr_data_pin { + wsa_swr_data_sleep: wsa_swr_data_sleep { + mux { + pins = "gpio11"; + function = "func2"; + }; + + config { + pins = "gpio11"; + drive-strength = <2>; + input-enable; + bias-pull-down; + }; + }; + + wsa_swr_data_active: wsa_swr_data_active { + mux { + pins = "gpio11"; + function = "func2"; + }; + + config { + pins = "gpio11"; + drive-strength = <2>; + slew-rate = <1>; + bias-bus-hold; + }; + }; + }; + }; + + audiocc: audiocc@3300000 { + compatible = "qcom,sm8250-audiocc"; + reg = <0 0x03300000 0 0x30000>, + <0 0x03380000 0 0x40000>; + #clock-cells = <1>; + clocks = <&q6afecc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6afecc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "core", "audio"; + + + }; + + wsamacro: lpass-wsa-macro@3240000 { + pinctrl-names = "default"; + pinctrl-0 = <&wsa_swr_clk_active &wsa_swr_data_active>; + + compatible = "qcom,sm8250-lpass-wsa-macro"; + reg = <0 0x3240000 0 0x1000>, + <0 0x3370000 0 0x1000>; + clocks = <&audiocc LPASS_CDC_WSA_MCLK>, + <&audiocc LPASS_CDC_WSA_NPL>, + <&q6afecc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&q6afecc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>, + <&audiocc LPASS_CDC_VA_MCLK>; + + clock-names = "mclk", "npl", "macro", "dcodec", "va"; + + #clock-cells = <0>; + clock-frequency = <9600000>; + clock-output-names = "mclk"; + #address-cells = <2>; + #size-cells = <2>; + #sound-dai-cells = <0>; + ranges; + + swr0: controller@3250000 { + reg = <0 0x3250000 0 0x2000>; + compatible = "qcom,soundwire-v1.5.1"; + interrupts = <GIC_SPI 202 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&wsamacro>; + clock-names = "iface"; + + qcom,din-ports = <2>; + qcom,dout-ports = <6>; + + qcom,ports-sinterval-low = /bits/ 8 <0x07 0x1F 0x3F 0x07 0x1F 0x3F 0x0F 0x0F>; + qcom,ports-offset1 = /bits/ 8 <0x01 0x02 0x0C 0x06 0x12 0x0D 0x07 0x0A >; + qcom,ports-offset2 = /bits/ 8 <0x00 0x00 0x1F 0x00 0x00 0x1F 0x00 0x00>; + qcom,ports-block-pack-mode = /bits/ 8 <0x0 0x0 0x1 0x0 0x0 0x1 0x0 0x0>; + + #sound-dai-cells = <1>; + #address-cells = <2>; + #size-cells = <0>; + }; + }; + //}; adsp: remoteproc@17300000 { compatible = "qcom,sm8250-adsp-pas"; reg = <0 0x17300000 0 0x100>; - interrupts-extended = <&pdc 6 IRQ_TYPE_LEVEL_HIGH>, + interrupts-extended = <&pdc 6 IRQ_TYPE_EDGE_RISING>, <&smp2p_adsp_in 0 IRQ_TYPE_EDGE_RISING>, <&smp2p_adsp_in 1 IRQ_TYPE_EDGE_RISING>, <&smp2p_adsp_in 2 IRQ_TYPE_EDGE_RISING>, @@ -1918,6 +3359,61 @@ label = "lpass"; qcom,remote-pid = <2>; + + apr { + compatible = "qcom,apr-v2"; + qcom,glink-channels = "apr_audio_svc"; + qcom,apr-domain = <APR_DOMAIN_ADSP>; + #address-cells = <1>; + #size-cells = <0>; + qcom,intents = <512 20>; + + apr-service@3 { + reg = <APR_SVC_ADSP_CORE>; + compatible = "qcom,q6core"; + qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; + }; + + q6afe: apr-service@4 { + compatible = "qcom,q6afe"; + reg = <APR_SVC_AFE>; + qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; + q6afedai: dais { + compatible = "qcom,q6afe-dais"; + #address-cells = <1>; + #size-cells = <0>; + #sound-dai-cells = <1>; + }; + + q6afecc: cc { + compatible = "qcom,q6afe-clocks"; + #clock-cells = <2>; + }; + }; + + q6asm: apr-service@7 { + compatible = "qcom,q6asm"; + reg = <APR_SVC_ASM>; + qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; + q6asmdai: dais { + compatible = "qcom,q6asm-dais"; + #address-cells = <1>; + #size-cells = <0>; + #sound-dai-cells = <1>; + iommus = <&apps_smmu 0x1801 0x0>; + }; + }; + + q6adm: apr-service@8 { + compatible = "qcom,q6adm"; + reg = <APR_SVC_ADM>; + qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; + q6routing: routing { + compatible = "qcom,q6adm-routing"; + #sound-dai-cells = <0>; + }; + }; + }; }; }; @@ -2066,6 +3562,24 @@ }; }; }; + + apps_bcm_voter: bcm_voter { + compatible = "qcom,bcm-voter"; + }; + }; + + cpufreq_hw: cpufreq@18591000 { + compatible = "qcom,sm8250-epss"; + reg = <0 0x18591000 0 0x1000>, + <0 0x18592000 0 0x1000>, + <0 0x18593000 0 0x1000>; + reg-names = "freq-domain0", "freq-domain1", + "freq-domain2"; + + clocks = <&rpmhcc RPMH_CXO_CLK>, <&gcc GPLL0>; + clock-names = "xo", "alternate"; + + #freq-domain-cells = <1>; }; }; @@ -2080,4 +3594,739 @@ <GIC_PPI 12 (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>; }; + + thermal-zones { + cpu0-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 1>; + + trips { + cpu0_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu0_alert1: trip-point1 { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu0_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu0_alert0>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu0_alert1>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu1-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 2>; + + trips { + cpu1_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu1_alert1: trip-point1 { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu1_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu1_alert0>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu1_alert1>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu2-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 3>; + + trips { + cpu2_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu2_alert1: trip-point1 { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu2_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu2_alert0>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu2_alert1>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu3-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 4>; + + trips { + cpu3_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu3_alert1: trip-point1 { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu3_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu3_alert0>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu3_alert1>; + cooling-device = <&CPU0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu4-top-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 7>; + + trips { + cpu4_top_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu4_top_alert1: trip-point1 { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu4_top_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu4_top_alert0>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu4_top_alert1>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu5-top-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 8>; + + trips { + cpu5_top_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu5_top_alert1: trip-point1 { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu5_top_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu5_top_alert0>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu5_top_alert1>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu6-top-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 9>; + + trips { + cpu6_top_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu6_top_alert1: trip-point1 { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu6_top_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu6_top_alert0>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu6_top_alert1>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu7-top-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 10>; + + trips { + cpu7_top_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu7_top_alert1: trip-point1 { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu7_top_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu7_top_alert0>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu7_top_alert1>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu4-bottom-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 11>; + + trips { + cpu4_bottom_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu4_bottom_alert1: trip-point1 { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu4_bottom_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu4_bottom_alert0>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu4_bottom_alert1>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu5-bottom-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 12>; + + trips { + cpu5_bottom_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu5_bottom_alert1: trip-point1 { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu5_bottom_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu5_bottom_alert0>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu5_bottom_alert1>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu6-bottom-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 13>; + + trips { + cpu6_bottom_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu6_bottom_alert1: trip-point1 { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu6_bottom_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu6_bottom_alert0>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu6_bottom_alert1>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + cpu7-bottom-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 14>; + + trips { + cpu7_bottom_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu7_bottom_alert1: trip-point1 { + temperature = <95000>; + hysteresis = <2000>; + type = "passive"; + }; + + cpu7_bottom_crit: cpu_crit { + temperature = <110000>; + hysteresis = <1000>; + type = "critical"; + }; + }; + + cooling-maps { + map0 { + trip = <&cpu7_bottom_alert0>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + map1 { + trip = <&cpu7_bottom_alert1>; + cooling-device = <&CPU4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>, + <&CPU7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>; + }; + }; + }; + + aoss0-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 0>; + + trips { + aoss0_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "hot"; + }; + }; + }; + + cluster0-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 5>; + + trips { + cluster0_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "hot"; + }; + cluster0_crit: cluster0_crit { + temperature = <110000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + }; + + cluster1-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 6>; + + trips { + cluster1_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "hot"; + }; + cluster1_crit: cluster1_crit { + temperature = <110000>; + hysteresis = <2000>; + type = "critical"; + }; + }; + }; + + gpu-thermal-top { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens0 15>; + + trips { + gpu1_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "hot"; + }; + }; + }; + + aoss1-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens1 0>; + + trips { + aoss1_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "hot"; + }; + }; + }; + + wlan-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens1 1>; + + trips { + wlan_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "hot"; + }; + }; + }; + + video-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens1 2>; + + trips { + video_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "hot"; + }; + }; + }; + + mem-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens1 3>; + + trips { + mem_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "hot"; + }; + }; + }; + + q6-hvx-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens1 4>; + + trips { + q6_hvx_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "hot"; + }; + }; + }; + + camera-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens1 5>; + + trips { + camera_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "hot"; + }; + }; + }; + + compute-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens1 6>; + + trips { + compute_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "hot"; + }; + }; + }; + + npu-thermal { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens1 7>; + + trips { + npu_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "hot"; + }; + }; + }; + + gpu-thermal-bottom { + polling-delay-passive = <250>; + polling-delay = <1000>; + + thermal-sensors = <&tsens1 8>; + + trips { + gpu2_alert0: trip-point0 { + temperature = <90000>; + hysteresis = <2000>; + type = "hot"; + }; + }; + }; + }; }; diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 73db58f2e23b..b52be35d1aff 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -442,7 +442,7 @@ CONFIG_I2C_MV64XXX=y CONFIG_I2C_OWL=y CONFIG_I2C_PXA=y CONFIG_I2C_QCOM_CCI=m -CONFIG_I2C_QCOM_GENI=m +CONFIG_I2C_QCOM_GENI=y CONFIG_I2C_QUP=y CONFIG_I2C_RK3X=y CONFIG_I2C_SH_MOBILE=y @@ -450,6 +450,7 @@ CONFIG_I2C_TEGRA=y CONFIG_I2C_UNIPHIER_F=y CONFIG_I2C_RCAR=y CONFIG_I2C_CROS_EC_TUNNEL=y +CONFIG_I2C_QCOM_CCI=y CONFIG_SPI=y CONFIG_SPI_ARMADA_3700=y CONFIG_SPI_BCM2835=m @@ -466,7 +467,7 @@ CONFIG_SPI_PL022=y CONFIG_SPI_ROCKCHIP=y CONFIG_SPI_QCOM_QSPI=m CONFIG_SPI_QUP=y -CONFIG_SPI_QCOM_GENI=m +CONFIG_SPI_QCOM_GENI=y CONFIG_SPI_S3C64XX=y CONFIG_SPI_SH_MSIOF=m CONFIG_SPI_SUN6I=y @@ -496,6 +497,7 @@ CONFIG_PINCTRL_SC7180=y CONFIG_PINCTRL_SDM845=y CONFIG_PINCTRL_SM8150=y CONFIG_PINCTRL_SM8250=y +CONFIG_PINCTRL_LPASS=y CONFIG_GPIO_ALTERA=m CONFIG_GPIO_DWAPB=y CONFIG_GPIO_MB86S7X=y @@ -544,6 +546,7 @@ CONFIG_EXYNOS_THERMAL=y CONFIG_TEGRA_BPMP_THERMAL=m CONFIG_QCOM_TSENS=y CONFIG_QCOM_SPMI_TEMP_ALARM=m +CONFIG_QCOM_SPMI_ADC_TM5=m CONFIG_UNIPHIER_THERMAL=y CONFIG_WATCHDOG=y CONFIG_ARM_SP805_WATCHDOG=y @@ -589,6 +592,7 @@ CONFIG_REGULATOR_PWM=y CONFIG_REGULATOR_QCOM_RPMH=y CONFIG_REGULATOR_QCOM_SMD_RPM=y CONFIG_REGULATOR_QCOM_SPMI=y +CONFIG_REGULATOR_QCOM_USB_VBUS=m CONFIG_REGULATOR_RK808=y CONFIG_REGULATOR_S2MPS11=y CONFIG_REGULATOR_VCTRL=m @@ -626,6 +630,8 @@ CONFIG_VIDEO_OV5645=m CONFIG_VIDEO_QCOM_CAMSS=m CONFIG_VIDEO_QCOM_VENUS=m CONFIG_DRM=m +CONFIG_VIDEO_QCOM_VENUS=m +CONFIG_VIDEO_QCOM_CAMSS=m CONFIG_DRM_I2C_NXP_TDA998X=m CONFIG_DRM_MALI_DISPLAY=m CONFIG_DRM_NOUVEAU=m @@ -660,6 +666,7 @@ CONFIG_DRM_THINE_THC63LVD1024=m CONFIG_DRM_TI_SN65DSI86=m CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA=m CONFIG_DRM_LONTIUM_LT9611=m +CONFIG_DRM_LONTIUM_LT9611UXC=m CONFIG_DRM_I2C_ADV7511=m CONFIG_DRM_I2C_ADV7511_AUDIO=y CONFIG_DRM_DW_HDMI_AHB_AUDIO=m @@ -692,6 +699,7 @@ CONFIG_SND_SOC_FSL_SAI=m CONFIG_SND_MESON_AXG_SOUND_CARD=m CONFIG_SND_MESON_GX_SOUND_CARD=m CONFIG_SND_SOC_SDM845=m +CONFIG_SND_SOC_SM8250=m CONFIG_SND_SOC_ROCKCHIP=m CONFIG_SND_SOC_ROCKCHIP_SPDIF=m CONFIG_SND_SOC_ROCKCHIP_RT5645=m @@ -708,6 +716,7 @@ CONFIG_SND_SOC_TAS571X=m CONFIG_SND_SOC_WCD934X=m CONFIG_SND_SOC_WM8904=m CONFIG_SND_SOC_WSA881X=m +CONFIG_SND_SOC_LPASS_WSA_MACRO=m CONFIG_SND_SIMPLE_CARD=m CONFIG_SND_AUDIO_GRAPH_CARD=m CONFIG_I2C_HID=m @@ -762,6 +771,7 @@ CONFIG_TYPEC=m CONFIG_TYPEC_TCPM=m CONFIG_TYPEC_FUSB302=m CONFIG_TYPEC_HD3SS3220=m +CONFIG_TYPEC_QCOM_PMIC=m CONFIG_MMC=y CONFIG_MMC_BLOCK_MINORS=32 CONFIG_MMC_ARMMMCI=y @@ -888,7 +898,10 @@ CONFIG_SDM_VIDEOCC_845=y CONFIG_SDM_DISPCC_845=y CONFIG_SM_GCC_8150=y CONFIG_SM_GCC_8250=y +CONFIG_SM_GPUCC_8250=y +CONFIG_SM_DISPCC_8250=y CONFIG_QCOM_HFPLL=y +CONFIG_CLK_GFM_LPASS=m CONFIG_HWSPINLOCK=y CONFIG_SDM_GPUCC_845=y CONFIG_SDM_DISPCC_845=y @@ -986,11 +999,13 @@ CONFIG_PHY_HISTB_COMBPHY=y CONFIG_PHY_HISI_INNO_USB2=y CONFIG_PHY_MVEBU_CP110_COMPHY=y CONFIG_PHY_QCOM_QMP=y +CONFIG_PHY_QCOM_QMP_TYPEC=y CONFIG_PHY_QCOM_QUSB2=m CONFIG_PHY_QCOM_UFS=y CONFIG_PHY_QCOM_USB_HS=y CONFIG_PHY_QCOM_USB_HS_SNPS_28NM=y CONFIG_PHY_QCOM_USB_SS=y +CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2=y CONFIG_PHY_RCAR_GEN3_PCIE=y CONFIG_PHY_RCAR_GEN3_USB2=y CONFIG_PHY_RCAR_GEN3_USB3=m @@ -1026,6 +1041,14 @@ CONFIG_SLIMBUS=m CONFIG_SLIM_QCOM_CTRL=m CONFIG_SLIM_QCOM_NGD_CTRL=m CONFIG_MUX_MMIO=y +CONFIG_INTERCONNECT=y +CONFIG_INTERCONNECT_QCOM=y +CONFIG_INTERCONNECT_QCOM_MSM8916=m +CONFIG_INTERCONNECT_QCOM_SMD_RPM=m +CONFIG_INTERCONNECT_QCOM_QCS404=m +CONFIG_INTERCONNECT_QCOM_BCM_VOTER=m +CONFIG_INTERCONNECT_QCOM_SDM845=m +CONFIG_INTERCONNECT_QCOM_SM8250=m CONFIG_EXT2_FS=y CONFIG_EXT3_FS=y CONFIG_EXT4_FS_POSIX_ACL=y diff --git a/arch/powerpc/configs/pasemi_defconfig b/arch/powerpc/configs/pasemi_defconfig index af9af03059e4..15ed8d0aa014 100644 --- a/arch/powerpc/configs/pasemi_defconfig +++ b/arch/powerpc/configs/pasemi_defconfig @@ -108,7 +108,6 @@ CONFIG_FB_NVIDIA=y CONFIG_FB_NVIDIA_I2C=y CONFIG_FB_RADEON=y # CONFIG_LCD_CLASS_DEVICE is not set -CONFIG_VGACON_SOFT_SCROLLBACK=y CONFIG_LOGO=y CONFIG_SOUND=y CONFIG_SND=y diff --git a/arch/powerpc/configs/ppc6xx_defconfig b/arch/powerpc/configs/ppc6xx_defconfig index 5e6f92ba3210..66e9a0fd64ff 100644 --- a/arch/powerpc/configs/ppc6xx_defconfig +++ b/arch/powerpc/configs/ppc6xx_defconfig @@ -743,7 +743,6 @@ CONFIG_FB_TRIDENT=m CONFIG_FB_SM501=m CONFIG_FB_IBM_GXT4500=y CONFIG_LCD_PLATFORM=m -CONFIG_VGACON_SOFT_SCROLLBACK=y CONFIG_FRAMEBUFFER_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y CONFIG_LOGO=y diff --git a/arch/x86/configs/i386_defconfig b/arch/x86/configs/i386_defconfig index d7577fece9eb..f556827dea58 100644 --- a/arch/x86/configs/i386_defconfig +++ b/arch/x86/configs/i386_defconfig @@ -186,7 +186,6 @@ CONFIG_DRM_I915=y CONFIG_FB_MODE_HELPERS=y CONFIG_FB_TILEBLITTING=y CONFIG_FB_EFI=y -CONFIG_VGACON_SOFT_SCROLLBACK=y CONFIG_LOGO=y # CONFIG_LOGO_LINUX_MONO is not set # CONFIG_LOGO_LINUX_VGA16 is not set diff --git a/arch/x86/configs/x86_64_defconfig b/arch/x86/configs/x86_64_defconfig index f85600143747..9936528e1939 100644 --- a/arch/x86/configs/x86_64_defconfig +++ b/arch/x86/configs/x86_64_defconfig @@ -181,7 +181,6 @@ CONFIG_DRM_I915=y CONFIG_FB_MODE_HELPERS=y CONFIG_FB_TILEBLITTING=y CONFIG_FB_EFI=y -CONFIG_VGACON_SOFT_SCROLLBACK=y CONFIG_LOGO=y # CONFIG_LOGO_LINUX_MONO is not set # CONFIG_LOGO_LINUX_VGA16 is not set diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index ce9dcffdc5bf..6e8b131bd245 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -467,7 +467,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, (soc_ver & 0x0000000f); snprintf(config.fwname, sizeof(config.fwname), "qca/crbtfw%02x.tlv", rom_ver); - } else if (soc_type == QCA_QCA6390) { + } else if (soc_type == QCA_QCA6390 || soc_type == QCA_QCA6391) { rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f); snprintf(config.fwname, sizeof(config.fwname), @@ -494,7 +494,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, else if (qca_is_wcn399x(soc_type)) snprintf(config.fwname, sizeof(config.fwname), "qca/crnv%02x.bin", rom_ver); - else if (soc_type == QCA_QCA6390) + else if (soc_type == QCA_QCA6390 || soc_type == QCA_QCA6391) snprintf(config.fwname, sizeof(config.fwname), "qca/htnv%02x.bin", rom_ver); else diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h index d81b74c408a5..67c1429f67fd 100644 --- a/drivers/bluetooth/btqca.h +++ b/drivers/bluetooth/btqca.h @@ -130,6 +130,7 @@ enum qca_btsoc_type { QCA_WCN3998, QCA_WCN3991, QCA_QCA6390, + QCA_QCA6391, }; #if IS_ENABLED(CONFIG_BT_QCA) diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 20e1dedbc58c..a4ec499fcec8 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1064,7 +1064,7 @@ static void qca_controller_memdump(struct work_struct *work) * bits, so skip this checking for missing packet. */ while ((seq_no > qca_memdump->current_seq_no + 1) && - (soc_type != QCA_QCA6390) && + (soc_type != QCA_QCA6390 || soc_type != QCA_QCA6391) && seq_no != QCA_LAST_SEQUENCE_NUM) { bt_dev_err(hu->hdev, "QCA controller missed packet:%d", qca_memdump->current_seq_no); @@ -1801,6 +1801,12 @@ static const struct qca_device_data qca_soc_data_qca6390 = { .num_vregs = 0, }; +/* TODO: Add regulators */ +static const struct qca_device_data qca_soc_data_qca6391 = { + .soc_type = QCA_QCA6391, + .num_vregs = 0, +}; + static void qca_power_shutdown(struct hci_uart *hu) { struct qca_serdev *qcadev; @@ -2050,7 +2056,7 @@ static void qca_serdev_shutdown(struct device *dev) const u8 ibs_wake_cmd[] = { 0xFD }; const u8 edl_reset_soc_cmd[] = { 0x01, 0x00, 0xFC, 0x01, 0x05 }; - if (qcadev->btsoc_type == QCA_QCA6390) { + if (qcadev->btsoc_type == QCA_QCA6390 || qcadev->btsoc_type == QCA_QCA6391) { serdev_device_write_flush(serdev); ret = serdev_device_write_buf(serdev, ibs_wake_cmd, sizeof(ibs_wake_cmd)); @@ -2174,6 +2180,7 @@ static SIMPLE_DEV_PM_OPS(qca_pm_ops, qca_suspend, qca_resume); static const struct of_device_id qca_bluetooth_of_match[] = { { .compatible = "qcom,qca6174-bt" }, { .compatible = "qcom,qca6390-bt", .data = &qca_soc_data_qca6390}, + { .compatible = "qcom,qca6391-bt", .data = &qca_soc_data_qca6391}, { .compatible = "qcom,qca9377-bt" }, { .compatible = "qcom,wcn3990-bt", .data = &qca_soc_data_wcn3990}, { .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991}, diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 21784c9395b2..57c265ad34e4 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -41,6 +41,7 @@ config QCOM_CLK_APCC_MSM8996 tristate "MSM8996 CPU Clock Controller" select QCOM_KRYO_L2_ACCESSORS depends on ARM64 + depends on COMMON_CLK_QCOM help Support for the CPU clock controller on msm8996 devices. Say Y if you want to support CPU clock scaling using CPUfreq @@ -444,6 +445,24 @@ config SM_GPUCC_8250 Say Y if you want to support graphics controller devices and functionality such as 3D graphics. +config SM_DISPCC_8150 + tristate "SM8150 Display Clock Controller" + select SM_GCC_8150 + help + Support for the display clock controller on Qualcomm Technologies, Inc + SM8150 devices. + Say Y if you want to support display devices and functionality such as + splash screen. + +config SM_DISPCC_8250 + tristate "SM8250 Display Clock Controller" + select SM_GCC_8250 + help + Support for the display clock controller on Qualcomm Technologies, Inc + SM8250 devices. + Say Y if you want to support display devices and functionality such as + splash screen. + config SPMI_PMIC_CLKDIV tristate "SPMI PMIC clkdiv Support" depends on SPMI || COMPILE_TEST diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index d918b4716d27..ad0072595ab6 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -64,6 +64,8 @@ obj-$(CONFIG_SDM_GCC_845) += gcc-sdm845.o obj-$(CONFIG_SDM_GPUCC_845) += gpucc-sdm845.o obj-$(CONFIG_SDM_LPASSCC_845) += lpasscc-sdm845.o obj-$(CONFIG_SDM_VIDEOCC_845) += videocc-sdm845.o +obj-$(CONFIG_SM_DISPCC_8150) += dispcc-sm8150.o +obj-$(CONFIG_SM_DISPCC_8250) += dispcc-sm8250.o obj-$(CONFIG_SM_GCC_8150) += gcc-sm8150.o obj-$(CONFIG_SM_GCC_8250) += gcc-sm8250.o obj-$(CONFIG_SM_GPUCC_8150) += gpucc-sm8150.o diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c index f869fc6aaed6..9ba202d28959 100644 --- a/drivers/clk/qcom/clk-branch.c +++ b/drivers/clk/qcom/clk-branch.c @@ -152,6 +152,70 @@ const struct clk_ops clk_branch2_aon_ops = { }; EXPORT_SYMBOL_GPL(clk_branch2_aon_ops); +static int clk_branch2_hw_ctl_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + if (!(hw->init->flags & CLK_SET_RATE_PARENT)) { + pr_err("SET_RATE_PARENT flag needs to be set for %s\n", + clk_hw_get_name(hw)); + return -EINVAL; + } + + return 0; +} + +static unsigned long clk_branch2_hw_ctl_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return parent_rate; +} + +static int clk_branch2_hw_ctl_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_hw *clkp; + + clkp = clk_hw_get_parent(hw); + if (!clkp) + return -EINVAL; + + req->best_parent_hw = clkp; + req->best_parent_rate = clk_hw_round_rate(clkp, req->rate); + + return 0; +} + +static int clk_branch2_hw_ctl_enable(struct clk_hw *hw) +{ + struct clk_hw *parent = clk_hw_get_parent(hw); + + /* The parent branch clock should have been prepared prior to this. */ + if (!parent || (parent && !clk_hw_is_prepared(parent))) + return -EINVAL; + + return clk_enable_regmap(hw); +} + +static void clk_branch2_hw_ctl_disable(struct clk_hw *hw) +{ + struct clk_hw *parent = clk_hw_get_parent(hw); + + if (!parent) + return; + + clk_disable_regmap(hw); +} + +const struct clk_ops clk_branch2_hw_ctl_ops = { + .enable = clk_branch2_hw_ctl_enable, + .disable = clk_branch2_hw_ctl_disable, + .is_enabled = clk_is_enabled_regmap, + .set_rate = clk_branch2_hw_ctl_set_rate, + .recalc_rate = clk_branch2_hw_ctl_recalc_rate, + .determine_rate = clk_branch2_hw_ctl_determine_rate, +}; +EXPORT_SYMBOL_GPL(clk_branch2_hw_ctl_ops); + const struct clk_ops clk_branch_simple_ops = { .enable = clk_enable_regmap, .disable = clk_disable_regmap, diff --git a/drivers/clk/qcom/clk-branch.h b/drivers/clk/qcom/clk-branch.h index 17a58119165e..83ae9d14a03f 100644 --- a/drivers/clk/qcom/clk-branch.h +++ b/drivers/clk/qcom/clk-branch.h @@ -39,6 +39,7 @@ struct clk_branch { extern const struct clk_ops clk_branch_ops; extern const struct clk_ops clk_branch2_ops; +extern const struct clk_ops clk_branch2_hw_ctl_ops; extern const struct clk_ops clk_branch_simple_ops; extern const struct clk_ops clk_branch2_aon_ops; diff --git a/drivers/clk/qcom/clk-cpu-8996.c b/drivers/clk/qcom/clk-cpu-8996.c index 4a4fde8dd12d..50683db99620 100644 --- a/drivers/clk/qcom/clk-cpu-8996.c +++ b/drivers/clk/qcom/clk-cpu-8996.c @@ -32,7 +32,9 @@ * * The primary PLL is what drives the CPU clk, except for times * when we are reprogramming the PLL itself (for rate changes) when - * we temporarily switch to an alternate PLL. + * we temporarily switch to an alternate PLL. A subsequent patch adds + * support to switch between primary and alternate PLL during rate + * changes. * * The primary PLL operates on a single VCO range, between 600MHz * and 3GHz. However the CPUs do support OPPs with frequencies @@ -44,6 +46,7 @@ * Primary PLL --> PLL_EARLY --> PMUX(1) --> CPU clk * and for frequencies between 300MHz and 600MHz we follow * Primary PLL --> PLL/2 --> SMUX(1) --> PMUX(0) --> CPU clk + * Support for this is added in a subsequent patch as well. * * ACD stands for Adaptive Clock Distribution and is used to * detect voltage droops. @@ -534,5 +537,6 @@ static struct platform_driver qcom_cpu_clk_msm8996_driver = { }; module_platform_driver(qcom_cpu_clk_msm8996_driver); +MODULE_ALIAS("platform:msm8996-apcc"); MODULE_DESCRIPTION("QCOM MSM8996 CPU Clock Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index 357159fe85b5..fc7fa8ed2da8 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -1182,15 +1182,8 @@ static int clk_rcg2_dp_set_rate_and_parent(struct clk_hw *hw, static int clk_rcg2_dp_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { - struct clk_rate_request parent_req = *req; - int ret; - - ret = __clk_determine_rate(clk_hw_get_parent(hw), &parent_req); - if (ret) - return ret; - - req->best_parent_rate = parent_req.rate; - + req->best_parent_rate = clk_hw_round_rate(req->best_parent_hw, + req->best_parent_rate); return 0; } diff --git a/drivers/clk/qcom/dispcc-sm8150.c b/drivers/clk/qcom/dispcc-sm8150.c new file mode 100644 index 000000000000..ccfc07540185 --- /dev/null +++ b/drivers/clk/qcom/dispcc-sm8150.c @@ -0,0 +1,1369 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> + +#include <dt-bindings/clock/qcom,dispcc-sm8150.h> + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "clk-regmap-divider.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +#define DISP_CC_MISC_CMD 0x8000 + +enum { + P_BI_TCXO, + P_CORE_BI_PLL_TEST_SE, + P_DISP_CC_PLL0_OUT_MAIN, + P_DISP_CC_PLL1_OUT_EVEN, + P_DISP_CC_PLL1_OUT_MAIN, + P_DP_PHY_PLL_LINK_CLK, + P_DP_PHY_PLL_VCO_DIV_CLK, + P_DPTX1_PHY_PLL_LINK_CLK, + P_DPTX1_PHY_PLL_VCO_DIV_CLK, + P_DPTX2_PHY_PLL_LINK_CLK, + P_DPTX2_PHY_PLL_VCO_DIV_CLK, + P_DSI0_PHY_PLL_OUT_BYTECLK, + P_DSI0_PHY_PLL_OUT_DSICLK, + P_DSI1_PHY_PLL_OUT_BYTECLK, + P_DSI1_PHY_PLL_OUT_DSICLK, + P_EDP_PHY_PLL_LINK_CLK, + P_EDP_PHY_PLL_VCO_DIV_CLK, +}; + +static const struct parent_map disp_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_DP_PHY_PLL_LINK_CLK, 1 }, + { P_DP_PHY_PLL_VCO_DIV_CLK, 2 }, + { P_DPTX1_PHY_PLL_LINK_CLK, 3 }, + { P_DPTX1_PHY_PLL_VCO_DIV_CLK, 4 }, + { P_DPTX2_PHY_PLL_LINK_CLK, 5 }, + { P_DPTX2_PHY_PLL_VCO_DIV_CLK, 6 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const disp_cc_parent_names_0[] = { + "bi_tcxo", + "dp_link_clk_divsel_ten", + "dp_vco_divided_clk_src_mux", + "dptx1_phy_pll_link_clk", + "dptx1_phy_pll_vco_div_clk", + "dptx2_phy_pll_link_clk", + "dptx2_phy_pll_vco_div_clk", + "core_bi_pll_test_se", +}; + +static const struct parent_map disp_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const disp_cc_parent_names_1[] = { + "bi_tcxo", + "core_bi_pll_test_se", +}; + +static const struct parent_map disp_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_DSI0_PHY_PLL_OUT_BYTECLK, 1 }, + { P_DSI1_PHY_PLL_OUT_BYTECLK, 2 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const disp_cc_parent_names_2[] = { + "bi_tcxo", + "dsi0_phy_pll_out_byteclk", + "dsi1_phy_pll_out_byteclk", + "core_bi_pll_test_se", +}; + +static const struct parent_map disp_cc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_DISP_CC_PLL1_OUT_MAIN, 4 }, + { P_DISP_CC_PLL1_OUT_EVEN, 5 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const disp_cc_parent_names_3[] = { + "bi_tcxo", + "disp_cc_pll1", + "disp_cc_pll1_out_even", + "core_bi_pll_test_se", +}; + +static const struct parent_map disp_cc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_EDP_PHY_PLL_LINK_CLK, 1 }, + { P_EDP_PHY_PLL_VCO_DIV_CLK, 2 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const disp_cc_parent_names_4[] = { + "bi_tcxo", + "edp_phy_pll_link_clk", + "edp_phy_pll_vco_div_clk", + "core_bi_pll_test_se", +}; + +static const struct parent_map disp_cc_parent_map_5[] = { + { P_BI_TCXO, 0 }, + { P_DISP_CC_PLL0_OUT_MAIN, 1 }, + { P_DISP_CC_PLL1_OUT_MAIN, 4 }, + { P_DISP_CC_PLL1_OUT_EVEN, 5 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const disp_cc_parent_names_5[] = { + "bi_tcxo", + "disp_cc_pll0", + "disp_cc_pll1", + "disp_cc_pll1_out_even", + "core_bi_pll_test_se", +}; + +static const struct parent_map disp_cc_parent_map_6[] = { + { P_BI_TCXO, 0 }, + { P_DSI0_PHY_PLL_OUT_DSICLK, 1 }, + { P_DSI1_PHY_PLL_OUT_DSICLK, 2 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const char * const disp_cc_parent_names_6[] = { + "bi_tcxo", + "dsi0_phy_pll_out_dsiclk", + "dsi1_phy_pll_out_dsiclk", + "core_bi_pll_test_se", +}; + +static struct pll_vco trion_vco[] = { + { 249600000, 2000000000, 0 }, +}; + +static struct alpha_pll_config disp_cc_pll0_config = { + .l = 0x47, + .alpha = 0xE000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002267, + .config_ctl_hi1_val = 0x00000024, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000805, + .user_ctl_hi1_val = 0x000000D0, +}; + +static struct clk_alpha_pll disp_cc_pll0 = { + .offset = 0x0, + .vco_table = trion_vco, + .num_vco = ARRAY_SIZE(trion_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_pll0", + .parent_names = (const char *[]){ "bi_tcxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_trion_ops, + }, + }, +}; + +static struct alpha_pll_config disp_cc_pll1_config = { + .l = 0x1F, + .alpha = 0x4000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002267, + .config_ctl_hi1_val = 0x00000024, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000805, + .user_ctl_hi1_val = 0x000000D0, +}; + +static struct clk_alpha_pll disp_cc_pll1 = { + .offset = 0x1000, + .vco_table = trion_vco, + .num_vco = ARRAY_SIZE(trion_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TRION], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_pll1", + .parent_names = (const char *[]){ "bi_tcxo" }, + .num_parents = 1, + .ops = &clk_alpha_pll_trion_ops, + }, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(37500000, P_DISP_CC_PLL1_OUT_MAIN, 16, 0, 0), + F(75000000, P_DISP_CC_PLL1_OUT_MAIN, 8, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_ahb_clk_src = { + .cmd_rcgr = 0x22bc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_3, + .freq_tbl = ftbl_disp_cc_mdss_ahb_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_ahb_clk_src", + .parent_names = disp_cc_parent_names_3, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_byte0_clk_src = { + .cmd_rcgr = 0x2110, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte0_clk_src", + .parent_names = disp_cc_parent_names_2, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_byte2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_byte1_clk_src = { + .cmd_rcgr = 0x212c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte1_clk_src", + .parent_names = disp_cc_parent_names_2, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_byte2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_dp_aux1_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_dp_aux1_clk_src = { + .cmd_rcgr = 0x2240, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_mdss_dp_aux1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_aux1_clk_src", + .parent_names = disp_cc_parent_names_1, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_aux_clk_src = { + .cmd_rcgr = 0x21dc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_mdss_dp_aux1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_aux_clk_src", + .parent_names = disp_cc_parent_names_1, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_dp_crypto1_clk_src[] = { + F( 108000, P_DP_PHY_PLL_LINK_CLK, 3, 0, 0), + F( 180000, P_DP_PHY_PLL_LINK_CLK, 3, 0, 0), + F( 360000, P_DP_PHY_PLL_LINK_CLK, 3, 0, 0), + F( 540000, P_DP_PHY_PLL_LINK_CLK, 3, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_dp_crypto1_clk_src = { + .cmd_rcgr = 0x2228, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_mdss_dp_crypto1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_crypto1_clk_src", + .parent_names = disp_cc_parent_names_0, + .num_parents = 8, + .flags = CLK_GET_RATE_NOCACHE, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_crypto_clk_src = { + .cmd_rcgr = 0x2194, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_mdss_dp_crypto1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_crypto_clk_src", + .parent_names = disp_cc_parent_names_0, + .num_parents = 8, + .flags = CLK_GET_RATE_NOCACHE, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_dp_link1_clk_src[] = { + F( 162000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0), + F( 270000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0), + F( 540000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0), + F( 810000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_dp_link1_clk_src = { + .cmd_rcgr = 0x220c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_mdss_dp_link1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_link1_clk_src", + .parent_names = disp_cc_parent_names_0, + .num_parents = 8, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_link_clk_src = { + .cmd_rcgr = 0x2178, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_mdss_dp_link1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_link_clk_src", + .parent_names = disp_cc_parent_names_0, + .num_parents = 8, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_pixel1_clk_src = { + .cmd_rcgr = 0x21c4, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel1_clk_src", + .parent_names = disp_cc_parent_names_0, + .num_parents = 8, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_pixel2_clk_src = { + .cmd_rcgr = 0x21f4, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel2_clk_src", + .parent_names = disp_cc_parent_names_0, + .num_parents = 8, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_pixel_clk_src = { + .cmd_rcgr = 0x21ac, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel_clk_src", + .parent_names = disp_cc_parent_names_0, + .num_parents = 8, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_edp_aux_clk_src = { + .cmd_rcgr = 0x228c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_mdss_dp_aux1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_aux_clk_src", + .parent_names = disp_cc_parent_names_1, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_edp_gtc_clk_src = { + .cmd_rcgr = 0x22a4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_3, + .freq_tbl = ftbl_disp_cc_mdss_dp_aux1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_gtc_clk_src", + .parent_names = disp_cc_parent_names_3, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_edp_link_clk_src[] = { + F( 19200000, P_BI_TCXO, 1, 0, 0), + F( 270000000, P_EDP_PHY_PLL_LINK_CLK, 1, 0, 0), + F( 594000000, P_EDP_PHY_PLL_LINK_CLK, 1, 0, 0), + F( 810000000, P_EDP_PHY_PLL_LINK_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_edp_link_clk_src = { + .cmd_rcgr = 0x2270, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_4, + .freq_tbl = ftbl_disp_cc_mdss_edp_link_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_link_clk_src", + .parent_names = disp_cc_parent_names_4, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_edp_pixel_clk_src = { + .cmd_rcgr = 0x2258, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_4, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_pixel_clk_src", + .parent_names = disp_cc_parent_names_4, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_esc0_clk_src = { + .cmd_rcgr = 0x2148, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .freq_tbl = ftbl_disp_cc_mdss_dp_aux1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_esc0_clk_src", + .parent_names = disp_cc_parent_names_2, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_esc1_clk_src = { + .cmd_rcgr = 0x2160, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .freq_tbl = ftbl_disp_cc_mdss_dp_aux1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_esc1_clk_src", + .parent_names = disp_cc_parent_names_2, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(85714286, P_DISP_CC_PLL1_OUT_MAIN, 7, 0, 0), + F(100000000, P_DISP_CC_PLL1_OUT_MAIN, 6, 0, 0), + F(150000000, P_DISP_CC_PLL1_OUT_MAIN, 4, 0, 0), + F(171428571, P_DISP_CC_PLL1_OUT_MAIN, 3.5, 0, 0), + F(200000000, P_DISP_CC_PLL1_OUT_MAIN, 3, 0, 0), + F(300000000, P_DISP_CC_PLL1_OUT_MAIN, 2, 0, 0), + F(345000000, P_DISP_CC_PLL0_OUT_MAIN, 4, 0, 0), + F(460000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_mdp_clk_src = { + .cmd_rcgr = 0x20c8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_5, + .freq_tbl = ftbl_disp_cc_mdss_mdp_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_mdp_clk_src", + .parent_names = disp_cc_parent_names_5, + .num_parents = 5, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_pclk0_clk_src = { + .cmd_rcgr = 0x2098, + .mnd_width = 8, + .hid_width = 5, + .parent_map = disp_cc_parent_map_6, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_pclk0_clk_src", + .parent_names = disp_cc_parent_names_6, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_pixel_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_pclk1_clk_src = { + .cmd_rcgr = 0x20b0, + .mnd_width = 8, + .hid_width = 5, + .parent_map = disp_cc_parent_map_6, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_pclk1_clk_src", + .parent_names = disp_cc_parent_names_6, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_pixel_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_rot_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(171428571, P_DISP_CC_PLL1_OUT_MAIN, 3.5, 0, 0), + F(300000000, P_DISP_CC_PLL1_OUT_MAIN, 2, 0, 0), + F(345000000, P_DISP_CC_PLL0_OUT_MAIN, 4, 0, 0), + F(460000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_rot_clk_src = { + .cmd_rcgr = 0x20e0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_5, + .freq_tbl = ftbl_disp_cc_mdss_rot_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_rot_clk_src", + .parent_names = disp_cc_parent_names_5, + .num_parents = 5, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_vsync_clk_src = { + .cmd_rcgr = 0x20f8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_mdss_dp_aux1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_vsync_clk_src", + .parent_names = disp_cc_parent_names_1, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch disp_cc_mdss_ahb_clk = { + .halt_reg = 0x2080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2080, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_ahb_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_byte0_clk = { + .halt_reg = 0x2028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2028, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte0_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_byte0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = { + .reg = 0x2128, + .shift = 0, + .width = 2, + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte0_div_clk_src", + .parent_names = (const char *[]){ + "disp_cc_mdss_byte0_clk_src", + }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + .ops = &clk_regmap_div_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_byte0_intf_clk = { + .halt_reg = 0x202c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x202c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte0_intf_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_byte0_div_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_byte1_clk = { + .halt_reg = 0x2030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2030, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte1_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_byte1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_div disp_cc_mdss_byte1_div_clk_src = { + .reg = 0x2144, + .shift = 0, + .width = 2, + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte1_div_clk_src", + .parent_names = (const char *[]){ + "disp_cc_mdss_byte1_clk_src", + }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + .ops = &clk_regmap_div_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_byte1_intf_clk = { + .halt_reg = 0x2034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2034, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte1_intf_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_byte1_div_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_aux1_clk = { + .halt_reg = 0x2068, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2068, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_aux1_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_aux1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_aux_clk = { + .halt_reg = 0x2054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2054, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_aux_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_aux_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_crypto1_clk = { + .halt_reg = 0x2064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2064, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_crypto1_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_crypto1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_crypto_clk = { + .halt_reg = 0x2048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2048, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_crypto_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_crypto_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_link1_clk = { + .halt_reg = 0x205c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x205c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_link1_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_link1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_link1_intf_clk = { + .halt_reg = 0x2060, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2060, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_link1_intf_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_link1_clk_src", + }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_link_clk = { + .halt_reg = 0x2040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2040, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_link_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_link_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_link_intf_clk = { + .halt_reg = 0x2044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2044, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_link_intf_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_link_clk_src", + }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_pixel1_clk = { + .halt_reg = 0x2050, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2050, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel1_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_pixel1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_pixel2_clk = { + .halt_reg = 0x2058, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2058, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel2_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_pixel2_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_pixel_clk = { + .halt_reg = 0x204c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x204c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_dp_pixel_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_edp_aux_clk = { + .halt_reg = 0x2078, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2078, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_aux_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_edp_aux_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_edp_gtc_clk = { + .halt_reg = 0x207c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x207c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_gtc_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_edp_gtc_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_edp_link_clk = { + .halt_reg = 0x2070, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2070, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_link_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_edp_link_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_edp_link_intf_clk = { + .halt_reg = 0x2074, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2074, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_link_intf_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_edp_link_clk_src", + }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_edp_pixel_clk = { + .halt_reg = 0x206c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x206c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_pixel_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_edp_pixel_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_esc0_clk = { + .halt_reg = 0x2038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2038, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_esc0_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_esc0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_esc1_clk = { + .halt_reg = 0x203c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x203c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_esc1_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_esc1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_mdp_clk = { + .halt_reg = 0x200c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x200c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_mdp_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_mdp_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_mdp_lut_clk = { + .halt_reg = 0x201c, + .halt_check = BRANCH_VOTED, + .clkr = { + .enable_reg = 0x201c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_mdp_lut_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_mdp_clk_src", + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_non_gdsc_ahb_clk = { + .halt_reg = 0x4004, + .halt_check = BRANCH_VOTED, + .clkr = { + .enable_reg = 0x4004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_non_gdsc_ahb_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_pclk0_clk = { + .halt_reg = 0x2004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_pclk0_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_pclk0_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_pclk1_clk = { + .halt_reg = 0x2008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_pclk1_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_pclk1_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_rot_clk = { + .halt_reg = 0x2014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_rot_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_rot_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_rscc_ahb_clk = { + .halt_reg = 0x400c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x400c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_rscc_ahb_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_ahb_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_rscc_vsync_clk = { + .halt_reg = 0x4008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_rscc_vsync_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_vsync_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_vsync_clk = { + .halt_reg = 0x2024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_vsync_clk", + .parent_names = (const char *[]){ + "disp_cc_mdss_vsync_clk_src", + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_xo_clk = { + .halt_reg = 0x605c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x605c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_xo_clk", + .flags = CLK_IS_CRITICAL, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc mdss_gdsc = { + .gdscr = 0x3000, + .pd = { + .name = "mdss_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = HW_CTRL | POLL_CFG_GDSCR, +}; + +static struct clk_regmap *disp_cc_sm8150_clocks[] = { + [DISP_CC_MDSS_AHB_CLK] = &disp_cc_mdss_ahb_clk.clkr, + [DISP_CC_MDSS_AHB_CLK_SRC] = &disp_cc_mdss_ahb_clk_src.clkr, + [DISP_CC_MDSS_BYTE0_CLK] = &disp_cc_mdss_byte0_clk.clkr, + [DISP_CC_MDSS_BYTE0_CLK_SRC] = &disp_cc_mdss_byte0_clk_src.clkr, + [DISP_CC_MDSS_BYTE0_DIV_CLK_SRC] = &disp_cc_mdss_byte0_div_clk_src.clkr, + [DISP_CC_MDSS_BYTE0_INTF_CLK] = &disp_cc_mdss_byte0_intf_clk.clkr, + [DISP_CC_MDSS_BYTE1_CLK] = &disp_cc_mdss_byte1_clk.clkr, + [DISP_CC_MDSS_BYTE1_CLK_SRC] = &disp_cc_mdss_byte1_clk_src.clkr, + [DISP_CC_MDSS_BYTE1_DIV_CLK_SRC] = &disp_cc_mdss_byte1_div_clk_src.clkr, + [DISP_CC_MDSS_BYTE1_INTF_CLK] = &disp_cc_mdss_byte1_intf_clk.clkr, + [DISP_CC_MDSS_DP_AUX1_CLK] = &disp_cc_mdss_dp_aux1_clk.clkr, + [DISP_CC_MDSS_DP_AUX1_CLK_SRC] = &disp_cc_mdss_dp_aux1_clk_src.clkr, + [DISP_CC_MDSS_DP_AUX_CLK] = &disp_cc_mdss_dp_aux_clk.clkr, + [DISP_CC_MDSS_DP_AUX_CLK_SRC] = &disp_cc_mdss_dp_aux_clk_src.clkr, + [DISP_CC_MDSS_DP_CRYPTO1_CLK] = &disp_cc_mdss_dp_crypto1_clk.clkr, + [DISP_CC_MDSS_DP_CRYPTO1_CLK_SRC] = + &disp_cc_mdss_dp_crypto1_clk_src.clkr, + [DISP_CC_MDSS_DP_CRYPTO_CLK] = &disp_cc_mdss_dp_crypto_clk.clkr, + [DISP_CC_MDSS_DP_CRYPTO_CLK_SRC] = &disp_cc_mdss_dp_crypto_clk_src.clkr, + [DISP_CC_MDSS_DP_LINK1_CLK] = &disp_cc_mdss_dp_link1_clk.clkr, + [DISP_CC_MDSS_DP_LINK1_CLK_SRC] = &disp_cc_mdss_dp_link1_clk_src.clkr, + [DISP_CC_MDSS_DP_LINK1_INTF_CLK] = &disp_cc_mdss_dp_link1_intf_clk.clkr, + [DISP_CC_MDSS_DP_LINK_CLK] = &disp_cc_mdss_dp_link_clk.clkr, + [DISP_CC_MDSS_DP_LINK_CLK_SRC] = &disp_cc_mdss_dp_link_clk_src.clkr, + [DISP_CC_MDSS_DP_LINK_INTF_CLK] = &disp_cc_mdss_dp_link_intf_clk.clkr, + [DISP_CC_MDSS_DP_PIXEL1_CLK] = &disp_cc_mdss_dp_pixel1_clk.clkr, + [DISP_CC_MDSS_DP_PIXEL1_CLK_SRC] = &disp_cc_mdss_dp_pixel1_clk_src.clkr, + [DISP_CC_MDSS_DP_PIXEL2_CLK] = &disp_cc_mdss_dp_pixel2_clk.clkr, + [DISP_CC_MDSS_DP_PIXEL2_CLK_SRC] = &disp_cc_mdss_dp_pixel2_clk_src.clkr, + [DISP_CC_MDSS_DP_PIXEL_CLK] = &disp_cc_mdss_dp_pixel_clk.clkr, + [DISP_CC_MDSS_DP_PIXEL_CLK_SRC] = &disp_cc_mdss_dp_pixel_clk_src.clkr, + [DISP_CC_MDSS_EDP_AUX_CLK] = &disp_cc_mdss_edp_aux_clk.clkr, + [DISP_CC_MDSS_EDP_AUX_CLK_SRC] = &disp_cc_mdss_edp_aux_clk_src.clkr, + [DISP_CC_MDSS_EDP_GTC_CLK] = &disp_cc_mdss_edp_gtc_clk.clkr, + [DISP_CC_MDSS_EDP_GTC_CLK_SRC] = &disp_cc_mdss_edp_gtc_clk_src.clkr, + [DISP_CC_MDSS_EDP_LINK_CLK] = &disp_cc_mdss_edp_link_clk.clkr, + [DISP_CC_MDSS_EDP_LINK_CLK_SRC] = &disp_cc_mdss_edp_link_clk_src.clkr, + [DISP_CC_MDSS_EDP_LINK_INTF_CLK] = &disp_cc_mdss_edp_link_intf_clk.clkr, + [DISP_CC_MDSS_EDP_PIXEL_CLK] = &disp_cc_mdss_edp_pixel_clk.clkr, + [DISP_CC_MDSS_EDP_PIXEL_CLK_SRC] = &disp_cc_mdss_edp_pixel_clk_src.clkr, + [DISP_CC_MDSS_ESC0_CLK] = &disp_cc_mdss_esc0_clk.clkr, + [DISP_CC_MDSS_ESC0_CLK_SRC] = &disp_cc_mdss_esc0_clk_src.clkr, + [DISP_CC_MDSS_ESC1_CLK] = &disp_cc_mdss_esc1_clk.clkr, + [DISP_CC_MDSS_ESC1_CLK_SRC] = &disp_cc_mdss_esc1_clk_src.clkr, + [DISP_CC_MDSS_MDP_CLK] = &disp_cc_mdss_mdp_clk.clkr, + [DISP_CC_MDSS_MDP_CLK_SRC] = &disp_cc_mdss_mdp_clk_src.clkr, + [DISP_CC_MDSS_MDP_LUT_CLK] = &disp_cc_mdss_mdp_lut_clk.clkr, + [DISP_CC_MDSS_NON_GDSC_AHB_CLK] = &disp_cc_mdss_non_gdsc_ahb_clk.clkr, + [DISP_CC_MDSS_PCLK0_CLK] = &disp_cc_mdss_pclk0_clk.clkr, + [DISP_CC_MDSS_PCLK0_CLK_SRC] = &disp_cc_mdss_pclk0_clk_src.clkr, + [DISP_CC_MDSS_PCLK1_CLK] = &disp_cc_mdss_pclk1_clk.clkr, + [DISP_CC_MDSS_PCLK1_CLK_SRC] = &disp_cc_mdss_pclk1_clk_src.clkr, + [DISP_CC_MDSS_ROT_CLK] = &disp_cc_mdss_rot_clk.clkr, + [DISP_CC_MDSS_ROT_CLK_SRC] = &disp_cc_mdss_rot_clk_src.clkr, + [DISP_CC_MDSS_RSCC_AHB_CLK] = &disp_cc_mdss_rscc_ahb_clk.clkr, + [DISP_CC_MDSS_RSCC_VSYNC_CLK] = &disp_cc_mdss_rscc_vsync_clk.clkr, + [DISP_CC_MDSS_VSYNC_CLK] = &disp_cc_mdss_vsync_clk.clkr, + [DISP_CC_MDSS_VSYNC_CLK_SRC] = &disp_cc_mdss_vsync_clk_src.clkr, + [DISP_CC_PLL0] = &disp_cc_pll0.clkr, + [DISP_CC_PLL1] = &disp_cc_pll1.clkr, + [DISP_CC_XO_CLK] = &disp_cc_xo_clk.clkr, +}; + +static const struct qcom_reset_map disp_cc_sm8150_resets[] = { + [DISP_CC_MDSS_CORE_BCR] = { 0x2000 }, + [DISP_CC_MDSS_RSCC_BCR] = { 0x4000 }, + [DISP_CC_MDSS_SPDM_BCR] = { 0x6000 }, +}; + +static struct gdsc *disp_cc_sm8150_gdscs[] = { + [MDSS_GDSC] = &mdss_gdsc, +}; + +static const struct regmap_config disp_cc_sm8150_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + +static const struct qcom_cc_desc disp_cc_sm8150_desc = { + .config = &disp_cc_sm8150_regmap_config, + .clks = disp_cc_sm8150_clocks, + .num_clks = ARRAY_SIZE(disp_cc_sm8150_clocks), + .resets = disp_cc_sm8150_resets, + .num_resets = ARRAY_SIZE(disp_cc_sm8150_resets), + .gdscs = disp_cc_sm8150_gdscs, + .num_gdscs = ARRAY_SIZE(disp_cc_sm8150_gdscs), +}; + +static const struct of_device_id disp_cc_sm8150_match_table[] = { + { .compatible = "qcom,sm8150-dispcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, disp_cc_sm8150_match_table); + +static int disp_cc_sm8150_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + + regmap = qcom_cc_map(pdev, &disp_cc_sm8150_desc); + if (IS_ERR(regmap)) { + pr_err("Failed to map the display CC registers\n"); + return PTR_ERR(regmap); + } + + clk_trion_pll_configure(&disp_cc_pll0, regmap, &disp_cc_pll0_config); + clk_trion_pll_configure(&disp_cc_pll1, regmap, &disp_cc_pll1_config); + + /* Enable clock gating for DSI and MDP clocks */ + regmap_update_bits(regmap, DISP_CC_MISC_CMD, 0x10, 0x10); + + return qcom_cc_really_probe(pdev, &disp_cc_sm8150_desc, regmap); +} + +static struct platform_driver disp_cc_sm8150_driver = { + .probe = disp_cc_sm8150_probe, + .driver = { + .name = "disp_cc-sm8150", + .of_match_table = disp_cc_sm8150_match_table, + }, +}; + +static int __init disp_cc_sm8150_init(void) +{ + return platform_driver_register(&disp_cc_sm8150_driver); +} +subsys_initcall(disp_cc_sm8150_init); + +static void __exit disp_cc_sm8150_exit(void) +{ + platform_driver_unregister(&disp_cc_sm8150_driver); +} +module_exit(disp_cc_sm8150_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("QTI DISPCC SM8150 Driver"); diff --git a/drivers/clk/qcom/dispcc-sm8250.c b/drivers/clk/qcom/dispcc-sm8250.c new file mode 100644 index 000000000000..96b9ccf3cd0f --- /dev/null +++ b/drivers/clk/qcom/dispcc-sm8250.c @@ -0,0 +1,1414 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reset-controller.h> + +#include <dt-bindings/clock/qcom,dispcc-sm8250.h> + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "clk-regmap-divider.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +#define DISP_CC_MISC_CMD 0x8000 + +enum { + P_BI_TCXO, + P_CHIP_SLEEP_CLK, + P_CORE_BI_PLL_TEST_SE, + P_DISP_CC_PLL0_OUT_MAIN, + P_DISP_CC_PLL1_OUT_EVEN, + P_DISP_CC_PLL1_OUT_MAIN, + P_DP_PHY_PLL_LINK_CLK, + P_DP_PHY_PLL_VCO_DIV_CLK, + P_DPTX1_PHY_PLL_LINK_CLK, + P_DPTX1_PHY_PLL_VCO_DIV_CLK, + P_DPTX2_PHY_PLL_LINK_CLK, + P_DPTX2_PHY_PLL_VCO_DIV_CLK, + P_DSI0_PHY_PLL_OUT_BYTECLK, + P_DSI0_PHY_PLL_OUT_DSICLK, + P_DSI1_PHY_PLL_OUT_BYTECLK, + P_DSI1_PHY_PLL_OUT_DSICLK, + P_EDP_PHY_PLL_LINK_CLK, + P_EDP_PHY_PLL_VCO_DIV_CLK, +}; + +static struct pll_vco lucid_vco[] = { + { 249600000, 2000000000, 0 }, +}; + +static const struct alpha_pll_config disp_cc_pll0_config = { + .l = 0x47, + .alpha = 0xE000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002261, + .config_ctl_hi1_val = 0x329A699C, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000805, + .user_ctl_hi1_val = 0x00000000, +}; + +static struct clk_alpha_pll disp_cc_pll0 = { + .offset = 0x0, + .vco_table = lucid_vco, + .num_vco = ARRAY_SIZE(lucid_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_pll0", + .parent_data = &(const struct clk_parent_data){ + .fw_name = "bi_tcxo" + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_ops, + }, + }, +}; + +static const struct alpha_pll_config disp_cc_pll1_config = { + .l = 0x1F, + .alpha = 0x4000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002261, + .config_ctl_hi1_val = 0x329A699C, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000805, + .user_ctl_hi1_val = 0x00000000, +}; + +static struct clk_alpha_pll disp_cc_pll1 = { + .offset = 0x1000, + .vco_table = lucid_vco, + .num_vco = ARRAY_SIZE(lucid_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_pll1", + .parent_data = &(const struct clk_parent_data){ + .fw_name = "bi_tcxo" + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_ops, + }, + }, +}; + +static const struct parent_map disp_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_DP_PHY_PLL_LINK_CLK, 1 }, + { P_DP_PHY_PLL_VCO_DIV_CLK, 2 }, + { P_DPTX1_PHY_PLL_LINK_CLK, 3 }, + { P_DPTX1_PHY_PLL_VCO_DIV_CLK, 4 }, + { P_DPTX2_PHY_PLL_LINK_CLK, 5 }, + { P_DPTX2_PHY_PLL_VCO_DIV_CLK, 6 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_0[] = { + { .fw_name = "bi_tcxo" }, + { .fw_name = "dp_link_clk_divsel_ten" }, + { .fw_name = "dp_vco_divided_clk_src_mux" }, + { .fw_name = "dptx1_phy_pll_link_clk" }, + { .fw_name = "dptx1_phy_pll_vco_div_clk" }, + { .fw_name = "dptx2_phy_pll_link_clk" }, + { .fw_name = "dptx2_phy_pll_vco_div_clk" }, + { .fw_name = "core_bi_pll_test_se" }, +}; + +static const struct parent_map disp_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_1[] = { + { .fw_name = "bi_tcxo" }, + { .fw_name = "core_bi_pll_test_se" }, +}; + +static const struct parent_map disp_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_DSI0_PHY_PLL_OUT_BYTECLK, 1 }, + { P_DSI1_PHY_PLL_OUT_BYTECLK, 2 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_2[] = { + { .fw_name = "bi_tcxo" }, + { .fw_name = "dsi0_phy_pll_out_byteclk" }, + { .fw_name = "dsi1_phy_pll_out_byteclk" }, + { .fw_name = "core_bi_pll_test_se" }, +}; + +static const struct parent_map disp_cc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_DISP_CC_PLL1_OUT_MAIN, 4 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_3[] = { + { .fw_name = "bi_tcxo" }, + { .hw = &disp_cc_pll1.clkr.hw }, + { .fw_name = "core_bi_pll_test_se" }, +}; + +static const struct parent_map disp_cc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_EDP_PHY_PLL_LINK_CLK, 1 }, + { P_EDP_PHY_PLL_VCO_DIV_CLK, 2 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_4[] = { + { .fw_name = "bi_tcxo" }, + { .fw_name = "edp_phy_pll_link_clk" }, + { .fw_name = "edp_phy_pll_vco_div_clk" }, + { .fw_name = "core_bi_pll_test_se" }, +}; + +static const struct parent_map disp_cc_parent_map_5[] = { + { P_BI_TCXO, 0 }, + { P_DISP_CC_PLL0_OUT_MAIN, 1 }, + { P_DISP_CC_PLL1_OUT_MAIN, 4 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_5[] = { + { .fw_name = "bi_tcxo" }, + { .hw = &disp_cc_pll0.clkr.hw }, + { .hw = &disp_cc_pll1.clkr.hw }, + { .fw_name = "core_bi_pll_test_se" }, +}; + +static const struct parent_map disp_cc_parent_map_6[] = { + { P_BI_TCXO, 0 }, + { P_DSI0_PHY_PLL_OUT_DSICLK, 1 }, + { P_DSI1_PHY_PLL_OUT_DSICLK, 2 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_6[] = { + { .fw_name = "bi_tcxo" }, + { .fw_name = "dsi0_phy_pll_out_dsiclk" }, + { .fw_name = "dsi1_phy_pll_out_dsiclk" }, + { .fw_name = "core_bi_pll_test_se" }, +}; + +static const struct parent_map disp_cc_parent_map_7[] = { + { P_CHIP_SLEEP_CLK, 0 }, + { P_CORE_BI_PLL_TEST_SE, 7 }, +}; + +static const struct clk_parent_data disp_cc_parent_data_7[] = { + { .fw_name = "sleep_clk" }, + { .fw_name = "core_bi_pll_test_se" }, +}; + + +static const struct freq_tbl ftbl_disp_cc_mdss_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(37500000, P_DISP_CC_PLL1_OUT_MAIN, 16, 0, 0), + F(75000000, P_DISP_CC_PLL1_OUT_MAIN, 8, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_ahb_clk_src = { + .cmd_rcgr = 0x22bc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_3, + .freq_tbl = ftbl_disp_cc_mdss_ahb_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_ahb_clk_src", + .parent_data = disp_cc_parent_data_3, + .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_byte0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_byte0_clk_src = { + .cmd_rcgr = 0x2110, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte0_clk_src", + .parent_data = disp_cc_parent_data_2, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_byte2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_byte1_clk_src = { + .cmd_rcgr = 0x212c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte1_clk_src", + .parent_data = disp_cc_parent_data_2, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_byte2_ops, + }, +}; + +static struct clk_regmap_div disp_cc_mdss_byte0_div_clk_src = { + .reg = 0x2128, + .shift = 0, + .width = 2, + .clkr.hw.init = &(struct clk_init_data) { + .name = "disp_cc_mdss_byte0_div_clk_src", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_byte0_clk_src.clkr.hw + }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + }, +}; + + +static struct clk_regmap_div disp_cc_mdss_byte1_div_clk_src = { + .reg = 0x2144, + .shift = 0, + .width = 2, + .clkr.hw.init = &(struct clk_init_data) { + .name = "disp_cc_mdss_byte1_div_clk_src", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_byte1_clk_src.clkr.hw + }, + .num_parents = 1, + .ops = &clk_regmap_div_ops, + }, +}; + + +static struct clk_rcg2 disp_cc_mdss_dp_aux1_clk_src = { + .cmd_rcgr = 0x2240, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_mdss_byte0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_aux1_clk_src", + .parent_data = disp_cc_parent_data_1, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_aux_clk_src = { + .cmd_rcgr = 0x21dc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_mdss_byte0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_aux_clk_src", + .parent_data = disp_cc_parent_data_1, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_dp_link1_clk_src[] = { + F( 162000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0), + F( 270000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0), + F( 540000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0), + F( 810000, P_DP_PHY_PLL_LINK_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_dp_link1_clk_src = { + .cmd_rcgr = 0x220c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_mdss_dp_link1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_link1_clk_src", + .parent_data = disp_cc_parent_data_0, + .num_parents = 8, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_link_clk_src = { + .cmd_rcgr = 0x2178, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .freq_tbl = ftbl_disp_cc_mdss_dp_link1_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_link_clk_src", + .parent_data = disp_cc_parent_data_0, + .num_parents = 8, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_regmap_div disp_cc_mdss_dp_link1_div_clk_src = { + .reg = 0x2224, + .shift = 0, + .width = 2, + .clkr.hw.init = &(struct clk_init_data) { + .name = "disp_cc_mdss_dp_link1_div_clk_src", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_dp_link1_clk_src.clkr.hw + }, + .num_parents = 1, + .ops = &clk_regmap_div_ro_ops, + }, +}; + + +static struct clk_regmap_div disp_cc_mdss_dp_link_div_clk_src = { + .reg = 0x2190, + .shift = 0, + .width = 2, + .clkr.hw.init = &(struct clk_init_data) { + .name = "disp_cc_mdss_dp_link_div_clk_src", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_dp_link_clk_src.clkr.hw + }, + .num_parents = 1, + .ops = &clk_regmap_div_ro_ops, + }, +}; + + +static struct clk_rcg2 disp_cc_mdss_dp_pixel1_clk_src = { + .cmd_rcgr = 0x21c4, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel1_clk_src", + .parent_data = disp_cc_parent_data_0, + .num_parents = 8, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_pixel2_clk_src = { + .cmd_rcgr = 0x21f4, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel2_clk_src", + .parent_data = disp_cc_parent_data_0, + .num_parents = 8, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_dp_pixel_clk_src = { + .cmd_rcgr = 0x21ac, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_0, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel_clk_src", + .parent_data = disp_cc_parent_data_0, + .num_parents = 8, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_edp_aux_clk_src = { + .cmd_rcgr = 0x228c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_mdss_byte0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_aux_clk_src", + .parent_data = disp_cc_parent_data_1, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_edp_gtc_clk_src = { + .cmd_rcgr = 0x22a4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_3, + .freq_tbl = ftbl_disp_cc_mdss_byte0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_gtc_clk_src", + .parent_data = disp_cc_parent_data_3, + .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_edp_link_clk_src[] = { + F( 19200000, P_BI_TCXO, 1, 0, 0), + F( 270000000, P_EDP_PHY_PLL_LINK_CLK, 1, 0, 0), + F( 594000000, P_EDP_PHY_PLL_LINK_CLK, 1, 0, 0), + F( 810000000, P_EDP_PHY_PLL_LINK_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_edp_link_clk_src = { + .cmd_rcgr = 0x2270, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_4, + .freq_tbl = ftbl_disp_cc_mdss_edp_link_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_link_clk_src", + .parent_data = disp_cc_parent_data_4, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_regmap_div disp_cc_mdss_edp_link_div_clk_src = { + .reg = 0x2288, + .shift = 0, + .width = 2, + .clkr.hw.init = &(struct clk_init_data) { + .name = "disp_cc_mdss_edp_link_div_clk_src", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_edp_link_clk_src.clkr.hw + }, + .num_parents = 1, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_edp_pixel_clk_src = { + .cmd_rcgr = 0x2258, + .mnd_width = 16, + .hid_width = 5, + .parent_map = disp_cc_parent_map_4, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_pixel_clk_src", + .parent_data = disp_cc_parent_data_4, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_dp_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_esc0_clk_src = { + .cmd_rcgr = 0x2148, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .freq_tbl = ftbl_disp_cc_mdss_byte0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_esc0_clk_src", + .parent_data = disp_cc_parent_data_2, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_esc1_clk_src = { + .cmd_rcgr = 0x2160, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_2, + .freq_tbl = ftbl_disp_cc_mdss_byte0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_esc1_clk_src", + .parent_data = disp_cc_parent_data_2, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(85714286, P_DISP_CC_PLL1_OUT_MAIN, 7, 0, 0), + F(100000000, P_DISP_CC_PLL1_OUT_MAIN, 6, 0, 0), + F(150000000, P_DISP_CC_PLL1_OUT_MAIN, 4, 0, 0), + F(200000000, P_DISP_CC_PLL1_OUT_MAIN, 3, 0, 0), + F(300000000, P_DISP_CC_PLL1_OUT_MAIN, 2, 0, 0), + F(345000000, P_DISP_CC_PLL0_OUT_MAIN, 4, 0, 0), + F(460000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_mdp_clk_src = { + .cmd_rcgr = 0x20c8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_5, + .freq_tbl = ftbl_disp_cc_mdss_mdp_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_mdp_clk_src", + .parent_data = disp_cc_parent_data_5, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_pclk0_clk_src = { + .cmd_rcgr = 0x2098, + .mnd_width = 8, + .hid_width = 5, + .parent_map = disp_cc_parent_map_6, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_pclk0_clk_src", + .parent_data = disp_cc_parent_data_6, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_pixel_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_pclk1_clk_src = { + .cmd_rcgr = 0x20b0, + .mnd_width = 8, + .hid_width = 5, + .parent_map = disp_cc_parent_map_6, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_pclk1_clk_src", + .parent_data = disp_cc_parent_data_6, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_pixel_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_mdss_rot_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(200000000, P_DISP_CC_PLL1_OUT_MAIN, 3, 0, 0), + F(300000000, P_DISP_CC_PLL1_OUT_MAIN, 2, 0, 0), + F(345000000, P_DISP_CC_PLL0_OUT_MAIN, 4, 0, 0), + F(460000000, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_mdss_rot_clk_src = { + .cmd_rcgr = 0x20e0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_5, + .freq_tbl = ftbl_disp_cc_mdss_rot_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_rot_clk_src", + .parent_data = disp_cc_parent_data_5, + .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 disp_cc_mdss_vsync_clk_src = { + .cmd_rcgr = 0x20f8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_1, + .freq_tbl = ftbl_disp_cc_mdss_byte0_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_vsync_clk_src", + .parent_data = disp_cc_parent_data_1, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_disp_cc_sleep_clk_src[] = { + F(32000, P_CHIP_SLEEP_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 disp_cc_sleep_clk_src = { + .cmd_rcgr = 0x6060, + .mnd_width = 0, + .hid_width = 5, + .parent_map = disp_cc_parent_map_7, + .freq_tbl = ftbl_disp_cc_sleep_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "disp_cc_sleep_clk_src", + .parent_data = disp_cc_parent_data_7, + .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch disp_cc_mdss_ahb_clk = { + .halt_reg = 0x2080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2080, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_ahb_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_byte0_clk = { + .halt_reg = 0x2028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2028, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte0_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_byte0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_byte0_intf_clk = { + .halt_reg = 0x202c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x202c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte0_intf_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_byte0_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_byte1_clk = { + .halt_reg = 0x2030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2030, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte1_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_byte1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_byte1_intf_clk = { + .halt_reg = 0x2034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2034, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_byte1_intf_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_byte1_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_aux1_clk = { + .halt_reg = 0x2068, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2068, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_aux1_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_dp_aux1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_aux_clk = { + .halt_reg = 0x2054, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2054, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_aux_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_dp_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_link1_clk = { + .halt_reg = 0x205c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x205c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_link1_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_dp_link1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_link1_intf_clk = { + .halt_reg = 0x2060, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2060, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_link1_intf_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_dp_link1_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_link_clk = { + .halt_reg = 0x2040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2040, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_link_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_dp_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_link_intf_clk = { + .halt_reg = 0x2044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2044, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_link_intf_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_dp_link_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_pixel1_clk = { + .halt_reg = 0x2050, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2050, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel1_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_dp_pixel1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_pixel2_clk = { + .halt_reg = 0x2058, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2058, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel2_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_dp_pixel2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_dp_pixel_clk = { + .halt_reg = 0x204c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x204c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_dp_pixel_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_dp_pixel_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_edp_aux_clk = { + .halt_reg = 0x2078, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2078, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_aux_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_edp_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_edp_gtc_clk = { + .halt_reg = 0x207c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x207c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_gtc_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_edp_gtc_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_edp_link_clk = { + .halt_reg = 0x2070, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2070, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_link_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_edp_link_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_edp_link_intf_clk = { + .halt_reg = 0x2074, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2074, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_link_intf_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_edp_link_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_edp_pixel_clk = { + .halt_reg = 0x206c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x206c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_edp_pixel_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_edp_pixel_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_esc0_clk = { + .halt_reg = 0x2038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2038, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_esc0_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_esc0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_esc1_clk = { + .halt_reg = 0x203c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x203c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_esc1_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_esc1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_mdp_clk = { + .halt_reg = 0x200c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x200c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_mdp_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_mdp_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_mdp_lut_clk = { + .halt_reg = 0x201c, + .halt_check = BRANCH_VOTED, + .clkr = { + .enable_reg = 0x201c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_mdp_lut_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_mdp_clk_src.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_non_gdsc_ahb_clk = { + .halt_reg = 0x4004, + .halt_check = BRANCH_VOTED, + .clkr = { + .enable_reg = 0x4004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_non_gdsc_ahb_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_pclk0_clk = { + .halt_reg = 0x2004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_pclk0_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_pclk0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_pclk1_clk = { + .halt_reg = 0x2008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_pclk1_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_pclk1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT | CLK_GET_RATE_NOCACHE, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_rot_clk = { + .halt_reg = 0x2014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2014, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_rot_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_rot_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_rscc_ahb_clk = { + .halt_reg = 0x400c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x400c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_rscc_ahb_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_rscc_vsync_clk = { + .halt_reg = 0x4008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4008, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_rscc_vsync_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_vsync_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_mdss_vsync_clk = { + .halt_reg = 0x2024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2024, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_mdss_vsync_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_mdss_vsync_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_sleep_clk = { + .halt_reg = 0x6078, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x6078, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_sleep_clk", + .parent_data = &(const struct clk_parent_data){ + .hw = &disp_cc_sleep_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch disp_cc_xo_clk = { + .halt_reg = 0x605c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x605c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "disp_cc_xo_clk", + .flags = CLK_IS_CRITICAL, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc mdss_gdsc = { + .gdscr = 0x3000, + .pd = { + .name = "mdss_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = HW_CTRL | POLL_CFG_GDSCR, +}; + +static struct clk_regmap *disp_cc_sm8250_clocks[] = { + [DISP_CC_MDSS_AHB_CLK] = &disp_cc_mdss_ahb_clk.clkr, + [DISP_CC_MDSS_AHB_CLK_SRC] = &disp_cc_mdss_ahb_clk_src.clkr, + [DISP_CC_MDSS_BYTE0_CLK] = &disp_cc_mdss_byte0_clk.clkr, + [DISP_CC_MDSS_BYTE0_CLK_SRC] = &disp_cc_mdss_byte0_clk_src.clkr, + [DISP_CC_MDSS_BYTE0_DIV_CLK_SRC] = &disp_cc_mdss_byte0_div_clk_src.clkr, + [DISP_CC_MDSS_BYTE0_INTF_CLK] = &disp_cc_mdss_byte0_intf_clk.clkr, + [DISP_CC_MDSS_BYTE1_CLK] = &disp_cc_mdss_byte1_clk.clkr, + [DISP_CC_MDSS_BYTE1_CLK_SRC] = &disp_cc_mdss_byte1_clk_src.clkr, + [DISP_CC_MDSS_BYTE1_DIV_CLK_SRC] = &disp_cc_mdss_byte1_div_clk_src.clkr, + [DISP_CC_MDSS_BYTE1_INTF_CLK] = &disp_cc_mdss_byte1_intf_clk.clkr, + [DISP_CC_MDSS_DP_AUX1_CLK] = &disp_cc_mdss_dp_aux1_clk.clkr, + [DISP_CC_MDSS_DP_AUX1_CLK_SRC] = &disp_cc_mdss_dp_aux1_clk_src.clkr, + [DISP_CC_MDSS_DP_AUX_CLK] = &disp_cc_mdss_dp_aux_clk.clkr, + [DISP_CC_MDSS_DP_AUX_CLK_SRC] = &disp_cc_mdss_dp_aux_clk_src.clkr, + [DISP_CC_MDSS_DP_LINK1_CLK] = &disp_cc_mdss_dp_link1_clk.clkr, + [DISP_CC_MDSS_DP_LINK1_CLK_SRC] = &disp_cc_mdss_dp_link1_clk_src.clkr, + [DISP_CC_MDSS_DP_LINK1_DIV_CLK_SRC] = + &disp_cc_mdss_dp_link1_div_clk_src.clkr, + [DISP_CC_MDSS_DP_LINK1_INTF_CLK] = &disp_cc_mdss_dp_link1_intf_clk.clkr, + [DISP_CC_MDSS_DP_LINK_CLK] = &disp_cc_mdss_dp_link_clk.clkr, + [DISP_CC_MDSS_DP_LINK_CLK_SRC] = &disp_cc_mdss_dp_link_clk_src.clkr, + [DISP_CC_MDSS_DP_LINK_DIV_CLK_SRC] = + &disp_cc_mdss_dp_link_div_clk_src.clkr, + [DISP_CC_MDSS_DP_LINK_INTF_CLK] = &disp_cc_mdss_dp_link_intf_clk.clkr, + [DISP_CC_MDSS_DP_PIXEL1_CLK] = &disp_cc_mdss_dp_pixel1_clk.clkr, + [DISP_CC_MDSS_DP_PIXEL1_CLK_SRC] = &disp_cc_mdss_dp_pixel1_clk_src.clkr, + [DISP_CC_MDSS_DP_PIXEL2_CLK] = &disp_cc_mdss_dp_pixel2_clk.clkr, + [DISP_CC_MDSS_DP_PIXEL2_CLK_SRC] = &disp_cc_mdss_dp_pixel2_clk_src.clkr, + [DISP_CC_MDSS_DP_PIXEL_CLK] = &disp_cc_mdss_dp_pixel_clk.clkr, + [DISP_CC_MDSS_DP_PIXEL_CLK_SRC] = &disp_cc_mdss_dp_pixel_clk_src.clkr, + [DISP_CC_MDSS_EDP_AUX_CLK] = &disp_cc_mdss_edp_aux_clk.clkr, + [DISP_CC_MDSS_EDP_AUX_CLK_SRC] = &disp_cc_mdss_edp_aux_clk_src.clkr, + [DISP_CC_MDSS_EDP_GTC_CLK] = &disp_cc_mdss_edp_gtc_clk.clkr, + [DISP_CC_MDSS_EDP_GTC_CLK_SRC] = &disp_cc_mdss_edp_gtc_clk_src.clkr, + [DISP_CC_MDSS_EDP_LINK_CLK] = &disp_cc_mdss_edp_link_clk.clkr, + [DISP_CC_MDSS_EDP_LINK_CLK_SRC] = &disp_cc_mdss_edp_link_clk_src.clkr, + [DISP_CC_MDSS_EDP_LINK_DIV_CLK_SRC] = + &disp_cc_mdss_edp_link_div_clk_src.clkr, + [DISP_CC_MDSS_EDP_LINK_INTF_CLK] = &disp_cc_mdss_edp_link_intf_clk.clkr, + [DISP_CC_MDSS_EDP_PIXEL_CLK] = &disp_cc_mdss_edp_pixel_clk.clkr, + [DISP_CC_MDSS_EDP_PIXEL_CLK_SRC] = &disp_cc_mdss_edp_pixel_clk_src.clkr, + [DISP_CC_MDSS_ESC0_CLK] = &disp_cc_mdss_esc0_clk.clkr, + [DISP_CC_MDSS_ESC0_CLK_SRC] = &disp_cc_mdss_esc0_clk_src.clkr, + [DISP_CC_MDSS_ESC1_CLK] = &disp_cc_mdss_esc1_clk.clkr, + [DISP_CC_MDSS_ESC1_CLK_SRC] = &disp_cc_mdss_esc1_clk_src.clkr, + [DISP_CC_MDSS_MDP_CLK] = &disp_cc_mdss_mdp_clk.clkr, + [DISP_CC_MDSS_MDP_CLK_SRC] = &disp_cc_mdss_mdp_clk_src.clkr, + [DISP_CC_MDSS_MDP_LUT_CLK] = &disp_cc_mdss_mdp_lut_clk.clkr, + [DISP_CC_MDSS_NON_GDSC_AHB_CLK] = &disp_cc_mdss_non_gdsc_ahb_clk.clkr, + [DISP_CC_MDSS_PCLK0_CLK] = &disp_cc_mdss_pclk0_clk.clkr, + [DISP_CC_MDSS_PCLK0_CLK_SRC] = &disp_cc_mdss_pclk0_clk_src.clkr, + [DISP_CC_MDSS_PCLK1_CLK] = &disp_cc_mdss_pclk1_clk.clkr, + [DISP_CC_MDSS_PCLK1_CLK_SRC] = &disp_cc_mdss_pclk1_clk_src.clkr, + [DISP_CC_MDSS_ROT_CLK] = &disp_cc_mdss_rot_clk.clkr, + [DISP_CC_MDSS_ROT_CLK_SRC] = &disp_cc_mdss_rot_clk_src.clkr, + [DISP_CC_MDSS_RSCC_AHB_CLK] = &disp_cc_mdss_rscc_ahb_clk.clkr, + [DISP_CC_MDSS_RSCC_VSYNC_CLK] = &disp_cc_mdss_rscc_vsync_clk.clkr, + [DISP_CC_MDSS_VSYNC_CLK] = &disp_cc_mdss_vsync_clk.clkr, + [DISP_CC_MDSS_VSYNC_CLK_SRC] = &disp_cc_mdss_vsync_clk_src.clkr, + [DISP_CC_PLL0] = &disp_cc_pll0.clkr, + [DISP_CC_PLL1] = &disp_cc_pll1.clkr, + [DISP_CC_SLEEP_CLK] = &disp_cc_sleep_clk.clkr, + [DISP_CC_SLEEP_CLK_SRC] = &disp_cc_sleep_clk_src.clkr, + [DISP_CC_XO_CLK] = &disp_cc_xo_clk.clkr, +}; + +static const struct qcom_reset_map disp_cc_sm8250_resets[] = { + [DISP_CC_MDSS_CORE_BCR] = { 0x2000 }, + [DISP_CC_MDSS_RSCC_BCR] = { 0x4000 }, +}; + +static struct gdsc *disp_cc_sm8250_gdscs[] = { + [MDSS_GDSC] = &mdss_gdsc, +}; + +static const struct regmap_config disp_cc_sm8250_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x10000, + .fast_io = true, +}; + +static const struct qcom_cc_desc disp_cc_sm8250_desc = { + .config = &disp_cc_sm8250_regmap_config, + .clks = disp_cc_sm8250_clocks, + .num_clks = ARRAY_SIZE(disp_cc_sm8250_clocks), + .resets = disp_cc_sm8250_resets, + .num_resets = ARRAY_SIZE(disp_cc_sm8250_resets), + .gdscs = disp_cc_sm8250_gdscs, + .num_gdscs = ARRAY_SIZE(disp_cc_sm8250_gdscs), +}; + +static const struct of_device_id disp_cc_sm8250_match_table[] = { + { .compatible = "qcom,sm8250-dispcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, disp_cc_sm8250_match_table); + +static int disp_cc_sm8250_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + + if (of_property_read_bool(pdev->dev.of_node, "dpu-disable-interfaces")) { +#define INTF_TIMING_ENGINE_EN 0x000 +#define INTF_MUX 0x25C + void *ptr; + u32 d; + int j, i; + phys_addr_t base[] = { + 0x6b000, + 0x6b800, + 0x6c000, + 0x6c800, + }; + for (j = 0; j < ARRAY_SIZE(base); j++) { + pr_info("base %x\n", base[j]); + ptr = ioremap(0xae00000 + base[j], 0x800); + for (i = 0; i < 4; i++) { + pr_info("%d\n", i); + writel(0, ptr + INTF_TIMING_ENGINE_EN); + d = readl(ptr + INTF_MUX); + pr_info("%d: %x\n", i, d); + //writel(d | 0xf, ptr + INTF_MUX); + writel(0xf000f, ptr + INTF_MUX); + } + iounmap(ptr); + } + pr_err("DISABLED INTF!!!\n"); + } + + regmap = qcom_cc_map(pdev, &disp_cc_sm8250_desc); + if (IS_ERR(regmap)) { + pr_err("Failed to map the disp_cc registers\n"); + return PTR_ERR(regmap); + } + + clk_lucid_pll_configure(&disp_cc_pll0, regmap, &disp_cc_pll0_config); + clk_lucid_pll_configure(&disp_cc_pll1, regmap, &disp_cc_pll1_config); + + /* Enable clock gating for MDP clocks */ + regmap_update_bits(regmap, DISP_CC_MISC_CMD, 0x10, 0x10); + + return qcom_cc_really_probe(pdev, &disp_cc_sm8250_desc, regmap); +} + +static struct platform_driver disp_cc_sm8250_driver = { + .probe = disp_cc_sm8250_probe, + .driver = { + .name = "disp_cc-sm8250", + .of_match_table = disp_cc_sm8250_match_table, + }, +}; + +static int __init disp_cc_sm8250_init(void) +{ + return platform_driver_register(&disp_cc_sm8250_driver); +} +subsys_initcall(disp_cc_sm8250_init); + +static void __exit disp_cc_sm8250_exit(void) +{ + platform_driver_unregister(&disp_cc_sm8250_driver); +} +module_exit(disp_cc_sm8250_exit); + +MODULE_DESCRIPTION("QTI DISPCC SM8150 Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/gcc-sm8250.c b/drivers/clk/qcom/gcc-sm8250.c index 6cb6617b8d88..a95e79f1bedd 100644 --- a/drivers/clk/qcom/gcc-sm8250.c +++ b/drivers/clk/qcom/gcc-sm8250.c @@ -188,6 +188,8 @@ static const struct clk_parent_data gcc_parent_data_5[] = { static const struct freq_tbl ftbl_gcc_cpuss_ahb_clk_src[] = { F(19200000, P_BI_TCXO, 1, 0, 0), + F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0), { } }; @@ -225,6 +227,7 @@ static struct clk_rcg2 gcc_gp1_clk_src = { .name = "gcc_gp1_clk_src", .parent_data = gcc_parent_data_1, .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -239,6 +242,7 @@ static struct clk_rcg2 gcc_gp2_clk_src = { .name = "gcc_gp2_clk_src", .parent_data = gcc_parent_data_1, .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -253,6 +257,7 @@ static struct clk_rcg2 gcc_gp3_clk_src = { .name = "gcc_gp3_clk_src", .parent_data = gcc_parent_data_1, .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -273,6 +278,7 @@ static struct clk_rcg2 gcc_pcie_0_aux_clk_src = { .name = "gcc_pcie_0_aux_clk_src", .parent_data = gcc_parent_data_2, .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -287,6 +293,7 @@ static struct clk_rcg2 gcc_pcie_1_aux_clk_src = { .name = "gcc_pcie_1_aux_clk_src", .parent_data = gcc_parent_data_2, .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -301,6 +308,7 @@ static struct clk_rcg2 gcc_pcie_2_aux_clk_src = { .name = "gcc_pcie_2_aux_clk_src", .parent_data = gcc_parent_data_2, .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -321,6 +329,7 @@ static struct clk_rcg2 gcc_pcie_phy_refgen_clk_src = { .name = "gcc_pcie_phy_refgen_clk_src", .parent_data = gcc_parent_data_0_ao, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -342,6 +351,7 @@ static struct clk_rcg2 gcc_pdm2_clk_src = { .name = "gcc_pdm2_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -370,6 +380,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s0_clk_src_init = { .name = "gcc_qupv3_wrap0_s0_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -386,6 +397,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s1_clk_src_init = { .name = "gcc_qupv3_wrap0_s1_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -418,6 +430,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s2_clk_src_init = { .name = "gcc_qupv3_wrap0_s2_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -434,6 +447,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s3_clk_src_init = { .name = "gcc_qupv3_wrap0_s3_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -450,6 +464,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s4_clk_src_init = { .name = "gcc_qupv3_wrap0_s4_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -466,6 +481,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s5_clk_src_init = { .name = "gcc_qupv3_wrap0_s5_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -482,6 +498,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s6_clk_src_init = { .name = "gcc_qupv3_wrap0_s6_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -498,6 +515,7 @@ static struct clk_init_data gcc_qupv3_wrap0_s7_clk_src_init = { .name = "gcc_qupv3_wrap0_s7_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -514,6 +532,7 @@ static struct clk_init_data gcc_qupv3_wrap1_s0_clk_src_init = { .name = "gcc_qupv3_wrap1_s0_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -530,6 +549,7 @@ static struct clk_init_data gcc_qupv3_wrap1_s1_clk_src_init = { .name = "gcc_qupv3_wrap1_s1_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -546,6 +566,7 @@ static struct clk_init_data gcc_qupv3_wrap1_s2_clk_src_init = { .name = "gcc_qupv3_wrap1_s2_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -562,6 +583,7 @@ static struct clk_init_data gcc_qupv3_wrap1_s3_clk_src_init = { .name = "gcc_qupv3_wrap1_s3_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -578,6 +600,7 @@ static struct clk_init_data gcc_qupv3_wrap1_s4_clk_src_init = { .name = "gcc_qupv3_wrap1_s4_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -594,6 +617,7 @@ static struct clk_init_data gcc_qupv3_wrap1_s5_clk_src_init = { .name = "gcc_qupv3_wrap1_s5_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -610,6 +634,7 @@ static struct clk_init_data gcc_qupv3_wrap2_s0_clk_src_init = { .name = "gcc_qupv3_wrap2_s0_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -626,6 +651,7 @@ static struct clk_init_data gcc_qupv3_wrap2_s1_clk_src_init = { .name = "gcc_qupv3_wrap2_s1_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -642,6 +668,7 @@ static struct clk_init_data gcc_qupv3_wrap2_s2_clk_src_init = { .name = "gcc_qupv3_wrap2_s2_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -658,6 +685,7 @@ static struct clk_init_data gcc_qupv3_wrap2_s3_clk_src_init = { .name = "gcc_qupv3_wrap2_s3_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -674,6 +702,7 @@ static struct clk_init_data gcc_qupv3_wrap2_s4_clk_src_init = { .name = "gcc_qupv3_wrap2_s4_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -690,6 +719,7 @@ static struct clk_init_data gcc_qupv3_wrap2_s5_clk_src_init = { .name = "gcc_qupv3_wrap2_s5_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }; @@ -722,6 +752,7 @@ static struct clk_rcg2 gcc_sdcc2_apps_clk_src = { .name = "gcc_sdcc2_apps_clk_src", .parent_data = gcc_parent_data_4, .num_parents = 5, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -745,6 +776,7 @@ static struct clk_rcg2 gcc_sdcc4_apps_clk_src = { .name = "gcc_sdcc4_apps_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -764,6 +796,7 @@ static struct clk_rcg2 gcc_tsif_ref_clk_src = { .name = "gcc_tsif_ref_clk_src", .parent_data = gcc_parent_data_5, .num_parents = 4, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -786,6 +819,7 @@ static struct clk_rcg2 gcc_ufs_card_axi_clk_src = { .name = "gcc_ufs_card_axi_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -808,6 +842,7 @@ static struct clk_rcg2 gcc_ufs_card_ice_core_clk_src = { .name = "gcc_ufs_card_ice_core_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -827,6 +862,7 @@ static struct clk_rcg2 gcc_ufs_card_phy_aux_clk_src = { .name = "gcc_ufs_card_phy_aux_clk_src", .parent_data = gcc_parent_data_3, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -848,6 +884,7 @@ static struct clk_rcg2 gcc_ufs_card_unipro_core_clk_src = { .name = "gcc_ufs_card_unipro_core_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -871,6 +908,7 @@ static struct clk_rcg2 gcc_ufs_phy_axi_clk_src = { .name = "gcc_ufs_phy_axi_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -885,6 +923,7 @@ static struct clk_rcg2 gcc_ufs_phy_ice_core_clk_src = { .name = "gcc_ufs_phy_ice_core_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -899,6 +938,7 @@ static struct clk_rcg2 gcc_ufs_phy_phy_aux_clk_src = { .name = "gcc_ufs_phy_phy_aux_clk_src", .parent_data = gcc_parent_data_3, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -913,6 +953,7 @@ static struct clk_rcg2 gcc_ufs_phy_unipro_core_clk_src = { .name = "gcc_ufs_phy_unipro_core_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -936,6 +977,7 @@ static struct clk_rcg2 gcc_usb30_prim_master_clk_src = { .name = "gcc_usb30_prim_master_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -950,6 +992,7 @@ static struct clk_rcg2 gcc_usb30_prim_mock_utmi_clk_src = { .name = "gcc_usb30_prim_mock_utmi_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -964,6 +1007,7 @@ static struct clk_rcg2 gcc_usb30_sec_master_clk_src = { .name = "gcc_usb30_sec_master_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -978,6 +1022,7 @@ static struct clk_rcg2 gcc_usb30_sec_mock_utmi_clk_src = { .name = "gcc_usb30_sec_mock_utmi_clk_src", .parent_data = gcc_parent_data_0, .num_parents = 3, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -992,6 +1037,7 @@ static struct clk_rcg2 gcc_usb3_prim_phy_aux_clk_src = { .name = "gcc_usb3_prim_phy_aux_clk_src", .parent_data = gcc_parent_data_2, .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; @@ -1006,6 +1052,7 @@ static struct clk_rcg2 gcc_usb3_sec_phy_aux_clk_src = { .name = "gcc_usb3_sec_phy_aux_clk_src", .parent_data = gcc_parent_data_2, .num_parents = 2, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_rcg2_ops, }, }; diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c index bfc4ac02f9ea..b119ab145ea4 100644 --- a/drivers/clk/qcom/gdsc.c +++ b/drivers/clk/qcom/gdsc.c @@ -11,6 +11,7 @@ #include <linux/kernel.h> #include <linux/ktime.h> #include <linux/pm_domain.h> +#include <linux/pm_opp.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/reset-controller.h> @@ -398,6 +399,11 @@ int gdsc_register(struct gdsc_desc *desc, if (!data->domains) return -ENOMEM; + /* For some GDSC an external power domain must be enabled to access registers. */ + ret = of_get_required_opp_performance_state(dev->of_node, 0); + if (ret > 0) + dev_pm_genpd_set_performance_state(dev, ret); + for (i = 0; i < num; i++) { if (!scs[i] || !scs[i]->supply) continue; @@ -445,6 +451,7 @@ void gdsc_unregister(struct gdsc_desc *desc) pm_genpd_remove_subdomain(scs[i]->parent, &scs[i]->pd); } of_genpd_del_provider(dev->of_node); + dev_pm_genpd_set_performance_state(dev, 0); } /* diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index 3fb044b907a8..6eeeb2bd4dfa 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -12,6 +12,7 @@ #include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/pm_opp.h> +#include <linux/regmap.h> #include <linux/slab.h> #define LUT_MAX_ENTRIES 40U @@ -19,18 +20,24 @@ #define LUT_L_VAL GENMASK(7, 0) #define LUT_CORE_COUNT GENMASK(18, 16) #define LUT_VOLT GENMASK(11, 0) -#define LUT_ROW_SIZE 32 #define CLK_HW_DIV 2 #define LUT_TURBO_IND 1 -/* Register offsets */ -#define REG_ENABLE 0x0 -#define REG_FREQ_LUT 0x110 -#define REG_VOLT_LUT 0x114 -#define REG_PERF_STATE 0x920 +struct qcom_cpufreq_soc_data { + u32 reg_enable; + u32 reg_freq_lut; + u32 reg_volt_lut; + u32 reg_perf_state; + u8 lut_row_size; +}; + +struct qcom_cpufreq_data { + void __iomem *base; + struct regmap *regmap; + const struct qcom_cpufreq_soc_data *soc_data; +}; static unsigned long cpu_hw_rate, xo_rate; -static struct platform_device *global_pdev; static bool icc_scaling_enabled; static int qcom_cpufreq_set_bw(struct cpufreq_policy *policy, @@ -77,10 +84,14 @@ static int qcom_cpufreq_update_opp(struct device *cpu_dev, static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, unsigned int index) { - void __iomem *perf_state_reg = policy->driver_data; + struct qcom_cpufreq_data *data = policy->driver_data; + const struct qcom_cpufreq_soc_data *soc_data = data->soc_data; unsigned long freq = policy->freq_table[index].frequency; + int ret; - writel_relaxed(index, perf_state_reg); + ret = regmap_write(data->regmap, soc_data->reg_perf_state, index); + if (ret) + return ret; if (icc_scaling_enabled) qcom_cpufreq_set_bw(policy, freq); @@ -92,17 +103,23 @@ static int qcom_cpufreq_hw_target_index(struct cpufreq_policy *policy, static unsigned int qcom_cpufreq_hw_get(unsigned int cpu) { - void __iomem *perf_state_reg; + struct qcom_cpufreq_data *data; + const struct qcom_cpufreq_soc_data *soc_data; struct cpufreq_policy *policy; unsigned int index; + int ret; policy = cpufreq_cpu_get_raw(cpu); if (!policy) return 0; - perf_state_reg = policy->driver_data; + data = policy->driver_data; + soc_data = data->soc_data; + + ret = regmap_read(data->regmap, soc_data->reg_perf_state, &index); + if (ret) + return 0; - index = readl_relaxed(perf_state_reg); index = min(index, LUT_MAX_ENTRIES - 1); return policy->freq_table[index].frequency; @@ -111,12 +128,16 @@ static unsigned int qcom_cpufreq_hw_get(unsigned int cpu) static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy, unsigned int target_freq) { - void __iomem *perf_state_reg = policy->driver_data; + struct qcom_cpufreq_data *data = policy->driver_data; + const struct qcom_cpufreq_soc_data *soc_data = data->soc_data; unsigned int index; unsigned long freq; + int ret; index = policy->cached_resolved_idx; - writel_relaxed(index, perf_state_reg); + ret = regmap_write(data->regmap, soc_data->reg_perf_state, index); + if (ret) + return 0; freq = policy->freq_table[index].frequency; arch_set_freq_scale(policy->related_cpus, freq, @@ -126,8 +147,7 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy, } static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, - struct cpufreq_policy *policy, - void __iomem *base) + struct cpufreq_policy *policy) { u32 data, src, lval, i, core_count, prev_freq = 0, freq; u32 volt; @@ -135,6 +155,8 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, struct dev_pm_opp *opp; unsigned long rate; int ret; + struct qcom_cpufreq_data *drv_data = policy->driver_data; + const struct qcom_cpufreq_soc_data *soc_data = drv_data->soc_data; table = kcalloc(LUT_MAX_ENTRIES + 1, sizeof(*table), GFP_KERNEL); if (!table) @@ -161,14 +183,24 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, } for (i = 0; i < LUT_MAX_ENTRIES; i++) { - data = readl_relaxed(base + REG_FREQ_LUT + - i * LUT_ROW_SIZE); + ret = regmap_read(drv_data->regmap, soc_data->reg_freq_lut + + i * soc_data->lut_row_size, &data); + if (ret) { + kfree(table); + return ret; + } + src = FIELD_GET(LUT_SRC, data); lval = FIELD_GET(LUT_L_VAL, data); core_count = FIELD_GET(LUT_CORE_COUNT, data); - data = readl_relaxed(base + REG_VOLT_LUT + - i * LUT_ROW_SIZE); + ret = regmap_read(drv_data->regmap, soc_data->reg_volt_lut + + i * soc_data->lut_row_size, &data); + if (ret) { + kfree(table); + return ret; + } + volt = FIELD_GET(LUT_VOLT, data) * 1000; if (src) @@ -238,15 +270,48 @@ static void qcom_get_related_cpus(int index, struct cpumask *m) } } +static struct regmap_config qcom_cpufreq_regmap = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, +}; + +static const struct qcom_cpufreq_soc_data qcom_soc_data = { + .reg_enable = 0x0, + .reg_freq_lut = 0x110, + .reg_volt_lut = 0x114, + .reg_perf_state = 0x920, + .lut_row_size = 32, +}; + +static const struct qcom_cpufreq_soc_data sm8250_soc_data = { + .reg_enable = 0x0, + .reg_freq_lut = 0x100, + .reg_volt_lut = 0x200, + .reg_perf_state = 0x320, + .lut_row_size = 4, +}; + +static const struct of_device_id qcom_cpufreq_hw_match[] = { + { .compatible = "qcom,cpufreq-hw", .data = &qcom_soc_data }, + { .compatible = "qcom,sm8250-epss", .data = &sm8250_soc_data }, + {} +}; +MODULE_DEVICE_TABLE(of, qcom_cpufreq_hw_match); + static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) { - struct device *dev = &global_pdev->dev; + struct platform_device *pdev = cpufreq_get_driver_data(); + struct device *dev = &pdev->dev; struct of_phandle_args args; struct device_node *cpu_np; struct device *cpu_dev; - struct resource *res; void __iomem *base; + struct qcom_cpufreq_data *data; + const struct of_device_id *match; int ret, index; + u32 val; cpu_dev = get_cpu_device(policy->cpu); if (!cpu_dev) { @@ -267,16 +332,36 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) index = args.args[0]; - res = platform_get_resource(global_pdev, IORESOURCE_MEM, index); - if (!res) - return -ENODEV; + base = devm_platform_ioremap_resource(pdev, index); + if (IS_ERR(base)) + return PTR_ERR(base); - base = devm_ioremap(dev, res->start, resource_size(res)); - if (!base) - return -ENOMEM; + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto error; + } + + match = of_match_device(qcom_cpufreq_hw_match, &pdev->dev); + if (!match) { + ret = -ENODEV; + goto error; + } + + data->soc_data = match->data; + data->base = base; + data->regmap = devm_regmap_init_mmio(dev, base, &qcom_cpufreq_regmap); + if (IS_ERR(data->regmap)) { + ret = PTR_ERR(data->regmap); + goto error; + } /* HW should be in enabled state to proceed */ - if (!(readl_relaxed(base + REG_ENABLE) & 0x1)) { + ret = regmap_read(data->regmap, data->soc_data->reg_enable, &val); + if (ret) + goto error; + + if (!(val & 0x1)) { dev_err(dev, "Domain-%d cpufreq hardware not enabled\n", index); ret = -ENODEV; goto error; @@ -289,9 +374,9 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy) goto error; } - policy->driver_data = base + REG_PERF_STATE; + policy->driver_data = data; - ret = qcom_cpufreq_hw_read_lut(cpu_dev, policy, base); + ret = qcom_cpufreq_hw_read_lut(cpu_dev, policy); if (ret) { dev_err(dev, "Domain-%d failed to read LUT\n", index); goto error; @@ -315,12 +400,13 @@ error: static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy) { struct device *cpu_dev = get_cpu_device(policy->cpu); - void __iomem *base = policy->driver_data - REG_PERF_STATE; + struct qcom_cpufreq_data *data = policy->driver_data; + struct platform_device *pdev = cpufreq_get_driver_data(); dev_pm_opp_remove_all_dynamic(cpu_dev); dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); kfree(policy->freq_table); - devm_iounmap(&global_pdev->dev, base); + devm_iounmap(&pdev->dev, data->base); return 0; } @@ -365,7 +451,7 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev) cpu_hw_rate = clk_get_rate(clk) / CLK_HW_DIV; clk_put(clk); - global_pdev = pdev; + cpufreq_qcom_hw_driver.driver_data = pdev; /* Check for optional interconnect paths on CPU0 */ cpu_dev = get_cpu_device(0); @@ -390,12 +476,6 @@ static int qcom_cpufreq_hw_driver_remove(struct platform_device *pdev) return cpufreq_unregister_driver(&cpufreq_qcom_hw_driver); } -static const struct of_device_id qcom_cpufreq_hw_match[] = { - { .compatible = "qcom,cpufreq-hw" }, - {} -}; -MODULE_DEVICE_TABLE(of, qcom_cpufreq_hw_match); - static struct platform_driver qcom_cpufreq_hw_driver = { .probe = qcom_cpufreq_hw_driver_probe, .remove = qcom_cpufreq_hw_driver_remove, diff --git a/drivers/dma/qcom/Kconfig b/drivers/dma/qcom/Kconfig index 3bcb689162c6..f925296b0c85 100644 --- a/drivers/dma/qcom/Kconfig +++ b/drivers/dma/qcom/Kconfig @@ -8,6 +8,18 @@ config QCOM_BAM_DMA Enable support for the QCOM BAM DMA controller. This controller provides DMA capabilities for a variety of on-chip devices. +config QCOM_GPI_DMA + tristate "Qualcomm Technologies GPI DMA support" + depends on ARCH_QCOM + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + Enable support for the QCOM GPI DMA controller. This controller + provides DMA capabilities for a variety of peripheral buses such + as I2C, UART, and SPI. By using GPI dmaengine driver, bus drivers + can use a standardize interface that is protocol independent to + transfer data between DDR and peripheral. + config QCOM_HIDMA_MGMT tristate "Qualcomm Technologies HIDMA Management support" select DMA_ENGINE diff --git a/drivers/dma/qcom/Makefile b/drivers/dma/qcom/Makefile index 1ae92da88b0c..f33f027dd0fc 100644 --- a/drivers/dma/qcom/Makefile +++ b/drivers/dma/qcom/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_QCOM_BAM_DMA) += bam_dma.o +obj-$(CONFIG_QCOM_GPI_DMA) += gpi.o obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mgmt.o hdma_mgmt-objs := hidma_mgmt.o hidma_mgmt_sys.o obj-$(CONFIG_QCOM_HIDMA) += hdma.o diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c new file mode 100644 index 000000000000..29b63ce76e2f --- /dev/null +++ b/drivers/dma/qcom/gpi.c @@ -0,0 +1,2280 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2020, Linaro Limited + */ + +#include <dt-bindings/dma/qcom-gpi.h> +#include <linux/bitfield.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/module.h> +#include <linux/of_dma.h> +#include <linux/platform_device.h> +#include <linux/scatterlist.h> +#include <linux/slab.h> +#include "../dmaengine.h" +#include "../virt-dma.h" + +#define TRE_TYPE_DMA 0x10 +#define TRE_TYPE_GO 0x20 +#define TRE_TYPE_CONFIG0 0x22 + +/* TRE flags */ +#define TRE_FLAGS_CHAIN BIT(0) +#define TRE_FLAGS_IEOB BIT(8) +#define TRE_FLAGS_IEOT BIT(9) +#define TRE_FLAGS_BEI BIT(10) +#define TRE_FLAGS_LINK BIT(11) +#define TRE_FLAGS_TYPE GENMASK(23, 16) + +/* SPI CONFIG0 WD0 */ +#define TRE_SPI_C0_WORD_SZ GENMASK(4, 0) +#define TRE_SPI_C0_LOOPBACK BIT(8) +#define TRE_SPI_C0_CS BIT(11) +#define TRE_SPI_C0_CPHA BIT(12) +#define TRE_SPI_C0_CPOL BIT(13) +#define TRE_SPI_C0_TX_PACK BIT(24) +#define TRE_SPI_C0_RX_PACK BIT(25) + +/* CONFIG0 WD2 */ +#define TRE_C0_CLK_DIV GENMASK(11, 0) +#define TRE_C0_CLK_SRC GENMASK(19, 16) + +/* SPI GO WD0 */ +#define TRE_SPI_GO_CMD GENMASK(4, 0) +#define TRE_SPI_GO_CS GENMASK(10, 8) +#define TRE_SPI_GO_FRAG BIT(26) + +/* GO WD2 */ +#define TRE_RX_LEN GENMASK(23, 0) + +/* I2C Config0 WD0 */ +#define TRE_I2C_C0_TLOW GENMASK(7, 0) +#define TRE_I2C_C0_THIGH GENMASK(15, 8) +#define TRE_I2C_C0_TCYL GENMASK(23, 16) +#define TRE_I2C_C0_TX_PACK BIT(24) +#define TRE_I2C_C0_RX_PACK BIT(25) + +/* I2C GO WD0 */ +#define TRE_I2C_GO_CMD GENMASK(4, 0) +#define TRE_I2C_GO_ADDR GENMASK(14, 8) +#define TRE_I2C_GO_STRETCH BIT(26) + +/* DMA TRE */ +#define TRE_DMA_LEN GENMASK(23, 0) + +/* Register offsets from gpi-top */ +#define GPII_n_CH_k_CNTXT_0_OFFS(n, k) (0x20000 + (0x4000 * (n)) + (0x80 * (k))) +#define GPII_n_CH_k_CNTXT_0_EL_SIZE GENMASK(31, 24) +#define GPII_n_CH_k_CNTXT_0_CHSTATE GENMASK(23, 20) +#define GPII_n_CH_k_CNTXT_0_ERIDX GENMASK(18, 14) +#define GPII_n_CH_k_CNTXT_0_DIR BIT(3) +#define GPII_n_CH_k_CNTXT_0_PROTO GENMASK(2, 0) + +#define GPII_n_CH_k_CNTXT_0(el_size, erindex, dir, chtype_proto) \ + (FIELD_PREP(GPII_n_CH_k_CNTXT_0_EL_SIZE, el_size) | \ + FIELD_PREP(GPII_n_CH_k_CNTXT_0_ERIDX, erindex) | \ + FIELD_PREP(GPII_n_CH_k_CNTXT_0_DIR, dir) | \ + FIELD_PREP(GPII_n_CH_k_CNTXT_0_PROTO, chtype_proto)) + +#define GPI_CHTYPE_DIR_IN (0) +#define GPI_CHTYPE_DIR_OUT (1) + +#define GPI_CHTYPE_PROTO_GPI (0x2) + +#define GPII_n_CH_k_DOORBELL_0_OFFS(n, k) (0x22000 + (0x4000 * (n)) + (0x8 * (k))) +#define GPII_n_CH_CMD_OFFS(n) (0x23008 + (0x4000 * (n))) +#define GPII_n_CH_CMD_OPCODE GENMASK(31, 24) +#define GPII_n_CH_CMD_CHID GENMASK(7, 0) +#define GPII_n_CH_CMD(opcode, chid) \ + (FIELD_PREP(GPII_n_CH_CMD_OPCODE, opcode) | \ + FIELD_PREP(GPII_n_CH_CMD_CHID, chid)) + +#define GPII_n_CH_CMD_ALLOCATE (0) +#define GPII_n_CH_CMD_START (1) +#define GPII_n_CH_CMD_STOP (2) +#define GPII_n_CH_CMD_RESET (9) +#define GPII_n_CH_CMD_DE_ALLOC (10) +#define GPII_n_CH_CMD_UART_SW_STALE (32) +#define GPII_n_CH_CMD_UART_RFR_READY (33) +#define GPII_n_CH_CMD_UART_RFR_NOT_READY (34) + +/* EV Context Array */ +#define GPII_n_EV_CH_k_CNTXT_0_OFFS(n, k) (0x21000 + (0x4000 * (n)) + (0x80 * (k))) +#define GPII_n_EV_k_CNTXT_0_EL_SIZE GENMASK(31, 24) +#define GPII_n_EV_k_CNTXT_0_CHSTATE GENMASK(23, 20) +#define GPII_n_EV_k_CNTXT_0_INTYPE BIT(16) +#define GPII_n_EV_k_CNTXT_0_CHTYPE GENMASK(3, 0) + +#define GPII_n_EV_k_CNTXT_0(el_size, inttype, chtype) \ + (FIELD_PREP(GPII_n_EV_k_CNTXT_0_EL_SIZE, el_size) | \ + FIELD_PREP(GPII_n_EV_k_CNTXT_0_INTYPE, inttype) | \ + FIELD_PREP(GPII_n_EV_k_CNTXT_0_CHTYPE, chtype)) + +#define GPI_INTTYPE_IRQ (1) +#define GPI_CHTYPE_GPI_EV (0x2) + +enum CNTXT_OFFS { + CNTXT_0_CONFIG = 0x0, + CNTXT_1_R_LENGTH = 0x4, + CNTXT_2_RING_BASE_LSB = 0x8, + CNTXT_3_RING_BASE_MSB = 0xC, + CNTXT_4_RING_RP_LSB = 0x10, + CNTXT_5_RING_RP_MSB = 0x14, + CNTXT_6_RING_WP_LSB = 0x18, + CNTXT_7_RING_WP_MSB = 0x1C, + CNTXT_8_RING_INT_MOD = 0x20, + CNTXT_9_RING_INTVEC = 0x24, + CNTXT_10_RING_MSI_LSB = 0x28, + CNTXT_11_RING_MSI_MSB = 0x2C, + CNTXT_12_RING_RP_UPDATE_LSB = 0x30, + CNTXT_13_RING_RP_UPDATE_MSB = 0x34, +}; + +#define GPII_n_EV_CH_k_DOORBELL_0_OFFS(n, k) (0x22100 + (0x4000 * (n)) + (0x8 * (k))) +#define GPII_n_EV_CH_CMD_OFFS(n) (0x23010 + (0x4000 * (n))) +#define GPII_n_EV_CMD_OPCODE GENMASK(31, 24) +#define GPII_n_EV_CMD_CHID GENMASK(7, 0) +#define GPII_n_EV_CMD(opcode, chid) \ + (FIELD_PREP(GPII_n_EV_CMD_OPCODE, opcode) | \ + FIELD_PREP(GPII_n_EV_CMD_CHID, chid)) + +#define GPII_n_EV_CH_CMD_ALLOCATE (0x00) +#define GPII_n_EV_CH_CMD_RESET (0x09) +#define GPII_n_EV_CH_CMD_DE_ALLOC (0x0A) + +#define GPII_n_CNTXT_TYPE_IRQ_OFFS(n) (0x23080 + (0x4000 * (n))) + +/* mask type register */ +#define GPII_n_CNTXT_TYPE_IRQ_MSK_OFFS(n) (0x23088 + (0x4000 * (n))) +#define GPII_n_CNTXT_TYPE_IRQ_MSK_BMSK GENMASK(6, 0) +#define GPII_n_CNTXT_TYPE_IRQ_MSK_GENERAL BIT(6) +#define GPII_n_CNTXT_TYPE_IRQ_MSK_IEOB BIT(3) +#define GPII_n_CNTXT_TYPE_IRQ_MSK_GLOB BIT(2) +#define GPII_n_CNTXT_TYPE_IRQ_MSK_EV_CTRL BIT(1) +#define GPII_n_CNTXT_TYPE_IRQ_MSK_CH_CTRL BIT(0) + +#define GPII_n_CNTXT_SRC_GPII_CH_IRQ_OFFS(n) (0x23090 + (0x4000 * (n))) +#define GPII_n_CNTXT_SRC_EV_CH_IRQ_OFFS(n) (0x23094 + (0x4000 * (n))) + +/* Mask channel control interrupt register */ +#define GPII_n_CNTXT_SRC_CH_IRQ_MSK_OFFS(n) (0x23098 + (0x4000 * (n))) +#define GPII_n_CNTXT_SRC_CH_IRQ_MSK_BMSK GENMASK(1, 0) + +/* Mask event control interrupt register */ +#define GPII_n_CNTXT_SRC_EV_CH_IRQ_MSK_OFFS(n) (0x2309C + (0x4000 * (n))) +#define GPII_n_CNTXT_SRC_EV_CH_IRQ_MSK_BMSK BIT(0) + +#define GPII_n_CNTXT_SRC_CH_IRQ_CLR_OFFS(n) (0x230A0 + (0x4000 * (n))) +#define GPII_n_CNTXT_SRC_EV_CH_IRQ_CLR_OFFS(n) (0x230A4 + (0x4000 * (n))) + +/* Mask event interrupt register */ +#define GPII_n_CNTXT_SRC_IEOB_IRQ_MSK_OFFS(n) (0x230B8 + (0x4000 * (n))) +#define GPII_n_CNTXT_SRC_IEOB_IRQ_MSK_BMSK BIT(0) + +#define GPII_n_CNTXT_SRC_IEOB_IRQ_CLR_OFFS(n) (0x230C0 + (0x4000 * (n))) +#define GPII_n_CNTXT_GLOB_IRQ_STTS_OFFS(n) (0x23100 + (0x4000 * (n))) +#define GPI_GLOB_IRQ_ERROR_INT_MSK BIT(0) + +/* GPII specific Global - Enable bit register */ +#define GPII_n_CNTXT_GLOB_IRQ_EN_OFFS(n) (0x23108 + (0x4000 * (n))) +#define GPII_n_CNTXT_GLOB_IRQ_CLR_OFFS(n) (0x23110 + (0x4000 * (n))) +#define GPII_n_CNTXT_GPII_IRQ_STTS_OFFS(n) (0x23118 + (0x4000 * (n))) + +/* GPII general interrupt - Enable bit register */ +#define GPII_n_CNTXT_GPII_IRQ_EN_OFFS(n) (0x23120 + (0x4000 * (n))) +#define GPII_n_CNTXT_GPII_IRQ_EN_BMSK GENMASK(3, 0) + +#define GPII_n_CNTXT_GPII_IRQ_CLR_OFFS(n) (0x23128 + (0x4000 * (n))) + +/* GPII Interrupt Type register */ +#define GPII_n_CNTXT_INTSET_OFFS(n) (0x23180 + (0x4000 * (n))) +#define GPII_n_CNTXT_INTSET_BMSK BIT(0) + +#define GPII_n_CNTXT_MSI_BASE_LSB_OFFS(n) (0x23188 + (0x4000 * (n))) +#define GPII_n_CNTXT_MSI_BASE_MSB_OFFS(n) (0x2318C + (0x4000 * (n))) +#define GPII_n_CNTXT_SCRATCH_0_OFFS(n) (0x23400 + (0x4000 * (n))) +#define GPII_n_CNTXT_SCRATCH_1_OFFS(n) (0x23404 + (0x4000 * (n))) + +#define GPII_n_ERROR_LOG_OFFS(n) (0x23200 + (0x4000 * (n))) + +/* QOS Registers */ +#define GPII_n_CH_k_QOS_OFFS(n, k) (0x2005C + (0x4000 * (n)) + (0x80 * (k))) + +/* Scratch registers */ +#define GPII_n_CH_k_SCRATCH_0_OFFS(n, k) (0x20060 + (0x4000 * (n)) + (0x80 * (k))) +#define GPII_n_CH_k_SCRATCH_0_SEID GENMASK(2, 0) +#define GPII_n_CH_k_SCRATCH_0_PROTO GENMASK(7, 4) +#define GPII_n_CH_k_SCRATCH_0_PAIR GENMASK(20, 16) +#define GPII_n_CH_k_SCRATCH_0(pair, proto, seid) \ + (FIELD_PREP(GPII_n_CH_k_SCRATCH_0_PAIR, pair) | \ + FIELD_PREP(GPII_n_CH_k_SCRATCH_0_PROTO, proto) | \ + FIELD_PREP(GPII_n_CH_k_SCRATCH_0_SEID, seid)) +#define GPII_n_CH_k_SCRATCH_1_OFFS(n, k) (0x20064 + (0x4000 * (n)) + (0x80 * (k))) +#define GPII_n_CH_k_SCRATCH_2_OFFS(n, k) (0x20068 + (0x4000 * (n)) + (0x80 * (k))) +#define GPII_n_CH_k_SCRATCH_3_OFFS(n, k) (0x2006C + (0x4000 * (n)) + (0x80 * (k))) + +struct __packed gpi_tre { + u32 dword[4]; +}; + +enum msm_gpi_tce_code { + MSM_GPI_TCE_SUCCESS = 1, + MSM_GPI_TCE_EOT = 2, + MSM_GPI_TCE_EOB = 4, + MSM_GPI_TCE_UNEXP_ERR = 16, +}; + +#define CMD_TIMEOUT_MS (250) + +#define MAX_CHANNELS_PER_GPII (2) +#define GPI_TX_CHAN (0) +#define GPI_RX_CHAN (1) +#define STATE_IGNORE (U32_MAX) +#define EV_FACTOR (2) +#define REQ_OF_DMA_ARGS (5) /* # of arguments required from client */ +#define CHAN_TRES 64 + +struct __packed xfer_compl_event { + u64 ptr; + u32 length:24; + u8 code; + u16 status; + u8 type; + u8 chid; +}; + +struct __packed immediate_data_event { + u8 data_bytes[8]; + u8 length:4; + u8 resvd:4; + u16 tre_index; + u8 code; + u16 status; + u8 type; + u8 chid; +}; + +struct __packed qup_notif_event { + u32 status; + u32 time; + u32 count:24; + u8 resvd; + u16 resvd1; + u8 type; + u8 chid; +}; + +struct __packed gpi_ere { + u32 dword[4]; +}; + +enum GPI_EV_TYPE { + XFER_COMPLETE_EV_TYPE = 0x22, + IMMEDIATE_DATA_EV_TYPE = 0x30, + QUP_NOTIF_EV_TYPE = 0x31, + STALE_EV_TYPE = 0xFF, +}; + +union __packed gpi_event { + struct __packed xfer_compl_event xfer_compl_event; + struct __packed immediate_data_event immediate_data_event; + struct __packed qup_notif_event qup_notif_event; + struct __packed gpi_ere gpi_ere; +}; + +enum gpii_irq_settings { + DEFAULT_IRQ_SETTINGS, + MASK_IEOB_SETTINGS, +}; + +enum gpi_ev_state { + DEFAULT_EV_CH_STATE = 0, + EV_STATE_NOT_ALLOCATED = DEFAULT_EV_CH_STATE, + EV_STATE_ALLOCATED, + MAX_EV_STATES +}; + +static const char *const gpi_ev_state_str[MAX_EV_STATES] = { + [EV_STATE_NOT_ALLOCATED] = "NOT ALLOCATED", + [EV_STATE_ALLOCATED] = "ALLOCATED", +}; + +#define TO_GPI_EV_STATE_STR(_state) (((_state) >= MAX_EV_STATES) ? \ + "INVALID" : gpi_ev_state_str[(_state)]) + +enum gpi_ch_state { + DEFAULT_CH_STATE = 0x0, + CH_STATE_NOT_ALLOCATED = DEFAULT_CH_STATE, + CH_STATE_ALLOCATED = 0x1, + CH_STATE_STARTED = 0x2, + CH_STATE_STOPPED = 0x3, + CH_STATE_STOP_IN_PROC = 0x4, + CH_STATE_ERROR = 0xf, + MAX_CH_STATES +}; + +enum gpi_cmd { + GPI_CH_CMD_BEGIN, + GPI_CH_CMD_ALLOCATE = GPI_CH_CMD_BEGIN, + GPI_CH_CMD_START, + GPI_CH_CMD_STOP, + GPI_CH_CMD_RESET, + GPI_CH_CMD_DE_ALLOC, + GPI_CH_CMD_UART_SW_STALE, + GPI_CH_CMD_UART_RFR_READY, + GPI_CH_CMD_UART_RFR_NOT_READY, + GPI_CH_CMD_END = GPI_CH_CMD_UART_RFR_NOT_READY, + GPI_EV_CMD_BEGIN, + GPI_EV_CMD_ALLOCATE = GPI_EV_CMD_BEGIN, + GPI_EV_CMD_RESET, + GPI_EV_CMD_DEALLOC, + GPI_EV_CMD_END = GPI_EV_CMD_DEALLOC, + GPI_MAX_CMD, +}; + +#define IS_CHAN_CMD(_cmd) ((_cmd) <= GPI_CH_CMD_END) + +static const char *const gpi_cmd_str[GPI_MAX_CMD] = { + [GPI_CH_CMD_ALLOCATE] = "CH ALLOCATE", + [GPI_CH_CMD_START] = "CH START", + [GPI_CH_CMD_STOP] = "CH STOP", + [GPI_CH_CMD_RESET] = "CH_RESET", + [GPI_CH_CMD_DE_ALLOC] = "DE ALLOC", + [GPI_CH_CMD_UART_SW_STALE] = "UART SW STALE", + [GPI_CH_CMD_UART_RFR_READY] = "UART RFR READY", + [GPI_CH_CMD_UART_RFR_NOT_READY] = "UART RFR NOT READY", + [GPI_EV_CMD_ALLOCATE] = "EV ALLOCATE", + [GPI_EV_CMD_RESET] = "EV RESET", + [GPI_EV_CMD_DEALLOC] = "EV DEALLOC", +}; + +#define TO_GPI_CMD_STR(_cmd) (((_cmd) >= GPI_MAX_CMD) ? "INVALID" : \ + gpi_cmd_str[(_cmd)]) + +/* + * @DISABLE_STATE: no register access allowed + * @CONFIG_STATE: client has configured the channel + * @PREP_HARDWARE: register access is allowed + * however, no processing EVENTS + * @ACTIVE_STATE: channels are fully operational + * @PREPARE_TERMINATE: graceful termination of channels + * register access is allowed + * @PAUSE_STATE: channels are active, but not processing any events + */ +enum gpi_pm_state { + DISABLE_STATE, + CONFIG_STATE, + PREPARE_HARDWARE, + ACTIVE_STATE, + PREPARE_TERMINATE, + PAUSE_STATE, + MAX_PM_STATE +}; + +#define REG_ACCESS_VALID(_pm_state) ((_pm_state) >= PREPARE_HARDWARE) + +static const char *const gpi_pm_state_str[MAX_PM_STATE] = { + [DISABLE_STATE] = "DISABLE", + [CONFIG_STATE] = "CONFIG", + [PREPARE_HARDWARE] = "PREPARE HARDWARE", + [ACTIVE_STATE] = "ACTIVE", + [PREPARE_TERMINATE] = "PREPARE TERMINATE", + [PAUSE_STATE] = "PAUSE", +}; + +#define TO_GPI_PM_STR(_state) (((_state) >= MAX_PM_STATE) ? \ + "INVALID" : gpi_pm_state_str[(_state)]) + +static const struct { + enum gpi_cmd gpi_cmd; + u32 opcode; + u32 state; +} gpi_cmd_info[GPI_MAX_CMD] = { + { + GPI_CH_CMD_ALLOCATE, + GPII_n_CH_CMD_ALLOCATE, + CH_STATE_ALLOCATED, + }, + { + GPI_CH_CMD_START, + GPII_n_CH_CMD_START, + CH_STATE_STARTED, + }, + { + GPI_CH_CMD_STOP, + GPII_n_CH_CMD_STOP, + CH_STATE_STOPPED, + }, + { + GPI_CH_CMD_RESET, + GPII_n_CH_CMD_RESET, + CH_STATE_ALLOCATED, + }, + { + GPI_CH_CMD_DE_ALLOC, + GPII_n_CH_CMD_DE_ALLOC, + CH_STATE_NOT_ALLOCATED, + }, + { + GPI_CH_CMD_UART_SW_STALE, + GPII_n_CH_CMD_UART_SW_STALE, + STATE_IGNORE, + }, + { + GPI_CH_CMD_UART_RFR_READY, + GPII_n_CH_CMD_UART_RFR_READY, + STATE_IGNORE, + }, + { + GPI_CH_CMD_UART_RFR_NOT_READY, + GPII_n_CH_CMD_UART_RFR_NOT_READY, + STATE_IGNORE, + }, + { + GPI_EV_CMD_ALLOCATE, + GPII_n_EV_CH_CMD_ALLOCATE, + EV_STATE_ALLOCATED, + }, + { + GPI_EV_CMD_RESET, + GPII_n_EV_CH_CMD_RESET, + EV_STATE_ALLOCATED, + }, + { + GPI_EV_CMD_DEALLOC, + GPII_n_EV_CH_CMD_DE_ALLOC, + EV_STATE_NOT_ALLOCATED, + }, +}; + +struct gpi_ring { + void *pre_aligned; + size_t alloc_size; + phys_addr_t phys_addr; + dma_addr_t dma_handle; + void *base; + void *wp; + void *rp; + u32 len; + u32 el_size; + u32 elements; + bool configured; +}; + +struct gpi_dev { + struct dma_device dma_device; + struct device *dev; + struct resource *res; + void __iomem *regs; + void __iomem *ee_base; /*ee register base address*/ + u32 max_gpii; /* maximum # of gpii instances available per gpi block */ + u32 gpii_mask; /* gpii instances available for apps */ + u32 ev_factor; /* ev ring length factor */ + struct gpii *gpiis; +}; + +struct reg_info { + char *name; + u32 offset; + u32 val; +}; + +struct gchan { + struct virt_dma_chan vc; + u32 chid; + u32 seid; + u32 protocol; + struct gpii *gpii; + enum gpi_ch_state ch_state; + enum gpi_pm_state pm_state; + void __iomem *ch_cntxt_base_reg; + void __iomem *ch_cntxt_db_reg; + void __iomem *ch_cmd_reg; + u32 dir; + struct gpi_ring ch_ring; + struct dmaengine_peripheral_config config; +}; + +struct gpii { + u32 gpii_id; + struct gchan gchan[MAX_CHANNELS_PER_GPII]; + struct gpi_dev *gpi_dev; + int irq; + void __iomem *regs; /* points to gpi top */ + void __iomem *ev_cntxt_base_reg; + void __iomem *ev_cntxt_db_reg; + void __iomem *ev_ring_rp_lsb_reg; + void __iomem *ev_cmd_reg; + void __iomem *ieob_clr_reg; + struct mutex ctrl_lock; + enum gpi_ev_state ev_state; + bool configured_irq; + enum gpi_pm_state pm_state; + rwlock_t pm_lock; + struct gpi_ring ev_ring; + struct tasklet_struct ev_task; /* event processing tasklet */ + struct completion cmd_completion; + enum gpi_cmd gpi_cmd; + u32 cntxt_type_irq_msk; + bool ieob_set; +}; + +#define MAX_TRE 3 + +struct gpi_desc { + struct virt_dma_desc vd; + size_t len; + void *db; /* DB register to program */ + struct gchan *gchan; + struct gpi_tre tre[MAX_TRE]; + u32 num_tre; +}; + +static const u32 GPII_CHAN_DIR[MAX_CHANNELS_PER_GPII] = { + GPI_CHTYPE_DIR_OUT, GPI_CHTYPE_DIR_IN +}; + +static irqreturn_t gpi_handle_irq(int irq, void *data); +static void gpi_ring_recycle_ev_element(struct gpi_ring *ring); +static int gpi_ring_add_element(struct gpi_ring *ring, void **wp); +static void gpi_process_events(struct gpii *gpii); + +static inline struct gchan *to_gchan(struct dma_chan *dma_chan) +{ + return container_of(dma_chan, struct gchan, vc.chan); +} + +static inline struct gpi_desc *to_gpi_desc(struct virt_dma_desc *vd) +{ + return container_of(vd, struct gpi_desc, vd); +} + +static inline phys_addr_t to_physical(const struct gpi_ring *const ring, + void *addr) +{ + return ring->phys_addr + (addr - ring->base); +} + +static inline void *to_virtual(const struct gpi_ring *const ring, phys_addr_t addr) +{ + return ring->base + (addr - ring->phys_addr); +} + +static inline u32 gpi_read_reg(struct gpii *gpii, void __iomem *addr) +{ + return readl_relaxed(addr); +} + +static inline void gpi_write_reg(struct gpii *gpii, void __iomem *addr, u32 val) +{ + writel_relaxed(val, addr); +} + +/* gpi_write_reg_field - write to specific bit field */ +static inline void gpi_write_reg_field(struct gpii *gpii, void __iomem *addr, + u32 mask, u32 shift, u32 val) +{ + u32 tmp = gpi_read_reg(gpii, addr); + + tmp &= ~mask; + val = tmp | ((val << shift) & mask); + gpi_write_reg(gpii, addr, val); +} + +static inline void +gpi_update_reg(struct gpii *gpii, u32 offset, u32 mask, u32 val) +{ + void __iomem *addr = gpii->regs + offset; + u32 tmp = gpi_read_reg(gpii, addr); + + tmp &= ~mask; + tmp |= u32_encode_bits(val, mask); + + gpi_write_reg(gpii, addr, tmp); +} + +static void gpi_disable_interrupts(struct gpii *gpii) +{ + gpi_update_reg(gpii, GPII_n_CNTXT_TYPE_IRQ_MSK_OFFS(gpii->gpii_id), + GPII_n_CNTXT_TYPE_IRQ_MSK_BMSK, 0); + gpi_update_reg(gpii, GPII_n_CNTXT_SRC_IEOB_IRQ_MSK_OFFS(gpii->gpii_id), + GPII_n_CNTXT_SRC_IEOB_IRQ_MSK_BMSK, 0); + gpi_update_reg(gpii, GPII_n_CNTXT_SRC_CH_IRQ_MSK_OFFS(gpii->gpii_id), + GPII_n_CNTXT_SRC_CH_IRQ_MSK_BMSK, 0); + gpi_update_reg(gpii, GPII_n_CNTXT_SRC_EV_CH_IRQ_MSK_OFFS(gpii->gpii_id), + GPII_n_CNTXT_SRC_EV_CH_IRQ_MSK_BMSK, 0); + gpi_update_reg(gpii, GPII_n_CNTXT_GLOB_IRQ_EN_OFFS(gpii->gpii_id), + GPII_n_CNTXT_GPII_IRQ_EN_BMSK, 0); + gpi_update_reg(gpii, GPII_n_CNTXT_GPII_IRQ_EN_OFFS(gpii->gpii_id), + GPII_n_CNTXT_GPII_IRQ_EN_BMSK, 0); + gpi_update_reg(gpii, GPII_n_CNTXT_INTSET_OFFS(gpii->gpii_id), + GPII_n_CNTXT_INTSET_BMSK, 0); + + gpii->cntxt_type_irq_msk = 0; + devm_free_irq(gpii->gpi_dev->dev, gpii->irq, gpii); + gpii->configured_irq = false; +} + +/* configure and enable interrupts */ +static int gpi_config_interrupts(struct gpii *gpii, enum gpii_irq_settings settings, bool mask) +{ + const u32 enable = (GPII_n_CNTXT_TYPE_IRQ_MSK_GENERAL | + GPII_n_CNTXT_TYPE_IRQ_MSK_IEOB | + GPII_n_CNTXT_TYPE_IRQ_MSK_GLOB | + GPII_n_CNTXT_TYPE_IRQ_MSK_EV_CTRL | + GPII_n_CNTXT_TYPE_IRQ_MSK_CH_CTRL); + int ret; + + if (!gpii->configured_irq) { + ret = devm_request_irq(gpii->gpi_dev->dev, gpii->irq, + gpi_handle_irq, IRQF_TRIGGER_HIGH, + "gpi-dma", gpii); + if (ret < 0) { + dev_err(gpii->gpi_dev->dev, "error request irq:%d ret:%d\n", + gpii->irq, ret); + return ret; + } + } + + if (settings == MASK_IEOB_SETTINGS) { + /* + * GPII only uses one EV ring per gpii so we can globally + * enable/disable IEOB interrupt + */ + if (mask) + gpii->cntxt_type_irq_msk |= GPII_n_CNTXT_TYPE_IRQ_MSK_IEOB; + else + gpii->cntxt_type_irq_msk &= ~(GPII_n_CNTXT_TYPE_IRQ_MSK_IEOB); + gpi_update_reg(gpii, GPII_n_CNTXT_TYPE_IRQ_MSK_OFFS(gpii->gpii_id), + GPII_n_CNTXT_TYPE_IRQ_MSK_BMSK, gpii->cntxt_type_irq_msk); + } else { + gpi_update_reg(gpii, GPII_n_CNTXT_TYPE_IRQ_MSK_OFFS(gpii->gpii_id), + GPII_n_CNTXT_TYPE_IRQ_MSK_BMSK, enable); + gpi_update_reg(gpii, GPII_n_CNTXT_SRC_IEOB_IRQ_MSK_OFFS(gpii->gpii_id), + GPII_n_CNTXT_SRC_IEOB_IRQ_MSK_BMSK, + GPII_n_CNTXT_SRC_IEOB_IRQ_MSK_BMSK); + gpi_update_reg(gpii, GPII_n_CNTXT_SRC_CH_IRQ_MSK_OFFS(gpii->gpii_id), + GPII_n_CNTXT_SRC_CH_IRQ_MSK_BMSK, + GPII_n_CNTXT_SRC_CH_IRQ_MSK_BMSK); + gpi_update_reg(gpii, GPII_n_CNTXT_SRC_EV_CH_IRQ_MSK_OFFS(gpii->gpii_id), + GPII_n_CNTXT_SRC_EV_CH_IRQ_MSK_BMSK, + GPII_n_CNTXT_SRC_EV_CH_IRQ_MSK_BMSK); + gpi_update_reg(gpii, GPII_n_CNTXT_GLOB_IRQ_EN_OFFS(gpii->gpii_id), + GPII_n_CNTXT_GPII_IRQ_EN_BMSK, + GPII_n_CNTXT_GPII_IRQ_EN_BMSK); + gpi_update_reg(gpii, GPII_n_CNTXT_GPII_IRQ_EN_OFFS(gpii->gpii_id), + GPII_n_CNTXT_GPII_IRQ_EN_BMSK, GPII_n_CNTXT_GPII_IRQ_EN_BMSK); + gpi_update_reg(gpii, GPII_n_CNTXT_MSI_BASE_LSB_OFFS(gpii->gpii_id), U32_MAX, 0); + gpi_update_reg(gpii, GPII_n_CNTXT_MSI_BASE_MSB_OFFS(gpii->gpii_id), U32_MAX, 0); + gpi_update_reg(gpii, GPII_n_CNTXT_SCRATCH_0_OFFS(gpii->gpii_id), U32_MAX, 0); + gpi_update_reg(gpii, GPII_n_CNTXT_SCRATCH_1_OFFS(gpii->gpii_id), U32_MAX, 0); + gpi_update_reg(gpii, GPII_n_CNTXT_INTSET_OFFS(gpii->gpii_id), + GPII_n_CNTXT_INTSET_BMSK, 1); + gpi_update_reg(gpii, GPII_n_ERROR_LOG_OFFS(gpii->gpii_id), U32_MAX, 0); + + gpii->cntxt_type_irq_msk = enable; + } + + gpii->configured_irq = true; + return 0; +} + +/* Sends gpii event or channel command */ +static int gpi_send_cmd(struct gpii *gpii, struct gchan *gchan, + enum gpi_cmd gpi_cmd) +{ + u32 chid = MAX_CHANNELS_PER_GPII; + unsigned long timeout; + void __iomem *cmd_reg; + u32 cmd; + + if (gpi_cmd >= GPI_MAX_CMD) + return -EINVAL; + if (IS_CHAN_CMD(gpi_cmd)) + chid = gchan->chid; + + dev_dbg(gpii->gpi_dev->dev, + "sending cmd: %s:%u\n", TO_GPI_CMD_STR(gpi_cmd), chid); + + /* send opcode and wait for completion */ + reinit_completion(&gpii->cmd_completion); + gpii->gpi_cmd = gpi_cmd; + + cmd_reg = IS_CHAN_CMD(gpi_cmd) ? gchan->ch_cmd_reg : gpii->ev_cmd_reg; + cmd = IS_CHAN_CMD(gpi_cmd) ? GPII_n_CH_CMD(gpi_cmd_info[gpi_cmd].opcode, chid) : + GPII_n_EV_CMD(gpi_cmd_info[gpi_cmd].opcode, 0); + gpi_write_reg(gpii, cmd_reg, cmd); + timeout = wait_for_completion_timeout(&gpii->cmd_completion, + msecs_to_jiffies(CMD_TIMEOUT_MS)); + if (!timeout) { + dev_err(gpii->gpi_dev->dev, "cmd: %s completion timeout:%u\n", + TO_GPI_CMD_STR(gpi_cmd), chid); + return -EIO; + } + + /* confirm new ch state is correct , if the cmd is a state change cmd */ + if (gpi_cmd_info[gpi_cmd].state == STATE_IGNORE) + return 0; + + if (IS_CHAN_CMD(gpi_cmd) && gchan->ch_state == gpi_cmd_info[gpi_cmd].state) + return 0; + + if (!IS_CHAN_CMD(gpi_cmd) && gpii->ev_state == gpi_cmd_info[gpi_cmd].state) + return 0; + + return -EIO; +} + +/* program transfer ring DB register */ +static inline void gpi_write_ch_db(struct gchan *gchan, + struct gpi_ring *ring, void *wp) +{ + struct gpii *gpii = gchan->gpii; + phys_addr_t p_wp; + + p_wp = to_physical(ring, wp); + gpi_write_reg(gpii, gchan->ch_cntxt_db_reg, p_wp); +} + +/* program event ring DB register */ +static inline void gpi_write_ev_db(struct gpii *gpii, + struct gpi_ring *ring, void *wp) +{ + phys_addr_t p_wp; + + p_wp = ring->phys_addr + (wp - ring->base); + gpi_write_reg(gpii, gpii->ev_cntxt_db_reg, p_wp); +} + +/* process transfer completion interrupt */ +static void gpi_process_ieob(struct gpii *gpii) +{ + gpi_write_reg(gpii, gpii->ieob_clr_reg, BIT(0)); + + gpi_config_interrupts(gpii, MASK_IEOB_SETTINGS, 0); + tasklet_hi_schedule(&gpii->ev_task); +} + +/* process channel control interrupt */ +static void gpi_process_ch_ctrl_irq(struct gpii *gpii) +{ + u32 gpii_id = gpii->gpii_id; + u32 offset = GPII_n_CNTXT_SRC_GPII_CH_IRQ_OFFS(gpii_id); + u32 ch_irq = gpi_read_reg(gpii, gpii->regs + offset); + struct gchan *gchan; + u32 chid, state; + + /* clear the status */ + offset = GPII_n_CNTXT_SRC_CH_IRQ_CLR_OFFS(gpii_id); + gpi_write_reg(gpii, gpii->regs + offset, (u32)ch_irq); + + for (chid = 0; chid < MAX_CHANNELS_PER_GPII; chid++) { + if (!(BIT(chid) & ch_irq)) + continue; + + gchan = &gpii->gchan[chid]; + state = gpi_read_reg(gpii, gchan->ch_cntxt_base_reg + + CNTXT_0_CONFIG); + state = FIELD_GET(GPII_n_CH_k_CNTXT_0_CHSTATE, state); + + /* + * CH_CMD_DEALLOC cmd always successful. However cmd does + * not change hardware status. So overwriting software state + * to default state. + */ + if (gpii->gpi_cmd == GPI_CH_CMD_DE_ALLOC) + state = DEFAULT_CH_STATE; + gchan->ch_state = state; + + /* + * Triggering complete all if ch_state is not a stop in process. + * Stop in process is a transition state and we will wait for + * stop interrupt before notifying. + */ + if (gchan->ch_state != CH_STATE_STOP_IN_PROC) + complete_all(&gpii->cmd_completion); + } +} + +/* processing gpi general error interrupts */ +static void gpi_process_gen_err_irq(struct gpii *gpii) +{ + u32 gpii_id = gpii->gpii_id; + u32 offset = GPII_n_CNTXT_GPII_IRQ_STTS_OFFS(gpii_id); + u32 irq_stts = gpi_read_reg(gpii, gpii->regs + offset); + + /* clear the status */ + dev_dbg(gpii->gpi_dev->dev, "irq_stts:0x%x\n", irq_stts); + + /* Clear the register */ + offset = GPII_n_CNTXT_GPII_IRQ_CLR_OFFS(gpii_id); + gpi_write_reg(gpii, gpii->regs + offset, irq_stts); +} + +/* processing gpi level error interrupts */ +static void gpi_process_glob_err_irq(struct gpii *gpii) +{ + u32 gpii_id = gpii->gpii_id; + u32 offset = GPII_n_CNTXT_GLOB_IRQ_STTS_OFFS(gpii_id); + u32 irq_stts = gpi_read_reg(gpii, gpii->regs + offset); + + offset = GPII_n_CNTXT_GLOB_IRQ_CLR_OFFS(gpii_id); + gpi_write_reg(gpii, gpii->regs + offset, irq_stts); + + /* only error interrupt should be set */ + if (irq_stts & ~GPI_GLOB_IRQ_ERROR_INT_MSK) { + dev_err(gpii->gpi_dev->dev, "invalid error status:0x%x\n", irq_stts); + return; + } + + offset = GPII_n_ERROR_LOG_OFFS(gpii_id); + gpi_write_reg(gpii, gpii->regs + offset, 0); +} + +/* gpii interrupt handler */ +static irqreturn_t gpi_handle_irq(int irq, void *data) +{ + struct gpii *gpii = data; + u32 gpii_id = gpii->gpii_id; + u32 type, offset; + unsigned long flags; + + read_lock_irqsave(&gpii->pm_lock, flags); + + /* + * States are out of sync to receive interrupt + * while software state is in DISABLE state, bailing out. + */ + if (!REG_ACCESS_VALID(gpii->pm_state)) { + dev_err(gpii->gpi_dev->dev, "receive interrupt while in %s state\n", + TO_GPI_PM_STR(gpii->pm_state)); + goto exit_irq; + } + + offset = GPII_n_CNTXT_TYPE_IRQ_OFFS(gpii->gpii_id); + type = gpi_read_reg(gpii, gpii->regs + offset); + + do { + /* global gpii error */ + if (type & GPII_n_CNTXT_TYPE_IRQ_MSK_GLOB) { + gpi_process_glob_err_irq(gpii); + type &= ~(GPII_n_CNTXT_TYPE_IRQ_MSK_GLOB); + } + + /* transfer complete interrupt */ + if (type & GPII_n_CNTXT_TYPE_IRQ_MSK_IEOB) { + gpi_process_ieob(gpii); + type &= ~GPII_n_CNTXT_TYPE_IRQ_MSK_IEOB; + } + + /* event control irq */ + if (type & GPII_n_CNTXT_TYPE_IRQ_MSK_EV_CTRL) { + u32 ev_state; + u32 ev_ch_irq; + + dev_dbg(gpii->gpi_dev->dev, + "processing EV CTRL interrupt\n"); + offset = GPII_n_CNTXT_SRC_EV_CH_IRQ_OFFS(gpii_id); + ev_ch_irq = gpi_read_reg(gpii, gpii->regs + offset); + + offset = GPII_n_CNTXT_SRC_EV_CH_IRQ_CLR_OFFS + (gpii_id); + gpi_write_reg(gpii, gpii->regs + offset, ev_ch_irq); + ev_state = gpi_read_reg(gpii, gpii->ev_cntxt_base_reg + + CNTXT_0_CONFIG); + ev_state = FIELD_GET(GPII_n_EV_k_CNTXT_0_CHSTATE, ev_state); + + /* + * CMD EV_CMD_DEALLOC is always successful. However + * cmd does not change hardware status. So overwriting + * software state to default state. + */ + if (gpii->gpi_cmd == GPI_EV_CMD_DEALLOC) + ev_state = DEFAULT_EV_CH_STATE; + + gpii->ev_state = ev_state; + dev_dbg(gpii->gpi_dev->dev, "setting EV state to %s\n", + TO_GPI_EV_STATE_STR(gpii->ev_state)); + complete_all(&gpii->cmd_completion); + type &= ~(GPII_n_CNTXT_TYPE_IRQ_MSK_EV_CTRL); + } + + /* channel control irq */ + if (type & GPII_n_CNTXT_TYPE_IRQ_MSK_CH_CTRL) { + dev_dbg(gpii->gpi_dev->dev, "process CH CTRL interrupts\n"); + gpi_process_ch_ctrl_irq(gpii); + type &= ~(GPII_n_CNTXT_TYPE_IRQ_MSK_CH_CTRL); + } + + if (type) { + dev_err(gpii->gpi_dev->dev, "Unhandled interrupt status:0x%x\n", type); + gpi_process_gen_err_irq(gpii); + goto exit_irq; + } + + offset = GPII_n_CNTXT_TYPE_IRQ_OFFS(gpii->gpii_id); + type = gpi_read_reg(gpii, gpii->regs + offset); + } while (type); + +exit_irq: + read_unlock_irqrestore(&gpii->pm_lock, flags); + + return IRQ_HANDLED; +} + +/* process DMA Immediate completion data events */ +static void gpi_process_imed_data_event(struct gchan *gchan, + struct immediate_data_event *imed_event) +{ + struct gpii *gpii = gchan->gpii; + struct gpi_ring *ch_ring = &gchan->ch_ring; + void *tre = ch_ring->base + (ch_ring->el_size * imed_event->tre_index); + struct dmaengine_result result; + struct gpi_desc *gpi_desc; + struct virt_dma_desc *vd; + unsigned long flags; + u32 chid; + + /* + * If channel not active don't process event + */ + if (gchan->pm_state != ACTIVE_STATE) { + dev_err(gpii->gpi_dev->dev, "skipping processing event because ch @ %s state\n", + TO_GPI_PM_STR(gchan->pm_state)); + return; + } + + spin_lock_irqsave(&gchan->vc.lock, flags); + vd = vchan_next_desc(&gchan->vc); + if (!vd) { + struct gpi_ere *gpi_ere; + struct gpi_tre *gpi_tre; + + spin_unlock_irqrestore(&gchan->vc.lock, flags); + dev_dbg(gpii->gpi_dev->dev, "event without a pending descriptor!\n"); + gpi_ere = (struct gpi_ere *)imed_event; + dev_dbg(gpii->gpi_dev->dev, + "Event: %08x %08x %08x %08x\n", + gpi_ere->dword[0], gpi_ere->dword[1], + gpi_ere->dword[2], gpi_ere->dword[3]); + gpi_tre = tre; + dev_dbg(gpii->gpi_dev->dev, + "Pending TRE: %08x %08x %08x %08x\n", + gpi_tre->dword[0], gpi_tre->dword[1], + gpi_tre->dword[2], gpi_tre->dword[3]); + return; + } + gpi_desc = to_gpi_desc(vd); + spin_unlock_irqrestore(&gchan->vc.lock, flags); + + /* + * RP pointed by Event is to last TRE processed, + * we need to update ring rp to tre + 1 + */ + tre += ch_ring->el_size; + if (tre >= (ch_ring->base + ch_ring->len)) + tre = ch_ring->base; + ch_ring->rp = tre; + + /* make sure rp updates are immediately visible to all cores */ + smp_wmb(); + + chid = imed_event->chid; + if (imed_event->code == MSM_GPI_TCE_EOT && gpii->ieob_set) { + if (chid == GPI_RX_CHAN) + goto gpi_free_desc; + else + return; + } + + if (imed_event->code == MSM_GPI_TCE_UNEXP_ERR) + result.result = DMA_TRANS_ABORTED; + else + result.result = DMA_TRANS_NOERROR; + result.residue = gpi_desc->len - imed_event->length; + + dma_cookie_complete(&vd->tx); + dmaengine_desc_get_callback_invoke(&vd->tx, &result); + +gpi_free_desc: + spin_lock_irqsave(&gchan->vc.lock, flags); + list_del(&vd->node); + spin_unlock_irqrestore(&gchan->vc.lock, flags); + kfree(gpi_desc); + gpi_desc = NULL; +} + +/* processing transfer completion events */ +static void gpi_process_xfer_compl_event(struct gchan *gchan, + struct xfer_compl_event *compl_event) +{ + struct gpii *gpii = gchan->gpii; + struct gpi_ring *ch_ring = &gchan->ch_ring; + void *ev_rp = to_virtual(ch_ring, compl_event->ptr); + struct virt_dma_desc *vd; + struct gpi_desc *gpi_desc; + struct dmaengine_result result; + unsigned long flags; + u32 chid; + + /* only process events on active channel */ + if (unlikely(gchan->pm_state != ACTIVE_STATE)) { + dev_err(gpii->gpi_dev->dev, "skipping processing event because ch @ %s state\n", + TO_GPI_PM_STR(gchan->pm_state)); + return; + } + + spin_lock_irqsave(&gchan->vc.lock, flags); + vd = vchan_next_desc(&gchan->vc); + if (!vd) { + struct gpi_ere *gpi_ere; + + spin_unlock_irqrestore(&gchan->vc.lock, flags); + dev_err(gpii->gpi_dev->dev, "Event without a pending descriptor!\n"); + gpi_ere = (struct gpi_ere *)compl_event; + dev_err(gpii->gpi_dev->dev, + "Event: %08x %08x %08x %08x\n", + gpi_ere->dword[0], gpi_ere->dword[1], + gpi_ere->dword[2], gpi_ere->dword[3]); + return; + } + + gpi_desc = to_gpi_desc(vd); + spin_unlock_irqrestore(&gchan->vc.lock, flags); + + /* + * RP pointed by Event is to last TRE processed, + * we need to update ring rp to ev_rp + 1 + */ + ev_rp += ch_ring->el_size; + if (ev_rp >= (ch_ring->base + ch_ring->len)) + ev_rp = ch_ring->base; + ch_ring->rp = ev_rp; + + /* update must be visible to other cores */ + smp_wmb(); + + chid = compl_event->chid; + if (compl_event->code == MSM_GPI_TCE_EOT && gpii->ieob_set) { + if (chid == GPI_RX_CHAN) + goto gpi_free_desc; + else + return; + } + + if (compl_event->code == MSM_GPI_TCE_UNEXP_ERR) + result.result = DMA_TRANS_ABORTED; + else + result.result = DMA_TRANS_NOERROR; + result.residue = gpi_desc->len - compl_event->length; + + dma_cookie_complete(&vd->tx); + dmaengine_desc_get_callback_invoke(&vd->tx, &result); + +gpi_free_desc: + spin_lock_irqsave(&gchan->vc.lock, flags); + list_del(&vd->node); + spin_unlock_irqrestore(&gchan->vc.lock, flags); + kfree(gpi_desc); + gpi_desc = NULL; +} + +/* process all events */ +static void gpi_process_events(struct gpii *gpii) +{ + struct gpi_ring *ev_ring = &gpii->ev_ring; + phys_addr_t cntxt_rp; + void *rp; + union gpi_event *gpi_event; + struct gchan *gchan; + u32 chid, type; + + cntxt_rp = gpi_read_reg(gpii, gpii->ev_ring_rp_lsb_reg); + rp = to_virtual(ev_ring, cntxt_rp); + + do { + while (rp != ev_ring->rp) { + gpi_event = ev_ring->rp; + chid = gpi_event->xfer_compl_event.chid; + type = gpi_event->xfer_compl_event.type; + + switch (type) { + case XFER_COMPLETE_EV_TYPE: + gchan = &gpii->gchan[chid]; + gpi_process_xfer_compl_event(gchan, + &gpi_event->xfer_compl_event); + break; + case STALE_EV_TYPE: + dev_dbg(gpii->gpi_dev->dev, "stale event, not processing\n"); + break; + case IMMEDIATE_DATA_EV_TYPE: + gchan = &gpii->gchan[chid]; + gpi_process_imed_data_event(gchan, + &gpi_event->immediate_data_event); + break; + case QUP_NOTIF_EV_TYPE: + dev_dbg(gpii->gpi_dev->dev, "QUP_NOTIF_EV_TYPE\n"); + break; + default: + dev_dbg(gpii->gpi_dev->dev, + "not supported event type:0x%x\n", type); + } + gpi_ring_recycle_ev_element(ev_ring); + } + gpi_write_ev_db(gpii, ev_ring, ev_ring->wp); + + /* clear pending IEOB events */ + gpi_write_reg(gpii, gpii->ieob_clr_reg, BIT(0)); + + cntxt_rp = gpi_read_reg(gpii, gpii->ev_ring_rp_lsb_reg); + rp = to_virtual(ev_ring, cntxt_rp); + + } while (rp != ev_ring->rp); +} + +/* processing events using tasklet */ +static void gpi_ev_tasklet(unsigned long data) +{ + struct gpii *gpii = (struct gpii *)data; + + read_lock_bh(&gpii->pm_lock); + if (!REG_ACCESS_VALID(gpii->pm_state)) { + read_unlock_bh(&gpii->pm_lock); + dev_err(gpii->gpi_dev->dev, "not processing any events, pm_state:%s\n", + TO_GPI_PM_STR(gpii->pm_state)); + return; + } + + /* process the events */ + gpi_process_events(gpii); + + /* enable IEOB, switching back to interrupts */ + gpi_config_interrupts(gpii, MASK_IEOB_SETTINGS, 1); + read_unlock_bh(&gpii->pm_lock); +} + +/* marks all pending events for the channel as stale */ +static void gpi_mark_stale_events(struct gchan *gchan) +{ + struct gpii *gpii = gchan->gpii; + struct gpi_ring *ev_ring = &gpii->ev_ring; + u32 cntxt_rp, local_rp; + void *ev_rp; + + cntxt_rp = gpi_read_reg(gpii, gpii->ev_ring_rp_lsb_reg); + + ev_rp = ev_ring->rp; + local_rp = (u32)to_physical(ev_ring, ev_rp); + while (local_rp != cntxt_rp) { + union gpi_event *gpi_event = ev_rp; + u32 chid = gpi_event->xfer_compl_event.chid; + + if (chid == gchan->chid) + gpi_event->xfer_compl_event.type = STALE_EV_TYPE; + ev_rp += ev_ring->el_size; + if (ev_rp >= (ev_ring->base + ev_ring->len)) + ev_rp = ev_ring->base; + cntxt_rp = gpi_read_reg(gpii, gpii->ev_ring_rp_lsb_reg); + local_rp = (u32)to_physical(ev_ring, ev_rp); + } +} + +/* reset sw state and issue channel reset or de-alloc */ +static int gpi_reset_chan(struct gchan *gchan, enum gpi_cmd gpi_cmd) +{ + struct gpii *gpii = gchan->gpii; + struct gpi_ring *ch_ring = &gchan->ch_ring; + unsigned long flags; + LIST_HEAD(list); + int ret; + + ret = gpi_send_cmd(gpii, gchan, gpi_cmd); + if (ret) { + dev_err(gpii->gpi_dev->dev, "Error with cmd:%s ret:%d\n", + TO_GPI_CMD_STR(gpi_cmd), ret); + return ret; + } + + /* initialize the local ring ptrs */ + ch_ring->rp = ch_ring->base; + ch_ring->wp = ch_ring->base; + + /* visible to other cores */ + smp_wmb(); + + /* check event ring for any stale events */ + write_lock_irq(&gpii->pm_lock); + gpi_mark_stale_events(gchan); + + /* remove all async descriptors */ + spin_lock_irqsave(&gchan->vc.lock, flags); + vchan_get_all_descriptors(&gchan->vc, &list); + spin_unlock_irqrestore(&gchan->vc.lock, flags); + write_unlock_irq(&gpii->pm_lock); + vchan_dma_desc_free_list(&gchan->vc, &list); + + return 0; +} + +static int gpi_start_chan(struct gchan *gchan) +{ + struct gpii *gpii = gchan->gpii; + int ret; + + ret = gpi_send_cmd(gpii, gchan, GPI_CH_CMD_START); + if (ret) { + dev_err(gpii->gpi_dev->dev, "Error with cmd:%s ret:%d\n", + TO_GPI_CMD_STR(GPI_CH_CMD_START), ret); + return ret; + } + + /* gpii CH is active now */ + write_lock_irq(&gpii->pm_lock); + gchan->pm_state = ACTIVE_STATE; + write_unlock_irq(&gpii->pm_lock); + + return 0; +} + +static int gpi_stop_chan(struct gchan *gchan) +{ + struct gpii *gpii = gchan->gpii; + int ret; + + ret = gpi_send_cmd(gpii, gchan, GPI_CH_CMD_STOP); + if (ret) { + dev_err(gpii->gpi_dev->dev, "Error with cmd:%s ret:%d\n", + TO_GPI_CMD_STR(GPI_CH_CMD_STOP), ret); + return ret; + } + + return 0; +} + +/* allocate and configure the transfer channel */ +static int gpi_alloc_chan(struct gchan *chan, bool send_alloc_cmd) +{ + struct gpii *gpii = chan->gpii; + struct gpi_ring *ring = &chan->ch_ring; + int ret; + u32 id = gpii->gpii_id; + u32 chid = chan->chid; + u32 pair_chid = !chid; + + if (send_alloc_cmd) { + ret = gpi_send_cmd(gpii, chan, GPI_CH_CMD_ALLOCATE); + if (ret) { + dev_err(gpii->gpi_dev->dev, "Error with cmd:%s ret:%d\n", + TO_GPI_CMD_STR(GPI_CH_CMD_ALLOCATE), ret); + return ret; + } + } + + gpi_write_reg(gpii, chan->ch_cntxt_base_reg + CNTXT_0_CONFIG, + GPII_n_CH_k_CNTXT_0(ring->el_size, 0, chan->dir, GPI_CHTYPE_PROTO_GPI)); + gpi_write_reg(gpii, chan->ch_cntxt_base_reg + CNTXT_1_R_LENGTH, ring->len); + gpi_write_reg(gpii, chan->ch_cntxt_base_reg + CNTXT_2_RING_BASE_LSB, ring->phys_addr); + gpi_write_reg(gpii, chan->ch_cntxt_base_reg + CNTXT_3_RING_BASE_MSB, + upper_32_bits(ring->phys_addr)); + gpi_write_reg(gpii, chan->ch_cntxt_db_reg + CNTXT_5_RING_RP_MSB - CNTXT_4_RING_RP_LSB, + upper_32_bits(ring->phys_addr)); + gpi_write_reg(gpii, gpii->regs + GPII_n_CH_k_SCRATCH_0_OFFS(id, chid), + GPII_n_CH_k_SCRATCH_0(pair_chid, chan->protocol, chan->seid)); + gpi_write_reg(gpii, gpii->regs + GPII_n_CH_k_SCRATCH_1_OFFS(id, chid), 0); + gpi_write_reg(gpii, gpii->regs + GPII_n_CH_k_SCRATCH_2_OFFS(id, chid), 0); + gpi_write_reg(gpii, gpii->regs + GPII_n_CH_k_SCRATCH_3_OFFS(id, chid), 0); + gpi_write_reg(gpii, gpii->regs + GPII_n_CH_k_QOS_OFFS(id, chid), 1); + + /* flush all the writes */ + wmb(); + return 0; +} + +/* allocate and configure event ring */ +static int gpi_alloc_ev_chan(struct gpii *gpii) +{ + struct gpi_ring *ring = &gpii->ev_ring; + void __iomem *base = gpii->ev_cntxt_base_reg; + int ret; + + ret = gpi_send_cmd(gpii, NULL, GPI_EV_CMD_ALLOCATE); + if (ret) { + dev_err(gpii->gpi_dev->dev, "error with cmd:%s ret:%d\n", + TO_GPI_CMD_STR(GPI_EV_CMD_ALLOCATE), ret); + return ret; + } + + /* program event context */ + gpi_write_reg(gpii, base + CNTXT_0_CONFIG, + GPII_n_EV_k_CNTXT_0(ring->el_size, GPI_INTTYPE_IRQ, GPI_CHTYPE_GPI_EV)); + gpi_write_reg(gpii, base + CNTXT_1_R_LENGTH, ring->len); + gpi_write_reg(gpii, base + CNTXT_2_RING_BASE_LSB, lower_32_bits(ring->phys_addr)); + gpi_write_reg(gpii, base + CNTXT_3_RING_BASE_MSB, upper_32_bits(ring->phys_addr)); + gpi_write_reg(gpii, gpii->ev_cntxt_db_reg + CNTXT_5_RING_RP_MSB - CNTXT_4_RING_RP_LSB, + upper_32_bits(ring->phys_addr)); + gpi_write_reg(gpii, base + CNTXT_8_RING_INT_MOD, 0); + gpi_write_reg(gpii, base + CNTXT_10_RING_MSI_LSB, 0); + gpi_write_reg(gpii, base + CNTXT_11_RING_MSI_MSB, 0); + gpi_write_reg(gpii, base + CNTXT_8_RING_INT_MOD, 0); + gpi_write_reg(gpii, base + CNTXT_12_RING_RP_UPDATE_LSB, 0); + gpi_write_reg(gpii, base + CNTXT_13_RING_RP_UPDATE_MSB, 0); + + /* add events to ring */ + ring->wp = (ring->base + ring->len - ring->el_size); + + /* flush all the writes */ + wmb(); + + /* gpii is active now */ + write_lock_irq(&gpii->pm_lock); + gpii->pm_state = ACTIVE_STATE; + write_unlock_irq(&gpii->pm_lock); + gpi_write_ev_db(gpii, ring, ring->wp); + + return 0; +} + +/* calculate # of ERE/TRE available to queue */ +static int gpi_ring_num_elements_avail(const struct gpi_ring * const ring) +{ + int elements = 0; + + if (ring->wp < ring->rp) { + elements = ((ring->rp - ring->wp) / ring->el_size) - 1; + } else { + elements = (ring->rp - ring->base) / ring->el_size; + elements += ((ring->base + ring->len - ring->wp) / ring->el_size) - 1; + } + + return elements; +} + +static int gpi_ring_add_element(struct gpi_ring *ring, void **wp) +{ + if (gpi_ring_num_elements_avail(ring) <= 0) + return -ENOMEM; + + *wp = ring->wp; + ring->wp += ring->el_size; + if (ring->wp >= (ring->base + ring->len)) + ring->wp = ring->base; + + /* visible to other cores */ + smp_wmb(); + + return 0; +} + +static void gpi_ring_recycle_ev_element(struct gpi_ring *ring) +{ + /* Update the WP */ + ring->wp += ring->el_size; + if (ring->wp >= (ring->base + ring->len)) + ring->wp = ring->base; + + /* Update the RP */ + ring->rp += ring->el_size; + if (ring->rp >= (ring->base + ring->len)) + ring->rp = ring->base; + + /* visible to other cores */ + smp_wmb(); +} + +static void gpi_free_ring(struct gpi_ring *ring, + struct gpii *gpii) +{ + dma_free_coherent(gpii->gpi_dev->dev, ring->alloc_size, + ring->pre_aligned, ring->dma_handle); + memset(ring, 0, sizeof(*ring)); +} + +/* allocate memory for transfer and event rings */ +static int gpi_alloc_ring(struct gpi_ring *ring, u32 elements, + u32 el_size, struct gpii *gpii) +{ + u64 len = elements * el_size; + int bit; + + /* ring len must be power of 2 */ + bit = find_last_bit((unsigned long *)&len, 32); + if (((1 << bit) - 1) & len) + bit++; + len = 1 << bit; + ring->alloc_size = (len + (len - 1)); + dev_dbg(gpii->gpi_dev->dev, + "#el:%u el_size:%u len:%u actual_len:%llu alloc_size:%lu\n", + elements, el_size, (elements * el_size), len, + ring->alloc_size); + + ring->pre_aligned = dma_alloc_coherent(gpii->gpi_dev->dev, + ring->alloc_size, + &ring->dma_handle, GFP_KERNEL); + if (!ring->pre_aligned) { + dev_err(gpii->gpi_dev->dev, "could not alloc size:%lu mem for ring\n", + ring->alloc_size); + return -ENOMEM; + } + + /* align the physical mem */ + ring->phys_addr = (ring->dma_handle + (len - 1)) & ~(len - 1); + ring->base = ring->pre_aligned + (ring->phys_addr - ring->dma_handle); + ring->rp = ring->base; + ring->wp = ring->base; + ring->len = len; + ring->el_size = el_size; + ring->elements = ring->len / ring->el_size; + memset(ring->base, 0, ring->len); + ring->configured = true; + + /* update to other cores */ + smp_wmb(); + + dev_dbg(gpii->gpi_dev->dev, + "phy_pre:0x%0llx phy_alig:0x%0llx len:%u el_size:%u elements:%u\n", + ring->dma_handle, ring->phys_addr, ring->len, + ring->el_size, ring->elements); + + return 0; +} + +/* copy tre into transfer ring */ +static void gpi_queue_xfer(struct gpii *gpii, struct gchan *gchan, + struct gpi_tre *gpi_tre, void **wp) +{ + struct gpi_tre *ch_tre; + int ret; + + /* get next tre location we can copy */ + ret = gpi_ring_add_element(&gchan->ch_ring, (void **)&ch_tre); + if (unlikely(ret)) { + dev_err(gpii->gpi_dev->dev, "Error adding ring element to xfer ring\n"); + return; + } + + /* copy the tre info */ + memcpy(ch_tre, gpi_tre, sizeof(*ch_tre)); + *wp = ch_tre; +} + +/* reset and restart transfer channel */ +static int gpi_terminate_all(struct dma_chan *chan) +{ + struct gchan *gchan = to_gchan(chan); + struct gpii *gpii = gchan->gpii; + int schid, echid, i; + int ret = 0; + + mutex_lock(&gpii->ctrl_lock); + + /* + * treat both channels as a group if its protocol is not UART + * STOP, RESET, or START needs to be in lockstep + */ + schid = (gchan->protocol == QCOM_GPI_UART) ? gchan->chid : 0; + echid = (gchan->protocol == QCOM_GPI_UART) ? schid + 1 : MAX_CHANNELS_PER_GPII; + + /* stop the channel */ + for (i = schid; i < echid; i++) { + gchan = &gpii->gchan[i]; + + /* disable ch state so no more TRE processing */ + write_lock_irq(&gpii->pm_lock); + gchan->pm_state = PREPARE_TERMINATE; + write_unlock_irq(&gpii->pm_lock); + + /* send command to Stop the channel */ + ret = gpi_stop_chan(gchan); + } + + /* reset the channels (clears any pending tre) */ + for (i = schid; i < echid; i++) { + gchan = &gpii->gchan[i]; + + ret = gpi_reset_chan(gchan, GPI_CH_CMD_RESET); + if (ret) { + dev_err(gpii->gpi_dev->dev, "Error resetting channel ret:%d\n", ret); + goto terminate_exit; + } + + /* reprogram channel CNTXT */ + ret = gpi_alloc_chan(gchan, false); + if (ret) { + dev_err(gpii->gpi_dev->dev, "Error alloc_channel ret:%d\n", ret); + goto terminate_exit; + } + } + + /* restart the channels */ + for (i = schid; i < echid; i++) { + gchan = &gpii->gchan[i]; + + ret = gpi_start_chan(gchan); + if (ret) { + dev_err(gpii->gpi_dev->dev, "Error Starting Channel ret:%d\n", ret); + goto terminate_exit; + } + } + +terminate_exit: + mutex_unlock(&gpii->ctrl_lock); + return ret; +} + +/* pause dma transfer for all channels */ +static int gpi_pause(struct dma_chan *chan) +{ + struct gchan *gchan = to_gchan(chan); + struct gpii *gpii = gchan->gpii; + int i, ret; + + mutex_lock(&gpii->ctrl_lock); + + /* + * pause/resume are per gpii not per channel, so + * client needs to call pause only once + */ + if (gpii->pm_state == PAUSE_STATE) { + dev_dbg(gpii->gpi_dev->dev, "channel is already paused\n"); + mutex_unlock(&gpii->ctrl_lock); + return 0; + } + + /* send stop command to stop the channels */ + for (i = 0; i < MAX_CHANNELS_PER_GPII; i++) { + ret = gpi_stop_chan(&gpii->gchan[i]); + if (ret) { + mutex_unlock(&gpii->ctrl_lock); + return ret; + } + } + + disable_irq(gpii->irq); + + /* Wait for threads to complete out */ + tasklet_kill(&gpii->ev_task); + + write_lock_irq(&gpii->pm_lock); + gpii->pm_state = PAUSE_STATE; + write_unlock_irq(&gpii->pm_lock); + mutex_unlock(&gpii->ctrl_lock); + + return 0; +} + +/* resume dma transfer */ +static int gpi_resume(struct dma_chan *chan) +{ + struct gchan *gchan = to_gchan(chan); + struct gpii *gpii = gchan->gpii; + int i, ret; + + mutex_lock(&gpii->ctrl_lock); + if (gpii->pm_state == ACTIVE_STATE) { + dev_dbg(gpii->gpi_dev->dev, "channel is already active\n"); + mutex_unlock(&gpii->ctrl_lock); + return 0; + } + + enable_irq(gpii->irq); + + /* send start command to start the channels */ + for (i = 0; i < MAX_CHANNELS_PER_GPII; i++) { + ret = gpi_send_cmd(gpii, &gpii->gchan[i], GPI_CH_CMD_START); + if (ret) { + dev_err(gpii->gpi_dev->dev, "Error starting chan, ret:%d\n", ret); + mutex_unlock(&gpii->ctrl_lock); + return ret; + } + } + + write_lock_irq(&gpii->pm_lock); + gpii->pm_state = ACTIVE_STATE; + write_unlock_irq(&gpii->pm_lock); + mutex_unlock(&gpii->ctrl_lock); + + return 0; +} + +static void gpi_desc_free(struct virt_dma_desc *vd) +{ + struct gpi_desc *gpi_desc = to_gpi_desc(vd); + + kfree(gpi_desc); + gpi_desc = NULL; +} + +static int +gpi_peripheral_config(struct dma_chan *chan, struct dma_slave_config *config) +{ + struct gchan *gchan = to_gchan(chan); + + if (!config->peripheral) + return -EINVAL; + + memcpy(&gchan->config, config->peripheral, sizeof(gchan->config)); + + return 0; +} + +static int gpi_create_i2c_tre(struct gchan *chan, struct gpi_desc *desc, + struct scatterlist *sgl, enum dma_transfer_direction direction) +{ + struct dmaengine_i2c_config *i2c = &chan->config.i2c; + struct device *dev = chan->gpii->gpi_dev->dev; + unsigned int tre_idx = 0; + dma_addr_t address; + struct gpi_tre *tre; + unsigned int i; + + /* first create config tre if applicable */ + if (chan->config.set_config) { + tre = &desc->tre[tre_idx]; + tre_idx++; + + tre->dword[0] = u32_encode_bits(i2c->low_count, TRE_I2C_C0_TLOW); + tre->dword[0] |= u32_encode_bits(i2c->high_count, TRE_I2C_C0_THIGH); + tre->dword[0] |= u32_encode_bits(i2c->cycle_count, TRE_I2C_C0_TCYL); + tre->dword[0] |= u32_encode_bits(i2c->pack_enable, TRE_I2C_C0_TX_PACK); + tre->dword[0] |= u32_encode_bits(i2c->pack_enable, TRE_I2C_C0_RX_PACK); + + tre->dword[1] = 0; + + tre->dword[2] = u32_encode_bits(i2c->clk_div, TRE_C0_CLK_DIV); + + tre->dword[3] = u32_encode_bits(TRE_TYPE_CONFIG0, TRE_FLAGS_TYPE); + tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN); + } + + /* create GO TRE */ + tre = &desc->tre[tre_idx]; + tre_idx++; + tre->dword[0] = u32_encode_bits(i2c->op, TRE_I2C_GO_CMD); + tre->dword[0] |= u32_encode_bits(i2c->addr, TRE_I2C_GO_ADDR); + tre->dword[0] |= u32_encode_bits(i2c->stretch, TRE_I2C_GO_STRETCH); + + tre->dword[1] = 0; + + tre->dword[3] = u32_encode_bits(TRE_TYPE_GO, TRE_FLAGS_TYPE); + + if (i2c->op & I2C_READ) { + tre->dword[2] = u32_encode_bits(chan->config.rx_len, TRE_RX_LEN); + + tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_LINK); + } else { + tre->dword[2] = 0; + + tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN); + } + + /* create the DMA TRE */ + tre = &desc->tre[tre_idx]; + tre_idx++; + + address = sg_dma_address(sgl); + tre->dword[0] = lower_32_bits(address); + tre->dword[1] = upper_32_bits(address); + + tre->dword[2] = u32_encode_bits(sg_dma_len(sgl), TRE_DMA_LEN); + + tre->dword[3] = u32_encode_bits(TRE_TYPE_DMA, TRE_FLAGS_TYPE); + tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOT); + + for (i = 0; i < tre_idx; i++) + dev_dbg(dev, "TRE:%d %x:%x:%x:%x\n", i, desc->tre[i].dword[0], + desc->tre[i].dword[1], desc->tre[i].dword[2], desc->tre[i].dword[3]); + + return tre_idx; +} + +static int gpi_create_spi_tre(struct gchan *chan, struct gpi_desc *desc, + struct scatterlist *sgl, enum dma_transfer_direction direction) +{ + struct dmaengine_spi_config *spi = &chan->config.spi; + struct device *dev = chan->gpii->gpi_dev->dev; + unsigned int tre_idx = 0; + dma_addr_t address; + struct gpi_tre *tre; + unsigned int i; + + /* first create config tre if applicable */ + if (direction == DMA_MEM_TO_DEV && chan->config.set_config) { + tre = &desc->tre[tre_idx]; + tre_idx++; + + tre->dword[0] = u32_encode_bits(spi->word_len, TRE_SPI_C0_WORD_SZ); + tre->dword[0] |= u32_encode_bits(spi->loopback_en, TRE_SPI_C0_LOOPBACK); + tre->dword[0] |= u32_encode_bits(spi->clock_pol, TRE_SPI_C0_CPOL); + tre->dword[0] |= u32_encode_bits(spi->data_pol, TRE_SPI_C0_CPHA); + tre->dword[0] |= u32_encode_bits(spi->pack_en, TRE_SPI_C0_TX_PACK); + tre->dword[0] |= u32_encode_bits(spi->pack_en, TRE_SPI_C0_RX_PACK); + + tre->dword[1] = 0; + + tre->dword[2] = u32_encode_bits(spi->clk_div, TRE_C0_CLK_DIV); + tre->dword[2] |= u32_encode_bits(spi->clk_src, TRE_C0_CLK_SRC); + + tre->dword[3] = u32_encode_bits(TRE_TYPE_CONFIG0, TRE_FLAGS_TYPE); + tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN); + } + + /* create the GO tre for Tx */ + if (direction == DMA_MEM_TO_DEV) { + tre = &desc->tre[tre_idx]; + tre_idx++; + + tre->dword[0] = u32_encode_bits(spi->fragmentation, TRE_SPI_GO_FRAG); + tre->dword[0] |= u32_encode_bits(spi->cs, TRE_SPI_GO_CS); + tre->dword[0] |= u32_encode_bits(spi->cmd, TRE_SPI_GO_CMD); + + tre->dword[1] = 0; + + tre->dword[2] = u32_encode_bits(chan->config.rx_len, TRE_RX_LEN); + + tre->dword[3] = u32_encode_bits(TRE_TYPE_GO, TRE_FLAGS_TYPE); + if (spi->cmd == SPI_RX) + tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOB); + else + tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN); + } + + /* create the dma tre */ + tre = &desc->tre[tre_idx]; + tre_idx++; + + address = sg_dma_address(sgl); + tre->dword[0] = lower_32_bits(address); + tre->dword[1] = upper_32_bits(address); + + tre->dword[2] = u32_encode_bits(sg_dma_len(sgl), TRE_DMA_LEN); + + tre->dword[3] = u32_encode_bits(TRE_TYPE_DMA, TRE_FLAGS_TYPE); + if (direction == DMA_MEM_TO_DEV) + tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOT); + + for (i = 0; i < tre_idx; i++) + dev_dbg(dev, "TRE:%d %x:%x:%x:%x\n", i, desc->tre[i].dword[0], + desc->tre[i].dword[1], desc->tre[i].dword[2], desc->tre[i].dword[3]); + + return tre_idx; +} + +/* copy tre into transfer ring */ +static struct dma_async_tx_descriptor * +gpi_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct gchan *gchan = to_gchan(chan); + struct gpii *gpii = gchan->gpii; + struct device *dev = gpii->gpi_dev->dev; + struct gpi_ring *ch_ring = &gchan->ch_ring; + struct gpi_desc *gpi_desc; + u32 nr, nr_tre = 0; + int i; + + gpii->ieob_set = false; + if (!is_slave_direction(direction)) { + dev_err(gpii->gpi_dev->dev, "invalid dma direction: %d\n", direction); + return NULL; + } + + if (sg_len > 1) { + dev_err(dev, "Multi sg sent, we support only one atm: %d\n", sg_len); + return NULL; + } + + nr_tre = 3; + if (!gchan->config.set_config) + nr_tre = 2; + if (direction == DMA_DEV_TO_MEM) /* rx */ + nr_tre = 1; + + /* calculate # of elements required & available */ + nr = gpi_ring_num_elements_avail(ch_ring); + if (nr < nr_tre) { + dev_err(dev, "not enough space in ring, avail:%u required:%u\n", nr, nr_tre); + return NULL; + } + + gpi_desc = kzalloc(sizeof(*gpi_desc), GFP_NOWAIT); + if (!gpi_desc) + return NULL; + + /* create TREs for xfer */ + if (gchan->config.peripheral == DMAENGINE_PERIPHERAL_SPI) { + i = gpi_create_spi_tre(gchan, gpi_desc, sgl, direction); + } else if (gchan->config.peripheral == DMAENGINE_PERIPHERAL_I2C) { + i = gpi_create_i2c_tre(gchan, gpi_desc, sgl, direction); + } else { + dev_err(dev, "invalid peripheral: %d\n", gchan->config.peripheral); + kfree(gpi_desc); + return NULL; + } + + /* set up the descriptor */ + gpi_desc->gchan = gchan; + gpi_desc->len = sg_dma_len(sgl); + gpi_desc->num_tre = i; + + return vchan_tx_prep(&gchan->vc, &gpi_desc->vd, flags); +} + +/* rings transfer ring db to being transfer */ +static void gpi_issue_pending(struct dma_chan *chan) +{ + struct gchan *gchan = to_gchan(chan); + struct gpii *gpii = gchan->gpii; + unsigned long flags, pm_lock_flags; + struct virt_dma_desc *vd = NULL; + struct gpi_desc *gpi_desc; + struct gpi_ring *ch_ring = &gchan->ch_ring; + void *tre, *wp = NULL; + int i; + + read_lock_irqsave(&gpii->pm_lock, pm_lock_flags); + + /* move all submitted discriptors to issued list */ + spin_lock_irqsave(&gchan->vc.lock, flags); + if (vchan_issue_pending(&gchan->vc)) + vd = list_last_entry(&gchan->vc.desc_issued, + struct virt_dma_desc, node); + spin_unlock_irqrestore(&gchan->vc.lock, flags); + + /* nothing to do list is empty */ + if (!vd) { + read_unlock_irqrestore(&gpii->pm_lock, pm_lock_flags); + return; + } + + gpi_desc = to_gpi_desc(vd); + for (i = 0; i < gpi_desc->num_tre; i++) { + tre = &gpi_desc->tre[i]; + gpi_queue_xfer(gpii, gchan, tre, &wp); + } + + gpi_desc->db = ch_ring->wp; + gpi_write_ch_db(gchan, &gchan->ch_ring, gpi_desc->db); + read_unlock_irqrestore(&gpii->pm_lock, pm_lock_flags); +} + +static int gpi_ch_init(struct gchan *gchan) +{ + struct gpii *gpii = gchan->gpii; + const int ev_factor = gpii->gpi_dev->ev_factor; + u32 elements; + int i = 0, ret = 0; + + gchan->pm_state = CONFIG_STATE; + + /* check if both channels are configured before continue */ + for (i = 0; i < MAX_CHANNELS_PER_GPII; i++) + if (gpii->gchan[i].pm_state != CONFIG_STATE) + goto exit_gpi_init; + + /* protocol must be same for both channels */ + if (gpii->gchan[0].protocol != gpii->gchan[1].protocol) { + dev_err(gpii->gpi_dev->dev, "protocol did not match protocol %u != %u\n", + gpii->gchan[0].protocol, gpii->gchan[1].protocol); + ret = -EINVAL; + goto exit_gpi_init; + } + + /* allocate memory for event ring */ + elements = CHAN_TRES << ev_factor; + ret = gpi_alloc_ring(&gpii->ev_ring, elements, + sizeof(union gpi_event), gpii); + if (ret) + goto exit_gpi_init; + + /* configure interrupts */ + write_lock_irq(&gpii->pm_lock); + gpii->pm_state = PREPARE_HARDWARE; + write_unlock_irq(&gpii->pm_lock); + ret = gpi_config_interrupts(gpii, DEFAULT_IRQ_SETTINGS, 0); + if (ret) { + dev_err(gpii->gpi_dev->dev, "error config. interrupts, ret:%d\n", ret); + goto error_config_int; + } + + /* allocate event rings */ + ret = gpi_alloc_ev_chan(gpii); + if (ret) { + dev_err(gpii->gpi_dev->dev, "error alloc_ev_chan:%d\n", ret); + goto error_alloc_ev_ring; + } + + /* Allocate all channels */ + for (i = 0; i < MAX_CHANNELS_PER_GPII; i++) { + ret = gpi_alloc_chan(&gpii->gchan[i], true); + if (ret) { + dev_err(gpii->gpi_dev->dev, "Error allocating chan:%d\n", ret); + goto error_alloc_chan; + } + } + + /* start channels */ + for (i = 0; i < MAX_CHANNELS_PER_GPII; i++) { + ret = gpi_start_chan(&gpii->gchan[i]); + if (ret) { + dev_err(gpii->gpi_dev->dev, "Error start chan:%d\n", ret); + goto error_start_chan; + } + } + return ret; + +error_start_chan: + for (i = i - 1; i >= 0; i++) { + gpi_stop_chan(&gpii->gchan[i]); + gpi_send_cmd(gpii, gchan, GPI_CH_CMD_RESET); + } + i = 2; +error_alloc_chan: + for (i = i - 1; i >= 0; i--) + gpi_reset_chan(gchan, GPI_CH_CMD_DE_ALLOC); +error_alloc_ev_ring: + gpi_disable_interrupts(gpii); +error_config_int: + gpi_free_ring(&gpii->ev_ring, gpii); +exit_gpi_init: + mutex_unlock(&gpii->ctrl_lock); + return ret; +} + +/* release all channel resources */ +static void gpi_free_chan_resources(struct dma_chan *chan) +{ + struct gchan *gchan = to_gchan(chan); + struct gpii *gpii = gchan->gpii; + enum gpi_pm_state cur_state; + int ret, i; + + mutex_lock(&gpii->ctrl_lock); + + cur_state = gchan->pm_state; + + /* disable ch state so no more TRE processing for this channel */ + write_lock_irq(&gpii->pm_lock); + gchan->pm_state = PREPARE_TERMINATE; + write_unlock_irq(&gpii->pm_lock); + + /* attempt to do graceful hardware shutdown */ + if (cur_state == ACTIVE_STATE) { + gpi_stop_chan(gchan); + + ret = gpi_send_cmd(gpii, gchan, GPI_CH_CMD_RESET); + if (ret) + dev_err(gpii->gpi_dev->dev, "error resetting channel:%d\n", ret); + + gpi_reset_chan(gchan, GPI_CH_CMD_DE_ALLOC); + } + + /* free all allocated memory */ + gpi_free_ring(&gchan->ch_ring, gpii); + vchan_free_chan_resources(&gchan->vc); + + write_lock_irq(&gpii->pm_lock); + gchan->pm_state = DISABLE_STATE; + write_unlock_irq(&gpii->pm_lock); + + /* if other rings are still active exit */ + for (i = 0; i < MAX_CHANNELS_PER_GPII; i++) + if (gpii->gchan[i].ch_ring.configured) + goto exit_free; + + /* deallocate EV Ring */ + cur_state = gpii->pm_state; + write_lock_irq(&gpii->pm_lock); + gpii->pm_state = PREPARE_TERMINATE; + write_unlock_irq(&gpii->pm_lock); + + /* wait for threads to complete out */ + tasklet_kill(&gpii->ev_task); + + /* send command to de allocate event ring */ + if (cur_state == ACTIVE_STATE) + gpi_send_cmd(gpii, NULL, GPI_EV_CMD_DEALLOC); + + gpi_free_ring(&gpii->ev_ring, gpii); + + /* disable interrupts */ + if (cur_state == ACTIVE_STATE) + gpi_disable_interrupts(gpii); + + /* set final state to disable */ + write_lock_irq(&gpii->pm_lock); + gpii->pm_state = DISABLE_STATE; + write_unlock_irq(&gpii->pm_lock); + +exit_free: + mutex_unlock(&gpii->ctrl_lock); +} + +/* allocate channel resources */ +static int gpi_alloc_chan_resources(struct dma_chan *chan) +{ + struct gchan *gchan = to_gchan(chan); + struct gpii *gpii = gchan->gpii; + int ret; + + mutex_lock(&gpii->ctrl_lock); + + /* allocate memory for transfer ring */ + ret = gpi_alloc_ring(&gchan->ch_ring, CHAN_TRES, + sizeof(struct gpi_tre), gpii); + if (ret) + goto xfer_alloc_err; + + ret = gpi_ch_init(gchan); + + mutex_unlock(&gpii->ctrl_lock); + + return ret; +xfer_alloc_err: + mutex_unlock(&gpii->ctrl_lock); + + return ret; +} + +static int gpi_find_avail_gpii(struct gpi_dev *gpi_dev, u32 seid) +{ + struct gchan *tx_chan, *rx_chan; + unsigned int gpii; + + /* check if same seid is already configured for another chid */ + for (gpii = 0; gpii < gpi_dev->max_gpii; gpii++) { + if (!((1 << gpii) & gpi_dev->gpii_mask)) + continue; + + tx_chan = &gpi_dev->gpiis[gpii].gchan[GPI_TX_CHAN]; + rx_chan = &gpi_dev->gpiis[gpii].gchan[GPI_RX_CHAN]; + + if (rx_chan->vc.chan.client_count && rx_chan->seid == seid) + return gpii; + if (tx_chan->vc.chan.client_count && tx_chan->seid == seid) + return gpii; + } + + /* no channels configured with same seid, return next avail gpii */ + for (gpii = 0; gpii < gpi_dev->max_gpii; gpii++) { + if (!((1 << gpii) & gpi_dev->gpii_mask)) + continue; + + tx_chan = &gpi_dev->gpiis[gpii].gchan[GPI_TX_CHAN]; + rx_chan = &gpi_dev->gpiis[gpii].gchan[GPI_RX_CHAN]; + + /* check if gpii is configured */ + if (tx_chan->vc.chan.client_count || + rx_chan->vc.chan.client_count) + continue; + + /* found a free gpii */ + return gpii; + } + + /* no gpii instance available to use */ + return -EIO; +} + +/* gpi_of_dma_xlate: open client requested channel */ +static struct dma_chan *gpi_of_dma_xlate(struct of_phandle_args *args, + struct of_dma *of_dma) +{ + struct gpi_dev *gpi_dev = (struct gpi_dev *)of_dma->of_dma_data; + u32 seid, chid; + int gpii; + struct gchan *gchan; + + if (args->args_count < 3) { + dev_err(gpi_dev->dev, "gpii require minimum 2 args, client passed:%d args\n", + args->args_count); + return NULL; + } + + chid = args->args[0]; + if (chid >= MAX_CHANNELS_PER_GPII) { + dev_err(gpi_dev->dev, "gpii channel:%d not valid\n", chid); + return NULL; + } + + seid = args->args[1]; + + /* find next available gpii to use */ + gpii = gpi_find_avail_gpii(gpi_dev, seid); + if (gpii < 0) { + dev_err(gpi_dev->dev, "no available gpii instances\n"); + return NULL; + } + + gchan = &gpi_dev->gpiis[gpii].gchan[chid]; + if (gchan->vc.chan.client_count) { + dev_err(gpi_dev->dev, "gpii:%d chid:%d seid:%d already configured\n", + gpii, chid, gchan->seid); + return NULL; + } + + gchan->seid = seid; + gchan->protocol = args->args[2]; + + return dma_get_slave_channel(&gchan->vc.chan); +} + +static int gpi_probe(struct platform_device *pdev) +{ + struct gpi_dev *gpi_dev; + unsigned int i; + int ret; + + gpi_dev = devm_kzalloc(&pdev->dev, sizeof(*gpi_dev), GFP_KERNEL); + if (!gpi_dev) + return -ENOMEM; + + gpi_dev->dev = &pdev->dev; + gpi_dev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + gpi_dev->regs = devm_ioremap_resource(gpi_dev->dev, gpi_dev->res); + if (IS_ERR(gpi_dev->regs)) + return PTR_ERR(gpi_dev->regs); + gpi_dev->ee_base = gpi_dev->regs; + + ret = of_property_read_u32(gpi_dev->dev->of_node, "dma-channels", + &gpi_dev->max_gpii); + if (ret) { + dev_err(gpi_dev->dev, "missing 'max-no-gpii' DT node\n"); + return ret; + } + + ret = of_property_read_u32(gpi_dev->dev->of_node, "dma-channel-mask", + &gpi_dev->gpii_mask); + if (ret) { + dev_err(gpi_dev->dev, "missing 'gpii-mask' DT node\n"); + return ret; + } + + gpi_dev->ev_factor = EV_FACTOR; + + ret = dma_set_mask(gpi_dev->dev, DMA_BIT_MASK(64)); + if (ret) { + dev_err(gpi_dev->dev, "Error setting dma_mask to 64, ret:%d\n", ret); + return ret; + } + + gpi_dev->gpiis = devm_kzalloc(gpi_dev->dev, sizeof(*gpi_dev->gpiis) * + gpi_dev->max_gpii, GFP_KERNEL); + if (!gpi_dev->gpiis) + return -ENOMEM; + + /* setup all the supported gpii */ + INIT_LIST_HEAD(&gpi_dev->dma_device.channels); + for (i = 0; i < gpi_dev->max_gpii; i++) { + struct gpii *gpii = &gpi_dev->gpiis[i]; + int chan; + + if (!((1 << i) & gpi_dev->gpii_mask)) + continue; + + /* set up ev cntxt register map */ + gpii->ev_cntxt_base_reg = gpi_dev->ee_base + GPII_n_EV_CH_k_CNTXT_0_OFFS(i, 0); + gpii->ev_cntxt_db_reg = gpi_dev->ee_base + GPII_n_EV_CH_k_DOORBELL_0_OFFS(i, 0); + gpii->ev_ring_rp_lsb_reg = gpii->ev_cntxt_base_reg + CNTXT_4_RING_RP_LSB; + gpii->ev_cmd_reg = gpi_dev->ee_base + GPII_n_EV_CH_CMD_OFFS(i); + gpii->ieob_clr_reg = gpi_dev->ee_base + GPII_n_CNTXT_SRC_IEOB_IRQ_CLR_OFFS(i); + + /* set up irq */ + ret = platform_get_irq(pdev, i); + if (ret < 0) { + dev_err(gpi_dev->dev, "platform_get_irq failed for %d:%d\n", i, ret); + return ret; + } + gpii->irq = ret; + + /* set up channel specific register info */ + for (chan = 0; chan < MAX_CHANNELS_PER_GPII; chan++) { + struct gchan *gchan = &gpii->gchan[chan]; + + /* set up ch cntxt register map */ + gchan->ch_cntxt_base_reg = gpi_dev->ee_base + + GPII_n_CH_k_CNTXT_0_OFFS(i, chan); + gchan->ch_cntxt_db_reg = gpi_dev->ee_base + + GPII_n_CH_k_DOORBELL_0_OFFS(i, chan); + gchan->ch_cmd_reg = gpi_dev->ee_base + GPII_n_CH_CMD_OFFS(i); + + /* vchan setup */ + vchan_init(&gchan->vc, &gpi_dev->dma_device); + gchan->vc.desc_free = gpi_desc_free; + gchan->chid = chan; + gchan->gpii = gpii; + gchan->dir = GPII_CHAN_DIR[chan]; + } + mutex_init(&gpii->ctrl_lock); + rwlock_init(&gpii->pm_lock); + tasklet_init(&gpii->ev_task, gpi_ev_tasklet, + (unsigned long)gpii); + init_completion(&gpii->cmd_completion); + gpii->gpii_id = i; + gpii->regs = gpi_dev->ee_base; + gpii->gpi_dev = gpi_dev; + } + + platform_set_drvdata(pdev, gpi_dev); + + /* clear and Set capabilities */ + dma_cap_zero(gpi_dev->dma_device.cap_mask); + dma_cap_set(DMA_SLAVE, gpi_dev->dma_device.cap_mask); + + /* configure dmaengine apis */ + gpi_dev->dma_device.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + gpi_dev->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; + gpi_dev->dma_device.src_addr_widths = DMA_SLAVE_BUSWIDTH_8_BYTES; + gpi_dev->dma_device.dst_addr_widths = DMA_SLAVE_BUSWIDTH_8_BYTES; + gpi_dev->dma_device.device_alloc_chan_resources = gpi_alloc_chan_resources; + gpi_dev->dma_device.device_free_chan_resources = gpi_free_chan_resources; + gpi_dev->dma_device.device_tx_status = dma_cookie_status; + gpi_dev->dma_device.device_issue_pending = gpi_issue_pending; + gpi_dev->dma_device.device_prep_slave_sg = gpi_prep_slave_sg; + gpi_dev->dma_device.device_config = gpi_peripheral_config; + gpi_dev->dma_device.device_terminate_all = gpi_terminate_all; + gpi_dev->dma_device.dev = gpi_dev->dev; + gpi_dev->dma_device.device_pause = gpi_pause; + gpi_dev->dma_device.device_resume = gpi_resume; + + /* register with dmaengine framework */ + ret = dma_async_device_register(&gpi_dev->dma_device); + if (ret) { + dev_err(gpi_dev->dev, "async_device_register failed ret:%d", ret); + return ret; + } + + ret = of_dma_controller_register(gpi_dev->dev->of_node, + gpi_of_dma_xlate, gpi_dev); + if (ret) { + dev_err(gpi_dev->dev, "of_dma_controller_reg failed ret:%d", ret); + return ret; + } + + return ret; +} + +static const struct of_device_id gpi_of_match[] = { + { .compatible = "qcom,gpi-dma" }, + { }, +}; +MODULE_DEVICE_TABLE(of, gpi_of_match); + +static struct platform_driver gpi_driver = { + .probe = gpi_probe, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = gpi_of_match, + }, +}; + +static int __init gpi_init(void) +{ + return platform_driver_register(&gpi_driver); +} +subsys_initcall(gpi_init) + +MODULE_DESCRIPTION("QCOM GPI DMA engine driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index e8bbf2d38ae7..5b19f1301c1d 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -863,6 +863,30 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, } EXPORT_SYMBOL(qcom_scm_assign_mem); +int qcom_scm_mem_protect_video_var(u32 cp_start, u32 cp_size, + u32 cp_nonpixel_start, + u32 cp_nonpixel_size) +{ + int ret; + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_MP, + .cmd = QCOM_SCM_MP_VIDEO_VAR, + .arginfo = QCOM_SCM_ARGS(4, QCOM_SCM_VAL, QCOM_SCM_VAL, + QCOM_SCM_VAL, QCOM_SCM_VAL), + .args[0] = cp_start, + .args[1] = cp_size, + .args[2] = cp_nonpixel_start, + .args[3] = cp_nonpixel_size, + .owner = ARM_SMCCC_OWNER_SIP, + }; + struct qcom_scm_res res; + + ret = qcom_scm_call(__scm->dev, &desc, &res); + + return ret ? : res.result[0]; +} +EXPORT_SYMBOL(qcom_scm_mem_protect_video_var); + /** * qcom_scm_ocmem_lock_available() - is OCMEM lock/unlock interface available */ diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 38ea614d29fe..95cd1ac30ab0 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -97,6 +97,7 @@ extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, #define QCOM_SCM_MP_RESTORE_SEC_CFG 0x02 #define QCOM_SCM_MP_IOMMU_SECURE_PTBL_SIZE 0x03 #define QCOM_SCM_MP_IOMMU_SECURE_PTBL_INIT 0x04 +#define QCOM_SCM_MP_VIDEO_VAR 0x08 #define QCOM_SCM_MP_ASSIGN 0x16 #define QCOM_SCM_SVC_OCMEM 0x0f diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index c7f0dacfb57a..cb405a29ef0d 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -219,6 +219,19 @@ config DRM_TI_TPD12S015 Texas Instruments TPD12S015 HDMI level shifter and ESD protection driver. +config DRM_LONTIUM_LT9611UXC + tristate "Lontium LT9611UXC DSI/HDMI bridge" + select SND_SOC_HDMI_CODEC if SND_SOC + depends on OF + select DRM_PANEL_BRIDGE + select DRM_KMS_HELPER + select REGMAP_I2C + help + Driver for Lontium LT9611UXC DSI to HDMI bridge + chip driver that converts dual DSI and I2S to + HDMI signals + Please say Y if you have such hardware. + source "drivers/gpu/drm/bridge/analogix/Kconfig" source "drivers/gpu/drm/bridge/adv7511/Kconfig" diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index 7d7c123a95e4..c902995e2c7a 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o obj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o obj-$(CONFIG_DRM_TI_TPD12S015) += ti-tpd12s015.o obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-dsi.o +obj-$(CONFIG_DRM_LONTIUM_LT9611UXC) += lontium-lt9611uxc.o obj-y += analogix/ obj-y += synopsys/ diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c new file mode 100644 index 000000000000..b6e6bdcdb28f --- /dev/null +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c @@ -0,0 +1,778 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2020. Linaro Limited. + */ + +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_graph.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#include <sound/hdmi-codec.h> + +#include <video/display_timing.h> +#include <video/of_display_timing.h> +#include <video/videomode.h> + +#include <drm/drm_probe_helper.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_bridge.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_print.h> + +#define EDID_BLOCK_SIZE 128 +#define EDID_NUM_BLOCKS 2 + +struct lt9611uxc { + struct device *dev; + struct drm_bridge bridge; + + struct regmap *regmap; + /* Protects all accesses to registers by stopping the on-chip MCU */ + struct mutex ocm_lock; + + struct device_node *dsi0_node; + struct device_node *dsi1_node; + struct mipi_dsi_device *dsi0; + struct mipi_dsi_device *dsi1; + struct platform_device *audio_pdev; + + struct gpio_desc *reset_gpio; + struct gpio_desc *enable_gpio; + + bool sleep; + + struct regulator_bulk_data supplies[2]; + + struct i2c_client *client; + + bool hpd_supported; + struct display_timings *timings; + u8 edid_buf[EDID_BLOCK_SIZE * EDID_NUM_BLOCKS]; +}; + +#define LT9611_PAGE_CONTROL 0xff + +static const struct regmap_range_cfg lt9611uxc_ranges[] = { + { + .name = "register_range", + .range_min = 0, + .range_max = 0xd0ff, + .selector_reg = LT9611_PAGE_CONTROL, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, + }, +}; + +static const struct regmap_config lt9611uxc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xffff, + .ranges = lt9611uxc_ranges, + .num_ranges = ARRAY_SIZE(lt9611uxc_ranges), +}; + +struct lt9611uxc_mode { + u16 hdisplay; + u16 vdisplay; + u8 vrefresh; + u8 lanes; + u8 intfs; +}; + +static struct lt9611uxc_mode lt9611uxc_modes[] = { +#if 0 + { 3840, 2160, 60, 4, 2 }, /* 3840x2160 24bit 60Hz 4Lane 2ports */ + { 3840, 2160, 30, 4, 2 }, /* 3840x2160 24bit 30Hz 4Lane 2ports */ +#endif + { 1920, 1080, 60, 4, 1 }, /* 1080P 24bit 60Hz 4lane 1port */ + { 1920, 1080, 30, 3, 1 }, /* 1080P 24bit 30Hz 3lane 1port */ + { 1920, 1080, 24, 3, 1 }, + { 1024, 768, 60, 4, 1}, + { 800, 600, 60, 4, 1}, + { 720, 480, 60, 4, 1 }, + { 720, 576, 50, 2, 1 }, + { 640, 480, 60, 2, 1 }, +}; + +static struct lt9611uxc *bridge_to_lt9611uxc(struct drm_bridge *bridge) +{ + return container_of(bridge, struct lt9611uxc, bridge); +} + +static void lt9611uxc_lock(struct lt9611uxc *lt9611uxc) +{ + mutex_lock(<9611uxc->ocm_lock); + regmap_write(lt9611uxc->regmap, 0x80ee, 0x01); +} + +static void lt9611uxc_unlock(struct lt9611uxc *lt9611uxc) +{ + regmap_write(lt9611uxc->regmap, 0x80ee, 0x00); + msleep(50); + mutex_unlock(<9611uxc->ocm_lock); +} + +static irqreturn_t lt9611uxc_irq_thread_handler(int irq, void *dev_id) +{ + struct lt9611uxc *lt9611uxc = dev_id; + unsigned int irq_status = 0; + unsigned int hpd_status = 0; + + lt9611uxc_lock(lt9611uxc); + + regmap_read(lt9611uxc->regmap, 0xb022, &irq_status); + if (irq_status) { + regmap_write(lt9611uxc->regmap, 0xb022, 0); + regmap_read(lt9611uxc->regmap, 0xb023, &hpd_status); + } + + lt9611uxc_unlock(lt9611uxc); + + if (irq_status & 0x3) + drm_bridge_hpd_notify(<9611uxc->bridge, (hpd_status & BIT(1)) ? + connector_status_connected : + connector_status_disconnected); + + return IRQ_HANDLED; +} + +static void lt9611uxc_reset(struct lt9611uxc *lt9611uxc) +{ + gpiod_set_value_cansleep(lt9611uxc->reset_gpio, 1); + msleep(20); + + gpiod_set_value_cansleep(lt9611uxc->reset_gpio, 0); + msleep(20); + + gpiod_set_value_cansleep(lt9611uxc->reset_gpio, 1); + msleep(180); + + lt9611uxc->sleep = false; +} + +static void lt9611uxc_assert_5v(struct lt9611uxc *lt9611uxc) +{ + if (!lt9611uxc->enable_gpio) + return; + + gpiod_set_value_cansleep(lt9611uxc->enable_gpio, 1); + msleep(20); +} + +static int lt9611uxc_regulator_init(struct lt9611uxc *lt9611uxc) +{ + int ret; + + lt9611uxc->supplies[0].supply = "vdd"; + lt9611uxc->supplies[1].supply = "vcc"; + + ret = devm_regulator_bulk_get(lt9611uxc->dev, 2, lt9611uxc->supplies); + if (ret < 0) + return ret; + + return regulator_set_load(lt9611uxc->supplies[0].consumer, 200000); +} + +static int lt9611uxc_regulator_enable(struct lt9611uxc *lt9611uxc) +{ + int ret; + + ret = regulator_enable(lt9611uxc->supplies[0].consumer); + if (ret < 0) + return ret; + + usleep_range(1000, 10000); /* 50000 according to dtsi */ + + ret = regulator_enable(lt9611uxc->supplies[1].consumer); + if (ret < 0) { + regulator_disable(lt9611uxc->supplies[0].consumer); + return ret; + } + + return 0; +} + +static struct lt9611uxc_mode *lt9611uxc_find_mode(const struct drm_display_mode *mode) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(lt9611uxc_modes); i++) { + if (lt9611uxc_modes[i].hdisplay == mode->hdisplay && + lt9611uxc_modes[i].vdisplay == mode->vdisplay && + lt9611uxc_modes[i].vrefresh == drm_mode_vrefresh(mode)) { + return <9611uxc_modes[i]; + } + } + + return NULL; +} + +static struct mipi_dsi_device *lt9611uxc_attach_dsi(struct lt9611uxc *lt9611uxc, + struct device_node *dsi_node) +{ + const struct mipi_dsi_device_info info = { "lt9611uxc", 0, NULL }; + struct mipi_dsi_device *dsi; + struct mipi_dsi_host *host; + int ret; + + host = of_find_mipi_dsi_host_by_node(dsi_node); + if (!host) { + dev_err(lt9611uxc->dev, "failed to find dsi host\n"); + return ERR_PTR(-EPROBE_DEFER); + } + + dsi = mipi_dsi_device_register_full(host, &info); + if (IS_ERR(dsi)) { + dev_err(lt9611uxc->dev, "failed to create dsi device\n"); + return dsi; + } + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_VIDEO_HSE; + + ret = mipi_dsi_attach(dsi); + if (ret < 0) { + dev_err(lt9611uxc->dev, "failed to attach dsi to host\n"); + mipi_dsi_device_unregister(dsi); + return ERR_PTR(ret); + } + + return dsi; +} + +static void lt9611uxc_bridge_detach(struct drm_bridge *bridge) +{ + struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge); + + if (lt9611uxc->dsi1) { + mipi_dsi_detach(lt9611uxc->dsi1); + mipi_dsi_device_unregister(lt9611uxc->dsi1); + } + + mipi_dsi_detach(lt9611uxc->dsi0); + mipi_dsi_device_unregister(lt9611uxc->dsi0); +} + +static int lt9611uxc_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge); + int ret; + + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { + dev_err(lt9611uxc->dev, "Fix bridge driver to make connector optional!"); + return -EINVAL; + } + + /* Attach primary DSI */ + lt9611uxc->dsi0 = lt9611uxc_attach_dsi(lt9611uxc, lt9611uxc->dsi0_node); + if (IS_ERR(lt9611uxc->dsi0)) + return PTR_ERR(lt9611uxc->dsi0); + + /* Attach secondary DSI, if specified */ + if (lt9611uxc->dsi1_node) { + lt9611uxc->dsi1 = lt9611uxc_attach_dsi(lt9611uxc, lt9611uxc->dsi1_node); + if (IS_ERR(lt9611uxc->dsi1)) { + ret = PTR_ERR(lt9611uxc->dsi1); + goto err_unregister_dsi0; + } + } + + return 0; + +err_unregister_dsi0: + lt9611uxc_bridge_detach(bridge); + mipi_dsi_device_unregister(lt9611uxc->dsi0); + + return ret; +} + +static enum drm_mode_status +lt9611uxc_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_info *info, + const struct drm_display_mode *mode) +{ + struct lt9611uxc_mode *lt9611uxc_mode; + + lt9611uxc_mode = lt9611uxc_find_mode(mode); + + return lt9611uxc_mode ? MODE_OK : MODE_BAD; +} + +static void lt9611uxc_bridge_post_disable(struct drm_bridge *bridge) +{ + struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge); + + lt9611uxc_lock(lt9611uxc); + regmap_update_bits(lt9611uxc->regmap, 0xb024, 0x1, 0x1); + lt9611uxc->sleep = true; + lt9611uxc_unlock(lt9611uxc); +} + +static void lt9611uxc_video_setup(struct lt9611uxc *lt9611uxc, + const struct drm_display_mode *mode) +{ + u32 h_total, hactive, hsync_len, hfront_porch, hsync_porch; + u32 v_total, vactive, vsync_len, vfront_porch, vsync_porch; + + h_total = mode->htotal; + v_total = mode->vtotal; + + hactive = mode->hdisplay; + hsync_len = mode->hsync_end - mode->hsync_start; + hfront_porch = mode->hsync_start - mode->hdisplay; + hsync_porch = hsync_len + mode->htotal - mode->hsync_end; + + vactive = mode->vdisplay; + vsync_len = mode->vsync_end - mode->vsync_start; + vfront_porch = mode->vsync_start - mode->vdisplay; + vsync_porch = vsync_len + mode->vtotal - mode->vsync_end; + + regmap_write(lt9611uxc->regmap, 0xd00d, (u8)(v_total / 256)); + regmap_write(lt9611uxc->regmap, 0xd00e, (u8)(v_total % 256)); + + regmap_write(lt9611uxc->regmap, 0xd00f, (u8)(vactive / 256)); + regmap_write(lt9611uxc->regmap, 0xd010, (u8)(vactive % 256)); + + regmap_write(lt9611uxc->regmap, 0xd011, (u8)(h_total / 256)); + regmap_write(lt9611uxc->regmap, 0xd012, (u8)(h_total % 256)); + + regmap_write(lt9611uxc->regmap, 0xd013, (u8)(hactive / 256)); + regmap_write(lt9611uxc->regmap, 0xd014, (u8)(hactive % 256)); + + regmap_write(lt9611uxc->regmap, 0xd015, (u8)(vsync_len % 256)); + + regmap_update_bits(lt9611uxc->regmap, 0xd016, 0xf, (u8)(hsync_len / 256)); + regmap_write(lt9611uxc->regmap, 0xd017, (u8)(hsync_len % 256)); + + regmap_update_bits(lt9611uxc->regmap, 0xd018, 0xf, (u8)(vfront_porch / 256)); + regmap_write(lt9611uxc->regmap, 0xd019, (u8)(vfront_porch % 256)); + + regmap_update_bits(lt9611uxc->regmap, 0xd01a, 0xf, (u8)(hfront_porch / 256)); + regmap_write(lt9611uxc->regmap, 0xd01b, (u8)(hfront_porch % 256)); +} + +static void lt9611uxc_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adj_mode) +{ + struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge); + + if (lt9611uxc->sleep) + lt9611uxc_reset(lt9611uxc); + + lt9611uxc_lock(lt9611uxc); + lt9611uxc_video_setup(lt9611uxc, mode); + lt9611uxc_unlock(lt9611uxc); +} + +static enum drm_connector_status lt9611uxc_bridge_detect(struct drm_bridge *bridge) +{ + struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge); + unsigned int reg_val = 0; + int ret; + int connected = 1; + + if (lt9611uxc->hpd_supported) { + lt9611uxc_lock(lt9611uxc); + ret = regmap_read(lt9611uxc->regmap, 0xb023, ®_val); + lt9611uxc_unlock(lt9611uxc); + + if (ret) + dev_err(lt9611uxc->dev, "failed to read hpd status: %d\n", ret); + else + connected = reg_val & BIT(1); + } + + return connected ? connector_status_connected : + connector_status_disconnected; +} + +static int lt9611uxc_bridge_get_modes(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge); + struct display_timings *timings = lt9611uxc->timings; + int i; + + for (i = 0; i < timings->num_timings; i++) { + struct drm_display_mode *mode = drm_mode_create(bridge->dev); + struct videomode vm; + + if (videomode_from_timings(timings, &vm, i)) + break; + + drm_display_mode_from_videomode(&vm, mode); + + mode->type = DRM_MODE_TYPE_DRIVER; + + if (timings->native_mode == i) + mode->type |= DRM_MODE_TYPE_PREFERRED; + + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + } + + return i; +} + +static int lt9611uxc_read_edid(struct lt9611uxc *lt9611uxc) +{ + int ret = 0; + int i; + + /* memset to clear old buffer, if any */ + memset(lt9611uxc->edid_buf, 0, sizeof(lt9611uxc->edid_buf)); + + lt9611uxc_lock(lt9611uxc); + + regmap_write(lt9611uxc->regmap, 0xb00b, 0x10); + +#define EDID_SEG 16 + for (i = 0; i < 2 * EDID_BLOCK_SIZE; i += EDID_SEG) { + regmap_write(lt9611uxc->regmap, 0xb00a, i); + ret = regmap_noinc_read(lt9611uxc->regmap, 0xb0b0, <9611uxc->edid_buf[i], EDID_SEG); + if (ret < 0) + break; + } + + lt9611uxc_unlock(lt9611uxc); + return ret; +} + +static int lt9611uxc_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len) +{ + struct lt9611uxc *lt9611uxc = data; + int ret; + + if (len > EDID_BLOCK_SIZE) + return -EINVAL; + + if (block >= EDID_NUM_BLOCKS) + return -EINVAL; + + if (block == 0) { + ret = lt9611uxc_read_edid(lt9611uxc); + if (ret) { + dev_err(lt9611uxc->dev, "edid read failed\n"); + return ret; + } + } + + memcpy(buf, lt9611uxc->edid_buf + block * EDID_BLOCK_SIZE, len); + return 0; +}; + +struct edid* lt9611uxc_bridge_get_edid(struct drm_bridge *bridge, + struct drm_connector *connector) +{ + struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge); + + return drm_do_get_edid(connector, lt9611uxc_get_edid_block, lt9611uxc); +} + +static const struct drm_bridge_funcs lt9611uxc_bridge_funcs = { + .attach = lt9611uxc_bridge_attach, + .detach = lt9611uxc_bridge_detach, + .mode_valid = lt9611uxc_bridge_mode_valid, + .post_disable = lt9611uxc_bridge_post_disable, + .mode_set = lt9611uxc_bridge_mode_set, + .detect = lt9611uxc_bridge_detect, + .get_modes = lt9611uxc_bridge_get_modes, + .get_edid = lt9611uxc_bridge_get_edid, +}; + +static int lt9611uxc_parse_dt(struct device *dev, + struct lt9611uxc *lt9611uxc) +{ + lt9611uxc->timings = of_get_display_timings(dev->of_node); + if (!lt9611uxc->timings) + dev_info(dev, "no display timings provided\n"); + + lt9611uxc->dsi0_node = of_graph_get_remote_node(dev->of_node, 1, -1); + if (!lt9611uxc->dsi0_node) { + dev_err(lt9611uxc->dev, "failed to get remote node for primary dsi\n"); + return -ENODEV; + } + + lt9611uxc->dsi1_node = of_graph_get_remote_node(dev->of_node, 2, -1); + + return 0; +} + +static int lt9611uxc_gpio_init(struct lt9611uxc *lt9611uxc) +{ + struct device *dev = lt9611uxc->dev; + + lt9611uxc->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(lt9611uxc->reset_gpio)) { + dev_err(dev, "failed to acquire reset gpio\n"); + return PTR_ERR(lt9611uxc->reset_gpio); + } + + lt9611uxc->enable_gpio = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(lt9611uxc->enable_gpio)) { + dev_err(dev, "failed to acquire enable gpio\n"); + return PTR_ERR(lt9611uxc->enable_gpio); + } + + return 0; +} + +static int lt9611uxc_read_device_rev(struct lt9611uxc *lt9611uxc) +{ + unsigned int rev0, rev1, rev2; + int ret; + + lt9611uxc_lock(lt9611uxc); + + ret = regmap_read(lt9611uxc->regmap, 0x8100, &rev0); + ret |= regmap_read(lt9611uxc->regmap, 0x8101, &rev1); + ret |= regmap_read(lt9611uxc->regmap, 0x8102, &rev2); + if (ret) + dev_err(lt9611uxc->dev, "failed to read revision: %d\n", ret); + else + dev_info(lt9611uxc->dev, "LT9611 revision: 0x%02x.%02x.%02x\n", rev0, rev1, rev2); + + lt9611uxc_unlock(lt9611uxc); + + return ret; +} + +static int lt9611uxc_read_version(struct lt9611uxc *lt9611uxc) +{ + unsigned int rev; + int ret; + + lt9611uxc_lock(lt9611uxc); + + ret = regmap_read(lt9611uxc->regmap, 0xb021, &rev); + if (ret) + dev_err(lt9611uxc->dev, "failed to read revision: %d\n", ret); + else + dev_info(lt9611uxc->dev, "LT9611 version: 0x%02x\n", rev); + + lt9611uxc_unlock(lt9611uxc); + + return ret < 0 ? ret : rev; +} + +static int lt9611uxc_hdmi_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *fmt, + struct hdmi_codec_params *hparms) +{ + /* LT9611UXC will automatically detect rate and bitness, so no need to + * setup anything here. */ + return 0; +} + +static void lt9611uxc_audio_shutdown(struct device *dev, void *data) +{ +} + +static int lt9611uxc_hdmi_i2s_get_dai_id(struct snd_soc_component *component, + struct device_node *endpoint) +{ + struct of_endpoint of_ep; + int ret; + + ret = of_graph_parse_endpoint(endpoint, &of_ep); + if (ret < 0) + return ret; + + /* + * HDMI sound should be located as reg = <2> + * Then, it is sound port 0 + */ + if (of_ep.port == 2) + return 0; + + return -EINVAL; +} + +static const struct hdmi_codec_ops lt9611uxc_codec_ops = { + .hw_params = lt9611uxc_hdmi_hw_params, + .audio_shutdown = lt9611uxc_audio_shutdown, + .get_dai_id = lt9611uxc_hdmi_i2s_get_dai_id, +}; + +static int lt9611uxc_audio_init(struct device *dev, struct lt9611uxc *lt9611uxc) +{ + struct hdmi_codec_pdata codec_data = { + .ops = <9611uxc_codec_ops, + .max_i2s_channels = 2, + .i2s = 1, + .data = lt9611uxc, + }; + + lt9611uxc->audio_pdev = + platform_device_register_data(dev, HDMI_CODEC_DRV_NAME, + PLATFORM_DEVID_AUTO, + &codec_data, sizeof(codec_data)); + + return PTR_ERR_OR_ZERO(lt9611uxc->audio_pdev); +} + +static void lt9611uxc_audio_exit(struct lt9611uxc *lt9611uxc) +{ + if (lt9611uxc->audio_pdev) { + platform_device_unregister(lt9611uxc->audio_pdev); + lt9611uxc->audio_pdev = NULL; + } +} + +static int lt9611uxc_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lt9611uxc *lt9611uxc; + struct device *dev = &client->dev; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(dev, "device doesn't support I2C\n"); + return -ENODEV; + } + + lt9611uxc = devm_kzalloc(dev, sizeof(*lt9611uxc), GFP_KERNEL); + if (!lt9611uxc) + return -ENOMEM; + + lt9611uxc->dev = &client->dev; + lt9611uxc->client = client; + mutex_init(<9611uxc->ocm_lock); + + lt9611uxc->regmap = devm_regmap_init_i2c(client, <9611uxc_regmap_config); + if (IS_ERR(lt9611uxc->regmap)) { + dev_err(lt9611uxc->dev, "regmap i2c init failed\n"); + return PTR_ERR(lt9611uxc->regmap); + } + + ret = lt9611uxc_parse_dt(&client->dev, lt9611uxc); + if (ret) { + dev_err(dev, "failed to parse device tree\n"); + return ret; + } + + ret = lt9611uxc_gpio_init(lt9611uxc); + if (ret < 0) + goto err_of_put; + + ret = lt9611uxc_regulator_init(lt9611uxc); + if (ret < 0) + goto err_of_put; + + lt9611uxc_assert_5v(lt9611uxc); + + ret = lt9611uxc_regulator_enable(lt9611uxc); + if (ret) + goto err_of_put; + + lt9611uxc_reset(lt9611uxc); + + ret = lt9611uxc_read_device_rev(lt9611uxc); + if (ret) { + dev_err(dev, "failed to read chip rev\n"); + goto err_disable_regulators; + } + + ret = lt9611uxc_read_version(lt9611uxc); + if (ret < 0) { + dev_err(dev, "failed to read FW version\n"); + goto err_disable_regulators; + } else if (ret == 0) { + dev_err(dev, "FW version 0, FW update not supported\n"); + ret = -EOPNOTSUPP; + goto err_disable_regulators; + } else if (ret < 0x40) { + dev_info(dev, "FW version 0x%x, HPD not supported\n", ret); + } else { + lt9611uxc->hpd_supported = true; + } + + ret = devm_request_threaded_irq(dev, client->irq, NULL, + lt9611uxc_irq_thread_handler, + IRQF_ONESHOT, "lt9611uxc", lt9611uxc); + if (ret) { + dev_err(dev, "failed to request irq\n"); + goto err_disable_regulators; + } + + i2c_set_clientdata(client, lt9611uxc); + + lt9611uxc->bridge.funcs = <9611uxc_bridge_funcs; + lt9611uxc->bridge.of_node = client->dev.of_node; + lt9611uxc->bridge.ops = DRM_BRIDGE_OP_DETECT; + if (lt9611uxc->timings) + lt9611uxc->bridge.ops |= DRM_BRIDGE_OP_MODES; + else + lt9611uxc->bridge.ops |= DRM_BRIDGE_OP_EDID; + if (lt9611uxc->hpd_supported) + lt9611uxc->bridge.ops |= DRM_BRIDGE_OP_HPD; + lt9611uxc->bridge.type = DRM_MODE_CONNECTOR_HDMIA; + + drm_bridge_add(<9611uxc->bridge); + + return lt9611uxc_audio_init(dev, lt9611uxc); + +err_disable_regulators: + regulator_bulk_disable(ARRAY_SIZE(lt9611uxc->supplies), lt9611uxc->supplies); + +err_of_put: + of_node_put(lt9611uxc->dsi1_node); + of_node_put(lt9611uxc->dsi0_node); + + return ret; +} + +static int lt9611uxc_remove(struct i2c_client *client) +{ + struct lt9611uxc *lt9611uxc = i2c_get_clientdata(client); + + disable_irq(client->irq); + lt9611uxc_audio_exit(lt9611uxc); + drm_bridge_remove(<9611uxc->bridge); + + mutex_destroy(<9611uxc->ocm_lock); + + regulator_bulk_disable(ARRAY_SIZE(lt9611uxc->supplies), lt9611uxc->supplies); + + of_node_put(lt9611uxc->dsi1_node); + of_node_put(lt9611uxc->dsi0_node); + + return 0; +} + +static struct i2c_device_id lt9611uxc_id[] = { + { "lontium,lt9611uxc", 0 }, + {} +}; + +static const struct of_device_id lt9611uxc_match_table[] = { + { .compatible = "lontium,lt9611uxc" }, + { } +}; +MODULE_DEVICE_TABLE(of, lt9611uxc_match_table); + +static struct i2c_driver lt9611uxc_driver = { + .driver = { + .name = "lt9611uxc", + .of_match_table = lt9611uxc_match_table, + }, + .probe = lt9611uxc_probe, + .remove = lt9611uxc_remove, + .id_table = lt9611uxc_id, +}; +module_i2c_driver(lt9611uxc_driver); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 6deaa7d01654..82bbc67228fe 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -110,3 +110,10 @@ config DRM_MSM_DSI_10NM_PHY default y help Choose this option if DSI PHY on SDM845 is used on the platform. + +config DRM_MSM_DSI_7NM_PHY + bool "Enable DSI 7nm PHY driver in MSM DRM (used by SM8150)" + depends on DRM_MSM_DSI + default y + help + Choose this option if DSI PHY on SM8150 is used on the platform. diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 42f8aae28b31..1592b9503f6a 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -66,6 +66,7 @@ msm-y := \ disp/dpu1/dpu_hw_pingpong.o \ disp/dpu1/dpu_hw_sspp.o \ disp/dpu1/dpu_hw_dspp.o \ + disp/dpu1/dpu_hw_merge3d.o \ disp/dpu1/dpu_hw_top.o \ disp/dpu1/dpu_hw_util.o \ disp/dpu1/dpu_hw_vbif.o \ @@ -119,6 +120,7 @@ msm-$(CONFIG_DRM_MSM_DSI_20NM_PHY) += dsi/phy/dsi_phy_20nm.o msm-$(CONFIG_DRM_MSM_DSI_28NM_8960_PHY) += dsi/phy/dsi_phy_28nm_8960.o msm-$(CONFIG_DRM_MSM_DSI_14NM_PHY) += dsi/phy/dsi_phy_14nm.o msm-$(CONFIG_DRM_MSM_DSI_10NM_PHY) += dsi/phy/dsi_phy_10nm.o +msm-$(CONFIG_DRM_MSM_DSI_7NM_PHY) += dsi/phy/dsi_phy_7nm.o ifeq ($(CONFIG_DRM_MSM_DSI_PLL),y) msm-y += dsi/pll/dsi_pll.o @@ -126,6 +128,7 @@ msm-$(CONFIG_DRM_MSM_DSI_28NM_PHY) += dsi/pll/dsi_pll_28nm.o msm-$(CONFIG_DRM_MSM_DSI_28NM_8960_PHY) += dsi/pll/dsi_pll_28nm_8960.o msm-$(CONFIG_DRM_MSM_DSI_14NM_PHY) += dsi/pll/dsi_pll_14nm.o msm-$(CONFIG_DRM_MSM_DSI_10NM_PHY) += dsi/pll/dsi_pll_10nm.o +msm-$(CONFIG_DRM_MSM_DSI_7NM_PHY) += dsi/pll/dsi_pll_7nm.o endif obj-$(CONFIG_DRM_MSM) += msm.o diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c index 8493d68ad841..5a056c1191df 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_cmd.c @@ -437,7 +437,6 @@ static void dpu_encoder_phys_cmd_enable_helper( struct dpu_encoder_phys *phys_enc) { struct dpu_hw_ctl *ctl; - u32 flush_mask = 0; if (!phys_enc->hw_pp) { DPU_ERROR("invalid arg(s), encoder %d\n", phys_enc != NULL); @@ -452,8 +451,7 @@ static void dpu_encoder_phys_cmd_enable_helper( return; ctl = phys_enc->hw_ctl; - ctl->ops.get_bitmask_intf(ctl, &flush_mask, phys_enc->intf_idx); - ctl->ops.update_pending_flush(ctl, flush_mask); + ctl->ops.update_pending_flush_intf(ctl, phys_enc->intf_idx); } static void dpu_encoder_phys_cmd_enable(struct dpu_encoder_phys *phys_enc) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c index b5a49050d131..7718e01b3d9d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_vid.c @@ -5,6 +5,7 @@ #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ #include "dpu_encoder_phys.h" #include "dpu_hw_interrupts.h" +#include "dpu_hw_merge3d.h" #include "dpu_core_irq.h" #include "dpu_formats.h" #include "dpu_trace.h" @@ -274,6 +275,8 @@ static void dpu_encoder_phys_vid_setup_timing_engine( intf_cfg.intf_mode_sel = DPU_CTL_MODE_SEL_VID; intf_cfg.stream_sel = 0; /* Don't care value for video mode */ intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc); + if (phys_enc->hw_pp->merge_3d) + intf_cfg.merge_3d = phys_enc->hw_pp->merge_3d->id; spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); phys_enc->hw_intf->ops.setup_timing_gen(phys_enc->hw_intf, @@ -287,6 +290,12 @@ static void dpu_encoder_phys_vid_setup_timing_engine( true, phys_enc->hw_pp->idx); + if (intf_cfg.mode_3d) { + struct dpu_hw_merge_3d *merge_3d = to_dpu_hw_merge_3d(phys_enc->hw_pp->merge_3d); + + merge_3d->ops.setup_3d_mode(merge_3d, intf_cfg.mode_3d); + } + spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); programmable_fetch_config(phys_enc, &timing_params); @@ -423,8 +432,6 @@ end: static void dpu_encoder_phys_vid_enable(struct dpu_encoder_phys *phys_enc) { struct dpu_hw_ctl *ctl; - u32 flush_mask = 0; - u32 intf_flush_mask = 0; ctl = phys_enc->hw_ctl; @@ -446,20 +453,14 @@ static void dpu_encoder_phys_vid_enable(struct dpu_encoder_phys *phys_enc) !dpu_encoder_phys_vid_is_master(phys_enc)) goto skip_flush; - ctl->ops.get_bitmask_intf(ctl, &flush_mask, phys_enc->hw_intf->idx); - ctl->ops.update_pending_flush(ctl, flush_mask); - - if (ctl->ops.get_bitmask_active_intf) - ctl->ops.get_bitmask_active_intf(ctl, &intf_flush_mask, - phys_enc->hw_intf->idx); - - if (ctl->ops.update_pending_intf_flush) - ctl->ops.update_pending_intf_flush(ctl, intf_flush_mask); + ctl->ops.update_pending_flush_intf(ctl, phys_enc->hw_intf->idx); + if (ctl->ops.update_pending_flush_merge_3d && phys_enc->hw_pp->merge_3d) + ctl->ops.update_pending_flush_merge_3d(ctl, phys_enc->hw_pp->merge_3d->id); skip_flush: DPU_DEBUG_VIDENC(phys_enc, - "update pending flush ctl %d flush_mask 0%x intf_mask 0x%x\n", - ctl->idx - CTL_0, flush_mask, intf_flush_mask); + "update pending flush ctl %d intf %d\n", + ctl->idx - CTL_0, phys_enc->hw_intf->idx); /* ctl_flush & timing engine enable will be triggered by framework */ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c index 97d122eee96d..1027dbaabfcf 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.c @@ -41,6 +41,8 @@ #define PINGPONG_SDM845_SPLIT_MASK \ (PINGPONG_SDM845_MASK | BIT(DPU_PINGPONG_TE2)) +#define MERGE_3D_SM8150_MASK (0) + #define DSPP_SC7180_MASK BIT(DPU_DSPP_PCC) #define INTF_SDM845_MASK (0) @@ -481,40 +483,59 @@ static const struct dpu_pingpong_sub_blks sdm845_pp_sblk = { .len = 0x20, .version = 0x10000}, }; -#define PP_BLK_TE(_name, _id, _base) \ +#define PP_BLK_TE(_name, _id, _base, _merge_3d) \ {\ .name = _name, .id = _id, \ .base = _base, .len = 0xd4, \ .features = PINGPONG_SDM845_SPLIT_MASK, \ + .merge_3d = _merge_3d, \ .sblk = &sdm845_pp_sblk_te \ } -#define PP_BLK(_name, _id, _base) \ +#define PP_BLK(_name, _id, _base, _merge_3d) \ {\ .name = _name, .id = _id, \ .base = _base, .len = 0xd4, \ .features = PINGPONG_SDM845_MASK, \ + .merge_3d = _merge_3d, \ .sblk = &sdm845_pp_sblk \ } static const struct dpu_pingpong_cfg sdm845_pp[] = { - PP_BLK_TE("pingpong_0", PINGPONG_0, 0x70000), - PP_BLK_TE("pingpong_1", PINGPONG_1, 0x70800), - PP_BLK("pingpong_2", PINGPONG_2, 0x71000), - PP_BLK("pingpong_3", PINGPONG_3, 0x71800), + PP_BLK_TE("pingpong_0", PINGPONG_0, 0x70000, 0), + PP_BLK_TE("pingpong_1", PINGPONG_1, 0x70800, 0), + PP_BLK("pingpong_2", PINGPONG_2, 0x71000, 0), + PP_BLK("pingpong_3", PINGPONG_3, 0x71800, 0), }; static struct dpu_pingpong_cfg sc7180_pp[] = { - PP_BLK_TE("pingpong_0", PINGPONG_0, 0x70000), - PP_BLK_TE("pingpong_1", PINGPONG_1, 0x70800), + PP_BLK_TE("pingpong_0", PINGPONG_0, 0x70000, 0), + PP_BLK_TE("pingpong_1", PINGPONG_1, 0x70800, 0), }; static const struct dpu_pingpong_cfg sm8150_pp[] = { - PP_BLK_TE("pingpong_0", PINGPONG_0, 0x70000), - PP_BLK_TE("pingpong_1", PINGPONG_1, 0x70800), - PP_BLK("pingpong_2", PINGPONG_2, 0x71000), - PP_BLK("pingpong_3", PINGPONG_3, 0x71800), - PP_BLK("pingpong_4", PINGPONG_4, 0x72000), - PP_BLK("pingpong_5", PINGPONG_5, 0x72800), + PP_BLK_TE("pingpong_0", PINGPONG_0, 0x70000, MERGE_3D_0), + PP_BLK_TE("pingpong_1", PINGPONG_1, 0x70800, MERGE_3D_0), + PP_BLK("pingpong_2", PINGPONG_2, 0x71000, MERGE_3D_1), + PP_BLK("pingpong_3", PINGPONG_3, 0x71800, MERGE_3D_1), + PP_BLK("pingpong_4", PINGPONG_4, 0x72000, MERGE_3D_2), + PP_BLK("pingpong_5", PINGPONG_5, 0x72800, MERGE_3D_2), +}; + +/************************************************************* + * MERGE_3D sub blocks config + *************************************************************/ +#define MERGE_3D_BLK(_name, _id, _base) \ + {\ + .name = _name, .id = _id, \ + .base = _base, .len = 0x100, \ + .features = MERGE_3D_SM8150_MASK, \ + .sblk = NULL \ + } + +static const struct dpu_merge_3d_cfg sm8150_merge_3d[] = { + MERGE_3D_BLK("merge_3d_0", MERGE_3D_0, 0x83000), + MERGE_3D_BLK("merge_3d_1", MERGE_3D_1, 0x83100), + MERGE_3D_BLK("merge_3d_2", MERGE_3D_2, 0x83200), }; /************************************************************* @@ -835,6 +856,8 @@ static void sm8150_cfg_init(struct dpu_mdss_cfg *dpu_cfg) .mixer = sm8150_lm, .pingpong_count = ARRAY_SIZE(sm8150_pp), .pingpong = sm8150_pp, + .merge_3d_count = ARRAY_SIZE(sm8150_merge_3d), + .merge_3d = sm8150_merge_3d, .intf_count = ARRAY_SIZE(sm8150_intf), .intf = sm8150_intf, .vbif_count = ARRAY_SIZE(sdm845_vbif), @@ -865,6 +888,8 @@ static void sm8250_cfg_init(struct dpu_mdss_cfg *dpu_cfg) .mixer = sm8150_lm, .pingpong_count = ARRAY_SIZE(sm8150_pp), .pingpong = sm8150_pp, + .merge_3d_count = ARRAY_SIZE(sm8150_merge_3d), + .merge_3d = sm8150_merge_3d, .intf_count = ARRAY_SIZE(sm8150_intf), .intf = sm8150_intf, .vbif_count = ARRAY_SIZE(sdm845_vbif), diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h index 1b7a9213a756..20630966dcee 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_catalog.h @@ -124,6 +124,7 @@ enum { DPU_SSPP_EXCL_RECT, DPU_SSPP_SMART_DMA_V1, DPU_SSPP_SMART_DMA_V2, + DPU_SSPP_SMART_DMA_V2_5, DPU_SSPP_TS_PREFILL, DPU_SSPP_TS_PREFILL_REC1, DPU_SSPP_CDP, @@ -524,10 +525,24 @@ struct dpu_dspp_cfg { */ struct dpu_pingpong_cfg { DPU_HW_BLK_INFO; + u32 merge_3d; const struct dpu_pingpong_sub_blks *sblk; }; /** + * struct dpu_merge_3d_cfg - information of DSPP blocks + * @id enum identifying this block + * @base register offset of this block + * @features bit mask identifying sub-blocks/features + * supported by this block + * @sblk sub-blocks information + */ +struct dpu_merge_3d_cfg { + DPU_HW_BLK_INFO; + const struct dpu_merge_3d_sub_blks *sblk; +}; + +/** * struct dpu_intf_cfg - information of timing engine blocks * @id enum identifying this block * @base register offset of this block @@ -720,6 +735,9 @@ struct dpu_mdss_cfg { u32 pingpong_count; const struct dpu_pingpong_cfg *pingpong; + u32 merge_3d_count; + const struct dpu_merge_3d_cfg *merge_3d; + u32 intf_count; const struct dpu_intf_cfg *intf; @@ -763,6 +781,7 @@ struct dpu_mdss_hw_cfg_handler { #define BLK_INTF(s) ((s)->intf) #define BLK_AD(s) ((s)->ad) #define BLK_DSPP(s) ((s)->dspp) +#define BLK_MERGE3d(s) ((s)->merge_3d) /** * dpu_hw_catalog_init - dpu hardware catalog init API retrieves diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c index 758c355b4fd8..8134b039ddc8 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.c @@ -22,7 +22,9 @@ #define CTL_PREPARE 0x0d0 #define CTL_SW_RESET 0x030 #define CTL_LAYER_EXTN_OFFSET 0x40 +#define CTL_MERGE_3D_ACTIVE 0x0E4 #define CTL_INTF_ACTIVE 0x0F4 +#define CTL_MERGE_3D_FLUSH 0x100 #define CTL_INTF_FLUSH 0x110 #define CTL_INTF_MASTER 0x134 @@ -30,6 +32,7 @@ #define CTL_FLUSH_MASK_CTL BIT(17) #define DPU_REG_RESET_TIMEOUT_US 2000 +#define MERGE_3D_IDX 23 #define INTF_IDX 31 static const struct dpu_ctl_cfg *_ctl_offset(enum dpu_ctl ctl, @@ -104,12 +107,6 @@ static inline void dpu_hw_ctl_update_pending_flush(struct dpu_hw_ctl *ctx, ctx->pending_flush_mask |= flushbits; } -static inline void dpu_hw_ctl_update_pending_intf_flush(struct dpu_hw_ctl *ctx, - u32 flushbits) -{ - ctx->pending_intf_flush_mask |= flushbits; -} - static u32 dpu_hw_ctl_get_pending_flush(struct dpu_hw_ctl *ctx) { return ctx->pending_flush_mask; @@ -118,6 +115,9 @@ static u32 dpu_hw_ctl_get_pending_flush(struct dpu_hw_ctl *ctx) static inline void dpu_hw_ctl_trigger_flush_v1(struct dpu_hw_ctl *ctx) { + if (ctx->pending_flush_mask & BIT(MERGE_3D_IDX)) + DPU_REG_WRITE(&ctx->hw, CTL_MERGE_3D_FLUSH, + ctx->pending_merge_3d_flush_mask); if (ctx->pending_flush_mask & BIT(INTF_IDX)) DPU_REG_WRITE(&ctx->hw, CTL_INTF_FLUSH, ctx->pending_intf_flush_mask); @@ -220,40 +220,39 @@ static uint32_t dpu_hw_ctl_get_bitmask_mixer(struct dpu_hw_ctl *ctx, return flushbits; } -static int dpu_hw_ctl_get_bitmask_intf(struct dpu_hw_ctl *ctx, - u32 *flushbits, enum dpu_intf intf) +static void dpu_hw_ctl_update_pending_flush_intf(struct dpu_hw_ctl *ctx, + enum dpu_intf intf) { switch (intf) { case INTF_0: - *flushbits |= BIT(31); + ctx->pending_flush_mask |= BIT(31); break; case INTF_1: - *flushbits |= BIT(30); + ctx->pending_flush_mask |= BIT(30); break; case INTF_2: - *flushbits |= BIT(29); + ctx->pending_flush_mask |= BIT(29); break; case INTF_3: - *flushbits |= BIT(28); + ctx->pending_flush_mask |= BIT(28); break; default: - return -EINVAL; + break; } - return 0; } -static int dpu_hw_ctl_get_bitmask_intf_v1(struct dpu_hw_ctl *ctx, - u32 *flushbits, enum dpu_intf intf) +static void dpu_hw_ctl_update_pending_flush_intf_v1(struct dpu_hw_ctl *ctx, + enum dpu_intf intf) { - *flushbits |= BIT(31); - return 0; + ctx->pending_intf_flush_mask |= BIT(intf - INTF_0); + ctx->pending_flush_mask |= BIT(INTF_IDX); } -static int dpu_hw_ctl_active_get_bitmask_intf(struct dpu_hw_ctl *ctx, - u32 *flushbits, enum dpu_intf intf) +static void dpu_hw_ctl_update_pending_flush_merge_3d_v1(struct dpu_hw_ctl *ctx, + enum dpu_merge_3d merge_3d) { - *flushbits |= BIT(intf - INTF_0); - return 0; + ctx->pending_merge_3d_flush_mask |= BIT(merge_3d - MERGE_3D_0); + ctx->pending_flush_mask |= BIT(MERGE_3D_IDX); } static uint32_t dpu_hw_ctl_get_bitmask_dspp(struct dpu_hw_ctl *ctx, @@ -495,6 +494,9 @@ static void dpu_hw_ctl_intf_cfg_v1(struct dpu_hw_ctl *ctx, intf_active = DPU_REG_READ(c, CTL_INTF_ACTIVE); intf_active |= BIT(cfg->intf - INTF_0); + if (cfg->mode_3d) + DPU_REG_WRITE(c, CTL_MERGE_3D_ACTIVE, BIT(cfg->merge_3d - MERGE_3D_0)); + DPU_REG_WRITE(c, CTL_TOP, mode_sel); DPU_REG_WRITE(c, CTL_INTF_ACTIVE, intf_active); } @@ -535,15 +537,15 @@ static void _setup_ctl_ops(struct dpu_hw_ctl_ops *ops, if (cap & BIT(DPU_CTL_ACTIVE_CFG)) { ops->trigger_flush = dpu_hw_ctl_trigger_flush_v1; ops->setup_intf_cfg = dpu_hw_ctl_intf_cfg_v1; - ops->get_bitmask_intf = dpu_hw_ctl_get_bitmask_intf_v1; - ops->get_bitmask_active_intf = - dpu_hw_ctl_active_get_bitmask_intf; - ops->update_pending_intf_flush = - dpu_hw_ctl_update_pending_intf_flush; + ops->update_pending_flush_intf = + dpu_hw_ctl_update_pending_flush_intf_v1; + ops->update_pending_flush_merge_3d = + dpu_hw_ctl_update_pending_flush_merge_3d_v1; } else { ops->trigger_flush = dpu_hw_ctl_trigger_flush; ops->setup_intf_cfg = dpu_hw_ctl_intf_cfg; - ops->get_bitmask_intf = dpu_hw_ctl_get_bitmask_intf; + ops->update_pending_flush_intf = + dpu_hw_ctl_update_pending_flush_intf; } ops->clear_pending_flush = dpu_hw_ctl_clear_pending_flush; ops->update_pending_flush = dpu_hw_ctl_update_pending_flush; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h index ec579b470a80..e93a42ab60b1 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_ctl.h @@ -37,12 +37,14 @@ struct dpu_hw_stage_cfg { * struct dpu_hw_intf_cfg :Describes how the DPU writes data to output interface * @intf : Interface id * @mode_3d: 3d mux configuration + * @merge_3d: 3d merge block used * @intf_mode_sel: Interface mode, cmd / vid * @stream_sel: Stream selection for multi-stream interfaces */ struct dpu_hw_intf_cfg { enum dpu_intf intf; enum dpu_3d_blend_mode mode_3d; + enum dpu_merge_3d merge_3d; enum dpu_ctl_mode_sel intf_mode_sel; int stream_sel; }; @@ -91,13 +93,22 @@ struct dpu_hw_ctl_ops { u32 flushbits); /** - * OR in the given flushbits to the cached pending_intf_flush_mask + * OR in the given flushbits to the cached pending_(intf_)flush_mask * No effect on hardware * @ctx : ctl path ctx pointer - * @flushbits : module flushmask + * @blk : interface block index */ - void (*update_pending_intf_flush)(struct dpu_hw_ctl *ctx, - u32 flushbits); + void (*update_pending_flush_intf)(struct dpu_hw_ctl *ctx, + enum dpu_intf blk); + + /** + * OR in the given flushbits to the cached pending_(merge_3d_)flush_mask + * No effect on hardware + * @ctx : ctl path ctx pointer + * @blk : interface block index + */ + void (*update_pending_flush_merge_3d)(struct dpu_hw_ctl *ctx, + enum dpu_merge_3d blk); /** * Write the value of the pending_flush_mask to hardware @@ -143,23 +154,6 @@ struct dpu_hw_ctl_ops { enum dpu_dspp blk); /** - * Query the value of the intf flush mask - * No effect on hardware - * @ctx : ctl path ctx pointer - */ - int (*get_bitmask_intf)(struct dpu_hw_ctl *ctx, - u32 *flushbits, - enum dpu_intf blk); - - /** - * Query the value of the intf active flush mask - * No effect on hardware - * @ctx : ctl path ctx pointer - */ - int (*get_bitmask_active_intf)(struct dpu_hw_ctl *ctx, - u32 *flushbits, enum dpu_intf blk); - - /** * Set all blend stages to disabled * @ctx : ctl path ctx pointer */ @@ -198,6 +192,7 @@ struct dpu_hw_ctl { const struct dpu_lm_cfg *mixer_hw_caps; u32 pending_flush_mask; u32 pending_intf_flush_mask; + u32 pending_merge_3d_flush_mask; /* ops */ struct dpu_hw_ctl_ops ops; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h index 979fd2c60aa0..09a3fb3e89f5 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_mdss.h @@ -96,6 +96,7 @@ enum dpu_hw_blk_type { DPU_HW_BLK_INTF, DPU_HW_BLK_WB, DPU_HW_BLK_DSPP, + DPU_HW_BLK_MERGE_3D, DPU_HW_BLK_MAX, }; @@ -186,6 +187,13 @@ enum dpu_pingpong { PINGPONG_MAX }; +enum dpu_merge_3d { + MERGE_3D_0 = 1, + MERGE_3D_1, + MERGE_3D_2, + MERGE_3D_MAX +}; + enum dpu_intf { INTF_0 = 1, INTF_1, diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_merge3d.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_merge3d.c new file mode 100644 index 000000000000..bf63bfbcdec2 --- /dev/null +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_merge3d.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. + */ + +#include <linux/iopoll.h> + +#include "dpu_hw_mdss.h" +#include "dpu_hwio.h" +#include "dpu_hw_catalog.h" +#include "dpu_hw_merge3d.h" +#include "dpu_kms.h" +#include "dpu_trace.h" + +#define MERGE_3D_MUX 0x000 +#define MERGE_3D_MODE 0x004 + +static const struct dpu_merge_3d_cfg *_merge_3d_offset(enum dpu_merge_3d pp, + const struct dpu_mdss_cfg *m, + void __iomem *addr, + struct dpu_hw_blk_reg_map *b) +{ + int i; + + for (i = 0; i < m->merge_3d_count; i++) { + if (pp == m->merge_3d[i].id) { + b->base_off = addr; + b->blk_off = m->merge_3d[i].base; + b->length = m->merge_3d[i].len; + b->hwversion = m->hwversion; + b->log_mask = DPU_DBG_MASK_PINGPONG; + return &m->merge_3d[i]; + } + } + + return ERR_PTR(-EINVAL); +} + +static void dpu_hw_merge_3d_setup_3d_mode(struct dpu_hw_merge_3d *merge_3d, + enum dpu_3d_blend_mode mode_3d) +{ + struct dpu_hw_blk_reg_map *c; + u32 data; + + + c = &merge_3d->hw; + data = BIT(0) | ((mode_3d - 1) << 1); + pr_info("MERGE 3D MODE %d %d %x\n", merge_3d->base.id, mode_3d, data); + DPU_REG_WRITE(c, MERGE_3D_MODE, data); +} + +static void _setup_merge_3d_ops(struct dpu_hw_merge_3d *c, + unsigned long features) +{ + c->ops.setup_3d_mode = dpu_hw_merge_3d_setup_3d_mode; +}; + +static struct dpu_hw_blk_ops dpu_hw_ops; + +struct dpu_hw_merge_3d *dpu_hw_merge_3d_init(enum dpu_merge_3d idx, + void __iomem *addr, + const struct dpu_mdss_cfg *m) +{ + struct dpu_hw_merge_3d *c; + const struct dpu_merge_3d_cfg *cfg; + + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + return ERR_PTR(-ENOMEM); + + cfg = _merge_3d_offset(idx, m, addr, &c->hw); + if (IS_ERR_OR_NULL(cfg)) { + kfree(c); + return ERR_PTR(-EINVAL); + } + + c->idx = idx; + c->caps = cfg; + _setup_merge_3d_ops(c, c->caps->features); + + dpu_hw_blk_init(&c->base, DPU_HW_BLK_MERGE_3D, idx, &dpu_hw_ops); + + return c; +} + +void dpu_hw_merge_3d_destroy(struct dpu_hw_merge_3d *pp) +{ + if (pp) + dpu_hw_blk_destroy(&pp->base); + kfree(pp); +} diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_merge3d.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_merge3d.h new file mode 100644 index 000000000000..870bdb14613e --- /dev/null +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_merge3d.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. + */ + +#ifndef _DPU_HW_MERGE3D_H +#define _DPU_HW_MERGE3D_H + +#include "dpu_hw_catalog.h" +#include "dpu_hw_mdss.h" +#include "dpu_hw_util.h" +#include "dpu_hw_blk.h" + +struct dpu_hw_merge_3d; + +/** + * + * struct dpu_hw_merge_3d_ops : Interface to the merge_3d Hw driver functions + * Assumption is these functions will be called after clocks are enabled + * @setup_3d_mode : enable 3D merge + */ +struct dpu_hw_merge_3d_ops { + void (*setup_3d_mode)(struct dpu_hw_merge_3d *merge_3d, + enum dpu_3d_blend_mode mode_3d); + +}; + +struct dpu_hw_merge_3d { + struct dpu_hw_blk base; + struct dpu_hw_blk_reg_map hw; + + /* merge_3d */ + enum dpu_merge_3d idx; + const struct dpu_merge_3d_cfg *caps; + + /* ops */ + struct dpu_hw_merge_3d_ops ops; +}; + +/** + * to_dpu_hw_merge_3d - convert base object dpu_hw_base to container + * @hw: Pointer to base hardware block + * return: Pointer to hardware block container + */ +static inline struct dpu_hw_merge_3d *to_dpu_hw_merge_3d(struct dpu_hw_blk *hw) +{ + return container_of(hw, struct dpu_hw_merge_3d, base); +} + +/** + * dpu_hw_merge_3d_init - initializes the merge_3d driver for the passed + * merge_3d idx. + * @idx: Pingpong index for which driver object is required + * @addr: Mapped register io address of MDP + * @m: Pointer to mdss catalog data + * Returns: Error code or allocated dpu_hw_merge_3d context + */ +struct dpu_hw_merge_3d *dpu_hw_merge_3d_init(enum dpu_merge_3d idx, + void __iomem *addr, + const struct dpu_mdss_cfg *m); + +/** + * dpu_hw_merge_3d_destroy - destroys merge_3d driver context + * should be called to free the context + * @pp: Pointer to PP driver context returned by dpu_hw_merge_3d_init + */ +void dpu_hw_merge_3d_destroy(struct dpu_hw_merge_3d *pp); + +#endif /*_DPU_HW_MERGE3D_H */ diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h index 065996b3ece9..6902b9b95c8e 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_pingpong.h @@ -119,6 +119,7 @@ struct dpu_hw_pingpong { /* pingpong */ enum dpu_pingpong idx; const struct dpu_pingpong_cfg *caps; + struct dpu_hw_blk *merge_3d; /* ops */ struct dpu_hw_pingpong_ops ops; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c index c940b69435e1..113218a78ee9 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_sspp.c @@ -669,7 +669,8 @@ static void _setup_layer_ops(struct dpu_hw_pipe *c, c->ops.setup_csc = dpu_hw_sspp_setup_csc; if (test_bit(DPU_SSPP_SMART_DMA_V1, &c->cap->features) || - test_bit(DPU_SSPP_SMART_DMA_V2, &c->cap->features)) + test_bit(DPU_SSPP_SMART_DMA_V2, &c->cap->features) || + test_bit(DPU_SSPP_SMART_DMA_V2_5, &c->cap->features)) c->ops.setup_multirect = dpu_hw_sspp_setup_multirect; if (test_bit(DPU_SSPP_SCALER_QSEED3, &features) || diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index c0a4d4e16d82..fdf13e95fb33 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -1058,7 +1058,8 @@ static void dpu_unbind(struct device *dev, struct device *master, void *data) if (dpu_kms->has_opp_table) dev_pm_opp_of_remove_table(dev); - dev_pm_opp_put_clkname(dpu_kms->opp_table); + if (dpu_kms->opp_table) + dev_pm_opp_put_clkname(dpu_kms->opp_table); } static const struct component_ops dpu_ops = { diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c index 9b2b5044e8e0..a6781667ffcf 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c @@ -10,6 +10,7 @@ #include "dpu_hw_pingpong.h" #include "dpu_hw_intf.h" #include "dpu_hw_dspp.h" +#include "dpu_hw_merge3d.h" #include "dpu_encoder.h" #include "dpu_trace.h" @@ -119,6 +120,24 @@ int dpu_rm_init(struct dpu_rm *rm, } } + for (i = 0; i < cat->merge_3d_count; i++) { + struct dpu_hw_merge_3d *hw; + const struct dpu_merge_3d_cfg *merge_3d = &cat->merge_3d[i]; + + if (merge_3d->id < MERGE_3D_0 || merge_3d->id >= MERGE_3D_MAX) { + DPU_ERROR("skip merge_3d %d with invalid id\n", merge_3d->id); + continue; + } + hw = dpu_hw_merge_3d_init(merge_3d->id, mmio, cat); + if (IS_ERR_OR_NULL(hw)) { + rc = PTR_ERR(hw); + DPU_ERROR("failed merge_3d object creation: err %d\n", + rc); + goto fail; + } + rm->merge_3d_blks[merge_3d->id - MERGE_3D_0] = &hw->base; + } + for (i = 0; i < cat->pingpong_count; i++) { struct dpu_hw_pingpong *hw; const struct dpu_pingpong_cfg *pp = &cat->pingpong[i]; @@ -134,6 +153,8 @@ int dpu_rm_init(struct dpu_rm *rm, rc); goto fail; } + if (pp->merge_3d && pp->merge_3d < MERGE_3D_MAX) + hw->merge_3d = rm->merge_3d_blks[pp->merge_3d - MERGE_3D_0]; rm->pingpong_blks[pp->id - PINGPONG_0] = &hw->base; } @@ -280,7 +301,7 @@ static bool _dpu_rm_check_lm_and_get_connected_blks(struct dpu_rm *rm, idx = lm_cfg->dspp - DSPP_0; if (idx < 0 || idx >= ARRAY_SIZE(rm->dspp_blks)) { - DPU_ERROR("failed to get dspp on lm %d\n", lm_cfg->dspp); + DPU_ERROR("failed to get dspp %d (%d) on lm %d\n", idx, lm_cfg->dspp, lm_cfg->id); return false; } diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h index 08726bb1063a..1f12c8d5b8aa 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.h @@ -29,6 +29,7 @@ struct dpu_rm { struct dpu_hw_blk *ctl_blks[CTL_MAX - CTL_0]; struct dpu_hw_blk *intf_blks[INTF_MAX - INTF_0]; struct dpu_hw_blk *dspp_blks[DSPP_MAX - DSPP_0]; + struct dpu_hw_blk *merge_3d_blks[MERGE_3D_MAX - MERGE_3D_0]; uint32_t lm_max_width; }; diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c index 627048851d99..22898f920a2c 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.c +++ b/drivers/gpu/drm/msm/dsi/dsi.c @@ -221,12 +221,7 @@ int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev, goto fail; } - /* - * check if the dsi encoder output is connected to a panel or an - * external bridge. We create a connector only if we're connected to a - * drm_panel device. When we're connected to an external bridge, we - * assume that the drm_bridge driver will create the connector itself. - */ + /* Initialize the internal panel or external bridge */ ext_bridge = msm_dsi_host_get_bridge(msm_dsi->host); if (ext_bridge) diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h index 4de771d6f0be..6911d44d3dd5 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.h +++ b/drivers/gpu/drm/msm/dsi/dsi.h @@ -30,6 +30,7 @@ enum msm_dsi_phy_type { MSM_DSI_PHY_28NM_8960, MSM_DSI_PHY_14NM, MSM_DSI_PHY_10NM, + MSM_DSI_PHY_7NM, MSM_DSI_PHY_MAX }; diff --git a/drivers/gpu/drm/msm/dsi/dsi.xml.h b/drivers/gpu/drm/msm/dsi/dsi.xml.h index 8e536e060070..3b8bd715968e 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.xml.h +++ b/drivers/gpu/drm/msm/dsi/dsi.xml.h @@ -1886,5 +1886,427 @@ static inline uint32_t REG_DSI_10nm_PHY_LN_TX_DCTRL(uint32_t i0) { return 0x0000 #define REG_DSI_10nm_PHY_PLL_COMMON_STATUS_ONE 0x000001a0 +#define REG_DSI_7nm_PHY_CMN_REVISION_ID0 0x00000000 + +#define REG_DSI_7nm_PHY_CMN_REVISION_ID1 0x00000004 + +#define REG_DSI_7nm_PHY_CMN_REVISION_ID2 0x00000008 + +#define REG_DSI_7nm_PHY_CMN_REVISION_ID3 0x0000000c + +#define REG_DSI_7nm_PHY_CMN_CLK_CFG0 0x00000010 + +#define REG_DSI_7nm_PHY_CMN_CLK_CFG1 0x00000014 + +#define REG_DSI_7nm_PHY_CMN_GLBL_CTRL 0x00000018 + +#define REG_DSI_7nm_PHY_CMN_RBUF_CTRL 0x0000001c + +#define REG_DSI_7nm_PHY_CMN_VREG_CTRL_0 0x00000020 + +#define REG_DSI_7nm_PHY_CMN_CTRL_0 0x00000024 + +#define REG_DSI_7nm_PHY_CMN_CTRL_1 0x00000028 + +#define REG_DSI_7nm_PHY_CMN_CTRL_2 0x0000002c + +#define REG_DSI_7nm_PHY_CMN_CTRL_3 0x00000030 + +#define REG_DSI_7nm_PHY_CMN_LANE_CFG0 0x00000034 + +#define REG_DSI_7nm_PHY_CMN_LANE_CFG1 0x00000038 + +#define REG_DSI_7nm_PHY_CMN_PLL_CNTRL 0x0000003c + +#define REG_DSI_7nm_PHY_CMN_DPHY_SOT 0x00000040 + +#define REG_DSI_7nm_PHY_CMN_LANE_CTRL0 0x000000a0 + +#define REG_DSI_7nm_PHY_CMN_LANE_CTRL1 0x000000a4 + +#define REG_DSI_7nm_PHY_CMN_LANE_CTRL2 0x000000a8 + +#define REG_DSI_7nm_PHY_CMN_LANE_CTRL3 0x000000ac + +#define REG_DSI_7nm_PHY_CMN_LANE_CTRL4 0x000000b0 + +#define REG_DSI_7nm_PHY_CMN_TIMING_CTRL_0 0x000000b4 + +#define REG_DSI_7nm_PHY_CMN_TIMING_CTRL_1 0x000000b8 + +#define REG_DSI_7nm_PHY_CMN_TIMING_CTRL_2 0x000000bc + +#define REG_DSI_7nm_PHY_CMN_TIMING_CTRL_3 0x000000c0 + +#define REG_DSI_7nm_PHY_CMN_TIMING_CTRL_4 0x000000c4 + +#define REG_DSI_7nm_PHY_CMN_TIMING_CTRL_5 0x000000c8 + +#define REG_DSI_7nm_PHY_CMN_TIMING_CTRL_6 0x000000cc + +#define REG_DSI_7nm_PHY_CMN_TIMING_CTRL_7 0x000000d0 + +#define REG_DSI_7nm_PHY_CMN_TIMING_CTRL_8 0x000000d4 + +#define REG_DSI_7nm_PHY_CMN_TIMING_CTRL_9 0x000000d8 + +#define REG_DSI_7nm_PHY_CMN_TIMING_CTRL_10 0x000000dc + +#define REG_DSI_7nm_PHY_CMN_TIMING_CTRL_11 0x000000e0 + +#define REG_DSI_7nm_PHY_CMN_TIMING_CTRL_12 0x000000e4 + +#define REG_DSI_7nm_PHY_CMN_TIMING_CTRL_13 0x000000e8 + +#define REG_DSI_7nm_PHY_CMN_GLBL_HSTX_STR_CTRL_0 0x000000ec + +#define REG_DSI_7nm_PHY_CMN_GLBL_HSTX_STR_CTRL_1 0x000000f0 + +#define REG_DSI_7nm_PHY_CMN_GLBL_RESCODE_OFFSET_TOP_CTRL 0x000000f4 + +#define REG_DSI_7nm_PHY_CMN_GLBL_RESCODE_OFFSET_BOT_CTRL 0x000000f8 + +#define REG_DSI_7nm_PHY_CMN_GLBL_RESCODE_OFFSET_MID_CTRL 0x000000fc + +#define REG_DSI_7nm_PHY_CMN_GLBL_LPTX_STR_CTRL 0x00000100 + +#define REG_DSI_7nm_PHY_CMN_GLBL_PEMPH_CTRL_0 0x00000104 + +#define REG_DSI_7nm_PHY_CMN_GLBL_PEMPH_CTRL_1 0x00000108 + +#define REG_DSI_7nm_PHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL 0x0000010c + +#define REG_DSI_7nm_PHY_CMN_VREG_CTRL_1 0x00000110 + +#define REG_DSI_7nm_PHY_CMN_CTRL_4 0x00000114 + +#define REG_DSI_7nm_PHY_CMN_GLBL_DIGTOP_SPARE4 0x00000128 + +#define REG_DSI_7nm_PHY_CMN_PHY_STATUS 0x00000140 + +#define REG_DSI_7nm_PHY_CMN_LANE_STATUS0 0x00000148 + +#define REG_DSI_7nm_PHY_CMN_LANE_STATUS1 0x0000014c + +static inline uint32_t REG_DSI_7nm_PHY_LN(uint32_t i0) { return 0x00000000 + 0x80*i0; } + +static inline uint32_t REG_DSI_7nm_PHY_LN_CFG0(uint32_t i0) { return 0x00000000 + 0x80*i0; } + +static inline uint32_t REG_DSI_7nm_PHY_LN_CFG1(uint32_t i0) { return 0x00000004 + 0x80*i0; } + +static inline uint32_t REG_DSI_7nm_PHY_LN_CFG2(uint32_t i0) { return 0x00000008 + 0x80*i0; } + +static inline uint32_t REG_DSI_7nm_PHY_LN_TEST_DATAPATH(uint32_t i0) { return 0x0000000c + 0x80*i0; } + +static inline uint32_t REG_DSI_7nm_PHY_LN_PIN_SWAP(uint32_t i0) { return 0x00000010 + 0x80*i0; } + +static inline uint32_t REG_DSI_7nm_PHY_LN_LPRX_CTRL(uint32_t i0) { return 0x00000014 + 0x80*i0; } + +static inline uint32_t REG_DSI_7nm_PHY_LN_TX_DCTRL(uint32_t i0) { return 0x00000018 + 0x80*i0; } + +#define REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_ONE 0x00000000 + +#define REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_TWO 0x00000004 + +#define REG_DSI_7nm_PHY_PLL_INT_LOOP_SETTINGS 0x00000008 + +#define REG_DSI_7nm_PHY_PLL_INT_LOOP_SETTINGS_TWO 0x0000000c + +#define REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_THREE 0x00000010 + +#define REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_FOUR 0x00000014 + +#define REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_FIVE 0x00000018 + +#define REG_DSI_7nm_PHY_PLL_INT_LOOP_CONTROLS 0x0000001c + +#define REG_DSI_7nm_PHY_PLL_DSM_DIVIDER 0x00000020 + +#define REG_DSI_7nm_PHY_PLL_FEEDBACK_DIVIDER 0x00000024 + +#define REG_DSI_7nm_PHY_PLL_SYSTEM_MUXES 0x00000028 + +#define REG_DSI_7nm_PHY_PLL_FREQ_UPDATE_CONTROL_OVERRIDES 0x0000002c + +#define REG_DSI_7nm_PHY_PLL_CMODE 0x00000030 + +#define REG_DSI_7nm_PHY_PLL_PSM_CTRL 0x00000034 + +#define REG_DSI_7nm_PHY_PLL_RSM_CTRL 0x00000038 + +#define REG_DSI_7nm_PHY_PLL_VCO_TUNE_MAP 0x0000003c + +#define REG_DSI_7nm_PHY_PLL_PLL_CNTRL 0x00000040 + +#define REG_DSI_7nm_PHY_PLL_CALIBRATION_SETTINGS 0x00000044 + +#define REG_DSI_7nm_PHY_PLL_BAND_SEL_CAL_TIMER_LOW 0x00000048 + +#define REG_DSI_7nm_PHY_PLL_BAND_SEL_CAL_TIMER_HIGH 0x0000004c + +#define REG_DSI_7nm_PHY_PLL_BAND_SEL_CAL_SETTINGS 0x00000050 + +#define REG_DSI_7nm_PHY_PLL_BAND_SEL_MIN 0x00000054 + +#define REG_DSI_7nm_PHY_PLL_BAND_SEL_MAX 0x00000058 + +#define REG_DSI_7nm_PHY_PLL_BAND_SEL_PFILT 0x0000005c + +#define REG_DSI_7nm_PHY_PLL_BAND_SEL_IFILT 0x00000060 + +#define REG_DSI_7nm_PHY_PLL_BAND_SEL_CAL_SETTINGS_TWO 0x00000064 + +#define REG_DSI_7nm_PHY_PLL_BAND_SEL_CAL_SETTINGS_THREE 0x00000068 + +#define REG_DSI_7nm_PHY_PLL_BAND_SEL_CAL_SETTINGS_FOUR 0x0000006c + +#define REG_DSI_7nm_PHY_PLL_BAND_SEL_ICODE_HIGH 0x00000070 + +#define REG_DSI_7nm_PHY_PLL_BAND_SEL_ICODE_LOW 0x00000074 + +#define REG_DSI_7nm_PHY_PLL_FREQ_DETECT_SETTINGS_ONE 0x00000078 + +#define REG_DSI_7nm_PHY_PLL_FREQ_DETECT_THRESH 0x0000007c + +#define REG_DSI_7nm_PHY_PLL_FREQ_DET_REFCLK_HIGH 0x00000080 + +#define REG_DSI_7nm_PHY_PLL_FREQ_DET_REFCLK_LOW 0x00000084 + +#define REG_DSI_7nm_PHY_PLL_FREQ_DET_PLLCLK_HIGH 0x00000088 + +#define REG_DSI_7nm_PHY_PLL_FREQ_DET_PLLCLK_LOW 0x0000008c + +#define REG_DSI_7nm_PHY_PLL_PFILT 0x00000090 + +#define REG_DSI_7nm_PHY_PLL_IFILT 0x00000094 + +#define REG_DSI_7nm_PHY_PLL_PLL_GAIN 0x00000098 + +#define REG_DSI_7nm_PHY_PLL_ICODE_LOW 0x0000009c + +#define REG_DSI_7nm_PHY_PLL_ICODE_HIGH 0x000000a0 + +#define REG_DSI_7nm_PHY_PLL_LOCKDET 0x000000a4 + +#define REG_DSI_7nm_PHY_PLL_OUTDIV 0x000000a8 + +#define REG_DSI_7nm_PHY_PLL_FASTLOCK_CONTROL 0x000000ac + +#define REG_DSI_7nm_PHY_PLL_PASS_OUT_OVERRIDE_ONE 0x000000b0 + +#define REG_DSI_7nm_PHY_PLL_PASS_OUT_OVERRIDE_TWO 0x000000b4 + +#define REG_DSI_7nm_PHY_PLL_CORE_OVERRIDE 0x000000b8 + +#define REG_DSI_7nm_PHY_PLL_CORE_INPUT_OVERRIDE 0x000000bc + +#define REG_DSI_7nm_PHY_PLL_RATE_CHANGE 0x000000c0 + +#define REG_DSI_7nm_PHY_PLL_PLL_DIGITAL_TIMERS 0x000000c4 + +#define REG_DSI_7nm_PHY_PLL_PLL_DIGITAL_TIMERS_TWO 0x000000c8 + +#define REG_DSI_7nm_PHY_PLL_DECIMAL_DIV_START 0x000000cc + +#define REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_LOW 0x000000d0 + +#define REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_MID 0x000000d4 + +#define REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_HIGH 0x000000d8 + +#define REG_DSI_7nm_PHY_PLL_DEC_FRAC_MUXES 0x000000dc + +#define REG_DSI_7nm_PHY_PLL_DECIMAL_DIV_START_1 0x000000e0 + +#define REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_LOW_1 0x000000e4 + +#define REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_MID_1 0x000000e8 + +#define REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_HIGH_1 0x000000ec + +#define REG_DSI_7nm_PHY_PLL_DECIMAL_DIV_START_2 0x000000f0 + +#define REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_LOW_2 0x000000f4 + +#define REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_MID_2 0x000000f8 + +#define REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_HIGH_2 0x000000fc + +#define REG_DSI_7nm_PHY_PLL_MASH_CONTROL 0x00000100 + +#define REG_DSI_7nm_PHY_PLL_SSC_STEPSIZE_LOW 0x00000104 + +#define REG_DSI_7nm_PHY_PLL_SSC_STEPSIZE_HIGH 0x00000108 + +#define REG_DSI_7nm_PHY_PLL_SSC_DIV_PER_LOW 0x0000010c + +#define REG_DSI_7nm_PHY_PLL_SSC_DIV_PER_HIGH 0x00000110 + +#define REG_DSI_7nm_PHY_PLL_SSC_ADJPER_LOW 0x00000114 + +#define REG_DSI_7nm_PHY_PLL_SSC_ADJPER_HIGH 0x00000118 + +#define REG_DSI_7nm_PHY_PLL_SSC_MUX_CONTROL 0x0000011c + +#define REG_DSI_7nm_PHY_PLL_SSC_STEPSIZE_LOW_1 0x00000120 + +#define REG_DSI_7nm_PHY_PLL_SSC_STEPSIZE_HIGH_1 0x00000124 + +#define REG_DSI_7nm_PHY_PLL_SSC_DIV_PER_LOW_1 0x00000128 + +#define REG_DSI_7nm_PHY_PLL_SSC_DIV_PER_HIGH_1 0x0000012c + +#define REG_DSI_7nm_PHY_PLL_SSC_ADJPER_LOW_1 0x00000130 + +#define REG_DSI_7nm_PHY_PLL_SSC_ADJPER_HIGH_1 0x00000134 + +#define REG_DSI_7nm_PHY_PLL_SSC_STEPSIZE_LOW_2 0x00000138 + +#define REG_DSI_7nm_PHY_PLL_SSC_STEPSIZE_HIGH_2 0x0000013c + +#define REG_DSI_7nm_PHY_PLL_SSC_DIV_PER_LOW_2 0x00000140 + +#define REG_DSI_7nm_PHY_PLL_SSC_DIV_PER_HIGH_2 0x00000144 + +#define REG_DSI_7nm_PHY_PLL_SSC_ADJPER_LOW_2 0x00000148 + +#define REG_DSI_7nm_PHY_PLL_SSC_ADJPER_HIGH_2 0x0000014c + +#define REG_DSI_7nm_PHY_PLL_SSC_CONTROL 0x00000150 + +#define REG_DSI_7nm_PHY_PLL_PLL_OUTDIV_RATE 0x00000154 + +#define REG_DSI_7nm_PHY_PLL_PLL_LOCKDET_RATE_1 0x00000158 + +#define REG_DSI_7nm_PHY_PLL_PLL_LOCKDET_RATE_2 0x0000015c + +#define REG_DSI_7nm_PHY_PLL_PLL_PROP_GAIN_RATE_1 0x00000160 + +#define REG_DSI_7nm_PHY_PLL_PLL_PROP_GAIN_RATE_2 0x00000164 + +#define REG_DSI_7nm_PHY_PLL_PLL_BAND_SEL_RATE_1 0x00000168 + +#define REG_DSI_7nm_PHY_PLL_PLL_BAND_SEL_RATE_2 0x0000016c + +#define REG_DSI_7nm_PHY_PLL_PLL_INT_GAIN_IFILT_BAND_1 0x00000170 + +#define REG_DSI_7nm_PHY_PLL_PLL_INT_GAIN_IFILT_BAND_2 0x00000174 + +#define REG_DSI_7nm_PHY_PLL_PLL_FL_INT_GAIN_PFILT_BAND_1 0x00000178 + +#define REG_DSI_7nm_PHY_PLL_PLL_FL_INT_GAIN_PFILT_BAND_2 0x0000017c + +#define REG_DSI_7nm_PHY_PLL_PLL_FASTLOCK_EN_BAND 0x00000180 + +#define REG_DSI_7nm_PHY_PLL_FREQ_TUNE_ACCUM_INIT_MID 0x00000184 + +#define REG_DSI_7nm_PHY_PLL_FREQ_TUNE_ACCUM_INIT_HIGH 0x00000188 + +#define REG_DSI_7nm_PHY_PLL_FREQ_TUNE_ACCUM_INIT_MUX 0x0000018c + +#define REG_DSI_7nm_PHY_PLL_PLL_LOCK_OVERRIDE 0x00000190 + +#define REG_DSI_7nm_PHY_PLL_PLL_LOCK_DELAY 0x00000194 + +#define REG_DSI_7nm_PHY_PLL_PLL_LOCK_MIN_DELAY 0x00000198 + +#define REG_DSI_7nm_PHY_PLL_CLOCK_INVERTERS 0x0000019c + +#define REG_DSI_7nm_PHY_PLL_SPARE_AND_JPC_OVERRIDES 0x000001a0 + +#define REG_DSI_7nm_PHY_PLL_BIAS_CONTROL_1 0x000001a4 + +#define REG_DSI_7nm_PHY_PLL_BIAS_CONTROL_2 0x000001a8 + +#define REG_DSI_7nm_PHY_PLL_ALOG_OBSV_BUS_CTRL_1 0x000001ac + +#define REG_DSI_7nm_PHY_PLL_COMMON_STATUS_ONE 0x000001b0 + +#define REG_DSI_7nm_PHY_PLL_COMMON_STATUS_TWO 0x000001b4 + +#define REG_DSI_7nm_PHY_PLL_BAND_SEL_CAL 0x000001b8 + +#define REG_DSI_7nm_PHY_PLL_ICODE_ACCUM_STATUS_LOW 0x000001bc + +#define REG_DSI_7nm_PHY_PLL_ICODE_ACCUM_STATUS_HIGH 0x000001c0 + +#define REG_DSI_7nm_PHY_PLL_FD_OUT_LOW 0x000001c4 + +#define REG_DSI_7nm_PHY_PLL_FD_OUT_HIGH 0x000001c8 + +#define REG_DSI_7nm_PHY_PLL_ALOG_OBSV_BUS_STATUS_1 0x000001cc + +#define REG_DSI_7nm_PHY_PLL_PLL_MISC_CONFIG 0x000001d0 + +#define REG_DSI_7nm_PHY_PLL_FLL_CONFIG 0x000001d4 + +#define REG_DSI_7nm_PHY_PLL_FLL_FREQ_ACQ_TIME 0x000001d8 + +#define REG_DSI_7nm_PHY_PLL_FLL_CODE0 0x000001dc + +#define REG_DSI_7nm_PHY_PLL_FLL_CODE1 0x000001e0 + +#define REG_DSI_7nm_PHY_PLL_FLL_GAIN0 0x000001e4 + +#define REG_DSI_7nm_PHY_PLL_FLL_GAIN1 0x000001e8 + +#define REG_DSI_7nm_PHY_PLL_SW_RESET 0x000001ec + +#define REG_DSI_7nm_PHY_PLL_FAST_PWRUP 0x000001f0 + +#define REG_DSI_7nm_PHY_PLL_LOCKTIME0 0x000001f4 + +#define REG_DSI_7nm_PHY_PLL_LOCKTIME1 0x000001f8 + +#define REG_DSI_7nm_PHY_PLL_DEBUG_BUS_SEL 0x000001fc + +#define REG_DSI_7nm_PHY_PLL_DEBUG_BUS0 0x00000200 + +#define REG_DSI_7nm_PHY_PLL_DEBUG_BUS1 0x00000204 + +#define REG_DSI_7nm_PHY_PLL_DEBUG_BUS2 0x00000208 + +#define REG_DSI_7nm_PHY_PLL_DEBUG_BUS3 0x0000020c + +#define REG_DSI_7nm_PHY_PLL_ANALOG_FLL_CONTROL_OVERRIDES 0x00000210 + +#define REG_DSI_7nm_PHY_PLL_VCO_CONFIG 0x00000214 + +#define REG_DSI_7nm_PHY_PLL_VCO_CAL_CODE1_MODE0_STATUS 0x00000218 + +#define REG_DSI_7nm_PHY_PLL_VCO_CAL_CODE1_MODE1_STATUS 0x0000021c + +#define REG_DSI_7nm_PHY_PLL_RESET_SM_STATUS 0x00000220 + +#define REG_DSI_7nm_PHY_PLL_TDC_OFFSET 0x00000224 + +#define REG_DSI_7nm_PHY_PLL_PS3_PWRDOWN_CONTROLS 0x00000228 + +#define REG_DSI_7nm_PHY_PLL_PS4_PWRDOWN_CONTROLS 0x0000022c + +#define REG_DSI_7nm_PHY_PLL_PLL_RST_CONTROLS 0x00000230 + +#define REG_DSI_7nm_PHY_PLL_GEAR_BAND_SELECT_CONTROLS 0x00000234 + +#define REG_DSI_7nm_PHY_PLL_PSM_CLK_CONTROLS 0x00000238 + +#define REG_DSI_7nm_PHY_PLL_SYSTEM_MUXES_2 0x0000023c + +#define REG_DSI_7nm_PHY_PLL_VCO_CONFIG_1 0x00000240 + +#define REG_DSI_7nm_PHY_PLL_VCO_CONFIG_2 0x00000244 + +#define REG_DSI_7nm_PHY_PLL_CLOCK_INVERTERS_1 0x00000248 + +#define REG_DSI_7nm_PHY_PLL_CLOCK_INVERTERS_2 0x0000024c + +#define REG_DSI_7nm_PHY_PLL_CMODE_1 0x00000250 + +#define REG_DSI_7nm_PHY_PLL_CMODE_2 0x00000254 + +#define REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_FIVE_1 0x00000258 + +#define REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_FIVE_2 0x0000025c + #endif /* DSI_XML */ diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.c b/drivers/gpu/drm/msm/dsi/dsi_cfg.c index f892f2cbe8bb..b2ff68a15791 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_cfg.c +++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.c @@ -265,9 +265,12 @@ static const struct msm_dsi_cfg_handler dsi_cfg_handlers[] = { &msm8998_dsi_cfg, &msm_dsi_6g_v2_host_ops}, {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_2_1, &sdm845_dsi_cfg, &msm_dsi_6g_v2_host_ops}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_3_0, + &sdm845_dsi_cfg, &msm_dsi_6g_v2_host_ops}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_4_0, + &sdm845_dsi_cfg, &msm_dsi_6g_v2_host_ops}, {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V2_4_1, &sc7180_dsi_cfg, &msm_dsi_6g_v2_host_ops}, - }; const struct msm_dsi_cfg_handler *msm_dsi_cfg_get(u32 major, u32 minor) diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.h b/drivers/gpu/drm/msm/dsi/dsi_cfg.h index efd469d1db45..ade9b609c7d9 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_cfg.h +++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.h @@ -21,6 +21,8 @@ #define MSM_DSI_6G_VER_MINOR_V2_1_0 0x20010000 #define MSM_DSI_6G_VER_MINOR_V2_2_0 0x20000000 #define MSM_DSI_6G_VER_MINOR_V2_2_1 0x20020001 +#define MSM_DSI_6G_VER_MINOR_V2_3_0 0x20030000 +#define MSM_DSI_6G_VER_MINOR_V2_4_0 0x20040000 #define MSM_DSI_6G_VER_MINOR_V2_4_1 0x20040001 #define MSM_DSI_V2_VER_MINOR_8064 0x0 diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index b17ac6c27554..6d9e65422a1c 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -1936,7 +1936,8 @@ void msm_dsi_host_destroy(struct mipi_dsi_host *host) if (msm_host->has_opp_table) dev_pm_opp_of_remove_table(&msm_host->pdev->dev); - dev_pm_opp_put_clkname(msm_host->opp_table); + if (msm_host->opp_table) + dev_pm_opp_put_clkname(msm_host->opp_table); pm_runtime_disable(&msm_host->pdev->dev); } diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index 4b363bd7ddff..72cfd0a8187b 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -3,6 +3,7 @@ * Copyright (c) 2015, The Linux Foundation. All rights reserved. */ +#include <drm/drm_bridge_connector.h> #include "msm_kms.h" #include "dsi.h" @@ -689,7 +690,7 @@ struct drm_bridge *msm_dsi_manager_bridge_init(u8 id) bridge = &dsi_bridge->base; bridge->funcs = &dsi_mgr_bridge_funcs; - ret = drm_bridge_attach(encoder, bridge, NULL, 0); + ret = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); if (ret) goto fail; @@ -709,7 +710,6 @@ struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id) struct drm_encoder *encoder; struct drm_bridge *int_bridge, *ext_bridge; struct drm_connector *connector; - struct list_head *connector_list; int_bridge = msm_dsi->bridge; ext_bridge = msm_dsi->external_bridge = @@ -717,22 +717,21 @@ struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id) encoder = msm_dsi->encoder; - /* link the internal dsi bridge to the external bridge */ - drm_bridge_attach(encoder, ext_bridge, int_bridge, 0); - - /* - * we need the drm_connector created by the external bridge - * driver (or someone else) to feed it to our driver's - * priv->connector[] list, mainly for msm_fbdev_init() + /* link the internal dsi bridge to the external bridge and attach + * the connector, we are supporting DRM_BRIDGE_ATTACH_NO_CONNECTOR + * so always create connector */ - connector_list = &dev->mode_config.connector_list; + drm_bridge_attach(encoder, ext_bridge, int_bridge, DRM_BRIDGE_ATTACH_NO_CONNECTOR); - list_for_each_entry(connector, connector_list, head) { - if (drm_connector_has_possible_encoder(connector, encoder)) - return connector; + connector = drm_bridge_connector_init(dev, encoder); + if (IS_ERR(connector)) { + DRM_DEV_ERROR(dev->dev, "drm_bridge_connector_init failed: %ld\n", + PTR_ERR(connector)); + return ERR_PTR(-ENODEV); } - return ERR_PTR(-ENODEV); + drm_connector_attach_encoder(connector, msm_dsi->encoder); + return connector; } void msm_dsi_manager_bridge_destroy(struct drm_bridge *bridge) diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c index 009f5b843dd1..7059ae6da772 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c @@ -364,6 +364,145 @@ int msm_dsi_dphy_timing_calc_v3(struct msm_dsi_dphy_timing *timing, return 0; } +static s64 cal_clk_pulse_time(u32 inp1, u32 inp2, u32 bitclk_mbps) +{ + u64 const multiplier = BIT(20); + u64 clk_multiple; + s32 frac; + s64 temp, result; + + clk_multiple = div_s64((inp1 * multiplier * 1000), bitclk_mbps); + div_s64_rem(clk_multiple, multiplier, &frac); + temp = (inp2 * multiplier) + (clk_multiple + frac); + result = div_s64(temp, multiplier); + + return result; +} + +int msm_dsi_dphy_timing_calc_v4(struct msm_dsi_dphy_timing *timing, + struct msm_dsi_phy_clk_request *clk_req) +{ + const unsigned long bit_rate = clk_req->bitclk_rate; + const unsigned long esc_rate = clk_req->escclk_rate; + s32 ui, ui_x8; + s32 tmax, tmin; + s32 pcnt0 = 50; + s32 pcnt1 = 50; + s32 pcnt2 = 10; + s32 pcnt3 = 30; + s32 pcnt4 = 10; + s32 pcnt5 = 2; + s32 coeff = 1000; /* Precision, should avoid overflow */ + s32 hb_en, hb_en_ckln; + s32 temp; + u32 bitclk_mbps = 929; + + if (!bit_rate || !esc_rate) + return -EINVAL; + + timing->hs_halfbyte_en = 0; + hb_en = 0; + timing->hs_halfbyte_en_ckln = 0; + hb_en_ckln = 0; + + ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000); + ui_x8 = ui << 3; + + temp = S_DIV_ROUND_UP(38 * coeff, ui_x8); + tmin = max_t(s32, temp, 0); + temp = (95 * coeff) / ui_x8; + tmax = max_t(s32, temp, 0); + timing->clk_prepare = linear_inter(tmax, tmin, pcnt0, 0, false); + + temp = 300 * coeff - (timing->clk_prepare << 3) * ui; + tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1; + tmax = (tmin > 255) ? 511 : 255; + timing->clk_zero = linear_inter(tmax, tmin, pcnt5, 0, false); + + tmin = DIV_ROUND_UP(60 * coeff + 3 * ui, ui_x8); + temp = 105 * coeff + 12 * ui - 20 * coeff; + tmax = (temp + 3 * ui) / ui_x8; + timing->clk_trail = linear_inter(tmax, tmin, pcnt3, 0, false); + + temp = S_DIV_ROUND_UP(40 * coeff + 4 * ui, ui_x8); + tmin = max_t(s32, temp, 0); + temp = (85 * coeff + 6 * ui) / ui_x8; + tmax = max_t(s32, temp, 0); + timing->hs_prepare = linear_inter(tmax, tmin, pcnt1, 0, false); + + temp = 145 * coeff + 10 * ui - (timing->hs_prepare << 3) * ui; + tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1; + tmax = 255; + timing->hs_zero = linear_inter(tmax, tmin, pcnt4, 0, false); + + tmin = DIV_ROUND_UP(60 * coeff + 4 * ui, ui_x8) - 1; + temp = 105 * coeff + 12 * ui - 20 * coeff; + tmax = (temp / ui_x8) - 1; + timing->hs_trail = linear_inter(tmax, tmin, pcnt3, 0, false); + + temp = 50 * coeff + ((hb_en << 2) - 8) * ui; + timing->hs_rqst = S_DIV_ROUND_UP(temp, ui_x8); + + tmin = DIV_ROUND_UP(100 * coeff, ui_x8) - 1; + tmax = 255; + timing->hs_exit = linear_inter(tmax, tmin, pcnt2, 0, false); + + temp = 50 * coeff + ((hb_en_ckln << 2) - 8) * ui; + timing->hs_rqst_ckln = S_DIV_ROUND_UP(temp, ui_x8); + + temp = 60 * coeff + 52 * ui - 43 * ui; + tmin = DIV_ROUND_UP(temp, ui_x8) - 1; + tmax = 63; + timing->shared_timings.clk_post = + linear_inter(tmax, tmin, pcnt2, 0, false); + + temp = 8 * ui + (timing->clk_prepare << 3) * ui; + temp += (((timing->clk_zero + 3) << 3) + 11) * ui; + temp += hb_en_ckln ? (((timing->hs_rqst_ckln << 3) + 4) * ui) : + (((timing->hs_rqst_ckln << 3) + 8) * ui); + tmin = S_DIV_ROUND_UP(temp, ui_x8) - 1; + tmax = 63; + if (tmin > tmax) { + temp = linear_inter(tmax << 1, tmin, pcnt2, 0, false); + timing->shared_timings.clk_pre = temp >> 1; + timing->shared_timings.clk_pre_inc_by_2 = 1; + } else { + timing->shared_timings.clk_pre = + linear_inter(tmax, tmin, pcnt2, 0, false); + timing->shared_timings.clk_pre_inc_by_2 = 0; + } + + timing->ta_go = 3; + timing->ta_sure = 0; + timing->ta_get = 4; + + temp = cal_clk_pulse_time((timing->hs_trail + 1) * 8, 0, bitclk_mbps) + + cal_clk_pulse_time(52, 60, bitclk_mbps); + + temp = div_s64(temp, cal_clk_pulse_time(16, 0, bitclk_mbps)) - 1; + + timing->clk_post = DIV_ROUND_UP(1275 + temp * 95, 100); + + temp = cal_clk_pulse_time(timing->clk_prepare * 8, 0, bitclk_mbps) + + cal_clk_pulse_time((timing->clk_zero + 1) * 8, 0, bitclk_mbps) + + 52 + 54; + + temp = div_s64(temp, cal_clk_pulse_time(16, 0, bitclk_mbps)) - 1; + + timing->clk_pre = DIV_ROUND_UP(31875 + temp * 9875, 10000); + + DBG("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + timing->shared_timings.clk_pre, timing->shared_timings.clk_post, + timing->shared_timings.clk_pre_inc_by_2, timing->clk_zero, + timing->clk_trail, timing->clk_prepare, timing->hs_exit, + timing->hs_zero, timing->hs_prepare, timing->hs_trail, + timing->hs_rqst, timing->hs_rqst_ckln, timing->hs_halfbyte_en, + timing->hs_halfbyte_en_ckln, timing->hs_prep_dly, + timing->hs_prep_dly_ckln); + + return 0; +} + void msm_dsi_phy_set_src_pll(struct msm_dsi_phy *phy, int pll_id, u32 reg, u32 bit_mask) { @@ -508,6 +647,10 @@ static const struct of_device_id dsi_phy_dt_match[] = { { .compatible = "qcom,dsi-phy-10nm-8998", .data = &dsi_phy_10nm_8998_cfgs }, #endif +#ifdef CONFIG_DRM_MSM_DSI_7NM_PHY + { .compatible = "qcom,dsi-phy-7nm", + .data = &dsi_phy_7nm_cfgs }, +#endif {} }; diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h index ef8672d7b123..26e4098b7237 100644 --- a/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h @@ -48,6 +48,7 @@ extern const struct msm_dsi_phy_cfg dsi_phy_14nm_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_14nm_660_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_10nm_cfgs; extern const struct msm_dsi_phy_cfg dsi_phy_10nm_8998_cfgs; +extern const struct msm_dsi_phy_cfg dsi_phy_7nm_cfgs; struct msm_dsi_dphy_timing { u32 clk_pre; @@ -102,6 +103,8 @@ int msm_dsi_dphy_timing_calc_v2(struct msm_dsi_dphy_timing *timing, struct msm_dsi_phy_clk_request *clk_req); int msm_dsi_dphy_timing_calc_v3(struct msm_dsi_dphy_timing *timing, struct msm_dsi_phy_clk_request *clk_req); +int msm_dsi_dphy_timing_calc_v4(struct msm_dsi_dphy_timing *timing, + struct msm_dsi_phy_clk_request *clk_req); void msm_dsi_phy_set_src_pll(struct msm_dsi_phy *phy, int pll_id, u32 reg, u32 bit_mask); int msm_dsi_phy_init_common(struct msm_dsi_phy *phy); diff --git a/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c new file mode 100644 index 000000000000..1da9742e79be --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/phy/dsi_phy_7nm.c @@ -0,0 +1,217 @@ +/* + * SPDX-License-Identifier: GPL-2.0 + * Copyright (c) 2018, The Linux Foundation + */ + +#include <linux/iopoll.h> + +#include "dsi_phy.h" +#include "dsi.xml.h" + +static int dsi_phy_hw_v4_0_is_pll_on(struct msm_dsi_phy *phy) +{ + void __iomem *base = phy->base; + u32 data = 0; + + data = dsi_phy_read(base + REG_DSI_7nm_PHY_CMN_PLL_CNTRL); + mb(); /* make sure read happened */ + + return (data & BIT(0)); +} + +static void dsi_phy_hw_v4_0_config_lpcdrx(struct msm_dsi_phy *phy, bool enable) +{ + void __iomem *lane_base = phy->lane_base; + int phy_lane_0 = 0; /* TODO: Support all lane swap configs */ + + /* + * LPRX and CDRX need to enabled only for physical data lane + * corresponding to the logical data lane 0 + */ + if (enable) + dsi_phy_write(lane_base + + REG_DSI_7nm_PHY_LN_LPRX_CTRL(phy_lane_0), 0x3); + else + dsi_phy_write(lane_base + + REG_DSI_7nm_PHY_LN_LPRX_CTRL(phy_lane_0), 0); +} + +static void dsi_phy_hw_v4_0_lane_settings(struct msm_dsi_phy *phy) +{ + int i; + u8 cfg2[] = { 0x0a, 0x0a, 0x0a, 0x0a, 0x8a }; + u8 tx_dctrl[] = { 0x40, 0x40, 0x40, 0x46, 0x41 }; + void __iomem *lane_base = phy->lane_base; + + /* Strength ctrl settings */ + for (i = 0; i < 5; i++) { + /* + * Disable LPRX and CDRX for all lanes. And later on, it will + * be only enabled for the physical data lane corresponding + * to the logical data lane 0 + */ + dsi_phy_write(lane_base + REG_DSI_7nm_PHY_LN_LPRX_CTRL(i), 0); + dsi_phy_write(lane_base + REG_DSI_7nm_PHY_LN_PIN_SWAP(i), 0x0); + } + + dsi_phy_hw_v4_0_config_lpcdrx(phy, true); + + /* other settings */ + for (i = 0; i < 5; i++) { + dsi_phy_write(lane_base + REG_DSI_7nm_PHY_LN_CFG0(i), 0x0); + dsi_phy_write(lane_base + REG_DSI_7nm_PHY_LN_CFG1(i), 0x0); + dsi_phy_write(lane_base + REG_DSI_7nm_PHY_LN_CFG2(i), cfg2[i]); + dsi_phy_write(lane_base + REG_DSI_7nm_PHY_LN_TX_DCTRL(i), + tx_dctrl[i]); + } + + // TODO: if force_clk_lane_hs? +} + +static int dsi_7nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, + struct msm_dsi_phy_clk_request *clk_req) +{ + int ret; + u32 status; + u32 const delay_us = 5; + u32 const timeout_us = 1000; + struct msm_dsi_dphy_timing *timing = &phy->timing; + void __iomem *base = phy->base; + u32 data; + + DBG(""); + + if (msm_dsi_dphy_timing_calc_v4(timing, clk_req)) { + DRM_DEV_ERROR(&phy->pdev->dev, + "%s: D-PHY timing calculation failed\n", __func__); + return -EINVAL; + } + + if (dsi_phy_hw_v4_0_is_pll_on(phy)) + pr_warn("PLL turned on before configuring PHY\n"); + + /* wait for REFGEN READY */ + ret = readl_poll_timeout_atomic(base + REG_DSI_7nm_PHY_CMN_PHY_STATUS, + status, (status & BIT(0)), + delay_us, timeout_us); + if (ret) { + pr_err("Ref gen not ready. Aborting\n"); + return -EINVAL; + } + + /* de-assert digital and pll power down */ + data = BIT(6) | BIT(5); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_CTRL_0, data); + + /* Assert PLL core reset */ + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_PLL_CNTRL, 0x00); + + /* turn off resync FIFO */ + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_RBUF_CTRL, 0x00); + + /* program CMN_CTRL_4 for minor_ver 2 chipsets*/ + data = dsi_phy_read(base + REG_DSI_7nm_PHY_CMN_REVISION_ID0); + data = data & (0xf0); + if (data == 0x20) + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_CTRL_4, 0x04); + + /* Configure PHY lane swap (TODO: we need to calculate this) */ + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_LANE_CFG0, 0x21); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_LANE_CFG1, 0x84); + + /* Enable LDO */ + // TODO: less than 1500mhz check + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_VREG_CTRL_0, 0x52); // 0x53 + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_VREG_CTRL_1, 0x5c); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_CTRL_3, 0x00); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_GLBL_STR_SWI_CAL_SEL_CTRL, 0x00); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_GLBL_HSTX_STR_CTRL_0, 0x88); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_GLBL_PEMPH_CTRL_0, 0x00); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_GLBL_RESCODE_OFFSET_TOP_CTRL, 0x00); // 0x3d + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_GLBL_RESCODE_OFFSET_BOT_CTRL, 0x3c); // 0x39 + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_GLBL_LPTX_STR_CTRL, 0x55); + + /* Remove power down from all blocks */ + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_CTRL_0, 0x7f); + + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_LANE_CTRL0, 0x1F); + + /* Select full-rate mode */ + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_CTRL_2, 0x40); + + ret = msm_dsi_pll_set_usecase(phy->pll, phy->usecase); + if (ret) { + DRM_DEV_ERROR(&phy->pdev->dev, "%s: set pll usecase failed, %d\n", + __func__, ret); + return ret; + } + + /* DSI PHY timings */ + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_0, + timing->hs_halfbyte_en); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_1, + timing->clk_zero); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_2, + timing->clk_prepare); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_3, + timing->clk_trail); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_4, + timing->hs_exit); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_5, + timing->hs_zero); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_6, + timing->hs_prepare); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_7, + timing->hs_trail); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_8, + timing->hs_rqst); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_9, 0x02); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_10, 0x04); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_11, 0x00); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_12, timing->clk_pre); + dsi_phy_write(base + REG_DSI_7nm_PHY_CMN_TIMING_CTRL_13, timing->clk_post); + + /* DSI lane settings */ + dsi_phy_hw_v4_0_lane_settings(phy); + + DBG("DSI%d PHY enabled", phy->id); + + return 0; +} + +static void dsi_7nm_phy_disable(struct msm_dsi_phy *phy) +{ +} + +static int dsi_7nm_phy_init(struct msm_dsi_phy *phy) +{ + struct platform_device *pdev = phy->pdev; + + phy->lane_base = msm_ioremap(pdev, "dsi_phy_lane", + "DSI_PHY_LANE"); + if (IS_ERR(phy->lane_base)) { + DRM_DEV_ERROR(&pdev->dev, "%s: failed to map phy lane base\n", + __func__); + return -ENOMEM; + } + + return 0; +} + +const struct msm_dsi_phy_cfg dsi_phy_7nm_cfgs = { + .type = MSM_DSI_PHY_7NM, + .src_pll_truthtable = { {false, false}, {true, false} }, + .reg_cfg = { + .num = 1, + .regs = { + {"vdds", 36000, 32}, + }, + }, + .ops = { + .enable = dsi_7nm_phy_enable, + .disable = dsi_7nm_phy_disable, + .init = dsi_7nm_phy_init, + }, + .io_start = { 0xae94400, 0xae96400 }, + .num_dsi_phy = 2, +}; diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c index 4a4aa3c61d71..3865ab5ace16 100644 --- a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c @@ -161,6 +161,9 @@ struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev, case MSM_DSI_PHY_10NM: pll = msm_dsi_pll_10nm_init(pdev, id); break; + case MSM_DSI_PHY_7NM: + pll = msm_dsi_pll_7nm_init(pdev, id); + break; default: pll = ERR_PTR(-ENXIO); break; diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h index c6a3623f905d..3405982a092c 100644 --- a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h @@ -116,5 +116,15 @@ msm_dsi_pll_10nm_init(struct platform_device *pdev, int id) return ERR_PTR(-ENODEV); } #endif +#ifdef CONFIG_DRM_MSM_DSI_7NM_PHY +struct msm_dsi_pll *msm_dsi_pll_7nm_init(struct platform_device *pdev, int id); +#else +static inline struct msm_dsi_pll * +msm_dsi_pll_7nm_init(struct platform_device *pdev, int id) +{ + return ERR_PTR(-ENODEV); +} +#endif + #endif /* __DSI_PLL_H__ */ diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_7nm.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_7nm.c new file mode 100644 index 000000000000..953660cec0cd --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_7nm.c @@ -0,0 +1,904 @@ +/* + * SPDX-License-Identifier: GPL-2.0 + * Copyright (c) 2018, The Linux Foundation + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/iopoll.h> + +#include "dsi_pll.h" +#include "dsi.xml.h" + +/* + * DSI PLL 7nm - clock diagram (eg: DSI0): + * + * dsi0_pll_out_div_clk dsi0_pll_bit_clk + * | | + * | | + * +---------+ | +----------+ | +----+ + * dsi0vco_clk ---| out_div |--o--| divl_3_0 |--o--| /8 |-- dsi0_phy_pll_out_byteclk + * +---------+ | +----------+ | +----+ + * | | + * | | dsi0_pll_by_2_bit_clk + * | | | + * | | +----+ | |\ dsi0_pclk_mux + * | |--| /2 |--o--| \ | + * | | +----+ | \ | +---------+ + * | --------------| |--o--| div_7_4 |-- dsi0_phy_pll_out_dsiclk + * |------------------------------| / +---------+ + * | +-----+ | / + * -----------| /4? |--o----------|/ + * +-----+ | | + * | |dsiclk_sel + * | + * dsi0_pll_post_out_div_clk + */ + +#define DSI_BYTE_PLL_CLK 0 +#define DSI_PIXEL_PLL_CLK 1 +#define NUM_PROVIDED_CLKS 2 + +#define VCO_REF_CLK_RATE 19200000 + +struct dsi_pll_regs { + u32 pll_prop_gain_rate; + u32 pll_lockdet_rate; + u32 decimal_div_start; + u32 frac_div_start_low; + u32 frac_div_start_mid; + u32 frac_div_start_high; + u32 pll_clock_inverters; + u32 ssc_stepsize_low; + u32 ssc_stepsize_high; + u32 ssc_div_per_low; + u32 ssc_div_per_high; + u32 ssc_adjper_low; + u32 ssc_adjper_high; + u32 ssc_control; +}; + +struct dsi_pll_config { + u32 ref_freq; + bool div_override; + u32 output_div; + bool ignore_frac; + bool disable_prescaler; + bool enable_ssc; + bool ssc_center; + u32 dec_bits; + u32 frac_bits; + u32 lock_timer; + u32 ssc_freq; + u32 ssc_offset; + u32 ssc_adj_per; + u32 thresh_cycles; + u32 refclk_cycles; +}; + +struct pll_7nm_cached_state { + unsigned long vco_rate; + u8 bit_clk_div; + u8 pix_clk_div; + u8 pll_out_div; + u8 pll_mux; +}; + +struct dsi_pll_7nm { + struct msm_dsi_pll base; + + int id; + struct platform_device *pdev; + + void __iomem *phy_cmn_mmio; + void __iomem *mmio; + + u64 vco_ref_clk_rate; + u64 vco_current_rate; + + /* protects REG_DSI_7nm_PHY_CMN_CLK_CFG0 register */ + spinlock_t postdiv_lock; + + int vco_delay; + struct dsi_pll_config pll_configuration; + struct dsi_pll_regs reg_setup; + + /* private clocks: */ + struct clk_hw *out_div_clk_hw; + struct clk_hw *bit_clk_hw; + struct clk_hw *byte_clk_hw; + struct clk_hw *by_2_bit_clk_hw; + struct clk_hw *post_out_div_clk_hw; + struct clk_hw *pclk_mux_hw; + struct clk_hw *out_dsiclk_hw; + + /* clock-provider: */ + struct clk_hw_onecell_data *hw_data; + + struct pll_7nm_cached_state cached_state; + + enum msm_dsi_phy_usecase uc; + struct dsi_pll_7nm *slave; +}; + +#define to_pll_7nm(x) container_of(x, struct dsi_pll_7nm, base) + +/* + * Global list of private DSI PLL struct pointers. We need this for Dual DSI + * mode, where the master PLL's clk_ops needs access the slave's private data + */ +static struct dsi_pll_7nm *pll_7nm_list[DSI_MAX]; + +static void dsi_pll_setup_config(struct dsi_pll_7nm *pll) +{ + struct dsi_pll_config *config = &pll->pll_configuration; + + config->ref_freq = pll->vco_ref_clk_rate; + config->output_div = 1; + config->dec_bits = 8; + config->frac_bits = 18; + config->lock_timer = 64; + config->ssc_freq = 31500; + config->ssc_offset = 4800; + config->ssc_adj_per = 2; + config->thresh_cycles = 32; + config->refclk_cycles = 256; + + config->div_override = false; + config->ignore_frac = false; + config->disable_prescaler = false; + + config->enable_ssc = false; + config->ssc_center = 0; +} + +static void dsi_pll_calc_dec_frac(struct dsi_pll_7nm *pll) +{ + struct dsi_pll_config *config = &pll->pll_configuration; + struct dsi_pll_regs *regs = &pll->reg_setup; + u64 fref = pll->vco_ref_clk_rate; + u64 pll_freq; + u64 divider; + u64 dec, dec_multiple; + u32 frac; + u64 multiplier; + + pll_freq = pll->vco_current_rate; + + if (config->disable_prescaler) + divider = fref; + else + divider = fref * 2; + + multiplier = 1 << config->frac_bits; + dec_multiple = div_u64(pll_freq * multiplier, divider); + div_u64_rem(dec_multiple, multiplier, &frac); + + dec = div_u64(dec_multiple, multiplier); + + if (pll_freq <= 1000000000ULL) + regs->pll_clock_inverters = 0xA0; + else if (pll_freq <= 2500000000ULL) + regs->pll_clock_inverters = 0x20; + else if (pll_freq <= 3020000000ULL) + regs->pll_clock_inverters = 0x00; + else + regs->pll_clock_inverters = 0x40; + + regs->pll_lockdet_rate = config->lock_timer; + regs->decimal_div_start = dec; + regs->frac_div_start_low = (frac & 0xff); + regs->frac_div_start_mid = (frac & 0xff00) >> 8; + regs->frac_div_start_high = (frac & 0x30000) >> 16; +} + +#define SSC_CENTER BIT(0) +#define SSC_EN BIT(1) + +static void dsi_pll_calc_ssc(struct dsi_pll_7nm *pll) +{ + struct dsi_pll_config *config = &pll->pll_configuration; + struct dsi_pll_regs *regs = &pll->reg_setup; + u32 ssc_per; + u32 ssc_mod; + u64 ssc_step_size; + u64 frac; + + if (!config->enable_ssc) { + DBG("SSC not enabled\n"); + return; + } + + ssc_per = DIV_ROUND_CLOSEST(config->ref_freq, config->ssc_freq) / 2 - 1; + ssc_mod = (ssc_per + 1) % (config->ssc_adj_per + 1); + ssc_per -= ssc_mod; + + frac = regs->frac_div_start_low | + (regs->frac_div_start_mid << 8) | + (regs->frac_div_start_high << 16); + ssc_step_size = regs->decimal_div_start; + ssc_step_size *= (1 << config->frac_bits); + ssc_step_size += frac; + ssc_step_size *= config->ssc_offset; + ssc_step_size *= (config->ssc_adj_per + 1); + ssc_step_size = div_u64(ssc_step_size, (ssc_per + 1)); + ssc_step_size = DIV_ROUND_CLOSEST_ULL(ssc_step_size, 1000000); + + regs->ssc_div_per_low = ssc_per & 0xFF; + regs->ssc_div_per_high = (ssc_per & 0xFF00) >> 8; + regs->ssc_stepsize_low = (u32)(ssc_step_size & 0xFF); + regs->ssc_stepsize_high = (u32)((ssc_step_size & 0xFF00) >> 8); + regs->ssc_adjper_low = config->ssc_adj_per & 0xFF; + regs->ssc_adjper_high = (config->ssc_adj_per & 0xFF00) >> 8; + + regs->ssc_control = config->ssc_center ? SSC_CENTER : 0; + + pr_debug("SCC: Dec:%d, frac:%llu, frac_bits:%d\n", + regs->decimal_div_start, frac, config->frac_bits); + pr_debug("SSC: div_per:0x%X, stepsize:0x%X, adjper:0x%X\n", + ssc_per, (u32)ssc_step_size, config->ssc_adj_per); +} + +static void dsi_pll_ssc_commit(struct dsi_pll_7nm *pll) +{ + void __iomem *base = pll->mmio; + struct dsi_pll_regs *regs = &pll->reg_setup; + + if (pll->pll_configuration.enable_ssc) { + pr_debug("SSC is enabled\n"); + + pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_STEPSIZE_LOW_1, + regs->ssc_stepsize_low); + pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_STEPSIZE_HIGH_1, + regs->ssc_stepsize_high); + pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_DIV_PER_LOW_1, + regs->ssc_div_per_low); + pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_DIV_PER_HIGH_1, + regs->ssc_div_per_high); + pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_ADJPER_LOW_1, + regs->ssc_adjper_low); + pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_ADJPER_HIGH_1, + regs->ssc_adjper_high); + pll_write(base + REG_DSI_7nm_PHY_PLL_SSC_CONTROL, + SSC_EN | regs->ssc_control); + } +} + +static void dsi_pll_config_hzindep_reg(struct dsi_pll_7nm *pll) +{ + void __iomem *base = pll->mmio; + u64 vco_rate = pll->vco_current_rate; + + if (vco_rate < 3100000000ULL) + pll_write(base + REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_FIVE_1, 0x01); + else + pll_write(base + REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_FIVE_1, 0x03); + + if (vco_rate < 1520000000ULL) + pll_write(base + REG_DSI_7nm_PHY_PLL_VCO_CONFIG_1, 0x08); + else if (vco_rate < 2990000000ULL) + pll_write(base + REG_DSI_7nm_PHY_PLL_VCO_CONFIG_1, 0x01); + else + pll_write(base + REG_DSI_7nm_PHY_PLL_VCO_CONFIG_1, 0x00); + + pll_write(base + REG_DSI_7nm_PHY_PLL_GEAR_BAND_SELECT_CONTROLS, 0x22); + pll_write(base + REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_FIVE, 0x01); + pll_write(base + REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_TWO, 0x03); + pll_write(base + REG_DSI_7nm_PHY_PLL_ANALOG_CONTROLS_THREE, 0x00); + pll_write(base + REG_DSI_7nm_PHY_PLL_DSM_DIVIDER, 0x00); + pll_write(base + REG_DSI_7nm_PHY_PLL_FEEDBACK_DIVIDER, 0x4e); + pll_write(base + REG_DSI_7nm_PHY_PLL_CALIBRATION_SETTINGS, 0x40); + pll_write(base + REG_DSI_7nm_PHY_PLL_BAND_SEL_CAL_SETTINGS_THREE, 0xba); + pll_write(base + REG_DSI_7nm_PHY_PLL_FREQ_DETECT_SETTINGS_ONE, 0x0c); + pll_write(base + REG_DSI_7nm_PHY_PLL_OUTDIV, 0x00); + pll_write(base + REG_DSI_7nm_PHY_PLL_CORE_OVERRIDE, 0x00); + pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_DIGITAL_TIMERS_TWO, 0x08); + pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_PROP_GAIN_RATE_1, 0x0a); + pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_BAND_SEL_RATE_1, 0xc0); + pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_INT_GAIN_IFILT_BAND_1, 0x84); + pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_INT_GAIN_IFILT_BAND_1, 0x82); + pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_FL_INT_GAIN_PFILT_BAND_1, 0x4c); + pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_LOCK_OVERRIDE, 0x80); + pll_write(base + REG_DSI_7nm_PHY_PLL_PFILT, 0x29); + pll_write(base + REG_DSI_7nm_PHY_PLL_PFILT, 0x2f); + pll_write(base + REG_DSI_7nm_PHY_PLL_IFILT, 0x2a); + pll_write(base + REG_DSI_7nm_PHY_PLL_IFILT, 0x3f); +#define REG_DSI_7nm_PHY_PLL_PERF_OPTIMIZE 0x0260 + pll_write(base + REG_DSI_7nm_PHY_PLL_PERF_OPTIMIZE, 0x22); + if (pll->slave) + pll_write(pll->slave->mmio + REG_DSI_7nm_PHY_PLL_PERF_OPTIMIZE, 0x22); +} + +static void dsi_pll_commit(struct dsi_pll_7nm *pll) +{ + void __iomem *base = pll->mmio; + struct dsi_pll_regs *reg = &pll->reg_setup; + + pll_write(base + REG_DSI_7nm_PHY_PLL_CORE_INPUT_OVERRIDE, 0x12); + pll_write(base + REG_DSI_7nm_PHY_PLL_DECIMAL_DIV_START_1, + reg->decimal_div_start); + pll_write(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_LOW_1, + reg->frac_div_start_low); + pll_write(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_MID_1, + reg->frac_div_start_mid); + pll_write(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_HIGH_1, + reg->frac_div_start_high); + pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_LOCKDET_RATE_1, 0x40); + pll_write(base + REG_DSI_7nm_PHY_PLL_PLL_LOCK_DELAY, 0x06); + pll_write(base + REG_DSI_7nm_PHY_PLL_CMODE_1, 0x10); + pll_write(base + REG_DSI_7nm_PHY_PLL_CLOCK_INVERTERS, + reg->pll_clock_inverters); +} + +static int dsi_pll_7nm_vco_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll); + + DBG("DSI PLL%d rate=%lu, parent's=%lu", pll_7nm->id, rate, + parent_rate); + + pll_7nm->vco_current_rate = rate; + pll_7nm->vco_ref_clk_rate = VCO_REF_CLK_RATE; + + dsi_pll_setup_config(pll_7nm); + + dsi_pll_calc_dec_frac(pll_7nm); + + dsi_pll_calc_ssc(pll_7nm); + + dsi_pll_commit(pll_7nm); + + dsi_pll_config_hzindep_reg(pll_7nm); + + dsi_pll_ssc_commit(pll_7nm); + + /* flush, ensure all register writes are done*/ + wmb(); + + return 0; +} + +static int dsi_pll_7nm_lock_status(struct dsi_pll_7nm *pll) +{ + int rc; + u32 status = 0; + u32 const delay_us = 100; + u32 const timeout_us = 5000; + + rc = readl_poll_timeout_atomic(pll->mmio + + REG_DSI_7nm_PHY_PLL_COMMON_STATUS_ONE, + status, + ((status & BIT(0)) > 0), + delay_us, + timeout_us); + if (rc) + pr_err("DSI PLL(%d) lock failed, status=0x%08x\n", + pll->id, status); + + return rc; +} + +static void dsi_pll_disable_pll_bias(struct dsi_pll_7nm *pll) +{ + u32 data = pll_read(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CTRL_0); + + pll_write(pll->mmio + REG_DSI_7nm_PHY_PLL_SYSTEM_MUXES, 0); + pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CTRL_0, + data & ~BIT(5)); + ndelay(250); +} + +static void dsi_pll_enable_pll_bias(struct dsi_pll_7nm *pll) +{ + u32 data = pll_read(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CTRL_0); + + pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CTRL_0, + data | BIT(5)); + pll_write(pll->mmio + REG_DSI_7nm_PHY_PLL_SYSTEM_MUXES, 0xc0); + ndelay(250); +} + +static void dsi_pll_disable_global_clk(struct dsi_pll_7nm *pll) +{ + u32 data; + + data = pll_read(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CLK_CFG1); + pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CLK_CFG1, + data & ~BIT(5)); +} + +static void dsi_pll_enable_global_clk(struct dsi_pll_7nm *pll) +{ + u32 data; + + pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CTRL_3, 0x04); + + data = pll_read(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CLK_CFG1); + pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_CLK_CFG1, + data | BIT(5) | BIT(4)); +} + +static void dsi_pll_phy_dig_reset(struct dsi_pll_7nm *pll) +{ + /* + * Reset the PHY digital domain. This would be needed when + * coming out of a CX or analog rail power collapse while + * ensuring that the pads maintain LP00 or LP11 state + */ + pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_GLBL_DIGTOP_SPARE4, BIT(0)); + wmb(); /* Ensure that the reset is deasserted */ + pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_GLBL_DIGTOP_SPARE4, 0x0); + wmb(); /* Ensure that the reset is deasserted */ +} + +static int dsi_pll_7nm_vco_prepare(struct clk_hw *hw) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll); + int rc; + + dsi_pll_enable_pll_bias(pll_7nm); + if (pll_7nm->slave) + dsi_pll_enable_pll_bias(pll_7nm->slave); + + /* Start PLL */ + pll_write(pll_7nm->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_PLL_CNTRL, + 0x01); + + /* + * ensure all PLL configurations are written prior to checking + * for PLL lock. + */ + wmb(); + + /* Check for PLL lock */ + rc = dsi_pll_7nm_lock_status(pll_7nm); + if (rc) { + pr_err("PLL(%d) lock failed\n", pll_7nm->id); + goto error; + } + + pll->pll_on = true; + + /* + * assert power on reset for PHY digital in case the PLL is + * enabled after CX of analog domain power collapse. This needs + * to be done before enabling the global clk. + */ + dsi_pll_phy_dig_reset(pll_7nm); + if (pll_7nm->slave) + dsi_pll_phy_dig_reset(pll_7nm->slave); + + dsi_pll_enable_global_clk(pll_7nm); + if (pll_7nm->slave) + dsi_pll_enable_global_clk(pll_7nm->slave); + +error: + return rc; +} + +static void dsi_pll_disable_sub(struct dsi_pll_7nm *pll) +{ + pll_write(pll->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_RBUF_CTRL, 0); + dsi_pll_disable_pll_bias(pll); +} + +static void dsi_pll_7nm_vco_unprepare(struct clk_hw *hw) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll); + + /* + * To avoid any stray glitches while abruptly powering down the PLL + * make sure to gate the clock using the clock enable bit before + * powering down the PLL + */ + dsi_pll_disable_global_clk(pll_7nm); + pll_write(pll_7nm->phy_cmn_mmio + REG_DSI_7nm_PHY_CMN_PLL_CNTRL, 0); + dsi_pll_disable_sub(pll_7nm); + if (pll_7nm->slave) { + dsi_pll_disable_global_clk(pll_7nm->slave); + dsi_pll_disable_sub(pll_7nm->slave); + } + /* flush, ensure all register writes are done */ + wmb(); + pll->pll_on = false; +} + +static unsigned long dsi_pll_7nm_vco_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll); + void __iomem *base = pll_7nm->mmio; + u64 ref_clk = pll_7nm->vco_ref_clk_rate; + u64 vco_rate = 0x0; + u64 multiplier; + u32 frac; + u32 dec; + u32 outdiv; + u64 pll_freq, tmp64; + + dec = pll_read(base + REG_DSI_7nm_PHY_PLL_DECIMAL_DIV_START_1); + dec &= 0xff; + + frac = pll_read(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_LOW_1); + frac |= ((pll_read(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_MID_1) & + 0xff) << 8); + frac |= ((pll_read(base + REG_DSI_7nm_PHY_PLL_FRAC_DIV_START_HIGH_1) & + 0x3) << 16); + + outdiv = 1 << (pll_read(base + REG_DSI_7nm_PHY_PLL_PLL_OUTDIV_RATE) & 0x3); + + /* + * TODO: + * 1. Assumes prescaler is disabled + * 2. Multiplier is 2^18. it should be 2^(num_of_frac_bits) + */ + multiplier = 1 << 18; + pll_freq = dec * (ref_clk * 2); + tmp64 = (ref_clk * 2 * frac); + pll_freq += div_u64(tmp64, multiplier); + + vco_rate = div_u64(pll_freq, outdiv); + + DBG("DSI PLL%d returning vco rate = %lu, dec = %x, frac = %x, outdiv = %x", + pll_7nm->id, (unsigned long)vco_rate, dec, frac, outdiv); + + return (unsigned long)vco_rate; +} + +static const struct clk_ops clk_ops_dsi_pll_7nm_vco = { + .round_rate = msm_dsi_pll_helper_clk_round_rate, + .set_rate = dsi_pll_7nm_vco_set_rate, + .recalc_rate = dsi_pll_7nm_vco_recalc_rate, + .prepare = dsi_pll_7nm_vco_prepare, + .unprepare = dsi_pll_7nm_vco_unprepare, +}; + +/* + * PLL Callbacks + */ + +static void dsi_pll_7nm_save_state(struct msm_dsi_pll *pll) +{ + struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll); + struct pll_7nm_cached_state *cached = &pll_7nm->cached_state; + void __iomem *phy_base = pll_7nm->phy_cmn_mmio; + u32 cmn_clk_cfg0, cmn_clk_cfg1; + + cached->pll_out_div = pll_read(pll_7nm->mmio + + REG_DSI_7nm_PHY_PLL_PLL_OUTDIV_RATE); + cached->pll_out_div &= 0x3; + + cmn_clk_cfg0 = pll_read(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG0); + cached->bit_clk_div = cmn_clk_cfg0 & 0xf; + cached->pix_clk_div = (cmn_clk_cfg0 & 0xf0) >> 4; + + cmn_clk_cfg1 = pll_read(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG1); + cached->pll_mux = cmn_clk_cfg1 & 0x3; + + DBG("DSI PLL%d outdiv %x bit_clk_div %x pix_clk_div %x pll_mux %x", + pll_7nm->id, cached->pll_out_div, cached->bit_clk_div, + cached->pix_clk_div, cached->pll_mux); +} + +static int dsi_pll_7nm_restore_state(struct msm_dsi_pll *pll) +{ + struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll); + struct pll_7nm_cached_state *cached = &pll_7nm->cached_state; + void __iomem *phy_base = pll_7nm->phy_cmn_mmio; + u32 val; + + val = pll_read(pll_7nm->mmio + REG_DSI_7nm_PHY_PLL_PLL_OUTDIV_RATE); + val &= ~0x3; + val |= cached->pll_out_div; + pll_write(pll_7nm->mmio + REG_DSI_7nm_PHY_PLL_PLL_OUTDIV_RATE, val); + + pll_write(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG0, + cached->bit_clk_div | (cached->pix_clk_div << 4)); + + val = pll_read(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG1); + val &= ~0x3; + val |= cached->pll_mux; + pll_write(phy_base + REG_DSI_7nm_PHY_CMN_CLK_CFG1, val); + + DBG("DSI PLL%d", pll_7nm->id); + + return 0; +} + +static int dsi_pll_7nm_set_usecase(struct msm_dsi_pll *pll, + enum msm_dsi_phy_usecase uc) +{ + struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll); + void __iomem *base = pll_7nm->phy_cmn_mmio; + u32 data = 0x0; /* internal PLL */ + + DBG("DSI PLL%d", pll_7nm->id); + + switch (uc) { + case MSM_DSI_PHY_STANDALONE: + break; + case MSM_DSI_PHY_MASTER: + pll_7nm->slave = pll_7nm_list[(pll_7nm->id + 1) % DSI_MAX]; + break; + case MSM_DSI_PHY_SLAVE: + data = 0x1; /* external PLL */ + break; + default: + return -EINVAL; + } + + /* set PLL src */ + pll_write(base + REG_DSI_7nm_PHY_CMN_CLK_CFG1, (data << 2)); + + pll_7nm->uc = uc; + + return 0; +} + +static int dsi_pll_7nm_get_provider(struct msm_dsi_pll *pll, + struct clk **byte_clk_provider, + struct clk **pixel_clk_provider) +{ + struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll); + struct clk_hw_onecell_data *hw_data = pll_7nm->hw_data; + + DBG("DSI PLL%d", pll_7nm->id); + + if (byte_clk_provider) + *byte_clk_provider = hw_data->hws[DSI_BYTE_PLL_CLK]->clk; + if (pixel_clk_provider) + *pixel_clk_provider = hw_data->hws[DSI_PIXEL_PLL_CLK]->clk; + + return 0; +} + +static void dsi_pll_7nm_destroy(struct msm_dsi_pll *pll) +{ + struct dsi_pll_7nm *pll_7nm = to_pll_7nm(pll); + struct device *dev = &pll_7nm->pdev->dev; + + DBG("DSI PLL%d", pll_7nm->id); + of_clk_del_provider(dev->of_node); + + clk_hw_unregister_divider(pll_7nm->out_dsiclk_hw); + clk_hw_unregister_mux(pll_7nm->pclk_mux_hw); + clk_hw_unregister_fixed_factor(pll_7nm->post_out_div_clk_hw); + clk_hw_unregister_fixed_factor(pll_7nm->by_2_bit_clk_hw); + clk_hw_unregister_fixed_factor(pll_7nm->byte_clk_hw); + clk_hw_unregister_divider(pll_7nm->bit_clk_hw); + clk_hw_unregister_divider(pll_7nm->out_div_clk_hw); + clk_hw_unregister(&pll_7nm->base.clk_hw); +} + +/* + * The post dividers and mux clocks are created using the standard divider and + * mux API. Unlike the 14nm PHY, the slave PLL doesn't need its dividers/mux + * state to follow the master PLL's divider/mux state. Therefore, we don't + * require special clock ops that also configure the slave PLL registers + */ +static int pll_7nm_register(struct dsi_pll_7nm *pll_7nm) +{ + char clk_name[32], parent[32], vco_name[32]; + char parent2[32], parent3[32], parent4[32]; + struct clk_init_data vco_init = { + .parent_names = (const char *[]){ "bi_tcxo" }, + .num_parents = 1, + .name = vco_name, + .flags = CLK_IGNORE_UNUSED, + .ops = &clk_ops_dsi_pll_7nm_vco, + }; + struct device *dev = &pll_7nm->pdev->dev; + struct clk_hw_onecell_data *hw_data; + struct clk_hw *hw; + int ret; + + DBG("DSI%d", pll_7nm->id); + + hw_data = devm_kzalloc(dev, sizeof(*hw_data) + + NUM_PROVIDED_CLKS * sizeof(struct clk_hw *), + GFP_KERNEL); + if (!hw_data) + return -ENOMEM; + + snprintf(vco_name, 32, "dsi%dvco_clk", pll_7nm->id); + pll_7nm->base.clk_hw.init = &vco_init; + + ret = clk_hw_register(dev, &pll_7nm->base.clk_hw); + if (ret) + return ret; + + snprintf(clk_name, 32, "dsi%d_pll_out_div_clk", pll_7nm->id); + snprintf(parent, 32, "dsi%dvco_clk", pll_7nm->id); + + hw = clk_hw_register_divider(dev, clk_name, + parent, CLK_SET_RATE_PARENT, + pll_7nm->mmio + + REG_DSI_7nm_PHY_PLL_PLL_OUTDIV_RATE, + 0, 2, CLK_DIVIDER_POWER_OF_TWO, NULL); + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); + goto err_base_clk_hw; + } + + pll_7nm->out_div_clk_hw = hw; + + snprintf(clk_name, 32, "dsi%d_pll_bit_clk", pll_7nm->id); + snprintf(parent, 32, "dsi%d_pll_out_div_clk", pll_7nm->id); + + /* BIT CLK: DIV_CTRL_3_0 */ + hw = clk_hw_register_divider(dev, clk_name, parent, + CLK_SET_RATE_PARENT, + pll_7nm->phy_cmn_mmio + + REG_DSI_7nm_PHY_CMN_CLK_CFG0, + 0, 4, CLK_DIVIDER_ONE_BASED, + &pll_7nm->postdiv_lock); + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); + goto err_out_div_clk_hw; + } + + pll_7nm->bit_clk_hw = hw; + + snprintf(clk_name, 32, "dsi%d_phy_pll_out_byteclk", pll_7nm->id); + snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_7nm->id); + + /* DSI Byte clock = VCO_CLK / OUT_DIV / BIT_DIV / 8 */ + hw = clk_hw_register_fixed_factor(dev, clk_name, parent, + CLK_SET_RATE_PARENT, 1, 8); + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); + goto err_bit_clk_hw; + } + + pll_7nm->byte_clk_hw = hw; + hw_data->hws[DSI_BYTE_PLL_CLK] = hw; + + snprintf(clk_name, 32, "dsi%d_pll_by_2_bit_clk", pll_7nm->id); + snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_7nm->id); + + hw = clk_hw_register_fixed_factor(dev, clk_name, parent, + 0, 1, 2); + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); + goto err_byte_clk_hw; + } + + pll_7nm->by_2_bit_clk_hw = hw; + + snprintf(clk_name, 32, "dsi%d_pll_post_out_div_clk", pll_7nm->id); + snprintf(parent, 32, "dsi%d_pll_out_div_clk", pll_7nm->id); + + hw = clk_hw_register_fixed_factor(dev, clk_name, parent, + 0, 1, 4); + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); + goto err_by_2_bit_clk_hw; + } + + pll_7nm->post_out_div_clk_hw = hw; + + snprintf(clk_name, 32, "dsi%d_pclk_mux", pll_7nm->id); + snprintf(parent, 32, "dsi%d_pll_bit_clk", pll_7nm->id); + snprintf(parent2, 32, "dsi%d_pll_by_2_bit_clk", pll_7nm->id); + snprintf(parent3, 32, "dsi%d_pll_out_div_clk", pll_7nm->id); + snprintf(parent4, 32, "dsi%d_pll_post_out_div_clk", pll_7nm->id); + + hw = clk_hw_register_mux(dev, clk_name, + ((const char *[]){ + parent, parent2, parent3, parent4 + }), 4, 0, pll_7nm->phy_cmn_mmio + + REG_DSI_7nm_PHY_CMN_CLK_CFG1, + 0, 2, 0, NULL); + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); + goto err_post_out_div_clk_hw; + } + + pll_7nm->pclk_mux_hw = hw; + + snprintf(clk_name, 32, "dsi%d_phy_pll_out_dsiclk", pll_7nm->id); + snprintf(parent, 32, "dsi%d_pclk_mux", pll_7nm->id); + + /* PIX CLK DIV : DIV_CTRL_7_4*/ + hw = clk_hw_register_divider(dev, clk_name, parent, + 0, pll_7nm->phy_cmn_mmio + + REG_DSI_7nm_PHY_CMN_CLK_CFG0, + 4, 4, CLK_DIVIDER_ONE_BASED, + &pll_7nm->postdiv_lock); + if (IS_ERR(hw)) { + ret = PTR_ERR(hw); + goto err_pclk_mux_hw; + } + + pll_7nm->out_dsiclk_hw = hw; + hw_data->hws[DSI_PIXEL_PLL_CLK] = hw; + + hw_data->num = NUM_PROVIDED_CLKS; + pll_7nm->hw_data = hw_data; + + ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, + pll_7nm->hw_data); + if (ret) { + DRM_DEV_ERROR(dev, "failed to register clk provider: %d\n", ret); + goto err_dsiclk_hw; + } + + return 0; + +err_dsiclk_hw: + clk_hw_unregister_divider(pll_7nm->out_dsiclk_hw); +err_pclk_mux_hw: + clk_hw_unregister_mux(pll_7nm->pclk_mux_hw); +err_post_out_div_clk_hw: + clk_hw_unregister_fixed_factor(pll_7nm->post_out_div_clk_hw); +err_by_2_bit_clk_hw: + clk_hw_unregister_fixed_factor(pll_7nm->by_2_bit_clk_hw); +err_byte_clk_hw: + clk_hw_unregister_fixed_factor(pll_7nm->byte_clk_hw); +err_bit_clk_hw: + clk_hw_unregister_divider(pll_7nm->bit_clk_hw); +err_out_div_clk_hw: + clk_hw_unregister_divider(pll_7nm->out_div_clk_hw); +err_base_clk_hw: + clk_hw_unregister(&pll_7nm->base.clk_hw); + + return ret; +} + +struct msm_dsi_pll *msm_dsi_pll_7nm_init(struct platform_device *pdev, int id) +{ + struct dsi_pll_7nm *pll_7nm; + struct msm_dsi_pll *pll; + int ret; + + pll_7nm = devm_kzalloc(&pdev->dev, sizeof(*pll_7nm), GFP_KERNEL); + if (!pll_7nm) + return ERR_PTR(-ENOMEM); + + DBG("DSI PLL%d", id); + + pll_7nm->pdev = pdev; + pll_7nm->id = id; + pll_7nm_list[id] = pll_7nm; + + pll_7nm->phy_cmn_mmio = msm_ioremap(pdev, "dsi_phy", "DSI_PHY"); + if (IS_ERR_OR_NULL(pll_7nm->phy_cmn_mmio)) { + DRM_DEV_ERROR(&pdev->dev, "failed to map CMN PHY base\n"); + return ERR_PTR(-ENOMEM); + } + + pll_7nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL"); + if (IS_ERR_OR_NULL(pll_7nm->mmio)) { + DRM_DEV_ERROR(&pdev->dev, "failed to map PLL base\n"); + return ERR_PTR(-ENOMEM); + } + + spin_lock_init(&pll_7nm->postdiv_lock); + + pll = &pll_7nm->base; + pll->min_rate = 600000000UL; + pll->max_rate = 5000000000UL; + pll->get_provider = dsi_pll_7nm_get_provider; + pll->destroy = dsi_pll_7nm_destroy; + pll->save_state = dsi_pll_7nm_save_state; + pll->restore_state = dsi_pll_7nm_restore_state; + pll->set_usecase = dsi_pll_7nm_set_usecase; + + pll_7nm->vco_delay = 1; + + ret = pll_7nm_register(pll_7nm); + if (ret) { + DRM_DEV_ERROR(&pdev->dev, "failed to register PLL: %d\n", ret); + return ERR_PTR(ret); + } + + /* TODO: Remove this when we have proper display handover support */ + msm_dsi_pll_save_state(pll); + + return pll; +} diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index dead5db3315a..ac629999f535 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -12,6 +12,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/dmaengine.h> #include <linux/qcom-geni-se.h> #include <linux/spinlock.h> @@ -48,6 +49,8 @@ #define LOW_COUNTER_SHFT 10 #define CYCLE_COUNTER_MSK GENMASK(9, 0) +#define I2C_PACK_EN (BIT(0) | BIT(1)) + enum geni_i2c_err_code { GP_IRQ0, NACK, @@ -72,6 +75,12 @@ enum geni_i2c_err_code { #define XFER_TIMEOUT HZ #define RST_TIMEOUT HZ +enum i2c_se_mode { + UNINITIALIZED, + FIFO_SE_DMA, + GSI_ONLY, +}; + struct geni_i2c_dev { struct geni_se se; u32 tx_wm; @@ -86,6 +95,16 @@ struct geni_i2c_dev { u32 clk_freq_out; const struct geni_i2c_clk_fld *clk_fld; int suspended; + struct dma_chan *tx_c; + struct dma_chan *rx_c; + dma_addr_t tx_ph; + dma_addr_t rx_ph; + int cfg_sent; + struct dma_async_tx_descriptor *tx_desc; + struct dma_async_tx_descriptor *rx_desc; + enum i2c_se_mode se_mode; + bool cmd_done; + bool is_shared; }; struct geni_i2c_err_log { @@ -436,6 +455,176 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, return gi2c->err; } +static void i2c_gsi_cb_result(void *cb, const struct dmaengine_result *result) +{ + struct geni_i2c_dev *gi2c = cb; + + if (result->result != DMA_TRANS_NOERROR) { + dev_err(gi2c->se.dev, "DMA txn failed:%d\n", result->result); + return; + } + + if (!result->residue) { + dev_dbg(gi2c->se.dev, "callback for gsi dma txn\n"); + complete(&gi2c->done); + } else { + dev_err(gi2c->se.dev, "DMA xfer has pending: %d\n", result->residue); + } +} + +static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], + int num) +{ + struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap); + struct dma_slave_config config; + struct dmaengine_peripheral_config peripheral; + int i, ret = 0, timeout = 0; + + memset(&config, 0, sizeof(config)); + memset(&peripheral, 0, sizeof(peripheral)); + config.peripheral = &peripheral; + peripheral.peripheral = DMAENGINE_PERIPHERAL_I2C; + + if (!gi2c->tx_c) { + gi2c->tx_c = dma_request_slave_channel(gi2c->se.dev, "tx"); + if (!gi2c->tx_c) { + dev_err(gi2c->se.dev, "tx dma_request_slave_channel fail\n"); + ret = -EIO; + goto geni_i2c_gsi_xfer_out; + } + } + + if (!gi2c->rx_c) { + gi2c->rx_c = dma_request_slave_channel(gi2c->se.dev, "rx"); + if (!gi2c->rx_c) { + dev_err(gi2c->se.dev, "rx dma_request_slave_channel fail\n"); + ret = -EIO; + goto geni_i2c_gsi_xfer_out; + } + } + + if (!gi2c->cfg_sent) { + const struct geni_i2c_clk_fld *itr = gi2c->clk_fld; + + peripheral.i2c.pack_enable = I2C_PACK_EN; + peripheral.i2c.cycle_count = itr->t_cycle_cnt; + peripheral.i2c.high_count = itr->t_high_cnt; + peripheral.i2c.low_count = itr->t_low_cnt; + peripheral.i2c.clk_div = itr->clk_div; + gi2c->cfg_sent = true; + peripheral.set_config = true; + } + + for (i = 0; i < num; i++) { + struct device *rx_dev = gi2c->se.dev; + struct device *tx_dev = gi2c->se.dev; + dma_cookie_t tx_cookie, rx_cookie; + int stretch = (i < (num - 1)); + u8 *dma_buf = NULL; + + gi2c->cur = &msgs[i]; + + peripheral.i2c.addr = msgs[i].addr; + peripheral.i2c.stretch = stretch; + if (msgs[i].flags & I2C_M_RD) + peripheral.i2c.op = I2C_READ; + else + peripheral.i2c.op = I2C_WRITE; + + dma_buf = i2c_get_dma_safe_msg_buf(&msgs[i], 1); + if (!dma_buf) { + ret = -ENOMEM; + goto geni_i2c_gsi_xfer_out; + } + + if (msgs[i].flags & I2C_M_RD) { + gi2c->rx_ph = dma_map_single(rx_dev, dma_buf, + msgs[i].len, DMA_FROM_DEVICE); + if (dma_mapping_error(rx_dev, gi2c->rx_ph)) { + dev_err(gi2c->se.dev, "dma_map_single for rx failed :%d\n", ret); + i2c_put_dma_safe_msg_buf(dma_buf, &msgs[i], false); + goto geni_i2c_gsi_xfer_out; + } + + ret = dmaengine_slave_config(gi2c->rx_c, &config); + if (ret) { + dev_err(gi2c->se.dev, "rx dma config error:%d\n", ret); + goto geni_i2c_gsi_xfer_out; + } + peripheral.rx_len = msgs[i].len; + + gi2c->rx_desc = dmaengine_prep_slave_single(gi2c->rx_c, gi2c->rx_ph, + msgs[i].len, DMA_DEV_TO_MEM, + (DMA_PREP_INTERRUPT | DMA_CTRL_ACK)); + if (!gi2c->rx_desc) { + dev_err(gi2c->se.dev, "prep_slave_sg for rx failed\n"); + gi2c->err = -EIO; + goto geni_i2c_err_prep_sg; + } + + gi2c->rx_desc->callback_result = i2c_gsi_cb_result; + gi2c->rx_desc->callback_param = &gi2c; + + /* Issue RX */ + rx_cookie = dmaengine_submit(gi2c->rx_desc); + dma_async_issue_pending(gi2c->rx_c); + } + + dev_dbg(gi2c->se.dev, "msg[%d].len:%d W\n", i, gi2c->cur->len); + gi2c->tx_ph = dma_map_single(tx_dev, dma_buf, msgs[i].len, DMA_TO_DEVICE); + if (dma_mapping_error(tx_dev, gi2c->tx_ph)) { + dev_err(gi2c->se.dev, "dma_map_single for tx failed :%d\n", ret); + i2c_put_dma_safe_msg_buf(dma_buf, &msgs[i], false); + goto geni_i2c_gsi_xfer_out; + } + + ret = dmaengine_slave_config(gi2c->tx_c, &config); + if (ret) { + dev_err(gi2c->se.dev, "tx dma config error:%d\n", ret); + goto geni_i2c_gsi_xfer_out; + } + gi2c->tx_desc = dmaengine_prep_slave_single(gi2c->tx_c, gi2c->tx_ph, msgs[i].len, + DMA_MEM_TO_DEV, + (DMA_PREP_INTERRUPT | DMA_CTRL_ACK)); + if (!gi2c->tx_desc) { + dev_err(gi2c->se.dev, "prep_slave_sg for tx failed\n"); + gi2c->err = -ENOMEM; + goto geni_i2c_err_prep_sg; + } + gi2c->rx_desc->callback_result = i2c_gsi_cb_result; + gi2c->rx_desc->callback_param = &gi2c; + + /* Issue TX */ + tx_cookie = dmaengine_submit(gi2c->tx_desc); + dma_async_issue_pending(gi2c->tx_c); + + timeout = wait_for_completion_timeout(&gi2c->done, + XFER_TIMEOUT); + if (!timeout) { + dev_err(gi2c->se.dev, "I2C timeout gsi flags:%d addr:0x%x\n", + gi2c->cur->flags, gi2c->cur->addr); + gi2c->err = -ETIMEDOUT; + } +geni_i2c_err_prep_sg: + if (gi2c->err) { + dmaengine_terminate_all(gi2c->tx_c); + gi2c->cfg_sent = 0; + } + if (msgs[i].flags & I2C_M_RD) + dma_unmap_single(rx_dev, gi2c->rx_ph, msgs[i].len, DMA_FROM_DEVICE); + else + dma_unmap_single(tx_dev, gi2c->tx_ph, msgs[i].len, DMA_TO_DEVICE); + i2c_put_dma_safe_msg_buf(dma_buf, &msgs[i], !gi2c->err); + if (gi2c->err) + goto geni_i2c_gsi_xfer_out; + } + +geni_i2c_gsi_xfer_out: + if (!ret && gi2c->err) + ret = gi2c->err; + return ret; +} + static int geni_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) @@ -455,6 +644,15 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, } qcom_geni_i2c_conf(gi2c); + + if (gi2c->se_mode == GSI_ONLY) { + ret = geni_i2c_gsi_xfer(adap, msgs, num); + goto geni_i2c_txn_ret; + } else { + /* Don't set shared flag in non-GSI mode */ + gi2c->is_shared = false; + } + for (i = 0; i < num; i++) { u32 m_param = i < (num - 1) ? STOP_STRETCH : 0; @@ -469,6 +667,7 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, if (ret) break; } +geni_i2c_txn_ret: if (ret == 0) ret = num; @@ -635,7 +834,8 @@ static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev) int ret; struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); - disable_irq(gi2c->irq); + if (gi2c->se_mode == FIFO_SE_DMA) + disable_irq(gi2c->irq); ret = geni_se_resources_off(&gi2c->se); if (ret) { enable_irq(gi2c->irq); @@ -660,8 +860,42 @@ static int __maybe_unused geni_i2c_runtime_resume(struct device *dev) ret = geni_se_resources_on(&gi2c->se); if (ret) return ret; + if (gi2c->se_mode == UNINITIALIZED) { + int proto = geni_se_read_proto(&gi2c->se); + u32 se_mode; + + if (unlikely(proto != GENI_SE_I2C)) { + dev_err(gi2c->se.dev, "Invalid proto %d\n", proto); + geni_se_resources_off(&gi2c->se); + return -ENXIO; + } + + se_mode = readl_relaxed(gi2c->se.base + GENI_IF_DISABLE_RO) & + FIFO_IF_DISABLE; + if (se_mode) { + gi2c->se_mode = GSI_ONLY; + geni_se_select_mode(&gi2c->se, GENI_GPI_DMA); + dev_dbg(gi2c->se.dev, "i2c GSI mode\n"); + } else { + int gi2c_tx_depth = geni_se_get_tx_fifo_depth(&gi2c->se); + + gi2c->se_mode = FIFO_SE_DMA; + gi2c->tx_wm = gi2c_tx_depth - 1; + geni_se_init(&gi2c->se, gi2c->tx_wm, gi2c_tx_depth); + geni_se_config_packing(&gi2c->se, BITS_PER_BYTE, PACKING_BYTES_PW, + true, true, true); + qcom_geni_i2c_conf(gi2c); + dev_dbg(gi2c->se.dev, + "i2c fifo/se-dma mode. fifo depth:%d\n", + gi2c_tx_depth); + } + dev_dbg(gi2c->se.dev, "i2c-%d: %s\n", + gi2c->adap.nr, dev_name(gi2c->se.dev)); + } + + if (gi2c->se_mode == FIFO_SE_DMA) + enable_irq(gi2c->irq); - enable_irq(gi2c->irq); gi2c->suspended = 0; return 0; } diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index b4b73c9920b4..605b0c17d6ae 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -14,6 +14,7 @@ #include <linux/math64.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/slab.h> @@ -154,18 +155,6 @@ struct adc5_chip { const struct adc5_data *data; }; -static const struct vadc_prescale_ratio adc5_prescale_ratios[] = { - {.num = 1, .den = 1}, - {.num = 1, .den = 3}, - {.num = 1, .den = 4}, - {.num = 1, .den = 6}, - {.num = 1, .den = 20}, - {.num = 1, .den = 8}, - {.num = 10, .den = 81}, - {.num = 1, .den = 10}, - {.num = 1, .den = 16} -}; - static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len) { return regmap_bulk_read(adc->regmap, adc->base + offset, data, len); @@ -181,55 +170,6 @@ static int adc5_masked_write(struct adc5_chip *adc, u16 offset, u8 mask, u8 val) return regmap_update_bits(adc->regmap, adc->base + offset, mask, val); } -static int adc5_prescaling_from_dt(u32 num, u32 den) -{ - unsigned int pre; - - for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++) - if (adc5_prescale_ratios[pre].num == num && - adc5_prescale_ratios[pre].den == den) - break; - - if (pre == ARRAY_SIZE(adc5_prescale_ratios)) - return -EINVAL; - - return pre; -} - -static int adc5_hw_settle_time_from_dt(u32 value, - const unsigned int *hw_settle) -{ - unsigned int i; - - for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) { - if (value == hw_settle[i]) - return i; - } - - return -EINVAL; -} - -static int adc5_avg_samples_from_dt(u32 value) -{ - if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX) - return -EINVAL; - - return __ffs(value); -} - -static int adc5_decimation_from_dt(u32 value, - const unsigned int *decimation) -{ - unsigned int i; - - for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) { - if (value == decimation[i]) - return i; - } - - return -EINVAL; -} - static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data) { int ret; @@ -511,7 +451,7 @@ static int adc_read_raw_common(struct iio_dev *indio_dev, return ret; ret = qcom_adc5_hw_scale(prop->scale_fn_type, - &adc5_prescale_ratios[prop->prescale], + prop->prescale, adc->data, adc_code_volt, val); if (ret) @@ -717,7 +657,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc, ret = of_property_read_u32(node, "qcom,decimation", &value); if (!ret) { - ret = adc5_decimation_from_dt(value, data->decimation); + ret = qcom_adc5_decimation_from_dt(value, data->decimation); if (ret < 0) { dev_err(dev, "%02x invalid decimation %d\n", chan, value); @@ -730,7 +670,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc, ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2); if (!ret) { - ret = adc5_prescaling_from_dt(varr[0], varr[1]); + ret = qcom_adc5_prescaling_from_dt(varr[0], varr[1]); if (ret < 0) { dev_err(dev, "%02x invalid pre-scaling <%d %d>\n", chan, varr[0], varr[1]); @@ -759,10 +699,10 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc, if ((dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR && dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR) || adc->data->info == &adc7_info) - ret = adc5_hw_settle_time_from_dt(value, + ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_2); else - ret = adc5_hw_settle_time_from_dt(value, + ret = qcom_adc5_hw_settle_time_from_dt(value, data->hw_settle_1); if (ret < 0) { @@ -777,7 +717,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc, ret = of_property_read_u32(node, "qcom,avg-samples", &value); if (!ret) { - ret = adc5_avg_samples_from_dt(value); + ret = qcom_adc5_avg_samples_from_dt(value); if (ret < 0) { dev_err(dev, "%02x invalid avg-samples %d\n", chan, value); @@ -870,8 +810,6 @@ static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node) struct adc5_channel_prop prop, *chan_props; struct device_node *child; unsigned int index = 0; - const struct of_device_id *id; - const struct adc5_data *data; int ret; adc->nchannels = of_get_available_child_count(node); @@ -890,24 +828,19 @@ static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node) chan_props = adc->chan_props; iio_chan = adc->iio_chans; - id = of_match_node(adc5_match_table, node); - if (id) - data = id->data; - else - data = &adc5_data_pmic; - adc->data = data; + adc->data = of_device_get_match_data(adc->dev); for_each_available_child_of_node(node, child) { - ret = adc5_get_dt_channel_data(adc, &prop, child, data); + ret = adc5_get_dt_channel_data(adc, &prop, child, adc->data); if (ret) { of_node_put(child); return ret; } prop.scale_fn_type = - data->adc_chans[prop.channel].scale_fn_type; + adc->data->adc_chans[prop.channel].scale_fn_type; *chan_props = prop; - adc_chan = &data->adc_chans[prop.channel]; + adc_chan = &adc->data->adc_chans[prop.channel]; iio_chan->channel = prop.channel; iio_chan->datasheet_name = prop.datasheet_name; @@ -982,7 +915,7 @@ static int adc5_probe(struct platform_device *pdev) static struct platform_driver adc5_driver = { .driver = { - .name = "qcom-spmi-adc5.c", + .name = "qcom-spmi-adc5", .of_match_table = adc5_match_table, }, .probe = adc5_probe, diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c index 5113aaa6ba67..3d39e3406a7a 100644 --- a/drivers/iio/adc/qcom-vadc-common.c +++ b/drivers/iio/adc/qcom-vadc-common.c @@ -2,6 +2,7 @@ #include <linux/bug.h> #include <linux/kernel.h> #include <linux/bitops.h> +#include <linux/fixp-arith.h> #include <linux/math64.h> #include <linux/log2.h> #include <linux/err.h> @@ -278,6 +279,18 @@ static const struct vadc_map_pt adcmap7_100k[] = { { 2420, 130048 } }; +static const struct vadc_prescale_ratio adc5_prescale_ratios[] = { + {.num = 1, .den = 1}, + {.num = 1, .den = 3}, + {.num = 1, .den = 4}, + {.num = 1, .den = 6}, + {.num = 1, .den = 20}, + {.num = 1, .den = 8}, + {.num = 10, .den = 81}, + {.num = 1, .den = 10}, + {.num = 1, .den = 16} +}; + static int qcom_vadc_scale_hw_calib_volt( const struct vadc_prescale_ratio *prescale, const struct adc5_data *data, @@ -356,15 +369,50 @@ static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts, } else { /* result is between search_index and search_index-1 */ /* interpolate linearly */ - *output = (((s32)((pts[i].y - pts[i - 1].y) * - (input - pts[i - 1].x)) / - (pts[i].x - pts[i - 1].x)) + - pts[i - 1].y); + *output = fixp_linear_interpolate(pts[i - 1].x, pts[i - 1].y, + pts[i].x, pts[i].y, input); } return 0; } +static s32 qcom_vadc_map_temp_voltage(const struct vadc_map_pt *pts, + u32 tablesize, int input) +{ + bool descending = 1; + u32 i = 0; + + /* Check if table is descending or ascending */ + if (tablesize > 1) { + if (pts[0].y < pts[1].y) + descending = 0; + } + + while (i < tablesize) { + if ((descending) && (pts[i].y < input)) { + /* table entry is less than measured*/ + /* value and table is descending, stop */ + break; + } else if ((!descending) && + (pts[i].y > input)) { + /* table entry is greater than measured*/ + /*value and table is ascending, stop */ + break; + } + i++; + } + + if (i == 0) + return pts[0].x; + if (i == tablesize) + return pts[tablesize - 1].x; + + /* result is between search_index and search_index-1 */ + /* interpolate linearly */ + return fixp_linear_interpolate(pts[i - 1].y, pts[i - 1].x, + pts[i].y, pts[i].x, input); +} + static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph, u16 adc_code, bool absolute, @@ -462,6 +510,19 @@ static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph, return 0; } +static u16 qcom_vadc_scale_voltage_code(int voltage, + const struct vadc_prescale_ratio *prescale, + const u32 full_scale_code_volt, + unsigned int factor) +{ + s64 volt = voltage, adc_vdd_ref_mv = 1875; + + volt *= prescale->num * factor * full_scale_code_volt; + volt = div64_s64(volt, (s64)prescale->den * adc_vdd_ref_mv * 1000); + + return volt; +} + static int qcom_vadc_scale_code_voltage_factor(u16 adc_code, const struct vadc_prescale_ratio *prescale, const struct adc5_data *data, @@ -646,11 +707,26 @@ int qcom_vadc_scale(enum vadc_scale_fn_type scaletype, } EXPORT_SYMBOL(qcom_vadc_scale); +u16 qcom_adc_tm5_temp_volt_scale(unsigned int prescale_ratio, + u32 full_scale_code_volt, int temp) +{ + const struct vadc_prescale_ratio *prescale = &adc5_prescale_ratios[prescale_ratio]; + s32 voltage; + + voltage = qcom_vadc_map_temp_voltage(adcmap_100k_104ef_104fb_1875_vref, + ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref), + temp); + return qcom_vadc_scale_voltage_code(voltage, prescale, full_scale_code_volt, 1000); +} +EXPORT_SYMBOL(qcom_adc_tm5_temp_volt_scale); + int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype, - const struct vadc_prescale_ratio *prescale, + unsigned int prescale_ratio, const struct adc5_data *data, u16 adc_code, int *result) { + const struct vadc_prescale_ratio *prescale = &adc5_prescale_ratios[prescale_ratio]; + if (!(scaletype >= SCALE_HW_CALIB_DEFAULT && scaletype < SCALE_HW_CALIB_INVALID)) { pr_err("Invalid scale type %d\n", scaletype); @@ -662,6 +738,59 @@ int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype, } EXPORT_SYMBOL(qcom_adc5_hw_scale); +int qcom_adc5_prescaling_from_dt(u32 num, u32 den) +{ + unsigned int pre; + + for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++) + if (adc5_prescale_ratios[pre].num == num && + adc5_prescale_ratios[pre].den == den) + break; + + if (pre == ARRAY_SIZE(adc5_prescale_ratios)) + return -EINVAL; + + return pre; +} +EXPORT_SYMBOL(qcom_adc5_prescaling_from_dt); + +int qcom_adc5_hw_settle_time_from_dt(u32 value, + const unsigned int *hw_settle) +{ + unsigned int i; + + for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) { + if (value == hw_settle[i]) + return i; + } + + return -EINVAL; +} +EXPORT_SYMBOL(qcom_adc5_hw_settle_time_from_dt); + +int qcom_adc5_avg_samples_from_dt(u32 value) +{ + if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX) + return -EINVAL; + + return __ffs(value); +} +EXPORT_SYMBOL(qcom_adc5_avg_samples_from_dt); + +int qcom_adc5_decimation_from_dt(u32 value, + const unsigned int *decimation) +{ + unsigned int i; + + for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) { + if (value == decimation[i]) + return i; + } + + return -EINVAL; +} +EXPORT_SYMBOL(qcom_adc5_decimation_from_dt); + int qcom_vadc_decimation_from_dt(u32 value) { if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN || diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h index 17b2fc4d8bf2..aa3aef64d51e 100644 --- a/drivers/iio/adc/qcom-vadc-common.h +++ b/drivers/iio/adc/qcom-vadc-common.h @@ -168,10 +168,23 @@ struct qcom_adc5_scale_type { }; int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype, - const struct vadc_prescale_ratio *prescale, + unsigned int prescale_ratio, const struct adc5_data *data, u16 adc_code, int *result_mdec); +u16 qcom_adc_tm5_temp_volt_scale(unsigned int prescale_ratio, + u32 full_scale_code_volt, int temp); + +int qcom_adc5_prescaling_from_dt(u32 num, u32 den); + +int qcom_adc5_hw_settle_time_from_dt(u32 value, + const unsigned int *hw_settle); + +int qcom_adc5_avg_samples_from_dt(u32 value); + +int qcom_adc5_decimation_from_dt(u32 value, + const unsigned int *decimation); + int qcom_vadc_decimation_from_dt(u32 value); #endif /* QCOM_VADC_COMMON_H */ diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index 4ca27675cc5a..2f4ceaa80593 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -338,6 +338,209 @@ static const struct ov8856_reg mode_3280x2464_regs[] = { {0x5e00, 0x00} }; +static const struct ov8856_reg mode_3264x2448_regs[] = { + {0x0103, 0x01}, + {0x0302, 0x3c}, + {0x0303, 0x01}, + {0x031e, 0x0c}, + {0x3000, 0x20}, + {0x3003, 0x08}, + {0x300e, 0x20}, + {0x3010, 0x00}, + {0x3015, 0x84}, + {0x3018, 0x72}, + {0x3021, 0x23}, + {0x3033, 0x24}, + {0x3500, 0x00}, + {0x3501, 0x9a}, + {0x3502, 0x20}, + {0x3503, 0x08}, + {0x3505, 0x83}, + {0x3508, 0x01}, + {0x3509, 0x80}, + {0x350c, 0x00}, + {0x350d, 0x80}, + {0x350e, 0x04}, + {0x350f, 0x00}, + {0x3510, 0x00}, + {0x3511, 0x02}, + {0x3512, 0x00}, + {0x3600, 0x72}, + {0x3601, 0x40}, + {0x3602, 0x30}, + {0x3610, 0xc5}, + {0x3611, 0x58}, + {0x3612, 0x5c}, + {0x3613, 0xca}, + {0x3614, 0x60}, + {0x3628, 0xff}, + {0x3629, 0xff}, + {0x362a, 0xff}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3636, 0x10}, + {0x3663, 0x08}, + {0x3669, 0x34}, + {0x366d, 0x00}, + {0x366e, 0x10}, + {0x3706, 0x86}, + {0x370b, 0x7e}, + {0x3714, 0x23}, + {0x3730, 0x12}, + {0x3733, 0x10}, + {0x3764, 0x00}, + {0x3765, 0x00}, + {0x3769, 0x62}, + {0x376a, 0x2a}, + {0x376b, 0x30}, + {0x3780, 0x00}, + {0x3781, 0x24}, + {0x3782, 0x00}, + {0x3783, 0x23}, + {0x3798, 0x2f}, + {0x37a1, 0x60}, + {0x37a8, 0x6a}, + {0x37ab, 0x3f}, + {0x37c2, 0x04}, + {0x37c3, 0xf1}, + {0x37c9, 0x80}, + {0x37cb, 0x16}, + {0x37cc, 0x16}, + {0x37cd, 0x16}, + {0x37ce, 0x16}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x0c}, + {0x3804, 0x0c}, + {0x3805, 0xdf}, + {0x3806, 0x09}, + {0x3807, 0xa3}, + {0x3808, 0x0c}, + {0x3809, 0xc0}, + {0x380a, 0x09}, + {0x380b, 0x90}, + {0x380c, 0x07}, + {0x380d, 0x8c}, + {0x380e, 0x09}, + {0x380f, 0xb2}, + {0x3810, 0x00}, + {0x3811, 0x04}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3814, 0x01}, + {0x3815, 0x01}, + {0x3816, 0x00}, + {0x3817, 0x00}, + {0x3818, 0x00}, + {0x3819, 0x10}, + {0x3820, 0x80}, + {0x3821, 0x46}, + {0x382a, 0x01}, + {0x382b, 0x01}, + {0x3830, 0x06}, + {0x3836, 0x02}, + {0x3862, 0x04}, + {0x3863, 0x08}, + {0x3cc0, 0x33}, + {0x3d85, 0x17}, + {0x3d8c, 0x73}, + {0x3d8d, 0xde}, + {0x4001, 0xe0}, + {0x4003, 0x40}, + {0x4008, 0x00}, + {0x4009, 0x0b}, + {0x400a, 0x00}, + {0x400b, 0x84}, + {0x400f, 0x80}, + {0x4010, 0xf0}, + {0x4011, 0xff}, + {0x4012, 0x02}, + {0x4013, 0x01}, + {0x4014, 0x01}, + {0x4015, 0x01}, + {0x4042, 0x00}, + {0x4043, 0x80}, + {0x4044, 0x00}, + {0x4045, 0x80}, + {0x4046, 0x00}, + {0x4047, 0x80}, + {0x4048, 0x00}, + {0x4049, 0x80}, + {0x4041, 0x03}, + {0x404c, 0x20}, + {0x404d, 0x00}, + {0x404e, 0x20}, + {0x4203, 0x80}, + {0x4307, 0x30}, + {0x4317, 0x00}, + {0x4502, 0x50}, + {0x4503, 0x08}, + {0x4601, 0x80}, + {0x4800, 0x44}, + {0x4816, 0x53}, + {0x481b, 0x50}, + {0x481f, 0x27}, + {0x4823, 0x3c}, + {0x482b, 0x00}, + {0x4831, 0x66}, + {0x4837, 0x16}, + {0x483c, 0x0f}, + {0x484b, 0x05}, + {0x5000, 0x77}, + {0x5001, 0x0a}, + {0x5003, 0xc8}, + {0x5004, 0x04}, + {0x5006, 0x00}, + {0x5007, 0x00}, + {0x502e, 0x03}, + {0x5030, 0x41}, + {0x5780, 0x14}, + {0x5781, 0x0f}, + {0x5782, 0x44}, + {0x5783, 0x02}, + {0x5784, 0x01}, + {0x5785, 0x01}, + {0x5786, 0x00}, + {0x5787, 0x04}, + {0x5788, 0x02}, + {0x5789, 0x0f}, + {0x578a, 0xfd}, + {0x578b, 0xf5}, + {0x578c, 0xf5}, + {0x578d, 0x03}, + {0x578e, 0x08}, + {0x578f, 0x0c}, + {0x5790, 0x08}, + {0x5791, 0x04}, + {0x5792, 0x00}, + {0x5793, 0x52}, + {0x5794, 0xa3}, + {0x5795, 0x02}, + {0x5796, 0x20}, + {0x5797, 0x20}, + {0x5798, 0xd5}, + {0x5799, 0xd5}, + {0x579a, 0x00}, + {0x579b, 0x50}, + {0x579c, 0x00}, + {0x579d, 0x2c}, + {0x579e, 0x0c}, + {0x579f, 0x40}, + {0x57a0, 0x09}, + {0x57a1, 0x40}, + {0x59f8, 0x3d}, + {0x5a08, 0x02}, + {0x5b00, 0x02}, + {0x5b01, 0x10}, + {0x5b02, 0x03}, + {0x5b03, 0xcf}, + {0x5b05, 0x6c}, + {0x5e00, 0x00}, + {0x5e10, 0xfc} +}; + static const struct ov8856_reg mode_1640x1232_regs[] = { {0x3000, 0x20}, {0x3003, 0x08}, @@ -528,6 +731,209 @@ static const struct ov8856_reg mode_1640x1232_regs[] = { {0x5e00, 0x00} }; +static const struct ov8856_reg mode_1632x1224_regs[] = { + {0x0103, 0x01}, + {0x0302, 0x3c}, + {0x0303, 0x01}, + {0x031e, 0x0c}, + {0x3000, 0x20}, + {0x3003, 0x08}, + {0x300e, 0x20}, + {0x3010, 0x00}, + {0x3015, 0x84}, + {0x3018, 0x72}, + {0x3021, 0x23}, + {0x3033, 0x24}, + {0x3500, 0x00}, + {0x3501, 0x4c}, + {0x3502, 0xe0}, + {0x3503, 0x08}, + {0x3505, 0x83}, + {0x3508, 0x01}, + {0x3509, 0x80}, + {0x350c, 0x00}, + {0x350d, 0x80}, + {0x350e, 0x04}, + {0x350f, 0x00}, + {0x3510, 0x00}, + {0x3511, 0x02}, + {0x3512, 0x00}, + {0x3600, 0x72}, + {0x3601, 0x40}, + {0x3602, 0x30}, + {0x3610, 0xc5}, + {0x3611, 0x58}, + {0x3612, 0x5c}, + {0x3613, 0xca}, + {0x3614, 0x60}, + {0x3628, 0xff}, + {0x3629, 0xff}, + {0x362a, 0xff}, + {0x3633, 0x10}, + {0x3634, 0x10}, + {0x3635, 0x10}, + {0x3636, 0x10}, + {0x3663, 0x08}, + {0x3669, 0x34}, + {0x366d, 0x00}, + {0x366e, 0x08}, + {0x3706, 0x86}, + {0x370b, 0x7e}, + {0x3714, 0x27}, + {0x3730, 0x12}, + {0x3733, 0x10}, + {0x3764, 0x00}, + {0x3765, 0x00}, + {0x3769, 0x62}, + {0x376a, 0x2a}, + {0x376b, 0x30}, + {0x3780, 0x00}, + {0x3781, 0x24}, + {0x3782, 0x00}, + {0x3783, 0x23}, + {0x3798, 0x2f}, + {0x37a1, 0x60}, + {0x37a8, 0x6a}, + {0x37ab, 0x3f}, + {0x37c2, 0x14}, + {0x37c3, 0xf1}, + {0x37c9, 0x80}, + {0x37cb, 0x16}, + {0x37cc, 0x16}, + {0x37cd, 0x16}, + {0x37ce, 0x16}, + {0x3800, 0x00}, + {0x3801, 0x00}, + {0x3802, 0x00}, + {0x3803, 0x0c}, + {0x3804, 0x0c}, + {0x3805, 0xdf}, + {0x3806, 0x09}, + {0x3807, 0xa3}, + {0x3808, 0x06}, + {0x3809, 0x60}, + {0x380a, 0x04}, + {0x380b, 0xc8}, + {0x380c, 0x07}, + {0x380d, 0x8c}, + {0x380e, 0x09}, + {0x380f, 0xb2}, + {0x3810, 0x00}, + {0x3811, 0x02}, + {0x3812, 0x00}, + {0x3813, 0x02}, + {0x3814, 0x03}, + {0x3815, 0x01}, + {0x3816, 0x00}, + {0x3817, 0x00}, + {0x3818, 0x00}, + {0x3819, 0x10}, + {0x3820, 0x80}, + {0x3821, 0x47}, + {0x382a, 0x03}, + {0x382b, 0x01}, + {0x3830, 0x06}, + {0x3836, 0x02}, + {0x3862, 0x04}, + {0x3863, 0x08}, + {0x3cc0, 0x33}, + {0x3d85, 0x17}, + {0x3d8c, 0x73}, + {0x3d8d, 0xde}, + {0x4001, 0xe0}, + {0x4003, 0x40}, + {0x4008, 0x00}, + {0x4009, 0x05}, + {0x400a, 0x00}, + {0x400b, 0x84}, + {0x400f, 0x80}, + {0x4010, 0xf0}, + {0x4011, 0xff}, + {0x4012, 0x02}, + {0x4013, 0x01}, + {0x4014, 0x01}, + {0x4015, 0x01}, + {0x4042, 0x00}, + {0x4043, 0x80}, + {0x4044, 0x00}, + {0x4045, 0x80}, + {0x4046, 0x00}, + {0x4047, 0x80}, + {0x4048, 0x00}, + {0x4049, 0x80}, + {0x4041, 0x03}, + {0x404c, 0x20}, + {0x404d, 0x00}, + {0x404e, 0x20}, + {0x4203, 0x80}, + {0x4307, 0x30}, + {0x4317, 0x00}, + {0x4502, 0x50}, + {0x4503, 0x08}, + {0x4601, 0x80}, + {0x4800, 0x44}, + {0x4816, 0x53}, + {0x481b, 0x50}, + {0x481f, 0x27}, + {0x4823, 0x3c}, + {0x482b, 0x00}, + {0x4831, 0x66}, + {0x4837, 0x16}, + {0x483c, 0x0f}, + {0x484b, 0x05}, + {0x5000, 0x77}, + {0x5001, 0x0a}, + {0x5003, 0xc8}, + {0x5004, 0x04}, + {0x5006, 0x00}, + {0x5007, 0x00}, + {0x502e, 0x03}, + {0x5030, 0x41}, + {0x5795, 0x00}, + {0x5796, 0x10}, + {0x5797, 0x10}, + {0x5798, 0x73}, + {0x5799, 0x73}, + {0x579a, 0x00}, + {0x579b, 0x28}, + {0x579c, 0x00}, + {0x579d, 0x16}, + {0x579e, 0x06}, + {0x579f, 0x20}, + {0x57a0, 0x04}, + {0x57a1, 0xa0}, + {0x5780, 0x14}, + {0x5781, 0x0f}, + {0x5782, 0x44}, + {0x5783, 0x02}, + {0x5784, 0x01}, + {0x5785, 0x01}, + {0x5786, 0x00}, + {0x5787, 0x04}, + {0x5788, 0x02}, + {0x5789, 0x0f}, + {0x578a, 0xfd}, + {0x578b, 0xf5}, + {0x578c, 0xf5}, + {0x578d, 0x03}, + {0x578e, 0x08}, + {0x578f, 0x0c}, + {0x5790, 0x08}, + {0x5791, 0x04}, + {0x5792, 0x00}, + {0x5793, 0x52}, + {0x5794, 0xa3}, + {0x59f8, 0x3d}, + {0x5a08, 0x02}, + {0x5b00, 0x02}, + {0x5b01, 0x10}, + {0x5b02, 0x03}, + {0x5b03, 0xcf}, + {0x5b05, 0x6c}, + {0x5e00, 0x00}, + {0x5e10, 0xfc} +}; + static const char * const ov8856_test_pattern_menu[] = { "Disabled", "Standard Color Bar", @@ -570,6 +976,18 @@ static const struct ov8856_mode supported_modes[] = { .link_freq_index = OV8856_LINK_FREQ_720MBPS, }, { + .width = 3264, + .height = 2448, + .hts = 1932, + .vts_def = 2482, + .vts_min = 2482, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3264x2448_regs), + .regs = mode_3264x2448_regs, + }, + .link_freq_index = OV8856_LINK_FREQ_720MBPS, + }, + { .width = 1640, .height = 1232, .hts = 3820, @@ -580,6 +998,18 @@ static const struct ov8856_mode supported_modes[] = { .regs = mode_1640x1232_regs, }, .link_freq_index = OV8856_LINK_FREQ_360MBPS, + }, + { + .width = 1632, + .height = 1224, + .hts = 1932, + .vts_def = 2482, + .vts_min = 2482, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1632x1224_regs), + .regs = mode_1632x1224_regs, + }, + .link_freq_index = OV8856_LINK_FREQ_360MBPS, } }; diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index cdbd6dba1122..546393ab0a52 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -18,6 +18,12 @@ #include "camss-video.h" #include "camss.h" +#define CAMSS_FRAME_MIN_WIDTH 1 +#define CAMSS_FRAME_MAX_WIDTH 8191 +#define CAMSS_FRAME_MIN_HEIGHT 1 +#define CAMSS_FRAME_MAX_HEIGHT_RDI 8191 +#define CAMSS_FRAME_MAX_HEIGHT_PIX 4096 + struct fract { u8 numerator; u8 denominator; @@ -529,17 +535,16 @@ static int video_querycap(struct file *file, void *fh, return 0; } -static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +/* + * Returns the index in the video->formats[] array of the element which + * has the "ndx"th unique value of pixelformat field. + * If not found (no more unique pixelformat's) returns -EINVAL. + */ +static int video_get_unique_pixelformat_by_index(struct camss_video *video, + int ndx) { - struct camss_video *video = video_drvdata(file); int i, j, k; - if (f->type != video->type) - return -EINVAL; - - if (f->index >= video->nformats) - return -EINVAL; - /* find index "i" of "k"th unique pixelformat in formats array */ k = -1; for (i = 0; i < video->nformats; i++) { @@ -552,11 +557,53 @@ static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) if (j == i) k++; - if (k == f->index) - break; + if (k == ndx) + return i; + } + + return -EINVAL; +} + +/* + * Returns the index in the video->formats[] array of the element which + * has code equal to mcode. + * If not found returns -EINVAL. + */ +static int video_get_pixelformat_by_mbus_code(struct camss_video *video, + u32 mcode) +{ + int i; + + for (i = 0; i < video->nformats; i++) { + if (video->formats[i].code == mcode) + return i; + } + + return -EINVAL; +} + +static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) +{ + struct camss_video *video = video_drvdata(file); + int i; + + if (f->type != video->type) + return -EINVAL; + + if (f->index >= video->nformats) + return -EINVAL; + + if (f->mbus_code) { + /* Each entry in formats[] table has unique mbus_code */ + if (f->index > 0) + return -EINVAL; + + i = video_get_pixelformat_by_mbus_code(video, f->mbus_code); + } else { + i = video_get_unique_pixelformat_by_index(video, f->index); } - if (k < f->index) + if (i < 0) return -EINVAL; f->pixelformat = video->formats[i].pixelformat; @@ -564,6 +611,36 @@ static int video_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f) return 0; } +static int video_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + struct camss_video *video = video_drvdata(file); + int i; + + if (fsize->index) + return -EINVAL; + + /* Only accept pixel format present in the formats[] table */ + for (i = 0; i < video->nformats; i++) { + if (video->formats[i].pixelformat == fsize->pixel_format) + break; + } + + if (i == video->nformats) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; + fsize->stepwise.min_width = CAMSS_FRAME_MIN_WIDTH; + fsize->stepwise.max_width = CAMSS_FRAME_MAX_WIDTH; + fsize->stepwise.min_height = CAMSS_FRAME_MIN_HEIGHT; + fsize->stepwise.max_height = (video->line_based) ? + CAMSS_FRAME_MAX_HEIGHT_PIX : CAMSS_FRAME_MAX_HEIGHT_RDI; + fsize->stepwise.step_width = 1; + fsize->stepwise.step_height = 1; + + return 0; +} + static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f) { struct camss_video *video = video_drvdata(file); @@ -593,7 +670,7 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) 1, 65528); sizeimage[i] = clamp_t(u32, p->sizeimage, bytesperline[i], - bytesperline[i] * 4096); + bytesperline[i] * CAMSS_FRAME_MAX_HEIGHT_PIX); } for (j = 0; j < video->nformats; j++) @@ -610,8 +687,8 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) memset(pix_mp, 0, sizeof(*pix_mp)); pix_mp->pixelformat = fi->pixelformat; - pix_mp->width = clamp_t(u32, width, 1, 8191); - pix_mp->height = clamp_t(u32, height, 1, 8191); + pix_mp->width = clamp_t(u32, width, 1, CAMSS_FRAME_MAX_WIDTH); + pix_mp->height = clamp_t(u32, height, 1, CAMSS_FRAME_MAX_HEIGHT_RDI); pix_mp->num_planes = fi->planes; for (i = 0; i < pix_mp->num_planes; i++) { bpl = pix_mp->width / fi->hsub[i].numerator * @@ -637,7 +714,7 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f) 1, 65528); p->sizeimage = clamp_t(u32, p->sizeimage, p->bytesperline, - p->bytesperline * 4096); + p->bytesperline * CAMSS_FRAME_MAX_HEIGHT_PIX); lines = p->sizeimage / p->bytesperline; if (p->bytesperline < bytesperline[i]) @@ -704,6 +781,7 @@ static int video_s_input(struct file *file, void *fh, unsigned int input) static const struct v4l2_ioctl_ops msm_vid_ioctl_ops = { .vidioc_querycap = video_querycap, .vidioc_enum_fmt_vid_cap = video_enum_fmt, + .vidioc_enum_framesizes = video_enum_framesizes, .vidioc_g_fmt_vid_cap_mplane = video_g_fmt, .vidioc_s_fmt_vid_cap_mplane = video_s_fmt, .vidioc_try_fmt_vid_cap_mplane = video_try_fmt, @@ -911,8 +989,8 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, } vdev->fops = &msm_vid_fops; - vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING + | V4L2_CAP_READWRITE | V4L2_CAP_IO_MC; vdev->ioctl_ops = &msm_vid_ioctl_ops; vdev->release = msm_video_release; vdev->v4l2_dev = v4l2_dev; diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 203c6538044f..5f8f7b72731c 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -527,6 +527,10 @@ static const struct venus_resources sdm845_res_v2 = { .vmem_size = 0, .vmem_addr = 0, .dma_mask = 0xe0000000 - 1, + .cp_start = 0, + .cp_size = 0x70800000, + .cp_nonpixel_start = 0x1000000, + .cp_nonpixel_size = 0x24800000, .fwname = "qcom/venus-5.2/venus.mdt", }; diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index 7118612673c9..8c88516e4694 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -68,6 +68,10 @@ struct venus_resources { unsigned int vmem_id; u32 vmem_size; u32 vmem_addr; + u32 cp_start; + u32 cp_size; + u32 cp_nonpixel_start; + u32 cp_nonpixel_size; const char *fwname; }; diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index 8801a6a7543d..ac906ffc608f 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -181,6 +181,7 @@ static int venus_shutdown_no_tz(struct venus_core *core) int venus_boot(struct venus_core *core) { struct device *dev = core->dev; + const struct venus_resources *res = core->res; phys_addr_t mem_phys; size_t mem_size; int ret; @@ -200,7 +201,22 @@ int venus_boot(struct venus_core *core) else ret = venus_boot_no_tz(core, mem_phys, mem_size); - return ret; + if (ret) + return ret; + + if (core->use_tz && res->cp_size) { + ret = qcom_scm_mem_protect_video_var(res->cp_start, + res->cp_size, + res->cp_nonpixel_start, + res->cp_nonpixel_size); + if (ret) { + dev_err(dev, "set virtual address ranges fail (%d)\n", + ret); + return ret; + } + } + + return 0; } int venus_shutdown(struct venus_core *core) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index ce136d685d14..2379a22bd132 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -263,6 +263,12 @@ config QCOM_FASTRPC applications DSP processor. Say M if you want to enable this module. +config QCOM_DUMMY + tristate "Qualcomm dummy" + depends on ARCH_QCOM || COMPILE_TEST + help + module. + config SGI_GRU tristate "SGI GRU driver" depends on X86_UV && SMP diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c7bd01ac6291..feeb205fa332 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -57,3 +57,4 @@ obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_HABANA_AI) += habanalabs/ obj-$(CONFIG_UACCE) += uacce/ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o +obj-$(CONFIG_QCOM_DUMMY) += dummy.o diff --git a/drivers/misc/dummy.c b/drivers/misc/dummy.c new file mode 100644 index 000000000000..5c6f2b04a2d9 --- /dev/null +++ b/drivers/misc/dummy.c @@ -0,0 +1,107 @@ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/regulator/consumer.h> + +struct vreg { + const char *name; + int minvolt; + int maxvolt; +} vregs [8] = { + { "vddio", 1800000, 1800000 }, + { "vddaon", 950000, 950000 }, + { "vddpmu", 950000, 950000 }, + { "vddrfa1", 950000, 950000 }, + { "vddrfa2", 1380000, 1380000 }, + { "vddrfa3", 2000000, 2000000 }, + { "vddpcie1", 1350000, 1350000 }, + { "vddpcie2", 2000000, 2000000 }, +}; + +static int dummy_probe(struct platform_device *pdev) +{ + struct regulator_bulk_data regulators[8]; + struct device *dev = &pdev->dev; + int i, ret; + struct pinctrl *pinctrl; + struct pinctrl_state *wlan_en_active; + struct pinctrl_state *bt_en_active; + + for (i = 0; i < ARRAY_SIZE(regulators); i++) + regulators[i].supply = vregs[i].name; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(regulators), regulators); + if (ret < 0) + return ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(regulators), regulators); + if (ret) + return ret; + + pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(pinctrl)) { + ret = PTR_ERR(pinctrl); + pr_err("Failed to get pinctrl, err = %d\n", ret); + return ret; + } + + wlan_en_active = pinctrl_lookup_state(pinctrl, "wlan_en_active"); + if (IS_ERR_OR_NULL(wlan_en_active)) { + ret = PTR_ERR(wlan_en_active); + pr_err("Failed to get wlan_en_active, err = %d\n", ret); + return ret; + } + + bt_en_active = pinctrl_lookup_state(pinctrl, "bt_en_active"); + if (IS_ERR_OR_NULL(bt_en_active)) { + ret = PTR_ERR(bt_en_active); + pr_err("Failed to get bt_en_active, err = %d\n", ret); + return ret; + } + + ret = pinctrl_select_state(pinctrl, wlan_en_active); + if (ret) { + pr_err("Failed to select wlan pinctrl state"); + return ret; + } + udelay(1000); + + ret = pinctrl_select_state(pinctrl, bt_en_active); + if (ret) { + pr_err("Failed to select bt pinctrl state"); + return ret; + } + udelay(1000); + + return 0; +} + +static int dummy_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id dummy_of_match[] = { + { .compatible = "qcom,dummy" }, +}; + +static struct platform_driver dummy_driver = { + .probe = dummy_probe, + .remove = dummy_remove, + .driver = { + .name = "dummy", + .of_match_table = dummy_of_match, + }, +}; + +static int __init dummy_driver_init(void) +{ + return platform_driver_register(&dummy_driver); +} + +postcore_initcall(dummy_driver_init); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig index f780c79aac6f..00b0a2e305b0 100644 --- a/drivers/net/can/spi/Kconfig +++ b/drivers/net/can/spi/Kconfig @@ -15,4 +15,6 @@ config CAN_MCP251X Driver for the Microchip MCP251x and MCP25625 SPI CAN controllers. +source "drivers/net/can/spi/mcp25xxfd/Kconfig" + endmenu diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile index f115b2c46623..f56514541ce4 100644 --- a/drivers/net/can/spi/Makefile +++ b/drivers/net/can/spi/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_CAN_HI311X) += hi311x.o obj-$(CONFIG_CAN_MCP251X) += mcp251x.o + +obj-y += mcp25xxfd/ diff --git a/drivers/net/can/spi/mcp25xxfd/Kconfig b/drivers/net/can/spi/mcp25xxfd/Kconfig new file mode 100644 index 000000000000..f720f1377612 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/Kconfig @@ -0,0 +1,5 @@ +config CAN_MCP25XXFD + tristate "Microchip MCP25xxFD SPI CAN controllers" + depends on HAS_DMA + help + Driver for the Microchip MCP25XXFD SPI FD-CAN controller family. diff --git a/drivers/net/can/spi/mcp25xxfd/Makefile b/drivers/net/can/spi/mcp25xxfd/Makefile new file mode 100644 index 000000000000..a586b2555ff9 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/Makefile @@ -0,0 +1,11 @@ +obj-$(CONFIG_CAN_MCP25XXFD) += mcp25xxfd.o +mcp25xxfd-objs := mcp25xxfd_base.o +mcp25xxfd-objs += mcp25xxfd_can.o +mcp25xxfd-objs += mcp25xxfd_can_fifo.o +mcp25xxfd-objs += mcp25xxfd_can_int.o +mcp25xxfd-objs += mcp25xxfd_can_rx.o +mcp25xxfd-objs += mcp25xxfd_can_tx.o +mcp25xxfd-objs += mcp25xxfd_cmd.o +mcp25xxfd-objs += mcp25xxfd_crc.o +mcp25xxfd-objs += mcp25xxfd_ecc.o +mcp25xxfd-objs += mcp25xxfd_int.o diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c new file mode 100644 index 000000000000..c6b67c54a3cd --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "mcp25xxfd_base.h" +#include "mcp25xxfd_can.h" +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_ecc.h" +#include "mcp25xxfd_int.h" +#include "mcp25xxfd_priv.h" + +int mcp25xxfd_base_power_enable(struct regulator *reg, int enable) +{ + if (enable) + return regulator_enable(reg); + else + return regulator_disable(reg); +} + +static const struct of_device_id mcp25xxfd_of_match[] = { + { + .compatible = "microchip,mcp2517fd", + .data = (void *)CAN_MCP2517FD, + }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp25xxfd_of_match); + +static int mcp25xxfd_base_probe(struct spi_device *spi) +{ + const struct of_device_id *of_id = + of_match_device(mcp25xxfd_of_match, &spi->dev); + struct mcp25xxfd_priv *priv; + struct clk *clk; + enum mcp25xxfd_model model; + u32 freq; + int ret; + + /* Check whether valid IRQ line is defined or not */ + if (spi->irq <= 0) { + dev_err(&spi->dev, "no valid irq line defined: irq = %i\n", + spi->irq); + return -EINVAL; + } + + priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spi_set_drvdata(spi, priv); + priv->spi = spi; + + /* Assign device name */ + snprintf(priv->device_name, sizeof(priv->device_name), + DEVICE_NAME "-%s", dev_name(&priv->spi->dev)); + + /* assign model from of or driver_data */ + if (of_id) + model = (enum mcp25xxfd_model)of_id->data; + else + model = spi_get_device_id(spi)->driver_data; + + clk = devm_clk_get(&spi->dev, NULL); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto out_free; + } + + freq = clk_get_rate(clk); + if (!(freq == CLOCK_4_MHZ || freq == CLOCK_10_MHZ || + freq == CLOCK_40_MHZ)) { + ret = -ERANGE; + goto out_free; + } + + ret = clk_prepare_enable(clk); + if (ret) + goto out_free; + + priv->clk = clk; + priv->clock_freq = freq; + + /* Configure the SPI bus */ + spi->bits_per_word = 8; + + /* The frequency of SCK has to be less than or equal to half the + * frequency of SYSCLK. + */ + spi->max_speed_hz = freq / 2; + ret = spi_setup(spi); + if (ret) + goto out_clk; + + priv->power = devm_regulator_get(&spi->dev, "vdd"); + if (IS_ERR(priv->power)) { + if (PTR_ERR(priv->power) != -EPROBE_DEFER) + dev_err(&spi->dev, "failed to get vdd\n"); + ret = PTR_ERR(priv->power); + goto out_clk; + } + + ret = mcp25xxfd_base_power_enable(priv->power, 1); + if (ret) + goto out_clk; + + /* disable interrupts */ + ret = mcp25xxfd_int_enable(priv, false); + if (ret) + goto out_power; + + /* setup ECC for SRAM */ + ret = mcp25xxfd_ecc_enable(priv); + if (ret) + goto out_power; + + /* setting up CAN */ + ret = mcp25xxfd_can_setup(priv); + if (ret) + goto out_power; + + dev_info(&spi->dev, + "MCP%04x successfully initialized.\n", model); + return 0; + +out_power: + mcp25xxfd_base_power_enable(priv->power, 0); +out_clk: + clk_disable_unprepare(clk); +out_free: + dev_err(&spi->dev, "Probe failed, err=%d\n", -ret); + return ret; +} + +static int mcp25xxfd_base_remove(struct spi_device *spi) +{ + struct mcp25xxfd_priv *priv = spi_get_drvdata(spi); + + mcp25xxfd_can_remove(priv); + mcp25xxfd_base_power_enable(priv->power, 0); + clk_disable_unprepare(priv->clk); + + return 0; +} + +static const struct spi_device_id mcp25xxfd_id_table[] = { + { + .name = "mcp2517fd", + .driver_data = (kernel_ulong_t)CAN_MCP2517FD, + }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp25xxfd_id_table); + +static struct spi_driver mcp25xxfd_can_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = mcp25xxfd_of_match, + }, + .id_table = mcp25xxfd_id_table, + .probe = mcp25xxfd_base_probe, + .remove = mcp25xxfd_base_remove, +}; +module_spi_driver(mcp25xxfd_can_driver); + +MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>"); +MODULE_DESCRIPTION("Microchip 25XXFD CAN driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.h new file mode 100644 index 000000000000..4559ac60645c --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ +#ifndef __MCP25XXFD_BASE_H +#define __MCP25XXFD_BASE_H + +#include <linux/regulator/consumer.h> + +int mcp25xxfd_base_power_enable(struct regulator *reg, int enable); + +#endif /* __MCP25XXFD_BASE_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c new file mode 100644 index 000000000000..1417f1a22d6e --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c @@ -0,0 +1,538 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#include <linux/bitfield.h> +#include <linux/can/core.h> +#include <linux/can/dev.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#include "mcp25xxfd_base.h" +#include "mcp25xxfd_can_fifo.h" +#include "mcp25xxfd_can_int.h" +#include "mcp25xxfd_can_priv.h" +#include "mcp25xxfd_can_tx.h" +#include "mcp25xxfd_can.h" +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_int.h" +#include "mcp25xxfd_priv.h" +#include "mcp25xxfd_regs.h" + +#include <uapi/linux/can/netlink.h> + +/* everything related to bit timing */ +static +const struct can_bittiming_const mcp25xxfd_can_nominal_bittiming_const = { + .name = DEVICE_NAME, + .tseg1_min = 2, + .tseg1_max = BIT(MCP25XXFD_CAN_NBTCFG_TSEG1_BITS), + .tseg2_min = 1, + .tseg2_max = BIT(MCP25XXFD_CAN_NBTCFG_TSEG2_BITS), + .sjw_max = BIT(MCP25XXFD_CAN_NBTCFG_SJW_BITS), + .brp_min = 1, + .brp_max = BIT(MCP25XXFD_CAN_NBTCFG_BRP_BITS), + .brp_inc = 1, +}; + +static +const struct can_bittiming_const mcp25xxfd_can_data_bittiming_const = { + .name = DEVICE_NAME, + .tseg1_min = 1, + .tseg1_max = BIT(MCP25XXFD_CAN_DBTCFG_TSEG1_BITS), + .tseg2_min = 1, + .tseg2_max = BIT(MCP25XXFD_CAN_DBTCFG_TSEG2_BITS), + .sjw_max = BIT(MCP25XXFD_CAN_DBTCFG_SJW_BITS), + .brp_min = 1, + .brp_max = BIT(MCP25XXFD_CAN_DBTCFG_BRP_BITS), + .brp_inc = 1, +}; + +static int mcp25xxfd_can_do_set_nominal_bittiming(struct net_device *net) +{ + struct mcp25xxfd_can_priv *cpriv = netdev_priv(net); + struct can_bittiming *bt = &cpriv->can.bittiming; + int sjw = bt->sjw; + int pseg2 = bt->phase_seg2; + int pseg1 = bt->phase_seg1; + int propseg = bt->prop_seg; + int brp = bt->brp; + int tseg1 = propseg + pseg1; + int tseg2 = pseg2; + + /* calculate nominal bit timing */ + cpriv->regs.nbtcfg = ((sjw - 1) << MCP25XXFD_CAN_NBTCFG_SJW_SHIFT) | + ((tseg2 - 1) << MCP25XXFD_CAN_NBTCFG_TSEG2_SHIFT) | + ((tseg1 - 1) << MCP25XXFD_CAN_NBTCFG_TSEG1_SHIFT) | + ((brp - 1) << MCP25XXFD_CAN_NBTCFG_BRP_SHIFT); + + return mcp25xxfd_cmd_write(cpriv->priv->spi, MCP25XXFD_CAN_NBTCFG, + cpriv->regs.nbtcfg); +} + +static int mcp25xxfd_can_do_set_data_bittiming(struct net_device *net) +{ + struct mcp25xxfd_can_priv *cpriv = netdev_priv(net); + struct mcp25xxfd_priv *priv = cpriv->priv; + struct can_bittiming *bt = &cpriv->can.data_bittiming; + struct spi_device *spi = priv->spi; + int sjw = bt->sjw; + int pseg2 = bt->phase_seg2; + int pseg1 = bt->phase_seg1; + int propseg = bt->prop_seg; + int brp = bt->brp; + int tseg1 = propseg + pseg1; + int tseg2 = pseg2; + int tdco; + int ret; + + /* set up Transmitter delay compensation */ + cpriv->regs.tdc = FIELD_PREP(MCP25XXFD_CAN_TDC_TDCMOD_MASK, + MCP25XXFD_CAN_TDC_TDCMOD_AUTO); + + /* configure TDC offsets */ + tdco = clamp_t(int, bt->brp * tseg1, -64, 63); + cpriv->regs.tdc &= ~MCP25XXFD_CAN_TDC_TDCO_MASK; + cpriv->regs.tdc |= FIELD_PREP(MCP25XXFD_CAN_TDC_TDCO_MASK, tdco); + + /* set TDC */ + ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_CAN_TDC, cpriv->regs.tdc); + if (ret) + return ret; + + /* calculate data bit timing */ + cpriv->regs.dbtcfg = + FIELD_PREP(MCP25XXFD_CAN_DBTCFG_SJW_MASK, (sjw - 1)) | + FIELD_PREP(MCP25XXFD_CAN_DBTCFG_TSEG2_MASK, (tseg2 - 1)) | + FIELD_PREP(MCP25XXFD_CAN_DBTCFG_TSEG1_MASK, (tseg1 - 1)) | + FIELD_PREP(MCP25XXFD_CAN_DBTCFG_BRP_MASK, (brp - 1)); + + return mcp25xxfd_cmd_write(spi, MCP25XXFD_CAN_DBTCFG, + cpriv->regs.dbtcfg); +} + +int mcp25xxfd_can_get_mode(struct mcp25xxfd_priv *priv, u32 *reg) +{ + int ret; + + ret = mcp25xxfd_cmd_read(priv->spi, MCP25XXFD_CAN_CON, reg); + if (ret) + return ret; + + return FIELD_GET(MCP25XXFD_CAN_CON_OPMOD_MASK, *reg); +} + +int mcp25xxfd_can_switch_mode_no_wait(struct mcp25xxfd_priv *priv, + u32 *reg, int mode) +{ + int ret; + + ret = mcp25xxfd_can_get_mode(priv, reg); + if (ret < 0) + return ret; + + /* Compute the effective mode in osc*/ + *reg &= ~(MCP25XXFD_CAN_CON_REQOP_MASK | + MCP25XXFD_CAN_CON_OPMOD_MASK); + *reg |= FIELD_PREP(MCP25XXFD_CAN_CON_REQOP_MASK, mode) | + FIELD_PREP(MCP25XXFD_CAN_CON_OPMOD_MASK, mode); + + /* Request the mode switch */ + return mcp25xxfd_cmd_write(priv->spi, MCP25XXFD_CAN_CON, *reg); +} + +int mcp25xxfd_can_switch_mode(struct mcp25xxfd_priv *priv, u32 *reg, int mode) +{ + int ret, i; + + /* trigger the mode switch itself */ + ret = mcp25xxfd_can_switch_mode_no_wait(priv, reg, mode); + if (ret) + return ret; + + /* Wait for 256 rounds to stabilize. This is essentially > 12ms + * at 1MHz + */ + for (i = 0; i < 256; i++) { + /* get the mode */ + ret = mcp25xxfd_can_get_mode(priv, reg); + if (ret < 0) + return ret; + /* check that we have reached our mode */ + if (ret == mode) + return 0; + } + + dev_err(&priv->spi->dev, "Failed to switch to mode %u in time\n", + mode); + + return -ETIMEDOUT; +} + +static int mcp25xxfd_can_probe_modeswitch(struct mcp25xxfd_priv *priv) +{ + u32 mode_data; + int ret; + + /* We should be in config mode now, so move to INT_LOOPBACK */ + ret = mcp25xxfd_can_switch_mode(priv, &mode_data, + MCP25XXFD_CAN_CON_MODE_INT_LOOPBACK); + if (ret) { + dev_err(&priv->spi->dev, + "Failed to switch into loopback mode\n"); + return ret; + } + + /* Switch back into config mode */ + ret = mcp25xxfd_can_switch_mode(priv, &mode_data, + MCP25XXFD_CAN_CON_MODE_CONFIG); + if (ret) { + dev_err(&priv->spi->dev, + "Failed to switch back to config mode\n"); + return ret; + } + + return 0; +} + +int mcp25xxfd_can_probe(struct mcp25xxfd_priv *priv) +{ + struct spi_device *spi = priv->spi; + u32 mode_data; + int mode, ret; + + /* For sanity check read TXQCON register. The TXEN bit should always + * be read as 1. + */ + ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_CAN_TXQCON, &mode_data); + if (ret) + return ret; + + if ((mode_data & MCP25XXFD_CAN_TXQCON_TXEN) == 0) { + dev_err(&spi->dev, "TXQCON does not have TXEN bit set"); + return -EINVAL; + } + + /* Try to get the current mode */ + mode = mcp25xxfd_can_get_mode(priv, &mode_data); + if (mode < 0) + return mode; + + /* SPI-reset should have moved us into config mode. But then the + * documentation says that SPI-reset may only work reliably when already + * in config mode. So if we are in config mode then everything is fine + * and we check that a mode switch works properly. + */ + if (mode == MCP25XXFD_CAN_CON_MODE_CONFIG) + return mcp25xxfd_can_probe_modeswitch(priv); + + /* Any other mode is unexpected */ + dev_err(&spi->dev, + "Found controller in unexpected mode: %d\n", mode); + + /* Once again try to move to config mode. If this fails, we'll + * bail out. + */ + ret = mcp25xxfd_can_switch_mode(priv, &mode_data, + MCP25XXFD_CAN_CON_MODE_CONFIG); + if (ret) { + dev_err(&priv->spi->dev, + "Unable to switch to config mode\n"); + return -EINVAL; + } + + /* Finally check if modeswitch is really working */ + return mcp25xxfd_can_probe_modeswitch(priv); +} + +static int mcp25xxfd_can_config(struct net_device *net) +{ + struct mcp25xxfd_can_priv *cpriv = netdev_priv(net); + struct mcp25xxfd_priv *priv = cpriv->priv; + struct spi_device *spi = priv->spi; + int ret; + + /* setup value of con_register */ + cpriv->regs.con = MCP25XXFD_CAN_CON_STEF; /* enable TEF, disable TXQ */ + + /* non iso FD mode */ + if (!(cpriv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO)) + cpriv->regs.con |= MCP25XXFD_CAN_CON_ISOCRCEN; + + /* one shot */ + if (cpriv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) + cpriv->regs.con |= MCP25XXFD_CAN_CON_RTXAT; + + /* apply it now together with a mode switch */ + ret = mcp25xxfd_can_switch_mode(cpriv->priv, &cpriv->regs.con, + MCP25XXFD_CAN_CON_MODE_CONFIG); + if (ret) + return 0; + + /* time stamp control register - 1ns resolution */ + cpriv->regs.tscon = 0; + ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_CAN_TBC, 0); + if (ret) + return ret; + + cpriv->regs.tscon = MCP25XXFD_CAN_TSCON_TBCEN | + FIELD_PREP(MCP25XXFD_CAN_TSCON_TBCPRE_MASK, + ((cpriv->can.clock.freq / 1000000))); + ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_CAN_TSCON, cpriv->regs.tscon); + if (ret) + return ret; + + /* setup fifos */ + ret = mcp25xxfd_can_fifo_setup(cpriv); + if (ret) + return ret; + + /* setup can bittiming now - the do_set_bittiming methods + * are not used as they get called before open + */ + ret = mcp25xxfd_can_do_set_nominal_bittiming(net); + if (ret) + return ret; + + ret = mcp25xxfd_can_do_set_data_bittiming(net); + if (ret) + return ret; + + return ret; +} + +/* mode setting */ +static int mcp25xxfd_can_do_set_mode(struct net_device *net, + enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +/* binary error counters */ +static int mcp25xxfd_can_get_berr_counter(const struct net_device *net, + struct can_berr_counter *bec) +{ + struct mcp25xxfd_can_priv *cpriv = netdev_priv(net); + + bec->txerr = FIELD_PREP(MCP25XXFD_CAN_TREC_TEC_MASK, + cpriv->status.trec); + bec->rxerr = FIELD_PREP(MCP25XXFD_CAN_TREC_REC_MASK, + cpriv->status.trec); + + return 0; +} + +static int mcp25xxfd_can_open(struct net_device *net) +{ + struct mcp25xxfd_can_priv *cpriv = netdev_priv(net); + struct spi_device *spi = cpriv->priv->spi; + int ret, mode; + + ret = open_candev(net); + if (ret) { + netdev_err(net, "unable to set initial baudrate!\n"); + return ret; + } + + /* request an IRQ but keep disabled for now */ + ret = request_threaded_irq(spi->irq, NULL, + mcp25xxfd_can_int, + IRQF_ONESHOT | IRQF_TRIGGER_LOW, + cpriv->priv->device_name, cpriv); + if (ret) { + dev_err(&spi->dev, "failed to acquire irq %d - %i\n", + spi->irq, ret); + goto out_candev; + } + + disable_irq(spi->irq); + cpriv->irq.allocated = true; + cpriv->irq.enabled = false; + + /* enable power to the transceiver */ + ret = mcp25xxfd_base_power_enable(cpriv->transceiver, 1); + if (ret) + goto out_irq; + + /* configure controller for reception */ + ret = mcp25xxfd_can_config(net); + if (ret) + goto out_power; + + /* setting up state */ + cpriv->can.state = CAN_STATE_ERROR_ACTIVE; + + /* enable interrupts */ + ret = mcp25xxfd_int_enable(cpriv->priv, true); + if (ret) + goto out_canconfig; + + if (cpriv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + mode = MCP25XXFD_CAN_CON_MODE_EXT_LOOPBACK; + else if (cpriv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + mode = MCP25XXFD_CAN_CON_MODE_LISTENONLY; + else if (cpriv->can.ctrlmode & CAN_CTRLMODE_FD) + mode = MCP25XXFD_CAN_CON_MODE_MIXED; + else + mode = MCP25XXFD_CAN_CON_MODE_CAN2_0; + + /* switch to active mode */ + ret = mcp25xxfd_can_switch_mode(cpriv->priv, &cpriv->regs.con, mode); + if (ret) + goto out_int; + + /* start the tx_queue */ + mcp25xxfd_can_tx_queue_manage(cpriv, + MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED); + + return 0; + +out_int: + mcp25xxfd_int_enable(cpriv->priv, false); +out_canconfig: + mcp25xxfd_can_fifo_release(cpriv); +out_power: + mcp25xxfd_base_power_enable(cpriv->transceiver, 0); +out_irq: + free_irq(spi->irq, cpriv); + cpriv->irq.allocated = false; + cpriv->irq.enabled = false; +out_candev: + close_candev(net); + return ret; +} + +static void mcp25xxfd_can_shutdown(struct mcp25xxfd_can_priv *cpriv) +{ + /* switch us to CONFIG mode - this disables the controller */ + mcp25xxfd_can_switch_mode(cpriv->priv, &cpriv->regs.con, + MCP25XXFD_CAN_CON_MODE_CONFIG); +} + +static int mcp25xxfd_can_stop(struct net_device *net) +{ + struct mcp25xxfd_can_priv *cpriv = netdev_priv(net); + struct mcp25xxfd_priv *priv = cpriv->priv; + struct spi_device *spi = priv->spi; + + /* stop transmit queue */ + mcp25xxfd_can_tx_queue_manage(cpriv, + MCP25XXFD_CAN_TX_QUEUE_STATE_STOPPED); + + /* shutdown the can controller */ + mcp25xxfd_can_shutdown(cpriv); + + /* disable inerrupts on controller */ + mcp25xxfd_int_enable(cpriv->priv, false); + + /* disable the transceiver */ + mcp25xxfd_base_power_enable(cpriv->transceiver, 0); + + /* disable interrupt on host */ + free_irq(spi->irq, cpriv); + cpriv->irq.allocated = false; + cpriv->irq.enabled = false; + + /* close the can_decice */ + close_candev(net); + + return 0; +} + +static const struct net_device_ops mcp25xxfd_netdev_ops = { + .ndo_open = mcp25xxfd_can_open, + .ndo_stop = mcp25xxfd_can_stop, + .ndo_start_xmit = mcp25xxfd_can_tx_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +/* probe and remove */ +int mcp25xxfd_can_setup(struct mcp25xxfd_priv *priv) +{ + struct spi_device *spi = priv->spi; + struct mcp25xxfd_can_priv *cpriv; + struct net_device *net; + struct regulator *transceiver; + int ret; + + /* get transceiver power regulator*/ + transceiver = devm_regulator_get(&spi->dev, "xceiver"); + if (PTR_ERR(transceiver) == -EPROBE_DEFER) + return PTR_ERR(transceiver); + + /* allocate can device */ + net = alloc_candev(sizeof(*cpriv), TX_ECHO_SKB_MAX); + if (!net) + return -ENOMEM; + + cpriv = netdev_priv(net); + cpriv->priv = priv; + priv->cpriv = cpriv; + + /* setup network */ + SET_NETDEV_DEV(net, &spi->dev); + net->netdev_ops = &mcp25xxfd_netdev_ops; + net->flags |= IFF_ECHO; + + cpriv->transceiver = transceiver; + + cpriv->can.clock.freq = priv->clock_freq; + cpriv->can.bittiming_const = + &mcp25xxfd_can_nominal_bittiming_const; + cpriv->can.data_bittiming_const = + &mcp25xxfd_can_data_bittiming_const; + + /* we are not setting bit-timing methods here as they get called by + * the framework before open. So the controller would be still in sleep + * mode, which does not help as things are configured in open instead. + */ + cpriv->can.do_set_mode = + mcp25xxfd_can_do_set_mode; + cpriv->can.do_get_berr_counter = + mcp25xxfd_can_get_berr_counter; + cpriv->can.ctrlmode_supported = + CAN_CTRLMODE_FD | + CAN_CTRLMODE_FD_NON_ISO | + CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_ONE_SHOT; + + ret = register_candev(net); + if (ret) { + dev_err(&spi->dev, "Failed to register can device\n"); + goto out; + } + + return 0; + +out: + free_candev(net); + priv->cpriv = NULL; + + return ret; +} + +void mcp25xxfd_can_remove(struct mcp25xxfd_priv *priv) +{ + if (priv->cpriv) { + unregister_candev(priv->cpriv->can.dev); + free_candev(priv->cpriv->can.dev); + priv->cpriv = NULL; + } +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h new file mode 100644 index 000000000000..8ec20827f099 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#ifndef __MCP25XXFD_CAN_H +#define __MCP25XXFD_CAN_H + +#include "mcp25xxfd_can_priv.h" +#include "mcp25xxfd_priv.h" +#include "mcp25xxfd_regs.h" + +/* get the optimal controller target mode */ +static inline +int mcp25xxfd_can_targetmode(struct mcp25xxfd_can_priv *cpriv) +{ + return (cpriv->can.dev->mtu == CAN_MTU) ? + MCP25XXFD_CAN_CON_MODE_CAN2_0 : MCP25XXFD_CAN_CON_MODE_MIXED; +} + +static inline +void mcp25xxfd_can_queue_frame(struct mcp25xxfd_can_priv *cpriv, + s32 fifo, u16 ts, bool is_rx) +{ + int idx = cpriv->fifos.submit_queue_count; + + cpriv->fifos.submit_queue[idx].fifo = fifo; + cpriv->fifos.submit_queue[idx].ts = ts; + cpriv->fifos.submit_queue[idx].is_rx = is_rx; + + cpriv->fifos.submit_queue_count++; +} + +/* get the current controller mode */ +int mcp25xxfd_can_get_mode(struct mcp25xxfd_priv *priv, u32 *reg); + +/* switch controller mode */ +int mcp25xxfd_can_switch_mode_no_wait(struct mcp25xxfd_priv *priv, + u32 *reg, int mode); +int mcp25xxfd_can_switch_mode(struct mcp25xxfd_priv *priv, + u32 *reg, int mode); + +/* probe the can controller */ +int mcp25xxfd_can_probe(struct mcp25xxfd_priv *priv); + +/* setup and the can controller net interface */ +int mcp25xxfd_can_setup(struct mcp25xxfd_priv *priv); +void mcp25xxfd_can_remove(struct mcp25xxfd_priv *priv); + +#endif /* __MCP25XXFD_CAN_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c new file mode 100644 index 000000000000..4a1ad250bdaf --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spi/spi.h> + +#include "mcp25xxfd_can.h" +#include "mcp25xxfd_can_fifo.h" +#include "mcp25xxfd_can_priv.h" +#include "mcp25xxfd_can_tx.h" +#include "mcp25xxfd_cmd.h" + +static int mcp25xxfd_can_fifo_get_address(struct mcp25xxfd_can_priv *cpriv) +{ + int fifo, ret; + + /* we need to move out of config mode to force address computation */ + ret = mcp25xxfd_can_switch_mode(cpriv->priv, &cpriv->regs.con, + MCP25XXFD_CAN_CON_MODE_INT_LOOPBACK); + if (ret) + return ret; + + /* and get back into config mode */ + ret = mcp25xxfd_can_switch_mode(cpriv->priv, &cpriv->regs.con, + MCP25XXFD_CAN_CON_MODE_CONFIG); + if (ret) + return ret; + + /* read address and config back in */ + for (fifo = 1; fifo < 32; fifo++) { + ret = mcp25xxfd_cmd_read(cpriv->priv->spi, + MCP25XXFD_CAN_FIFOUA(fifo), + &cpriv->fifos.info[fifo].offset); + if (ret) + return ret; + } + + return 0; +} + +static int mcp25xxfd_can_fifo_setup_config(struct mcp25xxfd_can_priv *cpriv, + struct mcp25xxfd_fifo *desc, + u32 flags, u32 flags_last) +{ + u32 val; + int i, p, f, c, ret; + + for (i = 0, f = desc->start, c = desc->count, p = 31; + c > 0; i++, f++, p--, c--) { + val = (c > 1) ? flags : flags_last; + + /* are we in tx mode? */ + if (flags & MCP25XXFD_CAN_FIFOCON_TXEN) { + cpriv->fifos.info[f].is_rx = false; + cpriv->fifos.info[f].priority = p; + val |= (p << MCP25XXFD_CAN_FIFOCON_TXPRI_SHIFT); + } else { + cpriv->fifos.info[f].is_rx = true; + } + + /* write the config to the controller in one go */ + ret = mcp25xxfd_cmd_write(cpriv->priv->spi, + MCP25XXFD_CAN_FIFOCON(f), val); + if (ret) + return ret; + } + + return 0; +} + +static int mcp25xxfd_can_fifo_setup_tx(struct mcp25xxfd_can_priv *cpriv) +{ + u32 tx_flags = MCP25XXFD_CAN_FIFOCON_FRESET | /* reset FIFO */ + MCP25XXFD_CAN_FIFOCON_TXEN | /* a tx FIFO */ + MCP25XXFD_CAN_FIFOCON_TXATIE | /* state in txatif */ + (cpriv->fifos.payload_mode << + MCP25XXFD_CAN_FIFOCON_PLSIZE_SHIFT) | /* paylod size */ + (0 << MCP25XXFD_CAN_FIFOCON_FSIZE_SHIFT); /* 1 FIFO deep */ + + /* handle oneshot/three-shot */ + if (cpriv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) + tx_flags |= MCP25XXFD_CAN_FIFOCON_TXAT_ONE_SHOT << + MCP25XXFD_CAN_FIFOCON_TXAT_SHIFT; + else + tx_flags |= MCP25XXFD_CAN_FIFOCON_TXAT_UNLIMITED << + MCP25XXFD_CAN_FIFOCON_TXAT_SHIFT; + + return mcp25xxfd_can_fifo_setup_config(cpriv, &cpriv->fifos.tx, + tx_flags, tx_flags); +} + +static int mcp25xxfd_can_fifo_setup_rx(struct mcp25xxfd_can_priv *cpriv) +{ + u32 rx_flags = MCP25XXFD_CAN_FIFOCON_FRESET | /* reset FIFO */ + MCP25XXFD_CAN_FIFOCON_RXTSEN | /* RX timestamps */ + MCP25XXFD_CAN_FIFOCON_TFERFFIE | /* FIFO Full */ + MCP25XXFD_CAN_FIFOCON_TFHRFHIE | /* FIFO Half Full*/ + MCP25XXFD_CAN_FIFOCON_TFNRFNIE | /* FIFO not empty */ + (cpriv->fifos.payload_mode << + MCP25XXFD_CAN_FIFOCON_PLSIZE_SHIFT) | + (0 << MCP25XXFD_CAN_FIFOCON_FSIZE_SHIFT); /* 1 FIFO deep */ + /* enable overflow int on last fifo */ + u32 rx_flags_last = rx_flags | MCP25XXFD_CAN_FIFOCON_RXOVIE; + + return mcp25xxfd_can_fifo_setup_config(cpriv, &cpriv->fifos.rx, + rx_flags, rx_flags_last); +} + +static int mcp25xxfd_can_fifo_setup_rxfilter(struct mcp25xxfd_can_priv *cpriv) +{ + u8 filter_con[32]; + int c, f; + + /* clear the filters and filter mappings for all filters */ + memset(filter_con, 0, sizeof(filter_con)); + + /* and now set up the rx filters */ + for (c = 0, f = cpriv->fifos.rx.start; c < cpriv->fifos.rx.count; + c++, f++) { + /* set up filter config - we can use the mask of filter 0 */ + filter_con[c] = MCP25XXFD_CAN_FIFOCON_FLTEN(0) | + (f << MCP25XXFD_CAN_FILCON_SHIFT(0)); + } + + /* and set up filter control */ + return mcp25xxfd_cmd_write_regs(cpriv->priv->spi, + MCP25XXFD_CAN_FLTCON(0), + (u32 *)filter_con, sizeof(filter_con)); +} + +static int mcp25xxfd_can_fifo_compute(struct mcp25xxfd_can_priv *cpriv) +{ + int tef_memory_used, tx_memory_used, rx_memory_available; + + switch (cpriv->can.dev->mtu) { + case CAN_MTU: + /* MTU is 8 */ + cpriv->fifos.payload_size = 8; + cpriv->fifos.payload_mode = MCP25XXFD_CAN_TXQCON_PLSIZE_8; + + /* 7 tx fifos */ + cpriv->fifos.tx.count = 7; + + break; + case CANFD_MTU: + /* MTU is 64 */ + cpriv->fifos.payload_size = 64; + cpriv->fifos.payload_mode = MCP25XXFD_CAN_TXQCON_PLSIZE_64; + + /* 7 tx fifos */ + cpriv->fifos.tx.count = 7; + + break; + default: + return -EINVAL; + } + + /* compute effective sizes */ + cpriv->fifos.tef.size = sizeof(struct mcp25xxfd_can_obj_tef); + cpriv->fifos.tx.size = sizeof(struct mcp25xxfd_can_obj_tx) + + cpriv->fifos.payload_size; + cpriv->fifos.rx.size = sizeof(struct mcp25xxfd_can_obj_rx) + + cpriv->fifos.payload_size; + + /* set tef fifos to the number of tx fifos */ + cpriv->fifos.tef.count = cpriv->fifos.tx.count; + + /* compute size of the tx fifos and TEF */ + tx_memory_used = cpriv->fifos.tx.count * cpriv->fifos.tx.size; + tef_memory_used = cpriv->fifos.tef.count * cpriv->fifos.tef.size; + + /* calculate evailable memory for RX_fifos */ + rx_memory_available = MCP25XXFD_SRAM_SIZE - tx_memory_used - + tef_memory_used; + + /* we need at least one RX Frame */ + if (rx_memory_available < cpriv->fifos.rx.size) { + netdev_err(cpriv->can.dev, + "Configured %i tx-fifos exceeds available memory already\n", + cpriv->fifos.tx.count); + return -EINVAL; + } + + /* calculate possible amount of RX fifos */ + cpriv->fifos.rx.count = rx_memory_available / cpriv->fifos.rx.size; + + /* now calculate effective number of rx-fifos. There are only 31 fifos + * available in total, so we need to limit ourselves + */ + if (cpriv->fifos.rx.count + cpriv->fifos.tx.count > 31) + cpriv->fifos.rx.count = 31 - cpriv->fifos.tx.count; + + cpriv->fifos.tx.start = 1; + cpriv->fifos.rx.start = cpriv->fifos.tx.start + cpriv->fifos.tx.count; + + return 0; +} + +static int mcp25xxfd_can_fifo_clear_regs(struct mcp25xxfd_can_priv *cpriv, + u32 start, u32 end) +{ + size_t len = end - start; + u8 *data; + int ret; + + data = kzalloc(len, GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = mcp25xxfd_cmd_write_regs(cpriv->priv->spi, + start, (u32 *)data, len); + + kfree(data); + + return ret; +} + +static int mcp25xxfd_can_fifo_clear(struct mcp25xxfd_can_priv *cpriv) +{ + int ret; + + memset(&cpriv->fifos.info, 0, sizeof(cpriv->fifos.info)); + memset(&cpriv->fifos.tx, 0, sizeof(cpriv->fifos.tx)); + memset(&cpriv->fifos.rx, 0, sizeof(cpriv->fifos.rx)); + + /* clear FIFO config */ + ret = mcp25xxfd_can_fifo_clear_regs(cpriv, MCP25XXFD_CAN_FIFOCON(1), + MCP25XXFD_CAN_FIFOCON(32)); + if (ret) + return ret; + + /* clear the filter mask - match any frame with every filter */ + return mcp25xxfd_can_fifo_clear_regs(cpriv, MCP25XXFD_CAN_FLTCON(0), + MCP25XXFD_CAN_FLTCON(32)); +} + +int mcp25xxfd_can_fifo_setup(struct mcp25xxfd_can_priv *cpriv) +{ + int ret; + + /* clear fifo config */ + ret = mcp25xxfd_can_fifo_clear(cpriv); + if (ret) + return ret; + + ret = mcp25xxfd_can_fifo_compute(cpriv); + if (ret) + return ret; + + /* configure TEF */ + if (cpriv->fifos.tef.count) + cpriv->regs.tefcon = + MCP25XXFD_CAN_TEFCON_FRESET | + MCP25XXFD_CAN_TEFCON_TEFNEIE | + MCP25XXFD_CAN_TEFCON_TEFTSEN | + ((cpriv->fifos.tef.count - 1) << + MCP25XXFD_CAN_TEFCON_FSIZE_SHIFT); + else + cpriv->regs.tefcon = 0; + ret = mcp25xxfd_cmd_write(cpriv->priv->spi, MCP25XXFD_CAN_TEFCON, + cpriv->regs.tefcon); + if (ret) + return ret; + + /* TXQueue disabled */ + ret = mcp25xxfd_cmd_write(cpriv->priv->spi, MCP25XXFD_CAN_TXQCON, 0); + if (ret) + return ret; + + /* configure FIFOS themselves */ + ret = mcp25xxfd_can_fifo_setup_tx(cpriv); + if (ret) + return ret; + + ret = mcp25xxfd_can_fifo_setup_rx(cpriv); + if (ret) + return ret; + + ret = mcp25xxfd_can_fifo_setup_rxfilter(cpriv); + if (ret) + return ret; + + /* get fifo addresses */ + ret = mcp25xxfd_can_fifo_get_address(cpriv); + if (ret) + return ret; + + /* setup tx_fifo_queue */ + ret = mcp25xxfd_can_tx_queue_alloc(cpriv); + if (ret) + return ret; + + return 0; +} + +void mcp25xxfd_can_fifo_release(struct mcp25xxfd_can_priv *cpriv) +{ + mcp25xxfd_can_tx_queue_free(cpriv); + mcp25xxfd_can_fifo_clear(cpriv); +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.h new file mode 100644 index 000000000000..ed2daa05220a --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#ifndef __MCP25XXFD_CAN_FIFO_H +#define __MCP25XXFD_CAN_FIFO_H + +#include "mcp25xxfd_can_priv.h" + +int mcp25xxfd_can_fifo_setup(struct mcp25xxfd_can_priv *cpriv); +void mcp25xxfd_can_fifo_release(struct mcp25xxfd_can_priv *cpriv); + +#endif /* __MCP25XXFD_CAN_FIFO_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_id.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_id.h new file mode 100644 index 000000000000..00a6c6639bd5 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_id.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#ifndef __MCP25XXFD_CAN_IF_H +#define __MCP25XXFD_CAN_IF_H + +#include <uapi/linux/can.h> + +#include "mcp25xxfd_can_id.h" +#include "mcp25xxfd_regs.h" + +/* ideally these would be defined in uapi/linux/can.h */ +#define MCP25XXFD_CAN_EFF_SID_SHIFT (CAN_EFF_ID_BITS - CAN_SFF_ID_BITS) +#define MCP25XXFD_CAN_EFF_SID_BITS CAN_SFF_ID_BITS +#define MCP25XXFD_CAN_EFF_SID_MASK \ + GENMASK(MCP25XXFD_CAN_EFF_SID_SHIFT + MCP25XXFD_CAN_EFF_SID_BITS - 1, \ + MCP25XXFD_CAN_EFF_SID_SHIFT) +#define MCP25XXFD_CAN_EFF_EID_SHIFT 0 +#define MCP25XXFD_CAN_EFF_EID_BITS MCP25XXFD_CAN_EFF_SID_SHIFT +#define MCP25XXFD_CAN_EFF_EID_MASK \ + GENMASK(MCP25XXFD_CAN_EFF_EID_SHIFT + MCP25XXFD_CAN_EFF_EID_BITS - 1, \ + MCP25XXFD_CAN_EFF_EID_SHIFT) + +static inline +void mcp25xxfd_can_id_from_mcp25xxfd(u32 mcp_id, u32 mcp_flags, u32 *can_id) +{ + u32 sid = (mcp_id & MCP25XXFD_CAN_OBJ_ID_SID_MASK) >> + MCP25XXFD_CAN_OBJ_ID_SID_SHIFT; + u32 eid = (mcp_id & MCP25XXFD_CAN_OBJ_ID_EID_MASK) >> + MCP25XXFD_CAN_OBJ_ID_EID_SHIFT; + + /* select normal or extended ids */ + if (mcp_flags & MCP25XXFD_CAN_OBJ_FLAGS_IDE) { + *can_id = (eid << MCP25XXFD_CAN_EFF_EID_SHIFT) | + (sid << MCP25XXFD_CAN_EFF_SID_SHIFT) | + CAN_EFF_FLAG; + } else { + *can_id = sid << MCP25XXFD_CAN_EFF_EID_SHIFT; + } + /* handle rtr */ + *can_id |= (mcp_flags & MCP25XXFD_CAN_OBJ_FLAGS_RTR) ? CAN_RTR_FLAG : 0; +} + +static inline +void mcp25xxfd_can_id_to_mcp25xxfd(u32 can_id, u32 *id, u32 *flags) +{ + /* depending on can_id flag compute extended or standard ids */ + if (can_id & CAN_EFF_FLAG) { + int sid = (can_id & MCP25XXFD_CAN_EFF_SID_MASK) >> + MCP25XXFD_CAN_EFF_SID_SHIFT; + int eid = (can_id & MCP25XXFD_CAN_EFF_EID_MASK) >> + MCP25XXFD_CAN_EFF_EID_SHIFT; + *id = (eid << MCP25XXFD_CAN_OBJ_ID_EID_SHIFT) | + (sid << MCP25XXFD_CAN_OBJ_ID_SID_SHIFT); + *flags = MCP25XXFD_CAN_OBJ_FLAGS_IDE; + } else { + *id = can_id & CAN_SFF_MASK; + *flags = 0; + } + + /* Handle RTR */ + *flags |= (can_id & CAN_RTR_FLAG) ? MCP25XXFD_CAN_OBJ_FLAGS_RTR : 0; +} + +#endif /* __MCP25XXFD_CAN_IF_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c new file mode 100644 index 000000000000..b3cf3e77c299 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c @@ -0,0 +1,674 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#include <linux/can/core.h> +#include <linux/can/dev.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/irqreturn.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/net.h> +#include <linux/netdevice.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/sort.h> + +#include "mcp25xxfd_regs.h" +#include "mcp25xxfd_can.h" +#include "mcp25xxfd_can_int.h" +#include "mcp25xxfd_can_priv.h" +#include "mcp25xxfd_can_rx.h" +#include "mcp25xxfd_can_tx.h" +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_ecc.h" + +#define MCP25XXFD_RESCHEDULE_TIMES 4 + +static void mcp25xxfd_can_int_send_error_skb(struct mcp25xxfd_can_priv *cpriv) +{ + struct net_device *net = cpriv->can.dev; + struct sk_buff *skb; + struct can_frame *frame; + + /* allocate error frame */ + skb = alloc_can_err_skb(net, &frame); + if (!skb) { + netdev_err(net, "cannot allocate error skb\n"); + return; + } + + /* setup can error frame data */ + frame->can_id |= cpriv->error_frame.id; + memcpy(frame->data, cpriv->error_frame.data, sizeof(frame->data)); + + /* and submit it */ + netif_receive_skb(skb); +} + +static int mcp25xxfd_can_int_compare_obj_ts(const void *a, const void *b) +{ + s32 ats = ((struct mcp25xxfd_obj_ts *)a)->ts; + s32 bts = ((struct mcp25xxfd_obj_ts *)b)->ts; + + if (ats < bts) + return -1; + if (ats > bts) + return 1; + + return 0; +} + +static int mcp25xxfd_can_int_submit_frames(struct mcp25xxfd_can_priv *cpriv) +{ + struct mcp25xxfd_obj_ts *queue = cpriv->fifos.submit_queue; + int count = cpriv->fifos.submit_queue_count; + int i, fifo; + int ret; + + /* skip processing if the queue count is 0 */ + if (count == 0) + goto out; + + /* sort the fifos (rx and tx - actually TEF) by receive timestamp */ + sort(queue, count, sizeof(*queue), + mcp25xxfd_can_int_compare_obj_ts, NULL); + + /* now submit the fifos */ + for (i = 0; i < count; i++) { + fifo = queue[i].fifo; + ret = (queue[i].is_rx) ? + mcp25xxfd_can_rx_submit_frame(cpriv, fifo) : + mcp25xxfd_can_tx_submit_frame(cpriv, fifo); + if (ret) + return ret; + } + + /* if we have received or transmitted something and the IVMIE is + * disabled, then enable it. This is mostly to avoid unnecessary + * interrupts when CAN bus is disconnected. + */ + if (!(cpriv->status.intf | MCP25XXFD_CAN_INT_IVMIE)) { + cpriv->status.intf |= MCP25XXFD_CAN_INT_IVMIE; + ret = mcp25xxfd_cmd_write_mask(cpriv->priv->spi, + MCP25XXFD_CAN_INT, + cpriv->status.intf, + MCP25XXFD_CAN_INT_IVMIE); + if (ret) + return ret; + } + +out: + /* enable tx_queue if necessary */ + mcp25xxfd_can_tx_queue_restart(cpriv); + + return 0; +} + +static int mcp25xxfd_can_int_clear_int_flags(struct mcp25xxfd_can_priv *cpriv) +{ + u32 clearable_irq_active = cpriv->status.intf & + MCP25XXFD_CAN_INT_IF_CLEAR_MASK; + u32 clear_irq = cpriv->status.intf & (~MCP25XXFD_CAN_INT_IF_CLEAR_MASK); + + if (!clearable_irq_active) + return 0; + + return mcp25xxfd_cmd_write_mask(cpriv->priv->spi, MCP25XXFD_CAN_INT, + clear_irq, clearable_irq_active); +} + +static +int mcp25xxfd_can_int_handle_serrif_txmab(struct mcp25xxfd_can_priv *cpriv) +{ + int mode = mcp25xxfd_can_targetmode(cpriv); + + cpriv->can.dev->stats.tx_fifo_errors++; + cpriv->can.dev->stats.tx_errors++; + + /* data7 contains custom mcp25xxfd error flags */ + cpriv->error_frame.data[7] |= MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_SERR_TX; + + /* and switch back into the correct mode */ + return mcp25xxfd_can_switch_mode_no_wait(cpriv->priv, + &cpriv->regs.con, mode); +} + +static +int mcp25xxfd_can_int_handle_serrif_rxmab(struct mcp25xxfd_can_priv *cpriv) +{ + cpriv->can.dev->stats.rx_dropped++; + cpriv->can.dev->stats.rx_errors++; + + /* data7 contains custom mcp25xxfd error flags */ + cpriv->error_frame.data[7] |= MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_SERR_RX; + + return 0; +} + +static int mcp25xxfd_can_int_handle_serrif(struct mcp25xxfd_can_priv *cpriv) +{ + if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_SERRIF)) + return 0; + + /* Errors here are: + * * Bus Bandwidth Error: when a RX Message Assembly Buffer + * is still full when the next message has already arrived + * the recived message shall be ignored + * * TX MAB Underflow: when a TX Message is invalid + * due to ECC errors or TXMAB underflow + * in this situatioon the system will transition to + * Restricted or Listen Only mode + */ + + cpriv->error_frame.id |= CAN_ERR_CRTL; + cpriv->error_frame.data[1] |= CAN_ERR_CRTL_UNSPEC; + + /* mode change + invalid message would indicate TX MAB Underflow */ + if ((cpriv->status.intf & MCP25XXFD_CAN_INT_MODIF) && + (cpriv->status.intf & MCP25XXFD_CAN_INT_IVMIF)) { + return mcp25xxfd_can_int_handle_serrif_txmab(cpriv); + } + + /* for RX there is only the RXIF an indicator - surprizingly RX-MAB + * does not change mode or anything + */ + if (cpriv->status.intf & MCP25XXFD_CAN_INT_RXIF) + return mcp25xxfd_can_int_handle_serrif_rxmab(cpriv); + + dev_warn_ratelimited(&cpriv->priv->spi->dev, + "unidentified system interrupt - intf = %08x\n", + cpriv->status.intf); + + return 0; +} + +static int mcp25xxfd_can_int_handle_modif(struct mcp25xxfd_can_priv *cpriv) +{ + struct spi_device *spi = cpriv->priv->spi; + int mode; + int ret; + + /* Note that this irq does not get triggered in all situations + * for example SERRIF will move to RESTICTED or LISTENONLY but MODIF + * will not be raised! + */ + if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_MODIF)) + return 0; + + /* get the current mode */ + ret = mcp25xxfd_can_get_mode(cpriv->priv, &mode); + if (ret) + return ret; + + mode = ret; + + /* switches to the same mode as before are ignored + * - this typically happens if the driver is shortly + * switching to a different mode and then returning to the + * original mode + */ + if (mode == cpriv->mode) + return 0; + + /* if we are restricted, then return to "normal" mode */ + if (mode == MCP25XXFD_CAN_CON_MODE_RESTRICTED) { + cpriv->mode = mode; + mode = mcp25xxfd_can_targetmode(cpriv); + return mcp25xxfd_can_switch_mode_no_wait(cpriv->priv, + &cpriv->regs.con, + mode); + } + + /* the controller itself will transition to sleep, so we ignore it */ + if (mode == MCP25XXFD_CAN_CON_MODE_SLEEP) { + cpriv->mode = mode; + return 0; + } + + dev_warn(&spi->dev, + "Controller unexpectedly switched from mode %u to %u\n", + cpriv->mode, mode); + + /* assign the mode as current */ + cpriv->mode = mode; + + return 0; +} + +static int mcp25xxfd_can_int_handle_eccif(struct mcp25xxfd_can_priv *cpriv) +{ + if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_ECCIF)) + return 0; + + /* and prepare ERROR FRAME */ + cpriv->error_frame.id |= CAN_ERR_CRTL; + cpriv->error_frame.data[1] |= CAN_ERR_CRTL_UNSPEC; + /* data7 contains custom mcp25xxfd error flags */ + cpriv->error_frame.data[7] |= MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_ECC; + + /* delegate to interrupt cleaning */ + return mcp25xxfd_ecc_clear_int(cpriv->priv); +} + +static void mcp25xxfd_can_int_handle_ivmif_tx(struct mcp25xxfd_can_priv *cpriv, + u32 *mask) +{ + /* check if it is really a known tx error */ + if ((cpriv->bus.bdiag[1] & + (MCP25XXFD_CAN_BDIAG1_DBIT1ERR | + MCP25XXFD_CAN_BDIAG1_DBIT0ERR | + MCP25XXFD_CAN_BDIAG1_NACKERR | + MCP25XXFD_CAN_BDIAG1_NBIT1ERR | + MCP25XXFD_CAN_BDIAG1_NBIT0ERR + )) == 0) + return; + + /* mark it as a protocol error */ + cpriv->error_frame.id |= CAN_ERR_PROT; + + /* and update statistics */ + cpriv->can.dev->stats.tx_errors++; + + /* and handle all the known cases */ + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NACKERR) { + /* TX-Frame not acknowledged - connected to CAN-bus? */ + *mask |= MCP25XXFD_CAN_BDIAG1_NACKERR; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_TX; + cpriv->can.dev->stats.tx_aborted_errors++; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NBIT1ERR) { + /* TX-Frame CAN-BUS Level is unexpectedly dominant */ + *mask |= MCP25XXFD_CAN_BDIAG1_NBIT1ERR; + cpriv->can.dev->stats.tx_carrier_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT1; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NBIT0ERR) { + /* TX-Frame CAN-BUS Level is unexpectedly recessive */ + *mask |= MCP25XXFD_CAN_BDIAG1_NBIT0ERR; + cpriv->can.dev->stats.tx_carrier_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT0; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_DBIT1ERR) { + /* TX-Frame CAN-BUS Level is unexpectedly dominant + * during data phase + */ + *mask |= MCP25XXFD_CAN_BDIAG1_DBIT1ERR; + cpriv->can.dev->stats.tx_carrier_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT1; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_DBIT0ERR) { + /* TX-Frame CAN-BUS Level is unexpectedly recessive + * during data phase + */ + *mask |= MCP25XXFD_CAN_BDIAG1_DBIT0ERR; + cpriv->can.dev->stats.tx_carrier_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT0; + } +} + +static void mcp25xxfd_can_int_handle_ivmif_rx(struct mcp25xxfd_can_priv *cpriv, + u32 *mask) +{ + /* check if it is really a known tx error */ + if ((cpriv->bus.bdiag[1] & + (MCP25XXFD_CAN_BDIAG1_DCRCERR | + MCP25XXFD_CAN_BDIAG1_DSTUFERR | + MCP25XXFD_CAN_BDIAG1_DFORMERR | + MCP25XXFD_CAN_BDIAG1_NCRCERR | + MCP25XXFD_CAN_BDIAG1_NSTUFERR | + MCP25XXFD_CAN_BDIAG1_NFORMERR + )) == 0) + return; + + /* mark it as a protocol error */ + cpriv->error_frame.id |= CAN_ERR_PROT; + + /* and update statistics */ + cpriv->can.dev->stats.rx_errors++; + + /* handle the cases */ + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_DCRCERR) { + /* RX-Frame with bad CRC during data phase */ + *mask |= MCP25XXFD_CAN_BDIAG1_DCRCERR; + cpriv->can.dev->stats.rx_crc_errors++; + cpriv->error_frame.data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_DSTUFERR) { + /* RX-Frame with bad stuffing during data phase */ + *mask |= MCP25XXFD_CAN_BDIAG1_DSTUFERR; + cpriv->can.dev->stats.rx_frame_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_STUFF; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_DFORMERR) { + /* RX-Frame with bad format during data phase */ + *mask |= MCP25XXFD_CAN_BDIAG1_DFORMERR; + cpriv->can.dev->stats.rx_frame_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_FORM; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NCRCERR) { + /* RX-Frame with bad CRC during data phase */ + *mask |= MCP25XXFD_CAN_BDIAG1_NCRCERR; + cpriv->can.dev->stats.rx_crc_errors++; + cpriv->error_frame.data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NSTUFERR) { + /* RX-Frame with bad stuffing during data phase */ + *mask |= MCP25XXFD_CAN_BDIAG1_NSTUFERR; + cpriv->can.dev->stats.rx_frame_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_STUFF; + } + + if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NFORMERR) { + /* RX-Frame with bad format during data phase */ + *mask |= MCP25XXFD_CAN_BDIAG1_NFORMERR; + cpriv->can.dev->stats.rx_frame_errors++; + cpriv->error_frame.data[2] |= CAN_ERR_PROT_FORM; + } +} + +static int mcp25xxfd_can_int_handle_ivmif(struct mcp25xxfd_can_priv *cpriv) +{ + struct spi_device *spi = cpriv->priv->spi; + u32 mask, bdiag1; + int ret; + + if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_IVMIF)) + return 0; + + if (cpriv->status.intf & MCP25XXFD_CAN_INT_SERRIF) + return 0; + + ret = mcp25xxfd_cmd_read_regs(spi, MCP25XXFD_CAN_BDIAG0, + cpriv->bus.bdiag, + sizeof(cpriv->bus.bdiag)); + if (ret) + return ret; + + mask = 0; + + /* check rx and tx errors */ + mcp25xxfd_can_int_handle_ivmif_tx(cpriv, &mask); + mcp25xxfd_can_int_handle_ivmif_rx(cpriv, &mask); + + /* clear flags if we have bits masked */ + if (!mask) { + dev_warn_once(&spi->dev, + "found IVMIF situation not supported by driver - bdiag = [0x%08x, 0x%08x]", + cpriv->bus.bdiag[0], cpriv->bus.bdiag[1]); + return -EINVAL; + } + + bdiag1 = cpriv->bus.bdiag[1] & (~mask); + ret = mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_CAN_BDIAG1, bdiag1, mask); + if (ret) + return ret; + + /* clear the interrupt flag until we have received or transmited */ + cpriv->status.intf &= ~(MCP25XXFD_CAN_INT_IVMIE); + return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_CAN_INT, + cpriv->status.intf, + MCP25XXFD_CAN_INT_IVMIE); +} + +static int mcp25xxfd_can_int_handle_cerrif(struct mcp25xxfd_can_priv *cpriv) +{ + if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_CERRIF)) + return 0; + + /* this interrupt exists primarilly to counter possible bus off + * situations. More detailed information can be found and controlled in + * the TREC register + */ + + netdev_warn(cpriv->can.dev, "CAN Bus error experienced"); + + return 0; +} + +static int mcp25xxfd_can_int_error_counters(struct mcp25xxfd_can_priv *cpriv) +{ + if (cpriv->status.trec & MCP25XXFD_CAN_TREC_TXWARN) { + cpriv->bus.new_state = CAN_STATE_ERROR_WARNING; + cpriv->error_frame.id |= CAN_ERR_CRTL; + cpriv->error_frame.data[1] |= CAN_ERR_CRTL_TX_WARNING; + } + + if (cpriv->status.trec & MCP25XXFD_CAN_TREC_RXWARN) { + cpriv->bus.new_state = CAN_STATE_ERROR_WARNING; + cpriv->error_frame.id |= CAN_ERR_CRTL; + cpriv->error_frame.data[1] |= CAN_ERR_CRTL_RX_WARNING; + } + + if (cpriv->status.trec & MCP25XXFD_CAN_TREC_TXBP) { + cpriv->bus.new_state = CAN_STATE_ERROR_PASSIVE; + cpriv->error_frame.id |= CAN_ERR_CRTL; + cpriv->error_frame.data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + } + + if (cpriv->status.trec & MCP25XXFD_CAN_TREC_RXBP) { + cpriv->bus.new_state = CAN_STATE_ERROR_PASSIVE; + cpriv->error_frame.id |= CAN_ERR_CRTL; + cpriv->error_frame.data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + } + + if (cpriv->status.trec & MCP25XXFD_CAN_TREC_TXBO) { + cpriv->bus.new_state = CAN_STATE_BUS_OFF; + cpriv->error_frame.id |= CAN_ERR_BUSOFF; + } + + return 0; +} + +static int mcp25xxfd_can_int_error_handling(struct mcp25xxfd_can_priv *cpriv) +{ + /* based on the last state state check the new state */ + switch (cpriv->can.state) { + case CAN_STATE_ERROR_ACTIVE: + if (cpriv->bus.new_state >= CAN_STATE_ERROR_WARNING && + cpriv->bus.new_state <= CAN_STATE_BUS_OFF) + cpriv->can.can_stats.error_warning++; + fallthrough; + case CAN_STATE_ERROR_WARNING: + if (cpriv->bus.new_state >= CAN_STATE_ERROR_PASSIVE && + cpriv->bus.new_state <= CAN_STATE_BUS_OFF) + cpriv->can.can_stats.error_passive++; + break; + default: + break; + } + + cpriv->can.state = cpriv->bus.new_state; + + /* send error packet */ + if (cpriv->error_frame.id) + mcp25xxfd_can_int_send_error_skb(cpriv); + + /* handle BUS OFF */ + if (cpriv->can.state == CAN_STATE_BUS_OFF) { + if (cpriv->can.restart_ms == 0) { + cpriv->can.can_stats.bus_off++; + can_bus_off(cpriv->can.dev); + } + } else { + /* restart the tx queue if needed */ + mcp25xxfd_can_tx_queue_restart(cpriv); + } + + return 0; +} + +static int mcp25xxfd_can_int_handle_status(struct mcp25xxfd_can_priv *cpriv) +{ + int ret; + + ret = mcp25xxfd_can_int_clear_int_flags(cpriv); + if (ret) + return ret; + + /* set up new state and error frame for this loop */ + cpriv->bus.new_state = cpriv->bus.state; + memset(&cpriv->error_frame, 0, sizeof(cpriv->error_frame)); + + /* setup the process queue by clearing the counter */ + cpriv->fifos.submit_queue_count = 0; + + /* system error interrupt needs to get handled first + * to get us out of restricted mode + */ + ret = mcp25xxfd_can_int_handle_serrif(cpriv); + if (ret) + return ret; + + /* mode change interrupt */ + ret = mcp25xxfd_can_int_handle_modif(cpriv); + if (ret) + return ret; + + /* handle the rx */ + ret = mcp25xxfd_can_rx_handle_int_rxif(cpriv); + if (ret) + return ret; + + /* handle aborted TX FIFOs */ + ret = mcp25xxfd_can_tx_handle_int_txatif(cpriv); + if (ret) + return ret; + + /* handle the TEF */ + ret = mcp25xxfd_can_tx_handle_int_tefif(cpriv); + if (ret) + return ret; + + /* handle error interrupt flags */ + ret = mcp25xxfd_can_rx_handle_int_rxovif(cpriv); + if (ret) + return ret; + + /* sram ECC error interrupt */ + ret = mcp25xxfd_can_int_handle_eccif(cpriv); + if (ret) + return ret; + + /* message format interrupt */ + ret = mcp25xxfd_can_int_handle_ivmif(cpriv); + if (ret) + return ret; + + /* handle bus errors in more detail */ + ret = mcp25xxfd_can_int_handle_cerrif(cpriv); + if (ret) + return ret; + + /* error counter handling */ + ret = mcp25xxfd_can_int_error_counters(cpriv); + if (ret) + return ret; + + /* error counter handling */ + ret = mcp25xxfd_can_int_error_handling(cpriv); + if (ret) + return ret; + + /* and submit can frames to network stack */ + ret = mcp25xxfd_can_int_submit_frames(cpriv); + + return ret; +} + +irqreturn_t mcp25xxfd_can_int(int irq, void *dev_id) +{ + struct mcp25xxfd_can_priv *cpriv = dev_id; + int loops, ret; + + /* loop forever unless we need to exit */ + for (loops = 0; true; loops++) { + /* read interrupt status flags in bulk */ + ret = mcp25xxfd_cmd_read_regs(cpriv->priv->spi, + MCP25XXFD_CAN_INT, + &cpriv->status.intf, + sizeof(cpriv->status)); + if (ret) + return ret; + + /* only act if the IE mask configured has active IF bits + * otherwise the Interrupt line should be deasserted already + * so we can exit the loop + */ + if (((cpriv->status.intf >> MCP25XXFD_CAN_INT_IE_SHIFT) & + cpriv->status.intf) == 0) + break; + + /* handle the status */ + ret = mcp25xxfd_can_int_handle_status(cpriv); + if (ret) + return ret; + + /* allow voluntarily rescheduling every so often to avoid + * long CS lows at the end of a transfer on low power CPUs + * avoiding SERR happening + */ + if (loops % MCP25XXFD_RESCHEDULE_TIMES == 0) + cond_resched(); + } + + return IRQ_HANDLED; +} + +int mcp25xxfd_can_int_clear(struct mcp25xxfd_priv *priv) +{ + return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_CAN_INT, 0, + MCP25XXFD_CAN_INT_IF_MASK); +} + +int mcp25xxfd_can_int_enable(struct mcp25xxfd_priv *priv, bool enable) +{ + struct mcp25xxfd_can_priv *cpriv = priv->cpriv; + const u32 mask = MCP25XXFD_CAN_INT_TEFIE | + MCP25XXFD_CAN_INT_RXIE | + MCP25XXFD_CAN_INT_MODIE | + MCP25XXFD_CAN_INT_SERRIE | + MCP25XXFD_CAN_INT_IVMIE | + MCP25XXFD_CAN_INT_CERRIE | + MCP25XXFD_CAN_INT_RXOVIE | + MCP25XXFD_CAN_INT_ECCIE; + u32 value = cpriv ? cpriv->status.intf : 0; + int ret; + + value &= ~(MCP25XXFD_CAN_INT_IE_MASK); + if (enable) + value |= mask; + + ret = mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_CAN_INT, + value, mask); + if (ret) + return ret; + + if (!cpriv) + return 0; + + cpriv->status.intf = value; + if (cpriv->irq.allocated) { + if (enable && !cpriv->irq.enabled) + enable_irq(cpriv->priv->spi->irq); + if (!enable && cpriv->irq.enabled) + disable_irq(cpriv->priv->spi->irq); + cpriv->irq.enabled = enable; + } else { + cpriv->irq.enabled = false; + } + + return 0; +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.h new file mode 100644 index 000000000000..aa67a5da9271 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ +#ifndef __MCP25XXFD_CAN_INT_H +#define __MCP25XXFD_CAN_INT_H + +#include "mcp25xxfd_priv.h" +#include <linux/irqreturn.h> + +int mcp25xxfd_can_int_clear(struct mcp25xxfd_priv *priv); +int mcp25xxfd_can_int_enable(struct mcp25xxfd_priv *priv, bool enable); + +irqreturn_t mcp25xxfd_can_int(int irq, void *dev_id); + +#endif /* __MCP25XXFD_CAN_INT_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h new file mode 100644 index 000000000000..99c3ef6d08e0 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#ifndef __MCP25XXFD_CAN_PRIV_H +#define __MCP25XXFD_CAN_PRIV_H + +#include <linux/can/dev.h> +#include <linux/dcache.h> + +#include "mcp25xxfd_priv.h" + +#define TX_ECHO_SKB_MAX 32 + +/* information on each fifo type */ +struct mcp25xxfd_fifo { + u32 count; + u32 start; + u32 size; +}; + +/* used for sorting incoming messages */ +struct mcp25xxfd_obj_ts { + s32 ts; /* using signed to handle rollover correctly when sorting */ + u16 fifo; + s16 is_rx; +}; + +/* general info on each fifo */ +struct mcp25xxfd_fifo_info { + u32 is_rx; + u32 offset; + u32 priority; +}; + +struct mcp25xxfd_can_priv { + /* can_priv has to be the first one to be usable with alloc_candev + * which expects struct can_priv to be right at the start of the + * priv structure + */ + struct can_priv can; + struct mcp25xxfd_priv *priv; + struct regulator *transceiver; + + /* the can mode currently active */ + int mode; + + /* interrupt state */ + struct { + int enabled; + int allocated; + } irq; + + /* can config registers */ + struct { + u32 con; + u32 tdc; + u32 tscon; + u32 tefcon; + u32 nbtcfg; + u32 dbtcfg; + } regs; + + /* can status registers (mostly) - read in one go + * bdiag0 and bdiag1 are optional, but when + * berr counters are requested on a regular basis + * during high CAN-bus load this would trigger the fact + * that spi_sync would get queued for execution in the + * spi thread and the spi handler would not get + * called inline in the interrupt thread without any + * context switches or wakeups... + */ + struct { + u32 intf; + /* ASSERT(CAN_INT + 4 == CAN_RXIF) */ + u32 rxif; + /* ASSERT(CAN_RXIF + 4 == CAN_TXIF) */ + u32 txif; + /* ASSERT(CAN_TXIF + 4 == CAN_RXOVIF) */ + u32 rxovif; + /* ASSERT(CAN_RXOVIF + 4 == CAN_TXATIF) */ + u32 txatif; + /* ASSERT(CAN_TXATIF + 4 == CAN_TXREQ) */ + u32 txreq; + /* ASSERT(CAN_TXREQ + 4 == CAN_TREC) */ + u32 trec; + } status; + + /* information of fifo setup */ + struct { + /* define payload size and mode */ + u32 payload_size; + u32 payload_mode; + + /* infos on fifo layout */ + + /* TEF */ + struct { + u32 count; + u32 size; + u32 index; + } tef; + + /* info on each fifo */ + struct mcp25xxfd_fifo_info info[32]; + + /* extra info on rx/tx fifo groups */ + struct mcp25xxfd_fifo tx; + struct mcp25xxfd_fifo rx; + + /* queue of can frames that need to get submitted + * to the network stack during an interrupt loop in one go + * (this gets sorted by timestamp before submission + * and contains both rx frames as well tx frames that have + * gone over the CAN bus successfully + */ + struct mcp25xxfd_obj_ts submit_queue[32]; + int submit_queue_count; + + /* the tx queue of spi messages */ + struct mcp25xxfd_tx_spi_message_queue *tx_queue; + } fifos; + + /* bus state */ + struct { + u32 state; + u32 new_state; + u32 bdiag[2]; + } bus; + + /* can error messages */ + struct { + u32 id; + u8 data[8]; + } error_frame; + + /* a copy of mcp25xxfd-sram in ram */ + u8 sram[MCP25XXFD_SRAM_SIZE]; +}; + +#endif /* __MCP25XXFD_CAN_PRIV_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c new file mode 100644 index 000000000000..da99145e0c94 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + * + * Based on Microchip MCP251x CAN controller driver written by + * David Vrabel, Copyright 2006 Arcom Control Systems Ltd. + */ + +#include <linux/can/core.h> +#include <linux/can/dev.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> + +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_can.h" +#include "mcp25xxfd_can_id.h" +#include "mcp25xxfd_can_priv.h" +#include "mcp25xxfd_can_rx.h" + +static struct sk_buff * +mcp25xxfd_can_rx_submit_normal_frame(struct mcp25xxfd_can_priv *cpriv, + u32 id, u32 dlc, u8 **data) +{ + struct can_frame *frame; + struct sk_buff *skb; + + /* allocate frame */ + skb = alloc_can_skb(cpriv->can.dev, &frame); + if (!skb) + return NULL; + + /* set id, dlc and flags */ + frame->can_id = id; + frame->can_dlc = dlc; + + /* and set the pointer to data */ + *data = frame->data; + + return skb; +} + +/* it is almost identical except for the type of the frame... */ +static struct sk_buff * +mcp25xxfd_can_rx_submit_fd_frame(struct mcp25xxfd_can_priv *cpriv, + u32 id, u32 flags, u32 len, u8 **data) +{ + struct canfd_frame *frame; + struct sk_buff *skb; + + /* allocate frame */ + skb = alloc_canfd_skb(cpriv->can.dev, &frame); + if (!skb) + return NULL; + + /* set id, dlc and flags */ + frame->can_id = id; + frame->len = len; + frame->flags |= flags; + + /* and set the pointer to data */ + *data = frame->data; + + return skb; +} + +int mcp25xxfd_can_rx_submit_frame(struct mcp25xxfd_can_priv *cpriv, int fifo) +{ + struct net_device *net = cpriv->can.dev; + int addr = cpriv->fifos.info[fifo].offset; + struct mcp25xxfd_can_obj_rx *rx = + (struct mcp25xxfd_can_obj_rx *)(cpriv->sram + addr); + u8 *data = NULL; + struct sk_buff *skb; + u32 id, dlc, len, flags; + + /* compute the can_id */ + mcp25xxfd_can_id_from_mcp25xxfd(rx->id, rx->flags, &id); + + /* and dlc */ + dlc = (rx->flags & MCP25XXFD_CAN_OBJ_FLAGS_DLC_MASK) >> + MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT; + len = can_dlc2len(dlc); + + /* update stats */ + net->stats.rx_packets++; + net->stats.rx_bytes += len; + + /* allocate the skb buffer */ + if (rx->flags & MCP25XXFD_CAN_OBJ_FLAGS_FDF) { + flags = 0; + flags |= (rx->flags & MCP25XXFD_CAN_OBJ_FLAGS_BRS) ? + CANFD_BRS : 0; + flags |= (rx->flags & MCP25XXFD_CAN_OBJ_FLAGS_ESI) ? + CANFD_ESI : 0; + skb = mcp25xxfd_can_rx_submit_fd_frame(cpriv, id, flags, + len, &data); + } else { + skb = mcp25xxfd_can_rx_submit_normal_frame(cpriv, id, + len, &data); + } + if (!skb) { + netdev_err(net, "cannot allocate RX skb\n"); + net->stats.rx_dropped++; + return -ENOMEM; + } + + /* copy the payload data */ + memcpy(data, rx->data, len); + + /* and submit the frame */ + netif_rx_ni(skb); + + return 0; +} + +static int mcp25xxfd_can_rx_read_frame(struct mcp25xxfd_can_priv *cpriv, + int fifo, int prefetch_bytes) +{ + struct spi_device *spi = cpriv->priv->spi; + struct net_device *net = cpriv->can.dev; + int addr = cpriv->fifos.info[fifo].offset; + struct mcp25xxfd_can_obj_rx *rx = + (struct mcp25xxfd_can_obj_rx *)(cpriv->sram + addr); + int dlc; + int len, ret; + + /* we read the header plus prefetch_bytes */ + ret = mcp25xxfd_cmd_read_multi(spi, MCP25XXFD_SRAM_ADDR(addr), + rx, sizeof(*rx) + prefetch_bytes); + if (ret) + return ret; + + /* transpose the headers to CPU format*/ + rx->id = le32_to_cpu(rx->id); + rx->flags = le32_to_cpu(rx->flags); + rx->ts = le32_to_cpu(rx->ts); + + /* compute len */ + dlc = (rx->flags & MCP25XXFD_CAN_OBJ_FLAGS_DLC_MASK) >> + MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT; + len = can_dlc2len(min_t(int, dlc, (net->mtu == CANFD_MTU) ? 15 : 8)); + + /* read the remaining data for canfd frames */ + if (len > prefetch_bytes) { + /* here the extra portion reading data after prefetch */ + ret = mcp25xxfd_cmd_read_multi(spi, + MCP25XXFD_SRAM_ADDR(addr) + + sizeof(*rx) + prefetch_bytes, + &rx->data[prefetch_bytes], + len - prefetch_bytes); + if (ret) + return ret; + } + + /* clear the rest of the buffer - just to be safe */ + memset(rx->data + len, 0, ((net->mtu == CANFD_MTU) ? 64 : 8) - len); + + /* add the fifo to the process queues */ + mcp25xxfd_can_queue_frame(cpriv, fifo, rx->ts, true); + + /* and clear the interrupt flag for that fifo */ + return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_CAN_FIFOCON(fifo), + MCP25XXFD_CAN_FIFOCON_FRESET, + MCP25XXFD_CAN_FIFOCON_FRESET); +} + +static int mcp25xxfd_can_rx_read_frames(struct mcp25xxfd_can_priv *cpriv) +{ + int i, f, prefetch; + int ret; + + prefetch = 8; + /* TODO: Optimize this */ + for (i = 0, f = cpriv->fifos.rx.start; i < cpriv->fifos.rx.count; + i++, f++) { + if (cpriv->status.rxif & BIT(f)) { + /* read the frame */ + ret = mcp25xxfd_can_rx_read_frame(cpriv, f, prefetch); + if (ret) + return ret; + } + } + + return 0; +} + +int mcp25xxfd_can_rx_handle_int_rxif(struct mcp25xxfd_can_priv *cpriv) +{ + if (!cpriv->status.rxif) + return 0; + + /* read all the fifos */ + return mcp25xxfd_can_rx_read_frames(cpriv); +} + +int mcp25xxfd_can_rx_handle_int_rxovif(struct mcp25xxfd_can_priv *cpriv) +{ + u32 mask = MCP25XXFD_CAN_FIFOSTA_RXOVIF; + int ret, i, reg; + + if (!cpriv->status.rxovif) + return 0; + + /* clear all fifos that have an overflow bit set */ + for (i = 0; i < 32; i++) { + if (cpriv->status.rxovif & BIT(i)) { + /* clear fifo status */ + reg = MCP25XXFD_CAN_FIFOSTA(i); + ret = mcp25xxfd_cmd_write_mask(cpriv->priv->spi, + reg, 0, mask); + if (ret) + return ret; + + /* update statistics */ + cpriv->can.dev->stats.rx_over_errors++; + cpriv->can.dev->stats.rx_errors++; + + /* and prepare ERROR FRAME */ + cpriv->error_frame.id |= CAN_ERR_CRTL; + cpriv->error_frame.data[1] |= + CAN_ERR_CRTL_RX_OVERFLOW; + } + } + + return 0; +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.h new file mode 100644 index 000000000000..71953e2f3615 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#ifndef __MCP25XXFD_CAN_RX_H +#define __MCP25XXFD_CAN_RX_H + +#include "mcp25xxfd_priv.h" + +int mcp25xxfd_can_rx_submit_frame(struct mcp25xxfd_can_priv *cpriv, int fifo); + +int mcp25xxfd_can_rx_handle_int_rxif(struct mcp25xxfd_can_priv *cpriv); +int mcp25xxfd_can_rx_handle_int_rxovif(struct mcp25xxfd_can_priv *cpriv); + +#endif /* __MCP25XXFD_CAN_RX_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c new file mode 100644 index 000000000000..6f066cb95844 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c @@ -0,0 +1,653 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + * + * Based on Microchip MCP251x CAN controller driver written by + * David Vrabel, Copyright 2006 Arcom Control Systems Ltd. + */ + +#include <linux/bitfield.h> +#include <linux/can/core.h> +#include <linux/can/dev.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/netdevice.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> + +#include "mcp25xxfd_can.h" +#include "mcp25xxfd_can_id.h" +#include "mcp25xxfd_can_tx.h" +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_regs.h" + +static struct mcp25xxfd_tx_spi_message * +mcp25xxfd_can_tx_queue_first_spi_message(struct mcp25xxfd_tx_spi_message_queue * + queue, u32 *bitmap) +{ + u32 first = ffs(*bitmap); + + if (!first) + return NULL; + + return queue->fifo2message[first - 1]; +} + +static void mcp25xxfd_can_tx_queue_remove_spi_message(u32 *bitmap, int fifo) +{ + *bitmap &= ~BIT(fifo); +} + +static void mcp25xxfd_can_tx_queue_add_spi_message(u32 *bitmap, int fifo) +{ + *bitmap |= BIT(fifo); +} + +static void mcp25xxfd_can_tx_queue_move_spi_message(u32 *src, u32 *dest, + int fifo) +{ + mcp25xxfd_can_tx_queue_remove_spi_message(src, fifo); + mcp25xxfd_can_tx_queue_add_spi_message(dest, fifo); +} + +static void mcp25xxfd_can_tx_spi_message_fill_fifo_complete(void *context) +{ + struct mcp25xxfd_tx_spi_message *msg = context; + struct mcp25xxfd_can_priv *cpriv = msg->cpriv; + struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue; + unsigned long flags; + + /* reset transfer length to without data (DLC = 0) */ + msg->fill_fifo.xfer.len = sizeof(msg->fill_fifo.data.cmd) + + sizeof(msg->fill_fifo.data.header); + + spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags); + + /* move to in_trigger_fifo_transfer */ + mcp25xxfd_can_tx_queue_move_spi_message(&q->in_fill_fifo_transfer, + &q->in_trigger_fifo_transfer, + msg->fifo); + + spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags); +} + +static void mcp25xxfd_can_tx_spi_message_trigger_fifo_complete(void *context) +{ + struct mcp25xxfd_tx_spi_message *msg = context; + struct mcp25xxfd_can_priv *cpriv = msg->cpriv; + struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue; + unsigned long flags; + + spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags); + + /* move to can_transfer */ + mcp25xxfd_can_tx_queue_move_spi_message(&q->in_trigger_fifo_transfer, + &q->in_can_transfer, + msg->fifo); + + spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags); +} + +static +void mcp25xxfd_can_tx_message_init(struct mcp25xxfd_can_priv *cpriv, + struct mcp25xxfd_tx_spi_message *msg, + int fifo) +{ + const u32 trigger = MCP25XXFD_CAN_FIFOCON_TXREQ | + MCP25XXFD_CAN_FIFOCON_UINC; + const int first_byte = mcp25xxfd_cmd_first_byte(trigger); + u32 addr; + + msg->cpriv = cpriv; + msg->fifo = fifo; + + /* init fill_fifo */ + spi_message_init(&msg->fill_fifo.msg); + msg->fill_fifo.msg.complete = + mcp25xxfd_can_tx_spi_message_fill_fifo_complete; + msg->fill_fifo.msg.context = msg; + + msg->fill_fifo.xfer.tx_buf = msg->fill_fifo.data.cmd; + msg->fill_fifo.xfer.len = sizeof(msg->fill_fifo.data.cmd) + + sizeof(msg->fill_fifo.data.header); + spi_message_add_tail(&msg->fill_fifo.xfer, &msg->fill_fifo.msg); + + addr = MCP25XXFD_SRAM_ADDR(cpriv->fifos.info[fifo].offset); + mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_WRITE, addr, + msg->fill_fifo.data.cmd); + + /* init trigger_fifo */ + spi_message_init(&msg->trigger_fifo.msg); + msg->trigger_fifo.msg.complete = + mcp25xxfd_can_tx_spi_message_trigger_fifo_complete; + msg->trigger_fifo.msg.context = msg; + + msg->trigger_fifo.xfer.tx_buf = msg->trigger_fifo.data.cmd; + msg->trigger_fifo.xfer.len = sizeof(msg->trigger_fifo.data.cmd) + + sizeof(msg->trigger_fifo.data.data); + spi_message_add_tail(&msg->trigger_fifo.xfer, &msg->trigger_fifo.msg); + + mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_WRITE, + MCP25XXFD_CAN_FIFOCON(fifo) + first_byte, + msg->trigger_fifo.data.cmd); + msg->trigger_fifo.data.data = trigger >> (8 * first_byte); + + /* add to idle tx transfers */ + mcp25xxfd_can_tx_queue_add_spi_message(&cpriv->fifos.tx_queue->idle, + fifo); +} + +static +void mcp25xxfd_can_tx_queue_manage_nolock(struct mcp25xxfd_can_priv *cpriv, + int state) +{ + struct net_device *net = cpriv->can.dev; + + if (state == cpriv->fifos.tx_queue->state) + return; + + /* start/stop netif_queue if necessary */ + switch (cpriv->fifos.tx_queue->state) { + case MCP25XXFD_CAN_TX_QUEUE_STATE_RUNABLE: + switch (state) { + case MCP25XXFD_CAN_TX_QUEUE_STATE_RESTART: + case MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED: + netif_wake_queue(net); + cpriv->fifos.tx_queue->state = + MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED; + break; + } + break; + case MCP25XXFD_CAN_TX_QUEUE_STATE_STOPPED: + switch (state) { + case MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED: + netif_wake_queue(net); + cpriv->fifos.tx_queue->state = state; + break; + } + break; + case MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED: + switch (state) { + case MCP25XXFD_CAN_TX_QUEUE_STATE_RUNABLE: + case MCP25XXFD_CAN_TX_QUEUE_STATE_STOPPED: + netif_stop_queue(net); + cpriv->fifos.tx_queue->state = state; + break; + } + break; + default: + netdev_err(cpriv->can.dev, "Unsupported tx_queue state: %i\n", + cpriv->fifos.tx_queue->state); + break; + } +} + +void mcp25xxfd_can_tx_queue_manage(struct mcp25xxfd_can_priv *cpriv, int state) +{ + unsigned long flags; + + spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags); + + mcp25xxfd_can_tx_queue_manage_nolock(cpriv, state); + + spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags); +} + +void mcp25xxfd_can_tx_queue_restart(struct mcp25xxfd_can_priv *cpriv) +{ + u32 state = MCP25XXFD_CAN_TX_QUEUE_STATE_RESTART; + unsigned long flags; + u32 mask; + + spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags); + + /* only move if there is nothing pending or idle */ + mask = cpriv->fifos.tx_queue->idle | + cpriv->fifos.tx_queue->in_fill_fifo_transfer | + cpriv->fifos.tx_queue->in_trigger_fifo_transfer | + cpriv->fifos.tx_queue->in_can_transfer; + if (mask) + goto out; + + /* move all items from transferred to idle */ + cpriv->fifos.tx_queue->idle |= cpriv->fifos.tx_queue->transferred; + cpriv->fifos.tx_queue->transferred = 0; + + /* and enable queue */ + mcp25xxfd_can_tx_queue_manage_nolock(cpriv, state); +out: + spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags); +} + +static +int mcp25xxfd_can_tx_handle_int_tefif_fifo(struct mcp25xxfd_can_priv *cpriv) +{ + u32 tef_offset = cpriv->fifos.tef.index * cpriv->fifos.tef.size; + struct mcp25xxfd_can_obj_tef *tef = + (struct mcp25xxfd_can_obj_tef *)(cpriv->sram + tef_offset); + int fifo, ret; + unsigned long flags; + + /* read the next TEF entry to get the transmit timestamp and fifo */ + ret = mcp25xxfd_cmd_read_regs(cpriv->priv->spi, + MCP25XXFD_SRAM_ADDR(tef_offset), + &tef->id, sizeof(*tef)); + if (ret) + return ret; + + /* get the fifo from tef */ + fifo = FIELD_GET(MCP25XXFD_CAN_OBJ_FLAGS_SEQ_MASK, tef->flags); + + /* check that the fifo is valid */ + spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags); + if ((cpriv->fifos.tx_queue->in_can_transfer & BIT(fifo)) == 0) + netdev_err(cpriv->can.dev, + "tefif: fifo %i not pending - tef data: id: %08x flags: %08x, ts: %08x - this may be a problem with spi signal quality- try reducing spi-clock speed if this can get reproduced", + fifo, tef->id, tef->flags, tef->ts); + spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags); + + /* now we can schedule the fifo for echo submission */ + mcp25xxfd_can_queue_frame(cpriv, fifo, tef->ts, false); + + /* increment the tef index with wraparound */ + cpriv->fifos.tef.index++; + if (cpriv->fifos.tef.index >= cpriv->fifos.tef.count) + cpriv->fifos.tef.index = 0; + + /* finally just increment the TEF pointer */ + return mcp25xxfd_cmd_write_mask(cpriv->priv->spi, MCP25XXFD_CAN_TEFCON, + MCP25XXFD_CAN_TEFCON_UINC, + MCP25XXFD_CAN_TEFCON_UINC); +} + +/* reading TEF entries can be made even more efficient by reading + * multiple TEF entries in one go. + * Under the assumption that we have count(TEF) >= count(TX_FIFO) + * we can even release TEFs early (before we read them) + * (and potentially restarting the transmit-queue early aswell) + */ + +static int +mcp25xxfd_can_tx_handle_int_tefif_conservative(struct mcp25xxfd_can_priv *cpriv) +{ + u32 tefsta; + int ret; + + /* read the TEF status */ + ret = mcp25xxfd_cmd_read_mask(cpriv->priv->spi, MCP25XXFD_CAN_TEFSTA, + &tefsta, MCP25XXFD_CAN_TEFSTA_TEFNEIF); + if (ret) + return ret; + + /* read the tef in an inefficient loop */ + while (tefsta & MCP25XXFD_CAN_TEFSTA_TEFNEIF) { + /* read one tef */ + ret = mcp25xxfd_can_tx_handle_int_tefif_fifo(cpriv); + if (ret) + return ret; + + /* read the TEF status */ + ret = mcp25xxfd_cmd_read_mask(cpriv->priv->spi, + MCP25XXFD_CAN_TEFSTA, &tefsta, + MCP25XXFD_CAN_TEFSTA_TEFNEIF); + if (ret) + return ret; + } + + return 0; +} + +static int +mcp25xxfd_can_tx_handle_int_tefif_optimized(struct mcp25xxfd_can_priv *cpriv, + u32 finished) +{ + int i, fifo, ret; + + /* now iterate those */ + for (i = 0, fifo = cpriv->fifos.tx.start; i < cpriv->fifos.tx.count; + i++, fifo++) { + if (finished & BIT(fifo)) { + ret = mcp25xxfd_can_tx_handle_int_tefif_fifo(cpriv); + if (ret) + return ret; + } + } + + return 0; +} + +int mcp25xxfd_can_tx_handle_int_tefif(struct mcp25xxfd_can_priv *cpriv) +{ + unsigned long flags; + u32 finished; + + if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_TEFIF)) + return 0; + + spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags); + + /* compute finished fifos and clear them immediately */ + finished = (cpriv->fifos.tx_queue->in_can_transfer ^ + cpriv->status.txreq) & + cpriv->fifos.tx_queue->in_can_transfer; + + spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags); + + /* run in optimized mode if possible */ + if (finished) + return mcp25xxfd_can_tx_handle_int_tefif_optimized(cpriv, + finished); + /* otherwise play it safe */ + netdev_warn(cpriv->can.dev, + "Something is wrong - we got a TEF interrupt but we were not able to detect a finished fifo\n"); + return mcp25xxfd_can_tx_handle_int_tefif_conservative(cpriv); +} + +static +void mcp25xxfd_can_tx_fill_fifo_common(struct mcp25xxfd_can_priv *cpriv, + struct mcp25xxfd_tx_spi_message *smsg, + struct mcp25xxfd_can_obj_tx *tx, + int dlc, u8 *data) +{ + int len = can_dlc2len(dlc); + + /* add fifo number as seq */ + tx->flags |= smsg->fifo << MCP25XXFD_CAN_OBJ_FLAGS_SEQ_SHIFT; + + /* copy data to tx->data for future reference */ + memcpy(tx->data, data, len); + + /* transform header to controller format */ + mcp25xxfd_cmd_convert_from_cpu(&tx->id, sizeof(*tx) / sizeof(u32)); + + /* copy header + data to final location - we are not aligned */ + memcpy(smsg->fill_fifo.data.header, &tx->id, sizeof(*tx) + len); + + /* transfers to sram should be a multiple of 4 and be zero padded */ + for (; len & 3; len++) + *(smsg->fill_fifo.data.header + sizeof(*tx) + len) = 0; + + /* convert it back to CPU format */ + mcp25xxfd_cmd_convert_to_cpu(&tx->id, sizeof(*tx) / sizeof(u32)); + + /* set up size of transfer */ + smsg->fill_fifo.xfer.len = sizeof(smsg->fill_fifo.data.cmd) + + sizeof(smsg->fill_fifo.data.header) + len; +} + +static +void mcp25xxfd_can_tx_fill_fifo_fd(struct mcp25xxfd_can_priv *cpriv, + struct canfd_frame *frame, + struct mcp25xxfd_tx_spi_message *smsg, + struct mcp25xxfd_can_obj_tx *tx) +{ + int dlc = can_len2dlc(frame->len); + + /* compute can id */ + mcp25xxfd_can_id_to_mcp25xxfd(frame->can_id, &tx->id, &tx->flags); + + /* setup flags */ + tx->flags |= dlc << MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT; + tx->flags |= (frame->can_id & CAN_EFF_FLAG) ? + MCP25XXFD_CAN_OBJ_FLAGS_IDE : 0; + tx->flags |= (frame->can_id & CAN_RTR_FLAG) ? + MCP25XXFD_CAN_OBJ_FLAGS_RTR : 0; + if (frame->flags & CANFD_BRS) + tx->flags |= MCP25XXFD_CAN_OBJ_FLAGS_BRS; + + tx->flags |= (frame->flags & CANFD_ESI) ? + MCP25XXFD_CAN_OBJ_FLAGS_ESI : 0; + tx->flags |= MCP25XXFD_CAN_OBJ_FLAGS_FDF; + + /* and do common processing */ + mcp25xxfd_can_tx_fill_fifo_common(cpriv, smsg, tx, dlc, frame->data); +} + +static +void mcp25xxfd_can_tx_fill_fifo(struct mcp25xxfd_can_priv *cpriv, + struct can_frame *frame, + struct mcp25xxfd_tx_spi_message *smsg, + struct mcp25xxfd_can_obj_tx *tx) +{ + /* set frame to valid dlc */ + if (frame->can_dlc > 8) + frame->can_dlc = 8; + + /* compute can id */ + mcp25xxfd_can_id_to_mcp25xxfd(frame->can_id, &tx->id, &tx->flags); + + /* setup flags */ + tx->flags |= frame->can_dlc << MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT; + tx->flags |= (frame->can_id & CAN_EFF_FLAG) ? + MCP25XXFD_CAN_OBJ_FLAGS_IDE : 0; + tx->flags |= (frame->can_id & CAN_RTR_FLAG) ? + MCP25XXFD_CAN_OBJ_FLAGS_RTR : 0; + + /* and do common processing */ + mcp25xxfd_can_tx_fill_fifo_common(cpriv, smsg, tx, frame->can_dlc, + frame->data); +} + +static struct mcp25xxfd_tx_spi_message * +mcp25xxfd_can_tx_queue_get_next_fifo(struct mcp25xxfd_can_priv *cpriv) +{ + u32 state = MCP25XXFD_CAN_TX_QUEUE_STATE_RUNABLE; + struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue; + struct mcp25xxfd_tx_spi_message *smsg; + unsigned long flags; + + spin_lock_irqsave(&q->lock, flags); + + /* get the first entry from idle */ + smsg = mcp25xxfd_can_tx_queue_first_spi_message(q, &q->idle); + if (!smsg) + goto out_busy; + + /* and move the fifo to next stage */ + mcp25xxfd_can_tx_queue_move_spi_message(&q->idle, + &q->in_fill_fifo_transfer, + smsg->fifo); + + /* if queue is empty then stop the network queue immediately */ + if (!q->idle) + mcp25xxfd_can_tx_queue_manage_nolock(cpriv, state); +out_busy: + spin_unlock_irqrestore(&q->lock, flags); + + return smsg; +} + +/* submit the can message to the can-bus */ +netdev_tx_t mcp25xxfd_can_tx_start_xmit(struct sk_buff *skb, + struct net_device *net) +{ + u32 state = MCP25XXFD_CAN_TX_QUEUE_STATE_STOPPED; + struct mcp25xxfd_can_priv *cpriv = netdev_priv(net); + struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue; + struct mcp25xxfd_priv *priv = cpriv->priv; + struct spi_device *spi = priv->spi; + struct mcp25xxfd_tx_spi_message *smsg; + struct mcp25xxfd_can_obj_tx *tx; + unsigned long flags; + int ret; + + /* invalid skb we can ignore */ + if (can_dropped_invalid_skb(net, skb)) + return NETDEV_TX_OK; + + spin_lock_irqsave(&q->spi_lock, flags); + + /* get the fifo message structure to process now */ + smsg = mcp25xxfd_can_tx_queue_get_next_fifo(cpriv); + if (!smsg) + goto out_busy; + + /* compute the fifo in sram */ + tx = (struct mcp25xxfd_can_obj_tx *) + (cpriv->sram + cpriv->fifos.info[smsg->fifo].offset); + + /* fill in message from skb->data depending on can2.0 or canfd */ + if (can_is_canfd_skb(skb)) + mcp25xxfd_can_tx_fill_fifo_fd(cpriv, + (struct canfd_frame *)skb->data, + smsg, tx); + else + mcp25xxfd_can_tx_fill_fifo(cpriv, + (struct can_frame *)skb->data, + smsg, tx); + + /* submit the two messages asyncronously + * the reason why we separate transfers into two spi_messages is: + * * because the spi framework (currently) does add a 10us delay + * between 2 spi_transfers in a single spi_message when + * change_cs is set - 2 consecutive spi messages show a shorter + * cs disable phase increasing bus utilization + * (code reduction with a fix in spi core would be aprox.50 lines) + * * this allows the interrupt handler to start spi messages earlier + * so reducing latencies a bit and to allow for better concurrency + * * this separation - in the future - may get used to fill fifos + * early and reduce the delay on "rollover" + */ + ret = spi_async(spi, &smsg->fill_fifo.msg); + if (ret) + goto out_async_failed; + + ret = spi_async(spi, &smsg->trigger_fifo.msg); + if (ret) + goto out_async_failed; + + spin_unlock_irqrestore(&q->spi_lock, flags); + + can_put_echo_skb(skb, net, smsg->fifo); + + return NETDEV_TX_OK; + +out_async_failed: + netdev_err(net, "spi_async submission of fifo %i failed - %i\n", + smsg->fifo, ret); + +out_busy: + mcp25xxfd_can_tx_queue_manage_nolock(cpriv, state); + spin_unlock_irqrestore(&q->spi_lock, flags); + + return NETDEV_TX_BUSY; +} + +/* submit the fifo back to the network stack */ +int mcp25xxfd_can_tx_submit_frame(struct mcp25xxfd_can_priv *cpriv, int fifo) +{ + struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue; + struct mcp25xxfd_can_obj_tx *tx = (struct mcp25xxfd_can_obj_tx *) + (cpriv->sram + cpriv->fifos.info[fifo].offset); + int dlc = (tx->flags & MCP25XXFD_CAN_OBJ_FLAGS_DLC_MASK) >> + MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT; + unsigned long flags; + + /* update counters */ + cpriv->can.dev->stats.tx_packets++; + cpriv->can.dev->stats.tx_bytes += can_dlc2len(dlc); + + spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags); + + /* release the echo buffer */ + can_get_echo_skb(cpriv->can.dev, fifo); + + /* move from in_can_transfer to transferred */ + mcp25xxfd_can_tx_queue_move_spi_message(&q->in_can_transfer, + &q->transferred, fifo); + + spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags); + + return 0; +} + +static int +mcp25xxfd_can_tx_handle_int_txatif_fifo(struct mcp25xxfd_can_priv *cpriv, + int fifo) +{ + struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue; + unsigned long flags; + u32 val; + int ret; + + ret = mcp25xxfd_cmd_read(cpriv->priv->spi, + MCP25XXFD_CAN_FIFOSTA(fifo), &val); + if (ret) + return ret; + + ret = mcp25xxfd_cmd_write_mask(cpriv->priv->spi, + MCP25XXFD_CAN_FIFOSTA(fifo), 0, + MCP25XXFD_CAN_FIFOSTA_TXABT | + MCP25XXFD_CAN_FIFOSTA_TXLARB | + MCP25XXFD_CAN_FIFOSTA_TXERR | + MCP25XXFD_CAN_FIFOSTA_TXATIF); + if (ret) + return ret; + + spin_lock_irqsave(&q->lock, flags); + + can_get_echo_skb(cpriv->can.dev, fifo); + mcp25xxfd_can_tx_queue_move_spi_message(&q->in_can_transfer, + &q->transferred, fifo); + + spin_unlock_irqrestore(&q->lock, flags); + + cpriv->status.txif &= ~BIT(fifo); + cpriv->can.dev->stats.tx_aborted_errors++; + + return 0; +} + +int mcp25xxfd_can_tx_handle_int_txatif(struct mcp25xxfd_can_priv *cpriv) +{ + int i, f, ret; + + if (!cpriv->status.txatif) + return 0; + + /* process all the fifos with txatif flag set */ + for (i = 0, f = cpriv->fifos.tx.start; i < cpriv->fifos.tx.count; + i++, f++) { + if (cpriv->status.txatif & BIT(f)) { + ret = mcp25xxfd_can_tx_handle_int_txatif_fifo(cpriv, f); + if (ret) + return ret; + } + } + + return 0; +} + +int mcp25xxfd_can_tx_queue_alloc(struct mcp25xxfd_can_priv *cpriv) +{ + struct mcp25xxfd_tx_spi_message *msg; + size_t size = sizeof(struct mcp25xxfd_tx_spi_message_queue) + + cpriv->fifos.tx.count * sizeof(*msg); + int i, f; + + cpriv->fifos.tx_queue = kzalloc(size, GFP_KERNEL); + if (!cpriv->fifos.tx_queue) + return -ENOMEM; + + spin_lock_init(&cpriv->fifos.tx_queue->lock); + spin_lock_init(&cpriv->fifos.tx_queue->spi_lock); + + /* initialize the individual spi_message structures */ + for (i = 0, f = cpriv->fifos.tx.start; i < cpriv->fifos.tx.count; + i++, f++) { + msg = &cpriv->fifos.tx_queue->message[i]; + cpriv->fifos.tx_queue->fifo2message[f] = msg; + mcp25xxfd_can_tx_message_init(cpriv, msg, f); + } + + return 0; +} + +void mcp25xxfd_can_tx_queue_free(struct mcp25xxfd_can_priv *cpriv) +{ + kfree(cpriv->fifos.tx_queue); + cpriv->fifos.tx_queue = NULL; +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.h new file mode 100644 index 000000000000..1947b3420d58 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#ifndef __MCP25XXFD_CAN_TX_H +#define __MCP25XXFD_CAN_TX_H + +#include <linux/spinlock.h> +#include <linux/spi/spi.h> + +#include "mcp25xxfd_can_priv.h" + +/* structure of a spi message that is prepared and can get submitted quickly */ +struct mcp25xxfd_tx_spi_message { + /* the network device this is related to */ + struct mcp25xxfd_can_priv *cpriv; + /* the fifo this fills */ + u32 fifo; + /* the xfer to fill in the fifo data */ + struct { + struct spi_message msg; + struct spi_transfer xfer; + struct { + u8 cmd[2]; + u8 header[sizeof(struct mcp25xxfd_can_obj_tx)]; + u8 data[64]; + } data; + } fill_fifo; + /* the xfer to enable transmission on the can bus */ + struct { + struct spi_message msg; + struct spi_transfer xfer; + struct { + u8 cmd[2]; + u8 data; + } data; + } trigger_fifo; +}; + +struct mcp25xxfd_tx_spi_message_queue { + /* spinlock protecting the bitmaps + * as well as state and the skb_echo_* functions + */ + spinlock_t lock; + /* bitmap of which fifo is in which stage */ + u32 idle; + u32 in_fill_fifo_transfer; + u32 in_trigger_fifo_transfer; + u32 in_can_transfer; + u32 transferred; + + /* the queue state as seen per controller */ + int state; +#define MCP25XXFD_CAN_TX_QUEUE_STATE_STOPPED 0 +#define MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED 1 +#define MCP25XXFD_CAN_TX_QUEUE_STATE_RUNABLE 2 +#define MCP25XXFD_CAN_TX_QUEUE_STATE_RESTART 3 + + /* spinlock protecting spi submission order */ + spinlock_t spi_lock; + + /* map each fifo to a mcp25xxfd_tx_spi_message */ + struct mcp25xxfd_tx_spi_message *fifo2message[32]; + + /* the individual messages */ + struct mcp25xxfd_tx_spi_message message[]; +}; + +int mcp25xxfd_can_tx_submit_frame(struct mcp25xxfd_can_priv *cpriv, int fifo); +void mcp25xxfd_can_tx_queue_restart(struct mcp25xxfd_can_priv *cpriv); + +int mcp25xxfd_can_tx_handle_int_txatif(struct mcp25xxfd_can_priv *cpriv); +int mcp25xxfd_can_tx_handle_int_tefif(struct mcp25xxfd_can_priv *cpriv); + +netdev_tx_t mcp25xxfd_can_tx_start_xmit(struct sk_buff *skb, + struct net_device *net); + +void mcp25xxfd_can_tx_queue_manage(struct mcp25xxfd_can_priv *cpriv, int state); + +int mcp25xxfd_can_tx_queue_alloc(struct mcp25xxfd_can_priv *cpriv); +void mcp25xxfd_can_tx_queue_free(struct mcp25xxfd_can_priv *cpriv); + +#endif /* __MCP25XXFD_CAN_TX_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.c new file mode 100644 index 000000000000..24d01e6f59d4 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.c @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#include <linux/slab.h> +#include <linux/spi/spi.h> + +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_priv.h" + +static int mcp25xxfd_cmd_sync_write(struct spi_device *spi, + const void *tx_buf, + unsigned int tx_len) +{ + struct spi_transfer xfer; + + memset(&xfer, 0, sizeof(xfer)); + xfer.tx_buf = tx_buf; + xfer.len = tx_len; + + return spi_sync_transfer(spi, &xfer, 1); +} + +static int mcp25xxfd_cmd_write_then_read(struct spi_device *spi, + const void *tx_buf, + unsigned int tx_len, + void *rx_buf, + unsigned int rx_len) +{ + struct spi_transfer xfer[2]; + u8 *spi_tx, *spi_rx; + int xfers; + int ret; + + spi_tx = kzalloc(tx_len + rx_len, GFP_KERNEL); + if (!spi_tx) + return -ENOMEM; + + spi_rx = spi_tx + tx_len; + memset(xfer, 0, sizeof(xfer)); + + /* Special handling for half-duplex */ + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + xfers = 2; + xfer[0].tx_buf = spi_tx; + xfer[0].len = tx_len; + /* Offset for rx_buf needs to get aligned */ + xfer[1].rx_buf = spi_rx + tx_len; + xfer[1].len = rx_len; + } else { + xfers = 1; + xfer[0].len = tx_len + rx_len; + xfer[0].tx_buf = spi_tx; + xfer[0].rx_buf = spi_rx; + } + + memcpy(spi_tx, tx_buf, tx_len); + ret = spi_sync_transfer(spi, xfer, xfers); + if (ret) + goto out; + + memcpy(rx_buf, xfer[0].rx_buf + tx_len, rx_len); + +out: + kfree(spi_tx); + + return ret; +} + +static int mcp25xxfd_cmd_write_then_write(struct spi_device *spi, + const void *tx_buf, + unsigned int tx_len, + const void *tx2_buf, + unsigned int tx2_len) +{ + struct spi_transfer xfer; + u8 *spi_tx; + int ret; + + spi_tx = kzalloc(tx_len + tx2_len, GFP_KERNEL); + if (!spi_tx) + return -ENOMEM; + + memset(&xfer, 0, sizeof(xfer)); + xfer.len = tx_len + tx2_len; + xfer.tx_buf = spi_tx; + + memcpy(spi_tx, tx_buf, tx_len); + memcpy(spi_tx + tx_len, tx2_buf, tx2_len); + + ret = spi_sync_transfer(spi, &xfer, 1); + kfree(spi_tx); + + return ret; +} + +/* Read multiple bytes from registers */ +int mcp25xxfd_cmd_read_multi(struct spi_device *spi, u32 reg, + void *data, int n) +{ + u8 cmd[2]; + int ret; + + mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_READ, reg, cmd); + + ret = mcp25xxfd_cmd_write_then_read(spi, &cmd, 2, data, n); + if (ret) + return ret; + + return 0; +} + +int mcp25xxfd_cmd_read_mask(struct spi_device *spi, u32 reg, + u32 *data, u32 mask) +{ + int first_byte, last_byte, len_byte; + int ret; + + /* Make sure at least one bit is set */ + if (!mask) + return -EINVAL; + + /* Calculate first and last byte used */ + first_byte = mcp25xxfd_cmd_first_byte(mask); + last_byte = mcp25xxfd_cmd_last_byte(mask); + len_byte = last_byte - first_byte + 1; + + *data = 0; + ret = mcp25xxfd_cmd_read_multi(spi, reg + first_byte, + ((void *)data + first_byte), len_byte); + if (ret) + return ret; + + mcp25xxfd_cmd_convert_to_cpu(data, 1); + + return 0; +} + +/* Write multiple bytes to registers */ +int mcp25xxfd_cmd_write_multi(struct spi_device *spi, u32 reg, + void *data, int n) +{ + int ret; + u8 cmd[2]; + + mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_WRITE, reg, cmd); + + ret = mcp25xxfd_cmd_write_then_write(spi, &cmd, 2, data, n); + if (ret) + return ret; + + return 0; +} + +int mcp25xxfd_cmd_write_mask(struct spi_device *spi, u32 reg, + u32 data, u32 mask) +{ + int first_byte, last_byte, len_byte; + u8 cmd[2]; + + /* Make sure at least one bit is set */ + if (!mask) + return -EINVAL; + + /* calculate first and last byte used */ + first_byte = mcp25xxfd_cmd_first_byte(mask); + last_byte = mcp25xxfd_cmd_last_byte(mask); + len_byte = last_byte - first_byte + 1; + + mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_WRITE, + reg + first_byte, cmd); + + mcp25xxfd_cmd_convert_from_cpu(&data, 1); + + return mcp25xxfd_cmd_write_then_write(spi, + cmd, sizeof(cmd), + ((void *)&data + first_byte), + len_byte); +} + +int mcp25xxfd_cmd_write_regs(struct spi_device *spi, u32 reg, + u32 *data, u32 bytes) +{ + int ret; + + mcp25xxfd_cmd_convert_from_cpu(data, bytes / sizeof(bytes)); + + ret = mcp25xxfd_cmd_write_multi(spi, reg, data, bytes); + + mcp25xxfd_cmd_convert_to_cpu(data, bytes / sizeof(bytes)); + + return ret; +} + +int mcp25xxfd_cmd_read_regs(struct spi_device *spi, u32 reg, + u32 *data, u32 bytes) +{ + int ret; + + ret = mcp25xxfd_cmd_read_multi(spi, reg, data, bytes); + + mcp25xxfd_cmd_convert_to_cpu((u32 *)data, bytes / sizeof(bytes)); + + return ret; +} + +int mcp25xxfd_cmd_reset(struct spi_device *spi) +{ + u8 *cmd; + int ret; + + cmd = kzalloc(2, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_RESET, 0, cmd); + + ret = mcp25xxfd_cmd_sync_write(spi, cmd, 2); + + kfree(cmd); + + return ret; +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.h new file mode 100644 index 000000000000..a61815f97bf6 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#ifndef __MCP25XXFD_CMD_H +#define __MCP25XXFD_CMD_H + +#include <linux/byteorder/generic.h> +#include <linux/spi/spi.h> + +/* SPI commands */ +#define MCP25XXFD_INSTRUCTION_RESET 0x0000 +#define MCP25XXFD_INSTRUCTION_READ 0x3000 +#define MCP25XXFD_INSTRUCTION_WRITE 0x2000 +#define MCP25XXFD_INSTRUCTION_READ_CRC 0xB000 +#define MCP25XXFD_INSTRUCTION_WRITE_CRC 0xA000 +#define MCP25XXFD_INSTRUCTION_WRITE_SAVE 0xC000 + +#define MCP25XXFD_ADDRESS_MASK 0x0fff + +static inline void mcp25xxfd_cmd_convert_to_cpu(u32 *data, int n) +{ + le32_to_cpu_array(data, n); +} + +static inline void mcp25xxfd_cmd_convert_from_cpu(u32 *data, int n) +{ + cpu_to_le32_array(data, n); +} + +static inline void mcp25xxfd_cmd_calc(u16 cmd, u16 addr, u8 *data) +{ + cmd = cmd | (addr & MCP25XXFD_ADDRESS_MASK); + + data[0] = (cmd >> 8) & 0xff; + data[1] = (cmd >> 0) & 0xff; +} + +static inline int mcp25xxfd_cmd_first_byte(u32 mask) +{ + return (mask & 0x0000ffff) ? + ((mask & 0x000000ff) ? 0 : 1) : + ((mask & 0x00ff0000) ? 2 : 3); +} + +static inline int mcp25xxfd_cmd_last_byte(u32 mask) +{ + return (mask & 0xffff0000) ? + ((mask & 0xff000000) ? 3 : 2) : + ((mask & 0x0000ff00) ? 1 : 0); +} + +int mcp25xxfd_cmd_read_multi(struct spi_device *spi, u32 reg, + void *data, int n); +int mcp25xxfd_cmd_read_mask(struct spi_device *spi, u32 reg, + u32 *data, u32 mask); +static inline int mcp25xxfd_cmd_read(struct spi_device *spi, u32 reg, + u32 *data) +{ + return mcp25xxfd_cmd_read_mask(spi, reg, data, -1); +} + +int mcp25xxfd_cmd_read_regs(struct spi_device *spi, u32 reg, + u32 *data, u32 bytes); + +int mcp25xxfd_cmd_write_multi(struct spi_device *spi, u32 reg, + void *data, int n); +int mcp25xxfd_cmd_write_mask(struct spi_device *spi, u32 reg, + u32 data, u32 mask); +static inline int mcp25xxfd_cmd_write(struct spi_device *spi, u32 reg, + u32 data) +{ + return mcp25xxfd_cmd_write_mask(spi, reg, data, -1); +} + +int mcp25xxfd_cmd_write_regs(struct spi_device *spi, u32 reg, + u32 *data, u32 bytes); + +int mcp25xxfd_cmd_reset(struct spi_device *spi); + +#endif /* __MCP25XXFD_CMD_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.c new file mode 100644 index 000000000000..b893d8009448 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_crc.h" +#include "mcp25xxfd_regs.h" +#include "mcp25xxfd_priv.h" + +int mcp25xxfd_crc_enable_int(struct mcp25xxfd_priv *priv, bool enable) +{ + u32 mask = MCP25XXFD_CRC_CRCERRIE | MCP25XXFD_CRC_FERRIE; + + priv->regs.crc &= ~mask; + priv->regs.crc |= enable ? mask : 0; + + return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_CRC, + priv->regs.crc, mask); +} + +int mcp25xxfd_crc_clear_int(struct mcp25xxfd_priv *priv) +{ + return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_CRC, 0, + MCP25XXFD_CRC_CRCERRIF | + MCP25XXFD_CRC_FERRIF); +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.h new file mode 100644 index 000000000000..6e42fe0fad0f --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ +#ifndef __MCP25XXFD_CRC_H +#define __MCP25XXFD_CRC_H + +#include "mcp25xxfd_priv.h" + +int mcp25xxfd_crc_enable_int(struct mcp25xxfd_priv *priv, bool enable); +int mcp25xxfd_crc_clear_int(struct mcp25xxfd_priv *priv); + +#endif /* __MCP25XXFD_CRC_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.c new file mode 100644 index 000000000000..56e2c4fbf52d --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#include <linux/kernel.h> +#include <linux/spi/spi.h> +#include "mcp25xxfd_cmd.h" +#include "mcp25xxfd_ecc.h" +#include "mcp25xxfd_priv.h" +#include "mcp25xxfd_regs.h" + +int mcp25xxfd_ecc_clear_int(struct mcp25xxfd_priv *priv) +{ + u32 val, addr; + int ret; + + /* First report the error address */ + ret = mcp25xxfd_cmd_read(priv->spi, MCP25XXFD_ECCSTAT, &val); + if (ret) + return ret; + + /* If no flags are set then nothing to do */ + if (!(val & (MCP25XXFD_ECCSTAT_SECIF | MCP25XXFD_ECCSTAT_DEDIF))) + return 0; + + addr = (val & MCP25XXFD_ECCSTAT_ERRADDR_MASK) >> + MCP25XXFD_ECCSTAT_ERRADDR_SHIFT; + + dev_err_ratelimited(&priv->spi->dev, "ECC %s bit error at %03x\n", + (val & MCP25XXFD_ECCSTAT_DEDIF) ? + "double" : "single", + addr); + + /* Clear the error */ + return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_ECCSTAT, 0, + MCP25XXFD_ECCSTAT_SECIF | + MCP25XXFD_ECCSTAT_DEDIF); +} + +int mcp25xxfd_ecc_enable_int(struct mcp25xxfd_priv *priv, bool enable) +{ + u32 mask = MCP25XXFD_ECCCON_SECIE | MCP25XXFD_ECCCON_DEDIE; + + priv->regs.ecccon &= ~mask; + priv->regs.ecccon |= MCP25XXFD_ECCCON_ECCEN | (enable ? mask : 0); + + return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_ECCCON, + priv->regs.ecccon, + MCP25XXFD_ECCCON_ECCEN | mask); +} + +int mcp25xxfd_ecc_enable(struct mcp25xxfd_priv *priv) +{ + u8 buffer[256]; + int i, ret; + + ret = mcp25xxfd_ecc_enable_int(priv, false); + if (ret) + return ret; + + memset(buffer, 0, sizeof(buffer)); + for (i = 0; i < MCP25XXFD_SRAM_SIZE; i += sizeof(buffer)) { + ret = mcp25xxfd_cmd_write_multi(priv->spi, + MCP25XXFD_SRAM_ADDR(i), + buffer, sizeof(buffer)); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.h new file mode 100644 index 000000000000..117f58c65a46 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ +#ifndef __MCP25XXFD_ECC_H +#define __MCP25XXFD_ECC_H + +#include "mcp25xxfd_priv.h" + +int mcp25xxfd_ecc_clear_int(struct mcp25xxfd_priv *priv); +int mcp25xxfd_ecc_enable_int(struct mcp25xxfd_priv *priv, bool enable); +int mcp25xxfd_ecc_enable(struct mcp25xxfd_priv *priv); + +#endif /* __MCP25XXFD_ECC_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c new file mode 100644 index 000000000000..182172b6c59c --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#include <linux/kernel.h> +#include <linux/spi/spi.h> + +#include "mcp25xxfd_can_int.h" +#include "mcp25xxfd_crc.h" +#include "mcp25xxfd_ecc.h" +#include "mcp25xxfd_int.h" +#include "mcp25xxfd_priv.h" + +int mcp25xxfd_int_clear(struct mcp25xxfd_priv *priv) +{ + int ret; + + ret = mcp25xxfd_ecc_clear_int(priv); + if (ret) + return ret; + + ret = mcp25xxfd_crc_clear_int(priv); + if (ret) + return ret; + + return mcp25xxfd_can_int_clear(priv); +} + +int mcp25xxfd_int_enable(struct mcp25xxfd_priv *priv, bool enable) +{ + int ret; + + /* If we enable interrupts, then clear interrupt first */ + if (enable) { + ret = mcp25xxfd_int_clear(priv); + if (ret) + return ret; + } + + ret = mcp25xxfd_crc_enable_int(priv, enable); + if (ret) + return ret; + + ret = mcp25xxfd_ecc_enable(priv); + if (ret) + goto out_crc; + + ret = mcp25xxfd_ecc_enable_int(priv, enable); + if (ret) + goto out_crc; + + ret = mcp25xxfd_can_int_enable(priv, enable); + if (ret) + goto out_ecc; + + /* If we disable interrupts, then clear interrupt flags last */ + if (!enable) + mcp25xxfd_int_clear(priv); + + return 0; + +out_ecc: + mcp25xxfd_ecc_enable_int(priv, false); + +out_crc: + mcp25xxfd_crc_enable_int(priv, false); + return ret; +} diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.h new file mode 100644 index 000000000000..4daf0182d1af --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ +#ifndef __MCP25XXFD_INT_H +#define __MCP25XXFD_INT_H + +#include "mcp25xxfd_priv.h" + +int mcp25xxfd_int_clear(struct mcp25xxfd_priv *priv); +int mcp25xxfd_int_enable(struct mcp25xxfd_priv *priv, bool enable); + +#endif /* __MCP25XXFD_INT_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h new file mode 100644 index 000000000000..85c27a7f6785 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#ifndef __MCP25XXFD_PRIV_H +#define __MCP25XXFD_PRIV_H + +#include <linux/clk.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#include "mcp25xxfd_regs.h" + +#define DEVICE_NAME "mcp25xxfd" +#define CLOCK_4_MHZ 4000000 +#define CLOCK_10_MHZ 10000000 +#define CLOCK_40_MHZ 40000000 + +enum mcp25xxfd_model { + CAN_MCP2517FD = 0x2517, +}; + +struct mcp25xxfd_can_priv; +struct mcp25xxfd_priv { + struct spi_device *spi; + struct clk *clk; + struct mcp25xxfd_can_priv *cpriv; + + /* actual model of the mcp25xxfd */ + enum mcp25xxfd_model model; + + /* full device name used for interrupts */ + char device_name[32]; + + int clock_freq; + struct regulator *power; + + /* configuration registers */ + struct { + u32 osc; + u32 iocon; + u32 crc; + u32 ecccon; + } regs; +}; + +#endif /* __MCP25XXFD_PRIV_H */ diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h new file mode 100644 index 000000000000..222527439c70 --- /dev/null +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h @@ -0,0 +1,661 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface + * + * Copyright 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#ifndef __MCP25XXFD_REGS_H +#define __MCP25XXFD_REGS_H + +#include <linux/bitops.h> + +/* some constants derived from the datasheets */ +#define MCP25XXFD_OST_DELAY_MS 3 +#define MCP25XXFD_MIN_CLOCK_FREQUENCY 1000000 +#define MCP25XXFD_MAX_CLOCK_FREQUENCY 40000000 +#define MCP25XXFD_PLL_MULTIPLIER 10 +#define MCP25XXFD_AUTO_PLL_MAX_CLOCK_FREQUENCY \ + (MCP25XXFD_MAX_CLOCK_FREQUENCY / MCP25XXFD_PLL_MULTIPLIER) +#define MCP25XXFD_SCLK_DIVIDER 2 + +/* GPIO, clock, ecc related register definitions of Controller itself */ +#define MCP25XXFD_SFR_BASE(x) (0xE00 + (x)) +#define MCP25XXFD_OSC MCP25XXFD_SFR_BASE(0x00) +# define MCP25XXFD_OSC_PLLEN BIT(0) +# define MCP25XXFD_OSC_OSCDIS BIT(2) +# define MCP25XXFD_OSC_SCLKDIV BIT(4) +# define MCP25XXFD_OSC_CLKODIV_BITS 2 +# define MCP25XXFD_OSC_CLKODIV_SHIFT 5 +# define MCP25XXFD_OSC_CLKODIV_MASK \ + GENMASK(MCP25XXFD_OSC_CLKODIV_SHIFT \ + + MCP25XXFD_OSC_CLKODIV_BITS - 1, \ + MCP25XXFD_OSC_CLKODIV_SHIFT) +# define MCP25XXFD_OSC_CLKODIV_10 3 +# define MCP25XXFD_OSC_CLKODIV_4 2 +# define MCP25XXFD_OSC_CLKODIV_2 1 +# define MCP25XXFD_OSC_CLKODIV_1 0 +# define MCP25XXFD_OSC_PLLRDY BIT(8) +# define MCP25XXFD_OSC_OSCRDY BIT(10) +# define MCP25XXFD_OSC_SCLKRDY BIT(12) +#define MCP25XXFD_IOCON MCP25XXFD_SFR_BASE(0x04) +# define MCP25XXFD_IOCON_TRIS0 BIT(0) +# define MCP25XXFD_IOCON_TRIS1 BIT(1) +# define MCP25XXFD_IOCON_XSTBYEN BIT(6) +# define MCP25XXFD_IOCON_LAT0 BIT(8) +# define MCP25XXFD_IOCON_LAT1 BIT(9) +# define MCP25XXFD_IOCON_GPIO0 BIT(16) +# define MCP25XXFD_IOCON_GPIO1 BIT(17) +# define MCP25XXFD_IOCON_PM0 BIT(24) +# define MCP25XXFD_IOCON_PM1 BIT(25) +# define MCP25XXFD_IOCON_TXCANOD BIT(28) +# define MCP25XXFD_IOCON_SOF BIT(29) +# define MCP25XXFD_IOCON_INTOD BIT(30) +#define MCP25XXFD_CRC MCP25XXFD_SFR_BASE(0x08) +# define MCP25XXFD_CRC_MASK GENMASK(15, 0) +# define MCP25XXFD_CRC_CRCERRIE BIT(16) +# define MCP25XXFD_CRC_FERRIE BIT(17) +# define MCP25XXFD_CRC_CRCERRIF BIT(24) +# define MCP25XXFD_CRC_FERRIF BIT(25) +#define MCP25XXFD_ECCCON MCP25XXFD_SFR_BASE(0x0C) +# define MCP25XXFD_ECCCON_ECCEN BIT(0) +# define MCP25XXFD_ECCCON_SECIE BIT(1) +# define MCP25XXFD_ECCCON_DEDIE BIT(2) +# define MCP25XXFD_ECCCON_PARITY_BITS 6 +# define MCP25XXFD_ECCCON_PARITY_SHIFT 8 +# define MCP25XXFD_ECCCON_PARITY_MASK \ + GENMASK(MCP25XXFD_ECCCON_PARITY_SHIFT \ + + MCP25XXFD_ECCCON_PARITY_BITS - 1, \ + MCP25XXFD_ECCCON_PARITY_SHIFT) +#define MCP25XXFD_ECCSTAT MCP25XXFD_SFR_BASE(0x10) +# define MCP25XXFD_ECCSTAT_SECIF BIT(1) +# define MCP25XXFD_ECCSTAT_DEDIF BIT(2) +# define MCP25XXFD_ECCSTAT_ERRADDR_SHIFT 8 +# define MCP25XXFD_ECCSTAT_ERRADDR_MASK \ + GENMASK(MCP25XXFD_ECCSTAT_ERRADDR_SHIFT + 11, \ + MCP25XXFD_ECCSTAT_ERRADDR_SHIFT) + +/* CAN related register definitions of Controller CAN block */ +#define MCP25XXFD_CAN_SFR_BASE(x) (0x000 + (x)) +#define MCP25XXFD_CAN_CON \ + MCP25XXFD_CAN_SFR_BASE(0x00) +# define MCP25XXFD_CAN_CON_DNCNT_BITS 5 +# define MCP25XXFD_CAN_CON_DNCNT_SHIFT 0 +# define MCP25XXFD_CAN_CON_DNCNT_MASK \ + GENMASK(MCP25XXFD_CAN_CON_DNCNT_SHIFT + \ + MCP25XXFD_CAN_CON_DNCNT_BITS - 1, \ + MCP25XXFD_CAN_CON_DNCNT_SHIFT) +# define MCP25XXFD_CAN_CON_ISOCRCEN BIT(5) +# define MCP25XXFD_CAN_CON_PXEDIS BIT(6) +# define MCP25XXFD_CAN_CON_WAKFIL BIT(8) +# define MCP25XXFD_CAN_CON_WFT_BITS 2 +# define MCP25XXFD_CAN_CON_WFT_SHIFT 9 +# define MCP25XXFD_CAN_CON_WFT_MASK \ + GENMASK(MCP25XXFD_CAN_CON_WFT_SHIFT + \ + MCP25XXFD_CAN_CON_WFT_BITS - 1, \ + MCP25XXFD_CAN_CON_WFT_SHIFT) +# define MCP25XXFD_CAN_CON_BUSY BIT(11) +# define MCP25XXFD_CAN_CON_BRSDIS BIT(12) +# define MCP25XXFD_CAN_CON_RTXAT BIT(16) +# define MCP25XXFD_CAN_CON_ESIGM BIT(17) +# define MCP25XXFD_CAN_CON_SERR2LOM BIT(18) +# define MCP25XXFD_CAN_CON_STEF BIT(19) +# define MCP25XXFD_CAN_CON_TXQEN BIT(20) +# define MCP25XXFD_CAN_CON_OPMODE_BITS 3 +# define MCP25XXFD_CAN_CON_OPMOD_SHIFT 21 +# define MCP25XXFD_CAN_CON_OPMOD_MASK \ + GENMASK(MCP25XXFD_CAN_CON_OPMOD_SHIFT + \ + MCP25XXFD_CAN_CON_OPMODE_BITS - 1, \ + MCP25XXFD_CAN_CON_OPMOD_SHIFT) +# define MCP25XXFD_CAN_CON_REQOP_BITS 3 +# define MCP25XXFD_CAN_CON_REQOP_SHIFT 24 +# define MCP25XXFD_CAN_CON_REQOP_MASK \ + GENMASK(MCP25XXFD_CAN_CON_REQOP_SHIFT + \ + MCP25XXFD_CAN_CON_REQOP_BITS - 1, \ + MCP25XXFD_CAN_CON_REQOP_SHIFT) +# define MCP25XXFD_CAN_CON_MODE_MIXED 0 +# define MCP25XXFD_CAN_CON_MODE_SLEEP 1 +# define MCP25XXFD_CAN_CON_MODE_INT_LOOPBACK 2 +# define MCP25XXFD_CAN_CON_MODE_LISTENONLY 3 +# define MCP25XXFD_CAN_CON_MODE_CONFIG 4 +# define MCP25XXFD_CAN_CON_MODE_EXT_LOOPBACK 5 +# define MCP25XXFD_CAN_CON_MODE_CAN2_0 6 +# define MCP25XXFD_CAN_CON_MODE_RESTRICTED 7 +# define MCP25XXFD_CAN_CON_ABAT BIT(27) +# define MCP25XXFD_CAN_CON_TXBWS_BITS 3 +# define MCP25XXFD_CAN_CON_TXBWS_SHIFT 28 +# define MCP25XXFD_CAN_CON_TXBWS_MASK \ + GENMASK(MCP25XXFD_CAN_CON_TXBWS_SHIFT + \ + MCP25XXFD_CAN_CON_TXBWS_BITS - 1, \ + MCP25XXFD_CAN_CON_TXBWS_SHIFT) +# define MCP25XXFD_CAN_CON_DEFAULT \ + (MCP25XXFD_CAN_CON_ISOCRCEN | \ + MCP25XXFD_CAN_CON_PXEDIS | \ + MCP25XXFD_CAN_CON_WAKFIL | \ + (3 << MCP25XXFD_CAN_CON_WFT_SHIFT) | \ + MCP25XXFD_CAN_CON_STEF | \ + MCP25XXFD_CAN_CON_TXQEN | \ + (MCP25XXFD_CAN_CON_MODE_CONFIG << MCP25XXFD_CAN_CON_OPMOD_SHIFT) | \ + (MCP25XXFD_CAN_CON_MODE_CONFIG << MCP25XXFD_CAN_CON_REQOP_SHIFT)) +# define MCP25XXFD_CAN_CON_DEFAULT_MASK \ + (MCP25XXFD_CAN_CON_DNCNT_MASK | \ + MCP25XXFD_CAN_CON_ISOCRCEN | \ + MCP25XXFD_CAN_CON_PXEDIS | \ + MCP25XXFD_CAN_CON_WAKFIL | \ + MCP25XXFD_CAN_CON_WFT_MASK | \ + MCP25XXFD_CAN_CON_BRSDIS | \ + MCP25XXFD_CAN_CON_RTXAT | \ + MCP25XXFD_CAN_CON_ESIGM | \ + MCP25XXFD_CAN_CON_SERR2LOM | \ + MCP25XXFD_CAN_CON_STEF | \ + MCP25XXFD_CAN_CON_TXQEN | \ + MCP25XXFD_CAN_CON_OPMOD_MASK | \ + MCP25XXFD_CAN_CON_REQOP_MASK | \ + MCP25XXFD_CAN_CON_ABAT | \ + MCP25XXFD_CAN_CON_TXBWS_MASK) +#define MCP25XXFD_CAN_NBTCFG MCP25XXFD_CAN_SFR_BASE(0x04) +# define MCP25XXFD_CAN_NBTCFG_SJW_BITS 7 +# define MCP25XXFD_CAN_NBTCFG_SJW_SHIFT 0 +# define MCP25XXFD_CAN_NBTCFG_SJW_MASK \ + GENMASK(MCP25XXFD_CAN_NBTCFG_SJW_SHIFT + \ + MCP25XXFD_CAN_NBTCFG_SJW_BITS - 1, \ + MCP25XXFD_CAN_NBTCFG_SJW_SHIFT) +# define MCP25XXFD_CAN_NBTCFG_TSEG2_BITS 7 +# define MCP25XXFD_CAN_NBTCFG_TSEG2_SHIFT 8 +# define MCP25XXFD_CAN_NBTCFG_TSEG2_MASK \ + GENMASK(MCP25XXFD_CAN_NBTCFG_TSEG2_SHIFT + \ + MCP25XXFD_CAN_NBTCFG_TSEG2_BITS - 1, \ + MCP25XXFD_CAN_NBTCFG_TSEG2_SHIFT) +# define MCP25XXFD_CAN_NBTCFG_TSEG1_BITS 8 +# define MCP25XXFD_CAN_NBTCFG_TSEG1_SHIFT 16 +# define MCP25XXFD_CAN_NBTCFG_TSEG1_MASK \ + GENMASK(MCP25XXFD_CAN_NBTCFG_TSEG1_SHIFT + \ + MCP25XXFD_CAN_NBTCFG_TSEG1_BITS - 1, \ + MCP25XXFD_CAN_NBTCFG_TSEG1_SHIFT) +# define MCP25XXFD_CAN_NBTCFG_BRP_BITS 8 +# define MCP25XXFD_CAN_NBTCFG_BRP_SHIFT 24 +# define MCP25XXFD_CAN_NBTCFG_BRP_MASK \ + GENMASK(MCP25XXFD_CAN_NBTCFG_BRP_SHIFT + \ + MCP25XXFD_CAN_NBTCFG_BRP_BITS - 1, \ + MCP25XXFD_CAN_NBTCFG_BRP_SHIFT) +#define MCP25XXFD_CAN_DBTCFG MCP25XXFD_CAN_SFR_BASE(0x08) +# define MCP25XXFD_CAN_DBTCFG_SJW_BITS 4 +# define MCP25XXFD_CAN_DBTCFG_SJW_SHIFT 0 +# define MCP25XXFD_CAN_DBTCFG_SJW_MASK \ + GENMASK(MCP25XXFD_CAN_DBTCFG_SJW_SHIFT + \ + MCP25XXFD_CAN_DBTCFG_SJW_BITS - 1, \ + MCP25XXFD_CAN_DBTCFG_SJW_SHIFT) +# define MCP25XXFD_CAN_DBTCFG_TSEG2_BITS 4 +# define MCP25XXFD_CAN_DBTCFG_TSEG2_SHIFT 8 +# define MCP25XXFD_CAN_DBTCFG_TSEG2_MASK \ + GENMASK(MCP25XXFD_CAN_DBTCFG_TSEG2_SHIFT + \ + MCP25XXFD_CAN_DBTCFG_TSEG2_BITS - 1, \ + MCP25XXFD_CAN_DBTCFG_TSEG2_SHIFT) +# define MCP25XXFD_CAN_DBTCFG_TSEG1_BITS 5 +# define MCP25XXFD_CAN_DBTCFG_TSEG1_SHIFT 16 +# define MCP25XXFD_CAN_DBTCFG_TSEG1_MASK \ + GENMASK(MCP25XXFD_CAN_DBTCFG_TSEG1_SHIFT + \ + MCP25XXFD_CAN_DBTCFG_TSEG1_BITS - 1, \ + MCP25XXFD_CAN_DBTCFG_TSEG1_SHIFT) +# define MCP25XXFD_CAN_DBTCFG_BRP_BITS 8 +# define MCP25XXFD_CAN_DBTCFG_BRP_SHIFT 24 +# define MCP25XXFD_CAN_DBTCFG_BRP_MASK \ + GENMASK(MCP25XXFD_CAN_DBTCFG_BRP_SHIFT + \ + MCP25XXFD_CAN_DBTCFG_BRP_BITS - 1, \ + MCP25XXFD_CAN_DBTCFG_BRP_SHIFT) +#define MCP25XXFD_CAN_TDC MCP25XXFD_CAN_SFR_BASE(0x0C) +# define MCP25XXFD_CAN_TDC_TDCV_BITS 5 +# define MCP25XXFD_CAN_TDC_TDCV_SHIFT 0 +# define MCP25XXFD_CAN_TDC_TDCV_MASK \ + GENMASK(MCP25XXFD_CAN_TDC_TDCV_SHIFT + \ + MCP25XXFD_CAN_TDC_TDCV_BITS - 1, \ + MCP25XXFD_CAN_TDC_TDCV_SHIFT) +# define MCP25XXFD_CAN_TDC_TDCO_BITS 5 +# define MCP25XXFD_CAN_TDC_TDCO_SHIFT 8 +# define MCP25XXFD_CAN_TDC_TDCO_MASK \ + GENMASK(MCP25XXFD_CAN_TDC_TDCO_SHIFT + \ + MCP25XXFD_CAN_TDC_TDCO_BITS - 1, \ + MCP25XXFD_CAN_TDC_TDCO_SHIFT) +# define MCP25XXFD_CAN_TDC_TDCMOD_BITS 2 +# define MCP25XXFD_CAN_TDC_TDCMOD_SHIFT 16 +# define MCP25XXFD_CAN_TDC_TDCMOD_MASK \ + GENMASK(MCP25XXFD_CAN_TDC_TDCMOD_SHIFT + \ + MCP25XXFD_CAN_TDC_TDCMOD_BITS - 1, \ + MCP25XXFD_CAN_TDC_TDCMOD_SHIFT) +# define MCP25XXFD_CAN_TDC_TDCMOD_DISABLED 0 +# define MCP25XXFD_CAN_TDC_TDCMOD_MANUAL 1 +# define MCP25XXFD_CAN_TDC_TDCMOD_AUTO 2 +# define MCP25XXFD_CAN_TDC_SID11EN BIT(24) +# define MCP25XXFD_CAN_TDC_EDGFLTEN BIT(25) +#define MCP25XXFD_CAN_TBC MCP25XXFD_CAN_SFR_BASE(0x10) +#define MCP25XXFD_CAN_TSCON MCP25XXFD_CAN_SFR_BASE(0x14) +# define MCP25XXFD_CAN_TSCON_TBCPRE_BITS 10 +# define MCP25XXFD_CAN_TSCON_TBCPRE_SHIFT 0 +# define MCP25XXFD_CAN_TSCON_TBCPRE_MASK \ + GENMASK(MCP25XXFD_CAN_TSCON_TBCPRE_SHIFT + \ + MCP25XXFD_CAN_TSCON_TBCPRE_BITS - 1, \ + MCP25XXFD_CAN_TSCON_TBCPRE_SHIFT) +# define MCP25XXFD_CAN_TSCON_TBCEN BIT(16) +# define MCP25XXFD_CAN_TSCON_TSEOF BIT(17) +# define MCP25XXFD_CAN_TSCON_TSRES BIT(18) +#define MCP25XXFD_CAN_VEC MCP25XXFD_CAN_SFR_BASE(0x18) +# define MCP25XXFD_CAN_VEC_ICODE_BITS 7 +# define MCP25XXFD_CAN_VEC_ICODE_SHIFT 0 +# define MCP25XXFD_CAN_VEC_ICODE_MASK \ + GENMASK(MCP25XXFD_CAN_VEC_ICODE_SHIFT + \ + MCP25XXFD_CAN_VEC_ICODE_BITS - 1, \ + MCP25XXFD_CAN_VEC_ICODE_SHIFT) +# define MCP25XXFD_CAN_VEC_FILHIT_BITS 5 +# define MCP25XXFD_CAN_VEC_FILHIT_SHIFT 8 +# define MCP25XXFD_CAN_VEC_FILHIT_MASK \ + GENMASK(MCP25XXFD_CAN_VEC_FILHIT_SHIFT + \ + MCP25XXFD_CAN_VEC_FILHIT_BITS - 1, \ + MCP25XXFD_CAN_VEC_FILHIT_SHIFT) +# define MCP25XXFD_CAN_VEC_TXCODE_BITS 7 +# define MCP25XXFD_CAN_VEC_TXCODE_SHIFT 16 +# define MCP25XXFD_CAN_VEC_TXCODE_MASK \ + GENMASK(MCP25XXFD_CAN_VEC_TXCODE_SHIFT + \ + MCP25XXFD_CAN_VEC_TXCODE_BITS - 1, \ + MCP25XXFD_CAN_VEC_TXCODE_SHIFT) +# define MCP25XXFD_CAN_VEC_RXCODE_BITS 7 +# define MCP25XXFD_CAN_VEC_RXCODE_SHIFT 24 +# define MCP25XXFD_CAN_VEC_RXCODE_MASK \ + GENMASK(MCP25XXFD_CAN_VEC_RXCODE_SHIFT + \ + MCP25XXFD_CAN_VEC_RXCODE_BITS - 1, \ + MCP25XXFD_CAN_VEC_RXCODE_SHIFT) +#define MCP25XXFD_CAN_INT MCP25XXFD_CAN_SFR_BASE(0x1C) +# define MCP25XXFD_CAN_INT_IF_SHIFT 0 +# define MCP25XXFD_CAN_INT_TXIF BIT(0) +# define MCP25XXFD_CAN_INT_RXIF BIT(1) +# define MCP25XXFD_CAN_INT_TBCIF BIT(2) +# define MCP25XXFD_CAN_INT_MODIF BIT(3) +# define MCP25XXFD_CAN_INT_TEFIF BIT(4) +# define MCP25XXFD_CAN_INT_ECCIF BIT(8) +# define MCP25XXFD_CAN_INT_SPICRCIF BIT(9) +# define MCP25XXFD_CAN_INT_TXATIF BIT(10) +# define MCP25XXFD_CAN_INT_RXOVIF BIT(11) +# define MCP25XXFD_CAN_INT_SERRIF BIT(12) +# define MCP25XXFD_CAN_INT_CERRIF BIT(13) +# define MCP25XXFD_CAN_INT_WAKIF BIT(14) +# define MCP25XXFD_CAN_INT_IVMIF BIT(15) +# define MCP25XXFD_CAN_INT_IF_MASK \ + (MCP25XXFD_CAN_INT_TXIF | \ + MCP25XXFD_CAN_INT_RXIF | \ + MCP25XXFD_CAN_INT_TBCIF | \ + MCP25XXFD_CAN_INT_MODIF | \ + MCP25XXFD_CAN_INT_TEFIF | \ + MCP25XXFD_CAN_INT_ECCIF | \ + MCP25XXFD_CAN_INT_SPICRCIF | \ + MCP25XXFD_CAN_INT_TXATIF | \ + MCP25XXFD_CAN_INT_RXOVIF | \ + MCP25XXFD_CAN_INT_CERRIF | \ + MCP25XXFD_CAN_INT_SERRIF | \ + MCP25XXFD_CAN_INT_WAKIF | \ + MCP25XXFD_CAN_INT_IVMIF) +# define MCP25XXFD_CAN_INT_IF_CLEAR_MASK \ + (MCP25XXFD_CAN_INT_TBCIF | \ + MCP25XXFD_CAN_INT_MODIF | \ + MCP25XXFD_CAN_INT_CERRIF | \ + MCP25XXFD_CAN_INT_SERRIF | \ + MCP25XXFD_CAN_INT_WAKIF | \ + MCP25XXFD_CAN_INT_IVMIF) +# define MCP25XXFD_CAN_INT_IE_SHIFT 16 +# define MCP25XXFD_CAN_INT_TXIE \ + (MCP25XXFD_CAN_INT_TXIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_RXIE \ + (MCP25XXFD_CAN_INT_RXIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_TBCIE \ + (MCP25XXFD_CAN_INT_TBCIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_MODIE \ + (MCP25XXFD_CAN_INT_MODIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_TEFIE \ + (MCP25XXFD_CAN_INT_TEFIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_ECCIE \ + (MCP25XXFD_CAN_INT_ECCIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_SPICRCIE \ + (MCP25XXFD_CAN_INT_SPICRCIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_TXATIE \ + (MCP25XXFD_CAN_INT_TXATIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_RXOVIE \ + (MCP25XXFD_CAN_INT_RXOVIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_CERRIE \ + (MCP25XXFD_CAN_INT_CERRIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_SERRIE \ + (MCP25XXFD_CAN_INT_SERRIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_WAKIE \ + (MCP25XXFD_CAN_INT_WAKIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_IVMIE \ + (MCP25XXFD_CAN_INT_IVMIF << MCP25XXFD_CAN_INT_IE_SHIFT) +# define MCP25XXFD_CAN_INT_IE_MASK \ + (MCP25XXFD_CAN_INT_TXIE | \ + MCP25XXFD_CAN_INT_RXIE | \ + MCP25XXFD_CAN_INT_TBCIE | \ + MCP25XXFD_CAN_INT_MODIE | \ + MCP25XXFD_CAN_INT_TEFIE | \ + MCP25XXFD_CAN_INT_ECCIE | \ + MCP25XXFD_CAN_INT_SPICRCIE | \ + MCP25XXFD_CAN_INT_TXATIE | \ + MCP25XXFD_CAN_INT_RXOVIE | \ + MCP25XXFD_CAN_INT_CERRIE | \ + MCP25XXFD_CAN_INT_SERRIE | \ + MCP25XXFD_CAN_INT_WAKIE | \ + MCP25XXFD_CAN_INT_IVMIE) +#define MCP25XXFD_CAN_RXIF MCP25XXFD_CAN_SFR_BASE(0x20) +#define MCP25XXFD_CAN_TXIF MCP25XXFD_CAN_SFR_BASE(0x24) +#define MCP25XXFD_CAN_RXOVIF MCP25XXFD_CAN_SFR_BASE(0x28) +#define MCP25XXFD_CAN_TXATIF MCP25XXFD_CAN_SFR_BASE(0x2C) +#define MCP25XXFD_CAN_TXREQ MCP25XXFD_CAN_SFR_BASE(0x30) +#define MCP25XXFD_CAN_TREC MCP25XXFD_CAN_SFR_BASE(0x34) +# define MCP25XXFD_CAN_TREC_REC_BITS 8 +# define MCP25XXFD_CAN_TREC_REC_SHIFT 0 +# define MCP25XXFD_CAN_TREC_REC_MASK \ + GENMASK(MCP25XXFD_CAN_TREC_REC_SHIFT + \ + MCP25XXFD_CAN_TREC_REC_BITS - 1, \ + MCP25XXFD_CAN_TREC_REC_SHIFT) +# define MCP25XXFD_CAN_TREC_TEC_BITS 8 +# define MCP25XXFD_CAN_TREC_TEC_SHIFT 8 +# define MCP25XXFD_CAN_TREC_TEC_MASK \ + GENMASK(MCP25XXFD_CAN_TREC_TEC_SHIFT + \ + MCP25XXFD_CAN_TREC_TEC_BITS - 1, \ + MCP25XXFD_CAN_TREC_TEC_SHIFT) +# define MCP25XXFD_CAN_TREC_EWARN BIT(16) +# define MCP25XXFD_CAN_TREC_RXWARN BIT(17) +# define MCP25XXFD_CAN_TREC_TXWARN BIT(18) +# define MCP25XXFD_CAN_TREC_RXBP BIT(19) +# define MCP25XXFD_CAN_TREC_TXBP BIT(20) +# define MCP25XXFD_CAN_TREC_TXBO BIT(21) +#define MCP25XXFD_CAN_BDIAG0 MCP25XXFD_CAN_SFR_BASE(0x38) +# define MCP25XXFD_CAN_BDIAG0_NRERRCNT_BITS 8 +# define MCP25XXFD_CAN_BDIAG0_NRERRCNT_SHIFT 0 +# define MCP25XXFD_CAN_BDIAG0_NRERRCNT_MASK \ + GENMASK(MCP25XXFD_CAN_BDIAG0_NRERRCNT_SHIFT + \ + MCP25XXFD_CAN_BDIAG0_NRERRCNT_BITS - 1, \ + MCP25XXFD_CAN_BDIAG0_NRERRCNT_SHIFT) +# define MCP25XXFD_CAN_BDIAG0_NTERRCNT_BITS 8 +# define MCP25XXFD_CAN_BDIAG0_NTERRCNT_SHIFT 8 +# define MCP25XXFD_CAN_BDIAG0_NTERRCNT_MASK \ + GENMASK(MCP25XXFD_CAN_BDIAG0_NTERRCNT_SHIFT + \ + MCP25XXFD_CAN_BDIAG0_NTERRCNT_BITS - 1, \ + MCP25XXFD_CAN_BDIAG0_NTERRCNT_SHIFT) +# define MCP25XXFD_CAN_BDIAG0_DRERRCNT_BITS 8 +# define MCP25XXFD_CAN_BDIAG0_DRERRCNT_SHIFT 16 +# define MCP25XXFD_CAN_BDIAG0_DRERRCNT_MASK \ + GENMASK(MCP25XXFD_CAN_BDIAG0_DRERRCNT_SHIFT + \ + MCP25XXFD_CAN_BDIAG0_DRERRCNT_BITS - 1, \ + MCP25XXFD_CAN_BDIAG0_DRERRCNT_SHIFT) +# define MCP25XXFD_CAN_BDIAG0_DTERRCNT_BITS 8 +# define MCP25XXFD_CAN_BDIAG0_DTERRCNT_SHIFT 24 +# define MCP25XXFD_CAN_BDIAG0_DTERRCNT_MASK \ + GENMASK(MCP25XXFD_CAN_BDIAG0_DTERRCNT_SHIFT + \ + MCP25XXFD_CAN_BDIAG0_DTERRCNT_BITS - 1, \ + MCP25XXFD_CAN_BDIAG0_DTERRCNT_SHIFT) +#define MCP25XXFD_CAN_BDIAG1 MCP25XXFD_CAN_SFR_BASE(0x3C) +# define MCP25XXFD_CAN_BDIAG1_EFMSGCNT_BITS 16 +# define MCP25XXFD_CAN_BDIAG1_EFMSGCNT_SHIFT 0 +# define MCP25XXFD_CAN_BDIAG1_EFMSGCNT_MASK \ + GENMASK(MCP25XXFD_CAN_BDIAG1_EFMSGCNT_SHIFT + \ + MCP25XXFD_CAN_BDIAG1_EFMSGCNT_BITS - 1, \ + MCP25XXFD_CAN_BDIAG1_EFMSGCNT_SHIFT) +# define MCP25XXFD_CAN_BDIAG1_NBIT0ERR BIT(16) +# define MCP25XXFD_CAN_BDIAG1_NBIT1ERR BIT(17) +# define MCP25XXFD_CAN_BDIAG1_NACKERR BIT(18) +# define MCP25XXFD_CAN_BDIAG1_NSTUFERR BIT(19) +# define MCP25XXFD_CAN_BDIAG1_NFORMERR BIT(20) +# define MCP25XXFD_CAN_BDIAG1_NCRCERR BIT(21) +# define MCP25XXFD_CAN_BDIAG1_TXBOERR BIT(23) +# define MCP25XXFD_CAN_BDIAG1_DBIT0ERR BIT(24) +# define MCP25XXFD_CAN_BDIAG1_DBIT1ERR BIT(25) +# define MCP25XXFD_CAN_BDIAG1_DFORMERR BIT(27) +# define MCP25XXFD_CAN_BDIAG1_DSTUFERR BIT(28) +# define MCP25XXFD_CAN_BDIAG1_DCRCERR BIT(29) +# define MCP25XXFD_CAN_BDIAG1_ESI BIT(30) +# define MCP25XXFD_CAN_BDIAG1_DLCMM BIT(31) +#define MCP25XXFD_CAN_TEFCON MCP25XXFD_CAN_SFR_BASE(0x40) +# define MCP25XXFD_CAN_TEFCON_TEFNEIE BIT(0) +# define MCP25XXFD_CAN_TEFCON_TEFHIE BIT(1) +# define MCP25XXFD_CAN_TEFCON_TEFFIE BIT(2) +# define MCP25XXFD_CAN_TEFCON_TEFOVIE BIT(3) +# define MCP25XXFD_CAN_TEFCON_TEFTSEN BIT(5) +# define MCP25XXFD_CAN_TEFCON_UINC BIT(8) +# define MCP25XXFD_CAN_TEFCON_FRESET BIT(10) +# define MCP25XXFD_CAN_TEFCON_FSIZE_BITS 5 +# define MCP25XXFD_CAN_TEFCON_FSIZE_SHIFT 24 +# define MCP25XXFD_CAN_TEFCON_FSIZE_MASK \ + GENMASK(MCP25XXFD_CAN_TEFCON_FSIZE_SHIFT + \ + MCP25XXFD_CAN_TEFCON_FSIZE_BITS - 1, \ + MCP25XXFD_CAN_TEFCON_FSIZE_SHIFT) +#define MCP25XXFD_CAN_TEFSTA MCP25XXFD_CAN_SFR_BASE(0x44) +# define MCP25XXFD_CAN_TEFSTA_TEFNEIF BIT(0) +# define MCP25XXFD_CAN_TEFSTA_TEFHIF BIT(1) +# define MCP25XXFD_CAN_TEFSTA_TEFFIF BIT(2) +# define MCP25XXFD_CAN_TEFSTA_TEVOVIF BIT(3) +#define MCP25XXFD_CAN_TEFUA MCP25XXFD_CAN_SFR_BASE(0x48) +#define MCP25XXFD_CAN_RESERVED MCP25XXFD_CAN_SFR_BASE(0x4C) +#define MCP25XXFD_CAN_TXQCON MCP25XXFD_CAN_SFR_BASE(0x50) +# define MCP25XXFD_CAN_TXQCON_TXQNIE BIT(0) +# define MCP25XXFD_CAN_TXQCON_TXQEIE BIT(2) +# define MCP25XXFD_CAN_TXQCON_TXATIE BIT(4) +# define MCP25XXFD_CAN_TXQCON_TXEN BIT(7) +# define MCP25XXFD_CAN_TXQCON_UINC BIT(8) +# define MCP25XXFD_CAN_TXQCON_TXREQ BIT(9) +# define MCP25XXFD_CAN_TXQCON_FRESET BIT(10) +# define MCP25XXFD_CAN_TXQCON_TXPRI_BITS 5 +# define MCP25XXFD_CAN_TXQCON_TXPRI_SHIFT 16 +# define MCP25XXFD_CAN_TXQCON_TXPRI_MASK \ + GENMASK(MCP25XXFD_CAN_TXQCON_TXPRI_SHIFT + \ + MCP25XXFD_CAN_TXQCON_TXPRI_BITS - 1, \ + MCP25XXFD_CAN_TXQCON_TXPRI_SHIFT) +# define MCP25XXFD_CAN_TXQCON_TXAT_BITS 2 +# define MCP25XXFD_CAN_TXQCON_TXAT_SHIFT 21 +# define MCP25XXFD_CAN_TXQCON_TXAT_MASK \ + GENMASK(MCP25XXFD_CAN_TXQCON_TXAT_SHIFT + \ + #MCP25XXFD_CAN_TXQCON_TXAT_BITS - 1, \ + MCP25XXFD_CAN_TXQCON_TXAT_SHIFT) +# define MCP25XXFD_CAN_TXQCON_FSIZE_BITS 5 +# define MCP25XXFD_CAN_TXQCON_FSIZE_SHIFT 24 +# define MCP25XXFD_CAN_TXQCON_FSIZE_MASK \ + GENMASK(MCP25XXFD_CAN_TXQCON_FSIZE_SHIFT + \ + MCP25XXFD_CAN_TXQCON_FSIZE_BITS - 1, \ + MCP25XXFD_CAN_TXQCON_FSIZE_SHIFT) +# define MCP25XXFD_CAN_TXQCON_PLSIZE_BITS 3 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_SHIFT 29 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_MASK \ + GENMASK(MCP25XXFD_CAN_TXQCON_PLSIZE_SHIFT + \ + MCP25XXFD_CAN_TXQCON_PLSIZE_BITS - 1, \ + MCP25XXFD_CAN_TXQCON_PLSIZE_SHIFT) +# define MCP25XXFD_CAN_TXQCON_PLSIZE_8 0 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_12 1 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_16 2 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_20 3 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_24 4 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_32 5 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_48 6 +# define MCP25XXFD_CAN_TXQCON_PLSIZE_64 7 + +#define MCP25XXFD_CAN_TXQSTA MCP25XXFD_CAN_SFR_BASE(0x54) +# define MCP25XXFD_CAN_TXQSTA_TXQNIF BIT(0) +# define MCP25XXFD_CAN_TXQSTA_TXQEIF BIT(2) +# define MCP25XXFD_CAN_TXQSTA_TXATIF BIT(4) +# define MCP25XXFD_CAN_TXQSTA_TXERR BIT(5) +# define MCP25XXFD_CAN_TXQSTA_TXLARB BIT(6) +# define MCP25XXFD_CAN_TXQSTA_TXABT BIT(7) +# define MCP25XXFD_CAN_TXQSTA_TXQCI_BITS 5 +# define MCP25XXFD_CAN_TXQSTA_TXQCI_SHIFT 8 +# define MCP25XXFD_CAN_TXQSTA_TXQCI_MASK \ + GENMASK(MCP25XXFD_CAN_TXQSTA_TXQCI_SHIFT + \ + MCP25XXFD_CAN_TXQSTA_TXQCI_BITS - 1, \ + MCP25XXFD_CAN_TXQSTA_TXQCI_SHIFT) + +#define MCP25XXFD_CAN_TXQUA MCP25XXFD_CAN_SFR_BASE(0x58) +#define MCP25XXFD_CAN_FIFOCON(x) \ + MCP25XXFD_CAN_SFR_BASE(0x5C + 12 * ((x) - 1)) +#define MCP25XXFD_CAN_FIFOCON_TFNRFNIE BIT(0) +#define MCP25XXFD_CAN_FIFOCON_TFHRFHIE BIT(1) +#define MCP25XXFD_CAN_FIFOCON_TFERFFIE BIT(2) +#define MCP25XXFD_CAN_FIFOCON_RXOVIE BIT(3) +#define MCP25XXFD_CAN_FIFOCON_TXATIE BIT(4) +#define MCP25XXFD_CAN_FIFOCON_RXTSEN BIT(5) +#define MCP25XXFD_CAN_FIFOCON_RTREN BIT(6) +#define MCP25XXFD_CAN_FIFOCON_TXEN BIT(7) +#define MCP25XXFD_CAN_FIFOCON_UINC BIT(8) +#define MCP25XXFD_CAN_FIFOCON_TXREQ BIT(9) +#define MCP25XXFD_CAN_FIFOCON_FRESET BIT(10) +# define MCP25XXFD_CAN_FIFOCON_TXPRI_BITS 5 +# define MCP25XXFD_CAN_FIFOCON_TXPRI_SHIFT 16 +# define MCP25XXFD_CAN_FIFOCON_TXPRI_MASK \ + GENMASK(MCP25XXFD_CAN_FIFOCON_TXPRI_SHIFT + \ + MCP25XXFD_CAN_FIFOCON_TXPRI_BITS - 1, \ + MCP25XXFD_CAN_FIFOCON_TXPRI_SHIFT) +# define MCP25XXFD_CAN_FIFOCON_TXAT_BITS 2 +# define MCP25XXFD_CAN_FIFOCON_TXAT_SHIFT 21 +# define MCP25XXFD_CAN_FIFOCON_TXAT_MASK \ + GENMASK(MCP25XXFD_CAN_FIFOCON_TXAT_SHIFT + \ + MCP25XXFD_CAN_FIFOCON_TXAT_BITS - 1, \ + MCP25XXFD_CAN_FIFOCON_TXAT_SHIFT) +# define MCP25XXFD_CAN_FIFOCON_TXAT_ONE_SHOT 0 +# define MCP25XXFD_CAN_FIFOCON_TXAT_THREE_SHOT 1 +# define MCP25XXFD_CAN_FIFOCON_TXAT_UNLIMITED 2 +# define MCP25XXFD_CAN_FIFOCON_FSIZE_BITS 5 +# define MCP25XXFD_CAN_FIFOCON_FSIZE_SHIFT 24 +# define MCP25XXFD_CAN_FIFOCON_FSIZE_MASK \ + GENMASK(MCP25XXFD_CAN_FIFOCON_FSIZE_SHIFT + \ + MCP25XXFD_CAN_FIFOCON_FSIZE_BITS - 1, \ + MCP25XXFD_CAN_FIFOCON_FSIZE_SHIFT) +# define MCP25XXFD_CAN_FIFOCON_PLSIZE_BITS 3 +# define MCP25XXFD_CAN_FIFOCON_PLSIZE_SHIFT 29 +# define MCP25XXFD_CAN_FIFOCON_PLSIZE_MASK \ + GENMASK(MCP25XXFD_CAN_FIFOCON_PLSIZE_SHIFT + \ + MCP25XXFD_CAN_FIFOCON_PLSIZE_BITS - 1, \ + MCP25XXFD_CAN_FIFOCON_PLSIZE_SHIFT) +#define MCP25XXFD_CAN_FIFOSTA(x) \ + MCP25XXFD_CAN_SFR_BASE(0x60 + 12 * ((x) - 1)) +# define MCP25XXFD_CAN_FIFOSTA_TFNRFNIF BIT(0) +# define MCP25XXFD_CAN_FIFOSTA_TFHRFHIF BIT(1) +# define MCP25XXFD_CAN_FIFOSTA_TFERFFIF BIT(2) +# define MCP25XXFD_CAN_FIFOSTA_RXOVIF BIT(3) +# define MCP25XXFD_CAN_FIFOSTA_TXATIF BIT(4) +# define MCP25XXFD_CAN_FIFOSTA_TXERR BIT(5) +# define MCP25XXFD_CAN_FIFOSTA_TXLARB BIT(6) +# define MCP25XXFD_CAN_FIFOSTA_TXABT BIT(7) +# define MCP25XXFD_CAN_FIFOSTA_FIFOCI_BITS 5 +# define MCP25XXFD_CAN_FIFOSTA_FIFOCI_SHIFT 8 +# define MCP25XXFD_CAN_FIFOSTA_FIFOCI_MASK \ + GENMASK(MCP25XXFD_CAN_FIFOSTA_FIFOCI_SHIFT + \ + MCP25XXFD_CAN_FIFOSTA_FIFOCI_BITS - 1, \ + MCP25XXFD_CAN_FIFOSTA_FIFOCI_SHIFT) +#define MCP25XXFD_CAN_FIFOUA(x) \ + MCP25XXFD_CAN_SFR_BASE(0x64 + 12 * ((x) - 1)) +#define MCP25XXFD_CAN_FLTCON(x) \ + MCP25XXFD_CAN_SFR_BASE(0x1D0 + ((x) & 0x1c)) +# define MCP25XXFD_CAN_FILCON_SHIFT(x) (((x) & 3) * 8) +# define MCP25XXFD_CAN_FILCON_BITS(x) MCP25XXFD_CAN_FILCON_BITS_ +# define MCP25XXFD_CAN_FILCON_BITS_ 4 + /* avoid macro reuse warning, so do not use GENMASK as above */ +# define MCP25XXFD_CAN_FILCON_MASK(x) \ + (GENMASK(MCP25XXFD_CAN_FILCON_BITS_ - 1, 0) << \ + MCP25XXFD_CAN_FILCON_SHIFT(x)) +# define MCP25XXFD_CAN_FIFOCON_FLTEN(x) \ + BIT(7 + MCP25XXFD_CAN_FILCON_SHIFT(x)) +#define MCP25XXFD_CAN_FLTOBJ(x) \ + MCP25XXFD_CAN_SFR_BASE(0x1F0 + 8 * (x)) +# define MCP25XXFD_CAN_FILOBJ_SID_BITS 11 +# define MCP25XXFD_CAN_FILOBJ_SID_SHIFT 0 +# define MCP25XXFD_CAN_FILOBJ_SID_MASK \ + GENMASK(MCP25XXFD_CAN_FILOBJ_SID_SHIFT + \ + MCP25XXFD_CAN_FILOBJ_SID_BITS - 1, \ + MCP25XXFD_CAN_FILOBJ_SID_SHIFT) +# define MCP25XXFD_CAN_FILOBJ_EID_BITS 18 +# define MCP25XXFD_CAN_FILOBJ_EID_SHIFT 12 +# define MCP25XXFD_CAN_FILOBJ_EID_MASK \ + GENMASK(MCP25XXFD_CAN_FILOBJ_EID_SHIFT + \ + MCP25XXFD_CAN_FILOBJ_EID_BITS - 1, \ + MCP25XXFD_CAN_FILOBJ_EID_SHIFT) +# define MCP25XXFD_CAN_FILOBJ_SID11 BIT(29) +# define MCP25XXFD_CAN_FILOBJ_EXIDE BIT(30) +#define MCP25XXFD_CAN_FLTMASK(x) \ + MCP25XXFD_CAN_SFR_BASE(0x1F4 + 8 * (x)) +# define MCP25XXFD_CAN_FILMASK_MSID_BITS 11 +# define MCP25XXFD_CAN_FILMASK_MSID_SHIFT 0 +# define MCP25XXFD_CAN_FILMASK_MSID_MASK \ + GENMASK(MCP25XXFD_CAN_FILMASK_MSID_SHIFT + \ + MCP25XXFD_CAN_FILMASK_MSID_BITS - 1, \ + MCP25XXFD_CAN_FILMASK_MSID_SHIFT) +# define MCP25XXFD_CAN_FILMASK_MEID_BITS 18 +# define MCP25XXFD_CAN_FILMASK_MEID_SHIFT 12 +# define MCP25XXFD_CAN_FILMASK_MEID_MASK \ + GENMASK(MCP25XXFD_CAN_FILMASK_MEID_SHIFT + \ + MCP25XXFD_CAN_FILMASK_MEID_BITS - 1, \ + MCP25XXFD_CAN_FILMASK_MEID_SHIFT) +# define MCP25XXFD_CAN_FILMASK_MSID11 BIT(29) +# define MCP25XXFD_CAN_FILMASK_MIDE BIT(30) + +/* the FIFO Objects in SRAM */ +#define MCP25XXFD_SRAM_SIZE 2048 +#define MCP25XXFD_SRAM_ADDR(x) (0x400 + (x)) + +/* memory structure in sram for tx fifos */ +struct mcp25xxfd_can_obj_tx { + u32 id; + u32 flags; + u8 data[]; +}; + +/* memory structure in sram for rx fifos */ +struct mcp25xxfd_can_obj_rx { + u32 id; + u32 flags; + u32 ts; + u8 data[]; +}; + +/* memory structure in sram for tef fifos */ +struct mcp25xxfd_can_obj_tef { + u32 id; + u32 flags; + u32 ts; +}; + +#define MCP25XXFD_CAN_OBJ_ID_SID_BITS 11 +#define MCP25XXFD_CAN_OBJ_ID_SID_SHIFT 0 +#define MCP25XXFD_CAN_OBJ_ID_SID_MASK \ + GENMASK(MCP25XXFD_CAN_OBJ_ID_SID_SHIFT + \ + MCP25XXFD_CAN_OBJ_ID_SID_BITS - 1, \ + MCP25XXFD_CAN_OBJ_ID_SID_SHIFT) +#define MCP25XXFD_CAN_OBJ_ID_EID_BITS 18 +#define MCP25XXFD_CAN_OBJ_ID_EID_SHIFT 11 +#define MCP25XXFD_CAN_OBJ_ID_EID_MASK \ + GENMASK(MCP25XXFD_CAN_OBJ_ID_EID_SHIFT + \ + MCP25XXFD_CAN_OBJ_ID_EID_BITS - 1, \ + MCP25XXFD_CAN_OBJ_ID_EID_SHIFT) +#define MCP25XXFD_CAN_OBJ_ID_SID_BIT11 BIT(29) + +#define MCP25XXFD_CAN_OBJ_FLAGS_DLC_BITS 4 +#define MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT 0 +#define MCP25XXFD_CAN_OBJ_FLAGS_DLC_MASK \ + GENMASK(MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT + \ + MCP25XXFD_CAN_OBJ_FLAGS_DLC_BITS - 1, \ + MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT) +#define MCP25XXFD_CAN_OBJ_FLAGS_IDE BIT(4) +#define MCP25XXFD_CAN_OBJ_FLAGS_RTR BIT(5) +#define MCP25XXFD_CAN_OBJ_FLAGS_BRS BIT(6) +#define MCP25XXFD_CAN_OBJ_FLAGS_FDF BIT(7) +#define MCP25XXFD_CAN_OBJ_FLAGS_ESI BIT(8) +#define MCP25XXFD_CAN_OBJ_FLAGS_SEQ_BITS 7 +#define MCP25XXFD_CAN_OBJ_FLAGS_SEQ_SHIFT 9 +#define MCP25XXFD_CAN_OBJ_FLAGS_SEQ_MASK \ + GENMASK(MCP25XXFD_CAN_OBJ_FLAGS_SEQ_SHIFT + \ + MCP25XXFD_CAN_OBJ_FLAGS_SEQ_BITS - 1, \ + MCP25XXFD_CAN_OBJ_FLAGS_SEQ_SHIFT) +#define MCP25XXFD_CAN_OBJ_FLAGS_FILHIT_BITS 11 +#define MCP25XXFD_CAN_OBJ_FLAGS_FILHIT_SHIFT 5 +#define MCP25XXFD_CAN_OBJ_FLAGS_FILHIT_MASK \ + GENMASK(MCP25XXFD_CAN_FLAGS_FILHIT_SHIFT + \ + MCP25XXFD_CAN_FLAGS_FILHIT_BITS - 1, \ + MCP25XXFD_CAN_FLAGS_FILHIT_SHIFT) + +/* custom status error */ +#define MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_SERR_RX BIT(0) +#define MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_SERR_TX BIT(1) +#define MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_ECC BIT(2) + +#endif /* __MCP25XXFD_REGS_H */ diff --git a/drivers/net/wireless/admtek/adm8211.c b/drivers/net/wireless/admtek/adm8211.c index 22f9f2f8af10..5cf2045fadef 100644 --- a/drivers/net/wireless/admtek/adm8211.c +++ b/drivers/net/wireless/admtek/adm8211.c @@ -324,8 +324,8 @@ static void adm8211_interrupt_tci(struct ieee80211_hw *dev) /* TODO: check TDES0_STATUS_TUF and TDES0_STATUS_TRO */ - pci_unmap_single(priv->pdev, info->mapping, - info->skb->len, PCI_DMA_TODEVICE); + dma_unmap_single(&priv->pdev->dev, info->mapping, + info->skb->len, DMA_TO_DEVICE); ieee80211_tx_info_clear_status(txi); @@ -382,35 +382,34 @@ static void adm8211_interrupt_rci(struct ieee80211_hw *dev) } else if (pktlen < RX_COPY_BREAK) { skb = dev_alloc_skb(pktlen); if (skb) { - pci_dma_sync_single_for_cpu( - priv->pdev, - priv->rx_buffers[entry].mapping, - pktlen, PCI_DMA_FROMDEVICE); + dma_sync_single_for_cpu(&priv->pdev->dev, + priv->rx_buffers[entry].mapping, + pktlen, + DMA_FROM_DEVICE); skb_put_data(skb, skb_tail_pointer(priv->rx_buffers[entry].skb), pktlen); - pci_dma_sync_single_for_device( - priv->pdev, - priv->rx_buffers[entry].mapping, - RX_PKT_SIZE, PCI_DMA_FROMDEVICE); + dma_sync_single_for_device(&priv->pdev->dev, + priv->rx_buffers[entry].mapping, + RX_PKT_SIZE, + DMA_FROM_DEVICE); } } else { newskb = dev_alloc_skb(RX_PKT_SIZE); if (newskb) { skb = priv->rx_buffers[entry].skb; skb_put(skb, pktlen); - pci_unmap_single( - priv->pdev, - priv->rx_buffers[entry].mapping, - RX_PKT_SIZE, PCI_DMA_FROMDEVICE); + dma_unmap_single(&priv->pdev->dev, + priv->rx_buffers[entry].mapping, + RX_PKT_SIZE, DMA_FROM_DEVICE); priv->rx_buffers[entry].skb = newskb; priv->rx_buffers[entry].mapping = - pci_map_single(priv->pdev, + dma_map_single(&priv->pdev->dev, skb_tail_pointer(newskb), RX_PKT_SIZE, - PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(priv->pdev, - priv->rx_buffers[entry].mapping)) { + DMA_FROM_DEVICE); + if (dma_mapping_error(&priv->pdev->dev, + priv->rx_buffers[entry].mapping)) { priv->rx_buffers[entry].skb = NULL; dev_kfree_skb(newskb); skb = NULL; @@ -1449,11 +1448,11 @@ static int adm8211_init_rings(struct ieee80211_hw *dev) rx_info->skb = dev_alloc_skb(RX_PKT_SIZE); if (rx_info->skb == NULL) break; - rx_info->mapping = pci_map_single(priv->pdev, + rx_info->mapping = dma_map_single(&priv->pdev->dev, skb_tail_pointer(rx_info->skb), RX_PKT_SIZE, - PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(priv->pdev, rx_info->mapping)) { + DMA_FROM_DEVICE); + if (dma_mapping_error(&priv->pdev->dev, rx_info->mapping)) { dev_kfree_skb(rx_info->skb); rx_info->skb = NULL; break; @@ -1490,10 +1489,9 @@ static void adm8211_free_rings(struct ieee80211_hw *dev) if (!priv->rx_buffers[i].skb) continue; - pci_unmap_single( - priv->pdev, - priv->rx_buffers[i].mapping, - RX_PKT_SIZE, PCI_DMA_FROMDEVICE); + dma_unmap_single(&priv->pdev->dev, + priv->rx_buffers[i].mapping, RX_PKT_SIZE, + DMA_FROM_DEVICE); dev_kfree_skb(priv->rx_buffers[i].skb); } @@ -1502,10 +1500,9 @@ static void adm8211_free_rings(struct ieee80211_hw *dev) if (!priv->tx_buffers[i].skb) continue; - pci_unmap_single(priv->pdev, + dma_unmap_single(&priv->pdev->dev, priv->tx_buffers[i].mapping, - priv->tx_buffers[i].skb->len, - PCI_DMA_TODEVICE); + priv->tx_buffers[i].skb->len, DMA_TO_DEVICE); dev_kfree_skb(priv->tx_buffers[i].skb); } @@ -1632,9 +1629,9 @@ static int adm8211_tx_raw(struct ieee80211_hw *dev, struct sk_buff *skb, unsigned int entry; u32 flag; - mapping = pci_map_single(priv->pdev, skb->data, skb->len, - PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(priv->pdev, mapping)) + mapping = dma_map_single(&priv->pdev->dev, skb->data, skb->len, + DMA_TO_DEVICE); + if (dma_mapping_error(&priv->pdev->dev, mapping)) return -ENOMEM; spin_lock_irqsave(&priv->lock, flags); @@ -1745,8 +1742,8 @@ static int adm8211_alloc_rings(struct ieee80211_hw *dev) /* Allocate TX/RX descriptors */ ring_size = sizeof(struct adm8211_desc) * priv->rx_ring_size + sizeof(struct adm8211_desc) * priv->tx_ring_size; - priv->rx_ring = pci_alloc_consistent(priv->pdev, ring_size, - &priv->rx_ring_dma); + priv->rx_ring = dma_alloc_coherent(&priv->pdev->dev, ring_size, + &priv->rx_ring_dma, GFP_KERNEL); if (!priv->rx_ring) { kfree(priv->rx_buffers); @@ -1818,8 +1815,8 @@ static int adm8211_probe(struct pci_dev *pdev, return err; /* someone else grabbed it? don't disable it */ } - if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) || - pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) { + if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)) || + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32))) { printk(KERN_ERR "%s (adm8211): No suitable DMA available\n", pci_name(pdev)); goto err_free_reg; @@ -1929,10 +1926,10 @@ static int adm8211_probe(struct pci_dev *pdev, kfree(priv->eeprom); err_free_desc: - pci_free_consistent(pdev, - sizeof(struct adm8211_desc) * priv->rx_ring_size + - sizeof(struct adm8211_desc) * priv->tx_ring_size, - priv->rx_ring, priv->rx_ring_dma); + dma_free_coherent(&pdev->dev, + sizeof(struct adm8211_desc) * priv->rx_ring_size + + sizeof(struct adm8211_desc) * priv->tx_ring_size, + priv->rx_ring, priv->rx_ring_dma); kfree(priv->rx_buffers); err_iounmap: @@ -1962,10 +1959,10 @@ static void adm8211_remove(struct pci_dev *pdev) priv = dev->priv; - pci_free_consistent(pdev, - sizeof(struct adm8211_desc) * priv->rx_ring_size + - sizeof(struct adm8211_desc) * priv->tx_ring_size, - priv->rx_ring, priv->rx_ring_dma); + dma_free_coherent(&pdev->dev, + sizeof(struct adm8211_desc) * priv->rx_ring_size + + sizeof(struct adm8211_desc) * priv->tx_ring_size, + priv->rx_ring, priv->rx_ring_dma); kfree(priv->rx_buffers); kfree(priv->eeprom); diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index 5b6db6e66f65..8b9d537c8900 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -12,17 +12,9 @@ void ath10k_bmi_start(struct ath10k *ar) { - int ret; - ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi start\n"); ar->bmi.done_sent = false; - - /* Enable hardware clock to speed up firmware download */ - if (ar->hw_params.hw_ops->enable_pll_clk) { - ret = ar->hw_params.hw_ops->enable_pll_clk(ar); - ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi enable pll ret %d\n", ret); - } } int ath10k_bmi_done(struct ath10k *ar) diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index 294fbc1e89ab..7be95c4a441f 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -481,6 +481,15 @@ static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar, ath10k_ce_write32(ar, ce_ctrl_addr + wm_regs->addr, mask); } +static inline bool ath10k_ce_engine_int_status_check(struct ath10k *ar, + u32 ce_ctrl_addr, + unsigned int mask) +{ + struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; + + return ath10k_ce_read32(ar, ce_ctrl_addr + wm_regs->addr) & mask; +} + /* * Guts of ath10k_ce_send. * The caller takes responsibility for any needed locking. @@ -1301,19 +1310,22 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) spin_lock_bh(&ce->ce_lock); - /* Clear the copy-complete interrupts that will be handled here. */ - ath10k_ce_engine_int_status_clear(ar, ctrl_addr, - wm_regs->cc_mask); + if (ath10k_ce_engine_int_status_check(ar, ctrl_addr, + wm_regs->cc_mask)) { + /* Clear before handling */ + ath10k_ce_engine_int_status_clear(ar, ctrl_addr, + wm_regs->cc_mask); - spin_unlock_bh(&ce->ce_lock); + spin_unlock_bh(&ce->ce_lock); - if (ce_state->recv_cb) - ce_state->recv_cb(ce_state); + if (ce_state->recv_cb) + ce_state->recv_cb(ce_state); - if (ce_state->send_cb) - ce_state->send_cb(ce_state); + if (ce_state->send_cb) + ce_state->send_cb(ce_state); - spin_lock_bh(&ce->ce_lock); + spin_lock_bh(&ce->ce_lock); + } /* * Misc CE interrupts are not being handled, but still need @@ -1555,7 +1567,7 @@ ath10k_ce_alloc_src_ring(struct ath10k *ar, unsigned int ce_id, ret = ath10k_ce_alloc_shadow_base(ar, src_ring, nentries); if (ret) { dma_free_coherent(ar->dev, - (nentries * sizeof(struct ce_desc_64) + + (nentries * sizeof(struct ce_desc) + CE_DESC_RING_ALIGN), src_ring->base_addr_owner_space_unaligned, base_addr); diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 340ce327ac14..222cd51b5c2d 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -334,6 +334,7 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .hw_filter_reset_required = true, .fw_diag_ce_download = true, .tx_stats_over_pktlog = false, + .supports_peer_stats_info = true, }, { .id = QCA99X0_HW_2_0_DEV_VERSION, @@ -2320,7 +2321,7 @@ static void ath10k_core_restart(struct work_struct *work) break; case ATH10K_STATE_RESTARTED: ar->state = ATH10K_STATE_WEDGED; - /* fall through */ + fallthrough; case ATH10K_STATE_WEDGED: ath10k_warn(ar, "device is wedged, will not restart\n"); break; @@ -2614,6 +2615,13 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode, ar->running_fw->fw_file.fw_features)) { ath10k_bmi_start(ar); + /* Enable hardware clock to speed up firmware download */ + if (ar->hw_params.hw_ops->enable_pll_clk) { + status = ar->hw_params.hw_ops->enable_pll_clk(ar); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot enable pll ret %d\n", + status); + } + if (ath10k_init_configure_target(ar)) { status = -EINVAL; goto err; diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index d787cbead56a..5c1af2021883 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -142,6 +142,14 @@ static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num) BUILD_BUG_ON(HTT_RX_RING_FILL_LEVEL >= HTT_RX_RING_SIZE / 2); idx = __le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr); + + if (idx < 0 || idx >= htt->rx_ring.size) { + ath10k_err(htt->ar, "rx ring index is not valid, firmware malfunctioning?\n"); + idx &= htt->rx_ring.size_mask; + ret = -ENOMEM; + goto fail; + } + while (num > 0) { skb = dev_alloc_skb(HTT_RX_BUF_SIZE + HTT_RX_DESC_ALIGN); if (!skb) { @@ -941,6 +949,7 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, u8 preamble = 0; u8 group_id; u32 info1, info2, info3; + u32 stbc, nsts_su; info1 = __le32_to_cpu(rxd->ppdu_start.info1); info2 = __le32_to_cpu(rxd->ppdu_start.info2); @@ -985,11 +994,16 @@ static void ath10k_htt_rx_h_rates(struct ath10k *ar, */ bw = info2 & 3; sgi = info3 & 1; + stbc = (info2 >> 3) & 1; group_id = (info2 >> 4) & 0x3F; if (GROUP_ID_IS_SU_MIMO(group_id)) { mcs = (info3 >> 4) & 0x0F; - nss = ((info2 >> 10) & 0x07) + 1; + nsts_su = ((info2 >> 10) & 0x07); + if (stbc) + nss = (nsts_su >> 2) + 1; + else + nss = (nsts_su + 1); } else { /* Hardware doesn't decode VHT-SIG-B into Rx descriptor * so it's impossible to decode MCS. Also since @@ -3017,7 +3031,7 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb) ath10k_htt_rx_h_enqueue(ar, &amsdu, status); break; case -EAGAIN: - /* fall through */ + fallthrough; default: /* Should not happen. */ ath10k_warn(ar, "failed to extract amsdu: %d\n", ret); @@ -3575,12 +3589,14 @@ ath10k_update_per_peer_tx_stats(struct ath10k *ar, } if (ar->htt.disable_tx_comp) { - arsta->tx_retries += peer_stats->retry_pkts; arsta->tx_failed += peer_stats->failed_pkts; - ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx retries %d tx failed %d\n", - arsta->tx_retries, arsta->tx_failed); + ath10k_dbg(ar, ATH10K_DBG_HTT, "tx failed %d\n", + arsta->tx_failed); } + arsta->tx_retries += peer_stats->retry_pkts; + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx retries %d", arsta->tx_retries); + if (ath10k_debug_is_extd_tx_stats_enabled(ar)) ath10k_accumulate_per_peer_tx_stats(ar, arsta, peer_stats, rate_idx); diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index bbe869575855..1fc0a312ab58 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -1314,7 +1314,7 @@ static int ath10k_htt_tx_hl(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txm case ATH10K_HW_TXRX_RAW: case ATH10K_HW_TXRX_NATIVE_WIFI: flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT; - /* fall through */ + fallthrough; case ATH10K_HW_TXRX_ETHERNET: flags0 |= SM(txmode, HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); break; @@ -1460,7 +1460,7 @@ static int ath10k_htt_tx_32(struct ath10k_htt *htt, case ATH10K_HW_TXRX_RAW: case ATH10K_HW_TXRX_NATIVE_WIFI: flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT; - /* fall through */ + fallthrough; case ATH10K_HW_TXRX_ETHERNET: if (ar->hw_params.continuous_frag_desc) { ext_desc_t = htt->frag_desc.vaddr_desc_32; @@ -1662,7 +1662,7 @@ static int ath10k_htt_tx_64(struct ath10k_htt *htt, case ATH10K_HW_TXRX_RAW: case ATH10K_HW_TXRX_NATIVE_WIFI: flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT; - /* fall through */ + fallthrough; case ATH10K_HW_TXRX_ETHERNET: if (ar->hw_params.continuous_frag_desc) { ext_desc_t = htt->frag_desc.vaddr_desc_64; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 3c0c33a9f30c..82b4bf5ba612 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2468,17 +2468,17 @@ ath10k_peer_assoc_h_vht_limit(u16 tx_mcs_set, idx_limit = -1; switch (idx_limit) { - case 0: /* fall through */ - case 1: /* fall through */ - case 2: /* fall through */ - case 3: /* fall through */ - case 4: /* fall through */ - case 5: /* fall through */ - case 6: /* fall through */ + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: default: /* see ath10k_mac_can_set_bitrate_mask() */ WARN_ON(1); - /* fall through */ + fallthrough; case -1: mcs = IEEE80211_VHT_MCS_NOT_SUPPORTED; break; @@ -4238,7 +4238,7 @@ void __ath10k_scan_finish(struct ath10k *ar) } else if (ar->scan.roc_notify) { ieee80211_remain_on_channel_expired(ar->hw); } - /* fall through */ + fallthrough; case ATH10K_SCAN_STARTING: ar->scan.state = ATH10K_SCAN_IDLE; ar->scan_channel = NULL; @@ -7278,7 +7278,7 @@ ath10k_mac_update_bss_chan_survey(struct ath10k *ar, struct ieee80211_channel *channel) { int ret; - enum wmi_bss_survey_req_type type = WMI_BSS_SURVEY_REQ_TYPE_READ_CLEAR; + enum wmi_bss_survey_req_type type = WMI_BSS_SURVEY_REQ_TYPE_READ; lockdep_assert_held(&ar->conf_mutex); @@ -8363,19 +8363,32 @@ static void ath10k_mac_get_rate_flags_ht(struct ath10k *ar, u32 rate, u8 nss, u8 u8 *flags, u8 *bw) { struct ath10k_index_ht_data_rate_type *mcs_rate; + u8 index; + size_t len_nss1 = ARRAY_SIZE(supported_ht_mcs_rate_nss1); + size_t len_nss2 = ARRAY_SIZE(supported_ht_mcs_rate_nss2); + + if (mcs >= (len_nss1 + len_nss2)) { + ath10k_warn(ar, "not supported mcs %d in current rate table", mcs); + return; + } mcs_rate = (struct ath10k_index_ht_data_rate_type *) ((nss == 1) ? &supported_ht_mcs_rate_nss1 : &supported_ht_mcs_rate_nss2); - if (rate == mcs_rate[mcs].supported_rate[0]) { + if (mcs >= len_nss1) + index = mcs - len_nss1; + else + index = mcs; + + if (rate == mcs_rate[index].supported_rate[0]) { *bw = RATE_INFO_BW_20; - } else if (rate == mcs_rate[mcs].supported_rate[1]) { + } else if (rate == mcs_rate[index].supported_rate[1]) { *bw |= RATE_INFO_BW_40; - } else if (rate == mcs_rate[mcs].supported_rate[2]) { + } else if (rate == mcs_rate[index].supported_rate[2]) { *bw |= RATE_INFO_BW_20; *flags |= RATE_INFO_FLAGS_SHORT_GI; - } else if (rate == mcs_rate[mcs].supported_rate[3]) { + } else if (rate == mcs_rate[index].supported_rate[3]) { *bw |= RATE_INFO_BW_40; *flags |= RATE_INFO_FLAGS_SHORT_GI; } else { @@ -8436,6 +8449,9 @@ static void ath10k_mac_parse_bitrate(struct ath10k *ar, u32 rate_code, u8 mcs = WMI_TLV_GET_HW_RC_RATE_V1(rate_code); u8 flags = 0, bw = 0; + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac parse rate code 0x%x bitrate %d kbps\n", + rate_code, bitrate_kbps); + if (preamble == WMI_RATE_PREAMBLE_HT) mode = ATH10K_PHY_MODE_HT; else if (preamble == WMI_RATE_PREAMBLE_VHT) @@ -8528,26 +8544,26 @@ static void ath10k_sta_statistics(struct ieee80211_hw *hw, sinfo->rx_duration = arsta->rx_duration; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION); - if (!arsta->txrate.legacy && !arsta->txrate.nss) - return; - - if (arsta->txrate.legacy) { - sinfo->txrate.legacy = arsta->txrate.legacy; - } else { - sinfo->txrate.mcs = arsta->txrate.mcs; - sinfo->txrate.nss = arsta->txrate.nss; - sinfo->txrate.bw = arsta->txrate.bw; + if (arsta->txrate.legacy || arsta->txrate.nss) { + if (arsta->txrate.legacy) { + sinfo->txrate.legacy = arsta->txrate.legacy; + } else { + sinfo->txrate.mcs = arsta->txrate.mcs; + sinfo->txrate.nss = arsta->txrate.nss; + sinfo->txrate.bw = arsta->txrate.bw; + } + sinfo->txrate.flags = arsta->txrate.flags; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); } - sinfo->txrate.flags = arsta->txrate.flags; - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); if (ar->htt.disable_tx_comp) { - sinfo->tx_retries = arsta->tx_retries; - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES); sinfo->tx_failed = arsta->tx_failed; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED); } + sinfo->tx_retries = arsta->tx_retries; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES); + ath10k_mac_sta_get_peer_stats_info(ar, sta, sinfo); } diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index cfde7791291a..36426efdb2ea 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -2184,7 +2184,7 @@ err_req: if (ret == 0 && resp_len) { *resp_len = min(*resp_len, xfer.resp_len); - memcpy(resp, tresp, xfer.resp_len); + memcpy(resp, tresp, *resp_len); } err_dma: kfree(treq); diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 63f882c690bf..0841e69b10b1 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -557,6 +557,10 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar, le16_to_cpu(htc_hdr->len), ATH10K_HTC_MBOX_MAX_PAYLOAD_LENGTH); ret = -ENOMEM; + + queue_work(ar->workqueue, &ar->restart_work); + ath10k_warn(ar, "exceeds length, start recovery\n"); + goto err; } diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index 354d49b1cd45..645ed5f63ef8 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -1772,9 +1772,18 @@ static int ath10k_snoc_remove(struct platform_device *pdev) return 0; } +static void ath10k_snoc_shutdown(struct platform_device *pdev) +{ + struct ath10k *ar = platform_get_drvdata(pdev); + + ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc shutdown\n"); + ath10k_snoc_remove(pdev); +} + static struct platform_driver ath10k_snoc_driver = { .probe = ath10k_snoc_probe, .remove = ath10k_snoc_remove, + .shutdown = ath10k_snoc_shutdown, .driver = { .name = "ath10k_snoc", .of_match_table = ath10k_snoc_dt_match, diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h index e77b97ca5c7f..b39c9b78b32b 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h @@ -1614,6 +1614,8 @@ wmi_tlv_svc_map(const __le32 *in, unsigned long *out, size_t len) WMI_SERVICE_MESH_11S, len); SVCMAP(WMI_TLV_SERVICE_SYNC_DELETE_CMDS, WMI_SERVICE_SYNC_DELETE_CMDS, len); + SVCMAP(WMI_TLV_SERVICE_PEER_STATS_INFO, + WMI_SERVICE_PEER_STATS, len); } static inline void diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c index 8c26adddd034..7d65c115669f 100644 --- a/drivers/net/wireless/ath/ath10k/wow.c +++ b/drivers/net/wireless/ath/ath10k/wow.c @@ -275,7 +275,7 @@ static int ath10k_vif_wow_set_wakeups(struct ath10k_vif *arvif, switch (arvif->vdev_type) { case WMI_VDEV_TYPE_IBSS: __set_bit(WOW_BEACON_EVENT, &wow_mask); - /* fall through */ + fallthrough; case WMI_VDEV_TYPE_AP: __set_bit(WOW_DEAUTH_RECVD_EVENT, &wow_mask); __set_bit(WOW_DISASSOC_RECVD_EVENT, &wow_mask); diff --git a/drivers/net/wireless/ath/ath11k/Kconfig b/drivers/net/wireless/ath/ath11k/Kconfig index 88a97356f0a1..ad5cc6cac05b 100644 --- a/drivers/net/wireless/ath/ath11k/Kconfig +++ b/drivers/net/wireless/ath/ath11k/Kconfig @@ -2,9 +2,7 @@ config ATH11K tristate "Qualcomm Technologies 802.11ax chipset support" depends on MAC80211 && HAS_DMA - depends on REMOTEPROC depends on CRYPTO_MICHAEL_MIC - depends on ARCH_QCOM || COMPILE_TEST select ATH_COMMON select QCOM_QMI_HELPERS help @@ -13,6 +11,22 @@ config ATH11K If you choose to build a module, it'll be called ath11k. +config ATH11K_AHB + tristate "Atheros ath11k AHB support" + depends on ATH11K + depends on REMOTEPROC + help + This module adds support for AHB bus + +config ATH11K_PCI + tristate "Atheros ath11k PCI support" + depends on ATH11K && PCI + select MHI_BUS + select QRTR + select QRTR_MHI + help + This module adds support for PCIE bus + config ATH11K_DEBUG bool "QCA ath11k debugging" depends on ATH11K diff --git a/drivers/net/wireless/ath/ath11k/Makefile b/drivers/net/wireless/ath/ath11k/Makefile index 104186373c9e..bc4911f0339d 100644 --- a/drivers/net/wireless/ath/ath11k/Makefile +++ b/drivers/net/wireless/ath/ath11k/Makefile @@ -4,7 +4,6 @@ ath11k-y += core.o \ hal.o \ hal_tx.o \ hal_rx.o \ - ahb.o \ wmi.o \ mac.o \ reg.o \ @@ -16,7 +15,8 @@ ath11k-y += core.o \ debug.o \ ce.o \ peer.o \ - dbring.o + dbring.o \ + hw.o ath11k-$(CONFIG_ATH11K_DEBUGFS) += debug_htt_stats.o debugfs_sta.o ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o @@ -24,5 +24,11 @@ ath11k-$(CONFIG_ATH11K_TRACING) += trace.o ath11k-$(CONFIG_THERMAL) += thermal.o ath11k-$(CONFIG_ATH11K_SPECTRAL) += spectral.o +obj-$(CONFIG_ATH11K_AHB) += ath11k_ahb.o +ath11k_ahb-y += ahb.o + +obj-$(CONFIG_ATH11K_PCI) += ath11k_pci.o +ath11k_pci-y += mhi.o pci.o + # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index 30092841ac46..8466c62a83d6 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -25,6 +25,13 @@ static const struct of_device_id ath11k_ahb_of_match[] = { MODULE_DEVICE_TABLE(of, ath11k_ahb_of_match); +static const struct ath11k_bus_params ath11k_ahb_bus_params = { + .mhi_support = false, + .m3_fw_support = false, + .fixed_bdf_addr = true, + .fixed_mem_region = true, +}; + /* Target firmware's Copy Engine configuration. */ static const struct ce_pipe_config target_ce_config_wlan[] = { /* CE0: host->target HTC control and raw streams */ @@ -321,78 +328,6 @@ static const char *irq_name[ATH11K_IRQ_NUM_MAX] = { "tcl2host-status-ring", }; -#define ATH11K_TX_RING_MASK_0 0x1 -#define ATH11K_TX_RING_MASK_1 0x2 -#define ATH11K_TX_RING_MASK_2 0x4 - -#define ATH11K_RX_RING_MASK_0 0x1 -#define ATH11K_RX_RING_MASK_1 0x2 -#define ATH11K_RX_RING_MASK_2 0x4 -#define ATH11K_RX_RING_MASK_3 0x8 - -#define ATH11K_RX_ERR_RING_MASK_0 0x1 - -#define ATH11K_RX_WBM_REL_RING_MASK_0 0x1 - -#define ATH11K_REO_STATUS_RING_MASK_0 0x1 - -#define ATH11K_RXDMA2HOST_RING_MASK_0 0x1 -#define ATH11K_RXDMA2HOST_RING_MASK_1 0x2 -#define ATH11K_RXDMA2HOST_RING_MASK_2 0x4 - -#define ATH11K_HOST2RXDMA_RING_MASK_0 0x1 -#define ATH11K_HOST2RXDMA_RING_MASK_1 0x2 -#define ATH11K_HOST2RXDMA_RING_MASK_2 0x4 - -#define ATH11K_RX_MON_STATUS_RING_MASK_0 0x1 -#define ATH11K_RX_MON_STATUS_RING_MASK_1 0x2 -#define ATH11K_RX_MON_STATUS_RING_MASK_2 0x4 - -const u8 ath11k_tx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { - ATH11K_TX_RING_MASK_0, - ATH11K_TX_RING_MASK_1, - ATH11K_TX_RING_MASK_2, -}; - -const u8 rx_mon_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { - 0, 0, 0, 0, - ATH11K_RX_MON_STATUS_RING_MASK_0, - ATH11K_RX_MON_STATUS_RING_MASK_1, - ATH11K_RX_MON_STATUS_RING_MASK_2, -}; - -const u8 ath11k_rx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { - 0, 0, 0, 0, 0, 0, 0, - ATH11K_RX_RING_MASK_0, - ATH11K_RX_RING_MASK_1, - ATH11K_RX_RING_MASK_2, - ATH11K_RX_RING_MASK_3, -}; - -const u8 ath11k_rx_err_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { - ATH11K_RX_ERR_RING_MASK_0, -}; - -const u8 ath11k_rx_wbm_rel_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { - ATH11K_RX_WBM_REL_RING_MASK_0, -}; - -const u8 ath11k_reo_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { - ATH11K_REO_STATUS_RING_MASK_0, -}; - -const u8 ath11k_rxdma2host_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { - ATH11K_RXDMA2HOST_RING_MASK_0, - ATH11K_RXDMA2HOST_RING_MASK_1, - ATH11K_RXDMA2HOST_RING_MASK_2, -}; - -const u8 ath11k_host2rxdma_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX] = { - ATH11K_HOST2RXDMA_RING_MASK_0, - ATH11K_HOST2RXDMA_RING_MASK_1, - ATH11K_HOST2RXDMA_RING_MASK_2, -}; - /* enum ext_irq_num - irq numbers that can be used by external modules * like datapath */ @@ -449,10 +384,10 @@ static void ath11k_ahb_kill_tasklets(struct ath11k_base *ab) { int i; - for (i = 0; i < CE_COUNT; i++) { + for (i = 0; i < ab->hw_params.ce_count; i++) { struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; - if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) continue; tasklet_kill(&ce_pipe->intr_tq); @@ -540,8 +475,8 @@ static void ath11k_ahb_sync_ce_irqs(struct ath11k_base *ab) int i; int irq_idx; - for (i = 0; i < CE_COUNT; i++) { - if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + for (i = 0; i < ab->hw_params.ce_count; i++) { + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) continue; irq_idx = ATH11K_IRQ_CE0_OFFSET + i; @@ -568,8 +503,8 @@ static void ath11k_ahb_ce_irqs_enable(struct ath11k_base *ab) { int i; - for (i = 0; i < CE_COUNT; i++) { - if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + for (i = 0; i < ab->hw_params.ce_count; i++) { + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) continue; ath11k_ahb_ce_irq_enable(ab, i); } @@ -579,8 +514,8 @@ static void ath11k_ahb_ce_irqs_disable(struct ath11k_base *ab) { int i; - for (i = 0; i < CE_COUNT; i++) { - if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + for (i = 0; i < ab->hw_params.ce_count; i++) { + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) continue; ath11k_ahb_ce_irq_disable(ab, i); } @@ -646,6 +581,7 @@ static void ath11k_ahb_init_qmi_ce_config(struct ath11k_base *ab) cfg->tgt_ce = target_ce_config_wlan; cfg->svc_to_ce_map_len = ARRAY_SIZE(target_service_to_ce_map_wlan); cfg->svc_to_ce_map = target_service_to_ce_map_wlan; + ab->qmi.service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ8074; } static void ath11k_ahb_free_ext_irq(struct ath11k_base *ab) @@ -665,8 +601,8 @@ static void ath11k_ahb_free_irq(struct ath11k_base *ab) int irq_idx; int i; - for (i = 0; i < CE_COUNT; i++) { - if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + for (i = 0; i < ab->hw_params.ce_count; i++) { + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) continue; irq_idx = ATH11K_IRQ_CE0_OFFSET + i; free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]); @@ -734,6 +670,7 @@ static irqreturn_t ath11k_ahb_ext_interrupt_handler(int irq, void *arg) static int ath11k_ahb_ext_irq_config(struct ath11k_base *ab) { + struct ath11k_hw_params *hw = &ab->hw_params; int i, j; int irq; int ret; @@ -749,45 +686,45 @@ static int ath11k_ahb_ext_irq_config(struct ath11k_base *ab) ath11k_ahb_ext_grp_napi_poll, NAPI_POLL_WEIGHT); for (j = 0; j < ATH11K_EXT_IRQ_NUM_MAX; j++) { - if (ath11k_tx_ring_mask[i] & BIT(j)) { + if (ab->hw_params.ring_mask->tx[i] & BIT(j)) { irq_grp->irqs[num_irq++] = wbm2host_tx_completions_ring1 - j; } - if (ath11k_rx_ring_mask[i] & BIT(j)) { + if (ab->hw_params.ring_mask->rx[i] & BIT(j)) { irq_grp->irqs[num_irq++] = reo2host_destination_ring1 - j; } - if (ath11k_rx_err_ring_mask[i] & BIT(j)) + if (ab->hw_params.ring_mask->rx_err[i] & BIT(j)) irq_grp->irqs[num_irq++] = reo2host_exception; - if (ath11k_rx_wbm_rel_ring_mask[i] & BIT(j)) + if (ab->hw_params.ring_mask->rx_wbm_rel[i] & BIT(j)) irq_grp->irqs[num_irq++] = wbm2host_rx_release; - if (ath11k_reo_status_ring_mask[i] & BIT(j)) + if (ab->hw_params.ring_mask->reo_status[i] & BIT(j)) irq_grp->irqs[num_irq++] = reo2host_status; - if (j < MAX_RADIOS) { - if (ath11k_rxdma2host_ring_mask[i] & BIT(j)) { + if (j < ab->hw_params.max_radios) { + if (ab->hw_params.ring_mask->rxdma2host[i] & BIT(j)) { irq_grp->irqs[num_irq++] = - rxdma2host_destination_ring_mac1 - - ath11k_core_get_hw_mac_id(ab, j); + rxdma2host_destination_ring_mac1 - + ath11k_hw_get_mac_from_pdev_id(hw, j); } - if (ath11k_host2rxdma_ring_mask[i] & BIT(j)) { + if (ab->hw_params.ring_mask->host2rxdma[i] & BIT(j)) { irq_grp->irqs[num_irq++] = - host2rxdma_host_buf_ring_mac1 - - ath11k_core_get_hw_mac_id(ab, j); + host2rxdma_host_buf_ring_mac1 - + ath11k_hw_get_mac_from_pdev_id(hw, j); } - if (rx_mon_status_ring_mask[i] & BIT(j)) { + if (ab->hw_params.ring_mask->rx_mon_status[i] & BIT(j)) { irq_grp->irqs[num_irq++] = ppdu_end_interrupts_mac1 - - ath11k_core_get_hw_mac_id(ab, j); + ath11k_hw_get_mac_from_pdev_id(hw, j); irq_grp->irqs[num_irq++] = rxdma2host_monitor_status_ring_mac1 - - ath11k_core_get_hw_mac_id(ab, j); + ath11k_hw_get_mac_from_pdev_id(hw, j); } } } @@ -819,10 +756,10 @@ static int ath11k_ahb_config_irq(struct ath11k_base *ab) int ret; /* Configure CE irqs */ - for (i = 0; i < CE_COUNT; i++) { + for (i = 0; i < ab->hw_params.ce_count; i++) { struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; - if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) continue; irq_idx = ATH11K_IRQ_CE0_OFFSET + i; @@ -926,7 +863,7 @@ static int ath11k_ahb_probe(struct platform_device *pdev) return ret; } - ab = ath11k_core_alloc(&pdev->dev, 0, ATH11K_BUS_AHB); + ab = ath11k_core_alloc(&pdev->dev, 0, ATH11K_BUS_AHB, &ath11k_ahb_bus_params); if (!ab) { dev_err(&pdev->dev, "failed to allocate ath11k base\n"); return -ENOMEM; @@ -939,6 +876,10 @@ static int ath11k_ahb_probe(struct platform_device *pdev) ab->mem_len = resource_size(mem_res); platform_set_drvdata(pdev, ab); + ret = ath11k_core_pre_init(ab); + if (ret) + goto err_core_free; + ret = ath11k_hal_srng_init(ab); if (ret) goto err_core_free; @@ -951,15 +892,15 @@ static int ath11k_ahb_probe(struct platform_device *pdev) ath11k_ahb_init_qmi_ce_config(ab); - ret = ath11k_ahb_config_irq(ab); + ret = ath11k_core_init(ab); if (ret) { - ath11k_err(ab, "failed to configure irq: %d\n", ret); + ath11k_err(ab, "failed to init core: %d\n", ret); goto err_ce_free; } - ret = ath11k_core_init(ab); + ret = ath11k_ahb_config_irq(ab); if (ret) { - ath11k_err(ab, "failed to init core: %d\n", ret); + ath11k_err(ab, "failed to configure irq: %d\n", ret); goto err_ce_free; } @@ -1023,5 +964,5 @@ static void ath11k_ahb_exit(void) } module_exit(ath11k_ahb_exit); -MODULE_DESCRIPTION("Driver support for Qualcomm Technologies 802.11ax wireless chip"); +MODULE_DESCRIPTION("Driver support for Qualcomm Technologies 802.11ax WLAN AHB devices"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c index cdd40c8fc867..b2da1f937478 100644 --- a/drivers/net/wireless/ath/ath11k/ce.c +++ b/drivers/net/wireless/ath/ath11k/ce.c @@ -5,8 +5,9 @@ #include "dp_rx.h" #include "debug.h" +#include "hif.h" -static const struct ce_attr host_ce_config_wlan[] = { +const struct ce_attr ath11k_host_ce_config_ipq8074[] = { /* CE0: host->target HTC control and raw streams */ { .flags = CE_ATTR_FLAGS, @@ -108,6 +109,84 @@ static const struct ce_attr host_ce_config_wlan[] = { }, }; +const struct ce_attr ath11k_host_ce_config_qca6390[] = { + /* CE0: host->target HTC control and raw streams */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 16, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE1: target->host HTT + HTC control */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath11k_htc_rx_completion_handler, + }, + + /* CE2: target->host WMI */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath11k_htc_rx_completion_handler, + }, + + /* CE3: host->target WMI (mac0) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE4: host->target HTT */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 2048, + .src_sz_max = 256, + .dest_nentries = 0, + }, + + /* CE5: target->host pktlog */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath11k_dp_htt_htc_t2h_msg_handler, + }, + + /* CE6: target autonomous hif_memcpy */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + + /* CE7: host->target WMI (mac1) */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE8: target autonomous hif_memcpy */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + +}; + static int ath11k_ce_rx_buf_enqueue_pipe(struct ath11k_ce_pipe *pipe, struct sk_buff *skb, dma_addr_t paddr) { @@ -352,6 +431,31 @@ static void ath11k_ce_send_done_cb(struct ath11k_ce_pipe *pipe) } } +static void ath11k_ce_srng_msi_ring_params_setup(struct ath11k_base *ab, u32 ce_id, + struct hal_srng_params *ring_params) +{ + u32 msi_data_start; + u32 msi_data_count; + u32 msi_irq_start; + u32 addr_lo; + u32 addr_hi; + int ret; + + ret = ath11k_get_user_msi_vector(ab, "CE", + &msi_data_count, &msi_data_start, + &msi_irq_start); + + if (ret) + return; + + ath11k_get_msi_address(ab, &addr_lo, &addr_hi); + + ring_params->msi_addr = addr_lo; + ring_params->msi_addr |= (dma_addr_t)(((uint64_t)addr_hi) << 32); + ring_params->msi_data = (ce_id % msi_data_count) + msi_data_start; + ring_params->flags |= HAL_SRNG_FLAGS_MSI_INTR; +} + static int ath11k_ce_init_ring(struct ath11k_base *ab, struct ath11k_ce_ring *ce_ring, int ce_id, enum hal_ring_type type) @@ -363,21 +467,24 @@ static int ath11k_ce_init_ring(struct ath11k_base *ab, params.ring_base_vaddr = ce_ring->base_addr_owner_space; params.num_entries = ce_ring->nentries; + if (!(CE_ATTR_DIS_INTR & ab->hw_params.host_ce_config[ce_id].flags)) + ath11k_ce_srng_msi_ring_params_setup(ab, ce_id, ¶ms); + switch (type) { case HAL_CE_SRC: - if (!(CE_ATTR_DIS_INTR & host_ce_config_wlan[ce_id].flags)) + if (!(CE_ATTR_DIS_INTR & ab->hw_params.host_ce_config[ce_id].flags)) params.intr_batch_cntr_thres_entries = 1; break; case HAL_CE_DST: - params.max_buffer_len = host_ce_config_wlan[ce_id].src_sz_max; - if (!(host_ce_config_wlan[ce_id].flags & CE_ATTR_DIS_INTR)) { + params.max_buffer_len = ab->hw_params.host_ce_config[ce_id].src_sz_max; + if (!(ab->hw_params.host_ce_config[ce_id].flags & CE_ATTR_DIS_INTR)) { params.intr_timer_thres_us = 1024; params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN; params.low_threshold = ce_ring->nentries - 3; } break; case HAL_CE_DST_STATUS: - if (!(host_ce_config_wlan[ce_id].flags & CE_ATTR_DIS_INTR)) { + if (!(ab->hw_params.host_ce_config[ce_id].flags & CE_ATTR_DIS_INTR)) { params.intr_batch_cntr_thres_entries = 1; params.intr_timer_thres_us = 0x1000; } @@ -395,6 +502,7 @@ static int ath11k_ce_init_ring(struct ath11k_base *ab, ret, ce_id); return ret; } + ce_ring->hal_ring_id = ret; return 0; @@ -440,7 +548,7 @@ ath11k_ce_alloc_ring(struct ath11k_base *ab, int nentries, int desc_sz) static int ath11k_ce_alloc_pipe(struct ath11k_base *ab, int ce_id) { struct ath11k_ce_pipe *pipe = &ab->ce.ce_pipe[ce_id]; - const struct ce_attr *attr = &host_ce_config_wlan[ce_id]; + const struct ce_attr *attr = &ab->hw_params.host_ce_config[ce_id]; struct ath11k_ce_ring *ring; int nentries; int desc_sz; @@ -494,6 +602,7 @@ void ath11k_ce_poll_send_completed(struct ath11k_base *ab, u8 pipe_id) if ((pipe->attr_flags & CE_ATTR_DIS_INTR) && pipe->send_cb) pipe->send_cb(pipe); } +EXPORT_SYMBOL(ath11k_ce_per_engine_service); int ath11k_ce_send(struct ath11k_base *ab, struct sk_buff *skb, u8 pipe_id, u16 transfer_id) @@ -609,7 +718,7 @@ void ath11k_ce_cleanup_pipes(struct ath11k_base *ab) struct ath11k_ce_pipe *pipe; int pipe_num; - for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { + for (pipe_num = 0; pipe_num < ab->hw_params.ce_count; pipe_num++) { pipe = &ab->ce.ce_pipe[pipe_num]; ath11k_ce_rx_pipe_cleanup(pipe); @@ -619,6 +728,7 @@ void ath11k_ce_cleanup_pipes(struct ath11k_base *ab) /* NOTE: Should we also clean up tx buffer in all pipes? */ } } +EXPORT_SYMBOL(ath11k_ce_cleanup_pipes); void ath11k_ce_rx_post_buf(struct ath11k_base *ab) { @@ -626,7 +736,7 @@ void ath11k_ce_rx_post_buf(struct ath11k_base *ab) int i; int ret; - for (i = 0; i < CE_COUNT; i++) { + for (i = 0; i < ab->hw_params.ce_count; i++) { pipe = &ab->ce.ce_pipe[i]; ret = ath11k_ce_rx_post_pipe(pipe); if (ret) { @@ -642,6 +752,7 @@ void ath11k_ce_rx_post_buf(struct ath11k_base *ab) } } } +EXPORT_SYMBOL(ath11k_ce_rx_post_buf); void ath11k_ce_rx_replenish_retry(struct timer_list *t) { @@ -656,7 +767,7 @@ int ath11k_ce_init_pipes(struct ath11k_base *ab) int i; int ret; - for (i = 0; i < CE_COUNT; i++) { + for (i = 0; i < ab->hw_params.ce_count; i++) { pipe = &ab->ce.ce_pipe[i]; if (pipe->src_ring) { @@ -714,7 +825,7 @@ void ath11k_ce_free_pipes(struct ath11k_base *ab) int desc_sz; int i; - for (i = 0; i < CE_COUNT; i++) { + for (i = 0; i < ab->hw_params.ce_count; i++) { pipe = &ab->ce.ce_pipe[i]; if (pipe->src_ring) { @@ -752,6 +863,7 @@ void ath11k_ce_free_pipes(struct ath11k_base *ab) } } } +EXPORT_SYMBOL(ath11k_ce_free_pipes); int ath11k_ce_alloc_pipes(struct ath11k_base *ab) { @@ -762,8 +874,8 @@ int ath11k_ce_alloc_pipes(struct ath11k_base *ab) spin_lock_init(&ab->ce.ce_lock); - for (i = 0; i < CE_COUNT; i++) { - attr = &host_ce_config_wlan[i]; + for (i = 0; i < ab->hw_params.ce_count; i++) { + attr = &ab->hw_params.host_ce_config[i]; pipe = &ab->ce.ce_pipe[i]; pipe->pipe_num = i; pipe->ab = ab; @@ -779,6 +891,7 @@ int ath11k_ce_alloc_pipes(struct ath11k_base *ab) return 0; } +EXPORT_SYMBOL(ath11k_ce_alloc_pipes); /* For Big Endian Host, Copy Engine byte_swap is enabled * When Copy Engine does byte_swap, need to byte swap again for the @@ -799,10 +912,11 @@ void ath11k_ce_byte_swap(void *mem, u32 len) } } -int ath11k_ce_get_attr_flags(int ce_id) +int ath11k_ce_get_attr_flags(struct ath11k_base *ab, int ce_id) { - if (ce_id >= CE_COUNT) + if (ce_id >= ab->hw_params.ce_count) return -EINVAL; - return host_ce_config_wlan[ce_id].flags; + return ab->hw_params.host_ce_config[ce_id].flags; } +EXPORT_SYMBOL(ath11k_ce_get_attr_flags); diff --git a/drivers/net/wireless/ath/ath11k/ce.h b/drivers/net/wireless/ath/ath11k/ce.h index 688f357e6eaf..cf704f18f3a1 100644 --- a/drivers/net/wireless/ath/ath11k/ce.h +++ b/drivers/net/wireless/ath/ath11k/ce.h @@ -6,7 +6,7 @@ #ifndef ATH11K_CE_H #define ATH11K_CE_H -#define CE_COUNT 12 +#define CE_COUNT_MAX 12 /* Byte swap data words */ #define CE_ATTR_BYTE_SWAP_DATA 2 @@ -165,11 +165,14 @@ struct ath11k_ce_pipe { }; struct ath11k_ce { - struct ath11k_ce_pipe ce_pipe[CE_COUNT]; + struct ath11k_ce_pipe ce_pipe[CE_COUNT_MAX]; /* Protects rings of all ce pipes */ spinlock_t ce_lock; }; +extern const struct ce_attr ath11k_host_ce_config_ipq8074[]; +extern const struct ce_attr ath11k_host_ce_config_qca6390[]; + void ath11k_ce_cleanup_pipes(struct ath11k_base *ab); void ath11k_ce_rx_replenish_retry(struct timer_list *t); void ath11k_ce_per_engine_service(struct ath11k_base *ab, u16 ce_id); @@ -179,6 +182,9 @@ void ath11k_ce_rx_post_buf(struct ath11k_base *ab); int ath11k_ce_init_pipes(struct ath11k_base *ab); int ath11k_ce_alloc_pipes(struct ath11k_base *ab); void ath11k_ce_free_pipes(struct ath11k_base *ab); -int ath11k_ce_get_attr_flags(int ce_id); +int ath11k_ce_get_attr_flags(struct ath11k_base *ab, int ce_id); void ath11k_ce_poll_send_completed(struct ath11k_base *ab, u8 pipe_id); +int ath11k_ce_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe); +int ath11k_ce_attr_attach(struct ath11k_base *ab); #endif diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 905cd8beaf28..55de538aa785 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -14,43 +14,94 @@ #include "hif.h" unsigned int ath11k_debug_mask; +EXPORT_SYMBOL(ath11k_debug_mask); module_param_named(debug_mask, ath11k_debug_mask, uint, 0644); MODULE_PARM_DESC(debug_mask, "Debugging mask"); -static const struct ath11k_hw_params ath11k_hw_params = { - .name = "ipq8074", - .fw = { - .dir = IPQ8074_FW_DIR, - .board_size = IPQ8074_MAX_BOARD_DATA_SZ, - .cal_size = IPQ8074_MAX_CAL_DATA_SZ, +static const struct ath11k_hw_params ath11k_hw_params[] = { + { + .hw_rev = ATH11K_HW_IPQ8074, + .name = "ipq8074 hw2.0", + .fw = { + .dir = "IPQ8074/hw2.0", + .board_size = 256 * 1024, + .cal_size = 256 * 1024, + }, + .max_radios = 3, + .bdf_addr = 0x4B0C0000, + .hw_ops = &ipq8074_ops, + .ring_mask = &ath11k_hw_ring_mask_ipq8074, + .internal_sleep_clock = false, + .regs = &ipq8074_regs, + .host_ce_config = ath11k_host_ce_config_ipq8074, + .ce_count = 12, + .single_pdev_only = false, + .needs_band_to_mac = true, + .rxdma1_enable = true, + .num_rxmda_per_pdev = 1, + .rx_mac_buf_ring = false, + .vdev_start_delay = false, + .htt_peer_map_v2 = true, + .tcl_0_only = false, + }, + { + .name = "qca6390 hw2.0", + .hw_rev = ATH11K_HW_QCA6390_HW20, + .fw = { + .dir = "QCA6390/hw2.0", + .board_size = 256 * 1024, + .cal_size = 256 * 1024, + }, + .max_radios = 3, + .bdf_addr = 0x4B0C0000, + .hw_ops = &qca6390_ops, + .ring_mask = &ath11k_hw_ring_mask_qca6390, + .internal_sleep_clock = true, + .regs = &qca6390_regs, + .host_ce_config = ath11k_host_ce_config_qca6390, + .ce_count = 9, + .single_pdev_only = true, + .needs_band_to_mac = false, + .rxdma1_enable = false, + .num_rxmda_per_pdev = 2, + .rx_mac_buf_ring = true, + .vdev_start_delay = true, + .htt_peer_map_v2 = false, + .tcl_0_only = true, + }, + { + .name = "qca6390 hw1.1", + .hw_rev = ATH11K_HW_QCA6390_HW11, + .fw = { + .dir = "QCA6390/hw1.1", + .board_size = 256 * 1024, + .cal_size = 256 * 1024, + }, + .max_radios = 3, + .bdf_addr = 0x4B0C0000, + .hw_ops = &qca6390_ops, + .ring_mask = &ath11k_hw_ring_mask_qca6390, + .internal_sleep_clock = true, + .regs = &qca6390_regs, + .host_ce_config = ath11k_host_ce_config_qca6390, + .ce_count = 9, + .single_pdev_only = true, + .needs_band_to_mac = false, + .rxdma1_enable = false, + .num_rxmda_per_pdev = 2, + .rx_mac_buf_ring = true, + .vdev_start_delay = true, + .htt_peer_map_v2 = false, + .tcl_0_only = true, }, }; -/* Map from pdev index to hw mac index */ -u8 ath11k_core_get_hw_mac_id(struct ath11k_base *ab, int pdev_idx) -{ - switch (pdev_idx) { - case 0: - return 0; - case 1: - return 2; - case 2: - return 1; - default: - ath11k_warn(ab, "Invalid pdev idx %d\n", pdev_idx); - return ATH11K_INVALID_HW_MAC_ID; - } -} -EXPORT_SYMBOL(ath11k_core_get_hw_mac_id); - static int ath11k_core_create_board_name(struct ath11k_base *ab, char *name, size_t name_len) { - /* Note: bus is fixed to ahb. When other bus type supported, - * make it to dynamic. - */ scnprintf(name, name_len, - "bus=ahb,qmi-chip-id=%d,qmi-board-id=%d", + "bus=%s,qmi-chip-id=%d,qmi-board-id=%d", + ath11k_bus_str(ab->hif.bus), ab->qmi.target.chip_id, ab->qmi.target.board_id); @@ -59,29 +110,24 @@ static int ath11k_core_create_board_name(struct ath11k_base *ab, char *name, return 0; } -static const struct firmware *ath11k_fetch_fw_file(struct ath11k_base *ab, - const char *dir, - const char *file) +const struct firmware *ath11k_core_firmware_request(struct ath11k_base *ab, + const char *file) { - char filename[100]; const struct firmware *fw; + char path[100]; int ret; if (file == NULL) return ERR_PTR(-ENOENT); - if (dir == NULL) - dir = "."; - - snprintf(filename, sizeof(filename), "%s/%s", dir, file); - ret = firmware_request_nowarn(&fw, filename, ab->dev); - ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot fw request '%s': %d\n", - filename, ret); + ath11k_core_create_firmware_path(ab, file, path, sizeof(path)); + ret = firmware_request_nowarn(&fw, path, ab->dev); if (ret) return ERR_PTR(ret); - ath11k_warn(ab, "Downloading BDF: %s, size: %zu\n", - filename, fw->size); + + ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot firmware request %s size %zu\n", + path, fw->size); return fw; } @@ -181,26 +227,30 @@ static int ath11k_core_fetch_board_data_api_n(struct ath11k_base *ab, { size_t len, magic_len; const u8 *data; - char *filename = ATH11K_BOARD_API2_FILE; + char *filename, filepath[100]; size_t ie_len; struct ath11k_fw_ie *hdr; int ret, ie_id; + filename = ATH11K_BOARD_API2_FILE; + if (!bd->fw) - bd->fw = ath11k_fetch_fw_file(ab, - ab->hw_params.fw.dir, - filename); + bd->fw = ath11k_core_firmware_request(ab, filename); + if (IS_ERR(bd->fw)) return PTR_ERR(bd->fw); data = bd->fw->data; len = bd->fw->size; + ath11k_core_create_firmware_path(ab, filename, + filepath, sizeof(filepath)); + /* magic has extra null byte padded */ magic_len = strlen(ATH11K_BOARD_MAGIC) + 1; if (len < magic_len) { - ath11k_err(ab, "failed to find magic value in %s/%s, file too short: %zu\n", - ab->hw_params.fw.dir, filename, len); + ath11k_err(ab, "failed to find magic value in %s, file too short: %zu\n", + filepath, len); ret = -EINVAL; goto err; } @@ -214,8 +264,8 @@ static int ath11k_core_fetch_board_data_api_n(struct ath11k_base *ab, /* magic is padded to 4 bytes */ magic_len = ALIGN(magic_len, 4); if (len < magic_len) { - ath11k_err(ab, "failed: %s/%s too small to contain board data, len: %zu\n", - ab->hw_params.fw.dir, filename, len); + ath11k_err(ab, "failed: %s too small to contain board data, len: %zu\n", + filepath, len); ret = -EINVAL; goto err; } @@ -263,8 +313,8 @@ static int ath11k_core_fetch_board_data_api_n(struct ath11k_base *ab, out: if (!bd->data || !bd->len) { ath11k_err(ab, - "failed to fetch board data for %s from %s/%s\n", - boardname, ab->hw_params.fw.dir, filename); + "failed to fetch board data for %s from %s\n", + boardname, filepath); ret = -ENODATA; goto err; } @@ -279,9 +329,7 @@ err: static int ath11k_core_fetch_board_data_api_1(struct ath11k_base *ab, struct ath11k_board_data *bd) { - bd->fw = ath11k_fetch_fw_file(ab, - ab->hw_params.fw.dir, - ATH11K_DEFAULT_BOARD_FILE); + bd->fw = ath11k_core_firmware_request(ab, ATH11K_DEFAULT_BOARD_FILE); if (IS_ERR(bd->fw)) return PTR_ERR(bd->fw); @@ -706,7 +754,7 @@ static void ath11k_core_restart(struct work_struct *work) break; case ATH11K_STATE_RESTARTED: ar->state = ATH11K_STATE_WEDGED; - /* fall through */ + fallthrough; case ATH11K_STATE_WEDGED: ath11k_warn(ab, "device is wedged, will not restart radio %d\n", i); @@ -717,12 +765,52 @@ static void ath11k_core_restart(struct work_struct *work) complete(&ab->driver_recovery); } -int ath11k_core_init(struct ath11k_base *ab) +static int ath11k_init_hw_params(struct ath11k_base *ab) +{ + const struct ath11k_hw_params *hw_params = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(ath11k_hw_params); i++) { + hw_params = &ath11k_hw_params[i]; + + if (hw_params->hw_rev == ab->hw_rev) + break; + } + + if (i == ARRAY_SIZE(ath11k_hw_params)) { + ath11k_err(ab, "Unsupported hardware version: 0x%x\n", ab->hw_rev); + return -EINVAL; + } + + ab->hw_params = *hw_params; + + ath11k_dbg(ab, ATH11K_DBG_BOOT, "Hardware name %s\n", ab->hw_params.name); + + return 0; +} + +int ath11k_core_pre_init(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_init_hw_params(ab); + if (ret) { + ath11k_err(ab, "failed to get hw params: %d\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(ath11k_core_pre_init); + +static int ath11k_core_get_rproc(struct ath11k_base *ab) { struct device *dev = ab->dev; struct rproc *prproc; phandle rproc_phandle; - int ret; + + if (ab->bus_params.mhi_support) + return 0; if (of_property_read_u32(dev->of_node, "qcom,rproc", &rproc_phandle)) { ath11k_err(ab, "failed to get q6_rproc handle\n"); @@ -735,7 +823,25 @@ int ath11k_core_init(struct ath11k_base *ab) return -EINVAL; } ab->tgt_rproc = prproc; - ab->hw_params = ath11k_hw_params; + + return 0; +} + +int ath11k_core_init(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_core_get_rproc(ab); + if (ret) { + ath11k_err(ab, "failed to get rproc: %d\n", ret); + return ret; + } + + ret = ath11k_init_hw_params(ab); + if (ret) { + ath11k_err(ab, "failed to get hw params %d\n", ret); + return ret; + } ret = ath11k_core_soc_create(ab); if (ret) { @@ -745,6 +851,7 @@ int ath11k_core_init(struct ath11k_base *ab) return 0; } +EXPORT_SYMBOL(ath11k_core_init); void ath11k_core_deinit(struct ath11k_base *ab) { @@ -759,14 +866,17 @@ void ath11k_core_deinit(struct ath11k_base *ab) ath11k_mac_destroy(ab); ath11k_core_soc_destroy(ab); } +EXPORT_SYMBOL(ath11k_core_deinit); void ath11k_core_free(struct ath11k_base *ab) { kfree(ab); } +EXPORT_SYMBOL(ath11k_core_free); struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size, - enum ath11k_bus bus) + enum ath11k_bus bus, + const struct ath11k_bus_params *bus_params) { struct ath11k_base *ab; @@ -789,6 +899,8 @@ struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size, INIT_WORK(&ab->restart_work, ath11k_core_restart); timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); ab->dev = dev; + ab->bus_params = *bus_params; + ab->hif.bus = bus; return ab; @@ -796,3 +908,7 @@ err_sc_free: kfree(ab); return NULL; } +EXPORT_SYMBOL(ath11k_core_alloc); + +MODULE_DESCRIPTION("Core module for Qualcomm Atheros 802.11ax wireless LAN cards."); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index e5c4e19020ee..ef1a934221f0 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -90,6 +90,8 @@ struct ath11k_skb_rxcb { enum ath11k_hw_rev { ATH11K_HW_IPQ8074, + ATH11K_HW_QCA6390_HW20, + ATH11K_HW_QCA6390_HW11, }; enum ath11k_firmware_mode { @@ -101,18 +103,8 @@ enum ath11k_firmware_mode { }; #define ATH11K_IRQ_NUM_MAX 52 -#define ATH11K_EXT_IRQ_GRP_NUM_MAX 11 #define ATH11K_EXT_IRQ_NUM_MAX 16 -extern const u8 ath11k_reo_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; -extern const u8 ath11k_tx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; -extern const u8 ath11k_rx_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; -extern const u8 ath11k_rx_err_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; -extern const u8 ath11k_rx_wbm_rel_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; -extern const u8 ath11k_rxdma2host_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; -extern const u8 ath11k_host2rxdma_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; -extern const u8 rx_mon_status_ring_mask[ATH11K_EXT_IRQ_GRP_NUM_MAX]; - struct ath11k_ext_irq_grp { struct ath11k_base *ab; u32 irqs[ATH11K_EXT_IRQ_NUM_MAX]; @@ -226,6 +218,7 @@ struct ath11k_vif { int txpower; bool rsnie_present; bool wpaie_present; + struct ieee80211_chanctx_conf chanctx; }; struct ath11k_vif_iter { @@ -554,6 +547,7 @@ struct ath11k { }; struct ath11k_band_cap { + u32 phy_id; u32 max_bw_supported; u32 ht_cap_info; u32 he_cap_info[2]; @@ -589,6 +583,13 @@ struct ath11k_board_data { size_t len; }; +struct ath11k_bus_params { + bool mhi_support; + bool m3_fw_support; + bool fixed_bdf_addr; + bool fixed_mem_region; +}; + /* IPQ8074 HW channel counters frequency value in hertz */ #define IPQ8074_CC_FREQ_HERTZ 320000 @@ -651,6 +652,7 @@ struct ath11k_base { unsigned long mem_len; struct { + enum ath11k_bus bus; const struct ath11k_hif_ops *ops; } hif; @@ -677,7 +679,10 @@ struct ath11k_base { u32 ext_service_bitmap[WMI_SERVICE_EXT_BM_SIZE]; bool pdevs_macaddr_valid; int bd_api; + struct ath11k_hw_params hw_params; + struct ath11k_bus_params bus_params; + const struct firmware *cal_file; /* Below regd's are protected by ab->data_lock */ @@ -850,17 +855,21 @@ struct ath11k_peer *ath11k_peer_find_by_addr(struct ath11k_base *ab, const u8 *addr); struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab, int peer_id); int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab); +int ath11k_core_pre_init(struct ath11k_base *ab); int ath11k_core_init(struct ath11k_base *ath11k); void ath11k_core_deinit(struct ath11k_base *ath11k); struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size, - enum ath11k_bus bus); + enum ath11k_bus bus, + const struct ath11k_bus_params *bus_params); void ath11k_core_free(struct ath11k_base *ath11k); int ath11k_core_fetch_bdf(struct ath11k_base *ath11k, struct ath11k_board_data *bd); void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd); void ath11k_core_halt(struct ath11k *ar); -u8 ath11k_core_get_hw_mac_id(struct ath11k_base *ab, int pdev_idx); + +const struct firmware *ath11k_core_firmware_request(struct ath11k_base *ab, + const char *filename); static inline const char *ath11k_scan_state_str(enum ath11k_scan_state state) { @@ -894,4 +903,30 @@ static inline struct ath11k_vif *ath11k_vif_to_arvif(struct ieee80211_vif *vif) return (struct ath11k_vif *)vif->drv_priv; } +static inline struct ath11k *ath11k_ab_to_ar(struct ath11k_base *ab, + int mac_id) +{ + return ab->pdevs[ath11k_hw_mac_id_to_pdev_id(&ab->hw_params, mac_id)].ar; +} + +static inline void ath11k_core_create_firmware_path(struct ath11k_base *ab, + const char *filename, + void *buf, size_t buf_len) +{ + snprintf(buf, buf_len, "%s/%s/%s", ATH11K_FW_DIR, + ab->hw_params.fw.dir, filename); +} + +static inline const char *ath11k_bus_str(enum ath11k_bus bus) +{ + switch (bus) { + case ATH11K_BUS_PCI: + return "pci"; + case ATH11K_BUS_AHB: + return "ahb"; + } + + return "unknown"; +} + #endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/dbring.c b/drivers/net/wireless/ath/ath11k/dbring.c index cf20db370123..5e1f5437b418 100644 --- a/drivers/net/wireless/ath/ath11k/dbring.c +++ b/drivers/net/wireless/ath/ath11k/dbring.c @@ -168,7 +168,7 @@ int ath11k_dbring_buf_setup(struct ath11k *ar, srng = &ab->hal.srng_list[ring->refill_srng.ring_id]; ring->bufs_max = ring->refill_srng.size / - ath11k_hal_srng_get_entrysize(HAL_RXDMA_DIR_BUF); + ath11k_hal_srng_get_entrysize(ab, HAL_RXDMA_DIR_BUF); ring->buf_sz = db_cap->min_buf_sz; ring->buf_align = db_cap->min_buf_align; diff --git a/drivers/net/wireless/ath/ath11k/debug.c b/drivers/net/wireless/ath/ath11k/debug.c index 62a1aa0565a9..2ac0df8aee26 100644 --- a/drivers/net/wireless/ath/ath11k/debug.c +++ b/drivers/net/wireless/ath/ath11k/debug.c @@ -62,6 +62,7 @@ void ath11k_info(struct ath11k_base *ab, const char *fmt, ...) /* TODO: Trace the log */ va_end(args); } +EXPORT_SYMBOL(ath11k_info); void ath11k_err(struct ath11k_base *ab, const char *fmt, ...) { @@ -76,6 +77,7 @@ void ath11k_err(struct ath11k_base *ab, const char *fmt, ...) /* TODO: Trace the log */ va_end(args); } +EXPORT_SYMBOL(ath11k_err); void ath11k_warn(struct ath11k_base *ab, const char *fmt, ...) { @@ -90,6 +92,7 @@ void ath11k_warn(struct ath11k_base *ab, const char *fmt, ...) /* TODO: Trace the log */ va_end(args); } +EXPORT_SYMBOL(ath11k_warn); #ifdef CONFIG_ATH11K_DEBUG void __ath11k_dbg(struct ath11k_base *ab, enum ath11k_debug_mask mask, @@ -110,6 +113,7 @@ void __ath11k_dbg(struct ath11k_base *ab, enum ath11k_debug_mask mask, va_end(args); } +EXPORT_SYMBOL(__ath11k_dbg); void ath11k_dbg_dump(struct ath11k_base *ab, enum ath11k_debug_mask mask, @@ -138,6 +142,7 @@ void ath11k_dbg_dump(struct ath11k_base *ab, } } } +EXPORT_SYMBOL(ath11k_dbg_dump); #endif @@ -693,8 +698,10 @@ static ssize_t ath11k_write_extd_rx_stats(struct file *file, size_t count, loff_t *ppos) { struct ath11k *ar = file->private_data; + struct ath11k_base *ab = ar->ab; struct htt_rx_ring_tlv_filter tlv_filter = {0}; u32 enable, rx_filter = 0, ring_id; + int i; int ret; if (kstrtouint_from_user(ubuf, count, 0, &enable)) @@ -737,14 +744,16 @@ static ssize_t ath11k_write_extd_rx_stats(struct file *file, ar->debug.rx_filter = tlv_filter.rx_filter; - ring_id = ar->dp.rx_mon_status_refill_ring.refill_buf_ring.ring_id; - ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, - HAL_RXDMA_MONITOR_STATUS, - DP_RX_BUFFER_SIZE, &tlv_filter); + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, + HAL_RXDMA_MONITOR_STATUS, + DP_RX_BUFFER_SIZE, &tlv_filter); - if (ret) { - ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n"); - goto exit; + if (ret) { + ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n"); + goto exit; + } } ar->debug.extd_rx_stats = enable; @@ -943,8 +952,8 @@ int ath11k_debug_pdev_create(struct ath11k_base *ab) void ath11k_debug_pdev_destroy(struct ath11k_base *ab) { - debugfs_remove_recursive(ab->debugfs_ath11k); - ab->debugfs_ath11k = NULL; + debugfs_remove_recursive(ab->debugfs_soc); + ab->debugfs_soc = NULL; } int ath11k_debug_soc_create(struct ath11k_base *ab) @@ -962,8 +971,8 @@ int ath11k_debug_soc_create(struct ath11k_base *ab) void ath11k_debug_soc_destroy(struct ath11k_base *ab) { - debugfs_remove_recursive(ab->debugfs_soc); - ab->debugfs_soc = NULL; + debugfs_remove_recursive(ab->debugfs_ath11k); + ab->debugfs_ath11k = NULL; } void ath11k_debug_fw_stats_init(struct ath11k *ar) @@ -995,10 +1004,11 @@ static ssize_t ath11k_write_pktlog_filter(struct file *file, size_t count, loff_t *ppos) { struct ath11k *ar = file->private_data; + struct ath11k_base *ab = ar->ab; struct htt_rx_ring_tlv_filter tlv_filter = {0}; u32 rx_filter = 0, ring_id, filter, mode; u8 buf[128] = {0}; - int ret; + int i, ret; ssize_t rc; mutex_lock(&ar->conf_mutex); @@ -1079,16 +1089,20 @@ static ssize_t ath11k_write_pktlog_filter(struct file *file, HTT_RX_FP_DATA_FILTER_FLASG3; } - ring_id = ar->dp.rx_mon_status_refill_ring.refill_buf_ring.ring_id; - ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, - HAL_RXDMA_MONITOR_STATUS, - DP_RX_BUFFER_SIZE, &tlv_filter); - if (ret) { - ath11k_warn(ar->ab, "failed to set rx filter for monitor status ring\n"); - goto out; + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_rx_filter_setup(ab, ring_id, + ar->dp.mac_id + i, + HAL_RXDMA_MONITOR_STATUS, + DP_RX_BUFFER_SIZE, &tlv_filter); + + if (ret) { + ath11k_warn(ab, "failed to set rx filter for moniter status ring\n"); + goto out; + } } - ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "pktlog filter %d mode %s\n", + ath11k_dbg(ab, ATH11K_DBG_WMI, "pktlog filter %d mode %s\n", filter, ((mode == ATH11K_PKTLOG_MODE_FULL) ? "full" : "lite")); ar->debug.pktlog_filter = filter; diff --git a/drivers/net/wireless/ath/ath11k/debug.h b/drivers/net/wireless/ath/ath11k/debug.h index c30085406bfb..1cfe54859388 100644 --- a/drivers/net/wireless/ath/ath11k/debug.h +++ b/drivers/net/wireless/ath/ath11k/debug.h @@ -25,6 +25,7 @@ enum ath11k_debug_mask { ATH11K_DBG_REG = 0x00000200, ATH11K_DBG_TESTMODE = 0x00000400, ATH11k_DBG_HAL = 0x00000800, + ATH11K_DBG_PCI = 0x00001000, ATH11K_DBG_ANY = 0xffffffff, }; diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index 1d64c3c51ac9..2617ec221775 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -7,6 +7,7 @@ #include "core.h" #include "dp_tx.h" #include "hal_tx.h" +#include "hif.h" #include "debug.h" #include "dp_rx.h" #include "peer.h" @@ -106,13 +107,120 @@ void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring) ring->vaddr_unaligned = NULL; } +static int ath11k_dp_srng_find_ring_in_mask(int ring_num, const u8 *grp_mask) +{ + int ext_group_num; + u8 mask = 1 << ring_num; + + for (ext_group_num = 0; ext_group_num < ATH11K_EXT_IRQ_GRP_NUM_MAX; + ext_group_num++) { + if (mask & grp_mask[ext_group_num]) + return ext_group_num; + } + + return -ENOENT; +} + +static int ath11k_dp_srng_calculate_msi_group(struct ath11k_base *ab, + enum hal_ring_type type, int ring_num) +{ + const u8 *grp_mask; + + switch (type) { + case HAL_WBM2SW_RELEASE: + if (ring_num < 3) { + grp_mask = &ab->hw_params.ring_mask->tx[0]; + } else if (ring_num == 3) { + grp_mask = &ab->hw_params.ring_mask->rx_wbm_rel[0]; + ring_num = 0; + } else { + return -ENOENT; + } + break; + case HAL_REO_EXCEPTION: + grp_mask = &ab->hw_params.ring_mask->rx_err[0]; + break; + case HAL_REO_DST: + grp_mask = &ab->hw_params.ring_mask->rx[0]; + break; + case HAL_REO_STATUS: + grp_mask = &ab->hw_params.ring_mask->reo_status[0]; + break; + case HAL_RXDMA_MONITOR_STATUS: + case HAL_RXDMA_MONITOR_DST: + grp_mask = &ab->hw_params.ring_mask->rx_mon_status[0]; + break; + case HAL_RXDMA_DST: + grp_mask = &ab->hw_params.ring_mask->rxdma2host[0]; + break; + case HAL_RXDMA_BUF: + grp_mask = &ab->hw_params.ring_mask->host2rxdma[0]; + break; + case HAL_RXDMA_MONITOR_BUF: + case HAL_TCL_DATA: + case HAL_TCL_CMD: + case HAL_REO_CMD: + case HAL_SW2WBM_RELEASE: + case HAL_WBM_IDLE_LINK: + case HAL_TCL_STATUS: + case HAL_REO_REINJECT: + case HAL_CE_SRC: + case HAL_CE_DST: + case HAL_CE_DST_STATUS: + default: + return -ENOENT; + } + + return ath11k_dp_srng_find_ring_in_mask(ring_num, grp_mask); +} + +static void ath11k_dp_srng_msi_setup(struct ath11k_base *ab, + struct hal_srng_params *ring_params, + enum hal_ring_type type, int ring_num) +{ + int msi_group_number, msi_data_count; + u32 msi_data_start, msi_irq_start, addr_lo, addr_hi; + int ret; + + ret = ath11k_get_user_msi_vector(ab, "DP", + &msi_data_count, &msi_data_start, + &msi_irq_start); + if (ret) + return; + + msi_group_number = ath11k_dp_srng_calculate_msi_group(ab, type, + ring_num); + if (msi_group_number < 0) { + ath11k_dbg(ab, ATH11K_DBG_PCI, + "ring not part of an ext_group; ring_type: %d,ring_num %d", + type, ring_num); + ring_params->msi_addr = 0; + ring_params->msi_data = 0; + return; + } + + if (msi_group_number > msi_data_count) { + ath11k_dbg(ab, ATH11K_DBG_PCI, + "multiple msi_groups share one msi, msi_group_num %d", + msi_group_number); + } + + ath11k_get_msi_address(ab, &addr_lo, &addr_hi); + + ring_params->msi_addr = addr_lo; + ring_params->msi_addr |= (dma_addr_t)(((uint64_t)addr_hi) << 32); + ring_params->msi_data = (msi_group_number % msi_data_count) + + msi_data_start; + ring_params->flags |= HAL_SRNG_FLAGS_MSI_INTR; +} + int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, enum hal_ring_type type, int ring_num, int mac_id, int num_entries) { struct hal_srng_params params = { 0 }; - int entry_sz = ath11k_hal_srng_get_entrysize(type); - int max_entries = ath11k_hal_srng_get_max_entries(type); + int entry_sz = ath11k_hal_srng_get_entrysize(ab, type); + int max_entries = ath11k_hal_srng_get_max_entries(ab, type); int ret; if (max_entries < 0 || entry_sz < 0) @@ -135,6 +243,7 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, params.ring_base_vaddr = ring->vaddr; params.ring_base_paddr = ring->paddr; params.num_entries = num_entries; + ath11k_dp_srng_msi_setup(ab, ¶ms, type, ring_num + mac_id); switch (type) { case HAL_REO_DST: @@ -159,7 +268,7 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, break; } /* follow through when ring_num >= 3 */ - /* fall through */ + fallthrough; case HAL_REO_EXCEPTION: case HAL_REO_REINJECT: case HAL_REO_CMD: @@ -367,7 +476,7 @@ static int ath11k_dp_scatter_idle_link_desc_setup(struct ath11k_base *ab, u32 end_offset; n_entries_per_buf = HAL_WBM_IDLE_SCATTER_BUF_SIZE / - ath11k_hal_srng_get_entrysize(HAL_WBM_IDLE_LINK); + ath11k_hal_srng_get_entrysize(ab, HAL_WBM_IDLE_LINK); num_scatter_buf = DIV_ROUND_UP(size, HAL_WBM_IDLE_SCATTER_BUF_SIZE); if (num_scatter_buf > DP_IDLE_SCATTER_BUFS_MAX) @@ -565,7 +674,7 @@ int ath11k_dp_link_desc_setup(struct ath11k_base *ab, return ret; /* Setup link desc idle list for HW internal usage */ - entry_sz = ath11k_hal_srng_get_entrysize(ring_type); + entry_sz = ath11k_hal_srng_get_entrysize(ab, ring_type); tot_mem_sz = entry_sz * n_link_desc; /* Setup scatter desc list when the total memory requirement is more */ @@ -622,16 +731,16 @@ int ath11k_dp_service_srng(struct ath11k_base *ab, struct napi_struct *napi = &irq_grp->napi; int grp_id = irq_grp->grp_id; int work_done = 0; - int i = 0; + int i = 0, j; int tot_work_done = 0; - while (ath11k_tx_ring_mask[grp_id] >> i) { - if (ath11k_tx_ring_mask[grp_id] & BIT(i)) + while (ab->hw_params.ring_mask->tx[grp_id] >> i) { + if (ab->hw_params.ring_mask->tx[grp_id] & BIT(i)) ath11k_dp_tx_completion_handler(ab, i); i++; } - if (ath11k_rx_err_ring_mask[grp_id]) { + if (ab->hw_params.ring_mask->rx_err[grp_id]) { work_done = ath11k_dp_process_rx_err(ab, napi, budget); budget -= work_done; tot_work_done += work_done; @@ -639,7 +748,7 @@ int ath11k_dp_service_srng(struct ath11k_base *ab, goto done; } - if (ath11k_rx_wbm_rel_ring_mask[grp_id]) { + if (ab->hw_params.ring_mask->rx_wbm_rel[grp_id]) { work_done = ath11k_dp_rx_process_wbm_err(ab, napi, budget); @@ -650,8 +759,8 @@ int ath11k_dp_service_srng(struct ath11k_base *ab, goto done; } - if (ath11k_rx_ring_mask[grp_id]) { - i = fls(ath11k_rx_ring_mask[grp_id]) - 1; + if (ab->hw_params.ring_mask->rx[grp_id]) { + i = fls(ab->hw_params.ring_mask->rx[grp_id]) - 1; work_done = ath11k_dp_process_rx(ab, i, napi, budget); budget -= work_done; @@ -660,41 +769,52 @@ int ath11k_dp_service_srng(struct ath11k_base *ab, goto done; } - if (rx_mon_status_ring_mask[grp_id]) { - for (i = 0; i < ab->num_radios; i++) { - if (rx_mon_status_ring_mask[grp_id] & BIT(i)) { - work_done = - ath11k_dp_rx_process_mon_rings(ab, - i, napi, - budget); - budget -= work_done; - tot_work_done += work_done; + if (ab->hw_params.ring_mask->rx_mon_status[grp_id]) { + for (i = 0; i < ab->num_radios; i++) { + for (j = 0; j < ab->hw_params.num_rxmda_per_pdev; j++) { + int id = i * ab->hw_params.num_rxmda_per_pdev + j; + + if (ab->hw_params.ring_mask->rx_mon_status[grp_id] & + BIT(id)) { + work_done = + ath11k_dp_rx_process_mon_rings(ab, + id, + napi, budget); + budget -= work_done; + tot_work_done += work_done; + + if (budget <= 0) + goto done; + } } - if (budget <= 0) - goto done; } } - if (ath11k_reo_status_ring_mask[grp_id]) + if (ab->hw_params.ring_mask->reo_status[grp_id]) ath11k_dp_process_reo_status(ab); for (i = 0; i < ab->num_radios; i++) { - if (ath11k_rxdma2host_ring_mask[grp_id] & BIT(i)) { - work_done = ath11k_dp_process_rxdma_err(ab, i, budget); - budget -= work_done; - tot_work_done += work_done; - } + for (j = 0; j < ab->hw_params.num_rxmda_per_pdev; j++) { + int id = i * ab->hw_params.num_rxmda_per_pdev + j; - if (budget <= 0) - goto done; + if (ab->hw_params.ring_mask->rxdma2host[grp_id] & BIT(id)) { + work_done = ath11k_dp_process_rxdma_err(ab, id, budget); + budget -= work_done; + tot_work_done += work_done; + } + + if (budget <= 0) + goto done; - if (ath11k_host2rxdma_ring_mask[grp_id] & BIT(i)) { - struct ath11k_pdev_dp *dp = &ab->pdevs[i].ar->dp; - struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + if (ab->hw_params.ring_mask->host2rxdma[grp_id] & BIT(id)) { + struct ath11k *ar = ath11k_ab_to_ar(ab, id); + struct ath11k_pdev_dp *dp = &ar->dp; + struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; - ath11k_dp_rxbufs_replenish(ab, i, rx_ring, 0, - HAL_RX_BUF_RBM_SW3_BM, - GFP_ATOMIC); + ath11k_dp_rxbufs_replenish(ab, id, rx_ring, 0, + HAL_RX_BUF_RBM_SW3_BM, + GFP_ATOMIC); + } } } /* TODO: Implement handler for other interrupts */ @@ -722,6 +842,7 @@ void ath11k_dp_pdev_pre_alloc(struct ath11k_base *ab) struct ath11k *ar; struct ath11k_pdev_dp *dp; int i; + int j; for (i = 0; i < ab->num_radios; i++) { ar = ab->pdevs[i].ar; @@ -731,8 +852,10 @@ void ath11k_dp_pdev_pre_alloc(struct ath11k_base *ab) spin_lock_init(&dp->rx_refill_buf_ring.idr_lock); atomic_set(&dp->num_tx_pending, 0); init_waitqueue_head(&dp->tx_empty_waitq); - idr_init(&dp->rx_mon_status_refill_ring.bufs_idr); - spin_lock_init(&dp->rx_mon_status_refill_ring.idr_lock); + for (j = 0; j < ab->hw_params.num_rxmda_per_pdev; j++) { + idr_init(&dp->rx_mon_status_refill_ring[j].bufs_idr); + spin_lock_init(&dp->rx_mon_status_refill_ring[j].idr_lock); + } idr_init(&dp->rxdma_mon_buf_ring.bufs_idr); spin_lock_init(&dp->rxdma_mon_buf_ring.idr_lock); } @@ -797,13 +920,20 @@ int ath11k_dp_htt_connect(struct ath11k_dp *dp) static void ath11k_dp_update_vdev_search(struct ath11k_vif *arvif) { - /* For STA mode, enable address search index, - * tcl uses ast_hash value in the descriptor. + /* When v2_map_support is true:for STA mode, enable address + * search index, tcl uses ast_hash value in the descriptor. + * When v2_map_support is false: for STA mode, dont' enable + * address search index. */ switch (arvif->vdev_type) { case WMI_VDEV_TYPE_STA: - arvif->hal_addr_search_flags = HAL_TX_ADDRX_EN; - arvif->search_type = HAL_TX_ADDR_SEARCH_INDEX; + if (arvif->ar->ab->hw_params.htt_peer_map_v2) { + arvif->hal_addr_search_flags = HAL_TX_ADDRX_EN; + arvif->search_type = HAL_TX_ADDR_SEARCH_INDEX; + } else { + arvif->hal_addr_search_flags = HAL_TX_ADDRY_EN; + arvif->search_type = HAL_TX_ADDR_SEARCH_DEFAULT; + } break; case WMI_VDEV_TYPE_AP: case WMI_VDEV_TYPE_IBSS: diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h index 7587862d2e32..558f03fbec99 100644 --- a/drivers/net/wireless/ath/ath11k/dp.h +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -8,6 +8,8 @@ #include "hal_rx.h" +#define MAX_RXDMA_PER_PDEV 2 + struct ath11k_base; struct ath11k_peer; struct ath11k_dp; @@ -142,12 +144,13 @@ struct ath11k_pdev_dp { atomic_t num_tx_pending; wait_queue_head_t tx_empty_waitq; struct dp_rxdma_ring rx_refill_buf_ring; - struct dp_srng rxdma_err_dst_ring; + struct dp_srng rx_mac_buf_ring[MAX_RXDMA_PER_PDEV]; + struct dp_srng rxdma_err_dst_ring[MAX_RXDMA_PER_PDEV]; struct dp_srng rxdma_mon_dst_ring; struct dp_srng rxdma_mon_desc_ring; struct dp_rxdma_ring rxdma_mon_buf_ring; - struct dp_rxdma_ring rx_mon_status_refill_ring; + struct dp_rxdma_ring rx_mon_status_refill_ring[MAX_RXDMA_PER_PDEV]; struct ieee80211_rx_status rx_status; struct ath11k_mon_data mon_data; }; @@ -936,11 +939,13 @@ struct htt_rx_ring_tlv_filter { enum htt_t2h_msg_type { HTT_T2H_MSG_TYPE_VERSION_CONF, + HTT_T2H_MSG_TYPE_PEER_MAP = 0x3, + HTT_T2H_MSG_TYPE_PEER_UNMAP = 0x4, HTT_T2H_MSG_TYPE_RX_ADDBA = 0x5, HTT_T2H_MSG_TYPE_PKTLOG = 0x8, HTT_T2H_MSG_TYPE_SEC_IND = 0xb, - HTT_T2H_MSG_TYPE_PEER_MAP = 0x1e, - HTT_T2H_MSG_TYPE_PEER_UNMAP = 0x1f, + HTT_T2H_MSG_TYPE_PEER_MAP2 = 0x1e, + HTT_T2H_MSG_TYPE_PEER_UNMAP2 = 0x1f, HTT_T2H_MSG_TYPE_PPDU_STATS_IND = 0x1d, HTT_T2H_MSG_TYPE_EXT_STATS_CONF = 0x1c, HTT_T2H_MSG_TYPE_BKPRESSURE_EVENT_IND = 0x24, diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 791d971784ce..e7686479278f 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -375,7 +375,13 @@ static int ath11k_dp_rxdma_buf_ring_free(struct ath11k *ar, idr_destroy(&rx_ring->bufs_idr); spin_unlock_bh(&rx_ring->idr_lock); - rx_ring = &dp->rx_mon_status_refill_ring; + /* if rxdma1_enable is false, mon_status_refill_ring + * isn't setup, so don't clean. + */ + if (!ar->ab->hw_params.rxdma1_enable) + return 0; + + rx_ring = &dp->rx_mon_status_refill_ring[0]; spin_lock_bh(&rx_ring->idr_lock); idr_for_each_entry(&rx_ring->bufs_idr, skb, buf_id) { @@ -390,21 +396,27 @@ static int ath11k_dp_rxdma_buf_ring_free(struct ath11k *ar, idr_destroy(&rx_ring->bufs_idr); spin_unlock_bh(&rx_ring->idr_lock); + return 0; } static int ath11k_dp_rxdma_pdev_buf_free(struct ath11k *ar) { struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_base *ab = ar->ab; struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + int i; ath11k_dp_rxdma_buf_ring_free(ar, rx_ring); rx_ring = &dp->rxdma_mon_buf_ring; ath11k_dp_rxdma_buf_ring_free(ar, rx_ring); - rx_ring = &dp->rx_mon_status_refill_ring; - ath11k_dp_rxdma_buf_ring_free(ar, rx_ring); + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + rx_ring = &dp->rx_mon_status_refill_ring[i]; + ath11k_dp_rxdma_buf_ring_free(ar, rx_ring); + } + return 0; } @@ -416,7 +428,7 @@ static int ath11k_dp_rxdma_ring_buf_setup(struct ath11k *ar, int num_entries; num_entries = rx_ring->refill_buf_ring.size / - ath11k_hal_srng_get_entrysize(ringtype); + ath11k_hal_srng_get_entrysize(ar->ab, ringtype); rx_ring->bufs_max = num_entries; ath11k_dp_rxbufs_replenish(ar->ab, dp->mac_id, rx_ring, num_entries, @@ -427,15 +439,21 @@ static int ath11k_dp_rxdma_ring_buf_setup(struct ath11k *ar, static int ath11k_dp_rxdma_pdev_buf_setup(struct ath11k *ar) { struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_base *ab = ar->ab; struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; + int i; ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_BUF); - rx_ring = &dp->rxdma_mon_buf_ring; - ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_MONITOR_BUF); + if (ar->ab->hw_params.rxdma1_enable) { + rx_ring = &dp->rxdma_mon_buf_ring; + ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_MONITOR_BUF); + } - rx_ring = &dp->rx_mon_status_refill_ring; - ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_MONITOR_STATUS); + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + rx_ring = &dp->rx_mon_status_refill_ring[i]; + ath11k_dp_rxdma_ring_buf_setup(ar, rx_ring, HAL_RXDMA_MONITOR_STATUS); + } return 0; } @@ -443,11 +461,21 @@ static int ath11k_dp_rxdma_pdev_buf_setup(struct ath11k *ar) static void ath11k_dp_rx_pdev_srng_free(struct ath11k *ar) { struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_base *ab = ar->ab; + int i; + + ath11k_dp_srng_cleanup(ab, &dp->rx_refill_buf_ring.refill_buf_ring); + + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + if (ab->hw_params.rx_mac_buf_ring) + ath11k_dp_srng_cleanup(ab, &dp->rx_mac_buf_ring[i]); - ath11k_dp_srng_cleanup(ar->ab, &dp->rx_refill_buf_ring.refill_buf_ring); - ath11k_dp_srng_cleanup(ar->ab, &dp->rxdma_err_dst_ring); - ath11k_dp_srng_cleanup(ar->ab, &dp->rx_mon_status_refill_ring.refill_buf_ring); - ath11k_dp_srng_cleanup(ar->ab, &dp->rxdma_mon_buf_ring.refill_buf_ring); + ath11k_dp_srng_cleanup(ab, &dp->rxdma_err_dst_ring[i]); + ath11k_dp_srng_cleanup(ab, + &dp->rx_mon_status_refill_ring[i].refill_buf_ring); + } + + ath11k_dp_srng_cleanup(ab, &dp->rxdma_mon_buf_ring.refill_buf_ring); } void ath11k_dp_pdev_reo_cleanup(struct ath11k_base *ab) @@ -486,7 +514,9 @@ err_reo_cleanup: static int ath11k_dp_rx_pdev_srng_alloc(struct ath11k *ar) { struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_base *ab = ar->ab; struct dp_srng *srng = NULL; + int i; int ret; ret = ath11k_dp_srng_setup(ar->ab, @@ -498,24 +528,50 @@ static int ath11k_dp_rx_pdev_srng_alloc(struct ath11k *ar) return ret; } - ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_err_dst_ring, - HAL_RXDMA_DST, 0, dp->mac_id, - DP_RXDMA_ERR_DST_RING_SIZE); - if (ret) { - ath11k_warn(ar->ab, "failed to setup rxdma_err_dst_ring\n"); - return ret; + if (ar->ab->hw_params.rx_mac_buf_ring) { + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + ret = ath11k_dp_srng_setup(ar->ab, + &dp->rx_mac_buf_ring[i], + HAL_RXDMA_BUF, 1, + dp->mac_id + i, 1024); + if (ret) { + ath11k_warn(ar->ab, "failed to setup rx_mac_buf_ring %d\n", + i); + return ret; + } + } } - srng = &dp->rx_mon_status_refill_ring.refill_buf_ring; - ret = ath11k_dp_srng_setup(ar->ab, - srng, - HAL_RXDMA_MONITOR_STATUS, 0, dp->mac_id, - DP_RXDMA_MON_STATUS_RING_SIZE); - if (ret) { - ath11k_warn(ar->ab, - "failed to setup rx_mon_status_refill_ring\n"); - return ret; + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_err_dst_ring[i], + HAL_RXDMA_DST, 0, dp->mac_id + i, + DP_RXDMA_ERR_DST_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, "failed to setup rxdma_err_dst_ring %d\n", i); + return ret; + } } + + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + srng = &dp->rx_mon_status_refill_ring[i].refill_buf_ring; + ret = ath11k_dp_srng_setup(ar->ab, + srng, + HAL_RXDMA_MONITOR_STATUS, 0, dp->mac_id + i, + DP_RXDMA_MON_STATUS_RING_SIZE); + if (ret) { + ath11k_warn(ar->ab, + "failed to setup rx_mon_status_refill_ring %d\n", i); + return ret; + } + } + + /* if rxdma1_enable is false, then it doesn't need + * to setup rxdam_mon_buf_ring, rxdma_mon_dst_ring + * and rxdma_mon_desc_ring. + */ + if (!ar->ab->hw_params.rxdma1_enable) + return 0; + ret = ath11k_dp_srng_setup(ar->ab, &dp->rxdma_mon_buf_ring.refill_buf_ring, HAL_RXDMA_MONITOR_BUF, 0, dp->mac_id, @@ -1577,11 +1633,23 @@ void ath11k_dp_htt_htc_t2h_msg_handler(struct ath11k_base *ab, resp->peer_map_ev.info1); ath11k_dp_get_mac_addr(resp->peer_map_ev.mac_addr_l32, peer_mac_h16, mac_addr); + ath11k_peer_map_event(ab, vdev_id, peer_id, mac_addr, 0); + break; + case HTT_T2H_MSG_TYPE_PEER_MAP2: + vdev_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_VDEV_ID, + resp->peer_map_ev.info); + peer_id = FIELD_GET(HTT_T2H_PEER_MAP_INFO_PEER_ID, + resp->peer_map_ev.info); + peer_mac_h16 = FIELD_GET(HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16, + resp->peer_map_ev.info1); + ath11k_dp_get_mac_addr(resp->peer_map_ev.mac_addr_l32, + peer_mac_h16, mac_addr); ast_hash = FIELD_GET(HTT_T2H_PEER_MAP_INFO2_AST_HASH_VAL, resp->peer_map_ev.info2); ath11k_peer_map_event(ab, vdev_id, peer_id, mac_addr, ast_hash); break; case HTT_T2H_MSG_TYPE_PEER_UNMAP: + case HTT_T2H_MSG_TYPE_PEER_UNMAP2: peer_id = FIELD_GET(HTT_T2H_PEER_UNMAP_INFO_PEER_ID, resp->peer_unmap_ev.info); ath11k_peer_unmap_event(ab, peer_id); @@ -2186,7 +2254,6 @@ static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc, struct ieee80211_rx_status *rx_status) { u8 channel_num; - u32 center_freq; rx_status->freq = 0; rx_status->rate_idx = 0; @@ -2197,11 +2264,8 @@ static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc, rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; channel_num = ath11k_dp_rx_h_msdu_start_freq(rx_desc); - center_freq = ath11k_dp_rx_h_msdu_start_freq(rx_desc) >> 16; - if (center_freq >= 5935 && center_freq <= 7105) { - rx_status->band = NL80211_BAND_6GHZ; - } else if (channel_num >= 1 && channel_num <= 14) { + if (channel_num >= 1 && channel_num <= 14) { rx_status->band = NL80211_BAND_2GHZ; } else if (channel_num >= 36 && channel_num <= 173) { rx_status->band = NL80211_BAND_5GHZ; @@ -2719,20 +2783,25 @@ fail_desc_get: static int ath11k_dp_rx_reap_mon_status_ring(struct ath11k_base *ab, int mac_id, int *budget, struct sk_buff_head *skb_list) { - struct ath11k *ar = ab->pdevs[mac_id].ar; - struct ath11k_pdev_dp *dp = &ar->dp; - struct dp_rxdma_ring *rx_ring = &dp->rx_mon_status_refill_ring; + struct ath11k *ar; + struct ath11k_pdev_dp *dp; + struct dp_rxdma_ring *rx_ring; struct hal_srng *srng; void *rx_mon_status_desc; struct sk_buff *skb; struct ath11k_skb_rxcb *rxcb; struct hal_tlv_hdr *tlv; u32 cookie; - int buf_id; + int buf_id, srng_id; dma_addr_t paddr; u8 rbm; int num_buffs_reaped = 0; + ar = ab->pdevs[ath11k_hw_mac_id_to_pdev_id(&ab->hw_params, mac_id)].ar; + dp = &ar->dp; + srng_id = ath11k_hw_mac_id_to_srng_id(&ab->hw_params, mac_id); + rx_ring = &dp->rx_mon_status_refill_ring[srng_id]; + srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id]; spin_lock_bh(&srng->lock); @@ -2813,7 +2882,7 @@ move_next: int ath11k_dp_rx_process_mon_status(struct ath11k_base *ab, int mac_id, struct napi_struct *napi, int budget) { - struct ath11k *ar = ab->pdevs[mac_id].ar; + struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id); enum hal_rx_mon_status hal_status; struct sk_buff *skb; struct sk_buff_head skb_list; @@ -3709,8 +3778,7 @@ static bool ath11k_dp_rx_h_reo_err(struct ath11k *ar, struct sk_buff *msdu, * instead, it is good to drop such packets in mac80211 * after incrementing the replay counters. */ - - /* fall through */ + fallthrough; default: /* TODO: Review other errors and process them to mac80211 * as appropriate. @@ -3820,7 +3888,7 @@ int ath11k_dp_rx_process_wbm_err(struct ath11k_base *ab, int total_num_buffs_reaped = 0; int ret, i; - for (i = 0; i < MAX_RADIOS; i++) + for (i = 0; i < ab->num_radios; i++) __skb_queue_head_init(&msdu_list[i]); srng = &ab->hal.srng_list[dp->rx_rel_ring.ring_id]; @@ -3923,9 +3991,9 @@ done: int ath11k_dp_process_rxdma_err(struct ath11k_base *ab, int mac_id, int budget) { - struct ath11k *ar = ab->pdevs[mac_id].ar; - struct dp_srng *err_ring = &ar->dp.rxdma_err_dst_ring; - struct dp_rxdma_ring *rx_ring = &ar->dp.rx_refill_buf_ring; + struct ath11k *ar; + struct dp_srng *err_ring; + struct dp_rxdma_ring *rx_ring; struct dp_link_desc_bank *link_desc_banks = ab->dp.link_desc_banks; struct hal_srng *srng; u32 msdu_cookies[HAL_NUM_RX_MSDUS_PER_LINK_DESC]; @@ -3944,6 +4012,11 @@ int ath11k_dp_process_rxdma_err(struct ath11k_base *ab, int mac_id, int budget) int i; int buf_id; + ar = ab->pdevs[ath11k_hw_mac_id_to_pdev_id(&ab->hw_params, mac_id)].ar; + err_ring = &ar->dp.rxdma_err_dst_ring[ath11k_hw_mac_id_to_srng_id(&ab->hw_params, + mac_id)]; + rx_ring = &ar->dp.rx_refill_buf_ring; + srng = &ab->hal.srng_list[err_ring->ring_id]; spin_lock_bh(&srng->lock); @@ -4097,6 +4170,7 @@ int ath11k_dp_rx_pdev_alloc(struct ath11k_base *ab, int mac_id) struct ath11k *ar = ab->pdevs[mac_id].ar; struct ath11k_pdev_dp *dp = &ar->dp; u32 ring_id; + int i; int ret; ret = ath11k_dp_rx_pdev_srng_alloc(ar); @@ -4119,14 +4193,33 @@ int ath11k_dp_rx_pdev_alloc(struct ath11k_base *ab, int mac_id) return ret; } - ring_id = dp->rxdma_err_dst_ring.ring_id; - ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, mac_id, HAL_RXDMA_DST); - if (ret) { - ath11k_warn(ab, "failed to configure rxdma_err_dest_ring %d\n", - ret); - return ret; + if (ab->hw_params.rx_mac_buf_ring) { + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + ring_id = dp->rx_mac_buf_ring[i].ring_id; + ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, + mac_id + i, HAL_RXDMA_BUF); + if (ret) { + ath11k_warn(ab, "failed to configure rx_mac_buf_ring%d %d\n", + i, ret); + return ret; + } + } + } + + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + ring_id = dp->rxdma_err_dst_ring[i].ring_id; + ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, + mac_id + i, HAL_RXDMA_DST); + if (ret) { + ath11k_warn(ab, "failed to configure rxdma_err_dest_ring%d %d\n", + i, ret); + return ret; + } } + if (!ab->hw_params.rxdma1_enable) + goto config_refill_ring; + ring_id = dp->rxdma_mon_buf_ring.refill_buf_ring.ring_id; ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, mac_id, HAL_RXDMA_MONITOR_BUF); @@ -4151,15 +4244,20 @@ int ath11k_dp_rx_pdev_alloc(struct ath11k_base *ab, int mac_id) ret); return ret; } - ring_id = dp->rx_mon_status_refill_ring.refill_buf_ring.ring_id; - ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, mac_id, - HAL_RXDMA_MONITOR_STATUS); - if (ret) { - ath11k_warn(ab, - "failed to configure mon_status_refill_ring %d\n", - ret); - return ret; + +config_refill_ring: + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + ring_id = dp->rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_srng_setup(ab, ring_id, mac_id + i, + HAL_RXDMA_MONITOR_STATUS); + if (ret) { + ath11k_warn(ab, + "failed to configure mon_status_refill_ring%d %d\n", + i, ret); + return ret; + } } + return 0; } @@ -4777,7 +4875,7 @@ static void ath11k_dp_rx_mon_status_process_tlv(struct ath11k *ar, static int ath11k_dp_mon_process_rx(struct ath11k_base *ab, int mac_id, struct napi_struct *napi, int budget) { - struct ath11k *ar = ab->pdevs[mac_id].ar; + struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id); struct ath11k_pdev_dp *dp = &ar->dp; struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; int num_buffs_reaped = 0; @@ -4793,7 +4891,7 @@ static int ath11k_dp_mon_process_rx(struct ath11k_base *ab, int mac_id, int ath11k_dp_rx_process_mon_rings(struct ath11k_base *ab, int mac_id, struct napi_struct *napi, int budget) { - struct ath11k *ar = ab->pdevs[mac_id].ar; + struct ath11k *ar = ath11k_ab_to_ar(ab, mac_id); int ret = 0; if (test_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags)) @@ -4832,9 +4930,15 @@ int ath11k_dp_rx_pdev_mon_attach(struct ath11k *ar) return ret; } + /* if rxdma1_enable is false, no need to setup + * rxdma_mon_desc_ring. + */ + if (!ar->ab->hw_params.rxdma1_enable) + return 0; + dp_srng = &dp->rxdma_mon_desc_ring; n_link_desc = dp_srng->size / - ath11k_hal_srng_get_entrysize(HAL_RXDMA_MONITOR_DESC); + ath11k_hal_srng_get_entrysize(ar->ab, HAL_RXDMA_MONITOR_DESC); mon_desc_srng = &ar->ab->hal.srng_list[dp->rxdma_mon_desc_ring.ring_id]; @@ -4848,6 +4952,7 @@ int ath11k_dp_rx_pdev_mon_attach(struct ath11k *ar) pmon->mon_last_linkdesc_paddr = 0; pmon->mon_last_buf_cookie = DP_RX_DESC_COOKIE_MAX + 1; spin_lock_init(&pmon->mon_lock); + return 0; } diff --git a/drivers/net/wireless/ath/ath11k/dp_tx.c b/drivers/net/wireless/ath/ath11k/dp_tx.c index 1af76775b1a8..503993340113 100644 --- a/drivers/net/wireless/ath/ath11k/dp_tx.c +++ b/drivers/net/wireless/ath/ath11k/dp_tx.c @@ -110,7 +110,12 @@ int ath11k_dp_tx(struct ath11k *ar, struct ath11k_vif *arvif, tcl_ring_sel: tcl_ring_retry = false; - ti.ring_id = ring_selector % DP_TCL_NUM_RING_MAX; + /* For some chip, it can only use tcl0 to tx */ + if (ar->ab->hw_params.tcl_0_only) + ti.ring_id = 0; + else + ti.ring_id = ring_selector % DP_TCL_NUM_RING_MAX; + ring_map |= BIT(ti.ring_id); tx_ring = &dp->tx_ring[ti.ring_id]; @@ -221,7 +226,8 @@ tcl_ring_sel: * checking this ring earlier for each pkt tx. * Restart ring selection if some rings are not checked yet. */ - if (ring_map != (BIT(DP_TCL_NUM_RING_MAX) - 1)) { + if (ring_map != (BIT(DP_TCL_NUM_RING_MAX) - 1) && + !ar->ab->hw_params.tcl_0_only) { tcl_ring_retry = true; ring_selector++; } @@ -633,14 +639,28 @@ ath11k_dp_tx_get_ring_id_type(struct ath11k_base *ab, switch (ring_type) { case HAL_RXDMA_BUF: lmac_ring_id_offset = mac_id * HAL_SRNG_RINGS_PER_LMAC; - if (!(ring_id == (HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_BUF + - lmac_ring_id_offset) || - ring_id == (HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_BUF + - lmac_ring_id_offset))) { - ret = -EINVAL; + + /* for QCA6390, host fills rx buffer to fw and fw fills to + * rxbuf ring for each rxdma + */ + if (!ab->hw_params.rx_mac_buf_ring) { + if (!(ring_id == (HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_BUF + + lmac_ring_id_offset) || + ring_id == (HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_BUF + + lmac_ring_id_offset))) { + ret = -EINVAL; + } + *htt_ring_id = HTT_RXDMA_HOST_BUF_RING; + *htt_ring_type = HTT_SW_TO_HW_RING; + } else { + if (ring_id == HAL_SRNG_RING_ID_WMAC1_SW2RXDMA0_BUF) { + *htt_ring_id = HTT_HOST1_TO_FW_RXBUF_RING; + *htt_ring_type = HTT_SW_TO_SW_RING; + } else { + *htt_ring_id = HTT_RXDMA_HOST_BUF_RING; + *htt_ring_type = HTT_SW_TO_HW_RING; + } } - *htt_ring_id = HTT_RXDMA_HOST_BUF_RING; - *htt_ring_type = HTT_SW_TO_HW_RING; break; case HAL_RXDMA_DST: *htt_ring_id = HTT_RXDMA_NON_MONITOR_DEST_RING; @@ -720,7 +740,7 @@ int ath11k_dp_tx_htt_srng_setup(struct ath11k_base *ab, u32 ring_id, cmd->ring_base_addr_hi = (u64)params.ring_base_paddr >> HAL_ADDR_MSB_REG_SHIFT; - ret = ath11k_hal_srng_get_entrysize(ring_type); + ret = ath11k_hal_srng_get_entrysize(ab, ring_type); if (ret < 0) goto err_free; @@ -869,6 +889,12 @@ int ath11k_dp_tx_htt_rx_filter_setup(struct ath11k_base *ab, u32 ring_id, enum htt_srng_ring_id htt_ring_id; int ret; + /* HACK_KVALO: hack to workaround broken pktlog handling on + * QCA61390 + */ + if (!ab->hw_params.rxdma1_enable) + return 0; + skb = ath11k_htc_alloc_skb(ab, len); if (!skb) return -ENOMEM; @@ -968,8 +994,9 @@ ath11k_dp_tx_htt_h2t_ext_stats_req(struct ath11k *ar, u8 type, int ath11k_dp_tx_htt_monitor_mode_ring_config(struct ath11k *ar, bool reset) { struct ath11k_pdev_dp *dp = &ar->dp; + struct ath11k_base *ab = ar->ab; struct htt_rx_ring_tlv_filter tlv_filter = {0}; - int ret = 0, ring_id = 0; + int ret = 0, ring_id = 0, i; ring_id = dp->rxdma_mon_buf_ring.refill_buf_ring.ring_id; @@ -998,16 +1025,20 @@ int ath11k_dp_tx_htt_monitor_mode_ring_config(struct ath11k *ar, bool reset) if (ret) return ret; - ring_id = dp->rx_mon_status_refill_ring.refill_buf_ring.ring_id; - if (!reset) - tlv_filter.rx_filter = - HTT_RX_MON_FILTER_TLV_FLAGS_MON_STATUS_RING; - else - tlv_filter = ath11k_mac_mon_status_filter_default; + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + ring_id = dp->rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; + if (!reset) + tlv_filter.rx_filter = + HTT_RX_MON_FILTER_TLV_FLAGS_MON_STATUS_RING; + else + tlv_filter = ath11k_mac_mon_status_filter_default; + + ret = ath11k_dp_tx_htt_rx_filter_setup(ab, ring_id, + dp->mac_id + i, + HAL_RXDMA_MONITOR_STATUS, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + } - ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, dp->mac_id, - HAL_RXDMA_MONITOR_STATUS, - DP_RXDMA_REFILL_RING_SIZE, - &tlv_filter); return ret; } diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c index d63785178afa..5cd948f55b95 100644 --- a/drivers/net/wireless/ath/ath11k/hal.c +++ b/drivers/net/wireless/ath/ath11k/hal.c @@ -8,7 +8,7 @@ #include "hal_desc.h" #include "hif.h" -static const struct hal_srng_config hw_srng_config[] = { +static const struct hal_srng_config hw_srng_config_template[] = { /* TODO: max_rings can populated by querying HW capabilities */ { /* REO_DST */ .start_ring_id = HAL_SRNG_RING_ID_REO2SW1, @@ -16,14 +16,6 @@ static const struct hal_srng_config hw_srng_config[] = { .entry_size = sizeof(struct hal_reo_dest_ring) >> 2, .lmac_ring = false, .ring_dir = HAL_SRNG_DIR_DST, - .reg_start = { - HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_RING_BASE_LSB, - HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_RING_HP, - }, - .reg_size = { - HAL_REO2_RING_BASE_LSB - HAL_REO1_RING_BASE_LSB, - HAL_REO2_RING_HP - HAL_REO1_RING_HP, - }, .max_size = HAL_REO_REO2SW1_RING_BASE_MSB_RING_SIZE, }, { /* REO_EXCEPTION */ @@ -36,10 +28,6 @@ static const struct hal_srng_config hw_srng_config[] = { .entry_size = sizeof(struct hal_reo_dest_ring) >> 2, .lmac_ring = false, .ring_dir = HAL_SRNG_DIR_DST, - .reg_start = { - HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_TCL_RING_BASE_LSB, - HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_TCL_RING_HP, - }, .max_size = HAL_REO_REO2TCL_RING_BASE_MSB_RING_SIZE, }, { /* REO_REINJECT */ @@ -48,10 +36,6 @@ static const struct hal_srng_config hw_srng_config[] = { .entry_size = sizeof(struct hal_reo_entrance_ring) >> 2, .lmac_ring = false, .ring_dir = HAL_SRNG_DIR_SRC, - .reg_start = { - HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_BASE_LSB, - HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_HP, - }, .max_size = HAL_REO_SW2REO_RING_BASE_MSB_RING_SIZE, }, { /* REO_CMD */ @@ -61,10 +45,6 @@ static const struct hal_srng_config hw_srng_config[] = { sizeof(struct hal_reo_get_queue_stats)) >> 2, .lmac_ring = false, .ring_dir = HAL_SRNG_DIR_SRC, - .reg_start = { - HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_RING_BASE_LSB, - HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_HP, - }, .max_size = HAL_REO_CMD_RING_BASE_MSB_RING_SIZE, }, { /* REO_STATUS */ @@ -74,11 +54,6 @@ static const struct hal_srng_config hw_srng_config[] = { sizeof(struct hal_reo_get_queue_stats_status)) >> 2, .lmac_ring = false, .ring_dir = HAL_SRNG_DIR_DST, - .reg_start = { - HAL_SEQ_WCSS_UMAC_REO_REG + - HAL_REO_STATUS_RING_BASE_LSB, - HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_STATUS_HP, - }, .max_size = HAL_REO_STATUS_RING_BASE_MSB_RING_SIZE, }, { /* TCL_DATA */ @@ -88,14 +63,6 @@ static const struct hal_srng_config hw_srng_config[] = { sizeof(struct hal_tcl_data_cmd)) >> 2, .lmac_ring = false, .ring_dir = HAL_SRNG_DIR_SRC, - .reg_start = { - HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_BASE_LSB, - HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_HP, - }, - .reg_size = { - HAL_TCL2_RING_BASE_LSB - HAL_TCL1_RING_BASE_LSB, - HAL_TCL2_RING_HP - HAL_TCL1_RING_HP, - }, .max_size = HAL_SW2TCL1_RING_BASE_MSB_RING_SIZE, }, { /* TCL_CMD */ @@ -105,10 +72,6 @@ static const struct hal_srng_config hw_srng_config[] = { sizeof(struct hal_tcl_gse_cmd)) >> 2, .lmac_ring = false, .ring_dir = HAL_SRNG_DIR_SRC, - .reg_start = { - HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_RING_BASE_LSB, - HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_RING_HP, - }, .max_size = HAL_SW2TCL1_CMD_RING_BASE_MSB_RING_SIZE, }, { /* TCL_STATUS */ @@ -118,11 +81,6 @@ static const struct hal_srng_config hw_srng_config[] = { sizeof(struct hal_tcl_status_ring)) >> 2, .lmac_ring = false, .ring_dir = HAL_SRNG_DIR_DST, - .reg_start = { - HAL_SEQ_WCSS_UMAC_TCL_REG + - HAL_TCL_STATUS_RING_BASE_LSB, - HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_HP, - }, .max_size = HAL_TCL_STATUS_RING_BASE_MSB_RING_SIZE, }, { /* CE_SRC */ @@ -344,7 +302,7 @@ static void ath11k_hal_free_cont_wrp(struct ath11k_base *ab) static void ath11k_hal_ce_dst_setup(struct ath11k_base *ab, struct hal_srng *srng, int ring_num) { - const struct hal_srng_config *srng_config = &hw_srng_config[HAL_CE_DST]; + struct hal_srng_config *srng_config = &ab->hal.srng_config[HAL_CE_DST]; u32 addr; u32 val; @@ -371,7 +329,7 @@ static void ath11k_hal_srng_dst_hw_init(struct ath11k_base *ab, if (srng->flags & HAL_SRNG_FLAGS_MSI_INTR) { ath11k_hif_write32(ab, reg_base + - HAL_REO1_RING_MSI1_BASE_LSB_OFFSET, + HAL_REO1_RING_MSI1_BASE_LSB_OFFSET(ab), (u32)srng->msi_addr); val = FIELD_PREP(HAL_REO1_RING_MSI1_BASE_MSB_ADDR, @@ -379,10 +337,10 @@ static void ath11k_hal_srng_dst_hw_init(struct ath11k_base *ab, HAL_ADDR_MSB_REG_SHIFT)) | HAL_REO1_RING_MSI1_BASE_MSB_MSI1_ENABLE; ath11k_hif_write32(ab, reg_base + - HAL_REO1_RING_MSI1_BASE_MSB_OFFSET, val); + HAL_REO1_RING_MSI1_BASE_MSB_OFFSET(ab), val); ath11k_hif_write32(ab, - reg_base + HAL_REO1_RING_MSI1_DATA_OFFSET, + reg_base + HAL_REO1_RING_MSI1_DATA_OFFSET(ab), srng->msi_data); } @@ -393,11 +351,11 @@ static void ath11k_hal_srng_dst_hw_init(struct ath11k_base *ab, HAL_ADDR_MSB_REG_SHIFT)) | FIELD_PREP(HAL_REO1_RING_BASE_MSB_RING_SIZE, (srng->entry_size * srng->num_entries)); - ath11k_hif_write32(ab, reg_base + HAL_REO1_RING_BASE_MSB_OFFSET, val); + ath11k_hif_write32(ab, reg_base + HAL_REO1_RING_BASE_MSB_OFFSET(ab), val); val = FIELD_PREP(HAL_REO1_RING_ID_RING_ID, srng->ring_id) | FIELD_PREP(HAL_REO1_RING_ID_ENTRY_SIZE, srng->entry_size); - ath11k_hif_write32(ab, reg_base + HAL_REO1_RING_ID_OFFSET, val); + ath11k_hif_write32(ab, reg_base + HAL_REO1_RING_ID_OFFSET(ab), val); /* interrupt setup */ val = FIELD_PREP(HAL_REO1_RING_PRDR_INT_SETUP_INTR_TMR_THOLD, @@ -408,21 +366,21 @@ static void ath11k_hal_srng_dst_hw_init(struct ath11k_base *ab, srng->entry_size)); ath11k_hif_write32(ab, - reg_base + HAL_REO1_RING_PRODUCER_INT_SETUP_OFFSET, + reg_base + HAL_REO1_RING_PRODUCER_INT_SETUP_OFFSET(ab), val); hp_addr = hal->rdp.paddr + ((unsigned long)srng->u.dst_ring.hp_addr - (unsigned long)hal->rdp.vaddr); - ath11k_hif_write32(ab, reg_base + HAL_REO1_RING_HP_ADDR_LSB_OFFSET, + ath11k_hif_write32(ab, reg_base + HAL_REO1_RING_HP_ADDR_LSB_OFFSET(ab), hp_addr & HAL_ADDR_LSB_REG_MASK); - ath11k_hif_write32(ab, reg_base + HAL_REO1_RING_HP_ADDR_MSB_OFFSET, + ath11k_hif_write32(ab, reg_base + HAL_REO1_RING_HP_ADDR_MSB_OFFSET(ab), hp_addr >> HAL_ADDR_MSB_REG_SHIFT); /* Initialize head and tail pointers to indicate ring is empty */ reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R2]; ath11k_hif_write32(ab, reg_base, 0); - ath11k_hif_write32(ab, reg_base + HAL_REO1_RING_TP_OFFSET, 0); + ath11k_hif_write32(ab, reg_base + HAL_REO1_RING_TP_OFFSET(ab), 0); *srng->u.dst_ring.hp_addr = 0; reg_base = srng->hwreg_base[HAL_SRNG_REG_GRP_R0]; @@ -435,7 +393,7 @@ static void ath11k_hal_srng_dst_hw_init(struct ath11k_base *ab, val |= HAL_REO1_RING_MISC_MSI_SWAP; val |= HAL_REO1_RING_MISC_SRNG_ENABLE; - ath11k_hif_write32(ab, reg_base + HAL_REO1_RING_MISC_OFFSET, val); + ath11k_hif_write32(ab, reg_base + HAL_REO1_RING_MISC_OFFSET(ab), val); } static void ath11k_hal_srng_src_hw_init(struct ath11k_base *ab, @@ -450,7 +408,7 @@ static void ath11k_hal_srng_src_hw_init(struct ath11k_base *ab, if (srng->flags & HAL_SRNG_FLAGS_MSI_INTR) { ath11k_hif_write32(ab, reg_base + - HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET, + HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET(ab), (u32)srng->msi_addr); val = FIELD_PREP(HAL_TCL1_RING_MSI1_BASE_MSB_ADDR, @@ -458,11 +416,11 @@ static void ath11k_hal_srng_src_hw_init(struct ath11k_base *ab, HAL_ADDR_MSB_REG_SHIFT)) | HAL_TCL1_RING_MSI1_BASE_MSB_MSI1_ENABLE; ath11k_hif_write32(ab, reg_base + - HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET, + HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET(ab), val); ath11k_hif_write32(ab, reg_base + - HAL_TCL1_RING_MSI1_DATA_OFFSET, + HAL_TCL1_RING_MSI1_DATA_OFFSET(ab), srng->msi_data); } @@ -473,10 +431,10 @@ static void ath11k_hal_srng_src_hw_init(struct ath11k_base *ab, HAL_ADDR_MSB_REG_SHIFT)) | FIELD_PREP(HAL_TCL1_RING_BASE_MSB_RING_SIZE, (srng->entry_size * srng->num_entries)); - ath11k_hif_write32(ab, reg_base + HAL_TCL1_RING_BASE_MSB_OFFSET, val); + ath11k_hif_write32(ab, reg_base + HAL_TCL1_RING_BASE_MSB_OFFSET(ab), val); val = FIELD_PREP(HAL_REO1_RING_ID_ENTRY_SIZE, srng->entry_size); - ath11k_hif_write32(ab, reg_base + HAL_TCL1_RING_ID_OFFSET, val); + ath11k_hif_write32(ab, reg_base + HAL_TCL1_RING_ID_OFFSET(ab), val); /* interrupt setup */ /* NOTE: IPQ8074 v2 requires the interrupt timer threshold in the @@ -490,7 +448,7 @@ static void ath11k_hal_srng_src_hw_init(struct ath11k_base *ab, srng->entry_size)); ath11k_hif_write32(ab, - reg_base + HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET, + reg_base + HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET(ab), val); val = 0; @@ -499,7 +457,7 @@ static void ath11k_hal_srng_src_hw_init(struct ath11k_base *ab, srng->u.src_ring.low_threshold); } ath11k_hif_write32(ab, - reg_base + HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET, + reg_base + HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET(ab), val); if (srng->ring_id != HAL_SRNG_RING_ID_WBM_IDLE_LINK) { @@ -507,10 +465,10 @@ static void ath11k_hal_srng_src_hw_init(struct ath11k_base *ab, ((unsigned long)srng->u.src_ring.tp_addr - (unsigned long)hal->rdp.vaddr); ath11k_hif_write32(ab, - reg_base + HAL_TCL1_RING_TP_ADDR_LSB_OFFSET, + reg_base + HAL_TCL1_RING_TP_ADDR_LSB_OFFSET(ab), tp_addr & HAL_ADDR_LSB_REG_MASK); ath11k_hif_write32(ab, - reg_base + HAL_TCL1_RING_TP_ADDR_MSB_OFFSET, + reg_base + HAL_TCL1_RING_TP_ADDR_MSB_OFFSET(ab), tp_addr >> HAL_ADDR_MSB_REG_SHIFT); } @@ -534,7 +492,7 @@ static void ath11k_hal_srng_src_hw_init(struct ath11k_base *ab, val |= HAL_TCL1_RING_MISC_SRNG_ENABLE; - ath11k_hif_write32(ab, reg_base + HAL_TCL1_RING_MISC_OFFSET, val); + ath11k_hif_write32(ab, reg_base + HAL_TCL1_RING_MISC_OFFSET(ab), val); } static void ath11k_hal_srng_hw_init(struct ath11k_base *ab, @@ -550,7 +508,7 @@ static int ath11k_hal_srng_get_ring_id(struct ath11k_base *ab, enum hal_ring_type type, int ring_num, int mac_id) { - const struct hal_srng_config *srng_config = &hw_srng_config[type]; + struct hal_srng_config *srng_config = &ab->hal.srng_config[type]; int ring_id; if (ring_num >= srng_config->max_rings) { @@ -568,26 +526,26 @@ static int ath11k_hal_srng_get_ring_id(struct ath11k_base *ab, return ring_id; } -int ath11k_hal_srng_get_entrysize(u32 ring_type) +int ath11k_hal_srng_get_entrysize(struct ath11k_base *ab, u32 ring_type) { - const struct hal_srng_config *srng_config; + struct hal_srng_config *srng_config; if (WARN_ON(ring_type >= HAL_MAX_RING_TYPES)) return -EINVAL; - srng_config = &hw_srng_config[ring_type]; + srng_config = &ab->hal.srng_config[ring_type]; return (srng_config->entry_size << 2); } -int ath11k_hal_srng_get_max_entries(u32 ring_type) +int ath11k_hal_srng_get_max_entries(struct ath11k_base *ab, u32 ring_type) { - const struct hal_srng_config *srng_config; + struct hal_srng_config *srng_config; if (WARN_ON(ring_type >= HAL_MAX_RING_TYPES)) return -EINVAL; - srng_config = &hw_srng_config[ring_type]; + srng_config = &ab->hal.srng_config[ring_type]; return (srng_config->max_size / srng_config->entry_size); } @@ -1003,7 +961,7 @@ int ath11k_hal_srng_setup(struct ath11k_base *ab, enum hal_ring_type type, struct hal_srng_params *params) { struct ath11k_hal *hal = &ab->hal; - const struct hal_srng_config *srng_config = &hw_srng_config[type]; + struct hal_srng_config *srng_config = &ab->hal.srng_config[type]; struct hal_srng *srng; int ring_id; u32 lmac_idx; @@ -1027,6 +985,8 @@ int ath11k_hal_srng_setup(struct ath11k_base *ab, enum hal_ring_type type, params->intr_batch_cntr_thres_entries; srng->intr_timer_thres_us = params->intr_timer_thres_us; srng->flags = params->flags; + srng->msi_addr = params->msi_addr; + srng->msi_data = params->msi_data; srng->initialized = 1; spin_lock_init(&srng->lock); @@ -1085,7 +1045,7 @@ int ath11k_hal_srng_setup(struct ath11k_base *ab, enum hal_ring_type type, } else { srng->u.dst_ring.tp_addr = (u32 *)((unsigned long)ab->mem + reg_base + - (HAL_REO1_RING_TP - HAL_REO1_RING_HP)); + (HAL_REO1_RING_TP(ab) - HAL_REO1_RING_HP(ab))); } } @@ -1102,6 +1062,56 @@ int ath11k_hal_srng_setup(struct ath11k_base *ab, enum hal_ring_type type, return ring_id; } +static int ath11k_hal_srng_create_config(struct ath11k_base *ab) +{ + struct ath11k_hal *hal = &ab->hal; + struct hal_srng_config *s; + + hal->srng_config = kmemdup(hw_srng_config_template, + sizeof(hw_srng_config_template), + GFP_KERNEL); + if (!hal->srng_config) + return -ENOMEM; + + s = &hal->srng_config[HAL_REO_DST]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_RING_HP(ab); + s->reg_size[0] = HAL_REO2_RING_BASE_LSB(ab) - HAL_REO1_RING_BASE_LSB(ab); + s->reg_size[1] = HAL_REO2_RING_HP(ab) - HAL_REO1_RING_HP(ab); + + s = &hal->srng_config[HAL_REO_EXCEPTION]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_TCL_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_TCL_RING_HP(ab); + + s = &hal->srng_config[HAL_REO_REINJECT]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_BASE_LSB; + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_SW2REO_RING_HP; + + s = &hal->srng_config[HAL_REO_CMD]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_RING_BASE_LSB; + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_CMD_HP; + + s = &hal->srng_config[HAL_REO_STATUS]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_STATUS_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_STATUS_HP(ab); + + s = &hal->srng_config[HAL_TCL_DATA]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_HP; + s->reg_size[0] = HAL_TCL2_RING_BASE_LSB(ab) - HAL_TCL1_RING_BASE_LSB(ab); + s->reg_size[1] = HAL_TCL2_RING_HP - HAL_TCL1_RING_HP; + + s = &hal->srng_config[HAL_TCL_CMD]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_RING_HP; + + s = &hal->srng_config[HAL_TCL_STATUS]; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_BASE_LSB(ab); + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_HP; + + return 0; +} + int ath11k_hal_srng_init(struct ath11k_base *ab) { struct ath11k_hal *hal = &ab->hal; @@ -1109,7 +1119,9 @@ int ath11k_hal_srng_init(struct ath11k_base *ab) memset(hal, 0, sizeof(*hal)); - hal->srng_config = hw_srng_config; + ret = ath11k_hal_srng_create_config(ab); + if (ret) + goto err_hal; ret = ath11k_hal_alloc_cont_rdp(ab); if (ret) @@ -1127,12 +1139,17 @@ err_free_cont_rdp: err_hal: return ret; } +EXPORT_SYMBOL(ath11k_hal_srng_init); void ath11k_hal_srng_deinit(struct ath11k_base *ab) { + struct ath11k_hal *hal = &ab->hal; + ath11k_hal_free_cont_rdp(ab); ath11k_hal_free_cont_wrp(ab); + kfree(hal->srng_config); } +EXPORT_SYMBOL(ath11k_hal_srng_deinit); void ath11k_hal_dump_srng_stats(struct ath11k_base *ab) { @@ -1142,10 +1159,10 @@ void ath11k_hal_dump_srng_stats(struct ath11k_base *ab) int i; ath11k_err(ab, "Last interrupt received for each CE:\n"); - for (i = 0; i < CE_COUNT; i++) { + for (i = 0; i < ab->hw_params.ce_count; i++) { ce_pipe = &ab->ce.ce_pipe[i]; - if (ath11k_ce_get_attr_flags(i) & CE_ATTR_DIS_INTR) + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) continue; ath11k_err(ab, "CE_id %d pipe_num %d %ums before\n", diff --git a/drivers/net/wireless/ath/ath11k/hal.h b/drivers/net/wireless/ath/ath11k/hal.h index 780a3e11b609..85192d170b6b 100644 --- a/drivers/net/wireless/ath/ath11k/hal.h +++ b/drivers/net/wireless/ath/ath11k/hal.h @@ -46,40 +46,47 @@ struct ath11k_base; /* SW2TCL(x) R0 ring configuration address */ #define HAL_TCL1_RING_CMN_CTRL_REG 0x00000014 #define HAL_TCL1_RING_DSCP_TID_MAP 0x0000002c -#define HAL_TCL1_RING_BASE_LSB 0x00000510 -#define HAL_TCL1_RING_BASE_MSB 0x00000514 -#define HAL_TCL1_RING_ID 0x00000518 -#define HAL_TCL1_RING_MISC 0x00000520 -#define HAL_TCL1_RING_TP_ADDR_LSB 0x0000052c -#define HAL_TCL1_RING_TP_ADDR_MSB 0x00000530 -#define HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0 0x00000540 -#define HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1 0x00000544 -#define HAL_TCL1_RING_MSI1_BASE_LSB 0x00000558 -#define HAL_TCL1_RING_MSI1_BASE_MSB 0x0000055c -#define HAL_TCL1_RING_MSI1_DATA 0x00000560 -#define HAL_TCL2_RING_BASE_LSB 0x00000568 -#define HAL_TCL_RING_BASE_LSB 0x00000618 - -#define HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET \ - (HAL_TCL1_RING_MSI1_BASE_LSB - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET \ - (HAL_TCL1_RING_MSI1_BASE_MSB - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_MSI1_DATA_OFFSET \ - (HAL_TCL1_RING_MSI1_DATA - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_BASE_MSB_OFFSET \ - (HAL_TCL1_RING_BASE_MSB - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_ID_OFFSET \ - (HAL_TCL1_RING_ID - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET \ - (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0 - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET \ - (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1 - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_TP_ADDR_LSB_OFFSET \ - (HAL_TCL1_RING_TP_ADDR_LSB - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_TP_ADDR_MSB_OFFSET \ - (HAL_TCL1_RING_TP_ADDR_MSB - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_MISC_OFFSET \ - (HAL_TCL1_RING_MISC - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_BASE_LSB(ab) ab->hw_params.regs->hal_tcl1_ring_base_lsb +#define HAL_TCL1_RING_BASE_MSB(ab) ab->hw_params.regs->hal_tcl1_ring_base_msb +#define HAL_TCL1_RING_ID(ab) ab->hw_params.regs->hal_tcl1_ring_id +#define HAL_TCL1_RING_MISC(ab) ab->hw_params.regs->hal_tcl1_ring_misc +#define HAL_TCL1_RING_TP_ADDR_LSB(ab) \ + ab->hw_params.regs->hal_tcl1_ring_tp_addr_lsb +#define HAL_TCL1_RING_TP_ADDR_MSB(ab) \ + ab->hw_params.regs->hal_tcl1_ring_tp_addr_msb +#define HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0(ab) \ + ab->hw_params.regs->hal_tcl1_ring_consumer_int_setup_ix0 +#define HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1(ab) \ + ab->hw_params.regs->hal_tcl1_ring_consumer_int_setup_ix1 +#define HAL_TCL1_RING_MSI1_BASE_LSB(ab) \ + ab->hw_params.regs->hal_tcl1_ring_msi1_base_lsb +#define HAL_TCL1_RING_MSI1_BASE_MSB(ab) \ + ab->hw_params.regs->hal_tcl1_ring_msi1_base_msb +#define HAL_TCL1_RING_MSI1_DATA(ab) \ + ab->hw_params.regs->hal_tcl1_ring_msi1_data +#define HAL_TCL2_RING_BASE_LSB(ab) ab->hw_params.regs->hal_tcl2_ring_base_lsb +#define HAL_TCL_RING_BASE_LSB(ab) ab->hw_params.regs->hal_tcl_ring_base_lsb + +#define HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET(ab) \ + (HAL_TCL1_RING_MSI1_BASE_LSB(ab) - HAL_TCL1_RING_BASE_LSB(ab)) +#define HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET(ab) \ + (HAL_TCL1_RING_MSI1_BASE_MSB(ab) - HAL_TCL1_RING_BASE_LSB(ab)) +#define HAL_TCL1_RING_MSI1_DATA_OFFSET(ab) \ + (HAL_TCL1_RING_MSI1_DATA(ab) - HAL_TCL1_RING_BASE_LSB(ab)) +#define HAL_TCL1_RING_BASE_MSB_OFFSET(ab) \ + (HAL_TCL1_RING_BASE_MSB(ab) - HAL_TCL1_RING_BASE_LSB(ab)) +#define HAL_TCL1_RING_ID_OFFSET(ab) \ + (HAL_TCL1_RING_ID(ab) - HAL_TCL1_RING_BASE_LSB(ab)) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET(ab) \ + (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0(ab) - HAL_TCL1_RING_BASE_LSB(ab)) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET(ab) \ + (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1(ab) - HAL_TCL1_RING_BASE_LSB(ab)) +#define HAL_TCL1_RING_TP_ADDR_LSB_OFFSET(ab) \ + (HAL_TCL1_RING_TP_ADDR_LSB(ab) - HAL_TCL1_RING_BASE_LSB(ab)) +#define HAL_TCL1_RING_TP_ADDR_MSB_OFFSET(ab) \ + (HAL_TCL1_RING_TP_ADDR_MSB(ab) - HAL_TCL1_RING_BASE_LSB(ab)) +#define HAL_TCL1_RING_MISC_OFFSET(ab) \ + (HAL_TCL1_RING_MISC(ab) - HAL_TCL1_RING_BASE_LSB(ab)) /* SW2TCL(x) R2 ring pointers (head/tail) address */ #define HAL_TCL1_RING_HP 0x00002000 @@ -91,7 +98,8 @@ struct ath11k_base; (HAL_TCL1_RING_TP - HAL_TCL1_RING_HP) /* TCL STATUS ring address */ -#define HAL_TCL_STATUS_RING_BASE_LSB 0x00000720 +#define HAL_TCL_STATUS_RING_BASE_LSB(ab) \ + ab->hw_params.regs->hal_tcl_status_ring_base_lsb #define HAL_TCL_STATUS_RING_HP 0x00002030 /* REO2SW(x) R0 ring configuration address */ @@ -100,51 +108,63 @@ struct ath11k_base; #define HAL_REO1_DEST_RING_CTRL_IX_1 0x00000008 #define HAL_REO1_DEST_RING_CTRL_IX_2 0x0000000c #define HAL_REO1_DEST_RING_CTRL_IX_3 0x00000010 -#define HAL_REO1_RING_BASE_LSB 0x0000029c -#define HAL_REO1_RING_BASE_MSB 0x000002a0 -#define HAL_REO1_RING_ID 0x000002a4 -#define HAL_REO1_RING_MISC 0x000002ac -#define HAL_REO1_RING_HP_ADDR_LSB 0x000002b0 -#define HAL_REO1_RING_HP_ADDR_MSB 0x000002b4 -#define HAL_REO1_RING_PRODUCER_INT_SETUP 0x000002c0 -#define HAL_REO1_RING_MSI1_BASE_LSB 0x000002e4 -#define HAL_REO1_RING_MSI1_BASE_MSB 0x000002e8 -#define HAL_REO1_RING_MSI1_DATA 0x000002ec -#define HAL_REO2_RING_BASE_LSB 0x000002f4 -#define HAL_REO1_AGING_THRESH_IX_0 0x00000564 -#define HAL_REO1_AGING_THRESH_IX_1 0x00000568 -#define HAL_REO1_AGING_THRESH_IX_2 0x0000056c -#define HAL_REO1_AGING_THRESH_IX_3 0x00000570 - -#define HAL_REO1_RING_MSI1_BASE_LSB_OFFSET \ - (HAL_REO1_RING_MSI1_BASE_LSB - HAL_REO1_RING_BASE_LSB) -#define HAL_REO1_RING_MSI1_BASE_MSB_OFFSET \ - (HAL_REO1_RING_MSI1_BASE_MSB - HAL_REO1_RING_BASE_LSB) -#define HAL_REO1_RING_MSI1_DATA_OFFSET \ - (HAL_REO1_RING_MSI1_DATA - HAL_REO1_RING_BASE_LSB) -#define HAL_REO1_RING_BASE_MSB_OFFSET \ - (HAL_REO1_RING_BASE_MSB - HAL_REO1_RING_BASE_LSB) -#define HAL_REO1_RING_ID_OFFSET (HAL_REO1_RING_ID - HAL_REO1_RING_BASE_LSB) -#define HAL_REO1_RING_PRODUCER_INT_SETUP_OFFSET \ - (HAL_REO1_RING_PRODUCER_INT_SETUP - HAL_REO1_RING_BASE_LSB) -#define HAL_REO1_RING_HP_ADDR_LSB_OFFSET \ - (HAL_REO1_RING_HP_ADDR_LSB - HAL_REO1_RING_BASE_LSB) -#define HAL_REO1_RING_HP_ADDR_MSB_OFFSET \ - (HAL_REO1_RING_HP_ADDR_MSB - HAL_REO1_RING_BASE_LSB) -#define HAL_REO1_RING_MISC_OFFSET (HAL_REO1_RING_MISC - HAL_REO1_RING_BASE_LSB) +#define HAL_REO1_RING_BASE_LSB(ab) ab->hw_params.regs->hal_reo1_ring_base_lsb +#define HAL_REO1_RING_BASE_MSB(ab) ab->hw_params.regs->hal_reo1_ring_base_msb +#define HAL_REO1_RING_ID(ab) ab->hw_params.regs->hal_reo1_ring_id +#define HAL_REO1_RING_MISC(ab) ab->hw_params.regs->hal_reo1_ring_misc +#define HAL_REO1_RING_HP_ADDR_LSB(ab) \ + ab->hw_params.regs->hal_reo1_ring_hp_addr_lsb +#define HAL_REO1_RING_HP_ADDR_MSB(ab) \ + ab->hw_params.regs->hal_reo1_ring_hp_addr_msb +#define HAL_REO1_RING_PRODUCER_INT_SETUP(ab) \ + ab->hw_params.regs->hal_reo1_ring_producer_int_setup +#define HAL_REO1_RING_MSI1_BASE_LSB(ab) \ + ab->hw_params.regs->hal_reo1_ring_msi1_base_lsb +#define HAL_REO1_RING_MSI1_BASE_MSB(ab) \ + ab->hw_params.regs->hal_reo1_ring_msi1_base_msb +#define HAL_REO1_RING_MSI1_DATA(ab) \ + ab->hw_params.regs->hal_reo1_ring_msi1_data +#define HAL_REO2_RING_BASE_LSB(ab) ab->hw_params.regs->hal_reo2_ring_base_lsb +#define HAL_REO1_AGING_THRESH_IX_0(ab) \ + ab->hw_params.regs->hal_reo1_aging_thresh_ix_0 +#define HAL_REO1_AGING_THRESH_IX_1(ab) \ + ab->hw_params.regs->hal_reo1_aging_thresh_ix_1 +#define HAL_REO1_AGING_THRESH_IX_2(ab) \ + ab->hw_params.regs->hal_reo1_aging_thresh_ix_2 +#define HAL_REO1_AGING_THRESH_IX_3(ab) \ + ab->hw_params.regs->hal_reo1_aging_thresh_ix_3 + +#define HAL_REO1_RING_MSI1_BASE_LSB_OFFSET(ab) \ + (HAL_REO1_RING_MSI1_BASE_LSB(ab) - HAL_REO1_RING_BASE_LSB(ab)) +#define HAL_REO1_RING_MSI1_BASE_MSB_OFFSET(ab) \ + (HAL_REO1_RING_MSI1_BASE_MSB(ab) - HAL_REO1_RING_BASE_LSB(ab)) +#define HAL_REO1_RING_MSI1_DATA_OFFSET(ab) \ + (HAL_REO1_RING_MSI1_DATA(ab) - HAL_REO1_RING_BASE_LSB(ab)) +#define HAL_REO1_RING_BASE_MSB_OFFSET(ab) \ + (HAL_REO1_RING_BASE_MSB(ab) - HAL_REO1_RING_BASE_LSB(ab)) +#define HAL_REO1_RING_ID_OFFSET(ab) (HAL_REO1_RING_ID(ab) - HAL_REO1_RING_BASE_LSB(ab)) +#define HAL_REO1_RING_PRODUCER_INT_SETUP_OFFSET(ab) \ + (HAL_REO1_RING_PRODUCER_INT_SETUP(ab) - HAL_REO1_RING_BASE_LSB(ab)) +#define HAL_REO1_RING_HP_ADDR_LSB_OFFSET(ab) \ + (HAL_REO1_RING_HP_ADDR_LSB(ab) - HAL_REO1_RING_BASE_LSB(ab)) +#define HAL_REO1_RING_HP_ADDR_MSB_OFFSET(ab) \ + (HAL_REO1_RING_HP_ADDR_MSB(ab) - HAL_REO1_RING_BASE_LSB(ab)) +#define HAL_REO1_RING_MISC_OFFSET(ab) \ + (HAL_REO1_RING_MISC(ab) - HAL_REO1_RING_BASE_LSB(ab)) /* REO2SW(x) R2 ring pointers (head/tail) address */ -#define HAL_REO1_RING_HP 0x00003038 -#define HAL_REO1_RING_TP 0x0000303c -#define HAL_REO2_RING_HP 0x00003040 +#define HAL_REO1_RING_HP(ab) ab->hw_params.regs->hal_reo1_ring_hp +#define HAL_REO1_RING_TP(ab) ab->hw_params.regs->hal_reo1_ring_tp +#define HAL_REO2_RING_HP(ab) ab->hw_params.regs->hal_reo2_ring_hp -#define HAL_REO1_RING_TP_OFFSET (HAL_REO1_RING_TP - HAL_REO1_RING_HP) +#define HAL_REO1_RING_TP_OFFSET(ab) (HAL_REO1_RING_TP(ab) - HAL_REO1_RING_HP(ab)) /* REO2TCL R0 ring configuration address */ -#define HAL_REO_TCL_RING_BASE_LSB 0x000003fc +#define HAL_REO_TCL_RING_BASE_LSB(ab) \ + ab->hw_params.regs->hal_reo_tcl_ring_base_lsb /* REO2TCL R2 ring pointer (head/tail) address */ -#define HAL_REO_TCL_RING_HP 0x00003058 +#define HAL_REO_TCL_RING_HP(ab) ab->hw_params.regs->hal_reo_tcl_ring_hp /* REO CMD R0 address */ #define HAL_REO_CMD_RING_BASE_LSB 0x00000194 @@ -168,8 +188,9 @@ struct ath11k_base; #define HAL_CE_DST_STATUS_RING_HP 0x00000408 /* REO status address */ -#define HAL_REO_STATUS_RING_BASE_LSB 0x00000504 -#define HAL_REO_STATUS_HP 0x00003070 +#define HAL_REO_STATUS_RING_BASE_LSB(ab) \ + ab->hw_params.regs->hal_reo_status_ring_base_lsb +#define HAL_REO_STATUS_HP(ab) ab->hw_params.regs->hal_reo_status_hp /* WBM Idle R0 address */ #define HAL_WBM_IDLE_LINK_RING_BASE_LSB 0x00000860 @@ -458,6 +479,8 @@ struct hal_srng_params { u32 flags; u32 max_buffer_len; u32 low_threshold; + dma_addr_t msi_addr; + u32 msi_data; /* Add more params as needed */ }; @@ -839,7 +862,7 @@ struct ath11k_hal { struct hal_srng srng_list[HAL_SRNG_RING_ID_MAX]; /* SRNG configuration table */ - const struct hal_srng_config *srng_config; + struct hal_srng_config *srng_config; /* Remote pointer memory for HW/FW updates */ struct { @@ -885,8 +908,8 @@ void ath11k_hal_ce_src_set_desc(void *buf, dma_addr_t paddr, u32 len, u32 id, u8 byte_swap_data); void ath11k_hal_ce_dst_set_desc(void *buf, dma_addr_t paddr); u32 ath11k_hal_ce_dst_status_get_length(void *buf); -int ath11k_hal_srng_get_entrysize(u32 ring_type); -int ath11k_hal_srng_get_max_entries(u32 ring_type); +int ath11k_hal_srng_get_entrysize(struct ath11k_base *ab, u32 ring_type); +int ath11k_hal_srng_get_max_entries(struct ath11k_base *ab, u32 ring_type); void ath11k_hal_srng_get_params(struct ath11k_base *ab, struct hal_srng *srng, struct hal_srng_params *params); u32 *ath11k_hal_srng_dst_get_next_entry(struct ath11k_base *ab, diff --git a/drivers/net/wireless/ath/ath11k/hal_rx.c b/drivers/net/wireless/ath/ath11k/hal_rx.c index 129c9e1efeb9..4bbad2e341ee 100644 --- a/drivers/net/wireless/ath/ath11k/hal_rx.c +++ b/drivers/net/wireless/ath/ath11k/hal_rx.c @@ -786,7 +786,7 @@ void ath11k_hal_reo_init_cmd_ring(struct ath11k_base *ab, memset(¶ms, 0, sizeof(params)); - entry_size = ath11k_hal_srng_get_entrysize(HAL_REO_CMD); + entry_size = ath11k_hal_srng_get_entrysize(ab, HAL_REO_CMD); ath11k_hal_srng_get_params(ab, srng, ¶ms); entry = (u8 *)params.ring_base_vaddr; @@ -813,13 +813,13 @@ void ath11k_hal_reo_hw_setup(struct ath11k_base *ab, u32 ring_hash_map) FIELD_PREP(HAL_REO1_GEN_ENABLE_AGING_FLUSH_ENABLE, 1); ath11k_hif_write32(ab, reo_base + HAL_REO1_GEN_ENABLE, val); - ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_0, + ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_0(ab), HAL_DEFAULT_REO_TIMEOUT_USEC); - ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_1, + ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_1(ab), HAL_DEFAULT_REO_TIMEOUT_USEC); - ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_2, + ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_2(ab), HAL_DEFAULT_REO_TIMEOUT_USEC); - ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_3, + ath11k_hif_write32(ab, reo_base + HAL_REO1_AGING_THRESH_IX_3(ab), HAL_DEFAULT_REO_TIMEOUT_USEC); ath11k_hif_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_0, diff --git a/drivers/net/wireless/ath/ath11k/hal_tx.c b/drivers/net/wireless/ath/ath11k/hal_tx.c index 81937c29ffca..a755aa86c5de 100644 --- a/drivers/net/wireless/ath/ath11k/hal_tx.c +++ b/drivers/net/wireless/ath/ath11k/hal_tx.c @@ -141,7 +141,7 @@ void ath11k_hal_tx_init_data_ring(struct ath11k_base *ab, struct hal_srng *srng) memset(¶ms, 0, sizeof(params)); - entry_size = ath11k_hal_srng_get_entrysize(HAL_TCL_DATA); + entry_size = ath11k_hal_srng_get_entrysize(ab, HAL_TCL_DATA); ath11k_hal_srng_get_params(ab, srng, ¶ms); desc = (u8 *)params.ring_base_vaddr; diff --git a/drivers/net/wireless/ath/ath11k/hif.h b/drivers/net/wireless/ath/ath11k/hif.h index 165f7e51c238..dbe5568916e8 100644 --- a/drivers/net/wireless/ath/ath11k/hif.h +++ b/drivers/net/wireless/ath/ath11k/hif.h @@ -3,6 +3,9 @@ * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. */ +#ifndef _HIF_H_ +#define _HIF_H_ + #include "core.h" struct ath11k_hif_ops { @@ -16,6 +19,11 @@ struct ath11k_hif_ops { void (*power_down)(struct ath11k_base *sc); int (*map_service_to_pipe)(struct ath11k_base *sc, u16 service_id, u8 *ul_pipe, u8 *dl_pipe); + int (*get_user_msi_vector)(struct ath11k_base *ab, char *user_name, + int *num_vectors, u32 *user_base_data, + u32 *base_vector); + void (*get_msi_address)(struct ath11k_base *ab, u32 *msi_addr_lo, + u32 *msi_addr_hi); }; static inline int ath11k_hif_start(struct ath11k_base *sc) @@ -63,3 +71,25 @@ static inline int ath11k_hif_map_service_to_pipe(struct ath11k_base *sc, u16 ser { return sc->hif.ops->map_service_to_pipe(sc, service_id, ul_pipe, dl_pipe); } + +static inline int ath11k_get_user_msi_vector(struct ath11k_base *ab, char *user_name, + int *num_vectors, u32 *user_base_data, + u32 *base_vector) +{ + if (!ab->hif.ops->get_user_msi_vector) + return -EOPNOTSUPP; + + return ab->hif.ops->get_user_msi_vector(ab, user_name, num_vectors, + user_base_data, + base_vector); +} + +static inline void ath11k_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo, + u32 *msi_addr_hi) +{ + if (!ab->hif.ops->get_msi_address) + return; + + ab->hif.ops->get_msi_address(ab, msi_addr_lo, msi_addr_hi); +} +#endif /* _HIF_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/htc.c b/drivers/net/wireless/ath/ath11k/htc.c index ad13c648b679..e9e354fc11fa 100644 --- a/drivers/net/wireless/ath/ath11k/htc.c +++ b/drivers/net/wireless/ath/ath11k/htc.c @@ -478,7 +478,7 @@ int ath11k_htc_wait_target(struct ath11k_htc *htc) if (!time_left) { ath11k_warn(ab, "failed to receive control response completion, polling..\n"); - for (i = 0; i < CE_COUNT; i++) + for (i = 0; i < ab->hw_params.ce_count; i++) ath11k_ce_per_engine_service(htc->ab, i); time_left = @@ -748,7 +748,7 @@ int ath11k_htc_init(struct ath11k_base *ab) htc->wmi_ep_count = 3; break; default: - htc->wmi_ep_count = 3; + htc->wmi_ep_count = ab->hw_params.max_radios; break; } diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c new file mode 100644 index 000000000000..e0788994ea22 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/hw.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved. + */ + +#include <linux/types.h> +#include <linux/bitops.h> +#include <linux/bitfield.h> + +#include "hw.h" +#include "core.h" +#include "ce.h" + +/* Map from pdev index to hw mac index */ +static u8 ath11k_hw_ipq8074_mac_from_pdev_id(int pdev_idx) +{ + switch (pdev_idx) { + case 0: + return 0; + case 1: + return 2; + case 2: + return 1; + default: + return ATH11K_INVALID_HW_MAC_ID; + } +} + +static u8 ath11k_hw_ipq6018_mac_from_pdev_id(int pdev_idx) +{ + return pdev_idx; +} + +static void ath11k_init_wmi_config_qca6390(struct ath11k_base *ab, + struct target_resource_config *config) +{ + config->num_vdevs = 4; + config->num_peers = 16; + config->num_tids = 32; + + config->num_offload_peers = 3; + config->num_offload_reorder_buffs = 3; + config->num_peer_keys = TARGET_NUM_PEER_KEYS; + config->ast_skid_limit = TARGET_AST_SKID_LIMIT; + config->tx_chain_mask = (1 << ab->target_caps.num_rf_chains) - 1; + config->rx_chain_mask = (1 << ab->target_caps.num_rf_chains) - 1; + config->rx_timeout_pri[0] = TARGET_RX_TIMEOUT_LO_PRI; + config->rx_timeout_pri[1] = TARGET_RX_TIMEOUT_LO_PRI; + config->rx_timeout_pri[2] = TARGET_RX_TIMEOUT_LO_PRI; + config->rx_timeout_pri[3] = TARGET_RX_TIMEOUT_HI_PRI; + config->rx_decap_mode = TARGET_DECAP_MODE_NATIVE_WIFI; + config->scan_max_pending_req = TARGET_SCAN_MAX_PENDING_REQS; + config->bmiss_offload_max_vdev = TARGET_BMISS_OFFLOAD_MAX_VDEV; + config->roam_offload_max_vdev = TARGET_ROAM_OFFLOAD_MAX_VDEV; + config->roam_offload_max_ap_profiles = TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES; + config->num_mcast_groups = 0; + config->num_mcast_table_elems = 0; + config->mcast2ucast_mode = 0; + config->tx_dbg_log_size = TARGET_TX_DBG_LOG_SIZE; + config->num_wds_entries = 0; + config->dma_burst_size = 0; + config->rx_skip_defrag_timeout_dup_detection_check = 0; + config->vow_config = TARGET_VOW_CONFIG; + config->gtk_offload_max_vdev = 2; + config->num_msdu_desc = 0x400; + config->beacon_tx_offload_max_vdev = 2; + config->rx_batchmode = TARGET_RX_BATCHMODE; + + config->peer_map_unmap_v2_support = 0; + config->use_pdev_id = 1; + config->max_frag_entries = 0xa; + config->num_tdls_vdevs = 0x1; + config->num_tdls_conn_table_entries = 8; + config->beacon_tx_offload_max_vdev = 0x2; + config->num_multicast_filter_entries = 0x20; + config->num_wow_filters = 0x16; + config->num_keep_alive_pattern = 0x1; + config->num_keep_alive_pattern = 0; +} + +static void ath11k_init_wmi_config_ipq8074(struct ath11k_base *ab, + struct target_resource_config *config) +{ + config->num_vdevs = ab->num_radios * TARGET_NUM_VDEVS; + + if (ab->num_radios == 2) { + config->num_peers = TARGET_NUM_PEERS(DBS); + config->num_tids = TARGET_NUM_TIDS(DBS); + } else if (ab->num_radios == 3) { + config->num_peers = TARGET_NUM_PEERS(DBS_SBS); + config->num_tids = TARGET_NUM_TIDS(DBS_SBS); + } else { + /* Control should not reach here */ + config->num_peers = TARGET_NUM_PEERS(SINGLE); + config->num_tids = TARGET_NUM_TIDS(SINGLE); + } + config->num_offload_peers = TARGET_NUM_OFFLD_PEERS; + config->num_offload_reorder_buffs = TARGET_NUM_OFFLD_REORDER_BUFFS; + config->num_peer_keys = TARGET_NUM_PEER_KEYS; + config->ast_skid_limit = TARGET_AST_SKID_LIMIT; + config->tx_chain_mask = (1 << ab->target_caps.num_rf_chains) - 1; + config->rx_chain_mask = (1 << ab->target_caps.num_rf_chains) - 1; + config->rx_timeout_pri[0] = TARGET_RX_TIMEOUT_LO_PRI; + config->rx_timeout_pri[1] = TARGET_RX_TIMEOUT_LO_PRI; + config->rx_timeout_pri[2] = TARGET_RX_TIMEOUT_LO_PRI; + config->rx_timeout_pri[3] = TARGET_RX_TIMEOUT_HI_PRI; + config->rx_decap_mode = TARGET_DECAP_MODE_NATIVE_WIFI; + config->scan_max_pending_req = TARGET_SCAN_MAX_PENDING_REQS; + config->bmiss_offload_max_vdev = TARGET_BMISS_OFFLOAD_MAX_VDEV; + config->roam_offload_max_vdev = TARGET_ROAM_OFFLOAD_MAX_VDEV; + config->roam_offload_max_ap_profiles = TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES; + config->num_mcast_groups = TARGET_NUM_MCAST_GROUPS; + config->num_mcast_table_elems = TARGET_NUM_MCAST_TABLE_ELEMS; + config->mcast2ucast_mode = TARGET_MCAST2UCAST_MODE; + config->tx_dbg_log_size = TARGET_TX_DBG_LOG_SIZE; + config->num_wds_entries = TARGET_NUM_WDS_ENTRIES; + config->dma_burst_size = TARGET_DMA_BURST_SIZE; + config->rx_skip_defrag_timeout_dup_detection_check = + TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK; + config->vow_config = TARGET_VOW_CONFIG; + config->gtk_offload_max_vdev = TARGET_GTK_OFFLOAD_MAX_VDEV; + config->num_msdu_desc = TARGET_NUM_MSDU_DESC; + config->beacon_tx_offload_max_vdev = ab->num_radios * TARGET_MAX_BCN_OFFLD; + config->rx_batchmode = TARGET_RX_BATCHMODE; + config->peer_map_unmap_v2_support = 1; + config->twt_ap_pdev_count = 2; + config->twt_ap_sta_count = 1000; +} + +static int ath11k_hw_mac_id_to_pdev_id_ipq8074(struct ath11k_hw_params *hw, + int mac_id) +{ + return mac_id; +} + +static int ath11k_hw_mac_id_to_srng_id_ipq8074(struct ath11k_hw_params *hw, + int mac_id) +{ + return 0; +} + +static int ath11k_hw_mac_id_to_pdev_id_qca6390(struct ath11k_hw_params *hw, + int mac_id) +{ + return 0; +} + +static int ath11k_hw_mac_id_to_srng_id_qca6390(struct ath11k_hw_params *hw, + int mac_id) +{ + return mac_id; +} + +const struct ath11k_hw_ops ipq8074_ops = { + .get_hw_mac_from_pdev_id = ath11k_hw_ipq8074_mac_from_pdev_id, + .wmi_init_config = ath11k_init_wmi_config_qca6390, + .mac_id_to_pdev_id = ath11k_hw_mac_id_to_pdev_id_ipq8074, + .mac_id_to_srng_id = ath11k_hw_mac_id_to_srng_id_ipq8074, +}; + +const struct ath11k_hw_ops ipq6018_ops = { + .get_hw_mac_from_pdev_id = ath11k_hw_ipq6018_mac_from_pdev_id, + .wmi_init_config = ath11k_init_wmi_config_ipq8074, + .mac_id_to_pdev_id = ath11k_hw_mac_id_to_pdev_id_ipq8074, + .mac_id_to_srng_id = ath11k_hw_mac_id_to_srng_id_ipq8074, +}; + +const struct ath11k_hw_ops qca6390_ops = { + .get_hw_mac_from_pdev_id = ath11k_hw_ipq8074_mac_from_pdev_id, + .wmi_init_config = ath11k_init_wmi_config_qca6390, + .mac_id_to_pdev_id = ath11k_hw_mac_id_to_pdev_id_qca6390, + .mac_id_to_srng_id = ath11k_hw_mac_id_to_srng_id_qca6390, +}; + +#define ATH11K_TX_RING_MASK_0 0x1 +#define ATH11K_TX_RING_MASK_1 0x2 +#define ATH11K_TX_RING_MASK_2 0x4 + +#define ATH11K_RX_RING_MASK_0 0x1 +#define ATH11K_RX_RING_MASK_1 0x2 +#define ATH11K_RX_RING_MASK_2 0x4 +#define ATH11K_RX_RING_MASK_3 0x8 + +#define ATH11K_RX_ERR_RING_MASK_0 0x1 + +#define ATH11K_RX_WBM_REL_RING_MASK_0 0x1 + +#define ATH11K_REO_STATUS_RING_MASK_0 0x1 + +#define ATH11K_RXDMA2HOST_RING_MASK_0 0x1 +#define ATH11K_RXDMA2HOST_RING_MASK_1 0x2 +#define ATH11K_RXDMA2HOST_RING_MASK_2 0x4 + +#define ATH11K_HOST2RXDMA_RING_MASK_0 0x1 +#define ATH11K_HOST2RXDMA_RING_MASK_1 0x2 +#define ATH11K_HOST2RXDMA_RING_MASK_2 0x4 + +#define ATH11K_RX_MON_STATUS_RING_MASK_0 0x1 +#define ATH11K_RX_MON_STATUS_RING_MASK_1 0x2 +#define ATH11K_RX_MON_STATUS_RING_MASK_2 0x4 + +const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_ipq8074 = { + .tx = { + ATH11K_TX_RING_MASK_0, + ATH11K_TX_RING_MASK_1, + ATH11K_TX_RING_MASK_2, + }, + .rx_mon_status = { + 0, 0, 0, 0, + ATH11K_RX_MON_STATUS_RING_MASK_0, + ATH11K_RX_MON_STATUS_RING_MASK_1, + ATH11K_RX_MON_STATUS_RING_MASK_2, + }, + .rx = { + 0, 0, 0, 0, 0, 0, 0, + ATH11K_RX_RING_MASK_0, + ATH11K_RX_RING_MASK_1, + ATH11K_RX_RING_MASK_2, + ATH11K_RX_RING_MASK_3, + }, + .rx_err = { + ATH11K_RX_ERR_RING_MASK_0, + }, + .rx_wbm_rel = { + ATH11K_RX_WBM_REL_RING_MASK_0, + }, + .reo_status = { + ATH11K_REO_STATUS_RING_MASK_0, + }, + .rxdma2host = { + ATH11K_RXDMA2HOST_RING_MASK_0, + ATH11K_RXDMA2HOST_RING_MASK_1, + ATH11K_RXDMA2HOST_RING_MASK_2, + }, + .host2rxdma = { + ATH11K_HOST2RXDMA_RING_MASK_0, + ATH11K_HOST2RXDMA_RING_MASK_1, + ATH11K_HOST2RXDMA_RING_MASK_2, + }, +}; + +const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qca6390 = { + .tx = { + ATH11K_TX_RING_MASK_0, + ATH11K_TX_RING_MASK_1, + ATH11K_TX_RING_MASK_2, + }, + .rx_mon_status = { + 0, 0, 0, 0, + ATH11K_RX_MON_STATUS_RING_MASK_0, + ATH11K_RX_MON_STATUS_RING_MASK_1, + ATH11K_RX_MON_STATUS_RING_MASK_2, + }, + .rx = { + 0, 0, 0, 0, 0, 0, 0, + ATH11K_RX_RING_MASK_0, + ATH11K_RX_RING_MASK_1, + ATH11K_RX_RING_MASK_2, + ATH11K_RX_RING_MASK_3, + }, + .rx_err = { + ATH11K_RX_ERR_RING_MASK_0, + }, + .rx_wbm_rel = { + ATH11K_RX_WBM_REL_RING_MASK_0, + }, + .reo_status = { + ATH11K_REO_STATUS_RING_MASK_0, + }, + .rxdma2host = { + ATH11K_RXDMA2HOST_RING_MASK_0, + ATH11K_RXDMA2HOST_RING_MASK_1, + ATH11K_RXDMA2HOST_RING_MASK_2, + }, + .host2rxdma = { + }, +}; + +const struct ath11k_hw_regs ipq8074_regs = { + /* SW2TCL(x) R0 ring configuration address */ + .hal_tcl1_ring_base_lsb = 0x00000510, + .hal_tcl1_ring_base_msb = 0x00000514, + .hal_tcl1_ring_id = 0x00000518, + .hal_tcl1_ring_misc = 0x00000520, + .hal_tcl1_ring_tp_addr_lsb = 0x0000052c, + .hal_tcl1_ring_tp_addr_msb = 0x00000530, + .hal_tcl1_ring_consumer_int_setup_ix0 = 0x00000540, + .hal_tcl1_ring_consumer_int_setup_ix1 = 0x00000544, + .hal_tcl1_ring_msi1_base_lsb = 0x00000558, + .hal_tcl1_ring_msi1_base_msb = 0x0000055c, + .hal_tcl1_ring_msi1_data = 0x00000560, + .hal_tcl2_ring_base_lsb = 0x00000568, + .hal_tcl_ring_base_lsb = 0x00000618, + + /* TCL STATUS ring address */ + .hal_tcl_status_ring_base_lsb = 0x00000720, + + /* REO2SW(x) R0 ring configuration address */ + .hal_reo1_ring_base_lsb = 0x0000029c, + .hal_reo1_ring_base_msb = 0x000002a0, + .hal_reo1_ring_id = 0x000002a4, + .hal_reo1_ring_misc = 0x000002ac, + .hal_reo1_ring_hp_addr_lsb = 0x000002b0, + .hal_reo1_ring_hp_addr_msb = 0x000002b4, + .hal_reo1_ring_producer_int_setup = 0x000002c0, + .hal_reo1_ring_msi1_base_lsb = 0x000002e4, + .hal_reo1_ring_msi1_base_msb = 0x000002e8, + .hal_reo1_ring_msi1_data = 0x000002ec, + .hal_reo2_ring_base_lsb = 0x000002f4, + .hal_reo1_aging_thresh_ix_0 = 0x00000564, + .hal_reo1_aging_thresh_ix_1 = 0x00000568, + .hal_reo1_aging_thresh_ix_2 = 0x0000056c, + .hal_reo1_aging_thresh_ix_3 = 0x00000570, + + /* REO2SW(x) R2 ring pointers (head/tail) address */ + .hal_reo1_ring_hp = 0x00003038, + .hal_reo1_ring_tp = 0x0000303c, + .hal_reo2_ring_hp = 0x00003040, + + /* REO2TCL R0 ring configuration address */ + .hal_reo_tcl_ring_base_lsb = 0x000003fc, + .hal_reo_tcl_ring_hp = 0x00003058, + + /* REO status address */ + .hal_reo_status_ring_base_lsb = 0x00000504, + .hal_reo_status_hp = 0x00003070, + +}; + +const struct ath11k_hw_regs qca6390_regs = { + /* SW2TCL(x) R0 ring configuration address */ + .hal_tcl1_ring_base_lsb = 0x00000684, + .hal_tcl1_ring_base_msb = 0x00000688, + .hal_tcl1_ring_id = 0x0000068c, + .hal_tcl1_ring_misc = 0x00000694, + .hal_tcl1_ring_tp_addr_lsb = 0x000006a0, + .hal_tcl1_ring_tp_addr_msb = 0x000006a4, + .hal_tcl1_ring_consumer_int_setup_ix0 = 0x000006b4, + .hal_tcl1_ring_consumer_int_setup_ix1 = 0x000006b8, + .hal_tcl1_ring_msi1_base_lsb = 0x000006cc, + .hal_tcl1_ring_msi1_base_msb = 0x000006d0, + .hal_tcl1_ring_msi1_data = 0x000006d4, + .hal_tcl2_ring_base_lsb = 0x000006dc, + .hal_tcl_ring_base_lsb = 0x0000078c, + + /* TCL STATUS ring address */ + .hal_tcl_status_ring_base_lsb = 0x00000894, + + /* REO2SW(x) R0 ring configuration address */ + .hal_reo1_ring_base_lsb = 0x00000244, + .hal_reo1_ring_base_msb = 0x00000248, + .hal_reo1_ring_id = 0x0000024c, + .hal_reo1_ring_misc = 0x00000254, + .hal_reo1_ring_hp_addr_lsb = 0x00000258, + .hal_reo1_ring_hp_addr_msb = 0x0000025c, + .hal_reo1_ring_producer_int_setup = 0x00000268, + .hal_reo1_ring_msi1_base_lsb = 0x0000028c, + .hal_reo1_ring_msi1_base_msb = 0x00000290, + .hal_reo1_ring_msi1_data = 0x00000294, + .hal_reo2_ring_base_lsb = 0x0000029c, + .hal_reo1_aging_thresh_ix_0 = 0x0000050c, + .hal_reo1_aging_thresh_ix_1 = 0x00000510, + .hal_reo1_aging_thresh_ix_2 = 0x00000514, + .hal_reo1_aging_thresh_ix_3 = 0x00000518, + + /* REO2SW(x) R2 ring pointers (head/tail) address */ + .hal_reo1_ring_hp = 0x00003030, + .hal_reo1_ring_tp = 0x00003034, + .hal_reo2_ring_hp = 0x00003038, + + /* REO2TCL R0 ring configuration address */ + .hal_reo_tcl_ring_base_lsb = 0x000003a4, + .hal_reo_tcl_ring_hp = 0x00003050, + + /* REO status address */ + .hal_reo_status_ring_base_lsb = 0x000004ac, + .hal_reo_status_hp = 0x00003068, +}; diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h index dc4434aefbbe..d15fa7fd6d5d 100644 --- a/drivers/net/wireless/ath/ath11k/hw.h +++ b/drivers/net/wireless/ath/ath11k/hw.h @@ -6,6 +6,8 @@ #ifndef ATH11K_HW_H #define ATH11K_HW_H +#include "wmi.h" + /* Target configuration defines */ /* Num VDEVS per radio */ @@ -68,15 +70,12 @@ #define ATH11K_FW_DIR "ath11k" -/* IPQ8074 definitions */ -#define IPQ8074_FW_DIR "IPQ8074" -#define IPQ8074_MAX_BOARD_DATA_SZ (256 * 1024) -#define IPQ8074_MAX_CAL_DATA_SZ IPQ8074_MAX_BOARD_DATA_SZ - #define ATH11K_BOARD_MAGIC "QCA-ATH11K-BOARD" #define ATH11K_BOARD_API2_FILE "board-2.bin" -#define ATH11K_DEFAULT_BOARD_FILE "bdwlan.bin" +#define ATH11K_DEFAULT_BOARD_FILE "board.bin" #define ATH11K_DEFAULT_CAL_FILE "caldata.bin" +#define ATH11K_AMSS_FILE "amss.bin" +#define ATH11K_M3_FILE "m3.bin" enum ath11k_hw_rate_cck { ATH11K_HW_RATE_CCK_LP_11M = 0, @@ -104,15 +103,100 @@ enum ath11k_bus { ATH11K_BUS_PCI, }; +#define ATH11K_EXT_IRQ_GRP_NUM_MAX 11 + +struct ath11k_hw_ring_mask { + u8 tx[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + u8 rx_mon_status[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + u8 rx[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + u8 rx_err[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + u8 rx_wbm_rel[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + u8 reo_status[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + u8 rxdma2host[ATH11K_EXT_IRQ_GRP_NUM_MAX]; + u8 host2rxdma[ATH11K_EXT_IRQ_GRP_NUM_MAX]; +}; + struct ath11k_hw_params { const char *name; + u16 hw_rev; + u8 max_radios; + u32 bdf_addr; + struct { const char *dir; size_t board_size; size_t cal_size; } fw; + + const struct ath11k_hw_ops *hw_ops; + + const struct ath11k_hw_ring_mask *ring_mask; + + bool internal_sleep_clock; + + const struct ath11k_hw_regs *regs; + const struct ce_attr *host_ce_config; + u32 ce_count; + + bool single_pdev_only; + + /* For example on QCA6390 struct + * wmi_init_cmd_param::band_to_mac_config needs to be false as the + * firmware creates the mapping. + */ + bool needs_band_to_mac; + + bool rxdma1_enable; + int num_rxmda_per_pdev; + bool rx_mac_buf_ring; + bool vdev_start_delay; + bool htt_peer_map_v2; + bool tcl_0_only; +}; + +struct ath11k_hw_ops { + u8 (*get_hw_mac_from_pdev_id)(int pdev_id); + void (*wmi_init_config)(struct ath11k_base *ab, + struct target_resource_config *config); + int (*mac_id_to_pdev_id)(struct ath11k_hw_params *hw, int mac_id); + int (*mac_id_to_srng_id)(struct ath11k_hw_params *hw, int mac_id); }; +extern const struct ath11k_hw_ops ipq8074_ops; +extern const struct ath11k_hw_ops ipq6018_ops; +extern const struct ath11k_hw_ops qca6390_ops; + +extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_ipq8074; +extern const struct ath11k_hw_ring_mask ath11k_hw_ring_mask_qca6390; + +static inline +int ath11k_hw_get_mac_from_pdev_id(struct ath11k_hw_params *hw, + int pdev_idx) +{ + if (hw->hw_ops->get_hw_mac_from_pdev_id) + return hw->hw_ops->get_hw_mac_from_pdev_id(pdev_idx); + + return 0; +} + +static inline int ath11k_hw_mac_id_to_pdev_id(struct ath11k_hw_params *hw, + int mac_id) +{ + if (hw->hw_ops->mac_id_to_pdev_id) + return hw->hw_ops->mac_id_to_pdev_id(hw, mac_id); + + return 0; +} + +static inline int ath11k_hw_mac_id_to_srng_id(struct ath11k_hw_params *hw, + int mac_id) +{ + if (hw->hw_ops->mac_id_to_srng_id) + return hw->hw_ops->mac_id_to_srng_id(hw, mac_id); + + return 0; +} + struct ath11k_fw_ie { __le32 id; __le32 len; @@ -130,4 +214,51 @@ enum ath11k_bd_ie_type { ATH11K_BD_IE_BOARD_EXT = 1, }; +struct ath11k_hw_regs { + u32 hal_tcl1_ring_base_lsb; + u32 hal_tcl1_ring_base_msb; + u32 hal_tcl1_ring_id; + u32 hal_tcl1_ring_misc; + u32 hal_tcl1_ring_tp_addr_lsb; + u32 hal_tcl1_ring_tp_addr_msb; + u32 hal_tcl1_ring_consumer_int_setup_ix0; + u32 hal_tcl1_ring_consumer_int_setup_ix1; + u32 hal_tcl1_ring_msi1_base_lsb; + u32 hal_tcl1_ring_msi1_base_msb; + u32 hal_tcl1_ring_msi1_data; + u32 hal_tcl2_ring_base_lsb; + u32 hal_tcl_ring_base_lsb; + + u32 hal_tcl_status_ring_base_lsb; + + u32 hal_reo1_ring_base_lsb; + u32 hal_reo1_ring_base_msb; + u32 hal_reo1_ring_id; + u32 hal_reo1_ring_misc; + u32 hal_reo1_ring_hp_addr_lsb; + u32 hal_reo1_ring_hp_addr_msb; + u32 hal_reo1_ring_producer_int_setup; + u32 hal_reo1_ring_msi1_base_lsb; + u32 hal_reo1_ring_msi1_base_msb; + u32 hal_reo1_ring_msi1_data; + u32 hal_reo2_ring_base_lsb; + u32 hal_reo1_aging_thresh_ix_0; + u32 hal_reo1_aging_thresh_ix_1; + u32 hal_reo1_aging_thresh_ix_2; + u32 hal_reo1_aging_thresh_ix_3; + + u32 hal_reo1_ring_hp; + u32 hal_reo1_ring_tp; + u32 hal_reo2_ring_hp; + + u32 hal_reo_tcl_ring_base_lsb; + u32 hal_reo_tcl_ring_hp; + + u32 hal_reo_status_ring_base_lsb; + u32 hal_reo_status_hp; +}; + +extern const struct ath11k_hw_regs ipq8074_regs; +extern const struct ath11k_hw_regs qca6390_regs; + #endif diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 94ae2b9ea663..f4a085baff38 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -244,6 +244,9 @@ static const u32 ath11k_smps_map[] = { [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE, }; +static int ath11k_start_vdev_delay(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + u8 ath11k_mac_bw_to_mac80211_bw(u8 bw) { u8 ret = 0; @@ -521,6 +524,11 @@ struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id) int i; struct ath11k_pdev *pdev; + if (ab->hw_params.single_pdev_only) { + pdev = rcu_dereference(ab->pdevs_active[0]); + return pdev ? pdev->ar : NULL; + } + if (WARN_ON(pdev_id > ab->num_radios)) return NULL; @@ -1138,13 +1146,13 @@ ath11k_peer_assoc_h_vht_limit(u16 tx_mcs_set, idx_limit = -1; switch (idx_limit) { - case 0: /* fall through */ - case 1: /* fall through */ - case 2: /* fall through */ - case 3: /* fall through */ - case 4: /* fall through */ - case 5: /* fall through */ - case 6: /* fall through */ + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: case 7: mcs = IEEE80211_VHT_MCS_SUPPORT_0_7; break; @@ -1156,7 +1164,7 @@ ath11k_peer_assoc_h_vht_limit(u16 tx_mcs_set, break; default: WARN_ON(1); - /* fall through */ + fallthrough; case -1: mcs = IEEE80211_VHT_MCS_NOT_SUPPORTED; break; @@ -1339,7 +1347,7 @@ static void ath11k_peer_assoc_h_he(struct ath11k *ar, arg->peer_he_tx_mcs_set[WMI_HECAP_TXRX_MCS_NSS_IDX_160] = v; arg->peer_he_mcs_count++; - /* fall through */ + fallthrough; default: v = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); @@ -2114,7 +2122,7 @@ void __ath11k_mac_scan_finish(struct ath11k *ar) } else if (ar->scan.roc_notify) { ieee80211_remain_on_channel_expired(ar->hw); } - /* fall through */ + fallthrough; case ATH11K_SCAN_STARTING: ar->scan.state = ATH11K_SCAN_IDLE; ar->scan_channel = NULL; @@ -2955,6 +2963,14 @@ static int ath11k_mac_station_add(struct ath11k *ar, goto free_tx_stats; } + if (ab->hw_params.vdev_start_delay) { + ret = ath11k_start_vdev_delay(ar->hw, vif); + if (ret) { + ath11k_warn(ab, "failed to delay vdev start: %d\n", ret); + goto free_tx_stats; + } + } + return 0; free_tx_stats: @@ -3039,10 +3055,6 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, if (ret) ath11k_warn(ar->ab, "Failed to associate station: %pM\n", sta->addr); - else - ath11k_info(ar->ab, - "Station %pM moved to assoc state\n", - sta->addr); } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH && (vif->type == NL80211_IFTYPE_AP || @@ -3052,10 +3064,6 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, if (ret) ath11k_warn(ar->ab, "Failed to disassociate station: %pM\n", sta->addr); - else - ath11k_info(ar->ab, - "Station %pM moved to disassociated state\n", - sta->addr); } mutex_unlock(&ar->conf_mutex); @@ -4057,6 +4065,8 @@ void ath11k_mac_drain_tx(struct ath11k *ar) static int ath11k_mac_config_mon_status_default(struct ath11k *ar, bool enable) { struct htt_rx_ring_tlv_filter tlv_filter = {0}; + struct ath11k_base *ab = ar->ab; + int i, ret = 0; u32 ring_id; if (enable) { @@ -4064,11 +4074,16 @@ static int ath11k_mac_config_mon_status_default(struct ath11k *ar, bool enable) tlv_filter.rx_filter = ath11k_debug_rx_filter(ar); } - ring_id = ar->dp.rx_mon_status_refill_ring.refill_buf_ring.ring_id; + for (i = 0; i < ab->hw_params.num_rxmda_per_pdev; i++) { + ring_id = ar->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; + ret = ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, + ar->dp.mac_id + i, + HAL_RXDMA_MONITOR_STATUS, + DP_RX_BUFFER_SIZE, + &tlv_filter); + } - return ath11k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id, - HAL_RXDMA_MONITOR_STATUS, - DP_RX_BUFFER_SIZE, &tlv_filter); + return ret; } static int ath11k_mac_op_start(struct ieee80211_hw *hw) @@ -4368,7 +4383,7 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, break; case NL80211_IFTYPE_MESH_POINT: arvif->vdev_subtype = WMI_VDEV_SUBTYPE_MESH_11S; - /* fall through */ + fallthrough; case NL80211_IFTYPE_AP: arvif->vdev_type = WMI_VDEV_TYPE_AP; break; @@ -5112,6 +5127,39 @@ unlock: mutex_unlock(&ar->conf_mutex); } +static int ath11k_start_vdev_delay(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + int ret; + + if (WARN_ON(arvif->is_started)) + return -EBUSY; + + ret = ath11k_mac_vdev_start(arvif, &arvif->chanctx.def); + if (ret) { + ath11k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n", + arvif->vdev_id, vif->addr, + arvif->chanctx.def.chan->center_freq, ret); + return ret; + } + + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + ret = ath11k_monitor_vdev_up(ar, arvif->vdev_id); + if (ret) { + ath11k_warn(ab, "failed put monitor up: %d\n", ret); + return ret; + } + } + + arvif->is_started = true; + + /* TODO: Setup ps and cts/rts protection */ + return 0; +} + static int ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -5128,6 +5176,13 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, "mac chanctx assign ptr %pK vdev_id %i\n", ctx, arvif->vdev_id); + /* for QCA6390 bss peer must be created before vdev_start */ + if (ab->hw_params.vdev_start_delay) { + memcpy(&arvif->chanctx, ctx, sizeof(*ctx)); + mutex_unlock(&ar->conf_mutex); + return 0; + } + if (WARN_ON(arvif->is_started)) { mutex_unlock(&ar->conf_mutex); return -EBUSY; @@ -5829,12 +5884,29 @@ static void ath11k_mac_update_ch_list(struct ath11k *ar, } } +static u32 ath11k_get_phy_id(struct ath11k *ar, u32 band) +{ + struct ath11k_pdev *pdev = ar->pdev; + struct ath11k_pdev_cap *pdev_cap = &pdev->cap; + + if (band == WMI_HOST_WLAN_2G_CAP) + return pdev_cap->band[NL80211_BAND_2GHZ].phy_id; + + if (band == WMI_HOST_WLAN_5G_CAP) + return pdev_cap->band[NL80211_BAND_5GHZ].phy_id; + + ath11k_warn(ar->ab, "unsupported phy cap:%d\n", band); + + return 0; +} + static int ath11k_mac_setup_channels_rates(struct ath11k *ar, u32 supported_bands) { struct ieee80211_supported_band *band; struct ath11k_hal_reg_capabilities_ext *reg_cap; void *channels; + u32 phy_id; BUILD_BUG_ON((ARRAY_SIZE(ath11k_2ghz_channels) + ARRAY_SIZE(ath11k_5ghz_channels) + @@ -5857,6 +5929,11 @@ static int ath11k_mac_setup_channels_rates(struct ath11k *ar, band->n_bitrates = ath11k_g_rates_size; band->bitrates = ath11k_g_rates; ar->hw->wiphy->bands[NL80211_BAND_2GHZ] = band; + + if (ar->ab->hw_params.single_pdev_only) { + phy_id = ath11k_get_phy_id(ar, WMI_HOST_WLAN_2G_CAP); + reg_cap = &ar->ab->hal_reg_cap[phy_id]; + } ath11k_mac_update_ch_list(ar, band, reg_cap->low_2ghz_chan, reg_cap->high_2ghz_chan); @@ -5901,6 +5978,12 @@ static int ath11k_mac_setup_channels_rates(struct ath11k *ar, band->n_bitrates = ath11k_a_rates_size; band->bitrates = ath11k_a_rates; ar->hw->wiphy->bands[NL80211_BAND_5GHZ] = band; + + if (ar->ab->hw_params.single_pdev_only) { + phy_id = ath11k_get_phy_id(ar, WMI_HOST_WLAN_5G_CAP); + reg_cap = &ar->ab->hal_reg_cap[phy_id]; + } + ath11k_mac_update_ch_list(ar, band, reg_cap->low_5ghz_chan, reg_cap->high_5ghz_chan); @@ -6194,7 +6277,7 @@ int ath11k_mac_allocate(struct ath11k_base *ab) ar->ab = ab; ar->pdev = pdev; ar->pdev_idx = i; - ar->lmac_id = ath11k_core_get_hw_mac_id(ab, i); + ar->lmac_id = ath11k_hw_get_mac_from_pdev_id(&ab->hw_params, i); ar->wmi = &ab->wmi_ab.wmi[i]; /* FIXME wmi[0] is already initialized during attach, diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c new file mode 100644 index 000000000000..d7e60dc5b300 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/mhi.c @@ -0,0 +1,467 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* Copyright (c) 2020 The Linux Foundation. All rights reserved. */ + +#include <linux/msi.h> +#include <linux/pci.h> + +#include "core.h" +#include "debug.h" +#include "mhi.h" + +#define MHI_TIMEOUT_DEFAULT_MS 90000 + +static struct mhi_channel_config ath11k_mhi_channels[] = { + { + .num = 0, + .name = "LOOPBACK", + .num_elements = 32, + .event_ring = 0, + .dir = DMA_TO_DEVICE, + .ee_mask = 0x4, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .auto_queue = false, + .auto_start = false, + }, + { + .num = 1, + .name = "LOOPBACK", + .num_elements = 32, + .event_ring = 0, + .dir = DMA_FROM_DEVICE, + .ee_mask = 0x4, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .auto_queue = false, + .auto_start = false, + }, + { + .num = 20, + .name = "IPCR", + .num_elements = 64, + .event_ring = 1, + .dir = DMA_TO_DEVICE, + .ee_mask = 0x4, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .auto_queue = false, + .auto_start = true, + }, + { + .num = 21, + .name = "IPCR", + .num_elements = 64, + .event_ring = 1, + .dir = DMA_FROM_DEVICE, + .ee_mask = 0x4, + .pollcfg = 0, + .doorbell = MHI_DB_BRST_DISABLE, + .lpm_notify = false, + .offload_channel = false, + .doorbell_mode_switch = false, + .auto_queue = true, + .auto_start = true, + }, +}; + +static struct mhi_event_config ath11k_mhi_events[] = { + { + .num_elements = 32, + .irq_moderation_ms = 0, + .irq = 1, + .mode = MHI_DB_BRST_DISABLE, + .data_type = MHI_ER_CTRL, + .hardware_event = false, + .client_managed = false, + .offload_channel = false, + }, + { + .num_elements = 256, + .irq_moderation_ms = 1, + .irq = 2, + .mode = MHI_DB_BRST_DISABLE, + .priority = 1, + .hardware_event = false, + .client_managed = false, + .offload_channel = false, + }, +}; + +static struct mhi_controller_config ath11k_mhi_config = { + .max_channels = 128, + .timeout_ms = 2000, + .use_bounce_buf = false, + .buf_len = 0, + .num_channels = ARRAY_SIZE(ath11k_mhi_channels), + .ch_cfg = ath11k_mhi_channels, + .num_events = ARRAY_SIZE(ath11k_mhi_events), + .event_cfg = ath11k_mhi_events, +}; + +void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab) +{ + u32 val; + + val = ath11k_pci_read32(ab, MHISTATUS); + + ath11k_dbg(ab, ATH11K_DBG_PCI, "MHISTATUS 0x%x\n", val); + + /* Observed on QCA6390 that after SOC_GLOBAL_RESET, MHISTATUS + * has SYSERR bit set and thus need to set MHICTRL_RESET + * to clear SYSERR. + */ + ath11k_pci_write32(ab, MHICTRL, MHICTRL_RESET_MASK); + + mdelay(10); +} + +static void ath11k_mhi_reset_txvecdb(struct ath11k_base *ab) +{ + ath11k_pci_write32(ab, PCIE_TXVECDB, 0); +} + +static void ath11k_mhi_reset_txvecstatus(struct ath11k_base *ab) +{ + ath11k_pci_write32(ab, PCIE_TXVECSTATUS, 0); +} + +static void ath11k_mhi_reset_rxvecdb(struct ath11k_base *ab) +{ + ath11k_pci_write32(ab, PCIE_RXVECDB, 0); +} + +static void ath11k_mhi_reset_rxvecstatus(struct ath11k_base *ab) +{ + ath11k_pci_write32(ab, PCIE_RXVECSTATUS, 0); +} + +void ath11k_mhi_clear_vector(struct ath11k_base *ab) +{ + ath11k_mhi_reset_txvecdb(ab); + ath11k_mhi_reset_txvecstatus(ab); + ath11k_mhi_reset_rxvecdb(ab); + ath11k_mhi_reset_rxvecstatus(ab); +} + +static int ath11k_mhi_get_msi(struct ath11k_pci *ab_pci) +{ + struct ath11k_base *ab = ab_pci->ab; + u32 user_base_data, base_vector; + int ret, num_vectors, i; + int *irq; + + ret = ath11k_pci_get_user_msi_assignment(ab_pci, + "MHI", &num_vectors, + &user_base_data, &base_vector); + if (ret) + return ret; + + ath11k_dbg(ab, ATH11K_DBG_PCI, "Number of assigned MSI for MHI is %d, base vector is %d\n", + num_vectors, base_vector); + + irq = kcalloc(num_vectors, sizeof(int), GFP_KERNEL); + if (!irq) + return -ENOMEM; + + for (i = 0; i < num_vectors; i++) + irq[i] = ath11k_pci_get_msi_irq(ab->dev, + base_vector + i); + + ab_pci->mhi_ctrl->irq = irq; + ab_pci->mhi_ctrl->nr_irqs = num_vectors; + + return 0; +} + +static int ath11k_mhi_op_runtime_get(struct mhi_controller *mhi_cntrl) +{ + return 0; +} + +static void ath11k_mhi_op_runtime_put(struct mhi_controller *mhi_cntrl) +{ +} + +static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl, + enum mhi_callback cb) +{ +} + +static int ath11k_mhi_op_read_reg(struct mhi_controller *mhi_cntrl, + void __iomem *addr, + u32 *out) +{ + *out = readl(addr); + + return 0; +} + +static void ath11k_mhi_op_write_reg(struct mhi_controller *mhi_cntrl, + void __iomem *addr, + u32 val) +{ + writel(val, addr); +} + +int ath11k_mhi_register(struct ath11k_pci *ab_pci) +{ + struct ath11k_base *ab = ab_pci->ab; + struct mhi_controller *mhi_ctrl; + int ret; + + mhi_ctrl = kzalloc(sizeof(*mhi_ctrl), GFP_KERNEL); + if (!mhi_ctrl) + return PTR_ERR(mhi_ctrl); + + ath11k_core_create_firmware_path(ab, ATH11K_AMSS_FILE, + ab_pci->amss_path, + sizeof(ab_pci->amss_path)); + + ab_pci->mhi_ctrl = mhi_ctrl; + mhi_ctrl->cntrl_dev = ab->dev; + mhi_ctrl->fw_image = ab_pci->amss_path; + mhi_ctrl->regs = ab->mem; + + ret = ath11k_mhi_get_msi(ab_pci); + if (ret) { + ath11k_err(ab, "failed to get msi for mhi\n"); + kfree(mhi_ctrl); + return ret; + } + + mhi_ctrl->iova_start = 0; + mhi_ctrl->iova_stop = 0xffffffff; + mhi_ctrl->sbl_size = SZ_512K; + mhi_ctrl->seg_len = SZ_512K; + mhi_ctrl->fbc_download = true; + mhi_ctrl->runtime_get = ath11k_mhi_op_runtime_get; + mhi_ctrl->runtime_put = ath11k_mhi_op_runtime_put; + mhi_ctrl->status_cb = ath11k_mhi_op_status_cb; + mhi_ctrl->read_reg = ath11k_mhi_op_read_reg; + mhi_ctrl->write_reg = ath11k_mhi_op_write_reg; + + ret = mhi_register_controller(mhi_ctrl, &ath11k_mhi_config); + if (ret) { + ath11k_err(ab, "failed to register to mhi bus, err = %d\n", ret); + kfree(mhi_ctrl); + return ret; + } + + return 0; +} + +void ath11k_mhi_unregister(struct ath11k_pci *ab_pci) +{ + struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl; + + mhi_unregister_controller(mhi_ctrl); + kfree(mhi_ctrl->irq); +} + +static char *ath11k_mhi_state_to_str(enum ath11k_mhi_state mhi_state) +{ + switch (mhi_state) { + case ATH11K_MHI_INIT: + return "INIT"; + case ATH11K_MHI_DEINIT: + return "DEINIT"; + case ATH11K_MHI_POWER_ON: + return "POWER_ON"; + case ATH11K_MHI_POWER_OFF: + return "POWER_OFF"; + case ATH11K_MHI_FORCE_POWER_OFF: + return "FORCE_POWER_OFF"; + case ATH11K_MHI_SUSPEND: + return "SUSPEND"; + case ATH11K_MHI_RESUME: + return "RESUME"; + case ATH11K_MHI_TRIGGER_RDDM: + return "TRIGGER_RDDM"; + case ATH11K_MHI_RDDM_DONE: + return "RDDM_DONE"; + default: + return "UNKNOWN"; + } +}; + +static void ath11k_mhi_set_state_bit(struct ath11k_pci *ab_pci, + enum ath11k_mhi_state mhi_state) +{ + struct ath11k_base *ab = ab_pci->ab; + + switch (mhi_state) { + case ATH11K_MHI_INIT: + set_bit(ATH11K_MHI_INIT, &ab_pci->mhi_state); + break; + case ATH11K_MHI_DEINIT: + clear_bit(ATH11K_MHI_INIT, &ab_pci->mhi_state); + break; + case ATH11K_MHI_POWER_ON: + set_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state); + break; + case ATH11K_MHI_POWER_OFF: + case ATH11K_MHI_FORCE_POWER_OFF: + clear_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state); + clear_bit(ATH11K_MHI_TRIGGER_RDDM, &ab_pci->mhi_state); + clear_bit(ATH11K_MHI_RDDM_DONE, &ab_pci->mhi_state); + break; + case ATH11K_MHI_SUSPEND: + set_bit(ATH11K_MHI_SUSPEND, &ab_pci->mhi_state); + break; + case ATH11K_MHI_RESUME: + clear_bit(ATH11K_MHI_SUSPEND, &ab_pci->mhi_state); + break; + case ATH11K_MHI_TRIGGER_RDDM: + set_bit(ATH11K_MHI_TRIGGER_RDDM, &ab_pci->mhi_state); + break; + case ATH11K_MHI_RDDM_DONE: + set_bit(ATH11K_MHI_RDDM_DONE, &ab_pci->mhi_state); + break; + default: + ath11k_err(ab, "unhandled mhi state (%d)\n", mhi_state); + } +} + +static int ath11k_mhi_check_state_bit(struct ath11k_pci *ab_pci, + enum ath11k_mhi_state mhi_state) +{ + struct ath11k_base *ab = ab_pci->ab; + + switch (mhi_state) { + case ATH11K_MHI_INIT: + if (!test_bit(ATH11K_MHI_INIT, &ab_pci->mhi_state)) + return 0; + break; + case ATH11K_MHI_DEINIT: + case ATH11K_MHI_POWER_ON: + if (test_bit(ATH11K_MHI_INIT, &ab_pci->mhi_state) && + !test_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state)) + return 0; + break; + case ATH11K_MHI_FORCE_POWER_OFF: + if (test_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state)) + return 0; + break; + case ATH11K_MHI_POWER_OFF: + case ATH11K_MHI_SUSPEND: + if (test_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state) && + !test_bit(ATH11K_MHI_SUSPEND, &ab_pci->mhi_state)) + return 0; + break; + case ATH11K_MHI_RESUME: + if (test_bit(ATH11K_MHI_SUSPEND, &ab_pci->mhi_state)) + return 0; + break; + case ATH11K_MHI_TRIGGER_RDDM: + if (test_bit(ATH11K_MHI_POWER_ON, &ab_pci->mhi_state) && + !test_bit(ATH11K_MHI_TRIGGER_RDDM, &ab_pci->mhi_state)) + return 0; + break; + case ATH11K_MHI_RDDM_DONE: + return 0; + default: + ath11k_err(ab, "unhandled mhi state: %s(%d)\n", + ath11k_mhi_state_to_str(mhi_state), mhi_state); + } + + ath11k_err(ab, "failed to set mhi state %s(%d) in current mhi state (0x%lx)\n", + ath11k_mhi_state_to_str(mhi_state), mhi_state, + ab_pci->mhi_state); + + return -EINVAL; +} + +static int ath11k_mhi_set_state(struct ath11k_pci *ab_pci, + enum ath11k_mhi_state mhi_state) +{ + struct ath11k_base *ab = ab_pci->ab; + int ret; + + ret = ath11k_mhi_check_state_bit(ab_pci, mhi_state); + if (ret) + goto out; + + ath11k_dbg(ab, ATH11K_DBG_PCI, "setting mhi state: %s(%d)\n", + ath11k_mhi_state_to_str(mhi_state), mhi_state); + + switch (mhi_state) { + case ATH11K_MHI_INIT: + ret = mhi_prepare_for_power_up(ab_pci->mhi_ctrl); + break; + case ATH11K_MHI_DEINIT: + mhi_unprepare_after_power_down(ab_pci->mhi_ctrl); + ret = 0; + break; + case ATH11K_MHI_POWER_ON: + ret = mhi_async_power_up(ab_pci->mhi_ctrl); + break; + case ATH11K_MHI_POWER_OFF: + mhi_power_down(ab_pci->mhi_ctrl, true); + ret = 0; + break; + case ATH11K_MHI_FORCE_POWER_OFF: + mhi_power_down(ab_pci->mhi_ctrl, false); + ret = 0; + break; + case ATH11K_MHI_SUSPEND: + break; + case ATH11K_MHI_RESUME: + break; + case ATH11K_MHI_TRIGGER_RDDM: + ret = mhi_force_rddm_mode(ab_pci->mhi_ctrl); + break; + case ATH11K_MHI_RDDM_DONE: + break; + default: + ath11k_err(ab, "unhandled MHI state (%d)\n", mhi_state); + ret = -EINVAL; + } + + if (ret) + goto out; + + ath11k_mhi_set_state_bit(ab_pci, mhi_state); + + return 0; + +out: + ath11k_err(ab, "failed to set mhi state: %s(%d)\n", + ath11k_mhi_state_to_str(mhi_state), mhi_state); + return ret; +} + +int ath11k_mhi_start(struct ath11k_pci *ab_pci) +{ + int ret; + + ab_pci->mhi_ctrl->timeout_ms = MHI_TIMEOUT_DEFAULT_MS; + + ret = ath11k_mhi_set_state(ab_pci, ATH11K_MHI_INIT); + if (ret) + goto out; + + ret = ath11k_mhi_set_state(ab_pci, ATH11K_MHI_POWER_ON); + if (ret) + goto out; + + return 0; + +out: + return ret; +} + +void ath11k_mhi_stop(struct ath11k_pci *ab_pci) +{ + ath11k_mhi_set_state(ab_pci, ATH11K_MHI_POWER_OFF); + ath11k_mhi_set_state(ab_pci, ATH11K_MHI_DEINIT); +} + diff --git a/drivers/net/wireless/ath/ath11k/mhi.h b/drivers/net/wireless/ath/ath11k/mhi.h new file mode 100644 index 000000000000..a7fd5e201d18 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/mhi.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ +#ifndef _ATH11K_MHI_H +#define _ATH11K_MHI_H + +#include "pci.h" + +#define PCIE_TXVECDB 0x360 +#define PCIE_TXVECSTATUS 0x368 +#define PCIE_RXVECDB 0x394 +#define PCIE_RXVECSTATUS 0x39C + +#define MHISTATUS 0x48 +#define MHICTRL 0x38 +#define MHICTRL_RESET_MASK 0x2 + +enum ath11k_mhi_state { + ATH11K_MHI_INIT, + ATH11K_MHI_DEINIT, + ATH11K_MHI_POWER_ON, + ATH11K_MHI_POWER_OFF, + ATH11K_MHI_FORCE_POWER_OFF, + ATH11K_MHI_SUSPEND, + ATH11K_MHI_RESUME, + ATH11K_MHI_TRIGGER_RDDM, + ATH11K_MHI_RDDM, + ATH11K_MHI_RDDM_DONE, +}; + +int ath11k_mhi_start(struct ath11k_pci *ar_pci); +void ath11k_mhi_stop(struct ath11k_pci *ar_pci); +int ath11k_mhi_register(struct ath11k_pci *ar_pci); +void ath11k_mhi_unregister(struct ath11k_pci *ar_pci); +void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab); +void ath11k_mhi_clear_vector(struct ath11k_base *ab); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c new file mode 100644 index 000000000000..09a07d3d0f9c --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -0,0 +1,1196 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/msi.h> +#include <linux/pci.h> + +#include "pci.h" +#include "core.h" +#include "hif.h" +#include "mhi.h" +#include "debug.h" + +#define ATH11K_PCI_BAR_NUM 0 +#define ATH11K_PCI_DMA_MASK 32 + +#define ATH11K_PCI_IRQ_CE0_OFFSET 3 + +#define WINDOW_ENABLE_BIT 0x40000000 +#define WINDOW_REG_ADDRESS 0x310c +#define WINDOW_VALUE_MASK GENMASK(24, 19) +#define WINDOW_START 0x80000 +#define WINDOW_RANGE_MASK GENMASK(18, 0) + +#define TCSR_SOC_HW_VERSION 0x0224 +#define TCSR_SOC_HW_VERSION_MAJOR_MASK GENMASK(16, 8) +#define TCSR_SOC_HW_VERSION_MINOR_MASK GENMASK(7, 0) + +#define QCA6390_DEVICE_ID 0x1101 + +static const struct pci_device_id ath11k_pci_id_table[] = { + { PCI_VDEVICE(QCOM, QCA6390_DEVICE_ID) }, + {0} +}; + +MODULE_DEVICE_TABLE(pci, ath11k_pci_id_table); + +static const struct ath11k_bus_params ath11k_pci_bus_params = { + .mhi_support = true, + .m3_fw_support = true, + .fixed_bdf_addr = false, + .fixed_mem_region = false, +}; + +static const struct ath11k_msi_config msi_config = { + .total_vectors = 32, + .total_users = 4, + .users = (struct ath11k_msi_user[]) { + { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, + { .name = "CE", .num_vectors = 10, .base_vector = 3 }, + { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, + { .name = "DP", .num_vectors = 18, .base_vector = 14 }, + }, +}; + +/* Target firmware's Copy Engine configuration. */ +static const struct ce_pipe_config target_ce_config_wlan[] = { + /* CE0: host->target HTC control and raw streams */ + { + .pipenum = __cpu_to_le32(0), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE1: target->host HTT + HTC control */ + { + .pipenum = __cpu_to_le32(1), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE2: target->host WMI */ + { + .pipenum = __cpu_to_le32(2), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE3: host->target WMI */ + { + .pipenum = __cpu_to_le32(3), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE4: host->target HTT */ + { + .pipenum = __cpu_to_le32(4), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(256), + .nbytes_max = __cpu_to_le32(256), + .flags = __cpu_to_le32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), + .reserved = __cpu_to_le32(0), + }, + + /* CE5: target->host Pktlog */ + { + .pipenum = __cpu_to_le32(5), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE6: Reserved for target autonomous hif_memcpy */ + { + .pipenum = __cpu_to_le32(6), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(16384), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + + /* CE7 used only by Host */ + { + .pipenum = __cpu_to_le32(7), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT_H2H), + .nentries = __cpu_to_le32(0), + .nbytes_max = __cpu_to_le32(0), + .flags = __cpu_to_le32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), + .reserved = __cpu_to_le32(0), + }, + + /* CE8 target->host used only by IPA */ + { + .pipenum = __cpu_to_le32(8), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(16384), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* CE 9, 10, 11 are used by MHI driver */ +}; + +/* Map from service/endpoint to Copy Engine. + * This table is derived from the CE_PCI TABLE, above. + * It is passed to the Target at startup for use by firmware. + */ +static const struct service_to_pipe target_service_to_ce_map_wlan[] = { + { + __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VO), + __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VO), + __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BK), + __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BK), + __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BE), + __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_BE), + __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VI), + __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_DATA_VI), + __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL), + __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH11K_HTC_SVC_ID_WMI_CONTROL), + __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + __cpu_to_le32(2), + }, + + { + __cpu_to_le32(ATH11K_HTC_SVC_ID_RSVD_CTRL), + __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + __cpu_to_le32(0), + }, + { + __cpu_to_le32(ATH11K_HTC_SVC_ID_RSVD_CTRL), + __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + __cpu_to_le32(2), + }, + + { + __cpu_to_le32(ATH11K_HTC_SVC_ID_HTT_DATA_MSG), + __cpu_to_le32(PIPEDIR_OUT), /* out = UL = host -> target */ + __cpu_to_le32(4), + }, + { + __cpu_to_le32(ATH11K_HTC_SVC_ID_HTT_DATA_MSG), + __cpu_to_le32(PIPEDIR_IN), /* in = DL = target -> host */ + __cpu_to_le32(1), + }, + + /* (Additions here) */ + + { /* must be last */ + __cpu_to_le32(0), + __cpu_to_le32(0), + __cpu_to_le32(0), + }, +}; + +static const char *irq_name[ATH11K_IRQ_NUM_MAX] = { + "bhi", + "mhi-er0", + "mhi-er1", + "ce0", + "ce1", + "ce2", + "ce3", + "ce4", + "ce5", + "ce6", + "ce7", + "ce8", + "ce9", + "ce10", + "ce11", + "host2wbm-desc-feed", + "host2reo-re-injection", + "host2reo-command", + "host2rxdma-monitor-ring3", + "host2rxdma-monitor-ring2", + "host2rxdma-monitor-ring1", + "reo2ost-exception", + "wbm2host-rx-release", + "reo2host-status", + "reo2host-destination-ring4", + "reo2host-destination-ring3", + "reo2host-destination-ring2", + "reo2host-destination-ring1", + "rxdma2host-monitor-destination-mac3", + "rxdma2host-monitor-destination-mac2", + "rxdma2host-monitor-destination-mac1", + "ppdu-end-interrupts-mac3", + "ppdu-end-interrupts-mac2", + "ppdu-end-interrupts-mac1", + "rxdma2host-monitor-status-ring-mac3", + "rxdma2host-monitor-status-ring-mac2", + "rxdma2host-monitor-status-ring-mac1", + "host2rxdma-host-buf-ring-mac3", + "host2rxdma-host-buf-ring-mac2", + "host2rxdma-host-buf-ring-mac1", + "rxdma2host-destination-ring-mac3", + "rxdma2host-destination-ring-mac2", + "rxdma2host-destination-ring-mac1", + "host2tcl-input-ring4", + "host2tcl-input-ring3", + "host2tcl-input-ring2", + "host2tcl-input-ring1", + "wbm2host-tx-completions-ring3", + "wbm2host-tx-completions-ring2", + "wbm2host-tx-completions-ring1", + "tcl2host-status-ring", +}; + +static inline void ath11k_pci_select_window(struct ath11k_pci *ab_pci, u32 offset) +{ + struct ath11k_base *ab = ab_pci->ab; + + u32 window = FIELD_GET(WINDOW_VALUE_MASK, offset); + + lockdep_assert_held(&ab_pci->window_lock); + + if (window != ab_pci->register_window) { + iowrite32(WINDOW_ENABLE_BIT | window, + ab->mem + WINDOW_REG_ADDRESS); + ab_pci->register_window = window; + } +} + +void ath11k_pci_write32(struct ath11k_base *ab, u32 offset, u32 value) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + + if (offset < WINDOW_START) { + iowrite32(value, ab->mem + offset); + } else { + spin_lock_bh(&ab_pci->window_lock); + ath11k_pci_select_window(ab_pci, offset); + iowrite32(value, ab->mem + WINDOW_START + (offset & WINDOW_RANGE_MASK)); + spin_unlock_bh(&ab_pci->window_lock); + } +} + +u32 ath11k_pci_read32(struct ath11k_base *ab, u32 offset) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + u32 val; + + if (offset < WINDOW_START) { + val = ioread32(ab->mem + offset); + } else { + spin_lock_bh(&ab_pci->window_lock); + ath11k_pci_select_window(ab_pci, offset); + val = ioread32(ab->mem + WINDOW_START + (offset & WINDOW_RANGE_MASK)); + spin_unlock_bh(&ab_pci->window_lock); + } + + return val; +} + +static void ath11k_pci_soc_global_reset(struct ath11k_base *ab) +{ + u32 val, delay; + + val = ath11k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET); + + val |= PCIE_SOC_GLOBAL_RESET_V; + + ath11k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + + /* TODO: exact time to sleep is uncertain */ + delay = 10; + mdelay(delay); + + /* Need to toggle V bit back otherwise stuck in reset status */ + val &= ~PCIE_SOC_GLOBAL_RESET_V; + + ath11k_pci_write32(ab, PCIE_SOC_GLOBAL_RESET, val); + + mdelay(delay); + + val = ath11k_pci_read32(ab, PCIE_SOC_GLOBAL_RESET); + if (val == 0xffffffff) + ath11k_warn(ab, "link down error during global reset\n"); +} + +static void ath11k_pci_clear_dbg_registers(struct ath11k_base *ab) +{ + u32 val; + + /* read cookie */ + val = ath11k_pci_read32(ab, PCIE_Q6_COOKIE_ADDR); + ath11k_dbg(ab, ATH11K_DBG_PCI, "cookie:0x%x\n", val); + + val = ath11k_pci_read32(ab, WLAON_WARM_SW_ENTRY); + ath11k_dbg(ab, ATH11K_DBG_PCI, "WLAON_WARM_SW_ENTRY 0x%x\n", val); + + /* TODO: exact time to sleep is uncertain */ + mdelay(10); + + /* write 0 to WLAON_WARM_SW_ENTRY to prevent Q6 from + * continuing warm path and entering dead loop. + */ + ath11k_pci_write32(ab, WLAON_WARM_SW_ENTRY, 0); + mdelay(10); + + val = ath11k_pci_read32(ab, WLAON_WARM_SW_ENTRY); + ath11k_dbg(ab, ATH11K_DBG_PCI, "WLAON_WARM_SW_ENTRY 0x%x\n", val); + + /* A read clear register. clear the register to prevent + * Q6 from entering wrong code path. + */ + val = ath11k_pci_read32(ab, WLAON_SOC_RESET_CAUSE_REG); + ath11k_dbg(ab, ATH11K_DBG_PCI, "soc reset cause:%d\n", val); +} + +static void ath11k_pci_force_wake(struct ath11k_base *ab) +{ + ath11k_pci_write32(ab, PCIE_SOC_WAKE_PCIE_LOCAL_REG, 1); + mdelay(5); +} + +static void ath11k_pci_sw_reset(struct ath11k_base *ab) +{ + ath11k_pci_soc_global_reset(ab); + ath11k_mhi_clear_vector(ab); + ath11k_pci_soc_global_reset(ab); + ath11k_mhi_set_mhictrl_reset(ab); + ath11k_pci_clear_dbg_registers(ab); +} + +int ath11k_pci_get_msi_irq(struct device *dev, unsigned int vector) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + + return pci_irq_vector(pci_dev, vector); +} + +static void ath11k_pci_get_msi_address(struct ath11k_base *ab, u32 *msi_addr_lo, + u32 *msi_addr_hi) +{ + struct pci_dev *pci_dev = to_pci_dev(ab->dev); + + pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_LO, + msi_addr_lo); + + pci_read_config_dword(pci_dev, pci_dev->msi_cap + PCI_MSI_ADDRESS_HI, + msi_addr_hi); +} + +int ath11k_pci_get_user_msi_assignment(struct ath11k_pci *ab_pci, char *user_name, + int *num_vectors, u32 *user_base_data, + u32 *base_vector) +{ + struct ath11k_base *ab = ab_pci->ab; + int idx; + + for (idx = 0; idx < msi_config.total_users; idx++) { + if (strcmp(user_name, msi_config.users[idx].name) == 0) { + *num_vectors = msi_config.users[idx].num_vectors; + *user_base_data = msi_config.users[idx].base_vector + + ab_pci->msi_ep_base_data; + *base_vector = msi_config.users[idx].base_vector; + + ath11k_dbg(ab, ATH11K_DBG_PCI, "Assign MSI to user: %s, num_vectors: %d, user_base_data: %u, base_vector: %u\n", + user_name, *num_vectors, *user_base_data, + *base_vector); + + return 0; + } + } + + ath11k_err(ab, "Failed to find MSI assignment for %s!\n", user_name); + + return -EINVAL; +} + +static int ath11k_get_user_msi_assignment(struct ath11k_base *ab, char *user_name, + int *num_vectors, u32 *user_base_data, + u32 *base_vector) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + + return ath11k_pci_get_user_msi_assignment(ab_pci, user_name, + num_vectors, user_base_data, + base_vector); +} + +static void ath11k_pci_free_ext_irq(struct ath11k_base *ab) +{ + int i, j; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + for (j = 0; j < irq_grp->num_irq; j++) + free_irq(ab->irq_num[irq_grp->irqs[j]], irq_grp); + + netif_napi_del(&irq_grp->napi); + } +} + +static void ath11k_pci_free_irq(struct ath11k_base *ab) +{ + int i, irq_idx; + + for (i = 0; i < ab->hw_params.ce_count; i++) { + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i; + free_irq(ab->irq_num[irq_idx], &ab->ce.ce_pipe[i]); + } + + ath11k_pci_free_ext_irq(ab); +} + +static void ath11k_pci_ce_irq_enable(struct ath11k_base *ab, u16 ce_id) +{ + u32 irq_idx; + + irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id; + enable_irq(ab->irq_num[irq_idx]); +} + +static void ath11k_pci_ce_irq_disable(struct ath11k_base *ab, u16 ce_id) +{ + u32 irq_idx; + + irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + ce_id; + disable_irq_nosync(ab->irq_num[irq_idx]); +} + +static void ath11k_pci_ce_irqs_disable(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < ab->hw_params.ce_count; i++) { + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + ath11k_pci_ce_irq_disable(ab, i); + } +} + +static void ath11k_pci_sync_ce_irqs(struct ath11k_base *ab) +{ + int i; + int irq_idx; + + for (i = 0; i < ab->hw_params.ce_count; i++) { + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + + irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i; + synchronize_irq(ab->irq_num[irq_idx]); + } +} + +static void ath11k_pci_ce_tasklet(unsigned long data) +{ + struct ath11k_ce_pipe *ce_pipe = (struct ath11k_ce_pipe *)data; + + ath11k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num); + + ath11k_pci_ce_irq_enable(ce_pipe->ab, ce_pipe->pipe_num); +} + +static irqreturn_t ath11k_pci_ce_interrupt_handler(int irq, void *arg) +{ + struct ath11k_ce_pipe *ce_pipe = arg; + + ath11k_pci_ce_irq_disable(ce_pipe->ab, ce_pipe->pipe_num); + tasklet_schedule(&ce_pipe->intr_tq); + + return IRQ_HANDLED; +} + +static void ath11k_pci_ext_grp_disable(struct ath11k_ext_irq_grp *irq_grp) +{ + int i; + + for (i = 0; i < irq_grp->num_irq; i++) + disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +} + +static void __ath11k_pci_ext_irq_disable(struct ath11k_base *sc) +{ + int i; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &sc->ext_irq_grp[i]; + + ath11k_pci_ext_grp_disable(irq_grp); + + napi_synchronize(&irq_grp->napi); + napi_disable(&irq_grp->napi); + } +} + +static void ath11k_pci_ext_grp_enable(struct ath11k_ext_irq_grp *irq_grp) +{ + int i; + + for (i = 0; i < irq_grp->num_irq; i++) + enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +} + +static void ath11k_pci_ext_irq_enable(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + napi_enable(&irq_grp->napi); + ath11k_pci_ext_grp_enable(irq_grp); + } +} + +static void ath11k_pci_sync_ext_irqs(struct ath11k_base *ab) +{ + int i, j, irq_idx; + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + for (j = 0; j < irq_grp->num_irq; j++) { + irq_idx = irq_grp->irqs[j]; + synchronize_irq(ab->irq_num[irq_idx]); + } + } +} + +static void ath11k_pci_ext_irq_disable(struct ath11k_base *ab) +{ + __ath11k_pci_ext_irq_disable(ab); + ath11k_pci_sync_ext_irqs(ab); +} + +static int ath11k_pci_ext_grp_napi_poll(struct napi_struct *napi, int budget) +{ + struct ath11k_ext_irq_grp *irq_grp = container_of(napi, + struct ath11k_ext_irq_grp, + napi); + struct ath11k_base *ab = irq_grp->ab; + int work_done; + + work_done = ath11k_dp_service_srng(ab, irq_grp, budget); + if (work_done < budget) { + napi_complete_done(napi, work_done); + ath11k_pci_ext_grp_enable(irq_grp); + } + + if (work_done > budget) + work_done = budget; + + return work_done; +} + +static irqreturn_t ath11k_pci_ext_interrupt_handler(int irq, void *arg) +{ + struct ath11k_ext_irq_grp *irq_grp = arg; + + ath11k_dbg(irq_grp->ab, ATH11K_DBG_PCI, "ext irq:%d\n", irq); + + ath11k_pci_ext_grp_disable(irq_grp); + + napi_schedule(&irq_grp->napi); + + return IRQ_HANDLED; +} + +static int ath11k_pci_ext_irq_config(struct ath11k_base *ab) +{ + int i, j, ret, num_vectors = 0; + u32 user_base_data = 0, base_vector = 0; + + ath11k_pci_get_user_msi_assignment(ath11k_pci_priv(ab), "DP", + &num_vectors, &user_base_data, + &base_vector); + + for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + u32 num_irq = 0; + + irq_grp->ab = ab; + irq_grp->grp_id = i; + init_dummy_netdev(&irq_grp->napi_ndev); + netif_napi_add(&irq_grp->napi_ndev, &irq_grp->napi, + ath11k_pci_ext_grp_napi_poll, NAPI_POLL_WEIGHT); + + if (ab->hw_params.ring_mask->tx[i] || + ab->hw_params.ring_mask->rx[i] || + ab->hw_params.ring_mask->rx_err[i] || + ab->hw_params.ring_mask->rx_wbm_rel[i] || + ab->hw_params.ring_mask->reo_status[i] || + ab->hw_params.ring_mask->rxdma2host[i] || + ab->hw_params.ring_mask->host2rxdma[i] || + ab->hw_params.ring_mask->rx_mon_status[i]) { + num_irq = 1; + } + + irq_grp->num_irq = num_irq; + irq_grp->irqs[0] = base_vector + i; + + for (j = 0; j < irq_grp->num_irq; j++) { + int irq_idx = irq_grp->irqs[j]; + int vector = (i % num_vectors) + base_vector; + int irq = ath11k_pci_get_msi_irq(ab->dev, vector); + + ab->irq_num[irq_idx] = irq; + + ath11k_dbg(ab, ATH11K_DBG_PCI, + "irq:%d group:%d\n", irq, i); + ret = request_irq(irq, ath11k_pci_ext_interrupt_handler, + IRQF_SHARED, + "DP_EXT_IRQ", irq_grp); + if (ret) { + ath11k_err(ab, "failed request irq %d: %d\n", + vector, ret); + return ret; + } + + disable_irq_nosync(ab->irq_num[irq_idx]); + } + } + + return 0; +} + +static int ath11k_pci_config_irq(struct ath11k_base *ab) +{ + struct ath11k_ce_pipe *ce_pipe; + u32 msi_data_start; + u32 msi_data_count; + u32 msi_irq_start; + unsigned int msi_data; + int irq, i, ret, irq_idx; + + ret = ath11k_pci_get_user_msi_assignment(ath11k_pci_priv(ab), + "CE", &msi_data_count, + &msi_data_start, &msi_irq_start); + if (ret) + return ret; + + /* Configure CE irqs */ + for (i = 0; i < ab->hw_params.ce_count; i++) { + msi_data = (i % msi_data_count) + msi_irq_start; + irq = ath11k_pci_get_msi_irq(ab->dev, msi_data); + ce_pipe = &ab->ce.ce_pipe[i]; + + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + + irq_idx = ATH11K_PCI_IRQ_CE0_OFFSET + i; + + tasklet_init(&ce_pipe->intr_tq, ath11k_pci_ce_tasklet, + (unsigned long)ce_pipe); + + ret = request_irq(irq, ath11k_pci_ce_interrupt_handler, + IRQF_SHARED, irq_name[irq_idx], + ce_pipe); + if (ret) { + ath11k_err(ab, "failed to request irq %d: %d\n", + irq_idx, ret); + return ret; + } + + ab->irq_num[irq_idx] = irq; + ath11k_pci_ce_irq_disable(ab, i); + } + + ret = ath11k_pci_ext_irq_config(ab); + if (ret) + return ret; + + return 0; +} + +static void ath11k_pci_init_qmi_ce_config(struct ath11k_base *ab) +{ + struct ath11k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg; + + cfg->tgt_ce = target_ce_config_wlan; + cfg->tgt_ce_len = ARRAY_SIZE(target_ce_config_wlan); + + cfg->svc_to_ce_map = target_service_to_ce_map_wlan; + cfg->svc_to_ce_map_len = ARRAY_SIZE(target_service_to_ce_map_wlan); + ab->qmi.service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390; +} + +static void ath11k_pci_ce_irqs_enable(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < ab->hw_params.ce_count; i++) { + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + ath11k_pci_ce_irq_enable(ab, i); + } +} + +static int ath11k_pci_enable_msi(struct ath11k_pci *ab_pci) +{ + struct ath11k_base *ab = ab_pci->ab; + struct msi_desc *msi_desc; + int num_vectors; + int ret; + + num_vectors = pci_alloc_irq_vectors(ab_pci->pdev, + msi_config.total_vectors, + msi_config.total_vectors, + PCI_IRQ_MSI); + if (num_vectors != msi_config.total_vectors) { + ath11k_err(ab, "failed to get %d MSI vectors, only %d available", + msi_config.total_vectors, num_vectors); + + if (num_vectors >= 0) + return -EINVAL; + else + return num_vectors; + } + + msi_desc = irq_get_msi_desc(ab_pci->pdev->irq); + if (!msi_desc) { + ath11k_err(ab, "msi_desc is NULL!\n"); + ret = -EINVAL; + goto free_msi_vector; + } + + ab_pci->msi_ep_base_data = msi_desc->msg.data; + + ath11k_dbg(ab, ATH11K_DBG_PCI, "msi base data is %d\n", ab_pci->msi_ep_base_data); + + return 0; + +free_msi_vector: + pci_free_irq_vectors(ab_pci->pdev); + + return ret; +} + +static void ath11k_pci_disable_msi(struct ath11k_pci *ab_pci) +{ + pci_free_irq_vectors(ab_pci->pdev); +} + +static int ath11k_pci_claim(struct ath11k_pci *ab_pci, struct pci_dev *pdev) +{ + struct ath11k_base *ab = ab_pci->ab; + u16 device_id; + int ret = 0; + + pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id); + if (device_id != ab_pci->dev_id) { + ath11k_err(ab, "pci device id mismatch: 0x%x 0x%x\n", + device_id, ab_pci->dev_id); + ret = -EIO; + goto out; + } + + ret = pci_assign_resource(pdev, ATH11K_PCI_BAR_NUM); + if (ret) { + ath11k_err(ab, "failed to assign pci resource: %d\n", ret); + goto out; + } + + ret = pci_enable_device(pdev); + if (ret) { + ath11k_err(ab, "failed to enable pci device: %d\n", ret); + goto out; + } + + ret = pci_request_region(pdev, ATH11K_PCI_BAR_NUM, "ath11k_pci"); + if (ret) { + ath11k_err(ab, "failed to request pci region: %d\n", ret); + goto disable_device; + } + + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(ATH11K_PCI_DMA_MASK)); + if (ret) { + ath11k_err(ab, "failed to set pci dma mask to %d: %d\n", + ATH11K_PCI_DMA_MASK, ret); + goto release_region; + } + + ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(ATH11K_PCI_DMA_MASK)); + if (ret) { + ath11k_err(ab, "failed to set pci consistent dma mask to %d: %d\n", + ATH11K_PCI_DMA_MASK, ret); + goto release_region; + } + + pci_set_master(pdev); + + ab->mem_len = pci_resource_len(pdev, ATH11K_PCI_BAR_NUM); + ab->mem = pci_iomap(pdev, ATH11K_PCI_BAR_NUM, 0); + if (!ab->mem) { + ath11k_err(ab, "failed to map pci bar %d\n", ATH11K_PCI_BAR_NUM); + ret = -EIO; + goto clear_master; + } + + ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot pci_mem 0x%pK\n", ab->mem); + return 0; + +clear_master: + pci_clear_master(pdev); +release_region: + pci_release_region(pdev, ATH11K_PCI_BAR_NUM); +disable_device: + pci_disable_device(pdev); +out: + return ret; +} + +static void ath11k_pci_free_region(struct ath11k_pci *ab_pci) +{ + struct ath11k_base *ab = ab_pci->ab; + struct pci_dev *pci_dev = ab_pci->pdev; + + pci_iounmap(pci_dev, ab->mem); + ab->mem = NULL; + pci_clear_master(pci_dev); + pci_release_region(pci_dev, ATH11K_PCI_BAR_NUM); + if (pci_is_enabled(pci_dev)) + pci_disable_device(pci_dev); +} + +static int ath11k_pci_power_up(struct ath11k_base *ab) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + u8 aspm; + int ret; + + ath11k_pci_sw_reset(ab_pci->ab); + + pci_read_config_byte(ab_pci->pdev, 0x80, &aspm); + pci_write_config_byte(ab_pci->pdev, 0x80, aspm & 0xfd); + + ath11k_info(ab, "aspm 0x%x changed to 0x%x\n", + aspm, aspm & 0xfd); + + ret = ath11k_mhi_start(ab_pci); + if (ret) { + ath11k_err(ab, "failed to start mhi: %d\n", ret); + return ret; + } + + return 0; +} + +static void ath11k_pci_power_down(struct ath11k_base *ab) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + + ath11k_mhi_stop(ab_pci); + ath11k_pci_force_wake(ab_pci->ab); + ath11k_pci_sw_reset(ab_pci->ab); +} + +static void ath11k_pci_kill_tasklets(struct ath11k_base *ab) +{ + int i; + + for (i = 0; i < ab->hw_params.ce_count; i++) { + struct ath11k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; + + if (ath11k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + + tasklet_kill(&ce_pipe->intr_tq); + } +} + +static void ath11k_pci_stop(struct ath11k_base *ab) +{ + ath11k_pci_ce_irqs_disable(ab); + ath11k_pci_sync_ce_irqs(ab); + ath11k_pci_kill_tasklets(ab); + ath11k_ce_cleanup_pipes(ab); +} + +static int ath11k_pci_start(struct ath11k_base *ab) +{ + ath11k_pci_ce_irqs_enable(ab); + ath11k_ce_rx_post_buf(ab); + + return 0; +} + +static int ath11k_pci_map_service_to_pipe(struct ath11k_base *ab, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe) +{ + const struct service_to_pipe *entry; + bool ul_set = false, dl_set = false; + int i; + + for (i = 0; i < ARRAY_SIZE(target_service_to_ce_map_wlan); i++) { + entry = &target_service_to_ce_map_wlan[i]; + + if (__le32_to_cpu(entry->service_id) != service_id) + continue; + + switch (__le32_to_cpu(entry->pipedir)) { + case PIPEDIR_NONE: + break; + case PIPEDIR_IN: + WARN_ON(dl_set); + *dl_pipe = __le32_to_cpu(entry->pipenum); + dl_set = true; + break; + case PIPEDIR_OUT: + WARN_ON(ul_set); + *ul_pipe = __le32_to_cpu(entry->pipenum); + ul_set = true; + break; + case PIPEDIR_INOUT: + WARN_ON(dl_set); + WARN_ON(ul_set); + *dl_pipe = __le32_to_cpu(entry->pipenum); + *ul_pipe = __le32_to_cpu(entry->pipenum); + dl_set = true; + ul_set = true; + break; + } + } + + if (WARN_ON(!ul_set || !dl_set)) + return -ENOENT; + + return 0; +} + +static const struct ath11k_hif_ops ath11k_pci_hif_ops = { + .start = ath11k_pci_start, + .stop = ath11k_pci_stop, + .read32 = ath11k_pci_read32, + .write32 = ath11k_pci_write32, + .power_down = ath11k_pci_power_down, + .power_up = ath11k_pci_power_up, + .irq_enable = ath11k_pci_ext_irq_enable, + .irq_disable = ath11k_pci_ext_irq_disable, + .get_msi_address = ath11k_pci_get_msi_address, + .get_user_msi_vector = ath11k_get_user_msi_assignment, + .map_service_to_pipe = ath11k_pci_map_service_to_pipe, +}; + +static int ath11k_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_dev) +{ + struct ath11k_base *ab; + struct ath11k_pci *ab_pci; + int ret; + u32 val; + + dev_warn(&pdev->dev, "WARNING: ath11k PCI support is experimental!\n"); + + ab = ath11k_core_alloc(&pdev->dev, sizeof(*ab_pci), ATH11K_BUS_PCI, + &ath11k_pci_bus_params); + if (!ab) { + dev_err(&pdev->dev, "failed to allocate ath11k base\n"); + return -ENOMEM; + } + + ab->dev = &pdev->dev; + pci_set_drvdata(pdev, ab); + ab_pci = ath11k_pci_priv(ab); + ab_pci->dev_id = pci_dev->device; + ab_pci->ab = ab; + ab_pci->pdev = pdev; + ab->hif.ops = &ath11k_pci_hif_ops; + pci_set_drvdata(pdev, ab); + spin_lock_init(&ab_pci->window_lock); + + ret = ath11k_pci_claim(ab_pci, pdev); + if (ret) { + ath11k_err(ab, "failed to claim device: %d\n", ret); + goto err_free_core; + } + + switch (pci_dev->device) { + case QCA6390_DEVICE_ID: + val = ath11k_pci_read32(ab, TCSR_SOC_HW_VERSION); + ath11k_dbg(ab, ATH11K_DBG_PCI, "tcsr_soc_hw_version 0x%x\n", val); + + if (FIELD_GET(TCSR_SOC_HW_VERSION_MAJOR_MASK, val) == 1) + ab->hw_rev = ATH11K_HW_QCA6390_HW11; + else + ab->hw_rev = ATH11K_HW_QCA6390_HW20; + break; + default: + dev_err(&pdev->dev, "Unknown PCI device found: 0x%x\n", + pci_dev->device); + return -ENOTSUPP; + } + + ret = ath11k_pci_enable_msi(ab_pci); + if (ret) { + ath11k_err(ab, "failed to enable msi: %d\n", ret); + goto err_pci_free_region; + } + + ret = ath11k_core_pre_init(ab); + if (ret) + goto err_pci_disable_msi; + + ret = ath11k_mhi_register(ab_pci); + if (ret) { + ath11k_err(ab, "failed to register mhi: %d\n", ret); + goto err_pci_disable_msi; + } + + ret = ath11k_hal_srng_init(ab); + if (ret) + goto err_mhi_unregister; + + ret = ath11k_ce_alloc_pipes(ab); + if (ret) { + ath11k_err(ab, "failed to allocate ce pipes: %d\n", ret); + goto err_hal_srng_deinit; + } + + ath11k_pci_init_qmi_ce_config(ab); + + ret = ath11k_pci_config_irq(ab); + if (ret) { + ath11k_err(ab, "failed to config irq: %d\n", ret); + goto err_ce_free; + } + + ret = ath11k_core_init(ab); + if (ret) { + ath11k_err(ab, "failed to init core: %d\n", ret); + goto err_free_irq; + } + return 0; + +err_free_irq: + ath11k_pci_free_irq(ab); + +err_ce_free: + ath11k_ce_free_pipes(ab); + +err_hal_srng_deinit: + ath11k_hal_srng_deinit(ab); + +err_mhi_unregister: + ath11k_mhi_unregister(ab_pci); + +err_pci_disable_msi: + ath11k_pci_disable_msi(ab_pci); + +err_pci_free_region: + ath11k_pci_free_region(ab_pci); + +err_free_core: + ath11k_core_free(ab); + + return ret; +} + +static void ath11k_pci_remove(struct pci_dev *pdev) +{ + struct ath11k_base *ab = pci_get_drvdata(pdev); + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + + set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags); + + ath11k_core_deinit(ab); + + ath11k_mhi_unregister(ab_pci); + + ath11k_pci_free_irq(ab); + ath11k_pci_disable_msi(ab_pci); + ath11k_pci_free_region(ab_pci); + + ath11k_hal_srng_deinit(ab); + ath11k_ce_free_pipes(ab); + ath11k_core_free(ab); +} + +static void ath11k_pci_shutdown(struct pci_dev *pdev) +{ + struct ath11k_base *ab = pci_get_drvdata(pdev); + + ath11k_pci_power_down(ab); +} + +static struct pci_driver ath11k_pci_driver = { + .name = "ath11k_pci", + .id_table = ath11k_pci_id_table, + .probe = ath11k_pci_probe, + .remove = ath11k_pci_remove, + .shutdown = ath11k_pci_shutdown, +}; + +static int ath11k_pci_init(void) +{ + int ret; + + ret = pci_register_driver(&ath11k_pci_driver); + if (ret) + pr_err("failed to register ath11k pci driver: %d\n", + ret); + + return ret; +} +module_init(ath11k_pci_init); + +static void ath11k_pci_exit(void) +{ + pci_unregister_driver(&ath11k_pci_driver); +} + +module_exit(ath11k_pci_exit); + +MODULE_DESCRIPTION("Driver support for Qualcomm Technologies 802.11ax WLAN PCIe devices"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath11k/pci.h b/drivers/net/wireless/ath/ath11k/pci.h new file mode 100644 index 000000000000..f2f280eb8b55 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/pci.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + */ +#ifndef _ATH11K_PCI_H +#define _ATH11K_PCI_H + +#include <linux/mhi.h> + +#include "core.h" + +#define PCIE_SOC_GLOBAL_RESET 0x3008 +#define PCIE_SOC_GLOBAL_RESET_V 1 + +#define WLAON_WARM_SW_ENTRY 0x1f80504 +#define WLAON_SOC_RESET_CAUSE_REG 0x01f8060c + +#define PCIE_Q6_COOKIE_ADDR 0x01f80500 +#define PCIE_Q6_COOKIE_DATA 0xc0000000 + +/* register to wake the UMAC from power collapse */ +#define PCIE_SCRATCH_0_SOC_PCIE_REG 0x4040 + +/* register used for handshake mechanism to validate UMAC is awake */ +#define PCIE_SOC_WAKE_PCIE_LOCAL_REG 0x3004 + +struct ath11k_msi_user { + char *name; + int num_vectors; + u32 base_vector; +}; + +struct ath11k_msi_config { + int total_vectors; + int total_users; + struct ath11k_msi_user *users; +}; + +struct ath11k_pci { + struct pci_dev *pdev; + struct ath11k_base *ab; + u16 dev_id; + char amss_path[100]; + u32 msi_ep_base_data; + struct mhi_controller *mhi_ctrl; + unsigned long mhi_state; + u32 register_window; + + /* protects register_window above */ + spinlock_t window_lock; +}; + +static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab) +{ + return (struct ath11k_pci *)ab->drv_priv; +} + +int ath11k_pci_get_user_msi_assignment(struct ath11k_pci *ar_pci, char *user_name, + int *num_vectors, u32 *user_base_data, + u32 *base_vector); +int ath11k_pci_get_msi_irq(struct device *dev, unsigned int vector); +void ath11k_pci_write32(struct ath11k_base *ab, u32 offset, u32 value); +u32 ath11k_pci_read32(struct ath11k_base *ab, u32 offset); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/peer.c b/drivers/net/wireless/ath/ath11k/peer.c index 297172538620..61ad9300eafb 100644 --- a/drivers/net/wireless/ath/ath11k/peer.c +++ b/drivers/net/wireless/ath/ath11k/peer.c @@ -223,9 +223,6 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif, peer = ath11k_peer_find_by_pdev_idx(ar->ab, ar->pdev_idx, param->peer_addr); if (peer) { spin_unlock_bh(&ar->ab->base_lock); - ath11k_info(ar->ab, - "ignoring the peer %pM creation on same pdev idx %d\n", - param->peer_addr, ar->pdev_idx); return -EINVAL; } spin_unlock_bh(&ar->ab->base_lock); diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index c00a99ad8dbc..ad6f16f83373 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -9,6 +9,9 @@ #include <linux/of.h> #include <linux/firmware.h> +#define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02 +#define HOST_CSTATE_BIT 0x04 + static struct qmi_elem_info qmi_wlanfw_host_cap_req_msg_v01_ei[] = { { .data_type = QMI_OPT_FLAG, @@ -1516,15 +1519,35 @@ static int ath11k_qmi_host_cap_send(struct ath11k_base *ab) req.bdf_support_valid = 1; req.bdf_support = 1; - req.m3_support_valid = 0; - req.m3_support = 0; - - req.m3_cache_support_valid = 0; - req.m3_cache_support = 0; + if (ab->bus_params.m3_fw_support) { + req.m3_support_valid = 1; + req.m3_support = 1; + req.m3_cache_support_valid = 1; + req.m3_cache_support = 1; + } else { + req.m3_support_valid = 0; + req.m3_support = 0; + req.m3_cache_support_valid = 0; + req.m3_cache_support = 0; + } req.cal_done_valid = 1; req.cal_done = ab->qmi.cal_done; + if (ab->hw_params.internal_sleep_clock) { + req.nm_modem_valid = 1; + + /* Notify firmware that this is non-qualcomm platform. */ + req.nm_modem |= HOST_CSTATE_BIT; + + /* Notify firmware about the sleep clock selection, + * nm_modem_bit[1] is used for this purpose. Host driver on + * non-qualcomm platforms should select internal sleep + * clock. + */ + req.nm_modem |= SLEEP_CLOCK_SELECT_INTERNAL_BIT; + } + ret = qmi_txn_init(&ab->qmi.handle, &txn, qmi_wlanfw_host_cap_resp_msg_v01_ei, &resp); if (ret < 0) @@ -1634,19 +1657,30 @@ static int ath11k_qmi_respond_fw_mem_request(struct ath11k_base *ab) memset(&resp, 0, sizeof(resp)); - req->mem_seg_len = ab->qmi.mem_seg_count; + /* For QCA6390 by default FW requests a block of ~4M contiguous + * DMA memory, it's hard to allocate from OS. So host returns + * failure to FW and FW will then request mulitple blocks of small + * chunk size memory. + */ + if (!ab->bus_params.fixed_mem_region && ab->qmi.target_mem_delayed) { + ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi delays mem_request %d\n", + ab->qmi.mem_seg_count); + memset(req, 0, sizeof(*req)); + } else { + req->mem_seg_len = ab->qmi.mem_seg_count; + + for (i = 0; i < req->mem_seg_len ; i++) { + req->mem_seg[i].addr = ab->qmi.target_mem[i].paddr; + req->mem_seg[i].size = ab->qmi.target_mem[i].size; + req->mem_seg[i].type = ab->qmi.target_mem[i].type; + } + } ret = qmi_txn_init(&ab->qmi.handle, &txn, qmi_wlanfw_respond_mem_resp_msg_v01_ei, &resp); if (ret < 0) goto out; - for (i = 0; i < req->mem_seg_len ; i++) { - req->mem_seg[i].addr = ab->qmi.target_mem[i].paddr; - req->mem_seg[i].size = ab->qmi.target_mem[i].size; - req->mem_seg[i].type = ab->qmi.target_mem[i].type; - } - ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, QMI_WLANFW_RESPOND_MEM_REQ_V01, QMI_WLANFW_RESPOND_MEM_REQ_MSG_V01_MAX_LEN, @@ -1674,15 +1708,63 @@ out: return ret; } +static void ath11k_qmi_free_target_mem_chunk(struct ath11k_base *ab) +{ + int i; + + if (ab->bus_params.fixed_mem_region) + return; + + for (i = 0; i < ab->qmi.mem_seg_count; i++) { + if (!ab->qmi.target_mem[i].vaddr) + continue; + + dma_free_coherent(ab->dev, + ab->qmi.target_mem[i].size, + ab->qmi.target_mem[i].vaddr, + ab->qmi.target_mem[i].paddr); + ab->qmi.target_mem[i].vaddr = NULL; + } +} + static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab) { + int i; + struct target_mem_chunk *chunk; + + ab->qmi.target_mem_delayed = false; + + for (i = 0; i < ab->qmi.mem_seg_count; i++) { + chunk = &ab->qmi.target_mem[i]; + chunk->vaddr = dma_alloc_coherent(ab->dev, + chunk->size, + &chunk->paddr, + GFP_KERNEL); + if (!chunk->vaddr) { + ath11k_err(ab, "failed to alloc memory, size: 0x%x, type: %u\n", + chunk->size, + chunk->type); + if (ab->qmi.mem_seg_count <= 2) { + ath11k_qmi_free_target_mem_chunk(ab); + ab->qmi.target_mem_delayed = true; + return 0; + } + return -EINVAL; + } + } + + return 0; +} + +static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) +{ int i, idx; for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) { switch (ab->qmi.target_mem[i].type) { case BDF_MEM_REGION_TYPE: - ab->qmi.target_mem[idx].paddr = ATH11K_QMI_BDF_ADDRESS; - ab->qmi.target_mem[idx].vaddr = ATH11K_QMI_BDF_ADDRESS; + ab->qmi.target_mem[idx].paddr = ab->hw_params.bdf_addr; + ab->qmi.target_mem[idx].vaddr = NULL; ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; idx++; @@ -1694,7 +1776,7 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab) } /* TODO ath11k does not support cold boot calibration */ ab->qmi.target_mem[idx].paddr = 0; - ab->qmi.target_mem[idx].vaddr = 0; + ab->qmi.target_mem[idx].vaddr = NULL; ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; idx++; @@ -1772,11 +1854,11 @@ static int ath11k_qmi_request_target_cap(struct ath11k_base *ab) strlcpy(ab->qmi.target.fw_build_id, resp.fw_build_id, sizeof(ab->qmi.target.fw_build_id)); - ath11k_info(ab, "qmi target: chip_id: 0x%x, chip_family: 0x%x, board_id: 0x%x, soc_id: 0x%x\n", + ath11k_info(ab, "chip_id 0x%x chip_family 0x%x board_id 0x%x soc_id 0x%x\n", ab->qmi.target.chip_id, ab->qmi.target.chip_family, ab->qmi.target.board_id, ab->qmi.target.soc_id); - ath11k_info(ab, "qmi fw_version: 0x%x fw_build_timestamp: %s fw_build_id: %s", + ath11k_info(ab, "fw_version 0x%x fw_build_timestamp %s fw_build_id %s", ab->qmi.target.fw_version, ab->qmi.target.fw_build_timestamp, ab->qmi.target.fw_build_id); @@ -1790,8 +1872,6 @@ ath11k_qmi_prepare_bdf_download(struct ath11k_base *ab, int type, struct qmi_wlanfw_bdf_download_req_msg_v01 *req, void __iomem *bdf_addr) { - struct device *dev = ab->dev; - char filename[ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE]; const struct firmware *fw_entry; struct ath11k_board_data bd; u32 fw_size; @@ -1812,11 +1892,10 @@ ath11k_qmi_prepare_bdf_download(struct ath11k_base *ab, int type, ath11k_core_free_bdf(ab, &bd); break; case ATH11K_QMI_FILE_TYPE_CALDATA: - snprintf(filename, sizeof(filename), - "%s/%s", ab->hw_params.fw.dir, ATH11K_QMI_DEFAULT_CAL_FILE_NAME); - ret = request_firmware(&fw_entry, filename, dev); + fw_entry = ath11k_core_firmware_request(ab, ATH11K_DEFAULT_CAL_FILE); if (ret) { - ath11k_warn(ab, "qmi failed to load CAL: %s\n", filename); + ath11k_warn(ab, "failed to load %s: %d\n", + ATH11K_DEFAULT_CAL_FILE, ret); goto out; } @@ -1825,8 +1904,6 @@ ath11k_qmi_prepare_bdf_download(struct ath11k_base *ab, int type, memcpy_toio(bdf_addr + ATH11K_QMI_CALDATA_OFFSET, fw_entry->data, fw_size); - ath11k_info(ab, "qmi downloading BDF: %s, size: %zu\n", - filename, fw_entry->size); release_firmware(fw_entry); break; @@ -1841,7 +1918,7 @@ out: return ret; } -static int ath11k_qmi_load_bdf(struct ath11k_base *ab) +static int ath11k_qmi_load_bdf_fixed_addr(struct ath11k_base *ab) { struct qmi_wlanfw_bdf_download_req_msg_v01 *req; struct qmi_wlanfw_bdf_download_resp_msg_v01 resp; @@ -1854,7 +1931,7 @@ static int ath11k_qmi_load_bdf(struct ath11k_base *ab) return -ENOMEM; memset(&resp, 0, sizeof(resp)); - bdf_addr = ioremap(ATH11K_QMI_BDF_ADDRESS, ATH11K_QMI_BDF_MAX_SIZE); + bdf_addr = ioremap(ab->hw_params.bdf_addr, ATH11K_QMI_BDF_MAX_SIZE); if (!bdf_addr) { ath11k_warn(ab, "qmi ioremap error for BDF\n"); ret = -EIO; @@ -1905,7 +1982,6 @@ static int ath11k_qmi_load_bdf(struct ath11k_base *ab) goto out_qmi_bdf; } } - ath11k_info(ab, "qmi BDF downloaded\n"); out_qmi_bdf: iounmap(bdf_addr); @@ -1914,8 +1990,143 @@ out: return ret; } +static int ath11k_qmi_load_bdf_qmi(struct ath11k_base *ab) +{ + struct qmi_wlanfw_bdf_download_req_msg_v01 *req; + struct qmi_wlanfw_bdf_download_resp_msg_v01 resp; + struct ath11k_board_data bd; + unsigned int remaining; + struct qmi_txn txn = {}; + int ret; + const u8 *temp; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + memset(&resp, 0, sizeof(resp)); + + memset(&bd, 0, sizeof(bd)); + ret = ath11k_core_fetch_bdf(ab, &bd); + if (ret) { + ath11k_warn(ab, "qmi failed to load bdf:\n"); + goto out; + } + + temp = bd.data; + remaining = bd.len; + + while (remaining) { + req->valid = 1; + req->file_id_valid = 1; + req->file_id = ab->qmi.target.board_id; + req->total_size_valid = 1; + req->total_size = bd.len; + req->seg_id_valid = 1; + req->data_valid = 1; + req->data_len = ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE; + req->bdf_type = ATH11K_QMI_BDF_TYPE_ELF; + req->bdf_type_valid = 1; + req->end_valid = 1; + req->end = 0; + + if (remaining > QMI_WLANFW_MAX_DATA_SIZE_V01) { + req->data_len = QMI_WLANFW_MAX_DATA_SIZE_V01; + } else { + req->data_len = remaining; + req->end = 1; + } + + memcpy(req->data, temp, req->data_len); + + ret = qmi_txn_init(&ab->qmi.handle, &txn, + qmi_wlanfw_bdf_download_resp_msg_v01_ei, + &resp); + if (ret < 0) + goto out_qmi_bdf; + + ret = qmi_send_request(&ab->qmi.handle, NULL, &txn, + QMI_WLANFW_BDF_DOWNLOAD_REQ_V01, + QMI_WLANFW_BDF_DOWNLOAD_REQ_MSG_V01_MAX_LEN, + qmi_wlanfw_bdf_download_req_msg_v01_ei, req); + if (ret < 0) { + qmi_txn_cancel(&txn); + goto out_qmi_bdf; + } + + ret = qmi_txn_wait(&txn, msecs_to_jiffies(ATH11K_QMI_WLANFW_TIMEOUT_MS)); + if (ret < 0) + goto out_qmi_bdf; + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + ath11k_warn(ab, "qmi BDF download failed, result: %d, err: %d\n", + resp.resp.result, resp.resp.error); + ret = resp.resp.result; + goto out_qmi_bdf; + } + remaining -= req->data_len; + temp += req->data_len; + req->seg_id++; + } + +out_qmi_bdf: + ath11k_core_free_bdf(ab, &bd); + +out: + kfree(req); + return ret; +} + +static int ath11k_qmi_m3_load(struct ath11k_base *ab) +{ + struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; + const struct firmware *fw; + char path[100]; + int ret; + + if (m3_mem->vaddr || m3_mem->size) + return 0; + + fw = ath11k_core_firmware_request(ab, ATH11K_M3_FILE); + if (IS_ERR(fw)) { + ret = PTR_ERR(fw); + ath11k_core_create_firmware_path(ab, ATH11K_M3_FILE, + path, sizeof(path)); + ath11k_err(ab, "failed to load %s: %d\n", path, ret); + return ret; + } + + m3_mem->vaddr = dma_alloc_coherent(ab->dev, + fw->size, &m3_mem->paddr, + GFP_KERNEL); + if (!m3_mem->vaddr) { + ath11k_err(ab, "failed to allocate memory for M3 with size %zu\n", + fw->size); + release_firmware(fw); + return -ENOMEM; + } + + memcpy(m3_mem->vaddr, fw->data, fw->size); + m3_mem->size = fw->size; + release_firmware(fw); + + return 0; +} + +static void ath11k_qmi_m3_free(struct ath11k_base *ab) +{ + struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; + + if (!ab->bus_params.m3_fw_support || !m3_mem->vaddr) + return; + + dma_free_coherent(ab->dev, m3_mem->size, + m3_mem->vaddr, m3_mem->paddr); + m3_mem->vaddr = NULL; +} + static int ath11k_qmi_wlanfw_m3_info_send(struct ath11k_base *ab) { + struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; struct qmi_wlanfw_m3_info_req_msg_v01 req; struct qmi_wlanfw_m3_info_resp_msg_v01 resp; struct qmi_txn txn = {}; @@ -1923,8 +2134,20 @@ static int ath11k_qmi_wlanfw_m3_info_send(struct ath11k_base *ab) memset(&req, 0, sizeof(req)); memset(&resp, 0, sizeof(resp)); - req.addr = 0; - req.size = 0; + + if (ab->bus_params.m3_fw_support) { + ret = ath11k_qmi_m3_load(ab); + if (ret) { + ath11k_err(ab, "failed to load m3 firmware: %d", ret); + return ret; + } + + req.addr = m3_mem->paddr; + req.size = m3_mem->size; + } else { + req.addr = 0; + req.size = 0; + } ret = qmi_txn_init(&ab->qmi.handle, &txn, qmi_wlanfw_m3_info_resp_msg_v01_ei, &resp); @@ -2034,7 +2257,7 @@ static int ath11k_qmi_wlanfw_wlan_cfg_send(struct ath11k_base *ab) req->tgt_cfg_valid = 1; /* This is number of CE configs */ req->tgt_cfg_len = ab->qmi.ce_cfg.tgt_ce_len; - for (pipe_num = 0; pipe_num <= req->tgt_cfg_len ; pipe_num++) { + for (pipe_num = 0; pipe_num < req->tgt_cfg_len ; pipe_num++) { req->tgt_cfg[pipe_num].pipe_num = ce_cfg[pipe_num].pipenum; req->tgt_cfg[pipe_num].pipe_dir = ce_cfg[pipe_num].pipedir; req->tgt_cfg[pipe_num].nentries = ce_cfg[pipe_num].nentries; @@ -2181,7 +2404,10 @@ static void ath11k_qmi_event_load_bdf(struct ath11k_qmi *qmi) return; } - ret = ath11k_qmi_load_bdf(ab); + if (ab->bus_params.fixed_bdf_addr) + ret = ath11k_qmi_load_bdf_fixed_addr(ab); + else + ret = ath11k_qmi_load_bdf_qmi(ab); if (ret < 0) { ath11k_warn(ab, "qmi failed to load board data file:%d\n", ret); return; @@ -2220,10 +2446,20 @@ static void ath11k_qmi_msg_mem_request_cb(struct qmi_handle *qmi_hdl, msg->mem_seg[i].type, msg->mem_seg[i].size); } - ret = ath11k_qmi_alloc_target_mem_chunk(ab); - if (ret < 0) { - ath11k_warn(ab, "qmi failed to alloc target memory:%d\n", ret); - return; + if (ab->bus_params.fixed_mem_region) { + ret = ath11k_qmi_assign_target_mem_chunk(ab); + if (ret) { + ath11k_warn(ab, "qmi failed to assign target memory: %d\n", + ret); + return; + } + } else { + ret = ath11k_qmi_alloc_target_mem_chunk(ab); + if (ret) { + ath11k_warn(ab, "qmi failed to alloc target memory: %d\n", + ret); + return; + } } ath11k_qmi_driver_event_post(qmi, ATH11K_QMI_EVENT_REQUEST_MEM, NULL); @@ -2265,21 +2501,21 @@ static const struct qmi_msg_handler ath11k_qmi_msg_handlers[] = { .type = QMI_INDICATION, .msg_id = QMI_WLFW_REQUEST_MEM_IND_V01, .ei = qmi_wlanfw_request_mem_ind_msg_v01_ei, - .decoded_size = sizeof(qmi_wlanfw_request_mem_ind_msg_v01_ei), + .decoded_size = sizeof(struct qmi_wlanfw_request_mem_ind_msg_v01), .fn = ath11k_qmi_msg_mem_request_cb, }, { .type = QMI_INDICATION, .msg_id = QMI_WLFW_FW_MEM_READY_IND_V01, .ei = qmi_wlanfw_mem_ready_ind_msg_v01_ei, - .decoded_size = sizeof(qmi_wlanfw_mem_ready_ind_msg_v01_ei), + .decoded_size = sizeof(struct qmi_wlanfw_fw_mem_ready_ind_msg_v01), .fn = ath11k_qmi_msg_mem_ready_cb, }, { .type = QMI_INDICATION, .msg_id = QMI_WLFW_FW_READY_IND_V01, .ei = qmi_wlanfw_fw_ready_ind_msg_v01_ei, - .decoded_size = sizeof(qmi_wlanfw_fw_ready_ind_msg_v01_ei), + .decoded_size = sizeof(struct qmi_wlanfw_fw_ready_ind_msg_v01), .fn = ath11k_qmi_msg_fw_ready_cb, }, { @@ -2287,7 +2523,7 @@ static const struct qmi_msg_handler ath11k_qmi_msg_handlers[] = { .msg_id = QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01, .ei = qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei, .decoded_size = - sizeof(qmi_wlanfw_cold_boot_cal_done_ind_msg_v01_ei), + sizeof(struct qmi_wlanfw_fw_cold_cal_done_ind_msg_v01), .fn = ath11k_qmi_msg_cold_boot_cal_done_cb, }, }; @@ -2416,9 +2652,10 @@ int ath11k_qmi_init_service(struct ath11k_base *ab) ret = qmi_add_lookup(&ab->qmi.handle, ATH11K_QMI_WLFW_SERVICE_ID_V01, ATH11K_QMI_WLFW_SERVICE_VERS_V01, - ATH11K_QMI_WLFW_SERVICE_INS_ID_V01); + ab->qmi.service_ins_id); if (ret < 0) { ath11k_warn(ab, "failed to add qmi lookup\n"); + destroy_workqueue(ab->qmi.event_wq); return ret; } @@ -2430,5 +2667,7 @@ void ath11k_qmi_deinit_service(struct ath11k_base *ab) qmi_handle_release(&ab->qmi.handle); cancel_work_sync(&ab->qmi.event_work); destroy_workqueue(ab->qmi.event_wq); + ath11k_qmi_m3_free(ab); + ath11k_qmi_free_target_mem_chunk(ab); } diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h index 3f7db642d869..5d69c48df7fb 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.h +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -12,18 +12,18 @@ #define ATH11K_HOST_VERSION_STRING "WIN" #define ATH11K_QMI_WLANFW_TIMEOUT_MS 5000 #define ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE 64 -#define ATH11K_QMI_BDF_ADDRESS 0x4B0C0000 #define ATH11K_QMI_BDF_MAX_SIZE (256 * 1024) #define ATH11K_QMI_CALDATA_OFFSET (128 * 1024) #define ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128 #define ATH11K_QMI_WLFW_SERVICE_ID_V01 0x45 #define ATH11K_QMI_WLFW_SERVICE_VERS_V01 0x01 #define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01 0x02 +#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390 0x01 +#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ8074 0x02 #define ATH11K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 32 #define ATH11K_QMI_RESP_LEN_MAX 8192 #define ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 32 #define ATH11K_QMI_CALDB_SIZE 0x480000 -#define ATH11K_QMI_DEFAULT_CAL_FILE_NAME "caldata.bin" #define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035 #define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037 @@ -42,6 +42,11 @@ enum ath11k_qmi_file_type { ATH11K_QMI_MAX_FILE_TYPE, }; +enum ath11k_qmi_bdf_type { + ATH11K_QMI_BDF_TYPE_BIN = 0, + ATH11K_QMI_BDF_TYPE_ELF = 1, +}; + enum ath11k_qmi_event_type { ATH11K_QMI_EVENT_SERVER_ARRIVE, ATH11K_QMI_EVENT_SERVER_EXIT, @@ -85,7 +90,7 @@ struct target_mem_chunk { u32 size; u32 type; dma_addr_t paddr; - u32 vaddr; + u32 *vaddr; }; struct target_info { @@ -98,6 +103,12 @@ struct target_info { char fw_build_id[ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 + 1]; }; +struct m3_mem_region { + u32 size; + dma_addr_t paddr; + void *vaddr; +}; + struct ath11k_qmi { struct ath11k_base *ab; struct qmi_handle handle; @@ -110,8 +121,11 @@ struct ath11k_qmi { struct target_mem_chunk target_mem[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01]; u32 mem_seg_count; u32 target_mem_mode; + bool target_mem_delayed; u8 cal_done; struct target_info target; + struct m3_mem_region m3_mem; + unsigned int service_ins_id; }; #define QMI_WLANFW_HOST_CAP_REQ_MSG_V01_MAX_LEN 189 @@ -254,6 +268,14 @@ struct qmi_wlanfw_fw_mem_ready_ind_msg_v01 { char placeholder; }; +struct qmi_wlanfw_fw_ready_ind_msg_v01 { + char placeholder; +}; + +struct qmi_wlanfw_fw_cold_cal_done_ind_msg_v01 { + char placeholder; +}; + #define QMI_WLANFW_CAP_REQ_MSG_V01_MAX_LEN 0 #define QMI_WLANFW_CAP_RESP_MSG_V01_MAX_LEN 207 #define QMI_WLANFW_CAP_REQ_V01 0x0024 diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index 7c9dc91cc48a..0ba80e6f3979 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -699,7 +699,7 @@ void ath11k_reg_free(struct ath11k_base *ab) { int i; - for (i = 0; i < MAX_RADIOS; i++) { + for (i = 0; i < ab->hw_params.max_radios; i++) { kfree(ab->default_regd[i]); kfree(ab->new_regd[i]); } diff --git a/drivers/net/wireless/ath/ath11k/spectral.c b/drivers/net/wireless/ath/ath11k/spectral.c index 1c5d65bb411f..6cbe6f4e2864 100644 --- a/drivers/net/wireless/ath/ath11k/spectral.c +++ b/drivers/net/wireless/ath/ath11k/spectral.c @@ -954,10 +954,8 @@ int ath11k_spectral_init(struct ath11k_base *ab) int i; if (!test_bit(WMI_TLV_SERVICE_FREQINFO_IN_METADATA, - ab->wmi_ab.svc_map)) { - ath11k_info(ab, "spectral not supported\n"); + ab->wmi_ab.svc_map)) return 0; - } for (i = 0; i < ab->num_radios; i++) { ar = ab->pdevs[i].ar; @@ -966,10 +964,8 @@ int ath11k_spectral_init(struct ath11k_base *ab) ret = ath11k_dbring_get_cap(ar->ab, ar->pdev_idx, WMI_DIRECT_BUF_SPECTRAL, &db_cap); - if (ret) { - ath11k_info(ab, "spectral not enabled for pdev %d\n", i); + if (ret) continue; - } idr_init(&sp->rx_ring.bufs_idr); spin_lock_init(&sp->rx_ring.idr_lock); diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 8e3437a65673..03b669de3300 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -338,7 +338,7 @@ ath11k_pull_mac_phy_cap_svc_ready_ext(struct ath11k_pdev_wmi *wmi_handle, mac_phy_caps = wmi_mac_phy_caps + phy_idx; pdev->pdev_id = mac_phy_caps->pdev_id; - pdev_cap->supported_bands = mac_phy_caps->supported_bands; + pdev_cap->supported_bands |= mac_phy_caps->supported_bands; pdev_cap->ampdu_density = mac_phy_caps->ampdu_density; /* Take non-zero tx/rx chainmask. If tx/rx chainmask differs from @@ -371,27 +371,33 @@ ath11k_pull_mac_phy_cap_svc_ready_ext(struct ath11k_pdev_wmi *wmi_handle, pdev_cap->rx_chain_mask_shift = find_first_bit((unsigned long *)&pdev_cap->rx_chain_mask, 32); - cap_band = &pdev_cap->band[NL80211_BAND_2GHZ]; - cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_2g; - cap_band->ht_cap_info = mac_phy_caps->ht_cap_info_2g; - cap_band->he_cap_info[0] = mac_phy_caps->he_cap_info_2g; - cap_band->he_cap_info[1] = mac_phy_caps->he_cap_info_2g_ext; - cap_band->he_mcs = mac_phy_caps->he_supp_mcs_2g; - memcpy(cap_band->he_cap_phy_info, &mac_phy_caps->he_cap_phy_info_2g, - sizeof(u32) * PSOC_HOST_MAX_PHY_SIZE); - memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet2g, - sizeof(struct ath11k_ppe_threshold)); - - cap_band = &pdev_cap->band[NL80211_BAND_5GHZ]; - cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_5g; - cap_band->ht_cap_info = mac_phy_caps->ht_cap_info_5g; - cap_band->he_cap_info[0] = mac_phy_caps->he_cap_info_5g; - cap_band->he_cap_info[1] = mac_phy_caps->he_cap_info_5g_ext; - cap_band->he_mcs = mac_phy_caps->he_supp_mcs_5g; - memcpy(cap_band->he_cap_phy_info, &mac_phy_caps->he_cap_phy_info_5g, - sizeof(u32) * PSOC_HOST_MAX_PHY_SIZE); - memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet5g, - sizeof(struct ath11k_ppe_threshold)); + if (mac_phy_caps->supported_bands & WMI_HOST_WLAN_2G_CAP) { + cap_band = &pdev_cap->band[NL80211_BAND_2GHZ]; + cap_band->phy_id = mac_phy_caps->phy_id; + cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_2g; + cap_band->ht_cap_info = mac_phy_caps->ht_cap_info_2g; + cap_band->he_cap_info[0] = mac_phy_caps->he_cap_info_2g; + cap_band->he_cap_info[1] = mac_phy_caps->he_cap_info_2g_ext; + cap_band->he_mcs = mac_phy_caps->he_supp_mcs_2g; + memcpy(cap_band->he_cap_phy_info, &mac_phy_caps->he_cap_phy_info_2g, + sizeof(u32) * PSOC_HOST_MAX_PHY_SIZE); + memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet2g, + sizeof(struct ath11k_ppe_threshold)); + } + + if (mac_phy_caps->supported_bands & WMI_HOST_WLAN_5G_CAP) { + cap_band = &pdev_cap->band[NL80211_BAND_5GHZ]; + cap_band->phy_id = mac_phy_caps->phy_id; + cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_5g; + cap_band->ht_cap_info = mac_phy_caps->ht_cap_info_5g; + cap_band->he_cap_info[0] = mac_phy_caps->he_cap_info_5g; + cap_band->he_cap_info[1] = mac_phy_caps->he_cap_info_5g_ext; + cap_band->he_mcs = mac_phy_caps->he_supp_mcs_5g; + memcpy(cap_band->he_cap_phy_info, &mac_phy_caps->he_cap_phy_info_5g, + sizeof(u32) * PSOC_HOST_MAX_PHY_SIZE); + memcpy(&cap_band->he_ppet, &mac_phy_caps->he_ppet5g, + sizeof(struct ath11k_ppe_threshold)); + } cap_band = &pdev_cap->band[NL80211_BAND_6GHZ]; cap_band->max_bw_supported = mac_phy_caps->max_bw_supported_5g; @@ -3175,7 +3181,7 @@ static int ath11k_init_cmd_send(struct ath11k_pdev_wmi *wmi, (param->num_band_to_mac * sizeof(*band_to_mac)); len = sizeof(*cmd) + TLV_HDR_SIZE + sizeof(*cfg) + hw_mode_len + - (sizeof(*host_mem_chunks) * WMI_MAX_MEM_REQS); + (param->num_mem_chunks ? (sizeof(*host_mem_chunks) * WMI_MAX_MEM_REQS) : 0); skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); if (!skb) @@ -3381,6 +3387,8 @@ int ath11k_wmi_cmd_init(struct ath11k_base *ab) config.twt_ap_pdev_count = ab->num_radios; config.twt_ap_sta_count = 1000; + ab->hw_params.hw_ops->wmi_init_config(ab, &config); + memcpy(&wmi_sc->wlan_resource_config, &config, sizeof(config)); init_param.res_cfg = &wmi_sc->wlan_resource_config; @@ -3391,9 +3399,10 @@ int ath11k_wmi_cmd_init(struct ath11k_base *ab) if (wmi_sc->preferred_hw_mode == WMI_HOST_HW_MODE_SINGLE) init_param.hw_mode_id = WMI_HOST_HW_MODE_MAX; - init_param.num_band_to_mac = ab->num_radios; - - ath11k_fill_band_to_mac_param(ab, init_param.band_to_mac); + if (ab->hw_params.needs_band_to_mac) { + init_param.num_band_to_mac = ab->num_radios; + ath11k_fill_band_to_mac_param(ab, init_param.band_to_mac); + } return ath11k_init_cmd_send(&wmi_sc->wmi[0], &init_param); } @@ -3688,6 +3697,8 @@ static int ath11k_wmi_tlv_hw_mode_caps(struct ath11k_base *soc, i++; } + ath11k_dbg(soc, ATH11K_DBG_WMI, "preferred_hw_mode:%d\n", + soc->wmi_ab.preferred_hw_mode); if (soc->wmi_ab.preferred_hw_mode == WMI_HOST_HW_MODE_MAX) return -EINVAL; @@ -3778,6 +3789,7 @@ static int ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(struct ath11k_base *soc, struct wmi_tlv_svc_rdy_ext_parse *svc_rdy_ext = data; u8 hw_mode_id = svc_rdy_ext->pref_hw_mode_caps.hw_mode_id; u32 phy_id_map; + int pdev_index = 0; int ret; svc_rdy_ext->soc_hal_reg_caps = (struct wmi_soc_hal_reg_capabilities *)ptr; @@ -3793,7 +3805,7 @@ static int ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(struct ath11k_base *soc, svc_rdy_ext->soc_hal_reg_caps, svc_rdy_ext->mac_phy_caps, hw_mode_id, soc->num_radios, - &soc->pdevs[soc->num_radios]); + &soc->pdevs[pdev_index]); if (ret) { ath11k_warn(soc, "failed to extract mac caps, idx :%d\n", soc->num_radios); @@ -3802,9 +3814,25 @@ static int ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(struct ath11k_base *soc, soc->num_radios++; + /* For QCA6390, save mac_phy capability in the same pdev */ + if (soc->hw_params.single_pdev_only) + pdev_index = 0; + else + pdev_index = soc->num_radios; + /* TODO: mac_phy_cap prints */ phy_id_map >>= 1; } + + /* For QCA6390, set num_radios to 1 because host manages + * both 2G and 5G radio in one pdev. + * Set pdev_id = 0 and 0 means soc level. + */ + if (soc->hw_params.single_pdev_only) { + soc->num_radios = 1; + soc->pdevs[0].pdev_id = 0; + } + return 0; } @@ -4341,7 +4369,6 @@ static int ath11k_pull_mgmt_rx_params_tlv(struct ath11k_base *ab, } hdr->pdev_id = ev->pdev_id; - hdr->chan_freq = ev->chan_freq; hdr->channel = ev->channel; hdr->snr = ev->snr; hdr->rate = ev->rate; @@ -5434,8 +5461,17 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk pdev_idx = reg_info->phy_id; - if (pdev_idx >= ab->num_radios) - goto fallback; + if (pdev_idx >= ab->num_radios) { + /* Process the event for phy0 only if single_pdev_only + * is true. If pdev_idx is valid but not 0, discard the + * event. Otherwise, it goes to fallback. + */ + if (ab->hw_params.single_pdev_only && + pdev_idx < ab->hw_params.num_rxmda_per_pdev) + goto mem_free; + else + goto fallback; + } /* Avoid multiple overwrites to default regd, during core * stop-start after mac registration. @@ -5713,9 +5749,7 @@ static void ath11k_mgmt_rx_event(struct ath11k_base *ab, struct sk_buff *skb) if (rx_ev.status & WMI_RX_STATUS_ERR_MIC) status->flag |= RX_FLAG_MMIC_ERROR; - if (rx_ev.chan_freq >= ATH11K_MIN_6G_FREQ) { - status->band = NL80211_BAND_6GHZ; - } else if (rx_ev.channel >= 1 && rx_ev.channel <= 14) { + if (rx_ev.channel >= 1 && rx_ev.channel <= 14) { status->band = NL80211_BAND_2GHZ; } else if (rx_ev.channel >= 36 && rx_ev.channel <= ATH11K_MAX_5G_CHAN) { status->band = NL80211_BAND_5GHZ; @@ -6682,7 +6716,7 @@ int ath11k_wmi_connect(struct ath11k_base *ab) u8 wmi_ep_count; wmi_ep_count = ab->htc.wmi_ep_count; - if (wmi_ep_count > MAX_RADIOS) + if (wmi_ep_count > ab->hw_params.max_radios) return -1; for (i = 0; i < wmi_ep_count; i++) @@ -6704,7 +6738,7 @@ int ath11k_wmi_pdev_attach(struct ath11k_base *ab, { struct ath11k_pdev_wmi *wmi_handle; - if (pdev_id >= MAX_RADIOS) + if (pdev_id >= ab->hw_params.max_radios) return -EINVAL; wmi_handle = &ab->wmi_ab.wmi[pdev_id]; @@ -6728,6 +6762,10 @@ int ath11k_wmi_attach(struct ath11k_base *ab) ab->wmi_ab.ab = ab; ab->wmi_ab.preferred_hw_mode = WMI_HOST_HW_MODE_MAX; + /* It's overwritten when service_ext_ready is handled */ + if (ab->hw_params.single_pdev_only) + ab->wmi_ab.preferred_hw_mode = WMI_HOST_HW_MODE_SINGLE; + /* TODO: Init remaining wmi soc resources required */ init_completion(&ab->wmi_ab.service_ready); init_completion(&ab->wmi_ab.unified_ready); diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 5a32ba0eb4f5..9d4b14ccd51c 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -4289,7 +4289,6 @@ struct wmi_pdev_temperature_event { #define WLAN_MGMT_TXRX_HOST_MAX_ANTENNA 4 struct mgmt_rx_event_params { - u32 chan_freq; u32 channel; u32 snr; u8 rssi_ctl[WLAN_MGMT_TXRX_HOST_MAX_ANTENNA]; @@ -4319,7 +4318,6 @@ struct wmi_mgmt_rx_hdr { u32 rx_tsf_l32; u32 rx_tsf_u32; u32 pdev_id; - u32 chan_freq; } __packed; #define MAX_ANTENNA_EIGHT 8 diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 979800c6f57f..234ea939d316 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -410,7 +410,7 @@ enum ath5k_radio { * This article claims Super G sticks to bonding of channels 5 and 6 for * USA: * - * http://www.pcworld.com/article/id,113428-page,1/article.html + * https://www.pcworld.com/article/id,113428-page,1/article.html * * The channel bonding seems to be driver specific though. * diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 65a4c142640d..b70acefdc3fb 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1098,7 +1098,7 @@ err: /** * ath5k_drain_tx_buffs - Empty tx buffers * - * @ah The &struct ath5k_hw + * @ah: The &struct ath5k_hw * * Empty tx buffers from all queues in preparation * of a reset or during shutdown. diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c index 307f1fea0a88..1fbc2c19848f 100644 --- a/drivers/net/wireless/ath/ath5k/eeprom.c +++ b/drivers/net/wireless/ath/ath5k/eeprom.c @@ -1172,13 +1172,13 @@ ath5k_cal_data_offset_2413(struct ath5k_eeprom_info *ee, int mode) offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11B) + AR5K_EEPROM_N_2GHZ_CHAN_2413 / 2; - /* fall through */ + fallthrough; case AR5K_EEPROM_MODE_11B: if (AR5K_EEPROM_HDR_11A(ee->ee_header)) offset += ath5k_pdgains_size_2413(ee, AR5K_EEPROM_MODE_11A) + AR5K_EEPROM_N_5GHZ_CHAN / 2; - /* fall through */ + fallthrough; case AR5K_EEPROM_MODE_11A: break; default: diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index 05140d8baa36..f2db7cf16566 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -101,6 +101,7 @@ static const unsigned int ack_rates_high[] = /** * ath5k_hw_get_frame_duration() - Get tx time of a frame * @ah: The &struct ath5k_hw + * @band: One of enum nl80211_band * @len: Frame's length in bytes * @rate: The @struct ieee80211_rate * @shortpre: Indicate short preample @@ -670,7 +671,7 @@ ath5k_hw_init_beacon_timers(struct ath5k_hw *ah, u32 next_beacon, u32 interval) break; case NL80211_IFTYPE_ADHOC: AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_ADHOC_BCN_ATIM); - /* fall through */ + fallthrough; default: /* On non-STA modes timer1 is used as next DMA * beacon alert (DBA) timer and timer2 as next @@ -913,7 +914,7 @@ ath5k_hw_set_opmode(struct ath5k_hw *ah, enum nl80211_iftype op_mode) pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE | (ah->ah_version == AR5K_AR5210 ? AR5K_STA_ID1_PWR_SV : 0); - /* fall through */ + fallthrough; case NL80211_IFTYPE_MONITOR: pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE | (ah->ah_version == AR5K_AR5210 ? @@ -945,7 +946,6 @@ ath5k_hw_set_opmode(struct ath5k_hw *ah, enum nl80211_iftype op_mode) * ath5k_hw_pcu_init() - Initialize PCU * @ah: The &struct ath5k_hw * @op_mode: One of enum nl80211_iftype - * @mode: One of enum ath5k_driver_mode * * This function is used to initialize PCU by setting current * operation mode and various other settings. diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index ae08572c4b58..00f9e347d414 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -3229,10 +3229,10 @@ ath5k_write_pwr_to_pdadc_table(struct ath5k_hw *ah, u8 ee_mode) switch (pdcurves) { case 3: reg |= AR5K_REG_SM(pdg_to_idx[2], AR5K_PHY_TPC_RG1_PDGAIN_3); - /* Fall through */ + fallthrough; case 2: reg |= AR5K_REG_SM(pdg_to_idx[1], AR5K_PHY_TPC_RG1_PDGAIN_2); - /* Fall through */ + fallthrough; case 1: reg |= AR5K_REG_SM(pdg_to_idx[0], AR5K_PHY_TPC_RG1_PDGAIN_1); break; @@ -3353,7 +3353,7 @@ ath5k_setup_channel_powertable(struct ath5k_hw *ah, table_min[pdg] = table_max[pdg] - 126; } - /* Fall through */ + fallthrough; case AR5K_PWRTABLE_PWR_TO_PCDAC: case AR5K_PWRTABLE_PWR_TO_PDADC: diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index 56d7925a0c2c..9fdb5283b39c 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -522,7 +522,7 @@ ath5k_hw_set_power_mode(struct ath5k_hw *ah, enum ath5k_power_mode mode, switch (mode) { case AR5K_PM_AUTO: staid &= ~AR5K_STA_ID1_DEFAULT_ANTENNA; - /* fallthrough */ + fallthrough; case AR5K_PM_NETWORK_SLEEP: if (set_chip) ath5k_hw_reg_write(ah, diff --git a/drivers/net/wireless/ath/ath5k/rfbuffer.h b/drivers/net/wireless/ath/ath5k/rfbuffer.h index aed34d9954c0..151935c4827f 100644 --- a/drivers/net/wireless/ath/ath5k/rfbuffer.h +++ b/drivers/net/wireless/ath/ath5k/rfbuffer.h @@ -42,7 +42,7 @@ * Also check out reg.h and U.S. Patent 6677779 B1 (about buffer * registers and control registers): * - * http://www.google.com/patents?id=qNURAAAAEBAJ + * https://www.google.com/patents?id=qNURAAAAEBAJ */ diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 67f8f2aa7a53..9c83e9a4299b 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -3897,19 +3897,19 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) switch (ar->hw.cap) { case WMI_11AN_CAP: ht = true; - /* fall through */ + fallthrough; case WMI_11A_CAP: band_5gig = true; break; case WMI_11GN_CAP: ht = true; - /* fall through */ + fallthrough; case WMI_11G_CAP: band_2gig = true; break; case WMI_11AGN_CAP: ht = true; - /* fall through */ + fallthrough; case WMI_11AG_CAP: band_2gig = true; band_5gig = true; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 811fad6d60c0..39bf19686175 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1752,7 +1752,7 @@ static int __ath6kl_init_hw_start(struct ath6kl *ar) ret = ath6kl_init_service_ep(ar); if (ret) { - ath6kl_err("Endpoint service initilisation failed: %d\n", ret); + ath6kl_err("Endpoint service initialization failed: %d\n", ret); goto err_cleanup_scatter; } diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 5e7ea838a921..d3aa9e7a37c2 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -389,7 +389,7 @@ void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel) if (!ik->valid || ik->key_type != WAPI_CRYPT) break; /* for WAPI, we need to set the delayed group key, continue: */ - /* fall through */ + fallthrough; case WPA_PSK_AUTH: case WPA2_PSK_AUTH: case (WPA_PSK_AUTH | WPA2_PSK_AUTH): @@ -430,6 +430,9 @@ void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr, ath6kl_dbg(ATH6KL_DBG_TRC, "new station %pM aid=%d\n", mac_addr, aid); + if (aid < 1 || aid > AP_MAX_NUM_STA) + return; + if (assoc_req_len > sizeof(struct ieee80211_hdr_3addr)) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) assoc_info; diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 5214dd7a3936..41d192709e8e 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -74,7 +74,7 @@ static const struct ani_ofdm_level_entry ofdm_level_table[] = { * Regardless of alignment in time, the antenna signals add constructively after * FFT and improve your reception. For more information: * - * http://en.wikipedia.org/wiki/Maximal-ratio_combining + * https://en.wikipedia.org/wiki/Maximal-ratio_combining */ struct ani_cck_level_entry { diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index dae95402eb3a..0d34356baf73 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -579,14 +579,14 @@ static void ar5008_hw_init_chain_masks(struct ath_hw *ah) case 0x5: REG_SET_BIT(ah, AR_PHY_ANALOG_SWAP, AR_PHY_SWAP_ALT_CHAIN); - /* fall through */ + fallthrough; case 0x3: if (ah->hw_version.macVersion == AR_SREV_REVISION_5416_10) { REG_WRITE(ah, AR_PHY_RX_CHAINMASK, 0x7); REG_WRITE(ah, AR_PHY_CAL_CHAINMASK, 0x7); break; } - /* fall through */ + fallthrough; case 0x1: case 0x2: case 0x7: diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c index 4b3c9b108197..ce9a0a53771e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c @@ -267,7 +267,7 @@ ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) switch (i->aggr) { case AGGR_BUF_FIRST: ctl6 |= SM(i->aggr_len, AR_AggrLen); - /* fall through */ + fallthrough; case AGGR_BUF_MIDDLE: ctl1 |= AR_IsAggr | AR_MoreAggr; ctl6 |= SM(i->ndelim, AR_PadDelim); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index 6f32b8d2ec7f..fcfed8e59d29 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -119,7 +119,7 @@ static int ar9002_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) aModeRefSel = 2; if (aModeRefSel) break; - /* fall through */ + fallthrough; case 1: default: aModeRefSel = 0; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index e1fe7a7c3ad8..76b538942a79 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -120,7 +120,7 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) switch (i->aggr) { case AGGR_BUF_FIRST: ctl17 |= SM(i->aggr_len, AR_AggrLen); - /* fall through */ + fallthrough; case AGGR_BUF_MIDDLE: ctl12 |= AR_IsAggr | AR_MoreAggr; ctl17 |= SM(i->ndelim, AR_PadDelim); diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index fd61ae4782b6..6cf087522157 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -706,7 +706,7 @@ void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif, "Move chanctx state from FORCE_ACTIVE to IDLE\n"); sc->sched.state = ATH_CHANCTX_STATE_IDLE; - /* fall through */ + fallthrough; case ATH_CHANCTX_EVENT_SWITCH: if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) || sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE || @@ -1080,7 +1080,7 @@ static void ath_offchannel_timer(struct timer_list *t) mod_timer(&sc->offchannel.timer, jiffies + HZ / 10); break; } - /* fall through */ + fallthrough; case ATH_OFFCHANNEL_SUSPEND: if (!sc->offchannel.scan_req) return; diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index 56b44fc7a8e6..9729a69d3e2e 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -402,7 +402,7 @@ static u32 ath9k_hw_def_get_eeprom(struct ath_hw *ah, return AR5416_PWR_TABLE_OFFSET_DB; case EEP_ANTENNA_GAIN_2G: band = 1; - /* fall through */ + fallthrough; case EEP_ANTENNA_GAIN_5G: return max_t(u8, max_t(u8, pModal[band].antennaGainCh[0], diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 3f563e02d17d..38f07420f4f9 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -1375,7 +1375,7 @@ static void ath9k_hif_usb_disconnect(struct usb_interface *interface) if (hif_dev->flags & HIF_USB_READY) { ath9k_htc_hw_deinit(hif_dev->htc_handle, unplugged); ath9k_hif_usb_dev_deinit(hif_dev); - ath9k_destoy_wmi(hif_dev->htc_handle->drv_priv); + ath9k_destroy_wmi(hif_dev->htc_handle->drv_priv); ath9k_htc_hw_free(hif_dev->htc_handle); } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 1d6ad8d46607..ac79dfd5be7a 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -973,7 +973,7 @@ err_init: ath9k_stop_wmi(priv); hif_dev = (struct hif_device_usb *)htc_handle->hif_dev; ath9k_hif_usb_dealloc_urbs(hif_dev); - ath9k_destoy_wmi(priv); + ath9k_destroy_wmi(priv); err_free: ieee80211_free_hw(hw); return ret; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index b353995bdd45..f4c2a8d83f50 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -974,7 +974,7 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, struct ath_htc_rx_status *rxstatus; struct ath_rx_status rx_stats; bool decrypt_error = false; - __be16 rs_datalen; + u16 rs_datalen; bool is_phyerr; if (skb->len < HTC_RX_FRAME_HEADER_SIZE) { diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c index d2e062eaf561..510e61e97dbc 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -339,6 +339,8 @@ void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle, if (skb) { htc_hdr = (struct htc_frame_hdr *) skb->data; + if (htc_hdr->endpoint_id >= ARRAY_SIZE(htc_handle->endpoint)) + goto ret; endpoint = &htc_handle->endpoint[htc_hdr->endpoint_id]; skb_pull(skb, sizeof(struct htc_frame_hdr)); diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 8c97db73e34c..6609ce122e6e 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1277,12 +1277,12 @@ static void ath9k_hw_set_operating_mode(struct ath_hw *ah, int opmode) REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); break; } - /* fall through */ + fallthrough; case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: set |= AR_STA_ID1_STA_AP; - /* fall through */ + fallthrough; case NL80211_IFTYPE_STATION: REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION); break; @@ -2293,7 +2293,7 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) case NL80211_IFTYPE_ADHOC: REG_SET_BIT(ah, AR_TXCFG, AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY); - /* fall through */ + fallthrough; case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 4d72cd7daaa2..9af9fa71c1c2 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -1014,6 +1014,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS); wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS); + wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0); } int ath9k_init_device(u16 devid, struct ath_softc *sc, diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index a47f6e978095..411eece01bb1 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -19,6 +19,9 @@ #include "ath9k.h" #include "btcoex.h" +static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 queues, bool drop); + u8 ath9k_parse_mpdudensity(u8 mpdudensity) { /* @@ -1701,6 +1704,15 @@ static int ath9k_set_key(struct ieee80211_hw *hw, return -EOPNOTSUPP; } + /* There may be MPDUs queued for the outgoing PTK key. Flush queues to + * make sure these are not send unencrypted or with a wrong (new) key + */ + if (cmd == DISABLE_KEY && key->flags & IEEE80211_KEY_FLAG_PAIRWISE) { + ieee80211_stop_queues(hw); + ath9k_flush(hw, vif, 0, true); + ieee80211_wake_queues(hw); + } + mutex_lock(&sc->mutex); ath9k_ps_wakeup(sc); ath_dbg(common, CONFIG, "Set HW Key %d\n", cmd); @@ -1934,7 +1946,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: flush = true; - /* fall through */ + fallthrough; case IEEE80211_AMPDU_TX_STOP_CONT: ath9k_ps_wakeup(sc); ath_tx_aggr_stop(sc, sta, tid); diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index f3461b193c7a..cff9af3af38d 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -825,6 +825,7 @@ static void ath_pci_aspm_init(struct ath_common *common) struct pci_dev *pdev = to_pci_dev(sc->dev); struct pci_dev *parent; u16 aspm; + int ret; if (!ah->is_pciexpress) return; @@ -866,8 +867,8 @@ static void ath_pci_aspm_init(struct ath_common *common) if (AR_SREV_9462(ah)) pci_read_config_dword(pdev, 0x70c, &ah->config.aspm_l1_fix); - pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &aspm); - if (aspm & (PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1)) { + ret = pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &aspm); + if (!ret && (aspm & (PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1))) { ah->aspm_enabled = true; /* Initialize PCIe PM and SERDES registers. */ ath9k_hw_configpcipowersave(ah, false); diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c index e7a3127395be..9cf5ae3f7298 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.c +++ b/drivers/net/wireless/ath/ath9k/wmi.c @@ -121,7 +121,7 @@ void ath9k_stop_wmi(struct ath9k_htc_priv *priv) mutex_unlock(&wmi->op_mutex); } -void ath9k_destoy_wmi(struct ath9k_htc_priv *priv) +void ath9k_destroy_wmi(struct ath9k_htc_priv *priv) { kfree(priv->wmi); } diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h index d8b912206232..9386b3a9d303 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.h +++ b/drivers/net/wireless/ath/ath9k/wmi.h @@ -189,7 +189,7 @@ void ath9k_wmi_event_tasklet(unsigned long data); void ath9k_fatal_work(struct work_struct *work); void ath9k_wmi_event_drain(struct ath9k_htc_priv *priv); void ath9k_stop_wmi(struct ath9k_htc_priv *priv); -void ath9k_destoy_wmi(struct ath9k_htc_priv *priv); +void ath9k_destroy_wmi(struct ath9k_htc_priv *priv); #define WMI_CMD(_wmi_cmd) \ do { \ diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c index a274eb0d1968..0813473793df 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -253,17 +253,15 @@ channel_detector_get(struct dfs_pattern_detector *dpd, u16 freq) static void dpd_reset(struct dfs_pattern_detector *dpd) { struct channel_detector *cd; - if (!list_empty(&dpd->channel_detectors)) - list_for_each_entry(cd, &dpd->channel_detectors, head) - channel_detector_reset(dpd, cd); + list_for_each_entry(cd, &dpd->channel_detectors, head) + channel_detector_reset(dpd, cd); } static void dpd_exit(struct dfs_pattern_detector *dpd) { struct channel_detector *cd, *cd0; - if (!list_empty(&dpd->channel_detectors)) - list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head) - channel_detector_exit(dpd, cd); + list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head) + channel_detector_exit(dpd, cd); kfree(dpd); } @@ -331,9 +329,8 @@ static bool dpd_set_domain(struct dfs_pattern_detector *dpd, return false; /* delete all channel detectors for previous DFS domain */ - if (!list_empty(&dpd->channel_detectors)) - list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head) - channel_detector_exit(dpd, cd); + list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head) + channel_detector_exit(dpd, cd); dpd->radar_spec = rt->radar_types; dpd->num_radar_types = rt->num_radar_types; diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index bab30f7a443c..63079231e48e 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -334,6 +334,7 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) spin_lock_irqsave(&wcn->dxe_lock, flags); skb = wcn->tx_ack_skb; wcn->tx_ack_skb = NULL; + del_timer(&wcn->tx_ack_timer); spin_unlock_irqrestore(&wcn->dxe_lock, flags); if (!skb) { @@ -345,6 +346,8 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) if (status == 1) info->flags |= IEEE80211_TX_STAT_ACK; + else + info->flags &= ~IEEE80211_TX_STAT_ACK; wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ack status: %d\n", status); @@ -352,6 +355,32 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) ieee80211_wake_queues(wcn->hw); } +static void wcn36xx_dxe_tx_timer(struct timer_list *t) +{ + struct wcn36xx *wcn = from_timer(wcn, t, tx_ack_timer); + struct ieee80211_tx_info *info; + unsigned long flags; + struct sk_buff *skb; + + /* TX Timeout */ + wcn36xx_dbg(WCN36XX_DBG_DXE, "TX timeout\n"); + + spin_lock_irqsave(&wcn->dxe_lock, flags); + skb = wcn->tx_ack_skb; + wcn->tx_ack_skb = NULL; + spin_unlock_irqrestore(&wcn->dxe_lock, flags); + + if (!skb) + return; + + info = IEEE80211_SKB_CB(skb); + info->flags &= ~IEEE80211_TX_STAT_ACK; + info->flags &= ~IEEE80211_TX_STAT_NOACK_TRANSMITTED; + + ieee80211_tx_status_irqsafe(wcn->hw, skb); + ieee80211_wake_queues(wcn->hw); +} + static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch) { struct wcn36xx_dxe_ctl *ctl; @@ -397,6 +426,7 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) { struct wcn36xx *wcn = (struct wcn36xx *)dev; int int_src, int_reason; + bool transmitted = false; wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src); @@ -434,8 +464,10 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) int_reason); if (int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK | - WCN36XX_CH_STAT_INT_ED_MASK)) + WCN36XX_CH_STAT_INT_ED_MASK)) { reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch); + transmitted = true; + } } if (int_src & WCN36XX_INT_MASK_CHAN_TX_L) { @@ -473,9 +505,27 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) int_reason); if (int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK | - WCN36XX_CH_STAT_INT_ED_MASK)) + WCN36XX_CH_STAT_INT_ED_MASK)) { reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch); + transmitted = true; + } + } + + spin_lock(&wcn->dxe_lock); + if (wcn->tx_ack_skb && transmitted) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(wcn->tx_ack_skb); + + /* TX complete, no need to wait for 802.11 ack indication */ + if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS && + info->flags & IEEE80211_TX_CTL_NO_ACK) { + info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + del_timer(&wcn->tx_ack_timer); + ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb); + wcn->tx_ack_skb = NULL; + ieee80211_wake_queues(wcn->hw); + } } + spin_unlock(&wcn->dxe_lock); return IRQ_HANDLED; } @@ -916,6 +966,8 @@ int wcn36xx_dxe_init(struct wcn36xx *wcn) if (ret < 0) goto out_err_irq; + timer_setup(&wcn->tx_ack_timer, wcn36xx_dxe_tx_timer, 0); + return 0; out_err_irq: @@ -934,6 +986,7 @@ void wcn36xx_dxe_deinit(struct wcn36xx *wcn) { free_irq(wcn->tx_irq, wcn); free_irq(wcn->rx_irq, wcn); + del_timer(&wcn->tx_ack_timer); if (wcn->tx_ack_skb) { ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb); diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 702b689c06df..b78919117264 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -163,7 +163,7 @@ static struct ieee80211_supported_band wcn_band_5ghz = { .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, .mcs = { .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, - .rx_highest = cpu_to_le16(72), + .rx_highest = cpu_to_le16(150), .tx_params = IEEE80211_HT_MCS_TX_DEFINED, } } @@ -354,8 +354,6 @@ static void wcn36xx_stop(struct ieee80211_hw *hw) wcn36xx_dbg(WCN36XX_DBG_MAC, "mac stop\n"); - cancel_work_sync(&wcn->scan_work); - mutex_lock(&wcn->scan_lock); if (wcn->scan_req) { struct cfg80211_scan_info scan_info = { @@ -392,9 +390,24 @@ static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed) int ch = WCN36XX_HW_CHANNEL(wcn); wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n", ch); - list_for_each_entry(tmp, &wcn->vif_list, list) { - vif = wcn36xx_priv_to_vif(tmp); - wcn36xx_smd_switch_channel(wcn, vif, ch); + + if (wcn->sw_scan_opchannel == ch) { + /* If channel is the initial operating channel, we may + * want to receive/transmit regular data packets, then + * simply stop the scan session. + */ + wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN); + } else if (wcn->sw_scan) { + /* A scan is ongoing, do not change the operating + * channel, but start a scan session on the channel. + */ + wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN); + wcn36xx_smd_start_scan(wcn, ch); + } else { + list_for_each_entry(tmp, &wcn->vif_list, list) { + vif = wcn36xx_priv_to_vif(tmp); + wcn36xx_smd_switch_channel(wcn, vif, ch); + } } } @@ -614,55 +627,26 @@ out: return ret; } -static void wcn36xx_hw_scan_worker(struct work_struct *work) +static int wcn36xx_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) { - struct wcn36xx *wcn = container_of(work, struct wcn36xx, scan_work); - struct cfg80211_scan_request *req = wcn->scan_req; - u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX]; - struct cfg80211_scan_info scan_info = {}; - bool aborted = false; + struct wcn36xx *wcn = hw->priv; int i; - wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 scan %d channels worker\n", req->n_channels); - - for (i = 0; i < req->n_channels; i++) - channels[i] = req->channels[i]->hw_value; - - wcn36xx_smd_update_scan_params(wcn, channels, req->n_channels); - - wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN); - for (i = 0; i < req->n_channels; i++) { - mutex_lock(&wcn->scan_lock); - aborted = wcn->scan_aborted; - mutex_unlock(&wcn->scan_lock); - - if (aborted) - break; - - wcn->scan_freq = req->channels[i]->center_freq; - wcn->scan_band = req->channels[i]->band; - - wcn36xx_smd_start_scan(wcn, req->channels[i]->hw_value); - msleep(30); - wcn36xx_smd_end_scan(wcn, req->channels[i]->hw_value); - - wcn->scan_freq = 0; + if (!get_feat_caps(wcn->fw_feat_caps, SCAN_OFFLOAD)) { + /* fallback to mac80211 software scan */ + return 1; } - wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN); - - scan_info.aborted = aborted; - ieee80211_scan_completed(wcn->hw, &scan_info); - mutex_lock(&wcn->scan_lock); - wcn->scan_req = NULL; - mutex_unlock(&wcn->scan_lock); -} + /* For unknown reason, the hardware offloaded scan only works with + * 2.4Ghz channels, fallback to software scan in other cases. + */ + for (i = 0; i < hw_req->req.n_channels; i++) { + if (hw_req->req.channels[i]->band != NL80211_BAND_2GHZ) + return 1; + } -static int wcn36xx_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_scan_request *hw_req) -{ - struct wcn36xx *wcn = hw->priv; mutex_lock(&wcn->scan_lock); if (wcn->scan_req) { mutex_unlock(&wcn->scan_lock); @@ -674,12 +658,6 @@ static int wcn36xx_hw_scan(struct ieee80211_hw *hw, mutex_unlock(&wcn->scan_lock); - if (!get_feat_caps(wcn->fw_feat_caps, SCAN_OFFLOAD)) { - /* legacy manual/sw scan */ - schedule_work(&wcn->scan_work); - return 0; - } - return wcn36xx_smd_start_hw_scan(wcn, vif, &hw_req->req); } @@ -696,16 +674,30 @@ static void wcn36xx_cancel_hw_scan(struct ieee80211_hw *hw, /* ieee80211_scan_completed will be called on FW scan * indication */ wcn36xx_smd_stop_hw_scan(wcn); - } else { - struct cfg80211_scan_info scan_info = { - .aborted = true, - }; - - cancel_work_sync(&wcn->scan_work); - ieee80211_scan_completed(wcn->hw, &scan_info); } } +static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) +{ + struct wcn36xx *wcn = hw->priv; + + wcn->sw_scan = true; + wcn->sw_scan_opchannel = WCN36XX_HW_CHANNEL(wcn); +} + +static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct wcn36xx *wcn = hw->priv; + + /* ensure that any scan session is finished */ + wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN); + wcn->sw_scan = false; + wcn->sw_scan_opchannel = 0; +} + static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta, enum nl80211_band band) { @@ -1083,6 +1075,7 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, u16 tid = params->tid; u16 *ssn = ¶ms->ssn; int ret = 0; + u8 session; wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n", action, tid); @@ -1092,10 +1085,11 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, switch (action) { case IEEE80211_AMPDU_RX_START: sta_priv->tid = tid; - wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0, - get_sta_index(vif, sta_priv)); - wcn36xx_smd_add_ba(wcn); - wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv)); + session = wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0, + get_sta_index(vif, sta_priv)); + wcn36xx_smd_add_ba(wcn, session); + wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv), tid, + session); break; case IEEE80211_AMPDU_RX_STOP: wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv)); @@ -1149,6 +1143,8 @@ static const struct ieee80211_ops wcn36xx_ops = { .set_key = wcn36xx_set_key, .hw_scan = wcn36xx_hw_scan, .cancel_hw_scan = wcn36xx_cancel_hw_scan, + .sw_scan_start = wcn36xx_sw_scan_start, + .sw_scan_complete = wcn36xx_sw_scan_complete, .bss_info_changed = wcn36xx_bss_info_changed, .set_rts_threshold = wcn36xx_set_rts_threshold, .sta_add = wcn36xx_sta_add, @@ -1173,6 +1169,7 @@ static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) ieee80211_hw_set(wcn->hw, SIGNAL_DBM); ieee80211_hw_set(wcn->hw, HAS_RATE_CONTROL); ieee80211_hw_set(wcn->hw, SINGLE_SCAN_ON_ALL_BANDS); + ieee80211_hw_set(wcn->hw, REPORTS_TX_ACK_STATUS); wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | @@ -1326,8 +1323,6 @@ static int wcn36xx_probe(struct platform_device *pdev) goto out_wq; } - INIT_WORK(&wcn->scan_work, wcn36xx_hw_scan_worker); - wcn->smd_channel = qcom_wcnss_open_channel(wcnss, "WLAN_CTRL", wcn36xx_smd_rsp_process, hw); if (IS_ERR(wcn->smd_channel)) { wcn36xx_err("failed to open WLAN_CTRL channel\n"); diff --git a/drivers/net/wireless/ath/wcn36xx/pmc.c b/drivers/net/wireless/ath/wcn36xx/pmc.c index 1976b80c235f..2936aaf53273 100644 --- a/drivers/net/wireless/ath/wcn36xx/pmc.c +++ b/drivers/net/wireless/ath/wcn36xx/pmc.c @@ -28,6 +28,7 @@ int wcn36xx_pmc_enter_bmps_state(struct wcn36xx *wcn, if (!ret) { wcn36xx_dbg(WCN36XX_DBG_PMC, "Entered BMPS\n"); vif_priv->pw_state = WCN36XX_BMPS; + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; } else { /* * One of the reasons why HW will not enter BMPS is because @@ -52,6 +53,7 @@ int wcn36xx_pmc_exit_bmps_state(struct wcn36xx *wcn, } wcn36xx_smd_exit_bmps(wcn, vif); vif_priv->pw_state = WCN36XX_FULL_POWER; + vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER; return 0; } diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 77269ac7f352..908cc6cf7b1a 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -45,8 +45,8 @@ static struct wcn36xx_cfg_val wcn36xx_cfg_vals[] = { WCN36XX_CFG_VAL(MAX_MEDIUM_TIME, 6000), WCN36XX_CFG_VAL(MAX_MPDUS_IN_AMPDU, 64), WCN36XX_CFG_VAL(RTS_THRESHOLD, 2347), - WCN36XX_CFG_VAL(SHORT_RETRY_LIMIT, 6), - WCN36XX_CFG_VAL(LONG_RETRY_LIMIT, 6), + WCN36XX_CFG_VAL(SHORT_RETRY_LIMIT, 15), + WCN36XX_CFG_VAL(LONG_RETRY_LIMIT, 15), WCN36XX_CFG_VAL(FRAGMENTATION_THRESHOLD, 8000), WCN36XX_CFG_VAL(DYNAMIC_THRESHOLD_ZERO, 5), WCN36XX_CFG_VAL(DYNAMIC_THRESHOLD_ONE, 10), @@ -2102,6 +2102,22 @@ out: return ret; } +static int wcn36xx_smd_add_ba_session_rsp(void *buf, int len, u8 *session) +{ + struct wcn36xx_hal_add_ba_session_rsp_msg *rsp; + + if (len < sizeof(*rsp)) + return -EINVAL; + + rsp = (struct wcn36xx_hal_add_ba_session_rsp_msg *)buf; + if (rsp->status != WCN36XX_FW_MSG_RESULT_SUCCESS) + return rsp->status; + + *session = rsp->ba_session_id; + + return 0; +} + int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn, struct ieee80211_sta *sta, u16 tid, @@ -2110,6 +2126,7 @@ int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn, u8 sta_index) { struct wcn36xx_hal_add_ba_session_req_msg msg_body; + u8 session_id; int ret; mutex_lock(&wcn->hal_mutex); @@ -2135,17 +2152,20 @@ int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn, wcn36xx_err("Sending hal_add_ba_session failed\n"); goto out; } - ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + ret = wcn36xx_smd_add_ba_session_rsp(wcn->hal_buf, wcn->hal_rsp_len, + &session_id); if (ret) { wcn36xx_err("hal_add_ba_session response failed err=%d\n", ret); goto out; } + + ret = session_id; out: mutex_unlock(&wcn->hal_mutex); return ret; } -int wcn36xx_smd_add_ba(struct wcn36xx *wcn) +int wcn36xx_smd_add_ba(struct wcn36xx *wcn, u8 session_id) { struct wcn36xx_hal_add_ba_req_msg msg_body; int ret; @@ -2153,7 +2173,7 @@ int wcn36xx_smd_add_ba(struct wcn36xx *wcn) mutex_lock(&wcn->hal_mutex); INIT_HAL_MSG(msg_body, WCN36XX_HAL_ADD_BA_REQ); - msg_body.session_id = 0; + msg_body.session_id = session_id; msg_body.win_size = WCN36XX_AGGR_BUFFER_SIZE; PREPARE_HAL_BUF(wcn->hal_buf, msg_body); @@ -2212,7 +2232,7 @@ static int wcn36xx_smd_trigger_ba_rsp(void *buf, int len) return rsp->status; } -int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index) +int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index, u16 tid, u8 session_id) { struct wcn36xx_hal_trigger_ba_req_msg msg_body; struct wcn36xx_hal_trigger_ba_req_candidate *candidate; @@ -2221,7 +2241,7 @@ int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index) mutex_lock(&wcn->hal_mutex); INIT_HAL_MSG(msg_body, WCN36XX_HAL_TRIGGER_BA_REQ); - msg_body.session_id = 0; + msg_body.session_id = session_id; msg_body.candidate_cnt = 1; msg_body.header.len += sizeof(*candidate); PREPARE_HAL_BUF(wcn->hal_buf, msg_body); @@ -2229,7 +2249,7 @@ int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index) candidate = (struct wcn36xx_hal_trigger_ba_req_candidate *) (wcn->hal_buf + sizeof(msg_body)); candidate->sta_index = sta_index; - candidate->tid_bitmap = 1; + candidate->tid_bitmap = 1 << tid; ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); if (ret) { diff --git a/drivers/net/wireless/ath/wcn36xx/smd.h b/drivers/net/wireless/ath/wcn36xx/smd.h index ff15df8ab56f..68c59df7a0ad 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.h +++ b/drivers/net/wireless/ath/wcn36xx/smd.h @@ -132,9 +132,9 @@ int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn, u16 *ssn, u8 direction, u8 sta_index); -int wcn36xx_smd_add_ba(struct wcn36xx *wcn); +int wcn36xx_smd_add_ba(struct wcn36xx *wcn, u8 session_id); int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 sta_index); -int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index); +int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index, u16 tid, u8 session_id); int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value); diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c index a6902371e89c..820505619f66 100644 --- a/drivers/net/wireless/ath/wcn36xx/txrx.c +++ b/drivers/net/wireless/ath/wcn36xx/txrx.c @@ -23,9 +23,104 @@ static inline int get_rssi0(struct wcn36xx_rx_bd *bd) return 100 - ((bd->phy_stat0 >> 24) & 0xff); } +struct wcn36xx_rate { + u16 bitrate; + u16 mcs_or_legacy_index; + enum mac80211_rx_encoding encoding; + enum mac80211_rx_encoding_flags encoding_flags; + enum rate_info_bw bw; +}; + +static const struct wcn36xx_rate wcn36xx_rate_table[] = { + /* 11b rates */ + { 10, 0, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 }, + { 20, 1, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 }, + { 55, 2, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 }, + { 110, 3, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 }, + + /* 11b SP (short preamble) */ + { 10, 0, RX_ENC_LEGACY, RX_ENC_FLAG_SHORTPRE, RATE_INFO_BW_20 }, + { 20, 1, RX_ENC_LEGACY, RX_ENC_FLAG_SHORTPRE, RATE_INFO_BW_20 }, + { 55, 2, RX_ENC_LEGACY, RX_ENC_FLAG_SHORTPRE, RATE_INFO_BW_20 }, + { 110, 3, RX_ENC_LEGACY, RX_ENC_FLAG_SHORTPRE, RATE_INFO_BW_20 }, + + /* 11ag */ + { 60, 4, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 }, + { 90, 5, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 }, + { 120, 6, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 }, + { 180, 7, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 }, + { 240, 8, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 }, + { 360, 9, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 }, + { 480, 10, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 }, + { 540, 11, RX_ENC_LEGACY, 0, RATE_INFO_BW_20 }, + + /* 11n */ + { 65, 0, RX_ENC_HT, 0, RATE_INFO_BW_20 }, + { 130, 1, RX_ENC_HT, 0, RATE_INFO_BW_20 }, + { 195, 2, RX_ENC_HT, 0, RATE_INFO_BW_20 }, + { 260, 3, RX_ENC_HT, 0, RATE_INFO_BW_20 }, + { 390, 4, RX_ENC_HT, 0, RATE_INFO_BW_20 }, + { 520, 5, RX_ENC_HT, 0, RATE_INFO_BW_20 }, + { 585, 6, RX_ENC_HT, 0, RATE_INFO_BW_20 }, + { 650, 7, RX_ENC_HT, 0, RATE_INFO_BW_20 }, + + /* 11n SGI */ + { 72, 0, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 }, + { 144, 1, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 }, + { 217, 2, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 }, + { 289, 3, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 }, + { 434, 4, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 }, + { 578, 5, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 }, + { 650, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 }, + { 722, 7, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_20 }, + + /* 11n GF (greenfield) */ + { 65, 0, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 }, + { 130, 1, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 }, + { 195, 2, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 }, + { 260, 3, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 }, + { 390, 4, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 }, + { 520, 5, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 }, + { 585, 6, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 }, + { 650, 7, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_20 }, + + /* 11n CB (channel bonding) */ + { 135, 0, RX_ENC_HT, 0, RATE_INFO_BW_40 }, + { 270, 1, RX_ENC_HT, 0, RATE_INFO_BW_40 }, + { 405, 2, RX_ENC_HT, 0, RATE_INFO_BW_40 }, + { 540, 3, RX_ENC_HT, 0, RATE_INFO_BW_40 }, + { 810, 4, RX_ENC_HT, 0, RATE_INFO_BW_40 }, + { 1080, 5, RX_ENC_HT, 0, RATE_INFO_BW_40 }, + { 1215, 6, RX_ENC_HT, 0, RATE_INFO_BW_40 }, + { 1350, 7, RX_ENC_HT, 0, RATE_INFO_BW_40 }, + + /* 11n CB + SGI */ + { 150, 0, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 }, + { 300, 1, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 }, + { 450, 2, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 }, + { 600, 3, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 }, + { 900, 4, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 }, + { 1200, 5, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 }, + { 1350, 6, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 }, + { 1500, 7, RX_ENC_HT, RX_ENC_FLAG_SHORT_GI, RATE_INFO_BW_40 }, + + /* 11n GF + CB */ + { 135, 0, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 }, + { 270, 1, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 }, + { 405, 2, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 }, + { 540, 3, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 }, + { 810, 4, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 }, + { 1080, 5, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 }, + { 1215, 6, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 }, + { 1350, 7, RX_ENC_HT, RX_ENC_FLAG_HT_GF, RATE_INFO_BW_40 }, + + /* TODO: AC rates */ +}; + int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) { struct ieee80211_rx_status status; + const struct wcn36xx_rate *rate; struct ieee80211_hdr *hdr; struct wcn36xx_rx_bd *bd; u16 fc, sn; @@ -49,19 +144,11 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) fc = __le16_to_cpu(hdr->frame_control); sn = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)); - /* When scanning associate beacons to this */ - if (ieee80211_is_beacon(hdr->frame_control) && wcn->scan_freq) { - status.freq = wcn->scan_freq; - status.band = wcn->scan_band; - } else { - status.freq = WCN36XX_CENTER_FREQ(wcn); - status.band = WCN36XX_BAND(wcn); - } - + status.freq = WCN36XX_CENTER_FREQ(wcn); + status.band = WCN36XX_BAND(wcn); status.mactime = 10; status.signal = -get_rssi0(bd); status.antenna = 1; - status.rate_idx = 1; status.flag = 0; status.rx_flags = 0; status.flag |= RX_FLAG_IV_STRIPPED | @@ -70,6 +157,19 @@ int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x\n", status.flag); + if (bd->rate_id < ARRAY_SIZE(wcn36xx_rate_table)) { + rate = &wcn36xx_rate_table[bd->rate_id]; + status.encoding = rate->encoding; + status.enc_flags = rate->encoding_flags; + status.bw = rate->bw; + status.rate_idx = rate->mcs_or_legacy_index; + } else { + status.encoding = 0; + status.bw = 0; + status.enc_flags = 0; + status.rate_idx = 0; + } + memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); if (ieee80211_is_beacon(hdr->frame_control)) { @@ -100,7 +200,8 @@ static void wcn36xx_set_tx_pdu(struct wcn36xx_tx_bd *bd, bd->pdu.mpdu_header_off; bd->pdu.mpdu_len = len; bd->pdu.tid = tid; - bd->pdu.bd_ssn = WCN36XX_TXBD_SSN_FILL_DPU_QOS; + /* Use seq number generated by mac80211 */ + bd->pdu.bd_ssn = WCN36XX_TXBD_SSN_FILL_HOST; } static inline struct wcn36xx_vif *get_vif_by_addr(struct wcn36xx *wcn, @@ -160,9 +261,11 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd, bool bcast) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_vif *vif = NULL; struct wcn36xx_vif *__vif_priv = NULL; - bool is_data_qos; + bool is_data_qos = ieee80211_is_data_qos(hdr->frame_control); + u16 tid = 0; bd->bd_rate = WCN36XX_BD_RATE_DATA; @@ -191,9 +294,21 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd, bd->dpu_sign = __vif_priv->self_ucast_dpu_sign; } - if (ieee80211_is_nullfunc(hdr->frame_control) || - (sta_priv && !sta_priv->is_data_encrypted)) + if (is_data_qos) { + tid = ieee80211_get_tid(hdr); + /* TID->QID is one-to-one mapping */ + bd->queue_id = tid; + } + + if (info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT || + (sta_priv && !sta_priv->is_data_encrypted)) { bd->dpu_ne = 1; + } + + if (ieee80211_is_any_nullfunc(hdr->frame_control)) { + /* Don't use a regular queue for null packet (no ampdu) */ + bd->queue_id = WCN36XX_TX_U_WQ_ID; + } if (bcast) { bd->ub = 1; @@ -201,13 +316,11 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd, } *vif_priv = __vif_priv; - is_data_qos = ieee80211_is_data_qos(hdr->frame_control); - wcn36xx_set_tx_pdu(bd, is_data_qos ? sizeof(struct ieee80211_qos_hdr) : sizeof(struct ieee80211_hdr_3addr), - skb->len, sta_priv ? sta_priv->tid : 0); + skb->len, tid); if (sta_priv && is_data_qos) wcn36xx_tx_start_ampdu(wcn, sta_priv, skb); @@ -287,9 +400,9 @@ int wcn36xx_start_tx(struct wcn36xx *wcn, bd.dpu_rf = WCN36XX_BMU_WQ_TX; - bd.tx_comp = !!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS); - if (bd.tx_comp) { + if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS) { wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested\n"); + spin_lock_irqsave(&wcn->dxe_lock, flags); if (wcn->tx_ack_skb) { spin_unlock_irqrestore(&wcn->dxe_lock, flags); @@ -302,10 +415,15 @@ int wcn36xx_start_tx(struct wcn36xx *wcn, /* Only one at a time is supported by fw. Stop the TX queues * until the ack status gets back. - * - * TODO: Add watchdog in case FW does not answer */ ieee80211_stop_queues(wcn->hw); + + /* TX watchdog if no TX irq or ack indication received */ + mod_timer(&wcn->tx_ack_timer, jiffies + HZ / 10); + + /* Request ack indication from the firmware */ + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) + bd.tx_comp = 1; } /* Data frames served first*/ @@ -319,7 +437,7 @@ int wcn36xx_start_tx(struct wcn36xx *wcn, bd.tx_bd_sign = 0xbdbdbdbd; ret = wcn36xx_dxe_tx_frame(wcn, vif_priv, &bd, skb, is_low); - if (ret && bd.tx_comp) { + if (ret && (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) { /* If the skb has not been transmitted, * don't keep a reference to it. */ diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h index a58f313983b9..efc9383b7dc4 100644 --- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -223,10 +223,9 @@ struct wcn36xx { spinlock_t hal_ind_lock; struct list_head hal_ind_queue; - struct work_struct scan_work; struct cfg80211_scan_request *scan_req; - int scan_freq; - int scan_band; + bool sw_scan; + u8 sw_scan_opchannel; struct mutex scan_lock; bool scan_aborted; @@ -245,6 +244,7 @@ struct wcn36xx { struct wcn36xx_dxe_mem_pool data_mem_pool; struct sk_buff *tx_ack_skb; + struct timer_list tx_ack_timer; /* RF module */ unsigned rf_id; diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 0851d2bede89..1c42410d68e1 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -1739,7 +1739,7 @@ static int wil_cancel_remain_on_channel(struct wiphy *wiphy, return wil_p2p_cancel_listen(vif, cookie); } -/** +/* * find a specific IE in a list of IEs * return a pointer to the beginning of IE in the list * or NULL if not found @@ -1766,7 +1766,7 @@ static const u8 *_wil_cfg80211_find_ie(const u8 *ies, u16 ies_len, const u8 *ie, ies_len); } -/** +/* * merge the IEs in two lists into a single list. * do not include IEs from the second list which exist in the first list. * add only vendor specific IEs from second list to keep diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index ab0da2ff982e..444639f09aa4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -3991,10 +3991,7 @@ brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, } brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmk[npmk].bssid); - for (i = 0; i < WLAN_PMKID_LEN; i += 4) - brcmf_dbg(CONN, "%02x %02x %02x %02x\n", pmk[npmk].pmkid[i], - pmk[npmk].pmkid[i + 1], pmk[npmk].pmkid[i + 2], - pmk[npmk].pmkid[i + 3]); + brcmf_dbg(CONN, "%*ph\n", WLAN_PMKID_LEN, pmk[npmk].pmkid); err = brcmf_update_pmklist(cfg, ifp); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index f89010a81ffb..aa9ced3c86fb 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -486,7 +486,7 @@ static int brcmf_rx_hdrpull(struct brcmf_pub *drvr, struct sk_buff *skb, ret = brcmf_proto_hdrpull(drvr, true, skb, ifp); if (ret || !(*ifp) || !(*ifp)->ndev) { - if (ret != -ENODATA && *ifp) + if (ret != -ENODATA && *ifp && (*ifp)->ndev) (*ifp)->ndev->stats.rx_errors++; brcmu_pkt_buf_free_skb(skb); return -ENODATA; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c index f1a20db8daab..bfddb851e386 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c @@ -1620,6 +1620,8 @@ fail: BRCMF_TX_IOCTL_MAX_MSG_SIZE, msgbuf->ioctbuf, msgbuf->ioctbuf_handle); + if (msgbuf->txflow_wq) + destroy_workqueue(msgbuf->txflow_wq); kfree(msgbuf); } return -ENOMEM; diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index ff932627a46c..2fb69a590bd8 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -1889,7 +1889,7 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, chan, CFG80211_BSS_FTYPE_UNKNOWN, bssid, timestamp, cap_info_bitmap, beacon_period, - ie_buf, ie_len, rssi, GFP_KERNEL); + ie_buf, ie_len, rssi, GFP_ATOMIC); if (bss) { bss_priv = (struct mwifiex_bss_priv *)bss->priv; bss_priv->band = band; diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c index 351cd055a295..b7a98dbbb09c 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c @@ -1009,12 +1009,12 @@ static int rtw8822b_set_antenna(struct rtw_dev *rtwdev, antenna_tx, antenna_rx); if (!rtw8822b_check_rf_path(antenna_tx)) { - rtw_info(rtwdev, "unsupport tx path 0x%x\n", antenna_tx); + rtw_info(rtwdev, "unsupported tx path 0x%x\n", antenna_tx); return -EINVAL; } if (!rtw8822b_check_rf_path(antenna_rx)) { - rtw_info(rtwdev, "unsupport rx path 0x%x\n", antenna_rx); + rtw_info(rtwdev, "unsupported rx path 0x%x\n", antenna_rx); return -EINVAL; } diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c index 426808413baa..ed1c14af082b 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c @@ -2014,7 +2014,7 @@ static int rtw8822c_set_antenna(struct rtw_dev *rtwdev, case BB_PATH_AB: break; default: - rtw_info(rtwdev, "unsupport tx path 0x%x\n", antenna_tx); + rtw_info(rtwdev, "unsupported tx path 0x%x\n", antenna_tx); return -EINVAL; } @@ -2024,7 +2024,7 @@ static int rtw8822c_set_antenna(struct rtw_dev *rtwdev, case BB_PATH_AB: break; default: - rtw_info(rtwdev, "unsupport rx path 0x%x\n", antenna_rx); + rtw_info(rtwdev, "unsupported rx path 0x%x\n", antenna_rx); return -EINVAL; } diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 8852a1832951..75b5d545b49e 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -3112,7 +3112,7 @@ static int rndis_wlan_get_caps(struct usbnet *usbdev, struct wiphy *wiphy) retval = rndis_query_oid(usbdev, RNDIS_OID_802_11_NETWORK_TYPES_SUPPORTED, &networks_supported, &len); - if (retval >= 0) { + if (!retval) { n = le32_to_cpu(networks_supported.num_items); if (n > 8) n = 8; @@ -3137,7 +3137,7 @@ static int rndis_wlan_get_caps(struct usbnet *usbdev, struct wiphy *wiphy) retval = rndis_query_oid(usbdev, RNDIS_OID_802_11_CAPABILITY, &caps, &len); - if (retval >= 0) { + if (!retval) { netdev_dbg(usbdev->net, "RNDIS_OID_802_11_CAPABILITY -> len %d, " "ver %d, pmkids %d, auth-encr-pairs %d\n", le32_to_cpu(caps.length), diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 480a8d084878..136a0d3b23c9 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -558,7 +558,7 @@ static int wl1251_build_null_data(struct wl1251 *wl) out: dev_kfree_skb(skb); if (ret) - wl1251_warning("cmd buld null data failed: %d", ret); + wl1251_warning("cmd build null data failed: %d", ret); return ret; } diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index 6ef8fc9ae627..93424a1dffc9 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -1080,7 +1080,7 @@ int wl12xx_cmd_build_null_data(struct wl1271 *wl, struct wl12xx_vif *wlvif) out: dev_kfree_skb(skb); if (ret) - wl1271_warning("cmd buld null data failed %d", ret); + wl1271_warning("cmd build null data failed %d", ret); return ret; diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 1ae1dfd01bee..a6782671ad48 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -57,6 +57,7 @@ #define PCIE20_PARF_SID_OFFSET 0x234 #define PCIE20_PARF_BDF_TRANSLATE_CFG 0x24C #define PCIE20_PARF_DEVICE_TYPE 0x1000 +#define PCIE20_PARF_BDF_TO_SID_TABLE_N 0x2000 #define PCIE20_ELBI_SYS_CTRL 0x04 #define PCIE20_ELBI_SYS_CTRL_LT_ENABLE BIT(0) @@ -1290,6 +1291,9 @@ static int qcom_pcie_host_init(struct pcie_port *pp) if (ret) goto err; + writel(0x0, pcie->parf + PCIE20_PARF_BDF_TO_SID_TABLE_N); + writel(0x01000100, pcie->parf + PCIE20_PARF_BDF_TO_SID_TABLE_N + 0x054); + return 0; err: qcom_ep_reset_assert(pcie); @@ -1370,6 +1374,7 @@ static int qcom_pcie_probe(struct platform_device *pdev) struct pcie_port *pp; struct dw_pcie *pci; struct qcom_pcie *pcie; + void __iomem *atu_base; int ret; pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); @@ -1422,6 +1427,11 @@ static int qcom_pcie_probe(struct platform_device *pdev) goto err_pm_runtime_put; } + /* Get the optional ATU region if provided */ + atu_base = devm_platform_ioremap_resource_byname(pdev, "atu"); + if (!IS_ERR(atu_base)) + pci->atu_base = atu_base; + pcie->phy = devm_phy_optional_get(dev, "pciephy"); if (IS_ERR(pcie->phy)) { ret = PTR_ERR(pcie->phy); @@ -1476,6 +1486,7 @@ static const struct of_device_id qcom_pcie_match[] = { { .compatible = "qcom,pcie-ipq4019", .data = &ops_2_4_0 }, { .compatible = "qcom,pcie-qcs404", .data = &ops_2_4_0 }, { .compatible = "qcom,pcie-sdm845", .data = &ops_2_7_0 }, + { .compatible = "qcom,pcie-sm8250", .data = &ops_2_7_0 }, { } }; diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index 928db510b86c..43f46a1b3db1 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -48,6 +48,17 @@ config PHY_QCOM_QMP Enable this to support the QMP PHY transceiver that is used with controllers such as PCIe, UFS, and USB on Qualcomm chips. +if PHY_QCOM_QMP +config PHY_QCOM_QMP_TYPEC + bool "Enable QCOM QMP PHY Type C Switch Support" + depends on PHY_QCOM_QMP=y && TYPEC=y || PHY_QCOM_QMP=m && TYPEC + help + Register a type C switch from the QMP PHY driver for type C + orientation support. This has dependencies with if the type C kernel + configuration is enabled or not. This support will not be present if + USB type C is disabled. +endif + config PHY_QCOM_QUSB2 tristate "Qualcomm QUSB2 PHY Driver" depends on OF && (ARCH_QCOM || COMPILE_TEST) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 6e6f992a9524..90a357024382 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -19,6 +19,7 @@ #include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/slab.h> +#include <linux/usb/typec_mux.h> #include <dt-bindings/phy/phy.h> @@ -66,6 +67,9 @@ /* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */ #define CLAMP_EN BIT(0) /* enables i/o clamp_n */ +#define SW_PORTSELECT_VAL BIT(0) +#define SW_PORTSELECT_MUX BIT(1) + #define PHY_INIT_COMPLETE_TIMEOUT 10000 #define POWER_DOWN_DELAY_US_MIN 10 #define POWER_DOWN_DELAY_US_MAX 11 @@ -217,6 +221,13 @@ static const unsigned int sdm845_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_PCS_READY_STATUS] = 0x160, }; +static const unsigned int sm8250_pcie_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = 0x00, + [QPHY_START_CTRL] = 0x44, + [QPHY_PCS_STATUS] = 0x14, + [QPHY_PCS_POWER_DOWN_CONTROL] = 0x40, +}; + static const unsigned int sm8150_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_START_CTRL] = QPHY_V4_PCS_UFS_PHY_START, [QPHY_PCS_READY_STATUS] = QPHY_V4_PCS_UFS_READY_STATUS, @@ -1742,6 +1753,234 @@ static const struct qmp_phy_init_tbl sm8250_usb3_uniphy_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21), }; +static const struct qmp_phy_init_tbl sm8250_qmp_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(0x0094, 0x08), + QMP_PHY_INIT_CFG(0x0154, 0x34), + QMP_PHY_INIT_CFG(0x016c, 0x08), + QMP_PHY_INIT_CFG(0x0058, 0x0f), + QMP_PHY_INIT_CFG(0x00a4, 0x42), + QMP_PHY_INIT_CFG(0x0110, 0x24), + QMP_PHY_INIT_CFG(0x011c, 0x03), + QMP_PHY_INIT_CFG(0x0118, 0xb4), + QMP_PHY_INIT_CFG(0x010c, 0x02), + QMP_PHY_INIT_CFG(0x01bc, 0x11), + QMP_PHY_INIT_CFG(0x00bc, 0x82), + QMP_PHY_INIT_CFG(0x00d4, 0x03), + QMP_PHY_INIT_CFG(0x00d0, 0x55), + QMP_PHY_INIT_CFG(0x00cc, 0x55), + QMP_PHY_INIT_CFG(0x00b0, 0x1a), + QMP_PHY_INIT_CFG(0x00ac, 0x0a), + QMP_PHY_INIT_CFG(0x00c4, 0x68), + QMP_PHY_INIT_CFG(0x00e0, 0x02), + QMP_PHY_INIT_CFG(0x00dc, 0xaa), + QMP_PHY_INIT_CFG(0x00d8, 0xab), + QMP_PHY_INIT_CFG(0x00b8, 0x34), + QMP_PHY_INIT_CFG(0x00b4, 0x14), + QMP_PHY_INIT_CFG(0x0158, 0x01), + QMP_PHY_INIT_CFG(0x0074, 0x06), + QMP_PHY_INIT_CFG(0x007c, 0x16), + QMP_PHY_INIT_CFG(0x0084, 0x36), + QMP_PHY_INIT_CFG(0x0078, 0x06), + QMP_PHY_INIT_CFG(0x0080, 0x16), + QMP_PHY_INIT_CFG(0x0088, 0x36), + QMP_PHY_INIT_CFG(0x01b0, 0x1e), + QMP_PHY_INIT_CFG(0x01ac, 0xca), + QMP_PHY_INIT_CFG(0x01b8, 0x18), + QMP_PHY_INIT_CFG(0x01b4, 0xa2), + QMP_PHY_INIT_CFG(0x0050, 0x07), + QMP_PHY_INIT_CFG(0x0010, 0x01), + QMP_PHY_INIT_CFG(0x001c, 0x31), + QMP_PHY_INIT_CFG(0x0020, 0x01), + QMP_PHY_INIT_CFG(0x0024, 0xde), + QMP_PHY_INIT_CFG(0x0028, 0x07), + QMP_PHY_INIT_CFG(0x0030, 0x4c), + QMP_PHY_INIT_CFG(0x0034, 0x06), + QMP_PHY_INIT_CFG(0x0048, 0x90), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(0x09c, 0x12), + QMP_PHY_INIT_CFG(0x084, 0x35), + QMP_PHY_INIT_CFG(0x03c, 0x11), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(0x008, 0x0c), + QMP_PHY_INIT_CFG(0x014, 0x03), + QMP_PHY_INIT_CFG(0x0dc, 0x1b), + QMP_PHY_INIT_CFG(0x0fc, 0x00), + QMP_PHY_INIT_CFG(0x0f8, 0xc0), + QMP_PHY_INIT_CFG(0x060, 0x30), + QMP_PHY_INIT_CFG(0x064, 0x00), + QMP_PHY_INIT_CFG(0x0d4, 0x04), + QMP_PHY_INIT_CFG(0x0d8, 0x07), + QMP_PHY_INIT_CFG(0x034, 0x7f), + QMP_PHY_INIT_CFG(0x044, 0x70), + QMP_PHY_INIT_CFG(0x0e8, 0x00), + QMP_PHY_INIT_CFG(0x0ec, 0x0e), + QMP_PHY_INIT_CFG(0x0f0, 0x4a), + QMP_PHY_INIT_CFG(0x0f4, 0x0f), + + QMP_PHY_INIT_CFG(0x11c, 0x03), + QMP_PHY_INIT_CFG(0x118, 0x1c), + QMP_PHY_INIT_CFG(0x124, 0x1e), + QMP_PHY_INIT_CFG(0x1b4, 0x04), + QMP_PHY_INIT_CFG(0x110, 0x17), + QMP_PHY_INIT_CFG(0x198, 0xd4), + QMP_PHY_INIT_CFG(0x19c, 0x54), + QMP_PHY_INIT_CFG(0x1a0, 0xdb), + QMP_PHY_INIT_CFG(0x1a4, 0x3b), + QMP_PHY_INIT_CFG(0x1a8, 0x31), + + QMP_PHY_INIT_CFG(0x184, 0x24), + + QMP_PHY_INIT_CFG(0x170, 0x3f), + QMP_PHY_INIT_CFG(0x174, 0x3f), + QMP_PHY_INIT_CFG(0x178, 0xff), + QMP_PHY_INIT_CFG(0x17c, 0x7f), + QMP_PHY_INIT_CFG(0x180, 0x14), + + QMP_PHY_INIT_CFG(0x1bc, 0x0c), + QMP_PHY_INIT_CFG(0x1b8, 0x30), + + QMP_PHY_INIT_CFG(0x188, 0xe4), + QMP_PHY_INIT_CFG(0x18c, 0xec), + QMP_PHY_INIT_CFG(0x190, 0x3b), + QMP_PHY_INIT_CFG(0x194, 0x36), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(0x0dc, 0x0d), + QMP_PHY_INIT_CFG(0x1a4, 0x01), + + QMP_PHY_INIT_CFG(0x188, 0x77), + QMP_PHY_INIT_CFG(0x198, 0x0b), + QMP_PHY_INIT_CFG(0x1ec, 0x12), +}; + +static const struct qmp_phy_init_tbl sm8250_qmp_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(0x090, 0x00), + QMP_PHY_INIT_CFG(0x040, 0x01), + QMP_PHY_INIT_CFG(0x048, 0x01), + QMP_PHY_INIT_CFG(0x050, 0x00), + QMP_PHY_INIT_CFG(0x0b4, 0x33), + QMP_PHY_INIT_CFG(0x0bc, 0x00), + QMP_PHY_INIT_CFG(0x0e0, 0x58), + QMP_PHY_INIT_CFG(0x0a4, 0x0f), + QMP_PHY_INIT_CFG(0x01c, 0xc1), +}; + + +static const struct qmp_phy_init_tbl sm8250_qhp_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(0x0010, 0x01), + QMP_PHY_INIT_CFG(0x001c, 0x31), + QMP_PHY_INIT_CFG(0x0020, 0x01), + QMP_PHY_INIT_CFG(0x0024, 0xde), + QMP_PHY_INIT_CFG(0x0028, 0x07), + QMP_PHY_INIT_CFG(0x0030, 0x4c), + QMP_PHY_INIT_CFG(0x0034, 0x06), + QMP_PHY_INIT_CFG(0x0048, 0x90), + QMP_PHY_INIT_CFG(0x0058, 0x0f), + QMP_PHY_INIT_CFG(0x0074, 0x06), + QMP_PHY_INIT_CFG(0x0078, 0x06), + QMP_PHY_INIT_CFG(0x007c, 0x16), + QMP_PHY_INIT_CFG(0x0080, 0x16), + QMP_PHY_INIT_CFG(0x0084, 0x36), + QMP_PHY_INIT_CFG(0x0088, 0x36), + QMP_PHY_INIT_CFG(0x0094, 0x08), + QMP_PHY_INIT_CFG(0x00a4, 0x42), + QMP_PHY_INIT_CFG(0x00ac, 0x0a), + QMP_PHY_INIT_CFG(0x00b0, 0x1a), + QMP_PHY_INIT_CFG(0x00b4, 0x14), + QMP_PHY_INIT_CFG(0x00b8, 0x34), + QMP_PHY_INIT_CFG(0x00bc, 0x82), + QMP_PHY_INIT_CFG(0x00c4, 0x68), + QMP_PHY_INIT_CFG(0x00cc, 0x55), + QMP_PHY_INIT_CFG(0x00d0, 0x55), + QMP_PHY_INIT_CFG(0x00d4, 0x03), + QMP_PHY_INIT_CFG(0x00d8, 0xab), + QMP_PHY_INIT_CFG(0x00dc, 0xaa), + QMP_PHY_INIT_CFG(0x00e0, 0x02), + QMP_PHY_INIT_CFG(0x010c, 0x02), + QMP_PHY_INIT_CFG(0x0110, 0x24), + QMP_PHY_INIT_CFG(0x0118, 0xb4), + QMP_PHY_INIT_CFG(0x011c, 0x03), + QMP_PHY_INIT_CFG(0x0154, 0x34), + QMP_PHY_INIT_CFG(0x0158, 0x01), + QMP_PHY_INIT_CFG(0x016c, 0x08), + QMP_PHY_INIT_CFG(0x01ac, 0xca), + QMP_PHY_INIT_CFG(0x01b0, 0x1e), + QMP_PHY_INIT_CFG(0x01b4, 0xa2), + QMP_PHY_INIT_CFG(0x01b8, 0x18), + QMP_PHY_INIT_CFG(0x01bc, 0x11), +}; + +static const struct qmp_phy_init_tbl sm8250_qhp_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(0x03c, 0x11), + QMP_PHY_INIT_CFG(0x084, 0x75), + QMP_PHY_INIT_CFG(0x09c, 0x12), + QMP_PHY_INIT_CFG(0x104, 0x20), +}; + +static const struct qmp_phy_init_tbl sm8250_qhp_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(0x008, 0x0c), + QMP_PHY_INIT_CFG(0x014, 0x03), + QMP_PHY_INIT_CFG(0x034, 0x7f), + QMP_PHY_INIT_CFG(0x044, 0x70), + QMP_PHY_INIT_CFG(0x060, 0x30), + QMP_PHY_INIT_CFG(0x0d4, 0x04), + QMP_PHY_INIT_CFG(0x0d8, 0x07), + QMP_PHY_INIT_CFG(0x0dc, 0x1b), + QMP_PHY_INIT_CFG(0x0e8, 0x04), + QMP_PHY_INIT_CFG(0x0ec, 0x0e), + QMP_PHY_INIT_CFG(0x0f0, 0x4a), + QMP_PHY_INIT_CFG(0x0f4, 0x0f), + QMP_PHY_INIT_CFG(0x0f8, 0xc0), + QMP_PHY_INIT_CFG(0x0fc, 0x00), + QMP_PHY_INIT_CFG(0x110, 0x17), + + QMP_PHY_INIT_CFG(0x118, 0x1c), + QMP_PHY_INIT_CFG(0x11c, 0x03), + QMP_PHY_INIT_CFG(0x124, 0x1e), + QMP_PHY_INIT_CFG(0x170, 0xbf), + QMP_PHY_INIT_CFG(0x174, 0x3f), + QMP_PHY_INIT_CFG(0x178, 0xff), + QMP_PHY_INIT_CFG(0x17c, 0x7f), + QMP_PHY_INIT_CFG(0x180, 0x15), + QMP_PHY_INIT_CFG(0x184, 0x24), + QMP_PHY_INIT_CFG(0x188, 0xe4), + QMP_PHY_INIT_CFG(0x18c, 0xec), + QMP_PHY_INIT_CFG(0x190, 0x3b), + QMP_PHY_INIT_CFG(0x194, 0x36), + QMP_PHY_INIT_CFG(0x198, 0xd4), + QMP_PHY_INIT_CFG(0x19c, 0x54), + QMP_PHY_INIT_CFG(0x1a0, 0xdb), + QMP_PHY_INIT_CFG(0x1a4, 0x3b), + QMP_PHY_INIT_CFG(0x1a8, 0x31), + QMP_PHY_INIT_CFG(0x1bc, 0x0c), + QMP_PHY_INIT_CFG(0x1b8, 0x38), +}; + +static const struct qmp_phy_init_tbl sm8250_qhp_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(0x0dc, 0x05), + QMP_PHY_INIT_CFG(0x188, 0x77), + QMP_PHY_INIT_CFG(0x198, 0x0b), + QMP_PHY_INIT_CFG(0x1a4, 0x01), + QMP_PHY_INIT_CFG(0x1e0, 0x0f), +}; + +static const struct qmp_phy_init_tbl sm8250_qhp_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(0x00c, 0x0d), + QMP_PHY_INIT_CFG(0x014, 0x07), + QMP_PHY_INIT_CFG(0x01c, 0xc1), + QMP_PHY_INIT_CFG(0x040, 0x01), + QMP_PHY_INIT_CFG(0x048, 0x01), + QMP_PHY_INIT_CFG(0x090, 0x00), + QMP_PHY_INIT_CFG(0x0b4, 0x33), + QMP_PHY_INIT_CFG(0x0bc, 0x00), + QMP_PHY_INIT_CFG(0x0e0, 0x58), +}; + /* struct qmp_phy_cfg - per-PHY initialization config */ struct qmp_phy_cfg { /* phy-type - PCIE/UFS/USB */ @@ -1844,6 +2083,8 @@ struct qmp_phy { * @phy_initialized: indicate if PHY has been initialized * @mode: current PHY mode * @ufs_reset: optional UFS PHY reset handle + * @sw: typec switch for receiving orientation changes + * @orientation: carries current CC orientation */ struct qcom_qmp { struct device *dev; @@ -1863,6 +2104,8 @@ struct qcom_qmp { enum phy_mode mode; struct reset_control *ufs_reset; + struct typec_switch *sw; + enum typec_orientation orientation; }; static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) @@ -2141,6 +2384,67 @@ static const struct qmp_phy_cfg sdm845_qhp_pciephy_cfg = { .pwrdn_delay_max = 1005, /* us */ }; +static const struct qmp_phy_cfg sm8250_qmp_pciephy_cfg = { + .type = PHY_TYPE_PCIE, + .nlanes = 1, + + .serdes_tbl = sm8250_qmp_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_serdes_tbl), + .tx_tbl = sm8250_qmp_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_tx_tbl), + .rx_tbl = sm8250_qmp_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_rx_tbl), + .pcs_tbl = sm8250_qmp_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_pcs_tbl), + .pcs_misc_tbl = sm8250_qmp_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num = ARRAY_SIZE(sm8250_qmp_pcie_pcs_misc_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8250_pcie_regs_layout, + + .start_ctrl = PCS_START | SERDES_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + +static const struct qmp_phy_cfg sm8250_qhp_pciephy_cfg = { + .type = PHY_TYPE_PCIE, + .nlanes = 2, + + .serdes_tbl = sm8250_qhp_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8250_qhp_pcie_serdes_tbl), + .tx_tbl = sm8250_qhp_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8250_qhp_pcie_tx_tbl), + .rx_tbl = sm8250_qhp_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8250_qhp_pcie_rx_tbl), + .pcs_tbl = sm8250_qhp_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8250_qhp_pcie_pcs_tbl), + .pcs_misc_tbl = sm8250_qhp_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num = ARRAY_SIZE(sm8250_qhp_pcie_pcs_misc_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8250_pcie_regs_layout, + + .start_ctrl = PCS_START | SERDES_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + + .is_dual_lane_phy = true, + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { .type = PHY_TYPE_USB3, .nlanes = 1, @@ -2487,6 +2791,7 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy) void __iomem *pcs = qphy->pcs; void __iomem *dp_com = qmp->dp_com; int ret, i; + unsigned int val; mutex_lock(&qmp->phy_mutex); if (qmp->init_count++) { @@ -2536,6 +2841,13 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy) qphy_setbits(dp_com, QPHY_V3_DP_COM_PHY_MODE_CTRL, USB3_MODE | DP_MODE); + if (cfg->is_dual_lane_phy) { + val = SW_PORTSELECT_MUX; + if (qmp->orientation == TYPEC_ORIENTATION_REVERSE) + val |= SW_PORTSELECT_VAL; + qphy_setbits(dp_com, QPHY_V3_DP_COM_TYPEC_CTRL, val); + } + /* bring both QMP USB and QMP DP PHYs PCS block out of reset */ qphy_clrbits(dp_com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | @@ -2561,7 +2873,7 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy) if (cfg->has_phy_com_ctrl) { void __iomem *status; - unsigned int mask, val; + unsigned int mask; qphy_clrbits(serdes, cfg->regs[QPHY_COM_SW_RESET], SW_RESET); qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL], @@ -3234,6 +3546,12 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,sm8250-qmp-usb3-uni-phy", .data = &sm8250_usb3_uniphy_cfg, + }, { + .compatible = "qcom,sm8250-qmp-pcie-phy", + .data = &sm8250_qmp_pciephy_cfg, + }, { + .compatible = "qcom,sm8250-qhp-pcie-phy", + .data = &sm8250_qhp_pciephy_cfg, }, { }, }; @@ -3244,6 +3562,47 @@ static const struct dev_pm_ops qcom_qmp_phy_pm_ops = { qcom_qmp_phy_runtime_resume, NULL) }; +#if IS_ENABLED(CONFIG_PHY_QCOM_QMP_TYPEC) +static int qcom_qmp_phy_typec_switch_set(struct typec_switch *sw, + enum typec_orientation orientation) +{ + struct qcom_qmp *qmp = typec_switch_get_drvdata(sw); + struct qmp_phy *qphy = qmp->phys[0]; + + qmp->orientation = orientation; + if (qmp->phy_initialized) { + qcom_qmp_phy_disable(qphy->phy); + qcom_qmp_phy_enable(qphy->phy); + } + + return 0; +} + +static int qcom_qmp_phy_typec_switch_register(struct qcom_qmp *qmp) +{ + struct typec_switch_desc sw_desc; + struct device *dev = qmp->dev; + + if (qmp->cfg->is_dual_lane_phy) { + sw_desc.drvdata = qmp; + sw_desc.fwnode = dev->fwnode; + sw_desc.set = qcom_qmp_phy_typec_switch_set; + qmp->sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(qmp->sw)) { + dev_err(dev, "Error registering typec switch: %ld\n", + PTR_ERR(qmp->sw)); + } + } + + return 0; +} +#else +static int qcom_qmp_phy_typec_switch_register(struct qcom_qmp *qmp) +{ + return 0; +} +#endif + static int qcom_qmp_phy_probe(struct platform_device *pdev) { struct qcom_qmp *qmp; @@ -3252,7 +3611,7 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev) struct device_node *child; struct phy_provider *phy_provider; void __iomem *base; - int num, id; + int num = 0, id; int ret; qmp = devm_kzalloc(dev, sizeof(*qmp), GFP_KERNEL); @@ -3304,7 +3663,11 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev) return ret; } - num = of_get_available_child_count(dev->of_node); + qcom_qmp_phy_typec_switch_register(qmp); + for_each_available_child_of_node(dev->of_node, child) { + if (!strncmp("lanes", child->name, 5)) + num++; + } /* do we have a rogue child node ? */ if (num > qmp->cfg->nlanes) return -EINVAL; @@ -3324,6 +3687,9 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev) for_each_available_child_of_node(dev->of_node, child) { /* Create per-lane phy */ + if (strncmp("lanes", child->name, 5)) + continue; + ret = qcom_qmp_phy_create(dev, child, id); if (ret) { dev_err(dev, "failed to create lane%d phy, %d\n", diff --git a/drivers/pinctrl/qcom/pinctrl-sm8250.c b/drivers/pinctrl/qcom/pinctrl-sm8250.c index a660f1274b66..4f7dae61b089 100644 --- a/drivers/pinctrl/qcom/pinctrl-sm8250.c +++ b/drivers/pinctrl/qcom/pinctrl-sm8250.c @@ -1308,7 +1308,7 @@ static const struct msm_pingroup sm8250_groups[] = { [178] = PINGROUP(178, WEST, _, _, _, _, _, _, _, _, _), [179] = PINGROUP(179, WEST, _, _, _, _, _, _, _, _, _), [180] = UFS_RESET(ufs_reset, 0xb8000), - [181] = SDC_PINGROUP(sdc2_clk, 0x7000, 14, 6), + [181] = SDC_PINGROUP(sdc2_clk, 0xb7000, 14, 6), [182] = SDC_PINGROUP(sdc2_cmd, 0xb7000, 11, 3), [183] = SDC_PINGROUP(sdc2_data, 0xb7000, 9, 0), }; @@ -1320,7 +1320,7 @@ static const struct msm_pinctrl_soc_data sm8250_pinctrl = { .nfunctions = ARRAY_SIZE(sm8250_functions), .groups = sm8250_groups, .ngroups = ARRAY_SIZE(sm8250_groups), - .ngpios = 181, + .ngpios = 180, .tiles = sm8250_tiles, .ntiles = ARRAY_SIZE(sm8250_tiles), }; diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 08dcc614efa7..b62389520387 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -867,7 +867,7 @@ static const struct rpmh_vreg_init_data pm8150l_vreg_data[] = { static const struct rpmh_vreg_init_data pm8009_vreg_data[] = { RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps515, "vdd-s2"), + RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps510, "vdd-s2"), RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2"), RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), diff --git a/drivers/regulator/qcom_usb_vbus-regulator.c b/drivers/regulator/qcom_usb_vbus-regulator.c index 8ba947f3585f..457788b50572 100644 --- a/drivers/regulator/qcom_usb_vbus-regulator.c +++ b/drivers/regulator/qcom_usb_vbus-regulator.c @@ -63,6 +63,7 @@ static int qcom_usb_vbus_regulator_probe(struct platform_device *pdev) qcom_usb_vbus_rdesc.enable_mask = OTG_EN; config.dev = dev; config.init_data = init_data; + config.of_node = dev->of_node; config.regmap = regmap; rdev = devm_regulator_register(dev, &qcom_usb_vbus_rdesc, &config); diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c index e2573f79a137..93523f9aac6a 100644 --- a/drivers/remoteproc/qcom_wcnss.c +++ b/drivers/remoteproc/qcom_wcnss.c @@ -85,6 +85,7 @@ struct qcom_wcnss { struct completion start_done; struct completion stop_done; + struct completion iris_assigned; phys_addr_t mem_phys; phys_addr_t mem_reloc; @@ -139,6 +140,7 @@ void qcom_wcnss_assign_iris(struct qcom_wcnss *wcnss, wcnss->iris = iris; wcnss->use_48mhz_xo = use_48mhz_xo; + complete(&wcnss->iris_assigned); mutex_unlock(&wcnss->iris_lock); } @@ -221,6 +223,10 @@ static int wcnss_start(struct rproc *rproc) struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; int ret; + /* Grant some time for iris registration */ + wait_for_completion_timeout(&wcnss->iris_assigned, + msecs_to_jiffies(5000)); + mutex_lock(&wcnss->iris_lock); if (!wcnss->iris) { dev_err(wcnss->dev, "no iris registered\n"); @@ -465,6 +471,7 @@ static int wcnss_alloc_memory_region(struct qcom_wcnss *wcnss) static int wcnss_probe(struct platform_device *pdev) { + const char *fw_name = WCNSS_FIRMWARE_NAME; const struct wcnss_data *data; struct qcom_wcnss *wcnss; struct resource *res; @@ -482,8 +489,13 @@ static int wcnss_probe(struct platform_device *pdev) return -ENXIO; } + ret = of_property_read_string(pdev->dev.of_node, "firmware-name", + &fw_name); + if (ret < 0 && ret != -EINVAL) + return ret; + rproc = rproc_alloc(&pdev->dev, pdev->name, &wcnss_ops, - WCNSS_FIRMWARE_NAME, sizeof(*wcnss)); + fw_name, sizeof(*wcnss)); if (!rproc) { dev_err(&pdev->dev, "unable to allocate remoteproc\n"); return -ENOMEM; @@ -497,6 +509,7 @@ static int wcnss_probe(struct platform_device *pdev) init_completion(&wcnss->start_done); init_completion(&wcnss->stop_done); + init_completion(&wcnss->iris_assigned); mutex_init(&wcnss->iris_lock); diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 429b5a60a1ba..8ec472c8643b 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -45,6 +45,8 @@ #define LLCC_TRP_ATTR0_CFGn(n) (0x21000 + SZ_8 * n) #define LLCC_TRP_ATTR1_CFGn(n) (0x21004 + SZ_8 * n) +#define LLCC_TRP_WRSC_EN 0x21f20 + #define BANK_OFFSET_STRIDE 0x80000 /** @@ -70,6 +72,7 @@ * then the ways assigned to this client are not flushed on power * collapse. * @activate_on_init: Activate the slice immediately after it is programmed + * @write_scid_en: Bit enables write cache support for a given scid. */ struct llcc_slice_config { u32 usecase_id; @@ -84,6 +87,7 @@ struct llcc_slice_config { bool dis_cap_alloc; bool retain_on_pc; bool activate_on_init; + bool write_scid_en; }; struct qcom_llcc_config { @@ -119,6 +123,25 @@ static const struct llcc_slice_config sdm845_data[] = { { LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0 }, }; +static const struct llcc_slice_config sm8250_data[] = { + { LLCC_CPUSS, 1, 3072, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 1, 0 }, + { LLCC_VIDSC0, 2, 512, 3, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_AUDIO, 6, 1024, 1, 0, 0xfff, 0x0, 0, 0, 0, 0, 0, 0 }, + { LLCC_CMPT, 10, 1024, 1, 0, 0xfff, 0x0, 0, 0, 0, 0, 0, 0 }, + { LLCC_GPUHTW, 11, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_GPU, 12, 1024, 1, 0, 0xfff, 0x0, 0, 0, 0, 1, 0, 1 }, + { LLCC_MMUHWT, 13, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_CMPTDMA, 15, 1024, 1, 0, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_DISP, 16, 3072, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_AUDHW, 22, 1024, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_NPU, 23, 3072, 1, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_WLNHW, 24, 1024, 1, 0, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_CVP, 28, 256, 3, 1, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, + { LLCC_APTCM, 30, 128, 3, 0, 0x0, 0x3, 1, 0, 0, 1, 0, 0 }, + { LLCC_WRTCH, 31, 256, 1, 1, 0xfff, 0x0, 0, 0, 0, 0, 1, 0 }, + { LLCC_CVPFW, 17, 512, 1, 0, 0xfff, 0x0, 0, 0, 0, 1, 0, 0 }, +}; + static const struct qcom_llcc_config sc7180_cfg = { .sct_data = sc7180_data, .size = ARRAY_SIZE(sc7180_data), @@ -129,6 +152,11 @@ static const struct qcom_llcc_config sdm845_cfg = { .size = ARRAY_SIZE(sdm845_data), }; +static const struct qcom_llcc_config sm8250_cfg = { + .sct_data = sm8250_data, + .size = ARRAY_SIZE(sm8250_data), +}; + static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER; /** @@ -330,6 +358,9 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev) int ret = 0; const struct llcc_slice_config *llcc_table; struct llcc_slice_desc desc; + int is_sm8250 = of_device_is_compatible(pdev->dev.of_node, + "qcom,sm8250-llcc"); + u32 wren = 0; sz = drv_data->cfg_size; llcc_table = drv_data->cfg; @@ -369,6 +400,16 @@ static int qcom_llcc_cfg_program(struct platform_device *pdev) attr0_val); if (ret) return ret; + + if (is_sm8250) { + wren |= llcc_table[i].write_scid_en << + llcc_table[i].slice_id; + ret = regmap_write(drv_data->bcast_regmap, + LLCC_TRP_WRSC_EN, wren); + if (ret) + return ret; + } + if (llcc_table[i].activate_on_init) { desc.slice_id = llcc_table[i].slice_id; ret = llcc_slice_activate(&desc); @@ -499,6 +540,7 @@ err: static const struct of_device_id qcom_llcc_of_match[] = { { .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfg }, { .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfg }, + { .compatible = "qcom,sm8250-llcc", .data = &sm8250_cfg }, { } }; diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index d0e4f520cff8..b0e375d9d1cc 100644 --- a/drivers/soc/qcom/qcom-geni-se.c +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -107,7 +107,6 @@ static struct geni_wrapper *earlycon_wrapper; #define GENI_OUTPUT_CTRL 0x24 #define GENI_CGC_CTRL 0x28 #define GENI_CLK_CTRL_RO 0x60 -#define GENI_IF_DISABLE_RO 0x64 #define GENI_FW_S_REVISION_RO 0x6c #define SE_GENI_BYTE_GRAN 0x254 #define SE_GENI_TX_PACKING_CFG0 0x260 @@ -298,6 +297,39 @@ static void geni_se_select_dma_mode(struct geni_se *se) writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN); } +static int geni_se_select_gpi_mode(struct geni_se *se) +{ + unsigned int geni_dma_mode = 0; + unsigned int gpi_event_en = 0; + unsigned int common_geni_m_irq_en = 0; + unsigned int common_geni_s_irq_en = 0; + + common_geni_m_irq_en = readl_relaxed(se->base + SE_GENI_M_IRQ_EN); + common_geni_s_irq_en = readl_relaxed(se->base + SE_GENI_S_IRQ_EN); + common_geni_m_irq_en &= + ~(M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN | + M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN); + common_geni_s_irq_en &= ~S_CMD_DONE_EN; + geni_dma_mode = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN); + gpi_event_en = readl_relaxed(se->base + SE_GSI_EVENT_EN); + + geni_dma_mode |= GENI_DMA_MODE_EN; + gpi_event_en |= (DMA_RX_EVENT_EN | DMA_TX_EVENT_EN | + GENI_M_EVENT_EN | GENI_S_EVENT_EN); + + writel_relaxed(0, se->base + SE_IRQ_EN); + writel_relaxed(common_geni_s_irq_en, se->base + SE_GENI_S_IRQ_EN); + writel_relaxed(common_geni_m_irq_en, se->base + SE_GENI_M_IRQ_EN); + writel_relaxed(0xFFFFFFFF, se->base + SE_GENI_M_IRQ_CLEAR); + writel_relaxed(0xFFFFFFFF, se->base + SE_GENI_S_IRQ_CLEAR); + writel_relaxed(0xFFFFFFFF, se->base + SE_DMA_TX_IRQ_CLR); + writel_relaxed(0xFFFFFFFF, se->base + SE_DMA_RX_IRQ_CLR); + writel_relaxed(geni_dma_mode, se->base + SE_GENI_DMA_MODE_EN); + writel_relaxed(gpi_event_en, se->base + SE_GSI_EVENT_EN); + + return 0; +} + /** * geni_se_select_mode() - Select the serial engine transfer mode * @se: Pointer to the concerned serial engine. @@ -305,7 +337,8 @@ static void geni_se_select_dma_mode(struct geni_se *se) */ void geni_se_select_mode(struct geni_se *se, enum geni_se_xfer_mode mode) { - WARN_ON(mode != GENI_SE_FIFO && mode != GENI_SE_DMA); + WARN_ON(mode != GENI_SE_FIFO && mode != GENI_SE_DMA && + mode != GENI_GPI_DMA); switch (mode) { case GENI_SE_FIFO: @@ -314,6 +347,9 @@ void geni_se_select_mode(struct geni_se *se, enum geni_se_xfer_mode mode) case GENI_SE_DMA: geni_se_select_dma_mode(se); break; + case GENI_GPI_DMA: + geni_se_select_gpi_mode(se); + break; case GENI_SE_INVALID: default: break; diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 80cea5cd3612..28086dfcd7a6 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -2,6 +2,8 @@ // Copyright (c) 2017-2018, The Linux foundation. All rights reserved. #include <linux/clk.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/log2.h> @@ -63,6 +65,35 @@ #define TIMESTAMP_AFTER BIT(3) #define POST_CMD_DELAY BIT(4) +#define GSI_LOOPBACK_EN (BIT(0)) +#define GSI_CS_TOGGLE (BIT(3)) +#define GSI_CPHA (BIT(4)) +#define GSI_CPOL (BIT(5)) + +#define MAX_TX_SG (3) +#define NUM_SPI_XFER (8) +#define SPI_XFER_TIMEOUT_MS (250) + +struct gsi_desc_cb { + struct spi_geni_master *mas; + struct spi_transfer *xfer; +}; + +struct spi_geni_gsi { + dma_cookie_t tx_cookie; + dma_cookie_t rx_cookie; + struct dma_async_tx_descriptor *tx_desc; + struct dma_async_tx_descriptor *rx_desc; + struct gsi_desc_cb desc_cb; +}; + +enum spi_m_cmd_opcode { + CMD_NONE, + CMD_XFER, + CMD_CS, + CMD_CANCEL, +}; + struct spi_geni_master { struct geni_se se; struct device *dev; @@ -72,17 +103,28 @@ struct spi_geni_master { u32 last_mode; unsigned long cur_speed_hz; unsigned long cur_sclk_hz; - unsigned int cur_bits_per_word; + int cur_bits_per_word; unsigned int tx_rem_bytes; unsigned int rx_rem_bytes; const struct spi_transfer *cur_xfer; struct completion cs_done; struct completion cancel_done; struct completion abort_done; + struct completion xfer_done; unsigned int oversampling; spinlock_t lock; int irq; bool cs_flag; + struct spi_geni_gsi *gsi; + struct dma_chan *tx; + struct dma_chan *rx; + struct completion tx_cb; + struct completion rx_cb; + bool qn_err; + int cur_xfer_mode; + int num_tx_eot; + int num_rx_eot; + int num_xfers; }; static int get_spi_clk_cfg(unsigned int speed_hz, @@ -274,30 +316,271 @@ static int setup_fifo_params(struct spi_device *spi_slv, return geni_spi_set_clock_and_bw(mas, spi_slv->max_speed_hz); } +static int get_xfer_mode(struct spi_master *spi) +{ + struct spi_geni_master *mas = spi_master_get_devdata(spi); + struct geni_se *se = &mas->se; + int mode = GENI_SE_FIFO; + int fifo_disable; + bool dma_chan_valid; + + fifo_disable = readl(se->base + GENI_IF_DISABLE_RO) & FIFO_IF_DISABLE; + dma_chan_valid = !(IS_ERR_OR_NULL(mas->tx) || IS_ERR_OR_NULL(mas->rx)); + + /* + * If FIFO Interface is disabled and there are no DMA channels then we + * can't do this transfer. + * If FIFO interface is disabled, we can do GSI only, + * else pick FIFO mode. + */ + if (fifo_disable && !dma_chan_valid) { + dev_err(mas->dev, "Fifo and dma mode disabled!! can't xfer\n"); + mode = -EINVAL; + } + else if (fifo_disable) + mode = GENI_GPI_DMA; + else + mode = GENI_SE_FIFO; + return mode; +} + +static void +spi_gsi_callback_result(void *cb, const struct dmaengine_result *result, bool tx) +{ + struct gsi_desc_cb *gsi = cb; + + if (result->result != DMA_TRANS_NOERROR) { + dev_err(gsi->mas->dev, "%s: DMA %s txn failed\n", __func__, tx ? "tx" : "rx"); + return; + } + + if (!result->residue) { + dev_dbg(gsi->mas->dev, "%s\n", __func__); + if (tx) + complete(&gsi->mas->tx_cb); + else + complete(&gsi->mas->rx_cb); + } else { + dev_err(gsi->mas->dev, "DMA xfer has pending: %d\n", result->residue); + } +} + +static void +spi_gsi_rx_callback_result(void *cb, const struct dmaengine_result *result) +{ + spi_gsi_callback_result(cb, result, false); +} + +static void +spi_gsi_tx_callback_result(void *cb, const struct dmaengine_result *result) +{ + spi_gsi_callback_result(cb, result, true); +} + +static int setup_gsi_xfer(struct spi_transfer *xfer, + struct spi_geni_master *mas, + struct spi_device *spi_slv, + struct spi_master *spi) +{ + int ret = 0; + unsigned long flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK; + struct spi_geni_gsi *gsi; + struct dma_slave_config config; + struct dmaengine_peripheral_config peripheral; + + memset(&config, 0, sizeof(config)); + memset(&peripheral, 0, sizeof(peripheral)); + config.peripheral = &peripheral; + peripheral.peripheral = DMAENGINE_PERIPHERAL_SPI; + + if ((xfer->bits_per_word != mas->cur_bits_per_word) || + (xfer->speed_hz != mas->cur_speed_hz)) { + mas->cur_bits_per_word = xfer->bits_per_word; + mas->cur_speed_hz = xfer->speed_hz; + peripheral.set_config = true; + } + + if (!(mas->cur_bits_per_word % MIN_WORD_LEN)) { + peripheral.rx_len = ((xfer->len << 3) / mas->cur_bits_per_word); + } else { + int bytes_per_word = (mas->cur_bits_per_word / BITS_PER_BYTE) + 1; + + peripheral.rx_len = (xfer->len / bytes_per_word); + } + if (xfer->tx_buf && xfer->rx_buf) { + peripheral.spi.cmd = SPI_DUPLEX; + } else if (xfer->tx_buf) { + peripheral.spi.cmd = SPI_TX; + peripheral.rx_len = 0; + } else if (xfer->rx_buf) { + peripheral.spi.cmd = SPI_RX; + } + + peripheral.spi.cs = spi_slv->chip_select; + + if (spi_slv->mode & SPI_LOOP) + peripheral.spi.loopback_en = true; + if (spi_slv->mode & SPI_CPOL) + peripheral.spi.clock_pol = true; + if (spi_slv->mode & SPI_CPHA) + peripheral.spi.data_pol = true; + peripheral.spi.pack_en = true; + peripheral.spi.word_len = xfer->bits_per_word - MIN_WORD_LEN; + ret = get_spi_clk_cfg(mas->cur_speed_hz, mas, + &peripheral.spi.clk_src, &peripheral.spi.clk_div); + if (ret) { + dev_err(mas->dev, "%s:Err setting clks:%d\n", __func__, ret); + return ret; + } + + if (!xfer->cs_change) { + if (!list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers)) + peripheral.spi.fragmentation = FRAGMENTATION; + } + + gsi = &mas->gsi[mas->num_xfers]; + gsi->desc_cb.mas = mas; + gsi->desc_cb.xfer = xfer; + if (peripheral.spi.cmd & SPI_RX) { + dmaengine_slave_config(mas->rx, &config); + gsi->rx_desc = dmaengine_prep_slave_single(mas->rx, xfer->rx_dma, xfer->len, DMA_DEV_TO_MEM, flags); + if (IS_ERR_OR_NULL(gsi->rx_desc)) { + dev_err(mas->dev, "Err setting up rx desc\n"); + return -EIO; + } + gsi->rx_desc->callback_result = spi_gsi_rx_callback_result; + gsi->rx_desc->callback_param = &gsi->desc_cb; + mas->num_rx_eot++; + } + + if (peripheral.spi.cmd & SPI_TX_ONLY) { + mas->num_tx_eot++; + } + dmaengine_slave_config(mas->tx, &config); + gsi->tx_desc = dmaengine_prep_slave_single(mas->tx, xfer->tx_dma, xfer->len, DMA_MEM_TO_DEV, flags); + + if (IS_ERR_OR_NULL(gsi->tx_desc)) { + dev_err(mas->dev, "Err setting up tx desc\n"); + return -EIO; + } + gsi->tx_desc->callback_result = spi_gsi_tx_callback_result; + gsi->tx_desc->callback_param = &gsi->desc_cb; + if (peripheral.spi.cmd & SPI_RX) + gsi->rx_cookie = dmaengine_submit(gsi->rx_desc); + gsi->tx_cookie = dmaengine_submit(gsi->tx_desc); + if (peripheral.spi.cmd & SPI_RX) + dma_async_issue_pending(mas->rx); + dma_async_issue_pending(mas->tx); + mas->num_xfers++; + return ret; +} + +static int spi_geni_map_buf(struct spi_geni_master *mas, + struct spi_message *msg) +{ + struct spi_transfer *xfer; + struct device *gsi_dev = mas->dev->parent; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (xfer->rx_buf) { + xfer->rx_dma = dma_map_single(gsi_dev, xfer->rx_buf, + xfer->len, DMA_FROM_DEVICE); + if (dma_mapping_error(mas->dev, xfer->rx_dma)) { + dev_err(mas->dev, "Err mapping buf\n"); + return -ENOMEM; + } + } + + if (xfer->tx_buf) { + xfer->tx_dma = dma_map_single(gsi_dev, + (void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE); + if (dma_mapping_error(gsi_dev, xfer->tx_dma)) { + dev_err(mas->dev, "Err mapping buf\n"); + dma_unmap_single(gsi_dev, xfer->rx_dma, + xfer->len, DMA_FROM_DEVICE); + return -ENOMEM; + } + } + }; + + return 0; +} + +static void spi_geni_unmap_buf(struct spi_geni_master *mas, + struct spi_message *msg) +{ + struct spi_transfer *xfer; + struct device *gsi_dev = mas->dev; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (xfer->rx_buf) + dma_unmap_single(gsi_dev, xfer->rx_dma, + xfer->len, DMA_FROM_DEVICE); + if (xfer->tx_buf) + dma_unmap_single(gsi_dev, xfer->tx_dma, + xfer->len, DMA_TO_DEVICE); + }; +} + static int spi_geni_prepare_message(struct spi_master *spi, struct spi_message *spi_msg) { int ret; struct spi_geni_master *mas = spi_master_get_devdata(spi); + struct geni_se *se = &mas->se; + + mas->cur_xfer_mode = get_xfer_mode(spi); + + if (mas->cur_xfer_mode == GENI_SE_FIFO) { + geni_se_select_mode(se, GENI_SE_FIFO); + reinit_completion(&mas->xfer_done); + ret = setup_fifo_params(spi_msg->spi, spi); + if (ret) + dev_err(mas->dev, "Couldn't select mode %d\n", ret); + } else if (mas->cur_xfer_mode == GENI_GPI_DMA) { + mas->num_tx_eot = 0; + mas->num_rx_eot = 0; + mas->num_xfers = 0; + reinit_completion(&mas->tx_cb); + reinit_completion(&mas->rx_cb); + memset(mas->gsi, 0, + (sizeof(struct spi_geni_gsi) * NUM_SPI_XFER)); + geni_se_select_mode(se, GENI_GPI_DMA); + ret = spi_geni_map_buf(mas, spi_msg); + } else { + dev_err(mas->dev, "%s: Couldn't select mode %d", __func__, + mas->cur_xfer_mode); + ret = -EINVAL; + } - ret = setup_fifo_params(spi_msg->spi, spi); - if (ret) - dev_err(mas->dev, "Couldn't select mode %d\n", ret); return ret; } +static int spi_geni_unprepare_message(struct spi_master *spi_mas, + struct spi_message *spi_msg) +{ + struct spi_geni_master *mas = spi_master_get_devdata(spi_mas); + + mas->cur_speed_hz = 0; + mas->cur_bits_per_word = 0; + if (mas->cur_xfer_mode == GENI_GPI_DMA) + spi_geni_unmap_buf(mas, spi_msg); + return 0; +} + static int spi_geni_init(struct spi_geni_master *mas) { struct geni_se *se = &mas->se; unsigned int proto, major, minor, ver; + int ret = 0; pm_runtime_get_sync(mas->dev); proto = geni_se_read_proto(se); if (proto != GENI_SE_SPI) { dev_err(mas->dev, "Invalid proto %d\n", proto); - pm_runtime_put(mas->dev); - return -ENXIO; + ret = -ENXIO; + goto out_pm; } mas->tx_fifo_depth = geni_se_get_tx_fifo_depth(se); @@ -322,8 +605,37 @@ static int spi_geni_init(struct spi_geni_master *mas) geni_se_select_mode(se, GENI_SE_FIFO); + mas->tx = dma_request_slave_channel(mas->dev, "tx"); + if (IS_ERR_OR_NULL(mas->tx)) { + dev_err(mas->dev, "Failed to get tx DMA ch %ld", + PTR_ERR(mas->tx)); + ret = PTR_ERR(mas->tx); + goto out_pm; + } else { + mas->rx = dma_request_slave_channel(mas->dev, "rx"); + if (IS_ERR_OR_NULL(mas->rx)) { + dev_err(mas->dev, "Failed to get rx DMA ch %ld", + PTR_ERR(mas->rx)); + dma_release_channel(mas->tx); + ret = PTR_ERR(mas->rx); + goto out_pm; + } + mas->gsi = devm_kzalloc(mas->dev, + (sizeof(struct spi_geni_gsi) * NUM_SPI_XFER), + GFP_KERNEL); + if (IS_ERR_OR_NULL(mas->gsi)) { + dev_err(mas->dev, "Failed to get GSI mem\n"); + dma_release_channel(mas->tx); + dma_release_channel(mas->rx); + mas->tx = NULL; + mas->rx = NULL; + goto out_pm; + } + } + +out_pm: pm_runtime_put(mas->dev); - return 0; + return ret; } static void setup_fifo_xfer(struct spi_transfer *xfer, @@ -331,6 +643,7 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, u16 mode, struct spi_master *spi) { u32 m_cmd = 0; + u32 m_param = 0; u32 spi_tx_cfg, len; struct geni_se *se = &mas->se; int ret; @@ -372,6 +685,12 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, len = xfer->len / (mas->cur_bits_per_word / BITS_PER_BYTE + 1); len &= TRANS_LEN_MSK; + if (!xfer->cs_change) { + if (!list_is_last(&xfer->transfer_list, + &spi->cur_msg->transfers)) + m_param |= FRAGMENTATION; + } + mas->cur_xfer = xfer; if (xfer->tx_buf) { m_cmd |= SPI_TX_ONLY; @@ -391,7 +710,7 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, * interrupt could come in at any time now. */ spin_lock_irq(&mas->lock); - geni_se_setup_m_cmd(se, m_cmd, FRAGMENTATION); + geni_se_setup_m_cmd(se, m_cmd, m_param); /* * TX_WATERMARK_REG should be set after SPI configuration and @@ -408,13 +727,63 @@ static int spi_geni_transfer_one(struct spi_master *spi, struct spi_transfer *xfer) { struct spi_geni_master *mas = spi_master_get_devdata(spi); + unsigned long timeout; + int ret = 0; /* Terminate and return success for 0 byte length transfer */ if (!xfer->len) - return 0; + return ret; + + if (mas->cur_xfer_mode == GENI_SE_FIFO) { + setup_fifo_xfer(xfer, mas, slv->mode, spi); + } else { + setup_gsi_xfer(xfer, mas, slv, spi); + if ((mas->num_xfers >= NUM_SPI_XFER) || + (list_is_last(&xfer->transfer_list, + &spi->cur_msg->transfers))) { + int i; + + for (i = 0 ; i < mas->num_tx_eot; i++) { + timeout = + wait_for_completion_timeout( + &mas->tx_cb, + msecs_to_jiffies(SPI_XFER_TIMEOUT_MS)); + if (timeout <= 0) { + dev_err(mas->dev, + "Tx[%d] timeout%lu\n", i, timeout); + ret = -ETIMEDOUT; + goto err_gsi_geni_transfer_one; + } + /* TODO: Move this to tx callback */ + spi_finalize_current_transfer(spi); + } + for (i = 0 ; i < mas->num_rx_eot; i++) { + timeout = + wait_for_completion_timeout( + &mas->rx_cb, + msecs_to_jiffies(SPI_XFER_TIMEOUT_MS)); + if (timeout <= 0) { + dev_err(mas->dev, + "Rx[%d] timeout%lu\n", i, timeout); + ret = -ETIMEDOUT; + goto err_gsi_geni_transfer_one; + } + /* TODO: Move this to rx callback */ + spi_finalize_current_transfer(spi); + } + if (mas->qn_err) { + ret = -EIO; + mas->qn_err = false; + goto err_gsi_geni_transfer_one; + } + } + } + + return ret; - setup_fifo_xfer(xfer, mas, slv->mode, spi); - return 1; +err_gsi_geni_transfer_one: + dmaengine_terminate_all(mas->tx); + return ret; } static unsigned int geni_byte_per_fifo_word(struct spi_geni_master *mas) @@ -588,6 +957,15 @@ static int spi_geni_probe(struct platform_device *pdev) if (irq < 0) return irq; + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "could not set DMA mask\n"); + return ret; + } + } + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); @@ -627,14 +1005,17 @@ static int spi_geni_probe(struct platform_device *pdev) spi->num_chipselect = 4; spi->max_speed_hz = 50000000; spi->prepare_message = spi_geni_prepare_message; + spi->unprepare_message = spi_geni_unprepare_message; spi->transfer_one = spi_geni_transfer_one; spi->auto_runtime_pm = true; spi->handle_err = handle_fifo_timeout; - spi->set_cs = spi_geni_set_cs; init_completion(&mas->cs_done); init_completion(&mas->cancel_done); init_completion(&mas->abort_done); + init_completion(&mas->xfer_done); + init_completion(&mas->tx_cb); + init_completion(&mas->rx_cb); spin_lock_init(&mas->lock); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, 250); @@ -655,6 +1036,11 @@ static int spi_geni_probe(struct platform_device *pdev) if (ret) goto spi_geni_probe_runtime_disable; + /* query the mode supported and set_cs for fifo mode */ + mas->cur_xfer_mode = get_xfer_mode(spi); + if (mas->cur_xfer_mode == GENI_SE_FIFO) + spi->set_cs = spi_geni_set_cs; + ret = request_irq(mas->irq, geni_spi_isr, 0, dev_name(dev), spi); if (ret) goto spi_geni_probe_runtime_disable; diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig index aa9c1d80fae4..8d5ac2df26dc 100644 --- a/drivers/thermal/qcom/Kconfig +++ b/drivers/thermal/qcom/Kconfig @@ -10,6 +10,17 @@ config QCOM_TSENS Also able to set threshold temperature for both hot and cold and update when a threshold is reached. +config QCOM_SPMI_ADC_TM5 + tristate "Qualcomm SPMI PMIC Thermal Monitor ADC5" + depends on OF && SPMI && IIO + select REGMAP_SPMI + select QCOM_VADC_COMMON + help + This enables the thermal driver for the ADC thermal monitoring + device. It shows up as a thermal zone with multiple trip points. + Thermal client sets threshold temperature for both warm and cool and + gets updated when a threshold is reached. + config QCOM_SPMI_TEMP_ALARM tristate "Qualcomm SPMI PMIC Temperature Alarm" depends on OF && SPMI && IIO diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile index ec86eef7f6a6..252ea7d9da0b 100644 --- a/drivers/thermal/qcom/Makefile +++ b/drivers/thermal/qcom/Makefile @@ -3,4 +3,5 @@ obj-$(CONFIG_QCOM_TSENS) += qcom_tsens.o qcom_tsens-y += tsens.o tsens-v2.o tsens-v1.o tsens-v0_1.o \ tsens-8960.o +obj-$(CONFIG_QCOM_SPMI_ADC_TM5) += qcom-spmi-adc-tm5.o obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o diff --git a/drivers/thermal/qcom/qcom-spmi-adc-tm5.c b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c new file mode 100644 index 000000000000..124102e9c1fb --- /dev/null +++ b/drivers/thermal/qcom/qcom-spmi-adc-tm5.c @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2020 Linaro Limited + */ + +#include <linux/bitfield.h> +#include <linux/iio/consumer.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/thermal.h> + +#include "../../iio/adc/qcom-vadc-common.h" + +#define ADC5_MAX_CHANNEL 0xc0 +#define ADC_TM5_NUM_CHANNELS 8 + +#define ADC_TM5_STATUS_LOW 0x0a + +#define ADC_TM5_STATUS_HIGH 0x0b + +#define ADC_TM5_NUM_BTM 0x0f + +#define ADC_TM5_ADC_DIG_PARAM 0x42 + +#define ADC_TM5_FAST_AVG_CTL (ADC_TM5_ADC_DIG_PARAM + 1) +#define ADC_TM5_FAST_AVG_EN BIT(7) + +#define ADC_TM5_MEAS_INTERVAL_CTL (ADC_TM5_ADC_DIG_PARAM + 2) +#define ADC_TM5_TIMER1 3 /* 3.9ms */ + +#define ADC_TM5_MEAS_INTERVAL_CTL2 (ADC_TM5_ADC_DIG_PARAM + 3) +#define ADC_TM5_MEAS_INTERVAL_CTL2_MASK 0xf0 +#define ADC_TM5_TIMER2 10 /* 1 second */ +#define ADC_TM5_MEAS_INTERVAL_CTL3_MASK 0xf +#define ADC_TM5_TIMER3 4 /* 4 second */ + +#define ADC_TM5_M_CHAN_BASE 0x60 + +#define ADC_TM5_M_ADC_CH_SEL_CTL(n) (ADC_TM5_M_CHAN_BASE + (n * 8) + 0) +#define ADC_TM5_M_LOW_THR0(n) (ADC_TM5_M_CHAN_BASE + (n * 8) + 1) +#define ADC_TM5_M_LOW_THR1(n) (ADC_TM5_M_CHAN_BASE + (n * 8) + 2) +#define ADC_TM5_M_HIGH_THR0(n) (ADC_TM5_M_CHAN_BASE + (n * 8) + 3) +#define ADC_TM5_M_HIGH_THR1(n) (ADC_TM5_M_CHAN_BASE + (n * 8) + 4) +#define ADC_TM5_M_MEAS_INTERVAL_CTL(n) (ADC_TM5_M_CHAN_BASE + (n * 8) + 5) +#define ADC_TM5_M_CTL(n) (ADC_TM5_M_CHAN_BASE + (n * 8) + 6) +#define ADC_TM5_M_CTL_HW_SETTLE_DELAY_MASK 0xf +#define ADC_TM5_M_CTL_CAL_SEL_MASK 0x30 +#define ADC_TM5_M_CTL_CAL_VAL 0x40 +#define ADC_TM5_M_EN(n) (ADC_TM5_M_CHAN_BASE + (n * 8) + 7) +#define ADC_TM5_M_MEAS_EN BIT(7) +#define ADC_TM5_M_HIGH_THR_INT_EN BIT(1) +#define ADC_TM5_M_LOW_THR_INT_EN BIT(0) + + +enum adc5_timer_select { + ADC5_TIMER_SEL_1 = 0, + ADC5_TIMER_SEL_2, + ADC5_TIMER_SEL_3, + ADC5_TIMER_SEL_NONE, +}; + +struct adc_tm5_data { + const u32 full_scale_code_volt; + unsigned int *decimation; + unsigned int *hw_settle; +}; + +enum adc_tm5_cal_method { + ADC_TM5_NO_CAL = 0, + ADC_TM5_RATIOMETRIC_CAL, + ADC_TM5_ABSOLUTE_CAL +}; + +struct adc_tm5_chip; + +struct adc_tm5_channel { + unsigned int channel; + unsigned int adc_channel; + enum adc_tm5_cal_method cal_method; + unsigned int prescale; + unsigned int hw_settle_time; + struct iio_channel *iio; + struct adc_tm5_chip *chip; + struct thermal_zone_device *tzd; +}; + +/** + * struct adc_tm5_chip - ADC Thermal Monitoring properties + * @nchannels: amount of channels defined/allocated + * @decimation: sampling rate supported for the channel. + * @avg_samples: ability to provide single result from the ADC + * that is an average of multiple measurements. + * @base: base address of TM registers. + */ +struct adc_tm5_chip { + struct regmap *regmap; + struct device *dev; + const struct adc_tm5_data *data; + struct adc_tm5_channel *channels; + unsigned int nchannels; + unsigned int decimation; + unsigned int avg_samples; + u16 base; +}; + +static const struct adc_tm5_data adc_tm5_data_pmic = { + .full_scale_code_volt = 0x70e4, + .decimation = (unsigned int []) {250, 420, 840}, + .hw_settle = (unsigned int []) {15, 100, 200, 300, 400, 500, 600, 700, + 1, 2, 4, 8, 16, 32, 64, 128}, +}; + +static int adc_tm5_read(struct adc_tm5_chip *adc_tm, u16 offset, u8 *data, int len) +{ + return regmap_bulk_read(adc_tm->regmap, adc_tm->base + offset, data, len); +} + +static int adc_tm5_write(struct adc_tm5_chip *adc_tm, u16 offset, u8 *data, int len) +{ + return regmap_bulk_write(adc_tm->regmap, adc_tm->base + offset, data, len); +} + +static int adc_tm5_reg_update(struct adc_tm5_chip *adc_tm, u16 offset, u8 mask, u8 val) +{ + return regmap_write_bits(adc_tm->regmap, adc_tm->base + offset, mask, val); +} + +static irqreturn_t adc_tm5_isr(int irq, void *data) +{ + struct adc_tm5_chip *chip = data; + u8 status_low, status_high, ctl; + int ret = 0, i = 0; + + ret = adc_tm5_read(chip, ADC_TM5_STATUS_LOW, &status_low, 1); + if (ret) { + dev_err(chip->dev, "read status low failed with %d\n", ret); + return IRQ_HANDLED; + } + + ret = adc_tm5_read(chip, ADC_TM5_STATUS_HIGH, &status_high, 1); + if (ret) { + dev_err(chip->dev, "read status high failed with %d\n", ret); + return IRQ_HANDLED; + } + + for (i = 0; i < chip->nchannels; i++) { + bool upper_set = false, lower_set = false; + unsigned int ch = chip->channels[i].channel; + + if (!chip->channels[i].tzd) { + dev_err_once(chip->dev, "thermal device not found\n"); + continue; + } + + ret = adc_tm5_read(chip, ADC_TM5_M_EN(ch), &ctl, 1); + + if (ret) { + dev_err(chip->dev, "ctl read failed with %d\n", ret); + continue; + } + + lower_set = (status_low & BIT(ch)) && + (ctl & ADC_TM5_M_MEAS_EN) && + (ctl & ADC_TM5_M_LOW_THR_INT_EN); + + upper_set = (status_high & BIT(ch)) && + (ctl & ADC_TM5_M_MEAS_EN) && + (ctl & ADC_TM5_M_HIGH_THR_INT_EN); + + if (upper_set || lower_set) + thermal_zone_device_update(chip->channels[i].tzd, + THERMAL_EVENT_UNSPECIFIED); + } + + return IRQ_HANDLED; +} + +static int adc_tm5_get_temp(void *data, int *temp) +{ + struct adc_tm5_channel *channel = data; + int ret, milli_celsius; + + if (!channel || !channel->iio) + return -EINVAL; + + ret = iio_read_channel_processed(channel->iio, &milli_celsius); + if (ret < 0) + return ret; + + *temp = milli_celsius; + + return 0; +} + +static int adc_tm5_disable_channel(struct adc_tm5_channel *channel) +{ + struct adc_tm5_chip *chip = channel->chip; + unsigned int reg = ADC_TM5_M_EN(channel->channel); + + return adc_tm5_reg_update(chip, reg, + ADC_TM5_M_MEAS_EN | ADC_TM5_M_HIGH_THR_INT_EN | ADC_TM5_M_LOW_THR_INT_EN, + 0); +} + +static int adc_tm5_configure(struct adc_tm5_channel *channel, int low_temp, int high_temp) +{ + struct adc_tm5_chip *chip = channel->chip; + u8 buf[8]; + u16 reg = ADC_TM5_M_ADC_CH_SEL_CTL(channel->channel); + int ret = 0; + + ret = adc_tm5_read(chip, reg, buf, 8); + if (ret) { + dev_err(chip->dev, "block read failed with %d\n", ret); + return ret; + } + + /* Update ADC channel select */ + buf[0] = channel->adc_channel; + + /* Warm temperature corresponds to low voltage threshold */ + if (high_temp != INT_MAX) { + u16 adc_code = qcom_adc_tm5_temp_volt_scale(channel->prescale, + chip->data->full_scale_code_volt, high_temp); + + buf[1] = adc_code & 0xff; + buf[2] = adc_code >> 8; + buf[7] |= ADC_TM5_M_LOW_THR_INT_EN; + } + + /* Cool temperature corresponds to high voltage threshold */ + if (low_temp != -INT_MAX) { + u16 adc_code = qcom_adc_tm5_temp_volt_scale(channel->prescale, + chip->data->full_scale_code_volt, low_temp); + + buf[3] = adc_code & 0xff; + buf[4] = adc_code >> 8; + buf[7] |= ADC_TM5_M_HIGH_THR_INT_EN; + } + + /* Update timer select */ + buf[5] = ADC5_TIMER_SEL_2; + + /* Set calibration select, hw_settle delay */ + buf[6] &= ~ADC_TM5_M_CTL_HW_SETTLE_DELAY_MASK; + buf[6] |= FIELD_PREP(ADC_TM5_M_CTL_HW_SETTLE_DELAY_MASK, channel->hw_settle_time); + buf[6] &= ~ADC_TM5_M_CTL_CAL_SEL_MASK; + buf[6] |= FIELD_PREP(ADC_TM5_M_CTL_CAL_SEL_MASK, channel->cal_method); + + buf[7] |= ADC_TM5_M_MEAS_EN; + + ret = adc_tm5_write(chip, reg, buf, 8); + if (ret) + dev_err(chip->dev, "buf write failed\n"); + + return ret; +} + +static int adc_tm5_set_trips(void *data, int low_temp, int high_temp) +{ + struct adc_tm5_channel *channel = data; + struct adc_tm5_chip *chip; + int ret; + + if (!channel) + return -EINVAL; + + chip = channel->chip; + dev_dbg(chip->dev, "%d:low_temp(mdegC):%d, high_temp(mdegC):%d\n", + channel->channel, low_temp, high_temp); + + if (high_temp == INT_MAX && low_temp <= -INT_MAX) + ret = adc_tm5_disable_channel(channel); + else + ret = adc_tm5_configure(channel, low_temp, high_temp); + + return ret; +} + + +static struct thermal_zone_of_device_ops adc_tm5_ops = { + .get_temp = adc_tm5_get_temp, + .set_trips = adc_tm5_set_trips, +}; + +static int adc_tm5_register_tzd(struct adc_tm5_chip *adc_tm) +{ + unsigned int i; + struct thermal_zone_device *tzd; + + for (i = 0; i < adc_tm->nchannels; i++) { + adc_tm->channels[i].chip = adc_tm; + + tzd = devm_thermal_zone_of_sensor_register( + adc_tm->dev, adc_tm->channels[i].channel, + &adc_tm->channels[i], &adc_tm5_ops); + if (IS_ERR(tzd)) { + dev_err(adc_tm->dev, "Error registering TZ zone:%ld for channel:%d\n", + PTR_ERR(tzd), adc_tm->channels[i].channel); + continue; + } + adc_tm->channels[i].tzd = tzd; + } + + return 0; +} + +static int adc_tm5_init(struct adc_tm5_chip *chip) +{ + u8 buf[4], channels_available; + int ret; + unsigned int i; + + ret = adc_tm5_read(chip, ADC_TM5_NUM_BTM, &channels_available, 1); + if (ret) { + dev_err(chip->dev, "read failed for BTM channels\n"); + return ret; + } + + ret = adc_tm5_read(chip, ADC_TM5_ADC_DIG_PARAM, buf, 4); + if (ret) { + dev_err(chip->dev, "block read failed with %d\n", ret); + return ret; + } + + /* Select decimation */ + buf[0] = chip->decimation; + + /* Select number of samples in fast average mode */ + buf[1] = chip->avg_samples | ADC_TM5_FAST_AVG_EN; + + /* Select timer1 */ + buf[2] = ADC_TM5_TIMER1; + + /* Select timer2 and timer3 */ + buf[3] = FIELD_PREP(ADC_TM5_MEAS_INTERVAL_CTL2_MASK, ADC_TM5_TIMER2) | + FIELD_PREP(ADC_TM5_MEAS_INTERVAL_CTL3_MASK, ADC_TM5_TIMER3); + + ret = adc_tm5_write(chip, ADC_TM5_ADC_DIG_PARAM, buf, 4); + if (ret) + dev_err(chip->dev, "block write failed with %d\n", ret); + + for (i = 0; i < chip->nchannels; i++) { + if (chip->channels[i].channel >= channels_available) { + dev_err(chip->dev, "Invalid channel %d\n", chip->channels[i].channel); + return -EINVAL; + } + } + + return ret; +} + +static int adc_tm5_get_dt_channel_data(struct adc_tm5_chip *adc_tm, + struct adc_tm5_channel *channel, + struct device_node *node) +{ + const char *name = node->name; + u32 chan, value, varr[2]; + int ret; + struct device *dev = adc_tm->dev; + + ret = of_property_read_u32(node, "reg", &chan); + if (ret) { + dev_err(dev, "invalid channel number %s\n", name); + return ret; + } + + if (chan >= ADC_TM5_NUM_CHANNELS) { + dev_err(dev, "%s invalid channel number %d\n", name, chan); + return -EINVAL; + } + + /* the channel has DT description */ + channel->channel = chan; + + ret = of_property_read_u32(node, "qcom,adc-channel", &chan); + if (ret) { + dev_err(dev, "invalid channel number %s\n", name); + return ret; + } + if (chan >= ADC5_MAX_CHANNEL) { + dev_err(dev, "%s invalid ADC channel number %d\n", name, chan); + return ret; + } + channel->adc_channel = chan; + + channel->iio = devm_iio_channel_get(adc_tm->dev, name); + if (IS_ERR(channel->iio)) { + ret = PTR_ERR(channel->iio); + channel->iio = NULL; + dev_err(dev, "error getting channel %s: %d\n", name, ret); + return ret; + } + + ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2); + if (!ret) { + ret = qcom_adc5_prescaling_from_dt(varr[0], varr[1]); + if (ret < 0) { + dev_err(dev, "%02x invalid pre-scaling <%d %d>\n", + chan, varr[0], varr[1]); + return ret; + } + channel->prescale = ret; + } else { + /* 1:1 prescale is index 0 */ + channel->prescale = 0; + } + + ret = of_property_read_u32(node, "qcom,hw-settle-time", &value); + if (!ret) { + ret = qcom_adc5_hw_settle_time_from_dt(value, adc_tm->data->hw_settle); + if (ret < 0) { + dev_err(dev, "%02x invalid hw-settle-time %d us\n", + chan, value); + return ret; + } + channel->hw_settle_time = ret; + } else { + channel->hw_settle_time = VADC_DEF_HW_SETTLE_TIME; + } + + if (of_property_read_bool(node, "qcom,ratiometric")) + channel->cal_method = ADC_TM5_RATIOMETRIC_CAL; + else + channel->cal_method = ADC_TM5_ABSOLUTE_CAL; + + return 0; +} + +static int adc_tm5_get_dt_data(struct adc_tm5_chip *adc_tm, struct device_node *node) +{ + struct adc_tm5_channel *channels; + struct device_node *child; + unsigned int index = 0; + u32 value; + int ret; + struct device *dev = adc_tm->dev; + + adc_tm->nchannels = of_get_available_child_count(node); + if (!adc_tm->nchannels) + return -EINVAL; + + adc_tm->channels = devm_kcalloc(dev, adc_tm->nchannels, + sizeof(*adc_tm->channels), GFP_KERNEL); + if (!adc_tm->channels) + return -ENOMEM; + + channels = adc_tm->channels; + + adc_tm->data = of_device_get_match_data(dev); + + ret = of_property_read_u32(node, "qcom,decimation", &value); + if (!ret) { + ret = qcom_adc5_decimation_from_dt(value, adc_tm->data->decimation); + if (ret < 0) { + dev_err(dev, "invalid decimation %d\n", value); + return ret; + } + adc_tm->decimation = ret; + } else { + adc_tm->decimation = ADC5_DECIMATION_DEFAULT; + } + + ret = of_property_read_u32(node, "qcom,avg-samples", &value); + if (!ret) { + ret = qcom_adc5_avg_samples_from_dt(value); + if (ret < 0) { + dev_err(dev, "invalid avg-samples %d\n", value); + return ret; + } + adc_tm->avg_samples = ret; + } else { + adc_tm->avg_samples = VADC_DEF_AVG_SAMPLES; + } + + for_each_available_child_of_node(node, child) { + ret = adc_tm5_get_dt_channel_data(adc_tm, channels, child); + if (ret) { + of_node_put(child); + return ret; + } + + channels++; + index++; + } + + return 0; +} + +static int adc_tm5_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct adc_tm5_chip *adc_tm; + struct regmap *regmap; + int ret, irq; + u32 reg; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return -ENODEV; + + ret = of_property_read_u32(node, "reg", ®); + if (ret) + return ret; + + adc_tm = devm_kzalloc(&pdev->dev, sizeof(*adc_tm), GFP_KERNEL); + if (!adc_tm) + return -ENOMEM; + + adc_tm->regmap = regmap; + adc_tm->dev = dev; + adc_tm->base = reg; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "get_irq failed: %d\n", irq); + return irq; + } + + ret = adc_tm5_get_dt_data(adc_tm, node); + if (ret) { + dev_err(dev, "get dt data failed: %d\n", ret); + return ret; + } + + ret = adc_tm5_init(adc_tm); + if (ret) { + dev_err(dev, "adc-tm init failed\n"); + return ret; + } + + ret = adc_tm5_register_tzd(adc_tm); + if (ret) { + dev_err(dev, "tzd register failed\n"); + return ret; + } + + return devm_request_irq(dev, irq, adc_tm5_isr, 0, + "pm-adc-tm5", adc_tm); +} + +static const struct of_device_id adc_tm5_match_table[] = { + { + .compatible = "qcom,spmi-adc-tm5", + .data = &adc_tm5_data_pmic, + }, + { } +}; +MODULE_DEVICE_TABLE(of, adc_tm5_match_table); + +static struct platform_driver adc_tm5_driver = { + .driver = { + .name = "spmi-adc-tm5", + .of_match_table = adc_tm5_match_table, + }, + .probe = adc_tm5_probe, +}; +module_platform_driver(adc_tm5_driver); + +MODULE_DESCRIPTION("SPMI PMIC Thermal Monitor ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/thermal/qcom/tsens-v2.c b/drivers/thermal/qcom/tsens-v2.c index b293ed32174b..58cac8f2a358 100644 --- a/drivers/thermal/qcom/tsens-v2.c +++ b/drivers/thermal/qcom/tsens-v2.c @@ -26,7 +26,7 @@ #define TM_TRDY_OFF 0x00e4 #define TM_WDOG_LOG_OFF 0x013c -/* v2.x: 8996, 8998, sdm845 */ +/* v2.x: 8996, 8998, sc7180, sdm845, sm8150, sm8250 */ static struct tsens_features tsens_v2_feat = { .ver_major = VER_2_X, diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index e1e78e9824b1..fe1e8a4ab7d6 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -19,6 +19,8 @@ #include <linux/usb/of.h> #include <linux/reset.h> #include <linux/iopoll.h> +#include <linux/fwnode.h> +#include <linux/usb/role.h> #include "core.h" @@ -71,6 +73,9 @@ struct dwc3_qcom { struct notifier_block vbus_nb; struct notifier_block host_nb; + struct usb_role_switch *role_sw; + struct usb_role_switch *dwc3_drd_sw; + const struct dwc3_acpi_pdata *acpi_pdata; enum usb_dr_mode mode; @@ -190,6 +195,73 @@ static int dwc3_qcom_register_extcon(struct dwc3_qcom *qcom) return 0; } +static int dwc3_qcom_usb_role_switch_set(struct usb_role_switch *sw, + enum usb_role role) +{ + struct dwc3_qcom *qcom = usb_role_switch_get_drvdata(sw); + struct fwnode_handle *child; + bool enable = false; + + if (!qcom->dwc3_drd_sw) { + child = device_get_next_child_node(qcom->dev, NULL); + if (child) { + qcom->dwc3_drd_sw = usb_role_switch_find_by_fwnode(child); + fwnode_handle_put(child); + if (IS_ERR(qcom->dwc3_drd_sw)) { + qcom->dwc3_drd_sw = NULL; + return 0; + } + } + } + + usb_role_switch_set_role(qcom->dwc3_drd_sw, role); + + if (role == USB_ROLE_DEVICE) + enable = true; + else + enable = false; + + qcom->mode = (role == USB_ROLE_HOST) ? USB_DR_MODE_HOST : + USB_DR_MODE_PERIPHERAL; + dwc3_qcom_vbus_overrride_enable(qcom, enable); + return 0; +} + +static enum usb_role dwc3_qcom_usb_role_switch_get(struct usb_role_switch *sw) +{ + struct dwc3_qcom *qcom = usb_role_switch_get_drvdata(sw); + enum usb_role role; + + switch (qcom->mode) { + case USB_DR_MODE_HOST: + role = USB_ROLE_HOST; + break; + case USB_DR_MODE_PERIPHERAL: + role = USB_ROLE_DEVICE; + break; + default: + role = USB_ROLE_DEVICE; + break; + } + + return role; +} + +static int dwc3_qcom_setup_role_switch(struct dwc3_qcom *qcom) +{ + struct usb_role_switch_desc dwc3_role_switch = {NULL}; + + dwc3_role_switch.fwnode = dev_fwnode(qcom->dev); + dwc3_role_switch.set = dwc3_qcom_usb_role_switch_set; + dwc3_role_switch.get = dwc3_qcom_usb_role_switch_get; + dwc3_role_switch.driver_data = qcom; + qcom->role_sw = usb_role_switch_register(qcom->dev, &dwc3_role_switch); + if (IS_ERR(qcom->role_sw)) + return PTR_ERR(qcom->role_sw); + + return 0; +} + static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom) { if (qcom->hs_phy_irq) { @@ -540,6 +612,25 @@ static int dwc3_qcom_of_register_core(struct platform_device *pdev) return 0; } +static void *dwc3_qcom_find_usb_connector_match(struct device_connection *con, + int ep, void *data) +{ + if (!fwnode_property_match_string(con->fwnode, "compatible", + "gpio-usb-b-connector") || + !fwnode_property_match_string(con->fwnode, "compatible", + "usb-c-connector")) + return con->fwnode; + return 0; +} + +static bool dwc3_qcom_find_usb_connector(struct platform_device *pdev) +{ + struct fwnode_handle *fwnode = pdev->dev.fwnode; + + return fwnode_connection_find_match(fwnode, "connector", NULL, + dwc3_qcom_find_usb_connector_match); +} + static int dwc3_qcom_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -644,8 +735,13 @@ static int dwc3_qcom_probe(struct platform_device *pdev) if (qcom->mode == USB_DR_MODE_PERIPHERAL) dwc3_qcom_vbus_overrride_enable(qcom, true); - /* register extcon to override sw_vbus on Vbus change later */ - ret = dwc3_qcom_register_extcon(qcom); + if (dwc3_qcom_find_usb_connector(pdev)) { + ret = dwc3_qcom_setup_role_switch(qcom); + } else { + /* register extcon to override sw_vbus on Vbus change later */ + ret = dwc3_qcom_register_extcon(qcom); + } + if (ret) goto depopulate; @@ -679,6 +775,9 @@ static int dwc3_qcom_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; int i; + usb_role_switch_unregister(qcom->role_sw); + usb_role_switch_put(qcom->dwc3_drd_sw); + of_platform_depopulate(dev); for (i = qcom->num_clocks - 1; i >= 0; i--) { diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index 559dd06117e7..63789cf88fce 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -73,6 +73,18 @@ config TYPEC_TPS6598X If you choose to build this driver as a dynamically linked module, the module will be called tps6598x.ko. +config TYPEC_QCOM_PMIC + tristate "Qualcomm PMIC USB Type-C driver" + depends on ARCH_QCOM || COMPILE_TEST + help + Driver for supporting role switch over the Qualcomm PMIC. This will + handle the USB Type-C role and orientation detection reported by the + QCOM PMIC if the PMIC has the capability to handle USB Type-C + detection. + + It will also enable the VBUS output to connected devices when a + DFP connection is made. + source "drivers/usb/typec/mux/Kconfig" source "drivers/usb/typec/altmodes/Kconfig" diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index 7753a5c3cd46..cceffd987643 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -6,4 +6,5 @@ obj-$(CONFIG_TYPEC_TCPM) += tcpm/ obj-$(CONFIG_TYPEC_UCSI) += ucsi/ obj-$(CONFIG_TYPEC_HD3SS3220) += hd3ss3220.o obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o +obj-$(CONFIG_TYPEC_QCOM_PMIC) += qcom-pmic-typec.o obj-$(CONFIG_TYPEC) += mux/ diff --git a/drivers/usb/typec/qcom-pmic-typec.c b/drivers/usb/typec/qcom-pmic-typec.c new file mode 100644 index 000000000000..20b2b6502cb3 --- /dev/null +++ b/drivers/usb/typec/qcom-pmic-typec.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#include <linux/err.h> +#include <linux/regmap.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/mod_devicetable.h> +#include <linux/usb/role.h> +#include <linux/usb/typec_mux.h> +#include <linux/regulator/consumer.h> + +#define TYPEC_MISC_STATUS 0xb +#define CC_ATTACHED BIT(0) +#define CC_ORIENTATION BIT(1) +#define SNK_SRC_MODE BIT(6) +#define TYPEC_MODE_CFG 0x44 +#define TYPEC_DISABLE_CMD BIT(0) +#define EN_SNK_ONLY BIT(1) +#define EN_SRC_ONLY BIT(2) +#define TYPEC_VCONN_CONTROL 0x46 +#define VCONN_EN_SRC BIT(0) +#define VCONN_EN_VAL BIT(1) +#define TYPEC_EXIT_STATE_CFG 0x50 +#define SEL_SRC_UPPER_REF BIT(2) +#define TYPEC_INTR_EN_CFG_1 0x5e +#define TYPEC_INTR_EN_CFG_1_MASK GENMASK(7, 0) + +struct qcom_pmic_typec { + struct device *dev; + struct fwnode_handle *fwnode; + struct regmap *regmap; + u32 base; + + struct typec_capability *cap; + struct typec_port *port; + struct usb_role_switch *role_sw; + + struct regulator *vbus_reg; + bool vbus_enabled; +}; + +static void qcom_pmic_typec_enable_vbus_regulator(struct qcom_pmic_typec + *qcom_usb, bool enable) +{ + int ret; + + if (enable == qcom_usb->vbus_enabled) + return; + + if (!qcom_usb->vbus_reg) { + qcom_usb->vbus_reg = devm_regulator_get(qcom_usb->dev, + "usb_vbus"); + if (IS_ERR(qcom_usb->vbus_reg)) { + qcom_usb->vbus_reg = NULL; + return; + } + } + + if (enable) { + ret = regulator_enable(qcom_usb->vbus_reg); + if (ret) + return; + } else { + ret = regulator_disable(qcom_usb->vbus_reg); + if (ret) + return; + } + qcom_usb->vbus_enabled = enable; +} + +static void qcom_pmic_typec_check_connection(struct qcom_pmic_typec *qcom_usb) +{ + enum typec_orientation orientation; + enum usb_role role; + unsigned int stat; + bool enable_vbus; + + regmap_read(qcom_usb->regmap, qcom_usb->base + TYPEC_MISC_STATUS, + &stat); + + if (stat & CC_ATTACHED) { + orientation = (stat & CC_ORIENTATION) ? + TYPEC_ORIENTATION_REVERSE : + TYPEC_ORIENTATION_NORMAL; + typec_set_orientation(qcom_usb->port, orientation); + + role = (stat & SNK_SRC_MODE) ? USB_ROLE_HOST : USB_ROLE_DEVICE; + if (role == USB_ROLE_HOST) + enable_vbus = true; + else + enable_vbus = false; + } else { + role = USB_ROLE_NONE; + enable_vbus = false; + } + + qcom_pmic_typec_enable_vbus_regulator(qcom_usb, enable_vbus); + usb_role_switch_set_role(qcom_usb->role_sw, role); +} + +static irqreturn_t qcom_pmic_typec_interrupt(int irq, void *_qcom_usb) +{ + struct qcom_pmic_typec *qcom_usb = _qcom_usb; + + qcom_pmic_typec_check_connection(qcom_usb); + return IRQ_HANDLED; +} + +static void qcom_pmic_typec_typec_hw_init(struct qcom_pmic_typec *qcom_usb) +{ + u8 mode = 0; + + regmap_update_bits(qcom_usb->regmap, + qcom_usb->base + TYPEC_INTR_EN_CFG_1, + TYPEC_INTR_EN_CFG_1_MASK, 0); + + if (qcom_usb->cap->type != TYPEC_PORT_DRP) + mode = (qcom_usb->cap->type == TYPEC_PORT_SNK) ? + EN_SNK_ONLY : EN_SRC_ONLY; + regmap_update_bits(qcom_usb->regmap, qcom_usb->base + TYPEC_MODE_CFG, + EN_SNK_ONLY | EN_SRC_ONLY, mode); + + regmap_update_bits(qcom_usb->regmap, + qcom_usb->base + TYPEC_VCONN_CONTROL, + VCONN_EN_SRC | VCONN_EN_VAL, VCONN_EN_SRC); + regmap_update_bits(qcom_usb->regmap, + qcom_usb->base + TYPEC_EXIT_STATE_CFG, + SEL_SRC_UPPER_REF, SEL_SRC_UPPER_REF); +} + +static int qcom_pmic_typec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct qcom_pmic_typec *qcom_usb; + struct typec_capability *cap; + const char *buf; + int ret, irq, role; + u32 reg; + + ret = device_property_read_u32(dev, "reg", ®); + if (ret < 0) { + dev_err(dev, "missing base address\n"); + return ret; + } + + qcom_usb = devm_kzalloc(dev, sizeof(*qcom_usb), GFP_KERNEL); + if (!qcom_usb) + return -ENOMEM; + + qcom_usb->dev = dev; + qcom_usb->base = reg; + + qcom_usb->regmap = dev_get_regmap(dev->parent, NULL); + if (!qcom_usb->regmap) { + dev_err(dev, "Failed to get regmap\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -EINVAL; + + ret = devm_request_threaded_irq(qcom_usb->dev, irq, NULL, + qcom_pmic_typec_interrupt, IRQF_ONESHOT, + "qcom-pmic-typec", qcom_usb); + if (ret) { + dev_err(&pdev->dev, "Could not request IRQ\n"); + return ret; + } + + qcom_usb->fwnode = device_get_named_child_node(dev, "connector"); + if (!qcom_usb->fwnode) + return -EINVAL; + + cap = devm_kzalloc(dev, sizeof(*cap), GFP_KERNEL); + if (!cap) { + ret = -ENOMEM; + goto err_put_node; + } + + ret = fwnode_property_read_string(qcom_usb->fwnode, "power-role", &buf); + if (!ret) { + role = typec_find_port_power_role(buf); + if (role < 0) + role = TYPEC_PORT_SNK; + } else { + role = TYPEC_PORT_SNK; + } + cap->type = role; + + ret = fwnode_property_read_string(qcom_usb->fwnode, "data-role", &buf); + if (!ret) { + role = typec_find_port_data_role(buf); + if (role < 0) + role = TYPEC_PORT_UFP; + } else { + role = TYPEC_PORT_UFP; + } + cap->data = role; + + cap->prefer_role = TYPEC_NO_PREFERRED_ROLE; + cap->fwnode = qcom_usb->fwnode; + qcom_usb->port = typec_register_port(dev, cap); + if (IS_ERR(qcom_usb->port)) { + ret = PTR_ERR(qcom_usb->port); + dev_err(dev, "Failed to register type c port %d\n", ret); + goto err_put_node; + } + + qcom_usb->cap = cap; + + qcom_usb->role_sw = fwnode_usb_role_switch_get(qcom_usb->fwnode); + if (IS_ERR(qcom_usb->role_sw)) { + if (PTR_ERR(qcom_usb->role_sw) != -EPROBE_DEFER) + dev_err(dev, "failed to get role switch\n"); + ret = PTR_ERR(qcom_usb->role_sw); + goto err_typec_port; + } + + platform_set_drvdata(pdev, qcom_usb); + qcom_pmic_typec_typec_hw_init(qcom_usb); + qcom_pmic_typec_check_connection(qcom_usb); + + return 0; + +err_typec_port: + typec_unregister_port(qcom_usb->port); +err_put_node: + fwnode_handle_put(qcom_usb->fwnode); + + return ret; +} + +static int qcom_pmic_typec_remove(struct platform_device *pdev) +{ + struct qcom_pmic_typec *qcom_usb = platform_get_drvdata(pdev); + + usb_role_switch_set_role(qcom_usb->role_sw, USB_ROLE_NONE); + qcom_pmic_typec_enable_vbus_regulator(qcom_usb, 0); + + typec_unregister_port(qcom_usb->port); + usb_role_switch_put(qcom_usb->role_sw); + fwnode_handle_put(qcom_usb->fwnode); + + return 0; +} + +static const struct of_device_id qcom_pmic_typec_table[] = { + { .compatible = "qcom,pm8150b-usb-typec" }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_pmic_typec_table); + +static struct platform_driver qcom_pmic_typec = { + .driver = { + .name = "qcom,pmic-typec", + .of_match_table = qcom_pmic_typec_table, + }, + .probe = qcom_pmic_typec_probe, + .remove = qcom_pmic_typec_remove, +}; +module_platform_driver(qcom_pmic_typec); + +MODULE_DESCRIPTION("QCOM PMIC USB type C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 5e850cc9f891..39deb22a4180 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -22,52 +22,6 @@ config VGA_CONSOLE Say Y. -config VGACON_SOFT_SCROLLBACK - bool "Enable Scrollback Buffer in System RAM" - depends on VGA_CONSOLE - default n - help - The scrollback buffer of the standard VGA console is located in - the VGA RAM. The size of this RAM is fixed and is quite small. - If you require a larger scrollback buffer, this can be placed in - System RAM which is dynamically allocated during initialization. - Placing the scrollback buffer in System RAM will slightly slow - down the console. - - If you want this feature, say 'Y' here and enter the amount of - RAM to allocate for this buffer. If unsure, say 'N'. - -config VGACON_SOFT_SCROLLBACK_SIZE - int "Scrollback Buffer Size (in KB)" - depends on VGACON_SOFT_SCROLLBACK - range 1 1024 - default "64" - help - Enter the amount of System RAM to allocate for scrollback - buffers of VGA consoles. Each 64KB will give you approximately - 16 80x25 screenfuls of scrollback buffer. - -config VGACON_SOFT_SCROLLBACK_PERSISTENT_ENABLE_BY_DEFAULT - bool "Persistent Scrollback History for each console by default" - depends on VGACON_SOFT_SCROLLBACK - default n - help - Say Y here if the scrollback history should persist by default when - switching between consoles. Otherwise, the scrollback history will be - flushed each time the console is switched. This feature can also be - enabled using the boot command line parameter - 'vgacon.scrollback_persistent=1'. - - This feature might break your tool of choice to flush the scrollback - buffer, e.g. clear(1) will work fine but Debian's clear_console(1) - will be broken, which might cause security issues. - You can use the escape sequence \e[3J instead if this feature is - activated. - - Note that a buffer of VGACON_SOFT_SCROLLBACK_SIZE is taken for each - created tty device. - So if you use a RAM-constrained system, say N here. - config MDA_CONSOLE depends on !M68K && !PARISC && ISA tristate "MDA text console (dual-headed)" diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index a52bb3740073..17876f0179b5 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -165,214 +165,6 @@ static inline void vga_set_mem_top(struct vc_data *c) write_vga(12, (c->vc_visible_origin - vga_vram_base) / 2); } -#ifdef CONFIG_VGACON_SOFT_SCROLLBACK -/* software scrollback */ -struct vgacon_scrollback_info { - void *data; - int tail; - int size; - int rows; - int cnt; - int cur; - int save; - int restore; -}; - -static struct vgacon_scrollback_info *vgacon_scrollback_cur; -static struct vgacon_scrollback_info vgacon_scrollbacks[MAX_NR_CONSOLES]; -static bool scrollback_persistent = \ - IS_ENABLED(CONFIG_VGACON_SOFT_SCROLLBACK_PERSISTENT_ENABLE_BY_DEFAULT); -module_param_named(scrollback_persistent, scrollback_persistent, bool, 0000); -MODULE_PARM_DESC(scrollback_persistent, "Enable persistent scrollback for all vga consoles"); - -static void vgacon_scrollback_reset(int vc_num, size_t reset_size) -{ - struct vgacon_scrollback_info *scrollback = &vgacon_scrollbacks[vc_num]; - - if (scrollback->data && reset_size > 0) - memset(scrollback->data, 0, reset_size); - - scrollback->cnt = 0; - scrollback->tail = 0; - scrollback->cur = 0; -} - -static void vgacon_scrollback_init(int vc_num) -{ - int pitch = vga_video_num_columns * 2; - size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024; - int rows = size / pitch; - void *data; - - data = kmalloc_array(CONFIG_VGACON_SOFT_SCROLLBACK_SIZE, 1024, - GFP_NOWAIT); - - vgacon_scrollbacks[vc_num].data = data; - vgacon_scrollback_cur = &vgacon_scrollbacks[vc_num]; - - vgacon_scrollback_cur->rows = rows - 1; - vgacon_scrollback_cur->size = rows * pitch; - - vgacon_scrollback_reset(vc_num, size); -} - -static void vgacon_scrollback_switch(int vc_num) -{ - if (!scrollback_persistent) - vc_num = 0; - - if (!vgacon_scrollbacks[vc_num].data) { - vgacon_scrollback_init(vc_num); - } else { - if (scrollback_persistent) { - vgacon_scrollback_cur = &vgacon_scrollbacks[vc_num]; - } else { - size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024; - - vgacon_scrollback_reset(vc_num, size); - } - } -} - -static void vgacon_scrollback_startup(void) -{ - vgacon_scrollback_cur = &vgacon_scrollbacks[0]; - vgacon_scrollback_init(0); -} - -static void vgacon_scrollback_update(struct vc_data *c, int t, int count) -{ - void *p; - - if (!vgacon_scrollback_cur->data || !vgacon_scrollback_cur->size || - c->vc_num != fg_console) - return; - - p = (void *) (c->vc_origin + t * c->vc_size_row); - - while (count--) { - if ((vgacon_scrollback_cur->tail + c->vc_size_row) > - vgacon_scrollback_cur->size) - vgacon_scrollback_cur->tail = 0; - - scr_memcpyw(vgacon_scrollback_cur->data + - vgacon_scrollback_cur->tail, - p, c->vc_size_row); - - vgacon_scrollback_cur->cnt++; - p += c->vc_size_row; - vgacon_scrollback_cur->tail += c->vc_size_row; - - if (vgacon_scrollback_cur->tail >= vgacon_scrollback_cur->size) - vgacon_scrollback_cur->tail = 0; - - if (vgacon_scrollback_cur->cnt > vgacon_scrollback_cur->rows) - vgacon_scrollback_cur->cnt = vgacon_scrollback_cur->rows; - - vgacon_scrollback_cur->cur = vgacon_scrollback_cur->cnt; - } -} - -static void vgacon_restore_screen(struct vc_data *c) -{ - c->vc_origin = c->vc_visible_origin; - vgacon_scrollback_cur->save = 0; - - if (!vga_is_gfx && !vgacon_scrollback_cur->restore) { - scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf, - c->vc_screenbuf_size > vga_vram_size ? - vga_vram_size : c->vc_screenbuf_size); - vgacon_scrollback_cur->restore = 1; - vgacon_scrollback_cur->cur = vgacon_scrollback_cur->cnt; - } -} - -static void vgacon_scrolldelta(struct vc_data *c, int lines) -{ - int start, end, count, soff; - - if (!lines) { - vgacon_restore_screen(c); - return; - } - - if (!vgacon_scrollback_cur->data) - return; - - if (!vgacon_scrollback_cur->save) { - vgacon_cursor(c, CM_ERASE); - vgacon_save_screen(c); - c->vc_origin = (unsigned long)c->vc_screenbuf; - vgacon_scrollback_cur->save = 1; - } - - vgacon_scrollback_cur->restore = 0; - start = vgacon_scrollback_cur->cur + lines; - end = start + abs(lines); - - if (start < 0) - start = 0; - - if (start > vgacon_scrollback_cur->cnt) - start = vgacon_scrollback_cur->cnt; - - if (end < 0) - end = 0; - - if (end > vgacon_scrollback_cur->cnt) - end = vgacon_scrollback_cur->cnt; - - vgacon_scrollback_cur->cur = start; - count = end - start; - soff = vgacon_scrollback_cur->tail - - ((vgacon_scrollback_cur->cnt - end) * c->vc_size_row); - soff -= count * c->vc_size_row; - - if (soff < 0) - soff += vgacon_scrollback_cur->size; - - count = vgacon_scrollback_cur->cnt - start; - - if (count > c->vc_rows) - count = c->vc_rows; - - if (count) { - int copysize; - - int diff = c->vc_rows - count; - void *d = (void *) c->vc_visible_origin; - void *s = (void *) c->vc_screenbuf; - - count *= c->vc_size_row; - /* how much memory to end of buffer left? */ - copysize = min(count, vgacon_scrollback_cur->size - soff); - scr_memcpyw(d, vgacon_scrollback_cur->data + soff, copysize); - d += copysize; - count -= copysize; - - if (count) { - scr_memcpyw(d, vgacon_scrollback_cur->data, count); - d += count; - } - - if (diff) - scr_memcpyw(d, s, diff * c->vc_size_row); - } else - vgacon_cursor(c, CM_MOVE); -} - -static void vgacon_flush_scrollback(struct vc_data *c) -{ - size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024; - - vgacon_scrollback_reset(c->vc_num, size); -} -#else -#define vgacon_scrollback_startup(...) do { } while (0) -#define vgacon_scrollback_init(...) do { } while (0) -#define vgacon_scrollback_update(...) do { } while (0) -#define vgacon_scrollback_switch(...) do { } while (0) - static void vgacon_restore_screen(struct vc_data *c) { if (c->vc_origin != c->vc_visible_origin) @@ -386,11 +178,6 @@ static void vgacon_scrolldelta(struct vc_data *c, int lines) vga_set_mem_top(c); } -static void vgacon_flush_scrollback(struct vc_data *c) -{ -} -#endif /* CONFIG_VGACON_SOFT_SCROLLBACK */ - static const char *vgacon_startup(void) { const char *display_desc = NULL; @@ -573,10 +360,7 @@ static const char *vgacon_startup(void) vgacon_xres = screen_info.orig_video_cols * VGA_FONTWIDTH; vgacon_yres = vga_scan_lines; - if (!vga_init_done) { - vgacon_scrollback_startup(); - vga_init_done = true; - } + vga_init_done = true; return display_desc; } @@ -869,7 +653,6 @@ static int vgacon_switch(struct vc_data *c) vgacon_doresize(c, c->vc_cols, c->vc_rows); } - vgacon_scrollback_switch(c->vc_num); return 0; /* Redrawing not needed */ } @@ -1386,7 +1169,6 @@ static bool vgacon_scroll(struct vc_data *c, unsigned int t, unsigned int b, oldo = c->vc_origin; delta = lines * c->vc_size_row; if (dir == SM_UP) { - vgacon_scrollback_update(c, t, lines); if (c->vc_scr_end + delta >= vga_vram_end) { scr_memcpyw((u16 *) vga_vram_base, (u16 *) (oldo + delta), @@ -1450,7 +1232,6 @@ const struct consw vga_con = { .con_save_screen = vgacon_save_screen, .con_build_attr = vgacon_build_attr, .con_invert_region = vgacon_invert_region, - .con_flush_scrollback = vgacon_flush_scrollback, }; EXPORT_SYMBOL(vga_con); diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c index 4e6cbc24346d..9725ecd1255b 100644 --- a/drivers/video/fbdev/core/bitblit.c +++ b/drivers/video/fbdev/core/bitblit.c @@ -234,7 +234,7 @@ static void bit_clear_margins(struct vc_data *vc, struct fb_info *info, } static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode, - int softback_lines, int fg, int bg) + int fg, int bg) { struct fb_cursor cursor; struct fbcon_ops *ops = info->fbcon_par; @@ -247,15 +247,6 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode, cursor.set = 0; - if (softback_lines) { - if (y + softback_lines >= vc->vc_rows) { - mode = CM_ERASE; - ops->cursor_flash = 0; - return; - } else - y += softback_lines; - } - c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height)); diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 66167830fefd..0b49b0f44edf 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -122,12 +122,6 @@ static int logo_lines; /* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO enums. */ static int logo_shown = FBCON_LOGO_CANSHOW; -/* Software scrollback */ -static int fbcon_softback_size = 32768; -static unsigned long softback_buf, softback_curr; -static unsigned long softback_in; -static unsigned long softback_top, softback_end; -static int softback_lines; /* console mappings */ static int first_fb_vc; static int last_fb_vc = MAX_NR_CONSOLES - 1; @@ -167,8 +161,6 @@ static int margin_color; static const struct consw fb_con; -#define CM_SOFTBACK (8) - #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row) static int fbcon_set_origin(struct vc_data *); @@ -373,18 +365,6 @@ static int get_color(struct vc_data *vc, struct fb_info *info, return color; } -static void fbcon_update_softback(struct vc_data *vc) -{ - int l = fbcon_softback_size / vc->vc_size_row; - - if (l > 5) - softback_end = softback_buf + l * vc->vc_size_row; - else - /* Smaller scrollback makes no sense, and 0 would screw - the operation totally */ - softback_top = 0; -} - static void fb_flashcursor(struct work_struct *work) { struct fb_info *info = container_of(work, struct fb_info, queue); @@ -414,7 +394,7 @@ static void fb_flashcursor(struct work_struct *work) c = scr_readw((u16 *) vc->vc_pos); mode = (!ops->cursor_flash || ops->cursor_state.enable) ? CM_ERASE : CM_DRAW; - ops->cursor(vc, info, mode, softback_lines, get_color(vc, info, c, 1), + ops->cursor(vc, info, mode, get_color(vc, info, c, 1), get_color(vc, info, c, 0)); console_unlock(); } @@ -471,13 +451,7 @@ static int __init fb_console_setup(char *this_opt) } if (!strncmp(options, "scrollback:", 11)) { - options += 11; - if (*options) { - fbcon_softback_size = simple_strtoul(options, &options, 0); - if (*options == 'k' || *options == 'K') { - fbcon_softback_size *= 1024; - } - } + pr_warn("Ignoring scrollback size option\n"); continue; } @@ -1022,31 +996,6 @@ static const char *fbcon_startup(void) set_blitting_type(vc, info); - if (info->fix.type != FB_TYPE_TEXT) { - if (fbcon_softback_size) { - if (!softback_buf) { - softback_buf = - (unsigned long) - kvmalloc(fbcon_softback_size, - GFP_KERNEL); - if (!softback_buf) { - fbcon_softback_size = 0; - softback_top = 0; - } - } - } else { - if (softback_buf) { - kvfree((void *) softback_buf); - softback_buf = 0; - softback_top = 0; - } - } - if (softback_buf) - softback_in = softback_top = softback_curr = - softback_buf; - softback_lines = 0; - } - /* Setup default font */ if (!p->fontdata && !vc->vc_font.data) { if (!fontname[0] || !(font = find_font(fontname))) @@ -1220,9 +1169,6 @@ static void fbcon_init(struct vc_data *vc, int init) if (logo) fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows); - if (vc == svc && softback_buf) - fbcon_update_softback(vc); - if (ops->rotate_font && ops->rotate_font(info, vc)) { ops->rotate = FB_ROTATE_UR; set_blitting_type(vc, info); @@ -1385,7 +1331,6 @@ static void fbcon_cursor(struct vc_data *vc, int mode) { struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; struct fbcon_ops *ops = info->fbcon_par; - int y; int c = scr_readw((u16 *) vc->vc_pos); ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms); @@ -1399,16 +1344,8 @@ static void fbcon_cursor(struct vc_data *vc, int mode) fbcon_add_cursor_timer(info); ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1; - if (mode & CM_SOFTBACK) { - mode &= ~CM_SOFTBACK; - y = softback_lines; - } else { - if (softback_lines) - fbcon_set_origin(vc); - y = 0; - } - ops->cursor(vc, info, mode, y, get_color(vc, info, c, 1), + ops->cursor(vc, info, mode, get_color(vc, info, c, 1), get_color(vc, info, c, 0)); } @@ -1479,8 +1416,6 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, if (con_is_visible(vc)) { update_screen(vc); - if (softback_buf) - fbcon_update_softback(vc); } } @@ -1618,99 +1553,6 @@ static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) scrollback_current = 0; } -static void fbcon_redraw_softback(struct vc_data *vc, struct fbcon_display *p, - long delta) -{ - int count = vc->vc_rows; - unsigned short *d, *s; - unsigned long n; - int line = 0; - - d = (u16 *) softback_curr; - if (d == (u16 *) softback_in) - d = (u16 *) vc->vc_origin; - n = softback_curr + delta * vc->vc_size_row; - softback_lines -= delta; - if (delta < 0) { - if (softback_curr < softback_top && n < softback_buf) { - n += softback_end - softback_buf; - if (n < softback_top) { - softback_lines -= - (softback_top - n) / vc->vc_size_row; - n = softback_top; - } - } else if (softback_curr >= softback_top - && n < softback_top) { - softback_lines -= - (softback_top - n) / vc->vc_size_row; - n = softback_top; - } - } else { - if (softback_curr > softback_in && n >= softback_end) { - n += softback_buf - softback_end; - if (n > softback_in) { - n = softback_in; - softback_lines = 0; - } - } else if (softback_curr <= softback_in && n > softback_in) { - n = softback_in; - softback_lines = 0; - } - } - if (n == softback_curr) - return; - softback_curr = n; - s = (u16 *) softback_curr; - if (s == (u16 *) softback_in) - s = (u16 *) vc->vc_origin; - while (count--) { - unsigned short *start; - unsigned short *le; - unsigned short c; - int x = 0; - unsigned short attr = 1; - - start = s; - le = advance_row(s, 1); - do { - c = scr_readw(s); - if (attr != (c & 0xff00)) { - attr = c & 0xff00; - if (s > start) { - fbcon_putcs(vc, start, s - start, - line, x); - x += s - start; - start = s; - } - } - if (c == scr_readw(d)) { - if (s > start) { - fbcon_putcs(vc, start, s - start, - line, x); - x += s - start + 1; - start = s + 1; - } else { - x++; - start++; - } - } - s++; - d++; - } while (s < le); - if (s > start) - fbcon_putcs(vc, start, s - start, line, x); - line++; - if (d == (u16 *) softback_end) - d = (u16 *) softback_buf; - if (d == (u16 *) softback_in) - d = (u16 *) vc->vc_origin; - if (s == (u16 *) softback_end) - s = (u16 *) softback_buf; - if (s == (u16 *) softback_in) - s = (u16 *) vc->vc_origin; - } -} - static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p, int line, int count, int dy) { @@ -1850,31 +1692,6 @@ static void fbcon_redraw(struct vc_data *vc, struct fbcon_display *p, } } -static inline void fbcon_softback_note(struct vc_data *vc, int t, - int count) -{ - unsigned short *p; - - if (vc->vc_num != fg_console) - return; - p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row); - - while (count) { - scr_memcpyw((u16 *) softback_in, p, vc->vc_size_row); - count--; - p = advance_row(p, 1); - softback_in += vc->vc_size_row; - if (softback_in == softback_end) - softback_in = softback_buf; - if (softback_in == softback_top) { - softback_top += vc->vc_size_row; - if (softback_top == softback_end) - softback_top = softback_buf; - } - } - softback_curr = softback_in; -} - static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, enum con_scroll dir, unsigned int count) { @@ -1897,8 +1714,6 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, case SM_UP: if (count > vc->vc_rows) /* Maximum realistic size */ count = vc->vc_rows; - if (softback_top) - fbcon_softback_note(vc, t, count); if (logo_shown >= 0) goto redraw_up; switch (p->scrollmode) { @@ -2269,14 +2084,6 @@ static int fbcon_switch(struct vc_data *vc) info = registered_fb[con2fb_map[vc->vc_num]]; ops = info->fbcon_par; - if (softback_top) { - if (softback_lines) - fbcon_set_origin(vc); - softback_top = softback_curr = softback_in = softback_buf; - softback_lines = 0; - fbcon_update_softback(vc); - } - if (logo_shown >= 0) { struct vc_data *conp2 = vc_cons[logo_shown].d; @@ -2600,9 +2407,6 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int cnt; char *old_data = NULL; - if (con_is_visible(vc) && softback_lines) - fbcon_set_origin(vc); - resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); if (p->userfont) old_data = vc->vc_font.data; @@ -2628,8 +2432,6 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, cols /= w; rows /= h; vc_resize(vc, cols, rows); - if (con_is_visible(vc) && softback_buf) - fbcon_update_softback(vc); } else if (con_is_visible(vc) && vc->vc_mode == KD_TEXT) { fbcon_clear_margins(vc, 0); @@ -2788,19 +2590,7 @@ static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table) static u16 *fbcon_screen_pos(struct vc_data *vc, int offset) { - unsigned long p; - int line; - - if (vc->vc_num != fg_console || !softback_lines) - return (u16 *) (vc->vc_origin + offset); - line = offset / vc->vc_size_row; - if (line >= softback_lines) - return (u16 *) (vc->vc_origin + offset - - softback_lines * vc->vc_size_row); - p = softback_curr + offset; - if (p >= softback_end) - p += softback_buf - softback_end; - return (u16 *) p; + return (u16 *) (vc->vc_origin + offset); } static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos, @@ -2814,22 +2604,7 @@ static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos, x = offset % vc->vc_cols; y = offset / vc->vc_cols; - if (vc->vc_num == fg_console) - y += softback_lines; ret = pos + (vc->vc_cols - x) * 2; - } else if (vc->vc_num == fg_console && softback_lines) { - unsigned long offset = pos - softback_curr; - - if (pos < softback_curr) - offset += softback_end - softback_buf; - offset /= 2; - x = offset % vc->vc_cols; - y = offset / vc->vc_cols; - ret = pos + (vc->vc_cols - x) * 2; - if (ret == softback_end) - ret = softback_buf; - if (ret == softback_in) - ret = vc->vc_origin; } else { /* Should not happen */ x = y = 0; @@ -2857,106 +2632,11 @@ static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt) a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | (((a) & 0x0700) << 4); scr_writew(a, p++); - if (p == (u16 *) softback_end) - p = (u16 *) softback_buf; - if (p == (u16 *) softback_in) - p = (u16 *) vc->vc_origin; - } -} - -static void fbcon_scrolldelta(struct vc_data *vc, int lines) -{ - struct fb_info *info = registered_fb[con2fb_map[fg_console]]; - struct fbcon_ops *ops = info->fbcon_par; - struct fbcon_display *disp = &fb_display[fg_console]; - int offset, limit, scrollback_old; - - if (softback_top) { - if (vc->vc_num != fg_console) - return; - if (vc->vc_mode != KD_TEXT || !lines) - return; - if (logo_shown >= 0) { - struct vc_data *conp2 = vc_cons[logo_shown].d; - - if (conp2->vc_top == logo_lines - && conp2->vc_bottom == conp2->vc_rows) - conp2->vc_top = 0; - if (logo_shown == vc->vc_num) { - unsigned long p, q; - int i; - - p = softback_in; - q = vc->vc_origin + - logo_lines * vc->vc_size_row; - for (i = 0; i < logo_lines; i++) { - if (p == softback_top) - break; - if (p == softback_buf) - p = softback_end; - p -= vc->vc_size_row; - q -= vc->vc_size_row; - scr_memcpyw((u16 *) q, (u16 *) p, - vc->vc_size_row); - } - softback_in = softback_curr = p; - update_region(vc, vc->vc_origin, - logo_lines * vc->vc_cols); - } - logo_shown = FBCON_LOGO_CANSHOW; - } - fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK); - fbcon_redraw_softback(vc, disp, lines); - fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK); - return; } - - if (!scrollback_phys_max) - return; - - scrollback_old = scrollback_current; - scrollback_current -= lines; - if (scrollback_current < 0) - scrollback_current = 0; - else if (scrollback_current > scrollback_max) - scrollback_current = scrollback_max; - if (scrollback_current == scrollback_old) - return; - - if (fbcon_is_inactive(vc, info)) - return; - - fbcon_cursor(vc, CM_ERASE); - - offset = disp->yscroll - scrollback_current; - limit = disp->vrows; - switch (disp->scrollmode) { - case SCROLL_WRAP_MOVE: - info->var.vmode |= FB_VMODE_YWRAP; - break; - case SCROLL_PAN_MOVE: - case SCROLL_PAN_REDRAW: - limit -= vc->vc_rows; - info->var.vmode &= ~FB_VMODE_YWRAP; - break; - } - if (offset < 0) - offset += limit; - else if (offset >= limit) - offset -= limit; - - ops->var.xoffset = 0; - ops->var.yoffset = offset * vc->vc_font.height; - ops->update_start(info); - - if (!scrollback_current) - fbcon_cursor(vc, CM_DRAW); } static int fbcon_set_origin(struct vc_data *vc) { - if (softback_lines) - fbcon_scrolldelta(vc, softback_lines); return 0; } @@ -3020,8 +2700,6 @@ static void fbcon_modechanged(struct fb_info *info) fbcon_set_palette(vc, color_table); update_screen(vc); - if (softback_buf) - fbcon_update_softback(vc); } } @@ -3432,7 +3110,6 @@ static const struct consw fb_con = { .con_font_default = fbcon_set_def_font, .con_font_copy = fbcon_copy_font, .con_set_palette = fbcon_set_palette, - .con_scrolldelta = fbcon_scrolldelta, .con_set_origin = fbcon_set_origin, .con_invert_region = fbcon_invert_region, .con_screen_pos = fbcon_screen_pos, @@ -3667,9 +3344,6 @@ static void fbcon_exit(void) } #endif - kvfree((void *)softback_buf); - softback_buf = 0UL; - for_each_registered_fb(i) { int pending = 0; diff --git a/drivers/video/fbdev/core/fbcon.h b/drivers/video/fbdev/core/fbcon.h index 20dea853765f..78bb14c03643 100644 --- a/drivers/video/fbdev/core/fbcon.h +++ b/drivers/video/fbdev/core/fbcon.h @@ -62,7 +62,7 @@ struct fbcon_ops { void (*clear_margins)(struct vc_data *vc, struct fb_info *info, int color, int bottom_only); void (*cursor)(struct vc_data *vc, struct fb_info *info, int mode, - int softback_lines, int fg, int bg); + int fg, int bg); int (*update_start)(struct fb_info *info); int (*rotate_font)(struct fb_info *info, struct vc_data *vc); struct fb_var_screeninfo var; /* copy of the current fb_var_screeninfo */ diff --git a/drivers/video/fbdev/core/fbcon_ccw.c b/drivers/video/fbdev/core/fbcon_ccw.c index 5b177131e062..bbd869efd03b 100644 --- a/drivers/video/fbdev/core/fbcon_ccw.c +++ b/drivers/video/fbdev/core/fbcon_ccw.c @@ -219,7 +219,7 @@ static void ccw_clear_margins(struct vc_data *vc, struct fb_info *info, } static void ccw_cursor(struct vc_data *vc, struct fb_info *info, int mode, - int softback_lines, int fg, int bg) + int fg, int bg) { struct fb_cursor cursor; struct fbcon_ops *ops = info->fbcon_par; @@ -236,15 +236,6 @@ static void ccw_cursor(struct vc_data *vc, struct fb_info *info, int mode, cursor.set = 0; - if (softback_lines) { - if (y + softback_lines >= vc->vc_rows) { - mode = CM_ERASE; - ops->cursor_flash = 0; - return; - } else - y += softback_lines; - } - c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.width)); diff --git a/drivers/video/fbdev/core/fbcon_cw.c b/drivers/video/fbdev/core/fbcon_cw.c index 894d01a62f30..a34cbe8e9874 100644 --- a/drivers/video/fbdev/core/fbcon_cw.c +++ b/drivers/video/fbdev/core/fbcon_cw.c @@ -202,7 +202,7 @@ static void cw_clear_margins(struct vc_data *vc, struct fb_info *info, } static void cw_cursor(struct vc_data *vc, struct fb_info *info, int mode, - int softback_lines, int fg, int bg) + int fg, int bg) { struct fb_cursor cursor; struct fbcon_ops *ops = info->fbcon_par; @@ -219,15 +219,6 @@ static void cw_cursor(struct vc_data *vc, struct fb_info *info, int mode, cursor.set = 0; - if (softback_lines) { - if (y + softback_lines >= vc->vc_rows) { - mode = CM_ERASE; - ops->cursor_flash = 0; - return; - } else - y += softback_lines; - } - c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.width)); diff --git a/drivers/video/fbdev/core/fbcon_ud.c b/drivers/video/fbdev/core/fbcon_ud.c index 01b87f278d79..199cbc7abe35 100644 --- a/drivers/video/fbdev/core/fbcon_ud.c +++ b/drivers/video/fbdev/core/fbcon_ud.c @@ -249,7 +249,7 @@ static void ud_clear_margins(struct vc_data *vc, struct fb_info *info, } static void ud_cursor(struct vc_data *vc, struct fb_info *info, int mode, - int softback_lines, int fg, int bg) + int fg, int bg) { struct fb_cursor cursor; struct fbcon_ops *ops = info->fbcon_par; @@ -267,15 +267,6 @@ static void ud_cursor(struct vc_data *vc, struct fb_info *info, int mode, cursor.set = 0; - if (softback_lines) { - if (y + softback_lines >= vc->vc_rows) { - mode = CM_ERASE; - ops->cursor_flash = 0; - return; - } else - y += softback_lines; - } - c = scr_readw((u16 *) vc->vc_pos); attribute = get_attribute(info, c); src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.height)); diff --git a/drivers/video/fbdev/core/tileblit.c b/drivers/video/fbdev/core/tileblit.c index 1dfaff0881fb..31b85b71cc37 100644 --- a/drivers/video/fbdev/core/tileblit.c +++ b/drivers/video/fbdev/core/tileblit.c @@ -80,7 +80,7 @@ static void tile_clear_margins(struct vc_data *vc, struct fb_info *info, } static void tile_cursor(struct vc_data *vc, struct fb_info *info, int mode, - int softback_lines, int fg, int bg) + int fg, int bg) { struct fb_tilecursor cursor; int use_sw = vc->vc_cursor_type & CUR_SW; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index ac45f022b495..2d9109d9e98f 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2193,7 +2193,8 @@ static noinline int search_ioctl(struct inode *inode, key.offset = sk->min_offset; while (1) { - ret = fault_in_pages_writeable(ubuf, *buf_size - sk_offset); + ret = fault_in_pages_writeable(ubuf + sk_offset, + *buf_size - sk_offset); if (ret) break; diff --git a/include/dt-bindings/clock/qcom,dispcc-sm8150.h b/include/dt-bindings/clock/qcom,dispcc-sm8150.h new file mode 100644 index 000000000000..d5e8eaeee56f --- /dev/null +++ b/include/dt-bindings/clock/qcom,dispcc-sm8150.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_DISP_CC_SM8150_H +#define _DT_BINDINGS_CLK_QCOM_DISP_CC_SM8150_H + +/* DISP_CC clock registers */ +#define DISP_CC_MDSS_AHB_CLK 0 +#define DISP_CC_MDSS_AHB_CLK_SRC 1 +#define DISP_CC_MDSS_BYTE0_CLK 2 +#define DISP_CC_MDSS_BYTE0_CLK_SRC 3 +#define DISP_CC_MDSS_BYTE0_DIV_CLK_SRC 4 +#define DISP_CC_MDSS_BYTE0_INTF_CLK 5 +#define DISP_CC_MDSS_BYTE1_CLK 6 +#define DISP_CC_MDSS_BYTE1_CLK_SRC 7 +#define DISP_CC_MDSS_BYTE1_DIV_CLK_SRC 8 +#define DISP_CC_MDSS_BYTE1_INTF_CLK 9 +#define DISP_CC_MDSS_DP_AUX1_CLK 10 +#define DISP_CC_MDSS_DP_AUX1_CLK_SRC 11 +#define DISP_CC_MDSS_DP_AUX_CLK 12 +#define DISP_CC_MDSS_DP_AUX_CLK_SRC 13 +#define DISP_CC_MDSS_DP_CRYPTO1_CLK 14 +#define DISP_CC_MDSS_DP_CRYPTO1_CLK_SRC 15 +#define DISP_CC_MDSS_DP_CRYPTO_CLK 16 +#define DISP_CC_MDSS_DP_CRYPTO_CLK_SRC 17 +#define DISP_CC_MDSS_DP_LINK1_CLK 18 +#define DISP_CC_MDSS_DP_LINK1_CLK_SRC 19 +#define DISP_CC_MDSS_DP_LINK1_INTF_CLK 20 +#define DISP_CC_MDSS_DP_LINK_CLK 21 +#define DISP_CC_MDSS_DP_LINK_CLK_SRC 22 +#define DISP_CC_MDSS_DP_LINK_INTF_CLK 23 +#define DISP_CC_MDSS_DP_PIXEL1_CLK 24 +#define DISP_CC_MDSS_DP_PIXEL1_CLK_SRC 25 +#define DISP_CC_MDSS_DP_PIXEL2_CLK 26 +#define DISP_CC_MDSS_DP_PIXEL2_CLK_SRC 27 +#define DISP_CC_MDSS_DP_PIXEL_CLK 28 +#define DISP_CC_MDSS_DP_PIXEL_CLK_SRC 29 +#define DISP_CC_MDSS_EDP_AUX_CLK 30 +#define DISP_CC_MDSS_EDP_AUX_CLK_SRC 31 +#define DISP_CC_MDSS_EDP_GTC_CLK 32 +#define DISP_CC_MDSS_EDP_GTC_CLK_SRC 33 +#define DISP_CC_MDSS_EDP_LINK_CLK 34 +#define DISP_CC_MDSS_EDP_LINK_CLK_SRC 35 +#define DISP_CC_MDSS_EDP_LINK_INTF_CLK 36 +#define DISP_CC_MDSS_EDP_PIXEL_CLK 37 +#define DISP_CC_MDSS_EDP_PIXEL_CLK_SRC 38 +#define DISP_CC_MDSS_ESC0_CLK 39 +#define DISP_CC_MDSS_ESC0_CLK_SRC 40 +#define DISP_CC_MDSS_ESC1_CLK 41 +#define DISP_CC_MDSS_ESC1_CLK_SRC 42 +#define DISP_CC_MDSS_MDP_CLK 43 +#define DISP_CC_MDSS_MDP_CLK_SRC 44 +#define DISP_CC_MDSS_MDP_LUT_CLK 45 +#define DISP_CC_MDSS_NON_GDSC_AHB_CLK 46 +#define DISP_CC_MDSS_PCLK0_CLK 47 +#define DISP_CC_MDSS_PCLK0_CLK_SRC 48 +#define DISP_CC_MDSS_PCLK1_CLK 49 +#define DISP_CC_MDSS_PCLK1_CLK_SRC 50 +#define DISP_CC_MDSS_ROT_CLK 51 +#define DISP_CC_MDSS_ROT_CLK_SRC 52 +#define DISP_CC_MDSS_RSCC_AHB_CLK 53 +#define DISP_CC_MDSS_RSCC_VSYNC_CLK 54 +#define DISP_CC_MDSS_VSYNC_CLK 55 +#define DISP_CC_MDSS_VSYNC_CLK_SRC 56 +#define DISP_CC_PLL0 57 +#define DISP_CC_PLL1 58 +#define DISP_CC_XO_CLK 59 + +/* DISP_CC Reset */ +#define DISP_CC_MDSS_CORE_BCR 0 +#define DISP_CC_MDSS_RSCC_BCR 1 +#define DISP_CC_MDSS_SPDM_BCR 2 + +/* DISP_CC GDSCR */ +#define MDSS_GDSC 0 + +#endif diff --git a/include/dt-bindings/clock/qcom,dispcc-sm8250.h b/include/dt-bindings/clock/qcom,dispcc-sm8250.h new file mode 100644 index 000000000000..02d2174d1c16 --- /dev/null +++ b/include/dt-bindings/clock/qcom,dispcc-sm8250.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_DISP_CC_SM8250_H +#define _DT_BINDINGS_CLK_QCOM_DISP_CC_SM8250_H + +/* DISP_CC clock registers */ +#define DISP_CC_MDSS_AHB_CLK 0 +#define DISP_CC_MDSS_AHB_CLK_SRC 1 +#define DISP_CC_MDSS_BYTE0_CLK 2 +#define DISP_CC_MDSS_BYTE0_CLK_SRC 3 +#define DISP_CC_MDSS_BYTE0_DIV_CLK_SRC 4 +#define DISP_CC_MDSS_BYTE0_INTF_CLK 5 +#define DISP_CC_MDSS_BYTE1_CLK 6 +#define DISP_CC_MDSS_BYTE1_CLK_SRC 7 +#define DISP_CC_MDSS_BYTE1_DIV_CLK_SRC 8 +#define DISP_CC_MDSS_BYTE1_INTF_CLK 9 +#define DISP_CC_MDSS_DP_AUX1_CLK 10 +#define DISP_CC_MDSS_DP_AUX1_CLK_SRC 11 +#define DISP_CC_MDSS_DP_AUX_CLK 12 +#define DISP_CC_MDSS_DP_AUX_CLK_SRC 13 +#define DISP_CC_MDSS_DP_LINK1_CLK 14 +#define DISP_CC_MDSS_DP_LINK1_CLK_SRC 15 +#define DISP_CC_MDSS_DP_LINK1_DIV_CLK_SRC 16 +#define DISP_CC_MDSS_DP_LINK1_INTF_CLK 17 +#define DISP_CC_MDSS_DP_LINK_CLK 18 +#define DISP_CC_MDSS_DP_LINK_CLK_SRC 19 +#define DISP_CC_MDSS_DP_LINK_DIV_CLK_SRC 20 +#define DISP_CC_MDSS_DP_LINK_INTF_CLK 21 +#define DISP_CC_MDSS_DP_PIXEL1_CLK 22 +#define DISP_CC_MDSS_DP_PIXEL1_CLK_SRC 23 +#define DISP_CC_MDSS_DP_PIXEL2_CLK 24 +#define DISP_CC_MDSS_DP_PIXEL2_CLK_SRC 25 +#define DISP_CC_MDSS_DP_PIXEL_CLK 26 +#define DISP_CC_MDSS_DP_PIXEL_CLK_SRC 27 +#define DISP_CC_MDSS_EDP_AUX_CLK 28 +#define DISP_CC_MDSS_EDP_AUX_CLK_SRC 29 +#define DISP_CC_MDSS_EDP_GTC_CLK 30 +#define DISP_CC_MDSS_EDP_GTC_CLK_SRC 31 +#define DISP_CC_MDSS_EDP_LINK_CLK 32 +#define DISP_CC_MDSS_EDP_LINK_CLK_SRC 33 +#define DISP_CC_MDSS_EDP_LINK_DIV_CLK_SRC 34 +#define DISP_CC_MDSS_EDP_LINK_INTF_CLK 35 +#define DISP_CC_MDSS_EDP_PIXEL_CLK 36 +#define DISP_CC_MDSS_EDP_PIXEL_CLK_SRC 37 +#define DISP_CC_MDSS_ESC0_CLK 38 +#define DISP_CC_MDSS_ESC0_CLK_SRC 39 +#define DISP_CC_MDSS_ESC1_CLK 40 +#define DISP_CC_MDSS_ESC1_CLK_SRC 41 +#define DISP_CC_MDSS_MDP_CLK 42 +#define DISP_CC_MDSS_MDP_CLK_SRC 43 +#define DISP_CC_MDSS_MDP_LUT_CLK 44 +#define DISP_CC_MDSS_NON_GDSC_AHB_CLK 45 +#define DISP_CC_MDSS_PCLK0_CLK 46 +#define DISP_CC_MDSS_PCLK0_CLK_SRC 47 +#define DISP_CC_MDSS_PCLK1_CLK 48 +#define DISP_CC_MDSS_PCLK1_CLK_SRC 49 +#define DISP_CC_MDSS_ROT_CLK 50 +#define DISP_CC_MDSS_ROT_CLK_SRC 51 +#define DISP_CC_MDSS_RSCC_AHB_CLK 52 +#define DISP_CC_MDSS_RSCC_VSYNC_CLK 53 +#define DISP_CC_MDSS_VSYNC_CLK 54 +#define DISP_CC_MDSS_VSYNC_CLK_SRC 55 +#define DISP_CC_PLL0 56 +#define DISP_CC_PLL1 57 +#define DISP_CC_SLEEP_CLK 58 +#define DISP_CC_SLEEP_CLK_SRC 59 +#define DISP_CC_XO_CLK 60 + +/* DISP_CC Reset */ +#define DISP_CC_MDSS_CORE_BCR 0 +#define DISP_CC_MDSS_RSCC_BCR 1 + +/* DISP_CC GDSCR */ +#define MDSS_GDSC 0 + +#endif diff --git a/include/dt-bindings/dma/qcom-gpi.h b/include/dt-bindings/dma/qcom-gpi.h new file mode 100644 index 000000000000..71f79eb7614c --- /dev/null +++ b/include/dt-bindings/dma/qcom-gpi.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2020, Linaro Ltd. */ + +#ifndef __DT_BINDINGS_DMA_QCOM_GPI_H__ +#define __DT_BINDINGS_DMA_QCOM_GPI_H__ + +#define QCOM_GPI_SPI 1 +#define QCOM_GPI_UART 2 +#define QCOM_GPI_I2C 3 + +#endif /* __DT_BINDINGS_DMA_QCOM_GPI_H__ */ diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 6fbd5c99e30c..89e0fe8e0b1c 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -381,6 +381,93 @@ enum dma_slave_buswidth { }; /** + * enum spi_transfer_cmd - spi transfer commands + */ +enum spi_transfer_cmd { + SPI_TX = 1, + SPI_RX, + SPI_DUPLEX, +}; + +/** + * struct dmaengine_spi_config - spi config for peripheral + * + * @loopback_en: spi loopback enable when set + * @clock_pol: clock polarity + * @data_pol: data polarity + * @pack_en: process tx/rx buffers as packed + * @word_len: spi word length + * @clk_div: source clock divider + * @clk_src: serial clock + * @cmd: spi cmd + * @cs: chip select toggle + */ +struct dmaengine_spi_config { + u8 loopback_en; + u8 clock_pol; + u8 data_pol; + u8 pack_en; + u8 word_len; + u32 clk_div; + u32 clk_src; + u8 fragmentation; + enum spi_transfer_cmd cmd; + u8 cs; +}; + +enum i2c_op { + I2C_WRITE = 1, + I2C_READ, +}; + +/** + * struct dmaengine_i2c_config - i2c config for peripheral + * + * @pack_enable: process tx/rx buffers as packed + * @cycle_count: clock cycles to be sent + * @high_count: high period of clock + * @low_count: low period of clock + * @clk_div: source clock divider + * @addr: i2c bus address + * @stretch: stretch the clock at eot + * @op: i2c cmd + */ +struct dmaengine_i2c_config { + u8 pack_enable; + u8 cycle_count; + u8 high_count; + u8 low_count; + u16 clk_div; + u8 addr; + u8 stretch; + enum i2c_op op; +}; + +enum dmaengine_peripheral { + DMAENGINE_PERIPHERAL_SPI = 1, + DMAENGINE_PERIPHERAL_UART = 2, + DMAENGINE_PERIPHERAL_I2C = 3, + DMAENGINE_PERIPHERAL_LAST = DMAENGINE_PERIPHERAL_I2C, +}; + +/** + * struct dmaengine_peripheral_config - peripheral configuration for + * dmaengine peripherals + * + * @peripheral: type of peripheral to DMA to/from + * @set_config: set peripheral config + * @rx_len: receive length for buffer + * @spi: peripheral config for spi + * @i2c: peripheral config for i2c + */ +struct dmaengine_peripheral_config { + enum dmaengine_peripheral peripheral; + u8 set_config; + u32 rx_len; + struct dmaengine_spi_config spi; + struct dmaengine_i2c_config i2c; +}; +/** * struct dma_slave_config - dma slave channel runtime config * @direction: whether the data shall go in or out on this slave * channel, right now. DMA_MEM_TO_DEV and DMA_DEV_TO_MEM are @@ -418,6 +505,8 @@ enum dma_slave_buswidth { * @slave_id: Slave requester id. Only valid for slave channels. The dma * slave peripheral will have unique id as dma requester which need to be * pass as slave config. + * @peripheral: peripheral configuration for programming peripheral for + * dmaengine transfer * * This struct is passed in as configuration data to a DMA engine * in order to set up a certain channel for DMA transport at runtime. @@ -443,6 +532,7 @@ struct dma_slave_config { u32 dst_port_window_size; bool device_fc; unsigned int slave_id; + struct dmaengine_peripheral_config *peripheral; }; /** diff --git a/include/linux/fixp-arith.h b/include/linux/fixp-arith.h index 8396013785ef..4c1f3528599d 100644 --- a/include/linux/fixp-arith.h +++ b/include/linux/fixp-arith.h @@ -141,4 +141,24 @@ static inline s32 fixp_sin32_rad(u32 radians, u32 twopi) #define fixp_cos32_rad(rad, twopi) \ fixp_sin32_rad(rad + twopi / 4, twopi) + +/** + * fixp_linear_interpolate() - interpolates a value from two known points + * + * @x0: x value of point 0 + * @y0: y value of point 0 + * @x1: x value of point 1 + * @y1: y value of point 1 + * @x: the linear interpolant + */ +static inline int fixp_linear_interpolate(int x0, int y0, int x1, int y1, int x) +{ + if (y0 == y1 || x == x0) + return y0; + if (x1 == x0 || x == x1) + return y1; + + return y0 + ((y1 - y0) * (x - x0) / (x1 - x0)); +} + #endif diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h index 8f385fbe5a0e..5ef4455e6b24 100644 --- a/include/linux/qcom-geni-se.h +++ b/include/linux/qcom-geni-se.h @@ -12,6 +12,7 @@ enum geni_se_xfer_mode { GENI_SE_INVALID, GENI_SE_FIFO, + GENI_GPI_DMA, GENI_SE_DMA, }; @@ -67,6 +68,7 @@ struct geni_se { #define SE_GENI_STATUS 0x40 #define GENI_SER_M_CLK_CFG 0x48 #define GENI_SER_S_CLK_CFG 0x4c +#define GENI_IF_DISABLE_RO 0x64 #define GENI_FW_REVISION_RO 0x68 #define SE_GENI_CLK_SEL 0x7c #define SE_GENI_DMA_MODE_EN 0x258 @@ -109,6 +111,9 @@ struct geni_se { #define CLK_DIV_MSK GENMASK(15, 4) #define CLK_DIV_SHFT 4 +/* GENI_IF_DISABLE_RO fields */ +#define FIFO_IF_DISABLE (BIT(0)) + /* GENI_FW_REVISION_RO fields */ #define FW_REV_PROTOCOL_MSK GENMASK(15, 8) #define FW_REV_PROTOCOL_SHFT 8 diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 2e1193a3fb5f..f455a66e4f1b 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -88,7 +88,9 @@ extern int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, unsigned int *src, const struct qcom_scm_vmperm *newvm, unsigned int dest_cnt); - +extern int qcom_scm_mem_protect_video_var(u32 cp_start, u32 cp_size, + u32 cp_nonpixel_start, + u32 cp_nonpixel_size); extern bool qcom_scm_ocmem_lock_available(void); extern int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, u32 size, u32 mode); @@ -144,6 +146,10 @@ static inline int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) static inline int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, unsigned int *src, const struct qcom_scm_vmperm *newvm, unsigned int dest_cnt) { return -ENODEV; } +extern int qcom_scm_mem_protect_video_var(u32 cp_start, u32 cp_size, + u32 cp_nonpixel_start, + u32 cp_nonpixel_size) + { return -ENODEV }; static inline bool qcom_scm_ocmem_lock_available(void) { return false; } static inline int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, diff --git a/include/linux/soc/qcom/llcc-qcom.h b/include/linux/soc/qcom/llcc-qcom.h index 90b864655822..fe4b6dc830b1 100644 --- a/include/linux/soc/qcom/llcc-qcom.h +++ b/include/linux/soc/qcom/llcc-qcom.h @@ -26,6 +26,15 @@ #define LLCC_MDMHPFX 20 #define LLCC_MDMPNG 21 #define LLCC_AUDHW 22 +#define LLCC_NPU 23 +#define LLCC_WLNHW 24 +#define LLCC_PIMEM 25 +#define LLCC_DISPVG 27 +#define LLCC_CVP 28 +#define LLCC_MODEMVPE 29 +#define LLCC_APTCM 30 +#define LLCC_WRTCH 31 +#define LLCC_CVPFW 32 /** * llcc_slice_desc - Cache slice descriptor |