diff options
| author | Paolo Abeni <pabeni@redhat.com> | 2026-07-03 07:42:04 +0200 |
|---|---|---|
| committer | Paolo Abeni <pabeni@redhat.com> | 2026-07-03 07:46:27 +0200 |
| commit | 2bb62a85aff6d4c14a62a476dfabaada3c1cc014 (patch) | |
| tree | 89fe2bb7d5d671e24077d57a276c08c48561ab27 | |
| parent | 08bc5b2636afcbadc31bb17243eec094e048bd79 (diff) | |
| parent | 87320be9f0d24fce67631b7eef919f0b79c3e45c (diff) | |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Cross-merge networking fixes after downstream PR (net-7.2-rc2).
No conflicts.
Adjacent changes:
MAINTAINERS:
56114690ff3c ("MAINTAINERS: Update Marvell octeontx2 driver maintainers")
eb56577ae9a5 ("ehea: remove the ehea driver")
net/core/netpoll.c:
45f1458a8501 ("netpoll: fix a use-after-free on shutdown path")
84c0ff1efb62 ("netpoll: do not warn when the best-effort pool refill fails")
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
526 files changed, 22500 insertions, 3496 deletions
@@ -373,6 +373,7 @@ Jarkko Sakkinen <jarkko@kernel.org> <jarkko.sakkinen@opinsys.com> Jason Gunthorpe <jgg@ziepe.ca> <jgg@mellanox.com> Jason Gunthorpe <jgg@ziepe.ca> <jgg@nvidia.com> Jason Gunthorpe <jgg@ziepe.ca> <jgunthorpe@obsidianresearch.com> +Jason Wang <jasowangio@gmail.com> <jasowang@redhat.com> Jason Xing <kerneljasonxing@gmail.com> <kernelxing@tencent.com> <javier@osg.samsung.com> <javier.martinez@collabora.co.uk> Javi Merino <javi.merino@kernel.org> <javi.merino@arm.com> diff --git a/Documentation/devicetree/bindings/clock/amlogic,t7-peripherals-clkc.yaml b/Documentation/devicetree/bindings/clock/amlogic,t7-peripherals-clkc.yaml index 55bb73707d58..a4b214a941ea 100644 --- a/Documentation/devicetree/bindings/clock/amlogic,t7-peripherals-clkc.yaml +++ b/Documentation/devicetree/bindings/clock/amlogic,t7-peripherals-clkc.yaml @@ -24,7 +24,7 @@ properties: const: 1 clocks: - minItems: 14 + minItems: 15 items: - description: input oscillator - description: input sys clk @@ -40,12 +40,13 @@ properties: - description: input gp1 pll - description: input mpll1 - description: input mpll2 + - description: input mpll3 - description: external input rmii oscillator (optional) - description: input video pll0 (optional) - description: external pad input for rtc (optional) clock-names: - minItems: 14 + minItems: 15 items: - const: xtal - const: sys @@ -61,6 +62,7 @@ properties: - const: gp1 - const: mpll1 - const: mpll2 + - const: mpll3 - const: ext_rmii - const: vid_pll0 - const: ext_rtc @@ -97,7 +99,8 @@ examples: <&gp0 1>, <&gp1 1>, <&mpll 4>, - <&mpll 6>; + <&mpll 6>, + <&mpll 8>; clock-names = "xtal", "sys", "fix", @@ -111,6 +114,7 @@ examples: "gp0", "gp1", "mpll1", - "mpll2"; + "mpll2", + "mpll3"; }; }; diff --git a/Documentation/devicetree/bindings/clock/amlogic,t7-pll-clkc.yaml b/Documentation/devicetree/bindings/clock/amlogic,t7-pll-clkc.yaml index 49c61f65deff..b488d92b7984 100644 --- a/Documentation/devicetree/bindings/clock/amlogic,t7-pll-clkc.yaml +++ b/Documentation/devicetree/bindings/clock/amlogic,t7-pll-clkc.yaml @@ -72,7 +72,7 @@ allOf: contains: enum: - amlogic,t7-gp0-pll - - amlogic,t7-gp1--pll + - amlogic,t7-gp1-pll - amlogic,t7-hifi-pll - amlogic,t7-pcie-pll - amlogic,t7-mpll diff --git a/Documentation/devicetree/bindings/clock/canaan,k230-clk.yaml b/Documentation/devicetree/bindings/clock/canaan,k230-clk.yaml new file mode 100644 index 000000000000..34c93cb5db40 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/canaan,k230-clk.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/canaan,k230-clk.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Canaan Kendryte K230 Clock + +maintainers: + - Xukai Wang <kingxukai@zohomail.com> + +description: + The Canaan K230 clock controller generates various clocks for SoC + peripherals. See include/dt-bindings/clock/canaan,k230-clk.h for + valid clock IDs. + +properties: + compatible: + const: canaan,k230-clk + + reg: + items: + - description: PLL control registers + - description: Sysclk control registers + + clocks: + items: + - description: Main external reference clock + - description: + External clock which used as the pulse input + for the timer to provide timing signals. + + clock-names: + items: + - const: osc24m + - const: timer-pulse-in + + '#clock-cells': + const: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - '#clock-cells' + +additionalProperties: false + +examples: + - | + clock-controller@91102000 { + compatible = "canaan,k230-clk"; + reg = <0x91102000 0x40>, + <0x91100000 0x108>; + clocks = <&osc24m>, <&timerx_pulse_in>; + clock-names = "osc24m", "timer-pulse-in"; + #clock-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/clock/marvell,pxa1908.yaml b/Documentation/devicetree/bindings/clock/marvell,pxa1908.yaml index 6f3a8578fe2a..0db5504013d5 100644 --- a/Documentation/devicetree/bindings/clock/marvell,pxa1908.yaml +++ b/Documentation/devicetree/bindings/clock/marvell,pxa1908.yaml @@ -37,6 +37,9 @@ properties: '#power-domain-cells': const: 1 + '#reset-cells': + const: 1 + required: - compatible - reg @@ -44,16 +47,27 @@ required: additionalProperties: false -if: - not: - properties: - compatible: - contains: - const: marvell,pxa1908-apmu - -then: - properties: - '#power-domain-cells': false +allOf: + - if: + not: + properties: + compatible: + contains: + const: marvell,pxa1908-apmu + then: + properties: + '#power-domain-cells': false + - if: + not: + properties: + compatible: + contains: + enum: + - marvell,pxa1908-apbc + - marvell,pxa1908-apbcp + then: + properties: + '#reset-cells': false examples: # APMU block: diff --git a/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml b/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml index ef2b1e204430..a177a1934b19 100644 --- a/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,dispcc-sm6125.yaml @@ -42,12 +42,6 @@ properties: - const: cfg_ahb_clk - const: gcc_disp_gpll0_div_clk_src - '#clock-cells': - const: 1 - - '#power-domain-cells': - const: 1 - power-domains: description: A phandle and PM domain specifier for the CX power domain. @@ -58,18 +52,16 @@ properties: A phandle to an OPP node describing the power domain's performance point. maxItems: 1 - reg: - maxItems: 1 - required: - compatible - - reg - clocks - clock-names - - '#clock-cells' - '#power-domain-cells' -additionalProperties: false +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false examples: - | @@ -101,6 +93,7 @@ examples: power-domains = <&rpmpd SM6125_VDDCX>; #clock-cells = <1>; + #reset-cells = <1>; #power-domain-cells = <1>; }; ... diff --git a/Documentation/devicetree/bindings/clock/qcom,hawi-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,hawi-gcc.yaml new file mode 100644 index 000000000000..4f428c0f7286 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,hawi-gcc.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,hawi-gcc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Global Clock & Reset Controller on Hawi + +maintainers: + - Vivek Aknurwar <vivek.aknurwar@oss.qualcomm.com> + +description: | + Qualcomm global clock control module provides the clocks, resets and power + domains on Hawi. + + See also: include/dt-bindings/clock/qcom,hawi-gcc.h + +properties: + compatible: + const: qcom,hawi-gcc + + clocks: + items: + - description: Board XO source + - description: Board Always On XO source + - description: Sleep clock source + - description: PCIE 0 Pipe clock source + - description: PCIE 1 Pipe clock source + - description: UFS PHY RX symbol 0 clock + - description: UFS PHY RX symbol 1 clock + - description: UFS PHY TX symbol 0 clock + - description: USB3 PHY wrapper pipe clock + +required: + - compatible + - clocks + - '#power-domain-cells' + +allOf: + - $ref: qcom,gcc.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/qcom,rpmh.h> + clock-controller@100000 { + compatible = "qcom,hawi-gcc"; + reg = <0x00100000 0x1f4200>; + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&rpmhcc RPMH_CXO_CLK_A>, + <&sleep_clk>, + <&pcie0_phy>, + <&pcie1_phy>, + <&ufs_mem_phy 0>, + <&ufs_mem_phy 1>, + <&ufs_mem_phy 2>, + <&usb_1_qmpphy>; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; +... diff --git a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml index 8cb86d74e489..a4f9af8fa187 100644 --- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-cmn-pll.yaml @@ -25,6 +25,7 @@ properties: compatible: enum: - qcom,ipq5018-cmn-pll + - qcom,ipq5332-cmn-pll - qcom,ipq5424-cmn-pll - qcom,ipq6018-cmn-pll - qcom,ipq8074-cmn-pll diff --git a/Documentation/devicetree/bindings/clock/qcom,kaanapali-gxclkctl.yaml b/Documentation/devicetree/bindings/clock/qcom,kaanapali-gxclkctl.yaml index 466c884aa2ba..e868963f659b 100644 --- a/Documentation/devicetree/bindings/clock/qcom,kaanapali-gxclkctl.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,kaanapali-gxclkctl.yaml @@ -44,7 +44,7 @@ required: - power-domains - '#power-domain-cells' -unevaluatedProperties: false +additionalProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/clock/qcom,milos-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,milos-camcc.yaml index f63149ecf3e1..707b25d2c11e 100644 --- a/Documentation/devicetree/bindings/clock/qcom,milos-camcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,milos-camcc.yaml @@ -25,6 +25,10 @@ properties: - description: Sleep clock source - description: Camera AHB clock from GCC + interconnects: + items: + - description: Interconnect path to enable the MultiMedia NoC + required: - compatible - clocks @@ -37,12 +41,16 @@ unevaluatedProperties: false examples: - | #include <dt-bindings/clock/qcom,milos-gcc.h> + #include <dt-bindings/interconnect/qcom,icc.h> + #include <dt-bindings/interconnect/qcom,milos-rpmh.h> clock-controller@adb0000 { compatible = "qcom,milos-camcc"; reg = <0x0adb0000 0x40000>; clocks = <&bi_tcxo_div2>, <&sleep_clk>, <&gcc GCC_CAMERA_AHB_CLK>; + interconnects = <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS + &mmss_noc SLAVE_MNOC_HF_MEM_NOC QCOM_ICC_TAG_ALWAYS>; #clock-cells = <1>; #reset-cells = <1>; #power-domain-cells = <1>; diff --git a/Documentation/devicetree/bindings/clock/qcom,milos-gxclkctl.yaml b/Documentation/devicetree/bindings/clock/qcom,milos-gxclkctl.yaml new file mode 100644 index 000000000000..fbcb5d3f3e3d --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,milos-gxclkctl.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,milos-gxclkctl.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Graphics Power Domain Controller on Milos + +maintainers: + - Luca Weiss <luca.weiss@fairphone.com> + +description: | + Qualcomm GX(graphics) is a clock controller which has PLLs, clocks and + Power domains (GDSC). This module provides the power domains control + of gxclkctl on Qualcomm SoCs which helps the recovery of Graphics subsystem. + + See also: + include/dt-bindings/clock/qcom,kaanapali-gxclkctl.h + +properties: + compatible: + enum: + - qcom,milos-gxclkctl + + reg: + maxItems: 1 + + power-domains: + description: + Power domains required for the clock controller to operate + items: + - description: GFX power domain + - description: GPUCC(CX) power domain + + '#power-domain-cells': + const: 1 + +required: + - compatible + - reg + - power-domains + - '#power-domain-cells' + +additionalProperties: false + +examples: + - | + #include <dt-bindings/power/qcom,rpmhpd.h> + soc { + #address-cells = <2>; + #size-cells = <2>; + + clock-controller@3d64000 { + compatible = "qcom,milos-gxclkctl"; + reg = <0x0 0x03d64000 0x0 0x6000>; + power-domains = <&rpmhpd RPMHPD_GFX>, + <&gpucc 0>; + #power-domain-cells = <1>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml b/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml index a2c404a57981..d344b3386042 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml @@ -19,6 +19,7 @@ properties: enum: - qcom,eliza-rpmh-clk - qcom,glymur-rpmh-clk + - qcom,hawi-rpmh-clk - qcom,kaanapali-rpmh-clk - qcom,milos-rpmh-clk - qcom,nord-rpmh-clk diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml index 7bbf120d928c..5d77029bfaf8 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8450-videocc.yaml @@ -20,6 +20,7 @@ description: | include/dt-bindings/clock/qcom,sm8450-videocc.h include/dt-bindings/clock/qcom,sm8650-videocc.h include/dt-bindings/clock/qcom,sm8750-videocc.h + include/dt-bindings/clock/qcom,x1p42100-videocc.h properties: compatible: @@ -32,6 +33,7 @@ properties: - qcom,sm8650-videocc - qcom,sm8750-videocc - qcom,x1e80100-videocc + - qcom,x1p42100-videocc clocks: items: @@ -70,6 +72,7 @@ allOf: - qcom,sm8450-videocc - qcom,sm8550-videocc - qcom,sm8750-videocc + - qcom,x1p42100-videocc then: required: - required-opps diff --git a/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml b/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml index 1ccdf4b0f5dd..08824f848973 100644 --- a/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,sm8550-tcsr.yaml @@ -17,6 +17,7 @@ description: | See also: - include/dt-bindings/clock/qcom,eliza-tcsr.h - include/dt-bindings/clock/qcom,glymur-tcsr.h + - include/dt-bindings/clock/qcom,hawi-tcsrcc.h - include/dt-bindings/clock/qcom,nord-tcsrcc.h - include/dt-bindings/clock/qcom,sm8550-tcsr.h - include/dt-bindings/clock/qcom,sm8650-tcsr.h @@ -28,6 +29,7 @@ properties: - enum: - qcom,eliza-tcsr - qcom,glymur-tcsr + - qcom,hawi-tcsrcc - qcom,kaanapali-tcsr - qcom,milos-tcsr - qcom,nord-tcsrcc diff --git a/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml b/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml index 938a2f1ff3fc..b28614186cc0 100644 --- a/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,x1e80100-camcc.yaml @@ -23,6 +23,7 @@ properties: compatible: enum: - qcom,x1e80100-camcc + - qcom,x1p42100-camcc reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clock.yaml b/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clock.yaml index 2197c952e21d..b6ee8c8efd46 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clock.yaml +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clock.yaml @@ -60,7 +60,7 @@ examples: clock-output-names = "main", "pll0", "pll1", "pll2", "pll2s", "pll2h", "z", "z2", "i", "m3", "b", "m1", "m2", - "zx", "zs", "hp"; + "zx", "zs", "hp", "ztr", "zt"; }; sdhi2_clk: sdhi2_clk@e615007c { diff --git a/Documentation/devicetree/bindings/mfd/khadas,mcu.yaml b/Documentation/devicetree/bindings/mfd/khadas,mcu.yaml index 1f135618e3b6..c6f91e7bc8aa 100644 --- a/Documentation/devicetree/bindings/mfd/khadas,mcu.yaml +++ b/Documentation/devicetree/bindings/mfd/khadas,mcu.yaml @@ -28,7 +28,6 @@ properties: fan-supply: description: Phandle to the regulator that powers the fan. - $ref: /schemas/types.yaml#/definitions/phandle required: - compatible diff --git a/Documentation/devicetree/bindings/regulator/sprd,sc2730-regulator.yaml b/Documentation/devicetree/bindings/regulator/sprd,sc2730-regulator.yaml new file mode 100644 index 000000000000..ab945c46b08e --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/sprd,sc2730-regulator.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/sprd,sc2730-regulator.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Unisoc SC2730 Power Management IC regulators + +maintainers: + - Otto Pflüger <otto.pflueger@abscue.de> + +patternProperties: + "^dcdc-(core|cpu|gen[0-1]|gpu|mem|memq|modem|sram)$": + type: object + $ref: regulator.yaml# + unevaluatedProperties: false + + "^ldo-avdd(12|18)$": + type: object + $ref: regulator.yaml# + unevaluatedProperties: false + + "^ldo-vdd(18-dcxo|28)$": + type: object + $ref: regulator.yaml# + unevaluatedProperties: false + + "^ldo-vdd(emmccore|kpled|ldo[0-2]|sd(core|io)|sim[0-2]|usb33|wcn|wifipa)$": + type: object + $ref: regulator.yaml# + unevaluatedProperties: false + + "^ldo-vddcam(a0|a1|d0|d1|io|mot)$": + type: object + $ref: regulator.yaml# + unevaluatedProperties: false + + "^ldo-vddrf(1v25|18)$": + type: object + $ref: regulator.yaml# + unevaluatedProperties: false + +additionalProperties: false +... diff --git a/Documentation/devicetree/bindings/rtc/epson,rx6110.txt b/Documentation/devicetree/bindings/rtc/epson,rx6110.txt deleted file mode 100644 index 3dc313e01f77..000000000000 --- a/Documentation/devicetree/bindings/rtc/epson,rx6110.txt +++ /dev/null @@ -1,39 +0,0 @@ -Epson RX6110 Real Time Clock -============================ - -The Epson RX6110 can be used with SPI or I2C busses. The kind of -bus depends on the SPISEL pin and can not be configured via software. - -I2C mode --------- - -Required properties: - - compatible: should be: "epson,rx6110" - - reg : the I2C address of the device for I2C - -Example: - - rtc: rtc@32 { - compatible = "epson,rx6110" - reg = <0x32>; - }; - -SPI mode --------- - -Required properties: - - compatible: should be: "epson,rx6110" - - reg: chip select number - - spi-cs-high: RX6110 needs chipselect high - - spi-cpha: RX6110 works with SPI shifted clock phase - - spi-cpol: RX6110 works with SPI inverse clock polarity - -Example: - - rtc: rtc@3 { - compatible = "epson,rx6110" - reg = <3> - spi-cs-high; - spi-cpha; - spi-cpol; - }; diff --git a/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml new file mode 100644 index 000000000000..55086ac7d1e2 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/epson,rx6110.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/epson,rx6110.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Epson RX6110 Real Time Clock + +description: + The Epson RX6110 can be used with SPI or I2C busses. The kind of bus depends + on the SPISEL pin and cannot be configured via software. + +maintainers: + - Alexandre Belloni <alexandre.belloni@bootlin.com> + +allOf: + - $ref: rtc.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + const: epson,rx6110 + + reg: + maxItems: 1 + + spi-cs-high: true + spi-cpha: true + spi-cpol: true + +required: + - compatible + - reg + +dependencies: + spi-cs-high: [ spi-cpha, spi-cpol ] + spi-cpha: [ spi-cs-high, spi-cpol ] + spi-cpol: [ spi-cs-high, spi-cpha ] + +unevaluatedProperties: false + +examples: + # I2C mode + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + rtc@32 { + compatible = "epson,rx6110"; + reg = <0x32>; + }; + }; + + # SPI mode + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + rtc@3 { + compatible = "epson,rx6110"; + reg = <3>; + spi-cs-high; + spi-cpha; + spi-cpol; + }; + }; diff --git a/Documentation/devicetree/bindings/rtc/rtc-ds1307.yaml b/Documentation/devicetree/bindings/rtc/rtc-ds1307.yaml index 98d10e680144..9b2796804f07 100644 --- a/Documentation/devicetree/bindings/rtc/rtc-ds1307.yaml +++ b/Documentation/devicetree/bindings/rtc/rtc-ds1307.yaml @@ -31,6 +31,7 @@ properties: - epson,rx8025 - isil,isl12057 - epson,rx8130 + - epson,rx8901 - items: - enum: diff --git a/Documentation/devicetree/bindings/rtc/st,m41t93.yaml b/Documentation/devicetree/bindings/rtc/st,m41t93.yaml new file mode 100644 index 000000000000..bdd995c5c1f4 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/st,m41t93.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/st,m41t93.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ST M41T93 RTC and compatible + +maintainers: + - Akhilesh Patil <akhilesh@ee.iitb.ac.in> + +description: + ST M41T93 is spi based Real Time Clock (RTC) with time, date, + alarm, watchdog, square wave clock output, 8 bit timer and + 7 bytes of user SRAM. + +properties: + compatible: + enum: + - st,m41t93 + + reg: + maxItems: 1 + + "#clock-cells": + const: 0 + +required: + - compatible + - reg + +allOf: + - $ref: rtc.yaml + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + rtc@0 { + compatible = "st,m41t93"; + reg = <0>; + #clock-cells = <0>; + spi-max-frequency = <2000000>; + }; + }; + diff --git a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml index 722176c831aa..f4d0eed98a08 100644 --- a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml @@ -30,6 +30,8 @@ properties: - aspeed,ast2500-rtc # ASPEED BMC ast2600 Real-time Clock - aspeed,ast2600-rtc + # ASPEED BMC ast2700 Real-time Clock + - aspeed,ast2700-rtc # Conexant Digicolor Real Time Clock Controller - cnxt,cx92755-rtc # I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output diff --git a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml index 8ebebcebca16..4458316326fc 100644 --- a/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml +++ b/Documentation/devicetree/bindings/spi/snps,dw-apb-ssi.yaml @@ -54,6 +54,12 @@ properties: - sophgo,sg2042-spi - thead,th1520-spi - const: snps,dw-apb-ssi + - description: Vendor controllers which use snps,dwc-ssi-2.00a as fallback + items: + - enum: + - starfive,jhb100-spi + - const: snps,dwc-ssi-2.00a + - const: snps,dwc-ssi-1.01a - description: Intel Keem Bay SPI Controller const: intel,keembay-ssi - description: Intel Mount Evans Integrated Management Complex SPI Controller diff --git a/Documentation/devicetree/bindings/spmi/qcom,glymur-spmi-pmic-arb.yaml b/Documentation/devicetree/bindings/spmi/qcom,glymur-spmi-pmic-arb.yaml index 3b5005b96c6d..1593a1183a36 100644 --- a/Documentation/devicetree/bindings/spmi/qcom,glymur-spmi-pmic-arb.yaml +++ b/Documentation/devicetree/bindings/spmi/qcom,glymur-spmi-pmic-arb.yaml @@ -25,6 +25,7 @@ properties: oneOf: - items: - enum: + - qcom,hawi-spmi-pmic-arb - qcom,kaanapali-spmi-pmic-arb - const: qcom,glymur-spmi-pmic-arb - enum: diff --git a/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml b/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml index e28612510d67..8cfa44dcda58 100644 --- a/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/amlogic,thermal.yaml @@ -87,9 +87,12 @@ examples: amlogic,ao-secure = <&sec_AO>; }; - | + #include <dt-bindings/clock/amlogic,t7-peripherals-clkc.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + temperature-sensor@20000 { compatible = "amlogic,t7-thermal"; - reg = <0x0 0x20000 0x0 0x50>; + reg = <0x20000 0x50>; interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clkc_periphs CLKID_TS>; #thermal-sensor-cells = <0>; diff --git a/Documentation/features/core/thread-info-in-task/arch-support.txt b/Documentation/features/core/thread-info-in-task/arch-support.txt index f3d744c76061..e26efdfbb6b4 100644 --- a/Documentation/features/core/thread-info-in-task/arch-support.txt +++ b/Documentation/features/core/thread-info-in-task/arch-support.txt @@ -12,7 +12,7 @@ | arm64: | ok | | csky: | TODO | | hexagon: | TODO | - | loongarch: | TODO | + | loongarch: | ok | | m68k: | TODO | | microblaze: | TODO | | mips: | TODO | diff --git a/Documentation/hwmon/ltc4283.rst b/Documentation/hwmon/ltc4283.rst index a650c595bc8f..99b1ee05f629 100644 --- a/Documentation/hwmon/ltc4283.rst +++ b/Documentation/hwmon/ltc4283.rst @@ -256,7 +256,7 @@ these logs can be cleared by writing in the proper reset_history attribute. ``/sys/kernel/debug/i2c/i2c-[X]/[X]-addr/`` contains the following attributes: -======================= ========================================== +============================== ========================================================== power1_failed_fault_log Set to 1 by a power1 fault occurring. power1_good_input_fault_log Set to 1 by a power1 good input fault occurring at PGIO3. in11_fet_short_fault_log Set to 1 when a FET-short fault occurs. @@ -264,4 +264,4 @@ in11_fet_bad_fault_log Set to 1 when a FET-BAD fault occurs. in0_lcrit_fault_log Set to 1 by a VIN undervoltage fault occurring. in0_crit_fault_log Set to 1 by a VIN overvoltage fault occurring. curr1_crit_fault_log Set to 1 by an overcurrent fault occurring. -======================= ========================================== +============================== ========================================================== diff --git a/MAINTAINERS b/MAINTAINERS index ee4ee7b8e947..52f1a55eca99 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15733,8 +15733,8 @@ F: drivers/net/ethernet/marvell/octeon_ep_vf MARVELL OCTEONTX2 PHYSICAL FUNCTION DRIVER M: Sunil Goutham <sgoutham@marvell.com> M: Geetha sowjanya <gakula@marvell.com> +M: Ratheesh Kannoth <rkannoth@marvell.com> M: Subbaraya Sundeep <sbhatta@marvell.com> -M: hariprasad <hkelam@marvell.com> M: Bharat Bhushan <bbhushan2@marvell.com> L: netdev@vger.kernel.org S: Maintained @@ -15743,9 +15743,8 @@ F: include/linux/soc/marvell/octeontx2/ MARVELL OCTEONTX2 RVU ADMIN FUNCTION DRIVER M: Sunil Goutham <sgoutham@marvell.com> -M: Linu Cherian <lcherian@marvell.com> +M: Ratheesh Kannoth <rkannoth@marvell.com> M: Geetha sowjanya <gakula@marvell.com> -M: hariprasad <hkelam@marvell.com> M: Subbaraya Sundeep <sbhatta@marvell.com> L: netdev@vger.kernel.org S: Maintained @@ -15753,8 +15752,8 @@ F: Documentation/networking/device_drivers/ethernet/marvell/octeontx2.rst F: drivers/net/ethernet/marvell/octeontx2/af/ MARVELL PEM PMU DRIVER -M: Linu Cherian <lcherian@marvell.com> M: Gowthami Thiagarajan <gthiagarajan@marvell.com> +M: Geetha sowjanya <gakula@marvell.com> S: Supported F: drivers/perf/marvell_pem_pmu.c @@ -18437,6 +18436,7 @@ F: drivers/net/ethernet/mucse/ MULTIFUNCTION DEVICES (MFD) M: Lee Jones <lee@kernel.org> +L: mfd@lists.linux.dev S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git F: Documentation/devicetree/bindings/mfd/ @@ -27515,7 +27515,7 @@ F: drivers/net/ethernet/dec/tulip/ TUN/TAP DRIVER M: Willem de Bruijn <willemdebruijn.kernel@gmail.com> -M: Jason Wang <jasowang@redhat.com> +M: Jason Wang <jasowangio@gmail.com> S: Maintained W: http://vtun.sourceforge.net/tun F: Documentation/networking/tuntap.rst @@ -28507,7 +28507,7 @@ F: include/uapi/linux/virtio_balloon.h VIRTIO BLOCK AND SCSI DRIVERS M: "Michael S. Tsirkin" <mst@redhat.com> -M: Jason Wang <jasowang@redhat.com> +M: Jason Wang <jasowangio@gmail.com> R: Paolo Bonzini <pbonzini@redhat.com> R: Stefan Hajnoczi <stefanha@redhat.com> R: Eugenio Pérez <eperezma@redhat.com> @@ -28536,7 +28536,7 @@ F: include/uapi/linux/virtio_console.h VIRTIO CORE M: "Michael S. Tsirkin" <mst@redhat.com> -M: Jason Wang <jasowang@redhat.com> +M: Jason Wang <jasowangio@gmail.com> R: Xuan Zhuo <xuanzhuo@linux.alibaba.com> R: Eugenio Pérez <eperezma@redhat.com> L: virtualization@lists.linux.dev @@ -28614,7 +28614,7 @@ F: include/uapi/linux/virtio_gpu.h VIRTIO HOST (VHOST) M: "Michael S. Tsirkin" <mst@redhat.com> -M: Jason Wang <jasowang@redhat.com> +M: Jason Wang <jasowangio@gmail.com> R: Eugenio Pérez <eperezma@redhat.com> L: kvm@vger.kernel.org L: virtualization@lists.linux.dev @@ -28629,7 +28629,7 @@ F: kernel/vhost_task.c VIRTIO HOST (VHOST-SCSI) M: "Michael S. Tsirkin" <mst@redhat.com> -M: Jason Wang <jasowang@redhat.com> +M: Jason Wang <jasowangio@gmail.com> M: Mike Christie <michael.christie@oracle.com> R: Paolo Bonzini <pbonzini@redhat.com> R: Stefan Hajnoczi <stefanha@redhat.com> @@ -28669,7 +28669,7 @@ F: include/uapi/linux/virtio_mem.h VIRTIO NET DRIVER M: "Michael S. Tsirkin" <mst@redhat.com> -M: Jason Wang <jasowang@redhat.com> +M: Jason Wang <jasowangio@gmail.com> R: Xuan Zhuo <xuanzhuo@linux.alibaba.com> R: Eugenio Pérez <eperezma@redhat.com> L: netdev@vger.kernel.org @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 VERSION = 7 -PATCHLEVEL = 1 +PATCHLEVEL = 2 SUBLEVEL = 0 -EXTRAVERSION = +EXTRAVERSION = -rc1 NAME = Baby Opossum Posse # *DOCUMENTATION* diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c index ab76b36dce82..73cce8ac8368 100644 --- a/arch/arm64/kernel/hw_breakpoint.c +++ b/arch/arm64/kernel/hw_breakpoint.c @@ -559,6 +559,15 @@ int hw_breakpoint_arch_parse(struct perf_event *bp, else alignment_mask = 0x7; offset = hw->address & alignment_mask; + + /* + * BAS is an 8-bit field in WCR/BCR; the shift below would + * silently drop the high bits of ctrl.len when offset + len + * exceeds 8, programming hardware to watch fewer bytes than + * the user requested. + */ + if (((u32)hw->ctrl.len << offset) > ARM_BREAKPOINT_LEN_8) + return -EINVAL; } hw->address &= ~alignment_mask; diff --git a/arch/arm64/kernel/static_call.c b/arch/arm64/kernel/static_call.c index 8b3a19e10871..c126edced022 100644 --- a/arch/arm64/kernel/static_call.c +++ b/arch/arm64/kernel/static_call.c @@ -2,6 +2,7 @@ #include <linux/static_call.h> #include <linux/memory.h> #include <asm/text-patching.h> +#include <asm/insn.h> void arch_static_call_transform(void *site, void *tramp, void *func, bool tail) { diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index 12e862c96642..f2be501468ce 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -1198,11 +1198,6 @@ static void __init map_mem(void) __map_memblock(start, end, pgprot_tagged(PAGE_KERNEL), flags); } - - /* Map the kernel data/bss read-only in the linear map */ - __map_memblock(init_end, kernel_end, PAGE_KERNEL_RO, flags); - flush_tlb_kernel_range((unsigned long)lm_alias(__init_end), - (unsigned long)lm_alias(__bss_stop)); } void mark_rodata_ro(void) @@ -1221,6 +1216,12 @@ void mark_rodata_ro(void) update_mapping_prot(__pa_symbol(_text), (unsigned long)_text, (unsigned long)_stext - (unsigned long)_text, PAGE_KERNEL_RO); + + /* Map the kernel data/bss read-only in the linear map */ + update_mapping_prot(__pa_symbol(__init_end), + (unsigned long)lm_alias(__init_end), + (unsigned long)__bss_stop - (unsigned long)__init_end, + PAGE_KERNEL_RO); } static void __init declare_vma(struct vm_struct *vma, diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index bec7cc1d72ee..d8d252325017 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -209,6 +209,7 @@ config LOONGARCH select SYSCTL_ARCH_UNALIGN_NO_WARN select SYSCTL_EXCEPTION_TRACE select SWIOTLB if 64BIT + select THREAD_INFO_IN_TASK select TRACE_IRQFLAGS_SUPPORT select USE_PERCPU_NUMA_NODE_ID select USER_STACKTRACE_SUPPORT diff --git a/arch/loongarch/boot/dts/loongson-2k0500.dtsi b/arch/loongarch/boot/dts/loongson-2k0500.dtsi index 1b502064df11..992b1d0de9ec 100644 --- a/arch/loongarch/boot/dts/loongson-2k0500.dtsi +++ b/arch/loongarch/boot/dts/loongson-2k0500.dtsi @@ -405,6 +405,8 @@ i2c0: i2c@1ff48000 { compatible = "loongson,ls2k-i2c"; reg = <0x0 0x1ff48000 0x0 0x0800>; + clock-frequency = <100000>; + clocks = <&clk LOONGSON2_APB_CLK>; interrupt-parent = <&eiointc>; interrupts = <14>; status = "disabled"; @@ -413,6 +415,8 @@ i2c@1ff48800 { compatible = "loongson,ls2k-i2c"; reg = <0x0 0x1ff48800 0x0 0x0800>; + clock-frequency = <100000>; + clocks = <&clk LOONGSON2_APB_CLK>; interrupt-parent = <&eiointc>; interrupts = <15>; status = "disabled"; @@ -421,6 +425,8 @@ i2c@1ff49000 { compatible = "loongson,ls2k-i2c"; reg = <0x0 0x1ff49000 0x0 0x0800>; + clock-frequency = <100000>; + clocks = <&clk LOONGSON2_APB_CLK>; interrupt-parent = <&eiointc>; interrupts = <16>; status = "disabled"; @@ -429,6 +435,8 @@ i2c@1ff49800 { compatible = "loongson,ls2k-i2c"; reg = <0x0 0x1ff49800 0x0 0x0800>; + clock-frequency = <100000>; + clocks = <&clk LOONGSON2_APB_CLK>; interrupt-parent = <&eiointc>; interrupts = <17>; status = "disabled"; @@ -437,6 +445,8 @@ i2c@1ff4a000 { compatible = "loongson,ls2k-i2c"; reg = <0x0 0x1ff4a000 0x0 0x0800>; + clock-frequency = <100000>; + clocks = <&clk LOONGSON2_APB_CLK>; interrupt-parent = <&eiointc>; interrupts = <18>; status = "disabled"; @@ -445,6 +455,8 @@ i2c@1ff4a800 { compatible = "loongson,ls2k-i2c"; reg = <0x0 0x1ff4a800 0x0 0x0800>; + clock-frequency = <100000>; + clocks = <&clk LOONGSON2_APB_CLK>; interrupt-parent = <&eiointc>; interrupts = <19>; status = "disabled"; diff --git a/arch/loongarch/boot/dts/loongson-2k1000.dtsi b/arch/loongarch/boot/dts/loongson-2k1000.dtsi index ab6a55937e9e..e6f0fe093ab3 100644 --- a/arch/loongarch/boot/dts/loongson-2k1000.dtsi +++ b/arch/loongarch/boot/dts/loongson-2k1000.dtsi @@ -311,6 +311,8 @@ i2c2: i2c@1fe21000 { compatible = "loongson,ls2k-i2c"; reg = <0x0 0x1fe21000 0x0 0x8>; + clock-frequency = <100000>; + clocks = <&clk LOONGSON2_APB_CLK>; interrupt-parent = <&liointc0>; interrupts = <22 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; @@ -319,6 +321,8 @@ i2c3: i2c@1fe21800 { compatible = "loongson,ls2k-i2c"; reg = <0x0 0x1fe21800 0x0 0x8>; + clock-frequency = <100000>; + clocks = <&clk LOONGSON2_APB_CLK>; interrupt-parent = <&liointc0>; interrupts = <23 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; diff --git a/arch/loongarch/boot/dts/loongson-2k2000.dtsi b/arch/loongarch/boot/dts/loongson-2k2000.dtsi index 3678c084adf7..7fdc4192d2f2 100644 --- a/arch/loongarch/boot/dts/loongson-2k2000.dtsi +++ b/arch/loongarch/boot/dts/loongson-2k2000.dtsi @@ -239,6 +239,8 @@ i2c@1fe00120 { compatible = "loongson,ls2k-i2c"; reg = <0x0 0x1fe00120 0x0 0x8>; + clock-frequency = <100000>; + clocks = <&clk LOONGSON2_MISC_CLK>; interrupt-parent = <&liointc>; interrupts = <8 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; @@ -247,6 +249,8 @@ i2c@1fe00130 { compatible = "loongson,ls2k-i2c"; reg = <0x0 0x1fe00130 0x0 0x8>; + clock-frequency = <100000>; + clocks = <&clk LOONGSON2_MISC_CLK>; interrupt-parent = <&liointc>; interrupts = <9 IRQ_TYPE_LEVEL_HIGH>; status = "disabled"; diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h index eda9d4d0a493..c05168aedcaa 100644 --- a/arch/loongarch/include/asm/acpi.h +++ b/arch/loongarch/include/asm/acpi.h @@ -38,6 +38,8 @@ static inline bool acpi_has_cpu_in_madt(void) extern struct list_head acpi_wakeup_device_list; extern struct acpi_madt_core_pic acpi_core_pic[MAX_CORE_PIC]; +extern void acpi_add_early_pio(void); +extern void acpi_remove_early_pio(void); extern int __init parse_acpi_topology(void); #endif /* !CONFIG_ACPI */ diff --git a/arch/loongarch/include/asm/current.h b/arch/loongarch/include/asm/current.h new file mode 100644 index 000000000000..fa4fb7987075 --- /dev/null +++ b/arch/loongarch/include/asm/current.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __ASM_LOONGARCH_CURRENT_H +#define __ASM_LOONGARCH_CURRENT_H + +#include <linux/compiler.h> + +#ifndef __ASSEMBLER__ + +#include <asm/percpu.h> + +struct task_struct; + +DECLARE_PER_CPU(struct task_struct *, cpu_tasks); + +register struct task_struct *current_thread_pointer __asm__("$tp"); + +static __always_inline struct task_struct *get_current(void) +{ + return current_thread_pointer; +} + +#define current get_current() + +static __always_inline void set_current(struct task_struct *task) +{ + __this_cpu_write(cpu_tasks, task); +} + +#endif /* __ASSEMBLER__ */ + +#endif /* __ASM_LOONGARCH_CURRENT_H */ diff --git a/arch/loongarch/include/asm/kexec.h b/arch/loongarch/include/asm/kexec.h index 209fa43222e1..6be136e9f0a0 100644 --- a/arch/loongarch/include/asm/kexec.h +++ b/arch/loongarch/include/asm/kexec.h @@ -41,6 +41,8 @@ struct kimage_arch { unsigned long systable_ptr; }; +struct kimage; + #ifdef CONFIG_KEXEC_FILE extern const struct kexec_file_ops kexec_efi_ops; extern const struct kexec_file_ops kexec_elf_ops; @@ -59,7 +61,6 @@ typedef void (*do_kexec_t)(unsigned long efi_boot, unsigned long start_addr, unsigned long first_ind_entry); -struct kimage; extern const unsigned char relocate_new_kernel[]; extern const size_t relocate_new_kernel_size; extern void kexec_reboot(void); diff --git a/arch/loongarch/include/asm/percpu.h b/arch/loongarch/include/asm/percpu.h index 583f2466262f..500c51754ea5 100644 --- a/arch/loongarch/include/asm/percpu.h +++ b/arch/loongarch/include/asm/percpu.h @@ -5,7 +5,6 @@ #ifndef __ASM_PERCPU_H #define __ASM_PERCPU_H -#include <asm/cmpxchg.h> #include <asm/loongarch.h> /* diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h index 2a0b63ae421f..223528c04d73 100644 --- a/arch/loongarch/include/asm/pgtable.h +++ b/arch/loongarch/include/asm/pgtable.h @@ -429,6 +429,8 @@ static inline pte_t pte_mkwrite_novma(pte_t pte) static inline pte_t pte_wrprotect(pte_t pte) { + if (pte_val(pte) & _PAGE_DIRTY) + pte_val(pte) |= _PAGE_MODIFIED; pte_val(pte) &= ~(_PAGE_WRITE | _PAGE_DIRTY); return pte; } @@ -535,6 +537,8 @@ static inline pmd_t pmd_mkwrite_novma(pmd_t pmd) static inline pmd_t pmd_wrprotect(pmd_t pmd) { + if (pmd_val(pmd) & _PAGE_DIRTY) + pmd_val(pmd) |= _PAGE_MODIFIED; pmd_val(pmd) &= ~(_PAGE_WRITE | _PAGE_DIRTY); return pmd; } diff --git a/arch/loongarch/include/asm/smp.h b/arch/loongarch/include/asm/smp.h index 3a47f52959a8..dd1457065690 100644 --- a/arch/loongarch/include/asm/smp.h +++ b/arch/loongarch/include/asm/smp.h @@ -81,8 +81,9 @@ extern int __cpu_logical_map[NR_CPUS]; struct seq_file; struct secondary_data { + unsigned long task; unsigned long stack; - unsigned long thread_info; + unsigned long offset; }; extern struct secondary_data cpuboot_data; diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h index ecc8e50fffa8..79526fa423a7 100644 --- a/arch/loongarch/include/asm/stackframe.h +++ b/arch/loongarch/include/asm/stackframe.h @@ -184,6 +184,7 @@ .cfi_rel_offset ra, PT_ERA .endif cfi_st tp, PT_R2, \docfi + cfi_st u0, PT_R21, \docfi cfi_st fp, PT_R22, \docfi /* Set thread_info if we're coming from user mode */ @@ -191,10 +192,13 @@ andi t0, t0, 0x3 /* extract pplv bit */ beqz t0, 9f - LONG_LI tp, ~_THREAD_MASK - and tp, tp, sp - cfi_st u0, PT_R21, \docfi csrrd u0, PERCPU_BASE_KS + + la_abs t1, cpu_tasks +#ifdef CONFIG_SMP + LONG_ADD t1, t1, u0 +#endif + LONG_L tp, t1, 0 9: #ifdef CONFIG_KGDB li.w t0, CSR_CRMD_WE diff --git a/arch/loongarch/include/asm/switch_to.h b/arch/loongarch/include/asm/switch_to.h index 5b225aff3ba2..27acbf913774 100644 --- a/arch/loongarch/include/asm/switch_to.h +++ b/arch/loongarch/include/asm/switch_to.h @@ -15,7 +15,6 @@ struct task_struct; * __switch_to - switch execution of a task * @prev: The task previously executed. * @next: The task to begin executing. - * @next_ti: task_thread_info(next). * @sched_ra: __schedule return address. * @sched_cfa: __schedule call frame address. * @@ -23,8 +22,7 @@ struct task_struct; * the context of next. Returns prev. */ extern asmlinkage struct task_struct *__switch_to(struct task_struct *prev, - struct task_struct *next, struct thread_info *next_ti, - void *sched_ra, void *sched_cfa); + struct task_struct *next, void *sched_ra, void *sched_cfa); /* * For newly created kernel threads switch_to() will return to @@ -37,7 +35,8 @@ do { \ lose_fpu_inatomic(1, prev); \ lose_lbt_inatomic(1, prev); \ hw_breakpoint_thread_switch(next); \ - (last) = __switch_to(prev, next, task_thread_info(next), \ + set_current(next); \ + (last) = __switch_to(prev, next, \ __builtin_return_address(0), __builtin_frame_address(0)); \ } while (0) diff --git a/arch/loongarch/include/asm/thread_info.h b/arch/loongarch/include/asm/thread_info.h index 4d7117fcdc78..41eabe4fb647 100644 --- a/arch/loongarch/include/asm/thread_info.h +++ b/arch/loongarch/include/asm/thread_info.h @@ -22,7 +22,6 @@ * must also be changed */ struct thread_info { - struct task_struct *task; /* main task structure */ unsigned long flags; /* low level flags */ unsigned long tp_value; /* thread pointer */ __u32 cpu; /* current CPU */ @@ -37,20 +36,11 @@ struct thread_info { */ #define INIT_THREAD_INFO(tsk) \ { \ - .task = &tsk, \ .flags = _TIF_FIXADE, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ } -/* How to get the thread information struct from C. */ -register struct thread_info *__current_thread_info __asm__("$tp"); - -static inline struct thread_info *current_thread_info(void) -{ - return __current_thread_info; -} - register unsigned long current_stack_pointer __asm__("$sp"); #endif /* !__ASSEMBLER__ */ diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c index 058f0dbe8e8f..8f650c9ffecd 100644 --- a/arch/loongarch/kernel/acpi.c +++ b/arch/loongarch/kernel/acpi.c @@ -16,6 +16,7 @@ #include <linux/memblock.h> #include <linux/of_fdt.h> #include <linux/serial_core.h> +#include <linux/vmalloc.h> #include <asm/io.h> #include <asm/numa.h> #include <asm/loongson.h> @@ -59,6 +60,33 @@ void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size) return ioremap_cache(phys, size); } +#define PIO_BASE (unsigned long)PCI_IOBASE +#define PIO_SIZE ALIGN(ISA_IOSIZE, PAGE_SIZE) + +static bool acpi_pio; + +/* Add PIO for early access */ +void acpi_add_early_pio(void) +{ + if (!acpi_disabled) { + acpi_pio = true; + vmap_page_range(PIO_BASE, PIO_BASE + PIO_SIZE, + LOONGSON_LIO_BASE, pgprot_device(PAGE_KERNEL)); + } +} + +/* Remove PIO for PCI register */ +void acpi_remove_early_pio(void) +{ + if (!acpi_pio) + return; + + if (!acpi_disabled) { + acpi_pio = false; + vunmap_range(PIO_BASE, PIO_BASE + PIO_SIZE); + } +} + #ifdef CONFIG_SMP static int set_processor_mask(u32 id, u32 pass) { diff --git a/arch/loongarch/kernel/asm-offsets.c b/arch/loongarch/kernel/asm-offsets.c index 2cc953f113ac..1b861cbc5e10 100644 --- a/arch/loongarch/kernel/asm-offsets.c +++ b/arch/loongarch/kernel/asm-offsets.c @@ -70,7 +70,7 @@ static void __used output_task_defines(void) { COMMENT("LoongArch task_struct offsets."); OFFSET(TASK_STATE, task_struct, __state); - OFFSET(TASK_THREAD_INFO, task_struct, stack); + OFFSET(TASK_STACK, task_struct, stack); OFFSET(TASK_FLAGS, task_struct, flags); OFFSET(TASK_MM, task_struct, mm); OFFSET(TASK_PID, task_struct, pid); @@ -84,7 +84,6 @@ static void __used output_task_defines(void) static void __used output_thread_info_defines(void) { COMMENT("LoongArch thread_info offsets."); - OFFSET(TI_TASK, thread_info, task); OFFSET(TI_FLAGS, thread_info, flags); OFFSET(TI_TP_VALUE, thread_info, tp_value); OFFSET(TI_CPU, thread_info, cpu); @@ -266,8 +265,9 @@ static void __used output_signal_defines(void) static void __used output_smpboot_defines(void) { COMMENT("Linux smp cpu boot offsets."); + OFFSET(CPU_BOOT_TASK, secondary_data, task); OFFSET(CPU_BOOT_STACK, secondary_data, stack); - OFFSET(CPU_BOOT_TINFO, secondary_data, thread_info); + OFFSET(CPU_BOOT_OFFSET, secondary_data, offset); BLANK(); } #endif diff --git a/arch/loongarch/kernel/entry.S b/arch/loongarch/kernel/entry.S index b53d333a7c42..53bce27f516b 100644 --- a/arch/loongarch/kernel/entry.S +++ b/arch/loongarch/kernel/entry.S @@ -67,8 +67,11 @@ SYM_CODE_START(handle_syscall) #endif move u0, t0 - LONG_LI tp, ~_THREAD_MASK - and tp, tp, sp + la_abs t1, cpu_tasks +#ifdef CONFIG_SMP + LONG_ADD t1, t1, u0 +#endif + LONG_L tp, t1, 0 move a0, sp bl do_syscall diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S index 4eed7bc312a8..601ddd78b233 100644 --- a/arch/loongarch/kernel/head.S +++ b/arch/loongarch/kernel/head.S @@ -74,10 +74,11 @@ SYM_CODE_START(kernel_entry) # kernel entry point /* GPR21 used for percpu base (runtime), initialized as 0 */ move u0, zero - la.pcrel tp, init_thread_union - /* Set the SP after an empty pt_regs. */ - PTR_LI sp, (_THREAD_SIZE - PT_SIZE) - PTR_ADD sp, sp, tp + la.pcrel tp, init_task + la.pcrel t0, init_stack + PTR_LI t1, _THREAD_SIZE + PTR_ADD t0, t0, t1 + PTR_ADDI sp, t0, -PT_SIZE set_saved_sp sp, t0, t1 #ifdef CONFIG_RELOCATABLE @@ -86,8 +87,10 @@ SYM_CODE_START(kernel_entry) # kernel entry point #ifdef CONFIG_RANDOMIZE_BASE /* Repoint the sp into the new kernel */ - PTR_LI sp, (_THREAD_SIZE - PT_SIZE) - PTR_ADD sp, sp, tp + LONG_LPTR t0, tp, TASK_STACK + PTR_LI t1, _THREAD_SIZE + PTR_ADD t0, t0, t1 + PTR_ADDI sp, t0, -PT_SIZE set_saved_sp sp, t0, t1 /* Jump to the new kernel: new_pc = current_pc + random_offset */ @@ -127,8 +130,9 @@ SYM_CODE_START(smpboot_entry) csrxchg t0, t1, LOONGARCH_CSR_IMPCTL1 #endif la.pcrel t0, cpuboot_data + ld.d tp, t0, CPU_BOOT_TASK ld.d sp, t0, CPU_BOOT_STACK - ld.d tp, t0, CPU_BOOT_TINFO + ld.d u0, t0, CPU_BOOT_OFFSET bl start_secondary ASM_BUG() diff --git a/arch/loongarch/kernel/process.c b/arch/loongarch/kernel/process.c index 5505fc355e1b..baa683fbfc53 100644 --- a/arch/loongarch/kernel/process.c +++ b/arch/loongarch/kernel/process.c @@ -60,6 +60,8 @@ unsigned long __stack_chk_guard __read_mostly; EXPORT_SYMBOL(__stack_chk_guard); #endif +DEFINE_PER_CPU(struct task_struct *, cpu_tasks); + /* * Idle related variables and functions */ diff --git a/arch/loongarch/kernel/relocate.c b/arch/loongarch/kernel/relocate.c index 4b61a9632a98..2a42874e5eb7 100644 --- a/arch/loongarch/kernel/relocate.c +++ b/arch/loongarch/kernel/relocate.c @@ -313,7 +313,7 @@ unsigned long __init relocate_kernel(void) reloc_offset += random_offset; /* The current thread is now within the relocated kernel */ - __current_thread_info = RELOCATED_KASLR(__current_thread_info); + current_thread_pointer = RELOCATED_KASLR(current_thread_pointer); update_reloc_offset(&reloc_offset, random_offset); } diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c index 839b23edee87..eaebb52bd36e 100644 --- a/arch/loongarch/kernel/setup.c +++ b/arch/loongarch/kernel/setup.c @@ -502,6 +502,8 @@ static __init int arch_reserve_pio_range(void) { struct device_node *np; + acpi_add_early_pio(); + for_each_node_by_name(np, "isa") { struct of_range range; struct of_range_parser parser; @@ -593,6 +595,7 @@ void __init setup_arch(char **cmdline_p) { cpu_probe(); unwind_init(); + set_current(current); init_environ(); efi_init(); diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c index 50922610758b..5d792256bbb9 100644 --- a/arch/loongarch/kernel/smp.c +++ b/arch/loongarch/kernel/smp.c @@ -400,8 +400,9 @@ void loongson_boot_secondary(int cpu, struct task_struct *idle) pr_info("Booting CPU#%d...\n", cpu); entry = __pa_symbol((unsigned long)&smpboot_entry); - cpuboot_data.stack = (unsigned long)__KSTK_TOS(idle); - cpuboot_data.thread_info = (unsigned long)task_thread_info(idle); + cpuboot_data.task = (unsigned long)idle; + cpuboot_data.stack = (unsigned long)task_pt_regs(idle); + cpuboot_data.offset = per_cpu_offset(cpu); csr_mail_send(entry, cpu_logical_map(cpu), 0); @@ -663,6 +664,7 @@ asmlinkage void start_secondary(void) set_my_cpu_offset(per_cpu_offset(cpu)); cpu_probe(); + set_current(current); constant_clockevent_init(); loongson_init_secondary(); @@ -705,6 +707,7 @@ static void stop_this_cpu(void *dummy) set_cpu_online(smp_processor_id(), false); calculate_cpu_foreign_map(); local_irq_disable(); + rcutree_report_cpu_dead(); while (true); } diff --git a/arch/loongarch/kernel/switch.S b/arch/loongarch/kernel/switch.S index f377d8f5c51a..d6e0d05d6fd3 100644 --- a/arch/loongarch/kernel/switch.S +++ b/arch/loongarch/kernel/switch.S @@ -12,7 +12,7 @@ /* * task_struct *__switch_to(task_struct *prev, task_struct *next, - * struct thread_info *next_ti, void *sched_ra, void *sched_cfa) + * void *sched_ra, void *sched_cfa) */ .align 5 SYM_FUNC_START(__switch_to) @@ -24,8 +24,8 @@ SYM_FUNC_START(__switch_to) LONG_SPTR t1, a0, (THREAD_CSRPRMD - TASK_STRUCT_OFFSET) cpu_save_nonscratch a0 - LONG_SPTR a3, a0, (THREAD_SCHED_RA - TASK_STRUCT_OFFSET) - LONG_SPTR a4, a0, (THREAD_SCHED_CFA - TASK_STRUCT_OFFSET) + LONG_SPTR a2, a0, (THREAD_SCHED_RA - TASK_STRUCT_OFFSET) + LONG_SPTR a3, a0, (THREAD_SCHED_CFA - TASK_STRUCT_OFFSET) #if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_SMP) la t7, __stack_chk_guard @@ -33,11 +33,12 @@ SYM_FUNC_START(__switch_to) LONG_SPTR t8, t7, 0 #endif - move tp, a2 + move tp, a1 cpu_restore_nonscratch a1 - li.w t0, _THREAD_SIZE - PTR_ADD t0, t0, tp + LONG_LPTR t0, a1, (TASK_STACK - TASK_STRUCT_OFFSET) + PTR_LI t1, _THREAD_SIZE + PTR_ADD t0, t0, t1 set_saved_sp t0, t1, t2 LONG_LPTR t1, a1, (THREAD_CSRPRMD - TASK_STRUCT_OFFSET) @@ -45,6 +46,7 @@ SYM_FUNC_START(__switch_to) #ifdef CONFIG_32BIT PTR_ADDI a0, a0, -TASK_STRUCT_OFFSET + PTR_ADDI tp, tp, -TASK_STRUCT_OFFSET #endif jr ra SYM_FUNC_END(__switch_to) diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c index 5d49b742e3bf..776523747ea3 100644 --- a/arch/loongarch/kernel/traps.c +++ b/arch/loongarch/kernel/traps.c @@ -107,6 +107,12 @@ static void show_stacktrace(struct task_struct *task, unsigned long stackdata; unsigned long *sp = (unsigned long *)regs->regs[3]; + if (!task) + task = current; + + if (!try_get_task_stack(task)) + return; + printk("%sStack :", loglvl); i = 0; while ((unsigned long) sp & (PAGE_SIZE - 1)) { @@ -129,6 +135,8 @@ static void show_stacktrace(struct task_struct *task, } pr_cont("\n"); show_backtrace(task, regs, loglvl, user); + + put_task_stack(task); } void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl) diff --git a/arch/loongarch/mm/pageattr.c b/arch/loongarch/mm/pageattr.c index f5e910b68229..614ccc7afccb 100644 --- a/arch/loongarch/mm/pageattr.c +++ b/arch/loongarch/mm/pageattr.c @@ -234,5 +234,5 @@ int set_direct_map_valid_noflush(struct page *page, unsigned nr, bool valid) clear = __pgprot(_PAGE_PRESENT | _PAGE_VALID); } - return __set_memory(addr, 1, set, clear); + return __set_memory(addr, nr, set, clear); } diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c index 058ffbbaad85..ad7e28375aa9 100644 --- a/arch/loongarch/net/bpf_jit.c +++ b/arch/loongarch/net/bpf_jit.c @@ -5,11 +5,18 @@ * Copyright (C) 2022 Loongson Technology Corporation Limited */ #include <linux/memory.h> +#include <asm/asm-offsets.h> #include "bpf_jit.h" #define LOONGARCH_MAX_REG_ARGS 8 +#define LOONGARCH_SAVE_RA_NINSNS 1 #define LOONGARCH_LONG_JUMP_NINSNS 5 +#define LOONGARCH_TCC_SLOT_NINSNS 1 + +#define LOONGARCH_PROLOGUE_SKIP_INSNS \ + (LOONGARCH_SAVE_RA_NINSNS + LOONGARCH_LONG_JUMP_NINSNS + LOONGARCH_TCC_SLOT_NINSNS) + #define LOONGARCH_LONG_JUMP_NBYTES (LOONGARCH_LONG_JUMP_NINSNS * 4) #define LOONGARCH_FENTRY_NINSNS 2 @@ -143,8 +150,13 @@ static void build_prologue(struct jit_ctx *ctx) stack_adjust = round_up(stack_adjust, 16); stack_adjust += bpf_stack_adjust; + /* + * Save the original return address to a temporary register to prevent + * it from being overwritten, then reserve space for the long jump and + * fentry trampoline slot for dynamically patching by ftrace at runtime. + * These instructions are bypassed during a tail call invocation. + */ move_reg(ctx, LOONGARCH_GPR_T0, LOONGARCH_GPR_RA); - /* Reserve space for the move_imm + jirl instruction */ for (i = 0; i < LOONGARCH_LONG_JUMP_NINSNS; i++) emit_insn(ctx, nop); @@ -253,10 +265,11 @@ static void __build_epilogue(struct jit_ctx *ctx, bool is_tail_call) emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_RA, 0); } else { /* - * Call the next bpf prog and skip the first instruction - * of TCC initialization. + * Tail call to the next BPF program, passing offset in number + * of instructions to jirl to bypass the initial setup slots. */ - emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_T3, 7); + emit_insn(ctx, jirl, LOONGARCH_GPR_ZERO, + LOONGARCH_GPR_T3, LOONGARCH_PROLOGUE_SKIP_INSNS); } } @@ -312,12 +325,12 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn) */ emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, tcc_ptr_off); emit_insn(ctx, ldd, t3, REG_TCC, 0); - emit_insn(ctx, addid, t3, t3, 1); - emit_insn(ctx, std, t3, REG_TCC, 0); emit_insn(ctx, addid, t2, LOONGARCH_GPR_ZERO, MAX_TAIL_CALL_CNT); - if (emit_tailcall_jmp(ctx, BPF_JSGT, t3, t2, jmp_offset) < 0) + if (emit_tailcall_jmp(ctx, BPF_JSGE, t3, t2, jmp_offset) < 0) goto toofar; + emit_insn(ctx, addid, t3, t3, 1); + /* * prog = array->ptrs[index]; * if (!prog) @@ -330,6 +343,8 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn) if (emit_tailcall_jmp(ctx, BPF_JEQ, t2, LOONGARCH_GPR_ZERO, jmp_offset) < 0) goto toofar; + emit_insn(ctx, std, t3, REG_TCC, 0); + /* goto *(prog->bpf_func + 4); */ off = offsetof(struct bpf_prog, bpf_func); emit_insn(ctx, ldd, t3, t2, off); @@ -1147,6 +1162,19 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext /* function call */ case BPF_JMP | BPF_CALL: + /* Implement helper call to bpf_get_current_task/_btf() inline */ + if (insn->src_reg == 0 && (insn->imm == BPF_FUNC_get_current_task || + insn->imm == BPF_FUNC_get_current_task_btf)) { + move_reg(ctx, regmap[BPF_REG_0], LOONGARCH_GPR_TP); + break; + } + + /* Implement helper call to bpf_get_smp_processor_id() inline */ + if (insn->src_reg == 0 && insn->imm == BPF_FUNC_get_smp_processor_id) { + emit_insn(ctx, ldwu, regmap[BPF_REG_0], LOONGARCH_GPR_TP, TI_CPU); + break; + } + ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, &func_addr, &func_addr_fixed); if (ret < 0) @@ -2367,3 +2395,15 @@ bool bpf_jit_supports_subprog_tailcalls(void) { return true; } + +bool bpf_jit_inlines_helper_call(s32 imm) +{ + switch (imm) { + case BPF_FUNC_get_current_task: + case BPF_FUNC_get_current_task_btf: + case BPF_FUNC_get_smp_processor_id: + return true; + default: + return false; + } +} diff --git a/arch/loongarch/pci/acpi.c b/arch/loongarch/pci/acpi.c index b02698a338ee..ccbcea61fcd9 100644 --- a/arch/loongarch/pci/acpi.c +++ b/arch/loongarch/pci/acpi.c @@ -65,6 +65,8 @@ static int acpi_prepare_root_resources(struct acpi_pci_root_info *ci) struct resource_entry *entry, *tmp; struct acpi_device *device = ci->bridge; + acpi_remove_early_pio(); + status = acpi_pci_probe_root_resources(ci); if (status > 0) { acpi_evaluate_integer(device->handle, "PCIH", NULL, &pci_h); diff --git a/arch/loongarch/vdso/elf.S b/arch/loongarch/vdso/elf.S index 9bb21b9f9583..d6e000003b85 100644 --- a/arch/loongarch/vdso/elf.S +++ b/arch/loongarch/vdso/elf.S @@ -7,9 +7,12 @@ #include <asm/vdso/vdso.h> +#include <linux/build-salt.h> #include <linux/elfnote.h> #include <linux/version.h> ELFNOTE_START(Linux, 0, "a") .long LINUX_VERSION_CODE ELFNOTE_END + +BUILD_SALT diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 63976c3bcbe0..891112c2cddf 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -1300,7 +1300,7 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev, args->handle, args->offset, args->size); if (args->direction == SYNC_DIRECT_FROM_DEVICE) - ret = amdxdna_hwctx_sync_debug_bo(abo->client, args->handle); + ret = amdxdna_hwctx_sync_debug_bo(client, args->handle); put_obj: drm_gem_object_put(gobj); diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index 521931d1f7fc..b24f31a8b567 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -208,9 +208,9 @@ static int ivpu_hws_cmdq_init(struct ivpu_file_priv *file_priv, struct ivpu_cmdq ret = ivpu_jsm_hws_set_context_sched_properties(vdev, file_priv->ctx.id, cmdq->id, priority); if (ret) - return ret; + ivpu_jsm_hws_destroy_cmdq(vdev, file_priv->ctx.id, cmdq->id); - return 0; + return ret; } static int ivpu_register_db(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq) @@ -281,10 +281,10 @@ static int ivpu_cmdq_register(struct ivpu_file_priv *file_priv, struct ivpu_cmdq } ret = ivpu_register_db(file_priv, cmdq); - if (ret) - return ret; + if (ret && vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW) + ivpu_jsm_hws_destroy_cmdq(vdev, file_priv->ctx.id, cmdq->id); - return 0; + return ret; } static int ivpu_cmdq_unregister(struct ivpu_file_priv *file_priv, struct ivpu_cmdq *cmdq) diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c index 79ce6e72bf29..2dbed92d54b3 100644 --- a/drivers/acpi/acpi_ipmi.c +++ b/drivers/acpi/acpi_ipmi.c @@ -490,7 +490,7 @@ static void ipmi_bmc_gone(int iface) mutex_lock(&driver_data.ipmi_lock); list_for_each_entry_safe(iter, temp, &driver_data.ipmi_devices, head) { - if (iter->ipmi_ifnum != iface) { + if (iter->ipmi_ifnum == iface) { ipmi_device = iter; __ipmi_dev_kill(iter); break; diff --git a/drivers/acpi/acpica/utnonansi.c b/drivers/acpi/acpica/utnonansi.c index 3a7952be6545..93867ad7f342 100644 --- a/drivers/acpi/acpica/utnonansi.c +++ b/drivers/acpi/acpica/utnonansi.c @@ -168,7 +168,16 @@ void acpi_ut_safe_strncpy(char *dest, char *source, acpi_size dest_size) { /* Always terminate destination string */ +#ifdef __KERNEL__ strscpy_pad(dest, source, dest_size); +#else + /* + * strscpy_pad() is not defined in ACPICA tools builds, so use strncpy() + * and directly NUL-terminate the destination string in that case. + */ + strncpy(dest, source, dest_size); + dest[dest_size - 1] = 0; +#endif } #endif diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 390ab5f1d313..4482cf28f56a 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1143,7 +1143,7 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) return 0; } -int __weak acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) +int __weak __cpuidle acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) { return -ENODEV; } @@ -1156,7 +1156,7 @@ int __weak acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) * * Return: 0 for success or negative value for error */ -static int acpi_idle_lpi_enter(struct cpuidle_device *dev, +static int __cpuidle acpi_idle_lpi_enter(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { struct acpi_processor *pr; diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index bc8050d8a6f5..56df4599d360 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -871,7 +871,7 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt); /** - * acpi_dev_free_resource_list - Free resource from %acpi_dev_get_resources(). + * acpi_dev_free_resource_list - Free resource from acpi_dev_get_resources(). * @list: The head of the resource list to free. */ void acpi_dev_free_resource_list(struct list_head *list) @@ -991,7 +991,7 @@ static int __acpi_dev_get_resources(struct acpi_device *adev, * * The resultant struct resource objects are put on the list pointed to by * @list, that must be empty initially, as members of struct resource_entry - * objects. Callers of this routine should use %acpi_dev_free_resource_list() to + * objects. Callers of this routine should use acpi_dev_free_resource_list() to * free that list. * * The number of resources in the output list is returned on success, an error @@ -1032,7 +1032,7 @@ static int is_memory(struct acpi_resource *ares, void *not_used) * The resultant struct resource objects are put on the list pointed to * by @list, that must be empty initially, as members of struct * resource_entry objects. Callers of this routine should use - * %acpi_dev_free_resource_list() to free that list. + * acpi_dev_free_resource_list() to free that list. * * The number of resources in the output list is returned on success, * an error code reflecting the error condition is returned otherwise. diff --git a/drivers/acpi/riscv/cpuidle.c b/drivers/acpi/riscv/cpuidle.c index 624f9bbdb58c..c76dbabff702 100644 --- a/drivers/acpi/riscv/cpuidle.c +++ b/drivers/acpi/riscv/cpuidle.c @@ -66,7 +66,7 @@ int acpi_processor_ffh_lpi_probe(unsigned int cpu) return acpi_cpu_init_idle(cpu); } -int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) +int __cpuidle acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) { u32 state = lpi->address; diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index a625de3c3c8b..908cc5c7e643 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -17,12 +17,12 @@ #ifdef CONFIG_ACPI_DEBUG /* * ACPI debug sysfs I/F, including: - * /sys/modules/acpi/parameters/debug_layer - * /sys/modules/acpi/parameters/debug_level - * /sys/modules/acpi/parameters/trace_method_name - * /sys/modules/acpi/parameters/trace_state - * /sys/modules/acpi/parameters/trace_debug_layer - * /sys/modules/acpi/parameters/trace_debug_level + * /sys/module/acpi/parameters/debug_layer + * /sys/module/acpi/parameters/debug_level + * /sys/module/acpi/parameters/trace_method_name + * /sys/module/acpi/parameters/trace_state + * /sys/module/acpi/parameters/trace_debug_layer + * /sys/module/acpi/parameters/trace_debug_level */ struct acpi_dlayer { @@ -269,7 +269,7 @@ module_param_call(trace_state, param_set_trace_state, param_get_trace_state, #endif /* CONFIG_ACPI_DEBUG */ -/* /sys/modules/acpi/parameters/aml_debug_output */ +/* /sys/module/acpi/parameters/aml_debug_output */ module_param_named(aml_debug_output, acpi_gbl_enable_aml_debug_object, byte, 0644); diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 27616b05111c..aa8f2efed779 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -245,7 +245,7 @@ err_exit: if (map->cache_ops->exit) { dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name); map->lock(map->lock_arg); - ret = map->cache_ops->exit(map); + map->cache_ops->exit(map); map->unlock(map->lock_arg); } err_free_reg_defaults: diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 90c13de47cff..ac90d81aa294 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -580,14 +580,7 @@ static const struct bus_type rbd_bus_type = { .bus_groups = rbd_bus_groups, }; -static void rbd_root_dev_release(struct device *dev) -{ -} - -static struct device rbd_root_dev = { - .init_name = "rbd", - .release = rbd_root_dev_release, -}; +static struct device *rbd_root_dev; static __printf(2, 3) void rbd_warn(struct rbd_device *rbd_dev, const char *fmt, ...) @@ -5378,7 +5371,7 @@ static struct rbd_device *__rbd_dev_create(struct rbd_spec *spec) rbd_dev->dev.bus = &rbd_bus_type; rbd_dev->dev.type = &rbd_device_type; - rbd_dev->dev.parent = &rbd_root_dev; + rbd_dev->dev.parent = rbd_root_dev; device_initialize(&rbd_dev->dev); return rbd_dev; @@ -7324,15 +7317,13 @@ static int __init rbd_sysfs_init(void) { int ret; - ret = device_register(&rbd_root_dev); - if (ret < 0) { - put_device(&rbd_root_dev); - return ret; - } + rbd_root_dev = root_device_register("rbd"); + if (IS_ERR(rbd_root_dev)) + return PTR_ERR(rbd_root_dev); ret = bus_register(&rbd_bus_type); if (ret < 0) - device_unregister(&rbd_root_dev); + root_device_unregister(rbd_root_dev); return ret; } @@ -7340,7 +7331,7 @@ static int __init rbd_sysfs_init(void) static void __exit rbd_sysfs_cleanup(void) { bus_unregister(&rbd_bus_type); - device_unregister(&rbd_root_dev); + root_device_unregister(rbd_root_dev); } static int __init rbd_slab_init(void) diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index b2efbe9f6acb..1717ce75a907 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -480,6 +480,12 @@ config COMMON_CLK_K210 help Support for the Canaan Kendryte K210 RISC-V SoC clocks. +config COMMON_CLK_K230 + bool "Clock driver for the Canaan Kendryte K230 SoC" + depends on ARCH_CANAAN || COMPILE_TEST + help + Support for the Canaan Kendryte K230 RISC-V SoC clocks. + config COMMON_CLK_SP7021 tristate "Clock driver for Sunplus SP7021 SoC" depends on SOC_SP7021 || COMPILE_TEST diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index a3e2862ebd7e..cc108a75a900 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -65,6 +65,7 @@ obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o obj-$(CONFIG_CLK_HSDK) += clk-hsdk-pll.o obj-$(CONFIG_COMMON_CLK_K210) += clk-k210.o +obj-$(CONFIG_COMMON_CLK_K230) += clk-k230.o obj-$(CONFIG_LMK04832) += clk-lmk04832.o obj-$(CONFIG_COMMON_CLK_LAN966X) += clk-lan966x.o obj-$(CONFIG_COMMON_CLK_LOCHNAGAR) += clk-lochnagar.o @@ -141,7 +142,7 @@ obj-$(CONFIG_COMMON_CLK_PXA) += pxa/ obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/ obj-y += ralink/ obj-y += renesas/ -obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/ +obj-$(CONFIG_COMMON_CLK_ROCKCHIP) += rockchip/ obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/ obj-$(CONFIG_CLK_SIFIVE) += sifive/ obj-y += socfpga/ diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index b618a5e00b00..03a6c31d6aa8 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -180,9 +180,9 @@ static int __init pmc_register_ops(void) of_node_put(np); return -ENODEV; } - of_node_put(np); at91_pmc_backup_suspend = of_iomap(np, 0); + of_node_put(np); if (!at91_pmc_backup_suspend) { pr_warn("%s(): unable to map securam\n", __func__); return -ENOMEM; diff --git a/drivers/clk/at91/sam9x7.c b/drivers/clk/at91/sam9x7.c index 89868a0aeaba..8b52da194849 100644 --- a/drivers/clk/at91/sam9x7.c +++ b/drivers/clk/at91/sam9x7.c @@ -387,7 +387,7 @@ static const struct { { .n = "dma0_clk", .id = 20, }, { .n = "uhphs_clk", .id = 22, }, { .n = "udphs_clk", .id = 23, }, - { .n = "macb0_clk", .id = 24, }, + { .n = "gmac_clk", .id = 24, }, { .n = "lcd_clk", .id = 25, }, { .n = "sdmmc1_clk", .id = 26, }, { .n = "ssc_clk", .id = 28, }, @@ -420,7 +420,6 @@ static const struct { { .n = "lvdsc_clk", .id = 56, }, { .n = "pit64b1_clk", .id = 58, }, { .n = "puf_clk", .id = 59, }, - { .n = "gmactsu_clk", .id = 67, }, }; /* @@ -570,6 +569,15 @@ static const struct { }, { + .n = "gmac_gclk", + .id = 24, + .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, + .pp_mux_table = { 6, 8, }, + .pp_count = 2, + .pp_chg_id = INT_MIN, + }, + + { .n = "lcd_gclk", .id = 25, .r = { .max = 75000000 }, @@ -702,15 +710,6 @@ static const struct { .pp_count = 1, .pp_chg_id = INT_MIN, }, - - { - .n = "gmac_gclk", - .id = 67, - .pp = { "audiopll_divpmcck", "plla_div2pmcck", }, - .pp_mux_table = { 6, 8, }, - .pp_count = 2, - .pp_chg_id = INT_MIN, - }, }; static void __init sam9x7_pmc_setup(struct device_node *np) diff --git a/drivers/clk/at91/sama7d65.c b/drivers/clk/at91/sama7d65.c index 7dee2b160ffb..ba8ff413fa2c 100644 --- a/drivers/clk/at91/sama7d65.c +++ b/drivers/clk/at91/sama7d65.c @@ -677,6 +677,7 @@ static struct { { .n = "uhphs_clk", .p = PCK_PARENT_HW_MCK5, .id = 101, }, { .n = "dsi_clk", .p = PCK_PARENT_HW_MCK3, .id = 103, }, { .n = "lvdsc_clk", .p = PCK_PARENT_HW_MCK3, .id = 104, }, + { .n = "i3cc_clk", .p = PCK_PARENT_HW_MCK8, .id = 105, }, }; /* diff --git a/drivers/clk/bcm/clk-iproc-asiu.c b/drivers/clk/bcm/clk-iproc-asiu.c index 819ab1b55df3..56a813227981 100644 --- a/drivers/clk/bcm/clk-iproc-asiu.c +++ b/drivers/clk/bcm/clk-iproc-asiu.c @@ -27,8 +27,7 @@ struct iproc_asiu { void __iomem *div_base; void __iomem *gate_base; - struct clk_hw_onecell_data *clk_data; - struct iproc_asiu_clk *clks; + struct iproc_asiu_clk clks[]; }; #define to_asiu_clk(hw) container_of(hw, struct iproc_asiu_clk, hw) @@ -184,22 +183,19 @@ void __init iproc_asiu_setup(struct device_node *node, { int i, ret; struct iproc_asiu *asiu; + struct clk_hw_onecell_data *clk_data; if (WARN_ON(!gate || !div)) return; - asiu = kzalloc_obj(*asiu); + asiu = kzalloc_flex(*asiu, clks, num_clks); if (WARN_ON(!asiu)) return; - asiu->clk_data = kzalloc_flex(*asiu->clk_data, hws, num_clks); - if (WARN_ON(!asiu->clk_data)) + clk_data = kzalloc_flex(*clk_data, hws, num_clks); + if (WARN_ON(!clk_data)) goto err_clks; - asiu->clk_data->num = num_clks; - - asiu->clks = kzalloc_objs(*asiu->clks, num_clks); - if (WARN_ON(!asiu->clks)) - goto err_asiu_clks; + clk_data->num = num_clks; asiu->div_base = of_iomap(node, 0); if (WARN_ON(!asiu->div_base)) @@ -236,11 +232,11 @@ void __init iproc_asiu_setup(struct device_node *node, ret = clk_hw_register(NULL, &asiu_clk->hw); if (WARN_ON(ret)) goto err_clk_register; - asiu->clk_data->hws[i] = &asiu_clk->hw; + clk_data->hws[i] = &asiu_clk->hw; } ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, - asiu->clk_data); + clk_data); if (WARN_ON(ret)) goto err_clk_register; @@ -248,17 +244,14 @@ void __init iproc_asiu_setup(struct device_node *node, err_clk_register: while (--i >= 0) - clk_hw_unregister(asiu->clk_data->hws[i]); + clk_hw_unregister(clk_data->hws[i]); iounmap(asiu->gate_base); err_iomap_gate: iounmap(asiu->div_base); err_iomap_div: - kfree(asiu->clks); - -err_asiu_clks: - kfree(asiu->clk_data); + kfree(clk_data); err_clks: kfree(asiu); diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c index fa5ccef73e60..26f76a6db820 100644 --- a/drivers/clk/clk-axi-clkgen.c +++ b/drivers/clk/clk-axi-clkgen.c @@ -521,7 +521,7 @@ static int axi_clkgen_setup_limits(struct axi_clkgen *axi_clkgen, axi_clkgen->limits.fvco_max = 1200000; axi_clkgen->limits.fpfd_max = 450000; break; - case ADI_AXI_FPGA_SPEED_2 ... ADI_AXI_FPGA_SPEED_2LV: + case ADI_AXI_FPGA_SPEED_2 ... ADI_AXI_FPGA_SPEED_2MP: axi_clkgen->limits.fvco_max = 1440000; axi_clkgen->limits.fpfd_max = 500000; if (family == ADI_AXI_FPGA_FAMILY_KINTEX || family == ADI_AXI_FPGA_FAMILY_ARTIX) { @@ -546,6 +546,9 @@ static int axi_clkgen_setup_limits(struct axi_clkgen *axi_clkgen, if (tech == ADI_AXI_FPGA_TECH_ULTRASCALE_PLUS) { axi_clkgen->limits.fvco_max = 1600000; axi_clkgen->limits.fvco_min = 800000; + } else if (tech == ADI_AXI_FPGA_TECH_VERSAL) { + axi_clkgen->limits.fvco_max = 4320000; + axi_clkgen->limits.fvco_min = 2160000; } return 0; diff --git a/drivers/clk/clk-bulk.c b/drivers/clk/clk-bulk.c index d85dae4bdf89..acd9dff30072 100644 --- a/drivers/clk/clk-bulk.c +++ b/drivers/clk/clk-bulk.c @@ -12,7 +12,8 @@ #include <linux/of.h> #include <linux/slab.h> -static int __must_check of_clk_bulk_get(struct device_node *np, int num_clks, +static int __must_check of_clk_bulk_get(struct device *dev, + struct device_node *np, int num_clks, struct clk_bulk_data *clks) { int ret; @@ -28,8 +29,8 @@ static int __must_check of_clk_bulk_get(struct device_node *np, int num_clks, clks[i].clk = of_clk_get(np, i); if (IS_ERR(clks[i].clk)) { ret = PTR_ERR(clks[i].clk); - pr_err("%pOF: Failed to get clk index: %d ret: %d\n", - np, i, ret); + dev_err_probe(dev, ret, "%pOF: Failed to get clk index: %d (%s)\n", + np, i, clks[i].id); clks[i].clk = NULL; goto err; } @@ -43,7 +44,8 @@ err: return ret; } -static int __must_check of_clk_bulk_get_all(struct device_node *np, +static int __must_check of_clk_bulk_get_all(struct device *dev, + struct device_node *np, struct clk_bulk_data **clks) { struct clk_bulk_data *clk_bulk; @@ -58,7 +60,7 @@ static int __must_check of_clk_bulk_get_all(struct device_node *np, if (!clk_bulk) return -ENOMEM; - ret = of_clk_bulk_get(np, num_clks, clk_bulk); + ret = of_clk_bulk_get(dev, np, num_clks, clk_bulk); if (ret) { kfree(clk_bulk); return ret; @@ -144,7 +146,7 @@ int __must_check clk_bulk_get_all(struct device *dev, if (!np) return 0; - return of_clk_bulk_get_all(np, clks); + return of_clk_bulk_get_all(dev, np, clks); } EXPORT_SYMBOL(clk_bulk_get_all); diff --git a/drivers/clk/clk-k230.c b/drivers/clk/clk-k230.c new file mode 100644 index 000000000000..cfc437038e4e --- /dev/null +++ b/drivers/clk/clk-k230.c @@ -0,0 +1,2452 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Kendryte Canaan K230 Clock Drivers + * + * Author: Xukai Wang <kingxukai@zohomail.com> + * Author: Troy Mitchell <troymitchell988@gmail.com> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/iopoll.h> +#include <linux/platform_device.h> +#include <linux/rational.h> +#include <linux/spinlock.h> + +#include <dt-bindings/clock/canaan,k230-clk.h> + +/* PLL control register bits. */ +#define K230_PLL_BYPASS_ENABLE BIT(19) +#define K230_PLL_GATE_ENABLE BIT(2) +#define K230_PLL_GATE_WRITE_ENABLE BIT(18) +#define K230_PLL_OD_MASK GENMASK(27, 24) +#define K230_PLL_R_MASK GENMASK(21, 16) +#define K230_PLL_F_MASK GENMASK(12, 0) +#define K230_PLL_DIV_REG_OFFSET 0x00 +#define K230_PLL_BYPASS_REG_OFFSET 0x04 +#define K230_PLL_GATE_REG_OFFSET 0x08 +#define K230_PLL_LOCK_REG_OFFSET 0x0C + +/* PLL lock register */ +#define K230_PLL_LOCK_STATUS_MASK BIT(0) +#define K230_PLL_LOCK_TIME_DELAY 400 +#define K230_PLL_LOCK_TIMEOUT 0 + +/* K230 CLK registers offset */ +#define K230_CLK_AUDIO_CLKDIV_OFFSET 0x34 +#define K230_CLK_PDM_CLKDIV_OFFSET 0x40 +#define K230_CLK_CODEC_ADC_MCLKDIV_OFFSET 0x38 +#define K230_CLK_CODEC_DAC_MCLKDIV_OFFSET 0x3c + +#define K230_PLLX_DIV_ADDR(base, idx) \ + (K230_PLL_DIV_REG_OFFSET + (base) + (idx) * 0x10) + +#define K230_PLLX_BYPASS_ADDR(base, idx) \ + (K230_PLL_BYPASS_REG_OFFSET + (base) + (idx) * 0x10) + +#define K230_PLLX_GATE_ADDR(base, idx) \ + (K230_PLL_GATE_REG_OFFSET + (base) + (idx) * 0x10) + +#define K230_PLLX_LOCK_ADDR(base, idx) \ + (K230_PLL_LOCK_REG_OFFSET + (base) + (idx) * 0x10) + +#define K230_CLK_RATE_FORMAT_PNAME(_var, _id, \ + _mul_min, _mul_max, _mul_shift, _mul_mask, \ + _div_min, _div_max, _div_shift, _div_mask, \ + _reg, _bit, _method, _reg2, \ + _read_only, _flags, \ + _pname) \ + static struct k230_clk_rate _var = { \ + .div_reg_off = _reg, \ + .mul_reg_off = _reg2, \ + .id = _id, \ + .clk = { \ + .write_enable_bit = _bit, \ + .mul_min = _mul_min, \ + .mul_max = _mul_max, \ + .mul_shift = _mul_shift, \ + .mul_mask = _mul_mask, \ + .div_min = _div_min, \ + .div_max = _div_max, \ + .div_shift = _div_shift, \ + .div_mask = _div_mask, \ + .read_only = _read_only, \ + .hw.init = CLK_HW_INIT_FW_NAME(#_var, \ + _pname, &k230_clk_ops_##_method, \ + _flags), \ + }, \ + } + +#define K230_CLK_RATE_FORMAT(_var, _id, \ + _mul_min, _mul_max, _mul_shift, _mul_mask, \ + _div_min, _div_max, _div_shift, _div_mask, \ + _reg, _bit, _method, _reg2, \ + _read_only, _flags, \ + _phw) \ + static struct k230_clk_rate _var = { \ + .div_reg_off = _reg, \ + .mul_reg_off = _reg2, \ + .id = _id, \ + .clk = { \ + .write_enable_bit = _bit, \ + .mul_min = _mul_min, \ + .mul_max = _mul_max, \ + .mul_shift = _mul_shift, \ + .mul_mask = _mul_mask, \ + .div_min = _div_min, \ + .div_max = _div_max, \ + .div_shift = _div_shift, \ + .div_mask = _div_mask, \ + .read_only = _read_only, \ + .hw.init = CLK_HW_INIT_HW(#_var, \ + _phw, &k230_clk_ops_##_method, \ + _flags), \ + }, \ + } + +#define K230_CLK_GATE_FORMAT_PNAME(_var, _id, \ + _reg, _bit, _flags, _gate_flags, \ + _pname) \ + static struct k230_clk_gate _var = { \ + .reg_off = _reg, \ + .id = _id, \ + .clk = { \ + .bit_idx = _bit, \ + .flags = _gate_flags, \ + .hw.init = CLK_HW_INIT_FW_NAME(#_var, \ + _pname, &clk_gate_ops, _flags), \ + }, \ + } + +#define K230_CLK_GATE_FORMAT(_var, _id, \ + _reg, _bit, _flags, _gate_flags, \ + _phw) \ + static struct k230_clk_gate _var = { \ + .reg_off = _reg, \ + .id = _id, \ + .clk = { \ + .bit_idx = _bit, \ + .flags = _gate_flags, \ + .hw.init = CLK_HW_INIT_HW(#_var, \ + _phw, &clk_gate_ops, _flags), \ + }, \ + } + +#define K230_CLK_MUX_FORMAT(_var, _id, \ + _reg, _shift, _mask, _flags, _mux_flags, _pdata) \ + static struct k230_clk_mux _var = { \ + .reg_off = _reg, \ + .id = _id, \ + .clk = { \ + .flags = _mux_flags, \ + .shift = _shift, \ + .mask = _mask, \ + .hw.init = CLK_HW_INIT_PARENTS_DATA(#_var, \ + _pdata, &clk_mux_ops, _flags), \ + }, \ + } + +#define K230_CLK_FIXED_FACTOR_FORMAT(_var, \ + _mul, _div, _flags, \ + _phw) \ + static struct clk_fixed_factor _var = { \ + .mult = _mul, \ + .div = _div, \ + .hw.init = CLK_HW_INIT_HW(#_var, \ + _phw, &clk_fixed_factor_ops, _flags), \ + } + +#define K230_CLK_PLL_FORMAT(_var, _id, _flags, _pname) \ + static struct k230_pll _var = { \ + .hw.init = CLK_HW_INIT_FW_NAME(#_var, \ + _pname, &k230_pll_ops, _flags), \ + .id = _id, \ + } + +struct k230_pll { + struct clk_hw hw; + void __iomem *reg; + /* ensures mutual exclusion for concurrent register access. */ + spinlock_t *lock; + int id; +}; + +#define hw_to_k230_pll(_hw) container_of(_hw, struct k230_pll, hw) + +struct k230_clk_rate_self { + struct clk_hw hw; + void __iomem *reg; + bool read_only; + u32 write_enable_bit; + u32 mul_min; + u32 mul_max; + u32 mul_shift; + u32 mul_mask; + u32 div_min; + u32 div_max; + u32 div_shift; + u32 div_mask; + /* ensures mutual exclusion for concurrent register access. */ + spinlock_t *lock; +}; + +#define hw_to_k230_clk_rate_self(_hw) container_of(_hw, \ + struct k230_clk_rate_self, hw) + +struct k230_clk_rate { + u32 mul_reg_off; + u32 div_reg_off; + struct k230_clk_rate_self clk; + int id; +}; + +static inline struct k230_clk_rate *hw_to_k230_clk_rate(struct clk_hw *hw) +{ + return container_of(hw_to_k230_clk_rate_self(hw), struct k230_clk_rate, + clk); +} + +struct k230_clk_gate { + u32 reg_off; + struct clk_gate clk; + int id; +}; + +struct k230_clk_mux { + u32 reg_off; + struct clk_mux clk; + int id; +}; + +static int k230_pll_prepare(struct clk_hw *hw); +static int k230_pll_enable(struct clk_hw *hw); +static void k230_pll_disable(struct clk_hw *hw); +static int k230_pll_is_enabled(struct clk_hw *hw); +static unsigned long k230_pll_get_rate(struct clk_hw *hw, unsigned long parent_rate); +static int k230_clk_set_rate_mul(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); +static int k230_clk_determine_rate_mul(struct clk_hw *hw, struct clk_rate_request *req); +static unsigned long k230_clk_get_rate_mul(struct clk_hw *hw, + unsigned long parent_rate); +static int k230_clk_set_rate_div(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); +static int k230_clk_determine_rate_div(struct clk_hw *hw, struct clk_rate_request *req); +static unsigned long k230_clk_get_rate_div(struct clk_hw *hw, + unsigned long parent_rate); +static int k230_clk_set_rate_mul_div(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); +static int k230_clk_determine_rate_mul_div(struct clk_hw *hw, struct clk_rate_request *req); +static unsigned long k230_clk_get_rate_mul_div(struct clk_hw *hw, + unsigned long parent_rate); + +static const struct clk_ops k230_pll_ops = { + .prepare = k230_pll_prepare, + .enable = k230_pll_enable, + .disable = k230_pll_disable, + .is_enabled = k230_pll_is_enabled, + .recalc_rate = k230_pll_get_rate, +}; + +/* clk_ops for clocks whose rate is determined by a configurable multiplier */ +static const struct clk_ops k230_clk_ops_mul = { + .set_rate = k230_clk_set_rate_mul, + .determine_rate = k230_clk_determine_rate_mul, + .recalc_rate = k230_clk_get_rate_mul, +}; + +/* clk_ops for clocks whose rate is determined by a configurable divider */ +static const struct clk_ops k230_clk_ops_div = { + .set_rate = k230_clk_set_rate_div, + .determine_rate = k230_clk_determine_rate_div, + .recalc_rate = k230_clk_get_rate_div, +}; + +/* clk_ops for clocks whose rate is determined by both a multiplier and a divider */ +static const struct clk_ops k230_clk_ops_mul_div = { + .set_rate = k230_clk_set_rate_mul_div, + .determine_rate = k230_clk_determine_rate_mul_div, + .recalc_rate = k230_clk_get_rate_mul_div, +}; + +K230_CLK_PLL_FORMAT(pll0, 0, CLK_IS_CRITICAL, NULL); +K230_CLK_PLL_FORMAT(pll1, 1, CLK_IS_CRITICAL, NULL); +K230_CLK_PLL_FORMAT(pll2, 2, CLK_IS_CRITICAL, NULL); +K230_CLK_PLL_FORMAT(pll3, 3, CLK_IS_CRITICAL, NULL); + +static struct k230_pll *k230_plls[] = { + &pll0, + &pll1, + &pll2, + &pll3, +}; + +K230_CLK_FIXED_FACTOR_FORMAT(pll0_div2, 1, 2, 0, &pll0.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll0_div3, 1, 3, 0, &pll0.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll0_div4, 1, 4, 0, &pll0.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll0_div16, 1, 16, 0, &pll0.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll1_div2, 1, 2, 0, &pll1.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll1_div3, 1, 3, 0, &pll1.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll1_div4, 1, 4, 0, &pll1.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll2_div2, 1, 2, 0, &pll2.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll2_div3, 1, 3, 0, &pll2.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll2_div4, 1, 4, 0, &pll2.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll3_div2, 1, 2, 0, &pll3.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll3_div3, 1, 3, 0, &pll3.hw); +K230_CLK_FIXED_FACTOR_FORMAT(pll3_div4, 1, 4, 0, &pll3.hw); + +static struct clk_fixed_factor *k230_pll_divs[] = { + &pll0_div2, + &pll0_div3, + &pll0_div4, + &pll0_div16, + &pll1_div2, + &pll1_div3, + &pll1_div4, + &pll2_div2, + &pll2_div3, + &pll2_div4, + &pll3_div2, + &pll3_div3, + &pll3_div4, +}; + +K230_CLK_GATE_FORMAT(cpu0_src_gate, + K230_CPU0_SRC_GATE, + 0, 0, CLK_IS_CRITICAL, 0, + &pll0_div2.hw); + +K230_CLK_RATE_FORMAT(cpu0_src_rate, + K230_CPU0_SRC_RATE, + 1, 16, 1, 0xF, + 16, 16, 0, 0x0, + 0x0, 31, mul, 0x0, + false, 0, + &cpu0_src_gate.clk.hw); + +K230_CLK_RATE_FORMAT(cpu0_axi_rate, + K230_CPU0_AXI_RATE, + 1, 1, 0, 0, + 1, 8, 6, 0x7, + 0x0, 31, div, 0x0, + 0, 0, + &cpu0_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(cpu0_plic_gate, + K230_CPU0_PLIC_GATE, + 0x0, 9, CLK_IS_CRITICAL, 0, + &cpu0_src_rate.clk.hw); + +K230_CLK_RATE_FORMAT(cpu0_plic_rate, + K230_CPU0_PLIC_RATE, + 1, 1, 0, 0, + 1, 8, 10, 0x7, + 0x0, 31, div, 0x0, + false, 0, + &cpu0_plic_gate.clk.hw); + +K230_CLK_GATE_FORMAT(cpu0_noc_ddrcp4_gate, + K230_CPU0_NOC_DDRCP4_GATE, + 0x60, 7, CLK_IS_CRITICAL, 0, + &cpu0_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(cpu0_apb_gate, + K230_CPU0_APB_GATE, + 0x0, 13, CLK_IS_CRITICAL, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(cpu0_apb_rate, + K230_CPU0_APB_RATE, + 1, 1, 0, 0, + 1, 8, 15, 0x7, + 0x0, 31, div, 0x0, + false, 0, + &cpu0_apb_gate.clk.hw); + +static const struct clk_parent_data k230_cpu1_src_mux_pdata[] = { + { .hw = &pll1_div2.hw, }, + { .hw = &pll3.hw, }, + { .hw = &pll0.hw, }, +}; + +K230_CLK_MUX_FORMAT(cpu1_src_mux, + K230_CPU1_SRC_MUX, + 0x4, 1, 0x3, + 0, 0, + k230_cpu1_src_mux_pdata); + +K230_CLK_GATE_FORMAT(cpu1_src_gate, + K230_CPU1_SRC_GATE, + 0x4, 0, CLK_IS_CRITICAL, 0, + &cpu1_src_mux.clk.hw); + +K230_CLK_RATE_FORMAT(cpu1_src_rate, + K230_CPU1_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 3, 0x7, + 0x4, 31, div, 0x0, + false, 0, + &cpu1_src_gate.clk.hw); + +K230_CLK_RATE_FORMAT(cpu1_axi_rate, + K230_CPU1_AXI_RATE, + 1, 1, 0, 0, + 1, 8, 12, 0x7, + 0x4, 31, div, 0x0, + false, 0, + &cpu1_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(cpu1_plic_gate, + K230_CPU1_PLIC_GATE, + 0x4, 15, CLK_IS_CRITICAL, 0, + &cpu1_src_rate.clk.hw); + +K230_CLK_RATE_FORMAT(cpu1_plic_rate, + K230_CPU1_PLIC_RATE, + 1, 1, 0, 0, + 1, 8, 16, 0x7, + 0x4, 31, div, 0x0, + false, 0, + &cpu1_plic_gate.clk.hw); + +K230_CLK_GATE_FORMAT(cpu1_apb_gate, + K230_CPU1_APB_GATE, + 0x4, 19, CLK_IS_CRITICAL, 0, + &pll0_div4.hw); + +K230_CLK_GATE_FORMAT_PNAME(pmu_apb_gate, + K230_PMU_APB_GATE, + 0x10, 0, 0, 0, + "osc24m"); + +K230_CLK_GATE_FORMAT(hs_hclk_high_gate, + K230_HS_HCLK_HIGH_GATE, + 0x18, 1, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(hs_hclk_high_rate, + K230_HS_HCLK_HIGH_RATE, + 1, 1, 0, 0, + 1, 8, 0, 0x7, + 0x1C, 31, div, 0x0, + false, 0, + &hs_hclk_high_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_hclk_gate, + K230_HS_HCLK_GATE, + 0x18, 0, 0, 0, + &hs_hclk_high_rate.clk.hw); + +K230_CLK_RATE_FORMAT(hs_hclk_rate, + K230_HS_HCLK_RATE, + 1, 1, 0, 0, + 1, 8, 3, 0x7, + 0x1C, 31, div, 0x0, + false, 0, + &hs_hclk_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd0_ahb_gate, + K230_HS_SD0_AHB_GATE, + 0x18, 2, 0, 0, + &hs_hclk_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd1_ahb_gate, + K230_HS_SD1_AHB_GATE, + 0x18, 3, 0, 0, + &hs_hclk_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_ssi1_ahb_gate, + K230_HS_SSI1_AHB_GATE, + 0x18, 7, 0, 0, + &hs_hclk_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_ssi2_ahb_gate, + K230_HS_SSI2_AHB_GATE, + 0x18, 8, 0, 0, + &hs_hclk_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_usb0_ahb_gate, + K230_HS_USB0_AHB_GATE, + 0x18, 4, 0, 0, + &hs_hclk_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_usb1_ahb_gate, + K230_HS_USB1_AHB_GATE, + 0x18, 5, 0, 0, + &hs_hclk_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_ssi0_axi_gate, + K230_HS_SSI0_AXI_GATE, + 0x18, 27, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(hs_ssi0_axi_rate, + K230_HS_SSI0_AXI_RATE, + 1, 1, 0, 0, + 1, 8, 9, 0x7, + 0x20, 31, div, 0x0, + false, 0, + &hs_ssi0_axi_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_ssi1_gate, + K230_HS_SSI1_GATE, + 0x18, 25, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(hs_ssi1_rate, + K230_HS_SSI1_RATE, + 1, 1, 0, 0, + 1, 8, 3, 0x7, + 0x20, 31, div, 0x0, + false, 0, + &hs_ssi1_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_ssi2_gate, + K230_HS_SSI2_GATE, + 0x18, 26, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(hs_ssi2_rate, + K230_HS_SSI2_RATE, + 1, 1, 0, 0, + 1, 8, 6, 0x7, + 0x20, 31, div, 0x0, + false, 0, + &hs_ssi2_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_qspi_axi_src_gate, + K230_HS_QSPI_AXI_SRC_GATE, + 0x18, 28, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(hs_qspi_axi_src_rate, + K230_HS_QSPI_AXI_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 12, 0x7, + 0x20, 31, div, 0x0, + false, 0, + &hs_qspi_axi_src_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_ssi1_axi_gate, + K230_HS_SSI1_AXI_GATE, + 0x18, 29, 0, 0, + &hs_qspi_axi_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_ssi2_axi_gate, + K230_HS_SSI2_AXI_GATE, + 0x18, 30, 0, 0, + &hs_qspi_axi_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd_card_src_gate, + K230_HS_SD_CARD_SRC_GATE, + 0x18, 11, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(hs_sd_card_src_rate, + K230_HS_SD_CARD_SRC_RATE, + 1, 1, 0, 0, + 2, 8, 12, 0x7, + 0x1C, 31, div, 0x0, + false, 0, + &hs_sd_card_src_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd0_card_gate, + K230_HS_SD0_CARD_GATE, + 0x18, 15, 0, 0, + &hs_sd_card_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd1_card_gate, + K230_HS_SD1_CARD_GATE, + 0x18, 19, 0, 0, + &hs_sd_card_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd_axi_src_gate, + K230_HS_SD_AXI_SRC_GATE, + 0x18, 9, 0, 0, + &pll2_div4.hw); + +K230_CLK_RATE_FORMAT(hs_sd_axi_src_rate, + K230_HS_SD_AXI_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 6, 0x7, + 0x1C, 31, div, 0x0, + false, 0, + &hs_sd_axi_src_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd0_axi_gate, + K230_HS_SD0_AXI_GATE, + 0x18, 13, 0, 0, + &hs_sd_axi_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd1_axi_gate, + K230_HS_SD1_AXI_GATE, + 0x18, 17, 0, 0, + &hs_sd_axi_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd0_base_gate, + K230_HS_SD0_BASE_GATE, + 0x18, 14, 0, 0, + &hs_sd_axi_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd1_base_gate, + K230_HS_SD1_BASE_GATE, + 0x18, 18, 0, 0, + &hs_sd_axi_src_rate.clk.hw); + +static const struct clk_parent_data k230_hs_ssi0_mux_pdata[] = { + { .hw = &pll0_div2.hw, }, + { .hw = &pll2_div4.hw, }, +}; + +K230_CLK_MUX_FORMAT(hs_ssi0_mux, + K230_HS_SSI0_MUX, + 0x20, 18, 0x1, + 0, 0, + k230_hs_ssi0_mux_pdata); + +K230_CLK_GATE_FORMAT(hs_ssi0_gate, + K230_HS_SSI0_GATE, + 0x18, 24, CLK_IGNORE_UNUSED, 0, + &hs_ssi0_mux.clk.hw); + +K230_CLK_RATE_FORMAT(hs_usb_ref_50m_rate, + K230_HS_USB_REF_50M_RATE, + 1, 1, 0, 0, + 1, 8, 15, 0x7, + 0x20, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +K230_CLK_GATE_FORMAT_PNAME(hs_sd_timer_src_gate, + K230_HS_SD_TIMER_SRC_GATE, + 0x18, 12, 0, 0, + "osc24m"); + +K230_CLK_RATE_FORMAT(hs_sd_timer_src_rate, + K230_HS_SD_TIMER_SRC_RATE, + 1, 1, 0, 0, + 24, 32, 15, 0x1F, + 0x1C, 31, div, 0x0, + false, 0, + &hs_sd_timer_src_gate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd0_timer_gate, + K230_HS_SD0_TIMER_GATE, + 0x18, 16, 0, 0, + &hs_sd_timer_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(hs_sd1_timer_gate, + K230_HS_SD1_TIMER_GATE, + 0x18, 20, 0, 0, + &hs_sd_timer_src_rate.clk.hw); + +static const struct clk_parent_data k230_hs_usb_ref_mux_pdata[] = { + { .fw_name = "osc24m", }, + { .hw = &hs_usb_ref_50m_rate.clk.hw, }, +}; + +K230_CLK_MUX_FORMAT(hs_usb_ref_mux, + K230_HS_USB_REF_MUX, + 0x18, 23, 0x1, + 0, 0, + k230_hs_usb_ref_mux_pdata); + +K230_CLK_GATE_FORMAT(hs_usb0_ref_gate, + K230_HS_USB0_REF_GATE, + 0x18, 21, CLK_IGNORE_UNUSED, 0, + &hs_usb_ref_mux.clk.hw); + +K230_CLK_GATE_FORMAT(hs_usb1_ref_gate, + K230_HS_USB1_REF_GATE, + 0x18, 22, CLK_IGNORE_UNUSED, 0, + &hs_usb_ref_mux.clk.hw); + +K230_CLK_GATE_FORMAT(ls_apb_src_gate, + K230_LS_APB_SRC_GATE, + 0x24, 0, CLK_IS_CRITICAL, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_apb_src_rate, + K230_LS_APB_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 0, 0x7, + 0x30, 31, div, 0x0, + false, 0, + &ls_apb_src_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart0_apb_gate, + K230_LS_UART0_APB_GATE, + 0x24, 1, CLK_IS_CRITICAL, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart1_apb_gate, + K230_LS_UART1_APB_GATE, + 0x24, 2, CLK_IS_CRITICAL, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart2_apb_gate, + K230_LS_UART2_APB_GATE, + 0x24, 3, CLK_IS_CRITICAL, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart3_apb_gate, + K230_LS_UART3_APB_GATE, + 0x24, 4, CLK_IS_CRITICAL, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart4_apb_gate, + K230_LS_UART4_APB_GATE, + 0x24, 5, CLK_IS_CRITICAL, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c0_apb_gate, + K230_LS_I2C0_APB_GATE, + 0x24, 6, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c1_apb_gate, + K230_LS_I2C1_APB_GATE, + 0x24, 7, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c2_apb_gate, + K230_LS_I2C2_APB_GATE, + 0x24, 8, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c3_apb_gate, + K230_LS_I2C3_APB_GATE, + 0x24, 9, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c4_apb_gate, + K230_LS_I2C4_APB_GATE, + 0x24, 10, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_gpio_apb_gate, + K230_LS_GPIO_APB_GATE, + 0x24, 11, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_pwm_apb_gate, + K230_LS_PWM_APB_GATE, + 0x24, 12, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink0_apb_gate, + K230_LS_JAMLINK0_APB_GATE, + 0x28, 4, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink1_apb_gate, + K230_LS_JAMLINK1_APB_GATE, + 0x28, 5, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink2_apb_gate, + K230_LS_JAMLINK2_APB_GATE, + 0x28, 6, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink3_apb_gate, + K230_LS_JAMLINK3_APB_GATE, + 0x28, 7, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_audio_apb_gate, + K230_LS_AUDIO_APB_GATE, + 0x24, 13, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_adc_apb_gate, + K230_LS_ADC_APB_GATE, + 0x24, 15, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_codec_apb_gate, + K230_LS_CODEC_APB_GATE, + 0x24, 14, 0, 0, + &ls_apb_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c0_gate, + K230_LS_I2C0_GATE, + 0x24, 21, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_i2c0_rate, + K230_LS_I2C0_RATE, + 1, 1, 0, 0, + 1, 8, 15, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_i2c0_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c1_gate, + K230_LS_I2C1_GATE, + 0x24, 22, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_i2c1_rate, + K230_LS_I2C1_RATE, + 1, 1, 0, 0, + 1, 8, 18, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_i2c1_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c2_gate, + K230_LS_I2C2_GATE, + 0x24, 23, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_i2c2_rate, + K230_LS_I2C2_RATE, + 1, 1, 0, 0, + 1, 8, 21, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_i2c2_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c3_gate, + K230_LS_I2C3_GATE, + 0x24, 24, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_i2c3_rate, + K230_LS_I2C3_RATE, + 1, 1, 0, 0, + 1, 8, 24, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_i2c3_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_i2c4_gate, + K230_LS_I2C4_GATE, + 0x24, 25, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_i2c4_rate, + K230_LS_I2C4_RATE, + 1, 1, 0, 0, + 1, 8, 27, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_i2c4_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_codec_adc_gate, + K230_LS_CODEC_ADC_GATE, + 0x24, 29, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_codec_adc_rate, + K230_LS_CODEC_ADC_RATE, + 0x10, 0x1B9, 14, 0x1FFF, + 0xC35, 0x3D09, 0, 0x3FFF, + 0x38, 31, mul_div, 0x38, + false, 0, + &ls_codec_adc_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_codec_dac_gate, + K230_LS_CODEC_DAC_GATE, + 0x24, 30, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_codec_dac_rate, + K230_LS_CODEC_DAC_RATE, + 0x10, 0x1B9, 14, 0x1FFF, + 0xC35, 0x3D09, 0, 0x3FFF, + 0x3C, 31, mul_div, 0x3C, + false, 0, + &ls_codec_dac_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_audio_dev_gate, + K230_LS_AUDIO_DEV_GATE, + 0x24, 28, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_audio_dev_rate, + K230_LS_AUDIO_DEV_RATE, + 0x4, 0x1B9, 16, 0x7FFF, + 0xC35, 0xF424, 0, 0xFFFF, + 0x34, 31, mul_div, 0x34, + false, 0, + &ls_audio_dev_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_pdm_gate, + K230_LS_PDM_GATE, + 0x24, 31, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_pdm_rate, + K230_LS_PDM_RATE, + 0x2, 0x1B9, 0, 0xFFFF, + 0xC35, 0x1E848, 0, 0x1FFFF, + 0x40, 0, mul_div, 0x44, + false, 0, + &ls_pdm_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_adc_gate, + K230_LS_ADC_GATE, + 0x24, 26, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ls_adc_rate, + K230_LS_ADC_RATE, + 1, 1, 0, 0, + 1, 1024, 3, 0x3FF, + 0x30, 31, div, 0x0, + false, 0, + &ls_adc_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart0_gate, + K230_LS_UART0_GATE, + 0x24, 16, CLK_IS_CRITICAL, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(ls_uart0_rate, + K230_LS_UART0_RATE, + 1, 1, 0, 0, + 1, 8, 0, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_uart0_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart1_gate, + K230_LS_UART1_GATE, + 0x24, 17, CLK_IS_CRITICAL, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(ls_uart1_rate, + K230_LS_UART1_RATE, + 1, 1, 0, 0, + 1, 8, 3, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_uart1_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart2_gate, + K230_LS_UART2_GATE, + 0x24, 18, CLK_IS_CRITICAL, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(ls_uart2_rate, + K230_LS_UART2_RATE, + 1, 1, 0, 0, + 1, 8, 6, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_uart2_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart3_gate, + K230_LS_UART3_GATE, + 0x24, 19, CLK_IS_CRITICAL, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(ls_uart3_rate, + K230_LS_UART3_RATE, + 1, 1, 0, 0, + 1, 8, 9, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_uart3_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_uart4_gate, + K230_LS_UART4_GATE, + 0x24, 20, CLK_IS_CRITICAL, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(ls_uart4_rate, + K230_LS_UART4_RATE, + 1, 1, 0, 0, + 1, 8, 12, 0x7, + 0x2C, 31, div, 0x0, + false, 0, + &ls_uart4_gate.clk.hw); + +K230_CLK_RATE_FORMAT(ls_jamlinkco_src_rate, + K230_LS_JAMLINKCO_SRC_RATE, + 1, 1, 0, 0, + 2, 512, 23, 0xFF, + 0x30, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink0co_gate, + K230_LS_JAMLINK0CO_GATE, + 0x28, 0, 0, 0, + &ls_jamlinkco_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink1co_gate, + K230_LS_JAMLINK1CO_GATE, + 0x28, 1, 0, 0, + &ls_jamlinkco_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink2co_gate, + K230_LS_JAMLINK2CO_GATE, + 0x28, 2, 0, 0, + &ls_jamlinkco_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(ls_jamlink3co_gate, + K230_LS_JAMLINK3CO_GATE, + 0x28, 3, 0, 0, + &ls_jamlinkco_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT_PNAME(ls_gpio_debounce_gate, + K230_LS_GPIO_DEBOUNCE_GATE, + 0x24, 27, 0, 0, + "osc24m"); + +K230_CLK_RATE_FORMAT(ls_gpio_debounce_rate, + K230_LS_GPIO_DEBOUNCE_RATE, + 1, 1, 0, 0, + 1, 1024, 13, 0x3FF, + 0x30, 31, div, 0x0, + false, 0, + &ls_gpio_debounce_gate.clk.hw); + +K230_CLK_GATE_FORMAT(sysctl_wdt0_apb_gate, + K230_SYSCTL_WDT0_APB_GATE, + 0x50, 1, 0, 0, + &pll0_div16.hw); + +K230_CLK_GATE_FORMAT(sysctl_wdt1_apb_gate, + K230_SYSCTL_WDT1_APB_GATE, + 0x50, 2, 0, 0, + &pll0_div16.hw); + +K230_CLK_GATE_FORMAT(sysctl_timer_apb_gate, + K230_SYSCTL_TIMER_APB_GATE, + 0x50, 3, 0, 0, + &pll0_div16.hw); + +K230_CLK_GATE_FORMAT(sysctl_iomux_apb_gate, + K230_SYSCTL_IOMUX_APB_GATE, + 0x50, 20, 0, 0, + &pll0_div16.hw); + +K230_CLK_GATE_FORMAT(sysctl_mailbox_apb_gate, + K230_SYSCTL_MAILBOX_APB_GATE, + 0x50, 4, 0, 0, + &pll0_div16.hw); + +K230_CLK_GATE_FORMAT(sysctl_hdi_gate, + K230_SYSCTL_HDI_GATE, + 0x50, 21, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(sysctl_hdi_rate, + K230_SYSCTL_HDI_RATE, + 1, 1, 0, 0, + 1, 8, 28, 0x7, + 0x58, 31, div, 0x0, + false, 0, + &sysctl_hdi_gate.clk.hw); + +K230_CLK_GATE_FORMAT(sysctl_time_stamp_gate, + K230_SYSCTL_TIME_STAMP_GATE, + 0x50, 19, CLK_IS_CRITICAL, 0, + &pll1_div4.hw); + +K230_CLK_RATE_FORMAT(sysctl_time_stamp_rate, + K230_SYSCTL_TIME_STAMP_RATE, + 1, 1, 0, 0, + 1, 32, 15, 0x1F, + 0x58, 31, div, 0x0, + false, 0, + &sysctl_time_stamp_gate.clk.hw); + +K230_CLK_RATE_FORMAT_PNAME(sysctl_temp_sensor_rate, + K230_SYSCTL_TEMP_SENSOR_RATE, + 1, 1, 0, 0, + 1, 256, 20, 0xFF, + 0x58, 31, div, 0x0, + false, 0, + "osc24m"); + +K230_CLK_GATE_FORMAT_PNAME(sysctl_wdt0_gate, + K230_SYSCTL_WDT0_GATE, + 0x50, 5, 0, 0, + "osc24m"); + +K230_CLK_RATE_FORMAT(sysctl_wdt0_rate, + K230_SYSCTL_WDT0_RATE, + 1, 1, 0, 0, + 1, 64, 3, 0x3F, + 0x58, 31, div, 0x0, + false, 0, + &sysctl_wdt0_gate.clk.hw); + +K230_CLK_GATE_FORMAT_PNAME(sysctl_wdt1_gate, + K230_SYSCTL_WDT1_GATE, + 0x50, 6, 0, 0, + "osc24m"); + +K230_CLK_RATE_FORMAT(sysctl_wdt1_rate, + K230_SYSCTL_WDT1_RATE, + 1, 1, 0, 0, + 1, 64, 9, 0x3F, + 0x58, 31, div, 0x0, + false, 0, + &sysctl_wdt1_gate.clk.hw); + +K230_CLK_RATE_FORMAT(timer0_src_rate, + K230_TIMER0_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 0, 0x7, + 0x54, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(timer1_src_rate, + K230_TIMER1_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 3, 0x7, + 0x54, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(timer2_src_rate, + K230_TIMER2_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 6, 0x7, + 0x54, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(timer3_src_rate, + K230_TIMER3_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 9, 0x7, + 0x54, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(timer4_src_rate, + K230_TIMER4_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 12, 0x7, + 0x54, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +K230_CLK_RATE_FORMAT(timer5_src_rate, + K230_TIMER5_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 15, 0x7, + 0x54, 31, div, 0x0, + false, 0, + &pll0_div16.hw); + +static const struct clk_parent_data k230_timer0_mux_pdata[] = { + { .fw_name = "timer-pulse-in", }, + { .hw = &timer0_src_rate.clk.hw, }, +}; + +K230_CLK_MUX_FORMAT(timer0_mux, + K230_TIMER0_MUX, + 0x50, 7, 0x1, + 0, 0, + k230_timer0_mux_pdata); + +K230_CLK_GATE_FORMAT(timer0_gate, + K230_TIMER0_GATE, + 0x50, 13, CLK_IGNORE_UNUSED, 0, + &timer0_mux.clk.hw); + +static const struct clk_parent_data k230_timer1_mux_pdata[] = { + { .fw_name = "timer-pulse-in", }, + { .hw = &timer1_src_rate.clk.hw, }, +}; + +K230_CLK_MUX_FORMAT(timer1_mux, + K230_TIMER1_MUX, + 0x50, 8, 0x1, + 0, 0, + k230_timer1_mux_pdata); + +K230_CLK_GATE_FORMAT(timer1_gate, + K230_TIMER1_GATE, + 0x50, 14, CLK_IGNORE_UNUSED, 0, + &timer1_mux.clk.hw); + +static const struct clk_parent_data k230_timer2_mux_pdata[] = { + { .fw_name = "timer-pulse-in", }, + { .hw = &timer2_src_rate.clk.hw, }, +}; + +K230_CLK_MUX_FORMAT(timer2_mux, + K230_TIMER2_MUX, + 0x50, 9, 0x1, + 0, 0, + k230_timer2_mux_pdata); + +K230_CLK_GATE_FORMAT(timer2_gate, + K230_TIMER2_GATE, + 0x50, 15, CLK_IGNORE_UNUSED, 0, + &timer2_mux.clk.hw); + +static const struct clk_parent_data k230_timer3_mux_pdata[] = { + { .fw_name = "timer-pulse-in", }, + { .hw = &timer3_src_rate.clk.hw, }, +}; + +K230_CLK_MUX_FORMAT(timer3_mux, + K230_TIMER3_MUX, + 0x50, 10, 0x1, + 0, 0, + k230_timer3_mux_pdata); + +K230_CLK_GATE_FORMAT(timer3_gate, + K230_TIMER3_GATE, + 0x50, 16, CLK_IGNORE_UNUSED, 0, + &timer3_mux.clk.hw); + +static const struct clk_parent_data k230_timer4_mux_pdata[] = { + { .fw_name = "timer-pulse-in", }, + { .hw = &timer4_src_rate.clk.hw, }, +}; + +K230_CLK_MUX_FORMAT(timer4_mux, + K230_TIMER4_MUX, + 0x50, 11, 0x1, + 0, 0, + k230_timer4_mux_pdata); + +K230_CLK_GATE_FORMAT(timer4_gate, + K230_TIMER4_GATE, + 0x50, 17, CLK_IGNORE_UNUSED, 0, + &timer4_mux.clk.hw); + +static const struct clk_parent_data k230_timer5_mux_pdata[] = { + { .fw_name = "timer-pulse-in", }, + { .hw = &timer5_src_rate.clk.hw, }, +}; + +K230_CLK_MUX_FORMAT(timer5_mux, + K230_TIMER5_MUX, + 0x50, 12, 0x1, + 0, 0, + k230_timer5_mux_pdata); + +K230_CLK_GATE_FORMAT(timer5_gate, + K230_TIMER5_GATE, + 0x50, 18, CLK_IGNORE_UNUSED, 0, + &timer5_mux.clk.hw); + +K230_CLK_GATE_FORMAT(shrm_apb_gate, + K230_SHRM_APB_GATE, + 0x5C, 0, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(shrm_apb_rate, + K230_SHRM_APB_RATE, + 1, 1, 0, 0, + 1, 8, 18, 0x7, + 0x5C, 31, div, 0x0, + false, 0, + &shrm_apb_gate.clk.hw); + +static const struct clk_parent_data k230_shrm_sram_mux_pdata[] = { + { .hw = &pll3_div2.hw, }, + { .hw = &pll0_div2.hw, }, +}; + +K230_CLK_MUX_FORMAT(shrm_sram_mux, + K230_SHRM_SRAM_MUX, + 0x50, 14, 0x1, + 0, 0, + k230_shrm_sram_mux_pdata); + +K230_CLK_GATE_FORMAT(shrm_sram_gate, + K230_SHRM_SRAM_GATE, + 0x5c, 10, CLK_IGNORE_UNUSED, 0, + &shrm_sram_mux.clk.hw); + +K230_CLK_FIXED_FACTOR_FORMAT(shrm_sram_div2, + 1, 2, 0, + &shrm_sram_gate.clk.hw); + +K230_CLK_GATE_FORMAT(shrm_axi_slave_gate, + K230_SHRM_AXI_SLAVE_GATE, + 0x5C, 11, CLK_IGNORE_UNUSED, 0, + &shrm_sram_div2.hw); + +K230_CLK_GATE_FORMAT(shrm_axi_gate, + K230_SHRM_AXI_GATE, + 0x5C, 12, 0, 0, + &pll0_div4.hw); + +K230_CLK_GATE_FORMAT(shrm_nonai2d_axi_gate, + K230_SHRM_NONAI2D_AXI_GATE, + 0x5C, 9, 0, 0, + &shrm_axi_gate.clk.hw); + +K230_CLK_GATE_FORMAT(shrm_decompress_axi_gate, + K230_SHRM_DECOMPRESS_AXI_GATE, + 0x5C, 7, CLK_IGNORE_UNUSED, 0, + &shrm_sram_gate.clk.hw); + +K230_CLK_GATE_FORMAT(shrm_sdma_axi_gate, + K230_SHRM_SDMA_AXI_GATE, + 0x5C, 5, 0, 0, + &shrm_axi_gate.clk.hw); + +K230_CLK_GATE_FORMAT(shrm_pdma_axi_gate, + K230_SHRM_PDMA_AXI_GATE, + 0x5C, 3, 0, 0, + &shrm_axi_gate.clk.hw); + +static const struct clk_parent_data k230_ddrc_src_mux_pdata[] = { + { .hw = &pll0_div2.hw, }, + { .hw = &pll0_div3.hw, }, + { .hw = &pll2_div4.hw, }, +}; + +K230_CLK_MUX_FORMAT(ddrc_src_mux, + K230_DDRC_SRC_MUX, + 0x60, 0, 0x3, + 0, 0, + k230_ddrc_src_mux_pdata); + +K230_CLK_GATE_FORMAT(ddrc_src_gate, + K230_DDRC_SRC_GATE, + 0x60, 2, CLK_IGNORE_UNUSED, 0, + &ddrc_src_mux.clk.hw); + +K230_CLK_RATE_FORMAT(ddrc_src_rate, + K230_DDRC_SRC_RATE, + 1, 1, 0, 0, + 1, 16, 10, 0xF, + 0x60, 31, div, 0x0, + false, 0, + &ddrc_src_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ddrc_bypass_gate, + K230_DDRC_BYPASS_GATE, + 0x60, 8, 0, 0, + &pll2_div4.hw); + +K230_CLK_GATE_FORMAT(ddrc_apb_gate, + K230_DDRC_APB_GATE, + 0x60, 9, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(ddrc_apb_rate, + K230_DDRC_APB_RATE, + 1, 1, 0, 0, + 1, 16, 14, 0xF, + 0x60, 31, div, 0x0, + false, 0, + &ddrc_apb_gate.clk.hw); + +K230_CLK_GATE_FORMAT(display_ahb_gate, + K230_DISPLAY_AHB_GATE, + 0x74, 0, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(display_ahb_rate, + K230_DISPLAY_AHB_RATE, + 1, 1, 0, 0, + 1, 8, 0, 0x7, + 0x78, 31, div, 0x0, + false, 0, + &display_ahb_gate.clk.hw); + +K230_CLK_GATE_FORMAT(display_axi_gate, + K230_DISPLAY_AXI_GATE, + 0x74, 1, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(display_clkext_rate, + K230_DISPLAY_CLKEXT_RATE, + 1, 1, 0, 0, + 1, 16, 16, 0xF, + 0x78, 31, div, 0x0, + false, 0, + &pll0_div3.hw); + +K230_CLK_GATE_FORMAT(display_gpu_gate, + K230_DISPLAY_GPU_GATE, + 0x74, 6, 0, 0, + &pll0_div3.hw); + +K230_CLK_RATE_FORMAT(display_gpu_rate, + K230_DISPLAY_GPU_RATE, + 1, 1, 0, 0, + 1, 16, 20, 0xF, + 0x78, 31, div, 0x0, + false, 0, + &display_gpu_gate.clk.hw); + +K230_CLK_GATE_FORMAT(display_dpip_gate, + K230_DISPLAY_DPIP_GATE, + 0x74, 2, 0, 0, + &pll1_div4.hw); + +K230_CLK_RATE_FORMAT(display_dpip_rate, + K230_DISPLAY_DPIP_RATE, + 1, 1, 0, 0, + 1, 256, 3, 0xFF, + 0x78, 31, div, 0x0, + false, 0, + &display_dpip_gate.clk.hw); + +K230_CLK_GATE_FORMAT(display_cfg_gate, + K230_DISPLAY_CFG_GATE, + 0x74, 4, 0, 0, + &pll1_div4.hw); + +K230_CLK_RATE_FORMAT(display_cfg_rate, + K230_DISPLAY_CFG_RATE, + 1, 1, 0, 0, + 1, 32, 11, 0x1F, + 0x78, 31, div, 0x0, + false, 0, + &display_cfg_gate.clk.hw); + +K230_CLK_GATE_FORMAT_PNAME(display_ref_gate, + K230_DISPLAY_REF_GATE, + 0x74, 3, 0, 0, + "osc24m"); + +K230_CLK_GATE_FORMAT(vpu_src_gate, + K230_VPU_SRC_GATE, + 0xC, 0, 0, 0, + &pll0_div2.hw); + +K230_CLK_RATE_FORMAT(vpu_src_rate, + K230_VPU_SRC_RATE, + 1, 16, 1, 0xF, + 16, 16, 0, 0, + 0x0, 31, mul, 0xC, + false, 0, + &vpu_src_gate.clk.hw); + +K230_CLK_RATE_FORMAT(vpu_axi_src_rate, + K230_VPU_AXI_SRC_RATE, + 1, 1, 0, 0, + 1, 16, 6, 0xF, + 0xC, 31, div, 0x0, + false, 0, + &vpu_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(vpu_axi_gate, + K230_VPU_AXI_GATE, + 0xC, 5, 0, 0, + &vpu_axi_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(vpu_ddrcp2_gate, + K230_VPU_DDRCP2_GATE, + 0x60, 5, 0, 0, + &vpu_axi_src_rate.clk.hw); + +K230_CLK_GATE_FORMAT(vpu_cfg_gate, + K230_VPU_CFG_GATE, + 0xC, 10, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(vpu_cfg_rate, + K230_VPU_CFG_RATE, + 1, 1, 0, 0, + 1, 16, 11, 0xF, + 0xC, 31, div, 0x0, + false, 0, + &vpu_cfg_gate.clk.hw); + +K230_CLK_GATE_FORMAT(sec_apb_gate, + K230_SEC_APB_GATE, + 0x80, 0, 0, 0, + &pll1_div4.hw); + +K230_CLK_RATE_FORMAT(sec_apb_rate, + K230_SEC_APB_RATE, + 1, 1, 0, 0, + 1, 8, 1, 0x7, + 0x80, 31, div, 0x0, + false, 0, + &sec_apb_gate.clk.hw); + +K230_CLK_GATE_FORMAT(sec_fix_gate, + K230_SEC_FIX_GATE, + 0x80, 5, 0, 0, + &pll1_div4.hw); + +K230_CLK_RATE_FORMAT(sec_fix_rate, + K230_SEC_FIX_RATE, + 1, 1, 0, 0, + 1, 32, 6, 0x1F, + 0x80, 31, div, 0x0, + false, 0, + &sec_fix_gate.clk.hw); + +K230_CLK_GATE_FORMAT(sec_axi_gate, + K230_SEC_AXI_GATE, + 0x80, 4, 0, 0, + &pll1_div4.hw); + +K230_CLK_RATE_FORMAT(sec_axi_rate, + K230_SEC_AXI_RATE, + 1, 1, 0, 0, + 1, 8, 11, 0x3, + 0x80, 31, div, 0, + false, 0, + &sec_axi_gate.clk.hw); + +K230_CLK_GATE_FORMAT(usb_480m_gate, + K230_USB_480M_GATE, + 0x100, 0, 0, 0, + &pll1.hw); + +K230_CLK_RATE_FORMAT(usb_480m_rate, + K230_USB_480M_RATE, + 1, 1, 0, 0, + 1, 8, 1, 0x7, + 0x100, 31, div, 0, + false, 0, + &usb_480m_gate.clk.hw); + +K230_CLK_GATE_FORMAT(usb_100m_gate, + K230_USB_100M_GATE, + 0x100, 0, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(usb_100m_rate, + K230_USB_100M_RATE, + 1, 1, 0, 0, + 1, 8, 4, 0x7, + 0x100, 31, div, 0, + false, 0, + &usb_100m_gate.clk.hw); + +K230_CLK_GATE_FORMAT(dphy_dft_gate, + K230_DPHY_DFT_GATE, + 0x100, 0, 0, 0, + &pll0.hw); + +K230_CLK_RATE_FORMAT(dphy_dft_rate, + K230_DPHY_DFT_RATE, + 1, 1, 0, 0, + 1, 16, 1, 0xF, + 0x104, 31, div, 0, + false, 0, + &dphy_dft_gate.clk.hw); + +K230_CLK_GATE_FORMAT(spi2axi_gate, + K230_SPI2AXI_GATE, + 0x108, 0, 0, 0, + &pll0_div4.hw); + +K230_CLK_RATE_FORMAT(spi2axi_rate, + K230_SPI2AXI_RATE, + 1, 1, 0, 0, + 1, 8, 1, 0x7, + 0x108, 31, div, 0x0, + false, 0, + &spi2axi_gate.clk.hw); + +static const struct clk_parent_data k230_ai_src_mux_pdata[] = { + { .hw = &pll0_div2.hw, }, + { .hw = &pll3_div2.hw, }, +}; + +K230_CLK_MUX_FORMAT(ai_src_mux, + K230_AI_SRC_MUX, + 0x8, 2, 0x1, + 0, 0, + k230_ai_src_mux_pdata); + +K230_CLK_GATE_FORMAT(ai_src_gate, + K230_AI_SRC_GATE, + 0x8, 0, CLK_IGNORE_UNUSED, 0, + &ai_src_mux.clk.hw); + +K230_CLK_RATE_FORMAT(ai_src_rate, + K230_AI_SRC_RATE, + 1, 1, 0, 0, + 1, 8, 3, 0x7, + 0x8, 31, div, 0x0, + false, 0, + &ai_src_gate.clk.hw); + +K230_CLK_GATE_FORMAT(ai_axi_gate, + K230_AI_AXI_GATE, + 0x8, 10, 0, 0, + &pll0_div4.hw); + +static const struct clk_parent_data k230_camera0_mux_pdata[] = { + { .hw = &pll1_div3.hw, }, + { .hw = &pll1_div4.hw, }, + { .hw = &pll0_div4.hw, }, +}; + +K230_CLK_MUX_FORMAT(camera0_mux, + K230_CAMERA0_MUX, + 0x6C, 3, 0x3, + 0, 0, + k230_camera0_mux_pdata); + +K230_CLK_GATE_FORMAT(camera0_gate, + K230_CAMERA0_GATE, + 0x6C, 0, CLK_IGNORE_UNUSED, 0, + &camera0_mux.clk.hw); + +K230_CLK_RATE_FORMAT(camera0_rate, + K230_CAMERA0_RATE, + 1, 1, 0, 0, + 1, 32, 5, 0x1f, + 0x6C, 31, div, 0x0, + false, 0, + &camera0_gate.clk.hw); + +static const struct clk_parent_data k230_camera1_mux_pdata[] = { + { .hw = &pll1_div3.hw, }, + { .hw = &pll1_div4.hw, }, + { .hw = &pll0_div4.hw, }, +}; + +K230_CLK_MUX_FORMAT(camera1_mux, + K230_CAMERA1_MUX, + 0x6C, 10, 0x3, + 0, 0, + k230_camera1_mux_pdata); + +K230_CLK_GATE_FORMAT(camera1_gate, + K230_CAMERA1_GATE, + 0x6C, 1, CLK_IGNORE_UNUSED, 0, + &camera1_mux.clk.hw); + +K230_CLK_RATE_FORMAT(camera1_rate, + K230_CAMERA1_RATE, + 1, 1, 0, 0, + 1, 32, 12, 0x1f, + 0x6C, 31, div, 0x0, + false, 0, + &camera1_gate.clk.hw); + +static const struct clk_parent_data k230_camera2_mux_pdata[] = { + { .hw = &pll1_div3.hw, }, + { .hw = &pll1_div4.hw, }, + { .hw = &pll0_div4.hw, }, +}; + +K230_CLK_MUX_FORMAT(camera2_mux, + K230_CAMERA2_MUX, + 0x6C, 17, 0x3, + 0, 0, + k230_camera2_mux_pdata); + +K230_CLK_GATE_FORMAT(camera2_gate, + K230_CAMERA2_GATE, + 0x6C, 2, CLK_IGNORE_UNUSED, 0, + &camera2_mux.clk.hw); + +K230_CLK_RATE_FORMAT(camera2_rate, + K230_CAMERA2_RATE, + 1, 1, 0, 0, + 1, 32, 19, 0x1f, + 0x6C, 31, div, 0x0, + false, 0, + &camera2_gate.clk.hw); + +static struct k230_clk_mux *k230_clk_muxs[] = { + &hs_ssi0_mux, + &hs_usb_ref_mux, + &cpu1_src_mux, + &timer0_mux, + &timer1_mux, + &timer2_mux, + &timer3_mux, + &timer4_mux, + &timer5_mux, + &shrm_sram_mux, + &ddrc_src_mux, + &ai_src_mux, + &camera0_mux, + &camera1_mux, + &camera2_mux, +}; + +#define K230_CLK_MUX_NUM ARRAY_SIZE(k230_clk_muxs) + +static struct k230_clk_gate *k230_clk_gates[] = { + &cpu0_src_gate, + &cpu0_plic_gate, + &cpu0_noc_ddrcp4_gate, + &cpu0_apb_gate, + &cpu1_src_gate, + &cpu1_plic_gate, + &cpu1_apb_gate, + &pmu_apb_gate, + &hs_hclk_high_gate, + &hs_hclk_gate, + &hs_sd0_ahb_gate, + &hs_sd1_ahb_gate, + &hs_ssi1_ahb_gate, + &hs_ssi2_ahb_gate, + &hs_usb0_ahb_gate, + &hs_usb1_ahb_gate, + &hs_ssi0_axi_gate, + &hs_ssi1_gate, + &hs_ssi2_gate, + &hs_qspi_axi_src_gate, + &hs_ssi1_axi_gate, + &hs_ssi2_axi_gate, + &hs_sd_card_src_gate, + &hs_sd0_card_gate, + &hs_sd1_card_gate, + &hs_sd_axi_src_gate, + &hs_sd0_axi_gate, + &hs_sd1_axi_gate, + &hs_sd0_base_gate, + &hs_sd1_base_gate, + &hs_ssi0_gate, + &hs_sd_timer_src_gate, + &hs_sd0_timer_gate, + &hs_sd1_timer_gate, + &hs_usb0_ref_gate, + &hs_usb1_ref_gate, + &ls_apb_src_gate, + &ls_uart0_apb_gate, + &ls_uart1_apb_gate, + &ls_uart2_apb_gate, + &ls_uart3_apb_gate, + &ls_uart4_apb_gate, + &ls_i2c0_apb_gate, + &ls_i2c1_apb_gate, + &ls_i2c2_apb_gate, + &ls_i2c3_apb_gate, + &ls_i2c4_apb_gate, + &ls_gpio_apb_gate, + &ls_pwm_apb_gate, + &ls_jamlink0_apb_gate, + &ls_jamlink1_apb_gate, + &ls_jamlink2_apb_gate, + &ls_jamlink3_apb_gate, + &ls_audio_apb_gate, + &ls_adc_apb_gate, + &ls_codec_apb_gate, + &ls_i2c0_gate, + &ls_i2c1_gate, + &ls_i2c2_gate, + &ls_i2c3_gate, + &ls_i2c4_gate, + &ls_codec_adc_gate, + &ls_codec_dac_gate, + &ls_audio_dev_gate, + &ls_pdm_gate, + &ls_adc_gate, + &ls_uart0_gate, + &ls_uart1_gate, + &ls_uart2_gate, + &ls_uart3_gate, + &ls_uart4_gate, + &ls_jamlink0co_gate, + &ls_jamlink1co_gate, + &ls_jamlink2co_gate, + &ls_jamlink3co_gate, + &ls_gpio_debounce_gate, + &sysctl_wdt0_apb_gate, + &sysctl_wdt1_apb_gate, + &sysctl_timer_apb_gate, + &sysctl_iomux_apb_gate, + &sysctl_mailbox_apb_gate, + &sysctl_hdi_gate, + &sysctl_time_stamp_gate, + &sysctl_wdt0_gate, + &sysctl_wdt1_gate, + &timer0_gate, + &timer1_gate, + &timer2_gate, + &timer3_gate, + &timer4_gate, + &timer5_gate, + &shrm_apb_gate, + &shrm_sram_gate, + &shrm_axi_gate, + &shrm_axi_slave_gate, + &shrm_nonai2d_axi_gate, + &shrm_decompress_axi_gate, + &shrm_sdma_axi_gate, + &shrm_pdma_axi_gate, + &ddrc_src_gate, + &ddrc_bypass_gate, + &ddrc_apb_gate, + &display_ahb_gate, + &display_axi_gate, + &display_gpu_gate, + &display_dpip_gate, + &display_cfg_gate, + &display_ref_gate, + &vpu_src_gate, + &vpu_axi_gate, + &vpu_ddrcp2_gate, + &vpu_cfg_gate, + &sec_apb_gate, + &sec_fix_gate, + &sec_axi_gate, + &usb_480m_gate, + &usb_100m_gate, + &dphy_dft_gate, + &spi2axi_gate, + &ai_src_gate, + &ai_axi_gate, + &camera0_gate, + &camera1_gate, + &camera2_gate, +}; + +#define K230_CLK_GATE_NUM ARRAY_SIZE(k230_clk_gates) + +static struct k230_clk_rate *k230_clk_rates[] = { + &cpu0_src_rate, + &cpu0_axi_rate, + &cpu0_plic_rate, + &cpu0_apb_rate, + &cpu1_src_rate, + &cpu1_axi_rate, + &cpu1_plic_rate, + &hs_hclk_high_rate, + &hs_hclk_rate, + &hs_ssi0_axi_rate, + &hs_ssi1_rate, + &hs_ssi2_rate, + &hs_qspi_axi_src_rate, + &hs_sd_card_src_rate, + &hs_sd_axi_src_rate, + &hs_usb_ref_50m_rate, + &hs_sd_timer_src_rate, + &ls_apb_src_rate, + &ls_gpio_debounce_rate, + &ls_i2c0_rate, + &ls_i2c1_rate, + &ls_i2c2_rate, + &ls_i2c3_rate, + &ls_i2c4_rate, + &ls_codec_adc_rate, + &ls_codec_dac_rate, + &ls_audio_dev_rate, + &ls_pdm_rate, + &ls_adc_rate, + &ls_uart0_rate, + &ls_uart1_rate, + &ls_uart2_rate, + &ls_uart3_rate, + &ls_uart4_rate, + &ls_jamlinkco_src_rate, + &sysctl_hdi_rate, + &sysctl_time_stamp_rate, + &sysctl_temp_sensor_rate, + &sysctl_wdt0_rate, + &sysctl_wdt1_rate, + &timer0_src_rate, + &timer1_src_rate, + &timer2_src_rate, + &timer3_src_rate, + &timer4_src_rate, + &timer5_src_rate, + &shrm_apb_rate, + &ddrc_src_rate, + &ddrc_apb_rate, + &display_ahb_rate, + &display_clkext_rate, + &display_gpu_rate, + &display_dpip_rate, + &display_cfg_rate, + &vpu_src_rate, + &vpu_axi_src_rate, + &vpu_cfg_rate, + &sec_apb_rate, + &sec_fix_rate, + &sec_axi_rate, + &usb_480m_rate, + &usb_100m_rate, + &dphy_dft_rate, + &spi2axi_rate, + &ai_src_rate, + &camera0_rate, + &camera1_rate, + &camera2_rate, +}; + +#define K230_CLK_RATE_NUM ARRAY_SIZE(k230_clk_rates) + +#define K230_CLK_NUM (K230_CLK_MUX_NUM + K230_CLK_GATE_NUM + K230_CLK_RATE_NUM + 1) + +static int k230_pll_prepare(struct clk_hw *hw) +{ + struct k230_pll *pll = hw_to_k230_pll(hw); + u32 reg; + + /* wait for PLL lock until it reaches lock status */ + return readl_poll_timeout(K230_PLLX_LOCK_ADDR(pll->reg, pll->id), reg, + reg & K230_PLL_LOCK_STATUS_MASK, + K230_PLL_LOCK_TIME_DELAY, K230_PLL_LOCK_TIMEOUT); +} + +static inline bool k230_pll_hw_is_enabled(struct k230_pll *pll) +{ + return readl(K230_PLLX_GATE_ADDR(pll->reg, pll->id)) & K230_PLL_GATE_ENABLE; +} + +static void k230_pll_enable_hw(struct k230_pll *pll) +{ + u32 reg; + + if (k230_pll_hw_is_enabled(pll)) + return; + + reg = readl(K230_PLLX_GATE_ADDR(pll->reg, pll->id)); + reg |= K230_PLL_GATE_ENABLE | K230_PLL_GATE_WRITE_ENABLE; + writel(reg, K230_PLLX_GATE_ADDR(pll->reg, pll->id)); +} + +static int k230_pll_enable(struct clk_hw *hw) +{ + struct k230_pll *pll = hw_to_k230_pll(hw); + + guard(spinlock)(pll->lock); + + k230_pll_enable_hw(pll); + + return 0; +} + +static void k230_pll_disable(struct clk_hw *hw) +{ + struct k230_pll *pll = hw_to_k230_pll(hw); + u32 reg; + + guard(spinlock)(pll->lock); + + reg = readl(K230_PLLX_GATE_ADDR(pll->reg, pll->id)); + reg &= ~(K230_PLL_GATE_ENABLE); + reg |= (K230_PLL_GATE_WRITE_ENABLE); + writel(reg, K230_PLLX_GATE_ADDR(pll->reg, pll->id)); +} + +static int k230_pll_is_enabled(struct clk_hw *hw) +{ + return k230_pll_hw_is_enabled(hw_to_k230_pll(hw)); +} + +static unsigned long k230_pll_get_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct k230_pll *pll = hw_to_k230_pll(hw); + u32 reg; + u32 r, f, od; + + guard(spinlock)(pll->lock); + + reg = readl(K230_PLLX_BYPASS_ADDR(pll->reg, pll->id)); + if (reg & K230_PLL_BYPASS_ENABLE) + return parent_rate; + + reg = readl(K230_PLLX_LOCK_ADDR(pll->reg, pll->id)); + if (!(reg & (K230_PLL_LOCK_STATUS_MASK))) + return 0; + + reg = readl(K230_PLLX_DIV_ADDR(pll->reg, pll->id)); + r = FIELD_GET(K230_PLL_R_MASK, reg) + 1; + f = FIELD_GET(K230_PLL_F_MASK, reg) + 1; + od = FIELD_GET(K230_PLL_OD_MASK, reg) + 1; + + return mul_u64_u32_div(parent_rate, f, r * od); +} + +static int k230_register_plls(struct platform_device *pdev, spinlock_t *lock, + void __iomem *reg) +{ + int i, ret; + struct k230_pll *pll; + + for (i = 0; i < ARRAY_SIZE(k230_plls); i++) { + const char *name; + + pll = k230_plls[i]; + + name = pll->hw.init->name; + pll->lock = lock; + pll->reg = reg; + + ret = devm_clk_hw_register(&pdev->dev, &pll->hw); + if (ret) + return ret; + + ret = devm_clk_hw_register_clkdev(&pdev->dev, &pll->hw, name, NULL); + if (ret) + return ret; + } + + return 0; +} + +static int k230_register_pll_divs(struct platform_device *pdev) +{ + struct clk_fixed_factor *pll_div; + int ret; + + for (int i = 0; i < ARRAY_SIZE(k230_pll_divs); i++) { + const char *name; + + pll_div = k230_pll_divs[i]; + + name = pll_div->hw.init->name; + + ret = devm_clk_hw_register(&pdev->dev, &pll_div->hw); + if (ret) + return ret; + + ret = devm_clk_hw_register_clkdev(&pdev->dev, &pll_div->hw, + name, NULL); + if (ret) + return ret; + } + + return 0; +} + +static unsigned long k230_clk_get_rate_mul(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct k230_clk_rate *clk = hw_to_k230_clk_rate(hw); + struct k230_clk_rate_self *rate_self = &clk->clk; + u32 mul, div; + + guard(spinlock)(rate_self->lock); + + div = rate_self->div_max; + mul = (readl(rate_self->reg + clk->mul_reg_off) >> rate_self->mul_shift) + & rate_self->mul_mask; + + return mul_u64_u32_div(parent_rate, mul + 1, div); +} + +static unsigned long k230_clk_get_rate_div(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct k230_clk_rate *clk = hw_to_k230_clk_rate(hw); + struct k230_clk_rate_self *rate_self = &clk->clk; + u32 mul, div; + + guard(spinlock)(rate_self->lock); + + mul = rate_self->mul_max; + div = (readl(rate_self->reg + clk->div_reg_off) >> rate_self->div_shift) + & rate_self->div_mask; + + return mul_u64_u32_div(parent_rate, mul, div + 1); +} + +static unsigned long k230_clk_get_rate_mul_div(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct k230_clk_rate *clk = hw_to_k230_clk_rate(hw); + struct k230_clk_rate_self *rate_self = &clk->clk; + u32 mul, div; + + guard(spinlock)(rate_self->lock); + + div = (readl(rate_self->reg + clk->div_reg_off) >> rate_self->div_shift) + & rate_self->div_mask; + mul = (readl(rate_self->reg + clk->mul_reg_off) >> rate_self->mul_shift) + & rate_self->mul_mask; + + return mul_u64_u32_div(parent_rate, mul, div); +} + +static int k230_clk_find_approximate_mul(u32 mul_min, u32 mul_max, + u32 div_min, u32 div_max, + unsigned long rate, unsigned long parent_rate, + u32 *div, u32 *mul) +{ + long abs_min; + long abs_current; + long perfect_divide; + + if (!rate || !parent_rate || !mul_min) + return -EINVAL; + + perfect_divide = (long)((parent_rate * 1000) / rate); + abs_min = abs(perfect_divide - + (long)(((long)div_max * 1000) / (long)mul_min)); + *mul = mul_min; + + for (u32 i = mul_min + 1; i <= mul_max; i++) { + abs_current = abs(perfect_divide - + (long)(((long)div_max * 1000) / (long)i)); + + if (abs_min > abs_current) { + abs_min = abs_current; + *mul = i; + } + } + + *div = div_max; + + return 0; +} + +static int k230_clk_find_approximate_div(u32 mul_min, u32 mul_max, + u32 div_min, u32 div_max, + unsigned long rate, unsigned long parent_rate, + u32 *div, u32 *mul) +{ + long abs_min; + long abs_current; + long perfect_divide; + + if (!rate || !parent_rate || !mul_max) + return -EINVAL; + + perfect_divide = (long)((parent_rate * 1000) / rate); + abs_min = abs(perfect_divide - + (long)(((long)div_min * 1000) / (long)mul_max)); + *div = div_min; + + for (u32 i = div_min + 1; i <= div_max; i++) { + abs_current = abs(perfect_divide - + (long)(((long)i * 1000) / (long)mul_max)); + + if (abs_min > abs_current) { + abs_min = abs_current; + *div = i; + } + } + + *mul = mul_max; + + return 0; +} + +static int k230_clk_find_approximate_mul_div(u32 mul_min, u32 mul_max, + u32 div_min, u32 div_max, + unsigned long rate, + unsigned long parent_rate, + u32 *div, u32 *mul) +{ + unsigned long best_mul, best_div; + + if (!rate || !parent_rate || !mul_min) + return -EINVAL; + + rational_best_approximation(rate, parent_rate, + (unsigned long)mul_max, (unsigned long)div_max, + &best_mul, &best_div); + + if (best_mul < mul_min) + best_mul = mul_min; + + if (best_div < div_min) + best_div = div_min; + + *mul = (u32)best_mul; + *div = (u32)best_div; + + return 0; +} + +static int k230_clk_determine_rate_mul(struct clk_hw *hw, struct clk_rate_request *req) +{ + int ret; + struct k230_clk_rate_self *rate_self = hw_to_k230_clk_rate_self(hw); + unsigned long rate = req->rate; + unsigned long parent_rate = req->best_parent_rate; + u32 div, mul; + + ret = k230_clk_find_approximate_mul(rate_self->mul_min, rate_self->mul_max, + rate_self->div_min, rate_self->div_max, + rate, parent_rate, &div, &mul); + if (ret) + return ret; + + req->rate = mul_u64_u32_div(parent_rate, mul, div); + + return 0; +} + +static int k230_clk_determine_rate_div(struct clk_hw *hw, struct clk_rate_request *req) +{ + int ret; + struct k230_clk_rate_self *rate_self = hw_to_k230_clk_rate_self(hw); + unsigned long rate = req->rate; + unsigned long parent_rate = req->best_parent_rate; + u32 div, mul; + + ret = k230_clk_find_approximate_div(rate_self->mul_min, rate_self->mul_max, + rate_self->div_min, rate_self->div_max, + rate, parent_rate, &div, &mul); + if (ret) + return ret; + + req->rate = mul_u64_u32_div(parent_rate, mul, div); + + return 0; +} + +static int k230_clk_determine_rate_mul_div(struct clk_hw *hw, struct clk_rate_request *req) +{ + int ret; + struct k230_clk_rate_self *rate_self = hw_to_k230_clk_rate_self(hw); + unsigned long rate = req->rate; + unsigned long parent_rate = req->best_parent_rate; + u32 div, mul; + + ret = k230_clk_find_approximate_mul_div(rate_self->mul_min, rate_self->mul_max, + rate_self->div_min, rate_self->div_max, + rate, parent_rate, &div, &mul); + if (ret) + return ret; + + req->rate = mul_u64_u32_div(parent_rate, mul, div); + + return 0; +} + +static int k230_clk_set_rate_mul(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int ret; + struct k230_clk_rate *clk = hw_to_k230_clk_rate(hw); + struct k230_clk_rate_self *rate_self = &clk->clk; + u32 div, mul, mul_reg; + + if (rate > parent_rate) + return -EINVAL; + + if (rate_self->read_only) + return 0; + + ret = k230_clk_find_approximate_mul(rate_self->mul_min, rate_self->mul_max, + rate_self->div_min, rate_self->div_max, + rate, parent_rate, &div, &mul); + if (ret) + return ret; + + guard(spinlock)(rate_self->lock); + + mul_reg = readl(rate_self->reg + clk->mul_reg_off); + mul_reg |= ((mul - 1) & rate_self->mul_mask) << (rate_self->mul_shift); + mul_reg |= BIT(rate_self->write_enable_bit); + writel(mul_reg, rate_self->reg + clk->mul_reg_off); + + return 0; +} + +static int k230_clk_set_rate_div(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int ret; + struct k230_clk_rate *clk = hw_to_k230_clk_rate(hw); + struct k230_clk_rate_self *rate_self = &clk->clk; + u32 div, mul, div_reg; + + if (rate > parent_rate) + return -EINVAL; + + if (rate_self->read_only) + return 0; + + ret = k230_clk_find_approximate_div(rate_self->mul_min, rate_self->mul_max, + rate_self->div_min, rate_self->div_max, + rate, parent_rate, &div, &mul); + if (ret) + return ret; + + guard(spinlock)(rate_self->lock); + + div_reg = readl(rate_self->reg + clk->div_reg_off); + div_reg |= ((div - 1) & rate_self->div_mask) << (rate_self->div_shift); + div_reg |= BIT(rate_self->write_enable_bit); + writel(div_reg, rate_self->reg + clk->div_reg_off); + + return 0; +} + +static int k230_clk_set_rate_mul_div(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int ret; + struct k230_clk_rate *clk = hw_to_k230_clk_rate(hw); + struct k230_clk_rate_self *rate_self = &clk->clk; + u32 div, mul, div_reg, mul_reg; + + if (rate > parent_rate) + return -EINVAL; + + if (rate_self->read_only) + return 0; + + ret = k230_clk_find_approximate_mul_div(rate_self->mul_min, rate_self->mul_max, + rate_self->div_min, rate_self->div_max, + rate, parent_rate, &div, &mul); + if (ret) + return ret; + + guard(spinlock)(rate_self->lock); + + div_reg = readl(rate_self->reg + clk->div_reg_off); + div_reg |= ((div - 1) & rate_self->div_mask) << (rate_self->div_shift); + div_reg |= BIT(rate_self->write_enable_bit); + writel(div_reg, rate_self->reg + clk->div_reg_off); + + mul_reg = readl(rate_self->reg + clk->mul_reg_off); + mul_reg |= ((mul - 1) & rate_self->mul_mask) << (rate_self->mul_shift); + mul_reg |= BIT(rate_self->write_enable_bit); + writel(mul_reg, rate_self->reg + clk->mul_reg_off); + + return 0; +} + +static int k230_register_clk(int id, struct clk_hw *hw, struct device *dev, + struct clk_hw_onecell_data *hw_data) +{ + int ret; + + ret = devm_clk_hw_register(dev, hw); + if (ret) + return ret; + + hw_data->hws[id] = hw; + + return 0; +} + +static int k230_register_clks(struct platform_device *pdev, + struct clk_hw_onecell_data *hw_data, + spinlock_t *lock, void __iomem *reg) +{ + int i, ret; + struct device *dev = &pdev->dev; + struct clk_fixed_factor *fixed_factor = &shrm_sram_div2; + struct k230_clk_mux *mux; + struct k230_clk_gate *gate; + struct k230_clk_rate *rate; + + for (i = 0; i < K230_CLK_MUX_NUM; i++) { + mux = k230_clk_muxs[i]; + mux->clk.lock = lock; + mux->clk.reg = reg + mux->reg_off; + + ret = k230_register_clk(mux->id, &mux->clk.hw, dev, hw_data); + if (ret) + return ret; + } + + for (i = 0; i < K230_CLK_GATE_NUM; i++) { + gate = k230_clk_gates[i]; + gate->clk.lock = lock; + gate->clk.reg = reg + gate->reg_off; + + ret = k230_register_clk(gate->id, &gate->clk.hw, dev, hw_data); + if (ret) + return ret; + } + + for (i = 0; i < K230_CLK_RATE_NUM; i++) { + rate = k230_clk_rates[i]; + rate->clk.lock = lock; + rate->clk.reg = reg; + + ret = k230_register_clk(rate->id, &rate->clk.hw, dev, hw_data); + if (ret) + return ret; + } + + ret = k230_register_clk(K230_SHRM_SRAM_DIV2, &fixed_factor->hw, dev, hw_data); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get, hw_data); +} + +static int k230_clk_init_plls(struct platform_device *pdev) +{ + int ret; + void __iomem *reg; + /* used for all the plls */ + spinlock_t *lock; + + lock = devm_kzalloc(&pdev->dev, sizeof(*lock), GFP_KERNEL); + if (!lock) + return -ENOMEM; + + spin_lock_init(lock); + + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + ret = k230_register_plls(pdev, lock, reg); + if (ret) + return ret; + + ret = k230_register_pll_divs(pdev); + if (ret) + return ret; + + return 0; +} + +static int k230_clk_init_clks(struct platform_device *pdev, + struct clk_hw_onecell_data *hw_data) +{ + int ret; + void __iomem *reg; + /* used for all the clocks */ + spinlock_t *lock; + + lock = devm_kzalloc(&pdev->dev, sizeof(*lock), GFP_KERNEL); + if (!lock) + return -ENOMEM; + + spin_lock_init(lock); + + hw_data->num = K230_CLK_NUM; + + reg = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + ret = k230_register_clks(pdev, hw_data, lock, reg); + if (ret) + return ret; + + return 0; +} + +static int k230_clk_probe(struct platform_device *pdev) +{ + int ret; + struct clk_hw_onecell_data *hw_data; + + hw_data = devm_kzalloc(&pdev->dev, struct_size(hw_data, hws, K230_CLK_NUM), + GFP_KERNEL); + if (!hw_data) + return -ENOMEM; + + ret = k230_clk_init_plls(pdev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "init plls failed\n"); + + ret = k230_clk_init_clks(pdev, hw_data); + if (ret) + return dev_err_probe(&pdev->dev, ret, "init clks failed\n"); + + return 0; +} + +static const struct of_device_id k230_clk_ids[] = { + { .compatible = "canaan,k230-clk" }, + { /* Sentinel */ } +}; + +static struct platform_driver k230_clk_driver = { + .driver = { + .name = "k230_clock_controller", + .of_match_table = k230_clk_ids, + }, + .probe = k230_clk_probe, +}; +builtin_platform_driver(k230_clk_driver); diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c index 3727d5472450..9149ce4f702d 100644 --- a/drivers/clk/clk-max77686.c +++ b/drivers/clk/clk-max77686.c @@ -47,8 +47,8 @@ struct max77686_clk_init_data { struct max77686_clk_driver_data { enum max77686_chip_name chip; - struct max77686_clk_init_data *max_clk_data; size_t num_clks; + struct max77686_clk_init_data max_clk_data[] __counted_by(num_clks); }; static const struct @@ -168,19 +168,7 @@ static int max77686_clk_probe(struct platform_device *pdev) struct regmap *regmap; int i, ret, num_clks; - drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL); - if (!drv_data) - return -ENOMEM; - - regmap = dev_get_regmap(parent, NULL); - if (!regmap) { - dev_err(dev, "Failed to get rtc regmap\n"); - return -ENODEV; - } - - drv_data->chip = id->driver_data; - - switch (drv_data->chip) { + switch (id->driver_data) { case CHIP_MAX77686: num_clks = MAX77686_CLKS_NUM; hw_clks = max77686_hw_clks_info; @@ -201,13 +189,19 @@ static int max77686_clk_probe(struct platform_device *pdev) return -EINVAL; } - drv_data->num_clks = num_clks; - drv_data->max_clk_data = devm_kcalloc(dev, num_clks, - sizeof(*drv_data->max_clk_data), - GFP_KERNEL); - if (!drv_data->max_clk_data) + drv_data = devm_kzalloc(dev, struct_size(drv_data, max_clk_data, num_clks), GFP_KERNEL); + if (!drv_data) return -ENOMEM; + drv_data->num_clks = num_clks; + drv_data->chip = id->driver_data; + + regmap = dev_get_regmap(parent, NULL); + if (!regmap) { + dev_err(dev, "Failed to get rtc regmap\n"); + return -ENODEV; + } + for (i = 0; i < num_clks; i++) { struct max77686_clk_init_data *max_clk_data; const char *clk_name; diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c index fae65127cd4a..08050ff1c8cf 100644 --- a/drivers/clk/hisilicon/clk.c +++ b/drivers/clk/hisilicon/clk.c @@ -70,7 +70,7 @@ struct hisi_clock_data *hisi_clk_init(struct device_node *np, clk_data = kzalloc_obj(*clk_data); if (!clk_data) - goto err; + goto err_base; clk_data->base = base; clk_table = kzalloc_objs(*clk_table, nr_clks); @@ -83,6 +83,8 @@ struct hisi_clock_data *hisi_clk_init(struct device_node *np, return clk_data; err_data: kfree(clk_data); +err_base: + iounmap(base); err: return NULL; } diff --git a/drivers/clk/hisilicon/clkdivider-hi6220.c b/drivers/clk/hisilicon/clkdivider-hi6220.c index 1787ecefe601..20a337383a1e 100644 --- a/drivers/clk/hisilicon/clkdivider-hi6220.c +++ b/drivers/clk/hisilicon/clkdivider-hi6220.c @@ -26,8 +26,8 @@ * @shift: shift to the divider bit field * @width: width of the divider bit field * @mask: mask for setting divider rate - * @table: the div table that the divider supports * @lock: register lock + * @table: the div table that the divider supports */ struct hi6220_clk_divider { struct clk_hw hw; @@ -35,8 +35,8 @@ struct hi6220_clk_divider { u8 shift; u8 width; u32 mask; - const struct clk_div_table *table; spinlock_t *lock; + struct clk_div_table table[]; }; #define to_hi6220_clk_divider(_hw) \ @@ -108,24 +108,19 @@ struct clk *hi6220_register_clkdiv(struct device *dev, const char *name, u32 max_div, min_div; int i; - /* allocate the divider */ - div = kzalloc_obj(*div); - if (!div) - return ERR_PTR(-ENOMEM); - /* Init the divider table */ max_div = div_mask(width) + 1; min_div = 1; - table = kzalloc_objs(*table, max_div + 1); - if (!table) { - kfree(div); + /* allocate the divider */ + div = kzalloc_flex(*div, table, max_div + 1); + if (!div) return ERR_PTR(-ENOMEM); - } for (i = 0; i < max_div; i++) { - table[i].div = min_div + i; - table[i].val = table[i].div - 1; + table = &div->table[i]; + table->div = min_div + i; + table->val = table->div - 1; } init.name = name; @@ -141,14 +136,11 @@ struct clk *hi6220_register_clkdiv(struct device *dev, const char *name, div->mask = mask_bit ? BIT(mask_bit) : 0; div->lock = lock; div->hw.init = &init; - div->table = table; /* register the clock */ clk = clk_register(dev, &div->hw); - if (IS_ERR(clk)) { - kfree(table); + if (IS_ERR(clk)) kfree(div); - } return clk; } diff --git a/drivers/clk/keystone/sci-clk.c b/drivers/clk/keystone/sci-clk.c index 7c0f7b3e89e0..9d2094bd48e3 100644 --- a/drivers/clk/keystone/sci-clk.c +++ b/drivers/clk/keystone/sci-clk.c @@ -362,6 +362,14 @@ static int _sci_clk_build(struct sci_clk_provider *provider, init.ops = &sci_clk_ops; init.num_parents = sci_clk->num_parents; + + /* + * A clock rate query to the SCI firmware will return 0 if either the + * clock itself is disabled or the attached device/consumer is disabled. + * This makes it inherently unsuitable for the caching of the clk + * framework. + */ + init.flags = CLK_GET_RATE_NOCACHE; sci_clk->hw.init = &init; ret = devm_clk_hw_register(provider->dev, &sci_clk->hw); @@ -417,7 +425,7 @@ static struct clk_hw *sci_clk_get(struct of_phandle_args *clkspec, void *data) key.clk_id = clkspec->args[1]; clk = bsearch(&key, provider->clocks, provider->num_clocks, - sizeof(clk), _cmp_sci_clk); + sizeof(*clk), _cmp_sci_clk); if (!clk) return ERR_PTR(-ENODEV); diff --git a/drivers/clk/mediatek/clk-mt7988-infracfg.c b/drivers/clk/mediatek/clk-mt7988-infracfg.c index ef8267319d91..13ffa9d88e24 100644 --- a/drivers/clk/mediatek/clk-mt7988-infracfg.c +++ b/drivers/clk/mediatek/clk-mt7988-infracfg.c @@ -56,49 +56,45 @@ static const char *const infra_pcie_gfmux_tl_ck_o_p3_parents[] __initconst = { static const struct mtk_mux infra_muxes[] = { /* MODULE_CLK_SEL_0 */ - MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_UART0_SEL, "infra_mux_uart0_sel", - infra_mux_uart0_parents, 0x0018, 0x0010, 0x0014, 0, 1, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_UART1_SEL, "infra_mux_uart1_sel", - infra_mux_uart1_parents, 0x0018, 0x0010, 0x0014, 1, 1, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_UART2_SEL, "infra_mux_uart2_sel", - infra_mux_uart2_parents, 0x0018, 0x0010, 0x0014, 2, 1, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_SPI0_SEL, "infra_mux_spi0_sel", infra_mux_spi0_parents, - 0x0018, 0x0010, 0x0014, 4, 1, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_SPI1_SEL, "infra_mux_spi1_sel", infra_mux_spi1_parents, - 0x0018, 0x0010, 0x0014, 5, 1, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_MUX_SPI2_SEL, "infra_mux_spi2_sel", infra_mux_spi0_parents, - 0x0018, 0x0010, 0x0014, 6, 1, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_SEL, "infra_pwm_sel", infra_pwm_bck_parents, 0x0018, - 0x0010, 0x0014, 14, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK1_SEL, "infra_pwm_ck1_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 16, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK2_SEL, "infra_pwm_ck2_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 18, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK3_SEL, "infra_pwm_ck3_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 20, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK4_SEL, "infra_pwm_ck4_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 22, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK5_SEL, "infra_pwm_ck5_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 24, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK6_SEL, "infra_pwm_ck6_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 26, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK7_SEL, "infra_pwm_ck7_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 28, 2, -1, -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_CK8_SEL, "infra_pwm_ck8_sel", infra_pwm_bck_parents, - 0x0018, 0x0010, 0x0014, 30, 2, -1, -1, -1), + MUX_CLR_SET(CLK_INFRA_MUX_UART0_SEL, "infra_mux_uart0_sel", + infra_mux_uart0_parents, 0x0018, 0x0010, 0x0014, 0, 1), + MUX_CLR_SET(CLK_INFRA_MUX_UART1_SEL, "infra_mux_uart1_sel", + infra_mux_uart1_parents, 0x0018, 0x0010, 0x0014, 1, 1), + MUX_CLR_SET(CLK_INFRA_MUX_UART2_SEL, "infra_mux_uart2_sel", + infra_mux_uart2_parents, 0x0018, 0x0010, 0x0014, 2, 1), + MUX_CLR_SET(CLK_INFRA_MUX_SPI0_SEL, "infra_mux_spi0_sel", + infra_mux_spi0_parents, 0x0018, 0x0010, 0x0014, 4, 1), + MUX_CLR_SET(CLK_INFRA_MUX_SPI1_SEL, "infra_mux_spi1_sel", + infra_mux_spi1_parents, 0x0018, 0x0010, 0x0014, 5, 1), + MUX_CLR_SET(CLK_INFRA_MUX_SPI2_SEL, "infra_mux_spi2_sel", + infra_mux_spi0_parents, 0x0018, 0x0010, 0x0014, 6, 1), + MUX_CLR_SET(CLK_INFRA_PWM_SEL, "infra_pwm_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 14, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK1_SEL, "infra_pwm_ck1_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 16, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK2_SEL, "infra_pwm_ck2_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 18, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK3_SEL, "infra_pwm_ck3_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 20, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK4_SEL, "infra_pwm_ck4_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 22, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK5_SEL, "infra_pwm_ck5_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 24, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK6_SEL, "infra_pwm_ck6_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 26, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK7_SEL, "infra_pwm_ck7_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 28, 2), + MUX_CLR_SET(CLK_INFRA_PWM_CK8_SEL, "infra_pwm_ck8_sel", + infra_pwm_bck_parents, 0x0018, 0x0010, 0x0014, 30, 2), /* MODULE_CLK_SEL_1 */ - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PCIE_GFMUX_TL_O_P0_SEL, "infra_pcie_gfmux_tl_o_p0_sel", - infra_pcie_gfmux_tl_ck_o_p0_parents, 0x0028, 0x0020, 0x0024, 0, 2, -1, - -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PCIE_GFMUX_TL_O_P1_SEL, "infra_pcie_gfmux_tl_o_p1_sel", - infra_pcie_gfmux_tl_ck_o_p1_parents, 0x0028, 0x0020, 0x0024, 2, 2, -1, - -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PCIE_GFMUX_TL_O_P2_SEL, "infra_pcie_gfmux_tl_o_p2_sel", - infra_pcie_gfmux_tl_ck_o_p2_parents, 0x0028, 0x0020, 0x0024, 4, 2, -1, - -1, -1), - MUX_GATE_CLR_SET_UPD(CLK_INFRA_PCIE_GFMUX_TL_O_P3_SEL, "infra_pcie_gfmux_tl_o_p3_sel", - infra_pcie_gfmux_tl_ck_o_p3_parents, 0x0028, 0x0020, 0x0024, 6, 2, -1, - -1, -1), + MUX_CLR_SET(CLK_INFRA_PCIE_GFMUX_TL_O_P0_SEL, "infra_pcie_gfmux_tl_o_p0_sel", + infra_pcie_gfmux_tl_ck_o_p0_parents, 0x0028, 0x0020, 0x0024, 0, 2), + MUX_CLR_SET(CLK_INFRA_PCIE_GFMUX_TL_O_P1_SEL, "infra_pcie_gfmux_tl_o_p1_sel", + infra_pcie_gfmux_tl_ck_o_p1_parents, 0x0028, 0x0020, 0x0024, 2, 2), + MUX_CLR_SET(CLK_INFRA_PCIE_GFMUX_TL_O_P2_SEL, "infra_pcie_gfmux_tl_o_p2_sel", + infra_pcie_gfmux_tl_ck_o_p2_parents, 0x0028, 0x0020, 0x0024, 4, 2), + MUX_CLR_SET(CLK_INFRA_PCIE_GFMUX_TL_O_P3_SEL, "infra_pcie_gfmux_tl_o_p3_sel", + infra_pcie_gfmux_tl_ck_o_p3_parents, 0x0028, 0x0020, 0x0024, 6, 2), }; static const struct mtk_gate_regs infra0_cg_regs = { diff --git a/drivers/clk/mediatek/clk-mt8192.c b/drivers/clk/mediatek/clk-mt8192.c index 50b43807c60c..12c8890d922f 100644 --- a/drivers/clk/mediatek/clk-mt8192.c +++ b/drivers/clk/mediatek/clk-mt8192.c @@ -579,8 +579,8 @@ static const struct mtk_mux top_mtk_muxes[] = { dsp7_parents, 0x050, 0x054, 0x058, 0, 3, 7, 0x004, 16), MUX_GATE_CLR_SET_UPD(CLK_TOP_MFG_REF_SEL, "mfg_ref_sel", mfg_ref_parents, 0x050, 0x054, 0x058, 16, 2, 23, 0x004, 18), - MUX_CLR_SET_UPD(CLK_TOP_MFG_PLL_SEL, "mfg_pll_sel", - mfg_pll_parents, 0x050, 0x054, 0x058, 18, 1, -1, -1), + MUX_CLR_SET(CLK_TOP_MFG_PLL_SEL, "mfg_pll_sel", mfg_pll_parents, + 0x050, 0x054, 0x058, 18, 1), MUX_GATE_CLR_SET_UPD(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x050, 0x054, 0x058, 24, 3, 31, 0x004, 19), /* CLK_CFG_5 */ diff --git a/drivers/clk/mediatek/clk-mux.h b/drivers/clk/mediatek/clk-mux.h index 151e56dcf884..1a9baf306b4a 100644 --- a/drivers/clk/mediatek/clk-mux.h +++ b/drivers/clk/mediatek/clk-mux.h @@ -126,6 +126,11 @@ extern const struct clk_ops mtk_mux_gate_hwv_fenc_clr_set_upd_ops; 0, _upd_ofs, _upd, CLK_SET_RATE_PARENT, \ mtk_mux_clr_set_upd_ops) +#define MUX_CLR_SET(_id, _name, _parents, _mux_ofs, \ + _mux_set_ofs, _mux_clr_ofs, _shift, _width) \ + MUX_CLR_SET_UPD(_id, _name, _parents, _mux_ofs, \ + _mux_set_ofs, _mux_clr_ofs, _shift, _width, 0, -1) + #define MUX_GATE_HWV_FENC_CLR_SET_UPD_FLAGS(_id, _name, _parents, \ _mux_ofs, _mux_set_ofs, _mux_clr_ofs, \ _hwv_sta_ofs, _hwv_set_ofs, _hwv_clr_ofs, \ diff --git a/drivers/clk/microchip/Makefile b/drivers/clk/microchip/Makefile index 13250e04e46c..8e60bc1a03ae 100644 --- a/drivers/clk/microchip/Makefile +++ b/drivers/clk/microchip/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_COMMON_CLK_PIC32) += clk-core.o +obj-$(CONFIG_COMMON_CLK_PIC32) += clk-pic32.o obj-$(CONFIG_PIC32MZDA) += clk-pic32mzda.o obj-$(CONFIG_MCHP_CLK_MPFS) += clk-mpfs.o obj-$(CONFIG_MCHP_CLK_MPFS) += clk-mpfs-ccc.o diff --git a/drivers/clk/microchip/clk-mpfs-ccc.c b/drivers/clk/microchip/clk-mpfs-ccc.c index 0a76a1aaa50f..40c17593e594 100644 --- a/drivers/clk/microchip/clk-mpfs-ccc.c +++ b/drivers/clk/microchip/clk-mpfs-ccc.c @@ -32,6 +32,7 @@ #define MPFS_CCC_FIXED_DIV 4 #define MPFS_CCC_OUTPUTS_PER_PLL 4 #define MPFS_CCC_REFS_PER_PLL 2 +#define MPFS_CCC_NUM_CLKS 16 struct mpfs_ccc_data { void __iomem **pll_base; @@ -178,7 +179,7 @@ static int mpfs_ccc_register_outputs(struct device *dev, struct mpfs_ccc_out_hw_ return dev_err_probe(dev, ret, "failed to register clock id: %d\n", out_hw->id); - data->hw_data.hws[out_hw->id - 2] = &out_hw->divider.hw; + data->hw_data.hws[out_hw->id] = &out_hw->divider.hw; } return 0; @@ -231,17 +232,9 @@ static int mpfs_ccc_probe(struct platform_device *pdev) { struct mpfs_ccc_data *clk_data; void __iomem *pll_base[ARRAY_SIZE(mpfs_ccc_pll_clks)]; - unsigned int num_clks; int ret; - /* - * If DLLs get added here, mpfs_ccc_register_outputs() currently packs - * sparse clock IDs in the hws array - */ - num_clks = ARRAY_SIZE(mpfs_ccc_pll_clks) + ARRAY_SIZE(mpfs_ccc_pll0out_clks) + - ARRAY_SIZE(mpfs_ccc_pll1out_clks); - - clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hw_data.hws, num_clks), + clk_data = devm_kzalloc(&pdev->dev, struct_size(clk_data, hw_data.hws, MPFS_CCC_NUM_CLKS), GFP_KERNEL); if (!clk_data) return -ENOMEM; @@ -255,7 +248,7 @@ static int mpfs_ccc_probe(struct platform_device *pdev) return PTR_ERR(pll_base[1]); clk_data->pll_base = pll_base; - clk_data->hw_data.num = num_clks; + clk_data->hw_data.num = MPFS_CCC_NUM_CLKS; clk_data->dev = &pdev->dev; ret = mpfs_ccc_register_plls(clk_data->dev, mpfs_ccc_pll_clks, diff --git a/drivers/clk/microchip/clk-core.c b/drivers/clk/microchip/clk-pic32.c index 692152b5094e..9d128fba2cde 100644 --- a/drivers/clk/microchip/clk-core.c +++ b/drivers/clk/microchip/clk-pic32.c @@ -11,7 +11,7 @@ #include <linux/iopoll.h> #include <linux/platform_data/pic32.h> -#include "clk-core.h" +#include "clk-pic32.h" /* OSCCON Reg fields */ #define OSC_CUR_MASK 0x07 diff --git a/drivers/clk/microchip/clk-core.h b/drivers/clk/microchip/clk-pic32.h index acb81739a16f..acb81739a16f 100644 --- a/drivers/clk/microchip/clk-core.h +++ b/drivers/clk/microchip/clk-pic32.h diff --git a/drivers/clk/microchip/clk-pic32mzda.c b/drivers/clk/microchip/clk-pic32mzda.c index 27599829ea40..e11cbdd982a6 100644 --- a/drivers/clk/microchip/clk-pic32mzda.c +++ b/drivers/clk/microchip/clk-pic32mzda.c @@ -14,7 +14,7 @@ #include <linux/platform_device.h> #include <asm/traps.h> -#include "clk-core.h" +#include "clk-pic32.h" /* FRC Postscaler */ #define OSC_FRCDIV_MASK 0x07 diff --git a/drivers/clk/mmp/clk-pxa1908-apbc.c b/drivers/clk/mmp/clk-pxa1908-apbc.c index 3fd7b5e644f3..438ece4f047d 100644 --- a/drivers/clk/mmp/clk-pxa1908-apbc.c +++ b/drivers/clk/mmp/clk-pxa1908-apbc.c @@ -7,6 +7,7 @@ #include <dt-bindings/clock/marvell,pxa1908.h> #include "clk.h" +#include "reset.h" #define APBC_UART0 0x0 #define APBC_UART1 0x4 @@ -44,22 +45,25 @@ static const char * const uart_parent_names[] = {"pll1_117", "uart_pll"}; static const char * const ssp_parent_names[] = {"pll1_d16", "pll1_d48", "pll1_d24", "pll1_d12"}; static struct mmp_param_gate_clk apbc_gate_clks[] = { - {PXA1908_CLK_TWSI0, "twsi0_clk", "pll1_32", CLK_SET_RATE_PARENT, APBC_TWSI0, 0x7, 3, 0, 0, NULL}, - {PXA1908_CLK_TWSI1, "twsi1_clk", "pll1_32", CLK_SET_RATE_PARENT, APBC_TWSI1, 0x7, 3, 0, 0, NULL}, - {PXA1908_CLK_TWSI3, "twsi3_clk", "pll1_32", CLK_SET_RATE_PARENT, APBC_TWSI3, 0x7, 3, 0, 0, NULL}, - {PXA1908_CLK_GPIO, "gpio_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_GPIO, 0x7, 3, 0, 0, NULL}, - {PXA1908_CLK_KPC, "kpc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_KPC, 0x7, 3, 0, MMP_CLK_GATE_NEED_DELAY, NULL}, - {PXA1908_CLK_RTC, "rtc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_RTC, 0x87, 0x83, 0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA1908_CLK_TWSI0, "twsi0_clk", "pll1_32", CLK_SET_RATE_PARENT, APBC_TWSI0, 0x3, 3, 0, 0, NULL}, + {PXA1908_CLK_TWSI1, "twsi1_clk", "pll1_32", CLK_SET_RATE_PARENT, APBC_TWSI1, 0x3, 3, 0, 0, NULL}, + {PXA1908_CLK_TWSI3, "twsi3_clk", "pll1_32", CLK_SET_RATE_PARENT, APBC_TWSI3, 0x3, 3, 0, 0, NULL}, + {PXA1908_CLK_GPIO, "gpio_clk", "vctcxo", CLK_SET_RATE_PARENT, APBC_GPIO, 0x3, 3, 0, 0, NULL}, + {PXA1908_CLK_KPC, "kpc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_KPC, 0x3, 3, 0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA1908_CLK_RTC, "rtc_clk", "clk32", CLK_SET_RATE_PARENT, APBC_RTC, 0x83, 0x83, 0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA1908_CLK_PWM1, "pwm1_clk", "pwm01_apb_share", CLK_SET_RATE_PARENT, APBC_PWM1, 0x2, 2, 0, 0, NULL}, + {PXA1908_CLK_PWM3, "pwm3_clk", "pwm23_apb_share", CLK_SET_RATE_PARENT, APBC_PWM3, 0x2, 2, 0, 0, NULL}, + {PXA1908_CLK_UART0, "uart0_clk", "uart0_mux", CLK_SET_RATE_PARENT, APBC_UART0, 0x3, 3, 0, 0, &uart0_lock}, + {PXA1908_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, APBC_UART1, 0x3, 3, 0, 0, &uart1_lock}, + {PXA1908_CLK_THERMAL, "thermal_clk", NULL, 0, APBC_THERMAL, 0x3, 3, 0, 0, NULL}, + {PXA1908_CLK_IPC_RST, "ipc_clk", NULL, 0, APBC_IPC_RST, 0x3, 3, 0, 0, NULL}, + {PXA1908_CLK_SSP0, "ssp0_clk", "ssp0_mux", 0, APBC_SSP0, 0x3, 3, 0, 0, NULL}, + {PXA1908_CLK_SSP2, "ssp2_clk", "ssp2_mux", 0, APBC_SSP2, 0x3, 3, 0, 0, NULL}, +}; + +static struct mmp_param_gate_clk apbc_gate_no_reset_clks[] = { {PXA1908_CLK_PWM0, "pwm0_clk", "pwm01_apb_share", CLK_SET_RATE_PARENT, APBC_PWM0, 0x2, 2, 0, 0, &pwm0_lock}, - {PXA1908_CLK_PWM1, "pwm1_clk", "pwm01_apb_share", CLK_SET_RATE_PARENT, APBC_PWM1, 0x6, 2, 0, 0, NULL}, {PXA1908_CLK_PWM2, "pwm2_clk", "pwm23_apb_share", CLK_SET_RATE_PARENT, APBC_PWM2, 0x2, 2, 0, 0, NULL}, - {PXA1908_CLK_PWM3, "pwm3_clk", "pwm23_apb_share", CLK_SET_RATE_PARENT, APBC_PWM3, 0x6, 2, 0, 0, NULL}, - {PXA1908_CLK_UART0, "uart0_clk", "uart0_mux", CLK_SET_RATE_PARENT, APBC_UART0, 0x7, 3, 0, 0, &uart0_lock}, - {PXA1908_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, APBC_UART1, 0x7, 3, 0, 0, &uart1_lock}, - {PXA1908_CLK_THERMAL, "thermal_clk", NULL, 0, APBC_THERMAL, 0x7, 3, 0, 0, NULL}, - {PXA1908_CLK_IPC_RST, "ipc_clk", NULL, 0, APBC_IPC_RST, 0x7, 3, 0, 0, NULL}, - {PXA1908_CLK_SSP0, "ssp0_clk", "ssp0_mux", 0, APBC_SSP0, 0x7, 3, 0, 0, NULL}, - {PXA1908_CLK_SSP2, "ssp2_clk", "ssp2_mux", 0, APBC_SSP2, 0x7, 3, 0, 0, NULL}, }; static struct mmp_param_mux_clk apbc_mux_clks[] = { @@ -89,6 +93,30 @@ static void pxa1908_apb_periph_clk_init(struct pxa1908_clk_unit *pxa_unit) ARRAY_SIZE(apbc_mux_clks)); mmp_register_gate_clks(unit, apbc_gate_clks, pxa_unit->base, ARRAY_SIZE(apbc_gate_clks)); + mmp_register_gate_clks(unit, apbc_gate_no_reset_clks, pxa_unit->base, + ARRAY_SIZE(apbc_gate_no_reset_clks)); +} + +/* Taken from clk-of-pxa1928.c */ +static void pxa1908_clk_reset_init(struct device_node *np, + struct pxa1908_clk_unit *pxa_unit) +{ + struct mmp_clk_reset_cell *cells; + int nr_cells = ARRAY_SIZE(apbc_gate_clks); + + cells = kzalloc_objs(*cells, nr_cells); + if (!cells) + return; + + for (int i = 0; i < nr_cells; i++) { + cells[i].clk_id = apbc_gate_clks[i].id; + cells[i].reg = pxa_unit->base + apbc_gate_clks[i].offset; + cells[i].bits = BIT(2); + cells[i].flags = 0; + cells[i].lock = apbc_gate_clks[i].lock; + }; + + mmp_clk_reset_register(np, cells, nr_cells); } static int pxa1908_apbc_probe(struct platform_device *pdev) @@ -107,6 +135,8 @@ static int pxa1908_apbc_probe(struct platform_device *pdev) pxa1908_apb_periph_clk_init(pxa_unit); + pxa1908_clk_reset_init(pdev->dev.of_node, pxa_unit); + return 0; } diff --git a/drivers/clk/mmp/clk-pxa1908-apbcp.c b/drivers/clk/mmp/clk-pxa1908-apbcp.c index f638d7e89b47..1aa476103553 100644 --- a/drivers/clk/mmp/clk-pxa1908-apbcp.c +++ b/drivers/clk/mmp/clk-pxa1908-apbcp.c @@ -7,6 +7,7 @@ #include <dt-bindings/clock/marvell,pxa1908.h> #include "clk.h" +#include "reset.h" #define APBCP_UART2 0x1c #define APBCP_TWSI2 0x28 @@ -24,9 +25,9 @@ static DEFINE_SPINLOCK(uart2_lock); static const char * const uart_parent_names[] = {"pll1_117", "uart_pll"}; static struct mmp_param_gate_clk apbcp_gate_clks[] = { - {PXA1908_CLK_UART2, "uart2_clk", "uart2_mux", CLK_SET_RATE_PARENT, APBCP_UART2, 0x7, 0x3, 0x0, 0, &uart2_lock}, - {PXA1908_CLK_TWSI2, "twsi2_clk", "pll1_32", CLK_SET_RATE_PARENT, APBCP_TWSI2, 0x7, 0x3, 0x0, 0, NULL}, - {PXA1908_CLK_AICER, "ripc_clk", NULL, 0, APBCP_AICER, 0x7, 0x2, 0x0, 0, NULL}, + {PXA1908_CLK_UART2, "uart2_clk", "uart2_mux", CLK_SET_RATE_PARENT, APBCP_UART2, 0x3, 0x3, 0x0, 0, &uart2_lock}, + {PXA1908_CLK_TWSI2, "twsi2_clk", "pll1_32", CLK_SET_RATE_PARENT, APBCP_TWSI2, 0x3, 0x3, 0x0, 0, NULL}, + {PXA1908_CLK_AICER, "ripc_clk", NULL, 0, APBCP_AICER, 0x3, 0x2, 0x0, 0, NULL}, }; static struct mmp_param_mux_clk apbcp_mux_clks[] = { @@ -43,6 +44,28 @@ static void pxa1908_apb_p_periph_clk_init(struct pxa1908_clk_unit *pxa_unit) ARRAY_SIZE(apbcp_gate_clks)); } +/* Taken from clk-of-pxa1928.c */ +static void pxa1908_clk_reset_init(struct device_node *np, + struct pxa1908_clk_unit *pxa_unit) +{ + struct mmp_clk_reset_cell *cells; + int nr_cells = ARRAY_SIZE(apbcp_gate_clks); + + cells = kzalloc_objs(*cells, nr_cells); + if (!cells) + return; + + for (int i = 0; i < nr_cells; i++) { + cells[i].clk_id = apbcp_gate_clks[i].id; + cells[i].reg = pxa_unit->base + apbcp_gate_clks[i].offset; + cells[i].bits = BIT(2); + cells[i].flags = 0; + cells[i].lock = apbcp_gate_clks[i].lock; + }; + + mmp_clk_reset_register(np, cells, nr_cells); +} + static int pxa1908_apbcp_probe(struct platform_device *pdev) { struct pxa1908_clk_unit *pxa_unit; @@ -59,6 +82,8 @@ static int pxa1908_apbcp_probe(struct platform_device *pdev) pxa1908_apb_p_periph_clk_init(pxa_unit); + pxa1908_clk_reset_init(pdev->dev.of_node, pxa_unit); + return 0; } diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c index 28f2e1b2a932..0f9ae5f5cefd 100644 --- a/drivers/clk/mvebu/common.c +++ b/drivers/clk/mvebu/common.c @@ -189,10 +189,10 @@ DEFINE_SPINLOCK(ctrl_gating_lock); struct clk_gating_ctrl { spinlock_t *lock; - struct clk **gates; int num_gates; void __iomem *base; u32 saved_reg; + struct clk *gates[] __counted_by(num_gates); }; static struct clk_gating_ctrl *ctrl; @@ -257,24 +257,21 @@ void __init mvebu_clk_gating_setup(struct device_node *np, clk_put(clk); } - ctrl = kzalloc_obj(*ctrl); + /* Count, allocate, and register clock gates */ + for (n = 0; desc[n].name;) + n++; + + ctrl = kzalloc_flex(*ctrl, gates, n); if (WARN_ON(!ctrl)) goto ctrl_out; + ctrl->num_gates = n; + /* lock must already be initialized */ ctrl->lock = &ctrl_gating_lock; ctrl->base = base; - /* Count, allocate, and register clock gates */ - for (n = 0; desc[n].name;) - n++; - - ctrl->num_gates = n; - ctrl->gates = kzalloc_objs(*ctrl->gates, ctrl->num_gates); - if (WARN_ON(!ctrl->gates)) - goto gates_out; - for (n = 0; n < ctrl->num_gates; n++) { const char *parent = (desc[n].parent) ? desc[n].parent : default_parent; @@ -289,8 +286,6 @@ void __init mvebu_clk_gating_setup(struct device_node *np, register_syscore(&clk_gate_syscore); return; -gates_out: - kfree(ctrl); ctrl_out: iounmap(base); } diff --git a/drivers/clk/nxp/clk-lpc18xx-ccu.c b/drivers/clk/nxp/clk-lpc18xx-ccu.c index dcb6d0c0b41a..3793e701835f 100644 --- a/drivers/clk/nxp/clk-lpc18xx-ccu.c +++ b/drivers/clk/nxp/clk-lpc18xx-ccu.c @@ -27,8 +27,8 @@ #define CCU_BRANCH_HAVE_DIV2 BIT(1) struct lpc18xx_branch_clk_data { - const char **name; int num; + const char *name[] __counted_by(num); }; struct lpc18xx_clk_branch { @@ -266,6 +266,7 @@ static void __init lpc18xx_ccu_init(struct device_node *np) { struct lpc18xx_branch_clk_data *clk_data; void __iomem *reg_base; + size_t size; int i, ret; reg_base = of_iomap(np, 0); @@ -274,19 +275,14 @@ static void __init lpc18xx_ccu_init(struct device_node *np) return; } - clk_data = kzalloc_obj(*clk_data); + size = of_property_count_strings(np, "clock-names"); + clk_data = kzalloc_flex(*clk_data, name, size); if (!clk_data) { iounmap(reg_base); return; } - clk_data->num = of_property_count_strings(np, "clock-names"); - clk_data->name = kcalloc(clk_data->num, sizeof(char *), GFP_KERNEL); - if (!clk_data->name) { - iounmap(reg_base); - kfree(clk_data); - return; - } + clk_data->num = size; for (i = 0; i < clk_data->num; i++) { ret = of_property_read_string_index(np, "clock-names", i, diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index df21ef5ffd68..7d84c2f1d911 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -200,6 +200,17 @@ config CLK_X1E80100_TCSRCC Support for the TCSR clock controller on X1E80100 devices. Say Y if you want to use peripheral devices such as SD/UFS. +config CLK_X1P42100_CAMCC + tristate "X1P42100 Camera Clock Controller" + depends on ARM64 || COMPILE_TEST + select CLK_X1E80100_GCC + default m if ARCH_QCOM + help + Support for the camera clock controller on Qualcomm Technologies, Inc. + X1P42100 devices. + Say Y if you want to support camera devices and camera functionality + such as capturing pictures. + config CLK_X1P42100_GPUCC tristate "X1P42100 Graphics Clock Controller" depends on ARM64 || COMPILE_TEST @@ -209,6 +220,17 @@ config CLK_X1P42100_GPUCC Say Y if you want to support graphics controller devices and functionality such as 3D graphics. +config CLK_X1P42100_VIDEOCC + tristate "X1P42100 Video Clock Controller" + depends on ARM64 || COMPILE_TEST + select CLK_X1E80100_GCC + default m if ARCH_QCOM + help + Support for the video clock controller on Qualcomm Technologies, Inc. + X1P42100 devices. + Say Y if you want to support video devices and functionality such as + video encode/decode. + config CLK_QCM2290_GPUCC tristate "QCM2290 Graphics Clock Controller" depends on ARM64 || COMPILE_TEST @@ -296,6 +318,22 @@ config QCOM_CLK_RPMH Say Y if you want to support the clocks exposed by RPMh on platforms such as SDM845. +config CLK_HAWI_GCC + tristate "Hawi Global Clock Controller" + depends on ARM64 || COMPILE_TEST + select QCOM_GDSC + help + Support for the global clock controller on Hawi devices. + Say Y if you want to use peripheral devices such as UART, SPI, + I2C, USB, UFS, SD/eMMC, PCIe, etc. + +config CLK_HAWI_TCSRCC + tristate "Hawi TCSR Clock Controller" + depends on ARM64 || COMPILE_TEST + help + Support for the TCSR clock controller on Hawi devices. + Say Y if you want to use peripheral devices such as PCIe, USB, UFS. + config APQ_GCC_8084 tristate "APQ8084 Global Clock Controller" depends on ARM || COMPILE_TEST @@ -434,6 +472,16 @@ config IPQ_GCC_9574 i2c, USB, SD/eMMC, etc. Select this for the root clock of ipq9574. +config IPQ_GCC_9650 + tristate "IPQ9650 Global Clock Controller" + depends on ARM64 || COMPILE_TEST + default ARCH_QCOM + help + Support for global clock controller on ipq9650 devices. + Say Y if you want to use peripheral devices such as UART, SPI, + i2c, USB, SD/eMMC, etc. Select this for the root clock + of ipq9650. + config IPQ_NSSCC_5424 tristate "IPQ5424 NSS Clock Controller" depends on ARM64 || COMPILE_TEST diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 89d07c35e4d9..58f9a5eb6fd7 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -29,6 +29,8 @@ obj-$(CONFIG_CLK_GLYMUR_GCC) += gcc-glymur.o obj-$(CONFIG_CLK_GLYMUR_GPUCC) += gpucc-glymur.o gxclkctl-kaanapali.o obj-$(CONFIG_CLK_GLYMUR_TCSRCC) += tcsrcc-glymur.o obj-$(CONFIG_CLK_GLYMUR_VIDEOCC) += videocc-glymur.o +obj-$(CONFIG_CLK_HAWI_GCC) += gcc-hawi.o +obj-$(CONFIG_CLK_HAWI_TCSRCC) += tcsrcc-hawi.o obj-$(CONFIG_CLK_KAANAPALI_CAMCC) += cambistmclkcc-kaanapali.o camcc-kaanapali.o obj-$(CONFIG_CLK_KAANAPALI_DISPCC) += dispcc-kaanapali.o obj-$(CONFIG_CLK_KAANAPALI_GCC) += gcc-kaanapali.o @@ -42,7 +44,9 @@ obj-$(CONFIG_CLK_X1E80100_DISPCC) += dispcc-x1e80100.o obj-$(CONFIG_CLK_X1E80100_GCC) += gcc-x1e80100.o obj-$(CONFIG_CLK_X1E80100_GPUCC) += gpucc-x1e80100.o obj-$(CONFIG_CLK_X1E80100_TCSRCC) += tcsrcc-x1e80100.o +obj-$(CONFIG_CLK_X1P42100_CAMCC) += camcc-x1p42100.o obj-$(CONFIG_CLK_X1P42100_GPUCC) += gpucc-x1p42100.o +obj-$(CONFIG_CLK_X1P42100_VIDEOCC) += videocc-x1p42100.o obj-$(CONFIG_CLK_QCM2290_GPUCC) += gpucc-qcm2290.o obj-$(CONFIG_IPQ_APSS_PLL) += apss-ipq-pll.o obj-$(CONFIG_IPQ_APSS_5424) += apss-ipq5424.o @@ -57,6 +61,7 @@ obj-$(CONFIG_IPQ_GCC_6018) += gcc-ipq6018.o obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o obj-$(CONFIG_IPQ_GCC_8074) += gcc-ipq8074.o obj-$(CONFIG_IPQ_GCC_9574) += gcc-ipq9574.o +obj-$(CONFIG_IPQ_GCC_9650) += gcc-ipq9650.o obj-$(CONFIG_IPQ_NSSCC_5424) += nsscc-ipq5424.o obj-$(CONFIG_IPQ_NSSCC_9574) += nsscc-ipq9574.o obj-$(CONFIG_IPQ_LCC_806X) += lcc-ipq806x.o @@ -189,7 +194,7 @@ obj-$(CONFIG_SM_GPUCC_8450) += gpucc-sm8450.o obj-$(CONFIG_SM_GPUCC_8550) += gpucc-sm8550.o obj-$(CONFIG_SM_GPUCC_8650) += gpucc-sm8650.o obj-$(CONFIG_SM_GPUCC_8750) += gpucc-sm8750.o gxclkctl-kaanapali.o -obj-$(CONFIG_SM_GPUCC_MILOS) += gpucc-milos.o +obj-$(CONFIG_SM_GPUCC_MILOS) += gpucc-milos.o gxclkctl-kaanapali.o obj-$(CONFIG_SM_LPASSCC_6115) += lpasscc-sm6115.o obj-$(CONFIG_SM_TCSRCC_8550) += tcsrcc-sm8550.o obj-$(CONFIG_SM_TCSRCC_8650) += tcsrcc-sm8650.o diff --git a/drivers/clk/qcom/a53-pll.c b/drivers/clk/qcom/a53-pll.c index 724a642311e5..0549b214fcfc 100644 --- a/drivers/clk/qcom/a53-pll.c +++ b/drivers/clk/qcom/a53-pll.c @@ -20,7 +20,7 @@ static const struct pll_freq_tbl a53pll_freq[] = { { 998400000, 52, 0x0, 0x1, 0 }, { 1094400000, 57, 0x0, 0x1, 0 }, - { 1152000000, 62, 0x0, 0x1, 0 }, + { 1152000000, 60, 0x0, 0x1, 0 }, { 1209600000, 63, 0x0, 0x1, 0 }, { 1248000000, 65, 0x0, 0x1, 0 }, { 1363200000, 71, 0x0, 0x1, 0 }, diff --git a/drivers/clk/qcom/camcc-milos.c b/drivers/clk/qcom/camcc-milos.c index 409d47098c10..579b71e0e089 100644 --- a/drivers/clk/qcom/camcc-milos.c +++ b/drivers/clk/qcom/camcc-milos.c @@ -30,6 +30,11 @@ enum { DT_IFACE, }; +/* Need to match the order of interconnects in DT binding */ +enum { + DT_ICC_TOP_GDSC, +}; + enum { P_BI_TCXO, P_CAM_CC_PLL0_OUT_EVEN, @@ -1971,6 +1976,8 @@ static struct gdsc cam_cc_camss_top_gdsc = { }, .pwrsts = PWRSTS_OFF_ON, .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, + .needs_icc = true, + .icc_path_index = DT_ICC_TOP_GDSC, }; static struct clk_regmap *cam_cc_milos_clocks[] = { diff --git a/drivers/clk/qcom/camcc-x1e80100.c b/drivers/clk/qcom/camcc-x1e80100.c index 81f579ff6993..c12994af42cf 100644 --- a/drivers/clk/qcom/camcc-x1e80100.c +++ b/drivers/clk/qcom/camcc-x1e80100.c @@ -1052,6 +1052,31 @@ static struct clk_rcg2 cam_cc_mclk7_clk_src = { }, }; +static const struct freq_tbl ftbl_cam_cc_qdss_debug_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(60000000, P_CAM_CC_PLL8_OUT_EVEN, 8, 0, 0), + F(75000000, P_CAM_CC_PLL0_OUT_EVEN, 8, 0, 0), + F(150000000, P_CAM_CC_PLL0_OUT_EVEN, 4, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_qdss_debug_clk_src = { + .cmd_rcgr = 0x13938, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_qdss_debug_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + static const struct freq_tbl ftbl_cam_cc_sfe_0_clk_src[] = { F(345600000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), F(432000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), @@ -2182,6 +2207,42 @@ static struct clk_branch cam_cc_mclk7_clk = { }, }; +static struct clk_branch cam_cc_qdss_debug_clk = { + .halt_reg = 0x13a64, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13a64, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_qdss_debug_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_qdss_debug_xo_clk = { + .halt_reg = 0x13a68, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13a68, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_xo_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch cam_cc_sfe_0_clk = { .halt_reg = 0x133c0, .halt_check = BRANCH_HALT, @@ -2398,6 +2459,9 @@ static struct clk_regmap *cam_cc_x1e80100_clocks[] = { [CAM_CC_PLL6_OUT_EVEN] = &cam_cc_pll6_out_even.clkr, [CAM_CC_PLL8] = &cam_cc_pll8.clkr, [CAM_CC_PLL8_OUT_EVEN] = &cam_cc_pll8_out_even.clkr, + [CAM_CC_QDSS_DEBUG_CLK] = &cam_cc_qdss_debug_clk.clkr, + [CAM_CC_QDSS_DEBUG_CLK_SRC] = &cam_cc_qdss_debug_clk_src.clkr, + [CAM_CC_QDSS_DEBUG_XO_CLK] = &cam_cc_qdss_debug_xo_clk.clkr, [CAM_CC_SFE_0_CLK] = &cam_cc_sfe_0_clk.clkr, [CAM_CC_SFE_0_CLK_SRC] = &cam_cc_sfe_0_clk_src.clkr, [CAM_CC_SFE_0_FAST_AHB_CLK] = &cam_cc_sfe_0_fast_ahb_clk.clkr, diff --git a/drivers/clk/qcom/camcc-x1p42100.c b/drivers/clk/qcom/camcc-x1p42100.c new file mode 100644 index 000000000000..c1a61c267919 --- /dev/null +++ b/drivers/clk/qcom/camcc-x1p42100.c @@ -0,0 +1,2223 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/clk-provider.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/qcom,x1e80100-camcc.h> + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_IFACE, + DT_BI_TCXO, + DT_BI_TCXO_AO, + DT_SLEEP_CLK, +}; + +enum { + P_BI_TCXO, + P_BI_TCXO_AO, + P_CAM_CC_PLL0_OUT_EVEN, + P_CAM_CC_PLL0_OUT_MAIN, + P_CAM_CC_PLL0_OUT_ODD, + P_CAM_CC_PLL1_OUT_EVEN, + P_CAM_CC_PLL2_OUT_EVEN, + P_CAM_CC_PLL2_OUT_MAIN, + P_CAM_CC_PLL3_OUT_EVEN, + P_CAM_CC_PLL6_OUT_EVEN, + P_SLEEP_CLK, +}; + +static const struct pll_vco lucid_ole_vco[] = { + { 249600000, 2300000000, 0 }, +}; + +static const struct pll_vco rivian_ole_vco[] = { + { 777000000, 1285000000, 0 }, +}; + +/* 1200.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll0_config = { + .l = 0x3e, + .alpha = 0x8000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x82aa299c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000003, + .test_ctl_hi1_val = 0x00009000, + .test_ctl_hi2_val = 0x00000034, + .user_ctl_val = 0x00008400, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll cam_cc_pll0 = { + .offset = 0x0, + .config = &cam_cc_pll0_config, + .vco_table = lucid_ole_vco, + .num_vco = ARRAY_SIZE(lucid_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll0_out_even = { + .offset = 0x0, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll0_out_even", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll0_out_odd[] = { + { 0x2, 3 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll0_out_odd = { + .offset = 0x0, + .post_div_shift = 14, + .post_div_table = post_div_table_cam_cc_pll0_out_odd, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll0_out_odd), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll0_out_odd", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +/* 728.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll1_config = { + .l = 0x25, + .alpha = 0xeaaa, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x82aa299c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000003, + .test_ctl_hi1_val = 0x00009000, + .test_ctl_hi2_val = 0x00000034, + .user_ctl_val = 0x00000400, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll cam_cc_pll1 = { + .offset = 0x1000, + .config = &cam_cc_pll1_config, + .vco_table = lucid_ole_vco, + .num_vco = ARRAY_SIZE(lucid_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll1", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll1_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll1_out_even = { + .offset = 0x1000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll1_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll1_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll1_out_even", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll1.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +/* 960.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll2_config = { + .l = 0x32, + .alpha = 0x0, + .config_ctl_val = 0x10000030, + .config_ctl_hi_val = 0x80890263, + .config_ctl_hi1_val = 0x00000217, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00100000, +}; + +static struct clk_alpha_pll cam_cc_pll2 = { + .offset = 0x2000, + .config = &cam_cc_pll2_config, + .vco_table = rivian_ole_vco, + .num_vco = ARRAY_SIZE(rivian_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_RIVIAN_EVO], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll2", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_rivian_evo_ops, + }, + }, +}; + +/* 864.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll3_config = { + .l = 0x2d, + .alpha = 0x0, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x82aa299c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000003, + .test_ctl_hi1_val = 0x00009000, + .test_ctl_hi2_val = 0x00000034, + .user_ctl_val = 0x00000400, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll cam_cc_pll3 = { + .offset = 0x3000, + .config = &cam_cc_pll3_config, + .vco_table = lucid_ole_vco, + .num_vco = ARRAY_SIZE(lucid_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll3", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll3_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll3_out_even = { + .offset = 0x3000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll3_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll3_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll3_out_even", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll3.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +/* 960.0 MHz Configuration */ +static const struct alpha_pll_config cam_cc_pll6_config = { + .l = 0x32, + .alpha = 0x0, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x82aa299c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000003, + .test_ctl_hi1_val = 0x00009000, + .test_ctl_hi2_val = 0x00000034, + .user_ctl_val = 0x00000400, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll cam_cc_pll6 = { + .offset = 0x6000, + .config = &cam_cc_pll6_config, + .vco_table = lucid_ole_vco, + .num_vco = ARRAY_SIZE(lucid_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll6", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll6_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll6_out_even = { + .offset = 0x6000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll6_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll6_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll6_out_even", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_pll6.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_ole_ops, + }, +}; + +static const struct parent_map cam_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL0_OUT_EVEN, 2 }, + { P_CAM_CC_PLL0_OUT_ODD, 3 }, + { P_CAM_CC_PLL6_OUT_EVEN, 5 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll0.clkr.hw }, + { .hw = &cam_cc_pll0_out_even.clkr.hw }, + { .hw = &cam_cc_pll0_out_odd.clkr.hw }, + { .hw = &cam_cc_pll6_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL2_OUT_EVEN, 3 }, + { P_CAM_CC_PLL2_OUT_MAIN, 5 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll2.clkr.hw }, + { .hw = &cam_cc_pll2.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL3_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll3_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL1_OUT_EVEN, 4 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_3[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll1_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_4[] = { + { P_SLEEP_CLK, 0 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_4[] = { + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map cam_cc_parent_map_5[] = { + { P_BI_TCXO, 0 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_5[] = { + { .index = DT_BI_TCXO }, +}; + +static const struct freq_tbl ftbl_cam_cc_bps_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_ODD, 2, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_bps_clk_src = { + .cmd_rcgr = 0x10278, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_bps_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_camnoc_axi_rt_clk_src[] = { + F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_camnoc_axi_rt_clk_src = { + .cmd_rcgr = 0x138f8, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_camnoc_axi_rt_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_axi_rt_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cci_0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(37500000, P_CAM_CC_PLL0_OUT_EVEN, 16, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cci_0_clk_src = { + .cmd_rcgr = 0x1365c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cci_0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_0_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_cci_1_clk_src = { + .cmd_rcgr = 0x1378c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cci_0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_1_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cphy_rx_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(480000000, P_CAM_CC_PLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cphy_rx_clk_src = { + .cmd_rcgr = 0x11164, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cphy_rx_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_csi0phytimer_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_csi0phytimer_clk_src = { + .cmd_rcgr = 0x150e0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi0phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi1phytimer_clk_src = { + .cmd_rcgr = 0x15104, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi1phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi2phytimer_clk_src = { + .cmd_rcgr = 0x15124, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi2phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi3phytimer_clk_src = { + .cmd_rcgr = 0x15258, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi3phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi4phytimer_clk_src = { + .cmd_rcgr = 0x1538c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi4phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi5phytimer_clk_src = { + .cmd_rcgr = 0x154c0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi5phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_csid_clk_src[] = { + F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(480000000, P_CAM_CC_PLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_csid_clk_src = { + .cmd_rcgr = 0x138d4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csid_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_fast_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_EVEN, 3, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_MAIN, 4, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_fast_ahb_clk_src = { + .cmd_rcgr = 0x10018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_fast_ahb_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_fast_ahb_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_icp_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(480000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_icp_clk_src = { + .cmd_rcgr = 0x13520, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_icp_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(432000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(594000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(675000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(727000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_0_clk_src = { + .cmd_rcgr = 0x11018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_2, + .freq_tbl = ftbl_cam_cc_ife_0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_clk_src", + .parent_data = cam_cc_parent_data_2, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_lite_clk_src[] = { + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(480000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_lite_clk_src = { + .cmd_rcgr = 0x13000, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ife_lite_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_lite_csid_clk_src = { + .cmd_rcgr = 0x1313c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ife_lite_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ipe_nps_clk_src[] = { + F(364000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(500000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(700000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ipe_nps_clk_src = { + .cmd_rcgr = 0x103cc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_3, + .freq_tbl = ftbl_cam_cc_ipe_nps_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_nps_clk_src", + .parent_data = cam_cc_parent_data_3, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_jpeg_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_ODD, 2, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(480000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_jpeg_clk_src = { + .cmd_rcgr = 0x133dc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_jpeg_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_jpeg_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_mclk0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(24000000, P_CAM_CC_PLL2_OUT_MAIN, 10, 1, 4), + F(68571429, P_CAM_CC_PLL2_OUT_MAIN, 14, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_mclk0_clk_src = { + .cmd_rcgr = 0x15000, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk0_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk1_clk_src = { + .cmd_rcgr = 0x1501c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk1_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk2_clk_src = { + .cmd_rcgr = 0x15038, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk2_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk3_clk_src = { + .cmd_rcgr = 0x15054, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk3_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk4_clk_src = { + .cmd_rcgr = 0x15070, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk4_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk5_clk_src = { + .cmd_rcgr = 0x1508c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk5_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk6_clk_src = { + .cmd_rcgr = 0x150a8, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk6_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk7_clk_src = { + .cmd_rcgr = 0x150c4, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk7_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_qdss_debug_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(75000000, P_CAM_CC_PLL0_OUT_EVEN, 8, 0, 0), + F(150000000, P_CAM_CC_PLL0_OUT_EVEN, 4, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_qdss_debug_clk_src = { + .cmd_rcgr = 0x13938, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_qdss_debug_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_sleep_clk_src[] = { + F(32000, P_SLEEP_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_sleep_clk_src = { + .cmd_rcgr = 0x13aa0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_4, + .freq_tbl = ftbl_cam_cc_sleep_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_sleep_clk_src", + .parent_data = cam_cc_parent_data_4, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_slow_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(80000000, P_CAM_CC_PLL0_OUT_EVEN, 7.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_slow_ahb_clk_src = { + .cmd_rcgr = 0x10148, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_slow_ahb_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_slow_ahb_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_xo_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_xo_clk_src = { + .cmd_rcgr = 0x13a84, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_5, + .freq_tbl = ftbl_cam_cc_xo_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_xo_clk_src", + .parent_data = cam_cc_parent_data_5, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_branch cam_cc_bps_ahb_clk = { + .halt_reg = 0x10274, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10274, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_clk = { + .halt_reg = 0x103a4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x103a4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_bps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_fast_ahb_clk = { + .halt_reg = 0x10144, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10144, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_fast_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_axi_nrt_clk = { + .halt_reg = 0x13920, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x13920, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x13920, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_axi_nrt_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_camnoc_axi_rt_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_axi_rt_clk = { + .halt_reg = 0x13910, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13910, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_axi_rt_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_camnoc_axi_rt_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_dcd_xo_clk = { + .halt_reg = 0x1392c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1392c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_dcd_xo_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_xo_clk = { + .halt_reg = 0x13930, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13930, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_xo_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_0_clk = { + .halt_reg = 0x13788, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13788, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cci_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_1_clk = { + .halt_reg = 0x138b8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x138b8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cci_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_core_ahb_clk = { + .halt_reg = 0x13a80, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x13a80, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_core_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ahb_clk = { + .halt_reg = 0x138bc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x138bc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_bps_clk = { + .halt_reg = 0x103b0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x103b0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_bps_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_bps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_fast_ahb_clk = { + .halt_reg = 0x138c8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x138c8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_fast_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ife_0_clk = { + .halt_reg = 0x11150, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x11150, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ife_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ife_lite_clk = { + .halt_reg = 0x13138, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13138, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ife_lite_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ipe_nps_clk = { + .halt_reg = 0x10504, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10504, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ipe_nps_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ipe_nps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi0phytimer_clk = { + .halt_reg = 0x150f8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x150f8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi0phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi0phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi1phytimer_clk = { + .halt_reg = 0x1511c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1511c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi1phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi1phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi2phytimer_clk = { + .halt_reg = 0x15250, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15250, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi2phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi2phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi3phytimer_clk = { + .halt_reg = 0x15384, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15384, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi3phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi3phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi4phytimer_clk = { + .halt_reg = 0x154b8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x154b8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi4phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi4phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi5phytimer_clk = { + .halt_reg = 0x155ec, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x155ec, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi5phytimer_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csi5phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csid_clk = { + .halt_reg = 0x138ec, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x138ec, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csid_csiphy_rx_clk = { + .halt_reg = 0x15100, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15100, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csid_csiphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy0_clk = { + .halt_reg = 0x150fc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x150fc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy1_clk = { + .halt_reg = 0x15120, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15120, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy2_clk = { + .halt_reg = 0x15254, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15254, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy2_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy3_clk = { + .halt_reg = 0x15388, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15388, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy3_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy4_clk = { + .halt_reg = 0x154bc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x154bc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy4_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy5_clk = { + .halt_reg = 0x155f0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x155f0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy5_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_ahb_clk = { + .halt_reg = 0x13658, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13658, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_clk = { + .halt_reg = 0x1364c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1364c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_icp_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_clk = { + .halt_reg = 0x11144, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x11144, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_dsp_clk = { + .halt_reg = 0x11154, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x11154, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_dsp_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_fast_ahb_clk = { + .halt_reg = 0x11160, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x11160, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_fast_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_ahb_clk = { + .halt_reg = 0x13278, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13278, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_clk = { + .halt_reg = 0x1312c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1312c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_cphy_rx_clk = { + .halt_reg = 0x13274, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13274, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_cphy_rx_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_csid_clk = { + .halt_reg = 0x13268, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13268, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_csid_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ife_lite_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_nps_ahb_clk = { + .halt_reg = 0x1051c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1051c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_nps_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_nps_clk = { + .halt_reg = 0x104f8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x104f8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_nps_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ipe_nps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_nps_fast_ahb_clk = { + .halt_reg = 0x10520, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10520, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_nps_fast_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_pps_clk = { + .halt_reg = 0x10508, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10508, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_pps_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_ipe_nps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_pps_fast_ahb_clk = { + .halt_reg = 0x10524, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10524, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_pps_fast_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_jpeg_clk = { + .halt_reg = 0x13508, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13508, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_jpeg_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_jpeg_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk0_clk = { + .halt_reg = 0x15018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk0_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk1_clk = { + .halt_reg = 0x15034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk1_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk2_clk = { + .halt_reg = 0x15050, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15050, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk2_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk3_clk = { + .halt_reg = 0x1506c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1506c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk3_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk4_clk = { + .halt_reg = 0x15088, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15088, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk4_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk5_clk = { + .halt_reg = 0x150a4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x150a4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk5_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk6_clk = { + .halt_reg = 0x150c0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x150c0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk6_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk6_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk7_clk = { + .halt_reg = 0x150dc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x150dc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk7_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_mclk7_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_qdss_debug_clk = { + .halt_reg = 0x13a64, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13a64, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_qdss_debug_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_qdss_debug_xo_clk = { + .halt_reg = 0x13a68, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13a68, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_xo_clk", + .parent_hws = (const struct clk_hw*[]) { + &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc cam_cc_titan_top_gdsc = { + .gdscr = 0x13a6c, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "cam_cc_titan_top_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc cam_cc_bps_gdsc = { + .gdscr = 0x10004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "cam_cc_bps_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &cam_cc_titan_top_gdsc.pd, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc cam_cc_ife_0_gdsc = { + .gdscr = 0x11004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "cam_cc_ife_0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &cam_cc_titan_top_gdsc.pd, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc cam_cc_ipe_0_gdsc = { + .gdscr = 0x103b8, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "cam_cc_ipe_0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &cam_cc_titan_top_gdsc.pd, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *cam_cc_x1p42100_clocks[] = { + [CAM_CC_BPS_AHB_CLK] = &cam_cc_bps_ahb_clk.clkr, + [CAM_CC_BPS_CLK] = &cam_cc_bps_clk.clkr, + [CAM_CC_BPS_CLK_SRC] = &cam_cc_bps_clk_src.clkr, + [CAM_CC_BPS_FAST_AHB_CLK] = &cam_cc_bps_fast_ahb_clk.clkr, + [CAM_CC_CAMNOC_AXI_NRT_CLK] = &cam_cc_camnoc_axi_nrt_clk.clkr, + [CAM_CC_CAMNOC_AXI_RT_CLK] = &cam_cc_camnoc_axi_rt_clk.clkr, + [CAM_CC_CAMNOC_AXI_RT_CLK_SRC] = &cam_cc_camnoc_axi_rt_clk_src.clkr, + [CAM_CC_CAMNOC_DCD_XO_CLK] = &cam_cc_camnoc_dcd_xo_clk.clkr, + [CAM_CC_CAMNOC_XO_CLK] = &cam_cc_camnoc_xo_clk.clkr, + [CAM_CC_CCI_0_CLK] = &cam_cc_cci_0_clk.clkr, + [CAM_CC_CCI_0_CLK_SRC] = &cam_cc_cci_0_clk_src.clkr, + [CAM_CC_CCI_1_CLK] = &cam_cc_cci_1_clk.clkr, + [CAM_CC_CCI_1_CLK_SRC] = &cam_cc_cci_1_clk_src.clkr, + [CAM_CC_CORE_AHB_CLK] = &cam_cc_core_ahb_clk.clkr, + [CAM_CC_CPAS_AHB_CLK] = &cam_cc_cpas_ahb_clk.clkr, + [CAM_CC_CPAS_BPS_CLK] = &cam_cc_cpas_bps_clk.clkr, + [CAM_CC_CPAS_FAST_AHB_CLK] = &cam_cc_cpas_fast_ahb_clk.clkr, + [CAM_CC_CPAS_IFE_0_CLK] = &cam_cc_cpas_ife_0_clk.clkr, + [CAM_CC_CPAS_IFE_LITE_CLK] = &cam_cc_cpas_ife_lite_clk.clkr, + [CAM_CC_CPAS_IPE_NPS_CLK] = &cam_cc_cpas_ipe_nps_clk.clkr, + [CAM_CC_CPHY_RX_CLK_SRC] = &cam_cc_cphy_rx_clk_src.clkr, + [CAM_CC_CSI0PHYTIMER_CLK] = &cam_cc_csi0phytimer_clk.clkr, + [CAM_CC_CSI0PHYTIMER_CLK_SRC] = &cam_cc_csi0phytimer_clk_src.clkr, + [CAM_CC_CSI1PHYTIMER_CLK] = &cam_cc_csi1phytimer_clk.clkr, + [CAM_CC_CSI1PHYTIMER_CLK_SRC] = &cam_cc_csi1phytimer_clk_src.clkr, + [CAM_CC_CSI2PHYTIMER_CLK] = &cam_cc_csi2phytimer_clk.clkr, + [CAM_CC_CSI2PHYTIMER_CLK_SRC] = &cam_cc_csi2phytimer_clk_src.clkr, + [CAM_CC_CSI3PHYTIMER_CLK] = &cam_cc_csi3phytimer_clk.clkr, + [CAM_CC_CSI3PHYTIMER_CLK_SRC] = &cam_cc_csi3phytimer_clk_src.clkr, + [CAM_CC_CSI4PHYTIMER_CLK] = &cam_cc_csi4phytimer_clk.clkr, + [CAM_CC_CSI4PHYTIMER_CLK_SRC] = &cam_cc_csi4phytimer_clk_src.clkr, + [CAM_CC_CSI5PHYTIMER_CLK] = &cam_cc_csi5phytimer_clk.clkr, + [CAM_CC_CSI5PHYTIMER_CLK_SRC] = &cam_cc_csi5phytimer_clk_src.clkr, + [CAM_CC_CSID_CLK] = &cam_cc_csid_clk.clkr, + [CAM_CC_CSID_CLK_SRC] = &cam_cc_csid_clk_src.clkr, + [CAM_CC_CSID_CSIPHY_RX_CLK] = &cam_cc_csid_csiphy_rx_clk.clkr, + [CAM_CC_CSIPHY0_CLK] = &cam_cc_csiphy0_clk.clkr, + [CAM_CC_CSIPHY1_CLK] = &cam_cc_csiphy1_clk.clkr, + [CAM_CC_CSIPHY2_CLK] = &cam_cc_csiphy2_clk.clkr, + [CAM_CC_CSIPHY3_CLK] = &cam_cc_csiphy3_clk.clkr, + [CAM_CC_CSIPHY4_CLK] = &cam_cc_csiphy4_clk.clkr, + [CAM_CC_CSIPHY5_CLK] = &cam_cc_csiphy5_clk.clkr, + [CAM_CC_FAST_AHB_CLK_SRC] = &cam_cc_fast_ahb_clk_src.clkr, + [CAM_CC_ICP_AHB_CLK] = &cam_cc_icp_ahb_clk.clkr, + [CAM_CC_ICP_CLK] = &cam_cc_icp_clk.clkr, + [CAM_CC_ICP_CLK_SRC] = &cam_cc_icp_clk_src.clkr, + [CAM_CC_IFE_0_CLK] = &cam_cc_ife_0_clk.clkr, + [CAM_CC_IFE_0_CLK_SRC] = &cam_cc_ife_0_clk_src.clkr, + [CAM_CC_IFE_0_DSP_CLK] = &cam_cc_ife_0_dsp_clk.clkr, + [CAM_CC_IFE_0_FAST_AHB_CLK] = &cam_cc_ife_0_fast_ahb_clk.clkr, + [CAM_CC_IFE_LITE_AHB_CLK] = &cam_cc_ife_lite_ahb_clk.clkr, + [CAM_CC_IFE_LITE_CLK] = &cam_cc_ife_lite_clk.clkr, + [CAM_CC_IFE_LITE_CLK_SRC] = &cam_cc_ife_lite_clk_src.clkr, + [CAM_CC_IFE_LITE_CPHY_RX_CLK] = &cam_cc_ife_lite_cphy_rx_clk.clkr, + [CAM_CC_IFE_LITE_CSID_CLK] = &cam_cc_ife_lite_csid_clk.clkr, + [CAM_CC_IFE_LITE_CSID_CLK_SRC] = &cam_cc_ife_lite_csid_clk_src.clkr, + [CAM_CC_IPE_NPS_AHB_CLK] = &cam_cc_ipe_nps_ahb_clk.clkr, + [CAM_CC_IPE_NPS_CLK] = &cam_cc_ipe_nps_clk.clkr, + [CAM_CC_IPE_NPS_CLK_SRC] = &cam_cc_ipe_nps_clk_src.clkr, + [CAM_CC_IPE_NPS_FAST_AHB_CLK] = &cam_cc_ipe_nps_fast_ahb_clk.clkr, + [CAM_CC_IPE_PPS_CLK] = &cam_cc_ipe_pps_clk.clkr, + [CAM_CC_IPE_PPS_FAST_AHB_CLK] = &cam_cc_ipe_pps_fast_ahb_clk.clkr, + [CAM_CC_JPEG_CLK] = &cam_cc_jpeg_clk.clkr, + [CAM_CC_JPEG_CLK_SRC] = &cam_cc_jpeg_clk_src.clkr, + [CAM_CC_MCLK0_CLK] = &cam_cc_mclk0_clk.clkr, + [CAM_CC_MCLK0_CLK_SRC] = &cam_cc_mclk0_clk_src.clkr, + [CAM_CC_MCLK1_CLK] = &cam_cc_mclk1_clk.clkr, + [CAM_CC_MCLK1_CLK_SRC] = &cam_cc_mclk1_clk_src.clkr, + [CAM_CC_MCLK2_CLK] = &cam_cc_mclk2_clk.clkr, + [CAM_CC_MCLK2_CLK_SRC] = &cam_cc_mclk2_clk_src.clkr, + [CAM_CC_MCLK3_CLK] = &cam_cc_mclk3_clk.clkr, + [CAM_CC_MCLK3_CLK_SRC] = &cam_cc_mclk3_clk_src.clkr, + [CAM_CC_MCLK4_CLK] = &cam_cc_mclk4_clk.clkr, + [CAM_CC_MCLK4_CLK_SRC] = &cam_cc_mclk4_clk_src.clkr, + [CAM_CC_MCLK5_CLK] = &cam_cc_mclk5_clk.clkr, + [CAM_CC_MCLK5_CLK_SRC] = &cam_cc_mclk5_clk_src.clkr, + [CAM_CC_MCLK6_CLK] = &cam_cc_mclk6_clk.clkr, + [CAM_CC_MCLK6_CLK_SRC] = &cam_cc_mclk6_clk_src.clkr, + [CAM_CC_MCLK7_CLK] = &cam_cc_mclk7_clk.clkr, + [CAM_CC_MCLK7_CLK_SRC] = &cam_cc_mclk7_clk_src.clkr, + [CAM_CC_PLL0] = &cam_cc_pll0.clkr, + [CAM_CC_PLL0_OUT_EVEN] = &cam_cc_pll0_out_even.clkr, + [CAM_CC_PLL0_OUT_ODD] = &cam_cc_pll0_out_odd.clkr, + [CAM_CC_PLL1] = &cam_cc_pll1.clkr, + [CAM_CC_PLL1_OUT_EVEN] = &cam_cc_pll1_out_even.clkr, + [CAM_CC_PLL2] = &cam_cc_pll2.clkr, + [CAM_CC_PLL3] = &cam_cc_pll3.clkr, + [CAM_CC_PLL3_OUT_EVEN] = &cam_cc_pll3_out_even.clkr, + [CAM_CC_PLL6] = &cam_cc_pll6.clkr, + [CAM_CC_PLL6_OUT_EVEN] = &cam_cc_pll6_out_even.clkr, + [CAM_CC_QDSS_DEBUG_CLK] = &cam_cc_qdss_debug_clk.clkr, + [CAM_CC_QDSS_DEBUG_CLK_SRC] = &cam_cc_qdss_debug_clk_src.clkr, + [CAM_CC_QDSS_DEBUG_XO_CLK] = &cam_cc_qdss_debug_xo_clk.clkr, + [CAM_CC_SLEEP_CLK_SRC] = &cam_cc_sleep_clk_src.clkr, + [CAM_CC_SLOW_AHB_CLK_SRC] = &cam_cc_slow_ahb_clk_src.clkr, + [CAM_CC_XO_CLK_SRC] = &cam_cc_xo_clk_src.clkr, +}; + +static struct gdsc *cam_cc_x1p42100_gdscs[] = { + [CAM_CC_BPS_GDSC] = &cam_cc_bps_gdsc, + [CAM_CC_IFE_0_GDSC] = &cam_cc_ife_0_gdsc, + [CAM_CC_IPE_0_GDSC] = &cam_cc_ipe_0_gdsc, + [CAM_CC_TITAN_TOP_GDSC] = &cam_cc_titan_top_gdsc, +}; + +static const struct qcom_reset_map cam_cc_x1p42100_resets[] = { + [CAM_CC_BPS_BCR] = { 0x10000 }, + [CAM_CC_ICP_BCR] = { 0x1351c }, + [CAM_CC_IFE_0_BCR] = { 0x11000 }, + [CAM_CC_IPE_0_BCR] = { 0x103b4 }, +}; + +static struct clk_alpha_pll *cam_cc_x1p42100_plls[] = { + &cam_cc_pll0, + &cam_cc_pll1, + &cam_cc_pll2, + &cam_cc_pll3, + &cam_cc_pll6, +}; + +static u32 cam_cc_x1p42100_critical_cbcrs[] = { + 0x13a9c, /* CAM_CC_GDSC_CLK */ + 0x13ab8, /* CAM_CC_SLEEP_CLK */ +}; + +static const struct regmap_config cam_cc_x1p42100_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1603c, + .fast_io = true, +}; + +static struct qcom_cc_driver_data cam_cc_x1p42100_driver_data = { + .alpha_plls = cam_cc_x1p42100_plls, + .num_alpha_plls = ARRAY_SIZE(cam_cc_x1p42100_plls), + .clk_cbcrs = cam_cc_x1p42100_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(cam_cc_x1p42100_critical_cbcrs), +}; + +static struct qcom_cc_desc cam_cc_x1p42100_desc = { + .config = &cam_cc_x1p42100_regmap_config, + .clks = cam_cc_x1p42100_clocks, + .num_clks = ARRAY_SIZE(cam_cc_x1p42100_clocks), + .resets = cam_cc_x1p42100_resets, + .num_resets = ARRAY_SIZE(cam_cc_x1p42100_resets), + .gdscs = cam_cc_x1p42100_gdscs, + .num_gdscs = ARRAY_SIZE(cam_cc_x1p42100_gdscs), + .use_rpm = true, + .driver_data = &cam_cc_x1p42100_driver_data, +}; + +static const struct of_device_id cam_cc_x1p42100_match_table[] = { + { .compatible = "qcom,x1p42100-camcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, cam_cc_x1p42100_match_table); + +static int cam_cc_x1p42100_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &cam_cc_x1p42100_desc); +} + +static struct platform_driver cam_cc_x1p42100_driver = { + .probe = cam_cc_x1p42100_probe, + .driver = { + .name = "camcc-x1p42100", + .of_match_table = cam_cc_x1p42100_match_table, + }, +}; + +module_platform_driver(cam_cc_x1p42100_driver); + +MODULE_DESCRIPTION("QTI CAMCC X1P42100 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index 42d334492145..3a2157bebc52 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -31,6 +31,7 @@ enum { CLK_ALPHA_PLL_TYPE_PONGO_EKO_T = CLK_ALPHA_PLL_TYPE_PONGO_ELU, CLK_ALPHA_PLL_TYPE_TAYCAN_ELU, CLK_ALPHA_PLL_TYPE_TAYCAN_EKO_T = CLK_ALPHA_PLL_TYPE_TAYCAN_ELU, + CLK_ALPHA_PLL_TYPE_TAYCAN_EHA_T = CLK_ALPHA_PLL_TYPE_TAYCAN_ELU, CLK_ALPHA_PLL_TYPE_RIVIAN_EVO, CLK_ALPHA_PLL_TYPE_RIVIAN_ELU, CLK_ALPHA_PLL_TYPE_RIVIAN_EKO_T = CLK_ALPHA_PLL_TYPE_RIVIAN_ELU, @@ -198,16 +199,19 @@ extern const struct clk_ops clk_alpha_pll_zonda_ops; extern const struct clk_ops clk_alpha_pll_lucid_evo_ops; #define clk_alpha_pll_taycan_elu_ops clk_alpha_pll_lucid_evo_ops #define clk_alpha_pll_taycan_eko_t_ops clk_alpha_pll_lucid_evo_ops +#define clk_alpha_pll_taycan_eha_t_ops clk_alpha_pll_lucid_evo_ops extern const struct clk_ops clk_alpha_pll_reset_lucid_evo_ops; #define clk_alpha_pll_reset_lucid_ole_ops clk_alpha_pll_reset_lucid_evo_ops extern const struct clk_ops clk_alpha_pll_fixed_lucid_evo_ops; #define clk_alpha_pll_fixed_lucid_ole_ops clk_alpha_pll_fixed_lucid_evo_ops #define clk_alpha_pll_fixed_taycan_elu_ops clk_alpha_pll_fixed_lucid_evo_ops #define clk_alpha_pll_fixed_taycan_eko_t_ops clk_alpha_pll_fixed_lucid_evo_ops +#define clk_alpha_pll_fixed_taycan_eha_t_ops clk_alpha_pll_fixed_lucid_evo_ops extern const struct clk_ops clk_alpha_pll_postdiv_lucid_evo_ops; #define clk_alpha_pll_postdiv_lucid_ole_ops clk_alpha_pll_postdiv_lucid_evo_ops #define clk_alpha_pll_postdiv_taycan_elu_ops clk_alpha_pll_postdiv_lucid_evo_ops #define clk_alpha_pll_postdiv_taycan_eko_t_ops clk_alpha_pll_postdiv_lucid_evo_ops +#define clk_alpha_pll_postdiv_taycan_eha_t_ops clk_alpha_pll_postdiv_lucid_evo_ops extern const struct clk_ops clk_alpha_pll_pongo_elu_ops; #define clk_alpha_pll_pongo_eko_t_ops clk_alpha_pll_pongo_elu_ops @@ -246,6 +250,8 @@ void clk_pongo_elu_pll_configure(struct clk_alpha_pll *pll, struct regmap *regma clk_lucid_evo_pll_configure(pll, regmap, config) #define clk_taycan_eko_t_pll_configure(pll, regmap, config) \ clk_lucid_evo_pll_configure(pll, regmap, config) +#define clk_taycan_eha_t_pll_configure(pll, regmap, config) \ + clk_lucid_evo_pll_configure(pll, regmap, config) void clk_rivian_evo_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config); diff --git a/drivers/clk/qcom/clk-regmap-phy-mux.c b/drivers/clk/qcom/clk-regmap-phy-mux.c index 7b7243b7107d..b7d1c69d62f7 100644 --- a/drivers/clk/qcom/clk-regmap-phy-mux.c +++ b/drivers/clk/qcom/clk-regmap-phy-mux.c @@ -15,48 +15,66 @@ #define PHY_MUX_PHY_SRC 0 #define PHY_MUX_REF_SRC 2 +#define XO_RATE 19200000UL + static inline struct clk_regmap_phy_mux *to_clk_regmap_phy_mux(struct clk_regmap *clkr) { return container_of(clkr, struct clk_regmap_phy_mux, clkr); } -static int phy_mux_is_enabled(struct clk_hw *hw) +static unsigned long phy_mux_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_regmap *clkr = to_clk_regmap(hw); struct clk_regmap_phy_mux *phy_mux = to_clk_regmap_phy_mux(clkr); - unsigned int val; + u32 val; regmap_read(clkr->regmap, phy_mux->reg, &val); - val = FIELD_GET(PHY_MUX_MASK, val); - - WARN_ON(val != PHY_MUX_PHY_SRC && val != PHY_MUX_REF_SRC); - return val == PHY_MUX_PHY_SRC; + switch (FIELD_GET(PHY_MUX_MASK, val)) { + case PHY_MUX_PHY_SRC: + return ULONG_MAX; + case PHY_MUX_REF_SRC: + return XO_RATE; + default: + return 0; + } } -static int phy_mux_enable(struct clk_hw *hw) +static int phy_mux_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { - struct clk_regmap *clkr = to_clk_regmap(hw); - struct clk_regmap_phy_mux *phy_mux = to_clk_regmap_phy_mux(clkr); + if (req->rate == XO_RATE || req->rate == ULONG_MAX) + return 0; - return regmap_update_bits(clkr->regmap, phy_mux->reg, - PHY_MUX_MASK, - FIELD_PREP(PHY_MUX_MASK, PHY_MUX_PHY_SRC)); + return -EINVAL; } -static void phy_mux_disable(struct clk_hw *hw) +static int phy_mux_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { struct clk_regmap *clkr = to_clk_regmap(hw); struct clk_regmap_phy_mux *phy_mux = to_clk_regmap_phy_mux(clkr); + u32 val; + + switch (rate) { + case XO_RATE: + val = PHY_MUX_REF_SRC; + break; + case ULONG_MAX: + val = PHY_MUX_PHY_SRC; + break; + default: + return -EINVAL; + } regmap_update_bits(clkr->regmap, phy_mux->reg, PHY_MUX_MASK, - FIELD_PREP(PHY_MUX_MASK, PHY_MUX_REF_SRC)); + FIELD_PREP(PHY_MUX_MASK, val)); + + return 0; } const struct clk_ops clk_regmap_phy_mux_ops = { - .enable = phy_mux_enable, - .disable = phy_mux_disable, - .is_enabled = phy_mux_is_enabled, + .recalc_rate = phy_mux_recalc_rate, + .determine_rate = phy_mux_determine_rate, + .set_rate = phy_mux_set_rate, }; EXPORT_SYMBOL_GPL(clk_regmap_phy_mux_ops); diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c index 339a6bbcdc4c..6367b2a5a4e0 100644 --- a/drivers/clk/qcom/clk-rpmh.c +++ b/drivers/clk/qcom/clk-rpmh.c @@ -66,8 +66,6 @@ struct clk_rpmh { struct clk_rpmh_desc { struct clk_hw **clks; size_t num_clks; - /* RPMh clock clkaN are optional for this platform */ - bool clka_optional; }; static DEFINE_MUTEX(rpmh_clk_lock); @@ -409,7 +407,9 @@ DEFINE_CLK_RPMH_VRM(clk5, _a2_e0, "C5A_E0", 2); DEFINE_CLK_RPMH_VRM(clk6, _a2_e0, "C6A_E0", 2); DEFINE_CLK_RPMH_VRM(clk7, _a2_e0, "C7A_E0", 2); DEFINE_CLK_RPMH_VRM(clk8, _a2_e0, "C8A_E0", 2); +DEFINE_CLK_RPMH_VRM(clk9, _a2_e0, "C9A_E0", 2); +DEFINE_CLK_RPMH_VRM(clk7, _a4_e0, "C7A_E0", 4); DEFINE_CLK_RPMH_VRM(clk11, _a4_e0, "C11A_E0", 4); DEFINE_CLK_RPMH_BCM(ce, "CE0"); @@ -697,7 +697,6 @@ static struct clk_hw *sm8550_rpmh_clocks[] = { static const struct clk_rpmh_desc clk_rpmh_sm8550 = { .clks = sm8550_rpmh_clocks, .num_clks = ARRAY_SIZE(sm8550_rpmh_clocks), - .clka_optional = true, }; static struct clk_hw *sm8650_rpmh_clocks[] = { @@ -729,7 +728,6 @@ static struct clk_hw *sm8650_rpmh_clocks[] = { static const struct clk_rpmh_desc clk_rpmh_sm8650 = { .clks = sm8650_rpmh_clocks, .num_clks = ARRAY_SIZE(sm8650_rpmh_clocks), - .clka_optional = true, }; static struct clk_hw *sc7280_rpmh_clocks[] = { @@ -899,7 +897,6 @@ static struct clk_hw *sm8750_rpmh_clocks[] = { static const struct clk_rpmh_desc clk_rpmh_sm8750 = { .clks = sm8750_rpmh_clocks, .num_clks = ARRAY_SIZE(sm8750_rpmh_clocks), - .clka_optional = true, }; static struct clk_hw *glymur_rpmh_clocks[] = { @@ -984,6 +981,36 @@ static const struct clk_rpmh_desc clk_rpmh_nord = { .num_clks = ARRAY_SIZE(nord_rpmh_clocks), }; +static struct clk_hw *hawi_rpmh_clocks[] = { + [RPMH_CXO_CLK] = &clk_rpmh_bi_tcxo_div2.hw, + [RPMH_CXO_CLK_A] = &clk_rpmh_bi_tcxo_div2_ao.hw, + [RPMH_DIV_CLK1] = &clk_rpmh_clk11_a4_e0.hw, + [RPMH_LN_BB_CLK1] = &clk_rpmh_clk6_a2_e0.hw, + [RPMH_LN_BB_CLK1_A] = &clk_rpmh_clk6_a2_e0_ao.hw, + [RPMH_LN_BB_CLK2] = &clk_rpmh_clk7_a4_e0.hw, + [RPMH_LN_BB_CLK2_A] = &clk_rpmh_clk7_a4_e0_ao.hw, + [RPMH_LN_BB_CLK3] = &clk_rpmh_clk8_a2_e0.hw, + [RPMH_LN_BB_CLK3_A] = &clk_rpmh_clk8_a2_e0_ao.hw, + [RPMH_LN_BB_CLK4] = &clk_rpmh_clk9_a2_e0.hw, + [RPMH_LN_BB_CLK4_A] = &clk_rpmh_clk9_a2_e0_ao.hw, + [RPMH_RF_CLK1] = &clk_rpmh_clk1_a1_e0.hw, + [RPMH_RF_CLK1_A] = &clk_rpmh_clk1_a1_e0_ao.hw, + [RPMH_RF_CLK2] = &clk_rpmh_clk2_a1_e0.hw, + [RPMH_RF_CLK2_A] = &clk_rpmh_clk2_a1_e0_ao.hw, + [RPMH_RF_CLK3] = &clk_rpmh_clk3_a2_e0.hw, + [RPMH_RF_CLK3_A] = &clk_rpmh_clk3_a2_e0_ao.hw, + [RPMH_RF_CLK4] = &clk_rpmh_clk4_a2_e0.hw, + [RPMH_RF_CLK4_A] = &clk_rpmh_clk4_a2_e0_ao.hw, + [RPMH_RF_CLK5] = &clk_rpmh_clk5_a2_e0.hw, + [RPMH_RF_CLK5_A] = &clk_rpmh_clk5_a2_e0_ao.hw, + [RPMH_IPA_CLK] = &clk_rpmh_ipa.hw, +}; + +static const struct clk_rpmh_desc clk_rpmh_hawi = { + .clks = hawi_rpmh_clocks, + .num_clks = ARRAY_SIZE(hawi_rpmh_clocks), +}; + static struct clk_hw *of_clk_rpmh_hw_get(struct of_phandle_args *clkspec, void *data) { @@ -1027,8 +1054,7 @@ static int clk_rpmh_probe(struct platform_device *pdev) if (!res_addr) { hw_clks[i] = NULL; - if (desc->clka_optional && - !strncmp(rpmh_clk->res_name, "clka", sizeof("clka") - 1)) + if (rpmh_clk->res_addr == CLK_RPMH_VRM_EN_OFFSET) continue; dev_err(&pdev->dev, "missing RPMh resource address for %s\n", @@ -1075,6 +1101,7 @@ static int clk_rpmh_probe(struct platform_device *pdev) static const struct of_device_id clk_rpmh_match_table[] = { { .compatible = "qcom,eliza-rpmh-clk", .data = &clk_rpmh_eliza}, { .compatible = "qcom,glymur-rpmh-clk", .data = &clk_rpmh_glymur}, + { .compatible = "qcom,hawi-rpmh-clk", .data = &clk_rpmh_hawi}, { .compatible = "qcom,kaanapali-rpmh-clk", .data = &clk_rpmh_kaanapali}, { .compatible = "qcom,milos-rpmh-clk", .data = &clk_rpmh_milos}, { .compatible = "qcom,nord-rpmh-clk", .data = &clk_rpmh_nord}, diff --git a/drivers/clk/qcom/dispcc-x1e80100.c b/drivers/clk/qcom/dispcc-x1e80100.c index cd45bedf2649..1d7c569dc082 100644 --- a/drivers/clk/qcom/dispcc-x1e80100.c +++ b/drivers/clk/qcom/dispcc-x1e80100.c @@ -1634,7 +1634,7 @@ static const struct regmap_config disp_cc_x1e80100_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = 0x11008, + .max_register = 0xf004, /* 0x10000, 0x10004 and maybe others are for TZ */ .fast_io = true, }; diff --git a/drivers/clk/qcom/gcc-hawi.c b/drivers/clk/qcom/gcc-hawi.c new file mode 100644 index 000000000000..6dd07c772c29 --- /dev/null +++ b/drivers/clk/qcom/gcc-hawi.c @@ -0,0 +1,3657 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/clk-provider.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/qcom,hawi-gcc.h> + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "clk-regmap-phy-mux.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_BI_TCXO, + DT_BI_TCXO_AO, + DT_SLEEP_CLK, + DT_PCIE_0_PIPE_CLK, + DT_PCIE_1_PIPE_CLK, + DT_UFS_PHY_RX_SYMBOL_0_CLK, + DT_UFS_PHY_RX_SYMBOL_1_CLK, + DT_UFS_PHY_TX_SYMBOL_0_CLK, + DT_USB3_PHY_WRAPPER_GCC_USB30_PIPE_CLK, +}; + +enum { + P_BI_TCXO, + P_GCC_GPLL0_OUT_EVEN, + P_GCC_GPLL0_OUT_MAIN, + P_GCC_GPLL4_OUT_MAIN, + P_GCC_GPLL5_OUT_MAIN, + P_GCC_GPLL7_OUT_MAIN, + P_GCC_GPLL9_OUT_MAIN, + P_PCIE_0_PIPE_CLK, + P_PCIE_1_PIPE_CLK, + P_SLEEP_CLK, + P_UFS_PHY_RX_SYMBOL_0_CLK, + P_UFS_PHY_RX_SYMBOL_1_CLK, + P_UFS_PHY_TX_SYMBOL_0_CLK, + P_USB3_PHY_WRAPPER_GCC_USB30_PIPE_CLK, +}; + +static struct clk_alpha_pll gcc_gpll0 = { + .offset = 0x0, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EHA_T], + .clkr = { + .enable_reg = 0x52028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_taycan_eha_t_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gcc_gpll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gcc_gpll0_out_even = { + .offset = 0x0, + .post_div_shift = 10, + .post_div_table = post_div_table_gcc_gpll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_gcc_gpll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EHA_T], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll0_out_even", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gpll0.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_taycan_eha_t_ops, + }, +}; + +static struct clk_alpha_pll gcc_gpll4 = { + .offset = 0x4000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EHA_T], + .clkr = { + .enable_reg = 0x52028, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll4", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_taycan_eha_t_ops, + }, + }, +}; + +static struct clk_alpha_pll gcc_gpll5 = { + .offset = 0x5000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EHA_T], + .clkr = { + .enable_reg = 0x52028, + .enable_mask = BIT(5), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll5", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_taycan_eha_t_ops, + }, + }, +}; + +static struct clk_alpha_pll gcc_gpll7 = { + .offset = 0x7000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EHA_T], + .clkr = { + .enable_reg = 0x52028, + .enable_mask = BIT(7), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll7", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_taycan_eha_t_ops, + }, + }, +}; + +static struct clk_alpha_pll gcc_gpll9 = { + .offset = 0x9000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_TAYCAN_EHA_T], + .clkr = { + .enable_reg = 0x52028, + .enable_mask = BIT(9), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpll9", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_taycan_eha_t_ops, + }, + }, +}; + +static const struct parent_map gcc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL7_OUT_MAIN, 2 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll7.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_SLEEP_CLK, 5 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .index = DT_SLEEP_CLK }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_3[] = { + { P_BI_TCXO, 0 }, +}; + +static const struct clk_parent_data gcc_parent_data_3[] = { + { .index = DT_BI_TCXO }, +}; + +static const struct parent_map gcc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_SLEEP_CLK, 5 }, +}; + +static const struct clk_parent_data gcc_parent_data_4[] = { + { .index = DT_BI_TCXO }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map gcc_parent_map_5[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL5_OUT_MAIN, 3 }, + { P_GCC_GPLL4_OUT_MAIN, 5 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_5[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll5.clkr.hw }, + { .hw = &gcc_gpll4.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_8[] = { + { P_BI_TCXO, 0 }, + { P_GCC_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL9_OUT_MAIN, 2 }, + { P_GCC_GPLL4_OUT_MAIN, 5 }, + { P_GCC_GPLL0_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_8[] = { + { .index = DT_BI_TCXO }, + { .hw = &gcc_gpll0.clkr.hw }, + { .hw = &gcc_gpll9.clkr.hw }, + { .hw = &gcc_gpll4.clkr.hw }, + { .hw = &gcc_gpll0_out_even.clkr.hw }, +}; + +static struct clk_regmap_phy_mux gcc_pcie_0_pipe_clk_src = { + .reg = 0x6b0a8, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_pipe_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_PCIE_0_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie_1_pipe_clk_src = { + .reg = 0x670a4, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_pipe_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_PCIE_1_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_ufs_phy_rx_symbol_0_clk_src = { + .reg = 0x77068, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_rx_symbol_0_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_UFS_PHY_RX_SYMBOL_0_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_ufs_phy_rx_symbol_1_clk_src = { + .reg = 0x770ec, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_rx_symbol_1_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_UFS_PHY_RX_SYMBOL_1_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_ufs_phy_tx_symbol_0_clk_src = { + .reg = 0x77058, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_tx_symbol_0_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_UFS_PHY_TX_SYMBOL_0_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_usb3_prim_phy_pipe_clk_src = { + .reg = 0x39074, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_pipe_clk_src", + .parent_data = &(const struct clk_parent_data){ + .index = DT_USB3_PHY_WRAPPER_GCC_USB30_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static const struct freq_tbl ftbl_gcc_gp1_clk_src[] = { + F(50000000, P_GCC_GPLL0_OUT_EVEN, 6, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(200000000, P_GCC_GPLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_gp1_clk_src = { + .cmd_rcgr = 0x64004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gp1_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_gp2_clk_src = { + .cmd_rcgr = 0x65004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gp2_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_gp3_clk_src = { + .cmd_rcgr = 0x66004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_gp1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_gp3_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie_0_aux_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie_0_aux_clk_src = { + .cmd_rcgr = 0x6b0ac, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_aux_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_0_phy_aux_clk_src = { + .cmd_rcgr = 0x6b0c4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_phy_aux_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie_0_phy_rchng_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie_0_phy_rchng_clk_src = { + .cmd_rcgr = 0x6b08c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_phy_rchng_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_phy_rchng_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_1_aux_clk_src = { + .cmd_rcgr = 0x670a8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_aux_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_1_phy_aux_clk_src = { + .cmd_rcgr = 0x670c0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_phy_aux_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie_1_phy_rchng_clk_src = { + .cmd_rcgr = 0x67088, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_phy_rchng_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_phy_rchng_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pdm2_clk_src[] = { + F(60000000, P_GCC_GPLL0_OUT_MAIN, 10, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pdm2_clk_src = { + .cmd_rcgr = 0x33010, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pdm2_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_i2c_s0_clk_src = { + .cmd_rcgr = 0x17008, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s0_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_i2c_s1_clk_src = { + .cmd_rcgr = 0x17024, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s1_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_i2c_s2_clk_src = { + .cmd_rcgr = 0x17040, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_i2c_s3_clk_src = { + .cmd_rcgr = 0x1705c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s3_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_i2c_s4_clk_src = { + .cmd_rcgr = 0x17078, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s4_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap1_qspi_ref_clk_src[] = { + F(7372800, P_GCC_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GCC_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GCC_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 75), + F(48000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 25), + F(51200000, P_GCC_GPLL0_OUT_EVEN, 1, 64, 375), + F(64000000, P_GCC_GPLL0_OUT_EVEN, 1, 16, 75), + F(66666667, P_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(80000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(102400000, P_GCC_GPLL0_OUT_EVEN, 1, 128, 375), + F(112000000, P_GCC_GPLL0_OUT_EVEN, 1, 28, 75), + F(117964800, P_GCC_GPLL0_OUT_EVEN, 1, 6144, 15625), + F(120000000, P_GCC_GPLL0_OUT_EVEN, 2.5, 0, 0), + F(150000000, P_GCC_GPLL0_OUT_EVEN, 2, 0, 0), + F(200000000, P_GCC_GPLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap1_qspi_ref_clk_src_init = { + .name = "gcc_qupv3_wrap1_qspi_ref_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_qspi_ref_clk_src = { + .cmd_rcgr = 0x188c0, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_qspi_ref_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_qspi_ref_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap1_s0_clk_src[] = { + F(7372800, P_GCC_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GCC_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GCC_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 75), + F(48000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 25), + F(51200000, P_GCC_GPLL0_OUT_EVEN, 1, 64, 375), + F(60000000, P_GCC_GPLL0_OUT_EVEN, 5, 0, 0), + F(64000000, P_GCC_GPLL0_OUT_EVEN, 1, 16, 75), + F(66666667, P_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(80000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + { } +}; + +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 = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s0_clk_src = { + .cmd_rcgr = 0x18014, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s0_clk_src_init, +}; + +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 = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s1_clk_src = { + .cmd_rcgr = 0x18150, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s1_clk_src_init, +}; + +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 = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s3_clk_src = { + .cmd_rcgr = 0x182a0, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s3_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap1_s4_clk_src[] = { + F(7372800, P_GCC_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GCC_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GCC_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 75), + F(48000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 25), + F(51200000, P_GCC_GPLL0_OUT_EVEN, 1, 64, 375), + F(60000000, P_GCC_GPLL0_OUT_EVEN, 5, 0, 0), + F(64000000, P_GCC_GPLL0_OUT_EVEN, 1, 16, 75), + F(66666667, P_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(80000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(102400000, P_GCC_GPLL0_OUT_EVEN, 1, 128, 375), + F(112000000, P_GCC_GPLL0_OUT_EVEN, 1, 28, 75), + F(117964800, P_GCC_GPLL0_OUT_EVEN, 1, 6144, 15625), + F(120000000, P_GCC_GPLL0_OUT_MAIN, 5, 0, 0), + { } +}; + +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 = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s4_clk_src = { + .cmd_rcgr = 0x183dc, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s4_clk_src_init, +}; + +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 = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s5_clk_src = { + .cmd_rcgr = 0x18518, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s5_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s6_clk_src_init = { + .name = "gcc_qupv3_wrap1_s6_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s6_clk_src = { + .cmd_rcgr = 0x18654, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s6_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap1_s7_clk_src_init = { + .name = "gcc_qupv3_wrap1_s7_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap1_s7_clk_src = { + .cmd_rcgr = 0x18790, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap1_s7_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap2_s0_clk_src_init = { + .name = "gcc_qupv3_wrap2_s0_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s0_clk_src = { + .cmd_rcgr = 0x1e01c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s0_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap2_s1_clk_src_init = { + .name = "gcc_qupv3_wrap2_s1_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s1_clk_src = { + .cmd_rcgr = 0x1e160, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s1_clk_src_init, +}; + +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 = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s2_clk_src = { + .cmd_rcgr = 0x1e29c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s2_clk_src_init, +}; + +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 = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s3_clk_src = { + .cmd_rcgr = 0x1e3d8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s3_clk_src_init, +}; + +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 = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap2_s4_clk_src = { + .cmd_rcgr = 0x1e514, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap2_s4_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap3_qspi_ref_clk_src[] = { + F(7372800, P_GCC_GPLL0_OUT_EVEN, 1, 384, 15625), + F(14745600, P_GCC_GPLL0_OUT_EVEN, 1, 768, 15625), + F(19200000, P_BI_TCXO, 1, 0, 0), + F(29491200, P_GCC_GPLL0_OUT_EVEN, 1, 1536, 15625), + F(32000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 75), + F(48000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 25), + F(51200000, P_GCC_GPLL0_OUT_EVEN, 1, 64, 375), + F(64000000, P_GCC_GPLL0_OUT_EVEN, 1, 16, 75), + F(66666667, P_GCC_GPLL0_OUT_MAIN, 9, 0, 0), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(80000000, P_GCC_GPLL0_OUT_EVEN, 1, 4, 15), + F(96000000, P_GCC_GPLL0_OUT_EVEN, 1, 8, 25), + F(100000000, P_GCC_GPLL0_OUT_MAIN, 6, 0, 0), + F(102400000, P_GCC_GPLL0_OUT_EVEN, 1, 128, 375), + F(112000000, P_GCC_GPLL0_OUT_EVEN, 1, 28, 75), + F(117964800, P_GCC_GPLL0_OUT_EVEN, 1, 6144, 15625), + F(120000000, P_GCC_GPLL0_OUT_EVEN, 2.5, 0, 0), + F(150000000, P_GCC_GPLL0_OUT_EVEN, 2, 0, 0), + F(240000000, P_GCC_GPLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_init_data gcc_qupv3_wrap3_qspi_ref_clk_src_init = { + .name = "gcc_qupv3_wrap3_qspi_ref_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap3_qspi_ref_clk_src = { + .cmd_rcgr = 0xa8650, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap3_qspi_ref_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap3_qspi_ref_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap3_s0_clk_src_init = { + .name = "gcc_qupv3_wrap3_s0_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap3_s0_clk_src = { + .cmd_rcgr = 0xa8014, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap3_s0_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap3_s2_clk_src_init = { + .name = "gcc_qupv3_wrap3_s2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap3_s2_clk_src = { + .cmd_rcgr = 0xa8168, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap3_s2_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap3_s3_clk_src_init = { + .name = "gcc_qupv3_wrap3_s3_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap3_s3_clk_src = { + .cmd_rcgr = 0xa82a4, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap3_s3_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap3_s4_clk_src_init = { + .name = "gcc_qupv3_wrap3_s4_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap3_s4_clk_src = { + .cmd_rcgr = 0xa83e0, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap3_s4_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap3_s5_clk_src_init = { + .name = "gcc_qupv3_wrap3_s5_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap3_s5_clk_src = { + .cmd_rcgr = 0xa851c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap3_s5_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap4_s0_clk_src_init = { + .name = "gcc_qupv3_wrap4_s0_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap4_s0_clk_src = { + .cmd_rcgr = 0xa9014, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap4_s0_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap4_s1_clk_src_init = { + .name = "gcc_qupv3_wrap4_s1_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap4_s1_clk_src = { + .cmd_rcgr = 0xa9150, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap4_s1_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap4_s2_clk_src_init = { + .name = "gcc_qupv3_wrap4_s2_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap4_s2_clk_src = { + .cmd_rcgr = 0xa928c, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s4_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap4_s2_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap4_s3_clk_src_init = { + .name = "gcc_qupv3_wrap4_s3_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap4_s3_clk_src = { + .cmd_rcgr = 0xa93c8, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap4_s3_clk_src_init, +}; + +static struct clk_init_data gcc_qupv3_wrap4_s4_clk_src_init = { + .name = "gcc_qupv3_wrap4_s4_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, +}; + +static struct clk_rcg2 gcc_qupv3_wrap4_s4_clk_src = { + .cmd_rcgr = 0xa9504, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_qupv3_wrap1_s0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &gcc_qupv3_wrap4_s4_clk_src_init, +}; + +static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = { + F(400000, P_BI_TCXO, 12, 1, 4), + F(25000000, P_GCC_GPLL0_OUT_EVEN, 12, 0, 0), + F(37500000, P_GCC_GPLL0_OUT_EVEN, 8, 0, 0), + F(50000000, P_GCC_GPLL0_OUT_EVEN, 6, 0, 0), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + F(202000000, P_GCC_GPLL9_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc2_apps_clk_src = { + .cmd_rcgr = 0x1401c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_8, + .freq_tbl = ftbl_gcc_sdcc2_apps_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc2_apps_clk_src", + .parent_data = gcc_parent_data_8, + .num_parents = ARRAY_SIZE(gcc_parent_data_8), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_floor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc4_apps_clk_src[] = { + F(400000, P_BI_TCXO, 12, 1, 4), + F(25000000, P_GCC_GPLL0_OUT_EVEN, 12, 0, 0), + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc4_apps_clk_src = { + .cmd_rcgr = 0x1601c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_sdcc4_apps_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc4_apps_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_floor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_axi_clk_src[] = { + F(25000000, P_GCC_GPLL0_OUT_EVEN, 12, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + F(225000000, P_GCC_GPLL5_OUT_MAIN, 4, 0, 0), + F(450000000, P_GCC_GPLL5_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_axi_clk_src = { + .cmd_rcgr = 0x77034, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_ufs_phy_axi_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_axi_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_ice_core_clk_src[] = { + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + F(225000000, P_GCC_GPLL5_OUT_MAIN, 4, 0, 0), + F(450000000, P_GCC_GPLL5_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_ice_core_clk_src = { + .cmd_rcgr = 0x7708c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_ufs_phy_ice_core_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_ice_core_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_phy_aux_clk_src[] = { + F(9600000, P_BI_TCXO, 2, 0, 0), + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_phy_aux_clk_src = { + .cmd_rcgr = 0x770c0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_ufs_phy_phy_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_phy_aux_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_ufs_phy_unipro_5_core_clk_src[] = { + F(75000000, P_GCC_GPLL0_OUT_EVEN, 4, 0, 0), + F(100000000, P_GCC_GPLL0_OUT_EVEN, 3, 0, 0), + F(201500000, P_GCC_GPLL4_OUT_MAIN, 4, 0, 0), + F(403000000, P_GCC_GPLL4_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_ufs_phy_unipro_5_core_clk_src = { + .cmd_rcgr = 0x770a4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_ufs_phy_unipro_5_core_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_unipro_5_core_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb30_prim_master_clk_src[] = { + F(66666667, P_GCC_GPLL0_OUT_EVEN, 4.5, 0, 0), + F(133333333, P_GCC_GPLL0_OUT_MAIN, 4.5, 0, 0), + F(200000000, P_GCC_GPLL0_OUT_MAIN, 3, 0, 0), + F(240000000, P_GCC_GPLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_usb30_prim_master_clk_src = { + .cmd_rcgr = 0x39034, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_usb30_prim_master_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_master_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_usb30_prim_mock_utmi_clk_src = { + .cmd_rcgr = 0x3904c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_mock_utmi_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_rcg2 gcc_usb3_prim_phy_aux_clk_src = { + .cmd_rcgr = 0x39078, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_pcie_0_aux_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_aux_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_no_init_park_ops, + }, +}; + +static struct clk_regmap_div gcc_pcie_0_pipe_div_clk_src = { + .reg = 0x6b0a4, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_pipe_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_pcie_1_pipe_div_clk_src = { + .reg = 0x670a0, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_pipe_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_qupv3_wrap1_s2_clk_src = { + .reg = 0x1828c, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s2_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_qspi_ref_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_qupv3_wrap3_s1_clk_src = { + .reg = 0xa8154, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s1_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_qspi_ref_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_usb30_prim_mock_utmi_postdiv_clk_src = { + .reg = 0x39064, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_mock_utmi_postdiv_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_mock_utmi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch gcc_aggre_noc_pcie_axi_clk = { + .halt_reg = 0x10068, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x10068, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(24), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_aggre_noc_pcie_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_aggre_stardustnoc_usb3_prim_axi_clk = { + .halt_reg = 0x39094, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x39094, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x39094, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_aggre_stardustnoc_usb3_prim_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_aggre_ufs_phy_axi_clk = { + .halt_reg = 0x770f0, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x770f0, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x770f0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_aggre_ufs_phy_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_boot_rom_ahb_clk = { + .halt_reg = 0x38004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x38004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(18), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_boot_rom_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camera_hf_axi_clk = { + .halt_reg = 0x26014, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x26014, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x26014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camera_hf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_camera_sf_axi_clk = { + .halt_reg = 0x2601c, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x2601c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x2601c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_camera_sf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cfg_noc_pcie_anoc_ahb_clk = { + .halt_reg = 0x10050, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x10050, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(20), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cfg_noc_pcie_anoc_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cfg_noc_usb3_prim_axi_clk = { + .halt_reg = 0x39090, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x39090, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x39090, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cfg_noc_usb3_prim_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cnoc_pcie_sf_axi_clk = { + .halt_reg = 0x10058, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(6), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cnoc_pcie_sf_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_eva_axi0_clk = { + .halt_reg = 0x9f008, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x9f008, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9f008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_eva_axi0_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_eva_axi0c_clk = { + .halt_reg = 0x9f010, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x9f010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x9f010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_eva_axi0c_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp1_clk = { + .halt_reg = 0x64000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x64000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gp1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gp1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp2_clk = { + .halt_reg = 0x65000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x65000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gp2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gp2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gp3_clk = { + .halt_reg = 0x66000, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x66000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gp3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gp3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_gemnoc_gfx_clk = { + .halt_reg = 0x71010, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x71010, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x71010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_gemnoc_gfx_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_gpll0_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(15), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_gpll0_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gpll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_gpll0_div_clk_src = { + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x52000, + .enable_mask = BIT(16), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_gpll0_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_gpll0_out_even.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_gpu_smmu_vote_clk = { + .halt_reg = 0x7d000, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7d000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_gpu_smmu_vote_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mmu_tcu_vote_clk = { + .halt_reg = 0x7d02c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x7d02c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_mmu_tcu_vote_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_aux_clk = { + .halt_reg = 0x6b044, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b044, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_cfg_ahb_clk = { + .halt_reg = 0x6b040, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b040, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(3), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_mstr_axi_clk = { + .halt_reg = 0x6b030, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x6b030, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_mstr_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_phy_aux_clk = { + .halt_reg = 0x6b054, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b054, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(5), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_phy_rchng_clk = { + .halt_reg = 0x6b084, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b084, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_phy_rchng_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_phy_rchng_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_pipe_clk = { + .halt_reg = 0x6b074, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x6b074, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(7), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_pipe_div2_clk = { + .halt_reg = 0x6b064, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x6b064, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(6), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_pipe_div2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_0_pipe_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_slv_axi_clk = { + .halt_reg = 0x6b020, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b020, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_slv_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_0_slv_q2a_axi_clk = { + .halt_reg = 0x6b01c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6b01c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_0_slv_q2a_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_aux_clk = { + .halt_reg = 0x67040, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(10), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_cfg_ahb_clk = { + .halt_reg = 0x6703c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6703c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(9), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_cfg_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_mstr_axi_clk = { + .halt_reg = 0x6702c, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x6702c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(17), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_mstr_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_phy_aux_clk = { + .halt_reg = 0x67050, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(14), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_phy_rchng_clk = { + .halt_reg = 0x67080, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(26), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_phy_rchng_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_phy_rchng_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_pipe_clk = { + .halt_reg = 0x67070, + .halt_check = BRANCH_HALT_SKIP, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(17), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_pipe_div2_clk = { + .halt_reg = 0x67060, + .halt_check = BRANCH_HALT_SKIP, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(15), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_pipe_div2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_1_pipe_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_slv_axi_clk = { + .halt_reg = 0x6701c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x6701c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(16), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_slv_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie_1_slv_q2a_axi_clk = { + .halt_reg = 0x67018, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(15), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_1_slv_q2a_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm2_clk = { + .halt_reg = 0x3300c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3300c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pdm2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm_ahb_clk = { + .halt_reg = 0x33004, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x33004, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x33004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pdm_xo4_clk = { + .halt_reg = 0x33008, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x33008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pdm_xo4_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_i2c_core_clk = { + .halt_reg = 0x23004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_i2c_s0_clk = { + .halt_reg = 0x17004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(10), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_i2c_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_i2c_s1_clk = { + .halt_reg = 0x17020, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(11), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_i2c_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_i2c_s2_clk = { + .halt_reg = 0x1703c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(12), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_i2c_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_i2c_s3_clk = { + .halt_reg = 0x17058, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(13), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_i2c_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_i2c_s4_clk = { + .halt_reg = 0x17074, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(14), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_i2c_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_i2c_s_ahb_clk = { + .halt_reg = 0x23000, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23000, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(7), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_i2c_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_core_2x_clk = { + .halt_reg = 0x2315c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(18), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_core_clk = { + .halt_reg = 0x23148, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(19), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_qspi_ref_clk = { + .halt_reg = 0x188bc, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(29), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_qspi_ref_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_qspi_ref_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s0_clk = { + .halt_reg = 0x18004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(22), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s1_clk = { + .halt_reg = 0x18140, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(23), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s2_clk = { + .halt_reg = 0x1827c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(24), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s3_clk = { + .halt_reg = 0x18290, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(25), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s4_clk = { + .halt_reg = 0x183cc, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(26), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s5_clk = { + .halt_reg = 0x18508, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(27), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s5_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s6_clk = { + .halt_reg = 0x18644, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(28), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s6_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s6_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap1_s7_clk = { + .halt_reg = 0x18780, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(16), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap1_s7_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap1_s7_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_core_2x_clk = { + .halt_reg = 0x232b4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(3), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_core_clk = { + .halt_reg = 0x232a0, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s0_clk = { + .halt_reg = 0x1e004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s1_clk = { + .halt_reg = 0x1e148, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(5), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s2_clk = { + .halt_reg = 0x1e28c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(6), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s3_clk = { + .halt_reg = 0x1e3c8, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(7), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap2_s4_clk = { + .halt_reg = 0x1e504, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap2_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap2_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_core_2x_clk = { + .halt_reg = 0x2340c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(11), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_core_clk = { + .halt_reg = 0x233f8, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(10), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_qspi_ref_clk = { + .halt_reg = 0xa8648, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(25), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_qspi_ref_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_qspi_ref_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_s0_clk = { + .halt_reg = 0xa8004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(12), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_s1_clk = { + .halt_reg = 0xa8140, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(13), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_s2_clk = { + .halt_reg = 0xa8158, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(14), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_s3_clk = { + .halt_reg = 0xa8294, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(15), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_s4_clk = { + .halt_reg = 0xa83d0, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(16), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap3_s5_clk = { + .halt_reg = 0xa850c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(17), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap3_s5_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap3_s5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap4_core_2x_clk = { + .halt_reg = 0x23564, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(25), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap4_core_2x_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap4_core_clk = { + .halt_reg = 0x23550, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(24), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap4_core_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap4_s0_clk = { + .halt_reg = 0xa9004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(26), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap4_s0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap4_s0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap4_s1_clk = { + .halt_reg = 0xa9140, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(27), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap4_s1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap4_s1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap4_s2_clk = { + .halt_reg = 0xa927c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(28), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap4_s2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap4_s2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap4_s3_clk = { + .halt_reg = 0xa93b8, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(29), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap4_s3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap4_s3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap4_s4_clk = { + .halt_reg = 0xa94f4, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(30), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap4_s4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap4_s4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_1_m_axi_clk = { + .halt_reg = 0x23140, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23140, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(20), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_1_m_axi_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_1_s_ahb_clk = { + .halt_reg = 0x23144, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23144, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52008, + .enable_mask = BIT(21), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_1_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_2_m_ahb_clk = { + .halt_reg = 0x23298, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23298, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_2_m_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_2_s_ahb_clk = { + .halt_reg = 0x2329c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2329c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52010, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_2_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_3_m_ahb_clk = { + .halt_reg = 0x233f0, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x233f0, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(8), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_3_m_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_3_s_ahb_clk = { + .halt_reg = 0x233f4, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x233f4, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(9), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_3_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_4_m_ahb_clk = { + .halt_reg = 0x23548, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x23548, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(22), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_4_m_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_4_s_ahb_clk = { + .halt_reg = 0x2354c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x2354c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x52018, + .enable_mask = BIT(23), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_4_s_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc2_ahb_clk = { + .halt_reg = 0x14014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x14014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc2_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc2_apps_clk = { + .halt_reg = 0x14004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x14004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc2_apps_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc2_apps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc4_ahb_clk = { + .halt_reg = 0x16014, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x16014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc4_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc4_apps_clk = { + .halt_reg = 0x16004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x16004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc4_apps_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc4_apps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_ahb_clk = { + .halt_reg = 0x77028, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x77028, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_ahb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_axi_clk = { + .halt_reg = 0x77018, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x77018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x77018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_axi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_ice_core_clk = { + .halt_reg = 0x7707c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x7707c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x7707c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_ice_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_ice_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_phy_aux_clk = { + .halt_reg = 0x770bc, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x770bc, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x770bc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_rx_symbol_0_clk = { + .halt_reg = 0x77030, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x77030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_rx_symbol_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_rx_symbol_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_rx_symbol_1_clk = { + .halt_reg = 0x770d8, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x770d8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_rx_symbol_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_rx_symbol_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_tx_symbol_0_clk = { + .halt_reg = 0x7702c, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x7702c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_tx_symbol_0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_tx_symbol_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_ufs_phy_unipro_5_core_clk = { + .halt_reg = 0x7706c, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x7706c, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x7706c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_ufs_phy_unipro_5_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_ufs_phy_unipro_5_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_master_clk = { + .halt_reg = 0x39018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_master_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_mock_utmi_clk = { + .halt_reg = 0x3902c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3902c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_mock_utmi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb30_prim_mock_utmi_postdiv_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb30_prim_sleep_clk = { + .halt_reg = 0x39028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb30_prim_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_phy_aux_clk = { + .halt_reg = 0x39068, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x39068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb3_prim_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_phy_com_aux_clk = { + .halt_reg = 0x3906c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3906c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_com_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb3_prim_phy_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb3_prim_phy_pipe_clk = { + .halt_reg = 0x39070, + .halt_check = BRANCH_HALT_DELAY, + .hwcg_reg = 0x39070, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x39070, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb3_prim_phy_pipe_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb3_prim_phy_pipe_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_video_axi0_clk = { + .halt_reg = 0x32018, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x32018, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x32018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_video_axi0_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_video_axi0c_clk = { + .halt_reg = 0x32020, + .halt_check = BRANCH_HALT_SKIP, + .hwcg_reg = 0x32020, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x32020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_video_axi0c_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc gcc_pcie_0_gdsc = { + .gdscr = 0x6b004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .collapse_ctrl = 0x52154, + .collapse_mask = BIT(0), + .pd = { + .name = "gcc_pcie_0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_0_phy_gdsc = { + .gdscr = 0x6c000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .collapse_ctrl = 0x52154, + .collapse_mask = BIT(1), + .pd = { + .name = "gcc_pcie_0_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_1_gdsc = { + .gdscr = 0x67004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .collapse_ctrl = 0x5214c, + .collapse_mask = BIT(2), + .pd = { + .name = "gcc_pcie_1_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_pcie_1_phy_gdsc = { + .gdscr = 0x68000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .collapse_ctrl = 0x5214c, + .collapse_mask = BIT(3), + .pd = { + .name = "gcc_pcie_1_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE | VOTABLE, +}; + +static struct gdsc gcc_ufs_mem_phy_gdsc = { + .gdscr = 0x9e000, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "gcc_ufs_mem_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_ufs_phy_gdsc = { + .gdscr = 0x77004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "gcc_ufs_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_usb30_prim_gdsc = { + .gdscr = 0x39004, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "gcc_usb30_prim_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc gcc_usb3_phy_gdsc = { + .gdscr = 0x50018, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x2, + .pd = { + .name = "gcc_usb3_phy_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *gcc_hawi_clocks[] = { + [GCC_AGGRE_NOC_PCIE_AXI_CLK] = &gcc_aggre_noc_pcie_axi_clk.clkr, + [GCC_AGGRE_STARDUSTNOC_USB3_PRIM_AXI_CLK] = &gcc_aggre_stardustnoc_usb3_prim_axi_clk.clkr, + [GCC_AGGRE_UFS_PHY_AXI_CLK] = &gcc_aggre_ufs_phy_axi_clk.clkr, + [GCC_BOOT_ROM_AHB_CLK] = &gcc_boot_rom_ahb_clk.clkr, + [GCC_CAMERA_HF_AXI_CLK] = &gcc_camera_hf_axi_clk.clkr, + [GCC_CAMERA_SF_AXI_CLK] = &gcc_camera_sf_axi_clk.clkr, + [GCC_CFG_NOC_PCIE_ANOC_AHB_CLK] = &gcc_cfg_noc_pcie_anoc_ahb_clk.clkr, + [GCC_CFG_NOC_USB3_PRIM_AXI_CLK] = &gcc_cfg_noc_usb3_prim_axi_clk.clkr, + [GCC_CNOC_PCIE_SF_AXI_CLK] = &gcc_cnoc_pcie_sf_axi_clk.clkr, + [GCC_EVA_AXI0_CLK] = &gcc_eva_axi0_clk.clkr, + [GCC_EVA_AXI0C_CLK] = &gcc_eva_axi0c_clk.clkr, + [GCC_GP1_CLK] = &gcc_gp1_clk.clkr, + [GCC_GP1_CLK_SRC] = &gcc_gp1_clk_src.clkr, + [GCC_GP2_CLK] = &gcc_gp2_clk.clkr, + [GCC_GP2_CLK_SRC] = &gcc_gp2_clk_src.clkr, + [GCC_GP3_CLK] = &gcc_gp3_clk.clkr, + [GCC_GP3_CLK_SRC] = &gcc_gp3_clk_src.clkr, + [GCC_GPLL0] = &gcc_gpll0.clkr, + [GCC_GPLL0_OUT_EVEN] = &gcc_gpll0_out_even.clkr, + [GCC_GPLL4] = &gcc_gpll4.clkr, + [GCC_GPLL5] = &gcc_gpll5.clkr, + [GCC_GPLL7] = &gcc_gpll7.clkr, + [GCC_GPLL9] = &gcc_gpll9.clkr, + [GCC_GPU_GEMNOC_GFX_CLK] = &gcc_gpu_gemnoc_gfx_clk.clkr, + [GCC_GPU_GPLL0_CLK_SRC] = &gcc_gpu_gpll0_clk_src.clkr, + [GCC_GPU_GPLL0_DIV_CLK_SRC] = &gcc_gpu_gpll0_div_clk_src.clkr, + [GCC_GPU_SMMU_VOTE_CLK] = &gcc_gpu_smmu_vote_clk.clkr, + [GCC_MMU_TCU_VOTE_CLK] = &gcc_mmu_tcu_vote_clk.clkr, + [GCC_PCIE_0_AUX_CLK] = &gcc_pcie_0_aux_clk.clkr, + [GCC_PCIE_0_AUX_CLK_SRC] = &gcc_pcie_0_aux_clk_src.clkr, + [GCC_PCIE_0_CFG_AHB_CLK] = &gcc_pcie_0_cfg_ahb_clk.clkr, + [GCC_PCIE_0_MSTR_AXI_CLK] = &gcc_pcie_0_mstr_axi_clk.clkr, + [GCC_PCIE_0_PHY_AUX_CLK] = &gcc_pcie_0_phy_aux_clk.clkr, + [GCC_PCIE_0_PHY_AUX_CLK_SRC] = &gcc_pcie_0_phy_aux_clk_src.clkr, + [GCC_PCIE_0_PHY_RCHNG_CLK] = &gcc_pcie_0_phy_rchng_clk.clkr, + [GCC_PCIE_0_PHY_RCHNG_CLK_SRC] = &gcc_pcie_0_phy_rchng_clk_src.clkr, + [GCC_PCIE_0_PIPE_CLK] = &gcc_pcie_0_pipe_clk.clkr, + [GCC_PCIE_0_PIPE_CLK_SRC] = &gcc_pcie_0_pipe_clk_src.clkr, + [GCC_PCIE_0_PIPE_DIV2_CLK] = &gcc_pcie_0_pipe_div2_clk.clkr, + [GCC_PCIE_0_PIPE_DIV_CLK_SRC] = &gcc_pcie_0_pipe_div_clk_src.clkr, + [GCC_PCIE_0_SLV_AXI_CLK] = &gcc_pcie_0_slv_axi_clk.clkr, + [GCC_PCIE_0_SLV_Q2A_AXI_CLK] = &gcc_pcie_0_slv_q2a_axi_clk.clkr, + [GCC_PCIE_1_AUX_CLK] = &gcc_pcie_1_aux_clk.clkr, + [GCC_PCIE_1_AUX_CLK_SRC] = &gcc_pcie_1_aux_clk_src.clkr, + [GCC_PCIE_1_CFG_AHB_CLK] = &gcc_pcie_1_cfg_ahb_clk.clkr, + [GCC_PCIE_1_MSTR_AXI_CLK] = &gcc_pcie_1_mstr_axi_clk.clkr, + [GCC_PCIE_1_PHY_AUX_CLK] = &gcc_pcie_1_phy_aux_clk.clkr, + [GCC_PCIE_1_PHY_AUX_CLK_SRC] = &gcc_pcie_1_phy_aux_clk_src.clkr, + [GCC_PCIE_1_PHY_RCHNG_CLK] = &gcc_pcie_1_phy_rchng_clk.clkr, + [GCC_PCIE_1_PHY_RCHNG_CLK_SRC] = &gcc_pcie_1_phy_rchng_clk_src.clkr, + [GCC_PCIE_1_PIPE_CLK] = &gcc_pcie_1_pipe_clk.clkr, + [GCC_PCIE_1_PIPE_CLK_SRC] = &gcc_pcie_1_pipe_clk_src.clkr, + [GCC_PCIE_1_PIPE_DIV2_CLK] = &gcc_pcie_1_pipe_div2_clk.clkr, + [GCC_PCIE_1_PIPE_DIV_CLK_SRC] = &gcc_pcie_1_pipe_div_clk_src.clkr, + [GCC_PCIE_1_SLV_AXI_CLK] = &gcc_pcie_1_slv_axi_clk.clkr, + [GCC_PCIE_1_SLV_Q2A_AXI_CLK] = &gcc_pcie_1_slv_q2a_axi_clk.clkr, + [GCC_PDM2_CLK] = &gcc_pdm2_clk.clkr, + [GCC_PDM2_CLK_SRC] = &gcc_pdm2_clk_src.clkr, + [GCC_PDM_AHB_CLK] = &gcc_pdm_ahb_clk.clkr, + [GCC_PDM_XO4_CLK] = &gcc_pdm_xo4_clk.clkr, + [GCC_QUPV3_I2C_CORE_CLK] = &gcc_qupv3_i2c_core_clk.clkr, + [GCC_QUPV3_I2C_S0_CLK] = &gcc_qupv3_i2c_s0_clk.clkr, + [GCC_QUPV3_I2C_S0_CLK_SRC] = &gcc_qupv3_i2c_s0_clk_src.clkr, + [GCC_QUPV3_I2C_S1_CLK] = &gcc_qupv3_i2c_s1_clk.clkr, + [GCC_QUPV3_I2C_S1_CLK_SRC] = &gcc_qupv3_i2c_s1_clk_src.clkr, + [GCC_QUPV3_I2C_S2_CLK] = &gcc_qupv3_i2c_s2_clk.clkr, + [GCC_QUPV3_I2C_S2_CLK_SRC] = &gcc_qupv3_i2c_s2_clk_src.clkr, + [GCC_QUPV3_I2C_S3_CLK] = &gcc_qupv3_i2c_s3_clk.clkr, + [GCC_QUPV3_I2C_S3_CLK_SRC] = &gcc_qupv3_i2c_s3_clk_src.clkr, + [GCC_QUPV3_I2C_S4_CLK] = &gcc_qupv3_i2c_s4_clk.clkr, + [GCC_QUPV3_I2C_S4_CLK_SRC] = &gcc_qupv3_i2c_s4_clk_src.clkr, + [GCC_QUPV3_I2C_S_AHB_CLK] = &gcc_qupv3_i2c_s_ahb_clk.clkr, + [GCC_QUPV3_WRAP1_CORE_2X_CLK] = &gcc_qupv3_wrap1_core_2x_clk.clkr, + [GCC_QUPV3_WRAP1_CORE_CLK] = &gcc_qupv3_wrap1_core_clk.clkr, + [GCC_QUPV3_WRAP1_QSPI_REF_CLK] = &gcc_qupv3_wrap1_qspi_ref_clk.clkr, + [GCC_QUPV3_WRAP1_QSPI_REF_CLK_SRC] = &gcc_qupv3_wrap1_qspi_ref_clk_src.clkr, + [GCC_QUPV3_WRAP1_S0_CLK] = &gcc_qupv3_wrap1_s0_clk.clkr, + [GCC_QUPV3_WRAP1_S0_CLK_SRC] = &gcc_qupv3_wrap1_s0_clk_src.clkr, + [GCC_QUPV3_WRAP1_S1_CLK] = &gcc_qupv3_wrap1_s1_clk.clkr, + [GCC_QUPV3_WRAP1_S1_CLK_SRC] = &gcc_qupv3_wrap1_s1_clk_src.clkr, + [GCC_QUPV3_WRAP1_S2_CLK] = &gcc_qupv3_wrap1_s2_clk.clkr, + [GCC_QUPV3_WRAP1_S2_CLK_SRC] = &gcc_qupv3_wrap1_s2_clk_src.clkr, + [GCC_QUPV3_WRAP1_S3_CLK] = &gcc_qupv3_wrap1_s3_clk.clkr, + [GCC_QUPV3_WRAP1_S3_CLK_SRC] = &gcc_qupv3_wrap1_s3_clk_src.clkr, + [GCC_QUPV3_WRAP1_S4_CLK] = &gcc_qupv3_wrap1_s4_clk.clkr, + [GCC_QUPV3_WRAP1_S4_CLK_SRC] = &gcc_qupv3_wrap1_s4_clk_src.clkr, + [GCC_QUPV3_WRAP1_S5_CLK] = &gcc_qupv3_wrap1_s5_clk.clkr, + [GCC_QUPV3_WRAP1_S5_CLK_SRC] = &gcc_qupv3_wrap1_s5_clk_src.clkr, + [GCC_QUPV3_WRAP1_S6_CLK] = &gcc_qupv3_wrap1_s6_clk.clkr, + [GCC_QUPV3_WRAP1_S6_CLK_SRC] = &gcc_qupv3_wrap1_s6_clk_src.clkr, + [GCC_QUPV3_WRAP1_S7_CLK] = &gcc_qupv3_wrap1_s7_clk.clkr, + [GCC_QUPV3_WRAP1_S7_CLK_SRC] = &gcc_qupv3_wrap1_s7_clk_src.clkr, + [GCC_QUPV3_WRAP2_CORE_2X_CLK] = &gcc_qupv3_wrap2_core_2x_clk.clkr, + [GCC_QUPV3_WRAP2_CORE_CLK] = &gcc_qupv3_wrap2_core_clk.clkr, + [GCC_QUPV3_WRAP2_S0_CLK] = &gcc_qupv3_wrap2_s0_clk.clkr, + [GCC_QUPV3_WRAP2_S0_CLK_SRC] = &gcc_qupv3_wrap2_s0_clk_src.clkr, + [GCC_QUPV3_WRAP2_S1_CLK] = &gcc_qupv3_wrap2_s1_clk.clkr, + [GCC_QUPV3_WRAP2_S1_CLK_SRC] = &gcc_qupv3_wrap2_s1_clk_src.clkr, + [GCC_QUPV3_WRAP2_S2_CLK] = &gcc_qupv3_wrap2_s2_clk.clkr, + [GCC_QUPV3_WRAP2_S2_CLK_SRC] = &gcc_qupv3_wrap2_s2_clk_src.clkr, + [GCC_QUPV3_WRAP2_S3_CLK] = &gcc_qupv3_wrap2_s3_clk.clkr, + [GCC_QUPV3_WRAP2_S3_CLK_SRC] = &gcc_qupv3_wrap2_s3_clk_src.clkr, + [GCC_QUPV3_WRAP2_S4_CLK] = &gcc_qupv3_wrap2_s4_clk.clkr, + [GCC_QUPV3_WRAP2_S4_CLK_SRC] = &gcc_qupv3_wrap2_s4_clk_src.clkr, + [GCC_QUPV3_WRAP3_CORE_2X_CLK] = &gcc_qupv3_wrap3_core_2x_clk.clkr, + [GCC_QUPV3_WRAP3_CORE_CLK] = &gcc_qupv3_wrap3_core_clk.clkr, + [GCC_QUPV3_WRAP3_QSPI_REF_CLK] = &gcc_qupv3_wrap3_qspi_ref_clk.clkr, + [GCC_QUPV3_WRAP3_QSPI_REF_CLK_SRC] = &gcc_qupv3_wrap3_qspi_ref_clk_src.clkr, + [GCC_QUPV3_WRAP3_S0_CLK] = &gcc_qupv3_wrap3_s0_clk.clkr, + [GCC_QUPV3_WRAP3_S0_CLK_SRC] = &gcc_qupv3_wrap3_s0_clk_src.clkr, + [GCC_QUPV3_WRAP3_S1_CLK] = &gcc_qupv3_wrap3_s1_clk.clkr, + [GCC_QUPV3_WRAP3_S1_CLK_SRC] = &gcc_qupv3_wrap3_s1_clk_src.clkr, + [GCC_QUPV3_WRAP3_S2_CLK] = &gcc_qupv3_wrap3_s2_clk.clkr, + [GCC_QUPV3_WRAP3_S2_CLK_SRC] = &gcc_qupv3_wrap3_s2_clk_src.clkr, + [GCC_QUPV3_WRAP3_S3_CLK] = &gcc_qupv3_wrap3_s3_clk.clkr, + [GCC_QUPV3_WRAP3_S3_CLK_SRC] = &gcc_qupv3_wrap3_s3_clk_src.clkr, + [GCC_QUPV3_WRAP3_S4_CLK] = &gcc_qupv3_wrap3_s4_clk.clkr, + [GCC_QUPV3_WRAP3_S4_CLK_SRC] = &gcc_qupv3_wrap3_s4_clk_src.clkr, + [GCC_QUPV3_WRAP3_S5_CLK] = &gcc_qupv3_wrap3_s5_clk.clkr, + [GCC_QUPV3_WRAP3_S5_CLK_SRC] = &gcc_qupv3_wrap3_s5_clk_src.clkr, + [GCC_QUPV3_WRAP4_CORE_2X_CLK] = &gcc_qupv3_wrap4_core_2x_clk.clkr, + [GCC_QUPV3_WRAP4_CORE_CLK] = &gcc_qupv3_wrap4_core_clk.clkr, + [GCC_QUPV3_WRAP4_S0_CLK] = &gcc_qupv3_wrap4_s0_clk.clkr, + [GCC_QUPV3_WRAP4_S0_CLK_SRC] = &gcc_qupv3_wrap4_s0_clk_src.clkr, + [GCC_QUPV3_WRAP4_S1_CLK] = &gcc_qupv3_wrap4_s1_clk.clkr, + [GCC_QUPV3_WRAP4_S1_CLK_SRC] = &gcc_qupv3_wrap4_s1_clk_src.clkr, + [GCC_QUPV3_WRAP4_S2_CLK] = &gcc_qupv3_wrap4_s2_clk.clkr, + [GCC_QUPV3_WRAP4_S2_CLK_SRC] = &gcc_qupv3_wrap4_s2_clk_src.clkr, + [GCC_QUPV3_WRAP4_S3_CLK] = &gcc_qupv3_wrap4_s3_clk.clkr, + [GCC_QUPV3_WRAP4_S3_CLK_SRC] = &gcc_qupv3_wrap4_s3_clk_src.clkr, + [GCC_QUPV3_WRAP4_S4_CLK] = &gcc_qupv3_wrap4_s4_clk.clkr, + [GCC_QUPV3_WRAP4_S4_CLK_SRC] = &gcc_qupv3_wrap4_s4_clk_src.clkr, + [GCC_QUPV3_WRAP_1_M_AXI_CLK] = &gcc_qupv3_wrap_1_m_axi_clk.clkr, + [GCC_QUPV3_WRAP_1_S_AHB_CLK] = &gcc_qupv3_wrap_1_s_ahb_clk.clkr, + [GCC_QUPV3_WRAP_2_M_AHB_CLK] = &gcc_qupv3_wrap_2_m_ahb_clk.clkr, + [GCC_QUPV3_WRAP_2_S_AHB_CLK] = &gcc_qupv3_wrap_2_s_ahb_clk.clkr, + [GCC_QUPV3_WRAP_3_M_AHB_CLK] = &gcc_qupv3_wrap_3_m_ahb_clk.clkr, + [GCC_QUPV3_WRAP_3_S_AHB_CLK] = &gcc_qupv3_wrap_3_s_ahb_clk.clkr, + [GCC_QUPV3_WRAP_4_M_AHB_CLK] = &gcc_qupv3_wrap_4_m_ahb_clk.clkr, + [GCC_QUPV3_WRAP_4_S_AHB_CLK] = &gcc_qupv3_wrap_4_s_ahb_clk.clkr, + [GCC_SDCC2_AHB_CLK] = &gcc_sdcc2_ahb_clk.clkr, + [GCC_SDCC2_APPS_CLK] = &gcc_sdcc2_apps_clk.clkr, + [GCC_SDCC2_APPS_CLK_SRC] = &gcc_sdcc2_apps_clk_src.clkr, + [GCC_SDCC4_AHB_CLK] = &gcc_sdcc4_ahb_clk.clkr, + [GCC_SDCC4_APPS_CLK] = &gcc_sdcc4_apps_clk.clkr, + [GCC_SDCC4_APPS_CLK_SRC] = &gcc_sdcc4_apps_clk_src.clkr, + [GCC_UFS_PHY_AHB_CLK] = &gcc_ufs_phy_ahb_clk.clkr, + [GCC_UFS_PHY_AXI_CLK] = &gcc_ufs_phy_axi_clk.clkr, + [GCC_UFS_PHY_AXI_CLK_SRC] = &gcc_ufs_phy_axi_clk_src.clkr, + [GCC_UFS_PHY_ICE_CORE_CLK] = &gcc_ufs_phy_ice_core_clk.clkr, + [GCC_UFS_PHY_ICE_CORE_CLK_SRC] = &gcc_ufs_phy_ice_core_clk_src.clkr, + [GCC_UFS_PHY_PHY_AUX_CLK] = &gcc_ufs_phy_phy_aux_clk.clkr, + [GCC_UFS_PHY_PHY_AUX_CLK_SRC] = &gcc_ufs_phy_phy_aux_clk_src.clkr, + [GCC_UFS_PHY_RX_SYMBOL_0_CLK] = &gcc_ufs_phy_rx_symbol_0_clk.clkr, + [GCC_UFS_PHY_RX_SYMBOL_0_CLK_SRC] = &gcc_ufs_phy_rx_symbol_0_clk_src.clkr, + [GCC_UFS_PHY_RX_SYMBOL_1_CLK] = &gcc_ufs_phy_rx_symbol_1_clk.clkr, + [GCC_UFS_PHY_RX_SYMBOL_1_CLK_SRC] = &gcc_ufs_phy_rx_symbol_1_clk_src.clkr, + [GCC_UFS_PHY_TX_SYMBOL_0_CLK] = &gcc_ufs_phy_tx_symbol_0_clk.clkr, + [GCC_UFS_PHY_TX_SYMBOL_0_CLK_SRC] = &gcc_ufs_phy_tx_symbol_0_clk_src.clkr, + [GCC_UFS_PHY_UNIPRO_5_CORE_CLK] = &gcc_ufs_phy_unipro_5_core_clk.clkr, + [GCC_UFS_PHY_UNIPRO_5_CORE_CLK_SRC] = &gcc_ufs_phy_unipro_5_core_clk_src.clkr, + [GCC_USB30_PRIM_MASTER_CLK] = &gcc_usb30_prim_master_clk.clkr, + [GCC_USB30_PRIM_MASTER_CLK_SRC] = &gcc_usb30_prim_master_clk_src.clkr, + [GCC_USB30_PRIM_MOCK_UTMI_CLK] = &gcc_usb30_prim_mock_utmi_clk.clkr, + [GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC] = &gcc_usb30_prim_mock_utmi_clk_src.clkr, + [GCC_USB30_PRIM_MOCK_UTMI_POSTDIV_CLK_SRC] = &gcc_usb30_prim_mock_utmi_postdiv_clk_src.clkr, + [GCC_USB30_PRIM_SLEEP_CLK] = &gcc_usb30_prim_sleep_clk.clkr, + [GCC_USB3_PRIM_PHY_AUX_CLK] = &gcc_usb3_prim_phy_aux_clk.clkr, + [GCC_USB3_PRIM_PHY_AUX_CLK_SRC] = &gcc_usb3_prim_phy_aux_clk_src.clkr, + [GCC_USB3_PRIM_PHY_COM_AUX_CLK] = &gcc_usb3_prim_phy_com_aux_clk.clkr, + [GCC_USB3_PRIM_PHY_PIPE_CLK] = &gcc_usb3_prim_phy_pipe_clk.clkr, + [GCC_USB3_PRIM_PHY_PIPE_CLK_SRC] = &gcc_usb3_prim_phy_pipe_clk_src.clkr, + [GCC_VIDEO_AXI0_CLK] = &gcc_video_axi0_clk.clkr, + [GCC_VIDEO_AXI0C_CLK] = &gcc_video_axi0c_clk.clkr, +}; + +static struct gdsc *gcc_hawi_gdscs[] = { + [GCC_PCIE_0_GDSC] = &gcc_pcie_0_gdsc, + [GCC_PCIE_0_PHY_GDSC] = &gcc_pcie_0_phy_gdsc, + [GCC_PCIE_1_GDSC] = &gcc_pcie_1_gdsc, + [GCC_PCIE_1_PHY_GDSC] = &gcc_pcie_1_phy_gdsc, + [GCC_UFS_MEM_PHY_GDSC] = &gcc_ufs_mem_phy_gdsc, + [GCC_UFS_PHY_GDSC] = &gcc_ufs_phy_gdsc, + [GCC_USB30_PRIM_GDSC] = &gcc_usb30_prim_gdsc, + [GCC_USB3_PHY_GDSC] = &gcc_usb3_phy_gdsc, +}; + +static const struct qcom_reset_map gcc_hawi_resets[] = { + [GCC_CAMERA_BCR] = { 0x26000 }, + [GCC_EVA_AXI0_CLK_ARES] = { 0x9f008, 2 }, + [GCC_EVA_AXI0C_CLK_ARES] = { 0x9f010, 2 }, + [GCC_EVA_BCR] = { 0x9f000 }, + [GCC_GPU_BCR] = { 0x71000 }, + [GCC_PCIE_0_BCR] = { 0x6b000 }, + [GCC_PCIE_0_LINK_DOWN_BCR] = { 0x6c014 }, + [GCC_PCIE_0_NOCSR_COM_PHY_BCR] = { 0x6c020 }, + [GCC_PCIE_0_PHY_BCR] = { 0x6c01c }, + [GCC_PCIE_0_PHY_NOCSR_COM_PHY_BCR] = { 0x6c028 }, + [GCC_PCIE_1_BCR] = { 0x67000 }, + [GCC_PCIE_1_LINK_DOWN_BCR] = { 0x8e014 }, + [GCC_PCIE_1_NOCSR_COM_PHY_BCR] = { 0x8e020 }, + [GCC_PCIE_1_PHY_BCR] = { 0x8e01c }, + [GCC_PCIE_1_PHY_NOCSR_COM_PHY_BCR] = { 0x8e024 }, + [GCC_PCIE_PHY_BCR] = { 0x6f000 }, + [GCC_PCIE_PHY_CFG_AHB_BCR] = { 0x6f00c }, + [GCC_PCIE_PHY_COM_BCR] = { 0x6f010 }, + [GCC_PCIE_RSCC_BCR] = { 0x11000 }, + [GCC_PDM_BCR] = { 0x33000 }, + [GCC_QUPV3_WRAPPER_1_BCR] = { 0x18000 }, + [GCC_QUPV3_WRAPPER_2_BCR] = { 0x1e000 }, + [GCC_QUPV3_WRAPPER_3_BCR] = { 0xa8000 }, + [GCC_QUPV3_WRAPPER_4_BCR] = { 0xa9000 }, + [GCC_QUPV3_WRAPPER_I2C_BCR] = { 0x17000 }, + [GCC_QUSB2PHY_PRIM_BCR] = { 0x12000 }, + [GCC_QUSB2PHY_SEC_BCR] = { 0x12004 }, + [GCC_SDCC2_BCR] = { 0x14000 }, + [GCC_SDCC4_BCR] = { 0x16000 }, + [GCC_TCSR_PCIE_BCR] = { 0x6f018 }, + [GCC_UFS_PHY_BCR] = { 0x77000 }, + [GCC_USB30_PRIM_BCR] = { 0x39000 }, + [GCC_USB3_DP_PHY_PRIM_BCR] = { 0x50008 }, + [GCC_USB3_DP_PHY_SEC_BCR] = { 0x50014 }, + [GCC_USB3_PHY_PRIM_BCR] = { 0x50000 }, + [GCC_USB3_PHY_SEC_BCR] = { 0x5000c }, + [GCC_USB3PHY_PHY_PRIM_BCR] = { 0x50004 }, + [GCC_USB3PHY_PHY_SEC_BCR] = { 0x50010 }, + [GCC_VIDEO_AXI0_CLK_ARES] = { 0x32018, 2 }, + [GCC_VIDEO_AXI0C_CLK_ARES] = { 0x32020, 2 }, + [GCC_VIDEO_BCR] = { 0x32000 }, + [GCC_VIDEO_XO_CLK_ARES] = { 0x32028, 2 }, +}; + +static const u32 gcc_hawi_critical_cbcrs[] = { + 0xa0004, /* GCC_CAM_BIST_MCLK_AHB_CLK */ + 0x26004, /* GCC_CAMERA_AHB_CLK */ + 0x26028, /* GCC_CAMERA_RSC_CORE_CLK */ + 0x26024, /* GCC_CAMERA_XO_CLK */ + 0x9f004, /* GCC_EVA_AHB_CLK */ + 0x9f018, /* GCC_EVA_XO_CLK */ + 0x71004, /* GCC_GPU_CFG_AHB_CLK */ + 0x7101c, /* GCC_GPU_RSC_CORE_CLK */ + 0x67084, /* GCC_PCIE_1_RSC_CORE_CLK */ + 0x43014, /* GCC_PCIE_LINK_XO_CLK */ + 0x6b088, /* GCC_PCIE_RSC_CORE_CLK */ + 0x52010, /* GCC_PCIE_RSCC_CFG_AHB_CLK */ + 0x52010, /* GCC_PCIE_RSCC_XO_CLK */ + 0x32004, /* GCC_VIDEO_AHB_CLK */ + 0x32028, /* GCC_VIDEO_XO_CLK */ +}; + +static const struct clk_rcg_dfs_data gcc_hawi_dfs_clocks[] = { + DEFINE_RCG_DFS(gcc_qupv3_wrap1_qspi_ref_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s5_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s6_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap1_s7_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s2_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap2_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap3_qspi_ref_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap3_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap3_s2_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap3_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap3_s4_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap3_s5_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap4_s0_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap4_s1_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap4_s2_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap4_s3_clk_src), + DEFINE_RCG_DFS(gcc_qupv3_wrap4_s4_clk_src), +}; + +static const struct regmap_config gcc_hawi_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1f41f4, + .fast_io = true, +}; + +static void clk_hawi_regs_configure(struct device *dev, struct regmap *regmap) +{ + /* FORCE_MEM_CORE_ON for ufs phy ice core clocks */ + qcom_branch_set_force_mem_core(regmap, gcc_ufs_phy_ice_core_clk, true); +} + +static const struct qcom_cc_driver_data gcc_hawi_driver_data = { + .clk_cbcrs = gcc_hawi_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(gcc_hawi_critical_cbcrs), + .dfs_rcgs = gcc_hawi_dfs_clocks, + .num_dfs_rcgs = ARRAY_SIZE(gcc_hawi_dfs_clocks), + .clk_regs_configure = clk_hawi_regs_configure, +}; + +static const struct qcom_cc_desc gcc_hawi_desc = { + .config = &gcc_hawi_regmap_config, + .clks = gcc_hawi_clocks, + .num_clks = ARRAY_SIZE(gcc_hawi_clocks), + .resets = gcc_hawi_resets, + .num_resets = ARRAY_SIZE(gcc_hawi_resets), + .gdscs = gcc_hawi_gdscs, + .num_gdscs = ARRAY_SIZE(gcc_hawi_gdscs), + .use_rpm = true, + .driver_data = &gcc_hawi_driver_data, +}; + +static const struct of_device_id gcc_hawi_match_table[] = { + { .compatible = "qcom,hawi-gcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gcc_hawi_match_table); + +static int gcc_hawi_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &gcc_hawi_desc); +} + +static struct platform_driver gcc_hawi_driver = { + .probe = gcc_hawi_probe, + .driver = { + .name = "gcc-hawi", + .of_match_table = gcc_hawi_match_table, + }, +}; + +static int __init gcc_hawi_init(void) +{ + return platform_driver_register(&gcc_hawi_driver); +} +subsys_initcall(gcc_hawi_init); + +static void __exit gcc_hawi_exit(void) +{ + platform_driver_unregister(&gcc_hawi_driver); +} +module_exit(gcc_hawi_exit); + +MODULE_DESCRIPTION("QTI GCC HAWI Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/gcc-ipq9650.c b/drivers/clk/qcom/gcc-ipq9650.c new file mode 100644 index 000000000000..c556c2bbfd96 --- /dev/null +++ b/drivers/clk/qcom/gcc-ipq9650.c @@ -0,0 +1,3445 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/qcom,ipq9650-gcc.h> +#include <dt-bindings/reset/qcom,ipq9650-gcc.h> + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "clk-regmap-phy-mux.h" +#include "reset.h" + +enum { + DT_XO, + DT_SLEEP_CLK, + DT_PCIE30_PHY0_PIPE_CLK, + DT_PCIE30_PHY1_PIPE_CLK, + DT_PCIE30_PHY2_PIPE_CLK, + DT_PCIE30_PHY3_PIPE_CLK, + DT_PCIE30_PHY4_PIPE_CLK, + DT_USB3_PHY0_CC_PIPE_CLK, + DT_NSS_CMN_CLK, +}; + +enum { + P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, + P_GPLL0_OUT_MAIN, + P_GPLL0_OUT_ODD, + P_GPLL2_OUT_AUX, + P_GPLL2_OUT_MAIN, + P_GPLL4_OUT_MAIN, + P_GPLL4_OUT_ODD, + P_NSS_CMN_CLK, + P_SLEEP_CLK, + P_XO, +}; + +static const struct clk_parent_data gcc_parent_data_xo = { .index = DT_XO }; + +static struct clk_alpha_pll gpll0_main = { + .offset = 0x20000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID], + .clkr = { + .enable_reg = 0xb000, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gpll0_main", + .parent_data = &gcc_parent_data_xo, + .num_parents = 1, + .ops = &clk_alpha_pll_fixed_lucid_ops, + }, + }, +}; + +static struct clk_fixed_factor gpll0_div2 = { + .mult = 1, + .div = 2, + .hw.init = &(const struct clk_init_data) { + .name = "gpll0_div2", + .parent_hws = (const struct clk_hw *[]) { + &gpll0_main.clkr.hw + }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + }, +}; + +static struct clk_alpha_pll_postdiv gpll0 = { + .offset = 0x20000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID], + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpll0", + .parent_hws = (const struct clk_hw *[]) { + &gpll0_main.clkr.hw }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_ro_ops, + }, +}; + +static struct clk_alpha_pll gpll2 = { + .offset = 0x21000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_ZONDA], + .clkr = { + .enable_reg = 0xb000, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gpll2", + .parent_data = &gcc_parent_data_xo, + .num_parents = 1, + .ops = &clk_alpha_pll_zonda_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_gpll2_out_main[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv gpll2_out_main = { + .offset = 0x21000, + .post_div_shift = 8, + .post_div_table = post_div_table_gpll2_out_main, + .num_post_div = ARRAY_SIZE(post_div_table_gpll2_out_main), + .width = 2, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_ZONDA], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gpll2_out_main", + .parent_hws = (const struct clk_hw*[]) { + &gpll2.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_postdiv_zonda_ops, + }, +}; + +static struct clk_alpha_pll gpll4 = { + .offset = 0x22000, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID], + .clkr = { + .enable_reg = 0xb000, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "gpll4", + .parent_data = &gcc_parent_data_xo, + .num_parents = 1, + /* + * There are no consumers for this GPLL in kernel yet, + * (will be added soon), so the clock framework + * disables this source. But some of the clocks + * initialized by boot loaders uses this source. So we + * need to keep this clock ON. Add the + * CLK_IGNORE_UNUSED flag so the clock will not be + * disabled. Once the consumer in kernel is added, we + * can get rid of this flag. + */ + .flags = CLK_IS_CRITICAL, + .ops = &clk_alpha_pll_fixed_lucid_ops, + }, + }, +}; + +static const struct parent_map gcc_parent_map_xo[] = { + { P_XO, 0 }, +}; + +static const struct parent_map gcc_parent_map_0[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_0[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_1[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL4_OUT_MAIN, 2 }, +}; + +static const struct clk_parent_data gcc_parent_data_1[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll4.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_2[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, +}; + +static const struct clk_parent_data gcc_parent_data_2[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_3[] = { + { P_XO, 0 }, +}; + +static const struct clk_parent_data gcc_parent_data_3[] = { + { .index = DT_XO }, +}; + +static const struct parent_map gcc_parent_map_4[] = { + { P_XO, 0 }, + { P_GPLL4_OUT_MAIN, 1 }, + { P_GPLL0_OUT_ODD, 2 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_4[] = { + { .index = DT_XO }, + { .hw = &gpll4.clkr.hw }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_5[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL2_OUT_AUX, 2 }, +}; + +static const struct clk_parent_data gcc_parent_data_5[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll2.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_6[] = { + { P_XO, 0 }, + { P_GPLL4_OUT_ODD, 1 }, + { P_GPLL0_OUT_MAIN, 3 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_6[] = { + { .index = DT_XO }, + { .hw = &gpll4.clkr.hw }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_7[] = { + { P_XO, 0 }, + { P_NSS_CMN_CLK, 1 }, + { P_GPLL0_OUT_ODD, 2 }, + { P_GPLL2_OUT_AUX, 3 }, +}; + +static const struct clk_parent_data gcc_parent_data_7[] = { + { .index = DT_XO }, + { .index = DT_NSS_CMN_CLK }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll2.clkr.hw }, +}; + +static const struct parent_map gcc_parent_map_8[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL0_OUT_ODD, 2 }, + { P_SLEEP_CLK, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_8[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll0.clkr.hw }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map gcc_parent_map_9[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL2_OUT_MAIN, 2 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_9[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll2_out_main.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_10[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL4_OUT_MAIN, 2 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 4 }, +}; + +static const struct clk_parent_data gcc_parent_data_10[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll4.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct parent_map gcc_parent_map_11[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_ODD, 2 }, + { P_SLEEP_CLK, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_11[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map gcc_parent_map_12[] = { + { P_SLEEP_CLK, 6 }, +}; + +static const struct clk_parent_data gcc_parent_data_12[] = { + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map gcc_parent_map_13[] = { + { P_XO, 0 }, + { P_GPLL0_OUT_MAIN, 1 }, + { P_GPLL4_OUT_MAIN, 2 }, + { P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 3 }, +}; + +static const struct clk_parent_data gcc_parent_data_13[] = { + { .index = DT_XO }, + { .hw = &gpll0.clkr.hw }, + { .hw = &gpll4.clkr.hw }, + { .hw = &gpll0_div2.hw }, +}; + +static const struct freq_tbl ftbl_gcc_adss_pwm_clk_src[] = { + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_adss_pwm_clk_src = { + .cmd_rcgr = 0x1c004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_adss_pwm_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_adss_pwm_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_gemnoc_anoc_pcie_clk_src[] = { + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static const struct freq_tbl ftbl_gcc_nss_ts_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_xo_clk_src = { + .cmd_rcgr = 0x34004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_xo, + .freq_tbl = ftbl_gcc_nss_ts_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_xo_clk_src", + .parent_data = &gcc_parent_data_xo, + .num_parents = 1, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_fixed_factor gcc_xo_div4_clk_src = { + .mult = 1, + .div = 4, + .hw.init = &(const struct clk_init_data) { + .name = "gcc_xo_div4_clk_src", + .parent_hws = (const struct clk_hw *[]) { + &gcc_xo_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_fixed_factor_ops, + }, +}; + +static struct clk_rcg2 gcc_nss_ts_clk_src = { + .cmd_rcgr = 0x17088, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_nss_ts_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_nss_ts_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_nssnoc_memnoc_bfdcd_clk_src[] = { + F(462000000, P_NSS_CMN_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_nssnoc_memnoc_bfdcd_clk_src = { + .cmd_rcgr = 0x17004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_7, + .freq_tbl = ftbl_gcc_nssnoc_memnoc_bfdcd_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_memnoc_bfdcd_clk_src", + .parent_data = gcc_parent_data_7, + .num_parents = ARRAY_SIZE(gcc_parent_data_7), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_system_noc_bfdcd_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + F(133333333, P_GPLL0_OUT_MAIN, 6, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + F(266666667, P_GPLL4_OUT_MAIN, 4.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_system_noc_bfdcd_clk_src = { + .cmd_rcgr = 0x2e004, + .freq_tbl = ftbl_gcc_system_noc_bfdcd_clk_src, + .hid_width = 5, + .parent_map = gcc_parent_map_13, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_system_noc_bfdcd_clk_src", + .parent_data = gcc_parent_data_13, + .num_parents = ARRAY_SIZE(gcc_parent_data_13), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcnoc_bfdcd_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + F(50000000, P_GPLL0_OUT_MAIN, 16, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcnoc_bfdcd_clk_src = { + .cmd_rcgr = 0x31004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_pcnoc_bfdcd_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcnoc_bfdcd_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + /* + * There are no consumers for this source in kernel yet, + * (will be added soon), so the clock framework + * disables this source. But some of the clocks + * initialized by boot loaders uses this source. So we + * need to keep this clock ON. Add the + * CLK_IGNORE_UNUSED flag so the clock will not be + * disabled. Once the consumer in kernel is added, we + * can get rid of this flag. + */ + .flags = CLK_IS_CRITICAL, + .ops = &clk_rcg2_ops, + }, +}; +static const struct freq_tbl ftbl_gcc_pcie0_axi_m_clk_src[] = { + F(200000000, P_GPLL4_OUT_MAIN, 6, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie0_axi_m_clk_src = { + .cmd_rcgr = 0x28018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_axi_m_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie0_axi_s_clk_src = { + .cmd_rcgr = 0x28020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_axi_s_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie0_rchng_clk_src = { + .cmd_rcgr = 0x28028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_adss_pwm_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_rchng_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie1_axi_m_clk_src[] = { + F(266666667, P_GPLL4_OUT_MAIN, 4.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_pcie1_axi_m_clk_src = { + .cmd_rcgr = 0x29018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie1_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_axi_m_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie1_axi_s_clk_src = { + .cmd_rcgr = 0x29020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_axi_s_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie1_rchng_clk_src = { + .cmd_rcgr = 0x29028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_adss_pwm_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_rchng_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie2_axi_m_clk_src = { + .cmd_rcgr = 0x2a018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie1_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_axi_m_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie2_axi_s_clk_src = { + .cmd_rcgr = 0x2a020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_axi_s_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie2_rchng_clk_src = { + .cmd_rcgr = 0x2a028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_adss_pwm_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_rchng_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie3_axi_m_clk_src = { + .cmd_rcgr = 0x2b018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie1_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_axi_m_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie3_axi_s_clk_src = { + .cmd_rcgr = 0x2b020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_axi_s_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie3_rchng_clk_src = { + .cmd_rcgr = 0x2b028, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_adss_pwm_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_rchng_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie4_axi_m_clk_src = { + .cmd_rcgr = 0x25004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_axi_m_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie4_axi_s_clk_src = { + .cmd_rcgr = 0x2500c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_1, + .freq_tbl = ftbl_gcc_pcie0_axi_m_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_axi_s_clk_src", + .parent_data = gcc_parent_data_1, + .num_parents = ARRAY_SIZE(gcc_parent_data_1), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_pcie4_rchng_clk_src = { + .cmd_rcgr = 0x25014, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_2, + .freq_tbl = ftbl_gcc_adss_pwm_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_rchng_clk_src", + .parent_data = gcc_parent_data_2, + .num_parents = ARRAY_SIZE(gcc_parent_data_2), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_pcie_aux_clk_src[] = { + F(20000000, P_GPLL0_OUT_MAIN, 10, 1, 4), + { } +}; + +static struct clk_rcg2 gcc_pcie_aux_clk_src = { + .cmd_rcgr = 0x28004, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_8, + .freq_tbl = ftbl_gcc_pcie_aux_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie_aux_clk_src", + .parent_data = gcc_parent_data_8, + .num_parents = ARRAY_SIZE(gcc_parent_data_8), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qdss_at_clk_src[] = { + F(240000000, P_GPLL4_OUT_MAIN, 5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qdss_at_clk_src = { + .cmd_rcgr = 0x2d004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_qdss_at_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qdss_at_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_fixed_factor gcc_eud_at_div_clk_src = { + .mult = 1, + .div = 6, + .hw.init = &(const struct clk_init_data) { + .name = "gcc_eud_at_div_clk_src", + .parent_hws = (const struct clk_hw *[]) { + &gcc_qdss_at_clk_src.clkr.hw }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_fixed_factor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qdss_tsctr_clk_src[] = { + F(600000000, P_GPLL4_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qdss_tsctr_clk_src = { + .cmd_rcgr = 0x2d01c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_4, + .freq_tbl = ftbl_gcc_qdss_tsctr_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qdss_tsctr_clk_src", + .parent_data = gcc_parent_data_4, + .num_parents = ARRAY_SIZE(gcc_parent_data_4), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_fixed_factor gcc_qdss_dap_sync_clk_src = { + .mult = 1, + .div = 4, + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qdss_dap_sync_clk_src", + .parent_hws = (const struct clk_hw *[]) { + &gcc_qdss_tsctr_clk_src.clkr.hw + }, + .num_parents = 1, + .ops = &clk_fixed_factor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sleep_clk_src[] = { + F(32000, P_SLEEP_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sleep_clk_src = { + .cmd_rcgr = 0x3400c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_12, + .freq_tbl = ftbl_gcc_sleep_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sleep_clk_src", + .parent_data = gcc_parent_data_12, + .num_parents = ARRAY_SIZE(gcc_parent_data_12), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qpic_io_macro_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + F(320000000, P_GPLL0_OUT_MAIN, 2.5, 0, 0), + F(400000000, P_GPLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qpic_clk_src = { + .cmd_rcgr = 0x32020, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_qpic_io_macro_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qpic_io_macro_clk_src = { + .cmd_rcgr = 0x32004, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_5, + .freq_tbl = ftbl_gcc_qpic_io_macro_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_io_macro_clk_src", + .parent_data = gcc_parent_data_5, + .num_parents = ARRAY_SIZE(gcc_parent_data_5), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_2x_core_clk_src = { + .cmd_rcgr = 0x100c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_gemnoc_anoc_pcie_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_2x_core_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap_se0_clk_src[] = { + F(960000, P_XO, 10, 2, 5), + F(3686636, P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 1, 2, 217), + F(4800000, P_XO, 5, 0, 0), + F(7373272, P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 1, 4, 217), + F(9600000, P_XO, 2.5, 0, 0), + F(14746544, P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 1, 8, 217), + F(16000000, P_GPLL0_OUT_MAIN, 10, 1, 5), + F(24000000, P_XO, 1, 0, 0), + F(25000000, P_GPLL0_OUT_MAIN, 16, 1, 2), + F(32000000, P_GPLL0_OUT_MAIN, 1, 1, 25), + F(40000000, P_GPLL0_OUT_MAIN, 1, 1, 20), + F(46400000, P_GPLL0_OUT_MAIN, 2, 29, 250), + F(48000000, P_GPLL0_OUT_MAIN, 1, 3, 50), + F(50000000, P_GPLL0_OUT_MAIN, 16, 0, 0), + F(51200000, P_GPLL0_OUT_MAIN, 1, 8, 125), + F(56000000, P_GPLL0_OUT_MAIN, 1, 7, 100), + F(58986175, P_GPLL0_OUT_MAIN, 1, 16, 217), + F(60000000, P_GPLL0_OUT_MAIN, 1, 3, 40), + F(64000000, P_GPLL0_OUT_MAIN, 12.5, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se0_clk_src = { + .cmd_rcgr = 0x2018, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se0_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se1_clk_src = { + .cmd_rcgr = 0x3018, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se1_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se2_clk_src = { + .cmd_rcgr = 0x3034, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se2_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se3_clk_src = { + .cmd_rcgr = 0x3050, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se3_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_qupv3_wrap_se4_clk_src[] = { + F(960000, P_XO, 10, 2, 5), + F(3686636, P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 1, 2, 217), + F(4800000, P_XO, 5, 0, 0), + F(7373272, P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 1, 4, 217), + F(9600000, P_XO, 2.5, 0, 0), + F(14746544, P_GCC_GPLL0_OUT_MAIN_DIV_CLK_SRC, 1, 8, 217), + F(16000000, P_GPLL0_OUT_MAIN, 10, 1, 5), + F(24000000, P_XO, 1, 0, 0), + F(25000000, P_GPLL0_OUT_MAIN, 16, 1, 2), + F(32000000, P_GPLL0_OUT_MAIN, 1, 1, 25), + F(40000000, P_GPLL0_OUT_MAIN, 1, 1, 20), + F(46400000, P_GPLL0_OUT_MAIN, 2, 29, 250), + F(48000000, P_GPLL0_OUT_MAIN, 1, 3, 50), + F(50000000, P_GPLL0_OUT_MAIN, 16, 0, 0), + F(51200000, P_GPLL0_OUT_MAIN, 1, 8, 125), + F(56000000, P_GPLL0_OUT_MAIN, 1, 7, 100), + F(58986175, P_GPLL0_OUT_MAIN, 1, 16, 217), + F(60000000, P_GPLL0_OUT_MAIN, 1, 3, 40), + F(64000000, P_GPLL0_OUT_MAIN, 12.5, 0, 0), + F(100000000, P_GPLL0_OUT_MAIN, 8, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se4_clk_src = { + .cmd_rcgr = 0x306c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se4_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se4_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se5_clk_src = { + .cmd_rcgr = 0x3090, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se4_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se5_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se6_clk_src = { + .cmd_rcgr = 0x4004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se6_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_qupv3_wrap_se7_clk_src = { + .cmd_rcgr = 0x4020, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_qupv3_wrap_se0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se7_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk_src[] = { + F(144000, P_XO, 16, 12, 125), + F(400000, P_XO, 12, 1, 5), + F(24000000, P_GPLL2_OUT_MAIN, 12, 1, 2), + F(48000000, P_GPLL2_OUT_MAIN, 12, 0, 0), + F(96000000, P_GPLL2_OUT_MAIN, 6, 0, 0), + F(177777778, P_GPLL0_OUT_MAIN, 4.5, 0, 0), + F(192000000, P_GPLL2_OUT_MAIN, 3, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc1_apps_clk_src = { + .cmd_rcgr = 0x33004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_9, + .freq_tbl = ftbl_gcc_sdcc1_apps_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_apps_clk_src", + .parent_data = gcc_parent_data_9, + .num_parents = ARRAY_SIZE(gcc_parent_data_9), + .ops = &clk_rcg2_floor_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_sdcc1_ice_core_clk_src[] = { + F(300000000, P_GPLL4_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 gcc_sdcc1_ice_core_clk_src = { + .cmd_rcgr = 0x33018, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_10, + .freq_tbl = ftbl_gcc_sdcc1_ice_core_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_ice_core_clk_src", + .parent_data = gcc_parent_data_10, + .num_parents = ARRAY_SIZE(gcc_parent_data_10), + .ops = &clk_rcg2_floor_ops, + }, +}; + +static struct clk_rcg2 gcc_uniphy_sys_clk_src = { + .cmd_rcgr = 0x17090, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gcc_parent_map_3, + .freq_tbl = ftbl_gcc_nss_ts_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy_sys_clk_src", + .parent_data = gcc_parent_data_3, + .num_parents = ARRAY_SIZE(gcc_parent_data_3), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_usb0_aux_clk_src = { + .cmd_rcgr = 0x2c018, + .mnd_width = 16, + .hid_width = 5, + .parent_map = gcc_parent_map_11, + .freq_tbl = ftbl_gcc_nss_ts_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_aux_clk_src", + .parent_data = gcc_parent_data_11, + .num_parents = ARRAY_SIZE(gcc_parent_data_11), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_usb0_master_clk_src = { + .cmd_rcgr = 0x2c004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_0, + .freq_tbl = ftbl_gcc_gemnoc_anoc_pcie_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_master_clk_src", + .parent_data = gcc_parent_data_0, + .num_parents = ARRAY_SIZE(gcc_parent_data_0), + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gcc_usb0_mock_utmi_clk_src[] = { + F(24000000, P_XO, 1, 0, 0), + F(60000000, P_GPLL4_OUT_ODD, 10, 1, 2), + { } +}; + +static struct clk_rcg2 gcc_usb0_mock_utmi_clk_src = { + .cmd_rcgr = 0x2c02c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_6, + .freq_tbl = ftbl_gcc_usb0_mock_utmi_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_mock_utmi_clk_src", + .parent_data = gcc_parent_data_6, + .num_parents = ARRAY_SIZE(gcc_parent_data_6), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 gcc_usb1_mock_utmi_clk_src = { + .cmd_rcgr = 0x3c004, + .mnd_width = 8, + .hid_width = 5, + .parent_map = gcc_parent_map_6, + .freq_tbl = ftbl_gcc_usb0_mock_utmi_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb1_mock_utmi_clk_src", + .parent_data = gcc_parent_data_6, + .num_parents = ARRAY_SIZE(gcc_parent_data_6), + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_regmap_div gcc_nssnoc_memnoc_div_clk_src = { + .reg = 0x1700c, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_memnoc_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_nssnoc_memnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_usb0_mock_utmi_div_clk_src = { + .reg = 0x2c040, + .shift = 0, + .width = 2, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_mock_utmi_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb0_mock_utmi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gcc_usb1_mock_utmi_div_clk_src = { + .reg = 0x3c018, + .shift = 0, + .width = 2, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "gcc_usb1_mock_utmi_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb1_mock_utmi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch gcc_adss_pwm_clk = { + .halt_reg = 0x1c00c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1c00c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_adss_pwm_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_adss_pwm_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie0_1lane_m_clk = { + .halt_reg = 0x2e07c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e07c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie0_1lane_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie0_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie0_1lane_s_clk = { + .halt_reg = 0x2e0cc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0cc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie0_1lane_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie0_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie1_2lane_m_clk = { + .halt_reg = 0x2e084, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e084, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie1_2lane_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie1_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie1_2lane_s_clk = { + .halt_reg = 0x2e0d0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0d0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie1_2lane_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie1_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie2_2lane_m_clk = { + .halt_reg = 0x2e080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e080, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie2_2lane_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie2_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie2_2lane_s_clk = { + .halt_reg = 0x2e0d4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0d4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie2_2lane_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie2_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie3_2lane_m_clk = { + .halt_reg = 0x2e0bc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0bc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie3_2lane_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie3_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie3_2lane_s_clk = { + .halt_reg = 0x2e0d8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0d8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie3_2lane_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie3_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie4_1lane_m_clk = { + .halt_reg = 0x2e0c0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0c0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie4_1lane_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie4_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_anoc_pcie4_1lane_s_clk = { + .halt_reg = 0x2e0dc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0dc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_anoc_pcie4_1lane_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie4_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cmn_12gpll_ahb_clk = { + .halt_reg = 0x3a004, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3a004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cmn_12gpll_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_cmn_12gpll_sys_clk = { + .halt_reg = 0x3a008, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x3a008, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_cmn_12gpll_sys_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_uniphy_sys_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_mdio_ahb_clk = { + .halt_reg = 0x17040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_mdio_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nss_ts_clk = { + .halt_reg = 0x17018, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x17018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nss_ts_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_nss_ts_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nsscc_clk = { + .halt_reg = 0x17034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nsscc_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nsscfg_clk = { + .halt_reg = 0x1702c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1702c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nsscfg_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_atb_clk = { + .halt_reg = 0x17014, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x17014, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_atb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qdss_at_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_memnoc_1_clk = { + .halt_reg = 0x17084, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17084, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_memnoc_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_nssnoc_memnoc_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_memnoc_clk = { + .halt_reg = 0x17024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17024, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_memnoc_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_nssnoc_memnoc_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_nsscc_clk = { + .halt_reg = 0x17030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_nsscc_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_pcnoc_1_clk = { + .halt_reg = 0x17080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17080, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_pcnoc_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_qosgen_ref_clk = { + .halt_reg = 0x1701c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1701c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_qosgen_ref_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_xo_div4_clk_src.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_snoc_1_clk = { + .halt_reg = 0x1707c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1707c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_snoc_1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_system_noc_bfdcd_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_snoc_clk = { + .halt_reg = 0x17028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_snoc_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_system_noc_bfdcd_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_timeout_ref_clk = { + .halt_reg = 0x17020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_timeout_ref_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_xo_div4_clk_src.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_nssnoc_xo_dcd_clk = { + .halt_reg = 0x17074, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x17074, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_nssnoc_xo_dcd_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_ahb_clk = { + .halt_reg = 0x28030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_aux_clk = { + .halt_reg = 0x28070, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28070, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_axi_m_clk = { + .halt_reg = 0x28038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_axi_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie0_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_axi_s_bridge_clk = { + .halt_reg = 0x28048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_axi_s_bridge_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie0_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_axi_s_clk = { + .halt_reg = 0x28040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x28040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_axi_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie0_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie0_pipe_clk_src = { + .reg = 0x28064, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "pcie0_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_PCIE30_PHY0_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_pipe_clk = { + .halt_reg = 0x28068, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x28068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_pipe_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie0_pipe_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_ahb_clk = { + .halt_reg = 0x29030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_aux_clk = { + .halt_reg = 0x29074, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29074, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_axi_m_clk = { + .halt_reg = 0x29038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_axi_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie1_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_axi_s_bridge_clk = { + .halt_reg = 0x29048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_axi_s_bridge_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie1_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_axi_s_clk = { + .halt_reg = 0x29040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x29040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_axi_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie1_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie1_pipe_clk_src = { + .reg = 0x29064, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "pcie1_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_PCIE30_PHY1_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_pipe_clk = { + .halt_reg = 0x29068, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x29068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_pipe_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie1_pipe_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie2_ahb_clk = { + .halt_reg = 0x2a030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie2_aux_clk = { + .halt_reg = 0x2a078, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a078, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie2_axi_m_clk = { + .halt_reg = 0x2a038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_axi_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie2_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie2_axi_s_bridge_clk = { + .halt_reg = 0x2a048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_axi_s_bridge_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie2_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie2_axi_s_clk = { + .halt_reg = 0x2a040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2a040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_axi_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie2_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie2_pipe_clk_src = { + .reg = 0x2a064, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "pcie2_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_PCIE30_PHY2_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie2_pipe_clk = { + .halt_reg = 0x2a068, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x2a068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_pipe_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie2_pipe_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie3_ahb_clk = { + .halt_reg = 0x2b030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2b030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie3_aux_clk = { + .halt_reg = 0x2b07c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2b07c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie3_axi_m_clk = { + .halt_reg = 0x2b038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2b038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_axi_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie3_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie3_axi_s_bridge_clk = { + .halt_reg = 0x2b048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2b048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_axi_s_bridge_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie3_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie3_axi_s_clk = { + .halt_reg = 0x2b040, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2b040, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_axi_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie3_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie3_pipe_clk_src = { + .reg = 0x2b064, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "pcie3_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_PCIE30_PHY3_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie3_pipe_clk = { + .halt_reg = 0x2b068, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x2b068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_pipe_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie3_pipe_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie4_ahb_clk = { + .halt_reg = 0x2501c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2501c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie4_aux_clk = { + .halt_reg = 0x25020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x25020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie4_axi_m_clk = { + .halt_reg = 0x25028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x25028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_axi_m_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie4_axi_m_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie4_axi_s_bridge_clk = { + .halt_reg = 0x25038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x25038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_axi_s_bridge_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie4_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie4_axi_s_clk = { + .halt_reg = 0x25030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x25030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_axi_s_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcie4_axi_s_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_pcie4_pipe_clk_src = { + .reg = 0x25058, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "pcie4_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_PCIE30_PHY4_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie4_pipe_clk = { + .halt_reg = 0x2503c, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x2503c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_pipe_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie4_pipe_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie0_rchng_clk = { + .halt_reg = 0x28028, + .clkr = { + .enable_reg = 0x28028, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie0_rchng_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie0_rchng_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie1_rchng_clk = { + .halt_reg = 0x29028, + .clkr = { + .enable_reg = 0x29028, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie1_rchng_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie1_rchng_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie2_rchng_clk = { + .halt_reg = 0x2a028, + .clkr = { + .enable_reg = 0x2a028, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie2_rchng_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie2_rchng_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie3_rchng_clk = { + .halt_reg = 0x2b028, + .clkr = { + .enable_reg = 0x2b028, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie3_rchng_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie3_rchng_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_pcie4_rchng_clk = { + .halt_reg = 0x25014, + .clkr = { + .enable_reg = 0x25014, + .enable_mask = BIT(1), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_pcie4_rchng_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_pcie4_rchng_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qdss_at_clk = { + .halt_reg = 0x2d034, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x2d034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qdss_at_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qdss_at_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qdss_dap_clk = { + .halt_reg = 0x2d058, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xb004, + .enable_mask = BIT(2), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qdss_dap_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qdss_dap_sync_clk_src.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qpic_ahb_clk = { + .halt_reg = 0x32010, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x32010, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qpic_clk = { + .halt_reg = 0x32028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x32028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qpic_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qpic_io_macro_clk = { + .halt_reg = 0x3200c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3200c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_io_macro_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qpic_io_macro_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qpic_sleep_clk = { + .halt_reg = 0x32018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x32018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qpic_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_ahb_mst_clk = { + .halt_reg = 0x1014, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xb004, + .enable_mask = BIT(14), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_ahb_mst_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_ahb_slv_clk = { + .halt_reg = 0x102c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0xb004, + .enable_mask = BIT(4), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_ahb_slv_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se0_clk = { + .halt_reg = 0x202c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x202c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se0_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se1_clk = { + .halt_reg = 0x302c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x302c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se1_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se2_clk = { + .halt_reg = 0x3048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se2_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se3_clk = { + .halt_reg = 0x3064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se3_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se4_clk = { + .halt_reg = 0x3080, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3080, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se4_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se5_clk = { + .halt_reg = 0x30a4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x30a4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se5_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se6_clk = { + .halt_reg = 0x4018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se6_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se6_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_qupv3_wrap_se7_clk = { + .halt_reg = 0x4034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x4034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_qupv3_wrap_se7_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_qupv3_wrap_se7_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ahb_clk = { + .halt_reg = 0x3303c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3303c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_apps_clk = { + .halt_reg = 0x3302c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3302c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_apps_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc1_apps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_sdcc1_ice_core_clk = { + .halt_reg = 0x33034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x33034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_sdcc1_ice_core_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sdcc1_ice_core_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_snoc_usb_clk = { + .halt_reg = 0x2e0c4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x2e0c4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_snoc_usb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb0_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy0_ahb_clk = { + .halt_reg = 0x1704c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1704c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy0_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy0_sys_clk = { + .halt_reg = 0x17048, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x17048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy0_sys_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_uniphy_sys_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy1_ahb_clk = { + .halt_reg = 0x1705c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1705c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy1_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy1_sys_clk = { + .halt_reg = 0x17058, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x17058, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy1_sys_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_uniphy_sys_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy2_ahb_clk = { + .halt_reg = 0x1706c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1706c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy2_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_uniphy2_sys_clk = { + .halt_reg = 0x17068, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x17068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_uniphy2_sys_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_uniphy_sys_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_aux_clk = { + .halt_reg = 0x2c04c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x2c04c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_aux_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb0_aux_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_eud_at_clk = { + .halt_reg = 0x30004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x30004, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_eud_at_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_eud_at_div_clk_src.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_master_clk = { + .halt_reg = 0x2c044, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x2c044, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_master_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb0_master_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_mock_utmi_clk = { + .halt_reg = 0x2c050, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x2c050, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_mock_utmi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb0_mock_utmi_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_phy_cfg_ahb_clk = { + .halt_reg = 0x2c05c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x2c05c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_phy_cfg_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap_phy_mux gcc_usb0_pipe_clk_src = { + .reg = 0x2c074, + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_pipe_clk_src", + .parent_data = &(const struct clk_parent_data) { + .index = DT_USB3_PHY0_CC_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_pipe_clk = { + .halt_reg = 0x2c054, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x2c054, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_pipe_clk", + .parent_hws = (const struct clk_hw *[]) { + &gcc_usb0_pipe_clk_src.clkr.hw + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb0_sleep_clk = { + .halt_reg = 0x2c058, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x2c058, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb0_sleep_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sleep_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb1_master_clk = { + .halt_reg = 0x3c028, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3c028, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb1_master_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb1_mock_utmi_clk = { + .halt_reg = 0x3c024, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3c024, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb1_mock_utmi_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_usb1_mock_utmi_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb1_phy_cfg_ahb_clk = { + .halt_reg = 0x3c01c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x3c01c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb1_phy_cfg_ahb_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_pcnoc_bfdcd_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gcc_usb1_sleep_clk = { + .halt_reg = 0x3c020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x3c020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "gcc_usb1_sleep_clk", + .parent_hws = (const struct clk_hw*[]) { + &gcc_sleep_clk_src.clkr.hw, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap *gcc_ipq9650_clocks[] = { + [GCC_ADSS_PWM_CLK] = &gcc_adss_pwm_clk.clkr, + [GCC_ADSS_PWM_CLK_SRC] = &gcc_adss_pwm_clk_src.clkr, + [GCC_ANOC_PCIE0_1LANE_M_CLK] = &gcc_anoc_pcie0_1lane_m_clk.clkr, + [GCC_ANOC_PCIE0_1LANE_S_CLK] = &gcc_anoc_pcie0_1lane_s_clk.clkr, + [GCC_ANOC_PCIE1_2LANE_M_CLK] = &gcc_anoc_pcie1_2lane_m_clk.clkr, + [GCC_ANOC_PCIE1_2LANE_S_CLK] = &gcc_anoc_pcie1_2lane_s_clk.clkr, + [GCC_ANOC_PCIE2_2LANE_M_CLK] = &gcc_anoc_pcie2_2lane_m_clk.clkr, + [GCC_ANOC_PCIE2_2LANE_S_CLK] = &gcc_anoc_pcie2_2lane_s_clk.clkr, + [GCC_ANOC_PCIE3_2LANE_M_CLK] = &gcc_anoc_pcie3_2lane_m_clk.clkr, + [GCC_ANOC_PCIE3_2LANE_S_CLK] = &gcc_anoc_pcie3_2lane_s_clk.clkr, + [GCC_ANOC_PCIE4_1LANE_M_CLK] = &gcc_anoc_pcie4_1lane_m_clk.clkr, + [GCC_ANOC_PCIE4_1LANE_S_CLK] = &gcc_anoc_pcie4_1lane_s_clk.clkr, + [GCC_CMN_12GPLL_AHB_CLK] = &gcc_cmn_12gpll_ahb_clk.clkr, + [GCC_CMN_12GPLL_SYS_CLK] = &gcc_cmn_12gpll_sys_clk.clkr, + [GCC_MDIO_AHB_CLK] = &gcc_mdio_ahb_clk.clkr, + [GCC_NSS_TS_CLK] = &gcc_nss_ts_clk.clkr, + [GCC_NSS_TS_CLK_SRC] = &gcc_nss_ts_clk_src.clkr, + [GCC_NSSCC_CLK] = &gcc_nsscc_clk.clkr, + [GCC_NSSCFG_CLK] = &gcc_nsscfg_clk.clkr, + [GCC_NSSNOC_ATB_CLK] = &gcc_nssnoc_atb_clk.clkr, + [GCC_NSSNOC_MEMNOC_1_CLK] = &gcc_nssnoc_memnoc_1_clk.clkr, + [GCC_NSSNOC_MEMNOC_BFDCD_CLK_SRC] = &gcc_nssnoc_memnoc_bfdcd_clk_src.clkr, + [GCC_NSSNOC_MEMNOC_CLK] = &gcc_nssnoc_memnoc_clk.clkr, + [GCC_NSSNOC_MEMNOC_DIV_CLK_SRC] = &gcc_nssnoc_memnoc_div_clk_src.clkr, + [GCC_NSSNOC_NSSCC_CLK] = &gcc_nssnoc_nsscc_clk.clkr, + [GCC_NSSNOC_PCNOC_1_CLK] = &gcc_nssnoc_pcnoc_1_clk.clkr, + [GCC_NSSNOC_QOSGEN_REF_CLK] = &gcc_nssnoc_qosgen_ref_clk.clkr, + [GCC_NSSNOC_SNOC_1_CLK] = &gcc_nssnoc_snoc_1_clk.clkr, + [GCC_NSSNOC_SNOC_CLK] = &gcc_nssnoc_snoc_clk.clkr, + [GCC_NSSNOC_TIMEOUT_REF_CLK] = &gcc_nssnoc_timeout_ref_clk.clkr, + [GCC_NSSNOC_XO_DCD_CLK] = &gcc_nssnoc_xo_dcd_clk.clkr, + [GCC_PCIE0_AHB_CLK] = &gcc_pcie0_ahb_clk.clkr, + [GCC_PCIE0_AUX_CLK] = &gcc_pcie0_aux_clk.clkr, + [GCC_PCIE0_AXI_M_CLK] = &gcc_pcie0_axi_m_clk.clkr, + [GCC_PCIE0_AXI_M_CLK_SRC] = &gcc_pcie0_axi_m_clk_src.clkr, + [GCC_PCIE0_AXI_S_BRIDGE_CLK] = &gcc_pcie0_axi_s_bridge_clk.clkr, + [GCC_PCIE0_AXI_S_CLK] = &gcc_pcie0_axi_s_clk.clkr, + [GCC_PCIE0_AXI_S_CLK_SRC] = &gcc_pcie0_axi_s_clk_src.clkr, + [GCC_PCIE0_PIPE_CLK] = &gcc_pcie0_pipe_clk.clkr, + [GCC_PCIE0_PIPE_CLK_SRC] = &gcc_pcie0_pipe_clk_src.clkr, + [GCC_PCIE0_RCHNG_CLK_SRC] = &gcc_pcie0_rchng_clk_src.clkr, + [GCC_PCIE0_RCHNG_CLK] = &gcc_pcie0_rchng_clk.clkr, + [GCC_PCIE1_AHB_CLK] = &gcc_pcie1_ahb_clk.clkr, + [GCC_PCIE1_AUX_CLK] = &gcc_pcie1_aux_clk.clkr, + [GCC_PCIE1_AXI_M_CLK] = &gcc_pcie1_axi_m_clk.clkr, + [GCC_PCIE1_AXI_M_CLK_SRC] = &gcc_pcie1_axi_m_clk_src.clkr, + [GCC_PCIE1_AXI_S_BRIDGE_CLK] = &gcc_pcie1_axi_s_bridge_clk.clkr, + [GCC_PCIE1_AXI_S_CLK] = &gcc_pcie1_axi_s_clk.clkr, + [GCC_PCIE1_AXI_S_CLK_SRC] = &gcc_pcie1_axi_s_clk_src.clkr, + [GCC_PCIE1_PIPE_CLK] = &gcc_pcie1_pipe_clk.clkr, + [GCC_PCIE1_PIPE_CLK_SRC] = &gcc_pcie1_pipe_clk_src.clkr, + [GCC_PCIE1_RCHNG_CLK_SRC] = &gcc_pcie1_rchng_clk_src.clkr, + [GCC_PCIE1_RCHNG_CLK] = &gcc_pcie1_rchng_clk.clkr, + [GCC_PCIE2_AHB_CLK] = &gcc_pcie2_ahb_clk.clkr, + [GCC_PCIE2_AUX_CLK] = &gcc_pcie2_aux_clk.clkr, + [GCC_PCIE2_AXI_M_CLK] = &gcc_pcie2_axi_m_clk.clkr, + [GCC_PCIE2_AXI_M_CLK_SRC] = &gcc_pcie2_axi_m_clk_src.clkr, + [GCC_PCIE2_AXI_S_BRIDGE_CLK] = &gcc_pcie2_axi_s_bridge_clk.clkr, + [GCC_PCIE2_AXI_S_CLK] = &gcc_pcie2_axi_s_clk.clkr, + [GCC_PCIE2_AXI_S_CLK_SRC] = &gcc_pcie2_axi_s_clk_src.clkr, + [GCC_PCIE2_PIPE_CLK] = &gcc_pcie2_pipe_clk.clkr, + [GCC_PCIE2_PIPE_CLK_SRC] = &gcc_pcie2_pipe_clk_src.clkr, + [GCC_PCIE2_RCHNG_CLK_SRC] = &gcc_pcie2_rchng_clk_src.clkr, + [GCC_PCIE2_RCHNG_CLK] = &gcc_pcie2_rchng_clk.clkr, + [GCC_PCIE3_AHB_CLK] = &gcc_pcie3_ahb_clk.clkr, + [GCC_PCIE3_AUX_CLK] = &gcc_pcie3_aux_clk.clkr, + [GCC_PCIE3_AXI_M_CLK] = &gcc_pcie3_axi_m_clk.clkr, + [GCC_PCIE3_AXI_M_CLK_SRC] = &gcc_pcie3_axi_m_clk_src.clkr, + [GCC_PCIE3_AXI_S_BRIDGE_CLK] = &gcc_pcie3_axi_s_bridge_clk.clkr, + [GCC_PCIE3_AXI_S_CLK] = &gcc_pcie3_axi_s_clk.clkr, + [GCC_PCIE3_AXI_S_CLK_SRC] = &gcc_pcie3_axi_s_clk_src.clkr, + [GCC_PCIE3_PIPE_CLK] = &gcc_pcie3_pipe_clk.clkr, + [GCC_PCIE3_PIPE_CLK_SRC] = &gcc_pcie3_pipe_clk_src.clkr, + [GCC_PCIE3_RCHNG_CLK_SRC] = &gcc_pcie3_rchng_clk_src.clkr, + [GCC_PCIE3_RCHNG_CLK] = &gcc_pcie3_rchng_clk.clkr, + [GCC_PCIE4_AHB_CLK] = &gcc_pcie4_ahb_clk.clkr, + [GCC_PCIE4_AUX_CLK] = &gcc_pcie4_aux_clk.clkr, + [GCC_PCIE4_AXI_M_CLK] = &gcc_pcie4_axi_m_clk.clkr, + [GCC_PCIE4_AXI_M_CLK_SRC] = &gcc_pcie4_axi_m_clk_src.clkr, + [GCC_PCIE4_AXI_S_BRIDGE_CLK] = &gcc_pcie4_axi_s_bridge_clk.clkr, + [GCC_PCIE4_AXI_S_CLK] = &gcc_pcie4_axi_s_clk.clkr, + [GCC_PCIE4_AXI_S_CLK_SRC] = &gcc_pcie4_axi_s_clk_src.clkr, + [GCC_PCIE4_PIPE_CLK] = &gcc_pcie4_pipe_clk.clkr, + [GCC_PCIE4_PIPE_CLK_SRC] = &gcc_pcie4_pipe_clk_src.clkr, + [GCC_PCIE4_RCHNG_CLK_SRC] = &gcc_pcie4_rchng_clk_src.clkr, + [GCC_PCIE4_RCHNG_CLK] = &gcc_pcie4_rchng_clk.clkr, + [GCC_PCIE_AUX_CLK_SRC] = &gcc_pcie_aux_clk_src.clkr, + [GCC_PCNOC_BFDCD_CLK_SRC] = &gcc_pcnoc_bfdcd_clk_src.clkr, + [GCC_QDSS_AT_CLK] = &gcc_qdss_at_clk.clkr, + [GCC_QDSS_AT_CLK_SRC] = &gcc_qdss_at_clk_src.clkr, + [GCC_QDSS_DAP_CLK] = &gcc_qdss_dap_clk.clkr, + [GCC_QDSS_TSCTR_CLK_SRC] = &gcc_qdss_tsctr_clk_src.clkr, + [GCC_QPIC_AHB_CLK] = &gcc_qpic_ahb_clk.clkr, + [GCC_QPIC_CLK] = &gcc_qpic_clk.clkr, + [GCC_QPIC_CLK_SRC] = &gcc_qpic_clk_src.clkr, + [GCC_QPIC_IO_MACRO_CLK] = &gcc_qpic_io_macro_clk.clkr, + [GCC_QPIC_IO_MACRO_CLK_SRC] = &gcc_qpic_io_macro_clk_src.clkr, + [GCC_QPIC_SLEEP_CLK] = &gcc_qpic_sleep_clk.clkr, + [GCC_QUPV3_2X_CORE_CLK_SRC] = &gcc_qupv3_2x_core_clk_src.clkr, + [GCC_QUPV3_AHB_MST_CLK] = &gcc_qupv3_ahb_mst_clk.clkr, + [GCC_QUPV3_AHB_SLV_CLK] = &gcc_qupv3_ahb_slv_clk.clkr, + [GCC_QUPV3_WRAP_SE0_CLK] = &gcc_qupv3_wrap_se0_clk.clkr, + [GCC_QUPV3_WRAP_SE0_CLK_SRC] = &gcc_qupv3_wrap_se0_clk_src.clkr, + [GCC_QUPV3_WRAP_SE1_CLK] = &gcc_qupv3_wrap_se1_clk.clkr, + [GCC_QUPV3_WRAP_SE1_CLK_SRC] = &gcc_qupv3_wrap_se1_clk_src.clkr, + [GCC_QUPV3_WRAP_SE2_CLK] = &gcc_qupv3_wrap_se2_clk.clkr, + [GCC_QUPV3_WRAP_SE2_CLK_SRC] = &gcc_qupv3_wrap_se2_clk_src.clkr, + [GCC_QUPV3_WRAP_SE3_CLK] = &gcc_qupv3_wrap_se3_clk.clkr, + [GCC_QUPV3_WRAP_SE3_CLK_SRC] = &gcc_qupv3_wrap_se3_clk_src.clkr, + [GCC_QUPV3_WRAP_SE4_CLK] = &gcc_qupv3_wrap_se4_clk.clkr, + [GCC_QUPV3_WRAP_SE4_CLK_SRC] = &gcc_qupv3_wrap_se4_clk_src.clkr, + [GCC_QUPV3_WRAP_SE5_CLK] = &gcc_qupv3_wrap_se5_clk.clkr, + [GCC_QUPV3_WRAP_SE5_CLK_SRC] = &gcc_qupv3_wrap_se5_clk_src.clkr, + [GCC_QUPV3_WRAP_SE6_CLK] = &gcc_qupv3_wrap_se6_clk.clkr, + [GCC_QUPV3_WRAP_SE6_CLK_SRC] = &gcc_qupv3_wrap_se6_clk_src.clkr, + [GCC_QUPV3_WRAP_SE7_CLK] = &gcc_qupv3_wrap_se7_clk.clkr, + [GCC_QUPV3_WRAP_SE7_CLK_SRC] = &gcc_qupv3_wrap_se7_clk_src.clkr, + [GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr, + [GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr, + [GCC_SDCC1_APPS_CLK_SRC] = &gcc_sdcc1_apps_clk_src.clkr, + [GCC_SDCC1_ICE_CORE_CLK] = &gcc_sdcc1_ice_core_clk.clkr, + [GCC_SDCC1_ICE_CORE_CLK_SRC] = &gcc_sdcc1_ice_core_clk_src.clkr, + [GCC_SLEEP_CLK_SRC] = &gcc_sleep_clk_src.clkr, + [GCC_SNOC_USB_CLK] = &gcc_snoc_usb_clk.clkr, + [GCC_SYSTEM_NOC_BFDCD_CLK_SRC] = &gcc_system_noc_bfdcd_clk_src.clkr, + [GCC_UNIPHY0_AHB_CLK] = &gcc_uniphy0_ahb_clk.clkr, + [GCC_UNIPHY0_SYS_CLK] = &gcc_uniphy0_sys_clk.clkr, + [GCC_UNIPHY1_AHB_CLK] = &gcc_uniphy1_ahb_clk.clkr, + [GCC_UNIPHY1_SYS_CLK] = &gcc_uniphy1_sys_clk.clkr, + [GCC_UNIPHY2_AHB_CLK] = &gcc_uniphy2_ahb_clk.clkr, + [GCC_UNIPHY2_SYS_CLK] = &gcc_uniphy2_sys_clk.clkr, + [GCC_UNIPHY_SYS_CLK_SRC] = &gcc_uniphy_sys_clk_src.clkr, + [GCC_USB0_AUX_CLK] = &gcc_usb0_aux_clk.clkr, + [GCC_USB0_AUX_CLK_SRC] = &gcc_usb0_aux_clk_src.clkr, + [GCC_USB0_EUD_AT_CLK] = &gcc_usb0_eud_at_clk.clkr, + [GCC_USB0_MASTER_CLK] = &gcc_usb0_master_clk.clkr, + [GCC_USB0_MASTER_CLK_SRC] = &gcc_usb0_master_clk_src.clkr, + [GCC_USB0_MOCK_UTMI_CLK] = &gcc_usb0_mock_utmi_clk.clkr, + [GCC_USB0_MOCK_UTMI_CLK_SRC] = &gcc_usb0_mock_utmi_clk_src.clkr, + [GCC_USB0_MOCK_UTMI_DIV_CLK_SRC] = &gcc_usb0_mock_utmi_div_clk_src.clkr, + [GCC_USB0_PHY_CFG_AHB_CLK] = &gcc_usb0_phy_cfg_ahb_clk.clkr, + [GCC_USB0_PIPE_CLK] = &gcc_usb0_pipe_clk.clkr, + [GCC_USB0_PIPE_CLK_SRC] = &gcc_usb0_pipe_clk_src.clkr, + [GCC_USB0_SLEEP_CLK] = &gcc_usb0_sleep_clk.clkr, + [GCC_USB1_MASTER_CLK] = &gcc_usb1_master_clk.clkr, + [GCC_USB1_MOCK_UTMI_CLK] = &gcc_usb1_mock_utmi_clk.clkr, + [GCC_USB1_MOCK_UTMI_CLK_SRC] = &gcc_usb1_mock_utmi_clk_src.clkr, + [GCC_USB1_MOCK_UTMI_DIV_CLK_SRC] = &gcc_usb1_mock_utmi_div_clk_src.clkr, + [GCC_USB1_PHY_CFG_AHB_CLK] = &gcc_usb1_phy_cfg_ahb_clk.clkr, + [GCC_USB1_SLEEP_CLK] = &gcc_usb1_sleep_clk.clkr, + [GCC_XO_CLK_SRC] = &gcc_xo_clk_src.clkr, + [GPLL0_MAIN] = &gpll0_main.clkr, + [GPLL0] = &gpll0.clkr, + [GPLL2] = &gpll2.clkr, + [GPLL2_OUT_MAIN] = &gpll2_out_main.clkr, + [GPLL4] = &gpll4.clkr, +}; + +static const struct qcom_reset_map gcc_ipq9650_resets[] = { + [GCC_ADSS_BCR] = { 0x1c000 }, + [GCC_ADSS_PWM_CLK_ARES] = { 0x1c00c, 2 }, + [GCC_APC0_VOLTAGE_DROOP_DETECTOR_BCR] = { 0x38000 }, + [GCC_APC0_VOLTAGE_DROOP_DETECTOR_GPLL0_CLK_ARES] = { 0x3800c, 2 }, + [GCC_APSS_AHB_CLK_ARES] = { 0x24014, 2 }, + [GCC_APSS_ATB_CLK_ARES] = { 0x24034, 2 }, + [GCC_APSS_AXI_CLK_ARES] = { 0x24018, 2 }, + [GCC_APSS_TS_CLK_ARES] = { 0x24030, 2 }, + [GCC_BOOT_ROM_AHB_CLK_ARES] = { 0x1302c, 2 }, + [GCC_BOOT_ROM_BCR] = { 0x13028 }, + [GCC_CPUSS_TRIG_CLK_ARES] = { 0x2401c, 2 }, + [GCC_GP1_CLK_ARES] = { 0x8018, 2 }, + [GCC_GP2_CLK_ARES] = { 0x8030, 2 }, + [GCC_GP3_CLK_ARES] = { 0x8048, 2 }, + [GCC_MDIO_AHB_CLK_ARES] = { 0x17040, 2 }, + [GCC_MDIO_BCR] = { 0x1703c }, + [GCC_NSS_BCR] = { 0x17000 }, + [GCC_NSS_TS_CLK_ARES] = { 0x17018, 2 }, + [GCC_NSSCC_CLK_ARES] = { 0x17034, 2 }, + [GCC_NSSCFG_CLK_ARES] = { 0x1702c, 2 }, + [GCC_NSSNOC_ATB_CLK_ARES] = { 0x17014, 2 }, + [GCC_NSSNOC_MEMNOC_1_CLK_ARES] = { 0x17084, 2 }, + [GCC_NSSNOC_MEMNOC_CLK_ARES] = { 0x17024, 2 }, + [GCC_NSSNOC_NSSCC_CLK_ARES] = { 0x17030, 2 }, + [GCC_NSSNOC_PCNOC_1_CLK_ARES] = { 0x17080, 2 }, + [GCC_NSSNOC_QOSGEN_REF_CLK_ARES] = { 0x1701c, 2 }, + [GCC_NSSNOC_SNOC_1_CLK_ARES] = { 0x1707c, 2 }, + [GCC_NSSNOC_SNOC_CLK_ARES] = { 0x17028, 2 }, + [GCC_NSSNOC_TIMEOUT_REF_CLK_ARES] = { 0x17020, 2 }, + [GCC_NSSNOC_XO_DCD_CLK_ARES] = { 0x17074, 2 }, + [GCC_PCIE0_AHB_CLK_ARES] = { 0x28030, 2 }, + [GCC_PCIE0_AUX_CLK_ARES] = { 0x28070, 2 }, + [GCC_PCIE0_AXI_M_CLK_ARES] = { 0x28038, 2 }, + [GCC_PCIE0_AXI_S_BRIDGE_CLK_ARES] = { 0x28048, 2 }, + [GCC_PCIE0_AXI_S_CLK_ARES] = { 0x28040, 2 }, + [GCC_PCIE0_BCR] = { 0x28000 }, + [GCC_PCIE0_LINK_DOWN_BCR] = { 0x28054 }, + [GCC_PCIE0_PHY_BCR] = { 0x28060 }, + [GCC_PCIE0_PIPE_CLK_ARES] = { 0x28068, 2 }, + [GCC_PCIE0PHY_PHY_BCR] = { 0x2805c }, + [GCC_PCIE0_PIPE_RESET] = { 0x28058, 0 }, + [GCC_PCIE0_CORE_STICKY_RESET] = { 0x28058, 1 }, + [GCC_PCIE0_AXI_S_STICKY_RESET] = { 0x28058, 2 }, + [GCC_PCIE0_AXI_S_RESET] = { 0x28058, 3 }, + [GCC_PCIE0_AXI_M_STICKY_RESET] = { 0x28058, 4 }, + [GCC_PCIE0_AXI_M_RESET] = { 0x28058, 5 }, + [GCC_PCIE0_AUX_RESET] = { 0x28058, 6 }, + [GCC_PCIE0_AHB_RESET] = { 0x28058, 7 }, + [GCC_PCIE1_AHB_CLK_ARES] = { 0x29030, 2 }, + [GCC_PCIE1_AUX_CLK_ARES] = { 0x29074, 2 }, + [GCC_PCIE1_AXI_M_CLK_ARES] = { 0x29038, 2 }, + [GCC_PCIE1_AXI_S_BRIDGE_CLK_ARES] = { 0x29048, 2 }, + [GCC_PCIE1_AXI_S_CLK_ARES] = { 0x29040, 2 }, + [GCC_PCIE1_BCR] = { 0x29000 }, + [GCC_PCIE1_LINK_DOWN_BCR] = { 0x29054 }, + [GCC_PCIE1_PHY_BCR] = { 0x29060 }, + [GCC_PCIE1_PIPE_CLK_ARES] = { 0x29068, 2 }, + [GCC_PCIE1PHY_PHY_BCR] = { 0x2905c }, + [GCC_PCIE1_PIPE_RESET] = { 0x29058, 0 }, + [GCC_PCIE1_CORE_STICKY_RESET] = { 0x29058, 1 }, + [GCC_PCIE1_AXI_S_STICKY_RESET] = { 0x29058, 2 }, + [GCC_PCIE1_AXI_S_RESET] = { 0x29058, 3 }, + [GCC_PCIE1_AXI_M_STICKY_RESET] = { 0x29058, 4 }, + [GCC_PCIE1_AXI_M_RESET] = { 0x29058, 5 }, + [GCC_PCIE1_AUX_RESET] = { 0x29058, 6 }, + [GCC_PCIE1_AHB_RESET] = { 0x29058, 7 }, + [GCC_PCIE2_AHB_CLK_ARES] = { 0x2a030, 2 }, + [GCC_PCIE2_AUX_CLK_ARES] = { 0x2a078, 2 }, + [GCC_PCIE2_AXI_M_CLK_ARES] = { 0x2a038, 2 }, + [GCC_PCIE2_AXI_S_BRIDGE_CLK_ARES] = { 0x2a048, 2 }, + [GCC_PCIE2_AXI_S_CLK_ARES] = { 0x2a040, 2 }, + [GCC_PCIE2_BCR] = { 0x2a000 }, + [GCC_PCIE2_LINK_DOWN_BCR] = { 0x2a054 }, + [GCC_PCIE2_PHY_BCR] = { 0x2a060 }, + [GCC_PCIE2_PIPE_CLK_ARES] = { 0x2a068, 2 }, + [GCC_PCIE2PHY_PHY_BCR] = { 0x2a05c }, + [GCC_PCIE2_PIPE_RESET] = { 0x2a058, 0 }, + [GCC_PCIE2_CORE_STICKY_RESET] = { 0x2a058, 1 }, + [GCC_PCIE2_AXI_S_STICKY_RESET] = { 0x2a058, 2 }, + [GCC_PCIE2_AXI_S_RESET] = { 0x2a058, 3 }, + [GCC_PCIE2_AXI_M_STICKY_RESET] = { 0x2a058, 4 }, + [GCC_PCIE2_AXI_M_RESET] = { 0x2a058, 5 }, + [GCC_PCIE2_AUX_RESET] = { 0x2a058, 6 }, + [GCC_PCIE2_AHB_RESET] = { 0x2a058, 7 }, + [GCC_PCIE3_AHB_CLK_ARES] = { 0x2b030, 2 }, + [GCC_PCIE3_AUX_CLK_ARES] = { 0x2b07c, 2 }, + [GCC_PCIE3_AXI_M_CLK_ARES] = { 0x2b038, 2 }, + [GCC_PCIE3_AXI_S_BRIDGE_CLK_ARES] = { 0x2b048, 2 }, + [GCC_PCIE3_AXI_S_CLK_ARES] = { 0x2b040, 2 }, + [GCC_PCIE3_BCR] = { 0x2b000 }, + [GCC_PCIE3_LINK_DOWN_BCR] = { 0x2b054 }, + [GCC_PCIE3_PHY_BCR] = { 0x2b060 }, + [GCC_PCIE3_PIPE_CLK_ARES] = { 0x2b068, 2 }, + [GCC_PCIE3PHY_PHY_BCR] = { 0x2b05c }, + [GCC_PCIE3_PIPE_RESET] = { 0x2b058, 0 }, + [GCC_PCIE3_CORE_STICKY_RESET] = { 0x2b058, 1 }, + [GCC_PCIE3_AXI_S_STICKY_RESET] = { 0x2b058, 2 }, + [GCC_PCIE3_AXI_S_RESET] = { 0x2b058, 3 }, + [GCC_PCIE3_AXI_M_STICKY_RESET] = { 0x2b058, 4 }, + [GCC_PCIE3_AXI_M_RESET] = { 0x2b058, 5 }, + [GCC_PCIE3_AUX_RESET] = { 0x2b058, 6 }, + [GCC_PCIE3_AHB_RESET] = { 0x2b058, 7 }, + [GCC_PCIE4_AHB_CLK_ARES] = { 0x2501c, 2 }, + [GCC_PCIE4_AUX_CLK_ARES] = { 0x25020, 2 }, + [GCC_PCIE4_AXI_M_CLK_ARES] = { 0x25028, 2 }, + [GCC_PCIE4_AXI_S_BRIDGE_CLK_ARES] = { 0x25038, 2 }, + [GCC_PCIE4_AXI_S_CLK_ARES] = { 0x25030, 2 }, + [GCC_PCIE4_BCR] = { 0x25000 }, + [GCC_PCIE4_LINK_DOWN_BCR] = { 0x25044 }, + [GCC_PCIE4_PHY_BCR] = { 0x2504c }, + [GCC_PCIE4_PIPE_CLK_ARES] = { 0x2503c, 2 }, + [GCC_PCIE4_PIPE_RESET] = { 0x25054, 0 }, + [GCC_PCIE4_CORE_STICKY_RESET] = { 0x25054, 1 }, + [GCC_PCIE4_AXI_S_STICKY_RESET] = { 0x25054, 2 }, + [GCC_PCIE4_AXI_S_RESET] = { 0x25054, 3 }, + [GCC_PCIE4_AXI_M_STICKY_RESET] = { 0x25054, 4 }, + [GCC_PCIE4_AXI_M_RESET] = { 0x25054, 5 }, + [GCC_PCIE4_AUX_RESET] = { 0x25054, 6 }, + [GCC_PCIE4_AHB_RESET] = { 0x25054, 7 }, + [GCC_PCIE4PHY_PHY_BCR] = { 0x25048 }, + [GCC_QDSS_APB2JTAG_CLK_ARES] = { 0x2d05c, 2 }, + [GCC_QDSS_AT_CLK_ARES] = { 0x2d034, 2 }, + [GCC_QDSS_BCR] = { 0x2d000 }, + [GCC_QDSS_CFG_AHB_CLK_ARES] = { 0x2d068, 2 }, + [GCC_QDSS_DAP_AHB_CLK_ARES] = { 0x2d064, 2 }, + [GCC_QDSS_DAP_CLK_ARES] = { 0x2d058, 2 }, + [GCC_QDSS_ETR_USB_CLK_ARES] = { 0x2d060, 2 }, + [GCC_QDSS_EUD_AT_CLK_ARES] = { 0x2d06c, 2 }, + [GCC_QDSS_STM_CLK_ARES] = { 0x2d03c, 2 }, + [GCC_QDSS_TRACECLKIN_CLK_ARES] = { 0x2d040, 2 }, + [GCC_QDSS_TS_CLK_ARES] = { 0x2d078, 2 }, + [GCC_QDSS_TSCTR_DIV16_CLK_ARES] = { 0x2d054, 2 }, + [GCC_QDSS_TSCTR_DIV2_CLK_ARES] = { 0x2d044, 2 }, + [GCC_QDSS_TSCTR_DIV3_CLK_ARES] = { 0x2d048, 2 }, + [GCC_QDSS_TSCTR_DIV4_CLK_ARES] = { 0x2d04c, 2 }, + [GCC_QDSS_TSCTR_DIV8_CLK_ARES] = { 0x2d050, 2 }, + [GCC_QPIC_AHB_CLK_ARES] = { 0x32010, 2 }, + [GCC_QPIC_CLK_ARES] = { 0x32028, 2 }, + [GCC_QPIC_BCR] = { 0x32000 }, + [GCC_QPIC_IO_MACRO_CLK_ARES] = { 0x3200c, 2 }, + [GCC_QPIC_SLEEP_CLK_ARES] = { 0x32018, 2 }, + [GCC_QUPV3_2X_CORE_CLK_ARES] = { 0x1020, 2 }, + [GCC_QUPV3_AHB_MST_CLK_ARES] = { 0x1014, 2 }, + [GCC_QUPV3_AHB_SLV_CLK_ARES] = { 0x102c, 2 }, + [GCC_QUPV3_BCR] = { 0x1000 }, + [GCC_QUPV3_CORE_CLK_ARES] = { 0x1018, 2 }, + [GCC_QUPV3_WRAP_SE0_CLK_ARES] = { 0x202c, 2 }, + [GCC_QUPV3_WRAP_SE0_BCR] = { 0x2000 }, + [GCC_QUPV3_WRAP_SE1_CLK_ARES] = { 0x302c, 2 }, + [GCC_QUPV3_WRAP_SE1_BCR] = { 0x3000 }, + [GCC_QUPV3_WRAP_SE2_CLK_ARES] = { 0x3048, 2 }, + [GCC_QUPV3_WRAP_SE2_BCR] = { 0x3030 }, + [GCC_QUPV3_WRAP_SE3_CLK_ARES] = { 0x3064, 2 }, + [GCC_QUPV3_WRAP_SE3_BCR] = { 0x304c }, + [GCC_QUPV3_WRAP_SE4_CLK_ARES] = { 0x3080, 2 }, + [GCC_QUPV3_WRAP_SE4_BCR] = { 0x3068 }, + [GCC_QUPV3_WRAP_SE5_CLK_ARES] = { 0x30a4, 2 }, + [GCC_QUPV3_WRAP_SE5_BCR] = { 0x308c }, + [GCC_QUPV3_WRAP_SE6_CLK_ARES] = { 0x4018, 2 }, + [GCC_QUPV3_WRAP_SE6_BCR] = { 0x4000 }, + [GCC_QUPV3_WRAP_SE7_CLK_ARES] = { 0x4034, 2 }, + [GCC_QUPV3_WRAP_SE7_BCR] = { 0x401c }, + [GCC_QUSB2_0_PHY_BCR] = { 0x2c068 }, + [GCC_QUSB2_1_PHY_BCR] = { 0x3c030 }, + [GCC_SDCC1_APPS_CLK_ARES] = { 0x3302c, 2 }, + [GCC_SDCC1_ICE_CORE_CLK_ARES] = { 0x33034, 2 }, + [GCC_SDCC_BCR] = { 0x33000 }, + [GCC_TLMM_AHB_CLK_ARES] = { 0x3e004, 2 }, + [GCC_TLMM_CLK_ARES] = { 0x3e008, 2 }, + [GCC_TLMM_BCR] = { 0x3e000 }, + [GCC_UNIPHY0_AHB_CLK_ARES] = { 0x1704c, 2 }, + [GCC_UNIPHY0_BCR] = { 0x17044 }, + [GCC_UNIPHY0_PMA_BCR] = { 0x17098 }, + [GCC_UNIPHY0_SYS_CLK_ARES] = { 0x17048, 2 }, + [GCC_UNIPHY1_AHB_CLK_ARES] = { 0x1705c, 2 }, + [GCC_UNIPHY1_BCR] = { 0x17054 }, + [GCC_UNIPHY1_PMA_BCR] = { 0x1709c }, + [GCC_UNIPHY1_SYS_CLK_ARES] = { 0x17058, 2 }, + [GCC_UNIPHY2_AHB_CLK_ARES] = { 0x1706c, 2 }, + [GCC_UNIPHY2_BCR] = { 0x17064 }, + [GCC_UNIPHY2_PMA_BCR] = { 0x170a0 }, + [GCC_UNIPHY2_SYS_CLK_ARES] = { 0x17068, 2 }, + [GCC_UNIPHY0_XPCS_ARES] = { 0x17050, 2 }, + [GCC_UNIPHY1_XLGPCS_ARES] = { 0x17060, 1 }, + [GCC_UNIPHY1_XPCS_ARES] = { 0x17060, 2 }, + [GCC_UNIPHY2_XLGPCS_ARES] = { 0x17070, 1 }, + [GCC_UNIPHY2_XPCS_ARES] = { 0x17070, 2 }, + [GCC_USB0_AUX_CLK_ARES] = { 0x2c04c, 2 }, + [GCC_USB0_MASTER_CLK_ARES] = { 0x2c044, 2 }, + [GCC_USB0_MOCK_UTMI_CLK_ARES] = { 0x2c050, 2 }, + [GCC_USB0_PHY_BCR] = { 0x2c06c }, + [GCC_USB0_PHY_CFG_AHB_CLK_ARES] = { 0x2c05c, 2 }, + [GCC_USB0_PIPE_CLK_ARES] = { 0x2c054, 2 }, + [GCC_USB0_SLEEP_CLK_ARES] = { 0x2c058, 2 }, + [GCC_USB1_BCR] = { 0x3c000 }, + [GCC_USB1_MASTER_CLK_ARES] = { 0x3c028, 2 }, + [GCC_USB1_MOCK_UTMI_CLK_ARES] = { 0x3c024, 2 }, + [GCC_USB1_PHY_CFG_AHB_CLK_ARES] = { 0x3c01c, 2 }, + [GCC_USB1_SLEEP_CLK_ARES] = { 0x3c020, 2 }, + [GCC_USB3PHY_0_PHY_BCR] = { 0x2c070 }, + [GCC_USB_BCR] = { 0x2c000 }, +}; + +static const struct of_device_id gcc_ipq9650_match_table[] = { + { .compatible = "qcom,ipq9650-gcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gcc_ipq9650_match_table); + +static const struct regmap_config gcc_ipq9650_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3f024, + .fast_io = true, +}; + +static struct clk_hw *gcc_ipq9650_hws[] = { + &gpll0_div2.hw, + &gcc_xo_div4_clk_src.hw, + &gcc_qdss_dap_sync_clk_src.hw, + &gcc_eud_at_div_clk_src.hw, +}; + +static const struct qcom_cc_desc gcc_ipq9650_desc = { + .config = &gcc_ipq9650_regmap_config, + .clks = gcc_ipq9650_clocks, + .num_clks = ARRAY_SIZE(gcc_ipq9650_clocks), + .resets = gcc_ipq9650_resets, + .num_resets = ARRAY_SIZE(gcc_ipq9650_resets), + .clk_hws = gcc_ipq9650_hws, + .num_clk_hws = ARRAY_SIZE(gcc_ipq9650_hws), +}; + +static int gcc_ipq9650_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &gcc_ipq9650_desc); +} + +static struct platform_driver gcc_ipq9650_driver = { + .probe = gcc_ipq9650_probe, + .driver = { + .name = "qcom,gcc-ipq9650", + .of_match_table = gcc_ipq9650_match_table, + }, +}; + +static int __init gcc_ipq9650_init(void) +{ + return platform_driver_register(&gcc_ipq9650_driver); +} +core_initcall(gcc_ipq9650_init); + +static void __exit gcc_ipq9650_exit(void) +{ + platform_driver_unregister(&gcc_ipq9650_driver); +} +module_exit(gcc_ipq9650_exit); + +MODULE_DESCRIPTION("QTI GCC IPQ9650 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/gcc-nord.c b/drivers/clk/qcom/gcc-nord.c index 3098d8fac0fb..8a6e429f2640 100644 --- a/drivers/clk/qcom/gcc-nord.c +++ b/drivers/clk/qcom/gcc-nord.c @@ -1850,7 +1850,7 @@ static const struct regmap_config gcc_nord_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data gcc_nord_driver_data = { +static const struct qcom_cc_driver_data gcc_nord_driver_data = { .dfs_rcgs = gcc_nord_dfs_clocks, .num_dfs_rcgs = ARRAY_SIZE(gcc_nord_dfs_clocks), }; diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c index 95aa07120245..ee5f86ca50cb 100644 --- a/drivers/clk/qcom/gdsc.c +++ b/drivers/clk/qcom/gdsc.c @@ -7,6 +7,7 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/export.h> +#include <linux/interconnect.h> #include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/ktime.h> @@ -147,6 +148,12 @@ static int gdsc_toggle_logic(struct gdsc *sc, enum gdsc_status status, return ret; } + if (status == GDSC_ON) { + ret = icc_set_bw(sc->icc_path, 1, 1); + if (ret) + goto err_disable_supply; + } + ret = gdsc_update_collapse_bit(sc, status == GDSC_OFF); /* If disabling votable gdscs, don't poll on status */ @@ -177,6 +184,12 @@ static int gdsc_toggle_logic(struct gdsc *sc, enum gdsc_status status, ret = gdsc_poll_status(sc, status); WARN(ret, "%s status stuck at 'o%s'", sc->pd.name, status ? "ff" : "n"); + if (!ret && status == GDSC_OFF) { + ret = icc_set_bw(sc->icc_path, 0, 0); + if (ret) + return ret; + } + if (!ret && status == GDSC_OFF && sc->rsupply) { ret = regulator_disable(sc->rsupply); if (ret < 0) @@ -184,6 +197,12 @@ static int gdsc_toggle_logic(struct gdsc *sc, enum gdsc_status status, } return ret; + +err_disable_supply: + if (status == GDSC_ON && sc->rsupply) + regulator_disable(sc->rsupply); + + return ret; } static inline int gdsc_deassert_reset(struct gdsc *sc) @@ -585,6 +604,20 @@ int gdsc_register(struct gdsc_desc *desc, return -ENOMEM; for (i = 0; i < num; i++) { + if (!scs[i] || !scs[i]->needs_icc) + continue; + + scs[i]->icc_path = devm_of_icc_get_by_index(dev, scs[i]->icc_path_index); + if (IS_ERR(scs[i]->icc_path)) { + ret = PTR_ERR(scs[i]->icc_path); + if (ret != -ENODEV) + return ret; + + scs[i]->icc_path = NULL; + } + } + + for (i = 0; i < num; i++) { if (!scs[i] || !scs[i]->supply) continue; diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h index dd843e86c05b..92ff6bcce7b1 100644 --- a/drivers/clk/qcom/gdsc.h +++ b/drivers/clk/qcom/gdsc.h @@ -9,6 +9,7 @@ #include <linux/err.h> #include <linux/pm_domain.h> +struct icc_path; struct regmap; struct regulator; struct reset_controller_dev; @@ -74,6 +75,10 @@ struct gdsc { const char *supply; struct regulator *rsupply; + + bool needs_icc; + unsigned int icc_path_index; + struct icc_path *icc_path; }; struct gdsc_desc { diff --git a/drivers/clk/qcom/gpucc-sm8750.c b/drivers/clk/qcom/gpucc-sm8750.c index 5d52c6d8b5e5..1466bd36403f 100644 --- a/drivers/clk/qcom/gpucc-sm8750.c +++ b/drivers/clk/qcom/gpucc-sm8750.c @@ -421,7 +421,7 @@ static struct clk_alpha_pll *gpu_cc_alpha_plls[] = { &gpu_cc_pll0, }; -static u32 gpu_cc_sm8750_critical_cbcrs[] = { +static const u32 gpu_cc_sm8750_critical_cbcrs[] = { 0x9004, /* GPU_CC_RSCC_XO_AON_CLK */ 0x9008, /* GPU_CC_CXO_AON_CLK */ 0x9064, /* GPU_CC_GX_AHB_FF_CLK */ @@ -430,7 +430,7 @@ static u32 gpu_cc_sm8750_critical_cbcrs[] = { 0x93a8, /* GPU_CC_RSCC_HUB_AON_CLK */ }; -static struct qcom_cc_driver_data gpu_cc_sm8750_driver_data = { +static const struct qcom_cc_driver_data gpu_cc_sm8750_driver_data = { .alpha_plls = gpu_cc_alpha_plls, .num_alpha_plls = ARRAY_SIZE(gpu_cc_alpha_plls), .clk_cbcrs = gpu_cc_sm8750_critical_cbcrs, diff --git a/drivers/clk/qcom/gxclkctl-kaanapali.c b/drivers/clk/qcom/gxclkctl-kaanapali.c index 40d856378a74..7b0af0ba1e68 100644 --- a/drivers/clk/qcom/gxclkctl-kaanapali.c +++ b/drivers/clk/qcom/gxclkctl-kaanapali.c @@ -53,6 +53,7 @@ static const struct qcom_cc_desc gx_clkctl_kaanapali_desc = { static const struct of_device_id gx_clkctl_kaanapali_match_table[] = { { .compatible = "qcom,glymur-gxclkctl" }, { .compatible = "qcom,kaanapali-gxclkctl" }, + { .compatible = "qcom,milos-gxclkctl" }, { .compatible = "qcom,sm8750-gxclkctl" }, { } }; diff --git a/drivers/clk/qcom/ipq-cmn-pll.c b/drivers/clk/qcom/ipq-cmn-pll.c index 5763e4df59a1..441e88101ea3 100644 --- a/drivers/clk/qcom/ipq-cmn-pll.c +++ b/drivers/clk/qcom/ipq-cmn-pll.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ /* @@ -20,6 +20,11 @@ * and an output clock to NSS (network subsystem) at 300 MHZ. The other output * clocks from CMN PLL on IPQ5424 are the same as IPQ9574. * + * On the IPQ5332 SoC, the CMN PLL provides a single 50 MHZ clock output to + * the Ethernet PHY (or switch) via the UNIPHY (PCS). It also supplies a 200 + * MHZ clock to the PPE. The remaining fixed-rate clocks to the GCC and PCS + * are the same as those in the IPQ9574 SoC. + * * +---------+ * | GCC | * +--+---+--+ @@ -51,6 +56,7 @@ #include <dt-bindings/clock/qcom,ipq-cmn-pll.h> #include <dt-bindings/clock/qcom,ipq5018-cmn-pll.h> +#include <dt-bindings/clock/qcom,ipq5332-cmn-pll.h> #include <dt-bindings/clock/qcom,ipq5424-cmn-pll.h> #include <dt-bindings/clock/qcom,ipq6018-cmn-pll.h> #include <dt-bindings/clock/qcom,ipq8074-cmn-pll.h> @@ -131,6 +137,16 @@ static const struct cmn_pll_fixed_output_clk ipq8074_output_clks[] = { { /* Sentinel */ } }; +static const struct cmn_pll_fixed_output_clk ipq5332_output_clks[] = { + CLK_PLL_OUTPUT(IPQ5332_XO_24MHZ_CLK, "xo-24mhz", 24000000UL), + CLK_PLL_OUTPUT(IPQ5332_SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), + CLK_PLL_OUTPUT(IPQ5332_PCS_31P25MHZ_CLK, "pcs-31p25mhz", 31250000UL), + CLK_PLL_OUTPUT(IPQ5332_NSS_300MHZ_CLK, "nss-300mhz", 300000000UL), + CLK_PLL_OUTPUT(IPQ5332_PPE_200MHZ_CLK, "ppe-200mhz", 200000000UL), + CLK_PLL_OUTPUT(IPQ5332_ETH_50MHZ_CLK, "eth-50mhz", 50000000UL), + { /* Sentinel */ } +}; + static const struct cmn_pll_fixed_output_clk ipq5424_output_clks[] = { CLK_PLL_OUTPUT(IPQ5424_XO_24MHZ_CLK, "xo-24mhz", 24000000UL), CLK_PLL_OUTPUT(IPQ5424_SLEEP_32KHZ_CLK, "sleep-32khz", 32000UL), @@ -199,7 +215,7 @@ static unsigned long clk_cmn_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clk_cmn_pll *cmn_pll = to_clk_cmn_pll(hw); - u32 val, factor; + u32 val, factor, ref_div; /* * The value of CMN_PLL_DIVIDER_CTRL_FACTOR is automatically adjusted @@ -207,8 +223,15 @@ static unsigned long clk_cmn_pll_recalc_rate(struct clk_hw *hw, */ regmap_read(cmn_pll->regmap, CMN_PLL_DIVIDER_CTRL, &val); factor = FIELD_GET(CMN_PLL_DIVIDER_CTRL_FACTOR, val); + if (WARN_ON(factor == 0)) + factor = 1; + + regmap_read(cmn_pll->regmap, CMN_PLL_REFCLK_CONFIG, &val); + ref_div = FIELD_GET(CMN_PLL_REFCLK_DIV, val); + if (WARN_ON(ref_div == 0)) + ref_div = 1; - return parent_rate * 2 * factor; + return div_u64((u64)parent_rate * 2 * factor, ref_div); } static int clk_cmn_pll_determine_rate(struct clk_hw *hw, @@ -461,6 +484,7 @@ static const struct dev_pm_ops ipq_cmn_pll_pm_ops = { static const struct of_device_id ipq_cmn_pll_clk_ids[] = { { .compatible = "qcom,ipq5018-cmn-pll", .data = &ipq5018_output_clks }, + { .compatible = "qcom,ipq5332-cmn-pll", .data = &ipq5332_output_clks }, { .compatible = "qcom,ipq5424-cmn-pll", .data = &ipq5424_output_clks }, { .compatible = "qcom,ipq6018-cmn-pll", .data = &ipq6018_output_clks }, { .compatible = "qcom,ipq8074-cmn-pll", .data = &ipq8074_output_clks }, diff --git a/drivers/clk/qcom/negcc-nord.c b/drivers/clk/qcom/negcc-nord.c index 1aa24e2784e5..2e653ef0fe0e 100644 --- a/drivers/clk/qcom/negcc-nord.c +++ b/drivers/clk/qcom/negcc-nord.c @@ -1918,6 +1918,7 @@ static const struct qcom_reset_map ne_gcc_nord_resets[] = { [NE_GCC_USB3_PHY_SEC_BCR] = { 0x2d000 }, [NE_GCC_USB3PHY_PHY_PRIM_BCR] = { 0x2b004 }, [NE_GCC_USB3PHY_PHY_SEC_BCR] = { 0x2d004 }, + [NE_GCC_QUSB2PHY_PRIM_BCR] = { 0x2e000 }, }; static const struct clk_rcg_dfs_data ne_gcc_nord_dfs_clocks[] = { @@ -1945,7 +1946,7 @@ static void clk_nord_regs_configure(struct device *dev, struct regmap *regmap) qcom_branch_set_force_mem_core(regmap, ne_gcc_ufs_phy_axi_clk, true); } -static struct qcom_cc_driver_data ne_gcc_nord_driver_data = { +static const struct qcom_cc_driver_data ne_gcc_nord_driver_data = { .dfs_rcgs = ne_gcc_nord_dfs_clocks, .num_dfs_rcgs = ARRAY_SIZE(ne_gcc_nord_dfs_clocks), .clk_regs_configure = clk_nord_regs_configure, diff --git a/drivers/clk/qcom/nwgcc-nord.c b/drivers/clk/qcom/nwgcc-nord.c index 163ab63c872b..961cae47ff7c 100644 --- a/drivers/clk/qcom/nwgcc-nord.c +++ b/drivers/clk/qcom/nwgcc-nord.c @@ -626,7 +626,7 @@ static const struct qcom_reset_map nw_gcc_nord_resets[] = { [NW_GCC_VIDEO_BCR] = { 0x1a000 }, }; -static u32 nw_gcc_nord_critical_cbcrs[] = { +static const u32 nw_gcc_nord_critical_cbcrs[] = { 0x16004, /* NW_GCC_CAMERA_AHB_CLK */ 0x16030, /* NW_GCC_CAMERA_XO_CLK */ 0x18004, /* NW_GCC_DISP_0_AHB_CLK */ @@ -641,7 +641,7 @@ static u32 nw_gcc_nord_critical_cbcrs[] = { 0x1a044, /* NW_GCC_VIDEO_XO_CLK */ }; -static struct qcom_cc_driver_data nw_gcc_nord_driver_data = { +static const struct qcom_cc_driver_data nw_gcc_nord_driver_data = { .clk_cbcrs = nw_gcc_nord_critical_cbcrs, .num_clk_cbcrs = ARRAY_SIZE(nw_gcc_nord_critical_cbcrs), }; diff --git a/drivers/clk/qcom/segcc-nord.c b/drivers/clk/qcom/segcc-nord.c index 1aab0999de4d..c82a56d97154 100644 --- a/drivers/clk/qcom/segcc-nord.c +++ b/drivers/clk/qcom/segcc-nord.c @@ -1568,7 +1568,7 @@ static const struct regmap_config se_gcc_nord_regmap_config = { .fast_io = true, }; -static struct qcom_cc_driver_data se_gcc_nord_driver_data = { +static const struct qcom_cc_driver_data se_gcc_nord_driver_data = { .dfs_rcgs = se_gcc_nord_dfs_clocks, .num_dfs_rcgs = ARRAY_SIZE(se_gcc_nord_dfs_clocks), }; diff --git a/drivers/clk/qcom/tcsrcc-hawi.c b/drivers/clk/qcom/tcsrcc-hawi.c new file mode 100644 index 000000000000..c942b0c8e09f --- /dev/null +++ b/drivers/clk/qcom/tcsrcc-hawi.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/clk-provider.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/qcom,hawi-tcsrcc.h> + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "common.h" +#include "reset.h" + +enum { + DT_BI_TCXO_PAD, +}; + +static struct clk_branch tcsr_pcie_0_clkref_en = { + .halt_reg = 0x4c, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x4c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_pcie_0_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_pcie_1_clkref_en = { + .halt_reg = 0x0, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_pcie_1_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_ufs_clkref_en = { + .halt_reg = 0x10, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x10, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_ufs_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_usb2_clkref_en = { + .halt_reg = 0x18, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x18, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_usb2_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch tcsr_usb3_clkref_en = { + .halt_reg = 0x8, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "tcsr_usb3_clkref_en", + .parent_data = &(const struct clk_parent_data){ + .index = DT_BI_TCXO_PAD, + }, + .num_parents = 1, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap *tcsr_cc_hawi_clocks[] = { + [TCSR_PCIE_0_CLKREF_EN] = &tcsr_pcie_0_clkref_en.clkr, + [TCSR_PCIE_1_CLKREF_EN] = &tcsr_pcie_1_clkref_en.clkr, + [TCSR_UFS_CLKREF_EN] = &tcsr_ufs_clkref_en.clkr, + [TCSR_USB2_CLKREF_EN] = &tcsr_usb2_clkref_en.clkr, + [TCSR_USB3_CLKREF_EN] = &tcsr_usb3_clkref_en.clkr, +}; + +static const struct regmap_config tcsr_cc_hawi_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x4c, + .fast_io = true, +}; + +static const struct qcom_cc_desc tcsr_cc_hawi_desc = { + .config = &tcsr_cc_hawi_regmap_config, + .clks = tcsr_cc_hawi_clocks, + .num_clks = ARRAY_SIZE(tcsr_cc_hawi_clocks), +}; + +static const struct of_device_id tcsr_cc_hawi_match_table[] = { + { .compatible = "qcom,hawi-tcsrcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, tcsr_cc_hawi_match_table); + +static int tcsr_cc_hawi_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &tcsr_cc_hawi_desc); +} + +static struct platform_driver tcsr_cc_hawi_driver = { + .probe = tcsr_cc_hawi_probe, + .driver = { + .name = "tcsrcc-hawi", + .of_match_table = tcsr_cc_hawi_match_table, + }, +}; + +module_platform_driver(tcsr_cc_hawi_driver); + +MODULE_DESCRIPTION("QTI TCSRCC HAWI Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/videocc-x1p42100.c b/drivers/clk/qcom/videocc-x1p42100.c new file mode 100644 index 000000000000..2bb40ac6fcc5 --- /dev/null +++ b/drivers/clk/qcom/videocc-x1p42100.c @@ -0,0 +1,585 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/clk-provider.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/qcom,x1p42100-videocc.h> + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "clk-regmap-divider.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_BI_TCXO, +}; + +enum { + P_BI_TCXO, + P_VIDEO_CC_PLL0_OUT_MAIN, + P_VIDEO_CC_PLL1_OUT_MAIN, +}; + +static const struct pll_vco lucid_ole_vco[] = { + { 249600000, 2300000000, 0 }, +}; + +/* 420.0 MHz Configuration */ +static const struct alpha_pll_config video_cc_pll0_config = { + .l = 0x15, + .alpha = 0xe000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x82aa299c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000003, + .test_ctl_hi1_val = 0x00009000, + .test_ctl_hi2_val = 0x00000034, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll video_cc_pll0 = { + .offset = 0x0, + .config = &video_cc_pll0_config, + .vco_table = lucid_ole_vco, + .num_vco = ARRAY_SIZE(lucid_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_pll0", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +/* 1050.0 MHz Configuration */ +static const struct alpha_pll_config video_cc_pll1_config = { + .l = 0x36, + .alpha = 0xb000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x82aa299c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000003, + .test_ctl_hi1_val = 0x00009000, + .test_ctl_hi2_val = 0x00000034, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000005, +}; + +static struct clk_alpha_pll video_cc_pll1 = { + .offset = 0x1000, + .config = &video_cc_pll1_config, + .vco_table = lucid_ole_vco, + .num_vco = ARRAY_SIZE(lucid_ole_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_OLE], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_pll1", + .parent_data = &(const struct clk_parent_data) { + .index = DT_BI_TCXO, + }, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct parent_map video_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, +}; + +static const struct clk_parent_data video_cc_parent_data_0[] = { + { .index = DT_BI_TCXO }, +}; + +static const struct parent_map video_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_VIDEO_CC_PLL0_OUT_MAIN, 1 }, +}; + +static const struct clk_parent_data video_cc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &video_cc_pll0.clkr.hw }, +}; + +static const struct parent_map video_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_VIDEO_CC_PLL1_OUT_MAIN, 1 }, +}; + +static const struct clk_parent_data video_cc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .hw = &video_cc_pll1.clkr.hw }, +}; + +static const struct freq_tbl ftbl_video_cc_mvs0_bse_clk_src[] = { + F(420000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + F(600000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + F(670000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + F(848000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + F(920000000, P_VIDEO_CC_PLL0_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 video_cc_mvs0_bse_clk_src = { + .cmd_rcgr = 0x8154, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_1, + .freq_tbl = ftbl_video_cc_mvs0_bse_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_bse_clk_src", + .parent_data = video_cc_parent_data_1, + .num_parents = ARRAY_SIZE(video_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_video_cc_mvs0_clk_src[] = { + F(210000000, P_VIDEO_CC_PLL0_OUT_MAIN, 2, 0, 0), + F(300000000, P_VIDEO_CC_PLL0_OUT_MAIN, 2, 0, 0), + F(335000000, P_VIDEO_CC_PLL0_OUT_MAIN, 2, 0, 0), + F(424000000, P_VIDEO_CC_PLL0_OUT_MAIN, 2, 0, 0), + F(460000000, P_VIDEO_CC_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 video_cc_mvs0_clk_src = { + .cmd_rcgr = 0x8000, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_1, + .freq_tbl = ftbl_video_cc_mvs0_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_clk_src", + .parent_data = video_cc_parent_data_1, + .num_parents = ARRAY_SIZE(video_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_video_cc_mvs1_clk_src[] = { + F(1050000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), + F(1350000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), + F(1650000000, P_VIDEO_CC_PLL1_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 video_cc_mvs1_clk_src = { + .cmd_rcgr = 0x8018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_2, + .freq_tbl = ftbl_video_cc_mvs1_clk_src, + .hw_clk_ctrl = true, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1_clk_src", + .parent_data = video_cc_parent_data_2, + .num_parents = ARRAY_SIZE(video_cc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static const struct freq_tbl ftbl_video_cc_xo_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 video_cc_xo_clk_src = { + .cmd_rcgr = 0x810c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = video_cc_parent_map_0, + .freq_tbl = ftbl_video_cc_xo_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_xo_clk_src", + .parent_data = video_cc_parent_data_0, + .num_parents = ARRAY_SIZE(video_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_shared_ops, + }, +}; + +static struct clk_regmap_div video_cc_mvs0_bse_div4_div_clk_src = { + .reg = 0x817c, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_bse_div4_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs0_bse_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div video_cc_mvs1_div_clk_src = { + .reg = 0x80ec, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div video_cc_mvs1c_div2_div_clk_src = { + .reg = 0x809c, + .shift = 0, + .width = 4, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1c_div2_div_clk_src", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch video_cc_mvs0_bse_clk = { + .halt_reg = 0x8170, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8170, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_bse_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs0_bse_div4_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs0_clk = { + .halt_reg = 0x80b8, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x80b8, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x80b8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs0_shift_clk = { + .halt_reg = 0x8128, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x8128, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0_shift_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs0c_clk = { + .halt_reg = 0x8064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8064, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0c_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs0c_shift_clk = { + .halt_reg = 0x812c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x812c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs0c_shift_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs1_clk = { + .halt_reg = 0x80e0, + .halt_check = BRANCH_HALT_VOTED, + .hwcg_reg = 0x80e0, + .hwcg_bit = 1, + .clkr = { + .enable_reg = 0x80e0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs1_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs1_shift_clk = { + .halt_reg = 0x8130, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x8130, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1_shift_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs1c_clk = { + .halt_reg = 0x8090, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8090, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1c_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_mvs1c_div2_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch video_cc_mvs1c_shift_clk = { + .halt_reg = 0x8134, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x8134, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "video_cc_mvs1c_shift_clk", + .parent_hws = (const struct clk_hw*[]) { + &video_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc video_cc_mvs0c_gdsc = { + .gdscr = 0x804c, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x6, + .pd = { + .name = "video_cc_mvs0c_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc video_cc_mvs0_gdsc = { + .gdscr = 0x80a4, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0x6, + .pd = { + .name = "video_cc_mvs0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &video_cc_mvs0c_gdsc.pd, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc video_cc_mvs1c_gdsc = { + .gdscr = 0x8078, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "video_cc_mvs1c_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct gdsc video_cc_mvs1_gdsc = { + .gdscr = 0x80cc, + .en_rest_wait_val = 0x2, + .en_few_wait_val = 0x2, + .clk_dis_wait_val = 0xf, + .pd = { + .name = "video_cc_mvs1_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .parent = &video_cc_mvs1c_gdsc.pd, + .flags = HW_CTRL_TRIGGER | POLL_CFG_GDSCR | RETAIN_FF_ENABLE, +}; + +static struct clk_regmap *video_cc_x1p42100_clocks[] = { + [VIDEO_CC_MVS0_BSE_CLK] = &video_cc_mvs0_bse_clk.clkr, + [VIDEO_CC_MVS0_BSE_CLK_SRC] = &video_cc_mvs0_bse_clk_src.clkr, + [VIDEO_CC_MVS0_BSE_DIV4_DIV_CLK_SRC] = &video_cc_mvs0_bse_div4_div_clk_src.clkr, + [VIDEO_CC_MVS0_CLK] = &video_cc_mvs0_clk.clkr, + [VIDEO_CC_MVS0_CLK_SRC] = &video_cc_mvs0_clk_src.clkr, + [VIDEO_CC_MVS0_SHIFT_CLK] = &video_cc_mvs0_shift_clk.clkr, + [VIDEO_CC_MVS0C_CLK] = &video_cc_mvs0c_clk.clkr, + [VIDEO_CC_MVS0C_SHIFT_CLK] = &video_cc_mvs0c_shift_clk.clkr, + [VIDEO_CC_MVS1_CLK] = &video_cc_mvs1_clk.clkr, + [VIDEO_CC_MVS1_CLK_SRC] = &video_cc_mvs1_clk_src.clkr, + [VIDEO_CC_MVS1_DIV_CLK_SRC] = &video_cc_mvs1_div_clk_src.clkr, + [VIDEO_CC_MVS1_SHIFT_CLK] = &video_cc_mvs1_shift_clk.clkr, + [VIDEO_CC_MVS1C_CLK] = &video_cc_mvs1c_clk.clkr, + [VIDEO_CC_MVS1C_DIV2_DIV_CLK_SRC] = &video_cc_mvs1c_div2_div_clk_src.clkr, + [VIDEO_CC_MVS1C_SHIFT_CLK] = &video_cc_mvs1c_shift_clk.clkr, + [VIDEO_CC_PLL0] = &video_cc_pll0.clkr, + [VIDEO_CC_PLL1] = &video_cc_pll1.clkr, + [VIDEO_CC_XO_CLK_SRC] = &video_cc_xo_clk_src.clkr, +}; + +static struct gdsc *video_cc_x1p42100_gdscs[] = { + [VIDEO_CC_MVS0_GDSC] = &video_cc_mvs0_gdsc, + [VIDEO_CC_MVS0C_GDSC] = &video_cc_mvs0c_gdsc, + [VIDEO_CC_MVS1_GDSC] = &video_cc_mvs1_gdsc, + [VIDEO_CC_MVS1C_GDSC] = &video_cc_mvs1c_gdsc, +}; + +static const struct qcom_reset_map video_cc_x1p42100_resets[] = { + [CVP_VIDEO_CC_INTERFACE_BCR] = { 0x80f0 }, + [CVP_VIDEO_CC_MVS0_BCR] = { 0x80a0 }, + [CVP_VIDEO_CC_MVS0C_BCR] = { 0x8048 }, + [CVP_VIDEO_CC_MVS1_BCR] = { 0x80c8 }, + [CVP_VIDEO_CC_MVS1C_BCR] = { 0x8074 }, + [VIDEO_CC_MVS0_BSE_BCR] = { 0x816c }, + [VIDEO_CC_MVS0C_CLK_ARES] = { 0x8064, 2 }, + [VIDEO_CC_MVS1C_CLK_ARES] = { 0x8090, 2 }, + [VIDEO_CC_XO_CLK_ARES] = { 0x8124, 2 }, +}; + +static struct clk_alpha_pll *video_cc_x1p42100_plls[] = { + &video_cc_pll0, + &video_cc_pll1, +}; + +static u32 video_cc_x1p42100_critical_cbcrs[] = { + 0x80f4, /* VIDEO_CC_AHB_CLK */ + 0x8150, /* VIDEO_CC_SLEEP_CLK */ + 0x8124, /* VIDEO_CC_XO_CLK */ +}; + +static const struct regmap_config video_cc_x1p42100_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x9f54, + .fast_io = true, +}; + +static struct qcom_cc_driver_data video_cc_x1p42100_driver_data = { + .alpha_plls = video_cc_x1p42100_plls, + .num_alpha_plls = ARRAY_SIZE(video_cc_x1p42100_plls), + .clk_cbcrs = video_cc_x1p42100_critical_cbcrs, + .num_clk_cbcrs = ARRAY_SIZE(video_cc_x1p42100_critical_cbcrs), +}; + +static const struct qcom_cc_desc video_cc_x1p42100_desc = { + .config = &video_cc_x1p42100_regmap_config, + .clks = video_cc_x1p42100_clocks, + .num_clks = ARRAY_SIZE(video_cc_x1p42100_clocks), + .resets = video_cc_x1p42100_resets, + .num_resets = ARRAY_SIZE(video_cc_x1p42100_resets), + .gdscs = video_cc_x1p42100_gdscs, + .num_gdscs = ARRAY_SIZE(video_cc_x1p42100_gdscs), + .use_rpm = true, + .driver_data = &video_cc_x1p42100_driver_data, +}; + +static const struct of_device_id video_cc_x1p42100_match_table[] = { + { .compatible = "qcom,x1p42100-videocc" }, + { } +}; +MODULE_DEVICE_TABLE(of, video_cc_x1p42100_match_table); + +static int video_cc_x1p42100_probe(struct platform_device *pdev) +{ + return qcom_cc_probe(pdev, &video_cc_x1p42100_desc); +} + +static struct platform_driver video_cc_x1p42100_driver = { + .probe = video_cc_x1p42100_probe, + .driver = { + .name = "videocc-x1p42100", + .of_match_table = video_cc_x1p42100_match_table, + }, +}; + +module_platform_driver(video_cc_x1p42100_driver); + +MODULE_DESCRIPTION("QTI VIDEOCC X1P42100 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/renesas/clk-r8a73a4.c b/drivers/clk/renesas/clk-r8a73a4.c index 7f47644396e6..1a2bbc5e9299 100644 --- a/drivers/clk/renesas/clk-r8a73a4.c +++ b/drivers/clk/renesas/clk-r8a73a4.c @@ -42,6 +42,8 @@ static struct div4_clk div4_clks[] = { { "b", CPG_FRQCRA, 8 }, { "m1", CPG_FRQCRA, 4 }, { "m2", CPG_FRQCRA, 0 }, + { "ztr", CPG_FRQCRB, 20 }, + { "zt", CPG_FRQCRB, 16 }, { "zx", CPG_FRQCRB, 12 }, { "zs", CPG_FRQCRB, 8 }, { "hp", CPG_FRQCRB, 4 }, diff --git a/drivers/clk/renesas/clk-r8a7740.c b/drivers/clk/renesas/clk-r8a7740.c index 635d59ead499..31a79674583e 100644 --- a/drivers/clk/renesas/clk-r8a7740.c +++ b/drivers/clk/renesas/clk-r8a7740.c @@ -37,6 +37,8 @@ static struct div4_clk div4_clks[] = { { "zg", CPG_FRQCRA, 16 }, { "b", CPG_FRQCRA, 8 }, { "m1", CPG_FRQCRA, 4 }, + { "ztr", CPG_FRQCRB, 20 }, + { "zt", CPG_FRQCRB, 16 }, { "hp", CPG_FRQCRB, 4 }, { "hpp", CPG_FRQCRC, 20 }, { "usbp", CPG_FRQCRC, 16 }, diff --git a/drivers/clk/renesas/r8a779g0-cpg-mssr.c b/drivers/clk/renesas/r8a779g0-cpg-mssr.c index 015b9773cc55..54ba76ff5ab0 100644 --- a/drivers/clk/renesas/r8a779g0-cpg-mssr.c +++ b/drivers/clk/renesas/r8a779g0-cpg-mssr.c @@ -245,6 +245,7 @@ static const struct mssr_mod_clk r8a779g0_mod_clks[] __initconst = { DEF_MOD("fcpvx0", 1100, R8A779G0_CLK_S0D1_VIO), DEF_MOD("fcpvx1", 1101, R8A779G0_CLK_S0D1_VIO), DEF_MOD("tsn", 2723, R8A779G0_CLK_S0D4_HSC), + DEF_MOD("dsc", 2819, R8A779G0_CLK_VIOBUSD2), DEF_MOD("ssiu", 2926, R8A779G0_CLK_S0D6_PER), DEF_MOD("ssi", 2927, R8A779G0_CLK_S0D6_PER), }; diff --git a/drivers/clk/renesas/r9a07g043-cpg.c b/drivers/clk/renesas/r9a07g043-cpg.c index 70944ef8c5b8..59d6ee2e888f 100644 --- a/drivers/clk/renesas/r9a07g043-cpg.c +++ b/drivers/clk/renesas/r9a07g043-cpg.c @@ -103,7 +103,7 @@ static const struct cpg_core_clk r9a07g043_core_clks[] __initconst = { /* Internal Core Clocks */ DEF_FIXED(".osc", R9A07G043_OSCCLK, CLK_EXTAL, 1, 1), DEF_FIXED(".osc_div1000", CLK_OSC_DIV1000, CLK_EXTAL, 1, 1000), - DEF_SAMPLL(".pll1", CLK_PLL1, CLK_EXTAL, PLL146_CONF(0)), + DEF_SAMPLL(".pll1", CLK_PLL1, CLK_EXTAL, CPG_SAM_PLL_CONF(0)), DEF_FIXED(".pll2", CLK_PLL2, CLK_EXTAL, 200, 3), DEF_FIXED(".pll2_div2", CLK_PLL2_DIV2, CLK_PLL2, 1, 2), DEF_FIXED(".clk_800", CLK_PLL2_800, CLK_PLL2, 1, 2), diff --git a/drivers/clk/renesas/r9a07g044-cpg.c b/drivers/clk/renesas/r9a07g044-cpg.c index 2d3487203bf5..913cca6dd46f 100644 --- a/drivers/clk/renesas/r9a07g044-cpg.c +++ b/drivers/clk/renesas/r9a07g044-cpg.c @@ -159,7 +159,7 @@ static const struct { /* Internal Core Clocks */ DEF_FIXED(".osc", R9A07G044_OSCCLK, CLK_EXTAL, 1, 1), DEF_FIXED(".osc_div1000", CLK_OSC_DIV1000, CLK_EXTAL, 1, 1000), - DEF_SAMPLL(".pll1", CLK_PLL1, CLK_EXTAL, PLL146_CONF(0)), + DEF_SAMPLL(".pll1", CLK_PLL1, CLK_EXTAL, CPG_SAM_PLL_CONF(0)), DEF_FIXED(".pll2", CLK_PLL2, CLK_EXTAL, 200, 3), DEF_FIXED(".pll2_533", CLK_PLL2_533, CLK_PLL2, 1, 3), DEF_FIXED(".pll3", CLK_PLL3, CLK_EXTAL, 200, 3), diff --git a/drivers/clk/renesas/r9a08g045-cpg.c b/drivers/clk/renesas/r9a08g045-cpg.c index 1232fec913eb..624fc5e6fb24 100644 --- a/drivers/clk/renesas/r9a08g045-cpg.c +++ b/drivers/clk/renesas/r9a08g045-cpg.c @@ -9,7 +9,6 @@ #include <linux/device.h> #include <linux/init.h> #include <linux/kernel.h> -#include <linux/pm_domain.h> #include <dt-bindings/clock/r9a08g045-cpg.h> @@ -50,16 +49,6 @@ #define G3S_SEL_SDHI1 SEL_PLL_PACK(G3S_CPG_SDHI_DSEL, 4, 2) #define G3S_SEL_SDHI2 SEL_PLL_PACK(G3S_CPG_SDHI_DSEL, 8, 2) -/* PLL 1/4/6 configuration registers macro. */ -#define G3S_PLL146_CONF(clk1, clk2, setting) ((clk1) << 22 | (clk2) << 12 | (setting)) - -#define DEF_G3S_MUX(_name, _id, _conf, _parent_names, _mux_flags, _clk_flags) \ - DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = (_conf), \ - .parent_names = (_parent_names), \ - .num_parents = ARRAY_SIZE((_parent_names)), \ - .mux_flags = CLK_MUX_HIWORD_MASK | (_mux_flags), \ - .flag = (_clk_flags)) - enum clk_ids { /* Core Clock Outputs exported to DT */ LAST_DT_CORE_CLK = R9A08G045_SWD, @@ -134,7 +123,7 @@ static const struct cpg_core_clk r9a08g045_core_clks[] __initconst = { /* Internal Core Clocks */ DEF_FIXED(".osc_div1000", CLK_OSC_DIV1000, CLK_EXTAL, 1, 1000), - DEF_G3S_PLL(".pll1", CLK_PLL1, CLK_EXTAL, G3S_PLL146_CONF(0x4, 0x8, 0x100), + DEF_G3S_PLL(".pll1", CLK_PLL1, CLK_EXTAL, CPG_PLL_CONF(0, 0x100), 1100000000UL), DEF_FIXED(".pll2", CLK_PLL2, CLK_EXTAL, 200, 3), DEF_FIXED(".pll3", CLK_PLL3, CLK_EXTAL, 200, 3), diff --git a/drivers/clk/renesas/r9a08g046-cpg.c b/drivers/clk/renesas/r9a08g046-cpg.c index 6759957980f2..a57638734ce7 100644 --- a/drivers/clk/renesas/r9a08g046-cpg.c +++ b/drivers/clk/renesas/r9a08g046-cpg.c @@ -17,17 +17,69 @@ /* RZ/G3L Specific registers. */ #define G3L_CPG_PL2_DDIV (0x204) #define G3L_CPG_PL3_DDIV (0x208) +#define G3L_CPG_CA55CORE_DDIV (0x234) +#define G3L_CPG_RSCI_DDIV (0x238) +#define G3L_CPG_RSPI_DDIV (0x23c) #define G3L_CLKDIVSTATUS (0x280) +#define G3L_CPG_ETH_SSEL (0x410) +#define G3L_CPG_RSCI_SSEL (0x414) +#define G3L_CPG_RSPI_SSEL (0x418) +#define G3L_CPG_ETH_SDIV (0x434) /* RZ/G3L Specific division configuration. */ #define G3L_DIVPL2A DDIV_PACK(G3L_CPG_PL2_DDIV, 0, 2) #define G3L_DIVPL2B DDIV_PACK(G3L_CPG_PL2_DDIV, 4, 2) #define G3L_DIVPL3A DDIV_PACK(G3L_CPG_PL3_DDIV, 0, 2) +#define G3L_DIV_CA55_CORE0 DDIV_PACK(G3L_CPG_CA55CORE_DDIV, 0, 3) +#define G3L_DIV_CA55_CORE1 DDIV_PACK(G3L_CPG_CA55CORE_DDIV, 4, 3) +#define G3L_DIV_CA55_CORE2 DDIV_PACK(G3L_CPG_CA55CORE_DDIV, 8, 3) +#define G3L_DIV_CA55_CORE3 DDIV_PACK(G3L_CPG_CA55CORE_DDIV, 12, 3) +#define G3L_DIV_RSCI0 DDIV_PACK(G3L_CPG_RSCI_DDIV, 0, 2) +#define G3L_DIV_RSCI1 DDIV_PACK(G3L_CPG_RSCI_DDIV, 2, 2) +#define G3L_DIV_RSCI2 DDIV_PACK(G3L_CPG_RSCI_DDIV, 4, 2) +#define G3L_DIV_RSCI3 DDIV_PACK(G3L_CPG_RSCI_DDIV, 6, 2) +#define G3L_DIV_RSPI0 DDIV_PACK(G3L_CPG_RSPI_DDIV, 0, 2) +#define G3L_DIV_RSPI1 DDIV_PACK(G3L_CPG_RSPI_DDIV, 2, 2) +#define G3L_DIV_RSPI2 DDIV_PACK(G3L_CPG_RSPI_DDIV, 4, 2) +#define G3L_SDIV_ETH_A DDIV_PACK(G3L_CPG_ETH_SDIV, 0, 2) +#define G3L_SDIV_ETH_B DDIV_PACK(G3L_CPG_ETH_SDIV, 4, 1) +#define G3L_SDIV_ETH_C DDIV_PACK(G3L_CPG_ETH_SDIV, 8, 2) +#define G3L_SDIV_ETH_D DDIV_PACK(G3L_CPG_ETH_SDIV, 12, 1) /* RZ/G3L Clock status configuration. */ #define G3L_DIVPL2A_STS DDIV_PACK(G3L_CLKDIVSTATUS, 4, 1) #define G3L_DIVPL2B_STS DDIV_PACK(G3L_CLKDIVSTATUS, 5, 1) #define G3L_DIVPL3A_STS DDIV_PACK(G3L_CLKDIVSTATUS, 8, 1) +#define G3L_DIV_CA55_CORE0_STS DDIV_PACK(G3L_CLKDIVSTATUS, 12, 1) +#define G3L_DIV_CA55_CORE1_STS DDIV_PACK(G3L_CLKDIVSTATUS, 13, 1) +#define G3L_DIV_CA55_CORE2_STS DDIV_PACK(G3L_CLKDIVSTATUS, 14, 1) +#define G3L_DIV_CA55_CORE3_STS DDIV_PACK(G3L_CLKDIVSTATUS, 15, 1) +#define G3L_DIV_RSCI0_STS DDIV_PACK(G3L_CLKDIVSTATUS, 16, 1) +#define G3L_DIV_RSCI1_STS DDIV_PACK(G3L_CLKDIVSTATUS, 17, 1) +#define G3L_DIV_RSCI2_STS DDIV_PACK(G3L_CLKDIVSTATUS, 18, 1) +#define G3L_DIV_RSCI3_STS DDIV_PACK(G3L_CLKDIVSTATUS, 19, 1) +#define G3L_DIV_RSPI0_STS DDIV_PACK(G3L_CLKDIVSTATUS, 20, 1) +#define G3L_DIV_RSPI1_STS DDIV_PACK(G3L_CLKDIVSTATUS, 21, 1) +#define G3L_DIV_RSPI2_STS DDIV_PACK(G3L_CLKDIVSTATUS, 22, 1) + +/* RZ/G3L Specific clocks select. */ +#define G3L_SEL_ETH0_TX SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 0, 1) +#define G3L_SEL_ETH0_RX SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 1, 1) +#define G3L_SEL_ETH0_RM SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 2, 1) +#define G3L_SEL_ETH0_CLK_TX_I SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 3, 1) +#define G3L_SEL_ETH0_CLK_RX_I SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 4, 1) +#define G3L_SEL_ETH1_TX SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 8, 1) +#define G3L_SEL_ETH1_RX SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 9, 1) +#define G3L_SEL_ETH1_RM SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 10, 1) +#define G3L_SEL_ETH1_CLK_TX_I SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 11, 1) +#define G3L_SEL_ETH1_CLK_RX_I SEL_PLL_PACK(G3L_CPG_ETH_SSEL, 12, 1) +#define G3L_SEL_RSCI0 SEL_PLL_PACK(G3L_CPG_RSCI_SSEL, 0, 2) +#define G3L_SEL_RSCI1 SEL_PLL_PACK(G3L_CPG_RSCI_SSEL, 2, 2) +#define G3L_SEL_RSCI2 SEL_PLL_PACK(G3L_CPG_RSCI_SSEL, 4, 2) +#define G3L_SEL_RSCI3 SEL_PLL_PACK(G3L_CPG_RSCI_SSEL, 6, 2) +#define G3L_SEL_RSPI0 SEL_PLL_PACK(G3L_CPG_RSPI_SSEL, 0, 2) +#define G3L_SEL_RSPI1 SEL_PLL_PACK(G3L_CPG_RSPI_SSEL, 2, 2) +#define G3L_SEL_RSPI2 SEL_PLL_PACK(G3L_CPG_RSPI_SSEL, 4, 2) enum clk_ids { /* Core Clock Outputs exported to DT */ @@ -41,16 +93,72 @@ enum clk_ids { CLK_ETH1_RXC_RX_CLK_IN, /* Internal Core Clocks */ + CLK_PLL1, CLK_PLL2, CLK_PLL2_DIV2, + CLK_PLL2_DIV2_4, + CLK_PLL2_DIV5, + CLK_PLL2_DIV6, + CLK_PLL2_DIV7, CLK_PLL3, CLK_PLL3_DIV2, + CLK_PLL6, + CLK_PLL6_DIV10, + CLK_SEL_ETH0_TX, + CLK_SEL_ETH0_RX, + CLK_SEL_ETH0_RM, + CLK_SEL_ETH1_TX, + CLK_SEL_ETH1_RX, + CLK_SEL_ETH1_RM, + CLK_SEL_RSCI0, + CLK_SEL_RSCI1, + CLK_SEL_RSCI2, + CLK_SEL_RSCI3, + CLK_SEL_RSPI0, + CLK_SEL_RSPI1, + CLK_SEL_RSPI2, + CLK_ETH0_TR, + CLK_ETH0_RM, + CLK_ETH1_TR, + CLK_ETH1_RM, /* Module Clocks */ MOD_CLK_BASE, }; /* Divider tables */ +static const struct clk_div_table dtable_1_8[] = { + { 0, 1 }, + { 1, 2 }, + { 2, 4 }, + { 3, 8 }, + { 0, 0 }, +}; + +static const struct clk_div_table dtable_1_32[] = { + { 0, 1 }, + { 1, 2 }, + { 2, 4 }, + { 3, 8 }, + { 4, 16 }, + { 5, 32 }, + { 0, 0 }, +}; + +static const struct clk_div_table dtable_2_20[] = { + { 0, 2 }, + { 1, 20 }, + { 0, 0 }, +}; + +static const struct clk_div_table dtable_2_16[] = { + { 0, 2 }, + { 1, 4 }, + { 2, 8 }, + { 3, 16 }, + { 0, 0 }, +}; + static const struct clk_div_table dtable_4_128[] = { { 0, 4 }, { 1, 8 }, @@ -59,6 +167,13 @@ static const struct clk_div_table dtable_4_128[] = { { 0, 0 }, }; +static const struct clk_div_table dtable_4_200[] = { + { 0, 4 }, + { 1, 20 }, + { 2, 200 }, + { 0, 0 }, +}; + static const struct clk_div_table dtable_8_256[] = { { 0, 8 }, { 1, 16 }, @@ -67,6 +182,19 @@ static const struct clk_div_table dtable_8_256[] = { { 0, 0 }, }; +/* Mux clock names tables. */ +static const char * const sel_eth0_tx[] = { ".div_eth0_tr", "eth0_txc_tx_clk" }; +static const char * const sel_eth0_rx[] = { ".div_eth0_tr", "eth0_rxc_rx_clk" }; +static const char * const sel_eth0_rm[] = { ".pll6_div10", "eth0_rxc_rx_clk" }; +static const char * const sel_eth1_tx[] = { ".div_eth1_tr", "eth1_txc_tx_clk" }; +static const char * const sel_eth1_rx[] = { ".div_eth1_tr", "eth1_rxc_rx_clk" }; +static const char * const sel_eth1_rm[] = { ".pll6_div10", "eth1_rxc_rx_clk" }; +static const char * const sel_rsci_rspi[] = { ".pll2_div5", ".pll2_div6", ".pll2_div7", ".pll2_div2_4" }; +static const char * const sel_eth0_clk_tx_i[] = { ".sel_eth0_tx", ".div_eth0_rm" }; +static const char * const sel_eth0_clk_rx_i[] = { ".sel_eth0_rx", ".div_eth0_rm" }; +static const char * const sel_eth1_clk_tx_i[] = { ".sel_eth1_tx", ".div_eth1_rm" }; +static const char * const sel_eth1_clk_rx_i[] = { ".sel_eth1_rx", ".div_eth1_rm" }; + static const struct cpg_core_clk r9a08g046_core_clks[] __initconst = { /* External Clock Inputs */ DEF_INPUT("extal", CLK_EXTAL), @@ -76,18 +204,82 @@ static const struct cpg_core_clk r9a08g046_core_clks[] __initconst = { DEF_INPUT("eth1_rxc_rx_clk", CLK_ETH1_RXC_RX_CLK_IN), /* Internal Core Clocks */ + DEF_G3L_PLL(".pll1", CLK_PLL1, CLK_EXTAL, CPG_PLL_CONF(0, 0x100), + 1200000000UL), DEF_FIXED(".pll2", CLK_PLL2, CLK_EXTAL, 200, 3), DEF_FIXED(".pll3", CLK_PLL3, CLK_EXTAL, 200, 3), + DEF_G3L_PLL(".pll6", CLK_PLL6, CLK_EXTAL, CPG_PLL_CONF(0x50, 0), + 500000000UL), DEF_FIXED(".pll2_div2", CLK_PLL2_DIV2, CLK_PLL2, 1, 2), + DEF_FIXED(".pll2_div2_4", CLK_PLL2_DIV2_4, CLK_PLL2_DIV2, 1, 4), + DEF_FIXED(".pll2_div5", CLK_PLL2_DIV5, CLK_PLL2, 1, 5), + DEF_FIXED(".pll2_div6", CLK_PLL2_DIV6, CLK_PLL2, 1, 6), + DEF_FIXED(".pll2_div7", CLK_PLL2_DIV7, CLK_PLL2, 1, 7), DEF_FIXED(".pll3_div2", CLK_PLL3_DIV2, CLK_PLL3, 1, 2), + DEF_FIXED(".pll6_div10", CLK_PLL6_DIV10, CLK_PLL6, 1, 10), + DEF_MUX(".sel_rsci0", CLK_SEL_RSCI0, G3L_SEL_RSCI0, sel_rsci_rspi), + DEF_MUX(".sel_rsci1", CLK_SEL_RSCI1, G3L_SEL_RSCI1, sel_rsci_rspi), + DEF_MUX(".sel_rsci2", CLK_SEL_RSCI2, G3L_SEL_RSCI2, sel_rsci_rspi), + DEF_MUX(".sel_rsci3", CLK_SEL_RSCI3, G3L_SEL_RSCI3, sel_rsci_rspi), + DEF_MUX(".sel_rspi0", CLK_SEL_RSPI0, G3L_SEL_RSPI0, sel_rsci_rspi), + DEF_MUX(".sel_rspi1", CLK_SEL_RSPI1, G3L_SEL_RSPI1, sel_rsci_rspi), + DEF_MUX(".sel_rspi2", CLK_SEL_RSPI2, G3L_SEL_RSPI2, sel_rsci_rspi), + DEF_MUX(".sel_eth0_tx", CLK_SEL_ETH0_TX, G3L_SEL_ETH0_TX, sel_eth0_tx), + DEF_MUX(".sel_eth0_rx", CLK_SEL_ETH0_RX, G3L_SEL_ETH0_RX, sel_eth0_rx), + DEF_MUX(".sel_eth0_rm", CLK_SEL_ETH0_RM, G3L_SEL_ETH0_RM, sel_eth0_rm), + DEF_MUX(".sel_eth1_tx", CLK_SEL_ETH1_TX, G3L_SEL_ETH1_TX, sel_eth1_tx), + DEF_MUX(".sel_eth1_rx", CLK_SEL_ETH1_RX, G3L_SEL_ETH1_RX, sel_eth1_rx), + DEF_MUX(".sel_eth1_rm", CLK_SEL_ETH1_RM, G3L_SEL_ETH1_RM, sel_eth1_rm), + DEF_DIV(".div_eth0_tr", CLK_ETH0_TR, CLK_PLL6, G3L_SDIV_ETH_A, dtable_4_200), + DEF_DIV(".div_eth1_tr", CLK_ETH1_TR, CLK_PLL6, G3L_SDIV_ETH_C, dtable_4_200), + DEF_DIV(".div_eth0_rm", CLK_ETH0_RM, CLK_SEL_ETH0_RM, G3L_SDIV_ETH_B, dtable_2_20), + DEF_DIV(".div_eth1_rm", CLK_ETH1_RM, CLK_SEL_ETH1_RM, G3L_SDIV_ETH_D, dtable_2_20), /* Core output clk */ + DEF_G3S_DIV("IC0", R9A08G046_CLK_IC0, CLK_PLL1, G3L_DIV_CA55_CORE0, G3L_DIV_CA55_CORE0_STS, + dtable_1_32, 0, 0, 0, NULL), + DEF_G3S_DIV("IC1", R9A08G046_CLK_IC1, CLK_PLL1, G3L_DIV_CA55_CORE1, G3L_DIV_CA55_CORE1_STS, + dtable_1_32, 0, 0, 0, NULL), + DEF_G3S_DIV("IC2", R9A08G046_CLK_IC2, CLK_PLL1, G3L_DIV_CA55_CORE2, G3L_DIV_CA55_CORE2_STS, + dtable_1_32, 0, 0, 0, NULL), + DEF_G3S_DIV("IC3", R9A08G046_CLK_IC3, CLK_PLL1, G3L_DIV_CA55_CORE3, G3L_DIV_CA55_CORE3_STS, + dtable_1_32, 0, 0, 0, NULL), DEF_G3S_DIV("P0", R9A08G046_CLK_P0, CLK_PLL2_DIV2, G3L_DIVPL2B, G3L_DIVPL2B_STS, dtable_8_256, 0, 0, 0, NULL), DEF_G3S_DIV("P1", R9A08G046_CLK_P1, CLK_PLL3_DIV2, G3L_DIVPL3A, G3L_DIVPL3A_STS, dtable_4_128, 0, 0, 0, NULL), DEF_G3S_DIV("P3", R9A08G046_CLK_P3, CLK_PLL2_DIV2, G3L_DIVPL2A, G3L_DIVPL2A_STS, dtable_4_128, 0, 0, 0, NULL), + DEF_G3S_DIV("P13", R9A08G046_CLK_P13, CLK_SEL_RSCI0, G3L_DIV_RSCI0, G3L_DIV_RSCI0_STS, + dtable_2_16, 0, 100000000UL, 0, NULL), + DEF_G3S_DIV("P14", R9A08G046_CLK_P14, CLK_SEL_RSCI1, G3L_DIV_RSCI1, G3L_DIV_RSCI1_STS, + dtable_2_16, 0, 100000000UL, 0, NULL), + DEF_G3S_DIV("P15", R9A08G046_CLK_P15, CLK_SEL_RSCI2, G3L_DIV_RSCI2, G3L_DIV_RSCI2_STS, + dtable_2_16, 0, 100000000UL, 0, NULL), + DEF_G3S_DIV("P16", R9A08G046_CLK_P16, CLK_SEL_RSCI3, G3L_DIV_RSCI3, G3L_DIV_RSCI3_STS, + dtable_2_16, 0, 100000000UL, 0, NULL), + DEF_G3S_DIV("P17", R9A08G046_CLK_P17, CLK_SEL_RSPI0, G3L_DIV_RSPI0, G3L_DIV_RSPI0_STS, + dtable_1_8, 0, 200000000UL, 0, NULL), + DEF_G3S_DIV("P18", R9A08G046_CLK_P18, CLK_SEL_RSPI1, G3L_DIV_RSPI1, G3L_DIV_RSPI1_STS, + dtable_1_8, 0, 200000000UL, 0, NULL), + DEF_G3S_DIV("P19", R9A08G046_CLK_P19, CLK_SEL_RSPI2, G3L_DIV_RSPI2, G3L_DIV_RSPI2_STS, + dtable_1_8, 0, 200000000UL, 0, NULL), + DEF_FIXED("HP", R9A08G046_CLK_HP, CLK_PLL6_DIV10, 1, 1), + DEF_MUX_FLAGS("ETHTX01", R9A08G046_CLK_ETHTX01, G3L_SEL_ETH0_CLK_TX_I, sel_eth0_clk_tx_i, + CLK_SET_RATE_PARENT), + DEF_MUX_FLAGS("ETHRX01", R9A08G046_CLK_ETHRX01, G3L_SEL_ETH0_CLK_RX_I, sel_eth0_clk_rx_i, + CLK_SET_RATE_PARENT), + DEF_MUX_FLAGS("ETHTX11", R9A08G046_CLK_ETHTX11, G3L_SEL_ETH1_CLK_TX_I, sel_eth1_clk_tx_i, + CLK_SET_RATE_PARENT), + DEF_MUX_FLAGS("ETHRX11", R9A08G046_CLK_ETHRX11, G3L_SEL_ETH1_CLK_RX_I, sel_eth1_clk_rx_i, + CLK_SET_RATE_PARENT), + DEF_FIXED("ETHRM0", R9A08G046_CLK_ETHRM0, CLK_SEL_ETH0_RM, 1, 1), + DEF_FIXED("ETHTX02", R9A08G046_CLK_ETHTX02, CLK_SEL_ETH0_TX, 1, 1), + DEF_FIXED("ETHRX02", R9A08G046_CLK_ETHRX02, CLK_SEL_ETH0_RX, 1, 1), + DEF_FIXED("ETHRM1", R9A08G046_CLK_ETHRM1, CLK_SEL_ETH1_RM, 1, 1), + DEF_FIXED("ETHTX12", R9A08G046_CLK_ETHTX12, CLK_SEL_ETH1_TX, 1, 1), + DEF_FIXED("ETHRX12", R9A08G046_CLK_ETHRX12, CLK_SEL_ETH1_RX, 1, 1), + DEF_FIXED("OSCCLK", R9A08G046_OSCCLK, CLK_EXTAL, 1, 1), }; static const struct rzg2l_mod_clk r9a08g046_mod_clks[] = { @@ -101,8 +293,116 @@ static const struct rzg2l_mod_clk r9a08g046_mod_clks[] = { MSTOP(BUS_REG1, BIT(2))), DEF_MOD("dmac_pclk", R9A08G046_DMAC_PCLK, R9A08G046_CLK_P3, 0x52c, 1, MSTOP(BUS_REG1, BIT(3))), + DEF_MOD("wdt0_pclk", R9A08G046_WDT0_PCLK, R9A08G046_CLK_P0, 0x548, 0, + MSTOP(BUS_REG0, BIT(0))), + DEF_MOD("wdt0_clk", R9A08G046_WDT0_CLK, R9A08G046_OSCCLK, 0x548, 1, + MSTOP(BUS_REG0, BIT(0))), + DEF_MOD("ssi0_pclk2", R9A08G046_SSI0_PCLK2, R9A08G046_CLK_P0, 0x570, 0, + MSTOP(BUS_MCPU1, BIT(10))), + DEF_MOD("ssi0_pclk_sfr", R9A08G046_SSI0_PCLK_SFR, R9A08G046_CLK_P0, 0x570, 1, + MSTOP(BUS_MCPU1, BIT(10))), + DEF_MOD("ssi1_pclk2", R9A08G046_SSI1_PCLK2, R9A08G046_CLK_P0, 0x570, 2, + MSTOP(BUS_MCPU1, BIT(11))), + DEF_MOD("ssi1_pclk_sfr", R9A08G046_SSI1_PCLK_SFR, R9A08G046_CLK_P0, 0x570, 3, + MSTOP(BUS_MCPU1, BIT(11))), + DEF_MOD("ssi2_pclk2", R9A08G046_SSI2_PCLK2, R9A08G046_CLK_P0, 0x570, 4, + MSTOP(BUS_MCPU1, BIT(12))), + DEF_MOD("ssi2_pclk_sfr", R9A08G046_SSI2_PCLK_SFR, R9A08G046_CLK_P0, 0x570, 5, + MSTOP(BUS_MCPU1, BIT(12))), + DEF_MOD("ssi3_pclk2", R9A08G046_SSI3_PCLK2, R9A08G046_CLK_P0, 0x570, 6, + MSTOP(BUS_MCPU1, BIT(13))), + DEF_MOD("ssi3_pclk_sfr", R9A08G046_SSI3_PCLK_SFR, R9A08G046_CLK_P0, 0x570, 7, + MSTOP(BUS_MCPU1, BIT(13))), + DEF_MOD("eth0_clk_axi", R9A08G046_ETH0_CLK_AXI, R9A08G046_CLK_P1, 0x57c, 0, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_MOD("eth1_clk_axi", R9A08G046_ETH1_CLK_AXI, R9A08G046_CLK_P1, 0x57c, 1, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_MOD("eth0_clk_chi", R9A08G046_ETH0_CLK_CHI, R9A08G046_CLK_P1, 0x57c, 2, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_MOD("eth1_clk_chi", R9A08G046_ETH1_CLK_CHI, R9A08G046_CLK_P1, 0x57c, 3, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_COUPLED("eth0_tx_i", R9A08G046_ETH0_CLK_TX_I, R9A08G046_CLK_ETHTX01, 0x57c, 4, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_COUPLED("eth0_tx_180_i", R9A08G046_ETH0_CLK_TX_180_I, R9A08G046_CLK_ETHTX02, 0x57c, 4, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_COUPLED("eth1_tx_i", R9A08G046_ETH1_CLK_TX_I, R9A08G046_CLK_ETHTX11, 0x57c, 5, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_COUPLED("eth1_tx_180_i", R9A08G046_ETH1_CLK_TX_180_I, R9A08G046_CLK_ETHTX12, 0x57c, 5, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_COUPLED("eth0_rx_i", R9A08G046_ETH0_CLK_RX_I, R9A08G046_CLK_ETHRX01, 0x57c, 6, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_COUPLED("eth0_rx_180_i", R9A08G046_ETH0_CLK_RX_180_I, R9A08G046_CLK_ETHRX02, 0x57c, 6, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_COUPLED("eth1_rx_i", R9A08G046_ETH1_CLK_RX_I, R9A08G046_CLK_ETHRX11, 0x57c, 7, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_COUPLED("eth1_rx_180_i", R9A08G046_ETH1_CLK_RX_180_I, R9A08G046_CLK_ETHRX12, 0x57c, 7, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_MOD("eth0_ptp_ref_i", R9A08G046_ETH0_CLK_PTP_REF_I, R9A08G046_CLK_HP, 0x57c, 8, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_MOD("eth1_ptp_ref_i", R9A08G046_ETH1_CLK_PTP_REF_I, R9A08G046_CLK_HP, 0x57c, 9, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_MOD("eth0_rmii_i", R9A08G046_ETH0_CLK_RMII_I, R9A08G046_CLK_ETHRM0, 0x57c, 10, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_MOD("eth1_rmii_i", R9A08G046_ETH1_CLK_RMII_I, R9A08G046_CLK_ETHRM1, 0x57c, 11, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_COUPLED("eth0_tx_i_rmii", R9A08G046_ETH0_CLK_TX_I_RMII, R9A08G046_CLK_ETHTX01, 0x57c, 12, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_COUPLED("eth0_rx_i_rmii", R9A08G046_ETH0_CLK_RX_I_RMII, R9A08G046_CLK_ETHRX01, 0x57c, 12, + MSTOP(BUS_PERI_COM, BIT(2))), + DEF_COUPLED("eth1_tx_i_rmii", R9A08G046_ETH1_CLK_TX_I_RMII, R9A08G046_CLK_ETHTX11, 0x57c, 13, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_COUPLED("eth1_rx_i_rmii", R9A08G046_ETH1_CLK_RX_I_RMII, R9A08G046_CLK_ETHRX11, 0x57c, 13, + MSTOP(BUS_PERI_COM, BIT(3))), + DEF_MOD("i2c0_pclk", R9A08G046_I2C0_PCLK, R9A08G046_CLK_P0, 0x580, 0, + MSTOP(BUS_MCPU2, BIT(10))), + DEF_MOD("i2c1_pclk", R9A08G046_I2C1_PCLK, R9A08G046_CLK_P0, 0x580, 1, + MSTOP(BUS_MCPU2, BIT(11))), + DEF_MOD("i2c2_pclk", R9A08G046_I2C2_PCLK, R9A08G046_CLK_P0, 0x580, 2, + MSTOP(BUS_MCPU2, BIT(12))), + DEF_MOD("i2c3_pclk", R9A08G046_I2C3_PCLK, R9A08G046_CLK_P0, 0x580, 3, + MSTOP(BUS_MCPU2, BIT(13))), DEF_MOD("scif0_clk_pck", R9A08G046_SCIF0_CLK_PCK, R9A08G046_CLK_P0, 0x584, 0, MSTOP(BUS_MCPU2, BIT(1))), + DEF_MOD("scif1_clk_pck", R9A08G046_SCIF1_CLK_PCK, R9A08G046_CLK_P0, 0x584, 1, + MSTOP(BUS_MCPU2, BIT(2))), + DEF_MOD("scif2_clk_pck", R9A08G046_SCIF2_CLK_PCK, R9A08G046_CLK_P0, 0x584, 2, + MSTOP(BUS_MCPU2, BIT(3))), + DEF_MOD("scif3_clk_pck", R9A08G046_SCIF3_CLK_PCK, R9A08G046_CLK_P0, 0x584, 3, + MSTOP(BUS_MCPU2, BIT(4))), + DEF_MOD("scif4_clk_pck", R9A08G046_SCIF4_CLK_PCK, R9A08G046_CLK_P0, 0x584, 4, + MSTOP(BUS_MCPU2, BIT(5))), + DEF_MOD("scif5_clk_pck", R9A08G046_SCIF5_CLK_PCK, R9A08G046_CLK_P0, 0x584, 5, + MSTOP(BUS_MCPU3, BIT(4))), + DEF_MOD("rspi0_pclk", R9A08G046_RSPI0_PCLK, R9A08G046_CLK_P3, 0x590, 0, + MSTOP(BUS_MCPU1, BIT(14))), + DEF_MOD("rspi1_pclk", R9A08G046_RSPI1_PCLK, R9A08G046_CLK_P3, 0x590, 1, + MSTOP(BUS_MCPU1, BIT(15))), + DEF_MOD("rspi2_pclk", R9A08G046_RSPI2_PCLK, R9A08G046_CLK_P3, 0x590, 2, + MSTOP(BUS_MCPU2, BIT(0))), + DEF_MOD("rspi0_tclk", R9A08G046_RSPI0_TCLK, R9A08G046_CLK_P17, 0x590, 8, + MSTOP(BUS_MCPU1, BIT(14))), + DEF_MOD("rspi1_tclk", R9A08G046_RSPI1_TCLK, R9A08G046_CLK_P18, 0x590, 9, + MSTOP(BUS_MCPU1, BIT(15))), + DEF_MOD("rspi2_tclk", R9A08G046_RSPI2_TCLK, R9A08G046_CLK_P19, 0x590, 10, + MSTOP(BUS_MCPU2, BIT(0))), + DEF_MOD("gpio_hclk", R9A08G046_GPIO_HCLK, R9A08G046_OSCCLK, 0x598, 0, + MSTOP(BUS_PERI_CPU, BIT(6))), + DEF_MOD("rsci0_pclk", R9A08G046_RSCI0_PCLK, R9A08G046_CLK_P0, 0x618, 0, + MSTOP(BUS_MCPU2, BIT(7))), + DEF_MOD("rsci1_pclk", R9A08G046_RSCI1_PCLK, R9A08G046_CLK_P0, 0x618, 1, + MSTOP(BUS_MCPU2, BIT(8))), + DEF_MOD("rsci2_pclk", R9A08G046_RSCI2_PCLK, R9A08G046_CLK_P0, 0x618, 2, + MSTOP(BUS_MCPU3, BIT(11))), + DEF_MOD("rsci3_pclk", R9A08G046_RSCI3_PCLK, R9A08G046_CLK_P0, 0x618, 3, + MSTOP(BUS_MCPU3, BIT(12))), + DEF_MOD("rsci0_tclk", R9A08G046_RSCI0_TCLK, R9A08G046_CLK_P13, 0x618, 8, + MSTOP(BUS_MCPU2, BIT(7))), + DEF_MOD("rsci1_tclk", R9A08G046_RSCI1_TCLK, R9A08G046_CLK_P14, 0x618, 9, + MSTOP(BUS_MCPU2, BIT(8))), + DEF_MOD("rsci2_tclk", R9A08G046_RSCI2_TCLK, R9A08G046_CLK_P15, 0x618, 10, + MSTOP(BUS_MCPU3, BIT(11))), + DEF_MOD("rsci3_tclk", R9A08G046_RSCI3_TCLK, R9A08G046_CLK_P16, 0x618, 11, + MSTOP(BUS_MCPU3, BIT(12))), }; static const struct rzg2l_reset r9a08g046_resets[] = { @@ -111,11 +411,45 @@ static const struct rzg2l_reset r9a08g046_resets[] = { DEF_RST(R9A08G046_IA55_RESETN, 0x818, 0), DEF_RST(R9A08G046_DMAC_ARESETN, 0x82c, 0), DEF_RST(R9A08G046_DMAC_RST_ASYNC, 0x82c, 1), + DEF_RST(R9A08G046_WDT0_PRESETN, 0x848, 0), + DEF_RST(R9A08G046_SSI0_RST_M2_REG, 0x870, 0), + DEF_RST(R9A08G046_SSI1_RST_M2_REG, 0x870, 1), + DEF_RST(R9A08G046_SSI2_RST_M2_REG, 0x870, 2), + DEF_RST(R9A08G046_SSI3_RST_M2_REG, 0x870, 3), + DEF_RST(R9A08G046_ETH0_ARESET_N, 0x87c, 0), + DEF_RST(R9A08G046_ETH1_ARESET_N, 0x87c, 1), + DEF_RST(R9A08G046_I2C0_MRST, 0x880, 0), + DEF_RST(R9A08G046_I2C1_MRST, 0x880, 1), + DEF_RST(R9A08G046_I2C2_MRST, 0x880, 2), + DEF_RST(R9A08G046_I2C3_MRST, 0x880, 3), DEF_RST(R9A08G046_SCIF0_RST_SYSTEM_N, 0x884, 0), + DEF_RST(R9A08G046_SCIF1_RST_SYSTEM_N, 0x884, 1), + DEF_RST(R9A08G046_SCIF2_RST_SYSTEM_N, 0x884, 2), + DEF_RST(R9A08G046_SCIF3_RST_SYSTEM_N, 0x884, 3), + DEF_RST(R9A08G046_SCIF4_RST_SYSTEM_N, 0x884, 4), + DEF_RST(R9A08G046_SCIF5_RST_SYSTEM_N, 0x884, 5), + DEF_RST(R9A08G046_RSPI0_PRESETN, 0x890, 0), + DEF_RST(R9A08G046_RSPI1_PRESETN, 0x890, 1), + DEF_RST(R9A08G046_RSPI2_PRESETN, 0x890, 2), + DEF_RST(R9A08G046_RSPI0_TRESETN, 0x890, 8), + DEF_RST(R9A08G046_RSPI1_TRESETN, 0x890, 9), + DEF_RST(R9A08G046_RSPI2_TRESETN, 0x890, 10), + DEF_RST(R9A08G046_GPIO_RSTN, 0x898, 0), + DEF_RST(R9A08G046_GPIO_PORT_RESETN, 0x898, 1), + DEF_RST(R9A08G046_GPIO_SPARE_RESETN, 0x898, 2), + DEF_RST(R9A08G046_RSCI0_PRESETN, 0x918, 0), + DEF_RST(R9A08G046_RSCI1_PRESETN, 0x918, 1), + DEF_RST(R9A08G046_RSCI2_PRESETN, 0x918, 2), + DEF_RST(R9A08G046_RSCI3_PRESETN, 0x918, 3), + DEF_RST(R9A08G046_RSCI0_TRESETN, 0x918, 8), + DEF_RST(R9A08G046_RSCI1_TRESETN, 0x918, 9), + DEF_RST(R9A08G046_RSCI2_TRESETN, 0x918, 10), + DEF_RST(R9A08G046_RSCI3_TRESETN, 0x918, 11), }; static const unsigned int r9a08g046_crit_mod_clks[] __initconst = { MOD_CLK_BASE + R9A08G046_GIC600_GICCLK, + MOD_CLK_BASE + R9A08G046_IA55_PCLK, MOD_CLK_BASE + R9A08G046_IA55_CLK, MOD_CLK_BASE + R9A08G046_DMAC_ACLK, }; @@ -125,6 +459,23 @@ static const unsigned int r9a08g046_crit_resets[] = { R9A08G046_DMAC_RST_ASYNC, }; +static const unsigned int r9a08g046_no_pm_mod_clks[] = { + MOD_CLK_BASE + R9A08G046_ETH0_CLK_TX_I, + MOD_CLK_BASE + R9A08G046_ETH0_CLK_TX_180_I, + MOD_CLK_BASE + R9A08G046_ETH0_CLK_RX_I, + MOD_CLK_BASE + R9A08G046_ETH0_CLK_RX_180_I, + MOD_CLK_BASE + R9A08G046_ETH0_CLK_RMII_I, + MOD_CLK_BASE + R9A08G046_ETH0_CLK_TX_I_RMII, + MOD_CLK_BASE + R9A08G046_ETH0_CLK_RX_I_RMII, + MOD_CLK_BASE + R9A08G046_ETH1_CLK_TX_I, + MOD_CLK_BASE + R9A08G046_ETH1_CLK_TX_180_I, + MOD_CLK_BASE + R9A08G046_ETH1_CLK_RX_I, + MOD_CLK_BASE + R9A08G046_ETH1_CLK_RX_180_I, + MOD_CLK_BASE + R9A08G046_ETH1_CLK_RMII_I, + MOD_CLK_BASE + R9A08G046_ETH1_CLK_TX_I_RMII, + MOD_CLK_BASE + R9A08G046_ETH1_CLK_RX_I_RMII, +}; + const struct rzg2l_cpg_info r9a08g046_cpg_info = { /* Core Clocks */ .core_clks = r9a08g046_core_clks, @@ -141,6 +492,10 @@ const struct rzg2l_cpg_info r9a08g046_cpg_info = { .num_mod_clks = ARRAY_SIZE(r9a08g046_mod_clks), .num_hw_mod_clks = R9A08G046_BSC_X_BCK_BSC + 1, + /* No PM modules Clocks */ + .no_pm_mod_clks = r9a08g046_no_pm_mod_clks, + .num_no_pm_mod_clks = ARRAY_SIZE(r9a08g046_no_pm_mod_clks), + /* Resets */ .resets = r9a08g046_resets, .num_resets = R9A08G046_BSC_X_PRESET_BSC + 1, /* Last reset ID + 1 */ diff --git a/drivers/clk/renesas/r9a09g011-cpg.c b/drivers/clk/renesas/r9a09g011-cpg.c index ba25429c244d..a99ab1375f07 100644 --- a/drivers/clk/renesas/r9a09g011-cpg.c +++ b/drivers/clk/renesas/r9a09g011-cpg.c @@ -16,11 +16,6 @@ #include "rzg2l-cpg.h" -#define RZV2M_SAMPLL4_CLK1 0x104 -#define RZV2M_SAMPLL4_CLK2 0x108 - -#define PLL4_CONF (RZV2M_SAMPLL4_CLK1 << 22 | RZV2M_SAMPLL4_CLK2 << 12) - #define DIV_A DDIV_PACK(0x200, 0, 3) #define DIV_B DDIV_PACK(0x204, 0, 2) #define DIV_D DDIV_PACK(0x204, 4, 2) @@ -131,7 +126,7 @@ static const struct cpg_core_clk r9a09g011_core_clks[] __initconst = { DEF_FIXED(".pll2_400", CLK_PLL2_400, CLK_PLL2_800, 1, 2), DEF_FIXED(".pll2_200", CLK_PLL2_200, CLK_PLL2_800, 1, 4), DEF_FIXED(".pll2_100", CLK_PLL2_100, CLK_PLL2_800, 1, 8), - DEF_SAMPLL(".pll4", CLK_PLL4, CLK_MAIN_2, PLL4_CONF), + DEF_SAMPLL(".pll4", CLK_PLL4, CLK_MAIN_2, CPG_SAM_PLL_CONF(0x100)), DEF_DIV_RO(".diva", CLK_DIV_A, CLK_PLL1, DIV_A, dtable_diva), DEF_DIV_RO(".divb", CLK_DIV_B, CLK_PLL2_400, DIV_B, dtable_divb), diff --git a/drivers/clk/renesas/r9a09g047-cpg.c b/drivers/clk/renesas/r9a09g047-cpg.c index e59ac4a05a7f..94158b6834e6 100644 --- a/drivers/clk/renesas/r9a09g047-cpg.c +++ b/drivers/clk/renesas/r9a09g047-cpg.c @@ -6,6 +6,7 @@ */ #include <linux/clk-provider.h> +#include <linux/clk/renesas.h> #include <linux/device.h> #include <linux/init.h> #include <linux/kernel.h> @@ -30,6 +31,8 @@ enum clk_ids { CLK_PLLCA55, CLK_PLLVDO, CLK_PLLETH, + CLK_PLLDSI0, + CLK_PLLDSI1, /* Internal Core Clocks */ CLK_PLLCM33_DIV3, @@ -57,6 +60,8 @@ enum clk_ids { CLK_PLLETH_DIV_125_FIX, CLK_CSDIV_PLLETH_GBE0, CLK_CSDIV_PLLETH_GBE1, + CLK_SMUX2_DSI0_CLK, + CLK_SMUX2_DSI1_CLK, CLK_SMUX2_GBE0_TXCLK, CLK_SMUX2_GBE0_RXCLK, CLK_SMUX2_GBE1_TXCLK, @@ -64,6 +69,12 @@ enum clk_ids { CLK_PLLDTY_DIV16, CLK_PLLVDO_CRU0, CLK_PLLVDO_GPU, + CLK_PLLETH_DIV4_LPCLK, + CLK_PLLETH_LPCLK, + CLK_PLLDSI0_DIV7, + CLK_PLLDSI1_DIV7, + CLK_PLLDSI0_CSDIV, + CLK_PLLDSI1_CSDIV, /* Module Clocks */ MOD_CLK_BASE, @@ -91,6 +102,18 @@ static const struct clk_div_table dtable_2_16[] = { {0, 0}, }; +static const struct clk_div_table dtable_2_16_plldsi[] = { + {0, 2}, + {1, 4}, + {2, 6}, + {3, 8}, + {4, 10}, + {5, 12}, + {6, 14}, + {7, 16}, + {0, 0}, +}; + static const struct clk_div_table dtable_2_64[] = { {0, 2}, {1, 4}, @@ -107,7 +130,23 @@ static const struct clk_div_table dtable_2_100[] = { {0, 0}, }; +static const struct clk_div_table dtable_16_128[] = { + {0, 16}, + {1, 32}, + {2, 64}, + {3, 128}, + {0, 0}, +}; + +RZG3E_CPG_PLL_DSI0_LIMITS(rzg3e_cpg_pll_dsi0_limits); +RZG3E_CPG_PLL_DSI1_LIMITS(rzg3e_cpg_pll_dsi1_limits); + +#define PLLDSI0 PLL_PACK_LIMITS(0xc0, 1, 0, &rzg3e_cpg_pll_dsi0_limits) +#define PLLDSI1 PLL_PACK_LIMITS(0x160, 1, 1, &rzg3e_cpg_pll_dsi1_limits) + /* Mux clock tables */ +static const char * const smux2_dsi0_clk[] = { ".plldsi0_div7", ".plldsi0_csdiv" }; +static const char * const smux2_dsi1_clk[] = { ".plldsi1_div7", ".plldsi1_csdiv" }; static const char * const smux2_gbe0_rxclk[] = { ".plleth_gbe0", "et0_rxclk" }; static const char * const smux2_gbe0_txclk[] = { ".plleth_gbe0", "et0_txclk" }; static const char * const smux2_gbe1_rxclk[] = { ".plleth_gbe1", "et1_rxclk" }; @@ -128,6 +167,8 @@ static const struct cpg_core_clk r9a09g047_core_clks[] __initconst = { DEF_PLL(".pllca55", CLK_PLLCA55, CLK_QEXTAL, PLLCA55), DEF_FIXED(".plleth", CLK_PLLETH, CLK_QEXTAL, 125, 3), DEF_FIXED(".pllvdo", CLK_PLLVDO, CLK_QEXTAL, 105, 2), + DEF_PLLDSI(".plldsi0", CLK_PLLDSI0, CLK_QEXTAL, PLLDSI0), + DEF_PLLDSI(".plldsi1", CLK_PLLDSI1, CLK_QEXTAL, PLLDSI1), /* Internal Core Clocks */ DEF_FIXED(".pllcm33_div3", CLK_PLLCM33_DIV3, CLK_PLLCM33, 1, 3), @@ -171,6 +212,21 @@ static const struct cpg_core_clk r9a09g047_core_clks[] __initconst = { DEF_DDIV(".pllvdo_cru0", CLK_PLLVDO_CRU0, CLK_PLLVDO, CDDIV3_DIVCTL3, dtable_2_4), DEF_DDIV(".pllvdo_gpu", CLK_PLLVDO_GPU, CLK_PLLVDO, CDDIV3_DIVCTL1, dtable_2_64), + DEF_FIXED(".plleth_div4_lpclk", CLK_PLLETH_DIV4_LPCLK, CLK_PLLETH, 1, 4), + DEF_CSDIV(".plleth_lpclk", CLK_PLLETH_LPCLK, CLK_PLLETH_DIV4_LPCLK, + CSDIV0_DIVCTL2, dtable_16_128), + + DEF_PLLDSI_DIV(".plldsi0_csdiv", CLK_PLLDSI0_CSDIV, CLK_PLLDSI0, + CSDIV1_DIVCTL2, dtable_2_16_plldsi), + DEF_PLLDSI_DIV(".plldsi1_csdiv", CLK_PLLDSI1_CSDIV, CLK_PLLDSI1, + CSDIV1_DIVCTL3, dtable_2_16_plldsi), + DEF_FIXED(".plldsi0_div7", CLK_PLLDSI0_DIV7, CLK_PLLDSI0, 1, 7), + DEF_FIXED(".plldsi1_div7", CLK_PLLDSI1_DIV7, CLK_PLLDSI1, 1, 7), + DEF_PLLDSI_SMUX(".smux2_dsi0_clk", CLK_SMUX2_DSI0_CLK, + SSEL3_SELCTL0, smux2_dsi0_clk), + DEF_PLLDSI_SMUX(".smux2_dsi1_clk", CLK_SMUX2_DSI1_CLK, + SSEL3_SELCTL1, smux2_dsi1_clk), + /* Core Clocks */ DEF_FIXED("sys_0_pclk", R9A09G047_SYS_0_PCLK, CLK_QEXTAL, 1, 1), DEF_DDIV("ca55_0_coreclk0", R9A09G047_CA55_0_CORECLK0, CLK_PLLCA55, @@ -452,6 +508,22 @@ static const struct rzv2h_mod_clk r9a09g047_mod_clks[] __initconst = { BUS_MSTOP(9, BIT(4))), DEF_MOD("cru_0_pclk", CLK_PLLDTY_DIV16, 13, 4, 6, 20, BUS_MSTOP(9, BIT(4))), + DEF_MOD("dsi_0_pclk", CLK_PLLDTY_DIV16, 14, 8, 7, 8, + BUS_MSTOP(9, BIT(15) | BIT(14))), + DEF_MOD("dsi_0_aclk", CLK_PLLDTY_ACPU_DIV2, 14, 9, 7, 9, + BUS_MSTOP(9, BIT(15) | BIT(14))), + DEF_MOD("dsi_0_vclk1", CLK_SMUX2_DSI0_CLK, 14, 10, 7, 10, + BUS_MSTOP(9, BIT(15) | BIT(14))), + DEF_MOD("dsi_0_lpclk", CLK_PLLETH_LPCLK, 14, 11, 7, 11, + BUS_MSTOP(9, BIT(15) | BIT(14))), + DEF_MOD("dsi_0_pllref_clk", CLK_QEXTAL, 14, 12, 7, 12, + BUS_MSTOP(9, BIT(15) | BIT(14))), + DEF_MOD("lcdc_0_clk_a", CLK_PLLDTY_ACPU_DIV2, 14, 13, 7, 13, + BUS_MSTOP(10, BIT(3) | BIT(2) | BIT(1))), + DEF_MOD("lcdc_0_clk_p", CLK_PLLDTY_DIV16, 14, 14, 7, 14, + BUS_MSTOP(10, BIT(3) | BIT(2) | BIT(1))), + DEF_MOD("lcdc_0_clk_d", CLK_SMUX2_DSI0_CLK, 14, 15, 7, 15, + BUS_MSTOP(10, BIT(3) | BIT(2) | BIT(1))), DEF_MOD("ge3d_clk", CLK_PLLVDO_GPU, 15, 0, 7, 16, BUS_MSTOP(3, BIT(4))), DEF_MOD("ge3d_axi_clk", CLK_PLLDTY_ACPU_DIV2, 15, 1, 7, 17, @@ -460,6 +532,14 @@ static const struct rzv2h_mod_clk r9a09g047_mod_clks[] __initconst = { BUS_MSTOP(3, BIT(4))), DEF_MOD("tsu_1_pclk", CLK_QEXTAL, 16, 10, 8, 10, BUS_MSTOP(2, BIT(15))), + DEF_MOD("dsi_0_vclk2", CLK_SMUX2_DSI1_CLK, 25, 0, 10, 21, + BUS_MSTOP(9, BIT(15) | BIT(14))), + DEF_MOD("lcdc_1_clk_a", CLK_PLLDTY_ACPU_DIV2, 26, 8, 10, 30, + BUS_MSTOP(13, BIT(5) | BIT(4) | BIT(3))), + DEF_MOD("lcdc_1_clk_p", CLK_PLLDTY_DIV16, 26, 9, 10, 31, + BUS_MSTOP(13, BIT(5) | BIT(4) | BIT(3))), + DEF_MOD("lcdc_1_clk_d", CLK_SMUX2_DSI1_CLK, 26, 10, 11, 0, + BUS_MSTOP(13, BIT(5) | BIT(4) | BIT(3))), }; static const struct rzv2h_reset r9a09g047_resets[] __initconst = { @@ -535,10 +615,14 @@ static const struct rzv2h_reset r9a09g047_resets[] __initconst = { DEF_RST(12, 5, 5, 22), /* CRU_0_PRESETN */ DEF_RST(12, 6, 5, 23), /* CRU_0_ARESETN */ DEF_RST(12, 7, 5, 24), /* CRU_0_S_RESETN */ + DEF_RST(13, 7, 6, 8), /* DSI_0_PRESETN */ + DEF_RST(13, 8, 6, 9), /* DSI_0_ARESETN */ + DEF_RST(13, 12, 6, 13), /* LCDC_0_RESET_N */ DEF_RST(13, 13, 6, 14), /* GE3D_RESETN */ DEF_RST(13, 14, 6, 15), /* GE3D_AXI_RESETN */ DEF_RST(13, 15, 6, 16), /* GE3D_ACE_RESETN */ DEF_RST(15, 8, 7, 9), /* TSU_1_PRESETN */ + DEF_RST(17, 14, 8, 15), /* LCDC_1_RESET_N */ }; const struct rzv2h_cpg_info r9a09g047_cpg_info __initconst = { diff --git a/drivers/clk/renesas/r9a09g077-cpg.c b/drivers/clk/renesas/r9a09g077-cpg.c index 93b15e06a19b..f777601a23b9 100644 --- a/drivers/clk/renesas/r9a09g077-cpg.c +++ b/drivers/clk/renesas/r9a09g077-cpg.c @@ -257,6 +257,7 @@ static const struct mssr_mod_clk r9a09g077_mod_clks[] __initconst = { DEF_MOD("spi0", 104, CLK_SPI0ASYNC), DEF_MOD("spi1", 105, CLK_SPI1ASYNC), DEF_MOD("spi2", 106, CLK_SPI2ASYNC), + DEF_MOD("mtu3", 200, R9A09G077_CLK_PCLKH), DEF_MOD("adc0", 206, R9A09G077_CLK_PCLKH), DEF_MOD("adc1", 207, R9A09G077_CLK_PCLKH), DEF_MOD("adc2", 225, R9A09G077_CLK_PCLKM), diff --git a/drivers/clk/renesas/renesas-cpg-mssr.c b/drivers/clk/renesas/renesas-cpg-mssr.c index 26ea85cfaa02..5b84cbee030b 100644 --- a/drivers/clk/renesas/renesas-cpg-mssr.c +++ b/drivers/clk/renesas/renesas-cpg-mssr.c @@ -370,6 +370,9 @@ struct clk *cpg_mssr_clk_src_twocell_get(struct of_phandle_args *clkspec, struct clk *clk; int range_check; + if (clkspec->args_count != 2) + return ERR_PTR(-EINVAL); + switch (clkspec->args[0]) { case CPG_CORE: type = "core"; diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index abfd8634d2be..51c9e19e1575 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -58,15 +58,22 @@ #define RZG3S_DIV_NF GENMASK(12, 1) #define RZG3S_SEL_PLL BIT(0) +#define CPG_PLL1_SETTING_OFFSET(conf) FIELD_GET(GENMASK(11, 0), (conf)) +#define CPG_PLL_STBY_OFFSET(conf) FIELD_GET(GENMASK(23, 12), (conf)) +#define CPG_PLL_STBY_RESETB_WEN BIT(16) +#define CPG_PLL_STBY_RESETB BIT(0) +#define CPG_PLL_CLK1_OFFSET(x) (CPG_PLL_STBY_OFFSET(x) + 0x4) +#define CPG_PLL_CLK2_OFFSET(x) (CPG_PLL_STBY_OFFSET(x) + 0x8) +#define CPG_PLL_MON_OFFSET(x) (CPG_PLL_STBY_OFFSET(x) + 0xc) +#define CPG_PLL_MON_LOCK BIT(4) +#define CPG_PLL_MON_RESETB BIT(0) + #define CLK_ON_R(reg) (reg) #define CLK_MON_R(reg) (0x180 + (reg)) #define CLK_RST_R(reg) (reg) #define CLK_MRST_R(reg) (0x180 + (reg)) #define GET_REG_OFFSET(val) ((val >> 20) & 0xfff) -#define GET_REG_SAMPLL_CLK1(val) ((val >> 22) & 0xfff) -#define GET_REG_SAMPLL_CLK2(val) ((val >> 12) & 0xfff) -#define GET_REG_SAMPLL_SETTING(val) ((val) & 0xfff) #define CPG_WEN_BIT BIT(16) @@ -1086,8 +1093,8 @@ static unsigned long rzg2l_cpg_pll_clk_recalc_rate(struct clk_hw *hw, if (pll_clk->type != CLK_TYPE_SAM_PLL) return parent_rate; - val1 = readl(priv->base + GET_REG_SAMPLL_CLK1(pll_clk->conf)); - val2 = readl(priv->base + GET_REG_SAMPLL_CLK2(pll_clk->conf)); + val1 = readl(priv->base + CPG_PLL_CLK1_OFFSET(pll_clk->conf)); + val2 = readl(priv->base + CPG_PLL_CLK2_OFFSET(pll_clk->conf)); rate = mul_u64_u32_shr(parent_rate, (MDIV(val1) << 16) + KDIV(val1), 16 + SDIV(val2)); @@ -1107,17 +1114,14 @@ static unsigned long rzg3s_cpg_pll_clk_recalc_rate(struct clk_hw *hw, u32 nir, nfr, mr, pr, val, setting; u64 rate; - if (pll_clk->type != CLK_TYPE_G3S_PLL) - return parent_rate; - - setting = GET_REG_SAMPLL_SETTING(pll_clk->conf); + setting = CPG_PLL1_SETTING_OFFSET(pll_clk->conf); if (setting) { val = readl(priv->base + setting); if (val & RZG3S_SEL_PLL) return pll_clk->default_rate; } - val = readl(priv->base + GET_REG_SAMPLL_CLK1(pll_clk->conf)); + val = readl(priv->base + CPG_PLL_CLK1_OFFSET(pll_clk->conf)); pr = 1 << FIELD_GET(RZG3S_DIV_P, val); /* Hardware interprets values higher than 8 as p = 16. */ @@ -1178,6 +1182,61 @@ rzg2l_cpg_pll_clk_register(const struct cpg_core_clk *core, return pll_clk->hw.clk; } +static int rzg3l_cpg_pll_clk_is_enabled(struct clk_hw *hw) +{ + struct pll_clk *pll_clk = to_pll(hw); + struct rzg2l_cpg_priv *priv = pll_clk->priv; + u32 val = readl(priv->base + CPG_PLL_MON_OFFSET(pll_clk->conf)); + u32 mon_val = CPG_PLL_MON_RESETB | CPG_PLL_MON_LOCK; + + /* Ensure both RESETB and LOCK bits are set */ + return (mon_val == (val & mon_val)); +} + +static int rzg3l_cpg_pll_clk_endisable(struct clk_hw *hw, bool enable) +{ + struct pll_clk *pll_clk = to_pll(hw); + struct rzg2l_cpg_priv *priv = pll_clk->priv; + u32 mon_mask = CPG_PLL_MON_RESETB | CPG_PLL_MON_LOCK; + u32 val = CPG_PLL_STBY_RESETB_WEN; + u32 stby_offset, mon_offset; + u32 mon_val = 0; + int ret; + + stby_offset = CPG_PLL_STBY_OFFSET(pll_clk->conf); + mon_offset = CPG_PLL_MON_OFFSET(pll_clk->conf); + + if (enable) { + val |= CPG_PLL_STBY_RESETB; + mon_val = mon_mask; + } + + writel(val, priv->base + stby_offset); + + /* ensure PLL is in normal/standby mode */ + ret = readl_poll_timeout_atomic(priv->base + mon_offset, val, + mon_val == (val & mon_mask), 10, 100); + if (ret) + dev_err(priv->dev, "Failed to %s PLL 0x%x/%pC\n", enable ? + "enable" : "disable", stby_offset, hw->clk); + + return ret; +} + +static int rzg3l_cpg_pll_clk_enable(struct clk_hw *hw) +{ + if (rzg3l_cpg_pll_clk_is_enabled(hw)) + return 0; + + return rzg3l_cpg_pll_clk_endisable(hw, true); +} + +static const struct clk_ops rzg3l_cpg_pll_ops = { + .is_enabled = rzg3l_cpg_pll_clk_is_enabled, + .enable = rzg3l_cpg_pll_clk_enable, + .recalc_rate = rzg3s_cpg_pll_clk_recalc_rate, +}; + static struct clk *rzg2l_cpg_clk_src_twocell_get(struct of_phandle_args *clkspec, void *data) @@ -1261,6 +1320,9 @@ rzg2l_cpg_register_core_clk(const struct cpg_core_clk *core, case CLK_TYPE_SAM_PLL: clk = rzg2l_cpg_pll_clk_register(core, priv, &rzg2l_cpg_pll_ops); break; + case CLK_TYPE_G3L_PLL: + clk = rzg2l_cpg_pll_clk_register(core, priv, &rzg3l_cpg_pll_ops); + break; case CLK_TYPE_G3S_PLL: clk = rzg2l_cpg_pll_clk_register(core, priv, &rzg3s_cpg_pll_ops); break; @@ -1340,10 +1402,10 @@ struct mod_clock { #define to_mod_clock(_hw) container_of(_hw, struct mod_clock, hw) #define for_each_mod_clock(mod_clock, hw, priv) \ - for (unsigned int i = 0; (priv) && i < (priv)->num_mod_clks; i++) \ - if ((priv)->clks[(priv)->num_core_clks + i] == ERR_PTR(-ENOENT)) \ + for (unsigned int __i = 0; (priv) && __i < (priv)->num_mod_clks; __i++) \ + if ((priv)->clks[(priv)->num_core_clks + __i] == ERR_PTR(-ENOENT)) \ continue; \ - else if (((hw) = __clk_get_hw((priv)->clks[(priv)->num_core_clks + i])) && \ + else if (((hw) = __clk_get_hw((priv)->clks[(priv)->num_core_clks + __i])) && \ ((mod_clock) = to_mod_clock(hw))) /* Need to be called with a lock held to avoid concurrent access to mstop->usecnt. */ diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h index 10baf9e71a6e..bd6169f62538 100644 --- a/drivers/clk/renesas/rzg2l-cpg.h +++ b/drivers/clk/renesas/rzg2l-cpg.h @@ -58,11 +58,8 @@ #define CPG_CLKSTATUS_SELSDHI0_STS BIT(28) #define CPG_CLKSTATUS_SELSDHI1_STS BIT(29) -/* n = 0/1/2 for PLL1/4/6 */ -#define CPG_SAMPLL_CLK1(n) (0x04 + (16 * n)) -#define CPG_SAMPLL_CLK2(n) (0x08 + (16 * n)) - -#define PLL146_CONF(n) (CPG_SAMPLL_CLK1(n) << 22 | CPG_SAMPLL_CLK2(n) << 12) +#define CPG_SAM_PLL_CONF(stby) ((stby) << 12) +#define CPG_PLL_CONF(stby, setting) ((stby) << 12 | (setting)) #define DDIV_PACK(offset, bitpos, size) \ (((offset) << 20) | ((bitpos) << 12) | ((size) << 8)) @@ -123,6 +120,7 @@ enum clk_types { CLK_TYPE_IN, /* External Clock Input */ CLK_TYPE_FF, /* Fixed Factor Clock */ CLK_TYPE_SAM_PLL, + CLK_TYPE_G3L_PLL, CLK_TYPE_G3S_PLL, /* Clock with divider */ @@ -152,6 +150,9 @@ enum clk_types { DEF_TYPE(_name, _id, _type, .parent = _parent) #define DEF_SAMPLL(_name, _id, _parent, _conf) \ DEF_TYPE(_name, _id, CLK_TYPE_SAM_PLL, .parent = _parent, .conf = _conf) +#define DEF_G3L_PLL(_name, _id, _parent, _conf, _default_rate) \ + DEF_TYPE(_name, _id, CLK_TYPE_G3L_PLL, .parent = _parent, .conf = _conf, \ + .default_rate = _default_rate) #define DEF_G3S_PLL(_name, _id, _parent, _conf, _default_rate) \ DEF_TYPE(_name, _id, CLK_TYPE_G3S_PLL, .parent = _parent, .conf = _conf, \ .default_rate = _default_rate) @@ -174,11 +175,14 @@ enum clk_types { .invalid_rate = _invalid_rate, \ .max_rate = _max_rate, .flag = (_clk_flags), \ .notifier = _notif) -#define DEF_MUX(_name, _id, _conf, _parent_names) \ +#define DEF_MUX_FLAGS(_name, _id, _conf, _parent_names, _flag) \ DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = _conf, \ .parent_names = _parent_names, \ .num_parents = ARRAY_SIZE(_parent_names), \ - .mux_flags = CLK_MUX_HIWORD_MASK) + .mux_flags = CLK_MUX_HIWORD_MASK, \ + .flag = _flag) +#define DEF_MUX(_name, _id, _conf, _parent_names) \ + DEF_MUX_FLAGS(_name, _id, _conf, _parent_names, 0) #define DEF_MUX_RO(_name, _id, _conf, _parent_names) \ DEF_TYPE(_name, _id, CLK_TYPE_MUX, .conf = _conf, \ .parent_names = _parent_names, \ diff --git a/drivers/clk/renesas/rzv2h-cpg.c b/drivers/clk/renesas/rzv2h-cpg.c index f6c47fb89bca..e271c04cee34 100644 --- a/drivers/clk/renesas/rzv2h-cpg.c +++ b/drivers/clk/renesas/rzv2h-cpg.c @@ -76,6 +76,11 @@ /* On RZ/G3E SoC we have two DSI PLLs */ #define MAX_CPG_DSI_PLL 2 +#define CPG_PLLDSI_SMUX_LVDS_DUTY_NUM 4 +#define CPG_PLLDSI_SMUX_LVDS_DUTY_DEN 7 +#define CPG_PLLDSI_SMUX_DSI_RGB_DUTY_NUM 1 +#define CPG_PLLDSI_SMUX_DSI_RGB_DUTY_DEN 2 + /** * struct rzv2h_pll_dsi_info - PLL DSI information, holds the limits and parameters * @@ -418,6 +423,20 @@ bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits *limits, } EXPORT_SYMBOL_NS_GPL(rzv2h_get_pll_divs_pars, "RZV2H_CPG"); +/** + * struct rzv2h_plldsi_mux_clk - PLL DSI MUX clock + * + * @priv: CPG private data + * @mux: mux clk + */ +struct rzv2h_plldsi_mux_clk { + struct rzv2h_cpg_priv *priv; + struct clk_mux mux; +}; + +#define to_plldsi_clk_mux(_mux) \ + container_of(_mux, struct rzv2h_plldsi_mux_clk, mux) + static unsigned long rzv2h_cpg_plldsi_div_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -649,6 +668,165 @@ static int rzv2h_cpg_plldsi_set_rate(struct clk_hw *hw, unsigned long rate, return rzv2h_cpg_pll_set_rate(pll_clk, &dsi_info->pll_dsi_parameters.pll, true); } +static u8 rzv2h_cpg_plldsi_smux_get_parent(struct clk_hw *hw) +{ + return clk_mux_ops.get_parent(hw); +} + +static int rzv2h_cpg_plldsi_smux_set_parent(struct clk_hw *hw, u8 index) +{ + return clk_mux_ops.set_parent(hw, index); +} + +static int rzv2h_cpg_plldsi_smux_lvds_determine_rate(struct rzv2h_cpg_priv *priv, + struct pll_clk *pll_clk, + struct clk_rate_request *req) +{ + struct rzv2h_pll_div_pars *dsi_params; + struct rzv2h_pll_dsi_info *dsi_info; + u8 lvds_table[] = { 7 }; + u64 rate_millihz; + + dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance]; + dsi_params = &dsi_info->pll_dsi_parameters; + + rate_millihz = mul_u32_u32(req->rate, MILLI); + if (!rzv2h_get_pll_divs_pars(dsi_info->pll_dsi_limits, dsi_params, + lvds_table, ARRAY_SIZE(lvds_table), rate_millihz)) { + dev_err(priv->dev, "failed to determine rate for req->rate: %lu\n", + req->rate); + return -EINVAL; + } + + req->rate = DIV_ROUND_CLOSEST_ULL(dsi_params->div.freq_millihz, MILLI); + req->best_parent_rate = req->rate; + dsi_info->req_pll_dsi_rate = req->best_parent_rate * dsi_params->div.divider_value; + + return 0; +} + +static int rzv2h_cpg_plldsi_smux_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_mux *mux = to_clk_mux(hw); + struct rzv2h_plldsi_mux_clk *dsi_mux = to_plldsi_clk_mux(mux); + struct pll_clk *pll_clk = to_pll(clk_hw_get_parent(hw)); + struct rzv2h_cpg_priv *priv = dsi_mux->priv; + + /* + * For LVDS output (parent index 0), calculate PLL parameters with + * fixed divider value of 7. For DSI/RGB output (parent index 1) skip + * PLL calculation here as it's handled by determine_rate of the + * divider (up one level). + */ + if (!clk_mux_ops.get_parent(hw)) + return rzv2h_cpg_plldsi_smux_lvds_determine_rate(priv, pll_clk, req); + + req->best_parent_rate = req->rate; + return 0; +} + +static int rzv2h_cpg_plldsi_smux_get_duty_cycle(struct clk_hw *hw, + struct clk_duty *duty) +{ + u8 parent = clk_mux_ops.get_parent(hw); + + /* + * CDIV7_DSIx_CLK - LVDS path (div7) - duty 4/7. + * CSDIV_DSIx - DSI/RGB path (csdiv) - duty 1/2. + */ + if (parent == 0) { + duty->num = CPG_PLLDSI_SMUX_LVDS_DUTY_NUM; + duty->den = CPG_PLLDSI_SMUX_LVDS_DUTY_DEN; + } else { + duty->num = CPG_PLLDSI_SMUX_DSI_RGB_DUTY_NUM; + duty->den = CPG_PLLDSI_SMUX_DSI_RGB_DUTY_DEN; + } + + return 0; +} + +static int rzv2h_cpg_plldsi_smux_set_duty_cycle(struct clk_hw *hw, + struct clk_duty *duty) +{ + struct clk_hw *parent_hw; + u8 parent_idx; + + /* + * Select parent based on requested duty cycle: + * - If duty > 50% (num/den > 1/2), select LVDS path (parent 0) + * - Otherwise, select DSI/RGB path (parent 1) + */ + if (duty->num * CPG_PLLDSI_SMUX_DSI_RGB_DUTY_DEN > + duty->den * CPG_PLLDSI_SMUX_DSI_RGB_DUTY_NUM) + parent_idx = 0; + else + parent_idx = 1; + + if (parent_idx >= clk_hw_get_num_parents(hw)) + return -EINVAL; + + parent_hw = clk_hw_get_parent_by_index(hw, parent_idx); + if (!parent_hw) + return -EINVAL; + + return clk_hw_set_parent(hw, parent_hw); +} + +static const struct clk_ops rzv2h_cpg_plldsi_smux_ops = { + .determine_rate = rzv2h_cpg_plldsi_smux_determine_rate, + .get_parent = rzv2h_cpg_plldsi_smux_get_parent, + .set_parent = rzv2h_cpg_plldsi_smux_set_parent, + .get_duty_cycle = rzv2h_cpg_plldsi_smux_get_duty_cycle, + .set_duty_cycle = rzv2h_cpg_plldsi_smux_set_duty_cycle, +}; + +static struct clk * __init +rzv2h_cpg_plldsi_smux_clk_register(const struct cpg_core_clk *core, + struct rzv2h_cpg_priv *priv) +{ + struct rzv2h_plldsi_mux_clk *clk_hw_data; + struct clk_init_data init; + struct clk_hw *clk_hw; + struct smuxed smux; + int ret; + + smux = core->cfg.smux; + + if (smux.shift + smux.width > 16) { + dev_err(priv->dev, "mux value exceeds LOWORD field\n"); + return ERR_PTR(-EINVAL); + } + + clk_hw_data = devm_kzalloc(priv->dev, sizeof(*clk_hw_data), GFP_KERNEL); + if (!clk_hw_data) + return ERR_PTR(-ENOMEM); + + clk_hw_data->priv = priv; + + init.name = core->name; + init.ops = &rzv2h_cpg_plldsi_smux_ops; + init.flags = core->flag; + init.parent_names = core->parent_names; + init.num_parents = core->num_parents; + + clk_hw_data->mux.reg = priv->base + smux.offset; + + clk_hw_data->mux.shift = smux.shift; + clk_hw_data->mux.mask = clk_div_mask(smux.width); + clk_hw_data->mux.flags = core->mux_flags; + clk_hw_data->mux.lock = &priv->rmw_lock; + + clk_hw = &clk_hw_data->mux.hw; + clk_hw->init = &init; + + ret = devm_clk_hw_register(priv->dev, clk_hw); + if (ret) + return ERR_PTR(ret); + + return clk_hw->clk; +} + static int rzv2h_cpg_pll_clk_is_enabled(struct clk_hw *hw) { struct pll_clk *pll_clk = to_pll(hw); @@ -1085,6 +1263,9 @@ rzv2h_cpg_register_core_clk(const struct cpg_core_clk *core, case CLK_TYPE_PLLDSI_DIV: clk = rzv2h_cpg_plldsi_div_clk_register(core, priv); break; + case CLK_TYPE_PLLDSI_SMUX: + clk = rzv2h_cpg_plldsi_smux_clk_register(core, priv); + break; default: goto fail; } diff --git a/drivers/clk/renesas/rzv2h-cpg.h b/drivers/clk/renesas/rzv2h-cpg.h index dc957bdaf5e9..dec0f7b621d6 100644 --- a/drivers/clk/renesas/rzv2h-cpg.h +++ b/drivers/clk/renesas/rzv2h-cpg.h @@ -121,6 +121,7 @@ struct fixed_mod_conf { #define CPG_SSEL0 (0x300) #define CPG_SSEL1 (0x304) +#define CPG_SSEL3 (0x30C) #define CPG_CDDIV0 (0x400) #define CPG_CDDIV1 (0x404) #define CPG_CDDIV2 (0x408) @@ -148,6 +149,7 @@ struct fixed_mod_conf { #define CSDIV0_DIVCTL2 DDIV_PACK(CPG_CSDIV0, 8, 2, CSDIV_NO_MON) #define CSDIV0_DIVCTL3 DDIV_PACK_NO_RMW(CPG_CSDIV0, 12, 2, CSDIV_NO_MON) #define CSDIV1_DIVCTL2 DDIV_PACK(CPG_CSDIV1, 8, 4, CSDIV_NO_MON) +#define CSDIV1_DIVCTL3 DDIV_PACK(CPG_CSDIV1, 12, 4, CSDIV_NO_MON) #define SSEL0_SELCTL2 SMUX_PACK(CPG_SSEL0, 8, 1) #define SSEL0_SELCTL3 SMUX_PACK(CPG_SSEL0, 12, 1) @@ -155,6 +157,8 @@ struct fixed_mod_conf { #define SSEL1_SELCTL1 SMUX_PACK(CPG_SSEL1, 4, 1) #define SSEL1_SELCTL2 SMUX_PACK(CPG_SSEL1, 8, 1) #define SSEL1_SELCTL3 SMUX_PACK(CPG_SSEL1, 12, 1) +#define SSEL3_SELCTL0 SMUX_PACK(CPG_SSEL3, 0, 1) +#define SSEL3_SELCTL1 SMUX_PACK(CPG_SSEL3, 4, 1) #define BUS_MSTOP_IDX_MASK GENMASK(31, 16) #define BUS_MSTOP_BITS_MASK GENMASK(15, 0) @@ -203,6 +207,7 @@ enum clk_types { CLK_TYPE_SMUX, /* Static Mux */ CLK_TYPE_PLLDSI, /* PLLDSI */ CLK_TYPE_PLLDSI_DIV, /* PLLDSI divider */ + CLK_TYPE_PLLDSI_SMUX, /* PLLDSI Static Mux */ }; #define DEF_TYPE(_name, _id, _type...) \ @@ -241,6 +246,13 @@ enum clk_types { .dtable = _dtable, \ .parent = _parent, \ .flag = CLK_SET_RATE_PARENT) +#define DEF_PLLDSI_SMUX(_name, _id, _smux_packed, _parent_names) \ + DEF_TYPE(_name, _id, CLK_TYPE_PLLDSI_SMUX, \ + .cfg.smux = _smux_packed, \ + .parent_names = _parent_names, \ + .num_parents = ARRAY_SIZE(_parent_names), \ + .flag = CLK_SET_RATE_PARENT, \ + .mux_flags = CLK_MUX_HIWORD_MASK) /** * struct rzv2h_mod_clk - Module Clocks definitions diff --git a/drivers/clk/rockchip/Kconfig b/drivers/clk/rockchip/Kconfig index 7e1433502061..85133498f013 100644 --- a/drivers/clk/rockchip/Kconfig +++ b/drivers/clk/rockchip/Kconfig @@ -3,7 +3,7 @@ config COMMON_CLK_ROCKCHIP bool "Rockchip clock controller common support" - depends on ARCH_ROCKCHIP + depends on ARCH_ROCKCHIP || COMPILE_TEST default ARCH_ROCKCHIP help Say y here to enable common clock controller for Rockchip platforms. diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c index 1694223f4f84..2ba9976654cf 100644 --- a/drivers/clk/rockchip/clk-rk3588.c +++ b/drivers/clk/rockchip/clk-rk3588.c @@ -5,11 +5,13 @@ */ #include <linux/clk-provider.h> +#include <linux/mfd/syscon.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/syscore_ops.h> #include <dt-bindings/clock/rockchip,rk3588-cru.h> +#include <soc/rockchip/rk3588_grf.h> #include "clk.h" #define RK3588_GRF_SOC_STATUS0 0x600 @@ -892,6 +894,8 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { RK3588_CLKGATE_CON(8), 0, GFLAGS), MUX(I2S2_2CH_MCLKOUT, "i2s2_2ch_mclkout", i2s2_2ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_CLKSEL_CON(30), 2, 1, MFLAGS), + GATE_GRF(I2S2_2CH_MCLKOUT_TO_IO, "i2s2_2ch_mclkout_to_io", "i2s2_2ch_mclkout", + 0, RK3588_SYSGRF_SOC_CON6, 2, GFLAGS, grf_type_sys), COMPOSITE(CLK_I2S3_2CH_SRC, "clk_i2s3_2ch_src", gpll_aupll_p, 0, RK3588_CLKSEL_CON(30), 8, 1, MFLAGS, 3, 5, DFLAGS, @@ -907,6 +911,8 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { RK3588_CLKGATE_CON(8), 4, GFLAGS), MUX(I2S3_2CH_MCLKOUT, "i2s3_2ch_mclkout", i2s3_2ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_CLKSEL_CON(32), 2, 1, MFLAGS), + GATE_GRF(I2S3_2CH_MCLKOUT_TO_IO, "i2s3_2ch_mclkout_to_io", "i2s3_2ch_mclkout", + 0, RK3588_SYSGRF_SOC_CON6, 7, GFLAGS, grf_type_sys), GATE(PCLK_ACDCDIG, "pclk_acdcdig", "pclk_audio_root", 0, RK3588_CLKGATE_CON(7), 11, GFLAGS), GATE(HCLK_I2S0_8CH, "hclk_i2s0_8ch", "hclk_audio_root", 0, @@ -935,6 +941,8 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { RK3588_CLKGATE_CON(7), 10, GFLAGS), MUX(I2S0_8CH_MCLKOUT, "i2s0_8ch_mclkout", i2s0_8ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_CLKSEL_CON(28), 2, 2, MFLAGS), + GATE_GRF(I2S0_8CH_MCLKOUT_TO_IO, "i2s0_8ch_mclkout_to_io", "i2s0_8ch_mclkout", + 0, RK3588_SYSGRF_SOC_CON6, 0, GFLAGS, grf_type_sys), GATE(HCLK_PDM1, "hclk_pdm1", "hclk_audio_root", 0, RK3588_CLKGATE_CON(9), 6, GFLAGS), @@ -2220,6 +2228,8 @@ static struct rockchip_clk_branch rk3588_early_clk_branches[] __initdata = { RK3588_PMU_CLKGATE_CON(2), 13, GFLAGS), MUX(I2S1_8CH_MCLKOUT, "i2s1_8ch_mclkout", i2s1_8ch_mclkout_p, CLK_SET_RATE_PARENT, RK3588_PMU_CLKSEL_CON(9), 2, 2, MFLAGS), + GATE_GRF(I2S1_8CH_MCLKOUT_TO_IO, "i2s1_8ch_mclkout_to_io", "i2s1_8ch_mclkout", + 0, RK3588_SYSGRF_SOC_CON6, 1, GFLAGS, grf_type_sys), GATE(PCLK_PMU1, "pclk_pmu1", "pclk_pmu0_root", CLK_IS_CRITICAL, RK3588_PMU_CLKGATE_CON(1), 0, GFLAGS), GATE(CLK_DDR_FAIL_SAFE, "clk_ddr_fail_safe", "clk_pmu0", CLK_IGNORE_UNUSED, @@ -2439,6 +2449,7 @@ static struct rockchip_clk_branch rk3588_clk_branches[] = { static void __init rk3588_clk_early_init(struct device_node *np) { struct rockchip_clk_provider *ctx; + struct regmap *sys_grf; unsigned long clk_nr_clks, max_clk_id1, max_clk_id2; void __iomem *reg_base; @@ -2479,6 +2490,11 @@ static void __init rk3588_clk_early_init(struct device_node *np) &rk3588_cpub1clk_data, rk3588_cpub1clk_rates, ARRAY_SIZE(rk3588_cpub1clk_rates)); + /* Register SYS_GRF for I2S MCLK output to IO gate clocks */ + sys_grf = syscon_regmap_lookup_by_compatible("rockchip,rk3588-sys-grf"); + if (!IS_ERR(sys_grf)) + rockchip_clk_add_grf(ctx, sys_grf, grf_type_sys); + rockchip_clk_register_branches(ctx, rk3588_early_clk_branches, ARRAY_SIZE(rk3588_early_clk_branches)); diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index e8b3b0b9a4f8..ee8c79b938d3 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -429,6 +429,24 @@ void rockchip_clk_of_add_provider(struct device_node *np, } EXPORT_SYMBOL_GPL(rockchip_clk_of_add_provider); +int rockchip_clk_add_grf(struct rockchip_clk_provider *ctx, + struct regmap *grf, + enum rockchip_grf_type type) +{ + struct rockchip_aux_grf *aux_grf; + + aux_grf = kzalloc_obj(*aux_grf); + if (!aux_grf) + return -ENOMEM; + + aux_grf->grf = grf; + aux_grf->type = type; + hash_add(ctx->aux_grf_table, &aux_grf->node, type); + + return 0; +} +EXPORT_SYMBOL_GPL(rockchip_clk_add_grf); + void rockchip_clk_register_plls(struct rockchip_clk_provider *ctx, struct rockchip_pll_clock *list, unsigned int nr_pll, int grf_lock_offset) @@ -509,10 +527,9 @@ void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, clk = NULL; /* for GRF-dependent branches, choose the right grf first */ - if ((list->branch_type == branch_grf_mux || - list->branch_type == branch_grf_gate || - list->branch_type == branch_grf_mmc) && - list->grf_type != grf_type_sys) { + if (list->branch_type == branch_grf_mux || + list->branch_type == branch_grf_gate || + list->branch_type == branch_grf_mmc) { hash_for_each_possible(ctx->aux_grf_table, agrf, node, list->grf_type) { if (agrf->type == list->grf_type) { grf = agrf->grf; diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index cf0f5f11c34b..9e3503e2ffc2 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -1329,6 +1329,9 @@ struct rockchip_clk_provider *rockchip_clk_init_early(struct device_node *np, void rockchip_clk_finalize(struct rockchip_clk_provider *ctx); void rockchip_clk_of_add_provider(struct device_node *np, struct rockchip_clk_provider *ctx); +int rockchip_clk_add_grf(struct rockchip_clk_provider *ctx, + struct regmap *grf, + enum rockchip_grf_type type); unsigned long rockchip_clk_find_max_clk_id(struct rockchip_clk_branch *list, unsigned int nr_clk); void rockchip_clk_register_branches(struct rockchip_clk_provider *ctx, diff --git a/drivers/clk/samsung/clk-exynos850.c b/drivers/clk/samsung/clk-exynos850.c index eb9c80b60225..b143a42293f5 100644 --- a/drivers/clk/samsung/clk-exynos850.c +++ b/drivers/clk/samsung/clk-exynos850.c @@ -686,10 +686,11 @@ static const struct samsung_gate_clock apm_gate_clks[] __initconst = { CLK_CON_GAT_GOUT_APM_APBIF_RTC_PCLK, 21, 0, 0), GATE(CLK_GOUT_TOP_RTC_PCLK, "gout_top_rtc_pclk", "dout_apm_bus", CLK_CON_GAT_GOUT_APM_APBIF_TOP_RTC_PCLK, 21, 0, 0), + /* TODO: Should be dealt with or enabled in PMIC ACPM driver */ GATE(CLK_GOUT_I3C_PCLK, "gout_i3c_pclk", "dout_apm_bus", - CLK_CON_GAT_GOUT_APM_I3C_APM_PMIC_I_PCLK, 21, 0, 0), + CLK_CON_GAT_GOUT_APM_I3C_APM_PMIC_I_PCLK, 21, CLK_IS_CRITICAL, 0), GATE(CLK_GOUT_I3C_SCLK, "gout_i3c_sclk", "mout_apm_i3c", - CLK_CON_GAT_GOUT_APM_I3C_APM_PMIC_I_SCLK, 21, 0, 0), + CLK_CON_GAT_GOUT_APM_I3C_APM_PMIC_I_SCLK, 21, CLK_IS_CRITICAL, 0), GATE(CLK_GOUT_SPEEDY_PCLK, "gout_speedy_pclk", "dout_apm_bus", CLK_CON_GAT_GOUT_APM_SPEEDY_APM_PCLK, 21, 0, 0), /* TODO: Should be enabled in GPIO driver (or made CLK_IS_CRITICAL) */ diff --git a/drivers/clk/samsung/clk-exynos990.c b/drivers/clk/samsung/clk-exynos990.c index 6277dd557fab..4385c3b76dd6 100644 --- a/drivers/clk/samsung/clk-exynos990.c +++ b/drivers/clk/samsung/clk-exynos990.c @@ -1546,54 +1546,44 @@ static const unsigned long peric0_clk_regs[] __initconst = { /* Parent clock list for CMU_PERIC0 muxes */ PNAME(mout_peric0_bus_user_p) = { "oscclk", "dout_cmu_peric0_bus" }; -PNAME(mout_peric0_uart_dbg_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi00_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi01_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi02_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi03_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi04_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi05_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi13_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi14_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi15_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; -PNAME(mout_peric0_usi_i2c_user_p) = { "oscclk", "dout_cmu_peric0_ip" }; +PNAME(mout_peric0_nonbususer_p) = { "oscclk", "dout_cmu_peric0_ip" }; static const struct samsung_mux_clock peric0_mux_clks[] __initconst = { MUX(CLK_MOUT_PERIC0_BUS_USER, "mout_peric0_bus_user", mout_peric0_bus_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_BUS_USER, 4, 1), - MUX(CLK_MOUT_PERIC0_UART_DBG, "mout_peric0_uart_dbg", - mout_peric0_uart_dbg_p, PLL_CON0_MUX_CLKCMU_PERIC0_UART_DBG, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI00_USI_USER, "mout_peric0_usi00_usi_user", - mout_peric0_usi00_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI00_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI01_USI_USER, "mout_peric0_usi01_usi_user", - mout_peric0_usi01_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI01_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI02_USI_USER, "mout_peric0_usi02_usi_user", - mout_peric0_usi02_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI02_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI03_USI_USER, "mout_peric0_usi03_usi_user", - mout_peric0_usi03_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI03_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI04_USI_USER, "mout_peric0_usi04_usi_user", - mout_peric0_usi04_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI04_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI05_USI_USER, "mout_peric0_usi05_usi_user", - mout_peric0_usi05_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI05_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI13_USI_USER, "mout_peric0_usi13_usi_user", - mout_peric0_usi13_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI13_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI14_USI_USER, "mout_peric0_usi14_usi_user", - mout_peric0_usi14_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI14_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC0_USI15_USI_USER, "mout_peric0_usi15_usi_user", - mout_peric0_usi15_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI15_USI_USER, - 4, 1), + nMUX(CLK_MOUT_PERIC0_UART_DBG, "mout_peric0_uart_dbg", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_UART_DBG, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI00_USI_USER, "mout_peric0_usi00_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI00_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI01_USI_USER, "mout_peric0_usi01_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI01_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI02_USI_USER, "mout_peric0_usi02_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI02_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI03_USI_USER, "mout_peric0_usi03_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI03_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI04_USI_USER, "mout_peric0_usi04_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI04_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI05_USI_USER, "mout_peric0_usi05_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI05_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI13_USI_USER, "mout_peric0_usi13_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI13_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI14_USI_USER, "mout_peric0_usi14_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI14_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC0_USI15_USI_USER, "mout_peric0_usi15_usi_user", + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI15_USI_USER, + 4, 1), MUX(CLK_MOUT_PERIC0_USI_I2C_USER, "mout_peric0_usi_i2c_user", - mout_peric0_usi_i2c_user_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI_I2C_USER, + mout_peric0_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC0_USI_I2C_USER, 4, 1), }; @@ -1602,42 +1592,42 @@ static const struct samsung_div_clock peric0_div_clks[] __initconst = { "mout_peric0_uart_dbg", CLK_CON_DIV_DIV_CLK_PERIC0_UART_DBG, 0, 4), - DIV(CLK_DOUT_PERIC0_USI00_USI, "dout_peric0_usi00_usi", - "mout_peric0_usi00_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI00_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI01_USI, "dout_peric0_usi01_usi", - "mout_peric0_usi01_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI01_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI02_USI, "dout_peric0_usi02_usi", - "mout_peric0_usi02_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI02_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI03_USI, "dout_peric0_usi03_usi", - "mout_peric0_usi03_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI03_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI04_USI, "dout_peric0_usi04_usi", - "mout_peric0_usi04_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI04_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI05_USI, "dout_peric0_usi05_usi", - "mout_peric0_usi05_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI05_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI13_USI, "dout_peric0_usi13_usi", - "mout_peric0_usi13_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI13_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI14_USI, "dout_peric0_usi14_usi", - "mout_peric0_usi14_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI14_USI, - 0, 4), - DIV(CLK_DOUT_PERIC0_USI15_USI, "dout_peric0_usi15_usi", - "mout_peric0_usi15_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC0_USI15_USI, - 0, 4), + DIV_F(CLK_DOUT_PERIC0_USI00_USI, "dout_peric0_usi00_usi", + "mout_peric0_usi00_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI00_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI01_USI, "dout_peric0_usi01_usi", + "mout_peric0_usi01_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI01_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI02_USI, "dout_peric0_usi02_usi", + "mout_peric0_usi02_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI02_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI03_USI, "dout_peric0_usi03_usi", + "mout_peric0_usi03_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI03_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI04_USI, "dout_peric0_usi04_usi", + "mout_peric0_usi04_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI04_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI05_USI, "dout_peric0_usi05_usi", + "mout_peric0_usi05_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI05_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI13_USI, "dout_peric0_usi13_usi", + "mout_peric0_usi13_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI13_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI14_USI, "dout_peric0_usi14_usi", + "mout_peric0_usi14_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI14_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC0_USI15_USI, "dout_peric0_usi15_usi", + "mout_peric0_usi15_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC0_USI15_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), DIV(CLK_DOUT_PERIC0_USI_I2C, "dout_peric0_usi_i2c", "mout_peric0_usi_i2c_user", CLK_CON_DIV_DIV_CLK_PERIC0_USI_I2C, @@ -2107,58 +2097,47 @@ static const unsigned long peric1_clk_regs[] __initconst = { /* Parent clock list for CMU_PERIC1 muxes */ PNAME(mout_peric1_bus_user_p) = { "oscclk", "dout_cmu_peric1_bus" }; -PNAME(mout_peric1_uart_bt_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi06_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi07_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi08_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi09_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi10_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi11_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi12_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi18_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi16_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi17_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; -PNAME(mout_peric1_usi_i2c_user_p) = { "oscclk", "dout_cmu_peric1_ip" }; +PNAME(mout_peric1_nonbususer_p) = { "oscclk", "dout_cmu_peric1_ip" }; static const struct samsung_mux_clock peric1_mux_clks[] __initconst = { MUX(CLK_MOUT_PERIC1_BUS_USER, "mout_peric1_bus_user", mout_peric1_bus_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_BUS_USER, 4, 1), - MUX(CLK_MOUT_PERIC1_UART_BT_USER, "mout_peric1_uart_bt_user", - mout_peric1_uart_bt_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_UART_BT_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI06_USI_USER, "mout_peric1_usi06_usi_user", - mout_peric1_usi06_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI06_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI07_USI_USER, "mout_peric1_usi07_usi_user", - mout_peric1_usi07_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI07_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI08_USI_USER, "mout_peric1_usi08_usi_user", - mout_peric1_usi08_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI08_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI09_USI_USER, "mout_peric1_usi09_usi_user", - mout_peric1_usi09_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI09_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI10_USI_USER, "mout_peric1_usi10_usi_user", - mout_peric1_usi10_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI10_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI11_USI_USER, "mout_peric1_usi11_usi_user", - mout_peric1_usi11_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI11_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI12_USI_USER, "mout_peric1_usi12_usi_user", - mout_peric1_usi12_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI12_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI18_USI_USER, "mout_peric1_usi18_usi_user", - mout_peric1_usi18_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI18_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI16_USI_USER, "mout_peric1_usi16_usi_user", - mout_peric1_usi16_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI16_USI_USER, - 4, 1), - MUX(CLK_MOUT_PERIC1_USI17_USI_USER, "mout_peric1_usi17_usi_user", - mout_peric1_usi17_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI17_USI_USER, - 4, 1), + nMUX(CLK_MOUT_PERIC1_UART_BT_USER, "mout_peric1_uart_bt_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_UART_BT_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI06_USI_USER, "mout_peric1_usi06_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI06_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI07_USI_USER, "mout_peric1_usi07_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI07_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI08_USI_USER, "mout_peric1_usi08_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI08_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI09_USI_USER, "mout_peric1_usi09_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI09_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI10_USI_USER, "mout_peric1_usi10_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI10_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI11_USI_USER, "mout_peric1_usi11_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI11_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI12_USI_USER, "mout_peric1_usi12_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI12_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI18_USI_USER, "mout_peric1_usi18_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI18_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI16_USI_USER, "mout_peric1_usi16_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI16_USI_USER, + 4, 1), + nMUX(CLK_MOUT_PERIC1_USI17_USI_USER, "mout_peric1_usi17_usi_user", + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI17_USI_USER, + 4, 1), MUX(CLK_MOUT_PERIC1_USI_I2C_USER, "mout_peric1_usi_i2c_user", - mout_peric1_usi_i2c_user_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI_I2C_USER, + mout_peric1_nonbususer_p, PLL_CON0_MUX_CLKCMU_PERIC1_USI_I2C_USER, 4, 1), }; @@ -2167,46 +2146,46 @@ static const struct samsung_div_clock peric1_div_clks[] __initconst = { "mout_peric1_uart_bt_user", CLK_CON_DIV_DIV_CLK_PERIC1_UART_BT, 0, 4), - DIV(CLK_DOUT_PERIC1_USI06_USI, "dout_peric1_usi06_usi", - "mout_peric1_usi06_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI06_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI07_USI, "dout_peric1_usi07_usi", - "mout_peric1_usi07_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI07_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI08_USI, "dout_peric1_usi08_usi", - "mout_peric1_usi08_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI08_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI18_USI, "dout_peric1_usi18_usi", - "mout_peric1_usi18_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI18_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI12_USI, "dout_peric1_usi12_usi", - "mout_peric1_usi12_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI12_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI09_USI, "dout_peric1_usi09_usi", - "mout_peric1_usi09_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI09_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI10_USI, "dout_peric1_usi10_usi", - "mout_peric1_usi10_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI10_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI11_USI, "dout_peric1_usi11_usi", - "mout_peric1_usi11_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI11_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI16_USI, "dout_peric1_usi16_usi", - "mout_peric1_usi16_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI16_USI, - 0, 4), - DIV(CLK_DOUT_PERIC1_USI17_USI, "dout_peric1_usi17_usi", - "mout_peric1_usi17_usi_user", - CLK_CON_DIV_DIV_CLK_PERIC1_USI17_USI, - 0, 4), + DIV_F(CLK_DOUT_PERIC1_USI06_USI, "dout_peric1_usi06_usi", + "mout_peric1_usi06_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI06_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI07_USI, "dout_peric1_usi07_usi", + "mout_peric1_usi07_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI07_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI08_USI, "dout_peric1_usi08_usi", + "mout_peric1_usi08_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI08_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI18_USI, "dout_peric1_usi18_usi", + "mout_peric1_usi18_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI18_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI12_USI, "dout_peric1_usi12_usi", + "mout_peric1_usi12_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI12_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI09_USI, "dout_peric1_usi09_usi", + "mout_peric1_usi09_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI09_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI10_USI, "dout_peric1_usi10_usi", + "mout_peric1_usi10_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI10_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI11_USI, "dout_peric1_usi11_usi", + "mout_peric1_usi11_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI11_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI16_USI, "dout_peric1_usi16_usi", + "mout_peric1_usi16_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI16_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), + DIV_F(CLK_DOUT_PERIC1_USI17_USI, "dout_peric1_usi17_usi", + "mout_peric1_usi17_usi_user", + CLK_CON_DIV_DIV_CLK_PERIC1_USI17_USI, 0, 4, + CLK_SET_RATE_PARENT, 0), DIV(CLK_DOUT_PERIC1_USI_I2C, "dout_peric1_usi_i2c", "mout_peric1_usi_i2c_user", CLK_CON_DIV_DIV_CLK_PERIC1_USI_I2C, diff --git a/drivers/clk/socfpga/clk-agilex.c b/drivers/clk/socfpga/clk-agilex.c index 8dd94f64756b..2bdea1997b5e 100644 --- a/drivers/clk/socfpga/clk-agilex.c +++ b/drivers/clk/socfpga/clk-agilex.c @@ -259,6 +259,8 @@ static const struct stratix10_perip_cnt_clock agilex_main_perip_cnt_clks[] = { 0, 0x3C, 0, 0, 0}, { AGILEX_NOC_FREE_CLK, "noc_free_clk", NULL, noc_free_mux, ARRAY_SIZE(noc_free_mux), 0, 0x40, 0, 0, 0}, + { AGILEX_L3_MAIN_FREE_CLK, "l3_main_free_clk", "noc_free_clk", NULL, + 1, 0, 0, 1, 0, 0}, { AGILEX_L4_SYS_FREE_CLK, "l4_sys_free_clk", NULL, noc_mux, ARRAY_SIZE(noc_mux), 0, 0, 4, 0x30, 1}, { AGILEX_EMAC_A_FREE_CLK, "emaca_free_clk", NULL, emaca_free_mux, ARRAY_SIZE(emaca_free_mux), diff --git a/drivers/clk/spacemit/ccu-k3.c b/drivers/clk/spacemit/ccu-k3.c index bb8b75bdbdb3..cb0c4277f72a 100644 --- a/drivers/clk/spacemit/ccu-k3.c +++ b/drivers/clk/spacemit/ccu-k3.c @@ -947,16 +947,21 @@ static const struct clk_parent_data edp1_pclk_parents[] = { }; CCU_MUX_GATE_DEFINE(edp1_pxclk, edp1_pclk_parents, APMU_LCD_EDP_CTRL, 18, 1, BIT(17), 0); -CCU_GATE_DEFINE(pciea_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_A, BIT(2), 0); -CCU_GATE_DEFINE(pciea_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_A, BIT(1), 0); -CCU_GATE_DEFINE(pcieb_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_B, BIT(2), 0); -CCU_GATE_DEFINE(pcieb_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_B, BIT(1), 0); -CCU_GATE_DEFINE(pciec_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_C, BIT(2), 0); -CCU_GATE_DEFINE(pciec_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_C, BIT(1), 0); -CCU_GATE_DEFINE(pcied_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_D, BIT(2), 0); -CCU_GATE_DEFINE(pcied_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_D, BIT(1), 0); -CCU_GATE_DEFINE(pciee_mstr_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_E, BIT(2), 0); -CCU_GATE_DEFINE(pciee_slv_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_E, BIT(1), 0); +CCU_GATE_DEFINE(pciea_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_A, BIT(2), 0); +CCU_GATE_DEFINE(pciea_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_A, BIT(1), 0); +CCU_GATE_DEFINE(pciea_dbi_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_A, BIT(0), 0); +CCU_GATE_DEFINE(pcieb_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_B, BIT(2), 0); +CCU_GATE_DEFINE(pcieb_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_B, BIT(1), 0); +CCU_GATE_DEFINE(pcieb_dbi_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_B, BIT(0), 0); +CCU_GATE_DEFINE(pciec_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_C, BIT(2), 0); +CCU_GATE_DEFINE(pciec_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_C, BIT(1), 0); +CCU_GATE_DEFINE(pciec_dbi_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_C, BIT(0), 0); +CCU_GATE_DEFINE(pcied_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_D, BIT(2), 0); +CCU_GATE_DEFINE(pcied_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_D, BIT(1), 0); +CCU_GATE_DEFINE(pcied_dbi_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_D, BIT(0), 0); +CCU_GATE_DEFINE(pciee_mstr_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_E, BIT(2), 0); +CCU_GATE_DEFINE(pciee_slv_clk, CCU_PARENT_HW(pll2_d6), APMU_PCIE_CLK_RES_CTRL_E, BIT(1), 0); +CCU_GATE_DEFINE(pciee_dbi_clk, CCU_PARENT_HW(axi_clk), APMU_PCIE_CLK_RES_CTRL_E, BIT(0), 0); static const struct clk_parent_data emac_1588_parents[] = { CCU_PARENT_NAME(vctcxo_24m), @@ -1391,14 +1396,19 @@ static struct clk_hw *k3_ccu_apmu_hws[] = { [CLK_APMU_EDP1_PXCLK] = &edp1_pxclk.common.hw, [CLK_APMU_PCIE_PORTA_MSTE] = &pciea_mstr_clk.common.hw, [CLK_APMU_PCIE_PORTA_SLV] = &pciea_slv_clk.common.hw, + [CLK_APMU_PCIE_PORTA_DBI] = &pciea_dbi_clk.common.hw, [CLK_APMU_PCIE_PORTB_MSTE] = &pcieb_mstr_clk.common.hw, [CLK_APMU_PCIE_PORTB_SLV] = &pcieb_slv_clk.common.hw, + [CLK_APMU_PCIE_PORTB_DBI] = &pcieb_dbi_clk.common.hw, [CLK_APMU_PCIE_PORTC_MSTE] = &pciec_mstr_clk.common.hw, [CLK_APMU_PCIE_PORTC_SLV] = &pciec_slv_clk.common.hw, + [CLK_APMU_PCIE_PORTC_DBI] = &pciec_dbi_clk.common.hw, [CLK_APMU_PCIE_PORTD_MSTE] = &pcied_mstr_clk.common.hw, [CLK_APMU_PCIE_PORTD_SLV] = &pcied_slv_clk.common.hw, + [CLK_APMU_PCIE_PORTD_DBI] = &pcied_dbi_clk.common.hw, [CLK_APMU_PCIE_PORTE_MSTE] = &pciee_mstr_clk.common.hw, [CLK_APMU_PCIE_PORTE_SLV] = &pciee_slv_clk.common.hw, + [CLK_APMU_PCIE_PORTE_DBI] = &pciee_dbi_clk.common.hw, [CLK_APMU_EMAC0_BUS] = &emac0_bus_clk.common.hw, [CLK_APMU_EMAC0_REF] = &emac0_ref_clk.common.hw, [CLK_APMU_EMAC0_1588] = &emac0_1588_clk.common.hw, diff --git a/drivers/clk/visconti/pll.c b/drivers/clk/visconti/pll.c index 805b95481281..3ce1a906fb0c 100644 --- a/drivers/clk/visconti/pll.c +++ b/drivers/clk/visconti/pll.c @@ -21,9 +21,9 @@ struct visconti_pll { void __iomem *pll_base; spinlock_t *lock; unsigned long flags; - const struct visconti_pll_rate_table *rate_table; size_t rate_count; struct visconti_pll_provider *ctx; + struct visconti_pll_rate_table rate_table[] __counted_by(rate_count); }; #define PLL_CONF_REG 0x0000 @@ -255,10 +255,6 @@ static struct clk_hw *visconti_register_pll(struct visconti_pll_provider *ctx, size_t len; int ret; - pll = kzalloc_obj(*pll); - if (!pll) - return ERR_PTR(-ENOMEM); - init.name = name; init.flags = CLK_IGNORE_UNUSED; init.parent_names = &parent_name; @@ -266,11 +262,13 @@ static struct clk_hw *visconti_register_pll(struct visconti_pll_provider *ctx, for (len = 0; rate_table[len].rate != 0; ) len++; + + pll = kzalloc_flex(*pll, rate_table, len); + if (!pll) + return ERR_PTR(-ENOMEM); + pll->rate_count = len; - pll->rate_table = kmemdup_array(rate_table, - pll->rate_count, sizeof(*pll->rate_table), - GFP_KERNEL); - WARN(!pll->rate_table, "%s: could not allocate rate table for %s\n", __func__, name); + memcpy(pll->rate_table, rate_table, len * sizeof(*pll->rate_table)); init.ops = &visconti_pll_ops; pll->hw.init = &init; @@ -282,7 +280,6 @@ static struct clk_hw *visconti_register_pll(struct visconti_pll_provider *ctx, ret = clk_hw_register(NULL, &pll->hw); if (ret) { pr_err("failed to register pll clock %s : %d\n", name, ret); - kfree(pll->rate_table); kfree(pll); pll_hw_clk = ERR_PTR(ret); } diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c index 370664c47e65..e355b42043cf 100644 --- a/drivers/cpuidle/driver.c +++ b/drivers/cpuidle/driver.c @@ -195,14 +195,6 @@ static void __cpuidle_driver_init(struct cpuidle_driver *drv) s->exit_latency_ns = 0; else s->exit_latency = div_u64(s->exit_latency_ns, NSEC_PER_USEC); - - /* - * Warn if the exit latency of a CPU idle state exceeds its - * target residency which is assumed to never happen in cpuidle - * in multiple places. - */ - if (s->exit_latency_ns > s->target_residency_ns) - pr_warn("Idle state %d target residency too low\n", i); } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index 9783a3cefb04..da325863ad76 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -558,7 +558,7 @@ uint32_t amdgpu_amdkfd_get_max_engine_clock_in_mhz(struct amdgpu_device *adev) int amdgpu_amdkfd_get_dmabuf_info(struct amdgpu_device *adev, int dma_buf_fd, struct amdgpu_device **dmabuf_adev, - uint64_t *bo_size, void *metadata_buffer, + uint64_t *bo_size, void **metadata_buffer, size_t buffer_size, uint32_t *metadata_size, uint32_t *flags, int8_t *xcp_id) { @@ -593,9 +593,24 @@ int amdgpu_amdkfd_get_dmabuf_info(struct amdgpu_device *adev, int dma_buf_fd, *dmabuf_adev = adev; if (bo_size) *bo_size = amdgpu_bo_size(bo); - if (metadata_buffer) - r = amdgpu_bo_get_metadata(bo, metadata_buffer, buffer_size, - metadata_size, &metadata_flags); + if (metadata_buffer) { + /* first get metadata_size by buffer = NULL */ + r = amdgpu_bo_get_metadata(bo, NULL, 0, + metadata_size, NULL); + + /* user buf_size is bigger than bo metadata_size + * allocate a buf at kernel space and copy */ + if (*metadata_size <= buffer_size) { + *metadata_buffer = kzalloc(*metadata_size, GFP_KERNEL); + + if (!*metadata_buffer) + return -ENOMEM; + + r = amdgpu_bo_get_metadata(bo, *metadata_buffer, *metadata_size, + NULL, &metadata_flags); + } else + r = -EINVAL; + } if (flags) { *flags = (bo->preferred_domains & AMDGPU_GEM_DOMAIN_VRAM) ? KFD_IOC_ALLOC_MEM_FLAGS_VRAM diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h index 5333e052d56d..e443a7277299 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h @@ -262,7 +262,7 @@ uint64_t amdgpu_amdkfd_get_gpu_clock_counter(struct amdgpu_device *adev); uint32_t amdgpu_amdkfd_get_max_engine_clock_in_mhz(struct amdgpu_device *adev); int amdgpu_amdkfd_get_dmabuf_info(struct amdgpu_device *adev, int dma_buf_fd, struct amdgpu_device **dmabuf_adev, - uint64_t *bo_size, void *metadata_buffer, + uint64_t *bo_size, void **metadata_buffer, size_t buffer_size, uint32_t *metadata_size, uint32_t *flags, int8_t *xcp_id); int amdgpu_amdkfd_get_pcie_bandwidth_mbytes(struct amdgpu_device *adev, bool is_min); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index d54794e5b18b..35fe2c974699 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -1914,13 +1914,6 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu( mutex_lock(&mem->lock); - /* Unpin MMIO/DOORBELL BO's that were pinned during allocation */ - if (mem->alloc_flags & - (KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL | - KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP)) { - amdgpu_amdkfd_gpuvm_unpin_bo(mem->bo); - } - mapped_to_gpu_memory = mem->mapped_to_gpu_memory; is_imported = mem->is_imported; mutex_unlock(&mem->lock); @@ -1934,6 +1927,15 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu( return -EBUSY; } + /* At this point the BO is guaranteed to be freed, so unpin the + * MMIO/DOORBELL BOs that were pinned during allocation. + */ + if (mem->alloc_flags & + (KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL | + KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP)) { + amdgpu_amdkfd_gpuvm_unpin_bo(mem->bo); + } + /* Make sure restore workers don't access the BO any more */ mutex_lock(&process_info->lock); if (!list_empty(&mem->validate_list)) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 115b134b4cd1..c2e6495a28bc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -247,13 +247,17 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p, goto free_partial_kdata; break; + case AMDGPU_CHUNK_ID_CP_GFX_SHADOW: + if (size < sizeof(struct drm_amdgpu_cs_chunk_cp_gfx_shadow)) + goto free_partial_kdata; + break; + case AMDGPU_CHUNK_ID_DEPENDENCIES: case AMDGPU_CHUNK_ID_SYNCOBJ_IN: case AMDGPU_CHUNK_ID_SYNCOBJ_OUT: case AMDGPU_CHUNK_ID_SCHEDULED_DEPENDENCIES: case AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_WAIT: case AMDGPU_CHUNK_ID_SYNCOBJ_TIMELINE_SIGNAL: - case AMDGPU_CHUNK_ID_CP_GFX_SHADOW: break; default: diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c index 0d7f6cd74f79..ce35b415093d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c @@ -326,7 +326,6 @@ static int amdgpu_ctx_init(struct amdgpu_ctx_mgr *mgr, int32_t priority, struct drm_file *filp, struct amdgpu_ctx *ctx) { struct amdgpu_fpriv *fpriv = filp->driver_priv; - u32 current_stable_pstate; int r; r = amdgpu_ctx_priority_permit(filp, priority); @@ -344,36 +343,21 @@ static int amdgpu_ctx_init(struct amdgpu_ctx_mgr *mgr, int32_t priority, ctx->generation = amdgpu_vm_generation(mgr->adev, &fpriv->vm); ctx->init_priority = priority; ctx->override_priority = AMDGPU_CTX_PRIORITY_UNSET; - - r = amdgpu_ctx_get_stable_pstate(ctx, ¤t_stable_pstate); - if (r) - return r; - - if (mgr->adev->pm.stable_pstate_ctx) - ctx->stable_pstate = mgr->adev->pm.stable_pstate_ctx->stable_pstate; - else - ctx->stable_pstate = current_stable_pstate; + ctx->stable_pstate = AMDGPU_CTX_STABLE_PSTATE_NONE; return 0; } -static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, - u32 stable_pstate) +static int __amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, + u32 stable_pstate) { struct amdgpu_device *adev = ctx->mgr->adev; enum amd_dpm_forced_level level; + struct amdgpu_ctx *current_ctx; u32 current_stable_pstate; - int r; + int r = 0; - mutex_lock(&adev->pm.stable_pstate_ctx_lock); - if (adev->pm.stable_pstate_ctx && adev->pm.stable_pstate_ctx != ctx) { - r = -EBUSY; - goto done; - } - - r = amdgpu_ctx_get_stable_pstate(ctx, ¤t_stable_pstate); - if (r || (stable_pstate == current_stable_pstate)) - goto done; + lockdep_assert_held(&adev->pm.stable_pstate_ctx_lock); switch (stable_pstate) { case AMDGPU_CTX_STABLE_PSTATE_NONE: @@ -392,17 +376,41 @@ static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, level = AMD_DPM_FORCED_LEVEL_PROFILE_PEAK; break; default: - r = -EINVAL; - goto done; + return -EINVAL; } + current_ctx = adev->pm.stable_pstate_ctx; + if (current_ctx && current_ctx != ctx) + return -EBUSY; + + r = amdgpu_ctx_get_stable_pstate(ctx, ¤t_stable_pstate); + if (r || current_stable_pstate == stable_pstate) + return r; + r = amdgpu_dpm_force_performance_level(adev, level); + if (r) + return r; - if (level == AMD_DPM_FORCED_LEVEL_AUTO) - adev->pm.stable_pstate_ctx = NULL; - else + if (!current_ctx) { adev->pm.stable_pstate_ctx = ctx; -done: + /* + * Serialized by context taking ownership for the first time + * while holding adev->pm.stable_pstate_ctx_lock). + */ + WRITE_ONCE(ctx->stable_pstate, current_stable_pstate); + } + + return 0; +} + +static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, + u32 stable_pstate) +{ + struct amdgpu_device *adev = ctx->mgr->adev; + int r; + + mutex_lock(&adev->pm.stable_pstate_ctx_lock); + r = __amdgpu_ctx_set_stable_pstate(ctx, stable_pstate); mutex_unlock(&adev->pm.stable_pstate_ctx_lock); return r; @@ -428,7 +436,12 @@ static void amdgpu_ctx_fini(struct kref *ref) } if (drm_dev_enter(adev_to_drm(adev), &idx)) { - amdgpu_ctx_set_stable_pstate(ctx, ctx->stable_pstate); + mutex_lock(&adev->pm.stable_pstate_ctx_lock); + if (adev->pm.stable_pstate_ctx == ctx) { + __amdgpu_ctx_set_stable_pstate(ctx, ctx->stable_pstate); + adev->pm.stable_pstate_ctx = NULL; + } + mutex_unlock(&adev->pm.stable_pstate_ctx_lock); drm_dev_exit(idx); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index 27830518a230..e77db76b48b8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -22,8 +22,9 @@ * */ -#include <generated/utsrelease.h> #include <linux/devcoredump.h> +#include <linux/utsname.h> +#include <drm/drm_exec.h> #include "amdgpu_dev_coredump.h" #include "atom.h" @@ -207,28 +208,143 @@ static void amdgpu_devcoredump_fw_info(struct amdgpu_device *adev, } } +static void +amdgpu_devcoredump_print_ibs(struct drm_printer *p, + struct amdgpu_coredump_info *coredump, + bool sizing_pass) +{ + struct amdgpu_device *adev = coredump->adev; + struct amdgpu_bo_va_mapping *mapping; + struct amdgpu_bo *abo; + struct drm_exec exec; + struct amdgpu_vm *vm; + u32 *ib_content; + u64 va_start, offset; + u8 *kptr; + u32 off; + int r; + + /* + * On the sizing pass there is no VM to look up and no BO to lock; the + * size estimate doesn't depend on whether the IB BOs are reachable. + * Just emit the per-IB headers (the content is not written anywhere). + */ + if (sizing_pass) { + for (int i = 0; i < coredump->num_ibs; i++) { + drm_printf(p, "\nIB #%d 0x%llx %d dw\n", i, + coredump->ibs[i].gpu_addr, + coredump->ibs[i].ib_size_dw); + } + return; + } + + /* + * Lock the VM root PD and every IB BO together in a single drm_exec + * ticket. Reserving the IB BOs one by one while the root PD is held + * would be a recursive reservation_ww_class_mutex acquire without a + * ww_acquire_ctx, which trips lockdep and self-deadlocks for IB BOs + * that share their dma_resv with the root PD (always-valid BOs). + */ + drm_exec_init(&exec, DRM_EXEC_IGNORE_DUPLICATES, 1 + coredump->num_ibs); + drm_exec_until_all_locked(&exec) { + vm = amdgpu_vm_lock_by_pasid(adev, coredump->pasid, &exec); + if (!vm) + goto unlock; + + for (int i = 0; i < coredump->num_ibs; i++) { + u64 pfn = (coredump->ibs[i].gpu_addr & + AMDGPU_GMC_HOLE_MASK) / AMDGPU_GPU_PAGE_SIZE; + + mapping = amdgpu_vm_bo_lookup_mapping(vm, pfn); + if (!mapping) + continue; + + abo = mapping->bo_va->base.bo; + r = drm_exec_lock_obj(&exec, &abo->tbo.base); + drm_exec_retry_on_contention(&exec); + if (r) + goto unlock; + } + } + + for (int i = 0; i < coredump->num_ibs; i++) { + bool emit_content = false; + + ib_content = kvmalloc_array(coredump->ibs[i].ib_size_dw, 4, + GFP_KERNEL); + if (!ib_content) + continue; + + va_start = coredump->ibs[i].gpu_addr & AMDGPU_GMC_HOLE_MASK; + mapping = amdgpu_vm_bo_lookup_mapping(vm, + va_start / AMDGPU_GPU_PAGE_SIZE); + if (!mapping) + goto output_ib_content; + + abo = mapping->bo_va->base.bo; + offset = va_start - mapping->start * AMDGPU_GPU_PAGE_SIZE; + + if (abo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS) { + struct amdgpu_res_cursor cursor; + + off = 0; + + if (abo->tbo.resource->mem_type != TTM_PL_VRAM) + goto output_ib_content; + + amdgpu_res_first(abo->tbo.resource, offset, + coredump->ibs[i].ib_size_dw * 4, &cursor); + while (cursor.remaining) { + amdgpu_device_mm_access(adev, cursor.start / 4, + &ib_content[off], cursor.size / 4, + false); + off += cursor.size; + amdgpu_res_next(&cursor, cursor.size); + } + emit_content = true; + } else { + r = ttm_bo_kmap(&abo->tbo, 0, PFN_UP(abo->tbo.base.size), + &abo->kmap); + if (r) + goto output_ib_content; + + kptr = amdgpu_bo_kptr(abo); + kptr += offset; + memcpy(ib_content, kptr, coredump->ibs[i].ib_size_dw * 4); + + amdgpu_bo_kunmap(abo); + emit_content = true; + } + +output_ib_content: + drm_printf(p, "\nIB #%d 0x%llx %d dw\n", i, + coredump->ibs[i].gpu_addr, coredump->ibs[i].ib_size_dw); + if (emit_content) { + for (int j = 0; j < coredump->ibs[i].ib_size_dw; j++) + drm_printf(p, "0x%08x\n", ib_content[j]); + } + kvfree(ib_content); + } + +unlock: + drm_exec_fini(&exec); +} + static ssize_t amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_info *coredump) { - struct amdgpu_device *adev = coredump->adev; struct drm_printer p; struct drm_print_iterator iter; struct amdgpu_vm_fault_info *fault_info; - struct amdgpu_bo_va_mapping *mapping; struct amdgpu_ip_block *ip_block; - struct amdgpu_res_cursor cursor; - struct amdgpu_bo *abo, *root; - uint64_t va_start, offset; struct amdgpu_ring *ring; - struct amdgpu_vm *vm; - u32 *ib_content; - uint8_t *kptr; - int ver, i, j, r; + int ver, i, j; u32 ring_idx, off; bool sizing_pass; sizing_pass = buffer == NULL; iter.data = buffer; + iter.start = 0; iter.offset = 0; iter.remain = count; @@ -236,7 +352,7 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf drm_printf(&p, "**** AMDGPU Device Coredump ****\n"); drm_printf(&p, "version: " AMDGPU_COREDUMP_VERSION "\n"); - drm_printf(&p, "kernel: " UTS_RELEASE "\n"); + drm_printf(&p, "kernel: %s\n", init_utsname()->release); drm_printf(&p, "module: " KBUILD_MODNAME "\n"); drm_printf(&p, "time: %ptSp\n", &coredump->reset_time); @@ -342,86 +458,8 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf else if (coredump->reset_vram_lost) drm_printf(&p, "VRAM is lost due to GPU reset!\n"); - if (coredump->num_ibs) { - /* Don't try to lookup the VM or map the BOs when calculating the - * size required to store the devcoredump. - */ - if (sizing_pass) - vm = NULL; - else - vm = amdgpu_vm_lock_by_pasid(adev, &root, coredump->pasid); - - for (int i = 0; i < coredump->num_ibs && (sizing_pass || vm); i++) { - ib_content = kvmalloc_array(coredump->ibs[i].ib_size_dw, 4, - GFP_KERNEL); - if (!ib_content) - continue; - - /* vm=NULL can only happen when 'sizing_pass' is true. Skip to the - * drm_printf() calls (ib_content doesn't need to be initialized - * as its content won't be written anywhere). - */ - if (!vm) - goto output_ib_content; - - va_start = coredump->ibs[i].gpu_addr & AMDGPU_GMC_HOLE_MASK; - mapping = amdgpu_vm_bo_lookup_mapping(vm, va_start / AMDGPU_GPU_PAGE_SIZE); - if (!mapping) - goto free_ib_content; - - offset = va_start - (mapping->start * AMDGPU_GPU_PAGE_SIZE); - abo = amdgpu_bo_ref(mapping->bo_va->base.bo); - r = amdgpu_bo_reserve(abo, false); - if (r) - goto free_ib_content; - - if (abo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS) { - off = 0; - - if (abo->tbo.resource->mem_type != TTM_PL_VRAM) - goto unreserve_abo; - - amdgpu_res_first(abo->tbo.resource, offset, - coredump->ibs[i].ib_size_dw * 4, - &cursor); - while (cursor.remaining) { - amdgpu_device_mm_access(adev, cursor.start / 4, - &ib_content[off], cursor.size / 4, - false); - off += cursor.size; - amdgpu_res_next(&cursor, cursor.size); - } - } else { - r = ttm_bo_kmap(&abo->tbo, 0, - PFN_UP(abo->tbo.base.size), - &abo->kmap); - if (r) - goto unreserve_abo; - - kptr = amdgpu_bo_kptr(abo); - kptr += offset; - memcpy(ib_content, kptr, - coredump->ibs[i].ib_size_dw * 4); - - amdgpu_bo_kunmap(abo); - } - -output_ib_content: - drm_printf(&p, "\nIB #%d 0x%llx %d dw\n", - i, coredump->ibs[i].gpu_addr, coredump->ibs[i].ib_size_dw); - for (int j = 0; j < coredump->ibs[i].ib_size_dw; j++) - drm_printf(&p, "0x%08x\n", ib_content[j]); -unreserve_abo: - if (vm) - amdgpu_bo_unreserve(abo); -free_ib_content: - kvfree(ib_content); - } - if (vm) { - amdgpu_bo_unreserve(root); - amdgpu_bo_unref(&root); - } - } + if (coredump->num_ibs) + amdgpu_devcoredump_print_ibs(&p, coredump, sizing_pass); return count - iter.remain; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 942f0251c748..211d30f03d25 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -3043,7 +3043,7 @@ static int amdgpu_device_ip_suspend_phase2(struct amdgpu_device *adev) amdgpu_dpm_gfx_state_change(adev, sGpuChangeState_D3Entry); for (i = adev->num_ip_blocks - 1; i >= 0; i--) { - if (!adev->ip_blocks[i].status.valid) + if (!adev->ip_blocks[i].status.valid || !adev->ip_blocks[i].status.hw) continue; /* displays are handled in phase1 */ if (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_DCE) @@ -3771,6 +3771,8 @@ int amdgpu_device_init(struct amdgpu_device *adev, mutex_init(&adev->gfx.workload_profile_mutex); mutex_init(&adev->vcn.workload_profile_mutex); + spin_lock_init(&adev->irq.lock); + amdgpu_device_init_apu_flags(adev); r = amdgpu_device_check_arguments(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 212c14d99f6b..76da3f932f24 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -1094,6 +1094,11 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, * If that number is larger than the size of the array, the ioctl must * be retried. */ + if (!bo_va) { + r = -ENOENT; + goto out_exec; + } + if (args->num_entries > INT_MAX / sizeof(*vm_entries)) { r = -EINVAL; goto out_exec; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c index 1e190fb54a97..85372af1216d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c @@ -1664,12 +1664,13 @@ static int amdgpu_gfx_run_cleaner_shader_job(struct amdgpu_ring *ring) struct amdgpu_device *adev = ring->adev; struct drm_gpu_scheduler *sched = &ring->sched; struct drm_sched_entity entity; + unsigned int ib_size_dw = 16; static atomic_t counter; struct dma_fence *f; struct amdgpu_job *job; struct amdgpu_ib *ib; void *owner; - int i, r; + int r; /* Initialize the scheduler entity */ r = drm_sched_entity_init(&entity, DRM_SCHED_PRIORITY_NORMAL, @@ -1687,7 +1688,7 @@ static int amdgpu_gfx_run_cleaner_shader_job(struct amdgpu_ring *ring) owner = (void *)(unsigned long)atomic_inc_return(&counter); r = amdgpu_job_alloc_with_ib(ring->adev, &entity, owner, - 64, 0, &job, + ib_size_dw * sizeof(uint32_t), 0, &job, AMDGPU_KERNEL_JOB_ID_CLEANER_SHADER); if (r) goto err; @@ -1697,9 +1698,8 @@ static int amdgpu_gfx_run_cleaner_shader_job(struct amdgpu_ring *ring) job->run_cleaner_shader = true; ib = &job->ibs[0]; - for (i = 0; i <= ring->funcs->align_mask; ++i) - ib->ptr[i] = ring->funcs->nop; - ib->length_dw = ring->funcs->align_mask + 1; + memset32(ib->ptr, ring->funcs->nop, ib_size_dw); + ib->length_dw = ib_size_dw; f = amdgpu_job_submit(job); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c index d23a91d029aa..0ea32561c4bc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c @@ -272,7 +272,20 @@ static bool amdgpu_gtt_mgr_intersects(struct ttm_resource_manager *man, const struct ttm_place *place, size_t size) { - return !place->lpfn || amdgpu_gtt_mgr_has_gart_addr(res); + const struct drm_mm_node *const node = &to_ttm_range_mgr_node(res)->mm_nodes[0]; + const u32 num_pages = PFN_UP(size); + + if (!place->lpfn) + return true; + + if (!amdgpu_gtt_mgr_has_gart_addr(res)) + return false; + + if (place->fpfn >= (node->start + num_pages) || + (place->lpfn && place->lpfn <= node->start)) + return false; + + return true; } /** @@ -290,7 +303,20 @@ static bool amdgpu_gtt_mgr_compatible(struct ttm_resource_manager *man, const struct ttm_place *place, size_t size) { - return !place->lpfn || amdgpu_gtt_mgr_has_gart_addr(res); + const struct drm_mm_node *const node = &to_ttm_range_mgr_node(res)->mm_nodes[0]; + const u32 num_pages = PFN_UP(size); + + if (!place->lpfn) + return true; + + if (!amdgpu_gtt_mgr_has_gart_addr(res)) + return false; + + if (node->start < place->fpfn || + (place->lpfn && (node->start + num_pages) > place->lpfn)) + return false; + + return true; } /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c index 254a4e983f40..53be764968e4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c @@ -309,8 +309,6 @@ int amdgpu_irq_init(struct amdgpu_device *adev) unsigned int irq, flags; int r; - spin_lock_init(&adev->irq.lock); - /* Enable MSI if not disabled by module parameter */ adev->irq.msi_enabled = false; @@ -547,7 +545,7 @@ void amdgpu_irq_delegate(struct amdgpu_device *adev, unsigned int num_dw) { amdgpu_ih_ring_write(adev, &adev->irq.ih_soft, entry->iv_entry, num_dw); - schedule_work(&adev->irq.ih_soft_work); + queue_work(system_unbound_wq, &adev->irq.ih_soft_work); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c index d5d71fd7c70d..61450af539a6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_lockdep.c @@ -16,6 +16,17 @@ #ifdef CONFIG_LOCKDEP +struct amdgpu_lockdep_dummy_locks { + struct mutex reset_lock; + struct mutex userq_sch_mutex; + struct mutex userq_mutex; + struct mutex notifier_lock; + struct mutex vram_lock; + struct mutex srbm_mutex; + struct mutex grbm_idx_mutex; + spinlock_t mmio_idx_lock; +}; + /* Lock class keys for associating with real driver locks */ static struct lock_class_key amdgpu_userq_sch_mutex_key; static struct lock_class_key amdgpu_userq_mutex_key; @@ -84,72 +95,65 @@ void amdgpu_lockdep_set_class(struct amdgpu_device *adev) int amdgpu_lockdep_init(void) { struct amdgpu_reset_domain *reset_domain = NULL; - struct amdgpu_reset_control reset_ctl; - struct mutex userq_sch_mutex; - struct mutex userq_mutex; - struct mutex notifier_lock; - struct mutex vram_lock; - struct mutex srbm_mutex; - struct mutex grbm_idx_mutex; - spinlock_t mmio_idx_lock; + struct amdgpu_lockdep_dummy_locks *locks; unsigned long flags; + locks = kzalloc(sizeof(*locks), GFP_KERNEL); + if (!locks) + return -ENOMEM; + /* * Initialize dummy reset domain */ reset_domain = amdgpu_reset_create_reset_domain(SINGLE_DEVICE, "lockdep_test"); - if (!reset_domain) + if (!reset_domain) { + kfree(locks); return -ENOMEM; - + } /* Initialize dummy locks */ - mutex_init(&userq_sch_mutex); - mutex_init(&userq_mutex); - mutex_init(¬ifier_lock); - mutex_init(&vram_lock); - mutex_init(&reset_ctl.reset_lock); - mutex_init(&srbm_mutex); - mutex_init(&grbm_idx_mutex); - spin_lock_init(&mmio_idx_lock); + mutex_init(&locks->userq_sch_mutex); + mutex_init(&locks->userq_mutex); + mutex_init(&locks->notifier_lock); + mutex_init(&locks->vram_lock); + mutex_init(&locks->reset_lock); + mutex_init(&locks->srbm_mutex); + mutex_init(&locks->grbm_idx_mutex); + spin_lock_init(&locks->mmio_idx_lock); /* * Associate dummy locks with the same class keys used for real * driver locks. This ensures lockdep connects the ordering learned * here with the actual locks used at runtime. */ - lockdep_set_class(&userq_sch_mutex, &amdgpu_userq_sch_mutex_key); - lockdep_set_class(&userq_mutex, &amdgpu_userq_mutex_key); - lockdep_set_class(¬ifier_lock, &amdgpu_notifier_lock_key); - lockdep_set_class(&vram_lock, &amdgpu_vram_lock_key); + lockdep_set_class(&locks->userq_sch_mutex, &amdgpu_userq_sch_mutex_key); + lockdep_set_class(&locks->userq_mutex, &amdgpu_userq_mutex_key); + lockdep_set_class(&locks->notifier_lock, &amdgpu_notifier_lock_key); + lockdep_set_class(&locks->vram_lock, &amdgpu_vram_lock_key); lockdep_set_class(&reset_domain->sem, &amdgpu_reset_sem_key); - lockdep_set_class(&reset_ctl.reset_lock, &amdgpu_reset_lock_key); - lockdep_set_class(&srbm_mutex, &amdgpu_srbm_lock_key); - lockdep_set_class(&grbm_idx_mutex, &amdgpu_grbm_lock_key); - lockdep_set_class(&mmio_idx_lock, &amdgpu_mmio_lock_key); - + lockdep_set_class(&locks->reset_lock, &amdgpu_reset_lock_key); + lockdep_set_class(&locks->srbm_mutex, &amdgpu_srbm_lock_key); + lockdep_set_class(&locks->grbm_idx_mutex, &amdgpu_grbm_lock_key); + lockdep_set_class(&locks->mmio_idx_lock, &amdgpu_mmio_lock_key); /* * Take locks in the correct order to train lockdep. * This establishes the dependency chain. */ /* Level 1: Global userq scheduler mutex (outermost) */ - mutex_lock(&userq_sch_mutex); + mutex_lock(&locks->userq_sch_mutex); /* Level 2: Per-context userq mutex */ - mutex_lock(&userq_mutex); - + mutex_lock(&locks->userq_mutex); /* Level 3: MMU notifier lock */ - mutex_lock(¬ifier_lock); - + mutex_lock(&locks->notifier_lock); /* Level 4: VRAM allocator lock */ - mutex_lock(&vram_lock); - + mutex_lock(&locks->vram_lock); /* Level 5: Reset domain semaphore */ down_read(&reset_domain->sem); /* Level 6: Reset control lock */ - mutex_lock(&reset_ctl.reset_lock); - + mutex_lock(&locks->reset_lock); /* * Mark potential memory reclaim boundary. * GPU operations might trigger memory allocation/reclaim. @@ -157,36 +161,35 @@ int amdgpu_lockdep_init(void) fs_reclaim_acquire(GFP_KERNEL); /* Level 7: SRBM register access */ - mutex_lock(&srbm_mutex); - + mutex_lock(&locks->srbm_mutex); /* Level 8: GRBM index access */ - mutex_lock(&grbm_idx_mutex); + mutex_lock(&locks->grbm_idx_mutex); /* Level 9: MMIO index access (innermost lock, spinlock) */ - spin_lock_irqsave(&mmio_idx_lock, flags); - + spin_lock_irqsave(&locks->mmio_idx_lock, flags); /* * All locks acquired in order. * Lockdep has now learned the valid dependency chain. */ /* Release in reverse order */ - spin_unlock_irqrestore(&mmio_idx_lock, flags); - mutex_unlock(&grbm_idx_mutex); - mutex_unlock(&srbm_mutex); - + spin_unlock_irqrestore(&locks->mmio_idx_lock, flags); + mutex_unlock(&locks->grbm_idx_mutex); + mutex_unlock(&locks->srbm_mutex); fs_reclaim_release(GFP_KERNEL); - mutex_unlock(&reset_ctl.reset_lock); + mutex_unlock(&locks->reset_lock); up_read(&reset_domain->sem); - mutex_unlock(&vram_lock); - mutex_unlock(¬ifier_lock); - mutex_unlock(&userq_mutex); - mutex_unlock(&userq_sch_mutex); + + mutex_unlock(&locks->vram_lock); + mutex_unlock(&locks->notifier_lock); + mutex_unlock(&locks->userq_mutex); + mutex_unlock(&locks->userq_sch_mutex); /* Cleanup */ amdgpu_reset_put_reset_domain(reset_domain); + kfree(locks); pr_info("AMDGPU: Lockdep annotations initialized (9 lock levels)\n"); return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 2740de94e93c..16c060badaee 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -515,6 +515,15 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict, if (new_mem->mem_type == TTM_PL_TT || new_mem->mem_type == AMDGPU_PL_PREEMPT) { + if (old_mem && (old_mem->mem_type == TTM_PL_TT || + old_mem->mem_type == AMDGPU_PL_PREEMPT)) { + r = ttm_bo_wait_ctx(bo, ctx); + if (r) + return r; + + amdgpu_ttm_backend_unbind(bo->bdev, bo->ttm); + } + r = amdgpu_ttm_backend_bind(bo->bdev, bo->ttm, new_mem); if (r) return r; @@ -549,6 +558,15 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo, bool evict, ttm_bo_assign_mem(bo, new_mem); return 0; } + if ((old_mem->mem_type == TTM_PL_TT || + old_mem->mem_type == AMDGPU_PL_PREEMPT) && + (new_mem->mem_type == TTM_PL_TT || + new_mem->mem_type == AMDGPU_PL_PREEMPT)) { + amdgpu_bo_move_notify(bo, evict, new_mem); + ttm_resource_free(bo, &bo->resource); + ttm_bo_assign_mem(bo, new_mem); + return 0; + } if (old_mem->mem_type == AMDGPU_PL_GDS || old_mem->mem_type == AMDGPU_PL_GWS || diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 3a3bc0d370fa..480bf88def46 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -135,7 +135,7 @@ MODULE_FIRMWARE(FIRMWARE_VEGA12); MODULE_FIRMWARE(FIRMWARE_VEGA20); static void amdgpu_uvd_idle_work_handler(struct work_struct *work); -static void amdgpu_uvd_force_into_uvd_segment(struct amdgpu_bo *abo); +static void amdgpu_uvd_force_into_vcpu_segment(struct amdgpu_bo *abo); static int amdgpu_uvd_create_msg_bo_helper(struct amdgpu_device *adev, uint32_t size, @@ -158,7 +158,7 @@ static int amdgpu_uvd_create_msg_bo_helper(struct amdgpu_device *adev, amdgpu_bo_kunmap(bo); amdgpu_bo_unpin(bo); amdgpu_bo_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_VRAM); - amdgpu_uvd_force_into_uvd_segment(bo); + amdgpu_uvd_force_into_vcpu_segment(bo); r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); if (r) goto err; @@ -188,6 +188,7 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev) const struct common_firmware_header *hdr; unsigned int family_id; int i, j, r; + u32 vcpu_bo_domain; INIT_DELAYED_WORK(&adev->uvd.idle_work, amdgpu_uvd_idle_work_handler); @@ -319,12 +320,20 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev) if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) bo_size += AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8); + /* UVD 5.0 and newer HW can use 64 bit addressing. */ + adev->uvd.address_64_bit = + !amdgpu_device_ip_block_version_cmp(adev, AMD_IP_BLOCK_TYPE_UVD, 5, 0); + + vcpu_bo_domain = AMDGPU_GEM_DOMAIN_VRAM; + if (adev->uvd.address_64_bit) + vcpu_bo_domain |= AMDGPU_GEM_DOMAIN_GTT; + for (j = 0; j < adev->uvd.num_uvd_inst; j++) { if (adev->uvd.harvest_config & (1 << j)) continue; + r = amdgpu_bo_create_kernel(adev, bo_size, PAGE_SIZE, - AMDGPU_GEM_DOMAIN_VRAM | - AMDGPU_GEM_DOMAIN_GTT, + vcpu_bo_domain, &adev->uvd.inst[j].vcpu_bo, &adev->uvd.inst[j].gpu_addr, &adev->uvd.inst[j].cpu_addr); @@ -339,10 +348,6 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev) adev->uvd.filp[i] = NULL; } - /* from uvd v5.0 HW addressing capacity increased to 64 bits */ - if (!amdgpu_device_ip_block_version_cmp(adev, AMD_IP_BLOCK_TYPE_UVD, 5, 0)) - adev->uvd.address_64_bit = true; - r = amdgpu_uvd_create_msg_bo_helper(adev, 128 << 10, &adev->uvd.ib_bo); if (r) return r; @@ -545,6 +550,24 @@ void amdgpu_uvd_free_handles(struct amdgpu_device *adev, struct drm_file *filp) } } +static void amdgpu_uvd_force_into_vcpu_segment(struct amdgpu_bo *bo) +{ + struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); + struct amdgpu_bo *vcpu_bo = adev->uvd.inst[0].vcpu_bo; + struct amdgpu_res_cursor vcpu_cur; + + amdgpu_res_first(vcpu_bo->tbo.resource, 0, + amdgpu_bo_size(vcpu_bo), &vcpu_cur); + + bo->placement.num_placement = 1; + bo->placement.placement = &bo->placements[0]; + bo->placements[0].fpfn = ALIGN_DOWN(vcpu_cur.start, SZ_256M) >> PAGE_SHIFT; + bo->placements[0].lpfn = bo->placements[0].fpfn + (SZ_256M >> PAGE_SHIFT); + bo->placements[0].mem_type = vcpu_bo->tbo.resource->mem_type; + if (bo->placements[0].mem_type == TTM_PL_VRAM) + bo->placements[0].flags |= TTM_PL_FLAG_CONTIGUOUS; +} + static void amdgpu_uvd_force_into_uvd_segment(struct amdgpu_bo *abo) { int i; @@ -595,13 +618,10 @@ static int amdgpu_uvd_cs_pass1(struct amdgpu_uvd_cs_ctx *ctx) if (!ctx->parser->adev->uvd.address_64_bit) { /* check if it's a message or feedback command */ cmd = amdgpu_ib_get_value(ctx->ib, ctx->idx) >> 1; - if (cmd == 0x0 || cmd == 0x3) { - /* yes, force it into VRAM */ - uint32_t domain = AMDGPU_GEM_DOMAIN_VRAM; - - amdgpu_bo_placement_from_domain(bo, domain); - } - amdgpu_uvd_force_into_uvd_segment(bo); + if (cmd == 0x0 || cmd == 0x3) + amdgpu_uvd_force_into_vcpu_segment(bo); + else + amdgpu_uvd_force_into_uvd_segment(bo); r = ttm_bo_validate(&bo->tbo, &bo->placement, &tctx); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 7d51880b4860..fee4c94c2585 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -2920,47 +2920,56 @@ int amdgpu_vm_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) } /** - * amdgpu_vm_lock_by_pasid - return an amdgpu_vm and its root bo from a pasid, if possible. + * amdgpu_vm_lock_by_pasid - look up a VM by PASID and lock its root PD * @adev: amdgpu device pointer - * @root: root BO of the VM * @pasid: PASID of the VM - * The caller needs to unreserve and unref the root bo on success. + * @exec: drm_exec context to lock the root PD in + * + * Must be called from within a drm_exec_until_all_locked() loop; the caller + * runs drm_exec_retry_on_contention() afterwards. The drm_exec context holds + * a reference on the root BO until it is finalised. + * + * Return: the VM on success, or NULL if the PASID has no VM, the VM is being + * torn down, or locking the root PD failed. */ struct amdgpu_vm *amdgpu_vm_lock_by_pasid(struct amdgpu_device *adev, - struct amdgpu_bo **root, u32 pasid) + u32 pasid, struct drm_exec *exec) { unsigned long irqflags; + struct amdgpu_bo *root; struct amdgpu_vm *vm; int r; xa_lock_irqsave(&adev->vm_manager.pasids, irqflags); vm = xa_load(&adev->vm_manager.pasids, pasid); - *root = vm ? amdgpu_bo_ref(vm->root.bo) : NULL; + root = vm ? amdgpu_bo_ref(vm->root.bo) : NULL; xa_unlock_irqrestore(&adev->vm_manager.pasids, irqflags); - if (!*root) + if (!root) return NULL; - r = amdgpu_bo_reserve(*root, true); - if (r) - goto error_unref; + r = drm_exec_lock_obj(exec, &root->tbo.base); + if (r) { + amdgpu_bo_unref(&root); + return NULL; + } /* Double check that the VM still exists */ xa_lock_irqsave(&adev->vm_manager.pasids, irqflags); vm = xa_load(&adev->vm_manager.pasids, pasid); - if (vm && vm->root.bo != *root) + if (vm && vm->root.bo != root) vm = NULL; xa_unlock_irqrestore(&adev->vm_manager.pasids, irqflags); - if (!vm) - goto error_unlock; + if (!vm) { + drm_exec_unlock_obj(exec, &root->tbo.base); + amdgpu_bo_unref(&root); + return NULL; + } - return vm; -error_unlock: - amdgpu_bo_unreserve(*root); + /* The drm_exec context holds its own reference on the root BO. */ + amdgpu_bo_unref(&root); -error_unref: - amdgpu_bo_unref(root); - return NULL; + return vm; } /** @@ -2982,33 +2991,49 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid, uint64_t ts, bool write_fault) { bool is_compute_context = false; - struct amdgpu_bo *root; + struct drm_exec exec; uint64_t value, flags; struct amdgpu_vm *vm; int r; - vm = amdgpu_vm_lock_by_pasid(adev, &root, pasid); - if (!vm) + drm_exec_init(&exec, 0, 1); + drm_exec_until_all_locked(&exec) { + vm = amdgpu_vm_lock_by_pasid(adev, pasid, &exec); + drm_exec_retry_on_contention(&exec); + if (!vm) + break; + } + if (!vm) { + drm_exec_fini(&exec); return false; + } is_compute_context = vm->is_compute_context; if (is_compute_context) { - /* Unreserve root since svm_range_restore_pages might try to reserve it. */ - /* TODO: rework svm_range_restore_pages so that this isn't necessary. */ - amdgpu_bo_unreserve(root); + /* Release the root PD lock since svm_range_restore_pages + * might try to take it. + * TODO: rework svm_range_restore_pages so that this isn't + * necessary. + */ + drm_exec_fini(&exec); if (!svm_range_restore_pages(adev, pasid, vmid, - node_id, addr >> PAGE_SHIFT, ts, write_fault)) { - amdgpu_bo_unref(&root); + node_id, addr >> PAGE_SHIFT, ts, write_fault)) return true; - } - amdgpu_bo_unref(&root); /* Re-acquire the VM lock, could be that the VM was freed in between. */ - vm = amdgpu_vm_lock_by_pasid(adev, &root, pasid); - if (!vm) + drm_exec_init(&exec, 0, 1); + drm_exec_until_all_locked(&exec) { + vm = amdgpu_vm_lock_by_pasid(adev, pasid, &exec); + drm_exec_retry_on_contention(&exec); + if (!vm) + break; + } + if (!vm) { + drm_exec_fini(&exec); return false; + } } addr /= AMDGPU_GPU_PAGE_SIZE; @@ -3032,7 +3057,7 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid, value = 0; } - r = dma_resv_reserve_fences(root->tbo.base.resv, 1); + r = dma_resv_reserve_fences(vm->root.bo->tbo.base.resv, 1); if (r) { pr_debug("failed %d to reserve fence slot\n", r); goto error_unlock; @@ -3046,12 +3071,10 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid, r = amdgpu_vm_update_pdes(adev, vm, true); error_unlock: - amdgpu_bo_unreserve(root); + drm_exec_fini(&exec); if (r < 0) dev_err(adev->dev, "Can't handle page fault (%d)\n", r); - amdgpu_bo_unref(&root); - return false; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index 3695299f1a03..b32f51a78cd8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -592,7 +592,7 @@ bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, u32 pasid, bool write_fault); struct amdgpu_vm *amdgpu_vm_lock_by_pasid(struct amdgpu_device *adev, - struct amdgpu_bo **root, u32 pasid); + u32 pasid, struct drm_exec *exec); void amdgpu_vm_set_task_info(struct amdgpu_vm *vm); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 47721d0c3781..81a759a98725 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -4071,6 +4071,41 @@ err_priv_inst: return r; } +static void gfx_v9_0_deactivate_kcq_hqd(struct amdgpu_device *adev) +{ + amdgpu_gfx_rlc_enter_safe_mode(adev, 0); + for (int i = 0; i < adev->gfx.num_compute_rings; i++) { + u32 tmp; + struct amdgpu_ring *ring = &adev->gfx.compute_ring[i]; + + mutex_lock(&adev->srbm_mutex); + soc15_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0, 0); + tmp = RREG32_SOC15(GC, 0, mmCP_HQD_ACTIVE); + /* disable the queue if it's active */ + if (tmp & CP_HQD_ACTIVE__ACTIVE_MASK) { + int j; + + WREG32_SOC15(GC, 0, mmCP_HQD_DEQUEUE_REQUEST, 1); + for (j = 0; j < adev->usec_timeout; j++) { + tmp = RREG32_SOC15(GC, 0, mmCP_HQD_ACTIVE); + if (!(tmp & CP_HQD_ACTIVE__ACTIVE_MASK)) + break; + udelay(1); + } + if (j == AMDGPU_MAX_USEC_TIMEOUT) { + DRM_DEBUG("comp_%u_%u_%u dequeue request failed.\n", + ring->me, ring->pipe, ring->queue); + /* Manual disable if dequeue request times out */ + WREG32_SOC15(GC, 0, mmCP_HQD_ACTIVE, 0); + } + WREG32_SOC15(GC, 0, mmCP_HQD_DEQUEUE_REQUEST, 0); + } + soc15_grbm_select(adev, 0, 0, 0, 0, 0); + mutex_unlock(&adev->srbm_mutex); + } + amdgpu_gfx_rlc_exit_safe_mode(adev, 0); +} + static int gfx_v9_0_hw_fini(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; @@ -4095,6 +4130,10 @@ static int gfx_v9_0_hw_fini(struct amdgpu_ip_block *ip_block) return 0; } + if ((adev->flags & AMD_IS_APU) && amdgpu_in_reset(adev) && + amdgpu_asic_reset_method(adev) == AMD_RESET_METHOD_MODE2) + gfx_v9_0_deactivate_kcq_hqd(adev); + /* Use deinitialize sequence from CAIL when unbinding device from driver, * otherwise KIQ is hanging when binding back */ diff --git a/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c b/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c index 95b3f4e55ec3..699c274d357e 100644 --- a/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c +++ b/drivers/gpu/drm/amd/amdgpu/ih_v6_1.c @@ -790,7 +790,7 @@ static void ih_v6_1_set_interrupt_funcs(struct amdgpu_device *adev) const struct amdgpu_ip_block_version ih_v6_1_ip_block = { .type = AMD_IP_BLOCK_TYPE_IH, .major = 6, - .minor = 0, + .minor = 1, .rev = 0, .funcs = &ih_v6_1_ip_funcs, }; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index a2b100d14425..531e20748198 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -1299,18 +1299,11 @@ static int kfd_ioctl_map_memory_to_gpu(struct file *filep, return -EINVAL; } - devices_arr = kmalloc_array(args->n_devices, sizeof(*devices_arr), - GFP_KERNEL); - if (!devices_arr) - return -ENOMEM; + devices_arr = memdup_array_user((void *)args->device_ids_array_ptr, + args->n_devices, sizeof(*devices_arr)); - err = copy_from_user(devices_arr, - (void __user *)args->device_ids_array_ptr, - args->n_devices * sizeof(*devices_arr)); - if (err != 0) { - err = -EFAULT; - goto copy_from_user_failed; - } + if (IS_ERR(devices_arr)) + return PTR_ERR(devices_arr); mutex_lock(&p->mutex); pdd = kfd_process_device_data_by_id(p, GET_GPU_ID(args->handle)); @@ -1391,7 +1384,6 @@ get_mem_obj_from_handle_failed: map_memory_to_gpu_failed: sync_memory_failed: mutex_unlock(&p->mutex); -copy_from_user_failed: kfree(devices_arr); return err; @@ -1416,18 +1408,11 @@ static int kfd_ioctl_unmap_memory_from_gpu(struct file *filep, return -EINVAL; } - devices_arr = kmalloc_array(args->n_devices, sizeof(*devices_arr), - GFP_KERNEL); - if (!devices_arr) - return -ENOMEM; + devices_arr = memdup_array_user((void *)args->device_ids_array_ptr, + args->n_devices, sizeof(*devices_arr)); - err = copy_from_user(devices_arr, - (void __user *)args->device_ids_array_ptr, - args->n_devices * sizeof(*devices_arr)); - if (err != 0) { - err = -EFAULT; - goto copy_from_user_failed; - } + if (IS_ERR(devices_arr)) + return PTR_ERR(devices_arr); mutex_lock(&p->mutex); pdd = kfd_process_device_data_by_id(p, GET_GPU_ID(args->handle)); @@ -1493,7 +1478,6 @@ get_mem_obj_from_handle_failed: unmap_memory_from_gpu_failed: sync_memory_failed: mutex_unlock(&p->mutex); -copy_from_user_failed: kfree(devices_arr); return err; } @@ -1562,16 +1546,10 @@ static int kfd_ioctl_get_dmabuf_info(struct file *filep, if (!dev) return -EINVAL; - if (args->metadata_ptr) { - metadata_buffer = kzalloc(args->metadata_size, GFP_KERNEL); - if (!metadata_buffer) - return -ENOMEM; - } - /* Get dmabuf info from KGD */ r = amdgpu_amdkfd_get_dmabuf_info(dev->adev, args->dmabuf_fd, &dmabuf_adev, &args->size, - metadata_buffer, args->metadata_size, + &metadata_buffer, args->metadata_size, &args->metadata_size, &flags, &xcp_id); if (r) goto exit; @@ -1583,7 +1561,7 @@ static int kfd_ioctl_get_dmabuf_info(struct file *filep, args->flags = flags; /* Copy metadata buffer to user mode */ - if (metadata_buffer) { + if (metadata_buffer && args->metadata_ptr) { r = copy_to_user((void __user *)args->metadata_ptr, metadata_buffer, args->metadata_size); if (r != 0) @@ -2359,17 +2337,11 @@ static int criu_restore_devices(struct kfd_process *p, if (*priv_offset + (args->num_devices * sizeof(*device_privs)) > max_priv_data_size) return -EINVAL; - device_buckets = kmalloc_objs(*device_buckets, args->num_devices); - if (!device_buckets) - return -ENOMEM; + device_buckets = memdup_array_user((void *)args->devices, + args->num_devices, sizeof(*device_buckets)); - ret = copy_from_user(device_buckets, (void __user *)args->devices, - args->num_devices * sizeof(*device_buckets)); - if (ret) { - pr_err("Failed to copy devices buckets from user\n"); - ret = -EFAULT; - goto exit; - } + if (IS_ERR(device_buckets)) + return PTR_ERR(device_buckets); for (i = 0; i < args->num_devices; i++) { struct kfd_node *dev; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c index 05c74887fd6f..fdcf7f2d1b5b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c @@ -153,14 +153,16 @@ void __iomem *kfd_get_kernel_doorbell(struct kfd_dev *kfd, u32 inx; mutex_lock(&kfd->doorbell_mutex); + inx = find_first_zero_bit(kfd->doorbell_bitmap, PAGE_SIZE / sizeof(u32)); + if (inx >= KFD_MAX_NUM_OF_QUEUES_PER_PROCESS) { + mutex_unlock(&kfd->doorbell_mutex); + return NULL; + } __set_bit(inx, kfd->doorbell_bitmap); mutex_unlock(&kfd->doorbell_mutex); - if (inx >= KFD_MAX_NUM_OF_QUEUES_PER_PROCESS) - return NULL; - *doorbell_off = amdgpu_doorbell_index_on_bar(kfd->adev, kfd->doorbells, inx, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c index 28dc6886c1ff..226e76ae0be7 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c @@ -424,7 +424,7 @@ svm_migrate_vma_to_vram(struct kfd_node *node, struct svm_range *prange, migrate.dst = migrate.src + npages; scratch = (dma_addr_t *)(migrate.dst + npages); - kfd_smi_event_migration_start(node, p->lead_thread->pid, + kfd_smi_event_migration_start(node, p->lead_thread, start >> PAGE_SHIFT, end >> PAGE_SHIFT, 0, node->id, prange->prefetch_loc, prange->preferred_loc, trigger); @@ -462,7 +462,7 @@ svm_migrate_vma_to_vram(struct kfd_node *node, struct svm_range *prange, out_free: kvfree(buf); - kfd_smi_event_migration_end(node, p->lead_thread->pid, + kfd_smi_event_migration_end(node, p->lead_thread, start >> PAGE_SHIFT, end >> PAGE_SHIFT, 0, node->id, trigger, r); out: @@ -727,7 +727,7 @@ svm_migrate_vma_to_ram(struct kfd_node *node, struct svm_range *prange, migrate.fault_page = fault_page; scratch = (dma_addr_t *)(migrate.dst + npages); - kfd_smi_event_migration_start(node, p->lead_thread->pid, + kfd_smi_event_migration_start(node, p->lead_thread, start >> PAGE_SHIFT, end >> PAGE_SHIFT, node->id, 0, prange->prefetch_loc, prange->preferred_loc, trigger); @@ -766,7 +766,7 @@ svm_migrate_vma_to_ram(struct kfd_node *node, struct svm_range *prange, out_free: kvfree(buf); - kfd_smi_event_migration_end(node, p->lead_thread->pid, + kfd_smi_event_migration_end(node, p->lead_thread, start >> PAGE_SHIFT, end >> PAGE_SHIFT, node->id, 0, trigger, r); out: diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index 368283d53077..ca71fa726e32 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -1175,10 +1175,12 @@ static void kfd_process_remove_sysfs(struct kfd_process *p) if (!p->kobj) return; - sysfs_remove_file(p->kobj, &p->attr_pasid); - kobject_del(p->kobj_queues); - kobject_put(p->kobj_queues); - p->kobj_queues = NULL; + if (p->kobj_queues) { + sysfs_remove_file(p->kobj, &p->attr_pasid); + kobject_del(p->kobj_queues); + kobject_put(p->kobj_queues); + p->kobj_queues = NULL; + } for (i = 0; i < p->n_pdds; i++) { pdd = p->pdds[i]; @@ -1186,17 +1188,21 @@ static void kfd_process_remove_sysfs(struct kfd_process *p) sysfs_remove_file(p->kobj, &pdd->attr_vram); sysfs_remove_file(p->kobj, &pdd->attr_sdma); - sysfs_remove_file(pdd->kobj_stats, &pdd->attr_evict); - if (pdd->dev->kfd2kgd->get_cu_occupancy) - sysfs_remove_file(pdd->kobj_stats, - &pdd->attr_cu_occupancy); - kobject_del(pdd->kobj_stats); - kobject_put(pdd->kobj_stats); - pdd->kobj_stats = NULL; + if (pdd->kobj_stats) { + sysfs_remove_file(pdd->kobj_stats, &pdd->attr_evict); + if (pdd->dev->kfd2kgd->get_cu_occupancy) + sysfs_remove_file(pdd->kobj_stats, + &pdd->attr_cu_occupancy); + kobject_del(pdd->kobj_stats); + kobject_put(pdd->kobj_stats); + pdd->kobj_stats = NULL; + } } for_each_set_bit(i, p->svms.bitmap_supported, p->n_pdds) { pdd = p->pdds[i]; + if (!pdd->kobj_counters) + continue; sysfs_remove_file(pdd->kobj_counters, &pdd->attr_faults); sysfs_remove_file(pdd->kobj_counters, &pdd->attr_page_in); @@ -1254,6 +1260,13 @@ static void kfd_process_wq_release(struct work_struct *work) kfd_debugfs_remove_process(p); + /* + * Remove the proc/sysfs entries before destroying PDDs. The removal path + * walks the PDD array and sysfs callbacks dereference PDD fields, so the + * backing data must remain valid until sysfs removal has completed. + */ + kfd_process_remove_sysfs(p); + kfd_process_kunmap_signal_bo(p); kfd_process_free_outstanding_kfd_bos(p); svm_range_list_fini(p); @@ -1267,11 +1280,6 @@ static void kfd_process_wq_release(struct work_struct *work) put_task_struct(p->lead_thread); - /* the last step is removing process entries under /sys - * to indicate the process has been terminated. - */ - kfd_process_remove_sysfs(p); - kfree(p); } @@ -1969,7 +1977,7 @@ int kfd_process_evict_queues(struct kfd_process *p, uint32_t trigger) struct kfd_process_device *pdd = p->pdds[i]; struct device *dev = pdd->dev->adev->dev; - kfd_smi_event_queue_eviction(pdd->dev, p->lead_thread->pid, + kfd_smi_event_queue_eviction(pdd->dev, p->lead_thread, trigger); r = pdd->dev->dqm->ops.evict_process_queues(pdd->dev->dqm, @@ -1999,7 +2007,7 @@ fail: if (n_evicted == 0) break; - kfd_smi_event_queue_restore(pdd->dev, p->lead_thread->pid); + kfd_smi_event_queue_restore(pdd->dev, p->lead_thread); if (pdd->dev->dqm->ops.restore_process_queues(pdd->dev->dqm, &pdd->qpd)) @@ -2022,7 +2030,7 @@ int kfd_process_restore_queues(struct kfd_process *p) struct kfd_process_device *pdd = p->pdds[i]; struct device *dev = pdd->dev->adev->dev; - kfd_smi_event_queue_restore(pdd->dev, p->lead_thread->pid); + kfd_smi_event_queue_restore(pdd->dev, p->lead_thread); r = pdd->dev->dqm->ops.restore_process_queues(pdd->dev->dqm, &pdd->qpd); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index 44e39ce222b7..0ac35789b239 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -962,8 +962,8 @@ static void set_queue_properties_from_criu(struct queue_properties *qp, qp->priority = q_data->priority; qp->queue_address = q_data->q_address; qp->queue_size = q_data->q_size; - qp->read_ptr = (uint32_t *) q_data->read_ptr_addr; - qp->write_ptr = (uint32_t *) q_data->write_ptr_addr; + qp->read_ptr = (void __user *)q_data->read_ptr_addr; + qp->write_ptr = (void __user *)q_data->write_ptr_addr; qp->eop_ring_buffer_address = q_data->eop_ring_buffer_address; qp->eop_ring_buffer_size = q_data->eop_ring_buffer_size; qp->ctx_save_restore_area_address = q_data->ctx_save_restore_area_address; @@ -1042,10 +1042,18 @@ int kfd_criu_restore_queue(struct kfd_process *p, memset(&qp, 0, sizeof(qp)); set_queue_properties_from_criu(&qp, q_data, NUM_XCC(pdd->dev->adev->gfx.xcc_mask)); + ret = kfd_queue_acquire_buffers(pdd, &qp); + if (ret) { + pr_debug("failed to acquire user queue buffers for CRIU\n"); + goto exit; + } + print_queue_properties(&qp); ret = pqm_create_queue(&p->pqm, pdd->dev, &qp, &queue_id, q_data, mqd, ctl_stack, NULL); if (ret) { + kfd_queue_unref_bo_vas(pdd, &qp); + kfd_queue_release_buffers(pdd, &qp); pr_err("Failed to create new queue err:%d\n", ret); goto exit; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c index dfbde5a571f6..e659cd50eb0b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c @@ -195,17 +195,35 @@ static void add_event_to_kfifo(pid_t pid, struct kfd_node *dev, rcu_read_unlock(); } +/** + * kfd_smi_task_to_pid - Convert task to namespace-aware PID + * @task: task_struct pointer (typically p->lead_thread) + * + * Returns the PID as it appears in the task's own PID namespace. + * For containerized processes, this returns the container-local PID + * (what getpid() returns), not the global host PID. + * + * Returns 0 if task is NULL. + */ +static inline pid_t kfd_smi_task_to_pid(struct task_struct *task) +{ + return task ? task_tgid_nr_ns(task, task_active_pid_ns(task)) : 0; +} + __printf(4, 5) -static void kfd_smi_event_add(pid_t pid, struct kfd_node *dev, +static void kfd_smi_event_add(struct task_struct *task, struct kfd_node *dev, unsigned int event, char *fmt, ...) { char fifo_in[KFD_SMI_EVENT_MSG_SIZE]; int len; va_list args; + pid_t pid; if (list_empty(&dev->smi_clients)) return; + pid = kfd_smi_task_to_pid(task); + len = snprintf(fifo_in, sizeof(fifo_in), "%x ", event); va_start(args, fmt); @@ -234,14 +252,15 @@ void kfd_smi_event_update_gpu_reset(struct kfd_node *dev, bool post_reset, amdgpu_reset_get_desc(reset_context, reset_cause, sizeof(reset_cause)); - kfd_smi_event_add(0, dev, event, KFD_EVENT_FMT_UPDATE_GPU_RESET( + kfd_smi_event_add(NULL, dev, event, KFD_EVENT_FMT_UPDATE_GPU_RESET( dev->reset_seq_num, reset_cause)); } void kfd_smi_event_update_thermal_throttling(struct kfd_node *dev, uint64_t throttle_bitmask) { - kfd_smi_event_add(0, dev, KFD_SMI_EVENT_THERMAL_THROTTLE, KFD_EVENT_FMT_THERMAL_THROTTLING( + kfd_smi_event_add(NULL, dev, KFD_SMI_EVENT_THERMAL_THROTTLE, + KFD_EVENT_FMT_THERMAL_THROTTLING( throttle_bitmask, amdgpu_dpm_get_thermal_throttling_counter(dev->adev))); } @@ -254,67 +273,67 @@ void kfd_smi_event_update_vmfault(struct kfd_node *dev, uint16_t pasid) if (task_info) { /* Report VM faults from user applications, not retry from kernel */ if (task_info->task.pid) - kfd_smi_event_add(task_info->tgid, dev, - KFD_SMI_EVENT_VMFAULT, - KFD_EVENT_FMT_VMFAULT(task_info->task.pid, - task_info->task.comm)); + kfd_smi_event_add(NULL, dev, KFD_SMI_EVENT_VMFAULT, KFD_EVENT_FMT_VMFAULT( + task_info->task.pid, task_info->task.comm)); amdgpu_vm_put_task_info(task_info); } } -void kfd_smi_event_page_fault_start(struct kfd_node *node, pid_t pid, +void kfd_smi_event_page_fault_start(struct kfd_node *node, struct task_struct *task, unsigned long address, bool write_fault, ktime_t ts) { - kfd_smi_event_add(pid, node, KFD_SMI_EVENT_PAGE_FAULT_START, - KFD_EVENT_FMT_PAGEFAULT_START(ktime_to_ns(ts), pid, - address, node->id, write_fault ? 'W' : 'R')); + kfd_smi_event_add(task, node, KFD_SMI_EVENT_PAGE_FAULT_START, + KFD_EVENT_FMT_PAGEFAULT_START(ktime_to_ns(ts), + kfd_smi_task_to_pid(task), address, node->id, + write_fault ? 'W' : 'R')); } -void kfd_smi_event_page_fault_end(struct kfd_node *node, pid_t pid, +void kfd_smi_event_page_fault_end(struct kfd_node *node, struct task_struct *task, unsigned long address, bool migration) { - kfd_smi_event_add(pid, node, KFD_SMI_EVENT_PAGE_FAULT_END, + kfd_smi_event_add(task, node, KFD_SMI_EVENT_PAGE_FAULT_END, KFD_EVENT_FMT_PAGEFAULT_END(ktime_get_boottime_ns(), - pid, address, node->id, migration ? 'M' : 'U')); + kfd_smi_task_to_pid(task), address, node->id, + migration ? 'M' : 'U')); } -void kfd_smi_event_migration_start(struct kfd_node *node, pid_t pid, +void kfd_smi_event_migration_start(struct kfd_node *node, struct task_struct *task, unsigned long start, unsigned long end, uint32_t from, uint32_t to, uint32_t prefetch_loc, uint32_t preferred_loc, uint32_t trigger) { - kfd_smi_event_add(pid, node, KFD_SMI_EVENT_MIGRATE_START, - KFD_EVENT_FMT_MIGRATE_START( - ktime_get_boottime_ns(), pid, start, end - start, - from, to, prefetch_loc, preferred_loc, trigger)); + kfd_smi_event_add(task, node, KFD_SMI_EVENT_MIGRATE_START, + KFD_EVENT_FMT_MIGRATE_START(ktime_get_boottime_ns(), + kfd_smi_task_to_pid(task), start, end - start, from, + to, prefetch_loc, preferred_loc, trigger)); } -void kfd_smi_event_migration_end(struct kfd_node *node, pid_t pid, +void kfd_smi_event_migration_end(struct kfd_node *node, struct task_struct *task, unsigned long start, unsigned long end, uint32_t from, uint32_t to, uint32_t trigger, int error_code) { - kfd_smi_event_add(pid, node, KFD_SMI_EVENT_MIGRATE_END, - KFD_EVENT_FMT_MIGRATE_END( - ktime_get_boottime_ns(), pid, start, end - start, - from, to, trigger, error_code)); + kfd_smi_event_add(task, node, KFD_SMI_EVENT_MIGRATE_END, + KFD_EVENT_FMT_MIGRATE_END(ktime_get_boottime_ns(), + kfd_smi_task_to_pid(task), start, end - start, from, + to, trigger, error_code)); } -void kfd_smi_event_queue_eviction(struct kfd_node *node, pid_t pid, +void kfd_smi_event_queue_eviction(struct kfd_node *node, struct task_struct *task, uint32_t trigger) { - kfd_smi_event_add(pid, node, KFD_SMI_EVENT_QUEUE_EVICTION, - KFD_EVENT_FMT_QUEUE_EVICTION(ktime_get_boottime_ns(), pid, - node->id, trigger)); + kfd_smi_event_add(task, node, KFD_SMI_EVENT_QUEUE_EVICTION, + KFD_EVENT_FMT_QUEUE_EVICTION(ktime_get_boottime_ns(), + kfd_smi_task_to_pid(task), node->id, trigger)); } -void kfd_smi_event_queue_restore(struct kfd_node *node, pid_t pid) +void kfd_smi_event_queue_restore(struct kfd_node *node, struct task_struct *task) { - kfd_smi_event_add(pid, node, KFD_SMI_EVENT_QUEUE_RESTORE, - KFD_EVENT_FMT_QUEUE_RESTORE(ktime_get_boottime_ns(), pid, - node->id, '0')); + kfd_smi_event_add(task, node, KFD_SMI_EVENT_QUEUE_RESTORE, + KFD_EVENT_FMT_QUEUE_RESTORE(ktime_get_boottime_ns(), + kfd_smi_task_to_pid(task), node->id, '0')); } void kfd_smi_event_queue_restore_rescheduled(struct mm_struct *mm) @@ -329,21 +348,23 @@ void kfd_smi_event_queue_restore_rescheduled(struct mm_struct *mm) for (i = 0; i < p->n_pdds; i++) { struct kfd_process_device *pdd = p->pdds[i]; - kfd_smi_event_add(p->lead_thread->pid, pdd->dev, + kfd_smi_event_add(p->lead_thread, pdd->dev, KFD_SMI_EVENT_QUEUE_RESTORE, KFD_EVENT_FMT_QUEUE_RESTORE(ktime_get_boottime_ns(), - p->lead_thread->pid, pdd->dev->id, 'R')); + kfd_smi_task_to_pid(p->lead_thread), + pdd->dev->id, 'R')); } kfd_unref_process(p); } -void kfd_smi_event_unmap_from_gpu(struct kfd_node *node, pid_t pid, +void kfd_smi_event_unmap_from_gpu(struct kfd_node *node, struct task_struct *task, unsigned long address, unsigned long last, uint32_t trigger) { - kfd_smi_event_add(pid, node, KFD_SMI_EVENT_UNMAP_FROM_GPU, + kfd_smi_event_add(task, node, KFD_SMI_EVENT_UNMAP_FROM_GPU, KFD_EVENT_FMT_UNMAP_FROM_GPU(ktime_get_boottime_ns(), - pid, address, last - address + 1, node->id, trigger)); + kfd_smi_task_to_pid(task), address, + last - address + 1, node->id, trigger)); } void kfd_smi_event_process(struct kfd_process_device *pdd, bool start) @@ -358,7 +379,7 @@ void kfd_smi_event_process(struct kfd_process_device *pdd, bool start) task_info = amdgpu_vm_get_task_info_vm(avm); if (task_info) { - kfd_smi_event_add(task_info->tgid, pdd->dev, + kfd_smi_event_add(NULL, pdd->dev, start ? KFD_SMI_EVENT_PROCESS_START : KFD_SMI_EVENT_PROCESS_END, KFD_EVENT_FMT_PROCESS(task_info->task.pid, @@ -387,7 +408,7 @@ int kfd_smi_event_open(struct kfd_node *dev, uint32_t *fd) spin_lock_init(&client->lock); client->events = 0; client->dev = dev; - client->pid = current->tgid; + client->pid = kfd_smi_task_to_pid(current); client->suser = capable(CAP_SYS_ADMIN); spin_lock(&dev->smi_lock); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.h b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.h index bb4d72b57387..afa93d7cfa7f 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.h @@ -32,25 +32,25 @@ void kfd_smi_event_update_thermal_throttling(struct kfd_node *dev, uint64_t throttle_bitmask); void kfd_smi_event_update_gpu_reset(struct kfd_node *dev, bool post_reset, struct amdgpu_reset_context *reset_context); -void kfd_smi_event_page_fault_start(struct kfd_node *node, pid_t pid, +void kfd_smi_event_page_fault_start(struct kfd_node *node, struct task_struct *task, unsigned long address, bool write_fault, ktime_t ts); -void kfd_smi_event_page_fault_end(struct kfd_node *node, pid_t pid, +void kfd_smi_event_page_fault_end(struct kfd_node *node, struct task_struct *task, unsigned long address, bool migration); -void kfd_smi_event_migration_start(struct kfd_node *node, pid_t pid, +void kfd_smi_event_migration_start(struct kfd_node *node, struct task_struct *task, unsigned long start, unsigned long end, uint32_t from, uint32_t to, uint32_t prefetch_loc, uint32_t preferred_loc, uint32_t trigger); -void kfd_smi_event_migration_end(struct kfd_node *node, pid_t pid, +void kfd_smi_event_migration_end(struct kfd_node *node, struct task_struct *task, unsigned long start, unsigned long end, uint32_t from, uint32_t to, uint32_t trigger, int error_code); -void kfd_smi_event_queue_eviction(struct kfd_node *node, pid_t pid, +void kfd_smi_event_queue_eviction(struct kfd_node *node, struct task_struct *task, uint32_t trigger); -void kfd_smi_event_queue_restore(struct kfd_node *node, pid_t pid); +void kfd_smi_event_queue_restore(struct kfd_node *node, struct task_struct *task); void kfd_smi_event_queue_restore_rescheduled(struct mm_struct *mm); -void kfd_smi_event_unmap_from_gpu(struct kfd_node *node, pid_t pid, +void kfd_smi_event_unmap_from_gpu(struct kfd_node *node, struct task_struct *task, unsigned long address, unsigned long last, uint32_t trigger); void kfd_smi_event_process(struct kfd_process_device *pdd, bool start); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index 3841943da5ec..0900bb23349e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -1144,7 +1144,7 @@ static int svm_range_split_tail(struct svm_range *prange, uint64_t new_last, struct list_head *insert_list, struct list_head *remap_list) { - unsigned long last_align_down = ALIGN_DOWN(prange->last, 512); + unsigned long last_align_down = ALIGN_DOWN(prange->last + 1, 512); unsigned long start_align = ALIGN(prange->start, 512); bool huge_page_mapping = last_align_down > start_align; struct svm_range *tail = NULL; @@ -1168,7 +1168,7 @@ static int svm_range_split_head(struct svm_range *prange, uint64_t new_start, struct list_head *insert_list, struct list_head *remap_list) { - unsigned long last_align_down = ALIGN_DOWN(prange->last, 512); + unsigned long last_align_down = ALIGN_DOWN(prange->last + 1, 512); unsigned long start_align = ALIGN(prange->start, 512); bool huge_page_mapping = last_align_down > start_align; struct svm_range *head = NULL; @@ -1181,8 +1181,8 @@ svm_range_split_head(struct svm_range *prange, uint64_t new_start, list_add(&head->list, insert_list); - if (huge_page_mapping && head->last + 1 > start_align && - head->last + 1 < last_align_down && (!IS_ALIGNED(head->last, 512))) + if (huge_page_mapping && new_start > start_align && + new_start < last_align_down && !IS_ALIGNED(new_start, 512)) list_add(&head->update_list, remap_list); return 0; @@ -1408,7 +1408,7 @@ svm_range_unmap_from_gpus(struct svm_range *prange, unsigned long start, return -EINVAL; } - kfd_smi_event_unmap_from_gpu(pdd->dev, p->lead_thread->pid, + kfd_smi_event_unmap_from_gpu(pdd->dev, p->lead_thread, start, last, trigger); r = svm_range_unmap_from_gpu(pdd->dev->adev, @@ -3205,7 +3205,7 @@ retry_write_locked: svms, prange->start, prange->last, best_loc, prange->actual_loc); - kfd_smi_event_page_fault_start(node, p->lead_thread->pid, addr, + kfd_smi_event_page_fault_start(node, p->lead_thread, addr, write_fault, timestamp); /* Align migration range start and size to granularity size */ @@ -3248,7 +3248,7 @@ retry_write_locked: r, svms, start, last); out_migrate_fail: - kfd_smi_event_page_fault_end(node, p->lead_thread->pid, addr, + kfd_smi_event_page_fault_end(node, p->lead_thread, addr, migration); out_unlock_range: @@ -4115,6 +4115,7 @@ exit: list_for_each_entry_safe(criu_svm_md, next, &svms->criu_svm_metadata_list, list) { pr_debug("freeing criu_svm_md[]\n\tstart: 0x%llx\n", criu_svm_md->data.start_addr); + list_del(&criu_svm_md->list); kfree(criu_svm_md); } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 97ab1e83b318..d3a8d681227a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -12942,13 +12942,11 @@ static bool amdgpu_dm_crtc_mem_type_changed(struct drm_device *dev, struct drm_plane_state *new_plane_state, *old_plane_state; drm_for_each_plane_mask(plane, dev, crtc_state->plane_mask) { - new_plane_state = drm_atomic_get_plane_state(state, plane); - old_plane_state = drm_atomic_get_plane_state(state, plane); + new_plane_state = drm_atomic_get_new_plane_state(state, plane); + old_plane_state = drm_atomic_get_old_plane_state(state, plane); - if (IS_ERR(new_plane_state) || IS_ERR(old_plane_state)) { - drm_err(dev, "Failed to get plane state for plane %s\n", plane->name); - return false; - } + if (!old_plane_state || !new_plane_state) + continue; if (old_plane_state->fb && new_plane_state->fb && get_mem_type(old_plane_state->fb) != get_mem_type(new_plane_state->fb)) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index f257ea91a34d..c6f94eb71ffa 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -95,8 +95,11 @@ static u32 edid_extract_panel_id(struct edid *edid) (u32)EDID_PRODUCT_ID(edid); } -static void apply_edid_quirks(struct drm_device *dev, struct edid *edid, struct dc_edid_caps *edid_caps) +static void apply_edid_quirks(struct dc_link *link, struct edid *edid, + struct dc_edid_caps *edid_caps) { + struct amdgpu_dm_connector *aconnector = link->priv; + struct drm_device *dev = aconnector->base.dev; uint32_t panel_id = edid_extract_panel_id(edid); switch (panel_id) { @@ -126,6 +129,11 @@ static void apply_edid_quirks(struct drm_device *dev, struct edid *edid, struct drm_dbg_driver(dev, "Disabling VSC on monitor with panel id %X\n", panel_id); edid_caps->panel_patch.disable_colorimetry = true; break; + /* Workaround for monitors that get corrupted by the PHY SSC reduction */ + case drm_edid_encode_panel_id('D', 'E', 'L', 0x4147): + drm_dbg_driver(dev, "Skip PHY SSC reduction on panel id %X\n", panel_id); + link->wa_flags.skip_phy_ssc_reduction = true; + break; default: return; } @@ -147,7 +155,6 @@ enum dc_edid_status dm_helpers_parse_edid_caps( { struct amdgpu_dm_connector *aconnector = link->priv; struct drm_connector *connector = &aconnector->base; - struct drm_device *dev = connector->dev; struct edid *edid_buf = edid ? (struct edid *) edid->raw_edid : NULL; struct cea_sad *sads; int sad_count = -1; @@ -188,7 +195,7 @@ enum dc_edid_status dm_helpers_parse_edid_caps( edid_caps->frl_dsc_max_frl_rate, edid_caps->frl_dsc_total_chunk_kbytes); } - apply_edid_quirks(dev, edid_buf, edid_caps); + apply_edid_quirks(link, edid_buf, edid_caps); sad_count = drm_edid_to_sad((struct edid *) edid->raw_edid, &sads); if (sad_count <= 0) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c index e957657b06c7..c7f8e08feaf4 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c @@ -1859,6 +1859,7 @@ static const struct drm_plane_funcs dm_plane_funcs = { .atomic_duplicate_state = amdgpu_dm_plane_drm_plane_duplicate_state, .atomic_destroy_state = amdgpu_dm_plane_drm_plane_destroy_state, .format_mod_supported = amdgpu_dm_plane_format_mod_supported, + .format_mod_supported_async = amdgpu_dm_plane_format_mod_supported, #ifdef AMD_PRIVATE_COLOR .atomic_set_property = dm_atomic_plane_set_property, .atomic_get_property = dm_atomic_plane_get_property, diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c index 00c4be7c3aa4..ff47af3854b6 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn31/dcn31_clk_mgr.c @@ -158,7 +158,6 @@ void dcn31_update_clocks(struct clk_mgr *clk_mgr_base, if (new_clocks->zstate_support != DCN_ZSTATE_SUPPORT_DISALLOW && new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) { dcn31_smu_set_zstate_support(clk_mgr, new_clocks->zstate_support); - dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, true); clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; } @@ -184,7 +183,6 @@ void dcn31_update_clocks(struct clk_mgr *clk_mgr_base, if (new_clocks->zstate_support == DCN_ZSTATE_SUPPORT_DISALLOW && new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) { dcn31_smu_set_zstate_support(clk_mgr, DCN_ZSTATE_SUPPORT_DISALLOW); - dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, false); clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; } diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c index dd6f11ecb9c9..24f6304011ae 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn314/dcn314_clk_mgr.c @@ -230,7 +230,6 @@ void dcn314_update_clocks(struct clk_mgr *clk_mgr_base, if (new_clocks->zstate_support != DCN_ZSTATE_SUPPORT_DISALLOW && new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) { dcn314_smu_set_zstate_support(clk_mgr, new_clocks->zstate_support); - dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, true); clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; } @@ -255,7 +254,6 @@ void dcn314_update_clocks(struct clk_mgr *clk_mgr_base, if (new_clocks->zstate_support == DCN_ZSTATE_SUPPORT_DISALLOW && new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) { dcn314_smu_set_zstate_support(clk_mgr, DCN_ZSTATE_SUPPORT_DISALLOW); - dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, false); clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; } diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c index 103013e2a0de..a69824e1eb26 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn35/dcn35_clk_mgr.c @@ -419,6 +419,7 @@ void dcn35_update_clocks(struct clk_mgr *clk_mgr_base, if (new_clocks->zstate_support != DCN_ZSTATE_SUPPORT_DISALLOW && new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) { dcn35_smu_set_zstate_support(clk_mgr, new_clocks->zstate_support); + dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, true); clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; } @@ -438,6 +439,7 @@ void dcn35_update_clocks(struct clk_mgr *clk_mgr_base, if (new_clocks->zstate_support == DCN_ZSTATE_SUPPORT_DISALLOW && new_clocks->zstate_support != clk_mgr_base->clks.zstate_support) { dcn35_smu_set_zstate_support(clk_mgr, DCN_ZSTATE_SUPPORT_DISALLOW); + dm_helpers_enable_periodic_detection(clk_mgr_base->ctx, false); clk_mgr_base->clks.zstate_support = new_clocks->zstate_support; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c index fb1145691410..a214ddbd4c86 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c @@ -227,9 +227,14 @@ static int smu_v15_0_0_system_features_control(struct smu_context *smu, bool en) struct amdgpu_device *adev = smu->adev; int ret = 0; - if (!en && !adev->in_s0ix) + if (!en && !adev->in_s0ix) { ret = smu_cmn_send_smc_msg(smu, SMU_MSG_PrepareMp1ForUnload, NULL); + /* SMU resets BIF_FB_EN to zero, re-enable MC access on APUs with SMU V15 */ + if (!ret && adev->nbio.funcs && adev->nbio.funcs->mc_access_enable) + adev->nbio.funcs->mc_access_enable(adev, true); + } + return ret; } diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 3fa4d2082cd7..11646453aaac 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -3571,7 +3571,7 @@ EXPORT_SYMBOL(drm_mode_put_tile_group); /** * drm_mode_get_tile_group - get a reference to an existing tile group * @dev: DRM device - * @topology: 8-bytes unique per monitor. + * @topology_id: 9-byte unique ID per monitor. * * Use the unique bytes to get a reference to an existing tile group. * @@ -3579,14 +3579,14 @@ EXPORT_SYMBOL(drm_mode_put_tile_group); * tile group or NULL if not found. */ struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev, - const char topology[8]) + const char topology_id[9]) { struct drm_tile_group *tg; int id; mutex_lock(&dev->mode_config.idr_mutex); idr_for_each_entry(&dev->mode_config.tile_idr, tg, id) { - if (!memcmp(tg->group_data, topology, 8)) { + if (!memcmp(tg->group_data, topology_id, sizeof(tg->group_data))) { if (!kref_get_unless_zero(&tg->refcount)) tg = NULL; mutex_unlock(&dev->mode_config.idr_mutex); @@ -3601,7 +3601,7 @@ EXPORT_SYMBOL(drm_mode_get_tile_group); /** * drm_mode_create_tile_group - create a tile group from a displayid description * @dev: DRM device - * @topology: 8-bytes unique per monitor. + * @topology_id: 9-byte unique ID per monitor. * * Create a tile group for the unique monitor, and get a unique * identifier for the tile group. @@ -3610,7 +3610,7 @@ EXPORT_SYMBOL(drm_mode_get_tile_group); * new tile group or NULL. */ struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, - const char topology[8]) + const char topology_id[9]) { struct drm_tile_group *tg; int ret; @@ -3620,7 +3620,7 @@ struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, return NULL; kref_init(&tg->refcount); - memcpy(tg->group_data, topology, 8); + memcpy(tg->group_data, topology_id, sizeof(tg->group_data)); tg->dev = dev; mutex_lock(&dev->mode_config.idr_mutex); diff --git a/drivers/gpu/drm/drm_displayid_internal.h b/drivers/gpu/drm/drm_displayid_internal.h index 5b1b32f73516..4590d6a3d821 100644 --- a/drivers/gpu/drm/drm_displayid_internal.h +++ b/drivers/gpu/drm/drm_displayid_internal.h @@ -109,7 +109,7 @@ struct displayid_tiled_block { u8 topo[3]; u8 tile_size[4]; u8 tile_pixel_bezel[5]; - u8 topology_id[8]; + u8 topology_id[9]; } __packed; struct displayid_detailed_timings_1 { diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 404208bf23a6..df3c25bac761 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -7575,6 +7575,14 @@ static void drm_parse_tiled_block(struct drm_connector *connector, u8 num_v_tile, num_h_tile; struct drm_tile_group *tg; + /* tiled block payload per spec: cap 1 + topo 3 + size 4 + bezel 5 + id 9 = 22 */ + if (block->num_bytes < 22) { + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] Unexpected tiled block size %u\n", + connector->base.id, connector->name, block->num_bytes); + return; + } + w = tile->tile_size[0] | tile->tile_size[1] << 8; h = tile->tile_size[2] | tile->tile_size[3] << 8; diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c index 0e4f0678c53c..9d0d47c79dd1 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.c +++ b/drivers/gpu/drm/i915/display/intel_atomic.c @@ -288,6 +288,12 @@ static void intel_crtc_put_color_blobs(struct intel_crtc_state *crtc_state) drm_property_blob_put(crtc_state->pre_csc_lut); drm_property_blob_put(crtc_state->post_csc_lut); + + crtc_state->hw.degamma_lut = NULL; + crtc_state->hw.gamma_lut = NULL; + crtc_state->hw.ctm = NULL; + crtc_state->pre_csc_lut = NULL; + crtc_state->post_csc_lut = NULL; } void intel_crtc_free_hw_state(struct intel_crtc_state *crtc_state) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 189ae2d3cfc9..7bc9b956554b 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -1256,9 +1256,22 @@ static void skl_sanitize_cdclk(struct intel_display *display) cdctl = intel_de_read(display, CDCLK_CTL); expected = (cdctl & CDCLK_FREQ_SEL_MASK) | skl_cdclk_decimal(display->cdclk.hw.cdclk); - if (cdctl == expected) - /* All well; nothing to sanitize */ - return; + + if (cdctl != expected) { + cdctl &= ~CDCLK_FREQ_DECIMAL_MASK; + cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK; + + if (cdctl != expected) + goto sanitize; + + drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n", + intel_de_read(display, CDCLK_CTL), expected); + + intel_de_write(display, CDCLK_CTL, expected); + } + + /* All well; nothing to sanitize */ + return; sanitize: drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n"); @@ -2354,11 +2367,25 @@ static void bxt_sanitize_cdclk(struct intel_display *display) * (PIPE_NONE). */ cdctl &= ~bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); - expected &= ~bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); + cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); - if (cdctl == expected) - /* All well; nothing to sanitize */ - return; + if (cdctl != expected) { + if (DISPLAY_VER(display) < 20) { + cdctl &= ~CDCLK_FREQ_DECIMAL_MASK; + cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK; + } + + if (cdctl != expected) + goto sanitize; + + drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n", + intel_de_read(display, CDCLK_CTL), expected); + + intel_de_write(display, CDCLK_CTL, expected); + } + + /* All well; nothing to sanitize */ + return; sanitize: drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n"); diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 205978c9feb6..6296635c4e79 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -2652,9 +2652,6 @@ static void mtl_ddi_pre_enable_dp(struct intel_atomic_state *state, /* 3. Select Thunderbolt */ mtl_port_buf_ctl_io_selection(encoder); - /* 4. Enable Panel Power if PPS is required */ - intel_pps_on(intel_dp); - /* 5. Enable the port PLL */ intel_ddi_enable_clock(encoder, crtc_state); @@ -3708,6 +3705,14 @@ intel_ddi_pre_pll_enable(struct intel_atomic_state *state, else if (display->platform.geminilake || display->platform.broxton) bxt_dpio_phy_set_lane_optim_mask(encoder, crtc_state->lane_lat_optim_mask); + + /* + * There is no direct connection between the PLL and PPS, however + * enabling PPS before PLL is required to avoid PLL/DDI BUF timeouts + * during system resume. Do that matching the Bspec order as well. + */ + if (DISPLAY_VER(display) >= 14) + intel_pps_on(&dig_port->dp); } static void adlp_tbt_to_dp_alt_switch_wa(struct intel_encoder *encoder) diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index bcdc50491347..0aa3e6b4c781 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -726,6 +726,10 @@ static int mst_stream_compute_config(struct intel_encoder *encoder, if (ret) return ret; + ret = intel_pfit_compute_config(pipe_config, conn_state); + if (ret) + return ret; + for_each_joiner_candidate(connector, adjusted_mode, num_joined_pipes) { if (num_joined_pipes > 1) pipe_config->joiner_pipes = GENMASK(crtc->pipe + num_joined_pipes - 1, diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c index 6ac0f23570f3..aeafe1742d30 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_context.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c @@ -613,6 +613,7 @@ set_proto_ctx_engines_parallel_submit(struct i915_user_extension __user *base, return -EINVAL; } + slot = array_index_nospec(slot, set->num_engines); if (set->engines[slot].type != I915_GEM_ENGINE_TYPE_INVALID) { drm_dbg(&i915->drm, "Invalid placement[%d], already occupied\n", slot); diff --git a/drivers/gpu/drm/nouveau/nouveau_exec.c b/drivers/gpu/drm/nouveau/nouveau_exec.c index c01a01aee32b..a08ab1cfea9b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_exec.c +++ b/drivers/gpu/drm/nouveau/nouveau_exec.c @@ -331,10 +331,10 @@ nouveau_exec_ucopy(struct nouveau_exec_job_args *args, return 0; -err_free_pushs: - u_free(args->push.s); err_free_ins: u_free(args->in_sync.s); +err_free_pushs: + u_free(args->push.s); return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c index 36445915aa58..f5e4756b4de4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c @@ -1779,10 +1779,10 @@ nouveau_uvmm_vm_bind_ucopy(struct nouveau_uvmm_bind_job_args *args, return 0; -err_free_ops: - u_free(args->op.s); err_free_ins: u_free(args->in_sync.s); +err_free_ops: + u_free(args->op.s); return ret; } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c index 4c7745cd6ae5..7fd967a2554f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/acr/base.c @@ -315,6 +315,7 @@ nvkm_acr_oneinit(struct nvkm_subdev *subdev) i, us, fw); } } + nvkm_done(acr->wpr); return -EINVAL; } nvkm_done(acr->wpr); diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 3ac1a79b6f13..533215d6e9cb 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -906,6 +906,7 @@ struct radeon_fence *r100_copy_blit(struct radeon_device *rdev, { struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; struct radeon_fence *fence; + uint64_t cur_src_offset, cur_dst_offset; uint32_t cur_pages; uint32_t stride_bytes = RADEON_GPU_PAGE_SIZE; uint32_t pitch; @@ -934,6 +935,10 @@ struct radeon_fence *r100_copy_blit(struct radeon_device *rdev, cur_pages = 8191; } num_gpu_pages -= cur_pages; + cur_src_offset = src_offset + + (uint64_t)num_gpu_pages * RADEON_GPU_PAGE_SIZE; + cur_dst_offset = dst_offset + + (uint64_t)num_gpu_pages * RADEON_GPU_PAGE_SIZE; /* pages are in Y direction - height page width in X direction - width */ @@ -950,13 +955,13 @@ struct radeon_fence *r100_copy_blit(struct radeon_device *rdev, RADEON_DP_SRC_SOURCE_MEMORY | RADEON_GMC_CLR_CMP_CNTL_DIS | RADEON_GMC_WR_MSK_DIS); - radeon_ring_write(ring, (pitch << 22) | (src_offset >> 10)); - radeon_ring_write(ring, (pitch << 22) | (dst_offset >> 10)); + radeon_ring_write(ring, (pitch << 22) | (cur_src_offset >> 10)); + radeon_ring_write(ring, (pitch << 22) | (cur_dst_offset >> 10)); radeon_ring_write(ring, (0x1fff) | (0x1fff << 16)); radeon_ring_write(ring, 0); radeon_ring_write(ring, (0x1fff) | (0x1fff << 16)); - radeon_ring_write(ring, num_gpu_pages); - radeon_ring_write(ring, num_gpu_pages); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); radeon_ring_write(ring, cur_pages | (stride_pixels << 16)); } radeon_ring_write(ring, PACKET0(RADEON_DSTCACHE_CTLSTAT, 0)); diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h index 2a2b553366fb..547f2327af5e 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h +++ b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h @@ -50,7 +50,7 @@ struct resource *drm_sysfb_get_memory_si(struct drm_device *dev, int drm_sysfb_get_stride_si(struct drm_device *dev, const struct screen_info *si, const struct drm_format_info *format, unsigned int width, unsigned int height, u64 size); -u64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, +s64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, unsigned int height, unsigned int stride, u64 size); #endif diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c index 749290196c6a..1c479ce0e503 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c +++ b/drivers/gpu/drm/sysfb/drm_sysfb_screen_info.c @@ -2,6 +2,7 @@ #include <linux/export.h> #include <linux/limits.h> +#include <linux/math64.h> #include <linux/minmax.h> #include <linux/screen_info.h> @@ -56,18 +57,24 @@ int drm_sysfb_get_stride_si(struct drm_device *dev, const struct screen_info *si unsigned int width, unsigned int height, u64 size) { u64 lfb_linelength = si->lfb_linelength; + s64 stride; if (!lfb_linelength) lfb_linelength = drm_format_info_min_pitch(format, 0, width); - return drm_sysfb_get_validated_int0(dev, "stride", lfb_linelength, div64_u64(size, height)); + stride = drm_sysfb_get_validated_size0(dev, "stride", lfb_linelength, + div64_u64(size, height)); + if (stride < INT_MIN || stride > INT_MAX) + return -EINVAL; + + return (int)stride; /* stride or negative errno code */ } EXPORT_SYMBOL(drm_sysfb_get_stride_si); -u64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, +s64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si, unsigned int height, unsigned int stride, u64 size) { - u64 vsize = PAGE_ALIGN(height * stride); + u64 vsize = mul_u32_u32(height, stride); return drm_sysfb_get_validated_size0(dev, "visible size", vsize, size); } diff --git a/drivers/gpu/drm/sysfb/efidrm.c b/drivers/gpu/drm/sysfb/efidrm.c index 1a1e36700976..3f9cd5d03efb 100644 --- a/drivers/gpu/drm/sysfb/efidrm.c +++ b/drivers/gpu/drm/sysfb/efidrm.c @@ -152,7 +152,8 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv, const struct screen_info *si; const struct drm_format_info *format; int width, height, stride; - u64 vsize, mem_flags; + s64 vsize; + u64 mem_flags; struct resource resbuf; struct resource *res; struct efidrm_device *efi; @@ -206,8 +207,8 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv, if (stride < 0) return ERR_PTR(stride); vsize = drm_sysfb_get_visible_size_si(dev, si, height, stride, resource_size(res)); - if (!vsize) - return ERR_PTR(-EINVAL); + if (vsize < 0) + return ERR_PTR(vsize); drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, stride=%d bytes\n", &format->format, width, height, stride); diff --git a/drivers/gpu/drm/sysfb/vesadrm.c b/drivers/gpu/drm/sysfb/vesadrm.c index dbc317778d54..6a67b2d2e451 100644 --- a/drivers/gpu/drm/sysfb/vesadrm.c +++ b/drivers/gpu/drm/sysfb/vesadrm.c @@ -402,7 +402,7 @@ static struct vesadrm_device *vesadrm_device_create(struct drm_driver *drv, const struct screen_info *si; const struct drm_format_info *format; int width, height, stride; - u64 vsize; + s64 vsize; struct resource resbuf; struct resource *res; struct vesadrm_device *vesa; @@ -457,8 +457,8 @@ static struct vesadrm_device *vesadrm_device_create(struct drm_driver *drv, if (stride < 0) return ERR_PTR(stride); vsize = drm_sysfb_get_visible_size_si(dev, si, height, stride, resource_size(res)); - if (!vsize) - return ERR_PTR(-EINVAL); + if (vsize < 0) + return ERR_PTR(vsize); drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, stride=%d bytes\n", &format->format, width, height, stride); diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 09661f079d03..8e7b146880f4 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -16,14 +16,14 @@ subdir-ccflags-y += -I$(obj) -I$(src) hostprogs := xe_gen_wa_oob generated_oob := $(obj)/generated/xe_wa_oob.c $(obj)/generated/xe_wa_oob.h quiet_cmd_wa_oob = GEN $(notdir $(generated_oob)) - cmd_wa_oob = mkdir -p $(@D); $^ $(generated_oob) + cmd_wa_oob = mkdir -p $(@D); $(obj)/xe_gen_wa_oob $(src)/xe_wa_oob.rules $(generated_oob) $(obj)/generated/%_wa_oob.c $(obj)/generated/%_wa_oob.h: $(obj)/xe_gen_wa_oob \ $(src)/xe_wa_oob.rules $(call cmd,wa_oob) generated_device_oob := $(obj)/generated/xe_device_wa_oob.c $(obj)/generated/xe_device_wa_oob.h quiet_cmd_device_wa_oob = GEN $(notdir $(generated_device_oob)) - cmd_device_wa_oob = mkdir -p $(@D); $^ $(generated_device_oob) + cmd_device_wa_oob = mkdir -p $(@D); $(obj)/xe_gen_wa_oob $(src)/xe_device_wa_oob.rules $(generated_device_oob) $(obj)/generated/%_device_wa_oob.c $(obj)/generated/%_device_wa_oob.h: $(obj)/xe_gen_wa_oob \ $(src)/xe_device_wa_oob.rules $(call cmd,device_wa_oob) diff --git a/drivers/gpu/drm/xe/regs/xe_gtt_defs.h b/drivers/gpu/drm/xe/regs/xe_gtt_defs.h index 4d83461e538b..d6bc19ef277b 100644 --- a/drivers/gpu/drm/xe/regs/xe_gtt_defs.h +++ b/drivers/gpu/drm/xe/regs/xe_gtt_defs.h @@ -9,7 +9,11 @@ #define XELPG_GGTT_PTE_PAT0 BIT_ULL(52) #define XELPG_GGTT_PTE_PAT1 BIT_ULL(53) -#define XE_PTE_ADDR_MASK GENMASK_ULL(51, 12) +/* + * Mask for PTE address bits [51:shift]. + * shift is the lower address boundary of page. + */ +#define XE_PAGE_ADDR_MASK(shift) GENMASK_ULL(51, (shift)) #define GGTT_PTE_VFID GENMASK_ULL(11, 2) #define GUC_GGTT_TOP 0xFEE00000 diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index d224861b6f6f..abe25aedeead 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -526,7 +526,8 @@ int xe_device_init_early(struct xe_device *xe) err = ttm_device_init(&xe->ttm, &xe_ttm_funcs, xe->drm.dev, xe->drm.anon_inode->i_mapping, - xe->drm.vma_offset_manager, 0); + xe->drm.vma_offset_manager, + TTM_ALLOCATION_POOL_BENEFICIAL_ORDER(get_order(SZ_2M))); if (err) return err; diff --git a/drivers/gpu/drm/xe/xe_guc_capture.c b/drivers/gpu/drm/xe/xe_guc_capture.c index 21f7caf9ea08..1a019137ddf4 100644 --- a/drivers/gpu/drm/xe/xe_guc_capture.c +++ b/drivers/gpu/drm/xe/xe_guc_capture.c @@ -461,8 +461,14 @@ static void guc_capture_alloc_steered_lists(struct xe_guc *guc) if (!list || guc->capture->extlists) return; - total = bitmap_weight(gt->fuse_topo.g_dss_mask, sizeof(gt->fuse_topo.g_dss_mask) * 8) * - guc_capture_get_steer_reg_num(guc_to_xe(guc)); + { + xe_dss_mask_t all_dss; + + total = bitmap_weighted_or(all_dss, gt->fuse_topo.g_dss_mask, + gt->fuse_topo.c_dss_mask, + XE_MAX_DSS_FUSE_BITS) * + guc_capture_get_steer_reg_num(guc_to_xe(guc)); + } if (!total) return; diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 2669ff5ee747..18a98667c0e6 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -1602,23 +1602,21 @@ static bool xe_pt_check_kill(u64 addr, u64 next, unsigned int level, return false; } -/* page_size = 2^(reclamation_size + XE_PTE_SHIFT) */ -#define COMPUTE_RECLAIM_ADDRESS_MASK(page_size) \ -({ \ - BUILD_BUG_ON(!__builtin_constant_p(page_size)); \ - ilog2(page_size) - XE_PTE_SHIFT; \ -}) - static int generate_reclaim_entry(struct xe_tile *tile, struct xe_page_reclaim_list *prl, u64 pte, struct xe_pt *xe_child) { struct xe_gt *gt = tile->primary_gt; struct xe_guc_page_reclaim_entry *reclaim_entries = prl->entries; - u64 phys_addr = pte & XE_PTE_ADDR_MASK; + bool is_2m = xe_child->level == 1 && (pte & XE_PDE_PS_2M); + bool is_64k = xe_child->level == 0 && ((pte & XE_PTE_PS64) || xe_child->is_compact); + u32 page_shift = is_2m ? ilog2(SZ_2M) : is_64k ? ilog2(SZ_64K) : ilog2(SZ_4K); + /* Physical address bits start at page shift: 2M->[51:21], 64K->[51:16], 4K->[51:12] */ + u64 phys_addr = pte & XE_PAGE_ADDR_MASK(page_shift); + /* Page address is relative to 4K page regardless of entry level */ u64 phys_page = phys_addr >> XE_PTE_SHIFT; int num_entries = prl->num_entries; - u32 reclamation_size; + u32 reclamation_size = page_shift - XE_PTE_SHIFT; xe_tile_assert(tile, xe_child->level <= MAX_HUGEPTE_LEVEL); xe_tile_assert(tile, reclaim_entries); @@ -1633,18 +1631,12 @@ static int generate_reclaim_entry(struct xe_tile *tile, * Page size is computed as 2^(reclamation_size + XE_PTE_SHIFT) bytes. * Only 4K, 64K (level 0), and 2M pages are supported by hardware for page reclaim */ - if (xe_child->level == 0 && !(pte & XE_PTE_PS64)) { - xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_4K_ENTRY_COUNT, 1); - reclamation_size = COMPUTE_RECLAIM_ADDRESS_MASK(SZ_4K); /* reclamation_size = 0 */ - xe_tile_assert(tile, phys_addr % SZ_4K == 0); - } else if (xe_child->level == 0) { - xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_64K_ENTRY_COUNT, 1); - reclamation_size = COMPUTE_RECLAIM_ADDRESS_MASK(SZ_64K); /* reclamation_size = 4 */ - xe_tile_assert(tile, phys_addr % SZ_64K == 0); - } else if (xe_child->level == 1 && pte & XE_PDE_PS_2M) { + if (is_2m) { xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_2M_ENTRY_COUNT, 1); - reclamation_size = COMPUTE_RECLAIM_ADDRESS_MASK(SZ_2M); /* reclamation_size = 9 */ - xe_tile_assert(tile, phys_addr % SZ_2M == 0); + } else if (is_64k) { + xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_64K_ENTRY_COUNT, 1); + } else if (xe_child->level == 0) { + xe_gt_stats_incr(gt, XE_GT_STATS_ID_PRL_4K_ENTRY_COUNT, 1); } else { xe_page_reclaim_list_abort(tile->primary_gt, prl, "unsupported PTE level=%u pte=%#llx", @@ -1665,6 +1657,48 @@ static int generate_reclaim_entry(struct xe_tile *tile, return 0; } +static int add_pte_to_prl(struct xe_tile *tile, struct xe_page_reclaim_list *prl, + struct xe_pt *xe_child, u64 pte, u64 addr) +{ + /* + * In rare scenarios, pte may not be written yet due to racy conditions. + * In such cases, invalidate the PRL and fallback to full PPC invalidation. + */ + if (!pte) { + xe_page_reclaim_list_abort(tile->primary_gt, prl, + "found zero pte at addr=%#llx", addr); + return -EINVAL; + } + + /* Ensure it is a defined page */ + xe_tile_assert(tile, xe_child->level == 0 || + (pte & (XE_PDE_PS_2M | XE_PDPE_PS_1G))); + + /* Account for NULL terminated entry on end (-1) */ + if (prl->num_entries >= XE_PAGE_RECLAIM_MAX_ENTRIES - 1) { + xe_page_reclaim_list_abort(tile->primary_gt, prl, + "overflow while adding pte=%#llx", pte); + return -ENOSPC; + } + + return generate_reclaim_entry(tile, prl, pte, xe_child); +} + +static bool add_compact_pt_prl(struct xe_tile *tile, struct xe_page_reclaim_list *prl, + struct xe_device *xe, struct xe_pt *compact_pt, u64 addr) +{ + struct iosys_map *map = &compact_pt->bo->vmap; + + for (pgoff_t i = 0; i < SZ_2M / SZ_64K && xe_page_reclaim_list_valid(prl); i++) { + u64 pte = xe_map_rd(xe, map, i * sizeof(u64), u64); + + if (add_pte_to_prl(tile, prl, compact_pt, pte, addr + i * SZ_64K)) + break; + } + + return xe_page_reclaim_list_valid(prl); +} + static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, unsigned int level, u64 addr, u64 next, struct xe_ptw **child, @@ -1674,21 +1708,22 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, struct xe_pt *xe_child = container_of(*child, typeof(*xe_child), base); struct xe_pt_stage_unbind_walk *xe_walk = container_of(walk, typeof(*xe_walk), base); - struct xe_device *xe = tile_to_xe(xe_walk->tile); + struct xe_page_reclaim_list *prl = xe_walk->prl; + struct xe_tile *tile = xe_walk->tile; + struct xe_device *xe = tile_to_xe(tile); pgoff_t first = xe_pt_offset(addr, xe_child->level, walk); bool killed; XE_WARN_ON(!*child); XE_WARN_ON(!level); /* Check for leaf node */ - if (xe_walk->prl && xe_page_reclaim_list_valid(xe_walk->prl) && + if (prl && xe_page_reclaim_list_valid(prl) && xe_child->level <= MAX_HUGEPTE_LEVEL) { struct iosys_map *leaf_map = &xe_child->bo->vmap; pgoff_t count = xe_pt_num_entries(addr, next, xe_child->level, walk); for (pgoff_t i = 0; i < count; i++) { u64 pte; - int ret; /* * If not a leaf pt, skip unless non-leaf pt is interleaved between @@ -1698,10 +1733,23 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, u64 pt_size = 1ULL << walk->shifts[xe_child->level]; bool edge_pt = (i == 0 && !IS_ALIGNED(addr, pt_size)) || (i == count - 1 && !IS_ALIGNED(next, pt_size)); - - if (!edge_pt) { - xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, - xe_walk->prl, + struct xe_pt *child_pt = + container_of(xe_child->base.children[first + i], + struct xe_pt, base); + + /* Compact PTs always fill a full 2M-aligned slot, never an edge. */ + XE_WARN_ON(child_pt->is_compact && edge_pt); + if (edge_pt) + continue; + + /* Walker never descends into compact PTs, descend now */ + if (child_pt->is_compact) { + if (!add_compact_pt_prl(tile, prl, xe, child_pt, + addr + (u64)i * pt_size)) + break; + } else { + xe_page_reclaim_list_abort(tile->primary_gt, + prl, "PT is skipped by walk at level=%u offset=%lu", xe_child->level, first + i); break; @@ -1711,37 +1759,12 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, pte = xe_map_rd(xe, leaf_map, (first + i) * sizeof(u64), u64); - /* - * In rare scenarios, pte may not be written yet due to racy conditions. - * In such cases, invalidate the PRL and fallback to full PPC invalidation. - */ - if (!pte) { - xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, xe_walk->prl, - "found zero pte at addr=%#llx", addr); + if (add_pte_to_prl(tile, prl, xe_child, pte, addr)) break; - } - - /* Ensure it is a defined page */ - xe_tile_assert(xe_walk->tile, xe_child->level == 0 || - (pte & (XE_PDE_PS_2M | XE_PDPE_PS_1G))); /* An entry should be added for 64KB but contigious 4K have XE_PTE_PS64 */ if (pte & XE_PTE_PS64) i += 15; /* Skip other 15 consecutive 4K pages in the 64K page */ - - /* Account for NULL terminated entry on end (-1) */ - if (xe_walk->prl->num_entries < XE_PAGE_RECLAIM_MAX_ENTRIES - 1) { - ret = generate_reclaim_entry(xe_walk->tile, xe_walk->prl, - pte, xe_child); - if (ret) - break; - } else { - /* overflow, mark as invalid */ - xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, xe_walk->prl, - "overflow while adding pte=%#llx", - pte); - break; - } } } @@ -1751,7 +1774,7 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, * Verify if any PTE are potentially dropped at non-leaf levels, either from being * killed or the page walk covers the region. */ - if (xe_walk->prl && xe_page_reclaim_list_valid(xe_walk->prl) && + if (prl && xe_page_reclaim_list_valid(prl) && xe_child->level > MAX_HUGEPTE_LEVEL && xe_child->num_live) { bool covered = xe_pt_covers(addr, next, xe_child->level, &xe_walk->base); @@ -1760,7 +1783,7 @@ static int xe_pt_stage_unbind_entry(struct xe_ptw *parent, pgoff_t offset, * we need to invalidate the PRL. */ if (killed || covered) - xe_page_reclaim_list_abort(xe_walk->tile->primary_gt, xe_walk->prl, + xe_page_reclaim_list_abort(tile->primary_gt, prl, "kill at level=%u addr=%#llx next=%#llx num_live=%u", level, addr, next, xe_child->num_live); } diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5c2d3ff5fce8..2bfbcc033d59 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1098,6 +1098,7 @@ config SENSORS_LTC2992 tristate "Linear Technology LTC2992" depends on I2C depends on GPIOLIB + select REGMAP_I2C help If you say yes here you get support for Linear Technology LTC2992 I2C System Monitor. The LTC2992 measures current, voltage, and @@ -1248,6 +1249,7 @@ config SENSORS_MAX16065 config SENSORS_MAX1619 tristate "Maxim MAX1619 sensor chip" depends on I2C + select REGMAP help If you say yes here you get support for MAX1619 sensor chip. @@ -1366,6 +1368,7 @@ config SENSORS_MAX6650 config SENSORS_MAX6697 tristate "Maxim MAX6697 and compatibles" depends on I2C + select REGMAP_I2C help If you say yes here you get support for MAX6581, MAX6602, MAX6622, MAX6636, MAX6689, MAX6693, MAX6694, MAX6697, MAX6698, and MAX6699 diff --git a/drivers/hwmon/aspeed-g6-pwm-tach.c b/drivers/hwmon/aspeed-g6-pwm-tach.c index 4f6e6d440dd4..5d611a8e5269 100644 --- a/drivers/hwmon/aspeed-g6-pwm-tach.c +++ b/drivers/hwmon/aspeed-g6-pwm-tach.c @@ -293,7 +293,10 @@ static int aspeed_tach_val_to_rpm(struct aspeed_pwm_tach_data *priv, u32 tach_va priv->clk_rate, tach_val, tach_div); rpm = (u64)priv->clk_rate * 60; - do_div(rpm, tach_div); + if (tach_div) + do_div(rpm, tach_div); + else + rpm = 0; return (int)rpm; } diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index 109318b0434d..92afb64c09df 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -1037,6 +1037,9 @@ static int atk_ec_present(struct atk_data *data) if (obj->type != ACPI_TYPE_PACKAGE) continue; + if (!obj->package.count) + continue; + id = &obj->package.elements[0]; if (id->type != ACPI_TYPE_INTEGER) continue; diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index 42cc6068bb08..e18e80e832fd 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -214,6 +214,11 @@ int occ_update_response(struct occ *occ) if (rc) return rc; + if (!occ->active) { + rc = -ENODEV; + goto unlock; + } + /* limit the maximum rate of polling the OCC */ if (time_after(jiffies, occ->next_update)) { rc = occ_poll(occ); @@ -222,6 +227,7 @@ int occ_update_response(struct occ *occ) rc = occ->last_error; } +unlock: mutex_unlock(&occ->lock); return rc; } @@ -1105,11 +1111,16 @@ static void occ_parse_poll_response(struct occ *occ) int occ_active(struct occ *occ, bool active) { - int rc = mutex_lock_interruptible(&occ->lock); + struct device *hwmon = NULL; + int rc = mutex_lock_interruptible(&occ->hwmon_lock); if (rc) return rc; + rc = mutex_lock_interruptible(&occ->lock); + if (rc) + goto unlock_hwmon; + if (active) { if (occ->active) { rc = -EALREADY; @@ -1154,14 +1165,17 @@ int occ_active(struct occ *occ, bool active) goto unlock; } - if (occ->hwmon) - hwmon_device_unregister(occ->hwmon); + hwmon = occ->hwmon; occ->active = false; occ->hwmon = NULL; } unlock: mutex_unlock(&occ->lock); + if (hwmon) + hwmon_device_unregister(hwmon); +unlock_hwmon: + mutex_unlock(&occ->hwmon_lock); return rc; } @@ -1170,6 +1184,7 @@ int occ_setup(struct occ *occ) int rc; mutex_init(&occ->lock); + mutex_init(&occ->hwmon_lock); occ->groups[0] = &occ->group; rc = occ_setup_sysfs(occ); @@ -1190,15 +1205,22 @@ EXPORT_SYMBOL_GPL(occ_setup); void occ_shutdown(struct occ *occ) { - mutex_lock(&occ->lock); + struct device *hwmon; occ_shutdown_sysfs(occ); - if (occ->hwmon) - hwmon_device_unregister(occ->hwmon); + mutex_lock(&occ->hwmon_lock); + mutex_lock(&occ->lock); + + hwmon = occ->hwmon; + occ->active = false; occ->hwmon = NULL; mutex_unlock(&occ->lock); + + if (hwmon) + hwmon_device_unregister(hwmon); + mutex_unlock(&occ->hwmon_lock); } EXPORT_SYMBOL_GPL(occ_shutdown); diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h index 7ac4b2febce6..82f600093c7f 100644 --- a/drivers/hwmon/occ/common.h +++ b/drivers/hwmon/occ/common.h @@ -101,6 +101,7 @@ struct occ { unsigned long next_update; struct mutex lock; /* lock OCC access */ + struct mutex hwmon_lock; /* serialize hwmon registration/removal */ struct device *hwmon; struct occ_attribute *attrs; diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index 2e5963fb1e12..d3c3ff85dba3 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -512,7 +512,7 @@ static int adm1275_enable_vout_temp(struct adm1275_data *data, static int adm1275_probe(struct i2c_client *client) { s32 (*config_read_fn)(const struct i2c_client *client, u8 reg); - u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1] = {0}; int config, device_config; int ret; struct pmbus_driver_info *info; @@ -839,15 +839,25 @@ static int adm1275_probe(struct i2c_client *client) info->R[PSC_VOLTAGE_OUT] = coefficients[voindex].R; } if (cindex >= 0) { + u32 m; + /* Scale current with sense resistor value */ - info->m[PSC_CURRENT_OUT] = - coefficients[cindex].m * shunt / 1000; + if (unlikely(check_mul_overflow(coefficients[cindex].m, shunt, &m))) { + dev_err(&client->dev, "Current coefficient overflow\n"); + return -EOVERFLOW; + } + info->m[PSC_CURRENT_OUT] = m / 1000; info->b[PSC_CURRENT_OUT] = coefficients[cindex].b; info->R[PSC_CURRENT_OUT] = coefficients[cindex].R; } if (pindex >= 0) { - info->m[PSC_POWER] = - coefficients[pindex].m * shunt / 1000; + u32 m; + + if (unlikely(check_mul_overflow(coefficients[pindex].m, shunt, &m))) { + dev_err(&client->dev, "Power coefficient overflow\n"); + return -EOVERFLOW; + } + info->m[PSC_POWER] = m / 1000; info->b[PSC_POWER] = coefficients[pindex].b; info->R[PSC_POWER] = coefficients[pindex].R; } diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index e8fdd799c71c..3143b9e0316c 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -1095,9 +1095,27 @@ static u16 pmbus_data2reg_direct(struct pmbus_data *data, static u16 pmbus_data2reg_vid(struct pmbus_data *data, struct pmbus_sensor *sensor, s64 val) { - val = clamp_val(val, 500, 1600); - - return 2 + DIV_ROUND_CLOSEST_ULL((1600LL - val) * 100LL, 625); + switch (data->info->vrm_version[sensor->page]) { + case vr12: + val = clamp_val(val, 250, 1520); + return 1 + DIV_ROUND_CLOSEST_ULL(val - 250, 5); + case vr13: + val = clamp_val(val, 500, 3040); + return 1 + DIV_ROUND_CLOSEST_ULL(val - 500, 10); + case imvp9: + val = clamp_val(val, 200, 2740); + return 1 + DIV_ROUND_CLOSEST_ULL(val - 200, 10); + case amd625mv: + val = clamp_val(val, 200, 1550); + return DIV_ROUND_CLOSEST_ULL((1550LL - val) * 100LL, 625); + case nvidia195mv: + val = clamp_val(val, 195, 1465); + return 1 + DIV_ROUND_CLOSEST_ULL(val - 195, 5); + case vr11: + default: + val = clamp_val(val, 500, 1600); + return 2 + DIV_ROUND_CLOSEST_ULL((1600LL - val) * 100LL, 625); + } } static u16 pmbus_data2reg(struct pmbus_data *data, @@ -3329,18 +3347,23 @@ static void pmbus_regulator_notify_worker(struct work_struct *work) int i, j; for (i = 0; i < data->info->pages; i++) { - int event; + unsigned int event; event = atomic_xchg(&data->regulator_events[i], 0); if (!event) continue; for (j = 0; j < data->info->num_regulators; j++) { - if (i == rdev_get_id(data->rdevs[j])) { + if (i != rdev_get_id(data->rdevs[j])) + continue; + while (event) { + unsigned int _event = BIT(__ffs(event)); + regulator_notifier_call_chain(data->rdevs[j], - event, NULL); - break; + _event, NULL); + event &= ~_event; } + break; } } } diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index 95115d7b863e..bb993bb09f40 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -1823,6 +1823,8 @@ static int w83627hf_probe(struct platform_device *pdev) return 0; error: + device_remove_file(dev, &dev_attr_vrm); + device_remove_file(dev, &dev_attr_cpu0_vid); sysfs_remove_group(&dev->kobj, &w83627hf_group); sysfs_remove_group(&dev->kobj, &w83627hf_group_opt); return err; @@ -1834,6 +1836,8 @@ static void w83627hf_remove(struct platform_device *pdev) hwmon_device_unregister(data->hwmon_dev); + device_remove_file(&pdev->dev, &dev_attr_vrm); + device_remove_file(&pdev->dev, &dev_attr_cpu0_vid); sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group); sysfs_remove_group(&pdev->dev.kobj, &w83627hf_group_opt); } diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index b1f906f06ab4..a548586369e1 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -1917,6 +1917,7 @@ exit_remove: for (i = 0; i < ARRAY_SIZE(w83793_vid); i++) device_remove_file(dev, &w83793_vid[i].dev_attr); + device_remove_file(dev, &dev_attr_vrm); for (i = 0; i < ARRAY_SIZE(w83793_left_fan); i++) device_remove_file(dev, &w83793_left_fan[i].dev_attr); diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 32a3cef02c7b..b29c99ed3883 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -931,13 +931,13 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, */ if (hwpec) iowrite8(ioread8(SMBAUXCTL(priv)) & ~SMBAUXCTL_CRC, SMBAUXCTL(priv)); -out: /* * Unlock the SMBus device for use by BIOS/ACPI, * and clear status flags if not done already. */ iowrite8(SMBHSTSTS_INUSE_STS | STATUS_FLAGS, SMBHSTSTS(priv)); +out: pm_runtime_put_autosuspend(&priv->pci_dev->dev); mutex_unlock(&priv->acpi_lock); return ret; diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 28c5c5c1fb7a..a21fa45bd64c 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -844,7 +844,7 @@ static int fsl_i2c_probe(struct platform_device *op) "fsl,timeout", &mpc_ops.timeout); if (!result) { - mpc_ops.timeout *= HZ / 1000000; + mpc_ops.timeout = mpc_ops.timeout * HZ / 1000000; if (mpc_ops.timeout < 5) mpc_ops.timeout = 5; } else { diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c index 49a59da6a841..5d49a9021c7d 100644 --- a/drivers/input/rmi4/rmi_driver.c +++ b/drivers/input/rmi4/rmi_driver.c @@ -616,8 +616,8 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, unsigned int presence_offset; unsigned int map_offset; unsigned int offset; + unsigned int num_registers; unsigned int reg; - int i; int b; int ret; @@ -643,7 +643,7 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, ret = rmi_read_block(d, addr, buf, size_presence_reg); if (ret) return ret; - addr += size_presence_reg; + ++addr; if (buf[0] == 0) { if (size_presence_reg < 3) @@ -657,7 +657,7 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, memset(presence_map, 0, sizeof(presence_map)); map_offset = 0; - for (i = presence_offset; i < size_presence_reg; i++) { + for (int i = presence_offset; i < size_presence_reg; i++) { for (b = 0; b < 8; b++) { if (buf[i] & BIT(b)) { if (map_offset >= RMI_REG_DESC_PRESENCE_BITS) @@ -697,28 +697,41 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr, if (ret) return ret; - reg = find_first_bit(presence_map, RMI_REG_DESC_PRESENCE_BITS); offset = 0; - for (i = 0; i < rdesc->num_registers; i++) { - struct rmi_register_desc_item *item = &rdesc->registers[i]; + num_registers = 0; + for_each_set_bit(reg, presence_map, RMI_REG_DESC_PRESENCE_BITS) { + struct rmi_register_desc_item *item = &rdesc->registers[num_registers]; int item_size; + if (offset >= rdesc->struct_size) + break; + item_size = rmi_parse_register_desc_item(item, &struct_buf[offset], rdesc->struct_size - offset); - if (item_size < 0) - return item_size; + if (item_size < 0) { + dev_warn(&d->dev, + "%s: Failed to parse register %d descriptor, ignoring it\n", + __func__, reg); + break; + } item->reg = reg; offset += item_size; - rmi_dbg(RMI_DEBUG_CORE, &d->dev, - "%s: reg: %d reg size: %u subpackets: %d\n", __func__, - item->reg, item->reg_size, item->num_subpackets); + if (item->reg_size == 0) { + dev_warn(&d->dev, + "%s: Register %d has 0 size, ignoring it\n", + __func__, item->reg); + } else { + rmi_dbg(RMI_DEBUG_CORE, &d->dev, + "%s: reg: %d reg size: %u subpackets: %d\n", __func__, + item->reg, item->reg_size, item->num_subpackets); - reg = find_next_bit(presence_map, - RMI_REG_DESC_PRESENCE_BITS, reg + 1); + num_registers++; + } } + rdesc->num_registers = num_registers; return 0; } diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c index 35045f161dc2..b2155c8e20e7 100644 --- a/drivers/input/rmi4/rmi_f30.c +++ b/drivers/input/rmi4/rmi_f30.c @@ -233,7 +233,7 @@ static int rmi_f30_map_gpios(struct rmi_function *fn, int button_count = min_t(u8, f30->gpioled_count, TRACKSTICK_RANGE_END); f30->gpioled_key_map = devm_kcalloc(&fn->dev, - button_count, + f30->gpioled_count, sizeof(f30->gpioled_key_map[0]), GFP_KERNEL); if (!f30->gpioled_key_map) { diff --git a/drivers/input/rmi4/rmi_f3a.c b/drivers/input/rmi4/rmi_f3a.c index 0e8baed84dbb..a0777644eef0 100644 --- a/drivers/input/rmi4/rmi_f3a.c +++ b/drivers/input/rmi4/rmi_f3a.c @@ -132,7 +132,7 @@ static int rmi_f3a_map_gpios(struct rmi_function *fn, struct f3a_data *f3a, int button_count = min_t(u8, f3a->gpio_count, TRACKSTICK_RANGE_END); f3a->gpio_key_map = devm_kcalloc(&fn->dev, - button_count, + f3a->gpio_count, sizeof(f3a->gpio_key_map[0]), GFP_KERNEL); if (!f3a->gpio_key_map) { diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c index 22b2f57fd91f..bf9b993f5733 100644 --- a/drivers/input/serio/gscps2.c +++ b/drivers/input/serio/gscps2.c @@ -219,6 +219,7 @@ static void gscps2_read_data(struct gscps2port *ps2port) ps2port->buffer[ps2port->append].str = status; ps2port->buffer[ps2port->append].data = gscps2_readb_input(ps2port->addr); + ps2port->append = (ps2port->append + 1) & BUFFER_SIZE; } while (true); } diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index ce572c62f58f..73a9db2c7537 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -443,6 +443,26 @@ struct icc_path *devm_of_icc_get(struct device *dev, const char *name) } EXPORT_SYMBOL_GPL(devm_of_icc_get); +struct icc_path *devm_of_icc_get_by_index(struct device *dev, int idx) +{ + struct icc_path **ptr, *path; + + ptr = devres_alloc(devm_icc_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + path = of_icc_get_by_index(dev, idx); + if (!IS_ERR(path)) { + *ptr = path; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return path; +} +EXPORT_SYMBOL_GPL(devm_of_icc_get_by_index); + /** * of_icc_get_by_index() - get a path handle from a DT node based on index * @dev: device pointer for the consumer device diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c index 932b3a3df2e5..59001fd4b6f7 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.c +++ b/drivers/net/ethernet/airoha/airoha_eth.c @@ -178,10 +178,15 @@ static void airoha_fe_maccr_init(struct airoha_eth *eth) { int p; - for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) + for (p = 1; p <= ARRAY_SIZE(eth->ports); p++) { airoha_fe_set(eth, REG_GDM_FWD_CFG(p), GDM_TCP_CKSUM_MASK | GDM_UDP_CKSUM_MASK | GDM_IP4_CKSUM_MASK | GDM_DROP_CRC_ERR_MASK); + airoha_fe_rmw(eth, REG_GDM_LEN_CFG(p), + GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, + FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | + FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_RX_SIZE)); + } airoha_fe_rmw(eth, REG_CDM_VLAN_CTRL(1), CDM_VLAN_MASK, FIELD_PREP(CDM_VLAN_MASK, 0x8100)); @@ -944,6 +949,25 @@ static void airoha_qdma_wake_netdev_txqs(struct airoha_queue *q) q->txq_stopped = false; } +static void airoha_unmap_xmit_buf(struct airoha_eth *eth, + struct airoha_queue_entry *e) +{ + switch (e->dma_type) { + case AIROHA_DMA_MAP_PAGE: + dma_unmap_page(eth->dev, e->dma_addr, e->dma_len, + DMA_TO_DEVICE); + break; + case AIROHA_DMA_MAP_SINGLE: + dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, + DMA_TO_DEVICE); + break; + case AIROHA_DMA_UNMAPPED: + default: + break; + } + e->dma_type = AIROHA_DMA_UNMAPPED; +} + static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget) { struct airoha_tx_irq_queue *irq_q; @@ -1006,9 +1030,7 @@ static int airoha_qdma_tx_napi_poll(struct napi_struct *napi, int budget) skb = e->skb; e->skb = NULL; - dma_unmap_single(eth->dev, e->dma_addr, e->dma_len, - DMA_TO_DEVICE); - e->dma_addr = 0; + airoha_unmap_xmit_buf(eth, e); list_add_tail(&e->list, &q->tx_list); WRITE_ONCE(desc->msg0, 0); @@ -1177,12 +1199,10 @@ static void airoha_qdma_tx_cleanup(struct airoha_qdma *qdma) struct airoha_qdma_desc *desc = &q->desc[j]; struct sk_buff *skb = e->skb; - if (!e->dma_addr) + if (e->dma_type == AIROHA_DMA_UNMAPPED) continue; - dma_unmap_single(qdma->eth->dev, e->dma_addr, - e->dma_len, DMA_TO_DEVICE); - e->dma_addr = 0; + airoha_unmap_xmit_buf(qdma->eth, e); list_add_tail(&e->list, &q->tx_list); WRITE_ONCE(desc->ctrl, 0); @@ -1831,13 +1851,24 @@ static void airoha_update_hw_stats(struct airoha_gdm_dev *dev) spin_unlock(&port->stats_lock); } +static void airoha_dev_set_xmit_frame_size(struct net_device *netdev) +{ + struct airoha_gdm_dev *dev = netdev_priv(netdev); + + airoha_ppe_set_xmit_frame_size(dev); + if (!airoha_is_lan_gdm_dev(dev)) + airoha_fe_rmw(dev->eth, REG_WAN_MTU0, WAN_MTU0_MASK, + FIELD_PREP(WAN_MTU0_MASK, + VLAN_ETH_HLEN + netdev->mtu)); +} + static int airoha_dev_open(struct net_device *netdev) { - int err, len = ETH_HLEN + netdev->mtu + ETH_FCS_LEN; struct airoha_gdm_dev *dev = netdev_priv(netdev); struct airoha_gdm_port *port = dev->port; - u32 cur_len, pse_port = FE_PSE_PORT_PPE1; struct airoha_qdma *qdma = dev->qdma; + u32 pse_port = FE_PSE_PORT_PPE1; + int err; netif_tx_start_all_queues(netdev); err = airoha_set_vip_for_gdm_port(dev, true); @@ -1851,19 +1882,7 @@ static int airoha_dev_open(struct net_device *netdev) airoha_fe_clear(qdma->eth, REG_GDM_INGRESS_CFG(port->id), GDM_STAG_EN_MASK); - cur_len = airoha_fe_get(qdma->eth, REG_GDM_LEN_CFG(port->id), - GDM_LONG_LEN_MASK); - if (!port->users || len > cur_len) { - /* Opening a sibling net_device with a larger MTU updates the - * MTU of already running devices. This is required to allow - * multiple net_devices with different MTUs to share the same - * GDM port. - */ - airoha_fe_rmw(qdma->eth, REG_GDM_LEN_CFG(port->id), - GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, - FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | - FIELD_PREP(GDM_LONG_LEN_MASK, len)); - } + airoha_dev_set_xmit_frame_size(netdev); port->users++; if (!airoha_is_lan_gdm_dev(dev) && @@ -1875,30 +1894,6 @@ static int airoha_dev_open(struct net_device *netdev) return 0; } -static void airoha_set_port_mtu(struct airoha_eth *eth, - struct airoha_gdm_port *port) -{ - u32 len = 0; - int i; - - for (i = 0; i < ARRAY_SIZE(port->devs); i++) { - struct airoha_gdm_dev *dev = port->devs[i]; - struct net_device *netdev; - - if (!dev) - continue; - - netdev = netdev_from_priv(dev); - if (netif_running(netdev)) - len = max_t(u32, len, netdev->mtu); - } - len += ETH_HLEN + ETH_FCS_LEN; - - airoha_fe_rmw(eth, REG_GDM_LEN_CFG(port->id), - GDM_LONG_LEN_MASK, - FIELD_PREP(GDM_LONG_LEN_MASK, len)); -} - static int airoha_dev_stop(struct net_device *netdev) { struct airoha_gdm_dev *dev = netdev_priv(netdev); @@ -1909,7 +1904,7 @@ static int airoha_dev_stop(struct net_device *netdev) airoha_set_vip_for_gdm_port(dev, false); if (--port->users) - airoha_set_port_mtu(dev->eth, port); + airoha_ppe_set_xmit_frame_size(dev); else airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id), @@ -1962,10 +1957,6 @@ static int airoha_enable_gdm2_loopback(struct airoha_gdm_dev *dev) FIELD_PREP(LPBK_CHAN_MASK, chan) | LBK_GAP_MODE_MASK | LBK_LEN_MODE_MASK | LBK_CHAN_MODE_MASK | LPBK_EN_MASK); - airoha_fe_rmw(eth, REG_GDM_LEN_CFG(AIROHA_GDM2_IDX), - GDM_SHORT_LEN_MASK | GDM_LONG_LEN_MASK, - FIELD_PREP(GDM_SHORT_LEN_MASK, 60) | - FIELD_PREP(GDM_LONG_LEN_MASK, AIROHA_MAX_MTU)); /* Forward the traffic to the proper GDM port */ pse_port = port->id == AIROHA_GDM3_IDX ? FE_PSE_PORT_GDM3 : FE_PSE_PORT_GDM4; @@ -2098,7 +2089,7 @@ static int airoha_dev_change_mtu(struct net_device *netdev, int mtu) WRITE_ONCE(netdev->mtu, mtu); if (port->users) - airoha_set_port_mtu(dev->eth, port); + airoha_dev_set_xmit_frame_size(netdev); return 0; } @@ -2193,8 +2184,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, struct netdev_queue *txq; struct airoha_queue *q; LIST_HEAD(tx_list); + dma_addr_t addr; int i = 0, qid; - void *data; u16 index; u8 fport; @@ -2250,24 +2241,22 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, return NETDEV_TX_BUSY; } - len = skb_headlen(skb); - data = skb->data; - e = list_first_entry(&q->tx_list, struct airoha_queue_entry, list); + len = skb_headlen(skb); + addr = dma_map_single(netdev->dev.parent, skb->data, len, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(netdev->dev.parent, addr))) + goto error_unlock; + + e->dma_type = AIROHA_DMA_MAP_SINGLE; index = e - q->entry; while (true) { struct airoha_qdma_desc *desc = &q->desc[index]; skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; - dma_addr_t addr; u32 val; - addr = dma_map_single(netdev->dev.parent, data, len, - DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(netdev->dev.parent, addr))) - goto error_unmap; - list_move_tail(&e->list, &tx_list); e->skb = i == nr_frags - 1 ? skb : NULL; e->dma_addr = addr; @@ -2291,8 +2280,13 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, if (++i == nr_frags) break; - data = skb_frag_address(frag); len = skb_frag_size(frag); + addr = skb_frag_dma_map(netdev->dev.parent, frag, 0, len, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(netdev->dev.parent, addr))) + goto error_unmap; + + e->dma_type = AIROHA_DMA_MAP_PAGE; } q->queued += i; @@ -2313,11 +2307,8 @@ static netdev_tx_t airoha_dev_xmit(struct sk_buff *skb, return NETDEV_TX_OK; error_unmap: - list_for_each_entry(e, &tx_list, list) { - dma_unmap_single(netdev->dev.parent, e->dma_addr, e->dma_len, - DMA_TO_DEVICE); - e->dma_addr = 0; - } + list_for_each_entry(e, &tx_list, list) + airoha_unmap_xmit_buf(dev->eth, e); list_splice(&tx_list, &q->tx_list); error_unlock: spin_unlock_bh(&q->lock); diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h index d7ff8c5200e2..f6d01a8e8da1 100644 --- a/drivers/net/ethernet/airoha/airoha_eth.h +++ b/drivers/net/ethernet/airoha/airoha_eth.h @@ -23,6 +23,7 @@ #define AIROHA_MAX_DSA_PORTS 7 #define AIROHA_MAX_NUM_RSTS 3 #define AIROHA_MAX_MTU 9220 +#define AIROHA_MAX_RX_SIZE 16128 #define AIROHA_MAX_PACKET_SIZE 2048 #define AIROHA_NUM_QOS_CHANNELS 4 #define AIROHA_NUM_QOS_QUEUES 8 @@ -170,12 +171,19 @@ enum trtcm_param { #define TRTCM_TOKEN_RATE_MASK GENMASK(23, 6) #define TRTCM_TOKEN_RATE_FRACTION_MASK GENMASK(5, 0) +enum airoha_dma_map_type { + AIROHA_DMA_UNMAPPED, + AIROHA_DMA_MAP_SINGLE, + AIROHA_DMA_MAP_PAGE, +}; + struct airoha_queue_entry { union { void *buf; struct { struct list_head list; struct sk_buff *skb; + enum airoha_dma_map_type dma_type; }; }; dma_addr_t dma_addr; @@ -676,6 +684,7 @@ int airoha_get_fe_port(struct airoha_gdm_dev *dev); bool airoha_is_valid_gdm_dev(struct airoha_eth *eth, struct airoha_gdm_dev *dev); +void airoha_ppe_set_xmit_frame_size(struct airoha_gdm_dev *dev); void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport); bool airoha_ppe_is_enabled(struct airoha_eth *eth, int index); void airoha_ppe_check_skb(struct airoha_ppe_dev *dev, struct sk_buff *skb, diff --git a/drivers/net/ethernet/airoha/airoha_ppe.c b/drivers/net/ethernet/airoha/airoha_ppe.c index 42f4b0f21d17..e7c78293002a 100644 --- a/drivers/net/ethernet/airoha/airoha_ppe.c +++ b/drivers/net/ethernet/airoha/airoha_ppe.c @@ -97,6 +97,33 @@ void airoha_ppe_set_cpu_port(struct airoha_gdm_dev *dev, u8 ppe_id, u8 fport) __field_prep(DFT_CPORT_MASK(fport), fe_cpu_port)); } +void airoha_ppe_set_xmit_frame_size(struct airoha_gdm_dev *dev) +{ + struct airoha_gdm_port *port = dev->port; + struct airoha_eth *eth = dev->eth; + int i, ppe_id, index; + u32 len = 0; + + for (i = 0; i < ARRAY_SIZE(port->devs); i++) { + struct airoha_gdm_dev *d = port->devs[i]; + struct net_device *netdev; + + if (!d) + continue; + + netdev = netdev_from_priv(d); + if (netif_running(netdev)) + len = max_t(u32, len, netdev->mtu); + } + len += VLAN_ETH_HLEN; + + ppe_id = !airoha_is_lan_gdm_dev(dev) && airoha_ppe_is_enabled(eth, 1); + index = port->id == AIROHA_GDM4_IDX ? 7 : port->id; + airoha_fe_rmw(eth, REG_PPE_MTU(ppe_id, index), + FP_EGRESS_MTU_MASK(index), + __field_prep(FP_EGRESS_MTU_MASK(index), len)); +} + static void airoha_ppe_hw_init(struct airoha_ppe *ppe) { u32 sram_ppe_num_data_entries = PPE_SRAM_NUM_ENTRIES, sram_num_entries; @@ -115,8 +142,6 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe) PPE_RAM_NUM_ENTRIES_SHIFT(sram_ppe_num_data_entries); for (i = 0; i < eth->soc->num_ppe; i++) { - int p; - airoha_fe_wr(eth, REG_PPE_TB_BASE(i), ppe->foe_dma + sram_tb_size); @@ -166,15 +191,6 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe) airoha_fe_wr(eth, REG_PPE_HASH_SEED(i), PPE_HASH_SEED); airoha_fe_clear(eth, REG_PPE_PPE_FLOW_CFG(i), PPE_FLOW_CFG_IP6_6RD_MASK); - - for (p = 0; p < ARRAY_SIZE(eth->ports); p++) - airoha_fe_rmw(eth, REG_PPE_MTU(i, p), - FP0_EGRESS_MTU_MASK | - FP1_EGRESS_MTU_MASK, - FIELD_PREP(FP0_EGRESS_MTU_MASK, - AIROHA_MAX_MTU) | - FIELD_PREP(FP1_EGRESS_MTU_MASK, - AIROHA_MAX_MTU)); } for (i = 0; i < ARRAY_SIZE(eth->ports); i++) { @@ -196,6 +212,7 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe) airoha_ppe_is_enabled(eth, 1); fport = airoha_get_fe_port(dev); airoha_ppe_set_cpu_port(dev, ppe_id, fport); + airoha_ppe_set_xmit_frame_size(dev); } } } diff --git a/drivers/net/ethernet/airoha/airoha_regs.h b/drivers/net/ethernet/airoha/airoha_regs.h index 436f3c8779c1..6fed63d013b4 100644 --- a/drivers/net/ethernet/airoha/airoha_regs.h +++ b/drivers/net/ethernet/airoha/airoha_regs.h @@ -327,9 +327,8 @@ #define PPE_SRAM_TABLE_EN_MASK BIT(0) #define REG_PPE_MTU_BASE(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x304) -#define REG_PPE_MTU(_m, _n) (REG_PPE_MTU_BASE(_m) + ((_n) << 2)) -#define FP1_EGRESS_MTU_MASK GENMASK(29, 16) -#define FP0_EGRESS_MTU_MASK GENMASK(13, 0) +#define REG_PPE_MTU(_m, _n) (REG_PPE_MTU_BASE(_m) + (((_n) / 2) << 2)) +#define FP_EGRESS_MTU_MASK(_n) GENMASK(13 + (((_n) % 2) << 4), ((_n) % 2) << 4) #define REG_PPE_RAM_CTRL(_n) (((_n) ? PPE2_BASE : PPE1_BASE) + 0x31c) #define PPE_SRAM_CTRL_ACK_MASK BIT(31) @@ -377,6 +376,10 @@ #define REG_SRC_PORT_FC_MAP6 0x2298 #define FC_ID_OF_SRC_PORT_MASK(_n) GENMASK(4 + ((_n) << 3), ((_n) << 3)) +#define REG_WAN_MTU0 0x2300 +#define WAN_MTU1_MASK GENMASK(29, 16) +#define WAN_MTU0_MASK GENMASK(13, 0) + #define REG_CDM5_RX_OQ1_DROP_CNT 0x29d4 /* QDMA */ diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c index 75f22f74774c..06b4424e778e 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c @@ -1163,18 +1163,14 @@ int setup_cn23xx_octeon_pf_device(struct octeon_device *oct) if (octeon_map_pci_barx(oct, 1, MAX_BAR1_IOREMAP_SIZE)) { dev_err(&oct->pci_dev->dev, "%s CN23XX BAR1 map failed\n", __func__); - octeon_unmap_pci_barx(oct, 0); - return 1; + goto err_unmap_bar0; } if (cn23xx_get_pf_num(oct) != 0) - return 1; + goto err_unmap_bar1; - if (cn23xx_sriov_config(oct)) { - octeon_unmap_pci_barx(oct, 0); - octeon_unmap_pci_barx(oct, 1); - return 1; - } + if (cn23xx_sriov_config(oct)) + goto err_unmap_bar1; octeon_write_csr64(oct, CN23XX_SLI_MAC_CREDIT_CNT, 0x3F802080802080ULL); @@ -1205,6 +1201,12 @@ int setup_cn23xx_octeon_pf_device(struct octeon_device *oct) oct->coproc_clock_rate = 1000000ULL * cn23xx_coprocessor_clock(oct); return 0; + +err_unmap_bar1: + octeon_unmap_pci_barx(oct, 1); +err_unmap_bar0: + octeon_unmap_pci_barx(oct, 0); + return 1; } EXPORT_SYMBOL_GPL(setup_cn23xx_octeon_pf_device); diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 171750fad44f..6871127427fa 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -6737,14 +6737,6 @@ void t4_sge_decode_idma_state(struct adapter *adapter, int state) return; } - if (is_t4(adapter->params.chip)) { - sge_idma_decode = (const char **)t4_decode; - sge_idma_decode_nstates = ARRAY_SIZE(t4_decode); - } else { - sge_idma_decode = (const char **)t5_decode; - sge_idma_decode_nstates = ARRAY_SIZE(t5_decode); - } - if (state < sge_idma_decode_nstates) CH_WARN(adapter, "idma state %s\n", sge_idma_decode[state]); else diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c index aa8a87124b10..8e3f345dd9aa 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc.c +++ b/drivers/net/ethernet/freescale/enetc/enetc.c @@ -1783,6 +1783,7 @@ int enetc_xdp_xmit(struct net_device *ndev, int num_frames, { struct enetc_tx_swbd xdp_redirect_arr[ENETC_MAX_SKB_FRAGS] = {0}; struct enetc_ndev_priv *priv = netdev_priv(ndev); + struct skb_shared_info *shinfo; struct enetc_bdr *tx_ring; int xdp_tx_bd_cnt, i, k; int xdp_tx_frm_cnt = 0; @@ -1798,6 +1799,12 @@ int enetc_xdp_xmit(struct net_device *ndev, int num_frames, prefetchw(ENETC_TXBD(*tx_ring, tx_ring->next_to_use)); for (k = 0; k < num_frames; k++) { + if (xdp_frame_has_frags(frames[k])) { + shinfo = xdp_get_shared_info_from_frame(frames[k]); + if (unlikely((shinfo->nr_frags + 1) > ENETC_MAX_SKB_FRAGS)) + break; + } + xdp_tx_bd_cnt = enetc_xdp_frame_to_xdp_tx_swbd(tx_ring, xdp_redirect_arr, frames[k]); diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c index 013273a2de32..299bab043175 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -1995,8 +1995,10 @@ static int fman_init(struct fman *fman) /* Init KeyGen */ fman->keygen = keygen_init(fman->kg_regs); - if (!fman->keygen) + if (!fman->keygen) { + free_init_resources(fman); return -EINVAL; + } err = enable(fman, cfg); if (err != 0) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c index 71b3a059c964..daff53ba7d09 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c @@ -714,34 +714,43 @@ static int mlx5e_macsec_add_rxsc(struct macsec_context *ctx) } sc_xarray_element->rx_sc = rx_sc; - err = xa_alloc(&macsec->sc_xarray, &sc_xarray_element->fs_id, sc_xarray_element, - XA_LIMIT(1, MLX5_MACEC_RX_FS_ID_MAX), GFP_KERNEL); - if (err) { - if (err == -EBUSY) - netdev_err(ctx->netdev, - "MACsec offload: unable to create entry for RX SC (%d Rx SCs already allocated)\n", - MLX5_MACEC_RX_FS_ID_MAX); - goto destroy_sc_xarray_elemenet; - } rx_sc->md_dst = metadata_dst_alloc(0, METADATA_MACSEC, GFP_KERNEL); if (!rx_sc->md_dst) { err = -ENOMEM; - goto erase_xa_alloc; + goto destroy_sc_xarray_elemenet; } rx_sc->sci = ctx_rx_sc->sci; rx_sc->active = ctx_rx_sc->active; - list_add_rcu(&rx_sc->rx_sc_list_element, rx_sc_list); - rx_sc->sc_xarray_element = sc_xarray_element; rx_sc->md_dst->u.macsec_info.sci = rx_sc->sci; + + /* + * Publish the fully-initialised SC last: xa_alloc() makes + * sc_xarray_element->rx_sc (and rx_sc->md_dst) reachable from the RX + * datapath via xa_load(). Doing it only after md_dst is allocated and + * initialised pairs with the rcu_read_lock()/xa_load() in + * mlx5e_macsec_offload_handle_rx_skb(), so a reader can never observe + * a non-NULL md_dst with uninitialised contents. + */ + err = xa_alloc(&macsec->sc_xarray, &sc_xarray_element->fs_id, sc_xarray_element, + XA_LIMIT(1, MLX5_MACEC_RX_FS_ID_MAX), GFP_KERNEL); + if (err) { + if (err == -EBUSY) + netdev_err(ctx->netdev, + "MACsec offload: unable to create entry for RX SC (%d Rx SCs already allocated)\n", + MLX5_MACEC_RX_FS_ID_MAX); + goto destroy_md_dst; + } + + list_add_rcu(&rx_sc->rx_sc_list_element, rx_sc_list); mutex_unlock(&macsec->lock); return 0; -erase_xa_alloc: - xa_erase(&macsec->sc_xarray, sc_xarray_element->fs_id); +destroy_md_dst: + dst_release(&rx_sc->md_dst->dst); destroy_sc_xarray_elemenet: kfree(sc_xarray_element); destroy_rx_sc: @@ -829,7 +838,7 @@ static void macsec_del_rxsc_ctx(struct mlx5e_macsec *macsec, struct mlx5e_macsec */ list_del_rcu(&rx_sc->rx_sc_list_element); xa_erase(&macsec->sc_xarray, rx_sc->sc_xarray_element->fs_id); - metadata_dst_free(rx_sc->md_dst); + dst_release(&rx_sc->md_dst->dst); kfree(rx_sc->sc_xarray_element); kfree_rcu_mightsleep(rx_sc); } @@ -1695,10 +1704,10 @@ void mlx5e_macsec_offload_handle_rx_skb(struct net_device *netdev, rcu_read_lock(); sc_xarray_element = xa_load(&macsec->sc_xarray, fs_id); - rx_sc = sc_xarray_element->rx_sc; - if (rx_sc) { - dst_hold(&rx_sc->md_dst->dst); - skb_dst_set(skb, &rx_sc->md_dst->dst); + rx_sc = sc_xarray_element ? sc_xarray_element->rx_sc : NULL; + if (rx_sc && rx_sc->md_dst) { + if (dst_hold_safe(&rx_sc->md_dst->dst)) + skb_dst_set(skb, &rx_sc->md_dst->dst); } rcu_read_unlock(); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c index eae02bc74221..3bcf412a08c4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/bwc.c @@ -205,6 +205,7 @@ static int hws_bwc_matcher_move(struct mlx5hws_bwc_matcher *bwc_matcher) ret = mlx5hws_matcher_resize_set_target(old_matcher, new_matcher); if (ret) { mlx5hws_err(ctx, "Rehash error: failed setting resize target\n"); + mlx5hws_matcher_destroy(new_matcher); return ret; } diff --git a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c index 9cd85a0d0c3a..401f8b8ae1ca 100644 --- a/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c +++ b/drivers/net/ethernet/meta/fbnic/fbnic_txrx.c @@ -194,16 +194,18 @@ static bool fbnic_tx_tstamp(struct sk_buff *skb) static bool fbnic_tx_lso(struct fbnic_ring *ring, struct sk_buff *skb, - struct skb_shared_info *shinfo, __le64 *meta, - unsigned int *l2len, unsigned int *i3len) + __le64 *meta, unsigned int *l2len, unsigned int *i3len) { unsigned int l3_type, l4_type, l4len, hdrlen; + struct skb_shared_info *shinfo; unsigned char *l4hdr; __be16 payload_len; if (unlikely(skb_cow_head(skb, 0))) return true; + shinfo = skb_shinfo(skb); + if (shinfo->gso_type & SKB_GSO_PARTIAL) { l3_type = FBNIC_TWD_L3_TYPE_OTHER; } else if (!skb->encapsulation) { @@ -258,7 +260,6 @@ fbnic_tx_lso(struct fbnic_ring *ring, struct sk_buff *skb, static bool fbnic_tx_offloads(struct fbnic_ring *ring, struct sk_buff *skb, __le64 *meta) { - struct skb_shared_info *shinfo = skb_shinfo(skb); unsigned int l2len, i3len; if (fbnic_tx_tstamp(skb)) @@ -273,8 +274,8 @@ fbnic_tx_offloads(struct fbnic_ring *ring, struct sk_buff *skb, __le64 *meta) *meta |= cpu_to_le64(FIELD_PREP(FBNIC_TWD_CSUM_OFFSET_MASK, skb->csum_offset / 2)); - if (shinfo->gso_size) { - if (fbnic_tx_lso(ring, skb, shinfo, meta, &l2len, &i3len)) + if (skb_is_gso(skb)) { + if (fbnic_tx_lso(ring, skb, meta, &l2len, &i3len)) return true; } else { *meta |= cpu_to_le64(FBNIC_TWD_FLAG_REQ_CSO); diff --git a/drivers/net/ethernet/microchip/lan743x_main.c b/drivers/net/ethernet/microchip/lan743x_main.c index 1cdce35e1423..e759171bfd76 100644 --- a/drivers/net/ethernet/microchip/lan743x_main.c +++ b/drivers/net/ethernet/microchip/lan743x_main.c @@ -3541,8 +3541,8 @@ static int lan743x_hardware_init(struct lan743x_adapter *adapter, adapter->max_tx_channels = PCI11X1X_MAX_TX_CHANNELS; adapter->used_tx_channels = PCI11X1X_USED_TX_CHANNELS; adapter->max_vector_count = PCI11X1X_MAX_VECTOR_COUNT; - pci11x1x_strap_get_status(adapter); spin_lock_init(&adapter->eth_syslock_spinlock); + pci11x1x_strap_get_status(adapter); mutex_init(&adapter->sgmii_rw_lock); pci11x1x_set_rfe_rd_fifo_threshold(adapter); sgmii_ctl = lan743x_csr_read(adapter, SGMII_CTL); diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index e338bfc8b7b2..33e18bb69774 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -961,7 +961,7 @@ static inline void qede_tpa_cont(struct qede_dev *edev, { int i; - for (i = 0; cqe->len_list[i] && i < ARRAY_SIZE(cqe->len_list); i++) + for (i = 0; i < ARRAY_SIZE(cqe->len_list) && cqe->len_list[i]; i++) qede_fill_frag_skb(edev, rxq, cqe->tpa_agg_index, le16_to_cpu(cqe->len_list[i])); @@ -986,7 +986,7 @@ static int qede_tpa_end(struct qede_dev *edev, dma_unmap_page(rxq->dev, tpa_info->buffer.mapping, PAGE_SIZE, rxq->data_direction); - for (i = 0; cqe->len_list[i] && i < ARRAY_SIZE(cqe->len_list); i++) + for (i = 0; i < ARRAY_SIZE(cqe->len_list) && cqe->len_list[i]; i++) qede_fill_frag_skb(edev, rxq, cqe->tpa_agg_index, le16_to_cpu(cqe->len_list[i])); if (unlikely(i > 1)) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index d042567b8128..814d88d2aee4 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -1802,6 +1802,7 @@ static bool wx_set_vmdq_queues(struct wx *wx) rss_i = 4; } } else { + vmdq_m = WX_VMDQ_1Q_MASK; /* double check we are limited to maximum pools */ vmdq_i = min_t(u16, 8, vmdq_i); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index c7befe4cdfe9..65e3e55db1cf 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -486,6 +486,7 @@ enum WX_MSCA_CMD_value { #define WX_VMDQ_4Q_MASK 0x7C #define WX_VMDQ_2Q_MASK 0x7E +#define WX_VMDQ_1Q_MASK 0x7F /****************** Manageablility Host Interface defines ********************/ #define WX_HI_MAX_BLOCK_BYTE_LENGTH 256 /* Num of bytes in range */ diff --git a/drivers/net/ipa/ipa_smp2p.c b/drivers/net/ipa/ipa_smp2p.c index 2f0ccdd937cc..331c00ad02c0 100644 --- a/drivers/net/ipa/ipa_smp2p.c +++ b/drivers/net/ipa/ipa_smp2p.c @@ -232,19 +232,27 @@ ipa_smp2p_init(struct ipa *ipa, struct platform_device *pdev, bool modem_init) &valid_bit); if (IS_ERR(valid_state)) return PTR_ERR(valid_state); - if (valid_bit >= 32) /* BITS_PER_U32 */ - return -EINVAL; + if (valid_bit >= 32) { /* BITS_PER_U32 */ + ret = -EINVAL; + goto err_valid_state_put; + } enabled_state = qcom_smem_state_get(dev, "ipa-clock-enabled", &enabled_bit); - if (IS_ERR(enabled_state)) - return PTR_ERR(enabled_state); - if (enabled_bit >= 32) /* BITS_PER_U32 */ - return -EINVAL; + if (IS_ERR(enabled_state)) { + ret = PTR_ERR(enabled_state); + goto err_valid_state_put; + } + if (enabled_bit >= 32) { /* BITS_PER_U32 */ + ret = -EINVAL; + goto err_enabled_state_put; + } smp2p = kzalloc_obj(*smp2p); - if (!smp2p) - return -ENOMEM; + if (!smp2p) { + ret = -ENOMEM; + goto err_enabled_state_put; + } smp2p->ipa = ipa; @@ -289,6 +297,10 @@ err_null_smp2p: ipa->smp2p = NULL; mutex_destroy(&smp2p->mutex); kfree(smp2p); +err_enabled_state_put: + qcom_smem_state_put(enabled_state); +err_valid_state_put: + qcom_smem_state_put(valid_state); return ret; } @@ -305,6 +317,8 @@ void ipa_smp2p_exit(struct ipa *ipa) ipa_smp2p_power_release(ipa); ipa->smp2p = NULL; mutex_destroy(&smp2p->mutex); + qcom_smem_state_put(smp2p->enabled_state); + qcom_smem_state_put(smp2p->valid_state); kfree(smp2p); } diff --git a/drivers/net/mdio/mdio-i2c.c b/drivers/net/mdio/mdio-i2c.c index b88f63234b4e..ed20352a589a 100644 --- a/drivers/net/mdio/mdio-i2c.c +++ b/drivers/net/mdio/mdio-i2c.c @@ -419,50 +419,6 @@ static int i2c_mii_write_rollball(struct mii_bus *bus, int phy_id, int devad, return 0; } -static int i2c_mii_probe_rollball(struct i2c_adapter *i2c) -{ - u8 data_buf[] = { ROLLBALL_DATA_ADDR, 0x01, 0x00, 0x00 }; - u8 cmd_buf[] = { ROLLBALL_CMD_ADDR, ROLLBALL_CMD_READ }; - u8 cmd_addr = ROLLBALL_CMD_ADDR; - struct i2c_msg msgs[2]; - u8 result; - int ret; - int i; - - msgs[0].addr = ROLLBALL_PHY_I2C_ADDR; - msgs[0].flags = 0; - msgs[0].len = sizeof(data_buf); - msgs[0].buf = data_buf; - msgs[1].addr = ROLLBALL_PHY_I2C_ADDR; - msgs[1].flags = 0; - msgs[1].len = sizeof(cmd_buf); - msgs[1].buf = cmd_buf; - - ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs)); - if (ret < 0) - return -ENODEV; - - msgs[0].addr = ROLLBALL_PHY_I2C_ADDR; - msgs[0].flags = 0; - msgs[0].len = 1; - msgs[0].buf = &cmd_addr; - msgs[1].addr = ROLLBALL_PHY_I2C_ADDR; - msgs[1].flags = I2C_M_RD; - msgs[1].len = 1; - msgs[1].buf = &result; - - for (i = 0; i < 10; i++) { - msleep(20); - ret = i2c_transfer_rollball(i2c, msgs, ARRAY_SIZE(msgs)); - if (ret < 0) - return -ENODEV; - if (result == ROLLBALL_CMD_DONE) - return 0; - } - - return -ENODEV; -} - static int i2c_mii_init_rollball(struct i2c_adapter *i2c) { struct i2c_msg msg; @@ -482,11 +438,11 @@ static int i2c_mii_init_rollball(struct i2c_adapter *i2c) ret = i2c_transfer(i2c, &msg, 1); if (ret < 0) - return -ENODEV; - if (ret != 1) + return ret; + else if (ret != 1) return -EIO; - - return i2c_mii_probe_rollball(i2c); + else + return 0; } static bool mdio_i2c_check_functionality(struct i2c_adapter *i2c, @@ -531,10 +487,9 @@ struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c, case MDIO_I2C_ROLLBALL: ret = i2c_mii_init_rollball(i2c); if (ret < 0) { - if (ret != -ENODEV) - dev_err(parent, - "Cannot initialize RollBall MDIO I2C protocol: %d\n", - ret); + dev_err(parent, + "Cannot initialize RollBall MDIO I2C protocol: %d\n", + ret); mdiobus_free(mii); return ERR_PTR(ret); } diff --git a/drivers/net/netdevsim/ethtool.c b/drivers/net/netdevsim/ethtool.c index 9350ba48eb81..025ea79879f3 100644 --- a/drivers/net/netdevsim/ethtool.c +++ b/drivers/net/netdevsim/ethtool.c @@ -252,6 +252,7 @@ void nsim_ethtool_init(struct netdevsim *ns) ns->ethtool.channels = ns->nsim_bus_dev->num_queues; ethtool = debugfs_create_dir("ethtool", ns->nsim_dev_port->ddir); + ns->ethtool_ddir = ethtool; debugfs_create_u32("get_err", 0600, ethtool, &ns->ethtool.get_err); debugfs_create_u32("set_err", 0600, ethtool, &ns->ethtool.set_err); @@ -272,3 +273,8 @@ void nsim_ethtool_init(struct netdevsim *ns) debugfs_create_u32("tx_max_pending", 0600, dir, &ns->ethtool.ring.tx_max_pending); } + +void nsim_ethtool_fini(struct netdevsim *ns) +{ + debugfs_remove(ns->ethtool_ddir); +} diff --git a/drivers/net/netdevsim/netdev.c b/drivers/net/netdevsim/netdev.c index 27e5f109f933..4e9d7e10b527 100644 --- a/drivers/net/netdevsim/netdev.c +++ b/drivers/net/netdevsim/netdev.c @@ -1165,6 +1165,7 @@ struct netdevsim *nsim_create(struct nsim_dev *nsim_dev, return ns; err_free_netdev: + nsim_ethtool_fini(ns); free_netdev(dev); return ERR_PTR(err); } @@ -1178,6 +1179,7 @@ void nsim_destroy(struct netdevsim *ns) debugfs_remove(ns->vlan_dfs); debugfs_remove(ns->qr_dfs); debugfs_remove(ns->pp_dfs); + nsim_ethtool_fini(ns); if (ns->nb.notifier_call) unregister_netdevice_notifier_dev_net(ns->netdev, &ns->nb, diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index 4c9cc96dcec3..64f77f93d937 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -154,6 +154,7 @@ struct netdevsim { struct dentry *pp_dfs; struct dentry *qr_dfs; struct dentry *vlan_dfs; + struct dentry *ethtool_ddir; struct nsim_ethtool ethtool; struct netdevsim __rcu *peer; @@ -169,6 +170,7 @@ void nsim_destroy(struct netdevsim *ns); bool netdev_is_nsim(struct net_device *dev); void nsim_ethtool_init(struct netdevsim *ns); +void nsim_ethtool_fini(struct netdevsim *ns); void nsim_udp_tunnels_debugfs_create(struct nsim_dev *nsim_dev); int nsim_udp_tunnels_info_create(struct nsim_dev *nsim_dev, diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 03bfd8640db9..f520206734da 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -597,7 +597,6 @@ static const struct sfp_quirk sfp_quirks[] = { // OEM SFP-GE-T is a 1000Base-T module with broken TX_FAULT indicator SFP_QUIRK_F("OEM", "SFP-GE-T", sfp_fixup_ignore_tx_fault), - SFP_QUIRK_F("OEM", "SFP-10G-T-I", sfp_fixup_rollball), SFP_QUIRK_F("OEM", "SFP-10G-T", sfp_fixup_rollball_cc), SFP_QUIRK_S("OEM", "SFP-2.5G-T", sfp_quirk_oem_2_5g), SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-D", sfp_quirk_2500basex), @@ -963,6 +962,7 @@ static int sfp_i2c_mdiobus_create(struct sfp *sfp) static void sfp_i2c_mdiobus_destroy(struct sfp *sfp) { mdiobus_unregister(sfp->i2c_mii); + mdiobus_free(sfp->i2c_mii); sfp->i2c_mii = NULL; } @@ -2173,17 +2173,10 @@ static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn) static int sfp_sm_add_mdio_bus(struct sfp *sfp) { - int ret; - - if (sfp->mdio_protocol == MDIO_I2C_NONE) - return 0; + if (sfp->mdio_protocol != MDIO_I2C_NONE) + return sfp_i2c_mdiobus_create(sfp); - ret = sfp_i2c_mdiobus_create(sfp); - if (ret == -ENODEV) { - sfp->mdio_protocol = MDIO_I2C_NONE; - return 0; - } - return ret; + return 0; } /* Probe a SFP for a PHY device if the module supports copper - the PHY diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c index 69dbdbde9d71..a5e6d7b26b9f 100644 --- a/drivers/net/pse-pd/pse_core.c +++ b/drivers/net/pse-pd/pse_core.c @@ -1367,7 +1367,7 @@ static void __pse_control_release(struct kref *kref) if (psec->pcdev->pi[psec->id].admin_state_enabled) regulator_disable(psec->ps); - devm_regulator_put(psec->ps); + regulator_put(psec->ps); module_put(psec->pcdev->owner); @@ -1436,8 +1436,8 @@ pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index, goto free_psec; pcdev->pi[index].admin_state_enabled = ret; - psec->ps = devm_regulator_get_exclusive(pcdev->dev, - rdev_get_name(pcdev->pi[index].rdev)); + psec->ps = regulator_get_exclusive(pcdev->dev, + rdev_get_name(pcdev->pi[index].rdev)); if (IS_ERR(psec->ps)) { ret = PTR_ERR(psec->ps); goto put_module; diff --git a/drivers/net/usb/gl620a.c b/drivers/net/usb/gl620a.c index 0bfa37c14059..09afd137b64e 100644 --- a/drivers/net/usb/gl620a.c +++ b/drivers/net/usb/gl620a.c @@ -104,6 +104,9 @@ static int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb) return 0; } + if (!skb_pull(skb, size + 4)) + return 0; + // allocate the skb for the individual packet gl_skb = alloc_skb(size, GFP_ATOMIC); if (gl_skb) { @@ -116,9 +119,6 @@ static int genelink_rx_fixup(struct usbnet *dev, struct sk_buff *skb) // advance to the next packet packet = (struct gl_packet *)&packet->packet_data[size]; count--; - - // shift the data pointer to the next gl_packet - skb_pull(skb, size + 4); } // skip the packet length field 4 bytes diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 26afa6341d16..3e2a5876c6c8 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -3011,6 +3011,9 @@ static int virtnet_poll(struct napi_struct *napi, int budget) unsigned int xdp_xmit = 0; bool napi_complete; + if (budget) + virtqueue_disable_cb(rq->vq); + virtnet_poll_cleantx(rq, budget); received = virtnet_receive(rq, budget, &xdp_xmit); diff --git a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c index bff46f7ca59f..0bbd41263cc2 100644 --- a/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c +++ b/drivers/net/wwan/iosm/iosm_ipc_mux_codec.c @@ -553,19 +553,21 @@ static int mux_dl_process_dg(struct iosm_mux *ipc_mux, struct mux_adbh *adbh, u32 packet_offset, i, rc, dg_len; for (i = 0; i < nr_of_dg; i++, dg++) { - if (le32_to_cpu(dg->datagram_index) - < sizeof(struct mux_adbh)) + u32 dg_index = le32_to_cpu(dg->datagram_index); + + dg_len = le16_to_cpu(dg->datagram_length); + + if (dg_index < sizeof(struct mux_adbh)) goto dg_error; - /* Is the packet inside of the ADB */ - if (le32_to_cpu(dg->datagram_index) >= - le32_to_cpu(adbh->block_length)) { + /* Is the packet inside of the ADB and the received skb ? */ + if (dg_index >= le32_to_cpu(adbh->block_length) || + dg_index >= skb->len || + dg_len > skb->len - dg_index || + dl_head_pad_len >= dg_len) { goto dg_error; } else { - packet_offset = - le32_to_cpu(dg->datagram_index) + - dl_head_pad_len; - dg_len = le16_to_cpu(dg->datagram_length); + packet_offset = dg_index + dl_head_pad_len; /* Pass the packet to the netif layer. */ rc = ipc_mux_net_receive(ipc_mux, if_id, ipc_mux->wwan, packet_offset, @@ -589,12 +591,16 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux, struct mux_adbh *adbh; struct mux_adth *adth; int nr_of_dg, if_id; - u32 adth_index; + u32 adth_index, prev_index = 0; u8 *block; block = skb->data; adbh = (struct mux_adbh *)block; + /* The block header itself must fit in the received skb. */ + if (skb->len < sizeof(struct mux_adbh)) + goto adb_decode_err; + /* Process the aggregated datagram tables. */ adth_index = le32_to_cpu(adbh->first_table_index); @@ -606,6 +612,16 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux, /* Loop through mixed session tables. */ while (adth_index) { + /* The table header must lie within the received skb, and the + * chain must move forward so a modem cannot make the loop + * cycle between two tables. + */ + if (adth_index <= prev_index || + adth_index < sizeof(struct mux_adbh) || + adth_index > skb->len - sizeof(struct mux_adth)) + goto adb_decode_err; + prev_index = adth_index; + /* Get the reference to the table header. */ adth = (struct mux_adth *)(block + adth_index); @@ -629,6 +645,10 @@ static void mux_dl_adb_decode(struct iosm_mux *ipc_mux, if (le16_to_cpu(adth->table_length) < sizeof(struct mux_adth)) goto adb_decode_err; + /* The whole datagram table must fit in the received skb. */ + if (le16_to_cpu(adth->table_length) > skb->len - adth_index) + goto adb_decode_err; + /* Calculate the number of datagrams. */ nr_of_dg = (le16_to_cpu(adth->table_length) - sizeof(struct mux_adth)) / diff --git a/drivers/ntb/hw/amd/ntb_hw_amd.c b/drivers/ntb/hw/amd/ntb_hw_amd.c index 1a163596ddf5..36bee0d8ac6c 100644 --- a/drivers/ntb/hw/amd/ntb_hw_amd.c +++ b/drivers/ntb/hw/amd/ntb_hw_amd.c @@ -376,7 +376,7 @@ static int amd_ntb_link_disable(struct ntb_dev *ntb) if (ndev->ntb.topo == NTB_TOPO_SEC) return -EINVAL; - dev_dbg(&ntb->pdev->dev, "Enabling Link.\n"); + dev_dbg(&ntb->pdev->dev, "Disabling Link.\n"); return 0; } @@ -1328,15 +1328,15 @@ static const struct ntb_dev_data dev_data[] = { }; static const struct pci_device_id amd_ntb_pci_tbl[] = { - { PCI_VDEVICE(AMD, 0x145b), (kernel_ulong_t)&dev_data[0] }, - { PCI_VDEVICE(AMD, 0x148b), (kernel_ulong_t)&dev_data[1] }, - { PCI_VDEVICE(AMD, 0x14c0), (kernel_ulong_t)&dev_data[1] }, - { PCI_VDEVICE(AMD, 0x14c3), (kernel_ulong_t)&dev_data[1] }, - { PCI_VDEVICE(AMD, 0x155a), (kernel_ulong_t)&dev_data[1] }, - { PCI_VDEVICE(AMD, 0x17d4), (kernel_ulong_t)&dev_data[1] }, - { PCI_VDEVICE(AMD, 0x17d7), (kernel_ulong_t)&dev_data[2] }, - { PCI_VDEVICE(HYGON, 0x145b), (kernel_ulong_t)&dev_data[0] }, - { 0, } + { PCI_VDEVICE(AMD, 0x145b), .driver_data = (kernel_ulong_t)&dev_data[0] }, + { PCI_VDEVICE(AMD, 0x148b), .driver_data = (kernel_ulong_t)&dev_data[1] }, + { PCI_VDEVICE(AMD, 0x14c0), .driver_data = (kernel_ulong_t)&dev_data[1] }, + { PCI_VDEVICE(AMD, 0x14c3), .driver_data = (kernel_ulong_t)&dev_data[1] }, + { PCI_VDEVICE(AMD, 0x155a), .driver_data = (kernel_ulong_t)&dev_data[1] }, + { PCI_VDEVICE(AMD, 0x17d4), .driver_data = (kernel_ulong_t)&dev_data[1] }, + { PCI_VDEVICE(AMD, 0x17d7), .driver_data = (kernel_ulong_t)&dev_data[2] }, + { PCI_VDEVICE(HYGON, 0x145b), .driver_data = (kernel_ulong_t)&dev_data[0] }, + { } }; MODULE_DEVICE_TABLE(pci, amd_ntb_pci_tbl); diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index af5755472842..c47607d4f8a7 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -729,7 +729,8 @@ static void ntb_epf_deinit_pci(struct ntb_epf_dev *ndev) struct pci_dev *pdev = ndev->ntb.pdev; pci_iounmap(pdev, ndev->ctrl_reg); - pci_iounmap(pdev, ndev->peer_spad_reg); + if (ndev->barno_map[BAR_PEER_SPAD] != ndev->barno_map[BAR_CONFIG]) + pci_iounmap(pdev, ndev->peer_spad_reg); pci_iounmap(pdev, ndev->db_reg); pci_release_regions(pdev); diff --git a/drivers/of/base.c b/drivers/of/base.c index 7cb0d7e88247..6e7a42dedad3 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2160,7 +2160,7 @@ static bool of_check_bad_map(const __be32 *map, int len) * @map_mask_name: optional property name of the mask to use. * @filter_np: pointer to an optional filter node, or NULL to allow bypass. * If non-NULL, the map property must exist (-ENODEV if absent). If - * *filter_np is also non-NULL, only entries targeting that node match. + * ``*filter_np`` is also non-NULL, only entries targeting that node match. * @arg: pointer to a &struct of_phandle_args for the result. On success, * @arg->args_count will be set to the number of output specifier cells * as defined by @cells_name in the target node, and @@ -2350,7 +2350,7 @@ EXPORT_SYMBOL_GPL(of_map_iommu_id); * stream/device ID) used as the lookup key in the msi-map table. * @filter_np: pointer to an optional filter node, or NULL to allow bypass. * If non-NULL, the map property must exist (-ENODEV if absent). If - * *filter_np is also non-NULL, only entries targeting that node match. + * ``*filter_np`` is also non-NULL, only entries targeting that node match. * @arg: pointer to a &struct of_phandle_args for the result. On success, * @arg->args_count will be set to the number of output specifier cells * and @arg->args[0..args_count-1] will contain the translated output diff --git a/drivers/of/property.c b/drivers/of/property.c index b276d1de3222..72cf12907de0 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1172,6 +1172,14 @@ of_fwnode_get_reference_args(const struct fwnode_handle *fwnode, unsigned int i; int ret; + /* + * This function should return -ENOENT for out of bound indexes, + * but the OF API uses signed indexes and consider negative indexes + * as invalid. Catch them here to correctly implement the fwnode API. + */ + if ((int)index < 0) + return -ENOENT; + if (nargs_prop) ret = of_parse_phandle_with_args(to_of_node(fwnode), prop, nargs_prop, index, &of_args); diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index b2eb9909f6a4..d50ea62fa2f3 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -645,9 +645,12 @@ static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg) rtc_device = rtc_class_open("rtc0"); if (!rtc_device) return 0; - rc = rtc_read_alarm(rtc_device, &alarm); - if (rc) - return rc; + rc = rtc_read_next_alarm(rtc_device, &alarm); + if (rc) { + if (rc == -ENOENT) + dev_dbg(pdev->dev, "no alarm pending\n"); + return rc == -ENOENT ? 0 : rc; + } if (!alarm.enabled) { dev_dbg(pdev->dev, "alarm not enabled\n"); return 0; diff --git a/drivers/pwm/pwm-rzg2l-gpt.c b/drivers/pwm/pwm-rzg2l-gpt.c index 4856af080e8e..dfa1d11a48a8 100644 --- a/drivers/pwm/pwm-rzg2l-gpt.c +++ b/drivers/pwm/pwm-rzg2l-gpt.c @@ -81,7 +81,7 @@ struct rzg2l_gpt_chip { void __iomem *mmio; struct mutex lock; /* lock to protect shared channel resources */ unsigned long rate_khz; - u32 period_ticks[RZG2L_MAX_HW_CHANNELS]; + u64 period_ticks[RZG2L_MAX_HW_CHANNELS]; u32 channel_request_count[RZG2L_MAX_HW_CHANNELS]; u32 channel_enable_count[RZG2L_MAX_HW_CHANNELS]; }; @@ -408,14 +408,14 @@ static int rzg2l_gpt_probe(struct platform_device *pdev) rate = clk_get_rate(clk); if (!rate) - return dev_err_probe(dev, -EINVAL, "The gpt clk rate is 0"); + return dev_err_probe(dev, -EINVAL, "The gpt clk rate is 0\n"); /* * Refuse clk rates > 1 GHz to prevent overflow later for computing * period and duty cycle. */ if (rate > NSEC_PER_SEC) - return dev_err_probe(dev, -EINVAL, "The gpt clk rate is > 1GHz"); + return dev_err_probe(dev, -EINVAL, "The gpt clk rate is > 1GHz\n"); /* * Rate is in MHz and is always integer for peripheral clk @@ -424,7 +424,7 @@ static int rzg2l_gpt_probe(struct platform_device *pdev) */ rzg2l_gpt->rate_khz = rate / KILO; if (rzg2l_gpt->rate_khz * KILO != rate) - return dev_err_probe(dev, -EINVAL, "Rate is not multiple of 1000"); + return dev_err_probe(dev, -EINVAL, "Rate is not multiple of 1000\n"); mutex_init(&rzg2l_gpt->lock); diff --git a/drivers/regulator/da9121-regulator.c b/drivers/regulator/da9121-regulator.c index 2b150bb4d471..36a52f707602 100644 --- a/drivers/regulator/da9121-regulator.c +++ b/drivers/regulator/da9121-regulator.c @@ -1195,15 +1195,15 @@ static void da9121_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id da9121_i2c_id[] = { - {"da9121", DA9121_TYPE_DA9121_DA9130}, - {"da9130", DA9121_TYPE_DA9121_DA9130}, - {"da9217", DA9121_TYPE_DA9217}, - {"da9122", DA9121_TYPE_DA9122_DA9131}, - {"da9131", DA9121_TYPE_DA9122_DA9131}, - {"da9220", DA9121_TYPE_DA9220_DA9132}, - {"da9132", DA9121_TYPE_DA9220_DA9132}, - {"da9141", DA9121_TYPE_DA9141}, - {"da9142", DA9121_TYPE_DA9142}, + {"da9121", DA9121_SUBTYPE_DA9121}, + {"da9130", DA9121_SUBTYPE_DA9130}, + {"da9217", DA9121_SUBTYPE_DA9217}, + {"da9122", DA9121_SUBTYPE_DA9122}, + {"da9131", DA9121_SUBTYPE_DA9131}, + {"da9220", DA9121_SUBTYPE_DA9220}, + {"da9132", DA9121_SUBTYPE_DA9132}, + {"da9141", DA9121_SUBTYPE_DA9141}, + {"da9142", DA9121_SUBTYPE_DA9142}, {}, }; MODULE_DEVICE_TABLE(i2c, da9121_i2c_id); diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c index 45d7dc44c2cd..c41db70fa052 100644 --- a/drivers/regulator/pca9450-regulator.c +++ b/drivers/regulator/pca9450-regulator.c @@ -44,6 +44,7 @@ struct pca9450 { unsigned int rcnt; int irq; bool sd_vsel_fixed_low; + int default_t_off_deb; }; static const struct regmap_range pca9450_status_range = { @@ -1209,7 +1210,7 @@ static int pca9450_of_init(struct pca9450 *pca9450) ret = of_property_read_u32(i2c->dev.of_node, "nxp,pmic-on-req-off-debounce-us", &val); if (ret == -EINVAL) - t_off_deb = T_OFF_DEB_120US; + t_off_deb = pca9450->default_t_off_deb; else if (ret) return ret; else { @@ -1304,21 +1305,25 @@ static int pca9450_i2c_probe(struct i2c_client *i2c) case PCA9450_TYPE_PCA9450A: regulator_desc = pca9450a_regulators; pca9450->rcnt = ARRAY_SIZE(pca9450a_regulators); + pca9450->default_t_off_deb = T_OFF_DEB_120US; type_name = "pca9450a"; break; case PCA9450_TYPE_PCA9450BC: regulator_desc = pca9450bc_regulators; pca9450->rcnt = ARRAY_SIZE(pca9450bc_regulators); + pca9450->default_t_off_deb = T_OFF_DEB_120US; type_name = "pca9450bc"; break; case PCA9450_TYPE_PCA9451A: regulator_desc = pca9451a_regulators; pca9450->rcnt = ARRAY_SIZE(pca9451a_regulators); + pca9450->default_t_off_deb = T_OFF_DEB_2MS; type_name = "pca9451a"; break; case PCA9450_TYPE_PCA9452: regulator_desc = pca9451a_regulators; pca9450->rcnt = ARRAY_SIZE(pca9451a_regulators); + pca9450->default_t_off_deb = T_OFF_DEB_2MS; type_name = "pca9452"; break; default: diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 364afc73f8ab..01def8231873 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1763,13 +1763,6 @@ config RTC_DRV_STMP This driver can also be built as a module. If so, the module will be called rtc-stmp3xxx. -config RTC_DRV_PCAP - tristate "PCAP RTC" - depends on EZX_PCAP - help - If you say Y here you will get support for the RTC found on - the PCAP2 ASIC used on some Motorola phones. - config RTC_DRV_MC13XXX depends on MFD_MC13XXX tristate "Freescale MC13xxx RTC" diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 6cf7e066314e..0347645b021f 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -128,7 +128,6 @@ obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o obj-$(CONFIG_RTC_DRV_OPTEE) += rtc-optee.o obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o -obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf2127.o obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 1906f4884a83..96626f8068f9 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -384,6 +384,46 @@ done: return err; } +/** + * rtc_read_next_alarm - read the next expiring alarm + * @rtc: RTC device + * @alarm: storage for the alarm information + * + * Read the next expiring alarm from the RTC timerqueue. This returns + * the alarm that will actually fire next, which may be different from + * rtc_read_alarm() if multiple timers are queued (e.g., alarmtimer + * and wakealarm sysfs both active). + * + * Returns: 0 on success, -ENOENT if no alarm is pending, or other error. + */ +int rtc_read_next_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + struct timerqueue_node *next; + int err; + + if (!rtc || !alarm) + return -EINVAL; + + err = mutex_lock_interruptible(&rtc->ops_lock); + if (err) + return err; + + next = timerqueue_getnext(&rtc->timerqueue); + if (!next) { + err = -ENOENT; + goto unlock; + } + + memset(alarm, 0, sizeof(struct rtc_wkalrm)); + alarm->time = rtc_ktime_to_tm(next->expires); + alarm->enabled = 1; + +unlock: + mutex_unlock(&rtc->ops_lock); + return err; +} +EXPORT_SYMBOL_GPL(rtc_read_next_alarm); + int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) { int err; @@ -635,8 +675,8 @@ EXPORT_SYMBOL_GPL(rtc_update_irq_enable); /** * rtc_handle_legacy_irq - AIE, UIE and PIE event hook * @rtc: pointer to the rtc device - * @num: number of occurence of the event - * @mode: type of the event, RTC_AF, RTC_UF of RTC_PF + * @num: number of occurrence of the event + * @mode: type of the event, RTC_AF, RTC_UF or RTC_PF * * This function is called when an AIE, UIE or PIE mode interrupt * has occurred (or been emulated). diff --git a/drivers/rtc/rtc-88pm886.c b/drivers/rtc/rtc-88pm886.c index 57e9b0a66eed..13aa3ae82239 100644 --- a/drivers/rtc/rtc-88pm886.c +++ b/drivers/rtc/rtc-88pm886.c @@ -78,7 +78,7 @@ static int pm886_rtc_probe(struct platform_device *pdev) } static const struct platform_device_id pm886_rtc_id_table[] = { - { "88pm886-rtc", }, + { .name = "88pm886-rtc" }, { } }; MODULE_DEVICE_TABLE(platform, pm886_rtc_id_table); diff --git a/drivers/rtc/rtc-ab-b5ze-s3.c b/drivers/rtc/rtc-ab-b5ze-s3.c index 684f9898d768..6439ca427c32 100644 --- a/drivers/rtc/rtc-ab-b5ze-s3.c +++ b/drivers/rtc/rtc-ab-b5ze-s3.c @@ -933,7 +933,7 @@ MODULE_DEVICE_TABLE(of, abb5zes3_dt_match); #endif static const struct i2c_device_id abb5zes3_id[] = { - { "abb5zes3" }, + { .name = "abb5zes3" }, { } }; MODULE_DEVICE_TABLE(i2c, abb5zes3_id); diff --git a/drivers/rtc/rtc-ab-eoz9.c b/drivers/rtc/rtc-ab-eoz9.c index de002f7a39bf..b75f4f665076 100644 --- a/drivers/rtc/rtc-ab-eoz9.c +++ b/drivers/rtc/rtc-ab-eoz9.c @@ -546,7 +546,7 @@ MODULE_DEVICE_TABLE(of, abeoz9_dt_match); #endif static const struct i2c_device_id abeoz9_id[] = { - { "abeoz9" }, + { .name = "abeoz9" }, { } }; diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c index ed2b6b8bb3bf..0978bd0a3393 100644 --- a/drivers/rtc/rtc-ab8500.c +++ b/drivers/rtc/rtc-ab8500.c @@ -284,11 +284,10 @@ static ssize_t ab8500_sysfs_show_rtc_calibration(struct device *dev, retval = ab8500_rtc_get_calibration(dev, &calibration); if (retval < 0) { dev_err(dev, "Failed to read RTC calibration attribute\n"); - sprintf(buf, "0\n"); return retval; } - return sprintf(buf, "%d\n", calibration); + return sysfs_emit(buf, "%d\n", calibration); } static DEVICE_ATTR(rtc_calibration, S_IRUGO | S_IWUSR, @@ -324,14 +323,13 @@ static const struct rtc_class_ops ab8500_rtc_ops = { }; static const struct platform_device_id ab85xx_rtc_ids[] = { - { "ab8500-rtc", (kernel_ulong_t)&ab8500_rtc_ops, }, + { .name = "ab8500-rtc" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, ab85xx_rtc_ids); static int ab8500_rtc_probe(struct platform_device *pdev) { - const struct platform_device_id *platid = platform_get_device_id(pdev); int err; struct rtc_device *rtc; u8 rtc_ctrl; @@ -367,7 +365,7 @@ static int ab8500_rtc_probe(struct platform_device *pdev) if (IS_ERR(rtc)) return PTR_ERR(rtc); - rtc->ops = (struct rtc_class_ops *)platid->driver_data; + rtc->ops = &ab8500_rtc_ops; err = devm_request_threaded_irq(&pdev->dev, irq, NULL, rtc_alarm_handler, IRQF_ONESHOT, diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c index 00d7de64ed3e..5486d9d0b1e5 100644 --- a/drivers/rtc/rtc-abx80x.c +++ b/drivers/rtc/rtc-abx80x.c @@ -545,7 +545,8 @@ static int abx80x_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) status &= ~ABX8XX_STATUS_BLF; - tmp = i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, 0); + tmp = i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, + status); if (tmp < 0) return tmp; @@ -752,16 +753,16 @@ static int abx80x_setup_nvmem(struct abx80x_priv *priv) } static const struct i2c_device_id abx80x_id[] = { - { "abx80x", ABX80X }, - { "ab0801", AB0801 }, - { "ab0803", AB0803 }, - { "ab0804", AB0804 }, - { "ab0805", AB0805 }, - { "ab1801", AB1801 }, - { "ab1803", AB1803 }, - { "ab1804", AB1804 }, - { "ab1805", AB1805 }, - { "rv1805", RV1805 }, + { .name = "abx80x", .driver_data = ABX80X }, + { .name = "ab0801", .driver_data = AB0801 }, + { .name = "ab0803", .driver_data = AB0803 }, + { .name = "ab0804", .driver_data = AB0804 }, + { .name = "ab0805", .driver_data = AB0805 }, + { .name = "ab1801", .driver_data = AB1801 }, + { .name = "ab1803", .driver_data = AB1803 }, + { .name = "ab1804", .driver_data = AB1804 }, + { .name = "ab1805", .driver_data = AB1805 }, + { .name = "rv1805", .driver_data = RV1805 }, { } }; MODULE_DEVICE_TABLE(i2c, abx80x_id); diff --git a/drivers/rtc/rtc-aspeed.c b/drivers/rtc/rtc-aspeed.c index 0d0053b52f9b..8f5b440f8c0a 100644 --- a/drivers/rtc/rtc-aspeed.c +++ b/drivers/rtc/rtc-aspeed.c @@ -111,6 +111,7 @@ static const struct of_device_id aspeed_rtc_match[] = { { .compatible = "aspeed,ast2400-rtc", }, { .compatible = "aspeed,ast2500-rtc", }, { .compatible = "aspeed,ast2600-rtc", }, + { .compatible = "aspeed,ast2700-rtc", }, {} }; MODULE_DEVICE_TABLE(of, aspeed_rtc_match); diff --git a/drivers/rtc/rtc-bd70528.c b/drivers/rtc/rtc-bd70528.c index 4c8599761b2e..482810b61495 100644 --- a/drivers/rtc/rtc-bd70528.c +++ b/drivers/rtc/rtc-bd70528.c @@ -341,10 +341,10 @@ static int bd70528_probe(struct platform_device *pdev) } static const struct platform_device_id bd718x7_rtc_id[] = { - { "bd71828-rtc", ROHM_CHIP_TYPE_BD71828 }, - { "bd71815-rtc", ROHM_CHIP_TYPE_BD71815 }, - { "bd72720-rtc", ROHM_CHIP_TYPE_BD72720 }, - { }, + { .name = "bd71828-rtc", .driver_data = ROHM_CHIP_TYPE_BD71828 }, + { .name = "bd71815-rtc", .driver_data = ROHM_CHIP_TYPE_BD71815 }, + { .name = "bd72720-rtc", .driver_data = ROHM_CHIP_TYPE_BD72720 }, + { } }; MODULE_DEVICE_TABLE(platform, bd718x7_rtc_id); diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c index 7ad34539be4d..573231613e52 100644 --- a/drivers/rtc/rtc-bq32k.c +++ b/drivers/rtc/rtc-bq32k.c @@ -16,6 +16,7 @@ #include <linux/kstrtox.h> #include <linux/errno.h> #include <linux/bcd.h> +#include <linux/delay.h> #define BQ32K_SECONDS 0x00 /* Seconds register address */ #define BQ32K_SECONDS_MASK 0x7F /* Mask over seconds value */ @@ -89,9 +90,17 @@ static int bq32k_write(struct device *dev, void *data, uint8_t off, uint8_t len) static int bq32k_rtc_read_time(struct device *dev, struct rtc_time *tm) { + struct i2c_client *client = to_i2c_client(dev); struct bq32k_regs regs; int error; + /* + * When the device doesn't have the interrupt connected, prevent + * userpace from polling the RTC registers too frequently. + */ + if (client->irq <= 0) + usleep_range(2000, 2500); + error = bq32k_read(dev, ®s, 0, sizeof(regs)); if (error) return error; @@ -304,7 +313,7 @@ static void bq32k_remove(struct i2c_client *client) } static const struct i2c_device_id bq32k_id[] = { - { "bq32000" }, + { .name = "bq32000" }, { } }; MODULE_DEVICE_TABLE(i2c, bq32k_id); diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index f89ab58f5048..fa04ece151b8 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -934,6 +934,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) unsigned char rtc_control; unsigned address_space; u32 flags = 0; + bool hpet_registered = false; struct nvmem_config nvmem_cfg = { .name = "cmos_nvram", .word_size = 1, @@ -1091,6 +1092,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) " failed in rtc_init()."); goto cleanup1; } + hpet_registered = true; } else rtc_cmos_int_handler = cmos_interrupt; @@ -1140,6 +1142,10 @@ cleanup2: if (is_valid_irq(rtc_irq)) free_irq(rtc_irq, cmos_rtc.rtc); cleanup1: + if (hpet_registered) { + hpet_mask_rtc_irq_bit(RTC_IRQMASK); + hpet_unregister_irq_handler(cmos_interrupt); + } cmos_rtc.dev = NULL; cleanup0: if (RTC_IOMAPPED) diff --git a/drivers/rtc/rtc-cros-ec.c b/drivers/rtc/rtc-cros-ec.c index e956505a06fb..f3ecd017e2f7 100644 --- a/drivers/rtc/rtc-cros-ec.c +++ b/drivers/rtc/rtc-cros-ec.c @@ -388,8 +388,8 @@ static void cros_ec_rtc_remove(struct platform_device *pdev) } static const struct platform_device_id cros_ec_rtc_id[] = { - { DRV_NAME, 0 }, - {} + { .name = DRV_NAME }, + { } }; MODULE_DEVICE_TABLE(platform, cros_ec_rtc_id); diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 7205c59ff729..0707ded5368b 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -269,6 +269,16 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) if (tmp & DS1338_BIT_OSF) return -EINVAL; break; + case ds_1337: + case ds_1339: + case ds_1341: + case ds_3231: + ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp); + if (ret) + return ret; + if (tmp & DS1337_BIT_OSF) + return -EINVAL; + break; case ds_1340: if (tmp & DS1340_BIT_nEOSC) return -EINVAL; @@ -279,13 +289,6 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) if (tmp & DS1340_BIT_OSF) return -EINVAL; break; - case ds_1341: - ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &tmp); - if (ret) - return ret; - if (tmp & DS1337_BIT_OSF) - return -EINVAL; - break; case ds_1388: ret = regmap_read(ds1307->regmap, DS1388_REG_FLAG, &tmp); if (ret) @@ -308,7 +311,7 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t) t->tm_hour = bcd2bin(tmp); /* rx8130 is bit position, not BCD */ if (ds1307->type == rx_8130) - t->tm_wday = fls(regs[DS1307_REG_WDAY] & 0x7f); + t->tm_wday = fls(regs[DS1307_REG_WDAY] & 0x7f) - 1; else t->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1; t->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f); @@ -380,14 +383,17 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t) regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL, DS1338_BIT_OSF, 0); break; - case ds_1340: - regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG, - DS1340_BIT_OSF, 0); - break; + case ds_1337: + case ds_1339: case ds_1341: + case ds_3231: regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS, DS1337_BIT_OSF, 0); break; + case ds_1340: + regmap_update_bits(ds1307->regmap, DS1340_REG_FLAG, + DS1340_BIT_OSF, 0); + break; case ds_1388: regmap_update_bits(ds1307->regmap, DS1388_REG_FLAG, DS1388_BIT_OSF, 0); @@ -1063,24 +1069,24 @@ static const struct chip_desc chips[last_ds_type] = { }; static const struct i2c_device_id ds1307_id[] = { - { "ds1307", ds_1307 }, - { "ds1308", ds_1308 }, - { "ds1337", ds_1337 }, - { "ds1338", ds_1338 }, - { "ds1339", ds_1339 }, - { "ds1388", ds_1388 }, - { "ds1340", ds_1340 }, - { "ds1341", ds_1341 }, - { "ds3231", ds_3231 }, - { "m41t0", m41t0 }, - { "m41t00", m41t00 }, - { "m41t11", m41t11 }, - { "mcp7940x", mcp794xx }, - { "mcp7941x", mcp794xx }, - { "pt7c4338", ds_1307 }, - { "rx8025", rx_8025 }, - { "isl12057", ds_1337 }, - { "rx8130", rx_8130 }, + { .name = "ds1307", .driver_data = ds_1307 }, + { .name = "ds1308", .driver_data = ds_1308 }, + { .name = "ds1337", .driver_data = ds_1337 }, + { .name = "ds1338", .driver_data = ds_1338 }, + { .name = "ds1339", .driver_data = ds_1339 }, + { .name = "ds1388", .driver_data = ds_1388 }, + { .name = "ds1340", .driver_data = ds_1340 }, + { .name = "ds1341", .driver_data = ds_1341 }, + { .name = "ds3231", .driver_data = ds_3231 }, + { .name = "m41t0", .driver_data = m41t0 }, + { .name = "m41t00", .driver_data = m41t00 }, + { .name = "m41t11", .driver_data = m41t11 }, + { .name = "mcp7940x", .driver_data = mcp794xx }, + { .name = "mcp7941x", .driver_data = mcp794xx }, + { .name = "pt7c4338", .driver_data = ds_1307 }, + { .name = "rx8025", .driver_data = rx_8025 }, + { .name = "isl12057", .driver_data = ds_1337 }, + { .name = "rx8130", .driver_data = rx_8130 }, { } }; MODULE_DEVICE_TABLE(i2c, ds1307_id); @@ -1409,7 +1415,7 @@ static void ds1307_hwmon_register(struct ds1307 *ds1307) { } -#endif /* CONFIG_RTC_DRV_DS1307_HWMON */ +#endif /* IS_REACHABLE(CONFIG_HWMON) */ /*----------------------------------------------------------------------*/ @@ -1658,18 +1664,153 @@ static int ds3231_clks_register(struct ds1307 *ds1307) return 0; } +/* ds1307 RTC clock output support */ +static unsigned long ds1307_clk_rates[] = { + 1, + 4096, + 8192, + 32768, +}; + +static unsigned long ds1307_clk_sqw_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + int ret; + unsigned int rate_id; + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + ret = regmap_read(ds1307->regmap, DS1307_REG_CONTROL, &rate_id); + if (ret) + return ret; + + rate_id &= (DS1307_BIT_RS1 | DS1307_BIT_RS0); + + return ds1307_clk_rates[rate_id]; +} + +static int ds1307_clk_sqw_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ds1307_clk_rates); i++) { + if (req->rate <= ds1307_clk_rates[i]) { + req->rate = ds1307_clk_rates[i]; + return 0; + } + } + + /* Default rate 1Hz */ + req->rate = ds1307_clk_rates[0]; + + return 0; +} + +static int ds1307_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int id, ret; + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + for (id = 0; id < ARRAY_SIZE(ds1307_clk_rates); id++) { + if (ds1307_clk_rates[id] == rate) + break; + } + + if (id >= ARRAY_SIZE(ds1307_clk_rates)) + return -EINVAL; + + ret = regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL, + DS1307_BIT_RS0 | DS1307_BIT_RS1, id); + + return ret; +} + +static int ds1307_clk_sqw_prepare(struct clk_hw *hw) +{ + int ret; + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + ret = regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL, + DS1307_BIT_SQWE, DS1307_BIT_SQWE); + + return ret; +} + +static void ds1307_clk_sqw_unprepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + regmap_update_bits(ds1307->regmap, DS1307_REG_CONTROL, + DS1307_BIT_SQWE, ~DS1307_BIT_SQWE); +} + +static int ds1307_clk_sqw_is_prepared(struct clk_hw *hw) +{ + int ret; + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + unsigned int status; + + ret = regmap_read(ds1307->regmap, DS1307_REG_CONTROL, &status); + if (ret) + return ret; + + return !!(status & DS1307_BIT_SQWE); +} + +static const struct clk_ops ds1307_clk_sqw_ops = { + .prepare = ds1307_clk_sqw_prepare, + .unprepare = ds1307_clk_sqw_unprepare, + .is_prepared = ds1307_clk_sqw_is_prepared, + .recalc_rate = ds1307_clk_sqw_recalc_rate, + .set_rate = ds1307_clk_sqw_set_rate, + .determine_rate = ds1307_clk_sqw_determine_rate, +}; + +static int rtc_ds1307_clks_register(struct ds1307 *ds1307) +{ + struct device_node *node = ds1307->dev->of_node; + struct clk *clk; + struct clk_init_data init = {0}; + + init.name = "ds1307_clk_sqw"; + init.ops = &ds1307_clk_sqw_ops; + + ds1307->clks[0].init = &init; + + /* Register the clock with CCF */ + clk = devm_clk_register(ds1307->dev, &ds1307->clks[0]); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + if (node) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return 0; +} + static void ds1307_clks_register(struct ds1307 *ds1307) { int ret; - if (ds1307->type != ds_3231) + switch (ds1307->type) { + case ds_3231: + ret = ds3231_clks_register(ds1307); + break; + + case ds_1307: + ret = rtc_ds1307_clks_register(ds1307); + break; + + default: return; + } - ret = ds3231_clks_register(ds1307); if (ret) { dev_warn(ds1307->dev, "unable to register clock device %d\n", ret); } + } #else diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c index c2359eb86bc9..8c247215d611 100644 --- a/drivers/rtc/rtc-ds1374.c +++ b/drivers/rtc/rtc-ds1374.c @@ -52,7 +52,7 @@ #define DS1374_REG_TCR 0x09 /* Trickle Charge */ static const struct i2c_device_id ds1374_id[] = { - { "ds1374" }, + { .name = "ds1374" }, { } }; MODULE_DEVICE_TABLE(i2c, ds1374_id); diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 6e5314215d00..c610beb55bb5 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -133,7 +133,7 @@ static int ds1672_probe(struct i2c_client *client) } static const struct i2c_device_id ds1672_id[] = { - { "ds1672" }, + { .name = "ds1672" }, { } }; MODULE_DEVICE_TABLE(i2c, ds1672_id); diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index 18f35823b4b5..d1ef9e0dad34 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c @@ -566,7 +566,7 @@ static int ds3232_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id ds3232_id[] = { - { "ds3232" }, + { .name = "ds3232" }, { } }; MODULE_DEVICE_TABLE(i2c, ds3232_id); diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c index dc1ccbc65dcb..d555e5d59881 100644 --- a/drivers/rtc/rtc-em3027.c +++ b/drivers/rtc/rtc-em3027.c @@ -129,7 +129,7 @@ static int em3027_probe(struct i2c_client *client) } static const struct i2c_device_id em3027_id[] = { - { "em3027" }, + { .name = "em3027" }, { } }; MODULE_DEVICE_TABLE(i2c, em3027_id); diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c index f82728ebac0c..28bb6d3f644e 100644 --- a/drivers/rtc/rtc-fm3130.c +++ b/drivers/rtc/rtc-fm3130.c @@ -53,7 +53,7 @@ struct fm3130 { int data_valid; }; static const struct i2c_device_id fm3130_id[] = { - { "fm3130" }, + { .name = "fm3130" }, { } }; MODULE_DEVICE_TABLE(i2c, fm3130_id); diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c index 7a170c0f9710..3156aa5f2d9f 100644 --- a/drivers/rtc/rtc-hym8563.c +++ b/drivers/rtc/rtc-hym8563.c @@ -564,8 +564,8 @@ static int hym8563_probe(struct i2c_client *client) } static const struct i2c_device_id hym8563_id[] = { - { "hym8563" }, - {} + { .name = "hym8563" }, + { } }; MODULE_DEVICE_TABLE(i2c, hym8563_id); diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c index 5fc52dc64213..bc36288854ee 100644 --- a/drivers/rtc/rtc-isl12022.c +++ b/drivers/rtc/rtc-isl12022.c @@ -604,7 +604,7 @@ static const struct of_device_id isl12022_dt_match[] = { MODULE_DEVICE_TABLE(of, isl12022_dt_match); static const struct i2c_device_id isl12022_id[] = { - { "isl12022" }, + { .name = "isl12022" }, { } }; MODULE_DEVICE_TABLE(i2c, isl12022_id); diff --git a/drivers/rtc/rtc-isl12026.c b/drivers/rtc/rtc-isl12026.c index 45a2c9f676c5..b86a325d8a23 100644 --- a/drivers/rtc/rtc-isl12026.c +++ b/drivers/rtc/rtc-isl12026.c @@ -485,8 +485,8 @@ static const struct of_device_id isl12026_dt_match[] = { MODULE_DEVICE_TABLE(of, isl12026_dt_match); static const struct i2c_device_id isl12026_id[] = { - { "isl12026" }, - { }, + { .name = "isl12026" }, + { } }; MODULE_DEVICE_TABLE(i2c, isl12026_id); diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index f71a6bb77b2a..9bdd5d121571 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -110,11 +110,11 @@ static const struct isl1208_config config_raa215300_a0 = { }; static const struct i2c_device_id isl1208_id[] = { - { "isl1208", .driver_data = (kernel_ulong_t)&config_isl1208 }, - { "isl1209", .driver_data = (kernel_ulong_t)&config_isl1209 }, - { "isl1218", .driver_data = (kernel_ulong_t)&config_isl1218 }, - { "isl1219", .driver_data = (kernel_ulong_t)&config_isl1219 }, - { "raa215300_a0", .driver_data = (kernel_ulong_t)&config_raa215300_a0 }, + { .name = "isl1208", .driver_data = (kernel_ulong_t)&config_isl1208 }, + { .name = "isl1209", .driver_data = (kernel_ulong_t)&config_isl1209 }, + { .name = "isl1218", .driver_data = (kernel_ulong_t)&config_isl1218 }, + { .name = "isl1219", .driver_data = (kernel_ulong_t)&config_isl1219 }, + { .name = "raa215300_a0", .driver_data = (kernel_ulong_t)&config_raa215300_a0 }, { } }; MODULE_DEVICE_TABLE(i2c, isl1208_id); @@ -822,6 +822,11 @@ static const struct nvmem_config isl1208_nvmem_config = { .reg_write = isl1208_nvmem_write, }; +static void isl1208_disable_irq_wake_action(void *data) +{ + disable_irq_wake((unsigned long)data); +} + static int isl1208_setup_irq(struct i2c_client *client, int irq) { int rc = devm_request_threaded_irq(&client->dev, irq, NULL, @@ -831,7 +836,15 @@ static int isl1208_setup_irq(struct i2c_client *client, int irq) client); if (!rc) { device_init_wakeup(&client->dev, true); - enable_irq_wake(irq); + rc = enable_irq_wake(irq); + if (rc) + return rc; + + rc = devm_add_action_or_reset(&client->dev, + isl1208_disable_irq_wake_action, + (void *)(unsigned long)irq); + if (rc) + return rc; } else { dev_err(&client->dev, "Unable to request irq %d, no alarm support\n", diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index b26afef37d9c..3c8c379392c1 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -71,17 +71,17 @@ #define M41T80_FEATURE_SQ_ALT BIT(4) /* RSx bits are in reg 4 */ static const struct i2c_device_id m41t80_id[] = { - { "m41t62", M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT }, - { "m41t65", M41T80_FEATURE_WD }, - { "m41t80", M41T80_FEATURE_SQ }, - { "m41t81", M41T80_FEATURE_HT | M41T80_FEATURE_SQ}, - { "m41t81s", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, - { "m41t82", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, - { "m41t83", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, - { "m41st84", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, - { "m41st85", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, - { "m41st87", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, - { "rv4162", M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT }, + { .name = "m41t62", .driver_data = M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT }, + { .name = "m41t65", .driver_data = M41T80_FEATURE_WD }, + { .name = "m41t80", .driver_data = M41T80_FEATURE_SQ }, + { .name = "m41t81", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_SQ}, + { .name = "m41t81s", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "m41t82", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "m41t83", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "m41st84", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "m41st85", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "m41st87", .driver_data = M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { .name = "rv4162", .driver_data = M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT }, { } }; MODULE_DEVICE_TABLE(i2c, m41t80_id); diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c index 9444cb5f5190..fa7a4b5f848d 100644 --- a/drivers/rtc/rtc-m41t93.c +++ b/drivers/rtc/rtc-m41t93.c @@ -12,6 +12,9 @@ #include <linux/platform_device.h> #include <linux/rtc.h> #include <linux/spi/spi.h> +#include <linux/regmap.h> +#include <linux/clk-provider.h> +#include <linux/watchdog.h> #define M41T93_REG_SSEC 0 #define M41T93_REG_ST_SEC 1 @@ -21,7 +24,23 @@ #define M41T93_REG_DAY 5 #define M41T93_REG_MON 6 #define M41T93_REG_YEAR 7 - +#define M41T93_REG_AL1_MONTH 0xa +#define M41T93_REG_AL1_DATE 0xb +#define M41T93_REG_AL1_HOUR 0xc +#define M41T93_REG_AL1_MIN 0xd +#define M41T93_REG_AL1_SEC 0xe +#define M41T93_BIT_A1IE BIT(7) +#define M41T93_BIT_ABE BIT(5) +#define M41T93_FLAG_AF1 BIT(6) +#define M41T93_SRAM_BASE 0x19 +#define M41T93_REG_SQW 0x13 +#define M41T93_SQW_RS_MASK 0xf0 +#define M41T93_SQW_RS_SHIFT 4 +#define M41T93_BIT_SQWE BIT(6) +#define M41T93_REG_WATCHDOG 0x9 +#define M41T93_WDT_RB_MASK 0x3 +#define M41T93_WDT_BMB_MASK 0x7c +#define M41T93_WDT_BMB_SHIFT 2 #define M41T93_REG_ALM_HOUR_HT 0xc #define M41T93_REG_FLAGS 0xf @@ -31,23 +50,23 @@ #define M41T93_FLAG_BL (1 << 4) #define M41T93_FLAG_HT (1 << 6) -static inline int m41t93_set_reg(struct spi_device *spi, u8 addr, u8 data) -{ - u8 buf[2]; - - /* MSB must be '1' to write */ - buf[0] = addr | 0x80; - buf[1] = data; - - return spi_write(spi, buf, sizeof(buf)); -} +struct m41t93_data { + struct rtc_device *rtc; + struct regmap *regmap; +#ifdef CONFIG_COMMON_CLK + struct clk_hw clks; +#endif +#ifdef CONFIG_WATCHDOG + struct watchdog_device wdd; +#endif +}; static int m41t93_set_time(struct device *dev, struct rtc_time *tm) { - struct spi_device *spi = to_spi_device(dev); - int tmp; - u8 buf[9] = {0x80}; /* write cmd + 8 data bytes */ - u8 * const data = &buf[1]; /* ptr to first data byte */ + struct m41t93_data *m41t93 = dev_get_drvdata(dev); + int tmp, ret; + u8 buf[8] = {0}; /* 8 data bytes */ + u8 * const data = &buf[0]; /* ptr to first data byte */ dev_dbg(dev, "%s secs=%d, mins=%d, " "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", @@ -56,31 +75,31 @@ static int m41t93_set_time(struct device *dev, struct rtc_time *tm) tm->tm_mon, tm->tm_year, tm->tm_wday); if (tm->tm_year < 100) { - dev_warn(&spi->dev, "unsupported date (before 2000-01-01).\n"); + dev_warn(dev, "unsupported date (before 2000-01-01).\n"); return -EINVAL; } - tmp = spi_w8r8(spi, M41T93_REG_FLAGS); - if (tmp < 0) - return tmp; + ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &tmp); + if (ret < 0) + return ret; if (tmp & M41T93_FLAG_OF) { - dev_warn(&spi->dev, "OF bit is set, resetting.\n"); - m41t93_set_reg(spi, M41T93_REG_FLAGS, tmp & ~M41T93_FLAG_OF); + dev_warn(dev, "OF bit is set, resetting.\n"); + regmap_write(m41t93->regmap, M41T93_REG_FLAGS, tmp & ~M41T93_FLAG_OF); - tmp = spi_w8r8(spi, M41T93_REG_FLAGS); - if (tmp < 0) { - return tmp; + ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &tmp); + if (ret < 0) { + return ret; } else if (tmp & M41T93_FLAG_OF) { /* OF cannot be immediately reset: oscillator has to be * restarted. */ u8 reset_osc = buf[M41T93_REG_ST_SEC] | M41T93_FLAG_ST; - dev_warn(&spi->dev, + dev_warn(dev, "OF bit is still set, kickstarting clock.\n"); - m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc); + regmap_write(m41t93->regmap, M41T93_REG_ST_SEC, reset_osc); reset_osc &= ~M41T93_FLAG_ST; - m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc); + regmap_write(m41t93->regmap, M41T93_REG_ST_SEC, reset_osc); } } @@ -94,14 +113,13 @@ static int m41t93_set_time(struct device *dev, struct rtc_time *tm) data[M41T93_REG_MON] = bin2bcd(tm->tm_mon + 1); data[M41T93_REG_YEAR] = bin2bcd(tm->tm_year % 100); - return spi_write(spi, buf, sizeof(buf)); + return regmap_bulk_write(m41t93->regmap, M41T93_REG_SSEC, buf, sizeof(buf)); } static int m41t93_get_time(struct device *dev, struct rtc_time *tm) { - struct spi_device *spi = to_spi_device(dev); - const u8 start_addr = 0; + struct m41t93_data *m41t93 = dev_get_drvdata(dev); u8 buf[8]; int century_after_1900; int tmp; @@ -113,32 +131,32 @@ static int m41t93_get_time(struct device *dev, struct rtc_time *tm) case after poweron. Time is valid after resetting HT bit. 2. oscillator fail bit (OF) is set: time is invalid. */ - tmp = spi_w8r8(spi, M41T93_REG_ALM_HOUR_HT); - if (tmp < 0) - return tmp; + ret = regmap_read(m41t93->regmap, M41T93_REG_ALM_HOUR_HT, &tmp); + if (ret < 0) + return ret; if (tmp & M41T93_FLAG_HT) { - dev_dbg(&spi->dev, "HT bit is set, reenable clock update.\n"); - m41t93_set_reg(spi, M41T93_REG_ALM_HOUR_HT, - tmp & ~M41T93_FLAG_HT); + dev_dbg(dev, "HT bit is set, reenable clock update.\n"); + regmap_write(m41t93->regmap, M41T93_REG_ALM_HOUR_HT, + tmp & ~M41T93_FLAG_HT); } - tmp = spi_w8r8(spi, M41T93_REG_FLAGS); - if (tmp < 0) - return tmp; + ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &tmp); + if (ret < 0) + return ret; if (tmp & M41T93_FLAG_OF) { ret = -EINVAL; - dev_warn(&spi->dev, "OF bit is set, write time to restart.\n"); + dev_warn(dev, "OF bit is set, write time to restart.\n"); } if (tmp & M41T93_FLAG_BL) - dev_warn(&spi->dev, "BL bit is set, replace battery.\n"); + dev_warn(dev, "BL bit is set, replace battery.\n"); /* read actual time/date */ - tmp = spi_write_then_read(spi, &start_addr, 1, buf, sizeof(buf)); - if (tmp < 0) - return tmp; + ret = regmap_bulk_read(m41t93->regmap, M41T93_REG_SSEC, buf, sizeof(buf)); + if (ret < 0) + return ret; tm->tm_sec = bcd2bin(buf[M41T93_REG_ST_SEC]); tm->tm_min = bcd2bin(buf[M41T93_REG_MIN]); @@ -159,41 +177,407 @@ static int m41t93_get_time(struct device *dev, struct rtc_time *tm) return ret; } +static int m41t93_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct m41t93_data *m41t93 = dev_get_drvdata(dev); + int ret; + unsigned int val; + u8 alarm_vals[5] = {0}; + + ret = regmap_bulk_write(m41t93->regmap, M41T93_REG_AL1_DATE, alarm_vals, 4); + if (ret) + return ret; + + /* Set alarm values */ + alarm_vals[0] = bin2bcd(alrm->time.tm_mon + 1) & 0x1f; + alarm_vals[1] = bin2bcd(alrm->time.tm_mday) & 0x3f; + alarm_vals[2] = bin2bcd(alrm->time.tm_hour) & 0x3f; + alarm_vals[3] = bin2bcd(alrm->time.tm_min) & 0x7f; + alarm_vals[4] = bin2bcd(alrm->time.tm_sec) & 0x7f; + + if (alrm->enabled) { + /* Enable alarm IRQ generation */ + alarm_vals[0] |= M41T93_BIT_A1IE | M41T93_BIT_ABE; + } + + /* Preserve SQWE bit */ + ret = regmap_read(m41t93->regmap, M41T93_REG_AL1_MONTH, &val); + if (ret) + return ret; + + alarm_vals[0] |= val & 0x40; + + ret = regmap_bulk_write(m41t93->regmap, M41T93_REG_AL1_MONTH, + alarm_vals, sizeof(alarm_vals)); + if (ret) + return ret; + + /* Device address pointer is now at FLAG register, move it to other location + * to finish setting alarm, as recommended by the datasheet. + * We do read of AL1_MONTH register to achieve this. + */ + ret = regmap_read(m41t93->regmap, M41T93_REG_AL1_MONTH, &val); + if (ret) + return ret; + + if (bcd2bin(val & 0x1f) == (alrm->time.tm_mon & 0x1f)) + dev_notice(dev, "Alarm set successfully\n"); + + return 0; +} + +static int m41t93_get_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct m41t93_data *m41t93 = dev_get_drvdata(dev); + int ret; + unsigned int val; + u8 alarm_vals[5] = {0}; + + ret = regmap_bulk_read(m41t93->regmap, M41T93_REG_AL1_MONTH, + alarm_vals, sizeof(alarm_vals)); + if (ret) + return ret; + + alrm->time.tm_mon = bcd2bin(alarm_vals[0] & 0x1f) - 1; + alrm->time.tm_mday = bcd2bin(alarm_vals[1] & 0x3f); + alrm->time.tm_hour = bcd2bin(alarm_vals[2] & 0x3f); + alrm->time.tm_min = bcd2bin(alarm_vals[3] & 0x7f); + alrm->time.tm_sec = bcd2bin(alarm_vals[4] & 0x7f); + + alrm->enabled = !!(alarm_vals[0] & M41T93_BIT_A1IE); + + ret = regmap_read(m41t93->regmap, M41T93_REG_FLAGS, &val); + if (ret) + return ret; + + alrm->pending = (val & M41T93_FLAG_AF1) && alrm->enabled; + + return 0; +} + +static int m41t93_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct m41t93_data *m41t93 = dev_get_drvdata(dev); + unsigned int val; + int ret; + + val = enabled ? M41T93_BIT_A1IE | M41T93_BIT_ABE : 0; + + ret = regmap_update_bits(m41t93->regmap, M41T93_REG_AL1_MONTH, + M41T93_BIT_A1IE | M41T93_BIT_ABE, val); + if (ret) + return ret; + + return 0; +} + static const struct rtc_class_ops m41t93_rtc_ops = { .read_time = m41t93_get_time, .set_time = m41t93_set_time, + .set_alarm = m41t93_set_alarm, + .read_alarm = m41t93_get_alarm, + .alarm_irq_enable = m41t93_alarm_irq_enable, +}; + +#ifdef CONFIG_COMMON_CLK +#define clk_sqw_to_m41t93_data(clk) \ + container_of(clk, struct m41t93_data, clks) + +/* m41t93 RTC clock output support */ +static unsigned long m41t93_clk_rates[] = { + 0, + 32768, /* RS3:RS0 = 0b0001 */ + 8192, + 4096, + 2048, + 1024, + 512, + 256, + 128, + 64, + 32, + 16, + 8, + 4, + 2, + 1, /* RS3:RS0 = 0b1111 */ +}; + +static unsigned long m41t93_clk_sqw_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + int ret; + unsigned int rate_id; + struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw); + + ret = regmap_read(m41t93->regmap, M41T93_REG_SQW, &rate_id); + if (ret) + return ret; + + rate_id &= M41T93_SQW_RS_MASK; + rate_id >>= M41T93_SQW_RS_SHIFT; + + return m41t93_clk_rates[rate_id]; +} + +static int m41t93_clk_sqw_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + int i; + + for (i = 1; i < ARRAY_SIZE(m41t93_clk_rates); i++) { + if (req->rate >= m41t93_clk_rates[i]) { + req->rate = m41t93_clk_rates[i]; + return 0; + } + } + + return 0; +} + +static int m41t93_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int id, ret; + struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw); + + for (id = 0; id < ARRAY_SIZE(m41t93_clk_rates); id++) { + if (m41t93_clk_rates[id] == rate) + break; + } + + if (id >= ARRAY_SIZE(m41t93_clk_rates)) + return -EINVAL; + + ret = regmap_update_bits(m41t93->regmap, M41T93_REG_SQW, + M41T93_SQW_RS_MASK, id << M41T93_SQW_RS_SHIFT); + + return ret; +} + +static int m41t93_clk_sqw_prepare(struct clk_hw *hw) +{ + int ret; + struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw); + + ret = regmap_update_bits(m41t93->regmap, M41T93_REG_AL1_MONTH, + M41T93_BIT_SQWE, M41T93_BIT_SQWE); + + return ret; +} + +static void m41t93_clk_sqw_unprepare(struct clk_hw *hw) +{ + struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw); + + regmap_update_bits(m41t93->regmap, M41T93_REG_AL1_MONTH, + M41T93_BIT_SQWE, 0); +} + +static int m41t93_clk_sqw_is_prepared(struct clk_hw *hw) +{ + int ret; + struct m41t93_data *m41t93 = clk_sqw_to_m41t93_data(hw); + unsigned int status; + + ret = regmap_read(m41t93->regmap, M41T93_REG_AL1_MONTH, &status); + if (ret) + return ret; + + return !!(status & M41T93_BIT_SQWE); +} + +static const struct clk_ops m41t93_clk_sqw_ops = { + .prepare = m41t93_clk_sqw_prepare, + .unprepare = m41t93_clk_sqw_unprepare, + .is_prepared = m41t93_clk_sqw_is_prepared, + .recalc_rate = m41t93_clk_sqw_recalc_rate, + .set_rate = m41t93_clk_sqw_set_rate, + .determine_rate = m41t93_clk_sqw_determine_rate, +}; + +static int rtc_m41t93_clks_register(struct device *dev, struct m41t93_data *m41t93) +{ + struct device_node *node = dev->of_node; + struct clk *clk; + struct clk_init_data init = {0}; + + init.name = "m41t93_clk_sqw"; + init.ops = &m41t93_clk_sqw_ops; + + m41t93->clks.init = &init; + + /* Register the clock with CCF */ + clk = devm_clk_register(dev, &m41t93->clks); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + if (node) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return 0; +} +#endif + +#ifdef CONFIG_WATCHDOG +static int m41t93_wdt_ping(struct watchdog_device *wdd) +{ + u8 resolution, mult; + u8 val = 0; + int ret; + struct m41t93_data *m41t93 = watchdog_get_drvdata(wdd); + + /* Resolution supported by hardware + * 0b00 : 1/16 seconds + * 0b01 : 1/4 second + * 0b10 : 1 second + * 0b11 : 4 seconds + */ + resolution = 0x2; /* hardcode resolution to 1s */ + mult = wdd->timeout; + val = resolution | (mult << M41T93_WDT_BMB_SHIFT & M41T93_WDT_BMB_MASK); + + ret = regmap_write_bits(m41t93->regmap, M41T93_REG_WATCHDOG, + M41T93_WDT_RB_MASK | M41T93_WDT_BMB_MASK, val); + + return ret; +} + +static int m41t93_wdt_start(struct watchdog_device *wdd) +{ + return m41t93_wdt_ping(wdd); +} + +static int m41t93_wdt_stop(struct watchdog_device *wdd) +{ + struct m41t93_data *m41t93 = watchdog_get_drvdata(wdd); + + /* Write 0 to watchdog register */ + return regmap_write_bits(m41t93->regmap, M41T93_REG_WATCHDOG, + M41T93_WDT_RB_MASK | M41T93_WDT_BMB_MASK, 0); +} + +static int m41t93_wdt_set_timeout(struct watchdog_device *wdd, + unsigned int new_timeout) +{ + wdd->timeout = new_timeout; + + return 0; +} + +static const struct watchdog_info m41t93_wdt_info = { + .identity = "m41t93 rtc Watchdog", + .options = WDIOF_ALARMONLY | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, +}; + +static const struct watchdog_ops m41t93_watchdog_ops = { + .owner = THIS_MODULE, + .start = m41t93_wdt_start, + .stop = m41t93_wdt_stop, + .ping = m41t93_wdt_ping, + .set_timeout = m41t93_wdt_set_timeout, }; +static int m41t93_watchdog_register(struct device *dev, struct m41t93_data *m41t93) +{ + int ret; + + m41t93->wdd.parent = dev; + m41t93->wdd.info = &m41t93_wdt_info; + m41t93->wdd.ops = &m41t93_watchdog_ops; + m41t93->wdd.min_timeout = 0; + m41t93->wdd.max_timeout = 10; + m41t93->wdd.timeout = 3; /* Default timeout is 3 sec */ + m41t93->wdd.status = WATCHDOG_NOWAYOUT_INIT_STATUS; + + watchdog_set_drvdata(&m41t93->wdd, m41t93); + + ret = devm_watchdog_register_device(dev, &m41t93->wdd); + if (ret) { + dev_warn(dev, "Failed to register watchdog\n"); + return ret; + } + + /* Disable watchdog at start */ + ret = m41t93_wdt_stop(&m41t93->wdd); + + return ret; +} +#endif + static struct spi_driver m41t93_driver; +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = 0x00, + .write_flag_mask = 0x80, + .zero_flag_mask = true, +}; + static int m41t93_probe(struct spi_device *spi) { - struct rtc_device *rtc; - int res; + int res, ret; + struct m41t93_data *m41t93; spi->bits_per_word = 8; spi_setup(spi); - res = spi_w8r8(spi, M41T93_REG_WDAY); + m41t93 = devm_kzalloc(&spi->dev, sizeof(struct m41t93_data), GFP_KERNEL); + + if (!m41t93) + return -ENOMEM; + + /* Set up regmap to access device registers*/ + m41t93->regmap = devm_regmap_init_spi(spi, ®map_config); + if (IS_ERR(m41t93->regmap)) { + dev_err(&spi->dev, "regmap init failure\n"); + return PTR_ERR(m41t93->regmap); + } + + ret = regmap_read(m41t93->regmap, M41T93_REG_WDAY, &res); + if (ret < 0) { + dev_err(&spi->dev, "IO error\n"); + return -EIO; + } + if (res < 0 || (res & 0xf8) != 0) { dev_err(&spi->dev, "not found 0x%x.\n", res); return -ENODEV; } - rtc = devm_rtc_device_register(&spi->dev, m41t93_driver.driver.name, - &m41t93_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc)) - return PTR_ERR(rtc); + spi_set_drvdata(spi, m41t93); + + m41t93->rtc = devm_rtc_device_register(&spi->dev, m41t93_driver.driver.name, + &m41t93_rtc_ops, THIS_MODULE); + if (IS_ERR(m41t93->rtc)) + return PTR_ERR(m41t93->rtc); - spi_set_drvdata(spi, rtc); +#ifdef CONFIG_COMMON_CLK + ret = rtc_m41t93_clks_register(&spi->dev, m41t93); + if (ret) + dev_warn(&spi->dev, "Unable to register clock\n"); +#endif +#ifdef CONFIG_WATCHDOG + ret = m41t93_watchdog_register(&spi->dev, m41t93); + if (ret) + dev_warn(&spi->dev, "Unable to register watchdog\n"); +#endif return 0; } +static const struct of_device_id m41t93_dt_match[] = { + { .compatible = "st,m41t93" }, + { } +}; +MODULE_DEVICE_TABLE(of, m41t93_dt_match); + static struct spi_driver m41t93_driver = { .driver = { .name = "rtc-m41t93", + .of_match_table = m41t93_dt_match, }, .probe = m41t93_probe, }; diff --git a/drivers/rtc/rtc-max31335.c b/drivers/rtc/rtc-max31335.c index 952b455071d6..595816973851 100644 --- a/drivers/rtc/rtc-max31335.c +++ b/drivers/rtc/rtc-max31335.c @@ -745,8 +745,8 @@ static int max31335_probe(struct i2c_client *client) } static const struct i2c_device_id max31335_id[] = { - { "max31331", (kernel_ulong_t)&chip[ID_MAX31331] }, - { "max31335", (kernel_ulong_t)&chip[ID_MAX31335] }, + { .name = "max31331", .driver_data = (kernel_ulong_t)&chip[ID_MAX31331] }, + { .name = "max31335", .driver_data = (kernel_ulong_t)&chip[ID_MAX31335] }, { } }; diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c index 7be31fce5bc7..8ef6d0fcd032 100644 --- a/drivers/rtc/rtc-max6900.c +++ b/drivers/rtc/rtc-max6900.c @@ -215,7 +215,7 @@ static int max6900_probe(struct i2c_client *client) } static const struct i2c_device_id max6900_id[] = { - { "max6900" }, + { .name = "max6900" }, { } }; MODULE_DEVICE_TABLE(i2c, max6900_id); diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c index 3cdfd78a07cc..375565a3bddf 100644 --- a/drivers/rtc/rtc-max77686.c +++ b/drivers/rtc/rtc-max77686.c @@ -866,11 +866,11 @@ static SIMPLE_DEV_PM_OPS(max77686_rtc_pm_ops, max77686_rtc_suspend, max77686_rtc_resume); static const struct platform_device_id rtc_id[] = { - { "max77686-rtc", .driver_data = (kernel_ulong_t)&max77686_drv_data, }, - { "max77802-rtc", .driver_data = (kernel_ulong_t)&max77802_drv_data, }, - { "max77620-rtc", .driver_data = (kernel_ulong_t)&max77620_drv_data, }, - { "max77714-rtc", .driver_data = (kernel_ulong_t)&max77714_drv_data, }, - {}, + { .name = "max77686-rtc", .driver_data = (kernel_ulong_t)&max77686_drv_data }, + { .name = "max77802-rtc", .driver_data = (kernel_ulong_t)&max77802_drv_data }, + { .name = "max77620-rtc", .driver_data = (kernel_ulong_t)&max77620_drv_data }, + { .name = "max77714-rtc", .driver_data = (kernel_ulong_t)&max77714_drv_data }, + { } }; MODULE_DEVICE_TABLE(platform, rtc_id); diff --git a/drivers/rtc/rtc-max8997.c b/drivers/rtc/rtc-max8997.c index e7618d715bd8..89203c92e2cd 100644 --- a/drivers/rtc/rtc-max8997.c +++ b/drivers/rtc/rtc-max8997.c @@ -512,8 +512,8 @@ static void max8997_rtc_shutdown(struct platform_device *pdev) } static const struct platform_device_id rtc_id[] = { - { "max8997-rtc", 0 }, - {}, + { .name = "max8997-rtc" }, + { } }; MODULE_DEVICE_TABLE(platform, rtc_id); diff --git a/drivers/rtc/rtc-max8998.c b/drivers/rtc/rtc-max8998.c index c873b4509b3c..a2c946edcd1a 100644 --- a/drivers/rtc/rtc-max8998.c +++ b/drivers/rtc/rtc-max8998.c @@ -299,8 +299,8 @@ no_irq: } static const struct platform_device_id max8998_rtc_id[] = { - { "max8998-rtc", TYPE_MAX8998 }, - { "lp3974-rtc", TYPE_LP3974 }, + { .name = "max8998-rtc", .driver_data = TYPE_MAX8998 }, + { .name = "lp3974-rtc", .driver_data = TYPE_LP3974 }, { } }; MODULE_DEVICE_TABLE(platform, max8998_rtc_id); diff --git a/drivers/rtc/rtc-mpfs.c b/drivers/rtc/rtc-mpfs.c index 6aa3eae575d2..ece6de4a6adb 100644 --- a/drivers/rtc/rtc-mpfs.c +++ b/drivers/rtc/rtc-mpfs.c @@ -112,7 +112,7 @@ static int mpfs_rtc_settime(struct device *dev, struct rtc_time *tm) ctrl |= CONTROL_UPLOAD_BIT; writel(ctrl, rtcdev->base + CONTROL_REG); - ret = read_poll_timeout(readl, prog, prog & CONTROL_UPLOAD_BIT, 0, UPLOAD_TIMEOUT_US, + ret = read_poll_timeout(readl, prog, !(prog & CONTROL_UPLOAD_BIT), 0, UPLOAD_TIMEOUT_US, false, rtcdev->base + CONTROL_REG); if (ret) { dev_err(dev, "timed out uploading time to rtc"); diff --git a/drivers/rtc/rtc-msc313.c b/drivers/rtc/rtc-msc313.c index 8d7737e0e2e0..6ef9c4efd7c9 100644 --- a/drivers/rtc/rtc-msc313.c +++ b/drivers/rtc/rtc-msc313.c @@ -160,7 +160,7 @@ static const struct rtc_class_ops msc313_rtc_ops = { static irqreturn_t msc313_rtc_interrupt(s32 irq, void *dev_id) { - struct msc313_rtc *priv = dev_get_drvdata(dev_id); + struct msc313_rtc *priv = dev_id; u16 reg; reg = readw(priv->rtc_base + REG_RTC_STATUS_INT); @@ -206,7 +206,7 @@ static int msc313_rtc_probe(struct platform_device *pdev) priv->rtc_dev->range_max = U32_MAX; ret = devm_request_irq(dev, irq, msc313_rtc_interrupt, IRQF_SHARED, - dev_name(&pdev->dev), &pdev->dev); + dev_name(&pdev->dev), priv); if (ret) { dev_err(dev, "Could not request IRQ\n"); return ret; diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c index c27ad626d09f..f88976fd6d5d 100644 --- a/drivers/rtc/rtc-mv.c +++ b/drivers/rtc/rtc-mv.c @@ -301,6 +301,28 @@ static const struct of_device_id rtc_mv_of_match_table[] = { MODULE_DEVICE_TABLE(of, rtc_mv_of_match_table); #endif +#ifdef CONFIG_PM_SLEEP +static int mv_rtc_suspend(struct device *dev) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + + if (device_may_wakeup(dev) && pdata->irq >= 0) + enable_irq_wake(pdata->irq); + return 0; +} + +static int mv_rtc_resume(struct device *dev) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + + if (device_may_wakeup(dev) && pdata->irq >= 0) + disable_irq_wake(pdata->irq); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mv_rtc_pm_ops, mv_rtc_suspend, mv_rtc_resume); + /* * mv_rtc_remove() lives in .exit.text. For drivers registered via * module_platform_driver_probe() this is ok because they cannot get unbound at @@ -312,6 +334,7 @@ static struct platform_driver mv_rtc_driver __refdata = { .driver = { .name = "rtc-mv", .of_match_table = of_match_ptr(rtc_mv_of_match_table), + .pm = &mv_rtc_pm_ops, }, }; diff --git a/drivers/rtc/rtc-nct3018y.c b/drivers/rtc/rtc-nct3018y.c index cd4b1db902e9..700a395fad3a 100644 --- a/drivers/rtc/rtc-nct3018y.c +++ b/drivers/rtc/rtc-nct3018y.c @@ -572,7 +572,7 @@ static int nct3018y_probe(struct i2c_client *client) } static const struct i2c_device_id nct3018y_id[] = { - { "nct3018y" }, + { .name = "nct3018y" }, { } }; MODULE_DEVICE_TABLE(i2c, nct3018y_id); diff --git a/drivers/rtc/rtc-pcap.c b/drivers/rtc/rtc-pcap.c deleted file mode 100644 index d6651611a0c6..000000000000 --- a/drivers/rtc/rtc-pcap.c +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * pcap rtc code for Motorola EZX phones - * - * Copyright (c) 2008 guiming zhuo <gmzhuo@gmail.com> - * Copyright (c) 2009 Daniel Ribeiro <drwyrm@gmail.com> - * - * Based on Motorola's rtc.c Copyright (c) 2003-2005 Motorola - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/mfd/ezx-pcap.h> -#include <linux/rtc.h> -#include <linux/slab.h> -#include <linux/platform_device.h> - -struct pcap_rtc { - struct pcap_chip *pcap; - struct rtc_device *rtc; -}; - -static irqreturn_t pcap_rtc_irq(int irq, void *_pcap_rtc) -{ - struct pcap_rtc *pcap_rtc = _pcap_rtc; - unsigned long rtc_events; - - if (irq == pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ)) - rtc_events = RTC_IRQF | RTC_UF; - else if (irq == pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA)) - rtc_events = RTC_IRQF | RTC_AF; - else - rtc_events = 0; - - rtc_update_irq(pcap_rtc->rtc, 1, rtc_events); - return IRQ_HANDLED; -} - -static int pcap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) -{ - struct pcap_rtc *pcap_rtc = dev_get_drvdata(dev); - struct rtc_time *tm = &alrm->time; - unsigned long secs; - u32 tod; /* time of day, seconds since midnight */ - u32 days; /* days since 1/1/1970 */ - - ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_TODA, &tod); - secs = tod & PCAP_RTC_TOD_MASK; - - ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_DAYA, &days); - secs += (days & PCAP_RTC_DAY_MASK) * SEC_PER_DAY; - - rtc_time64_to_tm(secs, tm); - - return 0; -} - -static int pcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) -{ - struct pcap_rtc *pcap_rtc = dev_get_drvdata(dev); - unsigned long secs = rtc_tm_to_time64(&alrm->time); - u32 tod, days; - - tod = secs % SEC_PER_DAY; - ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_TODA, tod); - - days = secs / SEC_PER_DAY; - ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_DAYA, days); - - return 0; -} - -static int pcap_rtc_read_time(struct device *dev, struct rtc_time *tm) -{ - struct pcap_rtc *pcap_rtc = dev_get_drvdata(dev); - unsigned long secs; - u32 tod, days; - - ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_TOD, &tod); - secs = tod & PCAP_RTC_TOD_MASK; - - ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_DAY, &days); - secs += (days & PCAP_RTC_DAY_MASK) * SEC_PER_DAY; - - rtc_time64_to_tm(secs, tm); - - return 0; -} - -static int pcap_rtc_set_time(struct device *dev, struct rtc_time *tm) -{ - struct pcap_rtc *pcap_rtc = dev_get_drvdata(dev); - unsigned long secs = rtc_tm_to_time64(tm); - u32 tod, days; - - tod = secs % SEC_PER_DAY; - ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_TOD, tod); - - days = secs / SEC_PER_DAY; - ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_DAY, days); - - return 0; -} - -static int pcap_rtc_irq_enable(struct device *dev, int pirq, unsigned int en) -{ - struct pcap_rtc *pcap_rtc = dev_get_drvdata(dev); - - if (en) - enable_irq(pcap_to_irq(pcap_rtc->pcap, pirq)); - else - disable_irq(pcap_to_irq(pcap_rtc->pcap, pirq)); - - return 0; -} - -static int pcap_rtc_alarm_irq_enable(struct device *dev, unsigned int en) -{ - return pcap_rtc_irq_enable(dev, PCAP_IRQ_TODA, en); -} - -static const struct rtc_class_ops pcap_rtc_ops = { - .read_time = pcap_rtc_read_time, - .set_time = pcap_rtc_set_time, - .read_alarm = pcap_rtc_read_alarm, - .set_alarm = pcap_rtc_set_alarm, - .alarm_irq_enable = pcap_rtc_alarm_irq_enable, -}; - -static int __init pcap_rtc_probe(struct platform_device *pdev) -{ - struct pcap_rtc *pcap_rtc; - int timer_irq, alarm_irq; - int err = -ENOMEM; - - pcap_rtc = devm_kzalloc(&pdev->dev, sizeof(struct pcap_rtc), - GFP_KERNEL); - if (!pcap_rtc) - return err; - - pcap_rtc->pcap = dev_get_drvdata(pdev->dev.parent); - - platform_set_drvdata(pdev, pcap_rtc); - - pcap_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); - if (IS_ERR(pcap_rtc->rtc)) - return PTR_ERR(pcap_rtc->rtc); - - pcap_rtc->rtc->ops = &pcap_rtc_ops; - pcap_rtc->rtc->range_max = (1 << 14) * 86400ULL - 1; - - timer_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ); - alarm_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA); - - err = devm_request_irq(&pdev->dev, timer_irq, pcap_rtc_irq, 0, - "RTC Timer", pcap_rtc); - if (err) - return err; - - err = devm_request_irq(&pdev->dev, alarm_irq, pcap_rtc_irq, 0, - "RTC Alarm", pcap_rtc); - if (err) - return err; - - return devm_rtc_register_device(pcap_rtc->rtc); -} - -static struct platform_driver pcap_rtc_driver = { - .driver = { - .name = "pcap-rtc", - }, -}; - -module_platform_driver_probe(pcap_rtc_driver, pcap_rtc_probe); - -MODULE_DESCRIPTION("Motorola pcap rtc driver"); -MODULE_AUTHOR("guiming zhuo <gmzhuo@gmail.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c index e4785c5a55d0..1995e9f2756d 100644 --- a/drivers/rtc/rtc-pcf2127.c +++ b/drivers/rtc/rtc-pcf2127.c @@ -1449,10 +1449,10 @@ static const struct regmap_bus pcf2127_i2c_regmap = { static struct i2c_driver pcf2127_i2c_driver; static const struct i2c_device_id pcf2127_i2c_id[] = { - { "pcf2127", (kernel_ulong_t)&pcf21xx_cfg[PCF2127] }, - { "pcf2129", (kernel_ulong_t)&pcf21xx_cfg[PCF2129] }, - { "pca2129", (kernel_ulong_t)&pcf21xx_cfg[PCF2129] }, - { "pcf2131", (kernel_ulong_t)&pcf21xx_cfg[PCF2131] }, + { .name = "pcf2127", .driver_data = (kernel_ulong_t)&pcf21xx_cfg[PCF2127] }, + { .name = "pcf2129", .driver_data = (kernel_ulong_t)&pcf21xx_cfg[PCF2129] }, + { .name = "pca2129", .driver_data = (kernel_ulong_t)&pcf21xx_cfg[PCF2129] }, + { .name = "pcf2131", .driver_data = (kernel_ulong_t)&pcf21xx_cfg[PCF2131] }, { } }; MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id); diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c index f643e0bd7351..01e209d88f5f 100644 --- a/drivers/rtc/rtc-pcf85063.c +++ b/drivers/rtc/rtc-pcf85063.c @@ -665,12 +665,12 @@ static const struct pcf85063_config config_rv8263 = { }; static const struct i2c_device_id pcf85063_ids[] = { - { "pca85073a", .driver_data = (kernel_ulong_t)&config_pcf85063a }, - { "pcf85063", .driver_data = (kernel_ulong_t)&config_pcf85063 }, - { "pcf85063tp", .driver_data = (kernel_ulong_t)&config_pcf85063tp }, - { "pcf85063a", .driver_data = (kernel_ulong_t)&config_pcf85063a }, - { "rv8263", .driver_data = (kernel_ulong_t)&config_rv8263 }, - {} + { .name = "pca85073a", .driver_data = (kernel_ulong_t)&config_pcf85063a }, + { .name = "pcf85063", .driver_data = (kernel_ulong_t)&config_pcf85063 }, + { .name = "pcf85063tp", .driver_data = (kernel_ulong_t)&config_pcf85063tp }, + { .name = "pcf85063a", .driver_data = (kernel_ulong_t)&config_pcf85063a }, + { .name = "rv8263", .driver_data = (kernel_ulong_t)&config_rv8263 }, + { } }; MODULE_DEVICE_TABLE(i2c, pcf85063_ids); diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c index 2c63c0ffd05a..e8354953836c 100644 --- a/drivers/rtc/rtc-pcf8523.c +++ b/drivers/rtc/rtc-pcf8523.c @@ -495,7 +495,7 @@ static int pcf8523_probe(struct i2c_client *client) } static const struct i2c_device_id pcf8523_id[] = { - { "pcf8523" }, + { .name = "pcf8523" }, { } }; MODULE_DEVICE_TABLE(i2c, pcf8523_id); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index b281e9489df1..81d13733b1e9 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -557,9 +557,9 @@ static int pcf8563_probe(struct i2c_client *client) } static const struct i2c_device_id pcf8563_id[] = { - { "pcf8563" }, - { "rtc8564" }, - { "pca8565" }, + { .name = "pcf8563" }, + { .name = "rtc8564" }, + { .name = "pca8565" }, { } }; MODULE_DEVICE_TABLE(i2c, pcf8563_id); diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c index 652b9dfa7566..df5e20cbc26c 100644 --- a/drivers/rtc/rtc-pcf8583.c +++ b/drivers/rtc/rtc-pcf8583.c @@ -297,7 +297,7 @@ static int pcf8583_probe(struct i2c_client *client) } static const struct i2c_device_id pcf8583_id[] = { - { "pcf8583" }, + { .name = "pcf8583" }, { } }; MODULE_DEVICE_TABLE(i2c, pcf8583_id); diff --git a/drivers/rtc/rtc-renesas-rtca3.c b/drivers/rtc/rtc-renesas-rtca3.c index cbabaa4dc96a..b3875d041de5 100644 --- a/drivers/rtc/rtc-renesas-rtca3.c +++ b/drivers/rtc/rtc-renesas-rtca3.c @@ -103,7 +103,7 @@ enum rtca3_alrm_set_step { /** * struct rtca3_ppb_per_cycle - PPB per cycle - * @ten_sec: PPB per cycle in 10 seconds adjutment mode + * @ten_sec: PPB per cycle in 10 seconds adjustment mode * @sixty_sec: PPB per cycle in 60 seconds adjustment mode */ struct rtca3_ppb_per_cycle { @@ -228,12 +228,19 @@ static void rtca3_prepare_cntalrm_regs_for_read(struct rtca3_priv *priv, bool cn } } +static u32 rtca3_decode_year(u8 mask, u16 year) +{ + u8 y = FIELD_GET(mask, year); + u32 century = bcd2bin((y == 0x99) ? 0x19 : 0x20); + + return (century * 100 + bcd2bin(y)) - 1900; +} + static int rtca3_read_time(struct device *dev, struct rtc_time *tm) { struct rtca3_priv *priv = dev_get_drvdata(dev); u8 sec, min, hour, wday, mday, month, tmp; u8 trials = 0; - u32 year100; u16 year; guard(spinlock_irqsave)(&priv->lock); @@ -274,9 +281,7 @@ static int rtca3_read_time(struct device *dev, struct rtc_time *tm) tm->tm_wday = bcd2bin(FIELD_GET(RTCA3_RWKCNT_WK, wday)); tm->tm_mday = bcd2bin(FIELD_GET(RTCA3_RDAYCNT_DAY, mday)); tm->tm_mon = bcd2bin(FIELD_GET(RTCA3_RMONCNT_MONTH, month)) - 1; - year = FIELD_GET(RTCA3_RYRCNT_YEAR, year); - year100 = bcd2bin((year == 0x99) ? 0x19 : 0x20); - tm->tm_year = (year100 * 100 + bcd2bin(year)) - 1900; + tm->tm_year = rtca3_decode_year(RTCA3_RYRCNT_YEAR, year); return 0; } @@ -354,7 +359,6 @@ static int rtca3_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) struct rtca3_priv *priv = dev_get_drvdata(dev); u8 sec, min, hour, wday, mday, month; struct rtc_time *tm = &wkalrm->time; - u32 year100; u16 year; guard(spinlock_irqsave)(&priv->lock); @@ -373,9 +377,7 @@ static int rtca3_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) tm->tm_wday = bcd2bin(FIELD_GET(RTCA3_RWKAR_DAYW, wday)); tm->tm_mday = bcd2bin(FIELD_GET(RTCA3_RDAYAR_DATE, mday)); tm->tm_mon = bcd2bin(FIELD_GET(RTCA3_RMONAR_MON, month)) - 1; - year = FIELD_GET(RTCA3_RYRAR_YR, year); - year100 = bcd2bin((year == 0x99) ? 0x19 : 0x20); - tm->tm_year = (year100 * 100 + bcd2bin(year)) - 1900; + tm->tm_year = rtca3_decode_year(RTCA3_RYRAR_YR, year); wkalrm->enabled = !!(readb(priv->base + RTCA3_RCR1) & RTCA3_RCR1_AIE); @@ -455,7 +457,7 @@ setup_failed: * specified timeout for setup. */ writeb(rcr1 & ~RTCA3_RCR1_PIE, priv->base + RTCA3_RCR1); - readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, !(tmp & ~RTCA3_RCR1_PIE), + readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, !(tmp & RTCA3_RCR1_PIE), 10, RTCA3_DEFAULT_TIMEOUT_US); atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_DONE); } @@ -634,6 +636,8 @@ static int rtca3_initial_setup(struct clk *clk, struct rtca3_priv *priv) writeb(0, priv->base + RTCA3_RADJ); ret = readb_poll_timeout(priv->base + RTCA3_RADJ, tmp, !tmp, 10, RTCA3_DEFAULT_TIMEOUT_US); + if (ret) + return ret; /* Start the RTC and enable automatic time error adjustment. */ mask = RTCA3_RCR2_START | RTCA3_RCR2_AADJE; @@ -700,7 +704,7 @@ static void rtca3_action(void *data) ret = reset_control_assert(priv->rstc); if (ret) - dev_err(dev, "Failed to de-assert reset!"); + dev_err(dev, "Failed to assert reset!"); ret = pm_runtime_put_sync(dev); if (ret < 0) diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index 936f4f05c8c7..24bd795d9d95 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -75,12 +75,12 @@ enum rtc_type { }; static const struct i2c_device_id rs5c372_id[] = { - { "r2025sd", rtc_r2025sd }, - { "r2221tl", rtc_r2221tl }, - { "rs5c372a", rtc_rs5c372a }, - { "rs5c372b", rtc_rs5c372b }, - { "rv5c386", rtc_rv5c386 }, - { "rv5c387a", rtc_rv5c387a }, + { .name = "r2025sd", .driver_data = rtc_r2025sd }, + { .name = "r2221tl", .driver_data = rtc_r2221tl }, + { .name = "rs5c372a", .driver_data = rtc_rs5c372a }, + { .name = "rs5c372b", .driver_data = rtc_rs5c372b }, + { .name = "rv5c386", .driver_data = rtc_rv5c386 }, + { .name = "rv5c387a", .driver_data = rtc_rv5c387a }, { } }; MODULE_DEVICE_TABLE(i2c, rs5c372_id); diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c index 83331d1fcab0..98953af2a24a 100644 --- a/drivers/rtc/rtc-rv3029c2.c +++ b/drivers/rtc/rtc-rv3029c2.c @@ -807,8 +807,8 @@ static int rv3029_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id rv3029_id[] = { - { "rv3029" }, - { "rv3029c2" }, + { .name = "rv3029" }, + { .name = "rv3029c2" }, { } }; MODULE_DEVICE_TABLE(i2c, rv3029_id); diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c index 2bf988a89fd7..b9b5fee16ee4 100644 --- a/drivers/rtc/rtc-rv8803.c +++ b/drivers/rtc/rtc-rv8803.c @@ -631,10 +631,10 @@ static int rv8803_suspend(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(rv8803_pm_ops, rv8803_suspend, rv8803_resume); static const struct i2c_device_id rv8803_id[] = { - { "rv8803", rv_8803 }, - { "rv8804", rx_8804 }, - { "rx8803", rx_8803 }, - { "rx8900", rx_8900 }, + { .name = "rv8803", .driver_data = rv_8803 }, + { .name = "rv8804", .driver_data = rx_8804 }, + { .name = "rx8803", .driver_data = rx_8803 }, + { .name = "rx8900", .driver_data = rx_8900 }, { } }; MODULE_DEVICE_TABLE(i2c, rv8803_id); diff --git a/drivers/rtc/rtc-rx6110.c b/drivers/rtc/rtc-rx6110.c index 07bf35ac8d79..1eacd470a27c 100644 --- a/drivers/rtc/rtc-rx6110.c +++ b/drivers/rtc/rtc-rx6110.c @@ -449,7 +449,7 @@ static const struct acpi_device_id rx6110_i2c_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rx6110_i2c_acpi_match); static const struct i2c_device_id rx6110_i2c_id[] = { - { "rx6110" }, + { .name = "rx6110" }, { } }; MODULE_DEVICE_TABLE(i2c, rx6110_i2c_id); diff --git a/drivers/rtc/rtc-rx8010.c b/drivers/rtc/rtc-rx8010.c index 171240e50f48..ce3954132336 100644 --- a/drivers/rtc/rtc-rx8010.c +++ b/drivers/rtc/rtc-rx8010.c @@ -50,7 +50,7 @@ #define RX8010_ALARM_AE BIT(7) static const struct i2c_device_id rx8010_id[] = { - { "rx8010" }, + { .name = "rx8010" }, { } }; MODULE_DEVICE_TABLE(i2c, rx8010_id); diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c index c57081f9e02b..5eeaa929f3ef 100644 --- a/drivers/rtc/rtc-rx8025.c +++ b/drivers/rtc/rtc-rx8025.c @@ -71,8 +71,8 @@ enum rx_model { }; static const struct i2c_device_id rx8025_id[] = { - { "rx8025", model_rx_8025 }, - { "rx8035", model_rx_8035 }, + { .name = "rx8025", .driver_data = model_rx_8025 }, + { .name = "rx8035", .driver_data = model_rx_8035 }, { } }; MODULE_DEVICE_TABLE(i2c, rx8025_id); diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c index 20c2dff01bae..cf4dab94c337 100644 --- a/drivers/rtc/rtc-rx8581.c +++ b/drivers/rtc/rtc-rx8581.c @@ -294,7 +294,7 @@ static int rx8581_probe(struct i2c_client *client) } static const struct i2c_device_id rx8581_id[] = { - { "rx8581" }, + { .name = "rx8581" }, { } }; MODULE_DEVICE_TABLE(i2c, rx8581_id); diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c index a4678d7c6cf6..b72eef4fb099 100644 --- a/drivers/rtc/rtc-s35390a.c +++ b/drivers/rtc/rtc-s35390a.c @@ -51,7 +51,7 @@ #define S35390A_INT2_MODE_PMIN (BIT(3) | BIT(2)) /* INT2FE | INT2ME */ static const struct i2c_device_id s35390a_id[] = { - { "s35390a" }, + { .name = "s35390a" }, { } }; MODULE_DEVICE_TABLE(i2c, s35390a_id); @@ -297,7 +297,7 @@ static int s35390a_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) else sts = S35390A_INT2_MODE_NOINTR; - /* set interupt mode*/ + /* set interrupt mode*/ err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts)); if (err < 0) return err; diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c index c6ed5a4ca8a0..aa706074ec3e 100644 --- a/drivers/rtc/rtc-s5m.c +++ b/drivers/rtc/rtc-s5m.c @@ -807,12 +807,12 @@ static int s5m_rtc_suspend(struct device *dev) static SIMPLE_DEV_PM_OPS(s5m_rtc_pm_ops, s5m_rtc_suspend, s5m_rtc_resume); static const struct platform_device_id s5m_rtc_id[] = { - { "s5m-rtc", S5M8767X }, - { "s2mpg10-rtc", S2MPG10 }, - { "s2mps13-rtc", S2MPS13X }, - { "s2mps14-rtc", S2MPS14X }, - { "s2mps15-rtc", S2MPS15X }, - { }, + { .name = "s5m-rtc", .driver_data = S5M8767X }, + { .name = "s2mpg10-rtc", .driver_data = S2MPG10 }, + { .name = "s2mps13-rtc", .driver_data = S2MPS13X }, + { .name = "s2mps14-rtc", .driver_data = S2MPS14X }, + { .name = "s2mps15-rtc", .driver_data = S2MPS15X }, + { } }; MODULE_DEVICE_TABLE(platform, s5m_rtc_id); diff --git a/drivers/rtc/rtc-sd2405al.c b/drivers/rtc/rtc-sd2405al.c index 708ea5d964de..7982a910a537 100644 --- a/drivers/rtc/rtc-sd2405al.c +++ b/drivers/rtc/rtc-sd2405al.c @@ -202,7 +202,7 @@ static int sd2405al_probe(struct i2c_client *client) } static const struct i2c_device_id sd2405al_id[] = { - { "sd2405al" }, + { .name = "sd2405al" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, sd2405al_id); diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c index 10cc1dcfc774..2c61f0e204a4 100644 --- a/drivers/rtc/rtc-sd3078.c +++ b/drivers/rtc/rtc-sd3078.c @@ -186,7 +186,7 @@ static int sd3078_probe(struct i2c_client *client) } static const struct i2c_device_id sd3078_id[] = { - { "sd3078" }, + { .name = "sd3078" }, { } }; MODULE_DEVICE_TABLE(i2c, sd3078_id); diff --git a/drivers/rtc/rtc-tps6594.c b/drivers/rtc/rtc-tps6594.c index 7c6246e3f029..2cebd54c2dbf 100644 --- a/drivers/rtc/rtc-tps6594.c +++ b/drivers/rtc/rtc-tps6594.c @@ -485,8 +485,8 @@ static int tps6594_rtc_suspend(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(tps6594_rtc_pm_ops, tps6594_rtc_suspend, tps6594_rtc_resume); static const struct platform_device_id tps6594_rtc_id_table[] = { - { "tps6594-rtc", }, - {} + { .name = "tps6594-rtc" }, + { } }; MODULE_DEVICE_TABLE(platform, tps6594_rtc_id_table); diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c index b8a0fccef14e..bb1a5c86f621 100644 --- a/drivers/rtc/rtc-x1205.c +++ b/drivers/rtc/rtc-x1205.c @@ -663,7 +663,7 @@ static void x1205_remove(struct i2c_client *client) } static const struct i2c_device_id x1205_id[] = { - { "x1205" }, + { .name = "x1205" }, { } }; MODULE_DEVICE_TABLE(i2c, x1205_id); diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index f38a0f7d0f27..0490777fa406 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -1372,34 +1372,6 @@ int sdw_slave_get_current_bank(struct sdw_slave *slave) } EXPORT_SYMBOL_GPL(sdw_slave_get_current_bank); -/** - * sdw_slave_wait_for_init - Wait for device initialisation - * @slave: Pointer to the SoundWire peripheral. - * @timeout_ms: Timeout in milliseconds. - * - * Wait for a peripheral device to enumerate and be initialised by the - * SoundWire core. - * - * Return: Zero on success, and a negative error code on failure. - */ -int sdw_slave_wait_for_init(struct sdw_slave *slave, int timeout_ms) -{ - unsigned long time; - - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(timeout_ms)); - if (!time) { - dev_err(&slave->dev, "Initialization not complete\n"); - sdw_show_ping_status(slave->bus, true); - return -ETIMEDOUT; - } - - slave->unattach_request = 0; - - return 0; -} -EXPORT_SYMBOL_GPL(sdw_slave_wait_for_init); - static int sdw_slave_set_frequency(struct sdw_slave *slave) { int scale_index; diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index 4fc864d38cff..603e81a92c57 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -438,6 +438,7 @@ static const struct of_device_id dw_spi_mmio_of_match[] = { { .compatible = "amazon,alpine-dw-apb-ssi", .data = dw_spi_alpine_init}, { .compatible = "renesas,rzn1-spi", .data = dw_spi_pssi_init}, { .compatible = "snps,dwc-ssi-1.01a", .data = dw_spi_hssi_init}, + { .compatible = "snps,dwc-ssi-2.00a", .data = dw_spi_hssi_init}, { .compatible = "intel,keembay-ssi", .data = dw_spi_hssi_no_dma_init}, { .compatible = "intel,mountevans-imc-ssi", diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h index 9cc79c566a70..2f2debc64e73 100644 --- a/drivers/spi/spi-dw.h +++ b/drivers/spi/spi-dw.h @@ -282,6 +282,7 @@ static inline void dw_spi_shutdown_chip(struct dw_spi *dws) { dw_spi_enable_chip(dws, 0); dw_spi_set_clk(dws, 0); + dws->current_freq = 0; } extern void dw_spi_set_cs(struct spi_device *spi, bool enable); diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index ae9912905c67..79a6c1a60b0a 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -2152,7 +2152,8 @@ static int spi_imx_transfer_one(struct spi_controller *controller, if (spi_imx->usedma) { ret = spi_imx_dma_transfer(spi_imx, transfer); if (transfer->error & SPI_TRANS_FAIL_NO_START) { - spi_imx->usedma = false; + controller->fallback = true; + spi_imx_setupxfer(spi, transfer); if (spi_imx->target_mode) return spi_imx_pio_transfer_target(spi, transfer); else diff --git a/drivers/spi/spi-rpc-if.c b/drivers/spi/spi-rpc-if.c index 1ef7bd91b3b3..b63c7856e758 100644 --- a/drivers/spi/spi-rpc-if.c +++ b/drivers/spi/spi-rpc-if.c @@ -206,8 +206,12 @@ static int rpcif_spi_suspend(struct device *dev) static int rpcif_spi_resume(struct device *dev) { struct spi_controller *ctlr = dev_get_drvdata(dev); + struct rpcif *rpc = spi_controller_get_devdata(ctlr); + int ret; - rpcif_hw_init(dev, false); + ret = rpcif_hw_init(rpc->dev, false); + if (ret) + return ret; return spi_controller_resume(ctlr); } diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index cc20fd11f03f..86fce9a571da 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -656,6 +656,8 @@ static int uniphier_spi_probe(struct platform_device *pdev) priv->host = host; priv->is_save_param = false; + init_completion(&priv->xfer_done); + priv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); @@ -679,8 +681,6 @@ static int uniphier_spi_probe(struct platform_device *pdev) return ret; } - init_completion(&priv->xfer_done); - clk_rate = clk_get_rate(priv->clk); host->max_speed_hz = DIV_ROUND_UP(clk_rate, SSI_MIN_CLK_DIVIDER); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 76e3563c523f..f897789a44d1 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -355,12 +355,16 @@ EXPORT_SYMBOL_GPL(spi_get_device_id); const void *spi_get_device_match_data(const struct spi_device *sdev) { const void *match; + const struct spi_device_id *id; match = device_get_match_data(&sdev->dev); if (match) return match; - return (const void *)spi_get_device_id(sdev)->driver_data; + id = spi_get_device_id(sdev); + if (!id) + return NULL; + return (const void *)id->driver_data; } EXPORT_SYMBOL_GPL(spi_get_device_match_data); @@ -2975,12 +2979,12 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr, INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, acpi_spi_add_resource, &lookup); - acpi_dev_free_resource_list(&resource_list); - if (ret < 0) /* Found SPI in _CRS but it points to another controller */ return ERR_PTR(ret); + acpi_dev_free_resource_list(&resource_list); + if (!lookup.max_speed_hz && ACPI_SUCCESS(acpi_get_parent(adev->handle, &parent_handle)) && device_match_acpi_handle(lookup.ctlr->dev.parent, parent_handle)) { diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 69f8d456324a..2e2cb4774103 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -28,6 +28,7 @@ #define PMIC_ARB_VERSION_V5_MIN 0x50000000 #define PMIC_ARB_VERSION_V7_MIN 0x70000000 #define PMIC_ARB_VERSION_V8_MIN 0x80000000 +#define PMIC_ARB_VERSION_V8P5_MIN 0x80050000 #define PMIC_ARB_INT_EN 0x0004 #define PMIC_ARB_FEATURES 0x0004 @@ -62,14 +63,6 @@ /* Ownership Table */ #define SPMI_OWNERSHIP_PERIPH2OWNER(X) ((X) & 0x7) -/* Channel Status fields */ -enum pmic_arb_chnl_status { - PMIC_ARB_STATUS_DONE = BIT(0), - PMIC_ARB_STATUS_FAILURE = BIT(1), - PMIC_ARB_STATUS_DENIED = BIT(2), - PMIC_ARB_STATUS_DROPPED = BIT(3), -}; - /* Command register fields */ #define PMIC_ARB_CMD_MAX_BYTE_COUNT 8 @@ -239,6 +232,7 @@ struct spmi_pmic_arb { * on v2 address of SPMI_PIC_IRQ_CLEARn. * @apid_map_offset: offset of PMIC_ARB_REG_CHNLn * @apid_owner: on v2 and later address of SPMI_PERIPHn_2OWNER_TABLE_REG + * @check_chnl_status: checks channel status and returns error code if any */ struct pmic_arb_ver_ops { const char *ver_str; @@ -261,6 +255,8 @@ struct pmic_arb_ver_ops { void __iomem *(*irq_clear)(struct spmi_pmic_arb_bus *bus, u16 n); u32 (*apid_map_offset)(u16 n); void __iomem *(*apid_owner)(struct spmi_pmic_arb_bus *bus, u16 n); + int (*check_chnl_status)(struct spmi_controller *ctrl, u32 status, + u8 sid, u16 addr, u32 offset); }; static inline void pmic_arb_base_write(struct spmi_pmic_arb *pmic_arb, @@ -306,6 +302,84 @@ static void pmic_arb_write_data(struct spmi_pmic_arb *pmic_arb, const u8 *buf, __raw_writel(data, pmic_arb->wr_base + reg); } +static int pmic_arb_check_chnl_status_v1(struct spmi_controller *ctrl, + u32 status, u8 sid, u16 addr, + u32 offset) +{ + /* Check if DONE bit is set */ + if (!(status & BIT(0))) + return -EAGAIN; + + if (status & BIT(1)) { + dev_err(&ctrl->dev, "%s: %#x %#x: transaction failed (%#x) reg: 0x%x\n", + __func__, sid, addr, status, offset); + WARN_ON(1); + return -EIO; + } + + if (status & BIT(2)) { + dev_err(&ctrl->dev, "%s: %#x %#x: transaction denied (%#x)\n", + __func__, sid, addr, status); + return -EPERM; + } + + if (status & BIT(3)) { + dev_err(&ctrl->dev, "%s: %#x %#x: transaction dropped (%#x)\n", + __func__, sid, addr, status); + return -EIO; + } + + return 0; +} + +static int pmic_arb_check_chnl_status_v8p5(struct spmi_controller *ctrl, + u32 status, u8 sid, u16 addr, + u32 offset) +{ + /* Check if DONE bit is set */ + if (!(status & BIT(0))) + return -EAGAIN; + + if (status & BIT(1)) { + dev_err(&ctrl->dev, "%s: %#x %#x: transaction failed (%#x) reg: 0x%x\n", + __func__, sid, addr, status, offset); + WARN_ON(1); + return -EIO; + } + + if (status & BIT(2)) { + dev_err(&ctrl->dev, "%s: %#x %#x: CRC error (%#x)\n", + __func__, sid, addr, status); + return -EIO; + } + + if (status & BIT(3)) { + dev_err(&ctrl->dev, "%s: %#x %#x: parity error (%#x)\n", + __func__, sid, addr, status); + return -EIO; + } + + if (status & BIT(4)) { + dev_err(&ctrl->dev, "%s: %#x %#x: NACK error (%#x)\n", + __func__, sid, addr, status); + return -EIO; + } + + if (status & BIT(5)) { + dev_err(&ctrl->dev, "%s: %#x %#x: transaction denied (%#x)\n", + __func__, sid, addr, status); + return -EPERM; + } + + if (status & BIT(6)) { + dev_err(&ctrl->dev, "%s: %#x %#x: transaction dropped (%#x)\n", + __func__, sid, addr, status); + return -EIO; + } + + return 0; +} + static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, void __iomem *base, u8 sid, u16 addr, enum pmic_arb_channel ch_type) @@ -327,28 +401,10 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, while (timeout--) { status = readl_relaxed(base + offset); - if (status & PMIC_ARB_STATUS_DONE) { - if (status & PMIC_ARB_STATUS_DENIED) { - dev_err(&ctrl->dev, "%s: %#x %#x: transaction denied (%#x)\n", - __func__, sid, addr, status); - return -EPERM; - } + rc = pmic_arb->ver_ops->check_chnl_status(ctrl, status, sid, addr, offset); + if (rc != -EAGAIN) + return rc; - if (status & PMIC_ARB_STATUS_FAILURE) { - dev_err(&ctrl->dev, "%s: %#x %#x: transaction failed (%#x) reg: 0x%x\n", - __func__, sid, addr, status, offset); - WARN_ON(1); - return -EIO; - } - - if (status & PMIC_ARB_STATUS_DROPPED) { - dev_err(&ctrl->dev, "%s: %#x %#x: transaction dropped (%#x)\n", - __func__, sid, addr, status); - return -EIO; - } - - return 0; - } udelay(1); } @@ -1768,6 +1824,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v1 = { .irq_clear = pmic_arb_irq_clear_v1, .apid_map_offset = pmic_arb_apid_map_offset_v2, .apid_owner = pmic_arb_apid_owner_v2, + .check_chnl_status = pmic_arb_check_chnl_status_v1, }; static const struct pmic_arb_ver_ops pmic_arb_v2 = { @@ -1784,6 +1841,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v2 = { .irq_clear = pmic_arb_irq_clear_v2, .apid_map_offset = pmic_arb_apid_map_offset_v2, .apid_owner = pmic_arb_apid_owner_v2, + .check_chnl_status = pmic_arb_check_chnl_status_v1, }; static const struct pmic_arb_ver_ops pmic_arb_v3 = { @@ -1800,6 +1858,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v3 = { .irq_clear = pmic_arb_irq_clear_v2, .apid_map_offset = pmic_arb_apid_map_offset_v2, .apid_owner = pmic_arb_apid_owner_v2, + .check_chnl_status = pmic_arb_check_chnl_status_v1, }; static const struct pmic_arb_ver_ops pmic_arb_v5 = { @@ -1816,6 +1875,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v5 = { .irq_clear = pmic_arb_irq_clear_v5, .apid_map_offset = pmic_arb_apid_map_offset_v5, .apid_owner = pmic_arb_apid_owner_v2, + .check_chnl_status = pmic_arb_check_chnl_status_v1, }; static const struct pmic_arb_ver_ops pmic_arb_v7 = { @@ -1832,6 +1892,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v7 = { .irq_clear = pmic_arb_irq_clear_v7, .apid_map_offset = pmic_arb_apid_map_offset_v7, .apid_owner = pmic_arb_apid_owner_v7, + .check_chnl_status = pmic_arb_check_chnl_status_v1, }; static const struct pmic_arb_ver_ops pmic_arb_v8 = { @@ -1849,6 +1910,25 @@ static const struct pmic_arb_ver_ops pmic_arb_v8 = { .irq_clear = pmic_arb_irq_clear_v8, .apid_map_offset = pmic_arb_apid_map_offset_v8, .apid_owner = pmic_arb_apid_owner_v8, + .check_chnl_status = pmic_arb_check_chnl_status_v1, +}; + +static const struct pmic_arb_ver_ops pmic_arb_v8p5 = { + .ver_str = "v8.5", + .get_core_resources = pmic_arb_get_core_resources_v8, + .get_bus_resources = pmic_arb_get_bus_resources_v8, + .init_apid = pmic_arb_init_apid_v8, + .ppid_to_apid = pmic_arb_ppid_to_apid_v5, + .non_data_cmd = pmic_arb_non_data_cmd_v2, + .offset = pmic_arb_offset_v8, + .fmt_cmd = pmic_arb_fmt_cmd_v2, + .owner_acc_status = pmic_arb_owner_acc_status_v7, + .acc_enable = pmic_arb_acc_enable_v8, + .irq_status = pmic_arb_irq_status_v8, + .irq_clear = pmic_arb_irq_clear_v8, + .apid_map_offset = pmic_arb_apid_map_offset_v8, + .apid_owner = pmic_arb_apid_owner_v8, + .check_chnl_status = pmic_arb_check_chnl_status_v8p5, }; static const struct irq_domain_ops pmic_arb_irq_domain_ops = { @@ -2030,8 +2110,10 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) pmic_arb->ver_ops = &pmic_arb_v5; else if (hw_ver < PMIC_ARB_VERSION_V8_MIN) pmic_arb->ver_ops = &pmic_arb_v7; - else + else if (hw_ver < PMIC_ARB_VERSION_V8P5_MIN) pmic_arb->ver_ops = &pmic_arb_v8; + else + pmic_arb->ver_ops = &pmic_arb_v8p5; err = pmic_arb->ver_ops->get_core_resources(pdev, core); if (err) diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c index e889b129f3ac..57b7c0cb4240 100644 --- a/drivers/spmi/spmi.c +++ b/drivers/spmi/spmi.c @@ -450,7 +450,7 @@ struct spmi_controller *spmi_controller_alloc(struct device *parent, if (WARN_ON(!parent)) return ERR_PTR(-EINVAL); - ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL); + ctrl = kzalloc_flex(*ctrl, priv, size); if (!ctrl) return ERR_PTR(-ENOMEM); @@ -459,7 +459,7 @@ struct spmi_controller *spmi_controller_alloc(struct device *parent, ctrl->dev.bus = &spmi_bus_type; ctrl->dev.parent = parent; ctrl->dev.of_node = parent->of_node; - spmi_controller_set_drvdata(ctrl, &ctrl[1]); + spmi_controller_set_drvdata(ctrl, ctrl->priv); id = ida_alloc(&ctrl_ida, GFP_KERNEL); if (id < 0) { diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c index 44fa4dd15dd1..45a8ef4a608b 100644 --- a/drivers/thermal/intel/therm_throt.c +++ b/drivers/thermal/intel/therm_throt.c @@ -529,8 +529,13 @@ static int thermal_throttle_online(unsigned int cpu) { struct thermal_state *state = &per_cpu(thermal_state, cpu); struct device *dev = get_cpu_device(cpu); + int err; u32 l; + err = thermal_throttle_add_dev(dev, cpu); + if (err) + return err; + state->package_throttle.level = PACKAGE_LEVEL; state->core_throttle.level = CORE_LEVEL; @@ -548,7 +553,7 @@ static int thermal_throttle_online(unsigned int cpu) l = apic_read(APIC_LVTTHMR); apic_write(APIC_LVTTHMR, l & ~APIC_LVT_MASKED); - return thermal_throttle_add_dev(dev, cpu); + return err; } static int thermal_throttle_offline(unsigned int cpu) diff --git a/drivers/thermal/testing/command.c b/drivers/thermal/testing/command.c index fbf7ab9729b5..90e06c593327 100644 --- a/drivers/thermal/testing/command.c +++ b/drivers/thermal/testing/command.c @@ -86,7 +86,10 @@ #include "thermal_testing.h" -struct dentry *d_testing; +struct workqueue_struct *tt_wq __ro_after_init; + +struct dentry *d_testing __ro_after_init; +static struct dentry *d_command __ro_after_init; #define TT_COMMAND_SIZE 16 @@ -203,17 +206,42 @@ static const struct file_operations tt_command_fops = { static int __init thermal_testing_init(void) { + int error; + + tt_wq = alloc_workqueue("thermal_testing", WQ_UNBOUND, 0); + if (!tt_wq) + return -ENOMEM; + d_testing = debugfs_create_dir("thermal-testing", NULL); - if (!IS_ERR(d_testing)) - debugfs_create_file("command", 0200, d_testing, NULL, - &tt_command_fops); + if (IS_ERR(d_testing)) { + error = PTR_ERR(d_testing); + goto destroy_wq; + } + + d_command = debugfs_create_file("command", 0200, d_testing, NULL, &tt_command_fops); + if (IS_ERR(d_command)) { + error = PTR_ERR(d_command); + goto remove_d_testing; + } return 0; + +remove_d_testing: + debugfs_remove(d_testing); +destroy_wq: + destroy_workqueue(tt_wq); + return error; } module_init(thermal_testing_init); static void __exit thermal_testing_exit(void) { + /* First, prevent new commands from being entered. */ + debugfs_remove(d_command); + /* Flush commands in progress (if any). */ + flush_workqueue(tt_wq); + destroy_workqueue(tt_wq); + /* Remove the directory structure and clean up. */ debugfs_remove(d_testing); tt_zone_cleanup(); } diff --git a/drivers/thermal/testing/thermal_testing.h b/drivers/thermal/testing/thermal_testing.h index c790a32aae4e..5880c9a63dba 100644 --- a/drivers/thermal/testing/thermal_testing.h +++ b/drivers/thermal/testing/thermal_testing.h @@ -1,4 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/workqueue.h> + +extern struct workqueue_struct *tt_wq; + +static inline void tt_queue_work(struct work_struct *work) +{ + queue_work(tt_wq, work); +} extern struct dentry *d_testing; diff --git a/drivers/thermal/testing/zone.c b/drivers/thermal/testing/zone.c index f7f9ca2f1f2c..6db26276dd41 100644 --- a/drivers/thermal/testing/zone.c +++ b/drivers/thermal/testing/zone.c @@ -13,7 +13,6 @@ #include <linux/idr.h> #include <linux/list.h> #include <linux/thermal.h> -#include <linux/workqueue.h> #include "thermal_testing.h" @@ -207,7 +206,7 @@ int tt_add_tz(void) INIT_WORK(&tt_work->work, tt_add_tz_work_fn); tt_work->tt_zone = no_free_ptr(tt_zone); - schedule_work(&(no_free_ptr(tt_work)->work)); + tt_queue_work(&(no_free_ptr(tt_work)->work)); return 0; } @@ -269,7 +268,7 @@ int tt_del_tz(const char *arg) INIT_WORK(&tt_work->work, tt_del_tz_work_fn); tt_work->tt_zone = tt_zone; - schedule_work(&(no_free_ptr(tt_work)->work)); + tt_queue_work(&(no_free_ptr(tt_work)->work)); return 0; } @@ -358,7 +357,7 @@ int tt_zone_add_trip(const char *arg) INIT_WORK(&tt_work->work, tt_zone_add_trip_work_fn); tt_work->tt_zone = no_free_ptr(tt_zone); tt_work->tt_trip = no_free_ptr(tt_trip); - schedule_work(&(no_free_ptr(tt_work)->work)); + tt_queue_work(&(no_free_ptr(tt_work)->work)); return 0; } diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 9077d3b99357..9f5c4c101581 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -1273,6 +1273,7 @@ static void fbcon_deinit(struct vc_data *vc) int idx; fbcon_free_font(p); + p->mode = NULL; idx = con2fb_map[vc->vc_num]; if (idx == -1) @@ -1443,14 +1444,14 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, p = &fb_display[unit]; - if (var_to_display(p, var, info)) - return; - vc = vc_cons[unit].d; if (!vc) return; + if (var_to_display(p, var, info)) + return; + default_mode = vc->vc_display_fg; svc = *default_mode; t = &fb_display[svc->vc_num]; @@ -2405,6 +2406,7 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, int charcount, int resize, ret, old_width, old_height, old_charcount; font_data_t *old_fontdata = p->fontdata; const u8 *old_data = vc->vc_font.data; + unsigned short old_hi_font_mask = vc->vc_hi_font_mask; font_data_get(data); @@ -2451,6 +2453,12 @@ err_out: vc->vc_font.height = old_height; vc->vc_font.charcount = old_charcount; + /* Restore the hi_font state and screen buffer */ + if (old_hi_font_mask && !vc->vc_hi_font_mask) + set_vc_hi_font(vc, true); + else if (!old_hi_font_mask && vc->vc_hi_font_mask) + set_vc_hi_font(vc, false); + font_data_put(data); return ret; diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index e5221653ec2b..2f1c56e5a7a2 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -767,6 +767,18 @@ int fb_new_modelist(struct fb_info *info) if (list_empty(&info->modelist)) return 1; + /* + * The new modelist may not contain the current mode (info->var), and + * fbcon_new_modelist() below only re-points consoles mapped to this + * framebuffer. Add the current mode here so info->var keeps a match + * even when fbcon is unbound. + */ + if (!fb_match_mode(&info->var, &info->modelist)) { + fb_var_to_videomode(&mode, &info->var); + if (fb_add_videomode(&mode, &info->modelist)) + return 1; + } + fbcon_new_modelist(info); return 0; diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c index d9743ef35355..ea196603c7a8 100644 --- a/drivers/video/fbdev/core/fbsysfs.c +++ b/drivers/video/fbdev/core/fbsysfs.c @@ -10,6 +10,7 @@ #include <linux/major.h> #include "fb_internal.h" +#include "fbcon.h" static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var) { @@ -108,8 +109,15 @@ static ssize_t store_modes(struct device *device, if (fb_new_modelist(fb_info)) { fb_destroy_modelist(&fb_info->modelist); list_splice(&old_list, &fb_info->modelist); - } else + } else { + /* + * fb_display[i].mode and fb_info->mode both point into the old + * list. Clear them before it is freed. + */ + fbcon_delete_modelist(&old_list); + fb_info->mode = NULL; fb_destroy_modelist(&old_list); + } unlock_fb_info(fb_info); console_unlock(); diff --git a/drivers/video/fbdev/goldfishfb.c b/drivers/video/fbdev/goldfishfb.c index c9871281bc1d..e5d79886ea66 100644 --- a/drivers/video/fbdev/goldfishfb.c +++ b/drivers/video/fbdev/goldfishfb.c @@ -138,10 +138,12 @@ static int goldfish_fb_pan_display(struct fb_var_screeninfo *var, writel(fb->fb.fix.smem_start + fb->fb.var.xres * 2 * var->yoffset, fb->reg_base + FB_SET_BASE); spin_unlock_irqrestore(&fb->lock, irq_flags); - wait_event_timeout(fb->wait, - fb->base_update_count != base_update_count, HZ / 15); - if (fb->base_update_count == base_update_count) + if (!wait_event_timeout(fb->wait, + fb->base_update_count != base_update_count, + HZ / 15)) { pr_err("%s: timeout waiting for base update\n", __func__); + return -ETIMEDOUT; + } return 0; } @@ -251,7 +253,9 @@ static int goldfish_fb_probe(struct platform_device *pdev) goto err_request_irq_failed; writel(FB_INT_BASE_UPDATE_DONE, fb->reg_base + FB_INT_ENABLE); - goldfish_fb_pan_display(&fb->fb.var, &fb->fb); /* updates base */ + ret = goldfish_fb_pan_display(&fb->fb.var, &fb->fb); /* updates base */ + if (ret) + goto err_pan_display_failed; ret = register_framebuffer(&fb->fb); if (ret) @@ -259,6 +263,7 @@ static int goldfish_fb_probe(struct platform_device *pdev) return 0; err_register_framebuffer_failed: +err_pan_display_failed: free_irq(fb->irq, fb); err_request_irq_failed: err_fb_set_var_failed: diff --git a/drivers/video/fbdev/pm2fb.c b/drivers/video/fbdev/pm2fb.c index 412ff249b5c7..058075a34c5d 100644 --- a/drivers/video/fbdev/pm2fb.c +++ b/drivers/video/fbdev/pm2fb.c @@ -1711,6 +1711,7 @@ static int pm2fb_probe(struct pci_dev *pdev, const struct pci_device_id *id) err_exit_both: kfree(info->pixmap.addr); err_exit_pixmap: + arch_phys_wc_del(default_par->wc_cookie); iounmap(info->screen_base); release_mem_region(pm2fb_fix.smem_start, pm2fb_fix.smem_len); err_exit_mmio: diff --git a/drivers/video/fbdev/vga16fb.c b/drivers/video/fbdev/vga16fb.c index 22085d3668e8..cdd6b8de0ceb 100644 --- a/drivers/video/fbdev/vga16fb.c +++ b/drivers/video/fbdev/vga16fb.c @@ -1421,8 +1421,8 @@ static void vga16fb_remove(struct platform_device *dev) } static const struct platform_device_id vga16fb_driver_id_table[] = { - {"ega-framebuffer", 0}, - {"vga-framebuffer", 0}, + { .name = "ega-framebuffer" }, + { .name = "vga-framebuffer" }, { } }; MODULE_DEVICE_TABLE(platform, vga16fb_driver_id_table); diff --git a/drivers/video/fbdev/via/via-core.c b/drivers/video/fbdev/via/via-core.c index a8d4a3e2c65e..1632a669941a 100644 --- a/drivers/video/fbdev/via/via-core.c +++ b/drivers/video/fbdev/via/via-core.c @@ -234,6 +234,7 @@ int viafb_dma_copy_out_sg(unsigned int offset, struct scatterlist *sg, int nsg) dma_addr_t descr_handle; unsigned long flags; int i; + int ret = 0; struct scatterlist *sgentry; dma_addr_t nextdesc; @@ -290,8 +291,10 @@ int viafb_dma_copy_out_sg(unsigned int offset, struct scatterlist *sg, int nsg) */ wait_for_completion_timeout(&viafb_dma_completion, 1); msleep(1); - if ((viafb_mmio_read(VDMA_CSR0)&VDMA_C_DONE) == 0) + if ((viafb_mmio_read(VDMA_CSR0) & VDMA_C_DONE) == 0) { printk(KERN_ERR "VIA DMA timeout!\n"); + ret = -ETIMEDOUT; + } /* * Clean up and we're done. */ @@ -301,7 +304,7 @@ int viafb_dma_copy_out_sg(unsigned int offset, struct scatterlist *sg, int nsg) dma_free_coherent(&global_dev.pdev->dev, nsg*sizeof(struct viafb_vx855_dma_descr), descrpages, descr_handle); - return 0; + return ret; } EXPORT_SYMBOL_GPL(viafb_dma_copy_out_sg); #endif /* CONFIG_VIDEO_VIA_CAMERA */ diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index 0a86f672cc09..61bd6d92ff25 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -2561,7 +2561,8 @@ int ceph_pool_perm_check(struct inode *inode, int need) struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_string *pool_ns; s64 pool; - int ret, flags; + int ret; + unsigned long flags; /* Only need to do this for regular files */ if (!S_ISREG(inode->i_mode)) @@ -2603,20 +2604,19 @@ check: if (ret < 0) return ret; - flags = CEPH_I_POOL_PERM; - if (ret & POOL_READ) - flags |= CEPH_I_POOL_RD; - if (ret & POOL_WRITE) - flags |= CEPH_I_POOL_WR; - spin_lock(&ci->i_ceph_lock); if (pool == ci->i_layout.pool_id && pool_ns == rcu_dereference_raw(ci->i_layout.pool_ns)) { - ci->i_ceph_flags |= flags; - } else { + set_bit(CEPH_I_POOL_PERM_BIT, &ci->i_ceph_flags); + if (ret & POOL_READ) + set_bit(CEPH_I_POOL_RD_BIT, &ci->i_ceph_flags); + if (ret & POOL_WRITE) + set_bit(CEPH_I_POOL_WR_BIT, &ci->i_ceph_flags); + } else { pool = ci->i_layout.pool_id; - flags = ci->i_ceph_flags; } + /* Re-read flags under the lock so check: sees the updated bits. */ + flags = ci->i_ceph_flags; spin_unlock(&ci->i_ceph_lock); goto check; } diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index d51454e995a8..4b37d9ffdf7f 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -549,7 +549,7 @@ static void __cap_delay_requeue_front(struct ceph_mds_client *mdsc, doutc(mdsc->fsc->client, "%p %llx.%llx\n", inode, ceph_vinop(inode)); spin_lock(&mdsc->cap_delay_lock); - ci->i_ceph_flags |= CEPH_I_FLUSH; + set_bit(CEPH_I_FLUSH_BIT, &ci->i_ceph_flags); if (!list_empty(&ci->i_cap_delay_list)) list_del_init(&ci->i_cap_delay_list); list_add(&ci->i_cap_delay_list, &mdsc->cap_delay_list); @@ -1409,7 +1409,7 @@ static void __prep_cap(struct cap_msg_args *arg, struct ceph_cap *cap, ceph_cap_string(revoking)); BUG_ON((retain & CEPH_CAP_PIN) == 0); - ci->i_ceph_flags &= ~CEPH_I_FLUSH; + clear_bit(CEPH_I_FLUSH_BIT, &ci->i_ceph_flags); cap->issued &= retain; /* drop bits we don't want */ /* @@ -1648,6 +1648,7 @@ static void __ceph_flush_snaps(struct ceph_inode_info *ci, spin_lock(&mdsc->cap_dirty_lock); capsnap->cap_flush.tid = ++mdsc->last_cap_flush_tid; + capsnap->cap_flush.ci = ci; list_add_tail(&capsnap->cap_flush.g_list, &mdsc->cap_flush_list); if (oldest_flush_tid == 0) @@ -1666,7 +1667,7 @@ static void __ceph_flush_snaps(struct ceph_inode_info *ci, last_tid = capsnap->cap_flush.tid; } - ci->i_ceph_flags &= ~CEPH_I_FLUSH_SNAPS; + clear_bit(CEPH_I_FLUSH_SNAPS_BIT, &ci->i_ceph_flags); while (first_tid <= last_tid) { struct ceph_cap *cap = ci->i_auth_cap; @@ -1846,6 +1847,7 @@ struct ceph_cap_flush *ceph_alloc_cap_flush(void) return NULL; cf->is_capsnap = false; + cf->ci = NULL; return cf; } @@ -1931,6 +1933,7 @@ static u64 __mark_caps_flushing(struct inode *inode, doutc(cl, "%p %llx.%llx now !dirty\n", inode, ceph_vinop(inode)); swap(cf, ci->i_prealloc_cap_flush); + cf->ci = ci; cf->caps = flushing; cf->wake = wake; @@ -2026,7 +2029,7 @@ void ceph_check_caps(struct ceph_inode_info *ci, int flags) spin_lock(&ci->i_ceph_lock); if (ci->i_ceph_flags & CEPH_I_ASYNC_CREATE) { - ci->i_ceph_flags |= CEPH_I_ASYNC_CHECK_CAPS; + set_bit(CEPH_I_ASYNC_CHECK_CAPS_BIT, &ci->i_ceph_flags); /* Don't send messages until we get async create reply */ spin_unlock(&ci->i_ceph_lock); @@ -2577,7 +2580,7 @@ static void __kick_flushing_caps(struct ceph_mds_client *mdsc, if (ci->i_ceph_flags & CEPH_I_ASYNC_CREATE) return; - ci->i_ceph_flags &= ~CEPH_I_KICK_FLUSH; + clear_bit(CEPH_I_KICK_FLUSH_BIT, &ci->i_ceph_flags); list_for_each_entry_reverse(cf, &ci->i_cap_flush_list, i_list) { if (cf->is_capsnap) { @@ -2686,7 +2689,7 @@ void ceph_early_kick_flushing_caps(struct ceph_mds_client *mdsc, __kick_flushing_caps(mdsc, session, ci, oldest_flush_tid); } else { - ci->i_ceph_flags |= CEPH_I_KICK_FLUSH; + set_bit(CEPH_I_KICK_FLUSH_BIT, &ci->i_ceph_flags); } spin_unlock(&ci->i_ceph_lock); @@ -2829,7 +2832,7 @@ again: spin_lock(&ci->i_ceph_lock); if ((flags & CHECK_FILELOCK) && - (ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK)) { + test_bit(CEPH_I_ERROR_FILELOCK_BIT, &ci->i_ceph_flags)) { doutc(cl, "%p %llx.%llx error filelock\n", inode, ceph_vinop(inode)); ret = -EIO; @@ -3207,7 +3210,7 @@ static int ceph_try_drop_cap_snap(struct ceph_inode_info *ci, BUG_ON(capsnap->cap_flush.tid > 0); ceph_put_snap_context(capsnap->context); if (!list_is_last(&capsnap->ci_item, &ci->i_cap_snaps)) - ci->i_ceph_flags |= CEPH_I_FLUSH_SNAPS; + set_bit(CEPH_I_FLUSH_SNAPS_BIT, &ci->i_ceph_flags); list_del(&capsnap->ci_item); ceph_put_cap_snap(capsnap); @@ -3396,7 +3399,7 @@ void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr, if (ceph_try_drop_cap_snap(ci, capsnap)) { put++; } else { - ci->i_ceph_flags |= CEPH_I_FLUSH_SNAPS; + set_bit(CEPH_I_FLUSH_SNAPS_BIT, &ci->i_ceph_flags); flush_snaps = true; } } @@ -3648,7 +3651,7 @@ static void handle_cap_grant(struct inode *inode, if (ci->i_layout.pool_id != old_pool || extra_info->pool_ns != old_ns) - ci->i_ceph_flags &= ~CEPH_I_POOL_PERM; + clear_bit(CEPH_I_POOL_PERM_BIT, &ci->i_ceph_flags); extra_info->pool_ns = old_ns; @@ -3826,6 +3829,13 @@ static void handle_cap_flush_ack(struct inode *inode, u64 flush_tid, bool wake_ci = false; bool wake_mdsc = false; + /* + * Flush tids are monotonically increasing and acks arrive in + * order under i_ceph_lock, so this is always the latest tid. + * Diagnostic readers use READ_ONCE() without holding the lock. + */ + WRITE_ONCE(ci->i_last_cap_flush_ack, flush_tid); + list_for_each_entry_safe(cf, tmp_cf, &ci->i_cap_flush_list, i_list) { /* Is this the one that was flushed? */ if (cf->tid == flush_tid) @@ -4815,7 +4825,7 @@ int ceph_drop_caps_for_unlink(struct inode *inode) doutc(mdsc->fsc->client, "%p %llx.%llx\n", inode, ceph_vinop(inode)); spin_lock(&mdsc->cap_delay_lock); - ci->i_ceph_flags |= CEPH_I_FLUSH; + set_bit(CEPH_I_FLUSH_BIT, &ci->i_ceph_flags); if (!list_empty(&ci->i_cap_delay_list)) list_del_init(&ci->i_cap_delay_list); list_add_tail(&ci->i_cap_delay_list, @@ -5080,7 +5090,7 @@ int ceph_purge_inode_cap(struct inode *inode, struct ceph_cap *cap, bool *invali if (atomic_read(&ci->i_filelock_ref) > 0) { /* make further file lock syscall return -EIO */ - ci->i_ceph_flags |= CEPH_I_ERROR_FILELOCK; + set_bit(CEPH_I_ERROR_FILELOCK_BIT, &ci->i_ceph_flags); pr_warn_ratelimited_client(cl, " dropping file locks for %p %llx.%llx\n", inode, ceph_vinop(inode)); diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index e2463f93cf6b..18eb5da03411 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -9,6 +9,7 @@ #include <linux/seq_file.h> #include <linux/math64.h> #include <linux/ktime.h> +#include <linux/uaccess.h> #include <linux/atomic.h> #include <linux/ceph/libceph.h> @@ -392,6 +393,90 @@ static int status_show(struct seq_file *s, void *p) return 0; } +static int reset_status_show(struct seq_file *s, void *p) +{ + struct ceph_fs_client *fsc = s->private; + struct ceph_mds_client *mdsc = fsc->mdsc; + struct ceph_client_reset_state *st; + u64 trigger = 0, success = 0, failure = 0; + unsigned long last_start = 0, last_finish = 0; + int last_errno = 0; + enum ceph_client_reset_phase phase = CEPH_CLIENT_RESET_IDLE; + bool drain_timed_out = false; + int sessions_reset = 0; + int blocked_requests = 0; + char reason[CEPH_CLIENT_RESET_REASON_LEN]; + + if (!mdsc) + return 0; + + st = &mdsc->reset_state; + + spin_lock(&st->lock); + trigger = st->trigger_count; + success = st->success_count; + failure = st->failure_count; + last_start = st->last_start; + last_finish = st->last_finish; + last_errno = st->last_errno; + phase = st->phase; + drain_timed_out = st->drain_timed_out; + sessions_reset = st->sessions_reset; + strscpy(reason, st->last_reason, sizeof(reason)); + spin_unlock(&st->lock); + + blocked_requests = atomic_read(&st->blocked_requests); + + seq_printf(s, "phase: %s\n", ceph_reset_phase_name(phase)); + seq_printf(s, "trigger_count: %llu\n", trigger); + seq_printf(s, "success_count: %llu\n", success); + seq_printf(s, "failure_count: %llu\n", failure); + if (last_start) + seq_printf(s, "last_start_ms_ago: %u\n", + jiffies_to_msecs(jiffies - last_start)); + else + seq_puts(s, "last_start_ms_ago: (never)\n"); + if (last_finish) + seq_printf(s, "last_finish_ms_ago: %u\n", + jiffies_to_msecs(jiffies - last_finish)); + else + seq_puts(s, "last_finish_ms_ago: (never)\n"); + seq_printf(s, "last_errno: %d\n", last_errno); + seq_printf(s, "last_reason: %s\n", + reason[0] ? reason : "(none)"); + seq_printf(s, "drain_timed_out: %s\n", + drain_timed_out ? "yes" : "no"); + seq_printf(s, "sessions_reset: %d\n", sessions_reset); + seq_printf(s, "blocked_requests: %d\n", blocked_requests); + + return 0; +} + +static ssize_t reset_trigger_write(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct ceph_fs_client *fsc = file->private_data; + struct ceph_mds_client *mdsc = fsc->mdsc; + char reason[CEPH_CLIENT_RESET_REASON_LEN]; + size_t copy; + int ret; + + if (!mdsc) + return -ENODEV; + + copy = min_t(size_t, len, sizeof(reason) - 1); + if (copy && copy_from_user(reason, buf, copy)) + return -EFAULT; + reason[copy] = '\0'; + strim(reason); + + ret = ceph_mdsc_schedule_reset(mdsc, reason); + if (ret) + return ret; + + return len; +} + static int subvolume_metrics_show(struct seq_file *s, void *p) { struct ceph_fs_client *fsc = s->private; @@ -450,6 +535,7 @@ DEFINE_SHOW_ATTRIBUTE(mdsc); DEFINE_SHOW_ATTRIBUTE(caps); DEFINE_SHOW_ATTRIBUTE(mds_sessions); DEFINE_SHOW_ATTRIBUTE(status); +DEFINE_SHOW_ATTRIBUTE(reset_status); DEFINE_SHOW_ATTRIBUTE(metrics_file); DEFINE_SHOW_ATTRIBUTE(metrics_latency); DEFINE_SHOW_ATTRIBUTE(metrics_size); @@ -521,6 +607,13 @@ static int metric_features_show(struct seq_file *s, void *p) DEFINE_SHOW_ATTRIBUTE(metric_features); +static const struct file_operations ceph_reset_trigger_fops = { + .owner = THIS_MODULE, + .open = simple_open, + .write = reset_trigger_write, + .llseek = noop_llseek, +}; + /* * debugfs */ @@ -554,6 +647,7 @@ void ceph_fs_debugfs_cleanup(struct ceph_fs_client *fsc) debugfs_remove(fsc->debugfs_caps); debugfs_remove(fsc->debugfs_status); debugfs_remove(fsc->debugfs_mdsc); + debugfs_remove_recursive(fsc->debugfs_reset_dir); debugfs_remove(fsc->debugfs_subvolume_metrics); debugfs_remove_recursive(fsc->debugfs_metrics_dir); doutc(fsc->client, "done\n"); @@ -602,6 +696,15 @@ void ceph_fs_debugfs_init(struct ceph_fs_client *fsc) fsc, &caps_fops); + fsc->debugfs_reset_dir = debugfs_create_dir("reset", + fsc->client->debugfs_dir); + debugfs_create_file("trigger", 0200, + fsc->debugfs_reset_dir, fsc, + &ceph_reset_trigger_fops); + debugfs_create_file("status", 0400, + fsc->debugfs_reset_dir, fsc, + &reset_status_fops); + fsc->debugfs_status = debugfs_create_file("status", 0400, fsc->client->debugfs_dir, diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 0ad42e1cc305..71161f2b2151 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -598,12 +598,12 @@ static void wake_async_create_waiters(struct inode *inode, spin_lock(&ci->i_ceph_lock); if (ci->i_ceph_flags & CEPH_I_ASYNC_CREATE) { - clear_and_wake_up_bit(CEPH_ASYNC_CREATE_BIT, &ci->i_ceph_flags); + /* Serialized by i_ceph_lock; the two ops touch different bits. */ + clear_and_wake_up_bit(CEPH_I_ASYNC_CREATE_BIT, &ci->i_ceph_flags); - if (ci->i_ceph_flags & CEPH_I_ASYNC_CHECK_CAPS) { - ci->i_ceph_flags &= ~CEPH_I_ASYNC_CHECK_CAPS; + if (test_and_clear_bit(CEPH_I_ASYNC_CHECK_CAPS_BIT, + &ci->i_ceph_flags)) check_cap = true; - } } ceph_kick_flushing_inode_caps(session, ci); spin_unlock(&ci->i_ceph_lock); @@ -766,7 +766,8 @@ static int ceph_finish_async_create(struct inode *dir, struct inode *inode, * that point and don't worry about setting * CEPH_I_ASYNC_CREATE. */ - ceph_inode(inode)->i_ceph_flags = CEPH_I_ASYNC_CREATE; + set_bit(CEPH_I_ASYNC_CREATE_BIT, + &ceph_inode(inode)->i_ceph_flags); unlock_new_inode(inode); } if (d_in_lookup(dentry) || d_really_is_negative(dentry)) { @@ -2486,7 +2487,7 @@ retry_snap: if ((got & (CEPH_CAP_FILE_BUFFER|CEPH_CAP_FILE_LAZYIO)) == 0 || (iocb->ki_flags & IOCB_DIRECT) || (fi->flags & CEPH_F_SYNC) || - (ci->i_ceph_flags & CEPH_I_ERROR_WRITE)) { + test_bit(CEPH_I_ERROR_WRITE_BIT, &ci->i_ceph_flags)) { struct ceph_snap_context *snapc; struct iov_iter data; diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 22c7da1ea61c..61d7c0b8161f 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -671,6 +671,7 @@ struct inode *ceph_alloc_inode(struct super_block *sb) INIT_LIST_HEAD(&ci->i_cap_snaps); ci->i_head_snapc = NULL; ci->i_snap_caps = 0; + ci->i_last_cap_flush_ack = 0; ci->i_last_rd = ci->i_last_wr = jiffies - 3600 * HZ; for (i = 0; i < CEPH_FILE_MODE_BITS; i++) @@ -1180,7 +1181,7 @@ int ceph_fill_inode(struct inode *inode, struct page *locked_page, rcu_assign_pointer(ci->i_layout.pool_ns, pool_ns); if (ci->i_layout.pool_id != old_pool || pool_ns != old_ns) - ci->i_ceph_flags &= ~CEPH_I_POOL_PERM; + clear_bit(CEPH_I_POOL_PERM_BIT, &ci->i_ceph_flags); pool_ns = old_ns; @@ -3240,7 +3241,7 @@ void ceph_inode_shutdown(struct inode *inode) bool invalidate = false; spin_lock(&ci->i_ceph_lock); - ci->i_ceph_flags |= CEPH_I_SHUTDOWN; + set_bit(CEPH_I_SHUTDOWN_BIT, &ci->i_ceph_flags); p = rb_first(&ci->i_caps); while (p) { struct ceph_cap *cap = rb_entry(p, struct ceph_cap, ci_node); diff --git a/fs/ceph/locks.c b/fs/ceph/locks.c index dd764f9c64b9..677221bd64e0 100644 --- a/fs/ceph/locks.c +++ b/fs/ceph/locks.c @@ -57,9 +57,7 @@ static void ceph_fl_release_lock(struct file_lock *fl) ci = ceph_inode(inode); if (atomic_dec_and_test(&ci->i_filelock_ref)) { /* clear error when all locks are released */ - spin_lock(&ci->i_ceph_lock); - ci->i_ceph_flags &= ~CEPH_I_ERROR_FILELOCK; - spin_unlock(&ci->i_ceph_lock); + clear_bit(CEPH_I_ERROR_FILELOCK_BIT, &ci->i_ceph_flags); } fl->fl_u.ceph.inode = NULL; iput(inode); @@ -251,6 +249,7 @@ int ceph_lock(struct file *file, int cmd, struct file_lock *fl) { struct inode *inode = file_inode(file); struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb); struct ceph_client *cl = ceph_inode_to_client(inode); int err = 0; u16 op = CEPH_MDS_OP_SETFILELOCK; @@ -271,15 +270,17 @@ int ceph_lock(struct file *file, int cmd, struct file_lock *fl) else if (IS_SETLKW(cmd)) wait = 1; - spin_lock(&ci->i_ceph_lock); - if (ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) { - err = -EIO; - } - spin_unlock(&ci->i_ceph_lock); - if (err < 0) { + if (test_bit(CEPH_I_ERROR_FILELOCK_BIT, &ci->i_ceph_flags)) { if (op == CEPH_MDS_OP_SETFILELOCK && lock_is_unlock(fl)) posix_lock_file(file, fl, NULL); - return err; + return -EIO; + } + + /* Wait for reset to complete before acquiring new locks */ + if (op == CEPH_MDS_OP_SETFILELOCK && !lock_is_unlock(fl)) { + err = ceph_mdsc_wait_for_reset(mdsc); + if (err) + return err; } if (lock_is_read(fl)) @@ -318,6 +319,7 @@ int ceph_flock(struct file *file, int cmd, struct file_lock *fl) { struct inode *inode = file_inode(file); struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb); struct ceph_client *cl = ceph_inode_to_client(inode); int err = 0; u8 wait = 0; @@ -331,15 +333,17 @@ int ceph_flock(struct file *file, int cmd, struct file_lock *fl) doutc(cl, "fl_file: %p\n", fl->c.flc_file); - spin_lock(&ci->i_ceph_lock); - if (ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) { - err = -EIO; - } - spin_unlock(&ci->i_ceph_lock); - if (err < 0) { + if (test_bit(CEPH_I_ERROR_FILELOCK_BIT, &ci->i_ceph_flags)) { if (lock_is_unlock(fl)) locks_lock_file_wait(file, fl); - return err; + return -EIO; + } + + /* Wait for reset to complete before acquiring new locks */ + if (!lock_is_unlock(fl)) { + err = ceph_mdsc_wait_for_reset(mdsc); + if (err) + return err; } if (IS_SETLKW(cmd)) diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 0edb6a251501..853bf698b356 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -6,6 +6,7 @@ #include <linux/slab.h> #include <linux/gfp.h> #include <linux/sched.h> +#include <linux/delay.h> #include <linux/debugfs.h> #include <linux/seq_file.h> #include <linux/ratelimit.h> @@ -65,6 +66,7 @@ static void __wake_requests(struct ceph_mds_client *mdsc, struct list_head *head); static void ceph_cap_release_work(struct work_struct *work); static void ceph_cap_reclaim_work(struct work_struct *work); +static void ceph_mdsc_reset_workfn(struct work_struct *work); static const struct ceph_connection_operations mds_con_ops; @@ -2330,19 +2332,112 @@ static int check_caps_flush(struct ceph_mds_client *mdsc, } /* - * flush all dirty inode data to disk. + * Snapshot of a single cap_flush entry for diagnostic dump. + * Collected under cap_dirty_lock, printed after releasing it. + */ +struct flush_dump_entry { + u64 ino; /* inode number */ + u64 snap; /* snap id */ + int caps; /* dirty cap bits */ + u64 tid; /* flush transaction id */ + u64 last_ack; /* most recent ack tid for this inode */ + bool wake; /* whether completion was requested */ + bool is_capsnap; /* true if this is a cap snap flush */ + bool ci_null; /* true if cf->ci was unexpectedly NULL */ +}; + +/* + * Dump pending cap flushes for diagnostic purposes. * - * returns true if we've flushed through want_flush_tid + * cf->ci is safe to dereference here: cap_flush entries hold a + * reference on the inode (via the cap), and entries are removed from + * cap_flush_list under cap_dirty_lock before the cap (and thus the + * inode reference) is released. Holding cap_dirty_lock therefore + * guarantees the inode remains valid for the lifetime of the scan. + */ + +static void dump_cap_flushes(struct ceph_mds_client *mdsc, u64 want_tid) +{ + struct ceph_client *cl = mdsc->fsc->client; + struct flush_dump_entry entries[CEPH_CAP_FLUSH_MAX_DUMP_ENTRIES]; + struct ceph_cap_flush *cf; + int n = 0, remaining = 0; + int i; + + spin_lock(&mdsc->cap_dirty_lock); + list_for_each_entry(cf, &mdsc->cap_flush_list, g_list) { + if (cf->tid > want_tid) + break; + if (n < CEPH_CAP_FLUSH_MAX_DUMP_ENTRIES) { + struct flush_dump_entry *e = &entries[n++]; + + e->ci_null = WARN_ON_ONCE(!cf->ci); + if (!e->ci_null) { + e->ino = ceph_ino(&cf->ci->netfs.inode); + e->snap = ceph_snap(&cf->ci->netfs.inode); + e->last_ack = READ_ONCE(cf->ci->i_last_cap_flush_ack); + } + e->caps = cf->caps; + e->tid = cf->tid; + e->wake = cf->wake; + e->is_capsnap = cf->is_capsnap; + } else { + remaining++; + } + } + spin_unlock(&mdsc->cap_dirty_lock); + + pr_info_client(cl, "still waiting for cap flushes through %llu:\n", + want_tid); + for (i = 0; i < n; i++) { + struct flush_dump_entry *e = &entries[i]; + + if (e->ci_null) + pr_info_client(cl, + " (null ci) %s tid=%llu wake=%d%s\n", + ceph_cap_string(e->caps), e->tid, + e->wake, + e->is_capsnap ? " is_capsnap" : ""); + else + pr_info_client(cl, + " %llx.%llx %s tid=%llu last_ack=%llu wake=%d%s\n", + e->ino, e->snap, + ceph_cap_string(e->caps), e->tid, + e->last_ack, e->wake, + e->is_capsnap ? " is_capsnap" : ""); + } + if (remaining) + pr_info_client(cl, " ... and %d more pending flushes\n", + remaining); +} + +/* + * Wait for all cap flushes through @want_flush_tid to complete. + * Periodically dumps pending cap flush state for diagnostics. */ static void wait_caps_flush(struct ceph_mds_client *mdsc, u64 want_flush_tid) { struct ceph_client *cl = mdsc->fsc->client; + int i = 0; + long ret; doutc(cl, "want %llu\n", want_flush_tid); - wait_event(mdsc->cap_flushing_wq, - check_caps_flush(mdsc, want_flush_tid)); + do { + /* 60 * HZ fits in a long on all supported architectures. */ + ret = wait_event_timeout(mdsc->cap_flushing_wq, + check_caps_flush(mdsc, want_flush_tid), + CEPH_CAP_FLUSH_WAIT_TIMEOUT_SEC * HZ); + if (ret == 0) { + if (i < CEPH_CAP_FLUSH_MAX_DUMP_ITERS) + dump_cap_flushes(mdsc, want_flush_tid); + else if (i == CEPH_CAP_FLUSH_MAX_DUMP_ITERS) + pr_info_client(cl, + "still waiting for cap flushes; suppressing further dumps\n"); + i++; + } + } while (ret == 0); doutc(cl, "ok, flushed thru %llu\n", want_flush_tid); } @@ -3657,7 +3752,8 @@ static void __do_request(struct ceph_mds_client *mdsc, spin_lock(&ci->i_ceph_lock); cap = ci->i_auth_cap; - if (ci->i_ceph_flags & CEPH_I_ASYNC_CREATE && mds != cap->mds) { + if (test_bit(CEPH_I_ASYNC_CREATE_BIT, &ci->i_ceph_flags) && + mds != cap->mds) { doutc(cl, "session changed for auth cap %d -> %d\n", cap->session->s_mds, session->s_mds); @@ -3751,6 +3847,22 @@ int ceph_mdsc_submit_request(struct ceph_mds_client *mdsc, struct inode *dir, struct ceph_client *cl = mdsc->fsc->client; int err = 0; + /* + * If a reset is in progress, wait for it to complete. + * + * This is best-effort: a request can pass this check just + * before the phase leaves IDLE and proceed concurrently with + * reset. That is acceptable because (a) such requests will + * either complete normally or fail and be retried by the + * caller, and (b) adding lock serialization here would + * penalize every request for a rare manual operation. + */ + err = ceph_mdsc_wait_for_reset(mdsc); + if (err) { + doutc(cl, "wait_for_reset failed: %d\n", err); + return err; + } + /* take CAP_PIN refs for r_inode, r_parent, r_old_dentry */ if (req->r_inode) ceph_get_cap_refs(ceph_inode(req->r_inode), CEPH_CAP_PIN); @@ -4469,9 +4581,14 @@ skip_cap_auths: break; case CEPH_SESSION_REJECT: - WARN_ON(session->s_state != CEPH_MDS_SESSION_OPENING); - pr_info_client(cl, "mds%d rejected session\n", - session->s_mds); + WARN_ON(session->s_state != CEPH_MDS_SESSION_OPENING && + session->s_state != CEPH_MDS_SESSION_RECONNECTING); + if (session->s_state == CEPH_MDS_SESSION_RECONNECTING) + pr_info_client(cl, "mds%d reconnect rejected\n", + session->s_mds); + else + pr_info_client(cl, "mds%d rejected session\n", + session->s_mds); session->s_state = CEPH_MDS_SESSION_REJECTED; cleanup_session_requests(mdsc, session); remove_session_caps(session); @@ -4731,6 +4848,14 @@ static int reconnect_caps_cb(struct inode *inode, int mds, void *arg) cap->mseq = 0; /* and migrate_seq */ cap->cap_gen = atomic_read(&cap->session->s_cap_gen); + /* + * Note: CEPH_I_ERROR_FILELOCK is not set during reconnect. + * Instead, locks are submitted for best-effort MDS reclaim + * via the flock_len field below. If reclaim fails (e.g., + * another client grabbed a conflicting lock), future lock + * operations will fail and set the error flag at that point. + */ + /* These are lost when the session goes away */ if (S_ISDIR(inode->i_mode)) { if (cap->issued & CEPH_CAP_DIR_CREATE) { @@ -4746,8 +4871,9 @@ static int reconnect_caps_cb(struct inode *inode, int mds, void *arg) rec.v2.issued = cpu_to_le32(cap->issued); rec.v2.snaprealm = cpu_to_le64(ci->i_snap_realm->ino); rec.v2.pathbase = cpu_to_le64(path_info.vino.ino); - rec.v2.flock_len = (__force __le32) - ((ci->i_ceph_flags & CEPH_I_ERROR_FILELOCK) ? 0 : 1); + rec.v2.flock_len = cpu_to_le32( + test_bit(CEPH_I_ERROR_FILELOCK_BIT, + &ci->i_ceph_flags) ? 0 : 1); } else { struct timespec64 ts; @@ -4944,20 +5070,19 @@ fail: * * This is a relatively heavyweight operation, but it's rare. */ -static void send_mds_reconnect(struct ceph_mds_client *mdsc, - struct ceph_mds_session *session) +static int send_mds_reconnect(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) { struct ceph_client *cl = mdsc->fsc->client; struct ceph_msg *reply; int mds = session->s_mds; int err = -ENOMEM; + int old_state; struct ceph_reconnect_state recon_state = { .session = session, }; LIST_HEAD(dispose); - pr_info_client(cl, "mds%d reconnect start\n", mds); - recon_state.pagelist = ceph_pagelist_alloc(GFP_NOFS); if (!recon_state.pagelist) goto fail_nopagelist; @@ -4966,9 +5091,37 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, if (!reply) goto fail_nomsg; + mutex_lock(&session->s_mutex); + + /* Serialized by s_mutex against concurrent ceph_get_deleg_ino(). */ xa_destroy(&session->s_delegated_inos); + if (session->s_state == CEPH_MDS_SESSION_CLOSED || + session->s_state == CEPH_MDS_SESSION_REJECTED) { + pr_info_client(cl, "mds%d skipping reconnect, session %s\n", + mds, + ceph_session_state_name(session->s_state)); + mutex_unlock(&session->s_mutex); + ceph_msg_put(reply); + err = -ESTALE; + goto fail_return; + } - mutex_lock(&session->s_mutex); + /* s_mutex -> mdsc->mutex matches cleanup_session_requests() order. */ + mutex_lock(&mdsc->mutex); + if (mds >= mdsc->max_sessions || mdsc->sessions[mds] != session) { + mutex_unlock(&mdsc->mutex); + pr_info_client(cl, + "mds%d skipping reconnect, session unregistered\n", + mds); + mutex_unlock(&session->s_mutex); + ceph_msg_put(reply); + err = -ENOENT; + goto fail_return; + } + mutex_unlock(&mdsc->mutex); + + pr_info_client(cl, "mds%d reconnect start\n", mds); + old_state = session->s_state; session->s_state = CEPH_MDS_SESSION_RECONNECTING; session->s_seq = 0; @@ -5098,7 +5251,7 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, up_read(&mdsc->snap_rwsem); ceph_pagelist_release(recon_state.pagelist); - return; + return 0; fail_clear_cap_reconnect: spin_lock(&session->s_cap_lock); @@ -5107,13 +5260,504 @@ fail_clear_cap_reconnect: fail: ceph_msg_put(reply); up_read(&mdsc->snap_rwsem); + /* + * Restore prior session state so map-driven reconnect logic + * (check_new_map) can retry. Without this, a transient build + * failure strands the session in RECONNECTING indefinitely. + */ + session->s_state = old_state; mutex_unlock(&session->s_mutex); fail_nomsg: ceph_pagelist_release(recon_state.pagelist); fail_nopagelist: pr_err_client(cl, "error %d preparing reconnect for mds%d\n", err, mds); + return err; + +fail_return: + /* + * Early-exit path for expected concurrent-teardown races + * (-ESTALE for closed/rejected sessions, -ENOENT for + * unregistered sessions). Skip the pr_err_client diagnostic + * since these are not genuine reconnect build failures. + */ + ceph_pagelist_release(recon_state.pagelist); + return err; +} + +const char *ceph_reset_phase_name(enum ceph_client_reset_phase phase) +{ + switch (phase) { + case CEPH_CLIENT_RESET_IDLE: return "idle"; + case CEPH_CLIENT_RESET_QUIESCING: return "quiescing"; + case CEPH_CLIENT_RESET_DRAINING: return "draining"; + case CEPH_CLIENT_RESET_TEARDOWN: return "teardown"; + default: return "unknown"; + } +} + +/** + * ceph_mdsc_wait_for_reset - wait for an active reset to complete + * @mdsc: MDS client + * + * Returns 0 if reset completed successfully or no reset was active. + * Returns -EAGAIN if reset completed with an error, signalling the + * caller to retry. The internal error (e.g. -ENOMEM) is not propagated + * because callers like open() or flock() have no way to act on + * work-function internals. The detailed error is available via debugfs + * reset/status and tracepoints. + * Returns -ETIMEDOUT if we timed out waiting. + * Returns -ERESTARTSYS if interrupted by signal. + */ +int ceph_mdsc_wait_for_reset(struct ceph_mds_client *mdsc) +{ + struct ceph_client_reset_state *st = &mdsc->reset_state; + struct ceph_client *cl = mdsc->fsc->client; + unsigned long deadline = jiffies + CEPH_CLIENT_RESET_WAIT_TIMEOUT_SEC * HZ; + int blocked_count; + long remaining; + long wait_ret; + int ret; + + if (ceph_reset_is_idle(st)) + return 0; + + blocked_count = atomic_inc_return(&st->blocked_requests); + doutc(cl, "request blocked during reset, %d total blocked\n", + blocked_count); + trace_ceph_client_reset_blocked(mdsc, blocked_count); + +retry: + remaining = max_t(long, deadline - jiffies, 1); + wait_ret = wait_event_interruptible_timeout(st->blocked_wq, + ceph_reset_is_idle(st), + remaining); + + if (wait_ret == 0) { + atomic_dec(&st->blocked_requests); + pr_warn_client(cl, "timed out waiting for reset to complete\n"); + trace_ceph_client_reset_unblocked(mdsc, -ETIMEDOUT); + return -ETIMEDOUT; + } + if (wait_ret < 0) { + atomic_dec(&st->blocked_requests); + trace_ceph_client_reset_unblocked(mdsc, (int)wait_ret); + return (int)wait_ret; /* -ERESTARTSYS */ + } + + /* + * Verify phase is still IDLE under the lock. If another reset + * was scheduled between the wake-up and this check, loop back + * and wait for it to finish rather than returning a stale result. + */ + spin_lock(&st->lock); + if (st->phase != CEPH_CLIENT_RESET_IDLE) { + spin_unlock(&st->lock); + if (time_before(jiffies, deadline)) + goto retry; + atomic_dec(&st->blocked_requests); + trace_ceph_client_reset_unblocked(mdsc, -ETIMEDOUT); + return -ETIMEDOUT; + } + ret = st->last_errno; + spin_unlock(&st->lock); + + atomic_dec(&st->blocked_requests); + trace_ceph_client_reset_unblocked(mdsc, ret); + return ret ? -EAGAIN : 0; +} + +static void ceph_mdsc_reset_complete(struct ceph_mds_client *mdsc, int ret) +{ + struct ceph_client_reset_state *st = &mdsc->reset_state; + + spin_lock(&st->lock); + /* + * If destroy already marked us as shut down, it owns the + * final bookkeeping and waiter wakeup. Just bail so we + * don't overwrite its state. + */ + if (st->shutdown) { + spin_unlock(&st->lock); + return; + } + st->last_finish = jiffies; + st->last_errno = ret; + st->phase = CEPH_CLIENT_RESET_IDLE; + if (ret) + st->failure_count++; + else + st->success_count++; + spin_unlock(&st->lock); + + /* Wake up all requests that were blocked waiting for reset */ + wake_up_all(&st->blocked_wq); + + trace_ceph_client_reset_complete(mdsc, ret); +} + +static void ceph_mdsc_reset_workfn(struct work_struct *work) +{ + struct ceph_mds_client *mdsc = + container_of(work, struct ceph_mds_client, reset_work); + struct ceph_client_reset_state *st = &mdsc->reset_state; + struct ceph_client *cl = mdsc->fsc->client; + struct ceph_mds_session **sessions = NULL; + char reason[CEPH_CLIENT_RESET_REASON_LEN]; + unsigned long drain_deadline; + int max_sessions, i, n = 0, torn_down = 0; + int ret = 0; + + spin_lock(&st->lock); + strscpy(reason, st->last_reason, sizeof(reason)); + spin_unlock(&st->lock); + + mutex_lock(&mdsc->mutex); + max_sessions = mdsc->max_sessions; + if (max_sessions <= 0) { + mutex_unlock(&mdsc->mutex); + goto out_complete; + } + + sessions = kcalloc(max_sessions, sizeof(*sessions), GFP_KERNEL); + if (!sessions) { + mutex_unlock(&mdsc->mutex); + ret = -ENOMEM; + pr_err_client(cl, + "manual session reset failed to allocate session array\n"); + ceph_mdsc_reset_complete(mdsc, ret); + return; + } + + for (i = 0; i < max_sessions; i++) { + struct ceph_mds_session *session = mdsc->sessions[i]; + + if (!session) + continue; + + /* + * Read session state without s_mutex to avoid nesting + * mdsc->mutex -> s_mutex, which would invert the + * s_mutex -> mdsc->mutex order used by + * cleanup_session_requests(). s_state is an int + * so loads are atomic; the teardown loop below + * handles races with concurrent state transitions. + */ + switch (READ_ONCE(session->s_state)) { + case CEPH_MDS_SESSION_OPEN: + case CEPH_MDS_SESSION_HUNG: + case CEPH_MDS_SESSION_OPENING: + case CEPH_MDS_SESSION_RESTARTING: + case CEPH_MDS_SESSION_RECONNECTING: + case CEPH_MDS_SESSION_CLOSING: + sessions[n++] = ceph_get_mds_session(session); + break; + default: + pr_info_client(cl, + "mds%d in state %s, skipping reset\n", + session->s_mds, + ceph_session_state_name(session->s_state)); + break; + } + } + mutex_unlock(&mdsc->mutex); + + pr_info_client(cl, + "manual session reset executing (sessions=%d, reason=\"%s\")\n", + n, reason); + + if (n == 0) { + kfree(sessions); + goto out_complete; + } + + spin_lock(&st->lock); + if (st->shutdown) { + spin_unlock(&st->lock); + goto out_sessions; + } + st->phase = CEPH_CLIENT_RESET_DRAINING; + spin_unlock(&st->lock); + + /* + * Best-effort drain: flush dirty state while sessions are still + * alive. New requests are blocked while phase != IDLE. + * The sessions are functional, so non-stuck state drains normally. + * Stuck state (the cause of the stalemate the operator is trying + * to break) will not drain -- that is expected, and we proceed to + * forced teardown after the timeout. + * + * Four things are drained: + * 1. MDS journal -- send_flush_mdlog asks each MDS to journal + * pending unsafe operations (creates, renames, setattrs). + * 2. Unsafe requests -- bounded wait for each unsafe write + * request to reach safe status via r_safe_completion. + * 3. Dirty caps -- ceph_flush_dirty_caps triggers cap flush on + * all sessions. Non-stuck caps flush in milliseconds. + * 4. Cap releases -- push pending cap release messages. + * + * The unsafe-request wait and cap-flush wait below provide + * the bounded drain window during which all categories can + * make progress. + */ + for (i = 0; i < n; i++) + send_flush_mdlog(sessions[i]); + + /* + * Both drain legs (unsafe requests and cap flushes) share a + * single deadline so the total drain time is bounded at + * CEPH_CLIENT_RESET_DRAIN_SEC. + */ + drain_deadline = jiffies + CEPH_CLIENT_RESET_DRAIN_SEC * HZ; + + /* + * Wait for unsafe write requests (creates, renames, setattrs) + * to reach safe status. Uses the same pattern as + * flush_mdlog_and_wait_mdsc_unsafe_requests() but bounded by + * the shared drain deadline. Requests that do not complete within + * the window are force-dropped during teardown. + */ + { + struct ceph_mds_request *req; + struct rb_node *rn; + u64 last_tid; + + mutex_lock(&mdsc->mutex); + last_tid = mdsc->last_tid; + mutex_unlock(&mdsc->mutex); + + mutex_lock(&mdsc->mutex); + rn = rb_first(&mdsc->request_tree); + while (rn) { + req = rb_entry(rn, struct ceph_mds_request, r_node); + if (req->r_tid > last_tid) + break; + if (req->r_op == CEPH_MDS_OP_SETFILELOCK || + !(req->r_op & CEPH_MDS_OP_WRITE)) { + rn = rb_next(rn); + continue; + } + ceph_mdsc_get_request(req); + mutex_unlock(&mdsc->mutex); + + wait_for_completion_timeout(&req->r_safe_completion, + max_t(long, drain_deadline - jiffies, 1)); + + mutex_lock(&mdsc->mutex); + ceph_mdsc_put_request(req); + if (time_after(jiffies, drain_deadline)) + break; + rn = rb_first(&mdsc->request_tree); + } + mutex_unlock(&mdsc->mutex); + + if (time_after_eq(jiffies, drain_deadline)) + WRITE_ONCE(st->drain_timed_out, true); + } + + ceph_flush_dirty_caps(mdsc); + ceph_flush_cap_releases(mdsc); + + spin_lock(&mdsc->cap_dirty_lock); + if (!list_empty(&mdsc->cap_flush_list)) { + struct ceph_cap_flush *cf = + list_last_entry(&mdsc->cap_flush_list, + struct ceph_cap_flush, g_list); + u64 want_flush = mdsc->last_cap_flush_tid; + long drain_ret; + + /* + * Setting wake on the last entry is sufficient: flush + * entries complete in order, so when this entry finishes + * all earlier ones are already done. + */ + cf->wake = true; + spin_unlock(&mdsc->cap_dirty_lock); + pr_info_client(cl, + "draining (want_flush=%llu, %d sessions)\n", + want_flush, n); + drain_ret = wait_event_timeout(mdsc->cap_flushing_wq, + check_caps_flush(mdsc, + want_flush), + max_t(long, + drain_deadline - jiffies, + 1)); + if (drain_ret == 0) { + pr_info_client(cl, + "drain timed out, proceeding with forced teardown\n"); + WRITE_ONCE(st->drain_timed_out, true); + } else { + pr_info_client(cl, "drain completed successfully\n"); + } + } else { + spin_unlock(&mdsc->cap_dirty_lock); + } + + spin_lock(&st->lock); + if (st->shutdown) { + spin_unlock(&st->lock); + goto out_sessions; + } + st->phase = CEPH_CLIENT_RESET_TEARDOWN; + spin_unlock(&st->lock); + + /* + * Ask each MDS to close the session before we tear it down + * locally. Without this the MDS sees only a connection drop and + * waits for the client to reconnect (up to session_autoclose + * seconds) before evicting the session and releasing locks. + * + * Reuse the normal close machinery so the session state/sequence + * snapshot is serialized under s_mutex and a racing s_seq bump + * retransmits REQUEST_CLOSE while the session remains CLOSING. + * We send all close requests first, then yield briefly to let the + * network stack transmit them before __unregister_session() + * closes the connections. + */ + for (i = 0; i < n; i++) { + int err; + + mutex_lock(&sessions[i]->s_mutex); + err = __close_session(mdsc, sessions[i]); + mutex_unlock(&sessions[i]->s_mutex); + if (err < 0) + pr_warn_client(cl, + "mds%d failed to queue close request before reset: %d\n", + sessions[i]->s_mds, err); + } + /* + * Best-effort grace period: yield briefly so the network stack + * can transmit the queued REQUEST_CLOSE messages before we tear + * down connections. Not a correctness requirement -- the MDS + * will still evict via session_autoclose if it never receives + * the close request. + * + * Event-based waiting is not viable here: there is no completion + * event for "message left the NIC," and waiting for the MDS + * SESSION_CLOSE response would re-create the stalemate that the + * reset is meant to break. + */ + if (n > 0) + msleep(CEPH_CLIENT_RESET_CLOSE_GRACE_MS); + + /* + * Tear down each session: close the connection, remove all + * caps, clean up requests, then kick pending requests so they + * re-open a fresh session on the next attempt. + * + * This is modeled on the check_new_map() forced-close path + * for stopped MDS ranks - a proven pattern for hard session + * teardown. We do NOT attempt send_mds_reconnect() because + * the MDS only accepts reconnects during its own RECONNECT + * phase (after MDS restart), not from an active client. + * + * Any state that did not drain (caps that didn't flush, unsafe + * requests that the MDS didn't journal) is force-dropped here. + * This is intentional: that state is stuck and is the reason + * the operator triggered the reset. + */ + for (i = 0; i < n; i++) { + int mds = sessions[i]->s_mds; + + pr_info_client(cl, "mds%d resetting session\n", mds); + + mutex_lock(&mdsc->mutex); + if (mds >= mdsc->max_sessions || + mdsc->sessions[mds] != sessions[i]) { + pr_info_client(cl, + "mds%d session already torn down, skipping\n", + mds); + mutex_unlock(&mdsc->mutex); + ceph_put_mds_session(sessions[i]); + sessions[i] = NULL; + continue; + } + sessions[i]->s_state = CEPH_MDS_SESSION_CLOSED; + __unregister_session(mdsc, sessions[i]); + __wake_requests(mdsc, &sessions[i]->s_waiting); + mutex_unlock(&mdsc->mutex); + + mutex_lock(&sessions[i]->s_mutex); + cleanup_session_requests(mdsc, sessions[i]); + remove_session_caps(sessions[i]); + mutex_unlock(&sessions[i]->s_mutex); + + wake_up_all(&mdsc->session_close_wq); + + ceph_put_mds_session(sessions[i]); + + mutex_lock(&mdsc->mutex); + kick_requests(mdsc, mds); + mutex_unlock(&mdsc->mutex); + + torn_down++; + pr_info_client(cl, "mds%d session reset complete\n", mds); + } + + kfree(sessions); + + spin_lock(&st->lock); + st->sessions_reset = torn_down; + spin_unlock(&st->lock); + +out_complete: + ceph_mdsc_reset_complete(mdsc, ret); return; + +out_sessions: + /* shutdown == true: ceph_mdsc_destroy() owns the final transition. */ + for (i = 0; i < n; i++) + ceph_put_mds_session(sessions[i]); + kfree(sessions); +} + +int ceph_mdsc_schedule_reset(struct ceph_mds_client *mdsc, + const char *reason) +{ + struct ceph_client_reset_state *st = &mdsc->reset_state; + struct ceph_fs_client *fsc = mdsc->fsc; + const char *msg = (reason && reason[0]) ? reason : "manual"; + int mount_state; + + mount_state = READ_ONCE(fsc->mount_state); + if (mount_state != CEPH_MOUNT_MOUNTED) { + pr_warn_client(fsc->client, + "reset rejected: mount_state=%d (not mounted)\n", + mount_state); + return -EINVAL; + } + + spin_lock(&st->lock); + if (st->phase != CEPH_CLIENT_RESET_IDLE) { + spin_unlock(&st->lock); + return -EBUSY; + } + + st->phase = CEPH_CLIENT_RESET_QUIESCING; + st->last_start = jiffies; + st->last_errno = 0; + st->drain_timed_out = false; + st->sessions_reset = 0; + st->trigger_count++; + strscpy(st->last_reason, msg, sizeof(st->last_reason)); + spin_unlock(&st->lock); + + if (WARN_ON_ONCE(!queue_work(system_unbound_wq, &mdsc->reset_work))) { + spin_lock(&st->lock); + st->phase = CEPH_CLIENT_RESET_IDLE; + st->last_errno = -EALREADY; + st->last_finish = jiffies; + st->failure_count++; + spin_unlock(&st->lock); + wake_up_all(&st->blocked_wq); + return -EALREADY; + } + + pr_info_client(mdsc->fsc->client, + "manual session reset scheduled (reason=\"%s\")\n", + msg); + trace_ceph_client_reset_schedule(mdsc, msg); + return 0; } @@ -5194,9 +5838,15 @@ static void check_new_map(struct ceph_mds_client *mdsc, */ if (s->s_state == CEPH_MDS_SESSION_RESTARTING && newstate >= CEPH_MDS_STATE_RECONNECT) { + int rc; + mutex_unlock(&mdsc->mutex); clear_bit(i, targets); - send_mds_reconnect(mdsc, s); + rc = send_mds_reconnect(mdsc, s); + if (rc) + pr_warn_client(cl, + "mds%d reconnect failed: %d\n", + i, rc); mutex_lock(&mdsc->mutex); } @@ -5260,7 +5910,11 @@ static void check_new_map(struct ceph_mds_client *mdsc, } doutc(cl, "send reconnect to export target mds.%d\n", i); mutex_unlock(&mdsc->mutex); - send_mds_reconnect(mdsc, s); + err = send_mds_reconnect(mdsc, s); + if (err) + pr_warn_client(cl, + "mds%d export target reconnect failed: %d\n", + i, err); ceph_put_mds_session(s); mutex_lock(&mdsc->mutex); } @@ -5651,6 +6305,11 @@ int ceph_mdsc_init(struct ceph_fs_client *fsc) INIT_LIST_HEAD(&mdsc->dentry_leases); INIT_LIST_HEAD(&mdsc->dentry_dir_leases); + spin_lock_init(&mdsc->reset_state.lock); + init_waitqueue_head(&mdsc->reset_state.blocked_wq); + atomic_set(&mdsc->reset_state.blocked_requests, 0); + INIT_WORK(&mdsc->reset_work, ceph_mdsc_reset_workfn); + ceph_caps_init(mdsc); ceph_adjust_caps_max_min(mdsc, fsc->mount_options); @@ -6176,6 +6835,23 @@ void ceph_mdsc_destroy(struct ceph_fs_client *fsc) /* flush out any connection work with references to us */ ceph_msgr_flush(); + /* + * Mark reset as failed and wake any blocked waiters before + * cancelling, so unmount doesn't stall on blocked_wq timeout + * if cancel_work_sync() prevents the work from running. + */ + spin_lock(&mdsc->reset_state.lock); + mdsc->reset_state.shutdown = true; + if (mdsc->reset_state.phase != CEPH_CLIENT_RESET_IDLE) { + mdsc->reset_state.phase = CEPH_CLIENT_RESET_IDLE; + mdsc->reset_state.last_errno = -ESHUTDOWN; + mdsc->reset_state.last_finish = jiffies; + mdsc->reset_state.failure_count++; + } + spin_unlock(&mdsc->reset_state.lock); + wake_up_all(&mdsc->reset_state.blocked_wq); + + cancel_work_sync(&mdsc->reset_work); ceph_mdsc_stop(mdsc); ceph_metric_destroy(&mdsc->metric); @@ -6348,12 +7024,92 @@ static void mds_peer_reset(struct ceph_connection *con) { struct ceph_mds_session *s = con->private; struct ceph_mds_client *mdsc = s->s_mdsc; + int session_state; pr_warn_client(mdsc->fsc->client, "mds%d closed our session\n", s->s_mds); - if (READ_ONCE(mdsc->fsc->mount_state) != CEPH_MOUNT_FENCE_IO && - ceph_mdsmap_get_state(mdsc->mdsmap, s->s_mds) >= CEPH_MDS_STATE_RECONNECT) - send_mds_reconnect(mdsc, s); + + if (READ_ONCE(mdsc->fsc->mount_state) == CEPH_MOUNT_FENCE_IO || + ceph_mdsmap_get_state(mdsc->mdsmap, s->s_mds) < CEPH_MDS_STATE_RECONNECT) + return; + + /* + * Only reconnect if MDS is in its RECONNECT phase. An MDS past + * RECONNECT (REJOIN, CLIENTREPLAY, ACTIVE) will reject reconnect + * attempts, so those states fall through to session teardown below. + */ + if (ceph_mdsmap_get_state(mdsc->mdsmap, s->s_mds) == CEPH_MDS_STATE_RECONNECT) { + int rc = send_mds_reconnect(mdsc, s); + + if (rc) + pr_warn_client(mdsc->fsc->client, + "mds%d reconnect failed: %d\n", + s->s_mds, rc); + return; + } + + /* + * MDS is active (past RECONNECT). It will not accept a + * CLIENT_RECONNECT from us, so tear the session down locally + * and let new requests re-open a fresh session. + * + * Snapshot session state with READ_ONCE, then revalidate under + * mdsc->mutex before acting. The subsequent mdsc->mutex + * section rechecks s_state to catch concurrent transitions, so + * the lockless snapshot here is safe. s->s_mutex is taken + * separately for cleanup after unregistration, which avoids + * introducing a new s->s_mutex + mdsc->mutex nesting. + */ + session_state = READ_ONCE(s->s_state); + + switch (session_state) { + case CEPH_MDS_SESSION_RESTARTING: + case CEPH_MDS_SESSION_RECONNECTING: + case CEPH_MDS_SESSION_CLOSING: + case CEPH_MDS_SESSION_OPEN: + case CEPH_MDS_SESSION_HUNG: + case CEPH_MDS_SESSION_OPENING: + mutex_lock(&mdsc->mutex); + if (s->s_mds >= mdsc->max_sessions || + mdsc->sessions[s->s_mds] != s || + s->s_state != session_state) { + pr_info_client(mdsc->fsc->client, + "mds%d state changed to %s during peer reset\n", + s->s_mds, + ceph_session_state_name(s->s_state)); + mutex_unlock(&mdsc->mutex); + return; + } + + ceph_get_mds_session(s); + s->s_state = CEPH_MDS_SESSION_CLOSED; + __unregister_session(mdsc, s); + __wake_requests(mdsc, &s->s_waiting); + mutex_unlock(&mdsc->mutex); + + mutex_lock(&s->s_mutex); + cleanup_session_requests(mdsc, s); + remove_session_caps(s); + mutex_unlock(&s->s_mutex); + + wake_up_all(&mdsc->session_close_wq); + + mutex_lock(&mdsc->mutex); + kick_requests(mdsc, s->s_mds); + mutex_unlock(&mdsc->mutex); + + ceph_put_mds_session(s); + break; + case CEPH_MDS_SESSION_CLOSED: + case CEPH_MDS_SESSION_REJECTED: + break; + default: + pr_warn_client(mdsc->fsc->client, + "mds%d peer reset in unexpected state %s\n", + s->s_mds, + ceph_session_state_name(session_state)); + break; + } } static void mds_dispatch(struct ceph_connection *con, struct ceph_msg *msg) @@ -6365,6 +7121,8 @@ static void mds_dispatch(struct ceph_connection *con, struct ceph_msg *msg) mutex_lock(&mdsc->mutex); if (__verify_registered_session(mdsc, s) < 0) { + doutc(cl, "dropping tid %llu from unregistered session %d\n", + le64_to_cpu(msg->hdr.tid), s->s_mds); mutex_unlock(&mdsc->mutex); goto out; } diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 4e6c87f8414c..731d6ad04956 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -77,6 +77,50 @@ struct ceph_fs_client; struct ceph_cap; #define MDS_AUTH_UID_ANY -1 +#define CEPH_CAP_FLUSH_WAIT_TIMEOUT_SEC 60 +#define CEPH_CAP_FLUSH_MAX_DUMP_ENTRIES 5 +#define CEPH_CAP_FLUSH_MAX_DUMP_ITERS 5 +#define CEPH_CLIENT_RESET_REASON_LEN 64 +#define CEPH_CLIENT_RESET_DRAIN_SEC 30 +#define CEPH_CLIENT_RESET_CLOSE_GRACE_MS 100 +#define CEPH_CLIENT_RESET_WAIT_TIMEOUT_SEC 120 + +enum ceph_client_reset_phase { + CEPH_CLIENT_RESET_IDLE = 0, + /* + * QUIESCING is set synchronously by schedule_reset() before the + * workqueue item is dispatched. It gates new requests (any + * phase != IDLE blocks callers) during the window between + * scheduling and the work function's transition to DRAINING. + */ + CEPH_CLIENT_RESET_QUIESCING, + CEPH_CLIENT_RESET_DRAINING, + CEPH_CLIENT_RESET_TEARDOWN, +}; + +struct ceph_client_reset_state { + spinlock_t lock; /* protects all fields below */ + u64 trigger_count; /* number of resets triggered */ + u64 success_count; /* number of successful resets */ + u64 failure_count; /* number of failed resets */ + unsigned long last_start; /* jiffies when last reset started */ + unsigned long last_finish; /* jiffies when last reset finished */ + int last_errno; /* result of most recent reset */ + enum ceph_client_reset_phase phase; /* current reset phase */ + bool drain_timed_out; /* drain exceeded timeout */ + bool shutdown; /* destroy in progress */ + int sessions_reset; /* sessions torn down in last reset */ + char last_reason[CEPH_CLIENT_RESET_REASON_LEN]; /* operator-supplied reason */ + + /* Request blocking during reset */ + wait_queue_head_t blocked_wq; /* waitqueue for blocked callers */ + atomic_t blocked_requests; /* count of blocked callers */ +}; + +static inline bool ceph_reset_is_idle(struct ceph_client_reset_state *st) +{ + return READ_ONCE(st->phase) == CEPH_CLIENT_RESET_IDLE; +} struct ceph_mds_cap_match { s64 uid; /* default to MDS_AUTH_UID_ANY */ @@ -540,6 +584,8 @@ struct ceph_mds_client { struct list_head dentry_dir_leases; /* lru list */ struct ceph_client_metric metric; + struct work_struct reset_work; + struct ceph_client_reset_state reset_state; struct ceph_subvolume_metrics_tracker subvol_metrics; /* Subvolume metrics send tracking */ @@ -571,10 +617,14 @@ extern struct ceph_mds_session * __ceph_lookup_mds_session(struct ceph_mds_client *, int mds); extern const char *ceph_session_state_name(int s); +extern const char *ceph_reset_phase_name(enum ceph_client_reset_phase phase); extern struct ceph_mds_session * ceph_get_mds_session(struct ceph_mds_session *s); extern void ceph_put_mds_session(struct ceph_mds_session *s); +int ceph_mdsc_schedule_reset(struct ceph_mds_client *mdsc, + const char *reason); +int ceph_mdsc_wait_for_reset(struct ceph_mds_client *mdsc); extern int ceph_mdsc_init(struct ceph_fs_client *fsc); extern void ceph_mdsc_close_sessions(struct ceph_mds_client *mdsc); @@ -670,7 +720,7 @@ static inline int ceph_wait_on_async_create(struct inode *inode) { struct ceph_inode_info *ci = ceph_inode(inode); - return wait_on_bit(&ci->i_ceph_flags, CEPH_ASYNC_CREATE_BIT, + return wait_on_bit(&ci->i_ceph_flags, CEPH_I_ASYNC_CREATE_BIT, TASK_KILLABLE); } diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index 52b4c2684f92..9b79a5eaca93 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c @@ -700,7 +700,7 @@ int __ceph_finish_cap_snap(struct ceph_inode_info *ci, return 0; } - ci->i_ceph_flags |= CEPH_I_FLUSH_SNAPS; + set_bit(CEPH_I_FLUSH_SNAPS_BIT, &ci->i_ceph_flags); doutc(cl, "%p %llx.%llx cap_snap %p snapc %p %llu %s s=%llu\n", inode, ceph_vinop(inode), capsnap, capsnap->context, capsnap->context->seq, ceph_cap_string(capsnap->dirty), diff --git a/fs/ceph/super.h b/fs/ceph/super.h index afc89ce91804..1d6aab060780 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -179,6 +179,7 @@ struct ceph_fs_client { struct dentry *debugfs_status; struct dentry *debugfs_mds_sessions; struct dentry *debugfs_metrics_dir; + struct dentry *debugfs_reset_dir; struct dentry *debugfs_subvolume_metrics; #endif @@ -239,6 +240,7 @@ struct ceph_cap_flush { bool is_capsnap; /* true means capsnap */ struct list_head g_list; // global struct list_head i_list; // per inode + struct ceph_inode_info *ci; }; /* @@ -453,6 +455,11 @@ struct ceph_inode_info { struct ceph_snap_context *i_head_snapc; /* set if wr_buffer_head > 0 or dirty|flushing caps */ unsigned i_snap_caps; /* cap bits for snapped files */ + /* + * Written under i_ceph_lock, read via READ_ONCE() + * from diagnostic paths. + */ + u64 i_last_cap_flush_ack; unsigned long i_last_rd; unsigned long i_last_wr; @@ -665,23 +672,33 @@ static inline struct inode *ceph_find_inode(struct super_block *sb, /* * Ceph inode. */ -#define CEPH_I_DIR_ORDERED (1 << 0) /* dentries in dir are ordered */ -#define CEPH_I_FLUSH (1 << 2) /* do not delay flush of dirty metadata */ -#define CEPH_I_POOL_PERM (1 << 3) /* pool rd/wr bits are valid */ -#define CEPH_I_POOL_RD (1 << 4) /* can read from pool */ -#define CEPH_I_POOL_WR (1 << 5) /* can write to pool */ -#define CEPH_I_SEC_INITED (1 << 6) /* security initialized */ -#define CEPH_I_KICK_FLUSH (1 << 7) /* kick flushing caps */ -#define CEPH_I_FLUSH_SNAPS (1 << 8) /* need flush snapss */ -#define CEPH_I_ERROR_WRITE (1 << 9) /* have seen write errors */ -#define CEPH_I_ERROR_FILELOCK (1 << 10) /* have seen file lock errors */ -#define CEPH_I_ODIRECT_BIT (11) /* inode in direct I/O mode */ -#define CEPH_I_ODIRECT (1 << CEPH_I_ODIRECT_BIT) -#define CEPH_ASYNC_CREATE_BIT (12) /* async create in flight for this */ -#define CEPH_I_ASYNC_CREATE (1 << CEPH_ASYNC_CREATE_BIT) -#define CEPH_I_SHUTDOWN (1 << 13) /* inode is no longer usable */ -#define CEPH_I_ASYNC_CHECK_CAPS (1 << 14) /* check caps immediately after async - creating finishes */ +#define CEPH_I_DIR_ORDERED_BIT (0) /* dentries in dir are ordered */ + /* bit 1 historically unused */ +#define CEPH_I_FLUSH_BIT (2) /* do not delay flush of dirty metadata */ +#define CEPH_I_POOL_PERM_BIT (3) /* pool rd/wr bits are valid */ +#define CEPH_I_POOL_RD_BIT (4) /* can read from pool */ +#define CEPH_I_POOL_WR_BIT (5) /* can write to pool */ +#define CEPH_I_SEC_INITED_BIT (6) /* security initialized */ +#define CEPH_I_KICK_FLUSH_BIT (7) /* kick flushing caps */ +#define CEPH_I_FLUSH_SNAPS_BIT (8) /* need flush snaps */ +#define CEPH_I_ERROR_WRITE_BIT (9) /* have seen write errors */ +#define CEPH_I_ERROR_FILELOCK_BIT (10) /* have seen file lock errors */ +#define CEPH_I_ODIRECT_BIT (11) /* inode in direct I/O mode */ +#define CEPH_I_ASYNC_CREATE_BIT (12) /* async create in flight for this */ +#define CEPH_I_SHUTDOWN_BIT (13) /* inode is no longer usable */ +#define CEPH_I_ASYNC_CHECK_CAPS_BIT (14) /* check caps after async creating finishes */ + +#define CEPH_I_DIR_ORDERED (1 << CEPH_I_DIR_ORDERED_BIT) +#define CEPH_I_FLUSH (1 << CEPH_I_FLUSH_BIT) +#define CEPH_I_POOL_PERM (1 << CEPH_I_POOL_PERM_BIT) +#define CEPH_I_POOL_RD (1 << CEPH_I_POOL_RD_BIT) +#define CEPH_I_POOL_WR (1 << CEPH_I_POOL_WR_BIT) +#define CEPH_I_SEC_INITED (1 << CEPH_I_SEC_INITED_BIT) +#define CEPH_I_KICK_FLUSH (1 << CEPH_I_KICK_FLUSH_BIT) +#define CEPH_I_FLUSH_SNAPS (1 << CEPH_I_FLUSH_SNAPS_BIT) +#define CEPH_I_ODIRECT (1 << CEPH_I_ODIRECT_BIT) +#define CEPH_I_ASYNC_CREATE (1 << CEPH_I_ASYNC_CREATE_BIT) +#define CEPH_I_SHUTDOWN (1 << CEPH_I_SHUTDOWN_BIT) /* * Masks of ceph inode work. @@ -694,27 +711,18 @@ static inline struct inode *ceph_find_inode(struct super_block *sb, /* * We set the ERROR_WRITE bit when we start seeing write errors on an inode - * and then clear it when they start succeeding. Note that we do a lockless - * check first, and only take the lock if it looks like it needs to be changed. - * The write submission code just takes this as a hint, so we're not too - * worried if a few slip through in either direction. + * and then clear it when they start succeeding. The write submission code + * just takes this as a hint, so we're not too worried if a few slip through + * in either direction. */ static inline void ceph_set_error_write(struct ceph_inode_info *ci) { - if (!(READ_ONCE(ci->i_ceph_flags) & CEPH_I_ERROR_WRITE)) { - spin_lock(&ci->i_ceph_lock); - ci->i_ceph_flags |= CEPH_I_ERROR_WRITE; - spin_unlock(&ci->i_ceph_lock); - } + set_bit(CEPH_I_ERROR_WRITE_BIT, &ci->i_ceph_flags); } static inline void ceph_clear_error_write(struct ceph_inode_info *ci) { - if (READ_ONCE(ci->i_ceph_flags) & CEPH_I_ERROR_WRITE) { - spin_lock(&ci->i_ceph_lock); - ci->i_ceph_flags &= ~CEPH_I_ERROR_WRITE; - spin_unlock(&ci->i_ceph_lock); - } + clear_bit(CEPH_I_ERROR_WRITE_BIT, &ci->i_ceph_flags); } static inline void __ceph_dir_set_complete(struct ceph_inode_info *ci, diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index e773be07f767..860fc8e1867d 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -1054,7 +1054,7 @@ handle_non_vxattrs: if (current->journal_info && !strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) && security_ismaclabel(name + XATTR_SECURITY_PREFIX_LEN)) - ci->i_ceph_flags |= CEPH_I_SEC_INITED; + set_bit(CEPH_I_SEC_INITED_BIT, &ci->i_ceph_flags); out: spin_unlock(&ci->i_ceph_lock); return err; diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index 8d3c278a7591..0053b5c45412 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -236,7 +236,7 @@ struct fscrypt_symlink_data { * @tfm: crypto API transform object * @blk_key: key for blk-crypto * - * Normally only one of the fields will be non-NULL. + * Only one of the fields is non-NULL. */ struct fscrypt_prepared_key { struct crypto_sync_skcipher *tfm; @@ -245,6 +245,15 @@ struct fscrypt_prepared_key { #endif }; +/* An entry in the linked list ->mk_mode_keys */ +struct fscrypt_mode_key { + struct fscrypt_prepared_key key; + struct list_head link; + u8 hkdf_context; + u8 mode_num; + u8 data_unit_bits; +}; + /* * fscrypt_inode_info - the "encryption key" for an inode * @@ -430,20 +439,12 @@ int fscrypt_derive_sw_secret(struct super_block *sb, * @prep_key, depending on which encryption implementation the file will use. */ static inline bool -fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, +fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key, const struct fscrypt_inode_info *ci) { - /* - * The two smp_load_acquire()'s here pair with the smp_store_release()'s - * in fscrypt_prepare_inline_crypt_key() and fscrypt_prepare_key(). - * I.e., in some cases (namely, if this prep_key is a per-mode - * encryption key) another task can publish blk_key or tfm concurrently, - * executing a RELEASE barrier. We need to use smp_load_acquire() here - * to safely ACQUIRE the memory the other task published. - */ if (fscrypt_using_inline_encryption(ci)) - return smp_load_acquire(&prep_key->blk_key) != NULL; - return smp_load_acquire(&prep_key->tfm) != NULL; + return prep_key->blk_key != NULL; + return prep_key->tfm != NULL; } #else /* CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ @@ -486,16 +487,29 @@ fscrypt_derive_sw_secret(struct super_block *sb, } static inline bool -fscrypt_is_key_prepared(struct fscrypt_prepared_key *prep_key, +fscrypt_is_key_prepared(const struct fscrypt_prepared_key *prep_key, const struct fscrypt_inode_info *ci) { - return smp_load_acquire(&prep_key->tfm) != NULL; + return prep_key->tfm != NULL; } #endif /* !CONFIG_FS_ENCRYPTION_INLINE_CRYPT */ /* keyring.c */ /* + * fscrypt_master_key_user - a user's claim to a master key + */ +struct fscrypt_master_key_user { + struct list_head link; + kuid_t uid; + /* + * This 'struct key' contains no secret. It exists solely to charge the + * appropriate user's key quota. + */ + struct key *quota_key; +}; + +/* * fscrypt_master_key_secret - secret key material of an in-use master key */ struct fscrypt_master_key_secret { @@ -577,8 +591,8 @@ struct fscrypt_master_key { /* * Active and structural reference counts. An active ref guarantees * that the struct continues to exist, continues to be in the keyring - * ->s_master_keys, and that any embedded subkeys (e.g. - * ->mk_direct_keys) that have been prepared continue to exist. + * ->s_master_keys, and that any non-file-scoped subkeys (e.g. + * ->mk_mode_keys) that have been prepared continue to exist. * A structural ref only guarantees that the struct continues to exist. * * There is one active ref associated with ->mk_present being true, and @@ -610,19 +624,18 @@ struct fscrypt_master_key { struct fscrypt_key_specifier mk_spec; /* - * Keyring which contains a key of type 'key_type_fscrypt_user' for each - * user who has added this key. Normally each key will be added by just - * one user, but it's possible that multiple users share a key, and in - * that case we need to keep track of those users so that one user can't - * remove the key before the others want it removed too. + * List of user claims to this key (struct fscrypt_master_key_user). + * Normally each key will be added by just one user, but it's possible + * that multiple users share a key, and in that case we need to keep + * track of those users so that one user can't remove the key before the + * others want it removed too. * - * This is NULL for v1 policy keys; those can only be added by root. + * Used only for v2 policy keys. v1 policy keys can be added only by + * root, so user tracking doesn't apply to them. * - * Locking: protected by ->mk_sem. (We don't just rely on the keyrings - * subsystem semaphore ->mk_users->sem, as we need support for atomic - * search+insert along with proper synchronization with other fields.) + * Locking: protected by ->mk_sem. */ - struct key *mk_users; + struct list_head mk_users; /* * List of inodes that were unlocked using this key. This allows the @@ -632,12 +645,21 @@ struct fscrypt_master_key { spinlock_t mk_decrypted_inodes_lock; /* - * Per-mode encryption keys for the various types of encryption policies - * that use them. Allocated and derived on-demand. + * A list of 'struct fscrypt_mode_key' for the (hkdf_context, mode_num, + * data_unit_bits, inlinecrypt) combinations that are in use for this + * master key, for hkdf_context in [HKDF_CONTEXT_DIRECT_KEY, + * HKDF_CONTEXT_IV_INO_LBLK_32_KEY, HKDF_CONTEXT_IV_INO_LBLK_64_KEY]. + * + * This is a linked list and not a hash table because in practice + * there's just a single encryption policy per master key, using + * _at most_ 2 nodes in this list. Per-file keys don't use this at all. + * + * This list is append-only until the master key is fully removed, at + * which time the list is cleared. Before then, + * fscrypt_mode_key_setup_mutex synchronizes appends, and searches use + * the RCU read lock together with ->mk_sem held for read. */ - struct fscrypt_prepared_key mk_direct_keys[FSCRYPT_MODE_MAX + 1]; - struct fscrypt_prepared_key mk_iv_ino_lblk_64_keys[FSCRYPT_MODE_MAX + 1]; - struct fscrypt_prepared_key mk_iv_ino_lblk_32_keys[FSCRYPT_MODE_MAX + 1]; + struct list_head mk_mode_keys; /* Hash key for inode numbers. Initialized only when needed. */ siphash_key_t mk_ino_hash_key; diff --git a/fs/crypto/inline_crypt.c b/fs/crypto/inline_crypt.c index 37d42d357925..47324062fee5 100644 --- a/fs/crypto/inline_crypt.c +++ b/fs/crypto/inline_crypt.c @@ -198,13 +198,7 @@ int fscrypt_prepare_inline_crypt_key(struct fscrypt_prepared_key *prep_key, goto fail; } - /* - * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared(). - * I.e., here we publish ->blk_key with a RELEASE barrier so that - * concurrent tasks can ACQUIRE it. Note that this concurrency is only - * possible for per-mode keys, not for per-file keys. - */ - smp_store_release(&prep_key->blk_key, blk_key); + prep_key->blk_key = blk_key; return 0; fail: diff --git a/fs/crypto/keyring.c b/fs/crypto/keyring.c index be8e6e8011f2..38b73e703073 100644 --- a/fs/crypto/keyring.c +++ b/fs/crypto/keyring.c @@ -65,36 +65,33 @@ static void fscrypt_free_master_key(struct rcu_head *head) kfree_sensitive(mk); } +static void clear_mk_users(struct fscrypt_master_key *mk); + void fscrypt_put_master_key(struct fscrypt_master_key *mk) { if (!refcount_dec_and_test(&mk->mk_struct_refs)) return; /* - * No structural references left, so free ->mk_users, and also free the + * No structural references left, so clear ->mk_users, and also free the * fscrypt_master_key struct itself after an RCU grace period ensures * that concurrent keyring lookups can no longer find it. */ WARN_ON_ONCE(refcount_read(&mk->mk_active_refs) != 0); - if (mk->mk_users) { - /* Clear the keyring so the quota gets released right away. */ - keyring_clear(mk->mk_users); - key_put(mk->mk_users); - mk->mk_users = NULL; - } + clear_mk_users(mk); call_rcu(&mk->mk_rcu_head, fscrypt_free_master_key); } void fscrypt_put_master_key_activeref(struct super_block *sb, struct fscrypt_master_key *mk) { - size_t i; + struct fscrypt_mode_key *node, *tmp; if (!refcount_dec_and_test(&mk->mk_active_refs)) return; /* * No active references left, so complete the full removal of this * fscrypt_master_key struct by removing it from the keyring and - * destroying any subkeys embedded in it. + * destroying any non-file-scoped subkeys. */ if (WARN_ON_ONCE(!sb->s_master_keys)) @@ -110,13 +107,16 @@ void fscrypt_put_master_key_activeref(struct super_block *sb, WARN_ON_ONCE(mk->mk_present); WARN_ON_ONCE(!list_empty(&mk->mk_decrypted_inodes)); - for (i = 0; i <= FSCRYPT_MODE_MAX; i++) { - fscrypt_destroy_prepared_key( - sb, &mk->mk_direct_keys[i]); - fscrypt_destroy_prepared_key( - sb, &mk->mk_iv_ino_lblk_64_keys[i]); - fscrypt_destroy_prepared_key( - sb, &mk->mk_iv_ino_lblk_32_keys[i]); + /* + * Destroy any non-file-scoped subkeys. Since ->mk_active_refs == 0, + * they're no longer referenced by any inodes. Nor can key setup run + * and use them again. So they're no longer needed. (This implies no + * concurrent readers, so we don't need list_del_rcu() for example.) + */ + list_for_each_entry_safe(node, tmp, &mk->mk_mode_keys, link) { + fscrypt_destroy_prepared_key(sb, &node->key); + list_del(&node->link); + kfree(node); } memzero_explicit(&mk->mk_ino_hash_key, sizeof(mk->mk_ino_hash_key)); @@ -162,8 +162,8 @@ static void fscrypt_user_key_describe(const struct key *key, struct seq_file *m) } /* - * Type of key in ->mk_users. Each key of this type represents a particular - * user who has added a particular master key. + * Type of fscrypt_master_key_user::quota_key. This contains no secret; it + * exists solely to charge a user's key quota. * * Note that the name of this key type really should be something like * ".fscrypt-user" instead of simply ".fscrypt". But the shorter name is chosen @@ -177,30 +177,9 @@ static struct key_type key_type_fscrypt_user = { .describe = fscrypt_user_key_describe, }; -#define FSCRYPT_MK_USERS_DESCRIPTION_SIZE \ - (CONST_STRLEN("fscrypt-") + 2 * FSCRYPT_KEY_IDENTIFIER_SIZE + \ - CONST_STRLEN("-users") + 1) - #define FSCRYPT_MK_USER_DESCRIPTION_SIZE \ (2 * FSCRYPT_KEY_IDENTIFIER_SIZE + CONST_STRLEN(".uid.") + 10 + 1) -static void format_mk_users_keyring_description( - char description[FSCRYPT_MK_USERS_DESCRIPTION_SIZE], - const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) -{ - sprintf(description, "fscrypt-%*phN-users", - FSCRYPT_KEY_IDENTIFIER_SIZE, mk_identifier); -} - -static void format_mk_user_description( - char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE], - const u8 mk_identifier[FSCRYPT_KEY_IDENTIFIER_SIZE]) -{ - - sprintf(description, "%*phN.uid.%u", FSCRYPT_KEY_IDENTIFIER_SIZE, - mk_identifier, __kuid_val(current_fsuid())); -} - /* Create ->s_master_keys if needed. Synchronized by fscrypt_add_key_mutex. */ static int allocate_filesystem_keyring(struct super_block *sb) { @@ -335,91 +314,94 @@ out: return mk; } -static int allocate_master_key_users_keyring(struct fscrypt_master_key *mk) -{ - char description[FSCRYPT_MK_USERS_DESCRIPTION_SIZE]; - struct key *keyring; - - format_mk_users_keyring_description(description, - mk->mk_spec.u.identifier); - keyring = keyring_alloc(description, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, - current_cred(), KEY_POS_SEARCH | - KEY_USR_SEARCH | KEY_USR_READ | KEY_USR_VIEW, - KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); - if (IS_ERR(keyring)) - return PTR_ERR(keyring); - - mk->mk_users = keyring; - return 0; -} - -/* - * Find the current user's "key" in the master key's ->mk_users. - * Returns ERR_PTR(-ENOKEY) if not found. - */ -static struct key *find_master_key_user(struct fscrypt_master_key *mk) +/* Find the current user's claim in ->mk_users. ->mk_sem must be held. */ +static struct fscrypt_master_key_user * +find_master_key_user(struct fscrypt_master_key *mk) { - char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; - key_ref_t keyref; - - format_mk_user_description(description, mk->mk_spec.u.identifier); + struct fscrypt_master_key_user *mk_user; + kuid_t uid = current_fsuid(); - /* - * We need to mark the keyring reference as "possessed" so that we - * acquire permission to search it, via the KEY_POS_SEARCH permission. - */ - keyref = keyring_search(make_key_ref(mk->mk_users, true /*possessed*/), - &key_type_fscrypt_user, description, false); - if (IS_ERR(keyref)) { - if (PTR_ERR(keyref) == -EAGAIN || /* not found */ - PTR_ERR(keyref) == -EKEYREVOKED) /* recently invalidated */ - keyref = ERR_PTR(-ENOKEY); - return ERR_CAST(keyref); + list_for_each_entry(mk_user, &mk->mk_users, link) { + if (uid_eq(mk_user->uid, uid)) + return mk_user; } - return key_ref_to_ptr(keyref); + return NULL; } /* - * Give the current user a "key" in ->mk_users. This charges the user's quota + * Give the current user a claim in ->mk_users. This charges the user's quota * and marks the master key as added by the current user, so that it cannot be * removed by another user with the key. Either ->mk_sem must be held for * write, or the master key must be still undergoing initialization. */ static int add_master_key_user(struct fscrypt_master_key *mk) { + kuid_t uid = current_fsuid(); char description[FSCRYPT_MK_USER_DESCRIPTION_SIZE]; - struct key *mk_user; + struct key *quota_key; + struct fscrypt_master_key_user *mk_user; int err; - format_mk_user_description(description, mk->mk_spec.u.identifier); - mk_user = key_alloc(&key_type_fscrypt_user, description, - current_fsuid(), current_gid(), current_cred(), - KEY_POS_SEARCH | KEY_USR_VIEW, 0, NULL); - if (IS_ERR(mk_user)) - return PTR_ERR(mk_user); + snprintf(description, sizeof(description), "%*phN.uid.%u", + FSCRYPT_KEY_IDENTIFIER_SIZE, mk->mk_spec.u.identifier, + __kuid_val(uid)); + quota_key = key_alloc(&key_type_fscrypt_user, description, uid, + current_gid(), current_cred(), + KEY_POS_SEARCH | KEY_USR_VIEW, 0, NULL); + if (IS_ERR(quota_key)) + return PTR_ERR(quota_key); + + err = key_instantiate_and_link(quota_key, NULL, 0, NULL, NULL); + if (err) { + key_put(quota_key); + return err; + } - err = key_instantiate_and_link(mk_user, NULL, 0, mk->mk_users, NULL); - key_put(mk_user); - return err; + mk_user = kzalloc_obj(*mk_user); + if (!mk_user) { + key_put(quota_key); + return -ENOMEM; + } + mk_user->uid = uid; + mk_user->quota_key = quota_key; + list_add(&mk_user->link, &mk->mk_users); + return 0; +} + +static void unlink_and_free_mk_user(struct fscrypt_master_key_user *mk_user) +{ + list_del(&mk_user->link); + key_put(mk_user->quota_key); + kfree(mk_user); } /* - * Remove the current user's "key" from ->mk_users. + * Remove the current user's claim from ->mk_users. * ->mk_sem must be held for write. * - * Returns 0 if removed, -ENOKEY if not found, or another -errno code. + * Returns 0 if removed or -ENOKEY if not found. */ static int remove_master_key_user(struct fscrypt_master_key *mk) { - struct key *mk_user; - int err; + struct fscrypt_master_key_user *mk_user; mk_user = find_master_key_user(mk); - if (IS_ERR(mk_user)) - return PTR_ERR(mk_user); - err = key_unlink(mk->mk_users, mk_user); - key_put(mk_user); - return err; + if (!mk_user) + return -ENOKEY; + unlink_and_free_mk_user(mk_user); + return 0; +} + +/* + * Clear ->mk_users. Either ->mk_sem must be held for write, or 'mk' must have + * no structural references left. + */ +static void clear_mk_users(struct fscrypt_master_key *mk) +{ + struct fscrypt_master_key_user *mk_user, *tmp; + + list_for_each_entry_safe(mk_user, tmp, &mk->mk_users, link) + unlink_and_free_mk_user(mk_user); } /* @@ -442,13 +424,14 @@ static int add_new_master_key(struct super_block *sb, refcount_set(&mk->mk_struct_refs, 1); mk->mk_spec = *mk_spec; + INIT_LIST_HEAD(&mk->mk_users); + INIT_LIST_HEAD(&mk->mk_decrypted_inodes); spin_lock_init(&mk->mk_decrypted_inodes_lock); + INIT_LIST_HEAD(&mk->mk_mode_keys); + if (mk_spec->type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { - err = allocate_master_key_users_keyring(mk); - if (err) - goto out_put; err = add_master_key_user(mk); if (err) goto out_put; @@ -477,19 +460,13 @@ static int add_existing_master_key(struct fscrypt_master_key *mk, int err; /* - * If the current user is already in ->mk_users, then there's nothing to - * do. Otherwise, we need to add the user to ->mk_users. (Neither is - * applicable for v1 policy keys, which have NULL ->mk_users.) + * For v2 policy keys (FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER): If the current + * user is already in ->mk_users, then there's nothing to do. + * Otherwise, add the user to ->mk_users. */ - if (mk->mk_users) { - struct key *mk_user = find_master_key_user(mk); - - if (mk_user != ERR_PTR(-ENOKEY)) { - if (IS_ERR(mk_user)) - return PTR_ERR(mk_user); - key_put(mk_user); + if (mk->mk_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) { + if (find_master_key_user(mk) != NULL) return 0; - } err = add_master_key_user(mk); if (err) return err; @@ -888,7 +865,6 @@ int fscrypt_verify_key_added(struct super_block *sb, { struct fscrypt_key_specifier mk_spec; struct fscrypt_master_key *mk; - struct key *mk_user; int err; mk_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER; @@ -900,13 +876,10 @@ int fscrypt_verify_key_added(struct super_block *sb, goto out; } down_read(&mk->mk_sem); - mk_user = find_master_key_user(mk); - if (IS_ERR(mk_user)) { - err = PTR_ERR(mk_user); - } else { - key_put(mk_user); + if (find_master_key_user(mk) != NULL) err = 0; - } + else + err = -ENOKEY; up_read(&mk->mk_sem); fscrypt_put_master_key(mk); out: @@ -1098,16 +1071,18 @@ static int do_remove_key(struct file *filp, void __user *_uarg, bool all_users) down_write(&mk->mk_sem); /* If relevant, remove current user's (or all users) claim to the key */ - if (mk->mk_users && mk->mk_users->keys.nr_leaves_on_tree != 0) { - if (all_users) - err = keyring_clear(mk->mk_users); - else + if (!list_empty(&mk->mk_users)) { + if (all_users) { + clear_mk_users(mk); + err = 0; + } else { err = remove_master_key_user(mk); + } if (err) { up_write(&mk->mk_sem); goto out_put_key; } - if (mk->mk_users->keys.nr_leaves_on_tree != 0) { + if (!list_empty(&mk->mk_users)) { /* * Other users have still added the key too. We removed * the current user's claim to the key, but we still @@ -1193,6 +1168,8 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg) struct super_block *sb = file_inode(filp)->i_sb; struct fscrypt_get_key_status_arg arg; struct fscrypt_master_key *mk; + kuid_t uid; + const struct fscrypt_master_key_user *mk_user; int err; if (copy_from_user(&arg, uarg, sizeof(arg))) @@ -1225,19 +1202,13 @@ int fscrypt_ioctl_get_key_status(struct file *filp, void __user *uarg) } arg.status = FSCRYPT_KEY_STATUS_PRESENT; - if (mk->mk_users) { - struct key *mk_user; - arg.user_count = mk->mk_users->keys.nr_leaves_on_tree; - mk_user = find_master_key_user(mk); - if (!IS_ERR(mk_user)) { + uid = current_fsuid(); + list_for_each_entry(mk_user, &mk->mk_users, link) { + arg.user_count++; + if (uid_eq(mk_user->uid, uid)) arg.status_flags |= FSCRYPT_KEY_STATUS_FLAG_ADDED_BY_SELF; - key_put(mk_user); - } else if (mk_user != ERR_PTR(-ENOKEY)) { - err = PTR_ERR(mk_user); - goto out_release_key; - } } err = 0; out_release_key: diff --git a/fs/crypto/keysetup.c b/fs/crypto/keysetup.c index ce327bfdada4..f905f9f94bdd 100644 --- a/fs/crypto/keysetup.c +++ b/fs/crypto/keysetup.c @@ -163,13 +163,7 @@ int fscrypt_prepare_key(struct fscrypt_prepared_key *prep_key, tfm = fscrypt_allocate_skcipher(ci->ci_mode, raw_key, ci->ci_inode); if (IS_ERR(tfm)) return PTR_ERR(tfm); - /* - * Pairs with the smp_load_acquire() in fscrypt_is_key_prepared(). - * I.e., here we publish ->tfm with a RELEASE barrier so that - * concurrent tasks can ACQUIRE it. Note that this concurrency is only - * possible for per-mode keys, not for per-file keys. - */ - smp_store_release(&prep_key->tfm, tfm); + prep_key->tfm = tfm; return 0; } @@ -190,9 +184,37 @@ int fscrypt_set_per_file_enc_key(struct fscrypt_inode_info *ci, return fscrypt_prepare_key(&ci->ci_enc_key, raw_key, ci); } +/* + * Find the fscrypt_prepared_key (if any) for a particular (mk, hkdf_context, + * mode_num, data_unit_bits, inlinecrypt) combination. + * + * The caller must hold ->mk_sem for reading and ->mk_present must be true, + * ensuring that ->mk_mode_keys is still append-only. + */ +static struct fscrypt_prepared_key * +fscrypt_find_mode_key(struct fscrypt_master_key *mk, u8 hkdf_context, + u8 mode_num, const struct fscrypt_inode_info *ci) +{ + struct fscrypt_mode_key *node; + + /* + * The RCU read lock here is used only to synchronize with concurrent + * list_add_tail_rcu(). Concurrent deletions are impossible here, so + * returning a pointer to a node without taking any refcount is safe. + */ + guard(rcu)(); + list_for_each_entry_rcu(node, &mk->mk_mode_keys, link) { + if (node->hkdf_context == hkdf_context && + node->mode_num == mode_num && + node->data_unit_bits == ci->ci_data_unit_bits && + fscrypt_is_key_prepared(&node->key, ci)) + return &node->key; + } + return NULL; +} + static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, struct fscrypt_master_key *mk, - struct fscrypt_prepared_key *keys, u8 hkdf_context, bool include_fs_uuid) { const struct inode *inode = ci->ci_inode; @@ -200,7 +222,8 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, struct fscrypt_mode *mode = ci->ci_mode; const u8 mode_num = mode - fscrypt_modes; struct fscrypt_prepared_key *prep_key; - u8 mode_key[FSCRYPT_MAX_RAW_KEY_SIZE]; + struct fscrypt_mode_key *new_node; + u8 raw_mode_key[FSCRYPT_MAX_RAW_KEY_SIZE]; u8 hkdf_info[sizeof(mode_num) + sizeof(sb->s_uuid)]; unsigned int hkdf_infolen = 0; bool use_hw_wrapped_key = false; @@ -223,48 +246,56 @@ static int setup_per_mode_enc_key(struct fscrypt_inode_info *ci, use_hw_wrapped_key = true; } - prep_key = &keys[mode_num]; - if (fscrypt_is_key_prepared(prep_key, ci)) { + prep_key = fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci); + if (prep_key) { ci->ci_enc_key = *prep_key; return 0; } - mutex_lock(&fscrypt_mode_key_setup_mutex); + guard(mutex)(&fscrypt_mode_key_setup_mutex); - if (fscrypt_is_key_prepared(prep_key, ci)) - goto done_unlock; + prep_key = fscrypt_find_mode_key(mk, hkdf_context, mode_num, ci); + if (prep_key) { + ci->ci_enc_key = *prep_key; + return 0; + } + + new_node = kzalloc_obj(*new_node); + if (!new_node) + return -ENOMEM; + new_node->hkdf_context = hkdf_context; + new_node->mode_num = mode_num; + new_node->data_unit_bits = ci->ci_data_unit_bits; + prep_key = &new_node->key; if (use_hw_wrapped_key) { err = fscrypt_prepare_inline_crypt_key(prep_key, mk->mk_secret.bytes, mk->mk_secret.size, true, ci); - if (err) - goto out_unlock; - goto done_unlock; + } else { + static_assert(sizeof(mode_num) == 1); + static_assert(sizeof(sb->s_uuid) == 16); + static_assert(sizeof(hkdf_info) == 17); + hkdf_info[hkdf_infolen++] = mode_num; + if (include_fs_uuid) { + memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid, + sizeof(sb->s_uuid)); + hkdf_infolen += sizeof(sb->s_uuid); + } + fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context, + hkdf_info, hkdf_infolen, raw_mode_key, + mode->keysize); + err = fscrypt_prepare_key(prep_key, raw_mode_key, ci); + memzero_explicit(raw_mode_key, mode->keysize); } - - BUILD_BUG_ON(sizeof(mode_num) != 1); - BUILD_BUG_ON(sizeof(sb->s_uuid) != 16); - BUILD_BUG_ON(sizeof(hkdf_info) != 17); - hkdf_info[hkdf_infolen++] = mode_num; - if (include_fs_uuid) { - memcpy(&hkdf_info[hkdf_infolen], &sb->s_uuid, - sizeof(sb->s_uuid)); - hkdf_infolen += sizeof(sb->s_uuid); + if (err) { + kfree(new_node); + return err; } - fscrypt_hkdf_expand(&mk->mk_secret.hkdf, hkdf_context, hkdf_info, - hkdf_infolen, mode_key, mode->keysize); - err = fscrypt_prepare_key(prep_key, mode_key, ci); - memzero_explicit(mode_key, mode->keysize); - if (err) - goto out_unlock; -done_unlock: + list_add_tail_rcu(&new_node->link, &mk->mk_mode_keys); ci->ci_enc_key = *prep_key; - err = 0; -out_unlock: - mutex_unlock(&fscrypt_mode_key_setup_mutex); - return err; + return 0; } /* @@ -311,8 +342,8 @@ static int fscrypt_setup_iv_ino_lblk_32_key(struct fscrypt_inode_info *ci, { int err; - err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_32_keys, - HKDF_CONTEXT_IV_INO_LBLK_32_KEY, true); + err = setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_IV_INO_LBLK_32_KEY, + true); if (err) return err; @@ -364,8 +395,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci, * encryption key. This ensures that the master key is * consistently used only for HKDF, avoiding key reuse issues. */ - err = setup_per_mode_enc_key(ci, mk, mk->mk_direct_keys, - HKDF_CONTEXT_DIRECT_KEY, false); + err = setup_per_mode_enc_key(ci, mk, HKDF_CONTEXT_DIRECT_KEY, + false); } else if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64) { /* @@ -374,9 +405,8 @@ static int fscrypt_setup_v2_file_key(struct fscrypt_inode_info *ci, * the IVs. This format is optimized for use with inline * encryption hardware compliant with the UFS standard. */ - err = setup_per_mode_enc_key(ci, mk, mk->mk_iv_ino_lblk_64_keys, - HKDF_CONTEXT_IV_INO_LBLK_64_KEY, - true); + err = setup_per_mode_enc_key( + ci, mk, HKDF_CONTEXT_IV_INO_LBLK_64_KEY, true); } else if (ci->ci_policy.v2.flags & FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32) { err = fscrypt_setup_iv_ino_lblk_32_key(ci, mk); diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 5ef67b2674ee..74b02b55e3f6 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -49,25 +49,15 @@ void ecryptfs_from_hex(char *dst, char *src, int dst_size) } static int ecryptfs_crypto_api_algify_cipher_name(char **algified_name, - char *cipher_name, - char *chaining_modifier) + const char *cipher_name, + const char *chaining_modifier) { - int cipher_name_len = strlen(cipher_name); - int chaining_modifier_len = strlen(chaining_modifier); - int algified_name_len; - int rc; + (*algified_name) = kasprintf(GFP_KERNEL, "%s(%s)", chaining_modifier, + cipher_name); + if (!(*algified_name)) + return -ENOMEM; - algified_name_len = (chaining_modifier_len + cipher_name_len + 3); - (*algified_name) = kmalloc(algified_name_len, GFP_KERNEL); - if (!(*algified_name)) { - rc = -ENOMEM; - goto out; - } - snprintf((*algified_name), algified_name_len, "%s(%s)", - chaining_modifier, cipher_name); - rc = 0; -out: - return rc; + return 0; } /** diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index 0be746493e56..ebebc9551f1f 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -1718,7 +1718,6 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, struct dentry *ecryptfs_dentry) { size_t i = 0; - size_t found_auth_tok; size_t next_packet_is_auth_tok_packet; LIST_HEAD(auth_tok_list); struct ecryptfs_auth_tok *matching_auth_tok; @@ -1822,7 +1821,6 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, * the metadata. There may be several potential matches, but * just one will be sufficient to decrypt to get the FEK. */ find_next_matching_auth_tok: - found_auth_tok = 0; list_for_each_entry(auth_tok_list_item, &auth_tok_list, list) { candidate_auth_tok = &auth_tok_list_item->auth_tok; if (unlikely(ecryptfs_verbosity > 0)) { @@ -1843,17 +1841,13 @@ find_next_matching_auth_tok: &matching_auth_tok, crypt_stat->mount_crypt_stat, candidate_auth_tok_sig); - if (!rc) { - found_auth_tok = 1; + if (!rc) goto found_matching_auth_tok; - } - } - if (!found_auth_tok) { - ecryptfs_printk(KERN_ERR, "Could not find a usable " - "authentication token\n"); - rc = -EIO; - goto out_wipe_list; } + ecryptfs_printk(KERN_ERR, + "Could not find a usable authentication token\n"); + rc = -EIO; + goto out_wipe_list; found_matching_auth_tok: if (candidate_auth_tok->token_type == ECRYPTFS_PRIVATE_KEY) { memcpy(&(candidate_auth_tok->token.private_key), diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index d158c4b7413d..51ac1fd44f78 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1322,6 +1322,19 @@ static int gfs2_block_zero_range(struct inode *inode, loff_t from, loff_t length &gfs2_iomap_write_ops, NULL); } +int gfs2_clear_beyond_eof(struct inode *inode, loff_t end) +{ + loff_t isize = i_size_read(inode); + unsigned int len = isize & ~PAGE_MASK; + + if (!len || isize >= end) + return 0; + len = PAGE_SIZE - len; + if (end - isize < len) + len = end - isize; + return gfs2_block_zero_range(inode, isize, len); +} + #define GFS2_JTRUNC_REVOKES 8192 /** @@ -2097,6 +2110,12 @@ static int do_grow(struct inode *inode, u64 size) unstuff = 1; } + if (!unstuff) { + error = gfs2_clear_beyond_eof(inode, size); + if (error) + goto do_grow_qunlock; + } + error = gfs2_trans_begin(sdp, RES_DINODE + RES_STATFS + RES_RG_BIT + (unstuff && gfs2_is_jdata(ip) ? RES_JDATA : 0) + diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h index 6cdc72dd55a3..e3d6efdfd890 100644 --- a/fs/gfs2/bmap.h +++ b/fs/gfs2/bmap.h @@ -58,6 +58,7 @@ int gfs2_get_extent(struct inode *inode, u64 lblock, u64 *dblock, unsigned int *extlen); int gfs2_alloc_extent(struct inode *inode, u64 lblock, u64 *dblock, unsigned *extlen, bool *new); +int gfs2_clear_beyond_eof(struct inode *inode, loff_t end); int gfs2_setattr_size(struct inode *inode, u64 size); int gfs2_truncatei_resume(struct gfs2_inode *ip); int gfs2_file_dealloc(struct gfs2_inode *ip); diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 9704f1ef6ad1..b8c10de113ba 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -1057,6 +1057,10 @@ retry: goto out_unlock; } + ret = gfs2_clear_beyond_eof(inode, iocb->ki_pos); + if (ret) + goto out_unlock; + pagefault_disable(); ret = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops, &gfs2_iomap_write_ops, NULL); @@ -1173,8 +1177,7 @@ out_unlock: return ret; } -static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len, - int mode) +static int fallocate_chunk(struct inode *inode, loff_t offset, loff_t len) { struct super_block *sb = inode->i_sb; struct gfs2_inode *ip = GFS2_I(inode); @@ -1266,6 +1269,12 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t next = (next + 1) << sdp->sd_sb.sb_bsize_shift; + if (!(mode & FALLOC_FL_KEEP_SIZE)) { + error = gfs2_clear_beyond_eof(inode, offset + len); + if (error) + return error; + } + offset &= bsize_mask; len = next - offset; @@ -1336,7 +1345,7 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t if (error) goto out_trans_fail; - error = fallocate_chunk(inode, offset, max_bytes, mode); + error = fallocate_chunk(inode, offset, max_bytes); gfs2_trans_end(sdp); if (error) diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 5290865f27f1..91e9975d25e8 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -254,9 +254,13 @@ fail: return NULL; } -static struct gfs2_quota_data *gfs2_qd_search_bucket(unsigned int hash, - const struct gfs2_sbd *sdp, - struct kqid qid) +/* + * Lookup variant for callers which already hold qd_lock + bucket lock. + */ +static struct gfs2_quota_data * +gfs2_qd_search_bucket_noref(unsigned int hash, + const struct gfs2_sbd *sdp, + struct kqid qid) { struct gfs2_quota_data *qd; struct hlist_bl_node *h; @@ -264,12 +268,22 @@ static struct gfs2_quota_data *gfs2_qd_search_bucket(unsigned int hash, hlist_bl_for_each_entry_rcu(qd, h, &qd_hash_table[hash], qd_hlist) { if (!qid_eq(qd->qd_id, qid)) continue; - if (qd->qd_sbd != sdp) - continue; - if (lockref_get_not_dead(&qd->qd_lockref)) { - list_lru_del_obj(&gfs2_qd_lru, &qd->qd_lru); + if (qd->qd_sbd == sdp) return qd; - } + } + + return NULL; +} + +static struct gfs2_quota_data * +gfs2_qd_search_bucket(unsigned int hash, const struct gfs2_sbd *sdp, struct kqid qid) +{ + struct gfs2_quota_data *qd; + + qd = gfs2_qd_search_bucket_noref(hash, sdp, qid); + if (qd && lockref_get_not_dead(&qd->qd_lockref)) { + list_lru_del_obj(&gfs2_qd_lru, &qd->qd_lru); + return qd; } return NULL; @@ -1433,7 +1447,7 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) qc = (struct gfs2_quota_change *)(bh->b_data + sizeof(struct gfs2_meta_header)); for (y = 0; y < sdp->sd_qc_per_block && slot < sdp->sd_quota_slots; - y++, slot++) { + y++, slot++, qc++) { struct gfs2_quota_data *old_qd, *qd; s64 qc_change = be64_to_cpu(qc->qc_change); u32 qc_flags = be32_to_cpu(qc->qc_flags); @@ -1441,7 +1455,6 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) USRQUOTA : GRPQUOTA; struct kqid qc_id = make_kqid(&init_user_ns, qtype, be32_to_cpu(qc->qc_id)); - qc++; if (!qc_change) continue; @@ -1458,7 +1471,7 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) spin_lock(&qd_lock); spin_lock_bucket(hash); - old_qd = gfs2_qd_search_bucket(hash, sdp, qc_id); + old_qd = gfs2_qd_search_bucket_noref(hash, sdp, qc_id); if (old_qd) { fs_err(sdp, "Corruption found in quota_change%u" "file: duplicate identifier in " @@ -1467,7 +1480,6 @@ int gfs2_quota_init(struct gfs2_sbd *sdp) spin_unlock_bucket(hash); spin_unlock(&qd_lock); - qd_put(old_qd); gfs2_glock_put(qd->qd_gl); kmem_cache_free(gfs2_quotad_cachep, qd); diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index a2ea121331f1..4d854556b529 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -643,6 +643,7 @@ restart: gfs2_delete_debugfs_file(sdp); gfs2_sys_fs_del(sdp); + rcu_barrier(); free_sbd(sdp); } diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c index 42a3115359da..07cf0e578233 100644 --- a/fs/smb/client/cifsacl.c +++ b/fs/smb/client/cifsacl.c @@ -1185,7 +1185,8 @@ set_size: static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *pndacl, struct smb_sid *pownersid, struct smb_sid *pgrpsid, - struct smb_sid *pnownersid, struct smb_sid *pngrpsid) + struct smb_sid *pnownersid, struct smb_sid *pngrpsid, + int *aclflag) { int i; u16 size = 0; @@ -1209,12 +1210,15 @@ static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *p pntace = (struct smb_ace *) (acl_base + size); pnntace = (struct smb_ace *) (nacl_base + nsize); - if (pnownersid && compare_sids(&pntace->sid, pownersid) == 0) + if (pnownersid && compare_sids(&pntace->sid, pownersid) == 0) { ace_size = cifs_copy_ace(pnntace, pntace, pnownersid); - else if (pngrpsid && compare_sids(&pntace->sid, pgrpsid) == 0) + *aclflag |= CIFS_ACL_DACL; + } else if (pngrpsid && compare_sids(&pntace->sid, pgrpsid) == 0) { ace_size = cifs_copy_ace(pnntace, pntace, pngrpsid); - else + *aclflag |= CIFS_ACL_DACL; + } else { ace_size = cifs_copy_ace(pnntace, pntace, NULL); + } size += le16_to_cpu(pntace->size); nsize += ace_size; @@ -1521,7 +1525,8 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd, /* Replace ACEs for old owner with new one */ size = replace_sids_and_copy_aces(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr, - nowner_sid_ptr, ngroup_sid_ptr); + nowner_sid_ptr, ngroup_sid_ptr, + aclflag); ndacl_ptr->size = cpu_to_le16(size); } @@ -1738,7 +1743,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, kuid_t uid, kgid_t gid) { int rc = 0; - int aclflag = CIFS_ACL_DACL; /* default flag to set */ + int aclflag = 0; __u32 secdesclen = 0; __u32 nsecdesclen = 0; __u32 dacloffset = 0; @@ -1834,14 +1839,23 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode, cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc); - if (ops->set_acl == NULL) - rc = -EOPNOTSUPP; + if (rc != 0) + goto id_mode_to_cifs_acl_exit; - if (!rc) { - /* Set the security descriptor */ - rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag); - cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc); + if (aclflag == 0) { + cifs_dbg(FYI, "set_cifs_acl aclflag=0, no change mapped\n"); + goto id_mode_to_cifs_acl_exit; } + + if (ops->set_acl == NULL) { + rc = -EOPNOTSUPP; + goto id_mode_to_cifs_acl_exit; + } + + /* Set the security descriptor */ + rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag); + cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc); + id_mode_to_cifs_acl_exit: cifs_put_tlink(tlink); diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h index befc5eecb55c..99f9e6dca62b 100644 --- a/fs/smb/client/cifsglob.h +++ b/fs/smb/client/cifsglob.h @@ -1393,6 +1393,7 @@ struct cifs_search_info { bool emptyDir:1; bool unicode:1; bool smallBuf:1; /* so we know which buf_release function to call */ + bool is_dynamic_buf:1; /* dynamically allocated buffer - can be variable size */ }; #define ACL_NO_MODE ((umode_t)(-1)) @@ -1906,6 +1907,7 @@ enum cifs_find_flags { #define CIFS_NO_BUFFER 0 /* Response buffer not returned */ #define CIFS_SMALL_BUFFER 1 #define CIFS_LARGE_BUFFER 2 +#define CIFS_DYNAMIC_BUFFER 3 /* Dynamically allocated buffer */ #define CIFS_IOVEC 4 /* array of response buffers */ /* Type of Request to SendReceive2 */ diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c index 74197766471c..85aec302c89e 100644 --- a/fs/smb/client/connect.c +++ b/fs/smb/client/connect.c @@ -457,8 +457,8 @@ static int __reconnect_target_locked(struct TCP_Server_Info *server, server->hostname = hostname; spin_unlock(&server->srv_lock); } else { - cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n", - __func__, PTR_ERR(hostname)); + cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %pe\n", + __func__, hostname); cifs_dbg(FYI, "%s: default to last target server: %s\n", __func__, server->hostname); } diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index 58430ba51b10..8b25d6c9ec5e 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -1563,6 +1563,8 @@ int cifs_closedir(struct inode *inode, struct file *file) cfile->srch_inf.ntwrk_buf_start = NULL; if (cfile->srch_inf.smallBuf) cifs_small_buf_release(buf); + else if (cfile->srch_inf.is_dynamic_buf) + kfree(buf); else cifs_buf_release(buf); } diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 51fb7c418d52..1dbcfd163ff0 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -3038,13 +3038,20 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start, void cifs_setsize(struct inode *inode, loff_t offset) { + loff_t old_size; + u64 blocks = CIFS_INO_BLOCKS(offset); + spin_lock(&inode->i_lock); + old_size = i_size_read(inode); i_size_write(inode, offset); + /* - * Until we can query the server for actual allocation size, - * this is best estimate we have for blocks allocated for a file. + * Extending EOF does not allocate the intervening range. Only clamp + * i_blocks on shrink; allocation growth comes from writes or from the + * server-reported AllocationSize. */ - inode->i_blocks = CIFS_INO_BLOCKS(offset); + if (offset < old_size && (u64)inode->i_blocks > blocks) + inode->i_blocks = blocks; spin_unlock(&inode->i_lock); inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); truncate_pagecache(inode, offset); @@ -3376,7 +3383,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) if (attrs->ia_valid & ATTR_GID) gid = attrs->ia_gid; - if (sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) { + if ((sbflags & (CIFS_MOUNT_CIFS_ACL | CIFS_MOUNT_MODE_FROM_SID)) || + cifs_sb_master_tcon(cifs_sb)->posix_extensions) { if (uid_valid(uid) || gid_valid(gid)) { mode = NO_CHANGE_64; rc = id_mode_to_cifs_acl(inode, full_path, &mode, diff --git a/fs/smb/client/readdir.c b/fs/smb/client/readdir.c index 1ff77f3d1de0..a50c86bbe60f 100644 --- a/fs/smb/client/readdir.c +++ b/fs/smb/client/readdir.c @@ -732,6 +732,8 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, if (cfile->srch_inf.smallBuf) cifs_small_buf_release(cfile->srch_inf. ntwrk_buf_start); + else if (cfile->srch_inf.is_dynamic_buf) + kfree(cfile->srch_inf.ntwrk_buf_start); else cifs_buf_release(cfile->srch_inf. ntwrk_buf_start); diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index a8f8feeeccb5..06e9322a762a 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -2117,8 +2117,9 @@ smb2_sync_write(const unsigned int xid, struct cifs_fid *pfid, } /* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */ -static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse) +static int smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, struct inode *inode, + __u8 setsparse) { struct cifsInodeInfo *cifsi; int rc; @@ -2127,31 +2128,31 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, /* if file already sparse don't bother setting sparse again */ if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && setsparse) - return true; /* already sparse */ + return 0; /* already sparse */ if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && !setsparse) - return true; /* already not sparse */ + return 0; /* already not sparse */ /* * Can't check for sparse support on share the usual way via the * FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share * since Samba server doesn't set the flag on the share, yet * supports the set sparse FSCTL and returns sparse correctly - * in the file attributes. If we fail setting sparse though we - * mark that server does not support sparse files for this share - * to avoid repeatedly sending the unsupported fsctl to server - * if the file is repeatedly extended. + * in the file attributes. If the server returns EOPNOTSUPP, mark + * that sparse files are not supported on this share to avoid + * repeatedly sending the unsupported FSCTL. */ if (tcon->broken_sparse_sup) - return false; + return -EOPNOTSUPP; rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, cfile->fid.volatile_fid, FSCTL_SET_SPARSE, &setsparse, 1, CIFSMaxBufSize, NULL, NULL); if (rc) { - tcon->broken_sparse_sup = true; + if (rc == -EOPNOTSUPP) + tcon->broken_sparse_sup = true; cifs_dbg(FYI, "set sparse rc = %d\n", rc); - return false; + return rc; } if (setsparse) @@ -2159,7 +2160,7 @@ static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, else cifsi->cifsAttrs &= (~FILE_ATTRIBUTE_SPARSE_FILE); - return true; + return 0; } static int @@ -3483,10 +3484,9 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, /* Need to make file sparse, if not already, before freeing range. */ /* Consider adding equivalent for compressed since it could also work */ - if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) { - rc = -EOPNOTSUPP; + rc = smb2_set_sparse(xid, tcon, cfile, inode, set_sparse); + if (rc) goto out; - } filemap_invalidate_lock(inode->i_mapping); /* @@ -4071,7 +4071,7 @@ static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode, return smb3_collapse_range(file, tcon, off, len); else if (mode == FALLOC_FL_INSERT_RANGE) return smb3_insert_range(file, tcon, off, len); - else if (mode == 0) + else if (mode == FALLOC_FL_ALLOCATE_RANGE) return smb3_simple_falloc(file, tcon, off, len, false); return -EOPNOTSUPP; @@ -5111,6 +5111,12 @@ receive_encrypted_standard(struct TCP_Server_Info *server, one_more: shdr = (struct smb2_hdr *)buf; next_cmd = le32_to_cpu(shdr->NextCommand); + + if (*num_mids >= MAX_COMPOUND) { + cifs_server_dbg(VFS, "too many PDUs in compound\n"); + return -1; + } + if (next_cmd) { if (WARN_ON_ONCE(next_cmd > pdu_length)) return -1; @@ -5134,10 +5140,6 @@ one_more: mid_entry->resp_buf_size = server->pdu_size; } - if (*num_mids >= MAX_COMPOUND) { - cifs_server_dbg(VFS, "too many PDUs in compound\n"); - return -1; - } bufs[*num_mids] = buf; mids[(*num_mids)++] = mid_entry; diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 3c7691b39377..d058584b8f05 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -3305,6 +3305,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); oparms->replay = !!(retries); @@ -3530,6 +3532,8 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); @@ -3724,6 +3728,8 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; query_attrs = false; server = cifs_pick_channel(ses); @@ -3936,6 +3942,8 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; allocated = false; server = cifs_pick_channel(ses); @@ -4108,6 +4116,8 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); @@ -4450,6 +4460,8 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); @@ -5661,6 +5673,8 @@ smb2_parse_query_directory(struct cifs_tcon *tcon, if (srch_inf->ntwrk_buf_start) { if (srch_inf->smallBuf) cifs_small_buf_release(srch_inf->ntwrk_buf_start); + else if (srch_inf->is_dynamic_buf) + kfree(srch_inf->ntwrk_buf_start); else cifs_buf_release(srch_inf->ntwrk_buf_start); } @@ -5680,12 +5694,18 @@ smb2_parse_query_directory(struct cifs_tcon *tcon, cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n", srch_inf->entries_in_buffer, srch_inf->index_of_last_entry, srch_inf->srch_entries_start, srch_inf->last_entry); - if (resp_buftype == CIFS_LARGE_BUFFER) + if (resp_buftype == CIFS_LARGE_BUFFER) { srch_inf->smallBuf = false; - else if (resp_buftype == CIFS_SMALL_BUFFER) + srch_inf->is_dynamic_buf = false; + } else if (resp_buftype == CIFS_SMALL_BUFFER) { srch_inf->smallBuf = true; - else + srch_inf->is_dynamic_buf = false; + } else if (resp_buftype == CIFS_DYNAMIC_BUFFER) { + srch_inf->smallBuf = false; + srch_inf->is_dynamic_buf = true; + } else { cifs_tcon_dbg(VFS, "Invalid search buffer type\n"); + } return 0; } @@ -5708,6 +5728,8 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, replay_again: /* reinitialize for possible replay */ + resp_buftype = CIFS_NO_BUFFER; + memset(&rsp_iov, 0, sizeof(rsp_iov)); flags = 0; server = cifs_pick_channel(ses); diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h index 325ff83b12fe..e7ff52b8aba5 100644 --- a/fs/smb/common/smb2pdu.h +++ b/fs/smb/common/smb2pdu.h @@ -1244,6 +1244,30 @@ struct create_mxac_req { } __packed; /* + * AAPL flags. See Samba libcli/smb/smb2_create_ctx.h + */ + +/* "AAPL" Context Command Codes */ +#define SMB2_CRTCTX_AAPL_SERVER_QUERY 1 +#define SMB2_CRTCTX_AAPL_RESOLVE_ID 2 + +/* "AAPL" Server Query request/response bitmap */ +#define SMB2_CRTCTX_AAPL_SERVER_CAPS 1 +#define SMB2_CRTCTX_AAPL_VOLUME_CAPS 2 +#define SMB2_CRTCTX_AAPL_MODEL_INFO 4 + +/* "AAPL" Client/Server Capabilities bitmap */ +#define SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR 1 +#define SMB2_CRTCTX_AAPL_SUPPORTS_OSX_COPYFILE 2 +#define SMB2_CRTCTX_AAPL_UNIX_BASED 4 +#define SMB2_CRTCTX_AAPL_SUPPORTS_NFS_ACE 8 + +/* "AAPL" Volume Capabilities bitmap */ +#define SMB2_CRTCTX_AAPL_SUPPORT_RESOLVE_ID 1 +#define SMB2_CRTCTX_AAPL_CASE_SENSITIVE 2 +#define SMB2_CRTCTX_AAPL_FULL_SYNC 4 + +/* * Flags * See MS-SMB2 2.2.13.2.11 * MS-SMB2 2.2.13.2.12 diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c index e99409fa721c..86f521e849d5 100644 --- a/fs/smb/server/auth.c +++ b/fs/smb/server/auth.c @@ -142,6 +142,7 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, { char ntlmv2_hash[CIFS_ENCPWD_SIZE]; char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE]; + char sess_key[SMB2_NTLMV2_SESSKEY_SIZE]; struct hmac_md5_ctx ctx; int rc; @@ -164,12 +165,21 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess, /* Generate the session key */ hmac_md5_usingrawkey(ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE, - sess->sess_key); + sess_key); if (crypto_memneq(ntlmv2->ntlmv2_hash, ntlmv2_rsp, - CIFS_HMAC_MD5_HASH_SIZE)) - return -EINVAL; - return 0; + CIFS_HMAC_MD5_HASH_SIZE)) { + rc = -EINVAL; + goto out; + } + + memcpy(sess->sess_key, sess_key, sizeof(sess_key)); + rc = 0; +out: + memzero_explicit(ntlmv2_hash, sizeof(ntlmv2_hash)); + memzero_explicit(ntlmv2_rsp, sizeof(ntlmv2_rsp)); + memzero_explicit(sess_key, sizeof(sess_key)); + return rc; } /** @@ -226,6 +236,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob, nt_len - CIFS_ENCPWD_SIZE, domain_name, conn->ntlmssp.cryptkey); kfree(domain_name); + if (ret) + return ret; /* The recovered secondary session key */ if (conn->ntlmssp.client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) { diff --git a/fs/smb/server/ksmbd_work.c b/fs/smb/server/ksmbd_work.c index a5ab6799a65c..e2c2f45264be 100644 --- a/fs/smb/server/ksmbd_work.c +++ b/fs/smb/server/ksmbd_work.c @@ -16,6 +16,36 @@ static struct kmem_cache *work_cache; static struct workqueue_struct *ksmbd_wq; +static int ksmbd_reserve_iov(struct ksmbd_work *work, int need_iov_cnt) +{ + struct kvec *new; + int new_alloc_cnt = work->iov_alloc_cnt; + + if (work->iov_alloc_cnt >= work->iov_cnt + need_iov_cnt) + return 0; + + do { + new_alloc_cnt += KSMBD_WORK_INLINE_IOVS; + } while (new_alloc_cnt < work->iov_cnt + need_iov_cnt); + + if (work->iov == work->iov_inline) { + new = kcalloc(new_alloc_cnt, sizeof(*new), KSMBD_DEFAULT_GFP); + if (!new) + return -ENOMEM; + + memcpy(new, work->iov_inline, sizeof(work->iov_inline)); + } else { + new = krealloc(work->iov, sizeof(*new) * new_alloc_cnt, + KSMBD_DEFAULT_GFP | __GFP_ZERO); + if (!new) + return -ENOMEM; + } + + work->iov = new; + work->iov_alloc_cnt = new_alloc_cnt; + return 0; +} + struct ksmbd_work *ksmbd_alloc_work_struct(void) { struct ksmbd_work *work = kmem_cache_zalloc(work_cache, KSMBD_DEFAULT_GFP); @@ -27,13 +57,8 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void) INIT_LIST_HEAD(&work->async_request_entry); INIT_LIST_HEAD(&work->fp_entry); INIT_LIST_HEAD(&work->aux_read_list); - work->iov_alloc_cnt = 4; - work->iov = kzalloc_objs(struct kvec, work->iov_alloc_cnt, - KSMBD_DEFAULT_GFP); - if (!work->iov) { - kmem_cache_free(work_cache, work); - work = NULL; - } + work->iov_alloc_cnt = ARRAY_SIZE(work->iov_inline); + work->iov = work->iov_inline; } return work; } @@ -55,7 +80,8 @@ void ksmbd_free_work_struct(struct ksmbd_work *work) kfree(work->tr_buf); kvfree(work->compress_buf); kvfree(work->request_buf); - kfree(work->iov); + if (work->iov != work->iov_inline) + kfree(work->iov); if (work->async_id) ksmbd_release_id(&work->conn->async_ida, work->async_id); @@ -117,19 +143,9 @@ static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len, return -ENOMEM; } - if (work->iov_alloc_cnt < work->iov_cnt + need_iov_cnt) { - struct kvec *new; - - work->iov_alloc_cnt += 4; - new = krealloc(work->iov, - sizeof(struct kvec) * work->iov_alloc_cnt, - KSMBD_DEFAULT_GFP | __GFP_ZERO); - if (!new) { - kfree(ar); - work->iov_alloc_cnt -= 4; - return -ENOMEM; - } - work->iov = new; + if (ksmbd_reserve_iov(work, need_iov_cnt)) { + kfree(ar); + return -ENOMEM; } /* Plus rfc_length size on first iov */ diff --git a/fs/smb/server/ksmbd_work.h b/fs/smb/server/ksmbd_work.h index 0da8cc0972d6..df0554a2c50d 100644 --- a/fs/smb/server/ksmbd_work.h +++ b/fs/smb/server/ksmbd_work.h @@ -13,6 +13,8 @@ struct ksmbd_conn; struct ksmbd_session; struct ksmbd_tree_connect; +#define KSMBD_WORK_INLINE_IOVS 4 + enum { KSMBD_WORK_ACTIVE = 0, KSMBD_WORK_CANCELLED, @@ -42,6 +44,7 @@ struct ksmbd_work { int iov_alloc_cnt; int iov_cnt; int iov_idx; + struct kvec iov_inline[KSMBD_WORK_INLINE_IOVS]; /* Next cmd hdr in compound req buf*/ int next_smb2_rcv_hdr_off; @@ -57,6 +60,7 @@ struct ksmbd_work { u64 compound_fid; u64 compound_pfid; u64 compound_sid; + __le32 compound_status; const struct cred *saved_cred; diff --git a/fs/smb/server/misc.c b/fs/smb/server/misc.c index 966004c414a8..2dd91c9e956a 100644 --- a/fs/smb/server/misc.c +++ b/fs/smb/server/misc.c @@ -121,7 +121,9 @@ int parse_stream_name(char *filename, char **stream_name, int *s_type) char *stream_type; char *s_name; int rc = 0; + bool has_stream_type = false; + *stream_name = NULL; s_name = filename; filename = strsep(&s_name, ":"); ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name); @@ -137,14 +139,20 @@ int parse_stream_name(char *filename, char **stream_name, int *s_type) ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name, stream_type); - if (!strncasecmp("$data", stream_type, 5)) + if (!strncasecmp("$data", stream_type, 5)) { *s_type = DATA_STREAM; - else if (!strncasecmp("$index_allocation", stream_type, 17)) + has_stream_type = true; + } else if (!strncasecmp("$index_allocation", stream_type, 17)) { *s_type = DIR_STREAM; - else + has_stream_type = true; + } else { rc = -ENOENT; + } } + if (has_stream_type && !s_name[0] && *s_type == DATA_STREAM) + goto out; + *stream_name = s_name; out: return rc; diff --git a/fs/smb/server/oplock.c b/fs/smb/server/oplock.c index 60e7e821c245..31dd9f3479b2 100644 --- a/fs/smb/server/oplock.c +++ b/fs/smb/server/oplock.c @@ -5,6 +5,7 @@ */ #include <linux/moduleparam.h> +#include <linux/err.h> #include "glob.h" #include "oplock.h" @@ -19,6 +20,53 @@ static LIST_HEAD(lease_table_list); static DEFINE_RWLOCK(lease_list_lock); +#define SMB2_LEASE_STATE_MASK_LE (SMB2_LEASE_READ_CACHING_LE | \ + SMB2_LEASE_HANDLE_CACHING_LE | \ + SMB2_LEASE_WRITE_CACHING_LE) + +static bool lease_state_valid(__le32 state) +{ + return !(state & ~SMB2_LEASE_STATE_MASK_LE); +} + +static __le32 lease_state_grantable(__le32 state) +{ + if (state == SMB2_LEASE_READ_CACHING_LE || + state == (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE) || + state == (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_WRITE_CACHING_LE) || + state == SMB2_LEASE_STATE_MASK_LE) + return state; + + return 0; +} + +static bool lease_v2_flags_valid(__le32 flags) +{ + return !(flags & ~SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE); +} + +static bool lease_has_parent_key(struct lease *lease) +{ + return lease->flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE; +} + +static bool lease_break_in_progress(struct lease *lease) +{ + struct oplock_info *opinfo; + bool ret = false; + + spin_lock(&lease->lock); + list_for_each_entry(opinfo, &lease->open_list, lease_entry) { + if (opinfo->op_state == OPLOCK_ACK_WAIT) { + ret = true; + break; + } + } + spin_unlock(&lease->lock); + + return ret; +} + /** * alloc_opinfo() - allocate a new opinfo object for oplock info * @work: smb work @@ -45,6 +93,7 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, opinfo->fid = id; opinfo->Tid = Tid; INIT_LIST_HEAD(&opinfo->op_entry); + INIT_LIST_HEAD(&opinfo->lease_entry); init_waitqueue_head(&opinfo->oplock_q); init_waitqueue_head(&opinfo->oplock_brk); atomic_set(&opinfo->refcount, 1); @@ -53,31 +102,44 @@ static struct oplock_info *alloc_opinfo(struct ksmbd_work *work, return opinfo; } -static void lease_add_list(struct oplock_info *opinfo) +static void lease_get(struct lease *lease) { - struct lease_table *lb = opinfo->o_lease->l_lb; + atomic_inc(&lease->refcount); +} +static void lease_put(struct lease *lease) +{ + if (lease && atomic_dec_and_test(&lease->refcount)) + kfree(lease); +} + +static void lease_add_table(struct lease *lease, struct lease_table *lb) +{ + lease_get(lease); + lease->l_lb = lb; spin_lock(&lb->lb_lock); - list_add_rcu(&opinfo->lease_entry, &lb->lease_list); + list_add_rcu(&lease->l_entry, &lb->lease_list); spin_unlock(&lb->lb_lock); } -static void lease_del_list(struct oplock_info *opinfo) +static void lease_del_table(struct lease *lease) { - struct lease_table *lb = opinfo->o_lease->l_lb; + struct lease_table *lb = lease->l_lb; if (!lb) return; spin_lock(&lb->lb_lock); - if (list_empty(&opinfo->lease_entry)) { + if (list_empty(&lease->l_entry)) { spin_unlock(&lb->lb_lock); return; } - list_del_init(&opinfo->lease_entry); - opinfo->o_lease->l_lb = NULL; + list_del_init(&lease->l_entry); + lease->l_lb = NULL; spin_unlock(&lb->lb_lock); + + lease_put(lease); } static struct lease_table *alloc_lease_table(struct oplock_info *opinfo) @@ -90,18 +152,29 @@ static struct lease_table *alloc_lease_table(struct oplock_info *opinfo) memcpy(lb->client_guid, opinfo->conn->ClientGUID, SMB2_CLIENT_GUID_SIZE); + lb->conn = ksmbd_conn_get(opinfo->conn); INIT_LIST_HEAD(&lb->lease_list); spin_lock_init(&lb->lb_lock); return lb; } -static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) +static void free_lease_table(struct lease_table *lb) +{ + if (!lb) + return; + + ksmbd_conn_put(lb->conn); + kfree(lb); +} + +static struct lease *alloc_lease(struct lease_ctx_info *lctx, + struct ksmbd_inode *ci) { struct lease *lease; lease = kmalloc_obj(struct lease, KSMBD_DEFAULT_GFP); if (!lease) - return -ENOMEM; + return NULL; memcpy(lease->lease_key, lctx->lease_key, SMB2_LEASE_KEY_SIZE); lease->state = lctx->req_state; @@ -111,19 +184,50 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx) lease->is_dir = lctx->is_dir; memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE); lease->version = lctx->version; - lease->epoch = le16_to_cpu(lctx->epoch) + 1; - INIT_LIST_HEAD(&opinfo->lease_entry); - opinfo->o_lease = lease; + lease->epoch = lctx->version == 2 ? le16_to_cpu(lctx->epoch) + 1 : 0; + lease->ci = ci; + lease->reuse_epoch = false; + lease->l_lb = NULL; + INIT_LIST_HEAD(&lease->l_entry); + INIT_LIST_HEAD(&lease->open_list); + spin_lock_init(&lease->lock); + atomic_set(&lease->refcount, 1); + + return lease; +} - return 0; +static void lease_add_open(struct lease *lease, struct oplock_info *opinfo) +{ + spin_lock(&lease->lock); + list_add(&opinfo->lease_entry, &lease->open_list); + spin_unlock(&lease->lock); } -static void free_lease(struct oplock_info *opinfo) +static void lease_del_open(struct oplock_info *opinfo) { - struct lease *lease; + struct lease *lease = opinfo->o_lease; + bool remove_table = false; - lease = opinfo->o_lease; - kfree(lease); + if (!lease) + return; + + spin_lock(&lease->lock); + if (!list_empty(&opinfo->lease_entry)) { + list_del_init(&opinfo->lease_entry); + remove_table = list_empty(&lease->open_list); + } + spin_unlock(&lease->lock); + + if (remove_table) { + write_lock(&lease_list_lock); + lease_del_table(lease); + write_unlock(&lease_list_lock); + } +} + +static void free_lease(struct oplock_info *opinfo) +{ + lease_put(opinfo->o_lease); } static void __free_opinfo(struct oplock_info *opinfo) @@ -146,6 +250,21 @@ static void free_opinfo(struct oplock_info *opinfo) call_rcu(&opinfo->rcu, free_opinfo_rcu); } +void lease_update_oplock_levels(struct lease *lease) +{ + struct oplock_info *opinfo; + __u8 level; + + if (!lease) + return; + + level = smb2_map_lease_to_oplock(lease->state); + spin_lock(&lease->lock); + list_for_each_entry(opinfo, &lease->open_list, lease_entry) + opinfo->level = level; + spin_unlock(&lease->lock); +} + struct oplock_info *opinfo_get(struct ksmbd_file *fp) { struct oplock_info *opinfo; @@ -193,6 +312,18 @@ void opinfo_put(struct oplock_info *opinfo) free_opinfo(opinfo); } +static bool ksmbd_inode_has_lease(struct ksmbd_inode *ci) +{ + struct oplock_info *opinfo = opinfo_get_list(ci); + bool is_lease; + + if (!opinfo) + return false; + is_lease = opinfo->is_lease; + opinfo_put(opinfo); + return is_lease; +} + static void opinfo_add(struct oplock_info *opinfo, struct ksmbd_file *fp) { struct ksmbd_inode *ci = fp->f_ci; @@ -206,11 +337,9 @@ static void opinfo_del(struct oplock_info *opinfo) { struct ksmbd_inode *ci = opinfo->o_fp->f_ci; - if (opinfo->is_lease) { - write_lock(&lease_list_lock); - lease_del_list(opinfo); - write_unlock(&lease_list_lock); - } + if (opinfo->is_lease) + lease_del_open(opinfo); + down_write(&ci->m_lock); list_del(&opinfo->op_entry); up_write(&ci->m_lock); @@ -259,8 +388,10 @@ int opinfo_write_to_read(struct oplock_info *opinfo) } opinfo->level = SMB2_OPLOCK_LEVEL_II; - if (opinfo->is_lease) + if (opinfo->is_lease) { lease->state = lease->new_state; + lease_update_oplock_levels(lease); + } return 0; } @@ -275,7 +406,7 @@ int opinfo_read_handle_to_read(struct oplock_info *opinfo) struct lease *lease = opinfo->o_lease; lease->state = lease->new_state; - opinfo->level = SMB2_OPLOCK_LEVEL_II; + lease_update_oplock_levels(lease); return 0; } @@ -297,8 +428,10 @@ int opinfo_write_to_none(struct oplock_info *opinfo) return -EINVAL; } opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - if (opinfo->is_lease) + if (opinfo->is_lease) { lease->state = lease->new_state; + lease_update_oplock_levels(lease); + } return 0; } @@ -319,8 +452,10 @@ int opinfo_read_to_none(struct oplock_info *opinfo) return -EINVAL; } opinfo->level = SMB2_OPLOCK_LEVEL_NONE; - if (opinfo->is_lease) + if (opinfo->is_lease) { lease->state = lease->new_state; + lease_update_oplock_levels(lease); + } return 0; } @@ -341,10 +476,7 @@ int lease_read_to_write(struct oplock_info *opinfo) lease->new_state = SMB2_LEASE_NONE_LE; lease->state |= SMB2_LEASE_WRITE_CACHING_LE; - if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; - else - opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; + lease_update_oplock_levels(lease); return 0; } @@ -366,15 +498,7 @@ static int lease_none_upgrade(struct oplock_info *opinfo, __le32 new_state) lease->new_state = SMB2_LEASE_NONE_LE; lease->state = new_state; - if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_BATCH; - else - opinfo->level = SMB2_OPLOCK_LEVEL_II; - else if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_EXCLUSIVE; - else if (lease->state & SMB2_LEASE_READ_CACHING_LE) - opinfo->level = SMB2_OPLOCK_LEVEL_II; + lease_update_oplock_levels(lease); return 0; } @@ -505,7 +629,7 @@ static inline int compare_guid_key(struct oplock_info *opinfo, * Return: oplock(lease) object on success, otherwise NULL */ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, - char *client_guid, + const char *client_guid, struct lease_ctx_info *lctx) { int ret; @@ -555,8 +679,11 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, if (lctx->req_state == (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) { - lease->epoch++; - lease->state = lctx->req_state; + if (lease->state != lctx->req_state) { + lease->epoch++; + lease->state = lctx->req_state; + lease_update_oplock_levels(lease); + } } } @@ -572,7 +699,7 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci, return m_opinfo; } -static void wait_for_break_ack(struct oplock_info *opinfo) +static bool wait_for_break_ack(struct oplock_info *opinfo) { int rc = 0; @@ -583,11 +710,16 @@ static void wait_for_break_ack(struct oplock_info *opinfo) /* is this a timeout ? */ if (!rc) { - if (opinfo->is_lease) + if (opinfo->is_lease) { opinfo->o_lease->state = SMB2_LEASE_NONE_LE; + lease_update_oplock_levels(opinfo->o_lease); + } opinfo->level = SMB2_OPLOCK_LEVEL_NONE; opinfo->op_state = OPLOCK_STATE_NONE; + return true; } + + return false; } static void wake_up_oplock_break(struct oplock_info *opinfo) @@ -601,6 +733,9 @@ static void wake_up_oplock_break(struct oplock_info *opinfo) static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level) { while (test_and_set_bit(0, &opinfo->pending_break)) { + if (opinfo->is_lease) + opinfo->o_lease->reuse_epoch = true; + wait_on_bit(&opinfo->pending_break, 0, TASK_UNINTERRUPTIBLE); /* Not immediately break to none. */ @@ -634,6 +769,17 @@ static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level) return 0; } +static bool lease_break_needed(struct oplock_info *opinfo, int req_op_level, + bool open_trunc) +{ + struct lease *lease = opinfo->o_lease; + + if (open_trunc) + return lease->state != SMB2_LEASE_NONE_LE; + + return opinfo->level > req_op_level; +} + /** * __smb2_oplock_break_noti() - send smb2 oplock break cmd from conn * to client @@ -723,7 +869,7 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo) conn = READ_ONCE(opinfo->conn); if (!conn) - return 0; + return ksmbd_invalidate_durable_fd(opinfo->fid); work = ksmbd_alloc_work_struct(); if (!work) @@ -748,7 +894,8 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo) INIT_WORK(&work->work, __smb2_oplock_break_noti); ksmbd_queue_work(work); - wait_for_break_ack(opinfo); + if (wait_for_break_ack(opinfo)) + ret = ksmbd_invalidate_durable_fd(opinfo->fid); } else { __smb2_oplock_break_noti(&work->work); if (opinfo->level == SMB2_OPLOCK_LEVEL_II) @@ -820,19 +967,26 @@ out: * smb2_lease_break_noti() - break lease when a new client request * write lease * @opinfo: contains lease state information + * @wait_ack: wait for lease break acknowledgment from the client + * @inc_epoch: increment the lease epoch before sending the break * * Return: 0 on success, otherwise error */ -static int smb2_lease_break_noti(struct oplock_info *opinfo) +static int smb2_lease_break_noti(struct oplock_info *opinfo, bool wait_ack, + bool inc_epoch) { struct ksmbd_conn *conn; struct ksmbd_work *work; struct lease_break_info *br_info; struct lease *lease = opinfo->o_lease; + int ret = 0; conn = READ_ONCE(opinfo->conn); + if (lease->version == 2 && lease->l_lb && lease->l_lb->conn && + !ksmbd_conn_releasing(lease->l_lb->conn)) + conn = lease->l_lb->conn; if (!conn) - return 0; + return ksmbd_invalidate_durable_fd(opinfo->fid); work = ksmbd_alloc_work_struct(); if (!work) @@ -846,10 +1000,13 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) br_info->curr_state = lease->state; br_info->new_state = lease->new_state; - if (lease->version == 2) - br_info->epoch = cpu_to_le16(++lease->epoch); - else + if (lease->version == 2) { + if (inc_epoch) + lease->epoch++; + br_info->epoch = cpu_to_le16(lease->epoch); + } else { br_info->epoch = 0; + } memcpy(br_info->lease_key, lease->lease_key, SMB2_LEASE_KEY_SIZE); work->request_buf = (char *)br_info; @@ -860,15 +1017,18 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo) if (opinfo->op_state == OPLOCK_ACK_WAIT) { INIT_WORK(&work->work, __smb2_lease_break_noti); ksmbd_queue_work(work); - wait_for_break_ack(opinfo); + if (wait_ack) { + if (wait_for_break_ack(opinfo)) + ret = ksmbd_invalidate_durable_fd(opinfo->fid); + } } else { __smb2_lease_break_noti(&work->work); if (opinfo->o_lease->new_state == SMB2_LEASE_NONE_LE) { - opinfo->level = SMB2_OPLOCK_LEVEL_NONE; opinfo->o_lease->state = SMB2_LEASE_NONE_LE; + lease_update_oplock_levels(opinfo->o_lease); } } - return 0; + return ret; } static void wait_lease_breaking(struct oplock_info *opinfo) @@ -889,9 +1049,10 @@ static void wait_lease_breaking(struct oplock_info *opinfo) } static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, - struct ksmbd_work *in_work) + struct ksmbd_work *in_work, bool share_break) { int err = 0; + bool sent_interim = false; /* Need to break exclusive/batch oplock, write lease or overwrite_if */ ksmbd_debug(OPLOCK, @@ -900,18 +1061,36 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, if (brk_opinfo->is_lease) { struct lease *lease = brk_opinfo->o_lease; + bool open_trunc = brk_opinfo->open_trunc; + bool was_pending = test_bit(0, &brk_opinfo->pending_break); + bool wait_ack; + bool inc_epoch = true; + + if (in_work && was_pending) { + setup_async_work(in_work, NULL, NULL); + smb2_send_interim_resp(in_work, STATUS_PENDING); + release_async_work(in_work); + sent_interim = true; + } - atomic_inc(&brk_opinfo->breaking_cnt); err = oplock_break_pending(brk_opinfo, req_op_level); if (err) return err < 0 ? err : 0; + if (was_pending) + open_trunc = brk_opinfo->open_trunc; - if (brk_opinfo->open_trunc) { +again: + atomic_inc(&brk_opinfo->breaking_cnt); + if (open_trunc) { /* * Create overwrite break trigger the lease break to * none. */ lease->new_state = SMB2_LEASE_NONE_LE; + } else if (share_break && + lease->state & SMB2_LEASE_HANDLE_CACHING_LE) { + lease->new_state = + lease->state & ~SMB2_LEASE_HANDLE_CACHING_LE; } else { if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) { if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE) @@ -931,17 +1110,56 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, } } + if (in_work && !sent_interim) { + setup_async_work(in_work, NULL, NULL); + smb2_send_interim_resp(in_work, STATUS_PENDING); + release_async_work(in_work); + sent_interim = true; + } + if (lease->state & (SMB2_LEASE_WRITE_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) { - if (in_work) { - setup_async_work(in_work, NULL, NULL); - smb2_send_interim_resp(in_work, STATUS_PENDING); - release_async_work(in_work); - } - brk_opinfo->op_state = OPLOCK_ACK_WAIT; } else atomic_dec(&brk_opinfo->breaking_cnt); + + wait_ack = !(open_trunc && + lease->state == (SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)); + if (lease->reuse_epoch) { + inc_epoch = false; + lease->reuse_epoch = false; + } + err = smb2_lease_break_noti(brk_opinfo, wait_ack, inc_epoch); + inc_epoch = false; + + ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); + if (brk_opinfo->op_state == OPLOCK_CLOSING) + err = -ENOENT; + + if (wait_ack) + wait_lease_breaking(brk_opinfo); + /* + * A share-mode conflict break only drops the conflicting + * caching bit; the triggering open fails with a sharing + * violation, so keep it to a single break. + * + * Otherwise chain another break while the lease is still + * incompatible with this open (req_op_level), or while a + * truncating waiter that arrived during the break still needs + * the lease dropped to none. open_trunc snapshotted for this + * break stays cleared, so the next state is computed from the + * lease state and the cascade steps down (e.g. RH->R->none) + * instead of collapsing straight to none. + */ + if (wait_ack && !err && !share_break && + (lease_break_needed(brk_opinfo, req_op_level, open_trunc) || + (brk_opinfo->open_trunc && + lease->state != SMB2_LEASE_NONE_LE))) + goto again; + + wake_up_oplock_break(brk_opinfo); + return err; } else { err = oplock_break_pending(brk_opinfo, req_op_level); if (err) @@ -952,25 +1170,20 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level, brk_opinfo->op_state = OPLOCK_ACK_WAIT; } - if (brk_opinfo->is_lease) - err = smb2_lease_break_noti(brk_opinfo); - else - err = smb2_oplock_break_noti(brk_opinfo); + err = smb2_oplock_break_noti(brk_opinfo); ksmbd_debug(OPLOCK, "oplock granted = %d\n", brk_opinfo->level); if (brk_opinfo->op_state == OPLOCK_CLOSING) - err = -ENOENT; + err = -EAGAIN; wake_up_oplock_break(brk_opinfo); - wait_lease_breaking(brk_opinfo); - return err; } void destroy_lease_table(struct ksmbd_conn *conn) { struct lease_table *lb, *lbtmp; - struct oplock_info *opinfo; + struct lease *lease, *ltmp; write_lock(&lease_list_lock); if (list_empty(&lease_table_list)) { @@ -982,25 +1195,18 @@ void destroy_lease_table(struct ksmbd_conn *conn) if (conn && memcmp(lb->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) continue; -again: - rcu_read_lock(); - list_for_each_entry_rcu(opinfo, &lb->lease_list, - lease_entry) { - rcu_read_unlock(); - lease_del_list(opinfo); - goto again; - } - rcu_read_unlock(); + list_for_each_entry_safe(lease, ltmp, &lb->lease_list, l_entry) + lease_del_table(lease); list_del(&lb->l_entry); - kfree(lb); + free_lease_table(lb); } write_unlock(&lease_list_lock); } -int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, +int find_same_lease_key(struct ksmbd_conn *conn, struct ksmbd_inode *ci, struct lease_ctx_info *lctx) { - struct oplock_info *opinfo; + struct lease *lease; int err = 0; struct lease_table *lb; @@ -1014,7 +1220,7 @@ int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, } list_for_each_entry(lb, &lease_table_list, l_entry) { - if (!memcmp(lb->client_guid, sess->ClientGUID, + if (!memcmp(lb->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) goto found; } @@ -1023,67 +1229,40 @@ int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, return 0; found: - rcu_read_lock(); - list_for_each_entry_rcu(opinfo, &lb->lease_list, lease_entry) { - if (!atomic_inc_not_zero(&opinfo->refcount)) + list_for_each_entry(lease, &lb->lease_list, l_entry) { + if (lease->ci == ci) continue; - rcu_read_unlock(); - if (opinfo->o_fp->f_ci == ci) - goto op_next; - err = compare_guid_key(opinfo, sess->ClientGUID, - lctx->lease_key); - if (err) { + if (!memcmp(lease->lease_key, lctx->lease_key, + SMB2_LEASE_KEY_SIZE)) { err = -EINVAL; ksmbd_debug(OPLOCK, "found same lease key is already used in other files\n"); - opinfo_put(opinfo); goto out; } -op_next: - opinfo_put(opinfo); - rcu_read_lock(); } - rcu_read_unlock(); out: read_unlock(&lease_list_lock); return err; } -static void copy_lease(struct oplock_info *op1, struct oplock_info *op2) -{ - struct lease *lease1 = op1->o_lease; - struct lease *lease2 = op2->o_lease; - - op2->level = op1->level; - lease2->state = lease1->state; - memcpy(lease2->lease_key, lease1->lease_key, - SMB2_LEASE_KEY_SIZE); - lease2->duration = lease1->duration; - lease2->flags = lease1->flags; - lease2->epoch = lease1->epoch; - lease2->version = lease1->version; -} - -static void add_lease_global_list(struct oplock_info *opinfo, +static void add_lease_global_list(struct lease *lease, struct ksmbd_conn *conn, struct lease_table *new_lb) { struct lease_table *lb; write_lock(&lease_list_lock); list_for_each_entry(lb, &lease_table_list, l_entry) { - if (!memcmp(lb->client_guid, opinfo->conn->ClientGUID, + if (!memcmp(lb->client_guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { - opinfo->o_lease->l_lb = lb; - lease_add_list(opinfo); + lease_add_table(lease, lb); write_unlock(&lease_list_lock); - kfree(new_lb); + free_lease_table(new_lb); return; } } - opinfo->o_lease->l_lb = new_lb; - lease_add_list(opinfo); + lease_add_table(lease, new_lb); list_add(&new_lb->l_entry, &lease_table_list); write_unlock(&lease_list_lock); } @@ -1135,7 +1314,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, continue; } - oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL); + oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL, false); opinfo_put(opinfo); } } @@ -1176,7 +1355,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp) continue; } - oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL); + oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE, NULL, false); opinfo_put(opinfo); } } @@ -1201,19 +1380,21 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, struct ksmbd_file *fp, __u16 tid, struct lease_ctx_info *lctx, int share_ret) { - struct ksmbd_session *sess = work->sess; int err = 0; + int break_level = SMB2_OPLOCK_LEVEL_II; struct oplock_info *opinfo = NULL, *prev_opinfo = NULL; struct ksmbd_inode *ci = fp->f_ci; struct lease_table *new_lb = NULL; bool prev_op_has_lease; + bool prev_durable_open = false; + bool prev_durable_detached = false; + unsigned long long prev_fid = KSMBD_NO_FID; + bool new_lease = false; __le32 prev_op_state = 0; /* Only v2 leases handle the directory */ if (S_ISDIR(file_inode(fp->filp)->i_mode)) { - if (!lctx || lctx->version != 2 || - (lctx->flags != SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE && - !lctx->epoch)) + if (!lctx || lctx->version != 2) return 0; } @@ -1222,20 +1403,35 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, return -ENOMEM; if (lctx) { - err = alloc_lease(opinfo, lctx); - if (err) + opinfo->o_lease = alloc_lease(lctx, ci); + if (!opinfo->o_lease) { + err = -ENOMEM; goto err_out; + } opinfo->is_lease = 1; + new_lease = true; } /* ci does not have any oplock */ if (!opinfo_count(fp)) goto set_lev; - /* grant none-oplock if second open is trunc */ - if (fp->attrib_only && fp->cdoption != FILE_OVERWRITE_IF_LE && + /* + * A stat open that only requests metadata access must not break the + * existing caching state. READ_CONTROL (reading the security + * descriptor) does not conflict with a lease, but it does conflict + * with an oplock, so only treat a read-control-only open as a stat + * open when the existing holder is a lease. + */ + if (fp->cdoption != FILE_OVERWRITE_IF_LE && fp->cdoption != FILE_OVERWRITE_LE && - fp->cdoption != FILE_SUPERSEDE_LE) { + fp->cdoption != FILE_SUPERSEDE_LE && + (fp->attrib_only || + (!(fp->daccess & ~(FILE_READ_ATTRIBUTES_LE | + FILE_WRITE_ATTRIBUTES_LE | + FILE_SYNCHRONIZE_LE | + FILE_READ_CONTROL_LE)) && + ksmbd_inode_has_lease(ci)))) { req_op_level = SMB2_OPLOCK_LEVEL_NONE; goto set_lev; } @@ -1244,13 +1440,14 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, struct oplock_info *m_opinfo; /* is lease already granted ? */ - m_opinfo = same_client_has_lease(ci, sess->ClientGUID, + m_opinfo = same_client_has_lease(ci, work->conn->ClientGUID, lctx); if (m_opinfo) { - copy_lease(m_opinfo, opinfo); - if (atomic_read(&m_opinfo->breaking_cnt)) - opinfo->o_lease->flags = - SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; + lease_put(opinfo->o_lease); + lease_get(m_opinfo->o_lease); + opinfo->o_lease = m_opinfo->o_lease; + opinfo->level = m_opinfo->level; + new_lease = false; opinfo_put(m_opinfo); goto out; } @@ -1264,7 +1461,6 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, prev_op_has_lease = prev_opinfo->is_lease; if (prev_op_has_lease) prev_op_state = prev_opinfo->o_lease->state; - if (share_ret < 0 && prev_opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { err = share_ret; @@ -1278,10 +1474,32 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid, goto op_break_not_needed; } - err = oplock_break(prev_opinfo, SMB2_OPLOCK_LEVEL_II, work); + if (prev_opinfo->o_fp && prev_opinfo->o_fp != fp && + prev_opinfo->o_fp->is_durable) { + prev_durable_open = true; + prev_durable_detached = !prev_opinfo->o_fp->conn || + !prev_opinfo->o_fp->tcon; + prev_fid = prev_opinfo->fid; + } + + err = oplock_break(prev_opinfo, break_level, work, + share_ret < 0 && prev_opinfo->is_lease); + if (prev_durable_detached || (prev_durable_open && err == -ENOENT)) + ksmbd_invalidate_durable_fd(prev_fid); opinfo_put(prev_opinfo); - if (err == -ENOENT) + if (err == -EAGAIN) { + share_ret = ksmbd_smb_check_shared_mode(fp->filp, fp); + if (share_ret < 0) { + err = share_ret; + goto err_out; + } + goto set_lev; + } + if (err == -ENOENT) { + if (req_op_level != SMB2_OPLOCK_LEVEL_NONE) + req_op_level = SMB2_OPLOCK_LEVEL_II; goto set_lev; + } /* Check all oplock was freed by close */ else if (err < 0) goto err_out; @@ -1310,17 +1528,13 @@ set_lev: out: /* - * Set o_fp before any publication so that concurrent readers - * (e.g. find_same_lease_key() on the lease list) that - * dereference opinfo->o_fp don't hit a NULL pointer. - * * Keep the original publication order so concurrent opens can * still observe the in-flight grant via ci->m_op_list, but make * everything after opinfo_add() no-fail by preallocating any new * lease_table first. */ opinfo->o_fp = fp; - if (opinfo->is_lease) { + if (new_lease) { new_lb = alloc_lease_table(opinfo); if (!new_lb) { err = -ENOMEM; @@ -1331,8 +1545,10 @@ out: opinfo_count_inc(fp); opinfo_add(opinfo, fp); + if (new_lease) + add_lease_global_list(opinfo->o_lease, opinfo->conn, new_lb); if (opinfo->is_lease) - add_lease_global_list(opinfo, new_lb); + lease_add_open(opinfo->o_lease, opinfo); rcu_assign_pointer(fp->f_opinfo, opinfo); @@ -1349,38 +1565,46 @@ err_out: * @fp: ksmbd file pointer * @is_trunc: truncate on open */ -static void smb_break_all_write_oplock(struct ksmbd_work *work, +static bool smb_break_all_write_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, int is_trunc) { struct oplock_info *brk_opinfo; + bool sent_break = false; brk_opinfo = opinfo_get_list(fp->f_ci); if (!brk_opinfo) - return; + return false; if (brk_opinfo->level != SMB2_OPLOCK_LEVEL_BATCH && brk_opinfo->level != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { opinfo_put(brk_opinfo); - return; + return false; } brk_opinfo->open_trunc = is_trunc; - oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II, work); + oplock_break(brk_opinfo, SMB2_OPLOCK_LEVEL_II, work, false); + sent_break = true; opinfo_put(brk_opinfo); + + return sent_break; } /** - * smb_break_all_levII_oplock() - send level2 oplock or read lease break command + * __smb_break_all_levII_oplock() - send level2 oplock or read lease break command * from server to client - * @work: smb work - * @fp: ksmbd file pointer - * @is_trunc: truncate on open + * @work: smb work + * @fp: ksmbd file pointer + * @is_trunc: truncate on open + * @send_interim: send interim response to the client + * @send_oplock_break: send oplock break notification to the client */ -void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, - int is_trunc) +static void __smb_break_all_levII_oplock(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc, + bool send_interim, bool send_oplock_break) { struct oplock_info *op, *brk_op; struct ksmbd_inode *ci; struct ksmbd_conn *conn = work->conn; + bool sent_interim = false; if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS)) @@ -1402,14 +1626,8 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, continue; } - if (brk_op->is_lease && (brk_op->o_lease->state & - (~(SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_HANDLE_CACHING_LE)))) { - ksmbd_debug(OPLOCK, "unexpected lease state(0x%x)\n", - brk_op->o_lease->state); - goto next; - } else if (brk_op->level != - SMB2_OPLOCK_LEVEL_II) { + if (!brk_op->is_lease && + brk_op->level != SMB2_OPLOCK_LEVEL_II) { ksmbd_debug(OPLOCK, "unexpected oplock(0x%x)\n", brk_op->level); goto next; @@ -1428,7 +1646,17 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, SMB2_LEASE_KEY_SIZE)) goto next; brk_op->open_trunc = is_trunc; - oplock_break(brk_op, SMB2_OPLOCK_LEVEL_NONE, NULL); + if (!brk_op->is_lease && !send_oplock_break) { + brk_op->level = SMB2_OPLOCK_LEVEL_NONE; + brk_op->op_state = OPLOCK_STATE_NONE; + } else { + oplock_break(brk_op, + brk_op->is_lease && !is_trunc ? + SMB2_OPLOCK_LEVEL_II : SMB2_OPLOCK_LEVEL_NONE, + send_interim && !sent_interim ? work : NULL, + false); + } + sent_interim = true; next: opinfo_put(brk_op); } @@ -1438,6 +1666,24 @@ next: opinfo_put(op); } +void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, + int is_trunc) +{ + __smb_break_all_levII_oplock(work, fp, is_trunc, true, true); +} + +void smb_break_all_levII_oplock_no_interim(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc) +{ + __smb_break_all_levII_oplock(work, fp, is_trunc, false, true); +} + +void smb_break_all_levII_oplock_for_delete(struct ksmbd_work *work, + struct ksmbd_file *fp) +{ + __smb_break_all_levII_oplock(work, fp, 0, false, false); +} + /** * smb_break_all_oplock() - break both batch/exclusive and level2 oplock * @work: smb work @@ -1445,12 +1691,14 @@ next: */ void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp) { + bool sent_break; + if (!test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_OPLOCKS)) return; - smb_break_all_write_oplock(work, fp, 1); - smb_break_all_levII_oplock(work, fp, 1); + sent_break = smb_break_all_write_oplock(work, fp, 1); + __smb_break_all_levII_oplock(work, fp, 1, !sent_break, true); } /** @@ -1461,15 +1709,13 @@ void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp) */ __u8 smb2_map_lease_to_oplock(__le32 lease_state) { - if (lease_state == (SMB2_LEASE_HANDLE_CACHING_LE | - SMB2_LEASE_READ_CACHING_LE | - SMB2_LEASE_WRITE_CACHING_LE)) { + if ((lease_state & SMB2_LEASE_WRITE_CACHING_LE) && + (lease_state & SMB2_LEASE_HANDLE_CACHING_LE)) { return SMB2_OPLOCK_LEVEL_BATCH; - } else if (lease_state != SMB2_LEASE_WRITE_CACHING_LE && - lease_state & SMB2_LEASE_WRITE_CACHING_LE) { - if (!(lease_state & SMB2_LEASE_HANDLE_CACHING_LE)) - return SMB2_OPLOCK_LEVEL_EXCLUSIVE; - } else if (lease_state & SMB2_LEASE_READ_CACHING_LE) { + } else if (lease_state & SMB2_LEASE_WRITE_CACHING_LE) { + return SMB2_OPLOCK_LEVEL_EXCLUSIVE; + } else if (lease_state & (SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE)) { return SMB2_OPLOCK_LEVEL_II; } return 0; @@ -1484,14 +1730,19 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) { if (lease->version == 2) { struct create_lease_v2 *buf = (struct create_lease_v2 *)rbuf; + __le32 flags = 0; memset(buf, 0, sizeof(struct create_lease_v2)); memcpy(buf->lcontext.LeaseKey, lease->lease_key, SMB2_LEASE_KEY_SIZE); - buf->lcontext.LeaseFlags = lease->flags; + if (lease_has_parent_key(lease)) + flags |= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE; + if (lease_break_in_progress(lease)) + flags |= SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; + buf->lcontext.LeaseFlags = flags; buf->lcontext.Epoch = cpu_to_le16(lease->epoch); buf->lcontext.LeaseState = lease->state; - if (lease->flags == SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) + if (lease_has_parent_key(lease)) memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key, SMB2_LEASE_KEY_SIZE); buf->ccontext.DataOffset = cpu_to_le16(offsetof @@ -1509,7 +1760,9 @@ void create_lease_buf(u8 *rbuf, struct lease *lease) memset(buf, 0, sizeof(struct create_lease)); memcpy(buf->lcontext.LeaseKey, lease->lease_key, SMB2_LEASE_KEY_SIZE); - buf->lcontext.LeaseFlags = lease->flags; + if (lease_break_in_progress(lease)) + buf->lcontext.LeaseFlags = + SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE; buf->lcontext.LeaseState = lease->state; buf->ccontext.DataOffset = cpu_to_le16(offsetof (struct create_lease, lcontext)); @@ -1537,12 +1790,14 @@ struct lease_ctx_info *parse_lease_state(void *open_req) struct lease_ctx_info *lreq; cc = smb2_find_context_vals(req, SMB2_CREATE_REQUEST_LEASE, 4); - if (IS_ERR_OR_NULL(cc)) + if (IS_ERR(cc)) + return ERR_CAST(cc); + if (!cc) return NULL; lreq = kzalloc_obj(struct lease_ctx_info, KSMBD_DEFAULT_GFP); if (!lreq) - return NULL; + return ERR_PTR(-ENOMEM); if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) { struct create_lease_v2 *lc = (struct create_lease_v2 *)cc; @@ -1556,11 +1811,15 @@ struct lease_ctx_info *parse_lease_state(void *open_req) lreq->flags = lc->lcontext.LeaseFlags; lreq->epoch = lc->lcontext.Epoch; lreq->duration = lc->lcontext.LeaseDuration; + if (!lease_state_valid(lreq->req_state) || + !lease_v2_flags_valid(lreq->flags)) + goto err_out; + lreq->req_state = lease_state_grantable(lreq->req_state); if (lreq->flags == SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey, SMB2_LEASE_KEY_SIZE); lreq->version = 2; - } else { + } else if (sizeof(struct lease_context) == le32_to_cpu(cc->DataLength)) { struct create_lease *lc = (struct create_lease *)cc; if (le16_to_cpu(cc->DataOffset) + le32_to_cpu(cc->DataLength) < @@ -1569,14 +1828,18 @@ struct lease_ctx_info *parse_lease_state(void *open_req) memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE); lreq->req_state = lc->lcontext.LeaseState; - lreq->flags = lc->lcontext.LeaseFlags; + lreq->flags = 0; lreq->duration = lc->lcontext.LeaseDuration; + if (!lease_state_valid(lreq->req_state)) + goto err_out; + lreq->req_state = lease_state_grantable(lreq->req_state); lreq->version = 1; - } + } else + goto err_out; return lreq; err_out: kfree(lreq); - return NULL; + return ERR_PTR(-EINVAL); } /** @@ -1601,6 +1864,9 @@ struct create_context *smb2_find_context_vals(void *open_req, const char *tag, i * CreateContextsOffset and CreateContextsLength are guaranteed to * be valid because of ksmbd_smb2_check_message(). */ + if (!req->CreateContextsOffset || !req->CreateContextsLength) + return NULL; + cc = (struct create_context *)((char *)req + le32_to_cpu(req->CreateContextsOffset)); remain_len = le32_to_cpu(req->CreateContextsLength); @@ -1810,8 +2076,8 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, char *lease_key) { struct oplock_info *opinfo = NULL, *ret_op = NULL; + struct lease *lease; struct lease_table *lt; - int ret; read_lock(&lease_list_lock); list_for_each_entry(lt, &lease_table_list, l_entry) { @@ -1824,29 +2090,30 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, return NULL; found: - rcu_read_lock(); - list_for_each_entry_rcu(opinfo, <->lease_list, lease_entry) { - if (!atomic_inc_not_zero(&opinfo->refcount)) + list_for_each_entry(lease, <->lease_list, l_entry) { + if (memcmp(lease->lease_key, lease_key, SMB2_LEASE_KEY_SIZE)) continue; - rcu_read_unlock(); - if (!opinfo->op_state || opinfo->op_state == OPLOCK_CLOSING) - goto op_next; - if (!(opinfo->o_lease->state & - (SMB2_LEASE_HANDLE_CACHING_LE | - SMB2_LEASE_WRITE_CACHING_LE))) - goto op_next; - ret = compare_guid_key(opinfo, conn->ClientGUID, - lease_key); - if (ret) { - ksmbd_debug(OPLOCK, "found opinfo\n"); + if (!(lease->state & (SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE))) + break; + + spin_lock(&lease->lock); + list_for_each_entry(opinfo, &lease->open_list, lease_entry) { + if (!opinfo->op_state || + opinfo->op_state == OPLOCK_CLOSING) + continue; + if (!atomic_inc_not_zero(&opinfo->refcount)) + continue; ret_op = opinfo; + break; + } + spin_unlock(&lease->lock); + if (ret_op) { + ksmbd_debug(OPLOCK, "found opinfo\n"); goto out; } -op_next: - opinfo_put(opinfo); - rcu_read_lock(); + break; } - rcu_read_unlock(); out: read_unlock(&lease_list_lock); @@ -1866,6 +2133,12 @@ int smb2_check_durable_oplock(struct ksmbd_conn *conn, if (!opinfo) return 0; + if (ksmbd_has_other_active_fd(fp)) { + ksmbd_debug(SMB, "Durable handle reconnect failed: competing open\n"); + ret = -EBADF; + goto out; + } + if (ksmbd_vfs_compare_durable_owner(fp, user) == false) { ksmbd_debug(SMB, "Durable handle reconnect failed: owner mismatch\n"); ret = -EBADF; diff --git a/fs/smb/server/oplock.h b/fs/smb/server/oplock.h index d91a8266e065..3f581d22bb67 100644 --- a/fs/smb/server/oplock.h +++ b/fs/smb/server/oplock.h @@ -34,6 +34,7 @@ struct lease_ctx_info { struct lease_table { char client_guid[SMB2_CLIENT_GUID_SIZE]; + struct ksmbd_conn *conn; struct list_head lease_list; struct list_head l_entry; spinlock_t lb_lock; @@ -49,7 +50,13 @@ struct lease { int version; unsigned short epoch; bool is_dir; + bool reuse_epoch; + struct ksmbd_inode *ci; struct lease_table *l_lb; + struct list_head l_entry; + struct list_head open_list; + spinlock_t lock; + atomic_t refcount; }; struct oplock_info { @@ -92,6 +99,10 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, struct lease_ctx_info *lctx, int share_ret); void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp, int is_trunc); +void smb_break_all_levII_oplock_no_interim(struct ksmbd_work *work, + struct ksmbd_file *fp, int is_trunc); +void smb_break_all_levII_oplock_for_delete(struct ksmbd_work *work, + struct ksmbd_file *fp); int opinfo_write_to_read(struct oplock_info *opinfo); int opinfo_read_handle_to_read(struct oplock_info *opinfo); int opinfo_write_to_none(struct oplock_info *opinfo); @@ -105,6 +116,7 @@ void opinfo_put(struct oplock_info *opinfo); void create_lease_buf(u8 *rbuf, struct lease *lease); struct lease_ctx_info *parse_lease_state(void *open_req); __u8 smb2_map_lease_to_oplock(__le32 lease_state); +void lease_update_oplock_levels(struct lease *lease); int lease_read_to_write(struct oplock_info *opinfo); /* Durable related functions */ @@ -116,7 +128,7 @@ void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp); struct create_context *smb2_find_context_vals(void *open_req, const char *tag, int tag_len); struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn, char *lease_key); -int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci, +int find_same_lease_key(struct ksmbd_conn *conn, struct ksmbd_inode *ci, struct lease_ctx_info *lctx); void destroy_lease_table(struct ksmbd_conn *conn); void smb_send_parent_lease_break_noti(struct ksmbd_file *fp, diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index c1c6c1a64f60..5859fa68bb84 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -57,6 +57,10 @@ static void __wbuf(struct ksmbd_work *work, void **req, void **rsp) #define WORK_BUFFERS(w, rq, rs) __wbuf((w), (void **)&(rq), (void **)&(rs)) +#define SMB2_CREATE_FILE_ATTRIBUTE_MASK \ + (FILE_ATTRIBUTE_MASK & ~(FILE_ATTRIBUTE_INTEGRITY_STREAM | \ + FILE_ATTRIBUTE_NO_SCRUB_DATA)) + /** * check_session_id() - check for valid session id in smb header * @conn: connection instance @@ -246,6 +250,13 @@ void set_smb2_rsp_status(struct ksmbd_work *work, __le32 err) { struct smb2_hdr *rsp_hdr; + if (work->next_smb2_rcv_hdr_off) { + rsp_hdr = ksmbd_resp_buf_next(work); + rsp_hdr->Status = err; + smb2_set_err_rsp(work); + return; + } + rsp_hdr = smb_get_msg(work->response_buf); rsp_hdr->Status = err; @@ -405,6 +416,59 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) work->compound_fid = ((struct smb2_create_rsp *)rsp)->VolatileFileId; work->compound_pfid = ((struct smb2_create_rsp *)rsp)->PersistentFileId; work->compound_sid = le64_to_cpu(rsp->SessionId); + work->compound_status = STATUS_SUCCESS; + } else if ((req->Command == SMB2_FLUSH || + req->Command == SMB2_READ || + req->Command == SMB2_WRITE) && + rsp->Status == STATUS_SUCCESS) { + u64 volatile_id = KSMBD_NO_FID; + u64 persistent_id = KSMBD_NO_FID; + + if (req->Command == SMB2_FLUSH) { + struct smb2_flush_req *flush_req = + (struct smb2_flush_req *)req; + + volatile_id = flush_req->VolatileFileId; + persistent_id = flush_req->PersistentFileId; + } else if (req->Command == SMB2_READ) { + struct smb2_read_req *read_req = + (struct smb2_read_req *)req; + + volatile_id = read_req->VolatileFileId; + persistent_id = read_req->PersistentFileId; + } else { + struct smb2_write_req *write_req = + (struct smb2_write_req *)req; + + volatile_id = write_req->VolatileFileId; + persistent_id = write_req->PersistentFileId; + } + + if (has_file_id(volatile_id)) { + work->compound_fid = volatile_id; + work->compound_pfid = persistent_id; + work->compound_sid = le64_to_cpu(rsp->SessionId); + work->compound_status = STATUS_SUCCESS; + } + } else if (req->Command == SMB2_CREATE) { + work->compound_fid = KSMBD_NO_FID; + work->compound_pfid = KSMBD_NO_FID; + work->compound_sid = le64_to_cpu(rsp->SessionId); + work->compound_status = rsp->Status; + } else if (rsp->Status != STATUS_SUCCESS) { + work->compound_sid = le64_to_cpu(rsp->SessionId); + /* + * Only carry the failed status forward when the failing command + * was itself part of the related chain. An unrelated command + * that fails (e.g. a standalone request with a bad session id) + * must not seed the status for a following related command, + * which has to be evaluated on its own (and may legitimately + * fail with a different status such as INVALID_PARAMETER). The + * compound session id is still tracked so a following related + * command can validate it. + */ + if (req->Flags & SMB2_FLAGS_RELATED_OPERATIONS) + work->compound_status = rsp->Status; } len = get_rfc1002_len(work->response_buf) - work->next_smb2_rsp_hdr_off; @@ -430,6 +494,7 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) ksmbd_debug(SMB, "related flag should be set\n"); work->compound_fid = KSMBD_NO_FID; work->compound_pfid = KSMBD_NO_FID; + work->compound_status = STATUS_SUCCESS; } memset((char *)rsp_hdr, 0, sizeof(struct smb2_hdr) + 2); rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER; @@ -449,6 +514,19 @@ static void init_chained_smb2_rsp(struct ksmbd_work *work) memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16); } +static bool smb2_compound_has_failed(struct ksmbd_work *work, + struct smb2_hdr *rsp) +{ + if (!work->next_smb2_rcv_hdr_off || + has_file_id(work->compound_fid) || + work->compound_status == STATUS_SUCCESS) + return false; + + rsp->Status = work->compound_status; + smb2_set_err_rsp(work); + return true; +} + /** * is_chained_smb2_message() - check for chained command * @work: smb work containing smb request buffer @@ -1016,6 +1094,7 @@ static __le32 decode_compress_ctxt(struct ksmbd_conn *conn, int ctxt_len) { int alg_cnt, algs_size, i; + __le16 *algs; if (sizeof(struct smb2_neg_context) + 10 > ctxt_len) { pr_err("Invalid SMB2_COMPRESSION_CAPABILITIES context length\n"); @@ -1040,8 +1119,15 @@ static __le32 decode_compress_ctxt(struct ksmbd_conn *conn, return STATUS_INVALID_PARAMETER; } + /* + * CompressionAlgorithms[] is declared as a fixed 4-element array, but + * the actual element count is variable (clients such as Windows may + * advertise more). The on-wire length was validated above, so walk the + * algorithms through a pointer to avoid a fixed-array bounds check. + */ + algs = pneg_ctxt->CompressionAlgorithms; for (i = 0; i < alg_cnt; i++) { - __le16 alg = pneg_ctxt->CompressionAlgorithms[i]; + __le16 alg = algs[i]; /* * LZ77 is the required general-purpose codec. Pattern_V1 is an @@ -2807,8 +2893,10 @@ struct durable_info { unsigned short int type; bool persistent; bool reconnected; + bool app_instance_id; unsigned int timeout; char *CreateGuid; + char AppInstanceId[SMB2_CREATE_GUID_SIZE]; }; static int parse_durable_handle_context(struct ksmbd_work *work, @@ -2845,9 +2933,8 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } - if (le16_to_cpu(context->DataOffset) + - le32_to_cpu(context->DataLength) < - sizeof(struct create_durable_handle_reconnect_v2)) { + if (le32_to_cpu(context->DataLength) < + sizeof(recon_v2->dcontext)) { err = -EINVAL; goto out; } @@ -2861,6 +2948,13 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } + if (dh_info->fp->durable_volatile_id != + recon_v2->dcontext.Fid.VolatileFileId) { + err = -EBADF; + ksmbd_put_durable_fd(dh_info->fp); + goto out; + } + if (memcmp(dh_info->fp->create_guid, recon_v2->dcontext.CreateGuid, SMB2_CREATE_GUID_SIZE)) { err = -EBADF; @@ -2885,9 +2979,8 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } - if (le16_to_cpu(context->DataOffset) + - le32_to_cpu(context->DataLength) < - sizeof(create_durable_reconn_t)) { + if (le32_to_cpu(context->DataLength) < + sizeof(recon->Data)) { err = -EINVAL; goto out; } @@ -2901,6 +2994,13 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } + if (dh_info->fp->durable_volatile_id != + recon->Data.Fid.VolatileFileId) { + err = -EBADF; + ksmbd_put_durable_fd(dh_info->fp); + goto out; + } + dh_info->type = dh_idx; dh_info->reconnected = true; ksmbd_debug(SMB, "reconnect Persistent-id from reconnect = %llu\n", @@ -2917,9 +3017,8 @@ static int parse_durable_handle_context(struct ksmbd_work *work, goto out; } - if (le16_to_cpu(context->DataOffset) + - le32_to_cpu(context->DataLength) < - sizeof(struct create_durable_req_v2)) { + if (le32_to_cpu(context->DataLength) < + sizeof(durable_v2_blob->dcontext)) { err = -EINVAL; goto out; } @@ -2982,6 +3081,31 @@ out: return err; } +static int parse_app_instance_id(struct smb2_create_req *req, + struct durable_info *dh_info) +{ + struct create_context *context; + char *data; + + context = smb2_find_context_vals(req, SMB2_CREATE_APP_INSTANCE_ID, + SMB2_CREATE_GUID_SIZE); + if (IS_ERR(context)) + return PTR_ERR(context); + if (!context) + return 0; + + if (le32_to_cpu(context->DataLength) < 20) + return -EINVAL; + + data = (char *)context + le16_to_cpu(context->DataOffset); + if (data[0] != 20 || data[1]) + return -EINVAL; + + memcpy(dh_info->AppInstanceId, data + 4, SMB2_CREATE_GUID_SIZE); + dh_info->app_instance_id = true; + return 0; +} + /** * smb2_open() - handler for smb file open request * @work: smb work containing request buffer @@ -3017,7 +3141,7 @@ int smb2_open(struct ksmbd_work *work) char *stream_name = NULL; bool file_present = false, created = false, already_permitted = false; int share_ret, need_truncate = 0; - u64 time; + u64 time, alloc_size = 0; umode_t posix_mode = 0; __le32 daccess, maximal_access = 0; int iov_len = 0; @@ -3108,11 +3232,25 @@ int smb2_open(struct ksmbd_work *work) if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && req->CreateContextsOffset) { lc = parse_lease_state(req); + if (IS_ERR(lc)) { + rc = PTR_ERR(lc); + lc = NULL; + goto err_out2; + } + if (lc && lc->version == 2 && conn->dialect < SMB30_PROT_ID) { + kfree(lc); + lc = NULL; + if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) + req_op_level = SMB2_OPLOCK_LEVEL_NONE; + } rc = parse_durable_handle_context(work, req, lc, &dh_info); if (rc) { ksmbd_debug(SMB, "error parsing durable handle context\n"); goto err_out2; } + rc = parse_app_instance_id(req, &dh_info); + if (rc) + goto err_out2; if (dh_info.reconnected == true) { rc = smb2_check_durable_oplock(conn, share, dh_info.fp, @@ -3139,8 +3277,22 @@ int smb2_open(struct ksmbd_work *work) goto reconnected_fp; } - } else if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) + + if (dh_info.type == DURABLE_REQ_V2 && dh_info.app_instance_id) + ksmbd_close_fd_app_instance_id(dh_info.AppInstanceId); + } else if (req_op_level == SMB2_OPLOCK_LEVEL_LEASE) { lc = parse_lease_state(req); + if (IS_ERR(lc)) { + rc = PTR_ERR(lc); + lc = NULL; + goto err_out2; + } + if (lc && lc->version == 2 && conn->dialect < SMB30_PROT_ID) { + kfree(lc); + lc = NULL; + req_op_level = SMB2_OPLOCK_LEVEL_NONE; + } + } if (le32_to_cpu(req->ImpersonationLevel) > le32_to_cpu(IL_DELEGATE)) { pr_err("Invalid impersonationlevel : 0x%x\n", @@ -3192,7 +3344,15 @@ int smb2_open(struct ksmbd_work *work) goto err_out2; } - if (req->FileAttributes && !(req->FileAttributes & FILE_ATTRIBUTE_MASK_LE)) { + if (req->DesiredAccess == FILE_SYNCHRONIZE_LE && + req->CreateDisposition == FILE_OPEN_IF_LE && + !req->FileAttributes) { + rc = -EACCES; + goto err_out2; + } + + if (req->FileAttributes && + (req->FileAttributes & ~cpu_to_le32(SMB2_CREATE_FILE_ATTRIBUTE_MASK))) { pr_err("Invalid file attribute : 0x%x\n", le32_to_cpu(req->FileAttributes)); rc = -EINVAL; @@ -3250,6 +3410,23 @@ int smb2_open(struct ksmbd_work *work) rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, &path, 1); + + /* + * A durable handle opened with delete-on-close is preserved across a + * disconnect so it can be reclaimed by a durable reconnect. When a new + * delete-on-close open for the same name arrives instead, the + * disconnected handle must give way: close it so its delete-on-close + * removes the file, then re-resolve so this open can create a fresh one. + */ + if (!rc && (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) && + (req->CreateDisposition == FILE_OVERWRITE_IF_LE || + req->CreateDisposition == FILE_OPEN_IF_LE) && + ksmbd_close_disconnected_durable_delete_on_close(path.dentry)) { + path_put(&path); + rc = ksmbd_vfs_kern_path(work, name, LOOKUP_NO_SYMLINKS, + &path, 1); + } + if (!rc) { file_present = true; @@ -3284,7 +3461,14 @@ int smb2_open(struct ksmbd_work *work) rc = 0; } - if (stream_name) { + /* + * An explicit ::$DATA suffix names the unnamed data stream and is + * canonicalized to a NULL stream name (base file), but the request + * still has to be validated against the data-stream type, e.g. opening + * <dir>::$DATA with FILE_DIRECTORY_FILE must fail with + * STATUS_NOT_A_DIRECTORY. + */ + if (stream_name || s_type == DATA_STREAM) { if (req->CreateOptions & FILE_DIRECTORY_FILE_LE) { if (s_type == DATA_STREAM) { rc = -EIO; @@ -3482,14 +3666,16 @@ int smb2_open(struct ksmbd_work *work) if (posix_acl_rc) ksmbd_debug(SMB, "inherit posix acl failed : %d\n", posix_acl_rc); - if (test_share_config_flag(work->tcon->share_conf, - KSMBD_SHARE_FLAG_ACL_XATTR)) { - rc = smb_inherit_dacl(conn, &path, sess->user->uid, - sess->user->gid); - } + rc = smb2_create_sd_buffer(work, req, &path); + if (rc && rc != -ENOENT) + goto err_out; - if (rc) { - rc = smb2_create_sd_buffer(work, req, &path); + if (rc == -ENOENT) { + if (test_share_config_flag(work->tcon->share_conf, + KSMBD_SHARE_FLAG_ACL_XATTR)) { + rc = smb_inherit_dacl(conn, &path, sess->user->uid, + sess->user->gid); + } if (rc) { if (posix_acl_rc) ksmbd_vfs_set_init_posix_acl(idmap, @@ -3580,6 +3766,12 @@ int smb2_open(struct ksmbd_work *work) goto err_out; } + if (!stream_name && daccess & FILE_DELETE_LE && + ksmbd_has_stream_without_delete_share(fp)) { + rc = -EPERM; + goto err_out; + } + if (file_present || created) path_put(&path); @@ -3615,7 +3807,7 @@ int smb2_open(struct ksmbd_work *work) ksmbd_debug(SMB, "lease req for(%s) req oplock state 0x%x, lease state 0x%x\n", name, req_op_level, lc->req_state); - rc = find_same_lease_key(sess, fp->f_ci, lc); + rc = find_same_lease_key(conn, fp->f_ci, lc); if (rc) goto err_out1; } else if (open_flags == O_RDONLY && @@ -3631,8 +3823,10 @@ int smb2_open(struct ksmbd_work *work) goto err_out1; } - if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) + if (req->CreateOptions & FILE_DELETE_ON_CLOSE_LE) { + smb_break_all_levII_oplock_for_delete(work, fp); ksmbd_fd_set_delete_on_close(fp, file_info); + } if (need_truncate) { rc = smb2_create_truncate(&fp->filp->f_path); @@ -3649,7 +3843,6 @@ int smb2_open(struct ksmbd_work *work) rc = PTR_ERR(az_req); goto err_out1; } else if (az_req) { - loff_t alloc_size; int err; if (le16_to_cpu(az_req->ccontext.DataOffset) + @@ -3698,6 +3891,9 @@ int smb2_open(struct ksmbd_work *work) fp->create_time = ksmbd_UnixTimeToNT(stat.btime); else fp->create_time = ksmbd_UnixTimeToNT(stat.ctime); + fp->change_time = ksmbd_UnixTimeToNT(stat.ctime); + fp->allocation_size = S_ISDIR(stat.mode) ? 0 : + (alloc_size ?: stat.blocks << 9); if (req->FileAttributes || fp->f_ci->m_fattr == 0) fp->f_ci->m_fattr = cpu_to_le32(smb2_get_dos_mode(&stat, le32_to_cpu(req->FileAttributes))); @@ -3723,6 +3919,10 @@ int smb2_open(struct ksmbd_work *work) if (dh_info.type == DURABLE_REQ_V2) { memcpy(fp->create_guid, dh_info.CreateGuid, SMB2_CREATE_GUID_SIZE); + if (dh_info.app_instance_id) + memcpy(fp->app_instance_id, + dh_info.AppInstanceId, + SMB2_CREATE_GUID_SIZE); if (dh_info.timeout) fp->durable_timeout = min_t(unsigned int, dh_info.timeout, @@ -3743,10 +3943,20 @@ reconnected_fp: rsp->LastAccessTime = cpu_to_le64(time); time = ksmbd_UnixTimeToNT(stat.mtime); rsp->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - rsp->ChangeTime = cpu_to_le64(time); - rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 : - cpu_to_le64(stat.blocks << 9); + rsp->ChangeTime = cpu_to_le64(fp->change_time); + /* + * The cached allocation size hides filesystem rounding for the + * requested allocation, but it can go stale when the file grows past + * it via writes (e.g. across a durable reconnect). Refresh it once the + * file exceeds the cached value, rounding the end of file up to the + * volume allocation unit (the filesystem block size, matching the + * SectorsPerAllocationUnit/BytesPerSector ksmbd advertises) rather than + * using the raw on-disk block count, which can include filesystem + * preallocation and metadata rounding. + */ + if (!S_ISDIR(stat.mode) && stat.size > fp->allocation_size) + fp->allocation_size = round_up(stat.size, stat.blksize); + rsp->AllocationSize = cpu_to_le64(fp->allocation_size); rsp->EndofFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); rsp->FileAttributes = fp->f_ci->m_fattr; @@ -4519,11 +4729,28 @@ int smb2_query_dir(struct ksmbd_work *work) unsigned char srch_flag; int buffer_sz; struct smb2_query_dir_private query_dir_private = {NULL, }; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; ksmbd_debug(SMB, "Received smb2 query directory request\n"); WORK_BUFFERS(work, req, rsp); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; + + if (work->next_smb2_rcv_hdr_off && + !has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + if (ksmbd_override_fsids(work)) { rsp->hdr.Status = STATUS_NO_MEMORY; smb2_set_err_rsp(work); @@ -4536,7 +4763,7 @@ int smb2_query_dir(struct ksmbd_work *work) goto err_out2; } - dir_fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + dir_fp = ksmbd_lookup_fd_slow(work, id, pid); if (!dir_fp) { rc = -EBADF; goto err_out2; @@ -5013,8 +5240,7 @@ static int get_file_basic_info(struct smb2_query_info_rsp *rsp, basic_info->LastAccessTime = cpu_to_le64(time); time = ksmbd_UnixTimeToNT(stat.mtime); basic_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - basic_info->ChangeTime = cpu_to_le64(time); + basic_info->ChangeTime = cpu_to_le64(fp->change_time); basic_info->Attributes = fp->f_ci->m_fattr; basic_info->Pad = 0; rsp->OutputBufferLength = @@ -5039,7 +5265,7 @@ static int get_file_standard_info(struct smb2_query_info_rsp *rsp, delete_pending = ksmbd_inode_pending_delete(fp); if (ksmbd_stream_fd(fp) == false) { - sinfo->AllocationSize = cpu_to_le64(stat.blocks << 9); + sinfo->AllocationSize = cpu_to_le64(fp->allocation_size); sinfo->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); } else { sinfo->AllocationSize = cpu_to_le64(fp->stream.size); @@ -5116,13 +5342,11 @@ static int get_file_all_info(struct ksmbd_work *work, file_info->LastAccessTime = cpu_to_le64(time); time = ksmbd_UnixTimeToNT(stat.mtime); file_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - file_info->ChangeTime = cpu_to_le64(time); + file_info->ChangeTime = cpu_to_le64(fp->change_time); file_info->Attributes = fp->f_ci->m_fattr; file_info->Pad1 = 0; if (ksmbd_stream_fd(fp) == false) { - file_info->AllocationSize = - cpu_to_le64(stat.blocks << 9); + file_info->AllocationSize = cpu_to_le64(fp->allocation_size); file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); } else { file_info->AllocationSize = cpu_to_le64(fp->stream.size); @@ -5326,11 +5550,10 @@ static int get_file_network_open_info(struct smb2_query_info_rsp *rsp, file_info->LastAccessTime = cpu_to_le64(time); time = ksmbd_UnixTimeToNT(stat.mtime); file_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - file_info->ChangeTime = cpu_to_le64(time); + file_info->ChangeTime = cpu_to_le64(fp->change_time); file_info->Attributes = fp->f_ci->m_fattr; if (ksmbd_stream_fd(fp) == false) { - file_info->AllocationSize = cpu_to_le64(stat.blocks << 9); + file_info->AllocationSize = cpu_to_le64(fp->allocation_size); file_info->EndOfFile = S_ISDIR(stat.mode) ? 0 : cpu_to_le64(stat.size); } else { file_info->AllocationSize = cpu_to_le64(fp->stream.size); @@ -5458,13 +5681,12 @@ static int find_file_posix_info(struct smb2_query_info_rsp *rsp, file_info->LastAccessTime = cpu_to_le64(time); time = ksmbd_UnixTimeToNT(stat.mtime); file_info->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - file_info->ChangeTime = cpu_to_le64(time); + file_info->ChangeTime = cpu_to_le64(fp->change_time); file_info->DosAttributes = fp->f_ci->m_fattr; file_info->Inode = cpu_to_le64(stat.ino); if (ksmbd_stream_fd(fp) == false) { file_info->EndOfFile = cpu_to_le64(stat.size); - file_info->AllocationSize = cpu_to_le64(stat.blocks << 9); + file_info->AllocationSize = cpu_to_le64(fp->allocation_size); } else { file_info->EndOfFile = cpu_to_le64(fp->stream.size); file_info->AllocationSize = cpu_to_le64(fp->stream.size); @@ -6003,6 +6225,9 @@ int smb2_query_info(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; + if (ksmbd_override_fsids(work)) { rc = -ENOMEM; goto err_out; @@ -6107,6 +6332,9 @@ int smb2_close(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; + if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { ksmbd_debug(SMB, "IPC pipe close request\n"); @@ -6173,8 +6401,7 @@ int smb2_close(struct ksmbd_work *work) } rsp->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB; - rsp->AllocationSize = S_ISDIR(stat.mode) ? 0 : - cpu_to_le64(stat.blocks << 9); + rsp->AllocationSize = cpu_to_le64(fp->allocation_size); rsp->EndOfFile = cpu_to_le64(stat.size); rsp->Attributes = fp->f_ci->m_fattr; rsp->CreationTime = cpu_to_le64(fp->create_time); @@ -6182,8 +6409,7 @@ int smb2_close(struct ksmbd_work *work) rsp->LastAccessTime = cpu_to_le64(time); time = ksmbd_UnixTimeToNT(stat.mtime); rsp->LastWriteTime = cpu_to_le64(time); - time = ksmbd_UnixTimeToNT(stat.ctime); - rsp->ChangeTime = cpu_to_le64(time); + rsp->ChangeTime = cpu_to_le64(fp->change_time); ksmbd_fd_put(work, fp); } else { rsp->Flags = 0; @@ -6396,9 +6622,11 @@ static int set_file_basic_info(struct ksmbd_file *fp, attrs.ia_valid |= (ATTR_ATIME | ATTR_ATIME_SET); } - if (file_info->ChangeTime) + if (file_info->ChangeTime) { + fp->change_time = le64_to_cpu(file_info->ChangeTime); inode_set_ctime_to_ts(inode, ksmbd_NTtimeToUnix(file_info->ChangeTime)); + } if (file_info->LastWriteTime) { attrs.ia_mtime = ksmbd_NTtimeToUnix(file_info->LastWriteTime); @@ -6506,6 +6734,8 @@ static int set_file_allocation_info(struct ksmbd_work *work, if (size < alloc_blks * 512) i_size_write(inode, size); } + + fp->allocation_size = le64_to_cpu(file_alloc_info->AllocationSize); return 0; } @@ -6562,7 +6792,8 @@ static int set_rename_info(struct ksmbd_work *work, struct ksmbd_file *fp, return smb2_rename(work, fp, rename_info, work->conn->local_nls); } -static int set_file_disposition_info(struct ksmbd_file *fp, +static int set_file_disposition_info(struct ksmbd_work *work, + struct ksmbd_file *fp, struct smb2_file_disposition_info *file_info) { struct inode *inode; @@ -6574,9 +6805,13 @@ static int set_file_disposition_info(struct ksmbd_file *fp, inode = file_inode(fp->filp); if (file_info->DeletePending) { + if (ksmbd_has_stream_without_delete_share(fp)) + return -ESHARE; + if (S_ISDIR(inode->i_mode) && ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY) return -EBUSY; + smb_break_all_levII_oplock_for_delete(work, fp); ksmbd_set_inode_pending_delete(fp); } else { ksmbd_clear_inode_pending_delete(fp); @@ -6703,7 +6938,7 @@ static int smb2_set_info_file(struct ksmbd_work *work, struct ksmbd_file *fp, if (buf_len < sizeof(struct smb2_file_disposition_info)) return -EMSGSIZE; - return set_file_disposition_info(fp, + return set_file_disposition_info(work, fp, (struct smb2_file_disposition_info *)buffer); } case FILE_FULL_EA_INFORMATION: @@ -6774,6 +7009,8 @@ int smb2_set_info(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); rsp = ksmbd_resp_buf_next(work); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; if (!has_file_id(req->VolatileFileId)) { ksmbd_debug(SMB, "Compound request set FID = %llu\n", work->compound_fid); @@ -6989,7 +7226,7 @@ int smb2_read(struct ksmbd_work *work) size_t length, mincount; ssize_t nbytes = 0, remain_bytes = 0; int err = 0; - bool is_rdma_channel = false; + bool is_rdma_channel = false, async_interim = false; unsigned int max_read_size = conn->vals->max_read_size; unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; void *aux_payload_buf; @@ -7005,6 +7242,8 @@ int smb2_read(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); rsp = ksmbd_resp_buf_next(work); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; if (!has_file_id(req->VolatileFileId)) { ksmbd_debug(SMB, "Compound request set FID = %llu\n", work->compound_fid); @@ -7059,6 +7298,14 @@ int smb2_read(struct ksmbd_work *work) goto out; } + if (work->next_smb2_rcv_hdr_off && !req->hdr.NextCommand) { + err = setup_async_work(work, NULL, NULL); + if (err) + goto out; + smb2_send_interim_resp(work, STATUS_PENDING); + async_interim = true; + } + offset = le64_to_cpu(req->Offset); if (offset < 0) { err = -EINVAL; @@ -7094,6 +7341,8 @@ int smb2_read(struct ksmbd_work *work) kvfree(aux_payload_buf); rsp->hdr.Status = STATUS_END_OF_FILE; smb2_set_err_rsp(work); + if (async_interim) + release_async_work(work); ksmbd_fd_put(work, fp); return -ENODATA; } @@ -7128,6 +7377,8 @@ int smb2_read(struct ksmbd_work *work) kvfree(aux_payload_buf); goto out; } + if (async_interim) + release_async_work(work); /* * RDMA responses are transferred through channel buffers and encrypted * responses use the encryption transform, so only normal SMB transport @@ -7141,6 +7392,8 @@ int smb2_read(struct ksmbd_work *work) return 0; out: + if (async_interim) + release_async_work(work); if (err) { if (err == -EISDIR) rsp->hdr.Status = STATUS_INVALID_DEVICE_REQUEST; @@ -7276,13 +7529,31 @@ int smb2_write(struct ksmbd_work *work) ssize_t nbytes; char *data_buf; bool writethrough = false, is_rdma_channel = false; + bool async_interim = false; int err = 0; unsigned int max_write_size = work->conn->vals->max_write_size; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; ksmbd_debug(SMB, "Received smb2 write request\n"); WORK_BUFFERS(work, req, rsp); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; + + if (work->next_smb2_rcv_hdr_off && + !has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + if (test_share_config_flag(work->tcon->share_conf, KSMBD_SHARE_FLAG_PIPE)) { ksmbd_debug(SMB, "IPC pipe write request\n"); return smb2_write_pipe(work); @@ -7327,7 +7598,7 @@ int smb2_write(struct ksmbd_work *work) goto out; } - fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + fp = ksmbd_lookup_fd_slow(work, id, pid); if (!fp) { err = -ENOENT; goto out; @@ -7339,6 +7610,14 @@ int smb2_write(struct ksmbd_work *work) goto out; } + if (work->next_smb2_rcv_hdr_off && !req->hdr.NextCommand) { + err = setup_async_work(work, NULL, NULL); + if (err) + goto out; + smb2_send_interim_resp(work, STATUS_PENDING); + async_interim = true; + } + if (length > max_write_size) { ksmbd_debug(SMB, "limiting write size to max size(%u)\n", max_write_size); @@ -7387,10 +7666,15 @@ int smb2_write(struct ksmbd_work *work) err = ksmbd_iov_pin_rsp(work, rsp, offsetof(struct smb2_write_rsp, Buffer)); if (err) goto out; + if (async_interim) + release_async_work(work); ksmbd_fd_put(work, fp); return 0; out: + if (async_interim) + release_async_work(work); + if (err == -EAGAIN) rsp->hdr.Status = STATUS_FILE_LOCK_CONFLICT; else if (err == -ENOSPC || err == -EFBIG) @@ -7421,13 +7705,30 @@ int smb2_flush(struct ksmbd_work *work) { struct smb2_flush_req *req; struct smb2_flush_rsp *rsp; + u64 id = KSMBD_NO_FID, pid = KSMBD_NO_FID; int err; WORK_BUFFERS(work, req, rsp); ksmbd_debug(SMB, "Received smb2 flush request(fid : %llu)\n", req->VolatileFileId); - err = ksmbd_vfs_fsync(work, req->VolatileFileId, req->PersistentFileId); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; + + if (work->next_smb2_rcv_hdr_off && + !has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + + err = ksmbd_vfs_fsync(work, id, pid); if (err) goto out; @@ -7459,7 +7760,8 @@ int smb2_cancel(struct ksmbd_work *work) hdr = ksmbd_resp_buf_next(work); ksmbd_debug(SMB, "smb2 cancel called on mid %llu, async flags 0x%x\n", - hdr->MessageId, hdr->Flags); + le64_to_cpu(hdr->MessageId), + le32_to_cpu(hdr->Flags)); if (hdr->Flags & SMB2_FLAGS_ASYNC_COMMAND) { command_list = &conn->async_requests; @@ -7645,11 +7947,29 @@ int smb2_lock(struct ksmbd_work *work) LIST_HEAD(lock_list); LIST_HEAD(rollback_list); int prior_lock = 0, bkt; + unsigned int id = KSMBD_NO_FID, pid = KSMBD_NO_FID; WORK_BUFFERS(work, req, rsp); ksmbd_debug(SMB, "Received smb2 lock request\n"); - fp = ksmbd_lookup_fd_slow(work, req->VolatileFileId, req->PersistentFileId); + + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; + + if (work->next_smb2_rcv_hdr_off && + !has_file_id(req->VolatileFileId)) { + ksmbd_debug(SMB, "Compound request set FID = %llu\n", + work->compound_fid); + id = work->compound_fid; + pid = work->compound_pfid; + } + + if (!has_file_id(id)) { + id = req->VolatileFileId; + pid = req->PersistentFileId; + } + + fp = ksmbd_lookup_fd_slow(work, id, pid); if (!fp) { ksmbd_debug(SMB, "Invalid file id for lock : %llu\n", req->VolatileFileId); err = -ENOENT; @@ -7774,9 +8094,11 @@ int smb2_lock(struct ksmbd_work *work) nolock = 0; list_del(&cmp_lock->flist); list_del(&cmp_lock->clist); + cmp_lock->conn = NULL; spin_unlock(&conn->llist_lock); up_read(&conn_list_lock); + ksmbd_conn_put(conn); locks_free_lock(cmp_lock->fl); kfree(cmp_lock); goto out_check_cl; @@ -7911,6 +8233,7 @@ skip: goto out2; } else if (!rc) { list_add(&smb_lock->llist, &rollback_list); + smb_lock->conn = ksmbd_conn_get(work->conn); spin_lock(&work->conn->llist_lock); list_add_tail(&smb_lock->clist, &work->conn->lock_list); @@ -7965,11 +8288,14 @@ out: } list_del(&smb_lock->llist); - spin_lock(&work->conn->llist_lock); + conn = smb_lock->conn; + spin_lock(&conn->llist_lock); if (!list_empty(&smb_lock->flist)) list_del(&smb_lock->flist); list_del(&smb_lock->clist); - spin_unlock(&work->conn->llist_lock); + smb_lock->conn = NULL; + spin_unlock(&conn->llist_lock); + ksmbd_conn_put(conn); locks_free_lock(smb_lock->fl); if (rlock) @@ -8449,6 +8775,8 @@ int smb2_ioctl(struct ksmbd_work *work) if (work->next_smb2_rcv_hdr_off) { req = ksmbd_req_buf_next(work); rsp = ksmbd_resp_buf_next(work); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; if (!has_file_id(req->VolatileFileId)) { ksmbd_debug(SMB, "Compound request set FID = %llu\n", work->compound_fid); @@ -8546,6 +8874,15 @@ int smb2_ioctl(struct ksmbd_work *work) case FSCTL_CREATE_OR_GET_OBJECT_ID: { struct file_object_buf_type1_ioctl_rsp *obj_buf; + struct ksmbd_file *fp; + + fp = ksmbd_lookup_fd_fast(work, id); + if (!fp) { + ret = -EBADF; + rsp->hdr.Status = STATUS_FILE_CLOSED; + goto out2; + } + ksmbd_fd_put(work, fp); nbytes = sizeof(struct file_object_buf_type1_ioctl_rsp); obj_buf = (struct file_object_buf_type1_ioctl_rsp *) @@ -8871,11 +9208,10 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) struct smb2_oplock_break *rsp; struct ksmbd_file *fp; struct oplock_info *opinfo = NULL; - __le32 err = 0; - int ret = 0; + __le32 status = STATUS_SUCCESS; + int ret; u64 volatile_id, persistent_id; char req_oplevel = 0, rsp_oplevel = 0; - unsigned int oplock_change_type; WORK_BUFFERS(work, req, rsp); @@ -8901,71 +9237,58 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) return; } - if (opinfo->level == SMB2_OPLOCK_LEVEL_NONE) { - rsp->hdr.Status = STATUS_INVALID_OPLOCK_PROTOCOL; + if (opinfo->op_state != OPLOCK_ACK_WAIT) { + ksmbd_debug(SMB, "unexpected oplock state 0x%x\n", + opinfo->op_state); + if (opinfo->level == SMB2_OPLOCK_LEVEL_NONE) + status = STATUS_INVALID_OPLOCK_PROTOCOL; + else + status = STATUS_INVALID_DEVICE_STATE; goto err_out; } - if (opinfo->op_state == OPLOCK_STATE_NONE) { - ksmbd_debug(SMB, "unexpected oplock state 0x%x\n", opinfo->op_state); - rsp->hdr.Status = STATUS_UNSUCCESSFUL; + if (req_oplevel == SMB2_OPLOCK_LEVEL_LEASE) { + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + status = STATUS_INVALID_PARAMETER; goto err_out; } - if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || - opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && - (req_oplevel != SMB2_OPLOCK_LEVEL_II && - req_oplevel != SMB2_OPLOCK_LEVEL_NONE)) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - oplock_change_type = OPLOCK_WRITE_TO_NONE; - } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && - req_oplevel != SMB2_OPLOCK_LEVEL_NONE) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - oplock_change_type = OPLOCK_READ_TO_NONE; - } else if (req_oplevel == SMB2_OPLOCK_LEVEL_II || - req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { - err = STATUS_INVALID_DEVICE_STATE; - if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || - opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && - req_oplevel == SMB2_OPLOCK_LEVEL_II) { - oplock_change_type = OPLOCK_WRITE_TO_READ; - } else if ((opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE || - opinfo->level == SMB2_OPLOCK_LEVEL_BATCH) && - req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { - oplock_change_type = OPLOCK_WRITE_TO_NONE; - } else if (opinfo->level == SMB2_OPLOCK_LEVEL_II && - req_oplevel == SMB2_OPLOCK_LEVEL_NONE) { - oplock_change_type = OPLOCK_READ_TO_NONE; - } else { - oplock_change_type = 0; - } - } else { - oplock_change_type = 0; + if (opinfo->level == SMB2_OPLOCK_LEVEL_NONE) { + status = STATUS_INVALID_OPLOCK_PROTOCOL; + goto err_out; } - switch (oplock_change_type) { - case OPLOCK_WRITE_TO_READ: - ret = opinfo_write_to_read(opinfo); - rsp_oplevel = SMB2_OPLOCK_LEVEL_II; - break; - case OPLOCK_WRITE_TO_NONE: - ret = opinfo_write_to_none(opinfo); - rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; - break; - case OPLOCK_READ_TO_NONE: - ret = opinfo_read_to_none(opinfo); - rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; - break; - default: - pr_err("unknown oplock change 0x%x -> 0x%x\n", - opinfo->level, rsp_oplevel); + if (opinfo->level == SMB2_OPLOCK_LEVEL_EXCLUSIVE && + req_oplevel != SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE) { + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + status = STATUS_INVALID_OPLOCK_PROTOCOL; + goto err_out; } - if (ret < 0) { - rsp->hdr.Status = err; + if (opinfo->level == SMB2_OPLOCK_LEVEL_BATCH && + req_oplevel != SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE && + req_oplevel != SMB2_OPLOCK_LEVEL_EXCLUSIVE) { + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + status = STATUS_INVALID_OPLOCK_PROTOCOL; goto err_out; } + if (opinfo->level == SMB2_OPLOCK_LEVEL_II && + req_oplevel != SMB2_OPLOCK_LEVEL_NONE) { + opinfo->level = SMB2_OPLOCK_LEVEL_NONE; + status = STATUS_INVALID_OPLOCK_PROTOCOL; + goto err_out; + } + + if (req_oplevel == SMB2_OPLOCK_LEVEL_EXCLUSIVE) + rsp_oplevel = SMB2_OPLOCK_LEVEL_NONE; + else + rsp_oplevel = req_oplevel; + + opinfo->level = rsp_oplevel; + rsp->StructureSize = cpu_to_le16(24); rsp->OplockLevel = rsp_oplevel; rsp->Reserved = 0; @@ -8973,27 +9296,33 @@ static void smb20_oplock_break_ack(struct ksmbd_work *work) rsp->VolatileFid = volatile_id; rsp->PersistentFid = persistent_id; ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_oplock_break)); - if (ret) { + if (ret) + ksmbd_debug(SMB, "failed to pin oplock break response: %d\n", + ret); + goto out; + err_out: - smb2_set_err_rsp(work); - } + rsp->hdr.Status = status; + smb2_set_err_rsp(work); +out: opinfo->op_state = OPLOCK_STATE_NONE; wake_up_interruptible_all(&opinfo->oplock_q); opinfo_put(opinfo); ksmbd_fd_put(work, fp); } -static int check_lease_state(struct lease *lease, __le32 req_state) +static bool smb2_lease_state_valid(__le32 state) { - if ((lease->new_state == - (SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE)) && - !(req_state & SMB2_LEASE_WRITE_CACHING_LE)) { - lease->new_state = req_state; - return 0; - } + return !(state & ~(SMB2_LEASE_READ_CACHING_LE | + SMB2_LEASE_HANDLE_CACHING_LE | + SMB2_LEASE_WRITE_CACHING_LE)); +} - if (lease->new_state == req_state) +static int check_lease_state(struct lease *lease, __le32 req_state) +{ + if (smb2_lease_state_valid(req_state) && + !(req_state & ~lease->new_state)) return 0; return 1; @@ -9011,9 +9340,7 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) struct smb2_lease_ack *req; struct smb2_lease_ack *rsp; struct oplock_info *opinfo; - __le32 err = 0; int ret = 0; - unsigned int lease_change_type; __le32 lease_state; struct lease *lease; @@ -9037,80 +9364,23 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) goto err_out; } - if (check_lease_state(lease, req->LeaseState)) { - rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; - ksmbd_debug(OPLOCK, - "req lease state: 0x%x, expected state: 0x%x\n", - req->LeaseState, lease->new_state); - goto err_out; - } - if (!atomic_read(&opinfo->breaking_cnt)) { rsp->hdr.Status = STATUS_UNSUCCESSFUL; goto err_out; } - /* check for bad lease state */ - if (req->LeaseState & - (~(SMB2_LEASE_READ_CACHING_LE | SMB2_LEASE_HANDLE_CACHING_LE))) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - lease_change_type = OPLOCK_WRITE_TO_NONE; - else - lease_change_type = OPLOCK_READ_TO_NONE; - ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", - le32_to_cpu(lease->state), - le32_to_cpu(req->LeaseState)); - } else if (lease->state == SMB2_LEASE_READ_CACHING_LE && - req->LeaseState != SMB2_LEASE_NONE_LE) { - err = STATUS_INVALID_OPLOCK_PROTOCOL; - lease_change_type = OPLOCK_READ_TO_NONE; - ksmbd_debug(OPLOCK, "handle bad lease state 0x%x -> 0x%x\n", - le32_to_cpu(lease->state), - le32_to_cpu(req->LeaseState)); - } else { - /* valid lease state changes */ - err = STATUS_INVALID_DEVICE_STATE; - if (req->LeaseState == SMB2_LEASE_NONE_LE) { - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - lease_change_type = OPLOCK_WRITE_TO_NONE; - else - lease_change_type = OPLOCK_READ_TO_NONE; - } else if (req->LeaseState & SMB2_LEASE_READ_CACHING_LE) { - if (lease->state & SMB2_LEASE_WRITE_CACHING_LE) - lease_change_type = OPLOCK_WRITE_TO_READ; - else - lease_change_type = OPLOCK_READ_HANDLE_TO_READ; - } else { - lease_change_type = 0; - } - } - - switch (lease_change_type) { - case OPLOCK_WRITE_TO_READ: - ret = opinfo_write_to_read(opinfo); - break; - case OPLOCK_READ_HANDLE_TO_READ: - ret = opinfo_read_handle_to_read(opinfo); - break; - case OPLOCK_WRITE_TO_NONE: - ret = opinfo_write_to_none(opinfo); - break; - case OPLOCK_READ_TO_NONE: - ret = opinfo_read_to_none(opinfo); - break; - default: - ksmbd_debug(OPLOCK, "unknown lease change 0x%x -> 0x%x\n", - le32_to_cpu(lease->state), - le32_to_cpu(req->LeaseState)); - } - - if (ret < 0) { - rsp->hdr.Status = err; + if (check_lease_state(lease, req->LeaseState)) { + rsp->hdr.Status = STATUS_REQUEST_NOT_ACCEPTED; + ksmbd_debug(OPLOCK, + "req lease state: 0x%x, expected state: 0x%x\n", + req->LeaseState, lease->new_state); goto err_out; } - lease_state = lease->state; + lease_state = req->LeaseState; + lease->state = lease_state; + lease->new_state = SMB2_LEASE_NONE_LE; + lease_update_oplock_levels(lease); rsp->StructureSize = cpu_to_le16(36); rsp->Reserved = 0; @@ -9119,16 +9389,20 @@ static void smb21_lease_break_ack(struct ksmbd_work *work) rsp->LeaseState = lease_state; rsp->LeaseDuration = 0; ret = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_lease_ack)); - if (ret) { -err_out: - smb2_set_err_rsp(work); - } + if (ret) + goto err_out; opinfo->op_state = OPLOCK_STATE_NONE; wake_up_interruptible_all(&opinfo->oplock_q); atomic_dec(&opinfo->breaking_cnt); wake_up_interruptible_all(&opinfo->oplock_brk); opinfo_put(opinfo); + return; + +err_out: + smb2_set_err_rsp(work); + opinfo_put(opinfo); + return; } /** @@ -9179,6 +9453,9 @@ int smb2_notify(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); + if (smb2_compound_has_failed(work, &rsp->hdr)) + return -EACCES; + if (work->next_smb2_rcv_hdr_off && req->hdr.NextCommand) { rsp->hdr.Status = STATUS_INTERNAL_ERROR; smb2_set_err_rsp(work); @@ -9401,10 +9678,13 @@ void smb3_preauth_hash_rsp(struct ksmbd_work *work) WORK_BUFFERS(work, req, rsp); - if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE && - conn->preauth_info) - ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, - conn->preauth_info->Preauth_HashValue); + if (le16_to_cpu(req->Command) == SMB2_NEGOTIATE_HE) { + ksmbd_conn_lock(conn); + if (conn->preauth_info) + ksmbd_gen_preauth_integrity_hash(conn, work->response_buf, + conn->preauth_info->Preauth_HashValue); + ksmbd_conn_unlock(conn); + } if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE && sess) { __u8 *hash_value; diff --git a/fs/smb/server/smb2pdu.h b/fs/smb/server/smb2pdu.h index 3bed676bb5ad..c2512dbcdec8 100644 --- a/fs/smb/server/smb2pdu.h +++ b/fs/smb/server/smb2pdu.h @@ -23,7 +23,7 @@ #define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */ #define SMB21_DEFAULT_IOSIZE (1024 * 1024) -#define SMB3_DEFAULT_TRANS_SIZE (1024 * 1024) +#define SMB3_DEFAULT_TRANS_SIZE (4 * 1024 * 1024) #define SMB3_MIN_IOSIZE (64 * 1024) #define SMB3_MAX_IOSIZE (8 * 1024 * 1024) #define SMB3_MAX_MSGSIZE (4 * 4096) diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index 74b0307cb100..f5fa22d87603 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -700,10 +700,18 @@ retry: if (err) goto out_drop_write; + if (!work->tcon->posix_extensions && d_is_dir(old_child) && + ksmbd_has_open_files(old_child)) { + err = -EACCES; + goto out3; + } + parent_fp = ksmbd_lookup_fd_inode(old_child->d_parent); if (parent_fp) { - if (parent_fp->daccess & FILE_DELETE_LE) { - pr_err("parent dir is opened with delete access\n"); + if ((parent_fp->daccess & FILE_DELETE_LE) || + (!parent_fp->attrib_only && + !(parent_fp->saccess & FILE_SHARE_DELETE_LE))) { + pr_err("parent dir blocks delete sharing\n"); err = -ESHARE; ksmbd_fd_put(work, parent_fp); goto out3; diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index 8c556e46cc10..fde22742d193 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -10,6 +10,7 @@ #include <linux/vmalloc.h> #include <linux/kthread.h> #include <linux/freezer.h> +#include <linux/dcache.h> #include "glob.h" #include "vfs_cache.h" @@ -37,6 +38,8 @@ static struct ksmbd_file_table global_ft; static atomic_long_t fd_limit; static struct kmem_cache *filp_cache; +static int ksmbd_mark_fp_closed(struct ksmbd_file *fp); + #define OPLOCK_NONE 0 #define OPLOCK_EXCLUSIVE 1 #define OPLOCK_BATCH 2 @@ -251,6 +254,33 @@ void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp) up_write(&ci->m_lock); } +bool ksmbd_has_stream_without_delete_share(struct ksmbd_file *fp) +{ + struct ksmbd_file *prev_fp; + struct ksmbd_inode *ci = fp->f_ci; + bool ret = false; + + if (ksmbd_stream_fd(fp)) + return false; + + down_read(&ci->m_lock); + list_for_each_entry(prev_fp, &ci->m_fp_list, node) { + if (prev_fp == fp || !ksmbd_stream_fd(prev_fp)) + continue; + + if (file_inode(fp->filp) != file_inode(prev_fp->filp)) + continue; + + if (!(prev_fp->saccess & FILE_SHARE_DELETE_LE)) { + ret = true; + break; + } + } + up_read(&ci->m_lock); + + return ret; +} + void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp, int file_info) { @@ -484,10 +514,14 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) * there are not accesses to fp->lock_list. */ list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { - if (!list_empty(&smb_lock->clist) && fp->conn) { - spin_lock(&fp->conn->llist_lock); - list_del(&smb_lock->clist); - spin_unlock(&fp->conn->llist_lock); + struct ksmbd_conn *conn = smb_lock->conn; + + if (conn) { + spin_lock(&conn->llist_lock); + list_del_init(&smb_lock->clist); + smb_lock->conn = NULL; + spin_unlock(&conn->llist_lock); + ksmbd_conn_put(conn); } list_del(&smb_lock->flist); @@ -513,6 +547,63 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp) kmem_cache_free(filp_cache, fp); } +/** + * ksmbd_close_disconnected_durable_delete_on_close() - drop a delete-on-close + * file kept present only by disconnected durable handles + * @dentry: dentry of the file being opened + * + * A durable handle opened with delete-on-close is preserved across a + * disconnect so it can be reclaimed by a durable reconnect. When a new + * (non-reconnect) open arrives for the same name instead, the disconnected + * handle has to give way. Close such handles so their delete-on-close is + * applied and the file is removed once the last handle is gone, letting the + * new open create a fresh file. + * + * The caller's inode reference is dropped before closing so that the final + * close can promote S_DEL_ON_CLS to S_DEL_PENDING and unlink the file. + * + * Return: true if a disconnected durable handle was closed. + */ +bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry) +{ + struct ksmbd_inode *ci; + struct ksmbd_file *fp, *tmp; + LIST_HEAD(dispose); + bool closed = false; + + ci = ksmbd_inode_lookup_lock(dentry); + if (!ci) + return false; + + down_write(&ci->m_lock); + if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_ON_CLS_STREAM | S_DEL_PENDING)) { + list_for_each_entry_safe(fp, tmp, &ci->m_fp_list, node) { + if (fp->conn || !fp->is_durable || + fp->f_state != FP_INITED) + continue; + list_move_tail(&fp->node, &dispose); + } + } + up_write(&ci->m_lock); + + /* + * Drop our lookup reference before closing so the last __ksmbd_close_fd() + * can drop m_count to zero and unlink the delete-on-close file. The + * collected handles still hold references, so ci stays valid until they + * are closed below. + */ + ksmbd_inode_put(ci); + + while (!list_empty(&dispose)) { + fp = list_first_entry(&dispose, struct ksmbd_file, node); + list_del_init(&fp->node); + __ksmbd_close_fd(NULL, fp); + closed = true; + } + + return closed; +} + static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp) { if (fp->f_state != FP_INITED) @@ -576,6 +667,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) { struct ksmbd_file *fp; struct ksmbd_file_table *ft; + bool closed = false; if (!has_file_id(id)) return 0; @@ -590,6 +682,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) fp = NULL; else { fp->f_state = FP_CLOSED; + closed = true; if (!atomic_dec_and_test(&fp->refcount)) fp = NULL; } @@ -597,7 +690,7 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) write_unlock(&ft->lock); if (!fp) - return -EINVAL; + return closed ? 0 : -EINVAL; __put_fd_final(work, fp); return 0; @@ -670,7 +763,8 @@ struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id) struct ksmbd_file *fp; fp = __ksmbd_lookup_fd(&global_ft, id); - if (fp && (fp->conn || + if (fp && (fp->durable_reconnect_disabled || + fp->conn || (fp->durable_scavenger_timeout && (fp->durable_scavenger_timeout < jiffies_to_msecs(jiffies))))) { @@ -689,6 +783,122 @@ void ksmbd_put_durable_fd(struct ksmbd_file *fp) __ksmbd_close_fd(NULL, fp); } +bool ksmbd_has_other_active_fd(struct ksmbd_file *fp) +{ + struct ksmbd_file *lfp; + struct ksmbd_inode *ci = fp->f_ci; + bool ret = false; + + down_read(&ci->m_lock); + list_for_each_entry(lfp, &ci->m_fp_list, node) { + if (lfp == fp) + continue; + + if (lfp->f_state == FP_INITED && + (READ_ONCE(lfp->conn) || READ_ONCE(lfp->tcon))) { + ret = true; + break; + } + } + up_read(&ci->m_lock); + + return ret; +} + +static struct ksmbd_file *ksmbd_lookup_fd_app_instance_id(char *app_instance_id) +{ + struct ksmbd_file *fp = NULL; + unsigned int id; + + if (!memchr_inv(app_instance_id, 0, SMB2_CREATE_GUID_SIZE)) + return NULL; + + read_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { + if (!memcmp(fp->app_instance_id, app_instance_id, + SMB2_CREATE_GUID_SIZE)) { + fp = ksmbd_fp_get(fp); + break; + } + } + read_unlock(&global_ft.lock); + + return fp; +} + +int ksmbd_close_fd_app_instance_id(char *app_instance_id) +{ + struct ksmbd_file_table *ft; + struct ksmbd_file *fp; + struct oplock_info *opinfo; + int n_to_drop = 0; + + fp = ksmbd_lookup_fd_app_instance_id(app_instance_id); + if (!fp) + return 0; + + opinfo = opinfo_get(fp); + if (!opinfo || !opinfo->sess) + goto out; + + ft = &opinfo->sess->file_table; + write_lock(&ft->lock); + if (fp->f_state == FP_INITED) { + if (has_file_id(fp->volatile_id)) { + idr_remove(ft->idr, fp->volatile_id); + fp->volatile_id = KSMBD_NO_FID; + } + n_to_drop = ksmbd_mark_fp_closed(fp); + } + write_unlock(&ft->lock); + opinfo_put(opinfo); + opinfo = NULL; + + if (!n_to_drop) + goto out; + + down_write(&fp->f_ci->m_lock); + list_del_init(&fp->node); + up_write(&fp->f_ci->m_lock); + + if (atomic_sub_and_test(n_to_drop, &fp->refcount)) { + if (fp->conn) + atomic_dec(&fp->conn->stats.open_files_count); + __ksmbd_close_fd(NULL, fp); + } + return 0; + +out: + if (opinfo) + opinfo_put(opinfo); + ksmbd_put_durable_fd(fp); + return 0; +} + +int ksmbd_invalidate_durable_fd(unsigned long long id) +{ + struct ksmbd_file *fp; + + fp = ksmbd_lookup_global_fd(id); + if (!fp) + return -ENOENT; + + fp->durable_reconnect_disabled = true; + + if (fp->conn) { + ksmbd_put_durable_fd(fp); + return -ENOENT; + } + + fp->durable_timeout = 1; + fp->durable_scavenger_timeout = jiffies_to_msecs(jiffies); + ksmbd_put_durable_fd(fp); + if (waitqueue_active(&dh_wq)) + wake_up(&dh_wq); + + return -ENOENT; +} + struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid) { struct ksmbd_file *fp = NULL; @@ -734,6 +944,30 @@ struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry) return NULL; } +bool ksmbd_has_open_files(struct dentry *dentry) +{ + struct ksmbd_file *fp; + unsigned int id; + bool ret = false; + + read_lock(&global_ft.lock); + idr_for_each_entry(global_ft.idr, fp, id) { + struct dentry *fp_dentry = fp->filp->f_path.dentry; + + if (fp->f_state != FP_INITED) + continue; + if (fp_dentry == dentry) + continue; + if (is_subdir(fp_dentry, dentry)) { + ret = true; + break; + } + } + read_unlock(&global_ft.lock); + + return ret; +} + #define OPEN_ID_TYPE_VOLATILE_ID (0) #define OPEN_ID_TYPE_PERSISTENT_ID (1) @@ -758,7 +992,8 @@ static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp, idr_preload(KSMBD_DEFAULT_GFP); write_lock(&ft->lock); - ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX - 1, GFP_NOWAIT); + ret = idr_alloc_cyclic(ft->idr, fp, KSMBD_START_FID, INT_MAX - 1, + GFP_NOWAIT); if (ret >= 0) { id = ret; ret = 0; @@ -929,6 +1164,7 @@ __close_file_table_ids(struct ksmbd_session *sess, * global_ft. */ idr_remove(ft->idr, id); + fp->durable_volatile_id = fp->volatile_id; fp->volatile_id = KSMBD_NO_FID; write_unlock(&ft->lock); @@ -1108,10 +1344,10 @@ static int ksmbd_durable_scavenger(void *dummy) if (try_to_freeze()) continue; - remaining_jiffies = wait_event_timeout(dh_wq, + remaining_jiffies = wait_event_interruptible_timeout(dh_wq, ksmbd_durable_scavenger_alive() == false, __msecs_to_jiffies(min_timeout)); - if (remaining_jiffies) + if ((long)remaining_jiffies > 0) min_timeout = jiffies_to_msecs(remaining_jiffies); else min_timeout = DURABLE_HANDLE_MAX_TIMEOUT; @@ -1303,9 +1539,15 @@ static bool session_fd_check(struct ksmbd_tree_connect *tcon, up_write(&ci->m_lock); list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) { - spin_lock(&conn->llist_lock); + struct ksmbd_conn *lock_conn = smb_lock->conn; + + if (!lock_conn) + continue; + spin_lock(&lock_conn->llist_lock); list_del_init(&smb_lock->clist); - spin_unlock(&conn->llist_lock); + smb_lock->conn = NULL; + spin_unlock(&lock_conn->llist_lock); + ksmbd_conn_put(lock_conn); } fp->conn = NULL; @@ -1435,6 +1677,7 @@ int ksmbd_reopen_durable_fd(struct ksmbd_work *work, struct ksmbd_file *fp) } list_for_each_entry(smb_lock, &fp->lock_list, flist) { + smb_lock->conn = ksmbd_conn_get(conn); spin_lock(&conn->llist_lock); list_add_tail(&smb_lock->clist, &conn->lock_list); spin_unlock(&conn->llist_lock); diff --git a/fs/smb/server/vfs_cache.h b/fs/smb/server/vfs_cache.h index 7d547e1a74f7..287f3e675cd3 100644 --- a/fs/smb/server/vfs_cache.h +++ b/fs/smb/server/vfs_cache.h @@ -23,7 +23,12 @@ #define FILE_GENERIC_WRITE 0x120116 #define FILE_GENERIC_EXECUTE 0X1200a0 -#define KSMBD_START_FID 0 +/* + * Start volatile/persistent file id allocation at 1. A file id of 0 yields an + * SMB2 FileId of {0, 0}, which clients (e.g. Windows, Samba) treat as a null + * handle and never close, leaking the open on the server. + */ +#define KSMBD_START_FID 1 #define KSMBD_NO_FID (INT_MAX) #define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL) @@ -32,6 +37,7 @@ struct ksmbd_session; struct ksmbd_lock { struct file_lock *fl; + struct ksmbd_conn *conn; struct list_head clist; struct list_head flist; struct list_head llist; @@ -80,6 +86,7 @@ struct ksmbd_file { struct file *filp; u64 persistent_id; u64 volatile_id; + u64 durable_volatile_id; spinlock_t f_lock; @@ -95,6 +102,8 @@ struct ksmbd_file { __le32 coption; __le32 cdoption; __u64 create_time; + __u64 change_time; + __u64 allocation_size; __u64 itime; bool is_nt_open; @@ -121,6 +130,7 @@ struct ksmbd_file { bool is_durable; bool is_persistent; bool is_resilient; + bool durable_reconnect_disabled; bool is_posix_ctxt; struct durable_owner owner; @@ -159,11 +169,17 @@ struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id, void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp); struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d); void ksmbd_inode_put(struct ksmbd_inode *ci); +bool ksmbd_close_disconnected_durable_delete_on_close(struct dentry *dentry); struct ksmbd_file *ksmbd_lookup_global_fd(unsigned long long id); struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id); void ksmbd_put_durable_fd(struct ksmbd_file *fp); +int ksmbd_invalidate_durable_fd(unsigned long long id); +bool ksmbd_has_other_active_fd(struct ksmbd_file *fp); +bool ksmbd_has_stream_without_delete_share(struct ksmbd_file *fp); +int ksmbd_close_fd_app_instance_id(char *app_instance_id); struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid); struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry); +bool ksmbd_has_open_files(struct dentry *dentry); unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp); struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp); void ksmbd_launch_ksmbd_durable_scavenger(void); diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 5ad62c207d00..5f5ca023cd65 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -2608,13 +2608,13 @@ struct drm_tile_group { struct kref refcount; struct drm_device *dev; int id; - u8 group_data[8]; + u8 group_data[9]; }; struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, - const char topology[8]); + const char topology_id[9]); struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev, - const char topology[8]); + const char topology_id[9]); void drm_mode_put_tile_group(struct drm_device *dev, struct drm_tile_group *tg); diff --git a/include/dt-bindings/clock/canaan,k230-clk.h b/include/dt-bindings/clock/canaan,k230-clk.h new file mode 100644 index 000000000000..3b916678cc5b --- /dev/null +++ b/include/dt-bindings/clock/canaan,k230-clk.h @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Kendryte Canaan K230 Clock Drivers + * + * Author: Xukai Wang <kingxukai@zohomail.com> + */ + +#ifndef __DT_BINDINGS_CANAAN_K230_CLOCK_H__ +#define __DT_BINDINGS_CANAAN_K230_CLOCK_H__ + +#define K230_CPU0_SRC_GATE 0 +#define K230_CPU0_PLIC_GATE 1 +#define K230_CPU0_NOC_DDRCP4_GATE 2 +#define K230_CPU0_APB_GATE 3 +#define K230_CPU0_SRC_RATE 4 +#define K230_CPU0_AXI_RATE 5 +#define K230_CPU0_PLIC_RATE 6 +#define K230_CPU0_APB_RATE 7 +#define K230_HS_SSI0_MUX 8 +#define K230_HS_USB_REF_MUX 9 +#define K230_HS_HCLK_HIGH_GATE 10 +#define K230_HS_HCLK_GATE 11 +#define K230_HS_SD0_AHB_GATE 12 +#define K230_HS_SD1_AHB_GATE 13 +#define K230_HS_SSI1_AHB_GATE 14 +#define K230_HS_SSI2_AHB_GATE 15 +#define K230_HS_USB0_AHB_GATE 16 +#define K230_HS_USB1_AHB_GATE 17 +#define K230_HS_SSI0_AXI_GATE 18 +#define K230_HS_SSI1_GATE 19 +#define K230_HS_SSI2_GATE 20 +#define K230_HS_QSPI_AXI_SRC_GATE 21 +#define K230_HS_SSI1_AXI_GATE 22 +#define K230_HS_SSI2_AXI_GATE 23 +#define K230_HS_SD_CARD_SRC_GATE 24 +#define K230_HS_SD0_CARD_GATE 25 +#define K230_HS_SD1_CARD_GATE 26 +#define K230_HS_SD_AXI_SRC_GATE 27 +#define K230_HS_SD0_AXI_GATE 28 +#define K230_HS_SD1_AXI_GATE 29 +#define K230_HS_SD0_BASE_GATE 30 +#define K230_HS_SD1_BASE_GATE 31 +#define K230_HS_SSI0_GATE 32 +#define K230_HS_SD_TIMER_SRC_GATE 33 +#define K230_HS_SD0_TIMER_GATE 34 +#define K230_HS_SD1_TIMER_GATE 35 +#define K230_HS_USB0_REF_GATE 36 +#define K230_HS_USB1_REF_GATE 37 +#define K230_HS_HCLK_HIGH_RATE 38 +#define K230_HS_HCLK_RATE 39 +#define K230_HS_SSI0_AXI_RATE 40 +#define K230_HS_SSI1_RATE 41 +#define K230_HS_SSI2_RATE 42 +#define K230_HS_QSPI_AXI_SRC_RATE 43 +#define K230_HS_SD_CARD_SRC_RATE 44 +#define K230_HS_SD_AXI_SRC_RATE 45 +#define K230_HS_USB_REF_50M_RATE 46 +#define K230_HS_SD_TIMER_SRC_RATE 47 +#define K230_TIMER0_MUX 48 +#define K230_TIMER1_MUX 49 +#define K230_TIMER2_MUX 50 +#define K230_TIMER3_MUX 51 +#define K230_TIMER4_MUX 52 +#define K230_TIMER5_MUX 53 +#define K230_SHRM_SRAM_MUX 54 +#define K230_DDRC_SRC_MUX 55 +#define K230_AI_SRC_MUX 56 +#define K230_CAMERA0_MUX 57 +#define K230_CAMERA1_MUX 58 +#define K230_CAMERA2_MUX 59 +#define K230_CPU1_SRC_MUX 60 +#define K230_CPU1_SRC_GATE 61 +#define K230_CPU1_PLIC_GATE 62 +#define K230_CPU1_APB_GATE 63 +#define K230_CPU1_SRC_RATE 64 +#define K230_CPU1_AXI_RATE 65 +#define K230_CPU1_PLIC_RATE 66 +#define K230_PMU_APB_GATE 67 +#define K230_LS_APB_SRC_GATE 68 +#define K230_LS_UART0_APB_GATE 69 +#define K230_LS_UART1_APB_GATE 70 +#define K230_LS_UART2_APB_GATE 71 +#define K230_LS_UART3_APB_GATE 72 +#define K230_LS_UART4_APB_GATE 73 +#define K230_LS_I2C0_APB_GATE 74 +#define K230_LS_I2C1_APB_GATE 75 +#define K230_LS_I2C2_APB_GATE 76 +#define K230_LS_I2C3_APB_GATE 77 +#define K230_LS_I2C4_APB_GATE 78 +#define K230_LS_GPIO_APB_GATE 79 +#define K230_LS_PWM_APB_GATE 80 +#define K230_LS_JAMLINK0_APB_GATE 81 +#define K230_LS_JAMLINK1_APB_GATE 82 +#define K230_LS_JAMLINK2_APB_GATE 83 +#define K230_LS_JAMLINK3_APB_GATE 84 +#define K230_LS_AUDIO_APB_GATE 85 +#define K230_LS_ADC_APB_GATE 86 +#define K230_LS_CODEC_APB_GATE 87 +#define K230_LS_I2C0_GATE 88 +#define K230_LS_I2C1_GATE 89 +#define K230_LS_I2C2_GATE 90 +#define K230_LS_I2C3_GATE 91 +#define K230_LS_I2C4_GATE 92 +#define K230_LS_CODEC_ADC_GATE 93 +#define K230_LS_CODEC_DAC_GATE 94 +#define K230_LS_AUDIO_DEV_GATE 95 +#define K230_LS_PDM_GATE 96 +#define K230_LS_ADC_GATE 97 +#define K230_LS_UART0_GATE 98 +#define K230_LS_UART1_GATE 99 +#define K230_LS_UART2_GATE 100 +#define K230_LS_UART3_GATE 101 +#define K230_LS_UART4_GATE 102 +#define K230_LS_JAMLINK0CO_GATE 103 +#define K230_LS_JAMLINK1CO_GATE 104 +#define K230_LS_JAMLINK2CO_GATE 105 +#define K230_LS_JAMLINK3CO_GATE 106 +#define K230_LS_GPIO_DEBOUNCE_GATE 107 +#define K230_SYSCTL_WDT0_APB_GATE 108 +#define K230_SYSCTL_WDT1_APB_GATE 109 +#define K230_SYSCTL_TIMER_APB_GATE 110 +#define K230_SYSCTL_IOMUX_APB_GATE 111 +#define K230_SYSCTL_MAILBOX_APB_GATE 112 +#define K230_SYSCTL_HDI_GATE 113 +#define K230_SYSCTL_TIME_STAMP_GATE 114 +#define K230_SYSCTL_WDT0_GATE 115 +#define K230_SYSCTL_WDT1_GATE 116 +#define K230_TIMER0_GATE 117 +#define K230_TIMER1_GATE 118 +#define K230_TIMER2_GATE 119 +#define K230_TIMER3_GATE 120 +#define K230_TIMER4_GATE 121 +#define K230_TIMER5_GATE 122 +#define K230_SHRM_APB_GATE 123 +#define K230_SHRM_AXI_GATE 124 +#define K230_SHRM_AXI_SLAVE_GATE 125 +#define K230_SHRM_NONAI2D_AXI_GATE 126 +#define K230_SHRM_SRAM_GATE 127 +#define K230_SHRM_DECOMPRESS_AXI_GATE 128 +#define K230_SHRM_SDMA_AXI_GATE 129 +#define K230_SHRM_PDMA_AXI_GATE 130 +#define K230_DDRC_SRC_GATE 131 +#define K230_DDRC_BYPASS_GATE 132 +#define K230_DDRC_APB_GATE 133 +#define K230_DISPLAY_AHB_GATE 134 +#define K230_DISPLAY_AXI_GATE 135 +#define K230_DISPLAY_GPU_GATE 136 +#define K230_DISPLAY_DPIP_GATE 137 +#define K230_DISPLAY_CFG_GATE 138 +#define K230_DISPLAY_REF_GATE 139 +#define K230_USB_480M_GATE 140 +#define K230_USB_100M_GATE 141 +#define K230_DPHY_DFT_GATE 142 +#define K230_SPI2AXI_GATE 143 +#define K230_AI_SRC_GATE 144 +#define K230_AI_AXI_GATE 145 +#define K230_AI_SRC_RATE 146 +#define K230_CAMERA0_GATE 147 +#define K230_CAMERA1_GATE 148 +#define K230_CAMERA2_GATE 149 +#define K230_LS_APB_SRC_RATE 150 +#define K230_LS_I2C0_RATE 151 +#define K230_LS_I2C1_RATE 152 +#define K230_LS_I2C2_RATE 153 +#define K230_LS_I2C3_RATE 154 +#define K230_LS_I2C4_RATE 155 +#define K230_LS_CODEC_ADC_RATE 156 +#define K230_LS_CODEC_DAC_RATE 157 +#define K230_LS_AUDIO_DEV_RATE 158 +#define K230_LS_PDM_RATE 159 +#define K230_LS_ADC_RATE 160 +#define K230_LS_UART0_RATE 161 +#define K230_LS_UART1_RATE 162 +#define K230_LS_UART2_RATE 163 +#define K230_LS_UART3_RATE 164 +#define K230_LS_UART4_RATE 165 +#define K230_LS_JAMLINKCO_SRC_RATE 166 +#define K230_LS_GPIO_DEBOUNCE_RATE 167 +#define K230_SYSCTL_HDI_RATE 168 +#define K230_SYSCTL_TIME_STAMP_RATE 169 +#define K230_SYSCTL_TEMP_SENSOR_RATE 170 +#define K230_SYSCTL_WDT0_RATE 171 +#define K230_SYSCTL_WDT1_RATE 172 +#define K230_TIMER0_SRC_RATE 173 +#define K230_TIMER1_SRC_RATE 174 +#define K230_TIMER2_SRC_RATE 175 +#define K230_TIMER3_SRC_RATE 176 +#define K230_TIMER4_SRC_RATE 177 +#define K230_TIMER5_SRC_RATE 178 +#define K230_SHRM_APB_RATE 179 +#define K230_DDRC_SRC_RATE 180 +#define K230_DDRC_APB_RATE 181 +#define K230_DISPLAY_AHB_RATE 182 +#define K230_DISPLAY_CLKEXT_RATE 183 +#define K230_DISPLAY_GPU_RATE 184 +#define K230_DISPLAY_DPIP_RATE 185 +#define K230_DISPLAY_CFG_RATE 186 +#define K230_VPU_SRC_GATE 187 +#define K230_VPU_AXI_GATE 188 +#define K230_VPU_DDRCP2_GATE 189 +#define K230_VPU_CFG_GATE 190 +#define K230_VPU_SRC_RATE 191 +#define K230_VPU_AXI_SRC_RATE 192 +#define K230_VPU_CFG_RATE 193 +#define K230_SEC_APB_GATE 194 +#define K230_SEC_FIX_GATE 195 +#define K230_SEC_AXI_GATE 196 +#define K230_SEC_APB_RATE 197 +#define K230_SEC_FIX_RATE 198 +#define K230_SEC_AXI_RATE 199 +#define K230_USB_480M_RATE 200 +#define K230_USB_100M_RATE 201 +#define K230_DPHY_DFT_RATE 202 +#define K230_SPI2AXI_RATE 203 +#define K230_CAMERA0_RATE 204 +#define K230_CAMERA1_RATE 205 +#define K230_CAMERA2_RATE 206 +#define K230_SHRM_SRAM_DIV2 207 + +#endif /* __DT_BINDINGS_CANAAN_K230_CLOCK_H__ */ diff --git a/include/dt-bindings/clock/qcom,hawi-gcc.h b/include/dt-bindings/clock/qcom,hawi-gcc.h new file mode 100644 index 000000000000..6cd7fa0884f5 --- /dev/null +++ b/include/dt-bindings/clock/qcom,hawi-gcc.h @@ -0,0 +1,253 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_GCC_HAWI_H +#define _DT_BINDINGS_CLK_QCOM_GCC_HAWI_H + +/* GCC clocks */ +#define GCC_AGGRE_NOC_PCIE_AXI_CLK 0 +#define GCC_AGGRE_STARDUSTNOC_USB3_PRIM_AXI_CLK 1 +#define GCC_AGGRE_UFS_PHY_AXI_CLK 2 +#define GCC_BOOT_ROM_AHB_CLK 3 +#define GCC_CAM_BIST_MCLK_AHB_CLK 4 +#define GCC_CAMERA_AHB_CLK 5 +#define GCC_CAMERA_HF_AXI_CLK 6 +#define GCC_CAMERA_RSC_CORE_CLK 7 +#define GCC_CAMERA_SF_AXI_CLK 8 +#define GCC_CAMERA_XO_CLK 9 +#define GCC_CFG_NOC_PCIE_ANOC_AHB_CLK 10 +#define GCC_CFG_NOC_USB3_PRIM_AXI_CLK 11 +#define GCC_CNOC_PCIE_SF_AXI_CLK 12 +#define GCC_EVA_AHB_CLK 13 +#define GCC_EVA_AXI0_CLK 14 +#define GCC_EVA_AXI0C_CLK 15 +#define GCC_EVA_XO_CLK 16 +#define GCC_GP1_CLK 17 +#define GCC_GP1_CLK_SRC 18 +#define GCC_GP2_CLK 19 +#define GCC_GP2_CLK_SRC 20 +#define GCC_GP3_CLK 21 +#define GCC_GP3_CLK_SRC 22 +#define GCC_GPLL0 23 +#define GCC_GPLL0_OUT_EVEN 24 +#define GCC_GPLL4 25 +#define GCC_GPLL5 26 +#define GCC_GPLL7 27 +#define GCC_GPLL9 28 +#define GCC_GPU_CFG_AHB_CLK 29 +#define GCC_GPU_GEMNOC_GFX_CLK 30 +#define GCC_GPU_GPLL0_CLK_SRC 31 +#define GCC_GPU_GPLL0_DIV_CLK_SRC 32 +#define GCC_GPU_RSC_CORE_CLK 33 +#define GCC_GPU_SMMU_VOTE_CLK 34 +#define GCC_MMU_TCU_VOTE_CLK 35 +#define GCC_PCIE_0_AUX_CLK 36 +#define GCC_PCIE_0_AUX_CLK_SRC 37 +#define GCC_PCIE_0_CFG_AHB_CLK 38 +#define GCC_PCIE_0_MSTR_AXI_CLK 39 +#define GCC_PCIE_0_PHY_AUX_CLK 40 +#define GCC_PCIE_0_PHY_AUX_CLK_SRC 41 +#define GCC_PCIE_0_PHY_RCHNG_CLK 42 +#define GCC_PCIE_0_PHY_RCHNG_CLK_SRC 43 +#define GCC_PCIE_0_PIPE_CLK 44 +#define GCC_PCIE_0_PIPE_CLK_SRC 45 +#define GCC_PCIE_0_PIPE_DIV2_CLK 46 +#define GCC_PCIE_0_PIPE_DIV_CLK_SRC 47 +#define GCC_PCIE_0_SLV_AXI_CLK 48 +#define GCC_PCIE_0_SLV_Q2A_AXI_CLK 49 +#define GCC_PCIE_1_AUX_CLK 50 +#define GCC_PCIE_1_AUX_CLK_SRC 51 +#define GCC_PCIE_1_CFG_AHB_CLK 52 +#define GCC_PCIE_1_MSTR_AXI_CLK 53 +#define GCC_PCIE_1_PHY_AUX_CLK 54 +#define GCC_PCIE_1_PHY_AUX_CLK_SRC 55 +#define GCC_PCIE_1_PHY_RCHNG_CLK 56 +#define GCC_PCIE_1_PHY_RCHNG_CLK_SRC 57 +#define GCC_PCIE_1_PIPE_CLK 58 +#define GCC_PCIE_1_PIPE_CLK_SRC 59 +#define GCC_PCIE_1_PIPE_DIV2_CLK 60 +#define GCC_PCIE_1_PIPE_DIV_CLK_SRC 61 +#define GCC_PCIE_1_RSC_CORE_CLK 62 +#define GCC_PCIE_1_SLV_AXI_CLK 63 +#define GCC_PCIE_1_SLV_Q2A_AXI_CLK 64 +#define GCC_PCIE_RSC_CORE_CLK 65 +#define GCC_PCIE_RSCC_CFG_AHB_CLK 66 +#define GCC_PCIE_RSCC_XO_CLK 67 +#define GCC_PDM2_CLK 68 +#define GCC_PDM2_CLK_SRC 69 +#define GCC_PDM_AHB_CLK 70 +#define GCC_PDM_XO4_CLK 71 +#define GCC_QUPV3_I2C_CORE_CLK 72 +#define GCC_QUPV3_I2C_S0_CLK 73 +#define GCC_QUPV3_I2C_S0_CLK_SRC 74 +#define GCC_QUPV3_I2C_S1_CLK 75 +#define GCC_QUPV3_I2C_S1_CLK_SRC 76 +#define GCC_QUPV3_I2C_S2_CLK 77 +#define GCC_QUPV3_I2C_S2_CLK_SRC 78 +#define GCC_QUPV3_I2C_S3_CLK 79 +#define GCC_QUPV3_I2C_S3_CLK_SRC 80 +#define GCC_QUPV3_I2C_S4_CLK 81 +#define GCC_QUPV3_I2C_S4_CLK_SRC 82 +#define GCC_QUPV3_I2C_S_AHB_CLK 83 +#define GCC_QUPV3_WRAP1_CORE_2X_CLK 84 +#define GCC_QUPV3_WRAP1_CORE_CLK 85 +#define GCC_QUPV3_WRAP1_QSPI_REF_CLK 86 +#define GCC_QUPV3_WRAP1_QSPI_REF_CLK_SRC 87 +#define GCC_QUPV3_WRAP1_S0_CLK 88 +#define GCC_QUPV3_WRAP1_S0_CLK_SRC 89 +#define GCC_QUPV3_WRAP1_S1_CLK 90 +#define GCC_QUPV3_WRAP1_S1_CLK_SRC 91 +#define GCC_QUPV3_WRAP1_S2_CLK 92 +#define GCC_QUPV3_WRAP1_S2_CLK_SRC 93 +#define GCC_QUPV3_WRAP1_S3_CLK 94 +#define GCC_QUPV3_WRAP1_S3_CLK_SRC 95 +#define GCC_QUPV3_WRAP1_S4_CLK 96 +#define GCC_QUPV3_WRAP1_S4_CLK_SRC 97 +#define GCC_QUPV3_WRAP1_S5_CLK 98 +#define GCC_QUPV3_WRAP1_S5_CLK_SRC 99 +#define GCC_QUPV3_WRAP1_S6_CLK 100 +#define GCC_QUPV3_WRAP1_S6_CLK_SRC 101 +#define GCC_QUPV3_WRAP1_S7_CLK 102 +#define GCC_QUPV3_WRAP1_S7_CLK_SRC 103 +#define GCC_QUPV3_WRAP2_CORE_2X_CLK 104 +#define GCC_QUPV3_WRAP2_CORE_CLK 105 +#define GCC_QUPV3_WRAP2_S0_CLK 106 +#define GCC_QUPV3_WRAP2_S0_CLK_SRC 107 +#define GCC_QUPV3_WRAP2_S1_CLK 108 +#define GCC_QUPV3_WRAP2_S1_CLK_SRC 109 +#define GCC_QUPV3_WRAP2_S2_CLK 110 +#define GCC_QUPV3_WRAP2_S2_CLK_SRC 111 +#define GCC_QUPV3_WRAP2_S3_CLK 112 +#define GCC_QUPV3_WRAP2_S3_CLK_SRC 113 +#define GCC_QUPV3_WRAP2_S4_CLK 114 +#define GCC_QUPV3_WRAP2_S4_CLK_SRC 115 +#define GCC_QUPV3_WRAP3_CORE_2X_CLK 116 +#define GCC_QUPV3_WRAP3_CORE_CLK 117 +#define GCC_QUPV3_WRAP3_QSPI_REF_CLK 118 +#define GCC_QUPV3_WRAP3_QSPI_REF_CLK_SRC 119 +#define GCC_QUPV3_WRAP3_S0_CLK 120 +#define GCC_QUPV3_WRAP3_S0_CLK_SRC 121 +#define GCC_QUPV3_WRAP3_S1_CLK 122 +#define GCC_QUPV3_WRAP3_S1_CLK_SRC 123 +#define GCC_QUPV3_WRAP3_S2_CLK 124 +#define GCC_QUPV3_WRAP3_S2_CLK_SRC 125 +#define GCC_QUPV3_WRAP3_S3_CLK 126 +#define GCC_QUPV3_WRAP3_S3_CLK_SRC 127 +#define GCC_QUPV3_WRAP3_S4_CLK 128 +#define GCC_QUPV3_WRAP3_S4_CLK_SRC 129 +#define GCC_QUPV3_WRAP3_S5_CLK 130 +#define GCC_QUPV3_WRAP3_S5_CLK_SRC 131 +#define GCC_QUPV3_WRAP4_CORE_2X_CLK 132 +#define GCC_QUPV3_WRAP4_CORE_CLK 133 +#define GCC_QUPV3_WRAP4_S0_CLK 134 +#define GCC_QUPV3_WRAP4_S0_CLK_SRC 135 +#define GCC_QUPV3_WRAP4_S1_CLK 136 +#define GCC_QUPV3_WRAP4_S1_CLK_SRC 137 +#define GCC_QUPV3_WRAP4_S2_CLK 138 +#define GCC_QUPV3_WRAP4_S2_CLK_SRC 139 +#define GCC_QUPV3_WRAP4_S3_CLK 140 +#define GCC_QUPV3_WRAP4_S3_CLK_SRC 141 +#define GCC_QUPV3_WRAP4_S4_CLK 142 +#define GCC_QUPV3_WRAP4_S4_CLK_SRC 143 +#define GCC_QUPV3_WRAP_1_M_AXI_CLK 144 +#define GCC_QUPV3_WRAP_1_S_AHB_CLK 145 +#define GCC_QUPV3_WRAP_2_M_AHB_CLK 146 +#define GCC_QUPV3_WRAP_2_S_AHB_CLK 147 +#define GCC_QUPV3_WRAP_3_M_AHB_CLK 148 +#define GCC_QUPV3_WRAP_3_S_AHB_CLK 149 +#define GCC_QUPV3_WRAP_4_M_AHB_CLK 150 +#define GCC_QUPV3_WRAP_4_S_AHB_CLK 151 +#define GCC_SDCC2_AHB_CLK 152 +#define GCC_SDCC2_APPS_CLK 153 +#define GCC_SDCC2_APPS_CLK_SRC 154 +#define GCC_SDCC4_AHB_CLK 155 +#define GCC_SDCC4_APPS_CLK 156 +#define GCC_SDCC4_APPS_CLK_SRC 157 +#define GCC_UFS_PHY_AHB_CLK 158 +#define GCC_UFS_PHY_AXI_CLK 159 +#define GCC_UFS_PHY_AXI_CLK_SRC 160 +#define GCC_UFS_PHY_ICE_CORE_CLK 161 +#define GCC_UFS_PHY_ICE_CORE_CLK_SRC 162 +#define GCC_UFS_PHY_PHY_AUX_CLK 163 +#define GCC_UFS_PHY_PHY_AUX_CLK_SRC 164 +#define GCC_UFS_PHY_RX_SYMBOL_0_CLK 165 +#define GCC_UFS_PHY_RX_SYMBOL_0_CLK_SRC 166 +#define GCC_UFS_PHY_RX_SYMBOL_1_CLK 167 +#define GCC_UFS_PHY_RX_SYMBOL_1_CLK_SRC 168 +#define GCC_UFS_PHY_TX_SYMBOL_0_CLK 169 +#define GCC_UFS_PHY_TX_SYMBOL_0_CLK_SRC 170 +#define GCC_UFS_PHY_UNIPRO_5_CORE_CLK 171 +#define GCC_UFS_PHY_UNIPRO_5_CORE_CLK_SRC 172 +#define GCC_USB30_PRIM_MASTER_CLK 173 +#define GCC_USB30_PRIM_MASTER_CLK_SRC 174 +#define GCC_USB30_PRIM_MOCK_UTMI_CLK 175 +#define GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC 176 +#define GCC_USB30_PRIM_MOCK_UTMI_POSTDIV_CLK_SRC 177 +#define GCC_USB30_PRIM_SLEEP_CLK 178 +#define GCC_USB3_PRIM_PHY_AUX_CLK 179 +#define GCC_USB3_PRIM_PHY_AUX_CLK_SRC 180 +#define GCC_USB3_PRIM_PHY_COM_AUX_CLK 181 +#define GCC_USB3_PRIM_PHY_PIPE_CLK 182 +#define GCC_USB3_PRIM_PHY_PIPE_CLK_SRC 183 +#define GCC_VIDEO_AHB_CLK 184 +#define GCC_VIDEO_AXI0_CLK 185 +#define GCC_VIDEO_AXI0C_CLK 186 +#define GCC_VIDEO_XO_CLK 187 + +/* GCC power domains */ +#define GCC_PCIE_0_GDSC 0 +#define GCC_PCIE_0_PHY_GDSC 1 +#define GCC_PCIE_1_GDSC 2 +#define GCC_PCIE_1_PHY_GDSC 3 +#define GCC_UFS_MEM_PHY_GDSC 4 +#define GCC_UFS_PHY_GDSC 5 +#define GCC_USB30_PRIM_GDSC 6 +#define GCC_USB3_PHY_GDSC 7 + +/* GCC resets */ +#define GCC_CAMERA_BCR 0 +#define GCC_EVA_AXI0_CLK_ARES 1 +#define GCC_EVA_AXI0C_CLK_ARES 2 +#define GCC_EVA_BCR 3 +#define GCC_GPU_BCR 4 +#define GCC_PCIE_0_BCR 5 +#define GCC_PCIE_0_LINK_DOWN_BCR 6 +#define GCC_PCIE_0_NOCSR_COM_PHY_BCR 7 +#define GCC_PCIE_0_PHY_BCR 8 +#define GCC_PCIE_0_PHY_NOCSR_COM_PHY_BCR 9 +#define GCC_PCIE_1_BCR 10 +#define GCC_PCIE_1_LINK_DOWN_BCR 11 +#define GCC_PCIE_1_NOCSR_COM_PHY_BCR 12 +#define GCC_PCIE_1_PHY_BCR 13 +#define GCC_PCIE_1_PHY_NOCSR_COM_PHY_BCR 14 +#define GCC_PCIE_PHY_BCR 15 +#define GCC_PCIE_PHY_CFG_AHB_BCR 16 +#define GCC_PCIE_PHY_COM_BCR 17 +#define GCC_PCIE_RSCC_BCR 18 +#define GCC_PDM_BCR 19 +#define GCC_QUPV3_WRAPPER_1_BCR 20 +#define GCC_QUPV3_WRAPPER_2_BCR 21 +#define GCC_QUPV3_WRAPPER_3_BCR 22 +#define GCC_QUPV3_WRAPPER_4_BCR 23 +#define GCC_QUPV3_WRAPPER_I2C_BCR 24 +#define GCC_QUSB2PHY_PRIM_BCR 25 +#define GCC_QUSB2PHY_SEC_BCR 26 +#define GCC_SDCC2_BCR 27 +#define GCC_SDCC4_BCR 28 +#define GCC_TCSR_PCIE_BCR 29 +#define GCC_UFS_PHY_BCR 30 +#define GCC_USB30_PRIM_BCR 31 +#define GCC_USB3_DP_PHY_PRIM_BCR 32 +#define GCC_USB3_DP_PHY_SEC_BCR 33 +#define GCC_USB3_PHY_PRIM_BCR 34 +#define GCC_USB3_PHY_SEC_BCR 35 +#define GCC_USB3PHY_PHY_PRIM_BCR 36 +#define GCC_USB3PHY_PHY_SEC_BCR 37 +#define GCC_VIDEO_AXI0_CLK_ARES 38 +#define GCC_VIDEO_AXI0C_CLK_ARES 39 +#define GCC_VIDEO_BCR 40 +#define GCC_VIDEO_XO_CLK_ARES 41 + +#endif diff --git a/include/dt-bindings/clock/qcom,hawi-tcsrcc.h b/include/dt-bindings/clock/qcom,hawi-tcsrcc.h new file mode 100644 index 000000000000..957bc5f75bb7 --- /dev/null +++ b/include/dt-bindings/clock/qcom,hawi-tcsrcc.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_TCSR_CC_HAWI_H +#define _DT_BINDINGS_CLK_QCOM_TCSR_CC_HAWI_H + +/* TCSR_CC clocks */ +#define TCSR_PCIE_0_CLKREF_EN 0 +#define TCSR_PCIE_1_CLKREF_EN 1 +#define TCSR_UFS_CLKREF_EN 2 +#define TCSR_USB2_CLKREF_EN 3 +#define TCSR_USB3_CLKREF_EN 4 + +#endif diff --git a/include/dt-bindings/clock/qcom,ipq5332-cmn-pll.h b/include/dt-bindings/clock/qcom,ipq5332-cmn-pll.h new file mode 100644 index 000000000000..172330e43669 --- /dev/null +++ b/include/dt-bindings/clock/qcom,ipq5332-cmn-pll.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_IPQ5332_CMN_PLL_H +#define _DT_BINDINGS_CLK_QCOM_IPQ5332_CMN_PLL_H + +/* CMN PLL core clock. */ +#define IPQ5332_CMN_PLL_CLK 0 + +/* The output clocks from CMN PLL of IPQ5332. */ +#define IPQ5332_XO_24MHZ_CLK 1 +#define IPQ5332_SLEEP_32KHZ_CLK 2 +#define IPQ5332_PCS_31P25MHZ_CLK 3 +#define IPQ5332_NSS_300MHZ_CLK 4 +#define IPQ5332_PPE_200MHZ_CLK 5 +#define IPQ5332_ETH_50MHZ_CLK 6 +#endif diff --git a/include/dt-bindings/clock/qcom,nord-negcc.h b/include/dt-bindings/clock/qcom,nord-negcc.h index 95f333d8e1aa..a7024317e90b 100644 --- a/include/dt-bindings/clock/qcom,nord-negcc.h +++ b/include/dt-bindings/clock/qcom,nord-negcc.h @@ -120,5 +120,6 @@ #define NE_GCC_USB3_PHY_SEC_BCR 10 #define NE_GCC_USB3PHY_PHY_PRIM_BCR 11 #define NE_GCC_USB3PHY_PHY_SEC_BCR 12 +#define NE_GCC_QUSB2PHY_PRIM_BCR 13 #endif diff --git a/include/dt-bindings/clock/qcom,rpmh.h b/include/dt-bindings/clock/qcom,rpmh.h index 0a7d1be0d124..2d62d5d0b08d 100644 --- a/include/dt-bindings/clock/qcom,rpmh.h +++ b/include/dt-bindings/clock/qcom,rpmh.h @@ -33,5 +33,7 @@ #define RPMH_HWKM_CLK 24 #define RPMH_QLINK_CLK 25 #define RPMH_QLINK_CLK_A 26 +#define RPMH_LN_BB_CLK4 27 +#define RPMH_LN_BB_CLK4_A 28 #endif diff --git a/include/dt-bindings/clock/qcom,x1e80100-camcc.h b/include/dt-bindings/clock/qcom,x1e80100-camcc.h index d72fdfb06a7c..06c316022fb0 100644 --- a/include/dt-bindings/clock/qcom,x1e80100-camcc.h +++ b/include/dt-bindings/clock/qcom,x1e80100-camcc.h @@ -115,6 +115,9 @@ #define CAM_CC_SLEEP_CLK_SRC 105 #define CAM_CC_SLOW_AHB_CLK_SRC 106 #define CAM_CC_XO_CLK_SRC 107 +#define CAM_CC_QDSS_DEBUG_CLK 108 +#define CAM_CC_QDSS_DEBUG_CLK_SRC 109 +#define CAM_CC_QDSS_DEBUG_XO_CLK 110 /* CAM_CC power domains */ #define CAM_CC_BPS_GDSC 0 diff --git a/include/dt-bindings/clock/qcom,x1p42100-videocc.h b/include/dt-bindings/clock/qcom,x1p42100-videocc.h new file mode 100644 index 000000000000..996408d1a0c3 --- /dev/null +++ b/include/dt-bindings/clock/qcom,x1p42100-videocc.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_VIDEO_CC_X1P42100_H +#define _DT_BINDINGS_CLK_QCOM_VIDEO_CC_X1P42100_H + +/* VIDEO_CC clocks */ +#define VIDEO_CC_MVS0_CLK 0 +#define VIDEO_CC_MVS0_CLK_SRC 1 +#define VIDEO_CC_MVS0_DIV_CLK_SRC 2 +#define VIDEO_CC_MVS0C_CLK 3 +#define VIDEO_CC_MVS0C_DIV2_DIV_CLK_SRC 4 +#define VIDEO_CC_MVS1_CLK 5 +#define VIDEO_CC_MVS1_CLK_SRC 6 +#define VIDEO_CC_MVS1_DIV_CLK_SRC 7 +#define VIDEO_CC_MVS1C_CLK 8 +#define VIDEO_CC_MVS1C_DIV2_DIV_CLK_SRC 9 +#define VIDEO_CC_PLL0 10 +#define VIDEO_CC_PLL1 11 +#define VIDEO_CC_MVS0_SHIFT_CLK 12 +#define VIDEO_CC_MVS0C_SHIFT_CLK 13 +#define VIDEO_CC_MVS1_SHIFT_CLK 14 +#define VIDEO_CC_MVS1C_SHIFT_CLK 15 +#define VIDEO_CC_XO_CLK_SRC 16 +#define VIDEO_CC_MVS0_BSE_CLK 17 +#define VIDEO_CC_MVS0_BSE_CLK_SRC 18 +#define VIDEO_CC_MVS0_BSE_DIV4_DIV_CLK_SRC 19 + +/* VIDEO_CC power domains */ +#define VIDEO_CC_MVS0C_GDSC 0 +#define VIDEO_CC_MVS0_GDSC 1 +#define VIDEO_CC_MVS1C_GDSC 2 +#define VIDEO_CC_MVS1_GDSC 3 + +/* VIDEO_CC resets */ +#define CVP_VIDEO_CC_INTERFACE_BCR 0 +#define CVP_VIDEO_CC_MVS0_BCR 1 +#define CVP_VIDEO_CC_MVS0C_BCR 2 +#define CVP_VIDEO_CC_MVS1_BCR 3 +#define CVP_VIDEO_CC_MVS1C_BCR 4 +#define VIDEO_CC_MVS0C_CLK_ARES 5 +#define VIDEO_CC_MVS1C_CLK_ARES 6 +#define VIDEO_CC_XO_CLK_ARES 7 +#define VIDEO_CC_MVS0_BSE_BCR 8 + +#endif diff --git a/include/dt-bindings/clock/rockchip,rk3588-cru.h b/include/dt-bindings/clock/rockchip,rk3588-cru.h index 0c7d3ca2d5bc..7528034cff56 100644 --- a/include/dt-bindings/clock/rockchip,rk3588-cru.h +++ b/include/dt-bindings/clock/rockchip,rk3588-cru.h @@ -734,6 +734,10 @@ #define PCLK_AV1_PRE 719 #define HCLK_SDIO_PRE 720 #define PCLK_VO1GRF 721 +#define I2S0_8CH_MCLKOUT_TO_IO 722 +#define I2S1_8CH_MCLKOUT_TO_IO 723 +#define I2S2_2CH_MCLKOUT_TO_IO 724 +#define I2S3_2CH_MCLKOUT_TO_IO 725 /* scmi-clocks indices */ diff --git a/include/dt-bindings/clock/spacemit,k3-clocks.h b/include/dt-bindings/clock/spacemit,k3-clocks.h index b22336f3ae40..dfae52547cda 100644 --- a/include/dt-bindings/clock/spacemit,k3-clocks.h +++ b/include/dt-bindings/clock/spacemit,k3-clocks.h @@ -380,6 +380,11 @@ #define CLK_APMU_ISIM_VCLK1 86 #define CLK_APMU_ISIM_VCLK2 87 #define CLK_APMU_ISIM_VCLK3 88 +#define CLK_APMU_PCIE_PORTA_DBI 89 +#define CLK_APMU_PCIE_PORTB_DBI 90 +#define CLK_APMU_PCIE_PORTC_DBI 91 +#define CLK_APMU_PCIE_PORTD_DBI 92 +#define CLK_APMU_PCIE_PORTE_DBI 93 /* DCIU clocks */ #define CLK_DCIU_HDMA 0 diff --git a/include/linux/adi-axi-common.h b/include/linux/adi-axi-common.h index 37962ba530df..e7ba393061ee 100644 --- a/include/linux/adi-axi-common.h +++ b/include/linux/adi-axi-common.h @@ -51,6 +51,7 @@ enum adi_axi_fpga_technology { ADI_AXI_FPGA_TECH_SERIES7, ADI_AXI_FPGA_TECH_ULTRASCALE, ADI_AXI_FPGA_TECH_ULTRASCALE_PLUS, + ADI_AXI_FPGA_TECH_VERSAL, }; enum adi_axi_fpga_family { @@ -71,6 +72,7 @@ enum adi_axi_fpga_speed_grade { ADI_AXI_FPGA_SPEED_2 = 20, ADI_AXI_FPGA_SPEED_2L = 21, ADI_AXI_FPGA_SPEED_2LV = 22, + ADI_AXI_FPGA_SPEED_2MP = 23, ADI_AXI_FPGA_SPEED_3 = 30, }; diff --git a/include/linux/clk/renesas.h b/include/linux/clk/renesas.h index c360df9fa735..0949400f44de 100644 --- a/include/linux/clk/renesas.h +++ b/include/linux/clk/renesas.h @@ -164,6 +164,26 @@ struct rzv2h_pll_div_pars { .k = { .min = -32768, .max = 32767 }, \ } \ +#define RZG3E_CPG_PLL_DSI0_LIMITS(name) \ + static const struct rzv2h_pll_limits (name) = { \ + .fout = { .min = 25 * MEGA, .max = 1218 * MEGA }, \ + .fvco = { .min = 1600 * MEGA, .max = 3200 * MEGA }, \ + .m = { .min = 64, .max = 533 }, \ + .p = { .min = 1, .max = 4 }, \ + .s = { .min = 0, .max = 6 }, \ + .k = { .min = -32768, .max = 32767 }, \ + } \ + +#define RZG3E_CPG_PLL_DSI1_LIMITS(name) \ + static const struct rzv2h_pll_limits (name) = { \ + .fout = { .min = 25 * MEGA, .max = 609 * MEGA }, \ + .fvco = { .min = 1600 * MEGA, .max = 3200 * MEGA }, \ + .m = { .min = 64, .max = 533 }, \ + .p = { .min = 1, .max = 4 }, \ + .s = { .min = 0, .max = 6 }, \ + .k = { .min = -32768, .max = 32767 }, \ + } \ + #ifdef CONFIG_CLK_RZV2H bool rzv2h_get_pll_pars(const struct rzv2h_pll_limits *limits, struct rzv2h_pll_pars *pars, u64 freq_millihz); diff --git a/include/linux/interconnect.h b/include/linux/interconnect.h index 4b12821528a6..75a32ad0482e 100644 --- a/include/linux/interconnect.h +++ b/include/linux/interconnect.h @@ -47,6 +47,7 @@ struct icc_path *of_icc_get(struct device *dev, const char *name); struct icc_path *devm_of_icc_get(struct device *dev, const char *name); int devm_of_icc_bulk_get(struct device *dev, int num_paths, struct icc_bulk_data *paths); struct icc_path *of_icc_get_by_index(struct device *dev, int idx); +struct icc_path *devm_of_icc_get_by_index(struct device *dev, int idx); void icc_put(struct icc_path *path); int icc_enable(struct icc_path *path); int icc_disable(struct icc_path *path); @@ -79,6 +80,11 @@ static inline struct icc_path *of_icc_get_by_index(struct device *dev, int idx) return NULL; } +static inline struct icc_path *devm_of_icc_get_by_index(struct device *dev, int idx) +{ + return NULL; +} + static inline void icc_put(struct icc_path *path) { } diff --git a/include/linux/ntb.h b/include/linux/ntb.h index 879c3e89e026..ae72caa03859 100644 --- a/include/linux/ntb.h +++ b/include/linux/ntb.h @@ -567,7 +567,7 @@ void ntb_msg_event(struct ntb_dev *ntb); int ntb_default_port_number(struct ntb_dev *ntb); /** - * ntb_default_port_count() - get the default number of peer device ports + * ntb_default_peer_port_count() - get the default number of peer device ports * @ntb: NTB device context. * * By default hardware driver supports just one peer device. @@ -582,7 +582,7 @@ int ntb_default_peer_port_count(struct ntb_dev *ntb); /** * ntb_default_peer_port_number() - get the default peer port by given index * @ntb: NTB device context. - * @idx: Peer port index (should not differ from zero). + * @pidx: Peer port index (should not differ from zero). * * By default hardware driver supports just one peer device, so this method * shall return the corresponding value from enum ntb_default_port. diff --git a/include/linux/rtc.h b/include/linux/rtc.h index 95da051fb155..c09fc22819d0 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -190,6 +190,8 @@ extern int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm); int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm); extern int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alrm); +extern int rtc_read_next_alarm(struct rtc_device *rtc, + struct rtc_wkalrm *alrm); extern int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alrm); extern int rtc_initialize_alarm(struct rtc_device *rtc, diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index a46cbaec5949..b484784e2690 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -11,6 +11,7 @@ #include <linux/idr.h> #include <linux/irq.h> #include <linux/irqdomain.h> +#include <linux/jiffies.h> #include <linux/lockdep_types.h> #include <linux/mod_devicetable.h> #include <linux/mutex.h> @@ -1093,8 +1094,6 @@ int sdw_slave_get_current_bank(struct sdw_slave *sdev); int sdw_slave_get_scale_index(struct sdw_slave *slave, u8 *base); -int sdw_slave_wait_for_init(struct sdw_slave *slave, int timeout_ms); - /* messaging and data APIs */ int sdw_read(struct sdw_slave *slave, u32 addr); int sdw_write(struct sdw_slave *slave, u32 addr, u8 value); @@ -1138,12 +1137,6 @@ static inline int sdw_slave_get_current_bank(struct sdw_slave *sdev) return -EINVAL; } -static inline int sdw_slave_wait_for_init(struct sdw_slave *slave, int timeout_ms) -{ - WARN_ONCE(1, "SoundWire API is disabled"); - return -EINVAL; -} - /* messaging and data APIs */ static inline int sdw_read(struct sdw_slave *slave, u32 addr) { @@ -1207,4 +1200,33 @@ static inline int sdw_update_no_pm(struct sdw_slave *slave, u32 addr, u8 mask, u #endif /* CONFIG_SOUNDWIRE */ +/** + * sdw_slave_wait_for_init - Wait for device initialisation + * @slave: Pointer to the SoundWire peripheral. + * @timeout_ms: Timeout in milliseconds. + * + * Wait for a peripheral device to enumerate and be initialised by the + * SoundWire core. + * + * Return: Zero on success, and a negative error code on failure. + */ +static inline int sdw_slave_wait_for_init(struct sdw_slave *slave, int timeout_ms) +{ + unsigned long time; + + if (!slave) + return 0; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(timeout_ms)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete\n"); + return -ETIMEDOUT; + } + + slave->unattach_request = 0; + + return 0; +} + #endif /* __SOUNDWIRE_H */ diff --git a/include/linux/spmi.h b/include/linux/spmi.h index 28e8c8bd3944..4eb9564a7fb3 100644 --- a/include/linux/spmi.h +++ b/include/linux/spmi.h @@ -76,6 +76,7 @@ void spmi_device_remove(struct spmi_device *sdev); * @cmd: sends a non-data command sequence on the SPMI bus. * @read_cmd: sends a register read command sequence on the SPMI bus. * @write_cmd: sends a register write command sequence on the SPMI bus. + * @priv: array of private data. */ struct spmi_controller { struct device dev; @@ -85,6 +86,7 @@ struct spmi_controller { u8 sid, u16 addr, u8 *buf, size_t len); int (*write_cmd)(struct spmi_controller *ctrl, u8 opcode, u8 sid, u16 addr, const u8 *buf, size_t len); + u8 priv[]; }; static inline struct spmi_controller *to_spmi_controller(struct device *d) @@ -109,7 +111,7 @@ struct spmi_controller *spmi_controller_alloc(struct device *parent, /** * spmi_controller_put() - decrement controller refcount - * @ctrl SPMI controller. + * @ctrl: SPMI controller. */ static inline void spmi_controller_put(struct spmi_controller *ctrl) { @@ -129,6 +131,7 @@ int devm_spmi_controller_add(struct device *parent, struct spmi_controller *ctrl * this structure. * @probe: binds this driver to a SPMI device. * @remove: unbinds this driver from the SPMI device. + * @shutdown: shuts down this driver. * * If PM runtime support is desired for a slave, a device driver can call * pm_runtime_put() from their probe() routine (and a balancing diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 29fd7b735afa..9a2333e62e99 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -145,6 +145,7 @@ struct tcp_ao_info { u32 snd_sne; u32 rcv_sne; refcount_t refcnt; /* Protects twsk destruction */ + struct rcu_head rcu; }; #ifdef CONFIG_TCP_MD5SIG diff --git a/include/soc/rockchip/rk3588_grf.h b/include/soc/rockchip/rk3588_grf.h index 02a7b2432d99..db0092fc66ad 100644 --- a/include/soc/rockchip/rk3588_grf.h +++ b/include/soc/rockchip/rk3588_grf.h @@ -19,4 +19,6 @@ /* Whether the LPDDR5 is in 2:1 (= 0) or 4:1 (= 1) CKR a.k.a. DQS mode */ #define RK3588_PMUGRF_OS_REG6_LP5_CKR BIT(0) +#define RK3588_SYSGRF_SOC_CON6 0x0318 + #endif /* __SOC_RK3588_GRF_H */ diff --git a/include/soc/spacemit/k3-syscon.h b/include/soc/spacemit/k3-syscon.h index 0299bea065a0..a68255dd641f 100644 --- a/include/soc/spacemit/k3-syscon.h +++ b/include/soc/spacemit/k3-syscon.h @@ -168,8 +168,8 @@ #define APMU_CPU_C2_CLK_CTRL 0x394 #define APMU_CPU_C3_CLK_CTRL 0x208 #define APMU_PCIE_CLK_RES_CTRL_A 0x1f0 -#define APMU_PCIE_CLK_RES_CTRL_B 0x1c8 -#define APMU_PCIE_CLK_RES_CTRL_C 0x1d0 +#define APMU_PCIE_CLK_RES_CTRL_B 0x1d0 +#define APMU_PCIE_CLK_RES_CTRL_C 0x1c8 #define APMU_PCIE_CLK_RES_CTRL_D 0x1e0 #define APMU_PCIE_CLK_RES_CTRL_E 0x1e8 #define APMU_EMAC0_CLK_RES_CTRL 0x3e4 diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 11bc9527653f..aa423865dbe7 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -10,6 +10,8 @@ #include <sound/soc.h> +struct device_link; + /* * Component probe and remove ordering levels for components with runtime * dependencies. @@ -216,6 +218,8 @@ struct snd_soc_component { struct list_head card_aux_list; /* for auxiliary bound components */ struct list_head card_list; + struct device_link *card_device_link; + const struct snd_soc_component_driver *driver; struct list_head dai_list; diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h index d45f805b5d18..b763da613769 100644 --- a/include/sound/tas2781.h +++ b/include/sound/tas2781.h @@ -59,9 +59,9 @@ #define TASDEVICE_CHECKSUM_REG TASDEVICE_REG(0x0, 0x0, 0x7e) /* XM_340 */ -#define TASDEVICE_XM_A1_REG TASDEVICE_REG(0x64, 0x63, 0x3c) +#define TASDEVICE_XM_A1_REG TASDEVICE_REG(0x64, 0x02, 0x4c) /* XM_341 */ -#define TASDEVICE_XM_A2_REG TASDEVICE_REG(0x64, 0x63, 0x38) +#define TASDEVICE_XM_A2_REG TASDEVICE_REG(0x64, 0x02, 0x64) /* Volume control */ #define TAS2563_DVC_LVL TASDEVICE_REG(0x00, 0x02, 0x0c) diff --git a/include/trace/events/ceph.h b/include/trace/events/ceph.h index 08cb0659fbfc..1b990632f62b 100644 --- a/include/trace/events/ceph.h +++ b/include/trace/events/ceph.h @@ -226,6 +226,73 @@ TRACE_EVENT(ceph_handle_caps, __entry->mseq) ); +/* + * Client reset tracepoints - identify the client by its monitor- + * assigned global_id so traces remain meaningful when kernel pointer + * hashing is enabled. + */ +TRACE_EVENT(ceph_client_reset_schedule, + TP_PROTO(const struct ceph_mds_client *mdsc, const char *reason), + TP_ARGS(mdsc, reason), + TP_STRUCT__entry( + __field(u64, client_id) + __string(reason, reason ? reason : "") + ), + TP_fast_assign( + __entry->client_id = mdsc->fsc->client->monc.auth ? + mdsc->fsc->client->monc.auth->global_id : 0; + __assign_str(reason); + ), + TP_printk("client_id=%llu reason=%s", + __entry->client_id, __get_str(reason)) +); + +TRACE_EVENT(ceph_client_reset_complete, + TP_PROTO(const struct ceph_mds_client *mdsc, int ret), + TP_ARGS(mdsc, ret), + TP_STRUCT__entry( + __field(u64, client_id) + __field(int, ret) + ), + TP_fast_assign( + __entry->client_id = mdsc->fsc->client->monc.auth ? + mdsc->fsc->client->monc.auth->global_id : 0; + __entry->ret = ret; + ), + TP_printk("client_id=%llu ret=%d", __entry->client_id, __entry->ret) +); + +TRACE_EVENT(ceph_client_reset_blocked, + TP_PROTO(const struct ceph_mds_client *mdsc, int blocked_count), + TP_ARGS(mdsc, blocked_count), + TP_STRUCT__entry( + __field(u64, client_id) + __field(int, blocked_count) + ), + TP_fast_assign( + __entry->client_id = mdsc->fsc->client->monc.auth ? + mdsc->fsc->client->monc.auth->global_id : 0; + __entry->blocked_count = blocked_count; + ), + TP_printk("client_id=%llu blocked_count=%d", __entry->client_id, + __entry->blocked_count) +); + +TRACE_EVENT(ceph_client_reset_unblocked, + TP_PROTO(const struct ceph_mds_client *mdsc, int ret), + TP_ARGS(mdsc, ret), + TP_STRUCT__entry( + __field(u64, client_id) + __field(int, ret) + ), + TP_fast_assign( + __entry->client_id = mdsc->fsc->client->monc.auth ? + mdsc->fsc->client->monc.auth->global_id : 0; + __entry->ret = ret; + ), + TP_printk("client_id=%llu ret=%d", __entry->client_id, __entry->ret) +); + #undef EM #undef E_ #endif /* _TRACE_CEPH_H */ diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index 564071a92d7d..1433809bb166 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -51,6 +51,9 @@ BTF_ID(func, bpf_lsm_key_getsecurity) #ifdef CONFIG_AUDIT BTF_ID(func, bpf_lsm_audit_rule_match) #endif +#ifdef CONFIG_SECURITY_NETWORK_XFRM +BTF_ID(func, bpf_lsm_xfrm_decode_session) +#endif BTF_ID(func, bpf_lsm_ismaclabel) BTF_ID(func, bpf_lsm_file_alloc_security) BTF_SET_END(bpf_lsm_disabled_hooks) diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 15ae7c43f594..64572f85edc8 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -28,6 +28,7 @@ #include <linux/string.h> #include <linux/sysfs.h> #include <linux/overflow.h> +#include <linux/bitops.h> #include <net/netfilter/nf_bpf_link.h> @@ -3472,12 +3473,69 @@ static int btf_find_struct(const struct btf *btf, const struct btf_type *t, return BTF_FIELD_FOUND; } +struct btf_type_tag_match { + const char *name; + u32 flag; +}; + +struct btf_type_tag_walk_ctx { + const struct btf_type *t; /* Input/Output */ + u32 id; /* Output */ + u32 res; /* Output */ +}; + +static int btf_type_tag_walk(const struct btf *btf, + struct btf_type_tag_walk_ctx *ctx, + const struct btf_type_tag_match *matches, + u32 match_cnt) +{ + const struct btf_type *t = ctx->t; + u32 res = 0; + const char *tag; + u32 id, i; + + do { + id = t->type; + t = btf_type_by_id(btf, id); + + if (!btf_type_is_modifier(t)) + break; + + if (!btf_type_is_type_tag(t) || btf_type_kflag(t)) + continue; + + tag = __btf_name_by_offset(btf, t->name_off); + for (i = 0; i < match_cnt; i++) { + if (strcmp(tag, matches[i].name)) + continue; + res |= matches[i].flag; + break; + } + } while (true); + + /* We only support a single tag. */ + if (hweight32(res) > 1) + return -EINVAL; + + ctx->t = t; + ctx->id = id; + ctx->res = res; + + return 0; +} + static int btf_find_kptr(const struct btf *btf, const struct btf_type *t, u32 off, int sz, struct btf_field_info *info, u32 field_mask) { - enum btf_field_type type; - const char *tag_value; - bool is_type_tag; + static const struct btf_type_tag_match kptr_type_tags[] = { + { "kptr_untrusted", BPF_KPTR_UNREF }, + { "kptr", BPF_KPTR_REF }, + { "percpu_kptr", BPF_KPTR_PERCPU }, + { "uptr", BPF_UPTR }, + }; + struct btf_type_tag_walk_ctx ctx; + enum btf_field_type type = 0; + int err; u32 res_id; /* Permit modifiers on the pointer itself */ @@ -3486,30 +3544,20 @@ static int btf_find_kptr(const struct btf *btf, const struct btf_type *t, /* For PTR, sz is always == 8 */ if (!btf_type_is_ptr(t)) return BTF_FIELD_IGNORE; - t = btf_type_by_id(btf, t->type); - is_type_tag = btf_type_is_type_tag(t) && !btf_type_kflag(t); - if (!is_type_tag) - return BTF_FIELD_IGNORE; - /* Reject extra tags */ - if (btf_type_is_type_tag(btf_type_by_id(btf, t->type))) - return -EINVAL; - tag_value = __btf_name_by_offset(btf, t->name_off); - if (!strcmp("kptr_untrusted", tag_value)) - type = BPF_KPTR_UNREF; - else if (!strcmp("kptr", tag_value)) - type = BPF_KPTR_REF; - else if (!strcmp("percpu_kptr", tag_value)) - type = BPF_KPTR_PERCPU; - else if (!strcmp("uptr", tag_value)) - type = BPF_UPTR; - else - return -EINVAL; + + ctx.t = t; + err = btf_type_tag_walk(btf, &ctx, kptr_type_tags, + ARRAY_SIZE(kptr_type_tags)); + if (err) + return err; + + t = ctx.t; + res_id = ctx.id; + type = ctx.res; if (!(type & field_mask)) return BTF_FIELD_IGNORE; - /* Get the base type */ - t = btf_type_skip_modifiers(btf, t->type, &res_id); /* Only pointer to struct is allowed */ if (!__btf_type_is_struct(t)) return -EINVAL; @@ -5859,11 +5907,10 @@ struct btf_struct_meta *btf_find_struct_meta(const struct btf *btf, u32 btf_id) return bsearch(&btf_id, tab->types, tab->cnt, sizeof(tab->types[0]), btf_id_cmp_func); } -static int btf_check_type_tags(struct btf_verifier_env *env, - struct btf *btf, int start_id) +static int btf_check_modifier_chain_length(struct btf_verifier_env *env, + struct btf *btf, int start_id) { int i, n, good_id = start_id - 1; - bool in_tags; n = btf_nr_types(btf); for (i = start_id; i < n; i++) { @@ -5879,20 +5926,12 @@ static int btf_check_type_tags(struct btf_verifier_env *env, cond_resched(); - in_tags = btf_type_is_type_tag(t); while (btf_type_is_modifier(t)) { if (!chain_limit--) { btf_verifier_log(env, "Max chain length or cycle detected"); return -ELOOP; } - if (btf_type_is_type_tag(t)) { - if (!in_tags) { - btf_verifier_log(env, "Type tags don't precede modifiers"); - return -EINVAL; - } - } else if (in_tags) { - in_tags = false; - } + if (cur_id <= good_id) break; /* Move to next type */ @@ -5970,7 +6009,7 @@ static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr, if (err) goto errout; - err = btf_check_type_tags(env, btf, 1); + err = btf_check_modifier_chain_length(env, btf, 1); if (err) goto errout; @@ -6378,7 +6417,7 @@ static struct btf *btf_parse_base(struct btf_verifier_env *env, const char *name if (err) goto errout; - err = btf_check_type_tags(env, btf, 1); + err = btf_check_modifier_chain_length(env, btf, 1); if (err) goto errout; @@ -6504,7 +6543,7 @@ static struct btf *btf_parse_module(const char *module_name, const void *data, if (err) goto errout; - err = btf_check_type_tags(env, btf, btf_nr_types(base_btf)); + err = btf_check_modifier_chain_length(env, btf, btf_nr_types(base_btf)); if (err) goto errout; @@ -6810,14 +6849,18 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, const struct bpf_prog *prog, struct bpf_insn_access_aux *info) { + static const struct btf_type_tag_match ctx_type_tags[] = { + { "user", MEM_USER }, + { "percpu", MEM_PERCPU }, + }; const struct btf_type *t = prog->aux->attach_func_proto; struct bpf_prog *tgt_prog = prog->aux->dst_prog; struct btf *btf = bpf_prog_get_target_btf(prog); const char *tname = prog->aux->attach_func_name; struct bpf_verifier_log *log = info->log; + struct btf_type_tag_walk_ctx ctx; const struct btf_param *args; bool ptr_err_raw_tp = false; - const char *tag_value; u32 nr_args, arg; int i, ret; @@ -7020,22 +7063,18 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, } info->btf = btf; - info->btf_id = t->type; - t = btf_type_by_id(btf, t->type); - - if (btf_type_is_type_tag(t) && !btf_type_kflag(t)) { - tag_value = __btf_name_by_offset(btf, t->name_off); - if (strcmp(tag_value, "user") == 0) - info->reg_type |= MEM_USER; - if (strcmp(tag_value, "percpu") == 0) - info->reg_type |= MEM_PERCPU; + ctx.t = t; + ret = btf_type_tag_walk(btf, &ctx, ctx_type_tags, + ARRAY_SIZE(ctx_type_tags)); + if (ret) { + bpf_log(log, "func '%s' arg%d type %s has multiple type tags\n", + tname, arg, btf_type_str(t)); + return false; } + info->reg_type |= ctx.res; + info->btf_id = ctx.id; + t = ctx.t; - /* skip modifiers */ - while (btf_type_is_modifier(t)) { - info->btf_id = t->type; - t = btf_type_by_id(btf, t->type); - } if (!btf_type_is_struct(t)) { bpf_log(log, "func '%s' arg%d type %s is not a struct\n", @@ -7074,7 +7113,7 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf, u32 i, moff, mtrue_end, msize = 0, total_nelems = 0; const struct btf_type *mtype, *elem_type = NULL; const struct btf_member *member; - const char *tname, *mname, *tag_value; + const char *tname, *mname; u32 vlen, elem_id, mid; again: @@ -7270,8 +7309,15 @@ error: } if (btf_type_is_ptr(mtype)) { - const struct btf_type *stype, *t; + static const struct btf_type_tag_match walk_type_tags[] = { + { "user", MEM_USER }, + { "percpu", MEM_PERCPU }, + { "rcu", MEM_RCU }, + }; enum bpf_type_flag tmp_flag = 0; + struct btf_type_tag_walk_ctx ctx = { .t = mtype }; + const struct btf_type *stype; + int err; u32 id; if (msize != size || off != moff) { @@ -7281,22 +7327,17 @@ error: return -EACCES; } - /* check type tag */ - t = btf_type_by_id(btf, mtype->type); - if (btf_type_is_type_tag(t) && !btf_type_kflag(t)) { - tag_value = __btf_name_by_offset(btf, t->name_off); - /* check __user tag */ - if (strcmp(tag_value, "user") == 0) - tmp_flag = MEM_USER; - /* check __percpu tag */ - if (strcmp(tag_value, "percpu") == 0) - tmp_flag = MEM_PERCPU; - /* check __rcu tag */ - if (strcmp(tag_value, "rcu") == 0) - tmp_flag = MEM_RCU; + err = btf_type_tag_walk(btf, &ctx, walk_type_tags, + ARRAY_SIZE(walk_type_tags)); + if (err) { + bpf_log(log, "type '%s' has multiple type tags\n", + btf_type_str(mtype)); + return err; } + tmp_flag = ctx.res; + id = ctx.id; + stype = ctx.t; - stype = btf_type_skip_modifiers(btf, mtype->type, &id); if (btf_type_is_struct(stype)) { *next_btf_id = id; *flag |= tmp_flag; @@ -7867,7 +7908,12 @@ static int btf_scan_type_tags(struct bpf_verifier_env *env, const struct btf *btf, u32 type_id, u32 *tags) { + static const struct btf_type_tag_match func_type_tags[] = { + { "arena", ARG_TAG_ARENA }, + }; + struct btf_type_tag_walk_ctx ctx; const struct btf_type *t; + int err; /* Find the first pointer type in the chain. */ t = btf_type_skip_modifiers(btf, type_id, NULL); @@ -7879,24 +7925,15 @@ static int btf_scan_type_tags(struct bpf_verifier_env *env, if (!t || !btf_type_is_ptr(t)) return 0; - /* We got a pointer, get all associated type tags. */ - for (t = btf_type_by_id(btf, t->type); t && btf_type_is_modifier(t); - t = btf_type_by_id(btf, t->type)) { - - /* Skip non-type tag modifiers. */ - if (!btf_type_is_type_tag(t)) - continue; - - const char *tag = __btf_name_by_offset(btf, t->name_off); - - if (strcmp(tag, "arena") == 0) { - *tags |= ARG_TAG_ARENA; - } else { - bpf_log(&env->log, "function signature member has unsupported type tag '%s'\n", - tag); - return -EOPNOTSUPP; - } + ctx.t = t; + err = btf_type_tag_walk(btf, &ctx, func_type_tags, + ARRAY_SIZE(func_type_tags)); + if (err) { + bpf_log(&env->log, + "function signature member has multiple type tags\n"); + return err; } + *tags |= ctx.res; return 0; } diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 83ce66296ac1..4355ccb78a9c 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -939,19 +939,65 @@ static int cgroup_bpf_attach(struct cgroup *cgrp, return ret; } +static int effective_prog_pos(struct cgroup *cgrp, + enum cgroup_bpf_attach_type atype, + struct bpf_prog_list *target_pl) +{ + int cnt = 0, preorder_cnt = 0, fstart, bstart, init_bstart, pos = -1; + struct bpf_prog_list *pl; + struct cgroup *p = cgrp; + + /* count effective programs to find where the preorder region ends */ + do { + if (cnt == 0 || (p->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) + cnt += prog_list_length(&p->bpf.progs[atype], &preorder_cnt); + p = cgroup_parent(p); + } while (p); + + /* replay compute_effective_progs() placement and record target's slot */ + cnt = 0; + p = cgrp; + fstart = preorder_cnt; + bstart = preorder_cnt - 1; + do { + if (cnt > 0 && !(p->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) + continue; + + init_bstart = bstart; + hlist_for_each_entry(pl, &p->bpf.progs[atype], node) { + if (!prog_list_prog(pl)) + continue; + + if (pl->flags & BPF_F_PREORDER) { + if (pl == target_pl) + pos = bstart; + bstart--; + } else { + if (pl == target_pl) + pos = fstart; + fstart++; + } + cnt++; + } + + /* reverse pre-ordering progs at this cgroup level */ + if (pos >= bstart + 1 && pos <= init_bstart) + pos = bstart + 1 + init_bstart - pos; + } while ((p = cgroup_parent(p))); + + return pos; +} + /* Swap updated BPF program for given link in effective program arrays across * all descendant cgroups. This function is guaranteed to succeed. */ static void replace_effective_prog(struct cgroup *cgrp, enum cgroup_bpf_attach_type atype, - struct bpf_cgroup_link *link) + struct bpf_prog_list *pl) { struct bpf_prog_array_item *item; struct cgroup_subsys_state *css; struct bpf_prog_array *progs; - struct bpf_prog_list *pl; - struct hlist_head *head; - struct cgroup *cg; int pos; css_for_each_descendant_pre(css, &cgrp->self) { @@ -960,27 +1006,15 @@ static void replace_effective_prog(struct cgroup *cgrp, if (percpu_ref_is_zero(&desc->bpf.refcnt)) continue; - /* find position of link in effective progs array */ - for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) { - if (pos && !(cg->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) - continue; + pos = effective_prog_pos(desc, atype, pl); + if (WARN_ON_ONCE(pos < 0)) + continue; - head = &cg->bpf.progs[atype]; - hlist_for_each_entry(pl, head, node) { - if (!prog_list_prog(pl)) - continue; - if (pl->link == link) - goto found; - pos++; - } - } -found: - BUG_ON(!cg); progs = rcu_dereference_protected( desc->bpf.effective[atype], lockdep_is_held(&cgroup_mutex)); item = &progs->items[pos]; - WRITE_ONCE(item->prog, link->link.prog); + WRITE_ONCE(item->prog, pl->link->link.prog); } } @@ -1024,7 +1058,7 @@ static int __cgroup_bpf_replace(struct cgroup *cgrp, cgrp->bpf.revisions[atype] += 1; old_prog = xchg(&link->link.prog, new_prog); - replace_effective_prog(cgrp, atype, link); + replace_effective_prog(cgrp, atype, pl); bpf_prog_put(old_prog); return 0; } @@ -1091,19 +1125,14 @@ static struct bpf_prog_list *find_detach_entry(struct hlist_head *progs, * recomputing the array in place. * * @cgrp: The cgroup which descendants to travers - * @prog: A program to detach or NULL - * @link: A link to detach or NULL + * @pl: The prog_list entry being detached * @atype: Type of detach operation */ -static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog, - struct bpf_cgroup_link *link, +static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog_list *pl, enum cgroup_bpf_attach_type atype) { struct cgroup_subsys_state *css; struct bpf_prog_array *progs; - struct bpf_prog_list *pl; - struct hlist_head *head; - struct cgroup *cg; int pos; /* recompute effective prog array in place */ @@ -1113,24 +1142,11 @@ static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog, if (percpu_ref_is_zero(&desc->bpf.refcnt)) continue; - /* find position of link or prog in effective progs array */ - for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) { - if (pos && !(cg->bpf.flags[atype] & BPF_F_ALLOW_MULTI)) - continue; - - head = &cg->bpf.progs[atype]; - hlist_for_each_entry(pl, head, node) { - if (!prog_list_prog(pl)) - continue; - if (pl->prog == prog && pl->link == link) - goto found; - pos++; - } - } - + pos = effective_prog_pos(desc, atype, pl); /* no link or prog match, skip the cgroup of this layer */ - continue; -found: + if (pos < 0) + continue; + progs = rcu_dereference_protected( desc->bpf.effective[atype], lockdep_is_held(&cgroup_mutex)); @@ -1196,7 +1212,7 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, /* if update effective array failed replace the prog with a dummy prog*/ pl->prog = old_prog; pl->link = link; - purge_effective_progs(cgrp, old_prog, link, atype); + purge_effective_progs(cgrp, pl, atype); } /* now can actually delete it from this cgroup list */ diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c index 77ba03216c09..41fe87d7302f 100644 --- a/kernel/bpf/stackmap.c +++ b/kernel/bpf/stackmap.c @@ -175,6 +175,95 @@ static inline void stack_map_build_id_set_valid(struct bpf_stack_build_id *id, memcpy(id->build_id, build_id, BUILD_ID_SIZE_MAX); } +/* + * A cached VMA lookup result. The range [vm_start, vm_end) is always set. + * vm_pgoff, file, build_id are set only when the build ID was resolved. + * Zero vm_end marks the slot empty. build_id aliases the id_offs[] entry. + */ +struct stack_map_cached_vma { + unsigned long vm_start; + unsigned long vm_end; + unsigned long vm_pgoff; + struct file *file; /* pinned in the sleepable path; NULL otherwise */ + const unsigned char *build_id; +}; + +/* + * Per stack_map_get_build_id_offset() call cache of the last VMA with a build ID + * resolved and the last VMA with no usable build ID. Adjacent stack frames tend + * to land in the same VMA or the same backing file, so caching the last result + * of each kind lets us skip unnecessary VMA lookups and build ID parse calls. + * Keeping the two slots independent means a build-ID-less VMA doesn't evict the + * last resolved build ID. + */ +struct stack_map_build_id_cache { + struct stack_map_cached_vma resolved; + struct stack_map_cached_vma unresolved; +}; + +/* + * Fill @id from a cached range covering @ip. On a hit this writes @id (resolved + * range -> build ID + offset, unresolved range -> raw ip) and returns 0; on a + * miss it leaves @id untouched and returns -ENOENT. + */ +static int stack_map_build_id_set_from_cache(struct stack_map_build_id_cache *cache, + struct bpf_stack_build_id *id, u64 ip) +{ + unsigned long vm_start, vm_end, vm_pgoff; + u64 offset; + + vm_start = cache->resolved.vm_start; + vm_end = cache->resolved.vm_end; + if (vm_end && ip >= vm_start && ip < vm_end) { + vm_pgoff = cache->resolved.vm_pgoff; + offset = stack_map_build_id_offset(vm_pgoff, vm_start, ip); + stack_map_build_id_set_valid(id, offset, cache->resolved.build_id); + return 0; + } + + vm_start = cache->unresolved.vm_start; + vm_end = cache->unresolved.vm_end; + if (vm_end && ip >= vm_start && ip < vm_end) { + stack_map_build_id_set_ip(id); + return 0; + } + + return -ENOENT; +} + +/* + * Record @vma's build ID as the last resolved one. @file is the pinned backing + * file in the sleepable path (released when evicted), or NULL otherwise. + */ +static void stack_map_build_id_cache_set_resolved(struct stack_map_build_id_cache *cache, + struct file *file, + const unsigned char *build_id, + unsigned long vm_start, + unsigned long vm_end, + unsigned long vm_pgoff) +{ + if (cache->resolved.file) + fput(cache->resolved.file); + cache->resolved = (struct stack_map_cached_vma){ + .vm_start = vm_start, + .vm_end = vm_end, + .vm_pgoff = vm_pgoff, + .file = file, + .build_id = build_id, + }; +} + +/* Record [vm_start, vm_end) as a range with no usable build ID. */ +static void stack_map_build_id_cache_set_unresolved(struct stack_map_build_id_cache *cache, + unsigned long vm_start, + unsigned long vm_end) +{ + cache->unresolved = (struct stack_map_cached_vma){ + .vm_start = vm_start, + .vm_end = vm_end, + }; +} + struct stack_map_vma_lock { struct vm_area_struct *vma; struct mm_struct *mm; @@ -244,15 +333,9 @@ static void stack_map_unlock_vma(struct stack_map_vma_lock *lock) static void stack_map_get_build_id_offset_sleepable(struct bpf_stack_build_id *id_offs, u32 trace_nr) { - struct mm_struct *mm = current->mm; - struct stack_map_vma_lock lock = { .mm = mm }; - struct { - struct file *file; - const unsigned char *build_id; - unsigned long vm_start; - unsigned long vm_end; - unsigned long vm_pgoff; - } cache = {}; + struct stack_map_vma_lock lock = { .mm = current->mm }; + struct stack_map_build_id_cache cache = {}; + struct stack_map_cached_vma *res = &cache.resolved; unsigned long vm_pgoff, vm_start, vm_end; struct vm_area_struct *vma; struct file *file; @@ -262,44 +345,39 @@ static void stack_map_get_build_id_offset_sleepable(struct bpf_stack_build_id *i for (u32 i = 0; i < trace_nr; i++) { ip = READ_ONCE(id_offs[i].ip); - /* - * Range cache fast path: if ip falls within the previously - * resolved VMA range, reuse the cache build_id without - * re-acquiring the VMA lock. - */ - if (cache.build_id && ip >= cache.vm_start && ip < cache.vm_end) { - offset = stack_map_build_id_offset(cache.vm_pgoff, cache.vm_start, ip); - stack_map_build_id_set_valid(&id_offs[i], offset, cache.build_id); + if (!stack_map_build_id_set_from_cache(&cache, &id_offs[i], ip)) continue; - } vma = stack_map_lock_vma(&lock, ip); if (!vma) { stack_map_build_id_set_ip(&id_offs[i]); continue; } + + vm_pgoff = vma->vm_pgoff; + vm_start = vma->vm_start; + vm_end = vma->vm_end; + if (vma_is_anonymous(vma) || !vma->vm_file) { - stack_map_build_id_set_ip(&id_offs[i]); stack_map_unlock_vma(&lock); + stack_map_build_id_set_ip(&id_offs[i]); + stack_map_build_id_cache_set_unresolved(&cache, vm_start, vm_end); continue; } file = vma->vm_file; - vm_pgoff = vma->vm_pgoff; - vm_start = vma->vm_start; - vm_end = vma->vm_end; offset = stack_map_build_id_offset(vm_pgoff, vm_start, ip); /* - * Same backing file as previous (e.g. different VMAs - * of the same ELF binary). Reuse the cache build_id. + * Same backing file as the last resolved VMA (another mapping + * of the same ELF binary): reuse its build_id without re-parsing. */ - if (file == cache.file) { + if (file == res->file) { stack_map_unlock_vma(&lock); - stack_map_build_id_set_valid(&id_offs[i], offset, cache.build_id); - cache.vm_start = vm_start; - cache.vm_end = vm_end; - cache.vm_pgoff = vm_pgoff; + stack_map_build_id_set_valid(&id_offs[i], offset, res->build_id); + res->vm_start = vm_start; + res->vm_end = vm_end; + res->vm_pgoff = vm_pgoff; continue; } @@ -310,21 +388,17 @@ static void stack_map_get_build_id_offset_sleepable(struct bpf_stack_build_id *i if (build_id_parse_file(file, id_offs[i].build_id, NULL)) { stack_map_build_id_set_ip(&id_offs[i]); fput(file); + stack_map_build_id_cache_set_unresolved(&cache, vm_start, vm_end); continue; } stack_map_build_id_set_valid(&id_offs[i], offset, id_offs[i].build_id); - if (cache.file) - fput(cache.file); - cache.file = file; - cache.build_id = id_offs[i].build_id; - cache.vm_start = vm_start; - cache.vm_end = vm_end; - cache.vm_pgoff = vm_pgoff; + stack_map_build_id_cache_set_resolved(&cache, file, id_offs[i].build_id, + vm_start, vm_end, vm_pgoff); } - if (cache.file) - fput(cache.file); + if (res->file) + fput(res->file); } /* @@ -343,8 +417,8 @@ static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs, struct mmap_unlock_irq_work *work = NULL; bool irq_work_busy = bpf_mmap_unlock_get_irq_work(&work); bool has_user_ctx = user && current && current->mm; - struct vm_area_struct *vma, *prev_vma = NULL; - const unsigned char *prev_build_id = NULL; + struct stack_map_build_id_cache cache = {}; + struct vm_area_struct *vma; int i; if (may_fault && has_user_ctx) { @@ -365,27 +439,30 @@ static void stack_map_get_build_id_offset(struct bpf_stack_build_id *id_offs, for (i = 0; i < trace_nr; i++) { u64 ip = READ_ONCE(id_offs[i].ip); - u64 offset; - if (prev_build_id && range_in_vma(prev_vma, ip, ip)) { - vma = prev_vma; - offset = stack_map_build_id_offset(vma->vm_pgoff, vma->vm_start, ip); - stack_map_build_id_set_valid(&id_offs[i], offset, prev_build_id); + if (!stack_map_build_id_set_from_cache(&cache, &id_offs[i], ip)) continue; - } + vma = find_vma(current->mm, ip); if (!vma || vma_is_anonymous(vma) || fetch_build_id(vma, id_offs[i].build_id, may_fault)) { - /* per entry fall back to ips */ + /* per entry fall back to ips; cache build-ID-less range */ stack_map_build_id_set_ip(&id_offs[i]); - prev_vma = vma; - prev_build_id = NULL; + if (vma) + stack_map_build_id_cache_set_unresolved(&cache, + vma->vm_start, vma->vm_end); continue; } - offset = stack_map_build_id_offset(vma->vm_pgoff, vma->vm_start, ip); - stack_map_build_id_set_valid(&id_offs[i], offset, id_offs[i].build_id); - prev_vma = vma; - prev_build_id = id_offs[i].build_id; + /* + * mmap_lock is held for the whole loop, so the cached VMA + * fields stay valid; no file pinning is needed here. + */ + stack_map_build_id_set_valid(&id_offs[i], + stack_map_build_id_offset(vma->vm_pgoff, vma->vm_start, ip), + id_offs[i].build_id); + stack_map_build_id_cache_set_resolved(&cache, NULL, id_offs[i].build_id, + vma->vm_start, vma->vm_end, + vma->vm_pgoff); } bpf_mmap_unlock_mm(work, current->mm); } diff --git a/kernel/bpf/states.c b/kernel/bpf/states.c index 32f346ce3ffc..ea2153cf28d0 100644 --- a/kernel/bpf/states.c +++ b/kernel/bpf/states.c @@ -436,12 +436,10 @@ static void __clean_func_state(struct bpf_verifier_env *env, continue; /* - * Only destroy spilled_ptr when hi half is dead. - * If hi half is still live with STACK_SPILL, the - * spilled_ptr metadata is needed for correct state - * comparison in stacksafe(). - * is_spilled_reg() is using slot_type[7], but - * is_spilled_scalar_after() check either slot_type[0] or [4] + * Only scalar spills can be degraded to raw stack bytes + * when their high half is dead. Pointer spills need the + * saved spilled_ptr metadata so partial fills keep + * rejecting as non-scalar register fills. */ if (!hi_live) { struct bpf_reg_state *spill = &st->stack[i].spilled_ptr; @@ -449,6 +447,9 @@ static void __clean_func_state(struct bpf_verifier_env *env, if (lo_live && stype == STACK_SPILL) { u8 val = STACK_MISC; + if (spill->type != SCALAR_VALUE) + continue; + /* * 8 byte spill of scalar 0 where half slot is dead * should become STACK_ZERO in lo 4 bytes. diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b44106c8ea75..6db306d23b47 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -6308,7 +6308,7 @@ static int prog_stream_read(union bpf_attr *attr) return ret; } -#define BPF_PROG_ASSOC_STRUCT_OPS_LAST_FIELD prog_assoc_struct_ops.prog_fd +#define BPF_PROG_ASSOC_STRUCT_OPS_LAST_FIELD prog_assoc_struct_ops.flags static int prog_assoc_struct_ops(union bpf_attr *attr) { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 2abc79dbf281..21a365d436a5 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3479,7 +3479,8 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env, bool sanitize = reg && is_spillable_regtype(reg->type); for (i = 0; i < size; i++) { - u8 type = state->stack[spi].slot_type[i]; + u8 type = state->stack[spi].slot_type[(slot - i) % + BPF_REG_SIZE]; if (type != STACK_MISC && type != STACK_ZERO) { sanitize = true; @@ -5786,6 +5787,10 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, return -EFAULT; } ret = env->ops->btf_struct_access(&env->log, reg, off, size); + if (ret < 0) + verbose(env, + "%s cannot write into ptr_%s at off=%d size=%d\n", + reg_arg_name(env, argno), tname, off, size); } else { /* Writes are permitted with default btf_struct_access for * program allocated objects (which always have id > 0), @@ -6196,6 +6201,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b */ if (info.reg_type == SCALAR_VALUE) { if (info.is_retval && get_func_retval_range(env->prog, &range)) { + mark_reg_unknown(env, regs, value_regno); err = __mark_reg_s32_range(env, regs, value_regno, range.minval, range.maxval); if (err) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index ae9fd211cec1..a4e689eefdfb 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -486,6 +486,7 @@ static void sugov_update_single_perf(struct update_util_data *hook, u64 time, cpufreq_driver_adjust_perf(sg_policy->policy, sg_cpu->bw_min, sg_cpu->util, max_cap); + sg_policy->need_freq_update = false; sg_policy->last_freq_update_time = time; } diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 82f8feea6931..75495a5c3507 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -2376,9 +2376,12 @@ static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32 int err = -ENOMEM; unsigned int i; + if (!access_ok(usyms, cnt * sizeof(*usyms))) + return -EFAULT; + syms = kvmalloc_array(cnt, sizeof(*syms), GFP_KERNEL); if (!syms) - goto error; + return -ENOMEM; buf = kvmalloc_array(cnt, KSYM_NAME_LEN, GFP_KERNEL); if (!buf) @@ -2403,10 +2406,8 @@ static int copy_user_syms(struct user_syms *us, unsigned long __user *usyms, u32 return 0; error: - if (err) { - kvfree(syms); - kvfree(buf); - } + kvfree(syms); + kvfree(buf); return err; } diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c index f378613ad120..f215990b9061 100644 --- a/kernel/trace/fprobe.c +++ b/kernel/trace/fprobe.c @@ -613,6 +613,16 @@ static int fprobe_fgraph_entry(struct ftrace_graph_ent *trace, struct fgraph_ops continue; data_size = fp->entry_data_size; + /* + * The list may have grown since it was sized, so this node + * may not fit. Skip it as missed rather than overrun the + * reservation. + */ + if (fp->exit_handler && + used + FPROBE_HEADER_SIZE_IN_LONG + SIZE_IN_LONG(data_size) > reserved_words) { + fp->nmissed++; + continue; + } if (data_size && fp->exit_handler) data = fgraph_data + used + FPROBE_HEADER_SIZE_IN_LONG; else diff --git a/kernel/trace/trace_eprobe.c b/kernel/trace/trace_eprobe.c index b66d6196338d..50518b071414 100644 --- a/kernel/trace/trace_eprobe.c +++ b/kernel/trace/trace_eprobe.c @@ -315,7 +315,7 @@ get_event_field(struct fetch_insn *code, void *rec) val = (unsigned long)addr; break; case FILTER_PTR_STRING: - val = (unsigned long)(*(char *)addr); + val = *(unsigned long *)addr; break; default: WARN_ON_ONCE(1); diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c index fd1caa1f9723..d17cfee77d9c 100644 --- a/kernel/trace/trace_probe.c +++ b/kernel/trace/trace_probe.c @@ -342,10 +342,6 @@ static int parse_trace_event(char *arg, struct fetch_insn *code, ret = parse_trace_event_arg(arg, code, ctx); if (!ret) return 0; - if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) { - code->op = FETCH_OP_COMM; - return 0; - } return -EINVAL; } @@ -678,7 +674,7 @@ static int parse_btf_arg(char *varname, int i, is_ptr, ret; u32 tid; - if (WARN_ON_ONCE(!ctx->funcname && !(ctx->flags & TPARG_FL_TEVENT))) + if (!ctx->funcname && !(ctx->flags & TPARG_FL_TEVENT)) return -EINVAL; is_ptr = split_next_field(varname, &field, ctx); @@ -1068,8 +1064,14 @@ static int parse_probe_vars(char *orig_arg, const struct fetch_type *t, int len; if (ctx->flags & TPARG_FL_TEVENT) { - if (parse_trace_event(arg, code, ctx) < 0) + if (parse_trace_event(arg, code, ctx) < 0) { + /* 'comm' should be checked after field parsing. */ + if (strcmp(arg, "comm") == 0 || strcmp(arg, "COMM") == 0) { + code->op = FETCH_OP_COMM; + return 0; + } goto inval; + } return 0; } @@ -1241,6 +1243,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type, code->op = FETCH_OP_FOFFS; code->immediate = (unsigned long)offset; // imm64? + offset = 0; } else { /* uprobes don't support symbols */ if (!(ctx->flags & TPARG_FL_KERNEL)) { diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index 15758cc11fc6..0f09f7aaf93f 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -511,7 +511,7 @@ extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, C(NO_RETVAL, "This function returns 'void' type"), \ C(BAD_STACK_NUM, "Invalid stack number"), \ C(BAD_ARG_NUM, "Invalid argument number"), \ - C(BAD_VAR, "Invalid $-valiable specified"), \ + C(BAD_VAR, "Invalid $-variable specified"), \ C(BAD_REG_NAME, "Invalid register name"), \ C(BAD_MEM_ADDR, "Invalid memory address"), \ C(BAD_IMM, "Invalid immediate value"), \ diff --git a/lib/bootconfig.c b/lib/bootconfig.c index f445b7703fdd..2ed9ee3dc81c 100644 --- a/lib/bootconfig.c +++ b/lib/bootconfig.c @@ -427,10 +427,18 @@ static char xbc_namebuf[XBC_KEYLEN_MAX] __initdata; int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root) { struct xbc_node *knode, *vnode; - char *end = buf + size; const char *val, *q; + size_t len = 0; int ret; + /* + * Track the running written length rather than advancing @buf, so we + * never form "buf + size" or "buf += ret" while @buf is NULL (the + * size-probe call passes buf=NULL, size=0). NULL pointer arithmetic + * is undefined behavior and trips host UBSan / FORTIFY_SOURCE when + * this renderer runs at kernel build time. snprintf(NULL, 0, ...) + * itself is well defined and returns the would-be length. + */ xbc_node_for_each_key_value(root, knode, val) { ret = xbc_node_compose_key_after(root, knode, xbc_namebuf, XBC_KEYLEN_MAX); @@ -439,10 +447,11 @@ int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root) vnode = xbc_node_get_child(knode); if (!vnode) { - ret = snprintf(buf, rest(buf, end), "%s ", xbc_namebuf); + ret = snprintf(buf ? buf + len : NULL, rest(len, size), + "%s ", xbc_namebuf); if (ret < 0) return ret; - buf += ret; + len += ret; continue; } xbc_array_for_each_value(vnode, val) { @@ -452,15 +461,15 @@ int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root) * whitespace. */ q = strpbrk(val, " \t\r\n") ? "\"" : ""; - ret = snprintf(buf, rest(buf, end), "%s=%s%s%s ", - xbc_namebuf, q, val, q); + ret = snprintf(buf ? buf + len : NULL, rest(len, size), + "%s=%s%s%s ", xbc_namebuf, q, val, q); if (ret < 0) return ret; - buf += ret; + len += ret; } } - return buf - (end - size); + return len; } #undef rest diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index ae39ceaa2e29..c40c9e02391b 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1066,6 +1066,9 @@ out: * @skb: the buffer containing the packet to extract the VID from * @hdr_size: the size of the batman-adv header encapsulating the packet * + * The caller must ensure that at least @hdr_size + ETH_HLEN bytes are + * accessible after skb->data. + * * Return: If the packet embedded in the skb is vlan tagged this function * returns the VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS * is returned. @@ -1148,6 +1151,10 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, if (!READ_ONCE(bat_priv->distributed_arp_table)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1243,6 +1250,10 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, if (!READ_ONCE(bat_priv->distributed_arp_table)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1305,6 +1316,10 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, if (!READ_ONCE(bat_priv->distributed_arp_table)) return; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + return; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1353,6 +1368,10 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, if (!READ_ONCE(bat_priv->distributed_arp_table)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(skb, &hdr_size); type = batadv_arp_get_type(bat_priv, skb, hdr_size); @@ -1747,6 +1766,7 @@ void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv, struct ethhdr *ethhdr; __be32 ip_src, yiaddr; unsigned short vid; + int hdr_size_tmp; __be16 proto; u8 *hw_src; @@ -1763,8 +1783,10 @@ void batadv_dat_snoop_incoming_dhcp_ack(struct batadv_priv *bat_priv, if (!batadv_dat_check_dhcp_ack(skb, proto, &ip_src, chaddr, &yiaddr)) return; + hdr_size_tmp = hdr_size; + vid = batadv_dat_get_vid(skb, &hdr_size_tmp); + ethhdr = (struct ethhdr *)(skb->data + hdr_size); hw_src = ethhdr->h_source; - vid = batadv_dat_get_vid(skb, &hdr_size); batadv_dat_entry_add(bat_priv, yiaddr, chaddr, vid); batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); @@ -1804,6 +1826,10 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, if (batadv_forw_packet_is_rebroadcast(forw_packet)) goto out; + /* first, find out the vid. */ + if (!pskb_may_pull(forw_packet->skb, hdr_size + ETH_HLEN)) + goto out; + vid = batadv_dat_get_vid(forw_packet->skb, &hdr_size); type = batadv_arp_get_type(bat_priv, forw_packet->skb, hdr_size); diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 305488a74a25..a5ac82eabd25 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -684,12 +684,13 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, struct batadv_gw_node *gw_node = NULL; struct batadv_gw_node *curr_gw = NULL; struct batadv_neigh_ifinfo *curr_ifinfo, *old_ifinfo; - struct ethhdr *ethhdr = (struct ethhdr *)skb->data; + struct ethhdr *ethhdr; bool out_of_range = false; u8 curr_tq_avg; unsigned short vid; vid = batadv_get_vid(skb, 0); + ethhdr = (struct ethhdr *)skb->data; if (is_multicast_ether_addr(ethhdr->h_dest)) goto out; diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 04bb030ef299..fb2b0e35e349 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -571,6 +571,9 @@ void batadv_recv_handler_unregister(u8 packet_type) * @skb: the buffer containing the packet * @header_len: length of the batman header preceding the ethernet header * + * The caller must ensure that at least @header_len + ETH_HLEN bytes are + * accessible after skb->data. + * * Return: VID with the BATADV_VLAN_HAS_TAG flag when the packet embedded in the * skb is vlan tagged. Otherwise BATADV_NO_FLAGS. */ diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c index a37368c1f5b5..5cc72bbc4570 100644 --- a/net/batman-adv/mesh-interface.c +++ b/net/batman-adv/mesh-interface.c @@ -434,6 +434,7 @@ void batadv_interface_rx(struct net_device *mesh_iface, if (!pskb_may_pull(skb, VLAN_ETH_HLEN)) goto dropped; + ethhdr = eth_hdr(skb); vhdr = skb_vlan_eth_hdr(skb); /* drop batman-in-batman packets to prevent loops */ diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index c05fcc9241ad..bbd40fe3a8e5 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -855,8 +855,8 @@ static bool batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, if (skb_cow(skb, sizeof(*unicast_packet)) < 0) return false; - unicast_packet = (struct batadv_unicast_packet *)skb->data; vid = batadv_get_vid(skb, hdr_len); + unicast_packet = (struct batadv_unicast_packet *)skb->data; ethhdr = (struct ethhdr *)(skb->data + hdr_len); /* do not reroute multicast frames in a unicast header */ @@ -1029,6 +1029,7 @@ int batadv_recv_unicast_packet(struct sk_buff *skb, hdr_size); batadv_orig_node_put(orig_node_gw); if (is_gw) { + orig_addr_gw = eth_hdr(skb)->h_source; batadv_dbg(BATADV_DBG_BLA, bat_priv, "%s(): Dropped unicast pkt received from another backbone gw %pM.\n", __func__, orig_addr_gw); diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 7fdee8f52ee2..5d51f6cb7d15 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -452,12 +452,8 @@ static int bpf_test_finish(const union bpf_attr *kattr, } if (data_out) { - int len = sinfo ? copy_size - frag_size : copy_size; - - if (len < 0) { - err = -ENOSPC; - goto out; - } + u32 head_len = size - frag_size; + u32 len = min(copy_size, head_len); if (copy_to_user(data_out, data, len)) goto out; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 7ed19aa8ae59..c52613431f88 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -392,6 +392,9 @@ void br_dev_delete(struct net_device *dev, struct list_head *head) br_fdb_delete_by_port(br, NULL, 0, 1); + timer_shutdown_sync(&br->hello_timer); + timer_shutdown_sync(&br->topology_change_timer); + timer_shutdown_sync(&br->tcn_timer); cancel_delayed_work_sync(&br->gc_work); br_sysfs_delbr(br->dev); diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index 46919d73d42f..c7e7e924f155 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -382,7 +382,8 @@ void br_topology_change_detection(struct net_bridge *br) { int isroot = br_is_root_bridge(br); - if (br->stp_enabled != BR_KERNEL_STP) + if (br->stp_enabled != BR_KERNEL_STP || + !(br->dev->flags & IFF_UP)) return; br_info(br, "topology change detected, %s\n", diff --git a/net/core/filter.c b/net/core/filter.c index 69ec1a4c0f9d..b446aa8be5c3 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -6221,7 +6221,7 @@ static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params, struct in_device *in_dev; struct net_device *dev; struct fib_result res; - struct flowi4 fl4; + struct flowi4 fl4 = {}; u32 mtu = 0; int err; @@ -6361,7 +6361,7 @@ static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params, struct neighbour *neigh; struct net_device *dev; struct inet6_dev *idev; - struct flowi6 fl6; + struct flowi6 fl6 = {}; int strict = 0; int oif, err; u32 mtu = 0; diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 85aa51350881..aed415d3cd74 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -633,14 +633,6 @@ static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head) container_of(rcu_head, struct netpoll_info, rcu); skb_queue_purge(&npinfo->txq); - - /* we can't call cancel_delayed_work_sync here, as we are in softirq */ - cancel_delayed_work(&npinfo->tx_work); - - /* clean after last, unfinished work */ - __skb_queue_purge(&npinfo->txq); - /* now cancel it again */ - cancel_delayed_work(&npinfo->tx_work); kfree(npinfo); } @@ -664,6 +656,7 @@ static void __netpoll_cleanup(struct netpoll *np) ops->ndo_netpoll_cleanup(np->dev); RCU_INIT_POINTER(np->dev->npinfo, NULL); + disable_delayed_work_sync(&npinfo->tx_work); call_rcu(&npinfo->rcu, rcu_cleanup_netpoll_info); } diff --git a/net/dsa/tag.c b/net/dsa/tag.c index 79ad105902d9..991732d6eae2 100644 --- a/net/dsa/tag.c +++ b/net/dsa/tag.c @@ -79,15 +79,16 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, if (likely(skb->dev)) { dsa_default_offload_fwd_mark(skb); nskb = skb; + } else { + /* Just drop the skb if we can't find the user */ + kfree_skb(skb); } } else { nskb = cpu_dp->rcv(skb, dev); } - if (!nskb) { - kfree_skb(skb); + if (!nskb) return 0; - } skb = nskb; skb_push(skb, ETH_HLEN); @@ -107,11 +108,10 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, if (unlikely(cpu_dp->ds->untag_bridge_pvid || cpu_dp->ds->untag_vlan_aware_bridge_pvid)) { + /* dsa_software_vlan_untag() drops skb on failure */ nskb = dsa_software_vlan_untag(skb); - if (!nskb) { - kfree_skb(skb); + if (!nskb) return 0; - } skb = nskb; } diff --git a/net/dsa/tag_ar9331.c b/net/dsa/tag_ar9331.c index cbb588ca73aa..2e2388143b02 100644 --- a/net/dsa/tag_ar9331.c +++ b/net/dsa/tag_ar9331.c @@ -51,8 +51,10 @@ static struct sk_buff *ar9331_tag_rcv(struct sk_buff *skb, u8 ver, port; u16 hdr; - if (unlikely(!pskb_may_pull(skb, AR9331_HDR_LEN))) + if (unlikely(!pskb_may_pull(skb, AR9331_HDR_LEN))) { + kfree_skb(skb); return NULL; + } hdr = le16_to_cpu(*(__le16 *)skb_mac_header(skb)); @@ -60,12 +62,14 @@ static struct sk_buff *ar9331_tag_rcv(struct sk_buff *skb, if (unlikely(ver != AR9331_HDR_VERSION)) { netdev_warn_once(ndev, "%s:%i wrong header version 0x%2x\n", __func__, __LINE__, hdr); + kfree_skb(skb); return NULL; } if (unlikely(hdr & AR9331_HDR_FROM_CPU)) { netdev_warn_once(ndev, "%s:%i packet should not be from cpu 0x%2x\n", __func__, __LINE__, hdr); + kfree_skb(skb); return NULL; } @@ -75,8 +79,10 @@ static struct sk_buff *ar9331_tag_rcv(struct sk_buff *skb, port = FIELD_GET(AR9331_HDR_PORT_NUM_MASK, hdr); skb->dev = dsa_conduit_find_user(ndev, 0, port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } return skb; } diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c index cf9420439054..411e3b57d16a 100644 --- a/net/dsa/tag_brcm.c +++ b/net/dsa/tag_brcm.c @@ -102,9 +102,9 @@ static struct sk_buff *brcm_tag_xmit_ll(struct sk_buff *skb, * (including FCS and tag) because the length verification is done after * the Broadcom tag is stripped off the ingress packet. * - * Let dsa_user_xmit() free the SKB + * Free the SKB on error. */ - if (__skb_put_padto(skb, ETH_ZLEN + BRCM_TAG_LEN, false)) + if (skb_put_padto(skb, ETH_ZLEN + BRCM_TAG_LEN)) return NULL; skb_push(skb, BRCM_TAG_LEN); @@ -151,27 +151,35 @@ static struct sk_buff *brcm_tag_rcv_ll(struct sk_buff *skb, int source_port; u8 *brcm_tag; - if (unlikely(!pskb_may_pull(skb, BRCM_TAG_LEN))) + if (unlikely(!pskb_may_pull(skb, BRCM_TAG_LEN))) { + kfree_skb(skb); return NULL; + } brcm_tag = skb->data - offset; /* The opcode should never be different than 0b000 */ - if (unlikely((brcm_tag[0] >> BRCM_OPCODE_SHIFT) & BRCM_OPCODE_MASK)) + if (unlikely((brcm_tag[0] >> BRCM_OPCODE_SHIFT) & BRCM_OPCODE_MASK)) { + kfree_skb(skb); return NULL; + } /* We should never see a reserved reason code without knowing how to * handle it */ - if (unlikely(brcm_tag[2] & BRCM_EG_RC_RSVD)) + if (unlikely(brcm_tag[2] & BRCM_EG_RC_RSVD)) { + kfree_skb(skb); return NULL; + } /* Locate which port this is coming from */ source_port = brcm_tag[3] & BRCM_EG_PID_MASK; skb->dev = dsa_conduit_find_user(dev, 0, source_port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } /* Remove Broadcom tag and update checksum */ skb_pull_rcsum(skb, BRCM_TAG_LEN); @@ -228,8 +236,10 @@ static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb, __be16 *proto; u8 *brcm_tag; - if (unlikely(!pskb_may_pull(skb, BRCM_LEG_TAG_LEN + VLAN_HLEN))) + if (unlikely(!pskb_may_pull(skb, BRCM_LEG_TAG_LEN + VLAN_HLEN))) { + kfree_skb(skb); return NULL; + } brcm_tag = dsa_etype_header_pos_rx(skb); proto = (__be16 *)(brcm_tag + BRCM_LEG_TAG_LEN); @@ -237,8 +247,10 @@ static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb, source_port = brcm_tag[5] & BRCM_LEG_PORT_ID; skb->dev = dsa_conduit_find_user(dev, 0, source_port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } /* The internal switch in BCM63XX SoCs always tags on egress on the CPU * port. We use VID 0 internally for untagged traffic, so strip the tag @@ -273,10 +285,8 @@ static struct sk_buff *brcm_leg_tag_xmit(struct sk_buff *skb, * need to make sure that packets are at least 70 bytes * (including FCS and tag) because the length verification is done after * the Broadcom tag is stripped off the ingress packet. - * - * Let dsa_user_xmit() free the SKB */ - if (__skb_put_padto(skb, ETH_ZLEN + BRCM_LEG_TAG_LEN, false)) + if (skb_put_padto(skb, ETH_ZLEN + BRCM_LEG_TAG_LEN)) return NULL; skb_push(skb, BRCM_LEG_TAG_LEN); @@ -325,10 +335,8 @@ static struct sk_buff *brcm_leg_fcs_tag_xmit(struct sk_buff *skb, * need to make sure that packets are at least 70 bytes (including FCS * and tag) because the length verification is done after the Broadcom * tag is stripped off the ingress packet. - * - * Let dsa_user_xmit() free the SKB. */ - if (__skb_put_padto(skb, ETH_ZLEN + BRCM_LEG_TAG_LEN, false)) + if (skb_put_padto(skb, ETH_ZLEN + BRCM_LEG_TAG_LEN)) return NULL; fcs_len = skb->len; @@ -351,8 +359,9 @@ static struct sk_buff *brcm_leg_fcs_tag_xmit(struct sk_buff *skb, brcm_tag[5] = dp->index & BRCM_LEG_PORT_ID; /* Original FCS value */ - if (__skb_pad(skb, ETH_FCS_LEN, false)) + if (skb_pad(skb, ETH_FCS_LEN)) return NULL; + skb_put_data(skb, &fcs_val, ETH_FCS_LEN); return skb; diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c index 2a2c4fb61a65..d5ffee35fbb5 100644 --- a/net/dsa/tag_dsa.c +++ b/net/dsa/tag_dsa.c @@ -224,6 +224,7 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, /* Remote management is not implemented yet, * drop. */ + kfree_skb(skb); return NULL; case DSA_CODE_ARP_MIRROR: case DSA_CODE_POLICY_MIRROR: @@ -244,12 +245,14 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, /* Reserved code, this could be anything. Drop * seems like the safest option. */ + kfree_skb(skb); return NULL; } break; default: + kfree_skb(skb); return NULL; } @@ -271,8 +274,10 @@ static struct sk_buff *dsa_rcv_ll(struct sk_buff *skb, struct net_device *dev, source_port); } - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } /* When using LAG offload, skb->dev is not a DSA user interface, * so we cannot call dsa_default_offload_fwd_mark and we need to @@ -335,8 +340,10 @@ static struct sk_buff *dsa_xmit(struct sk_buff *skb, struct net_device *dev) static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev) { - if (unlikely(!pskb_may_pull(skb, DSA_HLEN))) + if (unlikely(!pskb_may_pull(skb, DSA_HLEN))) { + kfree_skb(skb); return NULL; + } return dsa_rcv_ll(skb, dev, 0); } @@ -375,8 +382,10 @@ static struct sk_buff *edsa_xmit(struct sk_buff *skb, struct net_device *dev) static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev) { - if (unlikely(!pskb_may_pull(skb, EDSA_HLEN))) + if (unlikely(!pskb_may_pull(skb, EDSA_HLEN))) { + kfree_skb(skb); return NULL; + } skb_pull_rcsum(skb, EDSA_HLEN - DSA_HLEN); diff --git a/net/dsa/tag_gswip.c b/net/dsa/tag_gswip.c index 5fa436121087..5c407d448c9f 100644 --- a/net/dsa/tag_gswip.c +++ b/net/dsa/tag_gswip.c @@ -80,16 +80,20 @@ static struct sk_buff *gswip_tag_rcv(struct sk_buff *skb, int port; u8 *gswip_tag; - if (unlikely(!pskb_may_pull(skb, GSWIP_RX_HEADER_LEN))) + if (unlikely(!pskb_may_pull(skb, GSWIP_RX_HEADER_LEN))) { + kfree_skb(skb); return NULL; + } gswip_tag = skb->data - ETH_HLEN; /* Get source port information */ port = (gswip_tag[7] & GSWIP_RX_SPPID_MASK) >> GSWIP_RX_SPPID_SHIFT; skb->dev = dsa_conduit_find_user(dev, 0, port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } /* remove GSWIP tag */ skb_pull_rcsum(skb, GSWIP_RX_HEADER_LEN); diff --git a/net/dsa/tag_hellcreek.c b/net/dsa/tag_hellcreek.c index 544ab15685a2..dd9f328f3182 100644 --- a/net/dsa/tag_hellcreek.c +++ b/net/dsa/tag_hellcreek.c @@ -27,8 +27,10 @@ static struct sk_buff *hellcreek_xmit(struct sk_buff *skb, * checksums after the switch strips the tag. */ if (skb->ip_summed == CHECKSUM_PARTIAL && - skb_checksum_help(skb)) + skb_checksum_help(skb)) { + kfree_skb(skb); return NULL; + } /* Tag encoding */ tag = skb_put(skb, HELLCREEK_TAG_LEN); @@ -47,11 +49,14 @@ static struct sk_buff *hellcreek_rcv(struct sk_buff *skb, skb->dev = dsa_conduit_find_user(dev, 0, port); if (!skb->dev) { netdev_warn_once(dev, "Failed to get source port: %d\n", port); + kfree_skb(skb); return NULL; } - if (pskb_trim_rcsum(skb, skb->len - HELLCREEK_TAG_LEN)) + if (pskb_trim_rcsum(skb, skb->len - HELLCREEK_TAG_LEN)) { + kfree_skb(skb); return NULL; + } dsa_default_offload_fwd_mark(skb); diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c index d2475c3bbb7d..67fa89f102e0 100644 --- a/net/dsa/tag_ksz.c +++ b/net/dsa/tag_ksz.c @@ -88,11 +88,15 @@ static struct sk_buff *ksz_common_rcv(struct sk_buff *skb, unsigned int port, unsigned int len) { skb->dev = dsa_conduit_find_user(dev, 0, port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } - if (pskb_trim_rcsum(skb, skb->len - len)) + if (pskb_trim_rcsum(skb, skb->len - len)) { + kfree_skb(skb); return NULL; + } dsa_default_offload_fwd_mark(skb); @@ -123,8 +127,10 @@ static struct sk_buff *ksz8795_xmit(struct sk_buff *skb, struct net_device *dev) struct ethhdr *hdr; u8 *tag; - if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) + if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) { + kfree_skb(skb); return NULL; + } /* Tag encoding */ tag = skb_put(skb, KSZ_INGRESS_TAG_LEN); @@ -141,8 +147,10 @@ static struct sk_buff *ksz8795_rcv(struct sk_buff *skb, struct net_device *dev) { u8 *tag; - if (skb_linearize(skb)) + if (skb_linearize(skb)) { + kfree_skb(skb); return NULL; + } tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN; @@ -255,22 +263,24 @@ static struct sk_buff *ksz_defer_xmit(struct dsa_port *dp, struct sk_buff *skb) xmit_work_fn = tagger_data->xmit_work_fn; xmit_worker = priv->xmit_worker; - if (!xmit_work_fn || !xmit_worker) + if (!xmit_work_fn || !xmit_worker) { + kfree_skb(skb); return NULL; + } xmit_work = kzalloc_obj(*xmit_work, GFP_ATOMIC); - if (!xmit_work) + if (!xmit_work) { + kfree_skb(skb); return NULL; + } kthread_init_work(&xmit_work->work, xmit_work_fn); - /* Increase refcount so the kfree_skb in dsa_user_xmit - * won't really free the packet. - */ xmit_work->dp = dp; xmit_work->skb = skb_get(skb); kthread_queue_work(xmit_worker, &xmit_work->work); + kfree_skb(skb); return NULL; } @@ -284,8 +294,10 @@ static struct sk_buff *ksz9477_xmit(struct sk_buff *skb, __be16 *tag; u16 val; - if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) + if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) { + kfree_skb(skb); return NULL; + } /* Tag encoding */ ksz_xmit_timestamp(dp, skb); @@ -310,8 +322,10 @@ static struct sk_buff *ksz9477_rcv(struct sk_buff *skb, struct net_device *dev) unsigned int port; u8 *tag; - if (skb_linearize(skb)) + if (skb_linearize(skb)) { + kfree_skb(skb); return NULL; + } /* Tag decoding */ tag = skb_tail_pointer(skb) - KSZ_EGRESS_TAG_LEN; @@ -352,8 +366,10 @@ static struct sk_buff *ksz9893_xmit(struct sk_buff *skb, struct ethhdr *hdr; u8 *tag; - if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) + if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) { + kfree_skb(skb); return NULL; + } /* Tag encoding */ ksz_xmit_timestamp(dp, skb); @@ -418,8 +434,10 @@ static struct sk_buff *lan937x_xmit(struct sk_buff *skb, __be16 *tag; u16 val; - if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) + if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) { + kfree_skb(skb); return NULL; + } ksz_xmit_timestamp(dp, skb); diff --git a/net/dsa/tag_lan9303.c b/net/dsa/tag_lan9303.c index 258e5d7dc5ef..d1194696499a 100644 --- a/net/dsa/tag_lan9303.c +++ b/net/dsa/tag_lan9303.c @@ -85,6 +85,7 @@ static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev) if (unlikely(!pskb_may_pull(skb, LAN9303_TAG_LEN))) { dev_warn_ratelimited(&dev->dev, "Dropping packet, cannot pull\n"); + kfree_skb(skb); return NULL; } @@ -102,6 +103,7 @@ static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev) skb->dev = dsa_conduit_find_user(dev, 0, source_port); if (!skb->dev) { dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid source port\n"); + kfree_skb(skb); return NULL; } diff --git a/net/dsa/tag_mtk.c b/net/dsa/tag_mtk.c index dea3eecaf093..c7dc7731675e 100644 --- a/net/dsa/tag_mtk.c +++ b/net/dsa/tag_mtk.c @@ -72,8 +72,10 @@ static struct sk_buff *mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev) int port; __be16 *phdr; - if (unlikely(!pskb_may_pull(skb, MTK_HDR_LEN))) + if (unlikely(!pskb_may_pull(skb, MTK_HDR_LEN))) { + kfree_skb(skb); return NULL; + } phdr = dsa_etype_header_pos_rx(skb); hdr = ntohs(*phdr); @@ -87,8 +89,10 @@ static struct sk_buff *mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev) port = (hdr & MTK_HDR_RECV_SOURCE_PORT_MASK); skb->dev = dsa_conduit_find_user(dev, 0, port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } dsa_default_offload_fwd_mark(skb); diff --git a/net/dsa/tag_mxl-gsw1xx.c b/net/dsa/tag_mxl-gsw1xx.c index 60f7c445e656..4b1b6ef94196 100644 --- a/net/dsa/tag_mxl-gsw1xx.c +++ b/net/dsa/tag_mxl-gsw1xx.c @@ -73,6 +73,7 @@ static struct sk_buff *gsw1xx_tag_rcv(struct sk_buff *skb, if (unlikely(!pskb_may_pull(skb, GSW1XX_HEADER_LEN))) { dev_warn_ratelimited(&dev->dev, "Dropping packet, cannot pull SKB\n"); + kfree_skb(skb); return NULL; } @@ -81,6 +82,7 @@ static struct sk_buff *gsw1xx_tag_rcv(struct sk_buff *skb, if (unlikely(ntohs(gsw1xx_tag[0]) != ETH_P_MXLGSW)) { dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid special tag\n"); dev_warn_ratelimited(&dev->dev, "Tag: %8ph\n", gsw1xx_tag); + kfree_skb(skb); return NULL; } @@ -90,6 +92,7 @@ static struct sk_buff *gsw1xx_tag_rcv(struct sk_buff *skb, if (!skb->dev) { dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid source port\n"); dev_warn_ratelimited(&dev->dev, "Tag: %8ph\n", gsw1xx_tag); + kfree_skb(skb); return NULL; } diff --git a/net/dsa/tag_mxl862xx.c b/net/dsa/tag_mxl862xx.c index 8daefeb8d49d..87b80ddf0946 100644 --- a/net/dsa/tag_mxl862xx.c +++ b/net/dsa/tag_mxl862xx.c @@ -64,6 +64,7 @@ static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb, if (unlikely(!pskb_may_pull(skb, MXL862_HEADER_LEN))) { dev_warn_ratelimited(&dev->dev, "Cannot pull SKB, packet dropped\n"); + kfree_skb(skb); return NULL; } @@ -73,6 +74,7 @@ static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb, dev_warn_ratelimited(&dev->dev, "Invalid special tag marker, packet dropped, tag: %8ph\n", mxl862_tag); + kfree_skb(skb); return NULL; } @@ -83,6 +85,7 @@ static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb, dev_warn_ratelimited(&dev->dev, "Invalid source port, packet dropped, tag: %8ph\n", mxl862_tag); + kfree_skb(skb); return NULL; } diff --git a/net/dsa/tag_netc.c b/net/dsa/tag_netc.c index ccedfe3a80b6..df72a61796ad 100644 --- a/net/dsa/tag_netc.c +++ b/net/dsa/tag_netc.c @@ -131,14 +131,13 @@ static struct sk_buff *netc_rcv(struct sk_buff *skb, int type, subtype; if (unlikely(!pskb_may_pull(skb, NETC_TAG_MAX_LEN))) - return NULL; + goto err_free_skb; tag_cmn = dsa_etype_header_pos_rx(skb); if (ntohs(tag_cmn->tpid) != ETH_P_NXP_NETC) { dev_warn_ratelimited(&ndev->dev, "Unknown TPID 0x%04x\n", ntohs(tag_cmn->tpid)); - - return NULL; + goto err_free_skb; } if (tag_cmn->qos & NETC_TAG_QV) @@ -149,14 +148,13 @@ static struct sk_buff *netc_rcv(struct sk_buff *skb, if (!sw_id) { dev_warn_ratelimited(&ndev->dev, "VEPA switch ID is not supported yet\n"); - - return NULL; + goto err_free_skb; } port = FIELD_GET(NETC_TAG_PORT, tag_cmn->switch_port); skb->dev = dsa_conduit_find_user(ndev, sw_id, port); if (!skb->dev) - return NULL; + goto err_free_skb; type = FIELD_GET(NETC_TAG_TYPE, tag_cmn->type); subtype = FIELD_GET(NETC_TAG_SUBTYPE, tag_cmn->type); @@ -165,11 +163,11 @@ static struct sk_buff *netc_rcv(struct sk_buff *skb, } else if (type == NETC_TAG_TO_HOST) { /* Currently only subtype0 supported */ if (subtype != NETC_TAG_TH_SUBTYPE0) - return NULL; + goto err_free_skb; } else { dev_warn_ratelimited(&ndev->dev, "Unexpected tag type %d\n", type); - return NULL; + goto err_free_skb; } /* Remove Switch tag from the frame */ @@ -178,6 +176,10 @@ static struct sk_buff *netc_rcv(struct sk_buff *skb, dsa_strip_etype_header(skb, tag_len); return skb; + +err_free_skb: + kfree_skb(skb); + return NULL; } static void netc_flow_dissect(const struct sk_buff *skb, __be16 *proto, diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c index 3405def79c2d..d208c7322cd6 100644 --- a/net/dsa/tag_ocelot.c +++ b/net/dsa/tag_ocelot.c @@ -107,14 +107,16 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb, ocelot_xfh_get_rew_val(extraction, &rew_val); skb->dev = dsa_conduit_find_user(netdev, 0, src_port); - if (!skb->dev) + if (!skb->dev) { /* The switch will reflect back some frames sent through * sockets opened on the bare DSA conduit. These will come back * with src_port equal to the index of the CPU port, for which * there is no user registered. So don't print any error * message here (ignore and drop those frames). */ + kfree_skb(skb); return NULL; + } dsa_default_offload_fwd_mark(skb); skb->priority = qos_class; diff --git a/net/dsa/tag_ocelot_8021q.c b/net/dsa/tag_ocelot_8021q.c index e89d9254e90a..f50f1cd83f16 100644 --- a/net/dsa/tag_ocelot_8021q.c +++ b/net/dsa/tag_ocelot_8021q.c @@ -33,30 +33,34 @@ static struct sk_buff *ocelot_defer_xmit(struct dsa_port *dp, xmit_work_fn = data->xmit_work_fn; xmit_worker = priv->xmit_worker; - if (!xmit_work_fn || !xmit_worker) + if (!xmit_work_fn || !xmit_worker) { + kfree_skb(skb); return NULL; + } /* PTP over IP packets need UDP checksumming. We may have inherited * NETIF_F_HW_CSUM from the DSA conduit, but these packets are not sent * through the DSA conduit, so calculate the checksum here. */ - if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) + if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) { + kfree_skb(skb); return NULL; + } xmit_work = kzalloc_obj(*xmit_work, GFP_ATOMIC); - if (!xmit_work) + if (!xmit_work) { + kfree_skb(skb); return NULL; + } /* Calls felix_port_deferred_xmit in felix.c */ kthread_init_work(&xmit_work->work, xmit_work_fn); - /* Increase refcount so the kfree_skb in dsa_user_xmit - * won't really free the packet. - */ xmit_work->dp = dp; xmit_work->skb = skb_get(skb); kthread_queue_work(xmit_worker, &xmit_work->work); + kfree_skb(skb); return NULL; } @@ -84,8 +88,10 @@ static struct sk_buff *ocelot_rcv(struct sk_buff *skb, dsa_8021q_rcv(skb, &src_port, &switch_id, NULL, NULL); skb->dev = dsa_conduit_find_user(netdev, switch_id, src_port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } dsa_default_offload_fwd_mark(skb); diff --git a/net/dsa/tag_qca.c b/net/dsa/tag_qca.c index 9e3b429e8b36..510792fbfa92 100644 --- a/net/dsa/tag_qca.c +++ b/net/dsa/tag_qca.c @@ -46,16 +46,20 @@ static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev) tagger_data = ds->tagger_data; - if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN))) + if (unlikely(!pskb_may_pull(skb, QCA_HDR_LEN))) { + kfree_skb(skb); return NULL; + } phdr = dsa_etype_header_pos_rx(skb); hdr = ntohs(*phdr); /* Make sure the version is correct */ ver = FIELD_GET(QCA_HDR_RECV_VERSION, hdr); - if (unlikely(ver != QCA_HDR_VERSION)) + if (unlikely(ver != QCA_HDR_VERSION)) { + kfree_skb(skb); return NULL; + } /* Get pk type */ pk_type = FIELD_GET(QCA_HDR_RECV_TYPE, hdr); @@ -64,6 +68,7 @@ static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev) if (pk_type == QCA_HDR_RECV_TYPE_RW_REG_ACK) { if (likely(tagger_data->rw_reg_ack_handler)) tagger_data->rw_reg_ack_handler(ds, skb); + kfree_skb(skb); return NULL; } @@ -71,6 +76,7 @@ static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev) if (pk_type == QCA_HDR_RECV_TYPE_MIB) { if (likely(tagger_data->mib_autocast_handler)) tagger_data->mib_autocast_handler(ds, skb); + kfree_skb(skb); return NULL; } @@ -78,8 +84,10 @@ static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev) port = FIELD_GET(QCA_HDR_RECV_SOURCE_PORT, hdr); skb->dev = dsa_conduit_find_user(dev, 0, port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } /* Remove QCA tag and recalculate checksum */ skb_pull_rcsum(skb, QCA_HDR_LEN); diff --git a/net/dsa/tag_rtl4_a.c b/net/dsa/tag_rtl4_a.c index 3cc63eacfa03..590ea3b921c9 100644 --- a/net/dsa/tag_rtl4_a.c +++ b/net/dsa/tag_rtl4_a.c @@ -41,7 +41,7 @@ static struct sk_buff *rtl4a_tag_xmit(struct sk_buff *skb, u16 out; /* Pad out to at least 60 bytes */ - if (unlikely(__skb_put_padto(skb, ETH_ZLEN, false))) + if (unlikely(eth_skb_pad(skb))) return NULL; netdev_dbg(dev, "add realtek tag to package to port %d\n", @@ -75,8 +75,10 @@ static struct sk_buff *rtl4a_tag_rcv(struct sk_buff *skb, u8 prot; u8 port; - if (unlikely(!pskb_may_pull(skb, RTL4_A_HDR_LEN))) + if (unlikely(!pskb_may_pull(skb, RTL4_A_HDR_LEN))) { + kfree_skb(skb); return NULL; + } tag = dsa_etype_header_pos_rx(skb); p = (__be16 *)tag; @@ -92,6 +94,7 @@ static struct sk_buff *rtl4a_tag_rcv(struct sk_buff *skb, prot = (protport >> RTL4_A_PROTOCOL_SHIFT) & 0x0f; if (prot != RTL4_A_PROTOCOL_RTL8366RB) { netdev_err(dev, "unknown realtek protocol 0x%01x\n", prot); + kfree_skb(skb); return NULL; } port = protport & 0xff; @@ -99,6 +102,7 @@ static struct sk_buff *rtl4a_tag_rcv(struct sk_buff *skb, skb->dev = dsa_conduit_find_user(dev, 0, port); if (!skb->dev) { netdev_dbg(dev, "could not find user for port %d\n", port); + kfree_skb(skb); return NULL; } diff --git a/net/dsa/tag_rtl8_4.c b/net/dsa/tag_rtl8_4.c index 852c6b88079a..4da3beebef75 100644 --- a/net/dsa/tag_rtl8_4.c +++ b/net/dsa/tag_rtl8_4.c @@ -143,8 +143,10 @@ static struct sk_buff *rtl8_4t_tag_xmit(struct sk_buff *skb, /* Calculate the checksum here if not done yet as trailing tags will * break either software or hardware based checksum */ - if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) + if (skb->ip_summed == CHECKSUM_PARTIAL && skb_checksum_help(skb)) { + kfree_skb(skb); return NULL; + } rtl8_4_write_tag(skb, dev, skb_put(skb, RTL8_4_TAG_LEN)); @@ -201,11 +203,15 @@ static int rtl8_4_read_tag(struct sk_buff *skb, struct net_device *dev, static struct sk_buff *rtl8_4_tag_rcv(struct sk_buff *skb, struct net_device *dev) { - if (unlikely(!pskb_may_pull(skb, RTL8_4_TAG_LEN))) + if (unlikely(!pskb_may_pull(skb, RTL8_4_TAG_LEN))) { + kfree_skb(skb); return NULL; + } - if (unlikely(rtl8_4_read_tag(skb, dev, dsa_etype_header_pos_rx(skb)))) + if (unlikely(rtl8_4_read_tag(skb, dev, dsa_etype_header_pos_rx(skb)))) { + kfree_skb(skb); return NULL; + } /* Remove tag and recalculate checksum */ skb_pull_rcsum(skb, RTL8_4_TAG_LEN); @@ -218,14 +224,20 @@ static struct sk_buff *rtl8_4_tag_rcv(struct sk_buff *skb, static struct sk_buff *rtl8_4t_tag_rcv(struct sk_buff *skb, struct net_device *dev) { - if (skb_linearize(skb)) + if (skb_linearize(skb)) { + kfree_skb(skb); return NULL; + } - if (unlikely(rtl8_4_read_tag(skb, dev, skb_tail_pointer(skb) - RTL8_4_TAG_LEN))) + if (unlikely(rtl8_4_read_tag(skb, dev, skb_tail_pointer(skb) - RTL8_4_TAG_LEN))) { + kfree_skb(skb); return NULL; + } - if (pskb_trim_rcsum(skb, skb->len - RTL8_4_TAG_LEN)) + if (pskb_trim_rcsum(skb, skb->len - RTL8_4_TAG_LEN)) { + kfree_skb(skb); return NULL; + } return skb; } diff --git a/net/dsa/tag_rzn1_a5psw.c b/net/dsa/tag_rzn1_a5psw.c index 10994b3470f6..734910156dc3 100644 --- a/net/dsa/tag_rzn1_a5psw.c +++ b/net/dsa/tag_rzn1_a5psw.c @@ -48,7 +48,7 @@ static struct sk_buff *a5psw_tag_xmit(struct sk_buff *skb, struct net_device *de * least 60 bytes otherwise they will be discarded when they enter the * switch port logic. */ - if (__skb_put_padto(skb, ETH_ZLEN, false)) + if (eth_skb_pad(skb)) return NULL; /* provide 'A5PSW_TAG_LEN' bytes additional space */ @@ -77,6 +77,7 @@ static struct sk_buff *a5psw_tag_rcv(struct sk_buff *skb, if (unlikely(!pskb_may_pull(skb, A5PSW_TAG_LEN))) { dev_warn_ratelimited(&dev->dev, "Dropping packet, cannot pull\n"); + kfree_skb(skb); return NULL; } @@ -84,14 +85,17 @@ static struct sk_buff *a5psw_tag_rcv(struct sk_buff *skb, if (tag->ctrl_tag != htons(ETH_P_DSA_A5PSW)) { dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid TAG marker\n"); + kfree_skb(skb); return NULL; } port = FIELD_GET(A5PSW_CTRL_DATA_PORT, ntohs(tag->ctrl_data)); skb->dev = dsa_conduit_find_user(dev, 0, port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } skb_pull_rcsum(skb, A5PSW_TAG_LEN); dsa_strip_etype_header(skb, A5PSW_TAG_LEN); diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c index de6d4ce8668b..bfe1f746f55b 100644 --- a/net/dsa/tag_sja1105.c +++ b/net/dsa/tag_sja1105.c @@ -149,19 +149,20 @@ static struct sk_buff *sja1105_defer_xmit(struct dsa_port *dp, xmit_work_fn = tagger_data->xmit_work_fn; xmit_worker = priv->xmit_worker; - if (!xmit_work_fn || !xmit_worker) + if (!xmit_work_fn || !xmit_worker) { + kfree_skb(skb); return NULL; + } xmit_work = kzalloc_obj(*xmit_work, GFP_ATOMIC); - if (!xmit_work) + if (!xmit_work) { + kfree_skb(skb); return NULL; + } kthread_init_work(&xmit_work->work, xmit_work_fn); - /* Increase refcount so the kfree_skb in dsa_user_xmit - * won't really free the packet. - */ xmit_work->dp = dp; - xmit_work->skb = skb_get(skb); + xmit_work->skb = skb; kthread_queue_work(xmit_worker, &xmit_work->work); @@ -401,10 +402,7 @@ static struct sk_buff kfree_skb(priv->stampable_skb); } - /* Hold a reference to avoid dsa_switch_rcv - * from freeing the skb. - */ - priv->stampable_skb = skb_get(skb); + priv->stampable_skb = skb; spin_unlock(&priv->meta_lock); /* Tell DSA we got nothing */ @@ -436,6 +434,7 @@ static struct sk_buff dev_err_ratelimited(ds->dev, "Unexpected meta frame\n"); spin_unlock(&priv->meta_lock); + kfree_skb(skb); return NULL; } @@ -443,6 +442,7 @@ static struct sk_buff dev_err_ratelimited(ds->dev, "Meta frame on wrong port\n"); spin_unlock(&priv->meta_lock); + kfree_skb(skb); return NULL; } @@ -501,18 +501,21 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, /* Normal data plane traffic and link-local frames are tagged with * a tag_8021q VLAN which we have to strip */ - if (sja1105_skb_has_tag_8021q(skb)) + if (sja1105_skb_has_tag_8021q(skb)) { dsa_8021q_rcv(skb, &source_port, &switch_id, &vbid, &vid); - else if (source_port == -1 && switch_id == -1) + } else if (source_port == -1 && switch_id == -1) { /* Packets with no source information have no chance of * getting accepted, drop them straight away. */ + kfree_skb(skb); return NULL; + } skb->dev = dsa_tag_8021q_find_user(netdev, source_port, switch_id, vid, vbid); if (!skb->dev) { netdev_warn(netdev, "Couldn't decode source port\n"); + kfree_skb(skb); return NULL; } @@ -539,12 +542,15 @@ static struct sk_buff *sja1110_rcv_meta(struct sk_buff *skb, u16 rx_header) if (!ds) { net_err_ratelimited("%s: cannot find switch id %d\n", conduit->name, switch_id); + kfree_skb(skb); return NULL; } tagger_data = sja1105_tagger_data(ds); - if (!tagger_data->meta_tstamp_handler) + if (!tagger_data->meta_tstamp_handler) { + kfree_skb(skb); return NULL; + } for (i = 0; i <= n_ts; i++) { u8 ts_id, source_port, dir; @@ -562,6 +568,7 @@ static struct sk_buff *sja1110_rcv_meta(struct sk_buff *skb, u16 rx_header) } /* Discard the meta frame, we've consumed the timestamps it contained */ + kfree_skb(skb); return NULL; } @@ -572,8 +579,10 @@ static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb, { u16 rx_header; - if (unlikely(!pskb_may_pull(skb, SJA1110_HEADER_LEN))) + if (unlikely(!pskb_may_pull(skb, SJA1110_HEADER_LEN))) { + kfree_skb(skb); return NULL; + } /* skb->data points to skb_mac_header(skb) + ETH_HLEN, which is exactly * what we need because the caller has checked the EtherType (which is @@ -609,8 +618,10 @@ static struct sk_buff *sja1110_rcv_inband_control_extension(struct sk_buff *skb, * padding and trailer we need to account for the fact that * skb->data points to skb_mac_header(skb) + ETH_HLEN. */ - if (pskb_trim_rcsum(skb, start_of_padding - ETH_HLEN)) + if (pskb_trim_rcsum(skb, start_of_padding - ETH_HLEN)) { + kfree_skb(skb); return NULL; + } /* Trap-to-host frame, no timestamp trailer */ } else { *source_port = SJA1110_RX_HEADER_SRC_PORT(rx_header); @@ -653,6 +664,7 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb, if (!skb->dev) { netdev_warn(netdev, "Couldn't decode source port\n"); + kfree_skb(skb); return NULL; } diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c index 4dce24cfe6a7..49c802c10ca6 100644 --- a/net/dsa/tag_trailer.c +++ b/net/dsa/tag_trailer.c @@ -30,22 +30,30 @@ static struct sk_buff *trailer_rcv(struct sk_buff *skb, struct net_device *dev) u8 *trailer; int source_port; - if (skb_linearize(skb)) + if (skb_linearize(skb)) { + kfree_skb(skb); return NULL; + } trailer = skb_tail_pointer(skb) - 4; if (trailer[0] != 0x80 || (trailer[1] & 0xf8) != 0x00 || - (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00) + (trailer[2] & 0xef) != 0x00 || trailer[3] != 0x00) { + kfree_skb(skb); return NULL; + } source_port = trailer[1] & 7; skb->dev = dsa_conduit_find_user(dev, 0, source_port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } - if (pskb_trim_rcsum(skb, skb->len - 4)) + if (pskb_trim_rcsum(skb, skb->len - 4)) { + kfree_skb(skb); return NULL; + } return skb; } diff --git a/net/dsa/tag_vsc73xx_8021q.c b/net/dsa/tag_vsc73xx_8021q.c index af121a9aff7f..f4736a1a7a0f 100644 --- a/net/dsa/tag_vsc73xx_8021q.c +++ b/net/dsa/tag_vsc73xx_8021q.c @@ -44,6 +44,7 @@ vsc73xx_rcv(struct sk_buff *skb, struct net_device *netdev) if (!skb->dev) { dev_warn_ratelimited(&netdev->dev, "Couldn't decode source port\n"); + kfree_skb(skb); return NULL; } diff --git a/net/dsa/tag_xrs700x.c b/net/dsa/tag_xrs700x.c index a05219f702c6..bb268020ee86 100644 --- a/net/dsa/tag_xrs700x.c +++ b/net/dsa/tag_xrs700x.c @@ -30,15 +30,21 @@ static struct sk_buff *xrs700x_rcv(struct sk_buff *skb, struct net_device *dev) source_port = ffs((int)trailer[0]) - 1; - if (source_port < 0) + if (source_port < 0) { + kfree_skb(skb); return NULL; + } skb->dev = dsa_conduit_find_user(dev, 0, source_port); - if (!skb->dev) + if (!skb->dev) { + kfree_skb(skb); return NULL; + } - if (pskb_trim_rcsum(skb, skb->len - 1)) + if (pskb_trim_rcsum(skb, skb->len - 1)) { + kfree_skb(skb); return NULL; + } /* Frame is forwarded by hardware, don't forward in software. */ dsa_default_offload_fwd_mark(skb); diff --git a/net/dsa/tag_yt921x.c b/net/dsa/tag_yt921x.c index f3ced99b1c85..294784ab6694 100644 --- a/net/dsa/tag_yt921x.c +++ b/net/dsa/tag_yt921x.c @@ -87,8 +87,10 @@ yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev) __be16 *tag; u16 rx; - if (unlikely(!pskb_may_pull(skb, YT921X_TAG_LEN))) + if (unlikely(!pskb_may_pull(skb, YT921X_TAG_LEN))) { + kfree_skb(skb); return NULL; + } tag = dsa_etype_header_pos_rx(skb); @@ -96,6 +98,7 @@ yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev) dev_warn_ratelimited(&netdev->dev, "Unexpected EtherType 0x%04x\n", ntohs(tag[0])); + kfree_skb(skb); return NULL; } @@ -104,6 +107,7 @@ yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev) if (unlikely((rx & YT921X_TAG_PORT_EN) == 0)) { dev_warn_ratelimited(&netdev->dev, "Unexpected rx tag 0x%04x\n", rx); + kfree_skb(skb); return NULL; } @@ -112,6 +116,7 @@ yt921x_tag_rcv(struct sk_buff *skb, struct net_device *netdev) if (unlikely(!skb->dev)) { dev_warn_ratelimited(&netdev->dev, "Couldn't decode source port %u\n", port); + kfree_skb(skb); return NULL; } diff --git a/net/dsa/user.c b/net/dsa/user.c index 8704c1a3a5b7..072fa76972cc 100644 --- a/net/dsa/user.c +++ b/net/dsa/user.c @@ -935,13 +935,12 @@ static netdev_tx_t dsa_user_xmit(struct sk_buff *skb, struct net_device *dev) eth_skb_pad(skb); /* Transmit function may have to reallocate the original SKB, - * in which case it must have freed it. Only free it here on error. + * in which case it must have freed it. Taggers will drop the + * passed skb on error. */ nskb = p->xmit(skb, dev); - if (!nskb) { - kfree_skb(skb); + if (!nskb) return NETDEV_TX_OK; - } return dsa_enqueue_skb(nskb, dev); } diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index a56bb79e15e0..e4ec60a33496 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -371,8 +371,9 @@ static void tcp_ao_key_free_rcu(struct rcu_head *head) kfree_sensitive(key); } -static void tcp_ao_info_free(struct tcp_ao_info *ao) +static void tcp_ao_info_free_rcu(struct rcu_head *head) { + struct tcp_ao_info *ao = container_of(head, struct tcp_ao_info, rcu); struct tcp_ao_key *key; struct hlist_node *n; @@ -411,7 +412,7 @@ void tcp_ao_destroy_sock(struct sock *sk, bool twsk) if (!twsk) tcp_ao_sk_omem_free(sk, ao); - tcp_ao_info_free(ao); + call_rcu(&ao->rcu, tcp_ao_info_free_rcu); } void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index ec09f97cc9e6..209ef7522508 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1467,9 +1467,9 @@ void tcp_clear_md5_list(struct sock *sk) md5sig = rcu_dereference_protected(tp->md5sig_info, 1); hlist_for_each_entry_safe(key, n, &md5sig->head, node) { - hlist_del(&key->node); + hlist_del_rcu(&key->node); atomic_sub(sizeof(*key), &sk->sk_omem_alloc); - kfree(key); + kfree_rcu(key, rcu); } } diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 00ec4b5900f2..d7c1444b5e30 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -4329,9 +4329,13 @@ int tcp_connect(struct sock *sk) if (needs_md5) { tcp_ao_destroy_sock(sk, false); } else if (needs_ao) { + struct tcp_md5sig_info *md5sig; + tcp_clear_md5_list(sk); - kfree(rcu_replace_pointer(tp->md5sig_info, NULL, - lockdep_sock_is_held(sk))); + md5sig = rcu_replace_pointer(tp->md5sig_info, NULL, + lockdep_sock_is_held(sk)); + kfree_rcu(md5sig, rcu); + static_branch_slow_dec_deferred(&tcp_md5_needed); } } #endif diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index fc95738ded76..a130cdfaebfb 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -636,12 +636,12 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) }; const struct nlmsghdr *nlh = cb->nlh; struct net *net = sock_net(skb->sk); - unsigned int e = 0, s_e; struct hlist_head *head; struct fib6_walker *w; struct fib6_table *tb; unsigned int h, s_h; int err = 0; + u32 s_id; rcu_read_lock(); if (cb->strict_check) { @@ -701,23 +701,22 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb) } s_h = cb->args[0]; - s_e = cb->args[1]; + s_id = cb->args[1]; - for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) { - e = 0; + for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_id = 0) { head = &net->ipv6.fib_table_hash[h]; hlist_for_each_entry_rcu(tb, head, tb6_hlist) { - if (e < s_e) - goto next; + if (s_id && tb->tb6_id != s_id) + continue; + + s_id = 0; + cb->args[1] = tb->tb6_id; err = fib6_dump_table(tb, skb, cb); if (err != 0) goto out; -next: - e++; } } out: - cb->args[1] = e; cb->args[0] = h; unlock: diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c index 1c3ad25700c4..62a7eb779202 100644 --- a/net/ipv6/seg6.c +++ b/net/ipv6/seg6.c @@ -29,6 +29,9 @@ bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len, bool reduced) int max_last_entry; int trailing; + if (len < sizeof(*srh)) + return false; + if (srh->type != IPV6_SRCRT_TYPE_4) return false; diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index a531b654b8d9..6cfad152d7d1 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -1480,7 +1480,11 @@ ip_set_dump_done(struct netlink_callback *cb) struct ip_set_net *inst = (struct ip_set_net *)cb->args[IPSET_CB_NET]; ip_set_id_t index = (ip_set_id_t)cb->args[IPSET_CB_INDEX]; - struct ip_set *set = ip_set_ref_netlink(inst, index); + struct ip_set *set; + + rcu_read_lock(); + set = ip_set_ref_netlink(inst, index); + rcu_read_unlock(); if (set->variant->uref) set->variant->uref(set, cb, false); @@ -1686,7 +1690,9 @@ next_set: release_refcount: /* If there was an error or set is done, release set */ if (ret || !cb->args[IPSET_CB_ARG0]) { + rcu_read_lock(); set = ip_set_ref_netlink(inst, index); + rcu_read_unlock(); if (set->variant->uref) set->variant->uref(set, cb, false); pr_debug("release set %s\n", set->name); diff --git a/net/netfilter/nf_conntrack_bpf.c b/net/netfilter/nf_conntrack_bpf.c index 40c261cd0af3..f98d1d4b42c3 100644 --- a/net/netfilter/nf_conntrack_bpf.c +++ b/net/netfilter/nf_conntrack_bpf.c @@ -65,6 +65,15 @@ enum { NF_BPF_CT_OPTS_SZ = 16, }; +static void *bpf_ct_opts_result(struct bpf_ct_opts *opts, u32 opts__sz, void *ret) +{ + if (!IS_ERR(ret)) + return ret; + if (opts__sz >= offsetofend(struct bpf_ct_opts, error)) + opts->error = PTR_ERR(ret); + return NULL; +} + static int bpf_nf_ct_tuple_parse(struct bpf_sock_tuple *bpf_tuple, u32 tuple_len, u8 protonum, u8 dir, struct nf_conntrack_tuple *tuple) @@ -297,12 +306,7 @@ bpf_xdp_ct_alloc(struct xdp_md *xdp_ctx, struct bpf_sock_tuple *bpf_tuple, nfct = __bpf_nf_ct_alloc_entry(dev_net(ctx->rxq->dev), bpf_tuple, tuple__sz, opts, opts__sz, 10); - if (IS_ERR(nfct)) { - opts->error = PTR_ERR(nfct); - return NULL; - } - - return (struct nf_conn___init *)nfct; + return (struct nf_conn___init *)bpf_ct_opts_result(opts, opts__sz, nfct); } /* bpf_xdp_ct_lookup - Lookup CT entry for the given tuple, and acquire a @@ -331,11 +335,7 @@ bpf_xdp_ct_lookup(struct xdp_md *xdp_ctx, struct bpf_sock_tuple *bpf_tuple, caller_net = dev_net(ctx->rxq->dev); nfct = __bpf_nf_ct_lookup(caller_net, bpf_tuple, tuple__sz, opts, opts__sz); - if (IS_ERR(nfct)) { - opts->error = PTR_ERR(nfct); - return NULL; - } - return nfct; + return bpf_ct_opts_result(opts, opts__sz, nfct); } /* bpf_skb_ct_alloc - Allocate a new CT entry @@ -363,12 +363,7 @@ bpf_skb_ct_alloc(struct __sk_buff *skb_ctx, struct bpf_sock_tuple *bpf_tuple, net = skb->dev ? dev_net(skb->dev) : sock_net(skb->sk); nfct = __bpf_nf_ct_alloc_entry(net, bpf_tuple, tuple__sz, opts, opts__sz, 10); - if (IS_ERR(nfct)) { - opts->error = PTR_ERR(nfct); - return NULL; - } - - return (struct nf_conn___init *)nfct; + return (struct nf_conn___init *)bpf_ct_opts_result(opts, opts__sz, nfct); } /* bpf_skb_ct_lookup - Lookup CT entry for the given tuple, and acquire a @@ -397,11 +392,7 @@ bpf_skb_ct_lookup(struct __sk_buff *skb_ctx, struct bpf_sock_tuple *bpf_tuple, caller_net = skb->dev ? dev_net(skb->dev) : sock_net(skb->sk); nfct = __bpf_nf_ct_lookup(caller_net, bpf_tuple, tuple__sz, opts, opts__sz); - if (IS_ERR(nfct)) { - opts->error = PTR_ERR(nfct); - return NULL; - } - return nfct; + return bpf_ct_opts_result(opts, opts__sz, nfct); } /* bpf_ct_insert_entry - Add the provided entry into a CT map diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 38630c5e006f..7ae68d60586a 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -306,7 +306,7 @@ struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me) { struct nf_conntrack_expect *new; - new = kmem_cache_alloc(nf_ct_expect_cachep, GFP_ATOMIC); + new = kmem_cache_zalloc(nf_ct_expect_cachep, GFP_ATOMIC); if (!new) return NULL; @@ -391,6 +391,7 @@ void nf_ct_expect_init(struct nf_conntrack_expect *exp, unsigned int class, #if IS_ENABLED(CONFIG_NF_NAT) memset(&exp->saved_addr, 0, sizeof(exp->saved_addr)); memset(&exp->saved_proto, 0, sizeof(exp->saved_proto)); + exp->dir = 0; #endif } EXPORT_SYMBOL_GPL(nf_ct_expect_init); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 4217715d42dc..31cbb1b55b9e 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -3549,8 +3549,6 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, if (cda[CTA_EXPECT_FLAGS]) { exp->flags = ntohl(nla_get_be32(cda[CTA_EXPECT_FLAGS])); exp->flags &= ~NF_CT_EXPECT_USERSPACE; - } else { - exp->flags = 0; } if (cda[CTA_EXPECT_FN]) { const char *name = nla_data(cda[CTA_EXPECT_FN]); @@ -3562,8 +3560,7 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, goto err_out; } exp->expectfn = expfn->expectfn; - } else - exp->expectfn = NULL; + } exp->class = class; exp->master = ct; @@ -3583,12 +3580,6 @@ ctnetlink_alloc_expect(const struct nlattr * const cda[], struct nf_conn *ct, exp, nf_ct_l3num(ct)); if (err < 0) goto err_out; -#if IS_ENABLED(CONFIG_NF_NAT) - } else { - memset(&exp->saved_addr, 0, sizeof(exp->saved_addr)); - memset(&exp->saved_proto, 0, sizeof(exp->saved_proto)); - exp->dir = 0; -#endif } return exp; err_out: diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index 5ec3a4a4bbd7..f3f90a866338 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -956,7 +956,6 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, return NF_ACCEPT; saddr = &ct->tuplehash[!dir].tuple.src.u3; } else if (sip_external_media) { - struct net_device *dev = skb_dst(skb)->dev; struct dst_entry *dst = NULL; struct flowi fl; @@ -978,7 +977,11 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, * through the same interface as the signalling peer. */ if (dst) { - bool external_media = (dst->dev == dev); + const struct dst_entry *this_dst = skb_dst(skb); + bool external_media = false; + + if (this_dst && dst->dev == this_dst->dev) + external_media = true; dst_release(dst); if (external_media) diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c index f1460b683d7a..2cbcca9110db 100644 --- a/net/netfilter/nfnetlink_cthelper.c +++ b/net/netfilter/nfnetlink_cthelper.c @@ -163,6 +163,8 @@ nfnl_cthelper_expect_policy(struct nf_conntrack_expect_policy *expect_policy, tb[NFCTH_POLICY_NAME], NF_CT_HELPER_NAME_LEN); expect_policy->max_expected = ntohl(nla_get_be32(tb[NFCTH_POLICY_EXPECT_MAX])); + if (!expect_policy->max_expected) + expect_policy->max_expected = NF_CT_EXPECT_MAX_CNT; if (expect_policy->max_expected > NF_CT_EXPECT_MAX_CNT) return -EINVAL; diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 80ca077b81bd..35d4c6c628ff 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -1184,6 +1184,173 @@ nfqnl_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum) return err; } +static bool nfqnl_validate_ipopts(const struct iphdr *iph_new, + const struct nf_queue_entry *e) +{ + const struct iphdr *iph_orig = ip_hdr(e->skb); + unsigned int ihl = iph_new->ihl * 4; + + if (iph_new->ihl != iph_orig->ihl) + return false; + if (ihl == sizeof(*iph_orig)) + return true; + + return memcmp(iph_new + 1, ip_hdr(e->skb) + 1, ihl - sizeof(*iph_orig)) == 0; +} + +static bool nfqnl_validate_ip4(const struct iphdr *iph, unsigned int data_len, + const struct nf_queue_entry *e) +{ + unsigned int ihl; + + if (data_len < sizeof(*iph)) + return false; + + ihl = iph->ihl * 4u; + if (ihl < sizeof(*iph) || data_len < ihl) + return false; + + if (iph->version != 4 || + ((iph->frag_off ^ ip_hdr(e->skb)->frag_off) & ~htons(IP_DF)) != 0) + return false; + + /* BIG TCP won't work; netlink attr len is u16 */ + if (ntohs(iph->tot_len) != data_len) + return false; + + /* support for ipopts mangling would require + * recompile + skb transport header update. + */ + return nfqnl_validate_ipopts(iph, e); +} + +static bool nfqnl_validate_one_exthdr(const u8 *data, + unsigned int data_len, + const struct nf_queue_entry *e, + int start, int hdrlen) +{ + u16 octets; + + if (data_len < hdrlen || hdrlen < 2) + return false; + + while (hdrlen > 0) { + if (data_len < sizeof(octets)) + return false; + data_len -= sizeof(octets); + + if (skb_copy_bits(e->skb, start, &octets, sizeof(octets))) + return false; + + if (hdrlen < sizeof(octets)) + return false; + + hdrlen -= sizeof(octets); + if (memcmp(data, &octets, sizeof(octets))) + return false; + + start += sizeof(octets); + data += sizeof(octets); + } + + return true; +} + +static bool nfqnl_validate_exthdr(const struct ipv6hdr *ip6_new, + unsigned int data_len, + const struct nf_queue_entry *e) +{ + const struct ipv6hdr *ip6_orig = ipv6_hdr(e->skb); + int exthdr_cnt = 0, start = sizeof(*ip6_orig); + const u8 *data = (const u8 *)ip6_new; + u8 orig_nexthdr = ip6_orig->nexthdr; + u8 new_nexthdr = ip6_new->nexthdr; + + if (new_nexthdr != orig_nexthdr) + return false; + + data += sizeof(*ip6_new); + data_len -= sizeof(*ip6_new); + + while (ipv6_ext_hdr(orig_nexthdr)) { + const struct ipv6_opt_hdr *hp; + struct ipv6_opt_hdr _hdr; + int hdrlen; + + if (orig_nexthdr == NEXTHDR_NONE) + return true; + + if (unlikely(exthdr_cnt++ >= IP6_MAX_EXT_HDRS_CNT)) + return false; + + hp = skb_header_pointer(e->skb, start, sizeof(_hdr), &_hdr); + if (!hp) + return false; + + switch (orig_nexthdr) { + case NEXTHDR_FRAGMENT: + hdrlen = sizeof(struct frag_hdr); + break; + case NEXTHDR_AUTH: + hdrlen = ipv6_authlen(hp); + break; + default: + hdrlen = ipv6_optlen(hp); + break; + } + + if (!nfqnl_validate_one_exthdr(data, data_len, e, + start, hdrlen)) + return false; + + orig_nexthdr = hp->nexthdr; + hp = (const void *)data; + new_nexthdr = hp->nexthdr; + + if (new_nexthdr != orig_nexthdr) + return false; + + data_len -= hdrlen; + start += hdrlen; + data += hdrlen; + } + + return true; +} + +static bool nfqnl_validate_ip6(const struct ipv6hdr *ip6, unsigned int data_len, + const struct nf_queue_entry *e) +{ + if (data_len < sizeof(*ip6)) + return false; + + /* BIG TCP/jumbograms won't work; netlink attr len is u16 */ + if (ntohs(ip6->payload_len) != data_len - sizeof(*ip6)) + return false; + + if (ip6->version != 6) + return false; + + return nfqnl_validate_exthdr(ip6, data_len, e); +} + +static bool nfqnl_validate_write(const void *data, unsigned int data_len, + const struct nf_queue_entry *e) +{ + switch (e->state.pf) { + case NFPROTO_IPV4: + return nfqnl_validate_ip4(data, data_len, e); + case NFPROTO_IPV6: + return nfqnl_validate_ip6(data, data_len, e) && + !(IP6CB(e->skb)->flags & IP6SKB_JUMBOGRAM); + case NFPROTO_BRIDGE: + /* No write support. Bridge is dubious: userspace doesn't even see L2 header */ + return false; + } + + return false; +} + static int nfqnl_mangle(void *data, unsigned int data_len, struct nf_queue_entry *e, int diff) { @@ -1192,6 +1359,9 @@ nfqnl_mangle(void *data, unsigned int data_len, struct nf_queue_entry *e, int di if (e->state.net->user_ns != &init_user_ns) return -EPERM; + if (!nfqnl_validate_write(data, data_len, e)) + return -EINVAL; + if (diff < 0) { unsigned int min_len = skb_transport_offset(e->skb); diff --git a/net/netfilter/nft_fib.c b/net/netfilter/nft_fib.c index e048f05694cd..89555380f1c5 100644 --- a/net/netfilter/nft_fib.c +++ b/net/netfilter/nft_fib.c @@ -31,6 +31,15 @@ int nft_fib_validate(const struct nft_ctx *ctx, const struct nft_expr *expr) const struct nft_fib *priv = nft_expr_priv(expr); unsigned int hooks; + switch (ctx->family) { + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_INET: + break; + default: + return -EOPNOTSUPP; + } + switch (priv->result) { case NFT_FIB_RESULT_OIF: case NFT_FIB_RESULT_OIFNAME: diff --git a/net/netfilter/nft_fib_netdev.c b/net/netfilter/nft_fib_netdev.c index 3f3478abd845..5774a7544027 100644 --- a/net/netfilter/nft_fib_netdev.c +++ b/net/netfilter/nft_fib_netdev.c @@ -50,6 +50,33 @@ static void nft_fib_netdev_eval(const struct nft_expr *expr, regs->verdict.code = NFT_BREAK; } +static int nft_fib_netdev_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + const struct nft_fib *priv = nft_expr_priv(expr); + unsigned int hooks; + + switch (priv->result) { + case NFT_FIB_RESULT_OIF: + case NFT_FIB_RESULT_OIFNAME: + hooks = (1 << NF_NETDEV_INGRESS); + break; + case NFT_FIB_RESULT_ADDRTYPE: + if (priv->flags & NFTA_FIB_F_IIF) + hooks = (1 << NF_NETDEV_INGRESS); + else if (priv->flags & NFTA_FIB_F_OIF) + hooks = (1 << NF_NETDEV_EGRESS); + else + hooks = (1 << NF_NETDEV_INGRESS) | + (1 << NF_NETDEV_EGRESS); + break; + default: + return -EINVAL; + } + + return nft_chain_validate_hooks(ctx->chain, hooks); +} + static struct nft_expr_type nft_fib_netdev_type; static const struct nft_expr_ops nft_fib_netdev_ops = { .type = &nft_fib_netdev_type, @@ -57,7 +84,7 @@ static const struct nft_expr_ops nft_fib_netdev_ops = { .eval = nft_fib_netdev_eval, .init = nft_fib_init, .dump = nft_fib_dump, - .validate = nft_fib_validate, + .validate = nft_fib_netdev_validate, }; static struct nft_expr_type nft_fib_netdev_type __read_mostly = { diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 345eff140d56..391539a1ceaa 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -834,6 +834,249 @@ nft_payload_set_vlan(const u32 *src, struct sk_buff *skb, u16 offset, u8 len, return true; } +/* Ingress is very early, before l3 protocol handlers. + * There should be no in-tree code that trusts l3/l4 headers + * between ingress and NF_INET_PRE_ROUTING hooks. + */ +static bool nft_in_ingress(const struct nf_hook_state *s) +{ + return s->pf == NFPROTO_NETDEV && s->hook == NF_NETDEV_INGRESS; +} + +static bool nft_nh_write_ok_ip4(const struct nft_pktinfo *pkt, + const struct nft_payload_set *priv, + const u32 *src) +{ + unsigned int offset = priv->offset + skb_network_offset(pkt->skb); + const u8 *new_octets = (const u8 *)src; + u8 old_octet; + + switch (priv->offset) { + case 0: /* csum fixups does expand dscp/tos store to 2 bytes. + * make sure ihl/version remain unchanged. + */ + if (skb_copy_bits(pkt->skb, offset, &old_octet, sizeof(old_octet))) + return false; + + return priv->len == 2 && + *new_octets == old_octet; + case offsetof(struct iphdr, tos): + return priv->len == 1; + case offsetof(struct iphdr, id): + return priv->len == 2; + case offsetof(struct iphdr, ttl): + if (priv->len == 1) + return true; + + if (priv->len != 2) + return false; + + /* same, csum fixup does expand ttl store to two bytes. + * check protocol is not altered. + */ + if (skb_copy_bits(pkt->skb, offset + 1, &old_octet, sizeof(old_octet))) + return false; + + return new_octets[1] == old_octet; + case offsetof(struct iphdr, check): + return priv->len <= 2 + 4 + 4; + case offsetof(struct iphdr, saddr): + return priv->len <= 4 + 4; + case offsetof(struct iphdr, daddr): + return priv->len <= 4; + } + + return false; +} + +static bool nft_nh_write_ok_ip6(const struct nft_pktinfo *pkt, + const struct nft_payload_set *priv, + const u32 *src) +{ + const struct ipv6hdr *ih = (const void *)src; + + switch (priv->offset) { + case 0: /* store to dscp must not alter ip6 version */ + return priv->len <= 4 && ih->version == 6; + case 2: + return priv->len <= 2; + case offsetof(struct ipv6hdr, hop_limit): + return priv->len == 1; + case offsetof(struct ipv6hdr, saddr): + return priv->len <= 16 + 16; + case offsetof(struct ipv6hdr, daddr): + return priv->len <= 16; + } + + return false; +} + +static bool nft_nh_write_ok_arp(const struct nft_payload_set *priv) +{ + /* Variable size for standard ethernet arp */ + const unsigned int eth_ip = 2 * (ETH_ALEN + 4); + unsigned int offset = priv->offset; + + switch (offset) { + case offsetof(struct arphdr, ar_op): + return priv->len == 2; + default: + break; + } + + /* permit writes post fixed arp header size. offset + len are + * checked vs skb size via skb_ensure_writable. + */ + return offset >= sizeof(struct arphdr) && priv->len <= eth_ip; +} + +static bool nft_nh_write_ok_netdev(const struct nft_pktinfo *pkt, + const struct nft_payload_set *priv, + const u32 *src) +{ +#ifdef CONFIG_NF_TABLES_NETDEV + switch (pkt->skb->protocol) { + case htons(ETH_P_ARP): + return nft_nh_write_ok_arp(priv); + case htons(ETH_P_IP): + return nft_nh_write_ok_ip4(pkt, priv, src); + case htons(ETH_P_IPV6): + return nft_nh_write_ok_ip6(pkt, priv, src); + } +#endif + /* default to false for now, relax later in case we have + * use-cases that need inner header manipulation for + * encapsulated traffic like vlan or PPPoE. + */ + return false; +} + +static bool nft_nh_write_ok_bridge(const struct nft_pktinfo *pkt, + const struct nft_payload_set *priv, + const u32 *src) +{ +#if IS_ENABLED(CONFIG_NF_TABLES_BRIDGE) + switch (pkt->ethertype) { + case htons(ETH_P_ARP): + return nft_nh_write_ok_arp(priv); + case htons(ETH_P_IP): + return nft_nh_write_ok_ip4(pkt, priv, src); + case htons(ETH_P_IPV6): + return nft_nh_write_ok_ip6(pkt, priv, src); + } +#endif + /* see nft_nh_write_ok_netdev: default to false */ + return false; +} + +static bool nft_nh_write_ok(const struct nft_pktinfo *pkt, + const struct nft_payload_set *priv, + const u32 *src) +{ + switch (pkt->state->pf) { + case NFPROTO_ARP: + return nft_nh_write_ok_arp(priv); + case NFPROTO_BRIDGE: + return nft_nh_write_ok_bridge(pkt, priv, src); + case NFPROTO_IPV4: + return nft_nh_write_ok_ip4(pkt, priv, src); + case NFPROTO_IPV6: + return nft_nh_write_ok_ip6(pkt, priv, src); + case NFPROTO_NETDEV: + if (pkt->state->hook == NF_NETDEV_INGRESS) + return true; + return nft_nh_write_ok_netdev(pkt, priv, src); + } + + return false; +} + +/* check linklayer modifications don't spill into network header. */ +static bool nft_ll_write_ok(const struct nft_pktinfo *pkt, int offset) +{ + if (nft_in_ingress(pkt->state)) + return true; + + return offset <= skb_network_offset(pkt->skb); +} + +static bool nft_payload_validate_inet_csum_offset(const struct nft_ctx *ctx, + const struct nft_payload_set *priv) +{ + switch (priv->base) { + case NFT_PAYLOAD_LL_HEADER: + break; + case NFT_PAYLOAD_NETWORK_HEADER: + if (ctx->family == NFPROTO_IPV4) { + if (offsetof(struct iphdr, check) == priv->csum_offset) + return true; + + return false; + } + return true; /* run time validation required */ + case NFT_PAYLOAD_TRANSPORT_HEADER: + if (priv->csum_flags) /* makes no sense, asks for "re-update" of L4 checksum */ + return false; + + /* no further check here; offset can't be negative so bogus + * offsets can corrupt L4 or payload but not l3 headers. + * We already allow arbitrary l4/inner payload writes. + */ + return true; + case NFT_PAYLOAD_INNER_HEADER: + return true; + case NFT_PAYLOAD_TUN_HEADER: + break; + } + + return false; +} + +/* do not allow arbitrary network header mangling via bogus csum_off. + * We only support ipv4. Only NFPROTO_IPV4 can be checked from control + * plane. + */ +static bool nft_payload_csum_nh_write_ok(const struct nft_payload_set *priv, + const struct nft_pktinfo *pkt) +{ + switch (pkt->state->pf) { + case NFPROTO_IPV4: + /* Warning: NFPROTO_INET was not checked; we can't return true here. */ + return priv->csum_offset == offsetof(struct iphdr, check); + case NFPROTO_IPV6: + return false; + case NFPROTO_BRIDGE: + return pkt->ethertype == htons(ETH_P_IP) && + priv->csum_offset == offsetof(struct iphdr, check); + case NFPROTO_NETDEV: + return pkt->skb->protocol == htons(ETH_P_IP) && + priv->csum_offset == offsetof(struct iphdr, check); + } + + return false; +} + +static bool nft_payload_csum_write_ok(const struct nft_pktinfo *pkt, + const struct nft_payload_set *priv) +{ + switch (priv->base) { + case NFT_PAYLOAD_LL_HEADER: + break; + case NFT_PAYLOAD_NETWORK_HEADER: + return nft_payload_csum_nh_write_ok(priv, pkt); + case NFT_PAYLOAD_TRANSPORT_HEADER: + case NFT_PAYLOAD_INNER_HEADER: + /* neither offsets are validated, offsets cannot be + * negative so real l3 headers cannot be mangled. + */ + return true; + case NFT_PAYLOAD_TUN_HEADER: + break; + } + + return false; +} + static void nft_payload_set_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -861,8 +1104,12 @@ static void nft_payload_set_eval(const struct nft_expr *expr, } offset = skb_mac_header(skb) - skb->data - vlan_hlen; + if (!nft_ll_write_ok(pkt, priv->len + priv->offset + offset)) + goto err; break; case NFT_PAYLOAD_NETWORK_HEADER: + if (!nft_nh_write_ok(pkt, priv, src)) + goto err; offset = skb_network_offset(skb); break; case NFT_PAYLOAD_TRANSPORT_HEADER: @@ -894,6 +1141,7 @@ static void nft_payload_set_eval(const struct nft_expr *expr, tsum = csum_partial(src, priv->len, 0); if (priv->csum_type == NFT_PAYLOAD_CSUM_INET && + nft_payload_csum_write_ok(pkt, priv) && nft_payload_csum_inet(skb, src, fsum, tsum, csum_offset)) goto err; @@ -960,7 +1208,26 @@ static int nft_payload_set_init(const struct nft_ctx *ctx, switch (csum_type) { case NFT_PAYLOAD_CSUM_NONE: + if (priv->csum_offset) /* nonsensical */ + return -EINVAL; + + if (priv->csum_flags == 0) + break; + + /* Userspace requests L4 checksum update, e.g.: + * - IPv6 stateless NAT (no l3 csum) + * - transport header mangling + * - inner data mangling + */ + if (priv->base == NFT_PAYLOAD_NETWORK_HEADER || + priv->base == NFT_PAYLOAD_TRANSPORT_HEADER || + priv->base == NFT_PAYLOAD_INNER_HEADER) + break; + + return -EINVAL; case NFT_PAYLOAD_CSUM_INET: + if (!nft_payload_validate_inet_csum_offset(ctx, priv)) + return -EINVAL; break; case NFT_PAYLOAD_CSUM_SCTP: if (priv->base != NFT_PAYLOAD_TRANSPORT_HEADER) @@ -968,6 +1235,9 @@ static int nft_payload_set_init(const struct nft_ctx *ctx, if (priv->csum_offset != offsetof(struct sctphdr, checksum)) return -EINVAL; + + if (priv->csum_flags) + return -EINVAL; break; default: return -EOPNOTSUPP; diff --git a/net/netfilter/nft_set_pipapo.c b/net/netfilter/nft_set_pipapo.c index 706c78853f24..978bb0c01106 100644 --- a/net/netfilter/nft_set_pipapo.c +++ b/net/netfilter/nft_set_pipapo.c @@ -342,6 +342,8 @@ #include "nft_set_pipapo_avx2.h" #include "nft_set_pipapo.h" +static void nft_pipapo_abort(const struct nft_set *set); + /** * pipapo_refill() - For each set bit, set bits from selected mapping table item * @map: Bitmap to be scanned for set bits @@ -1296,7 +1298,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, const u8 *start_p, *end_p; int i, bsize_max, err = 0; - if (!m) + if (!m || m->state == NFT_PIPAPO_CLONE_ERR) return -ENOMEM; if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END)) @@ -1367,8 +1369,10 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, else ret = pipapo_expand(f, start, end, f->groups * f->bb); - if (ret < 0) - return ret; + if (ret < 0) { + err = ret; + goto abort; + } if (f->bsize > bsize_max) bsize_max = f->bsize; @@ -1384,7 +1388,7 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, err = pipapo_realloc_scratch(m, bsize_max); if (err) - return err; + goto abort; m->bsize_max = bsize_max; } else { @@ -1396,7 +1400,26 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set, pipapo_map(m, rulemap, e); + m->state = NFT_PIPAPO_CLONE_MOD; return 0; +abort: + DEBUG_NET_WARN_ON_ONCE(m->state == NFT_PIPAPO_CLONE_ERR); + + /* Two rollback cases: + * 1) no previous changes. nft_pipapo_abort is not + * guaranteed to be invoked (there might be no further + * add/delete requests coming after this). + * + * 2) we had previous changes: there are transaction + * records pointing to this set. Leave the rollback to + * the transaction handling. + */ + if (m->state == NFT_PIPAPO_CLONE_NEW) + nft_pipapo_abort(set); /* releases m */ + else + m->state = NFT_PIPAPO_CLONE_ERR; + + return err; } /** @@ -1473,6 +1496,7 @@ static struct nft_pipapo_match *pipapo_clone(struct nft_pipapo_match *old) dst++; } + new->state = NFT_PIPAPO_CLONE_NEW; return new; out_mt: @@ -1896,7 +1920,7 @@ nft_pipapo_deactivate(const struct net *net, const struct nft_set *set, /* removal must occur on priv->clone, if we are low on memory * we have no choice and must fail the removal request. */ - if (!m) + if (!m || m->state == NFT_PIPAPO_CLONE_ERR) return NULL; e = pipapo_get(m, (const u8 *)elem->key.val.data, diff --git a/net/netfilter/nft_set_pipapo.h b/net/netfilter/nft_set_pipapo.h index b82abb03576e..a19e980d06ef 100644 --- a/net/netfilter/nft_set_pipapo.h +++ b/net/netfilter/nft_set_pipapo.h @@ -131,9 +131,16 @@ struct nft_pipapo_scratch { unsigned long __map[]; }; +enum nft_pipapo_clone_state { + NFT_PIPAPO_CLONE_NEW, + NFT_PIPAPO_CLONE_MOD, + NFT_PIPAPO_CLONE_ERR, +}; + /** * struct nft_pipapo_match - Data used for lookup and matching * @field_count: Amount of fields in set + * @state: add/delete state; used from control plane * @bsize_max: Maximum lookup table bucket size of all fields, in longs * @scratch: Preallocated per-CPU maps for partial matching results * @rcu: Matching data is swapped on commits @@ -141,6 +148,7 @@ struct nft_pipapo_scratch { */ struct nft_pipapo_match { u8 field_count; + enum nft_pipapo_clone_state state:8; unsigned int bsize_max; struct nft_pipapo_scratch * __percpu *scratch; struct rcu_head rcu; diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index 58a074651176..09d46e195e33 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -44,7 +44,7 @@ TC_INDIRECT_SCOPE int tcf_bpf_act(struct sk_buff *skb, tcf_lastuse_update(&prog->tcf_tm); bstats_update(this_cpu_ptr(prog->common.cpu_bstats), skb); - filter = rcu_dereference(prog->filter); + filter = rcu_dereference_bh(prog->filter); if (at_ingress) { __skb_push(skb, skb->mac_len); filter_res = bpf_prog_run_data_pointers(filter, skb); diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index 5434df6ca8ef..27088760eff4 100644 --- a/net/sched/sch_dualpi2.c +++ b/net/sched/sch_dualpi2.c @@ -346,6 +346,8 @@ static int dualpi2_skb_classify(struct dualpi2_sched_data *q, struct tcf_proto *fl; int result; + cb->classified = DUALPI2_C_CLASSIC; + dualpi2_read_ect(skb); if (cb->ect & q->ecn_mask) { cb->classified = DUALPI2_C_L4S; @@ -359,10 +361,8 @@ static int dualpi2_skb_classify(struct dualpi2_sched_data *q, } fl = rcu_dereference_bh(q->tcf_filters); - if (!fl) { - cb->classified = DUALPI2_C_CLASSIC; + if (!fl) return NET_XMIT_SUCCESS; - } result = tcf_classify(skb, NULL, fl, &res, false); if (result >= 0) { diff --git a/net/sched/sch_hhf.c b/net/sched/sch_hhf.c index 1e25b75daae2..d85cb0263b67 100644 --- a/net/sched/sch_hhf.c +++ b/net/sched/sch_hhf.c @@ -462,12 +462,39 @@ begin: return skb; } +static void hhf_reset_classifier(struct hhf_sched_data *q) +{ + int i; + + if (!q->hh_flows) + return; + + for (i = 0; i < HH_FLOWS_CNT; i++) { + struct hh_flow_state *flow, *next; + struct list_head *head = &q->hh_flows[i]; + + list_for_each_entry_safe(flow, next, head, flowchain) { + list_del(&flow->flowchain); + kfree(flow); + } + } + WRITE_ONCE(q->hh_flows_current_cnt, 0); + + for (i = 0; i < HHF_ARRAYS_CNT; i++) { + if (q->hhf_valid_bits[i]) + bitmap_zero(q->hhf_valid_bits[i], HHF_ARRAYS_LEN); + } + q->hhf_arrays_reset_timestamp = hhf_time_stamp(); +} + static void hhf_reset(struct Qdisc *sch) { + struct hhf_sched_data *q = qdisc_priv(sch); struct sk_buff *skb; while ((skb = hhf_dequeue(sch)) != NULL) rtnl_kfree_skbs(skb, skb); + hhf_reset_classifier(q); } static void hhf_destroy(struct Qdisc *sch) diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index 4e465d11e3d7..a467dd122369 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -103,7 +103,7 @@ static struct sk_buff *multiq_dequeue(struct Qdisc *sch) if (!netif_xmit_stopped( netdev_get_tx_queue(qdisc_dev(sch), q->curband))) { qdisc = q->queues[q->curband]; - skb = qdisc->dequeue(qdisc); + skb = qdisc_dequeue_peeked(qdisc); if (skb) { qdisc_bstats_update(sch, skb); qdisc_qlen_dec(sch); diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c index 558987d9b977..299234a5f0fe 100644 --- a/net/sched/sch_taprio.c +++ b/net/sched/sch_taprio.c @@ -749,7 +749,7 @@ static struct sk_buff *taprio_dequeue_from_txq(struct Qdisc *sch, int txq, return NULL; skip_peek_checks: - skb = child->ops->dequeue(child); + skb = qdisc_dequeue_peeked(child); if (unlikely(!skb)) return NULL; diff --git a/net/sched/sch_teql.c b/net/sched/sch_teql.c index e7bbc9e5174d..24ba31f8c828 100644 --- a/net/sched/sch_teql.c +++ b/net/sched/sch_teql.c @@ -52,7 +52,8 @@ struct teql_master { struct Qdisc_ops qops; struct net_device *dev; - struct Qdisc *slaves; + struct Qdisc __rcu *slaves; + spinlock_t slaves_lock; /* serializes writes to ->slaves */ struct list_head master_list; unsigned long tx_bytes; unsigned long tx_packets; @@ -61,7 +62,7 @@ struct teql_master { }; struct teql_sched_data { - struct Qdisc *next; + struct Qdisc __rcu *next; struct teql_master *m; struct sk_buff_head q; }; @@ -101,7 +102,9 @@ teql_dequeue(struct Qdisc *sch) if (skb == NULL) { struct net_device *m = qdisc_dev(q); if (m) { - dat->m->slaves = sch; + spin_lock_bh(&dat->m->slaves_lock); + rcu_assign_pointer(dat->m->slaves, sch); + spin_unlock_bh(&dat->m->slaves_lock); netif_wake_queue(m); } } else { @@ -132,34 +135,49 @@ teql_destroy(struct Qdisc *sch) struct Qdisc *q, *prev; struct teql_sched_data *dat = qdisc_priv(sch); struct teql_master *master = dat->m; + struct netdev_queue *txq = NULL; + bool reset_master_queue = false; if (!master) return; - prev = master->slaves; + spin_lock_bh(&master->slaves_lock); + prev = rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)); if (prev) { do { - q = NEXT_SLAVE(prev); - if (q == sch) { - NEXT_SLAVE(prev) = NEXT_SLAVE(q); - if (q == master->slaves) { - master->slaves = NEXT_SLAVE(q); - if (q == master->slaves) { - struct netdev_queue *txq; - - txq = netdev_get_tx_queue(master->dev, 0); - master->slaves = NULL; - - dev_reset_queue(master->dev, - txq, NULL); - } - } - skb_queue_purge(&dat->q); - break; + struct Qdisc *head, *next; + + q = rcu_dereference_protected(NEXT_SLAVE(prev), + lockdep_is_held(&master->slaves_lock)); + if (q != sch) { + prev = q; + continue; } - } while ((prev = q) != master->slaves); + next = rcu_dereference_protected(NEXT_SLAVE(q), + lockdep_is_held(&master->slaves_lock)); + rcu_assign_pointer(NEXT_SLAVE(prev), next); + + head = rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)); + if (q == head) { + rcu_assign_pointer(master->slaves, next); + if (q == next) { + txq = netdev_get_tx_queue(master->dev, 0); + rcu_assign_pointer(master->slaves, NULL); + reset_master_queue = true; + } + } + skb_queue_purge(&dat->q); + break; + } while (prev != rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock))); } + spin_unlock_bh(&master->slaves_lock); + + if (reset_master_queue) + dev_reset_queue(master->dev, txq, NULL); } static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, @@ -168,6 +186,7 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, struct net_device *dev = qdisc_dev(sch); struct teql_master *m = (struct teql_master *)sch->ops; struct teql_sched_data *q = qdisc_priv(sch); + struct Qdisc *first; if (dev->hard_header_len > m->dev->hard_header_len) return -EINVAL; @@ -184,7 +203,9 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, skb_queue_head_init(&q->q); - if (m->slaves) { + spin_lock_bh(&m->slaves_lock); + first = rcu_dereference_protected(m->slaves, lockdep_is_held(&m->slaves_lock)); + if (first) { if (m->dev->flags & IFF_UP) { if ((m->dev->flags & IFF_POINTOPOINT && !(dev->flags & IFF_POINTOPOINT)) || @@ -192,8 +213,10 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, !(dev->flags & IFF_BROADCAST)) || (m->dev->flags & IFF_MULTICAST && !(dev->flags & IFF_MULTICAST)) || - dev->mtu < m->dev->mtu) + dev->mtu < m->dev->mtu) { + spin_unlock_bh(&m->slaves_lock); return -EINVAL; + } } else { if (!(dev->flags&IFF_POINTOPOINT)) m->dev->flags &= ~IFF_POINTOPOINT; @@ -204,14 +227,17 @@ static int teql_qdisc_init(struct Qdisc *sch, struct nlattr *opt, if (dev->mtu < m->dev->mtu) m->dev->mtu = dev->mtu; } - q->next = NEXT_SLAVE(m->slaves); - NEXT_SLAVE(m->slaves) = sch; + rcu_assign_pointer(q->next, + rcu_dereference_protected(NEXT_SLAVE(first), + lockdep_is_held(&m->slaves_lock))); + rcu_assign_pointer(NEXT_SLAVE(first), sch); } else { - q->next = sch; - m->slaves = sch; + rcu_assign_pointer(q->next, sch); + rcu_assign_pointer(m->slaves, sch); m->dev->mtu = dev->mtu; m->dev->flags = (m->dev->flags&~FMASK)|(dev->flags&FMASK); } + spin_unlock_bh(&m->slaves_lock); return 0; } @@ -285,7 +311,9 @@ static netdev_tx_t teql_master_xmit(struct sk_buff *skb, struct net_device *dev) int subq = skb_get_queue_mapping(skb); struct sk_buff *skb_res = NULL; - start = master->slaves; + rcu_read_lock_bh(); + + start = rcu_dereference_bh(master->slaves); restart: nores = 0; @@ -317,10 +345,17 @@ restart: netdev_start_xmit(skb, slave, slave_txq, false) == NETDEV_TX_OK) { __netif_tx_unlock(slave_txq); - master->slaves = NEXT_SLAVE(q); + spin_lock_bh(&master->slaves_lock); + if (rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)) == q) + rcu_assign_pointer(master->slaves, + rcu_dereference_protected(NEXT_SLAVE(q), + lockdep_is_held(&master->slaves_lock))); + spin_unlock_bh(&master->slaves_lock); netif_wake_queue(dev); master->tx_packets++; master->tx_bytes += length; + rcu_read_unlock_bh(); return NETDEV_TX_OK; } __netif_tx_unlock(slave_txq); @@ -329,14 +364,21 @@ restart: busy = 1; break; case 1: - master->slaves = NEXT_SLAVE(q); + spin_lock_bh(&master->slaves_lock); + if (rcu_dereference_protected(master->slaves, + lockdep_is_held(&master->slaves_lock)) == q) + rcu_assign_pointer(master->slaves, + rcu_dereference_protected(NEXT_SLAVE(q), + lockdep_is_held(&master->slaves_lock))); + spin_unlock_bh(&master->slaves_lock); + rcu_read_unlock_bh(); return NETDEV_TX_OK; default: nores = 1; break; } __skb_pull(skb, skb_network_offset(skb)); - } while ((q = NEXT_SLAVE(q)) != start); + } while ((q = rcu_dereference_bh(NEXT_SLAVE(q))) != start); if (nores && skb_res == NULL) { skb_res = skb; @@ -345,29 +387,32 @@ restart: if (busy) { netif_stop_queue(dev); + rcu_read_unlock_bh(); return NETDEV_TX_BUSY; } master->tx_errors++; drop: master->tx_dropped++; + rcu_read_unlock_bh(); dev_kfree_skb(skb); return NETDEV_TX_OK; } static int teql_master_open(struct net_device *dev) { - struct Qdisc *q; + struct Qdisc *q, *first; struct teql_master *m = netdev_priv(dev); int mtu = 0xFFFE; unsigned int flags = IFF_NOARP | IFF_MULTICAST; - if (m->slaves == NULL) + first = rtnl_dereference(m->slaves); + if (!first) return -EUNATCH; flags = FMASK; - q = m->slaves; + q = first; do { struct net_device *slave = qdisc_dev(q); @@ -389,7 +434,7 @@ static int teql_master_open(struct net_device *dev) flags &= ~IFF_BROADCAST; if (!(slave->flags&IFF_MULTICAST)) flags &= ~IFF_MULTICAST; - } while ((q = NEXT_SLAVE(q)) != m->slaves); + } while ((q = rtnl_dereference(NEXT_SLAVE(q))) != first); m->dev->mtu = mtu; m->dev->flags = (m->dev->flags&~FMASK) | flags; @@ -417,14 +462,15 @@ static void teql_master_stats64(struct net_device *dev, static int teql_master_mtu(struct net_device *dev, int new_mtu) { struct teql_master *m = netdev_priv(dev); - struct Qdisc *q; + struct Qdisc *q, *first; - q = m->slaves; + first = rtnl_dereference(m->slaves); + q = first; if (q) { do { if (new_mtu > qdisc_dev(q)->mtu) return -EINVAL; - } while ((q = NEXT_SLAVE(q)) != m->slaves); + } while ((q = rtnl_dereference(NEXT_SLAVE(q))) != first); } WRITE_ONCE(dev->mtu, new_mtu); @@ -444,6 +490,7 @@ static __init void teql_master_setup(struct net_device *dev) struct teql_master *master = netdev_priv(dev); struct Qdisc_ops *ops = &master->qops; + spin_lock_init(&master->slaves_lock); master->dev = dev; ops->priv_size = sizeof(struct teql_sched_data); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 587b0017a67d..cf335494bffe 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -663,8 +663,9 @@ static void sctp_free_addr_wq(struct net *net) struct sctp_sockaddr_entry *addrw; struct sctp_sockaddr_entry *temp; + timer_shutdown_sync(&net->sctp.addr_wq_timer); + spin_lock_bh(&net->sctp.addr_wq_lock); - timer_delete(&net->sctp.addr_wq_timer); list_for_each_entry_safe(addrw, temp, &net->sctp.addr_waitq, list) { list_del(&addrw->list); kfree(addrw); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 41958b8e59fd..8adac9e0cd66 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1761,6 +1761,8 @@ struct sctp_association *sctp_unpack_cookie( bear_cookie = &cookie->c; ch = (struct sctp_chunkhdr *)(bear_cookie + 1); + if (ch->type != SCTP_CID_INIT) + goto malformed; chlen = ntohs(ch->length); if (chlen < sizeof(struct sctp_init_chunk)) goto malformed; @@ -2298,7 +2300,8 @@ int sctp_verify_init(struct net *net, const struct sctp_endpoint *ep, * VIOLATION error. We build the ERROR chunk here and let the normal * error handling code build and send the packet. */ - if (param.v != (void *)chunk->chunk_end) + if (param.v != (void *)peer_init + + SCTP_PAD4(ntohs(peer_init->chunk_hdr.length))) return sctp_process_inv_paramlength(asoc, param.p, chunk, errp); /* The only missing mandatory param possible today is diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 8e920cef0858..d23d935e128e 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -707,11 +707,12 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, struct sctp_cmd_seq *commands) { struct sctp_ulpevent *ev, *ai_ev = NULL, *auth_ev = NULL; + struct sctp_chunk *err_chk_p = NULL; struct sctp_association *new_asoc; struct sctp_init_chunk *peer_init; struct sctp_chunk *chunk = arg; - struct sctp_chunk *err_chk_p; struct sctp_chunk *repl; + enum sctp_cid cid; struct sock *sk; int error = 0; @@ -785,6 +786,19 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, } } + peer_init = (struct sctp_init_chunk *)(chunk->subh.cookie_hdr + 1); + cid = peer_init->chunk_hdr.type; + if (!sctp_sk(sk)->cookie_auth_enable && + !sctp_verify_init(net, ep, asoc, cid, peer_init, chunk, + &err_chk_p)) { + sctp_association_free(new_asoc); + if (err_chk_p) + sctp_chunk_free(err_chk_p); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); + } + if (err_chk_p) + sctp_chunk_free(err_chk_p); + if (security_sctp_assoc_request(new_asoc, chunk->head_skb ?: chunk->skb)) { sctp_association_free(new_asoc); return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); @@ -798,7 +812,6 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, /* This is a brand-new association, so these are not yet side * effects--it is safe to run them here. */ - peer_init = (struct sctp_init_chunk *)(chunk->subh.cookie_hdr + 1); if (!sctp_process_init(new_asoc, chunk, &chunk->subh.cookie_hdr->c.peer_addr, peer_init, GFP_ATOMIC)) @@ -2215,10 +2228,12 @@ enum sctp_disposition sctp_sf_do_5_2_4_dupcook( void *arg, struct sctp_cmd_seq *commands) { + struct sctp_chunk *err_chk_p = NULL; struct sctp_association *new_asoc; + struct sctp_init_chunk *peer_init; struct sctp_chunk *chunk = arg; enum sctp_disposition retval; - struct sctp_chunk *err_chk_p; + enum sctp_cid cid; int error = 0; char action; @@ -2287,6 +2302,21 @@ enum sctp_disposition sctp_sf_do_5_2_4_dupcook( switch (action) { case 'A': /* Association restart. */ case 'B': /* Collision case B. */ + peer_init = (struct sctp_init_chunk *) + (chunk->subh.cookie_hdr + 1); + cid = peer_init->chunk_hdr.type; + if (!sctp_sk(ep->base.sk)->cookie_auth_enable && + !sctp_verify_init(net, ep, asoc, cid, peer_init, chunk, + &err_chk_p)) { + sctp_association_free(new_asoc); + if (err_chk_p) + sctp_chunk_free(err_chk_p); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, + commands); + } + if (err_chk_p) + sctp_chunk_free(err_chk_p); + fallthrough; case 'D': /* Collision case D. */ /* Update socket peer label if first association. */ if (security_sctp_assoc_request((struct sctp_association *)asoc, diff --git a/net/sctp/socket.c b/net/sctp/socket.c index c8481461f7d8..c7b9e325ec1c 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -4111,8 +4111,9 @@ static int sctp_setsockopt_reset_streams(struct sock *sk, if (optlen < sizeof(*params)) return -EINVAL; /* srs_number_streams is u16, so optlen can't be bigger than this. */ - optlen = min_t(unsigned int, optlen, USHRT_MAX + - sizeof(__u16) * sizeof(*params)); + optlen = min_t(unsigned int, optlen, + struct_size_t(struct sctp_reset_streams, srs_stream_list, + USHRT_MAX)); if (params->srs_number_streams * sizeof(__u16) > optlen - sizeof(*params)) @@ -4598,8 +4599,8 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, if (optlen > 0) { /* Trim it to the biggest size sctp sockopt may need if necessary */ optlen = min_t(unsigned int, optlen, - PAGE_ALIGN(USHRT_MAX + - sizeof(__u16) * sizeof(struct sctp_reset_streams))); + PAGE_ALIGN(struct_size_t(struct sctp_reset_streams, + srs_stream_list, USHRT_MAX))); kopt = memdup_sockptr(optval, optlen); if (IS_ERR(kopt)) return PTR_ERR(kopt); diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index 76a1585d3f6b..10d1ec593084 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -497,12 +497,13 @@ void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, */ int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr, - struct sk_buff_head *retrq) + struct sk_buff_head *retrq, bool *valid) { struct sk_buff_head *inputq = &tipc_bc_base(net)->inputq; struct tipc_gap_ack_blks *ga; struct sk_buff_head xmitq; int rc = 0; + u16 glen; __skb_queue_head_init(&xmitq); @@ -510,13 +511,18 @@ int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, if (msg_type(hdr) != STATE_MSG) { tipc_link_bc_init_rcv(l, hdr); } else if (!msg_bc_ack_invalid(hdr)) { - tipc_get_gap_ack_blks(&ga, l, hdr, false); - if (!sysctl_tipc_bc_retruni) - retrq = &xmitq; - rc = tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr), - msg_bc_gap(hdr), ga, &xmitq, - retrq); - rc |= tipc_link_bc_sync_rcv(l, hdr, &xmitq); + glen = tipc_get_gap_ack_blks(&ga, l, hdr, false); + if (glen > msg_data_sz(hdr)) { + /* Malformed Gap ACK blocks; caller drops the msg */ + *valid = false; + } else { + if (!sysctl_tipc_bc_retruni) + retrq = &xmitq; + rc = tipc_link_bc_ack_rcv(l, msg_bcast_ack(hdr), + msg_bc_gap(hdr), ga, &xmitq, + retrq); + rc |= tipc_link_bc_sync_rcv(l, hdr, &xmitq); + } } tipc_bcast_unlock(net); diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h index 2d9352dc7b0e..55d17b5413e1 100644 --- a/net/tipc/bcast.h +++ b/net/tipc/bcast.h @@ -97,7 +97,7 @@ void tipc_bcast_ack_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr); int tipc_bcast_sync_rcv(struct net *net, struct tipc_link *l, struct tipc_msg *hdr, - struct sk_buff_head *retrq); + struct sk_buff_head *retrq, bool *valid); int tipc_nl_add_bc_link(struct net *net, struct tipc_nl_msg *msg, struct tipc_link *bcl); int tipc_nl_bc_link_set(struct net *net, struct nlattr *attrs[]); diff --git a/net/tipc/node.c b/net/tipc/node.c index 97aa970a0d83..8e4ef2630ae4 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -1831,12 +1831,15 @@ static void tipc_node_mcast_rcv(struct tipc_node *n) } static void tipc_node_bc_sync_rcv(struct tipc_node *n, struct tipc_msg *hdr, - int bearer_id, struct sk_buff_head *xmitq) + int bearer_id, struct sk_buff_head *xmitq, + bool *valid) { struct tipc_link *ucl; int rc; - rc = tipc_bcast_sync_rcv(n->net, n->bc_entry.link, hdr, xmitq); + rc = tipc_bcast_sync_rcv(n->net, n->bc_entry.link, hdr, xmitq, valid); + if (!*valid) + return; if (rc & TIPC_LINK_DOWN_EVT) { tipc_node_reset_links(n); @@ -2140,12 +2143,18 @@ rcv: /* Ensure broadcast reception is in synch with peer's send state */ if (unlikely(usr == LINK_PROTOCOL)) { + bool valid = true; + if (unlikely(skb_linearize(skb))) { tipc_node_put(n); goto discard; } hdr = buf_msg(skb); - tipc_node_bc_sync_rcv(n, hdr, bearer_id, &xmitq); + tipc_node_bc_sync_rcv(n, hdr, bearer_id, &xmitq, &valid); + if (!valid) { + tipc_node_put(n); + goto discard; + } } else if (unlikely(tipc_link_acked(n->bc_entry.link) != bc_ack)) { tipc_bcast_ack_rcv(net, n->bc_entry.link, hdr); } diff --git a/samples/trace_events/trace-events-sample.c b/samples/trace_events/trace-events-sample.c index ecc7db237f2e..0b7a6efdb247 100644 --- a/samples/trace_events/trace-events-sample.c +++ b/samples/trace_events/trace-events-sample.c @@ -107,6 +107,10 @@ int foo_bar_reg(void) * for consistency sake, we still take the thread_mutex. */ simple_tsk_fn = kthread_run(simple_thread_fn, NULL, "event-sample-fn"); + if (IS_ERR_OR_NULL(simple_tsk_fn)) { + pr_err("Failed to create simple_thread_fn\n"); + simple_tsk_fn = NULL; + } out: mutex_unlock(&thread_mutex); return 0; diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index fd63d219bf86..ea699491f0c3 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -1083,15 +1083,18 @@ static int snd_compr_task_new(struct snd_compr_stream *stream, struct snd_compr_ file descriptors are allocated before fd_install() */ if (!task->input || !task->input->file || !task->output || !task->output->file) { retval = -EINVAL; - goto cleanup; + goto free_driver_task; } fd_i = get_unused_fd_flags(O_WRONLY|O_CLOEXEC); - if (fd_i < 0) - goto cleanup; + if (fd_i < 0) { + retval = fd_i; + goto free_driver_task; + } fd_o = get_unused_fd_flags(O_RDONLY|O_CLOEXEC); if (fd_o < 0) { + retval = fd_o; put_unused_fd(fd_i); - goto cleanup; + goto free_driver_task; } /* keep dmabuf reference until freed with task free ioctl */ get_dma_buf(task->input); @@ -1103,6 +1106,8 @@ static int snd_compr_task_new(struct snd_compr_stream *stream, struct snd_compr_ list_add_tail(&task->list, &stream->runtime->tasks); stream->runtime->total_tasks++; return 0; +free_driver_task: + stream->ops->task_free(stream, task); cleanup: snd_compr_task_free(task); return retval; diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index fe597f7d522d..4d665b4148d7 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -545,7 +545,7 @@ void snd_pcm_set_sync_per_card(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, const unsigned char *id, unsigned int len) { - *(__u32 *)params->sync = cpu_to_le32(substream->pcm->card->number); + *(__le32 *)params->sync = cpu_to_le32(substream->pcm->card->number); len = min(12, len); memcpy(params->sync + 4, id, len); memset(params->sync + 4 + len, 0, 12 - len); diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index ca9f6db0022c..209b08c2a940 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -364,7 +364,7 @@ int snd_seq_event_dup(struct snd_seq_pool *pool, struct snd_seq_event *event, size = snd_seq_event_packet_size(event); memcpy(&cell->ump, event, size); #if IS_ENABLED(CONFIG_SND_SEQ_UMP) - if (size < sizeof(cell->event)) + if (size < sizeof(cell->ump)) cell->ump.raw.extra = 0; #endif diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c index 2b7f071d593b..33c9dd48b3b0 100644 --- a/sound/firewire/isight.c +++ b/sound/firewire/isight.c @@ -179,7 +179,8 @@ static void isight_packet(struct fw_iso_context *context, u32 cycle, if (likely(length >= 16 && payload->signature == cpu_to_be32(0x73676874/*"sght"*/))) { count = be32_to_cpu(payload->sample_count); - if (likely(count <= (length - 16) / 4)) { + if (likely(count <= (length - 16) / 4 && + count <= MAX_FRAMES_PER_PACKET)) { total = be32_to_cpu(payload->sample_total); if (unlikely(total != isight->total_samples)) { if (!isight->first_packet) diff --git a/sound/hda/codecs/conexant.c b/sound/hda/codecs/conexant.c index e3b6aaabe3a9..3d92262763f6 100644 --- a/sound/hda/codecs/conexant.c +++ b/sound/hda/codecs/conexant.c @@ -291,6 +291,7 @@ enum { CXT_FIXUP_HEADSET_MIC, CXT_FIXUP_HP_MIC_NO_PRESENCE, CXT_PINCFG_SWS_JS201D, + CXT_PINCFG_LENOVO_IDEAPAD_SLIM5_16AKP10, CXT_PINCFG_TOP_SPEAKER, CXT_FIXUP_HP_A_U, CXT_FIXUP_ACER_SWIFT_HP, @@ -826,6 +827,12 @@ static const struct hda_pintbl cxt_pincfg_lemote[] = { {} }; +/* Lenovo IdeaPad Slim 5 16AKP10 with SN6140 */ +static const struct hda_pintbl cxt_pincfg_lenovo_ideapad_slim5_16akp10[] = { + { 0x1a, 0x95a60130 }, /* Internal mic, fixed/always-connected */ + {} +}; + /* SuoWoSi/South-holding JS201D with sn6140 */ static const struct hda_pintbl cxt_pincfg_sws_js201d[] = { { 0x16, 0x03211040 }, /* hp out */ @@ -1006,6 +1013,10 @@ static const struct hda_fixup cxt_fixups[] = { .type = HDA_FIXUP_PINS, .v.pins = cxt_pincfg_sws_js201d, }, + [CXT_PINCFG_LENOVO_IDEAPAD_SLIM5_16AKP10] = { + .type = HDA_FIXUP_PINS, + .v.pins = cxt_pincfg_lenovo_ideapad_slim5_16akp10, + }, [CXT_PINCFG_TOP_SPEAKER] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -1114,6 +1125,7 @@ static const struct hda_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD), + SND_PCI_QUIRK(0x17aa, 0x38b6, "Lenovo IdeaPad Slim 5 16AKP10", CXT_PINCFG_LENOVO_IDEAPAD_SLIM5_16AKP10), SND_PCI_QUIRK(0x17aa, 0x3905, "Lenovo G50-30", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC), diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 5d09baa6d32e..d9e2384fc0ba 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6807,11 +6807,13 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x159c, "Acer Nitro 5 AN515-58", ALC287_FIXUP_ACER_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x160e, "Acer PT316-51S", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x161f, "Acer S40-54", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x1640, "Acer Aspire A315-44P", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x1679, "Acer Nitro 16 AN16-41", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x169a, "Acer Swift SFG16", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x171e, "Acer Nitro ANV15-51", ALC245_FIXUP_ACER_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x173a, "Acer Swift SFG14-73", ALC245_FIXUP_ACER_MICMUTE_LED), + SND_PCI_QUIRK(0x1025, 0x1758, "Acer Nitro ANV15-41", ALC245_FIXUP_ACER_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x1826, "Acer Helios ZPC", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), SND_PCI_QUIRK(0x1025, 0x182c, "Acer Helios ZPD", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), SND_PCI_QUIRK(0x1025, 0x1844, "Acer Helios ZPS", ALC287_FIXUP_PREDATOR_SPK_CS35L41_I2C_2), @@ -6986,6 +6988,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8537, "HP ProBook 440 G6", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8548, "HP EliteBook x360 830 G6", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x854a, "HP EliteBook 830 G6", ALC285_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x854d, "HP EliteBook 840 G6", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x856a, "HP Pavilion 15-cs1xxx", ALC295_FIXUP_HP_PAVILION_MUTE_LED_1B), SND_PCI_QUIRK(0x103c, 0x85c6, "HP Pavilion x360 Convertible 14-dy1xxx", ALC295_FIXUP_HP_MUTE_LED_COEFBIT11), SND_PCI_QUIRK(0x103c, 0x85de, "HP Envy x360 13-ar0xxx", ALC285_FIXUP_HP_ENVY_X360), @@ -7326,6 +7329,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8f0e, "HP ZBook X G2i 16W", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8f2d, "HP Auster 14", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x103c, 0x8f2e, "HP Auster 14", ALC287_FIXUP_CS35L41_I2C_2), + SND_PCI_QUIRK(0x103c, 0x8f37, "HP EliteBook 6 G2i", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8f38, "HP EliteBook 6 G2i", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8f39, "HP EliteBook 6 G2i", ALC236_FIXUP_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8f3a, "HP EliteBook 6 G2i", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8f3c, "HP EliteBook 6 G2a", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8f3d, "HP EliteBook 6 G2a", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x8f40, "HP ZBook 8 G2a 14", ALC245_FIXUP_HP_TAS2781_I2C_MUTE_LED), @@ -7597,6 +7604,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1558, 0x51b3, "Clevo NS70AU", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x5630, "Clevo NP50RNJS", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x5700, "Clevo X560WN[RST]", ALC256_FIXUP_SYSTEM76_MIC_NO_PRESENCE), + SND_PCI_QUIRK(0x1558, 0x6480, "Clevo V6xxAW", ALC245_FIXUP_CLEVO_NOISY_MIC), SND_PCI_QUIRK(0x1558, 0x70a1, "Clevo NB70T[HJK]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x70b3, "Clevo NK70SB", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1558, 0x70f2, "Clevo NH79EPY", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE), @@ -7753,11 +7761,13 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x3865, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3866, "Lenovo 13X", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3869, "Lenovo Yoga7 14IAL7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), + HDA_CODEC_QUIRK(0x17aa, 0x386a, "Lenovo Yoga 7 16IAP7", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN), HDA_CODEC_QUIRK(0x17aa, 0x386e, "Legion Y9000X 2022 IAH7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x386e, "Yoga Pro 7 14ARP8", ALC285_FIXUP_SPEAKER2_TO_DAC1), HDA_CODEC_QUIRK(0x17aa, 0x38a8, "Legion Pro 7 16ARX8H", ALC287_FIXUP_TAS2781_I2C), /* this must match before PCI SSID 17aa:386f below */ SND_PCI_QUIRK(0x17aa, 0x386f, "Legion Pro 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3870, "Lenovo Yoga 7 14ARB7", ALC287_FIXUP_YOGA7_14ARB7_I2C), + SND_PCI_QUIRK(0x17aa, 0x3874, "Legion 7i 16IAX7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3877, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x3878, "Lenovo Legion 7 Slim 16ARHA7", ALC287_FIXUP_CS35L41_I2C_2), SND_PCI_QUIRK(0x17aa, 0x387d, "Yoga S780-16 pro Quad AAC", ALC287_FIXUP_TAS2781_I2C), diff --git a/sound/hda/codecs/side-codecs/cs35l41_hda_property.c b/sound/hda/codecs/side-codecs/cs35l41_hda_property.c index 732ae534db36..416d7bf3e289 100644 --- a/sound/hda/codecs/side-codecs/cs35l41_hda_property.c +++ b/sound/hda/codecs/side-codecs/cs35l41_hda_property.c @@ -128,6 +128,7 @@ static const struct cs35l41_config cs35l41_config_table[] = { { "17AA3866", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, { "17AA386E", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, { "17AA386F", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, + { "17AA3874", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, { "17AA3877", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, { "17AA3878", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, -1, -1, 0, 0, 0 }, { "17AA38A9", 2, EXTERNAL, { CS35L41_LEFT, CS35L41_RIGHT, 0, 0 }, 0, 2, -1, 0, 0, 0 }, @@ -554,6 +555,7 @@ static const struct cs35l41_prop_model cs35l41_prop_model_table[] = { { "CSC3551", "17AA3866", generic_dsd_config }, { "CSC3551", "17AA386E", generic_dsd_config }, { "CSC3551", "17AA386F", generic_dsd_config }, + { "CSC3551", "17AA3874", generic_dsd_config }, { "CSC3551", "17AA3877", generic_dsd_config }, { "CSC3551", "17AA3878", generic_dsd_config }, { "CSC3551", "17AA38A9", generic_dsd_config }, diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c index 9023f3444d20..b8749e0131ad 100644 --- a/sound/pci/emu10k1/emupcm.c +++ b/sound/pci/emu10k1/emupcm.c @@ -1181,19 +1181,17 @@ static int snd_emu10k1_playback_open(struct snd_pcm_substream *substream) runtime->private_free = snd_emu10k1_pcm_free_substream; runtime->hw = snd_emu10k1_playback; err = snd_emu10k1_playback_set_constraints(runtime); - if (err < 0) { - kfree(epcm); - return err; - } + if (err < 0) + goto free_epcm; + if (emu->card_capabilities->emu_model) sample_rate = emu->emu1010.word_clock; else sample_rate = 48000; err = snd_pcm_hw_rule_noresample(runtime, sample_rate); - if (err < 0) { - kfree(epcm); - return err; - } + if (err < 0) + goto free_epcm; + mix = &emu->pcm_mixer[substream->number]; for (i = 0; i < 8; i++) mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i; @@ -1204,6 +1202,10 @@ static int snd_emu10k1_playback_open(struct snd_pcm_substream *substream) mix->epcm = epcm; snd_emu10k1_pcm_mixer_notify(emu, substream->number, 1); return 0; + +free_epcm: + kfree(epcm); + return err; } static int snd_emu10k1_playback_close(struct snd_pcm_substream *substream) diff --git a/sound/sh/aica.c b/sound/sh/aica.c index 9438c3a68ee9..8196e1bf0416 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -564,10 +564,9 @@ static int snd_aica_probe(struct platform_device *devptr) return -ENOMEM; err = snd_card_new(&devptr->dev, index, SND_AICA_DRIVER, THIS_MODULE, 0, &dreamcastcard->card); - if (unlikely(err < 0)) { - kfree(dreamcastcard); - return err; - } + if (unlikely(err < 0)) + goto free_card; + strscpy(dreamcastcard->card->driver, "snd_aica"); strscpy(dreamcastcard->card->shortname, SND_AICA_DRIVER); strscpy(dreamcastcard->card->longname, @@ -593,6 +592,7 @@ static int snd_aica_probe(struct platform_device *devptr) return 0; freedreamcast: snd_card_free(dreamcastcard->card); +free_card: kfree(dreamcastcard); return err; } diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c index 18b5ff75feec..2c7e33135911 100644 --- a/sound/soc/codecs/cs530x.c +++ b/sound/soc/codecs/cs530x.c @@ -1093,6 +1093,29 @@ static int cs530x_component_probe(struct snd_soc_component *component) return 0; } +static bool cs530x_mclk_freq_is_valid(struct cs530x_priv *cs530x, + unsigned int freq) +{ + /* + * All these chips support 48 kHz- and 44.1 kHz-related sample rates, + * but they differ in what MCLK frequency is required for achieving + * the sample rate. + */ + switch (cs530x->devtype) { + case CS4282: + case CS4302: + case CS4304: + case CS4308: + return freq == 49152000 || freq == 45158400; + case CS5302: + case CS5304: + case CS5308: + return freq == 24576000 || freq == 22579200; + } + + return false; +} + static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id, int source, unsigned int freq, int dir) { @@ -1101,11 +1124,7 @@ static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id, switch (source) { case CS530X_SYSCLK_SRC_MCLK: - switch (freq) { - case CS530X_SYSCLK_REF_45_1MHZ: - case CS530X_SYSCLK_REF_49_1MHZ: - break; - default: + if (!cs530x_mclk_freq_is_valid(cs530x, freq)) { dev_err(component->dev, "Invalid MCLK source rate %d\n", freq); return -EINVAL; } diff --git a/sound/soc/codecs/cs530x.h b/sound/soc/codecs/cs530x.h index 1e2f6a7a589c..18aa4dfd0c86 100644 --- a/sound/soc/codecs/cs530x.h +++ b/sound/soc/codecs/cs530x.h @@ -200,12 +200,6 @@ /* IN_VOL_CTL5 and OUT_VOL_CTL5 */ #define CS530X_INOUT_VU BIT(0) -/* MCLK Reference Source Frequency */ -/* 41KHz related */ -#define CS530X_SYSCLK_REF_45_1MHZ 45158400 -/* 48KHz related */ -#define CS530X_SYSCLK_REF_49_1MHZ 49152000 - /* System Clock Source */ #define CS530X_SYSCLK_SRC_MCLK 0 #define CS530X_SYSCLK_SRC_PLL 1 diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c index 670e918b56a4..8db81d574624 100644 --- a/sound/soc/codecs/es9356.c +++ b/sound/soc/codecs/es9356.c @@ -1111,8 +1111,10 @@ static int es9356_sdca_dev_resume(struct device *dev) es9356->disable_irq = false; ret = sdw_slave_wait_for_init(slave, es9356_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(es9356->regmap, false); regcache_sync(es9356->regmap); diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index 6829fa07c9ec..7a42052dc051 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -272,8 +272,10 @@ static int max98373_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, MAX98373_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(max98373->regmap, false); regcache_sync(max98373->regmap); diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index fdef98ce52f1..fe3b5011fa16 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -633,7 +633,7 @@ static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream, struct regmap *regmap = pcm512x->regmap; if (IS_ERR(pcm512x->sclk)) { - dev_info(dev, "No SCLK, using BCLK: %ld\n", + dev_info_once(dev, "No SCLK, using BCLK: %ld\n", PTR_ERR(pcm512x->sclk)); /* Disable reporting of missing SCLK as an error */ diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c index d62e8a253676..91d3d43cd998 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.c +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -779,8 +779,10 @@ static int rt1017_sdca_dev_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, RT1017_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt1017->regmap, false); regcache_sync(rt1017->regmap); diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index 39e06a3a7560..60e5040b6dd9 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -774,8 +774,10 @@ static int rt1308_dev_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, RT1308_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt1308->regmap, false); regcache_sync_region(rt1308->regmap, 0xc000, 0xcfff); diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 1828fd9d5af6..5e8eda6a5f7f 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -751,8 +751,10 @@ static int rt1316_dev_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, RT1316_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt1316->regmap, false); regcache_sync(rt1316->regmap); diff --git a/sound/soc/codecs/rt5575-spi.c b/sound/soc/codecs/rt5575-spi.c index 9a349965435b..d5b3a57c8866 100644 --- a/sound/soc/codecs/rt5575-spi.c +++ b/sound/soc/codecs/rt5575-spi.c @@ -17,7 +17,7 @@ struct rt5575_spi_burst_write { u8 cmd; - u32 addr; + __le32 addr; u8 data[RT5575_SPI_BUF_LEN]; u8 dummy; } __packed; diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 8a9af260e5f7..e9819653b30d 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -83,6 +83,8 @@ static const struct reg_sequence rt5650_init_list[] = { {RT5645_PWR_ANLG1, 0x02}, {RT5645_IL_CMD3, 0x6728}, {RT5645_PR_BASE + 0x3a, 0x0000}, + {RT5645_CLSD_OUT_CTRL1, 0x4059}, + {RT5645_GEN_CTRL3, 0x0200}, }; static const struct reg_default rt5645_reg[] = { @@ -1855,13 +1857,9 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w, RT5645_PWR_CLS_D_L, RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | RT5645_PWR_CLS_D_L); - snd_soc_component_update_bits(component, RT5645_GEN_CTRL3, - RT5645_DET_CLK_MASK, RT5645_DET_CLK_MODE1); break; case SND_SOC_DAPM_PRE_PMD: - snd_soc_component_update_bits(component, RT5645_GEN_CTRL3, - RT5645_DET_CLK_MASK, RT5645_DET_CLK_DIS); snd_soc_component_write(component, RT5645_EQ_CTRL2, 0); snd_soc_component_update_bits(component, RT5645_PWR_DIG1, RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index bef74b29fd54..a5bfe9861b8b 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -118,6 +118,7 @@ #define RT5645_A_JD_CTRL1 0x94 #define RT5645_VAD_CTRL4 0x9d #define RT5645_CLSD_OUT_CTRL 0xa0 +#define RT5645_CLSD_OUT_CTRL1 0xa1 /* Function - Digital */ #define RT5645_ADC_EQ_CTRL1 0xae #define RT5645_ADC_EQ_CTRL2 0xaf diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index ec2a35a0cacd..dec8c2147d68 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -769,8 +769,10 @@ static int rt5682_dev_resume(struct device *dev) } ret = sdw_slave_wait_for_init(slave, RT5682_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt5682->sdw_regmap, false); regcache_cache_only(rt5682->regmap, false); diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 30fcca210f05..6bc636c86f42 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -528,8 +528,10 @@ static int rt700_dev_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, RT700_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt700->regmap, false); regcache_sync_region(rt700->regmap, 0x3000, 0x8fff); diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index a8164fc3979a..461315844ba9 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -454,8 +454,10 @@ static int rt711_sdca_dev_resume(struct device *dev) } ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt711->regmap, false); regcache_sync(rt711->regmap); diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 4c5c2f5ba5ed..8b7d50a80ff9 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -911,8 +911,10 @@ static int rt712_sdca_dmic_dev_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt712->regmap, false); regcache_sync(rt712->regmap); diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 581732180473..2787524c796e 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -467,8 +467,10 @@ static int rt712_sdca_dev_resume(struct device *dev) } ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt712->regmap, false); regcache_sync(rt712->regmap); diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c index 4b9815b5628d..fabd21bbbe5b 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.c +++ b/sound/soc/codecs/rt715-sdca-sdw.c @@ -230,8 +230,10 @@ static int rt715_dev_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, RT715_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt715->regmap, false); regcache_sync_region(rt715->regmap, diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index 7f83a8f1a06e..a4a3945522e8 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -507,8 +507,10 @@ static int rt715_dev_resume(struct device *dev) return 0; ret = sdw_slave_wait_for_init(slave, RT715_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt715->regmap, false); regcache_sync_region(rt715->regmap, 0x3000, 0x8fff); diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c index 58606209316a..02df04a0ddad 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.c +++ b/sound/soc/codecs/rt721-sdca-sdw.c @@ -505,8 +505,10 @@ static int rt721_sdca_dev_resume(struct device *dev) } ret = sdw_slave_wait_for_init(slave, RT721_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt721->regmap, false); regcache_sync(rt721->regmap); diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index 0f76492ff915..284900933ebf 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -552,8 +552,10 @@ static int rt722_sdca_dev_resume(struct device *dev) } ret = sdw_slave_wait_for_init(slave, RT722_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(rt722->regmap, false); regcache_sync(rt722->regmap); diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c index bb12cfb6da12..ace06f5ab58c 100644 --- a/sound/soc/codecs/tac5xx2-sdw.c +++ b/sound/soc/codecs/tac5xx2-sdw.c @@ -1445,8 +1445,10 @@ static s32 tac5xx2_sdca_dev_resume(struct device *dev) } ret = sdw_slave_wait_for_init(slave, TAC5XX2_PROBE_TIMEOUT_MS); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(tac_dev->regmap, false); regcache_mark_dirty(tac_dev->regmap); diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 7d70e7e3f24f..3d0b116544cc 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -100,6 +100,8 @@ struct tas2783_prv { wait_queue_head_t fw_wait; bool fw_dl_task_done; bool fw_dl_success; + /* use fallback fw name */ + bool fw_use_fallback; }; static const struct reg_default tas2783_reg_default[] = { @@ -740,11 +742,19 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context) goto out; } + /* firmware binary not found*/ if (!fmw || !fmw->data) { - /* firmware binary not found*/ - dev_err(tas_dev->dev, - "Failed to read fw binary %s\n", - tas_dev->rca_binaryname); + if (!tas_dev->fw_use_fallback) { + tas_dev->fw_use_fallback = true; + dev_info(tas_dev->dev, + "Failed to read preferred fw binary: %s, attempting fallback binary load\n", + tas_dev->rca_binaryname); + } else { + dev_err(tas_dev->dev, + "Failed to read fallback fw binary %s\n", + tas_dev->rca_binaryname); + } + ret = -EINVAL; goto out; } @@ -1083,8 +1093,10 @@ static s32 tas2783_sdca_dev_resume(struct device *dev) int ret; ret = sdw_slave_wait_for_init(slave, TAS2783_PROBE_TIMEOUT); - if (ret) + if (ret) { + sdw_show_ping_status(slave->bus, true); return ret; + } regcache_cache_only(tas_dev->regmap, false); regcache_sync(tas_dev->regmap); @@ -1103,13 +1115,16 @@ static void tas_generate_fw_name(struct sdw_slave *slave, char *name, size_t siz bool pci_found = false; #if IS_ENABLED(CONFIG_PCI) struct device *dev = &slave->dev; + struct tas2783_prv *tas_dev = dev_get_drvdata(&slave->dev); struct pci_dev *pci = NULL; + const char *fw_uid_prefix = tas_dev->fw_use_fallback ? "" : "0x"; for (; dev; dev = dev->parent) { if (dev->bus == &pci_bus_type) { pci = to_pci_dev(dev); - scnprintf(name, size, "%04X-%1X-%1X.bin", - pci->subsystem_device, bus->link_id, unique_id); + scnprintf(name, size, "%04X-%1X-%s%1X.bin", + pci->subsystem_device, bus->link_id, + fw_uid_prefix, unique_id); pci_found = true; break; } @@ -1121,28 +1136,15 @@ static void tas_generate_fw_name(struct sdw_slave *slave, char *name, size_t siz bus->link_id, unique_id); } -static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) +static s32 tas_fw_load(struct tas2783_prv *tas_dev, struct sdw_slave *slave) { - struct tas2783_prv *tas_dev = dev_get_drvdata(dev); s32 ret; u8 unique_id = tas_dev->sdw_peripheral->id.unique_id; - if (tas_dev->hw_init) - return 0; - - tas_dev->fw_dl_task_done = false; - tas_dev->fw_dl_success = false; - - ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1); - if (ret) { - dev_err(dev, "sw reset failed, err=%d", ret); - return ret; - } - usleep_range(2000, 2200); - tas_generate_fw_name(slave, tas_dev->rca_binaryname, sizeof(tas_dev->rca_binaryname)); + tas_dev->fw_dl_task_done = false; ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, tas_dev->rca_binaryname, tas_dev->dev, GFP_KERNEL, tas_dev, tas2783_fw_ready); @@ -1157,8 +1159,35 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) msecs_to_jiffies(TIMEOUT_FW_DL_MS)); if (!ret) { dev_err(tas_dev->dev, "fw request, wait_event timeout\n"); - ret = -EAGAIN; - } else { + return -EAGAIN; + } + + return 0; +} + +static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct tas2783_prv *tas_dev = dev_get_drvdata(dev); + s32 ret; + + if (tas_dev->hw_init) + return 0; + + tas_dev->fw_dl_success = false; + + ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1); + if (ret) { + dev_err(dev, "sw reset failed, err=%d", ret); + return ret; + } + usleep_range(2000, 2200); + + tas_dev->fw_use_fallback = false; + ret = tas_fw_load(tas_dev, slave); + if (!ret && tas_dev->fw_use_fallback) + ret = tas_fw_load(tas_dev, slave); + + if (!ret) { if (tas_dev->sa_func_data) ret = sdca_regmap_write_init(dev, tas_dev->regmap, tas_dev->sa_func_data); diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index b12c1952823b..b38393a8130f 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1049,11 +1049,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + static const u8 dual_rate_q[] = {4, 8, 9, 12, 16}; struct snd_soc_component *component = dai->component; struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0; u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1; u16 d, pll_d = 1; + bool dual_rate; int clk; int width = aic3x->slot_width; @@ -1079,14 +1081,25 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, /* Fsref can be 44100 or 48000 */ fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000; + dual_rate = params_rate(params) >= 64000; /* Try to find a value for Q which allows us to bypass the PLL and * generate CODEC_CLK directly. */ - for (pll_q = 2; pll_q < 18; pll_q++) - if (aic3x->sysclk / (128 * pll_q) == fsref) { - bypass_pll = 1; - break; + if (dual_rate) { + for (int i = 0; i < ARRAY_SIZE(dual_rate_q); i++) { + pll_q = dual_rate_q[i]; + if (aic3x->sysclk / (128 * pll_q) == fsref) { + bypass_pll = 1; + break; + } } + } else { + for (pll_q = 2; pll_q < 18; pll_q++) + if (aic3x->sysclk / (128 * pll_q) == fsref) { + bypass_pll = 1; + break; + } + } if (bypass_pll) { pll_q &= 0xf; @@ -1106,13 +1119,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, * right DAC to right channel input */ data = (LDAC2LCH | RDAC2RCH); data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000; - if (params_rate(params) >= 64000) + if (dual_rate) data |= DUAL_RATE_MODE; snd_soc_component_write(component, AIC3X_CODEC_DATAPATH_REG, data); /* codec sample rate select */ data = (fsref * 20) / params_rate(params); - if (params_rate(params) < 64000) + if (!dual_rate) data /= 2; data /= 5; data -= 2; diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c index 5aa96af994c4..38f2b7c63133 100644 --- a/sound/soc/fsl/fsl_asrc_dma.c +++ b/sound/soc/fsl/fsl_asrc_dma.c @@ -288,6 +288,26 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component, config_be.dst_addr_width = buswidth; config_be.dst_maxburst = dma_params_be->maxburst; + /* + * For eDMA, the back-end may report a maxburst size that is not evenly + * divisible by the channel count. This causes the DMA transfer length + * to misalign with the FIFO boundary, resulting in wrong data and + * audible noise. Align maxburst to the nearest valid boundary: + * - If maxburst >= channel count, override to the channel count so + * each transfer equals exactly one audio frame. + * - If maxburst < channel count, override to 1 to avoid partial-frame + * transfers. + */ + if (asrc->use_edma && (dma_params_be->maxburst % params_channels(params))) { + if (dma_params_be->maxburst >= params_channels(params)) { + config_be.src_maxburst = params_channels(params); + config_be.dst_maxburst = params_channels(params); + } else { + config_be.src_maxburst = 1; + config_be.dst_maxburst = 1; + } + } + memset(&audio_config, 0, sizeof(audio_config)); config_be.peripheral_config = &audio_config; config_be.peripheral_size = sizeof(audio_config); diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c index 6894bb936cfd..0202ed0ee78e 100644 --- a/sound/soc/generic/audio-graph-card2.c +++ b/sound/soc/generic/audio-graph-card2.c @@ -778,18 +778,6 @@ static void graph_link_init(struct simple_util_priv *priv, graph_parse_daifmt(ports_cpu, &daifmt); graph_parse_daifmt(ports_codec, &daifmt); graph_parse_daifmt(lnk, &daifmt); - if (daifmt) { - struct device *dev = simple_priv_to_dev(priv); - - /* - * Recommend to use Auto Select by using .auto_selectable_formats. - * linux/sound/soc/renesas/rcar/core.c can be good sample for it. - * - * One note is that Audio Graph Card2 still keeps compatible to set - * DAI format via DT. - */ - dev_warn_once(dev, "use .auto_selectable_formats on each corresponding CPU/Codec driver"); - } graph_util_parse_link_direction(lnk, &playback_only, &capture_only); graph_util_parse_link_direction(ports_cpu, &playback_only, &capture_only); diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 2e5b25b8d00f..641d6d243229 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -587,6 +587,10 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) token = hdr->token & APM_WRITE_TOKEN_MASK; done = data->payload; + if (!graph->rx_data.buf) { + mutex_unlock(&graph->lock); + break; + } phys = graph->rx_data.buf[token].phys; mutex_unlock(&graph->lock); /* token numbering starts at 0 */ @@ -609,6 +613,10 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op) client_event = APM_CLIENT_EVENT_DATA_READ_DONE; mutex_lock(&graph->lock); rd_done = data->payload; + if (!graph->tx_data.buf) { + mutex_unlock(&graph->lock); + break; + } phys = graph->tx_data.buf[hdr->token].phys; mutex_unlock(&graph->lock); /* token numbering starts at 0 */ diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c index a195e96fed0a..585e89f61f0d 100644 --- a/sound/soc/rockchip/rockchip_sai.c +++ b/sound/soc/rockchip/rockchip_sai.c @@ -11,6 +11,7 @@ #include <linux/delay.h> #include <linux/of_device.h> #include <linux/clk.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/reset.h> @@ -18,6 +19,7 @@ #include <sound/pcm_params.h> #include <sound/dmaengine_pcm.h> #include <sound/tlv.h> + #include "rockchip_sai.h" #define DRV_NAME "rockchip-sai" @@ -215,12 +217,14 @@ wait_for_idle: static int rockchip_sai_runtime_suspend(struct device *dev) { struct rk_sai_dev *sai = dev_get_drvdata(dev); + unsigned long flags; rockchip_sai_fsync_lost_detect(sai, 0); rockchip_sai_fsync_err_detect(sai, 0); - scoped_guard(spinlock_irqsave, &sai->xfer_lock) - rockchip_sai_xfer_clk_stop_and_wait(sai, NULL); + spin_lock_irqsave(&sai->xfer_lock, flags); + rockchip_sai_xfer_clk_stop_and_wait(sai, NULL); + spin_unlock_irqrestore(&sai->xfer_lock, flags); regcache_cache_only(sai->regmap, true); /* @@ -480,6 +484,7 @@ static int rockchip_sai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); unsigned int mask = 0, val = 0; unsigned int clk_gates; + unsigned long flags; int ret = 0; pm_runtime_get_sync(dai->dev); @@ -495,56 +500,56 @@ static int rockchip_sai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) sai->is_master_mode = false; break; default: - pm_runtime_put(dai->dev); - return -EINVAL; + ret = -EINVAL; + goto err_pm_put; } - scoped_guard(spinlock_irqsave, &sai->xfer_lock) { - rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates); - if (sai->initialized) { - if (sai->has_capture && sai->has_playback) - rockchip_sai_xfer_stop(sai, -1); - else if (sai->has_capture) - rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_CAPTURE); - else - rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_PLAYBACK); - } else { - rockchip_sai_clear(sai, 0); - sai->initialized = true; - } + spin_lock_irqsave(&sai->xfer_lock, flags); + rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates); + if (sai->initialized) { + if (sai->has_capture && sai->has_playback) + rockchip_sai_xfer_stop(sai, -1); + else if (sai->has_capture) + rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_CAPTURE); + else + rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_PLAYBACK); + } else { + rockchip_sai_clear(sai, 0); + sai->initialized = true; + } - regmap_update_bits(sai->regmap, SAI_CKR, mask, val); - - mask = SAI_CKR_CKP_MASK | SAI_CKR_FSP_MASK; - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_NORMAL; - break; - case SND_SOC_DAIFMT_NB_IF: - val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_INVERTED; - break; - case SND_SOC_DAIFMT_IB_NF: - val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_NORMAL; - break; - case SND_SOC_DAIFMT_IB_IF: - val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_INVERTED; - break; - default: - ret = -EINVAL; - break; - } + regmap_update_bits(sai->regmap, SAI_CKR, mask, val); - if (ret == 0) { - regmap_update_bits(sai->regmap, SAI_CKR, mask, val); - rockchip_sai_fmt_create(sai, fmt); - } - - if (clk_gates) - regmap_update_bits(sai->regmap, SAI_XFER, - SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK, - clk_gates); + mask = SAI_CKR_CKP_MASK | SAI_CKR_FSP_MASK; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_NORMAL; + break; + case SND_SOC_DAIFMT_NB_IF: + val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_INVERTED; + break; + case SND_SOC_DAIFMT_IB_NF: + val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_NORMAL; + break; + case SND_SOC_DAIFMT_IB_IF: + val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_INVERTED; + break; + default: + ret = -EINVAL; + goto err_xfer_unlock; } + regmap_update_bits(sai->regmap, SAI_CKR, mask, val); + + rockchip_sai_fmt_create(sai, fmt); + +err_xfer_unlock: + if (clk_gates) + regmap_update_bits(sai->regmap, SAI_XFER, + SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK, + clk_gates); + spin_unlock_irqrestore(&sai->xfer_lock, flags); +err_pm_put: pm_runtime_put(dai->dev); return ret; @@ -560,6 +565,7 @@ static int rockchip_sai_hw_params(struct snd_pcm_substream *substream, unsigned int ch_per_lane, slot_width; unsigned int val, fscr, reg; unsigned int lanes, req_lanes; + unsigned long flags; int ret = 0; if (!rockchip_sai_stream_valid(substream, dai)) @@ -586,8 +592,8 @@ static int rockchip_sai_hw_params(struct snd_pcm_substream *substream, dev_err(sai->dev, "not enough lanes (%d) for requested number of %s channels (%d)\n", lanes, reg == SAI_TXCR ? "playback" : "capture", params_channels(params)); - pm_runtime_put(sai->dev); - return -EINVAL; + ret = -EINVAL; + goto err_pm_put; } else { lanes = req_lanes; } @@ -613,88 +619,84 @@ static int rockchip_sai_hw_params(struct snd_pcm_substream *substream, val = SAI_XCR_VDW(32); break; default: - pm_runtime_put(sai->dev); - return -EINVAL; + ret = -EINVAL; + goto err_pm_put; } val |= SAI_XCR_CSR(lanes); - scoped_guard(spinlock_irqsave, &sai->xfer_lock) { + spin_lock_irqsave(&sai->xfer_lock, flags); + + regmap_update_bits(sai->regmap, reg, SAI_XCR_VDW_MASK | SAI_XCR_CSR_MASK, val); - regmap_update_bits(sai->regmap, reg, SAI_XCR_VDW_MASK | SAI_XCR_CSR_MASK, val); + if (!sai->is_tdm) + regmap_update_bits(sai->regmap, reg, SAI_XCR_SBW_MASK, + SAI_XCR_SBW(params_physical_width(params))); - if (!sai->is_tdm) - regmap_update_bits(sai->regmap, reg, SAI_XCR_SBW_MASK, - SAI_XCR_SBW(params_physical_width(params))); + regmap_read(sai->regmap, reg, &val); - regmap_read(sai->regmap, reg, &val); + slot_width = SAI_XCR_SBW_V(val); + ch_per_lane = params_channels(params) / lanes; - slot_width = SAI_XCR_SBW_V(val); - ch_per_lane = params_channels(params) / lanes; + regmap_update_bits(sai->regmap, reg, SAI_XCR_SNB_MASK, + SAI_XCR_SNB(ch_per_lane)); - regmap_update_bits(sai->regmap, reg, SAI_XCR_SNB_MASK, - SAI_XCR_SNB(ch_per_lane)); + fscr = SAI_FSCR_FW(sai->fw_ratio * slot_width * ch_per_lane); - fscr = SAI_FSCR_FW(sai->fw_ratio * slot_width * ch_per_lane); + switch (sai->fpw) { + case FPW_ONE_BCLK_WIDTH: + fscr |= SAI_FSCR_FPW(1); + break; + case FPW_ONE_SLOT_WIDTH: + fscr |= SAI_FSCR_FPW(slot_width); + break; + case FPW_HALF_FRAME_WIDTH: + fscr |= SAI_FSCR_FPW(sai->fw_ratio * slot_width * ch_per_lane / 2); + break; + default: + dev_err(sai->dev, "Invalid Frame Pulse Width %d\n", sai->fpw); + ret = -EINVAL; + goto err_xfer_unlock; + } - switch (sai->fpw) { - case FPW_ONE_BCLK_WIDTH: - fscr |= SAI_FSCR_FPW(1); - break; - case FPW_ONE_SLOT_WIDTH: - fscr |= SAI_FSCR_FPW(slot_width); - break; - case FPW_HALF_FRAME_WIDTH: - fscr |= SAI_FSCR_FPW(sai->fw_ratio * slot_width * ch_per_lane / 2); - break; - default: - dev_err(sai->dev, "Invalid Frame Pulse Width %d\n", sai->fpw); + regmap_update_bits(sai->regmap, SAI_FSCR, + SAI_FSCR_FW_MASK | SAI_FSCR_FPW_MASK, fscr); + + if (sai->is_master_mode) { + bclk_rate = sai->fw_ratio * slot_width * ch_per_lane * params_rate(params); + ret = clk_set_rate(sai->mclk, sai->mclk_rate); + if (ret) { + dev_err(sai->dev, "Failed to set mclk to %u: %pe\n", + sai->mclk_rate, ERR_PTR(ret)); + goto err_xfer_unlock; + } + + mclk_rate = clk_get_rate(sai->mclk); + if (mclk_rate < bclk_rate) { + dev_err(sai->dev, "Mismatch mclk: %u, at least %u\n", + mclk_rate, bclk_rate); ret = -EINVAL; - break; + goto err_xfer_unlock; } - if (ret == 0) { - regmap_update_bits(sai->regmap, SAI_FSCR, - SAI_FSCR_FW_MASK | SAI_FSCR_FPW_MASK, fscr); - - if (sai->is_master_mode) { - bclk_rate = sai->fw_ratio * slot_width * - ch_per_lane * params_rate(params); - ret = clk_set_rate(sai->mclk, sai->mclk_rate); - if (ret) - dev_err(sai->dev, "Failed to set mclk to %u: %pe\n", - sai->mclk_rate, ERR_PTR(ret)); - else { - mclk_rate = clk_get_rate(sai->mclk); - if (mclk_rate < bclk_rate) { - dev_err(sai->dev, "Mismatch mclk: %u, at least %u\n", - mclk_rate, bclk_rate); - ret = -EINVAL; - } else { - - div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate); - mclk_req_rate = bclk_rate * div_bclk; - - if (mclk_rate < - mclk_req_rate - CLK_SHIFT_RATE_HZ_MAX || - mclk_rate > - mclk_req_rate + CLK_SHIFT_RATE_HZ_MAX) { - dev_err(sai->dev, - "Mismatch mclk: %u, expected %u (+/- %dHz)\n", - mclk_rate, mclk_req_rate, - CLK_SHIFT_RATE_HZ_MAX); - ret = -EINVAL; - } else - regmap_update_bits(sai->regmap, - SAI_CKR, - SAI_CKR_MDIV_MASK, - SAI_CKR_MDIV(div_bclk)); - } - } - } + div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate); + mclk_req_rate = bclk_rate * div_bclk; + + if (mclk_rate < mclk_req_rate - CLK_SHIFT_RATE_HZ_MAX || + mclk_rate > mclk_req_rate + CLK_SHIFT_RATE_HZ_MAX) { + dev_err(sai->dev, "Mismatch mclk: %u, expected %u (+/- %dHz)\n", + mclk_rate, mclk_req_rate, CLK_SHIFT_RATE_HZ_MAX); + ret = -EINVAL; + goto err_xfer_unlock; } + + regmap_update_bits(sai->regmap, SAI_CKR, SAI_CKR_MDIV_MASK, + SAI_CKR_MDIV(div_bclk)); } +err_xfer_unlock: + spin_unlock_irqrestore(&sai->xfer_lock, flags); +err_pm_put: pm_runtime_put(sai->dev); return ret; @@ -704,6 +706,7 @@ static int rockchip_sai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + unsigned long flags; if (!rockchip_sai_stream_valid(substream, dai)) return 0; @@ -724,12 +727,13 @@ static int rockchip_sai_prepare(struct snd_pcm_substream *substream, * udelay falls short. */ udelay(20); - scoped_guard(spinlock_irqsave, &sai->xfer_lock) - regmap_update_bits(sai->regmap, SAI_XFER, - SAI_XFER_CLK_MASK | - SAI_XFER_FSS_MASK, - SAI_XFER_CLK_EN | - SAI_XFER_FSS_EN); + spin_lock_irqsave(&sai->xfer_lock, flags); + regmap_update_bits(sai->regmap, SAI_XFER, + SAI_XFER_CLK_MASK | + SAI_XFER_FSS_MASK, + SAI_XFER_CLK_EN | + SAI_XFER_FSS_EN); + spin_unlock_irqrestore(&sai->xfer_lock, flags); } rockchip_sai_fsync_lost_detect(sai, 1); @@ -912,6 +916,7 @@ static int rockchip_sai_set_tdm_slot(struct snd_soc_dai *dai, int slots, int slot_width) { struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai); + unsigned long flags; unsigned int clk_gates; int sw = slot_width; @@ -927,16 +932,16 @@ static int rockchip_sai_set_tdm_slot(struct snd_soc_dai *dai, return -EINVAL; pm_runtime_get_sync(dai->dev); - scoped_guard(spinlock_irqsave, &sai->xfer_lock) { - rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates); - regmap_update_bits(sai->regmap, SAI_TXCR, SAI_XCR_SBW_MASK, - SAI_XCR_SBW(sw)); - regmap_update_bits(sai->regmap, SAI_RXCR, SAI_XCR_SBW_MASK, - SAI_XCR_SBW(sw)); - regmap_update_bits(sai->regmap, SAI_XFER, - SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK, - clk_gates); - } + spin_lock_irqsave(&sai->xfer_lock, flags); + rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates); + regmap_update_bits(sai->regmap, SAI_TXCR, SAI_XCR_SBW_MASK, + SAI_XCR_SBW(sw)); + regmap_update_bits(sai->regmap, SAI_RXCR, SAI_XCR_SBW_MASK, + SAI_XCR_SBW(sw)); + regmap_update_bits(sai->regmap, SAI_XFER, + SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK, + clk_gates); + spin_unlock_irqrestore(&sai->xfer_lock, flags); pm_runtime_put(dai->dev); return 0; diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c index e76afa396b0a..b4dedba719dc 100644 --- a/sound/soc/sdca/sdca_asoc.c +++ b/sound/soc/sdca/sdca_asoc.c @@ -160,6 +160,9 @@ static int ge_put_enum_double(struct snd_kcontrol *kcontrol, unsigned int reg = e->reg; int ret; + if (item[0] >= e->items) + return -EINVAL; + reg &= ~SDW_SDCA_CTL_CSEL(0x3F); reg |= SDW_SDCA_CTL_CSEL(SDCA_CTL_GE_DETECTED_MODE); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 86b6c752a56b..7817beea5b3b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1980,19 +1980,15 @@ static void soc_cleanup_card_resources(struct snd_soc_card *card) } } -static void snd_soc_remove_device_links(struct snd_soc_card *card, - struct snd_soc_component *stop_at) +static void snd_soc_remove_device_links(struct snd_soc_card *card) { struct snd_soc_component *component; for_each_card_components(card, component) { - if (card->dev == component->dev) - continue; - - device_link_remove(card->dev, component->dev); - - if (component == stop_at) - return; + if (component->card_device_link) { + device_link_del(component->card_device_link); + component->card_device_link = NULL; + } } } @@ -2001,7 +1997,7 @@ static void snd_soc_unbind_card(struct snd_soc_card *card) if (snd_soc_card_is_instantiated(card)) { card->instantiated = false; - snd_soc_remove_device_links(card, NULL); + snd_soc_remove_device_links(card); soc_cleanup_card_resources(card); } @@ -2012,7 +2008,6 @@ static int snd_soc_bind_card(struct snd_soc_card *card) struct snd_soc_pcm_runtime *rtd; struct snd_soc_component *component; struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card); - struct snd_soc_component *last_devlinked_component = NULL; int ret; snd_soc_card_mutex_lock_root(card); @@ -2141,19 +2136,21 @@ static int snd_soc_bind_card(struct snd_soc_card *card) * Add device_link from card to component so that system_suspend * will be done in the correct order. The card must suspend first * to stop audio activity before the components suspend. + * + * If a driver pair already have a link in the opposite direction + * they must manage their own suspend order. */ for_each_card_components(card, component) { if (card->dev == component->dev) continue; - if (!device_link_add(card->dev, component->dev, DL_FLAG_STATELESS)) { - dev_warn(card->dev, "Failed to create device link to %s\n", + component->card_device_link = device_link_add(card->dev, + component->dev, + DL_FLAG_STATELESS); + if (!component->card_device_link) { + dev_warn(card->dev, "Could not create device link to %s\n", dev_name(component->dev)); - ret = -EINVAL; - goto probe_end; } - - last_devlinked_component = component; } ret = snd_soc_card_late_probe(card); @@ -2185,9 +2182,7 @@ static int snd_soc_bind_card(struct snd_soc_card *card) probe_end: if (ret < 0) { - if (last_devlinked_component) - snd_soc_remove_device_links(card, last_devlinked_component); - + snd_soc_remove_device_links(card); soc_cleanup_card_resources(card); } diff --git a/sound/usb/caiaq/input.c b/sound/usb/caiaq/input.c index 5c70fdf61cc1..eabbf41fdfb2 100644 --- a/sound/usb/caiaq/input.c +++ b/sound/usb/caiaq/input.c @@ -237,12 +237,16 @@ static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *cdev, switch (cdev->chip.usb_id) { case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_AK1): + if (len < 2) + return; i = decode_erp(buf[0], buf[1]); input_report_abs(input_dev, ABS_X, i); input_sync(input_dev); break; case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): + if (len < 16) + return; i = decode_erp(buf[7], buf[5]); input_report_abs(input_dev, ABS_HAT0X, i); i = decode_erp(buf[12], buf[14]); @@ -263,6 +267,8 @@ static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *cdev, break; case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER): + if (len < 22) + return; /* 4 under the left screen */ input_report_abs(input_dev, ABS_HAT0X, decode_erp(buf[21], buf[20])); input_report_abs(input_dev, ABS_HAT0Y, decode_erp(buf[15], buf[14])); @@ -308,9 +314,13 @@ static void snd_caiaq_input_read_io(struct snd_usb_caiaqdev *cdev, switch (cdev->chip.usb_id) { case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER): case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_KORECONTROLLER2): + if (len < 5) + return; input_report_abs(cdev->input_dev, ABS_MISC, 255 - buf[4]); break; case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1): + if (len < 7) + return; /* rotary encoders */ input_report_abs(cdev->input_dev, ABS_X, buf[5] & 0xf); input_report_abs(cdev->input_dev, ABS_Y, buf[5] >> 4); @@ -330,7 +340,7 @@ static void snd_usb_caiaq_tks4_dispatch(struct snd_usb_caiaqdev *cdev, { struct device *dev = caiaqdev_to_dev(cdev); - while (len) { + while (len >= TKS4_MSGBLOCK_SIZE) { unsigned int i, block_id = (buf[0] << 8) | buf[1]; switch (block_id) { diff --git a/sound/usb/fcp.c b/sound/usb/fcp.c index ea746bdb36ff..6f5dcd35e1d4 100644 --- a/sound/usb/fcp.c +++ b/sound/usb/fcp.c @@ -1083,6 +1083,8 @@ static int fcp_find_fc_interface(struct usb_mixer_interface *mixer) if (desc->bInterfaceClass != 255) continue; + if (desc->bNumEndpoints < 1) + continue; epd = get_endpoint(intf->altsetting, 0); private->bInterfaceNumber = desc->bInterfaceNumber; diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index 04aeb9052f13..3ec633291772 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -470,6 +470,11 @@ static int create_midi2_endpoint(struct snd_usb_midi2_interface *umidi, static void free_midi2_endpoint(struct snd_usb_midi2_endpoint *ep) { list_del(&ep->list); + if (!ep->disconnected) { + ep->disconnected = 1; + kill_midi_urbs(ep, false); + drain_urb_queue(ep); + } free_midi_urbs(ep); kfree(ep); } diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 628f841b04aa..10792d26fa94 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -577,46 +577,30 @@ static bool snd_dualsense_ih_match(struct input_handler *handler, { struct dualsense_mixer_elem_info *mei; struct usb_device *snd_dev; - char *input_dev_path, *usb_dev_path; - size_t usb_dev_path_len; - bool match = false; + struct device *parent; mei = container_of(handler, struct dualsense_mixer_elem_info, ih); snd_dev = mei->info.head.mixer->chip->dev; - input_dev_path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); - if (!input_dev_path) { - dev_warn(&snd_dev->dev, "Failed to get input dev path\n"); - return false; - } - - usb_dev_path = kobject_get_path(&snd_dev->dev.kobj, GFP_KERNEL); - if (!usb_dev_path) { - dev_warn(&snd_dev->dev, "Failed to get USB dev path\n"); - goto free_paths; - } - /* * Ensure the VID:PID matched input device supposedly owned by the * hid-playstation driver belongs to the actual hardware handled by - * the current USB audio device, which implies input_dev_path being - * a subpath of usb_dev_path. + * the current USB audio device. * * This verification is necessary when there is more than one identical * controller attached to the host system. + * + * The input device is registered below the HID device, USB interface and + * USB device, so compare the parent chain directly instead of building + * kobject path strings. This avoids dereferencing kobject names while the + * USB device hierarchy is being torn down during disconnect. */ - usb_dev_path_len = strlen(usb_dev_path); - if (usb_dev_path_len >= strlen(input_dev_path)) - goto free_paths; - - usb_dev_path[usb_dev_path_len] = '/'; - match = !memcmp(input_dev_path, usb_dev_path, usb_dev_path_len + 1); - -free_paths: - kfree(input_dev_path); - kfree(usb_dev_path); + for (parent = dev->dev.parent; parent; parent = parent->parent) { + if (parent == &snd_dev->dev) + return true; + } - return match; + return false; } static int snd_dualsense_ih_connect(struct input_handler *handler, @@ -4526,6 +4510,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x1235, 0x821b): /* Focusrite Scarlett 16i16 4th Gen */ case USB_ID(0x1235, 0x821c): /* Focusrite Scarlett 18i16 4th Gen */ case USB_ID(0x1235, 0x821d): /* Focusrite Scarlett 18i20 4th Gen */ + case USB_ID(0x1235, 0x821e): /* Focusrite ISA C8X */ err = snd_fcp_init(mixer); break; diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c index a3f90cc7c6ca..e4bfd43a2488 100644 --- a/sound/usb/qcom/qc_audio_offload.c +++ b/sound/usb/qcom/qc_audio_offload.c @@ -1160,6 +1160,7 @@ uaudio_endpoint_setup(struct snd_usb_substream *subs, tr_pa = page_to_phys(pg); mem_info->dma = sg_dma_address(sgt->sgl); sg_free_table(sgt); + kfree(sgt); /* data transfer ring */ iova = uaudio_iommu_map_pa(MEM_XFER_RING, dma_coherent, tr_pa, @@ -1229,6 +1230,7 @@ static int uaudio_event_ring_setup(struct snd_usb_substream *subs, er_pa = page_to_phys(pg); mem_info->dma = sg_dma_address(sgt->sgl); sg_free_table(sgt); + kfree(sgt); iova = uaudio_iommu_map_pa(MEM_EVENT_RING, dma_coherent, er_pa, PAGE_SIZE); @@ -1618,8 +1620,13 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle, if (req_msg->service_interval_valid) { ret = get_data_interval_from_si(subs, req_msg->service_interval); - if (ret == -EINVAL) + if (ret == -EINVAL) { + if (req_msg->enable) { + guard(mutex)(&chip->mutex); + subs->opened = 0; + } goto response; + } datainterval = ret; } @@ -1640,6 +1647,11 @@ static void handle_uaudio_stream_req(struct qmi_handle *handle, subs->opened = 0; } } else { + if (info_idx < 0) { + ret = -EINVAL; + goto response; + } + info = &uadev[pcm_card_num].info[info_idx]; if (info->data_ep_pipe) { ep = usb_pipe_endpoint(uadev[pcm_card_num].udev, @@ -1976,6 +1988,7 @@ static int qc_usb_audio_probe(struct auxiliary_device *auxdev, release_qmi: qc_usb_audio_cleanup_qmi_dev(); qmi_handle_release(svc->uaudio_svc_hdl); + kfree(svc->uaudio_svc_hdl); free_svc: kfree(svc); @@ -2000,6 +2013,7 @@ static void qc_usb_audio_remove(struct auxiliary_device *auxdev) qc_usb_audio_cleanup_qmi_dev(); qmi_handle_release(svc->uaudio_svc_hdl); + kfree(svc->uaudio_svc_hdl); kfree(svc); uaudio_svc = NULL; } diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 97f28c53d201..71444c2898b4 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -391,6 +391,20 @@ YAMAHA_DEVICE(0x105d, NULL), } }, { + USB_DEVICE(0x0499, 0x150d), + QUIRK_DRIVER_INFO { + /* .vendor_name = "Yamaha", */ + /* .product_name = "CDS3000", */ + QUIRK_DATA_COMPOSITE { + { QUIRK_DATA_STANDARD_AUDIO(1) }, + { QUIRK_DATA_STANDARD_AUDIO(2) }, + { QUIRK_DATA_MIDI_YAMAHA(3) }, + { QUIRK_DATA_IGNORE(4) }, + QUIRK_COMPOSITE_END + } + } +}, +{ USB_DEVICE(0x0499, 0x1718), QUIRK_DRIVER_INFO { /* .vendor_name = "Yamaha", */ diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 30f6d572e4d1..1cb588691e16 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2413,6 +2413,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), DEVICE_FLG(0x1bcf, 0x2283, /* NexiGo N930AF FHD Webcam */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), + DEVICE_FLG(0x1ff7, 0x0f81, /* SC13A Webcam */ + QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x2040, 0x7200, /* Hauppauge HVR-950Q */ QUIRK_FLAG_SHARE_MEDIA_DEVICE | QUIRK_FLAG_ALIGN_TRANSFER), DEVICE_FLG(0x2040, 0x7201, /* Hauppauge HVR-950Q-MXL */ diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c index ec356deb27c9..ce69d1e5468e 100644 --- a/tools/bpf/bpftool/cgroup.c +++ b/tools/bpf/bpftool/cgroup.c @@ -78,6 +78,13 @@ static unsigned int query_flags; static struct btf *btf_vmlinux; static __u32 btf_vmlinux_id; +static void free_btf_vmlinux(void) +{ + btf__free(btf_vmlinux); + btf_vmlinux = NULL; + btf_vmlinux_id = 0; +} + static enum bpf_attach_type parse_attach_type(const char *str) { const char *attach_type_str; @@ -388,6 +395,8 @@ static int do_show(int argc, char **argv) if (json_output) jsonw_end_array(json_wtr); + free_btf_vmlinux(); + exit_cgroup: close(cgroup_fd); exit: @@ -437,7 +446,9 @@ static int do_show_tree_fn(const char *fpath, const struct stat *sb, printf("%s\n", fpath); } - btf_vmlinux = libbpf_find_kernel_btf(); + if (!btf_vmlinux) + btf_vmlinux = libbpf_find_kernel_btf(); + for (i = 0; i < ARRAY_SIZE(cgroup_attach_types); i++) show_bpf_progs(cgroup_fd, cgroup_attach_types[i], ftw->level); @@ -540,6 +551,7 @@ static int do_show_tree(int argc, char **argv) if (json_output) jsonw_end_array(json_wtr); + free_btf_vmlinux(); free(cgroup_alloced); return ret; diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h index d1db355e872b..67ff7882299e 100644 --- a/tools/testing/selftests/bpf/bpf_experimental.h +++ b/tools/testing/selftests/bpf/bpf_experimental.h @@ -423,6 +423,8 @@ static inline int get_preempt_count(void) return bpf_get_current_task_btf()->thread_info.preempt_count; #elif defined(bpf_target_s390) return bpf_get_lowcore()->preempt_count; +#elif defined(bpf_target_loongarch) + return bpf_get_current_task_btf()->thread_info.preempt_count; #endif return 0; } @@ -433,6 +435,7 @@ static inline int get_preempt_count(void) * * arm64 * * powerpc64 * * s390x + * * loongarch */ static inline int bpf_in_interrupt(void) { @@ -454,6 +457,7 @@ static inline int bpf_in_interrupt(void) * * arm64 * * powerpc64 * * s390x + * * loongarch */ static inline int bpf_in_nmi(void) { @@ -466,6 +470,7 @@ static inline int bpf_in_nmi(void) * * arm64 * * powerpc64 * * s390x + * * loongarch */ static inline int bpf_in_hardirq(void) { @@ -478,6 +483,7 @@ static inline int bpf_in_hardirq(void) * * arm64 * * powerpc64 * * s390x + * * loongarch */ static inline int bpf_in_serving_softirq(void) { @@ -498,6 +504,7 @@ static inline int bpf_in_serving_softirq(void) * * arm64 * * powerpc64 * * s390x + * * loongarch */ static inline int bpf_in_task(void) { diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c index b33dba4b126e..14d4c1793aed 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_nf.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_nf.c @@ -5,6 +5,8 @@ #include "test_bpf_nf.skel.h" #include "test_bpf_nf_fail.skel.h" +#define CT_OPTS_ERROR_GUARD 0x12345678 + static char log_buf[1024 * 1024]; struct { @@ -119,6 +121,10 @@ static void test_bpf_nf_ct(int mode) ASSERT_EQ(skel->bss->test_einval_reserved_new, -EINVAL, "Test EINVAL for reserved in new struct not set to 0"); ASSERT_EQ(skel->bss->test_einval_netns_id, -EINVAL, "Test EINVAL for netns_id < -1"); ASSERT_EQ(skel->bss->test_einval_len_opts, -EINVAL, "Test EINVAL for len__opts != NF_BPF_CT_OPTS_SZ"); + ASSERT_EQ(skel->bss->test_einval_len_opts_small_lookup, CT_OPTS_ERROR_GUARD, + "Test no error write for lookup opts__sz before error field"); + ASSERT_EQ(skel->bss->test_einval_len_opts_small_alloc, CT_OPTS_ERROR_GUARD, + "Test no error write for alloc opts__sz before error field"); ASSERT_EQ(skel->bss->test_eproto_l4proto, -EPROTO, "Test EPROTO for l4proto != TCP or UDP"); ASSERT_EQ(skel->bss->test_enonet_netns_id, -ENONET, "Test ENONET for bad but valid netns_id"); ASSERT_EQ(skel->bss->test_enoent_lookup, -ENOENT, "Test ENOENT for failed lookup"); diff --git a/tools/testing/selftests/bpf/prog_tests/btf.c b/tools/testing/selftests/bpf/prog_tests/btf.c index 96f719a0cec9..66855cbd6b73 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf.c +++ b/tools/testing/selftests/bpf/prog_tests/btf.c @@ -4121,8 +4121,6 @@ static struct btf_raw_test raw_tests[] = { .key_type_id = 1, .value_type_id = 1, .max_entries = 1, - .btf_load_err = true, - .err_str = "Type tags don't precede modifiers", }, { .descr = "type_tag test #3, type tag order", @@ -4141,8 +4139,6 @@ static struct btf_raw_test raw_tests[] = { .key_type_id = 1, .value_type_id = 1, .max_entries = 1, - .btf_load_err = true, - .err_str = "Type tags don't precede modifiers", }, { .descr = "type_tag test #4, type tag order", @@ -4161,8 +4157,6 @@ static struct btf_raw_test raw_tests[] = { .key_type_id = 1, .value_type_id = 1, .max_entries = 1, - .btf_load_err = true, - .err_str = "Type tags don't precede modifiers", }, { .descr = "type_tag test #5, type tag order", @@ -4198,11 +4192,9 @@ static struct btf_raw_test raw_tests[] = { .map_name = "tag_type_check_btf", .key_size = sizeof(int), .value_size = 4, - .key_type_id = 1, - .value_type_id = 1, + .key_type_id = 4, + .value_type_id = 4, .max_entries = 1, - .btf_load_err = true, - .err_str = "Type tags don't precede modifiers", }, { .descr = "type_tag test #7, tag with kflag", diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_preorder.c b/tools/testing/selftests/bpf/prog_tests/cgroup_preorder.c index d4d583872fa2..d2ccf409dfe3 100644 --- a/tools/testing/selftests/bpf/prog_tests/cgroup_preorder.c +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_preorder.c @@ -102,6 +102,82 @@ close_skel: return err; } +/* + * Replacing a link's program (bpf_link_update) must target the correct slot in + * the effective array even when a BPF_F_PREORDER program is attached to the + * same cgroup. All programs here are attached to a single cgroup; "parent" is + * reused only as a third distinct program. + * + * Attach child(1) normally and child_2(2) with BPF_F_PREORDER, so the effective + * order is [2, 1]. Then replace child(1)'s program with parent(3): only the + * non-preorder slot changes, giving [2, 3]. + */ +static int run_link_replace_test(int cgroup_fd, int sock_fd) +{ + LIBBPF_OPTS(bpf_link_create_opts, create_opts); + int err = 0, normal_link = -1, preorder_link = -1; + struct cgroup_preorder *skel = NULL; + enum bpf_attach_type atype; + __u8 *result, buf = 0x00; + socklen_t optlen = 1; + + skel = cgroup_preorder__open_and_load(); + if (!ASSERT_OK_PTR(skel, "cgroup_preorder__open_and_load")) + return -1; + + err = setsockopt(sock_fd, SOL_IP, IP_TOS, &buf, 1); + if (!ASSERT_OK(err, "setsockopt")) + goto close_skel; + + atype = bpf_program__expected_attach_type(skel->progs.child); + + create_opts.flags = 0; + normal_link = bpf_link_create(bpf_program__fd(skel->progs.child), + cgroup_fd, atype, &create_opts); + if (!ASSERT_GE(normal_link, 0, "create_normal_link")) { + err = normal_link; + goto close_skel; + } + + create_opts.flags = BPF_F_PREORDER; + preorder_link = bpf_link_create(bpf_program__fd(skel->progs.child_2), + cgroup_fd, atype, &create_opts); + if (!ASSERT_GE(preorder_link, 0, "create_preorder_link")) { + err = preorder_link; + goto close_links; + } + + result = skel->bss->result; + skel->bss->idx = 0; + memset(result, 0, 4); + + err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); + if (!ASSERT_OK(err, "getsockopt-before")) + goto close_links; + ASSERT_TRUE(result[0] == 2 && result[1] == 1, "order before update"); + + /* Replace the normal link's program child(1) -> parent(3). */ + err = bpf_link_update(normal_link, bpf_program__fd(skel->progs.parent), NULL); + if (!ASSERT_OK(err, "bpf_link_update")) + goto close_links; + + skel->bss->idx = 0; + memset(result, 0, 4); + + err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); + if (!ASSERT_OK(err, "getsockopt-after")) + goto close_links; + ASSERT_TRUE(result[0] == 2 && result[1] == 3, "order after update"); + +close_links: + if (preorder_link >= 0) + close(preorder_link); + close(normal_link); +close_skel: + cgroup_preorder__destroy(skel); + return err; +} + void test_cgroup_preorder(void) { int cg_parent = -1, cg_child = -1, sock_fd = -1; @@ -120,6 +196,7 @@ void test_cgroup_preorder(void) ASSERT_OK(run_getsockopt_test(cg_parent, cg_child, sock_fd, false), "getsockopt_test_1"); ASSERT_OK(run_getsockopt_test(cg_parent, cg_child, sock_fd, true), "getsockopt_test_2"); + ASSERT_OK(run_link_replace_test(cg_child, sock_fd), "link_replace_test"); out: close(sock_fd); diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h index 9eeb5b0b63d6..b0c441384f20 100644 --- a/tools/testing/selftests/bpf/progs/bpf_misc.h +++ b/tools/testing/selftests/bpf/progs/bpf_misc.h @@ -158,6 +158,7 @@ #define __arch_arm64 __arch("ARM64") #define __arch_riscv64 __arch("RISCV64") #define __arch_s390x __arch("s390x") +#define __arch_loongarch __arch("LOONGARCH") #define __caps_unpriv(caps) __test_tag("test_caps_unpriv=" EXPAND_QUOTE(caps)) #define __load_if_JITed() __test_tag("load_mode=jited") #define __load_if_no_JITed() __test_tag("load_mode=no_jited") diff --git a/tools/testing/selftests/bpf/progs/test_bpf_nf.c b/tools/testing/selftests/bpf/progs/test_bpf_nf.c index 076fbf03a126..df43649ecb78 100644 --- a/tools/testing/selftests/bpf/progs/test_bpf_nf.c +++ b/tools/testing/selftests/bpf/progs/test_bpf_nf.c @@ -10,6 +10,8 @@ #define EINVAL 22 #define ENOENT 2 +#define CT_OPTS_ERROR_GUARD 0x12345678 + #define NF_CT_ZONE_DIR_ORIG (1 << IP_CT_DIR_ORIGINAL) #define NF_CT_ZONE_DIR_REPL (1 << IP_CT_DIR_REPLY) @@ -19,6 +21,8 @@ int test_einval_reserved = 0; int test_einval_reserved_new = 0; int test_einval_netns_id = 0; int test_einval_len_opts = 0; +int test_einval_len_opts_small_lookup = 0; +int test_einval_len_opts_small_alloc = 0; int test_eproto_l4proto = 0; int test_enonet_netns_id = 0; int test_enoent_lookup = 0; @@ -124,6 +128,28 @@ nf_ct_test(struct nf_conn *(*lookup_fn)(void *, struct bpf_sock_tuple *, u32, else test_einval_len_opts = opts_def.error; + opts_def.error = CT_OPTS_ERROR_GUARD; + ct = lookup_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, + sizeof(opts_def.netns_id)); + if (ct) { + bpf_ct_release(ct); + test_einval_len_opts_small_lookup = -EINVAL; + } else { + test_einval_len_opts_small_lookup = opts_def.error; + } + + opts_def.error = CT_OPTS_ERROR_GUARD; + ct = alloc_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, + sizeof(opts_def.netns_id)); + if (ct) { + ct = bpf_ct_insert_entry(ct); + if (ct) + bpf_ct_release(ct); + test_einval_len_opts_small_alloc = -EINVAL; + } else { + test_einval_len_opts_small_alloc = opts_def.error; + } + opts_def.l4proto = IPPROTO_ICMP; ct = lookup_fn(ctx, &bpf_tuple, sizeof(bpf_tuple.ipv4), &opts_def, sizeof(opts_def)); diff --git a/tools/testing/selftests/bpf/progs/verifier_jit_inline.c b/tools/testing/selftests/bpf/progs/verifier_jit_inline.c index 76d80605ec7f..02e562f56f9d 100644 --- a/tools/testing/selftests/bpf/progs/verifier_jit_inline.c +++ b/tools/testing/selftests/bpf/progs/verifier_jit_inline.c @@ -12,6 +12,8 @@ __arch_arm64 __jited(" mrs x8, SP_EL0") __arch_riscv64 __jited(" mv a5, tp") +__arch_loongarch +__jited(" move $a5, $tp") int inline_bpf_get_current_task(void) { bpf_get_current_task(); @@ -19,4 +21,15 @@ int inline_bpf_get_current_task(void) return 0; } +SEC("fentry/bpf_fentry_test2") +__success __retval(0) +__arch_loongarch +__jited(" ld.wu $a5, $tp, 16") +int inline_bpf_get_smp_processor_id(void) +{ + bpf_get_smp_processor_id(); + + return 0; +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_lsm.c b/tools/testing/selftests/bpf/progs/verifier_lsm.c index 2f8103bfa14e..c724bf389f5c 100644 --- a/tools/testing/selftests/bpf/progs/verifier_lsm.c +++ b/tools/testing/selftests/bpf/progs/verifier_lsm.c @@ -197,4 +197,19 @@ int BPF_PROG(sleepable_lsm_cgroup) return 0; } +SEC("lsm/file_mprotect") +__description("lsm retval load must reset stale register bounds") +__failure __msg("div by zero") +__naked int retval_load_resets_bounds(void *ctx) +{ + asm volatile ( + "r6 = 0;" + "r6 = *(u64 *)(r1 + 24);" + "if r6 == 0 goto +1;" + "r6 /= 0;" + "r0 = 0;" + "exit;" + ::: __clobber_all); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c index 6bc721accbae..0174887e28f5 100644 --- a/tools/testing/selftests/bpf/progs/verifier_spill_fill.c +++ b/tools/testing/selftests/bpf/progs/verifier_spill_fill.c @@ -1359,4 +1359,22 @@ __naked void var_off_write_over_scalar_spill(void) : __clobber_all); } +SEC("socket") +__description("partial fill from cleaned pointer spill") +__failure +__log_level(2) +__msg("1: (05) goto pc+0") +__msg("2: (61) r0 = *(u32 *)(r10 -4)") +__msg("invalid size of register fill") +__flag(BPF_F_TEST_STATE_FREQ) +__naked void partial_fill_from_cleaned_pointer_spill(void) +{ + /* Spill R1(ctx), then force a checkpoint and half-slot cleanup. */ + asm volatile ("*(u64 *)(r10 - 8) = r1;" + "goto +0;" + "r0 = *(u32 *)(r10 - 4);" + "exit;" + ::: __clobber_all); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/progs/verifier_unpriv.c b/tools/testing/selftests/bpf/progs/verifier_unpriv.c index c16f8382cf17..49f7bd05edad 100644 --- a/tools/testing/selftests/bpf/progs/verifier_unpriv.c +++ b/tools/testing/selftests/bpf/progs/verifier_unpriv.c @@ -976,4 +976,26 @@ l0_%=: exit; \ : __clobber_all); } +SEC("socket") +__description("unpriv: Spectre v4 stack write slot index") +__success __success_unpriv +__retval(0) +#ifdef SPEC_V4 +__xlated_unpriv("r0 = 0") +__xlated_unpriv("*(u32 *)(r10 -4) = r0") +__xlated_unpriv("nospec") +__xlated_unpriv("*(u32 *)(r10 -8) = r0") +__xlated_unpriv("nospec") +__xlated_unpriv("exit") +#endif +__naked void stack_write_nospec_slot_index(void) +{ + asm volatile (" \ + r0 = 0; \ + *(u32 *)(r10 - 4) = r0; \ + *(u32 *)(r10 - 8) = r0; \ + exit; \ +" ::: __clobber_all); +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c index abdb9e6e3713..3ce32d134e2c 100644 --- a/tools/testing/selftests/bpf/test_loader.c +++ b/tools/testing/selftests/bpf/test_loader.c @@ -377,6 +377,7 @@ enum arch { ARCH_ARM64 = 0x4, ARCH_RISCV64 = 0x8, ARCH_S390X = 0x10, + ARCH_LOONGARCH = 0x20, }; static int get_current_arch(void) @@ -389,6 +390,8 @@ static int get_current_arch(void) return ARCH_RISCV64; #elif defined(__s390x__) return ARCH_S390X; +#elif defined(__loongarch__) + return ARCH_LOONGARCH; #endif return ARCH_UNKNOWN; } @@ -580,6 +583,8 @@ static int parse_test_spec(struct test_loader *tester, arch = ARCH_RISCV64; } else if (strcmp(val, "s390x") == 0) { arch = ARCH_S390X; + } else if (strcmp(val, "LOONGARCH") == 0) { + arch = ARCH_LOONGARCH; } else { PRINT_FAIL("bad arch spec: '%s'\n", val); err = -EINVAL; diff --git a/tools/testing/selftests/drivers/net/hw/tso.py b/tools/testing/selftests/drivers/net/hw/tso.py index 1b789fea8929..802bb4868046 100755 --- a/tools/testing/selftests/drivers/net/hw/tso.py +++ b/tools/testing/selftests/drivers/net/hw/tso.py @@ -187,28 +187,24 @@ def query_nic_features(cfg) -> None: cfg.wanted_features.add(f["name"]) cfg.hw_features = set() - hw_all_features_cmd = "" for f in features["hw"]["bits"]["bit"]: if f.get("value", False): - feature = f["name"] - cfg.hw_features.add(feature) - hw_all_features_cmd += f" {feature} on" - try: - ethtool(f"-K {cfg.ifname} {hw_all_features_cmd}") - except Exception as e: - ksft_pr(f"WARNING: failure enabling all hw features: {e}") - ksft_pr("partial gso feature detection may be impacted") + cfg.hw_features.add(f["name"]) # Check which features are supported via GSO partial cfg.partial_features = set() if 'tx-gso-partial' in cfg.hw_features: + seg_features = {f for f in cfg.hw_features if "segmentation" in f} + ethtool(f"-K {cfg.ifname} " + + " ".join(f"{f} on" for f in seg_features)) + ethtool(f"-K {cfg.ifname} tx-gso-partial off") no_partial = set() features = cfg.ethnl.features_get({"header": {"dev-index": cfg.ifindex}}) for f in features["active"]["bits"]["bit"]: no_partial.add(f["name"]) - cfg.partial_features = cfg.hw_features - no_partial + cfg.partial_features = seg_features - no_partial ethtool(f"-K {cfg.ifname} tx-gso-partial on") restore_wanted_features(cfg) diff --git a/tools/testing/selftests/net/lib/py/utils.py b/tools/testing/selftests/net/lib/py/utils.py index 87eae79d01c1..184bb04343f6 100644 --- a/tools/testing/selftests/net/lib/py/utils.py +++ b/tools/testing/selftests/net/lib/py/utils.py @@ -44,7 +44,7 @@ class cmd: Use bkg() instead to run a command in the background. """ def __init__(self, comm, shell=None, fail=True, expect_fail=False, ns=None, - background=False, host=None, timeout=5, ksft_ready=None, + background=False, host=None, timeout=20, ksft_ready=None, ksft_wait=None): if ns: if hasattr(ns, 'user_ns_path'): @@ -113,7 +113,7 @@ class cmd: return stdout, stderr - def process(self, terminate=True, fail=None, expect_fail=False, timeout=5): + def process(self, terminate=True, fail=None, expect_fail=False, timeout=20): if fail is None: fail = not terminate diff --git a/tools/tracing/rtla/.gitignore b/tools/tracing/rtla/.gitignore index c7b4bf1c8ba9..2a1c99ddd1bf 100644 --- a/tools/tracing/rtla/.gitignore +++ b/tools/tracing/rtla/.gitignore @@ -1,14 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only -rtla -rtla-static -unit_tests +/rtla +/rtla-static +/unit_tests fixdep feature FEATURE-DUMP *.skel.h -custom_filename.txt -osnoise_irq_noise_hist.txt -osnoise_trace.txt -timerlat_trace.txt -libsubcmd/ -lib/ +/libsubcmd/ diff --git a/tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh b/tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh index 8aff98cd2c1f..d10a4e3b321d 100644 --- a/tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh +++ b/tools/tracing/rtla/tests/scripts/lib/get_workload_pids.sh @@ -5,7 +5,7 @@ get_workload_pids() { local rtla_pid=$(ps -o ppid= $shell_pid) # kernel threads - pgrep -P $(pgrep ^kthreadd$) -f '^(osnoise|timerlat)/[0-9]+$' + pgrep -P $(pgrep ^kthreadd$) -f '^\[?(osnoise|timerlat)/[0-9]+\]?$' # user threads pgrep -P $rtla_pid | grep -v "^$shell_pid$" } |
