aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2023-03-03 14:20:56 +0100
committerTakashi Iwai <tiwai@suse.de>2023-03-03 14:20:56 +0100
commit26ed1d29fc44f3f2f0c396c1392abefac5f0454e (patch)
treeffba9ebddf759f04cbeca8adace5cc8093c58c2d
parente97fc9cffbb9f372b53b42c36cd7b20aab44a554 (diff)
parent7933b90b42896f5b6596e6a829bb31c5121fc2a9 (diff)
downloadiio-26ed1d29fc44f3f2f0c396c1392abefac5f0454e.tar.gz
Merge branch 'for-next' into for-linus
-rw-r--r--.gitignore1
-rw-r--r--.mailmap4
-rw-r--r--CREDITS15
-rw-r--r--Documentation/ABI/testing/debugfs-pktcdvd18
-rw-r--r--Documentation/ABI/testing/sysfs-class-pktcdvd97
-rw-r--r--Documentation/admin-guide/cgroup-v2.rst15
-rw-r--r--Documentation/admin-guide/mm/zswap.rst4
-rw-r--r--Documentation/arm64/silicon-errata.rst2
-rw-r--r--Documentation/conf.py12
-rw-r--r--Documentation/devicetree/bindings/.gitignore5
-rw-r--r--Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml11
-rw-r--r--Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-aes.yaml2
-rw-r--r--Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-sha.yaml2
-rw-r--r--Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-tdes.yaml2
-rw-r--r--Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml4
-rw-r--r--Documentation/devicetree/bindings/display/msm/dsi-phy-10nm.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/msm/dsi-phy-14nm.yaml1
-rw-r--r--Documentation/devicetree/bindings/display/msm/dsi-phy-28nm.yaml4
-rw-r--r--Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml2
-rw-r--r--Documentation/devicetree/bindings/display/msm/qcom,sm6115-mdss.yaml2
-rw-r--r--Documentation/devicetree/bindings/dsp/mediatek,mt8186-dsp.yaml4
-rw-r--r--Documentation/devicetree/bindings/i2c/renesas,rzv2m.yaml4
-rw-r--r--Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml24
-rw-r--r--Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml2
-rw-r--r--Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml3
-rw-r--r--Documentation/devicetree/bindings/net/marvell,orion-mdio.yaml30
-rw-r--r--Documentation/devicetree/bindings/phy/amlogic,g12a-usb2-phy.yaml (renamed from Documentation/devicetree/bindings/phy/amlogic,meson-g12a-usb2-phy.yaml)8
-rw-r--r--Documentation/devicetree/bindings/phy/amlogic,g12a-usb3-pcie-phy.yaml (renamed from Documentation/devicetree/bindings/phy/amlogic,meson-g12a-usb3-pcie-phy.yaml)6
-rw-r--r--Documentation/devicetree/bindings/phy/qcom,usb-hs-28nm.yaml1
-rw-r--r--Documentation/devicetree/bindings/regulator/samsung,s2mps14.yaml21
-rw-r--r--Documentation/devicetree/bindings/riscv/cpus.yaml2
-rw-r--r--Documentation/devicetree/bindings/rtc/qcom-pm8xxx-rtc.yaml2
-rw-r--r--Documentation/devicetree/bindings/soc/qcom/qcom,apr-services.yaml5
-rw-r--r--Documentation/devicetree/bindings/sound/adi,adau7002.txt19
-rw-r--r--Documentation/devicetree/bindings/sound/adi,adau7002.yaml40
-rw-r--r--Documentation/devicetree/bindings/sound/ak4613.yaml7
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,axg-fifo.txt34
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,axg-fifo.yaml112
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,axg-pdm.txt29
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml82
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.txt124
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml183
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,axg-spdifin.txt27
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,axg-spdifin.yaml86
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,axg-spdifout.txt25
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,axg-spdifout.yaml79
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.txt36
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.yaml88
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.txt22
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml55
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/audio-graph-port.yaml65
-rw-r--r--Documentation/devicetree/bindings/sound/awinic,aw88395.yaml53
-rw-r--r--Documentation/devicetree/bindings/sound/cirrus,cs35l41.yaml43
-rw-r--r--Documentation/devicetree/bindings/sound/component-common.yaml21
-rw-r--r--Documentation/devicetree/bindings/sound/dai-common.yaml11
-rw-r--r--[-rwxr-xr-x]Documentation/devicetree/bindings/sound/everest,es8326.yaml0
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,sai.yaml38
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,xcvr.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml12
-rw-r--r--Documentation/devicetree/bindings/sound/infineon,peb2466.yaml91
-rw-r--r--Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml48
-rw-r--r--Documentation/devicetree/bindings/sound/max98090.txt59
-rw-r--r--Documentation/devicetree/bindings/sound/max98095.txt22
-rw-r--r--Documentation/devicetree/bindings/sound/maxim,max98090.yaml84
-rw-r--r--Documentation/devicetree/bindings/sound/maxim,max98095.yaml54
-rw-r--r--Documentation/devicetree/bindings/sound/mediatek,mt8188-afe.yaml208
-rw-r--r--Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml97
-rw-r--r--Documentation/devicetree/bindings/sound/microchip,sama7g5-i2smcc.yaml (renamed from Documentation/devicetree/bindings/sound/mchp,i2s-mcc.yaml)2
-rw-r--r--Documentation/devicetree/bindings/sound/microchip,sama7g5-pdmc.yaml (renamed from Documentation/devicetree/bindings/sound/microchip,pdmc.yaml)2
-rw-r--r--Documentation/devicetree/bindings/sound/microchip,sama7g5-spdifrx.yaml (renamed from Documentation/devicetree/bindings/sound/mchp,spdifrx.yaml)2
-rw-r--r--Documentation/devicetree/bindings/sound/microchip,sama7g5-spdiftx.yaml (renamed from Documentation/devicetree/bindings/sound/mchp,spdiftx.yaml)2
-rw-r--r--Documentation/devicetree/bindings/sound/nau8822.txt16
-rw-r--r--Documentation/devicetree/bindings/sound/nuvoton,nau8822.yaml46
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml136
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml3
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,sm8250.yaml24
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml62
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,wsa881x.yaml5
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,idt821034.yaml75
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,rsnd.yaml59
-rw-r--r--Documentation/devicetree/bindings/sound/rt5640.txt3
-rw-r--r--Documentation/devicetree/bindings/sound/samsung,odroid.yaml3
-rw-r--r--Documentation/devicetree/bindings/sound/samsung-i2s.yaml11
-rw-r--r--Documentation/devicetree/bindings/sound/simple-card.yaml18
-rw-r--r--Documentation/devicetree/bindings/sound/tas5720.txt2
-rw-r--r--Documentation/devicetree/bindings/sound/ti,pcm3168a.txt56
-rw-r--r--Documentation/devicetree/bindings/sound/ti,pcm3168a.yaml107
-rw-r--r--Documentation/devicetree/bindings/sound/ti,tlv320aic3x.yaml165
-rw-r--r--Documentation/devicetree/bindings/sound/tlv320aic3x.txt97
-rw-r--r--Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml10
-rw-r--r--Documentation/devicetree/bindings/spi/atmel,at91rm9200-spi.yaml2
-rw-r--r--Documentation/devicetree/bindings/spi/atmel,quadspi.yaml2
-rw-r--r--Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml4
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.yaml2
-rw-r--r--Documentation/filesystems/erofs.rst2
-rw-r--r--Documentation/kbuild/makefiles.rst2
-rw-r--r--Documentation/maintainer/maintainer-entry-profile.rst1
-rw-r--r--Documentation/networking/bridge.rst2
-rw-r--r--Documentation/networking/device_drivers/ethernet/intel/ice.rst2
-rw-r--r--Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst2
-rw-r--r--Documentation/networking/nf_conntrack-sysctl.rst10
-rw-r--r--Documentation/networking/rxrpc.rst4
-rw-r--r--Documentation/nvme/feature-and-quirk-policy.rst77
-rw-r--r--Documentation/process/maintainer-netdev.rst369
-rw-r--r--Documentation/sound/alsa-configuration.rst8
-rw-r--r--Documentation/sound/cards/audigy-mixer.rst2
-rw-r--r--Documentation/sound/cards/maya44.rst2
-rw-r--r--Documentation/sound/cards/sb-live-mixer.rst2
-rw-r--r--Documentation/sound/designs/jack-controls.rst2
-rw-r--r--Documentation/sound/designs/seq-oss.rst2
-rw-r--r--Documentation/sound/hd-audio/notes.rst2
-rw-r--r--Documentation/sound/kernel-api/writing-an-alsa-driver.rst10
-rw-r--r--Documentation/sphinx/load_config.py6
-rw-r--r--Documentation/virt/kvm/api.rst78
-rw-r--r--Documentation/virt/kvm/locking.rst22
-rw-r--r--Documentation/x86/amd-memory-encryption.rst36
-rw-r--r--MAINTAINERS132
-rw-r--r--Makefile21
-rw-r--r--arch/arm/Makefile2
-rw-r--r--arch/arm/boot/dts/armada-38x.dtsi4
-rw-r--r--arch/arm/boot/dts/armada-39x.dtsi4
-rw-r--r--arch/arm/boot/dts/aspeed-bmc-ibm-bonnell.dts2
-rw-r--r--arch/arm/boot/dts/imx53-ppd.dts2
-rw-r--r--arch/arm/boot/dts/imx6qdl-gw560x.dtsi1
-rw-r--r--arch/arm/boot/dts/imx6ul-pico-dwarf.dts2
-rw-r--r--arch/arm/boot/dts/imx7d-pico-dwarf.dts4
-rw-r--r--arch/arm/boot/dts/imx7d-pico-nymph.dts4
-rw-r--r--arch/arm/boot/dts/imx7d-smegw01.dts3
-rw-r--r--arch/arm/boot/dts/nuvoton-wpcm450.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom-apq8084-ifc6540.dts20
-rw-r--r--arch/arm/boot/dts/qcom-apq8084.dtsi4
-rw-r--r--arch/arm/boot/dts/rk3288.dtsi1
-rw-r--r--arch/arm/boot/dts/sam9x60.dtsi2
-rw-r--r--arch/arm/boot/dts/stihxxx-b2120.dtsi2
-rw-r--r--arch/arm/boot/dts/stm32mp151a-prtt1l.dtsi8
-rw-r--r--arch/arm/boot/dts/stm32mp157c-emstamp-argon.dtsi8
-rw-r--r--arch/arm/boot/dts/stm32mp15xx-dhcom-som.dtsi8
-rw-r--r--arch/arm/boot/dts/stm32mp15xx-dhcor-som.dtsi8
-rw-r--r--arch/arm/boot/dts/vf610-zii-dev-rev-b.dts2
-rw-r--r--arch/arm/boot/dts/vf610-zii-dev-rev-c.dts2
-rw-r--r--arch/arm/crypto/Makefile7
-rw-r--r--arch/arm/include/asm/thread_info.h13
-rw-r--r--arch/arm/mach-footbridge/isa-rtc.c1
-rw-r--r--arch/arm/mach-imx/cpu-imx25.c1
-rw-r--r--arch/arm/mach-imx/cpu-imx27.c1
-rw-r--r--arch/arm/mach-imx/cpu-imx31.c1
-rw-r--r--arch/arm/mach-imx/cpu-imx35.c1
-rw-r--r--arch/arm/mach-imx/cpu-imx5.c1
-rw-r--r--arch/arm/mach-omap1/Kconfig5
-rw-r--r--arch/arm/mach-omap1/Makefile4
-rw-r--r--arch/arm/mach-omap1/gpio15xx.c1
-rw-r--r--arch/arm/mach-omap1/io.c32
-rw-r--r--arch/arm/mach-omap1/mcbsp.c21
-rw-r--r--arch/arm/mach-omap1/pm.h7
-rw-r--r--arch/arm/mach-pxa/Kconfig2
-rw-r--r--arch/arm/mm/nommu.c2
-rw-r--r--arch/arm/mm/proc-macros.S1
-rw-r--r--arch/arm64/Kconfig18
-rw-r--r--arch/arm64/boot/dts/amlogic/meson-axg.dtsi4
-rw-r--r--arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi6
-rw-r--r--arch/arm64/boot/dts/amlogic/meson-gx.dtsi6
-rw-r--r--arch/arm64/boot/dts/amlogic/meson-sm1-odroid-hc4.dts8
-rw-r--r--arch/arm64/boot/dts/freescale/fsl-ls1012a-qds.dts2
-rw-r--r--arch/arm64/boot/dts/freescale/fsl-ls1043a-qds.dts2
-rw-r--r--arch/arm64/boot/dts/freescale/fsl-ls1046a-qds.dts2
-rw-r--r--arch/arm64/boot/dts/freescale/fsl-ls1088a-qds.dts2
-rw-r--r--arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts2
-rw-r--r--arch/arm64/boot/dts/freescale/fsl-ls1088a-ten64.dts2
-rw-r--r--arch/arm64/boot/dts/freescale/fsl-ls208xa-qds.dtsi2
-rw-r--r--arch/arm64/boot/dts/freescale/fsl-ls208xa-rdb.dtsi2
-rw-r--r--arch/arm64/boot/dts/freescale/fsl-lx2160a-cex7.dtsi2
-rw-r--r--arch/arm64/boot/dts/freescale/imx8dxl.dtsi2
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mm-beacon-baseboard.dtsi4
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mm-data-modul-edm-sbc.dts3
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mm-nitrogen-r2.dts2
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h2
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mm-venice-gw72xx-0x-rs232-rts.dtso1
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mm-venice-gw73xx-0x-rs232-rts.dtso1
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mm-venice-gw73xx.dtsi1
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mm-venice-gw7901.dts4
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mm-venice-gw7902.dts3
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mm-venice-gw7903.dts1
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mm-verdin-dahlia.dtsi1
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mm-verdin-dev.dtsi1
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mm-verdin.dtsi1
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mn-venice-gw7902.dts1
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mp-evk.dts4
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mp-phycore-som.dtsi10
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mp-venice-gw74xx.dts1
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mp.dtsi15
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mq-nitrogen.dts4
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mq-thor96.dts4
-rw-r--r--arch/arm64/boot/dts/freescale/imx8qxp-mek.dts2
-rw-r--r--arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts6
-rw-r--r--arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi2
-rw-r--r--arch/arm64/boot/dts/mediatek/mt8195.dtsi4
-rw-r--r--arch/arm64/boot/dts/qcom/msm8992-lg-bullhead.dtsi6
-rw-r--r--arch/arm64/boot/dts/qcom/msm8992-xiaomi-libra.dts77
-rw-r--r--arch/arm64/boot/dts/qcom/msm8992.dtsi4
-rw-r--r--arch/arm64/boot/dts/qcom/msm8994-huawei-angler-rev-101.dts19
-rw-r--r--arch/arm64/boot/dts/qcom/sc8280xp.dtsi79
-rw-r--r--arch/arm64/boot/dts/qcom/sm8250.dtsi1
-rw-r--r--arch/arm64/boot/dts/qcom/sm8350.dtsi4
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts2
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3399-op1-opp.dtsi2
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts7
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3399.dtsi6
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3566-box-demo.dts11
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3568-rock-3a.dts5
-rw-r--r--arch/arm64/boot/dts/rockchip/rk356x.dtsi1
-rw-r--r--arch/arm64/crypto/sm4-ce-ccm-core.S5
-rw-r--r--arch/arm64/crypto/sm4-ce-gcm-core.S5
-rw-r--r--arch/arm64/include/asm/atomic_ll_sc.h2
-rw-r--r--arch/arm64/include/asm/atomic_lse.h2
-rw-r--r--arch/arm64/include/asm/cputype.h4
-rw-r--r--arch/arm64/include/asm/efi.h9
-rw-r--r--arch/arm64/include/asm/esr.h9
-rw-r--r--arch/arm64/include/asm/hugetlb.h9
-rw-r--r--arch/arm64/include/asm/kvm_arm.h15
-rw-r--r--arch/arm64/include/asm/kvm_emulate.h42
-rw-r--r--arch/arm64/include/asm/pgtable.h16
-rw-r--r--arch/arm64/include/asm/stacktrace.h15
-rw-r--r--arch/arm64/include/asm/uprobes.h2
-rw-r--r--arch/arm64/kernel/cpu_errata.c7
-rw-r--r--arch/arm64/kernel/efi-rt-wrapper.S7
-rw-r--r--arch/arm64/kernel/efi.c3
-rw-r--r--arch/arm64/kernel/elfcore.c61
-rw-r--r--arch/arm64/kernel/fpsimd.c2
-rw-r--r--arch/arm64/kernel/ptrace.c2
-rw-r--r--arch/arm64/kernel/signal.c9
-rw-r--r--arch/arm64/kernel/stacktrace.c12
-rw-r--r--arch/arm64/kvm/guest.c2
-rw-r--r--arch/arm64/kvm/hyp/include/hyp/fault.h2
-rw-r--r--arch/arm64/kvm/hyp/include/hyp/switch.h2
-rw-r--r--arch/arm64/kvm/mmu.c21
-rw-r--r--arch/arm64/kvm/sys_regs.c2
-rw-r--r--arch/arm64/kvm/vgic/vgic-its.c13
-rw-r--r--arch/arm64/kvm/vgic/vgic-v3.c31
-rw-r--r--arch/arm64/kvm/vgic/vgic-v4.c8
-rw-r--r--arch/arm64/kvm/vgic/vgic.h15
-rw-r--r--arch/arm64/mm/hugetlbpage.c21
-rw-r--r--arch/arm64/mm/mmu.c21
-rw-r--r--arch/arm64/tools/cpucaps1
-rw-r--r--arch/ia64/kernel/elfcore.c4
-rw-r--r--arch/ia64/kernel/sys_ia64.c7
-rw-r--r--arch/loongarch/include/asm/ftrace.h2
-rw-r--r--arch/loongarch/include/asm/inst.h9
-rw-r--r--arch/loongarch/include/asm/unwind.h41
-rw-r--r--arch/loongarch/kernel/Makefile2
-rw-r--r--arch/loongarch/kernel/alternative.c6
-rw-r--r--arch/loongarch/kernel/cpu-probe.c2
-rw-r--r--arch/loongarch/kernel/genex.S3
-rw-r--r--arch/loongarch/kernel/inst.c45
-rw-r--r--arch/loongarch/kernel/process.c12
-rw-r--r--arch/loongarch/kernel/traps.c3
-rw-r--r--arch/loongarch/kernel/unwind.c32
-rw-r--r--arch/loongarch/kernel/unwind_guess.c49
-rw-r--r--arch/loongarch/kernel/unwind_prologue.c252
-rw-r--r--arch/loongarch/mm/tlb.c2
-rw-r--r--arch/mips/ralink/of.c2
-rw-r--r--arch/parisc/kernel/firmware.c5
-rw-r--r--arch/parisc/kernel/ptrace.c21
-rw-r--r--arch/powerpc/Kconfig1
-rw-r--r--arch/powerpc/boot/dts/fsl/t2081si-post.dtsi16
-rwxr-xr-xarch/powerpc/boot/wrapper4
-rw-r--r--arch/powerpc/include/asm/book3s/64/tlbflush.h2
-rw-r--r--arch/powerpc/include/asm/hw_irq.h43
-rw-r--r--arch/powerpc/include/asm/imc-pmu.h2
-rw-r--r--arch/powerpc/kernel/dbell.c2
-rw-r--r--arch/powerpc/kernel/head_85xx.S3
-rw-r--r--arch/powerpc/kernel/interrupt.c6
-rw-r--r--arch/powerpc/kernel/irq.c2
-rw-r--r--arch/powerpc/kernel/time.c2
-rw-r--r--arch/powerpc/kernel/vmlinux.lds.S6
-rw-r--r--arch/powerpc/kexec/file_load_64.c12
-rw-r--r--arch/powerpc/kvm/booke.c5
-rw-r--r--arch/powerpc/mm/book3s64/hash_utils.c2
-rw-r--r--arch/powerpc/mm/book3s64/radix_pgtable.c24
-rw-r--r--arch/powerpc/perf/imc-pmu.c122
-rw-r--r--arch/riscv/Makefile3
-rw-r--r--arch/riscv/boot/dts/sifive/fu740-c000.dtsi2
-rw-r--r--arch/riscv/include/asm/alternative-macros.h2
-rw-r--r--arch/riscv/include/asm/hwcap.h3
-rw-r--r--arch/riscv/include/asm/pgtable.h4
-rw-r--r--arch/riscv/include/asm/uaccess.h2
-rw-r--r--arch/riscv/include/asm/vdso/processor.h28
-rw-r--r--arch/riscv/kernel/head.S2
-rw-r--r--arch/riscv/kernel/probes/kprobes.c26
-rw-r--r--arch/riscv/kernel/probes/simulate-insn.c4
-rw-r--r--arch/riscv/kernel/probes/simulate-insn.h4
-rw-r--r--arch/riscv/kernel/smpboot.c3
-rw-r--r--arch/riscv/kernel/stacktrace.c3
-rw-r--r--arch/riscv/mm/cacheflush.c4
-rw-r--r--arch/riscv/mm/pgtable.c20
-rw-r--r--arch/s390/boot/decompressor.c6
-rw-r--r--arch/s390/configs/debug_defconfig7
-rw-r--r--arch/s390/configs/defconfig6
-rw-r--r--arch/s390/configs/zfcpdump_defconfig2
-rw-r--r--arch/s390/include/asm/cpu_mf.h31
-rw-r--r--arch/s390/include/asm/debug.h6
-rw-r--r--arch/s390/include/asm/percpu.h2
-rw-r--r--arch/s390/kernel/machine_kexec_file.c5
-rw-r--r--arch/s390/kernel/perf_cpum_sf.c101
-rw-r--r--arch/s390/kernel/setup.c5
-rw-r--r--arch/s390/kernel/vmlinux.lds.S4
-rw-r--r--arch/s390/kvm/interrupt.c12
-rw-r--r--arch/sh/include/asm/pgtable-3level.h2
-rw-r--r--arch/sh/kernel/vmlinux.lds.S1
-rw-r--r--arch/x86/Makefile2
-rw-r--r--arch/x86/boot/bioscall.S4
-rw-r--r--arch/x86/boot/compressed/ident_map_64.c6
-rw-r--r--arch/x86/boot/compressed/misc.h2
-rw-r--r--arch/x86/boot/compressed/sev.c70
-rw-r--r--arch/x86/coco/tdx/tdx.c26
-rw-r--r--arch/x86/events/amd/core.c2
-rw-r--r--arch/x86/events/intel/core.c1
-rw-r--r--arch/x86/events/intel/cstate.c22
-rw-r--r--arch/x86/events/intel/uncore.c1
-rw-r--r--arch/x86/events/msr.c3
-rw-r--r--arch/x86/events/rapl.c5
-rw-r--r--arch/x86/include/asm/acpi.h8
-rw-r--r--arch/x86/include/asm/debugreg.h26
-rw-r--r--arch/x86/include/asm/insn-eval.h18
-rw-r--r--arch/x86/include/asm/intel-family.h2
-rw-r--r--arch/x86/include/asm/kvm_host.h1
-rw-r--r--arch/x86/include/asm/msr-index.h20
-rw-r--r--arch/x86/include/uapi/asm/svm.h6
-rw-r--r--arch/x86/kernel/callthunks.c4
-rw-r--r--arch/x86/kernel/cpu/aperfmperf.c9
-rw-r--r--arch/x86/kernel/cpu/bugs.c2
-rw-r--r--arch/x86/kernel/cpu/resctrl/monitor.c49
-rw-r--r--arch/x86/kernel/cpu/resctrl/rdtgroup.c12
-rw-r--r--arch/x86/kernel/crash.c4
-rw-r--r--arch/x86/kernel/i8259.c1
-rw-r--r--arch/x86/kernel/irqinit.c4
-rw-r--r--arch/x86/kernel/kprobes/core.c12
-rw-r--r--arch/x86/kernel/kprobes/opt.c28
-rw-r--r--arch/x86/kernel/sev.c18
-rw-r--r--arch/x86/kvm/cpuid.c32
-rw-r--r--arch/x86/kvm/hyperv.c63
-rw-r--r--arch/x86/kvm/irq_comm.c5
-rw-r--r--arch/x86/kvm/lapic.h4
-rw-r--r--arch/x86/kvm/mmu/spte.h2
-rw-r--r--arch/x86/kvm/mmu/tdp_mmu.c25
-rw-r--r--arch/x86/kvm/pmu.c3
-rw-r--r--arch/x86/kvm/pmu.h3
-rw-r--r--arch/x86/kvm/svm/nested.c12
-rw-r--r--arch/x86/kvm/vmx/nested.c20
-rw-r--r--arch/x86/kvm/vmx/vmx.c28
-rw-r--r--arch/x86/kvm/x86.c3
-rw-r--r--arch/x86/kvm/xen.c230
-rw-r--r--arch/x86/lib/insn-eval.c20
-rw-r--r--arch/x86/lib/iomap_copy_64.S2
-rw-r--r--arch/x86/mm/init.c4
-rw-r--r--arch/x86/mm/pat/memtype.c3
-rw-r--r--arch/x86/pci/mmconfig-shared.c44
-rw-r--r--arch/x86/pci/xen.c2
-rw-r--r--arch/x86/um/elfcore.c4
-rw-r--r--arch/x86/xen/p2m.c5
-rw-r--r--arch/xtensa/include/asm/processor.h9
-rw-r--r--arch/xtensa/kernel/traps.c2
-rw-r--r--arch/xtensa/mm/fault.c4
-rw-r--r--block/Kconfig1
-rw-r--r--block/bfq-cgroup.c10
-rw-r--r--block/bfq-iosched.c6
-rw-r--r--block/bfq-iosched.h2
-rw-r--r--block/bio.c37
-rw-r--r--block/blk-cgroup.c8
-rw-r--r--block/blk-core.c3
-rw-r--r--block/blk-merge.c14
-rw-r--r--block/blk-mq.c16
-rw-r--r--block/genhd.c11
-rw-r--r--certs/Makefile4
-rw-r--r--drivers/accessibility/speakup/spk_ttyio.c3
-rw-r--r--drivers/acpi/acpi_video.c17
-rw-r--r--drivers/acpi/glue.c14
-rw-r--r--drivers/acpi/nfit/core.c2
-rw-r--r--drivers/acpi/prmt.c10
-rw-r--r--drivers/acpi/resource.c39
-rw-r--r--drivers/acpi/scan.c7
-rw-r--r--drivers/acpi/sleep.c6
-rw-r--r--drivers/acpi/video_detect.c42
-rw-r--r--drivers/acpi/x86/s2idle.c87
-rw-r--r--drivers/ata/Kconfig1
-rw-r--r--drivers/ata/ahci.c32
-rw-r--r--drivers/ata/libata-core.c2
-rw-r--r--drivers/base/property.c18
-rw-r--r--drivers/base/test/test_async_driver_probe.c2
-rw-r--r--drivers/block/Kconfig43
-rw-r--r--drivers/block/Makefile1
-rw-r--r--drivers/block/drbd/drbd_req.c2
-rw-r--r--drivers/block/pktcdvd.c2946
-rw-r--r--drivers/block/ps3vram.c2
-rw-r--r--drivers/block/rnbd/rnbd-clt.c2
-rw-r--r--drivers/block/ublk_drv.c12
-rw-r--r--drivers/block/virtio_blk.c35
-rw-r--r--drivers/block/xen-blkback/xenbus.c4
-rw-r--r--drivers/block/xen-blkfront.c3
-rw-r--r--drivers/bluetooth/hci_qca.c7
-rw-r--r--drivers/bus/sunxi-rsb.c8
-rw-r--r--drivers/char/tpm/tpm-interface.c4
-rw-r--r--drivers/char/tpm/xen-tpmfront.c3
-rw-r--r--drivers/clk/ingenic/jz4760-cgu.c18
-rw-r--r--drivers/clk/microchip/clk-mpfs-ccc.c10
-rw-r--r--drivers/comedi/drivers/adv_pci1760.c2
-rw-r--r--drivers/cpufreq/amd-pstate.c1
-rw-r--r--drivers/cpufreq/apple-soc-cpufreq.c3
-rw-r--r--drivers/cpufreq/armada-37xx-cpufreq.c2
-rw-r--r--drivers/cpufreq/cppc_cpufreq.c11
-rw-r--r--drivers/cpufreq/cpufreq-dt-platdev.c2
-rw-r--r--drivers/cpufreq/qcom-cpufreq-hw.c46
-rw-r--r--drivers/crypto/atmel-ecc.c4
-rw-r--r--drivers/crypto/atmel-i2c.c4
-rw-r--r--drivers/crypto/atmel-i2c.h2
-rw-r--r--drivers/crypto/caam/blob_gen.c2
-rw-r--r--drivers/crypto/virtio/virtio_crypto_skcipher_algs.c3
-rw-r--r--drivers/cxl/acpi.c1
-rw-r--r--drivers/cxl/core/pmem.c42
-rw-r--r--drivers/cxl/core/region.c12
-rw-r--r--drivers/cxl/pci.c7
-rw-r--r--drivers/cxl/pmem.c24
-rw-r--r--drivers/dax/super.c2
-rw-r--r--drivers/dma-buf/dma-buf-sysfs-stats.c7
-rw-r--r--drivers/dma-buf/dma-buf-sysfs-stats.h4
-rw-r--r--drivers/dma-buf/dma-buf.c82
-rw-r--r--drivers/dma-buf/dma-fence.c2
-rw-r--r--drivers/dma/dmaengine.c7
-rw-r--r--drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c6
-rw-r--r--drivers/dma/idxd/device.c16
-rw-r--r--drivers/dma/imx-sdma.c4
-rw-r--r--drivers/dma/lgm/lgm-dma.c10
-rw-r--r--drivers/dma/ptdma/ptdma-dev.c7
-rw-r--r--drivers/dma/ptdma/ptdma.h2
-rw-r--r--drivers/dma/qcom/gpi.c1
-rw-r--r--drivers/dma/tegra186-gpc-dma.c1
-rw-r--r--drivers/dma/tegra210-adma.c2
-rw-r--r--drivers/dma/ti/k3-udma.c5
-rw-r--r--drivers/dma/xilinx/xilinx_dma.c4
-rw-r--r--drivers/edac/edac_device.c30
-rw-r--r--drivers/edac/edac_module.h2
-rw-r--r--drivers/edac/highbank_mc_edac.c7
-rw-r--r--drivers/edac/qcom_edac.c5
-rw-r--r--drivers/firewire/core-cdev.c43
-rw-r--r--drivers/firewire/core-transaction.c53
-rw-r--r--drivers/firewire/core.h9
-rw-r--r--drivers/firmware/arm_scmi/driver.c2
-rw-r--r--drivers/firmware/arm_scmi/shmem.c9
-rw-r--r--drivers/firmware/arm_scmi/virtio.c7
-rw-r--r--drivers/firmware/efi/efi.c11
-rw-r--r--drivers/firmware/efi/libstub/arm64.c9
-rw-r--r--drivers/firmware/efi/memattr.c2
-rw-r--r--drivers/firmware/efi/runtime-wrappers.c1
-rw-r--r--drivers/firmware/google/coreboot_table.c9
-rw-r--r--drivers/firmware/google/coreboot_table.h1
-rw-r--r--drivers/firmware/google/gsmi.c7
-rw-r--r--drivers/firmware/psci/psci.c3
-rw-r--r--drivers/fpga/intel-m10-bmc-sec-update.c17
-rw-r--r--drivers/fpga/stratix10-soc.c4
-rw-r--r--drivers/gpio/gpio-eic-sprd.c23
-rw-r--r--drivers/gpio/gpio-ep93xx.c38
-rw-r--r--drivers/gpio/gpio-mxc.c16
-rw-r--r--drivers/gpio/gpio-pca953x.c3
-rw-r--r--drivers/gpio/gpio-pmic-eic-sprd.c29
-rw-r--r--drivers/gpio/gpio-sifive.c1
-rw-r--r--drivers/gpio/gpio-sprd.c9
-rw-r--r--drivers/gpio/gpiolib-acpi.c20
-rw-r--r--drivers/gpio/gpiolib.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c54
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c44
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_object.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c2
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c15
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/imu_v11_0.c1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/mes_v11_0.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c8
-rw-r--r--drivers/gpu/drm/amd/amdgpu/soc21.c5
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_svm.c9
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_topology.c2
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c115
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c51
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c6
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c4
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_link.c14
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c5
-rw-r--r--drivers/gpu/drm/amd/display/dc/dcn32/dcn32_init.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c6
-rw-r--r--drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c12
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_pm.c8
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c14
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h5
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h29
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h4
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c7
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c8
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c8
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c2
-rw-r--r--drivers/gpu/drm/display/drm_dp_mst_topology.c4
-rw-r--r--drivers/gpu/drm/drm_buddy.c81
-rw-r--r--drivers/gpu/drm/drm_client.c33
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c7
-rw-r--r--drivers/gpu/drm/drm_fbdev_generic.c15
-rw-r--r--drivers/gpu/drm/drm_panel_orientation_quirks.c6
-rw-r--r--drivers/gpu/drm/drm_vma_manager.c76
-rw-r--r--drivers/gpu/drm/i915/display/intel_bios.c33
-rw-r--r--drivers/gpu/drm/i915/display/intel_cdclk.c2
-rw-r--r--drivers/gpu/drm/i915/display/intel_dsi_vbt.c94
-rw-r--r--drivers/gpu/drm/i915/display/intel_fbdev.c12
-rw-r--r--drivers/gpu/drm/i915/display/skl_universal_plane.c2
-rw-r--r--drivers/gpu/drm/i915/display/skl_watermark.c3
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_context.c38
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c73
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_mman.c4
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_shmem.c2
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_tiling.c9
-rw-r--r--drivers/gpu/drm/i915/gem/selftests/huge_pages.c8
-rw-r--r--drivers/gpu/drm/i915/gt/intel_context.c4
-rw-r--r--drivers/gpu/drm/i915/gt/intel_context.h3
-rw-r--r--drivers/gpu/drm/i915/gt/intel_engine.h4
-rw-r--r--drivers/gpu/drm/i915/gt/intel_engine_cs.c74
-rw-r--r--drivers/gpu/drm/i915/gt/intel_execlists_submission.c27
-rw-r--r--drivers/gpu/drm/i915/gt/intel_execlists_submission.h4
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt.c8
-rw-r--r--drivers/gpu/drm/i915/gt/intel_gt_regs.h14
-rw-r--r--drivers/gpu/drm/i915/gt/intel_lrc.c37
-rw-r--r--drivers/gpu/drm/i915/gt/intel_reset.c34
-rw-r--r--drivers/gpu/drm/i915/gt/intel_workarounds.c14
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c14
-rw-r--r--drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c42
-rw-r--r--drivers/gpu/drm/i915/gvt/debugfs.c36
-rw-r--r--drivers/gpu/drm/i915/gvt/dmabuf.c3
-rw-r--r--drivers/gpu/drm/i915/gvt/gtt.c21
-rw-r--r--drivers/gpu/drm/i915/gvt/gvt.h15
-rw-r--r--drivers/gpu/drm/i915/gvt/interrupt.c2
-rw-r--r--drivers/gpu/drm/i915/gvt/kvmgt.c35
-rw-r--r--drivers/gpu/drm/i915/gvt/scheduler.c4
-rw-r--r--drivers/gpu/drm/i915/gvt/vgpu.c12
-rw-r--r--drivers/gpu/drm/i915/i915_driver.c5
-rw-r--r--drivers/gpu/drm/i915/i915_gem_evict.c37
-rw-r--r--drivers/gpu/drm/i915/i915_gem_evict.h4
-rw-r--r--drivers/gpu/drm/i915/i915_gpu_error.c33
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c3
-rw-r--r--drivers/gpu/drm/i915/i915_pci.c4
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h1
-rw-r--r--drivers/gpu/drm/i915/i915_switcheroo.c6
-rw-r--r--drivers/gpu/drm/i915/i915_vma.c4
-rw-r--r--drivers/gpu/drm/i915/selftests/i915_gem_evict.c4
-rw-r--r--drivers/gpu/drm/i915/selftests/intel_scheduler_helpers.c3
-rw-r--r--drivers/gpu/drm/imx/ipuv3-plane.c14
-rw-r--r--drivers/gpu/drm/meson/meson_viu.c5
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gmu.c15
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gpu.c7
-rw-r--r--drivers/gpu/drm/msm/adreno/a6xx_gpu.h1
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_device.c5
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.c4
-rw-r--r--drivers/gpu/drm/msm/adreno/adreno_gpu.h10
-rw-r--r--drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c3
-rw-r--r--drivers/gpu/drm/msm/dp/dp_aux.c4
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.c12
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c2
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.c2
-rw-r--r--drivers/gpu/drm/msm/msm_gpu.h12
-rw-r--r--drivers/gpu/drm/msm/msm_mdss.c6
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h1
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.c613
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/core/firmware.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/base.c10
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/falcon/gm200.c14
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c23
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c5
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/fb/tu102.c55
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c2
-rw-r--r--drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c16
-rw-r--r--drivers/gpu/drm/panfrost/Kconfig3
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_drv.c27
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gem.c16
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_gem.h5
-rw-r--r--drivers/gpu/drm/scheduler/sched_entity.c2
-rw-r--r--drivers/gpu/drm/scheduler/sched_main.c4
-rw-r--r--drivers/gpu/drm/solomon/ssd130x.c18
-rw-r--r--drivers/gpu/drm/tests/Makefile2
-rw-r--r--drivers/gpu/drm/tests/drm_mm_test.c6
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_util.c2
-rw-r--r--drivers/gpu/drm/vc4/vc4_bo.c6
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.c3
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_ioctl.c24
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_object.c6
-rw-r--r--drivers/gpu/drm/vmwgfx/ttm_object.c41
-rw-r--r--drivers/gpu/drm/vmwgfx/ttm_object.h14
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_bo.c38
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.h18
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c176
-rw-r--r--[-rwxr-xr-x]drivers/gpu/drm/vmwgfx/vmwgfx_msg_arm64.h0
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_resource.c33
-rw-r--r--drivers/gpu/drm/xen/xen_drm_front.c3
-rw-r--r--drivers/hid/amd-sfh-hid/amd_sfh_client.c15
-rw-r--r--drivers/hid/amd-sfh-hid/amd_sfh_hid.h1
-rw-r--r--drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c2
-rw-r--r--drivers/hid/hid-betopff.c17
-rw-r--r--drivers/hid/hid-bigbenff.c5
-rw-r--r--drivers/hid/hid-core.c7
-rw-r--r--drivers/hid/hid-elecom.c16
-rw-r--r--drivers/hid/hid-ids.h7
-rw-r--r--drivers/hid/hid-input.c4
-rw-r--r--drivers/hid/hid-logitech-hidpp.c3
-rw-r--r--drivers/hid/hid-playstation.c63
-rw-r--r--drivers/hid/hid-quirks.c4
-rw-r--r--drivers/hid/hid-uclogic-core.c2
-rw-r--r--drivers/hid/hid-uclogic-params.c2
-rw-r--r--drivers/hid/intel-ish-hid/ishtp/dma-if.c10
-rw-r--r--drivers/hv/hv_balloon.c2
-rw-r--r--drivers/i2c/busses/i2c-axxia.c2
-rw-r--r--drivers/i2c/busses/i2c-designware-common.c9
-rw-r--r--drivers/i2c/busses/i2c-designware-pcidrv.c2
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c20
-rw-r--r--drivers/i2c/busses/i2c-mxs.c4
-rw-r--r--drivers/i2c/busses/i2c-rk3x.c44
-rw-r--r--drivers/iio/accel/hid-sensor-accel-3d.c1
-rw-r--r--drivers/iio/adc/berlin2-adc.c4
-rw-r--r--drivers/iio/adc/imx8qxp-adc.c11
-rw-r--r--drivers/iio/adc/stm32-dfsdm-adc.c1
-rw-r--r--drivers/iio/adc/twl6030-gpadc.c32
-rw-r--r--drivers/iio/adc/xilinx-ams.c2
-rw-r--r--drivers/iio/gyro/hid-sensor-gyro-3d.c1
-rw-r--r--drivers/iio/imu/fxos8700_core.c111
-rw-r--r--drivers/iio/imu/st_lsm6dsx/Kconfig1
-rw-r--r--drivers/iio/light/cm32181.c9
-rw-r--r--drivers/infiniband/core/umem_dmabuf.c8
-rw-r--r--drivers/infiniband/core/verbs.c7
-rw-r--r--drivers/infiniband/hw/hfi1/file_ops.c7
-rw-r--r--drivers/infiniband/hw/hfi1/user_exp_rcv.c209
-rw-r--r--drivers/infiniband/hw/hfi1/user_exp_rcv.h3
-rw-r--r--drivers/infiniband/hw/irdma/cm.c3
-rw-r--r--drivers/infiniband/hw/mana/qp.c2
-rw-r--r--drivers/infiniband/hw/mlx5/counters.c6
-rw-r--r--drivers/infiniband/hw/mlx5/qp.c49
-rw-r--r--drivers/infiniband/hw/usnic/usnic_uiom.c8
-rw-r--r--drivers/infiniband/sw/rxe/rxe_param.h10
-rw-r--r--drivers/infiniband/sw/rxe/rxe_pool.c22
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c8
-rw-r--r--drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c3
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.h8
-rw-r--r--drivers/input/misc/xen-kbdfront.c5
-rw-r--r--drivers/input/mouse/synaptics.c1
-rw-r--r--drivers/input/serio/i8042-acpipnpio.h7
-rw-r--r--drivers/interconnect/qcom/icc-rpm.c2
-rw-r--r--drivers/interconnect/qcom/msm8996.c19
-rw-r--r--drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c4
-rw-r--r--drivers/iommu/arm/arm-smmu/arm-smmu.c32
-rw-r--r--drivers/iommu/iommu.c8
-rw-r--r--drivers/iommu/iova.c4
-rw-r--r--drivers/iommu/mtk_iommu_v1.c4
-rw-r--r--drivers/md/bcache/bcache_ondisk.h3
-rw-r--r--drivers/md/bcache/journal.c3
-rw-r--r--drivers/md/dm.c2
-rw-r--r--drivers/md/md.c6
-rw-r--r--drivers/media/common/videobuf2/videobuf2-core.c5
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-api.c2
-rw-r--r--drivers/memory/atmel-sdramc.c6
-rw-r--r--drivers/memory/mvebu-devbus.c3
-rw-r--r--drivers/memory/omap-gpmc.c3
-rw-r--r--drivers/memory/tegra/tegra186.c36
-rw-r--r--drivers/mfd/wm97xx-core.c4
-rw-r--r--drivers/misc/fastrpc.c83
-rw-r--r--drivers/misc/mei/bus.c12
-rw-r--r--drivers/misc/mei/hw-me-regs.h2
-rw-r--r--drivers/misc/mei/pci-me.c2
-rw-r--r--drivers/misc/vmw_vmci/vmci_guest.c49
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c22
-rw-r--r--drivers/mmc/host/sunxi-mmc.c8
-rw-r--r--drivers/mtd/parsers/scpart.c2
-rw-r--r--drivers/mtd/parsers/tplink_safeloader.c4
-rw-r--r--drivers/mtd/spi-nor/core.c1
-rw-r--r--drivers/net/bonding/bond_3ad.c1
-rw-r--r--drivers/net/bonding/bond_debugfs.c2
-rw-r--r--drivers/net/bonding/bond_main.c8
-rw-r--r--drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c1
-rw-r--r--drivers/net/dsa/Kconfig7
-rw-r--r--drivers/net/dsa/microchip/ksz9477.c4
-rw-r--r--drivers/net/dsa/microchip/ksz9477_i2c.c2
-rw-r--r--drivers/net/dsa/mt7530.c26
-rw-r--r--drivers/net/dsa/mv88e6xxx/Kconfig4
-rw-r--r--drivers/net/dsa/qca/qca8k-8xxx.c164
-rw-r--r--drivers/net/dsa/qca/qca8k.h5
-rw-r--r--drivers/net/ethernet/adi/adin1110.c2
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_com.c29
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_ethtool.c6
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_netdev.c83
-rw-r--r--drivers/net/ethernet/amazon/ena/ena_netdev.h17
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-dev.c23
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-drv.c3
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-i2c.c4
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe-mdio.c28
-rw-r--r--drivers/net/ethernet/amd/xgbe/xgbe.h2
-rw-r--r--drivers/net/ethernet/broadcom/bcm63xx_enet.c6
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.c31
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.h15
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c13
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h9
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c20
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h6
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c8
-rw-r--r--drivers/net/ethernet/cadence/macb_main.c40
-rw-r--r--drivers/net/ethernet/engleder/tsnep_main.c15
-rw-r--r--drivers/net/ethernet/freescale/dpaa/dpaa_eth.c6
-rw-r--r--drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c9
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc.c4
-rw-r--r--drivers/net/ethernet/freescale/enetc/enetc_ierb.c6
-rw-r--r--drivers/net/ethernet/freescale/fec_main.c2
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_dtsec.c2
-rw-r--r--drivers/net/ethernet/freescale/fman/fman_memac.c3
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3_enet.c10
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c132
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h7
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c71
-rw-r--r--drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c5
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf.h2
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_ethtool.c10
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_main.c115
-rw-r--r--drivers/net/ethernet/intel/iavf/iavf_virtchnl.c10
-rw-r--r--drivers/net/ethernet/intel/ice/ice.h2
-rw-r--r--drivers/net/ethernet/intel/ice/ice_common.c9
-rw-r--r--drivers/net/ethernet/intel/ice/ice_dcb_lib.c23
-rw-r--r--drivers/net/ethernet/intel/ice/ice_dcb_lib.h4
-rw-r--r--drivers/net/ethernet/intel/ice/ice_ethtool.c28
-rw-r--r--drivers/net/ethernet/intel/ice/ice_gnss.c24
-rw-r--r--drivers/net/ethernet/intel/ice/ice_lib.c3
-rw-r--r--drivers/net/ethernet/intel/ice/ice_main.c32
-rw-r--r--drivers/net/ethernet/intel/ice/ice_switch.c2
-rw-r--r--drivers/net/ethernet/intel/ice/ice_tc_lib.c2
-rw-r--r--drivers/net/ethernet/intel/ice/ice_vf_mbx.c21
-rw-r--r--drivers/net/ethernet/intel/ice/ice_vf_vsi_vlan_ops.c16
-rw-r--r--drivers/net/ethernet/intel/ice/ice_xsk.c2
-rw-r--r--drivers/net/ethernet/intel/igc/igc_defines.h2
-rw-r--r--drivers/net/ethernet/intel/igc/igc_main.c25
-rw-r--r--drivers/net/ethernet/intel/igc/igc_ptp.c24
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c14
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cgx.c4
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/cgx.h1
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c35
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c19
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h2
-rw-r--r--drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c2
-rw-r--r--drivers/net/ethernet/mediatek/mtk_eth_soc.c30
-rw-r--r--drivers/net/ethernet/mediatek/mtk_eth_soc.h4
-rw-r--r--drivers/net/ethernet/mediatek/mtk_ppe.c3
-rw-r--r--drivers/net/ethernet/mediatek/mtk_ppe.h1
-rw-r--r--drivers/net/ethernet/mediatek/mtk_sgmii.c46
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/cmd.c13
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/debugfs.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/devlink.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ecpf.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/htb.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/params.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c4
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c7
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c9
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_geneve.c5
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h7
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c12
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c19
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_fs.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_main.c95
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rep.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_rx.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_tc.c8
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/acl/egress_lgcy.c7
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_lgcy.c33
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c18
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.c31
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch.h6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/health.c7
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c29
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c42
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h6
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c18
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c1
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/main.c26
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c37
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/qos.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/qos.h2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/sriov.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c24
-rw-r--r--drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c2
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c6
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_main.c13
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_port.c2
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c3
-rw-r--r--drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c3
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_main.c2
-rw-r--r--drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c4
-rw-r--r--drivers/net/ethernet/microsoft/mana/gdma_main.c46
-rw-r--r--drivers/net/ethernet/mscc/ocelot_flower.c24
-rw-r--r--drivers/net/ethernet/mscc/ocelot_ptp.c8
-rw-r--r--drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c8
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net.h7
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_common.c61
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c194
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfp_port.h12
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_dev.c9
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_dev.h12
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_lif.c68
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_lif.h2
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_main.c29
-rw-r--r--drivers/net/ethernet/pensando/ionic/ionic_txrx.c87
-rw-r--r--drivers/net/ethernet/qlogic/qed/qed_debug.c28
-rw-r--r--drivers/net/ethernet/qlogic/qede/qede_fp.c7
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c8
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h10
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c8
-rw-r--r--drivers/net/ethernet/realtek/r8169_main.c63
-rw-r--r--drivers/net/ethernet/renesas/ravb_main.c10
-rw-r--r--drivers/net/ethernet/renesas/rswitch.c32
-rw-r--r--drivers/net/ethernet/renesas/rswitch.h12
-rw-r--r--drivers/net/ethernet/sfc/efx.c5
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c26
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c2
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/dwmac5.c14
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c8
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c8
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c5
-rw-r--r--drivers/net/hyperv/netvsc.c11
-rw-r--r--drivers/net/ipa/data/ipa_data-v4.7.c2
-rw-r--r--drivers/net/ipa/ipa_interrupt.c13
-rw-r--r--drivers/net/ipa/ipa_interrupt.h16
-rw-r--r--drivers/net/ipa/ipa_power.c17
-rw-r--r--drivers/net/mdio/mdio-mux-meson-g12a.c23
-rw-r--r--drivers/net/phy/dp83822.c6
-rw-r--r--drivers/net/phy/mdio_bus.c7
-rw-r--r--drivers/net/phy/meson-gxl.c4
-rw-r--r--drivers/net/phy/phy_device.c2
-rw-r--r--drivers/net/phy/phylink.c5
-rw-r--r--drivers/net/phy/xilinx_gmii2rgmii.c1
-rw-r--r--drivers/net/team/team.c2
-rw-r--r--drivers/net/usb/cdc_ether.c6
-rw-r--r--drivers/net/usb/plusb.c4
-rw-r--r--drivers/net/usb/r8152.c1
-rw-r--r--drivers/net/usb/rndis_host.c3
-rw-r--r--drivers/net/usb/sr9700.c2
-rw-r--r--drivers/net/veth.c5
-rw-r--r--drivers/net/virtio_net.c14
-rw-r--r--drivers/net/vmxnet3/vmxnet3_drv.c8
-rw-r--r--drivers/net/vrf.c6
-rw-r--r--drivers/net/vxlan/vxlan_core.c19
-rw-r--r--drivers/net/wan/fsl_ucc_hdlc.c6
-rw-r--r--drivers/net/wireless/ath/ath9k/htc.h14
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c37
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c5
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c2
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/acpi.c5
-rw-r--r--drivers/net/wireless/mediatek/mt76/dma.c131
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7915/mmio.c7
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7996/Kconfig1
-rw-r--r--drivers/net/wireless/mediatek/mt76/tx.c7
-rw-r--r--drivers/net/wireless/rndis_wlan.c19
-rw-r--r--drivers/net/wireless/ti/Makefile3
-rw-r--r--drivers/net/wwan/t7xx/t7xx_hif_dpmaif.c11
-rw-r--r--drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c29
-rw-r--r--drivers/net/wwan/t7xx/t7xx_netdev.c16
-rw-r--r--drivers/net/wwan/t7xx/t7xx_pci.c2
-rw-r--r--drivers/net/xen-netback/xenbus.c3
-rw-r--r--drivers/net/xen-netfront.c4
-rw-r--r--drivers/nfc/pn533/usb.c44
-rw-r--r--drivers/nvdimm/Kconfig19
-rw-r--r--drivers/nvdimm/nd.h2
-rw-r--r--drivers/nvdimm/pfn_devs.c42
-rw-r--r--drivers/nvme/host/apple.c26
-rw-r--r--drivers/nvme/host/auth.c16
-rw-r--r--drivers/nvme/host/core.c39
-rw-r--r--drivers/nvme/host/fc.c18
-rw-r--r--drivers/nvme/host/ioctl.c138
-rw-r--r--drivers/nvme/host/multipath.c2
-rw-r--r--drivers/nvme/host/nvme.h2
-rw-r--r--drivers/nvme/host/pci.c61
-rw-r--r--drivers/nvme/target/admin-cmd.c37
-rw-r--r--drivers/nvme/target/fc.c4
-rw-r--r--drivers/nvme/target/passthru.c11
-rw-r--r--drivers/nvmem/brcm_nvram.c3
-rw-r--r--drivers/nvmem/core.c60
-rw-r--r--drivers/nvmem/qcom-spmi-sdam.c1
-rw-r--r--drivers/nvmem/sunxi_sid.c15
-rw-r--r--drivers/of/address.c21
-rw-r--r--drivers/of/fdt.c66
-rw-r--r--drivers/of/platform.c12
-rw-r--r--drivers/parisc/pdc_stable.c9
-rw-r--r--drivers/pci/controller/dwc/Kconfig2
-rw-r--r--drivers/pci/pci.c7
-rw-r--r--drivers/pci/pci.h4
-rw-r--r--drivers/pci/pcie/aspm.c111
-rw-r--r--drivers/pci/xen-pcifront.c4
-rw-r--r--drivers/perf/arm-cmn.c7
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8m-pcie.c2
-rw-r--r--drivers/phy/phy-can-transceiver.c5
-rw-r--r--drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c13
-rw-r--r--drivers/phy/renesas/r8a779f0-ether-serdes.c2
-rw-r--r--drivers/phy/rockchip/phy-rockchip-inno-usb2.c4
-rw-r--r--drivers/phy/sunplus/phy-sunplus-usb2.c3
-rw-r--r--drivers/phy/ti/Kconfig4
-rw-r--r--drivers/pinctrl/aspeed/pinctrl-aspeed.c13
-rw-r--r--drivers/pinctrl/intel/pinctrl-intel.c16
-rw-r--r--drivers/pinctrl/mediatek/pinctrl-mt8195.c4
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-ab8500.c3
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-ab8505.c3
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-abx500.c32
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-abx500.h4
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-nomadik-db8500.c3
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-nomadik-stn8815.c3
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-nomadik.c32
-rw-r--r--drivers/pinctrl/nomadik/pinctrl-nomadik.h5
-rw-r--r--drivers/pinctrl/pinctrl-amd.c1
-rw-r--r--drivers/pinctrl/pinctrl-rockchip.c31
-rw-r--r--drivers/pinctrl/pinctrl-single.c2
-rw-r--r--drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c2
-rw-r--r--drivers/pinctrl/sunplus/sppctl.c7
-rw-r--r--drivers/platform/surface/aggregator/controller.c4
-rw-r--r--drivers/platform/surface/aggregator/ssh_request_layer.c14
-rw-r--r--drivers/platform/x86/amd/Kconfig1
-rw-r--r--drivers/platform/x86/amd/pmc.c60
-rw-r--r--drivers/platform/x86/amd/pmf/auto-mode.c9
-rw-r--r--drivers/platform/x86/amd/pmf/cnqf.c14
-rw-r--r--drivers/platform/x86/amd/pmf/core.c32
-rw-r--r--drivers/platform/x86/amd/pmf/pmf.h3
-rw-r--r--drivers/platform/x86/amd/pmf/sps.c28
-rw-r--r--drivers/platform/x86/apple-gmux.c93
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c15
-rw-r--r--drivers/platform/x86/asus-wmi.c24
-rw-r--r--drivers/platform/x86/asus-wmi.h1
-rw-r--r--drivers/platform/x86/dell/dell-wmi-base.c3
-rw-r--r--drivers/platform/x86/dell/dell-wmi-privacy.c41
-rw-r--r--drivers/platform/x86/gigabyte-wmi.c1
-rw-r--r--drivers/platform/x86/hp/hp-wmi.c6
-rw-r--r--drivers/platform/x86/ideapad-laptop.c6
-rw-r--r--drivers/platform/x86/intel/int3472/clk_and_regulator.c3
-rw-r--r--drivers/platform/x86/intel/int3472/discrete.c4
-rw-r--r--drivers/platform/x86/intel/pmc/core.c1
-rw-r--r--drivers/platform/x86/simatic-ipc.c3
-rw-r--r--drivers/platform/x86/sony-laptop.c21
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c34
-rw-r--r--drivers/platform/x86/touchscreen_dmi.c34
-rw-r--r--drivers/regulator/da9211-regulator.c11
-rw-r--r--drivers/regulator/qcom-rpmh-regulator.c2
-rw-r--r--drivers/reset/Kconfig2
-rw-r--r--drivers/reset/reset-uniphier-glue.c4
-rw-r--r--drivers/rtc/rtc-efi.c48
-rw-r--r--drivers/rtc/rtc-sunplus.c4
-rw-r--r--drivers/s390/block/dcssblk.c2
-rw-r--r--drivers/s390/net/qeth_core_sys.c12
-rw-r--r--drivers/scsi/device_handler/scsi_dh_alua.c5
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_main.c6
-rw-r--r--drivers/scsi/hpsa.c2
-rw-r--r--drivers/scsi/iscsi_tcp.c20
-rw-r--r--drivers/scsi/libiscsi.c38
-rw-r--r--drivers/scsi/libsas/sas_ata.c3
-rw-r--r--drivers/scsi/mpi3mr/Makefile2
-rw-r--r--drivers/scsi/mpi3mr/mpi3mr_fw.c3
-rw-r--r--drivers/scsi/mpt3sas/mpt3sas_base.c3
-rw-r--r--drivers/scsi/scsi.c2
-rw-r--r--drivers/scsi/scsi_debug.c2
-rw-r--r--drivers/scsi/scsi_error.c5
-rw-r--r--drivers/scsi/scsi_scan.c7
-rw-r--r--drivers/scsi/scsi_sysfs.c2
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c50
-rw-r--r--drivers/scsi/storvsc_drv.c3
-rw-r--r--drivers/scsi/xen-scsifront.c4
-rw-r--r--drivers/soc/imx/imx8mp-blk-ctrl.c7
-rw-r--r--drivers/soc/imx/soc-imx8m.c4
-rw-r--r--drivers/soc/qcom/apr.c3
-rw-r--r--drivers/soc/qcom/cpr.c6
-rw-r--r--drivers/soundwire/stream.c4
-rw-r--r--drivers/spi/spi-cadence-xspi.c5
-rw-r--r--drivers/spi/spi-dw-core.c2
-rw-r--r--drivers/spi/spi.c2
-rw-r--r--drivers/spi/spidev.c56
-rw-r--r--drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h2
-rw-r--r--drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h4
-rw-r--r--drivers/target/target_core_tmr.c4
-rw-r--r--drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c28
-rw-r--r--drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h1
-rw-r--r--drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c4
-rw-r--r--drivers/thermal/thermal_core.c13
-rw-r--r--drivers/thunderbolt/retimer.c20
-rw-r--r--drivers/thunderbolt/tb.c20
-rw-r--r--drivers/thunderbolt/tunnel.c2
-rw-r--r--drivers/thunderbolt/xdomain.c17
-rw-r--r--drivers/tty/hvc/hvc_xen.c50
-rw-r--r--drivers/tty/serial/8250/8250_dma.c21
-rw-r--r--drivers/tty/serial/8250/8250_exar.c14
-rw-r--r--drivers/tty/serial/amba-pl011.c8
-rw-r--r--drivers/tty/serial/atmel_serial.c8
-rw-r--r--drivers/tty/serial/kgdboc.c20
-rw-r--r--drivers/tty/serial/pch_uart.c2
-rw-r--r--drivers/tty/serial/qcom_geni_serial.c32
-rw-r--r--drivers/tty/serial/serial_core.c5
-rw-r--r--drivers/tty/serial/stm32-usart.c33
-rw-r--r--drivers/tty/vt/vc_screen.c9
-rw-r--r--drivers/ufs/core/ufshcd.c55
-rw-r--r--drivers/usb/cdns3/cdns3-gadget.c12
-rw-r--r--drivers/usb/chipidea/core.c4
-rw-r--r--drivers/usb/common/ulpi.c2
-rw-r--r--drivers/usb/core/hub.c13
-rw-r--r--drivers/usb/core/quirks.c3
-rw-r--r--drivers/usb/core/usb-acpi.c65
-rw-r--r--drivers/usb/dwc3/Kconfig2
-rw-r--r--drivers/usb/dwc3/dwc3-qcom.c2
-rw-r--r--drivers/usb/dwc3/dwc3-xilinx.c1
-rw-r--r--drivers/usb/dwc3/gadget.c5
-rw-r--r--drivers/usb/fotg210/fotg210-core.c5
-rw-r--r--drivers/usb/fotg210/fotg210-udc.c3
-rw-r--r--drivers/usb/gadget/configfs.c12
-rw-r--r--drivers/usb/gadget/function/f_fs.c9
-rw-r--r--drivers/usb/gadget/function/f_ncm.c4
-rw-r--r--drivers/usb/gadget/function/f_uac2.c1
-rw-r--r--drivers/usb/gadget/function/u_ether.c4
-rw-r--r--drivers/usb/gadget/legacy/inode.c28
-rw-r--r--drivers/usb/gadget/legacy/webcam.c3
-rw-r--r--drivers/usb/gadget/udc/bcm63xx_udc.c1
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c1
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c1
-rw-r--r--drivers/usb/gadget/udc/fusb300_udc.c1
-rw-r--r--drivers/usb/gadget/udc/goku_udc.c1
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c1
-rw-r--r--drivers/usb/gadget/udc/m66592-udc.c1
-rw-r--r--drivers/usb/gadget/udc/max3420_udc.c1
-rw-r--r--drivers/usb/gadget/udc/mv_u3d_core.c1
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c1
-rw-r--r--drivers/usb/gadget/udc/net2272.c1
-rw-r--r--drivers/usb/gadget/udc/net2280.c1
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c1
-rw-r--r--drivers/usb/gadget/udc/pch_udc.c1
-rw-r--r--drivers/usb/gadget/udc/snps_udc_core.c1
-rw-r--r--drivers/usb/host/ehci-fsl.c2
-rw-r--r--drivers/usb/host/xen-hcd.c4
-rw-r--r--drivers/usb/host/xhci-pci.c45
-rw-r--r--drivers/usb/host/xhci-ring.c5
-rw-r--r--drivers/usb/host/xhci.c18
-rw-r--r--drivers/usb/host/xhci.h5
-rw-r--r--drivers/usb/misc/iowarrior.c2
-rw-r--r--drivers/usb/misc/onboard_usb_hub.c18
-rw-r--r--drivers/usb/musb/omap2430.c4
-rw-r--r--drivers/usb/serial/cp210x.c1
-rw-r--r--drivers/usb/serial/option.c17
-rw-r--r--drivers/usb/storage/uas-detect.h13
-rw-r--r--drivers/usb/storage/unusual_uas.h7
-rw-r--r--drivers/usb/typec/altmodes/displayport.c30
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c7
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c33
-rw-r--r--drivers/usb/typec/ucsi/ucsi.h1
-rw-r--r--drivers/vdpa/ifcvf/ifcvf_main.c2
-rw-r--r--drivers/vdpa/mlx5/core/mlx5_vdpa.h5
-rw-r--r--drivers/vdpa/mlx5/core/mr.c46
-rw-r--r--drivers/vdpa/mlx5/net/mlx5_vnet.c78
-rw-r--r--drivers/vdpa/vdpa.c11
-rw-r--r--drivers/vdpa/vdpa_sim/vdpa_sim.c7
-rw-r--r--drivers/vdpa/vdpa_sim/vdpa_sim_blk.c4
-rw-r--r--drivers/vdpa/vdpa_sim/vdpa_sim_net.c7
-rw-r--r--drivers/vdpa/vdpa_user/vduse_dev.c3
-rw-r--r--drivers/vdpa/virtio_pci/vp_vdpa.c2
-rw-r--r--drivers/vfio/vfio_iommu_type1.c31
-rw-r--r--drivers/vhost/net.c3
-rw-r--r--drivers/vhost/scsi.c21
-rw-r--r--drivers/vhost/vdpa.c52
-rw-r--r--drivers/vhost/vhost.c7
-rw-r--r--drivers/vhost/vhost.h1
-rw-r--r--drivers/vhost/vringh.c5
-rw-r--r--drivers/vhost/vsock.c9
-rw-r--r--drivers/video/fbdev/Kconfig4
-rw-r--r--drivers/video/fbdev/atmel_lcdfb.c22
-rw-r--r--drivers/video/fbdev/aty/aty128fb.c6
-rw-r--r--drivers/video/fbdev/aty/atyfb_base.c11
-rw-r--r--drivers/video/fbdev/aty/radeon_backlight.c6
-rw-r--r--drivers/video/fbdev/core/fbcon.c7
-rw-r--r--drivers/video/fbdev/core/fbmon.c2
-rw-r--r--drivers/video/fbdev/matrox/matroxfb_base.c4
-rw-r--r--drivers/video/fbdev/mx3fb.c7
-rw-r--r--drivers/video/fbdev/nvidia/nv_backlight.c8
-rw-r--r--drivers/video/fbdev/nvidia/nvidia.c81
-rw-r--r--drivers/video/fbdev/omap/omapfb_main.c5
-rw-r--r--drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c8
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/display-sysfs.c7
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/dsi.c28
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c7
-rw-r--r--drivers/video/fbdev/omap2/omapfb/dss/overlay-sysfs.c3
-rw-r--r--drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c3
-rw-r--r--drivers/video/fbdev/riva/fbdev.c8
-rw-r--r--drivers/video/fbdev/xen-fbfront.c6
-rw-r--r--drivers/virtio/virtio.c12
-rw-r--r--drivers/virtio/virtio_pci_modern.c4
-rw-r--r--drivers/virtio/virtio_ring.c2
-rw-r--r--drivers/w1/w1.c6
-rw-r--r--drivers/w1/w1_int.c5
-rw-r--r--drivers/watchdog/diag288_wdt.c15
-rw-r--r--drivers/xen/pvcalls-back.c3
-rw-r--r--drivers/xen/pvcalls-front.c7
-rw-r--r--drivers/xen/xen-pciback/xenbus.c4
-rw-r--r--drivers/xen/xen-scsiback.c4
-rw-r--r--fs/affs/file.c2
-rw-r--r--fs/afs/cmservice.c6
-rw-r--r--fs/afs/rxrpc.c24
-rw-r--r--fs/binfmt_elf.c4
-rw-r--r--fs/binfmt_elf_fdpic.c4
-rw-r--r--fs/btrfs/backref.c4
-rw-r--r--fs/btrfs/bio.c11
-rw-r--r--fs/btrfs/defrag.c6
-rw-r--r--fs/btrfs/disk-io.c20
-rw-r--r--fs/btrfs/disk-io.h2
-rw-r--r--fs/btrfs/extent-io-tree.c2
-rw-r--r--fs/btrfs/extent-tree.c7
-rw-r--r--fs/btrfs/extent_io.c32
-rw-r--r--fs/btrfs/file.c15
-rw-r--r--fs/btrfs/fs.h6
-rw-r--r--fs/btrfs/inode.c8
-rw-r--r--fs/btrfs/qgroup.c40
-rw-r--r--fs/btrfs/raid56.c16
-rw-r--r--fs/btrfs/send.c6
-rw-r--r--fs/btrfs/space-info.c3
-rw-r--r--fs/btrfs/super.c3
-rw-r--r--fs/btrfs/tree-log.c75
-rw-r--r--fs/btrfs/tree-log.h2
-rw-r--r--fs/btrfs/volumes.c83
-rw-r--r--fs/btrfs/zlib.c2
-rw-r--r--fs/btrfs/zoned.c2
-rw-r--r--fs/ceph/addr.c17
-rw-r--r--fs/ceph/caps.c18
-rw-r--r--fs/ceph/file.c3
-rw-r--r--fs/ceph/locks.c24
-rw-r--r--fs/ceph/mds_client.c36
-rw-r--r--fs/ceph/snap.c36
-rw-r--r--fs/ceph/super.h12
-rw-r--r--fs/cifs/cifsencrypt.c1
-rw-r--r--fs/cifs/connect.c9
-rw-r--r--fs/cifs/dfs.c25
-rw-r--r--fs/cifs/dfs_cache.c244
-rw-r--r--fs/cifs/dfs_cache.h3
-rw-r--r--fs/cifs/file.c4
-rw-r--r--fs/cifs/link.c1
-rw-r--r--fs/cifs/misc.c2
-rw-r--r--fs/cifs/sess.c5
-rw-r--r--fs/cifs/smb1ops.c63
-rw-r--r--fs/cifs/smb2ops.c12
-rw-r--r--fs/cifs/smb2pdu.c32
-rw-r--r--fs/cifs/smbdirect.c1
-rw-r--r--fs/coredump.c48
-rw-r--r--fs/erofs/super.c13
-rw-r--r--fs/erofs/zdata.c12
-rw-r--r--fs/erofs/zmap.c10
-rw-r--r--fs/ext4/xattr.c40
-rw-r--r--fs/f2fs/data.c2
-rw-r--r--fs/f2fs/extent_cache.c34
-rw-r--r--fs/f2fs/file.c2
-rw-r--r--fs/f2fs/segment.c13
-rw-r--r--fs/freevxfs/Kconfig2
-rw-r--r--fs/fscache/volume.c14
-rw-r--r--fs/fuse/acl.c68
-rw-r--r--fs/fuse/dir.c6
-rw-r--r--fs/fuse/fuse_i.h6
-rw-r--r--fs/fuse/inode.c21
-rw-r--r--fs/fuse/xattr.c51
-rw-r--r--fs/gfs2/log.c11
-rw-r--r--fs/hfs/inode.c15
-rw-r--r--fs/ksmbd/auth.c3
-rw-r--r--fs/ksmbd/connection.c24
-rw-r--r--fs/ksmbd/ksmbd_netlink.h3
-rw-r--r--fs/ksmbd/ndr.c8
-rw-r--r--fs/ksmbd/server.h1
-rw-r--r--fs/ksmbd/smb2pdu.c9
-rw-r--r--fs/ksmbd/smb2pdu.h5
-rw-r--r--fs/ksmbd/transport_ipc.c3
-rw-r--r--fs/ksmbd/transport_tcp.c22
-rw-r--r--fs/nfs/dir.c7
-rw-r--r--fs/nfs/filelayout/filelayout.c8
-rw-r--r--fs/nfsd/filecache.c101
-rw-r--r--fs/nfsd/filecache.h5
-rw-r--r--fs/nfsd/netns.h2
-rw-r--r--fs/nfsd/nfs4proc.c8
-rw-r--r--fs/nfsd/nfs4state.c46
-rw-r--r--fs/nfsd/nfs4xdr.c13
-rw-r--r--fs/nfsd/nfsctl.c7
-rw-r--r--fs/nfsd/nfsd.h6
-rw-r--r--fs/nfsd/nfsproc.c4
-rw-r--r--fs/nfsd/nfssvc.c2
-rw-r--r--fs/nfsd/trace.h52
-rw-r--r--fs/nilfs2/btree.c15
-rw-r--r--fs/ntfs3/file.c4
-rw-r--r--fs/overlayfs/copy_up.c6
-rw-r--r--fs/proc/task_mmu.c4
-rw-r--r--fs/squashfs/squashfs_fs.h2
-rw-r--r--fs/squashfs/squashfs_fs_sb.h2
-rw-r--r--fs/squashfs/xattr.h4
-rw-r--r--fs/squashfs/xattr_id.c4
-rw-r--r--fs/udf/inode.c6
-rw-r--r--fs/userfaultfd.c28
-rw-r--r--fs/xfs/libxfs/xfs_btree.c7
-rw-r--r--fs/xfs/xfs_extent_busy.c1
-rw-r--r--fs/xfs/xfs_icache.c10
-rw-r--r--fs/xfs/xfs_ioctl.c4
-rw-r--r--fs/xfs/xfs_iomap.c2
-rw-r--r--fs/xfs/xfs_qm.c2
-rw-r--r--fs/xfs/xfs_reflink.c2
-rw-r--r--fs/zonefs/super.c22
-rw-r--r--include/acpi/acpi_bus.h3
-rw-r--r--include/acpi/video.h2
-rw-r--r--include/asm-generic/vmlinux.lds.h5
-rw-r--r--include/drm/drm_client.h5
-rw-r--r--include/drm/drm_fb_helper.h12
-rw-r--r--include/drm/drm_plane_helper.h1
-rw-r--r--include/drm/drm_vma_manager.h1
-rw-r--r--include/kunit/test.h6
-rw-r--r--include/kvm/arm_vgic.h2
-rw-r--r--include/linux/apple-gmux.h109
-rw-r--r--include/linux/bio.h2
-rw-r--r--include/linux/blkdev.h1
-rw-r--r--include/linux/bpf.h2
-rw-r--r--include/linux/ceph/libceph.h10
-rw-r--r--include/linux/dsa/tag_qca.h4
-rw-r--r--include/linux/efi.h3
-rw-r--r--include/linux/elfcore.h8
-rw-r--r--include/linux/firewire.h5
-rw-r--r--include/linux/firmware/xlnx-zynqmp.h8
-rw-r--r--include/linux/fs.h3
-rw-r--r--include/linux/highmem-internal.h4
-rw-r--r--include/linux/hugetlb.h13
-rw-r--r--include/linux/io_uring_types.h3
-rw-r--r--include/linux/memcontrol.h5
-rw-r--r--include/linux/mlx5/device.h5
-rw-r--r--include/linux/mlx5/driver.h15
-rw-r--r--include/linux/mlx5/mlx5_ifc.h3
-rw-r--r--include/linux/mm.h6
-rw-r--r--include/linux/mm_inline.h3
-rw-r--r--include/linux/mm_types.h2
-rw-r--r--include/linux/mtd/spi-nor.h1
-rw-r--r--include/linux/netfilter/ipset/ip_set.h2
-rw-r--r--include/linux/nvme.h4
-rw-r--r--include/linux/nvmem-provider.h2
-rw-r--r--include/linux/page_ref.h2
-rw-r--r--include/linux/phy.h5
-rw-r--r--include/linux/pktcdvd.h197
-rw-r--r--include/linux/platform_data/asoc-ux500-msp.h20
-rw-r--r--include/linux/platform_data/x86/simatic-ipc.h3
-rw-r--r--include/linux/soc/ti/omap1-io.h4
-rw-r--r--include/linux/soundwire/sdw.h8
-rw-r--r--include/linux/spinlock.h9
-rw-r--r--include/linux/stmmac.h1
-rw-r--r--include/linux/sunrpc/rpc_pipe_fs.h5
-rw-r--r--include/linux/swap.h3
-rw-r--r--include/linux/tpm_eventlog.h4
-rw-r--r--include/linux/trace_events.h1
-rw-r--r--include/linux/usb.h18
-rw-r--r--include/linux/util_macros.h12
-rw-r--r--include/net/af_rxrpc.h3
-rw-r--r--include/net/inet_hashtables.h4
-rw-r--r--include/net/inet_timewait_sock.h5
-rw-r--r--include/net/mac80211.h4
-rw-r--r--include/net/mana/gdma.h3
-rw-r--r--include/net/netfilter/nf_tables.h25
-rw-r--r--include/net/sch_generic.h7
-rw-r--r--include/net/tc_wrapper.h4
-rw-r--r--include/scsi/libiscsi.h2
-rw-r--r--include/scsi/scsi_transport_iscsi.h9
-rw-r--r--include/soc/bcm2835/raspberrypi-firmware.h2
-rw-r--r--include/sound/ac97/codec.h2
-rw-r--r--include/sound/acp63_chip_offset_byte.h751
-rw-r--r--include/sound/core.h6
-rw-r--r--include/sound/cs42l42.h5
-rw-r--r--include/sound/hda_codec.h1
-rw-r--r--include/sound/hdaudio.h2
-rw-r--r--include/sound/simple_card_utils.h3
-rw-r--r--include/sound/soc-component.h2
-rw-r--r--include/sound/soc-dai.h90
-rw-r--r--include/sound/soc-dapm.h1
-rw-r--r--include/sound/soc-dpcm.h2
-rw-r--r--include/sound/soc-topology.h2
-rw-r--r--include/sound/soc.h6
-rw-r--r--include/sound/sof/ipc4/header.h3
-rw-r--r--include/trace/events/btrfs.h2
-rw-r--r--include/trace/events/rxrpc.h166
-rw-r--r--include/trace/stages/stage4_event_fields.h3
-rw-r--r--include/uapi/drm/virtgpu_drm.h1
-rw-r--r--include/uapi/linux/atmbr2684.h2
-rw-r--r--include/uapi/linux/io_uring.h8
-rw-r--r--include/uapi/linux/ip.h1
-rw-r--r--include/uapi/linux/ipv6.h1
-rw-r--r--include/uapi/linux/kvm.h3
-rw-r--r--include/uapi/linux/netfilter/nf_conntrack_sctp.h3
-rw-r--r--include/uapi/linux/netfilter/nfnetlink_cttimeout.h3
-rw-r--r--include/uapi/linux/pktcdvd.h112
-rw-r--r--include/uapi/linux/psci.h4
-rw-r--r--include/uapi/linux/vdpa.h4
-rw-r--r--include/uapi/sound/firewire.h26
-rw-r--r--include/uapi/sound/intel/avs/tokens.h4
-rw-r--r--include/ufs/ufshcd.h2
-rw-r--r--include/xen/xenbus.h2
-rw-r--r--init/Kconfig10
-rw-r--r--init/Makefile1
-rw-r--r--init/version-timestamp.c1
-rw-r--r--io_uring/cancel.c9
-rw-r--r--io_uring/fdinfo.c12
-rw-r--r--io_uring/io-wq.c6
-rw-r--r--io_uring/io_uring.c71
-rw-r--r--io_uring/io_uring.h15
-rw-r--r--io_uring/msg_ring.c130
-rw-r--r--io_uring/net.c11
-rw-r--r--io_uring/poll.c52
-rw-r--r--io_uring/rw.c6
-rw-r--r--kernel/bpf/bpf_lsm.c3
-rw-r--r--kernel/bpf/btf.c4
-rw-r--r--kernel/bpf/hashtab.c4
-rw-r--r--kernel/bpf/memalloc.c2
-rw-r--r--kernel/bpf/offload.c3
-rw-r--r--kernel/bpf/syscall.c24
-rw-r--r--kernel/bpf/task_iter.c39
-rw-r--r--kernel/bpf/trampoline.c4
-rw-r--r--kernel/bpf/verifier.c56
-rw-r--r--kernel/cgroup/cpuset.c48
-rw-r--r--kernel/events/core.c93
-rw-r--r--kernel/futex/syscalls.c11
-rwxr-xr-xkernel/gen_kheaders.sh2
-rw-r--r--kernel/irq/irqdomain.c4
-rw-r--r--kernel/irq/msi.c6
-rw-r--r--kernel/kallsyms_selftest.c21
-rw-r--r--kernel/kcsan/kcsan_test.c7
-rw-r--r--kernel/locking/rtmutex.c60
-rw-r--r--kernel/locking/rtmutex_api.c6
-rw-r--r--kernel/module/main.c26
-rw-r--r--kernel/printk/printk.c2
-rw-r--r--kernel/sched/core.c77
-rw-r--r--kernel/sched/fair.c48
-rw-r--r--kernel/sys.c2
-rw-r--r--kernel/time/tick-oneshot.c4
-rw-r--r--kernel/time/time.c8
-rw-r--r--kernel/time/timekeeping.c8
-rw-r--r--kernel/trace/Kconfig8
-rw-r--r--kernel/trace/bpf_trace.c6
-rw-r--r--kernel/trace/ftrace.c23
-rw-r--r--kernel/trace/rv/rv.c2
-rw-r--r--kernel/trace/trace.c5
-rw-r--r--kernel/trace/trace.h2
-rw-r--r--kernel/trace/trace_events.c39
-rw-r--r--kernel/trace/trace_events_filter.c8
-rw-r--r--kernel/trace/trace_events_hist.c2
-rw-r--r--kernel/trace/trace_export.c3
-rw-r--r--kernel/trace/trace_osnoise.c5
-rw-r--r--kernel/trace/trace_output.c3
-rw-r--r--lib/Kconfig.debug14
-rw-r--r--lib/Kconfig.kcsan2
-rw-r--r--lib/dec_and_lock.c31
-rw-r--r--lib/kunit/assert.c40
-rw-r--r--lib/kunit/string-stream.c4
-rw-r--r--lib/kunit/test.c1
-rw-r--r--lib/lockref.c1
-rw-r--r--lib/maple_tree.c22
-rw-r--r--lib/memcpy_kunit.c2
-rw-r--r--lib/nlattr.c3
-rw-r--r--lib/scatterlist.c25
-rw-r--r--lib/test_maple_tree.c89
-rw-r--r--lib/win_minmax.c2
-rw-r--r--mm/compaction.c1
-rw-r--r--mm/hugetlb.c102
-rw-r--r--mm/kasan/report.c2
-rw-r--r--mm/khugepaged.c38
-rw-r--r--mm/kmemleak.c5
-rw-r--r--mm/madvise.c2
-rw-r--r--mm/memblock.c2
-rw-r--r--mm/memcontrol.c67
-rw-r--r--mm/memory.c14
-rw-r--r--mm/mempolicy.c3
-rw-r--r--mm/mmap.c8
-rw-r--r--mm/mprotect.c8
-rw-r--r--mm/mremap.c25
-rw-r--r--mm/nommu.c9
-rw-r--r--mm/page_alloc.c5
-rw-r--r--mm/shmem.c6
-rw-r--r--mm/slab.c2
-rw-r--r--mm/swapfile.c1
-rw-r--r--mm/vmscan.c9
-rw-r--r--mm/zsmalloc.c237
-rw-r--r--net/9p/trans_xen.c3
-rw-r--r--net/bluetooth/hci_conn.c18
-rw-r--r--net/bluetooth/hci_event.c5
-rw-r--r--net/bluetooth/hci_sync.c19
-rw-r--r--net/bluetooth/iso.c64
-rw-r--r--net/bluetooth/mgmt_util.h2
-rw-r--r--net/bluetooth/rfcomm/sock.c7
-rw-r--r--net/bridge/br_netfilter_hooks.c1
-rw-r--r--net/caif/cfctrl.c6
-rw-r--r--net/can/isotp.c69
-rw-r--r--net/can/j1939/address-claim.c40
-rw-r--r--net/can/j1939/transport.c4
-rw-r--r--net/can/raw.c47
-rw-r--r--net/core/devlink.c9
-rw-r--r--net/core/filter.c7
-rw-r--r--net/core/gro.c14
-rw-r--r--net/core/neighbour.c18
-rw-r--r--net/core/net_namespace.c2
-rw-r--r--net/core/skbuff.c5
-rw-r--r--net/core/sock.c3
-rw-r--r--net/core/sock_map.c61
-rw-r--r--net/ethtool/ioctl.c107
-rw-r--r--net/ethtool/rss.c11
-rw-r--r--net/ipv4/af_inet.c2
-rw-r--r--net/ipv4/fib_semantics.c2
-rw-r--r--net/ipv4/inet_connection_sock.c43
-rw-r--r--net/ipv4/inet_hashtables.c25
-rw-r--r--net/ipv4/inet_timewait_sock.c39
-rw-r--r--net/ipv4/metrics.c2
-rw-r--r--net/ipv4/tcp.c2
-rw-r--r--net/ipv4/tcp_bpf.c4
-rw-r--r--net/ipv4/tcp_ulp.c4
-rw-r--r--net/ipv6/addrconf.c59
-rw-r--r--net/ipv6/af_inet6.c1
-rw-r--r--net/ipv6/ip6_output.c15
-rw-r--r--net/ipv6/raw.c4
-rw-r--r--net/l2tp/l2tp_core.c102
-rw-r--r--net/mac80211/agg-tx.c8
-rw-r--r--net/mac80211/cfg.c7
-rw-r--r--net/mac80211/debugfs_sta.c5
-rw-r--r--net/mac80211/driver-ops.c3
-rw-r--r--net/mac80211/driver-ops.h2
-rw-r--r--net/mac80211/ht.c31
-rw-r--r--net/mac80211/ieee80211_i.h2
-rw-r--r--net/mac80211/iface.c5
-rw-r--r--net/mac80211/rx.c225
-rw-r--r--net/mac80211/tx.c34
-rw-r--r--net/mac80211/util.c42
-rw-r--r--net/mac802154/rx.c1
-rw-r--r--net/mctp/af_mctp.c16
-rw-r--r--net/mctp/route.c34
-rw-r--r--net/mptcp/pm.c25
-rw-r--r--net/mptcp/pm_netlink.c10
-rw-r--r--net/mptcp/pm_userspace.c7
-rw-r--r--net/mptcp/protocol.c31
-rw-r--r--net/mptcp/protocol.h10
-rw-r--r--net/mptcp/sockopt.c11
-rw-r--r--net/mptcp/subflow.c40
-rw-r--r--net/netfilter/ipset/ip_set_bitmap_ip.c4
-rw-r--r--net/netfilter/ipset/ip_set_core.c7
-rw-r--r--net/netfilter/ipset/ip_set_hash_ip.c14
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipmark.c13
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipport.c13
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipportip.c13
-rw-r--r--net/netfilter/ipset/ip_set_hash_ipportnet.c13
-rw-r--r--net/netfilter/ipset/ip_set_hash_net.c17
-rw-r--r--net/netfilter/ipset/ip_set_hash_netiface.c15
-rw-r--r--net/netfilter/ipset/ip_set_hash_netnet.c23
-rw-r--r--net/netfilter/ipset/ip_set_hash_netport.c19
-rw-r--r--net/netfilter/ipset/ip_set_hash_netportnet.c40
-rw-r--r--net/netfilter/nf_conntrack_proto.c7
-rw-r--r--net/netfilter/nf_conntrack_proto_sctp.c167
-rw-r--r--net/netfilter/nf_conntrack_proto_tcp.c15
-rw-r--r--net/netfilter/nf_conntrack_standalone.c16
-rw-r--r--net/netfilter/nf_tables_api.c261
-rw-r--r--net/netfilter/nft_payload.c2
-rw-r--r--net/netfilter/nft_set_rbtree.c332
-rw-r--r--net/netlink/af_netlink.c38
-rw-r--r--net/netrom/af_netrom.c5
-rw-r--r--net/netrom/nr_timer.c1
-rw-r--r--net/nfc/llcp_core.c1
-rw-r--r--net/nfc/netlink.c52
-rw-r--r--net/openvswitch/datapath.c20
-rw-r--r--net/qrtr/ns.c5
-rw-r--r--net/rds/message.c6
-rw-r--r--net/rose/af_rose.c8
-rw-r--r--net/rxrpc/Makefile1
-rw-r--r--net/rxrpc/af_rxrpc.c27
-rw-r--r--net/rxrpc/ar-internal.h212
-rw-r--r--net/rxrpc/call_accept.c57
-rw-r--r--net/rxrpc/call_event.c86
-rw-r--r--net/rxrpc/call_object.c116
-rw-r--r--net/rxrpc/call_state.c69
-rw-r--r--net/rxrpc/conn_client.c709
-rw-r--r--net/rxrpc/conn_event.c382
-rw-r--r--net/rxrpc/conn_object.c67
-rw-r--r--net/rxrpc/conn_service.c1
-rw-r--r--net/rxrpc/input.c175
-rw-r--r--net/rxrpc/insecure.c20
-rw-r--r--net/rxrpc/io_thread.c204
-rw-r--r--net/rxrpc/local_object.c35
-rw-r--r--net/rxrpc/net_ns.c17
-rw-r--r--net/rxrpc/output.c60
-rw-r--r--net/rxrpc/peer_object.c23
-rw-r--r--net/rxrpc/proc.c17
-rw-r--r--net/rxrpc/recvmsg.c270
-rw-r--r--net/rxrpc/rxkad.c356
-rw-r--r--net/rxrpc/rxperf.c17
-rw-r--r--net/rxrpc/security.c53
-rw-r--r--net/rxrpc/sendmsg.c195
-rw-r--r--net/sched/act_mpls.c8
-rw-r--r--net/sched/cls_tcindex.c12
-rw-r--r--net/sched/sch_api.c5
-rw-r--r--net/sched/sch_atm.c5
-rw-r--r--net/sched/sch_cbq.c4
-rw-r--r--net/sched/sch_gred.c2
-rw-r--r--net/sched/sch_htb.c40
-rw-r--r--net/sched/sch_taprio.c2
-rw-r--r--net/sctp/bind_addr.c6
-rw-r--r--net/sctp/transport.c4
-rw-r--r--net/sunrpc/auth_gss/auth_gss.c19
-rw-r--r--net/sunrpc/auth_gss/svcauth_gss.c4
-rw-r--r--net/sunrpc/svc.c6
-rw-r--r--net/sunrpc/svc_xprt.c2
-rw-r--r--net/sunrpc/svcsock.c8
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_transport.c2
-rw-r--r--net/tipc/node.c12
-rw-r--r--net/tls/tls_sw.c2
-rw-r--r--net/x25/af_x25.c6
-rw-r--r--net/xfrm/xfrm_compat.c4
-rw-r--r--net/xfrm/xfrm_input.c3
-rw-r--r--net/xfrm/xfrm_interface_core.c54
-rw-r--r--net/xfrm/xfrm_policy.c14
-rw-r--r--net/xfrm/xfrm_state.c18
-rw-r--r--rust/kernel/print.rs29
-rw-r--r--samples/ftrace/ftrace-direct-multi-modify.c1
-rw-r--r--samples/ftrace/ftrace-direct-multi.c1
-rw-r--r--scripts/Makefile.modinst6
-rw-r--r--scripts/Makefile.modpost17
-rw-r--r--scripts/Makefile.package1
-rw-r--r--scripts/Makefile.vmlinux1
-rw-r--r--[-rwxr-xr-x]scripts/atomic/atomics.tbl0
-rw-r--r--scripts/basic/fixdep.c1
-rw-r--r--scripts/gcc-plugins/gcc-common.h4
-rwxr-xr-xscripts/jobserver-exec19
-rw-r--r--scripts/kconfig/.gitignore2
-rw-r--r--scripts/kconfig/Makefile2
-rw-r--r--scripts/kconfig/mconf.c6
-rwxr-xr-xscripts/package/mkspec5
-rwxr-xr-xscripts/tracing/ftrace-bisect.sh34
-rw-r--r--security/tomoyo/Kconfig4
-rw-r--r--security/tomoyo/Makefile19
-rw-r--r--sound/ac97/bus.c5
-rw-r--r--sound/aoa/fabrics/layout.c3
-rw-r--r--sound/aoa/soundbus/soundbus.h2
-rw-r--r--sound/core/init.c40
-rw-r--r--sound/firewire/amdtp-am824.c60
-rw-r--r--sound/firewire/amdtp-stream-trace.h9
-rw-r--r--sound/firewire/amdtp-stream.c310
-rw-r--r--sound/firewire/amdtp-stream.h34
-rw-r--r--sound/firewire/digi00x/amdtp-dot.c28
-rw-r--r--sound/firewire/fireface/amdtp-ff.c28
-rw-r--r--sound/firewire/fireface/ff-hwdep.c41
-rw-r--r--sound/firewire/fireface/ff-protocol-former.c192
-rw-r--r--sound/firewire/fireface/ff-protocol-latter.c6
-rw-r--r--sound/firewire/fireface/ff-transaction.c17
-rw-r--r--sound/firewire/fireface/ff.c10
-rw-r--r--sound/firewire/fireface/ff.h9
-rw-r--r--sound/firewire/motu/amdtp-motu.c58
-rw-r--r--sound/firewire/motu/motu-command-dsp-message-parser.c11
-rw-r--r--sound/firewire/motu/motu-register-dsp-message-parser.c11
-rw-r--r--sound/firewire/motu/motu.h8
-rw-r--r--sound/firewire/tascam/amdtp-tascam.c28
-rw-r--r--sound/hda/hdac_stream.c7
-rw-r--r--sound/hda/hdac_sysfs.c2
-rw-r--r--sound/pci/hda/Kconfig14
-rw-r--r--sound/pci/hda/cs35l41_hda.c109
-rw-r--r--sound/pci/hda/hda_codec.c13
-rw-r--r--sound/pci/hda/hda_controller.c3
-rw-r--r--sound/pci/hda/hda_controller.h1
-rw-r--r--sound/pci/hda/hda_cs_dsp_ctl.c4
-rw-r--r--sound/pci/hda/hda_intel.c8
-rw-r--r--sound/pci/hda/hda_tegra.c6
-rw-r--r--sound/pci/hda/patch_ca0132.c2
-rw-r--r--sound/ppc/snd_ps3.c5
-rw-r--r--sound/soc/amd/acp/acp-legacy-mach.c13
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c465
-rw-r--r--sound/soc/amd/acp/acp-mach.h4
-rw-r--r--sound/soc/amd/acp/acp-sof-mach.c14
-rw-r--r--sound/soc/amd/ps/acp63.h14
-rw-r--r--sound/soc/amd/ps/pci-ps.c211
-rw-r--r--sound/soc/amd/ps/ps-pdm-dma.c40
-rw-r--r--sound/soc/amd/raven/acp3x-i2s.c8
-rw-r--r--sound/soc/amd/renoir/acp3x-pdm-dma.c8
-rw-r--r--sound/soc/amd/renoir/rn_acp3x.h2
-rw-r--r--sound/soc/amd/yc/acp6x-pdm-dma.c8
-rw-r--r--sound/soc/amd/yc/acp6x.h2
-rw-r--r--sound/soc/atmel/atmel-classd.c6
-rw-r--r--sound/soc/atmel/atmel-pdmic.c6
-rw-r--r--sound/soc/atmel/mchp-pdmc.c145
-rw-r--r--sound/soc/atmel/mchp-spdifrx.c554
-rw-r--r--sound/soc/atmel/mchp-spdiftx.c8
-rw-r--r--sound/soc/cirrus/ep93xx-ac97.c4
-rw-r--r--sound/soc/cirrus/ep93xx-i2s.c6
-rw-r--r--sound/soc/codecs/88pm860x-codec.c6
-rw-r--r--sound/soc/codecs/Kconfig69
-rw-r--r--sound/soc/codecs/Makefile19
-rw-r--r--sound/soc/codecs/ac97.c6
-rw-r--r--sound/soc/codecs/adau7002.c6
-rw-r--r--sound/soc/codecs/aw88395/aw88395.c579
-rw-r--r--sound/soc/codecs/aw88395/aw88395.h58
-rw-r--r--sound/soc/codecs/aw88395/aw88395_data_type.h142
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.c1748
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.h194
-rw-r--r--sound/soc/codecs/aw88395/aw88395_lib.c1066
-rw-r--r--sound/soc/codecs/aw88395/aw88395_lib.h92
-rw-r--r--sound/soc/codecs/aw88395/aw88395_reg.h383
-rw-r--r--sound/soc/codecs/bt-sco.c6
-rw-r--r--sound/soc/codecs/cq93vc.c6
-rw-r--r--sound/soc/codecs/cs35l45-i2c.c1
-rw-r--r--sound/soc/codecs/cs35l45-spi.c1
-rw-r--r--sound/soc/codecs/cs35l45-tables.c12
-rw-r--r--sound/soc/codecs/cs35l45.c1
-rw-r--r--sound/soc/codecs/cs42l42-sdw.c607
-rw-r--r--sound/soc/codecs/cs42l42.c133
-rw-r--r--sound/soc/codecs/cs42l42.h9
-rw-r--r--sound/soc/codecs/da7213.c30
-rw-r--r--sound/soc/codecs/da7213.h3
-rw-r--r--[-rwxr-xr-x]sound/soc/codecs/es8326.c0
-rw-r--r--[-rwxr-xr-x]sound/soc/codecs/es8326.h0
-rw-r--r--sound/soc/codecs/hda.c7
-rw-r--r--sound/soc/codecs/hdmi-codec.c11
-rw-r--r--sound/soc/codecs/idt821034.c1178
-rw-r--r--sound/soc/codecs/jz4760.c9
-rw-r--r--sound/soc/codecs/lpass-rx-macro.c31
-rw-r--r--sound/soc/codecs/lpass-tx-macro.c25
-rw-r--r--sound/soc/codecs/lpass-va-macro.c43
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c27
-rw-r--r--sound/soc/codecs/max98090.c8
-rw-r--r--sound/soc/codecs/max98373-sdw.c5
-rw-r--r--sound/soc/codecs/mc13783.c6
-rw-r--r--sound/soc/codecs/nau8822.c9
-rw-r--r--sound/soc/codecs/nau8822.h9
-rw-r--r--sound/soc/codecs/peb2466.c2071
-rw-r--r--sound/soc/codecs/rt1308-sdw.c5
-rw-r--r--sound/soc/codecs/rt1316-sdw.c7
-rw-r--r--sound/soc/codecs/rt1318-sdw.c7
-rw-r--r--sound/soc/codecs/rt5640.c9
-rw-r--r--sound/soc/codecs/rt5640.h2
-rw-r--r--sound/soc/codecs/rt5645.c2
-rw-r--r--sound/soc/codecs/rt5665.c4
-rw-r--r--sound/soc/codecs/rt5668.c4
-rw-r--r--sound/soc/codecs/rt5682-sdw.c5
-rw-r--r--sound/soc/codecs/rt5682.c4
-rw-r--r--sound/soc/codecs/rt5682s.c14
-rw-r--r--sound/soc/codecs/rt5682s.h1
-rw-r--r--sound/soc/codecs/rt700.c7
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.c2
-rw-r--r--sound/soc/codecs/rt711-sdca.c22
-rw-r--r--sound/soc/codecs/rt711-sdca.h8
-rw-r--r--sound/soc/codecs/rt711.c7
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.c485
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.h108
-rw-r--r--sound/soc/codecs/rt712-sdca.c1340
-rw-r--r--sound/soc/codecs/rt712-sdca.h220
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.c2
-rw-r--r--sound/soc/codecs/rt715-sdca.c5
-rw-r--r--sound/soc/codecs/rt715.c5
-rw-r--r--sound/soc/codecs/sdw-mockup.c5
-rw-r--r--sound/soc/codecs/sma1303.c1820
-rw-r--r--sound/soc/codecs/sma1303.h609
-rw-r--r--sound/soc/codecs/tas5720.c128
-rw-r--r--sound/soc/codecs/tas5720.h16
-rw-r--r--sound/soc/codecs/tlv320adcx140.c2
-rw-r--r--sound/soc/codecs/tlv320adcx140.h4
-rw-r--r--sound/soc/codecs/ts3a227e.c20
-rw-r--r--sound/soc/codecs/wcd934x.c10
-rw-r--r--sound/soc/codecs/wcd938x-sdw.c2
-rw-r--r--sound/soc/codecs/wl1273.c6
-rw-r--r--sound/soc/codecs/wm8940.c116
-rw-r--r--sound/soc/codecs/wm8940.h3
-rw-r--r--sound/soc/codecs/wsa881x.c63
-rw-r--r--sound/soc/codecs/wsa883x.c22
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c4
-rw-r--r--sound/soc/fsl/fsl_sai.c6
-rw-r--r--sound/soc/fsl/fsl_xcvr.c155
-rw-r--r--sound/soc/fsl/fsl_xcvr.h7
-rw-r--r--sound/soc/fsl/imx-hdmi.c2
-rw-r--r--sound/soc/fsl/imx-pcm-rpmsg.c6
-rw-r--r--sound/soc/generic/simple-card-utils.c49
-rw-r--r--sound/soc/generic/simple-card.c4
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.c9
-rw-r--r--sound/soc/intel/avs/Makefile2
-rw-r--r--sound/soc/intel/avs/boards/nau8825.c5
-rw-r--r--sound/soc/intel/avs/boards/rt286.c2
-rw-r--r--sound/soc/intel/avs/boards/rt298.c2
-rw-r--r--sound/soc/intel/avs/control.c105
-rw-r--r--sound/soc/intel/avs/control.h23
-rw-r--r--sound/soc/intel/avs/messages.c29
-rw-r--r--sound/soc/intel/avs/messages.h33
-rw-r--r--sound/soc/intel/avs/path.c62
-rw-r--r--sound/soc/intel/avs/pcm.c47
-rw-r--r--sound/soc/intel/avs/probes.c25
-rw-r--r--sound/soc/intel/avs/topology.c76
-rw-r--r--sound/soc/intel/avs/topology.h3
-rw-r--r--sound/soc/intel/avs/trace.c2
-rw-r--r--sound/soc/intel/boards/bytcht_cx2072x.c2
-rw-r--r--sound/soc/intel/boards/bytcht_da7213.c2
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c2
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5672.c2
-rw-r--r--sound/soc/intel/boards/sof_ssp_amp.c4
-rw-r--r--sound/soc/intel/boards/sof_wm8804.c2
-rw-r--r--sound/soc/intel/skylake/skl-messages.c2
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c4
-rw-r--r--sound/soc/intel/skylake/skl-topology.c19
-rw-r--r--sound/soc/intel/skylake/skl-topology.h5
-rw-r--r--sound/soc/kirkwood/kirkwood-dma.c2
-rw-r--r--sound/soc/mediatek/Kconfig23
-rw-r--r--sound/soc/mediatek/Makefile1
-rw-r--r--sound/soc/mediatek/common/Makefile2
-rw-r--r--sound/soc/mediatek/common/mtk-base-afe.h19
-rw-r--r--sound/soc/mediatek/common/mtk-dsp-sof-common.c18
-rw-r--r--sound/soc/mediatek/common/mtk-soundcard-driver.c79
-rw-r--r--sound/soc/mediatek/common/mtk-soundcard-driver.h14
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-dai-pcm.c8
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-dai-pcm.c7
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-pcm.c1
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-pcm.c7
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c2
-rw-r--r--sound/soc/mediatek/mt8188/Makefile15
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-clk.c658
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-clk.h115
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-common.h151
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-pcm.c3358
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clk.c205
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clk.h15
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h83
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-adda.c632
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-etdm.c2573
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-pcm.c368
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-mt6359.c785
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-reg.h3180
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-pcm.c8
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-dai-pcm.c7
-rw-r--r--sound/soc/meson/aiu-fifo-i2s.c4
-rw-r--r--sound/soc/meson/aiu-fifo-spdif.c2
-rw-r--r--sound/soc/meson/aiu-fifo.c21
-rw-r--r--sound/soc/meson/axg-tdm-interface.c34
-rw-r--r--sound/soc/meson/axg-tdmin.c2
-rw-r--r--sound/soc/meson/axg-tdmout.c2
-rw-r--r--sound/soc/meson/meson-codec-glue.c8
-rw-r--r--sound/soc/pxa/Kconfig5
-rw-r--r--sound/soc/pxa/e740_wm9705.c6
-rw-r--r--sound/soc/pxa/e750_wm9705.c6
-rw-r--r--sound/soc/pxa/e800_wm9712.c6
-rw-r--r--sound/soc/pxa/spitz.c6
-rw-r--r--sound/soc/qcom/apq8096.c1
-rw-r--r--sound/soc/qcom/lpass-platform.c2
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-dai.c22
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-lpass-dais.c5
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c14
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.h2
-rw-r--r--sound/soc/qcom/qdsp6/q6prm.c4
-rw-r--r--sound/soc/qcom/sdm845.c1
-rw-r--r--sound/soc/rockchip/Kconfig2
-rw-r--r--sound/soc/rockchip/rockchip_i2s_tdm.c4
-rw-r--r--sound/soc/rockchip/rockchip_pdm.c2
-rw-r--r--sound/soc/rockchip/rockchip_spdif.c2
-rw-r--r--sound/soc/samsung/i2s-regs.h1
-rw-r--r--sound/soc/samsung/i2s.c53
-rw-r--r--sound/soc/sh/rcar/adg.c169
-rw-r--r--sound/soc/sh/rcar/core.c73
-rw-r--r--sound/soc/sh/rcar/dma.c57
-rw-r--r--sound/soc/sh/rcar/gen.c70
-rw-r--r--sound/soc/sh/rcar/rsnd.h23
-rw-r--r--sound/soc/sh/rcar/ssi.c13
-rw-r--r--sound/soc/sh/rcar/ssiu.c15
-rw-r--r--sound/soc/soc-ac97.c68
-rw-r--r--sound/soc/soc-component.c20
-rw-r--r--sound/soc/soc-compress.c11
-rw-r--r--sound/soc/soc-core.c8
-rw-r--r--sound/soc/soc-dai.c13
-rw-r--r--sound/soc/soc-dapm.c26
-rw-r--r--sound/soc/soc-pcm.c15
-rw-r--r--sound/soc/soc-topology.c187
-rw-r--r--sound/soc/sof/amd/acp-dsp-offset.h4
-rw-r--r--sound/soc/sof/amd/acp-ipc.c8
-rw-r--r--sound/soc/sof/amd/acp-loader.c7
-rw-r--r--sound/soc/sof/amd/acp.h7
-rw-r--r--sound/soc/sof/compress.c9
-rw-r--r--sound/soc/sof/control.c44
-rw-r--r--sound/soc/sof/core.c4
-rw-r--r--sound/soc/sof/intel/Kconfig4
-rw-r--r--sound/soc/sof/intel/cnl.c2
-rw-r--r--sound/soc/sof/intel/hda-dai.c92
-rw-r--r--sound/soc/sof/intel/hda-dsp.c46
-rw-r--r--sound/soc/sof/intel/hda-ipc.c36
-rw-r--r--sound/soc/sof/intel/hda.h6
-rw-r--r--sound/soc/sof/intel/mtl.c16
-rw-r--r--sound/soc/sof/intel/mtl.h6
-rw-r--r--sound/soc/sof/ipc3-control.c46
-rw-r--r--sound/soc/sof/ipc3-pcm.c3
-rw-r--r--sound/soc/sof/ipc3-topology.c39
-rw-r--r--sound/soc/sof/ipc3.c22
-rw-r--r--sound/soc/sof/ipc4-control.c33
-rw-r--r--sound/soc/sof/ipc4-fw-reg.h155
-rw-r--r--sound/soc/sof/ipc4-pcm.c573
-rw-r--r--sound/soc/sof/ipc4-priv.h17
-rw-r--r--sound/soc/sof/ipc4-topology.c212
-rw-r--r--sound/soc/sof/ipc4-topology.h60
-rw-r--r--sound/soc/sof/ipc4.c29
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.c22
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.h10
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.c8
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.h2
-rw-r--r--sound/soc/sof/nocodec.c6
-rw-r--r--sound/soc/sof/ops.h18
-rw-r--r--sound/soc/sof/pcm.c36
-rw-r--r--sound/soc/sof/pm.c12
-rw-r--r--sound/soc/sof/sof-audio.c281
-rw-r--r--sound/soc/sof/sof-audio.h67
-rw-r--r--sound/soc/sof/sof-priv.h30
-rw-r--r--sound/soc/sof/stream-ipc.c53
-rw-r--r--sound/soc/sof/topology.c309
-rw-r--r--sound/soc/sof/trace.c8
-rw-r--r--sound/soc/spear/spdif_out.c3
-rw-r--r--sound/soc/tegra/Kconfig4
-rw-r--r--sound/soc/tegra/tegra20_ac97.c4
-rw-r--r--sound/soc/tegra/tegra20_i2s.c4
-rw-r--r--sound/soc/tegra/tegra20_spdif.c3
-rw-r--r--sound/soc/tegra/tegra210_admaif.c4
-rw-r--r--sound/soc/tegra/tegra30_i2s.c4
-rw-r--r--sound/soc/ti/davinci-i2s.c5
-rw-r--r--sound/soc/ti/davinci-mcasp.c5
-rw-r--r--sound/soc/ti/davinci-vcif.c5
-rw-r--r--sound/soc/ux500/mop500.c8
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c37
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c66
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.h14
-rw-r--r--sound/soc/ux500/ux500_pcm.c83
-rw-r--r--sound/xen/xen_snd_front.c3
-rw-r--r--tools/arch/arm64/include/asm/cputype.h8
-rw-r--r--tools/arch/arm64/include/uapi/asm/kvm.h1
-rw-r--r--tools/arch/x86/include/uapi/asm/kvm.h5
-rw-r--r--tools/bpf/bpftool/Makefile4
-rw-r--r--tools/gpio/gpio-event-mon.c1
-rw-r--r--tools/include/linux/build_bug.h9
-rw-r--r--tools/include/nolibc/arch-mips.h2
-rw-r--r--tools/include/nolibc/arch-riscv.h14
-rw-r--r--tools/include/nolibc/ctype.h3
-rw-r--r--tools/include/nolibc/errno.h3
-rw-r--r--tools/include/nolibc/signal.h3
-rw-r--r--tools/include/nolibc/stdio.h3
-rw-r--r--tools/include/nolibc/stdlib.h3
-rw-r--r--tools/include/nolibc/string.h8
-rw-r--r--tools/include/nolibc/sys.h2
-rw-r--r--tools/include/nolibc/time.h3
-rw-r--r--tools/include/nolibc/types.h70
-rw-r--r--tools/include/nolibc/unistd.h3
-rw-r--r--tools/include/uapi/linux/kvm.h3
-rw-r--r--tools/objtool/check.c9
-rw-r--r--tools/perf/Documentation/Makefile2
-rw-r--r--tools/perf/Makefile.config12
-rw-r--r--tools/perf/Makefile.perf31
-rw-r--r--tools/perf/arch/riscv/util/unwind-libdw.c2
-rw-r--r--tools/perf/builtin-kmem.c65
-rw-r--r--tools/perf/builtin-lock.c2
-rw-r--r--tools/perf/builtin-trace.c2
-rw-r--r--tools/perf/command-list.txt10
-rw-r--r--tools/perf/tests/bpf-script-test-prologue.c2
-rw-r--r--tools/perf/tests/make12
-rwxr-xr-xtools/perf/tests/shell/buildid.sh15
-rwxr-xr-xtools/perf/tests/shell/record+probe_libc_inet_pton.sh3
-rw-r--r--tools/perf/trace/beauty/include/linux/socket.h5
-rwxr-xr-xtools/perf/util/PERF-VERSION-GEN2
-rw-r--r--tools/perf/util/auxtrace.c2
-rw-r--r--tools/perf/util/bpf_counter.h6
-rw-r--r--tools/perf/util/bpf_counter_cgroup.c14
-rw-r--r--tools/perf/util/build-id.c10
-rw-r--r--tools/perf/util/cgroup.c23
-rw-r--r--tools/perf/util/data.c2
-rw-r--r--tools/perf/util/expr.l5
-rwxr-xr-xtools/perf/util/generate-cmdlist.sh19
-rw-r--r--tools/perf/util/sort.c12
-rw-r--r--tools/perf/util/trace-event.h1
-rw-r--r--tools/testing/memblock/.gitignore1
-rw-r--r--tools/testing/memblock/Makefile3
-rw-r--r--tools/testing/selftests/alsa/Makefile4
-rw-r--r--tools/testing/selftests/alsa/alsa-local.h3
-rw-r--r--tools/testing/selftests/alsa/conf.c26
-rw-r--r--tools/testing/selftests/alsa/conf.d/Lenovo_ThinkPad_P1_Gen2.conf43
-rw-r--r--tools/testing/selftests/alsa/pcm-test.c269
-rw-r--r--tools/testing/selftests/alsa/pcm-test.conf63
-rw-r--r--tools/testing/selftests/amd-pstate/Makefile5
-rw-r--r--tools/testing/selftests/bpf/.gitignore1
-rw-r--r--tools/testing/selftests/bpf/DENYLIST.s390x1
-rw-r--r--tools/testing/selftests/bpf/prog_tests/bpf_iter.c73
-rw-r--r--tools/testing/selftests/bpf/prog_tests/btf_dump.c2
-rw-r--r--tools/testing/selftests/bpf/prog_tests/decap_sanity.c85
-rw-r--r--tools/testing/selftests/bpf/prog_tests/jeq_infer_not_null.c9
-rw-r--r--tools/testing/selftests/bpf/prog_tests/sockmap_listen.c81
-rw-r--r--tools/testing/selftests/bpf/progs/bpf_tracing_net.h6
-rw-r--r--tools/testing/selftests/bpf/progs/decap_sanity.c68
-rw-r--r--tools/testing/selftests/bpf/progs/jeq_infer_not_null_fail.c42
-rw-r--r--tools/testing/selftests/bpf/verifier/search_pruning.c36
-rwxr-xr-xtools/testing/selftests/cgroup/test_cpuset_prs.sh1
-rwxr-xr-xtools/testing/selftests/drivers/net/ocelot/tc_flower_chains.sh2
-rwxr-xr-x[-rw-r--r--]tools/testing/selftests/filesystems/fat/run_fat_tests.sh0
-rw-r--r--tools/testing/selftests/kvm/.gitignore91
-rw-r--r--tools/testing/selftests/kvm/Makefile64
-rw-r--r--tools/testing/selftests/kvm/aarch64/page_fault_test.c189
-rw-r--r--tools/testing/selftests/kvm/lib/aarch64/ucall.c6
-rw-r--r--tools/testing/selftests/kvm/lib/kvm_util.c13
-rw-r--r--tools/testing/selftests/kvm/lib/ucall_common.c16
-rw-r--r--tools/testing/selftests/kvm/lib/x86_64/processor.c2
-rw-r--r--tools/testing/selftests/kvm/memslot_perf_test.c3
-rw-r--r--tools/testing/selftests/kvm/x86_64/hyperv_ipi.c3
-rw-r--r--tools/testing/selftests/kvm/x86_64/nx_huge_pages_test.c2
-rw-r--r--tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c13
-rw-r--r--tools/testing/selftests/kvm/x86_64/vmx_tsc_adjust_test.c5
-rw-r--r--tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c5
-rw-r--r--tools/testing/selftests/lib.mk2
-rw-r--r--tools/testing/selftests/net/.gitignore1
-rw-r--r--tools/testing/selftests/net/af_unix/test_unix_oob.c2
-rwxr-xr-xtools/testing/selftests/net/arp_ndisc_evict_nocarrier.sh15
-rw-r--r--tools/testing/selftests/net/bind_timewait.c92
-rwxr-xr-xtools/testing/selftests/net/cmsg_ipv6.sh2
-rw-r--r--tools/testing/selftests/net/cmsg_sender.c2
-rwxr-xr-xtools/testing/selftests/net/forwarding/lib.sh4
-rwxr-xr-xtools/testing/selftests/net/l2_tos_ttl_inherit.sh202
-rwxr-xr-xtools/testing/selftests/net/mptcp/mptcp_join.sh22
-rwxr-xr-xtools/testing/selftests/net/mptcp/userspace_pm.sh47
-rwxr-xr-xtools/testing/selftests/net/test_vxlan_vnifiltering.sh18
-rw-r--r--tools/testing/selftests/net/toeplitz.c12
-rwxr-xr-xtools/testing/selftests/net/udpgso_bench.sh24
-rw-r--r--tools/testing/selftests/net/udpgso_bench_rx.c4
-rw-r--r--tools/testing/selftests/net/udpgso_bench_tx.c36
-rwxr-xr-xtools/testing/selftests/netfilter/nft_trans_stress.sh16
-rw-r--r--tools/testing/selftests/netfilter/settings1
-rw-r--r--tools/testing/selftests/proc/proc-empty-vm.c12
-rw-r--r--tools/testing/selftests/proc/proc-pid-vm.c9
-rw-r--r--tools/testing/selftests/vm/hugetlb-madvise.c1
-rw-r--r--tools/virtio/linux/bug.h8
-rw-r--r--tools/virtio/linux/build_bug.h7
-rw-r--r--tools/virtio/linux/cpumask.h7
-rw-r--r--tools/virtio/linux/gfp.h7
-rw-r--r--tools/virtio/linux/kernel.h1
-rw-r--r--tools/virtio/linux/kmsan.h12
-rw-r--r--tools/virtio/linux/scatterlist.h1
-rw-r--r--tools/virtio/linux/topology.h7
-rw-r--r--tools/virtio/ringtest/main.h37
-rw-r--r--tools/virtio/virtio-trace/trace-agent-ctl.c2
-rw-r--r--tools/virtio/virtio_test.c2
-rw-r--r--tools/virtio/vringh_test.c2
-rw-r--r--virt/kvm/kvm_main.c7
-rw-r--r--virt/kvm/kvm_mm.h4
-rw-r--r--virt/kvm/vfio.c6
1960 files changed, 53392 insertions, 14626 deletions
diff --git a/.gitignore b/.gitignore
index 3ec73ead6757e2..20dce5c3b9e0f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,6 +39,7 @@
*.o.*
*.patch
*.rmeta
+*.rpm
*.rsi
*.s
*.so
diff --git a/.mailmap b/.mailmap
index ccba4cf0d89384..318e63f338b16e 100644
--- a/.mailmap
+++ b/.mailmap
@@ -130,6 +130,7 @@ Domen Puncer <domen@coderock.org>
Douglas Gilbert <dougg@torque.net>
Ed L. Cashin <ecashin@coraid.com>
Erik Kaneda <erik.kaneda@intel.com> <erik.schmauss@intel.com>
+Eugen Hristev <eugen.hristev@collabora.com> <eugen.hristev@microchip.com>
Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Ezequiel Garcia <ezequiel@vanguardiasur.com.ar> <ezequiel@collabora.com>
Felipe W Damasio <felipewd@terra.com.br>
@@ -214,6 +215,7 @@ Jisheng Zhang <jszhang@kernel.org> <jszhang@marvell.com>
Jisheng Zhang <jszhang@kernel.org> <Jisheng.Zhang@synaptics.com>
Johan Hovold <johan@kernel.org> <jhovold@gmail.com>
Johan Hovold <johan@kernel.org> <johan@hovoldconsulting.com>
+John Crispin <john@phrozen.org> <blogic@openwrt.org>
John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
John Stultz <johnstul@us.ibm.com>
Jordan Crouse <jordan@cosmicpenguin.net> <jcrouse@codeaurora.org>
@@ -371,6 +373,7 @@ Rémi Denis-Courmont <rdenis@simphalempin.com>
Ricardo Ribalda <ribalda@kernel.org> <ricardo@ribalda.com>
Ricardo Ribalda <ribalda@kernel.org> Ricardo Ribalda Delgado <ribalda@kernel.org>
Ricardo Ribalda <ribalda@kernel.org> <ricardo.ribalda@gmail.com>
+Robert Foss <rfoss@kernel.org> <robert.foss@linaro.org>
Roman Gushchin <roman.gushchin@linux.dev> <guro@fb.com>
Roman Gushchin <roman.gushchin@linux.dev> <guroan@gmail.com>
Roman Gushchin <roman.gushchin@linux.dev> <klamm@yandex-team.ru>
@@ -422,6 +425,7 @@ Tony Luck <tony.luck@intel.com>
TripleX Chung <xxx.phy@gmail.com> <triplex@zh-kernel.org>
TripleX Chung <xxx.phy@gmail.com> <zhongyu@18mail.cn>
Tsuneo Yoshioka <Tsuneo.Yoshioka@f-secure.com>
+Tudor Ambarus <tudor.ambarus@linaro.org> <tudor.ambarus@microchip.com>
Tycho Andersen <tycho@tycho.pizza> <tycho@tycho.ws>
Tzung-Bi Shih <tzungbi@kernel.org> <tzungbi@google.com>
Uwe Kleine-König <ukleinek@informatik.uni-freiburg.de>
diff --git a/CREDITS b/CREDITS
index 4e302a459ddf06..5f5d70c9c03892 100644
--- a/CREDITS
+++ b/CREDITS
@@ -1173,6 +1173,10 @@ D: Future Domain TMC-16x0 SCSI driver (author)
D: APM driver (early port)
D: DRM drivers (author of several)
+N: Veaceslav Falico
+E: vfalico@gmail.com
+D: Co-maintainer and co-author of the network bonding driver.
+
N: János Farkas
E: chexum@shadow.banki.hu
D: romfs, various (mostly networking) fixes
@@ -2489,6 +2493,13 @@ D: XF86_Mach8
D: XF86_8514
D: cfdisk (curses based disk partitioning program)
+N: Mat Martineau
+E: mat@martineau.name
+D: MPTCP subsystem co-maintainer 2020-2023
+D: Keyctl restricted keyring and Diffie-Hellman UAPI
+D: Bluetooth L2CAP ERTM mode and AMP
+S: USA
+
N: John S. Marvin
E: jsm@fc.hp.com
D: PA-RISC port
@@ -4172,6 +4183,10 @@ S: B-1206 Jingmao Guojigongyu
S: 16 Baliqiao Nanjie, Beijing 101100
S: People's Repulic of China
+N: Vlad Yasevich
+E: vyasevich@gmail.com
+D: SCTP protocol maintainer.
+
N: Aviad Yehezkel
E: aviadye@nvidia.com
D: Kernel TLS implementation and offload support.
diff --git a/Documentation/ABI/testing/debugfs-pktcdvd b/Documentation/ABI/testing/debugfs-pktcdvd
new file mode 100644
index 00000000000000..f6f65a4faea06c
--- /dev/null
+++ b/Documentation/ABI/testing/debugfs-pktcdvd
@@ -0,0 +1,18 @@
+What: /sys/kernel/debug/pktcdvd/pktcdvd[0-7]
+Date: Oct. 2006
+KernelVersion: 2.6.20
+Contact: Thomas Maier <balagi@justmail.de>
+Description:
+
+The pktcdvd module (packet writing driver) creates
+these files in debugfs:
+
+/sys/kernel/debug/pktcdvd/pktcdvd[0-7]/
+
+ ==== ====== ====================================
+ info 0444 Lots of driver statistics and infos.
+ ==== ====== ====================================
+
+Example::
+
+ cat /sys/kernel/debug/pktcdvd/pktcdvd0/info
diff --git a/Documentation/ABI/testing/sysfs-class-pktcdvd b/Documentation/ABI/testing/sysfs-class-pktcdvd
new file mode 100644
index 00000000000000..ba1ce626591d9c
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-pktcdvd
@@ -0,0 +1,97 @@
+sysfs interface
+---------------
+The pktcdvd module (packet writing driver) creates the following files in the
+sysfs: (<devid> is in the format major:minor)
+
+What: /sys/class/pktcdvd/add
+What: /sys/class/pktcdvd/remove
+What: /sys/class/pktcdvd/device_map
+Date: Oct. 2006
+KernelVersion: 2.6.20
+Contact: Thomas Maier <balagi@justmail.de>
+Description:
+
+ ========== ==============================================
+ add (WO) Write a block device id (major:minor) to
+ create a new pktcdvd device and map it to the
+ block device.
+
+ remove (WO) Write the pktcdvd device id (major:minor)
+ to remove the pktcdvd device.
+
+ device_map (RO) Shows the device mapping in format:
+ pktcdvd[0-7] <pktdevid> <blkdevid>
+ ========== ==============================================
+
+
+What: /sys/class/pktcdvd/pktcdvd[0-7]/dev
+What: /sys/class/pktcdvd/pktcdvd[0-7]/uevent
+Date: Oct. 2006
+KernelVersion: 2.6.20
+Contact: Thomas Maier <balagi@justmail.de>
+Description:
+ dev: (RO) Device id
+
+ uevent: (WO) To send a uevent
+
+
+What: /sys/class/pktcdvd/pktcdvd[0-7]/stat/packets_started
+What: /sys/class/pktcdvd/pktcdvd[0-7]/stat/packets_finished
+What: /sys/class/pktcdvd/pktcdvd[0-7]/stat/kb_written
+What: /sys/class/pktcdvd/pktcdvd[0-7]/stat/kb_read
+What: /sys/class/pktcdvd/pktcdvd[0-7]/stat/kb_read_gather
+What: /sys/class/pktcdvd/pktcdvd[0-7]/stat/reset
+Date: Oct. 2006
+KernelVersion: 2.6.20
+Contact: Thomas Maier <balagi@justmail.de>
+Description:
+ packets_started: (RO) Number of started packets.
+
+ packets_finished: (RO) Number of finished packets.
+
+ kb_written: (RO) kBytes written.
+
+ kb_read: (RO) kBytes read.
+
+ kb_read_gather: (RO) kBytes read to fill write packets.
+
+ reset: (WO) Write any value to it to reset
+ pktcdvd device statistic values, like
+ bytes read/written.
+
+
+What: /sys/class/pktcdvd/pktcdvd[0-7]/write_queue/size
+What: /sys/class/pktcdvd/pktcdvd[0-7]/write_queue/congestion_off
+What: /sys/class/pktcdvd/pktcdvd[0-7]/write_queue/congestion_on
+Date: Oct. 2006
+KernelVersion: 2.6.20
+Contact: Thomas Maier <balagi@justmail.de>
+Description:
+ ============== ================================================
+ size (RO) Contains the size of the bio write queue.
+
+ congestion_off (RW) If bio write queue size is below this mark,
+ accept new bio requests from the block layer.
+
+ congestion_on (RW) If bio write queue size is higher as this
+ mark, do no longer accept bio write requests
+ from the block layer and wait till the pktcdvd
+ device has processed enough bio's so that bio
+ write queue size is below congestion off mark.
+ A value of <= 0 disables congestion control.
+ ============== ================================================
+
+
+Example:
+--------
+To use the pktcdvd sysfs interface directly, you can do::
+
+ # create a new pktcdvd device mapped to /dev/hdc
+ echo "22:0" >/sys/class/pktcdvd/add
+ cat /sys/class/pktcdvd/device_map
+ # assuming device pktcdvd0 was created, look at stat's
+ cat /sys/class/pktcdvd/pktcdvd0/stat/kb_written
+ # print the device id of the mapped block device
+ fgrep pktcdvd0 /sys/class/pktcdvd/device_map
+ # remove device, using pktcdvd0 device id 253:0
+ echo "253:0" >/sys/class/pktcdvd/remove
diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst
index c8ae7c897f142a..74cec76be9f2c0 100644
--- a/Documentation/admin-guide/cgroup-v2.rst
+++ b/Documentation/admin-guide/cgroup-v2.rst
@@ -1245,13 +1245,17 @@ PAGE_SIZE multiple when read back.
This is a simple interface to trigger memory reclaim in the
target cgroup.
- This file accepts a string which contains the number of bytes to
- reclaim.
+ This file accepts a single key, the number of bytes to reclaim.
+ No nested keys are currently supported.
Example::
echo "1G" > memory.reclaim
+ The interface can be later extended with nested keys to
+ configure the reclaim behavior. For example, specify the
+ type of memory to reclaim from (anon, file, ..).
+
Please note that the kernel can over or under reclaim from
the target cgroup. If less bytes are reclaimed than the
specified amount, -EAGAIN is returned.
@@ -1263,13 +1267,6 @@ PAGE_SIZE multiple when read back.
This means that the networking layer will not adapt based on
reclaim induced by memory.reclaim.
- This file also allows the user to specify the nodes to reclaim from,
- via the 'nodes=' key, for example::
-
- echo "1G nodes=0,1" > memory.reclaim
-
- The above instructs the kernel to reclaim memory from nodes 0,1.
-
memory.peak
A read-only single value file which exists on non-root
cgroups.
diff --git a/Documentation/admin-guide/mm/zswap.rst b/Documentation/admin-guide/mm/zswap.rst
index f67de481c7f605..6dd74a18268baa 100644
--- a/Documentation/admin-guide/mm/zswap.rst
+++ b/Documentation/admin-guide/mm/zswap.rst
@@ -70,9 +70,7 @@ e.g. ``zswap.zpool=zbud``. It can also be changed at runtime using the sysfs
The zbud type zpool allocates exactly 1 page to store 2 compressed pages, which
means the compression ratio will always be 2:1 or worse (because of half-full
zbud pages). The zsmalloc type zpool has a more complex compressed page
-storage method, and it can achieve greater storage densities. However,
-zsmalloc does not implement compressed page eviction, so once zswap fills it
-cannot evict the oldest page, it can only reject new pages.
+storage method, and it can achieve greater storage densities.
When a swap page is passed from frontswap to zswap, zswap maintains a mapping
of the swap entry, a combination of the swap type and swap offset, to the zpool
diff --git a/Documentation/arm64/silicon-errata.rst b/Documentation/arm64/silicon-errata.rst
index 808ade4cc008ac..ec5f889d76819f 100644
--- a/Documentation/arm64/silicon-errata.rst
+++ b/Documentation/arm64/silicon-errata.rst
@@ -120,6 +120,8 @@ stable kernels.
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Cortex-A710 | #2224489 | ARM64_ERRATUM_2224489 |
+----------------+-----------------+-----------------+-----------------------------+
+| ARM | Cortex-A715 | #2645198 | ARM64_ERRATUM_2645198 |
++----------------+-----------------+-----------------+-----------------------------+
| ARM | Cortex-X2 | #2119858 | ARM64_ERRATUM_2119858 |
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Cortex-X2 | #2224489 | ARM64_ERRATUM_2224489 |
diff --git a/Documentation/conf.py b/Documentation/conf.py
index a5c45df0bd8397..d927737e3c10fe 100644
--- a/Documentation/conf.py
+++ b/Documentation/conf.py
@@ -31,6 +31,12 @@ def have_command(cmd):
# Get Sphinx version
major, minor, patch = sphinx.version_info[:3]
+#
+# Warn about older versions that we don't want to support for much
+# longer.
+#
+if (major < 2) or (major == 2 and minor < 4):
+ print('WARNING: support for Sphinx < 2.4 will be removed soon.')
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
@@ -339,7 +345,11 @@ html_use_smartypants = False
# Custom sidebar templates, maps document names to template names.
# Note that the RTD theme ignores this
-html_sidebars = { '**': ["about.html", 'searchbox.html', 'localtoc.html', 'sourcelink.html']}
+html_sidebars = { '**': ['searchbox.html', 'localtoc.html', 'sourcelink.html']}
+
+# about.html is available for alabaster theme. Add it at the front.
+if html_theme == 'alabaster':
+ html_sidebars['**'].insert(0, 'about.html')
# Output file base name for HTML help builder.
htmlhelp_basename = 'TheLinuxKerneldoc'
diff --git a/Documentation/devicetree/bindings/.gitignore b/Documentation/devicetree/bindings/.gitignore
index a77719968a7e1a..51ddb26d93f029 100644
--- a/Documentation/devicetree/bindings/.gitignore
+++ b/Documentation/devicetree/bindings/.gitignore
@@ -2,3 +2,8 @@
*.example.dts
/processed-schema*.yaml
/processed-schema*.json
+
+#
+# We don't want to ignore the following even if they are dot-files
+#
+!.yamllint
diff --git a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml
index 903b31129f012b..99e159bc5fb13b 100644
--- a/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml
+++ b/Documentation/devicetree/bindings/cpufreq/cpufreq-qcom-hw.yaml
@@ -54,6 +54,17 @@ properties:
- const: xo
- const: alternate
+ interrupts:
+ minItems: 1
+ maxItems: 3
+
+ interrupt-names:
+ minItems: 1
+ items:
+ - const: dcvsh-irq-0
+ - const: dcvsh-irq-1
+ - const: dcvsh-irq-2
+
'#freq-domain-cells':
const: 1
diff --git a/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-aes.yaml b/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-aes.yaml
index 0ccaab16dc614e..0b7383b3106b51 100644
--- a/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-aes.yaml
+++ b/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-aes.yaml
@@ -8,7 +8,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Atmel Advanced Encryption Standard (AES) HW cryptographic accelerator
maintainers:
- - Tudor Ambarus <tudor.ambarus@microchip.com>
+ - Tudor Ambarus <tudor.ambarus@linaro.org>
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-sha.yaml b/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-sha.yaml
index 5163c51b4547b5..ee2ffb0343251f 100644
--- a/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-sha.yaml
+++ b/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-sha.yaml
@@ -8,7 +8,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Atmel Secure Hash Algorithm (SHA) HW cryptographic accelerator
maintainers:
- - Tudor Ambarus <tudor.ambarus@microchip.com>
+ - Tudor Ambarus <tudor.ambarus@linaro.org>
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-tdes.yaml b/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-tdes.yaml
index fcc5adf03cadba..3d6ed24b1b0064 100644
--- a/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-tdes.yaml
+++ b/Documentation/devicetree/bindings/crypto/atmel,at91sam9g46-tdes.yaml
@@ -8,7 +8,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Atmel Triple Data Encryption Standard (TDES) HW cryptographic accelerator
maintainers:
- - Tudor Ambarus <tudor.ambarus@microchip.com>
+ - Tudor Ambarus <tudor.ambarus@linaro.org>
properties:
compatible:
diff --git a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml
index f2c143730a551f..6e2fd6e9fa7f0a 100644
--- a/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml
+++ b/Documentation/devicetree/bindings/display/msm/dsi-controller-main.yaml
@@ -32,7 +32,7 @@ properties:
- description: Display byte clock
- description: Display byte interface clock
- description: Display pixel clock
- - description: Display escape clock
+ - description: Display core clock
- description: Display AHB clock
- description: Display AXI clock
@@ -137,8 +137,6 @@ required:
- phys
- assigned-clocks
- assigned-clock-parents
- - power-domains
- - operating-points-v2
- ports
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/display/msm/dsi-phy-10nm.yaml b/Documentation/devicetree/bindings/display/msm/dsi-phy-10nm.yaml
index d9ad8b659f58ee..3ec466c3ab38bf 100644
--- a/Documentation/devicetree/bindings/display/msm/dsi-phy-10nm.yaml
+++ b/Documentation/devicetree/bindings/display/msm/dsi-phy-10nm.yaml
@@ -69,7 +69,6 @@ required:
- compatible
- reg
- reg-names
- - vdds-supply
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/display/msm/dsi-phy-14nm.yaml b/Documentation/devicetree/bindings/display/msm/dsi-phy-14nm.yaml
index 819de5ce0bc917..a43e11d3b00d2c 100644
--- a/Documentation/devicetree/bindings/display/msm/dsi-phy-14nm.yaml
+++ b/Documentation/devicetree/bindings/display/msm/dsi-phy-14nm.yaml
@@ -39,7 +39,6 @@ required:
- compatible
- reg
- reg-names
- - vcca-supply
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/display/msm/dsi-phy-28nm.yaml b/Documentation/devicetree/bindings/display/msm/dsi-phy-28nm.yaml
index 3d8540a06fe22a..2f1fd140c87df9 100644
--- a/Documentation/devicetree/bindings/display/msm/dsi-phy-28nm.yaml
+++ b/Documentation/devicetree/bindings/display/msm/dsi-phy-28nm.yaml
@@ -34,6 +34,10 @@ properties:
vddio-supply:
description: Phandle to vdd-io regulator device node.
+ qcom,dsi-phy-regulator-ldo-mode:
+ type: boolean
+ description: Indicates if the LDO mode PHY regulator is wanted.
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml
index d6f043a4b08d2d..4795e13c7b597b 100644
--- a/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml
+++ b/Documentation/devicetree/bindings/display/msm/qcom,qcm2290-mdss.yaml
@@ -72,7 +72,7 @@ examples:
#include <dt-bindings/interconnect/qcom,qcm2290.h>
#include <dt-bindings/power/qcom-rpmpd.h>
- mdss@5e00000 {
+ display-subsystem@5e00000 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "qcom,qcm2290-mdss";
diff --git a/Documentation/devicetree/bindings/display/msm/qcom,sm6115-mdss.yaml b/Documentation/devicetree/bindings/display/msm/qcom,sm6115-mdss.yaml
index a86d7f53fa84df..886858ef670005 100644
--- a/Documentation/devicetree/bindings/display/msm/qcom,sm6115-mdss.yaml
+++ b/Documentation/devicetree/bindings/display/msm/qcom,sm6115-mdss.yaml
@@ -62,7 +62,7 @@ examples:
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/power/qcom-rpmpd.h>
- mdss@5e00000 {
+ display-subsystem@5e00000 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "qcom,sm6115-mdss";
diff --git a/Documentation/devicetree/bindings/dsp/mediatek,mt8186-dsp.yaml b/Documentation/devicetree/bindings/dsp/mediatek,mt8186-dsp.yaml
index 3e63f79890b437..88575da1e6d5d2 100644
--- a/Documentation/devicetree/bindings/dsp/mediatek,mt8186-dsp.yaml
+++ b/Documentation/devicetree/bindings/dsp/mediatek,mt8186-dsp.yaml
@@ -15,7 +15,9 @@ description: |
properties:
compatible:
- const: mediatek,mt8186-dsp
+ enum:
+ - mediatek,mt8186-dsp
+ - mediatek,mt8188-dsp
reg:
items:
diff --git a/Documentation/devicetree/bindings/i2c/renesas,rzv2m.yaml b/Documentation/devicetree/bindings/i2c/renesas,rzv2m.yaml
index c46378efc12399..92e899905ef889 100644
--- a/Documentation/devicetree/bindings/i2c/renesas,rzv2m.yaml
+++ b/Documentation/devicetree/bindings/i2c/renesas,rzv2m.yaml
@@ -16,7 +16,7 @@ properties:
compatible:
items:
- enum:
- - renesas,i2c-r9a09g011 # RZ/V2M
+ - renesas,r9a09g011-i2c # RZ/V2M
- const: renesas,rzv2m-i2c
reg:
@@ -66,7 +66,7 @@ examples:
#include <dt-bindings/interrupt-controller/arm-gic.h>
i2c0: i2c@a4030000 {
- compatible = "renesas,i2c-r9a09g011", "renesas,rzv2m-i2c";
+ compatible = "renesas,r9a09g011-i2c", "renesas,rzv2m-i2c";
reg = <0xa4030000 0x80>;
interrupts = <GIC_SPI 232 IRQ_TYPE_EDGE_RISING>,
<GIC_SPI 236 IRQ_TYPE_EDGE_RISING>;
diff --git a/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml b/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml
index 4b37aa88a375b1..5e6be4e79201ee 100644
--- a/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml
+++ b/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml
@@ -84,7 +84,6 @@ allOf:
- qcom,msm8939-pcnoc
- qcom,msm8939-snoc
- qcom,msm8996-a1noc
- - qcom,msm8996-a2noc
- qcom,msm8996-bimc
- qcom,msm8996-cnoc
- qcom,msm8996-pnoc
@@ -191,6 +190,29 @@ allOf:
compatible:
contains:
enum:
+ - qcom,msm8996-a2noc
+
+ then:
+ properties:
+ clock-names:
+ items:
+ - const: bus
+ - const: bus_a
+ - const: aggre2_ufs_axi
+ - const: ufs_axi
+
+ clocks:
+ items:
+ - description: Bus Clock
+ - description: Bus A Clock
+ - description: Aggregate2 NoC UFS AXI Clock
+ - description: UFS AXI Clock
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
- qcom,sdm660-a2noc
then:
diff --git a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
index 9f7d3e11aacb8a..8449e14af9f358 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
+++ b/Documentation/devicetree/bindings/interrupt-controller/arm,gic-v3.yaml
@@ -108,7 +108,7 @@ properties:
msi-controller:
description:
- Only present if the Message Based Interrupt functionnality is
+ Only present if the Message Based Interrupt functionality is
being exposed by the HW, and the mbi-ranges property present.
mbi-ranges:
diff --git a/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml b/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml
index 1432fda3b603f7..47bc2057e6292a 100644
--- a/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml
+++ b/Documentation/devicetree/bindings/net/allwinner,sun8i-a83t-emac.yaml
@@ -40,6 +40,9 @@ properties:
clock-names:
const: stmmaceth
+ phy-supply:
+ description: PHY regulator
+
syscon:
$ref: /schemas/types.yaml#/definitions/phandle
description:
diff --git a/Documentation/devicetree/bindings/net/marvell,orion-mdio.yaml b/Documentation/devicetree/bindings/net/marvell,orion-mdio.yaml
index d2906b4a0f5957..e35da8b01dc257 100644
--- a/Documentation/devicetree/bindings/net/marvell,orion-mdio.yaml
+++ b/Documentation/devicetree/bindings/net/marvell,orion-mdio.yaml
@@ -16,9 +16,6 @@ description: |
8k has a second unit which provides an interface with the xMDIO bus. This
driver handles these interfaces.
-allOf:
- - $ref: "mdio.yaml#"
-
properties:
compatible:
enum:
@@ -39,13 +36,38 @@ required:
- compatible
- reg
+allOf:
+ - $ref: mdio.yaml#
+
+ - if:
+ required:
+ - interrupts
+
+ then:
+ properties:
+ reg:
+ items:
+ - items:
+ - $ref: /schemas/types.yaml#/definitions/cell
+ - const: 0x84
+
+ else:
+ properties:
+ reg:
+ items:
+ - items:
+ - $ref: /schemas/types.yaml#/definitions/cell
+ - enum:
+ - 0x4
+ - 0x10
+
unevaluatedProperties: false
examples:
- |
mdio@d0072004 {
compatible = "marvell,orion-mdio";
- reg = <0xd0072004 0x4>;
+ reg = <0xd0072004 0x84>;
#address-cells = <1>;
#size-cells = <0>;
interrupts = <30>;
diff --git a/Documentation/devicetree/bindings/phy/amlogic,meson-g12a-usb2-phy.yaml b/Documentation/devicetree/bindings/phy/amlogic,g12a-usb2-phy.yaml
index f3a5fbabbbb597..bb01c6b34dabcb 100644
--- a/Documentation/devicetree/bindings/phy/amlogic,meson-g12a-usb2-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/amlogic,g12a-usb2-phy.yaml
@@ -2,7 +2,7 @@
# Copyright 2019 BayLibre, SAS
%YAML 1.2
---
-$id: "http://devicetree.org/schemas/phy/amlogic,meson-g12a-usb2-phy.yaml#"
+$id: "http://devicetree.org/schemas/phy/amlogic,g12a-usb2-phy.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Amlogic G12A USB2 PHY
@@ -13,8 +13,8 @@ maintainers:
properties:
compatible:
enum:
- - amlogic,meson-g12a-usb2-phy
- - amlogic,meson-a1-usb2-phy
+ - amlogic,g12a-usb2-phy
+ - amlogic,a1-usb2-phy
reg:
maxItems: 1
@@ -68,7 +68,7 @@ additionalProperties: false
examples:
- |
phy@36000 {
- compatible = "amlogic,meson-g12a-usb2-phy";
+ compatible = "amlogic,g12a-usb2-phy";
reg = <0x36000 0x2000>;
clocks = <&xtal>;
clock-names = "xtal";
diff --git a/Documentation/devicetree/bindings/phy/amlogic,meson-g12a-usb3-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/amlogic,g12a-usb3-pcie-phy.yaml
index 868b4e6fde71f1..129d26e99776b6 100644
--- a/Documentation/devicetree/bindings/phy/amlogic,meson-g12a-usb3-pcie-phy.yaml
+++ b/Documentation/devicetree/bindings/phy/amlogic,g12a-usb3-pcie-phy.yaml
@@ -2,7 +2,7 @@
# Copyright 2019 BayLibre, SAS
%YAML 1.2
---
-$id: "http://devicetree.org/schemas/phy/amlogic,meson-g12a-usb3-pcie-phy.yaml#"
+$id: "http://devicetree.org/schemas/phy/amlogic,g12a-usb3-pcie-phy.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Amlogic G12A USB3 + PCIE Combo PHY
@@ -13,7 +13,7 @@ maintainers:
properties:
compatible:
enum:
- - amlogic,meson-g12a-usb3-pcie-phy
+ - amlogic,g12a-usb3-pcie-phy
reg:
maxItems: 1
@@ -49,7 +49,7 @@ additionalProperties: false
examples:
- |
phy@46000 {
- compatible = "amlogic,meson-g12a-usb3-pcie-phy";
+ compatible = "amlogic,g12a-usb3-pcie-phy";
reg = <0x46000 0x2000>;
clocks = <&ref_clk>;
clock-names = "ref_clk";
diff --git a/Documentation/devicetree/bindings/phy/qcom,usb-hs-28nm.yaml b/Documentation/devicetree/bindings/phy/qcom,usb-hs-28nm.yaml
index abcc4373f39e18..ca6a0836b53c41 100644
--- a/Documentation/devicetree/bindings/phy/qcom,usb-hs-28nm.yaml
+++ b/Documentation/devicetree/bindings/phy/qcom,usb-hs-28nm.yaml
@@ -16,7 +16,6 @@ properties:
compatible:
enum:
- qcom,usb-hs-28nm-femtophy
- - qcom,usb-hs-28nm-mdm9607
reg:
maxItems: 1
diff --git a/Documentation/devicetree/bindings/regulator/samsung,s2mps14.yaml b/Documentation/devicetree/bindings/regulator/samsung,s2mps14.yaml
index 01f9d4e236e949..a7feb497eb89bd 100644
--- a/Documentation/devicetree/bindings/regulator/samsung,s2mps14.yaml
+++ b/Documentation/devicetree/bindings/regulator/samsung,s2mps14.yaml
@@ -19,8 +19,8 @@ description: |
additional information and example.
patternProperties:
- # 25 LDOs
- "^LDO([1-9]|[1][0-9]|2[0-5])$":
+ # 25 LDOs, without LDO10-12
+ "^LDO([1-9]|1[3-9]|2[0-5])$":
type: object
$ref: regulator.yaml#
unevaluatedProperties: false
@@ -30,6 +30,23 @@ patternProperties:
required:
- regulator-name
+ "^LDO(1[0-2])$":
+ type: object
+ $ref: regulator.yaml#
+ unevaluatedProperties: false
+ description:
+ Properties for single LDO regulator.
+
+ properties:
+ samsung,ext-control-gpios:
+ maxItems: 1
+ description:
+ LDO10, LDO11 and LDO12 can be configured to external control over
+ GPIO.
+
+ required:
+ - regulator-name
+
# 5 bucks
"^BUCK[1-5]$":
type: object
diff --git a/Documentation/devicetree/bindings/riscv/cpus.yaml b/Documentation/devicetree/bindings/riscv/cpus.yaml
index c6720764e765c7..a2884e3113dade 100644
--- a/Documentation/devicetree/bindings/riscv/cpus.yaml
+++ b/Documentation/devicetree/bindings/riscv/cpus.yaml
@@ -83,7 +83,7 @@ properties:
insensitive, letters in the riscv,isa string must be all
lowercase to simplify parsing.
$ref: "/schemas/types.yaml#/definitions/string"
- pattern: ^rv(?:64|32)imaf?d?q?c?b?v?k?h?(?:_[hsxz](?:[a-z])+)*$
+ pattern: ^rv(?:64|32)imaf?d?q?c?b?k?j?p?v?h?(?:[hsxz](?:[a-z])+)?(?:_[hsxz](?:[a-z])+)*$
# RISC-V requires 'timebase-frequency' in /cpus, so disallow it here
timebase-frequency: false
diff --git a/Documentation/devicetree/bindings/rtc/qcom-pm8xxx-rtc.yaml b/Documentation/devicetree/bindings/rtc/qcom-pm8xxx-rtc.yaml
index 0a7aa29563c1c8..21c8ea08ff0a22 100644
--- a/Documentation/devicetree/bindings/rtc/qcom-pm8xxx-rtc.yaml
+++ b/Documentation/devicetree/bindings/rtc/qcom-pm8xxx-rtc.yaml
@@ -40,6 +40,8 @@ properties:
description:
Indicates that the setting of RTC time is allowed by the host CPU.
+ wakeup-source: true
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,apr-services.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,apr-services.yaml
index 290555426c39ca..bdf482db32aac1 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,apr-services.yaml
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,apr-services.yaml
@@ -39,8 +39,8 @@ properties:
qcom,protection-domain:
$ref: /schemas/types.yaml#/definitions/string-array
description: |
- Protection domain service name and path for APR service
- possible values are::
+ Protection domain service name and path for APR service (if supported).
+ Possible values are::
"avs/audio", "msm/adsp/audio_pd".
"kernel/elf_loader", "msm/modem/wlan_pd".
"tms/servreg", "msm/adsp/audio_pd".
@@ -49,6 +49,5 @@ properties:
required:
- reg
- - qcom,protection-domain
additionalProperties: true
diff --git a/Documentation/devicetree/bindings/sound/adi,adau7002.txt b/Documentation/devicetree/bindings/sound/adi,adau7002.txt
deleted file mode 100644
index f144ee1abf8550..00000000000000
--- a/Documentation/devicetree/bindings/sound/adi,adau7002.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter
-
-Required properties:
-
- - compatible: Must be "adi,adau7002"
-
-Optional properties:
-
- - IOVDD-supply: Phandle and specifier for the power supply providing the IOVDD
- supply as covered in Documentation/devicetree/bindings/regulator/regulator.txt
-
- If this property is not present it is assumed that the supply pin is
- hardwired to always on.
-
-Example:
- adau7002: pdm-to-i2s {
- compatible = "adi,adau7002";
- IOVDD-supply = <&supply>;
- };
diff --git a/Documentation/devicetree/bindings/sound/adi,adau7002.yaml b/Documentation/devicetree/bindings/sound/adi,adau7002.yaml
new file mode 100644
index 00000000000000..fcca0fde7d8663
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/adi,adau7002.yaml
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/adi,adau7002.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter
+
+maintainers:
+ - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: adi,adau7002
+
+ IOVDD-supply:
+ description:
+ IOVDD power supply, if skipped then it is assumed that the supply pin is
+ hardwired to always on.
+
+ wakeup-delay-ms:
+ description:
+ Delay after power up needed for device to settle.
+
+required:
+ - compatible
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ audio-codec {
+ compatible = "adi,adau7002";
+ IOVDD-supply = <&pp1800_l15a>;
+ #sound-dai-cells = <0>;
+ wakeup-delay-ms = <80>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/ak4613.yaml b/Documentation/devicetree/bindings/sound/ak4613.yaml
index 010574645e6aeb..75e13414d6eb18 100644
--- a/Documentation/devicetree/bindings/sound/ak4613.yaml
+++ b/Documentation/devicetree/bindings/sound/ak4613.yaml
@@ -25,6 +25,13 @@ properties:
"#sound-dai-cells":
const: 0
+ ports:
+ $ref: audio-graph-port.yaml#/definitions/ports
+
+ port:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+
patternProperties:
"^asahi-kasei,in[1-2]-single-end$":
description: Input Pin 1 - 2.
diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.txt b/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.txt
deleted file mode 100644
index fa4545ed81ca4a..00000000000000
--- a/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.txt
+++ /dev/null
@@ -1,34 +0,0 @@
-* Amlogic Audio FIFO controllers
-
-Required properties:
-- compatible: 'amlogic,axg-toddr' or
- 'amlogic,axg-toddr' or
- 'amlogic,g12a-frddr' or
- 'amlogic,g12a-toddr' or
- 'amlogic,sm1-frddr' or
- 'amlogic,sm1-toddr'
-- reg: physical base address of the controller and length of memory
- mapped region.
-- interrupts: interrupt specifier for the fifo.
-- clocks: phandle to the fifo peripheral clock provided by the audio
- clock controller.
-- resets: list of reset phandle, one for each entry reset-names.
-- reset-names: should contain the following:
- * "arb" : memory ARB line (required)
- * "rst" : dedicated device reset line (optional)
-- #sound-dai-cells: must be 0.
-- amlogic,fifo-depth: The size of the controller's fifo in bytes. This
- is useful for determining certain configuration such
- as the flush threshold of the fifo
-
-Example of FRDDR A on the A113 SoC:
-
-frddr_a: audio-controller@1c0 {
- compatible = "amlogic,axg-frddr";
- reg = <0x0 0x1c0 0x0 0x1c>;
- #sound-dai-cells = <0>;
- interrupts = <GIC_SPI 88 IRQ_TYPE_EDGE_RISING>;
- clocks = <&clkc_audio AUD_CLKID_FRDDR_A>;
- resets = <&arb AXG_ARB_FRDDR_A>;
- fifo-depth = <512>;
-};
diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.yaml
new file mode 100644
index 00000000000000..b1b48d683101a1
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/amlogic,axg-fifo.yaml
@@ -0,0 +1,112 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/amlogic,axg-fifo.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Amlogic AXG Audio FIFO controllers
+
+maintainers:
+ - Jerome Brunet <jbrunet@baylibre.com>
+
+properties:
+ compatible:
+ oneOf:
+ - enum:
+ - amlogic,axg-toddr
+ - amlogic,axg-frddr
+ - items:
+ - enum:
+ - amlogic,g12a-toddr
+ - amlogic,sm1-toddr
+ - const: amlogic,axg-toddr
+ - items:
+ - enum:
+ - amlogic,g12a-frddr
+ - amlogic,sm1-frddr
+ - const: amlogic,axg-frddr
+
+ reg:
+ maxItems: 1
+
+ "#sound-dai-cells":
+ const: 0
+
+ clocks:
+ items:
+ - description: Peripheral clock
+
+ interrupts:
+ maxItems: 1
+
+ resets:
+ minItems: 1
+ maxItems: 2
+
+ reset-names:
+ minItems: 1
+ maxItems: 2
+
+ amlogic,fifo-depth:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: Size of the controller's fifo in bytes
+
+required:
+ - compatible
+ - reg
+ - "#sound-dai-cells"
+ - clocks
+ - interrupts
+ - resets
+ - amlogic,fifo-depth
+
+allOf:
+ - $ref: dai-common.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - amlogic,g12a-toddr
+ - amlogic,sm1-toddr
+ - amlogic,g12a-frddr
+ - amlogic,sm1-frddr
+
+ then:
+ properties:
+ resets:
+ minItems: 2
+ reset-names:
+ items:
+ - const: arb
+ - const: rst
+ required:
+ - reset-names
+
+ else:
+ properties:
+ resets:
+ maxItems: 1
+ reset-names:
+ const: arb
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/axg-audio-clkc.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/reset/amlogic,meson-axg-audio-arb.h>
+ #include <dt-bindings/reset/amlogic,meson-g12a-audio-reset.h>
+
+ audio-controller@1c0 {
+ compatible = "amlogic,g12a-frddr", "amlogic,axg-frddr";
+ reg = <0x1c0 0x1c>;
+ #sound-dai-cells = <0>;
+ clocks = <&clkc_audio AUD_CLKID_FRDDR_A>;
+ interrupts = <GIC_SPI 88 IRQ_TYPE_EDGE_RISING>;
+ resets = <&arb>, <&clkc_audio AUD_RESET_FRDDR_A>;
+ reset-names = "arb", "rst";
+ amlogic,fifo-depth = <512>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.txt b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.txt
deleted file mode 100644
index 716878107a2423..00000000000000
--- a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.txt
+++ /dev/null
@@ -1,29 +0,0 @@
-* Amlogic Audio PDM input
-
-Required properties:
-- compatible: 'amlogic,axg-pdm' or
- 'amlogic,g12a-pdm' or
- 'amlogic,sm1-pdm'
-- reg: physical base address of the controller and length of memory
- mapped region.
-- clocks: list of clock phandle, one for each entry clock-names.
-- clock-names: should contain the following:
- * "pclk" : peripheral clock.
- * "dclk" : pdm digital clock
- * "sysclk" : dsp system clock
-- #sound-dai-cells: must be 0.
-
-Optional property:
-- resets: phandle to the dedicated reset line of the pdm input.
-
-Example of PDM on the A113 SoC:
-
-pdm: audio-controller@ff632000 {
- compatible = "amlogic,axg-pdm";
- reg = <0x0 0xff632000 0x0 0x34>;
- #sound-dai-cells = <0>;
- clocks = <&clkc_audio AUD_CLKID_PDM>,
- <&clkc_audio AUD_CLKID_PDM_DCLK>,
- <&clkc_audio AUD_CLKID_PDM_SYSCLK>;
- clock-names = "pclk", "dclk", "sysclk";
-};
diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
new file mode 100644
index 00000000000000..df21dd72fc65b6
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.yaml
@@ -0,0 +1,82 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/amlogic,axg-pdm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Amlogic Audio AXG PDM input
+
+maintainers:
+ - Jerome Brunet <jbrunet@baylibre.com>
+
+properties:
+ compatible:
+ oneOf:
+ - items:
+ - enum:
+ - amlogic,g12a-pdm
+ - amlogic,sm1-pdm
+ - const: amlogic,axg-pdm
+ - const: amlogic,axg-pdm
+
+ reg:
+ maxItems: 1
+
+ "#sound-dai-cells":
+ const: 0
+
+ clocks:
+ items:
+ - description: Peripheral clock
+ - description: PDM digital clock
+ - description: DSP system clock
+
+ clock-names:
+ items:
+ - const: pclk
+ - const: dclk
+ - const: sysclk
+
+ resets:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - "#sound-dai-cells"
+ - clocks
+ - clock-names
+
+allOf:
+ - $ref: dai-common.yaml#
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - amlogic,g12a-pdm
+ - amlogic,sm1-pdm
+ then:
+ required:
+ - resets
+
+ else:
+ properties:
+ resets: false
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/axg-audio-clkc.h>
+
+ audio-controller@ff632000 {
+ compatible = "amlogic,axg-pdm";
+ reg = <0xff632000 0x34>;
+ #sound-dai-cells = <0>;
+ clocks = <&clkc_audio AUD_CLKID_PDM>,
+ <&clkc_audio AUD_CLKID_PDM_DCLK>,
+ <&clkc_audio AUD_CLKID_PDM_SYSCLK>;
+ clock-names = "pclk", "dclk", "sysclk";
+ };
diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.txt b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.txt
deleted file mode 100644
index 80b411296480fb..00000000000000
--- a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.txt
+++ /dev/null
@@ -1,124 +0,0 @@
-Amlogic AXG sound card:
-
-Required properties:
-
-- compatible: "amlogic,axg-sound-card"
-- model : User specified audio sound card name, one string
-
-Optional properties:
-
-- audio-aux-devs : List of phandles pointing to auxiliary devices
-- audio-widgets : Please refer to widgets.txt.
-- audio-routing : A list of the connections between audio components.
-
-Subnodes:
-
-- dai-link: Container for dai-link level properties and the CODEC
- sub-nodes. There should be at least one (and probably more)
- subnode of this type.
-
-Required dai-link properties:
-
-- sound-dai: phandle and port of the CPU DAI.
-
-Required TDM Backend dai-link properties:
-- dai-format : CPU/CODEC common audio format
-
-Optional TDM Backend dai-link properties:
-- dai-tdm-slot-rx-mask-{0,1,2,3}: Receive direction slot masks
-- dai-tdm-slot-tx-mask-{0,1,2,3}: Transmit direction slot masks
- When omitted, mask is assumed to have to no
- slots. A valid must have at one slot, so at
- least one these mask should be provided with
- an enabled slot.
-- dai-tdm-slot-num : Please refer to tdm-slot.txt.
- If omitted, slot number is set to accommodate the largest
- mask provided.
-- dai-tdm-slot-width : Please refer to tdm-slot.txt. default to 32 if omitted.
-- mclk-fs : Multiplication factor between stream rate and mclk
-
-Backend dai-link subnodes:
-
-- codec: dai-link representing backend links should have at least one subnode.
- One subnode for each codec of the dai-link.
- dai-link representing frontend links have no codec, therefore have no
- subnodes
-
-Required codec subnodes properties:
-
-- sound-dai: phandle and port of the CODEC DAI.
-
-Optional codec subnodes properties:
-
-- dai-tdm-slot-tx-mask : Please refer to tdm-slot.txt.
-- dai-tdm-slot-rx-mask : Please refer to tdm-slot.txt.
-
-Example:
-
-sound {
- compatible = "amlogic,axg-sound-card";
- model = "AXG-S420";
- audio-aux-devs = <&tdmin_a>, <&tdmout_c>;
- audio-widgets = "Line", "Lineout",
- "Line", "Linein",
- "Speaker", "Speaker1 Left",
- "Speaker", "Speaker1 Right";
- "Speaker", "Speaker2 Left",
- "Speaker", "Speaker2 Right";
- audio-routing = "TDMOUT_C IN 0", "FRDDR_A OUT 2",
- "SPDIFOUT IN 0", "FRDDR_A OUT 3",
- "TDM_C Playback", "TDMOUT_C OUT",
- "TDMIN_A IN 2", "TDM_C Capture",
- "TDMIN_A IN 5", "TDM_C Loopback",
- "TODDR_A IN 0", "TDMIN_A OUT",
- "Lineout", "Lineout AOUTL",
- "Lineout", "Lineout AOUTR",
- "Speaker1 Left", "SPK1 OUT_A",
- "Speaker2 Left", "SPK2 OUT_A",
- "Speaker1 Right", "SPK1 OUT_B",
- "Speaker2 Right", "SPK2 OUT_B",
- "Linein AINL", "Linein",
- "Linein AINR", "Linein";
-
- dai-link@0 {
- sound-dai = <&frddr_a>;
- };
-
- dai-link@1 {
- sound-dai = <&toddr_a>;
- };
-
- dai-link@2 {
- sound-dai = <&tdmif_c>;
- dai-format = "i2s";
- dai-tdm-slot-tx-mask-2 = <1 1>;
- dai-tdm-slot-tx-mask-3 = <1 1>;
- dai-tdm-slot-rx-mask-1 = <1 1>;
- mclk-fs = <256>;
-
- codec@0 {
- sound-dai = <&lineout>;
- };
-
- codec@1 {
- sound-dai = <&speaker_amp1>;
- };
-
- codec@2 {
- sound-dai = <&speaker_amp2>;
- };
-
- codec@3 {
- sound-dai = <&linein>;
- };
-
- };
-
- dai-link@3 {
- sound-dai = <&spdifout>;
-
- codec {
- sound-dai = <&spdif_dit>;
- };
- };
-};
diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml
new file mode 100644
index 00000000000000..bf123455034352
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/amlogic,axg-sound-card.yaml
@@ -0,0 +1,183 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/amlogic,axg-sound-card.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Amlogic AXG sound card
+
+maintainers:
+ - Jerome Brunet <jbrunet@baylibre.com>
+
+properties:
+ compatible:
+ const: amlogic,axg-sound-card
+
+ audio-aux-devs:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ description: list of auxiliary devices
+
+ audio-routing:
+ $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+ description:
+ A list of the connections between audio components. Each entry is a
+ pair of strings, the first being the connection's sink, the second
+ being the connection's source.
+
+ audio-widgets:
+ $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+ description:
+ A list off component DAPM widget. Each entry is a pair of strings,
+ the first being the widget type, the second being the widget name
+
+ model:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: User specified audio sound card name
+
+patternProperties:
+ "^dai-link-[0-9]+$":
+ type: object
+ additionalProperties: false
+ description:
+ Container for dai-link level properties and the CODEC sub-nodes.
+ There should be at least one (and probably more) subnode of this type
+
+ properties:
+ dai-format:
+ $ref: /schemas/types.yaml#/definitions/string
+ enum: [ i2s, left-j, dsp_a ]
+
+ dai-tdm-slot-num:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Number of slots in use. If omitted, slot number is set to
+ accommodate the largest mask provided.
+ maximum: 32
+
+ dai-tdm-slot-width:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: Width in bits for each slot
+ enum: [ 8, 16, 20, 24, 32 ]
+ default: 32
+
+ mclk-fs:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ Multiplication factor between the frame rate and master clock
+ rate
+
+ sound-dai:
+ maxItems: 1
+ description: phandle of the CPU DAI
+
+ patternProperties:
+ "^dai-tdm-slot-(t|r)x-mask-[0-3]$":
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 32
+ description:
+ Transmit and receive cpu slot masks of each TDM lane
+ When omitted, mask is assumed to have to no slots. A valid
+ interface must have at least one slot, so at least one these
+ mask should be provided with an enabled slot.
+
+ "^codec(-[0-9]+)?$":
+ type: object
+ additionalProperties: false
+ description:
+ dai-link representing backend links should have at least one subnode.
+ One subnode for each codec of the dai-link. dai-link representing
+ frontend links have no codec, therefore have no subnodes
+
+ properties:
+ sound-dai:
+ maxItems: 1
+ description: phandle of the codec DAI
+
+ patternProperties:
+ "^dai-tdm-slot-(t|r)x-mask$":
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 1
+ maxItems: 32
+ description: Transmit and receive codec slot masks
+
+ required:
+ - sound-dai
+
+ required:
+ - sound-dai
+
+required:
+ - model
+ - dai-link-0
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ sound {
+ compatible = "amlogic,axg-sound-card";
+ model = "AXG-S420";
+ audio-aux-devs = <&tdmin_a>, <&tdmout_c>;
+ audio-widgets = "Line", "Lineout",
+ "Line", "Linein",
+ "Speaker", "Speaker1 Left",
+ "Speaker", "Speaker1 Right",
+ "Speaker", "Speaker2 Left",
+ "Speaker", "Speaker2 Right";
+ audio-routing = "TDMOUT_C IN 0", "FRDDR_A OUT 2",
+ "SPDIFOUT IN 0", "FRDDR_A OUT 3",
+ "TDM_C Playback", "TDMOUT_C OUT",
+ "TDMIN_A IN 2", "TDM_C Capture",
+ "TDMIN_A IN 5", "TDM_C Loopback",
+ "TODDR_A IN 0", "TDMIN_A OUT",
+ "Lineout", "Lineout AOUTL",
+ "Lineout", "Lineout AOUTR",
+ "Speaker1 Left", "SPK1 OUT_A",
+ "Speaker2 Left", "SPK2 OUT_A",
+ "Speaker1 Right", "SPK1 OUT_B",
+ "Speaker2 Right", "SPK2 OUT_B",
+ "Linein AINL", "Linein",
+ "Linein AINR", "Linein";
+
+ dai-link-0 {
+ sound-dai = <&frddr_a>;
+ };
+
+ dai-link-1 {
+ sound-dai = <&toddr_a>;
+ };
+
+ dai-link-2 {
+ sound-dai = <&tdmif_c>;
+ dai-format = "i2s";
+ dai-tdm-slot-tx-mask-2 = <1 1>;
+ dai-tdm-slot-tx-mask-3 = <1 1>;
+ dai-tdm-slot-rx-mask-1 = <1 1>;
+ mclk-fs = <256>;
+
+ codec-0 {
+ sound-dai = <&lineout>;
+ };
+
+ codec-1 {
+ sound-dai = <&speaker_amp1>;
+ };
+
+ codec-2 {
+ sound-dai = <&speaker_amp2>;
+ };
+
+ codec-3 {
+ sound-dai = <&linein>;
+ };
+ };
+
+ dai-link-3 {
+ sound-dai = <&spdifout>;
+
+ codec {
+ sound-dai = <&spdif_dit>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-spdifin.txt b/Documentation/devicetree/bindings/sound/amlogic,axg-spdifin.txt
deleted file mode 100644
index df92a4ecf28831..00000000000000
--- a/Documentation/devicetree/bindings/sound/amlogic,axg-spdifin.txt
+++ /dev/null
@@ -1,27 +0,0 @@
-* Amlogic Audio SPDIF Input
-
-Required properties:
-- compatible: 'amlogic,axg-spdifin' or
- 'amlogic,g12a-spdifin' or
- 'amlogic,sm1-spdifin'
-- interrupts: interrupt specifier for the spdif input.
-- clocks: list of clock phandle, one for each entry clock-names.
-- clock-names: should contain the following:
- * "pclk" : peripheral clock.
- * "refclk" : spdif input reference clock
-- #sound-dai-cells: must be 0.
-
-Optional property:
-- resets: phandle to the dedicated reset line of the spdif input.
-
-Example on the A113 SoC:
-
-spdifin: audio-controller@400 {
- compatible = "amlogic,axg-spdifin";
- reg = <0x0 0x400 0x0 0x30>;
- #sound-dai-cells = <0>;
- interrupts = <GIC_SPI 87 IRQ_TYPE_EDGE_RISING>;
- clocks = <&clkc_audio AUD_CLKID_SPDIFIN>,
- <&clkc_audio AUD_CLKID_SPDIFIN_CLK>;
- clock-names = "pclk", "refclk";
-};
diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-spdifin.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-spdifin.yaml
new file mode 100644
index 00000000000000..a0bd7a5fb9b3da
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/amlogic,axg-spdifin.yaml
@@ -0,0 +1,86 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/amlogic,axg-spdifin.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Amlogic Audio AXG SPDIF Input
+
+maintainers:
+ - Jerome Brunet <jbrunet@baylibre.com>
+
+properties:
+ compatible:
+ oneOf:
+ - const: amlogic,axg-spdifin
+ - items:
+ - enum:
+ - amlogic,g12a-spdifin
+ - amlogic,sm1-spdifin
+ - const: amlogic,axg-spdifin
+
+ reg:
+ maxItems: 1
+
+ "#sound-dai-cells":
+ const: 0
+
+ clocks:
+ items:
+ - description: Peripheral clock
+ - description: SPDIF input reference clock
+
+ clock-names:
+ items:
+ - const: pclk
+ - const: refclk
+
+ interrupts:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - "#sound-dai-cells"
+ - clocks
+ - clock-names
+ - interrupts
+
+allOf:
+ - $ref: dai-common.yaml#
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - amlogic,g12a-spdifin
+ - amlogic,sm1-spdifin
+ then:
+ required:
+ - resets
+
+ else:
+ properties:
+ resets: false
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/axg-audio-clkc.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+ audio-controller@400 {
+ compatible = "amlogic,axg-spdifin";
+ reg = <0x400 0x30>;
+ #sound-dai-cells = <0>;
+ interrupts = <GIC_SPI 87 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&clkc_audio AUD_CLKID_SPDIFIN>,
+ <&clkc_audio AUD_CLKID_SPDIFIN_CLK>;
+ clock-names = "pclk", "refclk";
+ };
diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-spdifout.txt b/Documentation/devicetree/bindings/sound/amlogic,axg-spdifout.txt
deleted file mode 100644
index 28381dd1f633f2..00000000000000
--- a/Documentation/devicetree/bindings/sound/amlogic,axg-spdifout.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-* Amlogic Audio SPDIF Output
-
-Required properties:
-- compatible: 'amlogic,axg-spdifout' or
- 'amlogic,g12a-spdifout' or
- 'amlogic,sm1-spdifout'
-- clocks: list of clock phandle, one for each entry clock-names.
-- clock-names: should contain the following:
- * "pclk" : peripheral clock.
- * "mclk" : master clock
-- #sound-dai-cells: must be 0.
-
-Optional property:
-- resets: phandle to the dedicated reset line of the spdif output.
-
-Example on the A113 SoC:
-
-spdifout: audio-controller@480 {
- compatible = "amlogic,axg-spdifout";
- reg = <0x0 0x480 0x0 0x50>;
- #sound-dai-cells = <0>;
- clocks = <&clkc_audio AUD_CLKID_SPDIFOUT>,
- <&clkc_audio AUD_CLKID_SPDIFOUT_CLK>;
- clock-names = "pclk", "mclk";
-};
diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-spdifout.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-spdifout.yaml
new file mode 100644
index 00000000000000..15be8dae93982c
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/amlogic,axg-spdifout.yaml
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/amlogic,axg-spdifout.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Amlogic Audio AXG SPDIF Output
+
+maintainers:
+ - Jerome Brunet <jbrunet@baylibre.com>
+
+properties:
+ compatible:
+ oneOf:
+ - const: amlogic,axg-spdifout
+ - items:
+ - enum:
+ - amlogic,g12a-spdifout
+ - amlogic,sm1-spdifout
+ - const: amlogic,axg-spdifout
+
+ reg:
+ maxItems: 1
+
+ "#sound-dai-cells":
+ const: 0
+
+ clocks:
+ items:
+ - description: Peripheral clock
+ - description: SPDIF output master clock
+
+ clock-names:
+ items:
+ - const: pclk
+ - const: mclk
+
+ resets:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - "#sound-dai-cells"
+ - clocks
+ - clock-names
+
+allOf:
+ - $ref: dai-common.yaml#
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - amlogic,g12a-spdifout
+ - amlogic,sm1-spdifout
+ then:
+ required:
+ - resets
+
+ else:
+ properties:
+ resets: false
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/axg-audio-clkc.h>
+
+ audio-controller@480 {
+ compatible = "amlogic,axg-spdifout";
+ reg = <0x480 0x50>;
+ #sound-dai-cells = <0>;
+ clocks = <&clkc_audio AUD_CLKID_SPDIFOUT>,
+ <&clkc_audio AUD_CLKID_SPDIFOUT_CLK>;
+ clock-names = "pclk", "mclk";
+ };
diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.txt b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.txt
deleted file mode 100644
index 5996c0cd89c29e..00000000000000
--- a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-* Amlogic Audio TDM formatters
-
-Required properties:
-- compatible: 'amlogic,axg-tdmin' or
- 'amlogic,axg-tdmout' or
- 'amlogic,g12a-tdmin' or
- 'amlogic,g12a-tdmout' or
- 'amlogic,sm1-tdmin' or
- 'amlogic,sm1-tdmout
-- reg: physical base address of the controller and length of memory
- mapped region.
-- clocks: list of clock phandle, one for each entry clock-names.
-- clock-names: should contain the following:
- * "pclk" : peripheral clock.
- * "sclk" : bit clock.
- * "sclk_sel" : bit clock input multiplexer.
- * "lrclk" : sample clock
- * "lrclk_sel": sample clock input multiplexer
-
-Optional property:
-- resets: phandle to the dedicated reset line of the tdm formatter.
-
-Example of TDMOUT_A on the S905X2 SoC:
-
-tdmout_a: audio-controller@500 {
- compatible = "amlogic,axg-tdmout";
- reg = <0x0 0x500 0x0 0x40>;
- resets = <&clkc_audio AUD_RESET_TDMOUT_A>;
- clocks = <&clkc_audio AUD_CLKID_TDMOUT_A>,
- <&clkc_audio AUD_CLKID_TDMOUT_A_SCLK>,
- <&clkc_audio AUD_CLKID_TDMOUT_A_SCLK_SEL>,
- <&clkc_audio AUD_CLKID_TDMOUT_A_LRCLK>,
- <&clkc_audio AUD_CLKID_TDMOUT_A_LRCLK>;
- clock-names = "pclk", "sclk", "sclk_sel",
- "lrclk", "lrclk_sel";
-};
diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.yaml
new file mode 100644
index 00000000000000..719ca8fc98c7d9
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-formatters.yaml
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/amlogic,axg-tdm-formatters.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Amlogic Audio AXG TDM formatters
+
+maintainers:
+ - Jerome Brunet <jbrunet@baylibre.com>
+
+properties:
+ compatible:
+ enum:
+ - amlogic,g12a-tdmout
+ - amlogic,sm1-tdmout
+ - amlogic,axg-tdmout
+ - amlogic,g12a-tdmin
+ - amlogic,sm1-tdmin
+ - amlogic,axg-tdmin
+
+ clocks:
+ items:
+ - description: Peripheral clock
+ - description: Bit clock
+ - description: Bit clock input multiplexer
+ - description: Sample clock
+ - description: Sample clock input multiplexer
+
+ clock-names:
+ items:
+ - const: pclk
+ - const: sclk
+ - const: sclk_sel
+ - const: lrclk
+ - const: lrclk_sel
+
+ reg:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+
+allOf:
+ - $ref: component-common.yaml#
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - amlogic,g12a-tdmin
+ - amlogic,sm1-tdmin
+ - amlogic,g12a-tdmout
+ - amlogic,sm1-tdmout
+ then:
+ required:
+ - resets
+
+ else:
+ properties:
+ resets: false
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/axg-audio-clkc.h>
+ #include <dt-bindings/reset/amlogic,meson-g12a-audio-reset.h>
+
+ audio-controller@500 {
+ compatible = "amlogic,g12a-tdmout";
+ reg = <0x500 0x40>;
+ resets = <&clkc_audio AUD_RESET_TDMOUT_A>;
+ clocks = <&clkc_audio AUD_CLKID_TDMOUT_A>,
+ <&clkc_audio AUD_CLKID_TDMOUT_A_SCLK>,
+ <&clkc_audio AUD_CLKID_TDMOUT_A_SCLK_SEL>,
+ <&clkc_audio AUD_CLKID_TDMOUT_A_LRCLK>,
+ <&clkc_audio AUD_CLKID_TDMOUT_A_LRCLK>;
+ clock-names = "pclk", "sclk", "sclk_sel",
+ "lrclk", "lrclk_sel";
+ };
diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.txt b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.txt
deleted file mode 100644
index cabfb26a5f227b..00000000000000
--- a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-* Amlogic Audio TDM Interfaces
-
-Required properties:
-- compatible: 'amlogic,axg-tdm-iface'
-- clocks: list of clock phandle, one for each entry clock-names.
-- clock-names: should contain the following:
- * "sclk" : bit clock.
- * "lrclk": sample clock
- * "mclk" : master clock
- -> optional if the interface is in clock slave mode.
-- #sound-dai-cells: must be 0.
-
-Example of TDM_A on the A113 SoC:
-
-tdmif_a: audio-controller@0 {
- compatible = "amlogic,axg-tdm-iface";
- #sound-dai-cells = <0>;
- clocks = <&clkc_audio AUD_CLKID_MST_A_MCLK>,
- <&clkc_audio AUD_CLKID_MST_A_SCLK>,
- <&clkc_audio AUD_CLKID_MST_A_LRCLK>;
- clock-names = "mclk", "sclk", "lrclk";
-};
diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml
new file mode 100644
index 00000000000000..320f0002649d55
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/amlogic,axg-tdm-iface.yaml
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/amlogic,axg-tdm-iface.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Amlogic Audio TDM Interfaces
+
+maintainers:
+ - Jerome Brunet <jbrunet@baylibre.com>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: amlogic,axg-tdm-iface
+
+ "#sound-dai-cells":
+ const: 0
+
+ clocks:
+ minItems: 2
+ items:
+ - description: Bit clock
+ - description: Sample clock
+ - description: Master clock #optional
+
+ clock-names:
+ minItems: 2
+ items:
+ - const: sclk
+ - const: lrclk
+ - const: mclk
+
+required:
+ - compatible
+ - "#sound-dai-cells"
+ - clocks
+ - clock-names
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/axg-audio-clkc.h>
+
+ audio-controller {
+ compatible = "amlogic,axg-tdm-iface";
+ #sound-dai-cells = <0>;
+ clocks = <&clkc_audio AUD_CLKID_MST_A_SCLK>,
+ <&clkc_audio AUD_CLKID_MST_A_LRCLK>,
+ <&clkc_audio AUD_CLKID_MST_A_MCLK>;
+ clock-names = "sclk", "lrclk", "mclk";
+ };
diff --git a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml
index 5b8d59245f82fb..b358fd601ed381 100644
--- a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml
+++ b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml
@@ -62,7 +62,7 @@ patternProperties:
description: phandle of the CPU DAI
patternProperties:
- "^codec-[0-9]+$":
+ "^codec(-[0-9]+)?$":
type: object
additionalProperties: false
description: |-
diff --git a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml
index f5b8b6d13077cc..6b4e02a0695ad9 100644
--- a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml
+++ b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml
@@ -11,32 +11,24 @@ maintainers:
select: false
-allOf:
- - $ref: /schemas/graph.yaml#/$defs/port-base
-
-properties:
- prefix:
- description: "device name prefix"
- $ref: /schemas/types.yaml#/definitions/string
- convert-rate:
- $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-rate"
- convert-channels:
- $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-channels"
- convert-sample-format:
- $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-format"
+definitions:
+ port-base:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ properties:
+ convert-rate:
+ $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-rate"
+ convert-channels:
+ $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-channels"
+ convert-sample-format:
+ $ref: "/schemas/sound/dai-params.yaml#/$defs/dai-sample-format"
+ mclk-fs:
+ $ref: "simple-card.yaml#/definitions/mclk-fs"
-patternProperties:
- "^endpoint(@[0-9a-f]+)?":
+ endpoint-base:
$ref: /schemas/graph.yaml#/$defs/endpoint-base
- unevaluatedProperties: false
-
properties:
mclk-fs:
- description: |
- Multiplication factor between stream rate and codec mclk.
- When defined, mclk-fs property defined in dai-link sub nodes are
- ignored.
- $ref: /schemas/types.yaml#/definitions/uint32
+ $ref: "simple-card.yaml#/definitions/mclk-fs"
frame-inversion:
description: dai-link uses frame clock inversion
$ref: /schemas/types.yaml#/definitions/flag
@@ -53,6 +45,15 @@ patternProperties:
oneOf:
- $ref: /schemas/types.yaml#/definitions/flag
- $ref: /schemas/types.yaml#/definitions/phandle
+ clocks:
+ description: Indicates system clock
+ $ref: /schemas/types.yaml#/definitions/phandle
+ system-clock-frequency:
+ $ref: "simple-card.yaml#/definitions/system-clock-frequency"
+ system-clock-direction-out:
+ $ref: "simple-card.yaml#/definitions/system-clock-direction-out"
+ system-clock-fixed:
+ $ref: "simple-card.yaml#/definitions/system-clock-fixed"
dai-format:
description: audio format.
@@ -100,4 +101,24 @@ patternProperties:
minimum: 1
maximum: 64
+ ports:
+ $ref: "#/definitions/port-base"
+ unevaluatedProperties: false
+ patternProperties:
+ "^port(@[0-9a-f]+)?$":
+ $ref: "#/definitions/port-base"
+ unevaluatedProperties: false
+ patternProperties:
+ "^endpoint(@[0-9a-f]+)?":
+ $ref: "#/definitions/endpoint-base"
+ unevaluatedProperties: false
+
+allOf:
+ - $ref: "#/definitions/port-base"
+
+patternProperties:
+ "^endpoint(@[0-9a-f]+)?":
+ $ref: "#/definitions/endpoint-base"
+ unevaluatedProperties: false
+
additionalProperties: true
diff --git a/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml
new file mode 100644
index 00000000000000..35eef7d818a2fb
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/awinic,aw88395.yaml
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/awinic,aw88395.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Awinic AW88395 Smart Audio Amplifier
+
+maintainers:
+ - Weidong Wang <wangweidong.a@awinic.com>
+
+description:
+ The Awinic AW88395 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier with an integrated 10.25V
+ smart boost convert.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: awinic,aw88395
+
+ reg:
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 0
+
+ reset-gpios:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - '#sound-dai-cells'
+ - reset-gpios
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ audio-codec@34 {
+ compatible = "awinic,aw88395";
+ reg = <0x34>;
+ #sound-dai-cells = <0>;
+ reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs35l41.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs35l41.yaml
index 82062d80d700da..18fb471aa891bf 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs35l41.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs35l41.yaml
@@ -22,6 +22,9 @@ properties:
reg:
maxItems: 1
+ interrupts:
+ maxItems: 1
+
'#sound-dai-cells':
description:
The first cell indicating the audio interface.
@@ -42,7 +45,7 @@ properties:
Configures the peak current by monitoring the current through the boost FET.
Range starts at 1600 mA and goes to a maximum of 4500 mA with increments
of 50 mA. See section 4.3.6 of the datasheet for details.
- $ref: "/schemas/types.yaml#/definitions/uint32"
+ $ref: /schemas/types.yaml#/definitions/uint32
minimum: 1600
maximum: 4500
default: 4500
@@ -51,7 +54,7 @@ properties:
description:
Boost inductor value, expressed in nH. Valid
values include 1000, 1200, 1500 and 2200.
- $ref: "/schemas/types.yaml#/definitions/uint32"
+ $ref: /schemas/types.yaml#/definitions/uint32
minimum: 1000
maximum: 2200
@@ -60,7 +63,7 @@ properties:
Total equivalent boost capacitance on the VBST
and VAMP pins, derated at 11 volts DC. The value must be rounded to the
nearest integer and expressed in uF.
- $ref: "/schemas/types.yaml#/definitions/uint32"
+ $ref: /schemas/types.yaml#/definitions/uint32
cirrus,asp-sdout-hiz:
description:
@@ -70,7 +73,7 @@ properties:
1 = Hi-Z during unused slots but logic 0 while all transmit channels disabled
2 = (Default) Logic 0 during unused slots, but Hi-Z while all transmit channels disabled
3 = Hi-Z during unused slots and while all transmit channels disabled
- $ref: "/schemas/types.yaml#/definitions/uint32"
+ $ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 3
default: 2
@@ -84,7 +87,7 @@ properties:
enable boost voltage.
0 = Internal Boost
1 = External Boost
- $ref: "/schemas/types.yaml#/definitions/uint32"
+ $ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 1
@@ -109,7 +112,7 @@ properties:
1 = GPIO
2 = Sync
3 = MCLK input
- $ref: "/schemas/types.yaml#/definitions/uint32"
+ $ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 3
@@ -136,7 +139,7 @@ properties:
3 = MCLK input
4 = Push-pull INTB (active low)
5 = Push-pull INT (active high)
- $ref: "/schemas/types.yaml#/definitions/uint32"
+ $ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 5
@@ -176,21 +179,23 @@ unevaluatedProperties: false
examples:
- |
+ #include <dt-bindings/gpio/gpio.h>
+
spi {
#address-cells = <1>;
#size-cells = <0>;
- cs35l41: cs35l41@2 {
- #sound-dai-cells = <1>;
- compatible = "cirrus,cs35l41";
- reg = <2>;
- VA-supply = <&dummy_vreg>;
- VP-supply = <&dummy_vreg>;
- reset-gpios = <&gpio 110 0>;
-
- cirrus,boost-type = <0>;
- cirrus,boost-peak-milliamp = <4500>;
- cirrus,boost-ind-nanohenry = <1000>;
- cirrus,boost-cap-microfarad = <15>;
+ cs35l41: speaker-amp@2 {
+ #sound-dai-cells = <1>;
+ compatible = "cirrus,cs35l41";
+ reg = <2>;
+ VA-supply = <&dummy_vreg>;
+ VP-supply = <&dummy_vreg>;
+ reset-gpios = <&gpio 110 GPIO_ACTIVE_HIGH>;
+
+ cirrus,boost-type = <0>;
+ cirrus,boost-peak-milliamp = <4500>;
+ cirrus,boost-ind-nanohenry = <1000>;
+ cirrus,boost-cap-microfarad = <15>;
};
};
diff --git a/Documentation/devicetree/bindings/sound/component-common.yaml b/Documentation/devicetree/bindings/sound/component-common.yaml
new file mode 100644
index 00000000000000..37766c5f3974e8
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/component-common.yaml
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/component-common.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Audio Component Common Properties
+
+maintainers:
+ - Jerome Brunet <jbrunet@baylibre.com>
+
+properties:
+ sound-name-prefix:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: |
+ Card implementing the routing property define the connection between
+ audio components as list of string pair. Component using the same
+ sink/source names may use this property to prepend the name of their
+ sinks/sources with the provided string.
+
+additionalProperties: true
diff --git a/Documentation/devicetree/bindings/sound/dai-common.yaml b/Documentation/devicetree/bindings/sound/dai-common.yaml
index d858eea73ed782..1aed2f0f17756f 100644
--- a/Documentation/devicetree/bindings/sound/dai-common.yaml
+++ b/Documentation/devicetree/bindings/sound/dai-common.yaml
@@ -9,15 +9,10 @@ title: Digital Audio Interface Common Properties
maintainers:
- Jerome Brunet <jbrunet@baylibre.com>
-properties:
- sound-name-prefix:
- $ref: /schemas/types.yaml#/definitions/string
- description: |
- Card implementing the routing property define the connection between
- audio components as list of string pair. Component using the same
- sink/source names may use this property to prepend the name of their
- sinks/sources with the provided string.
+allOf:
+ - $ref: component-common.yaml#
+properties:
'#sound-dai-cells': true
additionalProperties: true
diff --git a/Documentation/devicetree/bindings/sound/everest,es8326.yaml b/Documentation/devicetree/bindings/sound/everest,es8326.yaml
index 07781408e78820..07781408e78820 100755..100644
--- a/Documentation/devicetree/bindings/sound/everest,es8326.yaml
+++ b/Documentation/devicetree/bindings/sound/everest,es8326.yaml
diff --git a/Documentation/devicetree/bindings/sound/fsl,sai.yaml b/Documentation/devicetree/bindings/sound/fsl,sai.yaml
index 7e56337d8edc16..088c26b001cc02 100644
--- a/Documentation/devicetree/bindings/sound/fsl,sai.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,sai.yaml
@@ -76,10 +76,14 @@ properties:
minItems: 4
dmas:
- maxItems: 2
+ items:
+ - description: DMA controller phandle and request line for RX
+ - description: DMA controller phandle and request line for TX
dma-names:
- maxItems: 2
+ items:
+ - const: rx
+ - const: tx
interrupts:
items:
@@ -143,31 +147,6 @@ properties:
allOf:
- $ref: dai-common.yaml#
- if:
- properties:
- compatible:
- contains:
- const: fsl,vf610-sai
- then:
- properties:
- dmas:
- items:
- - description: DMA controller phandle and request line for TX
- - description: DMA controller phandle and request line for RX
- dma-names:
- items:
- - const: tx
- - const: rx
- else:
- properties:
- dmas:
- items:
- - description: DMA controller phandle and request line for RX
- - description: DMA controller phandle and request line for TX
- dma-names:
- items:
- - const: rx
- - const: tx
- - if:
required:
- fsl,sai-asynchronous
then:
@@ -199,9 +178,8 @@ examples:
<&clks VF610_CLK_SAI2>,
<&clks 0>, <&clks 0>;
clock-names = "bus", "mclk1", "mclk2", "mclk3";
- dma-names = "tx", "rx";
- dmas = <&edma0 0 21>,
- <&edma0 0 20>;
+ dma-names = "rx", "tx";
+ dmas = <&edma0 0 20>, <&edma0 0 21>;
big-endian;
lsb-first;
};
diff --git a/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml b/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml
index 223b8ea693dc4b..799b362ba4987c 100644
--- a/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,xcvr.yaml
@@ -21,6 +21,7 @@ properties:
compatible:
enum:
- fsl,imx8mp-xcvr
+ - fsl,imx93-xcvr
reg:
items:
diff --git a/Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml b/Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml
index 869b40363af8bf..0b1a01a4c14ed1 100644
--- a/Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml
+++ b/Documentation/devicetree/bindings/sound/google,sc7280-herobrine.yaml
@@ -75,6 +75,18 @@ patternProperties:
additionalProperties: false
+ platform:
+ description: Holds subnode which includes the phandle of q6apm platform device.
+ type: object
+ properties:
+ sound-dai:
+ maxItems: 1
+
+ required:
+ - sound-dai
+
+ additionalProperties: false
+
required:
- link-name
- cpu
diff --git a/Documentation/devicetree/bindings/sound/infineon,peb2466.yaml b/Documentation/devicetree/bindings/sound/infineon,peb2466.yaml
new file mode 100644
index 00000000000000..66993d378aaf59
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/infineon,peb2466.yaml
@@ -0,0 +1,91 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/infineon,peb2466.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Infineon PEB2466 codec
+
+maintainers:
+ - Herve Codina <herve.codina@bootlin.com>
+
+description: |
+ The Infineon PEB2466 codec is a programmable DSP-based four channels codec
+ with filters capabilities.
+
+ The time-slots used by the codec must be set and so, the properties
+ 'dai-tdm-slot-num', 'dai-tdm-slot-width', 'dai-tdm-slot-tx-mask' and
+ 'dai-tdm-slot-rx-mask' must be present in the sound card node for sub-nodes
+ that involve the codec. The codec uses one 8bit time-slot per channel.
+ 'dai-tdm-tdm-slot-with' must be set to 8.
+
+ The PEB2466 codec also supports 28 gpios (signaling pins).
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: infineon,peb2466
+
+ reg:
+ description:
+ SPI device address.
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: Master clock
+
+ clock-names:
+ items:
+ - const: mclk
+
+ spi-max-frequency:
+ maximum: 8192000
+
+ reset-gpios:
+ description:
+ GPIO used to reset the device.
+ maxItems: 1
+
+ firmware-name:
+ $ref: /schemas/types.yaml#/definitions/string
+ description:
+ Filters coefficients file to load. If this property is omitted, internal
+ filters are disabled.
+
+ '#sound-dai-cells':
+ const: 0
+
+ '#gpio-cells':
+ const: 2
+
+ gpio-controller: true
+
+required:
+ - compatible
+ - reg
+ - '#sound-dai-cells'
+ - gpio-controller
+ - '#gpio-cells'
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ audio-codec@0 {
+ compatible = "infineon,peb2466";
+ reg = <0>;
+ spi-max-frequency = <8192000>;
+ reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
+ #sound-dai-cells = <0>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml b/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml
new file mode 100644
index 00000000000000..b36c35e5da1a49
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/irondevice,sma1303.yaml
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/irondevice,sma1303.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Iron Device SMA1303 Audio Amplifier
+
+maintainers:
+ - Kiseok Jo <kiseok.jo@irondevice.com>
+
+description:
+ SMA1303 digital class-D audio amplifier
+ with an integrated boost converter.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - irondevice,sma1303
+
+ reg:
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - '#sound-dai-cells'
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ amplifier@1e {
+ compatible = "irondevice,sma1303";
+ reg = <0x1e>;
+ #sound-dai-cells = <1>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/max98090.txt b/Documentation/devicetree/bindings/sound/max98090.txt
deleted file mode 100644
index 39d640294c6211..00000000000000
--- a/Documentation/devicetree/bindings/sound/max98090.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-MAX98090 audio CODEC
-
-This device supports I2C only.
-
-Required properties:
-
-- compatible : "maxim,max98090" or "maxim,max98091".
-
-- reg : The I2C address of the device.
-
-- interrupts : The CODEC's interrupt output.
-
-Optional properties:
-
-- clocks: The phandle of the master clock to the CODEC
-
-- clock-names: Should be "mclk"
-
-- #sound-dai-cells : should be 0.
-
-- maxim,dmic-freq: Frequency at which to clock DMIC
-
-- maxim,micbias: Micbias voltage applies to the analog mic, valid voltages value are:
- 0 - 2.2v
- 1 - 2.55v
- 2 - 2.4v
- 3 - 2.8v
-
-Pins on the device (for linking into audio routes):
-
- * MIC1
- * MIC2
- * DMICL
- * DMICR
- * IN1
- * IN2
- * IN3
- * IN4
- * IN5
- * IN6
- * IN12
- * IN34
- * IN56
- * HPL
- * HPR
- * SPKL
- * SPKR
- * RCVL
- * RCVR
- * MICBIAS
-
-Example:
-
-audio-codec@10 {
- compatible = "maxim,max98090";
- reg = <0x10>;
- interrupt-parent = <&gpio>;
- interrupts = <TEGRA_GPIO(H, 4) IRQ_TYPE_LEVEL_HIGH>;
-};
diff --git a/Documentation/devicetree/bindings/sound/max98095.txt b/Documentation/devicetree/bindings/sound/max98095.txt
deleted file mode 100644
index 318a4c82f17f87..00000000000000
--- a/Documentation/devicetree/bindings/sound/max98095.txt
+++ /dev/null
@@ -1,22 +0,0 @@
-MAX98095 audio CODEC
-
-This device supports I2C only.
-
-Required properties:
-
-- compatible : "maxim,max98095".
-
-- reg : The I2C address of the device.
-
-Optional properties:
-
-- clocks: The phandle of the master clock to the CODEC
-
-- clock-names: Should be "mclk"
-
-Example:
-
-max98095: codec@11 {
- compatible = "maxim,max98095";
- reg = <0x11>;
-};
diff --git a/Documentation/devicetree/bindings/sound/maxim,max98090.yaml b/Documentation/devicetree/bindings/sound/maxim,max98090.yaml
new file mode 100644
index 00000000000000..65e4c516912ff5
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/maxim,max98090.yaml
@@ -0,0 +1,84 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/maxim,max98090.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Maxim Integrated MAX98090/MAX98091 audio codecs
+
+maintainers:
+ - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+
+description: |
+ Pins on the device (for linking into audio routes):
+ MIC1, MIC2, DMICL, DMICR, IN1, IN2, IN3, IN4, IN5, IN6, IN12, IN34, IN56,
+ HPL, HPR, SPKL, SPKR, RCVL, RCVR, MICBIAS
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - maxim,max98090
+ - maxim,max98091
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: master clock
+
+ clock-names:
+ items:
+ - const: mclk
+
+ interrupts:
+ maxItems: 1
+
+ maxim,dmic-freq:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ default: 2500000
+ description:
+ DMIC clock frequency
+
+ maxim,micbias:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [ 0, 1, 2, 3 ]
+ default: 3
+ description: |
+ Micbias voltage applied to the analog mic, valid voltages value are:
+ 0 - 2.2v
+ 1 - 2.55v
+ 2 - 2.4v
+ 3 - 2.8v
+
+ '#sound-dai-cells':
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ audio-codec@10 {
+ compatible = "maxim,max98090";
+ reg = <0x10>;
+ interrupt-parent = <&gpx3>;
+ interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
+ clocks = <&i2s0 0>;
+ clock-names = "mclk";
+ #sound-dai-cells = <0>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/maxim,max98095.yaml b/Documentation/devicetree/bindings/sound/maxim,max98095.yaml
new file mode 100644
index 00000000000000..77544a9e1587f5
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/maxim,max98095.yaml
@@ -0,0 +1,54 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/maxim,max98095.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Maxim Integrated MAX98095 audio codec
+
+maintainers:
+ - Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - maxim,max98095
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: master clock
+
+ clock-names:
+ items:
+ - const: mclk
+
+ '#sound-dai-cells':
+ const: 1
+
+required:
+ - compatible
+ - reg
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ audio-codec@11 {
+ compatible = "maxim,max98095";
+ reg = <0x11>;
+ clocks = <&i2s0 0>;
+ clock-names = "mclk";
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8188-afe.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8188-afe.yaml
new file mode 100644
index 00000000000000..82ccb32f08f27f
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mediatek,mt8188-afe.yaml
@@ -0,0 +1,208 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/mediatek,mt8188-afe.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek AFE PCM controller for mt8188
+
+maintainers:
+ - Trevor Wu <trevor.wu@mediatek.com>
+
+properties:
+ compatible:
+ const: mediatek,mt8188-afe
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+ reset-names:
+ const: audiosys
+
+ mediatek,topckgen:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: The phandle of the mediatek topckgen controller
+
+ power-domains:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: 26M clock
+ - description: audio pll1 clock
+ - description: audio pll2 clock
+ - description: clock divider for i2si1_mck
+ - description: clock divider for i2si2_mck
+ - description: clock divider for i2so1_mck
+ - description: clock divider for i2so2_mck
+ - description: clock divider for dptx_mck
+ - description: a1sys hoping clock
+ - description: audio intbus clock
+ - description: audio hires clock
+ - description: audio local bus clock
+ - description: mux for dptx_mck
+ - description: mux for i2so1_mck
+ - description: mux for i2so2_mck
+ - description: mux for i2si1_mck
+ - description: mux for i2si2_mck
+ - description: audio 26m clock
+
+ clock-names:
+ items:
+ - const: clk26m
+ - const: apll1
+ - const: apll2
+ - const: apll12_div0
+ - const: apll12_div1
+ - const: apll12_div2
+ - const: apll12_div3
+ - const: apll12_div9
+ - const: a1sys_hp_sel
+ - const: aud_intbus_sel
+ - const: audio_h_sel
+ - const: audio_local_bus_sel
+ - const: dptx_m_sel
+ - const: i2so1_m_sel
+ - const: i2so2_m_sel
+ - const: i2si1_m_sel
+ - const: i2si2_m_sel
+ - const: adsp_audio_26m
+
+ mediatek,etdm-in1-cowork-source:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ etdm modules can share the same external clock pin. Specify
+ which etdm clock source is required by this etdm in module.
+ enum:
+ - 1 # etdm2_in
+ - 2 # etdm1_out
+ - 3 # etdm2_out
+
+ mediatek,etdm-in2-cowork-source:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ etdm modules can share the same external clock pin. Specify
+ which etdm clock source is required by this etdm in module.
+ enum:
+ - 0 # etdm1_in
+ - 2 # etdm1_out
+ - 3 # etdm2_out
+
+ mediatek,etdm-out1-cowork-source:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ etdm modules can share the same external clock pin. Specify
+ which etdm clock source is required by this etdm out module.
+ enum:
+ - 0 # etdm1_in
+ - 1 # etdm2_in
+ - 3 # etdm2_out
+
+ mediatek,etdm-out2-cowork-source:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ etdm modules can share the same external clock pin. Specify
+ which etdm clock source is required by this etdm out module.
+ enum:
+ - 0 # etdm1_in
+ - 1 # etdm2_in
+ - 2 # etdm1_out
+
+patternProperties:
+ "^mediatek,etdm-in[1-2]-chn-disabled$":
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+ minItems: 1
+ maxItems: 16
+ description:
+ This is a list of channel IDs which should be disabled.
+ By default, all data received from ETDM pins will be outputed to
+ memory. etdm in supports disable_out in direct mode(w/o interconn),
+ so user can disable the specified channels by the property.
+ uniqueItems: true
+ items:
+ minimum: 0
+ maximum: 15
+
+ "^mediatek,etdm-in[1-2]-multi-pin-mode$":
+ type: boolean
+ description: if present, the etdm data mode is I2S.
+
+ "^mediatek,etdm-out[1-3]-multi-pin-mode$":
+ type: boolean
+ description: if present, the etdm data mode is I2S.
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - resets
+ - reset-names
+ - mediatek,topckgen
+ - power-domains
+ - clocks
+ - clock-names
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ afe@10b10000 {
+ compatible = "mediatek,mt8188-afe";
+ reg = <0x10b10000 0x10000>;
+ interrupts = <GIC_SPI 822 IRQ_TYPE_LEVEL_HIGH 0>;
+ resets = <&watchdog 14>;
+ reset-names = "audiosys";
+ mediatek,topckgen = <&topckgen>;
+ power-domains = <&spm 13>; //MT8188_POWER_DOMAIN_AUDIO
+ mediatek,etdm-in2-cowork-source = <2>;
+ mediatek,etdm-out2-cowork-source = <0>;
+ mediatek,etdm-in1-multi-pin-mode;
+ mediatek,etdm-in1-chn-disabled = /bits/ 8 <0x0 0x2>;
+ clocks = <&clk26m>,
+ <&apmixedsys 9>, //CLK_APMIXED_APLL1
+ <&apmixedsys 10>, //CLK_APMIXED_APLL2
+ <&topckgen 186>, //CLK_TOP_APLL12_CK_DIV0
+ <&topckgen 187>, //CLK_TOP_APLL12_CK_DIV1
+ <&topckgen 188>, //CLK_TOP_APLL12_CK_DIV2
+ <&topckgen 189>, //CLK_TOP_APLL12_CK_DIV3
+ <&topckgen 191>, //CLK_TOP_APLL12_CK_DIV9
+ <&topckgen 83>, //CLK_TOP_A1SYS_HP
+ <&topckgen 31>, //CLK_TOP_AUD_INTBUS
+ <&topckgen 32>, //CLK_TOP_AUDIO_H
+ <&topckgen 69>, //CLK_TOP_AUDIO_LOCAL_BUS
+ <&topckgen 81>, //CLK_TOP_DPTX
+ <&topckgen 77>, //CLK_TOP_I2SO1
+ <&topckgen 78>, //CLK_TOP_I2SO2
+ <&topckgen 79>, //CLK_TOP_I2SI1
+ <&topckgen 80>, //CLK_TOP_I2SI2
+ <&adsp_audio26m 0>; //CLK_AUDIODSP_AUDIO26M
+ clock-names = "clk26m",
+ "apll1",
+ "apll2",
+ "apll12_div0",
+ "apll12_div1",
+ "apll12_div2",
+ "apll12_div3",
+ "apll12_div9",
+ "a1sys_hp_sel",
+ "aud_intbus_sel",
+ "audio_h_sel",
+ "audio_local_bus_sel",
+ "dptx_m_sel",
+ "i2so1_m_sel",
+ "i2so2_m_sel",
+ "i2si1_m_sel",
+ "i2si2_m_sel",
+ "adsp_audio_26m";
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml
new file mode 100644
index 00000000000000..6640272b3f4f6d
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mediatek,mt8188-mt6359.yaml
@@ -0,0 +1,97 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/mediatek,mt8188-mt6359.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MediaTek MT8188 ASoC sound card
+
+maintainers:
+ - Trevor Wu <trevor.wu@mediatek.com>
+
+properties:
+ compatible:
+ const: mediatek,mt8188-mt6359-evb
+
+ model:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: User specified audio sound card name
+
+ audio-routing:
+ $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+ description:
+ A list of the connections between audio components. Each entry is a
+ sink/source pair of strings. Valid names could be the input or output
+ widgets of audio components, power supplies, MicBias of codec and the
+ software switch.
+
+ mediatek,platform:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: The phandle of MT8188 ASoC platform.
+
+patternProperties:
+ "^dai-link-[0-9]+$":
+ type: object
+ description:
+ Container for dai-link level properties and CODEC sub-nodes.
+
+ properties:
+ link-name:
+ description:
+ This property corresponds to the name of the BE dai-link to which
+ we are going to update parameters in this node.
+ items:
+ enum:
+ - ADDA_BE
+ - DPTX_BE
+ - ETDM1_IN_BE
+ - ETDM2_IN_BE
+ - ETDM1_OUT_BE
+ - ETDM2_OUT_BE
+ - ETDM3_OUT_BE
+ - PCM1_BE
+
+ codec:
+ description: Holds subnode which indicates codec dai.
+ type: object
+ additionalProperties: false
+ properties:
+ sound-dai:
+ minItems: 1
+ maxItems: 2
+ required:
+ - sound-dai
+
+ additionalProperties: false
+
+ required:
+ - link-name
+ - codec
+
+additionalProperties: false
+
+required:
+ - compatible
+ - mediatek,platform
+
+examples:
+ - |
+ sound {
+ compatible = "mediatek,mt8188-mt6359-evb";
+ mediatek,platform = <&afe>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&aud_pins_default>;
+ audio-routing =
+ "Headphone", "Headphone L",
+ "Headphone", "Headphone R",
+ "AIN1", "Headset Mic";
+ dai-link-0 {
+ link-name = "ETDM3_OUT_BE";
+
+ codec {
+ sound-dai = <&hdmi0>;
+ };
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/mchp,i2s-mcc.yaml b/Documentation/devicetree/bindings/sound/microchip,sama7g5-i2smcc.yaml
index 621022872c8d6e..651f61c7c25a00 100644
--- a/Documentation/devicetree/bindings/sound/mchp,i2s-mcc.yaml
+++ b/Documentation/devicetree/bindings/sound/microchip,sama7g5-i2smcc.yaml
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
-$id: http://devicetree.org/schemas/sound/mchp,i2s-mcc.yaml#
+$id: http://devicetree.org/schemas/sound/microchip,sama7g5-i2smcc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip I2S Multi-Channel Controller
diff --git a/Documentation/devicetree/bindings/sound/microchip,pdmc.yaml b/Documentation/devicetree/bindings/sound/microchip,sama7g5-pdmc.yaml
index c37b89d94c1211..c4cf1e5ab84b03 100644
--- a/Documentation/devicetree/bindings/sound/microchip,pdmc.yaml
+++ b/Documentation/devicetree/bindings/sound/microchip,sama7g5-pdmc.yaml
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
-$id: http://devicetree.org/schemas/sound/microchip,pdmc.yaml#
+$id: http://devicetree.org/schemas/sound/microchip,sama7g5-pdmc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip Pulse Density Microphone Controller
diff --git a/Documentation/devicetree/bindings/sound/mchp,spdifrx.yaml b/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdifrx.yaml
index 70a47c6823b1ad..2f43c684ab88d9 100644
--- a/Documentation/devicetree/bindings/sound/mchp,spdifrx.yaml
+++ b/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdifrx.yaml
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
-$id: http://devicetree.org/schemas/sound/mchp,spdifrx.yaml#
+$id: http://devicetree.org/schemas/sound/microchip,sama7g5-spdifrx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip S/PDIF Rx Controller
diff --git a/Documentation/devicetree/bindings/sound/mchp,spdiftx.yaml b/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdiftx.yaml
index c383162140bba1..4702c528700d4d 100644
--- a/Documentation/devicetree/bindings/sound/mchp,spdiftx.yaml
+++ b/Documentation/devicetree/bindings/sound/microchip,sama7g5-spdiftx.yaml
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
-$id: http://devicetree.org/schemas/sound/mchp,spdiftx.yaml#
+$id: http://devicetree.org/schemas/sound/microchip,sama7g5-spdiftx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip S/PDIF Tx Controller
diff --git a/Documentation/devicetree/bindings/sound/nau8822.txt b/Documentation/devicetree/bindings/sound/nau8822.txt
deleted file mode 100644
index a471d162d4e5a9..00000000000000
--- a/Documentation/devicetree/bindings/sound/nau8822.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-NAU8822 audio CODEC
-
-This device supports I2C only.
-
-Required properties:
-
- - compatible : "nuvoton,nau8822"
-
- - reg : the I2C address of the device.
-
-Example:
-
-codec: nau8822@1a {
- compatible = "nuvoton,nau8822";
- reg = <0x1a>;
-};
diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8822.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8822.yaml
new file mode 100644
index 00000000000000..65105402a53dde
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8822.yaml
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nuvoton,nau8822.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NAU8822 audio CODEC
+
+description: |
+ 24 bit stereo audio codec with speaker driver.
+ This device supports I2C/SPI.
+
+maintainers:
+ - David Lin <CTLIN0@nuvoton.com>
+
+properties:
+ compatible:
+ enum:
+ - nuvoton,nau8822
+
+ reg:
+ maxItems: 1
+
+ nuvoton,spk-btl:
+ description:
+ If set, configure the two loudspeaker outputs as a Bridge Tied Load output
+ to drive a high power external loudspeaker.
+ $ref: /schemas/types.yaml#/definitions/flag
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ codec@1a {
+ compatible = "nuvoton,nau8822";
+ reg = <0x1a>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml
index bb42220916b350..6cc8f86c753171 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml
@@ -35,7 +35,7 @@ properties:
clocks:
minItems: 3
- maxItems: 7
+ maxItems: 10
clock-names:
minItems: 1
@@ -65,6 +65,9 @@ properties:
power-domain-names:
maxItems: 1
+ required-opps:
+ maxItems: 1
+
'#sound-dai-cells':
const: 1
@@ -75,7 +78,7 @@ properties:
const: 0
patternProperties:
- "^dai-link@[0-9a-f]$":
+ "^dai-link@[0-9a-f]+$":
type: object
description: |
LPASS CPU dai node for each I2S device or Soundwire device. Bindings of each node
@@ -121,6 +124,8 @@ allOf:
then:
properties:
+ clocks:
+ maxItems: 3
clock-names:
items:
- const: ahbix-clk
@@ -135,6 +140,9 @@ allOf:
then:
properties:
+ clocks:
+ minItems: 7
+ maxItems: 7
clock-names:
items:
- const: ahbix-clk
@@ -153,33 +161,31 @@ allOf:
then:
properties:
+ clocks:
+ minItems: 6
+ maxItems: 6
clock-names:
- oneOf:
- - items: #for I2S
- - const: pcnoc-sway-clk
- - const: audio-core
- - const: mclk0
- - const: pcnoc-mport-clk
- - const: mi2s-bit-clk0
- - const: mi2s-bit-clk1
- - items: #for HDMI
- - const: pcnoc-sway-clk
- - const: audio-core
- - const: pcnoc-mport-clk
+ items:
+ - const: pcnoc-sway-clk
+ - const: audio-core
+ - const: mclk0
+ - const: pcnoc-mport-clk
+ - const: mi2s-bit-clk0
+ - const: mi2s-bit-clk1
+ reg:
+ minItems: 2
+ maxItems: 2
reg-names:
- anyOf:
- - items: #for I2S
- - const: lpass-lpaif
- - items: #for I2S and HDMI
- - const: lpass-hdmiif
- - const: lpass-lpaif
+ items:
+ - const: lpass-hdmiif
+ - const: lpass-lpaif
+ interrupts:
+ minItems: 2
+ maxItems: 2
interrupt-names:
- anyOf:
- - items: #for I2S
- - const: lpass-irq-lpaif
- - items: #for I2S and HDMI
- - const: lpass-irq-lpaif
- - const: lpass-irq-hdmi
+ items:
+ - const: lpass-irq-lpaif
+ - const: lpass-irq-hdmi
required:
- iommus
- power-domains
@@ -192,54 +198,44 @@ allOf:
then:
properties:
+ clocks:
+ minItems: 10
+ maxItems: 10
clock-names:
- oneOf:
- - items: #for I2S
- - const: aon_cc_audio_hm_h
- - const: audio_cc_ext_mclk0
- - const: core_cc_sysnoc_mport_core
- - const: core_cc_ext_if0_ibit
- - const: core_cc_ext_if1_ibit
- - items: #for Soundwire
- - const: aon_cc_audio_hm_h
- - const: audio_cc_codec_mem
- - const: audio_cc_codec_mem0
- - const: audio_cc_codec_mem1
- - const: audio_cc_codec_mem2
- - const: aon_cc_va_mem0
- - items: #for HDMI
- - const: core_cc_sysnoc_mport_core
-
+ items:
+ - const: aon_cc_audio_hm_h
+ - const: audio_cc_ext_mclk0
+ - const: core_cc_sysnoc_mport_core
+ - const: core_cc_ext_if0_ibit
+ - const: core_cc_ext_if1_ibit
+ - const: audio_cc_codec_mem
+ - const: audio_cc_codec_mem0
+ - const: audio_cc_codec_mem1
+ - const: audio_cc_codec_mem2
+ - const: aon_cc_va_mem0
+ reg:
+ minItems: 6
+ maxItems: 6
reg-names:
- anyOf:
- - items: #for I2S
- - const: lpass-lpaif
- - items: #for I2S and HDMI
- - const: lpass-hdmiif
- - const: lpass-lpaif
- - items: #for I2S, soundwire and HDMI
- - const: lpass-hdmiif
- - const: lpass-lpaif
- - const: lpass-rxtx-cdc-dma-lpm
- - const: lpass-rxtx-lpaif
- - const: lpass-va-lpaif
- - const: lpass-va-cdc-dma-lpm
+ items:
+ - const: lpass-hdmiif
+ - const: lpass-lpaif
+ - const: lpass-rxtx-cdc-dma-lpm
+ - const: lpass-rxtx-lpaif
+ - const: lpass-va-lpaif
+ - const: lpass-va-cdc-dma-lpm
+ interrupts:
+ minItems: 4
+ maxItems: 4
interrupt-names:
- anyOf:
- - items: #for I2S
- - const: lpass-irq-lpaif
- - items: #for I2S and HDMI
- - const: lpass-irq-lpaif
- - const: lpass-irq-hdmi
- - items: #for I2S, soundwire and HDMI
- - const: lpass-irq-lpaif
- - const: lpass-irq-hdmi
- - const: lpass-irq-vaif
- - const: lpass-irq-rxtxif
+ items:
+ - const: lpass-irq-lpaif
+ - const: lpass-irq-hdmi
+ - const: lpass-irq-vaif
+ - const: lpass-irq-rxtxif
power-domain-names:
- allOf:
- - items:
- - const: lcx
+ items:
+ - const: lcx
required:
- iommus
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
index 26f0343b5aac03..0a3c688ef1ec49 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-va-macro.yaml
@@ -36,7 +36,7 @@ properties:
oneOf:
- items: #for ADSP based platforms
- const: mclk
- - const: core
+ - const: macro
- const: dcodec
- items: #for ADSP bypass based platforms
- const: mclk
@@ -77,7 +77,7 @@ examples:
clocks = <&aoncc 0>,
<&q6afecc LPASS_HW_MACRO_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
<&q6afecc LPASS_HW_DCODEC_VOTE LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
- clock-names = "mclk", "core", "dcodec";
+ clock-names = "mclk", "macro", "dcodec";
clock-output-names = "fsgen";
qcom,dmic-sample-rate = <600000>;
vdd-micb-supply = <&vreg_s4a_1p8>;
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml b/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml
index a53c9ef938fa13..cdbb4096fa44f5 100644
--- a/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,q6apm-dai.yaml
@@ -17,7 +17,8 @@ properties:
const: qcom,q6apm-dais
iommus:
- maxItems: 1
+ minItems: 1
+ maxItems: 2
required:
- compatible
diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
index 70080d04ddc933..262de7a60a73e6 100644
--- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
@@ -15,16 +15,20 @@ description:
properties:
compatible:
- enum:
- - lenovo,yoga-c630-sndcard
- - qcom,apq8016-sbc-sndcard
- - qcom,db845c-sndcard
- - qcom,msm8916-qdsp6-sndcard
- - qcom,qrb5165-rb5-sndcard
- - qcom,sc8280xp-sndcard
- - qcom,sdm845-sndcard
- - qcom,sm8250-sndcard
- - qcom,sm8450-sndcard
+ oneOf:
+ - items:
+ - enum:
+ - lenovo,yoga-c630-sndcard
+ - qcom,db845c-sndcard
+ - const: qcom,sdm845-sndcard
+ - enum:
+ - qcom,apq8016-sbc-sndcard
+ - qcom,msm8916-qdsp6-sndcard
+ - qcom,qrb5165-rb5-sndcard
+ - qcom,sc8280xp-sndcard
+ - qcom,sdm845-sndcard
+ - qcom,sm8250-sndcard
+ - qcom,sm8450-sndcard
audio-routing:
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml
index 184e8ccbdd137f..ea09590bfa3074 100644
--- a/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,wcd934x.yaml
@@ -28,7 +28,9 @@ properties:
description: GPIO spec for reset line to use
maxItems: 1
- slim-ifc-dev: true
+ slim-ifc-dev:
+ description: IFC device interface
+ $ref: /schemas/types.yaml#/definitions/phandle
clocks:
maxItems: 1
@@ -147,21 +149,49 @@ patternProperties:
required:
- compatible
- reg
- - reset-gpios
- - slim-ifc-dev
- - interrupts
- - interrupt-controller
- - clock-frequency
- - clock-output-names
- - qcom,micbias1-microvolt
- - qcom,micbias2-microvolt
- - qcom,micbias3-microvolt
- - qcom,micbias4-microvolt
- - "#interrupt-cells"
- - "#clock-cells"
- - "#sound-dai-cells"
- - "#address-cells"
- - "#size-cells"
+
+allOf:
+ - if:
+ required:
+ - slim-ifc-dev
+ then:
+ required:
+ - reset-gpios
+ - slim-ifc-dev
+ - interrupt-controller
+ - clock-frequency
+ - clock-output-names
+ - qcom,micbias1-microvolt
+ - qcom,micbias2-microvolt
+ - qcom,micbias3-microvolt
+ - qcom,micbias4-microvolt
+ - "#interrupt-cells"
+ - "#clock-cells"
+ - "#sound-dai-cells"
+ - "#address-cells"
+ - "#size-cells"
+ oneOf:
+ - required:
+ - interrupts-extended
+ - required:
+ - interrupts
+ else:
+ properties:
+ reset-gpios: false
+ slim-ifc-dev: false
+ interrupts: false
+ interrupt-controller: false
+ clock-frequency: false
+ clock-output-names: false
+ qcom,micbias1-microvolt: false
+ qcom,micbias2-microvolt: false
+ qcom,micbias3-microvolt: false
+ qcom,micbias4-microvolt: false
+ "#interrupt-cells": false
+ "#clock-cells": false
+ "#sound-dai-cells": false
+ "#address-cells": false
+ "#size-cells": false
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/sound/qcom,wsa881x.yaml b/Documentation/devicetree/bindings/sound/qcom,wsa881x.yaml
index d702b489320fa0..ac03672ebf6de1 100644
--- a/Documentation/devicetree/bindings/sound/qcom,wsa881x.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,wsa881x.yaml
@@ -15,6 +15,9 @@ description: |
Their primary operating mode uses a SoundWire digital audio
interface. This binding is for SoundWire interface.
+allOf:
+ - $ref: dai-common.yaml#
+
properties:
compatible:
const: sdw10217201000
@@ -39,7 +42,7 @@ required:
- "#thermal-sensor-cells"
- "#sound-dai-cells"
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
diff --git a/Documentation/devicetree/bindings/sound/renesas,idt821034.yaml b/Documentation/devicetree/bindings/sound/renesas,idt821034.yaml
new file mode 100644
index 00000000000000..a2b92dba55297f
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/renesas,idt821034.yaml
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/renesas,idt821034.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas IDT821034 codec device
+
+maintainers:
+ - Herve Codina <herve.codina@bootlin.com>
+
+description: |
+ The IDT821034 codec is a four channel PCM codec with onchip filters and
+ programmable gain setting.
+
+ The time-slots used by the codec must be set and so, the properties
+ 'dai-tdm-slot-num', 'dai-tdm-slot-width', 'dai-tdm-slot-tx-mask' and
+ 'dai-tdm-slot-rx-mask' must be present in the ALSA sound card node for
+ sub-nodes that involve the codec. The codec uses one 8bit time-slot per
+ channel.
+ 'dai-tdm-tdm-slot-with' must be set to 8.
+
+ The IDT821034 codec also supports 5 gpios (SLIC signals) per channel.
+
+allOf:
+ - $ref: /schemas/spi/spi-peripheral-props.yaml#
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: renesas,idt821034
+
+ reg:
+ description:
+ SPI device address.
+ maxItems: 1
+
+ spi-max-frequency:
+ maximum: 8192000
+
+ spi-cpha: true
+
+ '#sound-dai-cells':
+ const: 0
+
+ '#gpio-cells':
+ const: 2
+
+ gpio-controller: true
+
+required:
+ - compatible
+ - reg
+ - spi-cpha
+ - '#sound-dai-cells'
+ - gpio-controller
+ - '#gpio-cells'
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ audio-codec@0 {
+ compatible = "renesas,idt821034";
+ reg = <0>;
+ spi-max-frequency = <8192000>;
+ spi-cpha;
+ #sound-dai-cells = <0>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml
index cb90463c729739..12ccf29338d92e 100644
--- a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml
+++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml
@@ -18,8 +18,7 @@ properties:
- enum:
- renesas,rcar_sound-r8a7778 # R-Car M1A
- renesas,rcar_sound-r8a7779 # R-Car H1
- - enum:
- - renesas,rcar_sound-gen1
+ - const: renesas,rcar_sound-gen1
# for Gen2 SoC
- items:
- enum:
@@ -32,8 +31,7 @@ properties:
- renesas,rcar_sound-r8a7791 # R-Car M2-W
- renesas,rcar_sound-r8a7793 # R-Car M2-N
- renesas,rcar_sound-r8a7794 # R-Car E2
- - enum:
- - renesas,rcar_sound-gen2
+ - const: renesas,rcar_sound-gen2
# for Gen3 SoC
- items:
- enum:
@@ -47,14 +45,16 @@ properties:
- renesas,rcar_sound-r8a77965 # R-Car M3-N
- renesas,rcar_sound-r8a77990 # R-Car E3
- renesas,rcar_sound-r8a77995 # R-Car D3
- - enum:
- - renesas,rcar_sound-gen3
- # for Generic
+ - const: renesas,rcar_sound-gen3
+ # for Gen4 SoC
- items:
- - enum:
- - renesas,rcar_sound-gen1
- - renesas,rcar_sound-gen2
- - renesas,rcar_sound-gen3
+ - const: renesas,rcar_sound-r8a779g0 # R-Car V4H
+ - const: renesas,rcar_sound-gen4
+ # for Generic
+ - enum:
+ - renesas,rcar_sound-gen1
+ - renesas,rcar_sound-gen2
+ - renesas,rcar_sound-gen3
reg:
minItems: 1
@@ -68,6 +68,7 @@ properties:
description: |
it must be 0 if your system is using single DAI
it must be 1 if your system is using multi DAIs
+ This is used on simple-audio-card
enum: [0, 1]
"#clock-cells":
@@ -113,15 +114,34 @@ properties:
- pattern: '^clk_(a|b|c|i)$'
ports:
- $ref: /schemas/graph.yaml#/properties/ports
+ $ref: audio-graph-port.yaml#/definitions/port-base
+ unevaluatedProperties: false
patternProperties:
'^port(@[0-9a-f]+)?$':
- $ref: audio-graph-port.yaml#
+ $ref: audio-graph-port.yaml#/definitions/port-base
unevaluatedProperties: false
+ patternProperties:
+ "^endpoint(@[0-9a-f]+)?":
+ $ref: audio-graph-port.yaml#/definitions/endpoint-base
+ properties:
+ playback:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ capture:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ unevaluatedProperties: false
port:
- $ref: audio-graph-port.yaml#
+ $ref: audio-graph-port.yaml#/definitions/port-base
unevaluatedProperties: false
+ patternProperties:
+ "^endpoint(@[0-9a-f]+)?":
+ $ref: audio-graph-port.yaml#/definitions/endpoint-base
+ properties:
+ playback:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ capture:
+ $ref: /schemas/types.yaml#/definitions/phandle-array
+ unevaluatedProperties: false
rcar_sound,dvc:
description: DVC subnode.
@@ -178,10 +198,6 @@ properties:
enum:
- tx
- rx
- required:
- - interrupts
- - dmas
- - dma-names
additionalProperties: false
rcar_sound,ssiu:
@@ -240,8 +256,6 @@ properties:
$ref: /schemas/types.yaml#/definitions/flag
required:
- interrupts
- - dmas
- - dma-names
additionalProperties: false
# For DAI base
@@ -271,7 +285,6 @@ required:
- reg-names
- clocks
- clock-names
- - "#sound-dai-cells"
allOf:
- $ref: dai-common.yaml#
@@ -285,7 +298,6 @@ allOf:
reg:
maxItems: 3
reg-names:
- maxItems: 3
items:
enum:
- scu
@@ -294,9 +306,8 @@ allOf:
else:
properties:
reg:
- maxItems: 5
+ minItems: 5
reg-names:
- maxItems: 5
items:
enum:
- scu
diff --git a/Documentation/devicetree/bindings/sound/rt5640.txt b/Documentation/devicetree/bindings/sound/rt5640.txt
index ff1228713f7e52..0c398581d52b0b 100644
--- a/Documentation/devicetree/bindings/sound/rt5640.txt
+++ b/Documentation/devicetree/bindings/sound/rt5640.txt
@@ -20,6 +20,9 @@ Optional properties:
- realtek,in3-differential
Boolean. Indicate MIC1/2/3 input are differential, rather than single-ended.
+- realtek,lout-differential
+ Boolean. Indicate LOUT output is differential, rather than stereo.
+
- realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin.
- realtek,dmic1-data-pin
diff --git a/Documentation/devicetree/bindings/sound/samsung,odroid.yaml b/Documentation/devicetree/bindings/sound/samsung,odroid.yaml
index 7b4e08ddef6aed..7774543b881974 100644
--- a/Documentation/devicetree/bindings/sound/samsung,odroid.yaml
+++ b/Documentation/devicetree/bindings/sound/samsung,odroid.yaml
@@ -43,9 +43,10 @@ properties:
type: object
properties:
sound-dai:
+ minItems: 1
items:
- - description: phandle of the MAX98090 CODEC
- description: phandle of the HDMI IP block node
+ - description: phandle of the MAX98090 CODEC
samsung,audio-routing:
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.yaml b/Documentation/devicetree/bindings/sound/samsung-i2s.yaml
index 8d5dcf9cd43efb..30b3b6e9824b7e 100644
--- a/Documentation/devicetree/bindings/sound/samsung-i2s.yaml
+++ b/Documentation/devicetree/bindings/sound/samsung-i2s.yaml
@@ -37,12 +37,20 @@ properties:
samsung,exynos7-i2s1: I2S1 on previous samsung platforms supports
stereo channels. Exynos7 I2S1 upgraded to 5.1 multichannel with
slightly modified bit offsets.
+
+ tesla,fsd-i2s: for 8/16/24bit stereo channel I2S for playback and
+ capture, secondary FIFO using external DMA, s/w reset control,
+ internal mux for root clock source with all root clock sampling
+ frequencies supported by Exynos7 I2S and 7.1 channel TDM support
+ for playback and capture TDM (Time division multiplexing) to allow
+ transfer of multiple channel audio data on single data line.
enum:
- samsung,s3c6410-i2s
- samsung,s5pv210-i2s
- samsung,exynos5420-i2s
- samsung,exynos7-i2s
- samsung,exynos7-i2s1
+ - tesla,fsd-i2s
'#address-cells':
const: 1
@@ -67,9 +75,6 @@ properties:
- const: rx
- const: tx-sec
- assigned-clock-parents: true
- assigned-clocks: true
-
clocks:
minItems: 1
maxItems: 3
diff --git a/Documentation/devicetree/bindings/sound/simple-card.yaml b/Documentation/devicetree/bindings/sound/simple-card.yaml
index ed19899bc94bac..f0d81bfe2598a6 100644
--- a/Documentation/devicetree/bindings/sound/simple-card.yaml
+++ b/Documentation/devicetree/bindings/sound/simple-card.yaml
@@ -205,6 +205,8 @@ patternProperties:
$ref: "#/definitions/dai"
"^simple-audio-card,codec(@[0-9a-f]+)?$":
$ref: "#/definitions/dai"
+ "^simple-audio-card,plat(@[0-9a-f]+)?$":
+ $ref: "#/definitions/dai"
"^simple-audio-card,dai-link(@[0-9a-f]+)?$":
description: |
@@ -215,6 +217,10 @@ patternProperties:
reg:
maxItems: 1
+ "#address-cells":
+ const: 1
+ "#size-cells":
+ const: 0
# common properties
frame-master:
$ref: "#/definitions/frame-master"
@@ -244,9 +250,9 @@ patternProperties:
maxItems: 1
patternProperties:
- "^cpu(@[0-9a-f]+)?":
+ "^cpu(-[0-9]+)?$":
$ref: "#/definitions/dai"
- "^codec(@[0-9a-f]+)?":
+ "^codec(-[0-9]+)?$":
$ref: "#/definitions/dai"
additionalProperties: false
@@ -462,16 +468,16 @@ examples:
convert-channels = <8>; /* TDM Split */
- sndcpu1: cpu0 {
+ sndcpu1: cpu-0 {
sound-dai = <&rcar_sound 1>;
};
- cpu1 {
+ cpu-1 {
sound-dai = <&rcar_sound 2>;
};
- cpu2 {
+ cpu-2 {
sound-dai = <&rcar_sound 3>;
};
- cpu3 {
+ cpu-3 {
sound-dai = <&rcar_sound 4>;
};
codec {
diff --git a/Documentation/devicetree/bindings/sound/tas5720.txt b/Documentation/devicetree/bindings/sound/tas5720.txt
index df99ca9451b07c..7d851ae2bba216 100644
--- a/Documentation/devicetree/bindings/sound/tas5720.txt
+++ b/Documentation/devicetree/bindings/sound/tas5720.txt
@@ -6,11 +6,13 @@ audio playback. For more product information please see the links below:
https://www.ti.com/product/TAS5720L
https://www.ti.com/product/TAS5720M
+https://www.ti.com/product/TAS5720A-Q1
https://www.ti.com/product/TAS5722L
Required properties:
- compatible : "ti,tas5720",
+ "ti,tas5720a-q1",
"ti,tas5722"
- reg : I2C slave address
- dvdd-supply : phandle to a 3.3-V supply for the digital circuitry
diff --git a/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt b/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt
deleted file mode 100644
index a02ecaab518327..00000000000000
--- a/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt
+++ /dev/null
@@ -1,56 +0,0 @@
-Texas Instruments pcm3168a DT bindings
-
-This driver supports both SPI and I2C bus access for this codec
-
-Required properties:
-
- - compatible: "ti,pcm3168a"
-
- - clocks : Contains an entry for each entry in clock-names
-
- - clock-names : Includes the following entries:
- "scki" The system clock
-
- - VDD1-supply : Digital power supply regulator 1 (+3.3V)
-
- - VDD2-supply : Digital power supply regulator 2 (+3.3V)
-
- - VCCAD1-supply : ADC power supply regulator 1 (+5V)
-
- - VCCAD2-supply : ADC power supply regulator 2 (+5V)
-
- - VCCDA1-supply : DAC power supply regulator 1 (+5V)
-
- - VCCDA2-supply : DAC power supply regulator 2 (+5V)
-
-For required properties on SPI/I2C, consult SPI/I2C device tree documentation
-
-Optional properties:
-
- - reset-gpios : Optional reset gpio line connected to RST pin of the codec.
- The RST line is low active:
- RST = low: device power-down
- RST = high: device is enabled
-
-Examples:
-
-i2c0: i2c0@0 {
-
- ...
-
- pcm3168a: audio-codec@44 {
- compatible = "ti,pcm3168a";
- reg = <0x44>;
- reset-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>;
- clocks = <&clk_core CLK_AUDIO>;
- clock-names = "scki";
- VDD1-supply = <&supply3v3>;
- VDD2-supply = <&supply3v3>;
- VCCAD1-supply = <&supply5v0>;
- VCCAD2-supply = <&supply5v0>;
- VCCDA1-supply = <&supply5v0>;
- VCCDA2-supply = <&supply5v0>;
- pinctrl-names = "default";
- pinctrl-0 = <&dac_clk_pin>;
- };
-};
diff --git a/Documentation/devicetree/bindings/sound/ti,pcm3168a.yaml b/Documentation/devicetree/bindings/sound/ti,pcm3168a.yaml
new file mode 100644
index 00000000000000..b6a4360ab845dd
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,pcm3168a.yaml
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,pcm3168a.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments PCM3168A Audio Codec
+
+maintainers:
+ - Damien Horsley <Damien.Horsley@imgtec.com>
+ - Geert Uytterhoeven <geert+renesas@glider.be>
+ - Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+
+description:
+ The Texas Instruments PCM3168A is a 24-bit Multi-channel Audio CODEC with
+ 96/192kHz sampling rate, supporting both SPI and I2C bus access.
+
+properties:
+ compatible:
+ const: ti,pcm3168a
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: System clock input
+
+ clock-names:
+ items:
+ - const: scki
+
+ reset-gpios:
+ items:
+ - description: |
+ GPIO line connected to the active-low RST pin of the codec.
+ RST = low: device power-down
+ RST = high: device is enabled
+
+ "#sound-dai-cells":
+ enum: [0, 1]
+
+ VDD1-supply:
+ description: Digital power supply regulator 1 (+3.3V)
+
+ VDD2-supply:
+ description: Digital power supply regulator 2 (+3.3V)
+
+ VCCAD1-supply:
+ description: ADC power supply regulator 1 (+5V)
+
+ VCCAD2-supply:
+ description: ADC power supply regulator 2 (+5V)
+
+ VCCDA1-supply:
+ description: DAC power supply regulator 1 (+5V)
+
+ VCCDA2-supply:
+ description: DAC power supply regulator 2 (+5V)
+
+ ports:
+ $ref: audio-graph-port.yaml#/definitions/port-base
+ properties:
+ port@0:
+ $ref: audio-graph-port.yaml#
+ description: Audio input port.
+
+ port@1:
+ $ref: audio-graph-port.yaml#
+ description: Audio output port.
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - VDD1-supply
+ - VDD2-supply
+ - VCCAD1-supply
+ - VCCAD2-supply
+ - VCCDA1-supply
+ - VCCDA2-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ pcm3168a: audio-codec@44 {
+ compatible = "ti,pcm3168a";
+ reg = <0x44>;
+ reset-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>;
+ clocks = <&clk_core 42>;
+ clock-names = "scki";
+ VDD1-supply = <&supply3v3>;
+ VDD2-supply = <&supply3v3>;
+ VCCAD1-supply = <&supply5v0>;
+ VCCAD2-supply = <&supply5v0>;
+ VCCDA1-supply = <&supply5v0>;
+ VCCDA2-supply = <&supply5v0>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320aic3x.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320aic3x.yaml
new file mode 100644
index 00000000000000..e8ca9f3369f8ec
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,tlv320aic3x.yaml
@@ -0,0 +1,165 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+# Copyright (C) 2022 Texas Instruments Incorporated
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,tlv320aic3x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments TLV320AIC3x Codec
+
+description: |
+ TLV320AIC3x are a series of low-power stereo audio codecs with stereo
+ headphone amplifier, as well as multiple inputs and outputs programmable in
+ single-ended or fully differential configurations.
+
+ The serial control bus supports SPI or I2C protocols, while the serial audio
+ data bus is programmable for I2S, left/right-justified, DSP, or TDM modes.
+
+ The following pins can be referred in the sound node's audio routing property:
+
+ CODEC output pins:
+ LLOUT
+ RLOUT
+ MONO_LOUT
+ HPLOUT
+ HPROUT
+ HPLCOM
+ HPRCOM
+
+ CODEC input pins for TLV320AIC3104:
+ MIC2L
+ MIC2R
+ LINE1L
+ LINE1R
+
+ CODEC input pins for other compatible codecs:
+ MIC3L
+ MIC3R
+ LINE1L
+ LINE2L
+ LINE1R
+ LINE2R
+
+maintainers:
+ - Jai Luthra <j-luthra@ti.com>
+
+properties:
+ compatible:
+ enum:
+ - ti,tlv320aic3x
+ - ti,tlv320aic33
+ - ti,tlv320aic3007
+ - ti,tlv320aic3106
+ - ti,tlv320aic3104
+
+ reg:
+ maxItems: 1
+
+ reset-gpios:
+ maxItems: 1
+ description:
+ GPIO specification for the active low RESET input.
+
+ gpio-reset:
+ maxItems: 1
+ description:
+ Deprecated, please use reset-gpios instead.
+ deprecated: true
+
+ ai3x-gpio-func:
+ description: AIC3X_GPIO1 & AIC3X_GPIO2 Functionality
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ maxItems: 2
+
+ ai3x-micbias-vg:
+ description: MicBias required voltage. If node is omitted then MicBias is powered down.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ oneOf:
+ - const: 1
+ description: MICBIAS output is powered to 2.0V.
+ - const: 2
+ description: MICBIAS output is powered to 2.5V.
+ - const: 3
+ description: MICBIAS output is connected to AVDD.
+
+ ai3x-ocmv:
+ description: Output Common-Mode Voltage selection.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ oneOf:
+ - const: 0
+ description: 1.35V
+ - const: 1
+ description: 1.5V
+ - const: 2
+ description: 1.65V
+ - const: 3
+ description: 1.8V
+
+ AVDD-supply:
+ description: Analog DAC voltage.
+
+ IOVDD-supply:
+ description: I/O voltage.
+
+ DRVDD-supply:
+ description: ADC analog and output driver voltage.
+
+ DVDD-supply:
+ description: Digital core voltage.
+
+ '#sound-dai-cells':
+ const: 0
+
+ clocks:
+ maxItems: 1
+
+ port:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ tlv320aic3x_i2c: audio-codec@1b {
+ compatible = "ti,tlv320aic3x";
+ reg = <0x1b>;
+
+ reset-gpios = <&gpio1 17 GPIO_ACTIVE_LOW>;
+
+ AVDD-supply = <&regulator>;
+ IOVDD-supply = <&regulator>;
+ DRVDD-supply = <&regulator>;
+ DVDD-supply = <&regulator>;
+ };
+ };
+
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ spi {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ tlv320aic3x_spi: audio-codec@0 {
+ compatible = "ti,tlv320aic3x";
+ reg = <0>; /* CS number */
+ #sound-dai-cells = <0>;
+
+ AVDD-supply = <&regulator>;
+ IOVDD-supply = <&regulator>;
+ DRVDD-supply = <&regulator>;
+ DVDD-supply = <&regulator>;
+ ai3x-ocmv = <0>;
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt b/Documentation/devicetree/bindings/sound/tlv320aic3x.txt
deleted file mode 100644
index 20931a63fd646b..00000000000000
--- a/Documentation/devicetree/bindings/sound/tlv320aic3x.txt
+++ /dev/null
@@ -1,97 +0,0 @@
-Texas Instruments - tlv320aic3x Codec module
-
-The tlv320aic3x serial control bus communicates through both I2C and SPI bus protocols
-
-Required properties:
-
-- compatible - "string" - One of:
- "ti,tlv320aic3x" - Generic TLV320AIC3x device
- "ti,tlv320aic33" - TLV320AIC33
- "ti,tlv320aic3007" - TLV320AIC3007
- "ti,tlv320aic3106" - TLV320AIC3106
- "ti,tlv320aic3104" - TLV320AIC3104
-
-
-- reg - <int> - I2C slave address
-
-
-Optional properties:
-
-- reset-gpios - GPIO specification for the active low RESET input.
-- ai3x-gpio-func - <array of 2 int> - AIC3X_GPIO1 & AIC3X_GPIO2 Functionality
- - Not supported on tlv320aic3104
-- ai3x-micbias-vg - MicBias Voltage required.
- 1 - MICBIAS output is powered to 2.0V,
- 2 - MICBIAS output is powered to 2.5V,
- 3 - MICBIAS output is connected to AVDD,
- If this node is not mentioned or if the value is incorrect, then MicBias
- is powered down.
-- ai3x-ocmv - Output Common-Mode Voltage selection:
- 0 - 1.35V,
- 1 - 1.5V,
- 2 - 1.65V,
- 3 - 1.8V
-- AVDD-supply, IOVDD-supply, DRVDD-supply, DVDD-supply : power supplies for the
- device as covered in Documentation/devicetree/bindings/regulator/regulator.txt
-
-Deprecated properties:
-
-- gpio-reset - gpio pin number used for codec reset
-
-CODEC output pins:
- * LLOUT
- * RLOUT
- * MONO_LOUT
- * HPLOUT
- * HPROUT
- * HPLCOM
- * HPRCOM
-
-CODEC input pins for TLV320AIC3104:
- * MIC2L
- * MIC2R
- * LINE1L
- * LINE1R
-
-CODEC input pins for other compatible codecs:
- * MIC3L
- * MIC3R
- * LINE1L
- * LINE2L
- * LINE1R
- * LINE2R
-
-The pins can be used in referring sound node's audio-routing property.
-
-I2C example:
-
-#include <dt-bindings/gpio/gpio.h>
-
-tlv320aic3x: tlv320aic3x@1b {
- compatible = "ti,tlv320aic3x";
- reg = <0x1b>;
-
- reset-gpios = <&gpio1 17 GPIO_ACTIVE_LOW>;
-
- AVDD-supply = <&regulator>;
- IOVDD-supply = <&regulator>;
- DRVDD-supply = <&regulator>;
- DVDD-supply = <&regulator>;
-};
-
-SPI example:
-
-spi0: spi@f0000000 {
- tlv320aic3x: codec@0 {
- compatible = "ti,tlv320aic3x";
- reg = <0>; /* CS number */
- #sound-dai-cells = <0>;
- spi-max-frequency = <1000000>;
-
- AVDD-supply = <&regulator>;
- IOVDD-supply = <&regulator>;
- DRVDD-supply = <&regulator>;
- DVDD-supply = <&regulator>;
- ai3x-ocmv = <0>;
- };
-};
diff --git a/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml b/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml
index bcbfa71536cda6..3efdc192ab0191 100644
--- a/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml
+++ b/Documentation/devicetree/bindings/soundwire/qcom,soundwire.yaml
@@ -80,7 +80,7 @@ properties:
or applicable for the respective data port.
More info in MIPI Alliance SoundWire 1.0 Specifications.
minItems: 3
- maxItems: 5
+ maxItems: 8
qcom,ports-sinterval-low:
$ref: /schemas/types.yaml#/definitions/uint8-array
@@ -124,7 +124,7 @@ properties:
or applicable for the respective data port.
More info in MIPI Alliance SoundWire 1.0 Specifications.
minItems: 3
- maxItems: 5
+ maxItems: 8
qcom,ports-block-pack-mode:
$ref: /schemas/types.yaml#/definitions/uint8-array
@@ -154,7 +154,7 @@ properties:
or applicable for the respective data port.
More info in MIPI Alliance SoundWire 1.0 Specifications.
minItems: 3
- maxItems: 5
+ maxItems: 8
items:
oneOf:
- minimum: 0
@@ -171,7 +171,7 @@ properties:
or applicable for the respective data port.
More info in MIPI Alliance SoundWire 1.0 Specifications.
minItems: 3
- maxItems: 5
+ maxItems: 8
items:
oneOf:
- minimum: 0
@@ -187,7 +187,7 @@ properties:
or applicable for the respective data port.
More info in MIPI Alliance SoundWire 1.0 Specifications.
minItems: 3
- maxItems: 5
+ maxItems: 8
items:
oneOf:
- minimum: 0
diff --git a/Documentation/devicetree/bindings/spi/atmel,at91rm9200-spi.yaml b/Documentation/devicetree/bindings/spi/atmel,at91rm9200-spi.yaml
index 4dd973e341e6c7..6c57dd6c3a3614 100644
--- a/Documentation/devicetree/bindings/spi/atmel,at91rm9200-spi.yaml
+++ b/Documentation/devicetree/bindings/spi/atmel,at91rm9200-spi.yaml
@@ -8,7 +8,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Atmel SPI device
maintainers:
- - Tudor Ambarus <tudor.ambarus@microchip.com>
+ - Tudor Ambarus <tudor.ambarus@linaro.org>
allOf:
- $ref: spi-controller.yaml#
diff --git a/Documentation/devicetree/bindings/spi/atmel,quadspi.yaml b/Documentation/devicetree/bindings/spi/atmel,quadspi.yaml
index 1d493add4053dd..b0d99bc105352c 100644
--- a/Documentation/devicetree/bindings/spi/atmel,quadspi.yaml
+++ b/Documentation/devicetree/bindings/spi/atmel,quadspi.yaml
@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Atmel Quad Serial Peripheral Interface (QSPI)
maintainers:
- - Tudor Ambarus <tudor.ambarus@microchip.com>
+ - Tudor Ambarus <tudor.ambarus@linaro.org>
allOf:
- $ref: spi-controller.yaml#
diff --git a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
index ead2cccf658fde..9a60c0664bbe7d 100644
--- a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
+++ b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
@@ -44,9 +44,9 @@ properties:
description:
Maximum SPI clocking speed of the device in Hz.
- spi-cs-setup-ns:
+ spi-cs-setup-delay-ns:
description:
- Delay in nanosecods to be introduced by the controller after CS is
+ Delay in nanoseconds to be introduced by the controller after CS is
asserted.
spi-rx-bus-width:
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 70ffb3780621bd..271ff119764a84 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -635,6 +635,8 @@ patternProperties:
description: Inverse Path
"^iom,.*":
description: Iomega Corporation
+ "^irondevice,.*":
+ description: Iron Device Corporation
"^isee,.*":
description: ISEE 2007 S.L.
"^isil,.*":
diff --git a/Documentation/filesystems/erofs.rst b/Documentation/filesystems/erofs.rst
index 067fd1670b1f1d..a43aacf1494e77 100644
--- a/Documentation/filesystems/erofs.rst
+++ b/Documentation/filesystems/erofs.rst
@@ -120,6 +120,8 @@ dax={always,never} Use direct access (no page cache). See
dax A legacy option which is an alias for ``dax=always``.
device=%s Specify a path to an extra device to be used together.
fsid=%s Specify a filesystem image ID for Fscache back-end.
+domain_id=%s Specify a domain ID in fscache mode so that different images
+ with the same blobs under a given domain ID can share storage.
=================== =========================================================
Sysfs Entries
diff --git a/Documentation/kbuild/makefiles.rst b/Documentation/kbuild/makefiles.rst
index 6b7368d1f51639..38bc74eaa54740 100644
--- a/Documentation/kbuild/makefiles.rst
+++ b/Documentation/kbuild/makefiles.rst
@@ -1042,7 +1042,7 @@ $(clean-files).
When executing "make clean", the file "crc32table.h" will be deleted.
Kbuild will assume files to be in the same relative directory as the
-Makefile, except if prefixed with $(objtree).
+Makefile.
To exclude certain files or directories from make clean, use the
$(no-clean-files) variable.
diff --git a/Documentation/maintainer/maintainer-entry-profile.rst b/Documentation/maintainer/maintainer-entry-profile.rst
index 93b2ae6c34a99b..cfd37f31077f6c 100644
--- a/Documentation/maintainer/maintainer-entry-profile.rst
+++ b/Documentation/maintainer/maintainer-entry-profile.rst
@@ -104,3 +104,4 @@ to do something different in the near future.
../riscv/patch-acceptance
../driver-api/media/maintainer-entry-profile
../driver-api/vfio-pci-device-specific-driver-acceptance
+ ../nvme/feature-and-quirk-policy
diff --git a/Documentation/networking/bridge.rst b/Documentation/networking/bridge.rst
index 4aef9cddde2fd6..c859f3c1636edf 100644
--- a/Documentation/networking/bridge.rst
+++ b/Documentation/networking/bridge.rst
@@ -8,7 +8,7 @@ In order to use the Ethernet bridging functionality, you'll need the
userspace tools.
Documentation for Linux bridging is on:
- http://www.linuxfoundation.org/collaborate/workgroups/networking/bridge
+ https://wiki.linuxfoundation.org/networking/bridge
The bridge-utilities are maintained at:
git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/bridge-utils.git
diff --git a/Documentation/networking/device_drivers/ethernet/intel/ice.rst b/Documentation/networking/device_drivers/ethernet/intel/ice.rst
index dc2e60ced927c3..b481b81f3be5ba 100644
--- a/Documentation/networking/device_drivers/ethernet/intel/ice.rst
+++ b/Documentation/networking/device_drivers/ethernet/intel/ice.rst
@@ -819,7 +819,7 @@ NAPI
----
This driver supports NAPI (Rx polling mode).
For more information on NAPI, see
-https://www.linuxfoundation.org/collaborate/workgroups/networking/napi
+https://wiki.linuxfoundation.org/networking/napi
MACVLAN
diff --git a/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst b/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst
index eaa87dbe8848d4..d052ef40fe3673 100644
--- a/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst
+++ b/Documentation/networking/device_drivers/ethernet/wangxun/txgbe.rst
@@ -16,5 +16,5 @@ Contents
Support
=======
-If you got any problem, contact Wangxun support team via support@trustnetic.com
+If you got any problem, contact Wangxun support team via nic-support@net-swift.com
and Cc: netdev.
diff --git a/Documentation/networking/nf_conntrack-sysctl.rst b/Documentation/networking/nf_conntrack-sysctl.rst
index 49db1d11d7c4a8..8b1045c3b59e0e 100644
--- a/Documentation/networking/nf_conntrack-sysctl.rst
+++ b/Documentation/networking/nf_conntrack-sysctl.rst
@@ -173,7 +173,9 @@ nf_conntrack_sctp_timeout_cookie_echoed - INTEGER (seconds)
default 3
nf_conntrack_sctp_timeout_established - INTEGER (seconds)
- default 432000 (5 days)
+ default 210
+
+ Default is set to (hb_interval * path_max_retrans + rto_max)
nf_conntrack_sctp_timeout_shutdown_sent - INTEGER (seconds)
default 0.3
@@ -190,12 +192,6 @@ nf_conntrack_sctp_timeout_heartbeat_sent - INTEGER (seconds)
This timeout is used to setup conntrack entry on secondary paths.
Default is set to hb_interval.
-nf_conntrack_sctp_timeout_heartbeat_acked - INTEGER (seconds)
- default 210
-
- This timeout is used to setup conntrack entry on secondary paths.
- Default is set to (hb_interval * path_max_retrans + rto_max)
-
nf_conntrack_udp_timeout - INTEGER (seconds)
default 30
diff --git a/Documentation/networking/rxrpc.rst b/Documentation/networking/rxrpc.rst
index 39494a6ea739cf..e1af54424192b3 100644
--- a/Documentation/networking/rxrpc.rst
+++ b/Documentation/networking/rxrpc.rst
@@ -880,8 +880,8 @@ The kernel interface functions are as follows:
notify_end_rx can be NULL or it can be used to specify a function to be
called when the call changes state to end the Tx phase. This function is
- called with the call-state spinlock held to prevent any reply or final ACK
- from being delivered first.
+ called with a spinlock held to prevent the last DATA packet from being
+ transmitted until the function returns.
(#) Receive data from a call::
diff --git a/Documentation/nvme/feature-and-quirk-policy.rst b/Documentation/nvme/feature-and-quirk-policy.rst
new file mode 100644
index 00000000000000..c01d836d8e4151
--- /dev/null
+++ b/Documentation/nvme/feature-and-quirk-policy.rst
@@ -0,0 +1,77 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=======================================
+Linux NVMe feature and and quirk policy
+=======================================
+
+This file explains the policy used to decide what is supported by the
+Linux NVMe driver and what is not.
+
+
+Introduction
+============
+
+NVM Express is an open collection of standards and information.
+
+The Linux NVMe host driver in drivers/nvme/host/ supports devices
+implementing the NVM Express (NVMe) family of specifications, which
+currently consists of a number of documents:
+
+ - the NVMe Base specification
+ - various Command Set specifications (e.g. NVM Command Set)
+ - various Transport specifications (e.g. PCIe, Fibre Channel, RDMA, TCP)
+ - the NVMe Management Interface specification
+
+See https://nvmexpress.org/developers/ for the NVMe specifications.
+
+
+Supported features
+==================
+
+NVMe is a large suite of specifications, and contains features that are only
+useful or suitable for specific use-cases. It is important to note that Linux
+does not aim to implement every feature in the specification. Every additional
+feature implemented introduces more code, more maintenance and potentially more
+bugs. Hence there is an inherent tradeoff between functionality and
+maintainability of the NVMe host driver.
+
+Any feature implemented in the Linux NVMe host driver must support the
+following requirements:
+
+ 1. The feature is specified in a release version of an official NVMe
+ specification, or in a ratified Technical Proposal (TP) that is
+ available on NVMe website. Or if it is not directly related to the
+ on-wire protocol, does not contradict any of the NVMe specifications.
+ 2. Does not conflict with the Linux architecture, nor the design of the
+ NVMe host driver.
+ 3. Has a clear, indisputable value-proposition and a wide consensus across
+ the community.
+
+Vendor specific extensions are generally not supported in the NVMe host
+driver.
+
+It is strongly recommended to work with the Linux NVMe and block layer
+maintainers and get feedback on specification changes that are intended
+to be used by the Linux NVMe host driver in order to avoid conflict at a
+later stage.
+
+
+Quirks
+======
+
+Sometimes implementations of open standards fail to correctly implement parts
+of the standards. Linux uses identifier-based quirks to work around such
+implementation bugs. The intent of quirks is to deal with widely available
+hardware, usually consumer, which Linux users can't use without these quirks.
+Typically these implementations are not or only superficially tested with Linux
+by the hardware manufacturer.
+
+The Linux NVMe maintainers decide ad hoc whether to quirk implementations
+based on the impact of the problem to Linux users and how it impacts
+maintainability of the driver. In general quirks are a last resort, if no
+firmware updates or other workarounds are available from the vendor.
+
+Quirks will not be added to the Linux kernel for hardware that isn't available
+on the mass market. Hardware that fails qualification for enterprise Linux
+distributions, ChromeOS, Android or other consumers of the Linux kernel
+should be fixed before it is shipped instead of relying on Linux quirks.
diff --git a/Documentation/process/maintainer-netdev.rst b/Documentation/process/maintainer-netdev.rst
index 1fa5ab8754d358..4a75686d35ab4a 100644
--- a/Documentation/process/maintainer-netdev.rst
+++ b/Documentation/process/maintainer-netdev.rst
@@ -2,9 +2,9 @@
.. _netdev-FAQ:
-==========
-netdev FAQ
-==========
+=============================
+Networking subsystem (netdev)
+=============================
tl;dr
-----
@@ -15,14 +15,15 @@ tl;dr
- don't repost your patches within one 24h period
- reverse xmas tree
-What is netdev?
----------------
-It is a mailing list for all network-related Linux stuff. This
+netdev
+------
+
+netdev is a mailing list for all network-related Linux stuff. This
includes anything found under net/ (i.e. core code like IPv6) and
drivers/net (i.e. hardware specific drivers) in the Linux source tree.
Note that some subsystems (e.g. wireless drivers) which have a high
-volume of traffic have their own specific mailing lists.
+volume of traffic have their own specific mailing lists and trees.
The netdev list is managed (like many other Linux mailing lists) through
VGER (http://vger.kernel.org/) with archives available at
@@ -32,32 +33,10 @@ Aside from subsystems like those mentioned above, all network-related
Linux development (i.e. RFC, review, comments, etc.) takes place on
netdev.
-How do the changes posted to netdev make their way into Linux?
---------------------------------------------------------------
-There are always two trees (git repositories) in play. Both are
-driven by David Miller, the main network maintainer. There is the
-``net`` tree, and the ``net-next`` tree. As you can probably guess from
-the names, the ``net`` tree is for fixes to existing code already in the
-mainline tree from Linus, and ``net-next`` is where the new code goes
-for the future release. You can find the trees here:
-
-- https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git
-- https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git
-
-How do I indicate which tree (net vs. net-next) my patch should be in?
-----------------------------------------------------------------------
-To help maintainers and CI bots you should explicitly mark which tree
-your patch is targeting. Assuming that you use git, use the prefix
-flag::
-
- git format-patch --subject-prefix='PATCH net-next' start..finish
+Development cycle
+-----------------
-Use ``net`` instead of ``net-next`` (always lower case) in the above for
-bug-fix ``net`` content.
-
-How often do changes from these trees make it to the mainline Linus tree?
--------------------------------------------------------------------------
-To understand this, you need to know a bit of background information on
+Here is a bit of background information on
the cadence of Linux development. Each new release starts off with a
two week "merge window" where the main maintainers feed their new stuff
to Linus for merging into the mainline tree. After the two weeks, the
@@ -69,9 +48,33 @@ rc2 is released. This repeats on a roughly weekly basis until rc7
state of churn), and a week after the last vX.Y-rcN was done, the
official vX.Y is released.
-Relating that to netdev: At the beginning of the 2-week merge window,
-the ``net-next`` tree will be closed - no new changes/features. The
-accumulated new content of the past ~10 weeks will be passed onto
+To find out where we are now in the cycle - load the mainline (Linus)
+page here:
+
+ https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
+
+and note the top of the "tags" section. If it is rc1, it is early in
+the dev cycle. If it was tagged rc7 a week ago, then a release is
+probably imminent. If the most recent tag is a final release tag
+(without an ``-rcN`` suffix) - we are most likely in a merge window
+and ``net-next`` is closed.
+
+git trees and patch flow
+------------------------
+
+There are two networking trees (git repositories) in play. Both are
+driven by David Miller, the main network maintainer. There is the
+``net`` tree, and the ``net-next`` tree. As you can probably guess from
+the names, the ``net`` tree is for fixes to existing code already in the
+mainline tree from Linus, and ``net-next`` is where the new code goes
+for the future release. You can find the trees here:
+
+- https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git
+- https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git
+
+Relating that to kernel development: At the beginning of the 2-week
+merge window, the ``net-next`` tree will be closed - no new changes/features.
+The accumulated new content of the past ~10 weeks will be passed onto
mainline/Linus via a pull request for vX.Y -- at the same time, the
``net`` tree will start accumulating fixes for this pulled content
relating to vX.Y
@@ -103,22 +106,14 @@ focus for ``net`` is on stabilization and bug fixes.
Finally, the vX.Y gets released, and the whole cycle starts over.
-So where are we now in this cycle?
-----------------------------------
+netdev patch review
+-------------------
-Load the mainline (Linus) page here:
+Patch status
+~~~~~~~~~~~~
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
-
-and note the top of the "tags" section. If it is rc1, it is early in
-the dev cycle. If it was tagged rc7 a week ago, then a release is
-probably imminent. If the most recent tag is a final release tag
-(without an ``-rcN`` suffix) - we are most likely in a merge window
-and ``net-next`` is closed.
-
-How can I tell the status of a patch I've sent?
------------------------------------------------
-Start by looking at the main patchworks queue for netdev:
+Status of a patch can be checked by looking at the main patchwork
+queue for netdev:
https://patchwork.kernel.org/project/netdevbpf/list/
@@ -127,73 +122,141 @@ patch. Patches are indexed by the ``Message-ID`` header of the emails
which carried them so if you have trouble finding your patch append
the value of ``Message-ID`` to the URL above.
-How long before my patch is accepted?
--------------------------------------
-Generally speaking, the patches get triaged quickly (in less than
-48h). But be patient, if your patch is active in patchwork (i.e. it's
-listed on the project's patch list) the chances it was missed are close to zero.
-Asking the maintainer for status updates on your
-patch is a good way to ensure your patch is ignored or pushed to the
-bottom of the priority list.
+Updating patch status
+~~~~~~~~~~~~~~~~~~~~~
-Should I directly update patchwork state of my own patches?
------------------------------------------------------------
It may be tempting to help the maintainers and update the state of your
-own patches when you post a new version or spot a bug. Please do not do that.
+own patches when you post a new version or spot a bug. Please **do not**
+do that.
Interfering with the patch status on patchwork will only cause confusion. Leave
it to the maintainer to figure out what is the most recent and current
version that should be applied. If there is any doubt, the maintainer
will reply and ask what should be done.
-How do I divide my work into patches?
--------------------------------------
+Review timelines
+~~~~~~~~~~~~~~~~
-Put yourself in the shoes of the reviewer. Each patch is read separately
-and therefore should constitute a comprehensible step towards your stated
-goal.
+Generally speaking, the patches get triaged quickly (in less than
+48h). But be patient, if your patch is active in patchwork (i.e. it's
+listed on the project's patch list) the chances it was missed are close to zero.
+Asking the maintainer for status updates on your
+patch is a good way to ensure your patch is ignored or pushed to the
+bottom of the priority list.
-Avoid sending series longer than 15 patches. Larger series takes longer
-to review as reviewers will defer looking at it until they find a large
-chunk of time. A small series can be reviewed in a short time, so Maintainers
-just do it. As a result, a sequence of smaller series gets merged quicker and
-with better review coverage. Re-posting large series also increases the mailing
-list traffic.
+Partial resends
+~~~~~~~~~~~~~~~
-I made changes to only a few patches in a patch series should I resend only those changed?
-------------------------------------------------------------------------------------------
-No, please resend the entire patch series and make sure you do number your
+Please always resend the entire patch series and make sure you do number your
patches such that it is clear this is the latest and greatest set of patches
-that can be applied.
-
-I have received review feedback, when should I post a revised version of the patches?
--------------------------------------------------------------------------------------
-Allow at least 24 hours to pass between postings. This will ensure reviewers
-from all geographical locations have a chance to chime in. Do not wait
-too long (weeks) between postings either as it will make it harder for reviewers
-to recall all the context.
+that can be applied. Do not try to resend just the patches which changed.
-Make sure you address all the feedback in your new posting. Do not post a new
-version of the code if the discussion about the previous version is still
-ongoing, unless directly instructed by a reviewer.
+Handling misapplied patches
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
-I submitted multiple versions of a patch series and it looks like a version other than the last one has been accepted, what should I do?
-----------------------------------------------------------------------------------------------------------------------------------------
+Occasionally a patch series gets applied before receiving critical feedback,
+or the wrong version of a series gets applied.
There is no revert possible, once it is pushed out, it stays like that.
Please send incremental versions on top of what has been merged in order to fix
the patches the way they would look like if your latest patch series was to be
merged.
-Are there special rules regarding stable submissions on netdev?
----------------------------------------------------------------
+Stable tree
+~~~~~~~~~~~
+
While it used to be the case that netdev submissions were not supposed
to carry explicit ``CC: stable@vger.kernel.org`` tags that is no longer
the case today. Please follow the standard stable rules in
:ref:`Documentation/process/stable-kernel-rules.rst <stable_kernel_rules>`,
and make sure you include appropriate Fixes tags!
-Is the comment style convention different for the networking content?
----------------------------------------------------------------------
-Yes, in a largely trivial way. Instead of this::
+Security fixes
+~~~~~~~~~~~~~~
+
+Do not email netdev maintainers directly if you think you discovered
+a bug that might have possible security implications.
+The current netdev maintainer has consistently requested that
+people use the mailing lists and not reach out directly. If you aren't
+OK with that, then perhaps consider mailing security@kernel.org or
+reading about http://oss-security.openwall.org/wiki/mailing-lists/distros
+as possible alternative mechanisms.
+
+
+Co-posting changes to user space components
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+User space code exercising kernel features should be posted
+alongside kernel patches. This gives reviewers a chance to see
+how any new interface is used and how well it works.
+
+When user space tools reside in the kernel repo itself all changes
+should generally come as one series. If series becomes too large
+or the user space project is not reviewed on netdev include a link
+to a public repo where user space patches can be seen.
+
+In case user space tooling lives in a separate repository but is
+reviewed on netdev (e.g. patches to ``iproute2`` tools) kernel and
+user space patches should form separate series (threads) when posted
+to the mailing list, e.g.::
+
+ [PATCH net-next 0/3] net: some feature cover letter
+ └─ [PATCH net-next 1/3] net: some feature prep
+ └─ [PATCH net-next 2/3] net: some feature do it
+ └─ [PATCH net-next 3/3] selftest: net: some feature
+
+ [PATCH iproute2-next] ip: add support for some feature
+
+Posting as one thread is discouraged because it confuses patchwork
+(as of patchwork 2.2.2).
+
+Preparing changes
+-----------------
+
+Attention to detail is important. Re-read your own work as if you were the
+reviewer. You can start with using ``checkpatch.pl``, perhaps even with
+the ``--strict`` flag. But do not be mindlessly robotic in doing so.
+If your change is a bug fix, make sure your commit log indicates the
+end-user visible symptom, the underlying reason as to why it happens,
+and then if necessary, explain why the fix proposed is the best way to
+get things done. Don't mangle whitespace, and as is common, don't
+mis-indent function arguments that span multiple lines. If it is your
+first patch, mail it to yourself so you can test apply it to an
+unpatched tree to confirm infrastructure didn't mangle it.
+
+Finally, go back and read
+:ref:`Documentation/process/submitting-patches.rst <submittingpatches>`
+to be sure you are not repeating some common mistake documented there.
+
+Indicating target tree
+~~~~~~~~~~~~~~~~~~~~~~
+
+To help maintainers and CI bots you should explicitly mark which tree
+your patch is targeting. Assuming that you use git, use the prefix
+flag::
+
+ git format-patch --subject-prefix='PATCH net-next' start..finish
+
+Use ``net`` instead of ``net-next`` (always lower case) in the above for
+bug-fix ``net`` content.
+
+Dividing work into patches
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Put yourself in the shoes of the reviewer. Each patch is read separately
+and therefore should constitute a comprehensible step towards your stated
+goal.
+
+Avoid sending series longer than 15 patches. Larger series takes longer
+to review as reviewers will defer looking at it until they find a large
+chunk of time. A small series can be reviewed in a short time, so Maintainers
+just do it. As a result, a sequence of smaller series gets merged quicker and
+with better review coverage. Re-posting large series also increases the mailing
+list traffic.
+
+Multi-line comments
+~~~~~~~~~~~~~~~~~~~
+
+Comment style convention is slightly different for networking and most of
+the tree. Instead of this::
/*
* foobar blah blah blah
@@ -206,8 +269,8 @@ it is requested that you make it look like this::
* another line of text
*/
-What is "reverse xmas tree"?
-----------------------------
+Local variable ordering ("reverse xmas tree", "RCS")
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Netdev has a convention for ordering local variables in functions.
Order the variable declaration lines longest to shortest, e.g.::
@@ -219,21 +282,31 @@ Order the variable declaration lines longest to shortest, e.g.::
If there are dependencies between the variables preventing the ordering
move the initialization out of line.
-I am working in existing code which uses non-standard formatting. Which formatting should I use?
-------------------------------------------------------------------------------------------------
-Make your code follow the most recent guidelines, so that eventually all code
+Format precedence
+~~~~~~~~~~~~~~~~~
+
+When working in existing code which uses nonstandard formatting make
+your code follow the most recent guidelines, so that eventually all code
in the domain of netdev is in the preferred format.
-I found a bug that might have possible security implications or similar. Should I mail the main netdev maintainer off-list?
----------------------------------------------------------------------------------------------------------------------------
-No. The current netdev maintainer has consistently requested that
-people use the mailing lists and not reach out directly. If you aren't
-OK with that, then perhaps consider mailing security@kernel.org or
-reading about http://oss-security.openwall.org/wiki/mailing-lists/distros
-as possible alternative mechanisms.
+Resending after review
+~~~~~~~~~~~~~~~~~~~~~~
+
+Allow at least 24 hours to pass between postings. This will ensure reviewers
+from all geographical locations have a chance to chime in. Do not wait
+too long (weeks) between postings either as it will make it harder for reviewers
+to recall all the context.
+
+Make sure you address all the feedback in your new posting. Do not post a new
+version of the code if the discussion about the previous version is still
+ongoing, unless directly instructed by a reviewer.
+
+Testing
+-------
+
+Expected level of testing
+~~~~~~~~~~~~~~~~~~~~~~~~~
-What level of testing is expected before I submit my change?
-------------------------------------------------------------
At the very minimum your changes must survive an ``allyesconfig`` and an
``allmodconfig`` build with ``W=1`` set without new warnings or failures.
@@ -244,86 +317,42 @@ and the patch series contains a set of kernel selftest for
You are expected to test your changes on top of the relevant networking
tree (``net`` or ``net-next``) and not e.g. a stable tree or ``linux-next``.
-How do I post corresponding changes to user space components?
--------------------------------------------------------------
-User space code exercising kernel features should be posted
-alongside kernel patches. This gives reviewers a chance to see
-how any new interface is used and how well it works.
-
-When user space tools reside in the kernel repo itself all changes
-should generally come as one series. If series becomes too large
-or the user space project is not reviewed on netdev include a link
-to a public repo where user space patches can be seen.
-
-In case user space tooling lives in a separate repository but is
-reviewed on netdev (e.g. patches to ``iproute2`` tools) kernel and
-user space patches should form separate series (threads) when posted
-to the mailing list, e.g.::
-
- [PATCH net-next 0/3] net: some feature cover letter
- └─ [PATCH net-next 1/3] net: some feature prep
- └─ [PATCH net-next 2/3] net: some feature do it
- └─ [PATCH net-next 3/3] selftest: net: some feature
-
- [PATCH iproute2-next] ip: add support for some feature
-
-Posting as one thread is discouraged because it confuses patchwork
-(as of patchwork 2.2.2).
-
-Can I reproduce the checks from patchwork on my local machine?
---------------------------------------------------------------
+patchwork checks
+~~~~~~~~~~~~~~~~
Checks in patchwork are mostly simple wrappers around existing kernel
scripts, the sources are available at:
https://github.com/kuba-moo/nipa/tree/master/tests
-Running all the builds and checks locally is a pain, can I post my patches and have the patchwork bot validate them?
---------------------------------------------------------------------------------------------------------------------
-
-No, you must ensure that your patches are ready by testing them locally
+**Do not** post your patches just to run them through the checks.
+You must ensure that your patches are ready by testing them locally
before posting to the mailing list. The patchwork build bot instance
gets overloaded very easily and netdev@vger really doesn't need more
traffic if we can help it.
-netdevsim is great, can I extend it for my out-of-tree tests?
--------------------------------------------------------------
+netdevsim
+~~~~~~~~~
-No, ``netdevsim`` is a test vehicle solely for upstream tests.
-(Please add your tests under ``tools/testing/selftests/``.)
+``netdevsim`` is a test driver which can be used to exercise driver
+configuration APIs without requiring capable hardware.
+Mock-ups and tests based on ``netdevsim`` are strongly encouraged when
+adding new APIs, but ``netdevsim`` in itself is **not** considered
+a use case/user. You must also implement the new APIs in a real driver.
-We also give no guarantees that ``netdevsim`` won't change in the future
+We give no guarantees that ``netdevsim`` won't change in the future
in a way which would break what would normally be considered uAPI.
-Is netdevsim considered a "user" of an API?
--------------------------------------------
-
-Linux kernel has a long standing rule that no API should be added unless
-it has a real, in-tree user. Mock-ups and tests based on ``netdevsim`` are
-strongly encouraged when adding new APIs, but ``netdevsim`` in itself
-is **not** considered a use case/user.
-
-Any other tips to help ensure my net/net-next patch gets OK'd?
---------------------------------------------------------------
-Attention to detail. Re-read your own work as if you were the
-reviewer. You can start with using ``checkpatch.pl``, perhaps even with
-the ``--strict`` flag. But do not be mindlessly robotic in doing so.
-If your change is a bug fix, make sure your commit log indicates the
-end-user visible symptom, the underlying reason as to why it happens,
-and then if necessary, explain why the fix proposed is the best way to
-get things done. Don't mangle whitespace, and as is common, don't
-mis-indent function arguments that span multiple lines. If it is your
-first patch, mail it to yourself so you can test apply it to an
-unpatched tree to confirm infrastructure didn't mangle it.
-
-Finally, go back and read
-:ref:`Documentation/process/submitting-patches.rst <submittingpatches>`
-to be sure you are not repeating some common mistake documented there.
+``netdevsim`` is reserved for use by upstream tests only, so any
+new ``netdevsim`` features must be accompanied by selftests under
+``tools/testing/selftests/``.
-My company uses peer feedback in employee performance reviews. Can I ask netdev maintainers for feedback?
----------------------------------------------------------------------------------------------------------
+Testimonials / feedback
+-----------------------
-Yes, especially if you spend significant amount of time reviewing code
+Some companies use peer feedback in employee performance reviews.
+Please feel free to request feedback from netdev maintainers,
+especially if you spend significant amount of time reviewing code
and go out of your way to improve shared infrastructure.
The feedback must be requested by you, the contributor, and will always
diff --git a/Documentation/sound/alsa-configuration.rst b/Documentation/sound/alsa-configuration.rst
index 21ab5e6f7062f4..5f31fa5e24356d 100644
--- a/Documentation/sound/alsa-configuration.rst
+++ b/Documentation/sound/alsa-configuration.rst
@@ -70,7 +70,7 @@ dsp_map
PCM device number maps assigned to the 1st OSS device;
Default: 0
adsp_map
- PCM device number maps assigned to the 2st OSS device;
+ PCM device number maps assigned to the 2nd OSS device;
Default: 1
nonblock_open
Don't block opening busy PCM devices;
@@ -97,7 +97,7 @@ midi_map
MIDI device number maps assigned to the 1st OSS device;
Default: 0
amidi_map
- MIDI device number maps assigned to the 2st OSS device;
+ MIDI device number maps assigned to the 2nd OSS device;
Default: 1
Module snd-soc-core
@@ -727,9 +727,9 @@ Module for EMU10K1/EMU10k2 based PCI sound cards.
* Sound Blaster Audigy
extin
- bitmap of available external inputs for FX8010 (see bellow)
+ bitmap of available external inputs for FX8010 (see below)
extout
- bitmap of available external outputs for FX8010 (see bellow)
+ bitmap of available external outputs for FX8010 (see below)
seq_ports
allocated sequencer ports (4 by default)
max_synth_voices
diff --git a/Documentation/sound/cards/audigy-mixer.rst b/Documentation/sound/cards/audigy-mixer.rst
index f3f4640ee2afc6..c506f8d16f2e5a 100644
--- a/Documentation/sound/cards/audigy-mixer.rst
+++ b/Documentation/sound/cards/audigy-mixer.rst
@@ -17,7 +17,7 @@ Digital mixer controls
======================
These controls are built using the DSP instructions. They offer extended
-functionality. Only the default build-in code in the ALSA driver is described
+functionality. Only the default built-in code in the ALSA driver is described
here. Note that the controls work as attenuators: the maximum value is the
neutral position leaving the signal unchanged. Note that if the same destination
is mentioned in multiple controls, the signal is accumulated and can be wrapped
diff --git a/Documentation/sound/cards/maya44.rst b/Documentation/sound/cards/maya44.rst
index bf09a584b4431b..ab973f66c973b3 100644
--- a/Documentation/sound/cards/maya44.rst
+++ b/Documentation/sound/cards/maya44.rst
@@ -156,7 +156,7 @@ IEC958 Output
S/PDIF should output the same signal as channel 3+4. [untested!]
-Digitial output selectors
+Digital output selectors
These switches allow a direct digital routing from the ADCs to the DACs.
Each switch determines where the digital input data to one of the DACs comes from.
They are not supported by the ESI windows driver.
diff --git a/Documentation/sound/cards/sb-live-mixer.rst b/Documentation/sound/cards/sb-live-mixer.rst
index 2ce41d3822d8b9..357fcd619d39d3 100644
--- a/Documentation/sound/cards/sb-live-mixer.rst
+++ b/Documentation/sound/cards/sb-live-mixer.rst
@@ -31,7 +31,7 @@ Digital mixer controls
======================
These controls are built using the DSP instructions. They offer extended
-functionality. Only the default build-in code in the ALSA driver is described
+functionality. Only the default built-in code in the ALSA driver is described
here. Note that the controls work as attenuators: the maximum value is the
neutral position leaving the signal unchanged. Note that if the same destination
is mentioned in multiple controls, the signal is accumulated and can be wrapped
diff --git a/Documentation/sound/designs/jack-controls.rst b/Documentation/sound/designs/jack-controls.rst
index ae25b1531bb0c5..e8a18f126a6369 100644
--- a/Documentation/sound/designs/jack-controls.rst
+++ b/Documentation/sound/designs/jack-controls.rst
@@ -8,7 +8,7 @@ Why we need Jack kcontrols
ALSA uses kcontrols to export audio controls(switch, volume, Mux, ...)
to user space. This means userspace applications like pulseaudio can
switch off headphones and switch on speakers when no headphones are
-pluged in.
+plugged in.
The old ALSA jack code only created input devices for each registered
jack. These jack input devices are not readable by userspace devices
diff --git a/Documentation/sound/designs/seq-oss.rst b/Documentation/sound/designs/seq-oss.rst
index e82ffe0e7f4361..ec6304a07441fe 100644
--- a/Documentation/sound/designs/seq-oss.rst
+++ b/Documentation/sound/designs/seq-oss.rst
@@ -96,7 +96,7 @@ if you use an AWE64 card, you'll see like the following:
Number of synth devices: 1
synth 0: [EMU8000]
type 0x1 : subtype 0x20 : voices 32
- capabilties : ioctl enabled / load_patch enabled
+ capabilities : ioctl enabled / load_patch enabled
Number of MIDI devices: 3
midi 0: [Emu8000 Port-0] ALSA port 65:0
diff --git a/Documentation/sound/hd-audio/notes.rst b/Documentation/sound/hd-audio/notes.rst
index d118b6fe269b96..3432feb0fa3320 100644
--- a/Documentation/sound/hd-audio/notes.rst
+++ b/Documentation/sound/hd-audio/notes.rst
@@ -500,7 +500,7 @@ add_jack_modes (bool)
change the headphone amp and mic bias VREF capabilities
power_save_node (bool)
advanced power management for each widget, controlling the power
- sate (D0/D3) of each widget node depending on the actual pin and
+ state (D0/D3) of each widget node depending on the actual pin and
stream states
power_down_unused (bool)
power down the unused widgets, a subset of power_save_node, and
diff --git a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst
index 07a620c5ca7451..5c9523b7d55cdb 100644
--- a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst
+++ b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst
@@ -1720,16 +1720,16 @@ Typically, you'll have a hardware descriptor as below:
- ``rate_min`` and ``rate_max`` define the minimum and maximum sample
rate. This should correspond somehow to ``rates`` bits.
-- ``channel_min`` and ``channel_max`` define, as you might already
+- ``channels_min`` and ``channels_max`` define, as you might already
expected, the minimum and maximum number of channels.
- ``buffer_bytes_max`` defines the maximum buffer size in
bytes. There is no ``buffer_bytes_min`` field, since it can be
calculated from the minimum period size and the minimum number of
- periods. Meanwhile, ``period_bytes_min`` and define the minimum and
- maximum size of the period in bytes. ``periods_max`` and
- ``periods_min`` define the maximum and minimum number of periods in
- the buffer.
+ periods. Meanwhile, ``period_bytes_min`` and ``period_bytes_max``
+ define the minimum and maximum size of the period in bytes.
+ ``periods_max`` and ``periods_min`` define the maximum and minimum
+ number of periods in the buffer.
The “period” is a term that corresponds to a fragment in the OSS
world. The period defines the size at which a PCM interrupt is
diff --git a/Documentation/sphinx/load_config.py b/Documentation/sphinx/load_config.py
index eeb394b39e2cc8..8b416bfd75ac17 100644
--- a/Documentation/sphinx/load_config.py
+++ b/Documentation/sphinx/load_config.py
@@ -3,7 +3,7 @@
import os
import sys
-from sphinx.util.pycompat import execfile_
+from sphinx.util.osutil import fs_encoding
# ------------------------------------------------------------------------------
def loadConfig(namespace):
@@ -48,7 +48,9 @@ def loadConfig(namespace):
sys.stdout.write("load additional sphinx-config: %s\n" % config_file)
config = namespace.copy()
config['__file__'] = config_file
- execfile_(config_file, config)
+ with open(config_file, 'rb') as f:
+ code = compile(f.read(), fs_encoding, 'exec')
+ exec(code, config)
del config['__file__']
namespace.update(config)
else:
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index 0dd5d8733dd583..0a67cb738013e6 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -1354,6 +1354,14 @@ the memory region are automatically reflected into the guest. For example, an
mmap() that affects the region will be made visible immediately. Another
example is madvise(MADV_DROP).
+Note: On arm64, a write generated by the page-table walker (to update
+the Access and Dirty flags, for example) never results in a
+KVM_EXIT_MMIO exit when the slot has the KVM_MEM_READONLY flag. This
+is because KVM cannot provide the data that would be written by the
+page-table walker, making it impossible to emulate the access.
+Instead, an abort (data abort if the cause of the page-table update
+was a load or a store, instruction abort if it was an instruction
+fetch) is injected in the guest.
4.36 KVM_SET_TSS_ADDR
---------------------
@@ -5343,9 +5351,9 @@ KVM_XEN_ATTR_TYPE_SHARED_INFO
32 vCPUs in the shared_info page, KVM does not automatically do so
and instead requires that KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO be used
explicitly even when the vcpu_info for a given vCPU resides at the
- "default" location in the shared_info page. This is because KVM is
- not aware of the Xen CPU id which is used as the index into the
- vcpu_info[] array, so cannot know the correct default location.
+ "default" location in the shared_info page. This is because KVM may
+ not be aware of the Xen CPU id which is used as the index into the
+ vcpu_info[] array, so may know the correct default location.
Note that the shared info page may be constantly written to by KVM;
it contains the event channel bitmap used to deliver interrupts to
@@ -5356,23 +5364,29 @@ KVM_XEN_ATTR_TYPE_SHARED_INFO
any vCPU has been running or any event channel interrupts can be
routed to the guest.
+ Setting the gfn to KVM_XEN_INVALID_GFN will disable the shared info
+ page.
+
KVM_XEN_ATTR_TYPE_UPCALL_VECTOR
Sets the exception vector used to deliver Xen event channel upcalls.
This is the HVM-wide vector injected directly by the hypervisor
(not through the local APIC), typically configured by a guest via
- HVM_PARAM_CALLBACK_IRQ.
+ HVM_PARAM_CALLBACK_IRQ. This can be disabled again (e.g. for guest
+ SHUTDOWN_soft_reset) by setting it to zero.
KVM_XEN_ATTR_TYPE_EVTCHN
This attribute is available when the KVM_CAP_XEN_HVM ioctl indicates
support for KVM_XEN_HVM_CONFIG_EVTCHN_SEND features. It configures
an outbound port number for interception of EVTCHNOP_send requests
- from the guest. A given sending port number may be directed back
- to a specified vCPU (by APIC ID) / port / priority on the guest,
- or to trigger events on an eventfd. The vCPU and priority can be
- changed by setting KVM_XEN_EVTCHN_UPDATE in a subsequent call,
- but other fields cannot change for a given sending port. A port
- mapping is removed by using KVM_XEN_EVTCHN_DEASSIGN in the flags
- field.
+ from the guest. A given sending port number may be directed back to
+ a specified vCPU (by APIC ID) / port / priority on the guest, or to
+ trigger events on an eventfd. The vCPU and priority can be changed
+ by setting KVM_XEN_EVTCHN_UPDATE in a subsequent call, but but other
+ fields cannot change for a given sending port. A port mapping is
+ removed by using KVM_XEN_EVTCHN_DEASSIGN in the flags field. Passing
+ KVM_XEN_EVTCHN_RESET in the flags field removes all interception of
+ outbound event channels. The values of the flags field are mutually
+ exclusive and cannot be combined as a bitmask.
KVM_XEN_ATTR_TYPE_XEN_VERSION
This attribute is available when the KVM_CAP_XEN_HVM ioctl indicates
@@ -5388,7 +5402,7 @@ KVM_XEN_ATTR_TYPE_RUNSTATE_UPDATE_FLAG
support for KVM_XEN_HVM_CONFIG_RUNSTATE_UPDATE_FLAG. It enables the
XEN_RUNSTATE_UPDATE flag which allows guest vCPUs to safely read
other vCPUs' vcpu_runstate_info. Xen guests enable this feature via
- the VM_ASST_TYPE_runstate_update_flag of the HYPERVISOR_vm_assist
+ the VMASST_TYPE_runstate_update_flag of the HYPERVISOR_vm_assist
hypercall.
4.127 KVM_XEN_HVM_GET_ATTR
@@ -5446,15 +5460,18 @@ KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO
As with the shared_info page for the VM, the corresponding page may be
dirtied at any time if event channel interrupt delivery is enabled, so
userspace should always assume that the page is dirty without relying
- on dirty logging.
+ on dirty logging. Setting the gpa to KVM_XEN_INVALID_GPA will disable
+ the vcpu_info.
KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO
Sets the guest physical address of an additional pvclock structure
for a given vCPU. This is typically used for guest vsyscall support.
+ Setting the gpa to KVM_XEN_INVALID_GPA will disable the structure.
KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR
Sets the guest physical address of the vcpu_runstate_info for a given
vCPU. This is how a Xen guest tracks CPU state such as steal time.
+ Setting the gpa to KVM_XEN_INVALID_GPA will disable the runstate area.
KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_CURRENT
Sets the runstate (RUNSTATE_running/_runnable/_blocked/_offline) of
@@ -5487,7 +5504,8 @@ KVM_XEN_VCPU_ATTR_TYPE_TIMER
This attribute is available when the KVM_CAP_XEN_HVM ioctl indicates
support for KVM_XEN_HVM_CONFIG_EVTCHN_SEND features. It sets the
event channel port/priority for the VIRQ_TIMER of the vCPU, as well
- as allowing a pending timer to be saved/restored.
+ as allowing a pending timer to be saved/restored. Setting the timer
+ port to zero disables kernel handling of the singleshot timer.
KVM_XEN_VCPU_ATTR_TYPE_UPCALL_VECTOR
This attribute is available when the KVM_CAP_XEN_HVM ioctl indicates
@@ -5495,7 +5513,8 @@ KVM_XEN_VCPU_ATTR_TYPE_UPCALL_VECTOR
per-vCPU local APIC upcall vector, configured by a Xen guest with
the HVMOP_set_evtchn_upcall_vector hypercall. This is typically
used by Windows guests, and is distinct from the HVM-wide upcall
- vector configured with HVM_PARAM_CALLBACK_IRQ.
+ vector configured with HVM_PARAM_CALLBACK_IRQ. It is disabled by
+ setting the vector to zero.
4.129 KVM_XEN_VCPU_GET_ATTR
@@ -6577,11 +6596,6 @@ Please note that the kernel is allowed to use the kvm_run structure as the
primary storage for certain register types. Therefore, the kernel may use the
values in kvm_run even if the corresponding bit in kvm_dirty_regs is not set.
-::
-
- };
-
-
6. Capabilities that can be enabled on vCPUs
============================================
@@ -8056,9 +8070,13 @@ considering the state as complete. VMM needs to ensure that the dirty
state is final and avoid missing dirty pages from another ioctl ordered
after the bitmap collection.
-NOTE: One example of using the backup bitmap is saving arm64 vgic/its
-tables through KVM_DEV_ARM_{VGIC_GRP_CTRL, ITS_SAVE_TABLES} command on
-KVM device "kvm-arm-vgic-its" when dirty ring is enabled.
+NOTE: Multiple examples of using the backup bitmap: (1) save vgic/its
+tables through command KVM_DEV_ARM_{VGIC_GRP_CTRL, ITS_SAVE_TABLES} on
+KVM device "kvm-arm-vgic-its". (2) restore vgic/its tables through
+command KVM_DEV_ARM_{VGIC_GRP_CTRL, ITS_RESTORE_TABLES} on KVM device
+"kvm-arm-vgic-its". VGICv3 LPI pending status is restored. (3) save
+vgic3 pending table through KVM_DEV_ARM_VGIC_{GRP_CTRL, SAVE_PENDING_TABLES}
+command on KVM device "kvm-arm-vgic-v3".
8.30 KVM_CAP_XEN_HVM
--------------------
@@ -8304,6 +8322,20 @@ CPU[EAX=1]:ECX[24] (TSC_DEADLINE) is not reported by ``KVM_GET_SUPPORTED_CPUID``
It can be enabled if ``KVM_CAP_TSC_DEADLINE_TIMER`` is present and the kernel
has enabled in-kernel emulation of the local APIC.
+CPU topology
+~~~~~~~~~~~~
+
+Several CPUID values include topology information for the host CPU:
+0x0b and 0x1f for Intel systems, 0x8000001e for AMD systems. Different
+versions of KVM return different values for this information and userspace
+should not rely on it. Currently they return all zeroes.
+
+If userspace wishes to set up a guest topology, it should be careful that
+the values of these three leaves differ for each CPU. In particular,
+the APIC ID is found in EDX for all subleaves of 0x0b and 0x1f, and in EAX
+for 0x8000001e; the latter also encodes the core id and node id in bits
+7:0 of EBX and ECX respectively.
+
Obsolete ioctls and capabilities
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/Documentation/virt/kvm/locking.rst b/Documentation/virt/kvm/locking.rst
index 845a561629f19d..a0146793d1972d 100644
--- a/Documentation/virt/kvm/locking.rst
+++ b/Documentation/virt/kvm/locking.rst
@@ -16,20 +16,30 @@ The acquisition orders for mutexes are as follows:
- kvm->slots_lock is taken outside kvm->irq_lock, though acquiring
them together is quite rare.
-- Unlike kvm->slots_lock, kvm->slots_arch_lock is released before
- synchronize_srcu(&kvm->srcu). Therefore kvm->slots_arch_lock
- can be taken inside a kvm->srcu read-side critical section,
- while kvm->slots_lock cannot.
-
- kvm->mn_active_invalidate_count ensures that pairs of
invalidate_range_start() and invalidate_range_end() callbacks
use the same memslots array. kvm->slots_lock and kvm->slots_arch_lock
are taken on the waiting side in install_new_memslots, so MMU notifiers
must not take either kvm->slots_lock or kvm->slots_arch_lock.
+For SRCU:
+
+- ``synchronize_srcu(&kvm->srcu)`` is called inside critical sections
+ for kvm->lock, vcpu->mutex and kvm->slots_lock. These locks _cannot_
+ be taken inside a kvm->srcu read-side critical section; that is, the
+ following is broken::
+
+ srcu_read_lock(&kvm->srcu);
+ mutex_lock(&kvm->slots_lock);
+
+- kvm->slots_arch_lock instead is released before the call to
+ ``synchronize_srcu()``. It _can_ therefore be taken inside a
+ kvm->srcu read-side critical section, for example while processing
+ a vmexit.
+
On x86:
-- vcpu->mutex is taken outside kvm->arch.hyperv.hv_lock
+- vcpu->mutex is taken outside kvm->arch.hyperv.hv_lock and kvm->arch.xen.xen_lock
- kvm->arch.mmu_lock is an rwlock. kvm->arch.tdp_mmu_pages_lock and
kvm->arch.mmu_unsync_pages_lock are taken inside kvm->arch.mmu_lock, and
diff --git a/Documentation/x86/amd-memory-encryption.rst b/Documentation/x86/amd-memory-encryption.rst
index a1940ebe7be50c..934310ce725829 100644
--- a/Documentation/x86/amd-memory-encryption.rst
+++ b/Documentation/x86/amd-memory-encryption.rst
@@ -95,3 +95,39 @@ by supplying mem_encrypt=on on the kernel command line. However, if BIOS does
not enable SME, then Linux will not be able to activate memory encryption, even
if configured to do so by default or the mem_encrypt=on command line parameter
is specified.
+
+Secure Nested Paging (SNP)
+==========================
+
+SEV-SNP introduces new features (SEV_FEATURES[1:63]) which can be enabled
+by the hypervisor for security enhancements. Some of these features need
+guest side implementation to function correctly. The below table lists the
+expected guest behavior with various possible scenarios of guest/hypervisor
+SNP feature support.
+
++-----------------+---------------+---------------+------------------+
+| Feature Enabled | Guest needs | Guest has | Guest boot |
+| by the HV | implementation| implementation| behaviour |
++=================+===============+===============+==================+
+| No | No | No | Boot |
+| | | | |
++-----------------+---------------+---------------+------------------+
+| No | Yes | No | Boot |
+| | | | |
++-----------------+---------------+---------------+------------------+
+| No | Yes | Yes | Boot |
+| | | | |
++-----------------+---------------+---------------+------------------+
+| Yes | No | No | Boot with |
+| | | | feature enabled |
++-----------------+---------------+---------------+------------------+
+| Yes | Yes | No | Graceful boot |
+| | | | failure |
++-----------------+---------------+---------------+------------------+
+| Yes | Yes | Yes | Boot with |
+| | | | feature enabled |
++-----------------+---------------+---------------+------------------+
+
+More details in AMD64 APM[1] Vol 2: 15.34.10 SEV_STATUS MSR
+
+[1] https://www.amd.com/system/files/TechDocs/40332.pdf
diff --git a/MAINTAINERS b/MAINTAINERS
index f61eb221415bd4..970adbcefc2f5b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -383,7 +383,7 @@ ACPI COMPONENT ARCHITECTURE (ACPICA)
M: Robert Moore <robert.moore@intel.com>
M: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
L: linux-acpi@vger.kernel.org
-L: devel@acpica.org
+L: acpica-devel@lists.linuxfoundation.org
S: Supported
W: https://acpica.org/
W: https://github.com/acpica/acpica/
@@ -1097,14 +1097,12 @@ S: Maintained
F: drivers/dma/ptdma/
AMD SEATTLE DEVICE TREE SUPPORT
-M: Brijesh Singh <brijeshkumar.singh@amd.com>
M: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
M: Tom Lendacky <thomas.lendacky@amd.com>
S: Supported
F: arch/arm64/boot/dts/amd/
AMD XGBE DRIVER
-M: Tom Lendacky <thomas.lendacky@amd.com>
M: "Shyam Sundar S K" <Shyam-sundar.S-k@amd.com>
L: netdev@vger.kernel.org
S: Supported
@@ -2213,6 +2211,9 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux.git
X: drivers/media/i2c/
+F: arch/arm64/boot/dts/freescale/
+X: arch/arm64/boot/dts/freescale/fsl-*
+X: arch/arm64/boot/dts/freescale/qoriq-*
N: imx
N: mxs
@@ -2451,11 +2452,14 @@ F: drivers/rtc/rtc-mt7622.c
ARM/Mediatek SoC support
M: Matthias Brugger <matthias.bgg@gmail.com>
+R: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+L: linux-kernel@vger.kernel.org
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: linux-mediatek@lists.infradead.org (moderated for non-subscribers)
S: Maintained
W: https://mtk.wiki.kernel.org/
-C: irc://chat.freenode.net/linux-mediatek
+C: irc://irc.libera.chat/linux-mediatek
+F: arch/arm/boot/dts/mt2*
F: arch/arm/boot/dts/mt6*
F: arch/arm/boot/dts/mt7*
F: arch/arm/boot/dts/mt8*
@@ -2463,7 +2467,7 @@ F: arch/arm/mach-mediatek/
F: arch/arm64/boot/dts/mediatek/
F: drivers/soc/mediatek/
N: mtk
-N: mt[678]
+N: mt[2678]
K: mediatek
ARM/Mediatek USB3 PHY DRIVER
@@ -3767,7 +3771,6 @@ F: net/bluetooth/
BONDING DRIVER
M: Jay Vosburgh <j.vosburgh@gmail.com>
-M: Veaceslav Falico <vfalico@gmail.com>
M: Andy Gospodarek <andy@greyhouse.net>
L: netdev@vger.kernel.org
S: Supported
@@ -6948,7 +6951,7 @@ F: drivers/gpu/drm/atmel-hlcdc/
DRM DRIVERS FOR BRIDGE CHIPS
M: Andrzej Hajda <andrzej.hajda@intel.com>
M: Neil Armstrong <neil.armstrong@linaro.org>
-M: Robert Foss <robert.foss@linaro.org>
+M: Robert Foss <rfoss@kernel.org>
R: Laurent Pinchart <Laurent.pinchart@ideasonboard.com>
R: Jonas Karlman <jonas@kwiboo.se>
R: Jernej Skrabec <jernej.skrabec@gmail.com>
@@ -7616,7 +7619,6 @@ S: Maintained
F: drivers/firmware/efi/test/
EFI VARIABLE FILESYSTEM
-M: Matthew Garrett <matthew.garrett@nebula.com>
M: Jeremy Kerr <jk@ozlabs.org>
M: Ard Biesheuvel <ardb@kernel.org>
L: linux-efi@vger.kernel.org
@@ -7895,7 +7897,11 @@ F: include/linux/extcon/
EXTRA BOOT CONFIG
M: Masami Hiramatsu <mhiramat@kernel.org>
+L: linux-kernel@vger.kernel.org
+L: linux-trace-kernel@vger.kernel.org
+Q: https://patchwork.kernel.org/project/linux-trace-kernel/list/
S: Maintained
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace.git
F: Documentation/admin-guide/bootconfig.rst
F: fs/proc/bootconfig.c
F: include/linux/bootconfig.h
@@ -8468,16 +8474,16 @@ F: fs/fscache/
F: include/linux/fscache*.h
FSCRYPT: FILE SYSTEM LEVEL ENCRYPTION SUPPORT
+M: Eric Biggers <ebiggers@kernel.org>
M: Theodore Y. Ts'o <tytso@mit.edu>
M: Jaegeuk Kim <jaegeuk@kernel.org>
-M: Eric Biggers <ebiggers@kernel.org>
L: linux-fscrypt@vger.kernel.org
S: Supported
Q: https://patchwork.kernel.org/project/linux-fscrypt/list/
-T: git git://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git
+T: git https://git.kernel.org/pub/scm/fs/fscrypt/linux.git
F: Documentation/filesystems/fscrypt.rst
F: fs/crypto/
-F: include/linux/fscrypt*.h
+F: include/linux/fscrypt.h
F: include/uapi/linux/fscrypt.h
FSI SUBSYSTEM
@@ -8520,10 +8526,10 @@ F: include/linux/fsnotify*.h
FSVERITY: READ-ONLY FILE-BASED AUTHENTICITY PROTECTION
M: Eric Biggers <ebiggers@kernel.org>
M: Theodore Y. Ts'o <tytso@mit.edu>
-L: linux-fscrypt@vger.kernel.org
+L: fsverity@lists.linux.dev
S: Supported
-Q: https://patchwork.kernel.org/project/linux-fscrypt/list/
-T: git git://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git fsverity
+Q: https://patchwork.kernel.org/project/fsverity/list/
+T: git https://git.kernel.org/pub/scm/fs/fsverity/linux.git
F: Documentation/filesystems/fsverity.rst
F: fs/verity/
F: include/linux/fsverity.h
@@ -8571,6 +8577,7 @@ F: kernel/trace/fgraph.c
F: arch/*/*/*/*ftrace*
F: arch/*/*/*ftrace*
F: include/*/ftrace.h
+F: samples/ftrace
FUNGIBLE ETHERNET DRIVERS
M: Dimitris Michailidis <dmichail@fungible.com>
@@ -9299,7 +9306,7 @@ F: net/dsa/tag_hellcreek.c
HISILICON DMA DRIVER
M: Zhou Wang <wangzhou1@hisilicon.com>
-M: Jie Hai <haijie1@hisilicon.com>
+M: Jie Hai <haijie1@huawei.com>
L: dmaengine@vger.kernel.org
S: Maintained
F: drivers/dma/hisi_dma.c
@@ -10159,6 +10166,13 @@ L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/pressure/dps310.c
+INFINEON PEB2466 ASoC CODEC
+M: Herve Codina <herve.codina@bootlin.com>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/sound/infineon,peb2466.yaml
+F: sound/soc/codecs/peb2466.c
+
INFINIBAND SUBSYSTEM
M: Jason Gunthorpe <jgg@nvidia.com>
M: Leon Romanovsky <leonro@nvidia.com>
@@ -10920,6 +10934,13 @@ M: David Sterba <dsterba@suse.com>
S: Odd Fixes
F: drivers/tty/ipwireless/
+IRON DEVICE AUDIO CODEC DRIVERS
+M: Kiseok Jo <kiseok.jo@irondevice.com>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/sound/irondevice,*
+F: sound/soc/codecs/sma*
+
IRQ DOMAINS (IRQ NUMBER MAPPING LIBRARY)
M: Marc Zyngier <maz@kernel.org>
S: Maintained
@@ -11356,9 +11377,9 @@ F: virt/kvm/*
KERNEL VIRTUAL MACHINE FOR ARM64 (KVM/arm64)
M: Marc Zyngier <maz@kernel.org>
R: James Morse <james.morse@arm.com>
-R: Alexandru Elisei <alexandru.elisei@arm.com>
R: Suzuki K Poulose <suzuki.poulose@arm.com>
R: Oliver Upton <oliver.upton@linux.dev>
+R: Zenghui Yu <yuzenghui@huawei.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: kvmarm@lists.linux.dev
L: kvmarm@lists.cs.columbia.edu (deprecated, moderated for non-subscribers)
@@ -11468,7 +11489,7 @@ F: arch/x86/kvm/hyperv.*
F: arch/x86/kvm/kvm_onhyperv.*
F: arch/x86/kvm/svm/hyperv.*
F: arch/x86/kvm/svm/svm_onhyperv.*
-F: arch/x86/kvm/vmx/evmcs.*
+F: arch/x86/kvm/vmx/hyperv.*
KVM X86 Xen (KVM/Xen)
M: David Woodhouse <dwmw2@infradead.org>
@@ -13620,7 +13641,7 @@ F: arch/microblaze/
MICROCHIP AT91 DMA DRIVERS
M: Ludovic Desroches <ludovic.desroches@microchip.com>
-M: Tudor Ambarus <tudor.ambarus@microchip.com>
+M: Tudor Ambarus <tudor.ambarus@linaro.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
L: dmaengine@vger.kernel.org
S: Supported
@@ -13665,7 +13686,7 @@ F: Documentation/devicetree/bindings/media/microchip,csi2dc.yaml
F: drivers/media/platform/microchip/microchip-csi2dc.c
MICROCHIP ECC DRIVER
-M: Tudor Ambarus <tudor.ambarus@microchip.com>
+M: Tudor Ambarus <tudor.ambarus@linaro.org>
L: linux-crypto@vger.kernel.org
S: Maintained
F: drivers/crypto/atmel-ecc.*
@@ -13762,7 +13783,7 @@ S: Maintained
F: drivers/mmc/host/atmel-mci.c
MICROCHIP NAND DRIVER
-M: Tudor Ambarus <tudor.ambarus@microchip.com>
+M: Tudor Ambarus <tudor.ambarus@linaro.org>
L: linux-mtd@lists.infradead.org
S: Supported
F: Documentation/devicetree/bindings/mtd/atmel-nand.txt
@@ -13814,7 +13835,7 @@ S: Supported
F: drivers/power/reset/at91-sama5d2_shdwc.c
MICROCHIP SPI DRIVER
-M: Tudor Ambarus <tudor.ambarus@microchip.com>
+M: Tudor Ambarus <tudor.ambarus@linaro.org>
S: Supported
F: drivers/spi/spi-atmel.*
@@ -14601,7 +14622,6 @@ F: tools/testing/selftests/net/ipsec.c
NETWORKING [IPv4/IPv6]
M: "David S. Miller" <davem@davemloft.net>
-M: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
M: David Ahern <dsahern@kernel.org>
L: netdev@vger.kernel.org
S: Maintained
@@ -14634,7 +14654,6 @@ F: net/netfilter/xt_SECMARK.c
F: net/netlabel/
NETWORKING [MPTCP]
-M: Mat Martineau <mathew.j.martineau@linux.intel.com>
M: Matthieu Baerts <matthieu.baerts@tessares.net>
L: netdev@vger.kernel.org
L: mptcp@lists.linux.dev
@@ -14916,9 +14935,11 @@ L: linux-nvme@lists.infradead.org
S: Supported
W: http://git.infradead.org/nvme.git
T: git://git.infradead.org/nvme.git
+F: Documentation/nvme/
F: drivers/nvme/host/
F: drivers/nvme/common/
-F: include/linux/nvme*
+F: include/linux/nvme.h
+F: include/linux/nvme-*.h
F: include/uapi/linux/nvme_ioctl.h
NVM EXPRESS FABRICS AUTHENTICATION
@@ -15657,7 +15678,7 @@ OPENRISC ARCHITECTURE
M: Jonas Bonn <jonas@southpole.se>
M: Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
M: Stafford Horne <shorne@gmail.com>
-L: openrisc@lists.librecores.org
+L: linux-openrisc@vger.kernel.org
S: Maintained
W: http://openrisc.io
T: git https://github.com/openrisc/linux.git
@@ -15748,6 +15769,12 @@ S: Maintained
W: https://wireless.wiki.kernel.org/en/users/Drivers/p54
F: drivers/net/wireless/intersil/p54/
+PACKET SOCKETS
+M: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
+S: Maintained
+F: include/uapi/linux/if_packet.h
+F: net/packet/af_packet.c
+
PACKING
M: Vladimir Oltean <olteanv@gmail.com>
L: netdev@vger.kernel.org
@@ -16107,7 +16134,7 @@ F: drivers/pci/controller/pci-v3-semi.c
PCI ENDPOINT SUBSYSTEM
M: Lorenzo Pieralisi <lpieralisi@kernel.org>
-R: Krzysztof Wilczyński <kw@linux.com>
+M: Krzysztof Wilczyński <kw@linux.com>
R: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
R: Kishon Vijay Abraham I <kishon@kernel.org>
L: linux-pci@vger.kernel.org
@@ -16115,7 +16142,7 @@ S: Supported
Q: https://patchwork.kernel.org/project/linux-pci/list/
B: https://bugzilla.kernel.org
C: irc://irc.oftc.net/linux-pci
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/pci.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git
F: Documentation/PCI/endpoint/*
F: Documentation/misc-devices/pci-endpoint-test.rst
F: drivers/misc/pci_endpoint_test.c
@@ -16150,7 +16177,7 @@ S: Supported
Q: https://patchwork.kernel.org/project/linux-pci/list/
B: https://bugzilla.kernel.org
C: irc://irc.oftc.net/linux-pci
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git
F: Documentation/driver-api/pci/p2pdma.rst
F: drivers/pci/p2pdma.c
F: include/linux/pci-p2pdma.h
@@ -16172,14 +16199,14 @@ F: drivers/pci/controller/pci-xgene-msi.c
PCI NATIVE HOST BRIDGE AND ENDPOINT DRIVERS
M: Lorenzo Pieralisi <lpieralisi@kernel.org>
+M: Krzysztof Wilczyński <kw@linux.com>
R: Rob Herring <robh@kernel.org>
-R: Krzysztof Wilczyński <kw@linux.com>
L: linux-pci@vger.kernel.org
S: Supported
Q: https://patchwork.kernel.org/project/linux-pci/list/
B: https://bugzilla.kernel.org
C: irc://irc.oftc.net/linux-pci
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/pci.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git
F: Documentation/devicetree/bindings/pci/
F: drivers/pci/controller/
F: drivers/pci/pci-bridge-emul.c
@@ -16192,7 +16219,7 @@ S: Supported
Q: https://patchwork.kernel.org/project/linux-pci/list/
B: https://bugzilla.kernel.org
C: irc://irc.oftc.net/linux-pci
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git
F: Documentation/PCI/
F: Documentation/devicetree/bindings/pci/
F: arch/x86/kernel/early-quirks.c
@@ -16609,6 +16636,13 @@ S: Supported
F: Documentation/devicetree/bindings/input/pine64,pinephone-keyboard.yaml
F: drivers/input/keyboard/pinephone-keyboard.c
+PKTCDVD DRIVER
+M: linux-block@vger.kernel.org
+S: Orphan
+F: drivers/block/pktcdvd.c
+F: include/linux/pktcdvd.h
+F: include/uapi/linux/pktcdvd.h
+
PLANTOWER PMS7003 AIR POLLUTION SENSOR DRIVER
M: Tomasz Duszynski <tduszyns@gmail.com>
S: Maintained
@@ -17229,7 +17263,7 @@ F: Documentation/devicetree/bindings/net/qcom,bam-dmux.yaml
F: drivers/net/wwan/qcom_bam_dmux.c
QUALCOMM CAMERA SUBSYSTEM DRIVER
-M: Robert Foss <robert.foss@linaro.org>
+M: Robert Foss <rfoss@kernel.org>
M: Todor Tomov <todor.too@gmail.com>
L: linux-media@vger.kernel.org
S: Maintained
@@ -17309,7 +17343,7 @@ F: drivers/dma/qcom/hidma*
QUALCOMM I2C CCI DRIVER
M: Loic Poulain <loic.poulain@linaro.org>
-M: Robert Foss <robert.foss@linaro.org>
+M: Robert Foss <rfoss@kernel.org>
L: linux-i2c@vger.kernel.org
L: linux-arm-msm@vger.kernel.org
S: Maintained
@@ -17783,6 +17817,13 @@ F: Documentation/devicetree/bindings/net/renesas,*.yaml
F: drivers/net/ethernet/renesas/
F: include/linux/sh_eth.h
+RENESAS IDT821034 ASoC CODEC
+M: Herve Codina <herve.codina@bootlin.com>
+L: alsa-devel@alsa-project.org (moderated for non-subscribers)
+S: Maintained
+F: Documentation/devicetree/bindings/sound/renesas,idt821034.yaml
+F: sound/soc/codecs/idt821034.c
+
RENESAS R-CAR GYROADC DRIVER
M: Marek Vasut <marek.vasut@gmail.com>
L: linux-iio@vger.kernel.org
@@ -17948,6 +17989,7 @@ M: Albert Ou <aou@eecs.berkeley.edu>
L: linux-riscv@lists.infradead.org
S: Supported
Q: https://patchwork.kernel.org/project/linux-riscv/list/
+C: irc://irc.libera.chat/riscv
P: Documentation/riscv/patch-acceptance.rst
T: git git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux.git
F: arch/riscv/
@@ -18669,9 +18711,9 @@ F: drivers/target/
F: include/target/
SCTP PROTOCOL
-M: Vlad Yasevich <vyasevich@gmail.com>
M: Neil Horman <nhorman@tuxdriver.com>
M: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
+M: Xin Long <lucien.xin@gmail.com>
L: linux-sctp@vger.kernel.org
S: Maintained
W: http://lksctp.sourceforge.net
@@ -19317,6 +19359,13 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers)
S: Orphan
F: sound/soc/uniphier/
+SOCKET TIMESTAMPING
+M: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
+S: Maintained
+F: Documentation/networking/timestamping.rst
+F: include/uapi/linux/net_tstamp.h
+F: tools/testing/selftests/net/so_txtime.c
+
SOEKRIS NET48XX LED SUPPORT
M: Chris Boot <bootc@bootc.net>
S: Maintained
@@ -19664,7 +19713,7 @@ F: drivers/clk/spear/
F: drivers/pinctrl/spear/
SPI NOR SUBSYSTEM
-M: Tudor Ambarus <tudor.ambarus@microchip.com>
+M: Tudor Ambarus <tudor.ambarus@linaro.org>
M: Pratyush Yadav <pratyush@kernel.org>
R: Michael Walle <michael@walle.cc>
L: linux-mtd@lists.infradead.org
@@ -20062,6 +20111,7 @@ F: drivers/watchdog/sunplus_wdt.c
SUPERH
M: Yoshinori Sato <ysato@users.sourceforge.jp>
M: Rich Felker <dalias@libc.org>
+M: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
L: linux-sh@vger.kernel.org
S: Maintained
Q: http://patchwork.kernel.org/project/linux-sh/list/
@@ -21702,6 +21752,7 @@ F: include/uapi/linux/uvcvideo.h
USB WEBCAM GADGET
M: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+M: Daniel Scally <dan.scally@ideasonboard.com>
L: linux-usb@vger.kernel.org
S: Maintained
F: drivers/usb/gadget/function/*uvc*
@@ -21737,6 +21788,13 @@ T: git git://linuxtv.org/media_tree.git
F: Documentation/admin-guide/media/zr364xx*
F: drivers/staging/media/deprecated/zr364xx/
+USER DATAGRAM PROTOCOL (UDP)
+M: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
+S: Maintained
+F: include/linux/udp.h
+F: net/ipv4/udp.c
+F: net/ipv6/udp.c
+
USER-MODE LINUX (UML)
M: Richard Weinberger <richard@nod.at>
M: Anton Ivanov <anton.ivanov@cambridgegreys.com>
@@ -22245,7 +22303,9 @@ F: drivers/scsi/vmw_pvscsi.c
F: drivers/scsi/vmw_pvscsi.h
VMWARE VIRTUAL PTP CLOCK DRIVER
-M: Vivek Thampi <vithampi@vmware.com>
+M: Srivatsa S. Bhat (VMware) <srivatsa@csail.mit.edu>
+M: Deep Shah <sdeep@vmware.com>
+R: Alexey Makhalov <amakhalov@vmware.com>
R: VMware PV-Drivers Reviewers <pv-drivers@vmware.com>
L: netdev@vger.kernel.org
S: Supported
diff --git a/Makefile b/Makefile
index d4b6af8c09e9c1..716a975730a68e 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
VERSION = 6
PATCHLEVEL = 2
SUBLEVEL = 0
-EXTRAVERSION = -rc1
+EXTRAVERSION = -rc8
NAME = Hurr durr I'ma ninja sloth
# *DOCUMENTATION*
@@ -297,7 +297,7 @@ no-compiler-targets := $(no-dot-config-targets) install dtbs_install \
headers_install modules_install kernelrelease image_name
no-sync-config-targets := $(no-dot-config-targets) %install kernelrelease \
image_name
-single-targets := %.a %.i %.rsi %.ko %.lds %.ll %.lst %.mod %.o %.s %.symtypes %/
+single-targets := %.a %.i %.ko %.lds %.ll %.lst %.mod %.o %.rsi %.s %.symtypes %/
config-build :=
mixed-build :=
@@ -549,7 +549,7 @@ LDFLAGS_MODULE =
CFLAGS_KERNEL =
RUSTFLAGS_KERNEL =
AFLAGS_KERNEL =
-export LDFLAGS_vmlinux =
+LDFLAGS_vmlinux =
# Use USERINCLUDE when you must reference the UAPI directories only.
USERINCLUDE := \
@@ -1248,6 +1248,18 @@ vmlinux.o modules.builtin.modinfo modules.builtin: vmlinux_o
@:
PHONY += vmlinux
+# LDFLAGS_vmlinux in the top Makefile defines linker flags for the top vmlinux,
+# not for decompressors. LDFLAGS_vmlinux in arch/*/boot/compressed/Makefile is
+# unrelated; the decompressors just happen to have the same base name,
+# arch/*/boot/compressed/vmlinux.
+# Export LDFLAGS_vmlinux only to scripts/Makefile.vmlinux.
+#
+# _LDFLAGS_vmlinux is a workaround for the 'private export' bug:
+# https://savannah.gnu.org/bugs/?61463
+# For Make > 4.4, the following simple code will work:
+# vmlinux: private export LDFLAGS_vmlinux := $(LDFLAGS_vmlinux)
+vmlinux: private _LDFLAGS_vmlinux := $(LDFLAGS_vmlinux)
+vmlinux: export LDFLAGS_vmlinux = $(_LDFLAGS_vmlinux)
vmlinux: vmlinux.o $(KBUILD_LDS) modpost
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.vmlinux
@@ -1533,6 +1545,7 @@ endif
# *.ko are usually independent of vmlinux, but CONFIG_DEBUG_INFOBTF_MODULES
# is an exception.
ifdef CONFIG_DEBUG_INFO_BTF_MODULES
+KBUILD_BUILTIN := 1
modules: vmlinux
endif
@@ -1986,7 +1999,7 @@ $(single-no-ko): $(build-dir)
# Remove MODORDER when done because it is not the real one.
PHONY += single_modules
single_modules: $(single-no-ko) modules_prepare
- $(Q){ $(foreach m, $(single-ko), echo $(extmod_prefix)$m;) } > $(MODORDER)
+ $(Q){ $(foreach m, $(single-ko), echo $(extmod_prefix)$(m:%.ko=%.o);) } > $(MODORDER)
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
ifneq ($(KBUILD_MODPOST_NOFINAL),1)
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modfinal
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 4067f516914424..955b0362cdfb7a 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -132,7 +132,7 @@ AFLAGS_NOWARN :=$(call as-option,-Wa$(comma)-mno-warn-deprecated,-Wa$(comma)-W)
ifeq ($(CONFIG_THUMB2_KERNEL),y)
CFLAGS_ISA :=-Wa,-mimplicit-it=always $(AFLAGS_NOWARN)
-AFLAGS_ISA :=$(CFLAGS_ISA) -Wa$(comma)-mthumb -D__thumb2__=2
+AFLAGS_ISA :=$(CFLAGS_ISA) -Wa$(comma)-mthumb
CFLAGS_ISA +=-mthumb
else
CFLAGS_ISA :=$(call cc-option,-marm,) $(AFLAGS_NOWARN)
diff --git a/arch/arm/boot/dts/armada-38x.dtsi b/arch/arm/boot/dts/armada-38x.dtsi
index 12933eff419ff8..446861b6b17b2a 100644
--- a/arch/arm/boot/dts/armada-38x.dtsi
+++ b/arch/arm/boot/dts/armada-38x.dtsi
@@ -304,7 +304,7 @@
};
gpio0: gpio@18100 {
- compatible = "marvell,armadaxp-gpio",
+ compatible = "marvell,armada-370-gpio",
"marvell,orion-gpio";
reg = <0x18100 0x40>, <0x181c0 0x08>;
reg-names = "gpio", "pwm";
@@ -323,7 +323,7 @@
};
gpio1: gpio@18140 {
- compatible = "marvell,armadaxp-gpio",
+ compatible = "marvell,armada-370-gpio",
"marvell,orion-gpio";
reg = <0x18140 0x40>, <0x181c8 0x08>;
reg-names = "gpio", "pwm";
diff --git a/arch/arm/boot/dts/armada-39x.dtsi b/arch/arm/boot/dts/armada-39x.dtsi
index 1e05208d9f3415..9d1cac49c022f0 100644
--- a/arch/arm/boot/dts/armada-39x.dtsi
+++ b/arch/arm/boot/dts/armada-39x.dtsi
@@ -213,7 +213,7 @@
};
gpio0: gpio@18100 {
- compatible = "marvell,armadaxp-gpio", "marvell,orion-gpio";
+ compatible = "marvell,orion-gpio";
reg = <0x18100 0x40>;
ngpios = <32>;
gpio-controller;
@@ -227,7 +227,7 @@
};
gpio1: gpio@18140 {
- compatible = "marvell,armadaxp-gpio", "marvell,orion-gpio";
+ compatible = "marvell,orion-gpio";
reg = <0x18140 0x40>;
ngpios = <28>;
gpio-controller;
diff --git a/arch/arm/boot/dts/aspeed-bmc-ibm-bonnell.dts b/arch/arm/boot/dts/aspeed-bmc-ibm-bonnell.dts
index d1971ddf06a5d4..7f755e5a4624df 100644
--- a/arch/arm/boot/dts/aspeed-bmc-ibm-bonnell.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-ibm-bonnell.dts
@@ -751,7 +751,7 @@
};
pca9849@75 {
- compatible = "nxp,pca849";
+ compatible = "nxp,pca9849";
reg = <0x75>;
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/boot/dts/imx53-ppd.dts b/arch/arm/boot/dts/imx53-ppd.dts
index 37d0cffea99c56..70c4a4852256c4 100644
--- a/arch/arm/boot/dts/imx53-ppd.dts
+++ b/arch/arm/boot/dts/imx53-ppd.dts
@@ -488,7 +488,7 @@
scl-gpios = <&gpio3 21 GPIO_ACTIVE_HIGH>;
status = "okay";
- i2c-switch@70 {
+ i2c-mux@70 {
compatible = "nxp,pca9547";
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/boot/dts/imx6qdl-gw560x.dtsi b/arch/arm/boot/dts/imx6qdl-gw560x.dtsi
index 4bc4371e6bae56..4b81a975c979de 100644
--- a/arch/arm/boot/dts/imx6qdl-gw560x.dtsi
+++ b/arch/arm/boot/dts/imx6qdl-gw560x.dtsi
@@ -632,7 +632,6 @@
&uart1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1>;
- uart-has-rtscts;
rts-gpios = <&gpio7 1 GPIO_ACTIVE_HIGH>;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx6ul-pico-dwarf.dts b/arch/arm/boot/dts/imx6ul-pico-dwarf.dts
index 162dc259edc8c5..5a74c7f68eb62d 100644
--- a/arch/arm/boot/dts/imx6ul-pico-dwarf.dts
+++ b/arch/arm/boot/dts/imx6ul-pico-dwarf.dts
@@ -32,7 +32,7 @@
};
&i2c2 {
- clock_frequency = <100000>;
+ clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
diff --git a/arch/arm/boot/dts/imx7d-pico-dwarf.dts b/arch/arm/boot/dts/imx7d-pico-dwarf.dts
index 5162fe227d1ea3..fdc10563f1473e 100644
--- a/arch/arm/boot/dts/imx7d-pico-dwarf.dts
+++ b/arch/arm/boot/dts/imx7d-pico-dwarf.dts
@@ -32,7 +32,7 @@
};
&i2c1 {
- clock_frequency = <100000>;
+ clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
@@ -52,7 +52,7 @@
};
&i2c4 {
- clock_frequency = <100000>;
+ clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
diff --git a/arch/arm/boot/dts/imx7d-pico-nymph.dts b/arch/arm/boot/dts/imx7d-pico-nymph.dts
index 104a85254adbbd..5afb1674e01251 100644
--- a/arch/arm/boot/dts/imx7d-pico-nymph.dts
+++ b/arch/arm/boot/dts/imx7d-pico-nymph.dts
@@ -43,7 +43,7 @@
};
&i2c1 {
- clock_frequency = <100000>;
+ clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
@@ -64,7 +64,7 @@
};
&i2c2 {
- clock_frequency = <100000>;
+ clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
diff --git a/arch/arm/boot/dts/imx7d-smegw01.dts b/arch/arm/boot/dts/imx7d-smegw01.dts
index 546268b8d0b13b..c0f00f5db11e15 100644
--- a/arch/arm/boot/dts/imx7d-smegw01.dts
+++ b/arch/arm/boot/dts/imx7d-smegw01.dts
@@ -198,6 +198,7 @@
&usbotg2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_usbotg2>;
+ over-current-active-low;
dr_mode = "host";
status = "okay";
};
@@ -374,7 +375,7 @@
pinctrl_usbotg2: usbotg2grp {
fsl,pins = <
- MX7D_PAD_UART3_RTS_B__USB_OTG2_OC 0x04
+ MX7D_PAD_UART3_RTS_B__USB_OTG2_OC 0x5c
>;
};
diff --git a/arch/arm/boot/dts/nuvoton-wpcm450.dtsi b/arch/arm/boot/dts/nuvoton-wpcm450.dtsi
index b637241316bb43..fd671c7a1e5d64 100644
--- a/arch/arm/boot/dts/nuvoton-wpcm450.dtsi
+++ b/arch/arm/boot/dts/nuvoton-wpcm450.dtsi
@@ -480,6 +480,7 @@
reg = <0xc8000000 0x1000>, <0xc0000000 0x4000000>;
reg-names = "control", "memory";
clocks = <&clk 0>;
+ nuvoton,shm = <&shm>;
status = "disabled";
};
diff --git a/arch/arm/boot/dts/qcom-apq8084-ifc6540.dts b/arch/arm/boot/dts/qcom-apq8084-ifc6540.dts
index 44cd72f1b1be47..116e59a3b76d01 100644
--- a/arch/arm/boot/dts/qcom-apq8084-ifc6540.dts
+++ b/arch/arm/boot/dts/qcom-apq8084-ifc6540.dts
@@ -19,16 +19,16 @@
serial@f995e000 {
status = "okay";
};
+ };
+};
- sdhci@f9824900 {
- bus-width = <8>;
- non-removable;
- status = "okay";
- };
+&sdhc_1 {
+ bus-width = <8>;
+ non-removable;
+ status = "okay";
+};
- sdhci@f98a4900 {
- cd-gpios = <&tlmm 122 GPIO_ACTIVE_LOW>;
- bus-width = <4>;
- };
- };
+&sdhc_2 {
+ cd-gpios = <&tlmm 122 GPIO_ACTIVE_LOW>;
+ bus-width = <4>;
};
diff --git a/arch/arm/boot/dts/qcom-apq8084.dtsi b/arch/arm/boot/dts/qcom-apq8084.dtsi
index fe30abfff90a77..4b0d2b4f4b6a90 100644
--- a/arch/arm/boot/dts/qcom-apq8084.dtsi
+++ b/arch/arm/boot/dts/qcom-apq8084.dtsi
@@ -421,7 +421,7 @@
status = "disabled";
};
- mmc@f9824900 {
+ sdhc_1: mmc@f9824900 {
compatible = "qcom,apq8084-sdhci", "qcom,sdhci-msm-v4";
reg = <0xf9824900 0x11c>, <0xf9824000 0x800>;
reg-names = "hc", "core";
@@ -434,7 +434,7 @@
status = "disabled";
};
- mmc@f98a4900 {
+ sdhc_2: mmc@f98a4900 {
compatible = "qcom,apq8084-sdhci", "qcom,sdhci-msm-v4";
reg = <0xf98a4900 0x11c>, <0xf98a4000 0x800>;
reg-names = "hc", "core";
diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index 487b0e03d4b438..2ca76b69add78d 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -1181,6 +1181,7 @@
clock-names = "dp", "pclk";
phys = <&edp_phy>;
phy-names = "dp";
+ power-domains = <&power RK3288_PD_VIO>;
resets = <&cru SRST_EDP>;
reset-names = "dp";
rockchip,grf = <&grf>;
diff --git a/arch/arm/boot/dts/sam9x60.dtsi b/arch/arm/boot/dts/sam9x60.dtsi
index 8f5477e307dd49..37a5d96aaf6421 100644
--- a/arch/arm/boot/dts/sam9x60.dtsi
+++ b/arch/arm/boot/dts/sam9x60.dtsi
@@ -564,7 +564,7 @@
mpddrc: mpddrc@ffffe800 {
compatible = "microchip,sam9x60-ddramc", "atmel,sama5d3-ddramc";
reg = <0xffffe800 0x200>;
- clocks = <&pmc PMC_TYPE_SYSTEM 2>, <&pmc PMC_TYPE_CORE PMC_MCK>;
+ clocks = <&pmc PMC_TYPE_SYSTEM 2>, <&pmc PMC_TYPE_PERIPHERAL 49>;
clock-names = "ddrck", "mpddr";
};
diff --git a/arch/arm/boot/dts/stihxxx-b2120.dtsi b/arch/arm/boot/dts/stihxxx-b2120.dtsi
index 920a0bad74945e..8d9a2dfa76f1fe 100644
--- a/arch/arm/boot/dts/stihxxx-b2120.dtsi
+++ b/arch/arm/boot/dts/stihxxx-b2120.dtsi
@@ -178,7 +178,7 @@
tsin-num = <0>;
serial-not-parallel;
i2c-bus = <&ssc2>;
- reset-gpios = <&pio15 4 GPIO_ACTIVE_HIGH>;
+ reset-gpios = <&pio15 4 GPIO_ACTIVE_LOW>;
dvb-card = <STV0367_TDA18212_NIMA_1>;
};
};
diff --git a/arch/arm/boot/dts/stm32mp151a-prtt1l.dtsi b/arch/arm/boot/dts/stm32mp151a-prtt1l.dtsi
index d865ab5d866b93..dd23de85100c48 100644
--- a/arch/arm/boot/dts/stm32mp151a-prtt1l.dtsi
+++ b/arch/arm/boot/dts/stm32mp151a-prtt1l.dtsi
@@ -101,8 +101,12 @@
&qspi {
pinctrl-names = "default", "sleep";
- pinctrl-0 = <&qspi_clk_pins_a &qspi_bk1_pins_a>;
- pinctrl-1 = <&qspi_clk_sleep_pins_a &qspi_bk1_sleep_pins_a>;
+ pinctrl-0 = <&qspi_clk_pins_a
+ &qspi_bk1_pins_a
+ &qspi_cs1_pins_a>;
+ pinctrl-1 = <&qspi_clk_sleep_pins_a
+ &qspi_bk1_sleep_pins_a
+ &qspi_cs1_sleep_pins_a>;
reg = <0x58003000 0x1000>, <0x70000000 0x4000000>;
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/boot/dts/stm32mp157c-emstamp-argon.dtsi b/arch/arm/boot/dts/stm32mp157c-emstamp-argon.dtsi
index aef02e6421a36d..7d11c50b9e4087 100644
--- a/arch/arm/boot/dts/stm32mp157c-emstamp-argon.dtsi
+++ b/arch/arm/boot/dts/stm32mp157c-emstamp-argon.dtsi
@@ -391,8 +391,12 @@
&qspi {
pinctrl-names = "default", "sleep";
- pinctrl-0 = <&qspi_clk_pins_a &qspi_bk1_pins_a>;
- pinctrl-1 = <&qspi_clk_sleep_pins_a &qspi_bk1_sleep_pins_a>;
+ pinctrl-0 = <&qspi_clk_pins_a
+ &qspi_bk1_pins_a
+ &qspi_cs1_pins_a>;
+ pinctrl-1 = <&qspi_clk_sleep_pins_a
+ &qspi_bk1_sleep_pins_a
+ &qspi_cs1_sleep_pins_a>;
reg = <0x58003000 0x1000>, <0x70000000 0x4000000>;
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/boot/dts/stm32mp15xx-dhcom-som.dtsi b/arch/arm/boot/dts/stm32mp15xx-dhcom-som.dtsi
index 002f221f1694c7..c06edd2eacb0cd 100644
--- a/arch/arm/boot/dts/stm32mp15xx-dhcom-som.dtsi
+++ b/arch/arm/boot/dts/stm32mp15xx-dhcom-som.dtsi
@@ -428,8 +428,12 @@
&qspi {
pinctrl-names = "default", "sleep";
- pinctrl-0 = <&qspi_clk_pins_a &qspi_bk1_pins_a>;
- pinctrl-1 = <&qspi_clk_sleep_pins_a &qspi_bk1_sleep_pins_a>;
+ pinctrl-0 = <&qspi_clk_pins_a
+ &qspi_bk1_pins_a
+ &qspi_cs1_pins_a>;
+ pinctrl-1 = <&qspi_clk_sleep_pins_a
+ &qspi_bk1_sleep_pins_a
+ &qspi_cs1_sleep_pins_a>;
reg = <0x58003000 0x1000>, <0x70000000 0x4000000>;
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/boot/dts/stm32mp15xx-dhcor-som.dtsi b/arch/arm/boot/dts/stm32mp15xx-dhcor-som.dtsi
index 134a798ad3f23f..bb40fb46da81d5 100644
--- a/arch/arm/boot/dts/stm32mp15xx-dhcor-som.dtsi
+++ b/arch/arm/boot/dts/stm32mp15xx-dhcor-som.dtsi
@@ -247,8 +247,12 @@
&qspi {
pinctrl-names = "default", "sleep";
- pinctrl-0 = <&qspi_clk_pins_a &qspi_bk1_pins_a>;
- pinctrl-1 = <&qspi_clk_sleep_pins_a &qspi_bk1_sleep_pins_a>;
+ pinctrl-0 = <&qspi_clk_pins_a
+ &qspi_bk1_pins_a
+ &qspi_cs1_pins_a>;
+ pinctrl-1 = <&qspi_clk_sleep_pins_a
+ &qspi_bk1_sleep_pins_a
+ &qspi_cs1_sleep_pins_a>;
reg = <0x58003000 0x1000>, <0x70000000 0x200000>;
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm/boot/dts/vf610-zii-dev-rev-b.dts b/arch/arm/boot/dts/vf610-zii-dev-rev-b.dts
index 42ed4a04a12e25..6280c5e86a124e 100644
--- a/arch/arm/boot/dts/vf610-zii-dev-rev-b.dts
+++ b/arch/arm/boot/dts/vf610-zii-dev-rev-b.dts
@@ -345,7 +345,7 @@
};
&i2c2 {
- tca9548@70 {
+ i2c-mux@70 {
compatible = "nxp,pca9548";
pinctrl-0 = <&pinctrl_i2c_mux_reset>;
pinctrl-names = "default";
diff --git a/arch/arm/boot/dts/vf610-zii-dev-rev-c.dts b/arch/arm/boot/dts/vf610-zii-dev-rev-c.dts
index f892977da9e4e0..c00d39562a10bb 100644
--- a/arch/arm/boot/dts/vf610-zii-dev-rev-c.dts
+++ b/arch/arm/boot/dts/vf610-zii-dev-rev-c.dts
@@ -340,7 +340,7 @@
};
&i2c2 {
- tca9548@70 {
+ i2c-mux@70 {
compatible = "nxp,pca9548";
pinctrl-0 = <&pinctrl_i2c_mux_reset>;
pinctrl-names = "default";
diff --git a/arch/arm/crypto/Makefile b/arch/arm/crypto/Makefile
index 971e74546fb1bf..13e62c7c25dca8 100644
--- a/arch/arm/crypto/Makefile
+++ b/arch/arm/crypto/Makefile
@@ -53,7 +53,12 @@ $(obj)/%-core.S: $(src)/%-armv4.pl
clean-files += poly1305-core.S sha256-core.S sha512-core.S
+aflags-thumb2-$(CONFIG_THUMB2_KERNEL) := -U__thumb2__ -D__thumb2__=1
+
+AFLAGS_sha256-core.o += $(aflags-thumb2-y)
+AFLAGS_sha512-core.o += $(aflags-thumb2-y)
+
# massage the perlasm code a bit so we only get the NEON routine if we need it
poly1305-aflags-$(CONFIG_CPU_V7) := -U__LINUX_ARM_ARCH__ -D__LINUX_ARM_ARCH__=5
poly1305-aflags-$(CONFIG_KERNEL_MODE_NEON) := -U__LINUX_ARM_ARCH__ -D__LINUX_ARM_ARCH__=7
-AFLAGS_poly1305-core.o += $(poly1305-aflags-y)
+AFLAGS_poly1305-core.o += $(poly1305-aflags-y) $(aflags-thumb2-y)
diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h
index aecc403b288049..7f092cb55a4171 100644
--- a/arch/arm/include/asm/thread_info.h
+++ b/arch/arm/include/asm/thread_info.h
@@ -128,15 +128,16 @@ extern int vfp_restore_user_hwstate(struct user_vfp *,
#define TIF_NEED_RESCHED 1 /* rescheduling necessary */
#define TIF_NOTIFY_RESUME 2 /* callback before returning to user */
#define TIF_UPROBE 3 /* breakpointed or singlestepping */
-#define TIF_SYSCALL_TRACE 4 /* syscall trace active */
-#define TIF_SYSCALL_AUDIT 5 /* syscall auditing active */
-#define TIF_SYSCALL_TRACEPOINT 6 /* syscall tracepoint instrumentation */
-#define TIF_SECCOMP 7 /* seccomp syscall filtering active */
-#define TIF_NOTIFY_SIGNAL 8 /* signal notifications exist */
+#define TIF_NOTIFY_SIGNAL 4 /* signal notifications exist */
#define TIF_USING_IWMMXT 17
#define TIF_MEMDIE 18 /* is terminating due to OOM killer */
-#define TIF_RESTORE_SIGMASK 20
+#define TIF_RESTORE_SIGMASK 19
+#define TIF_SYSCALL_TRACE 20 /* syscall trace active */
+#define TIF_SYSCALL_AUDIT 21 /* syscall auditing active */
+#define TIF_SYSCALL_TRACEPOINT 22 /* syscall tracepoint instrumentation */
+#define TIF_SECCOMP 23 /* seccomp syscall filtering active */
+
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
diff --git a/arch/arm/mach-footbridge/isa-rtc.c b/arch/arm/mach-footbridge/isa-rtc.c
index b8f741a3a37ea0..237b828dd2f10a 100644
--- a/arch/arm/mach-footbridge/isa-rtc.c
+++ b/arch/arm/mach-footbridge/isa-rtc.c
@@ -20,7 +20,6 @@
#include <linux/init.h>
#include <linux/mc146818rtc.h>
-#include <linux/bcd.h>
#include <linux/io.h>
#include "common.h"
diff --git a/arch/arm/mach-imx/cpu-imx25.c b/arch/arm/mach-imx/cpu-imx25.c
index 3e63445cde062e..cc86977d0a3407 100644
--- a/arch/arm/mach-imx/cpu-imx25.c
+++ b/arch/arm/mach-imx/cpu-imx25.c
@@ -23,6 +23,7 @@ static int mx25_read_cpu_rev(void)
np = of_find_compatible_node(NULL, NULL, "fsl,imx25-iim");
iim_base = of_iomap(np, 0);
+ of_node_put(np);
BUG_ON(!iim_base);
rev = readl(iim_base + MXC_IIMSREV);
iounmap(iim_base);
diff --git a/arch/arm/mach-imx/cpu-imx27.c b/arch/arm/mach-imx/cpu-imx27.c
index bf70e13bbe9ee0..1d289390836838 100644
--- a/arch/arm/mach-imx/cpu-imx27.c
+++ b/arch/arm/mach-imx/cpu-imx27.c
@@ -28,6 +28,7 @@ static int mx27_read_cpu_rev(void)
np = of_find_compatible_node(NULL, NULL, "fsl,imx27-ccm");
ccm_base = of_iomap(np, 0);
+ of_node_put(np);
BUG_ON(!ccm_base);
/*
* now we have access to the IO registers. As we need
diff --git a/arch/arm/mach-imx/cpu-imx31.c b/arch/arm/mach-imx/cpu-imx31.c
index b9c24b851d1abd..35c544924e5095 100644
--- a/arch/arm/mach-imx/cpu-imx31.c
+++ b/arch/arm/mach-imx/cpu-imx31.c
@@ -39,6 +39,7 @@ static int mx31_read_cpu_rev(void)
np = of_find_compatible_node(NULL, NULL, "fsl,imx31-iim");
iim_base = of_iomap(np, 0);
+ of_node_put(np);
BUG_ON(!iim_base);
/* read SREV register from IIM module */
diff --git a/arch/arm/mach-imx/cpu-imx35.c b/arch/arm/mach-imx/cpu-imx35.c
index 80e7d8ab9f1bb8..1fe75b39c2d995 100644
--- a/arch/arm/mach-imx/cpu-imx35.c
+++ b/arch/arm/mach-imx/cpu-imx35.c
@@ -21,6 +21,7 @@ static int mx35_read_cpu_rev(void)
np = of_find_compatible_node(NULL, NULL, "fsl,imx35-iim");
iim_base = of_iomap(np, 0);
+ of_node_put(np);
BUG_ON(!iim_base);
rev = imx_readl(iim_base + MXC_IIMSREV);
diff --git a/arch/arm/mach-imx/cpu-imx5.c b/arch/arm/mach-imx/cpu-imx5.c
index ad56263778f936..a67c89bf155dd3 100644
--- a/arch/arm/mach-imx/cpu-imx5.c
+++ b/arch/arm/mach-imx/cpu-imx5.c
@@ -28,6 +28,7 @@ static u32 imx5_read_srev_reg(const char *compat)
np = of_find_compatible_node(NULL, NULL, compat);
iim_base = of_iomap(np, 0);
+ of_node_put(np);
WARN_ON(!iim_base);
srev = readl(iim_base + IIM_SREV) & 0xff;
diff --git a/arch/arm/mach-omap1/Kconfig b/arch/arm/mach-omap1/Kconfig
index 538a960257cc70..7ec7ada287e050 100644
--- a/arch/arm/mach-omap1/Kconfig
+++ b/arch/arm/mach-omap1/Kconfig
@@ -4,6 +4,7 @@ menuconfig ARCH_OMAP1
depends on ARCH_MULTI_V4T || ARCH_MULTI_V5
depends on CPU_LITTLE_ENDIAN
depends on ATAGS
+ select ARCH_OMAP
select ARCH_HAS_HOLES_MEMORYMODEL
select ARCH_OMAP
select CLKSRC_MMIO
@@ -45,10 +46,6 @@ config ARCH_OMAP16XX
select CPU_ARM926T
select OMAP_DM_TIMER
-config ARCH_OMAP1_ANY
- select ARCH_OMAP
- def_bool ARCH_OMAP730 || ARCH_OMAP850 || ARCH_OMAP15XX || ARCH_OMAP16XX
-
config ARCH_OMAP
bool
diff --git a/arch/arm/mach-omap1/Makefile b/arch/arm/mach-omap1/Makefile
index 506074b86333fd..0615cb0ba580b0 100644
--- a/arch/arm/mach-omap1/Makefile
+++ b/arch/arm/mach-omap1/Makefile
@@ -3,8 +3,6 @@
# Makefile for the linux kernel.
#
-ifdef CONFIG_ARCH_OMAP1_ANY
-
# Common support
obj-y := io.o id.o sram-init.o sram.o time.o irq.o mux.o flash.o \
serial.o devices.o dma.o omap-dma.o fb.o
@@ -59,5 +57,3 @@ obj-$(CONFIG_ARCH_OMAP730) += gpio7xx.o
obj-$(CONFIG_ARCH_OMAP850) += gpio7xx.o
obj-$(CONFIG_ARCH_OMAP15XX) += gpio15xx.o
obj-$(CONFIG_ARCH_OMAP16XX) += gpio16xx.o
-
-endif
diff --git a/arch/arm/mach-omap1/gpio15xx.c b/arch/arm/mach-omap1/gpio15xx.c
index c675f11de99dbd..61fa26efd86534 100644
--- a/arch/arm/mach-omap1/gpio15xx.c
+++ b/arch/arm/mach-omap1/gpio15xx.c
@@ -11,6 +11,7 @@
#include <linux/gpio.h>
#include <linux/platform_data/gpio-omap.h>
#include <linux/soc/ti/omap1-soc.h>
+#include <asm/irq.h>
#include "irqs.h"
diff --git a/arch/arm/mach-omap1/io.c b/arch/arm/mach-omap1/io.c
index d2db9b8aed3fb0..0074b011a05a46 100644
--- a/arch/arm/mach-omap1/io.c
+++ b/arch/arm/mach-omap1/io.c
@@ -22,17 +22,14 @@
* The machine specific code may provide the extra mapping besides the
* default mapping provided here.
*/
-static struct map_desc omap_io_desc[] __initdata = {
+#if defined (CONFIG_ARCH_OMAP730) || defined (CONFIG_ARCH_OMAP850)
+static struct map_desc omap7xx_io_desc[] __initdata = {
{
.virtual = OMAP1_IO_VIRT,
.pfn = __phys_to_pfn(OMAP1_IO_PHYS),
.length = OMAP1_IO_SIZE,
.type = MT_DEVICE
- }
-};
-
-#if defined (CONFIG_ARCH_OMAP730) || defined (CONFIG_ARCH_OMAP850)
-static struct map_desc omap7xx_io_desc[] __initdata = {
+ },
{
.virtual = OMAP7XX_DSP_BASE,
.pfn = __phys_to_pfn(OMAP7XX_DSP_START),
@@ -50,6 +47,12 @@ static struct map_desc omap7xx_io_desc[] __initdata = {
#ifdef CONFIG_ARCH_OMAP15XX
static struct map_desc omap1510_io_desc[] __initdata = {
{
+ .virtual = OMAP1_IO_VIRT,
+ .pfn = __phys_to_pfn(OMAP1_IO_PHYS),
+ .length = OMAP1_IO_SIZE,
+ .type = MT_DEVICE
+ },
+ {
.virtual = OMAP1510_DSP_BASE,
.pfn = __phys_to_pfn(OMAP1510_DSP_START),
.length = OMAP1510_DSP_SIZE,
@@ -66,6 +69,12 @@ static struct map_desc omap1510_io_desc[] __initdata = {
#if defined(CONFIG_ARCH_OMAP16XX)
static struct map_desc omap16xx_io_desc[] __initdata = {
{
+ .virtual = OMAP1_IO_VIRT,
+ .pfn = __phys_to_pfn(OMAP1_IO_PHYS),
+ .length = OMAP1_IO_SIZE,
+ .type = MT_DEVICE
+ },
+ {
.virtual = OMAP16XX_DSP_BASE,
.pfn = __phys_to_pfn(OMAP16XX_DSP_START),
.length = OMAP16XX_DSP_SIZE,
@@ -79,18 +88,9 @@ static struct map_desc omap16xx_io_desc[] __initdata = {
};
#endif
-/*
- * Maps common IO regions for omap1
- */
-static void __init omap1_map_common_io(void)
-{
- iotable_init(omap_io_desc, ARRAY_SIZE(omap_io_desc));
-}
-
#if defined (CONFIG_ARCH_OMAP730) || defined (CONFIG_ARCH_OMAP850)
void __init omap7xx_map_io(void)
{
- omap1_map_common_io();
iotable_init(omap7xx_io_desc, ARRAY_SIZE(omap7xx_io_desc));
}
#endif
@@ -98,7 +98,6 @@ void __init omap7xx_map_io(void)
#ifdef CONFIG_ARCH_OMAP15XX
void __init omap15xx_map_io(void)
{
- omap1_map_common_io();
iotable_init(omap1510_io_desc, ARRAY_SIZE(omap1510_io_desc));
}
#endif
@@ -106,7 +105,6 @@ void __init omap15xx_map_io(void)
#if defined(CONFIG_ARCH_OMAP16XX)
void __init omap16xx_map_io(void)
{
- omap1_map_common_io();
iotable_init(omap16xx_io_desc, ARRAY_SIZE(omap16xx_io_desc));
}
#endif
diff --git a/arch/arm/mach-omap1/mcbsp.c b/arch/arm/mach-omap1/mcbsp.c
index 05c25c432449fd..b1632cbe37e6f9 100644
--- a/arch/arm/mach-omap1/mcbsp.c
+++ b/arch/arm/mach-omap1/mcbsp.c
@@ -89,7 +89,6 @@ static struct omap_mcbsp_ops omap1_mcbsp_ops = {
#define OMAP1610_MCBSP2_BASE 0xfffb1000
#define OMAP1610_MCBSP3_BASE 0xe1017000
-#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
struct resource omap7xx_mcbsp_res[][6] = {
{
{
@@ -159,14 +158,7 @@ static struct omap_mcbsp_platform_data omap7xx_mcbsp_pdata[] = {
};
#define OMAP7XX_MCBSP_RES_SZ ARRAY_SIZE(omap7xx_mcbsp_res[1])
#define OMAP7XX_MCBSP_COUNT ARRAY_SIZE(omap7xx_mcbsp_res)
-#else
-#define omap7xx_mcbsp_res_0 NULL
-#define omap7xx_mcbsp_pdata NULL
-#define OMAP7XX_MCBSP_RES_SZ 0
-#define OMAP7XX_MCBSP_COUNT 0
-#endif
-#ifdef CONFIG_ARCH_OMAP15XX
struct resource omap15xx_mcbsp_res[][6] = {
{
{
@@ -266,14 +258,7 @@ static struct omap_mcbsp_platform_data omap15xx_mcbsp_pdata[] = {
};
#define OMAP15XX_MCBSP_RES_SZ ARRAY_SIZE(omap15xx_mcbsp_res[1])
#define OMAP15XX_MCBSP_COUNT ARRAY_SIZE(omap15xx_mcbsp_res)
-#else
-#define omap15xx_mcbsp_res_0 NULL
-#define omap15xx_mcbsp_pdata NULL
-#define OMAP15XX_MCBSP_RES_SZ 0
-#define OMAP15XX_MCBSP_COUNT 0
-#endif
-#ifdef CONFIG_ARCH_OMAP16XX
struct resource omap16xx_mcbsp_res[][6] = {
{
{
@@ -373,12 +358,6 @@ static struct omap_mcbsp_platform_data omap16xx_mcbsp_pdata[] = {
};
#define OMAP16XX_MCBSP_RES_SZ ARRAY_SIZE(omap16xx_mcbsp_res[1])
#define OMAP16XX_MCBSP_COUNT ARRAY_SIZE(omap16xx_mcbsp_res)
-#else
-#define omap16xx_mcbsp_res_0 NULL
-#define omap16xx_mcbsp_pdata NULL
-#define OMAP16XX_MCBSP_RES_SZ 0
-#define OMAP16XX_MCBSP_COUNT 0
-#endif
static void omap_mcbsp_register_board_cfg(struct resource *res, int res_count,
struct omap_mcbsp_platform_data *config, int size)
diff --git a/arch/arm/mach-omap1/pm.h b/arch/arm/mach-omap1/pm.h
index d9165709c53238..0d1f092821ff8f 100644
--- a/arch/arm/mach-omap1/pm.h
+++ b/arch/arm/mach-omap1/pm.h
@@ -106,13 +106,6 @@
#define OMAP7XX_IDLECT3 0xfffece24
#define OMAP7XX_IDLE_LOOP_REQUEST 0x0C00
-#if !defined(CONFIG_ARCH_OMAP730) && \
- !defined(CONFIG_ARCH_OMAP850) && \
- !defined(CONFIG_ARCH_OMAP15XX) && \
- !defined(CONFIG_ARCH_OMAP16XX)
-#warning "Power management for this processor not implemented yet"
-#endif
-
#ifndef __ASSEMBLER__
#include <linux/clk.h>
diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig
index b90d98bae68d74..03e25af6f48c97 100644
--- a/arch/arm/mach-pxa/Kconfig
+++ b/arch/arm/mach-pxa/Kconfig
@@ -45,6 +45,8 @@ config MACH_PXA27X_DT
config MACH_PXA3XX_DT
bool "Support PXA3xx platforms from device tree"
select CPU_PXA300
+ select CPU_PXA310
+ select CPU_PXA320
select PINCTRL
select POWER_SUPPLY
select PXA3xx
diff --git a/arch/arm/mm/nommu.c b/arch/arm/mm/nommu.c
index c1494a4dee25b5..53f2d8774fdb9a 100644
--- a/arch/arm/mm/nommu.c
+++ b/arch/arm/mm/nommu.c
@@ -161,7 +161,7 @@ void __init paging_init(const struct machine_desc *mdesc)
mpu_setup();
/* allocate the zero page. */
- zero_page = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
+ zero_page = (void *)memblock_alloc(PAGE_SIZE, PAGE_SIZE);
if (!zero_page)
panic("%s: Failed to allocate %lu bytes align=0x%lx\n",
__func__, PAGE_SIZE, PAGE_SIZE);
diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S
index fa6999e24b074a..e43f6d716b4b87 100644
--- a/arch/arm/mm/proc-macros.S
+++ b/arch/arm/mm/proc-macros.S
@@ -6,6 +6,7 @@
* VM_EXEC
*/
#include <asm/asm-offsets.h>
+#include <asm/pgtable.h>
#include <asm/thread_info.h>
#ifdef CONFIG_CPU_V7M
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 03934808b2ed07..c5ccca26a40870 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -184,8 +184,6 @@ config ARM64
select HAVE_DEBUG_KMEMLEAK
select HAVE_DMA_CONTIGUOUS
select HAVE_DYNAMIC_FTRACE
- select HAVE_DYNAMIC_FTRACE_WITH_ARGS \
- if $(cc-option,-fpatchable-function-entry=2)
select FTRACE_MCOUNT_USE_PATCHABLE_FUNCTION_ENTRY \
if DYNAMIC_FTRACE_WITH_ARGS
select HAVE_EFFICIENT_UNALIGNED_ACCESS
@@ -972,6 +970,22 @@ config ARM64_ERRATUM_2457168
If unsure, say Y.
+config ARM64_ERRATUM_2645198
+ bool "Cortex-A715: 2645198: Workaround possible [ESR|FAR]_ELx corruption"
+ default y
+ help
+ This option adds the workaround for ARM Cortex-A715 erratum 2645198.
+
+ If a Cortex-A715 cpu sees a page mapping permissions change from executable
+ to non-executable, it may corrupt the ESR_ELx and FAR_ELx registers on the
+ next instruction abort caused by permission fault.
+
+ Only user-space does executable to non-executable permission transition via
+ mprotect() system call. Workaround the problem by doing a break-before-make
+ TLB invalidation, for all changes to executable user space mappings.
+
+ If unsure, say Y.
+
config CAVIUM_ERRATUM_22375
bool "Cavium erratum 22375, 24313"
default y
diff --git a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi
index 1648e67afbb6bc..417523dc4cc03a 100644
--- a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi
@@ -1886,7 +1886,7 @@
sd_emmc_b: sd@5000 {
compatible = "amlogic,meson-axg-mmc";
reg = <0x0 0x5000 0x0 0x800>;
- interrupts = <GIC_SPI 217 IRQ_TYPE_EDGE_RISING>;
+ interrupts = <GIC_SPI 217 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
clocks = <&clkc CLKID_SD_EMMC_B>,
<&clkc CLKID_SD_EMMC_B_CLK0>,
@@ -1898,7 +1898,7 @@
sd_emmc_c: mmc@7000 {
compatible = "amlogic,meson-axg-mmc";
reg = <0x0 0x7000 0x0 0x800>;
- interrupts = <GIC_SPI 218 IRQ_TYPE_EDGE_RISING>;
+ interrupts = <GIC_SPI 218 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
clocks = <&clkc CLKID_SD_EMMC_C>,
<&clkc CLKID_SD_EMMC_C_CLK0>,
diff --git a/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi b/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi
index 9dbd50820b1ced..7f55d97f6c2830 100644
--- a/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi
@@ -2324,7 +2324,7 @@
sd_emmc_a: sd@ffe03000 {
compatible = "amlogic,meson-axg-mmc";
reg = <0x0 0xffe03000 0x0 0x800>;
- interrupts = <GIC_SPI 189 IRQ_TYPE_EDGE_RISING>;
+ interrupts = <GIC_SPI 189 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
clocks = <&clkc CLKID_SD_EMMC_A>,
<&clkc CLKID_SD_EMMC_A_CLK0>,
@@ -2336,7 +2336,7 @@
sd_emmc_b: sd@ffe05000 {
compatible = "amlogic,meson-axg-mmc";
reg = <0x0 0xffe05000 0x0 0x800>;
- interrupts = <GIC_SPI 190 IRQ_TYPE_EDGE_RISING>;
+ interrupts = <GIC_SPI 190 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
clocks = <&clkc CLKID_SD_EMMC_B>,
<&clkc CLKID_SD_EMMC_B_CLK0>,
@@ -2348,7 +2348,7 @@
sd_emmc_c: mmc@ffe07000 {
compatible = "amlogic,meson-axg-mmc";
reg = <0x0 0xffe07000 0x0 0x800>;
- interrupts = <GIC_SPI 191 IRQ_TYPE_EDGE_RISING>;
+ interrupts = <GIC_SPI 191 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
clocks = <&clkc CLKID_SD_EMMC_C>,
<&clkc CLKID_SD_EMMC_C_CLK0>,
diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
index e3c12e0be99d74..5eed15035b6746 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
@@ -603,21 +603,21 @@
sd_emmc_a: mmc@70000 {
compatible = "amlogic,meson-gx-mmc", "amlogic,meson-gxbb-mmc";
reg = <0x0 0x70000 0x0 0x800>;
- interrupts = <GIC_SPI 216 IRQ_TYPE_EDGE_RISING>;
+ interrupts = <GIC_SPI 216 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
sd_emmc_b: mmc@72000 {
compatible = "amlogic,meson-gx-mmc", "amlogic,meson-gxbb-mmc";
reg = <0x0 0x72000 0x0 0x800>;
- interrupts = <GIC_SPI 217 IRQ_TYPE_EDGE_RISING>;
+ interrupts = <GIC_SPI 217 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
sd_emmc_c: mmc@74000 {
compatible = "amlogic,meson-gx-mmc", "amlogic,meson-gxbb-mmc";
reg = <0x0 0x74000 0x0 0x800>;
- interrupts = <GIC_SPI 218 IRQ_TYPE_EDGE_RISING>;
+ interrupts = <GIC_SPI 218 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
};
diff --git a/arch/arm64/boot/dts/amlogic/meson-sm1-odroid-hc4.dts b/arch/arm64/boot/dts/amlogic/meson-sm1-odroid-hc4.dts
index e3486f60645a41..a1f0c38ccaddad 100644
--- a/arch/arm64/boot/dts/amlogic/meson-sm1-odroid-hc4.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-sm1-odroid-hc4.dts
@@ -131,10 +131,6 @@
};
&usb {
- phys = <&usb2_phy1>;
- phy-names = "usb2-phy1";
-};
-
-&usb2_phy0 {
- status = "disabled";
+ phys = <&usb2_phy0>, <&usb2_phy1>;
+ phy-names = "usb2-phy0", "usb2-phy1";
};
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1012a-qds.dts b/arch/arm64/boot/dts/freescale/fsl-ls1012a-qds.dts
index 5a8d85a7d1612d..bbdf989058ff77 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1012a-qds.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1012a-qds.dts
@@ -110,7 +110,7 @@
&i2c0 {
status = "okay";
- pca9547@77 {
+ i2c-mux@77 {
compatible = "nxp,pca9547";
reg = <0x77>;
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a-qds.dts b/arch/arm64/boot/dts/freescale/fsl-ls1043a-qds.dts
index 9b726c2a484267..dda27ed7aaf2b1 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1043a-qds.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a-qds.dts
@@ -89,7 +89,7 @@
&i2c0 {
status = "okay";
- pca9547@77 {
+ i2c-mux@77 {
compatible = "nxp,pca9547";
reg = <0x77>;
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a-qds.dts b/arch/arm64/boot/dts/freescale/fsl-ls1046a-qds.dts
index b2fcbba60d3acc..3b0ed9305f2bd4 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1046a-qds.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a-qds.dts
@@ -88,7 +88,7 @@
&i2c0 {
status = "okay";
- pca9547@77 {
+ i2c-mux@77 {
compatible = "nxp,pca9547";
reg = <0x77>;
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1088a-qds.dts b/arch/arm64/boot/dts/freescale/fsl-ls1088a-qds.dts
index 41d8b15f25a540..aa52ff73ff9e02 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1088a-qds.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a-qds.dts
@@ -53,7 +53,7 @@
&i2c0 {
status = "okay";
- i2c-switch@77 {
+ i2c-mux@77 {
compatible = "nxp,pca9547";
reg = <0x77>;
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts b/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts
index 1bfbce69cc8b77..ee8e932628d17f 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a-rdb.dts
@@ -136,7 +136,7 @@
&i2c0 {
status = "okay";
- i2c-switch@77 {
+ i2c-mux@77 {
compatible = "nxp,pca9547";
reg = <0x77>;
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1088a-ten64.dts b/arch/arm64/boot/dts/freescale/fsl-ls1088a-ten64.dts
index ef6c8967533efa..d4867d6cf47cdc 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1088a-ten64.dts
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1088a-ten64.dts
@@ -245,7 +245,7 @@
&i2c3 {
status = "okay";
- i2c-switch@70 {
+ i2c-mux@70 {
compatible = "nxp,pca9540";
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls208xa-qds.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls208xa-qds.dtsi
index f598669e742fc9..52c5a43b30a0fb 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls208xa-qds.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls208xa-qds.dtsi
@@ -103,7 +103,7 @@
&i2c0 {
status = "okay";
- pca9547@77 {
+ i2c-mux@77 {
compatible = "nxp,pca9547";
reg = <0x77>;
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls208xa-rdb.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls208xa-rdb.dtsi
index 3d9647b3da1473..537cecb13dd08a 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls208xa-rdb.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls208xa-rdb.dtsi
@@ -44,7 +44,7 @@
&i2c0 {
status = "okay";
- pca9547@75 {
+ i2c-mux@75 {
compatible = "nxp,pca9547";
reg = <0x75>;
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2160a-cex7.dtsi b/arch/arm64/boot/dts/freescale/fsl-lx2160a-cex7.dtsi
index afb455210bd07e..d32a52ab00a42a 100644
--- a/arch/arm64/boot/dts/freescale/fsl-lx2160a-cex7.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-lx2160a-cex7.dtsi
@@ -54,7 +54,7 @@
&i2c0 {
status = "okay";
- i2c-switch@77 {
+ i2c-mux@77 {
compatible = "nxp,pca9547";
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/freescale/imx8dxl.dtsi b/arch/arm64/boot/dts/freescale/imx8dxl.dtsi
index 0c64b919462125..214f21bd0cb49e 100644
--- a/arch/arm64/boot/dts/freescale/imx8dxl.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8dxl.dtsi
@@ -164,7 +164,7 @@
sc_pwrkey: keys {
compatible = "fsl,imx8qxp-sc-key", "fsl,imx-sc-key";
- linux,keycode = <KEY_POWER>;
+ linux,keycodes = <KEY_POWER>;
wakeup-source;
};
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-beacon-baseboard.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-beacon-baseboard.dtsi
index 03266bd90a06ba..169f047fbca50e 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-beacon-baseboard.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mm-beacon-baseboard.dtsi
@@ -120,7 +120,7 @@
&ecspi2 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_espi2>;
- cs-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;
+ cs-gpios = <&gpio5 13 GPIO_ACTIVE_LOW>;
status = "okay";
eeprom@0 {
@@ -316,7 +316,7 @@
MX8MM_IOMUXC_ECSPI2_SCLK_ECSPI2_SCLK 0x82
MX8MM_IOMUXC_ECSPI2_MOSI_ECSPI2_MOSI 0x82
MX8MM_IOMUXC_ECSPI2_MISO_ECSPI2_MISO 0x82
- MX8MM_IOMUXC_ECSPI1_SS0_GPIO5_IO9 0x41
+ MX8MM_IOMUXC_ECSPI2_SS0_GPIO5_IO13 0x41
>;
};
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-data-modul-edm-sbc.dts b/arch/arm64/boot/dts/freescale/imx8mm-data-modul-edm-sbc.dts
index 24f61db33eba2a..9889319d4f0453 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-data-modul-edm-sbc.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mm-data-modul-edm-sbc.dts
@@ -88,6 +88,7 @@
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_watchdog_gpio>;
compatible = "linux,wdt-gpio";
+ always-running;
gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
hw_algo = "level";
/* Reset triggers in 2..3 seconds */
@@ -275,7 +276,7 @@
compatible = "rohm,bd71847";
reg = <0x4b>;
#clock-cells = <0>;
- clocks = <&clk_xtal32k 0>;
+ clocks = <&clk_xtal32k>;
clock-output-names = "clk-32k-out";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pmic>;
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-nitrogen-r2.dts b/arch/arm64/boot/dts/freescale/imx8mm-nitrogen-r2.dts
index 74c09891600f25..6357078185eddb 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-nitrogen-r2.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mm-nitrogen-r2.dts
@@ -214,7 +214,7 @@
pinctrl-0 = <&pinctrl_i2c3>;
status = "okay";
- i2cmux@70 {
+ i2c-mux@70 {
compatible = "nxp,pca9540";
reg = <0x70>;
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h b/arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h
index 83c8f715cd9019..b1f11098d248ed 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h
+++ b/arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h
@@ -602,7 +602,7 @@
#define MX8MM_IOMUXC_UART1_RXD_GPIO5_IO22 0x234 0x49C 0x000 0x5 0x0
#define MX8MM_IOMUXC_UART1_RXD_TPSMP_HDATA24 0x234 0x49C 0x000 0x7 0x0
#define MX8MM_IOMUXC_UART1_TXD_UART1_DCE_TX 0x238 0x4A0 0x000 0x0 0x0
-#define MX8MM_IOMUXC_UART1_TXD_UART1_DTE_RX 0x238 0x4A0 0x4F4 0x0 0x0
+#define MX8MM_IOMUXC_UART1_TXD_UART1_DTE_RX 0x238 0x4A0 0x4F4 0x0 0x1
#define MX8MM_IOMUXC_UART1_TXD_ECSPI3_MOSI 0x238 0x4A0 0x000 0x1 0x0
#define MX8MM_IOMUXC_UART1_TXD_GPIO5_IO23 0x238 0x4A0 0x000 0x5 0x0
#define MX8MM_IOMUXC_UART1_TXD_TPSMP_HDATA25 0x238 0x4A0 0x000 0x7 0x0
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw72xx-0x-rs232-rts.dtso b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw72xx-0x-rs232-rts.dtso
index 3ea73a6886ff40..f6ad1a4b8b6659 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw72xx-0x-rs232-rts.dtso
+++ b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw72xx-0x-rs232-rts.dtso
@@ -33,7 +33,6 @@
pinctrl-0 = <&pinctrl_uart2>;
rts-gpios = <&gpio5 29 GPIO_ACTIVE_LOW>;
cts-gpios = <&gpio5 28 GPIO_ACTIVE_LOW>;
- uart-has-rtscts;
status = "okay";
};
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw73xx-0x-rs232-rts.dtso b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw73xx-0x-rs232-rts.dtso
index 2fa635e1c1a82a..1f8ea20dfafcb9 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw73xx-0x-rs232-rts.dtso
+++ b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw73xx-0x-rs232-rts.dtso
@@ -33,7 +33,6 @@
pinctrl-0 = <&pinctrl_uart2>;
rts-gpios = <&gpio5 29 GPIO_ACTIVE_LOW>;
cts-gpios = <&gpio5 28 GPIO_ACTIVE_LOW>;
- uart-has-rtscts;
status = "okay";
};
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw73xx.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw73xx.dtsi
index 244ef8d6cc688c..7761d5671cb133 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw73xx.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw73xx.dtsi
@@ -222,7 +222,6 @@
pinctrl-0 = <&pinctrl_uart3>, <&pinctrl_bten>;
cts-gpios = <&gpio5 8 GPIO_ACTIVE_LOW>;
rts-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;
- uart-has-rtscts;
status = "okay";
bluetooth {
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7901.dts b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7901.dts
index 750a1f07ecb7a6..64b366e83fa14f 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7901.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7901.dts
@@ -733,7 +733,6 @@
dtr-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>;
dsr-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>;
dcd-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
- uart-has-rtscts;
status = "okay";
};
@@ -749,7 +748,6 @@
pinctrl-0 = <&pinctrl_uart3>, <&pinctrl_uart3_gpio>;
cts-gpios = <&gpio4 10 GPIO_ACTIVE_LOW>;
rts-gpios = <&gpio4 9 GPIO_ACTIVE_LOW>;
- uart-has-rtscts;
status = "okay";
};
@@ -758,7 +756,6 @@
pinctrl-0 = <&pinctrl_uart4>, <&pinctrl_uart4_gpio>;
cts-gpios = <&gpio5 11 GPIO_ACTIVE_LOW>;
rts-gpios = <&gpio5 12 GPIO_ACTIVE_LOW>;
- uart-has-rtscts;
status = "okay";
};
@@ -771,6 +768,7 @@
&usbotg2 {
dr_mode = "host";
vbus-supply = <&reg_usb2_vbus>;
+ over-current-active-low;
status = "okay";
};
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7902.dts b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7902.dts
index 32872b0b1aaf33..e8bc1fccc47be5 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7902.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7902.dts
@@ -664,7 +664,6 @@
pinctrl-0 = <&pinctrl_uart1>, <&pinctrl_uart1_gpio>;
rts-gpios = <&gpio4 10 GPIO_ACTIVE_LOW>;
cts-gpios = <&gpio4 24 GPIO_ACTIVE_LOW>;
- uart-has-rtscts;
status = "okay";
};
@@ -681,7 +680,6 @@
pinctrl-0 = <&pinctrl_uart3>, <&pinctrl_uart3_gpio>;
rts-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>;
cts-gpios = <&gpio2 0 GPIO_ACTIVE_LOW>;
- uart-has-rtscts;
status = "okay";
bluetooth {
@@ -699,7 +697,6 @@
dtr-gpios = <&gpio4 3 GPIO_ACTIVE_LOW>;
dsr-gpios = <&gpio4 4 GPIO_ACTIVE_LOW>;
dcd-gpios = <&gpio4 6 GPIO_ACTIVE_LOW>;
- uart-has-rtscts;
status = "okay";
};
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7903.dts b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7903.dts
index 8ce562246a08ec..acc2ba8e00a880 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7903.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mm-venice-gw7903.dts
@@ -581,7 +581,6 @@
dtr-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>;
dsr-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>;
dcd-gpios = <&gpio3 24 GPIO_ACTIVE_LOW>;
- uart-has-rtscts;
status = "okay";
};
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-verdin-dahlia.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-verdin-dahlia.dtsi
index c2a5c2f7b204b9..7c3f5c54f04006 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-verdin-dahlia.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mm-verdin-dahlia.dtsi
@@ -9,6 +9,7 @@
simple-audio-card,bitclock-master = <&dailink_master>;
simple-audio-card,format = "i2s";
simple-audio-card,frame-master = <&dailink_master>;
+ simple-audio-card,mclk-fs = <256>;
simple-audio-card,name = "imx8mm-wm8904";
simple-audio-card,routing =
"Headphone Jack", "HPOUTL",
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-verdin-dev.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-verdin-dev.dtsi
index 73cc3fafa01808..b2bcd228217020 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-verdin-dev.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mm-verdin-dev.dtsi
@@ -11,6 +11,7 @@
simple-audio-card,bitclock-master = <&dailink_master>;
simple-audio-card,format = "i2s";
simple-audio-card,frame-master = <&dailink_master>;
+ simple-audio-card,mclk-fs = <256>;
simple-audio-card,name = "imx8mm-nau8822";
simple-audio-card,routing =
"Headphones", "LHP",
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-verdin.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-verdin.dtsi
index 0d454e0e2f7c8b..702d87621bb43d 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-verdin.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mm-verdin.dtsi
@@ -98,6 +98,7 @@
off-on-delay = <500000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_reg_eth>;
+ regulator-always-on;
regulator-boot-on;
regulator-max-microvolt = <3300000>;
regulator-min-microvolt = <3300000>;
diff --git a/arch/arm64/boot/dts/freescale/imx8mn-venice-gw7902.dts b/arch/arm64/boot/dts/freescale/imx8mn-venice-gw7902.dts
index b9444e4a3d2d6f..7c12518dbc9637 100644
--- a/arch/arm64/boot/dts/freescale/imx8mn-venice-gw7902.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mn-venice-gw7902.dts
@@ -643,7 +643,6 @@
pinctrl-0 = <&pinctrl_uart3>, <&pinctrl_uart3_gpio>;
rts-gpios = <&gpio2 1 GPIO_ACTIVE_LOW>;
cts-gpios = <&gpio2 0 GPIO_ACTIVE_LOW>;
- uart-has-rtscts;
status = "okay";
bluetooth {
diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk.dts
index d4c7ca16abd057..f2d93437084be8 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp-evk.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mp-evk.dts
@@ -36,8 +36,8 @@
pcie0_refclk: pcie0-refclk {
compatible = "fixed-clock";
- #clock-cells = <0>;
- clock-frequency = <100000000>;
+ #clock-cells = <0>;
+ clock-frequency = <100000000>;
};
reg_can1_stby: regulator-can1-stby {
diff --git a/arch/arm64/boot/dts/freescale/imx8mp-phycore-som.dtsi b/arch/arm64/boot/dts/freescale/imx8mp-phycore-som.dtsi
index 79b290a002c19e..ecc4bce6db97c0 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp-phycore-som.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mp-phycore-som.dtsi
@@ -99,7 +99,6 @@
regulators {
buck1: BUCK1 {
- regulator-compatible = "BUCK1";
regulator-min-microvolt = <600000>;
regulator-max-microvolt = <2187500>;
regulator-boot-on;
@@ -108,7 +107,6 @@
};
buck2: BUCK2 {
- regulator-compatible = "BUCK2";
regulator-min-microvolt = <600000>;
regulator-max-microvolt = <2187500>;
regulator-boot-on;
@@ -119,7 +117,6 @@
};
buck4: BUCK4 {
- regulator-compatible = "BUCK4";
regulator-min-microvolt = <600000>;
regulator-max-microvolt = <3400000>;
regulator-boot-on;
@@ -127,7 +124,6 @@
};
buck5: BUCK5 {
- regulator-compatible = "BUCK5";
regulator-min-microvolt = <600000>;
regulator-max-microvolt = <3400000>;
regulator-boot-on;
@@ -135,7 +131,6 @@
};
buck6: BUCK6 {
- regulator-compatible = "BUCK6";
regulator-min-microvolt = <600000>;
regulator-max-microvolt = <3400000>;
regulator-boot-on;
@@ -143,7 +138,6 @@
};
ldo1: LDO1 {
- regulator-compatible = "LDO1";
regulator-min-microvolt = <1600000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
@@ -151,7 +145,6 @@
};
ldo2: LDO2 {
- regulator-compatible = "LDO2";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <1150000>;
regulator-boot-on;
@@ -159,7 +152,6 @@
};
ldo3: LDO3 {
- regulator-compatible = "LDO3";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
@@ -167,13 +159,11 @@
};
ldo4: LDO4 {
- regulator-compatible = "LDO4";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <3300000>;
};
ldo5: LDO5 {
- regulator-compatible = "LDO5";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
diff --git a/arch/arm64/boot/dts/freescale/imx8mp-venice-gw74xx.dts b/arch/arm64/boot/dts/freescale/imx8mp-venice-gw74xx.dts
index ceeca4966fc5c8..8eb7d5ee38daac 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp-venice-gw74xx.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mp-venice-gw74xx.dts
@@ -623,7 +623,6 @@
pinctrl-0 = <&pinctrl_uart3>, <&pinctrl_uart3_gpio>;
cts-gpios = <&gpio3 21 GPIO_ACTIVE_LOW>;
rts-gpios = <&gpio3 22 GPIO_ACTIVE_LOW>;
- uart-has-rtscts;
status = "okay";
bluetooth {
diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
index 7a6e6221f4219c..03034b439c1f79 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
@@ -524,6 +524,7 @@
compatible = "fsl,imx8mp-gpc";
reg = <0x303a0000 0x1000>;
interrupt-parent = <&gic>;
+ interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
interrupt-controller;
#interrupt-cells = <3>;
@@ -590,7 +591,7 @@
reg = <IMX8MP_POWER_DOMAIN_MIPI_PHY2>;
};
- pgc_hsiomix: power-domains@17 {
+ pgc_hsiomix: power-domain@17 {
#power-domain-cells = <0>;
reg = <IMX8MP_POWER_DOMAIN_HSIOMIX>;
clocks = <&clk IMX8MP_CLK_HSIO_AXI>,
@@ -1297,7 +1298,7 @@
reg = <0x32f10100 0x8>,
<0x381f0000 0x20>;
clocks = <&clk IMX8MP_CLK_HSIO_ROOT>,
- <&clk IMX8MP_CLK_USB_ROOT>;
+ <&clk IMX8MP_CLK_USB_SUSP>;
clock-names = "hsio", "suspend";
interrupts = <GIC_SPI 148 IRQ_TYPE_LEVEL_HIGH>;
power-domains = <&hsio_blk_ctrl IMX8MP_HSIOBLK_PD_USB>;
@@ -1310,9 +1311,9 @@
usb_dwc3_0: usb@38100000 {
compatible = "snps,dwc3";
reg = <0x38100000 0x10000>;
- clocks = <&clk IMX8MP_CLK_HSIO_AXI>,
+ clocks = <&clk IMX8MP_CLK_USB_ROOT>,
<&clk IMX8MP_CLK_USB_CORE_REF>,
- <&clk IMX8MP_CLK_USB_ROOT>;
+ <&clk IMX8MP_CLK_USB_SUSP>;
clock-names = "bus_early", "ref", "suspend";
interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
phys = <&usb3_phy0>, <&usb3_phy0>;
@@ -1339,7 +1340,7 @@
reg = <0x32f10108 0x8>,
<0x382f0000 0x20>;
clocks = <&clk IMX8MP_CLK_HSIO_ROOT>,
- <&clk IMX8MP_CLK_USB_ROOT>;
+ <&clk IMX8MP_CLK_USB_SUSP>;
clock-names = "hsio", "suspend";
interrupts = <GIC_SPI 149 IRQ_TYPE_LEVEL_HIGH>;
power-domains = <&hsio_blk_ctrl IMX8MP_HSIOBLK_PD_USB>;
@@ -1352,9 +1353,9 @@
usb_dwc3_1: usb@38200000 {
compatible = "snps,dwc3";
reg = <0x38200000 0x10000>;
- clocks = <&clk IMX8MP_CLK_HSIO_AXI>,
+ clocks = <&clk IMX8MP_CLK_USB_ROOT>,
<&clk IMX8MP_CLK_USB_CORE_REF>,
- <&clk IMX8MP_CLK_USB_ROOT>;
+ <&clk IMX8MP_CLK_USB_SUSP>;
clock-names = "bus_early", "ref", "suspend";
interrupts = <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
phys = <&usb3_phy1>, <&usb3_phy1>;
diff --git a/arch/arm64/boot/dts/freescale/imx8mq-nitrogen.dts b/arch/arm64/boot/dts/freescale/imx8mq-nitrogen.dts
index 9dda2a1554c328..8614c18b5998c2 100644
--- a/arch/arm64/boot/dts/freescale/imx8mq-nitrogen.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mq-nitrogen.dts
@@ -133,7 +133,7 @@
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
- i2cmux@70 {
+ i2c-mux@70 {
compatible = "nxp,pca9546";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1_pca9546>;
@@ -216,7 +216,7 @@
pinctrl-0 = <&pinctrl_i2c4>;
status = "okay";
- pca9546: i2cmux@70 {
+ pca9546: i2c-mux@70 {
compatible = "nxp,pca9546";
reg = <0x70>;
#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/freescale/imx8mq-thor96.dts b/arch/arm64/boot/dts/freescale/imx8mq-thor96.dts
index 5d5aa6537225fe..6e6182709d2201 100644
--- a/arch/arm64/boot/dts/freescale/imx8mq-thor96.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mq-thor96.dts
@@ -339,7 +339,7 @@
bus-width = <4>;
non-removable;
no-sd;
- no-emmc;
+ no-mmc;
status = "okay";
brcmf: wifi@1 {
@@ -359,7 +359,7 @@
cd-gpios = <&gpio2 12 GPIO_ACTIVE_LOW>;
bus-width = <4>;
no-sdio;
- no-emmc;
+ no-mmc;
disable-wp;
status = "okay";
};
diff --git a/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts b/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts
index 07d8dd8160f681..afa883389456ce 100644
--- a/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts
+++ b/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts
@@ -61,7 +61,7 @@
pinctrl-0 = <&pinctrl_lpi2c1 &pinctrl_ioexp_rst>;
status = "okay";
- i2c-switch@71 {
+ i2c-mux@71 {
compatible = "nxp,pca9646", "nxp,pca9546";
#address-cells = <1>;
#size-cells = <0>;
diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts
index 69786c326db000..27f9a9f3313466 100644
--- a/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts
+++ b/arch/arm64/boot/dts/freescale/imx93-11x11-evk.dts
@@ -74,7 +74,7 @@
pinctrl_usdhc1: usdhc1grp {
fsl,pins = <
- MX93_PAD_SD1_CLK__USDHC1_CLK 0x17fe
+ MX93_PAD_SD1_CLK__USDHC1_CLK 0x15fe
MX93_PAD_SD1_CMD__USDHC1_CMD 0x13fe
MX93_PAD_SD1_DATA0__USDHC1_DATA0 0x13fe
MX93_PAD_SD1_DATA1__USDHC1_DATA1 0x13fe
@@ -84,7 +84,7 @@
MX93_PAD_SD1_DATA5__USDHC1_DATA5 0x13fe
MX93_PAD_SD1_DATA6__USDHC1_DATA6 0x13fe
MX93_PAD_SD1_DATA7__USDHC1_DATA7 0x13fe
- MX93_PAD_SD1_STROBE__USDHC1_STROBE 0x17fe
+ MX93_PAD_SD1_STROBE__USDHC1_STROBE 0x15fe
>;
};
@@ -102,7 +102,7 @@
pinctrl_usdhc2: usdhc2grp {
fsl,pins = <
- MX93_PAD_SD2_CLK__USDHC2_CLK 0x17fe
+ MX93_PAD_SD2_CLK__USDHC2_CLK 0x15fe
MX93_PAD_SD2_CMD__USDHC2_CMD 0x13fe
MX93_PAD_SD2_DATA0__USDHC2_DATA0 0x13fe
MX93_PAD_SD2_DATA1__USDHC2_DATA1 0x13fe
diff --git a/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi b/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi
index 7308f7b6b22c94..8bce6406913875 100644
--- a/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi
+++ b/arch/arm64/boot/dts/marvell/ac5-98dx25xx.dtsi
@@ -98,7 +98,7 @@
uart1: serial@12100 {
compatible = "snps,dw-apb-uart";
- reg = <0x11000 0x100>;
+ reg = <0x12100 0x100>;
reg-shift = <2>;
interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
reg-io-width = <1>;
diff --git a/arch/arm64/boot/dts/mediatek/mt8195.dtsi b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
index 5d31536f4c486e..c10cfeb1214d58 100644
--- a/arch/arm64/boot/dts/mediatek/mt8195.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8195.dtsi
@@ -2146,7 +2146,7 @@
};
vdosys0: syscon@1c01a000 {
- compatible = "mediatek,mt8195-mmsys", "syscon";
+ compatible = "mediatek,mt8195-vdosys0", "mediatek,mt8195-mmsys", "syscon";
reg = <0 0x1c01a000 0 0x1000>;
mboxes = <&gce0 0 CMDQ_THR_PRIO_4>;
#clock-cells = <1>;
@@ -2292,7 +2292,7 @@
};
vdosys1: syscon@1c100000 {
- compatible = "mediatek,mt8195-mmsys", "syscon";
+ compatible = "mediatek,mt8195-vdosys1", "syscon";
reg = <0 0x1c100000 0 0x1000>;
#clock-cells = <1>;
};
diff --git a/arch/arm64/boot/dts/qcom/msm8992-lg-bullhead.dtsi b/arch/arm64/boot/dts/qcom/msm8992-lg-bullhead.dtsi
index 87c90e93667f73..79de9cc395c4cf 100644
--- a/arch/arm64/boot/dts/qcom/msm8992-lg-bullhead.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8992-lg-bullhead.dtsi
@@ -3,6 +3,7 @@
* Copyright (c) 2015, LGE Inc. All rights reserved.
* Copyright (c) 2016, The Linux Foundation. All rights reserved.
* Copyright (c) 2021, Petr Vorel <petr.vorel@gmail.com>
+ * Copyright (c) 2022, Dominik Kobinski <dominikkobinski314@gmail.com>
*/
/dts-v1/;
@@ -51,6 +52,11 @@
reg = <0 0x03400000 0 0x1200000>;
no-map;
};
+
+ removed_region: reserved@5000000 {
+ reg = <0 0x05000000 0 0x2200000>;
+ no-map;
+ };
};
};
diff --git a/arch/arm64/boot/dts/qcom/msm8992-xiaomi-libra.dts b/arch/arm64/boot/dts/qcom/msm8992-xiaomi-libra.dts
index b242c272d2af14..fcca1ba94da699 100644
--- a/arch/arm64/boot/dts/qcom/msm8992-xiaomi-libra.dts
+++ b/arch/arm64/boot/dts/qcom/msm8992-xiaomi-libra.dts
@@ -11,6 +11,12 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/gpio-keys.h>
+/delete-node/ &adsp_mem;
+/delete-node/ &audio_mem;
+/delete-node/ &mpss_mem;
+/delete-node/ &peripheral_region;
+/delete-node/ &rmtfs_mem;
+
/ {
model = "Xiaomi Mi 4C";
compatible = "xiaomi,libra", "qcom,msm8992";
@@ -70,25 +76,67 @@
#size-cells = <2>;
ranges;
- /* This is for getting crash logs using Android downstream kernels */
- ramoops@dfc00000 {
- compatible = "ramoops";
- reg = <0x0 0xdfc00000 0x0 0x40000>;
- console-size = <0x10000>;
- record-size = <0x10000>;
- ftrace-size = <0x10000>;
- pmsg-size = <0x20000>;
+ memory_hole: hole@6400000 {
+ reg = <0 0x06400000 0 0x600000>;
+ no-map;
+ };
+
+ memory_hole2: hole2@6c00000 {
+ reg = <0 0x06c00000 0 0x2400000>;
+ no-map;
+ };
+
+ mpss_mem: mpss@9000000 {
+ reg = <0 0x09000000 0 0x5a00000>;
+ no-map;
+ };
+
+ tzapp: tzapp@ea00000 {
+ reg = <0 0x0ea00000 0 0x1900000>;
+ no-map;
+ };
+
+ mdm_rfsa_mem: mdm-rfsa@ca0b0000 {
+ reg = <0 0xca0b0000 0 0x10000>;
+ no-map;
+ };
+
+ rmtfs_mem: rmtfs@ca100000 {
+ compatible = "qcom,rmtfs-mem";
+ reg = <0 0xca100000 0 0x180000>;
+ no-map;
+
+ qcom,client-id = <1>;
};
- modem_region: modem_region@9000000 {
- reg = <0x0 0x9000000 0x0 0x5a00000>;
+ audio_mem: audio@cb400000 {
+ reg = <0 0xcb000000 0 0x400000>;
+ no-mem;
+ };
+
+ qseecom_mem: qseecom@cb400000 {
+ reg = <0 0xcb400000 0 0x1c00000>;
+ no-mem;
+ };
+
+ adsp_rfsa_mem: adsp-rfsa@cd000000 {
+ reg = <0 0xcd000000 0 0x10000>;
no-map;
};
- tzapp: modem_region@ea00000 {
- reg = <0x0 0xea00000 0x0 0x1900000>;
+ sensor_rfsa_mem: sensor-rfsa@cd010000 {
+ reg = <0 0xcd010000 0 0x10000>;
no-map;
};
+
+ ramoops@dfc00000 {
+ compatible = "ramoops";
+ reg = <0 0xdfc00000 0 0x40000>;
+ console-size = <0x10000>;
+ record-size = <0x10000>;
+ ftrace-size = <0x10000>;
+ pmsg-size = <0x20000>;
+ };
};
};
@@ -130,11 +178,6 @@
status = "okay";
};
-&peripheral_region {
- reg = <0x0 0x7400000 0x0 0x1c00000>;
- no-map;
-};
-
&pm8994_spmi_regulators {
VDD_APC0: s8 {
regulator-min-microvolt = <680000>;
diff --git a/arch/arm64/boot/dts/qcom/msm8992.dtsi b/arch/arm64/boot/dts/qcom/msm8992.dtsi
index 10adb4986ef1b9..02fc3795dbfd73 100644
--- a/arch/arm64/boot/dts/qcom/msm8992.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8992.dtsi
@@ -37,10 +37,6 @@
compatible = "qcom,rpmcc-msm8992", "qcom,rpmcc";
};
-&tcsr_mutex {
- compatible = "qcom,sfpb-mutex";
-};
-
&timer {
interrupts = <GIC_PPI 2 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
<GIC_PPI 3 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
diff --git a/arch/arm64/boot/dts/qcom/msm8994-huawei-angler-rev-101.dts b/arch/arm64/boot/dts/qcom/msm8994-huawei-angler-rev-101.dts
index 85abff0e9b3f7c..7b0f62144c3eea 100644
--- a/arch/arm64/boot/dts/qcom/msm8994-huawei-angler-rev-101.dts
+++ b/arch/arm64/boot/dts/qcom/msm8994-huawei-angler-rev-101.dts
@@ -9,9 +9,6 @@
#include "msm8994.dtsi"
-/* Angler's firmware does not report where the memory is allocated */
-/delete-node/ &cont_splash_mem;
-
/ {
model = "Huawei Nexus 6P";
compatible = "huawei,angler", "qcom,msm8994";
@@ -28,6 +25,22 @@
chosen {
stdout-path = "serial0:115200n8";
};
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ tzapp_mem: tzapp@4800000 {
+ reg = <0 0x04800000 0 0x1900000>;
+ no-map;
+ };
+
+ removed_region: reserved@6300000 {
+ reg = <0 0x06300000 0 0xD00000>;
+ no-map;
+ };
+ };
};
&blsp1_uart2 {
diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi
index 109c9d2b684d11..71cf81a8eb4da6 100644
--- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi
@@ -10,6 +10,7 @@
#include <dt-bindings/interconnect/qcom,sc8280xp.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/mailbox/qcom-ipcc.h>
+#include <dt-bindings/phy/phy-qcom-qmp.h>
#include <dt-bindings/power/qcom-rpmpd.h>
#include <dt-bindings/soc/qcom,rpmh-rsc.h>
#include <dt-bindings/thermal/thermal.h>
@@ -762,7 +763,7 @@
<0>,
<0>,
<0>,
- <&usb_0_ssphy>,
+ <&usb_0_qmpphy QMP_USB43DP_USB3_PIPE_CLK>,
<0>,
<0>,
<0>,
@@ -770,7 +771,7 @@
<0>,
<0>,
<0>,
- <&usb_1_ssphy>,
+ <&usb_1_qmpphy QMP_USB43DP_USB3_PIPE_CLK>,
<0>,
<0>,
<0>,
@@ -1673,42 +1674,26 @@
};
};
- usb_0_qmpphy: phy-wrapper@88ec000 {
+ usb_0_qmpphy: phy@88eb000 {
compatible = "qcom,sc8280xp-qmp-usb43dp-phy";
- reg = <0 0x088ec000 0 0x1e4>,
- <0 0x088eb000 0 0x40>,
- <0 0x088ed000 0 0x1c8>;
- #address-cells = <2>;
- #size-cells = <2>;
- ranges;
+ reg = <0 0x088eb000 0 0x4000>;
clocks = <&gcc GCC_USB3_PRIM_PHY_AUX_CLK>,
- <&rpmhcc RPMH_CXO_CLK>,
<&gcc GCC_USB4_EUD_CLKREF_CLK>,
- <&gcc GCC_USB3_PRIM_PHY_COM_AUX_CLK>;
- clock-names = "aux", "ref_clk_src", "ref", "com_aux";
+ <&gcc GCC_USB3_PRIM_PHY_COM_AUX_CLK>,
+ <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>;
+ clock-names = "aux", "ref", "com_aux", "usb3_pipe";
+
+ power-domains = <&gcc USB30_PRIM_GDSC>;
resets = <&gcc GCC_USB3_PHY_PRIM_BCR>,
- <&gcc GCC_USB3_DP_PHY_PRIM_BCR>;
+ <&gcc GCC_USB4_DP_PHY_PRIM_BCR>;
reset-names = "phy", "common";
- power-domains = <&gcc USB30_PRIM_GDSC>;
+ #clock-cells = <1>;
+ #phy-cells = <1>;
status = "disabled";
-
- usb_0_ssphy: usb3-phy@88eb400 {
- reg = <0 0x088eb400 0 0x100>,
- <0 0x088eb600 0 0x3ec>,
- <0 0x088ec400 0 0x364>,
- <0 0x088eba00 0 0x100>,
- <0 0x088ebc00 0 0x3ec>,
- <0 0x088ec200 0 0x18>;
- #phy-cells = <0>;
- #clock-cells = <0>;
- clocks = <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>;
- clock-names = "pipe0";
- clock-output-names = "usb0_phy_pipe_clk_src";
- };
};
usb_1_hsphy: phy@8902000 {
@@ -1725,42 +1710,26 @@
status = "disabled";
};
- usb_1_qmpphy: phy-wrapper@8904000 {
+ usb_1_qmpphy: phy@8903000 {
compatible = "qcom,sc8280xp-qmp-usb43dp-phy";
- reg = <0 0x08904000 0 0x1e4>,
- <0 0x08903000 0 0x40>,
- <0 0x08905000 0 0x1c8>;
- #address-cells = <2>;
- #size-cells = <2>;
- ranges;
+ reg = <0 0x08903000 0 0x4000>;
clocks = <&gcc GCC_USB3_SEC_PHY_AUX_CLK>,
- <&rpmhcc RPMH_CXO_CLK>,
<&gcc GCC_USB4_CLKREF_CLK>,
- <&gcc GCC_USB3_SEC_PHY_COM_AUX_CLK>;
- clock-names = "aux", "ref_clk_src", "ref", "com_aux";
+ <&gcc GCC_USB3_SEC_PHY_COM_AUX_CLK>,
+ <&gcc GCC_USB3_SEC_PHY_PIPE_CLK>;
+ clock-names = "aux", "ref", "com_aux", "usb3_pipe";
+
+ power-domains = <&gcc USB30_SEC_GDSC>;
resets = <&gcc GCC_USB3_PHY_SEC_BCR>,
<&gcc GCC_USB4_1_DP_PHY_PRIM_BCR>;
reset-names = "phy", "common";
- power-domains = <&gcc USB30_SEC_GDSC>;
+ #clock-cells = <1>;
+ #phy-cells = <1>;
status = "disabled";
-
- usb_1_ssphy: usb3-phy@8903400 {
- reg = <0 0x08903400 0 0x100>,
- <0 0x08903600 0 0x3ec>,
- <0 0x08904400 0 0x364>,
- <0 0x08903a00 0 0x100>,
- <0 0x08903c00 0 0x3ec>,
- <0 0x08904200 0 0x18>;
- #phy-cells = <0>;
- #clock-cells = <0>;
- clocks = <&gcc GCC_USB3_SEC_PHY_PIPE_CLK>;
- clock-names = "pipe0";
- clock-output-names = "usb1_phy_pipe_clk_src";
- };
};
pmu@9091000 {
@@ -1910,7 +1879,7 @@
reg = <0 0x0a600000 0 0xcd00>;
interrupts = <GIC_SPI 803 IRQ_TYPE_LEVEL_HIGH>;
iommus = <&apps_smmu 0x820 0x0>;
- phys = <&usb_0_hsphy>, <&usb_0_ssphy>;
+ phys = <&usb_0_hsphy>, <&usb_0_qmpphy QMP_USB43DP_USB3_PHY>;
phy-names = "usb2-phy", "usb3-phy";
};
};
@@ -1964,7 +1933,7 @@
reg = <0 0x0a800000 0 0xcd00>;
interrupts = <GIC_SPI 810 IRQ_TYPE_LEVEL_HIGH>;
iommus = <&apps_smmu 0x860 0x0>;
- phys = <&usb_1_hsphy>, <&usb_1_ssphy>;
+ phys = <&usb_1_hsphy>, <&usb_1_qmpphy QMP_USB43DP_USB3_PHY>;
phy-names = "usb2-phy", "usb3-phy";
};
};
diff --git a/arch/arm64/boot/dts/qcom/sm8250.dtsi b/arch/arm64/boot/dts/qcom/sm8250.dtsi
index dab5579946f351..927032863e2f15 100644
--- a/arch/arm64/boot/dts/qcom/sm8250.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8250.dtsi
@@ -334,7 +334,6 @@
exit-latency-us = <6562>;
min-residency-us = <9987>;
local-timer-stop;
- status = "disabled";
};
};
};
diff --git a/arch/arm64/boot/dts/qcom/sm8350.dtsi b/arch/arm64/boot/dts/qcom/sm8350.dtsi
index 245dce24ec599c..fb3cd20a82b5ee 100644
--- a/arch/arm64/boot/dts/qcom/sm8350.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8350.dtsi
@@ -2382,8 +2382,8 @@
<&rpmhcc RPMH_CXO_CLK>;
clock-names = "iface", "core", "xo";
resets = <&gcc GCC_SDCC2_BCR>;
- interconnects = <&aggre2_noc MASTER_SDCC_2 0 &mc_virt SLAVE_EBI1 0>,
- <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_SDCC_2 0>;
+ interconnects = <&aggre2_noc MASTER_SDCC_2 &mc_virt SLAVE_EBI1>,
+ <&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_SDCC_2>;
interconnect-names = "sdhc-ddr","cpu-sdhc";
iommus = <&apps_smmu 0x4a0 0x0>;
power-domains = <&rpmhpd SM8350_CX>;
diff --git a/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts b/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts
index aa22a0c222655f..5d5d9574088cab 100644
--- a/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3328-roc-cc.dts
@@ -96,7 +96,6 @@
linux,default-trigger = "heartbeat";
gpios = <&rk805 1 GPIO_ACTIVE_LOW>;
default-state = "on";
- mode = <0x23>;
};
user_led: led-1 {
@@ -104,7 +103,6 @@
linux,default-trigger = "mmc1";
gpios = <&rk805 0 GPIO_ACTIVE_LOW>;
default-state = "off";
- mode = <0x05>;
};
};
};
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-op1-opp.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-op1-opp.dtsi
index 6e29e74f6fc680..783120e9cebeb4 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-op1-opp.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399-op1-opp.dtsi
@@ -111,7 +111,7 @@
};
};
- dmc_opp_table: dmc_opp_table {
+ dmc_opp_table: opp-table-3 {
compatible = "operating-points-v2";
opp00 {
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
index 04403a76238b83..a0795a2b1cb168 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
@@ -104,6 +104,13 @@
};
};
+&cpu_alert0 {
+ temperature = <65000>;
+};
+&cpu_alert1 {
+ temperature = <68000>;
+};
+
&cpu_l0 {
cpu-supply = <&vdd_cpu_l>;
};
diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
index 4391aea25984b5..1881b4b71f91d7 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi
@@ -589,7 +589,7 @@
clocks = <&cru HCLK_M_CRYPTO0>, <&cru HCLK_S_CRYPTO0>, <&cru SCLK_CRYPTO0>;
clock-names = "hclk_master", "hclk_slave", "sclk";
resets = <&cru SRST_CRYPTO0>, <&cru SRST_CRYPTO0_S>, <&cru SRST_CRYPTO0_M>;
- reset-names = "master", "lave", "crypto";
+ reset-names = "master", "slave", "crypto-rst";
};
crypto1: crypto@ff8b8000 {
@@ -599,7 +599,7 @@
clocks = <&cru HCLK_M_CRYPTO1>, <&cru HCLK_S_CRYPTO1>, <&cru SCLK_CRYPTO1>;
clock-names = "hclk_master", "hclk_slave", "sclk";
resets = <&cru SRST_CRYPTO1>, <&cru SRST_CRYPTO1_S>, <&cru SRST_CRYPTO1_M>;
- reset-names = "master", "slave", "crypto";
+ reset-names = "master", "slave", "crypto-rst";
};
i2c1: i2c@ff110000 {
@@ -2241,13 +2241,11 @@
pcfg_input_pull_up: pcfg-input-pull-up {
input-enable;
bias-pull-up;
- drive-strength = <2>;
};
pcfg_input_pull_down: pcfg-input-pull-down {
input-enable;
bias-pull-down;
- drive-strength = <2>;
};
clock {
diff --git a/arch/arm64/boot/dts/rockchip/rk3566-box-demo.dts b/arch/arm64/boot/dts/rockchip/rk3566-box-demo.dts
index 4c7f9abd594f16..d956496d522191 100644
--- a/arch/arm64/boot/dts/rockchip/rk3566-box-demo.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3566-box-demo.dts
@@ -353,6 +353,17 @@
};
};
+&pmu_io_domains {
+ pmuio2-supply = <&vcc_3v3>;
+ vccio1-supply = <&vcc_3v3>;
+ vccio3-supply = <&vcc_3v3>;
+ vccio4-supply = <&vcca_1v8>;
+ vccio5-supply = <&vcc_3v3>;
+ vccio6-supply = <&vcca_1v8>;
+ vccio7-supply = <&vcc_3v3>;
+ status = "okay";
+};
+
&pwm0 {
status = "okay";
};
diff --git a/arch/arm64/boot/dts/rockchip/rk3568-rock-3a.dts b/arch/arm64/boot/dts/rockchip/rk3568-rock-3a.dts
index a1c5fdf7d68f17..3c9d85257cc941 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568-rock-3a.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3568-rock-3a.dts
@@ -571,6 +571,8 @@
};
&i2s1_8ch {
+ pinctrl-names = "default";
+ pinctrl-0 = <&i2s1m0_sclktx &i2s1m0_lrcktx &i2s1m0_sdi0 &i2s1m0_sdo0>;
rockchip,trcm-sync-tx-only;
status = "okay";
};
@@ -730,14 +732,13 @@
disable-wp;
pinctrl-names = "default";
pinctrl-0 = <&sdmmc0_bus4 &sdmmc0_clk &sdmmc0_cmd &sdmmc0_det>;
- sd-uhs-sdr104;
+ sd-uhs-sdr50;
vmmc-supply = <&vcc3v3_sd>;
vqmmc-supply = <&vccio_sd>;
status = "okay";
};
&sdmmc2 {
- supports-sdio;
bus-width = <4>;
disable-wp;
cap-sd-highspeed;
diff --git a/arch/arm64/boot/dts/rockchip/rk356x.dtsi b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
index 5706c3e24f0a09..c27f1c7f072dae 100644
--- a/arch/arm64/boot/dts/rockchip/rk356x.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk356x.dtsi
@@ -966,6 +966,7 @@
clock-names = "aclk_mst", "aclk_slv",
"aclk_dbi", "pclk", "aux";
device_type = "pci";
+ #interrupt-cells = <1>;
interrupt-map-mask = <0 0 0 7>;
interrupt-map = <0 0 0 1 &pcie_intc 0>,
<0 0 0 2 &pcie_intc 1>,
diff --git a/arch/arm64/crypto/sm4-ce-ccm-core.S b/arch/arm64/crypto/sm4-ce-ccm-core.S
index 028207c4afd0f7..fa85856f33ceee 100644
--- a/arch/arm64/crypto/sm4-ce-ccm-core.S
+++ b/arch/arm64/crypto/sm4-ce-ccm-core.S
@@ -8,6 +8,7 @@
*/
#include <linux/linkage.h>
+#include <linux/cfi_types.h>
#include <asm/assembler.h>
#include "sm4-ce-asm.h"
@@ -104,7 +105,7 @@ SYM_FUNC_START(sm4_ce_ccm_final)
SYM_FUNC_END(sm4_ce_ccm_final)
.align 3
-SYM_FUNC_START(sm4_ce_ccm_enc)
+SYM_TYPED_FUNC_START(sm4_ce_ccm_enc)
/* input:
* x0: round key array, CTX
* x1: dst
@@ -216,7 +217,7 @@ SYM_FUNC_START(sm4_ce_ccm_enc)
SYM_FUNC_END(sm4_ce_ccm_enc)
.align 3
-SYM_FUNC_START(sm4_ce_ccm_dec)
+SYM_TYPED_FUNC_START(sm4_ce_ccm_dec)
/* input:
* x0: round key array, CTX
* x1: dst
diff --git a/arch/arm64/crypto/sm4-ce-gcm-core.S b/arch/arm64/crypto/sm4-ce-gcm-core.S
index 7aa3ec18a28912..347f25d7572793 100644
--- a/arch/arm64/crypto/sm4-ce-gcm-core.S
+++ b/arch/arm64/crypto/sm4-ce-gcm-core.S
@@ -9,6 +9,7 @@
*/
#include <linux/linkage.h>
+#include <linux/cfi_types.h>
#include <asm/assembler.h>
#include "sm4-ce-asm.h"
@@ -370,7 +371,7 @@ SYM_FUNC_START(pmull_ghash_update)
SYM_FUNC_END(pmull_ghash_update)
.align 3
-SYM_FUNC_START(sm4_ce_pmull_gcm_enc)
+SYM_TYPED_FUNC_START(sm4_ce_pmull_gcm_enc)
/* input:
* x0: round key array, CTX
* x1: dst
@@ -581,7 +582,7 @@ SYM_FUNC_END(sm4_ce_pmull_gcm_enc)
#define RH3 v20
.align 3
-SYM_FUNC_START(sm4_ce_pmull_gcm_dec)
+SYM_TYPED_FUNC_START(sm4_ce_pmull_gcm_dec)
/* input:
* x0: round key array, CTX
* x1: dst
diff --git a/arch/arm64/include/asm/atomic_ll_sc.h b/arch/arm64/include/asm/atomic_ll_sc.h
index 0890e4f568fb7f..cbb3d961123b17 100644
--- a/arch/arm64/include/asm/atomic_ll_sc.h
+++ b/arch/arm64/include/asm/atomic_ll_sc.h
@@ -315,7 +315,7 @@ __ll_sc__cmpxchg_double##name(unsigned long old1, \
" cbnz %w0, 1b\n" \
" " #mb "\n" \
"2:" \
- : "=&r" (tmp), "=&r" (ret), "+Q" (*(unsigned long *)ptr) \
+ : "=&r" (tmp), "=&r" (ret), "+Q" (*(__uint128_t *)ptr) \
: "r" (old1), "r" (old2), "r" (new1), "r" (new2) \
: cl); \
\
diff --git a/arch/arm64/include/asm/atomic_lse.h b/arch/arm64/include/asm/atomic_lse.h
index 52075e93de6c01..a94d6dacc0292e 100644
--- a/arch/arm64/include/asm/atomic_lse.h
+++ b/arch/arm64/include/asm/atomic_lse.h
@@ -311,7 +311,7 @@ __lse__cmpxchg_double##name(unsigned long old1, \
" eor %[old2], %[old2], %[oldval2]\n" \
" orr %[old1], %[old1], %[old2]" \
: [old1] "+&r" (x0), [old2] "+&r" (x1), \
- [v] "+Q" (*(unsigned long *)ptr) \
+ [v] "+Q" (*(__uint128_t *)ptr) \
: [new1] "r" (x2), [new2] "r" (x3), [ptr] "r" (x4), \
[oldval1] "r" (oldval1), [oldval2] "r" (oldval2) \
: cl); \
diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h
index 4e8b66c74ea2b2..683ca3af408485 100644
--- a/arch/arm64/include/asm/cputype.h
+++ b/arch/arm64/include/asm/cputype.h
@@ -124,6 +124,8 @@
#define APPLE_CPU_PART_M1_FIRESTORM_PRO 0x025
#define APPLE_CPU_PART_M1_ICESTORM_MAX 0x028
#define APPLE_CPU_PART_M1_FIRESTORM_MAX 0x029
+#define APPLE_CPU_PART_M2_BLIZZARD 0x032
+#define APPLE_CPU_PART_M2_AVALANCHE 0x033
#define AMPERE_CPU_PART_AMPERE1 0xAC3
@@ -177,6 +179,8 @@
#define MIDR_APPLE_M1_FIRESTORM_PRO MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_FIRESTORM_PRO)
#define MIDR_APPLE_M1_ICESTORM_MAX MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_ICESTORM_MAX)
#define MIDR_APPLE_M1_FIRESTORM_MAX MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_FIRESTORM_MAX)
+#define MIDR_APPLE_M2_BLIZZARD MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_BLIZZARD)
+#define MIDR_APPLE_M2_AVALANCHE MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_AVALANCHE)
#define MIDR_AMPERE1 MIDR_CPU_MODEL(ARM_CPU_IMP_AMPERE, AMPERE_CPU_PART_AMPERE1)
/* Fujitsu Erratum 010001 affects A64FX 1.0 and 1.1, (v0r0 and v1r0) */
diff --git a/arch/arm64/include/asm/efi.h b/arch/arm64/include/asm/efi.h
index 31d13a6001df49..de4ff90785b2c9 100644
--- a/arch/arm64/include/asm/efi.h
+++ b/arch/arm64/include/asm/efi.h
@@ -48,8 +48,17 @@ int efi_set_mapping_permissions(struct mm_struct *mm, efi_memory_desc_t *md);
})
extern spinlock_t efi_rt_lock;
+extern u64 *efi_rt_stack_top;
efi_status_t __efi_rt_asm_wrapper(void *, const char *, ...);
+/*
+ * efi_rt_stack_top[-1] contains the value the stack pointer had before
+ * switching to the EFI runtime stack.
+ */
+#define current_in_efi() \
+ (!preemptible() && efi_rt_stack_top != NULL && \
+ on_task_stack(current, READ_ONCE(efi_rt_stack_top[-1]), 1))
+
#define ARCH_EFI_IRQ_FLAGS_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT)
/*
diff --git a/arch/arm64/include/asm/esr.h b/arch/arm64/include/asm/esr.h
index 15b34fbfca6673..206de10524e338 100644
--- a/arch/arm64/include/asm/esr.h
+++ b/arch/arm64/include/asm/esr.h
@@ -114,6 +114,15 @@
#define ESR_ELx_FSC_ACCESS (0x08)
#define ESR_ELx_FSC_FAULT (0x04)
#define ESR_ELx_FSC_PERM (0x0C)
+#define ESR_ELx_FSC_SEA_TTW0 (0x14)
+#define ESR_ELx_FSC_SEA_TTW1 (0x15)
+#define ESR_ELx_FSC_SEA_TTW2 (0x16)
+#define ESR_ELx_FSC_SEA_TTW3 (0x17)
+#define ESR_ELx_FSC_SECC (0x18)
+#define ESR_ELx_FSC_SECC_TTW0 (0x1c)
+#define ESR_ELx_FSC_SECC_TTW1 (0x1d)
+#define ESR_ELx_FSC_SECC_TTW2 (0x1e)
+#define ESR_ELx_FSC_SECC_TTW3 (0x1f)
/* ISS field definitions for Data Aborts */
#define ESR_ELx_ISV_SHIFT (24)
diff --git a/arch/arm64/include/asm/hugetlb.h b/arch/arm64/include/asm/hugetlb.h
index d20f5da2d76fa3..6a4a1ab8eb238f 100644
--- a/arch/arm64/include/asm/hugetlb.h
+++ b/arch/arm64/include/asm/hugetlb.h
@@ -49,6 +49,15 @@ extern pte_t huge_ptep_get(pte_t *ptep);
void __init arm64_hugetlb_cma_reserve(void);
+#define huge_ptep_modify_prot_start huge_ptep_modify_prot_start
+extern pte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep);
+
+#define huge_ptep_modify_prot_commit huge_ptep_modify_prot_commit
+extern void huge_ptep_modify_prot_commit(struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep,
+ pte_t old_pte, pte_t new_pte);
+
#include <asm-generic/hugetlb.h>
#endif /* __ASM_HUGETLB_H */
diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
index 0df3fc3a017371..26b0c97df98636 100644
--- a/arch/arm64/include/asm/kvm_arm.h
+++ b/arch/arm64/include/asm/kvm_arm.h
@@ -319,21 +319,6 @@
BIT(18) | \
GENMASK(16, 15))
-/* For compatibility with fault code shared with 32-bit */
-#define FSC_FAULT ESR_ELx_FSC_FAULT
-#define FSC_ACCESS ESR_ELx_FSC_ACCESS
-#define FSC_PERM ESR_ELx_FSC_PERM
-#define FSC_SEA ESR_ELx_FSC_EXTABT
-#define FSC_SEA_TTW0 (0x14)
-#define FSC_SEA_TTW1 (0x15)
-#define FSC_SEA_TTW2 (0x16)
-#define FSC_SEA_TTW3 (0x17)
-#define FSC_SECC (0x18)
-#define FSC_SECC_TTW0 (0x1c)
-#define FSC_SECC_TTW1 (0x1d)
-#define FSC_SECC_TTW2 (0x1e)
-#define FSC_SECC_TTW3 (0x1f)
-
/* Hyp Prefetch Fault Address Register (HPFAR/HDFAR) */
#define HPFAR_MASK (~UL(0xf))
/*
diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
index 9bdba47f7e149f..193583df2d9c45 100644
--- a/arch/arm64/include/asm/kvm_emulate.h
+++ b/arch/arm64/include/asm/kvm_emulate.h
@@ -349,16 +349,16 @@ static __always_inline u8 kvm_vcpu_trap_get_fault_level(const struct kvm_vcpu *v
static __always_inline bool kvm_vcpu_abt_issea(const struct kvm_vcpu *vcpu)
{
switch (kvm_vcpu_trap_get_fault(vcpu)) {
- case FSC_SEA:
- case FSC_SEA_TTW0:
- case FSC_SEA_TTW1:
- case FSC_SEA_TTW2:
- case FSC_SEA_TTW3:
- case FSC_SECC:
- case FSC_SECC_TTW0:
- case FSC_SECC_TTW1:
- case FSC_SECC_TTW2:
- case FSC_SECC_TTW3:
+ case ESR_ELx_FSC_EXTABT:
+ case ESR_ELx_FSC_SEA_TTW0:
+ case ESR_ELx_FSC_SEA_TTW1:
+ case ESR_ELx_FSC_SEA_TTW2:
+ case ESR_ELx_FSC_SEA_TTW3:
+ case ESR_ELx_FSC_SECC:
+ case ESR_ELx_FSC_SECC_TTW0:
+ case ESR_ELx_FSC_SECC_TTW1:
+ case ESR_ELx_FSC_SECC_TTW2:
+ case ESR_ELx_FSC_SECC_TTW3:
return true;
default:
return false;
@@ -373,8 +373,26 @@ static __always_inline int kvm_vcpu_sys_get_rt(struct kvm_vcpu *vcpu)
static inline bool kvm_is_write_fault(struct kvm_vcpu *vcpu)
{
- if (kvm_vcpu_abt_iss1tw(vcpu))
- return true;
+ if (kvm_vcpu_abt_iss1tw(vcpu)) {
+ /*
+ * Only a permission fault on a S1PTW should be
+ * considered as a write. Otherwise, page tables baked
+ * in a read-only memslot will result in an exception
+ * being delivered in the guest.
+ *
+ * The drawback is that we end-up faulting twice if the
+ * guest is using any of HW AF/DB: a translation fault
+ * to map the page containing the PT (read only at
+ * first), then a permission fault to allow the flags
+ * to be set.
+ */
+ switch (kvm_vcpu_trap_get_fault_type(vcpu)) {
+ case ESR_ELx_FSC_PERM:
+ return true;
+ default:
+ return false;
+ }
+ }
if (kvm_vcpu_trap_is_iabt(vcpu))
return false;
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
index b4bbeed80fb6ed..65e78999c75d76 100644
--- a/arch/arm64/include/asm/pgtable.h
+++ b/arch/arm64/include/asm/pgtable.h
@@ -681,7 +681,7 @@ static inline unsigned long pmd_page_vaddr(pmd_t pmd)
#define pud_leaf(pud) (pud_present(pud) && !pud_table(pud))
#define pud_valid(pud) pte_valid(pud_pte(pud))
#define pud_user(pud) pte_user(pud_pte(pud))
-
+#define pud_user_exec(pud) pte_user_exec(pud_pte(pud))
static inline void set_pud(pud_t *pudp, pud_t pud)
{
@@ -730,6 +730,7 @@ static inline pmd_t *pud_pgtable(pud_t pud)
#else
#define pud_page_paddr(pud) ({ BUILD_BUG(); 0; })
+#define pud_user_exec(pud) pud_user(pud) /* Always 0 with folding */
/* Match pmd_offset folding in <asm/generic/pgtable-nopmd.h> */
#define pmd_set_fixmap(addr) NULL
@@ -862,12 +863,12 @@ static inline bool pte_user_accessible_page(pte_t pte)
static inline bool pmd_user_accessible_page(pmd_t pmd)
{
- return pmd_leaf(pmd) && (pmd_user(pmd) || pmd_user_exec(pmd));
+ return pmd_leaf(pmd) && !pmd_present_invalid(pmd) && (pmd_user(pmd) || pmd_user_exec(pmd));
}
static inline bool pud_user_accessible_page(pud_t pud)
{
- return pud_leaf(pud) && pud_user(pud);
+ return pud_leaf(pud) && (pud_user(pud) || pud_user_exec(pud));
}
#endif
@@ -1093,6 +1094,15 @@ static inline bool pud_sect_supported(void)
}
+#define __HAVE_ARCH_PTEP_MODIFY_PROT_TRANSACTION
+#define ptep_modify_prot_start ptep_modify_prot_start
+extern pte_t ptep_modify_prot_start(struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep);
+
+#define ptep_modify_prot_commit ptep_modify_prot_commit
+extern void ptep_modify_prot_commit(struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep,
+ pte_t old_pte, pte_t new_pte);
#endif /* !__ASSEMBLY__ */
#endif /* __ASM_PGTABLE_H */
diff --git a/arch/arm64/include/asm/stacktrace.h b/arch/arm64/include/asm/stacktrace.h
index 4e5354beafb01b..66ec8caa6ac07f 100644
--- a/arch/arm64/include/asm/stacktrace.h
+++ b/arch/arm64/include/asm/stacktrace.h
@@ -106,4 +106,19 @@ static inline struct stack_info stackinfo_get_sdei_critical(void)
#define stackinfo_get_sdei_critical() stackinfo_get_unknown()
#endif
+#ifdef CONFIG_EFI
+extern u64 *efi_rt_stack_top;
+
+static inline struct stack_info stackinfo_get_efi(void)
+{
+ unsigned long high = (u64)efi_rt_stack_top;
+ unsigned long low = high - THREAD_SIZE;
+
+ return (struct stack_info) {
+ .low = low,
+ .high = high,
+ };
+}
+#endif
+
#endif /* __ASM_STACKTRACE_H */
diff --git a/arch/arm64/include/asm/uprobes.h b/arch/arm64/include/asm/uprobes.h
index ba4bff5ca67494..2b09495499c618 100644
--- a/arch/arm64/include/asm/uprobes.h
+++ b/arch/arm64/include/asm/uprobes.h
@@ -16,7 +16,7 @@
#define UPROBE_SWBP_INSN_SIZE AARCH64_INSN_SIZE
#define UPROBE_XOL_SLOT_BYTES MAX_UINSN_BYTES
-typedef u32 uprobe_opcode_t;
+typedef __le32 uprobe_opcode_t;
struct arch_uprobe_task {
};
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index 89ac00084f38a4..307faa2b4395ed 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -661,6 +661,13 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
CAP_MIDR_RANGE_LIST(trbe_write_out_of_range_cpus),
},
#endif
+#ifdef CONFIG_ARM64_ERRATUM_2645198
+ {
+ .desc = "ARM erratum 2645198",
+ .capability = ARM64_WORKAROUND_2645198,
+ ERRATA_MIDR_ALL_VERSIONS(MIDR_CORTEX_A715)
+ },
+#endif
#ifdef CONFIG_ARM64_ERRATUM_2077057
{
.desc = "ARM erratum 2077057",
diff --git a/arch/arm64/kernel/efi-rt-wrapper.S b/arch/arm64/kernel/efi-rt-wrapper.S
index a00886410537d6..e8ae803662cf14 100644
--- a/arch/arm64/kernel/efi-rt-wrapper.S
+++ b/arch/arm64/kernel/efi-rt-wrapper.S
@@ -4,6 +4,7 @@
*/
#include <linux/linkage.h>
+#include <asm/assembler.h>
SYM_FUNC_START(__efi_rt_asm_wrapper)
stp x29, x30, [sp, #-112]!
@@ -45,7 +46,10 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
mov x4, x6
blr x8
+ mov x16, sp
mov sp, x29
+ str xzr, [x16, #8] // clear recorded task SP value
+
ldp x1, x2, [sp, #16]
cmp x2, x18
ldp x29, x30, [sp], #112
@@ -70,6 +74,9 @@ SYM_FUNC_END(__efi_rt_asm_wrapper)
SYM_CODE_START(__efi_rt_asm_recover)
mov sp, x30
+ ldr_l x16, efi_rt_stack_top // clear recorded task SP value
+ str xzr, [x16, #-8]
+
ldp x19, x20, [sp, #32]
ldp x21, x22, [sp, #48]
ldp x23, x24, [sp, #64]
diff --git a/arch/arm64/kernel/efi.c b/arch/arm64/kernel/efi.c
index fab05de2e12dd5..b273900f456685 100644
--- a/arch/arm64/kernel/efi.c
+++ b/arch/arm64/kernel/efi.c
@@ -11,6 +11,7 @@
#include <linux/init.h>
#include <asm/efi.h>
+#include <asm/stacktrace.h>
static bool region_is_misaligned(const efi_memory_desc_t *md)
{
@@ -154,7 +155,7 @@ asmlinkage efi_status_t __efi_rt_asm_recover(void);
bool efi_runtime_fixup_exception(struct pt_regs *regs, const char *msg)
{
/* Check whether the exception occurred while running the firmware */
- if (current_work() != &efi_rts_work.work || regs->pc >= TASK_SIZE_64)
+ if (!current_in_efi() || regs->pc >= TASK_SIZE_64)
return false;
pr_err(FW_BUG "Unable to handle %s in EFI runtime service\n", msg);
diff --git a/arch/arm64/kernel/elfcore.c b/arch/arm64/kernel/elfcore.c
index 353009d7f307f4..2e94d20c4ac7a7 100644
--- a/arch/arm64/kernel/elfcore.c
+++ b/arch/arm64/kernel/elfcore.c
@@ -8,28 +8,27 @@
#include <asm/cpufeature.h>
#include <asm/mte.h>
-#define for_each_mte_vma(vmi, vma) \
+#define for_each_mte_vma(cprm, i, m) \
if (system_supports_mte()) \
- for_each_vma(vmi, vma) \
- if (vma->vm_flags & VM_MTE)
+ for (i = 0, m = cprm->vma_meta; \
+ i < cprm->vma_count; \
+ i++, m = cprm->vma_meta + i) \
+ if (m->flags & VM_MTE)
-static unsigned long mte_vma_tag_dump_size(struct vm_area_struct *vma)
+static unsigned long mte_vma_tag_dump_size(struct core_vma_metadata *m)
{
- if (vma->vm_flags & VM_DONTDUMP)
- return 0;
-
- return vma_pages(vma) * MTE_PAGE_TAG_STORAGE;
+ return (m->dump_size >> PAGE_SHIFT) * MTE_PAGE_TAG_STORAGE;
}
/* Derived from dump_user_range(); start/end must be page-aligned */
static int mte_dump_tag_range(struct coredump_params *cprm,
- unsigned long start, unsigned long end)
+ unsigned long start, unsigned long len)
{
int ret = 1;
unsigned long addr;
void *tags = NULL;
- for (addr = start; addr < end; addr += PAGE_SIZE) {
+ for (addr = start; addr < start + len; addr += PAGE_SIZE) {
struct page *page = get_dump_page(addr);
/*
@@ -65,7 +64,6 @@ static int mte_dump_tag_range(struct coredump_params *cprm,
mte_save_page_tags(page_address(page), tags);
put_page(page);
if (!dump_emit(cprm, tags, MTE_PAGE_TAG_STORAGE)) {
- mte_free_tag_storage(tags);
ret = 0;
break;
}
@@ -77,13 +75,13 @@ static int mte_dump_tag_range(struct coredump_params *cprm,
return ret;
}
-Elf_Half elf_core_extra_phdrs(void)
+Elf_Half elf_core_extra_phdrs(struct coredump_params *cprm)
{
- struct vm_area_struct *vma;
+ int i;
+ struct core_vma_metadata *m;
int vma_count = 0;
- VMA_ITERATOR(vmi, current->mm, 0);
- for_each_mte_vma(vmi, vma)
+ for_each_mte_vma(cprm, i, m)
vma_count++;
return vma_count;
@@ -91,18 +89,18 @@ Elf_Half elf_core_extra_phdrs(void)
int elf_core_write_extra_phdrs(struct coredump_params *cprm, loff_t offset)
{
- struct vm_area_struct *vma;
- VMA_ITERATOR(vmi, current->mm, 0);
+ int i;
+ struct core_vma_metadata *m;
- for_each_mte_vma(vmi, vma) {
+ for_each_mte_vma(cprm, i, m) {
struct elf_phdr phdr;
phdr.p_type = PT_AARCH64_MEMTAG_MTE;
phdr.p_offset = offset;
- phdr.p_vaddr = vma->vm_start;
+ phdr.p_vaddr = m->start;
phdr.p_paddr = 0;
- phdr.p_filesz = mte_vma_tag_dump_size(vma);
- phdr.p_memsz = vma->vm_end - vma->vm_start;
+ phdr.p_filesz = mte_vma_tag_dump_size(m);
+ phdr.p_memsz = m->end - m->start;
offset += phdr.p_filesz;
phdr.p_flags = 0;
phdr.p_align = 0;
@@ -114,28 +112,25 @@ int elf_core_write_extra_phdrs(struct coredump_params *cprm, loff_t offset)
return 1;
}
-size_t elf_core_extra_data_size(void)
+size_t elf_core_extra_data_size(struct coredump_params *cprm)
{
- struct vm_area_struct *vma;
+ int i;
+ struct core_vma_metadata *m;
size_t data_size = 0;
- VMA_ITERATOR(vmi, current->mm, 0);
- for_each_mte_vma(vmi, vma)
- data_size += mte_vma_tag_dump_size(vma);
+ for_each_mte_vma(cprm, i, m)
+ data_size += mte_vma_tag_dump_size(m);
return data_size;
}
int elf_core_write_extra_data(struct coredump_params *cprm)
{
- struct vm_area_struct *vma;
- VMA_ITERATOR(vmi, current->mm, 0);
-
- for_each_mte_vma(vmi, vma) {
- if (vma->vm_flags & VM_DONTDUMP)
- continue;
+ int i;
+ struct core_vma_metadata *m;
- if (!mte_dump_tag_range(cprm, vma->vm_start, vma->vm_end))
+ for_each_mte_vma(cprm, i, m) {
+ if (!mte_dump_tag_range(cprm, m->start, m->dump_size))
return 0;
}
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index dcc81e7200d401..b6ef1af0122ebd 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -385,7 +385,7 @@ static void task_fpsimd_load(void)
WARN_ON(!system_supports_fpsimd());
WARN_ON(!have_cpu_fpsimd_context());
- if (system_supports_sve()) {
+ if (system_supports_sve() || system_supports_sme()) {
switch (current->thread.fp_type) {
case FP_STATE_FPSIMD:
/* Stop tracking SVE for this task until next use. */
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 2686ab15760173..0c321ad23cd3a4 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -1357,7 +1357,7 @@ enum aarch64_regset {
#ifdef CONFIG_ARM64_SVE
REGSET_SVE,
#endif
-#ifdef CONFIG_ARM64_SVE
+#ifdef CONFIG_ARM64_SME
REGSET_SSVE,
REGSET_ZA,
#endif
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index e0d09bf5b01b80..be279fd482480d 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -281,7 +281,12 @@ static int restore_sve_fpsimd_context(struct user_ctxs *user)
vl = task_get_sme_vl(current);
} else {
- if (!system_supports_sve())
+ /*
+ * A SME only system use SVE for streaming mode so can
+ * have a SVE formatted context with a zero VL and no
+ * payload data.
+ */
+ if (!system_supports_sve() && !system_supports_sme())
return -EINVAL;
vl = task_get_sve_vl(current);
@@ -732,7 +737,7 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
return err;
}
- if (system_supports_sve()) {
+ if (system_supports_sve() || system_supports_sme()) {
unsigned int vq = 0;
if (add_all || test_thread_flag(TIF_SVE) ||
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c
index 117e2c180f3c77..83154303e682c8 100644
--- a/arch/arm64/kernel/stacktrace.c
+++ b/arch/arm64/kernel/stacktrace.c
@@ -5,6 +5,7 @@
* Copyright (C) 2012 ARM Ltd.
*/
#include <linux/kernel.h>
+#include <linux/efi.h>
#include <linux/export.h>
#include <linux/ftrace.h>
#include <linux/sched.h>
@@ -12,6 +13,7 @@
#include <linux/sched/task_stack.h>
#include <linux/stacktrace.h>
+#include <asm/efi.h>
#include <asm/irq.h>
#include <asm/stack_pointer.h>
#include <asm/stacktrace.h>
@@ -186,6 +188,13 @@ void show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl)
: stackinfo_get_unknown(); \
})
+#define STACKINFO_EFI \
+ ({ \
+ ((task == current) && current_in_efi()) \
+ ? stackinfo_get_efi() \
+ : stackinfo_get_unknown(); \
+ })
+
noinline noinstr void arch_stack_walk(stack_trace_consume_fn consume_entry,
void *cookie, struct task_struct *task,
struct pt_regs *regs)
@@ -200,6 +209,9 @@ noinline noinstr void arch_stack_walk(stack_trace_consume_fn consume_entry,
STACKINFO_SDEI(normal),
STACKINFO_SDEI(critical),
#endif
+#ifdef CONFIG_EFI
+ STACKINFO_EFI,
+#endif
};
struct unwind_state state = {
.stacks = stacks,
diff --git a/arch/arm64/kvm/guest.c b/arch/arm64/kvm/guest.c
index 5626ddb540ce33..cf4c495a432133 100644
--- a/arch/arm64/kvm/guest.c
+++ b/arch/arm64/kvm/guest.c
@@ -1079,7 +1079,7 @@ long kvm_vm_ioctl_mte_copy_tags(struct kvm *kvm,
/* uaccess failed, don't leave stale tags */
if (num_tags != MTE_GRANULES_PER_PAGE)
- mte_clear_page_tags(page);
+ mte_clear_page_tags(maddr);
set_page_mte_tagged(page);
kvm_release_pfn_dirty(pfn);
diff --git a/arch/arm64/kvm/hyp/include/hyp/fault.h b/arch/arm64/kvm/hyp/include/hyp/fault.h
index 1b8a2dcd712f32..9ddcfe2c3e574f 100644
--- a/arch/arm64/kvm/hyp/include/hyp/fault.h
+++ b/arch/arm64/kvm/hyp/include/hyp/fault.h
@@ -60,7 +60,7 @@ static inline bool __get_fault_info(u64 esr, struct kvm_vcpu_fault_info *fault)
*/
if (!(esr & ESR_ELx_S1PTW) &&
(cpus_have_final_cap(ARM64_WORKAROUND_834220) ||
- (esr & ESR_ELx_FSC_TYPE) == FSC_PERM)) {
+ (esr & ESR_ELx_FSC_TYPE) == ESR_ELx_FSC_PERM)) {
if (!__translate_far_to_hpfar(far, &hpfar))
return false;
} else {
diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h
index 3330d1b76bdd20..07d37ff88a3f2a 100644
--- a/arch/arm64/kvm/hyp/include/hyp/switch.h
+++ b/arch/arm64/kvm/hyp/include/hyp/switch.h
@@ -367,7 +367,7 @@ static bool kvm_hyp_handle_dabt_low(struct kvm_vcpu *vcpu, u64 *exit_code)
if (static_branch_unlikely(&vgic_v2_cpuif_trap)) {
bool valid;
- valid = kvm_vcpu_trap_get_fault_type(vcpu) == FSC_FAULT &&
+ valid = kvm_vcpu_trap_get_fault_type(vcpu) == ESR_ELx_FSC_FAULT &&
kvm_vcpu_dabt_isvalid(vcpu) &&
!kvm_vcpu_abt_issea(vcpu) &&
!kvm_vcpu_abt_iss1tw(vcpu);
diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c
index 31d7fa4c7c1405..a3ee3b605c9b80 100644
--- a/arch/arm64/kvm/mmu.c
+++ b/arch/arm64/kvm/mmu.c
@@ -1212,7 +1212,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
exec_fault = kvm_vcpu_trap_is_exec_fault(vcpu);
VM_BUG_ON(write_fault && exec_fault);
- if (fault_status == FSC_PERM && !write_fault && !exec_fault) {
+ if (fault_status == ESR_ELx_FSC_PERM && !write_fault && !exec_fault) {
kvm_err("Unexpected L2 read permission error\n");
return -EFAULT;
}
@@ -1277,7 +1277,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
* only exception to this is when dirty logging is enabled at runtime
* and a write fault needs to collapse a block entry into a table.
*/
- if (fault_status != FSC_PERM || (logging_active && write_fault)) {
+ if (fault_status != ESR_ELx_FSC_PERM ||
+ (logging_active && write_fault)) {
ret = kvm_mmu_topup_memory_cache(memcache,
kvm_mmu_cache_min_pages(kvm));
if (ret)
@@ -1342,7 +1343,8 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
* backed by a THP and thus use block mapping if possible.
*/
if (vma_pagesize == PAGE_SIZE && !(force_pte || device)) {
- if (fault_status == FSC_PERM && fault_granule > PAGE_SIZE)
+ if (fault_status == ESR_ELx_FSC_PERM &&
+ fault_granule > PAGE_SIZE)
vma_pagesize = fault_granule;
else
vma_pagesize = transparent_hugepage_adjust(kvm, memslot,
@@ -1350,7 +1352,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
&fault_ipa);
}
- if (fault_status != FSC_PERM && !device && kvm_has_mte(kvm)) {
+ if (fault_status != ESR_ELx_FSC_PERM && !device && kvm_has_mte(kvm)) {
/* Check the VMM hasn't introduced a new disallowed VMA */
if (kvm_vma_mte_allowed(vma)) {
sanitise_mte_tags(kvm, pfn, vma_pagesize);
@@ -1376,7 +1378,7 @@ static int user_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
* permissions only if vma_pagesize equals fault_granule. Otherwise,
* kvm_pgtable_stage2_map() should be called to change block size.
*/
- if (fault_status == FSC_PERM && vma_pagesize == fault_granule)
+ if (fault_status == ESR_ELx_FSC_PERM && vma_pagesize == fault_granule)
ret = kvm_pgtable_stage2_relax_perms(pgt, fault_ipa, prot);
else
ret = kvm_pgtable_stage2_map(pgt, fault_ipa, vma_pagesize,
@@ -1441,7 +1443,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
fault_ipa = kvm_vcpu_get_fault_ipa(vcpu);
is_iabt = kvm_vcpu_trap_is_iabt(vcpu);
- if (fault_status == FSC_FAULT) {
+ if (fault_status == ESR_ELx_FSC_FAULT) {
/* Beyond sanitised PARange (which is the IPA limit) */
if (fault_ipa >= BIT_ULL(get_kvm_ipa_limit())) {
kvm_inject_size_fault(vcpu);
@@ -1476,8 +1478,9 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
kvm_vcpu_get_hfar(vcpu), fault_ipa);
/* Check the stage-2 fault is trans. fault or write fault */
- if (fault_status != FSC_FAULT && fault_status != FSC_PERM &&
- fault_status != FSC_ACCESS) {
+ if (fault_status != ESR_ELx_FSC_FAULT &&
+ fault_status != ESR_ELx_FSC_PERM &&
+ fault_status != ESR_ELx_FSC_ACCESS) {
kvm_err("Unsupported FSC: EC=%#x xFSC=%#lx ESR_EL2=%#lx\n",
kvm_vcpu_trap_get_class(vcpu),
(unsigned long)kvm_vcpu_trap_get_fault(vcpu),
@@ -1539,7 +1542,7 @@ int kvm_handle_guest_abort(struct kvm_vcpu *vcpu)
/* Userspace should not be able to register out-of-bounds IPAs */
VM_BUG_ON(fault_ipa >= kvm_phys_size(vcpu->kvm));
- if (fault_status == FSC_ACCESS) {
+ if (fault_status == ESR_ELx_FSC_ACCESS) {
handle_access_fault(vcpu, fault_ipa);
ret = 1;
goto out_unlock;
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index d5ee52d6bf7326..c6cbfe6b854b31 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -646,7 +646,7 @@ static void reset_pmcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
return;
/* Only preserve PMCR_EL0.N, and reset the rest to 0 */
- pmcr = read_sysreg(pmcr_el0) & ARMV8_PMU_PMCR_N_MASK;
+ pmcr = read_sysreg(pmcr_el0) & (ARMV8_PMU_PMCR_N_MASK << ARMV8_PMU_PMCR_N_SHIFT);
if (!kvm_supports_32bit_el0())
pmcr |= ARMV8_PMU_PMCR_LC;
diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c
index 94a666dd1443d1..2642e9ce28199c 100644
--- a/arch/arm64/kvm/vgic/vgic-its.c
+++ b/arch/arm64/kvm/vgic/vgic-its.c
@@ -2187,7 +2187,7 @@ static int vgic_its_save_ite(struct vgic_its *its, struct its_device *dev,
((u64)ite->irq->intid << KVM_ITS_ITE_PINTID_SHIFT) |
ite->collection->collection_id;
val = cpu_to_le64(val);
- return kvm_write_guest_lock(kvm, gpa, &val, ite_esz);
+ return vgic_write_guest_lock(kvm, gpa, &val, ite_esz);
}
/**
@@ -2339,7 +2339,7 @@ static int vgic_its_save_dte(struct vgic_its *its, struct its_device *dev,
(itt_addr_field << KVM_ITS_DTE_ITTADDR_SHIFT) |
(dev->num_eventid_bits - 1));
val = cpu_to_le64(val);
- return kvm_write_guest_lock(kvm, ptr, &val, dte_esz);
+ return vgic_write_guest_lock(kvm, ptr, &val, dte_esz);
}
/**
@@ -2526,7 +2526,7 @@ static int vgic_its_save_cte(struct vgic_its *its,
((u64)collection->target_addr << KVM_ITS_CTE_RDBASE_SHIFT) |
collection->collection_id);
val = cpu_to_le64(val);
- return kvm_write_guest_lock(its->dev->kvm, gpa, &val, esz);
+ return vgic_write_guest_lock(its->dev->kvm, gpa, &val, esz);
}
/*
@@ -2607,7 +2607,7 @@ static int vgic_its_save_collection_table(struct vgic_its *its)
*/
val = 0;
BUG_ON(cte_esz > sizeof(val));
- ret = kvm_write_guest_lock(its->dev->kvm, gpa, &val, cte_esz);
+ ret = vgic_write_guest_lock(its->dev->kvm, gpa, &val, cte_esz);
return ret;
}
@@ -2743,7 +2743,6 @@ static int vgic_its_has_attr(struct kvm_device *dev,
static int vgic_its_ctrl(struct kvm *kvm, struct vgic_its *its, u64 attr)
{
const struct vgic_its_abi *abi = vgic_its_get_abi(its);
- struct vgic_dist *dist = &kvm->arch.vgic;
int ret = 0;
if (attr == KVM_DEV_ARM_VGIC_CTRL_INIT) /* Nothing to do */
@@ -2763,9 +2762,7 @@ static int vgic_its_ctrl(struct kvm *kvm, struct vgic_its *its, u64 attr)
vgic_its_reset(kvm, its);
break;
case KVM_DEV_ARM_ITS_SAVE_TABLES:
- dist->save_its_tables_in_progress = true;
ret = abi->save_tables(its);
- dist->save_its_tables_in_progress = false;
break;
case KVM_DEV_ARM_ITS_RESTORE_TABLES:
ret = abi->restore_tables(its);
@@ -2792,7 +2789,7 @@ bool kvm_arch_allow_write_without_running_vcpu(struct kvm *kvm)
{
struct vgic_dist *dist = &kvm->arch.vgic;
- return dist->save_its_tables_in_progress;
+ return dist->table_write_in_progress;
}
static int vgic_its_set_attr(struct kvm_device *dev,
diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 826ff6f2a4e7b7..684bdfaad4a9de 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -339,7 +339,7 @@ retry:
if (status) {
/* clear consumed data */
val &= ~(1 << bit_nr);
- ret = kvm_write_guest_lock(kvm, ptr, &val, 1);
+ ret = vgic_write_guest_lock(kvm, ptr, &val, 1);
if (ret)
return ret;
}
@@ -350,26 +350,23 @@ retry:
* The deactivation of the doorbell interrupt will trigger the
* unmapping of the associated vPE.
*/
-static void unmap_all_vpes(struct vgic_dist *dist)
+static void unmap_all_vpes(struct kvm *kvm)
{
- struct irq_desc *desc;
+ struct vgic_dist *dist = &kvm->arch.vgic;
int i;
- for (i = 0; i < dist->its_vm.nr_vpes; i++) {
- desc = irq_to_desc(dist->its_vm.vpes[i]->irq);
- irq_domain_deactivate_irq(irq_desc_get_irq_data(desc));
- }
+ for (i = 0; i < dist->its_vm.nr_vpes; i++)
+ free_irq(dist->its_vm.vpes[i]->irq, kvm_get_vcpu(kvm, i));
}
-static void map_all_vpes(struct vgic_dist *dist)
+static void map_all_vpes(struct kvm *kvm)
{
- struct irq_desc *desc;
+ struct vgic_dist *dist = &kvm->arch.vgic;
int i;
- for (i = 0; i < dist->its_vm.nr_vpes; i++) {
- desc = irq_to_desc(dist->its_vm.vpes[i]->irq);
- irq_domain_activate_irq(irq_desc_get_irq_data(desc), false);
- }
+ for (i = 0; i < dist->its_vm.nr_vpes; i++)
+ WARN_ON(vgic_v4_request_vpe_irq(kvm_get_vcpu(kvm, i),
+ dist->its_vm.vpes[i]->irq));
}
/**
@@ -394,7 +391,7 @@ int vgic_v3_save_pending_tables(struct kvm *kvm)
* and enabling of the doorbells have already been done.
*/
if (kvm_vgic_global_state.has_gicv4_1) {
- unmap_all_vpes(dist);
+ unmap_all_vpes(kvm);
vlpi_avail = true;
}
@@ -437,14 +434,14 @@ int vgic_v3_save_pending_tables(struct kvm *kvm)
else
val &= ~(1 << bit_nr);
- ret = kvm_write_guest_lock(kvm, ptr, &val, 1);
+ ret = vgic_write_guest_lock(kvm, ptr, &val, 1);
if (ret)
goto out;
}
out:
if (vlpi_avail)
- map_all_vpes(dist);
+ map_all_vpes(kvm);
return ret;
}
@@ -616,6 +613,8 @@ static const struct midr_range broken_seis[] = {
MIDR_ALL_VERSIONS(MIDR_APPLE_M1_FIRESTORM_PRO),
MIDR_ALL_VERSIONS(MIDR_APPLE_M1_ICESTORM_MAX),
MIDR_ALL_VERSIONS(MIDR_APPLE_M1_FIRESTORM_MAX),
+ MIDR_ALL_VERSIONS(MIDR_APPLE_M2_BLIZZARD),
+ MIDR_ALL_VERSIONS(MIDR_APPLE_M2_AVALANCHE),
{},
};
diff --git a/arch/arm64/kvm/vgic/vgic-v4.c b/arch/arm64/kvm/vgic/vgic-v4.c
index ad06ba6c9b0091..a413718be92b87 100644
--- a/arch/arm64/kvm/vgic/vgic-v4.c
+++ b/arch/arm64/kvm/vgic/vgic-v4.c
@@ -222,6 +222,11 @@ void vgic_v4_get_vlpi_state(struct vgic_irq *irq, bool *val)
*val = !!(*ptr & mask);
}
+int vgic_v4_request_vpe_irq(struct kvm_vcpu *vcpu, int irq)
+{
+ return request_irq(irq, vgic_v4_doorbell_handler, 0, "vcpu", vcpu);
+}
+
/**
* vgic_v4_init - Initialize the GICv4 data structures
* @kvm: Pointer to the VM being initialized
@@ -283,8 +288,7 @@ int vgic_v4_init(struct kvm *kvm)
irq_flags &= ~IRQ_NOAUTOEN;
irq_set_status_flags(irq, irq_flags);
- ret = request_irq(irq, vgic_v4_doorbell_handler,
- 0, "vcpu", vcpu);
+ ret = vgic_v4_request_vpe_irq(vcpu, irq);
if (ret) {
kvm_err("failed to allocate vcpu IRQ%d\n", irq);
/*
diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
index 0c8da72953f06b..7f7f3c5ed85a0f 100644
--- a/arch/arm64/kvm/vgic/vgic.h
+++ b/arch/arm64/kvm/vgic/vgic.h
@@ -6,6 +6,7 @@
#define __KVM_ARM_VGIC_NEW_H__
#include <linux/irqchip/arm-gic-common.h>
+#include <asm/kvm_mmu.h>
#define PRODUCT_ID_KVM 0x4b /* ASCII code K */
#define IMPLEMENTER_ARM 0x43b
@@ -131,6 +132,19 @@ static inline bool vgic_irq_is_multi_sgi(struct vgic_irq *irq)
return vgic_irq_get_lr_count(irq) > 1;
}
+static inline int vgic_write_guest_lock(struct kvm *kvm, gpa_t gpa,
+ const void *data, unsigned long len)
+{
+ struct vgic_dist *dist = &kvm->arch.vgic;
+ int ret;
+
+ dist->table_write_in_progress = true;
+ ret = kvm_write_guest_lock(kvm, gpa, data, len);
+ dist->table_write_in_progress = false;
+
+ return ret;
+}
+
/*
* This struct provides an intermediate representation of the fields contained
* in the GICH_VMCR and ICH_VMCR registers, such that code exporting the GIC
@@ -331,5 +345,6 @@ int vgic_v4_init(struct kvm *kvm);
void vgic_v4_teardown(struct kvm *kvm);
void vgic_v4_configure_vsgis(struct kvm *kvm);
void vgic_v4_get_vlpi_state(struct vgic_irq *irq, bool *val);
+int vgic_v4_request_vpe_irq(struct kvm_vcpu *vcpu, int irq);
#endif
diff --git a/arch/arm64/mm/hugetlbpage.c b/arch/arm64/mm/hugetlbpage.c
index 35e9a468d13e6a..95364e8bdc1944 100644
--- a/arch/arm64/mm/hugetlbpage.c
+++ b/arch/arm64/mm/hugetlbpage.c
@@ -559,3 +559,24 @@ bool __init arch_hugetlb_valid_size(unsigned long size)
{
return __hugetlb_valid_size(size);
}
+
+pte_t huge_ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep)
+{
+ if (IS_ENABLED(CONFIG_ARM64_ERRATUM_2645198) &&
+ cpus_have_const_cap(ARM64_WORKAROUND_2645198)) {
+ /*
+ * Break-before-make (BBM) is required for all user space mappings
+ * when the permission changes from executable to non-executable
+ * in cases where cpu is affected with errata #2645198.
+ */
+ if (pte_user_exec(READ_ONCE(*ptep)))
+ return huge_ptep_clear_flush(vma, addr, ptep);
+ }
+ return huge_ptep_get_and_clear(vma->vm_mm, addr, ptep);
+}
+
+void huge_ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep,
+ pte_t old_pte, pte_t pte)
+{
+ set_huge_pte_at(vma->vm_mm, addr, ptep, pte);
+}
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 14c87e8d69d839..d77c9f56b7b435 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -1630,3 +1630,24 @@ static int __init prevent_bootmem_remove_init(void)
}
early_initcall(prevent_bootmem_remove_init);
#endif
+
+pte_t ptep_modify_prot_start(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep)
+{
+ if (IS_ENABLED(CONFIG_ARM64_ERRATUM_2645198) &&
+ cpus_have_const_cap(ARM64_WORKAROUND_2645198)) {
+ /*
+ * Break-before-make (BBM) is required for all user space mappings
+ * when the permission changes from executable to non-executable
+ * in cases where cpu is affected with errata #2645198.
+ */
+ if (pte_user_exec(READ_ONCE(*ptep)))
+ return ptep_clear_flush(vma, addr, ptep);
+ }
+ return ptep_get_and_clear(vma->vm_mm, addr, ptep);
+}
+
+void ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep,
+ pte_t old_pte, pte_t pte)
+{
+ set_pte_at(vma->vm_mm, addr, ptep, pte);
+}
diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
index a86ee376920a08..dfeb2c51e2573d 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -71,6 +71,7 @@ WORKAROUND_2038923
WORKAROUND_2064142
WORKAROUND_2077057
WORKAROUND_2457168
+WORKAROUND_2645198
WORKAROUND_2658417
WORKAROUND_TRBE_OVERWRITE_FILL_MODE
WORKAROUND_TSB_FLUSH_FAILURE
diff --git a/arch/ia64/kernel/elfcore.c b/arch/ia64/kernel/elfcore.c
index 94680521fbf919..8895df12154047 100644
--- a/arch/ia64/kernel/elfcore.c
+++ b/arch/ia64/kernel/elfcore.c
@@ -7,7 +7,7 @@
#include <asm/elf.h>
-Elf64_Half elf_core_extra_phdrs(void)
+Elf64_Half elf_core_extra_phdrs(struct coredump_params *cprm)
{
return GATE_EHDR->e_phnum;
}
@@ -60,7 +60,7 @@ int elf_core_write_extra_data(struct coredump_params *cprm)
return 1;
}
-size_t elf_core_extra_data_size(void)
+size_t elf_core_extra_data_size(struct coredump_params *cprm)
{
const struct elf_phdr *const gate_phdrs =
(const struct elf_phdr *) (GATE_ADDR + GATE_EHDR->e_phoff);
diff --git a/arch/ia64/kernel/sys_ia64.c b/arch/ia64/kernel/sys_ia64.c
index f6a502e8f02c5b..6e948d015332ab 100644
--- a/arch/ia64/kernel/sys_ia64.c
+++ b/arch/ia64/kernel/sys_ia64.c
@@ -170,6 +170,9 @@ ia64_mremap (unsigned long addr, unsigned long old_len, unsigned long new_len, u
asmlinkage long
ia64_clock_getres(const clockid_t which_clock, struct __kernel_timespec __user *tp)
{
+ struct timespec64 rtn_tp;
+ s64 tick_ns;
+
/*
* ia64's clock_gettime() syscall is implemented as a vdso call
* fsys_clock_gettime(). Currently it handles only
@@ -185,8 +188,8 @@ ia64_clock_getres(const clockid_t which_clock, struct __kernel_timespec __user *
switch (which_clock) {
case CLOCK_REALTIME:
case CLOCK_MONOTONIC:
- s64 tick_ns = DIV_ROUND_UP(NSEC_PER_SEC, local_cpu_data->itc_freq);
- struct timespec64 rtn_tp = ns_to_timespec64(tick_ns);
+ tick_ns = DIV_ROUND_UP(NSEC_PER_SEC, local_cpu_data->itc_freq);
+ rtn_tp = ns_to_timespec64(tick_ns);
return put_timespec64(&rtn_tp, tp);
}
diff --git a/arch/loongarch/include/asm/ftrace.h b/arch/loongarch/include/asm/ftrace.h
index 90f9d3399b2aaf..3418d32d4fc7d2 100644
--- a/arch/loongarch/include/asm/ftrace.h
+++ b/arch/loongarch/include/asm/ftrace.h
@@ -10,8 +10,6 @@
#define FTRACE_REGS_PLT_IDX 1
#define NR_FTRACE_PLTS 2
-#define GRAPH_FAKE_OFFSET (sizeof(struct pt_regs) - offsetof(struct pt_regs, regs[1]))
-
#ifdef CONFIG_FUNCTION_TRACER
#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
index c00e1512d4fa33..7eedd83fd0d721 100644
--- a/arch/loongarch/include/asm/inst.h
+++ b/arch/loongarch/include/asm/inst.h
@@ -377,14 +377,6 @@ static inline bool unsigned_imm_check(unsigned long val, unsigned int bit)
return val < (1UL << bit);
}
-static inline unsigned long sign_extend(unsigned long val, unsigned int idx)
-{
- if (!is_imm_negative(val, idx + 1))
- return ((1UL << idx) - 1) & val;
- else
- return ~((1UL << idx) - 1) | val;
-}
-
#define DEF_EMIT_REG0I26_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
int offset) \
@@ -401,6 +393,7 @@ static inline void emit_##NAME(union loongarch_instruction *insn, \
}
DEF_EMIT_REG0I26_FORMAT(b, b_op)
+DEF_EMIT_REG0I26_FORMAT(bl, bl_op)
#define DEF_EMIT_REG1I20_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
diff --git a/arch/loongarch/include/asm/unwind.h b/arch/loongarch/include/asm/unwind.h
index f2b52b9ea93d28..b9dce87afd2e05 100644
--- a/arch/loongarch/include/asm/unwind.h
+++ b/arch/loongarch/include/asm/unwind.h
@@ -8,7 +8,9 @@
#define _ASM_UNWIND_H
#include <linux/sched.h>
+#include <linux/ftrace.h>
+#include <asm/ptrace.h>
#include <asm/stacktrace.h>
enum unwinder_type {
@@ -20,11 +22,13 @@ struct unwind_state {
char type; /* UNWINDER_XXX */
struct stack_info stack_info;
struct task_struct *task;
- bool first, error, is_ftrace;
+ bool first, error, reset;
int graph_idx;
unsigned long sp, pc, ra;
};
+bool default_next_frame(struct unwind_state *state);
+
void unwind_start(struct unwind_state *state,
struct task_struct *task, struct pt_regs *regs);
bool unwind_next_frame(struct unwind_state *state);
@@ -40,4 +44,39 @@ static inline bool unwind_error(struct unwind_state *state)
return state->error;
}
+#define GRAPH_FAKE_OFFSET (sizeof(struct pt_regs) - offsetof(struct pt_regs, regs[1]))
+
+static inline unsigned long unwind_graph_addr(struct unwind_state *state,
+ unsigned long pc, unsigned long cfa)
+{
+ return ftrace_graph_ret_addr(state->task, &state->graph_idx,
+ pc, (unsigned long *)(cfa - GRAPH_FAKE_OFFSET));
+}
+
+static __always_inline void __unwind_start(struct unwind_state *state,
+ struct task_struct *task, struct pt_regs *regs)
+{
+ memset(state, 0, sizeof(*state));
+ if (regs) {
+ state->sp = regs->regs[3];
+ state->pc = regs->csr_era;
+ state->ra = regs->regs[1];
+ } else if (task && task != current) {
+ state->sp = thread_saved_fp(task);
+ state->pc = thread_saved_ra(task);
+ state->ra = 0;
+ } else {
+ state->sp = (unsigned long)__builtin_frame_address(0);
+ state->pc = (unsigned long)__builtin_return_address(0);
+ state->ra = 0;
+ }
+ state->task = task;
+ get_stack_info(state->sp, state->task, &state->stack_info);
+ state->pc = unwind_graph_addr(state, state->pc, state->sp);
+}
+
+static __always_inline unsigned long __unwind_get_return_address(struct unwind_state *state)
+{
+ return unwind_done(state) ? 0 : state->pc;
+}
#endif /* _ASM_UNWIND_H */
diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile
index fcaa024a685ec6..c8cfbd562921d6 100644
--- a/arch/loongarch/kernel/Makefile
+++ b/arch/loongarch/kernel/Makefile
@@ -8,7 +8,7 @@ extra-y := vmlinux.lds
obj-y += head.o cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \
traps.o irq.o idle.o process.o dma.o mem.o io.o reset.o switch.o \
elf.o syscall.o signal.o time.o topology.o inst.o ptrace.o vdso.o \
- alternative.o unaligned.o
+ alternative.o unaligned.o unwind.o
obj-$(CONFIG_ACPI) += acpi.o
obj-$(CONFIG_EFI) += efi.o
diff --git a/arch/loongarch/kernel/alternative.c b/arch/loongarch/kernel/alternative.c
index c5aebeac960b6e..4ad13847e9626e 100644
--- a/arch/loongarch/kernel/alternative.c
+++ b/arch/loongarch/kernel/alternative.c
@@ -74,7 +74,7 @@ static void __init_or_module recompute_jump(union loongarch_instruction *buf,
switch (src->reg0i26_format.opcode) {
case b_op:
case bl_op:
- jump_addr = cur_pc + sign_extend((si_h << 16 | si_l) << 2, 27);
+ jump_addr = cur_pc + sign_extend64((si_h << 16 | si_l) << 2, 27);
if (in_alt_jump(jump_addr, start, end))
return;
offset = jump_addr - pc;
@@ -93,7 +93,7 @@ static void __init_or_module recompute_jump(union loongarch_instruction *buf,
fallthrough;
case beqz_op:
case bnez_op:
- jump_addr = cur_pc + sign_extend((si_h << 16 | si_l) << 2, 22);
+ jump_addr = cur_pc + sign_extend64((si_h << 16 | si_l) << 2, 22);
if (in_alt_jump(jump_addr, start, end))
return;
offset = jump_addr - pc;
@@ -112,7 +112,7 @@ static void __init_or_module recompute_jump(union loongarch_instruction *buf,
case bge_op:
case bltu_op:
case bgeu_op:
- jump_addr = cur_pc + sign_extend(si << 2, 17);
+ jump_addr = cur_pc + sign_extend64(si << 2, 17);
if (in_alt_jump(jump_addr, start, end))
return;
offset = jump_addr - pc;
diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
index 255a09876ef28d..3a3fce2d784611 100644
--- a/arch/loongarch/kernel/cpu-probe.c
+++ b/arch/loongarch/kernel/cpu-probe.c
@@ -94,7 +94,7 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c)
c->options = LOONGARCH_CPU_CPUCFG | LOONGARCH_CPU_CSR |
LOONGARCH_CPU_TLB | LOONGARCH_CPU_VINT | LOONGARCH_CPU_WATCH;
- elf_hwcap |= HWCAP_LOONGARCH_CRC32;
+ elf_hwcap = HWCAP_LOONGARCH_CPUCFG | HWCAP_LOONGARCH_CRC32;
config = read_cpucfg(LOONGARCH_CPUCFG1);
if (config & CPUCFG1_UAL) {
diff --git a/arch/loongarch/kernel/genex.S b/arch/loongarch/kernel/genex.S
index 75e5be807a0d50..7e5c293ed89f70 100644
--- a/arch/loongarch/kernel/genex.S
+++ b/arch/loongarch/kernel/genex.S
@@ -67,14 +67,17 @@ SYM_FUNC_END(except_vec_cex)
.macro BUILD_HANDLER exception handler prep
.align 5
SYM_FUNC_START(handle_\exception)
+ 666:
BACKUP_T0T1
SAVE_ALL
build_prep_\prep
move a0, sp
la.abs t0, do_\handler
jirl ra, t0, 0
+ 668:
RESTORE_ALL_AND_RET
SYM_FUNC_END(handle_\exception)
+ SYM_DATA(unwind_hint_\exception, .word 668b - 666b)
.endm
BUILD_HANDLER ade ade badv
diff --git a/arch/loongarch/kernel/inst.c b/arch/loongarch/kernel/inst.c
index 512579d79b221c..badc5908704234 100644
--- a/arch/loongarch/kernel/inst.c
+++ b/arch/loongarch/kernel/inst.c
@@ -58,7 +58,6 @@ u32 larch_insn_gen_nop(void)
u32 larch_insn_gen_b(unsigned long pc, unsigned long dest)
{
long offset = dest - pc;
- unsigned int immediate_l, immediate_h;
union loongarch_instruction insn;
if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) {
@@ -66,15 +65,7 @@ u32 larch_insn_gen_b(unsigned long pc, unsigned long dest)
return INSN_BREAK;
}
- offset >>= 2;
-
- immediate_l = offset & 0xffff;
- offset >>= 16;
- immediate_h = offset & 0x3ff;
-
- insn.reg0i26_format.opcode = b_op;
- insn.reg0i26_format.immediate_l = immediate_l;
- insn.reg0i26_format.immediate_h = immediate_h;
+ emit_b(&insn, offset >> 2);
return insn.word;
}
@@ -82,7 +73,6 @@ u32 larch_insn_gen_b(unsigned long pc, unsigned long dest)
u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest)
{
long offset = dest - pc;
- unsigned int immediate_l, immediate_h;
union loongarch_instruction insn;
if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) {
@@ -90,15 +80,7 @@ u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest)
return INSN_BREAK;
}
- offset >>= 2;
-
- immediate_l = offset & 0xffff;
- offset >>= 16;
- immediate_h = offset & 0x3ff;
-
- insn.reg0i26_format.opcode = bl_op;
- insn.reg0i26_format.immediate_l = immediate_l;
- insn.reg0i26_format.immediate_h = immediate_h;
+ emit_bl(&insn, offset >> 2);
return insn.word;
}
@@ -107,10 +89,7 @@ u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj, enum loongar
{
union loongarch_instruction insn;
- insn.reg3_format.opcode = or_op;
- insn.reg3_format.rd = rd;
- insn.reg3_format.rj = rj;
- insn.reg3_format.rk = rk;
+ emit_or(&insn, rd, rj, rk);
return insn.word;
}
@@ -124,9 +103,7 @@ u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm)
{
union loongarch_instruction insn;
- insn.reg1i20_format.opcode = lu12iw_op;
- insn.reg1i20_format.rd = rd;
- insn.reg1i20_format.immediate = imm;
+ emit_lu12iw(&insn, rd, imm);
return insn.word;
}
@@ -135,9 +112,7 @@ u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm)
{
union loongarch_instruction insn;
- insn.reg1i20_format.opcode = lu32id_op;
- insn.reg1i20_format.rd = rd;
- insn.reg1i20_format.immediate = imm;
+ emit_lu32id(&insn, rd, imm);
return insn.word;
}
@@ -146,10 +121,7 @@ u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm)
{
union loongarch_instruction insn;
- insn.reg2i12_format.opcode = lu52id_op;
- insn.reg2i12_format.rd = rd;
- insn.reg2i12_format.rj = rj;
- insn.reg2i12_format.immediate = imm;
+ emit_lu52id(&insn, rd, rj, imm);
return insn.word;
}
@@ -158,10 +130,7 @@ u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned l
{
union loongarch_instruction insn;
- insn.reg2i16_format.opcode = jirl_op;
- insn.reg2i16_format.rd = rd;
- insn.reg2i16_format.rj = rj;
- insn.reg2i16_format.immediate = (dest - pc) >> 2;
+ emit_jirl(&insn, rj, rd, (dest - pc) >> 2);
return insn.word;
}
diff --git a/arch/loongarch/kernel/process.c b/arch/loongarch/kernel/process.c
index c583b1ef1f44ce..edfd220a3737aa 100644
--- a/arch/loongarch/kernel/process.c
+++ b/arch/loongarch/kernel/process.c
@@ -191,20 +191,14 @@ out:
unsigned long __get_wchan(struct task_struct *task)
{
- unsigned long pc;
+ unsigned long pc = 0;
struct unwind_state state;
if (!try_get_task_stack(task))
return 0;
- unwind_start(&state, task, NULL);
- state.sp = thread_saved_fp(task);
- get_stack_info(state.sp, state.task, &state.stack_info);
- state.pc = thread_saved_ra(task);
-#ifdef CONFIG_UNWINDER_PROLOGUE
- state.type = UNWINDER_PROLOGUE;
-#endif
- for (; !unwind_done(&state); unwind_next_frame(&state)) {
+ for (unwind_start(&state, task, NULL);
+ !unwind_done(&state); unwind_next_frame(&state)) {
pc = unwind_get_return_address(&state);
if (!pc)
break;
diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c
index 7ea62faeeadb56..c38a146a973b45 100644
--- a/arch/loongarch/kernel/traps.c
+++ b/arch/loongarch/kernel/traps.c
@@ -72,9 +72,6 @@ static void show_backtrace(struct task_struct *task, const struct pt_regs *regs,
if (!task)
task = current;
- if (user_mode(regs))
- state.type = UNWINDER_GUESS;
-
printk("%sCall Trace:", loglvl);
for (unwind_start(&state, task, pregs);
!unwind_done(&state); unwind_next_frame(&state)) {
diff --git a/arch/loongarch/kernel/unwind.c b/arch/loongarch/kernel/unwind.c
new file mode 100644
index 00000000000000..a463d6961344c0
--- /dev/null
+++ b/arch/loongarch/kernel/unwind.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022-2023 Loongson Technology Corporation Limited
+ */
+#include <linux/kernel.h>
+#include <linux/ftrace.h>
+
+#include <asm/unwind.h>
+
+bool default_next_frame(struct unwind_state *state)
+{
+ struct stack_info *info = &state->stack_info;
+ unsigned long addr;
+
+ if (unwind_done(state))
+ return false;
+
+ do {
+ for (state->sp += sizeof(unsigned long);
+ state->sp < info->end; state->sp += sizeof(unsigned long)) {
+ addr = *(unsigned long *)(state->sp);
+ state->pc = unwind_graph_addr(state, addr, state->sp + 8);
+ if (__kernel_text_address(state->pc))
+ return true;
+ }
+
+ state->sp = info->next_sp;
+
+ } while (!get_stack_info(state->sp, state->task, info));
+
+ return false;
+}
diff --git a/arch/loongarch/kernel/unwind_guess.c b/arch/loongarch/kernel/unwind_guess.c
index e2d2e4f3001f49..98379b7d41475b 100644
--- a/arch/loongarch/kernel/unwind_guess.c
+++ b/arch/loongarch/kernel/unwind_guess.c
@@ -2,37 +2,18 @@
/*
* Copyright (C) 2022 Loongson Technology Corporation Limited
*/
-#include <linux/kernel.h>
-#include <linux/ftrace.h>
-
#include <asm/unwind.h>
unsigned long unwind_get_return_address(struct unwind_state *state)
{
- if (unwind_done(state))
- return 0;
- else if (state->first)
- return state->pc;
-
- return *(unsigned long *)(state->sp);
+ return __unwind_get_return_address(state);
}
EXPORT_SYMBOL_GPL(unwind_get_return_address);
void unwind_start(struct unwind_state *state, struct task_struct *task,
struct pt_regs *regs)
{
- memset(state, 0, sizeof(*state));
-
- if (regs) {
- state->sp = regs->regs[3];
- state->pc = regs->csr_era;
- }
-
- state->task = task;
- state->first = true;
-
- get_stack_info(state->sp, state->task, &state->stack_info);
-
+ __unwind_start(state, task, regs);
if (!unwind_done(state) && !__kernel_text_address(state->pc))
unwind_next_frame(state);
}
@@ -40,30 +21,6 @@ EXPORT_SYMBOL_GPL(unwind_start);
bool unwind_next_frame(struct unwind_state *state)
{
- struct stack_info *info = &state->stack_info;
- unsigned long addr;
-
- if (unwind_done(state))
- return false;
-
- if (state->first)
- state->first = false;
-
- do {
- for (state->sp += sizeof(unsigned long);
- state->sp < info->end;
- state->sp += sizeof(unsigned long)) {
- addr = *(unsigned long *)(state->sp);
- state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
- addr, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
- if (__kernel_text_address(addr))
- return true;
- }
-
- state->sp = info->next_sp;
-
- } while (!get_stack_info(state->sp, state->task, info));
-
- return false;
+ return default_next_frame(state);
}
EXPORT_SYMBOL_GPL(unwind_next_frame);
diff --git a/arch/loongarch/kernel/unwind_prologue.c b/arch/loongarch/kernel/unwind_prologue.c
index 0f8d1451ebb848..9095fde8e55d5c 100644
--- a/arch/loongarch/kernel/unwind_prologue.c
+++ b/arch/loongarch/kernel/unwind_prologue.c
@@ -2,61 +2,116 @@
/*
* Copyright (C) 2022 Loongson Technology Corporation Limited
*/
+#include <linux/cpumask.h>
#include <linux/ftrace.h>
#include <linux/kallsyms.h>
#include <asm/inst.h>
+#include <asm/loongson.h>
#include <asm/ptrace.h>
+#include <asm/setup.h>
#include <asm/unwind.h>
-static inline void unwind_state_fixup(struct unwind_state *state)
-{
-#ifdef CONFIG_DYNAMIC_FTRACE
- static unsigned long ftrace = (unsigned long)ftrace_call + 4;
-
- if (state->pc == ftrace)
- state->is_ftrace = true;
+extern const int unwind_hint_ade;
+extern const int unwind_hint_ale;
+extern const int unwind_hint_bp;
+extern const int unwind_hint_fpe;
+extern const int unwind_hint_fpu;
+extern const int unwind_hint_lsx;
+extern const int unwind_hint_lasx;
+extern const int unwind_hint_lbt;
+extern const int unwind_hint_ri;
+extern const int unwind_hint_watch;
+extern unsigned long eentry;
+#ifdef CONFIG_NUMA
+extern unsigned long pcpu_handlers[NR_CPUS];
#endif
-}
-unsigned long unwind_get_return_address(struct unwind_state *state)
+static inline bool scan_handlers(unsigned long entry_offset)
{
+ int idx, offset;
- if (unwind_done(state))
- return 0;
- else if (state->type)
- return state->pc;
- else if (state->first)
- return state->pc;
-
- return *(unsigned long *)(state->sp);
+ if (entry_offset >= EXCCODE_INT_START * VECSIZE)
+ return false;
+ idx = entry_offset / VECSIZE;
+ offset = entry_offset % VECSIZE;
+ switch (idx) {
+ case EXCCODE_ADE:
+ return offset == unwind_hint_ade;
+ case EXCCODE_ALE:
+ return offset == unwind_hint_ale;
+ case EXCCODE_BP:
+ return offset == unwind_hint_bp;
+ case EXCCODE_FPE:
+ return offset == unwind_hint_fpe;
+ case EXCCODE_FPDIS:
+ return offset == unwind_hint_fpu;
+ case EXCCODE_LSXDIS:
+ return offset == unwind_hint_lsx;
+ case EXCCODE_LASXDIS:
+ return offset == unwind_hint_lasx;
+ case EXCCODE_BTDIS:
+ return offset == unwind_hint_lbt;
+ case EXCCODE_INE:
+ return offset == unwind_hint_ri;
+ case EXCCODE_WATCH:
+ return offset == unwind_hint_watch;
+ default:
+ return false;
+ }
}
-EXPORT_SYMBOL_GPL(unwind_get_return_address);
-static bool unwind_by_guess(struct unwind_state *state)
+static inline bool fix_exception(unsigned long pc)
{
- struct stack_info *info = &state->stack_info;
- unsigned long addr;
-
- for (state->sp += sizeof(unsigned long);
- state->sp < info->end;
- state->sp += sizeof(unsigned long)) {
- addr = *(unsigned long *)(state->sp);
- state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
- addr, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
- if (__kernel_text_address(addr))
+#ifdef CONFIG_NUMA
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ if (!pcpu_handlers[cpu])
+ continue;
+ if (scan_handlers(pc - pcpu_handlers[cpu]))
return true;
}
+#endif
+ return scan_handlers(pc - eentry);
+}
+/*
+ * As we meet ftrace_regs_entry, reset first flag like first doing
+ * tracing. Prologue analysis will stop soon because PC is at entry.
+ */
+static inline bool fix_ftrace(unsigned long pc)
+{
+#ifdef CONFIG_DYNAMIC_FTRACE
+ return pc == (unsigned long)ftrace_call + LOONGARCH_INSN_SIZE;
+#else
return false;
+#endif
}
+static inline bool unwind_state_fixup(struct unwind_state *state)
+{
+ if (!fix_exception(state->pc) && !fix_ftrace(state->pc))
+ return false;
+
+ state->reset = true;
+ return true;
+}
+
+/*
+ * LoongArch function prologue is like follows,
+ * [instructions not use stack var]
+ * addi.d sp, sp, -imm
+ * st.d xx, sp, offset <- save callee saved regs and
+ * st.d yy, sp, offset save ra if function is nest.
+ * [others instructions]
+ */
static bool unwind_by_prologue(struct unwind_state *state)
{
long frame_ra = -1;
unsigned long frame_size = 0;
- unsigned long size, offset, pc = state->pc;
+ unsigned long size, offset, pc;
struct pt_regs *regs;
struct stack_info *info = &state->stack_info;
union loongarch_instruction *ip, *ip_end;
@@ -64,20 +119,21 @@ static bool unwind_by_prologue(struct unwind_state *state)
if (state->sp >= info->end || state->sp < info->begin)
return false;
- if (state->is_ftrace) {
- /*
- * As we meet ftrace_regs_entry, reset first flag like first doing
- * tracing. Prologue analysis will stop soon because PC is at entry.
- */
+ if (state->reset) {
regs = (struct pt_regs *)state->sp;
state->first = true;
- state->is_ftrace = false;
+ state->reset = false;
state->pc = regs->csr_era;
state->ra = regs->regs[1];
state->sp = regs->regs[3];
return true;
}
+ /*
+ * When first is not set, the PC is a return address in the previous frame.
+ * We need to adjust its value in case overflow to the next symbol.
+ */
+ pc = state->pc - (state->first ? 0 : LOONGARCH_INSN_SIZE);
if (!kallsyms_lookup_size_offset(pc, &size, &offset))
return false;
@@ -93,6 +149,10 @@ static bool unwind_by_prologue(struct unwind_state *state)
ip++;
}
+ /*
+ * Can't find stack alloc action, PC may be in a leaf function. Only the
+ * first being true is reasonable, otherwise indicate analysis is broken.
+ */
if (!frame_size) {
if (state->first)
goto first;
@@ -110,6 +170,7 @@ static bool unwind_by_prologue(struct unwind_state *state)
ip++;
}
+ /* Can't find save $ra action, PC may be in a leaf function, too. */
if (frame_ra < 0) {
if (state->first) {
state->sp = state->sp + frame_size;
@@ -118,88 +179,47 @@ static bool unwind_by_prologue(struct unwind_state *state)
return false;
}
- if (state->first)
- state->first = false;
-
state->pc = *(unsigned long *)(state->sp + frame_ra);
state->sp = state->sp + frame_size;
goto out;
first:
- state->first = false;
- if (state->pc == state->ra)
- return false;
-
state->pc = state->ra;
out:
- unwind_state_fixup(state);
- return !!__kernel_text_address(state->pc);
-}
-
-void unwind_start(struct unwind_state *state, struct task_struct *task,
- struct pt_regs *regs)
-{
- memset(state, 0, sizeof(*state));
-
- if (regs && __kernel_text_address(regs->csr_era)) {
- state->pc = regs->csr_era;
- state->sp = regs->regs[3];
- state->ra = regs->regs[1];
- state->type = UNWINDER_PROLOGUE;
- }
-
- state->task = task;
- state->first = true;
-
- get_stack_info(state->sp, state->task, &state->stack_info);
-
- if (!unwind_done(state) && !__kernel_text_address(state->pc))
- unwind_next_frame(state);
+ state->first = false;
+ return unwind_state_fixup(state) || __kernel_text_address(state->pc);
}
-EXPORT_SYMBOL_GPL(unwind_start);
-bool unwind_next_frame(struct unwind_state *state)
+static bool next_frame(struct unwind_state *state)
{
- struct stack_info *info = &state->stack_info;
- struct pt_regs *regs;
unsigned long pc;
+ struct pt_regs *regs;
+ struct stack_info *info = &state->stack_info;
if (unwind_done(state))
return false;
do {
- switch (state->type) {
- case UNWINDER_GUESS:
- state->first = false;
- if (unwind_by_guess(state))
- return true;
- break;
+ if (unwind_by_prologue(state)) {
+ state->pc = unwind_graph_addr(state, state->pc, state->sp);
+ return true;
+ }
+
+ if (info->type == STACK_TYPE_IRQ && info->end == state->sp) {
+ regs = (struct pt_regs *)info->next_sp;
+ pc = regs->csr_era;
+
+ if (user_mode(regs) || !__kernel_text_address(pc))
+ return false;
+
+ state->first = true;
+ state->pc = pc;
+ state->ra = regs->regs[1];
+ state->sp = regs->regs[3];
+ get_stack_info(state->sp, state->task, info);
- case UNWINDER_PROLOGUE:
- if (unwind_by_prologue(state)) {
- state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
- state->pc, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
- return true;
- }
-
- if (info->type == STACK_TYPE_IRQ &&
- info->end == state->sp) {
- regs = (struct pt_regs *)info->next_sp;
- pc = regs->csr_era;
-
- if (user_mode(regs) || !__kernel_text_address(pc))
- return false;
-
- state->first = true;
- state->ra = regs->regs[1];
- state->sp = regs->regs[3];
- state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
- pc, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
- get_stack_info(state->sp, state->task, info);
-
- return true;
- }
+ return true;
}
state->sp = info->next_sp;
@@ -208,4 +228,36 @@ bool unwind_next_frame(struct unwind_state *state)
return false;
}
+
+unsigned long unwind_get_return_address(struct unwind_state *state)
+{
+ return __unwind_get_return_address(state);
+}
+EXPORT_SYMBOL_GPL(unwind_get_return_address);
+
+void unwind_start(struct unwind_state *state, struct task_struct *task,
+ struct pt_regs *regs)
+{
+ __unwind_start(state, task, regs);
+ state->type = UNWINDER_PROLOGUE;
+ state->first = true;
+
+ /*
+ * The current PC is not kernel text address, we cannot find its
+ * relative symbol. Thus, prologue analysis will be broken. Luckily,
+ * we can use the default_next_frame().
+ */
+ if (!__kernel_text_address(state->pc)) {
+ state->type = UNWINDER_GUESS;
+ if (!unwind_done(state))
+ unwind_next_frame(state);
+ }
+}
+EXPORT_SYMBOL_GPL(unwind_start);
+
+bool unwind_next_frame(struct unwind_state *state)
+{
+ return state->type == UNWINDER_PROLOGUE ?
+ next_frame(state) : default_next_frame(state);
+}
EXPORT_SYMBOL_GPL(unwind_next_frame);
diff --git a/arch/loongarch/mm/tlb.c b/arch/loongarch/mm/tlb.c
index da3681f131c8d8..8bad6b0cff59b9 100644
--- a/arch/loongarch/mm/tlb.c
+++ b/arch/loongarch/mm/tlb.c
@@ -251,7 +251,7 @@ static void output_pgtable_bits_defines(void)
}
#ifdef CONFIG_NUMA
-static unsigned long pcpu_handlers[NR_CPUS];
+unsigned long pcpu_handlers[NR_CPUS];
#endif
extern long exception_handlers[VECSIZE * 128 / sizeof(long)];
diff --git a/arch/mips/ralink/of.c b/arch/mips/ralink/of.c
index 01c132bc33d540..4d06de77d92a69 100644
--- a/arch/mips/ralink/of.c
+++ b/arch/mips/ralink/of.c
@@ -64,7 +64,7 @@ void __init plat_mem_setup(void)
dtb = get_fdt();
__dt_setup_arch(dtb);
- if (!early_init_dt_scan_memory())
+ if (early_init_dt_scan_memory())
return;
if (soc_info.mem_detect)
diff --git a/arch/parisc/kernel/firmware.c b/arch/parisc/kernel/firmware.c
index 4dfe1f49c5c8b2..6817892a2c5857 100644
--- a/arch/parisc/kernel/firmware.c
+++ b/arch/parisc/kernel/firmware.c
@@ -1303,7 +1303,7 @@ static char iodc_dbuf[4096] __page_aligned_bss;
*/
int pdc_iodc_print(const unsigned char *str, unsigned count)
{
- unsigned int i;
+ unsigned int i, found = 0;
unsigned long flags;
count = min_t(unsigned int, count, sizeof(iodc_dbuf));
@@ -1315,6 +1315,7 @@ int pdc_iodc_print(const unsigned char *str, unsigned count)
iodc_dbuf[i+0] = '\r';
iodc_dbuf[i+1] = '\n';
i += 2;
+ found = 1;
goto print;
default:
iodc_dbuf[i] = str[i];
@@ -1330,7 +1331,7 @@ print:
__pa(pdc_result), 0, __pa(iodc_dbuf), i, 0);
spin_unlock_irqrestore(&pdc_lock, flags);
- return i;
+ return i - found;
}
#if !defined(BOOTLOADER)
diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c
index 69c62933e9525d..ceb45f51d52e98 100644
--- a/arch/parisc/kernel/ptrace.c
+++ b/arch/parisc/kernel/ptrace.c
@@ -126,6 +126,12 @@ long arch_ptrace(struct task_struct *child, long request,
unsigned long tmp;
long ret = -EIO;
+ unsigned long user_regs_struct_size = sizeof(struct user_regs_struct);
+#ifdef CONFIG_64BIT
+ if (is_compat_task())
+ user_regs_struct_size /= 2;
+#endif
+
switch (request) {
/* Read the word at location addr in the USER area. For ptraced
@@ -166,7 +172,7 @@ long arch_ptrace(struct task_struct *child, long request,
addr >= sizeof(struct pt_regs))
break;
if (addr == PT_IAOQ0 || addr == PT_IAOQ1) {
- data |= 3; /* ensure userspace privilege */
+ data |= PRIV_USER; /* ensure userspace privilege */
}
if ((addr >= PT_GR1 && addr <= PT_GR31) ||
addr == PT_IAOQ0 || addr == PT_IAOQ1 ||
@@ -181,14 +187,14 @@ long arch_ptrace(struct task_struct *child, long request,
return copy_regset_to_user(child,
task_user_regset_view(current),
REGSET_GENERAL,
- 0, sizeof(struct user_regs_struct),
+ 0, user_regs_struct_size,
datap);
case PTRACE_SETREGS: /* Set all gp regs in the child. */
return copy_regset_from_user(child,
task_user_regset_view(current),
REGSET_GENERAL,
- 0, sizeof(struct user_regs_struct),
+ 0, user_regs_struct_size,
datap);
case PTRACE_GETFPREGS: /* Get the child FPU state. */
@@ -285,7 +291,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
if (addr >= sizeof(struct pt_regs))
break;
if (addr == PT_IAOQ0+4 || addr == PT_IAOQ1+4) {
- data |= 3; /* ensure userspace privilege */
+ data |= PRIV_USER; /* ensure userspace privilege */
}
if (addr >= PT_FR0 && addr <= PT_FR31 + 4) {
/* Special case, fp regs are 64 bits anyway */
@@ -302,6 +308,11 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
}
}
break;
+ case PTRACE_GETREGS:
+ case PTRACE_SETREGS:
+ case PTRACE_GETFPREGS:
+ case PTRACE_SETFPREGS:
+ return arch_ptrace(child, request, addr, data);
default:
ret = compat_ptrace_request(child, request, addr, data);
@@ -484,7 +495,7 @@ static void set_reg(struct pt_regs *regs, int num, unsigned long val)
case RI(iaoq[0]):
case RI(iaoq[1]):
/* set 2 lowest bits to ensure userspace privilege: */
- regs->iaoq[num - RI(iaoq[0])] = val | 3;
+ regs->iaoq[num - RI(iaoq[0])] = val | PRIV_USER;
return;
case RI(sar): regs->sar = val;
return;
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index b8c4ac56bddc52..7a5f8dbfbdd033 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -163,7 +163,6 @@ config PPC
select ARCH_WANT_IRQS_OFF_ACTIVATE_MM
select ARCH_WANT_LD_ORPHAN_WARN
select ARCH_WANTS_MODULES_DATA_IN_VMALLOC if PPC_BOOK3S_32 || PPC_8xx
- select ARCH_WANTS_NO_INSTR
select ARCH_WEAK_RELEASE_ACQUIRE
select BINFMT_ELF
select BUILDTIME_TABLE_SORT
diff --git a/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi b/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi
index 74e17e134387d6..27714dc2f04a54 100644
--- a/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi
+++ b/arch/powerpc/boot/dts/fsl/t2081si-post.dtsi
@@ -659,3 +659,19 @@
interrupts = <16 2 1 9>;
};
};
+
+&fman0_rx_0x08 {
+ /delete-property/ fsl,fman-10g-port;
+};
+
+&fman0_tx_0x28 {
+ /delete-property/ fsl,fman-10g-port;
+};
+
+&fman0_rx_0x09 {
+ /delete-property/ fsl,fman-10g-port;
+};
+
+&fman0_tx_0x29 {
+ /delete-property/ fsl,fman-10g-port;
+};
diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper
index af04cea82b941f..352d7de24018fb 100755
--- a/arch/powerpc/boot/wrapper
+++ b/arch/powerpc/boot/wrapper
@@ -210,6 +210,10 @@ ld_version()
gsub(".*version ", "");
gsub("-.*", "");
split($1,a, ".");
+ if( length(a[3]) == "8" )
+ # a[3] is probably a date of format yyyymmdd used for release snapshots. We
+ # can assume it to be zero as it does not signify a new version as such.
+ a[3] = 0;
print a[1]*100000000 + a[2]*1000000 + a[3]*10000;
exit
}'
diff --git a/arch/powerpc/include/asm/book3s/64/tlbflush.h b/arch/powerpc/include/asm/book3s/64/tlbflush.h
index dd39313242b4d9..d5cd16270c5d57 100644
--- a/arch/powerpc/include/asm/book3s/64/tlbflush.h
+++ b/arch/powerpc/include/asm/book3s/64/tlbflush.h
@@ -97,6 +97,8 @@ static inline void tlb_flush(struct mmu_gather *tlb)
{
if (radix_enabled())
radix__tlb_flush(tlb);
+
+ return hash__tlb_flush(tlb);
}
#ifdef CONFIG_SMP
diff --git a/arch/powerpc/include/asm/hw_irq.h b/arch/powerpc/include/asm/hw_irq.h
index 77fa88c2aed0dc..eb6d094083fd68 100644
--- a/arch/powerpc/include/asm/hw_irq.h
+++ b/arch/powerpc/include/asm/hw_irq.h
@@ -173,6 +173,15 @@ static inline notrace unsigned long irq_soft_mask_or_return(unsigned long mask)
return flags;
}
+static inline notrace unsigned long irq_soft_mask_andc_return(unsigned long mask)
+{
+ unsigned long flags = irq_soft_mask_return();
+
+ irq_soft_mask_set(flags & ~mask);
+
+ return flags;
+}
+
static inline unsigned long arch_local_save_flags(void)
{
return irq_soft_mask_return();
@@ -192,7 +201,7 @@ static inline void arch_local_irq_enable(void)
static inline unsigned long arch_local_irq_save(void)
{
- return irq_soft_mask_set_return(IRQS_DISABLED);
+ return irq_soft_mask_or_return(IRQS_DISABLED);
}
static inline bool arch_irqs_disabled_flags(unsigned long flags)
@@ -331,10 +340,11 @@ bool power_pmu_wants_prompt_pmi(void);
* is a different soft-masked interrupt pending that requires hard
* masking.
*/
-static inline bool should_hard_irq_enable(void)
+static inline bool should_hard_irq_enable(struct pt_regs *regs)
{
if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG)) {
- WARN_ON(irq_soft_mask_return() == IRQS_ENABLED);
+ WARN_ON(irq_soft_mask_return() != IRQS_ALL_DISABLED);
+ WARN_ON(!(get_paca()->irq_happened & PACA_IRQ_HARD_DIS));
WARN_ON(mfmsr() & MSR_EE);
}
@@ -347,8 +357,17 @@ static inline bool should_hard_irq_enable(void)
*
* TODO: Add test for 64e
*/
- if (IS_ENABLED(CONFIG_PPC_BOOK3S_64) && !power_pmu_wants_prompt_pmi())
- return false;
+ if (IS_ENABLED(CONFIG_PPC_BOOK3S_64)) {
+ if (!power_pmu_wants_prompt_pmi())
+ return false;
+ /*
+ * If PMIs are disabled then IRQs should be disabled as well,
+ * so we shouldn't see this condition, check for it just in
+ * case because we are about to enable PMIs.
+ */
+ if (WARN_ON_ONCE(regs->softe & IRQS_PMI_DISABLED))
+ return false;
+ }
if (get_paca()->irq_happened & PACA_IRQ_MUST_HARD_MASK)
return false;
@@ -358,18 +377,16 @@ static inline bool should_hard_irq_enable(void)
/*
* Do the hard enabling, only call this if should_hard_irq_enable is true.
+ * This allows PMI interrupts to profile irq handlers.
*/
static inline void do_hard_irq_enable(void)
{
- if (IS_ENABLED(CONFIG_PPC_IRQ_SOFT_MASK_DEBUG)) {
- WARN_ON(irq_soft_mask_return() == IRQS_ENABLED);
- WARN_ON(get_paca()->irq_happened & PACA_IRQ_MUST_HARD_MASK);
- WARN_ON(mfmsr() & MSR_EE);
- }
/*
- * This allows PMI interrupts (and watchdog soft-NMIs) through.
- * There is no other reason to enable this way.
+ * Asynch interrupts come in with IRQS_ALL_DISABLED,
+ * PACA_IRQ_HARD_DIS, and MSR[EE]=0.
*/
+ if (IS_ENABLED(CONFIG_PPC_BOOK3S_64))
+ irq_soft_mask_andc_return(IRQS_PMI_DISABLED);
get_paca()->irq_happened &= ~PACA_IRQ_HARD_DIS;
__hard_irq_enable();
}
@@ -452,7 +469,7 @@ static inline bool arch_irq_disabled_regs(struct pt_regs *regs)
return !(regs->msr & MSR_EE);
}
-static __always_inline bool should_hard_irq_enable(void)
+static __always_inline bool should_hard_irq_enable(struct pt_regs *regs)
{
return false;
}
diff --git a/arch/powerpc/include/asm/imc-pmu.h b/arch/powerpc/include/asm/imc-pmu.h
index 4f897993b71075..699a88584ae16f 100644
--- a/arch/powerpc/include/asm/imc-pmu.h
+++ b/arch/powerpc/include/asm/imc-pmu.h
@@ -137,7 +137,7 @@ struct imc_pmu {
* are inited.
*/
struct imc_pmu_ref {
- struct mutex lock;
+ spinlock_t lock;
unsigned int id;
int refc;
};
diff --git a/arch/powerpc/kernel/dbell.c b/arch/powerpc/kernel/dbell.c
index f55c6fb34a3a03..5712dd846263c5 100644
--- a/arch/powerpc/kernel/dbell.c
+++ b/arch/powerpc/kernel/dbell.c
@@ -27,7 +27,7 @@ DEFINE_INTERRUPT_HANDLER_ASYNC(doorbell_exception)
ppc_msgsync();
- if (should_hard_irq_enable())
+ if (should_hard_irq_enable(regs))
do_hard_irq_enable();
kvmppc_clear_host_ipi(smp_processor_id());
diff --git a/arch/powerpc/kernel/head_85xx.S b/arch/powerpc/kernel/head_85xx.S
index d438ca74e96cc9..fdbee1093e2baa 100644
--- a/arch/powerpc/kernel/head_85xx.S
+++ b/arch/powerpc/kernel/head_85xx.S
@@ -864,7 +864,7 @@ _GLOBAL(load_up_spe)
* SPE unavailable trap from kernel - print a message, but let
* the task use SPE in the kernel until it returns to user mode.
*/
-KernelSPE:
+SYM_FUNC_START_LOCAL(KernelSPE)
lwz r3,_MSR(r1)
oris r3,r3,MSR_SPE@h
stw r3,_MSR(r1) /* enable use of SPE after return */
@@ -881,6 +881,7 @@ KernelSPE:
#endif
.align 4,0
+SYM_FUNC_END(KernelSPE)
#endif /* CONFIG_SPE */
/*
diff --git a/arch/powerpc/kernel/interrupt.c b/arch/powerpc/kernel/interrupt.c
index fc6631a8052720..0ec1581619db5b 100644
--- a/arch/powerpc/kernel/interrupt.c
+++ b/arch/powerpc/kernel/interrupt.c
@@ -50,16 +50,18 @@ static inline bool exit_must_hard_disable(void)
*/
static notrace __always_inline bool prep_irq_for_enabled_exit(bool restartable)
{
+ bool must_hard_disable = (exit_must_hard_disable() || !restartable);
+
/* This must be done with RI=1 because tracing may touch vmaps */
trace_hardirqs_on();
- if (exit_must_hard_disable() || !restartable)
+ if (must_hard_disable)
__hard_EE_RI_disable();
#ifdef CONFIG_PPC64
/* This pattern matches prep_irq_for_idle */
if (unlikely(lazy_irq_pending_nocheck())) {
- if (exit_must_hard_disable() || !restartable) {
+ if (must_hard_disable) {
local_paca->irq_happened |= PACA_IRQ_HARD_DIS;
__hard_RI_enable();
}
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index c5b9ce8874834a..c9535f2760b5b7 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -238,7 +238,7 @@ static void __do_irq(struct pt_regs *regs, unsigned long oldsp)
irq = static_call(ppc_get_irq)();
/* We can hard enable interrupts now to allow perf interrupts */
- if (should_hard_irq_enable())
+ if (should_hard_irq_enable(regs))
do_hard_irq_enable();
/* And finally process it */
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index d68de3618741e1..e26eb6618ae5d2 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -515,7 +515,7 @@ DEFINE_INTERRUPT_HANDLER_ASYNC(timer_interrupt)
}
/* Conditionally hard-enable interrupts. */
- if (should_hard_irq_enable()) {
+ if (should_hard_irq_enable(regs)) {
/*
* Ensure a positive value is written to the decrementer, or
* else some CPUs will continue to take decrementer exceptions.
diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S
index 8c3862b4c259d6..958e77a24f85bb 100644
--- a/arch/powerpc/kernel/vmlinux.lds.S
+++ b/arch/powerpc/kernel/vmlinux.lds.S
@@ -8,6 +8,7 @@
#define BSS_FIRST_SECTIONS *(.bss.prominit)
#define EMITS_PT_NOTE
#define RO_EXCEPTION_TABLE_ALIGN 0
+#define RUNTIME_DISCARD_EXIT
#define SOFT_MASK_TABLE(align) \
. = ALIGN(align); \
@@ -410,9 +411,12 @@ SECTIONS
DISCARDS
/DISCARD/ : {
*(*.EMB.apuinfo)
- *(.glink .iplt .plt .rela* .comment)
+ *(.glink .iplt .plt)
*(.gnu.version*)
*(.gnu.attributes)
*(.eh_frame)
+#ifndef CONFIG_RELOCATABLE
+ *(.rela*)
+#endif
}
}
diff --git a/arch/powerpc/kexec/file_load_64.c b/arch/powerpc/kexec/file_load_64.c
index af8854f9eae3c8..9be3e818a24068 100644
--- a/arch/powerpc/kexec/file_load_64.c
+++ b/arch/powerpc/kexec/file_load_64.c
@@ -26,6 +26,7 @@
#include <asm/firmware.h>
#include <asm/kexec_ranges.h>
#include <asm/crashdump-ppc64.h>
+#include <asm/mmzone.h>
#include <asm/prom.h>
struct umem_info {
@@ -989,10 +990,13 @@ unsigned int kexec_extra_fdt_size_ppc64(struct kimage *image)
* linux,drconf-usable-memory properties. Get an approximate on the
* number of usable memory entries and use for FDT size estimation.
*/
- usm_entries = ((memblock_end_of_DRAM() / drmem_lmb_size()) +
- (2 * (resource_size(&crashk_res) / drmem_lmb_size())));
-
- extra_size = (unsigned int)(usm_entries * sizeof(u64));
+ if (drmem_lmb_size()) {
+ usm_entries = ((memory_hotplug_max() / drmem_lmb_size()) +
+ (2 * (resource_size(&crashk_res) / drmem_lmb_size())));
+ extra_size = (unsigned int)(usm_entries * sizeof(u64));
+ } else {
+ extra_size = 0;
+ }
/*
* Get the number of CPU nodes in the current DT. This allows to
diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
index 0dce93ccaadfa2..e89281d3ba2819 100644
--- a/arch/powerpc/kvm/booke.c
+++ b/arch/powerpc/kvm/booke.c
@@ -912,16 +912,15 @@ static int kvmppc_handle_debug(struct kvm_vcpu *vcpu)
static void kvmppc_fill_pt_regs(struct pt_regs *regs)
{
- ulong r1, ip, msr, lr;
+ ulong r1, msr, lr;
asm("mr %0, 1" : "=r"(r1));
asm("mflr %0" : "=r"(lr));
asm("mfmsr %0" : "=r"(msr));
- asm("bl 1f; 1: mflr %0" : "=r"(ip));
memset(regs, 0, sizeof(*regs));
regs->gpr[1] = r1;
- regs->nip = ip;
+ regs->nip = _THIS_IP_;
regs->msr = msr;
regs->link = lr;
}
diff --git a/arch/powerpc/mm/book3s64/hash_utils.c b/arch/powerpc/mm/book3s64/hash_utils.c
index 80a148c57de813..44a35ed4f68605 100644
--- a/arch/powerpc/mm/book3s64/hash_utils.c
+++ b/arch/powerpc/mm/book3s64/hash_utils.c
@@ -1012,7 +1012,7 @@ static void __init hash_init_partition_table(phys_addr_t hash_table,
void hpt_clear_stress(void);
static struct timer_list stress_hpt_timer;
-void stress_hpt_timer_fn(struct timer_list *timer)
+static void stress_hpt_timer_fn(struct timer_list *timer)
{
int next_cpu;
diff --git a/arch/powerpc/mm/book3s64/radix_pgtable.c b/arch/powerpc/mm/book3s64/radix_pgtable.c
index cac727b0179990..26245aaf12b8bd 100644
--- a/arch/powerpc/mm/book3s64/radix_pgtable.c
+++ b/arch/powerpc/mm/book3s64/radix_pgtable.c
@@ -234,6 +234,14 @@ void radix__mark_rodata_ro(void)
end = (unsigned long)__end_rodata;
radix__change_memory_range(start, end, _PAGE_WRITE);
+
+ for (start = PAGE_OFFSET; start < (unsigned long)_stext; start += PAGE_SIZE) {
+ end = start + PAGE_SIZE;
+ if (overlaps_interrupt_vector_text(start, end))
+ radix__change_memory_range(start, end, _PAGE_WRITE);
+ else
+ break;
+ }
}
void radix__mark_initmem_nx(void)
@@ -262,6 +270,22 @@ print_mapping(unsigned long start, unsigned long end, unsigned long size, bool e
static unsigned long next_boundary(unsigned long addr, unsigned long end)
{
#ifdef CONFIG_STRICT_KERNEL_RWX
+ unsigned long stext_phys;
+
+ stext_phys = __pa_symbol(_stext);
+
+ // Relocatable kernel running at non-zero real address
+ if (stext_phys != 0) {
+ // The end of interrupts code at zero is a rodata boundary
+ unsigned long end_intr = __pa_symbol(__end_interrupts) - stext_phys;
+ if (addr < end_intr)
+ return end_intr;
+
+ // Start of relocated kernel text is a rodata boundary
+ if (addr < stext_phys)
+ return stext_phys;
+ }
+
if (addr < __pa_symbol(__srwx_boundary))
return __pa_symbol(__srwx_boundary);
#endif
diff --git a/arch/powerpc/perf/imc-pmu.c b/arch/powerpc/perf/imc-pmu.c
index d517aba94d1bc4..9d229ef7f86efa 100644
--- a/arch/powerpc/perf/imc-pmu.c
+++ b/arch/powerpc/perf/imc-pmu.c
@@ -14,6 +14,7 @@
#include <asm/cputhreads.h>
#include <asm/smp.h>
#include <linux/string.h>
+#include <linux/spinlock.h>
/* Nest IMC data structures and variables */
@@ -50,7 +51,7 @@ static int trace_imc_mem_size;
* core and trace-imc
*/
static struct imc_pmu_ref imc_global_refc = {
- .lock = __MUTEX_INITIALIZER(imc_global_refc.lock),
+ .lock = __SPIN_LOCK_INITIALIZER(imc_global_refc.lock),
.id = 0,
.refc = 0,
};
@@ -400,7 +401,7 @@ static int ppc_nest_imc_cpu_offline(unsigned int cpu)
get_hard_smp_processor_id(cpu));
/*
* If this is the last cpu in this chip then, skip the reference
- * count mutex lock and make the reference count on this chip zero.
+ * count lock and make the reference count on this chip zero.
*/
ref = get_nest_pmu_ref(cpu);
if (!ref)
@@ -462,15 +463,15 @@ static void nest_imc_counters_release(struct perf_event *event)
/*
* See if we need to disable the nest PMU.
* If no events are currently in use, then we have to take a
- * mutex to ensure that we don't race with another task doing
+ * lock to ensure that we don't race with another task doing
* enable or disable the nest counters.
*/
ref = get_nest_pmu_ref(event->cpu);
if (!ref)
return;
- /* Take the mutex lock for this node and then decrement the reference count */
- mutex_lock(&ref->lock);
+ /* Take the lock for this node and then decrement the reference count */
+ spin_lock(&ref->lock);
if (ref->refc == 0) {
/*
* The scenario where this is true is, when perf session is
@@ -482,7 +483,7 @@ static void nest_imc_counters_release(struct perf_event *event)
* an OPAL call to disable the engine in that node.
*
*/
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
return;
}
ref->refc--;
@@ -490,7 +491,7 @@ static void nest_imc_counters_release(struct perf_event *event)
rc = opal_imc_counters_stop(OPAL_IMC_COUNTERS_NEST,
get_hard_smp_processor_id(event->cpu));
if (rc) {
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
pr_err("nest-imc: Unable to stop the counters for core %d\n", node_id);
return;
}
@@ -498,7 +499,7 @@ static void nest_imc_counters_release(struct perf_event *event)
WARN(1, "nest-imc: Invalid event reference count\n");
ref->refc = 0;
}
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
}
static int nest_imc_event_init(struct perf_event *event)
@@ -557,26 +558,25 @@ static int nest_imc_event_init(struct perf_event *event)
/*
* Get the imc_pmu_ref struct for this node.
- * Take the mutex lock and then increment the count of nest pmu events
- * inited.
+ * Take the lock and then increment the count of nest pmu events inited.
*/
ref = get_nest_pmu_ref(event->cpu);
if (!ref)
return -EINVAL;
- mutex_lock(&ref->lock);
+ spin_lock(&ref->lock);
if (ref->refc == 0) {
rc = opal_imc_counters_start(OPAL_IMC_COUNTERS_NEST,
get_hard_smp_processor_id(event->cpu));
if (rc) {
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
pr_err("nest-imc: Unable to start the counters for node %d\n",
node_id);
return rc;
}
}
++ref->refc;
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
event->destroy = nest_imc_counters_release;
return 0;
@@ -612,9 +612,8 @@ static int core_imc_mem_init(int cpu, int size)
return -ENOMEM;
mem_info->vbase = page_address(page);
- /* Init the mutex */
core_imc_refc[core_id].id = core_id;
- mutex_init(&core_imc_refc[core_id].lock);
+ spin_lock_init(&core_imc_refc[core_id].lock);
rc = opal_imc_counters_init(OPAL_IMC_COUNTERS_CORE,
__pa((void *)mem_info->vbase),
@@ -703,9 +702,8 @@ static int ppc_core_imc_cpu_offline(unsigned int cpu)
perf_pmu_migrate_context(&core_imc_pmu->pmu, cpu, ncpu);
} else {
/*
- * If this is the last cpu in this core then, skip taking refernce
- * count mutex lock for this core and directly zero "refc" for
- * this core.
+ * If this is the last cpu in this core then skip taking reference
+ * count lock for this core and directly zero "refc" for this core.
*/
opal_imc_counters_stop(OPAL_IMC_COUNTERS_CORE,
get_hard_smp_processor_id(cpu));
@@ -720,11 +718,11 @@ static int ppc_core_imc_cpu_offline(unsigned int cpu)
* last cpu in this core and core-imc event running
* in this cpu.
*/
- mutex_lock(&imc_global_refc.lock);
+ spin_lock(&imc_global_refc.lock);
if (imc_global_refc.id == IMC_DOMAIN_CORE)
imc_global_refc.refc--;
- mutex_unlock(&imc_global_refc.lock);
+ spin_unlock(&imc_global_refc.lock);
}
return 0;
}
@@ -739,7 +737,7 @@ static int core_imc_pmu_cpumask_init(void)
static void reset_global_refc(struct perf_event *event)
{
- mutex_lock(&imc_global_refc.lock);
+ spin_lock(&imc_global_refc.lock);
imc_global_refc.refc--;
/*
@@ -751,7 +749,7 @@ static void reset_global_refc(struct perf_event *event)
imc_global_refc.refc = 0;
imc_global_refc.id = 0;
}
- mutex_unlock(&imc_global_refc.lock);
+ spin_unlock(&imc_global_refc.lock);
}
static void core_imc_counters_release(struct perf_event *event)
@@ -764,17 +762,17 @@ static void core_imc_counters_release(struct perf_event *event)
/*
* See if we need to disable the IMC PMU.
* If no events are currently in use, then we have to take a
- * mutex to ensure that we don't race with another task doing
+ * lock to ensure that we don't race with another task doing
* enable or disable the core counters.
*/
core_id = event->cpu / threads_per_core;
- /* Take the mutex lock and decrement the refernce count for this core */
+ /* Take the lock and decrement the refernce count for this core */
ref = &core_imc_refc[core_id];
if (!ref)
return;
- mutex_lock(&ref->lock);
+ spin_lock(&ref->lock);
if (ref->refc == 0) {
/*
* The scenario where this is true is, when perf session is
@@ -786,7 +784,7 @@ static void core_imc_counters_release(struct perf_event *event)
* an OPAL call to disable the engine in that core.
*
*/
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
return;
}
ref->refc--;
@@ -794,7 +792,7 @@ static void core_imc_counters_release(struct perf_event *event)
rc = opal_imc_counters_stop(OPAL_IMC_COUNTERS_CORE,
get_hard_smp_processor_id(event->cpu));
if (rc) {
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
pr_err("IMC: Unable to stop the counters for core %d\n", core_id);
return;
}
@@ -802,7 +800,7 @@ static void core_imc_counters_release(struct perf_event *event)
WARN(1, "core-imc: Invalid event reference count\n");
ref->refc = 0;
}
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
reset_global_refc(event);
}
@@ -840,7 +838,6 @@ static int core_imc_event_init(struct perf_event *event)
if ((!pcmi->vbase))
return -ENODEV;
- /* Get the core_imc mutex for this core */
ref = &core_imc_refc[core_id];
if (!ref)
return -EINVAL;
@@ -848,22 +845,22 @@ static int core_imc_event_init(struct perf_event *event)
/*
* Core pmu units are enabled only when it is used.
* See if this is triggered for the first time.
- * If yes, take the mutex lock and enable the core counters.
+ * If yes, take the lock and enable the core counters.
* If not, just increment the count in core_imc_refc struct.
*/
- mutex_lock(&ref->lock);
+ spin_lock(&ref->lock);
if (ref->refc == 0) {
rc = opal_imc_counters_start(OPAL_IMC_COUNTERS_CORE,
get_hard_smp_processor_id(event->cpu));
if (rc) {
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
pr_err("core-imc: Unable to start the counters for core %d\n",
core_id);
return rc;
}
}
++ref->refc;
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
/*
* Since the system can run either in accumulation or trace-mode
@@ -874,7 +871,7 @@ static int core_imc_event_init(struct perf_event *event)
* to know whether any other trace/thread imc
* events are running.
*/
- mutex_lock(&imc_global_refc.lock);
+ spin_lock(&imc_global_refc.lock);
if (imc_global_refc.id == 0 || imc_global_refc.id == IMC_DOMAIN_CORE) {
/*
* No other trace/thread imc events are running in
@@ -883,10 +880,10 @@ static int core_imc_event_init(struct perf_event *event)
imc_global_refc.id = IMC_DOMAIN_CORE;
imc_global_refc.refc++;
} else {
- mutex_unlock(&imc_global_refc.lock);
+ spin_unlock(&imc_global_refc.lock);
return -EBUSY;
}
- mutex_unlock(&imc_global_refc.lock);
+ spin_unlock(&imc_global_refc.lock);
event->hw.event_base = (u64)pcmi->vbase + (config & IMC_EVENT_OFFSET_MASK);
event->destroy = core_imc_counters_release;
@@ -958,10 +955,10 @@ static int ppc_thread_imc_cpu_offline(unsigned int cpu)
mtspr(SPRN_LDBAR, (mfspr(SPRN_LDBAR) & (~(1UL << 63))));
/* Reduce the refc if thread-imc event running on this cpu */
- mutex_lock(&imc_global_refc.lock);
+ spin_lock(&imc_global_refc.lock);
if (imc_global_refc.id == IMC_DOMAIN_THREAD)
imc_global_refc.refc--;
- mutex_unlock(&imc_global_refc.lock);
+ spin_unlock(&imc_global_refc.lock);
return 0;
}
@@ -1001,7 +998,7 @@ static int thread_imc_event_init(struct perf_event *event)
if (!target)
return -EINVAL;
- mutex_lock(&imc_global_refc.lock);
+ spin_lock(&imc_global_refc.lock);
/*
* Check if any other trace/core imc events are running in the
* system, if not set the global id to thread-imc.
@@ -1010,10 +1007,10 @@ static int thread_imc_event_init(struct perf_event *event)
imc_global_refc.id = IMC_DOMAIN_THREAD;
imc_global_refc.refc++;
} else {
- mutex_unlock(&imc_global_refc.lock);
+ spin_unlock(&imc_global_refc.lock);
return -EBUSY;
}
- mutex_unlock(&imc_global_refc.lock);
+ spin_unlock(&imc_global_refc.lock);
event->pmu->task_ctx_nr = perf_sw_context;
event->destroy = reset_global_refc;
@@ -1135,25 +1132,25 @@ static int thread_imc_event_add(struct perf_event *event, int flags)
/*
* imc pmus are enabled only when it is used.
* See if this is triggered for the first time.
- * If yes, take the mutex lock and enable the counters.
+ * If yes, take the lock and enable the counters.
* If not, just increment the count in ref count struct.
*/
ref = &core_imc_refc[core_id];
if (!ref)
return -EINVAL;
- mutex_lock(&ref->lock);
+ spin_lock(&ref->lock);
if (ref->refc == 0) {
if (opal_imc_counters_start(OPAL_IMC_COUNTERS_CORE,
get_hard_smp_processor_id(smp_processor_id()))) {
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
pr_err("thread-imc: Unable to start the counter\
for core %d\n", core_id);
return -EINVAL;
}
}
++ref->refc;
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
return 0;
}
@@ -1170,12 +1167,12 @@ static void thread_imc_event_del(struct perf_event *event, int flags)
return;
}
- mutex_lock(&ref->lock);
+ spin_lock(&ref->lock);
ref->refc--;
if (ref->refc == 0) {
if (opal_imc_counters_stop(OPAL_IMC_COUNTERS_CORE,
get_hard_smp_processor_id(smp_processor_id()))) {
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
pr_err("thread-imc: Unable to stop the counters\
for core %d\n", core_id);
return;
@@ -1183,7 +1180,7 @@ static void thread_imc_event_del(struct perf_event *event, int flags)
} else if (ref->refc < 0) {
ref->refc = 0;
}
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
/* Set bit 0 of LDBAR to zero, to stop posting updates to memory */
mtspr(SPRN_LDBAR, (mfspr(SPRN_LDBAR) & (~(1UL << 63))));
@@ -1224,9 +1221,8 @@ static int trace_imc_mem_alloc(int cpu_id, int size)
}
}
- /* Init the mutex, if not already */
trace_imc_refc[core_id].id = core_id;
- mutex_init(&trace_imc_refc[core_id].lock);
+ spin_lock_init(&trace_imc_refc[core_id].lock);
mtspr(SPRN_LDBAR, 0);
return 0;
@@ -1246,10 +1242,10 @@ static int ppc_trace_imc_cpu_offline(unsigned int cpu)
* Reduce the refc if any trace-imc event running
* on this cpu.
*/
- mutex_lock(&imc_global_refc.lock);
+ spin_lock(&imc_global_refc.lock);
if (imc_global_refc.id == IMC_DOMAIN_TRACE)
imc_global_refc.refc--;
- mutex_unlock(&imc_global_refc.lock);
+ spin_unlock(&imc_global_refc.lock);
return 0;
}
@@ -1371,17 +1367,17 @@ static int trace_imc_event_add(struct perf_event *event, int flags)
}
mtspr(SPRN_LDBAR, ldbar_value);
- mutex_lock(&ref->lock);
+ spin_lock(&ref->lock);
if (ref->refc == 0) {
if (opal_imc_counters_start(OPAL_IMC_COUNTERS_TRACE,
get_hard_smp_processor_id(smp_processor_id()))) {
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
pr_err("trace-imc: Unable to start the counters for core %d\n", core_id);
return -EINVAL;
}
}
++ref->refc;
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
return 0;
}
@@ -1414,19 +1410,19 @@ static void trace_imc_event_del(struct perf_event *event, int flags)
return;
}
- mutex_lock(&ref->lock);
+ spin_lock(&ref->lock);
ref->refc--;
if (ref->refc == 0) {
if (opal_imc_counters_stop(OPAL_IMC_COUNTERS_TRACE,
get_hard_smp_processor_id(smp_processor_id()))) {
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
pr_err("trace-imc: Unable to stop the counters for core %d\n", core_id);
return;
}
} else if (ref->refc < 0) {
ref->refc = 0;
}
- mutex_unlock(&ref->lock);
+ spin_unlock(&ref->lock);
trace_imc_event_stop(event, flags);
}
@@ -1448,7 +1444,7 @@ static int trace_imc_event_init(struct perf_event *event)
* no other thread is running any core/thread imc
* events
*/
- mutex_lock(&imc_global_refc.lock);
+ spin_lock(&imc_global_refc.lock);
if (imc_global_refc.id == 0 || imc_global_refc.id == IMC_DOMAIN_TRACE) {
/*
* No core/thread imc events are running in the
@@ -1457,10 +1453,10 @@ static int trace_imc_event_init(struct perf_event *event)
imc_global_refc.id = IMC_DOMAIN_TRACE;
imc_global_refc.refc++;
} else {
- mutex_unlock(&imc_global_refc.lock);
+ spin_unlock(&imc_global_refc.lock);
return -EBUSY;
}
- mutex_unlock(&imc_global_refc.lock);
+ spin_unlock(&imc_global_refc.lock);
event->hw.idx = -1;
@@ -1533,10 +1529,10 @@ static int init_nest_pmu_ref(void)
i = 0;
for_each_node(nid) {
/*
- * Mutex lock to avoid races while tracking the number of
+ * Take the lock to avoid races while tracking the number of
* sessions using the chip's nest pmu units.
*/
- mutex_init(&nest_imc_refc[i].lock);
+ spin_lock_init(&nest_imc_refc[i].lock);
/*
* Loop to init the "id" with the node_id. Variable "i" initialized to
diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
index faf2c2177094bb..82153960ac001e 100644
--- a/arch/riscv/Makefile
+++ b/arch/riscv/Makefile
@@ -80,6 +80,9 @@ ifeq ($(CONFIG_PERF_EVENTS),y)
KBUILD_CFLAGS += -fno-omit-frame-pointer
endif
+# Avoid generating .eh_frame sections.
+KBUILD_CFLAGS += -fno-asynchronous-unwind-tables -fno-unwind-tables
+
KBUILD_CFLAGS_MODULE += $(call cc-option,-mno-relax)
KBUILD_AFLAGS_MODULE += $(call as-option,-Wa$(comma)-mno-relax)
diff --git a/arch/riscv/boot/dts/sifive/fu740-c000.dtsi b/arch/riscv/boot/dts/sifive/fu740-c000.dtsi
index 43bed6c0a84fe8..5235fd1c9cb677 100644
--- a/arch/riscv/boot/dts/sifive/fu740-c000.dtsi
+++ b/arch/riscv/boot/dts/sifive/fu740-c000.dtsi
@@ -328,7 +328,7 @@
bus-range = <0x0 0xff>;
ranges = <0x81000000 0x0 0x60080000 0x0 0x60080000 0x0 0x10000>, /* I/O */
<0x82000000 0x0 0x60090000 0x0 0x60090000 0x0 0xff70000>, /* mem */
- <0x82000000 0x0 0x70000000 0x0 0x70000000 0x0 0x1000000>, /* mem */
+ <0x82000000 0x0 0x70000000 0x0 0x70000000 0x0 0x10000000>, /* mem */
<0xc3000000 0x20 0x00000000 0x20 0x00000000 0x20 0x00000000>; /* mem prefetchable */
num-lanes = <0x8>;
interrupts = <56>, <57>, <58>, <59>, <60>, <61>, <62>, <63>, <64>;
diff --git a/arch/riscv/include/asm/alternative-macros.h b/arch/riscv/include/asm/alternative-macros.h
index 7226e2462584b6..2c0f4c887289a5 100644
--- a/arch/riscv/include/asm/alternative-macros.h
+++ b/arch/riscv/include/asm/alternative-macros.h
@@ -46,7 +46,7 @@
.macro ALTERNATIVE_CFG_2 old_c, new_c_1, vendor_id_1, errata_id_1, enable_1, \
new_c_2, vendor_id_2, errata_id_2, enable_2
- ALTERNATIVE_CFG \old_c, \new_c_1, \vendor_id_1, \errata_id_1, \enable_1
+ ALTERNATIVE_CFG "\old_c", "\new_c_1", \vendor_id_1, \errata_id_1, \enable_1
ALT_NEW_CONTENT \vendor_id_2, \errata_id_2, \enable_2, \new_c_2
.endm
diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
index 86328e3acb02ee..64ad1937e71494 100644
--- a/arch/riscv/include/asm/hwcap.h
+++ b/arch/riscv/include/asm/hwcap.h
@@ -70,7 +70,6 @@ static_assert(RISCV_ISA_EXT_ID_MAX <= RISCV_ISA_EXT_MAX);
*/
enum riscv_isa_ext_key {
RISCV_ISA_EXT_KEY_FPU, /* For 'F' and 'D' */
- RISCV_ISA_EXT_KEY_ZIHINTPAUSE,
RISCV_ISA_EXT_KEY_SVINVAL,
RISCV_ISA_EXT_KEY_MAX,
};
@@ -91,8 +90,6 @@ static __always_inline int riscv_isa_ext2key(int num)
return RISCV_ISA_EXT_KEY_FPU;
case RISCV_ISA_EXT_d:
return RISCV_ISA_EXT_KEY_FPU;
- case RISCV_ISA_EXT_ZIHINTPAUSE:
- return RISCV_ISA_EXT_KEY_ZIHINTPAUSE;
case RISCV_ISA_EXT_SVINVAL:
return RISCV_ISA_EXT_KEY_SVINVAL;
default:
diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
index 4eba9a98d0e3d6..3e01f4f3ab08aa 100644
--- a/arch/riscv/include/asm/pgtable.h
+++ b/arch/riscv/include/asm/pgtable.h
@@ -721,6 +721,10 @@ static inline pmd_t pmdp_establish(struct vm_area_struct *vma,
page_table_check_pmd_set(vma->vm_mm, address, pmdp, pmd);
return __pmd(atomic_long_xchg((atomic_long_t *)pmdp, pmd_val(pmd)));
}
+
+#define pmdp_collapse_flush pmdp_collapse_flush
+extern pmd_t pmdp_collapse_flush(struct vm_area_struct *vma,
+ unsigned long address, pmd_t *pmdp);
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
/*
diff --git a/arch/riscv/include/asm/uaccess.h b/arch/riscv/include/asm/uaccess.h
index 855450bed9f52c..ec0cab9fbddd0d 100644
--- a/arch/riscv/include/asm/uaccess.h
+++ b/arch/riscv/include/asm/uaccess.h
@@ -165,7 +165,7 @@ do { \
might_fault(); \
access_ok(__p, sizeof(*__p)) ? \
__get_user((x), __p) : \
- ((x) = 0, -EFAULT); \
+ ((x) = (__force __typeof__(x))0, -EFAULT); \
})
#define __put_user_asm(insn, x, ptr, err) \
diff --git a/arch/riscv/include/asm/vdso/processor.h b/arch/riscv/include/asm/vdso/processor.h
index fa70cfe507aa11..14f5d27783b858 100644
--- a/arch/riscv/include/asm/vdso/processor.h
+++ b/arch/riscv/include/asm/vdso/processor.h
@@ -4,30 +4,26 @@
#ifndef __ASSEMBLY__
-#include <linux/jump_label.h>
#include <asm/barrier.h>
-#include <asm/hwcap.h>
static inline void cpu_relax(void)
{
- if (!static_branch_likely(&riscv_isa_ext_keys[RISCV_ISA_EXT_KEY_ZIHINTPAUSE])) {
#ifdef __riscv_muldiv
- int dummy;
- /* In lieu of a halt instruction, induce a long-latency stall. */
- __asm__ __volatile__ ("div %0, %0, zero" : "=r" (dummy));
+ int dummy;
+ /* In lieu of a halt instruction, induce a long-latency stall. */
+ __asm__ __volatile__ ("div %0, %0, zero" : "=r" (dummy));
#endif
- } else {
- /*
- * Reduce instruction retirement.
- * This assumes the PC changes.
- */
-#ifdef CONFIG_TOOLCHAIN_HAS_ZIHINTPAUSE
- __asm__ __volatile__ ("pause");
+
+#ifdef __riscv_zihintpause
+ /*
+ * Reduce instruction retirement.
+ * This assumes the PC changes.
+ */
+ __asm__ __volatile__ ("pause");
#else
- /* Encoding of the pause instruction */
- __asm__ __volatile__ (".4byte 0x100000F");
+ /* Encoding of the pause instruction */
+ __asm__ __volatile__ (".4byte 0x100000F");
#endif
- }
barrier();
}
diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S
index b865046e4dbbc4..4bf6c449d78b6a 100644
--- a/arch/riscv/kernel/head.S
+++ b/arch/riscv/kernel/head.S
@@ -326,7 +326,7 @@ clear_bss_done:
call soc_early_init
tail start_kernel
-#if CONFIG_RISCV_BOOT_SPINWAIT
+#ifdef CONFIG_RISCV_BOOT_SPINWAIT
.Lsecondary_start:
/* Set trap vector to spin forever to help debug */
la a3, .Lsecondary_park
diff --git a/arch/riscv/kernel/probes/kprobes.c b/arch/riscv/kernel/probes/kprobes.c
index f21592d203062a..2bedec37d092a6 100644
--- a/arch/riscv/kernel/probes/kprobes.c
+++ b/arch/riscv/kernel/probes/kprobes.c
@@ -48,15 +48,35 @@ static void __kprobes arch_simulate_insn(struct kprobe *p, struct pt_regs *regs)
post_kprobe_handler(p, kcb, regs);
}
+static bool __kprobes arch_check_kprobe(struct kprobe *p)
+{
+ unsigned long tmp = (unsigned long)p->addr - p->offset;
+ unsigned long addr = (unsigned long)p->addr;
+
+ while (tmp <= addr) {
+ if (tmp == addr)
+ return true;
+
+ tmp += GET_INSN_LENGTH(*(u16 *)tmp);
+ }
+
+ return false;
+}
+
int __kprobes arch_prepare_kprobe(struct kprobe *p)
{
- unsigned long probe_addr = (unsigned long)p->addr;
+ u16 *insn = (u16 *)p->addr;
+
+ if ((unsigned long)insn & 0x1)
+ return -EILSEQ;
- if (probe_addr & 0x1)
+ if (!arch_check_kprobe(p))
return -EILSEQ;
/* copy instruction */
- p->opcode = *p->addr;
+ p->opcode = (kprobe_opcode_t)(*insn++);
+ if (GET_INSN_LENGTH(p->opcode) == 4)
+ p->opcode |= (kprobe_opcode_t)(*insn) << 16;
/* decode instruction */
switch (riscv_probe_decode_insn(p->addr, &p->ainsn.api)) {
diff --git a/arch/riscv/kernel/probes/simulate-insn.c b/arch/riscv/kernel/probes/simulate-insn.c
index d73e96f6ed7c59..a20568bd1f1a88 100644
--- a/arch/riscv/kernel/probes/simulate-insn.c
+++ b/arch/riscv/kernel/probes/simulate-insn.c
@@ -71,11 +71,11 @@ bool __kprobes simulate_jalr(u32 opcode, unsigned long addr, struct pt_regs *reg
u32 rd_index = (opcode >> 7) & 0x1f;
u32 rs1_index = (opcode >> 15) & 0x1f;
- ret = rv_insn_reg_set_val(regs, rd_index, addr + 4);
+ ret = rv_insn_reg_get_val(regs, rs1_index, &base_addr);
if (!ret)
return ret;
- ret = rv_insn_reg_get_val(regs, rs1_index, &base_addr);
+ ret = rv_insn_reg_set_val(regs, rd_index, addr + 4);
if (!ret)
return ret;
diff --git a/arch/riscv/kernel/probes/simulate-insn.h b/arch/riscv/kernel/probes/simulate-insn.h
index cb6ff7dccb92e1..de8474146a9b6e 100644
--- a/arch/riscv/kernel/probes/simulate-insn.h
+++ b/arch/riscv/kernel/probes/simulate-insn.h
@@ -31,9 +31,9 @@ __RISCV_INSN_FUNCS(fence, 0x7f, 0x0f);
} while (0)
__RISCV_INSN_FUNCS(c_j, 0xe003, 0xa001);
-__RISCV_INSN_FUNCS(c_jr, 0xf007, 0x8002);
+__RISCV_INSN_FUNCS(c_jr, 0xf07f, 0x8002);
__RISCV_INSN_FUNCS(c_jal, 0xe003, 0x2001);
-__RISCV_INSN_FUNCS(c_jalr, 0xf007, 0x9002);
+__RISCV_INSN_FUNCS(c_jalr, 0xf07f, 0x9002);
__RISCV_INSN_FUNCS(c_beqz, 0xe003, 0xc001);
__RISCV_INSN_FUNCS(c_bnez, 0xe003, 0xe001);
__RISCV_INSN_FUNCS(c_ebreak, 0xffff, 0x9002);
diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c
index 3373df413c8820..ddb2afba6d2555 100644
--- a/arch/riscv/kernel/smpboot.c
+++ b/arch/riscv/kernel/smpboot.c
@@ -39,7 +39,6 @@ static DECLARE_COMPLETION(cpu_running);
void __init smp_prepare_boot_cpu(void)
{
- init_cpu_topology();
}
void __init smp_prepare_cpus(unsigned int max_cpus)
@@ -48,6 +47,8 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
int ret;
unsigned int curr_cpuid;
+ init_cpu_topology();
+
curr_cpuid = smp_processor_id();
store_cpu_topology(curr_cpuid);
numa_store_cpu_info(curr_cpuid);
diff --git a/arch/riscv/kernel/stacktrace.c b/arch/riscv/kernel/stacktrace.c
index 75c8dd64fc48e0..f9a5a7c90ff093 100644
--- a/arch/riscv/kernel/stacktrace.c
+++ b/arch/riscv/kernel/stacktrace.c
@@ -32,6 +32,7 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
fp = (unsigned long)__builtin_frame_address(0);
sp = current_stack_pointer;
pc = (unsigned long)walk_stackframe;
+ level = -1;
} else {
/* task blocked in __switch_to */
fp = task->thread.s[0];
@@ -43,7 +44,7 @@ void notrace walk_stackframe(struct task_struct *task, struct pt_regs *regs,
unsigned long low, high;
struct stackframe *frame;
- if (unlikely(!__kernel_text_address(pc) || (level++ >= 1 && !fn(arg, pc))))
+ if (unlikely(!__kernel_text_address(pc) || (level++ >= 0 && !fn(arg, pc))))
break;
/* Validate frame pointer */
diff --git a/arch/riscv/mm/cacheflush.c b/arch/riscv/mm/cacheflush.c
index 3cc07ed45aeb3c..fcd6145fbeadce 100644
--- a/arch/riscv/mm/cacheflush.c
+++ b/arch/riscv/mm/cacheflush.c
@@ -90,8 +90,10 @@ void flush_icache_pte(pte_t pte)
if (PageHuge(page))
page = compound_head(page);
- if (!test_and_set_bit(PG_dcache_clean, &page->flags))
+ if (!test_bit(PG_dcache_clean, &page->flags)) {
flush_icache_all();
+ set_bit(PG_dcache_clean, &page->flags);
+ }
}
#endif /* CONFIG_MMU */
diff --git a/arch/riscv/mm/pgtable.c b/arch/riscv/mm/pgtable.c
index 6645ead1a7c16d..fef4e7328e4905 100644
--- a/arch/riscv/mm/pgtable.c
+++ b/arch/riscv/mm/pgtable.c
@@ -81,3 +81,23 @@ int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)
}
#endif /* CONFIG_HAVE_ARCH_HUGE_VMAP */
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+pmd_t pmdp_collapse_flush(struct vm_area_struct *vma,
+ unsigned long address, pmd_t *pmdp)
+{
+ pmd_t pmd = pmdp_huge_get_and_clear(vma->vm_mm, address, pmdp);
+
+ VM_BUG_ON(address & ~HPAGE_PMD_MASK);
+ VM_BUG_ON(pmd_trans_huge(*pmdp));
+ /*
+ * When leaf PTE entries (regular pages) are collapsed into a leaf
+ * PMD entry (huge page), a valid non-leaf PTE is converted into a
+ * valid leaf PTE at the level 1 page table. Since the sfence.vma
+ * forms that specify an address only apply to leaf PTEs, we need a
+ * global flush here. collapse_huge_page() assumes these flushes are
+ * eager, so just do the fence here.
+ */
+ flush_tlb_mm(vma->vm_mm);
+ return pmd;
+}
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
diff --git a/arch/s390/boot/decompressor.c b/arch/s390/boot/decompressor.c
index e27c2140d62068..b519a1f045d8f2 100644
--- a/arch/s390/boot/decompressor.c
+++ b/arch/s390/boot/decompressor.c
@@ -23,9 +23,9 @@
#define memmove memmove
#define memzero(s, n) memset((s), 0, (n))
-#ifdef CONFIG_KERNEL_BZIP2
+#if defined(CONFIG_KERNEL_BZIP2)
#define BOOT_HEAP_SIZE 0x400000
-#elif CONFIG_KERNEL_ZSTD
+#elif defined(CONFIG_KERNEL_ZSTD)
#define BOOT_HEAP_SIZE 0x30000
#else
#define BOOT_HEAP_SIZE 0x10000
@@ -80,6 +80,6 @@ void *decompress_kernel(void)
void *output = (void *)decompress_offset;
__decompress(_compressed_start, _compressed_end - _compressed_start,
- NULL, NULL, output, 0, NULL, error);
+ NULL, NULL, output, vmlinux.image_size, NULL, error);
return output;
}
diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig
index a7b4e1d8275807..74b35ec2ad28a8 100644
--- a/arch/s390/configs/debug_defconfig
+++ b/arch/s390/configs/debug_defconfig
@@ -190,7 +190,6 @@ CONFIG_NFT_CT=m
CONFIG_NFT_LOG=m
CONFIG_NFT_LIMIT=m
CONFIG_NFT_NAT=m
-CONFIG_NFT_OBJREF=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m
@@ -569,6 +568,7 @@ CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
CONFIG_LEGACY_PTY_COUNT=0
+# CONFIG_LEGACY_TIOCSTI is not set
CONFIG_VIRTIO_CONSOLE=m
CONFIG_HW_RANDOM_VIRTIO=m
CONFIG_HANGCHECK_TIMER=m
@@ -660,6 +660,7 @@ CONFIG_CONFIGFS_FS=m
CONFIG_ECRYPT_FS=m
CONFIG_CRAMFS=m
CONFIG_SQUASHFS=m
+CONFIG_SQUASHFS_CHOICE_DECOMP_BY_MOUNT=y
CONFIG_SQUASHFS_XATTR=y
CONFIG_SQUASHFS_LZ4=y
CONFIG_SQUASHFS_LZO=y
@@ -705,6 +706,7 @@ CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y
CONFIG_SECURITY_LANDLOCK=y
CONFIG_INTEGRITY_SIGNATURE=y
CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y
+CONFIG_INTEGRITY_PLATFORM_KEYRING=y
CONFIG_IMA=y
CONFIG_IMA_DEFAULT_HASH_SHA256=y
CONFIG_IMA_WRITE_POLICY=y
@@ -781,6 +783,7 @@ CONFIG_ZCRYPT=m
CONFIG_PKEY=m
CONFIG_CRYPTO_PAES_S390=m
CONFIG_CRYPTO_DEV_VIRTIO=m
+CONFIG_SYSTEM_BLACKLIST_KEYRING=y
CONFIG_CORDIC=m
CONFIG_CRYPTO_LIB_CURVE25519=m
CONFIG_CRYPTO_LIB_CHACHA20POLY1305=m
@@ -848,7 +851,6 @@ CONFIG_PREEMPT_TRACER=y
CONFIG_SCHED_TRACER=y
CONFIG_FTRACE_SYSCALLS=y
CONFIG_BLK_DEV_IO_TRACE=y
-CONFIG_BPF_KPROBE_OVERRIDE=y
CONFIG_HIST_TRIGGERS=y
CONFIG_FTRACE_STARTUP_TEST=y
# CONFIG_EVENT_TRACE_STARTUP_TEST is not set
@@ -870,7 +872,6 @@ CONFIG_FAIL_MAKE_REQUEST=y
CONFIG_FAIL_IO_TIMEOUT=y
CONFIG_FAIL_FUTEX=y
CONFIG_FAULT_INJECTION_DEBUG_FS=y
-CONFIG_FAIL_FUNCTION=y
CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
CONFIG_LKDTM=m
CONFIG_TEST_MIN_HEAP=y
diff --git a/arch/s390/configs/defconfig b/arch/s390/configs/defconfig
index 2bc2d0fe57743c..cec71268e3bc2e 100644
--- a/arch/s390/configs/defconfig
+++ b/arch/s390/configs/defconfig
@@ -181,7 +181,6 @@ CONFIG_NFT_CT=m
CONFIG_NFT_LOG=m
CONFIG_NFT_LIMIT=m
CONFIG_NFT_NAT=m
-CONFIG_NFT_OBJREF=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m
@@ -559,6 +558,7 @@ CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
CONFIG_LEGACY_PTY_COUNT=0
+# CONFIG_LEGACY_TIOCSTI is not set
CONFIG_VIRTIO_CONSOLE=m
CONFIG_HW_RANDOM_VIRTIO=m
CONFIG_HANGCHECK_TIMER=m
@@ -645,6 +645,7 @@ CONFIG_CONFIGFS_FS=m
CONFIG_ECRYPT_FS=m
CONFIG_CRAMFS=m
CONFIG_SQUASHFS=m
+CONFIG_SQUASHFS_CHOICE_DECOMP_BY_MOUNT=y
CONFIG_SQUASHFS_XATTR=y
CONFIG_SQUASHFS_LZ4=y
CONFIG_SQUASHFS_LZO=y
@@ -688,6 +689,7 @@ CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y
CONFIG_SECURITY_LANDLOCK=y
CONFIG_INTEGRITY_SIGNATURE=y
CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y
+CONFIG_INTEGRITY_PLATFORM_KEYRING=y
CONFIG_IMA=y
CONFIG_IMA_DEFAULT_HASH_SHA256=y
CONFIG_IMA_WRITE_POLICY=y
@@ -766,6 +768,7 @@ CONFIG_ZCRYPT=m
CONFIG_PKEY=m
CONFIG_CRYPTO_PAES_S390=m
CONFIG_CRYPTO_DEV_VIRTIO=m
+CONFIG_SYSTEM_BLACKLIST_KEYRING=y
CONFIG_CORDIC=m
CONFIG_PRIME_NUMBERS=m
CONFIG_CRYPTO_LIB_CURVE25519=m
@@ -798,7 +801,6 @@ CONFIG_STACK_TRACER=y
CONFIG_SCHED_TRACER=y
CONFIG_FTRACE_SYSCALLS=y
CONFIG_BLK_DEV_IO_TRACE=y
-CONFIG_BPF_KPROBE_OVERRIDE=y
CONFIG_HIST_TRIGGERS=y
CONFIG_SAMPLES=y
CONFIG_SAMPLE_TRACE_PRINTK=m
diff --git a/arch/s390/configs/zfcpdump_defconfig b/arch/s390/configs/zfcpdump_defconfig
index ae14ab0b864d59..a9c0c81d1de992 100644
--- a/arch/s390/configs/zfcpdump_defconfig
+++ b/arch/s390/configs/zfcpdump_defconfig
@@ -13,7 +13,6 @@ CONFIG_TUNE_ZEC12=y
# CONFIG_COMPAT is not set
CONFIG_NR_CPUS=2
CONFIG_HZ_100=y
-# CONFIG_RELOCATABLE is not set
# CONFIG_CHSC_SCH is not set
# CONFIG_SCM_BUS is not set
CONFIG_CRASH_DUMP=y
@@ -50,6 +49,7 @@ CONFIG_ZFCP=y
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
+# CONFIG_LEGACY_TIOCSTI is not set
# CONFIG_HVC_IUCV is not set
# CONFIG_HW_RANDOM_S390 is not set
# CONFIG_HMC_DRV is not set
diff --git a/arch/s390/include/asm/cpu_mf.h b/arch/s390/include/asm/cpu_mf.h
index feaba12dbecb8d..efa103b52a1a19 100644
--- a/arch/s390/include/asm/cpu_mf.h
+++ b/arch/s390/include/asm/cpu_mf.h
@@ -131,19 +131,21 @@ struct hws_combined_entry {
struct hws_diag_entry diag; /* Diagnostic-sampling data entry */
} __packed;
-struct hws_trailer_entry {
- union {
- struct {
- unsigned int f:1; /* 0 - Block Full Indicator */
- unsigned int a:1; /* 1 - Alert request control */
- unsigned int t:1; /* 2 - Timestamp format */
- unsigned int :29; /* 3 - 31: Reserved */
- unsigned int bsdes:16; /* 32-47: size of basic SDE */
- unsigned int dsdes:16; /* 48-63: size of diagnostic SDE */
- };
- unsigned long long flags; /* 0 - 63: All indicators */
+union hws_trailer_header {
+ struct {
+ unsigned int f:1; /* 0 - Block Full Indicator */
+ unsigned int a:1; /* 1 - Alert request control */
+ unsigned int t:1; /* 2 - Timestamp format */
+ unsigned int :29; /* 3 - 31: Reserved */
+ unsigned int bsdes:16; /* 32-47: size of basic SDE */
+ unsigned int dsdes:16; /* 48-63: size of diagnostic SDE */
+ unsigned long long overflow; /* 64 - Overflow Count */
};
- unsigned long long overflow; /* 64 - sample Overflow count */
+ __uint128_t val;
+};
+
+struct hws_trailer_entry {
+ union hws_trailer_header header; /* 0 - 15 Flags + Overflow Count */
unsigned char timestamp[16]; /* 16 - 31 timestamp */
unsigned long long reserved1; /* 32 -Reserved */
unsigned long long reserved2; /* */
@@ -290,14 +292,11 @@ static inline unsigned long sample_rate_to_freq(struct hws_qsi_info_block *qsi,
return USEC_PER_SEC * qsi->cpu_speed / rate;
}
-#define SDB_TE_ALERT_REQ_MASK 0x4000000000000000UL
-#define SDB_TE_BUFFER_FULL_MASK 0x8000000000000000UL
-
/* Return TOD timestamp contained in an trailer entry */
static inline unsigned long long trailer_timestamp(struct hws_trailer_entry *te)
{
/* TOD in STCKE format */
- if (te->t)
+ if (te->header.t)
return *((unsigned long long *) &te->timestamp[1]);
/* TOD in STCK format */
diff --git a/arch/s390/include/asm/debug.h b/arch/s390/include/asm/debug.h
index 77f24262c25c18..ac665b9670c5db 100644
--- a/arch/s390/include/asm/debug.h
+++ b/arch/s390/include/asm/debug.h
@@ -4,8 +4,8 @@
*
* Copyright IBM Corp. 1999, 2020
*/
-#ifndef DEBUG_H
-#define DEBUG_H
+#ifndef _ASM_S390_DEBUG_H
+#define _ASM_S390_DEBUG_H
#include <linux/string.h>
#include <linux/spinlock.h>
@@ -487,4 +487,4 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas);
#endif /* MODULE */
-#endif /* DEBUG_H */
+#endif /* _ASM_S390_DEBUG_H */
diff --git a/arch/s390/include/asm/percpu.h b/arch/s390/include/asm/percpu.h
index cb5fc06904354a..081837b391e353 100644
--- a/arch/s390/include/asm/percpu.h
+++ b/arch/s390/include/asm/percpu.h
@@ -31,7 +31,7 @@
pcp_op_T__ *ptr__; \
preempt_disable_notrace(); \
ptr__ = raw_cpu_ptr(&(pcp)); \
- prev__ = *ptr__; \
+ prev__ = READ_ONCE(*ptr__); \
do { \
old__ = prev__; \
new__ = old__ op (val); \
diff --git a/arch/s390/kernel/machine_kexec_file.c b/arch/s390/kernel/machine_kexec_file.c
index fc6d5f58debeb4..2df94d32140c40 100644
--- a/arch/s390/kernel/machine_kexec_file.c
+++ b/arch/s390/kernel/machine_kexec_file.c
@@ -187,8 +187,6 @@ static int kexec_file_add_ipl_report(struct kimage *image,
data->memsz = ALIGN(data->memsz, PAGE_SIZE);
buf.mem = data->memsz;
- if (image->type == KEXEC_TYPE_CRASH)
- buf.mem += crashk_res.start;
ptr = (void *)ipl_cert_list_addr;
end = ptr + ipl_cert_list_size;
@@ -225,6 +223,9 @@ static int kexec_file_add_ipl_report(struct kimage *image,
data->kernel_buf + offsetof(struct lowcore, ipl_parmblock_ptr);
*lc_ipl_parmblock_ptr = (__u32)buf.mem;
+ if (image->type == KEXEC_TYPE_CRASH)
+ buf.mem += crashk_res.start;
+
ret = kexec_add_buffer(&buf);
out:
return ret;
diff --git a/arch/s390/kernel/perf_cpum_sf.c b/arch/s390/kernel/perf_cpum_sf.c
index 332a4996513087..ce886a03545ae1 100644
--- a/arch/s390/kernel/perf_cpum_sf.c
+++ b/arch/s390/kernel/perf_cpum_sf.c
@@ -163,14 +163,15 @@ static void free_sampling_buffer(struct sf_buffer *sfb)
static int alloc_sample_data_block(unsigned long *sdbt, gfp_t gfp_flags)
{
- unsigned long sdb, *trailer;
+ struct hws_trailer_entry *te;
+ unsigned long sdb;
/* Allocate and initialize sample-data-block */
sdb = get_zeroed_page(gfp_flags);
if (!sdb)
return -ENOMEM;
- trailer = trailer_entry_ptr(sdb);
- *trailer = SDB_TE_ALERT_REQ_MASK;
+ te = (struct hws_trailer_entry *)trailer_entry_ptr(sdb);
+ te->header.a = 1;
/* Link SDB into the sample-data-block-table */
*sdbt = sdb;
@@ -1206,7 +1207,7 @@ static void hw_collect_samples(struct perf_event *event, unsigned long *sdbt,
"%s: Found unknown"
" sampling data entry: te->f %i"
" basic.def %#4x (%p)\n", __func__,
- te->f, sample->def, sample);
+ te->header.f, sample->def, sample);
/* Sample slot is not yet written or other record.
*
* This condition can occur if the buffer was reused
@@ -1217,7 +1218,7 @@ static void hw_collect_samples(struct perf_event *event, unsigned long *sdbt,
* that are not full. Stop processing if the first
* invalid format was detected.
*/
- if (!te->f)
+ if (!te->header.f)
break;
}
@@ -1227,6 +1228,16 @@ static void hw_collect_samples(struct perf_event *event, unsigned long *sdbt,
}
}
+static inline __uint128_t __cdsg(__uint128_t *ptr, __uint128_t old, __uint128_t new)
+{
+ asm volatile(
+ " cdsg %[old],%[new],%[ptr]\n"
+ : [old] "+d" (old), [ptr] "+QS" (*ptr)
+ : [new] "d" (new)
+ : "memory", "cc");
+ return old;
+}
+
/* hw_perf_event_update() - Process sampling buffer
* @event: The perf event
* @flush_all: Flag to also flush partially filled sample-data-blocks
@@ -1243,10 +1254,11 @@ static void hw_collect_samples(struct perf_event *event, unsigned long *sdbt,
*/
static void hw_perf_event_update(struct perf_event *event, int flush_all)
{
+ unsigned long long event_overflow, sampl_overflow, num_sdb;
+ union hws_trailer_header old, prev, new;
struct hw_perf_event *hwc = &event->hw;
struct hws_trailer_entry *te;
unsigned long *sdbt;
- unsigned long long event_overflow, sampl_overflow, num_sdb, te_flags;
int done;
/*
@@ -1266,25 +1278,25 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all)
te = (struct hws_trailer_entry *) trailer_entry_ptr(*sdbt);
/* Leave loop if no more work to do (block full indicator) */
- if (!te->f) {
+ if (!te->header.f) {
done = 1;
if (!flush_all)
break;
}
/* Check the sample overflow count */
- if (te->overflow)
+ if (te->header.overflow)
/* Account sample overflows and, if a particular limit
* is reached, extend the sampling buffer.
* For details, see sfb_account_overflows().
*/
- sampl_overflow += te->overflow;
+ sampl_overflow += te->header.overflow;
/* Timestamps are valid for full sample-data-blocks only */
debug_sprintf_event(sfdbg, 6, "%s: sdbt %#lx "
"overflow %llu timestamp %#llx\n",
- __func__, (unsigned long)sdbt, te->overflow,
- (te->f) ? trailer_timestamp(te) : 0ULL);
+ __func__, (unsigned long)sdbt, te->header.overflow,
+ (te->header.f) ? trailer_timestamp(te) : 0ULL);
/* Collect all samples from a single sample-data-block and
* flag if an (perf) event overflow happened. If so, the PMU
@@ -1294,12 +1306,16 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all)
num_sdb++;
/* Reset trailer (using compare-double-and-swap) */
+ /* READ_ONCE() 16 byte header */
+ prev.val = __cdsg(&te->header.val, 0, 0);
do {
- te_flags = te->flags & ~SDB_TE_BUFFER_FULL_MASK;
- te_flags |= SDB_TE_ALERT_REQ_MASK;
- } while (!cmpxchg_double(&te->flags, &te->overflow,
- te->flags, te->overflow,
- te_flags, 0ULL));
+ old.val = prev.val;
+ new.val = prev.val;
+ new.f = 0;
+ new.a = 1;
+ new.overflow = 0;
+ prev.val = __cdsg(&te->header.val, old.val, new.val);
+ } while (prev.val != old.val);
/* Advance to next sample-data-block */
sdbt++;
@@ -1384,7 +1400,7 @@ static void aux_output_end(struct perf_output_handle *handle)
range_scan = AUX_SDB_NUM_ALERT(aux);
for (i = 0, idx = aux->head; i < range_scan; i++, idx++) {
te = aux_sdb_trailer(aux, idx);
- if (!(te->flags & SDB_TE_BUFFER_FULL_MASK))
+ if (!te->header.f)
break;
}
/* i is num of SDBs which are full */
@@ -1392,7 +1408,7 @@ static void aux_output_end(struct perf_output_handle *handle)
/* Remove alert indicators in the buffer */
te = aux_sdb_trailer(aux, aux->alert_mark);
- te->flags &= ~SDB_TE_ALERT_REQ_MASK;
+ te->header.a = 0;
debug_sprintf_event(sfdbg, 6, "%s: SDBs %ld range %ld head %ld\n",
__func__, i, range_scan, aux->head);
@@ -1437,9 +1453,9 @@ static int aux_output_begin(struct perf_output_handle *handle,
idx = aux->empty_mark + 1;
for (i = 0; i < range_scan; i++, idx++) {
te = aux_sdb_trailer(aux, idx);
- te->flags &= ~(SDB_TE_BUFFER_FULL_MASK |
- SDB_TE_ALERT_REQ_MASK);
- te->overflow = 0;
+ te->header.f = 0;
+ te->header.a = 0;
+ te->header.overflow = 0;
}
/* Save the position of empty SDBs */
aux->empty_mark = aux->head + range - 1;
@@ -1448,7 +1464,7 @@ static int aux_output_begin(struct perf_output_handle *handle,
/* Set alert indicator */
aux->alert_mark = aux->head + range/2 - 1;
te = aux_sdb_trailer(aux, aux->alert_mark);
- te->flags = te->flags | SDB_TE_ALERT_REQ_MASK;
+ te->header.a = 1;
/* Reset hardware buffer head */
head = AUX_SDB_INDEX(aux, aux->head);
@@ -1475,14 +1491,17 @@ static int aux_output_begin(struct perf_output_handle *handle,
static bool aux_set_alert(struct aux_buffer *aux, unsigned long alert_index,
unsigned long long *overflow)
{
- unsigned long long orig_overflow, orig_flags, new_flags;
+ union hws_trailer_header old, prev, new;
struct hws_trailer_entry *te;
te = aux_sdb_trailer(aux, alert_index);
+ /* READ_ONCE() 16 byte header */
+ prev.val = __cdsg(&te->header.val, 0, 0);
do {
- orig_flags = te->flags;
- *overflow = orig_overflow = te->overflow;
- if (orig_flags & SDB_TE_BUFFER_FULL_MASK) {
+ old.val = prev.val;
+ new.val = prev.val;
+ *overflow = old.overflow;
+ if (old.f) {
/*
* SDB is already set by hardware.
* Abort and try to set somewhere
@@ -1490,10 +1509,10 @@ static bool aux_set_alert(struct aux_buffer *aux, unsigned long alert_index,
*/
return false;
}
- new_flags = orig_flags | SDB_TE_ALERT_REQ_MASK;
- } while (!cmpxchg_double(&te->flags, &te->overflow,
- orig_flags, orig_overflow,
- new_flags, 0ULL));
+ new.a = 1;
+ new.overflow = 0;
+ prev.val = __cdsg(&te->header.val, old.val, new.val);
+ } while (prev.val != old.val);
return true;
}
@@ -1522,8 +1541,9 @@ static bool aux_set_alert(struct aux_buffer *aux, unsigned long alert_index,
static bool aux_reset_buffer(struct aux_buffer *aux, unsigned long range,
unsigned long long *overflow)
{
- unsigned long long orig_overflow, orig_flags, new_flags;
unsigned long i, range_scan, idx, idx_old;
+ union hws_trailer_header old, prev, new;
+ unsigned long long orig_overflow;
struct hws_trailer_entry *te;
debug_sprintf_event(sfdbg, 6, "%s: range %ld head %ld alert %ld "
@@ -1554,17 +1574,20 @@ static bool aux_reset_buffer(struct aux_buffer *aux, unsigned long range,
idx_old = idx = aux->empty_mark + 1;
for (i = 0; i < range_scan; i++, idx++) {
te = aux_sdb_trailer(aux, idx);
+ /* READ_ONCE() 16 byte header */
+ prev.val = __cdsg(&te->header.val, 0, 0);
do {
- orig_flags = te->flags;
- orig_overflow = te->overflow;
- new_flags = orig_flags & ~SDB_TE_BUFFER_FULL_MASK;
+ old.val = prev.val;
+ new.val = prev.val;
+ orig_overflow = old.overflow;
+ new.f = 0;
+ new.overflow = 0;
if (idx == aux->alert_mark)
- new_flags |= SDB_TE_ALERT_REQ_MASK;
+ new.a = 1;
else
- new_flags &= ~SDB_TE_ALERT_REQ_MASK;
- } while (!cmpxchg_double(&te->flags, &te->overflow,
- orig_flags, orig_overflow,
- new_flags, 0ULL));
+ new.a = 0;
+ prev.val = __cdsg(&te->header.val, old.val, new.val);
+ } while (prev.val != old.val);
*overflow += orig_overflow;
}
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 2b6091349daa25..696c9e007a369d 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -508,6 +508,7 @@ static void __init setup_lowcore_dat_on(void)
{
struct lowcore *abs_lc;
unsigned long flags;
+ int i;
__ctl_clear_bit(0, 28);
S390_lowcore.external_new_psw.mask |= PSW_MASK_DAT;
@@ -523,8 +524,8 @@ static void __init setup_lowcore_dat_on(void)
abs_lc = get_abs_lowcore(&flags);
abs_lc->restart_flags = RESTART_FLAG_CTLREGS;
abs_lc->program_new_psw = S390_lowcore.program_new_psw;
- memcpy(abs_lc->cregs_save_area, S390_lowcore.cregs_save_area,
- sizeof(abs_lc->cregs_save_area));
+ for (i = 0; i < 16; i++)
+ abs_lc->cregs_save_area[i] = S390_lowcore.cregs_save_area[i];
put_abs_lowcore(abs_lc, flags);
}
diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S
index 5ea3830af0ccff..cbf9c1b0beda4a 100644
--- a/arch/s390/kernel/vmlinux.lds.S
+++ b/arch/s390/kernel/vmlinux.lds.S
@@ -17,6 +17,8 @@
/* Handle ro_after_init data on our own. */
#define RO_AFTER_INIT_DATA
+#define RUNTIME_DISCARD_EXIT
+
#define EMITS_PT_NOTE
#include <asm-generic/vmlinux.lds.h>
@@ -79,6 +81,7 @@ SECTIONS
_end_amode31_refs = .;
}
+ . = ALIGN(PAGE_SIZE);
_edata = .; /* End of data section */
/* will be freed after init */
@@ -193,6 +196,7 @@ SECTIONS
BSS_SECTION(PAGE_SIZE, 4 * PAGE_SIZE, PAGE_SIZE)
+ . = ALIGN(PAGE_SIZE);
_end = . ;
/*
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index 1dae78deddf286..ab26aa53ee3713 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -83,8 +83,9 @@ static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id)
struct esca_block *sca = vcpu->kvm->arch.sca;
union esca_sigp_ctrl *sigp_ctrl =
&(sca->cpu[vcpu->vcpu_id].sigp_ctrl);
- union esca_sigp_ctrl new_val = {0}, old_val = *sigp_ctrl;
+ union esca_sigp_ctrl new_val = {0}, old_val;
+ old_val = READ_ONCE(*sigp_ctrl);
new_val.scn = src_id;
new_val.c = 1;
old_val.c = 0;
@@ -95,8 +96,9 @@ static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id)
struct bsca_block *sca = vcpu->kvm->arch.sca;
union bsca_sigp_ctrl *sigp_ctrl =
&(sca->cpu[vcpu->vcpu_id].sigp_ctrl);
- union bsca_sigp_ctrl new_val = {0}, old_val = *sigp_ctrl;
+ union bsca_sigp_ctrl new_val = {0}, old_val;
+ old_val = READ_ONCE(*sigp_ctrl);
new_val.scn = src_id;
new_val.c = 1;
old_val.c = 0;
@@ -126,16 +128,18 @@ static void sca_clear_ext_call(struct kvm_vcpu *vcpu)
struct esca_block *sca = vcpu->kvm->arch.sca;
union esca_sigp_ctrl *sigp_ctrl =
&(sca->cpu[vcpu->vcpu_id].sigp_ctrl);
- union esca_sigp_ctrl old = *sigp_ctrl;
+ union esca_sigp_ctrl old;
+ old = READ_ONCE(*sigp_ctrl);
expect = old.value;
rc = cmpxchg(&sigp_ctrl->value, old.value, 0);
} else {
struct bsca_block *sca = vcpu->kvm->arch.sca;
union bsca_sigp_ctrl *sigp_ctrl =
&(sca->cpu[vcpu->vcpu_id].sigp_ctrl);
- union bsca_sigp_ctrl old = *sigp_ctrl;
+ union bsca_sigp_ctrl old;
+ old = READ_ONCE(*sigp_ctrl);
expect = old.value;
rc = cmpxchg(&sigp_ctrl->value, old.value, 0);
}
diff --git a/arch/sh/include/asm/pgtable-3level.h b/arch/sh/include/asm/pgtable-3level.h
index a889a3a938bab8..d1ce73f3bd85ef 100644
--- a/arch/sh/include/asm/pgtable-3level.h
+++ b/arch/sh/include/asm/pgtable-3level.h
@@ -28,7 +28,7 @@
#define pmd_ERROR(e) \
printk("%s:%d: bad pmd %016llx.\n", __FILE__, __LINE__, pmd_val(e))
-typedef struct {
+typedef union {
struct {
unsigned long pmd_low;
unsigned long pmd_high;
diff --git a/arch/sh/kernel/vmlinux.lds.S b/arch/sh/kernel/vmlinux.lds.S
index 3161b9ccd2a570..b6276a3521d73b 100644
--- a/arch/sh/kernel/vmlinux.lds.S
+++ b/arch/sh/kernel/vmlinux.lds.S
@@ -4,6 +4,7 @@
* Written by Niibe Yutaka and Paul Mundt
*/
OUTPUT_ARCH(sh)
+#define RUNTIME_DISCARD_EXIT
#include <asm/thread_info.h>
#include <asm/cache.h>
#include <asm/vmlinux.lds.h>
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 9cf07322875a42..73ed982d410000 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -14,13 +14,13 @@ endif
ifdef CONFIG_CC_IS_GCC
RETPOLINE_CFLAGS := $(call cc-option,-mindirect-branch=thunk-extern -mindirect-branch-register)
-RETPOLINE_CFLAGS += $(call cc-option,-mindirect-branch-cs-prefix)
RETPOLINE_VDSO_CFLAGS := $(call cc-option,-mindirect-branch=thunk-inline -mindirect-branch-register)
endif
ifdef CONFIG_CC_IS_CLANG
RETPOLINE_CFLAGS := -mretpoline-external-thunk
RETPOLINE_VDSO_CFLAGS := -mretpoline
endif
+RETPOLINE_CFLAGS += $(call cc-option,-mindirect-branch-cs-prefix)
ifdef CONFIG_RETHUNK
RETHUNK_CFLAGS := -mfunction-return=thunk-extern
diff --git a/arch/x86/boot/bioscall.S b/arch/x86/boot/bioscall.S
index 5521ea12f44e06..aa9b9645758433 100644
--- a/arch/x86/boot/bioscall.S
+++ b/arch/x86/boot/bioscall.S
@@ -32,7 +32,7 @@ intcall:
movw %dx, %si
movw %sp, %di
movw $11, %cx
- rep; movsd
+ rep; movsl
/* Pop full state from the stack */
popal
@@ -67,7 +67,7 @@ intcall:
jz 4f
movw %sp, %si
movw $11, %cx
- rep; movsd
+ rep; movsl
4: addw $44, %sp
/* Restore state and return */
diff --git a/arch/x86/boot/compressed/ident_map_64.c b/arch/x86/boot/compressed/ident_map_64.c
index d4a314cc50d6ee..321a5011042d40 100644
--- a/arch/x86/boot/compressed/ident_map_64.c
+++ b/arch/x86/boot/compressed/ident_map_64.c
@@ -180,6 +180,12 @@ void initialize_identity_maps(void *rmode)
/* Load the new page-table. */
write_cr3(top_level_pgt);
+
+ /*
+ * Now that the required page table mappings are established and a
+ * GHCB can be used, check for SNP guest/HV feature compatibility.
+ */
+ snp_check_features();
}
static pte_t *split_large_pmd(struct x86_mapping_info *info,
diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h
index 62208ec04ca4b8..20118fb7c53bbb 100644
--- a/arch/x86/boot/compressed/misc.h
+++ b/arch/x86/boot/compressed/misc.h
@@ -126,6 +126,7 @@ static inline void console_init(void)
#ifdef CONFIG_AMD_MEM_ENCRYPT
void sev_enable(struct boot_params *bp);
+void snp_check_features(void);
void sev_es_shutdown_ghcb(void);
extern bool sev_es_check_ghcb_fault(unsigned long address);
void snp_set_page_private(unsigned long paddr);
@@ -143,6 +144,7 @@ static inline void sev_enable(struct boot_params *bp)
if (bp)
bp->cc_blob_address = 0;
}
+static inline void snp_check_features(void) { }
static inline void sev_es_shutdown_ghcb(void) { }
static inline bool sev_es_check_ghcb_fault(unsigned long address)
{
diff --git a/arch/x86/boot/compressed/sev.c b/arch/x86/boot/compressed/sev.c
index c93930d5ccbd0f..d63ad8f99f83a3 100644
--- a/arch/x86/boot/compressed/sev.c
+++ b/arch/x86/boot/compressed/sev.c
@@ -208,6 +208,23 @@ void sev_es_shutdown_ghcb(void)
error("Can't unmap GHCB page");
}
+static void __noreturn sev_es_ghcb_terminate(struct ghcb *ghcb, unsigned int set,
+ unsigned int reason, u64 exit_info_2)
+{
+ u64 exit_info_1 = SVM_VMGEXIT_TERM_REASON(set, reason);
+
+ vc_ghcb_invalidate(ghcb);
+ ghcb_set_sw_exit_code(ghcb, SVM_VMGEXIT_TERM_REQUEST);
+ ghcb_set_sw_exit_info_1(ghcb, exit_info_1);
+ ghcb_set_sw_exit_info_2(ghcb, exit_info_2);
+
+ sev_es_wr_ghcb_msr(__pa(ghcb));
+ VMGEXIT();
+
+ while (true)
+ asm volatile("hlt\n" : : : "memory");
+}
+
bool sev_es_check_ghcb_fault(unsigned long address)
{
/* Check whether the fault was on the GHCB page */
@@ -270,6 +287,59 @@ static void enforce_vmpl0(void)
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_NOT_VMPL0);
}
+/*
+ * SNP_FEATURES_IMPL_REQ is the mask of SNP features that will need
+ * guest side implementation for proper functioning of the guest. If any
+ * of these features are enabled in the hypervisor but are lacking guest
+ * side implementation, the behavior of the guest will be undefined. The
+ * guest could fail in non-obvious way making it difficult to debug.
+ *
+ * As the behavior of reserved feature bits is unknown to be on the
+ * safe side add them to the required features mask.
+ */
+#define SNP_FEATURES_IMPL_REQ (MSR_AMD64_SNP_VTOM | \
+ MSR_AMD64_SNP_REFLECT_VC | \
+ MSR_AMD64_SNP_RESTRICTED_INJ | \
+ MSR_AMD64_SNP_ALT_INJ | \
+ MSR_AMD64_SNP_DEBUG_SWAP | \
+ MSR_AMD64_SNP_VMPL_SSS | \
+ MSR_AMD64_SNP_SECURE_TSC | \
+ MSR_AMD64_SNP_VMGEXIT_PARAM | \
+ MSR_AMD64_SNP_VMSA_REG_PROTECTION | \
+ MSR_AMD64_SNP_RESERVED_BIT13 | \
+ MSR_AMD64_SNP_RESERVED_BIT15 | \
+ MSR_AMD64_SNP_RESERVED_MASK)
+
+/*
+ * SNP_FEATURES_PRESENT is the mask of SNP features that are implemented
+ * by the guest kernel. As and when a new feature is implemented in the
+ * guest kernel, a corresponding bit should be added to the mask.
+ */
+#define SNP_FEATURES_PRESENT (0)
+
+void snp_check_features(void)
+{
+ u64 unsupported;
+
+ if (!(sev_status & MSR_AMD64_SEV_SNP_ENABLED))
+ return;
+
+ /*
+ * Terminate the boot if hypervisor has enabled any feature lacking
+ * guest side implementation. Pass on the unsupported features mask through
+ * EXIT_INFO_2 of the GHCB protocol so that those features can be reported
+ * as part of the guest boot failure.
+ */
+ unsupported = sev_status & SNP_FEATURES_IMPL_REQ & ~SNP_FEATURES_PRESENT;
+ if (unsupported) {
+ if (ghcb_version < 2 || (!boot_ghcb && !early_setup_ghcb()))
+ sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED);
+
+ sev_es_ghcb_terminate(boot_ghcb, SEV_TERM_SET_GEN,
+ GHCB_SNP_UNSUPPORTED, unsupported);
+ }
+}
+
void sev_enable(struct boot_params *bp)
{
unsigned int eax, ebx, ecx, edx;
diff --git a/arch/x86/coco/tdx/tdx.c b/arch/x86/coco/tdx/tdx.c
index cfd4c95b9f045e..669d9e4f290152 100644
--- a/arch/x86/coco/tdx/tdx.c
+++ b/arch/x86/coco/tdx/tdx.c
@@ -386,8 +386,8 @@ static int handle_mmio(struct pt_regs *regs, struct ve_info *ve)
{
unsigned long *reg, val, vaddr;
char buffer[MAX_INSN_SIZE];
+ enum insn_mmio_type mmio;
struct insn insn = {};
- enum mmio_type mmio;
int size, extend_size;
u8 extend_val = 0;
@@ -402,10 +402,10 @@ static int handle_mmio(struct pt_regs *regs, struct ve_info *ve)
return -EINVAL;
mmio = insn_decode_mmio(&insn, &size);
- if (WARN_ON_ONCE(mmio == MMIO_DECODE_FAILED))
+ if (WARN_ON_ONCE(mmio == INSN_MMIO_DECODE_FAILED))
return -EINVAL;
- if (mmio != MMIO_WRITE_IMM && mmio != MMIO_MOVS) {
+ if (mmio != INSN_MMIO_WRITE_IMM && mmio != INSN_MMIO_MOVS) {
reg = insn_get_modrm_reg_ptr(&insn, regs);
if (!reg)
return -EINVAL;
@@ -426,23 +426,23 @@ static int handle_mmio(struct pt_regs *regs, struct ve_info *ve)
/* Handle writes first */
switch (mmio) {
- case MMIO_WRITE:
+ case INSN_MMIO_WRITE:
memcpy(&val, reg, size);
if (!mmio_write(size, ve->gpa, val))
return -EIO;
return insn.length;
- case MMIO_WRITE_IMM:
+ case INSN_MMIO_WRITE_IMM:
val = insn.immediate.value;
if (!mmio_write(size, ve->gpa, val))
return -EIO;
return insn.length;
- case MMIO_READ:
- case MMIO_READ_ZERO_EXTEND:
- case MMIO_READ_SIGN_EXTEND:
+ case INSN_MMIO_READ:
+ case INSN_MMIO_READ_ZERO_EXTEND:
+ case INSN_MMIO_READ_SIGN_EXTEND:
/* Reads are handled below */
break;
- case MMIO_MOVS:
- case MMIO_DECODE_FAILED:
+ case INSN_MMIO_MOVS:
+ case INSN_MMIO_DECODE_FAILED:
/*
* MMIO was accessed with an instruction that could not be
* decoded or handled properly. It was likely not using io.h
@@ -459,15 +459,15 @@ static int handle_mmio(struct pt_regs *regs, struct ve_info *ve)
return -EIO;
switch (mmio) {
- case MMIO_READ:
+ case INSN_MMIO_READ:
/* Zero-extend for 32-bit operation */
extend_size = size == 4 ? sizeof(*reg) : 0;
break;
- case MMIO_READ_ZERO_EXTEND:
+ case INSN_MMIO_READ_ZERO_EXTEND:
/* Zero extend based on operand size */
extend_size = insn.opnd_bytes;
break;
- case MMIO_READ_SIGN_EXTEND:
+ case INSN_MMIO_READ_SIGN_EXTEND:
/* Sign extend based on operand size */
extend_size = insn.opnd_bytes;
if (size == 1 && val & BIT(7))
diff --git a/arch/x86/events/amd/core.c b/arch/x86/events/amd/core.c
index d6f3703e41194a..4386b10682ce4f 100644
--- a/arch/x86/events/amd/core.c
+++ b/arch/x86/events/amd/core.c
@@ -1387,7 +1387,7 @@ static int __init amd_core_pmu_init(void)
* numbered counter following it.
*/
for (i = 0; i < x86_pmu.num_counters - 1; i += 2)
- even_ctr_mask |= 1 << i;
+ even_ctr_mask |= BIT_ULL(i);
pair_constraint = (struct event_constraint)
__EVENT_CONSTRAINT(0, even_ctr_mask, 0,
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
index dfd2c124cdf80a..bafdc2be479ace 100644
--- a/arch/x86/events/intel/core.c
+++ b/arch/x86/events/intel/core.c
@@ -6339,6 +6339,7 @@ __init int intel_pmu_init(void)
break;
case INTEL_FAM6_SAPPHIRERAPIDS_X:
+ case INTEL_FAM6_EMERALDRAPIDS_X:
pmem = true;
x86_pmu.late_ack = true;
memcpy(hw_cache_event_ids, spr_hw_cache_event_ids, sizeof(hw_cache_event_ids));
diff --git a/arch/x86/events/intel/cstate.c b/arch/x86/events/intel/cstate.c
index a2834bc93149a7..551741e79e038a 100644
--- a/arch/x86/events/intel/cstate.c
+++ b/arch/x86/events/intel/cstate.c
@@ -41,6 +41,7 @@
* MSR_CORE_C1_RES: CORE C1 Residency Counter
* perf code: 0x00
* Available model: SLM,AMT,GLM,CNL,ICX,TNT,ADL,RPL
+ * MTL
* Scope: Core (each processor core has a MSR)
* MSR_CORE_C3_RESIDENCY: CORE C3 Residency Counter
* perf code: 0x01
@@ -51,50 +52,50 @@
* perf code: 0x02
* Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW,
* SKL,KNL,GLM,CNL,KBL,CML,ICL,ICX,
- * TGL,TNT,RKL,ADL,RPL,SPR
+ * TGL,TNT,RKL,ADL,RPL,SPR,MTL
* Scope: Core
* MSR_CORE_C7_RESIDENCY: CORE C7 Residency Counter
* perf code: 0x03
* Available model: SNB,IVB,HSW,BDW,SKL,CNL,KBL,CML,
- * ICL,TGL,RKL,ADL,RPL
+ * ICL,TGL,RKL,ADL,RPL,MTL
* Scope: Core
* MSR_PKG_C2_RESIDENCY: Package C2 Residency Counter.
* perf code: 0x00
* Available model: SNB,IVB,HSW,BDW,SKL,KNL,GLM,CNL,
* KBL,CML,ICL,ICX,TGL,TNT,RKL,ADL,
- * RPL,SPR
+ * RPL,SPR,MTL
* Scope: Package (physical package)
* MSR_PKG_C3_RESIDENCY: Package C3 Residency Counter.
* perf code: 0x01
* Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL,KNL,
* GLM,CNL,KBL,CML,ICL,TGL,TNT,RKL,
- * ADL,RPL
+ * ADL,RPL,MTL
* Scope: Package (physical package)
* MSR_PKG_C6_RESIDENCY: Package C6 Residency Counter.
* perf code: 0x02
* Available model: SLM,AMT,NHM,WSM,SNB,IVB,HSW,BDW,
* SKL,KNL,GLM,CNL,KBL,CML,ICL,ICX,
- * TGL,TNT,RKL,ADL,RPL,SPR
+ * TGL,TNT,RKL,ADL,RPL,SPR,MTL
* Scope: Package (physical package)
* MSR_PKG_C7_RESIDENCY: Package C7 Residency Counter.
* perf code: 0x03
* Available model: NHM,WSM,SNB,IVB,HSW,BDW,SKL,CNL,
- * KBL,CML,ICL,TGL,RKL,ADL,RPL
+ * KBL,CML,ICL,TGL,RKL,ADL,RPL,MTL
* Scope: Package (physical package)
* MSR_PKG_C8_RESIDENCY: Package C8 Residency Counter.
* perf code: 0x04
* Available model: HSW ULT,KBL,CNL,CML,ICL,TGL,RKL,
- * ADL,RPL
+ * ADL,RPL,MTL
* Scope: Package (physical package)
* MSR_PKG_C9_RESIDENCY: Package C9 Residency Counter.
* perf code: 0x05
* Available model: HSW ULT,KBL,CNL,CML,ICL,TGL,RKL,
- * ADL,RPL
+ * ADL,RPL,MTL
* Scope: Package (physical package)
* MSR_PKG_C10_RESIDENCY: Package C10 Residency Counter.
* perf code: 0x06
* Available model: HSW ULT,KBL,GLM,CNL,CML,ICL,TGL,
- * TNT,RKL,ADL,RPL
+ * TNT,RKL,ADL,RPL,MTL
* Scope: Package (physical package)
*
*/
@@ -676,6 +677,7 @@ static const struct x86_cpu_id intel_cstates_match[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X, &icx_cstates),
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D, &icx_cstates),
X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, &icx_cstates),
+ X86_MATCH_INTEL_FAM6_MODEL(EMERALDRAPIDS_X, &icx_cstates),
X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, &icl_cstates),
X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, &icl_cstates),
@@ -686,6 +688,8 @@ static const struct x86_cpu_id intel_cstates_match[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, &adl_cstates),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, &adl_cstates),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, &adl_cstates),
+ X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE, &adl_cstates),
+ X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, &adl_cstates),
{ },
};
MODULE_DEVICE_TABLE(x86cpu, intel_cstates_match);
diff --git a/arch/x86/events/intel/uncore.c b/arch/x86/events/intel/uncore.c
index 6f1ccc57a6921f..459b1aafd4d4a3 100644
--- a/arch/x86/events/intel/uncore.c
+++ b/arch/x86/events/intel/uncore.c
@@ -1833,6 +1833,7 @@ static const struct x86_cpu_id intel_uncore_match[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, &adl_uncore_init),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, &adl_uncore_init),
X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, &spr_uncore_init),
+ X86_MATCH_INTEL_FAM6_MODEL(EMERALDRAPIDS_X, &spr_uncore_init),
X86_MATCH_INTEL_FAM6_MODEL(ATOM_TREMONT_D, &snr_uncore_init),
{},
};
diff --git a/arch/x86/events/msr.c b/arch/x86/events/msr.c
index ecced3a52668a1..c65d8906cbcf4d 100644
--- a/arch/x86/events/msr.c
+++ b/arch/x86/events/msr.c
@@ -69,6 +69,7 @@ static bool test_intel(int idx, void *data)
case INTEL_FAM6_BROADWELL_G:
case INTEL_FAM6_BROADWELL_X:
case INTEL_FAM6_SAPPHIRERAPIDS_X:
+ case INTEL_FAM6_EMERALDRAPIDS_X:
case INTEL_FAM6_ATOM_SILVERMONT:
case INTEL_FAM6_ATOM_SILVERMONT_D:
@@ -107,6 +108,8 @@ static bool test_intel(int idx, void *data)
case INTEL_FAM6_RAPTORLAKE:
case INTEL_FAM6_RAPTORLAKE_P:
case INTEL_FAM6_RAPTORLAKE_S:
+ case INTEL_FAM6_METEORLAKE:
+ case INTEL_FAM6_METEORLAKE_L:
if (idx == PERF_MSR_SMI || idx == PERF_MSR_PPERF)
return true;
break;
diff --git a/arch/x86/events/rapl.c b/arch/x86/events/rapl.c
index a829492bca4c19..52e6e7ed4f78a6 100644
--- a/arch/x86/events/rapl.c
+++ b/arch/x86/events/rapl.c
@@ -800,13 +800,18 @@ static const struct x86_cpu_id rapl_model_match[] __initconst = {
X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X, &model_hsx),
X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE_L, &model_skl),
X86_MATCH_INTEL_FAM6_MODEL(COMETLAKE, &model_skl),
+ X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE_L, &model_skl),
+ X86_MATCH_INTEL_FAM6_MODEL(TIGERLAKE, &model_skl),
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE, &model_skl),
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, &model_skl),
X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_N, &model_skl),
X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, &model_spr),
+ X86_MATCH_INTEL_FAM6_MODEL(EMERALDRAPIDS_X, &model_spr),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, &model_skl),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_P, &model_skl),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, &model_skl),
+ X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE, &model_skl),
+ X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, &model_skl),
{},
};
MODULE_DEVICE_TABLE(x86cpu, rapl_model_match);
diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h
index 65064d9f7fa6ea..8eb74cf386dbe5 100644
--- a/arch/x86/include/asm/acpi.h
+++ b/arch/x86/include/asm/acpi.h
@@ -14,6 +14,7 @@
#include <asm/mmu.h>
#include <asm/mpspec.h>
#include <asm/x86_init.h>
+#include <asm/cpufeature.h>
#ifdef CONFIG_ACPI_APEI
# include <asm/pgtable_types.h>
@@ -63,6 +64,13 @@ extern int (*acpi_suspend_lowlevel)(void);
/* Physical address to resume after wakeup */
unsigned long acpi_get_wakeup_address(void);
+static inline bool acpi_skip_set_wakeup_address(void)
+{
+ return cpu_feature_enabled(X86_FEATURE_XENPV);
+}
+
+#define acpi_skip_set_wakeup_address acpi_skip_set_wakeup_address
+
/*
* Check if the CPU can handle C2 and deeper
*/
diff --git a/arch/x86/include/asm/debugreg.h b/arch/x86/include/asm/debugreg.h
index b049d950612fdd..ca97442e8d49fc 100644
--- a/arch/x86/include/asm/debugreg.h
+++ b/arch/x86/include/asm/debugreg.h
@@ -39,7 +39,20 @@ static __always_inline unsigned long native_get_debugreg(int regno)
asm("mov %%db6, %0" :"=r" (val));
break;
case 7:
- asm("mov %%db7, %0" :"=r" (val));
+ /*
+ * Apply __FORCE_ORDER to DR7 reads to forbid re-ordering them
+ * with other code.
+ *
+ * This is needed because a DR7 access can cause a #VC exception
+ * when running under SEV-ES. Taking a #VC exception is not a
+ * safe thing to do just anywhere in the entry code and
+ * re-ordering might place the access into an unsafe location.
+ *
+ * This happened in the NMI handler, where the DR7 read was
+ * re-ordered to happen before the call to sev_es_ist_enter(),
+ * causing stack recursion.
+ */
+ asm volatile("mov %%db7, %0" : "=r" (val) : __FORCE_ORDER);
break;
default:
BUG();
@@ -66,7 +79,16 @@ static __always_inline void native_set_debugreg(int regno, unsigned long value)
asm("mov %0, %%db6" ::"r" (value));
break;
case 7:
- asm("mov %0, %%db7" ::"r" (value));
+ /*
+ * Apply __FORCE_ORDER to DR7 writes to forbid re-ordering them
+ * with other code.
+ *
+ * While is didn't happen with a DR7 write (see the DR7 read
+ * comment above which explains where it happened), add the
+ * __FORCE_ORDER here too to avoid similar problems in the
+ * future.
+ */
+ asm volatile("mov %0, %%db7" ::"r" (value), __FORCE_ORDER);
break;
default:
BUG();
diff --git a/arch/x86/include/asm/insn-eval.h b/arch/x86/include/asm/insn-eval.h
index f07faa61c7f311..54368a43abf67e 100644
--- a/arch/x86/include/asm/insn-eval.h
+++ b/arch/x86/include/asm/insn-eval.h
@@ -32,16 +32,16 @@ int insn_fetch_from_user_inatomic(struct pt_regs *regs,
bool insn_decode_from_regs(struct insn *insn, struct pt_regs *regs,
unsigned char buf[MAX_INSN_SIZE], int buf_size);
-enum mmio_type {
- MMIO_DECODE_FAILED,
- MMIO_WRITE,
- MMIO_WRITE_IMM,
- MMIO_READ,
- MMIO_READ_ZERO_EXTEND,
- MMIO_READ_SIGN_EXTEND,
- MMIO_MOVS,
+enum insn_mmio_type {
+ INSN_MMIO_DECODE_FAILED,
+ INSN_MMIO_WRITE,
+ INSN_MMIO_WRITE_IMM,
+ INSN_MMIO_READ,
+ INSN_MMIO_READ_ZERO_EXTEND,
+ INSN_MMIO_READ_SIGN_EXTEND,
+ INSN_MMIO_MOVS,
};
-enum mmio_type insn_decode_mmio(struct insn *insn, int *bytes);
+enum insn_mmio_type insn_decode_mmio(struct insn *insn, int *bytes);
#endif /* _ASM_X86_INSN_EVAL_H */
diff --git a/arch/x86/include/asm/intel-family.h b/arch/x86/include/asm/intel-family.h
index 347707d459c67f..cbaf174d8efd9e 100644
--- a/arch/x86/include/asm/intel-family.h
+++ b/arch/x86/include/asm/intel-family.h
@@ -123,6 +123,8 @@
#define INTEL_FAM6_METEORLAKE 0xAC
#define INTEL_FAM6_METEORLAKE_L 0xAA
+#define INTEL_FAM6_LUNARLAKE_M 0xBD
+
/* "Small Core" Processors (Atom/E-Core) */
#define INTEL_FAM6_ATOM_BONNELL 0x1C /* Diamondville, Pineview */
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index f35f1ff4427bb8..6aaae18f185447 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1111,6 +1111,7 @@ struct msr_bitmap_range {
/* Xen emulation context */
struct kvm_xen {
+ struct mutex xen_lock;
u32 xen_version;
bool long_mode;
bool runstate_update_flag;
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 37ff47552bcb7b..d3fe82c5d6b660 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -566,6 +566,26 @@
#define MSR_AMD64_SEV_ES_ENABLED BIT_ULL(MSR_AMD64_SEV_ES_ENABLED_BIT)
#define MSR_AMD64_SEV_SNP_ENABLED BIT_ULL(MSR_AMD64_SEV_SNP_ENABLED_BIT)
+/* SNP feature bits enabled by the hypervisor */
+#define MSR_AMD64_SNP_VTOM BIT_ULL(3)
+#define MSR_AMD64_SNP_REFLECT_VC BIT_ULL(4)
+#define MSR_AMD64_SNP_RESTRICTED_INJ BIT_ULL(5)
+#define MSR_AMD64_SNP_ALT_INJ BIT_ULL(6)
+#define MSR_AMD64_SNP_DEBUG_SWAP BIT_ULL(7)
+#define MSR_AMD64_SNP_PREVENT_HOST_IBS BIT_ULL(8)
+#define MSR_AMD64_SNP_BTB_ISOLATION BIT_ULL(9)
+#define MSR_AMD64_SNP_VMPL_SSS BIT_ULL(10)
+#define MSR_AMD64_SNP_SECURE_TSC BIT_ULL(11)
+#define MSR_AMD64_SNP_VMGEXIT_PARAM BIT_ULL(12)
+#define MSR_AMD64_SNP_IBS_VIRT BIT_ULL(14)
+#define MSR_AMD64_SNP_VMSA_REG_PROTECTION BIT_ULL(16)
+#define MSR_AMD64_SNP_SMT_PROTECTION BIT_ULL(17)
+
+/* SNP feature bits reserved for future use. */
+#define MSR_AMD64_SNP_RESERVED_BIT13 BIT_ULL(13)
+#define MSR_AMD64_SNP_RESERVED_BIT15 BIT_ULL(15)
+#define MSR_AMD64_SNP_RESERVED_MASK GENMASK_ULL(63, 18)
+
#define MSR_AMD64_VIRT_SPEC_CTRL 0xc001011f
/* AMD Collaborative Processor Performance Control MSRs */
diff --git a/arch/x86/include/uapi/asm/svm.h b/arch/x86/include/uapi/asm/svm.h
index f69c168391aa58..80e1df482337df 100644
--- a/arch/x86/include/uapi/asm/svm.h
+++ b/arch/x86/include/uapi/asm/svm.h
@@ -116,6 +116,12 @@
#define SVM_VMGEXIT_AP_CREATE 1
#define SVM_VMGEXIT_AP_DESTROY 2
#define SVM_VMGEXIT_HV_FEATURES 0x8000fffd
+#define SVM_VMGEXIT_TERM_REQUEST 0x8000fffe
+#define SVM_VMGEXIT_TERM_REASON(reason_set, reason_code) \
+ /* SW_EXITINFO1[3:0] */ \
+ (((((u64)reason_set) & 0xf)) | \
+ /* SW_EXITINFO1[11:4] */ \
+ ((((u64)reason_code) & 0xff) << 4))
#define SVM_VMGEXIT_UNSUPPORTED_EVENT 0x8000ffff
/* Exit code reserved for hypervisor/software use */
diff --git a/arch/x86/kernel/callthunks.c b/arch/x86/kernel/callthunks.c
index 7d2c75ec9a8cd9..ffea98f9064be6 100644
--- a/arch/x86/kernel/callthunks.c
+++ b/arch/x86/kernel/callthunks.c
@@ -119,7 +119,7 @@ static bool is_coretext(const struct core_text *ct, void *addr)
return within_module_coretext(addr);
}
-static __init_or_module bool skip_addr(void *dest)
+static bool skip_addr(void *dest)
{
if (dest == error_entry)
return true;
@@ -181,7 +181,7 @@ static const u8 nops[] = {
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
};
-static __init_or_module void *patch_dest(void *dest, bool direct)
+static void *patch_dest(void *dest, bool direct)
{
unsigned int tsize = SKL_TMPL_SIZE;
u8 *pad = dest - tsize;
diff --git a/arch/x86/kernel/cpu/aperfmperf.c b/arch/x86/kernel/cpu/aperfmperf.c
index 1f60a2b279368e..fdbb5f07448fad 100644
--- a/arch/x86/kernel/cpu/aperfmperf.c
+++ b/arch/x86/kernel/cpu/aperfmperf.c
@@ -330,7 +330,16 @@ static void __init bp_init_freq_invariance(void)
static void disable_freq_invariance_workfn(struct work_struct *work)
{
+ int cpu;
+
static_branch_disable(&arch_scale_freq_key);
+
+ /*
+ * Set arch_freq_scale to a default value on all cpus
+ * This negates the effect of scaling
+ */
+ for_each_possible_cpu(cpu)
+ per_cpu(arch_freq_scale, cpu) = SCHED_CAPACITY_SCALE;
}
static DECLARE_WORK(disable_freq_invariance_work,
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index d970ddb0cc65be..bca0bd8f484641 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -1981,6 +1981,8 @@ static int ib_prctl_set(struct task_struct *task, unsigned long ctrl)
if (ctrl == PR_SPEC_FORCE_DISABLE)
task_set_spec_ib_force_disable(task);
task_update_spec_tif(task);
+ if (task == current)
+ indirect_branch_prediction_barrier();
break;
default:
return -ERANGE;
diff --git a/arch/x86/kernel/cpu/resctrl/monitor.c b/arch/x86/kernel/cpu/resctrl/monitor.c
index efe0c30d3a12d5..77538abeb72afe 100644
--- a/arch/x86/kernel/cpu/resctrl/monitor.c
+++ b/arch/x86/kernel/cpu/resctrl/monitor.c
@@ -146,6 +146,30 @@ static inline struct rmid_entry *__rmid_entry(u32 rmid)
return entry;
}
+static int __rmid_read(u32 rmid, enum resctrl_event_id eventid, u64 *val)
+{
+ u64 msr_val;
+
+ /*
+ * As per the SDM, when IA32_QM_EVTSEL.EvtID (bits 7:0) is configured
+ * with a valid event code for supported resource type and the bits
+ * IA32_QM_EVTSEL.RMID (bits 41:32) are configured with valid RMID,
+ * IA32_QM_CTR.data (bits 61:0) reports the monitored data.
+ * IA32_QM_CTR.Error (bit 63) and IA32_QM_CTR.Unavailable (bit 62)
+ * are error bits.
+ */
+ wrmsr(MSR_IA32_QM_EVTSEL, eventid, rmid);
+ rdmsrl(MSR_IA32_QM_CTR, msr_val);
+
+ if (msr_val & RMID_VAL_ERROR)
+ return -EIO;
+ if (msr_val & RMID_VAL_UNAVAIL)
+ return -EINVAL;
+
+ *val = msr_val;
+ return 0;
+}
+
static struct arch_mbm_state *get_arch_mbm_state(struct rdt_hw_domain *hw_dom,
u32 rmid,
enum resctrl_event_id eventid)
@@ -172,8 +196,12 @@ void resctrl_arch_reset_rmid(struct rdt_resource *r, struct rdt_domain *d,
struct arch_mbm_state *am;
am = get_arch_mbm_state(hw_dom, rmid, eventid);
- if (am)
+ if (am) {
memset(am, 0, sizeof(*am));
+
+ /* Record any initial, non-zero count value. */
+ __rmid_read(rmid, eventid, &am->prev_msr);
+ }
}
static u64 mbm_overflow_count(u64 prev_msr, u64 cur_msr, unsigned int width)
@@ -191,25 +219,14 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_domain *d,
struct rdt_hw_domain *hw_dom = resctrl_to_arch_dom(d);
struct arch_mbm_state *am;
u64 msr_val, chunks;
+ int ret;
if (!cpumask_test_cpu(smp_processor_id(), &d->cpu_mask))
return -EINVAL;
- /*
- * As per the SDM, when IA32_QM_EVTSEL.EvtID (bits 7:0) is configured
- * with a valid event code for supported resource type and the bits
- * IA32_QM_EVTSEL.RMID (bits 41:32) are configured with valid RMID,
- * IA32_QM_CTR.data (bits 61:0) reports the monitored data.
- * IA32_QM_CTR.Error (bit 63) and IA32_QM_CTR.Unavailable (bit 62)
- * are error bits.
- */
- wrmsr(MSR_IA32_QM_EVTSEL, eventid, rmid);
- rdmsrl(MSR_IA32_QM_CTR, msr_val);
-
- if (msr_val & RMID_VAL_ERROR)
- return -EIO;
- if (msr_val & RMID_VAL_UNAVAIL)
- return -EINVAL;
+ ret = __rmid_read(rmid, eventid, &msr_val);
+ if (ret)
+ return ret;
am = get_arch_mbm_state(hw_dom, rmid, eventid);
if (am) {
diff --git a/arch/x86/kernel/cpu/resctrl/rdtgroup.c b/arch/x86/kernel/cpu/resctrl/rdtgroup.c
index e5a48f05e7876a..5993da21d82255 100644
--- a/arch/x86/kernel/cpu/resctrl/rdtgroup.c
+++ b/arch/x86/kernel/cpu/resctrl/rdtgroup.c
@@ -580,8 +580,10 @@ static int __rdtgroup_move_task(struct task_struct *tsk,
/*
* Ensure the task's closid and rmid are written before determining if
* the task is current that will decide if it will be interrupted.
+ * This pairs with the full barrier between the rq->curr update and
+ * resctrl_sched_in() during context switch.
*/
- barrier();
+ smp_mb();
/*
* By now, the task's closid and rmid are set. If the task is current
@@ -2402,6 +2404,14 @@ static void rdt_move_group_tasks(struct rdtgroup *from, struct rdtgroup *to,
WRITE_ONCE(t->rmid, to->mon.rmid);
/*
+ * Order the closid/rmid stores above before the loads
+ * in task_curr(). This pairs with the full barrier
+ * between the rq->curr update and resctrl_sched_in()
+ * during context switch.
+ */
+ smp_mb();
+
+ /*
* If the task is on a CPU, set the CPU in the mask.
* The detection is inaccurate as tasks might move or
* schedule before the smp function call takes place.
diff --git a/arch/x86/kernel/crash.c b/arch/x86/kernel/crash.c
index 9730c88530fc8b..305514431f26e0 100644
--- a/arch/x86/kernel/crash.c
+++ b/arch/x86/kernel/crash.c
@@ -401,10 +401,8 @@ int crash_load_segments(struct kimage *image)
kbuf.buf_align = ELF_CORE_HEADER_ALIGN;
kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
ret = kexec_add_buffer(&kbuf);
- if (ret) {
- vfree((void *)image->elf_headers);
+ if (ret)
return ret;
- }
image->elf_load_addr = kbuf.mem;
pr_debug("Loaded ELF headers at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
image->elf_load_addr, kbuf.bufsz, kbuf.memsz);
diff --git a/arch/x86/kernel/i8259.c b/arch/x86/kernel/i8259.c
index 3aa5304200c507..4d8aff05a509ec 100644
--- a/arch/x86/kernel/i8259.c
+++ b/arch/x86/kernel/i8259.c
@@ -114,6 +114,7 @@ static void make_8259A_irq(unsigned int irq)
disable_irq_nosync(irq);
io_apic_irqs &= ~(1<<irq);
irq_set_chip_and_handler(irq, &i8259A_chip, handle_level_irq);
+ irq_set_status_flags(irq, IRQ_LEVEL);
enable_irq(irq);
lapic_assign_legacy_vector(irq, true);
}
diff --git a/arch/x86/kernel/irqinit.c b/arch/x86/kernel/irqinit.c
index beb1bada1b0ab2..c683666876f1c7 100644
--- a/arch/x86/kernel/irqinit.c
+++ b/arch/x86/kernel/irqinit.c
@@ -65,8 +65,10 @@ void __init init_ISA_irqs(void)
legacy_pic->init(0);
- for (i = 0; i < nr_legacy_irqs(); i++)
+ for (i = 0; i < nr_legacy_irqs(); i++) {
irq_set_chip_and_handler(i, chip, handle_level_irq);
+ irq_set_status_flags(i, IRQ_LEVEL);
+ }
}
void __init init_IRQ(void)
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index 66299682b6b7e2..695873c0f50b5b 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -37,6 +37,7 @@
#include <linux/extable.h>
#include <linux/kdebug.h>
#include <linux/kallsyms.h>
+#include <linux/kgdb.h>
#include <linux/ftrace.h>
#include <linux/kasan.h>
#include <linux/moduleloader.h>
@@ -281,12 +282,15 @@ static int can_probe(unsigned long paddr)
if (ret < 0)
return 0;
+#ifdef CONFIG_KGDB
/*
- * Another debugging subsystem might insert this breakpoint.
- * In that case, we can't recover it.
+ * If there is a dynamically installed kgdb sw breakpoint,
+ * this function should not be probed.
*/
- if (insn.opcode.bytes[0] == INT3_INSN_OPCODE)
+ if (insn.opcode.bytes[0] == INT3_INSN_OPCODE &&
+ kgdb_has_hit_break(addr))
return 0;
+#endif
addr += insn.length;
}
@@ -621,7 +625,7 @@ static int prepare_emulation(struct kprobe *p, struct insn *insn)
/* 1 byte conditional jump */
p->ainsn.emulate_op = kprobe_emulate_jcc;
p->ainsn.jcc.type = opcode & 0xf;
- p->ainsn.rel32 = *(char *)insn->immediate.bytes;
+ p->ainsn.rel32 = insn->immediate.value;
break;
case 0x0f:
opcode = insn->opcode.bytes[1];
diff --git a/arch/x86/kernel/kprobes/opt.c b/arch/x86/kernel/kprobes/opt.c
index e6b8c5362b9455..e57e07b0edb64c 100644
--- a/arch/x86/kernel/kprobes/opt.c
+++ b/arch/x86/kernel/kprobes/opt.c
@@ -15,6 +15,7 @@
#include <linux/extable.h>
#include <linux/kdebug.h>
#include <linux/kallsyms.h>
+#include <linux/kgdb.h>
#include <linux/ftrace.h>
#include <linux/objtool.h>
#include <linux/pgtable.h>
@@ -279,19 +280,6 @@ static int insn_is_indirect_jump(struct insn *insn)
return ret;
}
-static bool is_padding_int3(unsigned long addr, unsigned long eaddr)
-{
- unsigned char ops;
-
- for (; addr < eaddr; addr++) {
- if (get_kernel_nofault(ops, (void *)addr) < 0 ||
- ops != INT3_INSN_OPCODE)
- return false;
- }
-
- return true;
-}
-
/* Decode whole function to ensure any instructions don't jump into target */
static int can_optimize(unsigned long paddr)
{
@@ -334,15 +322,15 @@ static int can_optimize(unsigned long paddr)
ret = insn_decode_kernel(&insn, (void *)recovered_insn);
if (ret < 0)
return 0;
-
+#ifdef CONFIG_KGDB
/*
- * In the case of detecting unknown breakpoint, this could be
- * a padding INT3 between functions. Let's check that all the
- * rest of the bytes are also INT3.
+ * If there is a dynamically installed kgdb sw breakpoint,
+ * this function should not be probed.
*/
- if (insn.opcode.bytes[0] == INT3_INSN_OPCODE)
- return is_padding_int3(addr, paddr - offset + size) ? 1 : 0;
-
+ if (insn.opcode.bytes[0] == INT3_INSN_OPCODE &&
+ kgdb_has_hit_break(addr))
+ return 0;
+#endif
/* Recover address */
insn.kaddr = (void *)addr;
insn.next_byte = (void *)(addr + insn.length);
diff --git a/arch/x86/kernel/sev.c b/arch/x86/kernel/sev.c
index a428c62330d371..679026a640efd8 100644
--- a/arch/x86/kernel/sev.c
+++ b/arch/x86/kernel/sev.c
@@ -1536,32 +1536,32 @@ static enum es_result vc_handle_mmio_movs(struct es_em_ctxt *ctxt,
static enum es_result vc_handle_mmio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
{
struct insn *insn = &ctxt->insn;
+ enum insn_mmio_type mmio;
unsigned int bytes = 0;
- enum mmio_type mmio;
enum es_result ret;
u8 sign_byte;
long *reg_data;
mmio = insn_decode_mmio(insn, &bytes);
- if (mmio == MMIO_DECODE_FAILED)
+ if (mmio == INSN_MMIO_DECODE_FAILED)
return ES_DECODE_FAILED;
- if (mmio != MMIO_WRITE_IMM && mmio != MMIO_MOVS) {
+ if (mmio != INSN_MMIO_WRITE_IMM && mmio != INSN_MMIO_MOVS) {
reg_data = insn_get_modrm_reg_ptr(insn, ctxt->regs);
if (!reg_data)
return ES_DECODE_FAILED;
}
switch (mmio) {
- case MMIO_WRITE:
+ case INSN_MMIO_WRITE:
memcpy(ghcb->shared_buffer, reg_data, bytes);
ret = vc_do_mmio(ghcb, ctxt, bytes, false);
break;
- case MMIO_WRITE_IMM:
+ case INSN_MMIO_WRITE_IMM:
memcpy(ghcb->shared_buffer, insn->immediate1.bytes, bytes);
ret = vc_do_mmio(ghcb, ctxt, bytes, false);
break;
- case MMIO_READ:
+ case INSN_MMIO_READ:
ret = vc_do_mmio(ghcb, ctxt, bytes, true);
if (ret)
break;
@@ -1572,7 +1572,7 @@ static enum es_result vc_handle_mmio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
memcpy(reg_data, ghcb->shared_buffer, bytes);
break;
- case MMIO_READ_ZERO_EXTEND:
+ case INSN_MMIO_READ_ZERO_EXTEND:
ret = vc_do_mmio(ghcb, ctxt, bytes, true);
if (ret)
break;
@@ -1581,7 +1581,7 @@ static enum es_result vc_handle_mmio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
memset(reg_data, 0, insn->opnd_bytes);
memcpy(reg_data, ghcb->shared_buffer, bytes);
break;
- case MMIO_READ_SIGN_EXTEND:
+ case INSN_MMIO_READ_SIGN_EXTEND:
ret = vc_do_mmio(ghcb, ctxt, bytes, true);
if (ret)
break;
@@ -1600,7 +1600,7 @@ static enum es_result vc_handle_mmio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
memset(reg_data, sign_byte, insn->opnd_bytes);
memcpy(reg_data, ghcb->shared_buffer, bytes);
break;
- case MMIO_MOVS:
+ case INSN_MMIO_MOVS:
ret = vc_handle_mmio_movs(ctxt, bytes);
break;
default:
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index b14653b61470c2..596061c1610e67 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -770,16 +770,22 @@ struct kvm_cpuid_array {
int nent;
};
+static struct kvm_cpuid_entry2 *get_next_cpuid(struct kvm_cpuid_array *array)
+{
+ if (array->nent >= array->maxnent)
+ return NULL;
+
+ return &array->entries[array->nent++];
+}
+
static struct kvm_cpuid_entry2 *do_host_cpuid(struct kvm_cpuid_array *array,
u32 function, u32 index)
{
- struct kvm_cpuid_entry2 *entry;
+ struct kvm_cpuid_entry2 *entry = get_next_cpuid(array);
- if (array->nent >= array->maxnent)
+ if (!entry)
return NULL;
- entry = &array->entries[array->nent++];
-
memset(entry, 0, sizeof(*entry));
entry->function = function;
entry->index = index;
@@ -956,22 +962,13 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
entry->edx = edx.full;
break;
}
- /*
- * Per Intel's SDM, the 0x1f is a superset of 0xb,
- * thus they can be handled by common code.
- */
case 0x1f:
case 0xb:
/*
- * Populate entries until the level type (ECX[15:8]) of the
- * previous entry is zero. Note, CPUID EAX.{0x1f,0xb}.0 is
- * the starting entry, filled by the primary do_host_cpuid().
+ * No topology; a valid topology is indicated by the presence
+ * of subleaf 1.
*/
- for (i = 1; entry->ecx & 0xff00; ++i) {
- entry = do_host_cpuid(array, function, i);
- if (!entry)
- goto out;
- }
+ entry->eax = entry->ebx = entry->ecx = 0;
break;
case 0xd: {
u64 permitted_xcr0 = kvm_caps.supported_xcr0 & xstate_get_guest_group_perm();
@@ -1202,6 +1199,9 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function)
entry->ebx = entry->ecx = entry->edx = 0;
break;
case 0x8000001e:
+ /* Do not return host topology information. */
+ entry->eax = entry->ebx = entry->ecx = 0;
+ entry->edx = 0; /* reserved */
break;
case 0x8000001F:
if (!kvm_cpu_cap_has(X86_FEATURE_SEV)) {
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index 2c7f2a26421e82..e8296942a86821 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -1769,6 +1769,7 @@ static bool hv_is_vp_in_sparse_set(u32 vp_id, u64 valid_bank_mask, u64 sparse_ba
}
struct kvm_hv_hcall {
+ /* Hypercall input data */
u64 param;
u64 ingpa;
u64 outgpa;
@@ -1779,12 +1780,21 @@ struct kvm_hv_hcall {
bool fast;
bool rep;
sse128_t xmm[HV_HYPERCALL_MAX_XMM_REGISTERS];
+
+ /*
+ * Current read offset when KVM reads hypercall input data gradually,
+ * either offset in bytes from 'ingpa' for regular hypercalls or the
+ * number of already consumed 'XMM halves' for 'fast' hypercalls.
+ */
+ union {
+ gpa_t data_offset;
+ int consumed_xmm_halves;
+ };
};
static int kvm_hv_get_hc_data(struct kvm *kvm, struct kvm_hv_hcall *hc,
- u16 orig_cnt, u16 cnt_cap, u64 *data,
- int consumed_xmm_halves, gpa_t offset)
+ u16 orig_cnt, u16 cnt_cap, u64 *data)
{
/*
* Preserve the original count when ignoring entries via a "cap", KVM
@@ -1799,11 +1809,11 @@ static int kvm_hv_get_hc_data(struct kvm *kvm, struct kvm_hv_hcall *hc,
* Each XMM holds two sparse banks, but do not count halves that
* have already been consumed for hypercall parameters.
*/
- if (orig_cnt > 2 * HV_HYPERCALL_MAX_XMM_REGISTERS - consumed_xmm_halves)
+ if (orig_cnt > 2 * HV_HYPERCALL_MAX_XMM_REGISTERS - hc->consumed_xmm_halves)
return HV_STATUS_INVALID_HYPERCALL_INPUT;
for (i = 0; i < cnt; i++) {
- j = i + consumed_xmm_halves;
+ j = i + hc->consumed_xmm_halves;
if (j % 2)
data[i] = sse128_hi(hc->xmm[j / 2]);
else
@@ -1812,27 +1822,24 @@ static int kvm_hv_get_hc_data(struct kvm *kvm, struct kvm_hv_hcall *hc,
return 0;
}
- return kvm_read_guest(kvm, hc->ingpa + offset, data,
+ return kvm_read_guest(kvm, hc->ingpa + hc->data_offset, data,
cnt * sizeof(*data));
}
static u64 kvm_get_sparse_vp_set(struct kvm *kvm, struct kvm_hv_hcall *hc,
- u64 *sparse_banks, int consumed_xmm_halves,
- gpa_t offset)
+ u64 *sparse_banks)
{
if (hc->var_cnt > HV_MAX_SPARSE_VCPU_BANKS)
return -EINVAL;
/* Cap var_cnt to ignore banks that cannot contain a legal VP index. */
return kvm_hv_get_hc_data(kvm, hc, hc->var_cnt, KVM_HV_MAX_SPARSE_VCPU_SET_BITS,
- sparse_banks, consumed_xmm_halves, offset);
+ sparse_banks);
}
-static int kvm_hv_get_tlb_flush_entries(struct kvm *kvm, struct kvm_hv_hcall *hc, u64 entries[],
- int consumed_xmm_halves, gpa_t offset)
+static int kvm_hv_get_tlb_flush_entries(struct kvm *kvm, struct kvm_hv_hcall *hc, u64 entries[])
{
- return kvm_hv_get_hc_data(kvm, hc, hc->rep_cnt, hc->rep_cnt,
- entries, consumed_xmm_halves, offset);
+ return kvm_hv_get_hc_data(kvm, hc, hc->rep_cnt, hc->rep_cnt, entries);
}
static void hv_tlb_flush_enqueue(struct kvm_vcpu *vcpu,
@@ -1926,8 +1933,6 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
struct kvm_vcpu *v;
unsigned long i;
bool all_cpus;
- int consumed_xmm_halves = 0;
- gpa_t data_offset;
/*
* The Hyper-V TLFS doesn't allow more than HV_MAX_SPARSE_VCPU_BANKS
@@ -1955,12 +1960,12 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
flush.address_space = hc->ingpa;
flush.flags = hc->outgpa;
flush.processor_mask = sse128_lo(hc->xmm[0]);
- consumed_xmm_halves = 1;
+ hc->consumed_xmm_halves = 1;
} else {
if (unlikely(kvm_read_guest(kvm, hc->ingpa,
&flush, sizeof(flush))))
return HV_STATUS_INVALID_HYPERCALL_INPUT;
- data_offset = sizeof(flush);
+ hc->data_offset = sizeof(flush);
}
trace_kvm_hv_flush_tlb(flush.processor_mask,
@@ -1985,12 +1990,12 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
flush_ex.flags = hc->outgpa;
memcpy(&flush_ex.hv_vp_set,
&hc->xmm[0], sizeof(hc->xmm[0]));
- consumed_xmm_halves = 2;
+ hc->consumed_xmm_halves = 2;
} else {
if (unlikely(kvm_read_guest(kvm, hc->ingpa, &flush_ex,
sizeof(flush_ex))))
return HV_STATUS_INVALID_HYPERCALL_INPUT;
- data_offset = sizeof(flush_ex);
+ hc->data_offset = sizeof(flush_ex);
}
trace_kvm_hv_flush_tlb_ex(flush_ex.hv_vp_set.valid_bank_mask,
@@ -2009,8 +2014,7 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
if (!hc->var_cnt)
goto ret_success;
- if (kvm_get_sparse_vp_set(kvm, hc, sparse_banks,
- consumed_xmm_halves, data_offset))
+ if (kvm_get_sparse_vp_set(kvm, hc, sparse_banks))
return HV_STATUS_INVALID_HYPERCALL_INPUT;
}
@@ -2021,8 +2025,10 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
* consumed_xmm_halves to make sure TLB flush entries are read
* from the correct offset.
*/
- data_offset += hc->var_cnt * sizeof(sparse_banks[0]);
- consumed_xmm_halves += hc->var_cnt;
+ if (hc->fast)
+ hc->consumed_xmm_halves += hc->var_cnt;
+ else
+ hc->data_offset += hc->var_cnt * sizeof(sparse_banks[0]);
}
if (hc->code == HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE ||
@@ -2030,8 +2036,7 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
hc->rep_cnt > ARRAY_SIZE(__tlb_flush_entries)) {
tlb_flush_entries = NULL;
} else {
- if (kvm_hv_get_tlb_flush_entries(kvm, hc, __tlb_flush_entries,
- consumed_xmm_halves, data_offset))
+ if (kvm_hv_get_tlb_flush_entries(kvm, hc, __tlb_flush_entries))
return HV_STATUS_INVALID_HYPERCALL_INPUT;
tlb_flush_entries = __tlb_flush_entries;
}
@@ -2180,9 +2185,13 @@ static u64 kvm_hv_send_ipi(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
if (!hc->var_cnt)
goto ret_success;
- if (kvm_get_sparse_vp_set(kvm, hc, sparse_banks, 1,
- offsetof(struct hv_send_ipi_ex,
- vp_set.bank_contents)))
+ if (!hc->fast)
+ hc->data_offset = offsetof(struct hv_send_ipi_ex,
+ vp_set.bank_contents);
+ else
+ hc->consumed_xmm_halves = 1;
+
+ if (kvm_get_sparse_vp_set(kvm, hc, sparse_banks))
return HV_STATUS_INVALID_HYPERCALL_INPUT;
}
diff --git a/arch/x86/kvm/irq_comm.c b/arch/x86/kvm/irq_comm.c
index 0687162c4f227d..3742d9adacfc1a 100644
--- a/arch/x86/kvm/irq_comm.c
+++ b/arch/x86/kvm/irq_comm.c
@@ -426,8 +426,9 @@ void kvm_scan_ioapic_routes(struct kvm_vcpu *vcpu,
kvm_set_msi_irq(vcpu->kvm, entry, &irq);
if (irq.trig_mode &&
- kvm_apic_match_dest(vcpu, NULL, APIC_DEST_NOSHORT,
- irq.dest_id, irq.dest_mode))
+ (kvm_apic_match_dest(vcpu, NULL, APIC_DEST_NOSHORT,
+ irq.dest_id, irq.dest_mode) ||
+ kvm_apic_pending_eoi(vcpu, irq.vector)))
__set_bit(irq.vector, ioapic_handled_vectors);
}
}
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index 28e3769066e215..58c3242fcc7ad9 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -188,11 +188,11 @@ static inline bool lapic_in_kernel(struct kvm_vcpu *vcpu)
extern struct static_key_false_deferred apic_hw_disabled;
-static inline int kvm_apic_hw_enabled(struct kvm_lapic *apic)
+static inline bool kvm_apic_hw_enabled(struct kvm_lapic *apic)
{
if (static_branch_unlikely(&apic_hw_disabled.key))
return apic->vcpu->arch.apic_base & MSR_IA32_APICBASE_ENABLE;
- return MSR_IA32_APICBASE_ENABLE;
+ return true;
}
extern struct static_key_false_deferred apic_sw_disabled;
diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h
index 1f03701b943a1c..6f54dc9409c943 100644
--- a/arch/x86/kvm/mmu/spte.h
+++ b/arch/x86/kvm/mmu/spte.h
@@ -363,7 +363,7 @@ static __always_inline bool is_rsvd_spte(struct rsvd_bits_validate *rsvd_check,
* A shadow-present leaf SPTE may be non-writable for 4 possible reasons:
*
* 1. To intercept writes for dirty logging. KVM write-protects huge pages
- * so that they can be split be split down into the dirty logging
+ * so that they can be split down into the dirty logging
* granularity (4KiB) whenever the guest writes to them. KVM also
* write-protects 4KiB pages so that writes can be recorded in the dirty log
* (e.g. if not using PML). SPTEs are write-protected for dirty logging
diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c
index 771210ce518112..d6df38d371a00c 100644
--- a/arch/x86/kvm/mmu/tdp_mmu.c
+++ b/arch/x86/kvm/mmu/tdp_mmu.c
@@ -1074,7 +1074,9 @@ static int tdp_mmu_map_handle_target_level(struct kvm_vcpu *vcpu,
int ret = RET_PF_FIXED;
bool wrprot = false;
- WARN_ON(sp->role.level != fault->goal_level);
+ if (WARN_ON_ONCE(sp->role.level != fault->goal_level))
+ return RET_PF_RETRY;
+
if (unlikely(!fault->slot))
new_spte = make_mmio_spte(vcpu, iter->gfn, ACC_ALL);
else
@@ -1173,9 +1175,6 @@ int kvm_tdp_mmu_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
if (fault->nx_huge_page_workaround_enabled)
disallowed_hugepage_adjust(fault, iter.old_spte, iter.level);
- if (iter.level == fault->goal_level)
- break;
-
/*
* If SPTE has been frozen by another thread, just give up and
* retry, avoiding unnecessary page table allocation and free.
@@ -1183,6 +1182,9 @@ int kvm_tdp_mmu_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
if (is_removed_spte(iter.old_spte))
goto retry;
+ if (iter.level == fault->goal_level)
+ goto map_target_level;
+
/* Step down into the lower level page table if it exists. */
if (is_shadow_present_pte(iter.old_spte) &&
!is_large_pte(iter.old_spte))
@@ -1203,8 +1205,8 @@ int kvm_tdp_mmu_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
r = tdp_mmu_link_sp(kvm, &iter, sp, true);
/*
- * Also force the guest to retry the access if the upper level SPTEs
- * aren't in place.
+ * Force the guest to retry if installing an upper level SPTE
+ * failed, e.g. because a different task modified the SPTE.
*/
if (r) {
tdp_mmu_free_sp(sp);
@@ -1214,11 +1216,20 @@ int kvm_tdp_mmu_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
if (fault->huge_page_disallowed &&
fault->req_level >= iter.level) {
spin_lock(&kvm->arch.tdp_mmu_pages_lock);
- track_possible_nx_huge_page(kvm, sp);
+ if (sp->nx_huge_page_disallowed)
+ track_possible_nx_huge_page(kvm, sp);
spin_unlock(&kvm->arch.tdp_mmu_pages_lock);
}
}
+ /*
+ * The walk aborted before reaching the target level, e.g. because the
+ * iterator detected an upper level SPTE was frozen during traversal.
+ */
+ WARN_ON_ONCE(iter.level == fault->goal_level);
+ goto retry;
+
+map_target_level:
ret = tdp_mmu_map_handle_target_level(vcpu, fault, &iter);
retry:
diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c
index 684393c2210523..eb594620dd75a1 100644
--- a/arch/x86/kvm/pmu.c
+++ b/arch/x86/kvm/pmu.c
@@ -238,7 +238,8 @@ static bool pmc_resume_counter(struct kvm_pmc *pmc)
return false;
/* recalibrate sample period and check if it's accepted by perf core */
- if (perf_event_period(pmc->perf_event,
+ if (is_sampling_event(pmc->perf_event) &&
+ perf_event_period(pmc->perf_event,
get_sample_period(pmc, pmc->counter)))
return false;
diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h
index 85ff3c0588bac0..cdb91009701dd1 100644
--- a/arch/x86/kvm/pmu.h
+++ b/arch/x86/kvm/pmu.h
@@ -140,7 +140,8 @@ static inline u64 get_sample_period(struct kvm_pmc *pmc, u64 counter_value)
static inline void pmc_update_sample_period(struct kvm_pmc *pmc)
{
- if (!pmc->perf_event || pmc->is_paused)
+ if (!pmc->perf_event || pmc->is_paused ||
+ !is_sampling_event(pmc->perf_event))
return;
perf_event_period(pmc->perf_event,
diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c
index bc9cd7086fa972..add65dd597569e 100644
--- a/arch/x86/kvm/svm/nested.c
+++ b/arch/x86/kvm/svm/nested.c
@@ -138,15 +138,13 @@ void recalc_intercepts(struct vcpu_svm *svm)
c->intercepts[i] = h->intercepts[i];
if (g->int_ctl & V_INTR_MASKING_MASK) {
- /* We only want the cr8 intercept bits of L1 */
- vmcb_clr_intercept(c, INTERCEPT_CR8_READ);
- vmcb_clr_intercept(c, INTERCEPT_CR8_WRITE);
-
/*
- * Once running L2 with HF_VINTR_MASK, EFLAGS.IF does not
- * affect any interrupt we may want to inject; therefore,
- * interrupt window vmexits are irrelevant to L0.
+ * Once running L2 with HF_VINTR_MASK, EFLAGS.IF and CR8
+ * does not affect any interrupt we may want to inject;
+ * therefore, writes to CR8 are irrelevant to L0, as are
+ * interrupt window vmexits.
*/
+ vmcb_clr_intercept(c, INTERCEPT_CR8_WRITE);
vmcb_clr_intercept(c, INTERCEPT_VINTR);
}
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index b6f4411b613e91..d93c715cda6ab3 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -5296,10 +5296,19 @@ static int handle_vmclear(struct kvm_vcpu *vcpu)
if (vmptr == vmx->nested.current_vmptr)
nested_release_vmcs12(vcpu);
- kvm_vcpu_write_guest(vcpu,
- vmptr + offsetof(struct vmcs12,
- launch_state),
- &zero, sizeof(zero));
+ /*
+ * Silently ignore memory errors on VMCLEAR, Intel's pseudocode
+ * for VMCLEAR includes a "ensure that data for VMCS referenced
+ * by the operand is in memory" clause that guards writes to
+ * memory, i.e. doing nothing for I/O is architecturally valid.
+ *
+ * FIXME: Suppress failures if and only if no memslot is found,
+ * i.e. exit to userspace if __copy_to_user() fails.
+ */
+ (void)kvm_vcpu_write_guest(vcpu,
+ vmptr + offsetof(struct vmcs12,
+ launch_state),
+ &zero, sizeof(zero));
} else if (vmx->nested.hv_evmcs && vmptr == vmx->nested.hv_evmcs_vmptr) {
nested_release_evmcs(vcpu);
}
@@ -6873,7 +6882,8 @@ void nested_vmx_setup_ctls_msrs(struct vmcs_config *vmcs_conf, u32 ept_caps)
SECONDARY_EXEC_ENABLE_INVPCID |
SECONDARY_EXEC_RDSEED_EXITING |
SECONDARY_EXEC_XSAVES |
- SECONDARY_EXEC_TSC_SCALING;
+ SECONDARY_EXEC_TSC_SCALING |
+ SECONDARY_EXEC_ENABLE_USR_WAIT_PAUSE;
/*
* We can emulate "VMCS shadowing," even if the hardware
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index fe5615fd8295c4..7eec0226d56a24 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -3440,18 +3440,15 @@ static u32 vmx_segment_access_rights(struct kvm_segment *var)
{
u32 ar;
- if (var->unusable || !var->present)
- ar = 1 << 16;
- else {
- ar = var->type & 15;
- ar |= (var->s & 1) << 4;
- ar |= (var->dpl & 3) << 5;
- ar |= (var->present & 1) << 7;
- ar |= (var->avl & 1) << 12;
- ar |= (var->l & 1) << 13;
- ar |= (var->db & 1) << 14;
- ar |= (var->g & 1) << 15;
- }
+ ar = var->type & 15;
+ ar |= (var->s & 1) << 4;
+ ar |= (var->dpl & 3) << 5;
+ ar |= (var->present & 1) << 7;
+ ar |= (var->avl & 1) << 12;
+ ar |= (var->l & 1) << 13;
+ ar |= (var->db & 1) << 14;
+ ar |= (var->g & 1) << 15;
+ ar |= (var->unusable || !var->present) << 16;
return ar;
}
@@ -4459,6 +4456,13 @@ vmx_adjust_secondary_exec_control(struct vcpu_vmx *vmx, u32 *exec_control,
* controls for features that are/aren't exposed to the guest.
*/
if (nested) {
+ /*
+ * All features that can be added or removed to VMX MSRs must
+ * be supported in the first place for nested virtualization.
+ */
+ if (WARN_ON_ONCE(!(vmcs_config.nested.secondary_ctls_high & control)))
+ enabled = false;
+
if (enabled)
vmx->nested.msrs.secondary_ctls_high |= control;
else
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 312aea1854ae6b..da4bbd043a7b6c 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -13132,6 +13132,9 @@ int kvm_handle_memory_failure(struct kvm_vcpu *vcpu, int r,
struct x86_exception *e)
{
if (r == X86EMUL_PROPAGATE_FAULT) {
+ if (KVM_BUG_ON(!e, vcpu->kvm))
+ return -EIO;
+
kvm_inject_emulated_page_fault(vcpu, e);
return 1;
}
diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
index d7af402402484b..8fd41f5deae39f 100644
--- a/arch/x86/kvm/xen.c
+++ b/arch/x86/kvm/xen.c
@@ -41,7 +41,7 @@ static int kvm_xen_shared_info_init(struct kvm *kvm, gfn_t gfn)
int ret = 0;
int idx = srcu_read_lock(&kvm->srcu);
- if (gfn == GPA_INVALID) {
+ if (gfn == KVM_XEN_INVALID_GFN) {
kvm_gpc_deactivate(gpc);
goto out;
}
@@ -271,7 +271,15 @@ static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic)
* Attempt to obtain the GPC lock on *both* (if there are two)
* gfn_to_pfn caches that cover the region.
*/
- read_lock_irqsave(&gpc1->lock, flags);
+ if (atomic) {
+ local_irq_save(flags);
+ if (!read_trylock(&gpc1->lock)) {
+ local_irq_restore(flags);
+ return;
+ }
+ } else {
+ read_lock_irqsave(&gpc1->lock, flags);
+ }
while (!kvm_gpc_check(gpc1, user_len1)) {
read_unlock_irqrestore(&gpc1->lock, flags);
@@ -304,9 +312,18 @@ static void kvm_xen_update_runstate_guest(struct kvm_vcpu *v, bool atomic)
* The guest's runstate_info is split across two pages and we
* need to hold and validate both GPCs simultaneously. We can
* declare a lock ordering GPC1 > GPC2 because nothing else
- * takes them more than one at a time.
+ * takes them more than one at a time. Set a subclass on the
+ * gpc1 lock to make lockdep shut up about it.
*/
- read_lock(&gpc2->lock);
+ lock_set_subclass(&gpc1->lock.dep_map, 1, _THIS_IP_);
+ if (atomic) {
+ if (!read_trylock(&gpc2->lock)) {
+ read_unlock_irqrestore(&gpc1->lock, flags);
+ return;
+ }
+ } else {
+ read_lock(&gpc2->lock);
+ }
if (!kvm_gpc_check(gpc2, user_len2)) {
read_unlock(&gpc2->lock);
@@ -590,26 +607,26 @@ int kvm_xen_hvm_set_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data)
if (!IS_ENABLED(CONFIG_64BIT) && data->u.long_mode) {
r = -EINVAL;
} else {
- mutex_lock(&kvm->lock);
+ mutex_lock(&kvm->arch.xen.xen_lock);
kvm->arch.xen.long_mode = !!data->u.long_mode;
- mutex_unlock(&kvm->lock);
+ mutex_unlock(&kvm->arch.xen.xen_lock);
r = 0;
}
break;
case KVM_XEN_ATTR_TYPE_SHARED_INFO:
- mutex_lock(&kvm->lock);
+ mutex_lock(&kvm->arch.xen.xen_lock);
r = kvm_xen_shared_info_init(kvm, data->u.shared_info.gfn);
- mutex_unlock(&kvm->lock);
+ mutex_unlock(&kvm->arch.xen.xen_lock);
break;
case KVM_XEN_ATTR_TYPE_UPCALL_VECTOR:
if (data->u.vector && data->u.vector < 0x10)
r = -EINVAL;
else {
- mutex_lock(&kvm->lock);
+ mutex_lock(&kvm->arch.xen.xen_lock);
kvm->arch.xen.upcall_vector = data->u.vector;
- mutex_unlock(&kvm->lock);
+ mutex_unlock(&kvm->arch.xen.xen_lock);
r = 0;
}
break;
@@ -619,9 +636,9 @@ int kvm_xen_hvm_set_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data)
break;
case KVM_XEN_ATTR_TYPE_XEN_VERSION:
- mutex_lock(&kvm->lock);
+ mutex_lock(&kvm->arch.xen.xen_lock);
kvm->arch.xen.xen_version = data->u.xen_version;
- mutex_unlock(&kvm->lock);
+ mutex_unlock(&kvm->arch.xen.xen_lock);
r = 0;
break;
@@ -630,9 +647,9 @@ int kvm_xen_hvm_set_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data)
r = -EOPNOTSUPP;
break;
}
- mutex_lock(&kvm->lock);
+ mutex_lock(&kvm->arch.xen.xen_lock);
kvm->arch.xen.runstate_update_flag = !!data->u.runstate_update_flag;
- mutex_unlock(&kvm->lock);
+ mutex_unlock(&kvm->arch.xen.xen_lock);
r = 0;
break;
@@ -647,7 +664,7 @@ int kvm_xen_hvm_get_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data)
{
int r = -ENOENT;
- mutex_lock(&kvm->lock);
+ mutex_lock(&kvm->arch.xen.xen_lock);
switch (data->type) {
case KVM_XEN_ATTR_TYPE_LONG_MODE:
@@ -659,7 +676,7 @@ int kvm_xen_hvm_get_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data)
if (kvm->arch.xen.shinfo_cache.active)
data->u.shared_info.gfn = gpa_to_gfn(kvm->arch.xen.shinfo_cache.gpa);
else
- data->u.shared_info.gfn = GPA_INVALID;
+ data->u.shared_info.gfn = KVM_XEN_INVALID_GFN;
r = 0;
break;
@@ -686,7 +703,7 @@ int kvm_xen_hvm_get_attr(struct kvm *kvm, struct kvm_xen_hvm_attr *data)
break;
}
- mutex_unlock(&kvm->lock);
+ mutex_unlock(&kvm->arch.xen.xen_lock);
return r;
}
@@ -694,7 +711,7 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
{
int idx, r = -ENOENT;
- mutex_lock(&vcpu->kvm->lock);
+ mutex_lock(&vcpu->kvm->arch.xen.xen_lock);
idx = srcu_read_lock(&vcpu->kvm->srcu);
switch (data->type) {
@@ -705,7 +722,7 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
BUILD_BUG_ON(offsetof(struct vcpu_info, time) !=
offsetof(struct compat_vcpu_info, time));
- if (data->u.gpa == GPA_INVALID) {
+ if (data->u.gpa == KVM_XEN_INVALID_GPA) {
kvm_gpc_deactivate(&vcpu->arch.xen.vcpu_info_cache);
r = 0;
break;
@@ -719,7 +736,7 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
break;
case KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO:
- if (data->u.gpa == GPA_INVALID) {
+ if (data->u.gpa == KVM_XEN_INVALID_GPA) {
kvm_gpc_deactivate(&vcpu->arch.xen.vcpu_time_info_cache);
r = 0;
break;
@@ -739,7 +756,7 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
r = -EOPNOTSUPP;
break;
}
- if (data->u.gpa == GPA_INVALID) {
+ if (data->u.gpa == KVM_XEN_INVALID_GPA) {
r = 0;
deactivate_out:
kvm_gpc_deactivate(&vcpu->arch.xen.runstate_cache);
@@ -922,7 +939,7 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
}
srcu_read_unlock(&vcpu->kvm->srcu, idx);
- mutex_unlock(&vcpu->kvm->lock);
+ mutex_unlock(&vcpu->kvm->arch.xen.xen_lock);
return r;
}
@@ -930,14 +947,14 @@ int kvm_xen_vcpu_get_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
{
int r = -ENOENT;
- mutex_lock(&vcpu->kvm->lock);
+ mutex_lock(&vcpu->kvm->arch.xen.xen_lock);
switch (data->type) {
case KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO:
if (vcpu->arch.xen.vcpu_info_cache.active)
data->u.gpa = vcpu->arch.xen.vcpu_info_cache.gpa;
else
- data->u.gpa = GPA_INVALID;
+ data->u.gpa = KVM_XEN_INVALID_GPA;
r = 0;
break;
@@ -945,7 +962,7 @@ int kvm_xen_vcpu_get_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
if (vcpu->arch.xen.vcpu_time_info_cache.active)
data->u.gpa = vcpu->arch.xen.vcpu_time_info_cache.gpa;
else
- data->u.gpa = GPA_INVALID;
+ data->u.gpa = KVM_XEN_INVALID_GPA;
r = 0;
break;
@@ -1013,7 +1030,7 @@ int kvm_xen_vcpu_get_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
break;
}
- mutex_unlock(&vcpu->kvm->lock);
+ mutex_unlock(&vcpu->kvm->arch.xen.xen_lock);
return r;
}
@@ -1069,6 +1086,7 @@ int kvm_xen_write_hypercall_page(struct kvm_vcpu *vcpu, u64 data)
u8 blob_size = lm ? kvm->arch.xen_hvm_config.blob_size_64
: kvm->arch.xen_hvm_config.blob_size_32;
u8 *page;
+ int ret;
if (page_num >= blob_size)
return 1;
@@ -1079,10 +1097,10 @@ int kvm_xen_write_hypercall_page(struct kvm_vcpu *vcpu, u64 data)
if (IS_ERR(page))
return PTR_ERR(page);
- if (kvm_vcpu_write_guest(vcpu, page_addr, page, PAGE_SIZE)) {
- kfree(page);
+ ret = kvm_vcpu_write_guest(vcpu, page_addr, page, PAGE_SIZE);
+ kfree(page);
+ if (ret)
return 1;
- }
}
return 0;
}
@@ -1105,7 +1123,7 @@ int kvm_xen_hvm_config(struct kvm *kvm, struct kvm_xen_hvm_config *xhc)
xhc->blob_size_32 || xhc->blob_size_64))
return -EINVAL;
- mutex_lock(&kvm->lock);
+ mutex_lock(&kvm->arch.xen.xen_lock);
if (xhc->msr && !kvm->arch.xen_hvm_config.msr)
static_branch_inc(&kvm_xen_enabled.key);
@@ -1114,7 +1132,7 @@ int kvm_xen_hvm_config(struct kvm *kvm, struct kvm_xen_hvm_config *xhc)
memcpy(&kvm->arch.xen_hvm_config, xhc, sizeof(*xhc));
- mutex_unlock(&kvm->lock);
+ mutex_unlock(&kvm->arch.xen.xen_lock);
return 0;
}
@@ -1183,30 +1201,22 @@ static bool wait_pending_event(struct kvm_vcpu *vcpu, int nr_ports,
static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode,
u64 param, u64 *r)
{
- int idx, i;
struct sched_poll sched_poll;
evtchn_port_t port, *ports;
- gpa_t gpa;
+ struct x86_exception e;
+ int i;
if (!lapic_in_kernel(vcpu) ||
!(vcpu->kvm->arch.xen_hvm_config.flags & KVM_XEN_HVM_CONFIG_EVTCHN_SEND))
return false;
- idx = srcu_read_lock(&vcpu->kvm->srcu);
- gpa = kvm_mmu_gva_to_gpa_system(vcpu, param, NULL);
- srcu_read_unlock(&vcpu->kvm->srcu, idx);
- if (!gpa) {
- *r = -EFAULT;
- return true;
- }
-
if (IS_ENABLED(CONFIG_64BIT) && !longmode) {
struct compat_sched_poll sp32;
/* Sanity check that the compat struct definition is correct */
BUILD_BUG_ON(sizeof(sp32) != 16);
- if (kvm_vcpu_read_guest(vcpu, gpa, &sp32, sizeof(sp32))) {
+ if (kvm_read_guest_virt(vcpu, param, &sp32, sizeof(sp32), &e)) {
*r = -EFAULT;
return true;
}
@@ -1220,8 +1230,8 @@ static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode,
sched_poll.nr_ports = sp32.nr_ports;
sched_poll.timeout = sp32.timeout;
} else {
- if (kvm_vcpu_read_guest(vcpu, gpa, &sched_poll,
- sizeof(sched_poll))) {
+ if (kvm_read_guest_virt(vcpu, param, &sched_poll,
+ sizeof(sched_poll), &e)) {
*r = -EFAULT;
return true;
}
@@ -1243,18 +1253,13 @@ static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode,
} else
ports = &port;
+ if (kvm_read_guest_virt(vcpu, (gva_t)sched_poll.ports, ports,
+ sched_poll.nr_ports * sizeof(*ports), &e)) {
+ *r = -EFAULT;
+ return true;
+ }
+
for (i = 0; i < sched_poll.nr_ports; i++) {
- idx = srcu_read_lock(&vcpu->kvm->srcu);
- gpa = kvm_mmu_gva_to_gpa_system(vcpu,
- (gva_t)(sched_poll.ports + i),
- NULL);
- srcu_read_unlock(&vcpu->kvm->srcu, idx);
-
- if (!gpa || kvm_vcpu_read_guest(vcpu, gpa,
- &ports[i], sizeof(port))) {
- *r = -EFAULT;
- goto out;
- }
if (ports[i] >= max_evtchn_port(vcpu->kvm)) {
*r = -EINVAL;
goto out;
@@ -1330,9 +1335,8 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,
int vcpu_id, u64 param, u64 *r)
{
struct vcpu_set_singleshot_timer oneshot;
+ struct x86_exception e;
s64 delta;
- gpa_t gpa;
- int idx;
if (!kvm_xen_timer_enabled(vcpu))
return false;
@@ -1343,9 +1347,6 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,
*r = -EINVAL;
return true;
}
- idx = srcu_read_lock(&vcpu->kvm->srcu);
- gpa = kvm_mmu_gva_to_gpa_system(vcpu, param, NULL);
- srcu_read_unlock(&vcpu->kvm->srcu, idx);
/*
* The only difference for 32-bit compat is the 4 bytes of
@@ -1363,9 +1364,8 @@ static bool kvm_xen_hcall_vcpu_op(struct kvm_vcpu *vcpu, bool longmode, int cmd,
BUILD_BUG_ON(sizeof_field(struct compat_vcpu_set_singleshot_timer, flags) !=
sizeof_field(struct vcpu_set_singleshot_timer, flags));
- if (!gpa ||
- kvm_vcpu_read_guest(vcpu, gpa, &oneshot, longmode ? sizeof(oneshot) :
- sizeof(struct compat_vcpu_set_singleshot_timer))) {
+ if (kvm_read_guest_virt(vcpu, param, &oneshot, longmode ? sizeof(oneshot) :
+ sizeof(struct compat_vcpu_set_singleshot_timer), &e)) {
*r = -EFAULT;
return true;
}
@@ -1675,15 +1675,7 @@ static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm)
mm_borrowed = true;
}
- /*
- * For the irqfd workqueue, using the main kvm->lock mutex is
- * fine since this function is invoked from kvm_set_irq() with
- * no other lock held, no srcu. In future if it will be called
- * directly from a vCPU thread (e.g. on hypercall for an IPI)
- * then it may need to switch to using a leaf-node mutex for
- * serializing the shared_info mapping.
- */
- mutex_lock(&kvm->lock);
+ mutex_lock(&kvm->arch.xen.xen_lock);
/*
* It is theoretically possible for the page to be unmapped
@@ -1712,7 +1704,7 @@ static int kvm_xen_set_evtchn(struct kvm_xen_evtchn *xe, struct kvm *kvm)
srcu_read_unlock(&kvm->srcu, idx);
} while(!rc);
- mutex_unlock(&kvm->lock);
+ mutex_unlock(&kvm->arch.xen.xen_lock);
if (mm_borrowed)
kthread_unuse_mm(kvm->mm);
@@ -1825,20 +1817,20 @@ static int kvm_xen_eventfd_update(struct kvm *kvm,
{
u32 port = data->u.evtchn.send_port;
struct evtchnfd *evtchnfd;
+ int ret;
- if (!port || port >= max_evtchn_port(kvm))
- return -EINVAL;
-
- mutex_lock(&kvm->lock);
+ /* Protect writes to evtchnfd as well as the idr lookup. */
+ mutex_lock(&kvm->arch.xen.xen_lock);
evtchnfd = idr_find(&kvm->arch.xen.evtchn_ports, port);
- mutex_unlock(&kvm->lock);
+ ret = -ENOENT;
if (!evtchnfd)
- return -ENOENT;
+ goto out_unlock;
/* For an UPDATE, nothing may change except the priority/vcpu */
+ ret = -EINVAL;
if (evtchnfd->type != data->u.evtchn.type)
- return -EINVAL;
+ goto out_unlock;
/*
* Port cannot change, and if it's zero that was an eventfd
@@ -1846,20 +1838,21 @@ static int kvm_xen_eventfd_update(struct kvm *kvm,
*/
if (!evtchnfd->deliver.port.port ||
evtchnfd->deliver.port.port != data->u.evtchn.deliver.port.port)
- return -EINVAL;
+ goto out_unlock;
/* We only support 2 level event channels for now */
if (data->u.evtchn.deliver.port.priority != KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL)
- return -EINVAL;
+ goto out_unlock;
- mutex_lock(&kvm->lock);
evtchnfd->deliver.port.priority = data->u.evtchn.deliver.port.priority;
if (evtchnfd->deliver.port.vcpu_id != data->u.evtchn.deliver.port.vcpu) {
evtchnfd->deliver.port.vcpu_id = data->u.evtchn.deliver.port.vcpu;
evtchnfd->deliver.port.vcpu_idx = -1;
}
- mutex_unlock(&kvm->lock);
- return 0;
+ ret = 0;
+out_unlock:
+ mutex_unlock(&kvm->arch.xen.xen_lock);
+ return ret;
}
/*
@@ -1871,12 +1864,9 @@ static int kvm_xen_eventfd_assign(struct kvm *kvm,
{
u32 port = data->u.evtchn.send_port;
struct eventfd_ctx *eventfd = NULL;
- struct evtchnfd *evtchnfd = NULL;
+ struct evtchnfd *evtchnfd;
int ret = -EINVAL;
- if (!port || port >= max_evtchn_port(kvm))
- return -EINVAL;
-
evtchnfd = kzalloc(sizeof(struct evtchnfd), GFP_KERNEL);
if (!evtchnfd)
return -ENOMEM;
@@ -1924,10 +1914,10 @@ static int kvm_xen_eventfd_assign(struct kvm *kvm,
evtchnfd->deliver.port.priority = data->u.evtchn.deliver.port.priority;
}
- mutex_lock(&kvm->lock);
+ mutex_lock(&kvm->arch.xen.xen_lock);
ret = idr_alloc(&kvm->arch.xen.evtchn_ports, evtchnfd, port, port + 1,
GFP_KERNEL);
- mutex_unlock(&kvm->lock);
+ mutex_unlock(&kvm->arch.xen.xen_lock);
if (ret >= 0)
return 0;
@@ -1945,15 +1935,14 @@ static int kvm_xen_eventfd_deassign(struct kvm *kvm, u32 port)
{
struct evtchnfd *evtchnfd;
- mutex_lock(&kvm->lock);
+ mutex_lock(&kvm->arch.xen.xen_lock);
evtchnfd = idr_remove(&kvm->arch.xen.evtchn_ports, port);
- mutex_unlock(&kvm->lock);
+ mutex_unlock(&kvm->arch.xen.xen_lock);
if (!evtchnfd)
return -ENOENT;
- if (kvm)
- synchronize_srcu(&kvm->srcu);
+ synchronize_srcu(&kvm->srcu);
if (!evtchnfd->deliver.port.port)
eventfd_ctx_put(evtchnfd->deliver.eventfd.ctx);
kfree(evtchnfd);
@@ -1962,18 +1951,42 @@ static int kvm_xen_eventfd_deassign(struct kvm *kvm, u32 port)
static int kvm_xen_eventfd_reset(struct kvm *kvm)
{
- struct evtchnfd *evtchnfd;
+ struct evtchnfd *evtchnfd, **all_evtchnfds;
int i;
+ int n = 0;
+
+ mutex_lock(&kvm->arch.xen.xen_lock);
- mutex_lock(&kvm->lock);
+ /*
+ * Because synchronize_srcu() cannot be called inside the
+ * critical section, first collect all the evtchnfd objects
+ * in an array as they are removed from evtchn_ports.
+ */
+ idr_for_each_entry(&kvm->arch.xen.evtchn_ports, evtchnfd, i)
+ n++;
+
+ all_evtchnfds = kmalloc_array(n, sizeof(struct evtchnfd *), GFP_KERNEL);
+ if (!all_evtchnfds) {
+ mutex_unlock(&kvm->arch.xen.xen_lock);
+ return -ENOMEM;
+ }
+
+ n = 0;
idr_for_each_entry(&kvm->arch.xen.evtchn_ports, evtchnfd, i) {
+ all_evtchnfds[n++] = evtchnfd;
idr_remove(&kvm->arch.xen.evtchn_ports, evtchnfd->send_port);
- synchronize_srcu(&kvm->srcu);
+ }
+ mutex_unlock(&kvm->arch.xen.xen_lock);
+
+ synchronize_srcu(&kvm->srcu);
+
+ while (n--) {
+ evtchnfd = all_evtchnfds[n];
if (!evtchnfd->deliver.port.port)
eventfd_ctx_put(evtchnfd->deliver.eventfd.ctx);
kfree(evtchnfd);
}
- mutex_unlock(&kvm->lock);
+ kfree(all_evtchnfds);
return 0;
}
@@ -2002,20 +2015,22 @@ static bool kvm_xen_hcall_evtchn_send(struct kvm_vcpu *vcpu, u64 param, u64 *r)
{
struct evtchnfd *evtchnfd;
struct evtchn_send send;
- gpa_t gpa;
- int idx;
+ struct x86_exception e;
- idx = srcu_read_lock(&vcpu->kvm->srcu);
- gpa = kvm_mmu_gva_to_gpa_system(vcpu, param, NULL);
- srcu_read_unlock(&vcpu->kvm->srcu, idx);
-
- if (!gpa || kvm_vcpu_read_guest(vcpu, gpa, &send, sizeof(send))) {
+ /* Sanity check: this structure is the same for 32-bit and 64-bit */
+ BUILD_BUG_ON(sizeof(send) != 4);
+ if (kvm_read_guest_virt(vcpu, param, &send, sizeof(send), &e)) {
*r = -EFAULT;
return true;
}
- /* The evtchn_ports idr is protected by vcpu->kvm->srcu */
+ /*
+ * evtchnfd is protected by kvm->srcu; the idr lookup instead
+ * is protected by RCU.
+ */
+ rcu_read_lock();
evtchnfd = idr_find(&vcpu->kvm->arch.xen.evtchn_ports, send.port);
+ rcu_read_unlock();
if (!evtchnfd)
return false;
@@ -2063,6 +2078,7 @@ void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu)
void kvm_xen_init_vm(struct kvm *kvm)
{
+ mutex_init(&kvm->arch.xen.xen_lock);
idr_init(&kvm->arch.xen.evtchn_ports);
kvm_gpc_init(&kvm->arch.xen.shinfo_cache, kvm, NULL, KVM_HOST_USES_PFN);
}
diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c
index 21104c41cba048..558a605929db52 100644
--- a/arch/x86/lib/insn-eval.c
+++ b/arch/x86/lib/insn-eval.c
@@ -1595,16 +1595,16 @@ bool insn_decode_from_regs(struct insn *insn, struct pt_regs *regs,
* Returns:
*
* Type of the instruction. Size of the memory operand is stored in
- * @bytes. If decode failed, MMIO_DECODE_FAILED returned.
+ * @bytes. If decode failed, INSN_MMIO_DECODE_FAILED returned.
*/
-enum mmio_type insn_decode_mmio(struct insn *insn, int *bytes)
+enum insn_mmio_type insn_decode_mmio(struct insn *insn, int *bytes)
{
- enum mmio_type type = MMIO_DECODE_FAILED;
+ enum insn_mmio_type type = INSN_MMIO_DECODE_FAILED;
*bytes = 0;
if (insn_get_opcode(insn))
- return MMIO_DECODE_FAILED;
+ return INSN_MMIO_DECODE_FAILED;
switch (insn->opcode.bytes[0]) {
case 0x88: /* MOV m8,r8 */
@@ -1613,7 +1613,7 @@ enum mmio_type insn_decode_mmio(struct insn *insn, int *bytes)
case 0x89: /* MOV m16/m32/m64, r16/m32/m64 */
if (!*bytes)
*bytes = insn->opnd_bytes;
- type = MMIO_WRITE;
+ type = INSN_MMIO_WRITE;
break;
case 0xc6: /* MOV m8, imm8 */
@@ -1622,7 +1622,7 @@ enum mmio_type insn_decode_mmio(struct insn *insn, int *bytes)
case 0xc7: /* MOV m16/m32/m64, imm16/imm32/imm64 */
if (!*bytes)
*bytes = insn->opnd_bytes;
- type = MMIO_WRITE_IMM;
+ type = INSN_MMIO_WRITE_IMM;
break;
case 0x8a: /* MOV r8, m8 */
@@ -1631,7 +1631,7 @@ enum mmio_type insn_decode_mmio(struct insn *insn, int *bytes)
case 0x8b: /* MOV r16/r32/r64, m16/m32/m64 */
if (!*bytes)
*bytes = insn->opnd_bytes;
- type = MMIO_READ;
+ type = INSN_MMIO_READ;
break;
case 0xa4: /* MOVS m8, m8 */
@@ -1640,7 +1640,7 @@ enum mmio_type insn_decode_mmio(struct insn *insn, int *bytes)
case 0xa5: /* MOVS m16/m32/m64, m16/m32/m64 */
if (!*bytes)
*bytes = insn->opnd_bytes;
- type = MMIO_MOVS;
+ type = INSN_MMIO_MOVS;
break;
case 0x0f: /* Two-byte instruction */
@@ -1651,7 +1651,7 @@ enum mmio_type insn_decode_mmio(struct insn *insn, int *bytes)
case 0xb7: /* MOVZX r32/r64, m16 */
if (!*bytes)
*bytes = 2;
- type = MMIO_READ_ZERO_EXTEND;
+ type = INSN_MMIO_READ_ZERO_EXTEND;
break;
case 0xbe: /* MOVSX r16/r32/r64, m8 */
@@ -1660,7 +1660,7 @@ enum mmio_type insn_decode_mmio(struct insn *insn, int *bytes)
case 0xbf: /* MOVSX r32/r64, m16 */
if (!*bytes)
*bytes = 2;
- type = MMIO_READ_SIGN_EXTEND;
+ type = INSN_MMIO_READ_SIGN_EXTEND;
break;
}
break;
diff --git a/arch/x86/lib/iomap_copy_64.S b/arch/x86/lib/iomap_copy_64.S
index a1f9416bf67a53..6ff2f56cb0f71a 100644
--- a/arch/x86/lib/iomap_copy_64.S
+++ b/arch/x86/lib/iomap_copy_64.S
@@ -10,6 +10,6 @@
*/
SYM_FUNC_START(__iowrite32_copy)
movl %edx,%ecx
- rep movsd
+ rep movsl
RET
SYM_FUNC_END(__iowrite32_copy)
diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c
index d3987359d44140..cb258f58fdc879 100644
--- a/arch/x86/mm/init.c
+++ b/arch/x86/mm/init.c
@@ -26,6 +26,7 @@
#include <asm/pti.h>
#include <asm/text-patching.h>
#include <asm/memtype.h>
+#include <asm/paravirt.h>
/*
* We need to define the tracepoints somewhere, and tlb.c
@@ -804,6 +805,9 @@ void __init poking_init(void)
poking_mm = mm_alloc();
BUG_ON(!poking_mm);
+ /* Xen PV guests need the PGD to be pinned. */
+ paravirt_arch_dup_mmap(NULL, poking_mm);
+
/*
* Randomize the poking address, but make sure that the following page
* will be mapped at the same PMD. We need 2 pages, so find space for 3,
diff --git a/arch/x86/mm/pat/memtype.c b/arch/x86/mm/pat/memtype.c
index 46de9cf5c91d2b..fb4b1b5e0deab2 100644
--- a/arch/x86/mm/pat/memtype.c
+++ b/arch/x86/mm/pat/memtype.c
@@ -387,7 +387,8 @@ static unsigned long pat_x_mtrr_type(u64 start, u64 end,
u8 mtrr_type, uniform;
mtrr_type = mtrr_type_lookup(start, end, &uniform);
- if (mtrr_type != MTRR_TYPE_WRBACK)
+ if (mtrr_type != MTRR_TYPE_WRBACK &&
+ mtrr_type != MTRR_TYPE_INVALID)
return _PAGE_CACHE_MODE_UC_MINUS;
return _PAGE_CACHE_MODE_WB;
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
index 758cbfe55daa35..4b3efaa82ab7c1 100644
--- a/arch/x86/pci/mmconfig-shared.c
+++ b/arch/x86/pci/mmconfig-shared.c
@@ -12,6 +12,7 @@
*/
#include <linux/acpi.h>
+#include <linux/efi.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/bitmap.h>
@@ -442,17 +443,42 @@ static bool is_acpi_reserved(u64 start, u64 end, enum e820_type not_used)
return mcfg_res.flags;
}
+static bool is_efi_mmio(u64 start, u64 end, enum e820_type not_used)
+{
+#ifdef CONFIG_EFI
+ efi_memory_desc_t *md;
+ u64 size, mmio_start, mmio_end;
+
+ for_each_efi_memory_desc(md) {
+ if (md->type == EFI_MEMORY_MAPPED_IO) {
+ size = md->num_pages << EFI_PAGE_SHIFT;
+ mmio_start = md->phys_addr;
+ mmio_end = mmio_start + size;
+
+ /*
+ * N.B. Caller supplies (start, start + size),
+ * so to match, mmio_end is the first address
+ * *past* the EFI_MEMORY_MAPPED_IO area.
+ */
+ if (mmio_start <= start && end <= mmio_end)
+ return true;
+ }
+ }
+#endif
+
+ return false;
+}
+
typedef bool (*check_reserved_t)(u64 start, u64 end, enum e820_type type);
static bool __ref is_mmconf_reserved(check_reserved_t is_reserved,
struct pci_mmcfg_region *cfg,
- struct device *dev, int with_e820)
+ struct device *dev, const char *method)
{
u64 addr = cfg->res.start;
u64 size = resource_size(&cfg->res);
u64 old_size = size;
int num_buses;
- char *method = with_e820 ? "E820" : "ACPI motherboard resources";
while (!is_reserved(addr, addr + size, E820_TYPE_RESERVED)) {
size >>= 1;
@@ -464,10 +490,10 @@ static bool __ref is_mmconf_reserved(check_reserved_t is_reserved,
return false;
if (dev)
- dev_info(dev, "MMCONFIG at %pR reserved in %s\n",
+ dev_info(dev, "MMCONFIG at %pR reserved as %s\n",
&cfg->res, method);
else
- pr_info(PREFIX "MMCONFIG at %pR reserved in %s\n",
+ pr_info(PREFIX "MMCONFIG at %pR reserved as %s\n",
&cfg->res, method);
if (old_size != size) {
@@ -500,7 +526,8 @@ static bool __ref
pci_mmcfg_check_reserved(struct device *dev, struct pci_mmcfg_region *cfg, int early)
{
if (!early && !acpi_disabled) {
- if (is_mmconf_reserved(is_acpi_reserved, cfg, dev, 0))
+ if (is_mmconf_reserved(is_acpi_reserved, cfg, dev,
+ "ACPI motherboard resource"))
return true;
if (dev)
@@ -513,6 +540,10 @@ pci_mmcfg_check_reserved(struct device *dev, struct pci_mmcfg_region *cfg, int e
"MMCONFIG at %pR not reserved in "
"ACPI motherboard resources\n",
&cfg->res);
+
+ if (is_mmconf_reserved(is_efi_mmio, cfg, dev,
+ "EfiMemoryMappedIO"))
+ return true;
}
/*
@@ -527,7 +558,8 @@ pci_mmcfg_check_reserved(struct device *dev, struct pci_mmcfg_region *cfg, int e
/* Don't try to do this check unless configuration
type 1 is available. how about type 2 ?*/
if (raw_pci_ops)
- return is_mmconf_reserved(e820__mapped_all, cfg, dev, 1);
+ return is_mmconf_reserved(e820__mapped_all, cfg, dev,
+ "E820 entry");
return false;
}
diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c
index b94f727251b643..8babce71915fe5 100644
--- a/arch/x86/pci/xen.c
+++ b/arch/x86/pci/xen.c
@@ -392,6 +392,7 @@ static void xen_teardown_msi_irqs(struct pci_dev *dev)
msi_for_each_desc(msidesc, &dev->dev, MSI_DESC_ASSOCIATED) {
for (i = 0; i < msidesc->nvec_used; i++)
xen_destroy_irq(msidesc->irq + i);
+ msidesc->irq = 0;
}
}
@@ -433,6 +434,7 @@ static struct msi_domain_ops xen_pci_msi_domain_ops = {
};
static struct msi_domain_info xen_pci_msi_domain_info = {
+ .flags = MSI_FLAG_PCI_MSIX | MSI_FLAG_FREE_MSI_DESCS | MSI_FLAG_DEV_SYSFS,
.ops = &xen_pci_msi_domain_ops,
};
diff --git a/arch/x86/um/elfcore.c b/arch/x86/um/elfcore.c
index 48a3eb09d95165..650cdbbdaf45e5 100644
--- a/arch/x86/um/elfcore.c
+++ b/arch/x86/um/elfcore.c
@@ -7,7 +7,7 @@
#include <asm/elf.h>
-Elf32_Half elf_core_extra_phdrs(void)
+Elf32_Half elf_core_extra_phdrs(struct coredump_params *cprm)
{
return vsyscall_ehdr ? (((struct elfhdr *)vsyscall_ehdr)->e_phnum) : 0;
}
@@ -60,7 +60,7 @@ int elf_core_write_extra_data(struct coredump_params *cprm)
return 1;
}
-size_t elf_core_extra_data_size(void)
+size_t elf_core_extra_data_size(struct coredump_params *cprm)
{
if ( vsyscall_ehdr ) {
const struct elfhdr *const ehdrp =
diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c
index 58db86f7b3846e..9bdc3b656b2c49 100644
--- a/arch/x86/xen/p2m.c
+++ b/arch/x86/xen/p2m.c
@@ -134,11 +134,6 @@ static inline unsigned p2m_mid_index(unsigned long pfn)
return (pfn / P2M_PER_PAGE) % P2M_MID_PER_PAGE;
}
-static inline unsigned p2m_index(unsigned long pfn)
-{
- return pfn % P2M_PER_PAGE;
-}
-
static void p2m_top_mfn_init(unsigned long *top)
{
unsigned i;
diff --git a/arch/xtensa/include/asm/processor.h b/arch/xtensa/include/asm/processor.h
index 228e4dff5fb2d3..a6d09fe0483110 100644
--- a/arch/xtensa/include/asm/processor.h
+++ b/arch/xtensa/include/asm/processor.h
@@ -154,11 +154,6 @@ struct thread_struct {
unsigned long ra; /* kernel's a0: return address and window call size */
unsigned long sp; /* kernel's a1: stack pointer */
- /* struct xtensa_cpuinfo info; */
-
- unsigned long bad_vaddr; /* last user fault */
- unsigned long bad_uaddr; /* last kernel fault accessing user space */
- unsigned long error_code;
#ifdef CONFIG_HAVE_HW_BREAKPOINT
struct perf_event *ptrace_bp[XCHAL_NUM_IBREAK];
struct perf_event *ptrace_wp[XCHAL_NUM_DBREAK];
@@ -176,10 +171,6 @@ struct thread_struct {
{ \
ra: 0, \
sp: sizeof(init_stack) + (long) &init_stack, \
- /*info: {0}, */ \
- bad_vaddr: 0, \
- bad_uaddr: 0, \
- error_code: 0, \
}
diff --git a/arch/xtensa/kernel/traps.c b/arch/xtensa/kernel/traps.c
index 0c25e035ff107b..cd98366a9b2388 100644
--- a/arch/xtensa/kernel/traps.c
+++ b/arch/xtensa/kernel/traps.c
@@ -362,8 +362,6 @@ static void do_unaligned_user(struct pt_regs *regs)
__die_if_kernel("Unhandled unaligned exception in kernel",
regs, SIGKILL);
- current->thread.bad_vaddr = regs->excvaddr;
- current->thread.error_code = -3;
pr_info_ratelimited("Unaligned memory access to %08lx in '%s' "
"(pid = %d, pc = %#010lx)\n",
regs->excvaddr, current->comm,
diff --git a/arch/xtensa/mm/fault.c b/arch/xtensa/mm/fault.c
index 8c781b05c0bdd9..faf7cf35a0ee3d 100644
--- a/arch/xtensa/mm/fault.c
+++ b/arch/xtensa/mm/fault.c
@@ -206,8 +206,6 @@ good_area:
bad_area:
mmap_read_unlock(mm);
if (user_mode(regs)) {
- current->thread.bad_vaddr = address;
- current->thread.error_code = is_write;
force_sig_fault(SIGSEGV, code, (void *) address);
return;
}
@@ -232,7 +230,6 @@ do_sigbus:
/* Send a sigbus, regardless of whether we were in kernel
* or user mode.
*/
- current->thread.bad_vaddr = address;
force_sig_fault(SIGBUS, BUS_ADRERR, (void *) address);
/* Kernel mode? Handle exceptions or die */
@@ -252,7 +249,6 @@ bad_page_fault(struct pt_regs *regs, unsigned long address, int sig)
if ((entry = search_exception_tables(regs->pc)) != NULL) {
pr_debug("%s: Exception at pc=%#010lx (%lx)\n",
current->comm, regs->pc, entry->fixup);
- current->thread.bad_uaddr = address;
regs->pc = entry->fixup;
return;
}
diff --git a/block/Kconfig b/block/Kconfig
index 444c5ab3b67e2f..5d9d9c84d51657 100644
--- a/block/Kconfig
+++ b/block/Kconfig
@@ -6,7 +6,6 @@ menuconfig BLOCK
bool "Enable the block layer" if EXPERT
default y
select SBITMAP
- select SRCU
help
Provide block layer support for the kernel.
diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c
index 1b2829e99dad02..0fbde0fc06287c 100644
--- a/block/bfq-cgroup.c
+++ b/block/bfq-cgroup.c
@@ -316,14 +316,12 @@ struct bfq_group *bfqq_group(struct bfq_queue *bfqq)
static void bfqg_get(struct bfq_group *bfqg)
{
- bfqg->ref++;
+ refcount_inc(&bfqg->ref);
}
static void bfqg_put(struct bfq_group *bfqg)
{
- bfqg->ref--;
-
- if (bfqg->ref == 0)
+ if (refcount_dec_and_test(&bfqg->ref))
kfree(bfqg);
}
@@ -530,7 +528,7 @@ static struct blkg_policy_data *bfq_pd_alloc(gfp_t gfp, struct request_queue *q,
}
/* see comments in bfq_bic_update_cgroup for why refcounting */
- bfqg_get(bfqg);
+ refcount_set(&bfqg->ref, 1);
return &bfqg->pd;
}
@@ -771,8 +769,8 @@ static void __bfq_bic_change_cgroup(struct bfq_data *bfqd,
* request from the old cgroup.
*/
bfq_put_cooperator(sync_bfqq);
- bfq_release_process_ref(bfqd, sync_bfqq);
bic_set_bfqq(bic, NULL, true);
+ bfq_release_process_ref(bfqd, sync_bfqq);
}
}
}
diff --git a/block/bfq-iosched.c b/block/bfq-iosched.c
index 16f43bbc575a0a..380e9bda2e57c5 100644
--- a/block/bfq-iosched.c
+++ b/block/bfq-iosched.c
@@ -5317,8 +5317,8 @@ static void bfq_exit_icq_bfqq(struct bfq_io_cq *bic, bool is_sync)
unsigned long flags;
spin_lock_irqsave(&bfqd->lock, flags);
- bfq_exit_bfqq(bfqd, bfqq);
bic_set_bfqq(bic, NULL, is_sync);
+ bfq_exit_bfqq(bfqd, bfqq);
spin_unlock_irqrestore(&bfqd->lock, flags);
}
}
@@ -5425,9 +5425,11 @@ static void bfq_check_ioprio_change(struct bfq_io_cq *bic, struct bio *bio)
bfqq = bic_to_bfqq(bic, false);
if (bfqq) {
- bfq_release_process_ref(bfqd, bfqq);
+ struct bfq_queue *old_bfqq = bfqq;
+
bfqq = bfq_get_queue(bfqd, bio, false, bic, true);
bic_set_bfqq(bic, bfqq, false);
+ bfq_release_process_ref(bfqd, old_bfqq);
}
bfqq = bic_to_bfqq(bic, true);
diff --git a/block/bfq-iosched.h b/block/bfq-iosched.h
index 41aa151ccc2234..466e4865ace63d 100644
--- a/block/bfq-iosched.h
+++ b/block/bfq-iosched.h
@@ -928,7 +928,7 @@ struct bfq_group {
char blkg_path[128];
/* reference counter (see comments in bfq_bic_update_cgroup) */
- int ref;
+ refcount_t ref;
/* Is bfq_group still online? */
bool online;
diff --git a/block/bio.c b/block/bio.c
index 5f96fcae3f7549..ab59a491a883e3 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1401,6 +1401,27 @@ void __bio_advance(struct bio *bio, unsigned bytes)
}
EXPORT_SYMBOL(__bio_advance);
+void bio_copy_data_iter(struct bio *dst, struct bvec_iter *dst_iter,
+ struct bio *src, struct bvec_iter *src_iter)
+{
+ while (src_iter->bi_size && dst_iter->bi_size) {
+ struct bio_vec src_bv = bio_iter_iovec(src, *src_iter);
+ struct bio_vec dst_bv = bio_iter_iovec(dst, *dst_iter);
+ unsigned int bytes = min(src_bv.bv_len, dst_bv.bv_len);
+ void *src_buf = bvec_kmap_local(&src_bv);
+ void *dst_buf = bvec_kmap_local(&dst_bv);
+
+ memcpy(dst_buf, src_buf, bytes);
+
+ kunmap_local(dst_buf);
+ kunmap_local(src_buf);
+
+ bio_advance_iter_single(src, src_iter, bytes);
+ bio_advance_iter_single(dst, dst_iter, bytes);
+ }
+}
+EXPORT_SYMBOL(bio_copy_data_iter);
+
/**
* bio_copy_data - copy contents of data buffers from one bio to another
* @src: source bio
@@ -1414,21 +1435,7 @@ void bio_copy_data(struct bio *dst, struct bio *src)
struct bvec_iter src_iter = src->bi_iter;
struct bvec_iter dst_iter = dst->bi_iter;
- while (src_iter.bi_size && dst_iter.bi_size) {
- struct bio_vec src_bv = bio_iter_iovec(src, src_iter);
- struct bio_vec dst_bv = bio_iter_iovec(dst, dst_iter);
- unsigned int bytes = min(src_bv.bv_len, dst_bv.bv_len);
- void *src_buf = bvec_kmap_local(&src_bv);
- void *dst_buf = bvec_kmap_local(&dst_bv);
-
- memcpy(dst_buf, src_buf, bytes);
-
- kunmap_local(dst_buf);
- kunmap_local(src_buf);
-
- bio_advance_iter_single(src, &src_iter, bytes);
- bio_advance_iter_single(dst, &dst_iter, bytes);
- }
+ bio_copy_data_iter(dst, &dst_iter, src, &src_iter);
}
EXPORT_SYMBOL(bio_copy_data);
diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index ce6a2b7d3dfb2b..9ac1efb053e08c 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -1455,6 +1455,10 @@ retry:
list_for_each_entry_reverse(blkg, &q->blkg_list, q_node)
pol->pd_init_fn(blkg->pd[pol->plid]);
+ if (pol->pd_online_fn)
+ list_for_each_entry_reverse(blkg, &q->blkg_list, q_node)
+ pol->pd_online_fn(blkg->pd[pol->plid]);
+
__set_bit(pol->plid, q->blkcg_pols);
ret = 0;
@@ -1997,6 +2001,10 @@ void blk_cgroup_bio_start(struct bio *bio)
struct blkg_iostat_set *bis;
unsigned long flags;
+ /* Root-level stats are sourced from system-wide IO stats */
+ if (!cgroup_parent(blkcg->css.cgroup))
+ return;
+
cpu = get_cpu();
bis = per_cpu_ptr(bio->bi_blkg->iostat_cpu, cpu);
flags = u64_stats_update_begin_irqsave(&bis->sync);
diff --git a/block/blk-core.c b/block/blk-core.c
index 9321767470dc5f..b5098355d8b273 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -283,12 +283,9 @@ static void blk_free_queue(struct request_queue *q)
*
* Decrements the refcount of the request_queue and free it when the refcount
* reaches 0.
- *
- * Context: Can sleep.
*/
void blk_put_queue(struct request_queue *q)
{
- might_sleep();
if (refcount_dec_and_test(&q->refs))
blk_free_queue(q);
}
diff --git a/block/blk-merge.c b/block/blk-merge.c
index 35a8f75cc45d1c..b7c193d67185de 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -309,6 +309,16 @@ static struct bio *bio_split_rw(struct bio *bio, const struct queue_limits *lim,
*segs = nsegs;
return NULL;
split:
+ /*
+ * We can't sanely support splitting for a REQ_NOWAIT bio. End it
+ * with EAGAIN if splitting is required and return an error pointer.
+ */
+ if (bio->bi_opf & REQ_NOWAIT) {
+ bio->bi_status = BLK_STS_AGAIN;
+ bio_endio(bio);
+ return ERR_PTR(-EAGAIN);
+ }
+
*segs = nsegs;
/*
@@ -358,11 +368,13 @@ struct bio *__bio_split_to_limits(struct bio *bio,
default:
split = bio_split_rw(bio, lim, nr_segs, bs,
get_max_io_size(bio, lim) << SECTOR_SHIFT);
+ if (IS_ERR(split))
+ return NULL;
break;
}
if (split) {
- /* there isn't chance to merge the splitted bio */
+ /* there isn't chance to merge the split bio */
split->bi_opf |= REQ_NOMERGE;
blkcg_bio_issue_init(split);
diff --git a/block/blk-mq.c b/block/blk-mq.c
index c5cf0dbca1db8d..9c8dc70020bc9a 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -2890,6 +2890,7 @@ static inline struct request *blk_mq_get_cached_request(struct request_queue *q,
struct blk_plug *plug, struct bio **bio, unsigned int nsegs)
{
struct request *rq;
+ enum hctx_type type, hctx_type;
if (!plug)
return NULL;
@@ -2902,7 +2903,10 @@ static inline struct request *blk_mq_get_cached_request(struct request_queue *q,
return NULL;
}
- if (blk_mq_get_hctx_type((*bio)->bi_opf) != rq->mq_hctx->type)
+ type = blk_mq_get_hctx_type((*bio)->bi_opf);
+ hctx_type = rq->mq_hctx->type;
+ if (type != hctx_type &&
+ !(type == HCTX_TYPE_READ && hctx_type == HCTX_TYPE_DEFAULT))
return NULL;
if (op_is_flush(rq->cmd_flags) != op_is_flush((*bio)->bi_opf))
return NULL;
@@ -2951,8 +2955,11 @@ void blk_mq_submit_bio(struct bio *bio)
blk_status_t ret;
bio = blk_queue_bounce(bio, q);
- if (bio_may_exceed_limits(bio, &q->limits))
+ if (bio_may_exceed_limits(bio, &q->limits)) {
bio = __bio_split_to_limits(bio, &q->limits, &nr_segs);
+ if (!bio)
+ return;
+ }
if (!bio_integrity_prep(bio))
return;
@@ -4062,8 +4069,9 @@ EXPORT_SYMBOL(blk_mq_init_queue);
* blk_mq_destroy_queue - shutdown a request queue
* @q: request queue to shutdown
*
- * This shuts down a request queue allocated by blk_mq_init_queue() and drops
- * the initial reference. All future requests will failed with -ENODEV.
+ * This shuts down a request queue allocated by blk_mq_init_queue(). All future
+ * requests will be failed with -ENODEV. The caller is responsible for dropping
+ * the reference from blk_mq_init_queue() by calling blk_put_queue().
*
* Context: can sleep
*/
diff --git a/block/genhd.c b/block/genhd.c
index ab3cbe44196f77..23cf83b3331cde 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -1201,10 +1201,21 @@ struct class block_class = {
.dev_uevent = block_uevent,
};
+static char *block_devnode(struct device *dev, umode_t *mode,
+ kuid_t *uid, kgid_t *gid)
+{
+ struct gendisk *disk = dev_to_disk(dev);
+
+ if (disk->fops->devnode)
+ return disk->fops->devnode(disk, mode);
+ return NULL;
+}
+
const struct device_type disk_type = {
.name = "disk",
.groups = disk_attr_groups,
.release = disk_release,
+ .devnode = block_devnode,
};
#ifdef CONFIG_PROC_FS
diff --git a/certs/Makefile b/certs/Makefile
index 9486ed924731b8..799ad7b9e68a0f 100644
--- a/certs/Makefile
+++ b/certs/Makefile
@@ -23,8 +23,8 @@ $(obj)/blacklist_hash_list: $(CONFIG_SYSTEM_BLACKLIST_HASH_LIST) FORCE
targets += blacklist_hash_list
quiet_cmd_extract_certs = CERT $@
- cmd_extract_certs = $(obj)/extract-cert $(extract-cert-in) $@
-extract-cert-in = $(or $(filter-out $(obj)/extract-cert, $(real-prereqs)),"")
+ cmd_extract_certs = $(obj)/extract-cert "$(extract-cert-in)" $@
+extract-cert-in = $(filter-out $(obj)/extract-cert, $(real-prereqs))
$(obj)/system_certificates.o: $(obj)/x509_certificate_list
diff --git a/drivers/accessibility/speakup/spk_ttyio.c b/drivers/accessibility/speakup/spk_ttyio.c
index 08cf8a17754bb6..07373b3debd1e6 100644
--- a/drivers/accessibility/speakup/spk_ttyio.c
+++ b/drivers/accessibility/speakup/spk_ttyio.c
@@ -354,6 +354,9 @@ void spk_ttyio_release(struct spk_synth *in_synth)
{
struct tty_struct *tty = in_synth->dev;
+ if (tty == NULL)
+ return;
+
tty_lock(tty);
if (tty->ops->close)
diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c
index 30d8fd03fec7ce..97b711e57bff45 100644
--- a/drivers/acpi/acpi_video.c
+++ b/drivers/acpi/acpi_video.c
@@ -70,11 +70,7 @@ module_param(device_id_scheme, bool, 0444);
static int only_lcd = -1;
module_param(only_lcd, int, 0444);
-/*
- * Display probing is known to take up to 5 seconds, so delay the fallback
- * backlight registration by 5 seconds + 3 seconds for some extra margin.
- */
-static int register_backlight_delay = 8;
+static int register_backlight_delay;
module_param(register_backlight_delay, int, 0444);
MODULE_PARM_DESC(register_backlight_delay,
"Delay in seconds before doing fallback (non GPU driver triggered) "
@@ -2176,6 +2172,17 @@ static bool should_check_lcd_flag(void)
return false;
}
+/*
+ * At least one graphics driver has reported that no LCD is connected
+ * via the native interface. cancel the registration for fallback acpi_video0.
+ * If another driver still deems this necessary, it can explicitly register it.
+ */
+void acpi_video_report_nolcd(void)
+{
+ cancel_delayed_work(&video_bus_register_backlight_work);
+}
+EXPORT_SYMBOL(acpi_video_report_nolcd);
+
int acpi_video_register(void)
{
int ret = 0;
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index 204fe94c7e458c..a194f30876c59a 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -75,7 +75,8 @@ static struct acpi_bus_type *acpi_get_bus_type(struct device *dev)
}
#define FIND_CHILD_MIN_SCORE 1
-#define FIND_CHILD_MAX_SCORE 2
+#define FIND_CHILD_MID_SCORE 2
+#define FIND_CHILD_MAX_SCORE 3
static int match_any(struct acpi_device *adev, void *not_used)
{
@@ -96,8 +97,17 @@ static int find_child_checks(struct acpi_device *adev, bool check_children)
return -ENODEV;
status = acpi_evaluate_integer(adev->handle, "_STA", NULL, &sta);
- if (status == AE_NOT_FOUND)
+ if (status == AE_NOT_FOUND) {
+ /*
+ * Special case: backlight device objects without _STA are
+ * preferred to other objects with the same _ADR value, because
+ * it is more likely that they are actually useful.
+ */
+ if (adev->pnp.type.backlight)
+ return FIND_CHILD_MID_SCORE;
+
return FIND_CHILD_MIN_SCORE;
+ }
if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED))
return -ENODEV;
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c
index f1cc5ec6a3b654..4e48d6db05eb1c 100644
--- a/drivers/acpi/nfit/core.c
+++ b/drivers/acpi/nfit/core.c
@@ -3297,8 +3297,8 @@ void acpi_nfit_shutdown(void *data)
mutex_lock(&acpi_desc->init_mutex);
set_bit(ARS_CANCEL, &acpi_desc->scrub_flags);
- cancel_delayed_work_sync(&acpi_desc->dwork);
mutex_unlock(&acpi_desc->init_mutex);
+ cancel_delayed_work_sync(&acpi_desc->dwork);
/*
* Bounce the nvdimm bus lock to make sure any in-flight
diff --git a/drivers/acpi/prmt.c b/drivers/acpi/prmt.c
index 998101cf16e471..3d4c4620f9f953 100644
--- a/drivers/acpi/prmt.c
+++ b/drivers/acpi/prmt.c
@@ -236,6 +236,11 @@ static acpi_status acpi_platformrt_space_handler(u32 function,
efi_status_t status;
struct prm_context_buffer context;
+ if (!efi_enabled(EFI_RUNTIME_SERVICES)) {
+ pr_err_ratelimited("PRM: EFI runtime services no longer available\n");
+ return AE_NO_HANDLER;
+ }
+
/*
* The returned acpi_status will always be AE_OK. Error values will be
* saved in the first byte of the PRM message buffer to be used by ASL.
@@ -325,6 +330,11 @@ void __init init_prmt(void)
pr_info("PRM: found %u modules\n", mc);
+ if (!efi_enabled(EFI_RUNTIME_SERVICES)) {
+ pr_err("PRM: EFI runtime services unavailable\n");
+ return;
+ }
+
status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
ACPI_ADR_SPACE_PLATFORM_RT,
&acpi_platformrt_space_handler,
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index f27914aedbd5ad..192d1784e409b4 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -432,10 +432,31 @@ static const struct dmi_system_id asus_laptop[] = {
DMI_MATCH(DMI_BOARD_NAME, "S5602ZA"),
},
},
+ {
+ .ident = "Asus ExpertBook B2402CBA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "B2402CBA"),
+ },
+ },
+ {
+ .ident = "Asus ExpertBook B2502",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_BOARD_NAME, "B2502CBA"),
+ },
+ },
{ }
};
-static const struct dmi_system_id lenovo_82ra[] = {
+static const struct dmi_system_id lenovo_laptop[] = {
+ {
+ .ident = "LENOVO IdeaPad Flex 5 14ALC7",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82R9"),
+ },
+ },
{
.ident = "LENOVO IdeaPad Flex 5 16ALC7",
.matches = {
@@ -446,6 +467,17 @@ static const struct dmi_system_id lenovo_82ra[] = {
{ }
};
+static const struct dmi_system_id schenker_gm_rg[] = {
+ {
+ .ident = "XMG CORE 15 (M22)",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"),
+ DMI_MATCH(DMI_BOARD_NAME, "GMxRGxx"),
+ },
+ },
+ { }
+};
+
struct irq_override_cmp {
const struct dmi_system_id *system;
unsigned char irq;
@@ -458,8 +490,9 @@ struct irq_override_cmp {
static const struct irq_override_cmp override_table[] = {
{ medion_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false },
{ asus_laptop, 1, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, false },
- { lenovo_82ra, 6, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, true },
- { lenovo_82ra, 10, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, true },
+ { lenovo_laptop, 6, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, true },
+ { lenovo_laptop, 10, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW, 0, true },
+ { schenker_gm_rg, 1, ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_LOW, 1, true },
};
static bool acpi_dev_irq_override(u32 gsi, u8 triggering, u8 polarity,
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 274344434282ee..0c6f06abe3f47f 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1370,9 +1370,12 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp,
* Some devices don't reliably have _HIDs & _CIDs, so add
* synthetic HIDs to make sure drivers can find them.
*/
- if (acpi_is_video_device(handle))
+ if (acpi_is_video_device(handle)) {
acpi_add_id(pnp, ACPI_VIDEO_HID);
- else if (acpi_bay_match(handle))
+ pnp->type.backlight = 1;
+ break;
+ }
+ if (acpi_bay_match(handle))
acpi_add_id(pnp, ACPI_BAY_HID);
else if (acpi_dock_match(handle))
acpi_add_id(pnp, ACPI_DOCK_HID);
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 0b557c0d405ef1..4ca66725127221 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -60,13 +60,17 @@ static struct notifier_block tts_notifier = {
.priority = 0,
};
+#ifndef acpi_skip_set_wakeup_address
+#define acpi_skip_set_wakeup_address() false
+#endif
+
static int acpi_sleep_prepare(u32 acpi_state)
{
#ifdef CONFIG_ACPI_SLEEP
unsigned long acpi_wakeup_address;
/* do we have a wakeup address for S2 and S3? */
- if (acpi_state == ACPI_STATE_S3) {
+ if (acpi_state == ACPI_STATE_S3 && !acpi_skip_set_wakeup_address()) {
acpi_wakeup_address = acpi_get_wakeup_address();
if (!acpi_wakeup_address)
return -EFAULT;
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index a934bbc9dd37c6..a8c02608dde453 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
@@ -34,6 +34,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_data/x86/nvidia-wmi-ec-backlight.h>
+#include <linux/pnp.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <acpi/video.h>
@@ -49,6 +50,10 @@ static void acpi_video_parse_cmdline(void)
acpi_backlight_cmdline = acpi_backlight_video;
if (!strcmp("native", acpi_video_backlight_string))
acpi_backlight_cmdline = acpi_backlight_native;
+ if (!strcmp("nvidia_wmi_ec", acpi_video_backlight_string))
+ acpi_backlight_cmdline = acpi_backlight_nvidia_wmi_ec;
+ if (!strcmp("apple_gmux", acpi_video_backlight_string))
+ acpi_backlight_cmdline = acpi_backlight_apple_gmux;
if (!strcmp("none", acpi_video_backlight_string))
acpi_backlight_cmdline = acpi_backlight_none;
}
@@ -492,6 +497,14 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
},
{
.callback = video_detect_force_native,
+ /* Acer Aspire 4810T */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 4810T"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
/* Acer Aspire 5738z */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
@@ -579,6 +592,14 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
},
{
.callback = video_detect_force_native,
+ /* Asus U46E */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "U46E"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
/* Asus UX303UB */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
@@ -587,6 +608,23 @@ static const struct dmi_system_id video_detect_dmi_table[] = {
},
{
.callback = video_detect_force_native,
+ /* HP EliteBook 8460p */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 8460p"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
+ /* HP Pavilion g6-1d80nr / B4U19UA */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion g6 Notebook PC"),
+ DMI_MATCH(DMI_PRODUCT_SKU, "B4U19UA"),
+ },
+ },
+ {
+ .callback = video_detect_force_native,
/* Samsung N150P */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
@@ -733,6 +771,7 @@ static enum acpi_backlight_type __acpi_video_get_backlight_type(bool native)
{
static DEFINE_MUTEX(init_mutex);
static bool nvidia_wmi_ec_present;
+ static bool apple_gmux_present;
static bool native_available;
static bool init_done;
static long video_caps;
@@ -746,6 +785,7 @@ static enum acpi_backlight_type __acpi_video_get_backlight_type(bool native)
ACPI_UINT32_MAX, find_video, NULL,
&video_caps, NULL);
nvidia_wmi_ec_present = nvidia_wmi_ec_supported();
+ apple_gmux_present = apple_gmux_detect(NULL, NULL);
init_done = true;
}
if (native)
@@ -767,7 +807,7 @@ static enum acpi_backlight_type __acpi_video_get_backlight_type(bool native)
if (nvidia_wmi_ec_present)
return acpi_backlight_nvidia_wmi_ec;
- if (apple_gmux_present())
+ if (apple_gmux_present)
return acpi_backlight_apple_gmux;
/* Use ACPI video if available, except when native should be preferred. */
diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c
index 5350c73564b601..c7afce465a0710 100644
--- a/drivers/acpi/x86/s2idle.c
+++ b/drivers/acpi/x86/s2idle.c
@@ -28,10 +28,6 @@ static bool sleep_no_lps0 __read_mostly;
module_param(sleep_no_lps0, bool, 0644);
MODULE_PARM_DESC(sleep_no_lps0, "Do not use the special LPS0 device interface");
-static bool prefer_microsoft_dsm_guid __read_mostly;
-module_param(prefer_microsoft_dsm_guid, bool, 0644);
-MODULE_PARM_DESC(prefer_microsoft_dsm_guid, "Prefer using Microsoft GUID in LPS0 device _DSM evaluation");
-
static const struct acpi_device_id lps0_device_ids[] = {
{"PNP0D80", },
{"", },
@@ -369,27 +365,15 @@ out:
}
struct amd_lps0_hid_device_data {
- const unsigned int rev_id;
const bool check_off_by_one;
- const bool prefer_amd_guid;
};
static const struct amd_lps0_hid_device_data amd_picasso = {
- .rev_id = 0,
.check_off_by_one = true,
- .prefer_amd_guid = false,
};
static const struct amd_lps0_hid_device_data amd_cezanne = {
- .rev_id = 0,
- .check_off_by_one = false,
- .prefer_amd_guid = false,
-};
-
-static const struct amd_lps0_hid_device_data amd_rembrandt = {
- .rev_id = 2,
.check_off_by_one = false,
- .prefer_amd_guid = true,
};
static const struct acpi_device_id amd_hid_ids[] = {
@@ -397,69 +381,27 @@ static const struct acpi_device_id amd_hid_ids[] = {
{"AMD0005", (kernel_ulong_t)&amd_picasso, },
{"AMDI0005", (kernel_ulong_t)&amd_picasso, },
{"AMDI0006", (kernel_ulong_t)&amd_cezanne, },
- {"AMDI0007", (kernel_ulong_t)&amd_rembrandt, },
{}
};
-static int lps0_prefer_microsoft(const struct dmi_system_id *id)
+static int lps0_prefer_amd(const struct dmi_system_id *id)
{
- pr_debug("Preferring Microsoft GUID.\n");
- prefer_microsoft_dsm_guid = true;
+ pr_debug("Using AMD GUID w/ _REV 2.\n");
+ rev_id = 2;
return 0;
}
-
static const struct dmi_system_id s2idle_dmi_table[] __initconst = {
{
/*
- * ASUS TUF Gaming A17 FA707RE
- * https://bugzilla.kernel.org/show_bug.cgi?id=216101
- */
- .callback = lps0_prefer_microsoft,
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "ASUS TUF Gaming A17"),
- },
- },
- {
- /* ASUS ROG Zephyrus G14 (2022) */
- .callback = lps0_prefer_microsoft,
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "ROG Zephyrus G14 GA402"),
- },
- },
- {
- /*
- * Lenovo Yoga Slim 7 Pro X 14ARH7
- * https://bugzilla.kernel.org/show_bug.cgi?id=216473 : 82V2
- * https://bugzilla.kernel.org/show_bug.cgi?id=216438 : 82TL
- */
- .callback = lps0_prefer_microsoft,
- .matches = {
- DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_NAME, "82"),
- },
- },
- {
- /*
- * ASUSTeK COMPUTER INC. ROG Flow X13 GV301RE_GV301RE
- * https://gitlab.freedesktop.org/drm/amd/-/issues/2148
+ * AMD Rembrandt based HP EliteBook 835/845/865 G9
+ * Contains specialized AML in AMD/_REV 2 path to avoid
+ * triggering a bug in Qualcomm WLAN firmware. This may be
+ * removed in the future if that firmware is fixed.
*/
- .callback = lps0_prefer_microsoft,
+ .callback = lps0_prefer_amd,
.matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "ROG Flow X13 GV301"),
- },
- },
- {
- /*
- * ASUSTeK COMPUTER INC. ROG Flow X16 GV601RW_GV601RW
- * https://gitlab.freedesktop.org/drm/amd/-/issues/2148
- */
- .callback = lps0_prefer_microsoft,
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "ROG Flow X16 GV601"),
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8990"),
},
},
{}
@@ -484,16 +426,14 @@ static int lps0_device_attach(struct acpi_device *adev,
if (dev_id->id[0])
data = (const struct amd_lps0_hid_device_data *) dev_id->driver_data;
else
- data = &amd_rembrandt;
- rev_id = data->rev_id;
+ data = &amd_cezanne;
lps0_dsm_func_mask = validate_dsm(adev->handle,
ACPI_LPS0_DSM_UUID_AMD, rev_id, &lps0_dsm_guid);
if (lps0_dsm_func_mask > 0x3 && data->check_off_by_one) {
lps0_dsm_func_mask = (lps0_dsm_func_mask << 1) | 0x1;
acpi_handle_debug(adev->handle, "_DSM UUID %s: Adjusted function mask: 0x%x\n",
ACPI_LPS0_DSM_UUID_AMD, lps0_dsm_func_mask);
- } else if (lps0_dsm_func_mask_microsoft > 0 && data->prefer_amd_guid &&
- !prefer_microsoft_dsm_guid) {
+ } else if (lps0_dsm_func_mask_microsoft > 0 && rev_id) {
lps0_dsm_func_mask_microsoft = -EINVAL;
acpi_handle_debug(adev->handle, "_DSM Using AMD method\n");
}
@@ -501,8 +441,7 @@ static int lps0_device_attach(struct acpi_device *adev,
rev_id = 1;
lps0_dsm_func_mask = validate_dsm(adev->handle,
ACPI_LPS0_DSM_UUID, rev_id, &lps0_dsm_guid);
- if (!prefer_microsoft_dsm_guid)
- lps0_dsm_func_mask_microsoft = -EINVAL;
+ lps0_dsm_func_mask_microsoft = -EINVAL;
}
if (lps0_dsm_func_mask < 0 && lps0_dsm_func_mask_microsoft < 0)
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index eceaec33af65b4..9695c4404e26a1 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -640,6 +640,7 @@ config PATA_CS5530
config PATA_CS5535
tristate "CS5535 PATA support (Experimental)"
depends on PCI && (X86_32 || (X86_64 && COMPILE_TEST))
+ depends on !UML
help
This option enables support for the NatSemi/AMD CS5535
companion chip used with the Geode processor family.
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 0cfd0ec6229b32..14a1c0d14916f9 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -83,6 +83,7 @@ enum board_ids {
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
static void ahci_remove_one(struct pci_dev *dev);
static void ahci_shutdown_one(struct pci_dev *dev);
+static void ahci_intel_pcs_quirk(struct pci_dev *pdev, struct ahci_host_priv *hpriv);
static int ahci_vt8251_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline);
static int ahci_avn_hardreset(struct ata_link *link, unsigned int *class,
@@ -676,6 +677,25 @@ static void ahci_pci_save_initial_config(struct pci_dev *pdev,
ahci_save_initial_config(&pdev->dev, hpriv);
}
+static int ahci_pci_reset_controller(struct ata_host *host)
+{
+ struct pci_dev *pdev = to_pci_dev(host->dev);
+ struct ahci_host_priv *hpriv = host->private_data;
+ int rc;
+
+ rc = ahci_reset_controller(host);
+ if (rc)
+ return rc;
+
+ /*
+ * If platform firmware failed to enable ports, try to enable
+ * them here.
+ */
+ ahci_intel_pcs_quirk(pdev, hpriv);
+
+ return 0;
+}
+
static void ahci_pci_init_controller(struct ata_host *host)
{
struct ahci_host_priv *hpriv = host->private_data;
@@ -870,7 +890,7 @@ static int ahci_pci_device_runtime_resume(struct device *dev)
struct ata_host *host = pci_get_drvdata(pdev);
int rc;
- rc = ahci_reset_controller(host);
+ rc = ahci_pci_reset_controller(host);
if (rc)
return rc;
ahci_pci_init_controller(host);
@@ -906,7 +926,7 @@ static int ahci_pci_device_resume(struct device *dev)
ahci_mcp89_apple_enable(pdev);
if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
- rc = ahci_reset_controller(host);
+ rc = ahci_pci_reset_controller(host);
if (rc)
return rc;
@@ -1784,12 +1804,6 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
/* save initial config */
ahci_pci_save_initial_config(pdev, hpriv);
- /*
- * If platform firmware failed to enable ports, try to enable
- * them here.
- */
- ahci_intel_pcs_quirk(pdev, hpriv);
-
/* prepare host */
if (hpriv->cap & HOST_CAP_NCQ) {
pi.flags |= ATA_FLAG_NCQ;
@@ -1899,7 +1913,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (rc)
return rc;
- rc = ahci_reset_controller(host);
+ rc = ahci_pci_reset_controller(host);
if (rc)
return rc;
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 884ae73b11eac1..2ea572628b1c8e 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -3109,7 +3109,7 @@ int sata_down_spd_limit(struct ata_link *link, u32 spd_limit)
*/
if (spd > 1)
mask &= (1 << (spd - 1)) - 1;
- else
+ else if (link->sata_spd)
return -EINVAL;
/* were we already at the bottom? */
diff --git a/drivers/base/property.c b/drivers/base/property.c
index bbb3e499ff4a5d..083a95791d3b3c 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -997,26 +997,32 @@ struct fwnode_handle *
fwnode_graph_get_next_endpoint(const struct fwnode_handle *fwnode,
struct fwnode_handle *prev)
{
+ struct fwnode_handle *ep, *port_parent = NULL;
const struct fwnode_handle *parent;
- struct fwnode_handle *ep;
/*
* If this function is in a loop and the previous iteration returned
* an endpoint from fwnode->secondary, then we need to use the secondary
* as parent rather than @fwnode.
*/
- if (prev)
- parent = fwnode_graph_get_port_parent(prev);
- else
+ if (prev) {
+ port_parent = fwnode_graph_get_port_parent(prev);
+ parent = port_parent;
+ } else {
parent = fwnode;
+ }
if (IS_ERR_OR_NULL(parent))
return NULL;
ep = fwnode_call_ptr_op(parent, graph_get_next_endpoint, prev);
if (ep)
- return ep;
+ goto out_put_port_parent;
+
+ ep = fwnode_graph_get_next_endpoint(parent->secondary, NULL);
- return fwnode_graph_get_next_endpoint(parent->secondary, NULL);
+out_put_port_parent:
+ fwnode_handle_put(port_parent);
+ return ep;
}
EXPORT_SYMBOL_GPL(fwnode_graph_get_next_endpoint);
diff --git a/drivers/base/test/test_async_driver_probe.c b/drivers/base/test/test_async_driver_probe.c
index 4d1976ca507271..929410d0dd6fe2 100644
--- a/drivers/base/test/test_async_driver_probe.c
+++ b/drivers/base/test/test_async_driver_probe.c
@@ -145,7 +145,7 @@ static int __init test_async_probe_init(void)
calltime = ktime_get();
for_each_online_cpu(cpu) {
nid = cpu_to_node(cpu);
- pdev = &sync_dev[sync_id];
+ pdev = &async_dev[async_id];
*pdev = test_platform_device_register_node("test_async_driver",
async_id,
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index a2184b42849361..a41145d52de942 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -285,6 +285,49 @@ config BLK_DEV_RAM_SIZE
The default value is 4096 kilobytes. Only change this if you know
what you are doing.
+config CDROM_PKTCDVD
+ tristate "Packet writing on CD/DVD media (DEPRECATED)"
+ depends on !UML
+ depends on SCSI
+ select CDROM
+ help
+ Note: This driver is deprecated and will be removed from the
+ kernel in the near future!
+
+ If you have a CDROM/DVD drive that supports packet writing, say
+ Y to include support. It should work with any MMC/Mt Fuji
+ compliant ATAPI or SCSI drive, which is just about any newer
+ DVD/CD writer.
+
+ Currently only writing to CD-RW, DVD-RW, DVD+RW and DVDRAM discs
+ is possible.
+ DVD-RW disks must be in restricted overwrite mode.
+
+ See the file <file:Documentation/cdrom/packet-writing.rst>
+ for further information on the use of this driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pktcdvd.
+
+config CDROM_PKTCDVD_BUFFERS
+ int "Free buffers for data gathering"
+ depends on CDROM_PKTCDVD
+ default "8"
+ help
+ This controls the maximum number of active concurrent packets. More
+ concurrent packets can increase write performance, but also require
+ more memory. Each concurrent packet will require approximately 64Kb
+ of non-swappable kernel memory, memory which will be allocated when
+ a disc is opened for writing.
+
+config CDROM_PKTCDVD_WCACHE
+ bool "Enable write caching"
+ depends on CDROM_PKTCDVD
+ help
+ If enabled, write caching will be set for the CD-R/W device. For now
+ this option is dangerous unless the CD-RW media is known good, as we
+ don't do deferred write error handling yet.
+
config ATA_OVER_ETH
tristate "ATA over Ethernet support"
depends on NET
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 962ee65d8ca303..101612cba303a3 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o
obj-$(CONFIG_N64CART) += n64cart.o
obj-$(CONFIG_BLK_DEV_RAM) += brd.o
obj-$(CONFIG_BLK_DEV_LOOP) += loop.o
+obj-$(CONFIG_CDROM_PKTCDVD) += pktcdvd.o
obj-$(CONFIG_SUNVDC) += sunvdc.o
obj-$(CONFIG_BLK_DEV_NBD) += nbd.o
diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c
index eb14ec8ec04cfc..e36216d50753c8 100644
--- a/drivers/block/drbd/drbd_req.c
+++ b/drivers/block/drbd/drbd_req.c
@@ -1607,6 +1607,8 @@ void drbd_submit_bio(struct bio *bio)
struct drbd_device *device = bio->bi_bdev->bd_disk->private_data;
bio = bio_split_to_limits(bio);
+ if (!bio)
+ return;
/*
* what we "blindly" assume:
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
new file mode 100644
index 00000000000000..2f1a92509271c4
--- /dev/null
+++ b/drivers/block/pktcdvd.c
@@ -0,0 +1,2946 @@
+/*
+ * Copyright (C) 2000 Jens Axboe <axboe@suse.de>
+ * Copyright (C) 2001-2004 Peter Osterlund <petero2@telia.com>
+ * Copyright (C) 2006 Thomas Maier <balagi@justmail.de>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Packet writing layer for ATAPI and SCSI CD-RW, DVD+RW, DVD-RW and
+ * DVD-RAM devices.
+ *
+ * Theory of operation:
+ *
+ * At the lowest level, there is the standard driver for the CD/DVD device,
+ * such as drivers/scsi/sr.c. This driver can handle read and write requests,
+ * but it doesn't know anything about the special restrictions that apply to
+ * packet writing. One restriction is that write requests must be aligned to
+ * packet boundaries on the physical media, and the size of a write request
+ * must be equal to the packet size. Another restriction is that a
+ * GPCMD_FLUSH_CACHE command has to be issued to the drive before a read
+ * command, if the previous command was a write.
+ *
+ * The purpose of the packet writing driver is to hide these restrictions from
+ * higher layers, such as file systems, and present a block device that can be
+ * randomly read and written using 2kB-sized blocks.
+ *
+ * The lowest layer in the packet writing driver is the packet I/O scheduler.
+ * Its data is defined by the struct packet_iosched and includes two bio
+ * queues with pending read and write requests. These queues are processed
+ * by the pkt_iosched_process_queue() function. The write requests in this
+ * queue are already properly aligned and sized. This layer is responsible for
+ * issuing the flush cache commands and scheduling the I/O in a good order.
+ *
+ * The next layer transforms unaligned write requests to aligned writes. This
+ * transformation requires reading missing pieces of data from the underlying
+ * block device, assembling the pieces to full packets and queuing them to the
+ * packet I/O scheduler.
+ *
+ * At the top layer there is a custom ->submit_bio function that forwards
+ * read requests directly to the iosched queue and puts write requests in the
+ * unaligned write queue. A kernel thread performs the necessary read
+ * gathering to convert the unaligned writes to aligned writes and then feeds
+ * them to the packet I/O scheduler.
+ *
+ *************************************************************************/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/pktcdvd.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/compat.h>
+#include <linux/kthread.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/file.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/miscdevice.h>
+#include <linux/freezer.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/backing-dev.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/nospec.h>
+#include <linux/uaccess.h>
+
+#define DRIVER_NAME "pktcdvd"
+
+#define pkt_err(pd, fmt, ...) \
+ pr_err("%s: " fmt, pd->name, ##__VA_ARGS__)
+#define pkt_notice(pd, fmt, ...) \
+ pr_notice("%s: " fmt, pd->name, ##__VA_ARGS__)
+#define pkt_info(pd, fmt, ...) \
+ pr_info("%s: " fmt, pd->name, ##__VA_ARGS__)
+
+#define pkt_dbg(level, pd, fmt, ...) \
+do { \
+ if (level == 2 && PACKET_DEBUG >= 2) \
+ pr_notice("%s: %s():" fmt, \
+ pd->name, __func__, ##__VA_ARGS__); \
+ else if (level == 1 && PACKET_DEBUG >= 1) \
+ pr_notice("%s: " fmt, pd->name, ##__VA_ARGS__); \
+} while (0)
+
+#define MAX_SPEED 0xffff
+
+static DEFINE_MUTEX(pktcdvd_mutex);
+static struct pktcdvd_device *pkt_devs[MAX_WRITERS];
+static struct proc_dir_entry *pkt_proc;
+static int pktdev_major;
+static int write_congestion_on = PKT_WRITE_CONGESTION_ON;
+static int write_congestion_off = PKT_WRITE_CONGESTION_OFF;
+static struct mutex ctl_mutex; /* Serialize open/close/setup/teardown */
+static mempool_t psd_pool;
+static struct bio_set pkt_bio_set;
+
+static struct class *class_pktcdvd = NULL; /* /sys/class/pktcdvd */
+static struct dentry *pkt_debugfs_root = NULL; /* /sys/kernel/debug/pktcdvd */
+
+/* forward declaration */
+static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev);
+static int pkt_remove_dev(dev_t pkt_dev);
+static int pkt_seq_show(struct seq_file *m, void *p);
+
+static sector_t get_zone(sector_t sector, struct pktcdvd_device *pd)
+{
+ return (sector + pd->offset) & ~(sector_t)(pd->settings.size - 1);
+}
+
+/**********************************************************
+ * sysfs interface for pktcdvd
+ * by (C) 2006 Thomas Maier <balagi@justmail.de>
+
+ /sys/class/pktcdvd/pktcdvd[0-7]/
+ stat/reset
+ stat/packets_started
+ stat/packets_finished
+ stat/kb_written
+ stat/kb_read
+ stat/kb_read_gather
+ write_queue/size
+ write_queue/congestion_off
+ write_queue/congestion_on
+ **********************************************************/
+
+static ssize_t packets_started_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pktcdvd_device *pd = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%lu\n", pd->stats.pkt_started);
+}
+static DEVICE_ATTR_RO(packets_started);
+
+static ssize_t packets_finished_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pktcdvd_device *pd = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%lu\n", pd->stats.pkt_ended);
+}
+static DEVICE_ATTR_RO(packets_finished);
+
+static ssize_t kb_written_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pktcdvd_device *pd = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%lu\n", pd->stats.secs_w >> 1);
+}
+static DEVICE_ATTR_RO(kb_written);
+
+static ssize_t kb_read_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pktcdvd_device *pd = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%lu\n", pd->stats.secs_r >> 1);
+}
+static DEVICE_ATTR_RO(kb_read);
+
+static ssize_t kb_read_gather_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pktcdvd_device *pd = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%lu\n", pd->stats.secs_rg >> 1);
+}
+static DEVICE_ATTR_RO(kb_read_gather);
+
+static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct pktcdvd_device *pd = dev_get_drvdata(dev);
+
+ if (len > 0) {
+ pd->stats.pkt_started = 0;
+ pd->stats.pkt_ended = 0;
+ pd->stats.secs_w = 0;
+ pd->stats.secs_rg = 0;
+ pd->stats.secs_r = 0;
+ }
+ return len;
+}
+static DEVICE_ATTR_WO(reset);
+
+static struct attribute *pkt_stat_attrs[] = {
+ &dev_attr_packets_finished.attr,
+ &dev_attr_packets_started.attr,
+ &dev_attr_kb_read.attr,
+ &dev_attr_kb_written.attr,
+ &dev_attr_kb_read_gather.attr,
+ &dev_attr_reset.attr,
+ NULL,
+};
+
+static const struct attribute_group pkt_stat_group = {
+ .name = "stat",
+ .attrs = pkt_stat_attrs,
+};
+
+static ssize_t size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pktcdvd_device *pd = dev_get_drvdata(dev);
+ int n;
+
+ spin_lock(&pd->lock);
+ n = sysfs_emit(buf, "%d\n", pd->bio_queue_size);
+ spin_unlock(&pd->lock);
+ return n;
+}
+static DEVICE_ATTR_RO(size);
+
+static void init_write_congestion_marks(int* lo, int* hi)
+{
+ if (*hi > 0) {
+ *hi = max(*hi, 500);
+ *hi = min(*hi, 1000000);
+ if (*lo <= 0)
+ *lo = *hi - 100;
+ else {
+ *lo = min(*lo, *hi - 100);
+ *lo = max(*lo, 100);
+ }
+ } else {
+ *hi = -1;
+ *lo = -1;
+ }
+}
+
+static ssize_t congestion_off_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pktcdvd_device *pd = dev_get_drvdata(dev);
+ int n;
+
+ spin_lock(&pd->lock);
+ n = sysfs_emit(buf, "%d\n", pd->write_congestion_off);
+ spin_unlock(&pd->lock);
+ return n;
+}
+
+static ssize_t congestion_off_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct pktcdvd_device *pd = dev_get_drvdata(dev);
+ int val;
+
+ if (sscanf(buf, "%d", &val) == 1) {
+ spin_lock(&pd->lock);
+ pd->write_congestion_off = val;
+ init_write_congestion_marks(&pd->write_congestion_off,
+ &pd->write_congestion_on);
+ spin_unlock(&pd->lock);
+ }
+ return len;
+}
+static DEVICE_ATTR_RW(congestion_off);
+
+static ssize_t congestion_on_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct pktcdvd_device *pd = dev_get_drvdata(dev);
+ int n;
+
+ spin_lock(&pd->lock);
+ n = sysfs_emit(buf, "%d\n", pd->write_congestion_on);
+ spin_unlock(&pd->lock);
+ return n;
+}
+
+static ssize_t congestion_on_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct pktcdvd_device *pd = dev_get_drvdata(dev);
+ int val;
+
+ if (sscanf(buf, "%d", &val) == 1) {
+ spin_lock(&pd->lock);
+ pd->write_congestion_on = val;
+ init_write_congestion_marks(&pd->write_congestion_off,
+ &pd->write_congestion_on);
+ spin_unlock(&pd->lock);
+ }
+ return len;
+}
+static DEVICE_ATTR_RW(congestion_on);
+
+static struct attribute *pkt_wq_attrs[] = {
+ &dev_attr_congestion_on.attr,
+ &dev_attr_congestion_off.attr,
+ &dev_attr_size.attr,
+ NULL,
+};
+
+static const struct attribute_group pkt_wq_group = {
+ .name = "write_queue",
+ .attrs = pkt_wq_attrs,
+};
+
+static const struct attribute_group *pkt_groups[] = {
+ &pkt_stat_group,
+ &pkt_wq_group,
+ NULL,
+};
+
+static void pkt_sysfs_dev_new(struct pktcdvd_device *pd)
+{
+ if (class_pktcdvd) {
+ pd->dev = device_create_with_groups(class_pktcdvd, NULL,
+ MKDEV(0, 0), pd, pkt_groups,
+ "%s", pd->name);
+ if (IS_ERR(pd->dev))
+ pd->dev = NULL;
+ }
+}
+
+static void pkt_sysfs_dev_remove(struct pktcdvd_device *pd)
+{
+ if (class_pktcdvd)
+ device_unregister(pd->dev);
+}
+
+
+/********************************************************************
+ /sys/class/pktcdvd/
+ add map block device
+ remove unmap packet dev
+ device_map show mappings
+ *******************************************************************/
+
+static void class_pktcdvd_release(struct class *cls)
+{
+ kfree(cls);
+}
+
+static ssize_t device_map_show(struct class *c, struct class_attribute *attr,
+ char *data)
+{
+ int n = 0;
+ int idx;
+ mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
+ for (idx = 0; idx < MAX_WRITERS; idx++) {
+ struct pktcdvd_device *pd = pkt_devs[idx];
+ if (!pd)
+ continue;
+ n += sprintf(data+n, "%s %u:%u %u:%u\n",
+ pd->name,
+ MAJOR(pd->pkt_dev), MINOR(pd->pkt_dev),
+ MAJOR(pd->bdev->bd_dev),
+ MINOR(pd->bdev->bd_dev));
+ }
+ mutex_unlock(&ctl_mutex);
+ return n;
+}
+static CLASS_ATTR_RO(device_map);
+
+static ssize_t add_store(struct class *c, struct class_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int major, minor;
+
+ if (sscanf(buf, "%u:%u", &major, &minor) == 2) {
+ /* pkt_setup_dev() expects caller to hold reference to self */
+ if (!try_module_get(THIS_MODULE))
+ return -ENODEV;
+
+ pkt_setup_dev(MKDEV(major, minor), NULL);
+
+ module_put(THIS_MODULE);
+
+ return count;
+ }
+
+ return -EINVAL;
+}
+static CLASS_ATTR_WO(add);
+
+static ssize_t remove_store(struct class *c, struct class_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int major, minor;
+ if (sscanf(buf, "%u:%u", &major, &minor) == 2) {
+ pkt_remove_dev(MKDEV(major, minor));
+ return count;
+ }
+ return -EINVAL;
+}
+static CLASS_ATTR_WO(remove);
+
+static struct attribute *class_pktcdvd_attrs[] = {
+ &class_attr_add.attr,
+ &class_attr_remove.attr,
+ &class_attr_device_map.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(class_pktcdvd);
+
+static int pkt_sysfs_init(void)
+{
+ int ret = 0;
+
+ /*
+ * create control files in sysfs
+ * /sys/class/pktcdvd/...
+ */
+ class_pktcdvd = kzalloc(sizeof(*class_pktcdvd), GFP_KERNEL);
+ if (!class_pktcdvd)
+ return -ENOMEM;
+ class_pktcdvd->name = DRIVER_NAME;
+ class_pktcdvd->owner = THIS_MODULE;
+ class_pktcdvd->class_release = class_pktcdvd_release;
+ class_pktcdvd->class_groups = class_pktcdvd_groups;
+ ret = class_register(class_pktcdvd);
+ if (ret) {
+ kfree(class_pktcdvd);
+ class_pktcdvd = NULL;
+ pr_err("failed to create class pktcdvd\n");
+ return ret;
+ }
+ return 0;
+}
+
+static void pkt_sysfs_cleanup(void)
+{
+ if (class_pktcdvd)
+ class_destroy(class_pktcdvd);
+ class_pktcdvd = NULL;
+}
+
+/********************************************************************
+ entries in debugfs
+
+ /sys/kernel/debug/pktcdvd[0-7]/
+ info
+
+ *******************************************************************/
+
+static int pkt_debugfs_seq_show(struct seq_file *m, void *p)
+{
+ return pkt_seq_show(m, p);
+}
+
+static int pkt_debugfs_fops_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pkt_debugfs_seq_show, inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+ .open = pkt_debugfs_fops_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static void pkt_debugfs_dev_new(struct pktcdvd_device *pd)
+{
+ if (!pkt_debugfs_root)
+ return;
+ pd->dfs_d_root = debugfs_create_dir(pd->name, pkt_debugfs_root);
+ if (!pd->dfs_d_root)
+ return;
+
+ pd->dfs_f_info = debugfs_create_file("info", 0444,
+ pd->dfs_d_root, pd, &debug_fops);
+}
+
+static void pkt_debugfs_dev_remove(struct pktcdvd_device *pd)
+{
+ if (!pkt_debugfs_root)
+ return;
+ debugfs_remove(pd->dfs_f_info);
+ debugfs_remove(pd->dfs_d_root);
+ pd->dfs_f_info = NULL;
+ pd->dfs_d_root = NULL;
+}
+
+static void pkt_debugfs_init(void)
+{
+ pkt_debugfs_root = debugfs_create_dir(DRIVER_NAME, NULL);
+}
+
+static void pkt_debugfs_cleanup(void)
+{
+ debugfs_remove(pkt_debugfs_root);
+ pkt_debugfs_root = NULL;
+}
+
+/* ----------------------------------------------------------*/
+
+
+static void pkt_bio_finished(struct pktcdvd_device *pd)
+{
+ BUG_ON(atomic_read(&pd->cdrw.pending_bios) <= 0);
+ if (atomic_dec_and_test(&pd->cdrw.pending_bios)) {
+ pkt_dbg(2, pd, "queue empty\n");
+ atomic_set(&pd->iosched.attention, 1);
+ wake_up(&pd->wqueue);
+ }
+}
+
+/*
+ * Allocate a packet_data struct
+ */
+static struct packet_data *pkt_alloc_packet_data(int frames)
+{
+ int i;
+ struct packet_data *pkt;
+
+ pkt = kzalloc(sizeof(struct packet_data), GFP_KERNEL);
+ if (!pkt)
+ goto no_pkt;
+
+ pkt->frames = frames;
+ pkt->w_bio = bio_kmalloc(frames, GFP_KERNEL);
+ if (!pkt->w_bio)
+ goto no_bio;
+
+ for (i = 0; i < frames / FRAMES_PER_PAGE; i++) {
+ pkt->pages[i] = alloc_page(GFP_KERNEL|__GFP_ZERO);
+ if (!pkt->pages[i])
+ goto no_page;
+ }
+
+ spin_lock_init(&pkt->lock);
+ bio_list_init(&pkt->orig_bios);
+
+ for (i = 0; i < frames; i++) {
+ pkt->r_bios[i] = bio_kmalloc(1, GFP_KERNEL);
+ if (!pkt->r_bios[i])
+ goto no_rd_bio;
+ }
+
+ return pkt;
+
+no_rd_bio:
+ for (i = 0; i < frames; i++)
+ kfree(pkt->r_bios[i]);
+no_page:
+ for (i = 0; i < frames / FRAMES_PER_PAGE; i++)
+ if (pkt->pages[i])
+ __free_page(pkt->pages[i]);
+ kfree(pkt->w_bio);
+no_bio:
+ kfree(pkt);
+no_pkt:
+ return NULL;
+}
+
+/*
+ * Free a packet_data struct
+ */
+static void pkt_free_packet_data(struct packet_data *pkt)
+{
+ int i;
+
+ for (i = 0; i < pkt->frames; i++)
+ kfree(pkt->r_bios[i]);
+ for (i = 0; i < pkt->frames / FRAMES_PER_PAGE; i++)
+ __free_page(pkt->pages[i]);
+ kfree(pkt->w_bio);
+ kfree(pkt);
+}
+
+static void pkt_shrink_pktlist(struct pktcdvd_device *pd)
+{
+ struct packet_data *pkt, *next;
+
+ BUG_ON(!list_empty(&pd->cdrw.pkt_active_list));
+
+ list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_free_list, list) {
+ pkt_free_packet_data(pkt);
+ }
+ INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
+}
+
+static int pkt_grow_pktlist(struct pktcdvd_device *pd, int nr_packets)
+{
+ struct packet_data *pkt;
+
+ BUG_ON(!list_empty(&pd->cdrw.pkt_free_list));
+
+ while (nr_packets > 0) {
+ pkt = pkt_alloc_packet_data(pd->settings.size >> 2);
+ if (!pkt) {
+ pkt_shrink_pktlist(pd);
+ return 0;
+ }
+ pkt->id = nr_packets;
+ pkt->pd = pd;
+ list_add(&pkt->list, &pd->cdrw.pkt_free_list);
+ nr_packets--;
+ }
+ return 1;
+}
+
+static inline struct pkt_rb_node *pkt_rbtree_next(struct pkt_rb_node *node)
+{
+ struct rb_node *n = rb_next(&node->rb_node);
+ if (!n)
+ return NULL;
+ return rb_entry(n, struct pkt_rb_node, rb_node);
+}
+
+static void pkt_rbtree_erase(struct pktcdvd_device *pd, struct pkt_rb_node *node)
+{
+ rb_erase(&node->rb_node, &pd->bio_queue);
+ mempool_free(node, &pd->rb_pool);
+ pd->bio_queue_size--;
+ BUG_ON(pd->bio_queue_size < 0);
+}
+
+/*
+ * Find the first node in the pd->bio_queue rb tree with a starting sector >= s.
+ */
+static struct pkt_rb_node *pkt_rbtree_find(struct pktcdvd_device *pd, sector_t s)
+{
+ struct rb_node *n = pd->bio_queue.rb_node;
+ struct rb_node *next;
+ struct pkt_rb_node *tmp;
+
+ if (!n) {
+ BUG_ON(pd->bio_queue_size > 0);
+ return NULL;
+ }
+
+ for (;;) {
+ tmp = rb_entry(n, struct pkt_rb_node, rb_node);
+ if (s <= tmp->bio->bi_iter.bi_sector)
+ next = n->rb_left;
+ else
+ next = n->rb_right;
+ if (!next)
+ break;
+ n = next;
+ }
+
+ if (s > tmp->bio->bi_iter.bi_sector) {
+ tmp = pkt_rbtree_next(tmp);
+ if (!tmp)
+ return NULL;
+ }
+ BUG_ON(s > tmp->bio->bi_iter.bi_sector);
+ return tmp;
+}
+
+/*
+ * Insert a node into the pd->bio_queue rb tree.
+ */
+static void pkt_rbtree_insert(struct pktcdvd_device *pd, struct pkt_rb_node *node)
+{
+ struct rb_node **p = &pd->bio_queue.rb_node;
+ struct rb_node *parent = NULL;
+ sector_t s = node->bio->bi_iter.bi_sector;
+ struct pkt_rb_node *tmp;
+
+ while (*p) {
+ parent = *p;
+ tmp = rb_entry(parent, struct pkt_rb_node, rb_node);
+ if (s < tmp->bio->bi_iter.bi_sector)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ rb_link_node(&node->rb_node, parent, p);
+ rb_insert_color(&node->rb_node, &pd->bio_queue);
+ pd->bio_queue_size++;
+}
+
+/*
+ * Send a packet_command to the underlying block device and
+ * wait for completion.
+ */
+static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command *cgc)
+{
+ struct request_queue *q = bdev_get_queue(pd->bdev);
+ struct scsi_cmnd *scmd;
+ struct request *rq;
+ int ret = 0;
+
+ rq = scsi_alloc_request(q, (cgc->data_direction == CGC_DATA_WRITE) ?
+ REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0);
+ if (IS_ERR(rq))
+ return PTR_ERR(rq);
+ scmd = blk_mq_rq_to_pdu(rq);
+
+ if (cgc->buflen) {
+ ret = blk_rq_map_kern(q, rq, cgc->buffer, cgc->buflen,
+ GFP_NOIO);
+ if (ret)
+ goto out;
+ }
+
+ scmd->cmd_len = COMMAND_SIZE(cgc->cmd[0]);
+ memcpy(scmd->cmnd, cgc->cmd, CDROM_PACKET_SIZE);
+
+ rq->timeout = 60*HZ;
+ if (cgc->quiet)
+ rq->rq_flags |= RQF_QUIET;
+
+ blk_execute_rq(rq, false);
+ if (scmd->result)
+ ret = -EIO;
+out:
+ blk_mq_free_request(rq);
+ return ret;
+}
+
+static const char *sense_key_string(__u8 index)
+{
+ static const char * const info[] = {
+ "No sense", "Recovered error", "Not ready",
+ "Medium error", "Hardware error", "Illegal request",
+ "Unit attention", "Data protect", "Blank check",
+ };
+
+ return index < ARRAY_SIZE(info) ? info[index] : "INVALID";
+}
+
+/*
+ * A generic sense dump / resolve mechanism should be implemented across
+ * all ATAPI + SCSI devices.
+ */
+static void pkt_dump_sense(struct pktcdvd_device *pd,
+ struct packet_command *cgc)
+{
+ struct scsi_sense_hdr *sshdr = cgc->sshdr;
+
+ if (sshdr)
+ pkt_err(pd, "%*ph - sense %02x.%02x.%02x (%s)\n",
+ CDROM_PACKET_SIZE, cgc->cmd,
+ sshdr->sense_key, sshdr->asc, sshdr->ascq,
+ sense_key_string(sshdr->sense_key));
+ else
+ pkt_err(pd, "%*ph - no sense\n", CDROM_PACKET_SIZE, cgc->cmd);
+}
+
+/*
+ * flush the drive cache to media
+ */
+static int pkt_flush_cache(struct pktcdvd_device *pd)
+{
+ struct packet_command cgc;
+
+ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+ cgc.cmd[0] = GPCMD_FLUSH_CACHE;
+ cgc.quiet = 1;
+
+ /*
+ * the IMMED bit -- we default to not setting it, although that
+ * would allow a much faster close, this is safer
+ */
+#if 0
+ cgc.cmd[1] = 1 << 1;
+#endif
+ return pkt_generic_packet(pd, &cgc);
+}
+
+/*
+ * speed is given as the normal factor, e.g. 4 for 4x
+ */
+static noinline_for_stack int pkt_set_speed(struct pktcdvd_device *pd,
+ unsigned write_speed, unsigned read_speed)
+{
+ struct packet_command cgc;
+ struct scsi_sense_hdr sshdr;
+ int ret;
+
+ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+ cgc.sshdr = &sshdr;
+ cgc.cmd[0] = GPCMD_SET_SPEED;
+ cgc.cmd[2] = (read_speed >> 8) & 0xff;
+ cgc.cmd[3] = read_speed & 0xff;
+ cgc.cmd[4] = (write_speed >> 8) & 0xff;
+ cgc.cmd[5] = write_speed & 0xff;
+
+ ret = pkt_generic_packet(pd, &cgc);
+ if (ret)
+ pkt_dump_sense(pd, &cgc);
+
+ return ret;
+}
+
+/*
+ * Queue a bio for processing by the low-level CD device. Must be called
+ * from process context.
+ */
+static void pkt_queue_bio(struct pktcdvd_device *pd, struct bio *bio)
+{
+ spin_lock(&pd->iosched.lock);
+ if (bio_data_dir(bio) == READ)
+ bio_list_add(&pd->iosched.read_queue, bio);
+ else
+ bio_list_add(&pd->iosched.write_queue, bio);
+ spin_unlock(&pd->iosched.lock);
+
+ atomic_set(&pd->iosched.attention, 1);
+ wake_up(&pd->wqueue);
+}
+
+/*
+ * Process the queued read/write requests. This function handles special
+ * requirements for CDRW drives:
+ * - A cache flush command must be inserted before a read request if the
+ * previous request was a write.
+ * - Switching between reading and writing is slow, so don't do it more often
+ * than necessary.
+ * - Optimize for throughput at the expense of latency. This means that streaming
+ * writes will never be interrupted by a read, but if the drive has to seek
+ * before the next write, switch to reading instead if there are any pending
+ * read requests.
+ * - Set the read speed according to current usage pattern. When only reading
+ * from the device, it's best to use the highest possible read speed, but
+ * when switching often between reading and writing, it's better to have the
+ * same read and write speeds.
+ */
+static void pkt_iosched_process_queue(struct pktcdvd_device *pd)
+{
+
+ if (atomic_read(&pd->iosched.attention) == 0)
+ return;
+ atomic_set(&pd->iosched.attention, 0);
+
+ for (;;) {
+ struct bio *bio;
+ int reads_queued, writes_queued;
+
+ spin_lock(&pd->iosched.lock);
+ reads_queued = !bio_list_empty(&pd->iosched.read_queue);
+ writes_queued = !bio_list_empty(&pd->iosched.write_queue);
+ spin_unlock(&pd->iosched.lock);
+
+ if (!reads_queued && !writes_queued)
+ break;
+
+ if (pd->iosched.writing) {
+ int need_write_seek = 1;
+ spin_lock(&pd->iosched.lock);
+ bio = bio_list_peek(&pd->iosched.write_queue);
+ spin_unlock(&pd->iosched.lock);
+ if (bio && (bio->bi_iter.bi_sector ==
+ pd->iosched.last_write))
+ need_write_seek = 0;
+ if (need_write_seek && reads_queued) {
+ if (atomic_read(&pd->cdrw.pending_bios) > 0) {
+ pkt_dbg(2, pd, "write, waiting\n");
+ break;
+ }
+ pkt_flush_cache(pd);
+ pd->iosched.writing = 0;
+ }
+ } else {
+ if (!reads_queued && writes_queued) {
+ if (atomic_read(&pd->cdrw.pending_bios) > 0) {
+ pkt_dbg(2, pd, "read, waiting\n");
+ break;
+ }
+ pd->iosched.writing = 1;
+ }
+ }
+
+ spin_lock(&pd->iosched.lock);
+ if (pd->iosched.writing)
+ bio = bio_list_pop(&pd->iosched.write_queue);
+ else
+ bio = bio_list_pop(&pd->iosched.read_queue);
+ spin_unlock(&pd->iosched.lock);
+
+ if (!bio)
+ continue;
+
+ if (bio_data_dir(bio) == READ)
+ pd->iosched.successive_reads +=
+ bio->bi_iter.bi_size >> 10;
+ else {
+ pd->iosched.successive_reads = 0;
+ pd->iosched.last_write = bio_end_sector(bio);
+ }
+ if (pd->iosched.successive_reads >= HI_SPEED_SWITCH) {
+ if (pd->read_speed == pd->write_speed) {
+ pd->read_speed = MAX_SPEED;
+ pkt_set_speed(pd, pd->write_speed, pd->read_speed);
+ }
+ } else {
+ if (pd->read_speed != pd->write_speed) {
+ pd->read_speed = pd->write_speed;
+ pkt_set_speed(pd, pd->write_speed, pd->read_speed);
+ }
+ }
+
+ atomic_inc(&pd->cdrw.pending_bios);
+ submit_bio_noacct(bio);
+ }
+}
+
+/*
+ * Special care is needed if the underlying block device has a small
+ * max_phys_segments value.
+ */
+static int pkt_set_segment_merging(struct pktcdvd_device *pd, struct request_queue *q)
+{
+ if ((pd->settings.size << 9) / CD_FRAMESIZE
+ <= queue_max_segments(q)) {
+ /*
+ * The cdrom device can handle one segment/frame
+ */
+ clear_bit(PACKET_MERGE_SEGS, &pd->flags);
+ return 0;
+ } else if ((pd->settings.size << 9) / PAGE_SIZE
+ <= queue_max_segments(q)) {
+ /*
+ * We can handle this case at the expense of some extra memory
+ * copies during write operations
+ */
+ set_bit(PACKET_MERGE_SEGS, &pd->flags);
+ return 0;
+ } else {
+ pkt_err(pd, "cdrom max_phys_segments too small\n");
+ return -EIO;
+ }
+}
+
+static void pkt_end_io_read(struct bio *bio)
+{
+ struct packet_data *pkt = bio->bi_private;
+ struct pktcdvd_device *pd = pkt->pd;
+ BUG_ON(!pd);
+
+ pkt_dbg(2, pd, "bio=%p sec0=%llx sec=%llx err=%d\n",
+ bio, (unsigned long long)pkt->sector,
+ (unsigned long long)bio->bi_iter.bi_sector, bio->bi_status);
+
+ if (bio->bi_status)
+ atomic_inc(&pkt->io_errors);
+ bio_uninit(bio);
+ if (atomic_dec_and_test(&pkt->io_wait)) {
+ atomic_inc(&pkt->run_sm);
+ wake_up(&pd->wqueue);
+ }
+ pkt_bio_finished(pd);
+}
+
+static void pkt_end_io_packet_write(struct bio *bio)
+{
+ struct packet_data *pkt = bio->bi_private;
+ struct pktcdvd_device *pd = pkt->pd;
+ BUG_ON(!pd);
+
+ pkt_dbg(2, pd, "id=%d, err=%d\n", pkt->id, bio->bi_status);
+
+ pd->stats.pkt_ended++;
+
+ bio_uninit(bio);
+ pkt_bio_finished(pd);
+ atomic_dec(&pkt->io_wait);
+ atomic_inc(&pkt->run_sm);
+ wake_up(&pd->wqueue);
+}
+
+/*
+ * Schedule reads for the holes in a packet
+ */
+static void pkt_gather_data(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+ int frames_read = 0;
+ struct bio *bio;
+ int f;
+ char written[PACKET_MAX_SIZE];
+
+ BUG_ON(bio_list_empty(&pkt->orig_bios));
+
+ atomic_set(&pkt->io_wait, 0);
+ atomic_set(&pkt->io_errors, 0);
+
+ /*
+ * Figure out which frames we need to read before we can write.
+ */
+ memset(written, 0, sizeof(written));
+ spin_lock(&pkt->lock);
+ bio_list_for_each(bio, &pkt->orig_bios) {
+ int first_frame = (bio->bi_iter.bi_sector - pkt->sector) /
+ (CD_FRAMESIZE >> 9);
+ int num_frames = bio->bi_iter.bi_size / CD_FRAMESIZE;
+ pd->stats.secs_w += num_frames * (CD_FRAMESIZE >> 9);
+ BUG_ON(first_frame < 0);
+ BUG_ON(first_frame + num_frames > pkt->frames);
+ for (f = first_frame; f < first_frame + num_frames; f++)
+ written[f] = 1;
+ }
+ spin_unlock(&pkt->lock);
+
+ if (pkt->cache_valid) {
+ pkt_dbg(2, pd, "zone %llx cached\n",
+ (unsigned long long)pkt->sector);
+ goto out_account;
+ }
+
+ /*
+ * Schedule reads for missing parts of the packet.
+ */
+ for (f = 0; f < pkt->frames; f++) {
+ int p, offset;
+
+ if (written[f])
+ continue;
+
+ bio = pkt->r_bios[f];
+ bio_init(bio, pd->bdev, bio->bi_inline_vecs, 1, REQ_OP_READ);
+ bio->bi_iter.bi_sector = pkt->sector + f * (CD_FRAMESIZE >> 9);
+ bio->bi_end_io = pkt_end_io_read;
+ bio->bi_private = pkt;
+
+ p = (f * CD_FRAMESIZE) / PAGE_SIZE;
+ offset = (f * CD_FRAMESIZE) % PAGE_SIZE;
+ pkt_dbg(2, pd, "Adding frame %d, page:%p offs:%d\n",
+ f, pkt->pages[p], offset);
+ if (!bio_add_page(bio, pkt->pages[p], CD_FRAMESIZE, offset))
+ BUG();
+
+ atomic_inc(&pkt->io_wait);
+ pkt_queue_bio(pd, bio);
+ frames_read++;
+ }
+
+out_account:
+ pkt_dbg(2, pd, "need %d frames for zone %llx\n",
+ frames_read, (unsigned long long)pkt->sector);
+ pd->stats.pkt_started++;
+ pd->stats.secs_rg += frames_read * (CD_FRAMESIZE >> 9);
+}
+
+/*
+ * Find a packet matching zone, or the least recently used packet if
+ * there is no match.
+ */
+static struct packet_data *pkt_get_packet_data(struct pktcdvd_device *pd, int zone)
+{
+ struct packet_data *pkt;
+
+ list_for_each_entry(pkt, &pd->cdrw.pkt_free_list, list) {
+ if (pkt->sector == zone || pkt->list.next == &pd->cdrw.pkt_free_list) {
+ list_del_init(&pkt->list);
+ if (pkt->sector != zone)
+ pkt->cache_valid = 0;
+ return pkt;
+ }
+ }
+ BUG();
+ return NULL;
+}
+
+static void pkt_put_packet_data(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+ if (pkt->cache_valid) {
+ list_add(&pkt->list, &pd->cdrw.pkt_free_list);
+ } else {
+ list_add_tail(&pkt->list, &pd->cdrw.pkt_free_list);
+ }
+}
+
+static inline void pkt_set_state(struct packet_data *pkt, enum packet_data_state state)
+{
+#if PACKET_DEBUG > 1
+ static const char *state_name[] = {
+ "IDLE", "WAITING", "READ_WAIT", "WRITE_WAIT", "RECOVERY", "FINISHED"
+ };
+ enum packet_data_state old_state = pkt->state;
+ pkt_dbg(2, pd, "pkt %2d : s=%6llx %s -> %s\n",
+ pkt->id, (unsigned long long)pkt->sector,
+ state_name[old_state], state_name[state]);
+#endif
+ pkt->state = state;
+}
+
+/*
+ * Scan the work queue to see if we can start a new packet.
+ * returns non-zero if any work was done.
+ */
+static int pkt_handle_queue(struct pktcdvd_device *pd)
+{
+ struct packet_data *pkt, *p;
+ struct bio *bio = NULL;
+ sector_t zone = 0; /* Suppress gcc warning */
+ struct pkt_rb_node *node, *first_node;
+ struct rb_node *n;
+
+ atomic_set(&pd->scan_queue, 0);
+
+ if (list_empty(&pd->cdrw.pkt_free_list)) {
+ pkt_dbg(2, pd, "no pkt\n");
+ return 0;
+ }
+
+ /*
+ * Try to find a zone we are not already working on.
+ */
+ spin_lock(&pd->lock);
+ first_node = pkt_rbtree_find(pd, pd->current_sector);
+ if (!first_node) {
+ n = rb_first(&pd->bio_queue);
+ if (n)
+ first_node = rb_entry(n, struct pkt_rb_node, rb_node);
+ }
+ node = first_node;
+ while (node) {
+ bio = node->bio;
+ zone = get_zone(bio->bi_iter.bi_sector, pd);
+ list_for_each_entry(p, &pd->cdrw.pkt_active_list, list) {
+ if (p->sector == zone) {
+ bio = NULL;
+ goto try_next_bio;
+ }
+ }
+ break;
+try_next_bio:
+ node = pkt_rbtree_next(node);
+ if (!node) {
+ n = rb_first(&pd->bio_queue);
+ if (n)
+ node = rb_entry(n, struct pkt_rb_node, rb_node);
+ }
+ if (node == first_node)
+ node = NULL;
+ }
+ spin_unlock(&pd->lock);
+ if (!bio) {
+ pkt_dbg(2, pd, "no bio\n");
+ return 0;
+ }
+
+ pkt = pkt_get_packet_data(pd, zone);
+
+ pd->current_sector = zone + pd->settings.size;
+ pkt->sector = zone;
+ BUG_ON(pkt->frames != pd->settings.size >> 2);
+ pkt->write_size = 0;
+
+ /*
+ * Scan work queue for bios in the same zone and link them
+ * to this packet.
+ */
+ spin_lock(&pd->lock);
+ pkt_dbg(2, pd, "looking for zone %llx\n", (unsigned long long)zone);
+ while ((node = pkt_rbtree_find(pd, zone)) != NULL) {
+ bio = node->bio;
+ pkt_dbg(2, pd, "found zone=%llx\n", (unsigned long long)
+ get_zone(bio->bi_iter.bi_sector, pd));
+ if (get_zone(bio->bi_iter.bi_sector, pd) != zone)
+ break;
+ pkt_rbtree_erase(pd, node);
+ spin_lock(&pkt->lock);
+ bio_list_add(&pkt->orig_bios, bio);
+ pkt->write_size += bio->bi_iter.bi_size / CD_FRAMESIZE;
+ spin_unlock(&pkt->lock);
+ }
+ /* check write congestion marks, and if bio_queue_size is
+ * below, wake up any waiters
+ */
+ if (pd->congested &&
+ pd->bio_queue_size <= pd->write_congestion_off) {
+ pd->congested = false;
+ wake_up_var(&pd->congested);
+ }
+ spin_unlock(&pd->lock);
+
+ pkt->sleep_time = max(PACKET_WAIT_TIME, 1);
+ pkt_set_state(pkt, PACKET_WAITING_STATE);
+ atomic_set(&pkt->run_sm, 1);
+
+ spin_lock(&pd->cdrw.active_list_lock);
+ list_add(&pkt->list, &pd->cdrw.pkt_active_list);
+ spin_unlock(&pd->cdrw.active_list_lock);
+
+ return 1;
+}
+
+/**
+ * bio_list_copy_data - copy contents of data buffers from one chain of bios to
+ * another
+ * @src: source bio list
+ * @dst: destination bio list
+ *
+ * Stops when it reaches the end of either the @src list or @dst list - that is,
+ * copies min(src->bi_size, dst->bi_size) bytes (or the equivalent for lists of
+ * bios).
+ */
+static void bio_list_copy_data(struct bio *dst, struct bio *src)
+{
+ struct bvec_iter src_iter = src->bi_iter;
+ struct bvec_iter dst_iter = dst->bi_iter;
+
+ while (1) {
+ if (!src_iter.bi_size) {
+ src = src->bi_next;
+ if (!src)
+ break;
+
+ src_iter = src->bi_iter;
+ }
+
+ if (!dst_iter.bi_size) {
+ dst = dst->bi_next;
+ if (!dst)
+ break;
+
+ dst_iter = dst->bi_iter;
+ }
+
+ bio_copy_data_iter(dst, &dst_iter, src, &src_iter);
+ }
+}
+
+/*
+ * Assemble a bio to write one packet and queue the bio for processing
+ * by the underlying block device.
+ */
+static void pkt_start_write(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+ int f;
+
+ bio_init(pkt->w_bio, pd->bdev, pkt->w_bio->bi_inline_vecs, pkt->frames,
+ REQ_OP_WRITE);
+ pkt->w_bio->bi_iter.bi_sector = pkt->sector;
+ pkt->w_bio->bi_end_io = pkt_end_io_packet_write;
+ pkt->w_bio->bi_private = pkt;
+
+ /* XXX: locking? */
+ for (f = 0; f < pkt->frames; f++) {
+ struct page *page = pkt->pages[(f * CD_FRAMESIZE) / PAGE_SIZE];
+ unsigned offset = (f * CD_FRAMESIZE) % PAGE_SIZE;
+
+ if (!bio_add_page(pkt->w_bio, page, CD_FRAMESIZE, offset))
+ BUG();
+ }
+ pkt_dbg(2, pd, "vcnt=%d\n", pkt->w_bio->bi_vcnt);
+
+ /*
+ * Fill-in bvec with data from orig_bios.
+ */
+ spin_lock(&pkt->lock);
+ bio_list_copy_data(pkt->w_bio, pkt->orig_bios.head);
+
+ pkt_set_state(pkt, PACKET_WRITE_WAIT_STATE);
+ spin_unlock(&pkt->lock);
+
+ pkt_dbg(2, pd, "Writing %d frames for zone %llx\n",
+ pkt->write_size, (unsigned long long)pkt->sector);
+
+ if (test_bit(PACKET_MERGE_SEGS, &pd->flags) || (pkt->write_size < pkt->frames))
+ pkt->cache_valid = 1;
+ else
+ pkt->cache_valid = 0;
+
+ /* Start the write request */
+ atomic_set(&pkt->io_wait, 1);
+ pkt_queue_bio(pd, pkt->w_bio);
+}
+
+static void pkt_finish_packet(struct packet_data *pkt, blk_status_t status)
+{
+ struct bio *bio;
+
+ if (status)
+ pkt->cache_valid = 0;
+
+ /* Finish all bios corresponding to this packet */
+ while ((bio = bio_list_pop(&pkt->orig_bios))) {
+ bio->bi_status = status;
+ bio_endio(bio);
+ }
+}
+
+static void pkt_run_state_machine(struct pktcdvd_device *pd, struct packet_data *pkt)
+{
+ pkt_dbg(2, pd, "pkt %d\n", pkt->id);
+
+ for (;;) {
+ switch (pkt->state) {
+ case PACKET_WAITING_STATE:
+ if ((pkt->write_size < pkt->frames) && (pkt->sleep_time > 0))
+ return;
+
+ pkt->sleep_time = 0;
+ pkt_gather_data(pd, pkt);
+ pkt_set_state(pkt, PACKET_READ_WAIT_STATE);
+ break;
+
+ case PACKET_READ_WAIT_STATE:
+ if (atomic_read(&pkt->io_wait) > 0)
+ return;
+
+ if (atomic_read(&pkt->io_errors) > 0) {
+ pkt_set_state(pkt, PACKET_RECOVERY_STATE);
+ } else {
+ pkt_start_write(pd, pkt);
+ }
+ break;
+
+ case PACKET_WRITE_WAIT_STATE:
+ if (atomic_read(&pkt->io_wait) > 0)
+ return;
+
+ if (!pkt->w_bio->bi_status) {
+ pkt_set_state(pkt, PACKET_FINISHED_STATE);
+ } else {
+ pkt_set_state(pkt, PACKET_RECOVERY_STATE);
+ }
+ break;
+
+ case PACKET_RECOVERY_STATE:
+ pkt_dbg(2, pd, "No recovery possible\n");
+ pkt_set_state(pkt, PACKET_FINISHED_STATE);
+ break;
+
+ case PACKET_FINISHED_STATE:
+ pkt_finish_packet(pkt, pkt->w_bio->bi_status);
+ return;
+
+ default:
+ BUG();
+ break;
+ }
+ }
+}
+
+static void pkt_handle_packets(struct pktcdvd_device *pd)
+{
+ struct packet_data *pkt, *next;
+
+ /*
+ * Run state machine for active packets
+ */
+ list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+ if (atomic_read(&pkt->run_sm) > 0) {
+ atomic_set(&pkt->run_sm, 0);
+ pkt_run_state_machine(pd, pkt);
+ }
+ }
+
+ /*
+ * Move no longer active packets to the free list
+ */
+ spin_lock(&pd->cdrw.active_list_lock);
+ list_for_each_entry_safe(pkt, next, &pd->cdrw.pkt_active_list, list) {
+ if (pkt->state == PACKET_FINISHED_STATE) {
+ list_del(&pkt->list);
+ pkt_put_packet_data(pd, pkt);
+ pkt_set_state(pkt, PACKET_IDLE_STATE);
+ atomic_set(&pd->scan_queue, 1);
+ }
+ }
+ spin_unlock(&pd->cdrw.active_list_lock);
+}
+
+static void pkt_count_states(struct pktcdvd_device *pd, int *states)
+{
+ struct packet_data *pkt;
+ int i;
+
+ for (i = 0; i < PACKET_NUM_STATES; i++)
+ states[i] = 0;
+
+ spin_lock(&pd->cdrw.active_list_lock);
+ list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+ states[pkt->state]++;
+ }
+ spin_unlock(&pd->cdrw.active_list_lock);
+}
+
+/*
+ * kcdrwd is woken up when writes have been queued for one of our
+ * registered devices
+ */
+static int kcdrwd(void *foobar)
+{
+ struct pktcdvd_device *pd = foobar;
+ struct packet_data *pkt;
+ long min_sleep_time, residue;
+
+ set_user_nice(current, MIN_NICE);
+ set_freezable();
+
+ for (;;) {
+ DECLARE_WAITQUEUE(wait, current);
+
+ /*
+ * Wait until there is something to do
+ */
+ add_wait_queue(&pd->wqueue, &wait);
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ /* Check if we need to run pkt_handle_queue */
+ if (atomic_read(&pd->scan_queue) > 0)
+ goto work_to_do;
+
+ /* Check if we need to run the state machine for some packet */
+ list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+ if (atomic_read(&pkt->run_sm) > 0)
+ goto work_to_do;
+ }
+
+ /* Check if we need to process the iosched queues */
+ if (atomic_read(&pd->iosched.attention) != 0)
+ goto work_to_do;
+
+ /* Otherwise, go to sleep */
+ if (PACKET_DEBUG > 1) {
+ int states[PACKET_NUM_STATES];
+ pkt_count_states(pd, states);
+ pkt_dbg(2, pd, "i:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n",
+ states[0], states[1], states[2],
+ states[3], states[4], states[5]);
+ }
+
+ min_sleep_time = MAX_SCHEDULE_TIMEOUT;
+ list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+ if (pkt->sleep_time && pkt->sleep_time < min_sleep_time)
+ min_sleep_time = pkt->sleep_time;
+ }
+
+ pkt_dbg(2, pd, "sleeping\n");
+ residue = schedule_timeout(min_sleep_time);
+ pkt_dbg(2, pd, "wake up\n");
+
+ /* make swsusp happy with our thread */
+ try_to_freeze();
+
+ list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+ if (!pkt->sleep_time)
+ continue;
+ pkt->sleep_time -= min_sleep_time - residue;
+ if (pkt->sleep_time <= 0) {
+ pkt->sleep_time = 0;
+ atomic_inc(&pkt->run_sm);
+ }
+ }
+
+ if (kthread_should_stop())
+ break;
+ }
+work_to_do:
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&pd->wqueue, &wait);
+
+ if (kthread_should_stop())
+ break;
+
+ /*
+ * if pkt_handle_queue returns true, we can queue
+ * another request.
+ */
+ while (pkt_handle_queue(pd))
+ ;
+
+ /*
+ * Handle packet state machine
+ */
+ pkt_handle_packets(pd);
+
+ /*
+ * Handle iosched queues
+ */
+ pkt_iosched_process_queue(pd);
+ }
+
+ return 0;
+}
+
+static void pkt_print_settings(struct pktcdvd_device *pd)
+{
+ pkt_info(pd, "%s packets, %u blocks, Mode-%c disc\n",
+ pd->settings.fp ? "Fixed" : "Variable",
+ pd->settings.size >> 2,
+ pd->settings.block_mode == 8 ? '1' : '2');
+}
+
+static int pkt_mode_sense(struct pktcdvd_device *pd, struct packet_command *cgc, int page_code, int page_control)
+{
+ memset(cgc->cmd, 0, sizeof(cgc->cmd));
+
+ cgc->cmd[0] = GPCMD_MODE_SENSE_10;
+ cgc->cmd[2] = page_code | (page_control << 6);
+ cgc->cmd[7] = cgc->buflen >> 8;
+ cgc->cmd[8] = cgc->buflen & 0xff;
+ cgc->data_direction = CGC_DATA_READ;
+ return pkt_generic_packet(pd, cgc);
+}
+
+static int pkt_mode_select(struct pktcdvd_device *pd, struct packet_command *cgc)
+{
+ memset(cgc->cmd, 0, sizeof(cgc->cmd));
+ memset(cgc->buffer, 0, 2);
+ cgc->cmd[0] = GPCMD_MODE_SELECT_10;
+ cgc->cmd[1] = 0x10; /* PF */
+ cgc->cmd[7] = cgc->buflen >> 8;
+ cgc->cmd[8] = cgc->buflen & 0xff;
+ cgc->data_direction = CGC_DATA_WRITE;
+ return pkt_generic_packet(pd, cgc);
+}
+
+static int pkt_get_disc_info(struct pktcdvd_device *pd, disc_information *di)
+{
+ struct packet_command cgc;
+ int ret;
+
+ /* set up command and get the disc info */
+ init_cdrom_command(&cgc, di, sizeof(*di), CGC_DATA_READ);
+ cgc.cmd[0] = GPCMD_READ_DISC_INFO;
+ cgc.cmd[8] = cgc.buflen = 2;
+ cgc.quiet = 1;
+
+ ret = pkt_generic_packet(pd, &cgc);
+ if (ret)
+ return ret;
+
+ /* not all drives have the same disc_info length, so requeue
+ * packet with the length the drive tells us it can supply
+ */
+ cgc.buflen = be16_to_cpu(di->disc_information_length) +
+ sizeof(di->disc_information_length);
+
+ if (cgc.buflen > sizeof(disc_information))
+ cgc.buflen = sizeof(disc_information);
+
+ cgc.cmd[8] = cgc.buflen;
+ return pkt_generic_packet(pd, &cgc);
+}
+
+static int pkt_get_track_info(struct pktcdvd_device *pd, __u16 track, __u8 type, track_information *ti)
+{
+ struct packet_command cgc;
+ int ret;
+
+ init_cdrom_command(&cgc, ti, 8, CGC_DATA_READ);
+ cgc.cmd[0] = GPCMD_READ_TRACK_RZONE_INFO;
+ cgc.cmd[1] = type & 3;
+ cgc.cmd[4] = (track & 0xff00) >> 8;
+ cgc.cmd[5] = track & 0xff;
+ cgc.cmd[8] = 8;
+ cgc.quiet = 1;
+
+ ret = pkt_generic_packet(pd, &cgc);
+ if (ret)
+ return ret;
+
+ cgc.buflen = be16_to_cpu(ti->track_information_length) +
+ sizeof(ti->track_information_length);
+
+ if (cgc.buflen > sizeof(track_information))
+ cgc.buflen = sizeof(track_information);
+
+ cgc.cmd[8] = cgc.buflen;
+ return pkt_generic_packet(pd, &cgc);
+}
+
+static noinline_for_stack int pkt_get_last_written(struct pktcdvd_device *pd,
+ long *last_written)
+{
+ disc_information di;
+ track_information ti;
+ __u32 last_track;
+ int ret;
+
+ ret = pkt_get_disc_info(pd, &di);
+ if (ret)
+ return ret;
+
+ last_track = (di.last_track_msb << 8) | di.last_track_lsb;
+ ret = pkt_get_track_info(pd, last_track, 1, &ti);
+ if (ret)
+ return ret;
+
+ /* if this track is blank, try the previous. */
+ if (ti.blank) {
+ last_track--;
+ ret = pkt_get_track_info(pd, last_track, 1, &ti);
+ if (ret)
+ return ret;
+ }
+
+ /* if last recorded field is valid, return it. */
+ if (ti.lra_v) {
+ *last_written = be32_to_cpu(ti.last_rec_address);
+ } else {
+ /* make it up instead */
+ *last_written = be32_to_cpu(ti.track_start) +
+ be32_to_cpu(ti.track_size);
+ if (ti.free_blocks)
+ *last_written -= (be32_to_cpu(ti.free_blocks) + 7);
+ }
+ return 0;
+}
+
+/*
+ * write mode select package based on pd->settings
+ */
+static noinline_for_stack int pkt_set_write_settings(struct pktcdvd_device *pd)
+{
+ struct packet_command cgc;
+ struct scsi_sense_hdr sshdr;
+ write_param_page *wp;
+ char buffer[128];
+ int ret, size;
+
+ /* doesn't apply to DVD+RW or DVD-RAM */
+ if ((pd->mmc3_profile == 0x1a) || (pd->mmc3_profile == 0x12))
+ return 0;
+
+ memset(buffer, 0, sizeof(buffer));
+ init_cdrom_command(&cgc, buffer, sizeof(*wp), CGC_DATA_READ);
+ cgc.sshdr = &sshdr;
+ ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0);
+ if (ret) {
+ pkt_dump_sense(pd, &cgc);
+ return ret;
+ }
+
+ size = 2 + ((buffer[0] << 8) | (buffer[1] & 0xff));
+ pd->mode_offset = (buffer[6] << 8) | (buffer[7] & 0xff);
+ if (size > sizeof(buffer))
+ size = sizeof(buffer);
+
+ /*
+ * now get it all
+ */
+ init_cdrom_command(&cgc, buffer, size, CGC_DATA_READ);
+ cgc.sshdr = &sshdr;
+ ret = pkt_mode_sense(pd, &cgc, GPMODE_WRITE_PARMS_PAGE, 0);
+ if (ret) {
+ pkt_dump_sense(pd, &cgc);
+ return ret;
+ }
+
+ /*
+ * write page is offset header + block descriptor length
+ */
+ wp = (write_param_page *) &buffer[sizeof(struct mode_page_header) + pd->mode_offset];
+
+ wp->fp = pd->settings.fp;
+ wp->track_mode = pd->settings.track_mode;
+ wp->write_type = pd->settings.write_type;
+ wp->data_block_type = pd->settings.block_mode;
+
+ wp->multi_session = 0;
+
+#ifdef PACKET_USE_LS
+ wp->link_size = 7;
+ wp->ls_v = 1;
+#endif
+
+ if (wp->data_block_type == PACKET_BLOCK_MODE1) {
+ wp->session_format = 0;
+ wp->subhdr2 = 0x20;
+ } else if (wp->data_block_type == PACKET_BLOCK_MODE2) {
+ wp->session_format = 0x20;
+ wp->subhdr2 = 8;
+#if 0
+ wp->mcn[0] = 0x80;
+ memcpy(&wp->mcn[1], PACKET_MCN, sizeof(wp->mcn) - 1);
+#endif
+ } else {
+ /*
+ * paranoia
+ */
+ pkt_err(pd, "write mode wrong %d\n", wp->data_block_type);
+ return 1;
+ }
+ wp->packet_size = cpu_to_be32(pd->settings.size >> 2);
+
+ cgc.buflen = cgc.cmd[8] = size;
+ ret = pkt_mode_select(pd, &cgc);
+ if (ret) {
+ pkt_dump_sense(pd, &cgc);
+ return ret;
+ }
+
+ pkt_print_settings(pd);
+ return 0;
+}
+
+/*
+ * 1 -- we can write to this track, 0 -- we can't
+ */
+static int pkt_writable_track(struct pktcdvd_device *pd, track_information *ti)
+{
+ switch (pd->mmc3_profile) {
+ case 0x1a: /* DVD+RW */
+ case 0x12: /* DVD-RAM */
+ /* The track is always writable on DVD+RW/DVD-RAM */
+ return 1;
+ default:
+ break;
+ }
+
+ if (!ti->packet || !ti->fp)
+ return 0;
+
+ /*
+ * "good" settings as per Mt Fuji.
+ */
+ if (ti->rt == 0 && ti->blank == 0)
+ return 1;
+
+ if (ti->rt == 0 && ti->blank == 1)
+ return 1;
+
+ if (ti->rt == 1 && ti->blank == 0)
+ return 1;
+
+ pkt_err(pd, "bad state %d-%d-%d\n", ti->rt, ti->blank, ti->packet);
+ return 0;
+}
+
+/*
+ * 1 -- we can write to this disc, 0 -- we can't
+ */
+static int pkt_writable_disc(struct pktcdvd_device *pd, disc_information *di)
+{
+ switch (pd->mmc3_profile) {
+ case 0x0a: /* CD-RW */
+ case 0xffff: /* MMC3 not supported */
+ break;
+ case 0x1a: /* DVD+RW */
+ case 0x13: /* DVD-RW */
+ case 0x12: /* DVD-RAM */
+ return 1;
+ default:
+ pkt_dbg(2, pd, "Wrong disc profile (%x)\n",
+ pd->mmc3_profile);
+ return 0;
+ }
+
+ /*
+ * for disc type 0xff we should probably reserve a new track.
+ * but i'm not sure, should we leave this to user apps? probably.
+ */
+ if (di->disc_type == 0xff) {
+ pkt_notice(pd, "unknown disc - no track?\n");
+ return 0;
+ }
+
+ if (di->disc_type != 0x20 && di->disc_type != 0) {
+ pkt_err(pd, "wrong disc type (%x)\n", di->disc_type);
+ return 0;
+ }
+
+ if (di->erasable == 0) {
+ pkt_notice(pd, "disc not erasable\n");
+ return 0;
+ }
+
+ if (di->border_status == PACKET_SESSION_RESERVED) {
+ pkt_err(pd, "can't write to last track (reserved)\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static noinline_for_stack int pkt_probe_settings(struct pktcdvd_device *pd)
+{
+ struct packet_command cgc;
+ unsigned char buf[12];
+ disc_information di;
+ track_information ti;
+ int ret, track;
+
+ init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
+ cgc.cmd[0] = GPCMD_GET_CONFIGURATION;
+ cgc.cmd[8] = 8;
+ ret = pkt_generic_packet(pd, &cgc);
+ pd->mmc3_profile = ret ? 0xffff : buf[6] << 8 | buf[7];
+
+ memset(&di, 0, sizeof(disc_information));
+ memset(&ti, 0, sizeof(track_information));
+
+ ret = pkt_get_disc_info(pd, &di);
+ if (ret) {
+ pkt_err(pd, "failed get_disc\n");
+ return ret;
+ }
+
+ if (!pkt_writable_disc(pd, &di))
+ return -EROFS;
+
+ pd->type = di.erasable ? PACKET_CDRW : PACKET_CDR;
+
+ track = 1; /* (di.last_track_msb << 8) | di.last_track_lsb; */
+ ret = pkt_get_track_info(pd, track, 1, &ti);
+ if (ret) {
+ pkt_err(pd, "failed get_track\n");
+ return ret;
+ }
+
+ if (!pkt_writable_track(pd, &ti)) {
+ pkt_err(pd, "can't write to this track\n");
+ return -EROFS;
+ }
+
+ /*
+ * we keep packet size in 512 byte units, makes it easier to
+ * deal with request calculations.
+ */
+ pd->settings.size = be32_to_cpu(ti.fixed_packet_size) << 2;
+ if (pd->settings.size == 0) {
+ pkt_notice(pd, "detected zero packet size!\n");
+ return -ENXIO;
+ }
+ if (pd->settings.size > PACKET_MAX_SECTORS) {
+ pkt_err(pd, "packet size is too big\n");
+ return -EROFS;
+ }
+ pd->settings.fp = ti.fp;
+ pd->offset = (be32_to_cpu(ti.track_start) << 2) & (pd->settings.size - 1);
+
+ if (ti.nwa_v) {
+ pd->nwa = be32_to_cpu(ti.next_writable);
+ set_bit(PACKET_NWA_VALID, &pd->flags);
+ }
+
+ /*
+ * in theory we could use lra on -RW media as well and just zero
+ * blocks that haven't been written yet, but in practice that
+ * is just a no-go. we'll use that for -R, naturally.
+ */
+ if (ti.lra_v) {
+ pd->lra = be32_to_cpu(ti.last_rec_address);
+ set_bit(PACKET_LRA_VALID, &pd->flags);
+ } else {
+ pd->lra = 0xffffffff;
+ set_bit(PACKET_LRA_VALID, &pd->flags);
+ }
+
+ /*
+ * fine for now
+ */
+ pd->settings.link_loss = 7;
+ pd->settings.write_type = 0; /* packet */
+ pd->settings.track_mode = ti.track_mode;
+
+ /*
+ * mode1 or mode2 disc
+ */
+ switch (ti.data_mode) {
+ case PACKET_MODE1:
+ pd->settings.block_mode = PACKET_BLOCK_MODE1;
+ break;
+ case PACKET_MODE2:
+ pd->settings.block_mode = PACKET_BLOCK_MODE2;
+ break;
+ default:
+ pkt_err(pd, "unknown data mode\n");
+ return -EROFS;
+ }
+ return 0;
+}
+
+/*
+ * enable/disable write caching on drive
+ */
+static noinline_for_stack int pkt_write_caching(struct pktcdvd_device *pd,
+ int set)
+{
+ struct packet_command cgc;
+ struct scsi_sense_hdr sshdr;
+ unsigned char buf[64];
+ int ret;
+
+ init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_READ);
+ cgc.sshdr = &sshdr;
+ cgc.buflen = pd->mode_offset + 12;
+
+ /*
+ * caching mode page might not be there, so quiet this command
+ */
+ cgc.quiet = 1;
+
+ ret = pkt_mode_sense(pd, &cgc, GPMODE_WCACHING_PAGE, 0);
+ if (ret)
+ return ret;
+
+ buf[pd->mode_offset + 10] |= (!!set << 2);
+
+ cgc.buflen = cgc.cmd[8] = 2 + ((buf[0] << 8) | (buf[1] & 0xff));
+ ret = pkt_mode_select(pd, &cgc);
+ if (ret) {
+ pkt_err(pd, "write caching control failed\n");
+ pkt_dump_sense(pd, &cgc);
+ } else if (!ret && set)
+ pkt_notice(pd, "enabled write caching\n");
+ return ret;
+}
+
+static int pkt_lock_door(struct pktcdvd_device *pd, int lockflag)
+{
+ struct packet_command cgc;
+
+ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+ cgc.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
+ cgc.cmd[4] = lockflag ? 1 : 0;
+ return pkt_generic_packet(pd, &cgc);
+}
+
+/*
+ * Returns drive maximum write speed
+ */
+static noinline_for_stack int pkt_get_max_speed(struct pktcdvd_device *pd,
+ unsigned *write_speed)
+{
+ struct packet_command cgc;
+ struct scsi_sense_hdr sshdr;
+ unsigned char buf[256+18];
+ unsigned char *cap_buf;
+ int ret, offset;
+
+ cap_buf = &buf[sizeof(struct mode_page_header) + pd->mode_offset];
+ init_cdrom_command(&cgc, buf, sizeof(buf), CGC_DATA_UNKNOWN);
+ cgc.sshdr = &sshdr;
+
+ ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+ if (ret) {
+ cgc.buflen = pd->mode_offset + cap_buf[1] + 2 +
+ sizeof(struct mode_page_header);
+ ret = pkt_mode_sense(pd, &cgc, GPMODE_CAPABILITIES_PAGE, 0);
+ if (ret) {
+ pkt_dump_sense(pd, &cgc);
+ return ret;
+ }
+ }
+
+ offset = 20; /* Obsoleted field, used by older drives */
+ if (cap_buf[1] >= 28)
+ offset = 28; /* Current write speed selected */
+ if (cap_buf[1] >= 30) {
+ /* If the drive reports at least one "Logical Unit Write
+ * Speed Performance Descriptor Block", use the information
+ * in the first block. (contains the highest speed)
+ */
+ int num_spdb = (cap_buf[30] << 8) + cap_buf[31];
+ if (num_spdb > 0)
+ offset = 34;
+ }
+
+ *write_speed = (cap_buf[offset] << 8) | cap_buf[offset + 1];
+ return 0;
+}
+
+/* These tables from cdrecord - I don't have orange book */
+/* standard speed CD-RW (1-4x) */
+static char clv_to_speed[16] = {
+ /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
+ 0, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+/* high speed CD-RW (-10x) */
+static char hs_clv_to_speed[16] = {
+ /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
+ 0, 2, 4, 6, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+/* ultra high speed CD-RW */
+static char us_clv_to_speed[16] = {
+ /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
+ 0, 2, 4, 8, 0, 0,16, 0,24,32,40,48, 0, 0, 0, 0
+};
+
+/*
+ * reads the maximum media speed from ATIP
+ */
+static noinline_for_stack int pkt_media_speed(struct pktcdvd_device *pd,
+ unsigned *speed)
+{
+ struct packet_command cgc;
+ struct scsi_sense_hdr sshdr;
+ unsigned char buf[64];
+ unsigned int size, st, sp;
+ int ret;
+
+ init_cdrom_command(&cgc, buf, 2, CGC_DATA_READ);
+ cgc.sshdr = &sshdr;
+ cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
+ cgc.cmd[1] = 2;
+ cgc.cmd[2] = 4; /* READ ATIP */
+ cgc.cmd[8] = 2;
+ ret = pkt_generic_packet(pd, &cgc);
+ if (ret) {
+ pkt_dump_sense(pd, &cgc);
+ return ret;
+ }
+ size = ((unsigned int) buf[0]<<8) + buf[1] + 2;
+ if (size > sizeof(buf))
+ size = sizeof(buf);
+
+ init_cdrom_command(&cgc, buf, size, CGC_DATA_READ);
+ cgc.sshdr = &sshdr;
+ cgc.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
+ cgc.cmd[1] = 2;
+ cgc.cmd[2] = 4;
+ cgc.cmd[8] = size;
+ ret = pkt_generic_packet(pd, &cgc);
+ if (ret) {
+ pkt_dump_sense(pd, &cgc);
+ return ret;
+ }
+
+ if (!(buf[6] & 0x40)) {
+ pkt_notice(pd, "disc type is not CD-RW\n");
+ return 1;
+ }
+ if (!(buf[6] & 0x4)) {
+ pkt_notice(pd, "A1 values on media are not valid, maybe not CDRW?\n");
+ return 1;
+ }
+
+ st = (buf[6] >> 3) & 0x7; /* disc sub-type */
+
+ sp = buf[16] & 0xf; /* max speed from ATIP A1 field */
+
+ /* Info from cdrecord */
+ switch (st) {
+ case 0: /* standard speed */
+ *speed = clv_to_speed[sp];
+ break;
+ case 1: /* high speed */
+ *speed = hs_clv_to_speed[sp];
+ break;
+ case 2: /* ultra high speed */
+ *speed = us_clv_to_speed[sp];
+ break;
+ default:
+ pkt_notice(pd, "unknown disc sub-type %d\n", st);
+ return 1;
+ }
+ if (*speed) {
+ pkt_info(pd, "maximum media speed: %d\n", *speed);
+ return 0;
+ } else {
+ pkt_notice(pd, "unknown speed %d for sub-type %d\n", sp, st);
+ return 1;
+ }
+}
+
+static noinline_for_stack int pkt_perform_opc(struct pktcdvd_device *pd)
+{
+ struct packet_command cgc;
+ struct scsi_sense_hdr sshdr;
+ int ret;
+
+ pkt_dbg(2, pd, "Performing OPC\n");
+
+ init_cdrom_command(&cgc, NULL, 0, CGC_DATA_NONE);
+ cgc.sshdr = &sshdr;
+ cgc.timeout = 60*HZ;
+ cgc.cmd[0] = GPCMD_SEND_OPC;
+ cgc.cmd[1] = 1;
+ ret = pkt_generic_packet(pd, &cgc);
+ if (ret)
+ pkt_dump_sense(pd, &cgc);
+ return ret;
+}
+
+static int pkt_open_write(struct pktcdvd_device *pd)
+{
+ int ret;
+ unsigned int write_speed, media_write_speed, read_speed;
+
+ ret = pkt_probe_settings(pd);
+ if (ret) {
+ pkt_dbg(2, pd, "failed probe\n");
+ return ret;
+ }
+
+ ret = pkt_set_write_settings(pd);
+ if (ret) {
+ pkt_dbg(1, pd, "failed saving write settings\n");
+ return -EIO;
+ }
+
+ pkt_write_caching(pd, USE_WCACHING);
+
+ ret = pkt_get_max_speed(pd, &write_speed);
+ if (ret)
+ write_speed = 16 * 177;
+ switch (pd->mmc3_profile) {
+ case 0x13: /* DVD-RW */
+ case 0x1a: /* DVD+RW */
+ case 0x12: /* DVD-RAM */
+ pkt_dbg(1, pd, "write speed %ukB/s\n", write_speed);
+ break;
+ default:
+ ret = pkt_media_speed(pd, &media_write_speed);
+ if (ret)
+ media_write_speed = 16;
+ write_speed = min(write_speed, media_write_speed * 177);
+ pkt_dbg(1, pd, "write speed %ux\n", write_speed / 176);
+ break;
+ }
+ read_speed = write_speed;
+
+ ret = pkt_set_speed(pd, write_speed, read_speed);
+ if (ret) {
+ pkt_dbg(1, pd, "couldn't set write speed\n");
+ return -EIO;
+ }
+ pd->write_speed = write_speed;
+ pd->read_speed = read_speed;
+
+ ret = pkt_perform_opc(pd);
+ if (ret) {
+ pkt_dbg(1, pd, "Optimum Power Calibration failed\n");
+ }
+
+ return 0;
+}
+
+/*
+ * called at open time.
+ */
+static int pkt_open_dev(struct pktcdvd_device *pd, fmode_t write)
+{
+ int ret;
+ long lba;
+ struct request_queue *q;
+ struct block_device *bdev;
+
+ /*
+ * We need to re-open the cdrom device without O_NONBLOCK to be able
+ * to read/write from/to it. It is already opened in O_NONBLOCK mode
+ * so open should not fail.
+ */
+ bdev = blkdev_get_by_dev(pd->bdev->bd_dev, FMODE_READ | FMODE_EXCL, pd);
+ if (IS_ERR(bdev)) {
+ ret = PTR_ERR(bdev);
+ goto out;
+ }
+
+ ret = pkt_get_last_written(pd, &lba);
+ if (ret) {
+ pkt_err(pd, "pkt_get_last_written failed\n");
+ goto out_putdev;
+ }
+
+ set_capacity(pd->disk, lba << 2);
+ set_capacity_and_notify(pd->bdev->bd_disk, lba << 2);
+
+ q = bdev_get_queue(pd->bdev);
+ if (write) {
+ ret = pkt_open_write(pd);
+ if (ret)
+ goto out_putdev;
+ /*
+ * Some CDRW drives can not handle writes larger than one packet,
+ * even if the size is a multiple of the packet size.
+ */
+ blk_queue_max_hw_sectors(q, pd->settings.size);
+ set_bit(PACKET_WRITABLE, &pd->flags);
+ } else {
+ pkt_set_speed(pd, MAX_SPEED, MAX_SPEED);
+ clear_bit(PACKET_WRITABLE, &pd->flags);
+ }
+
+ ret = pkt_set_segment_merging(pd, q);
+ if (ret)
+ goto out_putdev;
+
+ if (write) {
+ if (!pkt_grow_pktlist(pd, CONFIG_CDROM_PKTCDVD_BUFFERS)) {
+ pkt_err(pd, "not enough memory for buffers\n");
+ ret = -ENOMEM;
+ goto out_putdev;
+ }
+ pkt_info(pd, "%lukB available on disc\n", lba << 1);
+ }
+
+ return 0;
+
+out_putdev:
+ blkdev_put(bdev, FMODE_READ | FMODE_EXCL);
+out:
+ return ret;
+}
+
+/*
+ * called when the device is closed. makes sure that the device flushes
+ * the internal cache before we close.
+ */
+static void pkt_release_dev(struct pktcdvd_device *pd, int flush)
+{
+ if (flush && pkt_flush_cache(pd))
+ pkt_dbg(1, pd, "not flushing cache\n");
+
+ pkt_lock_door(pd, 0);
+
+ pkt_set_speed(pd, MAX_SPEED, MAX_SPEED);
+ blkdev_put(pd->bdev, FMODE_READ | FMODE_EXCL);
+
+ pkt_shrink_pktlist(pd);
+}
+
+static struct pktcdvd_device *pkt_find_dev_from_minor(unsigned int dev_minor)
+{
+ if (dev_minor >= MAX_WRITERS)
+ return NULL;
+
+ dev_minor = array_index_nospec(dev_minor, MAX_WRITERS);
+ return pkt_devs[dev_minor];
+}
+
+static int pkt_open(struct block_device *bdev, fmode_t mode)
+{
+ struct pktcdvd_device *pd = NULL;
+ int ret;
+
+ mutex_lock(&pktcdvd_mutex);
+ mutex_lock(&ctl_mutex);
+ pd = pkt_find_dev_from_minor(MINOR(bdev->bd_dev));
+ if (!pd) {
+ ret = -ENODEV;
+ goto out;
+ }
+ BUG_ON(pd->refcnt < 0);
+
+ pd->refcnt++;
+ if (pd->refcnt > 1) {
+ if ((mode & FMODE_WRITE) &&
+ !test_bit(PACKET_WRITABLE, &pd->flags)) {
+ ret = -EBUSY;
+ goto out_dec;
+ }
+ } else {
+ ret = pkt_open_dev(pd, mode & FMODE_WRITE);
+ if (ret)
+ goto out_dec;
+ /*
+ * needed here as well, since ext2 (among others) may change
+ * the blocksize at mount time
+ */
+ set_blocksize(bdev, CD_FRAMESIZE);
+ }
+
+ mutex_unlock(&ctl_mutex);
+ mutex_unlock(&pktcdvd_mutex);
+ return 0;
+
+out_dec:
+ pd->refcnt--;
+out:
+ mutex_unlock(&ctl_mutex);
+ mutex_unlock(&pktcdvd_mutex);
+ return ret;
+}
+
+static void pkt_close(struct gendisk *disk, fmode_t mode)
+{
+ struct pktcdvd_device *pd = disk->private_data;
+
+ mutex_lock(&pktcdvd_mutex);
+ mutex_lock(&ctl_mutex);
+ pd->refcnt--;
+ BUG_ON(pd->refcnt < 0);
+ if (pd->refcnt == 0) {
+ int flush = test_bit(PACKET_WRITABLE, &pd->flags);
+ pkt_release_dev(pd, flush);
+ }
+ mutex_unlock(&ctl_mutex);
+ mutex_unlock(&pktcdvd_mutex);
+}
+
+
+static void pkt_end_io_read_cloned(struct bio *bio)
+{
+ struct packet_stacked_data *psd = bio->bi_private;
+ struct pktcdvd_device *pd = psd->pd;
+
+ psd->bio->bi_status = bio->bi_status;
+ bio_put(bio);
+ bio_endio(psd->bio);
+ mempool_free(psd, &psd_pool);
+ pkt_bio_finished(pd);
+}
+
+static void pkt_make_request_read(struct pktcdvd_device *pd, struct bio *bio)
+{
+ struct bio *cloned_bio =
+ bio_alloc_clone(pd->bdev, bio, GFP_NOIO, &pkt_bio_set);
+ struct packet_stacked_data *psd = mempool_alloc(&psd_pool, GFP_NOIO);
+
+ psd->pd = pd;
+ psd->bio = bio;
+ cloned_bio->bi_private = psd;
+ cloned_bio->bi_end_io = pkt_end_io_read_cloned;
+ pd->stats.secs_r += bio_sectors(bio);
+ pkt_queue_bio(pd, cloned_bio);
+}
+
+static void pkt_make_request_write(struct request_queue *q, struct bio *bio)
+{
+ struct pktcdvd_device *pd = q->queuedata;
+ sector_t zone;
+ struct packet_data *pkt;
+ int was_empty, blocked_bio;
+ struct pkt_rb_node *node;
+
+ zone = get_zone(bio->bi_iter.bi_sector, pd);
+
+ /*
+ * If we find a matching packet in state WAITING or READ_WAIT, we can
+ * just append this bio to that packet.
+ */
+ spin_lock(&pd->cdrw.active_list_lock);
+ blocked_bio = 0;
+ list_for_each_entry(pkt, &pd->cdrw.pkt_active_list, list) {
+ if (pkt->sector == zone) {
+ spin_lock(&pkt->lock);
+ if ((pkt->state == PACKET_WAITING_STATE) ||
+ (pkt->state == PACKET_READ_WAIT_STATE)) {
+ bio_list_add(&pkt->orig_bios, bio);
+ pkt->write_size +=
+ bio->bi_iter.bi_size / CD_FRAMESIZE;
+ if ((pkt->write_size >= pkt->frames) &&
+ (pkt->state == PACKET_WAITING_STATE)) {
+ atomic_inc(&pkt->run_sm);
+ wake_up(&pd->wqueue);
+ }
+ spin_unlock(&pkt->lock);
+ spin_unlock(&pd->cdrw.active_list_lock);
+ return;
+ } else {
+ blocked_bio = 1;
+ }
+ spin_unlock(&pkt->lock);
+ }
+ }
+ spin_unlock(&pd->cdrw.active_list_lock);
+
+ /*
+ * Test if there is enough room left in the bio work queue
+ * (queue size >= congestion on mark).
+ * If not, wait till the work queue size is below the congestion off mark.
+ */
+ spin_lock(&pd->lock);
+ if (pd->write_congestion_on > 0
+ && pd->bio_queue_size >= pd->write_congestion_on) {
+ struct wait_bit_queue_entry wqe;
+
+ init_wait_var_entry(&wqe, &pd->congested, 0);
+ for (;;) {
+ prepare_to_wait_event(__var_waitqueue(&pd->congested),
+ &wqe.wq_entry,
+ TASK_UNINTERRUPTIBLE);
+ if (pd->bio_queue_size <= pd->write_congestion_off)
+ break;
+ pd->congested = true;
+ spin_unlock(&pd->lock);
+ schedule();
+ spin_lock(&pd->lock);
+ }
+ }
+ spin_unlock(&pd->lock);
+
+ /*
+ * No matching packet found. Store the bio in the work queue.
+ */
+ node = mempool_alloc(&pd->rb_pool, GFP_NOIO);
+ node->bio = bio;
+ spin_lock(&pd->lock);
+ BUG_ON(pd->bio_queue_size < 0);
+ was_empty = (pd->bio_queue_size == 0);
+ pkt_rbtree_insert(pd, node);
+ spin_unlock(&pd->lock);
+
+ /*
+ * Wake up the worker thread.
+ */
+ atomic_set(&pd->scan_queue, 1);
+ if (was_empty) {
+ /* This wake_up is required for correct operation */
+ wake_up(&pd->wqueue);
+ } else if (!list_empty(&pd->cdrw.pkt_free_list) && !blocked_bio) {
+ /*
+ * This wake up is not required for correct operation,
+ * but improves performance in some cases.
+ */
+ wake_up(&pd->wqueue);
+ }
+}
+
+static void pkt_submit_bio(struct bio *bio)
+{
+ struct pktcdvd_device *pd = bio->bi_bdev->bd_disk->queue->queuedata;
+ struct bio *split;
+
+ bio = bio_split_to_limits(bio);
+ if (!bio)
+ return;
+
+ pkt_dbg(2, pd, "start = %6llx stop = %6llx\n",
+ (unsigned long long)bio->bi_iter.bi_sector,
+ (unsigned long long)bio_end_sector(bio));
+
+ /*
+ * Clone READ bios so we can have our own bi_end_io callback.
+ */
+ if (bio_data_dir(bio) == READ) {
+ pkt_make_request_read(pd, bio);
+ return;
+ }
+
+ if (!test_bit(PACKET_WRITABLE, &pd->flags)) {
+ pkt_notice(pd, "WRITE for ro device (%llu)\n",
+ (unsigned long long)bio->bi_iter.bi_sector);
+ goto end_io;
+ }
+
+ if (!bio->bi_iter.bi_size || (bio->bi_iter.bi_size % CD_FRAMESIZE)) {
+ pkt_err(pd, "wrong bio size\n");
+ goto end_io;
+ }
+
+ do {
+ sector_t zone = get_zone(bio->bi_iter.bi_sector, pd);
+ sector_t last_zone = get_zone(bio_end_sector(bio) - 1, pd);
+
+ if (last_zone != zone) {
+ BUG_ON(last_zone != zone + pd->settings.size);
+
+ split = bio_split(bio, last_zone -
+ bio->bi_iter.bi_sector,
+ GFP_NOIO, &pkt_bio_set);
+ bio_chain(split, bio);
+ } else {
+ split = bio;
+ }
+
+ pkt_make_request_write(bio->bi_bdev->bd_disk->queue, split);
+ } while (split != bio);
+
+ return;
+end_io:
+ bio_io_error(bio);
+}
+
+static void pkt_init_queue(struct pktcdvd_device *pd)
+{
+ struct request_queue *q = pd->disk->queue;
+
+ blk_queue_logical_block_size(q, CD_FRAMESIZE);
+ blk_queue_max_hw_sectors(q, PACKET_MAX_SECTORS);
+ q->queuedata = pd;
+}
+
+static int pkt_seq_show(struct seq_file *m, void *p)
+{
+ struct pktcdvd_device *pd = m->private;
+ char *msg;
+ int states[PACKET_NUM_STATES];
+
+ seq_printf(m, "Writer %s mapped to %pg:\n", pd->name, pd->bdev);
+
+ seq_printf(m, "\nSettings:\n");
+ seq_printf(m, "\tpacket size:\t\t%dkB\n", pd->settings.size / 2);
+
+ if (pd->settings.write_type == 0)
+ msg = "Packet";
+ else
+ msg = "Unknown";
+ seq_printf(m, "\twrite type:\t\t%s\n", msg);
+
+ seq_printf(m, "\tpacket type:\t\t%s\n", pd->settings.fp ? "Fixed" : "Variable");
+ seq_printf(m, "\tlink loss:\t\t%d\n", pd->settings.link_loss);
+
+ seq_printf(m, "\ttrack mode:\t\t%d\n", pd->settings.track_mode);
+
+ if (pd->settings.block_mode == PACKET_BLOCK_MODE1)
+ msg = "Mode 1";
+ else if (pd->settings.block_mode == PACKET_BLOCK_MODE2)
+ msg = "Mode 2";
+ else
+ msg = "Unknown";
+ seq_printf(m, "\tblock mode:\t\t%s\n", msg);
+
+ seq_printf(m, "\nStatistics:\n");
+ seq_printf(m, "\tpackets started:\t%lu\n", pd->stats.pkt_started);
+ seq_printf(m, "\tpackets ended:\t\t%lu\n", pd->stats.pkt_ended);
+ seq_printf(m, "\twritten:\t\t%lukB\n", pd->stats.secs_w >> 1);
+ seq_printf(m, "\tread gather:\t\t%lukB\n", pd->stats.secs_rg >> 1);
+ seq_printf(m, "\tread:\t\t\t%lukB\n", pd->stats.secs_r >> 1);
+
+ seq_printf(m, "\nMisc:\n");
+ seq_printf(m, "\treference count:\t%d\n", pd->refcnt);
+ seq_printf(m, "\tflags:\t\t\t0x%lx\n", pd->flags);
+ seq_printf(m, "\tread speed:\t\t%ukB/s\n", pd->read_speed);
+ seq_printf(m, "\twrite speed:\t\t%ukB/s\n", pd->write_speed);
+ seq_printf(m, "\tstart offset:\t\t%lu\n", pd->offset);
+ seq_printf(m, "\tmode page offset:\t%u\n", pd->mode_offset);
+
+ seq_printf(m, "\nQueue state:\n");
+ seq_printf(m, "\tbios queued:\t\t%d\n", pd->bio_queue_size);
+ seq_printf(m, "\tbios pending:\t\t%d\n", atomic_read(&pd->cdrw.pending_bios));
+ seq_printf(m, "\tcurrent sector:\t\t0x%llx\n", (unsigned long long)pd->current_sector);
+
+ pkt_count_states(pd, states);
+ seq_printf(m, "\tstate:\t\t\ti:%d ow:%d rw:%d ww:%d rec:%d fin:%d\n",
+ states[0], states[1], states[2], states[3], states[4], states[5]);
+
+ seq_printf(m, "\twrite congestion marks:\toff=%d on=%d\n",
+ pd->write_congestion_off,
+ pd->write_congestion_on);
+ return 0;
+}
+
+static int pkt_new_dev(struct pktcdvd_device *pd, dev_t dev)
+{
+ int i;
+ struct block_device *bdev;
+ struct scsi_device *sdev;
+
+ if (pd->pkt_dev == dev) {
+ pkt_err(pd, "recursive setup not allowed\n");
+ return -EBUSY;
+ }
+ for (i = 0; i < MAX_WRITERS; i++) {
+ struct pktcdvd_device *pd2 = pkt_devs[i];
+ if (!pd2)
+ continue;
+ if (pd2->bdev->bd_dev == dev) {
+ pkt_err(pd, "%pg already setup\n", pd2->bdev);
+ return -EBUSY;
+ }
+ if (pd2->pkt_dev == dev) {
+ pkt_err(pd, "can't chain pktcdvd devices\n");
+ return -EBUSY;
+ }
+ }
+
+ bdev = blkdev_get_by_dev(dev, FMODE_READ | FMODE_NDELAY, NULL);
+ if (IS_ERR(bdev))
+ return PTR_ERR(bdev);
+ sdev = scsi_device_from_queue(bdev->bd_disk->queue);
+ if (!sdev) {
+ blkdev_put(bdev, FMODE_READ | FMODE_NDELAY);
+ return -EINVAL;
+ }
+ put_device(&sdev->sdev_gendev);
+
+ /* This is safe, since we have a reference from open(). */
+ __module_get(THIS_MODULE);
+
+ pd->bdev = bdev;
+ set_blocksize(bdev, CD_FRAMESIZE);
+
+ pkt_init_queue(pd);
+
+ atomic_set(&pd->cdrw.pending_bios, 0);
+ pd->cdrw.thread = kthread_run(kcdrwd, pd, "%s", pd->name);
+ if (IS_ERR(pd->cdrw.thread)) {
+ pkt_err(pd, "can't start kernel thread\n");
+ goto out_mem;
+ }
+
+ proc_create_single_data(pd->name, 0, pkt_proc, pkt_seq_show, pd);
+ pkt_dbg(1, pd, "writer mapped to %pg\n", bdev);
+ return 0;
+
+out_mem:
+ blkdev_put(bdev, FMODE_READ | FMODE_NDELAY);
+ /* This is safe: open() is still holding a reference. */
+ module_put(THIS_MODULE);
+ return -ENOMEM;
+}
+
+static int pkt_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg)
+{
+ struct pktcdvd_device *pd = bdev->bd_disk->private_data;
+ int ret;
+
+ pkt_dbg(2, pd, "cmd %x, dev %d:%d\n",
+ cmd, MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev));
+
+ mutex_lock(&pktcdvd_mutex);
+ switch (cmd) {
+ case CDROMEJECT:
+ /*
+ * The door gets locked when the device is opened, so we
+ * have to unlock it or else the eject command fails.
+ */
+ if (pd->refcnt == 1)
+ pkt_lock_door(pd, 0);
+ fallthrough;
+ /*
+ * forward selected CDROM ioctls to CD-ROM, for UDF
+ */
+ case CDROMMULTISESSION:
+ case CDROMREADTOCENTRY:
+ case CDROM_LAST_WRITTEN:
+ case CDROM_SEND_PACKET:
+ case SCSI_IOCTL_SEND_COMMAND:
+ if (!bdev->bd_disk->fops->ioctl)
+ ret = -ENOTTY;
+ else
+ ret = bdev->bd_disk->fops->ioctl(bdev, mode, cmd, arg);
+ break;
+ default:
+ pkt_dbg(2, pd, "Unknown ioctl (%x)\n", cmd);
+ ret = -ENOTTY;
+ }
+ mutex_unlock(&pktcdvd_mutex);
+
+ return ret;
+}
+
+static unsigned int pkt_check_events(struct gendisk *disk,
+ unsigned int clearing)
+{
+ struct pktcdvd_device *pd = disk->private_data;
+ struct gendisk *attached_disk;
+
+ if (!pd)
+ return 0;
+ if (!pd->bdev)
+ return 0;
+ attached_disk = pd->bdev->bd_disk;
+ if (!attached_disk || !attached_disk->fops->check_events)
+ return 0;
+ return attached_disk->fops->check_events(attached_disk, clearing);
+}
+
+static char *pkt_devnode(struct gendisk *disk, umode_t *mode)
+{
+ return kasprintf(GFP_KERNEL, "pktcdvd/%s", disk->disk_name);
+}
+
+static const struct block_device_operations pktcdvd_ops = {
+ .owner = THIS_MODULE,
+ .submit_bio = pkt_submit_bio,
+ .open = pkt_open,
+ .release = pkt_close,
+ .ioctl = pkt_ioctl,
+ .compat_ioctl = blkdev_compat_ptr_ioctl,
+ .check_events = pkt_check_events,
+ .devnode = pkt_devnode,
+};
+
+/*
+ * Set up mapping from pktcdvd device to CD-ROM device.
+ */
+static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev)
+{
+ int idx;
+ int ret = -ENOMEM;
+ struct pktcdvd_device *pd;
+ struct gendisk *disk;
+
+ mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
+
+ for (idx = 0; idx < MAX_WRITERS; idx++)
+ if (!pkt_devs[idx])
+ break;
+ if (idx == MAX_WRITERS) {
+ pr_err("max %d writers supported\n", MAX_WRITERS);
+ ret = -EBUSY;
+ goto out_mutex;
+ }
+
+ pd = kzalloc(sizeof(struct pktcdvd_device), GFP_KERNEL);
+ if (!pd)
+ goto out_mutex;
+
+ ret = mempool_init_kmalloc_pool(&pd->rb_pool, PKT_RB_POOL_SIZE,
+ sizeof(struct pkt_rb_node));
+ if (ret)
+ goto out_mem;
+
+ INIT_LIST_HEAD(&pd->cdrw.pkt_free_list);
+ INIT_LIST_HEAD(&pd->cdrw.pkt_active_list);
+ spin_lock_init(&pd->cdrw.active_list_lock);
+
+ spin_lock_init(&pd->lock);
+ spin_lock_init(&pd->iosched.lock);
+ bio_list_init(&pd->iosched.read_queue);
+ bio_list_init(&pd->iosched.write_queue);
+ sprintf(pd->name, DRIVER_NAME"%d", idx);
+ init_waitqueue_head(&pd->wqueue);
+ pd->bio_queue = RB_ROOT;
+
+ pd->write_congestion_on = write_congestion_on;
+ pd->write_congestion_off = write_congestion_off;
+
+ ret = -ENOMEM;
+ disk = blk_alloc_disk(NUMA_NO_NODE);
+ if (!disk)
+ goto out_mem;
+ pd->disk = disk;
+ disk->major = pktdev_major;
+ disk->first_minor = idx;
+ disk->minors = 1;
+ disk->fops = &pktcdvd_ops;
+ disk->flags = GENHD_FL_REMOVABLE | GENHD_FL_NO_PART;
+ strcpy(disk->disk_name, pd->name);
+ disk->private_data = pd;
+
+ pd->pkt_dev = MKDEV(pktdev_major, idx);
+ ret = pkt_new_dev(pd, dev);
+ if (ret)
+ goto out_mem2;
+
+ /* inherit events of the host device */
+ disk->events = pd->bdev->bd_disk->events;
+
+ ret = add_disk(disk);
+ if (ret)
+ goto out_mem2;
+
+ pkt_sysfs_dev_new(pd);
+ pkt_debugfs_dev_new(pd);
+
+ pkt_devs[idx] = pd;
+ if (pkt_dev)
+ *pkt_dev = pd->pkt_dev;
+
+ mutex_unlock(&ctl_mutex);
+ return 0;
+
+out_mem2:
+ put_disk(disk);
+out_mem:
+ mempool_exit(&pd->rb_pool);
+ kfree(pd);
+out_mutex:
+ mutex_unlock(&ctl_mutex);
+ pr_err("setup of pktcdvd device failed\n");
+ return ret;
+}
+
+/*
+ * Tear down mapping from pktcdvd device to CD-ROM device.
+ */
+static int pkt_remove_dev(dev_t pkt_dev)
+{
+ struct pktcdvd_device *pd;
+ int idx;
+ int ret = 0;
+
+ mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
+
+ for (idx = 0; idx < MAX_WRITERS; idx++) {
+ pd = pkt_devs[idx];
+ if (pd && (pd->pkt_dev == pkt_dev))
+ break;
+ }
+ if (idx == MAX_WRITERS) {
+ pr_debug("dev not setup\n");
+ ret = -ENXIO;
+ goto out;
+ }
+
+ if (pd->refcnt > 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+ if (!IS_ERR(pd->cdrw.thread))
+ kthread_stop(pd->cdrw.thread);
+
+ pkt_devs[idx] = NULL;
+
+ pkt_debugfs_dev_remove(pd);
+ pkt_sysfs_dev_remove(pd);
+
+ blkdev_put(pd->bdev, FMODE_READ | FMODE_NDELAY);
+
+ remove_proc_entry(pd->name, pkt_proc);
+ pkt_dbg(1, pd, "writer unmapped\n");
+
+ del_gendisk(pd->disk);
+ put_disk(pd->disk);
+
+ mempool_exit(&pd->rb_pool);
+ kfree(pd);
+
+ /* This is safe: open() is still holding a reference. */
+ module_put(THIS_MODULE);
+
+out:
+ mutex_unlock(&ctl_mutex);
+ return ret;
+}
+
+static void pkt_get_status(struct pkt_ctrl_command *ctrl_cmd)
+{
+ struct pktcdvd_device *pd;
+
+ mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
+
+ pd = pkt_find_dev_from_minor(ctrl_cmd->dev_index);
+ if (pd) {
+ ctrl_cmd->dev = new_encode_dev(pd->bdev->bd_dev);
+ ctrl_cmd->pkt_dev = new_encode_dev(pd->pkt_dev);
+ } else {
+ ctrl_cmd->dev = 0;
+ ctrl_cmd->pkt_dev = 0;
+ }
+ ctrl_cmd->num_devices = MAX_WRITERS;
+
+ mutex_unlock(&ctl_mutex);
+}
+
+static long pkt_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct pkt_ctrl_command ctrl_cmd;
+ int ret = 0;
+ dev_t pkt_dev = 0;
+
+ if (cmd != PACKET_CTRL_CMD)
+ return -ENOTTY;
+
+ if (copy_from_user(&ctrl_cmd, argp, sizeof(struct pkt_ctrl_command)))
+ return -EFAULT;
+
+ switch (ctrl_cmd.command) {
+ case PKT_CTRL_CMD_SETUP:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ ret = pkt_setup_dev(new_decode_dev(ctrl_cmd.dev), &pkt_dev);
+ ctrl_cmd.pkt_dev = new_encode_dev(pkt_dev);
+ break;
+ case PKT_CTRL_CMD_TEARDOWN:
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ ret = pkt_remove_dev(new_decode_dev(ctrl_cmd.pkt_dev));
+ break;
+ case PKT_CTRL_CMD_STATUS:
+ pkt_get_status(&ctrl_cmd);
+ break;
+ default:
+ return -ENOTTY;
+ }
+
+ if (copy_to_user(argp, &ctrl_cmd, sizeof(struct pkt_ctrl_command)))
+ return -EFAULT;
+ return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long pkt_ctl_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return pkt_ctl_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static const struct file_operations pkt_ctl_fops = {
+ .open = nonseekable_open,
+ .unlocked_ioctl = pkt_ctl_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = pkt_ctl_compat_ioctl,
+#endif
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice pkt_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = DRIVER_NAME,
+ .nodename = "pktcdvd/control",
+ .fops = &pkt_ctl_fops
+};
+
+static int __init pkt_init(void)
+{
+ int ret;
+
+ mutex_init(&ctl_mutex);
+
+ ret = mempool_init_kmalloc_pool(&psd_pool, PSD_POOL_SIZE,
+ sizeof(struct packet_stacked_data));
+ if (ret)
+ return ret;
+ ret = bioset_init(&pkt_bio_set, BIO_POOL_SIZE, 0, 0);
+ if (ret) {
+ mempool_exit(&psd_pool);
+ return ret;
+ }
+
+ ret = register_blkdev(pktdev_major, DRIVER_NAME);
+ if (ret < 0) {
+ pr_err("unable to register block device\n");
+ goto out2;
+ }
+ if (!pktdev_major)
+ pktdev_major = ret;
+
+ ret = pkt_sysfs_init();
+ if (ret)
+ goto out;
+
+ pkt_debugfs_init();
+
+ ret = misc_register(&pkt_misc);
+ if (ret) {
+ pr_err("unable to register misc device\n");
+ goto out_misc;
+ }
+
+ pkt_proc = proc_mkdir("driver/"DRIVER_NAME, NULL);
+
+ return 0;
+
+out_misc:
+ pkt_debugfs_cleanup();
+ pkt_sysfs_cleanup();
+out:
+ unregister_blkdev(pktdev_major, DRIVER_NAME);
+out2:
+ mempool_exit(&psd_pool);
+ bioset_exit(&pkt_bio_set);
+ return ret;
+}
+
+static void __exit pkt_exit(void)
+{
+ remove_proc_entry("driver/"DRIVER_NAME, NULL);
+ misc_deregister(&pkt_misc);
+
+ pkt_debugfs_cleanup();
+ pkt_sysfs_cleanup();
+
+ unregister_blkdev(pktdev_major, DRIVER_NAME);
+ mempool_exit(&psd_pool);
+ bioset_exit(&pkt_bio_set);
+}
+
+MODULE_DESCRIPTION("Packet writing layer for CD/DVD drives");
+MODULE_AUTHOR("Jens Axboe <axboe@suse.de>");
+MODULE_LICENSE("GPL");
+
+module_init(pkt_init);
+module_exit(pkt_exit);
diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c
index c76e0148eada33..574e470b220b09 100644
--- a/drivers/block/ps3vram.c
+++ b/drivers/block/ps3vram.c
@@ -587,6 +587,8 @@ static void ps3vram_submit_bio(struct bio *bio)
dev_dbg(&dev->core, "%s\n", __func__);
bio = bio_split_to_limits(bio);
+ if (!bio)
+ return;
spin_lock_irq(&priv->lock);
busy = !bio_list_empty(&priv->list);
diff --git a/drivers/block/rnbd/rnbd-clt.c b/drivers/block/rnbd/rnbd-clt.c
index 78334da74d8bf9..5eb8c7855970d1 100644
--- a/drivers/block/rnbd/rnbd-clt.c
+++ b/drivers/block/rnbd/rnbd-clt.c
@@ -1440,7 +1440,7 @@ static struct rnbd_clt_dev *init_dev(struct rnbd_clt_session *sess,
goto out_alloc;
}
- ret = ida_alloc_max(&index_ida, 1 << (MINORBITS - RNBD_PART_BITS),
+ ret = ida_alloc_max(&index_ida, (1 << (MINORBITS - RNBD_PART_BITS)) - 1,
GFP_KERNEL);
if (ret < 0) {
pr_err("Failed to initialize device '%s' from session %s, allocating idr failed, err: %d\n",
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index e9de9d846b7303..6368b56eacf11a 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -137,7 +137,7 @@ struct ublk_device {
char *__queues;
- unsigned short queue_size;
+ unsigned int queue_size;
struct ublksrv_ctrl_dev_info dev_info;
struct blk_mq_tag_set tag_set;
@@ -1992,6 +1992,9 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
int ret = -EINVAL;
+ if (issue_flags & IO_URING_F_NONBLOCK)
+ return -EAGAIN;
+
ublk_ctrl_cmd_dump(cmd);
if (!(issue_flags & IO_URING_F_SQE128))
@@ -2089,13 +2092,12 @@ static void __exit ublk_exit(void)
struct ublk_device *ub;
int id;
- class_destroy(ublk_chr_class);
-
- misc_deregister(&ublk_misc);
-
idr_for_each_entry(&ublk_index_idr, ub, id)
ublk_remove(ub);
+ class_destroy(ublk_chr_class);
+ misc_deregister(&ublk_misc);
+
idr_destroy(&ublk_index_idr);
unregister_chrdev_region(ublk_chr_devt, UBLK_MINORS);
}
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 68bd2f7961b3f5..6a77fa91742880 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -315,22 +315,35 @@ static void virtio_commit_rqs(struct blk_mq_hw_ctx *hctx)
virtqueue_notify(vq->vq);
}
+static blk_status_t virtblk_fail_to_queue(struct request *req, int rc)
+{
+ virtblk_cleanup_cmd(req);
+ switch (rc) {
+ case -ENOSPC:
+ return BLK_STS_DEV_RESOURCE;
+ case -ENOMEM:
+ return BLK_STS_RESOURCE;
+ default:
+ return BLK_STS_IOERR;
+ }
+}
+
static blk_status_t virtblk_prep_rq(struct blk_mq_hw_ctx *hctx,
struct virtio_blk *vblk,
struct request *req,
struct virtblk_req *vbr)
{
blk_status_t status;
+ int num;
status = virtblk_setup_cmd(vblk->vdev, req, vbr);
if (unlikely(status))
return status;
- vbr->sg_table.nents = virtblk_map_data(hctx, req, vbr);
- if (unlikely(vbr->sg_table.nents < 0)) {
- virtblk_cleanup_cmd(req);
- return BLK_STS_RESOURCE;
- }
+ num = virtblk_map_data(hctx, req, vbr);
+ if (unlikely(num < 0))
+ return virtblk_fail_to_queue(req, -ENOMEM);
+ vbr->sg_table.nents = num;
blk_mq_start_request(req);
@@ -364,15 +377,7 @@ static blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx *hctx,
blk_mq_stop_hw_queue(hctx);
spin_unlock_irqrestore(&vblk->vqs[qid].lock, flags);
virtblk_unmap_data(req, vbr);
- virtblk_cleanup_cmd(req);
- switch (err) {
- case -ENOSPC:
- return BLK_STS_DEV_RESOURCE;
- case -ENOMEM:
- return BLK_STS_RESOURCE;
- default:
- return BLK_STS_IOERR;
- }
+ return virtblk_fail_to_queue(req, err);
}
if (bd->last && virtqueue_kick_prepare(vblk->vqs[qid].vq))
@@ -991,7 +996,7 @@ static int virtblk_probe(struct virtio_device *vdev)
blk_queue_max_segments(q, sg_elems);
/* No real sector limit. */
- blk_queue_max_hw_sectors(q, -1U);
+ blk_queue_max_hw_sectors(q, UINT_MAX);
max_size = virtio_max_dma_size(vdev);
diff --git a/drivers/block/xen-blkback/xenbus.c b/drivers/block/xen-blkback/xenbus.c
index c0227dfa468879..4807af1d580593 100644
--- a/drivers/block/xen-blkback/xenbus.c
+++ b/drivers/block/xen-blkback/xenbus.c
@@ -524,7 +524,7 @@ static int xen_vbd_create(struct xen_blkif *blkif, blkif_vdev_t handle,
return 0;
}
-static int xen_blkbk_remove(struct xenbus_device *dev)
+static void xen_blkbk_remove(struct xenbus_device *dev)
{
struct backend_info *be = dev_get_drvdata(&dev->dev);
@@ -547,8 +547,6 @@ static int xen_blkbk_remove(struct xenbus_device *dev)
/* Put the reference we set in xen_blkif_alloc(). */
xen_blkif_put(be->blkif);
}
-
- return 0;
}
int xen_blkbk_flush_diskcache(struct xenbus_transaction xbt,
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index b28489290323fa..23ed258b57f0e5 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -2467,7 +2467,7 @@ static void blkback_changed(struct xenbus_device *dev,
}
}
-static int blkfront_remove(struct xenbus_device *xbdev)
+static void blkfront_remove(struct xenbus_device *xbdev)
{
struct blkfront_info *info = dev_get_drvdata(&xbdev->dev);
@@ -2488,7 +2488,6 @@ static int blkfront_remove(struct xenbus_device *xbdev)
}
kfree(info);
- return 0;
}
static int blkfront_is_ready(struct xenbus_device *dev)
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 6eddc23e49d9a7..bbe9cf1cae27f6 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -2164,10 +2164,17 @@ static void qca_serdev_shutdown(struct device *dev)
int timeout = msecs_to_jiffies(CMD_TRANS_TIMEOUT_MS);
struct serdev_device *serdev = to_serdev_device(dev);
struct qca_serdev *qcadev = serdev_device_get_drvdata(serdev);
+ struct hci_uart *hu = &qcadev->serdev_hu;
+ struct hci_dev *hdev = hu->hdev;
+ struct qca_data *qca = hu->priv;
const u8 ibs_wake_cmd[] = { 0xFD };
const u8 edl_reset_soc_cmd[] = { 0x01, 0x00, 0xFC, 0x01, 0x05 };
if (qcadev->btsoc_type == QCA_QCA6390) {
+ if (test_bit(QCA_BT_OFF, &qca->flags) ||
+ !test_bit(HCI_RUNNING, &hdev->flags))
+ return;
+
serdev_device_write_flush(serdev);
ret = serdev_device_write_buf(serdev, ibs_wake_cmd,
sizeof(ibs_wake_cmd));
diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c
index 3aa91aed3bf733..226e87b85116ea 100644
--- a/drivers/bus/sunxi-rsb.c
+++ b/drivers/bus/sunxi-rsb.c
@@ -857,7 +857,13 @@ static int __init sunxi_rsb_init(void)
return ret;
}
- return platform_driver_register(&sunxi_rsb_driver);
+ ret = platform_driver_register(&sunxi_rsb_driver);
+ if (ret) {
+ bus_unregister(&sunxi_rsb_bus);
+ return ret;
+ }
+
+ return 0;
}
module_init(sunxi_rsb_init);
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index d69905233aff2d..7e513b7718320c 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -412,7 +412,9 @@ int tpm_pm_suspend(struct device *dev)
}
suspended:
- return rc;
+ if (rc)
+ dev_err(dev, "Ignoring error %d while suspending\n", rc);
+ return 0;
}
EXPORT_SYMBOL_GPL(tpm_pm_suspend);
diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c
index 37929182626179..80cca3b83b226a 100644
--- a/drivers/char/tpm/xen-tpmfront.c
+++ b/drivers/char/tpm/xen-tpmfront.c
@@ -360,14 +360,13 @@ static int tpmfront_probe(struct xenbus_device *dev,
return tpm_chip_register(priv->chip);
}
-static int tpmfront_remove(struct xenbus_device *dev)
+static void tpmfront_remove(struct xenbus_device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(&dev->dev);
struct tpm_private *priv = dev_get_drvdata(&chip->dev);
tpm_chip_unregister(chip);
ring_free(priv);
dev_set_drvdata(&chip->dev, NULL);
- return 0;
}
static int tpmfront_resume(struct xenbus_device *dev)
diff --git a/drivers/clk/ingenic/jz4760-cgu.c b/drivers/clk/ingenic/jz4760-cgu.c
index ecd395ac8a28d4..e407f00bd59422 100644
--- a/drivers/clk/ingenic/jz4760-cgu.c
+++ b/drivers/clk/ingenic/jz4760-cgu.c
@@ -58,7 +58,7 @@ jz4760_cgu_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info,
unsigned long rate, unsigned long parent_rate,
unsigned int *pm, unsigned int *pn, unsigned int *pod)
{
- unsigned int m, n, od, m_max = (1 << pll_info->m_bits) - 2;
+ unsigned int m, n, od, m_max = (1 << pll_info->m_bits) - 1;
/* The frequency after the N divider must be between 1 and 50 MHz. */
n = parent_rate / (1 * MHZ);
@@ -66,19 +66,17 @@ jz4760_cgu_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info,
/* The N divider must be >= 2. */
n = clamp_val(n, 2, 1 << pll_info->n_bits);
- for (;; n >>= 1) {
- od = (unsigned int)-1;
+ rate /= MHZ;
+ parent_rate /= MHZ;
- do {
- m = (rate / MHZ) * (1 << ++od) * n / (parent_rate / MHZ);
- } while ((m > m_max || m & 1) && (od < 4));
-
- if (od < 4 && m >= 4 && m <= m_max)
- break;
+ for (m = m_max; m >= m_max && n >= 2; n--) {
+ m = rate * n / parent_rate;
+ od = m & 1;
+ m <<= od;
}
*pm = m;
- *pn = n;
+ *pn = n + 1;
*pod = 1 << od;
}
diff --git a/drivers/clk/microchip/clk-mpfs-ccc.c b/drivers/clk/microchip/clk-mpfs-ccc.c
index 32aae880a14f3b..0ddc73e07be429 100644
--- a/drivers/clk/microchip/clk-mpfs-ccc.c
+++ b/drivers/clk/microchip/clk-mpfs-ccc.c
@@ -164,12 +164,11 @@ static int mpfs_ccc_register_outputs(struct device *dev, struct mpfs_ccc_out_hw_
for (unsigned int i = 0; i < num_clks; i++) {
struct mpfs_ccc_out_hw_clock *out_hw = &out_hws[i];
- char *name = devm_kzalloc(dev, 23, GFP_KERNEL);
+ char *name = devm_kasprintf(dev, GFP_KERNEL, "%s_out%u", parent->name, i);
if (!name)
return -ENOMEM;
- snprintf(name, 23, "%s_out%u", parent->name, i);
out_hw->divider.hw.init = CLK_HW_INIT_HW(name, &parent->hw, &clk_divider_ops, 0);
out_hw->divider.reg = data->pll_base[i / MPFS_CCC_OUTPUTS_PER_PLL] +
out_hw->reg_offset;
@@ -201,14 +200,13 @@ static int mpfs_ccc_register_plls(struct device *dev, struct mpfs_ccc_pll_hw_clo
for (unsigned int i = 0; i < num_clks; i++) {
struct mpfs_ccc_pll_hw_clock *pll_hw = &pll_hws[i];
- char *name = devm_kzalloc(dev, 18, GFP_KERNEL);
- if (!name)
+ pll_hw->name = devm_kasprintf(dev, GFP_KERNEL, "ccc%s_pll%u",
+ strchrnul(dev->of_node->full_name, '@'), i);
+ if (!pll_hw->name)
return -ENOMEM;
pll_hw->base = data->pll_base[i];
- snprintf(name, 18, "ccc%s_pll%u", strchrnul(dev->of_node->full_name, '@'), i);
- pll_hw->name = (const char *)name;
pll_hw->hw.init = CLK_HW_INIT_PARENTS_DATA_FIXED_SIZE(pll_hw->name,
pll_hw->parents,
&mpfs_ccc_pll_ops, 0);
diff --git a/drivers/comedi/drivers/adv_pci1760.c b/drivers/comedi/drivers/adv_pci1760.c
index fcfc2e299110ed..27f3890f471df7 100644
--- a/drivers/comedi/drivers/adv_pci1760.c
+++ b/drivers/comedi/drivers/adv_pci1760.c
@@ -58,7 +58,7 @@
#define PCI1760_CMD_CLR_IMB2 0x00 /* Clears IMB2 */
#define PCI1760_CMD_SET_DO 0x01 /* Set output state */
#define PCI1760_CMD_GET_DO 0x02 /* Read output status */
-#define PCI1760_CMD_GET_STATUS 0x03 /* Read current status */
+#define PCI1760_CMD_GET_STATUS 0x07 /* Read current status */
#define PCI1760_CMD_GET_FW_VER 0x0e /* Read firmware version */
#define PCI1760_CMD_GET_HW_VER 0x0f /* Read hardware version */
#define PCI1760_CMD_SET_PWM_HI(x) (0x10 + (x) * 2) /* Set "hi" period */
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c
index 204e39006dda8f..c17bd845f5fcb8 100644
--- a/drivers/cpufreq/amd-pstate.c
+++ b/drivers/cpufreq/amd-pstate.c
@@ -307,6 +307,7 @@ static void amd_pstate_adjust_perf(unsigned int cpu,
max_perf = min_perf;
amd_pstate_update(cpudata, min_perf, des_perf, max_perf, true);
+ cpufreq_cpu_put(policy);
}
static int amd_get_min_freq(struct amd_cpudata *cpudata)
diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c
index d1801281cdd992..c11d22fd84c373 100644
--- a/drivers/cpufreq/apple-soc-cpufreq.c
+++ b/drivers/cpufreq/apple-soc-cpufreq.c
@@ -280,6 +280,7 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy)
policy->cpuinfo.transition_latency = transition_latency;
policy->dvfs_possible_from_any_cpu = true;
policy->fast_switch_possible = true;
+ policy->suspend_freq = freq_table[0].frequency;
if (policy_has_boost_freq(policy)) {
ret = cpufreq_enable_boost_support();
@@ -321,7 +322,6 @@ static struct cpufreq_driver apple_soc_cpufreq_driver = {
.flags = CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
CPUFREQ_NEED_INITIAL_FREQ_CHECK | CPUFREQ_IS_COOLING_DEV,
.verify = cpufreq_generic_frequency_table_verify,
- .attr = cpufreq_generic_attr,
.get = apple_soc_cpufreq_get_rate,
.init = apple_soc_cpufreq_init,
.exit = apple_soc_cpufreq_exit,
@@ -329,6 +329,7 @@ static struct cpufreq_driver apple_soc_cpufreq_driver = {
.fast_switch = apple_soc_cpufreq_fast_switch,
.register_em = cpufreq_register_em_with_opp,
.attr = apple_soc_cpufreq_hw_attr,
+ .suspend = cpufreq_generic_suspend,
};
static int __init apple_soc_cpufreq_module_init(void)
diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c
index c10fc33b29b181..b74289a95a1717 100644
--- a/drivers/cpufreq/armada-37xx-cpufreq.c
+++ b/drivers/cpufreq/armada-37xx-cpufreq.c
@@ -445,7 +445,7 @@ static int __init armada37xx_cpufreq_driver_init(void)
return -ENODEV;
}
- clk = clk_get(cpu_dev, 0);
+ clk = clk_get(cpu_dev, NULL);
if (IS_ERR(clk)) {
dev_err(cpu_dev, "Cannot get clock for CPU0\n");
return PTR_ERR(clk);
diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c
index 432dfb4e8027e9..022e3555407c86 100644
--- a/drivers/cpufreq/cppc_cpufreq.c
+++ b/drivers/cpufreq/cppc_cpufreq.c
@@ -487,7 +487,8 @@ static unsigned int get_perf_level_count(struct cpufreq_policy *policy)
cpu_data = policy->driver_data;
perf_caps = &cpu_data->perf_caps;
max_cap = arch_scale_cpu_capacity(cpu);
- min_cap = div_u64(max_cap * perf_caps->lowest_perf, perf_caps->highest_perf);
+ min_cap = div_u64((u64)max_cap * perf_caps->lowest_perf,
+ perf_caps->highest_perf);
if ((min_cap == 0) || (max_cap < min_cap))
return 0;
return 1 + max_cap / CPPC_EM_CAP_STEP - min_cap / CPPC_EM_CAP_STEP;
@@ -519,10 +520,10 @@ static int cppc_get_cpu_power(struct device *cpu_dev,
cpu_data = policy->driver_data;
perf_caps = &cpu_data->perf_caps;
max_cap = arch_scale_cpu_capacity(cpu_dev->id);
- min_cap = div_u64(max_cap * perf_caps->lowest_perf,
- perf_caps->highest_perf);
-
- perf_step = CPPC_EM_CAP_STEP * perf_caps->highest_perf / max_cap;
+ min_cap = div_u64((u64)max_cap * perf_caps->lowest_perf,
+ perf_caps->highest_perf);
+ perf_step = div_u64((u64)CPPC_EM_CAP_STEP * perf_caps->highest_perf,
+ max_cap);
min_step = min_cap / CPPC_EM_CAP_STEP;
max_step = max_cap / CPPC_EM_CAP_STEP;
diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
index 8ab67288304369..e8570365109869 100644
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
@@ -137,6 +137,7 @@ static const struct of_device_id blocklist[] __initconst = {
{ .compatible = "nvidia,tegra30", },
{ .compatible = "nvidia,tegra124", },
{ .compatible = "nvidia,tegra210", },
+ { .compatible = "nvidia,tegra234", },
{ .compatible = "qcom,apq8096", },
{ .compatible = "qcom,msm8996", },
@@ -150,6 +151,7 @@ static const struct of_device_id blocklist[] __initconst = {
{ .compatible = "qcom,sdm845", },
{ .compatible = "qcom,sm6115", },
{ .compatible = "qcom,sm6350", },
+ { .compatible = "qcom,sm6375", },
{ .compatible = "qcom,sm8150", },
{ .compatible = "qcom,sm8250", },
{ .compatible = "qcom,sm8350", },
diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c
index 340fed35e45dde..957cf6bb8c0523 100644
--- a/drivers/cpufreq/qcom-cpufreq-hw.c
+++ b/drivers/cpufreq/qcom-cpufreq-hw.c
@@ -143,40 +143,42 @@ static unsigned long qcom_lmh_get_throttle_freq(struct qcom_cpufreq_data *data)
return lval * xo_rate;
}
-/* Get the current frequency of the CPU (after throttling) */
-static unsigned int qcom_cpufreq_hw_get(unsigned int cpu)
+/* Get the frequency requested by the cpufreq core for the CPU */
+static unsigned int qcom_cpufreq_get_freq(unsigned int cpu)
{
struct qcom_cpufreq_data *data;
+ const struct qcom_cpufreq_soc_data *soc_data;
struct cpufreq_policy *policy;
+ unsigned int index;
policy = cpufreq_cpu_get_raw(cpu);
if (!policy)
return 0;
data = policy->driver_data;
+ soc_data = qcom_cpufreq.soc_data;
+
+ index = readl_relaxed(data->base + soc_data->reg_perf_state);
+ index = min(index, LUT_MAX_ENTRIES - 1);
- return qcom_lmh_get_throttle_freq(data) / HZ_PER_KHZ;
+ return policy->freq_table[index].frequency;
}
-/* Get the frequency requested by the cpufreq core for the CPU */
-static unsigned int qcom_cpufreq_get_freq(unsigned int cpu)
+static unsigned int qcom_cpufreq_hw_get(unsigned int cpu)
{
struct qcom_cpufreq_data *data;
- const struct qcom_cpufreq_soc_data *soc_data;
struct cpufreq_policy *policy;
- unsigned int index;
policy = cpufreq_cpu_get_raw(cpu);
if (!policy)
return 0;
data = policy->driver_data;
- soc_data = qcom_cpufreq.soc_data;
- index = readl_relaxed(data->base + soc_data->reg_perf_state);
- index = min(index, LUT_MAX_ENTRIES - 1);
+ if (data->throttle_irq >= 0)
+ return qcom_lmh_get_throttle_freq(data) / HZ_PER_KHZ;
- return policy->freq_table[index].frequency;
+ return qcom_cpufreq_get_freq(cpu);
}
static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy,
@@ -649,9 +651,10 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
{
struct clk_hw_onecell_data *clk_data;
struct device *dev = &pdev->dev;
+ struct device_node *soc_node;
struct device *cpu_dev;
struct clk *clk;
- int ret, i, num_domains;
+ int ret, i, num_domains, reg_sz;
clk = clk_get(dev, "xo");
if (IS_ERR(clk))
@@ -679,7 +682,21 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
return ret;
/* Allocate qcom_cpufreq_data based on the available frequency domains in DT */
- num_domains = of_property_count_elems_of_size(dev->of_node, "reg", sizeof(u32) * 4);
+ soc_node = of_get_parent(dev->of_node);
+ if (!soc_node)
+ return -EINVAL;
+
+ ret = of_property_read_u32(soc_node, "#address-cells", &reg_sz);
+ if (ret)
+ goto of_exit;
+
+ ret = of_property_read_u32(soc_node, "#size-cells", &i);
+ if (ret)
+ goto of_exit;
+
+ reg_sz += i;
+
+ num_domains = of_property_count_elems_of_size(dev->of_node, "reg", sizeof(u32) * reg_sz);
if (num_domains <= 0)
return num_domains;
@@ -743,6 +760,9 @@ static int qcom_cpufreq_hw_driver_probe(struct platform_device *pdev)
else
dev_dbg(dev, "QCOM CPUFreq HW driver initialized\n");
+of_exit:
+ of_node_put(soc_node);
+
return ret;
}
diff --git a/drivers/crypto/atmel-ecc.c b/drivers/crypto/atmel-ecc.c
index 53100fb9b07bdb..12205e2b53b457 100644
--- a/drivers/crypto/atmel-ecc.c
+++ b/drivers/crypto/atmel-ecc.c
@@ -3,7 +3,7 @@
* Microchip / Atmel ECC (I2C) driver.
*
* Copyright (c) 2017, Microchip Technology Inc.
- * Author: Tudor Ambarus <tudor.ambarus@microchip.com>
+ * Author: Tudor Ambarus
*/
#include <linux/delay.h>
@@ -411,6 +411,6 @@ static void __exit atmel_ecc_exit(void)
module_init(atmel_ecc_init);
module_exit(atmel_ecc_exit);
-MODULE_AUTHOR("Tudor Ambarus <tudor.ambarus@microchip.com>");
+MODULE_AUTHOR("Tudor Ambarus");
MODULE_DESCRIPTION("Microchip / Atmel ECC (I2C) driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/crypto/atmel-i2c.c b/drivers/crypto/atmel-i2c.c
index 81ce09bedda8f4..55bff1e1314260 100644
--- a/drivers/crypto/atmel-i2c.c
+++ b/drivers/crypto/atmel-i2c.c
@@ -3,7 +3,7 @@
* Microchip / Atmel ECC (I2C) driver.
*
* Copyright (c) 2017, Microchip Technology Inc.
- * Author: Tudor Ambarus <tudor.ambarus@microchip.com>
+ * Author: Tudor Ambarus
*/
#include <linux/bitrev.h>
@@ -390,6 +390,6 @@ static void __exit atmel_i2c_exit(void)
module_init(atmel_i2c_init);
module_exit(atmel_i2c_exit);
-MODULE_AUTHOR("Tudor Ambarus <tudor.ambarus@microchip.com>");
+MODULE_AUTHOR("Tudor Ambarus");
MODULE_DESCRIPTION("Microchip / Atmel ECC (I2C) driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/crypto/atmel-i2c.h b/drivers/crypto/atmel-i2c.h
index 48929efe2a5bf2..35f7857a7f7cc1 100644
--- a/drivers/crypto/atmel-i2c.h
+++ b/drivers/crypto/atmel-i2c.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2017, Microchip Technology Inc.
- * Author: Tudor Ambarus <tudor.ambarus@microchip.com>
+ * Author: Tudor Ambarus
*/
#ifndef __ATMEL_I2C_H__
diff --git a/drivers/crypto/caam/blob_gen.c b/drivers/crypto/caam/blob_gen.c
index 1f65df48984783..f46b161d2cda65 100644
--- a/drivers/crypto/caam/blob_gen.c
+++ b/drivers/crypto/caam/blob_gen.c
@@ -104,7 +104,7 @@ int caam_process_blob(struct caam_blob_priv *priv,
}
ctrlpriv = dev_get_drvdata(jrdev->parent);
- moo = FIELD_GET(CSTA_MOO, ioread32(&ctrlpriv->ctrl->perfmon.status));
+ moo = FIELD_GET(CSTA_MOO, rd_reg32(&ctrlpriv->ctrl->perfmon.status));
if (moo != CSTA_MOO_SECURE && moo != CSTA_MOO_TRUSTED)
dev_warn(jrdev,
"using insecure test key, enable HAB to use unique device key!\n");
diff --git a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c
index e553ccadbcbc85..e5876286828b8e 100644
--- a/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c
+++ b/drivers/crypto/virtio/virtio_crypto_skcipher_algs.c
@@ -239,7 +239,8 @@ static int virtio_crypto_alg_skcipher_close_session(
pr_err("virtio_crypto: Close session failed status: %u, session_id: 0x%llx\n",
ctrl_status->status, destroy_session->session_id);
- return -EINVAL;
+ err = -EINVAL;
+ goto out;
}
err = 0;
diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index ad0849af42d790..13cde44c608602 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -736,4 +736,3 @@ module_exit(cxl_acpi_exit);
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(CXL);
MODULE_IMPORT_NS(ACPI);
-MODULE_SOFTDEP("pre: cxl_pmem");
diff --git a/drivers/cxl/core/pmem.c b/drivers/cxl/core/pmem.c
index f3d2169b673118..c2e4b10937884a 100644
--- a/drivers/cxl/core/pmem.c
+++ b/drivers/cxl/core/pmem.c
@@ -227,34 +227,16 @@ static struct cxl_nvdimm *cxl_nvdimm_alloc(struct cxl_nvdimm_bridge *cxl_nvb,
return cxl_nvd;
}
-static void cxl_nvd_unregister(void *_cxl_nvd)
+static void cxlmd_release_nvdimm(void *_cxlmd)
{
- struct cxl_nvdimm *cxl_nvd = _cxl_nvd;
- struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
+ struct cxl_memdev *cxlmd = _cxlmd;
+ struct cxl_nvdimm *cxl_nvd = cxlmd->cxl_nvd;
struct cxl_nvdimm_bridge *cxl_nvb = cxlmd->cxl_nvb;
- /*
- * Either the bridge is in ->remove() context under the device_lock(),
- * or cxlmd_release_nvdimm() is cancelling the bridge's release action
- * for @cxl_nvd and doing it itself (while manually holding the bridge
- * lock).
- */
- device_lock_assert(&cxl_nvb->dev);
cxl_nvd->cxlmd = NULL;
cxlmd->cxl_nvd = NULL;
+ cxlmd->cxl_nvb = NULL;
device_unregister(&cxl_nvd->dev);
-}
-
-static void cxlmd_release_nvdimm(void *_cxlmd)
-{
- struct cxl_memdev *cxlmd = _cxlmd;
- struct cxl_nvdimm_bridge *cxl_nvb = cxlmd->cxl_nvb;
-
- device_lock(&cxl_nvb->dev);
- if (cxlmd->cxl_nvd)
- devm_release_action(&cxl_nvb->dev, cxl_nvd_unregister,
- cxlmd->cxl_nvd);
- device_unlock(&cxl_nvb->dev);
put_device(&cxl_nvb->dev);
}
@@ -293,22 +275,6 @@ int devm_cxl_add_nvdimm(struct cxl_memdev *cxlmd)
dev_dbg(&cxlmd->dev, "register %s\n", dev_name(dev));
- /*
- * The two actions below arrange for @cxl_nvd to be deleted when either
- * the top-level PMEM bridge goes down, or the endpoint device goes
- * through ->remove().
- */
- device_lock(&cxl_nvb->dev);
- if (cxl_nvb->dev.driver)
- rc = devm_add_action_or_reset(&cxl_nvb->dev, cxl_nvd_unregister,
- cxl_nvd);
- else
- rc = -ENXIO;
- device_unlock(&cxl_nvb->dev);
-
- if (rc)
- goto err_alloc;
-
/* @cxlmd carries a reference on @cxl_nvb until cxlmd_release_nvdimm */
return devm_add_action_or_reset(&cxlmd->dev, cxlmd_release_nvdimm, cxlmd);
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 02f28da519e351..940f805b153408 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -131,7 +131,7 @@ static int cxl_region_decode_reset(struct cxl_region *cxlr, int count)
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
struct cxl_port *iter = cxled_to_port(cxled);
struct cxl_ep *ep;
- int rc;
+ int rc = 0;
while (!is_cxl_root(to_cxl_port(iter->dev.parent)))
iter = to_cxl_port(iter->dev.parent);
@@ -143,7 +143,8 @@ static int cxl_region_decode_reset(struct cxl_region *cxlr, int count)
cxl_rr = cxl_rr_load(iter, cxlr);
cxld = cxl_rr->decoder;
- rc = cxld->reset(cxld);
+ if (cxld->reset)
+ rc = cxld->reset(cxld);
if (rc)
return rc;
}
@@ -186,7 +187,8 @@ static int cxl_region_decode_commit(struct cxl_region *cxlr)
iter = ep->next, ep = cxl_ep_load(iter, cxlmd)) {
cxl_rr = cxl_rr_load(iter, cxlr);
cxld = cxl_rr->decoder;
- cxld->reset(cxld);
+ if (cxld->reset)
+ cxld->reset(cxld);
}
cxled->cxld.reset(&cxled->cxld);
@@ -991,10 +993,10 @@ static int cxl_port_setup_targets(struct cxl_port *port,
int i, distance;
/*
- * Passthrough ports impose no distance requirements between
+ * Passthrough decoders impose no distance requirements between
* peers
*/
- if (port->nr_dports == 1)
+ if (cxl_rr->nr_targets == 1)
distance = 0;
else
distance = p->nr_targets / cxl_rr->nr_targets;
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 33083a522fd1cc..258004f34281ae 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -554,8 +554,11 @@ static bool cxl_report_and_clear(struct cxl_dev_state *cxlds)
/* If multiple errors, log header points to first error from ctrl reg */
if (hweight32(status) > 1) {
- addr = cxlds->regs.ras + CXL_RAS_CAP_CONTROL_OFFSET;
- fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK, readl(addr)));
+ void __iomem *rcc_addr =
+ cxlds->regs.ras + CXL_RAS_CAP_CONTROL_OFFSET;
+
+ fe = BIT(FIELD_GET(CXL_RAS_CAP_CONTROL_FE_MASK,
+ readl(rcc_addr)));
} else {
fe = status;
}
diff --git a/drivers/cxl/pmem.c b/drivers/cxl/pmem.c
index eedefebc428391..08bbbac9a6d08a 100644
--- a/drivers/cxl/pmem.c
+++ b/drivers/cxl/pmem.c
@@ -225,11 +225,35 @@ static int cxl_pmem_ctl(struct nvdimm_bus_descriptor *nd_desc,
return cxl_pmem_nvdimm_ctl(nvdimm, cmd, buf, buf_len);
}
+static int detach_nvdimm(struct device *dev, void *data)
+{
+ struct cxl_nvdimm *cxl_nvd;
+ bool release = false;
+
+ if (!is_cxl_nvdimm(dev))
+ return 0;
+
+ device_lock(dev);
+ if (!dev->driver)
+ goto out;
+
+ cxl_nvd = to_cxl_nvdimm(dev);
+ if (cxl_nvd->cxlmd && cxl_nvd->cxlmd->cxl_nvb == data)
+ release = true;
+out:
+ device_unlock(dev);
+ if (release)
+ device_release_driver(dev);
+ return 0;
+}
+
static void unregister_nvdimm_bus(void *_cxl_nvb)
{
struct cxl_nvdimm_bridge *cxl_nvb = _cxl_nvb;
struct nvdimm_bus *nvdimm_bus = cxl_nvb->nvdimm_bus;
+ bus_for_each_dev(&cxl_bus_type, NULL, cxl_nvb, detach_nvdimm);
+
cxl_nvb->nvdimm_bus = NULL;
nvdimm_bus_unregister(nvdimm_bus);
}
diff --git a/drivers/dax/super.c b/drivers/dax/super.c
index da4438f3188c8d..c4c4728a36e433 100644
--- a/drivers/dax/super.c
+++ b/drivers/dax/super.c
@@ -475,7 +475,7 @@ EXPORT_SYMBOL_GPL(put_dax);
/**
* dax_holder() - obtain the holder of a dax device
* @dax_dev: a dax_device instance
-
+ *
* Return: the holder's data which represents the holder if registered,
* otherwize NULL.
*/
diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.c b/drivers/dma-buf/dma-buf-sysfs-stats.c
index f69d68122b9b13..fbf725fae7c1c3 100644
--- a/drivers/dma-buf/dma-buf-sysfs-stats.c
+++ b/drivers/dma-buf/dma-buf-sysfs-stats.c
@@ -168,14 +168,11 @@ void dma_buf_uninit_sysfs_statistics(void)
kset_unregister(dma_buf_stats_kset);
}
-int dma_buf_stats_setup(struct dma_buf *dmabuf)
+int dma_buf_stats_setup(struct dma_buf *dmabuf, struct file *file)
{
struct dma_buf_sysfs_entry *sysfs_entry;
int ret;
- if (!dmabuf || !dmabuf->file)
- return -EINVAL;
-
if (!dmabuf->exp_name) {
pr_err("exporter name must not be empty if stats needed\n");
return -EINVAL;
@@ -192,7 +189,7 @@ int dma_buf_stats_setup(struct dma_buf *dmabuf)
/* create the directory for buffer stats */
ret = kobject_init_and_add(&sysfs_entry->kobj, &dma_buf_ktype, NULL,
- "%lu", file_inode(dmabuf->file)->i_ino);
+ "%lu", file_inode(file)->i_ino);
if (ret)
goto err_sysfs_dmabuf;
diff --git a/drivers/dma-buf/dma-buf-sysfs-stats.h b/drivers/dma-buf/dma-buf-sysfs-stats.h
index a49c6e2650cccd..7a8a995b75bae1 100644
--- a/drivers/dma-buf/dma-buf-sysfs-stats.h
+++ b/drivers/dma-buf/dma-buf-sysfs-stats.h
@@ -13,7 +13,7 @@
int dma_buf_init_sysfs_statistics(void);
void dma_buf_uninit_sysfs_statistics(void);
-int dma_buf_stats_setup(struct dma_buf *dmabuf);
+int dma_buf_stats_setup(struct dma_buf *dmabuf, struct file *file);
void dma_buf_stats_teardown(struct dma_buf *dmabuf);
#else
@@ -25,7 +25,7 @@ static inline int dma_buf_init_sysfs_statistics(void)
static inline void dma_buf_uninit_sysfs_statistics(void) {}
-static inline int dma_buf_stats_setup(struct dma_buf *dmabuf)
+static inline int dma_buf_stats_setup(struct dma_buf *dmabuf, struct file *file)
{
return 0;
}
diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c
index b6c36914e7c611..e6528767efc7c3 100644
--- a/drivers/dma-buf/dma-buf.c
+++ b/drivers/dma-buf/dma-buf.c
@@ -95,10 +95,11 @@ static int dma_buf_file_release(struct inode *inode, struct file *file)
return -EINVAL;
dmabuf = file->private_data;
-
- mutex_lock(&db_list.lock);
- list_del(&dmabuf->list_node);
- mutex_unlock(&db_list.lock);
+ if (dmabuf) {
+ mutex_lock(&db_list.lock);
+ list_del(&dmabuf->list_node);
+ mutex_unlock(&db_list.lock);
+ }
return 0;
}
@@ -528,17 +529,17 @@ static inline int is_dma_buf_file(struct file *file)
return file->f_op == &dma_buf_fops;
}
-static struct file *dma_buf_getfile(struct dma_buf *dmabuf, int flags)
+static struct file *dma_buf_getfile(size_t size, int flags)
{
static atomic64_t dmabuf_inode = ATOMIC64_INIT(0);
- struct file *file;
struct inode *inode = alloc_anon_inode(dma_buf_mnt->mnt_sb);
+ struct file *file;
if (IS_ERR(inode))
return ERR_CAST(inode);
- inode->i_size = dmabuf->size;
- inode_set_bytes(inode, dmabuf->size);
+ inode->i_size = size;
+ inode_set_bytes(inode, size);
/*
* The ->i_ino acquired from get_next_ino() is not unique thus
@@ -552,8 +553,6 @@ static struct file *dma_buf_getfile(struct dma_buf *dmabuf, int flags)
flags, &dma_buf_fops);
if (IS_ERR(file))
goto err_alloc_file;
- file->private_data = dmabuf;
- file->f_path.dentry->d_fsdata = dmabuf;
return file;
@@ -619,19 +618,11 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
size_t alloc_size = sizeof(struct dma_buf);
int ret;
- if (!exp_info->resv)
- alloc_size += sizeof(struct dma_resv);
- else
- /* prevent &dma_buf[1] == dma_buf->resv */
- alloc_size += 1;
-
- if (WARN_ON(!exp_info->priv
- || !exp_info->ops
- || !exp_info->ops->map_dma_buf
- || !exp_info->ops->unmap_dma_buf
- || !exp_info->ops->release)) {
+ if (WARN_ON(!exp_info->priv || !exp_info->ops
+ || !exp_info->ops->map_dma_buf
+ || !exp_info->ops->unmap_dma_buf
+ || !exp_info->ops->release))
return ERR_PTR(-EINVAL);
- }
if (WARN_ON(exp_info->ops->cache_sgt_mapping &&
(exp_info->ops->pin || exp_info->ops->unpin)))
@@ -643,10 +634,21 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
if (!try_module_get(exp_info->owner))
return ERR_PTR(-ENOENT);
+ file = dma_buf_getfile(exp_info->size, exp_info->flags);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto err_module;
+ }
+
+ if (!exp_info->resv)
+ alloc_size += sizeof(struct dma_resv);
+ else
+ /* prevent &dma_buf[1] == dma_buf->resv */
+ alloc_size += 1;
dmabuf = kzalloc(alloc_size, GFP_KERNEL);
if (!dmabuf) {
ret = -ENOMEM;
- goto err_module;
+ goto err_file;
}
dmabuf->priv = exp_info->priv;
@@ -658,43 +660,35 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
init_waitqueue_head(&dmabuf->poll);
dmabuf->cb_in.poll = dmabuf->cb_out.poll = &dmabuf->poll;
dmabuf->cb_in.active = dmabuf->cb_out.active = 0;
+ INIT_LIST_HEAD(&dmabuf->attachments);
if (!resv) {
- resv = (struct dma_resv *)&dmabuf[1];
- dma_resv_init(resv);
+ dmabuf->resv = (struct dma_resv *)&dmabuf[1];
+ dma_resv_init(dmabuf->resv);
+ } else {
+ dmabuf->resv = resv;
}
- dmabuf->resv = resv;
- file = dma_buf_getfile(dmabuf, exp_info->flags);
- if (IS_ERR(file)) {
- ret = PTR_ERR(file);
+ ret = dma_buf_stats_setup(dmabuf, file);
+ if (ret)
goto err_dmabuf;
- }
+ file->private_data = dmabuf;
+ file->f_path.dentry->d_fsdata = dmabuf;
dmabuf->file = file;
- INIT_LIST_HEAD(&dmabuf->attachments);
-
mutex_lock(&db_list.lock);
list_add(&dmabuf->list_node, &db_list.head);
mutex_unlock(&db_list.lock);
- ret = dma_buf_stats_setup(dmabuf);
- if (ret)
- goto err_sysfs;
-
return dmabuf;
-err_sysfs:
- /*
- * Set file->f_path.dentry->d_fsdata to NULL so that when
- * dma_buf_release() gets invoked by dentry_ops, it exits
- * early before calling the release() dma_buf op.
- */
- file->f_path.dentry->d_fsdata = NULL;
- fput(file);
err_dmabuf:
+ if (!resv)
+ dma_resv_fini(dmabuf->resv);
kfree(dmabuf);
+err_file:
+ fput(file);
err_module:
module_put(exp_info->owner);
return ERR_PTR(ret);
diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c
index 406b4e26f538fd..0de0482cd36e23 100644
--- a/drivers/dma-buf/dma-fence.c
+++ b/drivers/dma-buf/dma-fence.c
@@ -167,7 +167,7 @@ struct dma_fence *dma_fence_allocate_private_stub(void)
0, 0);
set_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT,
- &dma_fence_stub.flags);
+ &fence->flags);
dma_fence_signal(fence);
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index c741b6431958c2..8a6e6b60d66f37 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -451,7 +451,8 @@ static int dma_chan_get(struct dma_chan *chan)
/* The channel is already in use, update client count */
if (chan->client_count) {
__module_get(owner);
- goto out;
+ chan->client_count++;
+ return 0;
}
if (!try_module_get(owner))
@@ -470,11 +471,11 @@ static int dma_chan_get(struct dma_chan *chan)
goto err_out;
}
+ chan->client_count++;
+
if (!dma_has_cap(DMA_PRIVATE, chan->device->cap_mask))
balance_ref_count(chan);
-out:
- chan->client_count++;
return 0;
err_out:
diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
index a183d93bd7e298..bf85aa0979ecb8 100644
--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
@@ -1018,6 +1018,11 @@ static noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status)
/* The bad descriptor currently is in the head of vc list */
vd = vchan_next_desc(&chan->vc);
+ if (!vd) {
+ dev_err(chan2dev(chan), "BUG: %s, IRQ with no descriptors\n",
+ axi_chan_name(chan));
+ goto out;
+ }
/* Remove the completed descriptor from issued list */
list_del(&vd->node);
@@ -1032,6 +1037,7 @@ static noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status)
/* Try to restart the controller */
axi_chan_start_first_queued(chan);
+out:
spin_unlock_irqrestore(&chan->vc.lock, flags);
}
diff --git a/drivers/dma/idxd/device.c b/drivers/dma/idxd/device.c
index 06f5d3783d7719..29dbb0f52e186c 100644
--- a/drivers/dma/idxd/device.c
+++ b/drivers/dma/idxd/device.c
@@ -1172,8 +1172,19 @@ static void idxd_flush_pending_descs(struct idxd_irq_entry *ie)
spin_unlock(&ie->list_lock);
list_for_each_entry_safe(desc, itr, &flist, list) {
+ struct dma_async_tx_descriptor *tx;
+
list_del(&desc->list);
ctype = desc->completion->status ? IDXD_COMPLETE_NORMAL : IDXD_COMPLETE_ABORT;
+ /*
+ * wq is being disabled. Any remaining descriptors are
+ * likely to be stuck and can be dropped. callback could
+ * point to code that is no longer accessible, for example
+ * if dmatest module has been unloaded.
+ */
+ tx = &desc->txd;
+ tx->callback = NULL;
+ tx->callback_result = NULL;
idxd_dma_complete_txd(desc, ctype, true);
}
}
@@ -1390,8 +1401,7 @@ err_res_alloc:
err_irq:
idxd_wq_unmap_portal(wq);
err_map_portal:
- rc = idxd_wq_disable(wq, false);
- if (rc < 0)
+ if (idxd_wq_disable(wq, false))
dev_dbg(dev, "wq %s disable failed\n", dev_name(wq_confdev(wq)));
err:
return rc;
@@ -1408,11 +1418,11 @@ void drv_disable_wq(struct idxd_wq *wq)
dev_warn(dev, "Clients has claim on wq %d: %d\n",
wq->id, idxd_wq_refcount(wq));
- idxd_wq_free_resources(wq);
idxd_wq_unmap_portal(wq);
idxd_wq_drain(wq);
idxd_wq_free_irq(wq);
idxd_wq_reset(wq);
+ idxd_wq_free_resources(wq);
percpu_ref_exit(&wq->wq_active);
wq->type = IDXD_WQT_NONE;
wq->client_count = 0;
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index fbea5f62dd98b8..b926abe4fa43ac 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -1521,10 +1521,12 @@ static struct sdma_desc *sdma_transfer_init(struct sdma_channel *sdmac,
sdma_config_ownership(sdmac, false, true, false);
if (sdma_load_context(sdmac))
- goto err_desc_out;
+ goto err_bd_out;
return desc;
+err_bd_out:
+ sdma_free_bd(desc);
err_desc_out:
kfree(desc);
err_out:
diff --git a/drivers/dma/lgm/lgm-dma.c b/drivers/dma/lgm/lgm-dma.c
index 9b9184f964be39..1709d159af7e01 100644
--- a/drivers/dma/lgm/lgm-dma.c
+++ b/drivers/dma/lgm/lgm-dma.c
@@ -914,7 +914,7 @@ static void ldma_dev_init(struct ldma_dev *d)
}
}
-static int ldma_cfg_init(struct ldma_dev *d)
+static int ldma_parse_dt(struct ldma_dev *d)
{
struct fwnode_handle *fwnode = dev_fwnode(d->dev);
struct ldma_port *p;
@@ -1661,10 +1661,6 @@ static int intel_ldma_probe(struct platform_device *pdev)
p->ldev = d;
}
- ret = ldma_cfg_init(d);
- if (ret)
- return ret;
-
dma_dev->dev = &pdev->dev;
ch_mask = (unsigned long)d->channels_mask;
@@ -1675,6 +1671,10 @@ static int intel_ldma_probe(struct platform_device *pdev)
ldma_dma_init_v3X(j, d);
}
+ ret = ldma_parse_dt(d);
+ if (ret)
+ return ret;
+
dma_dev->device_alloc_chan_resources = ldma_alloc_chan_resources;
dma_dev->device_free_chan_resources = ldma_free_chan_resources;
dma_dev->device_terminate_all = ldma_terminate_all;
diff --git a/drivers/dma/ptdma/ptdma-dev.c b/drivers/dma/ptdma/ptdma-dev.c
index 377da23012ac23..a2bf13ff18b6d2 100644
--- a/drivers/dma/ptdma/ptdma-dev.c
+++ b/drivers/dma/ptdma/ptdma-dev.c
@@ -71,12 +71,13 @@ static int pt_core_execute_cmd(struct ptdma_desc *desc, struct pt_cmd_queue *cmd
bool soc = FIELD_GET(DWORD0_SOC, desc->dw0);
u8 *q_desc = (u8 *)&cmd_q->qbase[cmd_q->qidx];
u32 tail;
+ unsigned long flags;
if (soc) {
desc->dw0 |= FIELD_PREP(DWORD0_IOC, desc->dw0);
desc->dw0 &= ~DWORD0_SOC;
}
- mutex_lock(&cmd_q->q_mutex);
+ spin_lock_irqsave(&cmd_q->q_lock, flags);
/* Copy 32-byte command descriptor to hw queue. */
memcpy(q_desc, desc, 32);
@@ -91,7 +92,7 @@ static int pt_core_execute_cmd(struct ptdma_desc *desc, struct pt_cmd_queue *cmd
/* Turn the queue back on using our cached control register */
pt_start_queue(cmd_q);
- mutex_unlock(&cmd_q->q_mutex);
+ spin_unlock_irqrestore(&cmd_q->q_lock, flags);
return 0;
}
@@ -199,7 +200,7 @@ int pt_core_init(struct pt_device *pt)
cmd_q->pt = pt;
cmd_q->dma_pool = dma_pool;
- mutex_init(&cmd_q->q_mutex);
+ spin_lock_init(&cmd_q->q_lock);
/* Page alignment satisfies our needs for N <= 128 */
cmd_q->qsize = Q_SIZE(Q_DESC_SIZE);
diff --git a/drivers/dma/ptdma/ptdma.h b/drivers/dma/ptdma/ptdma.h
index d093c43b7d1343..21b4bf895200b5 100644
--- a/drivers/dma/ptdma/ptdma.h
+++ b/drivers/dma/ptdma/ptdma.h
@@ -196,7 +196,7 @@ struct pt_cmd_queue {
struct ptdma_desc *qbase;
/* Aligned queue start address (per requirement) */
- struct mutex q_mutex ____cacheline_aligned;
+ spinlock_t q_lock ____cacheline_aligned;
unsigned int qidx;
unsigned int qsize;
diff --git a/drivers/dma/qcom/gpi.c b/drivers/dma/qcom/gpi.c
index 061add83229512..59a36cbf9b5f7f 100644
--- a/drivers/dma/qcom/gpi.c
+++ b/drivers/dma/qcom/gpi.c
@@ -1756,6 +1756,7 @@ static int gpi_create_spi_tre(struct gchan *chan, struct gpi_desc *desc,
tre->dword[3] = u32_encode_bits(TRE_TYPE_GO, TRE_FLAGS_TYPE);
if (spi->cmd == SPI_RX) {
tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_IEOB);
+ tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_LINK);
} else if (spi->cmd == SPI_TX) {
tre->dword[3] |= u32_encode_bits(1, TRE_FLAGS_CHAIN);
} else { /* SPI_DUPLEX */
diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c
index 1d1180db6d4ecd..8f67f453a4922c 100644
--- a/drivers/dma/tegra186-gpc-dma.c
+++ b/drivers/dma/tegra186-gpc-dma.c
@@ -711,6 +711,7 @@ static int tegra_dma_terminate_all(struct dma_chan *dc)
return err;
}
+ vchan_terminate_vdesc(&tdc->dma_desc->vd);
tegra_dma_disable(tdc);
tdc->dma_desc = NULL;
}
diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c
index ae39b52012b2fb..79da93cc77b64b 100644
--- a/drivers/dma/tegra210-adma.c
+++ b/drivers/dma/tegra210-adma.c
@@ -221,7 +221,7 @@ static int tegra_adma_init(struct tegra_adma *tdma)
int ret;
/* Clear any interrupts */
- tdma_write(tdma, tdma->cdata->global_int_clear, 0x1);
+ tdma_write(tdma, tdma->cdata->ch_base_offset + tdma->cdata->global_int_clear, 0x1);
/* Assert soft reset */
tdma_write(tdma, ADMA_GLOBAL_SOFT_RESET, 0x1);
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index ce8b80bb34d7d1..4c62274e0b3326 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -762,11 +762,12 @@ static void udma_decrement_byte_counters(struct udma_chan *uc, u32 val)
if (uc->desc->dir == DMA_DEV_TO_MEM) {
udma_rchanrt_write(uc, UDMA_CHAN_RT_BCNT_REG, val);
udma_rchanrt_write(uc, UDMA_CHAN_RT_SBCNT_REG, val);
- udma_rchanrt_write(uc, UDMA_CHAN_RT_PEER_BCNT_REG, val);
+ if (uc->config.ep_type != PSIL_EP_NATIVE)
+ udma_rchanrt_write(uc, UDMA_CHAN_RT_PEER_BCNT_REG, val);
} else {
udma_tchanrt_write(uc, UDMA_CHAN_RT_BCNT_REG, val);
udma_tchanrt_write(uc, UDMA_CHAN_RT_SBCNT_REG, val);
- if (!uc->bchan)
+ if (!uc->bchan && uc->config.ep_type != PSIL_EP_NATIVE)
udma_tchanrt_write(uc, UDMA_CHAN_RT_PEER_BCNT_REG, val);
}
}
diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
index a8d23cdf883e5b..ac09f0e5f58d88 100644
--- a/drivers/dma/xilinx/xilinx_dma.c
+++ b/drivers/dma/xilinx/xilinx_dma.c
@@ -3143,8 +3143,10 @@ static int xilinx_dma_probe(struct platform_device *pdev)
/* Initialize the channels */
for_each_child_of_node(node, child) {
err = xilinx_dma_child_probe(xdev, child);
- if (err < 0)
+ if (err < 0) {
+ of_node_put(child);
goto error;
+ }
}
if (xdev->dma_config->dmatype == XDMA_TYPE_VDMA) {
diff --git a/drivers/edac/edac_device.c b/drivers/edac/edac_device.c
index 19522c568aa5de..0689e15107213c 100644
--- a/drivers/edac/edac_device.c
+++ b/drivers/edac/edac_device.c
@@ -34,6 +34,9 @@
static DEFINE_MUTEX(device_ctls_mutex);
static LIST_HEAD(edac_device_list);
+/* Default workqueue processing interval on this instance, in msecs */
+#define DEFAULT_POLL_INTERVAL 1000
+
#ifdef CONFIG_EDAC_DEBUG
static void edac_device_dump_device(struct edac_device_ctl_info *edac_dev)
{
@@ -336,7 +339,7 @@ static void edac_device_workq_function(struct work_struct *work_req)
* whole one second to save timers firing all over the period
* between integral seconds
*/
- if (edac_dev->poll_msec == 1000)
+ if (edac_dev->poll_msec == DEFAULT_POLL_INTERVAL)
edac_queue_work(&edac_dev->work, round_jiffies_relative(edac_dev->delay));
else
edac_queue_work(&edac_dev->work, edac_dev->delay);
@@ -366,7 +369,7 @@ static void edac_device_workq_setup(struct edac_device_ctl_info *edac_dev,
* timers firing on sub-second basis, while they are happy
* to fire together on the 1 second exactly
*/
- if (edac_dev->poll_msec == 1000)
+ if (edac_dev->poll_msec == DEFAULT_POLL_INTERVAL)
edac_queue_work(&edac_dev->work, round_jiffies_relative(edac_dev->delay));
else
edac_queue_work(&edac_dev->work, edac_dev->delay);
@@ -394,17 +397,16 @@ static void edac_device_workq_teardown(struct edac_device_ctl_info *edac_dev)
* Then restart the workq on the new delay
*/
void edac_device_reset_delay_period(struct edac_device_ctl_info *edac_dev,
- unsigned long value)
+ unsigned long msec)
{
- unsigned long jiffs = msecs_to_jiffies(value);
-
- if (value == 1000)
- jiffs = round_jiffies_relative(value);
-
- edac_dev->poll_msec = value;
- edac_dev->delay = jiffs;
+ edac_dev->poll_msec = msec;
+ edac_dev->delay = msecs_to_jiffies(msec);
- edac_mod_work(&edac_dev->work, jiffs);
+ /* See comment in edac_device_workq_setup() above */
+ if (edac_dev->poll_msec == DEFAULT_POLL_INTERVAL)
+ edac_mod_work(&edac_dev->work, round_jiffies_relative(edac_dev->delay));
+ else
+ edac_mod_work(&edac_dev->work, edac_dev->delay);
}
int edac_device_alloc_index(void)
@@ -443,11 +445,7 @@ int edac_device_add_device(struct edac_device_ctl_info *edac_dev)
/* This instance is NOW RUNNING */
edac_dev->op_state = OP_RUNNING_POLL;
- /*
- * enable workq processing on this instance,
- * default = 1000 msec
- */
- edac_device_workq_setup(edac_dev, 1000);
+ edac_device_workq_setup(edac_dev, edac_dev->poll_msec ?: DEFAULT_POLL_INTERVAL);
} else {
edac_dev->op_state = OP_RUNNING_INTERRUPT;
}
diff --git a/drivers/edac/edac_module.h b/drivers/edac/edac_module.h
index 763c076d96f21d..47593afdc2348f 100644
--- a/drivers/edac/edac_module.h
+++ b/drivers/edac/edac_module.h
@@ -53,7 +53,7 @@ bool edac_stop_work(struct delayed_work *work);
bool edac_mod_work(struct delayed_work *work, unsigned long delay);
extern void edac_device_reset_delay_period(struct edac_device_ctl_info
- *edac_dev, unsigned long value);
+ *edac_dev, unsigned long msec);
extern void edac_mc_reset_delay_period(unsigned long value);
/*
diff --git a/drivers/edac/highbank_mc_edac.c b/drivers/edac/highbank_mc_edac.c
index 61b76ec226af11..19fba258ae1088 100644
--- a/drivers/edac/highbank_mc_edac.c
+++ b/drivers/edac/highbank_mc_edac.c
@@ -174,8 +174,10 @@ static int highbank_mc_probe(struct platform_device *pdev)
drvdata = mci->pvt_info;
platform_set_drvdata(pdev, mci);
- if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL))
- return -ENOMEM;
+ if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) {
+ res = -ENOMEM;
+ goto free;
+ }
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r) {
@@ -243,6 +245,7 @@ err2:
edac_mc_del_mc(&pdev->dev);
err:
devres_release_group(&pdev->dev, NULL);
+free:
edac_mc_free(mci);
return res;
}
diff --git a/drivers/edac/qcom_edac.c b/drivers/edac/qcom_edac.c
index 97a27e42dd610d..c45519f59dc11b 100644
--- a/drivers/edac/qcom_edac.c
+++ b/drivers/edac/qcom_edac.c
@@ -252,7 +252,7 @@ clear:
static int
dump_syn_reg(struct edac_device_ctl_info *edev_ctl, int err_type, u32 bank)
{
- struct llcc_drv_data *drv = edev_ctl->pvt_info;
+ struct llcc_drv_data *drv = edev_ctl->dev->platform_data;
int ret;
ret = dump_syn_reg_values(drv, bank, err_type);
@@ -289,7 +289,7 @@ static irqreturn_t
llcc_ecc_irq_handler(int irq, void *edev_ctl)
{
struct edac_device_ctl_info *edac_dev_ctl = edev_ctl;
- struct llcc_drv_data *drv = edac_dev_ctl->pvt_info;
+ struct llcc_drv_data *drv = edac_dev_ctl->dev->platform_data;
irqreturn_t irq_rc = IRQ_NONE;
u32 drp_error, trp_error, i;
int ret;
@@ -358,7 +358,6 @@ static int qcom_llcc_edac_probe(struct platform_device *pdev)
edev_ctl->dev_name = dev_name(dev);
edev_ctl->ctl_name = "llcc";
edev_ctl->panic_on_ue = LLCC_ERP_PANIC_ON_UE;
- edev_ctl->pvt_info = llcc_driv_data;
rc = edac_device_add_device(edev_ctl);
if (rc)
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index 958aa4662ccb0c..2c16ee8fd842d3 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -111,6 +111,7 @@ struct inbound_transaction_resource {
struct client_resource resource;
struct fw_card *card;
struct fw_request *request;
+ bool is_fcp;
void *data;
size_t length;
};
@@ -643,19 +644,14 @@ static int ioctl_send_request(struct client *client, union ioctl_arg *arg)
client->device->max_speed);
}
-static inline bool is_fcp_request(struct fw_request *request)
-{
- return request == NULL;
-}
-
static void release_request(struct client *client,
struct client_resource *resource)
{
struct inbound_transaction_resource *r = container_of(resource,
struct inbound_transaction_resource, resource);
- if (is_fcp_request(r->request))
- kfree(r->data);
+ if (r->is_fcp)
+ fw_request_put(r->request);
else
fw_send_response(r->card, r->request, RCODE_CONFLICT_ERROR);
@@ -669,15 +665,20 @@ static void handle_request(struct fw_card *card, struct fw_request *request,
void *payload, size_t length, void *callback_data)
{
struct address_handler_resource *handler = callback_data;
+ bool is_fcp = is_in_fcp_region(offset, length);
struct inbound_transaction_resource *r;
struct inbound_transaction_event *e;
size_t event_size0;
- void *fcp_frame = NULL;
int ret;
/* card may be different from handler->client->device->card */
fw_card_get(card);
+ // Extend the lifetime of data for request so that its payload is safely accessible in
+ // the process context for the client.
+ if (is_fcp)
+ fw_request_get(request);
+
r = kmalloc(sizeof(*r), GFP_ATOMIC);
e = kmalloc(sizeof(*e), GFP_ATOMIC);
if (r == NULL || e == NULL)
@@ -685,21 +686,10 @@ static void handle_request(struct fw_card *card, struct fw_request *request,
r->card = card;
r->request = request;
+ r->is_fcp = is_fcp;
r->data = payload;
r->length = length;
- if (is_fcp_request(request)) {
- /*
- * FIXME: Let core-transaction.c manage a
- * single reference-counted copy?
- */
- fcp_frame = kmemdup(payload, length, GFP_ATOMIC);
- if (fcp_frame == NULL)
- goto failed;
-
- r->data = fcp_frame;
- }
-
r->resource.release = release_request;
ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC);
if (ret < 0)
@@ -741,10 +731,11 @@ static void handle_request(struct fw_card *card, struct fw_request *request,
failed:
kfree(r);
kfree(e);
- kfree(fcp_frame);
- if (!is_fcp_request(request))
+ if (!is_fcp)
fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+ else
+ fw_request_put(request);
fw_card_put(card);
}
@@ -819,19 +810,19 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg)
r = container_of(resource, struct inbound_transaction_resource,
resource);
- if (is_fcp_request(r->request)) {
- kfree(r->data);
+ if (r->is_fcp) {
+ fw_request_put(r->request);
goto out;
}
if (a->length != fw_get_response_length(r->request)) {
ret = -EINVAL;
- kfree(r->request);
+ fw_request_put(r->request);
goto out;
}
if (copy_from_user(r->data, u64_to_uptr(a->data), a->length)) {
ret = -EFAULT;
- kfree(r->request);
+ fw_request_put(r->request);
goto out;
}
fw_send_response(r->card, r->request, a->rcode);
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c
index af498d767702d1..a9f70c96323e6a 100644
--- a/drivers/firewire/core-transaction.c
+++ b/drivers/firewire/core-transaction.c
@@ -535,12 +535,6 @@ const struct fw_address_region fw_unit_space_region =
{ .start = 0xfffff0000900ULL, .end = 0x1000000000000ULL, };
#endif /* 0 */
-static bool is_in_fcp_region(u64 offset, size_t length)
-{
- return offset >= (CSR_REGISTER_BASE | CSR_FCP_COMMAND) &&
- offset + length <= (CSR_REGISTER_BASE | CSR_FCP_END);
-}
-
/**
* fw_core_add_address_handler() - register for incoming requests
* @handler: callback
@@ -617,6 +611,7 @@ void fw_core_remove_address_handler(struct fw_address_handler *handler)
EXPORT_SYMBOL(fw_core_remove_address_handler);
struct fw_request {
+ struct kref kref;
struct fw_packet response;
u32 request_header[4];
int ack;
@@ -625,13 +620,33 @@ struct fw_request {
u32 data[];
};
+void fw_request_get(struct fw_request *request)
+{
+ kref_get(&request->kref);
+}
+
+static void release_request(struct kref *kref)
+{
+ struct fw_request *request = container_of(kref, struct fw_request, kref);
+
+ kfree(request);
+}
+
+void fw_request_put(struct fw_request *request)
+{
+ kref_put(&request->kref, release_request);
+}
+
static void free_response_callback(struct fw_packet *packet,
struct fw_card *card, int status)
{
- struct fw_request *request;
+ struct fw_request *request = container_of(packet, struct fw_request, response);
- request = container_of(packet, struct fw_request, response);
- kfree(request);
+ // Decrease the reference count since not at in-flight.
+ fw_request_put(request);
+
+ // Decrease the reference count to release the object.
+ fw_request_put(request);
}
int fw_get_response_length(struct fw_request *r)
@@ -782,6 +797,7 @@ static struct fw_request *allocate_request(struct fw_card *card,
request = kmalloc(sizeof(*request) + length, GFP_ATOMIC);
if (request == NULL)
return NULL;
+ kref_init(&request->kref);
request->response.speed = p->speed;
request->response.timestamp =
@@ -800,16 +816,22 @@ static struct fw_request *allocate_request(struct fw_card *card,
return request;
}
+/**
+ * fw_send_response: - send response packet for asynchronous transaction.
+ * @card: interface to send the response at.
+ * @request: firewire request data for the transaction.
+ * @rcode: response code to send.
+ *
+ * Submit a response packet into the asynchronous response transmission queue. The @request
+ * is going to be released when the transmission successfully finishes later.
+ */
void fw_send_response(struct fw_card *card,
struct fw_request *request, int rcode)
{
- if (WARN_ONCE(!request, "invalid for FCP address handlers"))
- return;
-
/* unified transaction or broadcast transaction: don't respond */
if (request->ack != ACK_PENDING ||
HEADER_DESTINATION_IS_BROADCAST(request->request_header[0])) {
- kfree(request);
+ fw_request_put(request);
return;
}
@@ -821,6 +843,9 @@ void fw_send_response(struct fw_card *card,
fw_fill_response(&request->response, request->request_header,
rcode, NULL, 0);
+ // Increase the reference count so that the object is kept during in-flight.
+ fw_request_get(request);
+
card->driver->send_response(card, &request->response);
}
EXPORT_SYMBOL(fw_send_response);
@@ -910,7 +935,7 @@ static void handle_fcp_region_request(struct fw_card *card,
rcu_read_lock();
list_for_each_entry_rcu(handler, &address_handler_list, link) {
if (is_enclosing_handler(handler, offset, request->length))
- handler->address_callback(card, NULL, tcode,
+ handler->address_callback(card, request, tcode,
destination, source,
p->generation, offset,
request->data,
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
index 71d5f16f311c46..eafa4eaae737ac 100644
--- a/drivers/firewire/core.h
+++ b/drivers/firewire/core.h
@@ -244,6 +244,9 @@ int fw_get_response_length(struct fw_request *request);
void fw_fill_response(struct fw_packet *response, u32 *request_header,
int rcode, void *payload, size_t length);
+void fw_request_get(struct fw_request *request);
+void fw_request_put(struct fw_request *request);
+
#define FW_PHY_CONFIG_NO_NODE_ID -1
#define FW_PHY_CONFIG_CURRENT_GAP_COUNT -1
void fw_send_phy_config(struct fw_card *card,
@@ -254,4 +257,10 @@ static inline bool is_ping_packet(u32 *data)
return (data[0] & 0xc0ffffff) == 0 && ~data[0] == data[1];
}
+static inline bool is_in_fcp_region(u64 offset, size_t length)
+{
+ return offset >= (CSR_REGISTER_BASE | CSR_FCP_COMMAND) &&
+ offset + length <= (CSR_REGISTER_BASE | CSR_FCP_END);
+}
+
#endif /* _FIREWIRE_CORE_H */
diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
index f818d00bb2c69e..ffdad59ec81fcb 100644
--- a/drivers/firmware/arm_scmi/driver.c
+++ b/drivers/firmware/arm_scmi/driver.c
@@ -910,6 +910,8 @@ static int do_xfer(const struct scmi_protocol_handle *ph,
xfer->hdr.protocol_id, xfer->hdr.seq,
xfer->hdr.poll_completion);
+ /* Clear any stale status */
+ xfer->hdr.status = SCMI_SUCCESS;
xfer->state = SCMI_XFER_SENT_OK;
/*
* Even though spinlocking is not needed here since no race is possible
diff --git a/drivers/firmware/arm_scmi/shmem.c b/drivers/firmware/arm_scmi/shmem.c
index 1dfe534b851845..87b4f4d35f0623 100644
--- a/drivers/firmware/arm_scmi/shmem.c
+++ b/drivers/firmware/arm_scmi/shmem.c
@@ -81,10 +81,11 @@ u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem)
void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
struct scmi_xfer *xfer)
{
+ size_t len = ioread32(&shmem->length);
+
xfer->hdr.status = ioread32(shmem->msg_payload);
/* Skip the length of header and status in shmem area i.e 8 bytes */
- xfer->rx.len = min_t(size_t, xfer->rx.len,
- ioread32(&shmem->length) - 8);
+ xfer->rx.len = min_t(size_t, xfer->rx.len, len > 8 ? len - 8 : 0);
/* Take a copy to the rx buffer.. */
memcpy_fromio(xfer->rx.buf, shmem->msg_payload + 4, xfer->rx.len);
@@ -93,8 +94,10 @@ void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem,
size_t max_len, struct scmi_xfer *xfer)
{
+ size_t len = ioread32(&shmem->length);
+
/* Skip only the length of header in shmem area i.e 4 bytes */
- xfer->rx.len = min_t(size_t, max_len, ioread32(&shmem->length) - 4);
+ xfer->rx.len = min_t(size_t, max_len, len > 4 ? len - 4 : 0);
/* Take a copy to the rx buffer.. */
memcpy_fromio(xfer->rx.buf, shmem->msg_payload, xfer->rx.len);
diff --git a/drivers/firmware/arm_scmi/virtio.c b/drivers/firmware/arm_scmi/virtio.c
index 33c9b81a55cd11..1db975c0889698 100644
--- a/drivers/firmware/arm_scmi/virtio.c
+++ b/drivers/firmware/arm_scmi/virtio.c
@@ -160,7 +160,6 @@ static void scmi_vio_channel_cleanup_sync(struct scmi_vio_channel *vioch)
}
vioch->shutdown_done = &vioch_shutdown_done;
- virtio_break_device(vioch->vqueue->vdev);
if (!vioch->is_rx && vioch->deferred_tx_wq)
/* Cannot be kicked anymore after this...*/
vioch->deferred_tx_wq = NULL;
@@ -482,6 +481,12 @@ static int virtio_chan_free(int id, void *p, void *data)
struct scmi_chan_info *cinfo = p;
struct scmi_vio_channel *vioch = cinfo->transport_info;
+ /*
+ * Break device to inhibit further traffic flowing while shutting down
+ * the channels: doing it later holding vioch->lock creates unsafe
+ * locking dependency chains as reported by LOCKDEP.
+ */
+ virtio_break_device(vioch->vqueue->vdev);
scmi_vio_channel_cleanup_sync(vioch);
scmi_free_channel(cinfo, data, id);
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 09716eebe8ac3a..1e0b016fdc2b12 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -394,8 +394,8 @@ static int __init efisubsys_init(void)
efi_kobj = kobject_create_and_add("efi", firmware_kobj);
if (!efi_kobj) {
pr_err("efi: Firmware registration failed.\n");
- destroy_workqueue(efi_rts_wq);
- return -ENOMEM;
+ error = -ENOMEM;
+ goto err_destroy_wq;
}
if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE |
@@ -443,7 +443,10 @@ err_unregister:
err_put:
kobject_put(efi_kobj);
efi_kobj = NULL;
- destroy_workqueue(efi_rts_wq);
+err_destroy_wq:
+ if (efi_rts_wq)
+ destroy_workqueue(efi_rts_wq);
+
return error;
}
@@ -1004,6 +1007,8 @@ int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size)
/* first try to find a slot in an existing linked list entry */
for (prsv = efi_memreserve_root->next; prsv; ) {
rsv = memremap(prsv, sizeof(*rsv), MEMREMAP_WB);
+ if (!rsv)
+ return -ENOMEM;
index = atomic_fetch_add_unless(&rsv->count, 1, rsv->size);
if (index < rsv->size) {
rsv->entry[index].base = addr;
diff --git a/drivers/firmware/efi/libstub/arm64.c b/drivers/firmware/efi/libstub/arm64.c
index ff2d18c42ee749..4501652e11ab64 100644
--- a/drivers/firmware/efi/libstub/arm64.c
+++ b/drivers/firmware/efi/libstub/arm64.c
@@ -19,10 +19,13 @@ static bool system_needs_vamap(void)
const u8 *type1_family = efi_get_smbios_string(1, family);
/*
- * Ampere Altra machines crash in SetTime() if SetVirtualAddressMap()
- * has not been called prior.
+ * Ampere eMAG, Altra, and Altra Max machines crash in SetTime() if
+ * SetVirtualAddressMap() has not been called prior.
*/
- if (!type1_family || strcmp(type1_family, "Altra"))
+ if (!type1_family || (
+ strcmp(type1_family, "eMAG") &&
+ strcmp(type1_family, "Altra") &&
+ strcmp(type1_family, "Altra Max")))
return false;
efi_warn("Working around broken SetVirtualAddressMap()\n");
diff --git a/drivers/firmware/efi/memattr.c b/drivers/firmware/efi/memattr.c
index 0a9aba5f9ceff0..f178b2984dfb2a 100644
--- a/drivers/firmware/efi/memattr.c
+++ b/drivers/firmware/efi/memattr.c
@@ -33,7 +33,7 @@ int __init efi_memattr_init(void)
return -ENOMEM;
}
- if (tbl->version > 1) {
+ if (tbl->version > 2) {
pr_warn("Unexpected EFI Memory Attributes table version %d\n",
tbl->version);
goto unmap;
diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c
index 7feee3d9c2bfbe..1fba4e09cdcff8 100644
--- a/drivers/firmware/efi/runtime-wrappers.c
+++ b/drivers/firmware/efi/runtime-wrappers.c
@@ -62,6 +62,7 @@ struct efi_runtime_work efi_rts_work;
\
if (!efi_enabled(EFI_RUNTIME_SERVICES)) { \
pr_warn_once("EFI Runtime Services are disabled!\n"); \
+ efi_rts_work.status = EFI_DEVICE_ERROR; \
goto exit; \
} \
\
diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c
index 2652c396c42368..33ae94745aef97 100644
--- a/drivers/firmware/google/coreboot_table.c
+++ b/drivers/firmware/google/coreboot_table.c
@@ -93,14 +93,19 @@ static int coreboot_table_populate(struct device *dev, void *ptr)
for (i = 0; i < header->table_entries; i++) {
entry = ptr_entry;
- device = kzalloc(sizeof(struct device) + entry->size, GFP_KERNEL);
+ if (entry->size < sizeof(*entry)) {
+ dev_warn(dev, "coreboot table entry too small!\n");
+ return -EINVAL;
+ }
+
+ device = kzalloc(sizeof(device->dev) + entry->size, GFP_KERNEL);
if (!device)
return -ENOMEM;
device->dev.parent = dev;
device->dev.bus = &coreboot_bus_type;
device->dev.release = coreboot_device_release;
- memcpy(&device->entry, ptr_entry, entry->size);
+ memcpy(device->raw, ptr_entry, entry->size);
switch (device->entry.tag) {
case LB_TAG_CBMEM_ENTRY:
diff --git a/drivers/firmware/google/coreboot_table.h b/drivers/firmware/google/coreboot_table.h
index 37f4d335a606d7..d814dca33a084c 100644
--- a/drivers/firmware/google/coreboot_table.h
+++ b/drivers/firmware/google/coreboot_table.h
@@ -79,6 +79,7 @@ struct coreboot_device {
struct lb_cbmem_ref cbmem_ref;
struct lb_cbmem_entry cbmem_entry;
struct lb_framebuffer framebuffer;
+ DECLARE_FLEX_ARRAY(u8, raw);
};
};
diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c
index 4e2575dfeb908f..871bedf533a805 100644
--- a/drivers/firmware/google/gsmi.c
+++ b/drivers/firmware/google/gsmi.c
@@ -361,9 +361,10 @@ static efi_status_t gsmi_get_variable(efi_char16_t *name,
memcpy(data, gsmi_dev.data_buf->start, *data_size);
/* All variables are have the following attributes */
- *attr = EFI_VARIABLE_NON_VOLATILE |
- EFI_VARIABLE_BOOTSERVICE_ACCESS |
- EFI_VARIABLE_RUNTIME_ACCESS;
+ if (attr)
+ *attr = EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS;
}
spin_unlock_irqrestore(&gsmi_dev.lock, flags);
diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
index e7bcfca4159f60..447ee4ea5c9034 100644
--- a/drivers/firmware/psci/psci.c
+++ b/drivers/firmware/psci/psci.c
@@ -440,6 +440,9 @@ static const struct file_operations psci_debugfs_ops = {
static int __init psci_debugfs_init(void)
{
+ if (!invoke_psci_fn || !psci_ops.get_version)
+ return 0;
+
return PTR_ERR_OR_ZERO(debugfs_create_file("psci", 0444, NULL, NULL,
&psci_debugfs_ops));
}
diff --git a/drivers/fpga/intel-m10-bmc-sec-update.c b/drivers/fpga/intel-m10-bmc-sec-update.c
index 79d48852825ef4..03f1bd81c434d6 100644
--- a/drivers/fpga/intel-m10-bmc-sec-update.c
+++ b/drivers/fpga/intel-m10-bmc-sec-update.c
@@ -574,20 +574,27 @@ static int m10bmc_sec_probe(struct platform_device *pdev)
len = scnprintf(buf, SEC_UPDATE_LEN_MAX, "secure-update%d",
sec->fw_name_id);
sec->fw_name = kmemdup_nul(buf, len, GFP_KERNEL);
- if (!sec->fw_name)
- return -ENOMEM;
+ if (!sec->fw_name) {
+ ret = -ENOMEM;
+ goto fw_name_fail;
+ }
fwl = firmware_upload_register(THIS_MODULE, sec->dev, sec->fw_name,
&m10bmc_ops, sec);
if (IS_ERR(fwl)) {
dev_err(sec->dev, "Firmware Upload driver failed to start\n");
- kfree(sec->fw_name);
- xa_erase(&fw_upload_xa, sec->fw_name_id);
- return PTR_ERR(fwl);
+ ret = PTR_ERR(fwl);
+ goto fw_uploader_fail;
}
sec->fwl = fwl;
return 0;
+
+fw_uploader_fail:
+ kfree(sec->fw_name);
+fw_name_fail:
+ xa_erase(&fw_upload_xa, sec->fw_name_id);
+ return ret;
}
static int m10bmc_sec_remove(struct platform_device *pdev)
diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
index 357cea58ec98ef..f7f01982a51266 100644
--- a/drivers/fpga/stratix10-soc.c
+++ b/drivers/fpga/stratix10-soc.c
@@ -213,9 +213,9 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
/* Allocate buffers from the service layer's pool. */
for (i = 0; i < NUM_SVC_BUFS; i++) {
kbuf = stratix10_svc_allocate_memory(priv->chan, SVC_BUF_SIZE);
- if (!kbuf) {
+ if (IS_ERR(kbuf)) {
s10_free_buffers(mgr);
- ret = -ENOMEM;
+ ret = PTR_ERR(kbuf);
goto init_done;
}
diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c
index 8d722e026e9c98..84352a6f4973b5 100644
--- a/drivers/gpio/gpio-eic-sprd.c
+++ b/drivers/gpio/gpio-eic-sprd.c
@@ -91,7 +91,6 @@ enum sprd_eic_type {
struct sprd_eic {
struct gpio_chip chip;
- struct irq_chip intc;
void __iomem *base[SPRD_EIC_MAX_BANK];
enum sprd_eic_type type;
spinlock_t lock;
@@ -255,6 +254,8 @@ static void sprd_eic_irq_mask(struct irq_data *data)
default:
dev_err(chip->parent, "Unsupported EIC type.\n");
}
+
+ gpiochip_disable_irq(chip, offset);
}
static void sprd_eic_irq_unmask(struct irq_data *data)
@@ -263,6 +264,8 @@ static void sprd_eic_irq_unmask(struct irq_data *data)
struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
u32 offset = irqd_to_hwirq(data);
+ gpiochip_enable_irq(chip, offset);
+
switch (sprd_eic->type) {
case SPRD_EIC_DEBOUNCE:
sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IE, 1);
@@ -564,6 +567,15 @@ static void sprd_eic_irq_handler(struct irq_desc *desc)
chained_irq_exit(ic, desc);
}
+static const struct irq_chip sprd_eic_irq = {
+ .name = "sprd-eic",
+ .irq_ack = sprd_eic_irq_ack,
+ .irq_mask = sprd_eic_irq_mask,
+ .irq_unmask = sprd_eic_irq_unmask,
+ .irq_set_type = sprd_eic_irq_set_type,
+ .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
static int sprd_eic_probe(struct platform_device *pdev)
{
const struct sprd_eic_variant_data *pdata;
@@ -626,15 +638,8 @@ static int sprd_eic_probe(struct platform_device *pdev)
break;
}
- sprd_eic->intc.name = dev_name(&pdev->dev);
- sprd_eic->intc.irq_ack = sprd_eic_irq_ack;
- sprd_eic->intc.irq_mask = sprd_eic_irq_mask;
- sprd_eic->intc.irq_unmask = sprd_eic_irq_unmask;
- sprd_eic->intc.irq_set_type = sprd_eic_irq_set_type;
- sprd_eic->intc.flags = IRQCHIP_SKIP_SET_WAKE;
-
irq = &sprd_eic->chip.irq;
- irq->chip = &sprd_eic->intc;
+ gpio_irq_chip_set_chip(irq, &sprd_eic_irq);
irq->handler = handle_bad_irq;
irq->default_type = IRQ_TYPE_NONE;
irq->parent_handler = sprd_eic_irq_handler;
diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c
index 2e177970911331..6cedf46efec6cb 100644
--- a/drivers/gpio/gpio-ep93xx.c
+++ b/drivers/gpio/gpio-ep93xx.c
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#include <linux/gpio/driver.h>
#include <linux/bitops.h>
+#include <linux/seq_file.h>
#define EP93XX_GPIO_F_INT_STATUS 0x5c
#define EP93XX_GPIO_A_INT_STATUS 0xa0
@@ -40,7 +41,6 @@
#define EP93XX_GPIO_F_IRQ_BASE 80
struct ep93xx_gpio_irq_chip {
- struct irq_chip ic;
u8 irq_offset;
u8 int_unmasked;
u8 int_enabled;
@@ -148,7 +148,7 @@ static void ep93xx_gpio_f_irq_handler(struct irq_desc *desc)
*/
struct irq_chip *irqchip = irq_desc_get_chip(desc);
unsigned int irq = irq_desc_get_irq(desc);
- int port_f_idx = ((irq + 1) & 7) ^ 4; /* {19..22,47..50} -> {0..7} */
+ int port_f_idx = (irq & 7) ^ 4; /* {20..23,48..51} -> {0..7} */
int gpio_irq = EP93XX_GPIO_F_IRQ_BASE + port_f_idx;
chained_irq_enter(irqchip, desc);
@@ -185,6 +185,7 @@ static void ep93xx_gpio_irq_mask_ack(struct irq_data *d)
ep93xx_gpio_update_int_params(epg, eic);
writeb(port_mask, epg->base + eic->irq_offset + EP93XX_INT_EOI_OFFSET);
+ gpiochip_disable_irq(gc, irqd_to_hwirq(d));
}
static void ep93xx_gpio_irq_mask(struct irq_data *d)
@@ -195,6 +196,7 @@ static void ep93xx_gpio_irq_mask(struct irq_data *d)
eic->int_unmasked &= ~BIT(d->irq & 7);
ep93xx_gpio_update_int_params(epg, eic);
+ gpiochip_disable_irq(gc, irqd_to_hwirq(d));
}
static void ep93xx_gpio_irq_unmask(struct irq_data *d)
@@ -203,6 +205,7 @@ static void ep93xx_gpio_irq_unmask(struct irq_data *d)
struct ep93xx_gpio_irq_chip *eic = to_ep93xx_gpio_irq_chip(gc);
struct ep93xx_gpio *epg = gpiochip_get_data(gc);
+ gpiochip_enable_irq(gc, irqd_to_hwirq(d));
eic->int_unmasked |= BIT(d->irq & 7);
ep93xx_gpio_update_int_params(epg, eic);
}
@@ -320,15 +323,25 @@ static int ep93xx_gpio_set_config(struct gpio_chip *gc, unsigned offset,
return 0;
}
-static void ep93xx_init_irq_chip(struct device *dev, struct irq_chip *ic)
+static void ep93xx_irq_print_chip(struct irq_data *data, struct seq_file *p)
{
- ic->irq_ack = ep93xx_gpio_irq_ack;
- ic->irq_mask_ack = ep93xx_gpio_irq_mask_ack;
- ic->irq_mask = ep93xx_gpio_irq_mask;
- ic->irq_unmask = ep93xx_gpio_irq_unmask;
- ic->irq_set_type = ep93xx_gpio_irq_type;
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
+
+ seq_printf(p, dev_name(gc->parent));
}
+static const struct irq_chip gpio_eic_irq_chip = {
+ .name = "ep93xx-gpio-eic",
+ .irq_ack = ep93xx_gpio_irq_ack,
+ .irq_mask = ep93xx_gpio_irq_mask,
+ .irq_unmask = ep93xx_gpio_irq_unmask,
+ .irq_mask_ack = ep93xx_gpio_irq_mask_ack,
+ .irq_set_type = ep93xx_gpio_irq_type,
+ .irq_print_chip = ep93xx_irq_print_chip,
+ .flags = IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc,
struct platform_device *pdev,
struct ep93xx_gpio *epg,
@@ -350,8 +363,6 @@ static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc,
girq = &gc->irq;
if (bank->has_irq || bank->has_hierarchical_irq) {
- struct irq_chip *ic;
-
gc->set_config = ep93xx_gpio_set_config;
egc->eic = devm_kcalloc(dev, 1,
sizeof(*egc->eic),
@@ -359,12 +370,7 @@ static int ep93xx_gpio_add_bank(struct ep93xx_gpio_chip *egc,
if (!egc->eic)
return -ENOMEM;
egc->eic->irq_offset = bank->irq;
- ic = &egc->eic->ic;
- ic->name = devm_kasprintf(dev, GFP_KERNEL, "gpio-irq-%s", bank->label);
- if (!ic->name)
- return -ENOMEM;
- ep93xx_init_irq_chip(dev, ic);
- girq->chip = ic;
+ gpio_irq_chip_set_chip(girq, &gpio_eic_irq_chip);
}
if (bank->has_irq) {
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index d5626c572d24ec..9d0cec4b82a334 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/spinlock.h>
#include <linux/syscore_ops.h>
#include <linux/gpio/driver.h>
#include <linux/of.h>
@@ -159,6 +160,7 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)
{
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct mxc_gpio_port *port = gc->private;
+ unsigned long flags;
u32 bit, val;
u32 gpio_idx = d->hwirq;
int edge;
@@ -197,6 +199,8 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)
return -EINVAL;
}
+ raw_spin_lock_irqsave(&port->gc.bgpio_lock, flags);
+
if (GPIO_EDGE_SEL >= 0) {
val = readl(port->base + GPIO_EDGE_SEL);
if (edge == GPIO_INT_BOTH_EDGES)
@@ -217,15 +221,20 @@ static int gpio_set_irq_type(struct irq_data *d, u32 type)
writel(1 << gpio_idx, port->base + GPIO_ISR);
port->pad_type[gpio_idx] = type;
- return 0;
+ raw_spin_unlock_irqrestore(&port->gc.bgpio_lock, flags);
+
+ return port->gc.direction_input(&port->gc, gpio_idx);
}
static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
{
void __iomem *reg = port->base;
+ unsigned long flags;
u32 bit, val;
int edge;
+ raw_spin_lock_irqsave(&port->gc.bgpio_lock, flags);
+
reg += GPIO_ICR1 + ((gpio & 0x10) >> 2); /* lower or upper register */
bit = gpio & 0xf;
val = readl(reg);
@@ -240,9 +249,12 @@ static void mxc_flip_edge(struct mxc_gpio_port *port, u32 gpio)
} else {
pr_err("mxc: invalid configuration for GPIO %d: %x\n",
gpio, edge);
- return;
+ goto unlock;
}
writel(val | (edge << (bit << 1)), reg);
+
+unlock:
+ raw_spin_unlock_irqrestore(&port->gc.bgpio_lock, flags);
}
/* handle 32 interrupts in one status register */
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index a59d61cd44b2e5..5299e5bb76d6e3 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -474,6 +474,9 @@ static u8 pcal6534_recalc_addr(struct pca953x_chip *chip, int reg, int off)
case PCAL6524_DEBOUNCE:
pinctrl = ((reg & PCAL_PINCTRL_MASK) >> 1) + 0x1c;
break;
+ default:
+ pinctrl = 0;
+ break;
}
return pinctrl + addr + (off / BANK_SZ);
diff --git a/drivers/gpio/gpio-pmic-eic-sprd.c b/drivers/gpio/gpio-pmic-eic-sprd.c
index e518490c4b681a..c3e4d90f6b1833 100644
--- a/drivers/gpio/gpio-pmic-eic-sprd.c
+++ b/drivers/gpio/gpio-pmic-eic-sprd.c
@@ -47,7 +47,6 @@ enum {
/**
* struct sprd_pmic_eic - PMIC EIC controller
* @chip: the gpio_chip structure.
- * @intc: the irq_chip structure.
* @map: the regmap from the parent device.
* @offset: the EIC controller's offset address of the PMIC.
* @reg: the array to cache the EIC registers.
@@ -56,7 +55,6 @@ enum {
*/
struct sprd_pmic_eic {
struct gpio_chip chip;
- struct irq_chip intc;
struct regmap *map;
u32 offset;
u8 reg[CACHE_NR_REGS];
@@ -151,15 +149,21 @@ static void sprd_pmic_eic_irq_mask(struct irq_data *data)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
+ u32 offset = irqd_to_hwirq(data);
pmic_eic->reg[REG_IE] = 0;
pmic_eic->reg[REG_TRIG] = 0;
+
+ gpiochip_disable_irq(chip, offset);
}
static void sprd_pmic_eic_irq_unmask(struct irq_data *data)
{
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
+ u32 offset = irqd_to_hwirq(data);
+
+ gpiochip_enable_irq(chip, offset);
pmic_eic->reg[REG_IE] = 1;
pmic_eic->reg[REG_TRIG] = 1;
@@ -292,6 +296,17 @@ static irqreturn_t sprd_pmic_eic_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
+static const struct irq_chip pmic_eic_irq_chip = {
+ .name = "sprd-pmic-eic",
+ .irq_mask = sprd_pmic_eic_irq_mask,
+ .irq_unmask = sprd_pmic_eic_irq_unmask,
+ .irq_set_type = sprd_pmic_eic_irq_set_type,
+ .irq_bus_lock = sprd_pmic_eic_bus_lock,
+ .irq_bus_sync_unlock = sprd_pmic_eic_bus_sync_unlock,
+ .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
+};
+
static int sprd_pmic_eic_probe(struct platform_device *pdev)
{
struct gpio_irq_chip *irq;
@@ -338,16 +353,8 @@ static int sprd_pmic_eic_probe(struct platform_device *pdev)
pmic_eic->chip.set = sprd_pmic_eic_set;
pmic_eic->chip.get = sprd_pmic_eic_get;
- pmic_eic->intc.name = dev_name(&pdev->dev);
- pmic_eic->intc.irq_mask = sprd_pmic_eic_irq_mask;
- pmic_eic->intc.irq_unmask = sprd_pmic_eic_irq_unmask;
- pmic_eic->intc.irq_set_type = sprd_pmic_eic_irq_set_type;
- pmic_eic->intc.irq_bus_lock = sprd_pmic_eic_bus_lock;
- pmic_eic->intc.irq_bus_sync_unlock = sprd_pmic_eic_bus_sync_unlock;
- pmic_eic->intc.flags = IRQCHIP_SKIP_SET_WAKE;
-
irq = &pmic_eic->chip.irq;
- irq->chip = &pmic_eic->intc;
+ gpio_irq_chip_set_chip(irq, &pmic_eic_irq_chip);
irq->threaded = true;
ret = devm_gpiochip_add_data(&pdev->dev, &pmic_eic->chip, pmic_eic);
diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c
index 238f3210970cfa..bc5660f61c570d 100644
--- a/drivers/gpio/gpio-sifive.c
+++ b/drivers/gpio/gpio-sifive.c
@@ -215,6 +215,7 @@ static int sifive_gpio_probe(struct platform_device *pdev)
return -ENODEV;
}
parent = irq_find_host(irq_parent);
+ of_node_put(irq_parent);
if (!parent) {
dev_err(dev, "no IRQ parent domain\n");
return -ENODEV;
diff --git a/drivers/gpio/gpio-sprd.c b/drivers/gpio/gpio-sprd.c
index 9bff63990eee43..072b4e6532164e 100644
--- a/drivers/gpio/gpio-sprd.c
+++ b/drivers/gpio/gpio-sprd.c
@@ -120,6 +120,7 @@ static void sprd_gpio_irq_mask(struct irq_data *data)
u32 offset = irqd_to_hwirq(data);
sprd_gpio_update(chip, offset, SPRD_GPIO_IE, 0);
+ gpiochip_disable_irq(chip, offset);
}
static void sprd_gpio_irq_ack(struct irq_data *data)
@@ -136,6 +137,7 @@ static void sprd_gpio_irq_unmask(struct irq_data *data)
u32 offset = irqd_to_hwirq(data);
sprd_gpio_update(chip, offset, SPRD_GPIO_IE, 1);
+ gpiochip_enable_irq(chip, offset);
}
static int sprd_gpio_irq_set_type(struct irq_data *data,
@@ -205,13 +207,14 @@ static void sprd_gpio_irq_handler(struct irq_desc *desc)
chained_irq_exit(ic, desc);
}
-static struct irq_chip sprd_gpio_irqchip = {
+static const struct irq_chip sprd_gpio_irqchip = {
.name = "sprd-gpio",
.irq_ack = sprd_gpio_irq_ack,
.irq_mask = sprd_gpio_irq_mask,
.irq_unmask = sprd_gpio_irq_unmask,
.irq_set_type = sprd_gpio_irq_set_type,
- .flags = IRQCHIP_SKIP_SET_WAKE,
+ .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_IMMUTABLE,
+ GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
static int sprd_gpio_probe(struct platform_device *pdev)
@@ -245,7 +248,7 @@ static int sprd_gpio_probe(struct platform_device *pdev)
sprd_gpio->chip.direction_output = sprd_gpio_direction_output;
irq = &sprd_gpio->chip.irq;
- irq->chip = &sprd_gpio_irqchip;
+ gpio_irq_chip_set_chip(irq, &sprd_gpio_irqchip);
irq->handler = handle_bad_irq;
irq->default_type = IRQ_TYPE_NONE;
irq->parent_handler = sprd_gpio_irq_handler;
diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c
index bed0380c51360a..17c53f484280f0 100644
--- a/drivers/gpio/gpiolib-acpi.c
+++ b/drivers/gpio/gpiolib-acpi.c
@@ -385,7 +385,7 @@ err:
}
static bool acpi_gpio_irq_is_wake(struct device *parent,
- struct acpi_resource_gpio *agpio)
+ const struct acpi_resource_gpio *agpio)
{
unsigned int pin = agpio->pin_table[0];
@@ -778,7 +778,7 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)
lookup->info.pin_config = agpio->pin_config;
lookup->info.debounce = agpio->debounce_timeout;
lookup->info.gpioint = gpioint;
- lookup->info.wake_capable = agpio->wake_capable == ACPI_WAKE_CAPABLE;
+ lookup->info.wake_capable = acpi_gpio_irq_is_wake(&lookup->info.adev->dev, agpio);
/*
* Polarity and triggering are only specified for GpioInt
@@ -1104,7 +1104,8 @@ int acpi_dev_gpio_irq_wake_get_by(struct acpi_device *adev, const char *name, in
dev_dbg(&adev->dev, "IRQ %d already in use\n", irq);
}
- if (wake_capable)
+ /* avoid suspend issues with GPIOs when systems are using S3 */
+ if (wake_capable && acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)
*wake_capable = info.wake_capable;
return irq;
@@ -1623,6 +1624,19 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = {
.ignore_interrupt = "AMDI0030:00@18",
},
},
+ {
+ /*
+ * Spurious wakeups from TP_ATTN# pin
+ * Found in BIOS 1.7.8
+ * https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627
+ */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"),
+ },
+ .driver_data = &(struct acpi_gpiolib_dmi_quirk) {
+ .ignore_wake = "ELAN0415:00@9",
+ },
+ },
{} /* Terminating entry */
};
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 5a66d9616d7cce..939c776b948813 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -3905,8 +3905,8 @@ static struct gpio_desc *gpiod_find_and_request(struct device *consumer,
const char *label,
bool platform_lookup_allowed)
{
+ unsigned long lookupflags = GPIO_LOOKUP_FLAGS_DEFAULT;
struct gpio_desc *desc = ERR_PTR(-ENOENT);
- unsigned long lookupflags;
int ret;
if (!IS_ERR_OR_NULL(fwnode))
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 6b74df446694b3..d148a1bd85e67a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -195,6 +195,7 @@ extern int amdgpu_emu_mode;
extern uint amdgpu_smu_memory_pool_size;
extern int amdgpu_smu_pptable_id;
extern uint amdgpu_dc_feature_mask;
+extern uint amdgpu_freesync_vid_mode;
extern uint amdgpu_dc_debug_mask;
extern uint amdgpu_dc_visual_confirm;
extern uint amdgpu_dm_abm_level;
@@ -242,6 +243,7 @@ extern int amdgpu_num_kcq;
#define AMDGPU_VCNFW_LOG_SIZE (32 * 1024)
extern int amdgpu_vcnfw_log;
+extern int amdgpu_sg_display;
#define AMDGPU_VM_MAX_NUM_CTX 4096
#define AMDGPU_SG_THRESHOLD (256*1024*1024)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
index b15091d8310d9d..3b5c53712d319c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
@@ -2099,7 +2099,7 @@ int amdgpu_amdkfd_map_gtt_bo_to_gart(struct amdgpu_device *adev, struct amdgpu_b
}
amdgpu_amdkfd_remove_eviction_fence(
- bo, bo->kfd_bo->process_info->eviction_fence);
+ bo, bo->vm_bo->vm->process_info->eviction_fence);
amdgpu_bo_unreserve(bo);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index 8516c814bc9b5e..7af3041ccd0e82 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -61,6 +61,8 @@ static int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p,
amdgpu_ctx_put(p->ctx);
return -ECANCELED;
}
+
+ amdgpu_sync_create(&p->sync);
return 0;
}
@@ -452,18 +454,6 @@ static int amdgpu_syncobj_lookup_and_add(struct amdgpu_cs_parser *p,
}
r = amdgpu_sync_fence(&p->sync, fence);
- if (r)
- goto error;
-
- /*
- * When we have an explicit dependency it might be necessary to insert a
- * pipeline sync to make sure that all caches etc are flushed and the
- * next job actually sees the results from the previous one.
- */
- if (fence->context == p->gang_leader->base.entity->fence_context)
- r = amdgpu_sync_fence(&p->gang_leader->explicit_sync, fence);
-
-error:
dma_fence_put(fence);
return r;
}
@@ -1188,10 +1178,19 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p)
static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p)
{
struct amdgpu_fpriv *fpriv = p->filp->driver_priv;
+ struct drm_gpu_scheduler *sched;
struct amdgpu_bo_list_entry *e;
+ struct dma_fence *fence;
unsigned int i;
int r;
+ r = amdgpu_ctx_wait_prev_fence(p->ctx, p->entities[p->gang_leader_idx]);
+ if (r) {
+ if (r != -ERESTARTSYS)
+ DRM_ERROR("amdgpu_ctx_wait_prev_fence failed.\n");
+ return r;
+ }
+
list_for_each_entry(e, &p->validated, tv.head) {
struct amdgpu_bo *bo = ttm_to_amdgpu_bo(e->tv.bo);
struct dma_resv *resv = bo->tbo.base.resv;
@@ -1211,10 +1210,27 @@ static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p)
return r;
}
- r = amdgpu_ctx_wait_prev_fence(p->ctx, p->entities[p->gang_leader_idx]);
- if (r && r != -ERESTARTSYS)
- DRM_ERROR("amdgpu_ctx_wait_prev_fence failed.\n");
- return r;
+ sched = p->gang_leader->base.entity->rq->sched;
+ while ((fence = amdgpu_sync_get_fence(&p->sync))) {
+ struct drm_sched_fence *s_fence = to_drm_sched_fence(fence);
+
+ /*
+ * When we have an dependency it might be necessary to insert a
+ * pipeline sync to make sure that all caches etc are flushed and the
+ * next job actually sees the results from the previous one
+ * before we start executing on the same scheduler ring.
+ */
+ if (!s_fence || s_fence->sched != sched) {
+ dma_fence_put(fence);
+ continue;
+ }
+
+ r = amdgpu_sync_fence(&p->gang_leader->explicit_sync, fence);
+ dma_fence_put(fence);
+ if (r)
+ return r;
+ }
+ return 0;
}
static void amdgpu_cs_post_dependencies(struct amdgpu_cs_parser *p)
@@ -1254,9 +1270,12 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
continue;
fence = &p->jobs[i]->base.s_fence->scheduled;
+ dma_fence_get(fence);
r = drm_sched_job_add_dependency(&leader->base, fence);
- if (r)
+ if (r) {
+ dma_fence_put(fence);
goto error_cleanup;
+ }
}
if (p->gang_size > 1) {
@@ -1344,6 +1363,7 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser)
{
unsigned i;
+ amdgpu_sync_free(&parser->sync);
for (i = 0; i < parser->num_post_deps; i++) {
drm_syncobj_put(parser->post_deps[i].syncobj);
kfree(parser->post_deps[i].chain);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index afe6af9c013852..2f28a8c02f6412 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -36,6 +36,7 @@
#include <generated/utsrelease.h>
#include <linux/pci-p2pdma.h>
+#include <drm/drm_aperture.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_probe_helper.h>
@@ -90,6 +91,8 @@ MODULE_FIRMWARE("amdgpu/navi12_gpu_info.bin");
#define AMDGPU_MAX_RETRY_LIMIT 2
#define AMDGPU_RETRY_SRIOV_RESET(r) ((r) == -EBUSY || (r) == -ETIMEDOUT || (r) == -EINVAL)
+static const struct drm_driver amdgpu_kms_driver;
+
const char *amdgpu_asic_name[] = {
"TAHITI",
"PITCAIRN",
@@ -3687,6 +3690,11 @@ int amdgpu_device_init(struct amdgpu_device *adev,
if (r)
return r;
+ /* Get rid of things like offb */
+ r = drm_aperture_remove_conflicting_pci_framebuffers(adev->pdev, &amdgpu_kms_driver);
+ if (r)
+ return r;
+
/* Enable TMZ based on IP_VERSION */
amdgpu_gmc_tmz_set(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index b4f2d61ea0d53b..3fe277bc233f4f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -23,7 +23,6 @@
*/
#include <drm/amdgpu_drm.h>
-#include <drm/drm_aperture.h>
#include <drm/drm_drv.h>
#include <drm/drm_fbdev_generic.h>
#include <drm/drm_gem.h>
@@ -181,11 +180,13 @@ int amdgpu_mes_kiq;
int amdgpu_noretry = -1;
int amdgpu_force_asic_type = -1;
int amdgpu_tmz = -1; /* auto */
+uint amdgpu_freesync_vid_mode;
int amdgpu_reset_method = -1; /* auto */
int amdgpu_num_kcq = -1;
int amdgpu_smartshift_bias;
int amdgpu_use_xgmi_p2p = 1;
int amdgpu_vcnfw_log;
+int amdgpu_sg_display = -1; /* auto */
static void amdgpu_drv_delayed_reset_work_handler(struct work_struct *work);
@@ -880,6 +881,32 @@ MODULE_PARM_DESC(tmz, "Enable TMZ feature (-1 = auto (default), 0 = off, 1 = on)
module_param_named(tmz, amdgpu_tmz, int, 0444);
/**
+ * DOC: freesync_video (uint)
+ * Enable the optimization to adjust front porch timing to achieve seamless
+ * mode change experience when setting a freesync supported mode for which full
+ * modeset is not needed.
+ *
+ * The Display Core will add a set of modes derived from the base FreeSync
+ * video mode into the corresponding connector's mode list based on commonly
+ * used refresh rates and VRR range of the connected display, when users enable
+ * this feature. From the userspace perspective, they can see a seamless mode
+ * change experience when the change between different refresh rates under the
+ * same resolution. Additionally, userspace applications such as Video playback
+ * can read this modeset list and change the refresh rate based on the video
+ * frame rate. Finally, the userspace can also derive an appropriate mode for a
+ * particular refresh rate based on the FreeSync Mode and add it to the
+ * connector's mode list.
+ *
+ * Note: This is an experimental feature.
+ *
+ * The default value: 0 (off).
+ */
+MODULE_PARM_DESC(
+ freesync_video,
+ "Enable freesync modesetting optimization feature (0 = off (default), 1 = on)");
+module_param_named(freesync_video, amdgpu_freesync_vid_mode, uint, 0444);
+
+/**
* DOC: reset_method (int)
* GPU reset method (-1 = auto (default), 0 = legacy, 1 = mode0, 2 = mode1, 3 = mode2, 4 = baco)
*/
@@ -906,6 +933,16 @@ MODULE_PARM_DESC(vcnfw_log, "Enable vcnfw log(0 = disable (default value), 1 = e
module_param_named(vcnfw_log, amdgpu_vcnfw_log, int, 0444);
/**
+ * DOC: sg_display (int)
+ * Disable S/G (scatter/gather) display (i.e., display from system memory).
+ * This option is only relevant on APUs. Set this option to 0 to disable
+ * S/G display if you experience flickering or other issues under memory
+ * pressure and report the issue.
+ */
+MODULE_PARM_DESC(sg_display, "S/G Display (-1 = auto (default), 0 = disable)");
+module_param_named(sg_display, amdgpu_sg_display, int, 0444);
+
+/**
* DOC: smu_pptable_id (int)
* Used to override pptable id. id = 0 use VBIOS pptable.
* id > 0 use the soft pptable with specicfied id.
@@ -2095,11 +2132,6 @@ static int amdgpu_pci_probe(struct pci_dev *pdev,
}
#endif
- /* Get rid of things like offb */
- ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, &amdgpu_kms_driver);
- if (ret)
- return ret;
-
adev = devm_drm_dev_alloc(&pdev->dev, &amdgpu_kms_driver, typeof(*adev), ddev);
if (IS_ERR(adev))
return PTR_ERR(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
index 00444203220d2a..faff4a3f96e6e8 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
@@ -618,7 +618,13 @@ void amdgpu_fence_driver_sw_fini(struct amdgpu_device *adev)
if (!ring || !ring->fence_drv.initialized)
continue;
- if (!ring->no_scheduler)
+ /*
+ * Notice we check for sched.ops since there's some
+ * override on the meaning of sched.ready by amdgpu.
+ * The natural check would be sched.ready, which is
+ * set as drm_sched_init() finishes...
+ */
+ if (ring->sched.ops)
drm_sched_fini(&ring->sched);
for (j = 0; j <= ring->fence_drv.num_fences_mask; ++j)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
index 23692e5d4d13b3..3380daf42da8af 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c
@@ -156,6 +156,9 @@ static bool amdgpu_gfx_is_compute_multipipe_capable(struct amdgpu_device *adev)
return amdgpu_compute_multipipe == 1;
}
+ if (adev->ip_versions[GC_HWIP][0] > IP_VERSION(9, 0, 0))
+ return true;
+
/* FIXME: spreading the queues across pipes causes perf regressions
* on POLARIS11 compute workloads */
if (adev->asic_type == CHIP_POLARIS11)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
index fcb711a11a5b6b..3f07b1a2ce47f9 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c
@@ -497,6 +497,7 @@ void amdgpu_vmid_free_reserved(struct amdgpu_device *adev,
!--id_mgr->reserved_use_count) {
/* give the reserved ID back to normal round robin */
list_add(&id_mgr->reserved->list, &id_mgr->ids_lru);
+ id_mgr->reserved = NULL;
}
vm->reserved_vmid[vmhub] = false;
mutex_unlock(&id_mgr->lock);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
index 9e549923622bd4..c3d9d75143f4ff 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
@@ -161,8 +161,14 @@ void amdgpu_job_free_resources(struct amdgpu_job *job)
struct dma_fence *f;
unsigned i;
- /* use sched fence if available */
- f = job->base.s_fence ? &job->base.s_fence->finished : &job->hw_fence;
+ /* Check if any fences where initialized */
+ if (job->base.s_fence && job->base.s_fence->finished.ops)
+ f = &job->base.s_fence->finished;
+ else if (job->hw_fence.ops)
+ f = &job->hw_fence;
+ else
+ f = NULL;
+
for (i = 0; i < job->num_ibs; ++i)
amdgpu_ib_free(ring->adev, &job->ibs[i], f);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
index 4e684c2afc709f..25a68d8888e0d5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
@@ -470,8 +470,9 @@ static bool amdgpu_bo_validate_size(struct amdgpu_device *adev,
return true;
fail:
- DRM_DEBUG("BO size %lu > total memory in domain: %llu\n", size,
- man->size);
+ if (man)
+ DRM_DEBUG("BO size %lu > total memory in domain: %llu\n", size,
+ man->size);
return false;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
index f752c7ae7f60e4..3989e755a5b4be 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h
@@ -295,7 +295,7 @@ struct amdgpu_ring {
#define amdgpu_ring_parse_cs(r, p, job, ib) ((r)->funcs->parse_cs((p), (job), (ib)))
#define amdgpu_ring_patch_cs_in_place(r, p, job, ib) ((r)->funcs->patch_cs_in_place((p), (job), (ib)))
#define amdgpu_ring_test_ring(r) (r)->funcs->test_ring((r))
-#define amdgpu_ring_test_ib(r, t) (r)->funcs->test_ib((r), (t))
+#define amdgpu_ring_test_ib(r, t) ((r)->funcs->test_ib ? (r)->funcs->test_ib((r), (t)) : 0)
#define amdgpu_ring_get_rptr(r) (r)->funcs->get_rptr((r))
#define amdgpu_ring_get_wptr(r) (r)->funcs->get_wptr((r))
#define amdgpu_ring_set_wptr(r) (r)->funcs->set_wptr((r))
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c
index bac7976975bd3b..dcd8c066bc1f50 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c
@@ -391,8 +391,10 @@ int amdgpu_sync_push_to_job(struct amdgpu_sync *sync, struct amdgpu_job *job)
dma_fence_get(f);
r = drm_sched_job_add_dependency(&job->base, f);
- if (r)
+ if (r) {
+ dma_fence_put(f);
return r;
+ }
}
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c
index b5f3bba851db86..01e42bdd8e4e89 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c
@@ -974,7 +974,7 @@ int amdgpu_vm_ptes_update(struct amdgpu_vm_update_params *params,
trace_amdgpu_vm_update_ptes(params, frag_start, upd_end,
min(nptes, 32u), dst, incr,
upd_flags,
- vm->task_info.pid,
+ vm->task_info.tgid,
vm->immediate.fence_context);
amdgpu_vm_pte_update_flags(params, to_amdgpu_bo_vm(pt),
cursor.level, pe_start, dst,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
index faa12146635cf1..9fa1d814508a6c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
@@ -882,7 +882,7 @@ void amdgpu_vram_mgr_fini(struct amdgpu_device *adev)
kfree(rsv);
list_for_each_entry_safe(rsv, temp, &mgr->reserved_pages, blocks) {
- drm_buddy_free_list(&mgr->mm, &rsv->blocks);
+ drm_buddy_free_list(&mgr->mm, &rsv->allocated);
kfree(rsv);
}
drm_buddy_fini(&mgr->mm);
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
index a56c6e106d00df..66eb102cd88fbd 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
@@ -790,8 +790,8 @@ static void gfx_v11_0_read_wave_data(struct amdgpu_device *adev, uint32_t simd,
* zero here */
WARN_ON(simd != 0);
- /* type 2 wave data */
- dst[(*no_fields)++] = 2;
+ /* type 3 wave data */
+ dst[(*no_fields)++] = 3;
dst[(*no_fields)++] = wave_read_ind(adev, wave, ixSQ_WAVE_STATUS);
dst[(*no_fields)++] = wave_read_ind(adev, wave, ixSQ_WAVE_PC_LO);
dst[(*no_fields)++] = wave_read_ind(adev, wave, ixSQ_WAVE_PC_HI);
@@ -1287,10 +1287,8 @@ static int gfx_v11_0_sw_init(void *handle)
switch (adev->ip_versions[GC_HWIP][0]) {
case IP_VERSION(11, 0, 0):
- case IP_VERSION(11, 0, 1):
case IP_VERSION(11, 0, 2):
case IP_VERSION(11, 0, 3):
- case IP_VERSION(11, 0, 4):
adev->gfx.me.num_me = 1;
adev->gfx.me.num_pipe_per_me = 1;
adev->gfx.me.num_queue_per_pipe = 1;
@@ -1298,6 +1296,15 @@ static int gfx_v11_0_sw_init(void *handle)
adev->gfx.mec.num_pipe_per_mec = 4;
adev->gfx.mec.num_queue_per_pipe = 4;
break;
+ case IP_VERSION(11, 0, 1):
+ case IP_VERSION(11, 0, 4):
+ adev->gfx.me.num_me = 1;
+ adev->gfx.me.num_pipe_per_me = 1;
+ adev->gfx.me.num_queue_per_pipe = 1;
+ adev->gfx.mec.num_mec = 1;
+ adev->gfx.mec.num_pipe_per_mec = 4;
+ adev->gfx.mec.num_queue_per_pipe = 4;
+ break;
default:
adev->gfx.me.num_me = 1;
adev->gfx.me.num_pipe_per_me = 1;
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
index f202b45c413c90..5dde6f82a1ca09 100644
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
@@ -6877,7 +6877,6 @@ static const struct amdgpu_ring_funcs gfx_v9_0_ring_funcs_gfx = {
.emit_gds_switch = gfx_v9_0_ring_emit_gds_switch,
.emit_hdp_flush = gfx_v9_0_ring_emit_hdp_flush,
.test_ring = gfx_v9_0_ring_test_ring,
- .test_ib = gfx_v9_0_ring_test_ib,
.insert_nop = amdgpu_ring_insert_nop,
.pad_ib = amdgpu_ring_generic_pad_ib,
.emit_switch_buffer = gfx_v9_ring_emit_sb,
diff --git a/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c b/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c
index 95548c512f4fb7..077c53c6cc08b0 100644
--- a/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c
@@ -35,6 +35,7 @@ MODULE_FIRMWARE("amdgpu/gc_11_0_0_imu.bin");
MODULE_FIRMWARE("amdgpu/gc_11_0_1_imu.bin");
MODULE_FIRMWARE("amdgpu/gc_11_0_2_imu.bin");
MODULE_FIRMWARE("amdgpu/gc_11_0_3_imu.bin");
+MODULE_FIRMWARE("amdgpu/gc_11_0_4_imu.bin");
static int imu_v11_0_init_microcode(struct amdgpu_device *adev)
{
diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c
index 970b066b37bb91..5dff79e8f30110 100644
--- a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c
@@ -40,6 +40,8 @@ MODULE_FIRMWARE("amdgpu/gc_11_0_2_mes.bin");
MODULE_FIRMWARE("amdgpu/gc_11_0_2_mes1.bin");
MODULE_FIRMWARE("amdgpu/gc_11_0_3_mes.bin");
MODULE_FIRMWARE("amdgpu/gc_11_0_3_mes1.bin");
+MODULE_FIRMWARE("amdgpu/gc_11_0_4_mes.bin");
+MODULE_FIRMWARE("amdgpu/gc_11_0_4_mes1.bin");
static int mes_v11_0_hw_fini(void *handle);
static int mes_v11_0_kiq_hw_init(struct amdgpu_device *adev);
@@ -196,7 +198,6 @@ static int mes_v11_0_add_hw_queue(struct amdgpu_mes *mes,
mes_add_queue_pkt.trap_handler_addr = input->tba_addr;
mes_add_queue_pkt.tma_addr = input->tma_addr;
mes_add_queue_pkt.is_kfd_process = input->is_kfd_process;
- mes_add_queue_pkt.trap_en = 1;
/* For KFD, gds_size is re-used for queue size (needed in MES for AQL queues) */
mes_add_queue_pkt.is_aql_queue = input->is_aql_queue;
diff --git a/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c b/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c
index 15eb3658d70e64..09fdcd20cb9196 100644
--- a/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c
+++ b/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c
@@ -337,7 +337,13 @@ const struct nbio_hdp_flush_reg nbio_v4_3_hdp_flush_reg = {
static void nbio_v4_3_init_registers(struct amdgpu_device *adev)
{
- return;
+ if (adev->ip_versions[NBIO_HWIP][0] == IP_VERSION(4, 3, 0)) {
+ uint32_t data;
+
+ data = RREG32_SOC15(NBIO, 0, regRCC_DEV0_EPF2_STRAP2);
+ data &= ~RCC_DEV0_EPF2_STRAP2__STRAP_NO_SOFT_RESET_DEV0_F2_MASK;
+ WREG32_SOC15(NBIO, 0, regRCC_DEV0_EPF2_STRAP2, data);
+ }
}
static u32 nbio_v4_3_get_rom_offset(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c
index 5562670b7b5219..7050238c4c4899 100644
--- a/drivers/gpu/drm/amd/amdgpu/soc21.c
+++ b/drivers/gpu/drm/amd/amdgpu/soc21.c
@@ -640,7 +640,10 @@ static int soc21_common_early_init(void *handle)
AMD_CG_SUPPORT_GFX_CGCG |
AMD_CG_SUPPORT_GFX_CGLS |
AMD_CG_SUPPORT_REPEATER_FGCG |
- AMD_CG_SUPPORT_GFX_MGCG;
+ AMD_CG_SUPPORT_GFX_MGCG |
+ AMD_CG_SUPPORT_HDP_SD |
+ AMD_CG_SUPPORT_ATHUB_MGCG |
+ AMD_CG_SUPPORT_ATHUB_LS;
adev->pg_flags = AMD_PG_SUPPORT_VCN |
AMD_PG_SUPPORT_VCN_DPG |
AMD_PG_SUPPORT_JPEG;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
index ecb4c3abc62973..c06ada0844ba15 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
@@ -200,7 +200,7 @@ static int add_queue_mes(struct device_queue_manager *dqm, struct queue *q,
queue_input.wptr_addr = (uint64_t)q->properties.write_ptr;
if (q->wptr_bo) {
- wptr_addr_off = (uint64_t)q->properties.write_ptr - (uint64_t)q->wptr_bo->kfd_bo->va;
+ wptr_addr_off = (uint64_t)q->properties.write_ptr & (PAGE_SIZE - 1);
queue_input.wptr_mc_addr = ((uint64_t)q->wptr_bo->tbo.resource->start << PAGE_SHIFT) + wptr_addr_off;
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
index 814f99888ab10b..b94d2c1422ad82 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
@@ -570,6 +570,15 @@ svm_range_vram_node_new(struct amdgpu_device *adev, struct svm_range *prange,
goto reserve_bo_failed;
}
+ if (clear) {
+ r = amdgpu_bo_sync_wait(bo, AMDGPU_FENCE_OWNER_KFD, false);
+ if (r) {
+ pr_debug("failed %d to sync bo\n", r);
+ amdgpu_bo_unreserve(bo);
+ goto reserve_bo_failed;
+ }
+ }
+
r = dma_resv_reserve_fences(bo->tbo.base.resv, 1);
if (r) {
pr_debug("failed %d to reserve bo\n", r);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
index bceb1a5b25186d..3fdaba56be6fbf 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c
@@ -801,7 +801,7 @@ static int kfd_build_sysfs_node_entry(struct kfd_topology_device *dev,
p2plink->attr.name = "properties";
p2plink->attr.mode = KFD_SYSFS_FILE_MODE;
- sysfs_attr_init(&iolink->attr);
+ sysfs_attr_init(&p2plink->attr);
ret = sysfs_create_file(p2plink->kobj, &p2plink->attr);
if (ret < 0)
return ret;
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 50c783e19f5ab0..93dee3d1a483f1 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -1184,24 +1184,38 @@ static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_
memset(pa_config, 0, sizeof(*pa_config));
- logical_addr_low = min(adev->gmc.fb_start, adev->gmc.agp_start) >> 18;
- pt_base = amdgpu_gmc_pd_addr(adev->gart.bo);
-
- if (adev->apu_flags & AMD_APU_IS_RAVEN2)
- /*
- * Raven2 has a HW issue that it is unable to use the vram which
- * is out of MC_VM_SYSTEM_APERTURE_HIGH_ADDR. So here is the
- * workaround that increase system aperture high address (add 1)
- * to get rid of the VM fault and hardware hang.
- */
- logical_addr_high = max((adev->gmc.fb_end >> 18) + 0x1, adev->gmc.agp_end >> 18);
- else
- logical_addr_high = max(adev->gmc.fb_end, adev->gmc.agp_end) >> 18;
-
agp_base = 0;
agp_bot = adev->gmc.agp_start >> 24;
agp_top = adev->gmc.agp_end >> 24;
+ /* AGP aperture is disabled */
+ if (agp_bot == agp_top) {
+ logical_addr_low = adev->gmc.vram_start >> 18;
+ if (adev->apu_flags & AMD_APU_IS_RAVEN2)
+ /*
+ * Raven2 has a HW issue that it is unable to use the vram which
+ * is out of MC_VM_SYSTEM_APERTURE_HIGH_ADDR. So here is the
+ * workaround that increase system aperture high address (add 1)
+ * to get rid of the VM fault and hardware hang.
+ */
+ logical_addr_high = (adev->gmc.fb_end >> 18) + 0x1;
+ else
+ logical_addr_high = adev->gmc.vram_end >> 18;
+ } else {
+ logical_addr_low = min(adev->gmc.fb_start, adev->gmc.agp_start) >> 18;
+ if (adev->apu_flags & AMD_APU_IS_RAVEN2)
+ /*
+ * Raven2 has a HW issue that it is unable to use the vram which
+ * is out of MC_VM_SYSTEM_APERTURE_HIGH_ADDR. So here is the
+ * workaround that increase system aperture high address (add 1)
+ * to get rid of the VM fault and hardware hang.
+ */
+ logical_addr_high = max((adev->gmc.fb_end >> 18) + 0x1, adev->gmc.agp_end >> 18);
+ else
+ logical_addr_high = max(adev->gmc.fb_end, adev->gmc.agp_end) >> 18;
+ }
+
+ pt_base = amdgpu_gmc_pd_addr(adev->gart.bo);
page_table_start.high_part = (u32)(adev->gmc.gart_start >> 44) & 0xF;
page_table_start.low_part = (u32)(adev->gmc.gart_start >> 12);
@@ -1513,6 +1527,9 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
}
break;
}
+ if (init_data.flags.gpu_vm_support &&
+ (amdgpu_sg_display == 0))
+ init_data.flags.gpu_vm_support = false;
if (init_data.flags.gpu_vm_support)
adev->mode_info.gpu_vm_support = true;
@@ -1730,10 +1747,6 @@ static void amdgpu_dm_fini(struct amdgpu_device *adev)
adev->dm.vblank_control_workqueue = NULL;
}
- for (i = 0; i < adev->dm.display_indexes_num; i++) {
- drm_encoder_cleanup(&adev->dm.mst_encoders[i].base);
- }
-
amdgpu_dm_destroy_drm_device(&adev->dm);
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
@@ -4361,6 +4374,10 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
amdgpu_set_panel_orientation(&aconnector->base);
}
+ /* If we didn't find a panel, notify the acpi video detection */
+ if (dm->adev->flags & AMD_IS_APU && dm->num_of_edps == 0)
+ acpi_video_report_nolcd();
+
/* Software is initialized. Now we can register interrupt handlers. */
switch (adev->asic_type) {
#if defined(CONFIG_DRM_AMD_DC_SI)
@@ -4503,6 +4520,17 @@ DEVICE_ATTR_WO(s3_debug);
static int dm_early_init(void *handle)
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+ struct amdgpu_mode_info *mode_info = &adev->mode_info;
+ struct atom_context *ctx = mode_info->atom_context;
+ int index = GetIndexIntoMasterTable(DATA, Object_Header);
+ u16 data_offset;
+
+ /* if there is no object header, skip DM */
+ if (!amdgpu_atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset)) {
+ adev->harvest_ip_mask |= AMD_HARVEST_IP_DMU_MASK;
+ dev_info(adev->dev, "No object header, skipping DM\n");
+ return -ENOENT;
+ }
switch (adev->asic_type) {
#if defined(CONFIG_DRM_AMD_DC_SI)
@@ -5307,8 +5335,6 @@ static void fill_stream_properties_from_drm_display_mode(
timing_out->aspect_ratio = get_aspect_ratio(mode_in);
- stream->output_color_space = get_output_color_space(timing_out);
-
stream->out_transfer_func->type = TF_TYPE_PREDEFINED;
stream->out_transfer_func->tf = TRANSFER_FUNCTION_SRGB;
if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) {
@@ -5319,6 +5345,8 @@ static void fill_stream_properties_from_drm_display_mode(
adjust_colour_depth_from_display_info(timing_out, info);
}
}
+
+ stream->output_color_space = get_output_color_space(timing_out);
}
static void fill_audio_info(struct audio_info *audio_info,
@@ -5831,7 +5859,8 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector,
*/
DRM_DEBUG_DRIVER("No preferred mode found\n");
} else {
- recalculate_timing = is_freesync_video_mode(&mode, aconnector);
+ recalculate_timing = amdgpu_freesync_vid_mode &&
+ is_freesync_video_mode(&mode, aconnector);
if (recalculate_timing) {
freesync_mode = get_highest_refresh_rate_mode(aconnector, false);
drm_mode_copy(&saved_mode, &mode);
@@ -6982,7 +7011,7 @@ static void amdgpu_dm_connector_add_freesync_modes(struct drm_connector *connect
struct amdgpu_dm_connector *amdgpu_dm_connector =
to_amdgpu_dm_connector(connector);
- if (!edid)
+ if (!(amdgpu_freesync_vid_mode && edid))
return;
if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
@@ -8846,7 +8875,8 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
* TODO: Refactor this function to allow this check to work
* in all conditions.
*/
- if (dm_new_crtc_state->stream &&
+ if (amdgpu_freesync_vid_mode &&
+ dm_new_crtc_state->stream &&
is_timing_unchanged_for_freesync(new_crtc_state, old_crtc_state))
goto skip_modeset;
@@ -8881,7 +8911,14 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
if (!dm_old_crtc_state->stream)
goto skip_modeset;
- if (dm_new_crtc_state->stream &&
+ /* Unset freesync video if it was active before */
+ if (dm_old_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED) {
+ dm_new_crtc_state->freesync_config.state = VRR_STATE_INACTIVE;
+ dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = 0;
+ }
+
+ /* Now check if we should set freesync video mode */
+ if (amdgpu_freesync_vid_mode && dm_new_crtc_state->stream &&
is_timing_unchanged_for_freesync(new_crtc_state,
old_crtc_state)) {
new_crtc_state->mode_changed = false;
@@ -8893,7 +8930,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
set_freesync_fixed_config(dm_new_crtc_state);
goto skip_modeset;
- } else if (aconnector &&
+ } else if (amdgpu_freesync_vid_mode && aconnector &&
is_freesync_video_mode(&new_crtc_state->mode,
aconnector)) {
struct drm_display_mode *high_mode;
@@ -9497,6 +9534,8 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
bool lock_and_validation_needed = false;
struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
#if defined(CONFIG_DRM_AMD_DC_DCN)
+ struct drm_dp_mst_topology_mgr *mgr;
+ struct drm_dp_mst_topology_state *mst_state;
struct dsc_mst_fairness_vars vars[MAX_PIPES];
#endif
@@ -9524,8 +9563,8 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
goto fail;
}
- if (dm_old_con_state->abm_level !=
- dm_new_con_state->abm_level)
+ if (dm_old_con_state->abm_level != dm_new_con_state->abm_level ||
+ dm_old_con_state->scaling != dm_new_con_state->scaling)
new_crtc_state->connectors_changed = true;
}
@@ -9745,6 +9784,28 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
lock_and_validation_needed = true;
}
+#if defined(CONFIG_DRM_AMD_DC_DCN)
+ /* set the slot info for each mst_state based on the link encoding format */
+ for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
+ struct amdgpu_dm_connector *aconnector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter iter;
+ u8 link_coding_cap;
+
+ drm_connector_list_iter_begin(dev, &iter);
+ drm_for_each_connector_iter(connector, &iter) {
+ if (connector->index == mst_state->mgr->conn_base_id) {
+ aconnector = to_amdgpu_dm_connector(connector);
+ link_coding_cap = dc_link_dp_mst_decide_link_encoding_format(aconnector->dc_link);
+ drm_dp_mst_update_slots(mst_state, link_coding_cap);
+
+ break;
+ }
+ }
+ drm_connector_list_iter_end(&iter);
+ }
+#endif
+
/**
* Streams and planes are reset when there are changes that affect
* bandwidth. Anything that affects bandwidth needs to go through
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 6994c9a1ed8588..5cff56bb8f5602 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
@@ -120,23 +120,50 @@ enum dc_edid_status dm_helpers_parse_edid_caps(
}
static void
-fill_dc_mst_payload_table_from_drm(struct drm_dp_mst_topology_state *mst_state,
- struct amdgpu_dm_connector *aconnector,
+fill_dc_mst_payload_table_from_drm(struct dc_link *link,
+ bool enable,
+ struct drm_dp_mst_atomic_payload *target_payload,
struct dc_dp_mst_stream_allocation_table *table)
{
struct dc_dp_mst_stream_allocation_table new_table = { 0 };
struct dc_dp_mst_stream_allocation *sa;
- struct drm_dp_mst_atomic_payload *payload;
+ struct link_mst_stream_allocation_table copy_of_link_table =
+ link->mst_stream_alloc_table;
+
+ int i;
+ int current_hw_table_stream_cnt = copy_of_link_table.stream_count;
+ struct link_mst_stream_allocation *dc_alloc;
+
+ /* TODO: refactor to set link->mst_stream_alloc_table directly if possible.*/
+ if (enable) {
+ dc_alloc =
+ &copy_of_link_table.stream_allocations[current_hw_table_stream_cnt];
+ dc_alloc->vcp_id = target_payload->vcpi;
+ dc_alloc->slot_count = target_payload->time_slots;
+ } else {
+ for (i = 0; i < copy_of_link_table.stream_count; i++) {
+ dc_alloc =
+ &copy_of_link_table.stream_allocations[i];
+
+ if (dc_alloc->vcp_id == target_payload->vcpi) {
+ dc_alloc->vcp_id = 0;
+ dc_alloc->slot_count = 0;
+ break;
+ }
+ }
+ ASSERT(i != copy_of_link_table.stream_count);
+ }
/* Fill payload info*/
- list_for_each_entry(payload, &mst_state->payloads, next) {
- if (payload->delete)
- continue;
-
- sa = &new_table.stream_allocations[new_table.stream_count];
- sa->slot_count = payload->time_slots;
- sa->vcp_id = payload->vcpi;
- new_table.stream_count++;
+ for (i = 0; i < MAX_CONTROLLER_NUM; i++) {
+ dc_alloc =
+ &copy_of_link_table.stream_allocations[i];
+ if (dc_alloc->vcp_id > 0 && dc_alloc->slot_count > 0) {
+ sa = &new_table.stream_allocations[new_table.stream_count];
+ sa->slot_count = dc_alloc->slot_count;
+ sa->vcp_id = dc_alloc->vcp_id;
+ new_table.stream_count++;
+ }
}
/* Overwrite the old table */
@@ -185,7 +212,7 @@ bool dm_helpers_dp_mst_write_payload_allocation_table(
* AUX message. The sequence is slot 1-63 allocated sequence for each
* stream. AMD ASIC stream slot allocation should follow the same
* sequence. copy DRM MST allocation to dc */
- fill_dc_mst_payload_table_from_drm(mst_state, aconnector, proposed_table);
+ fill_dc_mst_payload_table_from_drm(stream->link, enable, payload, proposed_table);
return true;
}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index 1edf7385f8d89a..abdbd4352f6f3c 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -468,7 +468,6 @@ static const struct drm_connector_helper_funcs dm_dp_mst_connector_helper_funcs
static void amdgpu_dm_encoder_destroy(struct drm_encoder *encoder)
{
drm_encoder_cleanup(encoder);
- kfree(encoder);
}
static const struct drm_encoder_funcs amdgpu_dm_encoder_funcs = {
@@ -904,11 +903,6 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_state *state,
if (IS_ERR(mst_state))
return PTR_ERR(mst_state);
- mst_state->pbn_div = dm_mst_get_pbn_divider(dc_link);
-#if defined(CONFIG_DRM_AMD_DC_DCN)
- drm_dp_mst_update_slots(mst_state, dc_link_dp_mst_decide_link_encoding_format(dc_link));
-#endif
-
/* Set up params */
for (i = 0; i < dc_state->stream_count; i++) {
struct dc_dsc_policy dsc_policy = {0};
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
index 471078fc3900d7..652270a0b498ce 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c
@@ -90,8 +90,8 @@ static const struct out_csc_color_matrix_type output_csc_matrix[] = {
{ 0xE00, 0xF349, 0xFEB7, 0x1000, 0x6CE, 0x16E3,
0x24F, 0x200, 0xFCCB, 0xF535, 0xE00, 0x1000} },
{ COLOR_SPACE_YCBCR2020_TYPE,
- { 0x1000, 0xF149, 0xFEB7, 0x0000, 0x0868, 0x15B2,
- 0x01E6, 0x0000, 0xFB88, 0xF478, 0x1000, 0x0000} },
+ { 0x1000, 0xF149, 0xFEB7, 0x1004, 0x0868, 0x15B2,
+ 0x01E6, 0x201, 0xFB88, 0xF478, 0x1000, 0x1004} },
{ COLOR_SPACE_YCBCR709_BLACK_TYPE,
{ 0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000,
0x0000, 0x0200, 0x0000, 0x0000, 0x0000, 0x1000} },
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c
index 342e906ae26e41..c88f044666fee6 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c
@@ -3995,10 +3995,13 @@ static enum dc_status deallocate_mst_payload(struct pipe_ctx *pipe_ctx)
struct fixed31_32 avg_time_slots_per_mtp = dc_fixpt_from_int(0);
int i;
bool mst_mode = (link->type == dc_connection_mst_branch);
+ /* adjust for drm changes*/
+ bool update_drm_mst_state = true;
const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res);
const struct dc_link_settings empty_link_settings = {0};
DC_LOGGER_INIT(link->ctx->logger);
+
/* deallocate_mst_payload is called before disable link. When mode or
* disable/enable monitor, new stream is created which is not in link
* stream[] yet. For this, payload is not allocated yet, so de-alloc
@@ -4014,7 +4017,7 @@ static enum dc_status deallocate_mst_payload(struct pipe_ctx *pipe_ctx)
&empty_link_settings,
avg_time_slots_per_mtp);
- if (mst_mode) {
+ if (mst_mode || update_drm_mst_state) {
/* when link is in mst mode, reply on mst manager to remove
* payload
*/
@@ -4077,11 +4080,18 @@ static enum dc_status deallocate_mst_payload(struct pipe_ctx *pipe_ctx)
stream->ctx,
stream);
+ if (!update_drm_mst_state)
+ dm_helpers_dp_mst_send_payload_allocation(
+ stream->ctx,
+ stream,
+ false);
+ }
+
+ if (update_drm_mst_state)
dm_helpers_dp_mst_send_payload_allocation(
stream->ctx,
stream,
false);
- }
return DC_OK;
}
diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c
index fe2023f18b7d0b..8f894c1d1d1eb1 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c
@@ -3626,7 +3626,7 @@ void dcn10_set_cursor_position(struct pipe_ctx *pipe_ctx)
(int)hubp->curs_attr.width || pos_cpy.x
<= (int)hubp->curs_attr.width +
pipe_ctx->plane_state->src_rect.x) {
- pos_cpy.x = temp_x + viewport_width;
+ pos_cpy.x = 2 * viewport_width - temp_x;
}
}
} else {
diff --git a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c
index f9ea1e86707f2a..79850a68f62ab0 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c
@@ -874,8 +874,9 @@ static const struct dc_plane_cap plane_cap = {
},
// 6:1 downscaling ratio: 1000/6 = 166.666
+ // 4:1 downscaling ratio for ARGB888 to prevent underflow during P010 playback: 1000/4 = 250
.max_downscale_factor = {
- .argb8888 = 167,
+ .argb8888 = 250,
.nv12 = 167,
.fp16 = 167
},
@@ -1763,7 +1764,7 @@ static bool dcn314_resource_construct(
pool->base.underlay_pipe_index = NO_UNDERLAY_PIPE;
pool->base.pipe_count = pool->base.res_cap->num_timing_generator;
pool->base.mpcc_count = pool->base.res_cap->num_timing_generator;
- dc->caps.max_downscale_ratio = 600;
+ dc->caps.max_downscale_ratio = 400;
dc->caps.i2c_speed_in_khz = 100;
dc->caps.i2c_speed_in_khz_hdcp = 100;
dc->caps.max_cursor_size = 256;
diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_init.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_init.c
index dc464945856742..a4e9fd5307c63a 100644
--- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_init.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_init.c
@@ -94,7 +94,7 @@ static const struct hw_sequencer_funcs dcn32_funcs = {
.get_vupdate_offset_from_vsync = dcn10_get_vupdate_offset_from_vsync,
.calc_vupdate_position = dcn10_calc_vupdate_position,
.apply_idle_power_optimizations = dcn32_apply_idle_power_optimizations,
- .does_plane_fit_in_mall = dcn30_does_plane_fit_in_mall,
+ .does_plane_fit_in_mall = NULL,
.set_backlight_level = dcn21_set_backlight_level,
.set_abm_immediate_disable = dcn21_set_abm_immediate_disable,
.hardware_release = dcn30_hardware_release,
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c
index 950669f2c10d77..cb7c0c87842300 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c
@@ -3183,7 +3183,7 @@ static void DISPCLKDPPCLKDCFCLKDeepSleepPrefetchParametersWatermarksAndPerforman
} else {
v->MIN_DST_Y_NEXT_START[k] = v->VTotal[k] - v->VFrontPorch[k] + v->VTotal[k] - v->VActive[k] - v->VStartup[k];
}
- v->MIN_DST_Y_NEXT_START[k] += dml_floor(4.0 * v->TSetup[k] / (double)v->HTotal[k] / v->PixelClock[k], 1.0) / 4.0;
+ v->MIN_DST_Y_NEXT_START[k] += dml_floor(4.0 * v->TSetup[k] / ((double)v->HTotal[k] / v->PixelClock[k]), 1.0) / 4.0;
if (((v->VUpdateOffsetPix[k] + v->VUpdateWidthPix[k] + v->VReadyOffsetPix[k]) / v->HTotal[k])
<= (isInterlaceTiming ?
dml_floor((v->VTotal[k] - v->VActive[k] - v->VFrontPorch[k] - v->VStartup[k]) / 2.0, 1.0) :
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c
index 5af601cff1a0f7..b53feeaf5cf117 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/display_mode_vba_util_32.c
@@ -6257,12 +6257,12 @@ bool dml32_CalculateDETSwathFillLatencyHiding(unsigned int NumberOfActiveSurface
double SwathSizePerSurfaceC[DC__NUM_DPP__MAX];
bool NotEnoughDETSwathFillLatencyHiding = false;
- /* calculate sum of single swath size for all pipes in bytes*/
+ /* calculate sum of single swath size for all pipes in bytes */
for (k = 0; k < NumberOfActiveSurfaces; k++) {
- SwathSizePerSurfaceY[k] += SwathHeightY[k] * SwathWidthY[k] * BytePerPixelInDETY[k] * NumOfDPP[k];
+ SwathSizePerSurfaceY[k] = SwathHeightY[k] * SwathWidthY[k] * BytePerPixelInDETY[k] * NumOfDPP[k];
if (SwathHeightC[k] != 0)
- SwathSizePerSurfaceC[k] += SwathHeightC[k] * SwathWidthC[k] * BytePerPixelInDETC[k] * NumOfDPP[k];
+ SwathSizePerSurfaceC[k] = SwathHeightC[k] * SwathWidthC[k] * BytePerPixelInDETC[k] * NumOfDPP[k];
else
SwathSizePerSurfaceC[k] = 0;
diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c
index 4a122925c3ae96..92c18bfb98b3ba 100644
--- a/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c
+++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c
@@ -532,6 +532,9 @@ enum dmub_status dmub_srv_hw_init(struct dmub_srv *dmub,
if (dmub->hw_funcs.reset)
dmub->hw_funcs.reset(dmub);
+ /* reset the cache of the last wptr as well now that hw is reset */
+ dmub->inbox1_last_wptr = 0;
+
cw0.offset.quad_part = inst_fb->gpu_addr;
cw0.region.base = DMUB_CW0_BASE;
cw0.region.top = cw0.region.base + inst_fb->size - 1;
@@ -649,6 +652,15 @@ enum dmub_status dmub_srv_hw_reset(struct dmub_srv *dmub)
if (dmub->hw_funcs.reset)
dmub->hw_funcs.reset(dmub);
+ /* mailboxes have been reset in hw, so reset the sw state as well */
+ dmub->inbox1_last_wptr = 0;
+ dmub->inbox1_rb.wrpt = 0;
+ dmub->inbox1_rb.rptr = 0;
+ dmub->outbox0_rb.wrpt = 0;
+ dmub->outbox0_rb.rptr = 0;
+ dmub->outbox1_rb.wrpt = 0;
+ dmub->outbox1_rb.rptr = 0;
+
dmub->hw_init = false;
return DMUB_STATUS_OK;
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
index 236657eece4772..2f3e239e623dcf 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
@@ -1991,6 +1991,8 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_
case IP_VERSION(9, 4, 2):
case IP_VERSION(10, 3, 0):
case IP_VERSION(11, 0, 0):
+ case IP_VERSION(11, 0, 1):
+ case IP_VERSION(11, 0, 2):
*states = ATTR_STATE_SUPPORTED;
break;
default:
@@ -2007,14 +2009,16 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_
gc_ver == IP_VERSION(10, 3, 0) ||
gc_ver == IP_VERSION(10, 1, 2) ||
gc_ver == IP_VERSION(11, 0, 0) ||
- gc_ver == IP_VERSION(11, 0, 2)))
+ gc_ver == IP_VERSION(11, 0, 2) ||
+ gc_ver == IP_VERSION(11, 0, 3)))
*states = ATTR_STATE_UNSUPPORTED;
} else if (DEVICE_ATTR_IS(pp_dpm_dclk)) {
if (!(gc_ver == IP_VERSION(10, 3, 1) ||
gc_ver == IP_VERSION(10, 3, 0) ||
gc_ver == IP_VERSION(10, 1, 2) ||
gc_ver == IP_VERSION(11, 0, 0) ||
- gc_ver == IP_VERSION(11, 0, 2)))
+ gc_ver == IP_VERSION(11, 0, 2) ||
+ gc_ver == IP_VERSION(11, 0, 3)))
*states = ATTR_STATE_UNSUPPORTED;
} else if (DEVICE_ATTR_IS(pp_power_profile_mode)) {
if (amdgpu_dpm_get_power_profile_mode(adev, NULL) == -EOPNOTSUPP)
diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
index ca3beb5d8f2768..6ab15502359265 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
@@ -1500,6 +1500,20 @@ static int smu_disable_dpms(struct smu_context *smu)
}
/*
+ * For SMU 13.0.4/11, PMFW will handle the features disablement properly
+ * for gpu reset case. Driver involvement is unnecessary.
+ */
+ if (amdgpu_in_reset(adev)) {
+ switch (adev->ip_versions[MP1_HWIP][0]) {
+ case IP_VERSION(13, 0, 4):
+ case IP_VERSION(13, 0, 11):
+ return 0;
+ default:
+ break;
+ }
+ }
+
+ /*
* For gpu reset, runpm and hibernation through BACO,
* BACO feature has to be kept enabled.
*/
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h
index d6b964cf73bd18..4bc7aee4d44f82 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h
@@ -123,7 +123,8 @@
(1 << FEATURE_DS_FCLK_BIT) | \
(1 << FEATURE_DS_LCLK_BIT) | \
(1 << FEATURE_DS_DCFCLK_BIT) | \
- (1 << FEATURE_DS_UCLK_BIT))
+ (1 << FEATURE_DS_UCLK_BIT) | \
+ (1ULL << FEATURE_DS_VCN_BIT))
//For use with feature control messages
typedef enum {
@@ -522,9 +523,9 @@ typedef enum {
TEMP_HOTSPOT_M,
TEMP_MEM,
TEMP_VR_GFX,
- TEMP_VR_SOC,
TEMP_VR_MEM0,
TEMP_VR_MEM1,
+ TEMP_VR_SOC,
TEMP_VR_U,
TEMP_LIQUID0,
TEMP_LIQUID1,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h
index d6b13933a98fbe..48a3a3952ceb35 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_7.h
@@ -113,20 +113,21 @@
#define NUM_FEATURES 64
#define ALLOWED_FEATURE_CTRL_DEFAULT 0xFFFFFFFFFFFFFFFFULL
-#define ALLOWED_FEATURE_CTRL_SCPM (1 << FEATURE_DPM_GFXCLK_BIT) | \
- (1 << FEATURE_DPM_GFX_POWER_OPTIMIZER_BIT) | \
- (1 << FEATURE_DPM_UCLK_BIT) | \
- (1 << FEATURE_DPM_FCLK_BIT) | \
- (1 << FEATURE_DPM_SOCCLK_BIT) | \
- (1 << FEATURE_DPM_MP0CLK_BIT) | \
- (1 << FEATURE_DPM_LINK_BIT) | \
- (1 << FEATURE_DPM_DCN_BIT) | \
- (1 << FEATURE_DS_GFXCLK_BIT) | \
- (1 << FEATURE_DS_SOCCLK_BIT) | \
- (1 << FEATURE_DS_FCLK_BIT) | \
- (1 << FEATURE_DS_LCLK_BIT) | \
- (1 << FEATURE_DS_DCFCLK_BIT) | \
- (1 << FEATURE_DS_UCLK_BIT)
+#define ALLOWED_FEATURE_CTRL_SCPM ((1 << FEATURE_DPM_GFXCLK_BIT) | \
+ (1 << FEATURE_DPM_GFX_POWER_OPTIMIZER_BIT) | \
+ (1 << FEATURE_DPM_UCLK_BIT) | \
+ (1 << FEATURE_DPM_FCLK_BIT) | \
+ (1 << FEATURE_DPM_SOCCLK_BIT) | \
+ (1 << FEATURE_DPM_MP0CLK_BIT) | \
+ (1 << FEATURE_DPM_LINK_BIT) | \
+ (1 << FEATURE_DPM_DCN_BIT) | \
+ (1 << FEATURE_DS_GFXCLK_BIT) | \
+ (1 << FEATURE_DS_SOCCLK_BIT) | \
+ (1 << FEATURE_DS_FCLK_BIT) | \
+ (1 << FEATURE_DS_LCLK_BIT) | \
+ (1 << FEATURE_DS_DCFCLK_BIT) | \
+ (1 << FEATURE_DS_UCLK_BIT) | \
+ (1ULL << FEATURE_DS_VCN_BIT))
//For use with feature control messages
typedef enum {
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h
index e8c6febb8b64ed..992163e66f7b40 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h
@@ -28,11 +28,11 @@
#define SMU13_DRIVER_IF_VERSION_INV 0xFFFFFFFF
#define SMU13_DRIVER_IF_VERSION_YELLOW_CARP 0x04
#define SMU13_DRIVER_IF_VERSION_ALDE 0x08
-#define SMU13_DRIVER_IF_VERSION_SMU_V13_0_0_0 0x34
+#define SMU13_DRIVER_IF_VERSION_SMU_V13_0_0_0 0x37
#define SMU13_DRIVER_IF_VERSION_SMU_V13_0_4 0x07
#define SMU13_DRIVER_IF_VERSION_SMU_V13_0_5 0x04
#define SMU13_DRIVER_IF_VERSION_SMU_V13_0_0_10 0x32
-#define SMU13_DRIVER_IF_VERSION_SMU_V13_0_7 0x35
+#define SMU13_DRIVER_IF_VERSION_SMU_V13_0_7 0x37
#define SMU13_DRIVER_IF_VERSION_SMU_V13_0_10 0x1D
#define SMU13_MODE1_RESET_WAIT_TIME_IN_MS 500 //500ms
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c
index 85e22210963fc7..5cdc07165480b5 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c
@@ -1171,6 +1171,7 @@ static int renoir_get_smu_metrics_data(struct smu_context *smu,
int ret = 0;
uint32_t apu_percent = 0;
uint32_t dgpu_percent = 0;
+ struct amdgpu_device *adev = smu->adev;
ret = smu_cmn_get_metrics_table(smu,
@@ -1196,7 +1197,11 @@ static int renoir_get_smu_metrics_data(struct smu_context *smu,
*value = metrics->AverageUvdActivity / 100;
break;
case METRICS_AVERAGE_SOCKETPOWER:
- *value = (metrics->CurrentSocketPower << 8) / 1000;
+ if (((adev->ip_versions[MP1_HWIP][0] == IP_VERSION(12, 0, 1)) && (adev->pm.fw_version >= 0x40000f)) ||
+ ((adev->ip_versions[MP1_HWIP][0] == IP_VERSION(12, 0, 0)) && (adev->pm.fw_version >= 0x373200)))
+ *value = metrics->CurrentSocketPower << 8;
+ else
+ *value = (metrics->CurrentSocketPower << 8) / 1000;
break;
case METRICS_TEMPERATURE_EDGE:
*value = (metrics->GfxTemperature / 100) *
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c
index e54b760b875bfe..b4373b6568ae6c 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c
@@ -1261,7 +1261,8 @@ int smu_v13_0_set_fan_speed_rpm(struct smu_context *smu,
uint32_t speed)
{
struct amdgpu_device *adev = smu->adev;
- uint32_t tach_period, crystal_clock_freq;
+ uint32_t crystal_clock_freq = 2500;
+ uint32_t tach_period;
int ret;
if (!speed)
@@ -1271,7 +1272,6 @@ int smu_v13_0_set_fan_speed_rpm(struct smu_context *smu,
if (ret)
return ret;
- crystal_clock_freq = amdgpu_asic_get_xclk(adev);
tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed);
WREG32_SOC15(THM, 0, regCG_TACH_CTRL,
REG_SET_FIELD(RREG32_SOC15(THM, 0, regCG_TACH_CTRL),
@@ -2298,6 +2298,10 @@ bool smu_v13_0_baco_is_support(struct smu_context *smu)
!smu_baco->platform_support)
return false;
+ /* return true if ASIC is in BACO state already */
+ if (smu_v13_0_baco_get_state(smu) == SMU_BACO_STATE_ENTER)
+ return true;
+
if (smu_cmn_feature_is_supported(smu, SMU_FEATURE_BACO_BIT) &&
!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_BACO_BIT))
return false;
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
index 9643b21c636a9e..508e392547d7ad 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
@@ -145,6 +145,7 @@ static struct cmn2asic_msg_mapping smu_v13_0_0_message_map[SMU_MSG_MAX_COUNT] =
MSG_MAP(SetBadMemoryPagesRetiredFlagsPerChannel,
PPSMC_MSG_SetBadMemoryPagesRetiredFlagsPerChannel, 0),
MSG_MAP(AllowGpo, PPSMC_MSG_SetGpoAllow, 0),
+ MSG_MAP(AllowIHHostInterrupt, PPSMC_MSG_AllowIHHostInterrupt, 0),
};
static struct cmn2asic_mapping smu_v13_0_0_clk_map[SMU_CLK_COUNT] = {
@@ -213,6 +214,7 @@ static struct cmn2asic_mapping smu_v13_0_0_feature_mask_map[SMU_FEATURE_COUNT] =
FEA_MAP(SOC_PCC),
[SMU_FEATURE_DPM_VCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
[SMU_FEATURE_DPM_DCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
+ [SMU_FEATURE_PPT_BIT] = {1, FEATURE_THROTTLERS_BIT},
};
static struct cmn2asic_mapping smu_v13_0_0_table_map[SMU_TABLE_COUNT] = {
@@ -405,6 +407,9 @@ static int smu_v13_0_0_setup_pptable(struct smu_context *smu)
struct amdgpu_device *adev = smu->adev;
int ret = 0;
+ if (amdgpu_sriov_vf(smu->adev))
+ return 0;
+
ret = smu_v13_0_0_get_pptable_from_pmfw(smu,
&smu_table->power_play_table,
&smu_table->power_play_table_size);
@@ -1255,6 +1260,9 @@ static int smu_v13_0_0_get_thermal_temperature_range(struct smu_context *smu,
table_context->power_play_table;
PPTable_t *pptable = smu->smu_table.driver_pptable;
+ if (amdgpu_sriov_vf(smu->adev))
+ return 0;
+
if (!range)
return -EINVAL;
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
index 5c6c6ad011ca63..9e1967d8049e3a 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
@@ -124,6 +124,7 @@ static struct cmn2asic_msg_mapping smu_v13_0_7_message_map[SMU_MSG_MAX_COUNT] =
MSG_MAP(DFCstateControl, PPSMC_MSG_SetExternalClientDfCstateAllow, 0),
MSG_MAP(ArmD3, PPSMC_MSG_ArmD3, 0),
MSG_MAP(AllowGpo, PPSMC_MSG_SetGpoAllow, 0),
+ MSG_MAP(GetPptLimit, PPSMC_MSG_GetPptLimit, 0),
};
static struct cmn2asic_mapping smu_v13_0_7_clk_map[SMU_CLK_COUNT] = {
@@ -192,6 +193,7 @@ static struct cmn2asic_mapping smu_v13_0_7_feature_mask_map[SMU_FEATURE_COUNT] =
FEA_MAP(SOC_PCC),
[SMU_FEATURE_DPM_VCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
[SMU_FEATURE_DPM_DCLK_BIT] = {1, FEATURE_MM_DPM_BIT},
+ [SMU_FEATURE_PPT_BIT] = {1, FEATURE_THROTTLERS_BIT},
};
static struct cmn2asic_mapping smu_v13_0_7_table_map[SMU_TABLE_COUNT] = {
diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c
index 51a46689cda70f..4ca37261584a94 100644
--- a/drivers/gpu/drm/display/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c
@@ -3372,6 +3372,9 @@ void drm_dp_remove_payload(struct drm_dp_mst_topology_mgr *mgr,
mgr->payload_count--;
mgr->next_start_slot -= payload->time_slots;
+
+ if (payload->delete)
+ drm_dp_mst_put_port_malloc(payload->port);
}
EXPORT_SYMBOL(drm_dp_remove_payload);
@@ -4327,7 +4330,6 @@ int drm_dp_atomic_release_time_slots(struct drm_atomic_state *state,
drm_dbg_atomic(mgr->dev, "[MST PORT:%p] TU %d -> 0\n", port, payload->time_slots);
if (!payload->delete) {
- drm_dp_mst_put_port_malloc(port);
payload->pbn = 0;
payload->delete = true;
topology_state->payload_mask &= ~BIT(payload->vcpi - 1);
diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c
index 11bb5939947180..3d1f50f481cfda 100644
--- a/drivers/gpu/drm/drm_buddy.c
+++ b/drivers/gpu/drm/drm_buddy.c
@@ -38,6 +38,25 @@ static void drm_block_free(struct drm_buddy *mm,
kmem_cache_free(slab_blocks, block);
}
+static void list_insert_sorted(struct drm_buddy *mm,
+ struct drm_buddy_block *block)
+{
+ struct drm_buddy_block *node;
+ struct list_head *head;
+
+ head = &mm->free_list[drm_buddy_block_order(block)];
+ if (list_empty(head)) {
+ list_add(&block->link, head);
+ return;
+ }
+
+ list_for_each_entry(node, head, link)
+ if (drm_buddy_block_offset(block) < drm_buddy_block_offset(node))
+ break;
+
+ __list_add(&block->link, node->link.prev, &node->link);
+}
+
static void mark_allocated(struct drm_buddy_block *block)
{
block->header &= ~DRM_BUDDY_HEADER_STATE;
@@ -52,8 +71,7 @@ static void mark_free(struct drm_buddy *mm,
block->header &= ~DRM_BUDDY_HEADER_STATE;
block->header |= DRM_BUDDY_FREE;
- list_add(&block->link,
- &mm->free_list[drm_buddy_block_order(block)]);
+ list_insert_sorted(mm, block);
}
static void mark_split(struct drm_buddy_block *block)
@@ -387,20 +405,26 @@ err_undo:
}
static struct drm_buddy_block *
-get_maxblock(struct list_head *head)
+get_maxblock(struct drm_buddy *mm, unsigned int order)
{
struct drm_buddy_block *max_block = NULL, *node;
+ unsigned int i;
- max_block = list_first_entry_or_null(head,
- struct drm_buddy_block,
- link);
- if (!max_block)
- return NULL;
+ for (i = order; i <= mm->max_order; ++i) {
+ if (!list_empty(&mm->free_list[i])) {
+ node = list_last_entry(&mm->free_list[i],
+ struct drm_buddy_block,
+ link);
+ if (!max_block) {
+ max_block = node;
+ continue;
+ }
- list_for_each_entry(node, head, link) {
- if (drm_buddy_block_offset(node) >
- drm_buddy_block_offset(max_block))
- max_block = node;
+ if (drm_buddy_block_offset(node) >
+ drm_buddy_block_offset(max_block)) {
+ max_block = node;
+ }
+ }
}
return max_block;
@@ -412,20 +436,23 @@ alloc_from_freelist(struct drm_buddy *mm,
unsigned long flags)
{
struct drm_buddy_block *block = NULL;
- unsigned int i;
+ unsigned int tmp;
int err;
- for (i = order; i <= mm->max_order; ++i) {
- if (flags & DRM_BUDDY_TOPDOWN_ALLOCATION) {
- block = get_maxblock(&mm->free_list[i]);
- if (block)
- break;
- } else {
- block = list_first_entry_or_null(&mm->free_list[i],
- struct drm_buddy_block,
- link);
- if (block)
- break;
+ if (flags & DRM_BUDDY_TOPDOWN_ALLOCATION) {
+ block = get_maxblock(mm, order);
+ if (block)
+ /* Store the obtained block order */
+ tmp = drm_buddy_block_order(block);
+ } else {
+ for (tmp = order; tmp <= mm->max_order; ++tmp) {
+ if (!list_empty(&mm->free_list[tmp])) {
+ block = list_last_entry(&mm->free_list[tmp],
+ struct drm_buddy_block,
+ link);
+ if (block)
+ break;
+ }
}
}
@@ -434,18 +461,18 @@ alloc_from_freelist(struct drm_buddy *mm,
BUG_ON(!drm_buddy_block_is_free(block));
- while (i != order) {
+ while (tmp != order) {
err = split_block(mm, block);
if (unlikely(err))
goto err_undo;
block = block->right;
- i--;
+ tmp--;
}
return block;
err_undo:
- if (i != order)
+ if (tmp != order)
__drm_buddy_free(mm, block);
return ERR_PTR(err);
}
diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index fd67efe37c6360..056ab9d5f313b0 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -233,21 +233,17 @@ void drm_client_dev_restore(struct drm_device *dev)
static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
{
- struct drm_device *dev = buffer->client->dev;
-
if (buffer->gem) {
drm_gem_vunmap_unlocked(buffer->gem, &buffer->map);
drm_gem_object_put(buffer->gem);
}
- if (buffer->handle)
- drm_mode_destroy_dumb(dev, buffer->handle, buffer->client->file);
-
kfree(buffer);
}
static struct drm_client_buffer *
-drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
+drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height,
+ u32 format, u32 *handle)
{
const struct drm_format_info *info = drm_format_info(format);
struct drm_mode_create_dumb dumb_args = { };
@@ -269,16 +265,15 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
if (ret)
goto err_delete;
- buffer->handle = dumb_args.handle;
- buffer->pitch = dumb_args.pitch;
-
obj = drm_gem_object_lookup(client->file, dumb_args.handle);
if (!obj) {
ret = -ENOENT;
goto err_delete;
}
+ buffer->pitch = dumb_args.pitch;
buffer->gem = obj;
+ *handle = dumb_args.handle;
return buffer;
@@ -365,7 +360,8 @@ static void drm_client_buffer_rmfb(struct drm_client_buffer *buffer)
}
static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
- u32 width, u32 height, u32 format)
+ u32 width, u32 height, u32 format,
+ u32 handle)
{
struct drm_client_dev *client = buffer->client;
struct drm_mode_fb_cmd fb_req = { };
@@ -377,7 +373,7 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
fb_req.depth = info->depth;
fb_req.width = width;
fb_req.height = height;
- fb_req.handle = buffer->handle;
+ fb_req.handle = handle;
fb_req.pitch = buffer->pitch;
ret = drm_mode_addfb(client->dev, &fb_req, client->file);
@@ -414,13 +410,24 @@ struct drm_client_buffer *
drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
{
struct drm_client_buffer *buffer;
+ u32 handle;
int ret;
- buffer = drm_client_buffer_create(client, width, height, format);
+ buffer = drm_client_buffer_create(client, width, height, format,
+ &handle);
if (IS_ERR(buffer))
return buffer;
- ret = drm_client_buffer_addfb(buffer, width, height, format);
+ ret = drm_client_buffer_addfb(buffer, width, height, format, handle);
+
+ /*
+ * The handle is only needed for creating the framebuffer, destroy it
+ * again to solve a circular dependency should anybody export the GEM
+ * object as DMA-buf. The framebuffer and our buffer structure are still
+ * holding references to the GEM object to prevent its destruction.
+ */
+ drm_mode_destroy_dumb(client->dev, handle, client->file);
+
if (ret) {
drm_client_buffer_delete(buffer);
return ERR_PTR(ret);
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index b3a731b9170a6a..0d0c26ebab9069 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -30,7 +30,9 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/console.h>
+#include <linux/pci.h>
#include <linux/sysrq.h>
+#include <linux/vga_switcheroo.h>
#include <drm/drm_atomic.h>
#include <drm/drm_drv.h>
@@ -1909,6 +1911,11 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
return ret;
strcpy(fb_helper->fb->comm, "[fbcon]");
+
+ /* Set the fb info for vgaswitcheroo clients. Does nothing otherwise. */
+ if (dev_is_pci(dev->dev))
+ vga_switcheroo_client_fb_set(to_pci_dev(dev->dev), fb_helper->info);
+
return 0;
}
diff --git a/drivers/gpu/drm/drm_fbdev_generic.c b/drivers/gpu/drm/drm_fbdev_generic.c
index ab869566927958..593aa3283792b6 100644
--- a/drivers/gpu/drm/drm_fbdev_generic.c
+++ b/drivers/gpu/drm/drm_fbdev_generic.c
@@ -171,11 +171,6 @@ static const struct fb_ops drm_fbdev_fb_ops = {
.fb_imageblit = drm_fbdev_fb_imageblit,
};
-static struct fb_deferred_io drm_fbdev_defio = {
- .delay = HZ / 20,
- .deferred_io = drm_fb_helper_deferred_io,
-};
-
/*
* This function uses the client API to create a framebuffer backed by a dumb buffer.
*/
@@ -222,8 +217,14 @@ static int drm_fbdev_fb_probe(struct drm_fb_helper *fb_helper,
return -ENOMEM;
fbi->flags |= FBINFO_VIRTFB | FBINFO_READS_FAST;
- fbi->fbdefio = &drm_fbdev_defio;
- fb_deferred_io_init(fbi);
+ /* Set a default deferred I/O handler */
+ fb_helper->fbdefio.delay = HZ / 20;
+ fb_helper->fbdefio.deferred_io = drm_fb_helper_deferred_io;
+
+ fbi->fbdefio = &fb_helper->fbdefio;
+ ret = fb_deferred_io_init(fbi);
+ if (ret)
+ return ret;
} else {
/* buffer is mapped for HW framebuffer */
ret = drm_client_buffer_vmap(fb_helper->buffer, &map);
diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c
index 52d8800a8ab86c..3659f0465a7241 100644
--- a/drivers/gpu/drm/drm_panel_orientation_quirks.c
+++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c
@@ -304,6 +304,12 @@ static const struct dmi_system_id orientation_data[] = {
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGM"),
},
.driver_data = (void *)&lcd1200x1920_rightside_up,
+ }, { /* Lenovo Ideapad D330-10IGL (HD) */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad D330-10IGL"),
+ },
+ .driver_data = (void *)&lcd800x1280_rightside_up,
}, { /* Lenovo Yoga Book X90F / X91F / X91L */
.matches = {
/* Non exact match to match all versions */
diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c
index 7de37f8c68fd0c..83229a031af0f8 100644
--- a/drivers/gpu/drm/drm_vma_manager.c
+++ b/drivers/gpu/drm/drm_vma_manager.c
@@ -240,27 +240,8 @@ void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
}
EXPORT_SYMBOL(drm_vma_offset_remove);
-/**
- * drm_vma_node_allow - Add open-file to list of allowed users
- * @node: Node to modify
- * @tag: Tag of file to remove
- *
- * Add @tag to the list of allowed open-files for this node. If @tag is
- * already on this list, the ref-count is incremented.
- *
- * The list of allowed-users is preserved across drm_vma_offset_add() and
- * drm_vma_offset_remove() calls. You may even call it if the node is currently
- * not added to any offset-manager.
- *
- * You must remove all open-files the same number of times as you added them
- * before destroying the node. Otherwise, you will leak memory.
- *
- * This is locked against concurrent access internally.
- *
- * RETURNS:
- * 0 on success, negative error code on internal failure (out-of-mem)
- */
-int drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *tag)
+static int vma_node_allow(struct drm_vma_offset_node *node,
+ struct drm_file *tag, bool ref_counted)
{
struct rb_node **iter;
struct rb_node *parent = NULL;
@@ -282,7 +263,8 @@ int drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *tag)
entry = rb_entry(*iter, struct drm_vma_offset_file, vm_rb);
if (tag == entry->vm_tag) {
- entry->vm_count++;
+ if (ref_counted)
+ entry->vm_count++;
goto unlock;
} else if (tag > entry->vm_tag) {
iter = &(*iter)->rb_right;
@@ -307,9 +289,59 @@ unlock:
kfree(new);
return ret;
}
+
+/**
+ * drm_vma_node_allow - Add open-file to list of allowed users
+ * @node: Node to modify
+ * @tag: Tag of file to remove
+ *
+ * Add @tag to the list of allowed open-files for this node. If @tag is
+ * already on this list, the ref-count is incremented.
+ *
+ * The list of allowed-users is preserved across drm_vma_offset_add() and
+ * drm_vma_offset_remove() calls. You may even call it if the node is currently
+ * not added to any offset-manager.
+ *
+ * You must remove all open-files the same number of times as you added them
+ * before destroying the node. Otherwise, you will leak memory.
+ *
+ * This is locked against concurrent access internally.
+ *
+ * RETURNS:
+ * 0 on success, negative error code on internal failure (out-of-mem)
+ */
+int drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *tag)
+{
+ return vma_node_allow(node, tag, true);
+}
EXPORT_SYMBOL(drm_vma_node_allow);
/**
+ * drm_vma_node_allow_once - Add open-file to list of allowed users
+ * @node: Node to modify
+ * @tag: Tag of file to remove
+ *
+ * Add @tag to the list of allowed open-files for this node.
+ *
+ * The list of allowed-users is preserved across drm_vma_offset_add() and
+ * drm_vma_offset_remove() calls. You may even call it if the node is currently
+ * not added to any offset-manager.
+ *
+ * This is not ref-counted unlike drm_vma_node_allow() hence drm_vma_node_revoke()
+ * should only be called once after this.
+ *
+ * This is locked against concurrent access internally.
+ *
+ * RETURNS:
+ * 0 on success, negative error code on internal failure (out-of-mem)
+ */
+int drm_vma_node_allow_once(struct drm_vma_offset_node *node, struct drm_file *tag)
+{
+ return vma_node_allow(node, tag, false);
+}
+EXPORT_SYMBOL(drm_vma_node_allow_once);
+
+/**
* drm_vma_node_revoke - Remove open-file from list of allowed users
* @node: Node to modify
* @tag: Tag of file to remove
diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c
index 572a4e3769f388..a491e6c38875d4 100644
--- a/drivers/gpu/drm/i915/display/intel_bios.c
+++ b/drivers/gpu/drm/i915/display/intel_bios.c
@@ -2466,6 +2466,22 @@ static enum port dvo_port_to_port(struct drm_i915_private *i915,
dvo_port);
}
+static enum port
+dsi_dvo_port_to_port(struct drm_i915_private *i915, u8 dvo_port)
+{
+ switch (dvo_port) {
+ case DVO_PORT_MIPIA:
+ return PORT_A;
+ case DVO_PORT_MIPIC:
+ if (DISPLAY_VER(i915) >= 11)
+ return PORT_B;
+ else
+ return PORT_C;
+ default:
+ return PORT_NONE;
+ }
+}
+
static int parse_bdb_230_dp_max_link_rate(const int vbt_max_link_rate)
{
switch (vbt_max_link_rate) {
@@ -3414,19 +3430,16 @@ bool intel_bios_is_dsi_present(struct drm_i915_private *i915,
dvo_port = child->dvo_port;
- if (dvo_port == DVO_PORT_MIPIA ||
- (dvo_port == DVO_PORT_MIPIB && DISPLAY_VER(i915) >= 11) ||
- (dvo_port == DVO_PORT_MIPIC && DISPLAY_VER(i915) < 11)) {
- if (port)
- *port = dvo_port - DVO_PORT_MIPIA;
- return true;
- } else if (dvo_port == DVO_PORT_MIPIB ||
- dvo_port == DVO_PORT_MIPIC ||
- dvo_port == DVO_PORT_MIPID) {
+ if (dsi_dvo_port_to_port(i915, dvo_port) == PORT_NONE) {
drm_dbg_kms(&i915->drm,
"VBT has unsupported DSI port %c\n",
port_name(dvo_port - DVO_PORT_MIPIA));
+ continue;
}
+
+ if (port)
+ *port = dsi_dvo_port_to_port(i915, dvo_port);
+ return true;
}
return false;
@@ -3511,7 +3524,7 @@ bool intel_bios_get_dsc_params(struct intel_encoder *encoder,
if (!(child->device_type & DEVICE_TYPE_MIPI_OUTPUT))
continue;
- if (child->dvo_port - DVO_PORT_MIPIA == encoder->port) {
+ if (dsi_dvo_port_to_port(i915, child->dvo_port) == encoder->port) {
if (!devdata->dsc)
return false;
diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c
index b74e36d7601386..407a477939e532 100644
--- a/drivers/gpu/drm/i915/display/intel_cdclk.c
+++ b/drivers/gpu/drm/i915/display/intel_cdclk.c
@@ -1319,7 +1319,7 @@ static const struct intel_cdclk_vals adlp_cdclk_table[] = {
{ .refclk = 24000, .cdclk = 192000, .divider = 2, .ratio = 16 },
{ .refclk = 24000, .cdclk = 312000, .divider = 2, .ratio = 26 },
{ .refclk = 24000, .cdclk = 552000, .divider = 2, .ratio = 46 },
- { .refclk = 24400, .cdclk = 648000, .divider = 2, .ratio = 54 },
+ { .refclk = 24000, .cdclk = 648000, .divider = 2, .ratio = 54 },
{ .refclk = 38400, .cdclk = 179200, .divider = 3, .ratio = 14 },
{ .refclk = 38400, .cdclk = 192000, .divider = 2, .ratio = 10 },
diff --git a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c
index fce69fa446d586..2cbc1292ab3821 100644
--- a/drivers/gpu/drm/i915/display/intel_dsi_vbt.c
+++ b/drivers/gpu/drm/i915/display/intel_dsi_vbt.c
@@ -41,9 +41,11 @@
#include "i915_drv.h"
#include "i915_reg.h"
+#include "intel_de.h"
#include "intel_display_types.h"
#include "intel_dsi.h"
#include "intel_dsi_vbt.h"
+#include "intel_gmbus_regs.h"
#include "vlv_dsi.h"
#include "vlv_dsi_regs.h"
#include "vlv_sideband.h"
@@ -377,6 +379,85 @@ static void icl_exec_gpio(struct intel_connector *connector,
drm_dbg_kms(&dev_priv->drm, "Skipping ICL GPIO element execution\n");
}
+enum {
+ MIPI_RESET_1 = 0,
+ MIPI_AVDD_EN_1,
+ MIPI_BKLT_EN_1,
+ MIPI_AVEE_EN_1,
+ MIPI_VIO_EN_1,
+ MIPI_RESET_2,
+ MIPI_AVDD_EN_2,
+ MIPI_BKLT_EN_2,
+ MIPI_AVEE_EN_2,
+ MIPI_VIO_EN_2,
+};
+
+static void icl_native_gpio_set_value(struct drm_i915_private *dev_priv,
+ int gpio, bool value)
+{
+ int index;
+
+ if (drm_WARN_ON(&dev_priv->drm, DISPLAY_VER(dev_priv) == 11 && gpio >= MIPI_RESET_2))
+ return;
+
+ switch (gpio) {
+ case MIPI_RESET_1:
+ case MIPI_RESET_2:
+ index = gpio == MIPI_RESET_1 ? HPD_PORT_A : HPD_PORT_B;
+
+ /*
+ * Disable HPD to set the pin to output, and set output
+ * value. The HPD pin should not be enabled for DSI anyway,
+ * assuming the board design and VBT are sane, and the pin isn't
+ * used by a non-DSI encoder.
+ *
+ * The locking protects against concurrent SHOTPLUG_CTL_DDI
+ * modifications in irq setup and handling.
+ */
+ spin_lock_irq(&dev_priv->irq_lock);
+ intel_de_rmw(dev_priv, SHOTPLUG_CTL_DDI,
+ SHOTPLUG_CTL_DDI_HPD_ENABLE(index) |
+ SHOTPLUG_CTL_DDI_HPD_OUTPUT_DATA(index),
+ value ? SHOTPLUG_CTL_DDI_HPD_OUTPUT_DATA(index) : 0);
+ spin_unlock_irq(&dev_priv->irq_lock);
+ break;
+ case MIPI_AVDD_EN_1:
+ case MIPI_AVDD_EN_2:
+ index = gpio == MIPI_AVDD_EN_1 ? 0 : 1;
+
+ intel_de_rmw(dev_priv, PP_CONTROL(index), PANEL_POWER_ON,
+ value ? PANEL_POWER_ON : 0);
+ break;
+ case MIPI_BKLT_EN_1:
+ case MIPI_BKLT_EN_2:
+ index = gpio == MIPI_BKLT_EN_1 ? 0 : 1;
+
+ intel_de_rmw(dev_priv, PP_CONTROL(index), EDP_BLC_ENABLE,
+ value ? EDP_BLC_ENABLE : 0);
+ break;
+ case MIPI_AVEE_EN_1:
+ case MIPI_AVEE_EN_2:
+ index = gpio == MIPI_AVEE_EN_1 ? 1 : 2;
+
+ intel_de_rmw(dev_priv, GPIO(dev_priv, index),
+ GPIO_CLOCK_VAL_OUT,
+ GPIO_CLOCK_DIR_MASK | GPIO_CLOCK_DIR_OUT |
+ GPIO_CLOCK_VAL_MASK | (value ? GPIO_CLOCK_VAL_OUT : 0));
+ break;
+ case MIPI_VIO_EN_1:
+ case MIPI_VIO_EN_2:
+ index = gpio == MIPI_VIO_EN_1 ? 1 : 2;
+
+ intel_de_rmw(dev_priv, GPIO(dev_priv, index),
+ GPIO_DATA_VAL_OUT,
+ GPIO_DATA_DIR_MASK | GPIO_DATA_DIR_OUT |
+ GPIO_DATA_VAL_MASK | (value ? GPIO_DATA_VAL_OUT : 0));
+ break;
+ default:
+ MISSING_CASE(gpio);
+ }
+}
+
static const u8 *mipi_exec_gpio(struct intel_dsi *intel_dsi, const u8 *data)
{
struct drm_device *dev = intel_dsi->base.base.dev;
@@ -384,8 +465,7 @@ static const u8 *mipi_exec_gpio(struct intel_dsi *intel_dsi, const u8 *data)
struct intel_connector *connector = intel_dsi->attached_connector;
u8 gpio_source, gpio_index = 0, gpio_number;
bool value;
-
- drm_dbg_kms(&dev_priv->drm, "\n");
+ bool native = DISPLAY_VER(dev_priv) >= 11;
if (connector->panel.vbt.dsi.seq_version >= 3)
gpio_index = *data++;
@@ -398,10 +478,18 @@ static const u8 *mipi_exec_gpio(struct intel_dsi *intel_dsi, const u8 *data)
else
gpio_source = 0;
+ if (connector->panel.vbt.dsi.seq_version >= 4 && *data & BIT(1))
+ native = false;
+
/* pull up/down */
value = *data++ & 1;
- if (DISPLAY_VER(dev_priv) >= 11)
+ drm_dbg_kms(&dev_priv->drm, "GPIO index %u, number %u, source %u, native %s, set to %s\n",
+ gpio_index, gpio_number, gpio_source, str_yes_no(native), str_on_off(value));
+
+ if (native)
+ icl_native_gpio_set_value(dev_priv, gpio_number, value);
+ else if (DISPLAY_VER(dev_priv) >= 11)
icl_exec_gpio(connector, gpio_source, gpio_index, value);
else if (IS_VALLEYVIEW(dev_priv))
vlv_exec_gpio(connector, gpio_source, gpio_number, value);
diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c
index 5575d7abdc0926..f76c06b7f1d4a5 100644
--- a/drivers/gpu/drm/i915/display/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/display/intel_fbdev.c
@@ -328,8 +328,20 @@ out_unlock:
return ret;
}
+static int intelfb_dirty(struct drm_fb_helper *helper, struct drm_clip_rect *clip)
+{
+ if (!(clip->x1 < clip->x2 && clip->y1 < clip->y2))
+ return 0;
+
+ if (helper->fb->funcs->dirty)
+ return helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, clip, 1);
+
+ return 0;
+}
+
static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {
.fb_probe = intelfb_create,
+ .fb_dirty = intelfb_dirty,
};
static void intel_fbdev_destroy(struct intel_fbdev *ifbdev)
diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c
index 76490cc59d8f11..7d07fa3123ece7 100644
--- a/drivers/gpu/drm/i915/display/skl_universal_plane.c
+++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c
@@ -1627,7 +1627,7 @@ static int skl_check_main_surface(struct intel_plane_state *plane_state)
u32 offset;
int ret;
- if (w > max_width || w < min_width || h > max_height) {
+ if (w > max_width || w < min_width || h > max_height || h < 1) {
drm_dbg_kms(&dev_priv->drm,
"requested Y/RGB source size %dx%d outside limits (min: %dx1 max: %dx%d)\n",
w, h, min_width, max_width, max_height);
diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c
index e0766d1be966ff..11554645e6eed7 100644
--- a/drivers/gpu/drm/i915/display/skl_watermark.c
+++ b/drivers/gpu/drm/i915/display/skl_watermark.c
@@ -1587,7 +1587,8 @@ skl_crtc_allocate_plane_ddb(struct intel_atomic_state *state,
skl_check_wm_level(&wm->wm[level], ddb);
if (icl_need_wm1_wa(i915, plane_id) &&
- level == 1 && wm->wm[0].enable) {
+ level == 1 && !wm->wm[level].enable &&
+ wm->wm[0].enable) {
wm->wm[level].blocks = wm->wm[0].blocks;
wm->wm[level].lines = wm->wm[0].lines;
wm->wm[level].ignore_lines = wm->wm[0].ignore_lines;
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_context.c b/drivers/gpu/drm/i915/gem/i915_gem_context.c
index 7f2831efc798b9..e4b78ab4773b1f 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_context.c
@@ -1688,6 +1688,10 @@ void i915_gem_init__contexts(struct drm_i915_private *i915)
init_contexts(&i915->gem.contexts);
}
+/*
+ * Note that this implicitly consumes the ctx reference, by placing
+ * the ctx in the context_xa.
+ */
static void gem_context_register(struct i915_gem_context *ctx,
struct drm_i915_file_private *fpriv,
u32 id)
@@ -1703,10 +1707,6 @@ static void gem_context_register(struct i915_gem_context *ctx,
snprintf(ctx->name, sizeof(ctx->name), "%s[%d]",
current->comm, pid_nr(ctx->pid));
- /* And finally expose ourselves to userspace via the idr */
- old = xa_store(&fpriv->context_xa, id, ctx, GFP_KERNEL);
- WARN_ON(old);
-
spin_lock(&ctx->client->ctx_lock);
list_add_tail_rcu(&ctx->client_link, &ctx->client->ctx_list);
spin_unlock(&ctx->client->ctx_lock);
@@ -1714,6 +1714,10 @@ static void gem_context_register(struct i915_gem_context *ctx,
spin_lock(&i915->gem.contexts.lock);
list_add_tail(&ctx->link, &i915->gem.contexts.list);
spin_unlock(&i915->gem.contexts.lock);
+
+ /* And finally expose ourselves to userspace via the idr */
+ old = xa_store(&fpriv->context_xa, id, ctx, GFP_KERNEL);
+ WARN_ON(old);
}
int i915_gem_context_open(struct drm_i915_private *i915,
@@ -1857,11 +1861,19 @@ static int get_ppgtt(struct drm_i915_file_private *file_priv,
vm = ctx->vm;
GEM_BUG_ON(!vm);
+ /*
+ * Get a reference for the allocated handle. Once the handle is
+ * visible in the vm_xa table, userspace could try to close it
+ * from under our feet, so we need to hold the extra reference
+ * first.
+ */
+ i915_vm_get(vm);
+
err = xa_alloc(&file_priv->vm_xa, &id, vm, xa_limit_32b, GFP_KERNEL);
- if (err)
+ if (err) {
+ i915_vm_put(vm);
return err;
-
- i915_vm_get(vm);
+ }
GEM_BUG_ON(id == 0); /* reserved for invalid/unassigned ppgtt */
args->value = id;
@@ -2199,14 +2211,22 @@ finalize_create_context_locked(struct drm_i915_file_private *file_priv,
if (IS_ERR(ctx))
return ctx;
+ /*
+ * One for the xarray and one for the caller. We need to grab
+ * the reference *prior* to making the ctx visble to userspace
+ * in gem_context_register(), as at any point after that
+ * userspace can try to race us with another thread destroying
+ * the context under our feet.
+ */
+ i915_gem_context_get(ctx);
+
gem_context_register(ctx, file_priv, id);
old = xa_erase(&file_priv->proto_context_xa, id);
GEM_BUG_ON(old != pc);
proto_context_close(file_priv->dev_priv, pc);
- /* One for the xarray and one for the caller */
- return i915_gem_context_get(ctx);
+ return ctx;
}
struct i915_gem_context *
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index da09767fda0706..0f2e056c02ddf1 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -730,37 +730,74 @@ static int eb_reserve(struct i915_execbuffer *eb)
bool unpinned;
/*
- * Attempt to pin all of the buffers into the GTT.
- * This is done in 2 phases:
+ * We have one more buffers that we couldn't bind, which could be due to
+ * various reasons. To resolve this we have 4 passes, with every next
+ * level turning the screws tighter:
*
- * 1. Unbind all objects that do not match the GTT constraints for
- * the execbuffer (fenceable, mappable, alignment etc).
- * 2. Bind new objects.
+ * 0. Unbind all objects that do not match the GTT constraints for the
+ * execbuffer (fenceable, mappable, alignment etc). Bind all new
+ * objects. This avoids unnecessary unbinding of later objects in order
+ * to make room for the earlier objects *unless* we need to defragment.
*
- * This avoid unnecessary unbinding of later objects in order to make
- * room for the earlier objects *unless* we need to defragment.
+ * 1. Reorder the buffers, where objects with the most restrictive
+ * placement requirements go first (ignoring fixed location buffers for
+ * now). For example, objects needing the mappable aperture (the first
+ * 256M of GTT), should go first vs objects that can be placed just
+ * about anywhere. Repeat the previous pass.
*
- * Defragmenting is skipped if all objects are pinned at a fixed location.
+ * 2. Consider buffers that are pinned at a fixed location. Also try to
+ * evict the entire VM this time, leaving only objects that we were
+ * unable to lock. Try again to bind the buffers. (still using the new
+ * buffer order).
+ *
+ * 3. We likely have object lock contention for one or more stubborn
+ * objects in the VM, for which we need to evict to make forward
+ * progress (perhaps we are fighting the shrinker?). When evicting the
+ * VM this time around, anything that we can't lock we now track using
+ * the busy_bo, using the full lock (after dropping the vm->mutex to
+ * prevent deadlocks), instead of trylock. We then continue to evict the
+ * VM, this time with the stubborn object locked, which we can now
+ * hopefully unbind (if still bound in the VM). Repeat until the VM is
+ * evicted. Finally we should be able bind everything.
*/
- for (pass = 0; pass <= 2; pass++) {
+ for (pass = 0; pass <= 3; pass++) {
int pin_flags = PIN_USER | PIN_VALIDATE;
if (pass == 0)
pin_flags |= PIN_NONBLOCK;
if (pass >= 1)
- unpinned = eb_unbind(eb, pass == 2);
+ unpinned = eb_unbind(eb, pass >= 2);
if (pass == 2) {
err = mutex_lock_interruptible(&eb->context->vm->mutex);
if (!err) {
- err = i915_gem_evict_vm(eb->context->vm, &eb->ww);
+ err = i915_gem_evict_vm(eb->context->vm, &eb->ww, NULL);
mutex_unlock(&eb->context->vm->mutex);
}
if (err)
return err;
}
+ if (pass == 3) {
+retry:
+ err = mutex_lock_interruptible(&eb->context->vm->mutex);
+ if (!err) {
+ struct drm_i915_gem_object *busy_bo = NULL;
+
+ err = i915_gem_evict_vm(eb->context->vm, &eb->ww, &busy_bo);
+ mutex_unlock(&eb->context->vm->mutex);
+ if (err && busy_bo) {
+ err = i915_gem_object_lock(busy_bo, &eb->ww);
+ i915_gem_object_put(busy_bo);
+ if (!err)
+ goto retry;
+ }
+ }
+ if (err)
+ return err;
+ }
+
list_for_each_entry(ev, &eb->unbound, bind_link) {
err = eb_reserve_vma(eb, ev, pin_flags);
if (err)
@@ -3446,6 +3483,13 @@ err_request:
eb.composite_fence :
&eb.requests[0]->fence);
+ if (unlikely(eb.gem_context->syncobj)) {
+ drm_syncobj_replace_fence(eb.gem_context->syncobj,
+ eb.composite_fence ?
+ eb.composite_fence :
+ &eb.requests[0]->fence);
+ }
+
if (out_fence) {
if (err == 0) {
fd_install(out_fence_fd, out_fence->file);
@@ -3457,13 +3501,6 @@ err_request:
}
}
- if (unlikely(eb.gem_context->syncobj)) {
- drm_syncobj_replace_fence(eb.gem_context->syncobj,
- eb.composite_fence ?
- eb.composite_fence :
- &eb.requests[0]->fence);
- }
-
if (!out_fence && eb.composite_fence)
dma_fence_put(eb.composite_fence);
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
index c29efdef8313a5..c7c252d4d366d4 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_mman.c
@@ -369,7 +369,7 @@ retry:
if (vma == ERR_PTR(-ENOSPC)) {
ret = mutex_lock_interruptible(&ggtt->vm.mutex);
if (!ret) {
- ret = i915_gem_evict_vm(&ggtt->vm, &ww);
+ ret = i915_gem_evict_vm(&ggtt->vm, &ww, NULL);
mutex_unlock(&ggtt->vm.mutex);
}
if (ret)
@@ -697,7 +697,7 @@ insert:
GEM_BUG_ON(lookup_mmo(obj, mmap_type) != mmo);
out:
if (file)
- drm_vma_node_allow(&mmo->vma_node, file);
+ drm_vma_node_allow_once(&mmo->vma_node, file);
return mmo;
err:
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
index 9c759df700caaa..93772884042858 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
@@ -579,7 +579,7 @@ static int shmem_object_init(struct intel_memory_region *mem,
mapping_set_gfp_mask(mapping, mask);
GEM_BUG_ON(!(mapping_gfp_mask(mapping) & __GFP_RECLAIM));
- i915_gem_object_init(obj, &i915_gem_shmem_ops, &lock_class, 0);
+ i915_gem_object_init(obj, &i915_gem_shmem_ops, &lock_class, flags);
obj->mem_flags |= I915_BO_FLAG_STRUCT_PAGE;
obj->write_domain = I915_GEM_DOMAIN_CPU;
obj->read_domains = I915_GEM_DOMAIN_CPU;
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
index fd42b89b7162b7..bc21b1c2350a75 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_tiling.c
@@ -305,10 +305,6 @@ i915_gem_object_set_tiling(struct drm_i915_gem_object *obj,
spin_unlock(&obj->vma.lock);
obj->tiling_and_stride = tiling | stride;
- i915_gem_object_unlock(obj);
-
- /* Force the fence to be reacquired for GTT access */
- i915_gem_object_release_mmap_gtt(obj);
/* Try to preallocate memory required to save swizzling on put-pages */
if (i915_gem_object_needs_bit17_swizzle(obj)) {
@@ -321,6 +317,11 @@ i915_gem_object_set_tiling(struct drm_i915_gem_object *obj,
obj->bit_17 = NULL;
}
+ i915_gem_object_unlock(obj);
+
+ /* Force the fence to be reacquired for GTT access */
+ i915_gem_object_release_mmap_gtt(obj);
+
return 0;
}
diff --git a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
index beaf27e09e8a9d..977dead10ab57c 100644
--- a/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
+++ b/drivers/gpu/drm/i915/gem/selftests/huge_pages.c
@@ -1847,7 +1847,7 @@ static int igt_shrink_thp(void *arg)
I915_SHRINK_ACTIVE);
i915_vma_unpin(vma);
if (err)
- goto out_put;
+ goto out_wf;
/*
* Now that the pages are *unpinned* shrinking should invoke
@@ -1863,19 +1863,19 @@ static int igt_shrink_thp(void *arg)
pr_err("unexpected pages mismatch, should_swap=%s\n",
str_yes_no(should_swap));
err = -EINVAL;
- goto out_put;
+ goto out_wf;
}
if (should_swap == (obj->mm.page_sizes.sg || obj->mm.page_sizes.phys)) {
pr_err("unexpected residual page-size bits, should_swap=%s\n",
str_yes_no(should_swap));
err = -EINVAL;
- goto out_put;
+ goto out_wf;
}
err = i915_vma_pin(vma, 0, 0, flags);
if (err)
- goto out_put;
+ goto out_wf;
while (n--) {
err = cpu_check(obj, n, 0xdeadbeaf);
diff --git a/drivers/gpu/drm/i915/gt/intel_context.c b/drivers/gpu/drm/i915/gt/intel_context.c
index e94365b08f1efc..2aa63ec521b898 100644
--- a/drivers/gpu/drm/i915/gt/intel_context.c
+++ b/drivers/gpu/drm/i915/gt/intel_context.c
@@ -528,7 +528,7 @@ retry:
return rq;
}
-struct i915_request *intel_context_find_active_request(struct intel_context *ce)
+struct i915_request *intel_context_get_active_request(struct intel_context *ce)
{
struct intel_context *parent = intel_context_to_parent(ce);
struct i915_request *rq, *active = NULL;
@@ -552,6 +552,8 @@ struct i915_request *intel_context_find_active_request(struct intel_context *ce)
active = rq;
}
+ if (active)
+ active = i915_request_get_rcu(active);
spin_unlock_irqrestore(&parent->guc_state.lock, flags);
return active;
diff --git a/drivers/gpu/drm/i915/gt/intel_context.h b/drivers/gpu/drm/i915/gt/intel_context.h
index fb62b7b8cbcda6..0a8d553da3f439 100644
--- a/drivers/gpu/drm/i915/gt/intel_context.h
+++ b/drivers/gpu/drm/i915/gt/intel_context.h
@@ -268,8 +268,7 @@ int intel_context_prepare_remote_request(struct intel_context *ce,
struct i915_request *intel_context_create_request(struct intel_context *ce);
-struct i915_request *
-intel_context_find_active_request(struct intel_context *ce);
+struct i915_request *intel_context_get_active_request(struct intel_context *ce);
static inline bool intel_context_is_barrier(const struct intel_context *ce)
{
diff --git a/drivers/gpu/drm/i915/gt/intel_engine.h b/drivers/gpu/drm/i915/gt/intel_engine.h
index cbc8b857d5f7a0..7a4504ea35c365 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine.h
+++ b/drivers/gpu/drm/i915/gt/intel_engine.h
@@ -248,8 +248,8 @@ void intel_engine_dump_active_requests(struct list_head *requests,
ktime_t intel_engine_get_busy_time(struct intel_engine_cs *engine,
ktime_t *now);
-struct i915_request *
-intel_engine_execlist_find_hung_request(struct intel_engine_cs *engine);
+void intel_engine_get_hung_entity(struct intel_engine_cs *engine,
+ struct intel_context **ce, struct i915_request **rq);
u32 intel_engine_context_size(struct intel_gt *gt, u8 class);
struct intel_context *
diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
index c33e0d72d6702b..d37931e16fd9b2 100644
--- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c
+++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c
@@ -2094,17 +2094,6 @@ static void print_request_ring(struct drm_printer *m, struct i915_request *rq)
}
}
-static unsigned long list_count(struct list_head *list)
-{
- struct list_head *pos;
- unsigned long count = 0;
-
- list_for_each(pos, list)
- count++;
-
- return count;
-}
-
static unsigned long read_ul(void *p, size_t x)
{
return *(unsigned long *)(p + x);
@@ -2196,11 +2185,11 @@ void intel_engine_dump_active_requests(struct list_head *requests,
}
}
-static void engine_dump_active_requests(struct intel_engine_cs *engine, struct drm_printer *m)
+static void engine_dump_active_requests(struct intel_engine_cs *engine,
+ struct drm_printer *m)
{
+ struct intel_context *hung_ce = NULL;
struct i915_request *hung_rq = NULL;
- struct intel_context *ce;
- bool guc;
/*
* No need for an engine->irq_seqno_barrier() before the seqno reads.
@@ -2209,27 +2198,22 @@ static void engine_dump_active_requests(struct intel_engine_cs *engine, struct d
* But the intention here is just to report an instantaneous snapshot
* so that's fine.
*/
- lockdep_assert_held(&engine->sched_engine->lock);
+ intel_engine_get_hung_entity(engine, &hung_ce, &hung_rq);
drm_printf(m, "\tRequests:\n");
- guc = intel_uc_uses_guc_submission(&engine->gt->uc);
- if (guc) {
- ce = intel_engine_get_hung_context(engine);
- if (ce)
- hung_rq = intel_context_find_active_request(ce);
- } else {
- hung_rq = intel_engine_execlist_find_hung_request(engine);
- }
-
if (hung_rq)
engine_dump_request(hung_rq, m, "\t\thung");
+ else if (hung_ce)
+ drm_printf(m, "\t\tGot hung ce but no hung rq!\n");
- if (guc)
+ if (intel_uc_uses_guc_submission(&engine->gt->uc))
intel_guc_dump_active_requests(engine, hung_rq, m);
else
- intel_engine_dump_active_requests(&engine->sched_engine->requests,
- hung_rq, m);
+ intel_execlists_dump_active_requests(engine, hung_rq, m);
+
+ if (hung_rq)
+ i915_request_put(hung_rq);
}
void intel_engine_dump(struct intel_engine_cs *engine,
@@ -2239,7 +2223,6 @@ void intel_engine_dump(struct intel_engine_cs *engine,
struct i915_gpu_error * const error = &engine->i915->gpu_error;
struct i915_request *rq;
intel_wakeref_t wakeref;
- unsigned long flags;
ktime_t dummy;
if (header) {
@@ -2276,13 +2259,8 @@ void intel_engine_dump(struct intel_engine_cs *engine,
i915_reset_count(error));
print_properties(engine, m);
- spin_lock_irqsave(&engine->sched_engine->lock, flags);
engine_dump_active_requests(engine, m);
- drm_printf(m, "\tOn hold?: %lu\n",
- list_count(&engine->sched_engine->hold));
- spin_unlock_irqrestore(&engine->sched_engine->lock, flags);
-
drm_printf(m, "\tMMIO base: 0x%08x\n", engine->mmio_base);
wakeref = intel_runtime_pm_get_if_in_use(engine->uncore->rpm);
if (wakeref) {
@@ -2328,8 +2306,7 @@ intel_engine_create_virtual(struct intel_engine_cs **siblings,
return siblings[0]->cops->create_virtual(siblings, count, flags);
}
-struct i915_request *
-intel_engine_execlist_find_hung_request(struct intel_engine_cs *engine)
+static struct i915_request *engine_execlist_find_hung_request(struct intel_engine_cs *engine)
{
struct i915_request *request, *active = NULL;
@@ -2381,6 +2358,33 @@ intel_engine_execlist_find_hung_request(struct intel_engine_cs *engine)
return active;
}
+void intel_engine_get_hung_entity(struct intel_engine_cs *engine,
+ struct intel_context **ce, struct i915_request **rq)
+{
+ unsigned long flags;
+
+ *ce = intel_engine_get_hung_context(engine);
+ if (*ce) {
+ intel_engine_clear_hung_context(engine);
+
+ *rq = intel_context_get_active_request(*ce);
+ return;
+ }
+
+ /*
+ * Getting here with GuC enabled means it is a forced error capture
+ * with no actual hang. So, no need to attempt the execlist search.
+ */
+ if (intel_uc_uses_guc_submission(&engine->gt->uc))
+ return;
+
+ spin_lock_irqsave(&engine->sched_engine->lock, flags);
+ *rq = engine_execlist_find_hung_request(engine);
+ if (*rq)
+ *rq = i915_request_get_rcu(*rq);
+ spin_unlock_irqrestore(&engine->sched_engine->lock, flags);
+}
+
void xehp_enable_ccs_engines(struct intel_engine_cs *engine)
{
/*
diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
index 2daffa7c7dfd00..21cb5b69d82ebc 100644
--- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
+++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c
@@ -4148,6 +4148,33 @@ void intel_execlists_show_requests(struct intel_engine_cs *engine,
spin_unlock_irqrestore(&sched_engine->lock, flags);
}
+static unsigned long list_count(struct list_head *list)
+{
+ struct list_head *pos;
+ unsigned long count = 0;
+
+ list_for_each(pos, list)
+ count++;
+
+ return count;
+}
+
+void intel_execlists_dump_active_requests(struct intel_engine_cs *engine,
+ struct i915_request *hung_rq,
+ struct drm_printer *m)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&engine->sched_engine->lock, flags);
+
+ intel_engine_dump_active_requests(&engine->sched_engine->requests, hung_rq, m);
+
+ drm_printf(m, "\tOn hold?: %lu\n",
+ list_count(&engine->sched_engine->hold));
+
+ spin_unlock_irqrestore(&engine->sched_engine->lock, flags);
+}
+
#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
#include "selftest_execlists.c"
#endif
diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.h b/drivers/gpu/drm/i915/gt/intel_execlists_submission.h
index a1aa92c983a51a..d2c7d45ea0623b 100644
--- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.h
+++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.h
@@ -32,6 +32,10 @@ void intel_execlists_show_requests(struct intel_engine_cs *engine,
int indent),
unsigned int max);
+void intel_execlists_dump_active_requests(struct intel_engine_cs *engine,
+ struct i915_request *hung_rq,
+ struct drm_printer *m);
+
bool
intel_engine_in_execlists_submission_mode(const struct intel_engine_cs *engine);
diff --git a/drivers/gpu/drm/i915/gt/intel_gt.c b/drivers/gpu/drm/i915/gt/intel_gt.c
index 767e329e1cc5fd..9c18b5f2e7892b 100644
--- a/drivers/gpu/drm/i915/gt/intel_gt.c
+++ b/drivers/gpu/drm/i915/gt/intel_gt.c
@@ -1109,9 +1109,15 @@ static void mmio_invalidate_full(struct intel_gt *gt)
continue;
if (GRAPHICS_VER_FULL(i915) >= IP_VER(12, 50)) {
+ u32 val = BIT(engine->instance);
+
+ if (engine->class == VIDEO_DECODE_CLASS ||
+ engine->class == VIDEO_ENHANCEMENT_CLASS ||
+ engine->class == COMPUTE_CLASS)
+ val = _MASKED_BIT_ENABLE(val);
intel_gt_mcr_multicast_write_fw(gt,
xehp_regs[engine->class],
- BIT(engine->instance));
+ val);
} else {
rb = get_reg_and_bit(engine, regs == gen8_regs, regs, num);
if (!i915_mmio_reg_offset(rb.reg))
diff --git a/drivers/gpu/drm/i915/gt/intel_gt_regs.h b/drivers/gpu/drm/i915/gt/intel_gt_regs.h
index c3cd926917957f..a5454af2a9cfdc 100644
--- a/drivers/gpu/drm/i915/gt/intel_gt_regs.h
+++ b/drivers/gpu/drm/i915/gt/intel_gt_regs.h
@@ -406,10 +406,10 @@
#define GEN9_WM_CHICKEN3 _MMIO(0x5588)
#define GEN9_FACTOR_IN_CLR_VAL_HIZ (1 << 9)
-#define CHICKEN_RASTER_1 _MMIO(0x6204)
+#define CHICKEN_RASTER_1 MCR_REG(0x6204)
#define DIS_SF_ROUND_NEAREST_EVEN REG_BIT(8)
-#define CHICKEN_RASTER_2 _MMIO(0x6208)
+#define CHICKEN_RASTER_2 MCR_REG(0x6208)
#define TBIMR_FAST_CLIP REG_BIT(5)
#define VFLSKPD MCR_REG(0x62a8)
@@ -429,9 +429,10 @@
#define RC_OP_FLUSH_ENABLE (1 << 0)
#define HIZ_RAW_STALL_OPT_DISABLE (1 << 2)
#define CACHE_MODE_1 _MMIO(0x7004) /* IVB+ */
-#define PIXEL_SUBSPAN_COLLECT_OPT_DISABLE (1 << 6)
-#define GEN8_4x4_STC_OPTIMIZATION_DISABLE (1 << 6)
-#define GEN9_PARTIAL_RESOLVE_IN_VC_DISABLE (1 << 1)
+#define MSAA_OPTIMIZATION_REDUC_DISABLE REG_BIT(11)
+#define PIXEL_SUBSPAN_COLLECT_OPT_DISABLE REG_BIT(6)
+#define GEN8_4x4_STC_OPTIMIZATION_DISABLE REG_BIT(6)
+#define GEN9_PARTIAL_RESOLVE_IN_VC_DISABLE REG_BIT(1)
#define GEN7_GT_MODE _MMIO(0x7008)
#define GEN9_IZ_HASHING_MASK(slice) (0x3 << ((slice) * 2))
@@ -457,6 +458,9 @@
#define GEN8_L3CNTLREG _MMIO(0x7034)
#define GEN8_ERRDETBCTRL (1 << 9)
+#define PSS_MODE2 _MMIO(0x703c)
+#define SCOREBOARD_STALL_FLUSH_CONTROL REG_BIT(5)
+
#define GEN7_SC_INSTDONE _MMIO(0x7100)
#define GEN12_SC_INSTDONE_EXTRA _MMIO(0x7104)
#define GEN12_SC_INSTDONE_EXTRA2 _MMIO(0x7108)
diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c
index 7771a19008c604..bbeeb6dde7aee2 100644
--- a/drivers/gpu/drm/i915/gt/intel_lrc.c
+++ b/drivers/gpu/drm/i915/gt/intel_lrc.c
@@ -288,39 +288,6 @@ static const u8 dg2_xcs_offsets[] = {
END
};
-static const u8 mtl_xcs_offsets[] = {
- NOP(1),
- LRI(13, POSTED),
- REG16(0x244),
- REG(0x034),
- REG(0x030),
- REG(0x038),
- REG(0x03c),
- REG(0x168),
- REG(0x140),
- REG(0x110),
- REG(0x1c0),
- REG(0x1c4),
- REG(0x1c8),
- REG(0x180),
- REG16(0x2b4),
- NOP(4),
-
- NOP(1),
- LRI(9, POSTED),
- REG16(0x3a8),
- REG16(0x28c),
- REG16(0x288),
- REG16(0x284),
- REG16(0x280),
- REG16(0x27c),
- REG16(0x278),
- REG16(0x274),
- REG16(0x270),
-
- END
-};
-
static const u8 gen8_rcs_offsets[] = {
NOP(1),
LRI(14, POSTED),
@@ -739,9 +706,7 @@ static const u8 *reg_offsets(const struct intel_engine_cs *engine)
else
return gen8_rcs_offsets;
} else {
- if (GRAPHICS_VER_FULL(engine->i915) >= IP_VER(12, 70))
- return mtl_xcs_offsets;
- else if (GRAPHICS_VER_FULL(engine->i915) >= IP_VER(12, 55))
+ if (GRAPHICS_VER_FULL(engine->i915) >= IP_VER(12, 55))
return dg2_xcs_offsets;
else if (GRAPHICS_VER(engine->i915) >= 12)
return gen12_xcs_offsets;
diff --git a/drivers/gpu/drm/i915/gt/intel_reset.c b/drivers/gpu/drm/i915/gt/intel_reset.c
index 24736ebee17c28..78dc5e493c6222 100644
--- a/drivers/gpu/drm/i915/gt/intel_reset.c
+++ b/drivers/gpu/drm/i915/gt/intel_reset.c
@@ -278,6 +278,7 @@ out:
static int gen6_hw_domain_reset(struct intel_gt *gt, u32 hw_domain_mask)
{
struct intel_uncore *uncore = gt->uncore;
+ int loops = 2;
int err;
/*
@@ -285,18 +286,39 @@ static int gen6_hw_domain_reset(struct intel_gt *gt, u32 hw_domain_mask)
* for fifo space for the write or forcewake the chip for
* the read
*/
- intel_uncore_write_fw(uncore, GEN6_GDRST, hw_domain_mask);
+ do {
+ intel_uncore_write_fw(uncore, GEN6_GDRST, hw_domain_mask);
- /* Wait for the device to ack the reset requests */
- err = __intel_wait_for_register_fw(uncore,
- GEN6_GDRST, hw_domain_mask, 0,
- 500, 0,
- NULL);
+ /*
+ * Wait for the device to ack the reset requests.
+ *
+ * On some platforms, e.g. Jasperlake, we see that the
+ * engine register state is not cleared until shortly after
+ * GDRST reports completion, causing a failure as we try
+ * to immediately resume while the internal state is still
+ * in flux. If we immediately repeat the reset, the second
+ * reset appears to serialise with the first, and since
+ * it is a no-op, the registers should retain their reset
+ * value. However, there is still a concern that upon
+ * leaving the second reset, the internal engine state
+ * is still in flux and not ready for resuming.
+ */
+ err = __intel_wait_for_register_fw(uncore, GEN6_GDRST,
+ hw_domain_mask, 0,
+ 2000, 0,
+ NULL);
+ } while (err == 0 && --loops);
if (err)
GT_TRACE(gt,
"Wait for 0x%08x engines reset failed\n",
hw_domain_mask);
+ /*
+ * As we have observed that the engine state is still volatile
+ * after GDRST is acked, impose a small delay to let everything settle.
+ */
+ udelay(50);
+
return err;
}
diff --git a/drivers/gpu/drm/i915/gt/intel_workarounds.c b/drivers/gpu/drm/i915/gt/intel_workarounds.c
index 2afb4f80a954df..949c19339015b0 100644
--- a/drivers/gpu/drm/i915/gt/intel_workarounds.c
+++ b/drivers/gpu/drm/i915/gt/intel_workarounds.c
@@ -645,7 +645,7 @@ static void icl_ctx_workarounds_init(struct intel_engine_cs *engine,
static void dg2_ctx_gt_tuning_init(struct intel_engine_cs *engine,
struct i915_wa_list *wal)
{
- wa_masked_en(wal, CHICKEN_RASTER_2, TBIMR_FAST_CLIP);
+ wa_mcr_masked_en(wal, CHICKEN_RASTER_2, TBIMR_FAST_CLIP);
wa_mcr_write_clr_set(wal, XEHP_L3SQCREG5, L3_PWM_TIMER_INIT_VAL_MASK,
REG_FIELD_PREP(L3_PWM_TIMER_INIT_VAL_MASK, 0x7f));
wa_mcr_add(wal,
@@ -771,11 +771,19 @@ static void dg2_ctx_workarounds_init(struct intel_engine_cs *engine,
/* Wa_14014947963:dg2 */
if (IS_DG2_GRAPHICS_STEP(engine->i915, G10, STEP_B0, STEP_FOREVER) ||
- IS_DG2_G11(engine->i915) || IS_DG2_G12(engine->i915))
+ IS_DG2_G11(engine->i915) || IS_DG2_G12(engine->i915))
wa_masked_field_set(wal, VF_PREEMPTION, PREEMPTION_VERTEX_COUNT, 0x4000);
+ /* Wa_18018764978:dg2 */
+ if (IS_DG2_GRAPHICS_STEP(engine->i915, G10, STEP_C0, STEP_FOREVER) ||
+ IS_DG2_G11(engine->i915) || IS_DG2_G12(engine->i915))
+ wa_masked_en(wal, PSS_MODE2, SCOREBOARD_STALL_FLUSH_CONTROL);
+
/* Wa_15010599737:dg2 */
- wa_masked_en(wal, CHICKEN_RASTER_1, DIS_SF_ROUND_NEAREST_EVEN);
+ wa_mcr_masked_en(wal, CHICKEN_RASTER_1, DIS_SF_ROUND_NEAREST_EVEN);
+
+ /* Wa_18019271663:dg2 */
+ wa_masked_en(wal, CACHE_MODE_1, MSAA_OPTIMIZATION_REDUC_DISABLE);
}
static void fakewa_disable_nestedbb_mode(struct intel_engine_cs *engine,
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
index 0a42f1807f52c3..c10977cb06b972 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
@@ -1702,7 +1702,7 @@ static void __guc_reset_context(struct intel_context *ce, intel_engine_mask_t st
goto next_context;
guilty = false;
- rq = intel_context_find_active_request(ce);
+ rq = intel_context_get_active_request(ce);
if (!rq) {
head = ce->ring->tail;
goto out_replay;
@@ -1715,6 +1715,7 @@ static void __guc_reset_context(struct intel_context *ce, intel_engine_mask_t st
head = intel_ring_wrap(ce->ring, rq->head);
__i915_request_reset(rq, guilty);
+ i915_request_put(rq);
out_replay:
guc_reset_state(ce, head, guilty);
next_context:
@@ -4817,6 +4818,8 @@ void intel_guc_find_hung_context(struct intel_engine_cs *engine)
xa_lock_irqsave(&guc->context_lookup, flags);
xa_for_each(&guc->context_lookup, index, ce) {
+ bool found;
+
if (!kref_get_unless_zero(&ce->ref))
continue;
@@ -4833,10 +4836,18 @@ void intel_guc_find_hung_context(struct intel_engine_cs *engine)
goto next;
}
+ found = false;
+ spin_lock(&ce->guc_state.lock);
list_for_each_entry(rq, &ce->guc_state.requests, sched.link) {
if (i915_test_request_state(rq) != I915_REQUEST_ACTIVE)
continue;
+ found = true;
+ break;
+ }
+ spin_unlock(&ce->guc_state.lock);
+
+ if (found) {
intel_engine_set_hung_context(engine, ce);
/* Can only cope with one hang at a time... */
@@ -4844,6 +4855,7 @@ void intel_guc_find_hung_context(struct intel_engine_cs *engine)
xa_lock(&guc->context_lookup);
goto done;
}
+
next:
intel_context_put(ce);
xa_lock(&guc->context_lookup);
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c
index 0c80ba51a4bdce..2bcdd192f81471 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c
@@ -545,6 +545,32 @@ static int check_ccs_header(struct intel_gt *gt,
return 0;
}
+static int try_firmware_load(struct intel_uc_fw *uc_fw, const struct firmware **fw)
+{
+ struct intel_gt *gt = __uc_fw_to_gt(uc_fw);
+ struct device *dev = gt->i915->drm.dev;
+ int err;
+
+ err = firmware_request_nowarn(fw, uc_fw->file_selected.path, dev);
+
+ if (err)
+ return err;
+
+ if ((*fw)->size > INTEL_UC_RSVD_GGTT_PER_FW) {
+ drm_err(&gt->i915->drm,
+ "%s firmware %s: size (%zuKB) exceeds max supported size (%uKB)\n",
+ intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,
+ (*fw)->size / SZ_1K, INTEL_UC_RSVD_GGTT_PER_FW / SZ_1K);
+
+ /* try to find another blob to load */
+ release_firmware(*fw);
+ *fw = NULL;
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
/**
* intel_uc_fw_fetch - fetch uC firmware
* @uc_fw: uC firmware
@@ -558,7 +584,6 @@ int intel_uc_fw_fetch(struct intel_uc_fw *uc_fw)
struct intel_gt *gt = __uc_fw_to_gt(uc_fw);
struct drm_i915_private *i915 = gt->i915;
struct intel_uc_fw_file file_ideal;
- struct device *dev = i915->drm.dev;
struct drm_i915_gem_object *obj;
const struct firmware *fw = NULL;
bool old_ver = false;
@@ -574,20 +599,9 @@ int intel_uc_fw_fetch(struct intel_uc_fw *uc_fw)
__force_fw_fetch_failures(uc_fw, -EINVAL);
__force_fw_fetch_failures(uc_fw, -ESTALE);
- err = firmware_request_nowarn(&fw, uc_fw->file_selected.path, dev);
+ err = try_firmware_load(uc_fw, &fw);
memcpy(&file_ideal, &uc_fw->file_wanted, sizeof(file_ideal));
- if (!err && fw->size > INTEL_UC_RSVD_GGTT_PER_FW) {
- drm_err(&i915->drm,
- "%s firmware %s: size (%zuKB) exceeds max supported size (%uKB)\n",
- intel_uc_fw_type_repr(uc_fw->type), uc_fw->file_selected.path,
- fw->size / SZ_1K, INTEL_UC_RSVD_GGTT_PER_FW / SZ_1K);
-
- /* try to find another blob to load */
- release_firmware(fw);
- err = -ENOENT;
- }
-
/* Any error is terminal if overriding. Don't bother searching for older versions */
if (err && intel_uc_fw_is_overridden(uc_fw))
goto fail;
@@ -608,7 +622,7 @@ int intel_uc_fw_fetch(struct intel_uc_fw *uc_fw)
break;
}
- err = firmware_request_nowarn(&fw, uc_fw->file_selected.path, dev);
+ err = try_firmware_load(uc_fw, &fw);
}
if (err)
diff --git a/drivers/gpu/drm/i915/gvt/debugfs.c b/drivers/gpu/drm/i915/gvt/debugfs.c
index 9f1c209d925110..0616b73175f3e9 100644
--- a/drivers/gpu/drm/i915/gvt/debugfs.c
+++ b/drivers/gpu/drm/i915/gvt/debugfs.c
@@ -151,6 +151,22 @@ DEFINE_SIMPLE_ATTRIBUTE(vgpu_scan_nonprivbb_fops,
vgpu_scan_nonprivbb_get, vgpu_scan_nonprivbb_set,
"0x%llx\n");
+static int vgpu_status_get(void *data, u64 *val)
+{
+ struct intel_vgpu *vgpu = (struct intel_vgpu *)data;
+
+ *val = 0;
+
+ if (test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status))
+ *val |= (1 << INTEL_VGPU_STATUS_ATTACHED);
+ if (test_bit(INTEL_VGPU_STATUS_ACTIVE, vgpu->status))
+ *val |= (1 << INTEL_VGPU_STATUS_ACTIVE);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(vgpu_status_fops, vgpu_status_get, NULL, "0x%llx\n");
+
/**
* intel_gvt_debugfs_add_vgpu - register debugfs entries for a vGPU
* @vgpu: a vGPU
@@ -162,11 +178,12 @@ void intel_gvt_debugfs_add_vgpu(struct intel_vgpu *vgpu)
snprintf(name, 16, "vgpu%d", vgpu->id);
vgpu->debugfs = debugfs_create_dir(name, vgpu->gvt->debugfs_root);
- debugfs_create_bool("active", 0444, vgpu->debugfs, &vgpu->active);
debugfs_create_file("mmio_diff", 0444, vgpu->debugfs, vgpu,
&vgpu_mmio_diff_fops);
debugfs_create_file("scan_nonprivbb", 0644, vgpu->debugfs, vgpu,
&vgpu_scan_nonprivbb_fops);
+ debugfs_create_file("status", 0644, vgpu->debugfs, vgpu,
+ &vgpu_status_fops);
}
/**
@@ -175,8 +192,13 @@ void intel_gvt_debugfs_add_vgpu(struct intel_vgpu *vgpu)
*/
void intel_gvt_debugfs_remove_vgpu(struct intel_vgpu *vgpu)
{
- debugfs_remove_recursive(vgpu->debugfs);
- vgpu->debugfs = NULL;
+ struct intel_gvt *gvt = vgpu->gvt;
+ struct drm_minor *minor = gvt->gt->i915->drm.primary;
+
+ if (minor->debugfs_root && gvt->debugfs_root) {
+ debugfs_remove_recursive(vgpu->debugfs);
+ vgpu->debugfs = NULL;
+ }
}
/**
@@ -199,6 +221,10 @@ void intel_gvt_debugfs_init(struct intel_gvt *gvt)
*/
void intel_gvt_debugfs_clean(struct intel_gvt *gvt)
{
- debugfs_remove_recursive(gvt->debugfs_root);
- gvt->debugfs_root = NULL;
+ struct drm_minor *minor = gvt->gt->i915->drm.primary;
+
+ if (minor->debugfs_root) {
+ debugfs_remove_recursive(gvt->debugfs_root);
+ gvt->debugfs_root = NULL;
+ }
}
diff --git a/drivers/gpu/drm/i915/gvt/dmabuf.c b/drivers/gpu/drm/i915/gvt/dmabuf.c
index 355f1c0e866417..ffe41e9be04fc0 100644
--- a/drivers/gpu/drm/i915/gvt/dmabuf.c
+++ b/drivers/gpu/drm/i915/gvt/dmabuf.c
@@ -134,7 +134,8 @@ static void dmabuf_gem_object_free(struct kref *kref)
struct list_head *pos;
struct intel_vgpu_dmabuf_obj *dmabuf_obj;
- if (vgpu && vgpu->active && !list_empty(&vgpu->dmabuf_obj_list_head)) {
+ if (vgpu && test_bit(INTEL_VGPU_STATUS_ACTIVE, vgpu->status) &&
+ !list_empty(&vgpu->dmabuf_obj_list_head)) {
list_for_each(pos, &vgpu->dmabuf_obj_list_head) {
dmabuf_obj = list_entry(pos, struct intel_vgpu_dmabuf_obj, list);
if (dmabuf_obj == obj) {
diff --git a/drivers/gpu/drm/i915/gvt/gtt.c b/drivers/gpu/drm/i915/gvt/gtt.c
index 51e5e8fb505bcc..4ec85308379a4f 100644
--- a/drivers/gpu/drm/i915/gvt/gtt.c
+++ b/drivers/gpu/drm/i915/gvt/gtt.c
@@ -55,7 +55,7 @@ static bool intel_gvt_is_valid_gfn(struct intel_vgpu *vgpu, unsigned long gfn)
int idx;
bool ret;
- if (!vgpu->attached)
+ if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status))
return false;
idx = srcu_read_lock(&kvm->srcu);
@@ -1178,7 +1178,7 @@ static int is_2MB_gtt_possible(struct intel_vgpu *vgpu,
if (!HAS_PAGE_SIZES(vgpu->gvt->gt->i915, I915_GTT_PAGE_SIZE_2M))
return 0;
- if (!vgpu->attached)
+ if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status))
return -EINVAL;
pfn = gfn_to_pfn(vgpu->vfio_device.kvm, ops->get_pfn(entry));
if (is_error_noslot_pfn(pfn))
@@ -1209,10 +1209,8 @@ static int split_2MB_gtt_entry(struct intel_vgpu *vgpu,
for_each_shadow_entry(sub_spt, &sub_se, sub_index) {
ret = intel_gvt_dma_map_guest_page(vgpu, start_gfn + sub_index,
PAGE_SIZE, &dma_addr);
- if (ret) {
- ppgtt_invalidate_spt(spt);
- return ret;
- }
+ if (ret)
+ goto err;
sub_se.val64 = se->val64;
/* Copy the PAT field from PDE. */
@@ -1231,6 +1229,17 @@ static int split_2MB_gtt_entry(struct intel_vgpu *vgpu,
ops->set_pfn(se, sub_spt->shadow_page.mfn);
ppgtt_set_shadow_entry(spt, se, index);
return 0;
+err:
+ /* Cancel the existing addess mappings of DMA addr. */
+ for_each_present_shadow_entry(sub_spt, &sub_se, sub_index) {
+ gvt_vdbg_mm("invalidate 4K entry\n");
+ ppgtt_invalidate_pte(sub_spt, &sub_se);
+ }
+ /* Release the new allocated spt. */
+ trace_spt_change(sub_spt->vgpu->id, "release", sub_spt,
+ sub_spt->guest_page.gfn, sub_spt->shadow_page.type);
+ ppgtt_free_spt(sub_spt);
+ return ret;
}
static int split_64KB_gtt_entry(struct intel_vgpu *vgpu,
diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h
index 62823c0e13ab83..2d65800d8e93b9 100644
--- a/drivers/gpu/drm/i915/gvt/gvt.h
+++ b/drivers/gpu/drm/i915/gvt/gvt.h
@@ -172,13 +172,18 @@ struct intel_vgpu_submission {
#define KVMGT_DEBUGFS_FILENAME "kvmgt_nr_cache_entries"
+enum {
+ INTEL_VGPU_STATUS_ATTACHED = 0,
+ INTEL_VGPU_STATUS_ACTIVE,
+ INTEL_VGPU_STATUS_NR_BITS,
+};
+
struct intel_vgpu {
struct vfio_device vfio_device;
struct intel_gvt *gvt;
struct mutex vgpu_lock;
int id;
- bool active;
- bool attached;
+ DECLARE_BITMAP(status, INTEL_VGPU_STATUS_NR_BITS);
bool pv_notified;
bool failsafe;
unsigned int resetting_eng;
@@ -467,7 +472,7 @@ void intel_vgpu_write_fence(struct intel_vgpu *vgpu,
#define for_each_active_vgpu(gvt, vgpu, id) \
idr_for_each_entry((&(gvt)->vgpu_idr), (vgpu), (id)) \
- for_each_if(vgpu->active)
+ for_each_if(test_bit(INTEL_VGPU_STATUS_ACTIVE, vgpu->status))
static inline void intel_vgpu_write_pci_bar(struct intel_vgpu *vgpu,
u32 offset, u32 val, bool low)
@@ -725,7 +730,7 @@ static inline bool intel_gvt_mmio_is_cmd_write_patch(
static inline int intel_gvt_read_gpa(struct intel_vgpu *vgpu, unsigned long gpa,
void *buf, unsigned long len)
{
- if (!vgpu->attached)
+ if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status))
return -ESRCH;
return vfio_dma_rw(&vgpu->vfio_device, gpa, buf, len, false);
}
@@ -743,7 +748,7 @@ static inline int intel_gvt_read_gpa(struct intel_vgpu *vgpu, unsigned long gpa,
static inline int intel_gvt_write_gpa(struct intel_vgpu *vgpu,
unsigned long gpa, void *buf, unsigned long len)
{
- if (!vgpu->attached)
+ if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status))
return -ESRCH;
return vfio_dma_rw(&vgpu->vfio_device, gpa, buf, len, true);
}
diff --git a/drivers/gpu/drm/i915/gvt/interrupt.c b/drivers/gpu/drm/i915/gvt/interrupt.c
index a6b2021b665ffd..68eca023bbc68b 100644
--- a/drivers/gpu/drm/i915/gvt/interrupt.c
+++ b/drivers/gpu/drm/i915/gvt/interrupt.c
@@ -433,7 +433,7 @@ static int inject_virtual_interrupt(struct intel_vgpu *vgpu)
* enabled by guest. so if msi_trigger is null, success is still
* returned and don't inject interrupt into guest.
*/
- if (!vgpu->attached)
+ if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status))
return -ESRCH;
if (vgpu->msi_trigger && eventfd_signal(vgpu->msi_trigger, 1) != 1)
return -EFAULT;
diff --git a/drivers/gpu/drm/i915/gvt/kvmgt.c b/drivers/gpu/drm/i915/gvt/kvmgt.c
index f5451adcd4890c..8ae7039b368325 100644
--- a/drivers/gpu/drm/i915/gvt/kvmgt.c
+++ b/drivers/gpu/drm/i915/gvt/kvmgt.c
@@ -638,7 +638,7 @@ static bool __kvmgt_vgpu_exist(struct intel_vgpu *vgpu)
mutex_lock(&vgpu->gvt->lock);
for_each_active_vgpu(vgpu->gvt, itr, id) {
- if (!itr->attached)
+ if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, itr->status))
continue;
if (vgpu->vfio_device.kvm == itr->vfio_device.kvm) {
@@ -655,9 +655,6 @@ static int intel_vgpu_open_device(struct vfio_device *vfio_dev)
{
struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
- if (vgpu->attached)
- return -EEXIST;
-
if (!vgpu->vfio_device.kvm ||
vgpu->vfio_device.kvm->mm != current->mm) {
gvt_vgpu_err("KVM is required to use Intel vGPU\n");
@@ -667,14 +664,14 @@ static int intel_vgpu_open_device(struct vfio_device *vfio_dev)
if (__kvmgt_vgpu_exist(vgpu))
return -EEXIST;
- vgpu->attached = true;
-
vgpu->track_node.track_write = kvmgt_page_track_write;
vgpu->track_node.track_flush_slot = kvmgt_page_track_flush_slot;
kvm_get_kvm(vgpu->vfio_device.kvm);
kvm_page_track_register_notifier(vgpu->vfio_device.kvm,
&vgpu->track_node);
+ set_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status);
+
debugfs_create_ulong(KVMGT_DEBUGFS_FILENAME, 0444, vgpu->debugfs,
&vgpu->nr_cache_entries);
@@ -698,11 +695,10 @@ static void intel_vgpu_close_device(struct vfio_device *vfio_dev)
{
struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
- if (!vgpu->attached)
- return;
-
intel_gvt_release_vgpu(vgpu);
+ clear_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status);
+
debugfs_remove(debugfs_lookup(KVMGT_DEBUGFS_FILENAME, vgpu->debugfs));
kvm_page_track_unregister_notifier(vgpu->vfio_device.kvm,
@@ -718,8 +714,6 @@ static void intel_vgpu_close_device(struct vfio_device *vfio_dev)
vgpu->dma_addr_cache = RB_ROOT;
intel_vgpu_release_msi_eventfd_ctx(vgpu);
-
- vgpu->attached = false;
}
static u64 intel_vgpu_get_bar_addr(struct intel_vgpu *vgpu, int bar)
@@ -1512,9 +1506,6 @@ static void intel_vgpu_remove(struct mdev_device *mdev)
{
struct intel_vgpu *vgpu = dev_get_drvdata(&mdev->dev);
- if (WARN_ON_ONCE(vgpu->attached))
- return;
-
vfio_unregister_group_dev(&vgpu->vfio_device);
vfio_put_device(&vgpu->vfio_device);
}
@@ -1559,7 +1550,7 @@ int intel_gvt_page_track_add(struct intel_vgpu *info, u64 gfn)
struct kvm_memory_slot *slot;
int idx;
- if (!info->attached)
+ if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, info->status))
return -ESRCH;
idx = srcu_read_lock(&kvm->srcu);
@@ -1589,8 +1580,8 @@ int intel_gvt_page_track_remove(struct intel_vgpu *info, u64 gfn)
struct kvm_memory_slot *slot;
int idx;
- if (!info->attached)
- return 0;
+ if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, info->status))
+ return -ESRCH;
idx = srcu_read_lock(&kvm->srcu);
slot = gfn_to_memslot(kvm, gfn);
@@ -1668,7 +1659,7 @@ int intel_gvt_dma_map_guest_page(struct intel_vgpu *vgpu, unsigned long gfn,
struct gvt_dma *entry;
int ret;
- if (!vgpu->attached)
+ if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status))
return -EINVAL;
mutex_lock(&vgpu->cache_lock);
@@ -1714,8 +1705,8 @@ int intel_gvt_dma_pin_guest_page(struct intel_vgpu *vgpu, dma_addr_t dma_addr)
struct gvt_dma *entry;
int ret = 0;
- if (!vgpu->attached)
- return -ENODEV;
+ if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status))
+ return -EINVAL;
mutex_lock(&vgpu->cache_lock);
entry = __gvt_cache_find_dma_addr(vgpu, dma_addr);
@@ -1742,7 +1733,7 @@ void intel_gvt_dma_unmap_guest_page(struct intel_vgpu *vgpu,
{
struct gvt_dma *entry;
- if (!vgpu->attached)
+ if (!test_bit(INTEL_VGPU_STATUS_ATTACHED, vgpu->status))
return;
mutex_lock(&vgpu->cache_lock);
@@ -1778,7 +1769,7 @@ static void intel_gvt_test_and_emulate_vblank(struct intel_gvt *gvt)
idr_for_each_entry((&(gvt)->vgpu_idr), (vgpu), (id)) {
if (test_and_clear_bit(INTEL_GVT_REQUEST_EMULATE_VBLANK + id,
(void *)&gvt->service_request)) {
- if (vgpu->active)
+ if (test_bit(INTEL_VGPU_STATUS_ACTIVE, vgpu->status))
intel_vgpu_emulate_vblank(vgpu);
}
}
diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c
index 9cd8fcbf7cad16..f4055804aad1fe 100644
--- a/drivers/gpu/drm/i915/gvt/scheduler.c
+++ b/drivers/gpu/drm/i915/gvt/scheduler.c
@@ -695,6 +695,7 @@ intel_vgpu_shadow_mm_pin(struct intel_vgpu_workload *workload)
if (workload->shadow_mm->type != INTEL_GVT_MM_PPGTT ||
!workload->shadow_mm->ppgtt_mm.shadowed) {
+ intel_vgpu_unpin_mm(workload->shadow_mm);
gvt_vgpu_err("workload shadow ppgtt isn't ready\n");
return -EINVAL;
}
@@ -865,7 +866,8 @@ pick_next_workload(struct intel_gvt *gvt, struct intel_engine_cs *engine)
goto out;
}
- if (!scheduler->current_vgpu->active ||
+ if (!test_bit(INTEL_VGPU_STATUS_ACTIVE,
+ scheduler->current_vgpu->status) ||
list_empty(workload_q_head(scheduler->current_vgpu, engine)))
goto out;
diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c
index 3c529c2705ddca..a5497440484f12 100644
--- a/drivers/gpu/drm/i915/gvt/vgpu.c
+++ b/drivers/gpu/drm/i915/gvt/vgpu.c
@@ -166,9 +166,7 @@ void intel_gvt_clean_vgpu_types(struct intel_gvt *gvt)
*/
void intel_gvt_activate_vgpu(struct intel_vgpu *vgpu)
{
- mutex_lock(&vgpu->vgpu_lock);
- vgpu->active = true;
- mutex_unlock(&vgpu->vgpu_lock);
+ set_bit(INTEL_VGPU_STATUS_ACTIVE, vgpu->status);
}
/**
@@ -183,7 +181,7 @@ void intel_gvt_deactivate_vgpu(struct intel_vgpu *vgpu)
{
mutex_lock(&vgpu->vgpu_lock);
- vgpu->active = false;
+ clear_bit(INTEL_VGPU_STATUS_ACTIVE, vgpu->status);
if (atomic_read(&vgpu->submission.running_workload_num)) {
mutex_unlock(&vgpu->vgpu_lock);
@@ -228,7 +226,8 @@ void intel_gvt_destroy_vgpu(struct intel_vgpu *vgpu)
struct intel_gvt *gvt = vgpu->gvt;
struct drm_i915_private *i915 = gvt->gt->i915;
- drm_WARN(&i915->drm, vgpu->active, "vGPU is still active!\n");
+ drm_WARN(&i915->drm, test_bit(INTEL_VGPU_STATUS_ACTIVE, vgpu->status),
+ "vGPU is still active!\n");
/*
* remove idr first so later clean can judge if need to stop
@@ -285,8 +284,7 @@ struct intel_vgpu *intel_gvt_create_idle_vgpu(struct intel_gvt *gvt)
if (ret)
goto out_free_vgpu;
- vgpu->active = false;
-
+ clear_bit(INTEL_VGPU_STATUS_ACTIVE, vgpu->status);
return vgpu;
out_free_vgpu:
diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c
index 69103ae3777921..61c38fc734cfb6 100644
--- a/drivers/gpu/drm/i915/i915_driver.c
+++ b/drivers/gpu/drm/i915/i915_driver.c
@@ -1069,12 +1069,9 @@ static int i915_driver_open(struct drm_device *dev, struct drm_file *file)
*/
static void i915_driver_lastclose(struct drm_device *dev)
{
- struct drm_i915_private *i915 = to_i915(dev);
-
intel_fbdev_restore_mode(dev);
- if (HAS_DISPLAY(i915))
- vga_switcheroo_process_delayed_switch();
+ vga_switcheroo_process_delayed_switch();
}
static void i915_driver_postclose(struct drm_device *dev, struct drm_file *file)
diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
index f025ee4fa52618..a4b4d9b7d26c7a 100644
--- a/drivers/gpu/drm/i915/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/i915_gem_evict.c
@@ -416,6 +416,11 @@ int i915_gem_evict_for_node(struct i915_address_space *vm,
* @vm: Address space to cleanse
* @ww: An optional struct i915_gem_ww_ctx. If not NULL, i915_gem_evict_vm
* will be able to evict vma's locked by the ww as well.
+ * @busy_bo: Optional pointer to struct drm_i915_gem_object. If not NULL, then
+ * in the event i915_gem_evict_vm() is unable to trylock an object for eviction,
+ * then @busy_bo will point to it. -EBUSY is also returned. The caller must drop
+ * the vm->mutex, before trying again to acquire the contended lock. The caller
+ * also owns a reference to the object.
*
* This function evicts all vmas from a vm.
*
@@ -425,7 +430,8 @@ int i915_gem_evict_for_node(struct i915_address_space *vm,
* To clarify: This is for freeing up virtual address space, not for freeing
* memory in e.g. the shrinker.
*/
-int i915_gem_evict_vm(struct i915_address_space *vm, struct i915_gem_ww_ctx *ww)
+int i915_gem_evict_vm(struct i915_address_space *vm, struct i915_gem_ww_ctx *ww,
+ struct drm_i915_gem_object **busy_bo)
{
int ret = 0;
@@ -457,15 +463,22 @@ int i915_gem_evict_vm(struct i915_address_space *vm, struct i915_gem_ww_ctx *ww)
* the resv is shared among multiple objects, we still
* need the object ref.
*/
- if (dying_vma(vma) ||
+ if (!i915_gem_object_get_rcu(vma->obj) ||
(ww && (dma_resv_locking_ctx(vma->obj->base.resv) == &ww->ctx))) {
__i915_vma_pin(vma);
list_add(&vma->evict_link, &locked_eviction_list);
continue;
}
- if (!i915_gem_object_trylock(vma->obj, ww))
+ if (!i915_gem_object_trylock(vma->obj, ww)) {
+ if (busy_bo) {
+ *busy_bo = vma->obj; /* holds ref */
+ ret = -EBUSY;
+ break;
+ }
+ i915_gem_object_put(vma->obj);
continue;
+ }
__i915_vma_pin(vma);
list_add(&vma->evict_link, &eviction_list);
@@ -473,25 +486,29 @@ int i915_gem_evict_vm(struct i915_address_space *vm, struct i915_gem_ww_ctx *ww)
if (list_empty(&eviction_list) && list_empty(&locked_eviction_list))
break;
- ret = 0;
/* Unbind locked objects first, before unlocking the eviction_list */
list_for_each_entry_safe(vma, vn, &locked_eviction_list, evict_link) {
__i915_vma_unpin(vma);
- if (ret == 0)
+ if (ret == 0) {
ret = __i915_vma_unbind(vma);
- if (ret != -EINTR) /* "Get me out of here!" */
- ret = 0;
+ if (ret != -EINTR) /* "Get me out of here!" */
+ ret = 0;
+ }
+ if (!dying_vma(vma))
+ i915_gem_object_put(vma->obj);
}
list_for_each_entry_safe(vma, vn, &eviction_list, evict_link) {
__i915_vma_unpin(vma);
- if (ret == 0)
+ if (ret == 0) {
ret = __i915_vma_unbind(vma);
- if (ret != -EINTR) /* "Get me out of here!" */
- ret = 0;
+ if (ret != -EINTR) /* "Get me out of here!" */
+ ret = 0;
+ }
i915_gem_object_unlock(vma->obj);
+ i915_gem_object_put(vma->obj);
}
} while (ret == 0);
diff --git a/drivers/gpu/drm/i915/i915_gem_evict.h b/drivers/gpu/drm/i915/i915_gem_evict.h
index e593c530f9bd7a..bf0ee0e4fe6088 100644
--- a/drivers/gpu/drm/i915/i915_gem_evict.h
+++ b/drivers/gpu/drm/i915/i915_gem_evict.h
@@ -11,6 +11,7 @@
struct drm_mm_node;
struct i915_address_space;
struct i915_gem_ww_ctx;
+struct drm_i915_gem_object;
int __must_check i915_gem_evict_something(struct i915_address_space *vm,
struct i915_gem_ww_ctx *ww,
@@ -23,6 +24,7 @@ int __must_check i915_gem_evict_for_node(struct i915_address_space *vm,
struct drm_mm_node *node,
unsigned int flags);
int i915_gem_evict_vm(struct i915_address_space *vm,
- struct i915_gem_ww_ctx *ww);
+ struct i915_gem_ww_ctx *ww,
+ struct drm_i915_gem_object **busy_bo);
#endif /* __I915_GEM_EVICT_H__ */
diff --git a/drivers/gpu/drm/i915/i915_gpu_error.c b/drivers/gpu/drm/i915/i915_gpu_error.c
index 9d5d5a397b64e4..b20bd6365615b5 100644
--- a/drivers/gpu/drm/i915/i915_gpu_error.c
+++ b/drivers/gpu/drm/i915/i915_gpu_error.c
@@ -1596,43 +1596,20 @@ capture_engine(struct intel_engine_cs *engine,
{
struct intel_engine_capture_vma *capture = NULL;
struct intel_engine_coredump *ee;
- struct intel_context *ce;
+ struct intel_context *ce = NULL;
struct i915_request *rq = NULL;
- unsigned long flags;
ee = intel_engine_coredump_alloc(engine, ALLOW_FAIL, dump_flags);
if (!ee)
return NULL;
- ce = intel_engine_get_hung_context(engine);
- if (ce) {
- intel_engine_clear_hung_context(engine);
- rq = intel_context_find_active_request(ce);
- if (!rq || !i915_request_started(rq))
- goto no_request_capture;
- } else {
- /*
- * Getting here with GuC enabled means it is a forced error capture
- * with no actual hang. So, no need to attempt the execlist search.
- */
- if (!intel_uc_uses_guc_submission(&engine->gt->uc)) {
- spin_lock_irqsave(&engine->sched_engine->lock, flags);
- rq = intel_engine_execlist_find_hung_request(engine);
- spin_unlock_irqrestore(&engine->sched_engine->lock,
- flags);
- }
- }
- if (rq)
- rq = i915_request_get_rcu(rq);
-
- if (!rq)
+ intel_engine_get_hung_entity(engine, &ce, &rq);
+ if (!rq || !i915_request_started(rq))
goto no_request_capture;
capture = intel_engine_coredump_add_request(ee, rq, ATOMIC_MAYFAIL);
- if (!capture) {
- i915_request_put(rq);
+ if (!capture)
goto no_request_capture;
- }
if (dump_flags & CORE_DUMP_FLAG_IS_GUC_CAPTURE)
intel_guc_capture_get_matching_node(engine->gt, ee, ce);
@@ -1642,6 +1619,8 @@ capture_engine(struct intel_engine_cs *engine,
return ee;
no_request_capture:
+ if (rq)
+ i915_request_put(rq);
kfree(ee);
return NULL;
}
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index edfe363af8389f..91c53398604121 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -1974,7 +1974,10 @@ static void icp_irq_handler(struct drm_i915_private *dev_priv, u32 pch_iir)
if (ddi_hotplug_trigger) {
u32 dig_hotplug_reg;
+ /* Locking due to DSI native GPIO sequences */
+ spin_lock(&dev_priv->irq_lock);
dig_hotplug_reg = intel_uncore_rmw(&dev_priv->uncore, SHOTPLUG_CTL_DDI, 0, 0);
+ spin_unlock(&dev_priv->irq_lock);
intel_get_hpd_pins(dev_priv, &pin_mask, &long_mask,
ddi_hotplug_trigger, dig_hotplug_reg,
diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c
index 6da9784fe4a244..4fada7ebe8d823 100644
--- a/drivers/gpu/drm/i915/i915_pci.c
+++ b/drivers/gpu/drm/i915/i915_pci.c
@@ -423,7 +423,8 @@ static const struct intel_device_info ilk_m_info = {
.has_coherent_ggtt = true, \
.has_llc = 1, \
.has_rc6 = 1, \
- .has_rc6p = 1, \
+ /* snb does support rc6p, but enabling it causes various issues */ \
+ .has_rc6p = 0, \
.has_rps = true, \
.dma_mask_size = 40, \
.__runtime.ppgtt_type = INTEL_PPGTT_ALIASING, \
@@ -1129,7 +1130,6 @@ static const struct intel_gt_definition xelpmp_extra_gt[] = {
{}
};
-__maybe_unused
static const struct intel_device_info mtl_info = {
XE_HP_FEATURES,
XE_LPDP_FEATURES,
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 8e1892d147741c..91617687254492 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -5988,6 +5988,7 @@
#define SHOTPLUG_CTL_DDI _MMIO(0xc4030)
#define SHOTPLUG_CTL_DDI_HPD_ENABLE(hpd_pin) (0x8 << (_HPD_PIN_DDI(hpd_pin) * 4))
+#define SHOTPLUG_CTL_DDI_HPD_OUTPUT_DATA(hpd_pin) (0x4 << (_HPD_PIN_DDI(hpd_pin) * 4))
#define SHOTPLUG_CTL_DDI_HPD_STATUS_MASK(hpd_pin) (0x3 << (_HPD_PIN_DDI(hpd_pin) * 4))
#define SHOTPLUG_CTL_DDI_HPD_NO_DETECT(hpd_pin) (0x0 << (_HPD_PIN_DDI(hpd_pin) * 4))
#define SHOTPLUG_CTL_DDI_HPD_SHORT_DETECT(hpd_pin) (0x1 << (_HPD_PIN_DDI(hpd_pin) * 4))
diff --git a/drivers/gpu/drm/i915/i915_switcheroo.c b/drivers/gpu/drm/i915/i915_switcheroo.c
index 23777d500cdf95..f45bd6b6cede45 100644
--- a/drivers/gpu/drm/i915/i915_switcheroo.c
+++ b/drivers/gpu/drm/i915/i915_switcheroo.c
@@ -19,6 +19,10 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev,
dev_err(&pdev->dev, "DRM not initialized, aborting switch.\n");
return;
}
+ if (!HAS_DISPLAY(i915)) {
+ dev_err(&pdev->dev, "Device state not initialized, aborting switch.\n");
+ return;
+ }
if (state == VGA_SWITCHEROO_ON) {
drm_info(&i915->drm, "switched on\n");
@@ -44,7 +48,7 @@ static bool i915_switcheroo_can_switch(struct pci_dev *pdev)
* locking inversion with the driver load path. And the access here is
* completely racy anyway. So don't bother with locking for now.
*/
- return i915 && atomic_read(&i915->drm.open_count) == 0;
+ return i915 && HAS_DISPLAY(i915) && atomic_read(&i915->drm.open_count) == 0;
}
static const struct vga_switcheroo_client_ops i915_switcheroo_ops = {
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index 703fee6b5f7522..135390d975b6f1 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -1566,7 +1566,7 @@ static int __i915_ggtt_pin(struct i915_vma *vma, struct i915_gem_ww_ctx *ww,
* locked objects when called from execbuf when pinning
* is removed. This would probably regress badly.
*/
- i915_gem_evict_vm(vm, NULL);
+ i915_gem_evict_vm(vm, NULL, NULL);
mutex_unlock(&vm->mutex);
}
} while (1);
@@ -2116,7 +2116,7 @@ int i915_vma_unbind_async(struct i915_vma *vma, bool trylock_vm)
if (!obj->mm.rsgt)
return -EBUSY;
- err = dma_resv_reserve_fences(obj->base.resv, 1);
+ err = dma_resv_reserve_fences(obj->base.resv, 2);
if (err)
return -EBUSY;
diff --git a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
index 8c6517d29b8e0c..37068542aafe7f 100644
--- a/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/selftests/i915_gem_evict.c
@@ -344,7 +344,7 @@ static int igt_evict_vm(void *arg)
/* Everything is pinned, nothing should happen */
mutex_lock(&ggtt->vm.mutex);
- err = i915_gem_evict_vm(&ggtt->vm, NULL);
+ err = i915_gem_evict_vm(&ggtt->vm, NULL, NULL);
mutex_unlock(&ggtt->vm.mutex);
if (err) {
pr_err("i915_gem_evict_vm on a full GGTT returned err=%d]\n",
@@ -356,7 +356,7 @@ static int igt_evict_vm(void *arg)
for_i915_gem_ww(&ww, err, false) {
mutex_lock(&ggtt->vm.mutex);
- err = i915_gem_evict_vm(&ggtt->vm, &ww);
+ err = i915_gem_evict_vm(&ggtt->vm, &ww, NULL);
mutex_unlock(&ggtt->vm.mutex);
}
diff --git a/drivers/gpu/drm/i915/selftests/intel_scheduler_helpers.c b/drivers/gpu/drm/i915/selftests/intel_scheduler_helpers.c
index 310fb83c527ebc..2990dd4d4a0d8a 100644
--- a/drivers/gpu/drm/i915/selftests/intel_scheduler_helpers.c
+++ b/drivers/gpu/drm/i915/selftests/intel_scheduler_helpers.c
@@ -28,8 +28,7 @@ struct intel_engine_cs *intel_selftest_find_any_engine(struct intel_gt *gt)
int intel_selftest_modify_policy(struct intel_engine_cs *engine,
struct intel_selftest_saved_policy *saved,
- u32 modify_type)
-
+ enum selftest_scheduler_modify modify_type)
{
int err;
diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c
index dba4f7d81d6935..80142d9a4a5521 100644
--- a/drivers/gpu/drm/imx/ipuv3-plane.c
+++ b/drivers/gpu/drm/imx/ipuv3-plane.c
@@ -614,6 +614,11 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
break;
}
+ if (ipu_plane->dp_flow == IPU_DP_FLOW_SYNC_BG)
+ width = ipu_src_rect_width(new_state);
+ else
+ width = drm_rect_width(&new_state->src) >> 16;
+
eba = drm_plane_state_to_eba(new_state, 0);
/*
@@ -622,8 +627,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
*/
if (ipu_state->use_pre) {
axi_id = ipu_chan_assign_axi_id(ipu_plane->dma);
- ipu_prg_channel_configure(ipu_plane->ipu_ch, axi_id,
- ipu_src_rect_width(new_state),
+ ipu_prg_channel_configure(ipu_plane->ipu_ch, axi_id, width,
drm_rect_height(&new_state->src) >> 16,
fb->pitches[0], fb->format->format,
fb->modifier, &eba);
@@ -678,9 +682,8 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
break;
}
- ipu_dmfc_config_wait4eot(ipu_plane->dmfc, ALIGN(drm_rect_width(dst), 8));
+ ipu_dmfc_config_wait4eot(ipu_plane->dmfc, width);
- width = ipu_src_rect_width(new_state);
height = drm_rect_height(&new_state->src) >> 16;
info = drm_format_info(fb->format->format);
ipu_calculate_bursts(width, info->cpp[0], fb->pitches[0],
@@ -744,8 +747,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
ipu_cpmem_set_burstsize(ipu_plane->ipu_ch, 16);
ipu_cpmem_zero(ipu_plane->alpha_ch);
- ipu_cpmem_set_resolution(ipu_plane->alpha_ch,
- ipu_src_rect_width(new_state),
+ ipu_cpmem_set_resolution(ipu_plane->alpha_ch, width,
drm_rect_height(&new_state->src) >> 16);
ipu_cpmem_set_format_passthrough(ipu_plane->alpha_ch, 8);
ipu_cpmem_set_high_priority(ipu_plane->alpha_ch);
diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c
index d4b907889a21d1..cd399b0b718149 100644
--- a/drivers/gpu/drm/meson/meson_viu.c
+++ b/drivers/gpu/drm/meson/meson_viu.c
@@ -436,15 +436,14 @@ void meson_viu_init(struct meson_drm *priv)
/* Initialize OSD1 fifo control register */
reg = VIU_OSD_DDR_PRIORITY_URGENT |
- VIU_OSD_HOLD_FIFO_LINES(31) |
VIU_OSD_FIFO_DEPTH_VAL(32) | /* fifo_depth_val: 32*8=256 */
VIU_OSD_WORDS_PER_BURST(4) | /* 4 words in 1 burst */
VIU_OSD_FIFO_LIMITS(2); /* fifo_lim: 2*16=32 */
if (meson_vpu_is_compatible(priv, VPU_COMPATIBLE_G12A))
- reg |= VIU_OSD_BURST_LENGTH_32;
+ reg |= (VIU_OSD_BURST_LENGTH_32 | VIU_OSD_HOLD_FIFO_LINES(31));
else
- reg |= VIU_OSD_BURST_LENGTH_64;
+ reg |= (VIU_OSD_BURST_LENGTH_64 | VIU_OSD_HOLD_FIFO_LINES(4));
writel_relaxed(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT));
writel_relaxed(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT));
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
index 6484b97c5344f7..f3c9600221d48c 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
@@ -876,7 +876,8 @@ static void a6xx_gmu_rpmh_off(struct a6xx_gmu *gmu)
#define GBIF_CLIENT_HALT_MASK BIT(0)
#define GBIF_ARB_HALT_MASK BIT(1)
-static void a6xx_bus_clear_pending_transactions(struct adreno_gpu *adreno_gpu)
+static void a6xx_bus_clear_pending_transactions(struct adreno_gpu *adreno_gpu,
+ bool gx_off)
{
struct msm_gpu *gpu = &adreno_gpu->base;
@@ -889,9 +890,11 @@ static void a6xx_bus_clear_pending_transactions(struct adreno_gpu *adreno_gpu)
return;
}
- /* Halt the gx side of GBIF */
- gpu_write(gpu, REG_A6XX_RBBM_GBIF_HALT, 1);
- spin_until(gpu_read(gpu, REG_A6XX_RBBM_GBIF_HALT_ACK) & 1);
+ if (gx_off) {
+ /* Halt the gx side of GBIF */
+ gpu_write(gpu, REG_A6XX_RBBM_GBIF_HALT, 1);
+ spin_until(gpu_read(gpu, REG_A6XX_RBBM_GBIF_HALT_ACK) & 1);
+ }
/* Halt new client requests on GBIF */
gpu_write(gpu, REG_A6XX_GBIF_HALT, GBIF_CLIENT_HALT_MASK);
@@ -929,7 +932,7 @@ static void a6xx_gmu_force_off(struct a6xx_gmu *gmu)
/* Halt the gmu cm3 core */
gmu_write(gmu, REG_A6XX_GMU_CM3_SYSRESET, 1);
- a6xx_bus_clear_pending_transactions(adreno_gpu);
+ a6xx_bus_clear_pending_transactions(adreno_gpu, true);
/* Reset GPU core blocks */
gpu_write(gpu, REG_A6XX_RBBM_SW_RESET_CMD, 1);
@@ -1083,7 +1086,7 @@ static void a6xx_gmu_shutdown(struct a6xx_gmu *gmu)
return;
}
- a6xx_bus_clear_pending_transactions(adreno_gpu);
+ a6xx_bus_clear_pending_transactions(adreno_gpu, a6xx_gpu->hung);
/* tell the GMU we want to slumber */
ret = a6xx_gmu_notify_slumber(gmu);
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
index 36c8fb699b56be..3be0f2928b57c1 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
@@ -1270,6 +1270,12 @@ static void a6xx_recover(struct msm_gpu *gpu)
if (hang_debug)
a6xx_dump(gpu);
+ /*
+ * To handle recovery specific sequences during the rpm suspend we are
+ * about to trigger
+ */
+ a6xx_gpu->hung = true;
+
/* Halt SQE first */
gpu_write(gpu, REG_A6XX_CP_SQE_CNTL, 3);
@@ -1312,6 +1318,7 @@ static void a6xx_recover(struct msm_gpu *gpu)
mutex_unlock(&gpu->active_lock);
msm_gpu_hw_init(gpu);
+ a6xx_gpu->hung = false;
}
static const char *a6xx_uche_fault_block(struct msm_gpu *gpu, u32 mid)
diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
index ab853f61db6322..eea2e60ce3b7bb 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.h
@@ -32,6 +32,7 @@ struct a6xx_gpu {
void *llc_slice;
void *htw_llc_slice;
bool have_mmu500;
+ bool hung;
};
#define to_a6xx_gpu(x) container_of(x, struct a6xx_gpu, base)
diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c
index 628806423f7d2d..36f062c7582f9a 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_device.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_device.c
@@ -551,13 +551,14 @@ static int adreno_bind(struct device *dev, struct device *master, void *data)
return 0;
}
+static int adreno_system_suspend(struct device *dev);
static void adreno_unbind(struct device *dev, struct device *master,
void *data)
{
struct msm_drm_private *priv = dev_get_drvdata(master);
struct msm_gpu *gpu = dev_to_gpu(dev);
- pm_runtime_force_suspend(dev);
+ WARN_ON_ONCE(adreno_system_suspend(dev));
gpu->funcs->destroy(gpu);
priv->gpu_pdev = NULL;
@@ -609,7 +610,7 @@ static int adreno_remove(struct platform_device *pdev)
static void adreno_shutdown(struct platform_device *pdev)
{
- pm_runtime_force_suspend(&pdev->dev);
+ WARN_ON_ONCE(adreno_system_suspend(&pdev->dev));
}
static const struct of_device_id dt_match[] = {
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
index 57586c794b84bd..3605f095b2de24 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c
@@ -352,6 +352,8 @@ int adreno_set_param(struct msm_gpu *gpu, struct msm_file_private *ctx,
/* Ensure string is null terminated: */
str[len] = '\0';
+ mutex_lock(&gpu->lock);
+
if (param == MSM_PARAM_COMM) {
paramp = &ctx->comm;
} else {
@@ -361,6 +363,8 @@ int adreno_set_param(struct msm_gpu *gpu, struct msm_file_private *ctx,
kfree(*paramp);
*paramp = str;
+ mutex_unlock(&gpu->lock);
+
return 0;
}
case MSM_PARAM_SYSPROF:
diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
index 5d4b1c95033ff5..b4f9b1343d6379 100644
--- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h
+++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h
@@ -29,11 +29,9 @@ enum {
ADRENO_FW_MAX,
};
-enum adreno_quirks {
- ADRENO_QUIRK_TWO_PASS_USE_WFI = 1,
- ADRENO_QUIRK_FAULT_DETECT_MASK = 2,
- ADRENO_QUIRK_LMLOADKILL_DISABLE = 3,
-};
+#define ADRENO_QUIRK_TWO_PASS_USE_WFI BIT(0)
+#define ADRENO_QUIRK_FAULT_DETECT_MASK BIT(1)
+#define ADRENO_QUIRK_LMLOADKILL_DISABLE BIT(2)
struct adreno_rev {
uint8_t core;
@@ -65,7 +63,7 @@ struct adreno_info {
const char *name;
const char *fw[ADRENO_FW_MAX];
uint32_t gmem;
- enum adreno_quirks quirks;
+ u64 quirks;
struct msm_gpu *(*init)(struct drm_device *dev);
const char *zapfw;
u32 inactive_period;
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
index 7cbcef6efe1716..62f6ff6abf4106 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
@@ -132,7 +132,6 @@ static void dpu_encoder_phys_wb_set_qos(struct dpu_encoder_phys *phys_enc)
* dpu_encoder_phys_wb_setup_fb - setup output framebuffer
* @phys_enc: Pointer to physical encoder
* @fb: Pointer to output framebuffer
- * @wb_roi: Pointer to output region of interest
*/
static void dpu_encoder_phys_wb_setup_fb(struct dpu_encoder_phys *phys_enc,
struct drm_framebuffer *fb)
@@ -692,7 +691,7 @@ static void dpu_encoder_phys_wb_init_ops(struct dpu_encoder_phys_ops *ops)
/**
* dpu_encoder_phys_wb_init - initialize writeback encoder
- * @init: Pointer to init info structure with initialization params
+ * @p: Pointer to init info structure with initialization params
*/
struct dpu_encoder_phys *dpu_encoder_phys_wb_init(
struct dpu_enc_phys_init_params *p)
diff --git a/drivers/gpu/drm/msm/dp/dp_aux.c b/drivers/gpu/drm/msm/dp/dp_aux.c
index d030a93a08c361..cc3efed593aa19 100644
--- a/drivers/gpu/drm/msm/dp/dp_aux.c
+++ b/drivers/gpu/drm/msm/dp/dp_aux.c
@@ -423,6 +423,10 @@ void dp_aux_isr(struct drm_dp_aux *dp_aux)
isr = dp_catalog_aux_get_irq(aux->catalog);
+ /* no interrupts pending, return immediately */
+ if (!isr)
+ return;
+
if (!aux->cmd_busy)
return;
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
index 4d3fdc806befda..97372bb241d897 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
@@ -532,11 +532,19 @@ static int msm_hdmi_dev_probe(struct platform_device *pdev)
ret = devm_pm_runtime_enable(&pdev->dev);
if (ret)
- return ret;
+ goto err_put_phy;
platform_set_drvdata(pdev, hdmi);
- return component_add(&pdev->dev, &msm_hdmi_ops);
+ ret = component_add(&pdev->dev, &msm_hdmi_ops);
+ if (ret)
+ goto err_put_phy;
+
+ return 0;
+
+err_put_phy:
+ msm_hdmi_put_phy(hdmi);
+ return ret;
}
static int msm_hdmi_dev_remove(struct platform_device *pdev)
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 8b0b0ac74a6f1d..45e81eb148a8d0 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -1278,7 +1278,7 @@ void msm_drv_shutdown(struct platform_device *pdev)
* msm_drm_init, drm_dev->registered is used as an indicator that the
* shutdown will be successful.
*/
- if (drm && drm->registered)
+ if (drm && drm->registered && priv->kms)
drm_atomic_helper_shutdown(drm);
}
diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c
index 30ed45af76ade1..3802495003258a 100644
--- a/drivers/gpu/drm/msm/msm_gpu.c
+++ b/drivers/gpu/drm/msm/msm_gpu.c
@@ -335,6 +335,8 @@ static void get_comm_cmdline(struct msm_gem_submit *submit, char **comm, char **
struct msm_file_private *ctx = submit->queue->ctx;
struct task_struct *task;
+ WARN_ON(!mutex_is_locked(&submit->gpu->lock));
+
/* Note that kstrdup will return NULL if argument is NULL: */
*comm = kstrdup(ctx->comm, GFP_KERNEL);
*cmd = kstrdup(ctx->cmdline, GFP_KERNEL);
diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h
index 651786bc55e5a8..732295e2568345 100644
--- a/drivers/gpu/drm/msm/msm_gpu.h
+++ b/drivers/gpu/drm/msm/msm_gpu.h
@@ -376,10 +376,18 @@ struct msm_file_private {
*/
int sysprof;
- /** comm: Overridden task comm, see MSM_PARAM_COMM */
+ /**
+ * comm: Overridden task comm, see MSM_PARAM_COMM
+ *
+ * Accessed under msm_gpu::lock
+ */
char *comm;
- /** cmdline: Overridden task cmdline, see MSM_PARAM_CMDLINE */
+ /**
+ * cmdline: Overridden task cmdline, see MSM_PARAM_CMDLINE
+ *
+ * Accessed under msm_gpu::lock
+ */
char *cmdline;
/**
diff --git a/drivers/gpu/drm/msm/msm_mdss.c b/drivers/gpu/drm/msm/msm_mdss.c
index 86b28add1ffff4..2527afef9c199b 100644
--- a/drivers/gpu/drm/msm/msm_mdss.c
+++ b/drivers/gpu/drm/msm/msm_mdss.c
@@ -47,15 +47,17 @@ struct msm_mdss {
static int msm_mdss_parse_data_bus_icc_path(struct device *dev,
struct msm_mdss *msm_mdss)
{
- struct icc_path *path0 = of_icc_get(dev, "mdp0-mem");
- struct icc_path *path1 = of_icc_get(dev, "mdp1-mem");
+ struct icc_path *path0;
+ struct icc_path *path1;
+ path0 = of_icc_get(dev, "mdp0-mem");
if (IS_ERR_OR_NULL(path0))
return PTR_ERR_OR_ZERO(path0);
msm_mdss->path[0] = path0;
msm_mdss->num_paths = 1;
+ path1 = of_icc_get(dev, "mdp1-mem");
if (!IS_ERR_OR_NULL(path1)) {
msm_mdss->path[1] = path1;
msm_mdss->num_paths++;
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h
index 40768373cdd90d..c5a4f49ee20654 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/fb.h
@@ -97,6 +97,7 @@ int gp100_fb_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct n
int gp102_fb_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_fb **);
int gp10b_fb_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_fb **);
int gv100_fb_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_fb **);
+int tu102_fb_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_fb **);
int ga100_fb_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_fb **);
int ga102_fb_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_fb **);
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
deleted file mode 100644
index e87de7906f7808..00000000000000
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ /dev/null
@@ -1,613 +0,0 @@
-/*
- * Copyright © 2007 David Airlie
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * David Airlie
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/tty.h>
-#include <linux/sysrq.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/screen_info.h>
-#include <linux/vga_switcheroo.h>
-#include <linux/console.h>
-
-#include <drm/drm_crtc.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_probe_helper.h>
-#include <drm/drm_fb_helper.h>
-#include <drm/drm_fourcc.h>
-#include <drm/drm_atomic.h>
-
-#include "nouveau_drv.h"
-#include "nouveau_gem.h"
-#include "nouveau_bo.h"
-#include "nouveau_fbcon.h"
-#include "nouveau_chan.h"
-#include "nouveau_vmm.h"
-
-#include "nouveau_crtc.h"
-
-MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration");
-int nouveau_nofbaccel = 0;
-module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400);
-
-MODULE_PARM_DESC(fbcon_bpp, "fbcon bits-per-pixel (default: auto)");
-static int nouveau_fbcon_bpp;
-module_param_named(fbcon_bpp, nouveau_fbcon_bpp, int, 0400);
-
-static void
-nouveau_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
-{
- struct nouveau_fbdev *fbcon = info->par;
- struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
- struct nvif_device *device = &drm->client.device;
- int ret;
-
- if (info->state != FBINFO_STATE_RUNNING)
- return;
-
- ret = -ENODEV;
- if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) &&
- mutex_trylock(&drm->client.mutex)) {
- if (device->info.family < NV_DEVICE_INFO_V0_TESLA)
- ret = nv04_fbcon_fillrect(info, rect);
- else
- if (device->info.family < NV_DEVICE_INFO_V0_FERMI)
- ret = nv50_fbcon_fillrect(info, rect);
- else
- ret = nvc0_fbcon_fillrect(info, rect);
- mutex_unlock(&drm->client.mutex);
- }
-
- if (ret == 0)
- return;
-
- if (ret != -ENODEV)
- nouveau_fbcon_gpu_lockup(info);
- drm_fb_helper_cfb_fillrect(info, rect);
-}
-
-static void
-nouveau_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *image)
-{
- struct nouveau_fbdev *fbcon = info->par;
- struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
- struct nvif_device *device = &drm->client.device;
- int ret;
-
- if (info->state != FBINFO_STATE_RUNNING)
- return;
-
- ret = -ENODEV;
- if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) &&
- mutex_trylock(&drm->client.mutex)) {
- if (device->info.family < NV_DEVICE_INFO_V0_TESLA)
- ret = nv04_fbcon_copyarea(info, image);
- else
- if (device->info.family < NV_DEVICE_INFO_V0_FERMI)
- ret = nv50_fbcon_copyarea(info, image);
- else
- ret = nvc0_fbcon_copyarea(info, image);
- mutex_unlock(&drm->client.mutex);
- }
-
- if (ret == 0)
- return;
-
- if (ret != -ENODEV)
- nouveau_fbcon_gpu_lockup(info);
- drm_fb_helper_cfb_copyarea(info, image);
-}
-
-static void
-nouveau_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
-{
- struct nouveau_fbdev *fbcon = info->par;
- struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
- struct nvif_device *device = &drm->client.device;
- int ret;
-
- if (info->state != FBINFO_STATE_RUNNING)
- return;
-
- ret = -ENODEV;
- if (!in_interrupt() && !(info->flags & FBINFO_HWACCEL_DISABLED) &&
- mutex_trylock(&drm->client.mutex)) {
- if (device->info.family < NV_DEVICE_INFO_V0_TESLA)
- ret = nv04_fbcon_imageblit(info, image);
- else
- if (device->info.family < NV_DEVICE_INFO_V0_FERMI)
- ret = nv50_fbcon_imageblit(info, image);
- else
- ret = nvc0_fbcon_imageblit(info, image);
- mutex_unlock(&drm->client.mutex);
- }
-
- if (ret == 0)
- return;
-
- if (ret != -ENODEV)
- nouveau_fbcon_gpu_lockup(info);
- drm_fb_helper_cfb_imageblit(info, image);
-}
-
-static int
-nouveau_fbcon_sync(struct fb_info *info)
-{
- struct nouveau_fbdev *fbcon = info->par;
- struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
- struct nouveau_channel *chan = drm->channel;
- int ret;
-
- if (!chan || !chan->accel_done || in_interrupt() ||
- info->state != FBINFO_STATE_RUNNING ||
- info->flags & FBINFO_HWACCEL_DISABLED)
- return 0;
-
- if (!mutex_trylock(&drm->client.mutex))
- return 0;
-
- ret = nouveau_channel_idle(chan);
- mutex_unlock(&drm->client.mutex);
- if (ret) {
- nouveau_fbcon_gpu_lockup(info);
- return 0;
- }
-
- chan->accel_done = false;
- return 0;
-}
-
-static int
-nouveau_fbcon_open(struct fb_info *info, int user)
-{
- struct nouveau_fbdev *fbcon = info->par;
- struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
- int ret = pm_runtime_get_sync(drm->dev->dev);
- if (ret < 0 && ret != -EACCES) {
- pm_runtime_put(drm->dev->dev);
- return ret;
- }
- return 0;
-}
-
-static int
-nouveau_fbcon_release(struct fb_info *info, int user)
-{
- struct nouveau_fbdev *fbcon = info->par;
- struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
- pm_runtime_put(drm->dev->dev);
- return 0;
-}
-
-static const struct fb_ops nouveau_fbcon_ops = {
- .owner = THIS_MODULE,
- DRM_FB_HELPER_DEFAULT_OPS,
- .fb_open = nouveau_fbcon_open,
- .fb_release = nouveau_fbcon_release,
- .fb_fillrect = nouveau_fbcon_fillrect,
- .fb_copyarea = nouveau_fbcon_copyarea,
- .fb_imageblit = nouveau_fbcon_imageblit,
- .fb_sync = nouveau_fbcon_sync,
-};
-
-static const struct fb_ops nouveau_fbcon_sw_ops = {
- .owner = THIS_MODULE,
- DRM_FB_HELPER_DEFAULT_OPS,
- .fb_open = nouveau_fbcon_open,
- .fb_release = nouveau_fbcon_release,
- .fb_fillrect = drm_fb_helper_cfb_fillrect,
- .fb_copyarea = drm_fb_helper_cfb_copyarea,
- .fb_imageblit = drm_fb_helper_cfb_imageblit,
-};
-
-void
-nouveau_fbcon_accel_save_disable(struct drm_device *dev)
-{
- struct nouveau_drm *drm = nouveau_drm(dev);
- if (drm->fbcon && drm->fbcon->helper.info) {
- drm->fbcon->saved_flags = drm->fbcon->helper.info->flags;
- drm->fbcon->helper.info->flags |= FBINFO_HWACCEL_DISABLED;
- }
-}
-
-void
-nouveau_fbcon_accel_restore(struct drm_device *dev)
-{
- struct nouveau_drm *drm = nouveau_drm(dev);
- if (drm->fbcon && drm->fbcon->helper.info)
- drm->fbcon->helper.info->flags = drm->fbcon->saved_flags;
-}
-
-static void
-nouveau_fbcon_accel_fini(struct drm_device *dev)
-{
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_fbdev *fbcon = drm->fbcon;
- if (fbcon && drm->channel) {
- console_lock();
- if (fbcon->helper.info)
- fbcon->helper.info->flags |= FBINFO_HWACCEL_DISABLED;
- console_unlock();
- nouveau_channel_idle(drm->channel);
- nvif_object_dtor(&fbcon->twod);
- nvif_object_dtor(&fbcon->blit);
- nvif_object_dtor(&fbcon->gdi);
- nvif_object_dtor(&fbcon->patt);
- nvif_object_dtor(&fbcon->rop);
- nvif_object_dtor(&fbcon->clip);
- nvif_object_dtor(&fbcon->surf2d);
- }
-}
-
-static void
-nouveau_fbcon_accel_init(struct drm_device *dev)
-{
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_fbdev *fbcon = drm->fbcon;
- struct fb_info *info = fbcon->helper.info;
- int ret;
-
- if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA)
- ret = nv04_fbcon_accel_init(info);
- else
- if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI)
- ret = nv50_fbcon_accel_init(info);
- else
- ret = nvc0_fbcon_accel_init(info);
-
- if (ret == 0)
- info->fbops = &nouveau_fbcon_ops;
-}
-
-static void
-nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbdev *fbcon)
-{
- struct fb_info *info = fbcon->helper.info;
- struct fb_fillrect rect;
-
- /* Clear the entire fbcon. The drm will program every connector
- * with it's preferred mode. If the sizes differ, one display will
- * quite likely have garbage around the console.
- */
- rect.dx = rect.dy = 0;
- rect.width = info->var.xres_virtual;
- rect.height = info->var.yres_virtual;
- rect.color = 0;
- rect.rop = ROP_COPY;
- info->fbops->fb_fillrect(info, &rect);
-}
-
-static int
-nouveau_fbcon_create(struct drm_fb_helper *helper,
- struct drm_fb_helper_surface_size *sizes)
-{
- struct nouveau_fbdev *fbcon =
- container_of(helper, struct nouveau_fbdev, helper);
- struct drm_device *dev = fbcon->helper.dev;
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nvif_device *device = &drm->client.device;
- struct fb_info *info;
- struct drm_framebuffer *fb;
- struct nouveau_channel *chan;
- struct nouveau_bo *nvbo;
- struct drm_mode_fb_cmd2 mode_cmd = {};
- int ret;
-
- mode_cmd.width = sizes->surface_width;
- mode_cmd.height = sizes->surface_height;
-
- mode_cmd.pitches[0] = mode_cmd.width * (sizes->surface_bpp >> 3);
- mode_cmd.pitches[0] = roundup(mode_cmd.pitches[0], 256);
-
- mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
- sizes->surface_depth);
-
- ret = nouveau_gem_new(&drm->client, mode_cmd.pitches[0] *
- mode_cmd.height, 0, NOUVEAU_GEM_DOMAIN_VRAM,
- 0, 0x0000, &nvbo);
- if (ret) {
- NV_ERROR(drm, "failed to allocate framebuffer\n");
- goto out;
- }
-
- ret = nouveau_framebuffer_new(dev, &mode_cmd, &nvbo->bo.base, &fb);
- if (ret)
- goto out_unref;
-
- ret = nouveau_bo_pin(nvbo, NOUVEAU_GEM_DOMAIN_VRAM, false);
- if (ret) {
- NV_ERROR(drm, "failed to pin fb: %d\n", ret);
- goto out_unref;
- }
-
- ret = nouveau_bo_map(nvbo);
- if (ret) {
- NV_ERROR(drm, "failed to map fb: %d\n", ret);
- goto out_unpin;
- }
-
- chan = nouveau_nofbaccel ? NULL : drm->channel;
- if (chan && device->info.family >= NV_DEVICE_INFO_V0_TESLA) {
- ret = nouveau_vma_new(nvbo, chan->vmm, &fbcon->vma);
- if (ret) {
- NV_ERROR(drm, "failed to map fb into chan: %d\n", ret);
- chan = NULL;
- }
- }
-
- info = drm_fb_helper_alloc_info(helper);
- if (IS_ERR(info)) {
- ret = PTR_ERR(info);
- goto out_unlock;
- }
-
- /* setup helper */
- fbcon->helper.fb = fb;
-
- if (!chan)
- info->flags = FBINFO_HWACCEL_DISABLED;
- else
- info->flags = FBINFO_HWACCEL_COPYAREA |
- FBINFO_HWACCEL_FILLRECT |
- FBINFO_HWACCEL_IMAGEBLIT;
- info->fbops = &nouveau_fbcon_sw_ops;
- info->fix.smem_start = nvbo->bo.resource->bus.offset;
- info->fix.smem_len = nvbo->bo.base.size;
-
- info->screen_base = nvbo_kmap_obj_iovirtual(nvbo);
- info->screen_size = nvbo->bo.base.size;
-
- drm_fb_helper_fill_info(info, &fbcon->helper, sizes);
-
- /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
-
- if (chan)
- nouveau_fbcon_accel_init(dev);
- nouveau_fbcon_zfill(dev, fbcon);
-
- /* To allow resizeing without swapping buffers */
- NV_INFO(drm, "allocated %dx%d fb: 0x%llx, bo %p\n",
- fb->width, fb->height, nvbo->offset, nvbo);
-
- if (dev_is_pci(dev->dev))
- vga_switcheroo_client_fb_set(to_pci_dev(dev->dev), info);
-
- return 0;
-
-out_unlock:
- if (chan)
- nouveau_vma_del(&fbcon->vma);
- nouveau_bo_unmap(nvbo);
-out_unpin:
- nouveau_bo_unpin(nvbo);
-out_unref:
- nouveau_bo_ref(NULL, &nvbo);
-out:
- return ret;
-}
-
-static int
-nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon)
-{
- struct drm_framebuffer *fb = fbcon->helper.fb;
- struct nouveau_bo *nvbo;
-
- drm_fb_helper_unregister_info(&fbcon->helper);
- drm_fb_helper_fini(&fbcon->helper);
-
- if (fb && fb->obj[0]) {
- nvbo = nouveau_gem_object(fb->obj[0]);
- nouveau_vma_del(&fbcon->vma);
- nouveau_bo_unmap(nvbo);
- nouveau_bo_unpin(nvbo);
- drm_framebuffer_put(fb);
- }
-
- return 0;
-}
-
-void nouveau_fbcon_gpu_lockup(struct fb_info *info)
-{
- struct nouveau_fbdev *fbcon = info->par;
- struct nouveau_drm *drm = nouveau_drm(fbcon->helper.dev);
-
- NV_ERROR(drm, "GPU lockup - switching to software fbcon\n");
- info->flags |= FBINFO_HWACCEL_DISABLED;
-}
-
-static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
- .fb_probe = nouveau_fbcon_create,
-};
-
-static void
-nouveau_fbcon_set_suspend_work(struct work_struct *work)
-{
- struct nouveau_drm *drm = container_of(work, typeof(*drm), fbcon_work);
- int state = READ_ONCE(drm->fbcon_new_state);
-
- if (state == FBINFO_STATE_RUNNING)
- pm_runtime_get_sync(drm->dev->dev);
-
- console_lock();
- if (state == FBINFO_STATE_RUNNING)
- nouveau_fbcon_accel_restore(drm->dev);
- drm_fb_helper_set_suspend(&drm->fbcon->helper, state);
- if (state != FBINFO_STATE_RUNNING)
- nouveau_fbcon_accel_save_disable(drm->dev);
- console_unlock();
-
- if (state == FBINFO_STATE_RUNNING) {
- nouveau_fbcon_hotplug_resume(drm->fbcon);
- pm_runtime_mark_last_busy(drm->dev->dev);
- pm_runtime_put_autosuspend(drm->dev->dev);
- }
-}
-
-void
-nouveau_fbcon_set_suspend(struct drm_device *dev, int state)
-{
- struct nouveau_drm *drm = nouveau_drm(dev);
-
- if (!drm->fbcon)
- return;
-
- drm->fbcon_new_state = state;
- /* Since runtime resume can happen as a result of a sysfs operation,
- * it's possible we already have the console locked. So handle fbcon
- * init/deinit from a seperate work thread
- */
- schedule_work(&drm->fbcon_work);
-}
-
-void
-nouveau_fbcon_output_poll_changed(struct drm_device *dev)
-{
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_fbdev *fbcon = drm->fbcon;
- int ret;
-
- if (!fbcon)
- return;
-
- mutex_lock(&fbcon->hotplug_lock);
-
- ret = pm_runtime_get(dev->dev);
- if (ret == 1 || ret == -EACCES) {
- drm_fb_helper_hotplug_event(&fbcon->helper);
-
- pm_runtime_mark_last_busy(dev->dev);
- pm_runtime_put_autosuspend(dev->dev);
- } else if (ret == 0) {
- /* If the GPU was already in the process of suspending before
- * this event happened, then we can't block here as we'll
- * deadlock the runtime pmops since they wait for us to
- * finish. So, just defer this event for when we runtime
- * resume again. It will be handled by fbcon_work.
- */
- NV_DEBUG(drm, "fbcon HPD event deferred until runtime resume\n");
- fbcon->hotplug_waiting = true;
- pm_runtime_put_noidle(drm->dev->dev);
- } else {
- DRM_WARN("fbcon HPD event lost due to RPM failure: %d\n",
- ret);
- }
-
- mutex_unlock(&fbcon->hotplug_lock);
-}
-
-void
-nouveau_fbcon_hotplug_resume(struct nouveau_fbdev *fbcon)
-{
- struct nouveau_drm *drm;
-
- if (!fbcon)
- return;
- drm = nouveau_drm(fbcon->helper.dev);
-
- mutex_lock(&fbcon->hotplug_lock);
- if (fbcon->hotplug_waiting) {
- fbcon->hotplug_waiting = false;
-
- NV_DEBUG(drm, "Handling deferred fbcon HPD events\n");
- drm_fb_helper_hotplug_event(&fbcon->helper);
- }
- mutex_unlock(&fbcon->hotplug_lock);
-}
-
-int
-nouveau_fbcon_init(struct drm_device *dev)
-{
- struct nouveau_drm *drm = nouveau_drm(dev);
- struct nouveau_fbdev *fbcon;
- int preferred_bpp = nouveau_fbcon_bpp;
- int ret;
-
- if (!dev->mode_config.num_crtc ||
- (to_pci_dev(dev->dev)->class >> 8) != PCI_CLASS_DISPLAY_VGA)
- return 0;
-
- fbcon = kzalloc(sizeof(struct nouveau_fbdev), GFP_KERNEL);
- if (!fbcon)
- return -ENOMEM;
-
- drm->fbcon = fbcon;
- INIT_WORK(&drm->fbcon_work, nouveau_fbcon_set_suspend_work);
- mutex_init(&fbcon->hotplug_lock);
-
- drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs);
-
- ret = drm_fb_helper_init(dev, &fbcon->helper);
- if (ret)
- goto free;
-
- if (preferred_bpp != 8 && preferred_bpp != 16 && preferred_bpp != 32) {
- if (drm->client.device.info.ram_size <= 32 * 1024 * 1024)
- preferred_bpp = 8;
- else
- if (drm->client.device.info.ram_size <= 64 * 1024 * 1024)
- preferred_bpp = 16;
- else
- preferred_bpp = 32;
- }
-
- /* disable all the possible outputs/crtcs before entering KMS mode */
- if (!drm_drv_uses_atomic_modeset(dev))
- drm_helper_disable_unused_functions(dev);
-
- ret = drm_fb_helper_initial_config(&fbcon->helper, preferred_bpp);
- if (ret)
- goto fini;
-
- if (fbcon->helper.info)
- fbcon->helper.info->pixmap.buf_align = 4;
- return 0;
-
-fini:
- drm_fb_helper_fini(&fbcon->helper);
-free:
- kfree(fbcon);
- drm->fbcon = NULL;
- return ret;
-}
-
-void
-nouveau_fbcon_fini(struct drm_device *dev)
-{
- struct nouveau_drm *drm = nouveau_drm(dev);
-
- if (!drm->fbcon)
- return;
-
- drm_kms_helper_poll_fini(dev);
- nouveau_fbcon_accel_fini(dev);
- nouveau_fbcon_destroy(dev, drm->fbcon);
- kfree(drm->fbcon);
- drm->fbcon = NULL;
-}
diff --git a/drivers/gpu/drm/nouveau/nvkm/core/firmware.c b/drivers/gpu/drm/nouveau/nvkm/core/firmware.c
index fcf2a002f6cb2d..91fb494d400935 100644
--- a/drivers/gpu/drm/nouveau/nvkm/core/firmware.c
+++ b/drivers/gpu/drm/nouveau/nvkm/core/firmware.c
@@ -151,6 +151,9 @@ nvkm_firmware_mem_page(struct nvkm_memory *memory)
static enum nvkm_memory_target
nvkm_firmware_mem_target(struct nvkm_memory *memory)
{
+ if (nvkm_firmware_mem(memory)->device->func->tegra)
+ return NVKM_MEM_TARGET_NCOH;
+
return NVKM_MEM_TARGET_HOST;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
index 364fea320cb37c..1c81e5b34d29d8 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
@@ -2405,7 +2405,7 @@ nv162_chipset = {
.bus = { 0x00000001, gf100_bus_new },
.devinit = { 0x00000001, tu102_devinit_new },
.fault = { 0x00000001, tu102_fault_new },
- .fb = { 0x00000001, gv100_fb_new },
+ .fb = { 0x00000001, tu102_fb_new },
.fuse = { 0x00000001, gm107_fuse_new },
.gpio = { 0x00000001, gk104_gpio_new },
.gsp = { 0x00000001, gv100_gsp_new },
@@ -2440,7 +2440,7 @@ nv164_chipset = {
.bus = { 0x00000001, gf100_bus_new },
.devinit = { 0x00000001, tu102_devinit_new },
.fault = { 0x00000001, tu102_fault_new },
- .fb = { 0x00000001, gv100_fb_new },
+ .fb = { 0x00000001, tu102_fb_new },
.fuse = { 0x00000001, gm107_fuse_new },
.gpio = { 0x00000001, gk104_gpio_new },
.gsp = { 0x00000001, gv100_gsp_new },
@@ -2475,7 +2475,7 @@ nv166_chipset = {
.bus = { 0x00000001, gf100_bus_new },
.devinit = { 0x00000001, tu102_devinit_new },
.fault = { 0x00000001, tu102_fault_new },
- .fb = { 0x00000001, gv100_fb_new },
+ .fb = { 0x00000001, tu102_fb_new },
.fuse = { 0x00000001, gm107_fuse_new },
.gpio = { 0x00000001, gk104_gpio_new },
.gsp = { 0x00000001, gv100_gsp_new },
@@ -2510,7 +2510,7 @@ nv167_chipset = {
.bus = { 0x00000001, gf100_bus_new },
.devinit = { 0x00000001, tu102_devinit_new },
.fault = { 0x00000001, tu102_fault_new },
- .fb = { 0x00000001, gv100_fb_new },
+ .fb = { 0x00000001, tu102_fb_new },
.fuse = { 0x00000001, gm107_fuse_new },
.gpio = { 0x00000001, gk104_gpio_new },
.gsp = { 0x00000001, gv100_gsp_new },
@@ -2545,7 +2545,7 @@ nv168_chipset = {
.bus = { 0x00000001, gf100_bus_new },
.devinit = { 0x00000001, tu102_devinit_new },
.fault = { 0x00000001, tu102_fault_new },
- .fb = { 0x00000001, gv100_fb_new },
+ .fb = { 0x00000001, tu102_fb_new },
.fuse = { 0x00000001, gm107_fuse_new },
.gpio = { 0x00000001, gk104_gpio_new },
.gsp = { 0x00000001, gv100_gsp_new },
diff --git a/drivers/gpu/drm/nouveau/nvkm/falcon/gm200.c b/drivers/gpu/drm/nouveau/nvkm/falcon/gm200.c
index 393ade9f7e6c48..b7da3ab44c277d 100644
--- a/drivers/gpu/drm/nouveau/nvkm/falcon/gm200.c
+++ b/drivers/gpu/drm/nouveau/nvkm/falcon/gm200.c
@@ -48,6 +48,16 @@ gm200_flcn_pio_dmem_rd(struct nvkm_falcon *falcon, u8 port, const u8 *img, int l
img += 4;
len -= 4;
}
+
+ /* Sigh. Tegra PMU FW's init message... */
+ if (len) {
+ u32 data = nvkm_falcon_rd32(falcon, 0x1c4 + (port * 8));
+
+ while (len--) {
+ *(u8 *)img++ = data & 0xff;
+ data >>= 8;
+ }
+ }
}
static void
@@ -64,6 +74,8 @@ gm200_flcn_pio_dmem_wr(struct nvkm_falcon *falcon, u8 port, const u8 *img, int l
img += 4;
len -= 4;
}
+
+ WARN_ON(len);
}
static void
@@ -74,7 +86,7 @@ gm200_flcn_pio_dmem_wr_init(struct nvkm_falcon *falcon, u8 port, bool sec, u32 d
const struct nvkm_falcon_func_pio
gm200_flcn_dmem_pio = {
- .min = 4,
+ .min = 1,
.max = 0x100,
.wr_init = gm200_flcn_pio_dmem_wr_init,
.wr = gm200_flcn_pio_dmem_wr,
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c
index 634f64f88fc8be..81a1ad2c88a7e2 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c
@@ -65,10 +65,33 @@ tu102_devinit_pll_set(struct nvkm_devinit *init, u32 type, u32 freq)
return ret;
}
+static int
+tu102_devinit_wait(struct nvkm_device *device)
+{
+ unsigned timeout = 50 + 2000;
+
+ do {
+ if (nvkm_rd32(device, 0x118128) & 0x00000001) {
+ if ((nvkm_rd32(device, 0x118234) & 0x000000ff) == 0xff)
+ return 0;
+ }
+
+ usleep_range(1000, 2000);
+ } while (timeout--);
+
+ return -ETIMEDOUT;
+}
+
int
tu102_devinit_post(struct nvkm_devinit *base, bool post)
{
struct nv50_devinit *init = nv50_devinit(base);
+ int ret;
+
+ ret = tu102_devinit_wait(init->base.subdev.device);
+ if (ret)
+ return ret;
+
gm200_devinit_preos(init, post);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild
index 5d0bab8ecb433d..6ba5120a2ebe1d 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/Kbuild
@@ -32,6 +32,7 @@ nvkm-y += nvkm/subdev/fb/gp100.o
nvkm-y += nvkm/subdev/fb/gp102.o
nvkm-y += nvkm/subdev/fb/gp10b.o
nvkm-y += nvkm/subdev/fb/gv100.o
+nvkm-y += nvkm/subdev/fb/tu102.o
nvkm-y += nvkm/subdev/fb/ga100.o
nvkm-y += nvkm/subdev/fb/ga102.o
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c
index 8b7c8ea5e8a5f3..5a21b0ae459580 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/ga102.c
@@ -40,12 +40,6 @@ ga102_fb_vpr_scrub(struct nvkm_fb *fb)
return ret;
}
-static bool
-ga102_fb_vpr_scrub_required(struct nvkm_fb *fb)
-{
- return (nvkm_rd32(fb->subdev.device, 0x1fa80c) & 0x00000010) != 0;
-}
-
static const struct nvkm_fb_func
ga102_fb = {
.dtor = gf100_fb_dtor,
@@ -56,7 +50,7 @@ ga102_fb = {
.sysmem.flush_page_init = gf100_fb_sysmem_flush_page_init,
.ram_new = ga102_ram_new,
.default_bigpage = 16,
- .vpr.scrub_required = ga102_fb_vpr_scrub_required,
+ .vpr.scrub_required = tu102_fb_vpr_scrub_required,
.vpr.scrub = ga102_fb_vpr_scrub,
};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c
index 1f0126437c1aa1..0e3c0a8f5d716e 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/gv100.c
@@ -49,8 +49,3 @@ gv100_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, s
}
MODULE_FIRMWARE("nvidia/gv100/nvdec/scrubber.bin");
-MODULE_FIRMWARE("nvidia/tu102/nvdec/scrubber.bin");
-MODULE_FIRMWARE("nvidia/tu104/nvdec/scrubber.bin");
-MODULE_FIRMWARE("nvidia/tu106/nvdec/scrubber.bin");
-MODULE_FIRMWARE("nvidia/tu116/nvdec/scrubber.bin");
-MODULE_FIRMWARE("nvidia/tu117/nvdec/scrubber.bin");
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h
index ac03eac0f26169..f517751f94acdd 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/priv.h
@@ -89,4 +89,6 @@ bool gp102_fb_vpr_scrub_required(struct nvkm_fb *);
int gp102_fb_vpr_scrub(struct nvkm_fb *);
int gv100_fb_init_page(struct nvkm_fb *);
+
+bool tu102_fb_vpr_scrub_required(struct nvkm_fb *);
#endif
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/fb/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/tu102.c
new file mode 100644
index 00000000000000..be82af0364ee44
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/fb/tu102.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2018 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "gf100.h"
+#include "ram.h"
+
+bool
+tu102_fb_vpr_scrub_required(struct nvkm_fb *fb)
+{
+ return (nvkm_rd32(fb->subdev.device, 0x1fa80c) & 0x00000010) != 0;
+}
+
+static const struct nvkm_fb_func
+tu102_fb = {
+ .dtor = gf100_fb_dtor,
+ .oneinit = gf100_fb_oneinit,
+ .init = gm200_fb_init,
+ .init_page = gv100_fb_init_page,
+ .init_unkn = gp100_fb_init_unkn,
+ .sysmem.flush_page_init = gf100_fb_sysmem_flush_page_init,
+ .vpr.scrub_required = tu102_fb_vpr_scrub_required,
+ .vpr.scrub = gp102_fb_vpr_scrub,
+ .ram_new = gp100_ram_new,
+ .default_bigpage = 16,
+};
+
+int
+tu102_fb_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst, struct nvkm_fb **pfb)
+{
+ return gp102_fb_new_(&tu102_fb, device, type, inst, pfb);
+}
+
+MODULE_FIRMWARE("nvidia/tu102/nvdec/scrubber.bin");
+MODULE_FIRMWARE("nvidia/tu104/nvdec/scrubber.bin");
+MODULE_FIRMWARE("nvidia/tu106/nvdec/scrubber.bin");
+MODULE_FIRMWARE("nvidia/tu116/nvdec/scrubber.bin");
+MODULE_FIRMWARE("nvidia/tu117/nvdec/scrubber.bin");
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c
index a72403777329ea..2ed04da3621d53 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pmu/gm20b.c
@@ -225,7 +225,7 @@ gm20b_pmu_init(struct nvkm_pmu *pmu)
pmu->initmsg_received = false;
- nvkm_falcon_load_dmem(falcon, &args, addr_args, sizeof(args), 0);
+ nvkm_falcon_pio_wr(falcon, (u8 *)&args, 0, 0, DMEM, addr_args, sizeof(args), 0, false);
nvkm_falcon_start(falcon);
return 0;
}
diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c
index 857a2f0420d778..c924f1124ebca9 100644
--- a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c
+++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c
@@ -1193,14 +1193,11 @@ static int boe_panel_enter_sleep_mode(struct boe_panel *boe)
return 0;
}
-static int boe_panel_unprepare(struct drm_panel *panel)
+static int boe_panel_disable(struct drm_panel *panel)
{
struct boe_panel *boe = to_boe_panel(panel);
int ret;
- if (!boe->prepared)
- return 0;
-
ret = boe_panel_enter_sleep_mode(boe);
if (ret < 0) {
dev_err(panel->dev, "failed to set panel off: %d\n", ret);
@@ -1209,6 +1206,16 @@ static int boe_panel_unprepare(struct drm_panel *panel)
msleep(150);
+ return 0;
+}
+
+static int boe_panel_unprepare(struct drm_panel *panel)
+{
+ struct boe_panel *boe = to_boe_panel(panel);
+
+ if (!boe->prepared)
+ return 0;
+
if (boe->desc->discharge_on_disable) {
regulator_disable(boe->avee);
regulator_disable(boe->avdd);
@@ -1528,6 +1535,7 @@ static enum drm_panel_orientation boe_panel_get_orientation(struct drm_panel *pa
}
static const struct drm_panel_funcs boe_panel_funcs = {
+ .disable = boe_panel_disable,
.unprepare = boe_panel_unprepare,
.prepare = boe_panel_prepare,
.enable = boe_panel_enable,
diff --git a/drivers/gpu/drm/panfrost/Kconfig b/drivers/gpu/drm/panfrost/Kconfig
index 079600328be187..e6403a9d66adec 100644
--- a/drivers/gpu/drm/panfrost/Kconfig
+++ b/drivers/gpu/drm/panfrost/Kconfig
@@ -3,7 +3,8 @@
config DRM_PANFROST
tristate "Panfrost (DRM support for ARM Mali Midgard/Bifrost GPUs)"
depends on DRM
- depends on ARM || ARM64 || (COMPILE_TEST && !GENERIC_ATOMIC64)
+ depends on ARM || ARM64 || COMPILE_TEST
+ depends on !GENERIC_ATOMIC64 # for IOMMU_IO_PGTABLE_LPAE
depends on MMU
select DRM_SCHED
select IOMMU_SUPPORT
diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c
index 2fa5afe2128892..919e6cc0498281 100644
--- a/drivers/gpu/drm/panfrost/panfrost_drv.c
+++ b/drivers/gpu/drm/panfrost/panfrost_drv.c
@@ -82,6 +82,7 @@ static int panfrost_ioctl_create_bo(struct drm_device *dev, void *data,
struct panfrost_gem_object *bo;
struct drm_panfrost_create_bo *args = data;
struct panfrost_gem_mapping *mapping;
+ int ret;
if (!args->size || args->pad ||
(args->flags & ~(PANFROST_BO_NOEXEC | PANFROST_BO_HEAP)))
@@ -92,21 +93,29 @@ static int panfrost_ioctl_create_bo(struct drm_device *dev, void *data,
!(args->flags & PANFROST_BO_NOEXEC))
return -EINVAL;
- bo = panfrost_gem_create_with_handle(file, dev, args->size, args->flags,
- &args->handle);
+ bo = panfrost_gem_create(dev, args->size, args->flags);
if (IS_ERR(bo))
return PTR_ERR(bo);
+ ret = drm_gem_handle_create(file, &bo->base.base, &args->handle);
+ if (ret)
+ goto out;
+
mapping = panfrost_gem_mapping_get(bo, priv);
- if (!mapping) {
- drm_gem_object_put(&bo->base.base);
- return -EINVAL;
+ if (mapping) {
+ args->offset = mapping->mmnode.start << PAGE_SHIFT;
+ panfrost_gem_mapping_put(mapping);
+ } else {
+ /* This can only happen if the handle from
+ * drm_gem_handle_create() has already been guessed and freed
+ * by user space
+ */
+ ret = -EINVAL;
}
- args->offset = mapping->mmnode.start << PAGE_SHIFT;
- panfrost_gem_mapping_put(mapping);
-
- return 0;
+out:
+ drm_gem_object_put(&bo->base.base);
+ return ret;
}
/**
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.c b/drivers/gpu/drm/panfrost/panfrost_gem.c
index 293e799e2fe814..3c812fbd126fd0 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.c
@@ -235,12 +235,8 @@ struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t
}
struct panfrost_gem_object *
-panfrost_gem_create_with_handle(struct drm_file *file_priv,
- struct drm_device *dev, size_t size,
- u32 flags,
- uint32_t *handle)
+panfrost_gem_create(struct drm_device *dev, size_t size, u32 flags)
{
- int ret;
struct drm_gem_shmem_object *shmem;
struct panfrost_gem_object *bo;
@@ -256,16 +252,6 @@ panfrost_gem_create_with_handle(struct drm_file *file_priv,
bo->noexec = !!(flags & PANFROST_BO_NOEXEC);
bo->is_heap = !!(flags & PANFROST_BO_HEAP);
- /*
- * Allocate an id of idr table where the obj is registered
- * and handle has the id what user can see.
- */
- ret = drm_gem_handle_create(file_priv, &shmem->base, handle);
- /* drop reference from allocate - handle holds it now. */
- drm_gem_object_put(&shmem->base);
- if (ret)
- return ERR_PTR(ret);
-
return bo;
}
diff --git a/drivers/gpu/drm/panfrost/panfrost_gem.h b/drivers/gpu/drm/panfrost/panfrost_gem.h
index 8088d5fd8480e8..ad2877eeeccdfc 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gem.h
+++ b/drivers/gpu/drm/panfrost/panfrost_gem.h
@@ -69,10 +69,7 @@ panfrost_gem_prime_import_sg_table(struct drm_device *dev,
struct sg_table *sgt);
struct panfrost_gem_object *
-panfrost_gem_create_with_handle(struct drm_file *file_priv,
- struct drm_device *dev, size_t size,
- u32 flags,
- uint32_t *handle);
+panfrost_gem_create(struct drm_device *dev, size_t size, u32 flags);
int panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_priv);
void panfrost_gem_close(struct drm_gem_object *obj,
diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c
index fe09e5be79bddc..15d04a0ec62346 100644
--- a/drivers/gpu/drm/scheduler/sched_entity.c
+++ b/drivers/gpu/drm/scheduler/sched_entity.c
@@ -81,7 +81,7 @@ int drm_sched_entity_init(struct drm_sched_entity *entity,
init_completion(&entity->entity_idle);
/* We start in an idle state. */
- complete(&entity->entity_idle);
+ complete_all(&entity->entity_idle);
spin_lock_init(&entity->rq_lock);
spsc_queue_init(&entity->job_queue);
diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c
index 31f3a1267be44f..fd22d753b4ed0c 100644
--- a/drivers/gpu/drm/scheduler/sched_main.c
+++ b/drivers/gpu/drm/scheduler/sched_main.c
@@ -987,7 +987,7 @@ static int drm_sched_main(void *param)
sched_job = drm_sched_entity_pop_job(entity);
if (!sched_job) {
- complete(&entity->entity_idle);
+ complete_all(&entity->entity_idle);
continue;
}
@@ -998,7 +998,7 @@ static int drm_sched_main(void *param)
trace_drm_run_job(sched_job, entity);
fence = sched->ops->run_job(sched_job);
- complete(&entity->entity_idle);
+ complete_all(&entity->entity_idle);
drm_sched_fence_scheduled(s_fence);
if (!IS_ERR_OR_NULL(fence)) {
diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c
index 53464afc2b9ac7..91f69e62430bbc 100644
--- a/drivers/gpu/drm/solomon/ssd130x.c
+++ b/drivers/gpu/drm/solomon/ssd130x.c
@@ -656,18 +656,8 @@ static const struct drm_crtc_helper_funcs ssd130x_crtc_helper_funcs = {
.atomic_check = drm_crtc_helper_atomic_check,
};
-static void ssd130x_crtc_reset(struct drm_crtc *crtc)
-{
- struct drm_device *drm = crtc->dev;
- struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
-
- ssd130x_init(ssd130x);
-
- drm_atomic_helper_crtc_reset(crtc);
-}
-
static const struct drm_crtc_funcs ssd130x_crtc_funcs = {
- .reset = ssd130x_crtc_reset,
+ .reset = drm_atomic_helper_crtc_reset,
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
@@ -686,6 +676,12 @@ static void ssd130x_encoder_helper_atomic_enable(struct drm_encoder *encoder,
if (ret)
return;
+ ret = ssd130x_init(ssd130x);
+ if (ret) {
+ ssd130x_power_off(ssd130x);
+ return;
+ }
+
ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_ON);
backlight_enable(ssd130x->bl_dev);
diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile
index b29ef1085cad92..f896ef85c2f2b2 100644
--- a/drivers/gpu/drm/tests/Makefile
+++ b/drivers/gpu/drm/tests/Makefile
@@ -12,3 +12,5 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \
drm_mm_test.o \
drm_plane_helper_test.o \
drm_rect_test.o
+
+CFLAGS_drm_mm_test.o := $(DISABLE_STRUCTLEAK_PLUGIN)
diff --git a/drivers/gpu/drm/tests/drm_mm_test.c b/drivers/gpu/drm/tests/drm_mm_test.c
index 89f12d3b4a2199..186b28dc70380f 100644
--- a/drivers/gpu/drm/tests/drm_mm_test.c
+++ b/drivers/gpu/drm/tests/drm_mm_test.c
@@ -298,9 +298,9 @@ static bool expect_reserve_fail(struct kunit *test, struct drm_mm *mm, struct dr
return false;
}
-static bool check_reserve_boundaries(struct kunit *test, struct drm_mm *mm,
- unsigned int count,
- u64 size)
+static bool noinline_for_stack check_reserve_boundaries(struct kunit *test, struct drm_mm *mm,
+ unsigned int count,
+ u64 size)
{
const struct boundary {
u64 start, size;
diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
index ba3aa0a0fc43cb..da5493f789dfe5 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -173,7 +173,7 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo,
clear = src_iter->ops->maps_tt && (!ttm || !ttm_tt_is_populated(ttm));
if (!(clear && ttm && !(ttm->page_flags & TTM_TT_FLAG_ZERO_ALLOC)))
- ttm_move_memcpy(clear, ttm->num_pages, dst_iter, src_iter);
+ ttm_move_memcpy(clear, PFN_UP(dst_mem->size), dst_iter, src_iter);
if (!src_iter->ops->maps_tt)
ttm_kmap_iter_linear_io_fini(&_src_iter.io, bdev, src_mem);
diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
index 43d9b3a6a352c1..c5947ed8cc812b 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -179,6 +179,7 @@ static void vc4_bo_destroy(struct vc4_bo *bo)
bo->validated_shader = NULL;
}
+ mutex_destroy(&bo->madv_lock);
drm_gem_dma_free(&bo->base);
}
@@ -394,7 +395,6 @@ struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size)
{
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_bo *bo;
- int ret;
if (WARN_ON_ONCE(vc4->is_vc5))
return ERR_PTR(-ENODEV);
@@ -406,9 +406,7 @@ struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size)
bo->madv = VC4_MADV_WILLNEED;
refcount_set(&bo->usecnt, 0);
- ret = drmm_mutex_init(dev, &bo->madv_lock);
- if (ret)
- return ERR_PTR(ret);
+ mutex_init(&bo->madv_lock);
mutex_lock(&vc4->bo_lock);
bo->label = VC4_BO_TYPE_KERNEL;
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 12a00d644b6192..55744216392b29 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -3018,7 +3018,8 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
}
vc4_hdmi->cec_adap = cec_allocate_adapter(&vc4_hdmi_cec_adap_ops,
- vc4_hdmi, "vc4",
+ vc4_hdmi,
+ vc4_hdmi->variant->card_name,
CEC_CAP_DEFAULTS |
CEC_CAP_CONNECTOR_INFO, 1);
ret = PTR_ERR_OR_ZERO(vc4_hdmi->cec_adap);
diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
index 5d05093014ac3c..da45215a933d01 100644
--- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c
+++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
@@ -126,7 +126,6 @@ static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
void __user *user_bo_handles = NULL;
struct virtio_gpu_object_array *buflist = NULL;
struct sync_file *sync_file;
- int in_fence_fd = exbuf->fence_fd;
int out_fence_fd = -1;
void *buf;
uint64_t fence_ctx;
@@ -152,13 +151,11 @@ static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
ring_idx = exbuf->ring_idx;
}
- exbuf->fence_fd = -1;
-
virtio_gpu_create_context(dev, file);
if (exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_IN) {
struct dma_fence *in_fence;
- in_fence = sync_file_get_fence(in_fence_fd);
+ in_fence = sync_file_get_fence(exbuf->fence_fd);
if (!in_fence)
return -EINVAL;
@@ -358,10 +355,18 @@ static int virtio_gpu_resource_create_ioctl(struct drm_device *dev, void *data,
drm_gem_object_release(obj);
return ret;
}
- drm_gem_object_put(obj);
rc->res_handle = qobj->hw_res_handle; /* similiar to a VM address */
rc->bo_handle = handle;
+
+ /*
+ * The handle owns the reference now. But we must drop our
+ * remaining reference *after* we no longer need to dereference
+ * the obj. Otherwise userspace could guess the handle and
+ * race closing it from another thread.
+ */
+ drm_gem_object_put(obj);
+
return 0;
}
@@ -723,11 +728,18 @@ static int virtio_gpu_resource_create_blob_ioctl(struct drm_device *dev,
drm_gem_object_release(obj);
return ret;
}
- drm_gem_object_put(obj);
rc_blob->res_handle = bo->hw_res_handle;
rc_blob->bo_handle = handle;
+ /*
+ * The handle owns the reference now. But we must drop our
+ * remaining reference *after* we no longer need to dereference
+ * the obj. Otherwise userspace could guess the handle and
+ * race closing it from another thread.
+ */
+ drm_gem_object_put(obj);
+
return 0;
}
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index 8d7728181de016..c7e74cf130221b 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -184,7 +184,7 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object_array *objs = NULL;
struct drm_gem_shmem_object *shmem_obj;
struct virtio_gpu_object *bo;
- struct virtio_gpu_mem_entry *ents;
+ struct virtio_gpu_mem_entry *ents = NULL;
unsigned int nents;
int ret;
@@ -210,7 +210,7 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
ret = -ENOMEM;
objs = virtio_gpu_array_alloc(1);
if (!objs)
- goto err_put_id;
+ goto err_free_entry;
virtio_gpu_array_add_obj(objs, &bo->base.base);
ret = virtio_gpu_array_lock_resv(objs);
@@ -239,6 +239,8 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
err_put_objs:
virtio_gpu_array_put_free(objs);
+err_free_entry:
+ kvfree(ents);
err_put_id:
virtio_gpu_resource_id_put(vgdev, bo->hw_res_handle);
err_free_gem:
diff --git a/drivers/gpu/drm/vmwgfx/ttm_object.c b/drivers/gpu/drm/vmwgfx/ttm_object.c
index 932b125ebf3d64..ddf8373c1d779c 100644
--- a/drivers/gpu/drm/vmwgfx/ttm_object.c
+++ b/drivers/gpu/drm/vmwgfx/ttm_object.c
@@ -254,40 +254,6 @@ void ttm_base_object_unref(struct ttm_base_object **p_base)
kref_put(&base->refcount, ttm_release_base);
}
-/**
- * ttm_base_object_noref_lookup - look up a base object without reference
- * @tfile: The struct ttm_object_file the object is registered with.
- * @key: The object handle.
- *
- * This function looks up a ttm base object and returns a pointer to it
- * without refcounting the pointer. The returned pointer is only valid
- * until ttm_base_object_noref_release() is called, and the object
- * pointed to by the returned pointer may be doomed. Any persistent usage
- * of the object requires a refcount to be taken using kref_get_unless_zero().
- * Iff this function returns successfully it needs to be paired with
- * ttm_base_object_noref_release() and no sleeping- or scheduling functions
- * may be called inbetween these function callse.
- *
- * Return: A pointer to the object if successful or NULL otherwise.
- */
-struct ttm_base_object *
-ttm_base_object_noref_lookup(struct ttm_object_file *tfile, uint64_t key)
-{
- struct vmwgfx_hash_item *hash;
- int ret;
-
- rcu_read_lock();
- ret = ttm_tfile_find_ref_rcu(tfile, key, &hash);
- if (ret) {
- rcu_read_unlock();
- return NULL;
- }
-
- __release(RCU);
- return hlist_entry(hash, struct ttm_ref_object, hash)->obj;
-}
-EXPORT_SYMBOL(ttm_base_object_noref_lookup);
-
struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile,
uint64_t key)
{
@@ -295,15 +261,16 @@ struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile,
struct vmwgfx_hash_item *hash;
int ret;
- rcu_read_lock();
- ret = ttm_tfile_find_ref_rcu(tfile, key, &hash);
+ spin_lock(&tfile->lock);
+ ret = ttm_tfile_find_ref(tfile, key, &hash);
if (likely(ret == 0)) {
base = hlist_entry(hash, struct ttm_ref_object, hash)->obj;
if (!kref_get_unless_zero(&base->refcount))
base = NULL;
}
- rcu_read_unlock();
+ spin_unlock(&tfile->lock);
+
return base;
}
diff --git a/drivers/gpu/drm/vmwgfx/ttm_object.h b/drivers/gpu/drm/vmwgfx/ttm_object.h
index f0ebbe340ad698..8098a3846bae3e 100644
--- a/drivers/gpu/drm/vmwgfx/ttm_object.h
+++ b/drivers/gpu/drm/vmwgfx/ttm_object.h
@@ -307,18 +307,4 @@ extern int ttm_prime_handle_to_fd(struct ttm_object_file *tfile,
#define ttm_prime_object_kfree(__obj, __prime) \
kfree_rcu(__obj, __prime.base.rhead)
-struct ttm_base_object *
-ttm_base_object_noref_lookup(struct ttm_object_file *tfile, uint64_t key);
-
-/**
- * ttm_base_object_noref_release - release a base object pointer looked up
- * without reference
- *
- * Releases a base object pointer looked up with ttm_base_object_noref_lookup().
- */
-static inline void ttm_base_object_noref_release(void)
-{
- __acquire(RCU);
- rcu_read_unlock();
-}
#endif
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
index 321c551784a146..aa1cd5126a321d 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
@@ -716,44 +716,6 @@ int vmw_user_bo_lookup(struct drm_file *filp,
}
/**
- * vmw_user_bo_noref_lookup - Look up a vmw user buffer object without reference
- * @filp: The TTM object file the handle is registered with.
- * @handle: The user buffer object handle.
- *
- * This function looks up a struct vmw_bo and returns a pointer to the
- * struct vmw_buffer_object it derives from without refcounting the pointer.
- * The returned pointer is only valid until vmw_user_bo_noref_release() is
- * called, and the object pointed to by the returned pointer may be doomed.
- * Any persistent usage of the object requires a refcount to be taken using
- * ttm_bo_reference_unless_doomed(). Iff this function returns successfully it
- * needs to be paired with vmw_user_bo_noref_release() and no sleeping-
- * or scheduling functions may be called in between these function calls.
- *
- * Return: A struct vmw_buffer_object pointer if successful or negative
- * error pointer on failure.
- */
-struct vmw_buffer_object *
-vmw_user_bo_noref_lookup(struct drm_file *filp, u32 handle)
-{
- struct vmw_buffer_object *vmw_bo;
- struct ttm_buffer_object *bo;
- struct drm_gem_object *gobj = drm_gem_object_lookup(filp, handle);
-
- if (!gobj) {
- DRM_ERROR("Invalid buffer object handle 0x%08lx.\n",
- (unsigned long)handle);
- return ERR_PTR(-ESRCH);
- }
- vmw_bo = gem_to_vmw_bo(gobj);
- bo = ttm_bo_get_unless_zero(&vmw_bo->base);
- vmw_bo = vmw_buffer_object(bo);
- drm_gem_object_put(gobj);
-
- return vmw_bo;
-}
-
-
-/**
* vmw_bo_fence_single - Utility function to fence a single TTM buffer
* object without unreserving it.
*
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
index b062b020b37824..5acbf5849b2703 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
@@ -830,12 +830,7 @@ extern int vmw_user_resource_lookup_handle(
uint32_t handle,
const struct vmw_user_resource_conv *converter,
struct vmw_resource **p_res);
-extern struct vmw_resource *
-vmw_user_resource_noref_lookup_handle(struct vmw_private *dev_priv,
- struct ttm_object_file *tfile,
- uint32_t handle,
- const struct vmw_user_resource_conv *
- converter);
+
extern int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data,
@@ -875,15 +870,6 @@ static inline bool vmw_resource_mob_attached(const struct vmw_resource *res)
}
/**
- * vmw_user_resource_noref_release - release a user resource pointer looked up
- * without reference
- */
-static inline void vmw_user_resource_noref_release(void)
-{
- ttm_base_object_noref_release();
-}
-
-/**
* Buffer object helper functions - vmwgfx_bo.c
*/
extern int vmw_bo_pin_in_placement(struct vmw_private *vmw_priv,
@@ -934,8 +920,6 @@ extern void vmw_bo_unmap(struct vmw_buffer_object *vbo);
extern void vmw_bo_move_notify(struct ttm_buffer_object *bo,
struct ttm_resource *mem);
extern void vmw_bo_swap_notify(struct ttm_buffer_object *bo);
-extern struct vmw_buffer_object *
-vmw_user_bo_noref_lookup(struct drm_file *filp, u32 handle);
/**
* vmw_bo_adjust_prio - Adjust the buffer object eviction priority
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index a5379f6fb5ab18..a44d53e33cdb14 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -290,20 +290,26 @@ static void vmw_execbuf_rcache_update(struct vmw_res_cache_entry *rcache,
rcache->valid_handle = 0;
}
+enum vmw_val_add_flags {
+ vmw_val_add_flag_none = 0,
+ vmw_val_add_flag_noctx = 1 << 0,
+};
+
/**
- * vmw_execbuf_res_noref_val_add - Add a resource described by an unreferenced
- * rcu-protected pointer to the validation list.
+ * vmw_execbuf_res_val_add - Add a resource to the validation list.
*
* @sw_context: Pointer to the software context.
* @res: Unreferenced rcu-protected pointer to the resource.
* @dirty: Whether to change dirty status.
+ * @flags: specifies whether to use the context or not
*
* Returns: 0 on success. Negative error code on failure. Typical error codes
* are %-EINVAL on inconsistency and %-ESRCH if the resource was doomed.
*/
-static int vmw_execbuf_res_noref_val_add(struct vmw_sw_context *sw_context,
- struct vmw_resource *res,
- u32 dirty)
+static int vmw_execbuf_res_val_add(struct vmw_sw_context *sw_context,
+ struct vmw_resource *res,
+ u32 dirty,
+ u32 flags)
{
struct vmw_private *dev_priv = res->dev_priv;
int ret;
@@ -318,24 +324,30 @@ static int vmw_execbuf_res_noref_val_add(struct vmw_sw_context *sw_context,
if (dirty)
vmw_validation_res_set_dirty(sw_context->ctx,
rcache->private, dirty);
- vmw_user_resource_noref_release();
return 0;
}
- priv_size = vmw_execbuf_res_size(dev_priv, res_type);
- ret = vmw_validation_add_resource(sw_context->ctx, res, priv_size,
- dirty, (void **)&ctx_info,
- &first_usage);
- vmw_user_resource_noref_release();
- if (ret)
- return ret;
+ if ((flags & vmw_val_add_flag_noctx) != 0) {
+ ret = vmw_validation_add_resource(sw_context->ctx, res, 0, dirty,
+ (void **)&ctx_info, NULL);
+ if (ret)
+ return ret;
- if (priv_size && first_usage) {
- ret = vmw_cmd_ctx_first_setup(dev_priv, sw_context, res,
- ctx_info);
- if (ret) {
- VMW_DEBUG_USER("Failed first usage context setup.\n");
+ } else {
+ priv_size = vmw_execbuf_res_size(dev_priv, res_type);
+ ret = vmw_validation_add_resource(sw_context->ctx, res, priv_size,
+ dirty, (void **)&ctx_info,
+ &first_usage);
+ if (ret)
return ret;
+
+ if (priv_size && first_usage) {
+ ret = vmw_cmd_ctx_first_setup(dev_priv, sw_context, res,
+ ctx_info);
+ if (ret) {
+ VMW_DEBUG_USER("Failed first usage context setup.\n");
+ return ret;
+ }
}
}
@@ -344,43 +356,6 @@ static int vmw_execbuf_res_noref_val_add(struct vmw_sw_context *sw_context,
}
/**
- * vmw_execbuf_res_noctx_val_add - Add a non-context resource to the resource
- * validation list if it's not already on it
- *
- * @sw_context: Pointer to the software context.
- * @res: Pointer to the resource.
- * @dirty: Whether to change dirty status.
- *
- * Returns: Zero on success. Negative error code on failure.
- */
-static int vmw_execbuf_res_noctx_val_add(struct vmw_sw_context *sw_context,
- struct vmw_resource *res,
- u32 dirty)
-{
- struct vmw_res_cache_entry *rcache;
- enum vmw_res_type res_type = vmw_res_type(res);
- void *ptr;
- int ret;
-
- rcache = &sw_context->res_cache[res_type];
- if (likely(rcache->valid && rcache->res == res)) {
- if (dirty)
- vmw_validation_res_set_dirty(sw_context->ctx,
- rcache->private, dirty);
- return 0;
- }
-
- ret = vmw_validation_add_resource(sw_context->ctx, res, 0, dirty,
- &ptr, NULL);
- if (ret)
- return ret;
-
- vmw_execbuf_rcache_update(rcache, res, ptr);
-
- return 0;
-}
-
-/**
* vmw_view_res_val_add - Add a view and the surface it's pointing to to the
* validation list
*
@@ -398,13 +373,13 @@ static int vmw_view_res_val_add(struct vmw_sw_context *sw_context,
* First add the resource the view is pointing to, otherwise it may be
* swapped out when the view is validated.
*/
- ret = vmw_execbuf_res_noctx_val_add(sw_context, vmw_view_srf(view),
- vmw_view_dirtying(view));
+ ret = vmw_execbuf_res_val_add(sw_context, vmw_view_srf(view),
+ vmw_view_dirtying(view), vmw_val_add_flag_noctx);
if (ret)
return ret;
- return vmw_execbuf_res_noctx_val_add(sw_context, view,
- VMW_RES_DIRTY_NONE);
+ return vmw_execbuf_res_val_add(sw_context, view, VMW_RES_DIRTY_NONE,
+ vmw_val_add_flag_noctx);
}
/**
@@ -475,8 +450,9 @@ static int vmw_resource_context_res_add(struct vmw_private *dev_priv,
if (IS_ERR(res))
continue;
- ret = vmw_execbuf_res_noctx_val_add(sw_context, res,
- VMW_RES_DIRTY_SET);
+ ret = vmw_execbuf_res_val_add(sw_context, res,
+ VMW_RES_DIRTY_SET,
+ vmw_val_add_flag_noctx);
if (unlikely(ret != 0))
return ret;
}
@@ -490,9 +466,9 @@ static int vmw_resource_context_res_add(struct vmw_private *dev_priv,
if (vmw_res_type(entry->res) == vmw_res_view)
ret = vmw_view_res_val_add(sw_context, entry->res);
else
- ret = vmw_execbuf_res_noctx_val_add
- (sw_context, entry->res,
- vmw_binding_dirtying(entry->bt));
+ ret = vmw_execbuf_res_val_add(sw_context, entry->res,
+ vmw_binding_dirtying(entry->bt),
+ vmw_val_add_flag_noctx);
if (unlikely(ret != 0))
break;
}
@@ -658,7 +634,8 @@ vmw_cmd_res_check(struct vmw_private *dev_priv,
{
struct vmw_res_cache_entry *rcache = &sw_context->res_cache[res_type];
struct vmw_resource *res;
- int ret;
+ int ret = 0;
+ bool needs_unref = false;
if (p_res)
*p_res = NULL;
@@ -683,17 +660,18 @@ vmw_cmd_res_check(struct vmw_private *dev_priv,
if (ret)
return ret;
- res = vmw_user_resource_noref_lookup_handle
- (dev_priv, sw_context->fp->tfile, *id_loc, converter);
- if (IS_ERR(res)) {
+ ret = vmw_user_resource_lookup_handle
+ (dev_priv, sw_context->fp->tfile, *id_loc, converter, &res);
+ if (ret != 0) {
VMW_DEBUG_USER("Could not find/use resource 0x%08x.\n",
(unsigned int) *id_loc);
- return PTR_ERR(res);
+ return ret;
}
+ needs_unref = true;
- ret = vmw_execbuf_res_noref_val_add(sw_context, res, dirty);
+ ret = vmw_execbuf_res_val_add(sw_context, res, dirty, vmw_val_add_flag_none);
if (unlikely(ret != 0))
- return ret;
+ goto res_check_done;
if (rcache->valid && rcache->res == res) {
rcache->valid_handle = true;
@@ -708,7 +686,11 @@ vmw_cmd_res_check(struct vmw_private *dev_priv,
if (p_res)
*p_res = res;
- return 0;
+res_check_done:
+ if (needs_unref)
+ vmw_resource_unreference(&res);
+
+ return ret;
}
/**
@@ -1171,9 +1153,9 @@ static int vmw_translate_mob_ptr(struct vmw_private *dev_priv,
int ret;
vmw_validation_preload_bo(sw_context->ctx);
- vmw_bo = vmw_user_bo_noref_lookup(sw_context->filp, handle);
- if (IS_ERR(vmw_bo)) {
- VMW_DEBUG_USER("Could not find or use MOB buffer.\n");
+ ret = vmw_user_bo_lookup(sw_context->filp, handle, &vmw_bo);
+ if (ret != 0) {
+ drm_dbg(&dev_priv->drm, "Could not find or use MOB buffer.\n");
return PTR_ERR(vmw_bo);
}
ret = vmw_validation_add_bo(sw_context->ctx, vmw_bo, true, false);
@@ -1225,9 +1207,9 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv,
int ret;
vmw_validation_preload_bo(sw_context->ctx);
- vmw_bo = vmw_user_bo_noref_lookup(sw_context->filp, handle);
- if (IS_ERR(vmw_bo)) {
- VMW_DEBUG_USER("Could not find or use GMR region.\n");
+ ret = vmw_user_bo_lookup(sw_context->filp, handle, &vmw_bo);
+ if (ret != 0) {
+ drm_dbg(&dev_priv->drm, "Could not find or use GMR region.\n");
return PTR_ERR(vmw_bo);
}
ret = vmw_validation_add_bo(sw_context->ctx, vmw_bo, false, false);
@@ -2025,8 +2007,9 @@ static int vmw_cmd_set_shader(struct vmw_private *dev_priv,
res = vmw_shader_lookup(vmw_context_res_man(ctx),
cmd->body.shid, cmd->body.type);
if (!IS_ERR(res)) {
- ret = vmw_execbuf_res_noctx_val_add(sw_context, res,
- VMW_RES_DIRTY_NONE);
+ ret = vmw_execbuf_res_val_add(sw_context, res,
+ VMW_RES_DIRTY_NONE,
+ vmw_val_add_flag_noctx);
if (unlikely(ret != 0))
return ret;
@@ -2273,8 +2256,9 @@ static int vmw_cmd_dx_set_shader(struct vmw_private *dev_priv,
return PTR_ERR(res);
}
- ret = vmw_execbuf_res_noctx_val_add(sw_context, res,
- VMW_RES_DIRTY_NONE);
+ ret = vmw_execbuf_res_val_add(sw_context, res,
+ VMW_RES_DIRTY_NONE,
+ vmw_val_add_flag_noctx);
if (ret)
return ret;
}
@@ -2777,8 +2761,8 @@ static int vmw_cmd_dx_bind_shader(struct vmw_private *dev_priv,
return PTR_ERR(res);
}
- ret = vmw_execbuf_res_noctx_val_add(sw_context, res,
- VMW_RES_DIRTY_NONE);
+ ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_NONE,
+ vmw_val_add_flag_noctx);
if (ret) {
VMW_DEBUG_USER("Error creating resource validation node.\n");
return ret;
@@ -3098,8 +3082,8 @@ static int vmw_cmd_dx_bind_streamoutput(struct vmw_private *dev_priv,
vmw_dx_streamoutput_set_size(res, cmd->body.sizeInBytes);
- ret = vmw_execbuf_res_noctx_val_add(sw_context, res,
- VMW_RES_DIRTY_NONE);
+ ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_NONE,
+ vmw_val_add_flag_noctx);
if (ret) {
DRM_ERROR("Error creating resource validation node.\n");
return ret;
@@ -3148,8 +3132,8 @@ static int vmw_cmd_dx_set_streamoutput(struct vmw_private *dev_priv,
return 0;
}
- ret = vmw_execbuf_res_noctx_val_add(sw_context, res,
- VMW_RES_DIRTY_NONE);
+ ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_NONE,
+ vmw_val_add_flag_noctx);
if (ret) {
DRM_ERROR("Error creating resource validation node.\n");
return ret;
@@ -4066,22 +4050,26 @@ static int vmw_execbuf_tie_context(struct vmw_private *dev_priv,
if (ret)
return ret;
- res = vmw_user_resource_noref_lookup_handle
+ ret = vmw_user_resource_lookup_handle
(dev_priv, sw_context->fp->tfile, handle,
- user_context_converter);
- if (IS_ERR(res)) {
+ user_context_converter, &res);
+ if (ret != 0) {
VMW_DEBUG_USER("Could not find or user DX context 0x%08x.\n",
(unsigned int) handle);
- return PTR_ERR(res);
+ return ret;
}
- ret = vmw_execbuf_res_noref_val_add(sw_context, res, VMW_RES_DIRTY_SET);
- if (unlikely(ret != 0))
+ ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_SET,
+ vmw_val_add_flag_none);
+ if (unlikely(ret != 0)) {
+ vmw_resource_unreference(&res);
return ret;
+ }
sw_context->dx_ctx_node = vmw_execbuf_info_from_res(sw_context, res);
sw_context->man = vmw_context_res_man(res);
+ vmw_resource_unreference(&res);
return 0;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_msg_arm64.h b/drivers/gpu/drm/vmwgfx/vmwgfx_msg_arm64.h
index 4f40167ad61fb6..4f40167ad61fb6 100755..100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_msg_arm64.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg_arm64.h
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
index f66caa540e1460..c7d645e5ec7bf8 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
@@ -281,39 +281,6 @@ out_bad_resource:
return ret;
}
-/**
- * vmw_user_resource_noref_lookup_handle - lookup a struct resource from a
- * TTM user-space handle and perform basic type checks
- *
- * @dev_priv: Pointer to a device private struct
- * @tfile: Pointer to a struct ttm_object_file identifying the caller
- * @handle: The TTM user-space handle
- * @converter: Pointer to an object describing the resource type
- *
- * If the handle can't be found or is associated with an incorrect resource
- * type, -EINVAL will be returned.
- */
-struct vmw_resource *
-vmw_user_resource_noref_lookup_handle(struct vmw_private *dev_priv,
- struct ttm_object_file *tfile,
- uint32_t handle,
- const struct vmw_user_resource_conv
- *converter)
-{
- struct ttm_base_object *base;
-
- base = ttm_base_object_noref_lookup(tfile, handle);
- if (!base)
- return ERR_PTR(-ESRCH);
-
- if (unlikely(ttm_base_object_type(base) != converter->object_type)) {
- ttm_base_object_noref_release();
- return ERR_PTR(-EINVAL);
- }
-
- return converter->base_obj_to_res(base);
-}
-
/*
* Helper function that looks either a surface or bo.
*
diff --git a/drivers/gpu/drm/xen/xen_drm_front.c b/drivers/gpu/drm/xen/xen_drm_front.c
index 0d8e6bd1ccbf20..90996c108146db 100644
--- a/drivers/gpu/drm/xen/xen_drm_front.c
+++ b/drivers/gpu/drm/xen/xen_drm_front.c
@@ -717,7 +717,7 @@ static int xen_drv_probe(struct xenbus_device *xb_dev,
return xenbus_switch_state(xb_dev, XenbusStateInitialising);
}
-static int xen_drv_remove(struct xenbus_device *dev)
+static void xen_drv_remove(struct xenbus_device *dev)
{
struct xen_drm_front_info *front_info = dev_get_drvdata(&dev->dev);
int to = 100;
@@ -751,7 +751,6 @@ static int xen_drv_remove(struct xenbus_device *dev)
xen_drm_drv_fini(front_info);
xenbus_frontend_closed(dev);
- return 0;
}
static const struct xenbus_device_id xen_driver_ids[] = {
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c
index ab125f79408f25..c751d12f5df894 100644
--- a/drivers/hid/amd-sfh-hid/amd_sfh_client.c
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_client.c
@@ -227,6 +227,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
cl_data->num_hid_devices = amd_mp2_get_sensor_num(privdata, &cl_data->sensor_idx[0]);
if (cl_data->num_hid_devices == 0)
return -ENODEV;
+ cl_data->is_any_sensor_enabled = false;
INIT_DELAYED_WORK(&cl_data->work, amd_sfh_work);
INIT_DELAYED_WORK(&cl_data->work_buffer, amd_sfh_work_buffer);
@@ -282,11 +283,12 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
}
rc = mp2_ops->get_rep_desc(cl_idx, cl_data->report_descr[i]);
if (rc)
- return rc;
+ goto cleanup;
mp2_ops->start(privdata, info);
status = amd_sfh_wait_for_response
(privdata, cl_data->sensor_idx[i], SENSOR_ENABLED);
if (status == SENSOR_ENABLED) {
+ cl_data->is_any_sensor_enabled = true;
cl_data->sensor_sts[i] = SENSOR_ENABLED;
rc = amdtp_hid_probe(cl_data->cur_hid_dev, cl_data);
if (rc) {
@@ -301,19 +303,26 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata)
cl_data->sensor_sts[i]);
goto cleanup;
}
+ } else {
+ cl_data->sensor_sts[i] = SENSOR_DISABLED;
+ dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
+ cl_data->sensor_idx[i],
+ get_sensor_name(cl_data->sensor_idx[i]),
+ cl_data->sensor_sts[i]);
}
dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n",
cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]),
cl_data->sensor_sts[i]);
}
- if (mp2_ops->discovery_status && mp2_ops->discovery_status(privdata) == 0) {
+ if (!cl_data->is_any_sensor_enabled ||
+ (mp2_ops->discovery_status && mp2_ops->discovery_status(privdata) == 0)) {
amd_sfh_hid_client_deinit(privdata);
for (i = 0; i < cl_data->num_hid_devices; i++) {
devm_kfree(dev, cl_data->feature_report[i]);
devm_kfree(dev, in_data->input_report[i]);
devm_kfree(dev, cl_data->report_descr[i]);
}
- dev_warn(dev, "Failed to discover, sensors not enabled\n");
+ dev_warn(dev, "Failed to discover, sensors not enabled is %d\n", cl_data->is_any_sensor_enabled);
return -EOPNOTSUPP;
}
schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP));
diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h
index 3754fb423e3aef..528036892c9d21 100644
--- a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h
+++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h
@@ -32,6 +32,7 @@ struct amd_input_data {
struct amdtp_cl_data {
u8 init_done;
u32 cur_hid_dev;
+ bool is_any_sensor_enabled;
u32 hid_dev_count;
u32 num_hid_devices;
struct device_info *hid_devices;
diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c
index 4da2f9f62aba38..a1d6e08fab7d43 100644
--- a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c
+++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c
@@ -160,7 +160,7 @@ static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata)
}
rc = mp2_ops->get_rep_desc(cl_idx, cl_data->report_descr[i]);
if (rc)
- return rc;
+ goto cleanup;
writel(0, privdata->mmio + AMD_P2C_MSG(0));
mp2_ops->start(privdata, info);
diff --git a/drivers/hid/hid-betopff.c b/drivers/hid/hid-betopff.c
index 467d789f9bc2d3..25ed7b9a917e4f 100644
--- a/drivers/hid/hid-betopff.c
+++ b/drivers/hid/hid-betopff.c
@@ -60,7 +60,6 @@ static int betopff_init(struct hid_device *hid)
struct list_head *report_list =
&hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct input_dev *dev;
- int field_count = 0;
int error;
int i, j;
@@ -86,19 +85,21 @@ static int betopff_init(struct hid_device *hid)
* -----------------------------------------
* Do init them with default value.
*/
+ if (report->maxfield < 4) {
+ hid_err(hid, "not enough fields in the report: %d\n",
+ report->maxfield);
+ return -ENODEV;
+ }
for (i = 0; i < report->maxfield; i++) {
+ if (report->field[i]->report_count < 1) {
+ hid_err(hid, "no values in the field\n");
+ return -ENODEV;
+ }
for (j = 0; j < report->field[i]->report_count; j++) {
report->field[i]->value[j] = 0x00;
- field_count++;
}
}
- if (field_count < 4) {
- hid_err(hid, "not enough fields in the report: %d\n",
- field_count);
- return -ENODEV;
- }
-
betopff = kzalloc(sizeof(*betopff), GFP_KERNEL);
if (!betopff)
return -ENOMEM;
diff --git a/drivers/hid/hid-bigbenff.c b/drivers/hid/hid-bigbenff.c
index e8c5e3ac9fff15..e8b16665860d6b 100644
--- a/drivers/hid/hid-bigbenff.c
+++ b/drivers/hid/hid-bigbenff.c
@@ -344,6 +344,11 @@ static int bigben_probe(struct hid_device *hid,
}
report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ if (list_empty(report_list)) {
+ hid_err(hid, "no output report found\n");
+ error = -ENODEV;
+ goto error_hw_stop;
+ }
bigben->report = list_entry(report_list->next,
struct hid_report, list);
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index bd47628da6be0d..5c72aef3d3dd5f 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -993,8 +993,8 @@ struct hid_report *hid_validate_values(struct hid_device *hid,
* Validating on id 0 means we should examine the first
* report in the list.
*/
- report = list_entry(
- hid->report_enum[type].report_list.next,
+ report = list_first_entry_or_null(
+ &hid->report_enum[type].report_list,
struct hid_report, list);
} else {
report = hid->report_enum[type].report_id_hash[id];
@@ -1202,6 +1202,7 @@ int hid_open_report(struct hid_device *device)
__u8 *end;
__u8 *next;
int ret;
+ int i;
static int (*dispatch_type[])(struct hid_parser *parser,
struct hid_item *item) = {
hid_parser_main,
@@ -1252,6 +1253,8 @@ int hid_open_report(struct hid_device *device)
goto err;
}
device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
+ for (i = 0; i < HID_DEFAULT_NUM_COLLECTIONS; i++)
+ device->collection[i].parent_idx = -1;
ret = -EINVAL;
while ((next = fetch_item(start, end, &item)) != NULL) {
diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c
index e59e9911fc3705..4fa45ee77503bb 100644
--- a/drivers/hid/hid-elecom.c
+++ b/drivers/hid/hid-elecom.c
@@ -12,6 +12,7 @@
* Copyright (c) 2017 Alex Manoussakis <amanou@gnu.org>
* Copyright (c) 2017 Tomasz Kramkowski <tk@the-tk.com>
* Copyright (c) 2020 YOSHIOKA Takuma <lo48576@hard-wi.red>
+ * Copyright (c) 2022 Takahiro Fujii <fujii@xaxxi.net>
*/
/*
@@ -89,7 +90,7 @@ static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
case USB_DEVICE_ID_ELECOM_M_DT1URBK:
case USB_DEVICE_ID_ELECOM_M_DT1DRBK:
case USB_DEVICE_ID_ELECOM_M_HT1URBK:
- case USB_DEVICE_ID_ELECOM_M_HT1DRBK:
+ case USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D:
/*
* Report descriptor format:
* 12: button bit count
@@ -99,6 +100,16 @@ static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
*/
mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 8);
break;
+ case USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C:
+ /*
+ * Report descriptor format:
+ * 22: button bit count
+ * 30: padding bit count
+ * 24: button report size
+ * 16: button usage maximum
+ */
+ mouse_button_fixup(hdev, rdesc, *rsize, 22, 30, 24, 16, 8);
+ break;
}
return rdesc;
}
@@ -112,7 +123,8 @@ static const struct hid_device_id elecom_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1URBK) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C) },
{ }
};
MODULE_DEVICE_TABLE(hid, elecom_devices);
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 82713ef3aaa64c..9e36b4cd905ee9 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -274,7 +274,6 @@
#define USB_DEVICE_ID_CH_AXIS_295 0x001c
#define USB_VENDOR_ID_CHERRY 0x046a
-#define USB_DEVICE_ID_CHERRY_MOUSE_000C 0x000c
#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023
#define USB_DEVICE_ID_CHERRY_CYMOTION_SOLAR 0x0027
@@ -414,6 +413,8 @@
#define I2C_DEVICE_ID_HP_ENVY_X360_15T_DR100 0x29CF
#define I2C_DEVICE_ID_HP_ENVY_X360_EU0009NV 0x2CF9
#define I2C_DEVICE_ID_HP_SPECTRE_X360_15 0x2817
+#define I2C_DEVICE_ID_HP_SPECTRE_X360_13_AW0020NG 0x29DF
+#define I2C_DEVICE_ID_ASUS_TP420IA_TOUCHSCREEN 0x2BC8
#define USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN 0x2544
#define USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN 0x2706
#define I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN 0x261A
@@ -429,7 +430,8 @@
#define USB_DEVICE_ID_ELECOM_M_DT1URBK 0x00fe
#define USB_DEVICE_ID_ELECOM_M_DT1DRBK 0x00ff
#define USB_DEVICE_ID_ELECOM_M_HT1URBK 0x010c
-#define USB_DEVICE_ID_ELECOM_M_HT1DRBK 0x010d
+#define USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D 0x010d
+#define USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C 0x011c
#define USB_VENDOR_ID_DREAM_CHEEKY 0x1d34
#define USB_DEVICE_ID_DREAM_CHEEKY_WN 0x0004
@@ -1295,6 +1297,7 @@
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540 0x0075
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640 0x0094
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 0x0042
+#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2 0x0905
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L 0x0935
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S 0x0909
#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06 0x0078
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 9b59e436df0a5c..77c8c49852b5c2 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -370,6 +370,8 @@ static const struct hid_device_id hid_battery_quirks[] = {
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_DINOVO_EDGE_KBD),
HID_BATTERY_QUIRK_IGNORE },
+ { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_ASUS_TP420IA_TOUCHSCREEN),
+ HID_BATTERY_QUIRK_IGNORE },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN),
HID_BATTERY_QUIRK_IGNORE },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELAN, USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN),
@@ -384,6 +386,8 @@ static const struct hid_device_id hid_battery_quirks[] = {
HID_BATTERY_QUIRK_IGNORE },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_15),
HID_BATTERY_QUIRK_IGNORE },
+ { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_HP_SPECTRE_X360_13_AW0020NG),
+ HID_BATTERY_QUIRK_IGNORE },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN),
HID_BATTERY_QUIRK_IGNORE },
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_SURFACE_GO2_TOUCHSCREEN),
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index abf2c95e4d0b00..9c1ee8e91e0ca7 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -3978,7 +3978,8 @@ static void hidpp_connect_event(struct hidpp_device *hidpp)
}
hidpp_initialize_battery(hidpp);
- hidpp_initialize_hires_scroll(hidpp);
+ if (!hid_is_usb(hidpp->hid_dev))
+ hidpp_initialize_hires_scroll(hidpp);
/* forward current battery state */
if (hidpp->capabilities & HIDPP_CAPABILITY_HIDPP10_BATTERY) {
diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c
index f399bf0d3c8cc3..27c40894acab40 100644
--- a/drivers/hid/hid-playstation.c
+++ b/drivers/hid/hid-playstation.c
@@ -944,6 +944,7 @@ ATTRIBUTE_GROUPS(ps_device);
static int dualsense_get_calibration_data(struct dualsense *ds)
{
+ struct hid_device *hdev = ds->base.hdev;
short gyro_pitch_bias, gyro_pitch_plus, gyro_pitch_minus;
short gyro_yaw_bias, gyro_yaw_plus, gyro_yaw_minus;
short gyro_roll_bias, gyro_roll_plus, gyro_roll_minus;
@@ -954,6 +955,7 @@ static int dualsense_get_calibration_data(struct dualsense *ds)
int speed_2x;
int range_2g;
int ret = 0;
+ int i;
uint8_t *buf;
buf = kzalloc(DS_FEATURE_REPORT_CALIBRATION_SIZE, GFP_KERNEL);
@@ -1006,6 +1008,21 @@ static int dualsense_get_calibration_data(struct dualsense *ds)
ds->gyro_calib_data[2].sens_denom = gyro_roll_plus - gyro_roll_minus;
/*
+ * Sanity check gyro calibration data. This is needed to prevent crashes
+ * during report handling of virtual, clone or broken devices not implementing
+ * calibration data properly.
+ */
+ for (i = 0; i < ARRAY_SIZE(ds->gyro_calib_data); i++) {
+ if (ds->gyro_calib_data[i].sens_denom == 0) {
+ hid_warn(hdev, "Invalid gyro calibration data for axis (%d), disabling calibration.",
+ ds->gyro_calib_data[i].abs_code);
+ ds->gyro_calib_data[i].bias = 0;
+ ds->gyro_calib_data[i].sens_numer = DS_GYRO_RANGE;
+ ds->gyro_calib_data[i].sens_denom = S16_MAX;
+ }
+ }
+
+ /*
* Set accelerometer calibration and normalization parameters.
* Data values will be normalized to 1/DS_ACC_RES_PER_G g.
*/
@@ -1027,6 +1044,21 @@ static int dualsense_get_calibration_data(struct dualsense *ds)
ds->accel_calib_data[2].sens_numer = 2*DS_ACC_RES_PER_G;
ds->accel_calib_data[2].sens_denom = range_2g;
+ /*
+ * Sanity check accelerometer calibration data. This is needed to prevent crashes
+ * during report handling of virtual, clone or broken devices not implementing calibration
+ * data properly.
+ */
+ for (i = 0; i < ARRAY_SIZE(ds->accel_calib_data); i++) {
+ if (ds->accel_calib_data[i].sens_denom == 0) {
+ hid_warn(hdev, "Invalid accelerometer calibration data for axis (%d), disabling calibration.",
+ ds->accel_calib_data[i].abs_code);
+ ds->accel_calib_data[i].bias = 0;
+ ds->accel_calib_data[i].sens_numer = DS_ACC_RANGE;
+ ds->accel_calib_data[i].sens_denom = S16_MAX;
+ }
+ }
+
err_free:
kfree(buf);
return ret;
@@ -1737,6 +1769,7 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
int speed_2x;
int range_2g;
int ret = 0;
+ int i;
uint8_t *buf;
if (ds4->base.hdev->bus == BUS_USB) {
@@ -1831,6 +1864,21 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
ds4->gyro_calib_data[2].sens_denom = gyro_roll_plus - gyro_roll_minus;
/*
+ * Sanity check gyro calibration data. This is needed to prevent crashes
+ * during report handling of virtual, clone or broken devices not implementing
+ * calibration data properly.
+ */
+ for (i = 0; i < ARRAY_SIZE(ds4->gyro_calib_data); i++) {
+ if (ds4->gyro_calib_data[i].sens_denom == 0) {
+ hid_warn(hdev, "Invalid gyro calibration data for axis (%d), disabling calibration.",
+ ds4->gyro_calib_data[i].abs_code);
+ ds4->gyro_calib_data[i].bias = 0;
+ ds4->gyro_calib_data[i].sens_numer = DS4_GYRO_RANGE;
+ ds4->gyro_calib_data[i].sens_denom = S16_MAX;
+ }
+ }
+
+ /*
* Set accelerometer calibration and normalization parameters.
* Data values will be normalized to 1/DS4_ACC_RES_PER_G g.
*/
@@ -1852,6 +1900,21 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
ds4->accel_calib_data[2].sens_numer = 2*DS4_ACC_RES_PER_G;
ds4->accel_calib_data[2].sens_denom = range_2g;
+ /*
+ * Sanity check accelerometer calibration data. This is needed to prevent crashes
+ * during report handling of virtual, clone or broken devices not implementing calibration
+ * data properly.
+ */
+ for (i = 0; i < ARRAY_SIZE(ds4->accel_calib_data); i++) {
+ if (ds4->accel_calib_data[i].sens_denom == 0) {
+ hid_warn(hdev, "Invalid accelerometer calibration data for axis (%d), disabling calibration.",
+ ds4->accel_calib_data[i].abs_code);
+ ds4->accel_calib_data[i].bias = 0;
+ ds4->accel_calib_data[i].sens_numer = DS4_ACC_RANGE;
+ ds4->accel_calib_data[i].sens_denom = S16_MAX;
+ }
+ }
+
err_free:
kfree(buf);
return ret;
diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 0e9702c7f7d6c0..5bc91f68b37474 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -54,7 +54,6 @@ static const struct hid_device_id hid_quirks[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_FLIGHT_SIM_YOKE), HID_QUIRK_NOGET },
{ HID_USB_DEVICE(USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_PEDALS), HID_QUIRK_NOGET },
{ HID_USB_DEVICE(USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_PRO_THROTTLE), HID_QUIRK_NOGET },
- { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_MOUSE_000C), HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K65RGB), HID_QUIRK_NO_INIT_REPORTS },
{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K65RGB_RAPIDFIRE), HID_QUIRK_NO_INIT_REPORTS | HID_QUIRK_ALWAYS_POLL },
{ HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K70RGB), HID_QUIRK_NO_INIT_REPORTS },
@@ -394,7 +393,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1URBK) },
- { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C) },
#endif
#if IS_ENABLED(CONFIG_HID_ELO)
{ HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009) },
diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c
index 7fa6fe04f1b262..cfbbc39807a69f 100644
--- a/drivers/hid/hid-uclogic-core.c
+++ b/drivers/hid/hid-uclogic-core.c
@@ -526,6 +526,8 @@ static const struct hid_device_id uclogic_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
+ USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S) },
diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c
index cd1233d7e2535f..3c5eea3df3288b 100644
--- a/drivers/hid/hid-uclogic-params.c
+++ b/drivers/hid/hid-uclogic-params.c
@@ -1656,6 +1656,8 @@ int uclogic_params_init(struct uclogic_params *params,
case VID_PID(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_PARBLO_A610_PRO):
case VID_PID(USB_VENDOR_ID_UGEE,
+ USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01_V2):
+ case VID_PID(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L):
case VID_PID(USB_VENDOR_ID_UGEE,
USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_PRO_S):
diff --git a/drivers/hid/intel-ish-hid/ishtp/dma-if.c b/drivers/hid/intel-ish-hid/ishtp/dma-if.c
index 40554c8daca07b..00046cbfd4ed07 100644
--- a/drivers/hid/intel-ish-hid/ishtp/dma-if.c
+++ b/drivers/hid/intel-ish-hid/ishtp/dma-if.c
@@ -104,6 +104,11 @@ void *ishtp_cl_get_dma_send_buf(struct ishtp_device *dev,
int required_slots = (size / DMA_SLOT_SIZE)
+ 1 * (size % DMA_SLOT_SIZE != 0);
+ if (!dev->ishtp_dma_tx_map) {
+ dev_err(dev->devc, "Fail to allocate Tx map\n");
+ return NULL;
+ }
+
spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags);
for (i = 0; i <= (dev->ishtp_dma_num_slots - required_slots); i++) {
free = 1;
@@ -150,6 +155,11 @@ void ishtp_cl_release_dma_acked_mem(struct ishtp_device *dev,
return;
}
+ if (!dev->ishtp_dma_tx_map) {
+ dev_err(dev->devc, "Fail to allocate Tx map\n");
+ return;
+ }
+
i = (msg_addr - dev->ishtp_host_dma_tx_buf) / DMA_SLOT_SIZE;
spin_lock_irqsave(&dev->ishtp_dma_tx_lock, flags);
for (j = 0; j < acked_slots; j++) {
diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c
index cbe43e2567a7fb..64ac5bdee3a623 100644
--- a/drivers/hv/hv_balloon.c
+++ b/drivers/hv/hv_balloon.c
@@ -1963,7 +1963,7 @@ static void hv_balloon_debugfs_init(struct hv_dynmem_device *b)
static void hv_balloon_debugfs_exit(struct hv_dynmem_device *b)
{
- debugfs_remove(debugfs_lookup("hv-balloon", NULL));
+ debugfs_lookup_and_remove("hv-balloon", NULL);
}
#else
diff --git a/drivers/i2c/busses/i2c-axxia.c b/drivers/i2c/busses/i2c-axxia.c
index bdf3b50de8adbf..c1c74ce084071c 100644
--- a/drivers/i2c/busses/i2c-axxia.c
+++ b/drivers/i2c/busses/i2c-axxia.c
@@ -118,7 +118,7 @@
#define SDA_HOLD_TIME 0x90
/**
- * axxia_i2c_dev - I2C device context
+ * struct axxia_i2c_dev - I2C device context
* @base: pointer to register struct
* @msg: pointer to current message
* @msg_r: pointer to current read message (sequence transfer)
diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c
index a3240ece55b2b1..581e02cc979a06 100644
--- a/drivers/i2c/busses/i2c-designware-common.c
+++ b/drivers/i2c/busses/i2c-designware-common.c
@@ -351,7 +351,8 @@ u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
*
* If your hardware is free from tHD;STA issue, try this one.
*/
- return DIV_ROUND_CLOSEST(ic_clk * tSYMBOL, MICRO) - 8 + offset;
+ return DIV_ROUND_CLOSEST_ULL((u64)ic_clk * tSYMBOL, MICRO) -
+ 8 + offset;
else
/*
* Conditional expression:
@@ -367,7 +368,8 @@ u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
* The reason why we need to take into account "tf" here,
* is the same as described in i2c_dw_scl_lcnt().
*/
- return DIV_ROUND_CLOSEST(ic_clk * (tSYMBOL + tf), MICRO) - 3 + offset;
+ return DIV_ROUND_CLOSEST_ULL((u64)ic_clk * (tSYMBOL + tf), MICRO) -
+ 3 + offset;
}
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
@@ -383,7 +385,8 @@ u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
* account the fall time of SCL signal (tf). Default tf value
* should be 0.3 us, for safety.
*/
- return DIV_ROUND_CLOSEST(ic_clk * (tLOW + tf), MICRO) - 1 + offset;
+ return DIV_ROUND_CLOSEST_ULL((u64)ic_clk * (tLOW + tf), MICRO) -
+ 1 + offset;
}
int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev)
diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c
index e499f96506c521..782fe1ef3ca10e 100644
--- a/drivers/i2c/busses/i2c-designware-pcidrv.c
+++ b/drivers/i2c/busses/i2c-designware-pcidrv.c
@@ -396,6 +396,8 @@ static const struct pci_device_id i2_designware_pci_ids[] = {
{ PCI_VDEVICE(ATI, 0x73a4), navi_amd },
{ PCI_VDEVICE(ATI, 0x73e4), navi_amd },
{ PCI_VDEVICE(ATI, 0x73c4), navi_amd },
+ { PCI_VDEVICE(ATI, 0x7444), navi_amd },
+ { PCI_VDEVICE(ATI, 0x7464), navi_amd },
{ 0,}
};
MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids);
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index ba043b5473936b..74182db03a88b3 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -351,13 +351,11 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
if (dev->flags & ACCESS_NO_IRQ_SUSPEND) {
dev_pm_set_driver_flags(&pdev->dev,
- DPM_FLAG_SMART_PREPARE |
- DPM_FLAG_MAY_SKIP_RESUME);
+ DPM_FLAG_SMART_PREPARE);
} else {
dev_pm_set_driver_flags(&pdev->dev,
DPM_FLAG_SMART_PREPARE |
- DPM_FLAG_SMART_SUSPEND |
- DPM_FLAG_MAY_SKIP_RESUME);
+ DPM_FLAG_SMART_SUSPEND);
}
device_enable_async_suspend(&pdev->dev);
@@ -419,21 +417,8 @@ static int dw_i2c_plat_prepare(struct device *dev)
*/
return !has_acpi_companion(dev);
}
-
-static void dw_i2c_plat_complete(struct device *dev)
-{
- /*
- * The device can only be in runtime suspend at this point if it has not
- * been resumed throughout the ending system suspend/resume cycle, so if
- * the platform firmware might mess up with it, request the runtime PM
- * framework to resume it.
- */
- if (pm_runtime_suspended(dev) && pm_resume_via_firmware())
- pm_request_resume(dev);
-}
#else
#define dw_i2c_plat_prepare NULL
-#define dw_i2c_plat_complete NULL
#endif
#ifdef CONFIG_PM
@@ -483,7 +468,6 @@ static int __maybe_unused dw_i2c_plat_resume(struct device *dev)
static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
.prepare = dw_i2c_plat_prepare,
- .complete = dw_i2c_plat_complete,
SET_LATE_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume)
SET_RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend, dw_i2c_plat_runtime_resume, NULL)
};
diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c
index 5af5cffc444ef0..d113bed7954526 100644
--- a/drivers/i2c/busses/i2c-mxs.c
+++ b/drivers/i2c/busses/i2c-mxs.c
@@ -826,8 +826,8 @@ static int mxs_i2c_probe(struct platform_device *pdev)
/* Setup the DMA */
i2c->dmach = dma_request_chan(dev, "rx-tx");
if (IS_ERR(i2c->dmach)) {
- dev_err(dev, "Failed to request dma\n");
- return PTR_ERR(i2c->dmach);
+ return dev_err_probe(dev, PTR_ERR(i2c->dmach),
+ "Failed to request dma\n");
}
platform_set_drvdata(pdev, i2c);
diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
index d1658ed76562b8..b31cf4f18f8548 100644
--- a/drivers/i2c/busses/i2c-rk3x.c
+++ b/drivers/i2c/busses/i2c-rk3x.c
@@ -80,7 +80,7 @@ enum {
#define DEFAULT_SCL_RATE (100 * 1000) /* Hz */
/**
- * struct i2c_spec_values:
+ * struct i2c_spec_values - I2C specification values for various modes
* @min_hold_start_ns: min hold time (repeated) START condition
* @min_low_ns: min LOW period of the SCL clock
* @min_high_ns: min HIGH period of the SCL cloc
@@ -136,7 +136,7 @@ static const struct i2c_spec_values fast_mode_plus_spec = {
};
/**
- * struct rk3x_i2c_calced_timings:
+ * struct rk3x_i2c_calced_timings - calculated V1 timings
* @div_low: Divider output for low
* @div_high: Divider output for high
* @tuning: Used to adjust setup/hold data time,
@@ -159,7 +159,7 @@ enum rk3x_i2c_state {
};
/**
- * struct rk3x_i2c_soc_data:
+ * struct rk3x_i2c_soc_data - SOC-specific data
* @grf_offset: offset inside the grf regmap for setting the i2c type
* @calc_timings: Callback function for i2c timing information calculated
*/
@@ -239,7 +239,8 @@ static inline void rk3x_i2c_clean_ipd(struct rk3x_i2c *i2c)
}
/**
- * Generate a START condition, which triggers a REG_INT_START interrupt.
+ * rk3x_i2c_start - Generate a START condition, which triggers a REG_INT_START interrupt.
+ * @i2c: target controller data
*/
static void rk3x_i2c_start(struct rk3x_i2c *i2c)
{
@@ -258,8 +259,8 @@ static void rk3x_i2c_start(struct rk3x_i2c *i2c)
}
/**
- * Generate a STOP condition, which triggers a REG_INT_STOP interrupt.
- *
+ * rk3x_i2c_stop - Generate a STOP condition, which triggers a REG_INT_STOP interrupt.
+ * @i2c: target controller data
* @error: Error code to return in rk3x_i2c_xfer
*/
static void rk3x_i2c_stop(struct rk3x_i2c *i2c, int error)
@@ -298,7 +299,8 @@ static void rk3x_i2c_stop(struct rk3x_i2c *i2c, int error)
}
/**
- * Setup a read according to i2c->msg
+ * rk3x_i2c_prepare_read - Setup a read according to i2c->msg
+ * @i2c: target controller data
*/
static void rk3x_i2c_prepare_read(struct rk3x_i2c *i2c)
{
@@ -329,7 +331,8 @@ static void rk3x_i2c_prepare_read(struct rk3x_i2c *i2c)
}
/**
- * Fill the transmit buffer with data from i2c->msg
+ * rk3x_i2c_fill_transmit_buf - Fill the transmit buffer with data from i2c->msg
+ * @i2c: target controller data
*/
static void rk3x_i2c_fill_transmit_buf(struct rk3x_i2c *i2c)
{
@@ -532,11 +535,10 @@ out:
}
/**
- * Get timing values of I2C specification
- *
+ * rk3x_i2c_get_spec - Get timing values of I2C specification
* @speed: Desired SCL frequency
*
- * Returns: Matched i2c spec values.
+ * Return: Matched i2c_spec_values.
*/
static const struct i2c_spec_values *rk3x_i2c_get_spec(unsigned int speed)
{
@@ -549,13 +551,12 @@ static const struct i2c_spec_values *rk3x_i2c_get_spec(unsigned int speed)
}
/**
- * Calculate divider values for desired SCL frequency
- *
+ * rk3x_i2c_v0_calc_timings - Calculate divider values for desired SCL frequency
* @clk_rate: I2C input clock rate
* @t: Known I2C timing information
* @t_calc: Caculated rk3x private timings that would be written into regs
*
- * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
+ * Return: %0 on success, -%EINVAL if the goal SCL rate is too slow. In that case
* a best-effort divider value is returned in divs. If the target rate is
* too high, we silently use the highest possible rate.
*/
@@ -710,13 +711,12 @@ static int rk3x_i2c_v0_calc_timings(unsigned long clk_rate,
}
/**
- * Calculate timing values for desired SCL frequency
- *
+ * rk3x_i2c_v1_calc_timings - Calculate timing values for desired SCL frequency
* @clk_rate: I2C input clock rate
* @t: Known I2C timing information
* @t_calc: Caculated rk3x private timings that would be written into regs
*
- * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
+ * Return: %0 on success, -%EINVAL if the goal SCL rate is too slow. In that case
* a best-effort divider value is returned in divs. If the target rate is
* too high, we silently use the highest possible rate.
* The following formulas are v1's method to calculate timings.
@@ -960,14 +960,14 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
}
/**
- * Setup I2C registers for an I2C operation specified by msgs, num.
- *
- * Must be called with i2c->lock held.
- *
+ * rk3x_i2c_setup - Setup I2C registers for an I2C operation specified by msgs, num.
+ * @i2c: target controller data
* @msgs: I2C msgs to process
* @num: Number of msgs
*
- * returns: Number of I2C msgs processed or negative in case of error
+ * Must be called with i2c->lock held.
+ *
+ * Return: Number of I2C msgs processed or negative in case of error
*/
static int rk3x_i2c_setup(struct rk3x_i2c *i2c, struct i2c_msg *msgs, int num)
{
diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c
index a2def6f9380a30..5eac7ea1999312 100644
--- a/drivers/iio/accel/hid-sensor-accel-3d.c
+++ b/drivers/iio/accel/hid-sensor-accel-3d.c
@@ -280,6 +280,7 @@ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
hid_sensor_convert_timestamp(
&accel_state->common_attributes,
*(int64_t *)raw_data);
+ ret = 0;
break;
default:
break;
diff --git a/drivers/iio/adc/berlin2-adc.c b/drivers/iio/adc/berlin2-adc.c
index 3d2e8b4db61ae7..a4e7c7eff5acf4 100644
--- a/drivers/iio/adc/berlin2-adc.c
+++ b/drivers/iio/adc/berlin2-adc.c
@@ -298,8 +298,10 @@ static int berlin2_adc_probe(struct platform_device *pdev)
int ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
- if (!indio_dev)
+ if (!indio_dev) {
+ of_node_put(parent_np);
return -ENOMEM;
+ }
priv = iio_priv(indio_dev);
diff --git a/drivers/iio/adc/imx8qxp-adc.c b/drivers/iio/adc/imx8qxp-adc.c
index 36777b827165a0..f5a0fc9e64c542 100644
--- a/drivers/iio/adc/imx8qxp-adc.c
+++ b/drivers/iio/adc/imx8qxp-adc.c
@@ -86,6 +86,8 @@
#define IMX8QXP_ADC_TIMEOUT msecs_to_jiffies(100)
+#define IMX8QXP_ADC_MAX_FIFO_SIZE 16
+
struct imx8qxp_adc {
struct device *dev;
void __iomem *regs;
@@ -95,6 +97,7 @@ struct imx8qxp_adc {
/* Serialise ADC channel reads */
struct mutex lock;
struct completion completion;
+ u32 fifo[IMX8QXP_ADC_MAX_FIFO_SIZE];
};
#define IMX8QXP_ADC_CHAN(_idx) { \
@@ -238,8 +241,7 @@ static int imx8qxp_adc_read_raw(struct iio_dev *indio_dev,
return ret;
}
- *val = FIELD_GET(IMX8QXP_ADC_RESFIFO_VAL_MASK,
- readl(adc->regs + IMX8QXP_ADR_ADC_RESFIFO));
+ *val = adc->fifo[0];
mutex_unlock(&adc->lock);
return IIO_VAL_INT;
@@ -265,10 +267,15 @@ static irqreturn_t imx8qxp_adc_isr(int irq, void *dev_id)
{
struct imx8qxp_adc *adc = dev_id;
u32 fifo_count;
+ int i;
fifo_count = FIELD_GET(IMX8QXP_ADC_FCTRL_FCOUNT_MASK,
readl(adc->regs + IMX8QXP_ADR_ADC_FCTRL));
+ for (i = 0; i < fifo_count; i++)
+ adc->fifo[i] = FIELD_GET(IMX8QXP_ADC_RESFIFO_VAL_MASK,
+ readl_relaxed(adc->regs + IMX8QXP_ADR_ADC_RESFIFO));
+
if (fifo_count)
complete(&adc->completion);
diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index 6d21ea84fa82de..a428bdb567d52a 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -1520,6 +1520,7 @@ static const struct of_device_id stm32_dfsdm_adc_match[] = {
},
{}
};
+MODULE_DEVICE_TABLE(of, stm32_dfsdm_adc_match);
static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
{
diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c
index f53e8558b560c7..32873fb5f3676d 100644
--- a/drivers/iio/adc/twl6030-gpadc.c
+++ b/drivers/iio/adc/twl6030-gpadc.c
@@ -57,6 +57,18 @@
#define TWL6030_GPADCS BIT(1)
#define TWL6030_GPADCR BIT(0)
+#define USB_VBUS_CTRL_SET 0x04
+#define USB_ID_CTRL_SET 0x06
+
+#define TWL6030_MISC1 0xE4
+#define VBUS_MEAS 0x01
+#define ID_MEAS 0x01
+
+#define VAC_MEAS 0x04
+#define VBAT_MEAS 0x02
+#define BB_MEAS 0x01
+
+
/**
* struct twl6030_chnl_calib - channel calibration
* @gain: slope coefficient for ideal curve
@@ -927,6 +939,26 @@ static int twl6030_gpadc_probe(struct platform_device *pdev)
return ret;
}
+ ret = twl_i2c_write_u8(TWL_MODULE_USB, VBUS_MEAS, USB_VBUS_CTRL_SET);
+ if (ret < 0) {
+ dev_err(dev, "failed to wire up inputs\n");
+ return ret;
+ }
+
+ ret = twl_i2c_write_u8(TWL_MODULE_USB, ID_MEAS, USB_ID_CTRL_SET);
+ if (ret < 0) {
+ dev_err(dev, "failed to wire up inputs\n");
+ return ret;
+ }
+
+ ret = twl_i2c_write_u8(TWL6030_MODULE_ID0,
+ VBAT_MEAS | BB_MEAS | VAC_MEAS,
+ TWL6030_MISC1);
+ if (ret < 0) {
+ dev_err(dev, "failed to wire up inputs\n");
+ return ret;
+ }
+
indio_dev->name = DRIVER_NAME;
indio_dev->info = &twl6030_gpadc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c
index 5b4bdf3a26bb8a..a507d2e1707928 100644
--- a/drivers/iio/adc/xilinx-ams.c
+++ b/drivers/iio/adc/xilinx-ams.c
@@ -1329,7 +1329,7 @@ static int ams_parse_firmware(struct iio_dev *indio_dev)
dev_channels = devm_krealloc(dev, ams_channels, dev_size, GFP_KERNEL);
if (!dev_channels)
- ret = -ENOMEM;
+ return -ENOMEM;
indio_dev->channels = dev_channels;
indio_dev->num_channels = num_channels;
diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c
index 8f0ad022c7f1bc..698c50da1f1091 100644
--- a/drivers/iio/gyro/hid-sensor-gyro-3d.c
+++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c
@@ -231,6 +231,7 @@ static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
gyro_state->timestamp =
hid_sensor_convert_timestamp(&gyro_state->common_attributes,
*(s64 *)raw_data);
+ ret = 0;
break;
default:
break;
diff --git a/drivers/iio/imu/fxos8700_core.c b/drivers/iio/imu/fxos8700_core.c
index 423cfe526f2a13..6d189c4b9ff962 100644
--- a/drivers/iio/imu/fxos8700_core.c
+++ b/drivers/iio/imu/fxos8700_core.c
@@ -10,6 +10,7 @@
#include <linux/regmap.h>
#include <linux/acpi.h>
#include <linux/bitops.h>
+#include <linux/bitfield.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -144,9 +145,8 @@
#define FXOS8700_NVM_DATA_BNK0 0xa7
/* Bit definitions for FXOS8700_CTRL_REG1 */
-#define FXOS8700_CTRL_ODR_MSK 0x38
#define FXOS8700_CTRL_ODR_MAX 0x00
-#define FXOS8700_CTRL_ODR_MIN GENMASK(4, 3)
+#define FXOS8700_CTRL_ODR_MSK GENMASK(5, 3)
/* Bit definitions for FXOS8700_M_CTRL_REG1 */
#define FXOS8700_HMS_MASK GENMASK(1, 0)
@@ -320,7 +320,7 @@ static enum fxos8700_sensor fxos8700_to_sensor(enum iio_chan_type iio_type)
switch (iio_type) {
case IIO_ACCEL:
return FXOS8700_ACCEL;
- case IIO_ANGL_VEL:
+ case IIO_MAGN:
return FXOS8700_MAGN;
default:
return -EINVAL;
@@ -345,15 +345,35 @@ static int fxos8700_set_active_mode(struct fxos8700_data *data,
static int fxos8700_set_scale(struct fxos8700_data *data,
enum fxos8700_sensor t, int uscale)
{
- int i;
+ int i, ret, val;
+ bool active_mode;
static const int scale_num = ARRAY_SIZE(fxos8700_accel_scale);
struct device *dev = regmap_get_device(data->regmap);
if (t == FXOS8700_MAGN) {
- dev_err(dev, "Magnetometer scale is locked at 1200uT\n");
+ dev_err(dev, "Magnetometer scale is locked at 0.001Gs\n");
return -EINVAL;
}
+ /*
+ * When device is in active mode, it failed to set an ACCEL
+ * full-scale range(2g/4g/8g) in FXOS8700_XYZ_DATA_CFG.
+ * This is not align with the datasheet, but it is a fxos8700
+ * chip behavier. Set the device in standby mode before setting
+ * an ACCEL full-scale range.
+ */
+ ret = regmap_read(data->regmap, FXOS8700_CTRL_REG1, &val);
+ if (ret)
+ return ret;
+
+ active_mode = val & FXOS8700_ACTIVE;
+ if (active_mode) {
+ ret = regmap_write(data->regmap, FXOS8700_CTRL_REG1,
+ val & ~FXOS8700_ACTIVE);
+ if (ret)
+ return ret;
+ }
+
for (i = 0; i < scale_num; i++)
if (fxos8700_accel_scale[i].uscale == uscale)
break;
@@ -361,8 +381,12 @@ static int fxos8700_set_scale(struct fxos8700_data *data,
if (i == scale_num)
return -EINVAL;
- return regmap_write(data->regmap, FXOS8700_XYZ_DATA_CFG,
+ ret = regmap_write(data->regmap, FXOS8700_XYZ_DATA_CFG,
fxos8700_accel_scale[i].bits);
+ if (ret)
+ return ret;
+ return regmap_write(data->regmap, FXOS8700_CTRL_REG1,
+ active_mode);
}
static int fxos8700_get_scale(struct fxos8700_data *data,
@@ -372,7 +396,7 @@ static int fxos8700_get_scale(struct fxos8700_data *data,
static const int scale_num = ARRAY_SIZE(fxos8700_accel_scale);
if (t == FXOS8700_MAGN) {
- *uscale = 1200; /* Magnetometer is locked at 1200uT */
+ *uscale = 1000; /* Magnetometer is locked at 0.001Gs */
return 0;
}
@@ -394,22 +418,61 @@ static int fxos8700_get_data(struct fxos8700_data *data, int chan_type,
int axis, int *val)
{
u8 base, reg;
+ s16 tmp;
int ret;
- enum fxos8700_sensor type = fxos8700_to_sensor(chan_type);
- base = type ? FXOS8700_OUT_X_MSB : FXOS8700_M_OUT_X_MSB;
+ /*
+ * Different register base addresses varies with channel types.
+ * This bug hasn't been noticed before because using an enum is
+ * really hard to read. Use an a switch statement to take over that.
+ */
+ switch (chan_type) {
+ case IIO_ACCEL:
+ base = FXOS8700_OUT_X_MSB;
+ break;
+ case IIO_MAGN:
+ base = FXOS8700_M_OUT_X_MSB;
+ break;
+ default:
+ return -EINVAL;
+ }
/* Block read 6 bytes of device output registers to avoid data loss */
ret = regmap_bulk_read(data->regmap, base, data->buf,
- FXOS8700_DATA_BUF_SIZE);
+ sizeof(data->buf));
if (ret)
return ret;
/* Convert axis to buffer index */
reg = axis - IIO_MOD_X;
+ /*
+ * Convert to native endianness. The accel data and magn data
+ * are signed, so a forced type conversion is needed.
+ */
+ tmp = be16_to_cpu(data->buf[reg]);
+
+ /*
+ * ACCEL output data registers contain the X-axis, Y-axis, and Z-axis
+ * 14-bit left-justified sample data and MAGN output data registers
+ * contain the X-axis, Y-axis, and Z-axis 16-bit sample data. Apply
+ * a signed 2 bits right shift to the readback raw data from ACCEL
+ * output data register and keep that from MAGN sensor as the origin.
+ * Value should be extended to 32 bit.
+ */
+ switch (chan_type) {
+ case IIO_ACCEL:
+ tmp = tmp >> 2;
+ break;
+ case IIO_MAGN:
+ /* Nothing to do */
+ break;
+ default:
+ return -EINVAL;
+ }
+
/* Convert to native endianness */
- *val = sign_extend32(be16_to_cpu(data->buf[reg]), 15);
+ *val = sign_extend32(tmp, 15);
return 0;
}
@@ -445,10 +508,9 @@ static int fxos8700_set_odr(struct fxos8700_data *data, enum fxos8700_sensor t,
if (i >= odr_num)
return -EINVAL;
- return regmap_update_bits(data->regmap,
- FXOS8700_CTRL_REG1,
- FXOS8700_CTRL_ODR_MSK + FXOS8700_ACTIVE,
- fxos8700_odr[i].bits << 3 | active_mode);
+ val &= ~FXOS8700_CTRL_ODR_MSK;
+ val |= FIELD_PREP(FXOS8700_CTRL_ODR_MSK, fxos8700_odr[i].bits) | FXOS8700_ACTIVE;
+ return regmap_write(data->regmap, FXOS8700_CTRL_REG1, val);
}
static int fxos8700_get_odr(struct fxos8700_data *data, enum fxos8700_sensor t,
@@ -461,7 +523,7 @@ static int fxos8700_get_odr(struct fxos8700_data *data, enum fxos8700_sensor t,
if (ret)
return ret;
- val &= FXOS8700_CTRL_ODR_MSK;
+ val = FIELD_GET(FXOS8700_CTRL_ODR_MSK, val);
for (i = 0; i < odr_num; i++)
if (val == fxos8700_odr[i].bits)
@@ -526,7 +588,7 @@ static IIO_CONST_ATTR(in_accel_sampling_frequency_available,
static IIO_CONST_ATTR(in_magn_sampling_frequency_available,
"1.5625 6.25 12.5 50 100 200 400 800");
static IIO_CONST_ATTR(in_accel_scale_available, "0.000244 0.000488 0.000976");
-static IIO_CONST_ATTR(in_magn_scale_available, "0.000001200");
+static IIO_CONST_ATTR(in_magn_scale_available, "0.001000");
static struct attribute *fxos8700_attrs[] = {
&iio_const_attr_in_accel_sampling_frequency_available.dev_attr.attr,
@@ -592,14 +654,19 @@ static int fxos8700_chip_init(struct fxos8700_data *data, bool use_spi)
if (ret)
return ret;
- /* Max ODR (800Hz individual or 400Hz hybrid), active mode */
- ret = regmap_write(data->regmap, FXOS8700_CTRL_REG1,
- FXOS8700_CTRL_ODR_MAX | FXOS8700_ACTIVE);
+ /*
+ * Set max full-scale range (+/-8G) for ACCEL sensor in chip
+ * initialization then activate the device.
+ */
+ ret = regmap_write(data->regmap, FXOS8700_XYZ_DATA_CFG, MODE_8G);
if (ret)
return ret;
- /* Set for max full-scale range (+/-8G) */
- return regmap_write(data->regmap, FXOS8700_XYZ_DATA_CFG, MODE_8G);
+ /* Max ODR (800Hz individual or 400Hz hybrid), active mode */
+ return regmap_update_bits(data->regmap, FXOS8700_CTRL_REG1,
+ FXOS8700_CTRL_ODR_MSK | FXOS8700_ACTIVE,
+ FIELD_PREP(FXOS8700_CTRL_ODR_MSK, FXOS8700_CTRL_ODR_MAX) |
+ FXOS8700_ACTIVE);
}
static void fxos8700_chip_uninit(void *data)
diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig
index f6660847fb58d4..8c16cdacf2f212 100644
--- a/drivers/iio/imu/st_lsm6dsx/Kconfig
+++ b/drivers/iio/imu/st_lsm6dsx/Kconfig
@@ -4,6 +4,7 @@ config IIO_ST_LSM6DSX
tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors"
depends on (I2C || SPI || I3C)
select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
select IIO_KFIFO_BUF
select IIO_ST_LSM6DSX_I2C if (I2C)
select IIO_ST_LSM6DSX_SPI if (SPI_MASTER)
diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c
index 001055d097509e..b1674a5bfa368d 100644
--- a/drivers/iio/light/cm32181.c
+++ b/drivers/iio/light/cm32181.c
@@ -440,6 +440,8 @@ static int cm32181_probe(struct i2c_client *client)
if (!indio_dev)
return -ENOMEM;
+ i2c_set_clientdata(client, indio_dev);
+
/*
* Some ACPI systems list 2 I2C resources for the CM3218 sensor, the
* SMBus Alert Response Address (ARA, 0x0c) and the actual I2C address.
@@ -460,8 +462,6 @@ static int cm32181_probe(struct i2c_client *client)
return PTR_ERR(client);
}
- i2c_set_clientdata(client, indio_dev);
-
cm32181 = iio_priv(indio_dev);
cm32181->client = client;
cm32181->dev = dev;
@@ -490,7 +490,8 @@ static int cm32181_probe(struct i2c_client *client)
static int cm32181_suspend(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct cm32181_chip *cm32181 = iio_priv(dev_get_drvdata(dev));
+ struct i2c_client *client = cm32181->client;
return i2c_smbus_write_word_data(client, CM32181_REG_ADDR_CMD,
CM32181_CMD_ALS_DISABLE);
@@ -498,8 +499,8 @@ static int cm32181_suspend(struct device *dev)
static int cm32181_resume(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
struct cm32181_chip *cm32181 = iio_priv(dev_get_drvdata(dev));
+ struct i2c_client *client = cm32181->client;
return i2c_smbus_write_word_data(client, CM32181_REG_ADDR_CMD,
cm32181->conf_regs[CM32181_REG_ADDR_CMD]);
diff --git a/drivers/infiniband/core/umem_dmabuf.c b/drivers/infiniband/core/umem_dmabuf.c
index 43b26bc122885e..39357dc2d229f0 100644
--- a/drivers/infiniband/core/umem_dmabuf.c
+++ b/drivers/infiniband/core/umem_dmabuf.c
@@ -26,8 +26,8 @@ int ib_umem_dmabuf_map_pages(struct ib_umem_dmabuf *umem_dmabuf)
if (umem_dmabuf->sgt)
goto wait_fence;
- sgt = dma_buf_map_attachment_unlocked(umem_dmabuf->attach,
- DMA_BIDIRECTIONAL);
+ sgt = dma_buf_map_attachment(umem_dmabuf->attach,
+ DMA_BIDIRECTIONAL);
if (IS_ERR(sgt))
return PTR_ERR(sgt);
@@ -103,8 +103,8 @@ void ib_umem_dmabuf_unmap_pages(struct ib_umem_dmabuf *umem_dmabuf)
umem_dmabuf->last_sg_trim = 0;
}
- dma_buf_unmap_attachment_unlocked(umem_dmabuf->attach, umem_dmabuf->sgt,
- DMA_BIDIRECTIONAL);
+ dma_buf_unmap_attachment(umem_dmabuf->attach, umem_dmabuf->sgt,
+ DMA_BIDIRECTIONAL);
umem_dmabuf->sgt = NULL;
}
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index 26b021f43ba40f..11b1c1603aeb44 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -2957,15 +2957,18 @@ EXPORT_SYMBOL(__rdma_block_iter_start);
bool __rdma_block_iter_next(struct ib_block_iter *biter)
{
unsigned int block_offset;
+ unsigned int sg_delta;
if (!biter->__sg_nents || !biter->__sg)
return false;
biter->__dma_addr = sg_dma_address(biter->__sg) + biter->__sg_advance;
block_offset = biter->__dma_addr & (BIT_ULL(biter->__pg_bit) - 1);
- biter->__sg_advance += BIT_ULL(biter->__pg_bit) - block_offset;
+ sg_delta = BIT_ULL(biter->__pg_bit) - block_offset;
- if (biter->__sg_advance >= sg_dma_len(biter->__sg)) {
+ if (sg_dma_len(biter->__sg) - biter->__sg_advance > sg_delta) {
+ biter->__sg_advance += sg_delta;
+ } else {
biter->__sg_advance = 0;
biter->__sg = sg_next(biter->__sg);
biter->__sg_nents--;
diff --git a/drivers/infiniband/hw/hfi1/file_ops.c b/drivers/infiniband/hw/hfi1/file_ops.c
index f5f9269fdc1627..7c5d487ec9168e 100644
--- a/drivers/infiniband/hw/hfi1/file_ops.c
+++ b/drivers/infiniband/hw/hfi1/file_ops.c
@@ -1318,12 +1318,15 @@ static int user_exp_rcv_setup(struct hfi1_filedata *fd, unsigned long arg,
addr = arg + offsetof(struct hfi1_tid_info, tidcnt);
if (copy_to_user((void __user *)addr, &tinfo.tidcnt,
sizeof(tinfo.tidcnt)))
- return -EFAULT;
+ ret = -EFAULT;
addr = arg + offsetof(struct hfi1_tid_info, length);
- if (copy_to_user((void __user *)addr, &tinfo.length,
+ if (!ret && copy_to_user((void __user *)addr, &tinfo.length,
sizeof(tinfo.length)))
ret = -EFAULT;
+
+ if (ret)
+ hfi1_user_exp_rcv_invalid(fd, &tinfo);
}
return ret;
diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.c b/drivers/infiniband/hw/hfi1/user_exp_rcv.c
index 186d3029126069..350884d5f08961 100644
--- a/drivers/infiniband/hw/hfi1/user_exp_rcv.c
+++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.c
@@ -23,18 +23,25 @@ static void cacheless_tid_rb_remove(struct hfi1_filedata *fdata,
static bool tid_rb_invalidate(struct mmu_interval_notifier *mni,
const struct mmu_notifier_range *range,
unsigned long cur_seq);
+static bool tid_cover_invalidate(struct mmu_interval_notifier *mni,
+ const struct mmu_notifier_range *range,
+ unsigned long cur_seq);
static int program_rcvarray(struct hfi1_filedata *fd, struct tid_user_buf *,
struct tid_group *grp,
unsigned int start, u16 count,
u32 *tidlist, unsigned int *tididx,
unsigned int *pmapped);
-static int unprogram_rcvarray(struct hfi1_filedata *fd, u32 tidinfo,
- struct tid_group **grp);
+static int unprogram_rcvarray(struct hfi1_filedata *fd, u32 tidinfo);
+static void __clear_tid_node(struct hfi1_filedata *fd,
+ struct tid_rb_node *node);
static void clear_tid_node(struct hfi1_filedata *fd, struct tid_rb_node *node);
static const struct mmu_interval_notifier_ops tid_mn_ops = {
.invalidate = tid_rb_invalidate,
};
+static const struct mmu_interval_notifier_ops tid_cover_ops = {
+ .invalidate = tid_cover_invalidate,
+};
/*
* Initialize context and file private data needed for Expected
@@ -153,16 +160,11 @@ static void unpin_rcv_pages(struct hfi1_filedata *fd,
static int pin_rcv_pages(struct hfi1_filedata *fd, struct tid_user_buf *tidbuf)
{
int pinned;
- unsigned int npages;
+ unsigned int npages = tidbuf->npages;
unsigned long vaddr = tidbuf->vaddr;
struct page **pages = NULL;
struct hfi1_devdata *dd = fd->uctxt->dd;
- /* Get the number of pages the user buffer spans */
- npages = num_user_pages(vaddr, tidbuf->length);
- if (!npages)
- return -EINVAL;
-
if (npages > fd->uctxt->expected_count) {
dd_dev_err(dd, "Expected buffer too big\n");
return -EINVAL;
@@ -189,7 +191,6 @@ static int pin_rcv_pages(struct hfi1_filedata *fd, struct tid_user_buf *tidbuf)
return pinned;
}
tidbuf->pages = pages;
- tidbuf->npages = npages;
fd->tid_n_pinned += pinned;
return pinned;
}
@@ -253,53 +254,66 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd,
tididx = 0, mapped, mapped_pages = 0;
u32 *tidlist = NULL;
struct tid_user_buf *tidbuf;
+ unsigned long mmu_seq = 0;
if (!PAGE_ALIGNED(tinfo->vaddr))
return -EINVAL;
+ if (tinfo->length == 0)
+ return -EINVAL;
tidbuf = kzalloc(sizeof(*tidbuf), GFP_KERNEL);
if (!tidbuf)
return -ENOMEM;
+ mutex_init(&tidbuf->cover_mutex);
tidbuf->vaddr = tinfo->vaddr;
tidbuf->length = tinfo->length;
+ tidbuf->npages = num_user_pages(tidbuf->vaddr, tidbuf->length);
tidbuf->psets = kcalloc(uctxt->expected_count, sizeof(*tidbuf->psets),
GFP_KERNEL);
if (!tidbuf->psets) {
- kfree(tidbuf);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto fail_release_mem;
+ }
+
+ if (fd->use_mn) {
+ ret = mmu_interval_notifier_insert(
+ &tidbuf->notifier, current->mm,
+ tidbuf->vaddr, tidbuf->npages * PAGE_SIZE,
+ &tid_cover_ops);
+ if (ret)
+ goto fail_release_mem;
+ mmu_seq = mmu_interval_read_begin(&tidbuf->notifier);
}
pinned = pin_rcv_pages(fd, tidbuf);
if (pinned <= 0) {
- kfree(tidbuf->psets);
- kfree(tidbuf);
- return pinned;
+ ret = (pinned < 0) ? pinned : -ENOSPC;
+ goto fail_unpin;
}
/* Find sets of physically contiguous pages */
tidbuf->n_psets = find_phys_blocks(tidbuf, pinned);
- /*
- * We don't need to access this under a lock since tid_used is per
- * process and the same process cannot be in hfi1_user_exp_rcv_clear()
- * and hfi1_user_exp_rcv_setup() at the same time.
- */
+ /* Reserve the number of expected tids to be used. */
spin_lock(&fd->tid_lock);
if (fd->tid_used + tidbuf->n_psets > fd->tid_limit)
pageset_count = fd->tid_limit - fd->tid_used;
else
pageset_count = tidbuf->n_psets;
+ fd->tid_used += pageset_count;
spin_unlock(&fd->tid_lock);
- if (!pageset_count)
- goto bail;
+ if (!pageset_count) {
+ ret = -ENOSPC;
+ goto fail_unreserve;
+ }
ngroups = pageset_count / dd->rcv_entries.group_size;
tidlist = kcalloc(pageset_count, sizeof(*tidlist), GFP_KERNEL);
if (!tidlist) {
ret = -ENOMEM;
- goto nomem;
+ goto fail_unreserve;
}
tididx = 0;
@@ -395,43 +409,78 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd,
}
unlock:
mutex_unlock(&uctxt->exp_mutex);
-nomem:
hfi1_cdbg(TID, "total mapped: tidpairs:%u pages:%u (%d)", tididx,
mapped_pages, ret);
- if (tididx) {
- spin_lock(&fd->tid_lock);
- fd->tid_used += tididx;
- spin_unlock(&fd->tid_lock);
- tinfo->tidcnt = tididx;
- tinfo->length = mapped_pages * PAGE_SIZE;
-
- if (copy_to_user(u64_to_user_ptr(tinfo->tidlist),
- tidlist, sizeof(tidlist[0]) * tididx)) {
- /*
- * On failure to copy to the user level, we need to undo
- * everything done so far so we don't leak resources.
- */
- tinfo->tidlist = (unsigned long)&tidlist;
- hfi1_user_exp_rcv_clear(fd, tinfo);
- tinfo->tidlist = 0;
- ret = -EFAULT;
- goto bail;
+
+ /* fail if nothing was programmed, set error if none provided */
+ if (tididx == 0) {
+ if (ret >= 0)
+ ret = -ENOSPC;
+ goto fail_unreserve;
+ }
+
+ /* adjust reserved tid_used to actual count */
+ spin_lock(&fd->tid_lock);
+ fd->tid_used -= pageset_count - tididx;
+ spin_unlock(&fd->tid_lock);
+
+ /* unpin all pages not covered by a TID */
+ unpin_rcv_pages(fd, tidbuf, NULL, mapped_pages, pinned - mapped_pages,
+ false);
+
+ if (fd->use_mn) {
+ /* check for an invalidate during setup */
+ bool fail = false;
+
+ mutex_lock(&tidbuf->cover_mutex);
+ fail = mmu_interval_read_retry(&tidbuf->notifier, mmu_seq);
+ mutex_unlock(&tidbuf->cover_mutex);
+
+ if (fail) {
+ ret = -EBUSY;
+ goto fail_unprogram;
}
}
- /*
- * If not everything was mapped (due to insufficient RcvArray entries,
- * for example), unpin all unmapped pages so we can pin them nex time.
- */
- if (mapped_pages != pinned)
- unpin_rcv_pages(fd, tidbuf, NULL, mapped_pages,
- (pinned - mapped_pages), false);
-bail:
+ tinfo->tidcnt = tididx;
+ tinfo->length = mapped_pages * PAGE_SIZE;
+
+ if (copy_to_user(u64_to_user_ptr(tinfo->tidlist),
+ tidlist, sizeof(tidlist[0]) * tididx)) {
+ ret = -EFAULT;
+ goto fail_unprogram;
+ }
+
+ if (fd->use_mn)
+ mmu_interval_notifier_remove(&tidbuf->notifier);
+ kfree(tidbuf->pages);
kfree(tidbuf->psets);
+ kfree(tidbuf);
kfree(tidlist);
+ return 0;
+
+fail_unprogram:
+ /* unprogram, unmap, and unpin all allocated TIDs */
+ tinfo->tidlist = (unsigned long)tidlist;
+ hfi1_user_exp_rcv_clear(fd, tinfo);
+ tinfo->tidlist = 0;
+ pinned = 0; /* nothing left to unpin */
+ pageset_count = 0; /* nothing left reserved */
+fail_unreserve:
+ spin_lock(&fd->tid_lock);
+ fd->tid_used -= pageset_count;
+ spin_unlock(&fd->tid_lock);
+fail_unpin:
+ if (fd->use_mn)
+ mmu_interval_notifier_remove(&tidbuf->notifier);
+ if (pinned > 0)
+ unpin_rcv_pages(fd, tidbuf, NULL, 0, pinned, false);
+fail_release_mem:
kfree(tidbuf->pages);
+ kfree(tidbuf->psets);
kfree(tidbuf);
- return ret > 0 ? 0 : ret;
+ kfree(tidlist);
+ return ret;
}
int hfi1_user_exp_rcv_clear(struct hfi1_filedata *fd,
@@ -452,7 +501,7 @@ int hfi1_user_exp_rcv_clear(struct hfi1_filedata *fd,
mutex_lock(&uctxt->exp_mutex);
for (tididx = 0; tididx < tinfo->tidcnt; tididx++) {
- ret = unprogram_rcvarray(fd, tidinfo[tididx], NULL);
+ ret = unprogram_rcvarray(fd, tidinfo[tididx]);
if (ret) {
hfi1_cdbg(TID, "Failed to unprogram rcv array %d",
ret);
@@ -706,6 +755,7 @@ static int set_rcvarray_entry(struct hfi1_filedata *fd,
}
node->fdata = fd;
+ mutex_init(&node->invalidate_mutex);
node->phys = page_to_phys(pages[0]);
node->npages = npages;
node->rcventry = rcventry;
@@ -721,11 +771,6 @@ static int set_rcvarray_entry(struct hfi1_filedata *fd,
&tid_mn_ops);
if (ret)
goto out_unmap;
- /*
- * FIXME: This is in the wrong order, the notifier should be
- * established before the pages are pinned by pin_rcv_pages.
- */
- mmu_interval_read_begin(&node->notifier);
}
fd->entry_to_rb[node->rcventry - uctxt->expected_base] = node;
@@ -745,8 +790,7 @@ out_unmap:
return -EFAULT;
}
-static int unprogram_rcvarray(struct hfi1_filedata *fd, u32 tidinfo,
- struct tid_group **grp)
+static int unprogram_rcvarray(struct hfi1_filedata *fd, u32 tidinfo)
{
struct hfi1_ctxtdata *uctxt = fd->uctxt;
struct hfi1_devdata *dd = uctxt->dd;
@@ -769,9 +813,6 @@ static int unprogram_rcvarray(struct hfi1_filedata *fd, u32 tidinfo,
if (!node || node->rcventry != (uctxt->expected_base + rcventry))
return -EBADF;
- if (grp)
- *grp = node->grp;
-
if (fd->use_mn)
mmu_interval_notifier_remove(&node->notifier);
cacheless_tid_rb_remove(fd, node);
@@ -779,23 +820,34 @@ static int unprogram_rcvarray(struct hfi1_filedata *fd, u32 tidinfo,
return 0;
}
-static void clear_tid_node(struct hfi1_filedata *fd, struct tid_rb_node *node)
+static void __clear_tid_node(struct hfi1_filedata *fd, struct tid_rb_node *node)
{
struct hfi1_ctxtdata *uctxt = fd->uctxt;
struct hfi1_devdata *dd = uctxt->dd;
+ mutex_lock(&node->invalidate_mutex);
+ if (node->freed)
+ goto done;
+ node->freed = true;
+
trace_hfi1_exp_tid_unreg(uctxt->ctxt, fd->subctxt, node->rcventry,
node->npages,
node->notifier.interval_tree.start, node->phys,
node->dma_addr);
- /*
- * Make sure device has seen the write before we unpin the
- * pages.
- */
+ /* Make sure device has seen the write before pages are unpinned */
hfi1_put_tid(dd, node->rcventry, PT_INVALID_FLUSH, 0, 0);
unpin_rcv_pages(fd, NULL, node, 0, node->npages, true);
+done:
+ mutex_unlock(&node->invalidate_mutex);
+}
+
+static void clear_tid_node(struct hfi1_filedata *fd, struct tid_rb_node *node)
+{
+ struct hfi1_ctxtdata *uctxt = fd->uctxt;
+
+ __clear_tid_node(fd, node);
node->grp->used--;
node->grp->map &= ~(1 << (node->rcventry - node->grp->base));
@@ -854,10 +906,16 @@ static bool tid_rb_invalidate(struct mmu_interval_notifier *mni,
if (node->freed)
return true;
+ /* take action only if unmapping */
+ if (range->event != MMU_NOTIFY_UNMAP)
+ return true;
+
trace_hfi1_exp_tid_inval(uctxt->ctxt, fdata->subctxt,
node->notifier.interval_tree.start,
node->rcventry, node->npages, node->dma_addr);
- node->freed = true;
+
+ /* clear the hardware rcvarray entry */
+ __clear_tid_node(fdata, node);
spin_lock(&fdata->invalid_lock);
if (fdata->invalid_tid_idx < uctxt->expected_count) {
@@ -887,6 +945,23 @@ static bool tid_rb_invalidate(struct mmu_interval_notifier *mni,
return true;
}
+static bool tid_cover_invalidate(struct mmu_interval_notifier *mni,
+ const struct mmu_notifier_range *range,
+ unsigned long cur_seq)
+{
+ struct tid_user_buf *tidbuf =
+ container_of(mni, struct tid_user_buf, notifier);
+
+ /* take action only if unmapping */
+ if (range->event == MMU_NOTIFY_UNMAP) {
+ mutex_lock(&tidbuf->cover_mutex);
+ mmu_interval_set_seq(mni, cur_seq);
+ mutex_unlock(&tidbuf->cover_mutex);
+ }
+
+ return true;
+}
+
static void cacheless_tid_rb_remove(struct hfi1_filedata *fdata,
struct tid_rb_node *tnode)
{
diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.h b/drivers/infiniband/hw/hfi1/user_exp_rcv.h
index 8c53e416bf843c..f8ee997d0050ee 100644
--- a/drivers/infiniband/hw/hfi1/user_exp_rcv.h
+++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.h
@@ -16,6 +16,8 @@ struct tid_pageset {
};
struct tid_user_buf {
+ struct mmu_interval_notifier notifier;
+ struct mutex cover_mutex;
unsigned long vaddr;
unsigned long length;
unsigned int npages;
@@ -27,6 +29,7 @@ struct tid_user_buf {
struct tid_rb_node {
struct mmu_interval_notifier notifier;
struct hfi1_filedata *fdata;
+ struct mutex invalidate_mutex; /* covers hw removal */
unsigned long phys;
struct tid_group *grp;
u32 rcventry;
diff --git a/drivers/infiniband/hw/irdma/cm.c b/drivers/infiniband/hw/irdma/cm.c
index 7b086fe63a2459..195aa9ea18b6ca 100644
--- a/drivers/infiniband/hw/irdma/cm.c
+++ b/drivers/infiniband/hw/irdma/cm.c
@@ -1722,6 +1722,9 @@ static int irdma_add_mqh_4(struct irdma_device *iwdev,
continue;
idev = in_dev_get(ip_dev);
+ if (!idev)
+ continue;
+
in_dev_for_each_ifa_rtnl(ifa, idev) {
ibdev_dbg(&iwdev->ibdev,
"CM: Allocating child CM Listener forIP=%pI4, vlan_id=%d, MAC=%pM\n",
diff --git a/drivers/infiniband/hw/mana/qp.c b/drivers/infiniband/hw/mana/qp.c
index ea15ec77e3212a..54b61930a7fdb3 100644
--- a/drivers/infiniband/hw/mana/qp.c
+++ b/drivers/infiniband/hw/mana/qp.c
@@ -289,7 +289,7 @@ static int mana_ib_create_qp_raw(struct ib_qp *ibqp, struct ib_pd *ibpd,
/* IB ports start with 1, MANA Ethernet ports start with 0 */
port = ucmd.port;
- if (ucmd.port > mc->num_ports)
+ if (port < 1 || port > mc->num_ports)
return -EINVAL;
if (attr->cap.max_send_wr > MAX_SEND_BUFFERS_PER_QUEUE) {
diff --git a/drivers/infiniband/hw/mlx5/counters.c b/drivers/infiniband/hw/mlx5/counters.c
index 945758f3952365..3e1272695d993b 100644
--- a/drivers/infiniband/hw/mlx5/counters.c
+++ b/drivers/infiniband/hw/mlx5/counters.c
@@ -278,7 +278,6 @@ static int do_get_hw_stats(struct ib_device *ibdev,
const struct mlx5_ib_counters *cnts = get_counters(dev, port_num - 1);
struct mlx5_core_dev *mdev;
int ret, num_counters;
- u32 mdev_port_num;
if (!stats)
return -EINVAL;
@@ -299,8 +298,9 @@ static int do_get_hw_stats(struct ib_device *ibdev,
}
if (MLX5_CAP_GEN(dev->mdev, cc_query_allowed)) {
- mdev = mlx5_ib_get_native_port_mdev(dev, port_num,
- &mdev_port_num);
+ if (!port_num)
+ port_num = 1;
+ mdev = mlx5_ib_get_native_port_mdev(dev, port_num, NULL);
if (!mdev) {
/* If port is not affiliated yet, its in down state
* which doesn't have any counters yet, so it would be
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index 40d9410ec30334..cf953d23d18dae 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -4502,6 +4502,40 @@ static bool mlx5_ib_modify_qp_allowed(struct mlx5_ib_dev *dev,
return false;
}
+static int validate_rd_atomic(struct mlx5_ib_dev *dev, struct ib_qp_attr *attr,
+ int attr_mask, enum ib_qp_type qp_type)
+{
+ int log_max_ra_res;
+ int log_max_ra_req;
+
+ if (qp_type == MLX5_IB_QPT_DCI) {
+ log_max_ra_res = 1 << MLX5_CAP_GEN(dev->mdev,
+ log_max_ra_res_dc);
+ log_max_ra_req = 1 << MLX5_CAP_GEN(dev->mdev,
+ log_max_ra_req_dc);
+ } else {
+ log_max_ra_res = 1 << MLX5_CAP_GEN(dev->mdev,
+ log_max_ra_res_qp);
+ log_max_ra_req = 1 << MLX5_CAP_GEN(dev->mdev,
+ log_max_ra_req_qp);
+ }
+
+ if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC &&
+ attr->max_rd_atomic > log_max_ra_res) {
+ mlx5_ib_dbg(dev, "invalid max_rd_atomic value %d\n",
+ attr->max_rd_atomic);
+ return false;
+ }
+
+ if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC &&
+ attr->max_dest_rd_atomic > log_max_ra_req) {
+ mlx5_ib_dbg(dev, "invalid max_dest_rd_atomic value %d\n",
+ attr->max_dest_rd_atomic);
+ return false;
+ }
+ return true;
+}
+
int mlx5_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
int attr_mask, struct ib_udata *udata)
{
@@ -4589,21 +4623,8 @@ int mlx5_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
goto out;
}
- if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC &&
- attr->max_rd_atomic >
- (1 << MLX5_CAP_GEN(dev->mdev, log_max_ra_res_qp))) {
- mlx5_ib_dbg(dev, "invalid max_rd_atomic value %d\n",
- attr->max_rd_atomic);
- goto out;
- }
-
- if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC &&
- attr->max_dest_rd_atomic >
- (1 << MLX5_CAP_GEN(dev->mdev, log_max_ra_req_qp))) {
- mlx5_ib_dbg(dev, "invalid max_dest_rd_atomic value %d\n",
- attr->max_dest_rd_atomic);
+ if (!validate_rd_atomic(dev, attr, attr_mask, qp_type))
goto out;
- }
if (cur_state == new_state && cur_state == IB_QPS_RESET) {
err = 0;
diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.c b/drivers/infiniband/hw/usnic/usnic_uiom.c
index c301b3be9f303d..a2857accc4273e 100644
--- a/drivers/infiniband/hw/usnic/usnic_uiom.c
+++ b/drivers/infiniband/hw/usnic/usnic_uiom.c
@@ -276,8 +276,8 @@ iter_chunk:
size = pa_end - pa_start + PAGE_SIZE;
usnic_dbg("va 0x%lx pa %pa size 0x%zx flags 0x%x",
va_start, &pa_start, size, flags);
- err = iommu_map(pd->domain, va_start, pa_start,
- size, flags);
+ err = iommu_map_atomic(pd->domain, va_start,
+ pa_start, size, flags);
if (err) {
usnic_err("Failed to map va 0x%lx pa %pa size 0x%zx with err %d\n",
va_start, &pa_start, size, err);
@@ -293,8 +293,8 @@ iter_chunk:
size = pa - pa_start + PAGE_SIZE;
usnic_dbg("va 0x%lx pa %pa size 0x%zx flags 0x%x\n",
va_start, &pa_start, size, flags);
- err = iommu_map(pd->domain, va_start, pa_start,
- size, flags);
+ err = iommu_map_atomic(pd->domain, va_start,
+ pa_start, size, flags);
if (err) {
usnic_err("Failed to map va 0x%lx pa %pa size 0x%zx with err %d\n",
va_start, &pa_start, size, err);
diff --git a/drivers/infiniband/sw/rxe/rxe_param.h b/drivers/infiniband/sw/rxe/rxe_param.h
index a754fc902e3d19..7b41d79e72b2df 100644
--- a/drivers/infiniband/sw/rxe/rxe_param.h
+++ b/drivers/infiniband/sw/rxe/rxe_param.h
@@ -98,11 +98,11 @@ enum rxe_device_param {
RXE_MAX_SRQ = DEFAULT_MAX_VALUE - RXE_MIN_SRQ_INDEX,
RXE_MIN_MR_INDEX = 0x00000001,
- RXE_MAX_MR_INDEX = DEFAULT_MAX_VALUE,
- RXE_MAX_MR = DEFAULT_MAX_VALUE - RXE_MIN_MR_INDEX,
- RXE_MIN_MW_INDEX = 0x00010001,
- RXE_MAX_MW_INDEX = 0x00020000,
- RXE_MAX_MW = 0x00001000,
+ RXE_MAX_MR_INDEX = DEFAULT_MAX_VALUE >> 1,
+ RXE_MAX_MR = RXE_MAX_MR_INDEX - RXE_MIN_MR_INDEX,
+ RXE_MIN_MW_INDEX = RXE_MAX_MR_INDEX + 1,
+ RXE_MAX_MW_INDEX = DEFAULT_MAX_VALUE,
+ RXE_MAX_MW = RXE_MAX_MW_INDEX - RXE_MIN_MW_INDEX,
RXE_MAX_PKT_PER_ACK = 64,
diff --git a/drivers/infiniband/sw/rxe/rxe_pool.c b/drivers/infiniband/sw/rxe/rxe_pool.c
index f50620f5a0a141..1151c0b5cceaba 100644
--- a/drivers/infiniband/sw/rxe/rxe_pool.c
+++ b/drivers/infiniband/sw/rxe/rxe_pool.c
@@ -23,16 +23,16 @@ static const struct rxe_type_info {
.size = sizeof(struct rxe_ucontext),
.elem_offset = offsetof(struct rxe_ucontext, elem),
.min_index = 1,
- .max_index = UINT_MAX,
- .max_elem = UINT_MAX,
+ .max_index = RXE_MAX_UCONTEXT,
+ .max_elem = RXE_MAX_UCONTEXT,
},
[RXE_TYPE_PD] = {
.name = "pd",
.size = sizeof(struct rxe_pd),
.elem_offset = offsetof(struct rxe_pd, elem),
.min_index = 1,
- .max_index = UINT_MAX,
- .max_elem = UINT_MAX,
+ .max_index = RXE_MAX_PD,
+ .max_elem = RXE_MAX_PD,
},
[RXE_TYPE_AH] = {
.name = "ah",
@@ -40,7 +40,7 @@ static const struct rxe_type_info {
.elem_offset = offsetof(struct rxe_ah, elem),
.min_index = RXE_MIN_AH_INDEX,
.max_index = RXE_MAX_AH_INDEX,
- .max_elem = RXE_MAX_AH_INDEX - RXE_MIN_AH_INDEX + 1,
+ .max_elem = RXE_MAX_AH,
},
[RXE_TYPE_SRQ] = {
.name = "srq",
@@ -49,7 +49,7 @@ static const struct rxe_type_info {
.cleanup = rxe_srq_cleanup,
.min_index = RXE_MIN_SRQ_INDEX,
.max_index = RXE_MAX_SRQ_INDEX,
- .max_elem = RXE_MAX_SRQ_INDEX - RXE_MIN_SRQ_INDEX + 1,
+ .max_elem = RXE_MAX_SRQ,
},
[RXE_TYPE_QP] = {
.name = "qp",
@@ -58,7 +58,7 @@ static const struct rxe_type_info {
.cleanup = rxe_qp_cleanup,
.min_index = RXE_MIN_QP_INDEX,
.max_index = RXE_MAX_QP_INDEX,
- .max_elem = RXE_MAX_QP_INDEX - RXE_MIN_QP_INDEX + 1,
+ .max_elem = RXE_MAX_QP,
},
[RXE_TYPE_CQ] = {
.name = "cq",
@@ -66,8 +66,8 @@ static const struct rxe_type_info {
.elem_offset = offsetof(struct rxe_cq, elem),
.cleanup = rxe_cq_cleanup,
.min_index = 1,
- .max_index = UINT_MAX,
- .max_elem = UINT_MAX,
+ .max_index = RXE_MAX_CQ,
+ .max_elem = RXE_MAX_CQ,
},
[RXE_TYPE_MR] = {
.name = "mr",
@@ -76,7 +76,7 @@ static const struct rxe_type_info {
.cleanup = rxe_mr_cleanup,
.min_index = RXE_MIN_MR_INDEX,
.max_index = RXE_MAX_MR_INDEX,
- .max_elem = RXE_MAX_MR_INDEX - RXE_MIN_MR_INDEX + 1,
+ .max_elem = RXE_MAX_MR,
},
[RXE_TYPE_MW] = {
.name = "mw",
@@ -85,7 +85,7 @@ static const struct rxe_type_info {
.cleanup = rxe_mw_cleanup,
.min_index = RXE_MIN_MW_INDEX,
.max_index = RXE_MAX_MW_INDEX,
- .max_elem = RXE_MAX_MW_INDEX - RXE_MIN_MW_INDEX + 1,
+ .max_elem = RXE_MAX_MW,
},
};
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index ac25fc80fb3372..f10d4bcf87d270 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -2200,6 +2200,14 @@ int ipoib_intf_init(struct ib_device *hca, u32 port, const char *name,
rn->attach_mcast = ipoib_mcast_attach;
rn->detach_mcast = ipoib_mcast_detach;
rn->hca = hca;
+
+ rc = netif_set_real_num_tx_queues(dev, 1);
+ if (rc)
+ goto out;
+
+ rc = netif_set_real_num_rx_queues(dev, 1);
+ if (rc)
+ goto out;
}
priv->rn_ops = dev->netdev_ops;
diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c
index c76ba29da1e206..5adba0f754b6b0 100644
--- a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c
+++ b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c
@@ -312,9 +312,8 @@ void rtrs_srv_destroy_path_files(struct rtrs_srv_path *srv_path)
if (srv_path->kobj.state_in_sysfs) {
sysfs_remove_group(&srv_path->kobj, &rtrs_srv_path_attr_group);
- kobject_del(&srv_path->kobj);
kobject_put(&srv_path->kobj);
+ rtrs_srv_destroy_once_sysfs_root_folders(srv_path);
}
- rtrs_srv_destroy_once_sysfs_root_folders(srv_path);
}
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h
index 00b0068fda208b..5d94db453df322 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.h
+++ b/drivers/infiniband/ulp/srp/ib_srp.h
@@ -62,9 +62,6 @@ enum {
SRP_DEFAULT_CMD_SQ_SIZE = SRP_DEFAULT_QUEUE_SIZE - SRP_RSP_SQ_SIZE -
SRP_TSK_MGMT_SQ_SIZE,
- SRP_TAG_NO_REQ = ~0U,
- SRP_TAG_TSK_MGMT = 1U << 31,
-
SRP_MAX_PAGES_PER_MR = 512,
SRP_MAX_ADD_CDB_LEN = 16,
@@ -79,6 +76,11 @@ enum {
sizeof(struct srp_imm_buf),
};
+enum {
+ SRP_TAG_NO_REQ = ~0U,
+ SRP_TAG_TSK_MGMT = BIT(31),
+};
+
enum srp_target_state {
SRP_TARGET_SCANNING,
SRP_TARGET_LIVE,
diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c
index 8d8ebdc2039b8e..67f1c7364c95d3 100644
--- a/drivers/input/misc/xen-kbdfront.c
+++ b/drivers/input/misc/xen-kbdfront.c
@@ -51,7 +51,7 @@ module_param_array(ptr_size, int, NULL, 0444);
MODULE_PARM_DESC(ptr_size,
"Pointing device width, height in pixels (default 800,600)");
-static int xenkbd_remove(struct xenbus_device *);
+static void xenkbd_remove(struct xenbus_device *);
static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *);
static void xenkbd_disconnect_backend(struct xenkbd_info *);
@@ -404,7 +404,7 @@ static int xenkbd_resume(struct xenbus_device *dev)
return xenkbd_connect_backend(dev, info);
}
-static int xenkbd_remove(struct xenbus_device *dev)
+static void xenkbd_remove(struct xenbus_device *dev)
{
struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
@@ -417,7 +417,6 @@ static int xenkbd_remove(struct xenbus_device *dev)
input_unregister_device(info->mtouch);
free_page((unsigned long)info->page);
kfree(info);
- return 0;
}
static int xenkbd_connect_backend(struct xenbus_device *dev,
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index b0f776448a1cdd..fa021af8506e4f 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -192,7 +192,6 @@ static const char * const smbus_pnp_ids[] = {
"SYN3221", /* HP 15-ay000 */
"SYN323d", /* HP Spectre X360 13-w013dx */
"SYN3257", /* HP Envy 13-ad105ng */
- "SYN3286", /* HP Laptop 15-da3001TU */
NULL
};
diff --git a/drivers/input/serio/i8042-acpipnpio.h b/drivers/input/serio/i8042-acpipnpio.h
index 46f8a694291edd..efc61736099b9a 100644
--- a/drivers/input/serio/i8042-acpipnpio.h
+++ b/drivers/input/serio/i8042-acpipnpio.h
@@ -1240,6 +1240,13 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = {
},
{
.matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "PCX0DX"),
+ },
+ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
+ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
+ {
+ .matches = {
DMI_MATCH(DMI_BOARD_NAME, "X170SM"),
},
.driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c
index ba6781f54ab73a..df3196f7253687 100644
--- a/drivers/interconnect/qcom/icc-rpm.c
+++ b/drivers/interconnect/qcom/icc-rpm.c
@@ -488,7 +488,7 @@ int qnoc_probe(struct platform_device *pdev)
}
regmap_done:
- ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks);
+ ret = devm_clk_bulk_get_optional(dev, qp->num_clks, qp->bus_clks);
if (ret)
return ret;
diff --git a/drivers/interconnect/qcom/msm8996.c b/drivers/interconnect/qcom/msm8996.c
index c2903ae3b3bc37..25a1a32bc611fa 100644
--- a/drivers/interconnect/qcom/msm8996.c
+++ b/drivers/interconnect/qcom/msm8996.c
@@ -33,6 +33,13 @@ static const char * const bus_a0noc_clocks[] = {
"aggre0_noc_mpu_cfg"
};
+static const char * const bus_a2noc_clocks[] = {
+ "bus",
+ "bus_a",
+ "aggre2_ufs_axi",
+ "ufs_axi"
+};
+
static const u16 mas_a0noc_common_links[] = {
MSM8996_SLAVE_A0NOC_SNOC
};
@@ -1806,7 +1813,7 @@ static const struct regmap_config msm8996_a0noc_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
- .max_register = 0x9000,
+ .max_register = 0x6000,
.fast_io = true
};
@@ -1830,7 +1837,7 @@ static const struct regmap_config msm8996_a1noc_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
- .max_register = 0x7000,
+ .max_register = 0x5000,
.fast_io = true
};
@@ -1851,7 +1858,7 @@ static const struct regmap_config msm8996_a2noc_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
- .max_register = 0xa000,
+ .max_register = 0x7000,
.fast_io = true
};
@@ -1859,6 +1866,8 @@ static const struct qcom_icc_desc msm8996_a2noc = {
.type = QCOM_ICC_NOC,
.nodes = a2noc_nodes,
.num_nodes = ARRAY_SIZE(a2noc_nodes),
+ .clocks = bus_a2noc_clocks,
+ .num_clocks = ARRAY_SIZE(bus_a2noc_clocks),
.regmap_cfg = &msm8996_a2noc_regmap_config
};
@@ -1877,7 +1886,7 @@ static const struct regmap_config msm8996_bimc_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
- .max_register = 0x62000,
+ .max_register = 0x5a000,
.fast_io = true
};
@@ -1988,7 +1997,7 @@ static const struct regmap_config msm8996_mnoc_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
- .max_register = 0x20000,
+ .max_register = 0x1c000,
.fast_io = true
};
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index ab160198edd6b1..f2425b0f0cd625 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -3858,7 +3858,9 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
static void arm_smmu_device_shutdown(struct platform_device *pdev)
{
- arm_smmu_device_remove(pdev);
+ struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
+
+ arm_smmu_device_disable(smmu);
}
static const struct of_device_id arm_smmu_of_match[] = {
diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c
index 719fbca1fe52a0..2ff7a72cf3772e 100644
--- a/drivers/iommu/arm/arm-smmu/arm-smmu.c
+++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c
@@ -1316,8 +1316,14 @@ static bool arm_smmu_capable(struct device *dev, enum iommu_cap cap)
switch (cap) {
case IOMMU_CAP_CACHE_COHERENCY:
- /* Assume that a coherent TCU implies coherent TBUs */
- return cfg->smmu->features & ARM_SMMU_FEAT_COHERENT_WALK;
+ /*
+ * It's overwhelmingly the case in practice that when the pagetable
+ * walk interface is connected to a coherent interconnect, all the
+ * translation interfaces are too. Furthermore if the device is
+ * natively coherent, then its translation interface must also be.
+ */
+ return cfg->smmu->features & ARM_SMMU_FEAT_COHERENT_WALK ||
+ device_get_dma_attr(dev) == DEV_DMA_COHERENT;
case IOMMU_CAP_NOEXEC:
return true;
default:
@@ -2185,19 +2191,16 @@ static int arm_smmu_device_probe(struct platform_device *pdev)
return 0;
}
-static int arm_smmu_device_remove(struct platform_device *pdev)
+static void arm_smmu_device_shutdown(struct platform_device *pdev)
{
struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
if (!smmu)
- return -ENODEV;
+ return;
if (!bitmap_empty(smmu->context_map, ARM_SMMU_MAX_CBS))
dev_notice(&pdev->dev, "disabling translation\n");
- iommu_device_unregister(&smmu->iommu);
- iommu_device_sysfs_remove(&smmu->iommu);
-
arm_smmu_rpm_get(smmu);
/* Turn the thing off */
arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sCR0, ARM_SMMU_sCR0_CLIENTPD);
@@ -2209,12 +2212,21 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
clk_bulk_disable(smmu->num_clks, smmu->clks);
clk_bulk_unprepare(smmu->num_clks, smmu->clks);
- return 0;
}
-static void arm_smmu_device_shutdown(struct platform_device *pdev)
+static int arm_smmu_device_remove(struct platform_device *pdev)
{
- arm_smmu_device_remove(pdev);
+ struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
+
+ if (!smmu)
+ return -ENODEV;
+
+ iommu_device_unregister(&smmu->iommu);
+ iommu_device_sysfs_remove(&smmu->iommu);
+
+ arm_smmu_device_shutdown(pdev);
+
+ return 0;
}
static int __maybe_unused arm_smmu_runtime_resume(struct device *dev)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index de91dd88705bd3..5f6a85aea501ec 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -3185,14 +3185,16 @@ EXPORT_SYMBOL_GPL(iommu_group_claim_dma_owner);
*/
int iommu_device_claim_dma_owner(struct device *dev, void *owner)
{
- struct iommu_group *group = iommu_group_get(dev);
+ struct iommu_group *group;
int ret = 0;
- if (!group)
- return -ENODEV;
if (WARN_ON(!owner))
return -EINVAL;
+ group = iommu_group_get(dev);
+ if (!group)
+ return -ENODEV;
+
mutex_lock(&group->mutex);
if (group->owner_cnt) {
if (group->owner != owner) {
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index a44ad92fc5eb70..fe452ce466429a 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -197,7 +197,7 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad,
curr = __get_cached_rbnode(iovad, limit_pfn);
curr_iova = to_iova(curr);
- retry_pfn = curr_iova->pfn_hi + 1;
+ retry_pfn = curr_iova->pfn_hi;
retry:
do {
@@ -211,7 +211,7 @@ retry:
if (high_pfn < size || new_pfn < low_pfn) {
if (low_pfn == iovad->start_pfn && retry_pfn < limit_pfn) {
high_pfn = limit_pfn;
- low_pfn = retry_pfn;
+ low_pfn = retry_pfn + 1;
curr = iova_find_limit(iovad, limit_pfn);
curr_iova = to_iova(curr);
goto retry;
diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
index 69682ee068d2bd..ca581ff1c76964 100644
--- a/drivers/iommu/mtk_iommu_v1.c
+++ b/drivers/iommu/mtk_iommu_v1.c
@@ -683,7 +683,7 @@ static int mtk_iommu_v1_probe(struct platform_device *pdev)
ret = iommu_device_sysfs_add(&data->iommu, &pdev->dev, NULL,
dev_name(&pdev->dev));
if (ret)
- return ret;
+ goto out_clk_unprepare;
ret = iommu_device_register(&data->iommu, &mtk_iommu_v1_ops, dev);
if (ret)
@@ -698,6 +698,8 @@ out_dev_unreg:
iommu_device_unregister(&data->iommu);
out_sysfs_remove:
iommu_device_sysfs_remove(&data->iommu);
+out_clk_unprepare:
+ clk_disable_unprepare(data->bclk);
return ret;
}
diff --git a/drivers/md/bcache/bcache_ondisk.h b/drivers/md/bcache/bcache_ondisk.h
index 97413586195b4c..f96034e0ba4f20 100644
--- a/drivers/md/bcache/bcache_ondisk.h
+++ b/drivers/md/bcache/bcache_ondisk.h
@@ -106,7 +106,8 @@ static inline unsigned long bkey_bytes(const struct bkey *k)
return bkey_u64s(k) * sizeof(__u64);
}
-#define bkey_copy(_dest, _src) memcpy(_dest, _src, bkey_bytes(_src))
+#define bkey_copy(_dest, _src) unsafe_memcpy(_dest, _src, bkey_bytes(_src), \
+ /* bkey is always padded */)
static inline void bkey_copy_key(struct bkey *dest, const struct bkey *src)
{
diff --git a/drivers/md/bcache/journal.c b/drivers/md/bcache/journal.c
index e5da469a42357c..c182c21de2e819 100644
--- a/drivers/md/bcache/journal.c
+++ b/drivers/md/bcache/journal.c
@@ -149,7 +149,8 @@ add:
bytes, GFP_KERNEL);
if (!i)
return -ENOMEM;
- memcpy(&i->j, j, bytes);
+ unsafe_memcpy(&i->j, j, bytes,
+ /* "bytes" was calculated by set_bytes() above */);
/* Add to the location after 'where' points to */
list_add(&i->list, where);
ret = 1;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index e1ea3a7bd9d9f8..b424a6ee27baf2 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1742,6 +1742,8 @@ static void dm_split_and_process_bio(struct mapped_device *md,
* otherwise associated queue_limits won't be imposed.
*/
bio = bio_split_to_limits(bio);
+ if (!bio)
+ return;
}
init_clone_info(&ci, md, map, bio, is_abnormal);
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 775f1dde190a2e..02b0240e7c715a 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -455,6 +455,8 @@ static void md_submit_bio(struct bio *bio)
}
bio = bio_split_to_limits(bio);
+ if (!bio)
+ return;
if (mddev->ro == MD_RDONLY && unlikely(rw == WRITE)) {
if (bio_sectors(bio) != 0)
@@ -3642,7 +3644,7 @@ EXPORT_SYMBOL_GPL(md_rdev_init);
*/
static struct md_rdev *md_import_device(dev_t newdev, int super_format, int super_minor)
{
- static struct md_rdev *claim_rdev; /* just for claiming the bdev */
+ static struct md_rdev claim_rdev; /* just for claiming the bdev */
struct md_rdev *rdev;
sector_t size;
int err;
@@ -3660,7 +3662,7 @@ static struct md_rdev *md_import_device(dev_t newdev, int super_format, int supe
rdev->bdev = blkdev_get_by_dev(newdev,
FMODE_READ | FMODE_WRITE | FMODE_EXCL,
- super_format == -2 ? claim_rdev : rdev);
+ super_format == -2 ? &claim_rdev : rdev);
if (IS_ERR(rdev->bdev)) {
pr_warn("md: could not open device unknown-block(%u,%u).\n",
MAJOR(newdev), MINOR(newdev));
diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index fc3758a5bc1c2c..53e495223ea017 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -2149,8 +2149,6 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type)
if (ret)
return ret;
- q->streaming = 1;
-
/*
* Tell driver to start streaming provided sufficient buffers
* are available.
@@ -2161,12 +2159,13 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type)
goto unprepare;
}
+ q->streaming = 1;
+
dprintk(q, 3, "successful\n");
return 0;
unprepare:
call_void_qop(q, unprepare_streaming, q);
- q->streaming = 0;
return ret;
}
EXPORT_SYMBOL_GPL(vb2_core_streamon);
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-api.c b/drivers/media/v4l2-core/v4l2-ctrls-api.c
index 3d3b6dc24ca637..002ea6588edf16 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-api.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-api.c
@@ -150,8 +150,8 @@ static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
* then return an error.
*/
if (strlen(ctrl->p_new.p_char) == ctrl->maximum && last)
- ctrl->is_new = 1;
return -ERANGE;
+ ctrl->is_new = 1;
}
return ret;
default:
diff --git a/drivers/memory/atmel-sdramc.c b/drivers/memory/atmel-sdramc.c
index 9c49d00c2a966b..ea6e9e1eaf0468 100644
--- a/drivers/memory/atmel-sdramc.c
+++ b/drivers/memory/atmel-sdramc.c
@@ -47,19 +47,17 @@ static int atmel_ramc_probe(struct platform_device *pdev)
caps = of_device_get_match_data(&pdev->dev);
if (caps->has_ddrck) {
- clk = devm_clk_get(&pdev->dev, "ddrck");
+ clk = devm_clk_get_enabled(&pdev->dev, "ddrck");
if (IS_ERR(clk))
return PTR_ERR(clk);
- clk_prepare_enable(clk);
}
if (caps->has_mpddr_clk) {
- clk = devm_clk_get(&pdev->dev, "mpddr");
+ clk = devm_clk_get_enabled(&pdev->dev, "mpddr");
if (IS_ERR(clk)) {
pr_err("AT91 RAMC: couldn't get mpddr clock\n");
return PTR_ERR(clk);
}
- clk_prepare_enable(clk);
}
return 0;
diff --git a/drivers/memory/mvebu-devbus.c b/drivers/memory/mvebu-devbus.c
index 8450638e867007..efc6c08db2b70a 100644
--- a/drivers/memory/mvebu-devbus.c
+++ b/drivers/memory/mvebu-devbus.c
@@ -280,10 +280,9 @@ static int mvebu_devbus_probe(struct platform_device *pdev)
if (IS_ERR(devbus->base))
return PTR_ERR(devbus->base);
- clk = devm_clk_get(&pdev->dev, NULL);
+ clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(clk))
return PTR_ERR(clk);
- clk_prepare_enable(clk);
/*
* Obtain clock period in picoseconds,
diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c
index 57d9f91fe89bf9..d78f73db37c88c 100644
--- a/drivers/memory/omap-gpmc.c
+++ b/drivers/memory/omap-gpmc.c
@@ -1918,7 +1918,8 @@ int gpmc_cs_program_settings(int cs, struct gpmc_settings *p)
}
}
- if (p->wait_pin > gpmc_nr_waitpins) {
+ if (p->wait_pin != GPMC_WAITPIN_INVALID &&
+ p->wait_pin > gpmc_nr_waitpins) {
pr_err("%s: invalid wait-pin (%d)\n", __func__, p->wait_pin);
return -EINVAL;
}
diff --git a/drivers/memory/tegra/tegra186.c b/drivers/memory/tegra/tegra186.c
index 62477e592bf5f3..7bb73f06fad3ed 100644
--- a/drivers/memory/tegra/tegra186.c
+++ b/drivers/memory/tegra/tegra186.c
@@ -22,32 +22,6 @@
#define MC_SID_STREAMID_SECURITY_WRITE_ACCESS_DISABLED BIT(16)
#define MC_SID_STREAMID_SECURITY_OVERRIDE BIT(8)
-static void tegra186_mc_program_sid(struct tegra_mc *mc)
-{
- unsigned int i;
-
- for (i = 0; i < mc->soc->num_clients; i++) {
- const struct tegra_mc_client *client = &mc->soc->clients[i];
- u32 override, security;
-
- override = readl(mc->regs + client->regs.sid.override);
- security = readl(mc->regs + client->regs.sid.security);
-
- dev_dbg(mc->dev, "client %s: override: %x security: %x\n",
- client->name, override, security);
-
- dev_dbg(mc->dev, "setting SID %u for %s\n", client->sid,
- client->name);
- writel(client->sid, mc->regs + client->regs.sid.override);
-
- override = readl(mc->regs + client->regs.sid.override);
- security = readl(mc->regs + client->regs.sid.security);
-
- dev_dbg(mc->dev, "client %s: override: %x security: %x\n",
- client->name, override, security);
- }
-}
-
static int tegra186_mc_probe(struct tegra_mc *mc)
{
struct platform_device *pdev = to_platform_device(mc->dev);
@@ -85,8 +59,6 @@ populate:
if (err < 0)
return err;
- tegra186_mc_program_sid(mc);
-
return 0;
}
@@ -95,13 +67,6 @@ static void tegra186_mc_remove(struct tegra_mc *mc)
of_platform_depopulate(mc->dev);
}
-static int tegra186_mc_resume(struct tegra_mc *mc)
-{
- tegra186_mc_program_sid(mc);
-
- return 0;
-}
-
#if IS_ENABLED(CONFIG_IOMMU_API)
static void tegra186_mc_client_sid_override(struct tegra_mc *mc,
const struct tegra_mc_client *client,
@@ -173,7 +138,6 @@ static int tegra186_mc_probe_device(struct tegra_mc *mc, struct device *dev)
const struct tegra_mc_ops tegra186_mc_ops = {
.probe = tegra186_mc_probe,
.remove = tegra186_mc_remove,
- .resume = tegra186_mc_resume,
.probe_device = tegra186_mc_probe_device,
.handle_irq = tegra30_mc_handle_irq,
};
diff --git a/drivers/mfd/wm97xx-core.c b/drivers/mfd/wm97xx-core.c
index 9a2331eb1bfa60..663acbb1854c93 100644
--- a/drivers/mfd/wm97xx-core.c
+++ b/drivers/mfd/wm97xx-core.c
@@ -319,13 +319,11 @@ err_free_compat:
return ret;
}
-static int wm97xx_ac97_remove(struct ac97_codec_device *adev)
+static void wm97xx_ac97_remove(struct ac97_codec_device *adev)
{
struct wm97xx_priv *wm97xx = ac97_get_drvdata(adev);
snd_ac97_compat_release(wm97xx->ac97);
-
- return 0;
}
static const struct ac97_id wm97xx_ac97_ids[] = {
diff --git a/drivers/misc/fastrpc.c b/drivers/misc/fastrpc.c
index c9902a1dcf5d31..5310606113fe5c 100644
--- a/drivers/misc/fastrpc.c
+++ b/drivers/misc/fastrpc.c
@@ -321,7 +321,7 @@ static void fastrpc_free_map(struct kref *ref)
perm.vmid = QCOM_SCM_VMID_HLOS;
perm.perm = QCOM_SCM_PERM_RWX;
err = qcom_scm_assign_mem(map->phys, map->size,
- &(map->fl->cctx->vmperms[0].vmid), &perm, 1);
+ &map->fl->cctx->perms, &perm, 1);
if (err) {
dev_err(map->fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d",
map->phys, map->size, err);
@@ -334,6 +334,13 @@ static void fastrpc_free_map(struct kref *ref)
dma_buf_put(map->buf);
}
+ if (map->fl) {
+ spin_lock(&map->fl->lock);
+ list_del(&map->node);
+ spin_unlock(&map->fl->lock);
+ map->fl = NULL;
+ }
+
kfree(map);
}
@@ -343,38 +350,41 @@ static void fastrpc_map_put(struct fastrpc_map *map)
kref_put(&map->refcount, fastrpc_free_map);
}
-static void fastrpc_map_get(struct fastrpc_map *map)
+static int fastrpc_map_get(struct fastrpc_map *map)
{
- if (map)
- kref_get(&map->refcount);
+ if (!map)
+ return -ENOENT;
+
+ return kref_get_unless_zero(&map->refcount) ? 0 : -ENOENT;
}
static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd,
- struct fastrpc_map **ppmap)
+ struct fastrpc_map **ppmap, bool take_ref)
{
+ struct fastrpc_session_ctx *sess = fl->sctx;
struct fastrpc_map *map = NULL;
+ int ret = -ENOENT;
- mutex_lock(&fl->mutex);
+ spin_lock(&fl->lock);
list_for_each_entry(map, &fl->maps, node) {
- if (map->fd == fd) {
- *ppmap = map;
- mutex_unlock(&fl->mutex);
- return 0;
- }
- }
- mutex_unlock(&fl->mutex);
-
- return -ENOENT;
-}
+ if (map->fd != fd)
+ continue;
-static int fastrpc_map_find(struct fastrpc_user *fl, int fd,
- struct fastrpc_map **ppmap)
-{
- int ret = fastrpc_map_lookup(fl, fd, ppmap);
+ if (take_ref) {
+ ret = fastrpc_map_get(map);
+ if (ret) {
+ dev_dbg(sess->dev, "%s: Failed to get map fd=%d ret=%d\n",
+ __func__, fd, ret);
+ break;
+ }
+ }
- if (!ret)
- fastrpc_map_get(*ppmap);
+ *ppmap = map;
+ ret = 0;
+ break;
+ }
+ spin_unlock(&fl->lock);
return ret;
}
@@ -746,7 +756,7 @@ static int fastrpc_map_create(struct fastrpc_user *fl, int fd,
struct fastrpc_map *map = NULL;
int err = 0;
- if (!fastrpc_map_find(fl, fd, ppmap))
+ if (!fastrpc_map_lookup(fl, fd, ppmap, true))
return 0;
map = kzalloc(sizeof(*map), GFP_KERNEL);
@@ -788,10 +798,8 @@ static int fastrpc_map_create(struct fastrpc_user *fl, int fd,
* If subsystem VMIDs are defined in DTSI, then do
* hyp_assign from HLOS to those VM(s)
*/
- unsigned int perms = BIT(QCOM_SCM_VMID_HLOS);
-
map->attr = attr;
- err = qcom_scm_assign_mem(map->phys, (u64)map->size, &perms,
+ err = qcom_scm_assign_mem(map->phys, (u64)map->size, &fl->cctx->perms,
fl->cctx->vmperms, fl->cctx->vmcount);
if (err) {
dev_err(sess->dev, "Failed to assign memory with phys 0x%llx size 0x%llx err %d",
@@ -1070,7 +1078,7 @@ static int fastrpc_put_args(struct fastrpc_invoke_ctx *ctx,
for (i = 0; i < FASTRPC_MAX_FDLIST; i++) {
if (!fdlist[i])
break;
- if (!fastrpc_map_lookup(fl, (int)fdlist[i], &mmap))
+ if (!fastrpc_map_lookup(fl, (int)fdlist[i], &mmap, false))
fastrpc_map_put(mmap);
}
@@ -1258,10 +1266,9 @@ static int fastrpc_init_create_static_process(struct fastrpc_user *fl,
/* Map if we have any heap VMIDs associated with this ADSP Static Process. */
if (fl->cctx->vmcount) {
- unsigned int perms = BIT(QCOM_SCM_VMID_HLOS);
-
err = qcom_scm_assign_mem(fl->cctx->remote_heap->phys,
- (u64)fl->cctx->remote_heap->size, &perms,
+ (u64)fl->cctx->remote_heap->size,
+ &fl->cctx->perms,
fl->cctx->vmperms, fl->cctx->vmcount);
if (err) {
dev_err(fl->sctx->dev, "Failed to assign memory with phys 0x%llx size 0x%llx err %d",
@@ -1309,7 +1316,7 @@ err_invoke:
perm.perm = QCOM_SCM_PERM_RWX;
err = qcom_scm_assign_mem(fl->cctx->remote_heap->phys,
(u64)fl->cctx->remote_heap->size,
- &(fl->cctx->vmperms[0].vmid), &perm, 1);
+ &fl->cctx->perms, &perm, 1);
if (err)
dev_err(fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d",
fl->cctx->remote_heap->phys, fl->cctx->remote_heap->size, err);
@@ -1433,12 +1440,7 @@ err_invoke:
fl->init_mem = NULL;
fastrpc_buf_free(imem);
err_alloc:
- if (map) {
- spin_lock(&fl->lock);
- list_del(&map->node);
- spin_unlock(&fl->lock);
- fastrpc_map_put(map);
- }
+ fastrpc_map_put(map);
err:
kfree(args);
@@ -1514,10 +1516,8 @@ static int fastrpc_device_release(struct inode *inode, struct file *file)
fastrpc_context_put(ctx);
}
- list_for_each_entry_safe(map, m, &fl->maps, node) {
- list_del(&map->node);
+ list_for_each_entry_safe(map, m, &fl->maps, node)
fastrpc_map_put(map);
- }
list_for_each_entry_safe(buf, b, &fl->mmaps, node) {
list_del(&buf->node);
@@ -1894,12 +1894,11 @@ static int fastrpc_req_mmap(struct fastrpc_user *fl, char __user *argp)
/* Add memory to static PD pool, protection thru hypervisor */
if (req.flags != ADSP_MMAP_REMOTE_HEAP_ADDR && fl->cctx->vmcount) {
struct qcom_scm_vmperm perm;
- int err = 0;
perm.vmid = QCOM_SCM_VMID_HLOS;
perm.perm = QCOM_SCM_PERM_RWX;
err = qcom_scm_assign_mem(buf->phys, buf->size,
- &(fl->cctx->vmperms[0].vmid), &perm, 1);
+ &fl->cctx->perms, &perm, 1);
if (err) {
dev_err(fl->sctx->dev, "Failed to assign memory phys 0x%llx size 0x%llx err %d",
buf->phys, buf->size, err);
diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index 4a08b624910a0a..a81b890c7ee648 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -702,13 +702,15 @@ void *mei_cldev_dma_map(struct mei_cl_device *cldev, u8 buffer_id, size_t size)
if (cl->state == MEI_FILE_UNINITIALIZED) {
ret = mei_cl_link(cl);
if (ret)
- goto out;
+ goto notlinked;
/* update pointers */
cl->cldev = cldev;
}
ret = mei_cl_dma_alloc_and_map(cl, NULL, buffer_id, size);
-out:
+ if (ret)
+ mei_cl_unlink(cl);
+notlinked:
mutex_unlock(&bus->device_lock);
if (ret)
return ERR_PTR(ret);
@@ -758,7 +760,7 @@ int mei_cldev_enable(struct mei_cl_device *cldev)
if (cl->state == MEI_FILE_UNINITIALIZED) {
ret = mei_cl_link(cl);
if (ret)
- goto out;
+ goto notlinked;
/* update pointers */
cl->cldev = cldev;
}
@@ -785,6 +787,9 @@ int mei_cldev_enable(struct mei_cl_device *cldev)
}
out:
+ if (ret)
+ mei_cl_unlink(cl);
+notlinked:
mutex_unlock(&bus->device_lock);
return ret;
@@ -1277,7 +1282,6 @@ static void mei_cl_bus_dev_release(struct device *dev)
mei_cl_flush_queues(cldev->cl, NULL);
mei_me_cl_put(cldev->me_cl);
mei_dev_bus_put(cldev->bus);
- mei_cl_unlink(cldev->cl);
kfree(cldev->cl);
kfree(cldev);
}
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
index 99966cd3e7d892..bdc65d50b945fc 100644
--- a/drivers/misc/mei/hw-me-regs.h
+++ b/drivers/misc/mei/hw-me-regs.h
@@ -111,6 +111,8 @@
#define MEI_DEV_ID_RPL_S 0x7A68 /* Raptor Lake Point S */
+#define MEI_DEV_ID_MTL_M 0x7E70 /* Meteor Lake Point M */
+
/*
* MEI HW Section
*/
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index 704cd0caa172ca..5bf0d50d55a00b 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -118,6 +118,8 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_RPL_S, MEI_ME_PCH15_CFG)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_MTL_M, MEI_ME_PCH15_CFG)},
+
/* required last entry */
{0, }
};
diff --git a/drivers/misc/vmw_vmci/vmci_guest.c b/drivers/misc/vmw_vmci/vmci_guest.c
index aa7b05de97dd52..4f8d962bb5b2a3 100644
--- a/drivers/misc/vmw_vmci/vmci_guest.c
+++ b/drivers/misc/vmw_vmci/vmci_guest.c
@@ -56,8 +56,6 @@ struct vmci_guest_device {
bool exclusive_vectors;
- struct tasklet_struct datagram_tasklet;
- struct tasklet_struct bm_tasklet;
struct wait_queue_head inout_wq;
void *data_buffer;
@@ -304,9 +302,8 @@ static int vmci_check_host_caps(struct pci_dev *pdev)
* This function assumes that it has exclusive access to the data
* in register(s) for the duration of the call.
*/
-static void vmci_dispatch_dgs(unsigned long data)
+static void vmci_dispatch_dgs(struct vmci_guest_device *vmci_dev)
{
- struct vmci_guest_device *vmci_dev = (struct vmci_guest_device *)data;
u8 *dg_in_buffer = vmci_dev->data_buffer;
struct vmci_datagram *dg;
size_t dg_in_buffer_size = VMCI_MAX_DG_SIZE;
@@ -465,10 +462,8 @@ static void vmci_dispatch_dgs(unsigned long data)
* Scans the notification bitmap for raised flags, clears them
* and handles the notifications.
*/
-static void vmci_process_bitmap(unsigned long data)
+static void vmci_process_bitmap(struct vmci_guest_device *dev)
{
- struct vmci_guest_device *dev = (struct vmci_guest_device *)data;
-
if (!dev->notification_bitmap) {
dev_dbg(dev->dev, "No bitmap present in %s\n", __func__);
return;
@@ -486,13 +481,13 @@ static irqreturn_t vmci_interrupt(int irq, void *_dev)
struct vmci_guest_device *dev = _dev;
/*
- * If we are using MSI-X with exclusive vectors then we simply schedule
- * the datagram tasklet, since we know the interrupt was meant for us.
+ * If we are using MSI-X with exclusive vectors then we simply call
+ * vmci_dispatch_dgs(), since we know the interrupt was meant for us.
* Otherwise we must read the ICR to determine what to do.
*/
if (dev->exclusive_vectors) {
- tasklet_schedule(&dev->datagram_tasklet);
+ vmci_dispatch_dgs(dev);
} else {
unsigned int icr;
@@ -502,12 +497,12 @@ static irqreturn_t vmci_interrupt(int irq, void *_dev)
return IRQ_NONE;
if (icr & VMCI_ICR_DATAGRAM) {
- tasklet_schedule(&dev->datagram_tasklet);
+ vmci_dispatch_dgs(dev);
icr &= ~VMCI_ICR_DATAGRAM;
}
if (icr & VMCI_ICR_NOTIFICATION) {
- tasklet_schedule(&dev->bm_tasklet);
+ vmci_process_bitmap(dev);
icr &= ~VMCI_ICR_NOTIFICATION;
}
@@ -536,7 +531,7 @@ static irqreturn_t vmci_interrupt_bm(int irq, void *_dev)
struct vmci_guest_device *dev = _dev;
/* For MSI-X we can just assume it was meant for us. */
- tasklet_schedule(&dev->bm_tasklet);
+ vmci_process_bitmap(dev);
return IRQ_HANDLED;
}
@@ -638,10 +633,6 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
vmci_dev->iobase = iobase;
vmci_dev->mmio_base = mmio_base;
- tasklet_init(&vmci_dev->datagram_tasklet,
- vmci_dispatch_dgs, (unsigned long)vmci_dev);
- tasklet_init(&vmci_dev->bm_tasklet,
- vmci_process_bitmap, (unsigned long)vmci_dev);
init_waitqueue_head(&vmci_dev->inout_wq);
if (mmio_base != NULL) {
@@ -808,8 +799,9 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
* Request IRQ for legacy or MSI interrupts, or for first
* MSI-X vector.
*/
- error = request_irq(pci_irq_vector(pdev, 0), vmci_interrupt,
- IRQF_SHARED, KBUILD_MODNAME, vmci_dev);
+ error = request_threaded_irq(pci_irq_vector(pdev, 0), NULL,
+ vmci_interrupt, IRQF_SHARED,
+ KBUILD_MODNAME, vmci_dev);
if (error) {
dev_err(&pdev->dev, "Irq %u in use: %d\n",
pci_irq_vector(pdev, 0), error);
@@ -823,9 +815,9 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
* between the vectors.
*/
if (vmci_dev->exclusive_vectors) {
- error = request_irq(pci_irq_vector(pdev, 1),
- vmci_interrupt_bm, 0, KBUILD_MODNAME,
- vmci_dev);
+ error = request_threaded_irq(pci_irq_vector(pdev, 1), NULL,
+ vmci_interrupt_bm, 0,
+ KBUILD_MODNAME, vmci_dev);
if (error) {
dev_err(&pdev->dev,
"Failed to allocate irq %u: %d\n",
@@ -833,9 +825,11 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
goto err_free_irq;
}
if (caps_in_use & VMCI_CAPS_DMA_DATAGRAM) {
- error = request_irq(pci_irq_vector(pdev, 2),
- vmci_interrupt_dma_datagram,
- 0, KBUILD_MODNAME, vmci_dev);
+ error = request_threaded_irq(pci_irq_vector(pdev, 2),
+ NULL,
+ vmci_interrupt_dma_datagram,
+ 0, KBUILD_MODNAME,
+ vmci_dev);
if (error) {
dev_err(&pdev->dev,
"Failed to allocate irq %u: %d\n",
@@ -871,8 +865,6 @@ err_free_bm_irq:
err_free_irq:
free_irq(pci_irq_vector(pdev, 0), vmci_dev);
- tasklet_kill(&vmci_dev->datagram_tasklet);
- tasklet_kill(&vmci_dev->bm_tasklet);
err_disable_msi:
pci_free_irq_vectors(pdev);
@@ -943,9 +935,6 @@ static void vmci_guest_remove_device(struct pci_dev *pdev)
free_irq(pci_irq_vector(pdev, 0), vmci_dev);
pci_free_irq_vectors(pdev);
- tasklet_kill(&vmci_dev->datagram_tasklet);
- tasklet_kill(&vmci_dev->bm_tasklet);
-
if (vmci_dev->notification_bitmap) {
/*
* The device reset above cleared the bitmap state of the
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 89ef0c80ac3711..9e73c34b64017a 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -107,6 +107,7 @@
#define ESDHC_TUNING_START_TAP_DEFAULT 0x1
#define ESDHC_TUNING_START_TAP_MASK 0x7f
#define ESDHC_TUNING_CMD_CRC_CHECK_DISABLE (1 << 7)
+#define ESDHC_TUNING_STEP_DEFAULT 0x1
#define ESDHC_TUNING_STEP_MASK 0x00070000
#define ESDHC_TUNING_STEP_SHIFT 16
@@ -1368,7 +1369,7 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
struct cqhci_host *cq_host = host->mmc->cqe_private;
- int tmp;
+ u32 tmp;
if (esdhc_is_usdhc(imx_data)) {
/*
@@ -1423,17 +1424,24 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
- tmp |= ESDHC_STD_TUNING_EN |
- ESDHC_TUNING_START_TAP_DEFAULT;
- if (imx_data->boarddata.tuning_start_tap) {
- tmp &= ~ESDHC_TUNING_START_TAP_MASK;
+ tmp |= ESDHC_STD_TUNING_EN;
+
+ /*
+ * ROM code or bootloader may config the start tap
+ * and step, unmask them first.
+ */
+ tmp &= ~(ESDHC_TUNING_START_TAP_MASK | ESDHC_TUNING_STEP_MASK);
+ if (imx_data->boarddata.tuning_start_tap)
tmp |= imx_data->boarddata.tuning_start_tap;
- }
+ else
+ tmp |= ESDHC_TUNING_START_TAP_DEFAULT;
if (imx_data->boarddata.tuning_step) {
- tmp &= ~ESDHC_TUNING_STEP_MASK;
tmp |= imx_data->boarddata.tuning_step
<< ESDHC_TUNING_STEP_SHIFT;
+ } else {
+ tmp |= ESDHC_TUNING_STEP_DEFAULT
+ << ESDHC_TUNING_STEP_SHIFT;
}
/* Disable the CMD CRC check for tuning, if not, need to
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index b16e12e62e7222..3db9f32d6a7b9f 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -1492,9 +1492,11 @@ static int sunxi_mmc_remove(struct platform_device *pdev)
struct sunxi_mmc_host *host = mmc_priv(mmc);
mmc_remove_host(mmc);
- pm_runtime_force_suspend(&pdev->dev);
- disable_irq(host->irq);
- sunxi_mmc_disable(host);
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev)) {
+ disable_irq(host->irq);
+ sunxi_mmc_disable(host);
+ }
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
mmc_free_host(mmc);
diff --git a/drivers/mtd/parsers/scpart.c b/drivers/mtd/parsers/scpart.c
index 02601bb33de4e4..6e5e11c37078fd 100644
--- a/drivers/mtd/parsers/scpart.c
+++ b/drivers/mtd/parsers/scpart.c
@@ -50,7 +50,7 @@ static int scpart_scan_partmap(struct mtd_info *master, loff_t partmap_offs,
int cnt = 0;
int res = 0;
int res2;
- loff_t offs;
+ uint32_t offs;
size_t retlen;
struct sc_part_desc *pdesc = NULL;
struct sc_part_desc *tmpdesc;
diff --git a/drivers/mtd/parsers/tplink_safeloader.c b/drivers/mtd/parsers/tplink_safeloader.c
index f601e7bd862791..1c689dafca2ae9 100644
--- a/drivers/mtd/parsers/tplink_safeloader.c
+++ b/drivers/mtd/parsers/tplink_safeloader.c
@@ -91,7 +91,7 @@ static int mtd_parser_tplink_safeloader_parse(struct mtd_info *mtd,
buf = mtd_parser_tplink_safeloader_read_table(mtd);
if (!buf) {
err = -ENOENT;
- goto err_out;
+ goto err_free_parts;
}
for (idx = 0, offset = TPLINK_SAFELOADER_DATA_OFFSET;
@@ -118,6 +118,8 @@ static int mtd_parser_tplink_safeloader_parse(struct mtd_info *mtd,
err_free:
for (idx -= 1; idx >= 0; idx--)
kfree(parts[idx].name);
+err_free_parts:
+ kfree(parts);
err_out:
return err;
};
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index d8703d7dfd0af3..d67c926bca8ba1 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -10,6 +10,7 @@
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/module.h>
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/math64.h>
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 455b555275f129..c99ffe6c683a38 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -1549,6 +1549,7 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr)
slave_err(bond->dev, port->slave->dev,
"Port %d did not find a suitable aggregator\n",
port->actor_port_number);
+ return;
}
}
/* if all aggregator's ports are READY_N == TRUE, set ready=TRUE
diff --git a/drivers/net/bonding/bond_debugfs.c b/drivers/net/bonding/bond_debugfs.c
index 4f9b4a18c74cdc..5940945266489a 100644
--- a/drivers/net/bonding/bond_debugfs.c
+++ b/drivers/net/bonding/bond_debugfs.c
@@ -76,7 +76,7 @@ void bond_debug_reregister(struct bonding *bond)
d = debugfs_rename(bonding_debug_root, bond->debug_dir,
bonding_debug_root, bond->dev->name);
- if (d) {
+ if (!IS_ERR(d)) {
bond->debug_dir = d;
} else {
netdev_warn(bond->dev, "failed to reregister, so just unregister old one\n");
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index b4c65783960a5a..0363ce59766142 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -2654,10 +2654,12 @@ static void bond_miimon_link_change(struct bonding *bond,
static void bond_miimon_commit(struct bonding *bond)
{
- struct slave *slave, *primary;
+ struct slave *slave, *primary, *active;
bool do_failover = false;
struct list_head *iter;
+ ASSERT_RTNL();
+
bond_for_each_slave(bond, slave, iter) {
switch (slave->link_new_state) {
case BOND_LINK_NOCHANGE:
@@ -2700,8 +2702,8 @@ static void bond_miimon_commit(struct bonding *bond)
bond_miimon_link_change(bond, slave, BOND_LINK_UP);
- if (!rcu_access_pointer(bond->curr_active_slave) || slave == primary ||
- slave->prio > rcu_dereference(bond->curr_active_slave)->prio)
+ active = rtnl_dereference(bond->curr_active_slave);
+ if (!active || slave == primary || slave->prio > active->prio)
do_failover = true;
continue;
diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c
index 3585f02575dfbd..57eeb066a9456b 100644
--- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c
+++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c
@@ -48,6 +48,7 @@ mcp251xfd_ring_set_ringparam(struct net_device *ndev,
priv->rx_obj_num = layout.cur_rx;
priv->rx_obj_num_coalesce_irq = layout.rx_coalesce;
priv->tx->obj_num = layout.cur_tx;
+ priv->tx_obj_num_coalesce_irq = layout.tx_coalesce;
return 0;
}
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index c26755f662c1ff..f6f3b43dfb06ea 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -35,12 +35,13 @@ config NET_DSA_LANTIQ_GSWIP
the xrx200 / VR9 SoC.
config NET_DSA_MT7530
- tristate "MediaTek MT753x and MT7621 Ethernet switch support"
+ tristate "MediaTek MT7530 and MT7531 Ethernet switch support"
select NET_DSA_TAG_MTK
select MEDIATEK_GE_PHY
help
- This enables support for the MediaTek MT7530, MT7531, and MT7621
- Ethernet switch chips.
+ This enables support for the MediaTek MT7530 and MT7531 Ethernet
+ switch chips. Multi-chip module MT7530 in MT7621AT, MT7621DAT,
+ MT7621ST and MT7623AI SoCs is supported.
config NET_DSA_MV88E6060
tristate "Marvell 88E6060 ethernet switch chip support"
diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c
index 47b54ecf2c6f6b..6178a96e389f26 100644
--- a/drivers/net/dsa/microchip/ksz9477.c
+++ b/drivers/net/dsa/microchip/ksz9477.c
@@ -540,10 +540,10 @@ int ksz9477_fdb_del(struct ksz_device *dev, int port,
ksz_read32(dev, REG_SW_ALU_VAL_D, &alu_table[3]);
/* clear forwarding port */
- alu_table[2] &= ~BIT(port);
+ alu_table[1] &= ~BIT(port);
/* if there is no port to forward, clear table */
- if ((alu_table[2] & ALU_V_PORT_MAP) == 0) {
+ if ((alu_table[1] & ALU_V_PORT_MAP) == 0) {
alu_table[0] = 0;
alu_table[1] = 0;
alu_table[2] = 0;
diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c
index c1a633ca1e6d8b..e315f669ec0611 100644
--- a/drivers/net/dsa/microchip/ksz9477_i2c.c
+++ b/drivers/net/dsa/microchip/ksz9477_i2c.c
@@ -104,7 +104,7 @@ static const struct of_device_id ksz9477_dt_ids[] = {
},
{
.compatible = "microchip,ksz8563",
- .data = &ksz_switch_chips[KSZ9893]
+ .data = &ksz_switch_chips[KSZ8563]
},
{
.compatible = "microchip,ksz9567",
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
index 908fa89444c906..338f238f2043c4 100644
--- a/drivers/net/dsa/mt7530.c
+++ b/drivers/net/dsa/mt7530.c
@@ -1309,14 +1309,26 @@ mt7530_port_set_vlan_aware(struct dsa_switch *ds, int port)
if (!priv->ports[port].pvid)
mt7530_rmw(priv, MT7530_PVC_P(port), ACC_FRM_MASK,
MT7530_VLAN_ACC_TAGGED);
- }
- /* Set the port as a user port which is to be able to recognize VID
- * from incoming packets before fetching entry within the VLAN table.
- */
- mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK | PVC_EG_TAG_MASK,
- VLAN_ATTR(MT7530_VLAN_USER) |
- PVC_EG_TAG(MT7530_VLAN_EG_DISABLED));
+ /* Set the port as a user port which is to be able to recognize
+ * VID from incoming packets before fetching entry within the
+ * VLAN table.
+ */
+ mt7530_rmw(priv, MT7530_PVC_P(port),
+ VLAN_ATTR_MASK | PVC_EG_TAG_MASK,
+ VLAN_ATTR(MT7530_VLAN_USER) |
+ PVC_EG_TAG(MT7530_VLAN_EG_DISABLED));
+ } else {
+ /* Also set CPU ports to the "user" VLAN port attribute, to
+ * allow VLAN classification, but keep the EG_TAG attribute as
+ * "consistent" (i.o.w. don't change its value) for packets
+ * received by the switch from the CPU, so that tagged packets
+ * are forwarded to user ports as tagged, and untagged as
+ * untagged.
+ */
+ mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK,
+ VLAN_ATTR(MT7530_VLAN_USER));
+ }
}
static void
diff --git a/drivers/net/dsa/mv88e6xxx/Kconfig b/drivers/net/dsa/mv88e6xxx/Kconfig
index 7a2445a34eb778..e3181d5471dfe3 100644
--- a/drivers/net/dsa/mv88e6xxx/Kconfig
+++ b/drivers/net/dsa/mv88e6xxx/Kconfig
@@ -2,7 +2,6 @@
config NET_DSA_MV88E6XXX
tristate "Marvell 88E6xxx Ethernet switch fabric support"
depends on NET_DSA
- depends on PTP_1588_CLOCK_OPTIONAL
select IRQ_DOMAIN
select NET_DSA_TAG_EDSA
select NET_DSA_TAG_DSA
@@ -13,7 +12,8 @@ config NET_DSA_MV88E6XXX
config NET_DSA_MV88E6XXX_PTP
bool "PTP support for Marvell 88E6xxx"
default n
- depends on NET_DSA_MV88E6XXX && PTP_1588_CLOCK
+ depends on (NET_DSA_MV88E6XXX = y && PTP_1588_CLOCK = y) || \
+ (NET_DSA_MV88E6XXX = m && PTP_1588_CLOCK)
help
Say Y to enable PTP hardware timestamping on Marvell 88E6xxx switch
chips that support it.
diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c
index c5c3b4e92f28bf..2f224b166bbb32 100644
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c
@@ -37,77 +37,104 @@ qca8k_split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
}
static int
-qca8k_set_lo(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 lo)
+qca8k_mii_write_lo(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
{
- u16 *cached_lo = &priv->mdio_cache.lo;
- struct mii_bus *bus = priv->bus;
int ret;
+ u16 lo;
- if (lo == *cached_lo)
- return 0;
-
+ lo = val & 0xffff;
ret = bus->write(bus, phy_id, regnum, lo);
if (ret < 0)
dev_err_ratelimited(&bus->dev,
"failed to write qca8k 32bit lo register\n");
- *cached_lo = lo;
- return 0;
+ return ret;
}
static int
-qca8k_set_hi(struct qca8k_priv *priv, int phy_id, u32 regnum, u16 hi)
+qca8k_mii_write_hi(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
{
- u16 *cached_hi = &priv->mdio_cache.hi;
- struct mii_bus *bus = priv->bus;
int ret;
+ u16 hi;
- if (hi == *cached_hi)
- return 0;
-
+ hi = (u16)(val >> 16);
ret = bus->write(bus, phy_id, regnum, hi);
if (ret < 0)
dev_err_ratelimited(&bus->dev,
"failed to write qca8k 32bit hi register\n");
- *cached_hi = hi;
- return 0;
+ return ret;
}
static int
-qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
+qca8k_mii_read_lo(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
{
int ret;
ret = bus->read(bus, phy_id, regnum);
- if (ret >= 0) {
- *val = ret;
- ret = bus->read(bus, phy_id, regnum + 1);
- *val |= ret << 16;
- }
+ if (ret < 0)
+ goto err;
- if (ret < 0) {
- dev_err_ratelimited(&bus->dev,
- "failed to read qca8k 32bit register\n");
- *val = 0;
- return ret;
- }
+ *val = ret & 0xffff;
+ return 0;
+
+err:
+ dev_err_ratelimited(&bus->dev,
+ "failed to read qca8k 32bit lo register\n");
+ *val = 0;
+
+ return ret;
+}
+static int
+qca8k_mii_read_hi(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
+{
+ int ret;
+
+ ret = bus->read(bus, phy_id, regnum);
+ if (ret < 0)
+ goto err;
+
+ *val = ret << 16;
return 0;
+
+err:
+ dev_err_ratelimited(&bus->dev,
+ "failed to read qca8k 32bit hi register\n");
+ *val = 0;
+
+ return ret;
}
-static void
-qca8k_mii_write32(struct qca8k_priv *priv, int phy_id, u32 regnum, u32 val)
+static int
+qca8k_mii_read32(struct mii_bus *bus, int phy_id, u32 regnum, u32 *val)
{
- u16 lo, hi;
+ u32 hi, lo;
int ret;
- lo = val & 0xffff;
- hi = (u16)(val >> 16);
+ *val = 0;
- ret = qca8k_set_lo(priv, phy_id, regnum, lo);
- if (ret >= 0)
- ret = qca8k_set_hi(priv, phy_id, regnum + 1, hi);
+ ret = qca8k_mii_read_lo(bus, phy_id, regnum, &lo);
+ if (ret < 0)
+ goto err;
+
+ ret = qca8k_mii_read_hi(bus, phy_id, regnum + 1, &hi);
+ if (ret < 0)
+ goto err;
+
+ *val = lo | hi;
+
+err:
+ return ret;
+}
+
+static void
+qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val)
+{
+ if (qca8k_mii_write_lo(bus, phy_id, regnum, val) < 0)
+ return;
+
+ qca8k_mii_write_hi(bus, phy_id, regnum + 1, val);
}
static int
@@ -146,7 +173,16 @@ static void qca8k_rw_reg_ack_handler(struct dsa_switch *ds, struct sk_buff *skb)
command = get_unaligned_le32(&mgmt_ethhdr->command);
cmd = FIELD_GET(QCA_HDR_MGMT_CMD, command);
+
len = FIELD_GET(QCA_HDR_MGMT_LENGTH, command);
+ /* Special case for len of 15 as this is the max value for len and needs to
+ * be increased before converting it from word to dword.
+ */
+ if (len == 15)
+ len++;
+
+ /* We can ignore odd value, we always round up them in the alloc function. */
+ len *= sizeof(u16);
/* Make sure the seq match the requested packet */
if (get_unaligned_le32(&mgmt_ethhdr->seq) == mgmt_eth_data->seq)
@@ -193,17 +229,33 @@ static struct sk_buff *qca8k_alloc_mdio_header(enum mdio_cmd cmd, u32 reg, u32 *
if (!skb)
return NULL;
- /* Max value for len reg is 15 (0xf) but the switch actually return 16 byte
- * Actually for some reason the steps are:
- * 0: nothing
- * 1-4: first 4 byte
- * 5-6: first 12 byte
- * 7-15: all 16 byte
+ /* Hdr mgmt length value is in step of word size.
+ * As an example to process 4 byte of data the correct length to set is 2.
+ * To process 8 byte 4, 12 byte 6, 16 byte 8...
+ *
+ * Odd values will always return the next size on the ack packet.
+ * (length of 3 (6 byte) will always return 8 bytes of data)
+ *
+ * This means that a value of 15 (0xf) actually means reading/writing 32 bytes
+ * of data.
+ *
+ * To correctly calculate the length we devide the requested len by word and
+ * round up.
+ * On the ack function we can skip the odd check as we already handle the
+ * case here.
*/
- if (len == 16)
- real_len = 15;
- else
- real_len = len;
+ real_len = DIV_ROUND_UP(len, sizeof(u16));
+
+ /* We check if the result len is odd and we round up another time to
+ * the next size. (length of 3 will be increased to 4 as switch will always
+ * return 8 bytes)
+ */
+ if (real_len % sizeof(u16) != 0)
+ real_len++;
+
+ /* Max reg value is 0xf(15) but switch will always return the next size (32 byte) */
+ if (real_len == 16)
+ real_len--;
skb_reset_mac_header(skb);
skb_set_network_header(skb, skb->len);
@@ -417,7 +469,7 @@ qca8k_regmap_write(void *ctx, uint32_t reg, uint32_t val)
if (ret < 0)
goto exit;
- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+ qca8k_mii_write32(bus, 0x10 | r2, r1, val);
exit:
mutex_unlock(&bus->mdio_lock);
@@ -450,7 +502,7 @@ qca8k_regmap_update_bits(void *ctx, uint32_t reg, uint32_t mask, uint32_t write_
val &= ~mask;
val |= write_val;
- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+ qca8k_mii_write32(bus, 0x10 | r2, r1, val);
exit:
mutex_unlock(&bus->mdio_lock);
@@ -688,9 +740,9 @@ qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask)
qca8k_split_addr(reg, &r1, &r2, &page);
- ret = read_poll_timeout(qca8k_mii_read32, ret1, !(val & mask), 0,
+ ret = read_poll_timeout(qca8k_mii_read_hi, ret1, !(val & mask), 0,
QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false,
- bus, 0x10 | r2, r1, &val);
+ bus, 0x10 | r2, r1 + 1, &val);
/* Check if qca8k_read has failed for a different reason
* before returnting -ETIMEDOUT
@@ -725,14 +777,14 @@ qca8k_mdio_write(struct qca8k_priv *priv, int phy, int regnum, u16 data)
if (ret)
goto exit;
- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+ qca8k_mii_write32(bus, 0x10 | r2, r1, val);
ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
QCA8K_MDIO_MASTER_BUSY);
exit:
/* even if the busy_wait timeouts try to clear the MASTER_EN */
- qca8k_mii_write32(priv, 0x10 | r2, r1, 0);
+ qca8k_mii_write_hi(bus, 0x10 | r2, r1 + 1, 0);
mutex_unlock(&bus->mdio_lock);
@@ -762,18 +814,18 @@ qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum)
if (ret)
goto exit;
- qca8k_mii_write32(priv, 0x10 | r2, r1, val);
+ qca8k_mii_write_hi(bus, 0x10 | r2, r1 + 1, val);
ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL,
QCA8K_MDIO_MASTER_BUSY);
if (ret)
goto exit;
- ret = qca8k_mii_read32(bus, 0x10 | r2, r1, &val);
+ ret = qca8k_mii_read_lo(bus, 0x10 | r2, r1, &val);
exit:
/* even if the busy_wait timeouts try to clear the MASTER_EN */
- qca8k_mii_write32(priv, 0x10 | r2, r1, 0);
+ qca8k_mii_write_hi(bus, 0x10 | r2, r1 + 1, 0);
mutex_unlock(&bus->mdio_lock);
@@ -1943,8 +1995,6 @@ qca8k_sw_probe(struct mdio_device *mdiodev)
}
priv->mdio_cache.page = 0xffff;
- priv->mdio_cache.lo = 0xffff;
- priv->mdio_cache.hi = 0xffff;
/* Check the detected switch id */
ret = qca8k_read_switch_id(priv);
diff --git a/drivers/net/dsa/qca/qca8k.h b/drivers/net/dsa/qca/qca8k.h
index 0b7a5cb1232167..03514f7a20becc 100644
--- a/drivers/net/dsa/qca/qca8k.h
+++ b/drivers/net/dsa/qca/qca8k.h
@@ -375,11 +375,6 @@ struct qca8k_mdio_cache {
* mdio writes
*/
u16 page;
-/* lo and hi can also be cached and from Documentation we can skip one
- * extra mdio write if lo or hi is didn't change.
- */
- u16 lo;
- u16 hi;
};
struct qca8k_pcs {
diff --git a/drivers/net/ethernet/adi/adin1110.c b/drivers/net/ethernet/adi/adin1110.c
index 0805f249fff292..c26b8597945b5c 100644
--- a/drivers/net/ethernet/adi/adin1110.c
+++ b/drivers/net/ethernet/adi/adin1110.c
@@ -356,7 +356,7 @@ static int adin1110_read_fifo(struct adin1110_port_priv *port_priv)
if ((port_priv->flags & IFF_ALLMULTI && rxb->pkt_type == PACKET_MULTICAST) ||
(port_priv->flags & IFF_BROADCAST && rxb->pkt_type == PACKET_BROADCAST))
- rxb->offload_fwd_mark = 1;
+ rxb->offload_fwd_mark = port_priv->priv->forwarding;
netif_rx(rxb);
diff --git a/drivers/net/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c
index 8c8b4c88c7dead..451c3a1b625537 100644
--- a/drivers/net/ethernet/amazon/ena/ena_com.c
+++ b/drivers/net/ethernet/amazon/ena/ena_com.c
@@ -2400,29 +2400,18 @@ int ena_com_fill_hash_function(struct ena_com_dev *ena_dev,
return -EOPNOTSUPP;
}
- switch (func) {
- case ENA_ADMIN_TOEPLITZ:
- if (key) {
- if (key_len != sizeof(hash_key->key)) {
- netdev_err(ena_dev->net_device,
- "key len (%u) doesn't equal the supported size (%zu)\n",
- key_len, sizeof(hash_key->key));
- return -EINVAL;
- }
- memcpy(hash_key->key, key, key_len);
- rss->hash_init_val = init_val;
- hash_key->key_parts = key_len / sizeof(hash_key->key[0]);
+ if ((func == ENA_ADMIN_TOEPLITZ) && key) {
+ if (key_len != sizeof(hash_key->key)) {
+ netdev_err(ena_dev->net_device,
+ "key len (%u) doesn't equal the supported size (%zu)\n",
+ key_len, sizeof(hash_key->key));
+ return -EINVAL;
}
- break;
- case ENA_ADMIN_CRC32:
- rss->hash_init_val = init_val;
- break;
- default:
- netdev_err(ena_dev->net_device, "Invalid hash function (%d)\n",
- func);
- return -EINVAL;
+ memcpy(hash_key->key, key, key_len);
+ hash_key->key_parts = key_len / sizeof(hash_key->key[0]);
}
+ rss->hash_init_val = init_val;
old_func = rss->hash_func;
rss->hash_func = func;
rc = ena_com_set_hash_function(ena_dev);
diff --git a/drivers/net/ethernet/amazon/ena/ena_ethtool.c b/drivers/net/ethernet/amazon/ena/ena_ethtool.c
index 48ae6d810f8f9f..8da79eedc057c2 100644
--- a/drivers/net/ethernet/amazon/ena/ena_ethtool.c
+++ b/drivers/net/ethernet/amazon/ena/ena_ethtool.c
@@ -887,11 +887,7 @@ static int ena_set_tunable(struct net_device *netdev,
switch (tuna->id) {
case ETHTOOL_RX_COPYBREAK:
len = *(u32 *)data;
- if (len > adapter->netdev->mtu) {
- ret = -EINVAL;
- break;
- }
- adapter->rx_copybreak = len;
+ ret = ena_set_rx_copybreak(adapter, len);
break;
default:
ret = -EINVAL;
diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.c b/drivers/net/ethernet/amazon/ena/ena_netdev.c
index a95529a69cbb62..e8ad5ea31affec 100644
--- a/drivers/net/ethernet/amazon/ena/ena_netdev.c
+++ b/drivers/net/ethernet/amazon/ena/ena_netdev.c
@@ -374,9 +374,9 @@ static int ena_xdp_xmit(struct net_device *dev, int n,
static int ena_xdp_execute(struct ena_ring *rx_ring, struct xdp_buff *xdp)
{
+ u32 verdict = ENA_XDP_PASS;
struct bpf_prog *xdp_prog;
struct ena_ring *xdp_ring;
- u32 verdict = XDP_PASS;
struct xdp_frame *xdpf;
u64 *xdp_stat;
@@ -393,7 +393,7 @@ static int ena_xdp_execute(struct ena_ring *rx_ring, struct xdp_buff *xdp)
if (unlikely(!xdpf)) {
trace_xdp_exception(rx_ring->netdev, xdp_prog, verdict);
xdp_stat = &rx_ring->rx_stats.xdp_aborted;
- verdict = XDP_ABORTED;
+ verdict = ENA_XDP_DROP;
break;
}
@@ -409,29 +409,35 @@ static int ena_xdp_execute(struct ena_ring *rx_ring, struct xdp_buff *xdp)
spin_unlock(&xdp_ring->xdp_tx_lock);
xdp_stat = &rx_ring->rx_stats.xdp_tx;
+ verdict = ENA_XDP_TX;
break;
case XDP_REDIRECT:
if (likely(!xdp_do_redirect(rx_ring->netdev, xdp, xdp_prog))) {
xdp_stat = &rx_ring->rx_stats.xdp_redirect;
+ verdict = ENA_XDP_REDIRECT;
break;
}
trace_xdp_exception(rx_ring->netdev, xdp_prog, verdict);
xdp_stat = &rx_ring->rx_stats.xdp_aborted;
- verdict = XDP_ABORTED;
+ verdict = ENA_XDP_DROP;
break;
case XDP_ABORTED:
trace_xdp_exception(rx_ring->netdev, xdp_prog, verdict);
xdp_stat = &rx_ring->rx_stats.xdp_aborted;
+ verdict = ENA_XDP_DROP;
break;
case XDP_DROP:
xdp_stat = &rx_ring->rx_stats.xdp_drop;
+ verdict = ENA_XDP_DROP;
break;
case XDP_PASS:
xdp_stat = &rx_ring->rx_stats.xdp_pass;
+ verdict = ENA_XDP_PASS;
break;
default:
bpf_warn_invalid_xdp_action(rx_ring->netdev, xdp_prog, verdict);
xdp_stat = &rx_ring->rx_stats.xdp_invalid;
+ verdict = ENA_XDP_DROP;
}
ena_increase_stat(xdp_stat, 1, &rx_ring->syncp);
@@ -512,16 +518,18 @@ static void ena_xdp_exchange_program_rx_in_range(struct ena_adapter *adapter,
struct bpf_prog *prog,
int first, int count)
{
+ struct bpf_prog *old_bpf_prog;
struct ena_ring *rx_ring;
int i = 0;
for (i = first; i < count; i++) {
rx_ring = &adapter->rx_ring[i];
- xchg(&rx_ring->xdp_bpf_prog, prog);
- if (prog) {
+ old_bpf_prog = xchg(&rx_ring->xdp_bpf_prog, prog);
+
+ if (!old_bpf_prog && prog) {
ena_xdp_register_rxq_info(rx_ring);
rx_ring->rx_headroom = XDP_PACKET_HEADROOM;
- } else {
+ } else if (old_bpf_prog && !prog) {
ena_xdp_unregister_rxq_info(rx_ring);
rx_ring->rx_headroom = NET_SKB_PAD;
}
@@ -672,6 +680,7 @@ static void ena_init_io_rings_common(struct ena_adapter *adapter,
ring->ena_dev = adapter->ena_dev;
ring->per_napi_packets = 0;
ring->cpu = 0;
+ ring->numa_node = 0;
ring->no_interrupt_event_cnt = 0;
u64_stats_init(&ring->syncp);
}
@@ -775,6 +784,7 @@ static int ena_setup_tx_resources(struct ena_adapter *adapter, int qid)
tx_ring->next_to_use = 0;
tx_ring->next_to_clean = 0;
tx_ring->cpu = ena_irq->cpu;
+ tx_ring->numa_node = node;
return 0;
err_push_buf_intermediate_buf:
@@ -907,6 +917,7 @@ static int ena_setup_rx_resources(struct ena_adapter *adapter,
rx_ring->next_to_clean = 0;
rx_ring->next_to_use = 0;
rx_ring->cpu = ena_irq->cpu;
+ rx_ring->numa_node = node;
return 0;
}
@@ -1619,12 +1630,12 @@ static int ena_xdp_handle_buff(struct ena_ring *rx_ring, struct xdp_buff *xdp)
* we expect, then we simply drop it
*/
if (unlikely(rx_ring->ena_bufs[0].len > ENA_XDP_MAX_MTU))
- return XDP_DROP;
+ return ENA_XDP_DROP;
ret = ena_xdp_execute(rx_ring, xdp);
/* The xdp program might expand the headers */
- if (ret == XDP_PASS) {
+ if (ret == ENA_XDP_PASS) {
rx_info->page_offset = xdp->data - xdp->data_hard_start;
rx_ring->ena_bufs[0].len = xdp->data_end - xdp->data;
}
@@ -1663,7 +1674,7 @@ static int ena_clean_rx_irq(struct ena_ring *rx_ring, struct napi_struct *napi,
xdp_init_buff(&xdp, ENA_PAGE_SIZE, &rx_ring->xdp_rxq);
do {
- xdp_verdict = XDP_PASS;
+ xdp_verdict = ENA_XDP_PASS;
skb = NULL;
ena_rx_ctx.ena_bufs = rx_ring->ena_bufs;
ena_rx_ctx.max_bufs = rx_ring->sgl_size;
@@ -1691,7 +1702,7 @@ static int ena_clean_rx_irq(struct ena_ring *rx_ring, struct napi_struct *napi,
xdp_verdict = ena_xdp_handle_buff(rx_ring, &xdp);
/* allocate skb and fill it */
- if (xdp_verdict == XDP_PASS)
+ if (xdp_verdict == ENA_XDP_PASS)
skb = ena_rx_skb(rx_ring,
rx_ring->ena_bufs,
ena_rx_ctx.descs,
@@ -1709,14 +1720,15 @@ static int ena_clean_rx_irq(struct ena_ring *rx_ring, struct napi_struct *napi,
/* Packets was passed for transmission, unmap it
* from RX side.
*/
- if (xdp_verdict == XDP_TX || xdp_verdict == XDP_REDIRECT) {
+ if (xdp_verdict & ENA_XDP_FORWARDED) {
ena_unmap_rx_buff(rx_ring,
&rx_ring->rx_buffer_info[req_id]);
rx_ring->rx_buffer_info[req_id].page = NULL;
}
}
- if (xdp_verdict != XDP_PASS) {
+ if (xdp_verdict != ENA_XDP_PASS) {
xdp_flags |= xdp_verdict;
+ total_len += ena_rx_ctx.ena_bufs[0].len;
res_budget--;
continue;
}
@@ -1760,7 +1772,7 @@ static int ena_clean_rx_irq(struct ena_ring *rx_ring, struct napi_struct *napi,
ena_refill_rx_bufs(rx_ring, refill_required);
}
- if (xdp_flags & XDP_REDIRECT)
+ if (xdp_flags & ENA_XDP_REDIRECT)
xdp_do_flush_map();
return work_done;
@@ -1814,8 +1826,9 @@ static void ena_adjust_adaptive_rx_intr_moderation(struct ena_napi *ena_napi)
static void ena_unmask_interrupt(struct ena_ring *tx_ring,
struct ena_ring *rx_ring)
{
+ u32 rx_interval = tx_ring->smoothed_interval;
struct ena_eth_io_intr_reg intr_reg;
- u32 rx_interval = 0;
+
/* Rx ring can be NULL when for XDP tx queues which don't have an
* accompanying rx_ring pair.
*/
@@ -1853,20 +1866,27 @@ static void ena_update_ring_numa_node(struct ena_ring *tx_ring,
if (likely(tx_ring->cpu == cpu))
goto out;
+ tx_ring->cpu = cpu;
+ if (rx_ring)
+ rx_ring->cpu = cpu;
+
numa_node = cpu_to_node(cpu);
+
+ if (likely(tx_ring->numa_node == numa_node))
+ goto out;
+
put_cpu();
if (numa_node != NUMA_NO_NODE) {
ena_com_update_numa_node(tx_ring->ena_com_io_cq, numa_node);
- if (rx_ring)
+ tx_ring->numa_node = numa_node;
+ if (rx_ring) {
+ rx_ring->numa_node = numa_node;
ena_com_update_numa_node(rx_ring->ena_com_io_cq,
numa_node);
+ }
}
- tx_ring->cpu = cpu;
- if (rx_ring)
- rx_ring->cpu = cpu;
-
return;
out:
put_cpu();
@@ -1987,11 +2007,10 @@ static int ena_io_poll(struct napi_struct *napi, int budget)
if (ena_com_get_adaptive_moderation_enabled(rx_ring->ena_dev))
ena_adjust_adaptive_rx_intr_moderation(ena_napi);
+ ena_update_ring_numa_node(tx_ring, rx_ring);
ena_unmask_interrupt(tx_ring, rx_ring);
}
- ena_update_ring_numa_node(tx_ring, rx_ring);
-
ret = rx_work_done;
} else {
ret = budget;
@@ -2376,7 +2395,7 @@ static int ena_create_io_tx_queue(struct ena_adapter *adapter, int qid)
ctx.mem_queue_type = ena_dev->tx_mem_queue_type;
ctx.msix_vector = msix_vector;
ctx.queue_size = tx_ring->ring_size;
- ctx.numa_node = cpu_to_node(tx_ring->cpu);
+ ctx.numa_node = tx_ring->numa_node;
rc = ena_com_create_io_queue(ena_dev, &ctx);
if (rc) {
@@ -2444,7 +2463,7 @@ static int ena_create_io_rx_queue(struct ena_adapter *adapter, int qid)
ctx.mem_queue_type = ENA_ADMIN_PLACEMENT_POLICY_HOST;
ctx.msix_vector = msix_vector;
ctx.queue_size = rx_ring->ring_size;
- ctx.numa_node = cpu_to_node(rx_ring->cpu);
+ ctx.numa_node = rx_ring->numa_node;
rc = ena_com_create_io_queue(ena_dev, &ctx);
if (rc) {
@@ -2805,6 +2824,24 @@ int ena_update_queue_sizes(struct ena_adapter *adapter,
return dev_was_up ? ena_up(adapter) : 0;
}
+int ena_set_rx_copybreak(struct ena_adapter *adapter, u32 rx_copybreak)
+{
+ struct ena_ring *rx_ring;
+ int i;
+
+ if (rx_copybreak > min_t(u16, adapter->netdev->mtu, ENA_PAGE_SIZE))
+ return -EINVAL;
+
+ adapter->rx_copybreak = rx_copybreak;
+
+ for (i = 0; i < adapter->num_io_queues; i++) {
+ rx_ring = &adapter->rx_ring[i];
+ rx_ring->rx_copybreak = rx_copybreak;
+ }
+
+ return 0;
+}
+
int ena_update_queue_count(struct ena_adapter *adapter, u32 new_channel_count)
{
struct ena_com_dev *ena_dev = adapter->ena_dev;
diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.h b/drivers/net/ethernet/amazon/ena/ena_netdev.h
index 1bdce99bf68889..2cb141079474c6 100644
--- a/drivers/net/ethernet/amazon/ena/ena_netdev.h
+++ b/drivers/net/ethernet/amazon/ena/ena_netdev.h
@@ -262,9 +262,11 @@ struct ena_ring {
bool disable_meta_caching;
u16 no_interrupt_event_cnt;
- /* cpu for TPH */
+ /* cpu and NUMA for TPH */
int cpu;
- /* number of tx/rx_buffer_info's entries */
+ int numa_node;
+
+ /* number of tx/rx_buffer_info's entries */
int ring_size;
enum ena_admin_placement_policy_type tx_mem_queue_type;
@@ -392,6 +394,8 @@ int ena_update_queue_sizes(struct ena_adapter *adapter,
int ena_update_queue_count(struct ena_adapter *adapter, u32 new_channel_count);
+int ena_set_rx_copybreak(struct ena_adapter *adapter, u32 rx_copybreak);
+
int ena_get_sset_count(struct net_device *netdev, int sset);
static inline void ena_reset_device(struct ena_adapter *adapter,
@@ -409,6 +413,15 @@ enum ena_xdp_errors_t {
ENA_XDP_NO_ENOUGH_QUEUES,
};
+enum ENA_XDP_ACTIONS {
+ ENA_XDP_PASS = 0,
+ ENA_XDP_TX = BIT(0),
+ ENA_XDP_REDIRECT = BIT(1),
+ ENA_XDP_DROP = BIT(2)
+};
+
+#define ENA_XDP_FORWARDED (ENA_XDP_TX | ENA_XDP_REDIRECT)
+
static inline bool ena_xdp_present(struct ena_adapter *adapter)
{
return !!adapter->xdp_bpf_prog;
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
index 3936543a74d8f7..4030d619e84f56 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c
@@ -524,19 +524,28 @@ static void xgbe_disable_vxlan(struct xgbe_prv_data *pdata)
netif_dbg(pdata, drv, pdata->netdev, "VXLAN acceleration disabled\n");
}
+static unsigned int xgbe_get_fc_queue_count(struct xgbe_prv_data *pdata)
+{
+ unsigned int max_q_count = XGMAC_MAX_FLOW_CONTROL_QUEUES;
+
+ /* From MAC ver 30H the TFCR is per priority, instead of per queue */
+ if (XGMAC_GET_BITS(pdata->hw_feat.version, MAC_VR, SNPSVER) >= 0x30)
+ return max_q_count;
+ else
+ return min_t(unsigned int, pdata->tx_q_count, max_q_count);
+}
+
static int xgbe_disable_tx_flow_control(struct xgbe_prv_data *pdata)
{
- unsigned int max_q_count, q_count;
unsigned int reg, reg_val;
- unsigned int i;
+ unsigned int i, q_count;
/* Clear MTL flow control */
for (i = 0; i < pdata->rx_q_count; i++)
XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, EHFC, 0);
/* Clear MAC flow control */
- max_q_count = XGMAC_MAX_FLOW_CONTROL_QUEUES;
- q_count = min_t(unsigned int, pdata->tx_q_count, max_q_count);
+ q_count = xgbe_get_fc_queue_count(pdata);
reg = MAC_Q0TFCR;
for (i = 0; i < q_count; i++) {
reg_val = XGMAC_IOREAD(pdata, reg);
@@ -553,9 +562,8 @@ static int xgbe_enable_tx_flow_control(struct xgbe_prv_data *pdata)
{
struct ieee_pfc *pfc = pdata->pfc;
struct ieee_ets *ets = pdata->ets;
- unsigned int max_q_count, q_count;
unsigned int reg, reg_val;
- unsigned int i;
+ unsigned int i, q_count;
/* Set MTL flow control */
for (i = 0; i < pdata->rx_q_count; i++) {
@@ -579,8 +587,7 @@ static int xgbe_enable_tx_flow_control(struct xgbe_prv_data *pdata)
}
/* Set MAC flow control */
- max_q_count = XGMAC_MAX_FLOW_CONTROL_QUEUES;
- q_count = min_t(unsigned int, pdata->tx_q_count, max_q_count);
+ q_count = xgbe_get_fc_queue_count(pdata);
reg = MAC_Q0TFCR;
for (i = 0; i < q_count; i++) {
reg_val = XGMAC_IOREAD(pdata, reg);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index 7b666106feee93..614c0278419bcf 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -1064,6 +1064,9 @@ static void xgbe_free_irqs(struct xgbe_prv_data *pdata)
devm_free_irq(pdata->dev, pdata->dev_irq, pdata);
+ tasklet_kill(&pdata->tasklet_dev);
+ tasklet_kill(&pdata->tasklet_ecc);
+
if (pdata->vdata->ecc_support && (pdata->dev_irq != pdata->ecc_irq))
devm_free_irq(pdata->dev, pdata->ecc_irq, pdata);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-i2c.c b/drivers/net/ethernet/amd/xgbe/xgbe-i2c.c
index 22d4fc547a0a3f..a9ccc4258ee50d 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-i2c.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-i2c.c
@@ -447,8 +447,10 @@ static void xgbe_i2c_stop(struct xgbe_prv_data *pdata)
xgbe_i2c_disable(pdata);
xgbe_i2c_clear_all_interrupts(pdata);
- if (pdata->dev_irq != pdata->i2c_irq)
+ if (pdata->dev_irq != pdata->i2c_irq) {
devm_free_irq(pdata->dev, pdata->i2c_irq, pdata);
+ tasklet_kill(&pdata->tasklet_i2c);
+ }
}
static int xgbe_i2c_start(struct xgbe_prv_data *pdata)
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
index 4e97b486952205..43fdd111235a66 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
@@ -496,6 +496,7 @@ static enum xgbe_an xgbe_an73_tx_training(struct xgbe_prv_data *pdata,
reg |= XGBE_KR_TRAINING_ENABLE;
reg |= XGBE_KR_TRAINING_START;
XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg);
+ pdata->kr_start_time = jiffies;
netif_dbg(pdata, link, pdata->netdev,
"KR training initiated\n");
@@ -632,6 +633,8 @@ static enum xgbe_an xgbe_an73_incompat_link(struct xgbe_prv_data *pdata)
xgbe_switch_mode(pdata);
+ pdata->an_result = XGBE_AN_READY;
+
xgbe_an_restart(pdata);
return XGBE_AN_INCOMPAT_LINK;
@@ -1275,9 +1278,30 @@ static bool xgbe_phy_aneg_done(struct xgbe_prv_data *pdata)
static void xgbe_check_link_timeout(struct xgbe_prv_data *pdata)
{
unsigned long link_timeout;
+ unsigned long kr_time;
+ int wait;
link_timeout = pdata->link_check + (XGBE_LINK_TIMEOUT * HZ);
if (time_after(jiffies, link_timeout)) {
+ if ((xgbe_cur_mode(pdata) == XGBE_MODE_KR) &&
+ pdata->phy.autoneg == AUTONEG_ENABLE) {
+ /* AN restart should not happen while KR training is in progress.
+ * The while loop ensures no AN restart during KR training,
+ * waits up to 500ms and AN restart is triggered only if KR
+ * training is failed.
+ */
+ wait = XGBE_KR_TRAINING_WAIT_ITER;
+ while (wait--) {
+ kr_time = pdata->kr_start_time +
+ msecs_to_jiffies(XGBE_AN_MS_TIMEOUT);
+ if (time_after(jiffies, kr_time))
+ break;
+ /* AN restart is not required, if AN result is COMPLETE */
+ if (pdata->an_result == XGBE_AN_COMPLETE)
+ return;
+ usleep_range(10000, 11000);
+ }
+ }
netif_dbg(pdata, link, pdata->netdev, "AN link timeout\n");
xgbe_phy_config_aneg(pdata);
}
@@ -1390,8 +1414,10 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata)
/* Disable auto-negotiation */
xgbe_an_disable_all(pdata);
- if (pdata->dev_irq != pdata->an_irq)
+ if (pdata->dev_irq != pdata->an_irq) {
devm_free_irq(pdata->dev, pdata->an_irq, pdata);
+ tasklet_kill(&pdata->tasklet_an);
+ }
pdata->phy_if.phy_impl.stop(pdata);
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index 71f24cb479355e..7a41367c437ddf 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -290,6 +290,7 @@
/* Auto-negotiation */
#define XGBE_AN_MS_TIMEOUT 500
#define XGBE_LINK_TIMEOUT 5
+#define XGBE_KR_TRAINING_WAIT_ITER 50
#define XGBE_SGMII_AN_LINK_STATUS BIT(1)
#define XGBE_SGMII_AN_LINK_SPEED (BIT(2) | BIT(3))
@@ -1280,6 +1281,7 @@ struct xgbe_prv_data {
unsigned int parallel_detect;
unsigned int fec_ability;
unsigned long an_start;
+ unsigned long kr_start_time;
enum xgbe_an_mode an_mode;
/* I2C support */
diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c
index d91fdb0c2649d8..2cf96892e5650d 100644
--- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c
+++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c
@@ -2784,17 +2784,11 @@ static int bcm_enet_shared_probe(struct platform_device *pdev)
return 0;
}
-static int bcm_enet_shared_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
/* this "shared" driver is needed because both macs share a single
* address space
*/
struct platform_driver bcm63xx_enet_shared_driver = {
.probe = bcm_enet_shared_probe,
- .remove = bcm_enet_shared_remove,
.driver = {
.name = "bcm63xx_enet_shared",
.owner = THIS_MODULE,
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 4c7d07c684c492..240a7e8a76528c 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -991,10 +991,9 @@ static struct sk_buff *bnxt_rx_multi_page_skb(struct bnxt *bp,
dma_addr -= bp->rx_dma_offset;
dma_unmap_page_attrs(&bp->pdev->dev, dma_addr, PAGE_SIZE, bp->rx_dir,
DMA_ATTR_WEAK_ORDERING);
- skb = build_skb(page_address(page), BNXT_PAGE_MODE_BUF_SIZE +
- bp->rx_dma_offset);
+ skb = build_skb(page_address(page), PAGE_SIZE);
if (!skb) {
- __free_page(page);
+ page_pool_recycle_direct(rxr->page_pool, page);
return NULL;
}
skb_mark_for_recycle(skb);
@@ -1032,7 +1031,7 @@ static struct sk_buff *bnxt_rx_page_skb(struct bnxt *bp,
skb = napi_alloc_skb(&rxr->bnapi->napi, payload);
if (!skb) {
- __free_page(page);
+ page_pool_recycle_direct(rxr->page_pool, page);
return NULL;
}
@@ -1925,7 +1924,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr,
dma_addr = rx_buf->mapping;
if (bnxt_xdp_attached(bp, rxr)) {
- bnxt_xdp_buff_init(bp, rxr, cons, &data_ptr, &len, &xdp);
+ bnxt_xdp_buff_init(bp, rxr, cons, data_ptr, len, &xdp);
if (agg_bufs) {
u32 frag_len = bnxt_rx_agg_pages_xdp(bp, cpr, &xdp,
cp_cons, agg_bufs,
@@ -1940,7 +1939,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_cp_ring_info *cpr,
}
if (xdp_active) {
- if (bnxt_rx_xdp(bp, rxr, cons, xdp, data, &len, event)) {
+ if (bnxt_rx_xdp(bp, rxr, cons, xdp, data, &data_ptr, &len, event)) {
rc = 1;
goto next_rx;
}
@@ -3969,8 +3968,10 @@ void bnxt_set_ring_params(struct bnxt *bp)
bp->rx_agg_ring_mask = (bp->rx_agg_nr_pages * RX_DESC_CNT) - 1;
if (BNXT_RX_PAGE_MODE(bp)) {
- rx_space = BNXT_PAGE_MODE_BUF_SIZE;
- rx_size = BNXT_MAX_PAGE_MODE_MTU;
+ rx_space = PAGE_SIZE;
+ rx_size = PAGE_SIZE -
+ ALIGN(max(NET_SKB_PAD, XDP_PACKET_HEADROOM), 8) -
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
} else {
rx_size = SKB_DATA_ALIGN(BNXT_RX_COPY_THRESH + NET_IP_ALIGN);
rx_space = rx_size + NET_SKB_PAD +
@@ -5398,15 +5399,16 @@ static int bnxt_hwrm_vnic_set_hds(struct bnxt *bp, u16 vnic_id)
req->flags = cpu_to_le32(VNIC_PLCMODES_CFG_REQ_FLAGS_JUMBO_PLACEMENT);
req->enables = cpu_to_le32(VNIC_PLCMODES_CFG_REQ_ENABLES_JUMBO_THRESH_VALID);
- if (BNXT_RX_PAGE_MODE(bp) && !BNXT_RX_JUMBO_MODE(bp)) {
+ if (BNXT_RX_PAGE_MODE(bp)) {
+ req->jumbo_thresh = cpu_to_le16(bp->rx_buf_use_size);
+ } else {
req->flags |= cpu_to_le32(VNIC_PLCMODES_CFG_REQ_FLAGS_HDS_IPV4 |
VNIC_PLCMODES_CFG_REQ_FLAGS_HDS_IPV6);
req->enables |=
cpu_to_le32(VNIC_PLCMODES_CFG_REQ_ENABLES_HDS_THRESHOLD_VALID);
+ req->jumbo_thresh = cpu_to_le16(bp->rx_copy_thresh);
+ req->hds_threshold = cpu_to_le16(bp->rx_copy_thresh);
}
- /* thresholds not implemented in firmware yet */
- req->jumbo_thresh = cpu_to_le16(bp->rx_copy_thresh);
- req->hds_threshold = cpu_to_le16(bp->rx_copy_thresh);
req->vnic_id = cpu_to_le32(vnic->fw_vnic_id);
return hwrm_req_send(bp, req);
}
@@ -13591,7 +13593,6 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
return -ENOMEM;
bp = netdev_priv(dev);
- SET_NETDEV_DEVLINK_PORT(dev, &bp->dl_port);
bp->board_idx = ent->driver_data;
bp->msg_enable = BNXT_DEF_MSG_ENABLE;
bnxt_set_max_func_irqs(bp, max_irqs);
@@ -13599,6 +13600,10 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
if (bnxt_vf_pciid(bp->board_idx))
bp->flags |= BNXT_FLAG_VF;
+ /* No devlink port registration in case of a VF */
+ if (BNXT_PF(bp))
+ SET_NETDEV_DEVLINK_PORT(dev, &bp->dl_port);
+
if (pdev->msix_cap)
bp->flags |= BNXT_FLAG_MSIX_CAP;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
index 41c6dd0ae447e3..5163ef4a49ea35 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h
@@ -591,12 +591,20 @@ struct nqe_cn {
#define BNXT_RX_PAGE_SIZE (1 << BNXT_RX_PAGE_SHIFT)
#define BNXT_MAX_MTU 9500
-#define BNXT_PAGE_MODE_BUF_SIZE \
+
+/* First RX buffer page in XDP multi-buf mode
+ *
+ * +-------------------------------------------------------------------------+
+ * | XDP_PACKET_HEADROOM | bp->rx_buf_use_size | skb_shared_info|
+ * | (bp->rx_dma_offset) | | |
+ * +-------------------------------------------------------------------------+
+ */
+#define BNXT_MAX_PAGE_MODE_MTU_SBUF \
((unsigned int)PAGE_SIZE - VLAN_ETH_HLEN - NET_IP_ALIGN - \
XDP_PACKET_HEADROOM)
#define BNXT_MAX_PAGE_MODE_MTU \
- BNXT_PAGE_MODE_BUF_SIZE - \
- SKB_DATA_ALIGN((unsigned int)sizeof(struct skb_shared_info))
+ (BNXT_MAX_PAGE_MODE_MTU_SBUF - \
+ SKB_DATA_ALIGN((unsigned int)sizeof(struct skb_shared_info)))
#define BNXT_MIN_PKT_SIZE 52
@@ -2134,7 +2142,6 @@ struct bnxt {
#define BNXT_DUMP_CRASH 1
struct bpf_prog *xdp_prog;
- u8 xdp_has_frags;
struct bnxt_ptp_cfg *ptp_cfg;
u8 ptp_all_rx_tstamp;
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
index cbf17fcfb7ab43..ec573127b70762 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
@@ -3969,7 +3969,7 @@ void bnxt_ethtool_init(struct bnxt *bp)
test_info->timeout = HWRM_CMD_TIMEOUT;
for (i = 0; i < bp->num_tests; i++) {
char *str = test_info->string[i];
- char *fw_str = resp->test0_name + i * 32;
+ char *fw_str = resp->test_name[i];
if (i == BNXT_MACLPBK_TEST_IDX) {
strcpy(str, "Mac loopback test (offline)");
@@ -3980,14 +3980,9 @@ void bnxt_ethtool_init(struct bnxt *bp)
} else if (i == BNXT_IRQ_TEST_IDX) {
strcpy(str, "Interrupt_test (offline)");
} else {
- strscpy(str, fw_str, ETH_GSTRING_LEN);
- strncat(str, " test", ETH_GSTRING_LEN - strlen(str));
- if (test_info->offline_mask & (1 << i))
- strncat(str, " (offline)",
- ETH_GSTRING_LEN - strlen(str));
- else
- strncat(str, " (online)",
- ETH_GSTRING_LEN - strlen(str));
+ snprintf(str, ETH_GSTRING_LEN, "%s test (%s)",
+ fw_str, test_info->offline_mask & (1 << i) ?
+ "offline" : "online");
}
}
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
index 2686a714a59f77..a5408879e077e9 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h
@@ -10249,14 +10249,7 @@ struct hwrm_selftest_qlist_output {
u8 unused_0;
__le16 test_timeout;
u8 unused_1[2];
- char test0_name[32];
- char test1_name[32];
- char test2_name[32];
- char test3_name[32];
- char test4_name[32];
- char test5_name[32];
- char test6_name[32];
- char test7_name[32];
+ char test_name[8][32];
u8 eyescope_target_BER_support;
#define SELFTEST_QLIST_RESP_EYESCOPE_TARGET_BER_SUPPORT_BER_1E8_SUPPORTED 0x0UL
#define SELFTEST_QLIST_RESP_EYESCOPE_TARGET_BER_SUPPORT_BER_1E9_SUPPORTED 0x1UL
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
index c3065ec0a47981..36d5202c0aeec9 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c
@@ -177,7 +177,7 @@ bool bnxt_xdp_attached(struct bnxt *bp, struct bnxt_rx_ring_info *rxr)
}
void bnxt_xdp_buff_init(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
- u16 cons, u8 **data_ptr, unsigned int *len,
+ u16 cons, u8 *data_ptr, unsigned int len,
struct xdp_buff *xdp)
{
struct bnxt_sw_rx_bd *rx_buf;
@@ -191,13 +191,10 @@ void bnxt_xdp_buff_init(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
offset = bp->rx_offset;
mapping = rx_buf->mapping - bp->rx_dma_offset;
- dma_sync_single_for_cpu(&pdev->dev, mapping + offset, *len, bp->rx_dir);
-
- if (bp->xdp_has_frags)
- buflen = BNXT_PAGE_MODE_BUF_SIZE + offset;
+ dma_sync_single_for_cpu(&pdev->dev, mapping + offset, len, bp->rx_dir);
xdp_init_buff(xdp, buflen, &rxr->xdp_rxq);
- xdp_prepare_buff(xdp, *data_ptr - offset, offset, *len, false);
+ xdp_prepare_buff(xdp, data_ptr - offset, offset, len, false);
}
void bnxt_xdp_buff_frags_free(struct bnxt_rx_ring_info *rxr,
@@ -222,7 +219,8 @@ void bnxt_xdp_buff_frags_free(struct bnxt_rx_ring_info *rxr,
* false - packet should be passed to the stack.
*/
bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons,
- struct xdp_buff xdp, struct page *page, unsigned int *len, u8 *event)
+ struct xdp_buff xdp, struct page *page, u8 **data_ptr,
+ unsigned int *len, u8 *event)
{
struct bpf_prog *xdp_prog = READ_ONCE(rxr->xdp_prog);
struct bnxt_tx_ring_info *txr;
@@ -255,8 +253,10 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons,
*event &= ~BNXT_RX_EVENT;
*len = xdp.data_end - xdp.data;
- if (orig_data != xdp.data)
+ if (orig_data != xdp.data) {
offset = xdp.data - xdp.data_hard_start;
+ *data_ptr = xdp.data_hard_start + offset;
+ }
switch (act) {
case XDP_PASS:
@@ -401,10 +401,8 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog)
netdev_warn(dev, "ethtool rx/tx channels must be combined to support XDP.\n");
return -EOPNOTSUPP;
}
- if (prog) {
+ if (prog)
tx_xdp = bp->rx_nr_rings;
- bp->xdp_has_frags = prog->aux->xdp_has_frags;
- }
tc = netdev_get_num_tc(dev);
if (!tc)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h
index 505911ae095d33..ea430d6961df32 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h
@@ -18,8 +18,8 @@ struct bnxt_sw_tx_bd *bnxt_xmit_bd(struct bnxt *bp,
struct xdp_buff *xdp);
void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts);
bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons,
- struct xdp_buff xdp, struct page *page, unsigned int *len,
- u8 *event);
+ struct xdp_buff xdp, struct page *page, u8 **data_ptr,
+ unsigned int *len, u8 *event);
int bnxt_xdp(struct net_device *dev, struct netdev_bpf *xdp);
int bnxt_xdp_xmit(struct net_device *dev, int num_frames,
struct xdp_frame **frames, u32 flags);
@@ -27,7 +27,7 @@ int bnxt_xdp_xmit(struct net_device *dev, int num_frames,
bool bnxt_xdp_attached(struct bnxt *bp, struct bnxt_rx_ring_info *rxr);
void bnxt_xdp_buff_init(struct bnxt *bp, struct bnxt_rx_ring_info *rxr,
- u16 cons, u8 **data_ptr, unsigned int *len,
+ u16 cons, u8 *data_ptr, unsigned int len,
struct xdp_buff *xdp);
void bnxt_xdp_buff_frags_free(struct bnxt_rx_ring_info *rxr,
struct xdp_buff *xdp);
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 59debdc344a507..58747292521d81 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -11166,7 +11166,7 @@ static void tg3_reset_task(struct work_struct *work)
rtnl_lock();
tg3_full_lock(tp, 0);
- if (!netif_running(tp->dev)) {
+ if (tp->pcierr_recovery || !netif_running(tp->dev)) {
tg3_flag_clear(tp, RESET_TASK_PENDING);
tg3_full_unlock(tp);
rtnl_unlock();
@@ -18101,6 +18101,9 @@ static pci_ers_result_t tg3_io_error_detected(struct pci_dev *pdev,
netdev_info(netdev, "PCI I/O error detected\n");
+ /* Want to make sure that the reset task doesn't run */
+ tg3_reset_task_cancel(tp);
+
rtnl_lock();
/* Could be second call or maybe we don't have netdev yet */
@@ -18117,9 +18120,6 @@ static pci_ers_result_t tg3_io_error_detected(struct pci_dev *pdev,
tg3_timer_stop(tp);
- /* Want to make sure that the reset task doesn't run */
- tg3_reset_task_cancel(tp);
-
netif_device_detach(netdev);
/* Clean up software state, even if MMIO is blocked */
diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index 95667b979fab42..6cda31520c425e 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -2187,7 +2187,6 @@ static int macb_pad_and_fcs(struct sk_buff **skb, struct net_device *ndev)
bool cloned = skb_cloned(*skb) || skb_header_cloned(*skb) ||
skb_is_nonlinear(*skb);
int padlen = ETH_ZLEN - (*skb)->len;
- int headroom = skb_headroom(*skb);
int tailroom = skb_tailroom(*skb);
struct sk_buff *nskb;
u32 fcs;
@@ -2201,9 +2200,6 @@ static int macb_pad_and_fcs(struct sk_buff **skb, struct net_device *ndev)
/* FCS could be appeded to tailroom. */
if (tailroom >= ETH_FCS_LEN)
goto add_fcs;
- /* FCS could be appeded by moving data to headroom. */
- else if (!cloned && headroom + tailroom >= ETH_FCS_LEN)
- padlen = 0;
/* No room for FCS, need to reallocate skb. */
else
padlen = ETH_FCS_LEN;
@@ -2212,10 +2208,7 @@ static int macb_pad_and_fcs(struct sk_buff **skb, struct net_device *ndev)
padlen += ETH_FCS_LEN;
}
- if (!cloned && headroom + tailroom >= padlen) {
- (*skb)->data = memmove((*skb)->head, (*skb)->data, (*skb)->len);
- skb_set_tail_pointer(*skb, (*skb)->len);
- } else {
+ if (cloned || tailroom < padlen) {
nskb = skb_copy_expand(*skb, 0, padlen, GFP_ATOMIC);
if (!nskb)
return -ENOMEM;
@@ -4634,25 +4627,26 @@ static int init_reset_optional(struct platform_device *pdev)
if (ret)
return dev_err_probe(&pdev->dev, ret,
"failed to init SGMII PHY\n");
- }
- ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_SET_GEM_CONFIG);
- if (!ret) {
- u32 pm_info[2];
+ ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_SET_GEM_CONFIG);
+ if (!ret) {
+ u32 pm_info[2];
+
+ ret = of_property_read_u32_array(pdev->dev.of_node, "power-domains",
+ pm_info, ARRAY_SIZE(pm_info));
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to read power management information\n");
+ goto err_out_phy_exit;
+ }
+ ret = zynqmp_pm_set_gem_config(pm_info[1], GEM_CONFIG_FIXED, 0);
+ if (ret)
+ goto err_out_phy_exit;
- ret = of_property_read_u32_array(pdev->dev.of_node, "power-domains",
- pm_info, ARRAY_SIZE(pm_info));
- if (ret) {
- dev_err(&pdev->dev, "Failed to read power management information\n");
- goto err_out_phy_exit;
+ ret = zynqmp_pm_set_gem_config(pm_info[1], GEM_CONFIG_SGMII_MODE, 1);
+ if (ret)
+ goto err_out_phy_exit;
}
- ret = zynqmp_pm_set_gem_config(pm_info[1], GEM_CONFIG_FIXED, 0);
- if (ret)
- goto err_out_phy_exit;
- ret = zynqmp_pm_set_gem_config(pm_info[1], GEM_CONFIG_SGMII_MODE, 1);
- if (ret)
- goto err_out_phy_exit;
}
/* Fully reset controller at hardware level if mapped in device tree */
diff --git a/drivers/net/ethernet/engleder/tsnep_main.c b/drivers/net/ethernet/engleder/tsnep_main.c
index bf0190e1d2ea4c..00e2108f2ca4cb 100644
--- a/drivers/net/ethernet/engleder/tsnep_main.c
+++ b/drivers/net/ethernet/engleder/tsnep_main.c
@@ -450,7 +450,7 @@ static netdev_tx_t tsnep_xmit_frame_ring(struct sk_buff *skb,
/* ring full, shall not happen because queue is stopped if full
* below
*/
- netif_stop_queue(tx->adapter->netdev);
+ netif_stop_subqueue(tx->adapter->netdev, tx->queue_index);
spin_unlock_irqrestore(&tx->lock, flags);
@@ -493,7 +493,7 @@ static netdev_tx_t tsnep_xmit_frame_ring(struct sk_buff *skb,
if (tsnep_tx_desc_available(tx) < (MAX_SKB_FRAGS + 1)) {
/* ring can get full with next frame */
- netif_stop_queue(tx->adapter->netdev);
+ netif_stop_subqueue(tx->adapter->netdev, tx->queue_index);
}
spin_unlock_irqrestore(&tx->lock, flags);
@@ -503,11 +503,14 @@ static netdev_tx_t tsnep_xmit_frame_ring(struct sk_buff *skb,
static bool tsnep_tx_poll(struct tsnep_tx *tx, int napi_budget)
{
+ struct tsnep_tx_entry *entry;
+ struct netdev_queue *nq;
unsigned long flags;
int budget = 128;
- struct tsnep_tx_entry *entry;
- int count;
int length;
+ int count;
+
+ nq = netdev_get_tx_queue(tx->adapter->netdev, tx->queue_index);
spin_lock_irqsave(&tx->lock, flags);
@@ -564,8 +567,8 @@ static bool tsnep_tx_poll(struct tsnep_tx *tx, int napi_budget)
} while (likely(budget));
if ((tsnep_tx_desc_available(tx) >= ((MAX_SKB_FRAGS + 1) * 2)) &&
- netif_queue_stopped(tx->adapter->netdev)) {
- netif_wake_queue(tx->adapter->netdev);
+ netif_tx_queue_stopped(nq)) {
+ netif_tx_wake_queue(nq);
}
spin_unlock_irqrestore(&tx->lock, flags);
diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
index 3f8032947d86ef..027fff9f7db073 100644
--- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
+++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c
@@ -2410,6 +2410,9 @@ static int dpaa_eth_poll(struct napi_struct *napi, int budget)
cleaned = qman_p_poll_dqrr(np->p, budget);
+ if (np->xdp_act & XDP_REDIRECT)
+ xdp_do_flush();
+
if (cleaned < budget) {
napi_complete_done(napi, cleaned);
qman_p_irqsource_add(np->p, QM_PIRQ_DQRI);
@@ -2417,9 +2420,6 @@ static int dpaa_eth_poll(struct napi_struct *napi, int budget)
qman_p_irqsource_add(np->p, QM_PIRQ_DQRI);
}
- if (np->xdp_act & XDP_REDIRECT)
- xdp_do_flush();
-
return cleaned;
}
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
index 0c35abb7d065e6..2e79d18fc3c7ad 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
@@ -1993,10 +1993,15 @@ static int dpaa2_eth_poll(struct napi_struct *napi, int budget)
if (rx_cleaned >= budget ||
txconf_cleaned >= DPAA2_ETH_TXCONF_PER_NAPI) {
work_done = budget;
+ if (ch->xdp.res & XDP_REDIRECT)
+ xdp_do_flush();
goto out;
}
} while (store_cleaned);
+ if (ch->xdp.res & XDP_REDIRECT)
+ xdp_do_flush();
+
/* Update NET DIM with the values for this CDAN */
dpaa2_io_update_net_dim(ch->dpio, ch->stats.frames_per_cdan,
ch->stats.bytes_per_cdan);
@@ -2032,9 +2037,7 @@ out:
txc_fq->dq_bytes = 0;
}
- if (ch->xdp.res & XDP_REDIRECT)
- xdp_do_flush_map();
- else if (rx_cleaned && ch->xdp.res & XDP_TX)
+ if (rx_cleaned && ch->xdp.res & XDP_TX)
dpaa2_eth_xdp_tx_flush(priv, ch, &priv->fq[flowid]);
return work_done;
diff --git a/drivers/net/ethernet/freescale/enetc/enetc.c b/drivers/net/ethernet/freescale/enetc/enetc.c
index 3a79ead5219ae2..e96449eedfb542 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc.c
@@ -2290,14 +2290,14 @@ static void enetc_tx_onestep_tstamp(struct work_struct *work)
priv = container_of(work, struct enetc_ndev_priv, tx_onestep_tstamp);
- netif_tx_lock(priv->ndev);
+ netif_tx_lock_bh(priv->ndev);
clear_bit_unlock(ENETC_TX_ONESTEP_TSTAMP_IN_PROGRESS, &priv->flags);
skb = skb_dequeue(&priv->tx_skbs);
if (skb)
enetc_start_xmit(skb, priv->ndev);
- netif_tx_unlock(priv->ndev);
+ netif_tx_unlock_bh(priv->ndev);
}
static void enetc_tx_onestep_tstamp_init(struct enetc_ndev_priv *priv)
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ierb.c b/drivers/net/ethernet/freescale/enetc/enetc_ierb.c
index 91f02c5050285b..b307bef4dc298d 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_ierb.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_ierb.c
@@ -127,11 +127,6 @@ static int enetc_ierb_probe(struct platform_device *pdev)
return 0;
}
-static int enetc_ierb_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static const struct of_device_id enetc_ierb_match[] = {
{ .compatible = "fsl,ls1028a-enetc-ierb", },
{},
@@ -144,7 +139,6 @@ static struct platform_driver enetc_ierb_driver = {
.of_match_table = enetc_ierb_match,
},
.probe = enetc_ierb_probe,
- .remove = enetc_ierb_remove,
};
module_platform_driver(enetc_ierb_driver);
diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c
index 644f3c96373029..2341597408d129 100644
--- a/drivers/net/ethernet/freescale/fec_main.c
+++ b/drivers/net/ethernet/freescale/fec_main.c
@@ -3191,7 +3191,7 @@ static void fec_enet_free_buffers(struct net_device *ndev)
for (q = 0; q < fep->num_rx_queues; q++) {
rxq = fep->rx_queue[q];
for (i = 0; i < rxq->bd.ring_size; i++)
- page_pool_release_page(rxq->page_pool, rxq->rx_skb_info[i].page);
+ page_pool_put_full_page(rxq->page_pool, rxq->rx_skb_info[i].page, false);
for (i = 0; i < XDP_STATS_TOTAL; i++)
rxq->stats[i] = 0;
diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
index d00bae15a9017d..d528ca681b6f46 100644
--- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c
+++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c
@@ -1430,7 +1430,7 @@ int dtsec_initialization(struct mac_device *mac_dev,
dtsec->dtsec_drv_param->tx_pad_crc = true;
phy_node = of_parse_phandle(mac_node, "tbi-handle", 0);
- if (!phy_node || of_device_is_available(phy_node)) {
+ if (!phy_node || !of_device_is_available(phy_node)) {
of_node_put(phy_node);
err = -EINVAL;
dev_err_probe(mac_dev->dev, err,
diff --git a/drivers/net/ethernet/freescale/fman/fman_memac.c b/drivers/net/ethernet/freescale/fman/fman_memac.c
index 9349f841bd0645..587ad81a2dc329 100644
--- a/drivers/net/ethernet/freescale/fman/fman_memac.c
+++ b/drivers/net/ethernet/freescale/fman/fman_memac.c
@@ -1055,6 +1055,9 @@ static struct phylink_pcs *memac_pcs_create(struct device_node *mac_node,
return ERR_PTR(-EPROBE_DEFER);
pcs = lynx_pcs_create(mdiodev);
+ if (!pcs)
+ mdio_device_free(mdiodev);
+
return pcs;
}
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
index 0ec5730b178868..b4c4fb873568c6 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c
@@ -3855,18 +3855,16 @@ static int hns3_gro_complete(struct sk_buff *skb, u32 l234info)
return 0;
}
-static bool hns3_checksum_complete(struct hns3_enet_ring *ring,
+static void hns3_checksum_complete(struct hns3_enet_ring *ring,
struct sk_buff *skb, u32 ptype, u16 csum)
{
if (ptype == HNS3_INVALID_PTYPE ||
hns3_rx_ptype_tbl[ptype].ip_summed != CHECKSUM_COMPLETE)
- return false;
+ return;
hns3_ring_stats_update(ring, csum_complete);
skb->ip_summed = CHECKSUM_COMPLETE;
skb->csum = csum_unfold((__force __sum16)csum);
-
- return true;
}
static void hns3_rx_handle_csum(struct sk_buff *skb, u32 l234info,
@@ -3926,8 +3924,7 @@ static void hns3_rx_checksum(struct hns3_enet_ring *ring, struct sk_buff *skb,
ptype = hnae3_get_field(ol_info, HNS3_RXD_PTYPE_M,
HNS3_RXD_PTYPE_S);
- if (hns3_checksum_complete(ring, skb, ptype, csum))
- return;
+ hns3_checksum_complete(ring, skb, ptype, csum);
/* check if hardware has done checksum */
if (!(bd_base_info & BIT(HNS3_RXD_L3L4P_B)))
@@ -3936,6 +3933,7 @@ static void hns3_rx_checksum(struct hns3_enet_ring *ring, struct sk_buff *skb,
if (unlikely(l234info & (BIT(HNS3_RXD_L3E_B) | BIT(HNS3_RXD_L4E_B) |
BIT(HNS3_RXD_OL3E_B) |
BIT(HNS3_RXD_OL4E_B)))) {
+ skb->ip_summed = CHECKSUM_NONE;
hns3_ring_stats_update(ring, l3l4_csum_err);
return;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
index 4e54f91f7a6c15..07ad5f35219e26 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c
@@ -3910,9 +3910,17 @@ static int hclge_set_all_vf_rst(struct hclge_dev *hdev, bool reset)
return ret;
}
- if (!reset || !test_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state))
+ if (!reset ||
+ !test_bit(HCLGE_VPORT_STATE_INITED, &vport->state))
continue;
+ if (!test_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state) &&
+ hdev->reset_type == HNAE3_FUNC_RESET) {
+ set_bit(HCLGE_VPORT_NEED_NOTIFY_RESET,
+ &vport->need_notify);
+ continue;
+ }
+
/* Inform VF to process the reset.
* hclge_inform_reset_assert_to_vf may fail if VF
* driver is not loaded.
@@ -4609,18 +4617,25 @@ static void hclge_reset_service_task(struct hclge_dev *hdev)
static void hclge_update_vport_alive(struct hclge_dev *hdev)
{
+#define HCLGE_ALIVE_SECONDS_NORMAL 8
+
+ unsigned long alive_time = HCLGE_ALIVE_SECONDS_NORMAL * HZ;
int i;
/* start from vport 1 for PF is always alive */
for (i = 1; i < hdev->num_alloc_vport; i++) {
struct hclge_vport *vport = &hdev->vport[i];
- if (time_after(jiffies, vport->last_active_jiffies + 8 * HZ))
+ if (!test_bit(HCLGE_VPORT_STATE_INITED, &vport->state) ||
+ !test_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state))
+ continue;
+ if (time_after(jiffies, vport->last_active_jiffies +
+ alive_time)) {
clear_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
-
- /* If vf is not alive, set to default value */
- if (!test_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state))
- vport->mps = HCLGE_MAC_DEFAULT_FRAME;
+ dev_warn(&hdev->pdev->dev,
+ "VF %u heartbeat timeout\n",
+ i - HCLGE_VF_VPORT_START_NUM);
+ }
}
}
@@ -8064,9 +8079,11 @@ int hclge_vport_start(struct hclge_vport *vport)
{
struct hclge_dev *hdev = vport->back;
+ set_bit(HCLGE_VPORT_STATE_INITED, &vport->state);
set_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE, &vport->state);
vport->last_active_jiffies = jiffies;
+ vport->need_notify = 0;
if (test_bit(vport->vport_id, hdev->vport_config_block)) {
if (vport->vport_id) {
@@ -8084,7 +8101,9 @@ int hclge_vport_start(struct hclge_vport *vport)
void hclge_vport_stop(struct hclge_vport *vport)
{
+ clear_bit(HCLGE_VPORT_STATE_INITED, &vport->state);
clear_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
+ vport->need_notify = 0;
}
static int hclge_client_start(struct hnae3_handle *handle)
@@ -9208,7 +9227,8 @@ static int hclge_set_vf_mac(struct hnae3_handle *handle, int vf,
return 0;
}
- dev_info(&hdev->pdev->dev, "MAC of VF %d has been set to %s\n",
+ dev_info(&hdev->pdev->dev,
+ "MAC of VF %d has been set to %s, will be active after VF reset\n",
vf, format_mac_addr);
return 0;
}
@@ -10465,12 +10485,16 @@ static int hclge_set_vf_vlan_filter(struct hnae3_handle *handle, int vfid,
* for DEVICE_VERSION_V3, vf doesn't need to know about the port based
* VLAN state.
*/
- if (ae_dev->dev_version < HNAE3_DEVICE_VERSION_V3 &&
- test_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state))
- (void)hclge_push_vf_port_base_vlan_info(&hdev->vport[0],
- vport->vport_id,
- state, &vlan_info);
-
+ if (ae_dev->dev_version < HNAE3_DEVICE_VERSION_V3) {
+ if (test_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state))
+ (void)hclge_push_vf_port_base_vlan_info(&hdev->vport[0],
+ vport->vport_id,
+ state,
+ &vlan_info);
+ else
+ set_bit(HCLGE_VPORT_NEED_NOTIFY_VF_VLAN,
+ &vport->need_notify);
+ }
return 0;
}
@@ -11941,7 +11965,7 @@ static void hclge_reset_vport_state(struct hclge_dev *hdev)
int i;
for (i = 0; i < hdev->num_alloc_vport; i++) {
- hclge_vport_stop(vport);
+ clear_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
vport++;
}
}
@@ -12754,60 +12778,71 @@ static int hclge_gro_en(struct hnae3_handle *handle, bool enable)
return ret;
}
-static void hclge_sync_promisc_mode(struct hclge_dev *hdev)
+static int hclge_sync_vport_promisc_mode(struct hclge_vport *vport)
{
- struct hclge_vport *vport = &hdev->vport[0];
struct hnae3_handle *handle = &vport->nic;
+ struct hclge_dev *hdev = vport->back;
+ bool uc_en = false;
+ bool mc_en = false;
u8 tmp_flags;
+ bool bc_en;
int ret;
- u16 i;
if (vport->last_promisc_flags != vport->overflow_promisc_flags) {
set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE, &vport->state);
vport->last_promisc_flags = vport->overflow_promisc_flags;
}
- if (test_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE, &vport->state)) {
+ if (!test_and_clear_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE,
+ &vport->state))
+ return 0;
+
+ /* for PF */
+ if (!vport->vport_id) {
tmp_flags = handle->netdev_flags | vport->last_promisc_flags;
ret = hclge_set_promisc_mode(handle, tmp_flags & HNAE3_UPE,
tmp_flags & HNAE3_MPE);
- if (!ret) {
- clear_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE,
- &vport->state);
+ if (!ret)
set_bit(HCLGE_VPORT_STATE_VLAN_FLTR_CHANGE,
&vport->state);
- }
+ else
+ set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE,
+ &vport->state);
+ return ret;
}
- for (i = 1; i < hdev->num_alloc_vport; i++) {
- bool uc_en = false;
- bool mc_en = false;
- bool bc_en;
+ /* for VF */
+ if (vport->vf_info.trusted) {
+ uc_en = vport->vf_info.request_uc_en > 0 ||
+ vport->overflow_promisc_flags & HNAE3_OVERFLOW_UPE;
+ mc_en = vport->vf_info.request_mc_en > 0 ||
+ vport->overflow_promisc_flags & HNAE3_OVERFLOW_MPE;
+ }
+ bc_en = vport->vf_info.request_bc_en > 0;
- vport = &hdev->vport[i];
+ ret = hclge_cmd_set_promisc_mode(hdev, vport->vport_id, uc_en,
+ mc_en, bc_en);
+ if (ret) {
+ set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE, &vport->state);
+ return ret;
+ }
+ hclge_set_vport_vlan_fltr_change(vport);
- if (!test_and_clear_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE,
- &vport->state))
- continue;
+ return 0;
+}
- if (vport->vf_info.trusted) {
- uc_en = vport->vf_info.request_uc_en > 0 ||
- vport->overflow_promisc_flags &
- HNAE3_OVERFLOW_UPE;
- mc_en = vport->vf_info.request_mc_en > 0 ||
- vport->overflow_promisc_flags &
- HNAE3_OVERFLOW_MPE;
- }
- bc_en = vport->vf_info.request_bc_en > 0;
+static void hclge_sync_promisc_mode(struct hclge_dev *hdev)
+{
+ struct hclge_vport *vport;
+ int ret;
+ u16 i;
- ret = hclge_cmd_set_promisc_mode(hdev, vport->vport_id, uc_en,
- mc_en, bc_en);
- if (ret) {
- set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE,
- &vport->state);
+ for (i = 0; i < hdev->num_alloc_vport; i++) {
+ vport = &hdev->vport[i];
+
+ ret = hclge_sync_vport_promisc_mode(vport);
+ if (ret)
return;
- }
- hclge_set_vport_vlan_fltr_change(vport);
}
}
@@ -12944,6 +12979,11 @@ static void hclge_clear_vport_vf_info(struct hclge_vport *vport, int vfid)
struct hclge_vlan_info vlan_info;
int ret;
+ clear_bit(HCLGE_VPORT_STATE_INITED, &vport->state);
+ clear_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
+ vport->need_notify = 0;
+ vport->mps = 0;
+
/* after disable sriov, clean VF rate configured by PF */
ret = hclge_tm_qs_shaper_cfg(vport, 0);
if (ret)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
index 495b639b0dc249..13f23d606e77bd 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h
@@ -995,9 +995,15 @@ enum HCLGE_VPORT_STATE {
HCLGE_VPORT_STATE_MAC_TBL_CHANGE,
HCLGE_VPORT_STATE_PROMISC_CHANGE,
HCLGE_VPORT_STATE_VLAN_FLTR_CHANGE,
+ HCLGE_VPORT_STATE_INITED,
HCLGE_VPORT_STATE_MAX
};
+enum HCLGE_VPORT_NEED_NOTIFY {
+ HCLGE_VPORT_NEED_NOTIFY_RESET,
+ HCLGE_VPORT_NEED_NOTIFY_VF_VLAN,
+};
+
struct hclge_vlan_info {
u16 vlan_proto; /* so far support 802.1Q only */
u16 qos;
@@ -1044,6 +1050,7 @@ struct hclge_vport {
struct hnae3_handle roce;
unsigned long state;
+ unsigned long need_notify;
unsigned long last_active_jiffies;
u32 mps; /* Max packet size */
struct hclge_vf_info vf_info;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c
index a7b06c63143cc2..04ff9bf121853a 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mbx.c
@@ -124,17 +124,26 @@ static int hclge_send_mbx_msg(struct hclge_vport *vport, u8 *msg, u16 msg_len,
return status;
}
+static int hclge_inform_vf_reset(struct hclge_vport *vport, u16 reset_type)
+{
+ __le16 msg_data;
+ u8 dest_vfid;
+
+ dest_vfid = (u8)vport->vport_id;
+ msg_data = cpu_to_le16(reset_type);
+
+ /* send this requested info to VF */
+ return hclge_send_mbx_msg(vport, (u8 *)&msg_data, sizeof(msg_data),
+ HCLGE_MBX_ASSERTING_RESET, dest_vfid);
+}
+
int hclge_inform_reset_assert_to_vf(struct hclge_vport *vport)
{
struct hclge_dev *hdev = vport->back;
- __le16 msg_data;
u16 reset_type;
- u8 dest_vfid;
BUILD_BUG_ON(HNAE3_MAX_RESET > U16_MAX);
- dest_vfid = (u8)vport->vport_id;
-
if (hdev->reset_type == HNAE3_FUNC_RESET)
reset_type = HNAE3_VF_PF_FUNC_RESET;
else if (hdev->reset_type == HNAE3_FLR_RESET)
@@ -142,11 +151,7 @@ int hclge_inform_reset_assert_to_vf(struct hclge_vport *vport)
else
reset_type = HNAE3_VF_FUNC_RESET;
- msg_data = cpu_to_le16(reset_type);
-
- /* send this requested info to VF */
- return hclge_send_mbx_msg(vport, (u8 *)&msg_data, sizeof(msg_data),
- HCLGE_MBX_ASSERTING_RESET, dest_vfid);
+ return hclge_inform_vf_reset(vport, reset_type);
}
static void hclge_free_vector_ring_chain(struct hnae3_ring_chain_node *head)
@@ -652,9 +657,56 @@ static int hclge_reset_vf(struct hclge_vport *vport)
return hclge_func_reset_cmd(hdev, vport->vport_id);
}
+static void hclge_notify_vf_config(struct hclge_vport *vport)
+{
+ struct hclge_dev *hdev = vport->back;
+ struct hnae3_ae_dev *ae_dev = pci_get_drvdata(hdev->pdev);
+ struct hclge_port_base_vlan_config *vlan_cfg;
+ int ret;
+
+ hclge_push_vf_link_status(vport);
+ if (test_bit(HCLGE_VPORT_NEED_NOTIFY_RESET, &vport->need_notify)) {
+ ret = hclge_inform_vf_reset(vport, HNAE3_VF_PF_FUNC_RESET);
+ if (ret) {
+ dev_err(&hdev->pdev->dev,
+ "failed to inform VF %u reset!",
+ vport->vport_id - HCLGE_VF_VPORT_START_NUM);
+ return;
+ }
+ vport->need_notify = 0;
+ return;
+ }
+
+ if (ae_dev->dev_version < HNAE3_DEVICE_VERSION_V3 &&
+ test_bit(HCLGE_VPORT_NEED_NOTIFY_VF_VLAN, &vport->need_notify)) {
+ vlan_cfg = &vport->port_base_vlan_cfg;
+ ret = hclge_push_vf_port_base_vlan_info(&hdev->vport[0],
+ vport->vport_id,
+ vlan_cfg->state,
+ &vlan_cfg->vlan_info);
+ if (ret) {
+ dev_err(&hdev->pdev->dev,
+ "failed to inform VF %u port base vlan!",
+ vport->vport_id - HCLGE_VF_VPORT_START_NUM);
+ return;
+ }
+ clear_bit(HCLGE_VPORT_NEED_NOTIFY_VF_VLAN, &vport->need_notify);
+ }
+}
+
static void hclge_vf_keep_alive(struct hclge_vport *vport)
{
+ struct hclge_dev *hdev = vport->back;
+
vport->last_active_jiffies = jiffies;
+
+ if (test_bit(HCLGE_VPORT_STATE_INITED, &vport->state) &&
+ !test_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state)) {
+ set_bit(HCLGE_VPORT_STATE_ALIVE, &vport->state);
+ dev_info(&hdev->pdev->dev, "VF %u is alive!",
+ vport->vport_id - HCLGE_VF_VPORT_START_NUM);
+ hclge_notify_vf_config(vport);
+ }
}
static int hclge_set_vf_mtu(struct hclge_vport *vport,
@@ -954,6 +1006,7 @@ static int hclge_mbx_vf_uninit_handler(struct hclge_mbx_ops_param *param)
hclge_rm_vport_all_mac_table(param->vport, true,
HCLGE_MAC_ADDR_MC);
hclge_rm_vport_all_vlan_table(param->vport, true);
+ param->vport->mps = 0;
return 0;
}
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
index db6f7cdba9587e..e84e5be8e59ed8 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c
@@ -2767,7 +2767,8 @@ static int hclgevf_pci_reset(struct hclgevf_dev *hdev)
struct pci_dev *pdev = hdev->pdev;
int ret = 0;
- if (hdev->reset_type == HNAE3_VF_FULL_RESET &&
+ if ((hdev->reset_type == HNAE3_VF_FULL_RESET ||
+ hdev->reset_type == HNAE3_FLR_RESET) &&
test_bit(HCLGEVF_STATE_IRQ_INITED, &hdev->state)) {
hclgevf_misc_irq_uninit(hdev);
hclgevf_uninit_msi(hdev);
@@ -3129,7 +3130,7 @@ static int hclgevf_set_channels(struct hnae3_handle *handle, u32 new_tqps_num,
hclgevf_update_rss_size(handle, new_tqps_num);
- hclge_comm_get_rss_tc_info(cur_rss_size, hdev->hw_tc_map,
+ hclge_comm_get_rss_tc_info(kinfo->rss_size, hdev->hw_tc_map,
tc_offset, tc_valid, tc_size);
ret = hclge_comm_set_rss_tc_mode(&hdev->hw.hw, tc_offset,
tc_valid, tc_size);
diff --git a/drivers/net/ethernet/intel/iavf/iavf.h b/drivers/net/ethernet/intel/iavf/iavf.h
index 0d1bab4ac1b075..2a9f1eeeb7015f 100644
--- a/drivers/net/ethernet/intel/iavf/iavf.h
+++ b/drivers/net/ethernet/intel/iavf/iavf.h
@@ -249,6 +249,7 @@ struct iavf_cloud_filter {
/* board specific private data structure */
struct iavf_adapter {
+ struct workqueue_struct *wq;
struct work_struct reset_task;
struct work_struct adminq_task;
struct delayed_work client_task;
@@ -459,7 +460,6 @@ struct iavf_device {
/* needed by iavf_ethtool.c */
extern char iavf_driver_name[];
-extern struct workqueue_struct *iavf_wq;
static inline const char *iavf_state_str(enum iavf_state_t state)
{
diff --git a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c
index d79ead5e8d0ca6..6f171d1d85b75f 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c
@@ -532,7 +532,7 @@ static int iavf_set_priv_flags(struct net_device *netdev, u32 flags)
if (changed_flags & IAVF_FLAG_LEGACY_RX) {
if (netif_running(netdev)) {
adapter->flags |= IAVF_FLAG_RESET_NEEDED;
- queue_work(iavf_wq, &adapter->reset_task);
+ queue_work(adapter->wq, &adapter->reset_task);
}
}
@@ -672,7 +672,7 @@ static int iavf_set_ringparam(struct net_device *netdev,
if (netif_running(netdev)) {
adapter->flags |= IAVF_FLAG_RESET_NEEDED;
- queue_work(iavf_wq, &adapter->reset_task);
+ queue_work(adapter->wq, &adapter->reset_task);
}
return 0;
@@ -1433,7 +1433,7 @@ static int iavf_add_fdir_ethtool(struct iavf_adapter *adapter, struct ethtool_rx
adapter->aq_required |= IAVF_FLAG_AQ_ADD_FDIR_FILTER;
spin_unlock_bh(&adapter->fdir_fltr_lock);
- mod_delayed_work(iavf_wq, &adapter->watchdog_task, 0);
+ mod_delayed_work(adapter->wq, &adapter->watchdog_task, 0);
ret:
if (err && fltr)
@@ -1474,7 +1474,7 @@ static int iavf_del_fdir_ethtool(struct iavf_adapter *adapter, struct ethtool_rx
spin_unlock_bh(&adapter->fdir_fltr_lock);
if (fltr && fltr->state == IAVF_FDIR_FLTR_DEL_REQUEST)
- mod_delayed_work(iavf_wq, &adapter->watchdog_task, 0);
+ mod_delayed_work(adapter->wq, &adapter->watchdog_task, 0);
return err;
}
@@ -1658,7 +1658,7 @@ iavf_set_adv_rss_hash_opt(struct iavf_adapter *adapter,
spin_unlock_bh(&adapter->adv_rss_lock);
if (!err)
- mod_delayed_work(iavf_wq, &adapter->watchdog_task, 0);
+ mod_delayed_work(adapter->wq, &adapter->watchdog_task, 0);
mutex_unlock(&adapter->crit_lock);
diff --git a/drivers/net/ethernet/intel/iavf/iavf_main.c b/drivers/net/ethernet/intel/iavf/iavf_main.c
index c4e451ef79422a..4b09785d2147a4 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_main.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_main.c
@@ -49,7 +49,6 @@ MODULE_DESCRIPTION("Intel(R) Ethernet Adaptive Virtual Function Network Driver")
MODULE_LICENSE("GPL v2");
static const struct net_device_ops iavf_netdev_ops;
-struct workqueue_struct *iavf_wq;
int iavf_status_to_errno(enum iavf_status status)
{
@@ -277,7 +276,7 @@ void iavf_schedule_reset(struct iavf_adapter *adapter)
if (!(adapter->flags &
(IAVF_FLAG_RESET_PENDING | IAVF_FLAG_RESET_NEEDED))) {
adapter->flags |= IAVF_FLAG_RESET_NEEDED;
- queue_work(iavf_wq, &adapter->reset_task);
+ queue_work(adapter->wq, &adapter->reset_task);
}
}
@@ -291,7 +290,7 @@ void iavf_schedule_reset(struct iavf_adapter *adapter)
void iavf_schedule_request_stats(struct iavf_adapter *adapter)
{
adapter->aq_required |= IAVF_FLAG_AQ_REQUEST_STATS;
- mod_delayed_work(iavf_wq, &adapter->watchdog_task, 0);
+ mod_delayed_work(adapter->wq, &adapter->watchdog_task, 0);
}
/**
@@ -411,7 +410,7 @@ static irqreturn_t iavf_msix_aq(int irq, void *data)
if (adapter->state != __IAVF_REMOVE)
/* schedule work on the private workqueue */
- queue_work(iavf_wq, &adapter->adminq_task);
+ queue_work(adapter->wq, &adapter->adminq_task);
return IRQ_HANDLED;
}
@@ -1034,7 +1033,7 @@ int iavf_replace_primary_mac(struct iavf_adapter *adapter,
/* schedule the watchdog task to immediately process the request */
if (f) {
- queue_work(iavf_wq, &adapter->watchdog_task.work);
+ mod_delayed_work(adapter->wq, &adapter->watchdog_task, 0);
return 0;
}
return -ENOMEM;
@@ -1257,7 +1256,7 @@ static void iavf_up_complete(struct iavf_adapter *adapter)
adapter->aq_required |= IAVF_FLAG_AQ_ENABLE_QUEUES;
if (CLIENT_ENABLED(adapter))
adapter->flags |= IAVF_FLAG_CLIENT_NEEDS_OPEN;
- mod_delayed_work(iavf_wq, &adapter->watchdog_task, 0);
+ mod_delayed_work(adapter->wq, &adapter->watchdog_task, 0);
}
/**
@@ -1414,7 +1413,7 @@ void iavf_down(struct iavf_adapter *adapter)
adapter->aq_required |= IAVF_FLAG_AQ_DISABLE_QUEUES;
}
- mod_delayed_work(iavf_wq, &adapter->watchdog_task, 0);
+ mod_delayed_work(adapter->wq, &adapter->watchdog_task, 0);
}
/**
@@ -2248,7 +2247,7 @@ iavf_set_vlan_offload_features(struct iavf_adapter *adapter,
if (aq_required) {
adapter->aq_required |= aq_required;
- mod_delayed_work(iavf_wq, &adapter->watchdog_task, 0);
+ mod_delayed_work(adapter->wq, &adapter->watchdog_task, 0);
}
}
@@ -2693,6 +2692,15 @@ static void iavf_watchdog_task(struct work_struct *work)
goto restart_watchdog;
}
+ if ((adapter->flags & IAVF_FLAG_SETUP_NETDEV_FEATURES) &&
+ adapter->netdev_registered &&
+ !test_bit(__IAVF_IN_REMOVE_TASK, &adapter->crit_section) &&
+ rtnl_trylock()) {
+ netdev_update_features(adapter->netdev);
+ rtnl_unlock();
+ adapter->flags &= ~IAVF_FLAG_SETUP_NETDEV_FEATURES;
+ }
+
if (adapter->flags & IAVF_FLAG_PF_COMMS_FAILED)
iavf_change_state(adapter, __IAVF_COMM_FAILED);
@@ -2700,7 +2708,7 @@ static void iavf_watchdog_task(struct work_struct *work)
adapter->aq_required = 0;
adapter->current_op = VIRTCHNL_OP_UNKNOWN;
mutex_unlock(&adapter->crit_lock);
- queue_work(iavf_wq, &adapter->reset_task);
+ queue_work(adapter->wq, &adapter->reset_task);
return;
}
@@ -2708,31 +2716,31 @@ static void iavf_watchdog_task(struct work_struct *work)
case __IAVF_STARTUP:
iavf_startup(adapter);
mutex_unlock(&adapter->crit_lock);
- queue_delayed_work(iavf_wq, &adapter->watchdog_task,
+ queue_delayed_work(adapter->wq, &adapter->watchdog_task,
msecs_to_jiffies(30));
return;
case __IAVF_INIT_VERSION_CHECK:
iavf_init_version_check(adapter);
mutex_unlock(&adapter->crit_lock);
- queue_delayed_work(iavf_wq, &adapter->watchdog_task,
+ queue_delayed_work(adapter->wq, &adapter->watchdog_task,
msecs_to_jiffies(30));
return;
case __IAVF_INIT_GET_RESOURCES:
iavf_init_get_resources(adapter);
mutex_unlock(&adapter->crit_lock);
- queue_delayed_work(iavf_wq, &adapter->watchdog_task,
+ queue_delayed_work(adapter->wq, &adapter->watchdog_task,
msecs_to_jiffies(1));
return;
case __IAVF_INIT_EXTENDED_CAPS:
iavf_init_process_extended_caps(adapter);
mutex_unlock(&adapter->crit_lock);
- queue_delayed_work(iavf_wq, &adapter->watchdog_task,
+ queue_delayed_work(adapter->wq, &adapter->watchdog_task,
msecs_to_jiffies(1));
return;
case __IAVF_INIT_CONFIG_ADAPTER:
iavf_init_config_adapter(adapter);
mutex_unlock(&adapter->crit_lock);
- queue_delayed_work(iavf_wq, &adapter->watchdog_task,
+ queue_delayed_work(adapter->wq, &adapter->watchdog_task,
msecs_to_jiffies(1));
return;
case __IAVF_INIT_FAILED:
@@ -2751,14 +2759,14 @@ static void iavf_watchdog_task(struct work_struct *work)
adapter->flags |= IAVF_FLAG_PF_COMMS_FAILED;
iavf_shutdown_adminq(hw);
mutex_unlock(&adapter->crit_lock);
- queue_delayed_work(iavf_wq,
+ queue_delayed_work(adapter->wq,
&adapter->watchdog_task, (5 * HZ));
return;
}
/* Try again from failed step*/
iavf_change_state(adapter, adapter->last_state);
mutex_unlock(&adapter->crit_lock);
- queue_delayed_work(iavf_wq, &adapter->watchdog_task, HZ);
+ queue_delayed_work(adapter->wq, &adapter->watchdog_task, HZ);
return;
case __IAVF_COMM_FAILED:
if (test_bit(__IAVF_IN_REMOVE_TASK,
@@ -2789,13 +2797,14 @@ static void iavf_watchdog_task(struct work_struct *work)
adapter->aq_required = 0;
adapter->current_op = VIRTCHNL_OP_UNKNOWN;
mutex_unlock(&adapter->crit_lock);
- queue_delayed_work(iavf_wq,
+ queue_delayed_work(adapter->wq,
&adapter->watchdog_task,
msecs_to_jiffies(10));
return;
case __IAVF_RESETTING:
mutex_unlock(&adapter->crit_lock);
- queue_delayed_work(iavf_wq, &adapter->watchdog_task, HZ * 2);
+ queue_delayed_work(adapter->wq, &adapter->watchdog_task,
+ HZ * 2);
return;
case __IAVF_DOWN:
case __IAVF_DOWN_PENDING:
@@ -2834,9 +2843,9 @@ static void iavf_watchdog_task(struct work_struct *work)
adapter->aq_required = 0;
adapter->current_op = VIRTCHNL_OP_UNKNOWN;
dev_err(&adapter->pdev->dev, "Hardware reset detected\n");
- queue_work(iavf_wq, &adapter->reset_task);
+ queue_work(adapter->wq, &adapter->reset_task);
mutex_unlock(&adapter->crit_lock);
- queue_delayed_work(iavf_wq,
+ queue_delayed_work(adapter->wq,
&adapter->watchdog_task, HZ * 2);
return;
}
@@ -2845,12 +2854,13 @@ static void iavf_watchdog_task(struct work_struct *work)
mutex_unlock(&adapter->crit_lock);
restart_watchdog:
if (adapter->state >= __IAVF_DOWN)
- queue_work(iavf_wq, &adapter->adminq_task);
+ queue_work(adapter->wq, &adapter->adminq_task);
if (adapter->aq_required)
- queue_delayed_work(iavf_wq, &adapter->watchdog_task,
+ queue_delayed_work(adapter->wq, &adapter->watchdog_task,
msecs_to_jiffies(20));
else
- queue_delayed_work(iavf_wq, &adapter->watchdog_task, HZ * 2);
+ queue_delayed_work(adapter->wq, &adapter->watchdog_task,
+ HZ * 2);
}
/**
@@ -2952,7 +2962,7 @@ static void iavf_reset_task(struct work_struct *work)
*/
if (!mutex_trylock(&adapter->crit_lock)) {
if (adapter->state != __IAVF_REMOVE)
- queue_work(iavf_wq, &adapter->reset_task);
+ queue_work(adapter->wq, &adapter->reset_task);
goto reset_finish;
}
@@ -3116,7 +3126,7 @@ continue_reset:
bitmap_clear(adapter->vsi.active_cvlans, 0, VLAN_N_VID);
bitmap_clear(adapter->vsi.active_svlans, 0, VLAN_N_VID);
- mod_delayed_work(iavf_wq, &adapter->watchdog_task, 2);
+ mod_delayed_work(adapter->wq, &adapter->watchdog_task, 2);
/* We were running when the reset started, so we need to restore some
* state here.
@@ -3208,7 +3218,7 @@ static void iavf_adminq_task(struct work_struct *work)
if (adapter->state == __IAVF_REMOVE)
return;
- queue_work(iavf_wq, &adapter->adminq_task);
+ queue_work(adapter->wq, &adapter->adminq_task);
goto out;
}
@@ -3232,24 +3242,6 @@ static void iavf_adminq_task(struct work_struct *work)
} while (pending);
mutex_unlock(&adapter->crit_lock);
- if ((adapter->flags & IAVF_FLAG_SETUP_NETDEV_FEATURES)) {
- if (adapter->netdev_registered ||
- !test_bit(__IAVF_IN_REMOVE_TASK, &adapter->crit_section)) {
- struct net_device *netdev = adapter->netdev;
-
- rtnl_lock();
- netdev_update_features(netdev);
- rtnl_unlock();
- /* Request VLAN offload settings */
- if (VLAN_V2_ALLOWED(adapter))
- iavf_set_vlan_offload_features
- (adapter, 0, netdev->features);
-
- iavf_set_queue_vlan_tag_loc(adapter);
- }
-
- adapter->flags &= ~IAVF_FLAG_SETUP_NETDEV_FEATURES;
- }
if ((adapter->flags &
(IAVF_FLAG_RESET_PENDING | IAVF_FLAG_RESET_NEEDED)) ||
adapter->state == __IAVF_RESETTING)
@@ -3850,7 +3842,7 @@ static int iavf_parse_cls_flower(struct iavf_adapter *adapter,
field_flags |= IAVF_CLOUD_FIELD_IIP;
} else {
dev_err(&adapter->pdev->dev, "Bad ip src mask 0x%08x\n",
- be32_to_cpu(match.mask->dst));
+ be32_to_cpu(match.mask->src));
return -EINVAL;
}
}
@@ -4349,7 +4341,7 @@ static int iavf_change_mtu(struct net_device *netdev, int new_mtu)
if (netif_running(netdev)) {
adapter->flags |= IAVF_FLAG_RESET_NEEDED;
- queue_work(iavf_wq, &adapter->reset_task);
+ queue_work(adapter->wq, &adapter->reset_task);
}
return 0;
@@ -4898,6 +4890,13 @@ static int iavf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
hw = &adapter->hw;
hw->back = adapter;
+ adapter->wq = alloc_ordered_workqueue("%s", WQ_MEM_RECLAIM,
+ iavf_driver_name);
+ if (!adapter->wq) {
+ err = -ENOMEM;
+ goto err_alloc_wq;
+ }
+
adapter->msg_enable = BIT(DEFAULT_DEBUG_LEVEL_SHIFT) - 1;
iavf_change_state(adapter, __IAVF_STARTUP);
@@ -4942,7 +4941,7 @@ static int iavf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
INIT_WORK(&adapter->adminq_task, iavf_adminq_task);
INIT_DELAYED_WORK(&adapter->watchdog_task, iavf_watchdog_task);
INIT_DELAYED_WORK(&adapter->client_task, iavf_client_task);
- queue_delayed_work(iavf_wq, &adapter->watchdog_task,
+ queue_delayed_work(adapter->wq, &adapter->watchdog_task,
msecs_to_jiffies(5 * (pdev->devfn & 0x07)));
/* Setup the wait queue for indicating transition to down status */
@@ -4954,6 +4953,8 @@ static int iavf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
err_ioremap:
+ destroy_workqueue(adapter->wq);
+err_alloc_wq:
free_netdev(netdev);
err_alloc_etherdev:
pci_disable_pcie_error_reporting(pdev);
@@ -5023,7 +5024,7 @@ static int __maybe_unused iavf_resume(struct device *dev_d)
return err;
}
- queue_work(iavf_wq, &adapter->reset_task);
+ queue_work(adapter->wq, &adapter->reset_task);
netif_device_attach(adapter->netdev);
@@ -5170,6 +5171,8 @@ static void iavf_remove(struct pci_dev *pdev)
}
spin_unlock_bh(&adapter->adv_rss_lock);
+ destroy_workqueue(adapter->wq);
+
free_netdev(netdev);
pci_disable_pcie_error_reporting(pdev);
@@ -5196,24 +5199,11 @@ static struct pci_driver iavf_driver = {
**/
static int __init iavf_init_module(void)
{
- int ret;
-
pr_info("iavf: %s\n", iavf_driver_string);
pr_info("%s\n", iavf_copyright);
- iavf_wq = alloc_workqueue("%s", WQ_UNBOUND | WQ_MEM_RECLAIM, 1,
- iavf_driver_name);
- if (!iavf_wq) {
- pr_err("%s: Failed to create workqueue\n", iavf_driver_name);
- return -ENOMEM;
- }
-
- ret = pci_register_driver(&iavf_driver);
- if (ret)
- destroy_workqueue(iavf_wq);
-
- return ret;
+ return pci_register_driver(&iavf_driver);
}
module_init(iavf_init_module);
@@ -5227,7 +5217,6 @@ module_init(iavf_init_module);
static void __exit iavf_exit_module(void)
{
pci_unregister_driver(&iavf_driver);
- destroy_workqueue(iavf_wq);
}
module_exit(iavf_exit_module);
diff --git a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
index 24a701fd140e9d..365ca0c710c4a4 100644
--- a/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
+++ b/drivers/net/ethernet/intel/iavf/iavf_virtchnl.c
@@ -1952,7 +1952,7 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
if (!(adapter->flags & IAVF_FLAG_RESET_PENDING)) {
adapter->flags |= IAVF_FLAG_RESET_PENDING;
dev_info(&adapter->pdev->dev, "Scheduling reset task\n");
- queue_work(iavf_wq, &adapter->reset_task);
+ queue_work(adapter->wq, &adapter->reset_task);
}
break;
default:
@@ -2226,6 +2226,14 @@ void iavf_virtchnl_completion(struct iavf_adapter *adapter,
iavf_process_config(adapter);
adapter->flags |= IAVF_FLAG_SETUP_NETDEV_FEATURES;
+
+ /* Request VLAN offload settings */
+ if (VLAN_V2_ALLOWED(adapter))
+ iavf_set_vlan_offload_features(adapter, 0,
+ netdev->features);
+
+ iavf_set_queue_vlan_tag_loc(adapter);
+
was_mac_changed = !ether_addr_equal(netdev->dev_addr,
adapter->hw.mac.addr);
diff --git a/drivers/net/ethernet/intel/ice/ice.h b/drivers/net/ethernet/intel/ice/ice.h
index 2f0b604abc5e72..713069f809ec43 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -880,7 +880,7 @@ void ice_set_ethtool_repr_ops(struct net_device *netdev);
void ice_set_ethtool_safe_mode_ops(struct net_device *netdev);
u16 ice_get_avail_txq_count(struct ice_pf *pf);
u16 ice_get_avail_rxq_count(struct ice_pf *pf);
-int ice_vsi_recfg_qs(struct ice_vsi *vsi, int new_rx, int new_tx);
+int ice_vsi_recfg_qs(struct ice_vsi *vsi, int new_rx, int new_tx, bool locked);
void ice_update_vsi_stats(struct ice_vsi *vsi);
void ice_update_pf_stats(struct ice_pf *pf);
void
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
index d02b55b6aa9cf0..3e08847505ce1e 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -5524,7 +5524,7 @@ bool ice_fw_supports_report_dflt_cfg(struct ice_hw *hw)
* returned by the firmware is a 16 bit * value, but is indexed
* by [fls(speed) - 1]
*/
-static const u32 ice_aq_to_link_speed[15] = {
+static const u32 ice_aq_to_link_speed[] = {
SPEED_10, /* BIT(0) */
SPEED_100,
SPEED_1000,
@@ -5536,10 +5536,6 @@ static const u32 ice_aq_to_link_speed[15] = {
SPEED_40000,
SPEED_50000,
SPEED_100000, /* BIT(10) */
- 0,
- 0,
- 0,
- 0 /* BIT(14) */
};
/**
@@ -5550,5 +5546,8 @@ static const u32 ice_aq_to_link_speed[15] = {
*/
u32 ice_get_link_speed(u16 index)
{
+ if (index >= ARRAY_SIZE(ice_aq_to_link_speed))
+ return 0;
+
return ice_aq_to_link_speed[index];
}
diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
index 4f24d441c35e25..0a55c552189ab1 100644
--- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
@@ -441,7 +441,7 @@ int ice_pf_dcb_cfg(struct ice_pf *pf, struct ice_dcbx_cfg *new_cfg, bool locked)
goto out;
}
- ice_pf_dcb_recfg(pf);
+ ice_pf_dcb_recfg(pf, false);
out:
/* enable previously downed VSIs */
@@ -731,12 +731,13 @@ static int ice_dcb_noncontig_cfg(struct ice_pf *pf)
/**
* ice_pf_dcb_recfg - Reconfigure all VEBs and VSIs
* @pf: pointer to the PF struct
+ * @locked: is adev device lock held
*
* Assumed caller has already disabled all VSIs before
* calling this function. Reconfiguring DCB based on
* local_dcbx_cfg.
*/
-void ice_pf_dcb_recfg(struct ice_pf *pf)
+void ice_pf_dcb_recfg(struct ice_pf *pf, bool locked)
{
struct ice_dcbx_cfg *dcbcfg = &pf->hw.port_info->qos_cfg.local_dcbx_cfg;
struct iidc_event *event;
@@ -783,14 +784,16 @@ void ice_pf_dcb_recfg(struct ice_pf *pf)
if (vsi->type == ICE_VSI_PF)
ice_dcbnl_set_all(vsi);
}
- /* Notify the AUX drivers that TC change is finished */
- event = kzalloc(sizeof(*event), GFP_KERNEL);
- if (!event)
- return;
+ if (!locked) {
+ /* Notify the AUX drivers that TC change is finished */
+ event = kzalloc(sizeof(*event), GFP_KERNEL);
+ if (!event)
+ return;
- set_bit(IIDC_EVENT_AFTER_TC_CHANGE, event->type);
- ice_send_event_to_aux(pf, event);
- kfree(event);
+ set_bit(IIDC_EVENT_AFTER_TC_CHANGE, event->type);
+ ice_send_event_to_aux(pf, event);
+ kfree(event);
+ }
}
/**
@@ -1044,7 +1047,7 @@ ice_dcb_process_lldp_set_mib_change(struct ice_pf *pf,
}
/* changes in configuration update VSI */
- ice_pf_dcb_recfg(pf);
+ ice_pf_dcb_recfg(pf, false);
/* enable previously downed VSIs */
ice_dcb_ena_dis_vsi(pf, true, true);
diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.h b/drivers/net/ethernet/intel/ice/ice_dcb_lib.h
index 4c421c842a13fc..800879a88c5e7b 100644
--- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.h
+++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.h
@@ -23,7 +23,7 @@ u8 ice_dcb_get_tc(struct ice_vsi *vsi, int queue_index);
int
ice_pf_dcb_cfg(struct ice_pf *pf, struct ice_dcbx_cfg *new_cfg, bool locked);
int ice_dcb_bwchk(struct ice_pf *pf, struct ice_dcbx_cfg *dcbcfg);
-void ice_pf_dcb_recfg(struct ice_pf *pf);
+void ice_pf_dcb_recfg(struct ice_pf *pf, bool locked);
void ice_vsi_cfg_dcb_rings(struct ice_vsi *vsi);
int ice_init_pf_dcb(struct ice_pf *pf, bool locked);
void ice_update_dcb_stats(struct ice_pf *pf);
@@ -128,7 +128,7 @@ static inline u8 ice_get_pfc_mode(struct ice_pf *pf)
return 0;
}
-static inline void ice_pf_dcb_recfg(struct ice_pf *pf) { }
+static inline void ice_pf_dcb_recfg(struct ice_pf *pf, bool locked) { }
static inline void ice_vsi_cfg_dcb_rings(struct ice_vsi *vsi) { }
static inline void ice_update_dcb_stats(struct ice_pf *pf) { }
static inline void
diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c
index 4191994d8f3ae5..a359f1610fc196 100644
--- a/drivers/net/ethernet/intel/ice/ice_ethtool.c
+++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c
@@ -3641,7 +3641,9 @@ static int ice_set_channels(struct net_device *dev, struct ethtool_channels *ch)
struct ice_vsi *vsi = np->vsi;
struct ice_pf *pf = vsi->back;
int new_rx = 0, new_tx = 0;
+ bool locked = false;
u32 curr_combined;
+ int ret = 0;
/* do not support changing channels in Safe Mode */
if (ice_is_safe_mode(pf)) {
@@ -3705,15 +3707,33 @@ static int ice_set_channels(struct net_device *dev, struct ethtool_channels *ch)
return -EINVAL;
}
- ice_vsi_recfg_qs(vsi, new_rx, new_tx);
+ if (pf->adev) {
+ mutex_lock(&pf->adev_mutex);
+ device_lock(&pf->adev->dev);
+ locked = true;
+ if (pf->adev->dev.driver) {
+ netdev_err(dev, "Cannot change channels when RDMA is active\n");
+ ret = -EBUSY;
+ goto adev_unlock;
+ }
+ }
+
+ ice_vsi_recfg_qs(vsi, new_rx, new_tx, locked);
- if (!netif_is_rxfh_configured(dev))
- return ice_vsi_set_dflt_rss_lut(vsi, new_rx);
+ if (!netif_is_rxfh_configured(dev)) {
+ ret = ice_vsi_set_dflt_rss_lut(vsi, new_rx);
+ goto adev_unlock;
+ }
/* Update rss_size due to change in Rx queues */
vsi->rss_size = ice_get_valid_rss_size(&pf->hw, new_rx);
- return 0;
+adev_unlock:
+ if (locked) {
+ device_unlock(&pf->adev->dev);
+ mutex_unlock(&pf->adev_mutex);
+ }
+ return ret;
}
/**
diff --git a/drivers/net/ethernet/intel/ice/ice_gnss.c b/drivers/net/ethernet/intel/ice/ice_gnss.c
index b5a7f246d230fd..43e199b5b513b2 100644
--- a/drivers/net/ethernet/intel/ice/ice_gnss.c
+++ b/drivers/net/ethernet/intel/ice/ice_gnss.c
@@ -363,6 +363,7 @@ ice_gnss_tty_write(struct tty_struct *tty, const unsigned char *buf, int count)
/* Send the data out to a hardware port */
write_buf = kzalloc(sizeof(*write_buf), GFP_KERNEL);
if (!write_buf) {
+ kfree(cmd_buf);
err = -ENOMEM;
goto exit;
}
@@ -460,6 +461,9 @@ static struct tty_driver *ice_gnss_create_tty_driver(struct ice_pf *pf)
for (i = 0; i < ICE_GNSS_TTY_MINOR_DEVICES; i++) {
pf->gnss_tty_port[i] = kzalloc(sizeof(*pf->gnss_tty_port[i]),
GFP_KERNEL);
+ if (!pf->gnss_tty_port[i])
+ goto err_out;
+
pf->gnss_serial[i] = NULL;
tty_port_init(pf->gnss_tty_port[i]);
@@ -469,21 +473,23 @@ static struct tty_driver *ice_gnss_create_tty_driver(struct ice_pf *pf)
err = tty_register_driver(tty_driver);
if (err) {
dev_err(dev, "Failed to register TTY driver err=%d\n", err);
-
- for (i = 0; i < ICE_GNSS_TTY_MINOR_DEVICES; i++) {
- tty_port_destroy(pf->gnss_tty_port[i]);
- kfree(pf->gnss_tty_port[i]);
- }
- kfree(ttydrv_name);
- tty_driver_kref_put(pf->ice_gnss_tty_driver);
-
- return NULL;
+ goto err_out;
}
for (i = 0; i < ICE_GNSS_TTY_MINOR_DEVICES; i++)
dev_info(dev, "%s%d registered\n", ttydrv_name, i);
return tty_driver;
+
+err_out:
+ while (i--) {
+ tty_port_destroy(pf->gnss_tty_port[i]);
+ kfree(pf->gnss_tty_port[i]);
+ }
+ kfree(ttydrv_name);
+ tty_driver_kref_put(pf->ice_gnss_tty_driver);
+
+ return NULL;
}
/**
diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c b/drivers/net/ethernet/intel/ice/ice_lib.c
index 94aa834cd9a6bd..a596e07b3ce9db 100644
--- a/drivers/net/ethernet/intel/ice/ice_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_lib.c
@@ -3235,9 +3235,6 @@ int ice_vsi_release(struct ice_vsi *vsi)
}
}
- if (vsi->type == ICE_VSI_PF)
- ice_devlink_destroy_pf_port(pf);
-
if (vsi->type == ICE_VSI_VF &&
vsi->agg_node && vsi->agg_node->valid)
vsi->agg_node->num_vsis--;
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
index a9a7f8b52140b4..b288a01a321a08 100644
--- a/drivers/net/ethernet/intel/ice/ice_main.c
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
@@ -4195,12 +4195,13 @@ bool ice_is_wol_supported(struct ice_hw *hw)
* @vsi: VSI being changed
* @new_rx: new number of Rx queues
* @new_tx: new number of Tx queues
+ * @locked: is adev device_lock held
*
* Only change the number of queues if new_tx, or new_rx is non-0.
*
* Returns 0 on success.
*/
-int ice_vsi_recfg_qs(struct ice_vsi *vsi, int new_rx, int new_tx)
+int ice_vsi_recfg_qs(struct ice_vsi *vsi, int new_rx, int new_tx, bool locked)
{
struct ice_pf *pf = vsi->back;
int err = 0, timeout = 50;
@@ -4229,7 +4230,7 @@ int ice_vsi_recfg_qs(struct ice_vsi *vsi, int new_rx, int new_tx)
ice_vsi_close(vsi);
ice_vsi_rebuild(vsi, false);
- ice_pf_dcb_recfg(pf);
+ ice_pf_dcb_recfg(pf, locked);
ice_vsi_open(vsi);
done:
clear_bit(ICE_CFG_BUSY, pf->state);
@@ -4590,7 +4591,7 @@ static void ice_print_wake_reason(struct ice_pf *pf)
}
/**
- * ice_register_netdev - register netdev and devlink port
+ * ice_register_netdev - register netdev
* @pf: pointer to the PF struct
*/
static int ice_register_netdev(struct ice_pf *pf)
@@ -4602,11 +4603,6 @@ static int ice_register_netdev(struct ice_pf *pf)
if (!vsi || !vsi->netdev)
return -EIO;
- err = ice_devlink_create_pf_port(pf);
- if (err)
- goto err_devlink_create;
-
- SET_NETDEV_DEVLINK_PORT(vsi->netdev, &pf->devlink_port);
err = register_netdev(vsi->netdev);
if (err)
goto err_register_netdev;
@@ -4617,8 +4613,6 @@ static int ice_register_netdev(struct ice_pf *pf)
return 0;
err_register_netdev:
- ice_devlink_destroy_pf_port(pf);
-err_devlink_create:
free_netdev(vsi->netdev);
vsi->netdev = NULL;
clear_bit(ICE_VSI_NETDEV_ALLOCD, vsi->state);
@@ -4636,6 +4630,7 @@ static int
ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
{
struct device *dev = &pdev->dev;
+ struct ice_vsi *vsi;
struct ice_pf *pf;
struct ice_hw *hw;
int i, err;
@@ -4918,6 +4913,18 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
pcie_print_link_status(pf->pdev);
probe_done:
+ err = ice_devlink_create_pf_port(pf);
+ if (err)
+ goto err_create_pf_port;
+
+ vsi = ice_get_main_vsi(pf);
+ if (!vsi || !vsi->netdev) {
+ err = -EINVAL;
+ goto err_netdev_reg;
+ }
+
+ SET_NETDEV_DEVLINK_PORT(vsi->netdev, &pf->devlink_port);
+
err = ice_register_netdev(pf);
if (err)
goto err_netdev_reg;
@@ -4955,6 +4962,8 @@ err_init_aux_unroll:
err_devlink_reg_param:
ice_devlink_unregister_params(pf);
err_netdev_reg:
+ ice_devlink_destroy_pf_port(pf);
+err_create_pf_port:
err_send_version_unroll:
ice_vsi_release_all(pf);
err_alloc_sw_unroll:
@@ -5083,6 +5092,7 @@ static void ice_remove(struct pci_dev *pdev)
ice_setup_mc_magic_wake(pf);
ice_vsi_release_all(pf);
mutex_destroy(&(&pf->hw)->fdir_fltr_lock);
+ ice_devlink_destroy_pf_port(pf);
ice_set_wake(pf);
ice_free_irq_msix_misc(pf);
ice_for_each_vsi(pf, i) {
@@ -5531,7 +5541,7 @@ static int __init ice_module_init(void)
pr_info("%s\n", ice_driver_string);
pr_info("%s\n", ice_copyright);
- ice_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, KBUILD_MODNAME);
+ ice_wq = alloc_workqueue("%s", 0, 0, KBUILD_MODNAME);
if (!ice_wq) {
pr_err("Failed to create workqueue\n");
return -ENOMEM;
diff --git a/drivers/net/ethernet/intel/ice/ice_switch.c b/drivers/net/ethernet/intel/ice/ice_switch.c
index 9b762f7972ce5e..61f844d2251235 100644
--- a/drivers/net/ethernet/intel/ice/ice_switch.c
+++ b/drivers/net/ethernet/intel/ice/ice_switch.c
@@ -5420,7 +5420,7 @@ ice_add_adv_recipe(struct ice_hw *hw, struct ice_adv_lkup_elem *lkups,
*/
status = ice_add_special_words(rinfo, lkup_exts, ice_is_dvm_ena(hw));
if (status)
- goto err_free_lkup_exts;
+ goto err_unroll;
/* Group match words into recipes using preferred recipe grouping
* criteria.
diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.c b/drivers/net/ethernet/intel/ice/ice_tc_lib.c
index faba0f857cd9f8..95f392ab96708a 100644
--- a/drivers/net/ethernet/intel/ice/ice_tc_lib.c
+++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.c
@@ -1681,7 +1681,7 @@ ice_tc_forward_to_queue(struct ice_vsi *vsi, struct ice_tc_flower_fltr *fltr,
struct ice_vsi *ch_vsi = NULL;
u16 queue = act->rx_queue;
- if (queue > vsi->num_rxq) {
+ if (queue >= vsi->num_rxq) {
NL_SET_ERR_MSG_MOD(fltr->extack,
"Unable to add filter because specified queue is invalid");
return -EINVAL;
diff --git a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c
index d4a4001b6e5dd2..f56fa94ff3d0d1 100644
--- a/drivers/net/ethernet/intel/ice/ice_vf_mbx.c
+++ b/drivers/net/ethernet/intel/ice/ice_vf_mbx.c
@@ -39,7 +39,7 @@ ice_aq_send_msg_to_vf(struct ice_hw *hw, u16 vfid, u32 v_opcode, u32 v_retval,
return ice_sq_send_cmd(hw, &hw->mailboxq, &desc, msg, msglen, cd);
}
-static const u32 ice_legacy_aq_to_vc_speed[15] = {
+static const u32 ice_legacy_aq_to_vc_speed[] = {
VIRTCHNL_LINK_SPEED_100MB, /* BIT(0) */
VIRTCHNL_LINK_SPEED_100MB,
VIRTCHNL_LINK_SPEED_1GB,
@@ -51,10 +51,6 @@ static const u32 ice_legacy_aq_to_vc_speed[15] = {
VIRTCHNL_LINK_SPEED_40GB,
VIRTCHNL_LINK_SPEED_40GB,
VIRTCHNL_LINK_SPEED_40GB,
- VIRTCHNL_LINK_SPEED_UNKNOWN,
- VIRTCHNL_LINK_SPEED_UNKNOWN,
- VIRTCHNL_LINK_SPEED_UNKNOWN,
- VIRTCHNL_LINK_SPEED_UNKNOWN /* BIT(14) */
};
/**
@@ -71,21 +67,20 @@ static const u32 ice_legacy_aq_to_vc_speed[15] = {
*/
u32 ice_conv_link_speed_to_virtchnl(bool adv_link_support, u16 link_speed)
{
- u32 speed;
+ /* convert a BIT() value into an array index */
+ u32 index = fls(link_speed) - 1;
- if (adv_link_support) {
- /* convert a BIT() value into an array index */
- speed = ice_get_link_speed(fls(link_speed) - 1);
- } else {
+ if (adv_link_support)
+ return ice_get_link_speed(index);
+ else if (index < ARRAY_SIZE(ice_legacy_aq_to_vc_speed))
/* Virtchnl speeds are not defined for every speed supported in
* the hardware. To maintain compatibility with older AVF
* drivers, while reporting the speed the new speed values are
* resolved to the closest known virtchnl speeds
*/
- speed = ice_legacy_aq_to_vc_speed[fls(link_speed) - 1];
- }
+ return ice_legacy_aq_to_vc_speed[index];
- return speed;
+ return VIRTCHNL_LINK_SPEED_UNKNOWN;
}
/* The mailbox overflow detection algorithm helps to check if there
diff --git a/drivers/net/ethernet/intel/ice/ice_vf_vsi_vlan_ops.c b/drivers/net/ethernet/intel/ice/ice_vf_vsi_vlan_ops.c
index 5ecc0ee9a78e0f..b1ffb81893d48e 100644
--- a/drivers/net/ethernet/intel/ice/ice_vf_vsi_vlan_ops.c
+++ b/drivers/net/ethernet/intel/ice/ice_vf_vsi_vlan_ops.c
@@ -44,13 +44,17 @@ void ice_vf_vsi_init_vlan_ops(struct ice_vsi *vsi)
/* outer VLAN ops regardless of port VLAN config */
vlan_ops->add_vlan = ice_vsi_add_vlan;
- vlan_ops->dis_rx_filtering = ice_vsi_dis_rx_vlan_filtering;
vlan_ops->ena_tx_filtering = ice_vsi_ena_tx_vlan_filtering;
vlan_ops->dis_tx_filtering = ice_vsi_dis_tx_vlan_filtering;
if (ice_vf_is_port_vlan_ena(vf)) {
/* setup outer VLAN ops */
vlan_ops->set_port_vlan = ice_vsi_set_outer_port_vlan;
+ /* all Rx traffic should be in the domain of the
+ * assigned port VLAN, so prevent disabling Rx VLAN
+ * filtering
+ */
+ vlan_ops->dis_rx_filtering = noop_vlan;
vlan_ops->ena_rx_filtering =
ice_vsi_ena_rx_vlan_filtering;
@@ -63,6 +67,9 @@ void ice_vf_vsi_init_vlan_ops(struct ice_vsi *vsi)
vlan_ops->ena_insertion = ice_vsi_ena_inner_insertion;
vlan_ops->dis_insertion = ice_vsi_dis_inner_insertion;
} else {
+ vlan_ops->dis_rx_filtering =
+ ice_vsi_dis_rx_vlan_filtering;
+
if (!test_bit(ICE_FLAG_VF_VLAN_PRUNING, pf->flags))
vlan_ops->ena_rx_filtering = noop_vlan;
else
@@ -96,7 +103,14 @@ void ice_vf_vsi_init_vlan_ops(struct ice_vsi *vsi)
vlan_ops->set_port_vlan = ice_vsi_set_inner_port_vlan;
vlan_ops->ena_rx_filtering =
ice_vsi_ena_rx_vlan_filtering;
+ /* all Rx traffic should be in the domain of the
+ * assigned port VLAN, so prevent disabling Rx VLAN
+ * filtering
+ */
+ vlan_ops->dis_rx_filtering = noop_vlan;
} else {
+ vlan_ops->dis_rx_filtering =
+ ice_vsi_dis_rx_vlan_filtering;
if (!test_bit(ICE_FLAG_VF_VLAN_PRUNING, pf->flags))
vlan_ops->ena_rx_filtering = noop_vlan;
else
diff --git a/drivers/net/ethernet/intel/ice/ice_xsk.c b/drivers/net/ethernet/intel/ice/ice_xsk.c
index 907055b77af0e0..7105de6fb3444b 100644
--- a/drivers/net/ethernet/intel/ice/ice_xsk.c
+++ b/drivers/net/ethernet/intel/ice/ice_xsk.c
@@ -783,7 +783,7 @@ construct_skb:
static void
ice_clean_xdp_tx_buf(struct ice_tx_ring *xdp_ring, struct ice_tx_buf *tx_buf)
{
- xdp_return_frame((struct xdp_frame *)tx_buf->raw_buf);
+ page_frag_free(tx_buf->raw_buf);
xdp_ring->xdp_tx_active--;
dma_unmap_single(xdp_ring->dev, dma_unmap_addr(tx_buf, dma),
dma_unmap_len(tx_buf, len), DMA_TO_DEVICE);
diff --git a/drivers/net/ethernet/intel/igc/igc_defines.h b/drivers/net/ethernet/intel/igc/igc_defines.h
index a7b22639cfcd9b..e9747ec5ac0b83 100644
--- a/drivers/net/ethernet/intel/igc/igc_defines.h
+++ b/drivers/net/ethernet/intel/igc/igc_defines.h
@@ -475,7 +475,9 @@
#define IGC_TSAUXC_EN_TT0 BIT(0) /* Enable target time 0. */
#define IGC_TSAUXC_EN_TT1 BIT(1) /* Enable target time 1. */
#define IGC_TSAUXC_EN_CLK0 BIT(2) /* Enable Configurable Frequency Clock 0. */
+#define IGC_TSAUXC_ST0 BIT(4) /* Start Clock 0 Toggle on Target Time 0. */
#define IGC_TSAUXC_EN_CLK1 BIT(5) /* Enable Configurable Frequency Clock 1. */
+#define IGC_TSAUXC_ST1 BIT(7) /* Start Clock 1 Toggle on Target Time 1. */
#define IGC_TSAUXC_EN_TS0 BIT(8) /* Enable hardware timestamp 0. */
#define IGC_TSAUXC_AUTT0 BIT(9) /* Auxiliary Timestamp Taken. */
#define IGC_TSAUXC_EN_TS1 BIT(10) /* Enable hardware timestamp 0. */
diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
index 44b1740dc09834..1dd2a7fee8d46d 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -2942,7 +2942,9 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget)
if (tx_buffer->next_to_watch &&
time_after(jiffies, tx_buffer->time_stamp +
(adapter->tx_timeout_factor * HZ)) &&
- !(rd32(IGC_STATUS) & IGC_STATUS_TXOFF)) {
+ !(rd32(IGC_STATUS) & IGC_STATUS_TXOFF) &&
+ (rd32(IGC_TDH(tx_ring->reg_idx)) !=
+ readl(tx_ring->tail))) {
/* detected Tx unit hang */
netdev_err(tx_ring->netdev,
"Detected Tx Unit Hang\n"
@@ -5069,6 +5071,24 @@ static int igc_change_mtu(struct net_device *netdev, int new_mtu)
}
/**
+ * igc_tx_timeout - Respond to a Tx Hang
+ * @netdev: network interface device structure
+ * @txqueue: queue number that timed out
+ **/
+static void igc_tx_timeout(struct net_device *netdev,
+ unsigned int __always_unused txqueue)
+{
+ struct igc_adapter *adapter = netdev_priv(netdev);
+ struct igc_hw *hw = &adapter->hw;
+
+ /* Do the reset outside of interrupt context */
+ adapter->tx_timeout_count++;
+ schedule_work(&adapter->reset_task);
+ wr32(IGC_EICS,
+ (adapter->eims_enable_mask & ~adapter->eims_other));
+}
+
+/**
* igc_get_stats64 - Get System Network Statistics
* @netdev: network interface device structure
* @stats: rtnl_link_stats64 pointer
@@ -5495,7 +5515,7 @@ static void igc_watchdog_task(struct work_struct *work)
case SPEED_100:
case SPEED_1000:
case SPEED_2500:
- adapter->tx_timeout_factor = 7;
+ adapter->tx_timeout_factor = 1;
break;
}
@@ -6320,6 +6340,7 @@ static const struct net_device_ops igc_netdev_ops = {
.ndo_set_rx_mode = igc_set_rx_mode,
.ndo_set_mac_address = igc_set_mac,
.ndo_change_mtu = igc_change_mtu,
+ .ndo_tx_timeout = igc_tx_timeout,
.ndo_get_stats64 = igc_get_stats64,
.ndo_fix_features = igc_fix_features,
.ndo_set_features = igc_set_features,
diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c
index 8dbb9f903ca70f..4e10ced736dbb5 100644
--- a/drivers/net/ethernet/intel/igc/igc_ptp.c
+++ b/drivers/net/ethernet/intel/igc/igc_ptp.c
@@ -322,7 +322,7 @@ static int igc_ptp_feature_enable_i225(struct ptp_clock_info *ptp,
ts = ns_to_timespec64(ns);
if (rq->perout.index == 1) {
if (use_freq) {
- tsauxc_mask = IGC_TSAUXC_EN_CLK1;
+ tsauxc_mask = IGC_TSAUXC_EN_CLK1 | IGC_TSAUXC_ST1;
tsim_mask = 0;
} else {
tsauxc_mask = IGC_TSAUXC_EN_TT1;
@@ -333,7 +333,7 @@ static int igc_ptp_feature_enable_i225(struct ptp_clock_info *ptp,
freqout = IGC_FREQOUT1;
} else {
if (use_freq) {
- tsauxc_mask = IGC_TSAUXC_EN_CLK0;
+ tsauxc_mask = IGC_TSAUXC_EN_CLK0 | IGC_TSAUXC_ST0;
tsim_mask = 0;
} else {
tsauxc_mask = IGC_TSAUXC_EN_TT0;
@@ -347,10 +347,12 @@ static int igc_ptp_feature_enable_i225(struct ptp_clock_info *ptp,
tsauxc = rd32(IGC_TSAUXC);
tsim = rd32(IGC_TSIM);
if (rq->perout.index == 1) {
- tsauxc &= ~(IGC_TSAUXC_EN_TT1 | IGC_TSAUXC_EN_CLK1);
+ tsauxc &= ~(IGC_TSAUXC_EN_TT1 | IGC_TSAUXC_EN_CLK1 |
+ IGC_TSAUXC_ST1);
tsim &= ~IGC_TSICR_TT1;
} else {
- tsauxc &= ~(IGC_TSAUXC_EN_TT0 | IGC_TSAUXC_EN_CLK0);
+ tsauxc &= ~(IGC_TSAUXC_EN_TT0 | IGC_TSAUXC_EN_CLK0 |
+ IGC_TSAUXC_ST0);
tsim &= ~IGC_TSICR_TT0;
}
if (on) {
@@ -415,10 +417,12 @@ static int igc_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
*
* We need to convert the system time value stored in the RX/TXSTMP registers
* into a hwtstamp which can be used by the upper level timestamping functions.
+ *
+ * Returns 0 on success.
**/
-static void igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter,
- struct skb_shared_hwtstamps *hwtstamps,
- u64 systim)
+static int igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter,
+ struct skb_shared_hwtstamps *hwtstamps,
+ u64 systim)
{
switch (adapter->hw.mac.type) {
case igc_i225:
@@ -428,8 +432,9 @@ static void igc_ptp_systim_to_hwtstamp(struct igc_adapter *adapter,
systim & 0xFFFFFFFF);
break;
default:
- break;
+ return -EINVAL;
}
+ return 0;
}
/**
@@ -650,7 +655,8 @@ static void igc_ptp_tx_hwtstamp(struct igc_adapter *adapter)
regval = rd32(IGC_TXSTMPL);
regval |= (u64)rd32(IGC_TXSTMPH) << 32;
- igc_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval);
+ if (igc_ptp_systim_to_hwtstamp(adapter, &shhwtstamps, regval))
+ return;
switch (adapter->link_speed) {
case SPEED_10:
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
index 24aa97f993ca18..123dca9ce4683f 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.c
@@ -855,9 +855,11 @@ static struct pci_dev *ixgbe_get_first_secondary_devfn(unsigned int devfn)
rp_pdev = pci_get_domain_bus_and_slot(0, 0, devfn);
if (rp_pdev && rp_pdev->subordinate) {
bus = rp_pdev->subordinate->number;
+ pci_dev_put(rp_pdev);
return pci_get_domain_bus_and_slot(0, bus, 0);
}
+ pci_dev_put(rp_pdev);
return NULL;
}
@@ -874,6 +876,7 @@ static bool ixgbe_x550em_a_has_mii(struct ixgbe_hw *hw)
struct ixgbe_adapter *adapter = hw->back;
struct pci_dev *pdev = adapter->pdev;
struct pci_dev *func0_pdev;
+ bool has_mii = false;
/* For the C3000 family of SoCs (x550em_a) the internal ixgbe devices
* are always downstream of root ports @ 0000:00:16.0 & 0000:00:17.0
@@ -884,15 +887,16 @@ static bool ixgbe_x550em_a_has_mii(struct ixgbe_hw *hw)
func0_pdev = ixgbe_get_first_secondary_devfn(PCI_DEVFN(0x16, 0));
if (func0_pdev) {
if (func0_pdev == pdev)
- return true;
- else
- return false;
+ has_mii = true;
+ goto out;
}
func0_pdev = ixgbe_get_first_secondary_devfn(PCI_DEVFN(0x17, 0));
if (func0_pdev == pdev)
- return true;
+ has_mii = true;
- return false;
+out:
+ pci_dev_put(func0_pdev);
+ return has_mii;
}
/**
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
index b2b71fe80d61c2..724df6398bbe28 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
@@ -774,9 +774,9 @@ int cgx_lmac_rx_tx_enable(void *cgxd, int lmac_id, bool enable)
cfg = cgx_read(cgx, lmac_id, CGXX_CMRX_CFG);
if (enable)
- cfg |= CMR_EN | DATA_PKT_RX_EN | DATA_PKT_TX_EN;
+ cfg |= DATA_PKT_RX_EN | DATA_PKT_TX_EN;
else
- cfg &= ~(CMR_EN | DATA_PKT_RX_EN | DATA_PKT_TX_EN);
+ cfg &= ~(DATA_PKT_RX_EN | DATA_PKT_TX_EN);
cgx_write(cgx, lmac_id, CGXX_CMRX_CFG, cfg);
return 0;
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h
index fb2d37676d84e2..5a20d93004c718 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h
@@ -26,7 +26,6 @@
#define CMR_P2X_SEL_SHIFT 59ULL
#define CMR_P2X_SEL_NIX0 1ULL
#define CMR_P2X_SEL_NIX1 2ULL
-#define CMR_EN BIT_ULL(55)
#define DATA_PKT_TX_EN BIT_ULL(53)
#define DATA_PKT_RX_EN BIT_ULL(54)
#define CGX_LMAC_TYPE_SHIFT 40
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c b/drivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c
index fa8029a940689e..eb25e458266ca7 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/mcs_rvu_if.c
@@ -589,7 +589,7 @@ int rvu_mbox_handler_mcs_free_resources(struct rvu *rvu,
u16 pcifunc = req->hdr.pcifunc;
struct mcs_rsrc_map *map;
struct mcs *mcs;
- int rc;
+ int rc = 0;
if (req->mcs_id >= rvu->mcs_blk_cnt)
return MCS_AF_ERR_INVALID_MCSID;
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
index bda1a6fa2ec43c..e4407f09c9d34b 100644
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c
@@ -1500,6 +1500,9 @@ static const struct devlink_param rvu_af_dl_params[] = {
BIT(DEVLINK_PARAM_CMODE_RUNTIME),
rvu_af_dl_dwrr_mtu_get, rvu_af_dl_dwrr_mtu_set,
rvu_af_dl_dwrr_mtu_validate),
+};
+
+static const struct devlink_param rvu_af_dl_param_exact_match[] = {
DEVLINK_PARAM_DRIVER(RVU_AF_DEVLINK_PARAM_ID_NPC_EXACT_FEATURE_DISABLE,
"npc_exact_feature_disable", DEVLINK_PARAM_TYPE_STRING,
BIT(DEVLINK_PARAM_CMODE_RUNTIME),
@@ -1556,7 +1559,6 @@ int rvu_register_dl(struct rvu *rvu)
{
struct rvu_devlink *rvu_dl;
struct devlink *dl;
- size_t size;
int err;
dl = devlink_alloc(&rvu_devlink_ops, sizeof(struct rvu_devlink),
@@ -1578,21 +1580,32 @@ int rvu_register_dl(struct rvu *rvu)
goto err_dl_health;
}
+ err = devlink_params_register(dl, rvu_af_dl_params, ARRAY_SIZE(rvu_af_dl_params));
+ if (err) {
+ dev_err(rvu->dev,
+ "devlink params register failed with error %d", err);
+ goto err_dl_health;
+ }
+
/* Register exact match devlink only for CN10K-B */
- size = ARRAY_SIZE(rvu_af_dl_params);
if (!rvu_npc_exact_has_match_table(rvu))
- size -= 1;
+ goto done;
- err = devlink_params_register(dl, rvu_af_dl_params, size);
+ err = devlink_params_register(dl, rvu_af_dl_param_exact_match,
+ ARRAY_SIZE(rvu_af_dl_param_exact_match));
if (err) {
dev_err(rvu->dev,
- "devlink params register failed with error %d", err);
- goto err_dl_health;
+ "devlink exact match params register failed with error %d", err);
+ goto err_dl_exact_match;
}
+done:
devlink_register(dl);
return 0;
+err_dl_exact_match:
+ devlink_params_unregister(dl, rvu_af_dl_params, ARRAY_SIZE(rvu_af_dl_params));
+
err_dl_health:
rvu_health_reporters_destroy(rvu);
devlink_free(dl);
@@ -1605,8 +1618,14 @@ void rvu_unregister_dl(struct rvu *rvu)
struct devlink *dl = rvu_dl->dl;
devlink_unregister(dl);
- devlink_params_unregister(dl, rvu_af_dl_params,
- ARRAY_SIZE(rvu_af_dl_params));
+
+ devlink_params_unregister(dl, rvu_af_dl_params, ARRAY_SIZE(rvu_af_dl_params));
+
+ /* Unregister exact match devlink only for CN10K-B */
+ if (rvu_npc_exact_has_match_table(rvu))
+ devlink_params_unregister(dl, rvu_af_dl_param_exact_match,
+ ARRAY_SIZE(rvu_af_dl_param_exact_match));
+
rvu_health_reporters_destroy(rvu);
devlink_free(dl);
}
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
index 9e10e7471b8874..8a41ad8ca04f11 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c
@@ -1376,18 +1376,23 @@ int otx2_sq_aura_pool_init(struct otx2_nic *pfvf)
sq = &qset->sq[qidx];
sq->sqb_count = 0;
sq->sqb_ptrs = kcalloc(num_sqbs, sizeof(*sq->sqb_ptrs), GFP_KERNEL);
- if (!sq->sqb_ptrs)
- return -ENOMEM;
+ if (!sq->sqb_ptrs) {
+ err = -ENOMEM;
+ goto err_mem;
+ }
for (ptr = 0; ptr < num_sqbs; ptr++) {
- if (otx2_alloc_rbuf(pfvf, pool, &bufptr))
- return -ENOMEM;
+ err = otx2_alloc_rbuf(pfvf, pool, &bufptr);
+ if (err)
+ goto err_mem;
pfvf->hw_ops->aura_freeptr(pfvf, pool_id, bufptr);
sq->sqb_ptrs[sq->sqb_count++] = (u64)bufptr;
}
}
- return 0;
+err_mem:
+ return err ? -ENOMEM : 0;
+
fail:
otx2_mbox_reset(&pfvf->mbox.mbox, 0);
otx2_aura_pool_free(pfvf);
@@ -1430,13 +1435,13 @@ int otx2_rq_aura_pool_init(struct otx2_nic *pfvf)
for (pool_id = 0; pool_id < hw->rqpool_cnt; pool_id++) {
pool = &pfvf->qset.pool[pool_id];
for (ptr = 0; ptr < num_ptrs; ptr++) {
- if (otx2_alloc_rbuf(pfvf, pool, &bufptr))
+ err = otx2_alloc_rbuf(pfvf, pool, &bufptr);
+ if (err)
return -ENOMEM;
pfvf->hw_ops->aura_freeptr(pfvf, pool_id,
bufptr + OTX2_HEAD_ROOM);
}
}
-
return 0;
fail:
otx2_mbox_reset(&pfvf->mbox.mbox, 0);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
index 5bee3c3a7ce43f..3d22cc6a2804a8 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h
@@ -736,8 +736,10 @@ static inline void cn10k_aura_freeptr(void *dev, int aura, u64 buf)
u64 ptrs[2];
ptrs[1] = buf;
+ get_cpu();
/* Free only one buffer at time during init and teardown */
__cn10k_aura_freeptr(pfvf, aura, ptrs, 2);
+ put_cpu();
}
/* Alloc pointer from pool/aura */
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c
index 86653bb8e403a0..7f8ffbf79cf742 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_vf.c
@@ -758,6 +758,8 @@ static void otx2vf_remove(struct pci_dev *pdev)
if (vf->otx2_wq)
destroy_workqueue(vf->otx2_wq);
otx2_ptp_destroy(vf);
+ otx2_mcam_flow_del(vf);
+ otx2_shutdown_tc(vf);
otx2vf_disable_mbox_intr(vf);
otx2_detach_resources(&vf->mbox);
if (test_bit(CN10K_LMTST, &vf->hw.cap_flag))
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index e3de9a53b2d9aa..e3123723522e32 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1570,8 +1570,8 @@ static struct page_pool *mtk_create_page_pool(struct mtk_eth *eth,
if (IS_ERR(pp))
return pp;
- err = __xdp_rxq_info_reg(xdp_q, &eth->dummy_dev, eth->rx_napi.napi_id,
- id, PAGE_SIZE);
+ err = __xdp_rxq_info_reg(xdp_q, &eth->dummy_dev, id,
+ eth->rx_napi.napi_id, PAGE_SIZE);
if (err < 0)
goto err_free_pp;
@@ -1870,7 +1870,9 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
while (done < budget) {
unsigned int pktlen, *rxdcsum;
+ bool has_hwaccel_tag = false;
struct net_device *netdev;
+ u16 vlan_proto, vlan_tci;
dma_addr_t dma_addr;
u32 hash, reason;
int mac = 0;
@@ -2010,27 +2012,29 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) {
if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
- if (trxd.rxd3 & RX_DMA_VTAG_V2)
- __vlan_hwaccel_put_tag(skb,
- htons(RX_DMA_VPID(trxd.rxd4)),
- RX_DMA_VID(trxd.rxd4));
+ if (trxd.rxd3 & RX_DMA_VTAG_V2) {
+ vlan_proto = RX_DMA_VPID(trxd.rxd4);
+ vlan_tci = RX_DMA_VID(trxd.rxd4);
+ has_hwaccel_tag = true;
+ }
} else if (trxd.rxd2 & RX_DMA_VTAG) {
- __vlan_hwaccel_put_tag(skb, htons(RX_DMA_VPID(trxd.rxd3)),
- RX_DMA_VID(trxd.rxd3));
+ vlan_proto = RX_DMA_VPID(trxd.rxd3);
+ vlan_tci = RX_DMA_VID(trxd.rxd3);
+ has_hwaccel_tag = true;
}
}
/* When using VLAN untagging in combination with DSA, the
* hardware treats the MTK special tag as a VLAN and untags it.
*/
- if (skb_vlan_tag_present(skb) && netdev_uses_dsa(netdev)) {
- unsigned int port = ntohs(skb->vlan_proto) & GENMASK(2, 0);
+ if (has_hwaccel_tag && netdev_uses_dsa(netdev)) {
+ unsigned int port = vlan_proto & GENMASK(2, 0);
if (port < ARRAY_SIZE(eth->dsa_meta) &&
eth->dsa_meta[port])
skb_dst_set_noref(skb, &eth->dsa_meta[port]->dst);
-
- __vlan_hwaccel_clear_tag(skb);
+ } else if (has_hwaccel_tag) {
+ __vlan_hwaccel_put_tag(skb, htons(vlan_proto), vlan_tci);
}
skb_record_rx_queue(skb, 0);
@@ -3111,7 +3115,7 @@ static void mtk_gdm_config(struct mtk_eth *eth, u32 config)
val |= config;
- if (!i && eth->netdev[0] && netdev_uses_dsa(eth->netdev[0]))
+ if (eth->netdev[i] && netdev_uses_dsa(eth->netdev[i]))
val |= MTK_GDMA_SPECIAL_TAG;
mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i));
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 18a50529ce7b49..2d9186d32bc093 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -519,7 +519,7 @@
#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1)
#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2)
-#define SGMII_DUPLEX_FULL BIT(4)
+#define SGMII_DUPLEX_HALF BIT(4)
#define SGMII_IF_MODE_BIT5 BIT(5)
#define SGMII_REMOTE_FAULT_DIS BIT(8)
#define SGMII_CODE_SYNC_SET_VAL BIT(9)
@@ -1036,11 +1036,13 @@ struct mtk_soc_data {
* @regmap: The register map pointing at the range used to setup
* SGMII modes
* @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
+ * @interface: Currently configured interface mode
* @pcs: Phylink PCS structure
*/
struct mtk_pcs {
struct regmap *regmap;
u32 ana_rgc3;
+ phy_interface_t interface;
struct phylink_pcs pcs;
};
diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.c b/drivers/net/ethernet/mediatek/mtk_ppe.c
index 269208a841c763..1ff024f42444b5 100644
--- a/drivers/net/ethernet/mediatek/mtk_ppe.c
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.c
@@ -615,8 +615,7 @@ mtk_foe_entry_commit_subflow(struct mtk_ppe *ppe, struct mtk_flow_entry *entry,
u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP;
int type;
- flow_info = kzalloc(offsetof(struct mtk_flow_entry, l2_data.end),
- GFP_ATOMIC);
+ flow_info = kzalloc(sizeof(*flow_info), GFP_ATOMIC);
if (!flow_info)
return;
diff --git a/drivers/net/ethernet/mediatek/mtk_ppe.h b/drivers/net/ethernet/mediatek/mtk_ppe.h
index ea64fac1d425aa..b5e432031340bf 100644
--- a/drivers/net/ethernet/mediatek/mtk_ppe.h
+++ b/drivers/net/ethernet/mediatek/mtk_ppe.h
@@ -279,7 +279,6 @@ struct mtk_flow_entry {
struct {
struct mtk_flow_entry *base_flow;
struct hlist_node list;
- struct {} end;
} l2_data;
};
struct rhash_head node;
diff --git a/drivers/net/ethernet/mediatek/mtk_sgmii.c b/drivers/net/ethernet/mediatek/mtk_sgmii.c
index 5c286f2c9418f4..bb00de1003ac43 100644
--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c
+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c
@@ -43,11 +43,6 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
int advertise, link_timer;
bool changed, use_an;
- if (interface == PHY_INTERFACE_MODE_2500BASEX)
- rgc3 = RG_PHY_SPEED_3_125G;
- else
- rgc3 = 0;
-
advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
advertising);
if (advertise < 0)
@@ -88,9 +83,22 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
bmcr = 0;
}
- /* Configure the underlying interface speed */
- regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
- RG_PHY_SPEED_3_125G, rgc3);
+ if (mpcs->interface != interface) {
+ /* PHYA power down */
+ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
+ SGMII_PHYA_PWD, SGMII_PHYA_PWD);
+
+ if (interface == PHY_INTERFACE_MODE_2500BASEX)
+ rgc3 = RG_PHY_SPEED_3_125G;
+ else
+ rgc3 = 0;
+
+ /* Configure the underlying interface speed */
+ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
+ RG_PHY_SPEED_3_125G, rgc3);
+
+ mpcs->interface = interface;
+ }
/* Update the advertisement, noting whether it has changed */
regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
@@ -108,9 +116,17 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
SGMII_AN_RESTART | SGMII_AN_ENABLE, bmcr);
- /* Release PHYA power down state */
- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
- SGMII_PHYA_PWD, 0);
+ /* Release PHYA power down state
+ * Only removing bit SGMII_PHYA_PWD isn't enough.
+ * There are cases when the SGMII_PHYA_PWD register contains 0x9 which
+ * prevents SGMII from working. The SGMII still shows link but no traffic
+ * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was
+ * taken from a good working state of the SGMII interface.
+ * Unknown how much the QPHY needs but it is racy without a sleep.
+ * Tested on mt7622 & mt7986.
+ */
+ usleep_range(50, 100);
+ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
return changed;
}
@@ -138,11 +154,11 @@ static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
else
sgm_mode = SGMII_SPEED_1000;
- if (duplex == DUPLEX_FULL)
- sgm_mode |= SGMII_DUPLEX_FULL;
+ if (duplex != DUPLEX_FULL)
+ sgm_mode |= SGMII_DUPLEX_HALF;
regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
- SGMII_DUPLEX_FULL | SGMII_SPEED_MASK,
+ SGMII_DUPLEX_HALF | SGMII_SPEED_MASK,
sgm_mode);
}
}
@@ -171,6 +187,8 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
return PTR_ERR(ss->pcs[i].regmap);
ss->pcs[i].pcs.ops = &mtk_pcs_ops;
+ ss->pcs[i].pcs.poll = true;
+ ss->pcs[i].interface = PHY_INTERFACE_MODE_NA;
}
return 0;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
index d3ca745d107d62..c837103a9ee338 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
@@ -2176,15 +2176,9 @@ int mlx5_cmd_init(struct mlx5_core_dev *dev)
return -EINVAL;
}
- cmd->stats = kvcalloc(MLX5_CMD_OP_MAX, sizeof(*cmd->stats), GFP_KERNEL);
- if (!cmd->stats)
- return -ENOMEM;
-
cmd->pool = dma_pool_create("mlx5_cmd", mlx5_core_dma_dev(dev), size, align, 0);
- if (!cmd->pool) {
- err = -ENOMEM;
- goto dma_pool_err;
- }
+ if (!cmd->pool)
+ return -ENOMEM;
err = alloc_cmd_page(dev, cmd);
if (err)
@@ -2268,8 +2262,6 @@ err_free_page:
err_free_pool:
dma_pool_destroy(cmd->pool);
-dma_pool_err:
- kvfree(cmd->stats);
return err;
}
@@ -2282,7 +2274,6 @@ void mlx5_cmd_cleanup(struct mlx5_core_dev *dev)
destroy_msg_cache(dev);
free_cmd_page(dev, cmd);
dma_pool_destroy(cmd->pool);
- kvfree(cmd->stats);
}
void mlx5_cmd_set_state(struct mlx5_core_dev *dev,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c
index 3e232a65a0c3e1..bb95b40d25eb5d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c
@@ -245,8 +245,9 @@ void mlx5_pages_debugfs_init(struct mlx5_core_dev *dev)
pages = dev->priv.dbg.pages_debugfs;
debugfs_create_u32("fw_pages_total", 0400, pages, &dev->priv.fw_pages);
- debugfs_create_u32("fw_pages_vfs", 0400, pages, &dev->priv.vfs_pages);
- debugfs_create_u32("fw_pages_host_pf", 0400, pages, &dev->priv.host_pf_pages);
+ debugfs_create_u32("fw_pages_vfs", 0400, pages, &dev->priv.page_counters[MLX5_VF]);
+ debugfs_create_u32("fw_pages_sfs", 0400, pages, &dev->priv.page_counters[MLX5_SF]);
+ debugfs_create_u32("fw_pages_host_pf", 0400, pages, &dev->priv.page_counters[MLX5_HOST_PF]);
debugfs_create_u32("fw_pages_alloc_failed", 0400, pages, &dev->priv.fw_pages_alloc_failed);
debugfs_create_u32("fw_pages_give_dropped", 0400, pages, &dev->priv.give_pages_dropped);
debugfs_create_u32("fw_pages_reclaim_discard", 0400, pages,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
index ddb197970c22c5..5bd83c0275f821 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/devlink.c
@@ -468,7 +468,7 @@ static int mlx5_devlink_enable_roce_validate(struct devlink *devlink, u32 id,
bool new_state = val.vbool;
if (new_state && !MLX5_CAP_GEN(dev, roce) &&
- !MLX5_CAP_GEN(dev, roce_rw_supported)) {
+ !(MLX5_CAP_GEN(dev, roce_rw_supported) && MLX5_CAP_GEN_MAX(dev, roce))) {
NL_SET_ERR_MSG_MOD(extack, "Device doesn't support RoCE");
return -EOPNOTSUPP;
}
@@ -563,7 +563,7 @@ static int mlx5_devlink_eq_depth_validate(struct devlink *devlink, u32 id,
union devlink_param_value val,
struct netlink_ext_ack *extack)
{
- return (val.vu16 >= 64 && val.vu16 <= 4096) ? 0 : -EINVAL;
+ return (val.vu32 >= 64 && val.vu32 <= 4096) ? 0 : -EINVAL;
}
static const struct devlink_param mlx5_devlink_params[] = {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
index 21831386b26e84..5b05b884b5fb3c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c
@@ -64,6 +64,7 @@ static int mlx5_query_mtrc_caps(struct mlx5_fw_tracer *tracer)
MLX5_GET(mtrc_cap, out, num_string_trace);
tracer->str_db.num_string_db = MLX5_GET(mtrc_cap, out, num_string_db);
tracer->owner = !!MLX5_GET(mtrc_cap, out, trace_owner);
+ tracer->str_db.loaded = false;
for (i = 0; i < tracer->str_db.num_string_db; i++) {
mtrc_cap_sp = MLX5_ADDR_OF(mtrc_cap, out, string_db_param[i]);
@@ -756,6 +757,7 @@ static int mlx5_fw_tracer_set_mtrc_conf(struct mlx5_fw_tracer *tracer)
if (err)
mlx5_core_warn(dev, "FWTracer: Failed to set tracer configurations %d\n", err);
+ tracer->buff.consumer_index = 0;
return err;
}
@@ -820,7 +822,6 @@ static void mlx5_fw_tracer_ownership_change(struct work_struct *work)
mlx5_core_dbg(tracer->dev, "FWTracer: ownership changed, current=(%d)\n", tracer->owner);
if (tracer->owner) {
tracer->owner = false;
- tracer->buff.consumer_index = 0;
return;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ecpf.c b/drivers/net/ethernet/mellanox/mlx5/core/ecpf.c
index 464eb3a1845065..cdc87ecae5d399 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/ecpf.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/ecpf.c
@@ -87,7 +87,7 @@ void mlx5_ec_cleanup(struct mlx5_core_dev *dev)
mlx5_host_pf_cleanup(dev);
- err = mlx5_wait_for_pages(dev, &dev->priv.host_pf_pages);
+ err = mlx5_wait_for_pages(dev, &dev->priv.page_counters[MLX5_HOST_PF]);
if (err)
mlx5_core_warn(dev, "Timeout reclaiming external host PF pages err(%d)\n", err);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/htb.c b/drivers/net/ethernet/mellanox/mlx5/core/en/htb.c
index 6dac76fa58a3f1..09d441ecb9f6d2 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/htb.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/htb.c
@@ -637,7 +637,7 @@ mlx5e_htb_update_children(struct mlx5e_htb *htb, struct mlx5e_qos_node *node,
if (child->bw_share == old_bw_share)
continue;
- err_one = mlx5_qos_update_node(htb->mdev, child->hw_id, child->bw_share,
+ err_one = mlx5_qos_update_node(htb->mdev, child->bw_share,
child->max_average_bw, child->hw_id);
if (!err && err_one) {
err = err_one;
@@ -671,7 +671,7 @@ mlx5e_htb_node_modify(struct mlx5e_htb *htb, u16 classid, u64 rate, u64 ceil,
mlx5e_htb_convert_rate(htb, rate, node->parent, &bw_share);
mlx5e_htb_convert_ceil(htb, ceil, &max_average_bw);
- err = mlx5_qos_update_node(htb->mdev, node->parent->hw_id, bw_share,
+ err = mlx5_qos_update_node(htb->mdev, bw_share,
max_average_bw, node->hw_id);
if (err) {
NL_SET_ERR_MSG_MOD(extack, "Firmware error when modifying a node.");
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c
index 585bdc8383ee21..4ad19c98129440 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/params.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/params.c
@@ -578,7 +578,6 @@ int mlx5e_mpwrq_validate_xsk(struct mlx5_core_dev *mdev, struct mlx5e_params *pa
{
enum mlx5e_mpwrq_umr_mode umr_mode = mlx5e_mpwrq_umr_mode(mdev, xsk);
u8 page_shift = mlx5e_mpwrq_page_shift(mdev, xsk);
- bool unaligned = xsk ? xsk->unaligned : false;
u16 max_mtu_pkts;
if (!mlx5e_check_fragmented_striding_rq_cap(mdev, page_shift, umr_mode))
@@ -591,7 +590,7 @@ int mlx5e_mpwrq_validate_xsk(struct mlx5_core_dev *mdev, struct mlx5e_params *pa
* needed number of WQEs exceeds the maximum.
*/
max_mtu_pkts = min_t(u8, MLX5E_PARAMS_MAXIMUM_LOG_RQ_SIZE,
- mlx5e_mpwrq_max_log_rq_pkts(mdev, page_shift, unaligned));
+ mlx5e_mpwrq_max_log_rq_pkts(mdev, page_shift, xsk->unaligned));
if (params->log_rq_mtu_frames > max_mtu_pkts) {
mlx5_core_err(mdev, "Current RQ length %d is too big for XSK with given frame size %u\n",
1 << params->log_rq_mtu_frames, xsk->chunk_size);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c
index 8099a21e674c96..ce85b48d327daa 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/rep/bridge.c
@@ -438,10 +438,6 @@ static int mlx5_esw_bridge_switchdev_event(struct notifier_block *nb,
switch (event) {
case SWITCHDEV_FDB_ADD_TO_BRIDGE:
- /* only handle the event on native eswtich of representor */
- if (!mlx5_esw_bridge_is_local(dev, rep, esw))
- break;
-
fdb_info = container_of(info,
struct switchdev_notifier_fdb_info,
info);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
index 5f6f95ad6888c4..1ae15b8536a857 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/reporter_rx.c
@@ -459,7 +459,11 @@ static int mlx5e_rx_reporter_diagnose(struct devlink_health_reporter *reporter,
goto unlock;
for (i = 0; i < priv->channels.num; i++) {
- struct mlx5e_rq *rq = &priv->channels.c[i]->rq;
+ struct mlx5e_channel *c = priv->channels.c[i];
+ struct mlx5e_rq *rq;
+
+ rq = test_bit(MLX5E_CHANNEL_STATE_XSK, c->state) ?
+ &c->xskrq : &c->rq;
err = mlx5e_rx_reporter_build_diagnose_output(rq, fmsg);
if (err)
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c
index 512d431489228e..c4378afdec09e4 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/act/police.c
@@ -34,12 +34,6 @@ static int police_act_validate(const struct flow_action_entry *act,
return -EOPNOTSUPP;
}
- if (act->police.rate_pkt_ps) {
- NL_SET_ERR_MSG_MOD(extack,
- "QoS offload not support packets per second");
- return -EOPNOTSUPP;
- }
-
return 0;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c
index 8d7d761482d272..50b60fd0094676 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/post_meter.c
@@ -127,6 +127,7 @@ mlx5e_post_meter_add_rule(struct mlx5e_priv *priv,
attr->counter = act_counter;
attr->flags |= MLX5_ATTR_FLAG_NO_IN_PORT;
+ attr->inner_match_level = MLX5_MATCH_NONE;
attr->outer_match_level = MLX5_MATCH_NONE;
attr->chain = 0;
attr->prio = 0;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c
index 1cbd2eb9d04f90..f2c2c752bd1c34 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc/sample.c
@@ -477,7 +477,6 @@ mlx5e_tc_sample_offload(struct mlx5e_tc_psample *tc_psample,
struct mlx5e_sample_flow *sample_flow;
struct mlx5e_sample_attr *sample_attr;
struct mlx5_flow_attr *pre_attr;
- u32 tunnel_id = attr->tunnel_id;
struct mlx5_eswitch *esw;
u32 default_tbl_id;
u32 obj_id;
@@ -522,7 +521,7 @@ mlx5e_tc_sample_offload(struct mlx5e_tc_psample *tc_psample,
restore_obj.sample.group_id = sample_attr->group_num;
restore_obj.sample.rate = sample_attr->rate;
restore_obj.sample.trunc_size = sample_attr->trunc_size;
- restore_obj.sample.tunnel_id = tunnel_id;
+ restore_obj.sample.tunnel_id = attr->tunnel_id;
err = mapping_add(esw->offloads.reg_c0_obj_pool, &restore_obj, &obj_id);
if (err)
goto err_obj_id;
@@ -548,7 +547,7 @@ mlx5e_tc_sample_offload(struct mlx5e_tc_psample *tc_psample,
/* For decap action, do decap in the original flow table instead of the
* default flow table.
*/
- if (tunnel_id)
+ if (attr->action & MLX5_FLOW_CONTEXT_ACTION_DECAP)
pre_attr->action |= MLX5_FLOW_CONTEXT_ACTION_DECAP;
pre_attr->modify_hdr = sample_flow->restore->modify_hdr;
pre_attr->flags = MLX5_ATTR_FLAG_SAMPLE;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c
index a69849e0deed7e..313df8232db707 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c
@@ -2103,14 +2103,9 @@ out_err:
static void
mlx5_ct_tc_create_dbgfs(struct mlx5_tc_ct_priv *ct_priv)
{
- bool is_fdb = ct_priv->ns_type == MLX5_FLOW_NAMESPACE_FDB;
struct mlx5_tc_ct_debugfs *ct_dbgfs = &ct_priv->debugfs;
- char dirname[16] = {};
- if (sscanf(dirname, "ct_%s", is_fdb ? "fdb" : "nic") < 0)
- return;
-
- ct_dbgfs->root = debugfs_create_dir(dirname, mlx5_debugfs_get_dev_root(ct_priv->dev));
+ ct_dbgfs->root = debugfs_create_dir("ct", mlx5_debugfs_get_dev_root(ct_priv->dev));
debugfs_create_atomic_t("offloaded", 0400, ct_dbgfs->root,
&ct_dbgfs->stats.offloaded);
debugfs_create_atomic_t("rx_dropped", 0400, ct_dbgfs->root,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
index ff73d25bc6eb8b..2aaf8ab857b8f2 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c
@@ -222,7 +222,7 @@ void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv,
int err;
list_for_each_entry(flow, flow_list, tmp_list) {
- if (!mlx5e_is_offloaded_flow(flow) || flow_flag_test(flow, SLOW))
+ if (!mlx5e_is_offloaded_flow(flow))
continue;
attr = mlx5e_tc_get_encap_attr(flow);
@@ -231,6 +231,13 @@ void mlx5e_tc_encap_flows_del(struct mlx5e_priv *priv,
esw_attr->dests[flow->tmp_entry_index].flags &= ~MLX5_ESW_DEST_ENCAP_VALID;
esw_attr->dests[flow->tmp_entry_index].pkt_reformat = NULL;
+ /* Clear pkt_reformat before checking slow path flag. Because
+ * in next iteration, the same flow is already set slow path
+ * flag, but still need to clear the pkt_reformat.
+ */
+ if (flow_flag_test(flow, SLOW))
+ continue;
+
/* update from encap rule to slow path rule */
spec = &flow->attr->parse_attr->spec;
rule = mlx5e_tc_offload_to_slow_path(esw, flow, spec);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_geneve.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_geneve.c
index f5b26f5a7de460..054d80c4e65cfc 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_geneve.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_geneve.c
@@ -273,6 +273,11 @@ static int mlx5e_tc_tun_parse_geneve_options(struct mlx5e_priv *priv,
geneve_tlv_option_0_data, be32_to_cpu(opt_data_key));
MLX5_SET(fte_match_set_misc3, misc_3_c,
geneve_tlv_option_0_data, be32_to_cpu(opt_data_mask));
+ if (MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev,
+ ft_field_support.geneve_tlv_option_0_exist)) {
+ MLX5_SET_TO_ONES(fte_match_set_misc, misc_c, geneve_tlv_option_0_exist);
+ MLX5_SET_TO_ONES(fte_match_set_misc, misc_v, geneve_tlv_option_0_exist);
+ }
spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_3;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c
index fd07c4cbfd1d25..1f62c702b6255d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c
@@ -88,6 +88,8 @@ static int mlx5e_gen_ip_tunnel_header_vxlan(char buf[],
struct udphdr *udp = (struct udphdr *)(buf);
struct vxlanhdr *vxh;
+ if (tun_key->tun_flags & TUNNEL_VXLAN_OPT)
+ return -EOPNOTSUPP;
vxh = (struct vxlanhdr *)((char *)udp + sizeof(struct udphdr));
*ip_proto = IPPROTO_UDP;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h
index a92e19c4c499d4..8bed9c36107547 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec.h
@@ -122,11 +122,8 @@ struct mlx5e_ipsec_aso {
u8 ctx[MLX5_ST_SZ_BYTES(ipsec_aso)];
dma_addr_t dma_addr;
struct mlx5_aso *aso;
- /* IPsec ASO caches data on every query call,
- * so in nested calls, we can use this boolean to save
- * recursive calls to mlx5e_ipsec_aso_query()
- */
- u8 use_cache : 1;
+ /* Protect ASO WQ access, as it is global to whole IPsec */
+ spinlock_t lock;
};
struct mlx5e_ipsec {
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c
index 8e3614218fc4fa..2461462b7b991c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/ipsec_offload.c
@@ -320,7 +320,6 @@ static void mlx5e_ipsec_handle_event(struct work_struct *_work)
if (ret)
goto unlock;
- aso->use_cache = true;
if (attrs->esn_trigger &&
!MLX5_GET(ipsec_aso, aso->ctx, esn_event_arm)) {
u32 mode_param = MLX5_GET(ipsec_aso, aso->ctx, mode_parameter);
@@ -333,7 +332,6 @@ static void mlx5e_ipsec_handle_event(struct work_struct *_work)
!MLX5_GET(ipsec_aso, aso->ctx, hard_lft_arm) ||
!MLX5_GET(ipsec_aso, aso->ctx, remove_flow_enable))
xfrm_state_check_expire(sa_entry->x);
- aso->use_cache = false;
unlock:
spin_unlock(&sa_entry->x->lock);
@@ -398,6 +396,7 @@ int mlx5e_ipsec_aso_init(struct mlx5e_ipsec *ipsec)
goto err_aso_create;
}
+ spin_lock_init(&aso->lock);
ipsec->nb.notifier_call = mlx5e_ipsec_event;
mlx5_notifier_register(mdev, &ipsec->nb);
@@ -456,13 +455,12 @@ int mlx5e_ipsec_aso_query(struct mlx5e_ipsec_sa_entry *sa_entry,
struct mlx5e_hw_objs *res;
struct mlx5_aso_wqe *wqe;
u8 ds_cnt;
+ int ret;
lockdep_assert_held(&sa_entry->x->lock);
- if (aso->use_cache)
- return 0;
-
res = &mdev->mlx5e_res.hw_objs;
+ spin_lock_bh(&aso->lock);
memset(aso->ctx, 0, sizeof(aso->ctx));
wqe = mlx5_aso_get_wqe(aso->aso);
ds_cnt = DIV_ROUND_UP(sizeof(*wqe), MLX5_SEND_WQE_DS);
@@ -477,7 +475,9 @@ int mlx5e_ipsec_aso_query(struct mlx5e_ipsec_sa_entry *sa_entry,
mlx5e_ipsec_aso_copy(ctrl, data);
mlx5_aso_post_wqe(aso->aso, false, &wqe->ctrl);
- return mlx5_aso_poll_cq(aso->aso, false);
+ ret = mlx5_aso_poll_cq(aso->aso, false);
+ spin_unlock_bh(&aso->lock);
+ return ret;
}
void mlx5e_ipsec_aso_update_curlft(struct mlx5e_ipsec_sa_entry *sa_entry,
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 9369a580743e1f..7f6b940830b314 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c
@@ -62,6 +62,7 @@ struct mlx5e_macsec_sa {
u32 enc_key_id;
u32 next_pn;
sci_t sci;
+ ssci_t ssci;
salt_t salt;
struct rhash_head hash;
@@ -358,7 +359,6 @@ static int mlx5e_macsec_init_sa(struct macsec_context *ctx,
struct mlx5_core_dev *mdev = priv->mdev;
struct mlx5_macsec_obj_attrs obj_attrs;
union mlx5e_macsec_rule *macsec_rule;
- struct macsec_key *key;
int err;
obj_attrs.next_pn = sa->next_pn;
@@ -368,13 +368,9 @@ static int mlx5e_macsec_init_sa(struct macsec_context *ctx,
obj_attrs.aso_pdn = macsec->aso.pdn;
obj_attrs.epn_state = sa->epn_state;
- key = (is_tx) ? &ctx->sa.tx_sa->key : &ctx->sa.rx_sa->key;
-
if (sa->epn_state.epn_enabled) {
- obj_attrs.ssci = (is_tx) ? cpu_to_be32((__force u32)ctx->sa.tx_sa->ssci) :
- cpu_to_be32((__force u32)ctx->sa.rx_sa->ssci);
-
- memcpy(&obj_attrs.salt, &key->salt, sizeof(key->salt));
+ obj_attrs.ssci = cpu_to_be32((__force u32)sa->ssci);
+ memcpy(&obj_attrs.salt, &sa->salt, sizeof(sa->salt));
}
obj_attrs.replay_window = ctx->secy->replay_window;
@@ -499,10 +495,11 @@ mlx5e_macsec_get_macsec_device_context(const struct mlx5e_macsec *macsec,
}
static void update_macsec_epn(struct mlx5e_macsec_sa *sa, const struct macsec_key *key,
- const pn_t *next_pn_halves)
+ const pn_t *next_pn_halves, ssci_t ssci)
{
struct mlx5e_macsec_epn_state *epn_state = &sa->epn_state;
+ sa->ssci = ssci;
sa->salt = key->salt;
epn_state->epn_enabled = 1;
epn_state->epn_msb = next_pn_halves->upper;
@@ -550,7 +547,8 @@ static int mlx5e_macsec_add_txsa(struct macsec_context *ctx)
tx_sa->assoc_num = assoc_num;
if (secy->xpn)
- update_macsec_epn(tx_sa, &ctx_tx_sa->key, &ctx_tx_sa->next_pn_halves);
+ update_macsec_epn(tx_sa, &ctx_tx_sa->key, &ctx_tx_sa->next_pn_halves,
+ ctx_tx_sa->ssci);
err = mlx5_create_encryption_key(mdev, ctx->sa.key, secy->key_len,
MLX5_ACCEL_OBJ_MACSEC_KEY,
@@ -945,7 +943,8 @@ static int mlx5e_macsec_add_rxsa(struct macsec_context *ctx)
rx_sa->fs_id = rx_sc->sc_xarray_element->fs_id;
if (ctx->secy->xpn)
- update_macsec_epn(rx_sa, &ctx_rx_sa->key, &ctx_rx_sa->next_pn_halves);
+ update_macsec_epn(rx_sa, &ctx_rx_sa->key, &ctx_rx_sa->next_pn_halves,
+ ctx_rx_sa->ssci);
err = mlx5_create_encryption_key(mdev, ctx->sa.key, ctx->secy->key_len,
MLX5_ACCEL_OBJ_MACSEC_KEY,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
index 1892ccb889b3fe..7cd36f4ac3efc8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c
@@ -443,7 +443,7 @@ void mlx5e_enable_cvlan_filter(struct mlx5e_flow_steering *fs, bool promisc)
void mlx5e_disable_cvlan_filter(struct mlx5e_flow_steering *fs, bool promisc)
{
- if (fs->vlan->cvlan_filter_disabled)
+ if (!fs->vlan || fs->vlan->cvlan_filter_disabled)
return;
fs->vlan->cvlan_filter_disabled = true;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 8d36e2de53a992..6c24f33a5ea5c9 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -591,7 +591,8 @@ static int mlx5e_init_rxq_rq(struct mlx5e_channel *c, struct mlx5e_params *param
rq->ix = c->ix;
rq->channel = c;
rq->mdev = mdev;
- rq->hw_mtu = MLX5E_SW2HW_MTU(params, params->sw_mtu);
+ rq->hw_mtu =
+ MLX5E_SW2HW_MTU(params, params->sw_mtu) - ETH_FCS_LEN * !params->scatter_fcs_en;
rq->xdpsq = &c->rq_xdpsq;
rq->stats = &c->priv->channel_stats[c->ix]->rq;
rq->ptp_cyc2time = mlx5_rq_ts_translator(mdev);
@@ -1014,35 +1015,6 @@ int mlx5e_flush_rq(struct mlx5e_rq *rq, int curr_state)
return mlx5e_rq_to_ready(rq, curr_state);
}
-static int mlx5e_modify_rq_scatter_fcs(struct mlx5e_rq *rq, bool enable)
-{
- struct mlx5_core_dev *mdev = rq->mdev;
-
- void *in;
- void *rqc;
- int inlen;
- int err;
-
- inlen = MLX5_ST_SZ_BYTES(modify_rq_in);
- in = kvzalloc(inlen, GFP_KERNEL);
- if (!in)
- return -ENOMEM;
-
- rqc = MLX5_ADDR_OF(modify_rq_in, in, ctx);
-
- MLX5_SET(modify_rq_in, in, rq_state, MLX5_RQC_STATE_RDY);
- MLX5_SET64(modify_rq_in, in, modify_bitmask,
- MLX5_MODIFY_RQ_IN_MODIFY_BITMASK_SCATTER_FCS);
- MLX5_SET(rqc, rqc, scatter_fcs, enable);
- MLX5_SET(rqc, rqc, state, MLX5_RQC_STATE_RDY);
-
- err = mlx5_core_modify_rq(mdev, rq->rqn, in);
-
- kvfree(in);
-
- return err;
-}
-
static int mlx5e_modify_rq_vsd(struct mlx5e_rq *rq, bool vsd)
{
struct mlx5_core_dev *mdev = rq->mdev;
@@ -1305,7 +1277,7 @@ static int mlx5e_alloc_xdpsq(struct mlx5e_channel *c,
sq->channel = c;
sq->uar_map = mdev->mlx5e_res.hw_objs.bfreg.map;
sq->min_inline_mode = params->tx_min_inline_mode;
- sq->hw_mtu = MLX5E_SW2HW_MTU(params, params->sw_mtu);
+ sq->hw_mtu = MLX5E_SW2HW_MTU(params, params->sw_mtu) - ETH_FCS_LEN;
sq->xsk_pool = xsk_pool;
sq->stats = sq->xsk_pool ?
@@ -3314,20 +3286,6 @@ static void mlx5e_cleanup_nic_tx(struct mlx5e_priv *priv)
mlx5e_destroy_tises(priv);
}
-static int mlx5e_modify_channels_scatter_fcs(struct mlx5e_channels *chs, bool enable)
-{
- int err = 0;
- int i;
-
- for (i = 0; i < chs->num; i++) {
- err = mlx5e_modify_rq_scatter_fcs(&chs->c[i]->rq, enable);
- if (err)
- return err;
- }
-
- return 0;
-}
-
static int mlx5e_modify_channels_vsd(struct mlx5e_channels *chs, bool vsd)
{
int err;
@@ -3903,41 +3861,27 @@ static int mlx5e_set_rx_port_ts(struct mlx5_core_dev *mdev, bool enable)
return mlx5_set_ports_check(mdev, in, sizeof(in));
}
+static int mlx5e_set_rx_port_ts_wrap(struct mlx5e_priv *priv, void *ctx)
+{
+ struct mlx5_core_dev *mdev = priv->mdev;
+ bool enable = *(bool *)ctx;
+
+ return mlx5e_set_rx_port_ts(mdev, enable);
+}
+
static int set_feature_rx_fcs(struct net_device *netdev, bool enable)
{
struct mlx5e_priv *priv = netdev_priv(netdev);
struct mlx5e_channels *chs = &priv->channels;
- struct mlx5_core_dev *mdev = priv->mdev;
+ struct mlx5e_params new_params;
int err;
mutex_lock(&priv->state_lock);
- if (enable) {
- err = mlx5e_set_rx_port_ts(mdev, false);
- if (err)
- goto out;
-
- chs->params.scatter_fcs_en = true;
- err = mlx5e_modify_channels_scatter_fcs(chs, true);
- if (err) {
- chs->params.scatter_fcs_en = false;
- mlx5e_set_rx_port_ts(mdev, true);
- }
- } else {
- chs->params.scatter_fcs_en = false;
- err = mlx5e_modify_channels_scatter_fcs(chs, false);
- if (err) {
- chs->params.scatter_fcs_en = true;
- goto out;
- }
- err = mlx5e_set_rx_port_ts(mdev, true);
- if (err) {
- mlx5_core_warn(mdev, "Failed to set RX port timestamp %d\n", err);
- err = 0;
- }
- }
-
-out:
+ new_params = chs->params;
+ new_params.scatter_fcs_en = enable;
+ err = mlx5e_safe_switch_params(priv, &new_params, mlx5e_set_rx_port_ts_wrap,
+ &new_params.scatter_fcs_en, true);
mutex_unlock(&priv->state_lock);
return err;
}
@@ -4074,6 +4018,10 @@ static netdev_features_t mlx5e_fix_uplink_rep_features(struct net_device *netdev
if (netdev->features & NETIF_F_GRO_HW)
netdev_warn(netdev, "Disabling HW_GRO, not supported in switchdev mode\n");
+ features &= ~NETIF_F_HW_VLAN_CTAG_FILTER;
+ if (netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+ netdev_warn(netdev, "Disabling HW_VLAN CTAG FILTERING, not supported in switchdev mode\n");
+
return features;
}
@@ -4084,6 +4032,9 @@ static netdev_features_t mlx5e_fix_features(struct net_device *netdev,
struct mlx5e_vlan_table *vlan;
struct mlx5e_params *params;
+ if (!netif_device_present(netdev))
+ return features;
+
vlan = mlx5e_fs_get_vlan(priv->fs);
mutex_lock(&priv->state_lock);
params = &priv->channels.params;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
index 75b9e1528fd29f..7d90e5b728548d 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
@@ -191,7 +191,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(vport_rep)
if (err) {
netdev_warn(priv->netdev, "vport %d error %d reading stats\n",
rep->vport, err);
- return;
+ goto out;
}
#define MLX5_GET_CTR(p, x) \
@@ -241,6 +241,7 @@ static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(vport_rep)
rep_stats->tx_vport_rdma_multicast_bytes =
MLX5_GET_CTR(out, received_ib_multicast.octets);
+out:
kvfree(out);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
index c8820ab221694a..3df455f6b1685c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c
@@ -2419,7 +2419,7 @@ static inline void mlx5i_complete_rx_cqe(struct mlx5e_rq *rq,
priv = mlx5i_epriv(netdev);
tstamp = &priv->tstamp;
- stats = rq->stats;
+ stats = &priv->channel_stats[rq->ix]->rq;
flags_rqpn = be32_to_cpu(cqe->flags_rqpn);
g = (flags_rqpn >> 28) & 3;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
index 9af2aa2922f5da..243d5d7750beb5 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c
@@ -166,6 +166,7 @@ struct mlx5_fs_chains *mlx5e_nic_chains(struct mlx5e_tc_table *tc)
* it's different than the ht->mutex here.
*/
static struct lock_class_key tc_ht_lock_key;
+static struct lock_class_key tc_ht_wq_key;
static void mlx5e_put_flow_tunnel_id(struct mlx5e_tc_flow *flow);
static void free_flow_post_acts(struct mlx5e_tc_flow *flow);
@@ -1301,7 +1302,6 @@ mlx5e_tc_add_nic_flow(struct mlx5e_priv *priv,
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
err = mlx5e_attach_mod_hdr(priv, flow, parse_attr);
- mlx5e_mod_hdr_dealloc(&parse_attr->mod_hdr_acts);
if (err)
return err;
}
@@ -1359,8 +1359,10 @@ static void mlx5e_tc_del_nic_flow(struct mlx5e_priv *priv,
}
mutex_unlock(&tc->t_lock);
- if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR)
+ if (attr->action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) {
+ mlx5e_mod_hdr_dealloc(&attr->parse_attr->mod_hdr_acts);
mlx5e_detach_mod_hdr(priv, flow);
+ }
if (attr->action & MLX5_FLOW_CONTEXT_ACTION_COUNT)
mlx5_fc_destroy(priv->mdev, attr->counter);
@@ -5181,6 +5183,7 @@ int mlx5e_tc_nic_init(struct mlx5e_priv *priv)
return err;
lockdep_set_class(&tc->ht.mutex, &tc_ht_lock_key);
+ lockdep_init_map(&tc->ht.run_work.lockdep_map, "tc_ht_wq_key", &tc_ht_wq_key, 0);
mapping_id = mlx5_query_nic_system_image_guid(dev);
@@ -5287,6 +5290,7 @@ int mlx5e_tc_ht_init(struct rhashtable *tc_ht)
return err;
lockdep_set_class(&tc_ht->mutex, &tc_ht_lock_key);
+ lockdep_init_map(&tc_ht->run_work.lockdep_map, "tc_ht_wq_key", &tc_ht_wq_key, 0);
return 0;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/egress_lgcy.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/egress_lgcy.c
index 60a73990017c2a..6b4c9ffad95b29 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/egress_lgcy.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/egress_lgcy.c
@@ -67,6 +67,7 @@ static void esw_acl_egress_lgcy_groups_destroy(struct mlx5_vport *vport)
int esw_acl_egress_lgcy_setup(struct mlx5_eswitch *esw,
struct mlx5_vport *vport)
{
+ bool vst_mode_steering = esw_vst_mode_is_steering(esw);
struct mlx5_flow_destination drop_ctr_dst = {};
struct mlx5_flow_destination *dst = NULL;
struct mlx5_fc *drop_counter = NULL;
@@ -77,6 +78,7 @@ int esw_acl_egress_lgcy_setup(struct mlx5_eswitch *esw,
*/
int table_size = 2;
int dest_num = 0;
+ int actions_flag;
int err = 0;
if (vport->egress.legacy.drop_counter) {
@@ -119,8 +121,11 @@ int esw_acl_egress_lgcy_setup(struct mlx5_eswitch *esw,
vport->vport, vport->info.vlan, vport->info.qos);
/* Allowed vlan rule */
+ actions_flag = MLX5_FLOW_CONTEXT_ACTION_ALLOW;
+ if (vst_mode_steering)
+ actions_flag |= MLX5_FLOW_CONTEXT_ACTION_VLAN_POP;
err = esw_egress_acl_vlan_create(esw, vport, NULL, vport->info.vlan,
- MLX5_FLOW_CONTEXT_ACTION_ALLOW);
+ actions_flag);
if (err)
goto out;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_lgcy.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_lgcy.c
index b1a5199260f696..093ed86a0acd8c 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_lgcy.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_lgcy.c
@@ -139,11 +139,14 @@ static void esw_acl_ingress_lgcy_groups_destroy(struct mlx5_vport *vport)
int esw_acl_ingress_lgcy_setup(struct mlx5_eswitch *esw,
struct mlx5_vport *vport)
{
+ bool vst_mode_steering = esw_vst_mode_is_steering(esw);
struct mlx5_flow_destination drop_ctr_dst = {};
struct mlx5_flow_destination *dst = NULL;
struct mlx5_flow_act flow_act = {};
struct mlx5_flow_spec *spec = NULL;
struct mlx5_fc *counter = NULL;
+ bool vst_check_cvlan = false;
+ bool vst_push_cvlan = false;
/* The ingress acl table contains 4 groups
* (2 active rules at the same time -
* 1 allow rule from one of the first 3 groups.
@@ -203,7 +206,26 @@ int esw_acl_ingress_lgcy_setup(struct mlx5_eswitch *esw,
goto out;
}
- if (vport->info.vlan || vport->info.qos)
+ if ((vport->info.vlan || vport->info.qos)) {
+ if (vst_mode_steering)
+ vst_push_cvlan = true;
+ else if (!MLX5_CAP_ESW(esw->dev, vport_cvlan_insert_always))
+ vst_check_cvlan = true;
+ }
+
+ if (vst_check_cvlan || vport->info.spoofchk)
+ spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
+
+ /* Create ingress allow rule */
+ flow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW;
+ if (vst_push_cvlan) {
+ flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH;
+ flow_act.vlan[0].prio = vport->info.qos;
+ flow_act.vlan[0].vid = vport->info.vlan;
+ flow_act.vlan[0].ethtype = ETH_P_8021Q;
+ }
+
+ if (vst_check_cvlan)
MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria,
outer_headers.cvlan_tag);
@@ -218,9 +240,6 @@ int esw_acl_ingress_lgcy_setup(struct mlx5_eswitch *esw,
ether_addr_copy(smac_v, vport->info.mac);
}
- /* Create ingress allow rule */
- spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
- flow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW;
vport->ingress.allow_rule = mlx5_add_flow_rules(vport->ingress.acl, spec,
&flow_act, NULL, 0);
if (IS_ERR(vport->ingress.allow_rule)) {
@@ -232,6 +251,9 @@ int esw_acl_ingress_lgcy_setup(struct mlx5_eswitch *esw,
goto out;
}
+ if (!vst_check_cvlan && !vport->info.spoofchk)
+ goto out;
+
memset(&flow_act, 0, sizeof(flow_act));
flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
/* Attach drop flow counter */
@@ -257,7 +279,8 @@ int esw_acl_ingress_lgcy_setup(struct mlx5_eswitch *esw,
return 0;
out:
- esw_acl_ingress_lgcy_cleanup(esw, vport);
+ if (err)
+ esw_acl_ingress_lgcy_cleanup(esw, vport);
kvfree(spec);
return err;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c
index b176648d1343aa..3cdcb0e0b20f59 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/bridge.c
@@ -1715,7 +1715,7 @@ void mlx5_esw_bridge_fdb_update_used(struct net_device *dev, u16 vport_num, u16
struct mlx5_esw_bridge *bridge;
port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
- if (!port || port->flags & MLX5_ESW_BRIDGE_PORT_FLAG_PEER)
+ if (!port)
return;
bridge = port->bridge;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c b/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c
index 4f8a24d84a86a2..75015d370922e6 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c
@@ -22,15 +22,13 @@ struct mlx5_esw_rate_group {
};
static int esw_qos_tsar_config(struct mlx5_core_dev *dev, u32 *sched_ctx,
- u32 parent_ix, u32 tsar_ix,
- u32 max_rate, u32 bw_share)
+ u32 tsar_ix, u32 max_rate, u32 bw_share)
{
u32 bitmask = 0;
if (!MLX5_CAP_GEN(dev, qos) || !MLX5_CAP_QOS(dev, esw_scheduling))
return -EOPNOTSUPP;
- MLX5_SET(scheduling_context, sched_ctx, parent_element_id, parent_ix);
MLX5_SET(scheduling_context, sched_ctx, max_average_bw, max_rate);
MLX5_SET(scheduling_context, sched_ctx, bw_share, bw_share);
bitmask |= MODIFY_SCHEDULING_ELEMENT_IN_MODIFY_BITMASK_MAX_AVERAGE_BW;
@@ -51,7 +49,7 @@ static int esw_qos_group_config(struct mlx5_eswitch *esw, struct mlx5_esw_rate_g
int err;
err = esw_qos_tsar_config(dev, sched_ctx,
- esw->qos.root_tsar_ix, group->tsar_ix,
+ group->tsar_ix,
max_rate, bw_share);
if (err)
NL_SET_ERR_MSG_MOD(extack, "E-Switch modify group TSAR element failed");
@@ -67,23 +65,13 @@ static int esw_qos_vport_config(struct mlx5_eswitch *esw,
struct netlink_ext_ack *extack)
{
u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {};
- struct mlx5_esw_rate_group *group = vport->qos.group;
struct mlx5_core_dev *dev = esw->dev;
- u32 parent_tsar_ix;
- void *vport_elem;
int err;
if (!vport->qos.enabled)
return -EIO;
- parent_tsar_ix = group ? group->tsar_ix : esw->qos.root_tsar_ix;
- MLX5_SET(scheduling_context, sched_ctx, element_type,
- SCHEDULING_CONTEXT_ELEMENT_TYPE_VPORT);
- vport_elem = MLX5_ADDR_OF(scheduling_context, sched_ctx,
- element_attributes);
- MLX5_SET(vport_element, vport_elem, vport_number, vport->vport);
-
- err = esw_qos_tsar_config(dev, sched_ctx, parent_tsar_ix, vport->qos.esw_tsar_ix,
+ err = esw_qos_tsar_config(dev, sched_ctx, vport->qos.esw_tsar_ix,
max_rate, bw_share);
if (err) {
esw_warn(esw->dev,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
index 527e4bffda8d46..9daf55e90367b1 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c
@@ -161,10 +161,17 @@ static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u16 vport,
esw_vport_context.vport_cvlan_strip, 1);
if (set_flags & SET_VLAN_INSERT) {
- /* insert only if no vlan in packet */
- MLX5_SET(modify_esw_vport_context_in, in,
- esw_vport_context.vport_cvlan_insert, 1);
-
+ if (MLX5_CAP_ESW(dev, vport_cvlan_insert_always)) {
+ /* insert either if vlan exist in packet or not */
+ MLX5_SET(modify_esw_vport_context_in, in,
+ esw_vport_context.vport_cvlan_insert,
+ MLX5_VPORT_CVLAN_INSERT_ALWAYS);
+ } else {
+ /* insert only if no vlan in packet */
+ MLX5_SET(modify_esw_vport_context_in, in,
+ esw_vport_context.vport_cvlan_insert,
+ MLX5_VPORT_CVLAN_INSERT_WHEN_NO_CVLAN);
+ }
MLX5_SET(modify_esw_vport_context_in, in,
esw_vport_context.cvlan_pcp, qos);
MLX5_SET(modify_esw_vport_context_in, in,
@@ -809,6 +816,7 @@ out_free:
static int esw_vport_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport)
{
+ bool vst_mode_steering = esw_vst_mode_is_steering(esw);
u16 vport_num = vport->vport;
int flags;
int err;
@@ -839,8 +847,9 @@ static int esw_vport_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport)
flags = (vport->info.vlan || vport->info.qos) ?
SET_VLAN_STRIP | SET_VLAN_INSERT : 0;
- modify_esw_vport_cvlan(esw->dev, vport_num, vport->info.vlan,
- vport->info.qos, flags);
+ if (esw->mode == MLX5_ESWITCH_OFFLOADS || !vst_mode_steering)
+ modify_esw_vport_cvlan(esw->dev, vport_num, vport->info.vlan,
+ vport->info.qos, flags);
return 0;
@@ -1455,6 +1464,7 @@ void mlx5_eswitch_disable(struct mlx5_eswitch *esw)
mlx5_lag_disable_change(esw->dev);
down_write(&esw->mode_lock);
mlx5_eswitch_disable_locked(esw);
+ esw->mode = MLX5_ESWITCH_LEGACY;
up_write(&esw->mode_lock);
mlx5_lag_enable_change(esw->dev);
}
@@ -1848,6 +1858,7 @@ int __mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
u16 vport, u16 vlan, u8 qos, u8 set_flags)
{
struct mlx5_vport *evport = mlx5_eswitch_get_vport(esw, vport);
+ bool vst_mode_steering = esw_vst_mode_is_steering(esw);
int err = 0;
if (IS_ERR(evport))
@@ -1855,9 +1866,11 @@ int __mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
if (vlan > 4095 || qos > 7)
return -EINVAL;
- err = modify_esw_vport_cvlan(esw->dev, vport, vlan, qos, set_flags);
- if (err)
- return err;
+ if (esw->mode == MLX5_ESWITCH_OFFLOADS || !vst_mode_steering) {
+ err = modify_esw_vport_cvlan(esw->dev, vport, vlan, qos, set_flags);
+ if (err)
+ return err;
+ }
evport->info.vlan = vlan;
evport->info.qos = qos;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
index 5a85a5d32be7e1..92644fbb508164 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h
@@ -527,6 +527,12 @@ int mlx5_eswitch_del_vlan_action(struct mlx5_eswitch *esw,
int __mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
u16 vport, u16 vlan, u8 qos, u8 set_flags);
+static inline bool esw_vst_mode_is_steering(struct mlx5_eswitch *esw)
+{
+ return (MLX5_CAP_ESW_EGRESS_ACL(esw->dev, pop_vlan) &&
+ MLX5_CAP_ESW_INGRESS_ACL(esw->dev, push_vlan));
+}
+
static inline bool mlx5_eswitch_vlan_actions_supported(struct mlx5_core_dev *dev,
u8 vlan_depth)
{
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index e455b215c70885..c981fa77f43985 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -143,7 +143,7 @@ mlx5_eswitch_set_rule_source_port(struct mlx5_eswitch *esw,
if (mlx5_esw_indir_table_decap_vport(attr))
vport = mlx5_esw_indir_table_decap_vport(attr);
- if (attr && !attr->chain && esw_attr->int_port)
+ if (!attr->chain && esw_attr && esw_attr->int_port)
metadata =
mlx5e_tc_int_port_get_metadata_for_match(esw_attr->int_port);
else
@@ -4143,8 +4143,6 @@ int mlx5_devlink_port_fn_migratable_set(struct devlink_port *port, bool enable,
}
hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability);
- memcpy(hca_caps, MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability),
- MLX5_UN_SZ_BYTES(hca_cap_union));
MLX5_SET(cmd_hca_cap_2, hca_caps, migratable, 1);
err = mlx5_vport_set_other_func_cap(esw->dev, hca_caps, vport->vport,
@@ -4236,8 +4234,6 @@ int mlx5_devlink_port_fn_roce_set(struct devlink_port *port, bool enable,
}
hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability);
- memcpy(hca_caps, MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability),
- MLX5_UN_SZ_BYTES(hca_cap_union));
MLX5_SET(cmd_hca_cap, hca_caps, roce, enable);
err = mlx5_vport_set_other_func_cap(esw->dev, hca_caps, vport_num,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/health.c b/drivers/net/ethernet/mellanox/mlx5/core/health.c
index 86ed87d704f7d7..879555ba847ddc 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/health.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/health.c
@@ -674,6 +674,13 @@ static void mlx5_fw_fatal_reporter_err_work(struct work_struct *work)
dev = container_of(priv, struct mlx5_core_dev, priv);
devlink = priv_to_devlink(dev);
+ mutex_lock(&dev->intf_state_mutex);
+ if (test_bit(MLX5_DROP_NEW_HEALTH_WORK, &health->flags)) {
+ mlx5_core_err(dev, "health works are not permitted at this stage\n");
+ mutex_unlock(&dev->intf_state_mutex);
+ return;
+ }
+ mutex_unlock(&dev->intf_state_mutex);
enter_error_state(dev, false);
if (IS_ERR_OR_NULL(health->fw_fatal_reporter)) {
devl_lock(devlink);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c
index c247cca154e9c6..e09518f887a043 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c
@@ -90,9 +90,21 @@ static void mlx5i_get_ringparam(struct net_device *dev,
static int mlx5i_set_channels(struct net_device *dev,
struct ethtool_channels *ch)
{
- struct mlx5e_priv *priv = mlx5i_epriv(dev);
+ struct mlx5i_priv *ipriv = netdev_priv(dev);
+ struct mlx5e_priv *epriv = mlx5i_epriv(dev);
+
+ /* rtnl lock protects from race between this ethtool op and sub
+ * interface ndo_init/uninit.
+ */
+ ASSERT_RTNL();
+ if (ipriv->num_sub_interfaces > 0) {
+ mlx5_core_warn(epriv->mdev,
+ "can't change number of channels for interfaces with sub interfaces (%u)\n",
+ ipriv->num_sub_interfaces);
+ return -EINVAL;
+ }
- return mlx5e_ethtool_set_channels(priv, ch);
+ return mlx5e_ethtool_set_channels(epriv, ch);
}
static void mlx5i_get_channels(struct net_device *dev,
@@ -177,16 +189,16 @@ static inline int mlx5_ptys_rate_enum_to_int(enum mlx5_ptys_rate rate)
}
}
-static int mlx5i_get_speed_settings(u16 ib_link_width_oper, u16 ib_proto_oper)
+static u32 mlx5i_get_speed_settings(u16 ib_link_width_oper, u16 ib_proto_oper)
{
int rate, width;
rate = mlx5_ptys_rate_enum_to_int(ib_proto_oper);
if (rate < 0)
- return -EINVAL;
+ return SPEED_UNKNOWN;
width = mlx5_ptys_width_enum_to_int(ib_link_width_oper);
if (width < 0)
- return -EINVAL;
+ return SPEED_UNKNOWN;
return rate * width;
}
@@ -209,16 +221,13 @@ static int mlx5i_get_link_ksettings(struct net_device *netdev,
ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising);
speed = mlx5i_get_speed_settings(ib_link_width_oper, ib_proto_oper);
- if (speed < 0)
- return -EINVAL;
+ link_ksettings->base.speed = speed;
+ link_ksettings->base.duplex = speed == SPEED_UNKNOWN ? DUPLEX_UNKNOWN : DUPLEX_FULL;
- link_ksettings->base.duplex = DUPLEX_FULL;
link_ksettings->base.port = PORT_OTHER;
link_ksettings->base.autoneg = AUTONEG_DISABLE;
- link_ksettings->base.speed = speed;
-
return 0;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
index 7c5c500fd215e1..911cf4d2396454 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c
@@ -71,6 +71,10 @@ static void mlx5i_build_nic_params(struct mlx5_core_dev *mdev,
params->packet_merge.type = MLX5E_PACKET_MERGE_NONE;
params->hard_mtu = MLX5_IB_GRH_BYTES + MLX5_IPOIB_HARD_LEN;
params->tunneled_offload_en = false;
+
+ /* CQE compression is not supported for IPoIB */
+ params->rx_cqe_compress_def = false;
+ MLX5E_SET_PFLAG(params, MLX5E_PFLAG_RX_CQE_COMPRESS, params->rx_cqe_compress_def);
}
/* Called directly after IPoIB netdevice was created to initialize SW structs */
@@ -156,6 +160,44 @@ void mlx5i_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats)
stats->tx_dropped = sstats->tx_queue_dropped;
}
+struct net_device *mlx5i_parent_get(struct net_device *netdev)
+{
+ struct mlx5e_priv *priv = mlx5i_epriv(netdev);
+ struct mlx5i_priv *ipriv, *parent_ipriv;
+ struct net_device *parent_dev;
+ int parent_ifindex;
+
+ ipriv = priv->ppriv;
+
+ parent_ifindex = netdev->netdev_ops->ndo_get_iflink(netdev);
+ parent_dev = dev_get_by_index(dev_net(netdev), parent_ifindex);
+ if (!parent_dev)
+ return NULL;
+
+ parent_ipriv = netdev_priv(parent_dev);
+
+ ASSERT_RTNL();
+ parent_ipriv->num_sub_interfaces++;
+
+ ipriv->parent_dev = parent_dev;
+
+ return parent_dev;
+}
+
+void mlx5i_parent_put(struct net_device *netdev)
+{
+ struct mlx5e_priv *priv = mlx5i_epriv(netdev);
+ struct mlx5i_priv *ipriv, *parent_ipriv;
+
+ ipriv = priv->ppriv;
+ parent_ipriv = netdev_priv(ipriv->parent_dev);
+
+ ASSERT_RTNL();
+ parent_ipriv->num_sub_interfaces--;
+
+ dev_put(ipriv->parent_dev);
+}
+
int mlx5i_init_underlay_qp(struct mlx5e_priv *priv)
{
struct mlx5_core_dev *mdev = priv->mdev;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h
index 99d46fda9f82fa..f3f2af972020af 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.h
@@ -54,9 +54,11 @@ struct mlx5i_priv {
struct rdma_netdev rn; /* keep this first */
u32 qpn;
bool sub_interface;
+ u32 num_sub_interfaces;
u32 qkey;
u16 pkey_index;
struct mlx5i_pkey_qpn_ht *qpn_htbl;
+ struct net_device *parent_dev;
char *mlx5e_priv[];
};
@@ -117,5 +119,9 @@ void mlx5i_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb,
struct mlx5_av *av, u32 dqpn, u32 dqkey, bool xmit_more);
void mlx5i_get_stats(struct net_device *dev, struct rtnl_link_stats64 *stats);
+/* Reference management for child to parent interfaces. */
+struct net_device *mlx5i_parent_get(struct net_device *netdev);
+void mlx5i_parent_put(struct net_device *netdev);
+
#endif /* CONFIG_MLX5_CORE_IPOIB */
#endif /* __MLX5E_IPOB_H__ */
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c
index 4d9c9e49645c9e..03e681297937f8 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib_vlan.c
@@ -158,21 +158,28 @@ static int mlx5i_pkey_dev_init(struct net_device *dev)
struct mlx5e_priv *priv = mlx5i_epriv(dev);
struct mlx5i_priv *ipriv, *parent_ipriv;
struct net_device *parent_dev;
- int parent_ifindex;
ipriv = priv->ppriv;
- /* Get QPN to netdevice hash table from parent */
- parent_ifindex = dev->netdev_ops->ndo_get_iflink(dev);
- parent_dev = dev_get_by_index(dev_net(dev), parent_ifindex);
+ /* Link to parent */
+ parent_dev = mlx5i_parent_get(dev);
if (!parent_dev) {
mlx5_core_warn(priv->mdev, "failed to get parent device\n");
return -EINVAL;
}
+ if (dev->num_rx_queues < parent_dev->real_num_rx_queues) {
+ mlx5_core_warn(priv->mdev,
+ "failed to create child device with rx queues [%d] less than parent's [%d]\n",
+ dev->num_rx_queues,
+ parent_dev->real_num_rx_queues);
+ mlx5i_parent_put(dev);
+ return -EINVAL;
+ }
+
+ /* Get QPN to netdevice hash table from parent */
parent_ipriv = netdev_priv(parent_dev);
ipriv->qpn_htbl = parent_ipriv->qpn_htbl;
- dev_put(parent_dev);
return mlx5i_dev_init(dev);
}
@@ -184,6 +191,7 @@ static int mlx5i_pkey_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
static void mlx5i_pkey_dev_cleanup(struct net_device *netdev)
{
+ mlx5i_parent_put(netdev);
return mlx5i_dev_cleanup(netdev);
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
index 32c3e0a649a753..ad32b80e850185 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag/lag.c
@@ -228,6 +228,7 @@ static void mlx5_ldev_free(struct kref *ref)
if (ldev->nb.notifier_call)
unregister_netdevice_notifier_net(&init_net, &ldev->nb);
mlx5_lag_mp_cleanup(ldev);
+ cancel_delayed_work_sync(&ldev->bond_work);
destroy_workqueue(ldev->wq);
mlx5_lag_mpesw_cleanup(ldev);
mutex_destroy(&ldev->lock);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c
index 69cfe60c558a74..69318b14326881 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c
@@ -681,7 +681,7 @@ static int mlx5_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
static const struct ptp_clock_info mlx5_ptp_clock_info = {
.owner = THIS_MODULE,
.name = "mlx5_ptp",
- .max_adj = 100000000,
+ .max_adj = 50000000,
.n_alarm = 0,
.n_ext_ts = 0,
.n_per_out = 0,
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
index 7f5db13e3550c0..4e1b5757528a0b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
@@ -613,7 +613,7 @@ static int handle_hca_cap(struct mlx5_core_dev *dev, void *set_ctx)
MLX5_SET(cmd_hca_cap, set_hca_cap, num_total_dynamic_vf_msix,
MLX5_CAP_GEN_MAX(dev, num_total_dynamic_vf_msix));
- if (MLX5_CAP_GEN(dev, roce_rw_supported))
+ if (MLX5_CAP_GEN(dev, roce_rw_supported) && MLX5_CAP_GEN_MAX(dev, roce))
MLX5_SET(cmd_hca_cap, set_hca_cap, roce,
mlx5_is_roce_on(dev));
@@ -1050,6 +1050,8 @@ err_rl_cleanup:
err_tables_cleanup:
mlx5_geneve_destroy(dev->geneve);
mlx5_vxlan_destroy(dev->vxlan);
+ mlx5_cleanup_clock(dev);
+ mlx5_cleanup_reserved_gids(dev);
mlx5_cq_debugfs_cleanup(dev);
mlx5_fw_reset_cleanup(dev);
err_events_cleanup:
@@ -2096,7 +2098,7 @@ static void mlx5_core_verify_params(void)
}
}
-static int __init init(void)
+static int __init mlx5_init(void)
{
int err;
@@ -2108,7 +2110,7 @@ static int __init init(void)
mlx5_core_verify_params();
mlx5_register_debugfs();
- err = pci_register_driver(&mlx5_core_driver);
+ err = mlx5e_init();
if (err)
goto err_debug;
@@ -2116,28 +2118,28 @@ static int __init init(void)
if (err)
goto err_sf;
- err = mlx5e_init();
+ err = pci_register_driver(&mlx5_core_driver);
if (err)
- goto err_en;
+ goto err_pci;
return 0;
-err_en:
+err_pci:
mlx5_sf_driver_unregister();
err_sf:
- pci_unregister_driver(&mlx5_core_driver);
+ mlx5e_cleanup();
err_debug:
mlx5_unregister_debugfs();
return err;
}
-static void __exit cleanup(void)
+static void __exit mlx5_cleanup(void)
{
- mlx5e_cleanup();
- mlx5_sf_driver_unregister();
pci_unregister_driver(&mlx5_core_driver);
+ mlx5_sf_driver_unregister();
+ mlx5e_cleanup();
mlx5_unregister_debugfs();
}
-module_init(init);
-module_exit(cleanup);
+module_init(mlx5_init);
+module_exit(mlx5_cleanup);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
index 60596357bfc7ae..0eb50be175cc4b 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
@@ -74,6 +74,14 @@ static u32 get_function(u16 func_id, bool ec_function)
return (u32)func_id | (ec_function << 16);
}
+static u16 func_id_to_type(struct mlx5_core_dev *dev, u16 func_id, bool ec_function)
+{
+ if (!func_id)
+ return mlx5_core_is_ecpf(dev) && !ec_function ? MLX5_HOST_PF : MLX5_PF;
+
+ return func_id <= mlx5_core_max_vfs(dev) ? MLX5_VF : MLX5_SF;
+}
+
static struct rb_root *page_root_per_function(struct mlx5_core_dev *dev, u32 function)
{
struct rb_root *root;
@@ -332,6 +340,7 @@ static int give_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
u32 out[MLX5_ST_SZ_DW(manage_pages_out)] = {0};
int inlen = MLX5_ST_SZ_BYTES(manage_pages_in);
int notify_fail = event;
+ u16 func_type;
u64 addr;
int err;
u32 *in;
@@ -383,11 +392,9 @@ retry:
goto out_dropped;
}
+ func_type = func_id_to_type(dev, func_id, ec_function);
+ dev->priv.page_counters[func_type] += npages;
dev->priv.fw_pages += npages;
- if (func_id)
- dev->priv.vfs_pages += npages;
- else if (mlx5_core_is_ecpf(dev) && !ec_function)
- dev->priv.host_pf_pages += npages;
mlx5_core_dbg(dev, "npages %d, ec_function %d, func_id 0x%x, err %d\n",
npages, ec_function, func_id, err);
@@ -414,6 +421,7 @@ static void release_all_pages(struct mlx5_core_dev *dev, u16 func_id,
struct rb_root *root;
struct rb_node *p;
int npages = 0;
+ u16 func_type;
root = xa_load(&dev->priv.page_root_xa, function);
if (WARN_ON_ONCE(!root))
@@ -428,11 +436,9 @@ static void release_all_pages(struct mlx5_core_dev *dev, u16 func_id,
free_fwp(dev, fwp, fwp->free_count);
}
+ func_type = func_id_to_type(dev, func_id, ec_function);
+ dev->priv.page_counters[func_type] -= npages;
dev->priv.fw_pages -= npages;
- if (func_id)
- dev->priv.vfs_pages -= npages;
- else if (mlx5_core_is_ecpf(dev) && !ec_function)
- dev->priv.host_pf_pages -= npages;
mlx5_core_dbg(dev, "npages %d, ec_function %d, func_id 0x%x\n",
npages, ec_function, func_id);
@@ -498,6 +504,7 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
int outlen = MLX5_ST_SZ_BYTES(manage_pages_out);
u32 in[MLX5_ST_SZ_DW(manage_pages_in)] = {};
int num_claimed;
+ u16 func_type;
u32 *out;
int err;
int i;
@@ -549,11 +556,9 @@ static int reclaim_pages(struct mlx5_core_dev *dev, u16 func_id, int npages,
if (nclaimed)
*nclaimed = num_claimed;
+ func_type = func_id_to_type(dev, func_id, ec_function);
+ dev->priv.page_counters[func_type] -= num_claimed;
dev->priv.fw_pages -= num_claimed;
- if (func_id)
- dev->priv.vfs_pages -= num_claimed;
- else if (mlx5_core_is_ecpf(dev) && !ec_function)
- dev->priv.host_pf_pages -= num_claimed;
out_free:
kvfree(out);
@@ -706,12 +711,12 @@ int mlx5_reclaim_startup_pages(struct mlx5_core_dev *dev)
WARN(dev->priv.fw_pages,
"FW pages counter is %d after reclaiming all pages\n",
dev->priv.fw_pages);
- WARN(dev->priv.vfs_pages,
+ WARN(dev->priv.page_counters[MLX5_VF],
"VFs FW pages counter is %d after reclaiming all pages\n",
- dev->priv.vfs_pages);
- WARN(dev->priv.host_pf_pages,
+ dev->priv.page_counters[MLX5_VF]);
+ WARN(dev->priv.page_counters[MLX5_HOST_PF],
"External host PF FW pages counter is %d after reclaiming all pages\n",
- dev->priv.host_pf_pages);
+ dev->priv.page_counters[MLX5_HOST_PF]);
return 0;
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qos.c b/drivers/net/ethernet/mellanox/mlx5/core/qos.c
index 0777be24a3074a..8bce730b5c5bef 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/qos.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/qos.c
@@ -62,13 +62,12 @@ int mlx5_qos_create_root_node(struct mlx5_core_dev *mdev, u32 *id)
return mlx5_qos_create_inner_node(mdev, MLX5_QOS_DEFAULT_DWRR_UID, 0, 0, id);
}
-int mlx5_qos_update_node(struct mlx5_core_dev *mdev, u32 parent_id,
+int mlx5_qos_update_node(struct mlx5_core_dev *mdev,
u32 bw_share, u32 max_avg_bw, u32 id)
{
u32 sched_ctx[MLX5_ST_SZ_DW(scheduling_context)] = {0};
u32 bitmask = 0;
- MLX5_SET(scheduling_context, sched_ctx, parent_element_id, parent_id);
MLX5_SET(scheduling_context, sched_ctx, bw_share, bw_share);
MLX5_SET(scheduling_context, sched_ctx, max_average_bw, max_avg_bw);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/qos.h b/drivers/net/ethernet/mellanox/mlx5/core/qos.h
index 125e4e47e6f71f..624ce822b7f596 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/qos.h
+++ b/drivers/net/ethernet/mellanox/mlx5/core/qos.h
@@ -23,7 +23,7 @@ int mlx5_qos_create_leaf_node(struct mlx5_core_dev *mdev, u32 parent_id,
int mlx5_qos_create_inner_node(struct mlx5_core_dev *mdev, u32 parent_id,
u32 bw_share, u32 max_avg_bw, u32 *id);
int mlx5_qos_create_root_node(struct mlx5_core_dev *mdev, u32 *id);
-int mlx5_qos_update_node(struct mlx5_core_dev *mdev, u32 parent_id, u32 bw_share,
+int mlx5_qos_update_node(struct mlx5_core_dev *mdev, u32 bw_share,
u32 max_avg_bw, u32 id);
int mlx5_qos_destroy_node(struct mlx5_core_dev *mdev, u32 id);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c
index c0e6c487c63c1e..3008e9ce2bbff3 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c
@@ -147,7 +147,7 @@ mlx5_device_disable_sriov(struct mlx5_core_dev *dev, int num_vfs, bool clear_vf)
mlx5_eswitch_disable_sriov(dev->priv.eswitch, clear_vf);
- if (mlx5_wait_for_pages(dev, &dev->priv.vfs_pages))
+ if (mlx5_wait_for_pages(dev, &dev->priv.page_counters[MLX5_VF]))
mlx5_core_warn(dev, "timeout reclaiming VFs pages\n");
}
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c
index 74cbe53ee9dbb1..042ca034912433 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_rule.c
@@ -3,7 +3,12 @@
#include "dr_types.h"
+#if defined(CONFIG_FRAME_WARN) && (CONFIG_FRAME_WARN < 2048)
+/* don't try to optimize STE allocation if the stack is too constaraining */
+#define DR_RULE_MAX_STES_OPTIMIZED 0
+#else
#define DR_RULE_MAX_STES_OPTIMIZED 5
+#endif
#define DR_RULE_MAX_STE_CHAIN_OPTIMIZED (DR_RULE_MAX_STES_OPTIMIZED + DR_ACTION_MAX_STES)
static int dr_rule_append_to_miss_list(struct mlx5dr_domain *dmn,
@@ -1133,12 +1138,14 @@ dr_rule_create_rule_nic(struct mlx5dr_rule *rule,
rule->flow_source))
return 0;
+ mlx5dr_domain_nic_lock(nic_dmn);
+
ret = mlx5dr_matcher_select_builders(matcher,
nic_matcher,
dr_rule_get_ipv(&param->outer),
dr_rule_get_ipv(&param->inner));
if (ret)
- return ret;
+ goto err_unlock;
hw_ste_arr_is_opt = nic_matcher->num_of_builders <= DR_RULE_MAX_STES_OPTIMIZED;
if (likely(hw_ste_arr_is_opt)) {
@@ -1147,12 +1154,12 @@ dr_rule_create_rule_nic(struct mlx5dr_rule *rule,
hw_ste_arr = kzalloc((nic_matcher->num_of_builders + DR_ACTION_MAX_STES) *
DR_STE_SIZE, GFP_KERNEL);
- if (!hw_ste_arr)
- return -ENOMEM;
+ if (!hw_ste_arr) {
+ ret = -ENOMEM;
+ goto err_unlock;
+ }
}
- mlx5dr_domain_nic_lock(nic_dmn);
-
ret = mlx5dr_matcher_add_to_tbl_nic(dmn, nic_matcher);
if (ret)
goto free_hw_ste;
@@ -1236,11 +1243,12 @@ remove_from_nic_tbl:
mlx5dr_matcher_remove_from_tbl_nic(dmn, nic_matcher);
free_hw_ste:
- mlx5dr_domain_nic_unlock(nic_dmn);
-
- if (unlikely(!hw_ste_arr_is_opt))
+ if (!hw_ste_arr_is_opt)
kfree(hw_ste_arr);
+err_unlock:
+ mlx5dr_domain_nic_unlock(nic_dmn);
+
return ret;
}
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
index c22c3ac4e2a18d..09e32778b012d1 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
@@ -2951,7 +2951,7 @@ struct mlxsw_sp_nexthop_group_info {
gateway:1, /* routes using the group use a gateway */
is_resilient:1;
struct list_head list; /* member in nh_res_grp_list */
- struct mlxsw_sp_nexthop nexthops[0];
+ struct mlxsw_sp_nexthop nexthops[];
#define nh_rif nexthops[0].rif
};
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c
index 5314c064ceae73..55b484b1056275 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_fdma.c
@@ -608,12 +608,12 @@ allocate_new:
lan966x_fdma_rx_reload(rx);
}
- if (counter < weight && napi_complete_done(napi, counter))
- lan_wr(0xff, lan966x, FDMA_INTR_DB_ENA);
-
if (redirect)
xdp_do_flush();
+ if (counter < weight && napi_complete_done(napi, counter))
+ lan_wr(0xff, lan966x, FDMA_INTR_DB_ENA);
+
return counter;
}
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
index cadde20505ba06..580c91d24a5284 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c
@@ -1043,11 +1043,6 @@ static int lan966x_probe(struct platform_device *pdev)
lan966x->base_mac[5] &= 0xf0;
}
- ports = device_get_named_child_node(&pdev->dev, "ethernet-ports");
- if (!ports)
- return dev_err_probe(&pdev->dev, -ENODEV,
- "no ethernet-ports child found\n");
-
err = lan966x_create_targets(pdev, lan966x);
if (err)
return dev_err_probe(&pdev->dev, err,
@@ -1125,6 +1120,11 @@ static int lan966x_probe(struct platform_device *pdev)
}
}
+ ports = device_get_named_child_node(&pdev->dev, "ethernet-ports");
+ if (!ports)
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "no ethernet-ports child found\n");
+
/* init switch */
lan966x_init(lan966x);
lan966x_stats_init(lan966x);
@@ -1162,6 +1162,8 @@ static int lan966x_probe(struct platform_device *pdev)
goto cleanup_ports;
}
+ fwnode_handle_put(ports);
+
lan966x_mdb_init(lan966x);
err = lan966x_fdb_init(lan966x);
if (err)
@@ -1191,6 +1193,7 @@ cleanup_fdb:
lan966x_fdb_deinit(lan966x);
cleanup_ports:
+ fwnode_handle_put(ports);
fwnode_handle_put(portnp);
lan966x_cleanup_ports(lan966x);
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c
index 1a61c6cdb0779f..0050fcb988b75e 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_port.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_port.c
@@ -381,7 +381,7 @@ int lan966x_port_pcs_set(struct lan966x_port *port,
}
/* Take PCS out of reset */
- lan_rmw(DEV_CLOCK_CFG_LINK_SPEED_SET(2) |
+ lan_rmw(DEV_CLOCK_CFG_LINK_SPEED_SET(LAN966X_SPEED_1000) |
DEV_CLOCK_CFG_PCS_RX_RST_SET(0) |
DEV_CLOCK_CFG_PCS_TX_RST_SET(0),
DEV_CLOCK_CFG_LINK_SPEED |
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c
index f9ebfaafbebc26..a8348437dd87f6 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_ptp.c
@@ -1073,6 +1073,9 @@ void lan966x_ptp_deinit(struct lan966x *lan966x)
struct lan966x_port *port;
int i;
+ if (!lan966x->ptp)
+ return;
+
for (i = 0; i < lan966x->num_phys_ports; i++) {
port = lan966x->ports[i];
if (!port)
diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c
index d8dc9fbb81e1a7..a54c0426a35f39 100644
--- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c
+++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c
@@ -95,10 +95,7 @@ lan966x_vcap_is2_get_port_keysets(struct net_device *dev, int lookup,
bool found = false;
u32 val;
- /* Check if the port keyset selection is enabled */
val = lan_rd(lan966x, ANA_VCAP_S2_CFG(port->chip_port));
- if (!ANA_VCAP_S2_CFG_ENA_GET(val))
- return -ENOENT;
/* Collect all keysets for the port in a list */
if (l3_proto == ETH_P_ALL)
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
index d25f4f09faa06a..3c5d4fe9937375 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_main.c
@@ -834,7 +834,7 @@ static int mchp_sparx5_probe(struct platform_device *pdev)
if (err)
goto cleanup_config;
- if (!of_get_mac_address(np, sparx5->base_mac)) {
+ if (of_get_mac_address(np, sparx5->base_mac)) {
dev_info(sparx5->dev, "MAC addr was not set, use random MAC\n");
eth_random_addr(sparx5->base_mac);
sparx5->base_mac[5] = 0;
diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c
index 0ed1ea7727c54e..69e76634f9aa83 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_ptp.c
@@ -633,7 +633,7 @@ int sparx5_ptp_init(struct sparx5 *sparx5)
/* Enable master counters */
spx5_wr(PTP_PTP_DOM_CFG_PTP_ENA_SET(0x7), sparx5, PTP_PTP_DOM_CFG);
- for (i = 0; i < sparx5->port_count; i++) {
+ for (i = 0; i < SPX5_PORTS; i++) {
port = sparx5->ports[i];
if (!port)
continue;
@@ -649,7 +649,7 @@ void sparx5_ptp_deinit(struct sparx5 *sparx5)
struct sparx5_port *port;
int i;
- for (i = 0; i < sparx5->port_count; i++) {
+ for (i = 0; i < SPX5_PORTS; i++) {
port = sparx5->ports[i];
if (!port)
continue;
diff --git a/drivers/net/ethernet/microsoft/mana/gdma_main.c b/drivers/net/ethernet/microsoft/mana/gdma_main.c
index e708c2d049839f..f9b8f372ec8a74 100644
--- a/drivers/net/ethernet/microsoft/mana/gdma_main.c
+++ b/drivers/net/ethernet/microsoft/mana/gdma_main.c
@@ -1217,9 +1217,7 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev)
unsigned int max_queues_per_port = num_online_cpus();
struct gdma_context *gc = pci_get_drvdata(pdev);
struct gdma_irq_context *gic;
- unsigned int max_irqs;
- u16 *cpus;
- cpumask_var_t req_mask;
+ unsigned int max_irqs, cpu;
int nvec, irq;
int err, i = 0, j;
@@ -1240,39 +1238,31 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev)
goto free_irq_vector;
}
- if (!zalloc_cpumask_var(&req_mask, GFP_KERNEL)) {
- err = -ENOMEM;
- goto free_irq;
- }
-
- cpus = kcalloc(nvec, sizeof(*cpus), GFP_KERNEL);
- if (!cpus) {
- err = -ENOMEM;
- goto free_mask;
- }
- for (i = 0; i < nvec; i++)
- cpus[i] = cpumask_local_spread(i, gc->numa_node);
-
for (i = 0; i < nvec; i++) {
- cpumask_set_cpu(cpus[i], req_mask);
gic = &gc->irq_contexts[i];
gic->handler = NULL;
gic->arg = NULL;
+ if (!i)
+ snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_hwc@pci:%s",
+ pci_name(pdev));
+ else
+ snprintf(gic->name, MANA_IRQ_NAME_SZ, "mana_q%d@pci:%s",
+ i - 1, pci_name(pdev));
+
irq = pci_irq_vector(pdev, i);
if (irq < 0) {
err = irq;
- goto free_mask;
+ goto free_irq;
}
- err = request_irq(irq, mana_gd_intr, 0, "mana_intr", gic);
+ err = request_irq(irq, mana_gd_intr, 0, gic->name, gic);
if (err)
- goto free_mask;
- irq_set_affinity_and_hint(irq, req_mask);
- cpumask_clear(req_mask);
+ goto free_irq;
+
+ cpu = cpumask_local_spread(i, gc->numa_node);
+ irq_set_affinity_and_hint(irq, cpumask_of(cpu));
}
- free_cpumask_var(req_mask);
- kfree(cpus);
err = mana_gd_alloc_res_map(nvec, &gc->msix_resource);
if (err)
@@ -1283,13 +1273,12 @@ static int mana_gd_setup_irqs(struct pci_dev *pdev)
return 0;
-free_mask:
- free_cpumask_var(req_mask);
- kfree(cpus);
free_irq:
for (j = i - 1; j >= 0; j--) {
irq = pci_irq_vector(pdev, j);
gic = &gc->irq_contexts[j];
+
+ irq_update_affinity_hint(irq, NULL);
free_irq(irq, gic);
}
@@ -1317,6 +1306,9 @@ static void mana_gd_remove_irqs(struct pci_dev *pdev)
continue;
gic = &gc->irq_contexts[i];
+
+ /* Need to clear the hint before free_irq */
+ irq_update_affinity_hint(irq, NULL);
free_irq(irq, gic);
}
diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c b/drivers/net/ethernet/mscc/ocelot_flower.c
index 7c0897e779dc6c..ee052404eb55ae 100644
--- a/drivers/net/ethernet/mscc/ocelot_flower.c
+++ b/drivers/net/ethernet/mscc/ocelot_flower.c
@@ -605,6 +605,18 @@ ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress,
flow_rule_match_control(rule, &match);
}
+ if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
+ struct flow_match_vlan match;
+
+ flow_rule_match_vlan(rule, &match);
+ filter->key_type = OCELOT_VCAP_KEY_ANY;
+ filter->vlan.vid.value = match.key->vlan_id;
+ filter->vlan.vid.mask = match.mask->vlan_id;
+ filter->vlan.pcp.value[0] = match.key->vlan_priority;
+ filter->vlan.pcp.mask[0] = match.mask->vlan_priority;
+ match_protocol = false;
+ }
+
if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
struct flow_match_eth_addrs match;
@@ -737,18 +749,6 @@ ocelot_flower_parse_key(struct ocelot *ocelot, int port, bool ingress,
match_protocol = false;
}
- if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
- struct flow_match_vlan match;
-
- flow_rule_match_vlan(rule, &match);
- filter->key_type = OCELOT_VCAP_KEY_ANY;
- filter->vlan.vid.value = match.key->vlan_id;
- filter->vlan.vid.mask = match.mask->vlan_id;
- filter->vlan.pcp.value[0] = match.key->vlan_priority;
- filter->vlan.pcp.mask[0] = match.mask->vlan_priority;
- match_protocol = false;
- }
-
finished_key_parsing:
if (match_protocol && proto != ETH_P_ALL) {
if (filter->block_id == VCAP_ES0) {
diff --git a/drivers/net/ethernet/mscc/ocelot_ptp.c b/drivers/net/ethernet/mscc/ocelot_ptp.c
index 1a82f10c885394..2180ae94c74476 100644
--- a/drivers/net/ethernet/mscc/ocelot_ptp.c
+++ b/drivers/net/ethernet/mscc/ocelot_ptp.c
@@ -335,8 +335,8 @@ static void
ocelot_populate_ipv6_ptp_event_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_IPV6;
- trap->key.ipv4.proto.value[0] = IPPROTO_UDP;
- trap->key.ipv4.proto.mask[0] = 0xff;
+ trap->key.ipv6.proto.value[0] = IPPROTO_UDP;
+ trap->key.ipv6.proto.mask[0] = 0xff;
trap->key.ipv6.dport.value = PTP_EV_PORT;
trap->key.ipv6.dport.mask = 0xffff;
}
@@ -355,8 +355,8 @@ static void
ocelot_populate_ipv6_ptp_general_trap_key(struct ocelot_vcap_filter *trap)
{
trap->key_type = OCELOT_VCAP_KEY_IPV6;
- trap->key.ipv4.proto.value[0] = IPPROTO_UDP;
- trap->key.ipv4.proto.mask[0] = 0xff;
+ trap->key.ipv6.proto.value[0] = IPPROTO_UDP;
+ trap->key.ipv6.proto.mask[0] = 0xff;
trap->key.ipv6.dport.value = PTP_GEN_PORT;
trap->key.ipv6.dport.mask = 0xffff;
}
diff --git a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
index a8678d5612eeaf..060a77f2265d9a 100644
--- a/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
+++ b/drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c
@@ -460,6 +460,7 @@ nfp_tun_write_neigh(struct net_device *netdev, struct nfp_app *app,
sizeof(struct nfp_tun_neigh_v4);
unsigned long cookie = (unsigned long)neigh;
struct nfp_flower_priv *priv = app->priv;
+ struct nfp_tun_neigh_lag lag_info;
struct nfp_neigh_entry *nn_entry;
u32 port_id;
u8 mtype;
@@ -468,6 +469,11 @@ nfp_tun_write_neigh(struct net_device *netdev, struct nfp_app *app,
if (!port_id)
return;
+ if ((port_id & NFP_FL_LAG_OUT) == NFP_FL_LAG_OUT) {
+ memset(&lag_info, 0, sizeof(struct nfp_tun_neigh_lag));
+ nfp_flower_lag_get_info_from_netdev(app, netdev, &lag_info);
+ }
+
spin_lock_bh(&priv->predt_lock);
nn_entry = rhashtable_lookup_fast(&priv->neigh_table, &cookie,
neigh_table_params);
@@ -515,7 +521,7 @@ nfp_tun_write_neigh(struct net_device *netdev, struct nfp_app *app,
neigh_ha_snapshot(common->dst_addr, neigh, netdev);
if ((port_id & NFP_FL_LAG_OUT) == NFP_FL_LAG_OUT)
- nfp_flower_lag_get_info_from_netdev(app, netdev, lag);
+ memcpy(lag, &lag_info, sizeof(struct nfp_tun_neigh_lag));
common->port_id = cpu_to_be32(port_id);
if (rhashtable_insert_fast(&priv->neigh_table,
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h
index da33f09facb916..432d79d691c295 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h
@@ -617,6 +617,9 @@ struct nfp_net_dp {
* @vnic_no_name: For non-port PF vNIC make ndo_get_phys_port_name return
* -EOPNOTSUPP to keep backwards compatibility (set by app)
* @port: Pointer to nfp_port structure if vNIC is a port
+ * @mc_lock: Protect mc_addrs list
+ * @mc_addrs: List of mc addrs to add/del to HW
+ * @mc_work: Work to update mc addrs
* @app_priv: APP private data for this vNIC
*/
struct nfp_net {
@@ -718,6 +721,10 @@ struct nfp_net {
struct nfp_port *port;
+ spinlock_t mc_lock;
+ struct list_head mc_addrs;
+ struct work_struct mc_work;
+
void *app_priv;
};
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index 09053373288fe7..18fc9971f1c8f2 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -1334,9 +1334,14 @@ err_unlock:
return err;
}
-static int nfp_net_mc_cfg(struct net_device *netdev, const unsigned char *addr, const u32 cmd)
+struct nfp_mc_addr_entry {
+ u8 addr[ETH_ALEN];
+ u32 cmd;
+ struct list_head list;
+};
+
+static int nfp_net_mc_cfg(struct nfp_net *nn, const unsigned char *addr, const u32 cmd)
{
- struct nfp_net *nn = netdev_priv(netdev);
int ret;
ret = nfp_net_mbox_lock(nn, NFP_NET_CFG_MULTICAST_SZ);
@@ -1351,6 +1356,25 @@ static int nfp_net_mc_cfg(struct net_device *netdev, const unsigned char *addr,
return nfp_net_mbox_reconfig_and_unlock(nn, cmd);
}
+static int nfp_net_mc_prep(struct nfp_net *nn, const unsigned char *addr, const u32 cmd)
+{
+ struct nfp_mc_addr_entry *entry;
+
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry)
+ return -ENOMEM;
+
+ ether_addr_copy(entry->addr, addr);
+ entry->cmd = cmd;
+ spin_lock_bh(&nn->mc_lock);
+ list_add_tail(&entry->list, &nn->mc_addrs);
+ spin_unlock_bh(&nn->mc_lock);
+
+ schedule_work(&nn->mc_work);
+
+ return 0;
+}
+
static int nfp_net_mc_sync(struct net_device *netdev, const unsigned char *addr)
{
struct nfp_net *nn = netdev_priv(netdev);
@@ -1361,12 +1385,35 @@ static int nfp_net_mc_sync(struct net_device *netdev, const unsigned char *addr)
return -EINVAL;
}
- return nfp_net_mc_cfg(netdev, addr, NFP_NET_CFG_MBOX_CMD_MULTICAST_ADD);
+ return nfp_net_mc_prep(nn, addr, NFP_NET_CFG_MBOX_CMD_MULTICAST_ADD);
}
static int nfp_net_mc_unsync(struct net_device *netdev, const unsigned char *addr)
{
- return nfp_net_mc_cfg(netdev, addr, NFP_NET_CFG_MBOX_CMD_MULTICAST_DEL);
+ struct nfp_net *nn = netdev_priv(netdev);
+
+ return nfp_net_mc_prep(nn, addr, NFP_NET_CFG_MBOX_CMD_MULTICAST_DEL);
+}
+
+static void nfp_net_mc_addr_config(struct work_struct *work)
+{
+ struct nfp_net *nn = container_of(work, struct nfp_net, mc_work);
+ struct nfp_mc_addr_entry *entry, *tmp;
+ struct list_head tmp_list;
+
+ INIT_LIST_HEAD(&tmp_list);
+
+ spin_lock_bh(&nn->mc_lock);
+ list_splice_init(&nn->mc_addrs, &tmp_list);
+ spin_unlock_bh(&nn->mc_lock);
+
+ list_for_each_entry_safe(entry, tmp, &tmp_list, list) {
+ if (nfp_net_mc_cfg(nn, entry->addr, entry->cmd))
+ nn_err(nn, "Config mc address to HW failed.\n");
+
+ list_del(&entry->list);
+ kfree(entry);
+ }
}
static void nfp_net_set_rx_mode(struct net_device *netdev)
@@ -2633,6 +2680,11 @@ int nfp_net_init(struct nfp_net *nn)
if (!nn->dp.netdev)
return 0;
+
+ spin_lock_init(&nn->mc_lock);
+ INIT_LIST_HEAD(&nn->mc_addrs);
+ INIT_WORK(&nn->mc_work, nfp_net_mc_addr_config);
+
return register_netdev(nn->dp.netdev);
err_clean_mbox:
@@ -2652,5 +2704,6 @@ void nfp_net_clean(struct nfp_net *nn)
unregister_netdev(nn->dp.netdev);
nfp_net_ipsec_clean(nn);
nfp_ccm_mbox_clean(nn);
+ flush_work(&nn->mc_work);
nfp_net_reconfig_wait_posted(nn);
}
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
index a4a89ef3f18b4d..cc97b3d00414e6 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c
@@ -293,35 +293,131 @@ nfp_net_set_fec_link_mode(struct nfp_eth_table_port *eth_port,
}
}
-static const u16 nfp_eth_media_table[] = {
- [NFP_MEDIA_1000BASE_CX] = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
- [NFP_MEDIA_1000BASE_KX] = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
- [NFP_MEDIA_10GBASE_KX4] = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
- [NFP_MEDIA_10GBASE_KR] = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
- [NFP_MEDIA_10GBASE_CX4] = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
- [NFP_MEDIA_10GBASE_CR] = ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
- [NFP_MEDIA_10GBASE_SR] = ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
- [NFP_MEDIA_10GBASE_ER] = ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
- [NFP_MEDIA_25GBASE_KR] = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
- [NFP_MEDIA_25GBASE_KR_S] = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
- [NFP_MEDIA_25GBASE_CR] = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
- [NFP_MEDIA_25GBASE_CR_S] = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
- [NFP_MEDIA_25GBASE_SR] = ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
- [NFP_MEDIA_40GBASE_CR4] = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
- [NFP_MEDIA_40GBASE_KR4] = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
- [NFP_MEDIA_40GBASE_SR4] = ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
- [NFP_MEDIA_40GBASE_LR4] = ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
- [NFP_MEDIA_50GBASE_KR] = ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
- [NFP_MEDIA_50GBASE_SR] = ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
- [NFP_MEDIA_50GBASE_CR] = ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
- [NFP_MEDIA_50GBASE_LR] = ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
- [NFP_MEDIA_50GBASE_ER] = ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
- [NFP_MEDIA_50GBASE_FR] = ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
- [NFP_MEDIA_100GBASE_KR4] = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
- [NFP_MEDIA_100GBASE_SR4] = ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
- [NFP_MEDIA_100GBASE_CR4] = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
- [NFP_MEDIA_100GBASE_KP4] = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
- [NFP_MEDIA_100GBASE_CR10] = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
+static const struct nfp_eth_media_link_mode {
+ u16 ethtool_link_mode;
+ u16 speed;
+} nfp_eth_media_table[NFP_MEDIA_LINK_MODES_NUMBER] = {
+ [NFP_MEDIA_1000BASE_CX] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+ .speed = NFP_SPEED_1G,
+ },
+ [NFP_MEDIA_1000BASE_KX] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+ .speed = NFP_SPEED_1G,
+ },
+ [NFP_MEDIA_10GBASE_KX4] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+ .speed = NFP_SPEED_10G,
+ },
+ [NFP_MEDIA_10GBASE_KR] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+ .speed = NFP_SPEED_10G,
+ },
+ [NFP_MEDIA_10GBASE_CX4] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+ .speed = NFP_SPEED_10G,
+ },
+ [NFP_MEDIA_10GBASE_CR] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
+ .speed = NFP_SPEED_10G,
+ },
+ [NFP_MEDIA_10GBASE_SR] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
+ .speed = NFP_SPEED_10G,
+ },
+ [NFP_MEDIA_10GBASE_ER] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_10000baseER_Full_BIT,
+ .speed = NFP_SPEED_10G,
+ },
+ [NFP_MEDIA_25GBASE_KR] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
+ .speed = NFP_SPEED_25G,
+ },
+ [NFP_MEDIA_25GBASE_KR_S] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
+ .speed = NFP_SPEED_25G,
+ },
+ [NFP_MEDIA_25GBASE_CR] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
+ .speed = NFP_SPEED_25G,
+ },
+ [NFP_MEDIA_25GBASE_CR_S] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
+ .speed = NFP_SPEED_25G,
+ },
+ [NFP_MEDIA_25GBASE_SR] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
+ .speed = NFP_SPEED_25G,
+ },
+ [NFP_MEDIA_40GBASE_CR4] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
+ .speed = NFP_SPEED_40G,
+ },
+ [NFP_MEDIA_40GBASE_KR4] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
+ .speed = NFP_SPEED_40G,
+ },
+ [NFP_MEDIA_40GBASE_SR4] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
+ .speed = NFP_SPEED_40G,
+ },
+ [NFP_MEDIA_40GBASE_LR4] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
+ .speed = NFP_SPEED_40G,
+ },
+ [NFP_MEDIA_50GBASE_KR] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
+ .speed = NFP_SPEED_50G,
+ },
+ [NFP_MEDIA_50GBASE_SR] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
+ .speed = NFP_SPEED_50G,
+ },
+ [NFP_MEDIA_50GBASE_CR] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
+ .speed = NFP_SPEED_50G,
+ },
+ [NFP_MEDIA_50GBASE_LR] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
+ .speed = NFP_SPEED_50G,
+ },
+ [NFP_MEDIA_50GBASE_ER] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
+ .speed = NFP_SPEED_50G,
+ },
+ [NFP_MEDIA_50GBASE_FR] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT,
+ .speed = NFP_SPEED_50G,
+ },
+ [NFP_MEDIA_100GBASE_KR4] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
+ .speed = NFP_SPEED_100G,
+ },
+ [NFP_MEDIA_100GBASE_SR4] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
+ .speed = NFP_SPEED_100G,
+ },
+ [NFP_MEDIA_100GBASE_CR4] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
+ .speed = NFP_SPEED_100G,
+ },
+ [NFP_MEDIA_100GBASE_KP4] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
+ .speed = NFP_SPEED_100G,
+ },
+ [NFP_MEDIA_100GBASE_CR10] = {
+ .ethtool_link_mode = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
+ .speed = NFP_SPEED_100G,
+ },
+};
+
+static const unsigned int nfp_eth_speed_map[NFP_SUP_SPEED_NUMBER] = {
+ [NFP_SPEED_1G] = SPEED_1000,
+ [NFP_SPEED_10G] = SPEED_10000,
+ [NFP_SPEED_25G] = SPEED_25000,
+ [NFP_SPEED_40G] = SPEED_40000,
+ [NFP_SPEED_50G] = SPEED_50000,
+ [NFP_SPEED_100G] = SPEED_100000,
};
static void nfp_add_media_link_mode(struct nfp_port *port,
@@ -334,8 +430,12 @@ static void nfp_add_media_link_mode(struct nfp_port *port,
};
struct nfp_cpp *cpp = port->app->cpp;
- if (nfp_eth_read_media(cpp, &ethm))
+ if (nfp_eth_read_media(cpp, &ethm)) {
+ bitmap_fill(port->speed_bitmap, NFP_SUP_SPEED_NUMBER);
return;
+ }
+
+ bitmap_zero(port->speed_bitmap, NFP_SUP_SPEED_NUMBER);
for (u32 i = 0; i < 2; i++) {
supported_modes[i] = le64_to_cpu(ethm.supported_modes[i]);
@@ -344,20 +444,26 @@ static void nfp_add_media_link_mode(struct nfp_port *port,
for (u32 i = 0; i < NFP_MEDIA_LINK_MODES_NUMBER; i++) {
if (i < 64) {
- if (supported_modes[0] & BIT_ULL(i))
- __set_bit(nfp_eth_media_table[i],
+ if (supported_modes[0] & BIT_ULL(i)) {
+ __set_bit(nfp_eth_media_table[i].ethtool_link_mode,
cmd->link_modes.supported);
+ __set_bit(nfp_eth_media_table[i].speed,
+ port->speed_bitmap);
+ }
if (advertised_modes[0] & BIT_ULL(i))
- __set_bit(nfp_eth_media_table[i],
+ __set_bit(nfp_eth_media_table[i].ethtool_link_mode,
cmd->link_modes.advertising);
} else {
- if (supported_modes[1] & BIT_ULL(i - 64))
- __set_bit(nfp_eth_media_table[i],
+ if (supported_modes[1] & BIT_ULL(i - 64)) {
+ __set_bit(nfp_eth_media_table[i].ethtool_link_mode,
cmd->link_modes.supported);
+ __set_bit(nfp_eth_media_table[i].speed,
+ port->speed_bitmap);
+ }
if (advertised_modes[1] & BIT_ULL(i - 64))
- __set_bit(nfp_eth_media_table[i],
+ __set_bit(nfp_eth_media_table[i].ethtool_link_mode,
cmd->link_modes.advertising);
}
}
@@ -468,6 +574,22 @@ nfp_net_set_link_ksettings(struct net_device *netdev,
if (cmd->base.speed != SPEED_UNKNOWN) {
u32 speed = cmd->base.speed / eth_port->lanes;
+ bool is_supported = false;
+
+ for (u32 i = 0; i < NFP_SUP_SPEED_NUMBER; i++) {
+ if (cmd->base.speed == nfp_eth_speed_map[i] &&
+ test_bit(i, port->speed_bitmap)) {
+ is_supported = true;
+ break;
+ }
+ }
+
+ if (!is_supported) {
+ netdev_err(netdev, "Speed %u is not supported.\n",
+ cmd->base.speed);
+ err = -EINVAL;
+ goto err_bad_set;
+ }
if (req_aneg) {
netdev_err(netdev, "Speed changing is not allowed when working on autoneg mode.\n");
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h
index f8cd157ca1d798..9c04f9f0e2c903 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_port.h
+++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h
@@ -38,6 +38,16 @@ enum nfp_port_flags {
NFP_PORT_CHANGED = 0,
};
+enum {
+ NFP_SPEED_1G,
+ NFP_SPEED_10G,
+ NFP_SPEED_25G,
+ NFP_SPEED_40G,
+ NFP_SPEED_50G,
+ NFP_SPEED_100G,
+ NFP_SUP_SPEED_NUMBER
+};
+
/**
* struct nfp_port - structure representing NFP port
* @netdev: backpointer to associated netdev
@@ -52,6 +62,7 @@ enum nfp_port_flags {
* @eth_forced: for %NFP_PORT_PHYS_PORT port is forced UP or DOWN, don't change
* @eth_port: for %NFP_PORT_PHYS_PORT translated ETH Table port entry
* @eth_stats: for %NFP_PORT_PHYS_PORT MAC stats if available
+ * @speed_bitmap: for %NFP_PORT_PHYS_PORT supported speed bitmap
* @pf_id: for %NFP_PORT_PF_PORT, %NFP_PORT_VF_PORT ID of the PCI PF (0-3)
* @vf_id: for %NFP_PORT_VF_PORT ID of the PCI VF within @pf_id
* @pf_split: for %NFP_PORT_PF_PORT %true if PCI PF has more than one vNIC
@@ -78,6 +89,7 @@ struct nfp_port {
bool eth_forced;
struct nfp_eth_table_port *eth_port;
u8 __iomem *eth_stats;
+ DECLARE_BITMAP(speed_bitmap, NFP_SUP_SPEED_NUMBER);
};
/* NFP_PORT_PF_PORT, NFP_PORT_VF_PORT */
struct {
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.c b/drivers/net/ethernet/pensando/ionic/ionic_dev.c
index 626b9113e7c411..d911f4fd9af61a 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_dev.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.c
@@ -708,9 +708,16 @@ void ionic_q_post(struct ionic_queue *q, bool ring_doorbell, ionic_desc_cb cb,
q->lif->index, q->name, q->hw_type, q->hw_index,
q->head_idx, ring_doorbell);
- if (ring_doorbell)
+ if (ring_doorbell) {
ionic_dbell_ring(lif->kern_dbpage, q->hw_type,
q->dbval | q->head_idx);
+
+ q->dbell_jiffies = jiffies;
+
+ if (q_to_qcq(q)->napi_qcq)
+ mod_timer(&q_to_qcq(q)->napi_qcq->napi_deadline,
+ jiffies + IONIC_NAPI_DEADLINE);
+ }
}
static bool ionic_q_is_posted(struct ionic_queue *q, unsigned int pos)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_dev.h b/drivers/net/ethernet/pensando/ionic/ionic_dev.h
index 2a1d7b9c07e7ff..bce3ca38669bdc 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_dev.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_dev.h
@@ -25,6 +25,12 @@
#define IONIC_DEV_INFO_REG_COUNT 32
#define IONIC_DEV_CMD_REG_COUNT 32
+#define IONIC_NAPI_DEADLINE (HZ / 200) /* 5ms */
+#define IONIC_ADMIN_DOORBELL_DEADLINE (HZ / 2) /* 500ms */
+#define IONIC_TX_DOORBELL_DEADLINE (HZ / 100) /* 10ms */
+#define IONIC_RX_MIN_DOORBELL_DEADLINE (HZ / 100) /* 10ms */
+#define IONIC_RX_MAX_DOORBELL_DEADLINE (HZ * 5) /* 5s */
+
struct ionic_dev_bar {
void __iomem *vaddr;
phys_addr_t bus_addr;
@@ -216,6 +222,8 @@ struct ionic_queue {
struct ionic_lif *lif;
struct ionic_desc_info *info;
u64 dbval;
+ unsigned long dbell_deadline;
+ unsigned long dbell_jiffies;
u16 head_idx;
u16 tail_idx;
unsigned int index;
@@ -361,4 +369,8 @@ void ionic_q_service(struct ionic_queue *q, struct ionic_cq_info *cq_info,
int ionic_heartbeat_check(struct ionic *ionic);
bool ionic_is_fw_running(struct ionic_dev *idev);
+bool ionic_adminq_poke_doorbell(struct ionic_queue *q);
+bool ionic_txq_poke_doorbell(struct ionic_queue *q);
+bool ionic_rxq_poke_doorbell(struct ionic_queue *q);
+
#endif /* _IONIC_DEV_H_ */
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
index 4dd16c487f2b48..63a78a9ac241ad 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
@@ -16,6 +16,7 @@
#include "ionic.h"
#include "ionic_bus.h"
+#include "ionic_dev.h"
#include "ionic_lif.h"
#include "ionic_txrx.h"
#include "ionic_ethtool.h"
@@ -200,6 +201,13 @@ void ionic_link_status_check_request(struct ionic_lif *lif, bool can_sleep)
}
}
+static void ionic_napi_deadline(struct timer_list *timer)
+{
+ struct ionic_qcq *qcq = container_of(timer, struct ionic_qcq, napi_deadline);
+
+ napi_schedule(&qcq->napi);
+}
+
static irqreturn_t ionic_isr(int irq, void *data)
{
struct napi_struct *napi = data;
@@ -269,6 +277,7 @@ static int ionic_qcq_enable(struct ionic_qcq *qcq)
.oper = IONIC_Q_ENABLE,
},
};
+ int ret;
idev = &lif->ionic->idev;
dev = lif->ionic->dev;
@@ -276,16 +285,24 @@ static int ionic_qcq_enable(struct ionic_qcq *qcq)
dev_dbg(dev, "q_enable.index %d q_enable.qtype %d\n",
ctx.cmd.q_control.index, ctx.cmd.q_control.type);
+ if (qcq->flags & IONIC_QCQ_F_INTR)
+ ionic_intr_clean(idev->intr_ctrl, qcq->intr.index);
+
+ ret = ionic_adminq_post_wait(lif, &ctx);
+ if (ret)
+ return ret;
+
+ if (qcq->napi.poll)
+ napi_enable(&qcq->napi);
+
if (qcq->flags & IONIC_QCQ_F_INTR) {
irq_set_affinity_hint(qcq->intr.vector,
&qcq->intr.affinity_mask);
- napi_enable(&qcq->napi);
- ionic_intr_clean(idev->intr_ctrl, qcq->intr.index);
ionic_intr_mask(idev->intr_ctrl, qcq->intr.index,
IONIC_INTR_MASK_CLEAR);
}
- return ionic_adminq_post_wait(lif, &ctx);
+ return 0;
}
static int ionic_qcq_disable(struct ionic_lif *lif, struct ionic_qcq *qcq, int fw_err)
@@ -316,6 +333,7 @@ static int ionic_qcq_disable(struct ionic_lif *lif, struct ionic_qcq *qcq, int f
synchronize_irq(qcq->intr.vector);
irq_set_affinity_hint(qcq->intr.vector, NULL);
napi_disable(&qcq->napi);
+ del_timer_sync(&qcq->napi_deadline);
}
/* If there was a previous fw communcation error, don't bother with
@@ -451,6 +469,7 @@ static void ionic_link_qcq_interrupts(struct ionic_qcq *src_qcq,
n_qcq->intr.vector = src_qcq->intr.vector;
n_qcq->intr.index = src_qcq->intr.index;
+ n_qcq->napi_qcq = src_qcq->napi_qcq;
}
static int ionic_alloc_qcq_interrupt(struct ionic_lif *lif, struct ionic_qcq *qcq)
@@ -564,13 +583,15 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
}
if (flags & IONIC_QCQ_F_NOTIFYQ) {
- int q_size, cq_size;
+ int q_size;
- /* q & cq need to be contiguous in case of notifyq */
+ /* q & cq need to be contiguous in NotifyQ, so alloc it all in q
+ * and don't alloc qc. We leave new->qc_size and new->qc_base
+ * as 0 to be sure we don't try to free it later.
+ */
q_size = ALIGN(num_descs * desc_size, PAGE_SIZE);
- cq_size = ALIGN(num_descs * cq_desc_size, PAGE_SIZE);
-
- new->q_size = PAGE_SIZE + q_size + cq_size;
+ new->q_size = PAGE_SIZE + q_size +
+ ALIGN(num_descs * cq_desc_size, PAGE_SIZE);
new->q_base = dma_alloc_coherent(dev, new->q_size,
&new->q_base_pa, GFP_KERNEL);
if (!new->q_base) {
@@ -773,8 +794,14 @@ static int ionic_lif_txq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)
dev_dbg(dev, "txq->hw_type %d\n", q->hw_type);
dev_dbg(dev, "txq->hw_index %d\n", q->hw_index);
- if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state))
+ q->dbell_deadline = IONIC_TX_DOORBELL_DEADLINE;
+ q->dbell_jiffies = jiffies;
+
+ if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state)) {
netif_napi_add(lif->netdev, &qcq->napi, ionic_tx_napi);
+ qcq->napi_qcq = qcq;
+ timer_setup(&qcq->napi_deadline, ionic_napi_deadline, 0);
+ }
qcq->flags |= IONIC_QCQ_F_INITED;
@@ -828,11 +855,17 @@ static int ionic_lif_rxq_init(struct ionic_lif *lif, struct ionic_qcq *qcq)
dev_dbg(dev, "rxq->hw_type %d\n", q->hw_type);
dev_dbg(dev, "rxq->hw_index %d\n", q->hw_index);
+ q->dbell_deadline = IONIC_RX_MIN_DOORBELL_DEADLINE;
+ q->dbell_jiffies = jiffies;
+
if (test_bit(IONIC_LIF_F_SPLIT_INTR, lif->state))
netif_napi_add(lif->netdev, &qcq->napi, ionic_rx_napi);
else
netif_napi_add(lif->netdev, &qcq->napi, ionic_txrx_napi);
+ qcq->napi_qcq = qcq;
+ timer_setup(&qcq->napi_deadline, ionic_napi_deadline, 0);
+
qcq->flags |= IONIC_QCQ_F_INITED;
return 0;
@@ -1150,6 +1183,7 @@ static int ionic_adminq_napi(struct napi_struct *napi, int budget)
struct ionic_dev *idev = &lif->ionic->idev;
unsigned long irqflags;
unsigned int flags = 0;
+ bool resched = false;
int rx_work = 0;
int tx_work = 0;
int n_work = 0;
@@ -1187,6 +1221,16 @@ static int ionic_adminq_napi(struct napi_struct *napi, int budget)
ionic_intr_credits(idev->intr_ctrl, intr->index, credits, flags);
}
+ if (!a_work && ionic_adminq_poke_doorbell(&lif->adminqcq->q))
+ resched = true;
+ if (lif->hwstamp_rxq && !rx_work && ionic_rxq_poke_doorbell(&lif->hwstamp_rxq->q))
+ resched = true;
+ if (lif->hwstamp_txq && !tx_work && ionic_txq_poke_doorbell(&lif->hwstamp_txq->q))
+ resched = true;
+ if (resched)
+ mod_timer(&lif->adminqcq->napi_deadline,
+ jiffies + IONIC_NAPI_DEADLINE);
+
return work_done;
}
@@ -3245,8 +3289,14 @@ static int ionic_lif_adminq_init(struct ionic_lif *lif)
dev_dbg(dev, "adminq->hw_type %d\n", q->hw_type);
dev_dbg(dev, "adminq->hw_index %d\n", q->hw_index);
+ q->dbell_deadline = IONIC_ADMIN_DOORBELL_DEADLINE;
+ q->dbell_jiffies = jiffies;
+
netif_napi_add(lif->netdev, &qcq->napi, ionic_adminq_napi);
+ qcq->napi_qcq = qcq;
+ timer_setup(&qcq->napi_deadline, ionic_napi_deadline, 0);
+
napi_enable(&qcq->napi);
if (qcq->flags & IONIC_QCQ_F_INTR)
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.h b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
index a53984bf354483..734519895614f4 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.h
+++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.h
@@ -74,8 +74,10 @@ struct ionic_qcq {
struct ionic_queue q;
struct ionic_cq cq;
struct ionic_intr_info intr;
+ struct timer_list napi_deadline;
struct napi_struct napi;
unsigned int flags;
+ struct ionic_qcq *napi_qcq;
struct dentry *dentry;
};
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_main.c b/drivers/net/ethernet/pensando/ionic/ionic_main.c
index a13530ec4dd8fc..08c42b039d92db 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_main.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_main.c
@@ -289,6 +289,35 @@ static void ionic_adminq_cb(struct ionic_queue *q,
complete_all(&ctx->work);
}
+bool ionic_adminq_poke_doorbell(struct ionic_queue *q)
+{
+ struct ionic_lif *lif = q->lif;
+ unsigned long now, then, dif;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&lif->adminq_lock, irqflags);
+
+ if (q->tail_idx == q->head_idx) {
+ spin_unlock_irqrestore(&lif->adminq_lock, irqflags);
+ return false;
+ }
+
+ now = READ_ONCE(jiffies);
+ then = q->dbell_jiffies;
+ dif = now - then;
+
+ if (dif > q->dbell_deadline) {
+ ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type,
+ q->dbval | q->head_idx);
+
+ q->dbell_jiffies = now;
+ }
+
+ spin_unlock_irqrestore(&lif->adminq_lock, irqflags);
+
+ return true;
+}
+
int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx)
{
struct ionic_desc_info *desc_info;
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
index 0c3977416cd1c3..f761780f016244 100644
--- a/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
+++ b/drivers/net/ethernet/pensando/ionic/ionic_txrx.c
@@ -22,6 +22,67 @@ static inline void ionic_rxq_post(struct ionic_queue *q, bool ring_dbell,
ionic_q_post(q, ring_dbell, cb_func, cb_arg);
}
+bool ionic_txq_poke_doorbell(struct ionic_queue *q)
+{
+ unsigned long now, then, dif;
+ struct netdev_queue *netdev_txq;
+ struct net_device *netdev;
+
+ netdev = q->lif->netdev;
+ netdev_txq = netdev_get_tx_queue(netdev, q->index);
+
+ HARD_TX_LOCK(netdev, netdev_txq, smp_processor_id());
+
+ if (q->tail_idx == q->head_idx) {
+ HARD_TX_UNLOCK(netdev, netdev_txq);
+ return false;
+ }
+
+ now = READ_ONCE(jiffies);
+ then = q->dbell_jiffies;
+ dif = now - then;
+
+ if (dif > q->dbell_deadline) {
+ ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type,
+ q->dbval | q->head_idx);
+
+ q->dbell_jiffies = now;
+ }
+
+ HARD_TX_UNLOCK(netdev, netdev_txq);
+
+ return true;
+}
+
+bool ionic_rxq_poke_doorbell(struct ionic_queue *q)
+{
+ unsigned long now, then, dif;
+
+ /* no lock, called from rx napi or txrx napi, nothing else can fill */
+
+ if (q->tail_idx == q->head_idx)
+ return false;
+
+ now = READ_ONCE(jiffies);
+ then = q->dbell_jiffies;
+ dif = now - then;
+
+ if (dif > q->dbell_deadline) {
+ ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type,
+ q->dbval | q->head_idx);
+
+ q->dbell_jiffies = now;
+
+ dif = 2 * q->dbell_deadline;
+ if (dif > IONIC_RX_MAX_DOORBELL_DEADLINE)
+ dif = IONIC_RX_MAX_DOORBELL_DEADLINE;
+
+ q->dbell_deadline = dif;
+ }
+
+ return true;
+}
+
static inline struct netdev_queue *q_to_ndq(struct ionic_queue *q)
{
return netdev_get_tx_queue(q->lif->netdev, q->index);
@@ -424,6 +485,12 @@ void ionic_rx_fill(struct ionic_queue *q)
ionic_dbell_ring(q->lif->kern_dbpage, q->hw_type,
q->dbval | q->head_idx);
+
+ q->dbell_deadline = IONIC_RX_MIN_DOORBELL_DEADLINE;
+ q->dbell_jiffies = jiffies;
+
+ mod_timer(&q_to_qcq(q)->napi_qcq->napi_deadline,
+ jiffies + IONIC_NAPI_DEADLINE);
}
void ionic_rx_empty(struct ionic_queue *q)
@@ -511,6 +578,9 @@ int ionic_tx_napi(struct napi_struct *napi, int budget)
work_done, flags);
}
+ if (!work_done && ionic_txq_poke_doorbell(&qcq->q))
+ mod_timer(&qcq->napi_deadline, jiffies + IONIC_NAPI_DEADLINE);
+
return work_done;
}
@@ -544,23 +614,29 @@ int ionic_rx_napi(struct napi_struct *napi, int budget)
work_done, flags);
}
+ if (!work_done && ionic_rxq_poke_doorbell(&qcq->q))
+ mod_timer(&qcq->napi_deadline, jiffies + IONIC_NAPI_DEADLINE);
+
return work_done;
}
int ionic_txrx_napi(struct napi_struct *napi, int budget)
{
- struct ionic_qcq *qcq = napi_to_qcq(napi);
+ struct ionic_qcq *rxqcq = napi_to_qcq(napi);
struct ionic_cq *rxcq = napi_to_cq(napi);
unsigned int qi = rxcq->bound_q->index;
+ struct ionic_qcq *txqcq;
struct ionic_dev *idev;
struct ionic_lif *lif;
struct ionic_cq *txcq;
+ bool resched = false;
u32 rx_work_done = 0;
u32 tx_work_done = 0;
u32 flags = 0;
lif = rxcq->bound_q->lif;
idev = &lif->ionic->idev;
+ txqcq = lif->txqcqs[qi];
txcq = &lif->txqcqs[qi]->cq;
tx_work_done = ionic_cq_service(txcq, IONIC_TX_BUDGET_DEFAULT,
@@ -572,7 +648,7 @@ int ionic_txrx_napi(struct napi_struct *napi, int budget)
ionic_rx_fill(rxcq->bound_q);
if (rx_work_done < budget && napi_complete_done(napi, rx_work_done)) {
- ionic_dim_update(qcq, 0);
+ ionic_dim_update(rxqcq, 0);
flags |= IONIC_INTR_CRED_UNMASK;
rxcq->bound_intr->rearm_count++;
}
@@ -583,6 +659,13 @@ int ionic_txrx_napi(struct napi_struct *napi, int budget)
tx_work_done + rx_work_done, flags);
}
+ if (!rx_work_done && ionic_rxq_poke_doorbell(&rxqcq->q))
+ resched = true;
+ if (!tx_work_done && ionic_txq_poke_doorbell(&txqcq->q))
+ resched = true;
+ if (resched)
+ mod_timer(&rxqcq->napi_deadline, jiffies + IONIC_NAPI_DEADLINE);
+
return rx_work_done;
}
diff --git a/drivers/net/ethernet/qlogic/qed/qed_debug.c b/drivers/net/ethernet/qlogic/qed/qed_debug.c
index 86ecb080b15368..cdcead614e9fa8 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_debug.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_debug.c
@@ -1832,7 +1832,8 @@ static enum dbg_status qed_find_nvram_image(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 image_type,
u32 *nvram_offset_bytes,
- u32 *nvram_size_bytes)
+ u32 *nvram_size_bytes,
+ bool b_can_sleep)
{
u32 ret_mcp_resp, ret_mcp_param, ret_txn_size;
struct mcp_file_att file_att;
@@ -1846,7 +1847,8 @@ static enum dbg_status qed_find_nvram_image(struct qed_hwfn *p_hwfn,
&ret_mcp_resp,
&ret_mcp_param,
&ret_txn_size,
- (u32 *)&file_att, false);
+ (u32 *)&file_att,
+ b_can_sleep);
/* Check response */
if (nvm_result || (ret_mcp_resp & FW_MSG_CODE_MASK) !=
@@ -1873,7 +1875,9 @@ static enum dbg_status qed_find_nvram_image(struct qed_hwfn *p_hwfn,
static enum dbg_status qed_nvram_read(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 nvram_offset_bytes,
- u32 nvram_size_bytes, u32 *ret_buf)
+ u32 nvram_size_bytes,
+ u32 *ret_buf,
+ bool b_can_sleep)
{
u32 ret_mcp_resp, ret_mcp_param, ret_read_size, bytes_to_copy;
s32 bytes_left = nvram_size_bytes;
@@ -1899,7 +1903,7 @@ static enum dbg_status qed_nvram_read(struct qed_hwfn *p_hwfn,
&ret_mcp_resp,
&ret_mcp_param, &ret_read_size,
(u32 *)((u8 *)ret_buf + read_offset),
- false))
+ b_can_sleep))
return DBG_STATUS_NVRAM_READ_FAILED;
/* Check response */
@@ -3380,7 +3384,8 @@ static u32 qed_grc_dump_mcp_hw_dump(struct qed_hwfn *p_hwfn,
p_ptt,
NVM_TYPE_HW_DUMP_OUT,
&hw_dump_offset_bytes,
- &hw_dump_size_bytes);
+ &hw_dump_size_bytes,
+ false);
if (status != DBG_STATUS_OK)
return 0;
@@ -3397,7 +3402,9 @@ static u32 qed_grc_dump_mcp_hw_dump(struct qed_hwfn *p_hwfn,
status = qed_nvram_read(p_hwfn,
p_ptt,
hw_dump_offset_bytes,
- hw_dump_size_bytes, dump_buf + offset);
+ hw_dump_size_bytes,
+ dump_buf + offset,
+ false);
if (status != DBG_STATUS_OK) {
DP_NOTICE(p_hwfn,
"Failed to read MCP HW Dump image from NVRAM\n");
@@ -4123,7 +4130,9 @@ static enum dbg_status qed_mcp_trace_get_meta_info(struct qed_hwfn *p_hwfn,
return qed_find_nvram_image(p_hwfn,
p_ptt,
nvram_image_type,
- trace_meta_offset, trace_meta_size);
+ trace_meta_offset,
+ trace_meta_size,
+ true);
}
/* Reads the MCP Trace meta data from NVRAM into the specified buffer */
@@ -4139,7 +4148,10 @@ static enum dbg_status qed_mcp_trace_read_meta(struct qed_hwfn *p_hwfn,
/* Read meta data from NVRAM */
status = qed_nvram_read(p_hwfn,
p_ptt,
- nvram_offset_in_bytes, size_in_bytes, buf);
+ nvram_offset_in_bytes,
+ size_in_bytes,
+ buf,
+ true);
if (status != DBG_STATUS_OK)
return status;
diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c
index 7c2af482192d74..cb1746bc0e0c5d 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_fp.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c
@@ -1438,6 +1438,10 @@ int qede_poll(struct napi_struct *napi, int budget)
rx_work_done = (likely(fp->type & QEDE_FASTPATH_RX) &&
qede_has_rx_work(fp->rxq)) ?
qede_rx_int(fp, budget) : 0;
+
+ if (fp->xdp_xmit & QEDE_XDP_REDIRECT)
+ xdp_do_flush();
+
/* Handle case where we are called by netpoll with a budget of 0 */
if (rx_work_done < budget || !budget) {
if (!qede_poll_is_more_work(fp)) {
@@ -1457,9 +1461,6 @@ int qede_poll(struct napi_struct *napi, int budget)
qede_update_tx_producer(fp->xdp_tx);
}
- if (fp->xdp_xmit & QEDE_XDP_REDIRECT)
- xdp_do_flush_map();
-
return rx_work_done;
}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
index dbb800769cb63e..c95d56e56c59a1 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c
@@ -2505,7 +2505,13 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter)
goto disable_mbx_intr;
qlcnic_83xx_clear_function_resources(adapter);
- qlcnic_dcb_enable(adapter->dcb);
+
+ err = qlcnic_dcb_enable(adapter->dcb);
+ if (err) {
+ qlcnic_dcb_free(adapter->dcb);
+ goto disable_mbx_intr;
+ }
+
qlcnic_83xx_initialize_nic(adapter, 1);
qlcnic_dcb_get_info(adapter->dcb);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
index 7519773eaca6ee..22afa2be85fdba 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h
@@ -41,11 +41,6 @@ struct qlcnic_dcb {
unsigned long state;
};
-static inline void qlcnic_clear_dcb_ops(struct qlcnic_dcb *dcb)
-{
- kfree(dcb);
-}
-
static inline int qlcnic_dcb_get_hw_capability(struct qlcnic_dcb *dcb)
{
if (dcb && dcb->ops->get_hw_capability)
@@ -112,9 +107,8 @@ static inline void qlcnic_dcb_init_dcbnl_ops(struct qlcnic_dcb *dcb)
dcb->ops->init_dcbnl_ops(dcb);
}
-static inline void qlcnic_dcb_enable(struct qlcnic_dcb *dcb)
+static inline int qlcnic_dcb_enable(struct qlcnic_dcb *dcb)
{
- if (dcb && qlcnic_dcb_attach(dcb))
- qlcnic_clear_dcb_ops(dcb);
+ return dcb ? qlcnic_dcb_attach(dcb) : 0;
}
#endif
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 28476b982bab6c..44dac3c0908eb8 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -2599,7 +2599,13 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
"Device does not support MSI interrupts\n");
if (qlcnic_82xx_check(adapter)) {
- qlcnic_dcb_enable(adapter->dcb);
+ err = qlcnic_dcb_enable(adapter->dcb);
+ if (err) {
+ qlcnic_dcb_free(adapter->dcb);
+ dev_err(&pdev->dev, "Failed to enable DCB\n");
+ goto err_out_free_hw;
+ }
+
qlcnic_dcb_get_info(adapter->dcb);
err = qlcnic_setup_intr(adapter);
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
index a9dcc98b6af189..dadd61bccfe72c 100644
--- a/drivers/net/ethernet/realtek/r8169_main.c
+++ b/drivers/net/ethernet/realtek/r8169_main.c
@@ -1996,10 +1996,7 @@ static enum mac_version rtl8169_get_mac_version(u16 xid, bool gmii)
/* 8168F family. */
{ 0x7c8, 0x488, RTL_GIGA_MAC_VER_38 },
- /* It seems this chip version never made it to
- * the wild. Let's disable detection.
- * { 0x7cf, 0x481, RTL_GIGA_MAC_VER_36 },
- */
+ { 0x7cf, 0x481, RTL_GIGA_MAC_VER_36 },
{ 0x7cf, 0x480, RTL_GIGA_MAC_VER_35 },
/* 8168E family. */
@@ -2210,28 +2207,6 @@ static int rtl_set_mac_address(struct net_device *dev, void *p)
return 0;
}
-static void rtl_wol_enable_rx(struct rtl8169_private *tp)
-{
- if (tp->mac_version >= RTL_GIGA_MAC_VER_25)
- RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) |
- AcceptBroadcast | AcceptMulticast | AcceptMyPhys);
-}
-
-static void rtl_prepare_power_down(struct rtl8169_private *tp)
-{
- if (tp->dash_type != RTL_DASH_NONE)
- return;
-
- if (tp->mac_version == RTL_GIGA_MAC_VER_32 ||
- tp->mac_version == RTL_GIGA_MAC_VER_33)
- rtl_ephy_write(tp, 0x19, 0xff64);
-
- if (device_may_wakeup(tp_to_dev(tp))) {
- phy_speed_down(tp->phydev, false);
- rtl_wol_enable_rx(tp);
- }
-}
-
static void rtl_init_rxcfg(struct rtl8169_private *tp)
{
switch (tp->mac_version) {
@@ -2455,6 +2430,31 @@ static void rtl_enable_rxdvgate(struct rtl8169_private *tp)
rtl_wait_txrx_fifo_empty(tp);
}
+static void rtl_wol_enable_rx(struct rtl8169_private *tp)
+{
+ if (tp->mac_version >= RTL_GIGA_MAC_VER_25)
+ RTL_W32(tp, RxConfig, RTL_R32(tp, RxConfig) |
+ AcceptBroadcast | AcceptMulticast | AcceptMyPhys);
+
+ if (tp->mac_version >= RTL_GIGA_MAC_VER_40)
+ rtl_disable_rxdvgate(tp);
+}
+
+static void rtl_prepare_power_down(struct rtl8169_private *tp)
+{
+ if (tp->dash_type != RTL_DASH_NONE)
+ return;
+
+ if (tp->mac_version == RTL_GIGA_MAC_VER_32 ||
+ tp->mac_version == RTL_GIGA_MAC_VER_33)
+ rtl_ephy_write(tp, 0x19, 0xff64);
+
+ if (device_may_wakeup(tp_to_dev(tp))) {
+ phy_speed_down(tp->phydev, false);
+ rtl_wol_enable_rx(tp);
+ }
+}
+
static void rtl_set_tx_config_registers(struct rtl8169_private *tp)
{
u32 val = TX_DMA_BURST << TxDMAShift |
@@ -3872,7 +3872,7 @@ static void rtl8169_tx_clear(struct rtl8169_private *tp)
netdev_reset_queue(tp->dev);
}
-static void rtl8169_cleanup(struct rtl8169_private *tp, bool going_down)
+static void rtl8169_cleanup(struct rtl8169_private *tp)
{
napi_disable(&tp->napi);
@@ -3884,9 +3884,6 @@ static void rtl8169_cleanup(struct rtl8169_private *tp, bool going_down)
rtl_rx_close(tp);
- if (going_down && tp->dev->wol_enabled)
- goto no_reset;
-
switch (tp->mac_version) {
case RTL_GIGA_MAC_VER_28:
case RTL_GIGA_MAC_VER_31:
@@ -3907,7 +3904,7 @@ static void rtl8169_cleanup(struct rtl8169_private *tp, bool going_down)
}
rtl_hw_reset(tp);
-no_reset:
+
rtl8169_tx_clear(tp);
rtl8169_init_ring_indexes(tp);
}
@@ -3918,7 +3915,7 @@ static void rtl_reset_work(struct rtl8169_private *tp)
netif_stop_queue(tp->dev);
- rtl8169_cleanup(tp, false);
+ rtl8169_cleanup(tp);
for (i = 0; i < NUM_RX_DESC; i++)
rtl8169_mark_to_asic(tp->RxDescArray + i);
@@ -4605,7 +4602,7 @@ static void rtl8169_down(struct rtl8169_private *tp)
pci_clear_master(tp->pci_dev);
rtl_pci_commit(tp);
- rtl8169_cleanup(tp, true);
+ rtl8169_cleanup(tp);
rtl_disable_exit_l1(tp);
rtl_prepare_power_down(tp);
}
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
index b4e0fc7f65bdfe..0f54849a38235f 100644
--- a/drivers/net/ethernet/renesas/ravb_main.c
+++ b/drivers/net/ethernet/renesas/ravb_main.c
@@ -1101,14 +1101,14 @@ static void ravb_error_interrupt(struct net_device *ndev)
ravb_write(ndev, ~(EIS_QFS | EIS_RESERVED), EIS);
if (eis & EIS_QFS) {
ris2 = ravb_read(ndev, RIS2);
- ravb_write(ndev, ~(RIS2_QFF0 | RIS2_RFFF | RIS2_RESERVED),
+ ravb_write(ndev, ~(RIS2_QFF0 | RIS2_QFF1 | RIS2_RFFF | RIS2_RESERVED),
RIS2);
/* Receive Descriptor Empty int */
if (ris2 & RIS2_QFF0)
priv->stats[RAVB_BE].rx_over_errors++;
- /* Receive Descriptor Empty int */
+ /* Receive Descriptor Empty int */
if (ris2 & RIS2_QFF1)
priv->stats[RAVB_NC].rx_over_errors++;
@@ -2973,6 +2973,9 @@ static int __maybe_unused ravb_suspend(struct device *dev)
else
ret = ravb_close(ndev);
+ if (priv->info->ccc_gac)
+ ravb_ptp_stop(ndev);
+
return ret;
}
@@ -3011,6 +3014,9 @@ static int __maybe_unused ravb_resume(struct device *dev)
/* Restore descriptor base address table */
ravb_write(ndev, priv->desc_bat_dma, DBAT);
+ if (priv->info->ccc_gac)
+ ravb_ptp_init(ndev, priv->pdev);
+
if (netif_running(ndev)) {
if (priv->wol_enabled) {
ret = ravb_wol_restore(ndev);
diff --git a/drivers/net/ethernet/renesas/rswitch.c b/drivers/net/ethernet/renesas/rswitch.c
index e42ceaa0099fdb..2370c7797a0aad 100644
--- a/drivers/net/ethernet/renesas/rswitch.c
+++ b/drivers/net/ethernet/renesas/rswitch.c
@@ -1074,8 +1074,11 @@ static struct device_node *rswitch_get_port_node(struct rswitch_device *rdev)
port = NULL;
goto out;
}
- if (index == rdev->etha->index)
+ if (index == rdev->etha->index) {
+ if (!of_device_is_available(port))
+ port = NULL;
break;
+ }
}
out:
@@ -1106,7 +1109,7 @@ static int rswitch_etha_get_params(struct rswitch_device *rdev)
port = rswitch_get_port_node(rdev);
if (!port)
- return -ENODEV;
+ return 0; /* ignored */
err = of_get_phy_mode(port, &rdev->etha->phy_interface);
of_node_put(port);
@@ -1324,13 +1327,13 @@ static int rswitch_ether_port_init_all(struct rswitch_private *priv)
{
int i, err;
- for (i = 0; i < RSWITCH_NUM_PORTS; i++) {
+ rswitch_for_each_enabled_port(priv, i) {
err = rswitch_ether_port_init_one(priv->rdev[i]);
if (err)
goto err_init_one;
}
- for (i = 0; i < RSWITCH_NUM_PORTS; i++) {
+ rswitch_for_each_enabled_port(priv, i) {
err = rswitch_serdes_init(priv->rdev[i]);
if (err)
goto err_serdes;
@@ -1339,12 +1342,12 @@ static int rswitch_ether_port_init_all(struct rswitch_private *priv)
return 0;
err_serdes:
- for (i--; i >= 0; i--)
+ rswitch_for_each_enabled_port_continue_reverse(priv, i)
rswitch_serdes_deinit(priv->rdev[i]);
i = RSWITCH_NUM_PORTS;
err_init_one:
- for (i--; i >= 0; i--)
+ rswitch_for_each_enabled_port_continue_reverse(priv, i)
rswitch_ether_port_deinit_one(priv->rdev[i]);
return err;
@@ -1578,6 +1581,7 @@ static int rswitch_device_alloc(struct rswitch_private *priv, int index)
{
struct platform_device *pdev = priv->pdev;
struct rswitch_device *rdev;
+ struct device_node *port;
struct net_device *ndev;
int err;
@@ -1606,7 +1610,10 @@ static int rswitch_device_alloc(struct rswitch_private *priv, int index)
netif_napi_add(ndev, &rdev->napi, rswitch_poll);
- err = of_get_ethdev_address(pdev->dev.of_node, ndev);
+ port = rswitch_get_port_node(rdev);
+ rdev->disabled = !port;
+ err = of_get_ethdev_address(port, ndev);
+ of_node_put(port);
if (err) {
if (is_valid_ether_addr(rdev->etha->mac_addr))
eth_hw_addr_set(ndev, rdev->etha->mac_addr);
@@ -1704,16 +1711,16 @@ static int rswitch_init(struct rswitch_private *priv)
if (err)
goto err_ether_port_init_all;
- for (i = 0; i < RSWITCH_NUM_PORTS; i++) {
+ rswitch_for_each_enabled_port(priv, i) {
err = register_netdev(priv->rdev[i]->ndev);
if (err) {
- for (i--; i >= 0; i--)
+ rswitch_for_each_enabled_port_continue_reverse(priv, i)
unregister_netdev(priv->rdev[i]->ndev);
goto err_register_netdev;
}
}
- for (i = 0; i < RSWITCH_NUM_PORTS; i++)
+ rswitch_for_each_enabled_port(priv, i)
netdev_info(priv->rdev[i]->ndev, "MAC address %pM\n",
priv->rdev[i]->ndev->dev_addr);
@@ -1786,6 +1793,11 @@ static int renesas_eth_sw_probe(struct platform_device *pdev)
pm_runtime_get_sync(&pdev->dev);
ret = rswitch_init(priv);
+ if (ret < 0) {
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+ }
device_set_wakeup_capable(&pdev->dev, 1);
diff --git a/drivers/net/ethernet/renesas/rswitch.h b/drivers/net/ethernet/renesas/rswitch.h
index edbdd1b98d3d9c..49efb0f31c77ac 100644
--- a/drivers/net/ethernet/renesas/rswitch.h
+++ b/drivers/net/ethernet/renesas/rswitch.h
@@ -13,6 +13,17 @@
#define RSWITCH_MAX_NUM_QUEUES 128
#define RSWITCH_NUM_PORTS 3
+#define rswitch_for_each_enabled_port(priv, i) \
+ for (i = 0; i < RSWITCH_NUM_PORTS; i++) \
+ if (priv->rdev[i]->disabled) \
+ continue; \
+ else
+
+#define rswitch_for_each_enabled_port_continue_reverse(priv, i) \
+ for (i--; i >= 0; i--) \
+ if (priv->rdev[i]->disabled) \
+ continue; \
+ else
#define TX_RING_SIZE 1024
#define RX_RING_SIZE 1024
@@ -938,6 +949,7 @@ struct rswitch_device {
struct rswitch_gwca_queue *tx_queue;
struct rswitch_gwca_queue *rx_queue;
u8 ts_tag;
+ bool disabled;
int port;
struct rswitch_etha *etha;
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index 0556542d7a6b64..3a86f1213a0517 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -1003,8 +1003,11 @@ static int efx_pci_probe_post_io(struct efx_nic *efx)
/* Determine netdevice features */
net_dev->features |= (efx->type->offload_features | NETIF_F_SG |
NETIF_F_TSO | NETIF_F_RXCSUM | NETIF_F_RXALL);
- if (efx->type->offload_features & (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM))
+ if (efx->type->offload_features & (NETIF_F_IPV6_CSUM | NETIF_F_HW_CSUM)) {
net_dev->features |= NETIF_F_TSO6;
+ if (efx_has_cap(efx, TX_TSO_V2_ENCAP))
+ net_dev->hw_enc_features |= NETIF_F_TSO6;
+ }
/* Check whether device supports TSO */
if (!efx->type->tso_versions || !efx->type->tso_versions(efx))
net_dev->features &= ~NETIF_F_ALL_TSO;
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c
index d42e1afb652134..2f7d8e4561d920 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-mediatek.c
@@ -90,7 +90,6 @@ struct mediatek_dwmac_plat_data {
struct mediatek_dwmac_variant {
int (*dwmac_set_phy_interface)(struct mediatek_dwmac_plat_data *plat);
int (*dwmac_set_delay)(struct mediatek_dwmac_plat_data *plat);
- void (*dwmac_fix_mac_speed)(void *priv, unsigned int speed);
/* clock ids to be requested */
const char * const *clk_list;
@@ -443,32 +442,9 @@ static int mt8195_set_delay(struct mediatek_dwmac_plat_data *plat)
return 0;
}
-static void mt8195_fix_mac_speed(void *priv, unsigned int speed)
-{
- struct mediatek_dwmac_plat_data *priv_plat = priv;
-
- if ((phy_interface_mode_is_rgmii(priv_plat->phy_mode))) {
- /* prefer 2ns fixed delay which is controlled by TXC_PHASE_CTRL,
- * when link speed is 1Gbps with RGMII interface,
- * Fall back to delay macro circuit for 10/100Mbps link speed.
- */
- if (speed == SPEED_1000)
- regmap_update_bits(priv_plat->peri_regmap,
- MT8195_PERI_ETH_CTRL0,
- MT8195_RGMII_TXC_PHASE_CTRL |
- MT8195_DLY_GTXC_ENABLE |
- MT8195_DLY_GTXC_INV |
- MT8195_DLY_GTXC_STAGES,
- MT8195_RGMII_TXC_PHASE_CTRL);
- else
- mt8195_set_delay(priv_plat);
- }
-}
-
static const struct mediatek_dwmac_variant mt8195_gmac_variant = {
.dwmac_set_phy_interface = mt8195_set_interface,
.dwmac_set_delay = mt8195_set_delay,
- .dwmac_fix_mac_speed = mt8195_fix_mac_speed,
.clk_list = mt8195_dwmac_clk_l,
.num_clks = ARRAY_SIZE(mt8195_dwmac_clk_l),
.dma_bit_mask = 35,
@@ -619,8 +595,6 @@ static int mediatek_dwmac_common_data(struct platform_device *pdev,
plat->bsp_priv = priv_plat;
plat->init = mediatek_dwmac_init;
plat->clks_config = mediatek_dwmac_clks_config;
- if (priv_plat->variant->dwmac_fix_mac_speed)
- plat->fix_mac_speed = priv_plat->variant->dwmac_fix_mac_speed;
plat->safety_feat_cfg = devm_kzalloc(&pdev->dev,
sizeof(*plat->safety_feat_cfg),
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
index 835caa15d55fff..732774645c1a68 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
@@ -560,6 +560,8 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
plat_dat->has_gmac4 = 1;
plat_dat->pmt = 1;
plat_dat->tso_en = of_property_read_bool(np, "snps,tso");
+ if (of_device_is_compatible(np, "qcom,qcs404-ethqos"))
+ plat_dat->rx_clk_runs_in_lpi = 1;
ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
if (ret)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c
index 9c2d40f853ed04..413f660172199c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c
@@ -186,11 +186,25 @@ static void dwmac5_handle_dma_err(struct net_device *ndev,
int dwmac5_safety_feat_config(void __iomem *ioaddr, unsigned int asp,
struct stmmac_safety_feature_cfg *safety_feat_cfg)
{
+ struct stmmac_safety_feature_cfg all_safety_feats = {
+ .tsoee = 1,
+ .mrxpee = 1,
+ .mestee = 1,
+ .mrxee = 1,
+ .mtxee = 1,
+ .epsi = 1,
+ .edpp = 1,
+ .prtyen = 1,
+ .tmouten = 1,
+ };
u32 value;
if (!asp)
return -EINVAL;
+ if (!safety_feat_cfg)
+ safety_feat_cfg = &all_safety_feats;
+
/* 1. Enable Safety Features */
value = readl(ioaddr + MTL_ECC_CONTROL);
value |= MEEAO; /* MTL ECC Error Addr Status Override */
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
index f453b0d093663d..35c8dd92d3692f 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
@@ -551,16 +551,16 @@ static void stmmac_get_per_qstats(struct stmmac_priv *priv, u64 *data)
p = (char *)priv + offsetof(struct stmmac_priv,
xstats.txq_stats[q].tx_pkt_n);
for (stat = 0; stat < STMMAC_TXQ_STATS; stat++) {
- *data++ = (*(u64 *)p);
- p += sizeof(u64 *);
+ *data++ = (*(unsigned long *)p);
+ p += sizeof(unsigned long);
}
}
for (q = 0; q < rx_cnt; q++) {
p = (char *)priv + offsetof(struct stmmac_priv,
xstats.rxq_stats[q].rx_pkt_n);
for (stat = 0; stat < STMMAC_RXQ_STATS; stat++) {
- *data++ = (*(u64 *)p);
- p += sizeof(u64 *);
+ *data++ = (*(unsigned long *)p);
+ p += sizeof(unsigned long);
}
}
}
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index c6951c976f5ddb..1a5b8dab5e9b65 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1080,7 +1080,8 @@ static void stmmac_mac_link_up(struct phylink_config *config,
stmmac_mac_set(priv, priv->ioaddr, true);
if (phy && priv->dma_cap.eee) {
- priv->eee_active = phy_init_eee(phy, 1) >= 0;
+ priv->eee_active =
+ phy_init_eee(phy, !priv->plat->rx_clk_runs_in_lpi) >= 0;
priv->eee_enabled = stmmac_eee_init(priv);
priv->tx_lpi_enabled = priv->eee_enabled;
stmmac_set_eee_pls(priv, priv->hw, true);
@@ -1150,6 +1151,11 @@ static int stmmac_init_phy(struct net_device *dev)
int addr = priv->plat->phy_addr;
struct phy_device *phydev;
+ if (addr < 0) {
+ netdev_err(priv->dev, "no phy found\n");
+ return -ENODEV;
+ }
+
phydev = mdiobus_get_phy(priv->mii, addr);
if (!phydev) {
netdev_err(priv->dev, "no phy at addr %d\n", addr);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
index fc06ddeac0d534..b4388ca8d21160 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
@@ -210,7 +210,10 @@ static int stmmac_enable(struct ptp_clock_info *ptp,
}
writel(acr_value, ptpaddr + PTP_ACR);
mutex_unlock(&priv->aux_ts_lock);
- ret = 0;
+ /* wait for auxts fifo clear to finish */
+ ret = readl_poll_timeout(ptpaddr + PTP_ACR, acr_value,
+ !(acr_value & PTP_ACR_ATSFC),
+ 10, 10000);
break;
default:
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 9352dad58996d8..79f4e13620a460 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -987,9 +987,6 @@ static void netvsc_copy_to_send_buf(struct netvsc_device *net_device,
void netvsc_dma_unmap(struct hv_device *hv_dev,
struct hv_netvsc_packet *packet)
{
- u32 page_count = packet->cp_partial ?
- packet->page_buf_cnt - packet->rmsg_pgcnt :
- packet->page_buf_cnt;
int i;
if (!hv_is_isolation_supported())
@@ -998,7 +995,7 @@ void netvsc_dma_unmap(struct hv_device *hv_dev,
if (!packet->dma_range)
return;
- for (i = 0; i < page_count; i++)
+ for (i = 0; i < packet->page_buf_cnt; i++)
dma_unmap_single(&hv_dev->device, packet->dma_range[i].dma,
packet->dma_range[i].mapping_size,
DMA_TO_DEVICE);
@@ -1028,9 +1025,7 @@ static int netvsc_dma_map(struct hv_device *hv_dev,
struct hv_netvsc_packet *packet,
struct hv_page_buffer *pb)
{
- u32 page_count = packet->cp_partial ?
- packet->page_buf_cnt - packet->rmsg_pgcnt :
- packet->page_buf_cnt;
+ u32 page_count = packet->page_buf_cnt;
dma_addr_t dma;
int i;
@@ -1039,7 +1034,7 @@ static int netvsc_dma_map(struct hv_device *hv_dev,
packet->dma_range = kcalloc(page_count,
sizeof(*packet->dma_range),
- GFP_KERNEL);
+ GFP_ATOMIC);
if (!packet->dma_range)
return -ENOMEM;
diff --git a/drivers/net/ipa/data/ipa_data-v4.7.c b/drivers/net/ipa/data/ipa_data-v4.7.c
index 7552c400961eb7..b83390c4861580 100644
--- a/drivers/net/ipa/data/ipa_data-v4.7.c
+++ b/drivers/net/ipa/data/ipa_data-v4.7.c
@@ -357,7 +357,7 @@ static const struct ipa_mem ipa_mem_local_data[] = {
static const struct ipa_mem_data ipa_mem_data = {
.local_count = ARRAY_SIZE(ipa_mem_local_data),
.local = ipa_mem_local_data,
- .imem_addr = 0x146a9000,
+ .imem_addr = 0x146a8000,
.imem_size = 0x00002000,
.smem_id = 497,
.smem_size = 0x00009000,
diff --git a/drivers/net/ipa/ipa_interrupt.c b/drivers/net/ipa/ipa_interrupt.c
index a49f66efacb875..c1b3977e1ae484 100644
--- a/drivers/net/ipa/ipa_interrupt.c
+++ b/drivers/net/ipa/ipa_interrupt.c
@@ -127,15 +127,25 @@ out_power_put:
return IRQ_HANDLED;
}
+void ipa_interrupt_irq_disable(struct ipa *ipa)
+{
+ disable_irq(ipa->interrupt->irq);
+}
+
+void ipa_interrupt_irq_enable(struct ipa *ipa)
+{
+ enable_irq(ipa->interrupt->irq);
+}
+
/* Common function used to enable/disable TX_SUSPEND for an endpoint */
static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt,
u32 endpoint_id, bool enable)
{
struct ipa *ipa = interrupt->ipa;
+ u32 mask = BIT(endpoint_id % 32);
u32 unit = endpoint_id / 32;
const struct ipa_reg *reg;
u32 offset;
- u32 mask;
u32 val;
WARN_ON(!test_bit(endpoint_id, ipa->available));
@@ -148,7 +158,6 @@ static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt,
offset = ipa_reg_n_offset(reg, unit);
val = ioread32(ipa->reg_virt + offset);
- mask = BIT(endpoint_id);
if (enable)
val |= mask;
else
diff --git a/drivers/net/ipa/ipa_interrupt.h b/drivers/net/ipa/ipa_interrupt.h
index f31fd9965fdc67..8a1bd5b893932b 100644
--- a/drivers/net/ipa/ipa_interrupt.h
+++ b/drivers/net/ipa/ipa_interrupt.h
@@ -86,6 +86,22 @@ void ipa_interrupt_suspend_clear_all(struct ipa_interrupt *interrupt);
void ipa_interrupt_simulate_suspend(struct ipa_interrupt *interrupt);
/**
+ * ipa_interrupt_irq_enable() - Enable IPA interrupts
+ * @ipa: IPA pointer
+ *
+ * This enables the IPA interrupt line
+ */
+void ipa_interrupt_irq_enable(struct ipa *ipa);
+
+/**
+ * ipa_interrupt_irq_disable() - Disable IPA interrupts
+ * @ipa: IPA pointer
+ *
+ * This disables the IPA interrupt line
+ */
+void ipa_interrupt_irq_disable(struct ipa *ipa);
+
+/**
* ipa_interrupt_config() - Configure the IPA interrupt framework
* @ipa: IPA pointer
*
diff --git a/drivers/net/ipa/ipa_power.c b/drivers/net/ipa/ipa_power.c
index 8420f93128a268..8057be8cda8013 100644
--- a/drivers/net/ipa/ipa_power.c
+++ b/drivers/net/ipa/ipa_power.c
@@ -181,6 +181,17 @@ static int ipa_suspend(struct device *dev)
__set_bit(IPA_POWER_FLAG_SYSTEM, ipa->power->flags);
+ /* Increment the disable depth to ensure that the IRQ won't
+ * be re-enabled until the matching _enable call in
+ * ipa_resume(). We do this to ensure that the interrupt
+ * handler won't run whilst PM runtime is disabled.
+ *
+ * Note that disabling the IRQ is NOT the same as disabling
+ * irq wake. If wakeup is enabled for the IPA then the IRQ
+ * will still cause the system to wake up, see irq_set_irq_wake().
+ */
+ ipa_interrupt_irq_disable(ipa);
+
return pm_runtime_force_suspend(dev);
}
@@ -193,6 +204,12 @@ static int ipa_resume(struct device *dev)
__clear_bit(IPA_POWER_FLAG_SYSTEM, ipa->power->flags);
+ /* Now that PM runtime is enabled again it's safe
+ * to turn the IRQ back on and process any data
+ * that was received during suspend.
+ */
+ ipa_interrupt_irq_enable(ipa);
+
return ret;
}
diff --git a/drivers/net/mdio/mdio-mux-meson-g12a.c b/drivers/net/mdio/mdio-mux-meson-g12a.c
index 4a2e94faf57e2f..c4542ecf562305 100644
--- a/drivers/net/mdio/mdio-mux-meson-g12a.c
+++ b/drivers/net/mdio/mdio-mux-meson-g12a.c
@@ -4,6 +4,7 @@
*/
#include <linux/bitfield.h>
+#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/device.h>
@@ -150,6 +151,7 @@ static const struct clk_ops g12a_ephy_pll_ops = {
static int g12a_enable_internal_mdio(struct g12a_mdio_mux *priv)
{
+ u32 value;
int ret;
/* Enable the phy clock */
@@ -163,18 +165,25 @@ static int g12a_enable_internal_mdio(struct g12a_mdio_mux *priv)
/* Initialize ephy control */
writel(EPHY_G12A_ID, priv->regs + ETH_PHY_CNTL0);
- writel(FIELD_PREP(PHY_CNTL1_ST_MODE, 3) |
- FIELD_PREP(PHY_CNTL1_ST_PHYADD, EPHY_DFLT_ADD) |
- FIELD_PREP(PHY_CNTL1_MII_MODE, EPHY_MODE_RMII) |
- PHY_CNTL1_CLK_EN |
- PHY_CNTL1_CLKFREQ |
- PHY_CNTL1_PHY_ENB,
- priv->regs + ETH_PHY_CNTL1);
+
+ /* Make sure we get a 0 -> 1 transition on the enable bit */
+ value = FIELD_PREP(PHY_CNTL1_ST_MODE, 3) |
+ FIELD_PREP(PHY_CNTL1_ST_PHYADD, EPHY_DFLT_ADD) |
+ FIELD_PREP(PHY_CNTL1_MII_MODE, EPHY_MODE_RMII) |
+ PHY_CNTL1_CLK_EN |
+ PHY_CNTL1_CLKFREQ;
+ writel(value, priv->regs + ETH_PHY_CNTL1);
writel(PHY_CNTL2_USE_INTERNAL |
PHY_CNTL2_SMI_SRC_MAC |
PHY_CNTL2_RX_CLK_EPHY,
priv->regs + ETH_PHY_CNTL2);
+ value |= PHY_CNTL1_PHY_ENB;
+ writel(value, priv->regs + ETH_PHY_CNTL1);
+
+ /* The phy needs a bit of time to power up */
+ mdelay(10);
+
return 0;
}
diff --git a/drivers/net/phy/dp83822.c b/drivers/net/phy/dp83822.c
index a6f05e35d91f2c..b7cb71817780ce 100644
--- a/drivers/net/phy/dp83822.c
+++ b/drivers/net/phy/dp83822.c
@@ -233,7 +233,8 @@ static int dp83822_config_intr(struct phy_device *phydev)
DP83822_ENERGY_DET_INT_EN |
DP83822_LINK_QUAL_INT_EN);
- if (!dp83822->fx_enabled)
+ /* Private data pointer is NULL on DP83825/26 */
+ if (!dp83822 || !dp83822->fx_enabled)
misr_status |= DP83822_ANEG_COMPLETE_INT_EN |
DP83822_DUP_MODE_CHANGE_INT_EN |
DP83822_SPEED_CHANGED_INT_EN;
@@ -253,7 +254,8 @@ static int dp83822_config_intr(struct phy_device *phydev)
DP83822_PAGE_RX_INT_EN |
DP83822_EEE_ERROR_CHANGE_INT_EN);
- if (!dp83822->fx_enabled)
+ /* Private data pointer is NULL on DP83825/26 */
+ if (!dp83822 || !dp83822->fx_enabled)
misr_status |= DP83822_ANEG_ERR_INT_EN |
DP83822_WOL_PKT_INT_EN;
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 1cd604cd1fa1b1..16e021b477f063 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -108,7 +108,12 @@ EXPORT_SYMBOL(mdiobus_unregister_device);
struct phy_device *mdiobus_get_phy(struct mii_bus *bus, int addr)
{
- struct mdio_device *mdiodev = bus->mdio_map[addr];
+ struct mdio_device *mdiodev;
+
+ if (addr < 0 || addr >= ARRAY_SIZE(bus->mdio_map))
+ return NULL;
+
+ mdiodev = bus->mdio_map[addr];
if (!mdiodev)
return NULL;
diff --git a/drivers/net/phy/meson-gxl.c b/drivers/net/phy/meson-gxl.c
index c49062ad72c6c6..a6015cd03bff83 100644
--- a/drivers/net/phy/meson-gxl.c
+++ b/drivers/net/phy/meson-gxl.c
@@ -261,6 +261,8 @@ static struct phy_driver meson_gxl_phy[] = {
.handle_interrupt = meson_gxl_handle_interrupt,
.suspend = genphy_suspend,
.resume = genphy_resume,
+ .read_mmd = genphy_read_mmd_unsupported,
+ .write_mmd = genphy_write_mmd_unsupported,
}, {
PHY_ID_MATCH_EXACT(0x01803301),
.name = "Meson G12A Internal PHY",
@@ -271,6 +273,8 @@ static struct phy_driver meson_gxl_phy[] = {
.handle_interrupt = meson_gxl_handle_interrupt,
.suspend = genphy_suspend,
.resume = genphy_resume,
+ .read_mmd = genphy_read_mmd_unsupported,
+ .write_mmd = genphy_write_mmd_unsupported,
},
};
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 716870a4499c55..607aa786c8cb4e 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1517,7 +1517,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
* another mac interface, so we should create a device link between
* phy dev and mac dev.
*/
- if (phydev->mdio.bus->parent && dev->dev.parent != phydev->mdio.bus->parent)
+ if (dev && phydev->mdio.bus->parent && dev->dev.parent != phydev->mdio.bus->parent)
phydev->devlink = device_link_add(dev->dev.parent, &phydev->mdio.dev,
DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 09cc65c0da9304..4d2519cdb80126 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -1812,10 +1812,9 @@ int phylink_fwnode_phy_connect(struct phylink *pl,
ret = phy_attach_direct(pl->netdev, phy_dev, flags,
pl->link_interface);
- if (ret) {
- phy_device_free(phy_dev);
+ phy_device_free(phy_dev);
+ if (ret)
return ret;
- }
ret = phylink_bringup_phy(pl, phy_dev, pl->link_config.interface);
if (ret)
diff --git a/drivers/net/phy/xilinx_gmii2rgmii.c b/drivers/net/phy/xilinx_gmii2rgmii.c
index 8dcb49ed1f3d9a..7fd9fe6a602bcb 100644
--- a/drivers/net/phy/xilinx_gmii2rgmii.c
+++ b/drivers/net/phy/xilinx_gmii2rgmii.c
@@ -105,6 +105,7 @@ static int xgmiitorgmii_probe(struct mdio_device *mdiodev)
if (!priv->phy_dev->drv) {
dev_info(dev, "Attached phy not ready\n");
+ put_device(&priv->phy_dev->mdio.dev);
return -EPROBE_DEFER;
}
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index fcd43d62d86bcb..d10606f257c433 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -1044,7 +1044,6 @@ static int team_port_enter(struct team *team, struct team_port *port)
goto err_port_enter;
}
}
- port->dev->priv_flags |= IFF_NO_ADDRCONF;
return 0;
@@ -1058,7 +1057,6 @@ static void team_port_leave(struct team *team, struct team_port *port)
{
if (team->ops.port_leave)
team->ops.port_leave(team, port);
- port->dev->priv_flags &= ~IFF_NO_ADDRCONF;
dev_put(team->dev);
}
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 8911cd2ed5343d..c140edb4b6482c 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -1008,6 +1008,12 @@ static const struct usb_device_id products[] = {
USB_CDC_PROTO_NONE),
.driver_info = (unsigned long)&wwan_info,
}, {
+ /* Cinterion PLS62-W modem by GEMALTO/THALES */
+ USB_DEVICE_AND_INTERFACE_INFO(0x1e2d, 0x005b, USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ETHERNET,
+ USB_CDC_PROTO_NONE),
+ .driver_info = (unsigned long)&wwan_info,
+}, {
/* Cinterion PLS83/PLS63 modem by GEMALTO/THALES */
USB_DEVICE_AND_INTERFACE_INFO(0x1e2d, 0x0069, USB_CLASS_COMM,
USB_CDC_SUBCLASS_ETHERNET,
diff --git a/drivers/net/usb/plusb.c b/drivers/net/usb/plusb.c
index 2c82fbcaab2239..7a2b0094de51f5 100644
--- a/drivers/net/usb/plusb.c
+++ b/drivers/net/usb/plusb.c
@@ -57,9 +57,7 @@
static inline int
pl_vendor_req(struct usbnet *dev, u8 req, u8 val, u8 index)
{
- return usbnet_read_cmd(dev, req,
- USB_DIR_IN | USB_TYPE_VENDOR |
- USB_RECIP_DEVICE,
+ return usbnet_write_cmd(dev, req, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
val, index, NULL, 0);
}
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c
index a481a1d831e2f4..23da1d9dafd1f3 100644
--- a/drivers/net/usb/r8152.c
+++ b/drivers/net/usb/r8152.c
@@ -9836,6 +9836,7 @@ static const struct usb_device_id rtl8152_table[] = {
REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07ab),
REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x07c6),
REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x0927),
+ REALTEK_USB_DEVICE(VENDOR_ID_MICROSOFT, 0x0c5e),
REALTEK_USB_DEVICE(VENDOR_ID_SAMSUNG, 0xa101),
REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x304f),
REALTEK_USB_DEVICE(VENDOR_ID_LENOVO, 0x3054),
diff --git a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
index f79333fe17836c..7b3739b29c8f72 100644
--- a/drivers/net/usb/rndis_host.c
+++ b/drivers/net/usb/rndis_host.c
@@ -255,7 +255,8 @@ static int rndis_query(struct usbnet *dev, struct usb_interface *intf,
off = le32_to_cpu(u.get_c->offset);
len = le32_to_cpu(u.get_c->len);
- if (unlikely((8 + off + len) > CONTROL_BUFFER_SIZE))
+ if (unlikely((off > CONTROL_BUFFER_SIZE - 8) ||
+ (len > CONTROL_BUFFER_SIZE - 8 - off)))
goto response_error;
if (*reply_len != -1 && len != *reply_len)
diff --git a/drivers/net/usb/sr9700.c b/drivers/net/usb/sr9700.c
index 5a53e63d33a609..3164451e1010cc 100644
--- a/drivers/net/usb/sr9700.c
+++ b/drivers/net/usb/sr9700.c
@@ -413,7 +413,7 @@ static int sr9700_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
/* ignore the CRC length */
len = (skb->data[1] | (skb->data[2] << 8)) - 4;
- if (len > ETH_FRAME_LEN || len > skb->len)
+ if (len > ETH_FRAME_LEN || len > skb->len || len < 0)
return 0;
/* the last packet of current skb */
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index ac7c0653695f04..dfc7d87fad59fe 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -974,6 +974,9 @@ static int veth_poll(struct napi_struct *napi, int budget)
xdp_set_return_frame_no_direct();
done = veth_xdp_rcv(rq, budget, &bq, &stats);
+ if (stats.xdp_redirect > 0)
+ xdp_do_flush();
+
if (done < budget && napi_complete_done(napi, done)) {
/* Write rx_notify_masked before reading ptr_ring */
smp_store_mb(rq->rx_notify_masked, false);
@@ -987,8 +990,6 @@ static int veth_poll(struct napi_struct *napi, int budget)
if (stats.xdp_tx > 0)
veth_xdp_flush(rq, &bq);
- if (stats.xdp_redirect > 0)
- xdp_do_flush();
xdp_clear_return_frame_no_direct();
return done;
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 7723b2a49d8ecc..61e33e4dd0cd66 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -1677,13 +1677,13 @@ static int virtnet_poll(struct napi_struct *napi, int budget)
received = virtnet_receive(rq, budget, &xdp_xmit);
+ if (xdp_xmit & VIRTIO_XDP_REDIR)
+ xdp_do_flush();
+
/* Out of packets? */
if (received < budget)
virtqueue_napi_complete(napi, rq->vq, received);
- if (xdp_xmit & VIRTIO_XDP_REDIR)
- xdp_do_flush();
-
if (xdp_xmit & VIRTIO_XDP_TX) {
sq = virtnet_xdp_get_sq(vi);
if (virtqueue_kick_prepare(sq->vq) && virtqueue_notify(sq->vq)) {
@@ -1877,8 +1877,10 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)
*/
if (sq->vq->num_free < 2+MAX_SKB_FRAGS) {
netif_stop_subqueue(dev, qnum);
- if (!use_napi &&
- unlikely(!virtqueue_enable_cb_delayed(sq->vq))) {
+ if (use_napi) {
+ if (unlikely(!virtqueue_enable_cb_delayed(sq->vq)))
+ virtqueue_napi_schedule(&sq->napi, sq->vq);
+ } else if (unlikely(!virtqueue_enable_cb_delayed(sq->vq))) {
/* More just got used, free them then recheck. */
free_old_xmit_skbs(sq, false);
if (sq->vq->num_free >= 2+MAX_SKB_FRAGS) {
@@ -2156,8 +2158,8 @@ static int virtnet_close(struct net_device *dev)
cancel_delayed_work_sync(&vi->refill);
for (i = 0; i < vi->max_queue_pairs; i++) {
- xdp_rxq_info_unreg(&vi->rq[i].xdp_rxq);
napi_disable(&vi->rq[i].napi);
+ xdp_rxq_info_unreg(&vi->rq[i].xdp_rxq);
virtnet_napi_tx_disable(&vi->sq[i].napi);
}
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
index 6f1e560fb15c4f..56267c327f0b74 100644
--- a/drivers/net/vmxnet3/vmxnet3_drv.c
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c
@@ -1288,6 +1288,10 @@ vmxnet3_rx_csum(struct vmxnet3_adapter *adapter,
(le32_to_cpu(gdesc->dword[3]) &
VMXNET3_RCD_CSUM_OK) == VMXNET3_RCD_CSUM_OK) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
+ if ((le32_to_cpu(gdesc->dword[0]) &
+ (1UL << VMXNET3_RCD_HDR_INNER_SHIFT))) {
+ skb->csum_level = 1;
+ }
WARN_ON_ONCE(!(gdesc->rcd.tcp || gdesc->rcd.udp) &&
!(le32_to_cpu(gdesc->dword[0]) &
(1UL << VMXNET3_RCD_HDR_INNER_SHIFT)));
@@ -1297,6 +1301,10 @@ vmxnet3_rx_csum(struct vmxnet3_adapter *adapter,
} else if (gdesc->rcd.v6 && (le32_to_cpu(gdesc->dword[3]) &
(1 << VMXNET3_RCD_TUC_SHIFT))) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
+ if ((le32_to_cpu(gdesc->dword[0]) &
+ (1UL << VMXNET3_RCD_HDR_INNER_SHIFT))) {
+ skb->csum_level = 1;
+ }
WARN_ON_ONCE(!(gdesc->rcd.tcp || gdesc->rcd.udp) &&
!(le32_to_cpu(gdesc->dword[0]) &
(1UL << VMXNET3_RCD_HDR_INNER_SHIFT)));
diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c
index 6b5a4d036d1533..bdb3a76a352e45 100644
--- a/drivers/net/vrf.c
+++ b/drivers/net/vrf.c
@@ -1385,8 +1385,8 @@ static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev,
/* loopback, multicast & non-ND link-local traffic; do not push through
* packet taps again. Reset pkt_type for upper layers to process skb.
- * For strict packets with a source LLA, determine the dst using the
- * original ifindex.
+ * For non-loopback strict packets, determine the dst using the original
+ * ifindex.
*/
if (skb->pkt_type == PACKET_LOOPBACK || (need_strict && !is_ndisc)) {
skb->dev = vrf_dev;
@@ -1395,7 +1395,7 @@ static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev,
if (skb->pkt_type == PACKET_LOOPBACK)
skb->pkt_type = PACKET_HOST;
- else if (ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)
+ else
vrf_ip6_input_dst(skb, vrf_dev, orig_iif);
goto out;
diff --git a/drivers/net/vxlan/vxlan_core.c b/drivers/net/vxlan/vxlan_core.c
index 92224b36787a83..b1b179effe2a13 100644
--- a/drivers/net/vxlan/vxlan_core.c
+++ b/drivers/net/vxlan/vxlan_core.c
@@ -2917,16 +2917,23 @@ static int vxlan_init(struct net_device *dev)
vxlan_vnigroup_init(vxlan);
dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
- if (!dev->tstats)
- return -ENOMEM;
+ if (!dev->tstats) {
+ err = -ENOMEM;
+ goto err_vnigroup_uninit;
+ }
err = gro_cells_init(&vxlan->gro_cells, dev);
- if (err) {
- free_percpu(dev->tstats);
- return err;
- }
+ if (err)
+ goto err_free_percpu;
return 0;
+
+err_free_percpu:
+ free_percpu(dev->tstats);
+err_vnigroup_uninit:
+ if (vxlan->cfg.flags & VXLAN_F_VNIFILTER)
+ vxlan_vnigroup_uninit(vxlan);
+ return err;
}
static void vxlan_fdb_delete_default(struct vxlan_dev *vxlan, __be32 vni)
diff --git a/drivers/net/wan/fsl_ucc_hdlc.c b/drivers/net/wan/fsl_ucc_hdlc.c
index 22edea6ca4b817..1c53b554692705 100644
--- a/drivers/net/wan/fsl_ucc_hdlc.c
+++ b/drivers/net/wan/fsl_ucc_hdlc.c
@@ -1243,9 +1243,11 @@ static int ucc_hdlc_probe(struct platform_device *pdev)
free_dev:
free_netdev(dev);
undo_uhdlc_init:
- iounmap(utdm->siram);
+ if (utdm)
+ iounmap(utdm->siram);
unmap_si_regs:
- iounmap(utdm->si_regs);
+ if (utdm)
+ iounmap(utdm->si_regs);
free_utdm:
if (uhdlc_priv->tsa)
kfree(utdm);
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index 30f0765fb9fd8f..237f4ec2cffd7b 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -327,9 +327,9 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb)
}
#ifdef CONFIG_ATH9K_HTC_DEBUGFS
-#define __STAT_SAFE(hif_dev, expr) ((hif_dev)->htc_handle->drv_priv ? (expr) : 0)
-#define CAB_STAT_INC(priv) ((priv)->debug.tx_stats.cab_queued++)
-#define TX_QSTAT_INC(priv, q) ((priv)->debug.tx_stats.queue_stats[q]++)
+#define __STAT_SAFE(hif_dev, expr) do { ((hif_dev)->htc_handle->drv_priv ? (expr) : 0); } while (0)
+#define CAB_STAT_INC(priv) do { ((priv)->debug.tx_stats.cab_queued++); } while (0)
+#define TX_QSTAT_INC(priv, q) do { ((priv)->debug.tx_stats.queue_stats[q]++); } while (0)
#define TX_STAT_INC(hif_dev, c) \
__STAT_SAFE((hif_dev), (hif_dev)->htc_handle->drv_priv->debug.tx_stats.c++)
@@ -378,10 +378,10 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
struct ethtool_stats *stats, u64 *data);
#else
-#define TX_STAT_INC(hif_dev, c)
-#define TX_STAT_ADD(hif_dev, c, a)
-#define RX_STAT_INC(hif_dev, c)
-#define RX_STAT_ADD(hif_dev, c, a)
+#define TX_STAT_INC(hif_dev, c) do { } while (0)
+#define TX_STAT_ADD(hif_dev, c, a) do { } while (0)
+#define RX_STAT_INC(hif_dev, c) do { } while (0)
+#define RX_STAT_ADD(hif_dev, c, a) do { } while (0)
#define CAB_STAT_INC(priv)
#define TX_QSTAT_INC(priv, c)
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index bff3128c2f261c..b115902eb475e9 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -7937,6 +7937,9 @@ cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev,
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
+ if (chan->flags & IEEE80211_CHAN_DISABLED)
+ return -EINVAL;
+
/* set_channel */
chspec = channel_to_chanspec(&cfg->d11inf, chan);
if (chspec != INVCHANSPEC) {
@@ -7961,7 +7964,7 @@ brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev,
struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
struct brcmf_dump_survey survey = {};
struct ieee80211_supported_band *band;
- struct ieee80211_channel *chan;
+ enum nl80211_band band_id;
struct cca_msrmnt_query req;
u32 noise;
int err;
@@ -7974,26 +7977,25 @@ brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev,
return -EBUSY;
}
- band = wiphy->bands[NL80211_BAND_2GHZ];
- if (band && idx >= band->n_channels) {
- idx -= band->n_channels;
- band = NULL;
- }
+ for (band_id = 0; band_id < NUM_NL80211_BANDS; band_id++) {
+ band = wiphy->bands[band_id];
+ if (!band)
+ continue;
+ if (idx >= band->n_channels) {
+ idx -= band->n_channels;
+ continue;
+ }
- if (!band || idx >= band->n_channels) {
- band = wiphy->bands[NL80211_BAND_5GHZ];
- if (idx >= band->n_channels)
- return -ENOENT;
+ info->channel = &band->channels[idx];
+ break;
}
+ if (band_id == NUM_NL80211_BANDS)
+ return -ENOENT;
/* Setting current channel to the requested channel */
- chan = &band->channels[idx];
- err = cfg80211_set_channel(wiphy, ndev, chan, NL80211_CHAN_HT20);
- if (err) {
- info->channel = chan;
- info->filled = 0;
+ info->filled = 0;
+ if (cfg80211_set_channel(wiphy, ndev, info->channel, NL80211_CHAN_HT20))
return 0;
- }
/* Disable mpc */
brcmf_set_mpc(ifp, 0);
@@ -8028,7 +8030,6 @@ brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev,
if (err)
goto exit;
- info->channel = chan;
info->noise = noise;
info->time = ACS_MSRMNT_DELAY;
info->time_busy = ACS_MSRMNT_DELAY - survey.idle;
@@ -8040,7 +8041,7 @@ brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev,
SURVEY_INFO_TIME_TX;
brcmf_dbg(INFO, "OBSS dump: channel %d: survey duration %d\n",
- ieee80211_frequency_to_channel(chan->center_freq),
+ ieee80211_frequency_to_channel(info->channel->center_freq),
ACS_MSRMNT_DELAY);
brcmf_dbg(INFO, "noise(%d) busy(%llu) rx(%llu) tx(%llu)\n",
info->noise, info->time_busy, info->time_rx, info->time_tx);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
index a83699de01ec3c..fdd0c9abc1a10d 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c
@@ -79,7 +79,8 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
/* Apple ARM64 platforms have their own idea of board type, passed in
* via the device tree. They also have an antenna SKU parameter
*/
- if (!of_property_read_string(np, "brcm,board-type", &prop))
+ err = of_property_read_string(np, "brcm,board-type", &prop);
+ if (!err)
settings->board_type = prop;
if (!of_property_read_string(np, "apple,antenna-sku", &prop))
@@ -87,7 +88,7 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type,
/* Set board-type to the first string of the machine compatible prop */
root = of_find_node_by_path("/");
- if (root && !settings->board_type) {
+ if (root && err) {
char *board_type;
const char *tmp;
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
index ae57a9a3ab055f..b67f6d0810b6cc 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c
@@ -1228,7 +1228,7 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo)
BRCMF_NROF_H2D_COMMON_MSGRINGS;
max_completionrings = BRCMF_NROF_D2H_COMMON_MSGRINGS;
}
- if (max_flowrings > 256) {
+ if (max_flowrings > 512) {
brcmf_err(bus, "invalid max_flowrings(%d)\n", max_flowrings);
return -EIO;
}
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
index e6d64152c81a75..a02e5a67b7066b 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c
@@ -1106,6 +1106,11 @@ int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *c
int i, j, num_sub_bands;
s8 *gain;
+ /* many firmware images for JF lie about this */
+ if (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id) ==
+ CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF))
+ return -EOPNOTSUPP;
+
if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) {
IWL_DEBUG_RADIO(fwrt,
"PPAG capability not supported by FW, command not sent.\n");
diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c
index f795548562f556..06161815c180ed 100644
--- a/drivers/net/wireless/mediatek/mt76/dma.c
+++ b/drivers/net/wireless/mediatek/mt76/dma.c
@@ -206,71 +206,103 @@ mt76_dma_queue_reset(struct mt76_dev *dev, struct mt76_queue *q)
}
static int
+mt76_dma_add_rx_buf(struct mt76_dev *dev, struct mt76_queue *q,
+ struct mt76_queue_buf *buf, void *data)
+{
+ struct mt76_desc *desc = &q->desc[q->head];
+ struct mt76_queue_entry *entry = &q->entry[q->head];
+ struct mt76_txwi_cache *txwi = NULL;
+ u32 buf1 = 0, ctrl;
+ int idx = q->head;
+ int rx_token;
+
+ ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len);
+
+ if ((q->flags & MT_QFLAG_WED) &&
+ FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) {
+ txwi = mt76_get_rxwi(dev);
+ if (!txwi)
+ return -ENOMEM;
+
+ rx_token = mt76_rx_token_consume(dev, data, txwi, buf->addr);
+ if (rx_token < 0) {
+ mt76_put_rxwi(dev, txwi);
+ return -ENOMEM;
+ }
+
+ buf1 |= FIELD_PREP(MT_DMA_CTL_TOKEN, rx_token);
+ ctrl |= MT_DMA_CTL_TO_HOST;
+ }
+
+ WRITE_ONCE(desc->buf0, cpu_to_le32(buf->addr));
+ WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
+ WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
+ WRITE_ONCE(desc->info, 0);
+
+ entry->dma_addr[0] = buf->addr;
+ entry->dma_len[0] = buf->len;
+ entry->txwi = txwi;
+ entry->buf = data;
+ entry->wcid = 0xffff;
+ entry->skip_buf1 = true;
+ q->head = (q->head + 1) % q->ndesc;
+ q->queued++;
+
+ return idx;
+}
+
+static int
mt76_dma_add_buf(struct mt76_dev *dev, struct mt76_queue *q,
struct mt76_queue_buf *buf, int nbufs, u32 info,
struct sk_buff *skb, void *txwi)
{
struct mt76_queue_entry *entry;
struct mt76_desc *desc;
- u32 ctrl;
int i, idx = -1;
+ u32 ctrl, next;
+
+ if (txwi) {
+ q->entry[q->head].txwi = DMA_DUMMY_DATA;
+ q->entry[q->head].skip_buf0 = true;
+ }
for (i = 0; i < nbufs; i += 2, buf += 2) {
u32 buf0 = buf[0].addr, buf1 = 0;
idx = q->head;
- q->head = (q->head + 1) % q->ndesc;
+ next = (q->head + 1) % q->ndesc;
desc = &q->desc[idx];
entry = &q->entry[idx];
- if ((q->flags & MT_QFLAG_WED) &&
- FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) {
- struct mt76_txwi_cache *t = txwi;
- int rx_token;
-
- if (!t)
- return -ENOMEM;
-
- rx_token = mt76_rx_token_consume(dev, (void *)skb, t,
- buf[0].addr);
- buf1 |= FIELD_PREP(MT_DMA_CTL_TOKEN, rx_token);
- ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len) |
- MT_DMA_CTL_TO_HOST;
- } else {
- if (txwi) {
- q->entry[q->head].txwi = DMA_DUMMY_DATA;
- q->entry[q->head].skip_buf0 = true;
- }
-
- if (buf[0].skip_unmap)
- entry->skip_buf0 = true;
- entry->skip_buf1 = i == nbufs - 1;
-
- entry->dma_addr[0] = buf[0].addr;
- entry->dma_len[0] = buf[0].len;
-
- ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len);
- if (i < nbufs - 1) {
- entry->dma_addr[1] = buf[1].addr;
- entry->dma_len[1] = buf[1].len;
- buf1 = buf[1].addr;
- ctrl |= FIELD_PREP(MT_DMA_CTL_SD_LEN1, buf[1].len);
- if (buf[1].skip_unmap)
- entry->skip_buf1 = true;
- }
-
- if (i == nbufs - 1)
- ctrl |= MT_DMA_CTL_LAST_SEC0;
- else if (i == nbufs - 2)
- ctrl |= MT_DMA_CTL_LAST_SEC1;
+ if (buf[0].skip_unmap)
+ entry->skip_buf0 = true;
+ entry->skip_buf1 = i == nbufs - 1;
+
+ entry->dma_addr[0] = buf[0].addr;
+ entry->dma_len[0] = buf[0].len;
+
+ ctrl = FIELD_PREP(MT_DMA_CTL_SD_LEN0, buf[0].len);
+ if (i < nbufs - 1) {
+ entry->dma_addr[1] = buf[1].addr;
+ entry->dma_len[1] = buf[1].len;
+ buf1 = buf[1].addr;
+ ctrl |= FIELD_PREP(MT_DMA_CTL_SD_LEN1, buf[1].len);
+ if (buf[1].skip_unmap)
+ entry->skip_buf1 = true;
}
+ if (i == nbufs - 1)
+ ctrl |= MT_DMA_CTL_LAST_SEC0;
+ else if (i == nbufs - 2)
+ ctrl |= MT_DMA_CTL_LAST_SEC1;
+
WRITE_ONCE(desc->buf0, cpu_to_le32(buf0));
WRITE_ONCE(desc->buf1, cpu_to_le32(buf1));
WRITE_ONCE(desc->info, cpu_to_le32(info));
WRITE_ONCE(desc->ctrl, cpu_to_le32(ctrl));
+ q->head = next;
q->queued++;
}
@@ -577,17 +609,9 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
spin_lock_bh(&q->lock);
while (q->queued < q->ndesc - 1) {
- struct mt76_txwi_cache *t = NULL;
struct mt76_queue_buf qbuf;
void *buf = NULL;
- if ((q->flags & MT_QFLAG_WED) &&
- FIELD_GET(MT_QFLAG_WED_TYPE, q->flags) == MT76_WED_Q_RX) {
- t = mt76_get_rxwi(dev);
- if (!t)
- break;
- }
-
buf = page_frag_alloc(rx_page, q->buf_size, GFP_ATOMIC);
if (!buf)
break;
@@ -601,7 +625,12 @@ mt76_dma_rx_fill(struct mt76_dev *dev, struct mt76_queue *q)
qbuf.addr = addr + offset;
qbuf.len = len - offset;
qbuf.skip_unmap = false;
- mt76_dma_add_buf(dev, q, &qbuf, 1, 0, buf, t);
+ if (mt76_dma_add_rx_buf(dev, q, &qbuf, buf) < 0) {
+ dma_unmap_single(dev->dma_dev, addr, len,
+ DMA_FROM_DEVICE);
+ skb_free_frag(buf);
+ break;
+ }
frames++;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c
index 0a95c3da241b50..8388e2a658535f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c
@@ -653,6 +653,13 @@ static u32 mt7915_mmio_wed_init_rx_buf(struct mtk_wed_device *wed, int size)
desc->buf0 = cpu_to_le32(phy_addr);
token = mt76_rx_token_consume(&dev->mt76, ptr, t, phy_addr);
+ if (token < 0) {
+ dma_unmap_single(dev->mt76.dma_dev, phy_addr,
+ wed->wlan.rx_size, DMA_TO_DEVICE);
+ skb_free_frag(ptr);
+ goto unmap;
+ }
+
desc->token |= cpu_to_le32(FIELD_PREP(MT_DMA_CTL_TOKEN,
token));
desc++;
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
index 5c5fc569e6d59a..79fb47a73c91d0 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/Kconfig
@@ -2,6 +2,7 @@
config MT7996E
tristate "MediaTek MT7996 (PCIe) support"
select MT76_CONNAC_LIB
+ select RELAY
depends on MAC80211
depends on PCI
help
diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c
index 24568b98ed9dfb..1f309d05380ad5 100644
--- a/drivers/net/wireless/mediatek/mt76/tx.c
+++ b/drivers/net/wireless/mediatek/mt76/tx.c
@@ -764,11 +764,12 @@ int mt76_rx_token_consume(struct mt76_dev *dev, void *ptr,
spin_lock_bh(&dev->rx_token_lock);
token = idr_alloc(&dev->rx_token, t, 0, dev->rx_token_size,
GFP_ATOMIC);
+ if (token >= 0) {
+ t->ptr = ptr;
+ t->dma_addr = phys;
+ }
spin_unlock_bh(&dev->rx_token_lock);
- t->ptr = ptr;
- t->dma_addr = phys;
-
return token;
}
EXPORT_SYMBOL_GPL(mt76_rx_token_consume);
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 82a7458e01aec0..bf72e5fd39cf40 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -696,8 +696,8 @@ static int rndis_query_oid(struct usbnet *dev, u32 oid, void *data, int *len)
struct rndis_query *get;
struct rndis_query_c *get_c;
} u;
- int ret, buflen;
- int resplen, respoffs, copylen;
+ int ret;
+ size_t buflen, resplen, respoffs, copylen;
buflen = *len + sizeof(*u.get);
if (buflen < CONTROL_BUFFER_SIZE)
@@ -732,22 +732,15 @@ static int rndis_query_oid(struct usbnet *dev, u32 oid, void *data, int *len)
if (respoffs > buflen) {
/* Device returned data offset outside buffer, error. */
- netdev_dbg(dev->net, "%s(%s): received invalid "
- "data offset: %d > %d\n", __func__,
- oid_to_string(oid), respoffs, buflen);
+ netdev_dbg(dev->net,
+ "%s(%s): received invalid data offset: %zu > %zu\n",
+ __func__, oid_to_string(oid), respoffs, buflen);
ret = -EINVAL;
goto exit_unlock;
}
- if ((resplen + respoffs) > buflen) {
- /* Device would have returned more data if buffer would
- * have been big enough. Copy just the bits that we got.
- */
- copylen = buflen - respoffs;
- } else {
- copylen = resplen;
- }
+ copylen = min(resplen, buflen - respoffs);
if (copylen > *len)
copylen = *len;
diff --git a/drivers/net/wireless/ti/Makefile b/drivers/net/wireless/ti/Makefile
index 0530dd744275c5..05ee016594f8cf 100644
--- a/drivers/net/wireless/ti/Makefile
+++ b/drivers/net/wireless/ti/Makefile
@@ -3,6 +3,3 @@ obj-$(CONFIG_WLCORE) += wlcore/
obj-$(CONFIG_WL12XX) += wl12xx/
obj-$(CONFIG_WL1251) += wl1251/
obj-$(CONFIG_WL18XX) += wl18xx/
-
-# small builtin driver bit
-obj-$(CONFIG_WILINK_PLATFORM_DATA) += wilink_platform_data.o
diff --git a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.c b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.c
index 7eff3531b9a539..7ff33c1d6ac75f 100644
--- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.c
+++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.c
@@ -152,6 +152,15 @@ static irqreturn_t t7xx_dpmaif_isr_handler(int irq, void *data)
}
t7xx_pcie_mac_clear_int(dpmaif_ctrl->t7xx_dev, isr_para->pcie_int);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t t7xx_dpmaif_isr_thread(int irq, void *data)
+{
+ struct dpmaif_isr_para *isr_para = data;
+ struct dpmaif_ctrl *dpmaif_ctrl = isr_para->dpmaif_ctrl;
+
t7xx_dpmaif_irq_cb(isr_para);
t7xx_pcie_mac_set_int(dpmaif_ctrl->t7xx_dev, isr_para->pcie_int);
return IRQ_HANDLED;
@@ -188,7 +197,7 @@ static void t7xx_dpmaif_register_pcie_irq(struct dpmaif_ctrl *dpmaif_ctrl)
t7xx_pcie_mac_clear_int(t7xx_dev, int_type);
t7xx_dev->intr_handler[int_type] = t7xx_dpmaif_isr_handler;
- t7xx_dev->intr_thread[int_type] = NULL;
+ t7xx_dev->intr_thread[int_type] = t7xx_dpmaif_isr_thread;
t7xx_dev->callback_param[int_type] = isr_para;
t7xx_pcie_mac_clear_int_status(t7xx_dev, int_type);
diff --git a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
index aa2174a1043727..f4ff2198b5ef81 100644
--- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
+++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
@@ -840,14 +840,13 @@ int t7xx_dpmaif_napi_rx_poll(struct napi_struct *napi, const int budget)
if (!rxq->que_started) {
atomic_set(&rxq->rx_processing, 0);
+ pm_runtime_put_autosuspend(rxq->dpmaif_ctrl->dev);
dev_err(rxq->dpmaif_ctrl->dev, "Work RXQ: %d has not been started\n", rxq->index);
return work_done;
}
- if (!rxq->sleep_lock_pending) {
- pm_runtime_get_noresume(rxq->dpmaif_ctrl->dev);
+ if (!rxq->sleep_lock_pending)
t7xx_pci_disable_sleep(t7xx_dev);
- }
ret = try_wait_for_completion(&t7xx_dev->sleep_lock_acquire);
if (!ret) {
@@ -876,22 +875,22 @@ int t7xx_dpmaif_napi_rx_poll(struct napi_struct *napi, const int budget)
napi_complete_done(napi, work_done);
t7xx_dpmaif_clr_ip_busy_sts(&rxq->dpmaif_ctrl->hw_info);
t7xx_dpmaif_dlq_unmask_rx_done(&rxq->dpmaif_ctrl->hw_info, rxq->index);
+ t7xx_pci_enable_sleep(rxq->dpmaif_ctrl->t7xx_dev);
+ pm_runtime_mark_last_busy(rxq->dpmaif_ctrl->dev);
+ pm_runtime_put_autosuspend(rxq->dpmaif_ctrl->dev);
+ atomic_set(&rxq->rx_processing, 0);
} else {
t7xx_dpmaif_clr_ip_busy_sts(&rxq->dpmaif_ctrl->hw_info);
}
- t7xx_pci_enable_sleep(rxq->dpmaif_ctrl->t7xx_dev);
- pm_runtime_mark_last_busy(rxq->dpmaif_ctrl->dev);
- pm_runtime_put_noidle(rxq->dpmaif_ctrl->dev);
- atomic_set(&rxq->rx_processing, 0);
-
return work_done;
}
void t7xx_dpmaif_irq_rx_done(struct dpmaif_ctrl *dpmaif_ctrl, const unsigned int que_mask)
{
struct dpmaif_rx_queue *rxq;
- int qno;
+ struct dpmaif_ctrl *ctrl;
+ int qno, ret;
qno = ffs(que_mask) - 1;
if (qno < 0 || qno > DPMAIF_RXQ_NUM - 1) {
@@ -900,6 +899,18 @@ void t7xx_dpmaif_irq_rx_done(struct dpmaif_ctrl *dpmaif_ctrl, const unsigned int
}
rxq = &dpmaif_ctrl->rxq[qno];
+ ctrl = rxq->dpmaif_ctrl;
+ /* We need to make sure that the modem has been resumed before
+ * calling napi. This can't be done inside the polling function
+ * as we could be blocked waiting for device to be resumed,
+ * which can't be done from softirq context the poll function
+ * is running in.
+ */
+ ret = pm_runtime_resume_and_get(ctrl->dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err(ctrl->dev, "Failed to resume device: %d\n", ret);
+ return;
+ }
napi_schedule(&rxq->napi);
}
diff --git a/drivers/net/wwan/t7xx/t7xx_netdev.c b/drivers/net/wwan/t7xx/t7xx_netdev.c
index 494a28e386a380..3ef4a8a4f8fdbc 100644
--- a/drivers/net/wwan/t7xx/t7xx_netdev.c
+++ b/drivers/net/wwan/t7xx/t7xx_netdev.c
@@ -27,6 +27,7 @@
#include <linux/list.h>
#include <linux/netdev_features.h>
#include <linux/netdevice.h>
+#include <linux/pm_runtime.h>
#include <linux/skbuff.h>
#include <linux/types.h>
#include <linux/wwan.h>
@@ -45,12 +46,25 @@
static void t7xx_ccmni_enable_napi(struct t7xx_ccmni_ctrl *ctlb)
{
- int i;
+ struct dpmaif_ctrl *ctrl;
+ int i, ret;
+
+ ctrl = ctlb->hif_ctrl;
if (ctlb->is_napi_en)
return;
for (i = 0; i < RXQ_NUM; i++) {
+ /* The usage count has to be bumped every time before calling
+ * napi_schedule. It will be decresed in the poll routine,
+ * right after napi_complete_done is called.
+ */
+ ret = pm_runtime_resume_and_get(ctrl->dev);
+ if (ret < 0) {
+ dev_err(ctrl->dev, "Failed to resume device: %d\n",
+ ret);
+ return;
+ }
napi_enable(ctlb->napi[i]);
napi_schedule(ctlb->napi[i]);
}
diff --git a/drivers/net/wwan/t7xx/t7xx_pci.c b/drivers/net/wwan/t7xx/t7xx_pci.c
index 871f2a27a398a3..226fc1703e90f2 100644
--- a/drivers/net/wwan/t7xx/t7xx_pci.c
+++ b/drivers/net/wwan/t7xx/t7xx_pci.c
@@ -121,6 +121,8 @@ void t7xx_pci_pm_init_late(struct t7xx_pci_dev *t7xx_dev)
iowrite32(T7XX_L1_BIT(0), IREG_BASE(t7xx_dev) + ENABLE_ASPM_LOWPWR);
atomic_set(&t7xx_dev->md_pm_state, MTK_PM_RESUMED);
+ pm_runtime_mark_last_busy(&t7xx_dev->pdev->dev);
+ pm_runtime_allow(&t7xx_dev->pdev->dev);
pm_runtime_put_noidle(&t7xx_dev->pdev->dev);
}
diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c
index c1ba4294f36479..001636901ddae2 100644
--- a/drivers/net/xen-netback/xenbus.c
+++ b/drivers/net/xen-netback/xenbus.c
@@ -977,7 +977,7 @@ static int read_xenbus_vif_flags(struct backend_info *be)
return 0;
}
-static int netback_remove(struct xenbus_device *dev)
+static void netback_remove(struct xenbus_device *dev)
{
struct backend_info *be = dev_get_drvdata(&dev->dev);
@@ -992,7 +992,6 @@ static int netback_remove(struct xenbus_device *dev)
kfree(be->hotplug_script);
kfree(be);
dev_set_drvdata(&dev->dev, NULL);
- return 0;
}
/*
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index 14aec417fa063e..12b074286df9da 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -2646,7 +2646,7 @@ static void xennet_bus_close(struct xenbus_device *dev)
} while (!ret);
}
-static int xennet_remove(struct xenbus_device *dev)
+static void xennet_remove(struct xenbus_device *dev)
{
struct netfront_info *info = dev_get_drvdata(&dev->dev);
@@ -2662,8 +2662,6 @@ static int xennet_remove(struct xenbus_device *dev)
rtnl_unlock();
}
xennet_free_netdev(info->netdev);
-
- return 0;
}
static const struct xenbus_device_id netfront_ids[] = {
diff --git a/drivers/nfc/pn533/usb.c b/drivers/nfc/pn533/usb.c
index 6f71ac72012ea5..ed9c5e2cf3ad43 100644
--- a/drivers/nfc/pn533/usb.c
+++ b/drivers/nfc/pn533/usb.c
@@ -153,10 +153,17 @@ static int pn533_usb_send_ack(struct pn533 *dev, gfp_t flags)
return usb_submit_urb(phy->ack_urb, flags);
}
+struct pn533_out_arg {
+ struct pn533_usb_phy *phy;
+ struct completion done;
+};
+
static int pn533_usb_send_frame(struct pn533 *dev,
struct sk_buff *out)
{
struct pn533_usb_phy *phy = dev->phy;
+ struct pn533_out_arg arg;
+ void *cntx;
int rc;
if (phy->priv == NULL)
@@ -168,10 +175,17 @@ static int pn533_usb_send_frame(struct pn533 *dev,
print_hex_dump_debug("PN533 TX: ", DUMP_PREFIX_NONE, 16, 1,
out->data, out->len, false);
+ init_completion(&arg.done);
+ cntx = phy->out_urb->context;
+ phy->out_urb->context = &arg;
+
rc = usb_submit_urb(phy->out_urb, GFP_KERNEL);
if (rc)
return rc;
+ wait_for_completion(&arg.done);
+ phy->out_urb->context = cntx;
+
if (dev->protocol_type == PN533_PROTO_REQ_RESP) {
/* request for response for sent packet directly */
rc = pn533_submit_urb_for_response(phy, GFP_KERNEL);
@@ -408,7 +422,31 @@ static int pn533_acr122_poweron_rdr(struct pn533_usb_phy *phy)
return arg.rc;
}
-static void pn533_send_complete(struct urb *urb)
+static void pn533_out_complete(struct urb *urb)
+{
+ struct pn533_out_arg *arg = urb->context;
+ struct pn533_usb_phy *phy = arg->phy;
+
+ switch (urb->status) {
+ case 0:
+ break; /* success */
+ case -ECONNRESET:
+ case -ENOENT:
+ dev_dbg(&phy->udev->dev,
+ "The urb has been stopped (status %d)\n",
+ urb->status);
+ break;
+ case -ESHUTDOWN:
+ default:
+ nfc_err(&phy->udev->dev,
+ "Urb failure (status %d)\n",
+ urb->status);
+ }
+
+ complete(&arg->done);
+}
+
+static void pn533_ack_complete(struct urb *urb)
{
struct pn533_usb_phy *phy = urb->context;
@@ -496,10 +534,10 @@ static int pn533_usb_probe(struct usb_interface *interface,
usb_fill_bulk_urb(phy->out_urb, phy->udev,
usb_sndbulkpipe(phy->udev, out_endpoint),
- NULL, 0, pn533_send_complete, phy);
+ NULL, 0, pn533_out_complete, phy);
usb_fill_bulk_urb(phy->ack_urb, phy->udev,
usb_sndbulkpipe(phy->udev, out_endpoint),
- NULL, 0, pn533_send_complete, phy);
+ NULL, 0, pn533_ack_complete, phy);
switch (id->driver_info) {
case PN533_DEVICE_STD:
diff --git a/drivers/nvdimm/Kconfig b/drivers/nvdimm/Kconfig
index 79d93126453d3f..77b06d54cc62e0 100644
--- a/drivers/nvdimm/Kconfig
+++ b/drivers/nvdimm/Kconfig
@@ -102,6 +102,25 @@ config NVDIMM_KEYS
depends on ENCRYPTED_KEYS
depends on (LIBNVDIMM=ENCRYPTED_KEYS) || LIBNVDIMM=m
+config NVDIMM_KMSAN
+ bool
+ depends on KMSAN
+ help
+ KMSAN, and other memory debug facilities, increase the size of
+ 'struct page' to contain extra metadata. This collides with
+ the NVDIMM capability to store a potentially
+ larger-than-"System RAM" size 'struct page' array in a
+ reservation of persistent memory rather than limited /
+ precious DRAM. However, that reservation needs to persist for
+ the life of the given NVDIMM namespace. If you are using KMSAN
+ to debug an issue unrelated to NVDIMMs or DAX then say N to this
+ option. Otherwise, say Y but understand that any namespaces
+ (with the page array stored pmem) created with this build of
+ the kernel will permanently reserve and strand excess
+ capacity compared to the CONFIG_KMSAN=n case.
+
+ Select N if unsure.
+
config NVDIMM_TEST_BUILD
tristate "Build the unit test core"
depends on m
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h
index 85ca5b4da3cf3b..ec5219680092d7 100644
--- a/drivers/nvdimm/nd.h
+++ b/drivers/nvdimm/nd.h
@@ -652,7 +652,7 @@ void devm_namespace_disable(struct device *dev,
struct nd_namespace_common *ndns);
#if IS_ENABLED(CONFIG_ND_CLAIM)
/* max struct page size independent of kernel config */
-#define MAX_STRUCT_PAGE_SIZE 128
+#define MAX_STRUCT_PAGE_SIZE 64
int nvdimm_setup_pfn(struct nd_pfn *nd_pfn, struct dev_pagemap *pgmap);
#else
static inline int nvdimm_setup_pfn(struct nd_pfn *nd_pfn,
diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c
index 61af072ac98f99..af7d9301520c5b 100644
--- a/drivers/nvdimm/pfn_devs.c
+++ b/drivers/nvdimm/pfn_devs.c
@@ -13,6 +13,8 @@
#include "pfn.h"
#include "nd.h"
+static const bool page_struct_override = IS_ENABLED(CONFIG_NVDIMM_KMSAN);
+
static void nd_pfn_release(struct device *dev)
{
struct nd_region *nd_region = to_nd_region(dev->parent);
@@ -758,12 +760,6 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
return -ENXIO;
}
- /*
- * Note, we use 64 here for the standard size of struct page,
- * debugging options may cause it to be larger in which case the
- * implementation will limit the pfns advertised through
- * ->direct_access() to those that are included in the memmap.
- */
start = nsio->res.start;
size = resource_size(&nsio->res);
npfns = PHYS_PFN(size - SZ_8K);
@@ -782,20 +778,33 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
}
end_trunc = start + size - ALIGN_DOWN(start + size, align);
if (nd_pfn->mode == PFN_MODE_PMEM) {
+ unsigned long page_map_size = MAX_STRUCT_PAGE_SIZE * npfns;
+
/*
* The altmap should be padded out to the block size used
* when populating the vmemmap. This *should* be equal to
* PMD_SIZE for most architectures.
*
- * Also make sure size of struct page is less than 128. We
- * want to make sure we use large enough size here so that
- * we don't have a dynamic reserve space depending on
- * struct page size. But we also want to make sure we notice
- * when we end up adding new elements to struct page.
+ * Also make sure size of struct page is less than
+ * MAX_STRUCT_PAGE_SIZE. The goal here is compatibility in the
+ * face of production kernel configurations that reduce the
+ * 'struct page' size below MAX_STRUCT_PAGE_SIZE. For debug
+ * kernel configurations that increase the 'struct page' size
+ * above MAX_STRUCT_PAGE_SIZE, the page_struct_override allows
+ * for continuing with the capacity that will be wasted when
+ * reverting to a production kernel configuration. Otherwise,
+ * those configurations are blocked by default.
*/
- BUILD_BUG_ON(sizeof(struct page) > MAX_STRUCT_PAGE_SIZE);
- offset = ALIGN(start + SZ_8K + MAX_STRUCT_PAGE_SIZE * npfns, align)
- - start;
+ if (sizeof(struct page) > MAX_STRUCT_PAGE_SIZE) {
+ if (page_struct_override)
+ page_map_size = sizeof(struct page) * npfns;
+ else {
+ dev_err(&nd_pfn->dev,
+ "Memory debug options prevent using pmem for the page map\n");
+ return -EINVAL;
+ }
+ }
+ offset = ALIGN(start + SZ_8K + page_map_size, align) - start;
} else if (nd_pfn->mode == PFN_MODE_RAM)
offset = ALIGN(start + SZ_8K, align) - start;
else
@@ -818,7 +827,10 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn)
pfn_sb->version_minor = cpu_to_le16(4);
pfn_sb->end_trunc = cpu_to_le32(end_trunc);
pfn_sb->align = cpu_to_le32(nd_pfn->align);
- pfn_sb->page_struct_size = cpu_to_le16(MAX_STRUCT_PAGE_SIZE);
+ if (sizeof(struct page) > MAX_STRUCT_PAGE_SIZE && page_struct_override)
+ pfn_sb->page_struct_size = cpu_to_le16(sizeof(struct page));
+ else
+ pfn_sb->page_struct_size = cpu_to_le16(MAX_STRUCT_PAGE_SIZE);
pfn_sb->page_size = cpu_to_le32(PAGE_SIZE);
checksum = nd_sb_checksum((struct nd_gen_sb *) pfn_sb);
pfn_sb->checksum = cpu_to_le64(checksum);
diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c
index e36aeb50b4edc1..b317ce6c4ec318 100644
--- a/drivers/nvme/host/apple.c
+++ b/drivers/nvme/host/apple.c
@@ -829,7 +829,23 @@ static void apple_nvme_disable(struct apple_nvme *anv, bool shutdown)
apple_nvme_remove_cq(anv);
}
- nvme_disable_ctrl(&anv->ctrl, shutdown);
+ /*
+ * Always disable the NVMe controller after shutdown.
+ * We need to do this to bring it back up later anyway, and we
+ * can't do it while the firmware is not running (e.g. in the
+ * resume reset path before RTKit is initialized), so for Apple
+ * controllers it makes sense to unconditionally do it here.
+ * Additionally, this sequence of events is reliable, while
+ * others (like disabling after bringing back the firmware on
+ * resume) seem to run into trouble under some circumstances.
+ *
+ * Both U-Boot and m1n1 also use this convention (i.e. an ANS
+ * NVMe controller is handed off with firmware shut down, in an
+ * NVMe disabled state, after a clean shutdown).
+ */
+ if (shutdown)
+ nvme_disable_ctrl(&anv->ctrl, shutdown);
+ nvme_disable_ctrl(&anv->ctrl, false);
}
WRITE_ONCE(anv->ioq.enabled, false);
@@ -985,11 +1001,11 @@ static void apple_nvme_reset_work(struct work_struct *work)
goto out;
}
- if (anv->ctrl.ctrl_config & NVME_CC_ENABLE)
- apple_nvme_disable(anv, false);
-
/* RTKit must be shut down cleanly for the (soft)-reset to work */
if (apple_rtkit_is_running(anv->rtk)) {
+ /* reset the controller if it is enabled */
+ if (anv->ctrl.ctrl_config & NVME_CC_ENABLE)
+ apple_nvme_disable(anv, false);
dev_dbg(anv->dev, "Trying to shut down RTKit before reset.");
ret = apple_rtkit_shutdown(anv->rtk);
if (ret)
@@ -1493,7 +1509,7 @@ static int apple_nvme_probe(struct platform_device *pdev)
}
ret = nvme_init_ctrl(&anv->ctrl, anv->dev, &nvme_ctrl_ops,
- NVME_QUIRK_SKIP_CID_GEN);
+ NVME_QUIRK_SKIP_CID_GEN | NVME_QUIRK_IDENTIFY_CNS);
if (ret) {
dev_err_probe(dev, ret, "Failed to initialize nvme_ctrl");
goto put_dev;
diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c
index bb0abbe4491cdc..bdb97496ba2d1a 100644
--- a/drivers/nvme/host/auth.c
+++ b/drivers/nvme/host/auth.c
@@ -45,6 +45,8 @@ struct nvme_dhchap_queue_context {
int sess_key_len;
};
+static struct workqueue_struct *nvme_auth_wq;
+
#define nvme_auth_flags_from_qid(qid) \
(qid == 0) ? 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED
#define nvme_auth_queue_from_qid(ctrl, qid) \
@@ -866,7 +868,7 @@ int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid)
chap = &ctrl->dhchap_ctxs[qid];
cancel_work_sync(&chap->auth_work);
- queue_work(nvme_wq, &chap->auth_work);
+ queue_work(nvme_auth_wq, &chap->auth_work);
return 0;
}
EXPORT_SYMBOL_GPL(nvme_auth_negotiate);
@@ -953,7 +955,7 @@ int nvme_auth_init_ctrl(struct nvme_ctrl *ctrl)
goto err_free_dhchap_secret;
if (!ctrl->opts->dhchap_secret && !ctrl->opts->dhchap_ctrl_secret)
- return ret;
+ return 0;
ctrl->dhchap_ctxs = kvcalloc(ctrl_max_dhchaps(ctrl),
sizeof(*chap), GFP_KERNEL);
@@ -1008,10 +1010,15 @@ EXPORT_SYMBOL_GPL(nvme_auth_free);
int __init nvme_init_auth(void)
{
+ nvme_auth_wq = alloc_workqueue("nvme-auth-wq",
+ WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, 0);
+ if (!nvme_auth_wq)
+ return -ENOMEM;
+
nvme_chap_buf_cache = kmem_cache_create("nvme-chap-buf-cache",
CHAP_BUF_SIZE, 0, SLAB_HWCACHE_ALIGN, NULL);
if (!nvme_chap_buf_cache)
- return -ENOMEM;
+ goto err_destroy_workqueue;
nvme_chap_buf_pool = mempool_create(16, mempool_alloc_slab,
mempool_free_slab, nvme_chap_buf_cache);
@@ -1021,6 +1028,8 @@ int __init nvme_init_auth(void)
return 0;
err_destroy_chap_buf_cache:
kmem_cache_destroy(nvme_chap_buf_cache);
+err_destroy_workqueue:
+ destroy_workqueue(nvme_auth_wq);
return -ENOMEM;
}
@@ -1028,4 +1037,5 @@ void __exit nvme_exit_auth(void)
{
mempool_destroy(nvme_chap_buf_pool);
kmem_cache_destroy(nvme_chap_buf_cache);
+ destroy_workqueue(nvme_auth_wq);
}
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 95c488ea91c303..8b64211411626b 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -1074,6 +1074,18 @@ static u32 nvme_known_admin_effects(u8 opcode)
return 0;
}
+static u32 nvme_known_nvm_effects(u8 opcode)
+{
+ switch (opcode) {
+ case nvme_cmd_write:
+ case nvme_cmd_write_zeroes:
+ case nvme_cmd_write_uncor:
+ return NVME_CMD_EFFECTS_LBCC;
+ default:
+ return 0;
+ }
+}
+
u32 nvme_command_effects(struct nvme_ctrl *ctrl, struct nvme_ns *ns, u8 opcode)
{
u32 effects = 0;
@@ -1081,16 +1093,24 @@ u32 nvme_command_effects(struct nvme_ctrl *ctrl, struct nvme_ns *ns, u8 opcode)
if (ns) {
if (ns->head->effects)
effects = le32_to_cpu(ns->head->effects->iocs[opcode]);
+ if (ns->head->ids.csi == NVME_CSI_NVM)
+ effects |= nvme_known_nvm_effects(opcode);
if (effects & ~(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC))
dev_warn_once(ctrl->device,
- "IO command:%02x has unhandled effects:%08x\n",
+ "IO command:%02x has unusual effects:%08x\n",
opcode, effects);
- return 0;
- }
- if (ctrl->effects)
- effects = le32_to_cpu(ctrl->effects->acs[opcode]);
- effects |= nvme_known_admin_effects(opcode);
+ /*
+ * NVME_CMD_EFFECTS_CSE_MASK causes a freeze all I/O queues,
+ * which would deadlock when done on an I/O command. Note that
+ * We already warn about an unusual effect above.
+ */
+ effects &= ~NVME_CMD_EFFECTS_CSE_MASK;
+ } else {
+ if (ctrl->effects)
+ effects = le32_to_cpu(ctrl->effects->acs[opcode]);
+ effects |= nvme_known_admin_effects(opcode);
+ }
return effects;
}
@@ -4901,7 +4921,9 @@ out_cleanup_admin_q:
blk_mq_destroy_queue(ctrl->admin_q);
blk_put_queue(ctrl->admin_q);
out_free_tagset:
- blk_mq_free_tag_set(ctrl->admin_tagset);
+ blk_mq_free_tag_set(set);
+ ctrl->admin_q = NULL;
+ ctrl->fabrics_q = NULL;
return ret;
}
EXPORT_SYMBOL_GPL(nvme_alloc_admin_tag_set);
@@ -4926,7 +4948,7 @@ int nvme_alloc_io_tag_set(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set,
memset(set, 0, sizeof(*set));
set->ops = ops;
- set->queue_depth = ctrl->sqsize + 1;
+ set->queue_depth = min_t(unsigned, ctrl->sqsize, BLK_MQ_MAX_DEPTH - 1);
/*
* Some Apple controllers requires tags to be unique across admin and
* the (only) I/O queue, so reserve the first 32 tags of the I/O queue.
@@ -4963,6 +4985,7 @@ int nvme_alloc_io_tag_set(struct nvme_ctrl *ctrl, struct blk_mq_tag_set *set,
out_free_tag_set:
blk_mq_free_tag_set(set);
+ ctrl->connect_q = NULL;
return ret;
}
EXPORT_SYMBOL_GPL(nvme_alloc_io_tag_set);
diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c
index 4564f16a0b203c..456ee42a613343 100644
--- a/drivers/nvme/host/fc.c
+++ b/drivers/nvme/host/fc.c
@@ -3521,13 +3521,6 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
nvme_fc_init_queue(ctrl, 0);
- ret = nvme_alloc_admin_tag_set(&ctrl->ctrl, &ctrl->admin_tag_set,
- &nvme_fc_admin_mq_ops,
- struct_size((struct nvme_fcp_op_w_sgl *)NULL, priv,
- ctrl->lport->ops->fcprqst_priv_sz));
- if (ret)
- goto out_free_queues;
-
/*
* Would have been nice to init io queues tag set as well.
* However, we require interaction from the controller
@@ -3537,10 +3530,17 @@ nvme_fc_init_ctrl(struct device *dev, struct nvmf_ctrl_options *opts,
ret = nvme_init_ctrl(&ctrl->ctrl, dev, &nvme_fc_ctrl_ops, 0);
if (ret)
- goto out_cleanup_tagset;
+ goto out_free_queues;
/* at this point, teardown path changes to ref counting on nvme ctrl */
+ ret = nvme_alloc_admin_tag_set(&ctrl->ctrl, &ctrl->admin_tag_set,
+ &nvme_fc_admin_mq_ops,
+ struct_size((struct nvme_fcp_op_w_sgl *)NULL, priv,
+ ctrl->lport->ops->fcprqst_priv_sz));
+ if (ret)
+ goto fail_ctrl;
+
spin_lock_irqsave(&rport->lock, flags);
list_add_tail(&ctrl->ctrl_list, &rport->ctrl_list);
spin_unlock_irqrestore(&rport->lock, flags);
@@ -3592,8 +3592,6 @@ fail_ctrl:
return ERR_PTR(-EIO);
-out_cleanup_tagset:
- nvme_remove_admin_tag_set(&ctrl->ctrl);
out_free_queues:
kfree(ctrl->queues);
out_free_ida:
diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c
index 9ddda571f0461f..06f52db34be9bd 100644
--- a/drivers/nvme/host/ioctl.c
+++ b/drivers/nvme/host/ioctl.c
@@ -8,13 +8,27 @@
#include <linux/io_uring.h>
#include "nvme.h"
+enum {
+ NVME_IOCTL_VEC = (1 << 0),
+ NVME_IOCTL_PARTITION = (1 << 1),
+};
+
static bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,
- fmode_t mode)
+ unsigned int flags, fmode_t mode)
{
+ u32 effects;
+
if (capable(CAP_SYS_ADMIN))
return true;
/*
+ * Do not allow unprivileged passthrough on partitions, as that allows an
+ * escape from the containment of the partition.
+ */
+ if (flags & NVME_IOCTL_PARTITION)
+ return false;
+
+ /*
* Do not allow unprivileged processes to send vendor specific or fabrics
* commands as we can't be sure about their effects.
*/
@@ -43,11 +57,29 @@ static bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,
}
/*
- * Only allow I/O commands that transfer data to the controller if the
- * special file is open for writing, but always allow I/O commands that
- * transfer data from the controller.
+ * Check if the controller provides a Commands Supported and Effects log
+ * and marks this command as supported. If not reject unprivileged
+ * passthrough.
+ */
+ effects = nvme_command_effects(ns->ctrl, ns, c->common.opcode);
+ if (!(effects & NVME_CMD_EFFECTS_CSUPP))
+ return false;
+
+ /*
+ * Don't allow passthrough for command that have intrusive (or unknown)
+ * effects.
+ */
+ if (effects & ~(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC |
+ NVME_CMD_EFFECTS_UUID_SEL |
+ NVME_CMD_EFFECTS_SCOPE_MASK))
+ return false;
+
+ /*
+ * Only allow I/O commands that transfer data to the controller or that
+ * change the logical block contents if the file descriptor is open for
+ * writing.
*/
- if (nvme_is_write(c))
+ if (nvme_is_write(c) || (effects & NVME_CMD_EFFECTS_LBCC))
return mode & FMODE_WRITE;
return true;
}
@@ -130,7 +162,7 @@ static struct request *nvme_alloc_user_request(struct request_queue *q,
static int nvme_map_user_request(struct request *req, u64 ubuffer,
unsigned bufflen, void __user *meta_buffer, unsigned meta_len,
u32 meta_seed, void **metap, struct io_uring_cmd *ioucmd,
- bool vec)
+ unsigned int flags)
{
struct request_queue *q = req->q;
struct nvme_ns *ns = q->queuedata;
@@ -143,7 +175,7 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer,
struct iov_iter iter;
/* fixedbufs is only for non-vectored io */
- if (WARN_ON_ONCE(vec))
+ if (WARN_ON_ONCE(flags & NVME_IOCTL_VEC))
return -EINVAL;
ret = io_uring_cmd_import_fixed(ubuffer, bufflen,
rq_data_dir(req), &iter, ioucmd);
@@ -152,8 +184,8 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer,
ret = blk_rq_map_user_iov(q, req, NULL, &iter, GFP_KERNEL);
} else {
ret = blk_rq_map_user_io(req, NULL, nvme_to_user_ptr(ubuffer),
- bufflen, GFP_KERNEL, vec, 0, 0,
- rq_data_dir(req));
+ bufflen, GFP_KERNEL, flags & NVME_IOCTL_VEC, 0,
+ 0, rq_data_dir(req));
}
if (ret)
@@ -183,9 +215,9 @@ out:
}
static int nvme_submit_user_cmd(struct request_queue *q,
- struct nvme_command *cmd, u64 ubuffer,
- unsigned bufflen, void __user *meta_buffer, unsigned meta_len,
- u32 meta_seed, u64 *result, unsigned timeout, bool vec)
+ struct nvme_command *cmd, u64 ubuffer, unsigned bufflen,
+ void __user *meta_buffer, unsigned meta_len, u32 meta_seed,
+ u64 *result, unsigned timeout, unsigned int flags)
{
struct nvme_ctrl *ctrl;
struct request *req;
@@ -201,7 +233,7 @@ static int nvme_submit_user_cmd(struct request_queue *q,
req->timeout = timeout;
if (ubuffer && bufflen) {
ret = nvme_map_user_request(req, ubuffer, bufflen, meta_buffer,
- meta_len, meta_seed, &meta, NULL, vec);
+ meta_len, meta_seed, &meta, NULL, flags);
if (ret)
return ret;
}
@@ -284,10 +316,8 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
c.rw.apptag = cpu_to_le16(io.apptag);
c.rw.appmask = cpu_to_le16(io.appmask);
- return nvme_submit_user_cmd(ns->queue, &c,
- io.addr, length,
- metadata, meta_len, lower_32_bits(io.slba), NULL, 0,
- false);
+ return nvme_submit_user_cmd(ns->queue, &c, io.addr, length, metadata,
+ meta_len, lower_32_bits(io.slba), NULL, 0, 0);
}
static bool nvme_validate_passthru_nsid(struct nvme_ctrl *ctrl,
@@ -305,7 +335,8 @@ static bool nvme_validate_passthru_nsid(struct nvme_ctrl *ctrl,
}
static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
- struct nvme_passthru_cmd __user *ucmd, fmode_t mode)
+ struct nvme_passthru_cmd __user *ucmd, unsigned int flags,
+ fmode_t mode)
{
struct nvme_passthru_cmd cmd;
struct nvme_command c;
@@ -333,16 +364,15 @@ static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
c.common.cdw14 = cpu_to_le32(cmd.cdw14);
c.common.cdw15 = cpu_to_le32(cmd.cdw15);
- if (!nvme_cmd_allowed(ns, &c, mode))
+ if (!nvme_cmd_allowed(ns, &c, 0, mode))
return -EACCES;
if (cmd.timeout_ms)
timeout = msecs_to_jiffies(cmd.timeout_ms);
status = nvme_submit_user_cmd(ns ? ns->queue : ctrl->admin_q, &c,
- cmd.addr, cmd.data_len,
- nvme_to_user_ptr(cmd.metadata), cmd.metadata_len,
- 0, &result, timeout, false);
+ cmd.addr, cmd.data_len, nvme_to_user_ptr(cmd.metadata),
+ cmd.metadata_len, 0, &result, timeout, 0);
if (status >= 0) {
if (put_user(result, &ucmd->result))
@@ -353,8 +383,8 @@ static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
}
static int nvme_user_cmd64(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
- struct nvme_passthru_cmd64 __user *ucmd, bool vec,
- fmode_t mode)
+ struct nvme_passthru_cmd64 __user *ucmd, unsigned int flags,
+ fmode_t mode)
{
struct nvme_passthru_cmd64 cmd;
struct nvme_command c;
@@ -381,16 +411,15 @@ static int nvme_user_cmd64(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
c.common.cdw14 = cpu_to_le32(cmd.cdw14);
c.common.cdw15 = cpu_to_le32(cmd.cdw15);
- if (!nvme_cmd_allowed(ns, &c, mode))
+ if (!nvme_cmd_allowed(ns, &c, flags, mode))
return -EACCES;
if (cmd.timeout_ms)
timeout = msecs_to_jiffies(cmd.timeout_ms);
status = nvme_submit_user_cmd(ns ? ns->queue : ctrl->admin_q, &c,
- cmd.addr, cmd.data_len,
- nvme_to_user_ptr(cmd.metadata), cmd.metadata_len,
- 0, &cmd.result, timeout, vec);
+ cmd.addr, cmd.data_len, nvme_to_user_ptr(cmd.metadata),
+ cmd.metadata_len, 0, &cmd.result, timeout, flags);
if (status >= 0) {
if (put_user(cmd.result, &ucmd->result))
@@ -551,7 +580,7 @@ static int nvme_uring_cmd_io(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
c.common.cdw14 = cpu_to_le32(READ_ONCE(cmd->cdw14));
c.common.cdw15 = cpu_to_le32(READ_ONCE(cmd->cdw15));
- if (!nvme_cmd_allowed(ns, &c, ioucmd->file->f_mode))
+ if (!nvme_cmd_allowed(ns, &c, 0, ioucmd->file->f_mode))
return -EACCES;
d.metadata = READ_ONCE(cmd->metadata);
@@ -621,9 +650,9 @@ static int nvme_ctrl_ioctl(struct nvme_ctrl *ctrl, unsigned int cmd,
{
switch (cmd) {
case NVME_IOCTL_ADMIN_CMD:
- return nvme_user_cmd(ctrl, NULL, argp, mode);
+ return nvme_user_cmd(ctrl, NULL, argp, 0, mode);
case NVME_IOCTL_ADMIN64_CMD:
- return nvme_user_cmd64(ctrl, NULL, argp, false, mode);
+ return nvme_user_cmd64(ctrl, NULL, argp, 0, mode);
default:
return sed_ioctl(ctrl->opal_dev, cmd, argp);
}
@@ -648,14 +677,14 @@ struct nvme_user_io32 {
#endif /* COMPAT_FOR_U64_ALIGNMENT */
static int nvme_ns_ioctl(struct nvme_ns *ns, unsigned int cmd,
- void __user *argp, fmode_t mode)
+ void __user *argp, unsigned int flags, fmode_t mode)
{
switch (cmd) {
case NVME_IOCTL_ID:
force_successful_syscall_return();
return ns->head->ns_id;
case NVME_IOCTL_IO_CMD:
- return nvme_user_cmd(ns->ctrl, ns, argp, mode);
+ return nvme_user_cmd(ns->ctrl, ns, argp, flags, mode);
/*
* struct nvme_user_io can have different padding on some 32-bit ABIs.
* Just accept the compat version as all fields that are used are the
@@ -666,37 +695,40 @@ static int nvme_ns_ioctl(struct nvme_ns *ns, unsigned int cmd,
#endif
case NVME_IOCTL_SUBMIT_IO:
return nvme_submit_io(ns, argp);
- case NVME_IOCTL_IO64_CMD:
- return nvme_user_cmd64(ns->ctrl, ns, argp, false, mode);
case NVME_IOCTL_IO64_CMD_VEC:
- return nvme_user_cmd64(ns->ctrl, ns, argp, true, mode);
+ flags |= NVME_IOCTL_VEC;
+ fallthrough;
+ case NVME_IOCTL_IO64_CMD:
+ return nvme_user_cmd64(ns->ctrl, ns, argp, flags, mode);
default:
return -ENOTTY;
}
}
-static int __nvme_ioctl(struct nvme_ns *ns, unsigned int cmd, void __user *arg,
- fmode_t mode)
-{
- if (is_ctrl_ioctl(cmd))
- return nvme_ctrl_ioctl(ns->ctrl, cmd, arg, mode);
- return nvme_ns_ioctl(ns, cmd, arg, mode);
-}
-
int nvme_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
struct nvme_ns *ns = bdev->bd_disk->private_data;
+ void __user *argp = (void __user *)arg;
+ unsigned int flags = 0;
+
+ if (bdev_is_partition(bdev))
+ flags |= NVME_IOCTL_PARTITION;
- return __nvme_ioctl(ns, cmd, (void __user *)arg, mode);
+ if (is_ctrl_ioctl(cmd))
+ return nvme_ctrl_ioctl(ns->ctrl, cmd, argp, mode);
+ return nvme_ns_ioctl(ns, cmd, argp, flags, mode);
}
long nvme_ns_chr_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct nvme_ns *ns =
container_of(file_inode(file)->i_cdev, struct nvme_ns, cdev);
+ void __user *argp = (void __user *)arg;
- return __nvme_ioctl(ns, cmd, (void __user *)arg, file->f_mode);
+ if (is_ctrl_ioctl(cmd))
+ return nvme_ctrl_ioctl(ns->ctrl, cmd, argp, file->f_mode);
+ return nvme_ns_ioctl(ns, cmd, argp, 0, file->f_mode);
}
static int nvme_uring_cmd_checks(unsigned int issue_flags)
@@ -786,6 +818,10 @@ int nvme_ns_head_ioctl(struct block_device *bdev, fmode_t mode,
void __user *argp = (void __user *)arg;
struct nvme_ns *ns;
int srcu_idx, ret = -EWOULDBLOCK;
+ unsigned int flags = 0;
+
+ if (bdev_is_partition(bdev))
+ flags |= NVME_IOCTL_PARTITION;
srcu_idx = srcu_read_lock(&head->srcu);
ns = nvme_find_path(head);
@@ -801,7 +837,7 @@ int nvme_ns_head_ioctl(struct block_device *bdev, fmode_t mode,
return nvme_ns_head_ctrl_ioctl(ns, cmd, argp, head, srcu_idx,
mode);
- ret = nvme_ns_ioctl(ns, cmd, argp, mode);
+ ret = nvme_ns_ioctl(ns, cmd, argp, flags, mode);
out_unlock:
srcu_read_unlock(&head->srcu, srcu_idx);
return ret;
@@ -826,7 +862,7 @@ long nvme_ns_head_chr_ioctl(struct file *file, unsigned int cmd,
return nvme_ns_head_ctrl_ioctl(ns, cmd, argp, head, srcu_idx,
file->f_mode);
- ret = nvme_ns_ioctl(ns, cmd, argp, file->f_mode);
+ ret = nvme_ns_ioctl(ns, cmd, argp, 0, file->f_mode);
out_unlock:
srcu_read_unlock(&head->srcu, srcu_idx);
return ret;
@@ -925,7 +961,7 @@ static int nvme_dev_user_cmd(struct nvme_ctrl *ctrl, void __user *argp,
kref_get(&ns->kref);
up_read(&ctrl->namespaces_rwsem);
- ret = nvme_user_cmd(ctrl, ns, argp, mode);
+ ret = nvme_user_cmd(ctrl, ns, argp, 0, mode);
nvme_put_ns(ns);
return ret;
@@ -942,9 +978,9 @@ long nvme_dev_ioctl(struct file *file, unsigned int cmd,
switch (cmd) {
case NVME_IOCTL_ADMIN_CMD:
- return nvme_user_cmd(ctrl, NULL, argp, file->f_mode);
+ return nvme_user_cmd(ctrl, NULL, argp, 0, file->f_mode);
case NVME_IOCTL_ADMIN64_CMD:
- return nvme_user_cmd64(ctrl, NULL, argp, false, file->f_mode);
+ return nvme_user_cmd64(ctrl, NULL, argp, 0, file->f_mode);
case NVME_IOCTL_IO_CMD:
return nvme_dev_user_cmd(ctrl, argp, file->f_mode);
case NVME_IOCTL_RESET:
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index c03093b6813c59..fc39d01e7b63be 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -376,6 +376,8 @@ static void nvme_ns_head_submit_bio(struct bio *bio)
* pool from the original queue to allocate the bvecs from.
*/
bio = bio_split_to_limits(bio);
+ if (!bio)
+ return;
srcu_idx = srcu_read_lock(&head->srcu);
ns = nvme_find_path(head);
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 6bbb73ef8b2548..424c8a467a0c2a 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -893,7 +893,7 @@ static inline void nvme_trace_bio_complete(struct request *req)
{
struct nvme_ns *ns = req->q->queuedata;
- if (req->cmd_flags & REQ_NVME_MPATH)
+ if ((req->cmd_flags & REQ_NVME_MPATH) && req->bio)
trace_block_bio_complete(ns->head->disk->queue, req->bio);
}
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index f0f8027644bbf8..c734934c407ccf 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -36,7 +36,7 @@
#define SQ_SIZE(q) ((q)->q_depth << (q)->sqes)
#define CQ_SIZE(q) ((q)->q_depth * sizeof(struct nvme_completion))
-#define SGES_PER_PAGE (PAGE_SIZE / sizeof(struct nvme_sgl_desc))
+#define SGES_PER_PAGE (NVME_CTRL_PAGE_SIZE / sizeof(struct nvme_sgl_desc))
/*
* These can be higher, but we need to ensure that any command doesn't
@@ -144,9 +144,9 @@ struct nvme_dev {
mempool_t *iod_mempool;
/* shadow doorbell buffer support: */
- u32 *dbbuf_dbs;
+ __le32 *dbbuf_dbs;
dma_addr_t dbbuf_dbs_dma_addr;
- u32 *dbbuf_eis;
+ __le32 *dbbuf_eis;
dma_addr_t dbbuf_eis_dma_addr;
/* host memory buffer support: */
@@ -208,10 +208,10 @@ struct nvme_queue {
#define NVMEQ_SQ_CMB 1
#define NVMEQ_DELETE_ERROR 2
#define NVMEQ_POLLED 3
- u32 *dbbuf_sq_db;
- u32 *dbbuf_cq_db;
- u32 *dbbuf_sq_ei;
- u32 *dbbuf_cq_ei;
+ __le32 *dbbuf_sq_db;
+ __le32 *dbbuf_cq_db;
+ __le32 *dbbuf_sq_ei;
+ __le32 *dbbuf_cq_ei;
struct completion delete_done;
};
@@ -343,11 +343,11 @@ static inline int nvme_dbbuf_need_event(u16 event_idx, u16 new_idx, u16 old)
}
/* Update dbbuf and return true if an MMIO is required */
-static bool nvme_dbbuf_update_and_check_event(u16 value, u32 *dbbuf_db,
- volatile u32 *dbbuf_ei)
+static bool nvme_dbbuf_update_and_check_event(u16 value, __le32 *dbbuf_db,
+ volatile __le32 *dbbuf_ei)
{
if (dbbuf_db) {
- u16 old_value;
+ u16 old_value, event_idx;
/*
* Ensure that the queue is written before updating
@@ -355,8 +355,8 @@ static bool nvme_dbbuf_update_and_check_event(u16 value, u32 *dbbuf_db,
*/
wmb();
- old_value = *dbbuf_db;
- *dbbuf_db = value;
+ old_value = le32_to_cpu(*dbbuf_db);
+ *dbbuf_db = cpu_to_le32(value);
/*
* Ensure that the doorbell is updated before reading the event
@@ -366,7 +366,8 @@ static bool nvme_dbbuf_update_and_check_event(u16 value, u32 *dbbuf_db,
*/
mb();
- if (!nvme_dbbuf_need_event(*dbbuf_ei, value, old_value))
+ event_idx = le32_to_cpu(*dbbuf_ei);
+ if (!nvme_dbbuf_need_event(event_idx, value, old_value))
return false;
}
@@ -380,9 +381,9 @@ static bool nvme_dbbuf_update_and_check_event(u16 value, u32 *dbbuf_db,
*/
static int nvme_pci_npages_prp(void)
{
- unsigned nprps = DIV_ROUND_UP(NVME_MAX_KB_SZ + NVME_CTRL_PAGE_SIZE,
- NVME_CTRL_PAGE_SIZE);
- return DIV_ROUND_UP(8 * nprps, PAGE_SIZE - 8);
+ unsigned max_bytes = (NVME_MAX_KB_SZ * 1024) + NVME_CTRL_PAGE_SIZE;
+ unsigned nprps = DIV_ROUND_UP(max_bytes, NVME_CTRL_PAGE_SIZE);
+ return DIV_ROUND_UP(8 * nprps, NVME_CTRL_PAGE_SIZE - 8);
}
/*
@@ -392,7 +393,7 @@ static int nvme_pci_npages_prp(void)
static int nvme_pci_npages_sgl(void)
{
return DIV_ROUND_UP(NVME_MAX_SEGS * sizeof(struct nvme_sgl_desc),
- PAGE_SIZE);
+ NVME_CTRL_PAGE_SIZE);
}
static int nvme_admin_init_hctx(struct blk_mq_hw_ctx *hctx, void *data,
@@ -708,7 +709,7 @@ static void nvme_pci_sgl_set_seg(struct nvme_sgl_desc *sge,
sge->length = cpu_to_le32(entries * sizeof(*sge));
sge->type = NVME_SGL_FMT_LAST_SEG_DESC << 4;
} else {
- sge->length = cpu_to_le32(PAGE_SIZE);
+ sge->length = cpu_to_le32(NVME_CTRL_PAGE_SIZE);
sge->type = NVME_SGL_FMT_SEG_DESC << 4;
}
}
@@ -1361,7 +1362,7 @@ static enum blk_eh_timer_return nvme_timeout(struct request *req)
else
nvme_poll_irqdisable(nvmeq);
- if (blk_mq_request_completed(req)) {
+ if (blk_mq_rq_state(req) != MQ_RQ_IN_FLIGHT) {
dev_warn(dev->ctrl.device,
"I/O %d QID %d timeout, completion polled\n",
req->tag, nvmeq->qid);
@@ -2332,10 +2333,12 @@ static int nvme_setup_io_queues(struct nvme_dev *dev)
if (dev->cmb_use_sqes) {
result = nvme_cmb_qdepth(dev, nr_io_queues,
sizeof(struct nvme_command));
- if (result > 0)
+ if (result > 0) {
dev->q_depth = result;
- else
+ dev->ctrl.sqsize = result - 1;
+ } else {
dev->cmb_use_sqes = false;
+ }
}
do {
@@ -2530,13 +2533,12 @@ static int nvme_pci_enable(struct nvme_dev *dev)
*/
result = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
if (result < 0)
- return result;
+ goto disable;
dev->ctrl.cap = lo_hi_readq(dev->bar + NVME_REG_CAP);
dev->q_depth = min_t(u32, NVME_CAP_MQES(dev->ctrl.cap) + 1,
io_queue_depth);
- dev->ctrl.sqsize = dev->q_depth - 1; /* 0's based queue depth */
dev->db_stride = 1 << NVME_CAP_STRIDE(dev->ctrl.cap);
dev->dbs = dev->bar + 4096;
@@ -2577,15 +2579,20 @@ static int nvme_pci_enable(struct nvme_dev *dev)
dev_warn(dev->ctrl.device, "IO queue depth clamped to %d\n",
dev->q_depth);
}
-
+ dev->ctrl.sqsize = dev->q_depth - 1; /* 0's based queue depth */
nvme_map_cmb(dev);
pci_enable_pcie_error_reporting(pdev);
pci_save_state(pdev);
- return nvme_pci_configure_admin_queue(dev);
+ result = nvme_pci_configure_admin_queue(dev);
+ if (result)
+ goto free_irq;
+ return result;
+ free_irq:
+ pci_free_irq_vectors(pdev);
disable:
pci_disable_device(pdev);
return result;
@@ -3095,6 +3102,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
nvme_start_ctrl(&dev->ctrl);
nvme_put_ctrl(&dev->ctrl);
+ flush_work(&dev->ctrl.scan_work);
return 0;
out_disable:
@@ -3493,7 +3501,8 @@ static const struct pci_device_id nvme_id_table[] = {
.driver_data = NVME_QUIRK_SINGLE_VECTOR |
NVME_QUIRK_128_BYTES_SQES |
NVME_QUIRK_SHARED_TAGS |
- NVME_QUIRK_SKIP_CID_GEN },
+ NVME_QUIRK_SKIP_CID_GEN |
+ NVME_QUIRK_IDENTIFY_CNS },
{ PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) },
{ 0, }
};
diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c
index 53a004ea320c1f..6a54ed6fb12144 100644
--- a/drivers/nvme/target/admin-cmd.c
+++ b/drivers/nvme/target/admin-cmd.c
@@ -164,26 +164,31 @@ out:
static void nvmet_get_cmd_effects_nvm(struct nvme_effects_log *log)
{
- log->acs[nvme_admin_get_log_page] = cpu_to_le32(1 << 0);
- log->acs[nvme_admin_identify] = cpu_to_le32(1 << 0);
- log->acs[nvme_admin_abort_cmd] = cpu_to_le32(1 << 0);
- log->acs[nvme_admin_set_features] = cpu_to_le32(1 << 0);
- log->acs[nvme_admin_get_features] = cpu_to_le32(1 << 0);
- log->acs[nvme_admin_async_event] = cpu_to_le32(1 << 0);
- log->acs[nvme_admin_keep_alive] = cpu_to_le32(1 << 0);
-
- log->iocs[nvme_cmd_read] = cpu_to_le32(1 << 0);
- log->iocs[nvme_cmd_write] = cpu_to_le32(1 << 0);
- log->iocs[nvme_cmd_flush] = cpu_to_le32(1 << 0);
- log->iocs[nvme_cmd_dsm] = cpu_to_le32(1 << 0);
- log->iocs[nvme_cmd_write_zeroes] = cpu_to_le32(1 << 0);
+ log->acs[nvme_admin_get_log_page] =
+ log->acs[nvme_admin_identify] =
+ log->acs[nvme_admin_abort_cmd] =
+ log->acs[nvme_admin_set_features] =
+ log->acs[nvme_admin_get_features] =
+ log->acs[nvme_admin_async_event] =
+ log->acs[nvme_admin_keep_alive] =
+ cpu_to_le32(NVME_CMD_EFFECTS_CSUPP);
+
+ log->iocs[nvme_cmd_read] =
+ log->iocs[nvme_cmd_flush] =
+ log->iocs[nvme_cmd_dsm] =
+ cpu_to_le32(NVME_CMD_EFFECTS_CSUPP);
+ log->iocs[nvme_cmd_write] =
+ log->iocs[nvme_cmd_write_zeroes] =
+ cpu_to_le32(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC);
}
static void nvmet_get_cmd_effects_zns(struct nvme_effects_log *log)
{
- log->iocs[nvme_cmd_zone_append] = cpu_to_le32(1 << 0);
- log->iocs[nvme_cmd_zone_mgmt_send] = cpu_to_le32(1 << 0);
- log->iocs[nvme_cmd_zone_mgmt_recv] = cpu_to_le32(1 << 0);
+ log->iocs[nvme_cmd_zone_append] =
+ log->iocs[nvme_cmd_zone_mgmt_send] =
+ cpu_to_le32(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC);
+ log->iocs[nvme_cmd_zone_mgmt_recv] =
+ cpu_to_le32(NVME_CMD_EFFECTS_CSUPP);
}
static void nvmet_execute_get_log_cmd_effects_ns(struct nvmet_req *req)
diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c
index ab2627e17bb97f..1ab6601fdd5cf9 100644
--- a/drivers/nvme/target/fc.c
+++ b/drivers/nvme/target/fc.c
@@ -1685,8 +1685,10 @@ nvmet_fc_ls_create_association(struct nvmet_fc_tgtport *tgtport,
else {
queue = nvmet_fc_alloc_target_queue(iod->assoc, 0,
be16_to_cpu(rqst->assoc_cmd.sqsize));
- if (!queue)
+ if (!queue) {
ret = VERR_QUEUE_ALLOC_FAIL;
+ nvmet_fc_tgt_a_put(iod->assoc);
+ }
}
}
diff --git a/drivers/nvme/target/passthru.c b/drivers/nvme/target/passthru.c
index 79af5140af8bfe..adc0958755d66f 100644
--- a/drivers/nvme/target/passthru.c
+++ b/drivers/nvme/target/passthru.c
@@ -334,14 +334,13 @@ static void nvmet_passthru_execute_cmd(struct nvmet_req *req)
}
/*
- * If there are effects for the command we are about to execute, or
- * an end_req function we need to use nvme_execute_passthru_rq()
- * synchronously in a work item seeing the end_req function and
- * nvme_passthru_end() can't be called in the request done callback
- * which is typically in interrupt context.
+ * If a command needs post-execution fixups, or there are any
+ * non-trivial effects, make sure to execute the command synchronously
+ * in a workqueue so that nvme_passthru_end gets called.
*/
effects = nvme_command_effects(ctrl, ns, req->cmd->common.opcode);
- if (req->p.use_workqueue || effects) {
+ if (req->p.use_workqueue ||
+ (effects & ~(NVME_CMD_EFFECTS_CSUPP | NVME_CMD_EFFECTS_LBCC))) {
INIT_WORK(&req->p.work, nvmet_passthru_execute_cmd_work);
req->p.rq = rq;
queue_work(nvmet_wq, &req->p.work);
diff --git a/drivers/nvmem/brcm_nvram.c b/drivers/nvmem/brcm_nvram.c
index 34130449f2d28c..39aa27942f28b3 100644
--- a/drivers/nvmem/brcm_nvram.c
+++ b/drivers/nvmem/brcm_nvram.c
@@ -98,6 +98,9 @@ static int brcm_nvram_parse(struct brcm_nvram *priv)
len = le32_to_cpu(header.len);
data = kzalloc(len, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
memcpy_fromio(data, priv->base, len);
data[len - 1] = '\0';
diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
index 321d7d63e0683c..34ee9d36ee7ba2 100644
--- a/drivers/nvmem/core.c
+++ b/drivers/nvmem/core.c
@@ -770,31 +770,32 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
return ERR_PTR(rval);
}
- if (config->wp_gpio)
- nvmem->wp_gpio = config->wp_gpio;
- else if (!config->ignore_wp)
+ nvmem->id = rval;
+
+ nvmem->dev.type = &nvmem_provider_type;
+ nvmem->dev.bus = &nvmem_bus_type;
+ nvmem->dev.parent = config->dev;
+
+ device_initialize(&nvmem->dev);
+
+ if (!config->ignore_wp)
nvmem->wp_gpio = gpiod_get_optional(config->dev, "wp",
GPIOD_OUT_HIGH);
if (IS_ERR(nvmem->wp_gpio)) {
- ida_free(&nvmem_ida, nvmem->id);
rval = PTR_ERR(nvmem->wp_gpio);
- kfree(nvmem);
- return ERR_PTR(rval);
+ nvmem->wp_gpio = NULL;
+ goto err_put_device;
}
kref_init(&nvmem->refcnt);
INIT_LIST_HEAD(&nvmem->cells);
- nvmem->id = rval;
nvmem->owner = config->owner;
if (!nvmem->owner && config->dev->driver)
nvmem->owner = config->dev->driver->owner;
nvmem->stride = config->stride ?: 1;
nvmem->word_size = config->word_size ?: 1;
nvmem->size = config->size;
- nvmem->dev.type = &nvmem_provider_type;
- nvmem->dev.bus = &nvmem_bus_type;
- nvmem->dev.parent = config->dev;
nvmem->root_only = config->root_only;
nvmem->priv = config->priv;
nvmem->type = config->type;
@@ -822,11 +823,8 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
break;
}
- if (rval) {
- ida_free(&nvmem_ida, nvmem->id);
- kfree(nvmem);
- return ERR_PTR(rval);
- }
+ if (rval)
+ goto err_put_device;
nvmem->read_only = device_property_present(config->dev, "read-only") ||
config->read_only || !nvmem->reg_write;
@@ -835,28 +833,22 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
nvmem->dev.groups = nvmem_dev_groups;
#endif
- dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
-
- rval = device_register(&nvmem->dev);
- if (rval)
- goto err_put_device;
-
if (nvmem->nkeepout) {
rval = nvmem_validate_keepouts(nvmem);
if (rval)
- goto err_device_del;
+ goto err_put_device;
}
if (config->compat) {
rval = nvmem_sysfs_setup_compat(nvmem, config);
if (rval)
- goto err_device_del;
+ goto err_put_device;
}
if (config->cells) {
rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
if (rval)
- goto err_teardown_compat;
+ goto err_remove_cells;
}
rval = nvmem_add_cells_from_table(nvmem);
@@ -867,17 +859,20 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
if (rval)
goto err_remove_cells;
+ dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
+
+ rval = device_add(&nvmem->dev);
+ if (rval)
+ goto err_remove_cells;
+
blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
return nvmem;
err_remove_cells:
nvmem_device_remove_all_cells(nvmem);
-err_teardown_compat:
if (config->compat)
nvmem_sysfs_remove_compat(nvmem, config);
-err_device_del:
- device_del(&nvmem->dev);
err_put_device:
put_device(&nvmem->dev);
@@ -1242,16 +1237,21 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
if (!cell_np)
return ERR_PTR(-ENOENT);
- nvmem_np = of_get_next_parent(cell_np);
- if (!nvmem_np)
+ nvmem_np = of_get_parent(cell_np);
+ if (!nvmem_np) {
+ of_node_put(cell_np);
return ERR_PTR(-EINVAL);
+ }
nvmem = __nvmem_device_get(nvmem_np, device_match_of_node);
of_node_put(nvmem_np);
- if (IS_ERR(nvmem))
+ if (IS_ERR(nvmem)) {
+ of_node_put(cell_np);
return ERR_CAST(nvmem);
+ }
cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np);
+ of_node_put(cell_np);
if (!cell_entry) {
__nvmem_device_put(nvmem);
return ERR_PTR(-ENOENT);
diff --git a/drivers/nvmem/qcom-spmi-sdam.c b/drivers/nvmem/qcom-spmi-sdam.c
index 4fcb63507ecd15..8499892044b7b2 100644
--- a/drivers/nvmem/qcom-spmi-sdam.c
+++ b/drivers/nvmem/qcom-spmi-sdam.c
@@ -166,6 +166,7 @@ static const struct of_device_id sdam_match_table[] = {
{ .compatible = "qcom,spmi-sdam" },
{},
};
+MODULE_DEVICE_TABLE(of, sdam_match_table);
static struct platform_driver sdam_driver = {
.driver = {
diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c
index 5750e1f4bcdbbf..92dfe4cb10e386 100644
--- a/drivers/nvmem/sunxi_sid.c
+++ b/drivers/nvmem/sunxi_sid.c
@@ -41,8 +41,21 @@ static int sunxi_sid_read(void *context, unsigned int offset,
void *val, size_t bytes)
{
struct sunxi_sid *sid = context;
+ u32 word;
+
+ /* .stride = 4 so offset is guaranteed to be aligned */
+ __ioread32_copy(val, sid->base + sid->value_offset + offset, bytes / 4);
- memcpy_fromio(val, sid->base + sid->value_offset + offset, bytes);
+ val += round_down(bytes, 4);
+ offset += round_down(bytes, 4);
+ bytes = bytes % 4;
+
+ if (!bytes)
+ return 0;
+
+ /* Handle any trailing bytes */
+ word = readl_relaxed(sid->base + sid->value_offset + offset);
+ memcpy(val, &word, bytes);
return 0;
}
diff --git a/drivers/of/address.c b/drivers/of/address.c
index c34ac33b7338a9..67763e5b8c0efd 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -965,8 +965,19 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map)
}
of_dma_range_parser_init(&parser, node);
- for_each_of_range(&parser, &range)
+ for_each_of_range(&parser, &range) {
+ if (range.cpu_addr == OF_BAD_ADDR) {
+ pr_err("translation of DMA address(%llx) to CPU address failed node(%pOF)\n",
+ range.bus_addr, node);
+ continue;
+ }
num_ranges++;
+ }
+
+ if (!num_ranges) {
+ ret = -EINVAL;
+ goto out;
+ }
r = kcalloc(num_ranges + 1, sizeof(*r), GFP_KERNEL);
if (!r) {
@@ -975,18 +986,16 @@ int of_dma_get_range(struct device_node *np, const struct bus_dma_region **map)
}
/*
- * Record all info in the generic DMA ranges array for struct device.
+ * Record all info in the generic DMA ranges array for struct device,
+ * returning an error if we don't find any parsable ranges.
*/
*map = r;
of_dma_range_parser_init(&parser, node);
for_each_of_range(&parser, &range) {
pr_debug("dma_addr(%llx) cpu_addr(%llx) size(%llx)\n",
range.bus_addr, range.cpu_addr, range.size);
- if (range.cpu_addr == OF_BAD_ADDR) {
- pr_err("translation of DMA address(%llx) to CPU address failed node(%pOF)\n",
- range.bus_addr, node);
+ if (range.cpu_addr == OF_BAD_ADDR)
continue;
- }
r->cpu_start = range.cpu_addr;
r->dma_start = range.bus_addr;
r->size = range.size;
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index b2272bccf85c95..d1a68b6d03b3f1 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -26,7 +26,6 @@
#include <linux/serial_core.h>
#include <linux/sysfs.h>
#include <linux/random.h>
-#include <linux/kmemleak.h>
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */
#include <asm/page.h>
@@ -525,12 +524,9 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
size = dt_mem_next_cell(dt_root_size_cells, &prop);
if (size &&
- early_init_dt_reserve_memory(base, size, nomap) == 0) {
+ early_init_dt_reserve_memory(base, size, nomap) == 0)
pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %lu MiB\n",
uname, &base, (unsigned long)(size / SZ_1M));
- if (!nomap)
- kmemleak_alloc_phys(base, size, 0);
- }
else
pr_err("Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB\n",
uname, &base, (unsigned long)(size / SZ_1M));
@@ -1099,7 +1095,7 @@ u64 __init dt_mem_next_cell(int s, const __be32 **cellp)
*/
int __init early_init_dt_scan_memory(void)
{
- int node;
+ int node, found_memory = 0;
const void *fdt = initial_boot_params;
fdt_for_each_subnode(node, fdt, 0) {
@@ -1139,6 +1135,8 @@ int __init early_init_dt_scan_memory(void)
early_init_dt_add_memory_arch(base, size);
+ found_memory = 1;
+
if (!hotpluggable)
continue;
@@ -1147,7 +1145,7 @@ int __init early_init_dt_scan_memory(void)
base, base + size);
}
}
- return 0;
+ return found_memory;
}
int __init early_init_dt_scan_chosen(char *cmdline)
@@ -1161,18 +1159,14 @@ int __init early_init_dt_scan_chosen(char *cmdline)
if (node < 0)
node = fdt_path_offset(fdt, "/chosen@0");
if (node < 0)
- return -ENOENT;
+ /* Handle the cmdline config options even if no /chosen node */
+ goto handle_cmdline;
chosen_node_offset = node;
early_init_dt_check_for_initrd(node);
early_init_dt_check_for_elfcorehdr(node);
- /* Retrieve command line */
- p = of_get_flat_dt_prop(node, "bootargs", &l);
- if (p != NULL && l > 0)
- strscpy(cmdline, p, min(l, COMMAND_LINE_SIZE));
-
rng_seed = of_get_flat_dt_prop(node, "rng-seed", &l);
if (rng_seed && l > 0) {
add_bootloader_randomness(rng_seed, l);
@@ -1185,6 +1179,32 @@ int __init early_init_dt_scan_chosen(char *cmdline)
fdt_totalsize(initial_boot_params));
}
+ /* Retrieve command line */
+ p = of_get_flat_dt_prop(node, "bootargs", &l);
+ if (p != NULL && l > 0)
+ strscpy(cmdline, p, min(l, COMMAND_LINE_SIZE));
+
+handle_cmdline:
+ /*
+ * CONFIG_CMDLINE is meant to be a default in case nothing else
+ * managed to set the command line, unless CONFIG_CMDLINE_FORCE
+ * is set in which case we override whatever was found earlier.
+ */
+#ifdef CONFIG_CMDLINE
+#if defined(CONFIG_CMDLINE_EXTEND)
+ strlcat(cmdline, " ", COMMAND_LINE_SIZE);
+ strlcat(cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
+#elif defined(CONFIG_CMDLINE_FORCE)
+ strscpy(cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
+#else
+ /* No arguments from boot loader, use kernel's cmdl*/
+ if (!((char *)cmdline)[0])
+ strscpy(cmdline, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
+#endif
+#endif /* CONFIG_CMDLINE */
+
+ pr_debug("Command line is: %s\n", (char *)cmdline);
+
return 0;
}
@@ -1277,26 +1297,6 @@ void __init early_init_dt_scan_nodes(void)
if (rc)
pr_warn("No chosen node found, continuing without\n");
- /*
- * CONFIG_CMDLINE is meant to be a default in case nothing else
- * managed to set the command line, unless CONFIG_CMDLINE_FORCE
- * is set in which case we override whatever was found earlier.
- */
-#ifdef CONFIG_CMDLINE
-#if defined(CONFIG_CMDLINE_EXTEND)
- strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
- strlcat(boot_command_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
-#elif defined(CONFIG_CMDLINE_FORCE)
- strscpy(boot_command_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
-#else
- /* No arguments from boot loader, use kernel's cmdl */
- if (!boot_command_line[0])
- strscpy(boot_command_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
-#endif
-#endif /* CONFIG_CMDLINE */
-
- pr_debug("Command line is: %s\n", boot_command_line);
-
/* Setup memory, calling early_init_dt_add_memory_arch */
early_init_dt_scan_memory();
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 81c8c227ab6bf2..b3878a98d27f75 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -525,6 +525,7 @@ static int __init of_platform_default_populate_init(void)
if (IS_ENABLED(CONFIG_PPC)) {
struct device_node *boot_display = NULL;
struct platform_device *dev;
+ int display_number = 0;
int ret;
/* Check if we have a MacOS display without a node spec */
@@ -555,16 +556,23 @@ static int __init of_platform_default_populate_init(void)
if (!of_get_property(node, "linux,opened", NULL) ||
!of_get_property(node, "linux,boot-display", NULL))
continue;
- dev = of_platform_device_create(node, "of-display", NULL);
+ dev = of_platform_device_create(node, "of-display.0", NULL);
+ of_node_put(node);
if (WARN_ON(!dev))
return -ENOMEM;
boot_display = node;
+ display_number++;
break;
}
for_each_node_by_type(node, "display") {
+ char buf[14];
+ const char *of_display_format = "of-display.%d";
+
if (!of_get_property(node, "linux,opened", NULL) || node == boot_display)
continue;
- of_platform_device_create(node, "of-display", NULL);
+ ret = snprintf(buf, sizeof(buf), of_display_format, display_number++);
+ if (ret < sizeof(buf))
+ of_platform_device_create(node, buf, NULL);
}
} else {
diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c
index d6af5726ddf354..2a18f7ba2398bc 100644
--- a/drivers/parisc/pdc_stable.c
+++ b/drivers/parisc/pdc_stable.c
@@ -274,8 +274,7 @@ pdcspath_hwpath_write(struct pdcspath_entry *entry, const char *buf, size_t coun
/* We'll use a local copy of buf */
count = min_t(size_t, count, sizeof(in)-1);
- strncpy(in, buf, count);
- in[count] = '\0';
+ strscpy(in, buf, count + 1);
/* Let's clean up the target. 0xff is a blank pattern */
memset(&hwpath, 0xff, sizeof(hwpath));
@@ -388,8 +387,7 @@ pdcspath_layer_write(struct pdcspath_entry *entry, const char *buf, size_t count
/* We'll use a local copy of buf */
count = min_t(size_t, count, sizeof(in)-1);
- strncpy(in, buf, count);
- in[count] = '\0';
+ strscpy(in, buf, count + 1);
/* Let's clean up the target. 0 is a blank pattern */
memset(&layers, 0, sizeof(layers));
@@ -756,8 +754,7 @@ static ssize_t pdcs_auto_write(struct kobject *kobj,
/* We'll use a local copy of buf */
count = min_t(size_t, count, sizeof(in)-1);
- strncpy(in, buf, count);
- in[count] = '\0';
+ strscpy(in, buf, count + 1);
/* Current flags are stored in primary boot path entry */
pathentry = &pdcspath_entry_primary;
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
index a0d2713f0e8896..99ec91e2a5cfa3 100644
--- a/drivers/pci/controller/dwc/Kconfig
+++ b/drivers/pci/controller/dwc/Kconfig
@@ -225,7 +225,7 @@ config PCIE_ARTPEC6_EP
config PCIE_BT1
tristate "Baikal-T1 PCIe controller"
depends on MIPS_BAIKAL_T1 || COMPILE_TEST
- depends on PCI_MSI_IRQ_DOMAIN
+ depends on PCI_MSI
select PCIE_DW_HOST
help
Enables support for the PCIe controller in the Baikal-T1 SoC to work
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index fba95486caaf2f..5641786bd02066 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1665,7 +1665,6 @@ int pci_save_state(struct pci_dev *dev)
return i;
pci_save_ltr_state(dev);
- pci_save_aspm_l1ss_state(dev);
pci_save_dpc_state(dev);
pci_save_aer_state(dev);
pci_save_ptm_state(dev);
@@ -1772,7 +1771,6 @@ void pci_restore_state(struct pci_dev *dev)
* LTR itself (in the PCIe capability).
*/
pci_restore_ltr_state(dev);
- pci_restore_aspm_l1ss_state(dev);
pci_restore_pcie_state(dev);
pci_restore_pasid_state(dev);
@@ -3465,11 +3463,6 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev)
if (error)
pci_err(dev, "unable to allocate suspend buffer for LTR\n");
- error = pci_add_ext_cap_save_buffer(dev, PCI_EXT_CAP_ID_L1SS,
- 2 * sizeof(u32));
- if (error)
- pci_err(dev, "unable to allocate suspend buffer for ASPM-L1SS\n");
-
pci_allocate_vc_save_buffers(dev);
}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 9ed3b555004326..9049d07d3aaece 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -566,14 +566,10 @@ bool pcie_wait_for_link(struct pci_dev *pdev, bool active);
void pcie_aspm_init_link_state(struct pci_dev *pdev);
void pcie_aspm_exit_link_state(struct pci_dev *pdev);
void pcie_aspm_powersave_config_link(struct pci_dev *pdev);
-void pci_save_aspm_l1ss_state(struct pci_dev *dev);
-void pci_restore_aspm_l1ss_state(struct pci_dev *dev);
#else
static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) { }
static inline void pcie_aspm_exit_link_state(struct pci_dev *pdev) { }
static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev) { }
-static inline void pci_save_aspm_l1ss_state(struct pci_dev *dev) { }
-static inline void pci_restore_aspm_l1ss_state(struct pci_dev *dev) { }
#endif
#ifdef CONFIG_PCIE_ECRC
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 53a1fa306e1ee7..4b4184563a9272 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -470,31 +470,6 @@ static void pci_clear_and_set_dword(struct pci_dev *pdev, int pos,
pci_write_config_dword(pdev, pos, val);
}
-static void aspm_program_l1ss(struct pci_dev *dev, u32 ctl1, u32 ctl2)
-{
- u16 l1ss = dev->l1ss;
- u32 l1_2_enable;
-
- /*
- * Per PCIe r6.0, sec 5.5.4, T_POWER_ON in PCI_L1SS_CTL2 must be
- * programmed prior to setting the L1.2 enable bits in PCI_L1SS_CTL1.
- */
- pci_write_config_dword(dev, l1ss + PCI_L1SS_CTL2, ctl2);
-
- /*
- * In addition, Common_Mode_Restore_Time and LTR_L1.2_THRESHOLD in
- * PCI_L1SS_CTL1 must be programmed *before* setting the L1.2
- * enable bits, even though they're all in PCI_L1SS_CTL1.
- */
- l1_2_enable = ctl1 & PCI_L1SS_CTL1_L1_2_MASK;
- ctl1 &= ~PCI_L1SS_CTL1_L1_2_MASK;
-
- pci_write_config_dword(dev, l1ss + PCI_L1SS_CTL1, ctl1);
- if (l1_2_enable)
- pci_write_config_dword(dev, l1ss + PCI_L1SS_CTL1,
- ctl1 | l1_2_enable);
-}
-
/* Calculate L1.2 PM substate timing parameters */
static void aspm_calc_l1ss_info(struct pcie_link_state *link,
u32 parent_l1ss_cap, u32 child_l1ss_cap)
@@ -504,6 +479,7 @@ static void aspm_calc_l1ss_info(struct pcie_link_state *link,
u32 t_common_mode, t_power_on, l1_2_threshold, scale, value;
u32 ctl1 = 0, ctl2 = 0;
u32 pctl1, pctl2, cctl1, cctl2;
+ u32 pl1_2_enables, cl1_2_enables;
if (!(link->aspm_support & ASPM_STATE_L1_2_MASK))
return;
@@ -552,21 +528,39 @@ static void aspm_calc_l1ss_info(struct pcie_link_state *link,
ctl2 == pctl2 && ctl2 == cctl2)
return;
- pctl1 &= ~(PCI_L1SS_CTL1_CM_RESTORE_TIME |
- PCI_L1SS_CTL1_LTR_L12_TH_VALUE |
- PCI_L1SS_CTL1_LTR_L12_TH_SCALE);
- pctl1 |= (ctl1 & (PCI_L1SS_CTL1_CM_RESTORE_TIME |
- PCI_L1SS_CTL1_LTR_L12_TH_VALUE |
- PCI_L1SS_CTL1_LTR_L12_TH_SCALE));
- aspm_program_l1ss(parent, pctl1, ctl2);
-
- cctl1 &= ~(PCI_L1SS_CTL1_CM_RESTORE_TIME |
- PCI_L1SS_CTL1_LTR_L12_TH_VALUE |
- PCI_L1SS_CTL1_LTR_L12_TH_SCALE);
- cctl1 |= (ctl1 & (PCI_L1SS_CTL1_CM_RESTORE_TIME |
- PCI_L1SS_CTL1_LTR_L12_TH_VALUE |
- PCI_L1SS_CTL1_LTR_L12_TH_SCALE));
- aspm_program_l1ss(child, cctl1, ctl2);
+ /* Disable L1.2 while updating. See PCIe r5.0, sec 5.5.4, 7.8.3.3 */
+ pl1_2_enables = pctl1 & PCI_L1SS_CTL1_L1_2_MASK;
+ cl1_2_enables = cctl1 & PCI_L1SS_CTL1_L1_2_MASK;
+
+ if (pl1_2_enables || cl1_2_enables) {
+ pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_L1_2_MASK, 0);
+ pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_L1_2_MASK, 0);
+ }
+
+ /* Program T_POWER_ON times in both ports */
+ pci_write_config_dword(parent, parent->l1ss + PCI_L1SS_CTL2, ctl2);
+ pci_write_config_dword(child, child->l1ss + PCI_L1SS_CTL2, ctl2);
+
+ /* Program Common_Mode_Restore_Time in upstream device */
+ pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_CM_RESTORE_TIME, ctl1);
+
+ /* Program LTR_L1.2_THRESHOLD time in both ports */
+ pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_LTR_L12_TH_VALUE |
+ PCI_L1SS_CTL1_LTR_L12_TH_SCALE, ctl1);
+ pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1,
+ PCI_L1SS_CTL1_LTR_L12_TH_VALUE |
+ PCI_L1SS_CTL1_LTR_L12_TH_SCALE, ctl1);
+
+ if (pl1_2_enables || cl1_2_enables) {
+ pci_clear_and_set_dword(parent, parent->l1ss + PCI_L1SS_CTL1, 0,
+ pl1_2_enables);
+ pci_clear_and_set_dword(child, child->l1ss + PCI_L1SS_CTL1, 0,
+ cl1_2_enables);
+ }
}
static void aspm_l1ss_init(struct pcie_link_state *link)
@@ -757,43 +751,6 @@ static void pcie_config_aspm_l1ss(struct pcie_link_state *link, u32 state)
PCI_L1SS_CTL1_L1SS_MASK, val);
}
-void pci_save_aspm_l1ss_state(struct pci_dev *dev)
-{
- struct pci_cap_saved_state *save_state;
- u16 l1ss = dev->l1ss;
- u32 *cap;
-
- if (!l1ss)
- return;
-
- save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_L1SS);
- if (!save_state)
- return;
-
- cap = (u32 *)&save_state->cap.data[0];
- pci_read_config_dword(dev, l1ss + PCI_L1SS_CTL2, cap++);
- pci_read_config_dword(dev, l1ss + PCI_L1SS_CTL1, cap++);
-}
-
-void pci_restore_aspm_l1ss_state(struct pci_dev *dev)
-{
- struct pci_cap_saved_state *save_state;
- u32 *cap, ctl1, ctl2;
- u16 l1ss = dev->l1ss;
-
- if (!l1ss)
- return;
-
- save_state = pci_find_saved_ext_cap(dev, PCI_EXT_CAP_ID_L1SS);
- if (!save_state)
- return;
-
- cap = (u32 *)&save_state->cap.data[0];
- ctl2 = *cap++;
- ctl1 = *cap;
- aspm_program_l1ss(dev, ctl1, ctl2);
-}
-
static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
{
pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL,
diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
index 7378e2f3e525fd..fcd029ca2eb184 100644
--- a/drivers/pci/xen-pcifront.c
+++ b/drivers/pci/xen-pcifront.c
@@ -1055,14 +1055,12 @@ out:
return err;
}
-static int pcifront_xenbus_remove(struct xenbus_device *xdev)
+static void pcifront_xenbus_remove(struct xenbus_device *xdev)
{
struct pcifront_device *pdev = dev_get_drvdata(&xdev->dev);
if (pdev)
free_pdev(pdev);
-
- return 0;
}
static const struct xenbus_device_id xenpci_ids[] = {
diff --git a/drivers/perf/arm-cmn.c b/drivers/perf/arm-cmn.c
index b80a9b74662b1d..1deb61b22bc767 100644
--- a/drivers/perf/arm-cmn.c
+++ b/drivers/perf/arm-cmn.c
@@ -1576,7 +1576,6 @@ static int arm_cmn_event_init(struct perf_event *event)
hw->dn++;
continue;
}
- hw->dtcs_used |= arm_cmn_node_to_xp(cmn, dn)->dtc;
hw->num_dns++;
if (bynodeid)
break;
@@ -1589,6 +1588,12 @@ static int arm_cmn_event_init(struct perf_event *event)
nodeid, nid.x, nid.y, nid.port, nid.dev, type);
return -EINVAL;
}
+ /*
+ * Keep assuming non-cycles events count in all DTC domains; turns out
+ * it's hard to make a worthwhile optimisation around this, short of
+ * going all-in with domain-local counter allocation as well.
+ */
+ hw->dtcs_used = (1U << cmn->num_dtcs) - 1;
return arm_cmn_validate_group(cmn, event);
}
diff --git a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
index 7585e8080b77d5..afc63552ecaf79 100644
--- a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
+++ b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c
@@ -255,7 +255,7 @@ static int imx8_pcie_phy_probe(struct platform_device *pdev)
imx8_phy->perst =
devm_reset_control_get_exclusive(dev, "perst");
if (IS_ERR(imx8_phy->perst))
- dev_err_probe(dev, PTR_ERR(imx8_phy->perst),
+ return dev_err_probe(dev, PTR_ERR(imx8_phy->perst),
"Failed to get PCIE PHY PERST control\n");
}
diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c
index 95c6dbb52da720..ce511ad5d36907 100644
--- a/drivers/phy/phy-can-transceiver.c
+++ b/drivers/phy/phy-can-transceiver.c
@@ -99,6 +99,7 @@ static int can_transceiver_phy_probe(struct platform_device *pdev)
struct gpio_desc *standby_gpio;
struct gpio_desc *enable_gpio;
u32 max_bitrate = 0;
+ int err;
can_transceiver_phy = devm_kzalloc(dev, sizeof(struct can_transceiver_phy), GFP_KERNEL);
if (!can_transceiver_phy)
@@ -124,8 +125,8 @@ static int can_transceiver_phy_probe(struct platform_device *pdev)
return PTR_ERR(phy);
}
- device_property_read_u32(dev, "max-bitrate", &max_bitrate);
- if (!max_bitrate)
+ err = device_property_read_u32(dev, "max-bitrate", &max_bitrate);
+ if ((err != -EINVAL) && !max_bitrate)
dev_warn(dev, "Invalid value for transceiver max bitrate. Ignoring bitrate limit\n");
phy->attrs.max_link_rate = max_bitrate;
diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c b/drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c
index 8807e59a1162cb..a52a9bf13b758d 100644
--- a/drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c
+++ b/drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c
@@ -401,26 +401,13 @@ static const struct hsphy_init_seq init_seq_femtophy[] = {
HSPHY_INIT_CFG(0x90, 0x60, 0),
};
-static const struct hsphy_init_seq init_seq_mdm9607[] = {
- HSPHY_INIT_CFG(0x80, 0x44, 0),
- HSPHY_INIT_CFG(0x81, 0x38, 0),
- HSPHY_INIT_CFG(0x82, 0x24, 0),
- HSPHY_INIT_CFG(0x83, 0x13, 0),
-};
-
static const struct hsphy_data hsphy_data_femtophy = {
.init_seq = init_seq_femtophy,
.init_seq_num = ARRAY_SIZE(init_seq_femtophy),
};
-static const struct hsphy_data hsphy_data_mdm9607 = {
- .init_seq = init_seq_mdm9607,
- .init_seq_num = ARRAY_SIZE(init_seq_mdm9607),
-};
-
static const struct of_device_id qcom_snps_hsphy_match[] = {
{ .compatible = "qcom,usb-hs-28nm-femtophy", .data = &hsphy_data_femtophy, },
- { .compatible = "qcom,usb-hs-28nm-mdm9607", .data = &hsphy_data_mdm9607, },
{ },
};
MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_match);
diff --git a/drivers/phy/renesas/r8a779f0-ether-serdes.c b/drivers/phy/renesas/r8a779f0-ether-serdes.c
index ec6594e6dc275b..e7588a940d6991 100644
--- a/drivers/phy/renesas/r8a779f0-ether-serdes.c
+++ b/drivers/phy/renesas/r8a779f0-ether-serdes.c
@@ -126,7 +126,7 @@ r8a779f0_eth_serdes_chan_setting(struct r8a779f0_eth_serdes_channel *channel)
r8a779f0_eth_serdes_write32(channel->addr, 0x0160, 0x180, 0x0007);
r8a779f0_eth_serdes_write32(channel->addr, 0x01ac, 0x180, 0x0000);
r8a779f0_eth_serdes_write32(channel->addr, 0x00c4, 0x180, 0x0310);
- r8a779f0_eth_serdes_write32(channel->addr, 0x00c8, 0x380, 0x0101);
+ r8a779f0_eth_serdes_write32(channel->addr, 0x00c8, 0x180, 0x0101);
ret = r8a779f0_eth_serdes_reg_wait(channel, 0x00c8, 0x0180, BIT(0), 0);
if (ret)
return ret;
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index e6ededc5152390..a0bc10aa796183 100644
--- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -485,8 +485,10 @@ static int rockchip_usb2phy_power_on(struct phy *phy)
return ret;
ret = property_enable(base, &rport->port_cfg->phy_sus, false);
- if (ret)
+ if (ret) {
+ clk_disable_unprepare(rphy->clk480m);
return ret;
+ }
/* waiting for the utmi_clk to become stable */
usleep_range(1500, 2000);
diff --git a/drivers/phy/sunplus/phy-sunplus-usb2.c b/drivers/phy/sunplus/phy-sunplus-usb2.c
index e827b79f6d493c..56de41091d6397 100644
--- a/drivers/phy/sunplus/phy-sunplus-usb2.c
+++ b/drivers/phy/sunplus/phy-sunplus-usb2.c
@@ -254,6 +254,9 @@ static int sp_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR(usbphy->phy_regs);
usbphy->moon4_res_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "moon4");
+ if (!usbphy->moon4_res_mem)
+ return -EINVAL;
+
usbphy->moon4_regs = devm_ioremap(&pdev->dev, usbphy->moon4_res_mem->start,
resource_size(usbphy->moon4_res_mem));
if (!usbphy->moon4_regs)
diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig
index 15a3bcf3230860..b905902d57508d 100644
--- a/drivers/phy/ti/Kconfig
+++ b/drivers/phy/ti/Kconfig
@@ -23,7 +23,7 @@ config PHY_DM816X_USB
config PHY_AM654_SERDES
tristate "TI AM654 SERDES support"
- depends on OF && ARCH_K3 || COMPILE_TEST
+ depends on OF && (ARCH_K3 || COMPILE_TEST)
depends on COMMON_CLK
select GENERIC_PHY
select MULTIPLEXER
@@ -35,7 +35,7 @@ config PHY_AM654_SERDES
config PHY_J721E_WIZ
tristate "TI J721E WIZ (SERDES Wrapper) support"
- depends on OF && ARCH_K3 || COMPILE_TEST
+ depends on OF && (ARCH_K3 || COMPILE_TEST)
depends on HAS_IOMEM && OF_ADDRESS
depends on COMMON_CLK
select GENERIC_PHY
diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.c b/drivers/pinctrl/aspeed/pinctrl-aspeed.c
index 3945612900e6da..9c6ee46ac7a096 100644
--- a/drivers/pinctrl/aspeed/pinctrl-aspeed.c
+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.c
@@ -93,10 +93,19 @@ static int aspeed_sig_expr_enable(struct aspeed_pinmux_data *ctx,
static int aspeed_sig_expr_disable(struct aspeed_pinmux_data *ctx,
const struct aspeed_sig_expr *expr)
{
+ int ret;
+
pr_debug("Disabling signal %s for %s\n", expr->signal,
expr->function);
- return aspeed_sig_expr_set(ctx, expr, false);
+ ret = aspeed_sig_expr_eval(ctx, expr, true);
+ if (ret < 0)
+ return ret;
+
+ if (ret)
+ return aspeed_sig_expr_set(ctx, expr, false);
+
+ return 0;
}
/**
@@ -114,7 +123,7 @@ static int aspeed_disable_sig(struct aspeed_pinmux_data *ctx,
int ret = 0;
if (!exprs)
- return true;
+ return -EINVAL;
while (*exprs && !ret) {
ret = aspeed_sig_expr_disable(ctx, *exprs);
diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c
index cc3aaba2418824..e49f271de9366e 100644
--- a/drivers/pinctrl/intel/pinctrl-intel.c
+++ b/drivers/pinctrl/intel/pinctrl-intel.c
@@ -1709,6 +1709,12 @@ const struct intel_pinctrl_soc_data *intel_pinctrl_get_soc_data(struct platform_
EXPORT_SYMBOL_GPL(intel_pinctrl_get_soc_data);
#ifdef CONFIG_PM_SLEEP
+static bool __intel_gpio_is_direct_irq(u32 value)
+{
+ return (value & PADCFG0_GPIROUTIOXAPIC) && (value & PADCFG0_GPIOTXDIS) &&
+ (__intel_gpio_get_gpio_mode(value) == PADCFG0_PMODE_GPIO);
+}
+
static bool intel_pinctrl_should_save(struct intel_pinctrl *pctrl, unsigned int pin)
{
const struct pin_desc *pd = pin_desc_get(pctrl->pctldev, pin);
@@ -1742,8 +1748,7 @@ static bool intel_pinctrl_should_save(struct intel_pinctrl *pctrl, unsigned int
* See https://bugzilla.kernel.org/show_bug.cgi?id=214749.
*/
value = readl(intel_get_padcfg(pctrl, pin, PADCFG0));
- if ((value & PADCFG0_GPIROUTIOXAPIC) && (value & PADCFG0_GPIOTXDIS) &&
- (__intel_gpio_get_gpio_mode(value) == PADCFG0_PMODE_GPIO))
+ if (__intel_gpio_is_direct_irq(value))
return true;
return false;
@@ -1873,7 +1878,12 @@ int intel_pinctrl_resume_noirq(struct device *dev)
for (i = 0; i < pctrl->soc->npins; i++) {
const struct pinctrl_pin_desc *desc = &pctrl->soc->pins[i];
- if (!intel_pinctrl_should_save(pctrl, desc->number))
+ if (!(intel_pinctrl_should_save(pctrl, desc->number) ||
+ /*
+ * If the firmware mangled the register contents too much,
+ * check the saved value for the Direct IRQ mode.
+ */
+ __intel_gpio_is_direct_irq(pads[i].padcfg0)))
continue;
intel_restore_padcfg(pctrl, desc->number, PADCFG0, pads[i].padcfg0);
diff --git a/drivers/pinctrl/mediatek/pinctrl-mt8195.c b/drivers/pinctrl/mediatek/pinctrl-mt8195.c
index 89557c7ed2ab01..09c4dcef933838 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mt8195.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mt8195.c
@@ -659,7 +659,7 @@ static const struct mtk_pin_field_calc mt8195_pin_drv_range[] = {
PIN_FIELD_BASE(10, 10, 4, 0x010, 0x10, 9, 3),
PIN_FIELD_BASE(11, 11, 4, 0x000, 0x10, 24, 3),
PIN_FIELD_BASE(12, 12, 4, 0x010, 0x10, 12, 3),
- PIN_FIELD_BASE(13, 13, 4, 0x010, 0x10, 27, 3),
+ PIN_FIELD_BASE(13, 13, 4, 0x000, 0x10, 27, 3),
PIN_FIELD_BASE(14, 14, 4, 0x010, 0x10, 15, 3),
PIN_FIELD_BASE(15, 15, 4, 0x010, 0x10, 0, 3),
PIN_FIELD_BASE(16, 16, 4, 0x010, 0x10, 18, 3),
@@ -708,7 +708,7 @@ static const struct mtk_pin_field_calc mt8195_pin_drv_range[] = {
PIN_FIELD_BASE(78, 78, 3, 0x000, 0x10, 15, 3),
PIN_FIELD_BASE(79, 79, 3, 0x000, 0x10, 18, 3),
PIN_FIELD_BASE(80, 80, 3, 0x000, 0x10, 21, 3),
- PIN_FIELD_BASE(81, 81, 3, 0x000, 0x10, 28, 3),
+ PIN_FIELD_BASE(81, 81, 3, 0x000, 0x10, 24, 3),
PIN_FIELD_BASE(82, 82, 3, 0x000, 0x10, 27, 3),
PIN_FIELD_BASE(83, 83, 3, 0x010, 0x10, 0, 3),
PIN_FIELD_BASE(84, 84, 3, 0x010, 0x10, 3, 3),
diff --git a/drivers/pinctrl/nomadik/pinctrl-ab8500.c b/drivers/pinctrl/nomadik/pinctrl-ab8500.c
index 3106a21cd277fe..d7b244df058f28 100644
--- a/drivers/pinctrl/nomadik/pinctrl-ab8500.c
+++ b/drivers/pinctrl/nomadik/pinctrl-ab8500.c
@@ -6,9 +6,10 @@
*/
#include <linux/kernel.h>
-#include <linux/gpio/driver.h>
#include <linux/pinctrl/pinctrl.h>
+
#include <linux/mfd/abx500/ab8500.h>
+
#include "pinctrl-abx500.h"
/* All the pins that can be used for GPIO and some other functions */
diff --git a/drivers/pinctrl/nomadik/pinctrl-ab8505.c b/drivers/pinctrl/nomadik/pinctrl-ab8505.c
index b93af1fb37f017..45aa958b573e0a 100644
--- a/drivers/pinctrl/nomadik/pinctrl-ab8505.c
+++ b/drivers/pinctrl/nomadik/pinctrl-ab8505.c
@@ -6,9 +6,10 @@
*/
#include <linux/kernel.h>
-#include <linux/gpio/driver.h>
#include <linux/pinctrl/pinctrl.h>
+
#include <linux/mfd/abx500/ab8500.h>
+
#include "pinctrl-abx500.h"
/* All the pins that can be used for GPIO and some other functions */
diff --git a/drivers/pinctrl/nomadik/pinctrl-abx500.c b/drivers/pinctrl/nomadik/pinctrl-abx500.c
index 7aa534576a4598..28c3403df1b038 100644
--- a/drivers/pinctrl/nomadik/pinctrl-abx500.c
+++ b/drivers/pinctrl/nomadik/pinctrl-abx500.c
@@ -6,33 +6,37 @@
*
* Driver allows to use AxB5xx unused pins to be used as GPIO
*/
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/init.h>
+#include <linux/bitops.h>
#include <linux/err.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/platform_device.h>
#include <linux/gpio/driver.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
-#include <linux/interrupt.h>
-#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ab8500.h>
-#include <linux/pinctrl/pinctrl.h>
+
#include <linux/pinctrl/consumer.h>
-#include <linux/pinctrl/pinmux.h>
-#include <linux/pinctrl/pinconf.h>
-#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
-#include "pinctrl-abx500.h"
#include "../core.h"
#include "../pinconf.h"
#include "../pinctrl-utils.h"
+#include "pinctrl-abx500.h"
+
/*
* GPIO registers offset
* Bank: 0x10
diff --git a/drivers/pinctrl/nomadik/pinctrl-abx500.h b/drivers/pinctrl/nomadik/pinctrl-abx500.h
index 90bb12fe8073fd..d6752208467526 100644
--- a/drivers/pinctrl/nomadik/pinctrl-abx500.h
+++ b/drivers/pinctrl/nomadik/pinctrl-abx500.h
@@ -2,6 +2,10 @@
#ifndef PINCTRL_PINCTRL_ABx500_H
#define PINCTRL_PINCTRL_ABx500_H
+#include <linux/types.h>
+
+struct pinctrl_pin_desc;
+
/* Package definitions */
#define PINCTRL_AB8500 0
#define PINCTRL_AB8505 1
diff --git a/drivers/pinctrl/nomadik/pinctrl-nomadik-db8500.c b/drivers/pinctrl/nomadik/pinctrl-nomadik-db8500.c
index 758d21f0a8503a..490e0959e8be6c 100644
--- a/drivers/pinctrl/nomadik/pinctrl-nomadik-db8500.c
+++ b/drivers/pinctrl/nomadik/pinctrl-nomadik-db8500.c
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
+#include <linux/types.h>
+
#include <linux/pinctrl/pinctrl.h>
+
#include "pinctrl-nomadik.h"
/* All the pins that can be used for GPIO and some other functions */
diff --git a/drivers/pinctrl/nomadik/pinctrl-nomadik-stn8815.c b/drivers/pinctrl/nomadik/pinctrl-nomadik-stn8815.c
index c0d7c86d093910..1552222ac68e74 100644
--- a/drivers/pinctrl/nomadik/pinctrl-nomadik-stn8815.c
+++ b/drivers/pinctrl/nomadik/pinctrl-nomadik-stn8815.c
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
+#include <linux/types.h>
+
#include <linux/pinctrl/pinctrl.h>
+
#include "pinctrl-nomadik.h"
/* All the pins that can be used for GPIO and some other functions */
diff --git a/drivers/pinctrl/nomadik/pinctrl-nomadik.c b/drivers/pinctrl/nomadik/pinctrl-nomadik.c
index f7d02513d8cc10..86a638077a6973 100644
--- a/drivers/pinctrl/nomadik/pinctrl-nomadik.c
+++ b/drivers/pinctrl/nomadik/pinctrl-nomadik.c
@@ -7,30 +7,34 @@
* Rewritten based on work by Prafulla WADASKAR <prafulla.wadaskar@st.com>
* Copyright (C) 2011-2013 Linus Walleij <linus.walleij@linaro.org>
*/
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
+#include <linux/bitops.h>
#include <linux/clk.h>
+#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/driver.h>
-#include <linux/spinlock.h>
+#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/slab.h>
-#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
#include <linux/of_address.h>
-#include <linux/bitops.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+/* Since we request GPIOs from ourself */
+#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
-#include <linux/pinctrl/pinconf.h>
-/* Since we request GPIOs from ourself */
-#include <linux/pinctrl/consumer.h>
-#include "pinctrl-nomadik.h"
+
#include "../core.h"
#include "../pinctrl-utils.h"
+#include "pinctrl-nomadik.h"
+
/*
* The GPIO module in the Nomadik family of Systems-on-Chip is an
* AMBA device, managing 32 pins and alternate functions. The logic block
@@ -907,8 +911,6 @@ static int nmk_gpio_get_mode(struct nmk_gpio_chip *nmk_chip, int offset)
return (afunc ? NMK_GPIO_ALT_A : 0) | (bfunc ? NMK_GPIO_ALT_B : 0);
}
-#include <linux/seq_file.h>
-
static void nmk_gpio_dbg_show_one(struct seq_file *s,
struct pinctrl_dev *pctldev, struct gpio_chip *chip,
unsigned offset, unsigned gpio)
diff --git a/drivers/pinctrl/nomadik/pinctrl-nomadik.h b/drivers/pinctrl/nomadik/pinctrl-nomadik.h
index 84e29775733575..1ef2559bc5710b 100644
--- a/drivers/pinctrl/nomadik/pinctrl-nomadik.h
+++ b/drivers/pinctrl/nomadik/pinctrl-nomadik.h
@@ -2,6 +2,11 @@
#ifndef PINCTRL_PINCTRL_NOMADIK_H
#define PINCTRL_PINCTRL_NOMADIK_H
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include <linux/pinctrl/pinctrl.h>
+
/* Package definitions */
#define PINCTRL_NMK_STN8815 0
#define PINCTRL_NMK_DB8500 1
diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c
index 9bc6e3922e78e8..32c3edaf903854 100644
--- a/drivers/pinctrl/pinctrl-amd.c
+++ b/drivers/pinctrl/pinctrl-amd.c
@@ -365,6 +365,7 @@ static void amd_gpio_dbg_show(struct seq_file *s, struct gpio_chip *gc)
} else {
debounce_enable = " ∅";
+ time = 0;
}
snprintf(debounce_value, sizeof(debounce_value), "%u", time * unit);
seq_printf(s, "debounce %s (🕑 %sus)| ", debounce_enable, debounce_value);
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index da974ff2d75d0f..5eeac92f610a05 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -926,19 +926,19 @@ static struct rockchip_mux_route_data rk3568_mux_route_data[] = {
RK_MUXROUTE_PMU(0, RK_PB5, 4, 0x0110, WRITE_MASK_VAL(3, 2, 1)), /* PWM1 IO mux M1 */
RK_MUXROUTE_PMU(0, RK_PC1, 1, 0x0110, WRITE_MASK_VAL(5, 4, 0)), /* PWM2 IO mux M0 */
RK_MUXROUTE_PMU(0, RK_PB6, 4, 0x0110, WRITE_MASK_VAL(5, 4, 1)), /* PWM2 IO mux M1 */
- RK_MUXROUTE_PMU(0, RK_PB3, 2, 0x0300, WRITE_MASK_VAL(0, 0, 0)), /* CAN0 IO mux M0 */
+ RK_MUXROUTE_GRF(0, RK_PB3, 2, 0x0300, WRITE_MASK_VAL(0, 0, 0)), /* CAN0 IO mux M0 */
RK_MUXROUTE_GRF(2, RK_PA1, 4, 0x0300, WRITE_MASK_VAL(0, 0, 1)), /* CAN0 IO mux M1 */
RK_MUXROUTE_GRF(1, RK_PA1, 3, 0x0300, WRITE_MASK_VAL(2, 2, 0)), /* CAN1 IO mux M0 */
RK_MUXROUTE_GRF(4, RK_PC3, 3, 0x0300, WRITE_MASK_VAL(2, 2, 1)), /* CAN1 IO mux M1 */
RK_MUXROUTE_GRF(4, RK_PB5, 3, 0x0300, WRITE_MASK_VAL(4, 4, 0)), /* CAN2 IO mux M0 */
RK_MUXROUTE_GRF(2, RK_PB2, 4, 0x0300, WRITE_MASK_VAL(4, 4, 1)), /* CAN2 IO mux M1 */
RK_MUXROUTE_GRF(4, RK_PC4, 1, 0x0300, WRITE_MASK_VAL(6, 6, 0)), /* HPDIN IO mux M0 */
- RK_MUXROUTE_PMU(0, RK_PC2, 2, 0x0300, WRITE_MASK_VAL(6, 6, 1)), /* HPDIN IO mux M1 */
+ RK_MUXROUTE_GRF(0, RK_PC2, 2, 0x0300, WRITE_MASK_VAL(6, 6, 1)), /* HPDIN IO mux M1 */
RK_MUXROUTE_GRF(3, RK_PB1, 3, 0x0300, WRITE_MASK_VAL(8, 8, 0)), /* GMAC1 IO mux M0 */
RK_MUXROUTE_GRF(4, RK_PA7, 3, 0x0300, WRITE_MASK_VAL(8, 8, 1)), /* GMAC1 IO mux M1 */
RK_MUXROUTE_GRF(4, RK_PD1, 1, 0x0300, WRITE_MASK_VAL(10, 10, 0)), /* HDMITX IO mux M0 */
- RK_MUXROUTE_PMU(0, RK_PC7, 1, 0x0300, WRITE_MASK_VAL(10, 10, 1)), /* HDMITX IO mux M1 */
- RK_MUXROUTE_PMU(0, RK_PB6, 1, 0x0300, WRITE_MASK_VAL(14, 14, 0)), /* I2C2 IO mux M0 */
+ RK_MUXROUTE_GRF(0, RK_PC7, 1, 0x0300, WRITE_MASK_VAL(10, 10, 1)), /* HDMITX IO mux M1 */
+ RK_MUXROUTE_GRF(0, RK_PB6, 1, 0x0300, WRITE_MASK_VAL(14, 14, 0)), /* I2C2 IO mux M0 */
RK_MUXROUTE_GRF(4, RK_PB4, 1, 0x0300, WRITE_MASK_VAL(14, 14, 1)), /* I2C2 IO mux M1 */
RK_MUXROUTE_GRF(1, RK_PA0, 1, 0x0304, WRITE_MASK_VAL(0, 0, 0)), /* I2C3 IO mux M0 */
RK_MUXROUTE_GRF(3, RK_PB6, 4, 0x0304, WRITE_MASK_VAL(0, 0, 1)), /* I2C3 IO mux M1 */
@@ -964,7 +964,7 @@ static struct rockchip_mux_route_data rk3568_mux_route_data[] = {
RK_MUXROUTE_GRF(4, RK_PC3, 1, 0x0308, WRITE_MASK_VAL(12, 12, 1)), /* PWM15 IO mux M1 */
RK_MUXROUTE_GRF(3, RK_PD2, 3, 0x0308, WRITE_MASK_VAL(14, 14, 0)), /* SDMMC2 IO mux M0 */
RK_MUXROUTE_GRF(3, RK_PA5, 5, 0x0308, WRITE_MASK_VAL(14, 14, 1)), /* SDMMC2 IO mux M1 */
- RK_MUXROUTE_PMU(0, RK_PB5, 2, 0x030c, WRITE_MASK_VAL(0, 0, 0)), /* SPI0 IO mux M0 */
+ RK_MUXROUTE_GRF(0, RK_PB5, 2, 0x030c, WRITE_MASK_VAL(0, 0, 0)), /* SPI0 IO mux M0 */
RK_MUXROUTE_GRF(2, RK_PD3, 3, 0x030c, WRITE_MASK_VAL(0, 0, 1)), /* SPI0 IO mux M1 */
RK_MUXROUTE_GRF(2, RK_PB5, 3, 0x030c, WRITE_MASK_VAL(2, 2, 0)), /* SPI1 IO mux M0 */
RK_MUXROUTE_GRF(3, RK_PC3, 3, 0x030c, WRITE_MASK_VAL(2, 2, 1)), /* SPI1 IO mux M1 */
@@ -973,8 +973,8 @@ static struct rockchip_mux_route_data rk3568_mux_route_data[] = {
RK_MUXROUTE_GRF(4, RK_PB3, 4, 0x030c, WRITE_MASK_VAL(6, 6, 0)), /* SPI3 IO mux M0 */
RK_MUXROUTE_GRF(4, RK_PC2, 2, 0x030c, WRITE_MASK_VAL(6, 6, 1)), /* SPI3 IO mux M1 */
RK_MUXROUTE_GRF(2, RK_PB4, 2, 0x030c, WRITE_MASK_VAL(8, 8, 0)), /* UART1 IO mux M0 */
- RK_MUXROUTE_PMU(0, RK_PD1, 1, 0x030c, WRITE_MASK_VAL(8, 8, 1)), /* UART1 IO mux M1 */
- RK_MUXROUTE_PMU(0, RK_PD1, 1, 0x030c, WRITE_MASK_VAL(10, 10, 0)), /* UART2 IO mux M0 */
+ RK_MUXROUTE_GRF(3, RK_PD6, 4, 0x030c, WRITE_MASK_VAL(8, 8, 1)), /* UART1 IO mux M1 */
+ RK_MUXROUTE_GRF(0, RK_PD1, 1, 0x030c, WRITE_MASK_VAL(10, 10, 0)), /* UART2 IO mux M0 */
RK_MUXROUTE_GRF(1, RK_PD5, 2, 0x030c, WRITE_MASK_VAL(10, 10, 1)), /* UART2 IO mux M1 */
RK_MUXROUTE_GRF(1, RK_PA1, 2, 0x030c, WRITE_MASK_VAL(12, 12, 0)), /* UART3 IO mux M0 */
RK_MUXROUTE_GRF(3, RK_PB7, 4, 0x030c, WRITE_MASK_VAL(12, 12, 1)), /* UART3 IO mux M1 */
@@ -1004,13 +1004,13 @@ static struct rockchip_mux_route_data rk3568_mux_route_data[] = {
RK_MUXROUTE_GRF(3, RK_PD6, 5, 0x0314, WRITE_MASK_VAL(1, 0, 1)), /* PDM IO mux M1 */
RK_MUXROUTE_GRF(4, RK_PA0, 4, 0x0314, WRITE_MASK_VAL(1, 0, 1)), /* PDM IO mux M1 */
RK_MUXROUTE_GRF(3, RK_PC4, 5, 0x0314, WRITE_MASK_VAL(1, 0, 2)), /* PDM IO mux M2 */
- RK_MUXROUTE_PMU(0, RK_PA5, 3, 0x0314, WRITE_MASK_VAL(3, 2, 0)), /* PCIE20 IO mux M0 */
+ RK_MUXROUTE_GRF(0, RK_PA5, 3, 0x0314, WRITE_MASK_VAL(3, 2, 0)), /* PCIE20 IO mux M0 */
RK_MUXROUTE_GRF(2, RK_PD0, 4, 0x0314, WRITE_MASK_VAL(3, 2, 1)), /* PCIE20 IO mux M1 */
RK_MUXROUTE_GRF(1, RK_PB0, 4, 0x0314, WRITE_MASK_VAL(3, 2, 2)), /* PCIE20 IO mux M2 */
- RK_MUXROUTE_PMU(0, RK_PA4, 3, 0x0314, WRITE_MASK_VAL(5, 4, 0)), /* PCIE30X1 IO mux M0 */
+ RK_MUXROUTE_GRF(0, RK_PA4, 3, 0x0314, WRITE_MASK_VAL(5, 4, 0)), /* PCIE30X1 IO mux M0 */
RK_MUXROUTE_GRF(2, RK_PD2, 4, 0x0314, WRITE_MASK_VAL(5, 4, 1)), /* PCIE30X1 IO mux M1 */
RK_MUXROUTE_GRF(1, RK_PA5, 4, 0x0314, WRITE_MASK_VAL(5, 4, 2)), /* PCIE30X1 IO mux M2 */
- RK_MUXROUTE_PMU(0, RK_PA6, 2, 0x0314, WRITE_MASK_VAL(7, 6, 0)), /* PCIE30X2 IO mux M0 */
+ RK_MUXROUTE_GRF(0, RK_PA6, 2, 0x0314, WRITE_MASK_VAL(7, 6, 0)), /* PCIE30X2 IO mux M0 */
RK_MUXROUTE_GRF(2, RK_PD4, 4, 0x0314, WRITE_MASK_VAL(7, 6, 1)), /* PCIE30X2 IO mux M1 */
RK_MUXROUTE_GRF(4, RK_PC2, 4, 0x0314, WRITE_MASK_VAL(7, 6, 2)), /* PCIE30X2 IO mux M2 */
};
@@ -2436,10 +2436,19 @@ static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num)
case RK3308:
case RK3368:
case RK3399:
+ case RK3568:
case RK3588:
pull_type = bank->pull_type[pin_num / 8];
data >>= bit;
data &= (1 << RK3188_PULL_BITS_PER_PIN) - 1;
+ /*
+ * In the TRM, pull-up being 1 for everything except the GPIO0_D3-D6,
+ * where that pull up value becomes 3.
+ */
+ if (ctrl->type == RK3568 && bank->bank_num == 0 && pin_num >= 27 && pin_num <= 30) {
+ if (data == 3)
+ data = 1;
+ }
return rockchip_pull_list[pull_type][data];
default:
@@ -2497,7 +2506,7 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank,
}
}
/*
- * In the TRM, pull-up being 1 for everything except the GPIO0_D0-D6,
+ * In the TRM, pull-up being 1 for everything except the GPIO0_D3-D6,
* where that pull up value becomes 3.
*/
if (ctrl->type == RK3568 && bank->bank_num == 0 && pin_num >= 27 && pin_num <= 30) {
diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
index 99c3745da45620..190923757cda00 100644
--- a/drivers/pinctrl/pinctrl-single.c
+++ b/drivers/pinctrl/pinctrl-single.c
@@ -372,6 +372,8 @@ static int pcs_set_mux(struct pinctrl_dev *pctldev, unsigned fselector,
if (!pcs->fmask)
return 0;
function = pinmux_generic_get_function(pctldev, fselector);
+ if (!function)
+ return -EINVAL;
func = function->data;
if (!func)
return -EINVAL;
diff --git a/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c b/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c
index c3c8c34148f11c..e22d03ce292e74 100644
--- a/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c
+++ b/drivers/pinctrl/qcom/pinctrl-sm8450-lpass-lpi.c
@@ -105,7 +105,7 @@ static const struct pinctrl_pin_desc sm8450_lpi_pins[] = {
static const char * const swr_tx_clk_groups[] = { "gpio0" };
static const char * const swr_tx_data_groups[] = { "gpio1", "gpio2", "gpio14" };
static const char * const swr_rx_clk_groups[] = { "gpio3" };
-static const char * const swr_rx_data_groups[] = { "gpio4", "gpio5", "gpio15" };
+static const char * const swr_rx_data_groups[] = { "gpio4", "gpio5" };
static const char * const dmic1_clk_groups[] = { "gpio6" };
static const char * const dmic1_data_groups[] = { "gpio7" };
static const char * const dmic2_clk_groups[] = { "gpio8" };
diff --git a/drivers/pinctrl/sunplus/sppctl.c b/drivers/pinctrl/sunplus/sppctl.c
index 2b3335ab56c668..25101293268f6f 100644
--- a/drivers/pinctrl/sunplus/sppctl.c
+++ b/drivers/pinctrl/sunplus/sppctl.c
@@ -499,7 +499,6 @@ static int sppctl_gpio_set_config(struct gpio_chip *chip, unsigned int offset,
return 0;
}
-#ifdef CONFIG_DEBUG_FS
static void sppctl_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
const char *label;
@@ -521,7 +520,6 @@ static void sppctl_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
seq_puts(s, "\n");
}
}
-#endif
static int sppctl_gpio_new(struct platform_device *pdev, struct sppctl_pdata *pctl)
{
@@ -550,9 +548,8 @@ static int sppctl_gpio_new(struct platform_device *pdev, struct sppctl_pdata *pc
gchip->get = sppctl_gpio_get;
gchip->set = sppctl_gpio_set;
gchip->set_config = sppctl_gpio_set_config;
-#ifdef CONFIG_DEBUG_FS
- gchip->dbg_show = sppctl_gpio_dbg_show;
-#endif
+ gchip->dbg_show = IS_ENABLED(CONFIG_DEBUG_FS) ?
+ sppctl_gpio_dbg_show : NULL;
gchip->base = -1;
gchip->ngpio = sppctl_gpio_list_sz;
gchip->names = sppctl_gpio_list_s;
diff --git a/drivers/platform/surface/aggregator/controller.c b/drivers/platform/surface/aggregator/controller.c
index 43e7651991371b..c6537a1b3a2ec1 100644
--- a/drivers/platform/surface/aggregator/controller.c
+++ b/drivers/platform/surface/aggregator/controller.c
@@ -1700,8 +1700,10 @@ int ssam_request_sync(struct ssam_controller *ctrl,
return status;
status = ssam_request_sync_init(rqst, spec->flags);
- if (status)
+ if (status) {
+ ssam_request_sync_free(rqst);
return status;
+ }
ssam_request_sync_set_resp(rqst, rsp);
diff --git a/drivers/platform/surface/aggregator/ssh_request_layer.c b/drivers/platform/surface/aggregator/ssh_request_layer.c
index f5565570f16c79..69132976d297e2 100644
--- a/drivers/platform/surface/aggregator/ssh_request_layer.c
+++ b/drivers/platform/surface/aggregator/ssh_request_layer.c
@@ -916,6 +916,20 @@ static void ssh_rtl_rx_command(struct ssh_ptl *p, const struct ssam_span *data)
if (sshp_parse_command(dev, data, &command, &command_data))
return;
+ /*
+ * Check if the message was intended for us. If not, drop it.
+ *
+ * Note: We will need to change this to handle debug messages. On newer
+ * generation devices, these seem to be sent to tid_out=0x03. We as
+ * host can still receive them as they can be forwarded via an override
+ * option on SAM, but doing so does not change tid_out=0x00.
+ */
+ if (command->tid_out != 0x00) {
+ rtl_warn(rtl, "rtl: dropping message not intended for us (tid = %#04x)\n",
+ command->tid_out);
+ return;
+ }
+
if (ssh_rqid_is_event(get_unaligned_le16(&command->rqid)))
ssh_rtl_rx_event(rtl, command, &command_data);
else
diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kconfig
index a825af8126c836..2ce8cb2170dfc0 100644
--- a/drivers/platform/x86/amd/Kconfig
+++ b/drivers/platform/x86/amd/Kconfig
@@ -8,6 +8,7 @@ source "drivers/platform/x86/amd/pmf/Kconfig"
config AMD_PMC
tristate "AMD SoC PMC driver"
depends on ACPI && PCI && RTC_CLASS
+ select SERIO
help
The driver provides support for AMD Power Management Controller
primarily responsible for S2Idle transactions that are driven from
diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c
index 439d282aafd192..3cbb01ec10e320 100644
--- a/drivers/platform/x86/amd/pmc.c
+++ b/drivers/platform/x86/amd/pmc.c
@@ -22,6 +22,7 @@
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
+#include <linux/serio.h>
#include <linux/suspend.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
@@ -160,6 +161,10 @@ static bool enable_stb;
module_param(enable_stb, bool, 0644);
MODULE_PARM_DESC(enable_stb, "Enable the STB debug mechanism");
+static bool disable_workarounds;
+module_param(disable_workarounds, bool, 0644);
+MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs");
+
static struct amd_pmc_dev pmc;
static int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool ret);
static int amd_pmc_read_stb(struct amd_pmc_dev *dev, u32 *buf);
@@ -653,6 +658,33 @@ static int amd_pmc_get_os_hint(struct amd_pmc_dev *dev)
return -EINVAL;
}
+static int amd_pmc_czn_wa_irq1(struct amd_pmc_dev *pdev)
+{
+ struct device *d;
+ int rc;
+
+ if (!pdev->major) {
+ rc = amd_pmc_get_smu_version(pdev);
+ if (rc)
+ return rc;
+ }
+
+ if (pdev->major > 64 || (pdev->major == 64 && pdev->minor > 65))
+ return 0;
+
+ d = bus_find_device_by_name(&serio_bus, NULL, "serio0");
+ if (!d)
+ return 0;
+ if (device_may_wakeup(d)) {
+ dev_info_once(d, "Disabling IRQ1 wakeup source to avoid platform firmware bug\n");
+ disable_irq_wake(1);
+ device_set_wakeup_enable(d, false);
+ }
+ put_device(d);
+
+ return 0;
+}
+
static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg)
{
struct rtc_device *rtc_device;
@@ -715,8 +747,8 @@ static void amd_pmc_s2idle_prepare(void)
/* Reset and Start SMU logging - to monitor the s0i3 stats */
amd_pmc_setup_smu_logging(pdev);
- /* Activate CZN specific RTC functionality */
- if (pdev->cpu_id == AMD_CPU_ID_CZN) {
+ /* Activate CZN specific platform bug workarounds */
+ if (pdev->cpu_id == AMD_CPU_ID_CZN && !disable_workarounds) {
rc = amd_pmc_verify_czn_rtc(pdev, &arg);
if (rc) {
dev_err(pdev->dev, "failed to set RTC: %d\n", rc);
@@ -782,6 +814,25 @@ static struct acpi_s2idle_dev_ops amd_pmc_s2idle_dev_ops = {
.check = amd_pmc_s2idle_check,
.restore = amd_pmc_s2idle_restore,
};
+
+static int __maybe_unused amd_pmc_suspend_handler(struct device *dev)
+{
+ struct amd_pmc_dev *pdev = dev_get_drvdata(dev);
+
+ if (pdev->cpu_id == AMD_CPU_ID_CZN && !disable_workarounds) {
+ int rc = amd_pmc_czn_wa_irq1(pdev);
+
+ if (rc) {
+ dev_err(pdev->dev, "failed to adjust keyboard wakeup: %d\n", rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(amd_pmc_pm, amd_pmc_suspend_handler, NULL);
+
#endif
static const struct pci_device_id pmc_pci_ids[] = {
@@ -932,7 +983,7 @@ static int amd_pmc_probe(struct platform_device *pdev)
if (enable_stb && (dev->cpu_id == AMD_CPU_ID_YC || dev->cpu_id == AMD_CPU_ID_CB)) {
err = amd_pmc_s2d_init(dev);
if (err)
- return err;
+ goto err_pci_dev_put;
}
platform_set_drvdata(pdev, dev);
@@ -980,6 +1031,9 @@ static struct platform_driver amd_pmc_driver = {
.name = "amd_pmc",
.acpi_match_table = amd_pmc_acpi_ids,
.dev_groups = pmc_groups,
+#ifdef CONFIG_SUSPEND
+ .pm = &amd_pmc_pm,
+#endif
},
.probe = amd_pmc_probe,
.remove = amd_pmc_remove,
diff --git a/drivers/platform/x86/amd/pmf/auto-mode.c b/drivers/platform/x86/amd/pmf/auto-mode.c
index 644af42e07cf81..96a8e1832c05e9 100644
--- a/drivers/platform/x86/amd/pmf/auto-mode.c
+++ b/drivers/platform/x86/amd/pmf/auto-mode.c
@@ -275,13 +275,8 @@ int amd_pmf_reset_amt(struct amd_pmf_dev *dev)
*/
if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) {
- int mode = amd_pmf_get_pprof_modes(dev);
-
- if (mode < 0)
- return mode;
-
dev_dbg(dev->dev, "resetting AMT thermals\n");
- amd_pmf_update_slider(dev, SLIDER_OP_SET, mode, NULL);
+ amd_pmf_set_sps_power_limits(dev);
}
return 0;
}
@@ -299,7 +294,5 @@ void amd_pmf_deinit_auto_mode(struct amd_pmf_dev *dev)
void amd_pmf_init_auto_mode(struct amd_pmf_dev *dev)
{
amd_pmf_load_defaults_auto_mode(dev);
- /* update the thermal limits for Automode */
- amd_pmf_set_automode(dev, config_store.current_mode, NULL);
amd_pmf_init_metrics_table(dev);
}
diff --git a/drivers/platform/x86/amd/pmf/cnqf.c b/drivers/platform/x86/amd/pmf/cnqf.c
index 3f9731a2ac28d6..4beb22a194667d 100644
--- a/drivers/platform/x86/amd/pmf/cnqf.c
+++ b/drivers/platform/x86/amd/pmf/cnqf.c
@@ -103,7 +103,7 @@ int amd_pmf_trans_cnqf(struct amd_pmf_dev *dev, int socket_power, ktime_t time_l
src = amd_pmf_cnqf_get_power_source(dev);
- if (dev->current_profile == PLATFORM_PROFILE_BALANCED) {
+ if (is_pprof_balanced(dev)) {
amd_pmf_set_cnqf(dev, src, config_store.current_mode, NULL);
} else {
/*
@@ -307,13 +307,9 @@ static ssize_t cnqf_enable_store(struct device *dev,
const char *buf, size_t count)
{
struct amd_pmf_dev *pdev = dev_get_drvdata(dev);
- int mode, result, src;
+ int result, src;
bool input;
- mode = amd_pmf_get_pprof_modes(pdev);
- if (mode < 0)
- return mode;
-
result = kstrtobool(buf, &input);
if (result)
return result;
@@ -321,11 +317,11 @@ static ssize_t cnqf_enable_store(struct device *dev,
src = amd_pmf_cnqf_get_power_source(pdev);
pdev->cnqf_enabled = input;
- if (pdev->cnqf_enabled && pdev->current_profile == PLATFORM_PROFILE_BALANCED) {
+ if (pdev->cnqf_enabled && is_pprof_balanced(pdev)) {
amd_pmf_set_cnqf(pdev, src, config_store.current_mode, NULL);
} else {
if (is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR))
- amd_pmf_update_slider(pdev, SLIDER_OP_SET, mode, NULL);
+ amd_pmf_set_sps_power_limits(pdev);
}
dev_dbg(pdev->dev, "Received CnQF %s\n", input ? "on" : "off");
@@ -386,7 +382,7 @@ int amd_pmf_init_cnqf(struct amd_pmf_dev *dev)
dev->cnqf_enabled = amd_pmf_check_flags(dev);
/* update the thermal for CnQF */
- if (dev->cnqf_enabled && dev->current_profile == PLATFORM_PROFILE_BALANCED) {
+ if (dev->cnqf_enabled && is_pprof_balanced(dev)) {
src = amd_pmf_cnqf_get_power_source(dev);
amd_pmf_set_cnqf(dev, src, config_store.current_mode, NULL);
}
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index a5f5a4bcff6d9a..da23639071d798 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -58,6 +58,25 @@ static bool force_load;
module_param(force_load, bool, 0444);
MODULE_PARM_DESC(force_load, "Force load this driver on supported older platforms (experimental)");
+static int amd_pmf_pwr_src_notify_call(struct notifier_block *nb, unsigned long event, void *data)
+{
+ struct amd_pmf_dev *pmf = container_of(nb, struct amd_pmf_dev, pwr_src_notifier);
+
+ if (event != PSY_EVENT_PROP_CHANGED)
+ return NOTIFY_OK;
+
+ if (is_apmf_func_supported(pmf, APMF_FUNC_AUTO_MODE) ||
+ is_apmf_func_supported(pmf, APMF_FUNC_DYN_SLIDER_DC) ||
+ is_apmf_func_supported(pmf, APMF_FUNC_DYN_SLIDER_AC)) {
+ if ((pmf->amt_enabled || pmf->cnqf_enabled) && is_pprof_balanced(pmf))
+ return NOTIFY_DONE;
+ }
+
+ amd_pmf_set_sps_power_limits(pmf);
+
+ return NOTIFY_OK;
+}
+
static int current_power_limits_show(struct seq_file *seq, void *unused)
{
struct amd_pmf_dev *dev = seq->private;
@@ -366,14 +385,18 @@ static int amd_pmf_probe(struct platform_device *pdev)
if (!dev->regbase)
return -ENOMEM;
+ mutex_init(&dev->lock);
+ mutex_init(&dev->update_mutex);
+
apmf_acpi_init(dev);
platform_set_drvdata(pdev, dev);
amd_pmf_init_features(dev);
apmf_install_handler(dev);
amd_pmf_dbgfs_register(dev);
- mutex_init(&dev->lock);
- mutex_init(&dev->update_mutex);
+ dev->pwr_src_notifier.notifier_call = amd_pmf_pwr_src_notify_call;
+ power_supply_reg_notifier(&dev->pwr_src_notifier);
+
dev_info(dev->dev, "registered PMF device successfully\n");
return 0;
@@ -383,11 +406,12 @@ static int amd_pmf_remove(struct platform_device *pdev)
{
struct amd_pmf_dev *dev = platform_get_drvdata(pdev);
- mutex_destroy(&dev->lock);
- mutex_destroy(&dev->update_mutex);
+ power_supply_unreg_notifier(&dev->pwr_src_notifier);
amd_pmf_deinit_features(dev);
apmf_acpi_deinit(dev);
amd_pmf_dbgfs_unregister(dev);
+ mutex_destroy(&dev->lock);
+ mutex_destroy(&dev->update_mutex);
kfree(dev->buf);
return 0;
}
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index 84bbe2c6ea612b..06c30cdc05733a 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -169,6 +169,7 @@ struct amd_pmf_dev {
struct mutex update_mutex; /* protects race between ACPI handler and metrics thread */
bool cnqf_enabled;
bool cnqf_supported;
+ struct notifier_block pwr_src_notifier;
};
struct apmf_sps_prop_granular {
@@ -391,9 +392,11 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev);
void amd_pmf_deinit_sps(struct amd_pmf_dev *dev);
int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev,
struct apmf_static_slider_granular_output *output);
+bool is_pprof_balanced(struct amd_pmf_dev *pmf);
int apmf_update_fan_idx(struct amd_pmf_dev *pdev, bool manual, u32 idx);
+int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf);
/* Auto Mode Layer */
int apmf_get_auto_mode_def(struct amd_pmf_dev *pdev, struct apmf_auto_mode *data);
diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c
index dba7e36962dc1e..bed762d47a14a5 100644
--- a/drivers/platform/x86/amd/pmf/sps.c
+++ b/drivers/platform/x86/amd/pmf/sps.c
@@ -70,6 +70,24 @@ void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
}
}
+int amd_pmf_set_sps_power_limits(struct amd_pmf_dev *pmf)
+{
+ int mode;
+
+ mode = amd_pmf_get_pprof_modes(pmf);
+ if (mode < 0)
+ return mode;
+
+ amd_pmf_update_slider(pmf, SLIDER_OP_SET, mode, NULL);
+
+ return 0;
+}
+
+bool is_pprof_balanced(struct amd_pmf_dev *pmf)
+{
+ return (pmf->current_profile == PLATFORM_PROFILE_BALANCED) ? true : false;
+}
+
static int amd_pmf_profile_get(struct platform_profile_handler *pprof,
enum platform_profile_option *profile)
{
@@ -105,15 +123,10 @@ static int amd_pmf_profile_set(struct platform_profile_handler *pprof,
enum platform_profile_option profile)
{
struct amd_pmf_dev *pmf = container_of(pprof, struct amd_pmf_dev, pprof);
- int mode;
pmf->current_profile = profile;
- mode = amd_pmf_get_pprof_modes(pmf);
- if (mode < 0)
- return mode;
- amd_pmf_update_slider(pmf, SLIDER_OP_SET, mode, NULL);
- return 0;
+ return amd_pmf_set_sps_power_limits(pmf);
}
int amd_pmf_init_sps(struct amd_pmf_dev *dev)
@@ -123,6 +136,9 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev)
dev->current_profile = PLATFORM_PROFILE_BALANCED;
amd_pmf_load_defaults_sps(dev);
+ /* update SPS balanced power mode thermals */
+ amd_pmf_set_sps_power_limits(dev);
+
dev->pprof.profile_get = amd_pmf_profile_get;
dev->pprof.profile_set = amd_pmf_profile_set;
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
index ca33df7ea550ee..9333f82cfa8a0b 100644
--- a/drivers/platform/x86/apple-gmux.c
+++ b/drivers/platform/x86/apple-gmux.c
@@ -64,29 +64,6 @@ struct apple_gmux_data {
static struct apple_gmux_data *apple_gmux_data;
-/*
- * gmux port offsets. Many of these are not yet used, but may be in the
- * future, and it's useful to have them documented here anyhow.
- */
-#define GMUX_PORT_VERSION_MAJOR 0x04
-#define GMUX_PORT_VERSION_MINOR 0x05
-#define GMUX_PORT_VERSION_RELEASE 0x06
-#define GMUX_PORT_SWITCH_DISPLAY 0x10
-#define GMUX_PORT_SWITCH_GET_DISPLAY 0x11
-#define GMUX_PORT_INTERRUPT_ENABLE 0x14
-#define GMUX_PORT_INTERRUPT_STATUS 0x16
-#define GMUX_PORT_SWITCH_DDC 0x28
-#define GMUX_PORT_SWITCH_EXTERNAL 0x40
-#define GMUX_PORT_SWITCH_GET_EXTERNAL 0x41
-#define GMUX_PORT_DISCRETE_POWER 0x50
-#define GMUX_PORT_MAX_BRIGHTNESS 0x70
-#define GMUX_PORT_BRIGHTNESS 0x74
-#define GMUX_PORT_VALUE 0xc2
-#define GMUX_PORT_READ 0xd0
-#define GMUX_PORT_WRITE 0xd4
-
-#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
-
#define GMUX_INTERRUPT_ENABLE 0xff
#define GMUX_INTERRUPT_DISABLE 0x00
@@ -249,23 +226,6 @@ static void gmux_write32(struct apple_gmux_data *gmux_data, int port,
gmux_pio_write32(gmux_data, port, val);
}
-static bool gmux_is_indexed(struct apple_gmux_data *gmux_data)
-{
- u16 val;
-
- outb(0xaa, gmux_data->iostart + 0xcc);
- outb(0x55, gmux_data->iostart + 0xcd);
- outb(0x00, gmux_data->iostart + 0xce);
-
- val = inb(gmux_data->iostart + 0xcc) |
- (inb(gmux_data->iostart + 0xcd) << 8);
-
- if (val == 0x55aa)
- return true;
-
- return false;
-}
-
/**
* DOC: Backlight control
*
@@ -605,60 +565,43 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
int ret = -ENXIO;
acpi_status status;
unsigned long long gpe;
+ bool indexed = false;
+ u32 version;
if (apple_gmux_data)
return -EBUSY;
+ if (!apple_gmux_detect(pnp, &indexed)) {
+ pr_info("gmux device not present\n");
+ return -ENODEV;
+ }
+
gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL);
if (!gmux_data)
return -ENOMEM;
pnp_set_drvdata(pnp, gmux_data);
res = pnp_get_resource(pnp, IORESOURCE_IO, 0);
- if (!res) {
- pr_err("Failed to find gmux I/O resource\n");
- goto err_free;
- }
-
gmux_data->iostart = res->start;
gmux_data->iolen = resource_size(res);
- if (gmux_data->iolen < GMUX_MIN_IO_LEN) {
- pr_err("gmux I/O region too small (%lu < %u)\n",
- gmux_data->iolen, GMUX_MIN_IO_LEN);
- goto err_free;
- }
-
if (!request_region(gmux_data->iostart, gmux_data->iolen,
"Apple gmux")) {
pr_err("gmux I/O already in use\n");
goto err_free;
}
- /*
- * Invalid version information may indicate either that the gmux
- * device isn't present or that it's a new one that uses indexed
- * io
- */
-
- ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR);
- ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR);
- ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
- if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
- if (gmux_is_indexed(gmux_data)) {
- u32 version;
- mutex_init(&gmux_data->index_lock);
- gmux_data->indexed = true;
- version = gmux_read32(gmux_data,
- GMUX_PORT_VERSION_MAJOR);
- ver_major = (version >> 24) & 0xff;
- ver_minor = (version >> 16) & 0xff;
- ver_release = (version >> 8) & 0xff;
- } else {
- pr_info("gmux device not present\n");
- ret = -ENODEV;
- goto err_release;
- }
+ if (indexed) {
+ mutex_init(&gmux_data->index_lock);
+ gmux_data->indexed = true;
+ version = gmux_read32(gmux_data, GMUX_PORT_VERSION_MAJOR);
+ ver_major = (version >> 24) & 0xff;
+ ver_minor = (version >> 16) & 0xff;
+ ver_release = (version >> 8) & 0xff;
+ } else {
+ ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR);
+ ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR);
+ ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
}
pr_info("Found gmux version %d.%d.%d [%s]\n", ver_major, ver_minor,
ver_release, (gmux_data->indexed ? "indexed" : "classic"));
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index c685a705b73dd0..cb15acdf14a30a 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -121,6 +121,10 @@ static struct quirk_entry quirk_asus_tablet_mode = {
.tablet_switch_mode = asus_wmi_lid_flip_rog_devid,
};
+static struct quirk_entry quirk_asus_ignore_fan = {
+ .wmi_ignore_fan = true,
+};
+
static int dmi_matched(const struct dmi_system_id *dmi)
{
pr_info("Identified laptop model '%s'\n", dmi->ident);
@@ -473,6 +477,15 @@ static const struct dmi_system_id asus_quirks[] = {
},
.driver_data = &quirk_asus_tablet_mode,
},
+ {
+ .callback = dmi_matched,
+ .ident = "ASUS VivoBook E410MA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "E410MA"),
+ },
+ .driver_data = &quirk_asus_ignore_fan,
+ },
{},
};
@@ -511,6 +524,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x30, { KEY_VOLUMEUP } },
{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
{ KE_KEY, 0x32, { KEY_MUTE } },
+ { KE_KEY, 0x33, { KEY_SCREENLOCK } },
{ KE_KEY, 0x35, { KEY_SCREENLOCK } },
{ KE_KEY, 0x38, { KEY_PROG3 } }, /* Armoury Crate */
{ KE_KEY, 0x40, { KEY_PREVIOUSSONG } },
@@ -544,6 +558,7 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
{ KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
{ KE_KEY, 0x82, { KEY_CAMERA } },
+ { KE_KEY, 0x85, { KEY_CAMERA } },
{ KE_KEY, 0x86, { KEY_PROG1 } }, /* MyASUS Key */
{ KE_KEY, 0x88, { KEY_RFKILL } }, /* Radio Toggle Key */
{ KE_KEY, 0x8A, { KEY_PROG1 } }, /* Color enhancement mode */
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 6f81b2844dcbea..1038dfdcdd3250 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -225,6 +225,7 @@ struct asus_wmi {
int tablet_switch_event_code;
u32 tablet_switch_dev_id;
+ bool tablet_switch_inverted;
enum fan_type fan_type;
enum fan_type gpu_fan_type;
@@ -493,6 +494,13 @@ static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id)
}
/* Input **********************************************************************/
+static void asus_wmi_tablet_sw_report(struct asus_wmi *asus, bool value)
+{
+ input_report_switch(asus->inputdev, SW_TABLET_MODE,
+ asus->tablet_switch_inverted ? !value : value);
+ input_sync(asus->inputdev);
+}
+
static void asus_wmi_tablet_sw_init(struct asus_wmi *asus, u32 dev_id, int event_code)
{
struct device *dev = &asus->platform_device->dev;
@@ -501,7 +509,7 @@ static void asus_wmi_tablet_sw_init(struct asus_wmi *asus, u32 dev_id, int event
result = asus_wmi_get_devstate_simple(asus, dev_id);
if (result >= 0) {
input_set_capability(asus->inputdev, EV_SW, SW_TABLET_MODE);
- input_report_switch(asus->inputdev, SW_TABLET_MODE, result);
+ asus_wmi_tablet_sw_report(asus, result);
asus->tablet_switch_dev_id = dev_id;
asus->tablet_switch_event_code = event_code;
} else if (result == -ENODEV) {
@@ -534,6 +542,7 @@ static int asus_wmi_input_init(struct asus_wmi *asus)
case asus_wmi_no_tablet_switch:
break;
case asus_wmi_kbd_dock_devid:
+ asus->tablet_switch_inverted = true;
asus_wmi_tablet_sw_init(asus, ASUS_WMI_DEVID_KBD_DOCK, NOTIFY_KBD_DOCK_CHANGE);
break;
case asus_wmi_lid_flip_devid:
@@ -573,10 +582,8 @@ static void asus_wmi_tablet_mode_get_state(struct asus_wmi *asus)
return;
result = asus_wmi_get_devstate_simple(asus, asus->tablet_switch_dev_id);
- if (result >= 0) {
- input_report_switch(asus->inputdev, SW_TABLET_MODE, result);
- input_sync(asus->inputdev);
- }
+ if (result >= 0)
+ asus_wmi_tablet_sw_report(asus, result);
}
/* dGPU ********************************************************************/
@@ -2243,7 +2250,9 @@ static int asus_wmi_fan_init(struct asus_wmi *asus)
asus->fan_type = FAN_TYPE_NONE;
asus->agfn_pwm = -1;
- if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CPU_FAN_CTRL))
+ if (asus->driver->quirks->wmi_ignore_fan)
+ asus->fan_type = FAN_TYPE_NONE;
+ else if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_CPU_FAN_CTRL))
asus->fan_type = FAN_TYPE_SPEC83;
else if (asus_wmi_has_agfn_fan(asus))
asus->fan_type = FAN_TYPE_AGFN;
@@ -2436,6 +2445,9 @@ static int fan_curve_check_present(struct asus_wmi *asus, bool *available,
*available = false;
+ if (asus->fan_type == FAN_TYPE_NONE)
+ return 0;
+
err = fan_curve_get_factory_default(asus, fan_dev);
if (err) {
return 0;
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index 65316998b898aa..a478ebfd34dfad 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -38,6 +38,7 @@ struct quirk_entry {
bool store_backlight_power;
bool wmi_backlight_set_devstate;
bool wmi_force_als_set;
+ bool wmi_ignore_fan;
enum asus_wmi_tablet_switch_mode tablet_switch_mode;
int wapf;
/*
diff --git a/drivers/platform/x86/dell/dell-wmi-base.c b/drivers/platform/x86/dell/dell-wmi-base.c
index 0a259a27459f6d..502783a7adb118 100644
--- a/drivers/platform/x86/dell/dell-wmi-base.c
+++ b/drivers/platform/x86/dell/dell-wmi-base.c
@@ -261,6 +261,9 @@ static const struct key_entry dell_wmi_keymap_type_0010[] = {
{ KE_KEY, 0x57, { KEY_BRIGHTNESSDOWN } },
{ KE_KEY, 0x58, { KEY_BRIGHTNESSUP } },
+ /*Speaker Mute*/
+ { KE_KEY, 0x109, { KEY_MUTE} },
+
/* Mic mute */
{ KE_KEY, 0x150, { KEY_MICMUTE } },
diff --git a/drivers/platform/x86/dell/dell-wmi-privacy.c b/drivers/platform/x86/dell/dell-wmi-privacy.c
index c82b3d6867c5b2..c517bd45dd32e4 100644
--- a/drivers/platform/x86/dell/dell-wmi-privacy.c
+++ b/drivers/platform/x86/dell/dell-wmi-privacy.c
@@ -61,7 +61,7 @@ static const struct key_entry dell_wmi_keymap_type_0012[] = {
/* privacy mic mute */
{ KE_KEY, 0x0001, { KEY_MICMUTE } },
/* privacy camera mute */
- { KE_SW, 0x0002, { SW_CAMERA_LENS_COVER } },
+ { KE_VSW, 0x0002, { SW_CAMERA_LENS_COVER } },
{ KE_END, 0},
};
@@ -115,11 +115,15 @@ bool dell_privacy_process_event(int type, int code, int status)
switch (code) {
case DELL_PRIVACY_AUDIO_EVENT: /* Mic mute */
- case DELL_PRIVACY_CAMERA_EVENT: /* Camera mute */
priv->last_status = status;
sparse_keymap_report_entry(priv->input_dev, key, 1, true);
ret = true;
break;
+ case DELL_PRIVACY_CAMERA_EVENT: /* Camera mute */
+ priv->last_status = status;
+ sparse_keymap_report_entry(priv->input_dev, key, !(status & CAMERA_STATUS), false);
+ ret = true;
+ break;
default:
dev_dbg(&priv->wdev->dev, "unknown event type 0x%04x 0x%04x\n", type, code);
}
@@ -292,7 +296,7 @@ static int dell_privacy_wmi_probe(struct wmi_device *wdev, const void *context)
{
struct privacy_wmi_data *priv;
struct key_entry *keymap;
- int ret, i;
+ int ret, i, j;
ret = wmi_has_guid(DELL_PRIVACY_GUID);
if (!ret)
@@ -304,6 +308,11 @@ static int dell_privacy_wmi_probe(struct wmi_device *wdev, const void *context)
dev_set_drvdata(&wdev->dev, priv);
priv->wdev = wdev;
+
+ ret = get_current_status(priv->wdev);
+ if (ret)
+ return ret;
+
/* create evdev passing interface */
priv->input_dev = devm_input_allocate_device(&wdev->dev);
if (!priv->input_dev)
@@ -318,9 +327,20 @@ static int dell_privacy_wmi_probe(struct wmi_device *wdev, const void *context)
/* remap the keymap code with Dell privacy key type 0x12 as prefix
* KEY_MICMUTE scancode will be reported as 0x120001
*/
- for (i = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0012); i++) {
- keymap[i] = dell_wmi_keymap_type_0012[i];
- keymap[i].code |= (0x0012 << 16);
+ for (i = 0, j = 0; i < ARRAY_SIZE(dell_wmi_keymap_type_0012); i++) {
+ /*
+ * Unlike keys where only presses matter, userspace may act
+ * on switches in both of their positions. Only register
+ * SW_CAMERA_LENS_COVER if it is actually there.
+ */
+ if (dell_wmi_keymap_type_0012[i].type == KE_VSW &&
+ dell_wmi_keymap_type_0012[i].sw.code == SW_CAMERA_LENS_COVER &&
+ !(priv->features_present & BIT(DELL_PRIVACY_TYPE_CAMERA)))
+ continue;
+
+ keymap[j] = dell_wmi_keymap_type_0012[i];
+ keymap[j].code |= (0x0012 << 16);
+ j++;
}
ret = sparse_keymap_setup(priv->input_dev, keymap, NULL);
kfree(keymap);
@@ -331,11 +351,12 @@ static int dell_privacy_wmi_probe(struct wmi_device *wdev, const void *context)
priv->input_dev->name = "Dell Privacy Driver";
priv->input_dev->id.bustype = BUS_HOST;
- ret = input_register_device(priv->input_dev);
- if (ret)
- return ret;
+ /* Report initial camera-cover status */
+ if (priv->features_present & BIT(DELL_PRIVACY_TYPE_CAMERA))
+ input_report_switch(priv->input_dev, SW_CAMERA_LENS_COVER,
+ !(priv->last_status & CAMERA_STATUS));
- ret = get_current_status(priv->wdev);
+ ret = input_register_device(priv->input_dev);
if (ret)
return ret;
diff --git a/drivers/platform/x86/gigabyte-wmi.c b/drivers/platform/x86/gigabyte-wmi.c
index 5e7e6659a84971..322cfaeda17bad 100644
--- a/drivers/platform/x86/gigabyte-wmi.c
+++ b/drivers/platform/x86/gigabyte-wmi.c
@@ -141,6 +141,7 @@ static u8 gigabyte_wmi_detect_sensor_usability(struct wmi_device *wdev)
static const struct dmi_system_id gigabyte_wmi_known_working_platforms[] = {
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B450M DS3H-CF"),
+ DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B450M DS3H WIFI-CF"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B450M S2H V2"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 AORUS ELITE AX V2"),
DMI_EXACT_MATCH_GIGABYTE_BOARD_NAME("B550 AORUS ELITE"),
diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c
index 0a99058be81304..2ef201b625b38c 100644
--- a/drivers/platform/x86/hp/hp-wmi.c
+++ b/drivers/platform/x86/hp/hp-wmi.c
@@ -90,6 +90,7 @@ enum hp_wmi_event_ids {
HPWMI_PEAKSHIFT_PERIOD = 0x0F,
HPWMI_BATTERY_CHARGE_PERIOD = 0x10,
HPWMI_SANITIZATION_MODE = 0x17,
+ HPWMI_OMEN_KEY = 0x1D,
HPWMI_SMART_EXPERIENCE_APP = 0x21,
};
@@ -216,6 +217,8 @@ static const struct key_entry hp_wmi_keymap[] = {
{ KE_KEY, 0x213b, { KEY_INFO } },
{ KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } },
{ KE_KEY, 0x216a, { KEY_SETUP } },
+ { KE_KEY, 0x21a5, { KEY_PROG2 } }, /* HP Omen Key */
+ { KE_KEY, 0x21a7, { KEY_FN_ESC } },
{ KE_KEY, 0x21a9, { KEY_TOUCHPAD_OFF } },
{ KE_KEY, 0x121a9, { KEY_TOUCHPAD_ON } },
{ KE_KEY, 0x231b, { KEY_HELP } },
@@ -548,7 +551,7 @@ static int __init hp_wmi_enable_hotkeys(void)
static int hp_wmi_set_block(void *data, bool blocked)
{
- enum hp_wmi_radio r = (enum hp_wmi_radio) data;
+ enum hp_wmi_radio r = (long)data;
int query = BIT(r + 8) | ((!blocked) << r);
int ret;
@@ -810,6 +813,7 @@ static void hp_wmi_notify(u32 value, void *context)
case HPWMI_SMART_ADAPTER:
break;
case HPWMI_BEZEL_BUTTON:
+ case HPWMI_OMEN_KEY:
key_code = hp_wmi_read_int(HPWMI_HOTKEY_QUERY);
if (key_code < 0)
break;
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index 435d2d3d903b07..0eb5bfdd823a1d 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -1621,6 +1621,12 @@ static const struct dmi_system_id set_fn_lock_led_list[] = {
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Legion R7000P2020H"),
}
},
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Legion 5 15ARH05"),
+ }
+ },
{}
};
diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c
index b2342b3d78c723..74dc2cff799eec 100644
--- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c
+++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c
@@ -181,6 +181,9 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472,
return PTR_ERR(int3472->regulator.gpio);
}
+ /* Ensure the pin is in output mode and non-active state */
+ gpiod_direction_output(int3472->regulator.gpio, 0);
+
cfg.dev = &int3472->adev->dev;
cfg.init_data = &init_data;
cfg.ena_gpiod = int3472->regulator.gpio;
diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
index 974a132db65164..c42c3faa2c32da 100644
--- a/drivers/platform/x86/intel/int3472/discrete.c
+++ b/drivers/platform/x86/intel/int3472/discrete.c
@@ -168,6 +168,8 @@ static int skl_int3472_map_gpio_to_clk(struct int3472_discrete_device *int3472,
return (PTR_ERR(gpio));
int3472->clock.ena_gpio = gpio;
+ /* Ensure the pin is in output mode and non-active state */
+ gpiod_direction_output(int3472->clock.ena_gpio, 0);
break;
case INT3472_GPIO_TYPE_PRIVACY_LED:
gpio = acpi_get_and_request_gpiod(path, pin, "int3472,privacy-led");
@@ -175,6 +177,8 @@ static int skl_int3472_map_gpio_to_clk(struct int3472_discrete_device *int3472,
return (PTR_ERR(gpio));
int3472->clock.led_gpio = gpio;
+ /* Ensure the pin is in output mode and non-active state */
+ gpiod_direction_output(int3472->clock.led_gpio, 0);
break;
default:
dev_err(int3472->dev, "Invalid GPIO type 0x%02x for clock\n", type);
diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c
index f1d802f6ec3f91..3a15d32d7644c0 100644
--- a/drivers/platform/x86/intel/pmc/core.c
+++ b/drivers/platform/x86/intel/pmc/core.c
@@ -1029,6 +1029,7 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = {
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE, adl_core_init),
X86_MATCH_INTEL_FAM6_MODEL(RAPTORLAKE_S, adl_core_init),
X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE, mtl_core_init),
+ X86_MATCH_INTEL_FAM6_MODEL(METEORLAKE_L, mtl_core_init),
{}
};
diff --git a/drivers/platform/x86/simatic-ipc.c b/drivers/platform/x86/simatic-ipc.c
index ca76076fc706a6..b3622419cd1a48 100644
--- a/drivers/platform/x86/simatic-ipc.c
+++ b/drivers/platform/x86/simatic-ipc.c
@@ -46,7 +46,8 @@ static struct {
{SIMATIC_IPC_IPC427D, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_NONE},
{SIMATIC_IPC_IPC427E, SIMATIC_IPC_DEVICE_427E, SIMATIC_IPC_DEVICE_427E},
{SIMATIC_IPC_IPC477E, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_427E},
- {SIMATIC_IPC_IPC427G, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
+ {SIMATIC_IPC_IPCBX_39A, SIMATIC_IPC_DEVICE_227G, SIMATIC_IPC_DEVICE_227G},
+ {SIMATIC_IPC_IPCPX_39A, SIMATIC_IPC_DEVICE_NONE, SIMATIC_IPC_DEVICE_227G},
};
static int register_platform_devices(u32 station_id)
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index 7156ae2ad1962a..537d6a2d0781b7 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -1887,14 +1887,21 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
break;
}
- ret = sony_call_snc_handle(handle, probe_base, &result);
- if (ret)
- return ret;
+ /*
+ * Only probe if there is a separate probe_base, otherwise the probe call
+ * is equivalent to __sony_nc_kbd_backlight_mode_set(0), resulting in
+ * the keyboard backlight being turned off.
+ */
+ if (probe_base) {
+ ret = sony_call_snc_handle(handle, probe_base, &result);
+ if (ret)
+ return ret;
- if ((handle == 0x0137 && !(result & 0x02)) ||
- !(result & 0x01)) {
- dprintk("no backlight keyboard found\n");
- return 0;
+ if ((handle == 0x0137 && !(result & 0x02)) ||
+ !(result & 0x01)) {
+ dprintk("no backlight keyboard found\n");
+ return 0;
+ }
}
kbdbl_ctl = kzalloc(sizeof(*kbdbl_ctl), GFP_KERNEL);
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 1195293b22fddf..32c10457399e44 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -5563,7 +5563,7 @@ static int light_sysfs_set(struct led_classdev *led_cdev,
static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev)
{
- return (light_get_status() == 1) ? LED_FULL : LED_OFF;
+ return (light_get_status() == 1) ? LED_ON : LED_OFF;
}
static struct tpacpi_led_classdev tpacpi_led_thinklight = {
@@ -10311,9 +10311,11 @@ static DEFINE_MUTEX(dytc_mutex);
static int dytc_capabilities;
static bool dytc_mmc_get_available;
-static int convert_dytc_to_profile(int dytcmode, enum platform_profile_option *profile)
+static int convert_dytc_to_profile(int funcmode, int dytcmode,
+ enum platform_profile_option *profile)
{
- if (dytc_capabilities & BIT(DYTC_FC_MMC)) {
+ switch (funcmode) {
+ case DYTC_FUNCTION_MMC:
switch (dytcmode) {
case DYTC_MODE_MMC_LOWPOWER:
*profile = PLATFORM_PROFILE_LOW_POWER;
@@ -10329,8 +10331,7 @@ static int convert_dytc_to_profile(int dytcmode, enum platform_profile_option *p
return -EINVAL;
}
return 0;
- }
- if (dytc_capabilities & BIT(DYTC_FC_PSC)) {
+ case DYTC_FUNCTION_PSC:
switch (dytcmode) {
case DYTC_MODE_PSC_LOWPOWER:
*profile = PLATFORM_PROFILE_LOW_POWER;
@@ -10344,6 +10345,14 @@ static int convert_dytc_to_profile(int dytcmode, enum platform_profile_option *p
default: /* Unknown mode */
return -EINVAL;
}
+ return 0;
+ case DYTC_FUNCTION_AMT:
+ /* For now return balanced. It's the closest we have to 'auto' */
+ *profile = PLATFORM_PROFILE_BALANCED;
+ return 0;
+ default:
+ /* Unknown function */
+ return -EOPNOTSUPP;
}
return 0;
}
@@ -10487,11 +10496,11 @@ static int dytc_profile_set(struct platform_profile_handler *pprof,
if (err)
goto unlock;
}
- }
- if (dytc_capabilities & BIT(DYTC_FC_PSC)) {
+ } else if (dytc_capabilities & BIT(DYTC_FC_PSC)) {
err = dytc_command(DYTC_SET_COMMAND(DYTC_FUNCTION_PSC, perfmode, 1), &output);
if (err)
goto unlock;
+
/* system supports AMT, activate it when on balanced */
if (dytc_capabilities & BIT(DYTC_FC_AMT))
dytc_control_amt(profile == PLATFORM_PROFILE_BALANCED);
@@ -10507,7 +10516,7 @@ static void dytc_profile_refresh(void)
{
enum platform_profile_option profile;
int output, err = 0;
- int perfmode;
+ int perfmode, funcmode;
mutex_lock(&dytc_mutex);
if (dytc_capabilities & BIT(DYTC_FC_MMC)) {
@@ -10515,15 +10524,18 @@ static void dytc_profile_refresh(void)
err = dytc_command(DYTC_CMD_MMC_GET, &output);
else
err = dytc_cql_command(DYTC_CMD_GET, &output);
- } else if (dytc_capabilities & BIT(DYTC_FC_PSC))
+ funcmode = DYTC_FUNCTION_MMC;
+ } else if (dytc_capabilities & BIT(DYTC_FC_PSC)) {
err = dytc_command(DYTC_CMD_GET, &output);
-
+ /* Check if we are PSC mode, or have AMT enabled */
+ funcmode = (output >> DYTC_GET_FUNCTION_BIT) & 0xF;
+ }
mutex_unlock(&dytc_mutex);
if (err)
return;
perfmode = (output >> DYTC_GET_MODE_BIT) & 0xF;
- convert_dytc_to_profile(perfmode, &profile);
+ convert_dytc_to_profile(funcmode, perfmode, &profile);
if (profile != dytc_current_profile) {
dytc_current_profile = profile;
platform_profile_notify();
diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c
index baae3120efd052..13802a3c3591d8 100644
--- a/drivers/platform/x86/touchscreen_dmi.c
+++ b/drivers/platform/x86/touchscreen_dmi.c
@@ -264,6 +264,23 @@ static const struct ts_dmi_data connect_tablet9_data = {
.properties = connect_tablet9_props,
};
+static const struct property_entry csl_panther_tab_hd_props[] = {
+ PROPERTY_ENTRY_U32("touchscreen-min-x", 1),
+ PROPERTY_ENTRY_U32("touchscreen-min-y", 20),
+ PROPERTY_ENTRY_U32("touchscreen-size-x", 1980),
+ PROPERTY_ENTRY_U32("touchscreen-size-y", 1526),
+ PROPERTY_ENTRY_BOOL("touchscreen-inverted-y"),
+ PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
+ PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-csl-panther-tab-hd.fw"),
+ PROPERTY_ENTRY_U32("silead,max-fingers", 10),
+ { }
+};
+
+static const struct ts_dmi_data csl_panther_tab_hd_data = {
+ .acpi_name = "MSSL1680:00",
+ .properties = csl_panther_tab_hd_props,
+};
+
static const struct property_entry cube_iwork8_air_props[] = {
PROPERTY_ENTRY_U32("touchscreen-min-x", 1),
PROPERTY_ENTRY_U32("touchscreen-min-y", 3),
@@ -1081,6 +1098,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
},
},
{
+ /* Chuwi Vi8 (CWI501) */
+ .driver_data = (void *)&chuwi_vi8_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "i86"),
+ DMI_MATCH(DMI_BIOS_VERSION, "CHUWI.W86JLBNR01"),
+ },
+ },
+ {
/* Chuwi Vi8 (CWI506) */
.driver_data = (void *)&chuwi_vi8_data,
.matches = {
@@ -1125,6 +1151,14 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
},
},
{
+ /* CSL Panther Tab HD */
+ .driver_data = (void *)&csl_panther_tab_hd_data,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "CSL Computer GmbH & Co. KG"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CSL Panther Tab HD"),
+ },
+ },
+ {
/* CUBE iwork8 Air */
.driver_data = (void *)&cube_iwork8_air_data,
.matches = {
diff --git a/drivers/regulator/da9211-regulator.c b/drivers/regulator/da9211-regulator.c
index e01b32d1fa17d4..00828f5baa972a 100644
--- a/drivers/regulator/da9211-regulator.c
+++ b/drivers/regulator/da9211-regulator.c
@@ -498,6 +498,12 @@ static int da9211_i2c_probe(struct i2c_client *i2c)
chip->chip_irq = i2c->irq;
+ ret = da9211_regulator_init(chip);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to initialize regulator: %d\n", ret);
+ return ret;
+ }
+
if (chip->chip_irq != 0) {
ret = devm_request_threaded_irq(chip->dev, chip->chip_irq, NULL,
da9211_irq_handler,
@@ -512,11 +518,6 @@ static int da9211_i2c_probe(struct i2c_client *i2c)
dev_warn(chip->dev, "No IRQ configured\n");
}
- ret = da9211_regulator_init(chip);
-
- if (ret < 0)
- dev_err(chip->dev, "Failed to initialize regulator: %d\n", ret);
-
return ret;
}
diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c
index 43b5b93777149b..ae6021390143c6 100644
--- a/drivers/regulator/qcom-rpmh-regulator.c
+++ b/drivers/regulator/qcom-rpmh-regulator.c
@@ -1016,7 +1016,7 @@ static const struct rpmh_vreg_init_data pm8550_vreg_data[] = {
RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo_lv, "vdd-l8-l9"),
RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l8-l9"),
RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l1-l4-l10"),
- RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l11"),
+ RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo, "vdd-l11"),
RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo, "vdd-l12"),
RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l2-l13-l14"),
RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo, "vdd-l2-l13-l14"),
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index de176c2fbad96d..2a52c990d4fecb 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -257,7 +257,7 @@ config RESET_SUNXI
config RESET_TI_SCI
tristate "TI System Control Interface (TI-SCI) reset driver"
- depends on TI_SCI_PROTOCOL || COMPILE_TEST
+ depends on TI_SCI_PROTOCOL || (COMPILE_TEST && TI_SCI_PROTOCOL=n)
help
This enables the reset driver support over TI System Control Interface
available on some new TI's SoCs. If you wish to use reset resources
diff --git a/drivers/reset/reset-uniphier-glue.c b/drivers/reset/reset-uniphier-glue.c
index 146fd5d45e99d3..15abac9fc72c07 100644
--- a/drivers/reset/reset-uniphier-glue.c
+++ b/drivers/reset/reset-uniphier-glue.c
@@ -47,7 +47,6 @@ static int uniphier_glue_reset_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct uniphier_glue_reset_priv *priv;
struct resource *res;
- resource_size_t size;
int i, ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -60,7 +59,6 @@ static int uniphier_glue_reset_probe(struct platform_device *pdev)
return -EINVAL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- size = resource_size(res);
priv->rdata.membase = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->rdata.membase))
return PTR_ERR(priv->rdata.membase);
@@ -96,7 +94,7 @@ static int uniphier_glue_reset_probe(struct platform_device *pdev)
spin_lock_init(&priv->rdata.lock);
priv->rdata.rcdev.owner = THIS_MODULE;
- priv->rdata.rcdev.nr_resets = size * BITS_PER_BYTE;
+ priv->rdata.rcdev.nr_resets = resource_size(res) * BITS_PER_BYTE;
priv->rdata.rcdev.ops = &reset_simple_ops;
priv->rdata.rcdev.of_node = dev->of_node;
priv->rdata.active_low = true;
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
index e991cccdb6e9ce..1e8bc6cc1e12d8 100644
--- a/drivers/rtc/rtc-efi.c
+++ b/drivers/rtc/rtc-efi.c
@@ -188,9 +188,10 @@ static int efi_set_time(struct device *dev, struct rtc_time *tm)
static int efi_procfs(struct device *dev, struct seq_file *seq)
{
- efi_time_t eft, alm;
- efi_time_cap_t cap;
- efi_bool_t enabled, pending;
+ efi_time_t eft, alm;
+ efi_time_cap_t cap;
+ efi_bool_t enabled, pending;
+ struct rtc_device *rtc = dev_get_drvdata(dev);
memset(&eft, 0, sizeof(eft));
memset(&alm, 0, sizeof(alm));
@@ -213,23 +214,25 @@ static int efi_procfs(struct device *dev, struct seq_file *seq)
/* XXX fixme: convert to string? */
seq_printf(seq, "Timezone\t: %u\n", eft.timezone);
- seq_printf(seq,
- "Alarm Time\t: %u:%u:%u.%09u\n"
- "Alarm Date\t: %u-%u-%u\n"
- "Alarm Daylight\t: %u\n"
- "Enabled\t\t: %s\n"
- "Pending\t\t: %s\n",
- alm.hour, alm.minute, alm.second, alm.nanosecond,
- alm.year, alm.month, alm.day,
- alm.daylight,
- enabled == 1 ? "yes" : "no",
- pending == 1 ? "yes" : "no");
-
- if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE)
- seq_puts(seq, "Timezone\t: unspecified\n");
- else
- /* XXX fixme: convert to string? */
- seq_printf(seq, "Timezone\t: %u\n", alm.timezone);
+ if (test_bit(RTC_FEATURE_ALARM, rtc->features)) {
+ seq_printf(seq,
+ "Alarm Time\t: %u:%u:%u.%09u\n"
+ "Alarm Date\t: %u-%u-%u\n"
+ "Alarm Daylight\t: %u\n"
+ "Enabled\t\t: %s\n"
+ "Pending\t\t: %s\n",
+ alm.hour, alm.minute, alm.second, alm.nanosecond,
+ alm.year, alm.month, alm.day,
+ alm.daylight,
+ enabled == 1 ? "yes" : "no",
+ pending == 1 ? "yes" : "no");
+
+ if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE)
+ seq_puts(seq, "Timezone\t: unspecified\n");
+ else
+ /* XXX fixme: convert to string? */
+ seq_printf(seq, "Timezone\t: %u\n", alm.timezone);
+ }
/*
* now prints the capabilities
@@ -269,7 +272,10 @@ static int __init efi_rtc_probe(struct platform_device *dev)
rtc->ops = &efi_rtc_ops;
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
- set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY, rtc->features);
+ if (efi_rt_services_supported(EFI_RT_SUPPORTED_WAKEUP_SERVICES))
+ set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY, rtc->features);
+ else
+ clear_bit(RTC_FEATURE_ALARM, rtc->features);
device_init_wakeup(&dev->dev, true);
diff --git a/drivers/rtc/rtc-sunplus.c b/drivers/rtc/rtc-sunplus.c
index e8e2ab1103fc04..4b578e4d44f6ae 100644
--- a/drivers/rtc/rtc-sunplus.c
+++ b/drivers/rtc/rtc-sunplus.c
@@ -240,8 +240,8 @@ static int sp_rtc_probe(struct platform_device *plat_dev)
if (IS_ERR(sp_rtc->reg_base))
return dev_err_probe(&plat_dev->dev, PTR_ERR(sp_rtc->reg_base),
"%s devm_ioremap_resource fail\n", RTC_REG_NAME);
- dev_dbg(&plat_dev->dev, "res = 0x%x, reg_base = 0x%lx\n",
- sp_rtc->res->start, (unsigned long)sp_rtc->reg_base);
+ dev_dbg(&plat_dev->dev, "res = %pR, reg_base = %p\n",
+ sp_rtc->res, sp_rtc->reg_base);
sp_rtc->irq = platform_get_irq(plat_dev, 0);
if (sp_rtc->irq < 0)
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index b392b9f5482e0e..c0f85ffb2b62de 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -865,6 +865,8 @@ dcssblk_submit_bio(struct bio *bio)
unsigned long bytes_done;
bio = bio_split_to_limits(bio);
+ if (!bio)
+ return;
bytes_done = 0;
dev_info = bio->bi_bdev->bd_disk->private_data;
diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c
index 406be169173ce9..d1adc4b8319365 100644
--- a/drivers/s390/net/qeth_core_sys.c
+++ b/drivers/s390/net/qeth_core_sys.c
@@ -410,13 +410,13 @@ static ssize_t qeth_dev_isolation_show(struct device *dev,
switch (card->options.isolation) {
case ISOLATION_MODE_NONE:
- return snprintf(buf, 6, "%s\n", ATTR_QETH_ISOLATION_NONE);
+ return sysfs_emit(buf, "%s\n", ATTR_QETH_ISOLATION_NONE);
case ISOLATION_MODE_FWD:
- return snprintf(buf, 9, "%s\n", ATTR_QETH_ISOLATION_FWD);
+ return sysfs_emit(buf, "%s\n", ATTR_QETH_ISOLATION_FWD);
case ISOLATION_MODE_DROP:
- return snprintf(buf, 6, "%s\n", ATTR_QETH_ISOLATION_DROP);
+ return sysfs_emit(buf, "%s\n", ATTR_QETH_ISOLATION_DROP);
default:
- return snprintf(buf, 5, "%s\n", "N/A");
+ return sysfs_emit(buf, "%s\n", "N/A");
}
}
@@ -500,9 +500,9 @@ static ssize_t qeth_hw_trap_show(struct device *dev,
struct qeth_card *card = dev_get_drvdata(dev);
if (card->info.hwtrap)
- return snprintf(buf, 5, "arm\n");
+ return sysfs_emit(buf, "arm\n");
else
- return snprintf(buf, 8, "disarm\n");
+ return sysfs_emit(buf, "disarm\n");
}
static ssize_t qeth_hw_trap_store(struct device *dev,
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index 49cc18a8747313..29a2865b8e2e14 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -981,6 +981,9 @@ queue_rtpg:
*
* Returns true if and only if alua_rtpg_work() will be called asynchronously.
* That function is responsible for calling @qdata->fn().
+ *
+ * Context: may be called from atomic context (alua_check()) only if the caller
+ * holds an sdev reference.
*/
static bool alua_rtpg_queue(struct alua_port_group *pg,
struct scsi_device *sdev,
@@ -989,8 +992,6 @@ static bool alua_rtpg_queue(struct alua_port_group *pg,
int start_queue = 0;
unsigned long flags;
- might_sleep();
-
if (WARN_ON_ONCE(!pg) || scsi_device_get(sdev))
return false;
diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c
index 41ba22f6c7f051..8c038ccf1c0954 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_main.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_main.c
@@ -162,7 +162,7 @@ static void hisi_sas_slot_index_clear(struct hisi_hba *hisi_hba, int slot_idx)
static void hisi_sas_slot_index_free(struct hisi_hba *hisi_hba, int slot_idx)
{
if (hisi_hba->hw->slot_index_alloc ||
- slot_idx >= HISI_SAS_UNRESERVED_IPTT) {
+ slot_idx < HISI_SAS_RESERVED_IPTT) {
spin_lock(&hisi_hba->lock);
hisi_sas_slot_index_clear(hisi_hba, slot_idx);
spin_unlock(&hisi_hba->lock);
@@ -704,7 +704,7 @@ static int hisi_sas_init_device(struct domain_device *device)
int_to_scsilun(0, &lun);
while (retry-- > 0) {
- rc = sas_clear_task_set(device, lun.scsi_lun);
+ rc = sas_abort_task_set(device, lun.scsi_lun);
if (rc == TMF_RESP_FUNC_COMPLETE) {
hisi_sas_release_task(hisi_hba, device);
break;
@@ -1316,7 +1316,7 @@ static void hisi_sas_refresh_port_id(struct hisi_hba *hisi_hba)
device->linkrate = phy->sas_phy.linkrate;
hisi_hba->hw->setup_itct(hisi_hba, sas_dev);
- } else
+ } else if (!port->port_attached)
port->id = 0xff;
}
}
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 4dbf51e2623ad5..f6da34850af9dd 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -5850,7 +5850,7 @@ static int hpsa_scsi_host_alloc(struct ctlr_info *h)
{
struct Scsi_Host *sh;
- sh = scsi_host_alloc(&hpsa_driver_template, sizeof(h));
+ sh = scsi_host_alloc(&hpsa_driver_template, sizeof(struct ctlr_info));
if (sh == NULL) {
dev_err(&h->pdev->dev, "scsi_host_alloc failed\n");
return -ENOMEM;
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index 1d1cf641937c14..0454d94e8cf0d4 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -849,7 +849,7 @@ static int iscsi_sw_tcp_host_get_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf)
{
struct iscsi_sw_tcp_host *tcp_sw_host = iscsi_host_priv(shost);
- struct iscsi_session *session = tcp_sw_host->session;
+ struct iscsi_session *session;
struct iscsi_conn *conn;
struct iscsi_tcp_conn *tcp_conn;
struct iscsi_sw_tcp_conn *tcp_sw_conn;
@@ -859,6 +859,7 @@ static int iscsi_sw_tcp_host_get_param(struct Scsi_Host *shost,
switch (param) {
case ISCSI_HOST_PARAM_IPADDRESS:
+ session = tcp_sw_host->session;
if (!session)
return -ENOTCONN;
@@ -959,11 +960,13 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
if (!cls_session)
goto remove_host;
session = cls_session->dd_data;
- tcp_sw_host = iscsi_host_priv(shost);
- tcp_sw_host->session = session;
if (iscsi_tcp_r2tpool_alloc(session))
goto remove_session;
+
+ /* We are now fully setup so expose the session to sysfs. */
+ tcp_sw_host = iscsi_host_priv(shost);
+ tcp_sw_host->session = session;
return cls_session;
remove_session:
@@ -983,10 +986,17 @@ static void iscsi_sw_tcp_session_destroy(struct iscsi_cls_session *cls_session)
if (WARN_ON_ONCE(session->leadconn))
return;
+ iscsi_session_remove(cls_session);
+ /*
+ * Our get_host_param needs to access the session, so remove the
+ * host from sysfs before freeing the session to make sure userspace
+ * is no longer accessing the callout.
+ */
+ iscsi_host_remove(shost, false);
+
iscsi_tcp_r2tpool_free(cls_session->dd_data);
- iscsi_session_teardown(cls_session);
- iscsi_host_remove(shost, false);
+ iscsi_session_free(cls_session);
iscsi_host_free(shost);
}
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index ef2fc860257e6e..127f3d7f19dc69 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -3104,17 +3104,32 @@ dec_session_count:
}
EXPORT_SYMBOL_GPL(iscsi_session_setup);
-/**
- * iscsi_session_teardown - destroy session, host, and cls_session
- * @cls_session: iscsi session
+/*
+ * issi_session_remove - Remove session from iSCSI class.
*/
-void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
+void iscsi_session_remove(struct iscsi_cls_session *cls_session)
{
struct iscsi_session *session = cls_session->dd_data;
- struct module *owner = cls_session->transport->owner;
struct Scsi_Host *shost = session->host;
iscsi_remove_session(cls_session);
+ /*
+ * host removal only has to wait for its children to be removed from
+ * sysfs, and iscsi_tcp needs to do iscsi_host_remove before freeing
+ * the session, so drop the session count here.
+ */
+ iscsi_host_dec_session_cnt(shost);
+}
+EXPORT_SYMBOL_GPL(iscsi_session_remove);
+
+/**
+ * iscsi_session_free - Free iscsi session and it's resources
+ * @cls_session: iscsi session
+ */
+void iscsi_session_free(struct iscsi_cls_session *cls_session)
+{
+ struct iscsi_session *session = cls_session->dd_data;
+ struct module *owner = cls_session->transport->owner;
iscsi_pool_free(&session->cmdpool);
kfree(session->password);
@@ -3132,10 +3147,19 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
kfree(session->discovery_parent_type);
iscsi_free_session(cls_session);
-
- iscsi_host_dec_session_cnt(shost);
module_put(owner);
}
+EXPORT_SYMBOL_GPL(iscsi_session_free);
+
+/**
+ * iscsi_session_teardown - destroy session and cls_session
+ * @cls_session: iscsi session
+ */
+void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
+{
+ iscsi_session_remove(cls_session);
+ iscsi_session_free(cls_session);
+}
EXPORT_SYMBOL_GPL(iscsi_session_teardown);
/**
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
index 1ccce706167a5b..5e80225b530822 100644
--- a/drivers/scsi/libsas/sas_ata.c
+++ b/drivers/scsi/libsas/sas_ata.c
@@ -889,7 +889,9 @@ void sas_ata_device_link_abort(struct domain_device *device, bool force_reset)
{
struct ata_port *ap = device->sata_dev.ap;
struct ata_link *link = &ap->link;
+ unsigned long flags;
+ spin_lock_irqsave(ap->lock, flags);
device->sata_dev.fis[2] = ATA_ERR | ATA_DRDY; /* tf status */
device->sata_dev.fis[3] = ATA_ABORTED; /* tf error */
@@ -897,6 +899,7 @@ void sas_ata_device_link_abort(struct domain_device *device, bool force_reset)
if (force_reset)
link->eh_info.action |= ATA_EH_RESET;
ata_link_abort(link);
+ spin_unlock_irqrestore(ap->lock, flags);
}
EXPORT_SYMBOL_GPL(sas_ata_device_link_abort);
diff --git a/drivers/scsi/mpi3mr/Makefile b/drivers/scsi/mpi3mr/Makefile
index ef86ca46646b8f..3bf8cf34e1c3f9 100644
--- a/drivers/scsi/mpi3mr/Makefile
+++ b/drivers/scsi/mpi3mr/Makefile
@@ -1,5 +1,5 @@
# mpi3mr makefile
-obj-m += mpi3mr.o
+obj-$(CONFIG_SCSI_MPI3MR) += mpi3mr.o
mpi3mr-y += mpi3mr_os.o \
mpi3mr_fw.o \
mpi3mr_app.o \
diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c
index 0c4aabaefdcc4c..286a44506578b4 100644
--- a/drivers/scsi/mpi3mr/mpi3mr_fw.c
+++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c
@@ -3633,8 +3633,7 @@ int mpi3mr_setup_resources(struct mpi3mr_ioc *mrioc)
int i, retval = 0, capb = 0;
u16 message_control;
u64 dma_mask = mrioc->dma_mask ? mrioc->dma_mask :
- (((dma_get_required_mask(&pdev->dev) > DMA_BIT_MASK(32)) &&
- (sizeof(dma_addr_t) > 4)) ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32));
+ ((sizeof(dma_addr_t) > 4) ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32));
if (pci_enable_device_mem(pdev)) {
ioc_err(mrioc, "pci_enable_device_mem: failed\n");
diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c
index 4e981ccaac4163..69061545d9d2f1 100644
--- a/drivers/scsi/mpt3sas/mpt3sas_base.c
+++ b/drivers/scsi/mpt3sas/mpt3sas_base.c
@@ -2992,8 +2992,7 @@ _base_config_dma_addressing(struct MPT3SAS_ADAPTER *ioc, struct pci_dev *pdev)
struct sysinfo s;
u64 coherent_dma_mask, dma_mask;
- if (ioc->is_mcpu_endpoint || sizeof(dma_addr_t) == 4 ||
- dma_get_required_mask(&pdev->dev) <= DMA_BIT_MASK(32)) {
+ if (ioc->is_mcpu_endpoint || sizeof(dma_addr_t) == 4) {
ioc->dma_mask = 32;
coherent_dma_mask = dma_mask = DMA_BIT_MASK(32);
/* Set 63 bit DMA mask for all SAS3 and SAS35 controllers */
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 1426b9b03612f9..9feb0323bc44a5 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -588,8 +588,6 @@ void scsi_device_put(struct scsi_device *sdev)
{
struct module *mod = sdev->host->hostt->module;
- might_sleep();
-
put_device(&sdev->sdev_gendev);
module_put(mod);
}
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index cc6953809a248a..8553277effb3d8 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -1511,8 +1511,6 @@ static int inquiry_vpd_b0(unsigned char *arr)
put_unaligned_be64(sdebug_write_same_length, &arr[32]);
return 0x3c; /* Mandatory page length for Logical Block Provisioning */
-
- return sizeof(vpdb0_data);
}
/* Block device characteristics VPD page (SBC-3) */
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index a7960ad2d386a4..2aa2c2aee6e7f9 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -231,6 +231,11 @@ scsi_abort_command(struct scsi_cmnd *scmd)
struct Scsi_Host *shost = sdev->host;
unsigned long flags;
+ if (!shost->hostt->eh_abort_handler) {
+ /* No abort handler, fail command directly */
+ return FAILED;
+ }
+
if (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) {
/*
* Retry after abort failed, escalate to next level.
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 7a6904a3928e6f..f9b18fdc7b3c8e 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -1232,8 +1232,7 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget,
* that no LUN is present, so don't add sdev in these cases.
* Two specific examples are:
* 1) NetApp targets: return PQ=1, PDT=0x1f
- * 2) IBM/2145 targets: return PQ=1, PDT=0
- * 3) USB UFI: returns PDT=0x1f, with the PQ bits being "reserved"
+ * 2) USB UFI: returns PDT=0x1f, with the PQ bits being "reserved"
* in the UFI 1.0 spec (we cannot rely on reserved bits).
*
* References:
@@ -1247,8 +1246,8 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget,
* PDT=00h Direct-access device (floppy)
* PDT=1Fh none (no FDD connected to the requested logical unit)
*/
- if (((result[0] >> 5) == 1 ||
- (starget->pdt_1f_for_no_lun && (result[0] & 0x1f) == 0x1f)) &&
+ if (((result[0] >> 5) == 1 || starget->pdt_1f_for_no_lun) &&
+ (result[0] & 0x1f) == 0x1f &&
!scsi_is_wlun(lun)) {
SCSI_LOG_SCAN_BUS(3, sdev_printk(KERN_INFO, sdev,
"scsi scan: peripheral device type"
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 981d1bab212071..8ef9a54943403a 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -451,6 +451,8 @@ static void scsi_device_dev_release(struct device *dev)
struct scsi_vpd *vpd_pgb0 = NULL, *vpd_pgb1 = NULL, *vpd_pgb2 = NULL;
unsigned long flags;
+ might_sleep();
+
scsi_dh_release_device(sdev);
parent = sdev->sdev_gendev.parent;
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index 13cfd3e317cc06..b9b97300e3b3ca 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -1677,6 +1677,13 @@ static const char *iscsi_session_state_name(int state)
return name;
}
+static char *iscsi_session_target_state_name[] = {
+ [ISCSI_SESSION_TARGET_UNBOUND] = "UNBOUND",
+ [ISCSI_SESSION_TARGET_ALLOCATED] = "ALLOCATED",
+ [ISCSI_SESSION_TARGET_SCANNED] = "SCANNED",
+ [ISCSI_SESSION_TARGET_UNBINDING] = "UNBINDING",
+};
+
int iscsi_session_chkready(struct iscsi_cls_session *session)
{
int err;
@@ -1786,9 +1793,13 @@ static int iscsi_user_scan_session(struct device *dev, void *data)
if ((scan_data->channel == SCAN_WILD_CARD ||
scan_data->channel == 0) &&
(scan_data->id == SCAN_WILD_CARD ||
- scan_data->id == id))
+ scan_data->id == id)) {
scsi_scan_target(&session->dev, 0, id,
scan_data->lun, scan_data->rescan);
+ spin_lock_irqsave(&session->lock, flags);
+ session->target_state = ISCSI_SESSION_TARGET_SCANNED;
+ spin_unlock_irqrestore(&session->lock, flags);
+ }
}
user_scan_exit:
@@ -1961,31 +1972,41 @@ static void __iscsi_unbind_session(struct work_struct *work)
struct iscsi_cls_host *ihost = shost->shost_data;
unsigned long flags;
unsigned int target_id;
+ bool remove_target = true;
ISCSI_DBG_TRANS_SESSION(session, "Unbinding session\n");
/* Prevent new scans and make sure scanning is not in progress */
mutex_lock(&ihost->mutex);
spin_lock_irqsave(&session->lock, flags);
- if (session->target_id == ISCSI_MAX_TARGET) {
+ if (session->target_state == ISCSI_SESSION_TARGET_ALLOCATED) {
+ remove_target = false;
+ } else if (session->target_state != ISCSI_SESSION_TARGET_SCANNED) {
spin_unlock_irqrestore(&session->lock, flags);
mutex_unlock(&ihost->mutex);
- goto unbind_session_exit;
+ ISCSI_DBG_TRANS_SESSION(session,
+ "Skipping target unbinding: Session is unbound/unbinding.\n");
+ return;
}
+ session->target_state = ISCSI_SESSION_TARGET_UNBINDING;
target_id = session->target_id;
session->target_id = ISCSI_MAX_TARGET;
spin_unlock_irqrestore(&session->lock, flags);
mutex_unlock(&ihost->mutex);
- scsi_remove_target(&session->dev);
+ if (remove_target)
+ scsi_remove_target(&session->dev);
if (session->ida_used)
ida_free(&iscsi_sess_ida, target_id);
-unbind_session_exit:
iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION);
ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n");
+
+ spin_lock_irqsave(&session->lock, flags);
+ session->target_state = ISCSI_SESSION_TARGET_UNBOUND;
+ spin_unlock_irqrestore(&session->lock, flags);
}
static void __iscsi_destroy_session(struct work_struct *work)
@@ -2062,6 +2083,9 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
session->ida_used = true;
} else
session->target_id = target_id;
+ spin_lock_irqsave(&session->lock, flags);
+ session->target_state = ISCSI_SESSION_TARGET_ALLOCATED;
+ spin_unlock_irqrestore(&session->lock, flags);
dev_set_name(&session->dev, "session%u", session->sid);
err = device_add(&session->dev);
@@ -4370,6 +4394,19 @@ iscsi_session_attr(discovery_parent_idx, ISCSI_PARAM_DISCOVERY_PARENT_IDX, 0);
iscsi_session_attr(discovery_parent_type, ISCSI_PARAM_DISCOVERY_PARENT_TYPE, 0);
static ssize_t
+show_priv_session_target_state(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent);
+
+ return sysfs_emit(buf, "%s\n",
+ iscsi_session_target_state_name[session->target_state]);
+}
+
+static ISCSI_CLASS_ATTR(priv_sess, target_state, S_IRUGO,
+ show_priv_session_target_state, NULL);
+
+static ssize_t
show_priv_session_state(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -4471,6 +4508,7 @@ static struct attribute *iscsi_session_attrs[] = {
&dev_attr_sess_boot_target.attr,
&dev_attr_priv_sess_recovery_tmo.attr,
&dev_attr_priv_sess_state.attr,
+ &dev_attr_priv_sess_target_state.attr,
&dev_attr_priv_sess_creator.attr,
&dev_attr_sess_chap_out_idx.attr,
&dev_attr_sess_chap_in_idx.attr,
@@ -4584,6 +4622,8 @@ static umode_t iscsi_session_attr_is_visible(struct kobject *kobj,
return S_IRUGO | S_IWUSR;
else if (attr == &dev_attr_priv_sess_state.attr)
return S_IRUGO;
+ else if (attr == &dev_attr_priv_sess_target_state.attr)
+ return S_IRUGO;
else if (attr == &dev_attr_priv_sess_creator.attr)
return S_IRUGO;
else if (attr == &dev_attr_priv_sess_target_id.attr)
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index d7a84c0bfaeb73..22705eb781b0e3 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -1823,6 +1823,9 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd)
ret = storvsc_do_io(dev, cmd_request, get_cpu());
put_cpu();
+ if (ret)
+ scsi_dma_unmap(scmnd);
+
if (ret == -EAGAIN) {
/* no more space */
ret = SCSI_MLQUEUE_DEVICE_BUSY;
diff --git a/drivers/scsi/xen-scsifront.c b/drivers/scsi/xen-scsifront.c
index 66b316d173b0b4..71a3bb83984c00 100644
--- a/drivers/scsi/xen-scsifront.c
+++ b/drivers/scsi/xen-scsifront.c
@@ -995,7 +995,7 @@ static int scsifront_suspend(struct xenbus_device *dev)
return err;
}
-static int scsifront_remove(struct xenbus_device *dev)
+static void scsifront_remove(struct xenbus_device *dev)
{
struct vscsifrnt_info *info = dev_get_drvdata(&dev->dev);
@@ -1011,8 +1011,6 @@ static int scsifront_remove(struct xenbus_device *dev)
scsifront_free_ring(info);
scsi_host_put(info->host);
-
- return 0;
}
static void scsifront_disconnect(struct vscsifrnt_info *info)
diff --git a/drivers/soc/imx/imx8mp-blk-ctrl.c b/drivers/soc/imx/imx8mp-blk-ctrl.c
index 0e3b6ba22f943e..0f13853901dfec 100644
--- a/drivers/soc/imx/imx8mp-blk-ctrl.c
+++ b/drivers/soc/imx/imx8mp-blk-ctrl.c
@@ -212,7 +212,7 @@ static void imx8mp_hdmi_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc,
break;
case IMX8MP_HDMIBLK_PD_LCDIF:
regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
- BIT(7) | BIT(16) | BIT(17) | BIT(18) |
+ BIT(16) | BIT(17) | BIT(18) |
BIT(19) | BIT(20));
regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11));
regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0,
@@ -241,6 +241,7 @@ static void imx8mp_hdmi_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc,
regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(1));
break;
case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY:
+ regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(7));
regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24));
regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12));
regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3));
@@ -270,7 +271,7 @@ static void imx8mp_hdmi_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc,
BIT(4) | BIT(5) | BIT(6));
regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11));
regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0,
- BIT(7) | BIT(16) | BIT(17) | BIT(18) |
+ BIT(16) | BIT(17) | BIT(18) |
BIT(19) | BIT(20));
break;
case IMX8MP_HDMIBLK_PD_PAI:
@@ -298,6 +299,7 @@ static void imx8mp_hdmi_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc,
case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY:
regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3));
regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12));
+ regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(7));
regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24));
break;
case IMX8MP_HDMIBLK_PD_HDCP:
@@ -590,7 +592,6 @@ static int imx8mp_blk_ctrl_probe(struct platform_device *pdev)
ret = PTR_ERR(domain->power_dev);
goto cleanup_pds;
}
- dev_set_name(domain->power_dev, "%s", data->name);
domain->genpd.name = data->name;
domain->genpd.power_on = imx8mp_blk_ctrl_power_on;
diff --git a/drivers/soc/imx/soc-imx8m.c b/drivers/soc/imx/soc-imx8m.c
index 28144c699b0c39..32ed9dc88e4556 100644
--- a/drivers/soc/imx/soc-imx8m.c
+++ b/drivers/soc/imx/soc-imx8m.c
@@ -66,8 +66,8 @@ static u32 __init imx8mq_soc_revision(void)
ocotp_base = of_iomap(np, 0);
WARN_ON(!ocotp_base);
clk = of_clk_get_by_name(np, NULL);
- if (!clk) {
- WARN_ON(!clk);
+ if (IS_ERR(clk)) {
+ WARN_ON(IS_ERR(clk));
return 0;
}
diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c
index cd44f17dad3d04..d51abb462ae5d3 100644
--- a/drivers/soc/qcom/apr.c
+++ b/drivers/soc/qcom/apr.c
@@ -461,9 +461,10 @@ static int apr_add_device(struct device *dev, struct device_node *np,
goto out;
}
+ /* Protection domain is optional, it does not exist on older platforms */
ret = of_property_read_string_index(np, "qcom,protection-domain",
1, &adev->service_path);
- if (ret < 0) {
+ if (ret < 0 && ret != -EINVAL) {
dev_err(dev, "Failed to read second value of qcom,protection-domain\n");
goto out;
}
diff --git a/drivers/soc/qcom/cpr.c b/drivers/soc/qcom/cpr.c
index e9b854ed1bdfd6..144ea68e0920a3 100644
--- a/drivers/soc/qcom/cpr.c
+++ b/drivers/soc/qcom/cpr.c
@@ -1708,12 +1708,16 @@ static int cpr_probe(struct platform_device *pdev)
ret = of_genpd_add_provider_simple(dev->of_node, &drv->pd);
if (ret)
- return ret;
+ goto err_remove_genpd;
platform_set_drvdata(pdev, drv);
cpr_debugfs_init(drv);
return 0;
+
+err_remove_genpd:
+ pm_genpd_remove(&drv->pd);
+ return ret;
}
static int cpr_remove(struct platform_device *pdev)
diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c
index bd502368339e5d..55cace1fd77b60 100644
--- a/drivers/soundwire/stream.c
+++ b/drivers/soundwire/stream.c
@@ -469,7 +469,7 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
}
/* Inform slave about the impending port prepare */
- sdw_do_port_prep(s_rt, prep_ch, SDW_OPS_PORT_PRE_PREP);
+ sdw_do_port_prep(s_rt, prep_ch, prep ? SDW_OPS_PORT_PRE_PREP : SDW_OPS_PORT_PRE_DEPREP);
/* Prepare Slave port implementing CP_SM */
if (!dpn_prop->simple_ch_prep_sm) {
@@ -501,7 +501,7 @@ static int sdw_prep_deprep_slave_ports(struct sdw_bus *bus,
}
/* Inform slaves about ports prepared */
- sdw_do_port_prep(s_rt, prep_ch, SDW_OPS_PORT_POST_PREP);
+ sdw_do_port_prep(s_rt, prep_ch, prep ? SDW_OPS_PORT_POST_PREP : SDW_OPS_PORT_POST_DEPREP);
/* Disable interrupt after Port de-prepare */
if (!prep && intr)
diff --git a/drivers/spi/spi-cadence-xspi.c b/drivers/spi/spi-cadence-xspi.c
index 520b4cc69cdc95..91db3c973167b0 100644
--- a/drivers/spi/spi-cadence-xspi.c
+++ b/drivers/spi/spi-cadence-xspi.c
@@ -177,7 +177,10 @@
#define CDNS_XSPI_CMD_FLD_DSEQ_CMD_3(op) ( \
FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R3_DCNT_H, \
((op)->data.nbytes >> 16) & 0xffff) | \
- FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R3_NUM_OF_DUMMY, (op)->dummy.nbytes * 8))
+ FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R3_NUM_OF_DUMMY, \
+ (op)->dummy.buswidth != 0 ? \
+ (((op)->dummy.nbytes * 8) / (op)->dummy.buswidth) : \
+ 0))
#define CDNS_XSPI_CMD_FLD_DSEQ_CMD_4(op, chipsel) ( \
FIELD_PREP(CDNS_XSPI_CMD_DSEQ_R4_BANK, chipsel) | \
diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c
index 99edddf9958b95..c3bfb6c84cab2f 100644
--- a/drivers/spi/spi-dw-core.c
+++ b/drivers/spi/spi-dw-core.c
@@ -366,7 +366,7 @@ static void dw_spi_irq_setup(struct dw_spi *dws)
* will be adjusted at the final stage of the IRQ-based SPI transfer
* execution so not to lose the leftover of the incoming data.
*/
- level = min_t(u16, dws->fifo_len / 2, dws->tx_len);
+ level = min_t(unsigned int, dws->fifo_len / 2, dws->tx_len);
dw_writel(dws, DW_SPI_TXFTLR, level);
dw_writel(dws, DW_SPI_RXFTLR, level - 1);
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 3cc7bb4d03decd..15f174f4e0561a 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -2310,7 +2310,7 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
if (!of_property_read_u32(nc, "spi-max-frequency", &value))
spi->max_speed_hz = value;
- if (!of_property_read_u16(nc, "spi-cs-setup-ns", &cs_setup)) {
+ if (!of_property_read_u16(nc, "spi-cs-setup-delay-ns", &cs_setup)) {
spi->cs_setup.value = cs_setup;
spi->cs_setup.unit = SPI_DELAY_UNIT_NSECS;
}
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 6313e7d0cdf871..a1ea093795cfba 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -68,7 +68,7 @@ static_assert(N_SPI_MINORS > 0 && N_SPI_MINORS <= 256);
struct spidev_data {
dev_t devt;
- spinlock_t spi_lock;
+ struct mutex spi_lock;
struct spi_device *spi;
struct list_head device_entry;
@@ -90,22 +90,32 @@ MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
/*-------------------------------------------------------------------------*/
static ssize_t
+spidev_sync_unlocked(struct spi_device *spi, struct spi_message *message)
+{
+ ssize_t status;
+
+ status = spi_sync(spi, message);
+ if (status == 0)
+ status = message->actual_length;
+
+ return status;
+}
+
+static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
- int status;
+ ssize_t status;
struct spi_device *spi;
- spin_lock_irq(&spidev->spi_lock);
+ mutex_lock(&spidev->spi_lock);
spi = spidev->spi;
- spin_unlock_irq(&spidev->spi_lock);
if (spi == NULL)
status = -ESHUTDOWN;
else
- status = spi_sync(spi, message);
+ status = spidev_sync_unlocked(spi, message);
- if (status == 0)
- status = message->actual_length;
+ mutex_unlock(&spidev->spi_lock);
return status;
}
@@ -294,7 +304,7 @@ static int spidev_message(struct spidev_data *spidev,
spi_message_add_tail(k_tmp, &msg);
}
- status = spidev_sync(spidev, &msg);
+ status = spidev_sync_unlocked(spidev->spi, &msg);
if (status < 0)
goto done;
@@ -359,12 +369,12 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
* we issue this ioctl.
*/
spidev = filp->private_data;
- spin_lock_irq(&spidev->spi_lock);
+ mutex_lock(&spidev->spi_lock);
spi = spi_dev_get(spidev->spi);
- spin_unlock_irq(&spidev->spi_lock);
-
- if (spi == NULL)
+ if (spi == NULL) {
+ mutex_unlock(&spidev->spi_lock);
return -ESHUTDOWN;
+ }
/* use the buffer lock here for triple duty:
* - prevent I/O (from us) so calling spi_setup() is safe;
@@ -508,6 +518,7 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
mutex_unlock(&spidev->buf_lock);
spi_dev_put(spi);
+ mutex_unlock(&spidev->spi_lock);
return retval;
}
@@ -529,12 +540,12 @@ spidev_compat_ioc_message(struct file *filp, unsigned int cmd,
* we issue this ioctl.
*/
spidev = filp->private_data;
- spin_lock_irq(&spidev->spi_lock);
+ mutex_lock(&spidev->spi_lock);
spi = spi_dev_get(spidev->spi);
- spin_unlock_irq(&spidev->spi_lock);
-
- if (spi == NULL)
+ if (spi == NULL) {
+ mutex_unlock(&spidev->spi_lock);
return -ESHUTDOWN;
+ }
/* SPI_IOC_MESSAGE needs the buffer locked "normally" */
mutex_lock(&spidev->buf_lock);
@@ -561,6 +572,7 @@ spidev_compat_ioc_message(struct file *filp, unsigned int cmd,
done:
mutex_unlock(&spidev->buf_lock);
spi_dev_put(spi);
+ mutex_unlock(&spidev->spi_lock);
return retval;
}
@@ -601,7 +613,6 @@ static int spidev_open(struct inode *inode, struct file *filp)
if (!spidev->tx_buffer) {
spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);
if (!spidev->tx_buffer) {
- dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
status = -ENOMEM;
goto err_find_dev;
}
@@ -610,7 +621,6 @@ static int spidev_open(struct inode *inode, struct file *filp)
if (!spidev->rx_buffer) {
spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL);
if (!spidev->rx_buffer) {
- dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
status = -ENOMEM;
goto err_alloc_rx_buf;
}
@@ -640,10 +650,10 @@ static int spidev_release(struct inode *inode, struct file *filp)
spidev = filp->private_data;
filp->private_data = NULL;
- spin_lock_irq(&spidev->spi_lock);
+ mutex_lock(&spidev->spi_lock);
/* ... after we unbound from the underlying device? */
dofree = (spidev->spi == NULL);
- spin_unlock_irq(&spidev->spi_lock);
+ mutex_unlock(&spidev->spi_lock);
/* last close? */
spidev->users--;
@@ -776,7 +786,7 @@ static int spidev_probe(struct spi_device *spi)
/* Initialize the driver data */
spidev->spi = spi;
- spin_lock_init(&spidev->spi_lock);
+ mutex_init(&spidev->spi_lock);
mutex_init(&spidev->buf_lock);
INIT_LIST_HEAD(&spidev->device_entry);
@@ -821,9 +831,9 @@ static void spidev_remove(struct spi_device *spi)
/* prevent new opens */
mutex_lock(&device_list_lock);
/* make sure ops on existing fds can abort cleanly */
- spin_lock_irq(&spidev->spi_lock);
+ mutex_lock(&spidev->spi_lock);
spidev->spi = NULL;
- spin_unlock_irq(&spidev->spi_lock);
+ mutex_unlock(&spidev->spi_lock);
list_del(&spidev->device_entry);
device_destroy(spidev_class, spidev->devt);
diff --git a/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h b/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h
index db1441c0cc6620..690ab7165b2c18 100644
--- a/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h
+++ b/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h
@@ -86,7 +86,7 @@ struct vchiq_service_params_kernel {
struct vchiq_instance;
-extern enum vchiq_status vchiq_initialise(struct vchiq_instance **pinstance);
+extern int vchiq_initialise(struct vchiq_instance **pinstance);
extern enum vchiq_status vchiq_shutdown(struct vchiq_instance *instance);
extern enum vchiq_status vchiq_connect(struct vchiq_instance *instance);
extern enum vchiq_status vchiq_open_service(struct vchiq_instance *instance,
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h
index 2851ef6b9cd0f6..cd20eb18f27513 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h
@@ -100,10 +100,10 @@ vchiq_dump_platform_use_state(struct vchiq_state *state);
extern void
vchiq_dump_service_use_state(struct vchiq_state *state);
-extern enum vchiq_status
+extern int
vchiq_use_internal(struct vchiq_state *state, struct vchiq_service *service,
enum USE_TYPE_E use_type);
-extern enum vchiq_status
+extern int
vchiq_release_internal(struct vchiq_state *state,
struct vchiq_service *service);
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index bac111456fa1de..2b95b4550a637b 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -73,8 +73,8 @@ static bool __target_check_io_state(struct se_cmd *se_cmd,
{
struct se_session *sess = se_cmd->se_sess;
- assert_spin_locked(&sess->sess_cmd_lock);
- WARN_ON_ONCE(!irqs_disabled());
+ lockdep_assert_held(&sess->sess_cmd_lock);
+
/*
* If command already reached CMD_T_COMPLETE state within
* target_complete_cmd() or CMD_T_FABRIC_STOP due to shutdown,
diff --git a/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
index 62c0aa5d078377..0a4eaa307156d6 100644
--- a/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
+++ b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.c
@@ -44,11 +44,13 @@ static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone,
int trip, int *temp)
{
struct int34x_thermal_zone *d = zone->devdata;
- int i;
+ int i, ret = 0;
if (d->override_ops && d->override_ops->get_trip_temp)
return d->override_ops->get_trip_temp(zone, trip, temp);
+ mutex_lock(&d->trip_mutex);
+
if (trip < d->aux_trip_nr)
*temp = d->aux_trips[trip];
else if (trip == d->crt_trip_id)
@@ -66,10 +68,12 @@ static int int340x_thermal_get_trip_temp(struct thermal_zone_device *zone,
}
}
if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
- return -EINVAL;
+ ret = -EINVAL;
}
- return 0;
+ mutex_unlock(&d->trip_mutex);
+
+ return ret;
}
static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone,
@@ -77,11 +81,13 @@ static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone,
enum thermal_trip_type *type)
{
struct int34x_thermal_zone *d = zone->devdata;
- int i;
+ int i, ret = 0;
if (d->override_ops && d->override_ops->get_trip_type)
return d->override_ops->get_trip_type(zone, trip, type);
+ mutex_lock(&d->trip_mutex);
+
if (trip < d->aux_trip_nr)
*type = THERMAL_TRIP_PASSIVE;
else if (trip == d->crt_trip_id)
@@ -99,10 +105,12 @@ static int int340x_thermal_get_trip_type(struct thermal_zone_device *zone,
}
}
if (i == INT340X_THERMAL_MAX_ACT_TRIP_COUNT)
- return -EINVAL;
+ ret = -EINVAL;
}
- return 0;
+ mutex_unlock(&d->trip_mutex);
+
+ return ret;
}
static int int340x_thermal_set_trip_temp(struct thermal_zone_device *zone,
@@ -180,6 +188,8 @@ int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone)
int trip_cnt = int34x_zone->aux_trip_nr;
int i;
+ mutex_lock(&int34x_zone->trip_mutex);
+
int34x_zone->crt_trip_id = -1;
if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_CRT",
&int34x_zone->crt_temp))
@@ -207,6 +217,8 @@ int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone)
int34x_zone->act_trips[i].valid = true;
}
+ mutex_unlock(&int34x_zone->trip_mutex);
+
return trip_cnt;
}
EXPORT_SYMBOL_GPL(int340x_thermal_read_trips);
@@ -230,6 +242,8 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
if (!int34x_thermal_zone)
return ERR_PTR(-ENOMEM);
+ mutex_init(&int34x_thermal_zone->trip_mutex);
+
int34x_thermal_zone->adev = adev;
int34x_thermal_zone->override_ops = override_ops;
@@ -281,6 +295,7 @@ err_thermal_zone:
acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
kfree(int34x_thermal_zone->aux_trips);
err_trip_alloc:
+ mutex_destroy(&int34x_thermal_zone->trip_mutex);
kfree(int34x_thermal_zone);
return ERR_PTR(ret);
}
@@ -292,6 +307,7 @@ void int340x_thermal_zone_remove(struct int34x_thermal_zone
thermal_zone_device_unregister(int34x_thermal_zone->zone);
acpi_lpat_free_conversion_table(int34x_thermal_zone->lpat_table);
kfree(int34x_thermal_zone->aux_trips);
+ mutex_destroy(&int34x_thermal_zone->trip_mutex);
kfree(int34x_thermal_zone);
}
EXPORT_SYMBOL_GPL(int340x_thermal_zone_remove);
diff --git a/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
index 3b4971df1b33b0..8f9872afd0d3c7 100644
--- a/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
+++ b/drivers/thermal/intel/int340x_thermal/int340x_thermal_zone.h
@@ -32,6 +32,7 @@ struct int34x_thermal_zone {
struct thermal_zone_device_ops *override_ops;
void *priv_data;
struct acpi_lpat_conversion_table *lpat_table;
+ struct mutex trip_mutex;
};
struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *,
diff --git a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c
index 8c42e766203331..92ed1213fe3798 100644
--- a/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c
+++ b/drivers/thermal/intel/int340x_thermal/processor_thermal_rfim.c
@@ -172,6 +172,7 @@ static const struct attribute_group fivr_attribute_group = {
RFIM_SHOW(rfi_restriction_run_busy, 1)
RFIM_SHOW(rfi_restriction_err_code, 1)
RFIM_SHOW(rfi_restriction_data_rate, 1)
+RFIM_SHOW(rfi_restriction_data_rate_base, 1)
RFIM_SHOW(ddr_data_rate_point_0, 1)
RFIM_SHOW(ddr_data_rate_point_1, 1)
RFIM_SHOW(ddr_data_rate_point_2, 1)
@@ -181,11 +182,13 @@ RFIM_SHOW(rfi_disable, 1)
RFIM_STORE(rfi_restriction_run_busy, 1)
RFIM_STORE(rfi_restriction_err_code, 1)
RFIM_STORE(rfi_restriction_data_rate, 1)
+RFIM_STORE(rfi_restriction_data_rate_base, 1)
RFIM_STORE(rfi_disable, 1)
static DEVICE_ATTR_RW(rfi_restriction_run_busy);
static DEVICE_ATTR_RW(rfi_restriction_err_code);
static DEVICE_ATTR_RW(rfi_restriction_data_rate);
+static DEVICE_ATTR_RW(rfi_restriction_data_rate_base);
static DEVICE_ATTR_RO(ddr_data_rate_point_0);
static DEVICE_ATTR_RO(ddr_data_rate_point_1);
static DEVICE_ATTR_RO(ddr_data_rate_point_2);
@@ -248,6 +251,7 @@ static struct attribute *dvfs_attrs[] = {
&dev_attr_rfi_restriction_run_busy.attr,
&dev_attr_rfi_restriction_err_code.attr,
&dev_attr_rfi_restriction_data_rate.attr,
+ &dev_attr_rfi_restriction_data_rate_base.attr,
&dev_attr_ddr_data_rate_point_0.attr,
&dev_attr_ddr_data_rate_point_1.attr,
&dev_attr_ddr_data_rate_point_2.attr,
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index f17ab2316dbd0f..77bd47d976a21c 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -909,15 +909,20 @@ __thermal_cooling_device_register(struct device_node *np,
cdev->devdata = devdata;
ret = cdev->ops->get_max_state(cdev, &cdev->max_state);
- if (ret)
- goto out_kfree_type;
+ if (ret) {
+ kfree(cdev->type);
+ goto out_ida_remove;
+ }
thermal_cooling_device_setup_sysfs(cdev);
+
ret = dev_set_name(&cdev->device, "cooling_device%d", cdev->id);
if (ret) {
+ kfree(cdev->type);
thermal_cooling_device_destroy_sysfs(cdev);
- goto out_kfree_type;
+ goto out_ida_remove;
}
+
ret = device_register(&cdev->device);
if (ret)
goto out_kfree_type;
@@ -943,6 +948,8 @@ out_kfree_type:
thermal_cooling_device_destroy_sysfs(cdev);
kfree(cdev->type);
put_device(&cdev->device);
+
+ /* thermal_release() takes care of the rest */
cdev = NULL;
out_ida_remove:
ida_free(&thermal_cdev_ida, id);
diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c
index 81252e31014a18..56008eb91e2e44 100644
--- a/drivers/thunderbolt/retimer.c
+++ b/drivers/thunderbolt/retimer.c
@@ -427,13 +427,6 @@ int tb_retimer_scan(struct tb_port *port, bool add)
{
u32 status[TB_MAX_RETIMER_INDEX + 1] = {};
int ret, i, last_idx = 0;
- struct usb4_port *usb4;
-
- usb4 = port->usb4;
- if (!usb4)
- return 0;
-
- pm_runtime_get_sync(&usb4->dev);
/*
* Send broadcast RT to make sure retimer indices facing this
@@ -441,7 +434,7 @@ int tb_retimer_scan(struct tb_port *port, bool add)
*/
ret = usb4_port_enumerate_retimers(port);
if (ret)
- goto out;
+ return ret;
/*
* Enable sideband channel for each retimer. We can do this
@@ -471,12 +464,11 @@ int tb_retimer_scan(struct tb_port *port, bool add)
break;
}
- if (!last_idx) {
- ret = 0;
- goto out;
- }
+ if (!last_idx)
+ return 0;
/* Add on-board retimers if they do not exist already */
+ ret = 0;
for (i = 1; i <= last_idx; i++) {
struct tb_retimer *rt;
@@ -490,10 +482,6 @@ int tb_retimer_scan(struct tb_port *port, bool add)
}
}
-out:
- pm_runtime_mark_last_busy(&usb4->dev);
- pm_runtime_put_autosuspend(&usb4->dev);
-
return ret;
}
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
index 4628458044270a..3f1ab30c4fb158 100644
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -628,11 +628,15 @@ static void tb_scan_port(struct tb_port *port)
* Downstream switch is reachable through two ports.
* Only scan on the primary port (link_nr == 0).
*/
+
+ if (port->usb4)
+ pm_runtime_get_sync(&port->usb4->dev);
+
if (tb_wait_for_port(port, false) <= 0)
- return;
+ goto out_rpm_put;
if (port->remote) {
tb_port_dbg(port, "port already has a remote\n");
- return;
+ goto out_rpm_put;
}
tb_retimer_scan(port, true);
@@ -647,12 +651,12 @@ static void tb_scan_port(struct tb_port *port)
*/
if (PTR_ERR(sw) == -EIO || PTR_ERR(sw) == -EADDRNOTAVAIL)
tb_scan_xdomain(port);
- return;
+ goto out_rpm_put;
}
if (tb_switch_configure(sw)) {
tb_switch_put(sw);
- return;
+ goto out_rpm_put;
}
/*
@@ -681,7 +685,7 @@ static void tb_scan_port(struct tb_port *port)
if (tb_switch_add(sw)) {
tb_switch_put(sw);
- return;
+ goto out_rpm_put;
}
/* Link the switches using both links if available */
@@ -733,6 +737,12 @@ static void tb_scan_port(struct tb_port *port)
tb_add_dp_resources(sw);
tb_scan_switch(sw);
+
+out_rpm_put:
+ if (port->usb4) {
+ pm_runtime_mark_last_busy(&port->usb4->dev);
+ pm_runtime_put_autosuspend(&port->usb4->dev);
+ }
}
static void tb_deactivate_and_free_tunnel(struct tb_tunnel *tunnel)
diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c
index 2c3cf7fc335715..1fc3c29b24f839 100644
--- a/drivers/thunderbolt/tunnel.c
+++ b/drivers/thunderbolt/tunnel.c
@@ -1275,7 +1275,7 @@ static void tb_usb3_reclaim_available_bandwidth(struct tb_tunnel *tunnel,
return;
} else if (!ret) {
/* Use maximum link rate if the link valid is not set */
- ret = usb4_usb3_port_max_link_rate(tunnel->src_port);
+ ret = tb_usb3_max_link_rate(tunnel->dst_port, tunnel->src_port);
if (ret < 0) {
tb_tunnel_warn(tunnel, "failed to read maximum link rate\n");
return;
diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c
index cfa83486c9dad6..3c51e47dd86b77 100644
--- a/drivers/thunderbolt/xdomain.c
+++ b/drivers/thunderbolt/xdomain.c
@@ -1419,12 +1419,19 @@ static int tb_xdomain_get_properties(struct tb_xdomain *xd)
* registered, we notify the userspace that it has changed.
*/
if (!update) {
- struct tb_port *port;
+ /*
+ * Now disable lane 1 if bonding was not enabled. Do
+ * this only if bonding was possible at the beginning
+ * (that is we are the connection manager and there are
+ * two lanes).
+ */
+ if (xd->bonding_possible) {
+ struct tb_port *port;
- /* Now disable lane 1 if bonding was not enabled */
- port = tb_port_at(xd->route, tb_xdomain_parent(xd));
- if (!port->bonded)
- tb_port_disable(port->dual_link_port);
+ port = tb_port_at(xd->route, tb_xdomain_parent(xd));
+ if (!port->bonded)
+ tb_port_disable(port->dual_link_port);
+ }
if (device_add(&xd->dev)) {
dev_err(&xd->dev, "failed to add XDomain device\n");
diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c
index 7c23112dc923f7..5bddb2f5e93185 100644
--- a/drivers/tty/hvc/hvc_xen.c
+++ b/drivers/tty/hvc/hvc_xen.c
@@ -52,17 +52,22 @@ static DEFINE_SPINLOCK(xencons_lock);
static struct xencons_info *vtermno_to_xencons(int vtermno)
{
- struct xencons_info *entry, *n, *ret = NULL;
+ struct xencons_info *entry, *ret = NULL;
+ unsigned long flags;
- if (list_empty(&xenconsoles))
- return NULL;
+ spin_lock_irqsave(&xencons_lock, flags);
+ if (list_empty(&xenconsoles)) {
+ spin_unlock_irqrestore(&xencons_lock, flags);
+ return NULL;
+ }
- list_for_each_entry_safe(entry, n, &xenconsoles, list) {
+ list_for_each_entry(entry, &xenconsoles, list) {
if (entry->vtermno == vtermno) {
ret = entry;
break;
}
}
+ spin_unlock_irqrestore(&xencons_lock, flags);
return ret;
}
@@ -223,7 +228,7 @@ static int xen_hvm_console_init(void)
{
int r;
uint64_t v = 0;
- unsigned long gfn;
+ unsigned long gfn, flags;
struct xencons_info *info;
if (!xen_hvm_domain())
@@ -258,9 +263,9 @@ static int xen_hvm_console_init(void)
goto err;
info->vtermno = HVC_COOKIE;
- spin_lock(&xencons_lock);
+ spin_lock_irqsave(&xencons_lock, flags);
list_add_tail(&info->list, &xenconsoles);
- spin_unlock(&xencons_lock);
+ spin_unlock_irqrestore(&xencons_lock, flags);
return 0;
err:
@@ -283,6 +288,7 @@ static int xencons_info_pv_init(struct xencons_info *info, int vtermno)
static int xen_pv_console_init(void)
{
struct xencons_info *info;
+ unsigned long flags;
if (!xen_pv_domain())
return -ENODEV;
@@ -299,9 +305,9 @@ static int xen_pv_console_init(void)
/* already configured */
return 0;
}
- spin_lock(&xencons_lock);
+ spin_lock_irqsave(&xencons_lock, flags);
xencons_info_pv_init(info, HVC_COOKIE);
- spin_unlock(&xencons_lock);
+ spin_unlock_irqrestore(&xencons_lock, flags);
return 0;
}
@@ -309,6 +315,7 @@ static int xen_pv_console_init(void)
static int xen_initial_domain_console_init(void)
{
struct xencons_info *info;
+ unsigned long flags;
if (!xen_initial_domain())
return -ENODEV;
@@ -323,9 +330,9 @@ static int xen_initial_domain_console_init(void)
info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0, false);
info->vtermno = HVC_COOKIE;
- spin_lock(&xencons_lock);
+ spin_lock_irqsave(&xencons_lock, flags);
list_add_tail(&info->list, &xenconsoles);
- spin_unlock(&xencons_lock);
+ spin_unlock_irqrestore(&xencons_lock, flags);
return 0;
}
@@ -380,10 +387,12 @@ static void xencons_free(struct xencons_info *info)
static int xen_console_remove(struct xencons_info *info)
{
+ unsigned long flags;
+
xencons_disconnect_backend(info);
- spin_lock(&xencons_lock);
+ spin_lock_irqsave(&xencons_lock, flags);
list_del(&info->list);
- spin_unlock(&xencons_lock);
+ spin_unlock_irqrestore(&xencons_lock, flags);
if (info->xbdev != NULL)
xencons_free(info);
else {
@@ -394,9 +403,9 @@ static int xen_console_remove(struct xencons_info *info)
return 0;
}
-static int xencons_remove(struct xenbus_device *dev)
+static void xencons_remove(struct xenbus_device *dev)
{
- return xen_console_remove(dev_get_drvdata(&dev->dev));
+ xen_console_remove(dev_get_drvdata(&dev->dev));
}
static int xencons_connect_backend(struct xenbus_device *dev,
@@ -464,6 +473,7 @@ static int xencons_probe(struct xenbus_device *dev,
{
int ret, devid;
struct xencons_info *info;
+ unsigned long flags;
devid = dev->nodename[strlen(dev->nodename) - 1] - '0';
if (devid == 0)
@@ -482,9 +492,9 @@ static int xencons_probe(struct xenbus_device *dev,
ret = xencons_connect_backend(dev, info);
if (ret < 0)
goto error;
- spin_lock(&xencons_lock);
+ spin_lock_irqsave(&xencons_lock, flags);
list_add_tail(&info->list, &xenconsoles);
- spin_unlock(&xencons_lock);
+ spin_unlock_irqrestore(&xencons_lock, flags);
return 0;
@@ -584,10 +594,12 @@ static int __init xen_hvc_init(void)
info->hvc = hvc_alloc(HVC_COOKIE, info->irq, ops, 256);
if (IS_ERR(info->hvc)) {
+ unsigned long flags;
+
r = PTR_ERR(info->hvc);
- spin_lock(&xencons_lock);
+ spin_lock_irqsave(&xencons_lock, flags);
list_del(&info->list);
- spin_unlock(&xencons_lock);
+ spin_unlock_irqrestore(&xencons_lock, flags);
if (info->irq)
unbind_from_irqhandler(info->irq, NULL);
kfree(info);
diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
index 37d6af2ec4272e..7fa66501792dd8 100644
--- a/drivers/tty/serial/8250/8250_dma.c
+++ b/drivers/tty/serial/8250/8250_dma.c
@@ -43,15 +43,23 @@ static void __dma_rx_complete(struct uart_8250_port *p)
struct uart_8250_dma *dma = p->dma;
struct tty_port *tty_port = &p->port.state->port;
struct dma_tx_state state;
+ enum dma_status dma_status;
int count;
- dma->rx_running = 0;
- dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+ /*
+ * New DMA Rx can be started during the completion handler before it
+ * could acquire port's lock and it might still be ongoing. Don't to
+ * anything in such case.
+ */
+ dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+ if (dma_status == DMA_IN_PROGRESS)
+ return;
count = dma->rx_size - state.residue;
tty_insert_flip_string(tty_port, dma->rx_buf, count);
p->port.icount.rx += count;
+ dma->rx_running = 0;
tty_flip_buffer_push(tty_port);
}
@@ -62,9 +70,14 @@ static void dma_rx_complete(void *param)
struct uart_8250_dma *dma = p->dma;
unsigned long flags;
- __dma_rx_complete(p);
-
spin_lock_irqsave(&p->port.lock, flags);
+ if (dma->rx_running)
+ __dma_rx_complete(p);
+
+ /*
+ * Cannot be combined with the previous check because __dma_rx_complete()
+ * changes dma->rx_running.
+ */
if (!dma->rx_running && (serial_lsr_in(p) & UART_LSR_DR))
p->dma->rx_dma(p);
spin_unlock_irqrestore(&p->port.lock, flags);
diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c
index 314a05e009df97..64770c62bbec54 100644
--- a/drivers/tty/serial/8250/8250_exar.c
+++ b/drivers/tty/serial/8250/8250_exar.c
@@ -43,6 +43,12 @@
#define PCI_DEVICE_ID_EXAR_XR17V4358 0x4358
#define PCI_DEVICE_ID_EXAR_XR17V8358 0x8358
+#define PCI_DEVICE_ID_SEALEVEL_710xC 0x1001
+#define PCI_DEVICE_ID_SEALEVEL_720xC 0x1002
+#define PCI_DEVICE_ID_SEALEVEL_740xC 0x1004
+#define PCI_DEVICE_ID_SEALEVEL_780xC 0x1008
+#define PCI_DEVICE_ID_SEALEVEL_716xC 0x1010
+
#define UART_EXAR_INT0 0x80
#define UART_EXAR_8XMODE 0x88 /* 8X sampling rate select */
#define UART_EXAR_SLEEP 0x8b /* Sleep mode */
@@ -638,6 +644,8 @@ exar_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *ent)
nr_ports = BIT(((pcidev->device & 0x38) >> 3) - 1);
else if (board->num_ports)
nr_ports = board->num_ports;
+ else if (pcidev->vendor == PCI_VENDOR_ID_SEALEVEL)
+ nr_ports = pcidev->device & 0xff;
else
nr_ports = pcidev->device & 0x0f;
@@ -864,6 +872,12 @@ static const struct pci_device_id exar_pci_tbl[] = {
EXAR_DEVICE(COMMTECH, 4224PCI335, pbn_fastcom335_4),
EXAR_DEVICE(COMMTECH, 2324PCI335, pbn_fastcom335_4),
EXAR_DEVICE(COMMTECH, 2328PCI335, pbn_fastcom335_8),
+
+ EXAR_DEVICE(SEALEVEL, 710xC, pbn_exar_XR17V35x),
+ EXAR_DEVICE(SEALEVEL, 720xC, pbn_exar_XR17V35x),
+ EXAR_DEVICE(SEALEVEL, 740xC, pbn_exar_XR17V35x),
+ EXAR_DEVICE(SEALEVEL, 780xC, pbn_exar_XR17V35x),
+ EXAR_DEVICE(SEALEVEL, 716xC, pbn_exar_XR17V35x),
{ 0, }
};
MODULE_DEVICE_TABLE(pci, exar_pci_tbl);
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index d75c39f4622b7b..d8c2f3455eebad 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -1466,6 +1466,10 @@ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
struct circ_buf *xmit = &uap->port.state->xmit;
int count = uap->fifosize >> 1;
+ if ((uap->port.rs485.flags & SER_RS485_ENABLED) &&
+ !uap->rs485_tx_started)
+ pl011_rs485_tx_start(uap);
+
if (uap->port.x_char) {
if (!pl011_tx_char(uap, uap->port.x_char, from_irq))
return true;
@@ -1477,10 +1481,6 @@ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
return false;
}
- if ((uap->port.rs485.flags & SER_RS485_ENABLED) &&
- !uap->rs485_tx_started)
- pl011_rs485_tx_start(uap);
-
/* If we are using DMA mode, try to send some characters. */
if (pl011_dma_tx_irq(uap))
return true;
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index f1c06e12efa0bb..9cd7479b03c086 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -2657,13 +2657,7 @@ static void __init atmel_console_get_options(struct uart_port *port, int *baud,
else if (mr == ATMEL_US_PAR_ODD)
*parity = 'o';
- /*
- * The serial core only rounds down when matching this to a
- * supported baud rate. Make sure we don't end up slightly
- * lower than one of those, as it would make us fall through
- * to a much lower baud rate than we really want.
- */
- *baud = port->uartclk / (16 * (quot - 1));
+ *baud = port->uartclk / (16 * quot);
}
static int __init atmel_console_setup(struct console *co, char *options)
diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c
index a3ed9b34e2abb5..7ce7bb1640054d 100644
--- a/drivers/tty/serial/kgdboc.c
+++ b/drivers/tty/serial/kgdboc.c
@@ -171,6 +171,7 @@ static int configure_kgdboc(void)
int err = -ENODEV;
char *cptr = config;
struct console *cons;
+ int cookie;
if (!strlen(config) || isspace(config[0])) {
err = 0;
@@ -189,20 +190,9 @@ static int configure_kgdboc(void)
if (kgdboc_register_kbd(&cptr))
goto do_register;
- /*
- * tty_find_polling_driver() can call uart_set_options()
- * (via poll_init) to configure the uart. Take the console_list_lock
- * in order to synchronize against register_console(), which can also
- * configure the uart via uart_set_options(). This also allows safe
- * traversal of the console list.
- */
- console_list_lock();
-
p = tty_find_polling_driver(cptr, &tty_line);
- if (!p) {
- console_list_unlock();
+ if (!p)
goto noconfig;
- }
/*
* Take console_lock to serialize device() callback with
@@ -211,7 +201,8 @@ static int configure_kgdboc(void)
*/
console_lock();
- for_each_console(cons) {
+ cookie = console_srcu_read_lock();
+ for_each_console_srcu(cons) {
int idx;
if (cons->device && cons->device(cons, &idx) == p &&
idx == tty_line) {
@@ -219,11 +210,10 @@ static int configure_kgdboc(void)
break;
}
}
+ console_srcu_read_unlock(cookie);
console_unlock();
- console_list_unlock();
-
kgdb_tty_driver = p;
kgdb_tty_line = tty_line;
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 3d54a43768cd24..9576ba8bbc40ec 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -749,7 +749,7 @@ static void pch_dma_tx_complete(void *arg)
uart_xmit_advance(port, sg_dma_len(sg));
async_tx_ack(priv->desc_tx);
- dma_unmap_sg(port->dev, sg, priv->orig_nent, DMA_TO_DEVICE);
+ dma_unmap_sg(port->dev, priv->sg_tx_p, priv->orig_nent, DMA_TO_DEVICE);
priv->tx_dma_use = 0;
priv->nent = 0;
priv->orig_nent = 0;
diff --git a/drivers/tty/serial/qcom_geni_serial.c b/drivers/tty/serial/qcom_geni_serial.c
index b487823f0e6172..57f04f8bf50432 100644
--- a/drivers/tty/serial/qcom_geni_serial.c
+++ b/drivers/tty/serial/qcom_geni_serial.c
@@ -864,9 +864,10 @@ out_unlock:
return IRQ_HANDLED;
}
-static void get_tx_fifo_size(struct qcom_geni_serial_port *port)
+static int setup_fifos(struct qcom_geni_serial_port *port)
{
struct uart_port *uport;
+ u32 old_rx_fifo_depth = port->rx_fifo_depth;
uport = &port->uport;
port->tx_fifo_depth = geni_se_get_tx_fifo_depth(&port->se);
@@ -874,6 +875,16 @@ static void get_tx_fifo_size(struct qcom_geni_serial_port *port)
port->rx_fifo_depth = geni_se_get_rx_fifo_depth(&port->se);
uport->fifosize =
(port->tx_fifo_depth * port->tx_fifo_width) / BITS_PER_BYTE;
+
+ if (port->rx_fifo && (old_rx_fifo_depth != port->rx_fifo_depth) && port->rx_fifo_depth) {
+ port->rx_fifo = devm_krealloc(uport->dev, port->rx_fifo,
+ port->rx_fifo_depth * sizeof(u32),
+ GFP_KERNEL);
+ if (!port->rx_fifo)
+ return -ENOMEM;
+ }
+
+ return 0;
}
@@ -888,6 +899,7 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
u32 rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT;
u32 proto;
u32 pin_swap;
+ int ret;
proto = geni_se_read_proto(&port->se);
if (proto != GENI_SE_UART) {
@@ -897,7 +909,9 @@ static int qcom_geni_serial_port_setup(struct uart_port *uport)
qcom_geni_serial_stop_rx(uport);
- get_tx_fifo_size(port);
+ ret = setup_fifos(port);
+ if (ret)
+ return ret;
writel(rxstale, uport->membase + SE_UART_RX_STALE_CNT);
@@ -1516,7 +1530,7 @@ static int qcom_geni_serial_remove(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused qcom_geni_serial_sys_suspend(struct device *dev)
+static int qcom_geni_serial_sys_suspend(struct device *dev)
{
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
struct uart_port *uport = &port->uport;
@@ -1533,7 +1547,7 @@ static int __maybe_unused qcom_geni_serial_sys_suspend(struct device *dev)
return uart_suspend_port(private_data->drv, uport);
}
-static int __maybe_unused qcom_geni_serial_sys_resume(struct device *dev)
+static int qcom_geni_serial_sys_resume(struct device *dev)
{
int ret;
struct qcom_geni_serial_port *port = dev_get_drvdata(dev);
@@ -1581,10 +1595,12 @@ static int qcom_geni_serial_sys_hib_resume(struct device *dev)
}
static const struct dev_pm_ops qcom_geni_serial_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(qcom_geni_serial_sys_suspend,
- qcom_geni_serial_sys_resume)
- .restore = qcom_geni_serial_sys_hib_resume,
- .thaw = qcom_geni_serial_sys_hib_resume,
+ .suspend = pm_sleep_ptr(qcom_geni_serial_sys_suspend),
+ .resume = pm_sleep_ptr(qcom_geni_serial_sys_resume),
+ .freeze = pm_sleep_ptr(qcom_geni_serial_sys_suspend),
+ .poweroff = pm_sleep_ptr(qcom_geni_serial_sys_suspend),
+ .restore = pm_sleep_ptr(qcom_geni_serial_sys_hib_resume),
+ .thaw = pm_sleep_ptr(qcom_geni_serial_sys_hib_resume),
};
static const struct of_device_id qcom_geni_serial_match_table[] = {
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index b9fbbee598b815..ec874f3a567ca7 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2212,6 +2212,9 @@ EXPORT_SYMBOL_GPL(uart_parse_options);
* @parity: parity character - 'n' (none), 'o' (odd), 'e' (even)
* @bits: number of data bits
* @flow: flow control character - 'r' (rts)
+ *
+ * Locking: Caller must hold console_list_lock in order to serialize
+ * early initialization of the serial-console lock.
*/
int
uart_set_options(struct uart_port *port, struct console *co,
@@ -2619,7 +2622,9 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options)
if (!ret && options) {
uart_parse_options(options, &baud, &parity, &bits, &flow);
+ console_list_lock();
ret = uart_set_options(port, NULL, baud, parity, bits, flow);
+ console_list_unlock();
}
out:
mutex_unlock(&tport->mutex);
diff --git a/drivers/tty/serial/stm32-usart.c b/drivers/tty/serial/stm32-usart.c
index a1490033aa164b..409e91d6829a5d 100644
--- a/drivers/tty/serial/stm32-usart.c
+++ b/drivers/tty/serial/stm32-usart.c
@@ -797,25 +797,11 @@ static irqreturn_t stm32_usart_interrupt(int irq, void *ptr)
spin_unlock(&port->lock);
}
- if (stm32_usart_rx_dma_enabled(port))
- return IRQ_WAKE_THREAD;
- else
- return IRQ_HANDLED;
-}
-
-static irqreturn_t stm32_usart_threaded_interrupt(int irq, void *ptr)
-{
- struct uart_port *port = ptr;
- struct tty_port *tport = &port->state->port;
- struct stm32_port *stm32_port = to_stm32_port(port);
- unsigned int size;
- unsigned long flags;
-
/* Receiver timeout irq for DMA RX */
- if (!stm32_port->throttled) {
- spin_lock_irqsave(&port->lock, flags);
+ if (stm32_usart_rx_dma_enabled(port) && !stm32_port->throttled) {
+ spin_lock(&port->lock);
size = stm32_usart_receive_chars(port, false);
- uart_unlock_and_check_sysrq_irqrestore(port, flags);
+ uart_unlock_and_check_sysrq(port);
if (size)
tty_flip_buffer_push(tport);
}
@@ -1015,10 +1001,8 @@ static int stm32_usart_startup(struct uart_port *port)
u32 val;
int ret;
- ret = request_threaded_irq(port->irq, stm32_usart_interrupt,
- stm32_usart_threaded_interrupt,
- IRQF_ONESHOT | IRQF_NO_SUSPEND,
- name, port);
+ ret = request_irq(port->irq, stm32_usart_interrupt,
+ IRQF_NO_SUSPEND, name, port);
if (ret)
return ret;
@@ -1601,13 +1585,6 @@ static int stm32_usart_of_dma_rx_probe(struct stm32_port *stm32port,
struct dma_slave_config config;
int ret;
- /*
- * Using DMA and threaded handler for the console could lead to
- * deadlocks.
- */
- if (uart_console(port))
- return -ENODEV;
-
stm32port->rx_buf = dma_alloc_coherent(dev, RX_BUF_L,
&stm32port->rx_dma_buf,
GFP_KERNEL);
diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c
index 1850bacdb5b0e1..f566eb1839dc50 100644
--- a/drivers/tty/vt/vc_screen.c
+++ b/drivers/tty/vt/vc_screen.c
@@ -386,10 +386,6 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
uni_mode = use_unicode(inode);
attr = use_attributes(inode);
- ret = -ENXIO;
- vc = vcs_vc(inode, &viewed);
- if (!vc)
- goto unlock_out;
ret = -EINVAL;
if (pos < 0)
@@ -407,6 +403,11 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
unsigned int this_round, skip = 0;
int size;
+ ret = -ENXIO;
+ vc = vcs_vc(inode, &viewed);
+ if (!vc)
+ goto unlock_out;
+
/* Check whether we are above size each round,
* as copy_to_user at the end of this loop
* could sleep.
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index e18c9f4463ec59..3a1c4d31e010da 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -1234,12 +1234,14 @@ static int ufshcd_clock_scaling_prepare(struct ufs_hba *hba)
* clock scaling is in progress
*/
ufshcd_scsi_block_requests(hba);
+ mutex_lock(&hba->wb_mutex);
down_write(&hba->clk_scaling_lock);
if (!hba->clk_scaling.is_allowed ||
ufshcd_wait_for_doorbell_clr(hba, DOORBELL_CLR_TOUT_US)) {
ret = -EBUSY;
up_write(&hba->clk_scaling_lock);
+ mutex_unlock(&hba->wb_mutex);
ufshcd_scsi_unblock_requests(hba);
goto out;
}
@@ -1251,12 +1253,16 @@ out:
return ret;
}
-static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba, bool writelock)
+static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba, int err, bool scale_up)
{
- if (writelock)
- up_write(&hba->clk_scaling_lock);
- else
- up_read(&hba->clk_scaling_lock);
+ up_write(&hba->clk_scaling_lock);
+
+ /* Enable Write Booster if we have scaled up else disable it */
+ if (ufshcd_enable_wb_if_scaling_up(hba) && !err)
+ ufshcd_wb_toggle(hba, scale_up);
+
+ mutex_unlock(&hba->wb_mutex);
+
ufshcd_scsi_unblock_requests(hba);
ufshcd_release(hba);
}
@@ -1273,7 +1279,6 @@ static void ufshcd_clock_scaling_unprepare(struct ufs_hba *hba, bool writelock)
static int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up)
{
int ret = 0;
- bool is_writelock = true;
ret = ufshcd_clock_scaling_prepare(hba);
if (ret)
@@ -1302,15 +1307,8 @@ static int ufshcd_devfreq_scale(struct ufs_hba *hba, bool scale_up)
}
}
- /* Enable Write Booster if we have scaled up else disable it */
- if (ufshcd_enable_wb_if_scaling_up(hba)) {
- downgrade_write(&hba->clk_scaling_lock);
- is_writelock = false;
- ufshcd_wb_toggle(hba, scale_up);
- }
-
out_unprepare:
- ufshcd_clock_scaling_unprepare(hba, is_writelock);
+ ufshcd_clock_scaling_unprepare(hba, ret, scale_up);
return ret;
}
@@ -6056,11 +6054,21 @@ void ufshcd_schedule_eh_work(struct ufs_hba *hba)
}
}
+static void ufshcd_force_error_recovery(struct ufs_hba *hba)
+{
+ spin_lock_irq(hba->host->host_lock);
+ hba->force_reset = true;
+ ufshcd_schedule_eh_work(hba);
+ spin_unlock_irq(hba->host->host_lock);
+}
+
static void ufshcd_clk_scaling_allow(struct ufs_hba *hba, bool allow)
{
+ mutex_lock(&hba->wb_mutex);
down_write(&hba->clk_scaling_lock);
hba->clk_scaling.is_allowed = allow;
up_write(&hba->clk_scaling_lock);
+ mutex_unlock(&hba->wb_mutex);
}
static void ufshcd_clk_scaling_suspend(struct ufs_hba *hba, bool suspend)
@@ -9083,6 +9091,15 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
if (!hba->dev_info.b_rpm_dev_flush_capable) {
ret = ufshcd_set_dev_pwr_mode(hba, req_dev_pwr_mode);
+ if (ret && pm_op != UFS_SHUTDOWN_PM) {
+ /*
+ * If return err in suspend flow, IO will hang.
+ * Trigger error handler and break suspend for
+ * error recovery.
+ */
+ ufshcd_force_error_recovery(hba);
+ ret = -EBUSY;
+ }
if (ret)
goto enable_scaling;
}
@@ -9094,6 +9111,15 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
*/
check_for_bkops = !ufshcd_is_ufs_dev_deepsleep(hba);
ret = ufshcd_link_state_transition(hba, req_link_state, check_for_bkops);
+ if (ret && pm_op != UFS_SHUTDOWN_PM) {
+ /*
+ * If return err in suspend flow, IO will hang.
+ * Trigger error handler and break suspend for
+ * error recovery.
+ */
+ ufshcd_force_error_recovery(hba);
+ ret = -EBUSY;
+ }
if (ret)
goto set_dev_active;
@@ -9767,6 +9793,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
/* Initialize mutex for exception event control */
mutex_init(&hba->ee_ctrl_mutex);
+ mutex_init(&hba->wb_mutex);
init_rwsem(&hba->clk_scaling_lock);
ufshcd_init_clk_gating(hba);
diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c
index 5adcb349718c33..ccfaebca6faa75 100644
--- a/drivers/usb/cdns3/cdns3-gadget.c
+++ b/drivers/usb/cdns3/cdns3-gadget.c
@@ -2614,6 +2614,7 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
u8 req_on_hw_ring = 0;
unsigned long flags;
int ret = 0;
+ int val;
if (!ep || !request || !ep->desc)
return -EINVAL;
@@ -2649,6 +2650,13 @@ found:
/* Update ring only if removed request is on pending_req_list list */
if (req_on_hw_ring && link_trb) {
+ /* Stop DMA */
+ writel(EP_CMD_DFLUSH, &priv_dev->regs->ep_cmd);
+
+ /* wait for DFLUSH cleared */
+ readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
+ !(val & EP_CMD_DFLUSH), 1, 1000);
+
link_trb->buffer = cpu_to_le32(TRB_BUFFER(priv_ep->trb_pool_dma +
((priv_req->end_trb + 1) * TRB_SIZE)));
link_trb->control = cpu_to_le32((le32_to_cpu(link_trb->control) & TRB_CYCLE) |
@@ -2660,6 +2668,10 @@ found:
cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
+ req = cdns3_next_request(&priv_ep->pending_req_list);
+ if (req)
+ cdns3_rearm_transfer(priv_ep, 1);
+
not_found:
spin_unlock_irqrestore(&priv_dev->lock, flags);
return ret;
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 484b1cd2343170..27c601296130e2 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -1294,12 +1294,12 @@ static void ci_extcon_wakeup_int(struct ci_hdrc *ci)
cable_id = &ci->platdata->id_extcon;
cable_vbus = &ci->platdata->vbus_extcon;
- if ((!IS_ERR(cable_id->edev) || !IS_ERR(ci->role_switch))
+ if ((!IS_ERR(cable_id->edev) || ci->role_switch)
&& ci->is_otg &&
(otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS))
ci_irq(ci);
- if ((!IS_ERR(cable_vbus->edev) || !IS_ERR(ci->role_switch))
+ if ((!IS_ERR(cable_vbus->edev) || ci->role_switch)
&& ci->is_otg &&
(otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS))
ci_irq(ci);
diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c
index 60e8174686a17c..d7c8461976ce09 100644
--- a/drivers/usb/common/ulpi.c
+++ b/drivers/usb/common/ulpi.c
@@ -207,7 +207,7 @@ static int ulpi_read_id(struct ulpi *ulpi)
/* Test the interface */
ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa);
if (ret < 0)
- return ret;
+ goto err;
ret = ulpi_read(ulpi, ULPI_SCRATCH);
if (ret < 0)
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 77e73fc8d6736b..9eca403af2a85b 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -44,6 +44,9 @@
#define USB_PRODUCT_USB5534B 0x5534
#define USB_VENDOR_CYPRESS 0x04b4
#define USB_PRODUCT_CY7C65632 0x6570
+#define USB_VENDOR_TEXAS_INSTRUMENTS 0x0451
+#define USB_PRODUCT_TUSB8041_USB3 0x8140
+#define USB_PRODUCT_TUSB8041_USB2 0x8142
#define HUB_QUIRK_CHECK_PORT_AUTOSUSPEND 0x01
#define HUB_QUIRK_DISABLE_AUTOSUSPEND 0x02
@@ -5854,6 +5857,16 @@ static const struct usb_device_id hub_id_table[] = {
.idVendor = USB_VENDOR_GENESYS_LOGIC,
.bInterfaceClass = USB_CLASS_HUB,
.driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},
+ { .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ | USB_DEVICE_ID_MATCH_PRODUCT,
+ .idVendor = USB_VENDOR_TEXAS_INSTRUMENTS,
+ .idProduct = USB_PRODUCT_TUSB8041_USB2,
+ .driver_info = HUB_QUIRK_DISABLE_AUTOSUSPEND},
+ { .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ | USB_DEVICE_ID_MATCH_PRODUCT,
+ .idVendor = USB_VENDOR_TEXAS_INSTRUMENTS,
+ .idProduct = USB_PRODUCT_TUSB8041_USB3,
+ .driver_info = HUB_QUIRK_DISABLE_AUTOSUSPEND},
{ .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
.bDeviceClass = USB_CLASS_HUB},
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 079e183cf3bffb..934b3d997702e6 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -526,6 +526,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* DJI CineSSD */
{ USB_DEVICE(0x2ca3, 0x0031), .driver_info = USB_QUIRK_NO_LPM },
+ /* Alcor Link AK9563 SC Reader used in 2022 Lenovo ThinkPads */
+ { USB_DEVICE(0x2ce3, 0x9563), .driver_info = USB_QUIRK_NO_LPM },
+
/* DELL USB GEN2 */
{ USB_DEVICE(0x413c, 0xb062), .driver_info = USB_QUIRK_NO_LPM | USB_QUIRK_RESET_RESUME },
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 6d93428432f13f..533baa85083c2e 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -37,6 +37,71 @@ bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
}
EXPORT_SYMBOL_GPL(usb_acpi_power_manageable);
+#define UUID_USB_CONTROLLER_DSM "ce2ee385-00e6-48cb-9f05-2edb927c4899"
+#define USB_DSM_DISABLE_U1_U2_FOR_PORT 5
+
+/**
+ * usb_acpi_port_lpm_incapable - check if lpm should be disabled for a port.
+ * @hdev: USB device belonging to the usb hub
+ * @index: zero based port index
+ *
+ * Some USB3 ports may not support USB3 link power management U1/U2 states
+ * due to different retimer setup. ACPI provides _DSM method which returns 0x01
+ * if U1 and U2 states should be disabled. Evaluate _DSM with:
+ * Arg0: UUID = ce2ee385-00e6-48cb-9f05-2edb927c4899
+ * Arg1: Revision ID = 0
+ * Arg2: Function Index = 5
+ * Arg3: (empty)
+ *
+ * Return 1 if USB3 port is LPM incapable, negative on error, otherwise 0
+ */
+
+int usb_acpi_port_lpm_incapable(struct usb_device *hdev, int index)
+{
+ union acpi_object *obj;
+ acpi_handle port_handle;
+ int port1 = index + 1;
+ guid_t guid;
+ int ret;
+
+ ret = guid_parse(UUID_USB_CONTROLLER_DSM, &guid);
+ if (ret)
+ return ret;
+
+ port_handle = usb_get_hub_port_acpi_handle(hdev, port1);
+ if (!port_handle) {
+ dev_dbg(&hdev->dev, "port-%d no acpi handle\n", port1);
+ return -ENODEV;
+ }
+
+ if (!acpi_check_dsm(port_handle, &guid, 0,
+ BIT(USB_DSM_DISABLE_U1_U2_FOR_PORT))) {
+ dev_dbg(&hdev->dev, "port-%d no _DSM function %d\n",
+ port1, USB_DSM_DISABLE_U1_U2_FOR_PORT);
+ return -ENODEV;
+ }
+
+ obj = acpi_evaluate_dsm(port_handle, &guid, 0,
+ USB_DSM_DISABLE_U1_U2_FOR_PORT, NULL);
+
+ if (!obj)
+ return -ENODEV;
+
+ if (obj->type != ACPI_TYPE_INTEGER) {
+ dev_dbg(&hdev->dev, "evaluate port-%d _DSM failed\n", port1);
+ ACPI_FREE(obj);
+ return -EINVAL;
+ }
+
+ if (obj->integer.value == 0x01)
+ ret = 1;
+
+ ACPI_FREE(obj);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(usb_acpi_port_lpm_incapable);
+
/**
* usb_acpi_set_power_state - control usb port's power via acpi power
* resource
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index b2f72b0e75c6bb..be954a9abbe05e 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -3,6 +3,7 @@
config USB_DWC3
tristate "DesignWare USB3 DRD Core Support"
depends on (USB || USB_GADGET) && HAS_DMA
+ depends on (EXTCON || EXTCON=n)
select USB_XHCI_PLATFORM if USB_XHCI_HCD
select USB_ROLE_SWITCH if USB_DWC3_DUAL_ROLE
help
@@ -44,7 +45,6 @@ config USB_DWC3_GADGET
config USB_DWC3_DUAL_ROLE
bool "Dual Role mode"
depends on ((USB=y || USB=USB_DWC3) && (USB_GADGET=y || USB_GADGET=USB_DWC3))
- depends on (EXTCON=y || EXTCON=USB_DWC3)
help
This is the default mode of working of DWC3 controller where
both host and gadget features are enabled.
diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
index b0a0351d2d8b5c..959fc925ca7c52 100644
--- a/drivers/usb/dwc3/dwc3-qcom.c
+++ b/drivers/usb/dwc3/dwc3-qcom.c
@@ -901,7 +901,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev);
/* enable vbus override for device mode */
- if (qcom->mode == USB_DR_MODE_PERIPHERAL)
+ if (qcom->mode != USB_DR_MODE_HOST)
dwc3_qcom_vbus_override_enable(qcom, true);
/* register extcon to override sw_vbus on Vbus change later */
diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c
index 8607d4c23283c4..0745e9f11b2ef4 100644
--- a/drivers/usb/dwc3/dwc3-xilinx.c
+++ b/drivers/usb/dwc3/dwc3-xilinx.c
@@ -13,6 +13,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/gpio/consumer.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 789976567f9f00..89dcfac01235ff 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1727,6 +1727,7 @@ static int __dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool int
else if (!ret)
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
+ dep->flags &= ~DWC3_EP_DELAY_STOP;
return ret;
}
@@ -3732,8 +3733,10 @@ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
if (dep->number <= 1 && dwc->ep0state != EP0_DATA_PHASE)
return;
+ if (interrupt && (dep->flags & DWC3_EP_DELAY_STOP))
+ return;
+
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
- (dep->flags & DWC3_EP_DELAY_STOP) ||
(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
return;
diff --git a/drivers/usb/fotg210/fotg210-core.c b/drivers/usb/fotg210/fotg210-core.c
index 8a54edf921ac24..ee740a6da463fb 100644
--- a/drivers/usb/fotg210/fotg210-core.c
+++ b/drivers/usb/fotg210/fotg210-core.c
@@ -144,10 +144,7 @@ static struct platform_driver fotg210_driver = {
static int __init fotg210_init(void)
{
- if (usb_disabled())
- return -ENODEV;
-
- if (IS_ENABLED(CONFIG_USB_FOTG210_HCD))
+ if (IS_ENABLED(CONFIG_USB_FOTG210_HCD) && !usb_disabled())
fotg210_hcd_init();
return platform_driver_register(&fotg210_driver);
}
diff --git a/drivers/usb/fotg210/fotg210-udc.c b/drivers/usb/fotg210/fotg210-udc.c
index 66e1b7ee3346ed..eb076746f03206 100644
--- a/drivers/usb/fotg210/fotg210-udc.c
+++ b/drivers/usb/fotg210/fotg210-udc.c
@@ -1014,7 +1014,6 @@ static int fotg210_udc_start(struct usb_gadget *g,
int ret;
/* hook up the driver */
- driver->driver.bus = NULL;
fotg210->driver = driver;
if (!IS_ERR_OR_NULL(fotg210->phy)) {
@@ -1201,6 +1200,8 @@ int fotg210_udc_probe(struct platform_device *pdev)
dev_info(dev, "found and initialized PHY\n");
}
+ ret = -ENOMEM;
+
for (i = 0; i < FOTG210_MAX_NUM_EP; i++) {
fotg210->ep[i] = kzalloc(sizeof(struct fotg210_ep), GFP_KERNEL);
if (!fotg210->ep[i])
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 96121d1c8df4c7..0853536cbf2e64 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -393,6 +393,7 @@ static void gadget_info_attr_release(struct config_item *item)
WARN_ON(!list_empty(&gi->string_list));
WARN_ON(!list_empty(&gi->available_func));
kfree(gi->composite.gadget_driver.function);
+ kfree(gi->composite.gadget_driver.driver.name);
kfree(gi);
}
@@ -1572,7 +1573,6 @@ static const struct usb_gadget_driver configfs_driver_template = {
.max_speed = USB_SPEED_SUPER_PLUS,
.driver = {
.owner = THIS_MODULE,
- .name = "configfs-gadget",
},
.match_existing_only = 1,
};
@@ -1623,13 +1623,21 @@ static struct config_group *gadgets_make(
gi->composite.gadget_driver = configfs_driver_template;
+ gi->composite.gadget_driver.driver.name = kasprintf(GFP_KERNEL,
+ "configfs-gadget.%s", name);
+ if (!gi->composite.gadget_driver.driver.name)
+ goto err;
+
gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL);
gi->composite.name = gi->composite.gadget_driver.function;
if (!gi->composite.gadget_driver.function)
- goto err;
+ goto out_free_driver_name;
return &gi->group;
+
+out_free_driver_name:
+ kfree(gi->composite.gadget_driver.driver.name);
err:
kfree(gi);
return ERR_PTR(-ENOMEM);
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 73dc10a77cdead..8ad354741380dd 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -279,6 +279,11 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
struct usb_request *req = ffs->ep0req;
int ret;
+ if (!req) {
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ return -EINVAL;
+ }
+
req->zero = len < le16_to_cpu(ffs->ev.setup.wLength);
spin_unlock_irq(&ffs->ev.waitq.lock);
@@ -1892,10 +1897,14 @@ static void functionfs_unbind(struct ffs_data *ffs)
ENTER();
if (!WARN_ON(!ffs->gadget)) {
+ /* dequeue before freeing ep0req */
+ usb_ep_dequeue(ffs->gadget->ep0, ffs->ep0req);
+ mutex_lock(&ffs->mutex);
usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req);
ffs->ep0req = NULL;
ffs->gadget = NULL;
clear_bit(FFS_FL_BOUND, &ffs->flags);
+ mutex_unlock(&ffs->mutex);
ffs_data_put(ffs);
}
}
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index c36bcfa0e9b46f..424bb3b666dbd7 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -83,7 +83,9 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
/* peak (theoretical) bulk transfer rate in bits-per-second */
static inline unsigned ncm_bitrate(struct usb_gadget *g)
{
- if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER_PLUS)
+ if (!g)
+ return 0;
+ else if (gadget_is_superspeed(g) && g->speed >= USB_SPEED_SUPER_PLUS)
return 4250000000U;
else if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
return 3750000000U;
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 08726e4c68a56d..0219cd79493a73 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -1142,6 +1142,7 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn)
}
std_as_out_if0_desc.bInterfaceNumber = ret;
std_as_out_if1_desc.bInterfaceNumber = ret;
+ std_as_out_if1_desc.bNumEndpoints = 1;
uac2->as_out_intf = ret;
uac2->as_out_alt = 0;
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 8f12f3f8f6eeb6..e06022873df16a 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -798,6 +798,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
net->max_mtu = GETHER_MAX_MTU_SIZE;
dev->gadget = g;
+ SET_NETDEV_DEV(net, &g->dev);
SET_NETDEV_DEVTYPE(net, &gadget_type);
status = register_netdev(net);
@@ -872,6 +873,8 @@ int gether_register_netdev(struct net_device *net)
struct usb_gadget *g;
int status;
+ if (!net->dev.parent)
+ return -EINVAL;
dev = netdev_priv(net);
g = dev->gadget;
@@ -902,6 +905,7 @@ void gether_set_gadget(struct net_device *net, struct usb_gadget *g)
dev = netdev_priv(net);
dev->gadget = g;
+ SET_NETDEV_DEV(net, &g->dev);
}
EXPORT_SYMBOL_GPL(gether_set_gadget);
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index 01c3ead7d1b426..d605bc2e7e8fd4 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -229,6 +229,7 @@ static void put_ep (struct ep_data *data)
*/
static const char *CHIP;
+static DEFINE_MUTEX(sb_mutex); /* Serialize superblock operations */
/*----------------------------------------------------------------------*/
@@ -2010,13 +2011,20 @@ gadgetfs_fill_super (struct super_block *sb, struct fs_context *fc)
{
struct inode *inode;
struct dev_data *dev;
+ int rc;
- if (the_device)
- return -ESRCH;
+ mutex_lock(&sb_mutex);
+
+ if (the_device) {
+ rc = -ESRCH;
+ goto Done;
+ }
CHIP = usb_get_gadget_udc_name();
- if (!CHIP)
- return -ENODEV;
+ if (!CHIP) {
+ rc = -ENODEV;
+ goto Done;
+ }
/* superblock */
sb->s_blocksize = PAGE_SIZE;
@@ -2053,13 +2061,17 @@ gadgetfs_fill_super (struct super_block *sb, struct fs_context *fc)
* from binding to a controller.
*/
the_device = dev;
- return 0;
+ rc = 0;
+ goto Done;
-Enomem:
+ Enomem:
kfree(CHIP);
CHIP = NULL;
+ rc = -ENOMEM;
- return -ENOMEM;
+ Done:
+ mutex_unlock(&sb_mutex);
+ return rc;
}
/* "mount -t gadgetfs path /dev/gadget" ends up here */
@@ -2081,6 +2093,7 @@ static int gadgetfs_init_fs_context(struct fs_context *fc)
static void
gadgetfs_kill_sb (struct super_block *sb)
{
+ mutex_lock(&sb_mutex);
kill_litter_super (sb);
if (the_device) {
put_dev (the_device);
@@ -2088,6 +2101,7 @@ gadgetfs_kill_sb (struct super_block *sb)
}
kfree(CHIP);
CHIP = NULL;
+ mutex_unlock(&sb_mutex);
}
/*----------------------------------------------------------------------*/
diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c
index 53e38f87472b04..c06dd1af7a0c50 100644
--- a/drivers/usb/gadget/legacy/webcam.c
+++ b/drivers/usb/gadget/legacy/webcam.c
@@ -293,6 +293,7 @@ static const struct uvc_descriptor_header * const uvc_fs_streaming_cls[] = {
(const struct uvc_descriptor_header *) &uvc_format_yuv,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
+ (const struct uvc_descriptor_header *) &uvc_color_matching,
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
@@ -305,6 +306,7 @@ static const struct uvc_descriptor_header * const uvc_hs_streaming_cls[] = {
(const struct uvc_descriptor_header *) &uvc_format_yuv,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
+ (const struct uvc_descriptor_header *) &uvc_color_matching,
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
@@ -317,6 +319,7 @@ static const struct uvc_descriptor_header * const uvc_ss_streaming_cls[] = {
(const struct uvc_descriptor_header *) &uvc_format_yuv,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_360p,
(const struct uvc_descriptor_header *) &uvc_frame_yuv_720p,
+ (const struct uvc_descriptor_header *) &uvc_color_matching,
(const struct uvc_descriptor_header *) &uvc_format_mjpg,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_360p,
(const struct uvc_descriptor_header *) &uvc_frame_mjpg_720p,
diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c
index 2cdb07905bdebe..d04d72f5816e61 100644
--- a/drivers/usb/gadget/udc/bcm63xx_udc.c
+++ b/drivers/usb/gadget/udc/bcm63xx_udc.c
@@ -1830,7 +1830,6 @@ static int bcm63xx_udc_start(struct usb_gadget *gadget,
bcm63xx_select_phy_mode(udc, true);
udc->driver = driver;
- driver->driver.bus = NULL;
udc->gadget.dev.of_node = udc->dev->of_node;
spin_unlock_irqrestore(&udc->lock, flags);
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index bf745358e28e6b..3b1cc8fa30c830 100644
--- a/drivers/usb/gadget/udc/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -2285,7 +2285,6 @@ static int fsl_qe_start(struct usb_gadget *gadget,
/* lock is needed but whether should use this lock or another */
spin_lock_irqsave(&udc->lock, flags);
- driver->driver.bus = NULL;
/* hook up the driver */
udc->driver = driver;
udc->gadget.speed = driver->max_speed;
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index 50435e80411831..a67873a074b7bc 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -1943,7 +1943,6 @@ static int fsl_udc_start(struct usb_gadget *g,
/* lock is needed but whether should use this lock or another */
spin_lock_irqsave(&udc_controller->lock, flags);
- driver->driver.bus = NULL;
/* hook up the driver */
udc_controller->driver = driver;
spin_unlock_irqrestore(&udc_controller->lock, flags);
diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c
index 9af8b415f303bb..5954800d652ca9 100644
--- a/drivers/usb/gadget/udc/fusb300_udc.c
+++ b/drivers/usb/gadget/udc/fusb300_udc.c
@@ -1311,7 +1311,6 @@ static int fusb300_udc_start(struct usb_gadget *g,
struct fusb300 *fusb300 = to_fusb300(g);
/* hook up the driver */
- driver->driver.bus = NULL;
fusb300->driver = driver;
return 0;
diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c
index bdc56b24b5c900..5ffb3d5c635bec 100644
--- a/drivers/usb/gadget/udc/goku_udc.c
+++ b/drivers/usb/gadget/udc/goku_udc.c
@@ -1375,7 +1375,6 @@ static int goku_udc_start(struct usb_gadget *g,
struct goku_udc *dev = to_goku_udc(g);
/* hook up the driver */
- driver->driver.bus = NULL;
dev->driver = driver;
/*
diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index 22096f8505de4a..85cdc0af3bf952 100644
--- a/drivers/usb/gadget/udc/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -1906,7 +1906,6 @@ static int gr_udc_start(struct usb_gadget *gadget,
spin_lock(&dev->lock);
/* Hook up the driver */
- driver->driver.bus = NULL;
dev->driver = driver;
/* Get ready for host detection */
diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c
index c7e421b449f36c..06e21cee431be9 100644
--- a/drivers/usb/gadget/udc/m66592-udc.c
+++ b/drivers/usb/gadget/udc/m66592-udc.c
@@ -1454,7 +1454,6 @@ static int m66592_udc_start(struct usb_gadget *g,
struct m66592 *m66592 = to_m66592(g);
/* hook up the driver */
- driver->driver.bus = NULL;
m66592->driver = driver;
m66592_bset(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
diff --git a/drivers/usb/gadget/udc/max3420_udc.c b/drivers/usb/gadget/udc/max3420_udc.c
index 3074da00c3df76..ddf0ed3eb4f261 100644
--- a/drivers/usb/gadget/udc/max3420_udc.c
+++ b/drivers/usb/gadget/udc/max3420_udc.c
@@ -1108,7 +1108,6 @@ static int max3420_udc_start(struct usb_gadget *gadget,
spin_lock_irqsave(&udc->lock, flags);
/* hook up the driver */
- driver->driver.bus = NULL;
udc->driver = driver;
udc->gadget.speed = USB_SPEED_FULL;
diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c
index 598654a3cb4184..411b6179782c1d 100644
--- a/drivers/usb/gadget/udc/mv_u3d_core.c
+++ b/drivers/usb/gadget/udc/mv_u3d_core.c
@@ -1243,7 +1243,6 @@ static int mv_u3d_start(struct usb_gadget *g,
}
/* hook up the driver ... */
- driver->driver.bus = NULL;
u3d->driver = driver;
u3d->ep0_dir = USB_DIR_OUT;
diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
index fdb17d86cd6560..b397f3a848cf7a 100644
--- a/drivers/usb/gadget/udc/mv_udc_core.c
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
@@ -1359,7 +1359,6 @@ static int mv_udc_start(struct usb_gadget *gadget,
spin_lock_irqsave(&udc->lock, flags);
/* hook up the driver ... */
- driver->driver.bus = NULL;
udc->driver = driver;
udc->usb_state = USB_STATE_ATTACHED;
diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c
index 84605a4d0715f5..538c1b9a288354 100644
--- a/drivers/usb/gadget/udc/net2272.c
+++ b/drivers/usb/gadget/udc/net2272.c
@@ -1451,7 +1451,6 @@ static int net2272_start(struct usb_gadget *_gadget,
dev->ep[i].irqs = 0;
/* hook up the driver ... */
dev->softconnect = 1;
- driver->driver.bus = NULL;
dev->driver = driver;
/* ... then enable host detection and ep0; and we're ready
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index d6a68631354a28..1b929c519cd712 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -2423,7 +2423,6 @@ static int net2280_start(struct usb_gadget *_gadget,
dev->ep[i].irqs = 0;
/* hook up the driver ... */
- driver->driver.bus = NULL;
dev->driver = driver;
retval = device_create_file(&dev->pdev->dev, &dev_attr_function);
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index bea346e362b2b7..f660ebfa137995 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -2066,7 +2066,6 @@ static int omap_udc_start(struct usb_gadget *g,
udc->softconnect = 1;
/* hook up the driver */
- driver->driver.bus = NULL;
udc->driver = driver;
spin_unlock_irqrestore(&udc->lock, flags);
diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
index 9bb7a9d7a2fb24..4f8617210d8526 100644
--- a/drivers/usb/gadget/udc/pch_udc.c
+++ b/drivers/usb/gadget/udc/pch_udc.c
@@ -2908,7 +2908,6 @@ static int pch_udc_start(struct usb_gadget *g,
{
struct pch_udc_dev *dev = to_pch_udc(g);
- driver->driver.bus = NULL;
dev->driver = driver;
/* get ready for ep0 traffic */
diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c
index 52ea4dcf6a9275..2fc5d4d277bc4a 100644
--- a/drivers/usb/gadget/udc/snps_udc_core.c
+++ b/drivers/usb/gadget/udc/snps_udc_core.c
@@ -1933,7 +1933,6 @@ static int amd5536_udc_start(struct usb_gadget *g,
struct udc *dev = to_amd5536_udc(g);
u32 tmp;
- driver->driver.bus = NULL;
dev->driver = driver;
/* Some gadget drivers use both ep0 directions.
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index 9cea785934e596..38d06e5abfbb35 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -29,7 +29,7 @@
#include "ehci-fsl.h"
#define DRIVER_DESC "Freescale EHCI Host controller driver"
-#define DRV_NAME "ehci-fsl"
+#define DRV_NAME "fsl-ehci"
static struct hc_driver __read_mostly fsl_ehci_hc_driver;
diff --git a/drivers/usb/host/xen-hcd.c b/drivers/usb/host/xen-hcd.c
index de1b091583183f..46fdab940092e3 100644
--- a/drivers/usb/host/xen-hcd.c
+++ b/drivers/usb/host/xen-hcd.c
@@ -1530,15 +1530,13 @@ static void xenhcd_backend_changed(struct xenbus_device *dev,
}
}
-static int xenhcd_remove(struct xenbus_device *dev)
+static void xenhcd_remove(struct xenbus_device *dev)
{
struct xenhcd_info *info = dev_get_drvdata(&dev->dev);
struct usb_hcd *hcd = xenhcd_info_to_hcd(info);
xenhcd_destroy_rings(info);
usb_put_hcd(hcd);
-
- return 0;
}
static int xenhcd_probe(struct xenbus_device *dev,
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 79d679b3e07607..fb988e4ea9244e 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -78,9 +78,12 @@ static const char hcd_name[] = "xhci_hcd";
static struct hc_driver __read_mostly xhci_pci_hc_driver;
static int xhci_pci_setup(struct usb_hcd *hcd);
+static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
+ struct usb_tt *tt, gfp_t mem_flags);
static const struct xhci_driver_overrides xhci_pci_overrides __initconst = {
.reset = xhci_pci_setup,
+ .update_hub_device = xhci_pci_update_hub_device,
};
/* called after powerup, by probe or system-pm "wakeup" */
@@ -352,8 +355,38 @@ static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev)
NULL);
ACPI_FREE(obj);
}
+
+static void xhci_find_lpm_incapable_ports(struct usb_hcd *hcd, struct usb_device *hdev)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct xhci_hub *rhub = &xhci->usb3_rhub;
+ int ret;
+ int i;
+
+ /* This is not the usb3 roothub we are looking for */
+ if (hcd != rhub->hcd)
+ return;
+
+ if (hdev->maxchild > rhub->num_ports) {
+ dev_err(&hdev->dev, "USB3 roothub port number mismatch\n");
+ return;
+ }
+
+ for (i = 0; i < hdev->maxchild; i++) {
+ ret = usb_acpi_port_lpm_incapable(hdev, i);
+
+ dev_dbg(&hdev->dev, "port-%d disable U1/U2 _DSM: %d\n", i + 1, ret);
+
+ if (ret >= 0) {
+ rhub->ports[i]->lpm_incapable = ret;
+ continue;
+ }
+ }
+}
+
#else
static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) { }
+static void xhci_find_lpm_incapable_ports(struct usb_hcd *hcd, struct usb_device *hdev) { }
#endif /* CONFIG_ACPI */
/* called during probe() after chip reset completes */
@@ -386,6 +419,16 @@ static int xhci_pci_setup(struct usb_hcd *hcd)
return xhci_pci_reinit(xhci, pdev);
}
+static int xhci_pci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
+ struct usb_tt *tt, gfp_t mem_flags)
+{
+ /* Check if acpi claims some USB3 roothub ports are lpm incapable */
+ if (!hdev->parent)
+ xhci_find_lpm_incapable_ports(hcd, hdev);
+
+ return xhci_update_hub_device(hcd, hdev, tt, mem_flags);
+}
+
/*
* We need to register our own PCI probe function (instead of the USB core's
* function) in order to create a second roothub under xHCI.
@@ -455,6 +498,8 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (xhci->quirks & XHCI_DEFAULT_PM_RUNTIME_ALLOW)
pm_runtime_allow(&dev->dev);
+ dma_set_max_seg_size(&dev->dev, UINT_MAX);
+
return 0;
put_usb3_hcd:
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index ddc30037f9cefa..f5b0e1ce22af20 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1169,7 +1169,10 @@ static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci,
struct xhci_virt_ep *ep;
struct xhci_ring *ring;
- ep = &xhci->devs[slot_id]->eps[ep_index];
+ ep = xhci_get_virt_ep(xhci, slot_id, ep_index);
+ if (!ep)
+ return;
+
if ((ep->ep_state & EP_HAS_STREAMS) ||
(ep->ep_state & EP_GETTING_NO_STREAMS)) {
int stream_id;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 79d7931c048a8c..2b280beb001150 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -3974,6 +3974,7 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_virt_device *virt_dev;
struct xhci_slot_ctx *slot_ctx;
+ unsigned long flags;
int i, ret;
/*
@@ -4000,7 +4001,11 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
virt_dev->eps[i].ep_state &= ~EP_STOP_CMD_PENDING;
virt_dev->udev = NULL;
xhci_disable_slot(xhci, udev->slot_id);
+
+ spin_lock_irqsave(&xhci->lock, flags);
xhci_free_virt_device(xhci, udev->slot_id);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+
}
int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
@@ -5044,6 +5049,7 @@ static int xhci_enable_usb3_lpm_timeout(struct usb_hcd *hcd,
struct usb_device *udev, enum usb3_link_state state)
{
struct xhci_hcd *xhci;
+ struct xhci_port *port;
u16 hub_encoded_timeout;
int mel;
int ret;
@@ -5060,6 +5066,13 @@ static int xhci_enable_usb3_lpm_timeout(struct usb_hcd *hcd,
if (xhci_check_tier_policy(xhci, udev, state) < 0)
return USB3_LPM_DISABLED;
+ /* If connected to root port then check port can handle lpm */
+ if (udev->parent && !udev->parent->parent) {
+ port = xhci->usb3_rhub.ports[udev->portnum - 1];
+ if (port->lpm_incapable)
+ return USB3_LPM_DISABLED;
+ }
+
hub_encoded_timeout = xhci_calculate_lpm_timeout(hcd, udev, state);
mel = calculate_max_exit_latency(udev, state, hub_encoded_timeout);
if (mel < 0) {
@@ -5119,7 +5132,7 @@ static int xhci_disable_usb3_lpm_timeout(struct usb_hcd *hcd,
/* Once a hub descriptor is fetched for a device, we need to update the xHC's
* internal data structures for the device.
*/
-static int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
+int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
struct usb_tt *tt, gfp_t mem_flags)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
@@ -5219,6 +5232,7 @@ static int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
xhci_free_command(xhci, config_cmd);
return ret;
}
+EXPORT_SYMBOL_GPL(xhci_update_hub_device);
static int xhci_get_frame(struct usb_hcd *hcd)
{
@@ -5502,6 +5516,8 @@ void xhci_init_driver(struct hc_driver *drv,
drv->check_bandwidth = over->check_bandwidth;
if (over->reset_bandwidth)
drv->reset_bandwidth = over->reset_bandwidth;
+ if (over->update_hub_device)
+ drv->update_hub_device = over->update_hub_device;
}
}
EXPORT_SYMBOL_GPL(xhci_init_driver);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index c9f06c5e4e9d2a..dcee7f3207add0 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1735,6 +1735,7 @@ struct xhci_port {
int hcd_portnum;
struct xhci_hub *rhub;
struct xhci_port_cap *port_cap;
+ unsigned int lpm_incapable:1;
};
struct xhci_hub {
@@ -1943,6 +1944,8 @@ struct xhci_driver_overrides {
struct usb_host_endpoint *ep);
int (*check_bandwidth)(struct usb_hcd *, struct usb_device *);
void (*reset_bandwidth)(struct usb_hcd *, struct usb_device *);
+ int (*update_hub_device)(struct usb_hcd *hcd, struct usb_device *hdev,
+ struct usb_tt *tt, gfp_t mem_flags);
};
#define XHCI_CFC_DELAY 10
@@ -2122,6 +2125,8 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
struct usb_host_endpoint *ep);
int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
+int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev,
+ struct usb_tt *tt, gfp_t mem_flags);
int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id);
int xhci_ext_cap_init(struct xhci_hcd *xhci);
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index f9427a67789cf7..1e3df27bab58fd 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -814,7 +814,7 @@ static int iowarrior_probe(struct usb_interface *interface,
break;
case USB_DEVICE_ID_CODEMERCS_IOW100:
- dev->report_size = 13;
+ dev->report_size = 12;
break;
}
}
diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c
index 94e7966e199d19..969c4c4f2ae92d 100644
--- a/drivers/usb/misc/onboard_usb_hub.c
+++ b/drivers/usb/misc/onboard_usb_hub.c
@@ -27,7 +27,10 @@
#include "onboard_usb_hub.h"
+static void onboard_hub_attach_usb_driver(struct work_struct *work);
+
static struct usb_device_driver onboard_hub_usbdev_driver;
+static DECLARE_WORK(attach_usb_driver_work, onboard_hub_attach_usb_driver);
/************************** Platform driver **************************/
@@ -45,7 +48,6 @@ struct onboard_hub {
bool is_powered_on;
bool going_away;
struct list_head udev_list;
- struct work_struct attach_usb_driver_work;
struct mutex lock;
};
@@ -271,8 +273,7 @@ static int onboard_hub_probe(struct platform_device *pdev)
* This needs to be done deferred to avoid self-deadlocks on systems
* with nested onboard hubs.
*/
- INIT_WORK(&hub->attach_usb_driver_work, onboard_hub_attach_usb_driver);
- schedule_work(&hub->attach_usb_driver_work);
+ schedule_work(&attach_usb_driver_work);
return 0;
}
@@ -285,9 +286,6 @@ static int onboard_hub_remove(struct platform_device *pdev)
hub->going_away = true;
- if (&hub->attach_usb_driver_work != current_work())
- cancel_work_sync(&hub->attach_usb_driver_work);
-
mutex_lock(&hub->lock);
/* unbind the USB devices to avoid dangling references to this device */
@@ -433,13 +431,13 @@ static int __init onboard_hub_init(void)
{
int ret;
- ret = platform_driver_register(&onboard_hub_driver);
+ ret = usb_register_device_driver(&onboard_hub_usbdev_driver, THIS_MODULE);
if (ret)
return ret;
- ret = usb_register_device_driver(&onboard_hub_usbdev_driver, THIS_MODULE);
+ ret = platform_driver_register(&onboard_hub_driver);
if (ret)
- platform_driver_unregister(&onboard_hub_driver);
+ usb_deregister_device_driver(&onboard_hub_usbdev_driver);
return ret;
}
@@ -449,6 +447,8 @@ static void __exit onboard_hub_exit(void)
{
usb_deregister_device_driver(&onboard_hub_usbdev_driver);
platform_driver_unregister(&onboard_hub_driver);
+
+ cancel_work_sync(&attach_usb_driver_work);
}
module_exit(onboard_hub_exit);
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index 476f55d1fec308..44a21ec865fb21 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -411,8 +411,10 @@ static int omap2430_probe(struct platform_device *pdev)
memset(musb_res, 0, sizeof(*musb_res) * ARRAY_SIZE(musb_res));
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
+ if (!res) {
+ ret = -EINVAL;
goto err2;
+ }
musb_res[i].start = res->start;
musb_res[i].end = res->end;
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 67372acc23529d..832ad592b7ef3b 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -60,6 +60,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x0846, 0x1100) }, /* NetGear Managed Switch M4100 series, M5300 series, M7100 series */
{ USB_DEVICE(0x08e6, 0x5501) }, /* Gemalto Prox-PU/CU contactless smartcard reader */
{ USB_DEVICE(0x08FD, 0x000A) }, /* Digianswer A/S , ZigBee/802.15.4 MAC Device */
+ { USB_DEVICE(0x0908, 0x0070) }, /* Siemens SCALANCE LPE-9000 USB Serial Console */
{ USB_DEVICE(0x0908, 0x01FF) }, /* Siemens RUGGEDCOM USB Serial Console */
{ USB_DEVICE(0x0988, 0x0578) }, /* Teraoka AD2000 */
{ USB_DEVICE(0x0B00, 0x3070) }, /* Ingenico 3070 */
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index dee79c7d82d5ce..ee5ac4ef7e1620 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -255,10 +255,16 @@ static void option_instat_callback(struct urb *urb);
#define QUECTEL_PRODUCT_EP06 0x0306
#define QUECTEL_PRODUCT_EM05G 0x030a
#define QUECTEL_PRODUCT_EM060K 0x030b
+#define QUECTEL_PRODUCT_EM05G_CS 0x030c
+#define QUECTEL_PRODUCT_EM05CN_SG 0x0310
#define QUECTEL_PRODUCT_EM05G_SG 0x0311
+#define QUECTEL_PRODUCT_EM05CN 0x0312
+#define QUECTEL_PRODUCT_EM05G_GR 0x0313
+#define QUECTEL_PRODUCT_EM05G_RS 0x0314
#define QUECTEL_PRODUCT_EM12 0x0512
#define QUECTEL_PRODUCT_RM500Q 0x0800
#define QUECTEL_PRODUCT_RM520N 0x0801
+#define QUECTEL_PRODUCT_EC200U 0x0901
#define QUECTEL_PRODUCT_EC200S_CN 0x6002
#define QUECTEL_PRODUCT_EC200T 0x6026
#define QUECTEL_PRODUCT_RM500K 0x7001
@@ -1159,8 +1165,18 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0xff, 0xff),
.driver_info = RSVD(1) | RSVD(2) | RSVD(3) | RSVD(4) | NUMEP2 },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EP06, 0xff, 0, 0) },
+ { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05CN, 0xff),
+ .driver_info = RSVD(6) | ZLP },
+ { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05CN_SG, 0xff),
+ .driver_info = RSVD(6) | ZLP },
{ USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G, 0xff),
.driver_info = RSVD(6) | ZLP },
+ { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G_CS, 0xff),
+ .driver_info = RSVD(6) | ZLP },
+ { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G_GR, 0xff),
+ .driver_info = RSVD(6) | ZLP },
+ { USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G_RS, 0xff),
+ .driver_info = RSVD(6) | ZLP },
{ USB_DEVICE_INTERFACE_CLASS(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM05G_SG, 0xff),
.driver_info = RSVD(6) | ZLP },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EM060K, 0xff, 0x00, 0x40) },
@@ -1180,6 +1196,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM520N, 0xff, 0xff, 0x30) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM520N, 0xff, 0, 0x40) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM520N, 0xff, 0, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200U, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200S_CN, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200T, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500K, 0xff, 0x00, 0x00) },
diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h
index 3f720faa6f97c1..d73282c0ec5017 100644
--- a/drivers/usb/storage/uas-detect.h
+++ b/drivers/usb/storage/uas-detect.h
@@ -116,6 +116,19 @@ static int uas_use_uas_driver(struct usb_interface *intf,
if (le16_to_cpu(udev->descriptor.idVendor) == 0x0bc2)
flags |= US_FL_NO_ATA_1X;
+ /*
+ * RTL9210-based enclosure from HIKSEMI, MD202 reportedly have issues
+ * with UAS. This isn't distinguishable with just idVendor and
+ * idProduct, use manufacturer and product too.
+ *
+ * Reported-by: Hongling Zeng <zenghongling@kylinos.cn>
+ */
+ if (le16_to_cpu(udev->descriptor.idVendor) == 0x0bda &&
+ le16_to_cpu(udev->descriptor.idProduct) == 0x9210 &&
+ (udev->manufacturer && !strcmp(udev->manufacturer, "HIKSEMI")) &&
+ (udev->product && !strcmp(udev->product, "MD202")))
+ flags |= US_FL_IGNORE_UAS;
+
usb_stor_adjust_quirks(udev, &flags);
if (flags & US_FL_IGNORE_UAS) {
diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h
index 251778d14e2dd4..c7b763d6d10239 100644
--- a/drivers/usb/storage/unusual_uas.h
+++ b/drivers/usb/storage/unusual_uas.h
@@ -83,13 +83,6 @@ UNUSUAL_DEV(0x0bc2, 0x331a, 0x0000, 0x9999,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_REPORT_LUNS),
-/* Reported-by: Hongling Zeng <zenghongling@kylinos.cn> */
-UNUSUAL_DEV(0x0bda, 0x9210, 0x0000, 0x9999,
- "Hiksemi",
- "External HDD",
- USB_SC_DEVICE, USB_PR_DEVICE, NULL,
- US_FL_IGNORE_UAS),
-
/* Reported-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> */
UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x9999,
"Initio Corporation",
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index de66a2949e33b2..50b24096eb7f16 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -419,6 +419,18 @@ static const char * const pin_assignments[] = {
[DP_PIN_ASSIGN_F] = "F",
};
+/*
+ * Helper function to extract a peripheral's currently supported
+ * Pin Assignments from its DisplayPort alternate mode state.
+ */
+static u8 get_current_pin_assignments(struct dp_altmode *dp)
+{
+ if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_UFP_U_AS_DFP_D)
+ return DP_CAP_PIN_ASSIGN_DFP_D(dp->alt->vdo);
+ else
+ return DP_CAP_PIN_ASSIGN_UFP_D(dp->alt->vdo);
+}
+
static ssize_t
pin_assignment_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t size)
@@ -445,10 +457,7 @@ pin_assignment_store(struct device *dev, struct device_attribute *attr,
goto out_unlock;
}
- if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_DFP_D)
- assignments = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo);
- else
- assignments = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo);
+ assignments = get_current_pin_assignments(dp);
if (!(DP_CONF_GET_PIN_ASSIGN(conf) & assignments)) {
ret = -EINVAL;
@@ -485,10 +494,7 @@ static ssize_t pin_assignment_show(struct device *dev,
cur = get_count_order(DP_CONF_GET_PIN_ASSIGN(dp->data.conf));
- if (DP_CONF_CURRENTLY(dp->data.conf) == DP_CONF_DFP_D)
- assignments = DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo);
- else
- assignments = DP_CAP_DFP_D_PIN_ASSIGN(dp->alt->vdo);
+ assignments = get_current_pin_assignments(dp);
for (i = 0; assignments; assignments >>= 1, i++) {
if (assignments & 1) {
@@ -529,10 +535,10 @@ int dp_altmode_probe(struct typec_altmode *alt)
/* FIXME: Port can only be DFP_U. */
/* Make sure we have compatiple pin configurations */
- if (!(DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) &
- DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo)) &&
- !(DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) &
- DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo)))
+ if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) &
+ DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) &&
+ !(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) &
+ DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo)))
return -ENODEV;
ret = sysfs_create_group(&alt->dev.kobj, &dp_altmode_group);
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 904c7b4ce2f0c2..59b366b5c61447 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -4594,14 +4594,13 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_state(port, ready_state(port), 0);
break;
case DR_SWAP_CHANGE_DR:
- if (port->data_role == TYPEC_HOST) {
- tcpm_unregister_altmodes(port);
+ tcpm_unregister_altmodes(port);
+ if (port->data_role == TYPEC_HOST)
tcpm_set_roles(port, true, port->pwr_role,
TYPEC_DEVICE);
- } else {
+ else
tcpm_set_roles(port, true, port->pwr_role,
TYPEC_HOST);
- }
tcpm_ams_finish(port);
tcpm_set_state(port, ready_state(port), 0);
break;
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index eabe519013e785..1cf8947c6d6611 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -187,6 +187,7 @@ EXPORT_SYMBOL_GPL(ucsi_send_command);
struct ucsi_work {
struct delayed_work work;
+ struct list_head node;
unsigned long delay;
unsigned int count;
struct ucsi_connector *con;
@@ -202,6 +203,7 @@ static void ucsi_poll_worker(struct work_struct *work)
mutex_lock(&con->lock);
if (!con->partner) {
+ list_del(&uwork->node);
mutex_unlock(&con->lock);
kfree(uwork);
return;
@@ -209,10 +211,12 @@ static void ucsi_poll_worker(struct work_struct *work)
ret = uwork->cb(con);
- if (uwork->count-- && (ret == -EBUSY || ret == -ETIMEDOUT))
+ if (uwork->count-- && (ret == -EBUSY || ret == -ETIMEDOUT)) {
queue_delayed_work(con->wq, &uwork->work, uwork->delay);
- else
+ } else {
+ list_del(&uwork->node);
kfree(uwork);
+ }
mutex_unlock(&con->lock);
}
@@ -236,6 +240,7 @@ static int ucsi_partner_task(struct ucsi_connector *con,
uwork->con = con;
uwork->cb = cb;
+ list_add_tail(&uwork->node, &con->partner_tasks);
queue_delayed_work(con->wq, &uwork->work, delay);
return 0;
@@ -1056,6 +1061,7 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
INIT_WORK(&con->work, ucsi_handle_connector_change);
init_completion(&con->complete);
mutex_init(&con->lock);
+ INIT_LIST_HEAD(&con->partner_tasks);
con->num = index + 1;
con->ucsi = ucsi;
@@ -1263,6 +1269,9 @@ err_unregister:
con->port = NULL;
}
+ kfree(ucsi->connector);
+ ucsi->connector = NULL;
+
err_reset:
memset(&ucsi->cap, 0, sizeof(ucsi->cap));
ucsi_reset_ppm(ucsi);
@@ -1294,7 +1303,8 @@ static void ucsi_resume_work(struct work_struct *work)
int ucsi_resume(struct ucsi *ucsi)
{
- queue_work(system_long_wq, &ucsi->resume_work);
+ if (ucsi->connector)
+ queue_work(system_long_wq, &ucsi->resume_work);
return 0;
}
EXPORT_SYMBOL_GPL(ucsi_resume);
@@ -1414,14 +1424,29 @@ void ucsi_unregister(struct ucsi *ucsi)
/* Disable notifications */
ucsi->ops->async_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
+ if (!ucsi->connector)
+ return;
+
for (i = 0; i < ucsi->cap.num_connectors; i++) {
cancel_work_sync(&ucsi->connector[i].work);
ucsi_unregister_partner(&ucsi->connector[i]);
ucsi_unregister_altmodes(&ucsi->connector[i],
UCSI_RECIPIENT_CON);
ucsi_unregister_port_psy(&ucsi->connector[i]);
- if (ucsi->connector[i].wq)
+
+ if (ucsi->connector[i].wq) {
+ struct ucsi_work *uwork;
+
+ mutex_lock(&ucsi->connector[i].lock);
+ /*
+ * queue delayed items immediately so they can execute
+ * and free themselves before the wq is destroyed
+ */
+ list_for_each_entry(uwork, &ucsi->connector[i].partner_tasks, node)
+ mod_delayed_work(ucsi->connector[i].wq, &uwork->work, 0);
+ mutex_unlock(&ucsi->connector[i].lock);
destroy_workqueue(ucsi->connector[i].wq);
+ }
typec_unregister_port(ucsi->connector[i].port);
}
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index c968474ee54739..60ce9fb6e7450f 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -322,6 +322,7 @@ struct ucsi_connector {
struct work_struct work;
struct completion complete;
struct workqueue_struct *wq;
+ struct list_head partner_tasks;
struct typec_port *port;
struct typec_partner *partner;
diff --git a/drivers/vdpa/ifcvf/ifcvf_main.c b/drivers/vdpa/ifcvf/ifcvf_main.c
index f9c0044c6442e4..44b29289aa1938 100644
--- a/drivers/vdpa/ifcvf/ifcvf_main.c
+++ b/drivers/vdpa/ifcvf/ifcvf_main.c
@@ -849,7 +849,7 @@ static int ifcvf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
ret = ifcvf_init_hw(vf, pdev);
if (ret) {
IFCVF_ERR(pdev, "Failed to init IFCVF hw\n");
- return ret;
+ goto err;
}
for (i = 0; i < vf->nr_vring; i++)
diff --git a/drivers/vdpa/mlx5/core/mlx5_vdpa.h b/drivers/vdpa/mlx5/core/mlx5_vdpa.h
index 6af9fdbb86b7a4..058fbe28107e9e 100644
--- a/drivers/vdpa/mlx5/core/mlx5_vdpa.h
+++ b/drivers/vdpa/mlx5/core/mlx5_vdpa.h
@@ -116,8 +116,9 @@ int mlx5_vdpa_create_mkey(struct mlx5_vdpa_dev *mvdev, u32 *mkey, u32 *in,
int inlen);
int mlx5_vdpa_destroy_mkey(struct mlx5_vdpa_dev *mvdev, u32 mkey);
int mlx5_vdpa_handle_set_map(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb,
- bool *change_map);
-int mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb);
+ bool *change_map, unsigned int asid);
+int mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb,
+ unsigned int asid);
void mlx5_vdpa_destroy_mr(struct mlx5_vdpa_dev *mvdev);
#define mlx5_vdpa_warn(__dev, format, ...) \
diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c
index a639b9208d4148..0a1e0b0dc37e3d 100644
--- a/drivers/vdpa/mlx5/core/mr.c
+++ b/drivers/vdpa/mlx5/core/mr.c
@@ -311,7 +311,6 @@ static int add_direct_chain(struct mlx5_vdpa_dev *mvdev, u64 start, u64 size, u8
u64 st;
u64 sz;
int err;
- int i = 0;
st = start;
while (size) {
@@ -336,7 +335,6 @@ static int add_direct_chain(struct mlx5_vdpa_dev *mvdev, u64 start, u64 size, u8
mr->num_directs++;
mr->num_klms++;
st += sz;
- i++;
}
list_splice_tail(&tmp, &mr->head);
return 0;
@@ -511,7 +509,8 @@ out:
mutex_unlock(&mr->mkey_mtx);
}
-static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb)
+static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev,
+ struct vhost_iotlb *iotlb, unsigned int asid)
{
struct mlx5_vdpa_mr *mr = &mvdev->mr;
int err;
@@ -519,42 +518,49 @@ static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb
if (mr->initialized)
return 0;
- if (iotlb)
- err = create_user_mr(mvdev, iotlb);
- else
- err = create_dma_mr(mvdev, mr);
+ if (mvdev->group2asid[MLX5_VDPA_DATAVQ_GROUP] == asid) {
+ if (iotlb)
+ err = create_user_mr(mvdev, iotlb);
+ else
+ err = create_dma_mr(mvdev, mr);
- if (err)
- return err;
+ if (err)
+ return err;
+ }
- err = dup_iotlb(mvdev, iotlb);
- if (err)
- goto out_err;
+ if (mvdev->group2asid[MLX5_VDPA_CVQ_GROUP] == asid) {
+ err = dup_iotlb(mvdev, iotlb);
+ if (err)
+ goto out_err;
+ }
mr->initialized = true;
return 0;
out_err:
- if (iotlb)
- destroy_user_mr(mvdev, mr);
- else
- destroy_dma_mr(mvdev, mr);
+ if (mvdev->group2asid[MLX5_VDPA_DATAVQ_GROUP] == asid) {
+ if (iotlb)
+ destroy_user_mr(mvdev, mr);
+ else
+ destroy_dma_mr(mvdev, mr);
+ }
return err;
}
-int mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb)
+int mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb,
+ unsigned int asid)
{
int err;
mutex_lock(&mvdev->mr.mkey_mtx);
- err = _mlx5_vdpa_create_mr(mvdev, iotlb);
+ err = _mlx5_vdpa_create_mr(mvdev, iotlb, asid);
mutex_unlock(&mvdev->mr.mkey_mtx);
return err;
}
int mlx5_vdpa_handle_set_map(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb,
- bool *change_map)
+ bool *change_map, unsigned int asid)
{
struct mlx5_vdpa_mr *mr = &mvdev->mr;
int err = 0;
@@ -566,7 +572,7 @@ int mlx5_vdpa_handle_set_map(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *io
*change_map = true;
}
if (!*change_map)
- err = _mlx5_vdpa_create_mr(mvdev, iotlb);
+ err = _mlx5_vdpa_create_mr(mvdev, iotlb, asid);
mutex_unlock(&mr->mkey_mtx);
return err;
diff --git a/drivers/vdpa/mlx5/net/mlx5_vnet.c b/drivers/vdpa/mlx5/net/mlx5_vnet.c
index 90913365def43d..3a6dbbc6440d45 100644
--- a/drivers/vdpa/mlx5/net/mlx5_vnet.c
+++ b/drivers/vdpa/mlx5/net/mlx5_vnet.c
@@ -1468,11 +1468,13 @@ static int mlx5_vdpa_add_mac_vlan_rules(struct mlx5_vdpa_net *ndev, u8 *mac,
dmac_v = MLX5_ADDR_OF(fte_match_param, headers_v, outer_headers.dmac_47_16);
eth_broadcast_addr(dmac_c);
ether_addr_copy(dmac_v, mac);
- MLX5_SET(fte_match_set_lyr_2_4, headers_c, cvlan_tag, 1);
+ if (ndev->mvdev.actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VLAN)) {
+ MLX5_SET(fte_match_set_lyr_2_4, headers_c, cvlan_tag, 1);
+ MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, first_vid);
+ }
if (tagged) {
MLX5_SET(fte_match_set_lyr_2_4, headers_v, cvlan_tag, 1);
- MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, headers_c, first_vid);
- MLX5_SET(fte_match_set_lyr_2_4, headers_c, first_vid, vid);
+ MLX5_SET(fte_match_set_lyr_2_4, headers_v, first_vid, vid);
}
flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR;
@@ -1684,7 +1686,7 @@ static virtio_net_ctrl_ack handle_ctrl_mac(struct mlx5_vdpa_dev *mvdev, u8 cmd)
/* Need recreate the flow table entry, so that the packet could forward back
*/
- mac_vlan_del(ndev, ndev->config.mac, 0, false);
+ mac_vlan_del(ndev, mac_back, 0, false);
if (mac_vlan_add(ndev, ndev->config.mac, 0, false)) {
mlx5_vdpa_warn(mvdev, "failed to insert forward rules, try to restore\n");
@@ -1821,6 +1823,9 @@ static virtio_net_ctrl_ack handle_ctrl_vlan(struct mlx5_vdpa_dev *mvdev, u8 cmd)
size_t read;
u16 id;
+ if (!(ndev->mvdev.actual_features & BIT_ULL(VIRTIO_NET_F_CTRL_VLAN)))
+ return status;
+
switch (cmd) {
case VIRTIO_NET_CTRL_VLAN_ADD:
read = vringh_iov_pull_iotlb(&cvq->vring, &cvq->riov, &vlan, sizeof(vlan));
@@ -2389,7 +2394,8 @@ static void restore_channels_info(struct mlx5_vdpa_net *ndev)
}
}
-static int mlx5_vdpa_change_map(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb)
+static int mlx5_vdpa_change_map(struct mlx5_vdpa_dev *mvdev,
+ struct vhost_iotlb *iotlb, unsigned int asid)
{
struct mlx5_vdpa_net *ndev = to_mlx5_vdpa_ndev(mvdev);
int err;
@@ -2401,7 +2407,7 @@ static int mlx5_vdpa_change_map(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb
teardown_driver(ndev);
mlx5_vdpa_destroy_mr(mvdev);
- err = mlx5_vdpa_create_mr(mvdev, iotlb);
+ err = mlx5_vdpa_create_mr(mvdev, iotlb, asid);
if (err)
goto err_mr;
@@ -2582,7 +2588,7 @@ static int mlx5_vdpa_reset(struct vdpa_device *vdev)
++mvdev->generation;
if (MLX5_CAP_GEN(mvdev->mdev, umem_uid_0)) {
- if (mlx5_vdpa_create_mr(mvdev, NULL))
+ if (mlx5_vdpa_create_mr(mvdev, NULL, 0))
mlx5_vdpa_warn(mvdev, "create MR failed\n");
}
up_write(&ndev->reslock);
@@ -2618,41 +2624,20 @@ static u32 mlx5_vdpa_get_generation(struct vdpa_device *vdev)
return mvdev->generation;
}
-static int set_map_control(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb)
-{
- u64 start = 0ULL, last = 0ULL - 1;
- struct vhost_iotlb_map *map;
- int err = 0;
-
- spin_lock(&mvdev->cvq.iommu_lock);
- vhost_iotlb_reset(mvdev->cvq.iotlb);
-
- for (map = vhost_iotlb_itree_first(iotlb, start, last); map;
- map = vhost_iotlb_itree_next(map, start, last)) {
- err = vhost_iotlb_add_range(mvdev->cvq.iotlb, map->start,
- map->last, map->addr, map->perm);
- if (err)
- goto out;
- }
-
-out:
- spin_unlock(&mvdev->cvq.iommu_lock);
- return err;
-}
-
-static int set_map_data(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb)
+static int set_map_data(struct mlx5_vdpa_dev *mvdev, struct vhost_iotlb *iotlb,
+ unsigned int asid)
{
bool change_map;
int err;
- err = mlx5_vdpa_handle_set_map(mvdev, iotlb, &change_map);
+ err = mlx5_vdpa_handle_set_map(mvdev, iotlb, &change_map, asid);
if (err) {
mlx5_vdpa_warn(mvdev, "set map failed(%d)\n", err);
return err;
}
if (change_map)
- err = mlx5_vdpa_change_map(mvdev, iotlb);
+ err = mlx5_vdpa_change_map(mvdev, iotlb, asid);
return err;
}
@@ -2665,16 +2650,7 @@ static int mlx5_vdpa_set_map(struct vdpa_device *vdev, unsigned int asid,
int err = -EINVAL;
down_write(&ndev->reslock);
- if (mvdev->group2asid[MLX5_VDPA_DATAVQ_GROUP] == asid) {
- err = set_map_data(mvdev, iotlb);
- if (err)
- goto out;
- }
-
- if (mvdev->group2asid[MLX5_VDPA_CVQ_GROUP] == asid)
- err = set_map_control(mvdev, iotlb);
-
-out:
+ err = set_map_data(mvdev, iotlb, asid);
up_write(&ndev->reslock);
return err;
}
@@ -2840,8 +2816,8 @@ static int mlx5_vdpa_suspend(struct vdpa_device *vdev)
int i;
down_write(&ndev->reslock);
- mlx5_notifier_unregister(mvdev->mdev, &ndev->nb);
ndev->nb_registered = false;
+ mlx5_notifier_unregister(mvdev->mdev, &ndev->nb);
flush_workqueue(ndev->mvdev.wq);
for (i = 0; i < ndev->cur_num_vqs; i++) {
mvq = &ndev->vqs[i];
@@ -3019,7 +2995,7 @@ static void update_carrier(struct work_struct *work)
else
ndev->config.status &= cpu_to_mlx5vdpa16(mvdev, ~VIRTIO_NET_S_LINK_UP);
- if (ndev->config_cb.callback)
+ if (ndev->nb_registered && ndev->config_cb.callback)
ndev->config_cb.callback(ndev->config_cb.private);
kfree(wqent);
@@ -3036,21 +3012,13 @@ static int event_handler(struct notifier_block *nb, unsigned long event, void *p
switch (eqe->sub_type) {
case MLX5_PORT_CHANGE_SUBTYPE_DOWN:
case MLX5_PORT_CHANGE_SUBTYPE_ACTIVE:
- down_read(&ndev->reslock);
- if (!ndev->nb_registered) {
- up_read(&ndev->reslock);
- return NOTIFY_DONE;
- }
wqent = kzalloc(sizeof(*wqent), GFP_ATOMIC);
- if (!wqent) {
- up_read(&ndev->reslock);
+ if (!wqent)
return NOTIFY_DONE;
- }
wqent->mvdev = &ndev->mvdev;
INIT_WORK(&wqent->work, update_carrier);
queue_work(ndev->mvdev.wq, &wqent->work);
- up_read(&ndev->reslock);
ret = NOTIFY_OK;
break;
default:
@@ -3185,7 +3153,7 @@ static int mlx5_vdpa_dev_add(struct vdpa_mgmt_dev *v_mdev, const char *name,
goto err_mpfs;
if (MLX5_CAP_GEN(mvdev->mdev, umem_uid_0)) {
- err = mlx5_vdpa_create_mr(mvdev, NULL);
+ err = mlx5_vdpa_create_mr(mvdev, NULL, 0);
if (err)
goto err_res;
}
@@ -3237,8 +3205,8 @@ static void mlx5_vdpa_dev_del(struct vdpa_mgmt_dev *v_mdev, struct vdpa_device *
struct workqueue_struct *wq;
if (ndev->nb_registered) {
- mlx5_notifier_unregister(mvdev->mdev, &ndev->nb);
ndev->nb_registered = false;
+ mlx5_notifier_unregister(mvdev->mdev, &ndev->nb);
}
wq = mvdev->wq;
mvdev->wq = NULL;
diff --git a/drivers/vdpa/vdpa.c b/drivers/vdpa/vdpa.c
index febdc99b51a7b7..8ef7aa1365cc52 100644
--- a/drivers/vdpa/vdpa.c
+++ b/drivers/vdpa/vdpa.c
@@ -855,7 +855,7 @@ static int vdpa_dev_net_config_fill(struct vdpa_device *vdev, struct sk_buff *ms
features_device = vdev->config->get_device_features(vdev);
- if (nla_put_u64_64bit(msg, VDPA_ATTR_VDPA_DEV_SUPPORTED_FEATURES, features_device,
+ if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_FEATURES, features_device,
VDPA_ATTR_PAD))
return -EMSGSIZE;
@@ -935,7 +935,6 @@ static int vdpa_fill_stats_rec(struct vdpa_device *vdev, struct sk_buff *msg,
{
struct virtio_net_config config = {};
u64 features;
- u16 max_vqp;
u8 status;
int err;
@@ -946,15 +945,15 @@ static int vdpa_fill_stats_rec(struct vdpa_device *vdev, struct sk_buff *msg,
}
vdpa_get_config_unlocked(vdev, 0, &config, sizeof(config));
- max_vqp = __virtio16_to_cpu(true, config.max_virtqueue_pairs);
- if (nla_put_u16(msg, VDPA_ATTR_DEV_NET_CFG_MAX_VQP, max_vqp))
- return -EMSGSIZE;
-
features = vdev->config->get_driver_features(vdev);
if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_NEGOTIATED_FEATURES,
features, VDPA_ATTR_PAD))
return -EMSGSIZE;
+ err = vdpa_dev_net_mq_config_fill(msg, features, &config);
+ if (err)
+ return err;
+
if (nla_put_u32(msg, VDPA_ATTR_DEV_QUEUE_INDEX, index))
return -EMSGSIZE;
diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c
index b071f0d842fbad..cb88891b44a8c4 100644
--- a/drivers/vdpa/vdpa_sim/vdpa_sim.c
+++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c
@@ -67,8 +67,7 @@ static void vdpasim_queue_ready(struct vdpasim *vdpasim, unsigned int idx)
{
struct vdpasim_virtqueue *vq = &vdpasim->vqs[idx];
- vringh_init_iotlb(&vq->vring, vdpasim->dev_attr.supported_features,
- VDPASIM_QUEUE_MAX, false,
+ vringh_init_iotlb(&vq->vring, vdpasim->features, vq->num, false,
(struct vring_desc *)(uintptr_t)vq->desc_addr,
(struct vring_avail *)
(uintptr_t)vq->driver_addr,
@@ -690,7 +689,9 @@ static void vdpasim_free(struct vdpa_device *vdpa)
}
kvfree(vdpasim->buffer);
- vhost_iotlb_free(vdpasim->iommu);
+ for (i = 0; i < vdpasim->dev_attr.nas; i++)
+ vhost_iotlb_reset(&vdpasim->iommu[i]);
+ kfree(vdpasim->iommu);
kfree(vdpasim->vqs);
kfree(vdpasim->config);
}
diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c
index c6db1a1baf7685..f745926237a88d 100644
--- a/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c
+++ b/drivers/vdpa/vdpa_sim/vdpa_sim_blk.c
@@ -427,8 +427,10 @@ static int __init vdpasim_blk_init(void)
int ret;
ret = device_register(&vdpasim_blk_mgmtdev);
- if (ret)
+ if (ret) {
+ put_device(&vdpasim_blk_mgmtdev);
return ret;
+ }
ret = vdpa_mgmtdev_register(&mgmt_dev);
if (ret)
diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim_net.c b/drivers/vdpa/vdpa_sim/vdpa_sim_net.c
index c3cb225ea4693a..584b975a98a7ef 100644
--- a/drivers/vdpa/vdpa_sim/vdpa_sim_net.c
+++ b/drivers/vdpa/vdpa_sim/vdpa_sim_net.c
@@ -62,6 +62,9 @@ static bool receive_filter(struct vdpasim *vdpasim, size_t len)
if (len < ETH_ALEN + hdr_len)
return false;
+ if (is_broadcast_ether_addr(vdpasim->buffer + hdr_len) ||
+ is_multicast_ether_addr(vdpasim->buffer + hdr_len))
+ return true;
if (!strncmp(vdpasim->buffer + hdr_len, vio_config->mac, ETH_ALEN))
return true;
@@ -305,8 +308,10 @@ static int __init vdpasim_net_init(void)
int ret;
ret = device_register(&vdpasim_net_mgmtdev);
- if (ret)
+ if (ret) {
+ put_device(&vdpasim_net_mgmtdev);
return ret;
+ }
ret = vdpa_mgmtdev_register(&mgmt_dev);
if (ret)
diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c
index 0dd3c1f291da30..0c3b48616a9f36 100644
--- a/drivers/vdpa/vdpa_user/vduse_dev.c
+++ b/drivers/vdpa/vdpa_user/vduse_dev.c
@@ -1440,6 +1440,9 @@ static bool vduse_validate_config(struct vduse_dev_config *config)
if (config->config_size > PAGE_SIZE)
return false;
+ if (config->vq_num > 0xffff)
+ return false;
+
if (!device_is_allowed(config->device_id))
return false;
diff --git a/drivers/vdpa/virtio_pci/vp_vdpa.c b/drivers/vdpa/virtio_pci/vp_vdpa.c
index d448db0c4de3f8..8fe267ca3e76f2 100644
--- a/drivers/vdpa/virtio_pci/vp_vdpa.c
+++ b/drivers/vdpa/virtio_pci/vp_vdpa.c
@@ -647,7 +647,7 @@ static void vp_vdpa_remove(struct pci_dev *pdev)
mdev = vp_vdpa_mgtdev->mdev;
vp_modern_remove(mdev);
vdpa_mgmtdev_unregister(&vp_vdpa_mgtdev->mgtdev);
- kfree(&vp_vdpa_mgtdev->mgtdev.id_table);
+ kfree(vp_vdpa_mgtdev->mgtdev.id_table);
kfree(mdev);
kfree(vp_vdpa_mgtdev);
}
diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c
index 23c24fe98c00d4..2209372f236dbb 100644
--- a/drivers/vfio/vfio_iommu_type1.c
+++ b/drivers/vfio/vfio_iommu_type1.c
@@ -1856,24 +1856,33 @@ unwind:
* significantly boosts non-hugetlbfs mappings and doesn't seem to hurt when
* hugetlbfs is in use.
*/
-static void vfio_test_domain_fgsp(struct vfio_domain *domain)
+static void vfio_test_domain_fgsp(struct vfio_domain *domain, struct list_head *regions)
{
- struct page *pages;
int ret, order = get_order(PAGE_SIZE * 2);
+ struct vfio_iova *region;
+ struct page *pages;
+ dma_addr_t start;
pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
if (!pages)
return;
- ret = iommu_map(domain->domain, 0, page_to_phys(pages), PAGE_SIZE * 2,
- IOMMU_READ | IOMMU_WRITE | IOMMU_CACHE);
- if (!ret) {
- size_t unmapped = iommu_unmap(domain->domain, 0, PAGE_SIZE);
+ list_for_each_entry(region, regions, list) {
+ start = ALIGN(region->start, PAGE_SIZE * 2);
+ if (start >= region->end || (region->end - start < PAGE_SIZE * 2))
+ continue;
- if (unmapped == PAGE_SIZE)
- iommu_unmap(domain->domain, PAGE_SIZE, PAGE_SIZE);
- else
- domain->fgsp = true;
+ ret = iommu_map(domain->domain, start, page_to_phys(pages), PAGE_SIZE * 2,
+ IOMMU_READ | IOMMU_WRITE | IOMMU_CACHE);
+ if (!ret) {
+ size_t unmapped = iommu_unmap(domain->domain, start, PAGE_SIZE);
+
+ if (unmapped == PAGE_SIZE)
+ iommu_unmap(domain->domain, start + PAGE_SIZE, PAGE_SIZE);
+ else
+ domain->fgsp = true;
+ }
+ break;
}
__free_pages(pages, order);
@@ -2326,7 +2335,7 @@ static int vfio_iommu_type1_attach_group(void *iommu_data,
}
}
- vfio_test_domain_fgsp(domain);
+ vfio_test_domain_fgsp(domain, &iova_copy);
/* replay mappings on new domains */
ret = vfio_iommu_replay(iommu, domain);
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 9af19b0cf3b7e9..4c538b30fd76de 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -1511,6 +1511,9 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd)
nvq = &n->vqs[index];
mutex_lock(&vq->mutex);
+ if (fd == -1)
+ vhost_clear_msg(&n->dev);
+
/* Verify that ring has been setup correctly. */
if (!vhost_vq_access_ok(vq)) {
r = -EFAULT;
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index dca6346d75b306..d5ecb8876fc9a1 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -80,7 +80,7 @@ struct vhost_scsi_cmd {
struct scatterlist *tvc_prot_sgl;
struct page **tvc_upages;
/* Pointer to response header iovec */
- struct iovec tvc_resp_iov;
+ struct iovec *tvc_resp_iov;
/* Pointer to vhost_scsi for our device */
struct vhost_scsi *tvc_vhost;
/* Pointer to vhost_virtqueue for the cmd */
@@ -563,7 +563,7 @@ static void vhost_scsi_complete_cmd_work(struct vhost_work *work)
memcpy(v_rsp.sense, cmd->tvc_sense_buf,
se_cmd->scsi_sense_length);
- iov_iter_init(&iov_iter, ITER_DEST, &cmd->tvc_resp_iov,
+ iov_iter_init(&iov_iter, ITER_DEST, cmd->tvc_resp_iov,
cmd->tvc_in_iovs, sizeof(v_rsp));
ret = copy_to_iter(&v_rsp, sizeof(v_rsp), &iov_iter);
if (likely(ret == sizeof(v_rsp))) {
@@ -594,6 +594,7 @@ vhost_scsi_get_cmd(struct vhost_virtqueue *vq, struct vhost_scsi_tpg *tpg,
struct vhost_scsi_cmd *cmd;
struct vhost_scsi_nexus *tv_nexus;
struct scatterlist *sg, *prot_sg;
+ struct iovec *tvc_resp_iov;
struct page **pages;
int tag;
@@ -613,6 +614,7 @@ vhost_scsi_get_cmd(struct vhost_virtqueue *vq, struct vhost_scsi_tpg *tpg,
sg = cmd->tvc_sgl;
prot_sg = cmd->tvc_prot_sgl;
pages = cmd->tvc_upages;
+ tvc_resp_iov = cmd->tvc_resp_iov;
memset(cmd, 0, sizeof(*cmd));
cmd->tvc_sgl = sg;
cmd->tvc_prot_sgl = prot_sg;
@@ -625,6 +627,7 @@ vhost_scsi_get_cmd(struct vhost_virtqueue *vq, struct vhost_scsi_tpg *tpg,
cmd->tvc_data_direction = data_direction;
cmd->tvc_nexus = tv_nexus;
cmd->inflight = vhost_scsi_get_inflight(vq);
+ cmd->tvc_resp_iov = tvc_resp_iov;
memcpy(cmd->tvc_cdb, cdb, VHOST_SCSI_MAX_CDB_SIZE);
@@ -935,7 +938,7 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
struct iov_iter in_iter, prot_iter, data_iter;
u64 tag;
u32 exp_data_len, data_direction;
- int ret, prot_bytes, c = 0;
+ int ret, prot_bytes, i, c = 0;
u16 lun;
u8 task_attr;
bool t10_pi = vhost_has_feature(vq, VIRTIO_SCSI_F_T10_PI);
@@ -1092,7 +1095,8 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq)
}
cmd->tvc_vhost = vs;
cmd->tvc_vq = vq;
- cmd->tvc_resp_iov = vq->iov[vc.out];
+ for (i = 0; i < vc.in ; i++)
+ cmd->tvc_resp_iov[i] = vq->iov[vc.out + i];
cmd->tvc_in_iovs = vc.in;
pr_debug("vhost_scsi got command opcode: %#02x, lun: %d\n",
@@ -1461,6 +1465,7 @@ static void vhost_scsi_destroy_vq_cmds(struct vhost_virtqueue *vq)
kfree(tv_cmd->tvc_sgl);
kfree(tv_cmd->tvc_prot_sgl);
kfree(tv_cmd->tvc_upages);
+ kfree(tv_cmd->tvc_resp_iov);
}
sbitmap_free(&svq->scsi_tags);
@@ -1508,6 +1513,14 @@ static int vhost_scsi_setup_vq_cmds(struct vhost_virtqueue *vq, int max_cmds)
goto out;
}
+ tv_cmd->tvc_resp_iov = kcalloc(UIO_MAXIOV,
+ sizeof(struct iovec),
+ GFP_KERNEL);
+ if (!tv_cmd->tvc_resp_iov) {
+ pr_err("Unable to allocate tv_cmd->tvc_resp_iov\n");
+ goto out;
+ }
+
tv_cmd->tvc_prot_sgl = kcalloc(VHOST_SCSI_PREALLOC_PROT_SGLS,
sizeof(struct scatterlist),
GFP_KERNEL);
diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c
index 166044642fd5cc..ec32f785dfdec1 100644
--- a/drivers/vhost/vdpa.c
+++ b/drivers/vhost/vdpa.c
@@ -65,6 +65,10 @@ static DEFINE_IDA(vhost_vdpa_ida);
static dev_t vhost_vdpa_major;
+static void vhost_vdpa_iotlb_unmap(struct vhost_vdpa *v,
+ struct vhost_iotlb *iotlb, u64 start,
+ u64 last, u32 asid);
+
static inline u32 iotlb_to_asid(struct vhost_iotlb *iotlb)
{
struct vhost_vdpa_as *as = container_of(iotlb, struct
@@ -135,7 +139,7 @@ static int vhost_vdpa_remove_as(struct vhost_vdpa *v, u32 asid)
return -EINVAL;
hlist_del(&as->hash_link);
- vhost_iotlb_reset(&as->iotlb);
+ vhost_vdpa_iotlb_unmap(v, &as->iotlb, 0ULL, 0ULL - 1, asid);
kfree(as);
return 0;
@@ -683,10 +687,20 @@ static long vhost_vdpa_unlocked_ioctl(struct file *filep,
mutex_unlock(&d->mutex);
return r;
}
+static void vhost_vdpa_general_unmap(struct vhost_vdpa *v,
+ struct vhost_iotlb_map *map, u32 asid)
+{
+ struct vdpa_device *vdpa = v->vdpa;
+ const struct vdpa_config_ops *ops = vdpa->config;
+ if (ops->dma_map) {
+ ops->dma_unmap(vdpa, asid, map->start, map->size);
+ } else if (ops->set_map == NULL) {
+ iommu_unmap(v->domain, map->start, map->size);
+ }
+}
-static void vhost_vdpa_pa_unmap(struct vhost_vdpa *v,
- struct vhost_iotlb *iotlb,
- u64 start, u64 last)
+static void vhost_vdpa_pa_unmap(struct vhost_vdpa *v, struct vhost_iotlb *iotlb,
+ u64 start, u64 last, u32 asid)
{
struct vhost_dev *dev = &v->vdev;
struct vhost_iotlb_map *map;
@@ -703,13 +717,13 @@ static void vhost_vdpa_pa_unmap(struct vhost_vdpa *v,
unpin_user_page(page);
}
atomic64_sub(PFN_DOWN(map->size), &dev->mm->pinned_vm);
+ vhost_vdpa_general_unmap(v, map, asid);
vhost_iotlb_map_free(iotlb, map);
}
}
-static void vhost_vdpa_va_unmap(struct vhost_vdpa *v,
- struct vhost_iotlb *iotlb,
- u64 start, u64 last)
+static void vhost_vdpa_va_unmap(struct vhost_vdpa *v, struct vhost_iotlb *iotlb,
+ u64 start, u64 last, u32 asid)
{
struct vhost_iotlb_map *map;
struct vdpa_map_file *map_file;
@@ -718,20 +732,21 @@ static void vhost_vdpa_va_unmap(struct vhost_vdpa *v,
map_file = (struct vdpa_map_file *)map->opaque;
fput(map_file->file);
kfree(map_file);
+ vhost_vdpa_general_unmap(v, map, asid);
vhost_iotlb_map_free(iotlb, map);
}
}
static void vhost_vdpa_iotlb_unmap(struct vhost_vdpa *v,
- struct vhost_iotlb *iotlb,
- u64 start, u64 last)
+ struct vhost_iotlb *iotlb, u64 start,
+ u64 last, u32 asid)
{
struct vdpa_device *vdpa = v->vdpa;
if (vdpa->use_va)
- return vhost_vdpa_va_unmap(v, iotlb, start, last);
+ return vhost_vdpa_va_unmap(v, iotlb, start, last, asid);
- return vhost_vdpa_pa_unmap(v, iotlb, start, last);
+ return vhost_vdpa_pa_unmap(v, iotlb, start, last, asid);
}
static int perm_to_iommu_flags(u32 perm)
@@ -798,17 +813,12 @@ static void vhost_vdpa_unmap(struct vhost_vdpa *v,
const struct vdpa_config_ops *ops = vdpa->config;
u32 asid = iotlb_to_asid(iotlb);
- vhost_vdpa_iotlb_unmap(v, iotlb, iova, iova + size - 1);
+ vhost_vdpa_iotlb_unmap(v, iotlb, iova, iova + size - 1, asid);
- if (ops->dma_map) {
- ops->dma_unmap(vdpa, asid, iova, size);
- } else if (ops->set_map) {
+ if (ops->set_map) {
if (!v->in_batch)
ops->set_map(vdpa, asid, iotlb);
- } else {
- iommu_unmap(v->domain, iova, size);
}
-
/* If we are in the middle of batch processing, delay the free
* of AS until BATCH_END.
*/
@@ -1162,14 +1172,14 @@ static void vhost_vdpa_cleanup(struct vhost_vdpa *v)
struct vhost_vdpa_as *as;
u32 asid;
- vhost_dev_cleanup(&v->vdev);
- kfree(v->vdev.vqs);
-
for (asid = 0; asid < v->vdpa->nas; asid++) {
as = asid_to_as(v, asid);
if (as)
vhost_vdpa_remove_as(v, asid);
}
+
+ vhost_dev_cleanup(&v->vdev);
+ kfree(v->vdev.vqs);
}
static int vhost_vdpa_open(struct inode *inode, struct file *filep)
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 5c9fe3c9c36465..43c9770b86e5ae 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -661,7 +661,7 @@ void vhost_dev_stop(struct vhost_dev *dev)
}
EXPORT_SYMBOL_GPL(vhost_dev_stop);
-static void vhost_clear_msg(struct vhost_dev *dev)
+void vhost_clear_msg(struct vhost_dev *dev)
{
struct vhost_msg_node *node, *n;
@@ -679,6 +679,7 @@ static void vhost_clear_msg(struct vhost_dev *dev)
spin_unlock(&dev->iotlb_lock);
}
+EXPORT_SYMBOL_GPL(vhost_clear_msg);
void vhost_dev_cleanup(struct vhost_dev *dev)
{
@@ -2053,7 +2054,7 @@ static int translate_desc(struct vhost_virtqueue *vq, u64 addr, u32 len,
struct vhost_dev *dev = vq->dev;
struct vhost_iotlb *umem = dev->iotlb ? dev->iotlb : dev->umem;
struct iovec *_iov;
- u64 s = 0;
+ u64 s = 0, last = addr + len - 1;
int ret = 0;
while ((u64)len > s) {
@@ -2063,7 +2064,7 @@ static int translate_desc(struct vhost_virtqueue *vq, u64 addr, u32 len,
break;
}
- map = vhost_iotlb_itree_first(umem, addr, addr + len - 1);
+ map = vhost_iotlb_itree_first(umem, addr, last);
if (map == NULL || map->start > addr) {
if (umem != dev->iotlb) {
ret = -EFAULT;
diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h
index d9109107af087f..790b296271f1e4 100644
--- a/drivers/vhost/vhost.h
+++ b/drivers/vhost/vhost.h
@@ -181,6 +181,7 @@ long vhost_dev_ioctl(struct vhost_dev *, unsigned int ioctl, void __user *argp);
long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *argp);
bool vhost_vq_access_ok(struct vhost_virtqueue *vq);
bool vhost_log_access_ok(struct vhost_dev *);
+void vhost_clear_msg(struct vhost_dev *dev);
int vhost_get_vq_desc(struct vhost_virtqueue *,
struct iovec iov[], unsigned int iov_count,
diff --git a/drivers/vhost/vringh.c b/drivers/vhost/vringh.c
index c9f5c8ea3afbd1..33eb941fcf1546 100644
--- a/drivers/vhost/vringh.c
+++ b/drivers/vhost/vringh.c
@@ -1102,7 +1102,7 @@ static int iotlb_translate(const struct vringh *vrh,
struct vhost_iotlb_map *map;
struct vhost_iotlb *iotlb = vrh->iotlb;
int ret = 0;
- u64 s = 0;
+ u64 s = 0, last = addr + len - 1;
spin_lock(vrh->iotlb_lock);
@@ -1114,8 +1114,7 @@ static int iotlb_translate(const struct vringh *vrh,
break;
}
- map = vhost_iotlb_itree_first(iotlb, addr,
- addr + len - 1);
+ map = vhost_iotlb_itree_first(iotlb, addr, last);
if (!map || map->start > addr) {
ret = -EINVAL;
break;
diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
index cd6f7776013ac7..a2b3743723639c 100644
--- a/drivers/vhost/vsock.c
+++ b/drivers/vhost/vsock.c
@@ -959,7 +959,14 @@ static int __init vhost_vsock_init(void)
VSOCK_TRANSPORT_F_H2G);
if (ret < 0)
return ret;
- return misc_register(&vhost_vsock_misc);
+
+ ret = misc_register(&vhost_vsock_misc);
+ if (ret) {
+ vsock_core_unregister(&vhost_transport.transport);
+ return ret;
+ }
+
+ return 0;
};
static void __exit vhost_vsock_exit(void)
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
index df6e09f7d24222..b2bed599e6c6ea 100644
--- a/drivers/video/fbdev/Kconfig
+++ b/drivers/video/fbdev/Kconfig
@@ -456,8 +456,8 @@ config FB_ATARI
chipset found in Ataris.
config FB_OF
- bool "Open Firmware frame buffer device support"
- depends on (FB = y) && PPC && (!PPC_PSERIES || PCI)
+ tristate "Open Firmware frame buffer device support"
+ depends on FB && PPC && (!PPC_PSERIES || PCI)
depends on !DRM_OFDRM
select APERTURE_HELPERS
select FB_CFB_FILLRECT
diff --git a/drivers/video/fbdev/atmel_lcdfb.c b/drivers/video/fbdev/atmel_lcdfb.c
index 1fc8de4ecbebf9..8187a7c4f9106b 100644
--- a/drivers/video/fbdev/atmel_lcdfb.c
+++ b/drivers/video/fbdev/atmel_lcdfb.c
@@ -49,7 +49,6 @@ struct atmel_lcdfb_info {
struct clk *lcdc_clk;
struct backlight_device *backlight;
- u8 bl_power;
u8 saved_lcdcon;
u32 pseudo_palette[16];
@@ -109,22 +108,7 @@ static u32 contrast_ctr = ATMEL_LCDC_PS_DIV8
static int atmel_bl_update_status(struct backlight_device *bl)
{
struct atmel_lcdfb_info *sinfo = bl_get_data(bl);
- int power = sinfo->bl_power;
- int brightness = bl->props.brightness;
-
- /* REVISIT there may be a meaningful difference between
- * fb_blank and power ... there seem to be some cases
- * this doesn't handle correctly.
- */
- if (bl->props.fb_blank != sinfo->bl_power)
- power = bl->props.fb_blank;
- else if (bl->props.power != sinfo->bl_power)
- power = bl->props.power;
-
- if (brightness < 0 && power == FB_BLANK_UNBLANK)
- brightness = lcdc_readl(sinfo, ATMEL_LCDC_CONTRAST_VAL);
- else if (power != FB_BLANK_UNBLANK)
- brightness = 0;
+ int brightness = backlight_get_brightness(bl);
lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_VAL, brightness);
if (contrast_ctr & ATMEL_LCDC_POL_POSITIVE)
@@ -133,8 +117,6 @@ static int atmel_bl_update_status(struct backlight_device *bl)
else
lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, contrast_ctr);
- bl->props.fb_blank = bl->props.power = sinfo->bl_power = power;
-
return 0;
}
@@ -155,8 +137,6 @@ static void init_backlight(struct atmel_lcdfb_info *sinfo)
struct backlight_properties props;
struct backlight_device *bl;
- sinfo->bl_power = FB_BLANK_UNBLANK;
-
if (sinfo->backlight)
return;
diff --git a/drivers/video/fbdev/aty/aty128fb.c b/drivers/video/fbdev/aty/aty128fb.c
index dd31b9d7d337cc..36a9ac05a340f9 100644
--- a/drivers/video/fbdev/aty/aty128fb.c
+++ b/drivers/video/fbdev/aty/aty128fb.c
@@ -1766,12 +1766,10 @@ static int aty128_bl_update_status(struct backlight_device *bd)
unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL);
int level;
- if (bd->props.power != FB_BLANK_UNBLANK ||
- bd->props.fb_blank != FB_BLANK_UNBLANK ||
- !par->lcd_on)
+ if (!par->lcd_on)
level = 0;
else
- level = bd->props.brightness;
+ level = backlight_get_brightness(bd);
reg |= LVDS_BL_MOD_EN | LVDS_BLON;
if (level > 0) {
diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c
index 0ccf5d401ecbcc..b02e4e645035c4 100644
--- a/drivers/video/fbdev/aty/atyfb_base.c
+++ b/drivers/video/fbdev/aty/atyfb_base.c
@@ -2219,13 +2219,7 @@ static int aty_bl_update_status(struct backlight_device *bd)
{
struct atyfb_par *par = bl_get_data(bd);
unsigned int reg = aty_ld_lcd(LCD_MISC_CNTL, par);
- int level;
-
- if (bd->props.power != FB_BLANK_UNBLANK ||
- bd->props.fb_blank != FB_BLANK_UNBLANK)
- level = 0;
- else
- level = bd->props.brightness;
+ int level = backlight_get_brightness(bd);
reg |= (BLMOD_EN | BIASMOD_EN);
if (level > 0) {
@@ -3192,8 +3186,7 @@ static void aty_init_lcd(struct atyfb_par *par, u32 bios_base)
* which we print to the screen.
*/
id = *(u8 *)par->lcd_table;
- strncpy(model, (char *)par->lcd_table+1, 24);
- model[23] = 0;
+ strscpy(model, (char *)par->lcd_table+1, sizeof(model));
width = par->lcd_width = *(u16 *)(par->lcd_table+25);
height = par->lcd_height = *(u16 *)(par->lcd_table+27);
diff --git a/drivers/video/fbdev/aty/radeon_backlight.c b/drivers/video/fbdev/aty/radeon_backlight.c
index d2c1263ad26005..427adc838f77e2 100644
--- a/drivers/video/fbdev/aty/radeon_backlight.c
+++ b/drivers/video/fbdev/aty/radeon_backlight.c
@@ -57,11 +57,7 @@ static int radeon_bl_update_status(struct backlight_device *bd)
* backlight. This provides some greater power saving and the display
* is useless without backlight anyway.
*/
- if (bd->props.power != FB_BLANK_UNBLANK ||
- bd->props.fb_blank != FB_BLANK_UNBLANK)
- level = 0;
- else
- level = bd->props.brightness;
+ level = backlight_get_brightness(bd);
del_timer_sync(&rinfo->lvds_timer);
radeon_engine_idle();
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index 14a7d404062c38..1b14c21af2b749 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -2495,9 +2495,12 @@ static int fbcon_set_font(struct vc_data *vc, struct console_font *font,
h > FBCON_SWAP(info->var.rotate, info->var.yres, info->var.xres))
return -EINVAL;
+ if (font->width > 32 || font->height > 32)
+ return -EINVAL;
+
/* Make sure drawing engine can handle the font */
- if (!(info->pixmap.blit_x & (1 << (font->width - 1))) ||
- !(info->pixmap.blit_y & (1 << (font->height - 1))))
+ if (!(info->pixmap.blit_x & BIT(font->width - 1)) ||
+ !(info->pixmap.blit_y & BIT(font->height - 1)))
return -EINVAL;
/* Make sure driver can handle the font length */
diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c
index b0e690f41025a2..79e5bfbdd34c26 100644
--- a/drivers/video/fbdev/core/fbmon.c
+++ b/drivers/video/fbdev/core/fbmon.c
@@ -1050,7 +1050,7 @@ static u32 fb_get_vblank(u32 hfreq)
}
/**
- * fb_get_hblank_by_freq - get horizontal blank time given hfreq
+ * fb_get_hblank_by_hfreq - get horizontal blank time given hfreq
* @hfreq: horizontal freq
* @xres: horizontal resolution in pixels
*
diff --git a/drivers/video/fbdev/matrox/matroxfb_base.c b/drivers/video/fbdev/matrox/matroxfb_base.c
index 0d3cee7ae7268c..a043a737ea9f79 100644
--- a/drivers/video/fbdev/matrox/matroxfb_base.c
+++ b/drivers/video/fbdev/matrox/matroxfb_base.c
@@ -1378,8 +1378,8 @@ static struct video_board vbG200 = {
.lowlevel = &matrox_G100
};
static struct video_board vbG200eW = {
- .maxvram = 0x100000,
- .maxdisplayable = 0x800000,
+ .maxvram = 0x1000000,
+ .maxdisplayable = 0x0800000,
.accelID = FB_ACCEL_MATROX_MGAG200,
.lowlevel = &matrox_G100
};
diff --git a/drivers/video/fbdev/mx3fb.c b/drivers/video/fbdev/mx3fb.c
index b945b68984b973..76771e126d0af8 100644
--- a/drivers/video/fbdev/mx3fb.c
+++ b/drivers/video/fbdev/mx3fb.c
@@ -283,12 +283,7 @@ static int mx3fb_bl_get_brightness(struct backlight_device *bl)
static int mx3fb_bl_update_status(struct backlight_device *bl)
{
struct mx3fb_data *fbd = bl_get_data(bl);
- int brightness = bl->props.brightness;
-
- if (bl->props.power != FB_BLANK_UNBLANK)
- brightness = 0;
- if (bl->props.fb_blank != FB_BLANK_UNBLANK)
- brightness = 0;
+ int brightness = backlight_get_brightness(bl);
fbd->backlight_level = (fbd->backlight_level & ~0xFF) | brightness;
diff --git a/drivers/video/fbdev/nvidia/nv_backlight.c b/drivers/video/fbdev/nvidia/nv_backlight.c
index 2ce53529f63612..503a7a683855aa 100644
--- a/drivers/video/fbdev/nvidia/nv_backlight.c
+++ b/drivers/video/fbdev/nvidia/nv_backlight.c
@@ -49,17 +49,11 @@ static int nvidia_bl_update_status(struct backlight_device *bd)
{
struct nvidia_par *par = bl_get_data(bd);
u32 tmp_pcrt, tmp_pmc, fpcontrol;
- int level;
+ int level = backlight_get_brightness(bd);
if (!par->FlatPanel)
return 0;
- if (bd->props.power != FB_BLANK_UNBLANK ||
- bd->props.fb_blank != FB_BLANK_UNBLANK)
- level = 0;
- else
- level = bd->props.brightness;
-
tmp_pmc = NV_RD32(par->PMC, 0x10F0) & 0x0000FFFF;
tmp_pcrt = NV_RD32(par->PCRTC0, 0x081C) & 0xFFFFFFFC;
fpcontrol = NV_RD32(par->PRAMDAC, 0x0848) & 0xCFFFFFCC;
diff --git a/drivers/video/fbdev/nvidia/nvidia.c b/drivers/video/fbdev/nvidia/nvidia.c
index 1960916098d464..e60a276b4855dc 100644
--- a/drivers/video/fbdev/nvidia/nvidia.c
+++ b/drivers/video/fbdev/nvidia/nvidia.c
@@ -1197,17 +1197,17 @@ static int nvidia_set_fbinfo(struct fb_info *info)
return nvidiafb_check_var(&info->var, info);
}
-static u32 nvidia_get_chipset(struct fb_info *info)
+static u32 nvidia_get_chipset(struct pci_dev *pci_dev,
+ volatile u32 __iomem *REGS)
{
- struct nvidia_par *par = info->par;
- u32 id = (par->pci_dev->vendor << 16) | par->pci_dev->device;
+ u32 id = (pci_dev->vendor << 16) | pci_dev->device;
printk(KERN_INFO PFX "Device ID: %x \n", id);
if ((id & 0xfff0) == 0x00f0 ||
(id & 0xfff0) == 0x02e0) {
/* pci-e */
- id = NV_RD32(par->REGS, 0x1800);
+ id = NV_RD32(REGS, 0x1800);
if ((id & 0x0000ffff) == 0x000010DE)
id = 0x10DE0000 | (id >> 16);
@@ -1220,12 +1220,11 @@ static u32 nvidia_get_chipset(struct fb_info *info)
return id;
}
-static u32 nvidia_get_arch(struct fb_info *info)
+static u32 nvidia_get_arch(u32 Chipset)
{
- struct nvidia_par *par = info->par;
u32 arch = 0;
- switch (par->Chipset & 0x0ff0) {
+ switch (Chipset & 0x0ff0) {
case 0x0100: /* GeForce 256 */
case 0x0110: /* GeForce2 MX */
case 0x0150: /* GeForce2 */
@@ -1278,16 +1277,44 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent)
struct fb_info *info;
unsigned short cmd;
int ret;
+ volatile u32 __iomem *REGS;
+ int Chipset;
+ u32 Architecture;
NVTRACE_ENTER();
assert(pd != NULL);
+ if (pci_enable_device(pd)) {
+ printk(KERN_ERR PFX "cannot enable PCI device\n");
+ return -ENODEV;
+ }
+
+ /* enable IO and mem if not already done */
+ pci_read_config_word(pd, PCI_COMMAND, &cmd);
+ cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+ pci_write_config_word(pd, PCI_COMMAND, cmd);
+
+ nvidiafb_fix.mmio_start = pci_resource_start(pd, 0);
+ nvidiafb_fix.mmio_len = pci_resource_len(pd, 0);
+
+ REGS = ioremap(nvidiafb_fix.mmio_start, nvidiafb_fix.mmio_len);
+ if (!REGS) {
+ printk(KERN_ERR PFX "cannot ioremap MMIO base\n");
+ return -ENODEV;
+ }
+
+ Chipset = nvidia_get_chipset(pd, REGS);
+ Architecture = nvidia_get_arch(Chipset);
+ if (Architecture == 0) {
+ printk(KERN_ERR PFX "unknown NV_ARCH\n");
+ goto err_out;
+ }
+
ret = aperture_remove_conflicting_pci_devices(pd, "nvidiafb");
if (ret)
- return ret;
+ goto err_out;
info = framebuffer_alloc(sizeof(struct nvidia_par), &pd->dev);
-
if (!info)
goto err_out;
@@ -1298,11 +1325,6 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent)
if (info->pixmap.addr == NULL)
goto err_out_kfree;
- if (pci_enable_device(pd)) {
- printk(KERN_ERR PFX "cannot enable PCI device\n");
- goto err_out_enable;
- }
-
if (pci_request_regions(pd, "nvidiafb")) {
printk(KERN_ERR PFX "cannot request PCI regions\n");
goto err_out_enable;
@@ -1318,34 +1340,17 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent)
par->paneltweak = paneltweak;
par->reverse_i2c = reverse_i2c;
- /* enable IO and mem if not already done */
- pci_read_config_word(pd, PCI_COMMAND, &cmd);
- cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
- pci_write_config_word(pd, PCI_COMMAND, cmd);
-
- nvidiafb_fix.mmio_start = pci_resource_start(pd, 0);
nvidiafb_fix.smem_start = pci_resource_start(pd, 1);
- nvidiafb_fix.mmio_len = pci_resource_len(pd, 0);
-
- par->REGS = ioremap(nvidiafb_fix.mmio_start, nvidiafb_fix.mmio_len);
- if (!par->REGS) {
- printk(KERN_ERR PFX "cannot ioremap MMIO base\n");
- goto err_out_free_base0;
- }
+ par->REGS = REGS;
- par->Chipset = nvidia_get_chipset(info);
- par->Architecture = nvidia_get_arch(info);
-
- if (par->Architecture == 0) {
- printk(KERN_ERR PFX "unknown NV_ARCH\n");
- goto err_out_arch;
- }
+ par->Chipset = Chipset;
+ par->Architecture = Architecture;
sprintf(nvidiafb_fix.id, "NV%x", (pd->device & 0x0ff0) >> 4);
if (NVCommonSetup(info))
- goto err_out_arch;
+ goto err_out_free_base0;
par->FbAddress = nvidiafb_fix.smem_start;
par->FbMapSize = par->RamAmountKBytes * 1024;
@@ -1401,7 +1406,6 @@ static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent)
goto err_out_iounmap_fb;
}
-
printk(KERN_INFO PFX
"PCI nVidia %s framebuffer (%dMB @ 0x%lX)\n",
info->fix.id,
@@ -1415,15 +1419,14 @@ err_out_iounmap_fb:
err_out_free_base1:
fb_destroy_modedb(info->monspecs.modedb);
nvidia_delete_i2c_busses(par);
-err_out_arch:
- iounmap(par->REGS);
- err_out_free_base0:
+err_out_free_base0:
pci_release_regions(pd);
err_out_enable:
kfree(info->pixmap.addr);
err_out_kfree:
framebuffer_release(info);
err_out:
+ iounmap(REGS);
return -ENODEV;
}
diff --git a/drivers/video/fbdev/omap/omapfb_main.c b/drivers/video/fbdev/omap/omapfb_main.c
index 17cda576568382..1f3df2055ff0d5 100644
--- a/drivers/video/fbdev/omap/omapfb_main.c
+++ b/drivers/video/fbdev/omap/omapfb_main.c
@@ -1447,7 +1447,7 @@ static int fbinfo_init(struct omapfb_device *fbdev, struct fb_info *info)
info->fbops = &omapfb_ops;
info->flags = FBINFO_FLAG_DEFAULT;
- strncpy(fix->id, MODULE_NAME, sizeof(fix->id));
+ strscpy(fix->id, MODULE_NAME, sizeof(fix->id));
info->pseudo_palette = fbdev->pseudo_palette;
@@ -1573,8 +1573,7 @@ static int omapfb_find_ctrl(struct omapfb_device *fbdev)
fbdev->ctrl = NULL;
- strncpy(name, conf->lcd.ctrl_name, sizeof(name) - 1);
- name[sizeof(name) - 1] = '\0';
+ strscpy(name, conf->lcd.ctrl_name, sizeof(name));
if (strcmp(name, "internal") == 0) {
fbdev->ctrl = fbdev->int_ctrl;
diff --git a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
index 4fc4b26a8d3043..ba94a0a7bd4fc2 100644
--- a/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
+++ b/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
@@ -331,13 +331,7 @@ static int dsicm_bl_update_status(struct backlight_device *dev)
struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
struct omap_dss_device *in = ddata->in;
int r;
- int level;
-
- if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
- dev->props.power == FB_BLANK_UNBLANK)
- level = dev->props.brightness;
- else
- level = 0;
+ int level = backlight_get_brightness(dev);
dev_dbg(&ddata->pdev->dev, "update brightness to %d\n", level);
diff --git a/drivers/video/fbdev/omap2/omapfb/dss/display-sysfs.c b/drivers/video/fbdev/omap2/omapfb/dss/display-sysfs.c
index bc5a44c2a144ab..ae937854403b12 100644
--- a/drivers/video/fbdev/omap2/omapfb/dss/display-sysfs.c
+++ b/drivers/video/fbdev/omap2/omapfb/dss/display-sysfs.c
@@ -10,6 +10,7 @@
#define DSS_SUBSYS_NAME "DISPLAY"
#include <linux/kernel.h>
+#include <linux/kstrtox.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/sysfs.h>
@@ -36,7 +37,7 @@ static ssize_t display_enabled_store(struct omap_dss_device *dssdev,
int r;
bool enable;
- r = strtobool(buf, &enable);
+ r = kstrtobool(buf, &enable);
if (r)
return r;
@@ -73,7 +74,7 @@ static ssize_t display_tear_store(struct omap_dss_device *dssdev,
if (!dssdev->driver->enable_te || !dssdev->driver->get_te)
return -ENOENT;
- r = strtobool(buf, &te);
+ r = kstrtobool(buf, &te);
if (r)
return r;
@@ -183,7 +184,7 @@ static ssize_t display_mirror_store(struct omap_dss_device *dssdev,
if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
return -ENOENT;
- r = strtobool(buf, &mirror);
+ r = kstrtobool(buf, &mirror);
if (r)
return r;
diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dsi.c b/drivers/video/fbdev/omap2/omapfb/dss/dsi.c
index 54b0f034c2edfd..7cddb7b8ae3445 100644
--- a/drivers/video/fbdev/omap2/omapfb/dss/dsi.c
+++ b/drivers/video/fbdev/omap2/omapfb/dss/dsi.c
@@ -1536,22 +1536,28 @@ static void dsi_dump_dsidev_irqs(struct platform_device *dsidev,
{
struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
unsigned long flags;
- struct dsi_irq_stats stats;
+ struct dsi_irq_stats *stats;
+
+ stats = kzalloc(sizeof(*stats), GFP_KERNEL);
+ if (!stats) {
+ seq_printf(s, "out of memory\n");
+ return;
+ }
spin_lock_irqsave(&dsi->irq_stats_lock, flags);
- stats = dsi->irq_stats;
+ *stats = dsi->irq_stats;
memset(&dsi->irq_stats, 0, sizeof(dsi->irq_stats));
dsi->irq_stats.last_reset = jiffies;
spin_unlock_irqrestore(&dsi->irq_stats_lock, flags);
seq_printf(s, "period %u ms\n",
- jiffies_to_msecs(jiffies - stats.last_reset));
+ jiffies_to_msecs(jiffies - stats->last_reset));
- seq_printf(s, "irqs %d\n", stats.irq_count);
+ seq_printf(s, "irqs %d\n", stats->irq_count);
#define PIS(x) \
- seq_printf(s, "%-20s %10d\n", #x, stats.dsi_irqs[ffs(DSI_IRQ_##x)-1])
+ seq_printf(s, "%-20s %10d\n", #x, stats->dsi_irqs[ffs(DSI_IRQ_##x)-1])
seq_printf(s, "-- DSI%d interrupts --\n", dsi->module_id + 1);
PIS(VC0);
@@ -1575,10 +1581,10 @@ static void dsi_dump_dsidev_irqs(struct platform_device *dsidev,
#define PIS(x) \
seq_printf(s, "%-20s %10d %10d %10d %10d\n", #x, \
- stats.vc_irqs[0][ffs(DSI_VC_IRQ_##x)-1], \
- stats.vc_irqs[1][ffs(DSI_VC_IRQ_##x)-1], \
- stats.vc_irqs[2][ffs(DSI_VC_IRQ_##x)-1], \
- stats.vc_irqs[3][ffs(DSI_VC_IRQ_##x)-1]);
+ stats->vc_irqs[0][ffs(DSI_VC_IRQ_##x)-1], \
+ stats->vc_irqs[1][ffs(DSI_VC_IRQ_##x)-1], \
+ stats->vc_irqs[2][ffs(DSI_VC_IRQ_##x)-1], \
+ stats->vc_irqs[3][ffs(DSI_VC_IRQ_##x)-1]);
seq_printf(s, "-- VC interrupts --\n");
PIS(CS);
@@ -1594,7 +1600,7 @@ static void dsi_dump_dsidev_irqs(struct platform_device *dsidev,
#define PIS(x) \
seq_printf(s, "%-20s %10d\n", #x, \
- stats.cio_irqs[ffs(DSI_CIO_IRQ_##x)-1]);
+ stats->cio_irqs[ffs(DSI_CIO_IRQ_##x)-1]);
seq_printf(s, "-- CIO interrupts --\n");
PIS(ERRSYNCESC1);
@@ -1618,6 +1624,8 @@ static void dsi_dump_dsidev_irqs(struct platform_device *dsidev,
PIS(ULPSACTIVENOT_ALL0);
PIS(ULPSACTIVENOT_ALL1);
#undef PIS
+
+ kfree(stats);
}
static void dsi1_dump_irqs(struct seq_file *s)
diff --git a/drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c b/drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c
index ba21c4a2633dd0..1b644be5fe2ed0 100644
--- a/drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c
+++ b/drivers/video/fbdev/omap2/omapfb/dss/manager-sysfs.c
@@ -10,6 +10,7 @@
#define DSS_SUBSYS_NAME "MANAGER"
#include <linux/kernel.h>
+#include <linux/kstrtox.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -246,7 +247,7 @@ static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr,
bool enable;
int r;
- r = strtobool(buf, &enable);
+ r = kstrtobool(buf, &enable);
if (r)
return r;
@@ -290,7 +291,7 @@ static ssize_t manager_alpha_blending_enabled_store(
if(!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER))
return -ENODEV;
- r = strtobool(buf, &enable);
+ r = kstrtobool(buf, &enable);
if (r)
return r;
@@ -329,7 +330,7 @@ static ssize_t manager_cpr_enable_store(struct omap_overlay_manager *mgr,
if (!dss_has_feature(FEAT_CPR))
return -ENODEV;
- r = strtobool(buf, &enable);
+ r = kstrtobool(buf, &enable);
if (r)
return r;
diff --git a/drivers/video/fbdev/omap2/omapfb/dss/overlay-sysfs.c b/drivers/video/fbdev/omap2/omapfb/dss/overlay-sysfs.c
index 601c0beb6de969..1da4fb1c77b4a6 100644
--- a/drivers/video/fbdev/omap2/omapfb/dss/overlay-sysfs.c
+++ b/drivers/video/fbdev/omap2/omapfb/dss/overlay-sysfs.c
@@ -13,6 +13,7 @@
#include <linux/err.h>
#include <linux/sysfs.h>
#include <linux/kobject.h>
+#include <linux/kstrtox.h>
#include <linux/platform_device.h>
#include <video/omapfb_dss.h>
@@ -210,7 +211,7 @@ static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf,
int r;
bool enable;
- r = strtobool(buf, &enable);
+ r = kstrtobool(buf, &enable);
if (r)
return r;
diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c b/drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c
index 06dc41aa03545e..831b2c2fbdf9ae 100644
--- a/drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c
+++ b/drivers/video/fbdev/omap2/omapfb/omapfb-sysfs.c
@@ -15,6 +15,7 @@
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/kernel.h>
+#include <linux/kstrtox.h>
#include <linux/mm.h>
#include <linux/omapfb.h>
@@ -96,7 +97,7 @@ static ssize_t store_mirror(struct device *dev,
int r;
struct fb_var_screeninfo new_var;
- r = strtobool(buf, &mirror);
+ r = kstrtobool(buf, &mirror);
if (r)
return r;
diff --git a/drivers/video/fbdev/riva/fbdev.c b/drivers/video/fbdev/riva/fbdev.c
index 644278146d3b71..41edc6e7946031 100644
--- a/drivers/video/fbdev/riva/fbdev.c
+++ b/drivers/video/fbdev/riva/fbdev.c
@@ -293,13 +293,7 @@ static int riva_bl_update_status(struct backlight_device *bd)
{
struct riva_par *par = bl_get_data(bd);
U032 tmp_pcrt, tmp_pmc;
- int level;
-
- if (bd->props.power != FB_BLANK_UNBLANK ||
- bd->props.fb_blank != FB_BLANK_UNBLANK)
- level = 0;
- else
- level = bd->props.brightness;
+ int level = backlight_get_brightness(bd);
tmp_pmc = NV_RD32(par->riva.PMC, 0x10F0) & 0x0000FFFF;
tmp_pcrt = NV_RD32(par->riva.PCRTC0, 0x081C) & 0xFFFFFFFC;
diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c
index 8752d389e3823f..d7f3e6281ce480 100644
--- a/drivers/video/fbdev/xen-fbfront.c
+++ b/drivers/video/fbdev/xen-fbfront.c
@@ -67,7 +67,7 @@ MODULE_PARM_DESC(video,
"Video memory size in MB, width, height in pixels (default 2,800,600)");
static void xenfb_make_preferred_console(void);
-static int xenfb_remove(struct xenbus_device *);
+static void xenfb_remove(struct xenbus_device *);
static void xenfb_init_shared_page(struct xenfb_info *, struct fb_info *);
static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *);
static void xenfb_disconnect_backend(struct xenfb_info *);
@@ -523,7 +523,7 @@ static int xenfb_resume(struct xenbus_device *dev)
return xenfb_connect_backend(dev, info);
}
-static int xenfb_remove(struct xenbus_device *dev)
+static void xenfb_remove(struct xenbus_device *dev)
{
struct xenfb_info *info = dev_get_drvdata(&dev->dev);
@@ -538,8 +538,6 @@ static int xenfb_remove(struct xenbus_device *dev)
vfree(info->gfns);
vfree(info->fb);
kfree(info);
-
- return 0;
}
static unsigned long vmalloc_to_gfn(void *address)
diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index 828ced06074235..b9a80aedee1b73 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -15,7 +15,7 @@ static ssize_t device_show(struct device *_d,
struct device_attribute *attr, char *buf)
{
struct virtio_device *dev = dev_to_virtio(_d);
- return sprintf(buf, "0x%04x\n", dev->id.device);
+ return sysfs_emit(buf, "0x%04x\n", dev->id.device);
}
static DEVICE_ATTR_RO(device);
@@ -23,7 +23,7 @@ static ssize_t vendor_show(struct device *_d,
struct device_attribute *attr, char *buf)
{
struct virtio_device *dev = dev_to_virtio(_d);
- return sprintf(buf, "0x%04x\n", dev->id.vendor);
+ return sysfs_emit(buf, "0x%04x\n", dev->id.vendor);
}
static DEVICE_ATTR_RO(vendor);
@@ -31,7 +31,7 @@ static ssize_t status_show(struct device *_d,
struct device_attribute *attr, char *buf)
{
struct virtio_device *dev = dev_to_virtio(_d);
- return sprintf(buf, "0x%08x\n", dev->config->get_status(dev));
+ return sysfs_emit(buf, "0x%08x\n", dev->config->get_status(dev));
}
static DEVICE_ATTR_RO(status);
@@ -39,7 +39,7 @@ static ssize_t modalias_show(struct device *_d,
struct device_attribute *attr, char *buf)
{
struct virtio_device *dev = dev_to_virtio(_d);
- return sprintf(buf, "virtio:d%08Xv%08X\n",
+ return sysfs_emit(buf, "virtio:d%08Xv%08X\n",
dev->id.device, dev->id.vendor);
}
static DEVICE_ATTR_RO(modalias);
@@ -54,9 +54,9 @@ static ssize_t features_show(struct device *_d,
/* We actually represent this as a bitstring, as it could be
* arbitrary length in future. */
for (i = 0; i < sizeof(dev->features)*8; i++)
- len += sprintf(buf+len, "%c",
+ len += sysfs_emit_at(buf, len, "%c",
__virtio_test_bit(dev, i) ? '1' : '0');
- len += sprintf(buf+len, "\n");
+ len += sysfs_emit_at(buf, len, "\n");
return len;
}
static DEVICE_ATTR_RO(features);
diff --git a/drivers/virtio/virtio_pci_modern.c b/drivers/virtio/virtio_pci_modern.c
index c3b9f27618497a..9e496e288cfad5 100644
--- a/drivers/virtio/virtio_pci_modern.c
+++ b/drivers/virtio/virtio_pci_modern.c
@@ -303,14 +303,14 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
int err;
if (index >= vp_modern_get_num_queues(mdev))
- return ERR_PTR(-ENOENT);
+ return ERR_PTR(-EINVAL);
/* Check if queue is either not available or already active. */
num = vp_modern_get_queue_size(mdev, index);
if (!num || vp_modern_get_queue_enable(mdev, index))
return ERR_PTR(-ENOENT);
- if (num & (num - 1)) {
+ if (!is_power_of_2(num)) {
dev_warn(&vp_dev->pci_dev->dev, "bad queue size %u", num);
return ERR_PTR(-EINVAL);
}
diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c
index 2e7689bb933b8e..723c4e29e1d3b8 100644
--- a/drivers/virtio/virtio_ring.c
+++ b/drivers/virtio/virtio_ring.c
@@ -1052,7 +1052,7 @@ static int vring_alloc_queue_split(struct vring_virtqueue_split *vring_split,
dma_addr_t dma_addr;
/* We assume num is a power of 2. */
- if (num & (num - 1)) {
+ if (!is_power_of_2(num)) {
dev_warn(&vdev->dev, "Bad virtqueue length %u\n", num);
return -EINVAL;
}
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index f2ae2e563dc54a..4a2ddf730a3acd 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -1166,6 +1166,8 @@ int w1_process(void *data)
/* remainder if it woke up early */
unsigned long jremain = 0;
+ atomic_inc(&dev->refcnt);
+
for (;;) {
if (!jremain && dev->search_count) {
@@ -1193,8 +1195,10 @@ int w1_process(void *data)
*/
mutex_unlock(&dev->list_mutex);
- if (kthread_should_stop())
+ if (kthread_should_stop()) {
+ __set_current_state(TASK_RUNNING);
break;
+ }
/* Only sleep when the search is active. */
if (dev->search_count) {
diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c
index b3e1792d9c49fc..3a71c5eb2f837f 100644
--- a/drivers/w1/w1_int.c
+++ b/drivers/w1/w1_int.c
@@ -51,10 +51,9 @@ static struct w1_master *w1_alloc_dev(u32 id, int slave_count, int slave_ttl,
dev->search_count = w1_search_count;
dev->enable_pullup = w1_enable_pullup;
- /* 1 for w1_process to decrement
- * 1 for __w1_remove_master_device to decrement
+ /* For __w1_remove_master_device to decrement
*/
- atomic_set(&dev->refcnt, 2);
+ atomic_set(&dev->refcnt, 1);
INIT_LIST_HEAD(&dev->slist);
INIT_LIST_HEAD(&dev->async_list);
diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c
index 4cb10877017c77..6ca5d9515d85ca 100644
--- a/drivers/watchdog/diag288_wdt.c
+++ b/drivers/watchdog/diag288_wdt.c
@@ -86,7 +86,7 @@ static int __diag288(unsigned int func, unsigned int timeout,
"1:\n"
EX_TABLE(0b, 1b)
: "+d" (err) : "d"(__func), "d"(__timeout),
- "d"(__action), "d"(__len) : "1", "cc");
+ "d"(__action), "d"(__len) : "1", "cc", "memory");
return err;
}
@@ -268,12 +268,21 @@ static int __init diag288_init(void)
char ebc_begin[] = {
194, 197, 199, 201, 213
};
+ char *ebc_cmd;
watchdog_set_nowayout(&wdt_dev, nowayout_info);
if (MACHINE_IS_VM) {
- if (__diag288_vm(WDT_FUNC_INIT, 15,
- ebc_begin, sizeof(ebc_begin)) != 0) {
+ ebc_cmd = kmalloc(sizeof(ebc_begin), GFP_KERNEL);
+ if (!ebc_cmd) {
+ pr_err("The watchdog cannot be initialized\n");
+ return -ENOMEM;
+ }
+ memcpy(ebc_cmd, ebc_begin, sizeof(ebc_begin));
+ ret = __diag288_vm(WDT_FUNC_INIT, 15,
+ ebc_cmd, sizeof(ebc_begin));
+ kfree(ebc_cmd);
+ if (ret != 0) {
pr_err("The watchdog cannot be initialized\n");
return -EINVAL;
}
diff --git a/drivers/xen/pvcalls-back.c b/drivers/xen/pvcalls-back.c
index 28b2a1fa25ab58..0d4f8f4f494888 100644
--- a/drivers/xen/pvcalls-back.c
+++ b/drivers/xen/pvcalls-back.c
@@ -1181,9 +1181,8 @@ static void pvcalls_back_changed(struct xenbus_device *dev,
}
}
-static int pvcalls_back_remove(struct xenbus_device *dev)
+static void pvcalls_back_remove(struct xenbus_device *dev)
{
- return 0;
}
static int pvcalls_back_uevent(struct xenbus_device *xdev,
diff --git a/drivers/xen/pvcalls-front.c b/drivers/xen/pvcalls-front.c
index 1826e8e671251d..d5d589bda243d4 100644
--- a/drivers/xen/pvcalls-front.c
+++ b/drivers/xen/pvcalls-front.c
@@ -225,6 +225,8 @@ again:
return IRQ_HANDLED;
}
+static void free_active_ring(struct sock_mapping *map);
+
static void pvcalls_front_free_map(struct pvcalls_bedata *bedata,
struct sock_mapping *map)
{
@@ -240,7 +242,7 @@ static void pvcalls_front_free_map(struct pvcalls_bedata *bedata,
for (i = 0; i < (1 << PVCALLS_RING_ORDER); i++)
gnttab_end_foreign_access(map->active.ring->ref[i], NULL);
gnttab_end_foreign_access(map->active.ref, NULL);
- free_page((unsigned long)map->active.ring);
+ free_active_ring(map);
kfree(map);
}
@@ -1085,7 +1087,7 @@ static const struct xenbus_device_id pvcalls_front_ids[] = {
{ "" }
};
-static int pvcalls_front_remove(struct xenbus_device *dev)
+static void pvcalls_front_remove(struct xenbus_device *dev)
{
struct pvcalls_bedata *bedata;
struct sock_mapping *map = NULL, *n;
@@ -1121,7 +1123,6 @@ static int pvcalls_front_remove(struct xenbus_device *dev)
kfree(bedata->ring.sring);
kfree(bedata);
xenbus_switch_state(dev, XenbusStateClosed);
- return 0;
}
static int pvcalls_front_probe(struct xenbus_device *dev,
diff --git a/drivers/xen/xen-pciback/xenbus.c b/drivers/xen/xen-pciback/xenbus.c
index d171091eec123d..b11e401f1b1ee9 100644
--- a/drivers/xen/xen-pciback/xenbus.c
+++ b/drivers/xen/xen-pciback/xenbus.c
@@ -716,14 +716,12 @@ out:
return err;
}
-static int xen_pcibk_xenbus_remove(struct xenbus_device *dev)
+static void xen_pcibk_xenbus_remove(struct xenbus_device *dev)
{
struct xen_pcibk_device *pdev = dev_get_drvdata(&dev->dev);
if (pdev != NULL)
free_pdev(pdev);
-
- return 0;
}
static const struct xenbus_device_id xen_pcibk_ids[] = {
diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c
index 6106ed93817d67..954188b0b858a2 100644
--- a/drivers/xen/xen-scsiback.c
+++ b/drivers/xen/xen-scsiback.c
@@ -1249,7 +1249,7 @@ static void scsiback_release_translation_entry(struct vscsibk_info *info)
spin_unlock_irqrestore(&info->v2p_lock, flags);
}
-static int scsiback_remove(struct xenbus_device *dev)
+static void scsiback_remove(struct xenbus_device *dev)
{
struct vscsibk_info *info = dev_get_drvdata(&dev->dev);
@@ -1261,8 +1261,6 @@ static int scsiback_remove(struct xenbus_device *dev)
gnttab_page_cache_shrink(&info->free_pages, 0);
dev_set_drvdata(&dev->dev, NULL);
-
- return 0;
}
static int scsiback_probe(struct xenbus_device *dev,
diff --git a/fs/affs/file.c b/fs/affs/file.c
index cefa222f7881c7..8daeed31e1af94 100644
--- a/fs/affs/file.c
+++ b/fs/affs/file.c
@@ -880,7 +880,7 @@ affs_truncate(struct inode *inode)
if (inode->i_size > AFFS_I(inode)->mmu_private) {
struct address_space *mapping = inode->i_mapping;
struct page *page;
- void *fsdata;
+ void *fsdata = NULL;
loff_t isize = inode->i_size;
int res;
diff --git a/fs/afs/cmservice.c b/fs/afs/cmservice.c
index 7dcd59693a0c2f..d4ddb20d673208 100644
--- a/fs/afs/cmservice.c
+++ b/fs/afs/cmservice.c
@@ -13,6 +13,8 @@
#include "internal.h"
#include "afs_cm.h"
#include "protocol_yfs.h"
+#define RXRPC_TRACE_ONLY_DEFINE_ENUMS
+#include <trace/events/rxrpc.h>
static int afs_deliver_cb_init_call_back_state(struct afs_call *);
static int afs_deliver_cb_init_call_back_state3(struct afs_call *);
@@ -191,7 +193,7 @@ static void afs_cm_destructor(struct afs_call *call)
* Abort a service call from within an action function.
*/
static void afs_abort_service_call(struct afs_call *call, u32 abort_code, int error,
- const char *why)
+ enum rxrpc_abort_reason why)
{
rxrpc_kernel_abort_call(call->net->socket, call->rxcall,
abort_code, error, why);
@@ -469,7 +471,7 @@ static void SRXAFSCB_ProbeUuid(struct work_struct *work)
if (memcmp(r, &call->net->uuid, sizeof(call->net->uuid)) == 0)
afs_send_empty_reply(call);
else
- afs_abort_service_call(call, 1, 1, "K-1");
+ afs_abort_service_call(call, 1, 1, afs_abort_probeuuid_negative);
afs_put_call(call);
_leave("");
diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c
index c62939e5ea1f0b..7817e2b860e5eb 100644
--- a/fs/afs/rxrpc.c
+++ b/fs/afs/rxrpc.c
@@ -13,6 +13,8 @@
#include "internal.h"
#include "afs_cm.h"
#include "protocol_yfs.h"
+#define RXRPC_TRACE_ONLY_DEFINE_ENUMS
+#include <trace/events/rxrpc.h>
struct workqueue_struct *afs_async_calls;
@@ -397,7 +399,8 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp)
error_do_abort:
if (ret != -ECONNABORTED) {
rxrpc_kernel_abort_call(call->net->socket, rxcall,
- RX_USER_ABORT, ret, "KSD");
+ RX_USER_ABORT, ret,
+ afs_abort_send_data_error);
} else {
len = 0;
iov_iter_kvec(&msg.msg_iter, ITER_DEST, NULL, 0, 0);
@@ -527,7 +530,8 @@ static void afs_deliver_to_call(struct afs_call *call)
case -ENOTSUPP:
abort_code = RXGEN_OPCODE;
rxrpc_kernel_abort_call(call->net->socket, call->rxcall,
- abort_code, ret, "KIV");
+ abort_code, ret,
+ afs_abort_op_not_supported);
goto local_abort;
case -EIO:
pr_err("kAFS: Call %u in bad state %u\n",
@@ -542,12 +546,14 @@ static void afs_deliver_to_call(struct afs_call *call)
if (state != AFS_CALL_CL_AWAIT_REPLY)
abort_code = RXGEN_SS_UNMARSHAL;
rxrpc_kernel_abort_call(call->net->socket, call->rxcall,
- abort_code, ret, "KUM");
+ abort_code, ret,
+ afs_abort_unmarshal_error);
goto local_abort;
default:
abort_code = RX_CALL_DEAD;
rxrpc_kernel_abort_call(call->net->socket, call->rxcall,
- abort_code, ret, "KER");
+ abort_code, ret,
+ afs_abort_general_error);
goto local_abort;
}
}
@@ -619,7 +625,8 @@ long afs_wait_for_call_to_complete(struct afs_call *call,
/* Kill off the call if it's still live. */
_debug("call interrupted");
if (rxrpc_kernel_abort_call(call->net->socket, call->rxcall,
- RX_USER_ABORT, -EINTR, "KWI"))
+ RX_USER_ABORT, -EINTR,
+ afs_abort_interrupted))
afs_set_call_complete(call, -EINTR, 0);
}
}
@@ -836,7 +843,8 @@ void afs_send_empty_reply(struct afs_call *call)
case -ENOMEM:
_debug("oom");
rxrpc_kernel_abort_call(net->socket, call->rxcall,
- RXGEN_SS_MARSHAL, -ENOMEM, "KOO");
+ RXGEN_SS_MARSHAL, -ENOMEM,
+ afs_abort_oom);
fallthrough;
default:
_leave(" [error]");
@@ -878,7 +886,8 @@ void afs_send_simple_reply(struct afs_call *call, const void *buf, size_t len)
if (n == -ENOMEM) {
_debug("oom");
rxrpc_kernel_abort_call(net->socket, call->rxcall,
- RXGEN_SS_MARSHAL, -ENOMEM, "KOO");
+ RXGEN_SS_MARSHAL, -ENOMEM,
+ afs_abort_oom);
}
_leave(" [error]");
}
@@ -900,6 +909,7 @@ int afs_extract_data(struct afs_call *call, bool want_more)
ret = rxrpc_kernel_recv_data(net->socket, call->rxcall, iter,
&call->iov_len, want_more, &remote_abort,
&call->service_id);
+ trace_afs_receive_data(call, call->iter, want_more, ret);
if (ret == 0 || ret == -EAGAIN)
return ret;
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index de63572a94044a..9a780fafc5397e 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -2034,7 +2034,7 @@ static int elf_core_dump(struct coredump_params *cprm)
* The number of segs are recored into ELF header as 16bit value.
* Please check DEFAULT_MAX_MAP_COUNT definition when you modify here.
*/
- segs = cprm->vma_count + elf_core_extra_phdrs();
+ segs = cprm->vma_count + elf_core_extra_phdrs(cprm);
/* for notes section */
segs++;
@@ -2074,7 +2074,7 @@ static int elf_core_dump(struct coredump_params *cprm)
dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
offset += cprm->vma_data_size;
- offset += elf_core_extra_data_size();
+ offset += elf_core_extra_data_size(cprm);
e_shoff = offset;
if (e_phnum == PN_XNUM) {
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index 096e3520a0b106..a05eafcacfb271 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -1509,7 +1509,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
tmp->next = thread_list;
thread_list = tmp;
- segs = cprm->vma_count + elf_core_extra_phdrs();
+ segs = cprm->vma_count + elf_core_extra_phdrs(cprm);
/* for notes section */
segs++;
@@ -1555,7 +1555,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
offset += cprm->vma_data_size;
- offset += elf_core_extra_data_size();
+ offset += elf_core_extra_data_size(cprm);
e_shoff = offset;
if (e_phnum == PN_XNUM) {
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index 21c92c74bf71a4..46851511b661b3 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -484,6 +484,7 @@ static int add_all_parents(struct btrfs_backref_walk_ctx *ctx,
u64 wanted_disk_byte = ref->wanted_disk_byte;
u64 count = 0;
u64 data_offset;
+ u8 type;
if (level != 0) {
eb = path->nodes[level];
@@ -538,6 +539,9 @@ static int add_all_parents(struct btrfs_backref_walk_ctx *ctx,
continue;
}
fi = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
+ type = btrfs_file_extent_type(eb, fi);
+ if (type == BTRFS_FILE_EXTENT_INLINE)
+ goto next;
disk_byte = btrfs_file_extent_disk_bytenr(eb, fi);
data_offset = btrfs_file_extent_offset(eb, fi);
diff --git a/fs/btrfs/bio.c b/fs/btrfs/bio.c
index b8fb7ef6b52060..8affc88b0e0a4b 100644
--- a/fs/btrfs/bio.c
+++ b/fs/btrfs/bio.c
@@ -329,7 +329,16 @@ int btrfs_repair_io_failure(struct btrfs_fs_info *fs_info, u64 ino, u64 start,
&map_length, &bioc, mirror_num);
if (ret)
goto out_counter_dec;
- BUG_ON(mirror_num != bioc->mirror_num);
+ /*
+ * This happens when dev-replace is also running, and the
+ * mirror_num indicates the dev-replace target.
+ *
+ * In this case, we don't need to do anything, as the read
+ * error just means the replace progress hasn't reached our
+ * read range, and later replace routine would handle it well.
+ */
+ if (mirror_num != bioc->mirror_num)
+ goto out_counter_dec;
}
sector = bioc->stripes[bioc->mirror_num - 1].physical >> 9;
diff --git a/fs/btrfs/defrag.c b/fs/btrfs/defrag.c
index 0a3c261b69c9f9..d81b764a76446b 100644
--- a/fs/btrfs/defrag.c
+++ b/fs/btrfs/defrag.c
@@ -358,8 +358,10 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
goto out;
path = btrfs_alloc_path();
- if (!path)
- return -ENOMEM;
+ if (!path) {
+ ret = -ENOMEM;
+ goto out;
+ }
level = btrfs_header_level(root->node);
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 0888d484df80c9..3aa04224315eb3 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -367,7 +367,14 @@ error:
btrfs_print_tree(eb, 0);
btrfs_err(fs_info, "block=%llu write time tree block corruption detected",
eb->start);
- WARN_ON(IS_ENABLED(CONFIG_BTRFS_DEBUG));
+ /*
+ * Be noisy if this is an extent buffer from a log tree. We don't abort
+ * a transaction in case there's a bad log tree extent buffer, we just
+ * fallback to a transaction commit. Still we want to know when there is
+ * a bad log tree extent buffer, as that may signal a bug somewhere.
+ */
+ WARN_ON(IS_ENABLED(CONFIG_BTRFS_DEBUG) ||
+ btrfs_header_owner(eb) == BTRFS_TREE_LOG_OBJECTID);
return ret;
}
@@ -530,6 +537,9 @@ static int validate_extent_buffer(struct extent_buffer *eb,
}
if (found_level != check->level) {
+ btrfs_err(fs_info,
+ "level verify failed on logical %llu mirror %u wanted %u found %u",
+ eb->start, eb->read_mirror, check->level, found_level);
ret = -EIO;
goto out;
}
@@ -3381,6 +3391,8 @@ out:
/*
* Do various sanity and dependency checks of different features.
*
+ * @is_rw_mount: If the mount is read-write.
+ *
* This is the place for less strict checks (like for subpage or artificial
* feature dependencies).
*
@@ -3391,7 +3403,7 @@ out:
* (space cache related) can modify on-disk format like free space tree and
* screw up certain feature dependencies.
*/
-int btrfs_check_features(struct btrfs_fs_info *fs_info, struct super_block *sb)
+int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount)
{
struct btrfs_super_block *disk_super = fs_info->super_copy;
u64 incompat = btrfs_super_incompat_flags(disk_super);
@@ -3430,7 +3442,7 @@ int btrfs_check_features(struct btrfs_fs_info *fs_info, struct super_block *sb)
if (btrfs_super_nodesize(disk_super) > PAGE_SIZE)
incompat |= BTRFS_FEATURE_INCOMPAT_BIG_METADATA;
- if (compat_ro_unsupp && !sb_rdonly(sb)) {
+ if (compat_ro_unsupp && is_rw_mount) {
btrfs_err(fs_info,
"cannot mount read-write because of unknown compat_ro features (0x%llx)",
compat_ro);
@@ -3633,7 +3645,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
goto fail_alloc;
}
- ret = btrfs_check_features(fs_info, sb);
+ ret = btrfs_check_features(fs_info, !sb_rdonly(sb));
if (ret < 0) {
err = ret;
goto fail_alloc;
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index 363935cfc08446..f2f295eb6103da 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -50,7 +50,7 @@ int __cold open_ctree(struct super_block *sb,
void __cold close_ctree(struct btrfs_fs_info *fs_info);
int btrfs_validate_super(struct btrfs_fs_info *fs_info,
struct btrfs_super_block *sb, int mirror_num);
-int btrfs_check_features(struct btrfs_fs_info *fs_info, struct super_block *sb);
+int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount);
int write_all_supers(struct btrfs_fs_info *fs_info, int max_mirrors);
struct btrfs_super_block *btrfs_read_dev_super(struct block_device *bdev);
struct btrfs_super_block *btrfs_read_dev_one_super(struct block_device *bdev,
diff --git a/fs/btrfs/extent-io-tree.c b/fs/btrfs/extent-io-tree.c
index 9ae9cd1e70352a..3c7766dfaa694a 100644
--- a/fs/btrfs/extent-io-tree.c
+++ b/fs/btrfs/extent-io-tree.c
@@ -1551,7 +1551,7 @@ u64 count_range_bits(struct extent_io_tree *tree,
u64 last = 0;
int found = 0;
- if (WARN_ON(search_end <= cur_start))
+ if (WARN_ON(search_end < cur_start))
return 0;
spin_lock(&tree->lock);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 892d78c1853c76..72ba13b027a9e6 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -1713,6 +1713,11 @@ static int run_one_delayed_ref(struct btrfs_trans_handle *trans,
BUG();
if (ret && insert_reserved)
btrfs_pin_extent(trans, node->bytenr, node->num_bytes, 1);
+ if (ret < 0)
+ btrfs_err(trans->fs_info,
+"failed to run delayed ref for logical %llu num_bytes %llu type %u action %u ref_mod %d: %d",
+ node->bytenr, node->num_bytes, node->type,
+ node->action, node->ref_mod, ret);
return ret;
}
@@ -1954,8 +1959,6 @@ static int btrfs_run_delayed_refs_for_head(struct btrfs_trans_handle *trans,
if (ret) {
unselect_delayed_ref_head(delayed_refs, locked_ref);
btrfs_put_delayed_ref(ref);
- btrfs_debug(fs_info, "run_one_delayed_ref returned %d",
- ret);
return ret;
}
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index 83dd3aa5966357..3bbf8703db2a8f 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -104,6 +104,15 @@ struct btrfs_bio_ctrl {
btrfs_bio_end_io_t end_io_func;
/*
+ * This is for metadata read, to provide the extra needed verification
+ * info. This has to be provided for submit_one_bio(), as
+ * submit_one_bio() can submit a bio if it ends at stripe boundary. If
+ * no such parent_check is provided, the metadata can hit false alert at
+ * endio time.
+ */
+ struct btrfs_tree_parent_check *parent_check;
+
+ /*
* Tell writepage not to lock the state bits for this range, it still
* does the unlocking.
*/
@@ -133,13 +142,24 @@ static void submit_one_bio(struct btrfs_bio_ctrl *bio_ctrl)
btrfs_bio(bio)->file_offset = page_offset(bv->bv_page) + bv->bv_offset;
- if (!is_data_inode(&inode->vfs_inode))
+ if (!is_data_inode(&inode->vfs_inode)) {
+ if (btrfs_op(bio) != BTRFS_MAP_WRITE) {
+ /*
+ * For metadata read, we should have the parent_check,
+ * and copy it to bbio for metadata verification.
+ */
+ ASSERT(bio_ctrl->parent_check);
+ memcpy(&btrfs_bio(bio)->parent_check,
+ bio_ctrl->parent_check,
+ sizeof(struct btrfs_tree_parent_check));
+ }
btrfs_submit_metadata_bio(inode, bio, mirror_num);
- else if (btrfs_op(bio) == BTRFS_MAP_WRITE)
+ } else if (btrfs_op(bio) == BTRFS_MAP_WRITE) {
btrfs_submit_data_write_bio(inode, bio, mirror_num);
- else
+ } else {
btrfs_submit_data_read_bio(inode, bio, mirror_num,
bio_ctrl->compress_type);
+ }
/* The bio is owned by the end_io handler now */
bio_ctrl->bio = NULL;
@@ -3806,6 +3826,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,
lockend = round_up(start + len, inode->root->fs_info->sectorsize);
prev_extent_end = lockstart;
+ btrfs_inode_lock(inode, BTRFS_ILOCK_SHARED);
lock_extent(&inode->io_tree, lockstart, lockend, &cached_state);
ret = fiemap_find_last_extent_offset(inode, path, &last_extent_end);
@@ -3999,6 +4020,7 @@ check_eof_delalloc:
out_unlock:
unlock_extent(&inode->io_tree, lockstart, lockend, &cached_state);
+ btrfs_inode_unlock(inode, BTRFS_ILOCK_SHARED);
out:
free_extent_state(delalloc_cached_state);
btrfs_free_backref_share_ctx(backref_ctx);
@@ -4829,6 +4851,7 @@ static int read_extent_buffer_subpage(struct extent_buffer *eb, int wait,
struct extent_state *cached_state = NULL;
struct btrfs_bio_ctrl bio_ctrl = {
.mirror_num = mirror_num,
+ .parent_check = check,
};
int ret = 0;
@@ -4878,7 +4901,6 @@ static int read_extent_buffer_subpage(struct extent_buffer *eb, int wait,
*/
atomic_dec(&eb->io_pages);
}
- memcpy(&btrfs_bio(bio_ctrl.bio)->parent_check, check, sizeof(*check));
submit_one_bio(&bio_ctrl);
if (ret || wait != WAIT_COMPLETE) {
free_extent_state(cached_state);
@@ -4905,6 +4927,7 @@ int read_extent_buffer_pages(struct extent_buffer *eb, int wait, int mirror_num,
unsigned long num_reads = 0;
struct btrfs_bio_ctrl bio_ctrl = {
.mirror_num = mirror_num,
+ .parent_check = check,
};
if (test_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags))
@@ -4996,7 +5019,6 @@ int read_extent_buffer_pages(struct extent_buffer *eb, int wait, int mirror_num,
}
}
- memcpy(&btrfs_bio(bio_ctrl.bio)->parent_check, check, sizeof(*check));
submit_one_bio(&bio_ctrl);
if (ret || wait != WAIT_COMPLETE)
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 91b00eb2440e7d..af046d22300e2c 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -3354,7 +3354,7 @@ bool btrfs_find_delalloc_in_range(struct btrfs_inode *inode, u64 start, u64 end,
bool search_io_tree = true;
bool ret = false;
- while (cur_offset < end) {
+ while (cur_offset <= end) {
u64 delalloc_start;
u64 delalloc_end;
bool delalloc;
@@ -3541,6 +3541,7 @@ static loff_t find_desired_extent(struct file *file, loff_t offset, int whence)
struct extent_buffer *leaf = path->nodes[0];
struct btrfs_file_extent_item *extent;
u64 extent_end;
+ u8 type;
if (path->slots[0] >= btrfs_header_nritems(leaf)) {
ret = btrfs_next_leaf(root, path);
@@ -3596,10 +3597,16 @@ static loff_t find_desired_extent(struct file *file, loff_t offset, int whence)
extent = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
+ type = btrfs_file_extent_type(leaf, extent);
- if (btrfs_file_extent_disk_bytenr(leaf, extent) == 0 ||
- btrfs_file_extent_type(leaf, extent) ==
- BTRFS_FILE_EXTENT_PREALLOC) {
+ /*
+ * Can't access the extent's disk_bytenr field if this is an
+ * inline extent, since at that offset, it's where the extent
+ * data starts.
+ */
+ if (type == BTRFS_FILE_EXTENT_PREALLOC ||
+ (type == BTRFS_FILE_EXTENT_REG &&
+ btrfs_file_extent_disk_bytenr(leaf, extent) == 0)) {
/*
* Explicit hole or prealloc extent, search for delalloc.
* A prealloc extent is treated like a hole.
diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
index a749367e5ae2a2..37b86acfcbcf88 100644
--- a/fs/btrfs/fs.h
+++ b/fs/btrfs/fs.h
@@ -119,6 +119,12 @@ enum {
/* Indicate that we want to commit the transaction. */
BTRFS_FS_NEED_TRANS_COMMIT,
+ /*
+ * Indicate metadata over-commit is disabled. This is set when active
+ * zone tracking is needed.
+ */
+ BTRFS_FS_NO_OVERCOMMIT,
+
#if BITS_PER_LONG == 32
/* Indicate if we have error/warn message printed on 32bit systems */
BTRFS_FS_32BIT_ERROR,
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 8bcad994015487..98a800b8bd438b 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -7092,7 +7092,7 @@ next:
* Other members are not utilized for inline extents.
*/
ASSERT(em->block_start == EXTENT_MAP_INLINE);
- ASSERT(em->len = fs_info->sectorsize);
+ ASSERT(em->len == fs_info->sectorsize);
ret = read_inline_extent(inode, path, page);
if (ret < 0)
@@ -9377,8 +9377,10 @@ static int btrfs_rename(struct user_namespace *mnt_userns,
if (flags & RENAME_WHITEOUT) {
whiteout_args.inode = new_whiteout_inode(mnt_userns, old_dir);
- if (!whiteout_args.inode)
- return -ENOMEM;
+ if (!whiteout_args.inode) {
+ ret = -ENOMEM;
+ goto out_fscrypt_names;
+ }
ret = btrfs_new_inode_prepare(&whiteout_args, &trans_num_items);
if (ret)
goto out_whiteout_inode;
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 5c636e00d77da3..af97413abcf43b 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -2765,9 +2765,19 @@ int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans)
/*
* Old roots should be searched when inserting qgroup
- * extent record
+ * extent record.
+ *
+ * But for INCONSISTENT (NO_ACCOUNTING) -> rescan case,
+ * we may have some record inserted during
+ * NO_ACCOUNTING (thus no old_roots populated), but
+ * later we start rescan, which clears NO_ACCOUNTING,
+ * leaving some inserted records without old_roots
+ * populated.
+ *
+ * Those cases are rare and should not cause too much
+ * time spent during commit_transaction().
*/
- if (WARN_ON(!record->old_roots)) {
+ if (!record->old_roots) {
/* Search commit root to find old_roots */
ret = btrfs_find_all_roots(&ctx, false);
if (ret < 0)
@@ -2787,6 +2797,7 @@ int btrfs_qgroup_account_extents(struct btrfs_trans_handle *trans)
* current root. It's safe inside commit_transaction().
*/
ctx.trans = trans;
+ ctx.time_seq = BTRFS_SEQ_LAST;
ret = btrfs_find_all_roots(&ctx, false);
if (ret < 0)
goto cleanup;
@@ -3356,6 +3367,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
int err = -ENOMEM;
int ret = 0;
bool stopped = false;
+ bool did_leaf_rescans = false;
path = btrfs_alloc_path();
if (!path)
@@ -3376,6 +3388,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
}
err = qgroup_rescan_leaf(trans, path);
+ did_leaf_rescans = true;
if (err > 0)
btrfs_commit_transaction(trans);
@@ -3396,16 +3409,23 @@ out:
mutex_unlock(&fs_info->qgroup_rescan_lock);
/*
- * only update status, since the previous part has already updated the
- * qgroup info.
+ * Only update status, since the previous part has already updated the
+ * qgroup info, and only if we did any actual work. This also prevents
+ * race with a concurrent quota disable, which has already set
+ * fs_info->quota_root to NULL and cleared BTRFS_FS_QUOTA_ENABLED at
+ * btrfs_quota_disable().
*/
- trans = btrfs_start_transaction(fs_info->quota_root, 1);
- if (IS_ERR(trans)) {
- err = PTR_ERR(trans);
+ if (did_leaf_rescans) {
+ trans = btrfs_start_transaction(fs_info->quota_root, 1);
+ if (IS_ERR(trans)) {
+ err = PTR_ERR(trans);
+ trans = NULL;
+ btrfs_err(fs_info,
+ "fail to start transaction for status update: %d",
+ err);
+ }
+ } else {
trans = NULL;
- btrfs_err(fs_info,
- "fail to start transaction for status update: %d",
- err);
}
mutex_lock(&fs_info->qgroup_rescan_lock);
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index 2d90a6b5eb00e3..ff4b1d583788c8 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -1426,12 +1426,20 @@ static void rbio_update_error_bitmap(struct btrfs_raid_bio *rbio, struct bio *bi
u32 bio_size = 0;
struct bio_vec *bvec;
struct bvec_iter_all iter_all;
+ int i;
bio_for_each_segment_all(bvec, bio, iter_all)
bio_size += bvec->bv_len;
- bitmap_set(rbio->error_bitmap, total_sector_nr,
- bio_size >> rbio->bioc->fs_info->sectorsize_bits);
+ /*
+ * Since we can have multiple bios touching the error_bitmap, we cannot
+ * call bitmap_set() without protection.
+ *
+ * Instead use set_bit() for each bit, as set_bit() itself is atomic.
+ */
+ for (i = total_sector_nr; i < total_sector_nr +
+ (bio_size >> rbio->bioc->fs_info->sectorsize_bits); i++)
+ set_bit(i, rbio->error_bitmap);
}
/* Verify the data sectors at read time. */
@@ -1886,7 +1894,7 @@ pstripe:
sector->uptodate = 1;
}
if (failb >= 0) {
- ret = verify_one_sector(rbio, faila, sector_nr);
+ ret = verify_one_sector(rbio, failb, sector_nr);
if (ret < 0)
goto cleanup;
@@ -2646,7 +2654,7 @@ static int recover_scrub_rbio(struct btrfs_raid_bio *rbio)
void **pointers = NULL;
void **unmap_array = NULL;
int sector_nr;
- int ret;
+ int ret = 0;
/*
* @pointers array stores the pointer for each sector.
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index e65e6b6600a744..d50182b6deec8f 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -8073,10 +8073,10 @@ long btrfs_ioctl_send(struct inode *inode, struct btrfs_ioctl_send_args *arg)
/*
* Check that we don't overflow at later allocations, we request
* clone_sources_count + 1 items, and compare to unsigned long inside
- * access_ok.
+ * access_ok. Also set an upper limit for allocation size so this can't
+ * easily exhaust memory. Max number of clone sources is about 200K.
*/
- if (arg->clone_sources_count >
- ULONG_MAX / sizeof(struct clone_root) - 1) {
+ if (arg->clone_sources_count > SZ_8M / sizeof(struct clone_root)) {
ret = -EINVAL;
goto out;
}
diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c
index d28ee4e36f3d90..69c09508afb506 100644
--- a/fs/btrfs/space-info.c
+++ b/fs/btrfs/space-info.c
@@ -407,7 +407,8 @@ int btrfs_can_overcommit(struct btrfs_fs_info *fs_info,
return 0;
used = btrfs_space_info_used(space_info, true);
- if (btrfs_is_zoned(fs_info) && (space_info->flags & BTRFS_BLOCK_GROUP_METADATA))
+ if (test_bit(BTRFS_FS_NO_OVERCOMMIT, &fs_info->flags) &&
+ (space_info->flags & BTRFS_BLOCK_GROUP_METADATA))
avail = 0;
else
avail = calc_available_free_space(fs_info, space_info, flush);
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 93f52ee85f6fe9..433ce221dc5c79 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1705,7 +1705,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
if (ret)
goto restore;
- ret = btrfs_check_features(fs_info, sb);
+ ret = btrfs_check_features(fs_info, !(*flags & SB_RDONLY));
if (ret < 0)
goto restore;
@@ -2514,6 +2514,7 @@ static __always_inline void btrfs_exit_btrfs_fs(void)
static void __exit exit_btrfs_fs(void)
{
btrfs_exit_btrfs_fs();
+ btrfs_cleanup_fs_uuids();
}
static int __init init_btrfs_fs(void)
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c
index a3c43f0b1c95c0..58599189bd1886 100644
--- a/fs/btrfs/tree-log.c
+++ b/fs/btrfs/tree-log.c
@@ -2980,7 +2980,6 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
ret = 0;
if (ret) {
blk_finish_plug(&plug);
- btrfs_abort_transaction(trans, ret);
btrfs_set_log_full_commit(trans);
mutex_unlock(&root->log_mutex);
goto out;
@@ -3045,15 +3044,12 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
blk_finish_plug(&plug);
btrfs_set_log_full_commit(trans);
-
- if (ret != -ENOSPC) {
- btrfs_abort_transaction(trans, ret);
- mutex_unlock(&log_root_tree->log_mutex);
- goto out;
- }
+ if (ret != -ENOSPC)
+ btrfs_err(fs_info,
+ "failed to update log for root %llu ret %d",
+ root->root_key.objectid, ret);
btrfs_wait_tree_log_extents(log, mark);
mutex_unlock(&log_root_tree->log_mutex);
- ret = BTRFS_LOG_FORCE_COMMIT;
goto out;
}
@@ -3112,7 +3108,6 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
goto out_wake_log_root;
} else if (ret) {
btrfs_set_log_full_commit(trans);
- btrfs_abort_transaction(trans, ret);
mutex_unlock(&log_root_tree->log_mutex);
goto out_wake_log_root;
}
@@ -3581,17 +3576,19 @@ static noinline int insert_dir_log_key(struct btrfs_trans_handle *trans,
}
static int flush_dir_items_batch(struct btrfs_trans_handle *trans,
- struct btrfs_root *log,
+ struct btrfs_inode *inode,
struct extent_buffer *src,
struct btrfs_path *dst_path,
int start_slot,
int count)
{
+ struct btrfs_root *log = inode->root->log_root;
char *ins_data = NULL;
struct btrfs_item_batch batch;
struct extent_buffer *dst;
unsigned long src_offset;
unsigned long dst_offset;
+ u64 last_index;
struct btrfs_key key;
u32 item_size;
int ret;
@@ -3649,6 +3646,19 @@ static int flush_dir_items_batch(struct btrfs_trans_handle *trans,
src_offset = btrfs_item_ptr_offset(src, start_slot + count - 1);
copy_extent_buffer(dst, src, dst_offset, src_offset, batch.total_data_size);
btrfs_release_path(dst_path);
+
+ last_index = batch.keys[count - 1].offset;
+ ASSERT(last_index > inode->last_dir_index_offset);
+
+ /*
+ * If for some unexpected reason the last item's index is not greater
+ * than the last index we logged, warn and return an error to fallback
+ * to a transaction commit.
+ */
+ if (WARN_ON(last_index <= inode->last_dir_index_offset))
+ ret = -EUCLEAN;
+ else
+ inode->last_dir_index_offset = last_index;
out:
kfree(ins_data);
@@ -3698,7 +3708,6 @@ static int process_dir_items_leaf(struct btrfs_trans_handle *trans,
}
di = btrfs_item_ptr(src, i, struct btrfs_dir_item);
- ctx->last_dir_item_offset = key.offset;
/*
* Skip ranges of items that consist only of dir item keys created
@@ -3761,7 +3770,7 @@ static int process_dir_items_leaf(struct btrfs_trans_handle *trans,
if (batch_size > 0) {
int ret;
- ret = flush_dir_items_batch(trans, log, src, dst_path,
+ ret = flush_dir_items_batch(trans, inode, src, dst_path,
batch_start, batch_size);
if (ret < 0)
return ret;
@@ -3826,7 +3835,10 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
path->slots[0]);
if (tmp.type == BTRFS_DIR_INDEX_KEY)
last_old_dentry_offset = tmp.offset;
+ } else if (ret < 0) {
+ err = ret;
}
+
goto done;
}
@@ -3846,19 +3858,34 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
*/
if (tmp.type == BTRFS_DIR_INDEX_KEY)
last_old_dentry_offset = tmp.offset;
+ } else if (ret < 0) {
+ err = ret;
+ goto done;
}
+
btrfs_release_path(path);
/*
- * Find the first key from this transaction again. See the note for
- * log_new_dir_dentries, if we're logging a directory recursively we
- * won't be holding its i_mutex, which means we can modify the directory
- * while we're logging it. If we remove an entry between our first
- * search and this search we'll not find the key again and can just
- * bail.
+ * Find the first key from this transaction again or the one we were at
+ * in the loop below in case we had to reschedule. We may be logging the
+ * directory without holding its VFS lock, which happen when logging new
+ * dentries (through log_new_dir_dentries()) or in some cases when we
+ * need to log the parent directory of an inode. This means a dir index
+ * key might be deleted from the inode's root, and therefore we may not
+ * find it anymore. If we can't find it, just move to the next key. We
+ * can not bail out and ignore, because if we do that we will simply
+ * not log dir index keys that come after the one that was just deleted
+ * and we can end up logging a dir index range that ends at (u64)-1
+ * (@last_offset is initialized to that), resulting in removing dir
+ * entries we should not remove at log replay time.
*/
search:
ret = btrfs_search_slot(NULL, root, &min_key, path, 0, 0);
+ if (ret > 0)
+ ret = btrfs_next_item(root, path);
+ if (ret < 0)
+ err = ret;
+ /* If ret is 1, there are no more keys in the inode's root. */
if (ret != 0)
goto done;
@@ -4031,7 +4058,6 @@ static noinline int log_directory_changes(struct btrfs_trans_handle *trans,
min_key = BTRFS_DIR_START_INDEX;
max_key = 0;
- ctx->last_dir_item_offset = inode->last_dir_index_offset;
while (1) {
ret = log_dir_items(trans, inode, path, dst_path,
@@ -4043,8 +4069,6 @@ static noinline int log_directory_changes(struct btrfs_trans_handle *trans,
min_key = max_key + 1;
}
- inode->last_dir_index_offset = ctx->last_dir_item_offset;
-
return 0;
}
@@ -5580,8 +5604,10 @@ static int add_conflicting_inode(struct btrfs_trans_handle *trans,
* LOG_INODE_EXISTS mode) and slow down other fsyncs or transaction
* commits.
*/
- if (ctx->num_conflict_inodes >= MAX_CONFLICT_INODES)
+ if (ctx->num_conflict_inodes >= MAX_CONFLICT_INODES) {
+ btrfs_set_log_full_commit(trans);
return BTRFS_LOG_FORCE_COMMIT;
+ }
inode = btrfs_iget(root->fs_info->sb, ino, root);
/*
@@ -7459,8 +7485,11 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
* not fail, but if it does, it's not serious, just bail out and
* mark the log for a full commit.
*/
- if (WARN_ON_ONCE(ret < 0))
+ if (WARN_ON_ONCE(ret < 0)) {
+ fscrypt_free_filename(&fname);
goto out;
+ }
+
log_pinned = true;
path = btrfs_alloc_path();
diff --git a/fs/btrfs/tree-log.h b/fs/btrfs/tree-log.h
index 85b43075ac58fb..85cd24cb0540d1 100644
--- a/fs/btrfs/tree-log.h
+++ b/fs/btrfs/tree-log.h
@@ -24,8 +24,6 @@ struct btrfs_log_ctx {
bool logging_new_delayed_dentries;
/* Indicate if the inode being logged was logged before. */
bool logged_before;
- /* Tracks the last logged dir item/index key offset. */
- u64 last_dir_item_offset;
struct inode *inode;
struct list_head list;
/* Only used for fast fsyncs. */
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index aa25fa335d3ed1..df43093b7a46d0 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -403,6 +403,7 @@ void btrfs_free_device(struct btrfs_device *device)
static void free_fs_devices(struct btrfs_fs_devices *fs_devices)
{
struct btrfs_device *device;
+
WARN_ON(fs_devices->opened);
while (!list_empty(&fs_devices->devices)) {
device = list_entry(fs_devices->devices.next,
@@ -768,8 +769,11 @@ static noinline struct btrfs_device *device_list_add(const char *path,
BTRFS_SUPER_FLAG_CHANGING_FSID_V2);
error = lookup_bdev(path, &path_devt);
- if (error)
+ if (error) {
+ btrfs_err(NULL, "failed to lookup block device for path %s: %d",
+ path, error);
return ERR_PTR(error);
+ }
if (fsid_change_in_progress) {
if (!has_metadata_uuid)
@@ -836,6 +840,9 @@ static noinline struct btrfs_device *device_list_add(const char *path,
unsigned int nofs_flag;
if (fs_devices->opened) {
+ btrfs_err(NULL,
+ "device %s belongs to fsid %pU, and the fs is already mounted",
+ path, fs_devices->fsid);
mutex_unlock(&fs_devices->device_list_mutex);
return ERR_PTR(-EBUSY);
}
@@ -905,6 +912,9 @@ static noinline struct btrfs_device *device_list_add(const char *path,
* generation are equal.
*/
mutex_unlock(&fs_devices->device_list_mutex);
+ btrfs_err(NULL,
+"device %s already registered with a higher generation, found %llu expect %llu",
+ path, found_transid, device->generation);
return ERR_PTR(-EEXIST);
}
@@ -1172,9 +1182,22 @@ void btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
mutex_lock(&uuid_mutex);
close_fs_devices(fs_devices);
- if (!fs_devices->opened)
+ if (!fs_devices->opened) {
list_splice_init(&fs_devices->seed_list, &list);
+ /*
+ * If the struct btrfs_fs_devices is not assembled with any
+ * other device, it can be re-initialized during the next mount
+ * without the needing device-scan step. Therefore, it can be
+ * fully freed.
+ */
+ if (fs_devices->num_devices == 1) {
+ list_del(&fs_devices->fs_list);
+ free_fs_devices(fs_devices);
+ }
+ }
+
+
list_for_each_entry_safe(fs_devices, tmp, &list, seed_list) {
close_fs_devices(fs_devices);
list_del(&fs_devices->seed_list);
@@ -1591,7 +1614,7 @@ again:
if (ret < 0)
goto out;
- while (1) {
+ while (search_start < search_end) {
l = path->nodes[0];
slot = path->slots[0];
if (slot >= btrfs_header_nritems(l)) {
@@ -1614,6 +1637,9 @@ again:
if (key.type != BTRFS_DEV_EXTENT_KEY)
goto next;
+ if (key.offset > search_end)
+ break;
+
if (key.offset > search_start) {
hole_size = key.offset - search_start;
dev_extent_hole_check(device, &search_start, &hole_size,
@@ -1674,6 +1700,7 @@ next:
else
ret = 0;
+ ASSERT(max_hole_start + max_hole_size <= search_end);
out:
btrfs_free_path(path);
*start = max_hole_start;
@@ -2005,42 +2032,42 @@ static u64 btrfs_num_devices(struct btrfs_fs_info *fs_info)
return num_devices;
}
+static void btrfs_scratch_superblock(struct btrfs_fs_info *fs_info,
+ struct block_device *bdev, int copy_num)
+{
+ struct btrfs_super_block *disk_super;
+ const size_t len = sizeof(disk_super->magic);
+ const u64 bytenr = btrfs_sb_offset(copy_num);
+ int ret;
+
+ disk_super = btrfs_read_disk_super(bdev, bytenr, bytenr);
+ if (IS_ERR(disk_super))
+ return;
+
+ memset(&disk_super->magic, 0, len);
+ folio_mark_dirty(virt_to_folio(disk_super));
+ btrfs_release_disk_super(disk_super);
+
+ ret = sync_blockdev_range(bdev, bytenr, bytenr + len - 1);
+ if (ret)
+ btrfs_warn(fs_info, "error clearing superblock number %d (%d)",
+ copy_num, ret);
+}
+
void btrfs_scratch_superblocks(struct btrfs_fs_info *fs_info,
struct block_device *bdev,
const char *device_path)
{
- struct btrfs_super_block *disk_super;
int copy_num;
if (!bdev)
return;
for (copy_num = 0; copy_num < BTRFS_SUPER_MIRROR_MAX; copy_num++) {
- struct page *page;
- int ret;
-
- disk_super = btrfs_read_dev_one_super(bdev, copy_num, false);
- if (IS_ERR(disk_super))
- continue;
-
- if (bdev_is_zoned(bdev)) {
+ if (bdev_is_zoned(bdev))
btrfs_reset_sb_log_zones(bdev, copy_num);
- continue;
- }
-
- memset(&disk_super->magic, 0, sizeof(disk_super->magic));
-
- page = virt_to_page(disk_super);
- set_page_dirty(page);
- lock_page(page);
- /* write_on_page() unlocks the page */
- ret = write_one_page(page);
- if (ret)
- btrfs_warn(fs_info,
- "error clearing superblock number %d (%d)",
- copy_num, ret);
- btrfs_release_disk_super(disk_super);
-
+ else
+ btrfs_scratch_superblock(fs_info, bdev, copy_num);
}
/* Notify udev that device has changed */
diff --git a/fs/btrfs/zlib.c b/fs/btrfs/zlib.c
index 01a13de1183209..da7bb9187b68a3 100644
--- a/fs/btrfs/zlib.c
+++ b/fs/btrfs/zlib.c
@@ -63,7 +63,7 @@ struct list_head *zlib_alloc_workspace(unsigned int level)
workspacesize = max(zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL),
zlib_inflate_workspacesize());
- workspace->strm.workspace = kvmalloc(workspacesize, GFP_KERNEL);
+ workspace->strm.workspace = kvzalloc(workspacesize, GFP_KERNEL);
workspace->level = level;
workspace->buf = NULL;
/*
diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
index a759668477bb2e..1f503e8e42d483 100644
--- a/fs/btrfs/zoned.c
+++ b/fs/btrfs/zoned.c
@@ -539,6 +539,8 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache)
}
atomic_set(&zone_info->active_zones_left,
max_active_zones - nactive);
+ /* Overcommit does not work well with active zone tacking. */
+ set_bit(BTRFS_FS_NO_OVERCOMMIT, &fs_info->flags);
}
/* Validate superblock log */
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index 8c74871e37c9dc..cac4083e387a50 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -305,7 +305,7 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq)
struct inode *inode = rreq->inode;
struct ceph_inode_info *ci = ceph_inode(inode);
struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
- struct ceph_osd_request *req;
+ struct ceph_osd_request *req = NULL;
struct ceph_vino vino = ceph_vino(inode);
struct iov_iter iter;
struct page **pages;
@@ -313,6 +313,11 @@ static void ceph_netfs_issue_read(struct netfs_io_subrequest *subreq)
int err = 0;
u64 len = subreq->len;
+ if (ceph_inode_is_shutdown(inode)) {
+ err = -EIO;
+ goto out;
+ }
+
if (ceph_has_inline_data(ci) && ceph_netfs_issue_op_inline(subreq))
return;
@@ -563,6 +568,9 @@ static int writepage_nounlock(struct page *page, struct writeback_control *wbc)
dout("writepage %p idx %lu\n", page, page->index);
+ if (ceph_inode_is_shutdown(inode))
+ return -EIO;
+
/* verify this is a writeable snap context */
snapc = page_snap_context(page);
if (!snapc) {
@@ -1643,7 +1651,7 @@ int ceph_uninline_data(struct file *file)
struct ceph_inode_info *ci = ceph_inode(inode);
struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
struct ceph_osd_request *req = NULL;
- struct ceph_cap_flush *prealloc_cf;
+ struct ceph_cap_flush *prealloc_cf = NULL;
struct folio *folio = NULL;
u64 inline_version = CEPH_INLINE_NONE;
struct page *pages[1];
@@ -1657,6 +1665,11 @@ int ceph_uninline_data(struct file *file)
dout("uninline_data %p %llx.%llx inline_version %llu\n",
inode, ceph_vinop(inode), inline_version);
+ if (ceph_inode_is_shutdown(inode)) {
+ err = -EIO;
+ goto out;
+ }
+
if (inline_version == CEPH_INLINE_NONE)
return 0;
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
index 4b159f97fe7b52..210e4003788183 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -2913,7 +2913,7 @@ int ceph_get_caps(struct file *filp, int need, int want, loff_t endoff, int *got
while (true) {
flags &= CEPH_FILE_MODE_MASK;
- if (atomic_read(&fi->num_locks))
+ if (vfs_inode_has_locks(inode))
flags |= CHECK_FILELOCK;
_got = 0;
ret = try_get_cap_refs(inode, need, want, endoff,
@@ -4078,6 +4078,7 @@ void ceph_handle_caps(struct ceph_mds_session *session,
void *p, *end;
struct cap_extra_info extra_info = {};
bool queue_trunc;
+ bool close_sessions = false;
dout("handle_caps from mds%d\n", session->s_mds);
@@ -4215,9 +4216,13 @@ void ceph_handle_caps(struct ceph_mds_session *session,
realm = NULL;
if (snaptrace_len) {
down_write(&mdsc->snap_rwsem);
- ceph_update_snap_trace(mdsc, snaptrace,
- snaptrace + snaptrace_len,
- false, &realm);
+ if (ceph_update_snap_trace(mdsc, snaptrace,
+ snaptrace + snaptrace_len,
+ false, &realm)) {
+ up_write(&mdsc->snap_rwsem);
+ close_sessions = true;
+ goto done;
+ }
downgrade_write(&mdsc->snap_rwsem);
} else {
down_read(&mdsc->snap_rwsem);
@@ -4277,6 +4282,11 @@ done_unlocked:
iput(inode);
out:
ceph_put_string(extra_info.pool_ns);
+
+ /* Defer closing the sessions after s_mutex lock being released */
+ if (close_sessions)
+ ceph_mdsc_close_sessions(mdsc);
+
return;
flush_cap_releases:
diff --git a/fs/ceph/file.c b/fs/ceph/file.c
index 764598e1efd91f..b5cff85925a109 100644
--- a/fs/ceph/file.c
+++ b/fs/ceph/file.c
@@ -2011,6 +2011,9 @@ static int ceph_zero_partial_object(struct inode *inode,
loff_t zero = 0;
int op;
+ if (ceph_inode_is_shutdown(inode))
+ return -EIO;
+
if (!length) {
op = offset ? CEPH_OSD_OP_DELETE : CEPH_OSD_OP_TRUNCATE;
length = &zero;
diff --git a/fs/ceph/locks.c b/fs/ceph/locks.c
index f3b461c708a8be..9c8dc8a55e7e13 100644
--- a/fs/ceph/locks.c
+++ b/fs/ceph/locks.c
@@ -32,24 +32,36 @@ void __init ceph_flock_init(void)
static void ceph_fl_copy_lock(struct file_lock *dst, struct file_lock *src)
{
- struct ceph_file_info *fi = dst->fl_file->private_data;
struct inode *inode = file_inode(dst->fl_file);
atomic_inc(&ceph_inode(inode)->i_filelock_ref);
- atomic_inc(&fi->num_locks);
+ dst->fl_u.ceph.inode = igrab(inode);
}
+/*
+ * Do not use the 'fl->fl_file' in release function, which
+ * is possibly already released by another thread.
+ */
static void ceph_fl_release_lock(struct file_lock *fl)
{
- struct ceph_file_info *fi = fl->fl_file->private_data;
- struct inode *inode = file_inode(fl->fl_file);
- struct ceph_inode_info *ci = ceph_inode(inode);
- atomic_dec(&fi->num_locks);
+ struct inode *inode = fl->fl_u.ceph.inode;
+ struct ceph_inode_info *ci;
+
+ /*
+ * If inode is NULL it should be a request file_lock,
+ * nothing we can do.
+ */
+ if (!inode)
+ return;
+
+ 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);
}
+ fl->fl_u.ceph.inode = NULL;
+ iput(inode);
}
static const struct file_lock_operations ceph_fl_lock_ops = {
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index 26a0a8b9975ef3..27a245d959c0ad 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -806,6 +806,9 @@ static struct ceph_mds_session *register_session(struct ceph_mds_client *mdsc,
{
struct ceph_mds_session *s;
+ if (READ_ONCE(mdsc->fsc->mount_state) == CEPH_MOUNT_FENCE_IO)
+ return ERR_PTR(-EIO);
+
if (mds >= mdsc->mdsmap->possible_max_rank)
return ERR_PTR(-EINVAL);
@@ -1478,6 +1481,9 @@ static int __open_session(struct ceph_mds_client *mdsc,
int mstate;
int mds = session->s_mds;
+ if (READ_ONCE(mdsc->fsc->mount_state) == CEPH_MOUNT_FENCE_IO)
+ return -EIO;
+
/* wait for mds to go active? */
mstate = ceph_mdsmap_get_state(mdsc->mdsmap, mds);
dout("open_session to mds%d (%s)\n", mds,
@@ -2860,6 +2866,11 @@ static void __do_request(struct ceph_mds_client *mdsc,
return;
}
+ if (READ_ONCE(mdsc->fsc->mount_state) == CEPH_MOUNT_FENCE_IO) {
+ dout("do_request metadata corrupted\n");
+ err = -EIO;
+ goto finish;
+ }
if (req->r_timeout &&
time_after_eq(jiffies, req->r_started + req->r_timeout)) {
dout("do_request timed out\n");
@@ -3245,6 +3256,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg)
u64 tid;
int err, result;
int mds = session->s_mds;
+ bool close_sessions = false;
if (msg->front.iov_len < sizeof(*head)) {
pr_err("mdsc_handle_reply got corrupt (short) reply\n");
@@ -3351,10 +3363,17 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg)
realm = NULL;
if (rinfo->snapblob_len) {
down_write(&mdsc->snap_rwsem);
- ceph_update_snap_trace(mdsc, rinfo->snapblob,
+ err = ceph_update_snap_trace(mdsc, rinfo->snapblob,
rinfo->snapblob + rinfo->snapblob_len,
le32_to_cpu(head->op) == CEPH_MDS_OP_RMSNAP,
&realm);
+ if (err) {
+ up_write(&mdsc->snap_rwsem);
+ close_sessions = true;
+ if (err == -EIO)
+ ceph_msg_dump(msg);
+ goto out_err;
+ }
downgrade_write(&mdsc->snap_rwsem);
} else {
down_read(&mdsc->snap_rwsem);
@@ -3412,6 +3431,10 @@ out_err:
req->r_end_latency, err);
out:
ceph_mdsc_put_request(req);
+
+ /* Defer closing the sessions after s_mutex lock being released */
+ if (close_sessions)
+ ceph_mdsc_close_sessions(mdsc);
return;
}
@@ -3662,6 +3685,12 @@ static void handle_session(struct ceph_mds_session *session,
break;
case CEPH_SESSION_FLUSHMSG:
+ /* flush cap releases */
+ spin_lock(&session->s_cap_lock);
+ if (session->s_num_cap_releases)
+ ceph_flush_cap_releases(mdsc, session);
+ spin_unlock(&session->s_cap_lock);
+
send_flushmsg_ack(mdsc, session, seq);
break;
@@ -5011,7 +5040,7 @@ static bool done_closing_sessions(struct ceph_mds_client *mdsc, int skipped)
}
/*
- * called after sb is ro.
+ * called after sb is ro or when metadata corrupted.
*/
void ceph_mdsc_close_sessions(struct ceph_mds_client *mdsc)
{
@@ -5301,7 +5330,8 @@ static void mds_peer_reset(struct ceph_connection *con)
struct ceph_mds_client *mdsc = s->s_mdsc;
pr_warn("mds%d closed our session\n", s->s_mds);
- send_mds_reconnect(mdsc, s);
+ if (READ_ONCE(mdsc->fsc->mount_state) != CEPH_MOUNT_FENCE_IO)
+ send_mds_reconnect(mdsc, s);
}
static void mds_dispatch(struct ceph_connection *con, struct ceph_msg *msg)
diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c
index e4151852184e04..87007203f130e1 100644
--- a/fs/ceph/snap.c
+++ b/fs/ceph/snap.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/ceph/ceph_debug.h>
+#include <linux/fs.h>
#include <linux/sort.h>
#include <linux/slab.h>
#include <linux/iversion.h>
@@ -766,8 +767,10 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc,
struct ceph_snap_realm *realm;
struct ceph_snap_realm *first_realm = NULL;
struct ceph_snap_realm *realm_to_rebuild = NULL;
+ struct ceph_client *client = mdsc->fsc->client;
int rebuild_snapcs;
int err = -ENOMEM;
+ int ret;
LIST_HEAD(dirty_realms);
lockdep_assert_held_write(&mdsc->snap_rwsem);
@@ -884,6 +887,27 @@ fail:
if (first_realm)
ceph_put_snap_realm(mdsc, first_realm);
pr_err("%s error %d\n", __func__, err);
+
+ /*
+ * When receiving a corrupted snap trace we don't know what
+ * exactly has happened in MDS side. And we shouldn't continue
+ * writing to OSD, which may corrupt the snapshot contents.
+ *
+ * Just try to blocklist this kclient and then this kclient
+ * must be remounted to continue after the corrupted metadata
+ * fixed in the MDS side.
+ */
+ WRITE_ONCE(mdsc->fsc->mount_state, CEPH_MOUNT_FENCE_IO);
+ ret = ceph_monc_blocklist_add(&client->monc, &client->msgr.inst.addr);
+ if (ret)
+ pr_err("%s failed to blocklist %s: %d\n", __func__,
+ ceph_pr_addr(&client->msgr.inst.addr), ret);
+
+ WARN(1, "%s: %s%sdo remount to continue%s",
+ __func__, ret ? "" : ceph_pr_addr(&client->msgr.inst.addr),
+ ret ? "" : " was blocklisted, ",
+ err == -EIO ? " after corrupted snaptrace is fixed" : "");
+
return err;
}
@@ -984,6 +1008,7 @@ void ceph_handle_snap(struct ceph_mds_client *mdsc,
__le64 *split_inos = NULL, *split_realms = NULL;
int i;
int locked_rwsem = 0;
+ bool close_sessions = false;
/* decode */
if (msg->front.iov_len < sizeof(*h))
@@ -1092,8 +1117,12 @@ skip_inode:
* update using the provided snap trace. if we are deleting a
* snap, we can avoid queueing cap_snaps.
*/
- ceph_update_snap_trace(mdsc, p, e,
- op == CEPH_SNAP_OP_DESTROY, NULL);
+ if (ceph_update_snap_trace(mdsc, p, e,
+ op == CEPH_SNAP_OP_DESTROY,
+ NULL)) {
+ close_sessions = true;
+ goto bad;
+ }
if (op == CEPH_SNAP_OP_SPLIT)
/* we took a reference when we created the realm, above */
@@ -1112,6 +1141,9 @@ bad:
out:
if (locked_rwsem)
up_write(&mdsc->snap_rwsem);
+
+ if (close_sessions)
+ ceph_mdsc_close_sessions(mdsc);
return;
}
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 30bdb391a0dc08..07c6906cda70d6 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -100,6 +100,17 @@ struct ceph_mount_options {
char *mon_addr;
};
+/* mount state */
+enum {
+ CEPH_MOUNT_MOUNTING,
+ CEPH_MOUNT_MOUNTED,
+ CEPH_MOUNT_UNMOUNTING,
+ CEPH_MOUNT_UNMOUNTED,
+ CEPH_MOUNT_SHUTDOWN,
+ CEPH_MOUNT_RECOVER,
+ CEPH_MOUNT_FENCE_IO,
+};
+
#define CEPH_ASYNC_CREATE_CONFLICT_BITS 8
struct ceph_fs_client {
@@ -790,7 +801,6 @@ struct ceph_file_info {
struct list_head rw_contexts;
u32 filp_gen;
- atomic_t num_locks;
};
struct ceph_dir_file_info {
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index 5db73c0f792a55..cbc18b4a9cb20c 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -278,6 +278,7 @@ build_avpair_blob(struct cifs_ses *ses, const struct nls_table *nls_cp)
* ( for NTLMSSP_AV_NB_DOMAIN_NAME followed by NTLMSSP_AV_EOL ) +
* unicode length of a netbios domain name
*/
+ kfree_sensitive(ses->auth_key.response);
ses->auth_key.len = size + 2 * dlen;
ses->auth_key.response = kzalloc(ses->auth_key.len, GFP_KERNEL);
if (!ses->auth_key.response) {
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index d371259d6808a2..b2a04b4e89a5e7 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -2606,11 +2606,14 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
INIT_LIST_HEAD(&tcon->pending_opens);
tcon->status = TID_GOOD;
- /* schedule query interfaces poll */
INIT_DELAYED_WORK(&tcon->query_interfaces,
smb2_query_server_interfaces);
- queue_delayed_work(cifsiod_wq, &tcon->query_interfaces,
- (SMB_INTERFACE_POLL_INTERVAL * HZ));
+ if (ses->server->dialect >= SMB30_PROT_ID &&
+ (ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
+ /* schedule query interfaces poll */
+ queue_delayed_work(cifsiod_wq, &tcon->query_interfaces,
+ (SMB_INTERFACE_POLL_INTERVAL * HZ));
+ }
spin_lock(&cifs_tcp_ses_lock);
list_add(&tcon->tcon_list, &ses->tcon_list);
diff --git a/fs/cifs/dfs.c b/fs/cifs/dfs.c
index b541e68378f647..b64d20374b9c85 100644
--- a/fs/cifs/dfs.c
+++ b/fs/cifs/dfs.c
@@ -327,8 +327,8 @@ static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb
return rc;
}
-static int target_share_matches_server(struct TCP_Server_Info *server, const char *tcp_host,
- size_t tcp_host_len, char *share, bool *target_match)
+static int target_share_matches_server(struct TCP_Server_Info *server, char *share,
+ bool *target_match)
{
int rc = 0;
const char *dfs_host;
@@ -338,13 +338,16 @@ static int target_share_matches_server(struct TCP_Server_Info *server, const cha
extract_unc_hostname(share, &dfs_host, &dfs_host_len);
/* Check if hostnames or addresses match */
- if (dfs_host_len != tcp_host_len || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
- cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len,
- dfs_host, (int)tcp_host_len, tcp_host);
+ cifs_server_lock(server);
+ if (dfs_host_len != strlen(server->hostname) ||
+ strncasecmp(dfs_host, server->hostname, dfs_host_len)) {
+ cifs_dbg(FYI, "%s: %.*s doesn't match %s\n", __func__,
+ (int)dfs_host_len, dfs_host, server->hostname);
rc = match_target_ip(server, dfs_host, dfs_host_len, target_match);
if (rc)
cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc);
}
+ cifs_server_unlock(server);
return rc;
}
@@ -358,13 +361,9 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(tcon->ses);
struct cifs_tcon *ipc = root_ses->tcon_ipc;
char *share = NULL, *prefix = NULL;
- const char *tcp_host;
- size_t tcp_host_len;
struct dfs_cache_tgt_iterator *tit;
bool target_match;
- extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
-
tit = dfs_cache_get_tgt_iterator(tl);
if (!tit) {
rc = -ENOENT;
@@ -387,8 +386,7 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
break;
}
- rc = target_share_matches_server(server, tcp_host, tcp_host_len, share,
- &target_match);
+ rc = target_share_matches_server(server, share, &target_match);
if (rc)
break;
if (!target_match) {
@@ -401,8 +399,7 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
if (ipc->need_reconnect) {
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
rc = ops->tree_connect(xid, ipc->ses, tree, ipc, cifs_sb->local_nls);
- if (rc)
- break;
+ cifs_dbg(FYI, "%s: reconnect ipc: %d\n", __func__, rc);
}
scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
@@ -498,7 +495,9 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
}
if (tcon->ipc) {
+ cifs_server_lock(server);
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
+ cifs_server_unlock(server);
rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
goto out;
}
diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c
index 43ad1176dcb9d6..ac86bd0ebd637b 100644
--- a/fs/cifs/dfs_cache.c
+++ b/fs/cifs/dfs_cache.c
@@ -269,7 +269,7 @@ static int dfscache_proc_show(struct seq_file *m, void *v)
list_for_each_entry(t, &ce->tlist, list) {
seq_printf(m, " %s%s\n",
t->name,
- ce->tgthint == t ? " (target hint)" : "");
+ READ_ONCE(ce->tgthint) == t ? " (target hint)" : "");
}
}
}
@@ -321,7 +321,7 @@ static inline void dump_tgts(const struct cache_entry *ce)
cifs_dbg(FYI, "target list:\n");
list_for_each_entry(t, &ce->tlist, list) {
cifs_dbg(FYI, " %s%s\n", t->name,
- ce->tgthint == t ? " (target hint)" : "");
+ READ_ONCE(ce->tgthint) == t ? " (target hint)" : "");
}
}
@@ -427,7 +427,7 @@ static int cache_entry_hash(const void *data, int size, unsigned int *hash)
/* Return target hint of a DFS cache entry */
static inline char *get_tgt_name(const struct cache_entry *ce)
{
- struct cache_dfs_tgt *t = ce->tgthint;
+ struct cache_dfs_tgt *t = READ_ONCE(ce->tgthint);
return t ? t->name : ERR_PTR(-ENOENT);
}
@@ -470,6 +470,7 @@ static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed)
static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs,
struct cache_entry *ce, const char *tgthint)
{
+ struct cache_dfs_tgt *target;
int i;
ce->ttl = max_t(int, refs[0].ttl, CACHE_MIN_TTL);
@@ -496,8 +497,9 @@ static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs,
ce->numtgts++;
}
- ce->tgthint = list_first_entry_or_null(&ce->tlist,
- struct cache_dfs_tgt, list);
+ target = list_first_entry_or_null(&ce->tlist, struct cache_dfs_tgt,
+ list);
+ WRITE_ONCE(ce->tgthint, target);
return 0;
}
@@ -558,7 +560,8 @@ static void remove_oldest_entry_locked(void)
}
/* Add a new DFS cache entry */
-static int add_cache_entry_locked(struct dfs_info3_param *refs, int numrefs)
+static struct cache_entry *add_cache_entry_locked(struct dfs_info3_param *refs,
+ int numrefs)
{
int rc;
struct cache_entry *ce;
@@ -573,11 +576,11 @@ static int add_cache_entry_locked(struct dfs_info3_param *refs, int numrefs)
rc = cache_entry_hash(refs[0].path_name, strlen(refs[0].path_name), &hash);
if (rc)
- return rc;
+ return ERR_PTR(rc);
ce = alloc_cache_entry(refs, numrefs);
if (IS_ERR(ce))
- return PTR_ERR(ce);
+ return ce;
spin_lock(&cache_ttl_lock);
if (!cache_ttl) {
@@ -594,7 +597,7 @@ static int add_cache_entry_locked(struct dfs_info3_param *refs, int numrefs)
atomic_inc(&cache_count);
- return 0;
+ return ce;
}
/* Check if two DFS paths are equal. @s1 and @s2 are expected to be in @cache_cp's charset */
@@ -641,7 +644,9 @@ static struct cache_entry *__lookup_cache_entry(const char *path, unsigned int h
*
* Use whole path components in the match. Must be called with htable_rw_lock held.
*
+ * Return cached entry if successful.
* Return ERR_PTR(-ENOENT) if the entry is not found.
+ * Return error ptr otherwise.
*/
static struct cache_entry *lookup_cache_entry(const char *path)
{
@@ -711,14 +716,15 @@ void dfs_cache_destroy(void)
static int update_cache_entry_locked(struct cache_entry *ce, const struct dfs_info3_param *refs,
int numrefs)
{
+ struct cache_dfs_tgt *target;
+ char *th = NULL;
int rc;
- char *s, *th = NULL;
WARN_ON(!rwsem_is_locked(&htable_rw_lock));
- if (ce->tgthint) {
- s = ce->tgthint->name;
- th = kstrdup(s, GFP_ATOMIC);
+ target = READ_ONCE(ce->tgthint);
+ if (target) {
+ th = kstrdup(target->name, GFP_ATOMIC);
if (!th)
return -ENOMEM;
}
@@ -767,51 +773,75 @@ static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses, const
*
* For interlinks, cifs_mount() and expand_dfs_referral() are supposed to
* handle them properly.
+ *
+ * On success, return entry with acquired lock for reading, otherwise error ptr.
*/
-static int cache_refresh_path(const unsigned int xid, struct cifs_ses *ses, const char *path)
+static struct cache_entry *cache_refresh_path(const unsigned int xid,
+ struct cifs_ses *ses,
+ const char *path,
+ bool force_refresh)
{
- int rc;
- struct cache_entry *ce;
struct dfs_info3_param *refs = NULL;
+ struct cache_entry *ce;
int numrefs = 0;
- bool newent = false;
+ int rc;
cifs_dbg(FYI, "%s: search path: %s\n", __func__, path);
- down_write(&htable_rw_lock);
+ down_read(&htable_rw_lock);
ce = lookup_cache_entry(path);
if (!IS_ERR(ce)) {
- if (!cache_entry_expired(ce)) {
- dump_ce(ce);
- up_write(&htable_rw_lock);
- return 0;
- }
- } else {
- newent = true;
+ if (!force_refresh && !cache_entry_expired(ce))
+ return ce;
+ } else if (PTR_ERR(ce) != -ENOENT) {
+ up_read(&htable_rw_lock);
+ return ce;
}
/*
- * Either the entry was not found, or it is expired.
+ * Unlock shared access as we don't want to hold any locks while getting
+ * a new referral. The @ses used for performing the I/O could be
+ * reconnecting and it acquires @htable_rw_lock to look up the dfs cache
+ * in order to failover -- if necessary.
+ */
+ up_read(&htable_rw_lock);
+
+ /*
+ * Either the entry was not found, or it is expired, or it is a forced
+ * refresh.
* Request a new DFS referral in order to create or update a cache entry.
*/
rc = get_dfs_referral(xid, ses, path, &refs, &numrefs);
- if (rc)
- goto out_unlock;
+ if (rc) {
+ ce = ERR_PTR(rc);
+ goto out;
+ }
dump_refs(refs, numrefs);
- if (!newent) {
- rc = update_cache_entry_locked(ce, refs, numrefs);
- goto out_unlock;
+ down_write(&htable_rw_lock);
+ /* Re-check as another task might have it added or refreshed already */
+ ce = lookup_cache_entry(path);
+ if (!IS_ERR(ce)) {
+ if (force_refresh || cache_entry_expired(ce)) {
+ rc = update_cache_entry_locked(ce, refs, numrefs);
+ if (rc)
+ ce = ERR_PTR(rc);
+ }
+ } else if (PTR_ERR(ce) == -ENOENT) {
+ ce = add_cache_entry_locked(refs, numrefs);
}
- rc = add_cache_entry_locked(refs, numrefs);
+ if (IS_ERR(ce)) {
+ up_write(&htable_rw_lock);
+ goto out;
+ }
-out_unlock:
- up_write(&htable_rw_lock);
+ downgrade_write(&htable_rw_lock);
+out:
free_dfs_info_array(refs, numrefs);
- return rc;
+ return ce;
}
/*
@@ -878,7 +908,7 @@ static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl)
}
it->it_path_consumed = t->path_consumed;
- if (ce->tgthint == t)
+ if (READ_ONCE(ce->tgthint) == t)
list_add(&it->it_list, head);
else
list_add_tail(&it->it_list, head);
@@ -931,15 +961,8 @@ int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nl
if (IS_ERR(npath))
return PTR_ERR(npath);
- rc = cache_refresh_path(xid, ses, npath);
- if (rc)
- goto out_free_path;
-
- down_read(&htable_rw_lock);
-
- ce = lookup_cache_entry(npath);
+ ce = cache_refresh_path(xid, ses, npath, false);
if (IS_ERR(ce)) {
- up_read(&htable_rw_lock);
rc = PTR_ERR(ce);
goto out_free_path;
}
@@ -1003,72 +1026,6 @@ out_unlock:
}
/**
- * dfs_cache_update_tgthint - update target hint of a DFS cache entry
- *
- * If it doesn't find the cache entry, then it will get a DFS referral for @path
- * and create a new entry.
- *
- * In case the cache entry exists but expired, it will get a DFS referral
- * for @path and then update the respective cache entry.
- *
- * @xid: syscall id
- * @ses: smb session
- * @cp: codepage
- * @remap: type of character remapping for paths
- * @path: path to lookup in DFS referral cache
- * @it: DFS target iterator
- *
- * Return zero if the target hint was updated successfully, otherwise non-zero.
- */
-int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses,
- const struct nls_table *cp, int remap, const char *path,
- const struct dfs_cache_tgt_iterator *it)
-{
- int rc;
- const char *npath;
- struct cache_entry *ce;
- struct cache_dfs_tgt *t;
-
- npath = dfs_cache_canonical_path(path, cp, remap);
- if (IS_ERR(npath))
- return PTR_ERR(npath);
-
- cifs_dbg(FYI, "%s: update target hint - path: %s\n", __func__, npath);
-
- rc = cache_refresh_path(xid, ses, npath);
- if (rc)
- goto out_free_path;
-
- down_write(&htable_rw_lock);
-
- ce = lookup_cache_entry(npath);
- if (IS_ERR(ce)) {
- rc = PTR_ERR(ce);
- goto out_unlock;
- }
-
- t = ce->tgthint;
-
- if (likely(!strcasecmp(it->it_name, t->name)))
- goto out_unlock;
-
- list_for_each_entry(t, &ce->tlist, list) {
- if (!strcasecmp(t->name, it->it_name)) {
- ce->tgthint = t;
- cifs_dbg(FYI, "%s: new target hint: %s\n", __func__,
- it->it_name);
- break;
- }
- }
-
-out_unlock:
- up_write(&htable_rw_lock);
-out_free_path:
- kfree(npath);
- return rc;
-}
-
-/**
* dfs_cache_noreq_update_tgthint - update target hint of a DFS cache entry
* without sending any requests to the currently connected server.
*
@@ -1092,21 +1049,20 @@ void dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt
cifs_dbg(FYI, "%s: path: %s\n", __func__, path);
- if (!down_write_trylock(&htable_rw_lock))
- return;
+ down_read(&htable_rw_lock);
ce = lookup_cache_entry(path);
if (IS_ERR(ce))
goto out_unlock;
- t = ce->tgthint;
+ t = READ_ONCE(ce->tgthint);
if (unlikely(!strcasecmp(it->it_name, t->name)))
goto out_unlock;
list_for_each_entry(t, &ce->tlist, list) {
if (!strcasecmp(t->name, it->it_name)) {
- ce->tgthint = t;
+ WRITE_ONCE(ce->tgthint, t);
cifs_dbg(FYI, "%s: new target hint: %s\n", __func__,
it->it_name);
break;
@@ -1114,7 +1070,7 @@ void dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt
}
out_unlock:
- up_write(&htable_rw_lock);
+ up_read(&htable_rw_lock);
}
/**
@@ -1299,7 +1255,6 @@ static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, c
* Resolve share's hostname and check if server address matches. Otherwise just ignore it
* as we could not have upcall to resolve hostname or failed to convert ip address.
*/
- match = true;
extract_unc_hostname(s1, &host, &hostlen);
scnprintf(unc, sizeof(unc), "\\\\%.*s", (int)hostlen, host);
@@ -1321,35 +1276,37 @@ static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, c
* Mark dfs tcon for reconnecting when the currently connected tcon does not match any of the new
* target shares in @refs.
*/
-static void mark_for_reconnect_if_needed(struct cifs_tcon *tcon, struct dfs_cache_tgt_list *tl,
- const struct dfs_info3_param *refs, int numrefs)
+static void mark_for_reconnect_if_needed(struct TCP_Server_Info *server,
+ struct dfs_cache_tgt_list *old_tl,
+ struct dfs_cache_tgt_list *new_tl)
{
- struct dfs_cache_tgt_iterator *it;
- int i;
-
- for (it = dfs_cache_get_tgt_iterator(tl); it; it = dfs_cache_get_next_tgt(tl, it)) {
- for (i = 0; i < numrefs; i++) {
- if (target_share_equal(tcon->ses->server, dfs_cache_get_tgt_name(it),
- refs[i].node_name))
+ struct dfs_cache_tgt_iterator *oit, *nit;
+
+ for (oit = dfs_cache_get_tgt_iterator(old_tl); oit;
+ oit = dfs_cache_get_next_tgt(old_tl, oit)) {
+ for (nit = dfs_cache_get_tgt_iterator(new_tl); nit;
+ nit = dfs_cache_get_next_tgt(new_tl, nit)) {
+ if (target_share_equal(server,
+ dfs_cache_get_tgt_name(oit),
+ dfs_cache_get_tgt_name(nit)))
return;
}
}
cifs_dbg(FYI, "%s: no cached or matched targets. mark dfs share for reconnect.\n", __func__);
- cifs_signal_cifsd_for_reconnect(tcon->ses->server, true);
+ cifs_signal_cifsd_for_reconnect(server, true);
}
/* Refresh dfs referral of tcon and mark it for reconnect if needed */
static int __refresh_tcon(const char *path, struct cifs_tcon *tcon, bool force_refresh)
{
- struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
+ struct dfs_cache_tgt_list old_tl = DFS_CACHE_TGT_LIST_INIT(old_tl);
+ struct dfs_cache_tgt_list new_tl = DFS_CACHE_TGT_LIST_INIT(new_tl);
struct cifs_ses *ses = CIFS_DFS_ROOT_SES(tcon->ses);
struct cifs_tcon *ipc = ses->tcon_ipc;
- struct dfs_info3_param *refs = NULL;
bool needs_refresh = false;
struct cache_entry *ce;
unsigned int xid;
- int numrefs = 0;
int rc = 0;
xid = get_xid();
@@ -1358,9 +1315,8 @@ static int __refresh_tcon(const char *path, struct cifs_tcon *tcon, bool force_r
ce = lookup_cache_entry(path);
needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce);
if (!IS_ERR(ce)) {
- rc = get_targets(ce, &tl);
- if (rc)
- cifs_dbg(FYI, "%s: could not get dfs targets: %d\n", __func__, rc);
+ rc = get_targets(ce, &old_tl);
+ cifs_dbg(FYI, "%s: get_targets: %d\n", __func__, rc);
}
up_read(&htable_rw_lock);
@@ -1377,26 +1333,18 @@ static int __refresh_tcon(const char *path, struct cifs_tcon *tcon, bool force_r
}
spin_unlock(&ipc->tc_lock);
- rc = get_dfs_referral(xid, ses, path, &refs, &numrefs);
- if (!rc) {
- /* Create or update a cache entry with the new referral */
- dump_refs(refs, numrefs);
-
- down_write(&htable_rw_lock);
- ce = lookup_cache_entry(path);
- if (IS_ERR(ce))
- add_cache_entry_locked(refs, numrefs);
- else if (force_refresh || cache_entry_expired(ce))
- update_cache_entry_locked(ce, refs, numrefs);
- up_write(&htable_rw_lock);
-
- mark_for_reconnect_if_needed(tcon, &tl, refs, numrefs);
+ ce = cache_refresh_path(xid, ses, path, true);
+ if (!IS_ERR(ce)) {
+ rc = get_targets(ce, &new_tl);
+ up_read(&htable_rw_lock);
+ cifs_dbg(FYI, "%s: get_targets: %d\n", __func__, rc);
+ mark_for_reconnect_if_needed(tcon->ses->server, &old_tl, &new_tl);
}
out:
free_xid(xid);
- dfs_cache_free_tgts(&tl);
- free_dfs_info_array(refs, numrefs);
+ dfs_cache_free_tgts(&old_tl);
+ dfs_cache_free_tgts(&new_tl);
return rc;
}
diff --git a/fs/cifs/dfs_cache.h b/fs/cifs/dfs_cache.h
index f7cff0be932747..be3b5a44cf8271 100644
--- a/fs/cifs/dfs_cache.h
+++ b/fs/cifs/dfs_cache.h
@@ -35,9 +35,6 @@ int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nl
struct dfs_cache_tgt_list *tgt_list);
int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref,
struct dfs_cache_tgt_list *tgt_list);
-int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses,
- const struct nls_table *cp, int remap, const char *path,
- const struct dfs_cache_tgt_iterator *it);
void dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt_iterator *it);
int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iterator *it,
struct dfs_info3_param *ref);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 22dfc1f8b4f12f..b8d1cbadb68977 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -3889,7 +3889,7 @@ uncached_fill_pages(struct TCP_Server_Info *server,
rdata->got_bytes += result;
}
- return rdata->got_bytes > 0 && result != -ECONNABORTED ?
+ return result != -ECONNABORTED && rdata->got_bytes > 0 ?
rdata->got_bytes : result;
}
@@ -4665,7 +4665,7 @@ readpages_fill_pages(struct TCP_Server_Info *server,
rdata->got_bytes += result;
}
- return rdata->got_bytes > 0 && result != -ECONNABORTED ?
+ return result != -ECONNABORTED && rdata->got_bytes > 0 ?
rdata->got_bytes : result;
}
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index bd374feeccaa19..a5a097a6998371 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -428,6 +428,7 @@ smb3_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon,
oparms.disposition = FILE_CREATE;
oparms.fid = &fid;
oparms.reconnect = false;
+ oparms.mode = 0644;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL,
NULL, NULL);
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 4d3c586785a59d..2a19c7987c5bd8 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -1277,7 +1277,9 @@ int match_target_ip(struct TCP_Server_Info *server,
if (rc < 0)
return rc;
+ spin_lock(&server->srv_lock);
*result = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, (struct sockaddr *)&ss);
+ spin_unlock(&server->srv_lock);
cifs_dbg(FYI, "%s: ip addresses match: %u\n", __func__, *result);
return 0;
}
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 9e7d9f0baa18a1..c47b254f0d1e27 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -292,9 +292,10 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
continue;
}
kref_get(&iface->refcount);
+ break;
}
- if (!list_entry_is_head(iface, &ses->iface_list, iface_head)) {
+ if (list_entry_is_head(iface, &ses->iface_list, iface_head)) {
rc = 1;
iface = NULL;
cifs_dbg(FYI, "unable to find a suitable iface\n");
@@ -814,6 +815,7 @@ int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
return -EINVAL;
}
if (tilen) {
+ kfree_sensitive(ses->auth_key.response);
ses->auth_key.response = kmemdup(bcc_ptr + tioffset, tilen,
GFP_KERNEL);
if (!ses->auth_key.response) {
@@ -1427,6 +1429,7 @@ sess_auth_kerberos(struct sess_data *sess_data)
goto out_put_spnego_key;
}
+ kfree_sensitive(ses->auth_key.response);
ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
GFP_KERNEL);
if (!ses->auth_key.response) {
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
index 50480751e521ca..4cb364454e1309 100644
--- a/fs/cifs/smb1ops.c
+++ b/fs/cifs/smb1ops.c
@@ -562,17 +562,20 @@ static int cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) {
rc = SMBQueryInformation(xid, tcon, full_path, &fi, cifs_sb->local_nls,
cifs_remap(cifs_sb));
- if (!rc)
- move_cifs_info_to_smb2(&data->fi, &fi);
*adjustTZ = true;
}
- if (!rc && (le32_to_cpu(fi.Attributes) & ATTR_REPARSE)) {
+ if (!rc) {
int tmprc;
int oplock = 0;
struct cifs_fid fid;
struct cifs_open_parms oparms;
+ move_cifs_info_to_smb2(&data->fi, &fi);
+
+ if (!(le32_to_cpu(fi.Attributes) & ATTR_REPARSE))
+ return 0;
+
oparms.tcon = tcon;
oparms.cifs_sb = cifs_sb;
oparms.desired_access = FILE_READ_ATTRIBUTES;
@@ -716,17 +719,25 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path,
static int cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
void *buf)
{
- FILE_ALL_INFO *fi = buf;
+ struct cifs_open_info_data *data = buf;
+ FILE_ALL_INFO fi = {};
+ int rc;
if (!(oparms->tcon->ses->capabilities & CAP_NT_SMBS))
- return SMBLegacyOpen(xid, oparms->tcon, oparms->path,
- oparms->disposition,
- oparms->desired_access,
- oparms->create_options,
- &oparms->fid->netfid, oplock, fi,
- oparms->cifs_sb->local_nls,
- cifs_remap(oparms->cifs_sb));
- return CIFS_open(xid, oparms, oplock, fi);
+ rc = SMBLegacyOpen(xid, oparms->tcon, oparms->path,
+ oparms->disposition,
+ oparms->desired_access,
+ oparms->create_options,
+ &oparms->fid->netfid, oplock, &fi,
+ oparms->cifs_sb->local_nls,
+ cifs_remap(oparms->cifs_sb));
+ else
+ rc = CIFS_open(xid, oparms, oplock, &fi);
+
+ if (!rc && data)
+ move_cifs_info_to_smb2(&data->fi, &fi);
+
+ return rc;
}
static void
@@ -1050,7 +1061,7 @@ cifs_make_node(unsigned int xid, struct inode *inode,
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct inode *newinode = NULL;
int rc = -EPERM;
- FILE_ALL_INFO *buf = NULL;
+ struct cifs_open_info_data buf = {};
struct cifs_io_parms io_parms;
__u32 oplock = 0;
struct cifs_fid fid;
@@ -1082,14 +1093,14 @@ cifs_make_node(unsigned int xid, struct inode *inode,
cifs_sb->local_nls,
cifs_remap(cifs_sb));
if (rc)
- goto out;
+ return rc;
rc = cifs_get_inode_info_unix(&newinode, full_path,
inode->i_sb, xid);
if (rc == 0)
d_instantiate(dentry, newinode);
- goto out;
+ return rc;
}
/*
@@ -1097,19 +1108,13 @@ cifs_make_node(unsigned int xid, struct inode *inode,
* support block and char device (no socket & fifo)
*/
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
- goto out;
+ return rc;
if (!S_ISCHR(mode) && !S_ISBLK(mode))
- goto out;
+ return rc;
cifs_dbg(FYI, "sfu compat create special file\n");
- buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
- if (buf == NULL) {
- rc = -ENOMEM;
- goto out;
- }
-
oparms.tcon = tcon;
oparms.cifs_sb = cifs_sb;
oparms.desired_access = GENERIC_WRITE;
@@ -1124,21 +1129,21 @@ cifs_make_node(unsigned int xid, struct inode *inode,
oplock = REQ_OPLOCK;
else
oplock = 0;
- rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf);
+ rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf);
if (rc)
- goto out;
+ return rc;
/*
* BB Do not bother to decode buf since no local inode yet to put
* timestamps in, but we can reuse it safely.
*/
- pdev = (struct win_dev *)buf;
+ pdev = (struct win_dev *)&buf.fi;
io_parms.pid = current->tgid;
io_parms.tcon = tcon;
io_parms.offset = 0;
io_parms.length = sizeof(struct win_dev);
- iov[1].iov_base = buf;
+ iov[1].iov_base = &buf.fi;
iov[1].iov_len = sizeof(struct win_dev);
if (S_ISCHR(mode)) {
memcpy(pdev->type, "IntxCHR", 8);
@@ -1157,8 +1162,8 @@ cifs_make_node(unsigned int xid, struct inode *inode,
d_drop(dentry);
/* FIXME: add code here to set EAs */
-out:
- kfree(buf);
+
+ cifs_free_open_info(&buf);
return rc;
}
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index dc160de7a6de4d..e6bcd2baf446a9 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -530,7 +530,6 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
p = buf;
spin_lock(&ses->iface_lock);
- ses->iface_count = 0;
/*
* Go through iface_list and do kref_put to remove
* any unused ifaces. ifaces in use will be removed
@@ -540,6 +539,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
iface_head) {
iface->is_active = 0;
kref_put(&iface->refcount, release_iface);
+ ses->iface_count--;
}
spin_unlock(&ses->iface_lock);
@@ -618,6 +618,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
/* just get a ref so that it doesn't get picked/freed */
iface->is_active = 1;
kref_get(&iface->refcount);
+ ses->iface_count++;
spin_unlock(&ses->iface_lock);
goto next_iface;
} else if (ret < 0) {
@@ -4488,17 +4489,12 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst,
/* copy pages form the old */
for (j = 0; j < npages; j++) {
- char *dst, *src;
unsigned int offset, len;
rqst_page_get_length(new, j, &len, &offset);
- dst = kmap_local_page(new->rq_pages[j]) + offset;
- src = kmap_local_page(old->rq_pages[j]) + offset;
-
- memcpy(dst, src, len);
- kunmap(new->rq_pages[j]);
- kunmap(old->rq_pages[j]);
+ memcpy_page(new->rq_pages[j], offset,
+ old->rq_pages[j], offset, len);
}
}
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index a5695748a89b17..2c9ffa921e6f68 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -541,9 +541,10 @@ static void
assemble_neg_contexts(struct smb2_negotiate_req *req,
struct TCP_Server_Info *server, unsigned int *total_len)
{
- char *pneg_ctxt;
- char *hostname = NULL;
unsigned int ctxt_len, neg_context_count;
+ struct TCP_Server_Info *pserver;
+ char *pneg_ctxt;
+ char *hostname;
if (*total_len > 200) {
/* In case length corrupted don't want to overrun smb buffer */
@@ -574,8 +575,9 @@ assemble_neg_contexts(struct smb2_negotiate_req *req,
* secondary channels don't have the hostname field populated
* use the hostname field in the primary channel instead
*/
- hostname = CIFS_SERVER_IS_CHAN(server) ?
- server->primary_server->hostname : server->hostname;
+ pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
+ cifs_server_lock(pserver);
+ hostname = pserver->hostname;
if (hostname && (hostname[0] != 0)) {
ctxt_len = build_netname_ctxt((struct smb2_netname_neg_context *)pneg_ctxt,
hostname);
@@ -584,6 +586,7 @@ assemble_neg_contexts(struct smb2_negotiate_req *req,
neg_context_count = 3;
} else
neg_context_count = 2;
+ cifs_server_unlock(pserver);
build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt);
*total_len += sizeof(struct smb2_posix_neg_context);
@@ -1450,6 +1453,7 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
/* keep session key if binding */
if (!is_binding) {
+ kfree_sensitive(ses->auth_key.response);
ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
GFP_KERNEL);
if (!ses->auth_key.response) {
@@ -1479,8 +1483,11 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
out_put_spnego_key:
key_invalidate(spnego_key);
key_put(spnego_key);
- if (rc)
+ if (rc) {
kfree_sensitive(ses->auth_key.response);
+ ses->auth_key.response = NULL;
+ ses->auth_key.len = 0;
+ }
out:
sess_data->result = rc;
sess_data->func = NULL;
@@ -4156,12 +4163,15 @@ smb2_readv_callback(struct mid_q_entry *mid)
(struct smb2_hdr *)rdata->iov[0].iov_base;
struct cifs_credits credits = { .value = 0, .instance = 0 };
struct smb_rqst rqst = { .rq_iov = &rdata->iov[1],
- .rq_nvec = 1,
- .rq_pages = rdata->pages,
- .rq_offset = rdata->page_offset,
- .rq_npages = rdata->nr_pages,
- .rq_pagesz = rdata->pagesz,
- .rq_tailsz = rdata->tailsz };
+ .rq_nvec = 1, };
+
+ if (rdata->got_bytes) {
+ rqst.rq_pages = rdata->pages;
+ rqst.rq_offset = rdata->page_offset;
+ rqst.rq_npages = rdata->nr_pages;
+ rqst.rq_pagesz = rdata->pagesz;
+ rqst.rq_tailsz = rdata->tailsz;
+ }
WARN_ONCE(rdata->server != mid->server,
"rdata server %p != mid server %p",
diff --git a/fs/cifs/smbdirect.c b/fs/cifs/smbdirect.c
index 90789aaa6567e6..8c816b25ce7c6a 100644
--- a/fs/cifs/smbdirect.c
+++ b/fs/cifs/smbdirect.c
@@ -1405,6 +1405,7 @@ void smbd_destroy(struct TCP_Server_Info *server)
destroy_workqueue(info->workqueue);
log_rdma_event(INFO, "rdma session destroyed\n");
kfree(info);
+ server->smbd_conn = NULL;
}
/*
diff --git a/fs/coredump.c b/fs/coredump.c
index de78bde2991beb..a25ecec9ca7c45 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -838,6 +838,30 @@ static int __dump_skip(struct coredump_params *cprm, size_t nr)
}
}
+int dump_emit(struct coredump_params *cprm, const void *addr, int nr)
+{
+ if (cprm->to_skip) {
+ if (!__dump_skip(cprm, cprm->to_skip))
+ return 0;
+ cprm->to_skip = 0;
+ }
+ return __dump_emit(cprm, addr, nr);
+}
+EXPORT_SYMBOL(dump_emit);
+
+void dump_skip_to(struct coredump_params *cprm, unsigned long pos)
+{
+ cprm->to_skip = pos - cprm->pos;
+}
+EXPORT_SYMBOL(dump_skip_to);
+
+void dump_skip(struct coredump_params *cprm, size_t nr)
+{
+ cprm->to_skip += nr;
+}
+EXPORT_SYMBOL(dump_skip);
+
+#ifdef CONFIG_ELF_CORE
static int dump_emit_page(struct coredump_params *cprm, struct page *page)
{
struct bio_vec bvec = {
@@ -871,30 +895,6 @@ static int dump_emit_page(struct coredump_params *cprm, struct page *page)
return 1;
}
-int dump_emit(struct coredump_params *cprm, const void *addr, int nr)
-{
- if (cprm->to_skip) {
- if (!__dump_skip(cprm, cprm->to_skip))
- return 0;
- cprm->to_skip = 0;
- }
- return __dump_emit(cprm, addr, nr);
-}
-EXPORT_SYMBOL(dump_emit);
-
-void dump_skip_to(struct coredump_params *cprm, unsigned long pos)
-{
- cprm->to_skip = pos - cprm->pos;
-}
-EXPORT_SYMBOL(dump_skip_to);
-
-void dump_skip(struct coredump_params *cprm, size_t nr)
-{
- cprm->to_skip += nr;
-}
-EXPORT_SYMBOL(dump_skip);
-
-#ifdef CONFIG_ELF_CORE
int dump_user_range(struct coredump_params *cprm, unsigned long start,
unsigned long len)
{
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index 481788c24a68ba..626a615dafc2ff 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -577,26 +577,25 @@ static int erofs_fc_parse_param(struct fs_context *fc,
}
++ctx->devs->extra_devices;
break;
- case Opt_fsid:
#ifdef CONFIG_EROFS_FS_ONDEMAND
+ case Opt_fsid:
kfree(ctx->fsid);
ctx->fsid = kstrdup(param->string, GFP_KERNEL);
if (!ctx->fsid)
return -ENOMEM;
-#else
- errorfc(fc, "fsid option not supported");
-#endif
break;
case Opt_domain_id:
-#ifdef CONFIG_EROFS_FS_ONDEMAND
kfree(ctx->domain_id);
ctx->domain_id = kstrdup(param->string, GFP_KERNEL);
if (!ctx->domain_id)
return -ENOMEM;
+ break;
#else
- errorfc(fc, "domain_id option not supported");
-#endif
+ case Opt_fsid:
+ case Opt_domain_id:
+ errorfc(fc, "%s option not supported", erofs_fs_parameters[opt].name);
break;
+#endif
default:
return -ENOPARAM;
}
diff --git a/fs/erofs/zdata.c b/fs/erofs/zdata.c
index ccf7c55d477fe5..5200bb86e2643c 100644
--- a/fs/erofs/zdata.c
+++ b/fs/erofs/zdata.c
@@ -1032,12 +1032,12 @@ static int z_erofs_decompress_pcluster(struct z_erofs_decompress_backend *be,
if (!be->decompressed_pages)
be->decompressed_pages =
- kvcalloc(be->nr_pages, sizeof(struct page *),
- GFP_KERNEL | __GFP_NOFAIL);
+ kcalloc(be->nr_pages, sizeof(struct page *),
+ GFP_KERNEL | __GFP_NOFAIL);
if (!be->compressed_pages)
be->compressed_pages =
- kvcalloc(pclusterpages, sizeof(struct page *),
- GFP_KERNEL | __GFP_NOFAIL);
+ kcalloc(pclusterpages, sizeof(struct page *),
+ GFP_KERNEL | __GFP_NOFAIL);
z_erofs_parse_out_bvecs(be);
err2 = z_erofs_parse_in_bvecs(be, &overlapped);
@@ -1085,7 +1085,7 @@ out:
}
if (be->compressed_pages < be->onstack_pages ||
be->compressed_pages >= be->onstack_pages + Z_EROFS_ONSTACK_PAGES)
- kvfree(be->compressed_pages);
+ kfree(be->compressed_pages);
z_erofs_fill_other_copies(be, err);
for (i = 0; i < be->nr_pages; ++i) {
@@ -1104,7 +1104,7 @@ out:
}
if (be->decompressed_pages != be->onstack_pages)
- kvfree(be->decompressed_pages);
+ kfree(be->decompressed_pages);
pcl->length = 0;
pcl->partial = true;
diff --git a/fs/erofs/zmap.c b/fs/erofs/zmap.c
index 0150570c33aae1..98fb90b9af715c 100644
--- a/fs/erofs/zmap.c
+++ b/fs/erofs/zmap.c
@@ -793,12 +793,16 @@ static int z_erofs_iomap_begin_report(struct inode *inode, loff_t offset,
iomap->type = IOMAP_HOLE;
iomap->addr = IOMAP_NULL_ADDR;
/*
- * No strict rule how to describe extents for post EOF, yet
- * we need do like below. Otherwise, iomap itself will get
+ * No strict rule on how to describe extents for post EOF, yet
+ * we need to do like below. Otherwise, iomap itself will get
* into an endless loop on post EOF.
+ *
+ * Calculate the effective offset by subtracting extent start
+ * (map.m_la) from the requested offset, and add it to length.
+ * (NB: offset >= map.m_la always)
*/
if (iomap->offset >= inode->i_size)
- iomap->length = length + map.m_la - offset;
+ iomap->length = length + offset - map.m_la;
}
iomap->flags = 0;
return 0;
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 7decaaf27e82bd..a2f04a3808db57 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -81,6 +81,8 @@ ext4_xattr_block_cache_find(struct inode *, struct ext4_xattr_header *,
struct mb_cache_entry **);
static __le32 ext4_xattr_hash_entry(char *name, size_t name_len, __le32 *value,
size_t value_count);
+static __le32 ext4_xattr_hash_entry_signed(char *name, size_t name_len, __le32 *value,
+ size_t value_count);
static void ext4_xattr_rehash(struct ext4_xattr_header *);
static const struct xattr_handler * const ext4_xattr_handler_map[] = {
@@ -470,8 +472,22 @@ ext4_xattr_inode_verify_hashes(struct inode *ea_inode,
tmp_data = cpu_to_le32(hash);
e_hash = ext4_xattr_hash_entry(entry->e_name, entry->e_name_len,
&tmp_data, 1);
+ /* All good? */
+ if (e_hash == entry->e_hash)
+ return 0;
+
+ /*
+ * Not good. Maybe the entry hash was calculated
+ * using the buggy signed char version?
+ */
+ e_hash = ext4_xattr_hash_entry_signed(entry->e_name, entry->e_name_len,
+ &tmp_data, 1);
+ /* Still no match - bad */
if (e_hash != entry->e_hash)
return -EFSCORRUPTED;
+
+ /* Let people know about old hash */
+ pr_warn_once("ext4: filesystem with signed xattr name hash");
}
return 0;
}
@@ -3081,7 +3097,29 @@ static __le32 ext4_xattr_hash_entry(char *name, size_t name_len, __le32 *value,
while (name_len--) {
hash = (hash << NAME_HASH_SHIFT) ^
(hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
- *name++;
+ (unsigned char)*name++;
+ }
+ while (value_count--) {
+ hash = (hash << VALUE_HASH_SHIFT) ^
+ (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
+ le32_to_cpu(*value++);
+ }
+ return cpu_to_le32(hash);
+}
+
+/*
+ * ext4_xattr_hash_entry_signed()
+ *
+ * Compute the hash of an extended attribute incorrectly.
+ */
+static __le32 ext4_xattr_hash_entry_signed(char *name, size_t name_len, __le32 *value, size_t value_count)
+{
+ __u32 hash = 0;
+
+ while (name_len--) {
+ hash = (hash << NAME_HASH_SHIFT) ^
+ (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
+ (signed char)*name++;
}
while (value_count--) {
hash = (hash << VALUE_HASH_SHIFT) ^
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 6e43e19c7d1cac..97e816590cd95a 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -2183,7 +2183,7 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
sector_t last_block_in_file;
const unsigned blocksize = blks_to_bytes(inode, 1);
struct decompress_io_ctx *dic = NULL;
- struct extent_info ei = {0, };
+ struct extent_info ei = {};
bool from_dnode = true;
int i;
int ret = 0;
diff --git a/fs/f2fs/extent_cache.c b/fs/f2fs/extent_cache.c
index 1bd38a78ebba93..342af24b2f8cf7 100644
--- a/fs/f2fs/extent_cache.c
+++ b/fs/f2fs/extent_cache.c
@@ -546,7 +546,8 @@ static bool __lookup_extent_tree(struct inode *inode, pgoff_t pgofs,
struct extent_node *en;
bool ret = false;
- f2fs_bug_on(sbi, !et);
+ if (!et)
+ return false;
trace_f2fs_lookup_extent_tree_start(inode, pgofs, type);
@@ -881,12 +882,14 @@ static unsigned long long __calculate_block_age(unsigned long long new,
}
/* This returns a new age and allocated blocks in ei */
-static int __get_new_block_age(struct inode *inode, struct extent_info *ei)
+static int __get_new_block_age(struct inode *inode, struct extent_info *ei,
+ block_t blkaddr)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
loff_t f_size = i_size_read(inode);
unsigned long long cur_blocks =
atomic64_read(&sbi->allocated_data_blocks);
+ struct extent_info tei = *ei; /* only fofs and len are valid */
/*
* When I/O is not aligned to a PAGE_SIZE, update will happen to the last
@@ -894,20 +897,20 @@ static int __get_new_block_age(struct inode *inode, struct extent_info *ei)
* block here.
*/
if ((f_size >> PAGE_SHIFT) == ei->fofs && f_size & (PAGE_SIZE - 1) &&
- ei->blk == NEW_ADDR)
+ blkaddr == NEW_ADDR)
return -EINVAL;
- if (__lookup_extent_tree(inode, ei->fofs, ei, EX_BLOCK_AGE)) {
+ if (__lookup_extent_tree(inode, ei->fofs, &tei, EX_BLOCK_AGE)) {
unsigned long long cur_age;
- if (cur_blocks >= ei->last_blocks)
- cur_age = cur_blocks - ei->last_blocks;
+ if (cur_blocks >= tei.last_blocks)
+ cur_age = cur_blocks - tei.last_blocks;
else
/* allocated_data_blocks overflow */
- cur_age = ULLONG_MAX - ei->last_blocks + cur_blocks;
+ cur_age = ULLONG_MAX - tei.last_blocks + cur_blocks;
- if (ei->age)
- ei->age = __calculate_block_age(cur_age, ei->age);
+ if (tei.age)
+ ei->age = __calculate_block_age(cur_age, tei.age);
else
ei->age = cur_age;
ei->last_blocks = cur_blocks;
@@ -915,14 +918,14 @@ static int __get_new_block_age(struct inode *inode, struct extent_info *ei)
return 0;
}
- f2fs_bug_on(sbi, ei->blk == NULL_ADDR);
+ f2fs_bug_on(sbi, blkaddr == NULL_ADDR);
/* the data block was allocated for the first time */
- if (ei->blk == NEW_ADDR)
+ if (blkaddr == NEW_ADDR)
goto out;
- if (__is_valid_data_blkaddr(ei->blk) &&
- !f2fs_is_valid_blkaddr(sbi, ei->blk, DATA_GENERIC_ENHANCE)) {
+ if (__is_valid_data_blkaddr(blkaddr) &&
+ !f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC_ENHANCE)) {
f2fs_bug_on(sbi, 1);
return -EINVAL;
}
@@ -938,7 +941,7 @@ out:
static void __update_extent_cache(struct dnode_of_data *dn, enum extent_type type)
{
- struct extent_info ei;
+ struct extent_info ei = {};
if (!__may_extent_tree(dn->inode, type))
return;
@@ -953,8 +956,7 @@ static void __update_extent_cache(struct dnode_of_data *dn, enum extent_type typ
else
ei.blk = dn->data_blkaddr;
} else if (type == EX_BLOCK_AGE) {
- ei.blk = dn->data_blkaddr;
- if (__get_new_block_age(dn->inode, &ei))
+ if (__get_new_block_age(dn->inode, &ei, dn->data_blkaddr))
return;
}
__update_extent_tree_range(dn->inode, &ei, type);
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index a6c40127988600..ecbc8c135b494d 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -2559,7 +2559,7 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi,
struct f2fs_map_blocks map = { .m_next_extent = NULL,
.m_seg_type = NO_CHECK_TYPE,
.m_may_create = false };
- struct extent_info ei = {0, };
+ struct extent_info ei = {};
pgoff_t pg_start, pg_end, next_pgofs;
unsigned int blk_per_seg = sbi->blocks_per_seg;
unsigned int total = 0, sec_num;
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 25ddea478fc1a8..ae3c4e5474efa2 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -663,8 +663,7 @@ init_thread:
if (IS_ERR(fcc->f2fs_issue_flush)) {
int err = PTR_ERR(fcc->f2fs_issue_flush);
- kfree(fcc);
- SM_I(sbi)->fcc_info = NULL;
+ fcc->f2fs_issue_flush = NULL;
return err;
}
@@ -3161,7 +3160,7 @@ static int __get_segment_type_4(struct f2fs_io_info *fio)
static int __get_age_segment_type(struct inode *inode, pgoff_t pgofs)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
- struct extent_info ei;
+ struct extent_info ei = {};
if (f2fs_lookup_age_extent_cache(inode, pgofs, &ei)) {
if (!ei.age)
@@ -5138,11 +5137,9 @@ int f2fs_build_segment_manager(struct f2fs_sb_info *sbi)
init_f2fs_rwsem(&sm_info->curseg_lock);
- if (!f2fs_readonly(sbi->sb)) {
- err = f2fs_create_flush_cmd_control(sbi);
- if (err)
- return err;
- }
+ err = f2fs_create_flush_cmd_control(sbi);
+ if (err)
+ return err;
err = create_discard_cmd_control(sbi);
if (err)
diff --git a/fs/freevxfs/Kconfig b/fs/freevxfs/Kconfig
index c05c71d572911a..0e2fc08f7de492 100644
--- a/fs/freevxfs/Kconfig
+++ b/fs/freevxfs/Kconfig
@@ -8,7 +8,7 @@ config VXFS_FS
of SCO UnixWare (and possibly others) and optionally available
for Sunsoft Solaris, HP-UX and many other operating systems. However
these particular OS implementations of vxfs may differ in on-disk
- data endianess and/or superblock offset. The vxfs module has been
+ data endianness and/or superblock offset. The vxfs module has been
tested with SCO UnixWare and HP-UX B.10.20 (pa-risc 1.1 arch.)
Currently only readonly access is supported and VxFX versions
2, 3 and 4. Tests were performed with HP-UX VxFS version 3.
diff --git a/fs/fscache/volume.c b/fs/fscache/volume.c
index ab8ceddf9efad9..cdf991bdd9def4 100644
--- a/fs/fscache/volume.c
+++ b/fs/fscache/volume.c
@@ -141,13 +141,14 @@ static bool fscache_is_acquire_pending(struct fscache_volume *volume)
static void fscache_wait_on_volume_collision(struct fscache_volume *candidate,
unsigned int collidee_debug_id)
{
- wait_var_event_timeout(&candidate->flags,
- !fscache_is_acquire_pending(candidate), 20 * HZ);
+ wait_on_bit_timeout(&candidate->flags, FSCACHE_VOLUME_ACQUIRE_PENDING,
+ TASK_UNINTERRUPTIBLE, 20 * HZ);
if (fscache_is_acquire_pending(candidate)) {
pr_notice("Potential volume collision new=%08x old=%08x",
candidate->debug_id, collidee_debug_id);
fscache_stat(&fscache_n_volumes_collision);
- wait_var_event(&candidate->flags, !fscache_is_acquire_pending(candidate));
+ wait_on_bit(&candidate->flags, FSCACHE_VOLUME_ACQUIRE_PENDING,
+ TASK_UNINTERRUPTIBLE);
}
}
@@ -279,8 +280,7 @@ static void fscache_create_volume_work(struct work_struct *work)
fscache_end_cache_access(volume->cache,
fscache_access_acquire_volume_end);
- clear_bit_unlock(FSCACHE_VOLUME_CREATING, &volume->flags);
- wake_up_bit(&volume->flags, FSCACHE_VOLUME_CREATING);
+ clear_and_wake_up_bit(FSCACHE_VOLUME_CREATING, &volume->flags);
fscache_put_volume(volume, fscache_volume_put_create_work);
}
@@ -347,8 +347,8 @@ static void fscache_wake_pending_volume(struct fscache_volume *volume,
hlist_bl_for_each_entry(cursor, p, h, hash_link) {
if (fscache_volume_same(cursor, volume)) {
fscache_see_volume(cursor, fscache_volume_see_hash_wake);
- clear_bit(FSCACHE_VOLUME_ACQUIRE_PENDING, &cursor->flags);
- wake_up_bit(&cursor->flags, FSCACHE_VOLUME_ACQUIRE_PENDING);
+ clear_and_wake_up_bit(FSCACHE_VOLUME_ACQUIRE_PENDING,
+ &cursor->flags);
return;
}
}
diff --git a/fs/fuse/acl.c b/fs/fuse/acl.c
index a4850aee2639ff..ad670369955f6d 100644
--- a/fs/fuse/acl.c
+++ b/fs/fuse/acl.c
@@ -11,9 +11,10 @@
#include <linux/posix_acl.h>
#include <linux/posix_acl_xattr.h>
-struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu)
+static struct posix_acl *__fuse_get_acl(struct fuse_conn *fc,
+ struct user_namespace *mnt_userns,
+ struct inode *inode, int type, bool rcu)
{
- struct fuse_conn *fc = get_fuse_conn(inode);
int size;
const char *name;
void *value = NULL;
@@ -25,7 +26,7 @@ struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu)
if (fuse_is_bad(inode))
return ERR_PTR(-EIO);
- if (!fc->posix_acl || fc->no_getxattr)
+ if (fc->no_getxattr)
return NULL;
if (type == ACL_TYPE_ACCESS)
@@ -53,6 +54,46 @@ struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu)
return acl;
}
+static inline bool fuse_no_acl(const struct fuse_conn *fc,
+ const struct inode *inode)
+{
+ /*
+ * Refuse interacting with POSIX ACLs for daemons that
+ * don't support FUSE_POSIX_ACL and are not mounted on
+ * the host to retain backwards compatibility.
+ */
+ return !fc->posix_acl && (i_user_ns(inode) != &init_user_ns);
+}
+
+struct posix_acl *fuse_get_acl(struct user_namespace *mnt_userns,
+ struct dentry *dentry, int type)
+{
+ struct inode *inode = d_inode(dentry);
+ struct fuse_conn *fc = get_fuse_conn(inode);
+
+ if (fuse_no_acl(fc, inode))
+ return ERR_PTR(-EOPNOTSUPP);
+
+ return __fuse_get_acl(fc, mnt_userns, inode, type, false);
+}
+
+struct posix_acl *fuse_get_inode_acl(struct inode *inode, int type, bool rcu)
+{
+ struct fuse_conn *fc = get_fuse_conn(inode);
+
+ /*
+ * FUSE daemons before FUSE_POSIX_ACL was introduced could get and set
+ * POSIX ACLs without them being used for permission checking by the
+ * vfs. Retain that behavior for backwards compatibility as there are
+ * filesystems that do all permission checking for acls in the daemon
+ * and not in the kernel.
+ */
+ if (!fc->posix_acl)
+ return NULL;
+
+ return __fuse_get_acl(fc, &init_user_ns, inode, type, rcu);
+}
+
int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
struct posix_acl *acl, int type)
{
@@ -64,7 +105,7 @@ int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
if (fuse_is_bad(inode))
return -EIO;
- if (!fc->posix_acl || fc->no_setxattr)
+ if (fc->no_setxattr || fuse_no_acl(fc, inode))
return -EOPNOTSUPP;
if (type == ACL_TYPE_ACCESS)
@@ -99,7 +140,13 @@ int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
return ret;
}
- if (!vfsgid_in_group_p(i_gid_into_vfsgid(&init_user_ns, inode)) &&
+ /*
+ * Fuse daemons without FUSE_POSIX_ACL never changed the passed
+ * through POSIX ACLs. Such daemons don't expect setgid bits to
+ * be stripped.
+ */
+ if (fc->posix_acl &&
+ !vfsgid_in_group_p(i_gid_into_vfsgid(&init_user_ns, inode)) &&
!capable_wrt_inode_uidgid(&init_user_ns, inode, CAP_FSETID))
extra_flags |= FUSE_SETXATTR_ACL_KILL_SGID;
@@ -108,8 +155,15 @@ int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
} else {
ret = fuse_removexattr(inode, name);
}
- forget_all_cached_acls(inode);
- fuse_invalidate_attr(inode);
+
+ if (fc->posix_acl) {
+ /*
+ * Fuse daemons without FUSE_POSIX_ACL never cached POSIX ACLs
+ * and didn't invalidate attributes. Retain that behavior.
+ */
+ forget_all_cached_acls(inode);
+ fuse_invalidate_attr(inode);
+ }
return ret;
}
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index cd1a071b625abc..2725fb54328e58 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1942,7 +1942,8 @@ static const struct inode_operations fuse_dir_inode_operations = {
.permission = fuse_permission,
.getattr = fuse_getattr,
.listxattr = fuse_listxattr,
- .get_inode_acl = fuse_get_acl,
+ .get_inode_acl = fuse_get_inode_acl,
+ .get_acl = fuse_get_acl,
.set_acl = fuse_set_acl,
.fileattr_get = fuse_fileattr_get,
.fileattr_set = fuse_fileattr_set,
@@ -1964,7 +1965,8 @@ static const struct inode_operations fuse_common_inode_operations = {
.permission = fuse_permission,
.getattr = fuse_getattr,
.listxattr = fuse_listxattr,
- .get_inode_acl = fuse_get_acl,
+ .get_inode_acl = fuse_get_inode_acl,
+ .get_acl = fuse_get_acl,
.set_acl = fuse_set_acl,
.fileattr_get = fuse_fileattr_get,
.fileattr_set = fuse_fileattr_set,
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index c673faefdcb9c2..46797a171a84f4 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1264,11 +1264,11 @@ ssize_t fuse_getxattr(struct inode *inode, const char *name, void *value,
ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size);
int fuse_removexattr(struct inode *inode, const char *name);
extern const struct xattr_handler *fuse_xattr_handlers[];
-extern const struct xattr_handler *fuse_acl_xattr_handlers[];
-extern const struct xattr_handler *fuse_no_acl_xattr_handlers[];
struct posix_acl;
-struct posix_acl *fuse_get_acl(struct inode *inode, int type, bool rcu);
+struct posix_acl *fuse_get_inode_acl(struct inode *inode, int type, bool rcu);
+struct posix_acl *fuse_get_acl(struct user_namespace *mnt_userns,
+ struct dentry *dentry, int type);
int fuse_set_acl(struct user_namespace *mnt_userns, struct dentry *dentry,
struct posix_acl *acl, int type);
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 6b3beda16c1ba0..de9b9ec5ce810a 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -311,7 +311,8 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
fuse_dax_dontcache(inode, attr->flags);
}
-static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
+static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr,
+ struct fuse_conn *fc)
{
inode->i_mode = attr->mode & S_IFMT;
inode->i_size = attr->size;
@@ -333,6 +334,12 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr)
new_decode_dev(attr->rdev));
} else
BUG();
+ /*
+ * Ensure that we don't cache acls for daemons without FUSE_POSIX_ACL
+ * so they see the exact same behavior as before.
+ */
+ if (!fc->posix_acl)
+ inode->i_acl = inode->i_default_acl = ACL_DONT_CACHE;
}
static int fuse_inode_eq(struct inode *inode, void *_nodeidp)
@@ -372,7 +379,7 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid,
if (!inode)
return NULL;
- fuse_init_inode(inode, attr);
+ fuse_init_inode(inode, attr, fc);
get_fuse_inode(inode)->nodeid = nodeid;
inode->i_flags |= S_AUTOMOUNT;
goto done;
@@ -388,7 +395,7 @@ retry:
if (!fc->writeback_cache || !S_ISREG(attr->mode))
inode->i_flags |= S_NOCMTIME;
inode->i_generation = generation;
- fuse_init_inode(inode, attr);
+ fuse_init_inode(inode, attr, fc);
unlock_new_inode(inode);
} else if (fuse_stale_inode(inode, generation, attr)) {
/* nodeid was reused, any I/O on the old inode should fail */
@@ -1174,7 +1181,6 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
if ((flags & FUSE_POSIX_ACL)) {
fc->default_permissions = 1;
fc->posix_acl = 1;
- fm->sb->s_xattr = fuse_acl_xattr_handlers;
}
if (flags & FUSE_CACHE_SYMLINKS)
fc->cache_symlinks = 1;
@@ -1420,13 +1426,6 @@ static void fuse_sb_defaults(struct super_block *sb)
if (sb->s_user_ns != &init_user_ns)
sb->s_iflags |= SB_I_UNTRUSTED_MOUNTER;
sb->s_flags &= ~(SB_NOSEC | SB_I_VERSION);
-
- /*
- * If we are not in the initial user namespace posix
- * acls must be translated.
- */
- if (sb->s_user_ns != &init_user_ns)
- sb->s_xattr = fuse_no_acl_xattr_handlers;
}
static int fuse_fill_super_submount(struct super_block *sb,
diff --git a/fs/fuse/xattr.c b/fs/fuse/xattr.c
index 0d3e7177fce0a7..9fe571ab569ede 100644
--- a/fs/fuse/xattr.c
+++ b/fs/fuse/xattr.c
@@ -203,27 +203,6 @@ static int fuse_xattr_set(const struct xattr_handler *handler,
return fuse_setxattr(inode, name, value, size, flags, 0);
}
-static bool no_xattr_list(struct dentry *dentry)
-{
- return false;
-}
-
-static int no_xattr_get(const struct xattr_handler *handler,
- struct dentry *dentry, struct inode *inode,
- const char *name, void *value, size_t size)
-{
- return -EOPNOTSUPP;
-}
-
-static int no_xattr_set(const struct xattr_handler *handler,
- struct user_namespace *mnt_userns,
- struct dentry *dentry, struct inode *nodee,
- const char *name, const void *value,
- size_t size, int flags)
-{
- return -EOPNOTSUPP;
-}
-
static const struct xattr_handler fuse_xattr_handler = {
.prefix = "",
.get = fuse_xattr_get,
@@ -234,33 +213,3 @@ const struct xattr_handler *fuse_xattr_handlers[] = {
&fuse_xattr_handler,
NULL
};
-
-const struct xattr_handler *fuse_acl_xattr_handlers[] = {
- &posix_acl_access_xattr_handler,
- &posix_acl_default_xattr_handler,
- &fuse_xattr_handler,
- NULL
-};
-
-static const struct xattr_handler fuse_no_acl_access_xattr_handler = {
- .name = XATTR_NAME_POSIX_ACL_ACCESS,
- .flags = ACL_TYPE_ACCESS,
- .list = no_xattr_list,
- .get = no_xattr_get,
- .set = no_xattr_set,
-};
-
-static const struct xattr_handler fuse_no_acl_default_xattr_handler = {
- .name = XATTR_NAME_POSIX_ACL_DEFAULT,
- .flags = ACL_TYPE_ACCESS,
- .list = no_xattr_list,
- .get = no_xattr_get,
- .set = no_xattr_set,
-};
-
-const struct xattr_handler *fuse_no_acl_xattr_handlers[] = {
- &fuse_no_acl_access_xattr_handler,
- &fuse_no_acl_default_xattr_handler,
- &fuse_xattr_handler,
- NULL
-};
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index 723639376ae2a1..61323deb80bc7b 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -80,6 +80,15 @@ void gfs2_remove_from_ail(struct gfs2_bufdata *bd)
brelse(bd->bd_bh);
}
+static int __gfs2_writepage(struct page *page, struct writeback_control *wbc,
+ void *data)
+{
+ struct address_space *mapping = data;
+ int ret = mapping->a_ops->writepage(page, wbc);
+ mapping_set_error(mapping, ret);
+ return ret;
+}
+
/**
* gfs2_ail1_start_one - Start I/O on a transaction
* @sdp: The superblock
@@ -131,7 +140,7 @@ __acquires(&sdp->sd_ail_lock)
if (!mapping)
continue;
spin_unlock(&sdp->sd_ail_lock);
- ret = filemap_fdatawrite_wbc(mapping, wbc);
+ ret = write_cache_pages(mapping, wbc, __gfs2_writepage, mapping);
if (need_resched()) {
blk_finish_plug(plug);
cond_resched();
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index 9c329a365e7502..3a155c1d810ef8 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -458,15 +458,16 @@ int hfs_write_inode(struct inode *inode, struct writeback_control *wbc)
/* panic? */
return -EIO;
+ res = -EIO;
if (HFS_I(main_inode)->cat_key.CName.len > HFS_NAMELEN)
- return -EIO;
+ goto out;
fd.search_key->cat = HFS_I(main_inode)->cat_key;
if (hfs_brec_find(&fd))
- /* panic? */
goto out;
if (S_ISDIR(main_inode->i_mode)) {
- WARN_ON(fd.entrylength < sizeof(struct hfs_cat_dir));
+ if (fd.entrylength < sizeof(struct hfs_cat_dir))
+ goto out;
hfs_bnode_read(fd.bnode, &rec, fd.entryoffset,
sizeof(struct hfs_cat_dir));
if (rec.type != HFS_CDR_DIR ||
@@ -479,6 +480,8 @@ int hfs_write_inode(struct inode *inode, struct writeback_control *wbc)
hfs_bnode_write(fd.bnode, &rec, fd.entryoffset,
sizeof(struct hfs_cat_dir));
} else if (HFS_IS_RSRC(inode)) {
+ if (fd.entrylength < sizeof(struct hfs_cat_file))
+ goto out;
hfs_bnode_read(fd.bnode, &rec, fd.entryoffset,
sizeof(struct hfs_cat_file));
hfs_inode_write_fork(inode, rec.file.RExtRec,
@@ -486,7 +489,8 @@ int hfs_write_inode(struct inode *inode, struct writeback_control *wbc)
hfs_bnode_write(fd.bnode, &rec, fd.entryoffset,
sizeof(struct hfs_cat_file));
} else {
- WARN_ON(fd.entrylength < sizeof(struct hfs_cat_file));
+ if (fd.entrylength < sizeof(struct hfs_cat_file))
+ goto out;
hfs_bnode_read(fd.bnode, &rec, fd.entryoffset,
sizeof(struct hfs_cat_file));
if (rec.type != HFS_CDR_FIL ||
@@ -503,9 +507,10 @@ int hfs_write_inode(struct inode *inode, struct writeback_control *wbc)
hfs_bnode_write(fd.bnode, &rec, fd.entryoffset,
sizeof(struct hfs_cat_file));
}
+ res = 0;
out:
hfs_find_exit(&fd);
- return 0;
+ return res;
}
static struct dentry *hfs_file_lookup(struct inode *dir, struct dentry *dentry,
diff --git a/fs/ksmbd/auth.c b/fs/ksmbd/auth.c
index 2a39ffb8423b75..6e61b5bc7d86ed 100644
--- a/fs/ksmbd/auth.c
+++ b/fs/ksmbd/auth.c
@@ -322,7 +322,8 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
dn_off = le32_to_cpu(authblob->DomainName.BufferOffset);
dn_len = le16_to_cpu(authblob->DomainName.Length);
- if (blob_len < (u64)dn_off + dn_len || blob_len < (u64)nt_off + nt_len)
+ if (blob_len < (u64)dn_off + dn_len || blob_len < (u64)nt_off + nt_len ||
+ nt_len < CIFS_ENCPWD_SIZE)
return -EINVAL;
/* TODO : use domain name that imported from configuration file */
diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c
index 12be8386446a39..56be077e5d8ac5 100644
--- a/fs/ksmbd/connection.c
+++ b/fs/ksmbd/connection.c
@@ -280,7 +280,7 @@ int ksmbd_conn_handler_loop(void *p)
{
struct ksmbd_conn *conn = (struct ksmbd_conn *)p;
struct ksmbd_transport *t = conn->transport;
- unsigned int pdu_size;
+ unsigned int pdu_size, max_allowed_pdu_size;
char hdr_buf[4] = {0,};
int size;
@@ -305,20 +305,36 @@ int ksmbd_conn_handler_loop(void *p)
pdu_size = get_rfc1002_len(hdr_buf);
ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size);
+ if (conn->status == KSMBD_SESS_GOOD)
+ max_allowed_pdu_size =
+ SMB3_MAX_MSGSIZE + conn->vals->max_write_size;
+ else
+ max_allowed_pdu_size = SMB3_MAX_MSGSIZE;
+
+ if (pdu_size > max_allowed_pdu_size) {
+ pr_err_ratelimited("PDU length(%u) excceed maximum allowed pdu size(%u) on connection(%d)\n",
+ pdu_size, max_allowed_pdu_size,
+ conn->status);
+ break;
+ }
+
/*
* Check if pdu size is valid (min : smb header size,
* max : 0x00FFFFFF).
*/
if (pdu_size < __SMB2_HEADER_STRUCTURE_SIZE ||
pdu_size > MAX_STREAM_PROT_LEN) {
- continue;
+ break;
}
/* 4 for rfc1002 length field */
size = pdu_size + 4;
- conn->request_buf = kvmalloc(size, GFP_KERNEL);
+ conn->request_buf = kvmalloc(size,
+ GFP_KERNEL |
+ __GFP_NOWARN |
+ __GFP_NORETRY);
if (!conn->request_buf)
- continue;
+ break;
memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf));
if (!ksmbd_smb_request(conn))
diff --git a/fs/ksmbd/ksmbd_netlink.h b/fs/ksmbd/ksmbd_netlink.h
index b6bd8311e6b4d2..fb8b2d566efb66 100644
--- a/fs/ksmbd/ksmbd_netlink.h
+++ b/fs/ksmbd/ksmbd_netlink.h
@@ -106,7 +106,8 @@ struct ksmbd_startup_request {
__u32 sub_auth[3]; /* Subauth value for Security ID */
__u32 smb2_max_credits; /* MAX credits */
__u32 smbd_max_io_size; /* smbd read write size */
- __u32 reserved[127]; /* Reserved room */
+ __u32 max_connections; /* Number of maximum simultaneous connections */
+ __u32 reserved[126]; /* Reserved room */
__u32 ifc_list_sz; /* interfaces list size */
__s8 ____payload[];
};
diff --git a/fs/ksmbd/ndr.c b/fs/ksmbd/ndr.c
index 0ae8d08d85a87d..4d9e0b54e3dbfa 100644
--- a/fs/ksmbd/ndr.c
+++ b/fs/ksmbd/ndr.c
@@ -242,7 +242,7 @@ int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da)
return ret;
if (da->version != 3 && da->version != 4) {
- pr_err("v%d version is not supported\n", da->version);
+ ksmbd_debug(VFS, "v%d version is not supported\n", da->version);
return -EINVAL;
}
@@ -251,7 +251,7 @@ int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da)
return ret;
if (da->version != version2) {
- pr_err("ndr version mismatched(version: %d, version2: %d)\n",
+ ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n",
da->version, version2);
return -EINVAL;
}
@@ -457,7 +457,7 @@ int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)
if (ret)
return ret;
if (acl->version != 4) {
- pr_err("v%d version is not supported\n", acl->version);
+ ksmbd_debug(VFS, "v%d version is not supported\n", acl->version);
return -EINVAL;
}
@@ -465,7 +465,7 @@ int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)
if (ret)
return ret;
if (acl->version != version2) {
- pr_err("ndr version mismatched(version: %d, version2: %d)\n",
+ ksmbd_debug(VFS, "ndr version mismatched(version: %d, version2: %d)\n",
acl->version, version2);
return -EINVAL;
}
diff --git a/fs/ksmbd/server.h b/fs/ksmbd/server.h
index ac9d932f8c8aa1..db727818176038 100644
--- a/fs/ksmbd/server.h
+++ b/fs/ksmbd/server.h
@@ -41,6 +41,7 @@ struct ksmbd_server_config {
unsigned int share_fake_fscaps;
struct smb_sid domain_sid;
unsigned int auth_mechs;
+ unsigned int max_connections;
char *conf[SERVER_CONF_WORK_GROUP + 1];
};
diff --git a/fs/ksmbd/smb2pdu.c b/fs/ksmbd/smb2pdu.c
index 14d7f3599c6397..d681f91947d927 100644
--- a/fs/ksmbd/smb2pdu.c
+++ b/fs/ksmbd/smb2pdu.c
@@ -1928,13 +1928,13 @@ int smb2_tree_connect(struct ksmbd_work *work)
if (conn->posix_ext_supported)
status.tree_conn->posix_extensions = true;
-out_err1:
rsp->StructureSize = cpu_to_le16(16);
+ inc_rfc1001_len(work->response_buf, 16);
+out_err1:
rsp->Capabilities = 0;
rsp->Reserved = 0;
/* default manual caching */
rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING;
- inc_rfc1001_len(work->response_buf, 16);
if (!IS_ERR(treename))
kfree(treename);
@@ -1967,6 +1967,9 @@ out_err1:
rsp->hdr.Status = STATUS_ACCESS_DENIED;
}
+ if (status.ret != KSMBD_TREE_CONN_STATUS_OK)
+ smb2_set_err_rsp(work);
+
return rc;
}
@@ -8660,6 +8663,7 @@ int smb3_decrypt_req(struct ksmbd_work *work)
bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work)
{
struct ksmbd_conn *conn = work->conn;
+ struct ksmbd_session *sess = work->sess;
struct smb2_hdr *rsp = smb2_get_msg(work->response_buf);
if (conn->dialect < SMB30_PROT_ID)
@@ -8669,6 +8673,7 @@ bool smb3_11_final_sess_setup_resp(struct ksmbd_work *work)
rsp = ksmbd_resp_buf_next(work);
if (le16_to_cpu(rsp->Command) == SMB2_SESSION_SETUP_HE &&
+ sess->user && !user_guest(sess->user) &&
rsp->Status == STATUS_SUCCESS)
return true;
return false;
diff --git a/fs/ksmbd/smb2pdu.h b/fs/ksmbd/smb2pdu.h
index aa5dbe54f5a185..0c8a770fe3189b 100644
--- a/fs/ksmbd/smb2pdu.h
+++ b/fs/ksmbd/smb2pdu.h
@@ -24,8 +24,9 @@
#define SMB21_DEFAULT_IOSIZE (1024 * 1024)
#define SMB3_DEFAULT_TRANS_SIZE (1024 * 1024)
-#define SMB3_MIN_IOSIZE (64 * 1024)
-#define SMB3_MAX_IOSIZE (8 * 1024 * 1024)
+#define SMB3_MIN_IOSIZE (64 * 1024)
+#define SMB3_MAX_IOSIZE (8 * 1024 * 1024)
+#define SMB3_MAX_MSGSIZE (4 * 4096)
/*
* Definitions for SMB2 Protocol Data Units (network frames)
diff --git a/fs/ksmbd/transport_ipc.c b/fs/ksmbd/transport_ipc.c
index c9aca21637d5b6..40c721f9227e4a 100644
--- a/fs/ksmbd/transport_ipc.c
+++ b/fs/ksmbd/transport_ipc.c
@@ -308,6 +308,9 @@ static int ipc_server_config_on_startup(struct ksmbd_startup_request *req)
if (req->smbd_max_io_size)
init_smbd_max_io_size(req->smbd_max_io_size);
+ if (req->max_connections)
+ server_conf.max_connections = req->max_connections;
+
ret = ksmbd_set_netbios_name(req->netbios_name);
ret |= ksmbd_set_server_string(req->server_string);
ret |= ksmbd_set_work_group(req->work_group);
diff --git a/fs/ksmbd/transport_tcp.c b/fs/ksmbd/transport_tcp.c
index 63d55f543bd2e4..603893fd87f57d 100644
--- a/fs/ksmbd/transport_tcp.c
+++ b/fs/ksmbd/transport_tcp.c
@@ -15,6 +15,8 @@
#define IFACE_STATE_DOWN BIT(0)
#define IFACE_STATE_CONFIGURED BIT(1)
+static atomic_t active_num_conn;
+
struct interface {
struct task_struct *ksmbd_kthread;
struct socket *ksmbd_socket;
@@ -185,8 +187,10 @@ static int ksmbd_tcp_new_connection(struct socket *client_sk)
struct tcp_transport *t;
t = alloc_transport(client_sk);
- if (!t)
+ if (!t) {
+ sock_release(client_sk);
return -ENOMEM;
+ }
csin = KSMBD_TCP_PEER_SOCKADDR(KSMBD_TRANS(t)->conn);
if (kernel_getpeername(client_sk, csin) < 0) {
@@ -239,6 +243,15 @@ static int ksmbd_kthread_fn(void *p)
continue;
}
+ if (server_conf.max_connections &&
+ atomic_inc_return(&active_num_conn) >= server_conf.max_connections) {
+ pr_info_ratelimited("Limit the maximum number of connections(%u)\n",
+ atomic_read(&active_num_conn));
+ atomic_dec(&active_num_conn);
+ sock_release(client_sk);
+ continue;
+ }
+
ksmbd_debug(CONN, "connect success: accepted new connection\n");
client_sk->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT;
client_sk->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT;
@@ -295,6 +308,7 @@ static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig,
struct msghdr ksmbd_msg;
struct kvec *iov;
struct ksmbd_conn *conn = KSMBD_TRANS(t)->conn;
+ int max_retry = 2;
iov = get_conn_iovec(t, nr_segs);
if (!iov)
@@ -321,9 +335,11 @@ static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig,
} else if (conn->status == KSMBD_SESS_NEED_RECONNECT) {
total_read = -EAGAIN;
break;
- } else if (length == -ERESTARTSYS || length == -EAGAIN) {
+ } else if ((length == -ERESTARTSYS || length == -EAGAIN) &&
+ max_retry) {
usleep_range(1000, 2000);
length = 0;
+ max_retry--;
continue;
} else if (length <= 0) {
total_read = -EAGAIN;
@@ -365,6 +381,8 @@ static int ksmbd_tcp_writev(struct ksmbd_transport *t, struct kvec *iov,
static void ksmbd_tcp_disconnect(struct ksmbd_transport *t)
{
free_transport(TCP_TRANS(t));
+ if (server_conf.max_connections)
+ atomic_dec(&active_num_conn);
}
static void tcp_destroy_socket(struct socket *ksmbd_socket)
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index ea1ceffa1d3aa7..f7e4a88d5d9292 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -2957,12 +2957,14 @@ static u64 nfs_access_login_time(const struct task_struct *task,
const struct cred *cred)
{
const struct task_struct *parent;
+ const struct cred *pcred;
u64 ret;
rcu_read_lock();
for (;;) {
parent = rcu_dereference(task->real_parent);
- if (parent == task || cred_fscmp(parent->cred, cred) != 0)
+ pcred = rcu_dereference(parent->cred);
+ if (parent == task || cred_fscmp(pcred, cred) != 0)
break;
task = parent;
}
@@ -3023,6 +3025,7 @@ static int nfs_access_get_cached_rcu(struct inode *inode, const struct cred *cre
* but do it without locking.
*/
struct nfs_inode *nfsi = NFS_I(inode);
+ u64 login_time = nfs_access_login_time(current, cred);
struct nfs_access_entry *cache;
int err = -ECHILD;
struct list_head *lh;
@@ -3037,6 +3040,8 @@ static int nfs_access_get_cached_rcu(struct inode *inode, const struct cred *cre
cache = NULL;
if (cache == NULL)
goto out;
+ if ((s64)(login_time - cache->timestamp) > 0)
+ goto out;
if (nfs_check_cache_invalid(inode, NFS_INO_INVALID_ACCESS))
goto out;
*mask = cache->mask;
diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c
index ad34a33b0737c7..4974cd18ca468e 100644
--- a/fs/nfs/filelayout/filelayout.c
+++ b/fs/nfs/filelayout/filelayout.c
@@ -783,6 +783,12 @@ filelayout_alloc_lseg(struct pnfs_layout_hdr *layoutid,
return &fl->generic_hdr;
}
+static bool
+filelayout_lseg_is_striped(const struct nfs4_filelayout_segment *flseg)
+{
+ return flseg->num_fh > 1;
+}
+
/*
* filelayout_pg_test(). Called by nfs_can_coalesce_requests()
*
@@ -803,6 +809,8 @@ filelayout_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev,
size = pnfs_generic_pg_test(pgio, prev, req);
if (!size)
return 0;
+ else if (!filelayout_lseg_is_striped(FILELAYOUT_LSEG(pgio->pg_lseg)))
+ return size;
/* see if req and prev are in the same stripe */
if (prev) {
diff --git a/fs/nfsd/filecache.c b/fs/nfsd/filecache.c
index 45b2c9e3f63600..c0950edb26b0d3 100644
--- a/fs/nfsd/filecache.c
+++ b/fs/nfsd/filecache.c
@@ -662,6 +662,39 @@ static struct shrinker nfsd_file_shrinker = {
};
/**
+ * nfsd_file_cond_queue - conditionally unhash and queue a nfsd_file
+ * @nf: nfsd_file to attempt to queue
+ * @dispose: private list to queue successfully-put objects
+ *
+ * Unhash an nfsd_file, try to get a reference to it, and then put that
+ * reference. If it's the last reference, queue it to the dispose list.
+ */
+static void
+nfsd_file_cond_queue(struct nfsd_file *nf, struct list_head *dispose)
+ __must_hold(RCU)
+{
+ int decrement = 1;
+
+ /* If we raced with someone else unhashing, ignore it */
+ if (!nfsd_file_unhash(nf))
+ return;
+
+ /* If we can't get a reference, ignore it */
+ if (!nfsd_file_get(nf))
+ return;
+
+ /* Extra decrement if we remove from the LRU */
+ if (nfsd_file_lru_remove(nf))
+ ++decrement;
+
+ /* If refcount goes to 0, then put on the dispose list */
+ if (refcount_sub_and_test(decrement, &nf->nf_ref)) {
+ list_add(&nf->nf_lru, dispose);
+ trace_nfsd_file_closing(nf);
+ }
+}
+
+/**
* nfsd_file_queue_for_close: try to close out any open nfsd_files for an inode
* @inode: inode on which to close out nfsd_files
* @dispose: list on which to gather nfsd_files to close out
@@ -688,30 +721,11 @@ nfsd_file_queue_for_close(struct inode *inode, struct list_head *dispose)
rcu_read_lock();
do {
- int decrement = 1;
-
nf = rhashtable_lookup(&nfsd_file_rhash_tbl, &key,
nfsd_file_rhash_params);
if (!nf)
break;
-
- /* If we raced with someone else unhashing, ignore it */
- if (!nfsd_file_unhash(nf))
- continue;
-
- /* If we can't get a reference, ignore it */
- if (!nfsd_file_get(nf))
- continue;
-
- /* Extra decrement if we remove from the LRU */
- if (nfsd_file_lru_remove(nf))
- ++decrement;
-
- /* If refcount goes to 0, then put on the dispose list */
- if (refcount_sub_and_test(decrement, &nf->nf_ref)) {
- list_add(&nf->nf_lru, dispose);
- trace_nfsd_file_closing(nf);
- }
+ nfsd_file_cond_queue(nf, dispose);
} while (1);
rcu_read_unlock();
}
@@ -928,11 +942,8 @@ __nfsd_file_cache_purge(struct net *net)
nf = rhashtable_walk_next(&iter);
while (!IS_ERR_OR_NULL(nf)) {
- if (!net || nf->nf_net == net) {
- nfsd_file_unhash(nf);
- nfsd_file_lru_remove(nf);
- list_add(&nf->nf_lru, &dispose);
- }
+ if (!net || nf->nf_net == net)
+ nfsd_file_cond_queue(nf, &dispose);
nf = rhashtable_walk_next(&iter);
}
@@ -1071,8 +1082,8 @@ nfsd_file_is_cached(struct inode *inode)
static __be32
nfsd_file_do_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
- unsigned int may_flags, struct nfsd_file **pnf,
- bool open, bool want_gc)
+ unsigned int may_flags, struct file *file,
+ struct nfsd_file **pnf, bool want_gc)
{
struct nfsd_file_lookup_key key = {
.type = NFSD_FILE_KEY_FULL,
@@ -1147,8 +1158,7 @@ wait_for_construction:
status = nfserrno(nfsd_open_break_lease(file_inode(nf->nf_file), may_flags));
out:
if (status == nfs_ok) {
- if (open)
- this_cpu_inc(nfsd_file_acquisitions);
+ this_cpu_inc(nfsd_file_acquisitions);
*pnf = nf;
} else {
if (refcount_dec_and_test(&nf->nf_ref))
@@ -1158,20 +1168,23 @@ out:
out_status:
put_cred(key.cred);
- if (open)
- trace_nfsd_file_acquire(rqstp, key.inode, may_flags, nf, status);
+ trace_nfsd_file_acquire(rqstp, key.inode, may_flags, nf, status);
return status;
open_file:
trace_nfsd_file_alloc(nf);
nf->nf_mark = nfsd_file_mark_find_or_create(nf, key.inode);
if (nf->nf_mark) {
- if (open) {
+ if (file) {
+ get_file(file);
+ nf->nf_file = file;
+ status = nfs_ok;
+ trace_nfsd_file_opened(nf, status);
+ } else {
status = nfsd_open_verified(rqstp, fhp, may_flags,
&nf->nf_file);
trace_nfsd_file_open(nf, status);
- } else
- status = nfs_ok;
+ }
} else
status = nfserr_jukebox;
/*
@@ -1207,7 +1220,7 @@ __be32
nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **pnf)
{
- return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, true, true);
+ return nfsd_file_do_acquire(rqstp, fhp, may_flags, NULL, pnf, true);
}
/**
@@ -1228,28 +1241,30 @@ __be32
nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **pnf)
{
- return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, true, false);
+ return nfsd_file_do_acquire(rqstp, fhp, may_flags, NULL, pnf, false);
}
/**
- * nfsd_file_create - Get a struct nfsd_file, do not open
+ * nfsd_file_acquire_opened - Get a struct nfsd_file using existing open file
* @rqstp: the RPC transaction being executed
* @fhp: the NFS filehandle of the file just created
* @may_flags: NFSD_MAY_ settings for the file
+ * @file: cached, already-open file (may be NULL)
* @pnf: OUT: new or found "struct nfsd_file" object
*
- * The nfsd_file_object returned by this API is reference-counted
- * but not garbage-collected. The object is released immediately
- * one RCU grace period after the final nfsd_file_put().
+ * Acquire a nfsd_file object that is not GC'ed. If one doesn't already exist,
+ * and @file is non-NULL, use it to instantiate a new nfsd_file instead of
+ * opening a new one.
*
* Returns nfs_ok and sets @pnf on success; otherwise an nfsstat in
* network byte order is returned.
*/
__be32
-nfsd_file_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
- unsigned int may_flags, struct nfsd_file **pnf)
+nfsd_file_acquire_opened(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ unsigned int may_flags, struct file *file,
+ struct nfsd_file **pnf)
{
- return nfsd_file_do_acquire(rqstp, fhp, may_flags, pnf, false, false);
+ return nfsd_file_do_acquire(rqstp, fhp, may_flags, file, pnf, false);
}
/*
diff --git a/fs/nfsd/filecache.h b/fs/nfsd/filecache.h
index b7efb2c3ddb185..41516a4263ea51 100644
--- a/fs/nfsd/filecache.h
+++ b/fs/nfsd/filecache.h
@@ -60,7 +60,8 @@ __be32 nfsd_file_acquire_gc(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **nfp);
__be32 nfsd_file_acquire(struct svc_rqst *rqstp, struct svc_fh *fhp,
unsigned int may_flags, struct nfsd_file **nfp);
-__be32 nfsd_file_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
- unsigned int may_flags, struct nfsd_file **nfp);
+__be32 nfsd_file_acquire_opened(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ unsigned int may_flags, struct file *file,
+ struct nfsd_file **nfp);
int nfsd_file_cache_stats_show(struct seq_file *m, void *v);
#endif /* _FS_NFSD_FILECACHE_H */
diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index 8c854ba3285bbc..51a4b7885cae2a 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -195,7 +195,7 @@ struct nfsd_net {
atomic_t nfsd_courtesy_clients;
struct shrinker nfsd_client_shrinker;
- struct delayed_work nfsd_shrinker_work;
+ struct work_struct nfsd_shrinker_work;
};
/* Simple check to find out if a given net was properly initialized */
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index bd880d55f565b0..f189ba7995f5ae 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -937,7 +937,7 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
* the client wants us to do more in this compound:
*/
if (!nfsd4_last_compound_op(rqstp))
- __clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
+ clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
/* check stateid */
status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
@@ -1318,6 +1318,7 @@ try_again:
/* allow 20secs for mount/unmount for now - revisit */
if (signal_pending(current) ||
(schedule_timeout(20*HZ) == 0)) {
+ finish_wait(&nn->nfsd_ssc_waitq, &wait);
kfree(work);
return nfserr_eagain;
}
@@ -2607,12 +2608,11 @@ nfsd4_proc_compound(struct svc_rqst *rqstp)
cstate->minorversion = args->minorversion;
fh_init(current_fh, NFS4_FHSIZE);
fh_init(save_fh, NFS4_FHSIZE);
-
/*
* Don't use the deferral mechanism for NFSv4; compounds make it
* too hard to avoid non-idempotency problems.
*/
- __clear_bit(RQ_USEDEFERRAL, &rqstp->rq_flags);
+ clear_bit(RQ_USEDEFERRAL, &rqstp->rq_flags);
/*
* According to RFC3010, this takes precedence over all other errors.
@@ -2734,7 +2734,7 @@ encode_op:
out:
cstate->status = status;
/* Reset deferral mechanism for RPC deferrals */
- __set_bit(RQ_USEDEFERRAL, &rqstp->rq_flags);
+ set_bit(RQ_USEDEFERRAL, &rqstp->rq_flags);
return rpc_success;
}
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 7b2ee535ade832..4ef529379065a1 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4411,7 +4411,7 @@ nfsd4_state_shrinker_count(struct shrinker *shrink, struct shrink_control *sc)
if (!count)
count = atomic_long_read(&num_delegations);
if (count)
- mod_delayed_work(laundry_wq, &nn->nfsd_shrinker_work, 0);
+ queue_work(laundry_wq, &nn->nfsd_shrinker_work);
return (unsigned long)count;
}
@@ -4421,7 +4421,7 @@ nfsd4_state_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc)
return SHRINK_STOP;
}
-int
+void
nfsd4_init_leases_net(struct nfsd_net *nn)
{
struct sysinfo si;
@@ -4443,16 +4443,6 @@ nfsd4_init_leases_net(struct nfsd_net *nn)
nn->nfs4_max_clients = max_t(int, max_clients, NFS4_CLIENTS_PER_GB);
atomic_set(&nn->nfsd_courtesy_clients, 0);
- nn->nfsd_client_shrinker.scan_objects = nfsd4_state_shrinker_scan;
- nn->nfsd_client_shrinker.count_objects = nfsd4_state_shrinker_count;
- nn->nfsd_client_shrinker.seeks = DEFAULT_SEEKS;
- return register_shrinker(&nn->nfsd_client_shrinker, "nfsd-client");
-}
-
-void
-nfsd4_leases_net_shutdown(struct nfsd_net *nn)
-{
- unregister_shrinker(&nn->nfsd_client_shrinker);
}
static void init_nfs4_replay(struct nfs4_replay *rp)
@@ -5262,18 +5252,10 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp,
if (!fp->fi_fds[oflag]) {
spin_unlock(&fp->fi_lock);
- if (!open->op_filp) {
- status = nfsd_file_acquire(rqstp, cur_fh, access, &nf);
- if (status != nfs_ok)
- goto out_put_access;
- } else {
- status = nfsd_file_create(rqstp, cur_fh, access, &nf);
- if (status != nfs_ok)
- goto out_put_access;
- nf->nf_file = open->op_filp;
- open->op_filp = NULL;
- trace_nfsd_file_create(rqstp, access, nf);
- }
+ status = nfsd_file_acquire_opened(rqstp, cur_fh, access,
+ open->op_filp, &nf);
+ if (status != nfs_ok)
+ goto out_put_access;
spin_lock(&fp->fi_lock);
if (!fp->fi_fds[oflag]) {
@@ -6243,8 +6225,7 @@ deleg_reaper(struct nfsd_net *nn)
static void
nfsd4_state_shrinker_worker(struct work_struct *work)
{
- struct delayed_work *dwork = to_delayed_work(work);
- struct nfsd_net *nn = container_of(dwork, struct nfsd_net,
+ struct nfsd_net *nn = container_of(work, struct nfsd_net,
nfsd_shrinker_work);
courtesy_client_reaper(nn);
@@ -8074,11 +8055,20 @@ static int nfs4_state_create_net(struct net *net)
INIT_LIST_HEAD(&nn->blocked_locks_lru);
INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main);
- INIT_DELAYED_WORK(&nn->nfsd_shrinker_work, nfsd4_state_shrinker_worker);
+ INIT_WORK(&nn->nfsd_shrinker_work, nfsd4_state_shrinker_worker);
get_net(net);
+ nn->nfsd_client_shrinker.scan_objects = nfsd4_state_shrinker_scan;
+ nn->nfsd_client_shrinker.count_objects = nfsd4_state_shrinker_count;
+ nn->nfsd_client_shrinker.seeks = DEFAULT_SEEKS;
+
+ if (register_shrinker(&nn->nfsd_client_shrinker, "nfsd-client"))
+ goto err_shrinker;
return 0;
+err_shrinker:
+ put_net(net);
+ kfree(nn->sessionid_hashtbl);
err_sessionid:
kfree(nn->unconf_id_hashtbl);
err_unconf_id:
@@ -8171,6 +8161,8 @@ nfs4_state_shutdown_net(struct net *net)
struct list_head *pos, *next, reaplist;
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+ unregister_shrinker(&nn->nfsd_client_shrinker);
+ cancel_work(&nn->nfsd_shrinker_work);
cancel_delayed_work_sync(&nn->laundromat_work);
locks_end_grace(&nn->nfsd4_manager);
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 2b4ae858c89bea..97edb32be77f18 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -2523,7 +2523,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
argp->rqstp->rq_cachetype = cachethis ? RC_REPLBUFF : RC_NOCACHE;
if (readcount > 1 || max_reply > PAGE_SIZE - auth_slack)
- __clear_bit(RQ_SPLICE_OK, &argp->rqstp->rq_flags);
+ clear_bit(RQ_SPLICE_OK, &argp->rqstp->rq_flags);
return true;
}
@@ -3629,6 +3629,17 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
case nfserr_noent:
xdr_truncate_encode(xdr, start_offset);
goto skip_entry;
+ case nfserr_jukebox:
+ /*
+ * The pseudoroot should only display dentries that lead to
+ * exports. If we get EJUKEBOX here, then we can't tell whether
+ * this entry should be included. Just fail the whole READDIR
+ * with NFS4ERR_DELAY in that case, and hope that the situation
+ * will resolve itself by the client's next attempt.
+ */
+ if (cd->rd_fhp->fh_export->ex_flags & NFSEXP_V4ROOT)
+ goto fail;
+ fallthrough;
default:
/*
* If the client requested the RDATTR_ERROR attribute,
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index d1e581a60480c0..c2577ee7ffb220 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -1457,9 +1457,7 @@ static __net_init int nfsd_init_net(struct net *net)
goto out_idmap_error;
nn->nfsd_versions = NULL;
nn->nfsd4_minorversions = NULL;
- retval = nfsd4_init_leases_net(nn);
- if (retval)
- goto out_drc_error;
+ nfsd4_init_leases_net(nn);
retval = nfsd_reply_cache_init(nn);
if (retval)
goto out_cache_error;
@@ -1469,8 +1467,6 @@ static __net_init int nfsd_init_net(struct net *net)
return 0;
out_cache_error:
- nfsd4_leases_net_shutdown(nn);
-out_drc_error:
nfsd_idmap_shutdown(net);
out_idmap_error:
nfsd_export_shutdown(net);
@@ -1486,7 +1482,6 @@ static __net_exit void nfsd_exit_net(struct net *net)
nfsd_idmap_shutdown(net);
nfsd_export_shutdown(net);
nfsd_netns_free_versions(net_generic(net, nfsd_net_id));
- nfsd4_leases_net_shutdown(nn);
}
static struct pernet_operations nfsd_net_ops = {
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h
index 93b42ef9ed91b6..fa0144a742678f 100644
--- a/fs/nfsd/nfsd.h
+++ b/fs/nfsd/nfsd.h
@@ -504,8 +504,7 @@ extern void unregister_cld_notifier(void);
extern void nfsd4_ssc_init_umount_work(struct nfsd_net *nn);
#endif
-extern int nfsd4_init_leases_net(struct nfsd_net *nn);
-extern void nfsd4_leases_net_shutdown(struct nfsd_net *nn);
+extern void nfsd4_init_leases_net(struct nfsd_net *nn);
#else /* CONFIG_NFSD_V4 */
static inline int nfsd4_is_junction(struct dentry *dentry)
@@ -513,8 +512,7 @@ static inline int nfsd4_is_junction(struct dentry *dentry)
return 0;
}
-static inline int nfsd4_init_leases_net(struct nfsd_net *nn) { return 0; };
-static inline void nfsd4_leases_net_shutdown(struct nfsd_net *nn) {};
+static inline void nfsd4_init_leases_net(struct nfsd_net *nn) { };
#define register_cld_notifier() 0
#define unregister_cld_notifier() do { } while(0)
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index a5570cf75f3fd5..9744443c396522 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -211,7 +211,7 @@ nfsd_proc_read(struct svc_rqst *rqstp)
if (resp->status == nfs_ok)
resp->status = fh_getattr(&resp->fh, &resp->stat);
else if (resp->status == nfserr_jukebox)
- __set_bit(RQ_DROPME, &rqstp->rq_flags);
+ set_bit(RQ_DROPME, &rqstp->rq_flags);
return rpc_success;
}
@@ -246,7 +246,7 @@ nfsd_proc_write(struct svc_rqst *rqstp)
if (resp->status == nfs_ok)
resp->status = fh_getattr(&resp->fh, &resp->stat);
else if (resp->status == nfserr_jukebox)
- __set_bit(RQ_DROPME, &rqstp->rq_flags);
+ set_bit(RQ_DROPME, &rqstp->rq_flags);
return rpc_success;
}
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 56fba1cba3af7e..325d3d3f121109 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -453,8 +453,8 @@ static void nfsd_shutdown_net(struct net *net)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
- nfsd_file_cache_shutdown_net(net);
nfs4_state_shutdown_net(net);
+ nfsd_file_cache_shutdown_net(net);
if (nn->lockd_up) {
lockd_down(net);
nn->lockd_up = false;
diff --git a/fs/nfsd/trace.h b/fs/nfsd/trace.h
index c852ae8eaf3714..8f9c82d9e075b2 100644
--- a/fs/nfsd/trace.h
+++ b/fs/nfsd/trace.h
@@ -981,43 +981,6 @@ TRACE_EVENT(nfsd_file_acquire,
)
);
-TRACE_EVENT(nfsd_file_create,
- TP_PROTO(
- const struct svc_rqst *rqstp,
- unsigned int may_flags,
- const struct nfsd_file *nf
- ),
-
- TP_ARGS(rqstp, may_flags, nf),
-
- TP_STRUCT__entry(
- __field(const void *, nf_inode)
- __field(const void *, nf_file)
- __field(unsigned long, may_flags)
- __field(unsigned long, nf_flags)
- __field(unsigned long, nf_may)
- __field(unsigned int, nf_ref)
- __field(u32, xid)
- ),
-
- TP_fast_assign(
- __entry->nf_inode = nf->nf_inode;
- __entry->nf_file = nf->nf_file;
- __entry->may_flags = may_flags;
- __entry->nf_flags = nf->nf_flags;
- __entry->nf_may = nf->nf_may;
- __entry->nf_ref = refcount_read(&nf->nf_ref);
- __entry->xid = be32_to_cpu(rqstp->rq_xid);
- ),
-
- TP_printk("xid=0x%x inode=%p may_flags=%s ref=%u nf_flags=%s nf_may=%s nf_file=%p",
- __entry->xid, __entry->nf_inode,
- show_nfsd_may_flags(__entry->may_flags),
- __entry->nf_ref, show_nf_flags(__entry->nf_flags),
- show_nfsd_may_flags(__entry->nf_may), __entry->nf_file
- )
-);
-
TRACE_EVENT(nfsd_file_insert_err,
TP_PROTO(
const struct svc_rqst *rqstp,
@@ -1079,8 +1042,8 @@ TRACE_EVENT(nfsd_file_cons_err,
)
);
-TRACE_EVENT(nfsd_file_open,
- TP_PROTO(struct nfsd_file *nf, __be32 status),
+DECLARE_EVENT_CLASS(nfsd_file_open_class,
+ TP_PROTO(const struct nfsd_file *nf, __be32 status),
TP_ARGS(nf, status),
TP_STRUCT__entry(
__field(void *, nf_inode) /* cannot be dereferenced */
@@ -1104,6 +1067,17 @@ TRACE_EVENT(nfsd_file_open,
__entry->nf_file)
)
+#define DEFINE_NFSD_FILE_OPEN_EVENT(name) \
+DEFINE_EVENT(nfsd_file_open_class, name, \
+ TP_PROTO( \
+ const struct nfsd_file *nf, \
+ __be32 status \
+ ), \
+ TP_ARGS(nf, status))
+
+DEFINE_NFSD_FILE_OPEN_EVENT(nfsd_file_open);
+DEFINE_NFSD_FILE_OPEN_EVENT(nfsd_file_opened);
+
TRACE_EVENT(nfsd_file_is_cached,
TP_PROTO(
const struct inode *inode,
diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c
index b9d15c3df3cc19..40ce92a332fe7a 100644
--- a/fs/nilfs2/btree.c
+++ b/fs/nilfs2/btree.c
@@ -480,9 +480,18 @@ static int __nilfs_btree_get_block(const struct nilfs_bmap *btree, __u64 ptr,
ret = nilfs_btnode_submit_block(btnc, ptr, 0, REQ_OP_READ, &bh,
&submit_ptr);
if (ret) {
- if (ret != -EEXIST)
- return ret;
- goto out_check;
+ if (likely(ret == -EEXIST))
+ goto out_check;
+ if (ret == -ENOENT) {
+ /*
+ * Block address translation failed due to invalid
+ * value of 'ptr'. In this case, return internal code
+ * -EINVAL (broken bmap) to notify bmap layer of fatal
+ * metadata corruption.
+ */
+ ret = -EINVAL;
+ }
+ return ret;
}
if (ra) {
diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c
index e5399ebc3a2b2d..d294cd9756887e 100644
--- a/fs/ntfs3/file.c
+++ b/fs/ntfs3/file.c
@@ -390,10 +390,10 @@ static int ntfs_truncate(struct inode *inode, loff_t new_size)
new_valid = ntfs_up_block(sb, min_t(u64, ni->i_valid, new_size));
- ni_lock(ni);
-
truncate_setsize(inode, new_size);
+ ni_lock(ni);
+
down_write(&ni->file.run_lock);
err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, new_size,
&new_valid, ni->mi.sbi->options->prealloc, NULL);
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 6e4e65ee050d6d..c14e90764e3565 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -792,7 +792,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
if (!c->metacopy && c->stat.size) {
err = ovl_copy_up_file(ofs, c->dentry, tmpfile, c->stat.size);
if (err)
- return err;
+ goto out_fput;
}
err = ovl_copy_up_metadata(c, temp);
@@ -1011,6 +1011,10 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
if (err)
return err;
+ if (!kuid_has_mapping(current_user_ns(), ctx.stat.uid) ||
+ !kgid_has_mapping(current_user_ns(), ctx.stat.gid))
+ return -EOVERFLOW;
+
ctx.metacopy = ovl_need_meta_copy_up(dentry, ctx.stat.mode, flags);
if (parent) {
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index e35a0398db63f9..af1c49ae11b165 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -745,9 +745,7 @@ static int smaps_hugetlb_range(pte_t *pte, unsigned long hmask,
page = pfn_swap_entry_to_page(swpent);
}
if (page) {
- int mapcount = page_mapcount(page);
-
- if (mapcount >= 2)
+ if (page_mapcount(page) >= 2 || hugetlb_pmd_shared(pte))
mss->shared_hugetlb += huge_page_size(hstate_vma(vma));
else
mss->private_hugetlb += huge_page_size(hstate_vma(vma));
diff --git a/fs/squashfs/squashfs_fs.h b/fs/squashfs/squashfs_fs.h
index b3fdc8212c5f52..95f8e890176899 100644
--- a/fs/squashfs/squashfs_fs.h
+++ b/fs/squashfs/squashfs_fs.h
@@ -183,7 +183,7 @@ static inline int squashfs_block_size(__le32 raw)
#define SQUASHFS_ID_BLOCK_BYTES(A) (SQUASHFS_ID_BLOCKS(A) *\
sizeof(u64))
/* xattr id lookup table defines */
-#define SQUASHFS_XATTR_BYTES(A) ((A) * sizeof(struct squashfs_xattr_id))
+#define SQUASHFS_XATTR_BYTES(A) (((u64) (A)) * sizeof(struct squashfs_xattr_id))
#define SQUASHFS_XATTR_BLOCK(A) (SQUASHFS_XATTR_BYTES(A) / \
SQUASHFS_METADATA_SIZE)
diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h
index 659082e9e51d6f..72f6f4b378636e 100644
--- a/fs/squashfs/squashfs_fs_sb.h
+++ b/fs/squashfs/squashfs_fs_sb.h
@@ -63,7 +63,7 @@ struct squashfs_sb_info {
long long bytes_used;
unsigned int inodes;
unsigned int fragments;
- int xattr_ids;
+ unsigned int xattr_ids;
unsigned int ids;
bool panic_on_errors;
const struct squashfs_decompressor_thread_ops *thread_ops;
diff --git a/fs/squashfs/xattr.h b/fs/squashfs/xattr.h
index d8a270d3ac4cb2..f1a463d8bfa020 100644
--- a/fs/squashfs/xattr.h
+++ b/fs/squashfs/xattr.h
@@ -10,12 +10,12 @@
#ifdef CONFIG_SQUASHFS_XATTR
extern __le64 *squashfs_read_xattr_id_table(struct super_block *, u64,
- u64 *, int *);
+ u64 *, unsigned int *);
extern int squashfs_xattr_lookup(struct super_block *, unsigned int, int *,
unsigned int *, unsigned long long *);
#else
static inline __le64 *squashfs_read_xattr_id_table(struct super_block *sb,
- u64 start, u64 *xattr_table_start, int *xattr_ids)
+ u64 start, u64 *xattr_table_start, unsigned int *xattr_ids)
{
struct squashfs_xattr_id_table *id_table;
diff --git a/fs/squashfs/xattr_id.c b/fs/squashfs/xattr_id.c
index 087cab8c78f4e8..b88d19e9581e93 100644
--- a/fs/squashfs/xattr_id.c
+++ b/fs/squashfs/xattr_id.c
@@ -56,7 +56,7 @@ int squashfs_xattr_lookup(struct super_block *sb, unsigned int index,
* Read uncompressed xattr id lookup table indexes from disk into memory
*/
__le64 *squashfs_read_xattr_id_table(struct super_block *sb, u64 table_start,
- u64 *xattr_table_start, int *xattr_ids)
+ u64 *xattr_table_start, unsigned int *xattr_ids)
{
struct squashfs_sb_info *msblk = sb->s_fs_info;
unsigned int len, indexes;
@@ -76,7 +76,7 @@ __le64 *squashfs_read_xattr_id_table(struct super_block *sb, u64 table_start,
/* Sanity check values */
/* there is always at least one xattr id */
- if (*xattr_ids == 0)
+ if (*xattr_ids <= 0)
return ERR_PTR(-EINVAL);
len = SQUASHFS_XATTR_BLOCK_BYTES(*xattr_ids);
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index 1d7c2a812fc198..34e416327dd4ee 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -595,7 +595,7 @@ static void udf_do_extend_final_block(struct inode *inode,
*/
if (new_elen <= (last_ext->extLength & UDF_EXTENT_LENGTH_MASK))
return;
- added_bytes = (last_ext->extLength & UDF_EXTENT_LENGTH_MASK) - new_elen;
+ added_bytes = new_elen - (last_ext->extLength & UDF_EXTENT_LENGTH_MASK);
last_ext->extLength += added_bytes;
UDF_I(inode)->i_lenExtents += added_bytes;
@@ -684,7 +684,7 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
struct kernel_lb_addr eloc, tmpeloc;
int c = 1;
loff_t lbcount = 0, b_off = 0;
- udf_pblk_t newblocknum, newblock;
+ udf_pblk_t newblocknum, newblock = 0;
sector_t offset = 0;
int8_t etype;
struct udf_inode_info *iinfo = UDF_I(inode);
@@ -787,7 +787,6 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
ret = udf_do_extend_file(inode, &prev_epos, laarr, hole_len);
if (ret < 0) {
*err = ret;
- newblock = 0;
goto out_free;
}
c = 0;
@@ -852,7 +851,6 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
goal, err);
if (!newblocknum) {
*err = -ENOSPC;
- newblock = 0;
goto out_free;
}
if (isBeyondEOF)
diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c
index 98ac37e34e3d4b..cc694846617a52 100644
--- a/fs/userfaultfd.c
+++ b/fs/userfaultfd.c
@@ -108,6 +108,21 @@ static bool userfaultfd_is_initialized(struct userfaultfd_ctx *ctx)
return ctx->features & UFFD_FEATURE_INITIALIZED;
}
+static void userfaultfd_set_vm_flags(struct vm_area_struct *vma,
+ vm_flags_t flags)
+{
+ const bool uffd_wp_changed = (vma->vm_flags ^ flags) & VM_UFFD_WP;
+
+ vma->vm_flags = flags;
+ /*
+ * For shared mappings, we want to enable writenotify while
+ * userfaultfd-wp is enabled (see vma_wants_writenotify()). We'll simply
+ * recalculate vma->vm_page_prot whenever userfaultfd-wp changes.
+ */
+ if ((vma->vm_flags & VM_SHARED) && uffd_wp_changed)
+ vma_set_page_prot(vma);
+}
+
static int userfaultfd_wake_function(wait_queue_entry_t *wq, unsigned mode,
int wake_flags, void *key)
{
@@ -618,7 +633,8 @@ static void userfaultfd_event_wait_completion(struct userfaultfd_ctx *ctx,
for_each_vma(vmi, vma) {
if (vma->vm_userfaultfd_ctx.ctx == release_new_ctx) {
vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
- vma->vm_flags &= ~__VM_UFFD_FLAGS;
+ userfaultfd_set_vm_flags(vma,
+ vma->vm_flags & ~__VM_UFFD_FLAGS);
}
}
mmap_write_unlock(mm);
@@ -652,7 +668,7 @@ int dup_userfaultfd(struct vm_area_struct *vma, struct list_head *fcs)
octx = vma->vm_userfaultfd_ctx.ctx;
if (!octx || !(octx->features & UFFD_FEATURE_EVENT_FORK)) {
vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
- vma->vm_flags &= ~__VM_UFFD_FLAGS;
+ userfaultfd_set_vm_flags(vma, vma->vm_flags & ~__VM_UFFD_FLAGS);
return 0;
}
@@ -733,7 +749,7 @@ void mremap_userfaultfd_prep(struct vm_area_struct *vma,
} else {
/* Drop uffd context if remap feature not enabled */
vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
- vma->vm_flags &= ~__VM_UFFD_FLAGS;
+ userfaultfd_set_vm_flags(vma, vma->vm_flags & ~__VM_UFFD_FLAGS);
}
}
@@ -895,7 +911,7 @@ static int userfaultfd_release(struct inode *inode, struct file *file)
prev = vma;
}
- vma->vm_flags = new_flags;
+ userfaultfd_set_vm_flags(vma, new_flags);
vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
}
mmap_write_unlock(mm);
@@ -1463,7 +1479,7 @@ static int userfaultfd_register(struct userfaultfd_ctx *ctx,
* the next vma was merged into the current one and
* the current one has not been updated yet.
*/
- vma->vm_flags = new_flags;
+ userfaultfd_set_vm_flags(vma, new_flags);
vma->vm_userfaultfd_ctx.ctx = ctx;
if (is_vm_hugetlb_page(vma) && uffd_disable_huge_pmd_share(vma))
@@ -1651,7 +1667,7 @@ static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
* the next vma was merged into the current one and
* the current one has not been updated yet.
*/
- vma->vm_flags = new_flags;
+ userfaultfd_set_vm_flags(vma, new_flags);
vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
skip:
diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c
index 4c16c8c31fcbcd..35f574421670da 100644
--- a/fs/xfs/libxfs/xfs_btree.c
+++ b/fs/xfs/libxfs/xfs_btree.c
@@ -4666,7 +4666,12 @@ xfs_btree_space_to_height(
const unsigned int *limits,
unsigned long long leaf_blocks)
{
- unsigned long long node_blocks = limits[1];
+ /*
+ * The root btree block can have fewer than minrecs pointers in it
+ * because the tree might not be big enough to require that amount of
+ * fanout. Hence it has a minimum size of 2 pointers, not limits[1].
+ */
+ unsigned long long node_blocks = 2;
unsigned long long blocks_left = leaf_blocks - 1;
unsigned int height = 1;
diff --git a/fs/xfs/xfs_extent_busy.c b/fs/xfs/xfs_extent_busy.c
index ad22a003f9595c..f3d328e4a4408b 100644
--- a/fs/xfs/xfs_extent_busy.c
+++ b/fs/xfs/xfs_extent_busy.c
@@ -236,6 +236,7 @@ xfs_extent_busy_update_extent(
*
*/
busyp->bno = fend;
+ busyp->length = bend - fend;
} else if (bbno < fbno) {
/*
* Case 8:
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index f35e2cee526558..ddeaccc04aec94 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -1853,12 +1853,20 @@ xfs_inodegc_worker(
struct xfs_inodegc, work);
struct llist_node *node = llist_del_all(&gc->list);
struct xfs_inode *ip, *n;
+ unsigned int nofs_flag;
WRITE_ONCE(gc->items, 0);
if (!node)
return;
+ /*
+ * We can allocate memory here while doing writeback on behalf of
+ * memory reclaim. To avoid memory allocation deadlocks set the
+ * task-wide nofs context for the following operations.
+ */
+ nofs_flag = memalloc_nofs_save();
+
ip = llist_entry(node, struct xfs_inode, i_gclist);
trace_xfs_inodegc_worker(ip->i_mount, READ_ONCE(gc->shrinker_hits));
@@ -1867,6 +1875,8 @@ xfs_inodegc_worker(
xfs_iflags_set(ip, XFS_INACTIVATING);
xfs_inodegc_inactivate(ip);
}
+
+ memalloc_nofs_restore(nofs_flag);
}
/*
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 13f1b2add39041..736510bc241b85 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -754,7 +754,7 @@ xfs_bulkstat_fmt(
static int
xfs_bulk_ireq_setup(
struct xfs_mount *mp,
- struct xfs_bulk_ireq *hdr,
+ const struct xfs_bulk_ireq *hdr,
struct xfs_ibulk *breq,
void __user *ubuffer)
{
@@ -780,7 +780,7 @@ xfs_bulk_ireq_setup(
switch (hdr->ino) {
case XFS_BULK_IREQ_SPECIAL_ROOT:
- hdr->ino = mp->m_sb.sb_rootino;
+ breq->startino = mp->m_sb.sb_rootino;
break;
default:
return -EINVAL;
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 669c1bc5c3a777..fc1946f80a4aac 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -83,7 +83,7 @@ xfs_iomap_valid(
return true;
}
-const struct iomap_page_ops xfs_iomap_page_ops = {
+static const struct iomap_page_ops xfs_iomap_page_ops = {
.iomap_valid = xfs_iomap_valid,
};
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index ff53d40a2dae3a..e2c542f6dcd4d8 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -68,7 +68,7 @@ restart:
while (1) {
struct xfs_dquot *batch[XFS_DQ_LOOKUP_BATCH];
- int error = 0;
+ int error;
int i;
mutex_lock(&qi->qi_tree_lock);
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index fe46bce8cae638..5535778a98f925 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -416,8 +416,6 @@ xfs_reflink_fill_cow_hole(
goto convert;
}
- ASSERT(cmap->br_startoff > imap->br_startoff);
-
/* Allocate the entire reservation as unwritten blocks. */
nimaps = 1;
error = xfs_bmapi_write(tp, ip, imap->br_startoff, imap->br_blockcount,
diff --git a/fs/zonefs/super.c b/fs/zonefs/super.c
index 2c53fbb8d918e5..a9c5c3f720adf8 100644
--- a/fs/zonefs/super.c
+++ b/fs/zonefs/super.c
@@ -442,6 +442,10 @@ static int zonefs_io_error_cb(struct blk_zone *zone, unsigned int idx,
data_size = zonefs_check_zone_condition(inode, zone,
false, false);
}
+ } else if (sbi->s_mount_opts & ZONEFS_MNTOPT_ERRORS_RO &&
+ data_size > isize) {
+ /* Do not expose garbage data */
+ data_size = isize;
}
/*
@@ -805,6 +809,24 @@ static ssize_t zonefs_file_dio_append(struct kiocb *iocb, struct iov_iter *from)
ret = submit_bio_wait(bio);
+ /*
+ * If the file zone was written underneath the file system, the zone
+ * write pointer may not be where we expect it to be, but the zone
+ * append write can still succeed. So check manually that we wrote where
+ * we intended to, that is, at zi->i_wpoffset.
+ */
+ if (!ret) {
+ sector_t wpsector =
+ zi->i_zsector + (zi->i_wpoffset >> SECTOR_SHIFT);
+
+ if (bio->bi_iter.bi_sector != wpsector) {
+ zonefs_warn(inode->i_sb,
+ "Corrupted write pointer %llu for zone at %llu\n",
+ wpsector, zi->i_zsector);
+ ret = -EIO;
+ }
+ }
+
zonefs_file_write_dio_end_io(iocb, size, ret, 0);
trace_zonefs_file_dio_append(inode, size, ret);
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index cd3b75e08ec3fa..e44be31115a674 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -230,7 +230,8 @@ struct acpi_pnp_type {
u32 hardware_id:1;
u32 bus_address:1;
u32 platform_id:1;
- u32 reserved:29;
+ u32 backlight:1;
+ u32 reserved:28;
};
struct acpi_device_pnp {
diff --git a/include/acpi/video.h b/include/acpi/video.h
index a275c35e5249da..8ed9bec03e5341 100644
--- a/include/acpi/video.h
+++ b/include/acpi/video.h
@@ -53,6 +53,7 @@ enum acpi_backlight_type {
};
#if IS_ENABLED(CONFIG_ACPI_VIDEO)
+extern void acpi_video_report_nolcd(void);
extern int acpi_video_register(void);
extern void acpi_video_unregister(void);
extern void acpi_video_register_backlight(void);
@@ -69,6 +70,7 @@ extern int acpi_video_get_levels(struct acpi_device *device,
struct acpi_video_device_brightness **dev_br,
int *pmax_level);
#else
+static inline void acpi_video_report_nolcd(void) { return; };
static inline int acpi_video_register(void) { return -ENODEV; }
static inline void acpi_video_unregister(void) { return; }
static inline void acpi_video_register_backlight(void) { return; }
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index a94219e9916f18..659bf3b31c9111 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -891,7 +891,12 @@
#define PRINTK_INDEX
#endif
+/*
+ * Discard .note.GNU-stack, which is emitted as PROGBITS by the compiler.
+ * Otherwise, the type of .notes section would become PROGBITS instead of NOTES.
+ */
#define NOTES \
+ /DISCARD/ : { *(.note.GNU-stack) } \
.notes : AT(ADDR(.notes) - LOAD_OFFSET) { \
BOUNDED_SECTION_BY(.note.*, _notes) \
} NOTES_HEADERS \
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
index 4fc8018edddad7..1220d185c776b1 100644
--- a/include/drm/drm_client.h
+++ b/include/drm/drm_client.h
@@ -127,11 +127,6 @@ struct drm_client_buffer {
struct drm_client_dev *client;
/**
- * @handle: Buffer handle
- */
- u32 handle;
-
- /**
* @pitch: Buffer pitch
*/
u32 pitch;
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index b111dc7ada78d7..095370ef029ded 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -208,6 +208,18 @@ struct drm_fb_helper {
* the smem_start field should always be cleared to zero.
*/
bool hint_leak_smem_start;
+
+#ifdef CONFIG_FB_DEFERRED_IO
+ /**
+ * @fbdefio:
+ *
+ * Temporary storage for the driver's FB deferred I/O handler. If the
+ * driver uses the DRM fbdev emulation layer, this is set by the core
+ * to a generic deferred I/O handler if a driver is preferring to use
+ * a shadow buffer.
+ */
+ struct fb_deferred_io fbdefio;
+#endif
};
static inline struct drm_fb_helper *
diff --git a/include/drm/drm_plane_helper.h b/include/drm/drm_plane_helper.h
index ff83d26216876a..3a574e8cd22f43 100644
--- a/include/drm/drm_plane_helper.h
+++ b/include/drm/drm_plane_helper.h
@@ -26,6 +26,7 @@
#include <linux/types.h>
+struct drm_atomic_state;
struct drm_crtc;
struct drm_framebuffer;
struct drm_modeset_acquire_ctx;
diff --git a/include/drm/drm_vma_manager.h b/include/drm/drm_vma_manager.h
index 4f8c35206f7cd7..6c2a2f21dbf00d 100644
--- a/include/drm/drm_vma_manager.h
+++ b/include/drm/drm_vma_manager.h
@@ -74,6 +74,7 @@ void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
struct drm_vma_offset_node *node);
int drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *tag);
+int drm_vma_node_allow_once(struct drm_vma_offset_node *node, struct drm_file *tag);
void drm_vma_node_revoke(struct drm_vma_offset_node *node,
struct drm_file *tag);
bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node,
diff --git a/include/kunit/test.h b/include/kunit/test.h
index 87ea90576b5074..08d3559dd7038c 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -303,7 +303,6 @@ static inline int kunit_run_all_tests(void)
*/
#define kunit_test_init_section_suites(__suites...) \
__kunit_test_suites(CONCATENATE(__UNIQUE_ID(array), _probe), \
- CONCATENATE(__UNIQUE_ID(suites), _probe), \
##__suites)
#define kunit_test_init_section_suite(suite) \
@@ -683,8 +682,9 @@ do { \
.right_text = #right, \
}; \
\
- if (likely(memcmp(__left, __right, __size) op 0)) \
- break; \
+ if (likely(__left && __right)) \
+ if (likely(memcmp(__left, __right, __size) op 0)) \
+ break; \
\
_KUNIT_FAILED(test, \
assert_type, \
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 9270cd87da3fc8..6470f67e63c485 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -263,7 +263,7 @@ struct vgic_dist {
struct vgic_io_device dist_iodev;
bool has_its;
- bool save_its_tables_in_progress;
+ bool table_write_in_progress;
/*
* Contains the attributes and gpa of the LPI configuration table.
diff --git a/include/linux/apple-gmux.h b/include/linux/apple-gmux.h
index ddb10aa67b1436..1f68b49bcd6880 100644
--- a/include/linux/apple-gmux.h
+++ b/include/linux/apple-gmux.h
@@ -8,18 +8,118 @@
#define LINUX_APPLE_GMUX_H
#include <linux/acpi.h>
+#include <linux/io.h>
+#include <linux/pnp.h>
#define GMUX_ACPI_HID "APP000B"
+/*
+ * gmux port offsets. Many of these are not yet used, but may be in the
+ * future, and it's useful to have them documented here anyhow.
+ */
+#define GMUX_PORT_VERSION_MAJOR 0x04
+#define GMUX_PORT_VERSION_MINOR 0x05
+#define GMUX_PORT_VERSION_RELEASE 0x06
+#define GMUX_PORT_SWITCH_DISPLAY 0x10
+#define GMUX_PORT_SWITCH_GET_DISPLAY 0x11
+#define GMUX_PORT_INTERRUPT_ENABLE 0x14
+#define GMUX_PORT_INTERRUPT_STATUS 0x16
+#define GMUX_PORT_SWITCH_DDC 0x28
+#define GMUX_PORT_SWITCH_EXTERNAL 0x40
+#define GMUX_PORT_SWITCH_GET_EXTERNAL 0x41
+#define GMUX_PORT_DISCRETE_POWER 0x50
+#define GMUX_PORT_MAX_BRIGHTNESS 0x70
+#define GMUX_PORT_BRIGHTNESS 0x74
+#define GMUX_PORT_VALUE 0xc2
+#define GMUX_PORT_READ 0xd0
+#define GMUX_PORT_WRITE 0xd4
+
+#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
+
#if IS_ENABLED(CONFIG_APPLE_GMUX)
+static inline bool apple_gmux_is_indexed(unsigned long iostart)
+{
+ u16 val;
+
+ outb(0xaa, iostart + 0xcc);
+ outb(0x55, iostart + 0xcd);
+ outb(0x00, iostart + 0xce);
+
+ val = inb(iostart + 0xcc) | (inb(iostart + 0xcd) << 8);
+ if (val == 0x55aa)
+ return true;
+
+ return false;
+}
/**
- * apple_gmux_present() - detect if gmux is built into the machine
+ * apple_gmux_detect() - detect if gmux is built into the machine
+ *
+ * @pnp_dev: Device to probe or NULL to use the first matching device
+ * @indexed_ret: Returns (by reference) if the gmux is indexed or not
+ *
+ * Detect if a supported gmux device is present by actually probing it.
+ * This avoids the false positives returned on some models by
+ * apple_gmux_present().
+ *
+ * Return: %true if a supported gmux ACPI device is detected and the kernel
+ * was configured with CONFIG_APPLE_GMUX, %false otherwise.
+ */
+static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, bool *indexed_ret)
+{
+ u8 ver_major, ver_minor, ver_release;
+ struct device *dev = NULL;
+ struct acpi_device *adev;
+ struct resource *res;
+ bool indexed = false;
+ bool ret = false;
+
+ if (!pnp_dev) {
+ adev = acpi_dev_get_first_match_dev(GMUX_ACPI_HID, NULL, -1);
+ if (!adev)
+ return false;
+
+ dev = get_device(acpi_get_first_physical_node(adev));
+ acpi_dev_put(adev);
+ if (!dev)
+ return false;
+
+ pnp_dev = to_pnp_dev(dev);
+ }
+
+ res = pnp_get_resource(pnp_dev, IORESOURCE_IO, 0);
+ if (!res || resource_size(res) < GMUX_MIN_IO_LEN)
+ goto out;
+
+ /*
+ * Invalid version information may indicate either that the gmux
+ * device isn't present or that it's a new one that uses indexed io.
+ */
+ ver_major = inb(res->start + GMUX_PORT_VERSION_MAJOR);
+ ver_minor = inb(res->start + GMUX_PORT_VERSION_MINOR);
+ ver_release = inb(res->start + GMUX_PORT_VERSION_RELEASE);
+ if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
+ indexed = apple_gmux_is_indexed(res->start);
+ if (!indexed)
+ goto out;
+ }
+
+ if (indexed_ret)
+ *indexed_ret = indexed;
+
+ ret = true;
+out:
+ put_device(dev);
+ return ret;
+}
+
+/**
+ * apple_gmux_present() - check if gmux ACPI device is present
*
* Drivers may use this to activate quirks specific to dual GPU MacBook Pros
* and Mac Pros, e.g. for deferred probing, runtime pm and backlight.
*
- * Return: %true if gmux is present and the kernel was configured
+ * Return: %true if gmux ACPI device is present and the kernel was configured
* with CONFIG_APPLE_GMUX, %false otherwise.
*/
static inline bool apple_gmux_present(void)
@@ -34,6 +134,11 @@ static inline bool apple_gmux_present(void)
return false;
}
+static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, bool *indexed_ret)
+{
+ return false;
+}
+
#endif /* !CONFIG_APPLE_GMUX */
#endif /* LINUX_APPLE_GMUX_H */
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 22078a28d7cb13..c1da63f6c80800 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -475,6 +475,8 @@ void __bio_release_pages(struct bio *bio, bool mark_dirty);
extern void bio_set_pages_dirty(struct bio *bio);
extern void bio_check_pages_dirty(struct bio *bio);
+extern void bio_copy_data_iter(struct bio *dst, struct bvec_iter *dst_iter,
+ struct bio *src, struct bvec_iter *src_iter);
extern void bio_copy_data(struct bio *dst, struct bio *src);
extern void bio_free_pages(struct bio *bio);
void guard_bio_eod(struct bio *bio);
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 301cf1cf4f2fac..43d4e073b1115e 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1395,6 +1395,7 @@ struct block_device_operations {
void (*swap_slot_free_notify) (struct block_device *, unsigned long);
int (*report_zones)(struct gendisk *, sector_t sector,
unsigned int nr_zones, report_zones_cb cb, void *data);
+ char *(*devnode)(struct gendisk *disk, umode_t *mode);
/* returns the length of the identifier or a negative errno: */
int (*get_unique_id)(struct gendisk *disk, u8 id[16],
enum blk_unique_id id_type);
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 3de24cfb7a3ded..634d37a599fa71 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1832,7 +1832,7 @@ void bpf_prog_inc(struct bpf_prog *prog);
struct bpf_prog * __must_check bpf_prog_inc_not_zero(struct bpf_prog *prog);
void bpf_prog_put(struct bpf_prog *prog);
-void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock);
+void bpf_prog_free_id(struct bpf_prog *prog);
void bpf_map_free_id(struct bpf_map *map, bool do_idr_lock);
struct btf_field *btf_record_find(const struct btf_record *rec,
diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h
index 00af2c98da75a7..4497d0a6772cdf 100644
--- a/include/linux/ceph/libceph.h
+++ b/include/linux/ceph/libceph.h
@@ -99,16 +99,6 @@ struct ceph_options {
#define CEPH_AUTH_NAME_DEFAULT "guest"
-/* mount state */
-enum {
- CEPH_MOUNT_MOUNTING,
- CEPH_MOUNT_MOUNTED,
- CEPH_MOUNT_UNMOUNTING,
- CEPH_MOUNT_UNMOUNTED,
- CEPH_MOUNT_SHUTDOWN,
- CEPH_MOUNT_RECOVER,
-};
-
static inline unsigned long ceph_timeout_jiffies(unsigned long timeout)
{
return timeout ?: MAX_SCHEDULE_TIMEOUT;
diff --git a/include/linux/dsa/tag_qca.h b/include/linux/dsa/tag_qca.h
index b1b5720d89a596..ee657452f122a8 100644
--- a/include/linux/dsa/tag_qca.h
+++ b/include/linux/dsa/tag_qca.h
@@ -45,8 +45,8 @@ struct sk_buff;
QCA_HDR_MGMT_COMMAND_LEN + \
QCA_HDR_MGMT_DATA1_LEN)
-#define QCA_HDR_MGMT_DATA2_LEN 12 /* Other 12 byte for the mdio data */
-#define QCA_HDR_MGMT_PADDING_LEN 34 /* Padding to reach the min Ethernet packet */
+#define QCA_HDR_MGMT_DATA2_LEN 28 /* Other 28 byte for the mdio data */
+#define QCA_HDR_MGMT_PADDING_LEN 18 /* Padding to reach the min Ethernet packet */
#define QCA_HDR_MGMT_PKT_LEN (QCA_HDR_MGMT_HEADER_LEN + \
QCA_HDR_LEN + \
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 4b27519143f56f..98598bd1d2fa52 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -668,7 +668,8 @@ extern struct efi {
#define EFI_RT_SUPPORTED_ALL 0x3fff
-#define EFI_RT_SUPPORTED_TIME_SERVICES 0x000f
+#define EFI_RT_SUPPORTED_TIME_SERVICES 0x0003
+#define EFI_RT_SUPPORTED_WAKEUP_SERVICES 0x000c
#define EFI_RT_SUPPORTED_VARIABLE_SERVICES 0x0070
extern struct mm_struct efi_mm;
diff --git a/include/linux/elfcore.h b/include/linux/elfcore.h
index 9ec81290e3c893..bd5560542c799e 100644
--- a/include/linux/elfcore.h
+++ b/include/linux/elfcore.h
@@ -105,14 +105,14 @@ int elf_core_copy_task_fpregs(struct task_struct *t, elf_fpregset_t *fpu);
* Dumping its extra ELF program headers includes all the other information
* a debugger needs to easily find how the gate DSO was being used.
*/
-extern Elf_Half elf_core_extra_phdrs(void);
+extern Elf_Half elf_core_extra_phdrs(struct coredump_params *cprm);
extern int
elf_core_write_extra_phdrs(struct coredump_params *cprm, loff_t offset);
extern int
elf_core_write_extra_data(struct coredump_params *cprm);
-extern size_t elf_core_extra_data_size(void);
+extern size_t elf_core_extra_data_size(struct coredump_params *cprm);
#else
-static inline Elf_Half elf_core_extra_phdrs(void)
+static inline Elf_Half elf_core_extra_phdrs(struct coredump_params *cprm)
{
return 0;
}
@@ -127,7 +127,7 @@ static inline int elf_core_write_extra_data(struct coredump_params *cprm)
return 1;
}
-static inline size_t elf_core_extra_data_size(void)
+static inline size_t elf_core_extra_data_size(struct coredump_params *cprm)
{
return 0;
}
diff --git a/include/linux/firewire.h b/include/linux/firewire.h
index 980019053e541a..56505436d1590c 100644
--- a/include/linux/firewire.h
+++ b/include/linux/firewire.h
@@ -278,9 +278,8 @@ typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode,
* Otherwise there is a danger of recursion of inbound and outbound
* transactions from and to the local node.
*
- * The callback is responsible that either fw_send_response() or kfree()
- * is called on the @request, except for FCP registers for which the core
- * takes care of that.
+ * The callback is responsible that fw_send_response() is called on the @request, except for FCP
+ * registers for which the core takes care of that.
*/
typedef void (*fw_address_callback_t)(struct fw_card *card,
struct fw_request *request,
diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h
index b986e267d149be..b09f443d3ab9df 100644
--- a/include/linux/firmware/xlnx-zynqmp.h
+++ b/include/linux/firmware/xlnx-zynqmp.h
@@ -545,8 +545,8 @@ int zynqmp_pm_request_wake(const u32 node,
const u64 address,
const enum zynqmp_pm_request_ack ack);
int zynqmp_pm_get_rpu_mode(u32 node_id, enum rpu_oper_mode *rpu_mode);
-int zynqmp_pm_set_rpu_mode(u32 node_id, u32 arg1);
-int zynqmp_pm_set_tcm_config(u32 node_id, u32 arg1);
+int zynqmp_pm_set_rpu_mode(u32 node_id, enum rpu_oper_mode rpu_mode);
+int zynqmp_pm_set_tcm_config(u32 node_id, enum rpu_tcm_comb tcm_mode);
int zynqmp_pm_set_sd_config(u32 node, enum pm_sd_config_type config, u32 value);
int zynqmp_pm_set_gem_config(u32 node, enum pm_gem_config_type config,
u32 value);
@@ -845,12 +845,12 @@ static inline int zynqmp_pm_get_rpu_mode(u32 node_id, enum rpu_oper_mode *rpu_mo
return -ENODEV;
}
-static inline int zynqmp_pm_set_rpu_mode(u32 node_id, u32 arg1)
+static inline int zynqmp_pm_set_rpu_mode(u32 node_id, enum rpu_oper_mode rpu_mode)
{
return -ENODEV;
}
-static inline int zynqmp_pm_set_tcm_config(u32 node_id, u32 arg1)
+static inline int zynqmp_pm_set_tcm_config(u32 node_id, enum rpu_tcm_comb tcm_mode)
{
return -ENODEV;
}
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 066555ad1bf810..c1769a2c5d7082 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1119,6 +1119,9 @@ struct file_lock {
int state; /* state of grant or error if -ve */
unsigned int debug_id;
} afs;
+ struct {
+ struct inode *inode;
+ } ceph;
} fl_u;
} __randomize_layout;
diff --git a/include/linux/highmem-internal.h b/include/linux/highmem-internal.h
index 034b1106d02285..e098f38422af58 100644
--- a/include/linux/highmem-internal.h
+++ b/include/linux/highmem-internal.h
@@ -200,7 +200,7 @@ static inline void *kmap_local_pfn(unsigned long pfn)
static inline void __kunmap_local(const void *addr)
{
#ifdef ARCH_HAS_FLUSH_ON_KUNMAP
- kunmap_flush_on_unmap(addr);
+ kunmap_flush_on_unmap(PTR_ALIGN_DOWN(addr, PAGE_SIZE));
#endif
}
@@ -227,7 +227,7 @@ static inline void *kmap_atomic_pfn(unsigned long pfn)
static inline void __kunmap_atomic(const void *addr)
{
#ifdef ARCH_HAS_FLUSH_ON_KUNMAP
- kunmap_flush_on_unmap(addr);
+ kunmap_flush_on_unmap(PTR_ALIGN_DOWN(addr, PAGE_SIZE));
#endif
pagefault_enable();
if (IS_ENABLED(CONFIG_PREEMPT_RT))
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 551834cd529951..db194e2ba69fd0 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -7,6 +7,7 @@
#include <linux/fs.h>
#include <linux/hugetlb_inline.h>
#include <linux/cgroup.h>
+#include <linux/page_ref.h>
#include <linux/list.h>
#include <linux/kref.h>
#include <linux/pgtable.h>
@@ -1187,6 +1188,18 @@ static inline __init void hugetlb_cma_reserve(int order)
}
#endif
+#ifdef CONFIG_ARCH_WANT_HUGE_PMD_SHARE
+static inline bool hugetlb_pmd_shared(pte_t *pte)
+{
+ return page_count(virt_to_page(pte)) > 1;
+}
+#else
+static inline bool hugetlb_pmd_shared(pte_t *pte)
+{
+ return false;
+}
+#endif
+
bool want_pmd_share(struct vm_area_struct *vma, unsigned long addr);
#ifndef __HAVE_ARCH_FLUSH_HUGETLB_TLB_RANGE
diff --git a/include/linux/io_uring_types.h b/include/linux/io_uring_types.h
index dcd8a563ab522f..128a67a40065f2 100644
--- a/include/linux/io_uring_types.h
+++ b/include/linux/io_uring_types.h
@@ -292,6 +292,8 @@ struct io_ring_ctx {
struct {
spinlock_t completion_lock;
+ bool poll_multi_queue;
+
/*
* ->iopoll_list is protected by the ctx->uring_lock for
* io_uring instances that don't use IORING_SETUP_SQPOLL.
@@ -300,7 +302,6 @@ struct io_ring_ctx {
*/
struct io_wq_work_list iopoll_list;
struct io_hash_table cancel_table;
- bool poll_multi_queue;
struct llist_head work_llist;
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index d3c8203cab6ce7..85dc9b88ea3799 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -1666,10 +1666,13 @@ void mem_cgroup_track_foreign_dirty_slowpath(struct folio *folio,
static inline void mem_cgroup_track_foreign_dirty(struct folio *folio,
struct bdi_writeback *wb)
{
+ struct mem_cgroup *memcg;
+
if (mem_cgroup_disabled())
return;
- if (unlikely(&folio_memcg(folio)->css != wb->memcg_css))
+ memcg = folio_memcg(folio);
+ if (unlikely(memcg && &memcg->css != wb->memcg_css))
mem_cgroup_track_foreign_dirty_slowpath(folio, wb);
}
diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h
index 5fe5d198b57ade..29d4b201c7b26d 100644
--- a/include/linux/mlx5/device.h
+++ b/include/linux/mlx5/device.h
@@ -1091,6 +1091,11 @@ enum {
};
enum {
+ MLX5_VPORT_CVLAN_INSERT_WHEN_NO_CVLAN = 0x1,
+ MLX5_VPORT_CVLAN_INSERT_ALWAYS = 0x3,
+};
+
+enum {
MLX5_L3_PROT_TYPE_IPV4 = 0,
MLX5_L3_PROT_TYPE_IPV6 = 1,
};
diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h
index d476255c9a3f0d..333c1fec72f87d 100644
--- a/include/linux/mlx5/driver.h
+++ b/include/linux/mlx5/driver.h
@@ -315,7 +315,7 @@ struct mlx5_cmd {
struct mlx5_cmd_debug dbg;
struct cmd_msg_cache cache[MLX5_NUM_COMMAND_CACHES];
int checksum_disabled;
- struct mlx5_cmd_stats *stats;
+ struct mlx5_cmd_stats stats[MLX5_CMD_OP_MAX];
};
struct mlx5_cmd_mailbox {
@@ -573,6 +573,14 @@ struct mlx5_debugfs_entries {
struct dentry *lag_debugfs;
};
+enum mlx5_func_type {
+ MLX5_PF,
+ MLX5_VF,
+ MLX5_SF,
+ MLX5_HOST_PF,
+ MLX5_FUNC_TYPE_NUM,
+};
+
struct mlx5_ft_pool;
struct mlx5_priv {
/* IRQ table valid only for real pci devices PF or VF */
@@ -583,11 +591,10 @@ struct mlx5_priv {
struct mlx5_nb pg_nb;
struct workqueue_struct *pg_wq;
struct xarray page_root_xa;
- u32 fw_pages;
atomic_t reg_pages;
struct list_head free_list;
- u32 vfs_pages;
- u32 host_pf_pages;
+ u32 fw_pages;
+ u32 page_counters[MLX5_FUNC_TYPE_NUM];
u32 fw_pages_alloc_failed;
u32 give_pages_dropped;
u32 reclaim_pages_discard;
diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h
index f3d1c62c98ddc4..a9ee7bc59c9019 100644
--- a/include/linux/mlx5/mlx5_ifc.h
+++ b/include/linux/mlx5/mlx5_ifc.h
@@ -913,7 +913,8 @@ struct mlx5_ifc_e_switch_cap_bits {
u8 vport_svlan_insert[0x1];
u8 vport_cvlan_insert_if_not_exist[0x1];
u8 vport_cvlan_insert_overwrite[0x1];
- u8 reserved_at_5[0x2];
+ u8 reserved_at_5[0x1];
+ u8 vport_cvlan_insert_always[0x1];
u8 esw_shared_ingress_acl[0x1];
u8 esw_uplink_ingress_acl[0x1];
u8 root_ft_on_other_esw[0x1];
diff --git a/include/linux/mm.h b/include/linux/mm.h
index f3f196e4d66d6f..8f857163ac89c4 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -1270,10 +1270,10 @@ static inline void folio_put_refs(struct folio *folio, int refs)
__folio_put(folio);
}
-/**
- * release_pages - release an array of pages or folios
+/*
+ * union release_pages_arg - an array of pages or folios
*
- * This just releases a simple array of multiple pages, and
+ * release_pages() releases a simple array of multiple pages, and
* accepts various different forms of said page array: either
* a regular old boring array of pages, an array of folios, or
* an array of encoded page pointers.
diff --git a/include/linux/mm_inline.h b/include/linux/mm_inline.h
index e8ed225d8f7cac..ff3f3f23f64989 100644
--- a/include/linux/mm_inline.h
+++ b/include/linux/mm_inline.h
@@ -413,8 +413,7 @@ static inline void free_anon_vma_name(struct vm_area_struct *vma)
* Not using anon_vma_name because it generates a warning if mmap_lock
* is not held, which might be the case here.
*/
- if (!vma->vm_file)
- anon_vma_name_put(vma->anon_name);
+ anon_vma_name_put(vma->anon_name);
}
static inline bool anon_vma_name_eq(struct anon_vma_name *anon_name1,
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 3b8475007734dc..9757067c305374 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -581,7 +581,7 @@ struct vm_area_struct {
/*
* For private and shared anonymous mappings, a pointer to a null
* terminated string containing the name given to the vma, or NULL if
- * unnamed. Serialized by mmap_sem. Use anon_vma_name to access.
+ * unnamed. Serialized by mmap_lock. Use anon_vma_name to access.
*/
struct anon_vma_name *anon_name;
#endif
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index 25765556223a1f..a3f8cdca90c832 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -7,7 +7,6 @@
#define __LINUX_MTD_SPI_NOR_H
#include <linux/bitops.h>
-#include <linux/mtd/cfi.h>
#include <linux/mtd/mtd.h>
#include <linux/spi/spi-mem.h>
diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h
index ab934ad951a870..e8c350a3ade153 100644
--- a/include/linux/netfilter/ipset/ip_set.h
+++ b/include/linux/netfilter/ipset/ip_set.h
@@ -197,7 +197,7 @@ struct ip_set_region {
};
/* Max range where every element is added/deleted in one step */
-#define IPSET_MAX_RANGE (1<<20)
+#define IPSET_MAX_RANGE (1<<14)
/* The max revision number supported by any set type + 1 */
#define IPSET_REVISION_MAX 9
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index d6be2a6861000e..4fad4aa245fb06 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -7,6 +7,7 @@
#ifndef _LINUX_NVME_H
#define _LINUX_NVME_H
+#include <linux/bits.h>
#include <linux/types.h>
#include <linux/uuid.h>
@@ -639,8 +640,9 @@ enum {
NVME_CMD_EFFECTS_NCC = 1 << 2,
NVME_CMD_EFFECTS_NIC = 1 << 3,
NVME_CMD_EFFECTS_CCC = 1 << 4,
- NVME_CMD_EFFECTS_CSE_MASK = 3 << 16,
+ NVME_CMD_EFFECTS_CSE_MASK = GENMASK(18, 16),
NVME_CMD_EFFECTS_UUID_SEL = 1 << 19,
+ NVME_CMD_EFFECTS_SCOPE_MASK = GENMASK(31, 20),
};
struct nvme_effects_log {
diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h
index 50caa117cb6206..bb15c9234e21e8 100644
--- a/include/linux/nvmem-provider.h
+++ b/include/linux/nvmem-provider.h
@@ -70,7 +70,6 @@ struct nvmem_keepout {
* @word_size: Minimum read/write access granularity.
* @stride: Minimum read/write access stride.
* @priv: User context passed to read/write callbacks.
- * @wp-gpio: Write protect pin
* @ignore_wp: Write Protect pin is managed by the provider.
*
* Note: A default "nvmem<id>" name will be assigned to the device if
@@ -85,7 +84,6 @@ struct nvmem_config {
const char *name;
int id;
struct module *owner;
- struct gpio_desc *wp_gpio;
const struct nvmem_cell_info *cells;
int ncells;
const struct nvmem_keepout *keepout;
diff --git a/include/linux/page_ref.h b/include/linux/page_ref.h
index 2e677e6ad09fcf..d7c2d33baa7f8e 100644
--- a/include/linux/page_ref.h
+++ b/include/linux/page_ref.h
@@ -301,7 +301,7 @@ static inline bool folio_ref_try_add_rcu(struct folio *folio, int count)
*
* You can also use this function if you're holding a lock that prevents
* pages being frozen & removed; eg the i_pages lock for the page cache
- * or the mmap_sem or page table lock for page tables. In this case,
+ * or the mmap_lock or page table lock for page tables. In this case,
* it will always succeed, and you could have used a plain folio_get(),
* but it's sometimes more convenient to have a common function called
* from both locked and RCU-protected contexts.
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 71eeb4e3b1fded..6378c997ded56c 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -826,10 +826,7 @@ struct phy_driver {
* whether to advertise lower-speed modes for that interface. It is
* assumed that if a rate matching mode is supported on an interface,
* then that interface's rate can be adapted to all slower link speeds
- * supported by the phy. If iface is %PHY_INTERFACE_MODE_NA, and the phy
- * supports any kind of rate matching for any interface, then it must
- * return that rate matching mode (preferring %RATE_MATCH_PAUSE to
- * %RATE_MATCH_CRS). If the interface is not supported, this should
+ * supported by the phy. If the interface is not supported, this should
* return %RATE_MATCH_NONE.
*/
int (*get_rate_matching)(struct phy_device *phydev,
diff --git a/include/linux/pktcdvd.h b/include/linux/pktcdvd.h
new file mode 100644
index 00000000000000..f9c5ac80d59b39
--- /dev/null
+++ b/include/linux/pktcdvd.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2000 Jens Axboe <axboe@suse.de>
+ * Copyright (C) 2001-2004 Peter Osterlund <petero2@telia.com>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Packet writing layer for ATAPI and SCSI CD-R, CD-RW, DVD-R, and
+ * DVD-RW devices.
+ *
+ */
+#ifndef __PKTCDVD_H
+#define __PKTCDVD_H
+
+#include <linux/blkdev.h>
+#include <linux/completion.h>
+#include <linux/cdrom.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/mempool.h>
+#include <uapi/linux/pktcdvd.h>
+
+/* default bio write queue congestion marks */
+#define PKT_WRITE_CONGESTION_ON 10000
+#define PKT_WRITE_CONGESTION_OFF 9000
+
+
+struct packet_settings
+{
+ __u32 size; /* packet size in (512 byte) sectors */
+ __u8 fp; /* fixed packets */
+ __u8 link_loss; /* the rest is specified
+ * as per Mt Fuji */
+ __u8 write_type;
+ __u8 track_mode;
+ __u8 block_mode;
+};
+
+/*
+ * Very crude stats for now
+ */
+struct packet_stats
+{
+ unsigned long pkt_started;
+ unsigned long pkt_ended;
+ unsigned long secs_w;
+ unsigned long secs_rg;
+ unsigned long secs_r;
+};
+
+struct packet_cdrw
+{
+ struct list_head pkt_free_list;
+ struct list_head pkt_active_list;
+ spinlock_t active_list_lock; /* Serialize access to pkt_active_list */
+ struct task_struct *thread;
+ atomic_t pending_bios;
+};
+
+/*
+ * Switch to high speed reading after reading this many kilobytes
+ * with no interspersed writes.
+ */
+#define HI_SPEED_SWITCH 512
+
+struct packet_iosched
+{
+ atomic_t attention; /* Set to non-zero when queue processing is needed */
+ int writing; /* Non-zero when writing, zero when reading */
+ spinlock_t lock; /* Protecting read/write queue manipulations */
+ struct bio_list read_queue;
+ struct bio_list write_queue;
+ sector_t last_write; /* The sector where the last write ended */
+ int successive_reads;
+};
+
+/*
+ * 32 buffers of 2048 bytes
+ */
+#if (PAGE_SIZE % CD_FRAMESIZE) != 0
+#error "PAGE_SIZE must be a multiple of CD_FRAMESIZE"
+#endif
+#define PACKET_MAX_SIZE 128
+#define FRAMES_PER_PAGE (PAGE_SIZE / CD_FRAMESIZE)
+#define PACKET_MAX_SECTORS (PACKET_MAX_SIZE * CD_FRAMESIZE >> 9)
+
+enum packet_data_state {
+ PACKET_IDLE_STATE, /* Not used at the moment */
+ PACKET_WAITING_STATE, /* Waiting for more bios to arrive, so */
+ /* we don't have to do as much */
+ /* data gathering */
+ PACKET_READ_WAIT_STATE, /* Waiting for reads to fill in holes */
+ PACKET_WRITE_WAIT_STATE, /* Waiting for the write to complete */
+ PACKET_RECOVERY_STATE, /* Recover after read/write errors */
+ PACKET_FINISHED_STATE, /* After write has finished */
+
+ PACKET_NUM_STATES /* Number of possible states */
+};
+
+/*
+ * Information needed for writing a single packet
+ */
+struct pktcdvd_device;
+
+struct packet_data
+{
+ struct list_head list;
+
+ spinlock_t lock; /* Lock protecting state transitions and */
+ /* orig_bios list */
+
+ struct bio_list orig_bios; /* Original bios passed to pkt_make_request */
+ /* that will be handled by this packet */
+ int write_size; /* Total size of all bios in the orig_bios */
+ /* list, measured in number of frames */
+
+ struct bio *w_bio; /* The bio we will send to the real CD */
+ /* device once we have all data for the */
+ /* packet we are going to write */
+ sector_t sector; /* First sector in this packet */
+ int frames; /* Number of frames in this packet */
+
+ enum packet_data_state state; /* Current state */
+ atomic_t run_sm; /* Incremented whenever the state */
+ /* machine needs to be run */
+ long sleep_time; /* Set this to non-zero to make the state */
+ /* machine run after this many jiffies. */
+
+ atomic_t io_wait; /* Number of pending IO operations */
+ atomic_t io_errors; /* Number of read/write errors during IO */
+
+ struct bio *r_bios[PACKET_MAX_SIZE]; /* bios to use during data gathering */
+ struct page *pages[PACKET_MAX_SIZE / FRAMES_PER_PAGE];
+
+ int cache_valid; /* If non-zero, the data for the zone defined */
+ /* by the sector variable is completely cached */
+ /* in the pages[] vector. */
+
+ int id; /* ID number for debugging */
+ struct pktcdvd_device *pd;
+};
+
+struct pkt_rb_node {
+ struct rb_node rb_node;
+ struct bio *bio;
+};
+
+struct packet_stacked_data
+{
+ struct bio *bio; /* Original read request bio */
+ struct pktcdvd_device *pd;
+};
+#define PSD_POOL_SIZE 64
+
+struct pktcdvd_device
+{
+ struct block_device *bdev; /* dev attached */
+ dev_t pkt_dev; /* our dev */
+ char name[20];
+ struct packet_settings settings;
+ struct packet_stats stats;
+ int refcnt; /* Open count */
+ int write_speed; /* current write speed, kB/s */
+ int read_speed; /* current read speed, kB/s */
+ unsigned long offset; /* start offset */
+ __u8 mode_offset; /* 0 / 8 */
+ __u8 type;
+ unsigned long flags;
+ __u16 mmc3_profile;
+ __u32 nwa; /* next writable address */
+ __u32 lra; /* last recorded address */
+ struct packet_cdrw cdrw;
+ wait_queue_head_t wqueue;
+
+ spinlock_t lock; /* Serialize access to bio_queue */
+ struct rb_root bio_queue; /* Work queue of bios we need to handle */
+ int bio_queue_size; /* Number of nodes in bio_queue */
+ bool congested; /* Someone is waiting for bio_queue_size
+ * to drop. */
+ sector_t current_sector; /* Keep track of where the elevator is */
+ atomic_t scan_queue; /* Set to non-zero when pkt_handle_queue */
+ /* needs to be run. */
+ mempool_t rb_pool; /* mempool for pkt_rb_node allocations */
+
+ struct packet_iosched iosched;
+ struct gendisk *disk;
+
+ int write_congestion_off;
+ int write_congestion_on;
+
+ struct device *dev; /* sysfs pktcdvd[0-7] dev */
+
+ struct dentry *dfs_d_root; /* debugfs: devname directory */
+ struct dentry *dfs_f_info; /* debugfs: info file */
+};
+
+#endif /* __PKTCDVD_H */
diff --git a/include/linux/platform_data/asoc-ux500-msp.h b/include/linux/platform_data/asoc-ux500-msp.h
deleted file mode 100644
index b8d0f730dda8dc..00000000000000
--- a/include/linux/platform_data/asoc-ux500-msp.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
- */
-
-#ifndef __MSP_H
-#define __MSP_H
-
-#include <linux/platform_data/dma-ste-dma40.h>
-
-/* Platform data structure for a MSP I2S-device */
-struct msp_i2s_platform_data {
- int id;
- struct stedma40_chan_cfg *msp_i2s_dma_rx;
- struct stedma40_chan_cfg *msp_i2s_dma_tx;
-};
-
-#endif
diff --git a/include/linux/platform_data/x86/simatic-ipc.h b/include/linux/platform_data/x86/simatic-ipc.h
index 632320ec8f0826..a48bb524097770 100644
--- a/include/linux/platform_data/x86/simatic-ipc.h
+++ b/include/linux/platform_data/x86/simatic-ipc.h
@@ -32,7 +32,8 @@ enum simatic_ipc_station_ids {
SIMATIC_IPC_IPC477E = 0x00000A02,
SIMATIC_IPC_IPC127E = 0x00000D01,
SIMATIC_IPC_IPC227G = 0x00000F01,
- SIMATIC_IPC_IPC427G = 0x00001001,
+ SIMATIC_IPC_IPCBX_39A = 0x00001001,
+ SIMATIC_IPC_IPCPX_39A = 0x00001002,
};
static inline u32 simatic_ipc_get_station_id(u8 *data, int max_len)
diff --git a/include/linux/soc/ti/omap1-io.h b/include/linux/soc/ti/omap1-io.h
index f7f12728d4a637..9a60f45899d3c4 100644
--- a/include/linux/soc/ti/omap1-io.h
+++ b/include/linux/soc/ti/omap1-io.h
@@ -5,7 +5,7 @@
#ifndef __ASSEMBLER__
#include <linux/types.h>
-#ifdef CONFIG_ARCH_OMAP1_ANY
+#ifdef CONFIG_ARCH_OMAP1
/*
* NOTE: Please use ioremap + __raw_read/write where possible instead of these
*/
@@ -15,7 +15,7 @@ extern u32 omap_readl(u32 pa);
extern void omap_writeb(u8 v, u32 pa);
extern void omap_writew(u16 v, u32 pa);
extern void omap_writel(u32 v, u32 pa);
-#else
+#elif defined(CONFIG_COMPILE_TEST)
static inline u8 omap_readb(u32 pa) { return 0; }
static inline u16 omap_readw(u32 pa) { return 0; }
static inline u32 omap_readl(u32 pa) { return 0; }
diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
index 9e4537f409c295..7d9fbf9359e4d4 100644
--- a/include/linux/soundwire/sdw.h
+++ b/include/linux/soundwire/sdw.h
@@ -566,13 +566,15 @@ struct sdw_prepare_ch {
* enum sdw_port_prep_ops: Prepare operations for Data Port
*
* @SDW_OPS_PORT_PRE_PREP: Pre prepare operation for the Port
- * @SDW_OPS_PORT_PREP: Prepare operation for the Port
+ * @SDW_OPS_PORT_PRE_DEPREP: Pre deprepare operation for the Port
* @SDW_OPS_PORT_POST_PREP: Post prepare operation for the Port
+ * @SDW_OPS_PORT_POST_DEPREP: Post deprepare operation for the Port
*/
enum sdw_port_prep_ops {
SDW_OPS_PORT_PRE_PREP = 0,
- SDW_OPS_PORT_PREP = 1,
- SDW_OPS_PORT_POST_PREP = 2,
+ SDW_OPS_PORT_PRE_DEPREP,
+ SDW_OPS_PORT_POST_PREP,
+ SDW_OPS_PORT_POST_DEPREP,
};
/**
diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h
index 1341f7d62da446..be48f1cb1878ba 100644
--- a/include/linux/spinlock.h
+++ b/include/linux/spinlock.h
@@ -476,6 +476,15 @@ extern int _atomic_dec_and_lock_irqsave(atomic_t *atomic, spinlock_t *lock,
#define atomic_dec_and_lock_irqsave(atomic, lock, flags) \
__cond_lock(lock, _atomic_dec_and_lock_irqsave(atomic, lock, &(flags)))
+extern int _atomic_dec_and_raw_lock(atomic_t *atomic, raw_spinlock_t *lock);
+#define atomic_dec_and_raw_lock(atomic, lock) \
+ __cond_lock(lock, _atomic_dec_and_raw_lock(atomic, lock))
+
+extern int _atomic_dec_and_raw_lock_irqsave(atomic_t *atomic, raw_spinlock_t *lock,
+ unsigned long *flags);
+#define atomic_dec_and_raw_lock_irqsave(atomic, lock, flags) \
+ __cond_lock(lock, _atomic_dec_and_raw_lock_irqsave(atomic, lock, &(flags)))
+
int __alloc_bucket_spinlocks(spinlock_t **locks, unsigned int *lock_mask,
size_t max_size, unsigned int cpu_mult,
gfp_t gfp, const char *name,
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
index 83ca2e8eb6b574..a152678b82b714 100644
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -252,6 +252,7 @@ struct plat_stmmacenet_data {
int rss_en;
int mac_port_sel_speed;
bool en_tx_lpi_clockgating;
+ bool rx_clk_runs_in_lpi;
int has_xgmac;
bool vlan_fail_q_en;
u8 vlan_fail_q;
diff --git a/include/linux/sunrpc/rpc_pipe_fs.h b/include/linux/sunrpc/rpc_pipe_fs.h
index cd188a527d1690..3b35b6f6533aab 100644
--- a/include/linux/sunrpc/rpc_pipe_fs.h
+++ b/include/linux/sunrpc/rpc_pipe_fs.h
@@ -92,6 +92,11 @@ extern ssize_t rpc_pipe_generic_upcall(struct file *, struct rpc_pipe_msg *,
char __user *, size_t);
extern int rpc_queue_upcall(struct rpc_pipe *, struct rpc_pipe_msg *);
+/* returns true if the msg is in-flight, i.e., already eaten by the peer */
+static inline bool rpc_msg_is_inflight(const struct rpc_pipe_msg *msg) {
+ return (msg->copied != 0 && list_empty(&msg->list));
+}
+
struct rpc_clnt;
extern struct dentry *rpc_create_client_dir(struct dentry *, const char *, struct rpc_clnt *);
extern int rpc_remove_client_dir(struct rpc_clnt *);
diff --git a/include/linux/swap.h b/include/linux/swap.h
index 2787b84eaf12f7..0ceed49516adcc 100644
--- a/include/linux/swap.h
+++ b/include/linux/swap.h
@@ -418,8 +418,7 @@ extern unsigned long try_to_free_pages(struct zonelist *zonelist, int order,
extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
unsigned long nr_pages,
gfp_t gfp_mask,
- unsigned int reclaim_options,
- nodemask_t *nodemask);
+ unsigned int reclaim_options);
extern unsigned long mem_cgroup_shrink_node(struct mem_cgroup *mem,
gfp_t gfp_mask, bool noswap,
pg_data_t *pgdat,
diff --git a/include/linux/tpm_eventlog.h b/include/linux/tpm_eventlog.h
index 20c0ff54b7a0d3..7d68a5cc588163 100644
--- a/include/linux/tpm_eventlog.h
+++ b/include/linux/tpm_eventlog.h
@@ -198,8 +198,8 @@ static __always_inline int __calc_tpm2_event_size(struct tcg_pcr_event2_head *ev
* The loop below will unmap these fields if the log is larger than
* one page, so save them here for reference:
*/
- count = READ_ONCE(event->count);
- event_type = READ_ONCE(event->event_type);
+ count = event->count;
+ event_type = event->event_type;
/* Verify that it's the log header */
if (event_header->pcr_idx != 0 ||
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index 4342e996bcdb76..0e373222a6df8a 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -270,6 +270,7 @@ struct trace_event_fields {
const int align;
const int is_signed;
const int filter_type;
+ const int len;
};
int (*define_fields)(struct trace_event_call *);
};
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 7d5325d47c451d..86d1c8e79566ee 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -267,16 +267,15 @@ static inline void *usb_get_intfdata(struct usb_interface *intf)
}
/**
- * usb_set_intfdata() - associate driver-specific data with the interface
- * @intf: the usb interface
- * @data: pointer to the device priv structure or %NULL
+ * usb_set_intfdata() - associate driver-specific data with an interface
+ * @intf: USB interface
+ * @data: driver data
*
- * Drivers should use this function in their probe() to associate their
- * driver-specific data with the usb interface.
+ * Drivers can use this function in their probe() callbacks to associate
+ * driver-specific data with an interface.
*
- * When disconnecting, the core will take care of setting @intf back to %NULL,
- * so no actions are needed on the driver side. The interface should not be set
- * to %NULL before all actions completed (e.g. no outsanding URB remaining).
+ * Note that there is generally no need to clear the driver-data pointer even
+ * if some drivers do so for historical or implementation-specific reasons.
*/
static inline void usb_set_intfdata(struct usb_interface *intf, void *data)
{
@@ -774,11 +773,14 @@ extern struct device *usb_intf_get_dma_device(struct usb_interface *intf);
extern int usb_acpi_set_power_state(struct usb_device *hdev, int index,
bool enable);
extern bool usb_acpi_power_manageable(struct usb_device *hdev, int index);
+extern int usb_acpi_port_lpm_incapable(struct usb_device *hdev, int index);
#else
static inline int usb_acpi_set_power_state(struct usb_device *hdev, int index,
bool enable) { return 0; }
static inline bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
{ return true; }
+static inline int usb_acpi_port_lpm_incapable(struct usb_device *hdev, int index)
+ { return 0; }
#endif
/* USB autosuspend and autoresume */
diff --git a/include/linux/util_macros.h b/include/linux/util_macros.h
index 72299f261b2533..43db6e47503c72 100644
--- a/include/linux/util_macros.h
+++ b/include/linux/util_macros.h
@@ -38,4 +38,16 @@
*/
#define find_closest_descending(x, a, as) __find_closest(x, a, as, >=)
+/**
+ * is_insidevar - check if the @ptr points inside the @var memory range.
+ * @ptr: the pointer to a memory address.
+ * @var: the variable which address and size identify the memory range.
+ *
+ * Evaluates to true if the address in @ptr lies within the memory
+ * range allocated to @var.
+ */
+#define is_insidevar(ptr, var) \
+ ((uintptr_t)(ptr) >= (uintptr_t)(var) && \
+ (uintptr_t)(ptr) < (uintptr_t)(var) + sizeof(var))
+
#endif
diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h
index d5a5ae9263804a..ba717eac0229a2 100644
--- a/include/net/af_rxrpc.h
+++ b/include/net/af_rxrpc.h
@@ -15,6 +15,7 @@ struct key;
struct sock;
struct socket;
struct rxrpc_call;
+enum rxrpc_abort_reason;
enum rxrpc_interruptibility {
RXRPC_INTERRUPTIBLE, /* Call is interruptible */
@@ -55,7 +56,7 @@ int rxrpc_kernel_send_data(struct socket *, struct rxrpc_call *,
int rxrpc_kernel_recv_data(struct socket *, struct rxrpc_call *,
struct iov_iter *, size_t *, bool, u32 *, u16 *);
bool rxrpc_kernel_abort_call(struct socket *, struct rxrpc_call *,
- u32, int, const char *);
+ u32, int, enum rxrpc_abort_reason);
void rxrpc_kernel_end_call(struct socket *, struct rxrpc_call *);
void rxrpc_kernel_get_peer(struct socket *, struct rxrpc_call *,
struct sockaddr_rxrpc *);
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
index 69174093078f02..99bd823e97f624 100644
--- a/include/net/inet_hashtables.h
+++ b/include/net/inet_hashtables.h
@@ -108,6 +108,10 @@ struct inet_bind2_bucket {
struct hlist_node node;
/* List of sockets hashed to this bucket */
struct hlist_head owners;
+ /* bhash has twsk in owners, but bhash2 has twsk in
+ * deathrow not to add a member in struct sock_common.
+ */
+ struct hlist_head deathrow;
};
static inline struct net *ib_net(const struct inet_bind_bucket *ib)
diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h
index 5b47545f22d39e..4a8e578405cb37 100644
--- a/include/net/inet_timewait_sock.h
+++ b/include/net/inet_timewait_sock.h
@@ -73,9 +73,14 @@ struct inet_timewait_sock {
u32 tw_priority;
struct timer_list tw_timer;
struct inet_bind_bucket *tw_tb;
+ struct inet_bind2_bucket *tw_tb2;
+ struct hlist_node tw_bind2_node;
};
#define tw_tclass tw_tos
+#define twsk_for_each_bound_bhash2(__tw, list) \
+ hlist_for_each_entry(__tw, list, tw_bind2_node)
+
static inline struct inet_timewait_sock *inet_twsk(const struct sock *sk)
{
return (struct inet_timewait_sock *)sk;
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 689da327ce2e81..e3235b9c02c21f 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1832,8 +1832,6 @@ struct ieee80211_vif_cfg {
* @drv_priv: data area for driver use, will always be aligned to
* sizeof(void \*).
* @txq: the multicast data TX queue
- * @txqs_stopped: per AC flag to indicate that intermediate TXQs are stopped,
- * protected by fq->lock.
* @offload_flags: 802.3 -> 802.11 enapsulation offload flags, see
* &enum ieee80211_offload_flags.
* @mbssid_tx_vif: Pointer to the transmitting interface if MBSSID is enabled.
@@ -1863,8 +1861,6 @@ struct ieee80211_vif {
bool probe_req_reg;
bool rx_mcast_action_reg;
- bool txqs_stopped[IEEE80211_NUM_ACS];
-
struct ieee80211_vif *mbssid_tx_vif;
/* must be last */
diff --git a/include/net/mana/gdma.h b/include/net/mana/gdma.h
index b3ba04615caa32..56189e4252da3b 100644
--- a/include/net/mana/gdma.h
+++ b/include/net/mana/gdma.h
@@ -336,9 +336,12 @@ struct gdma_queue_spec {
};
};
+#define MANA_IRQ_NAME_SZ 32
+
struct gdma_irq_context {
void (*handler)(void *arg);
void *arg;
+ char name[MANA_IRQ_NAME_SZ];
};
struct gdma_context {
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index e69ce23566eab8..9430128aae9911 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -312,17 +312,29 @@ struct nft_set_iter {
/**
* struct nft_set_desc - description of set elements
*
+ * @ktype: key type
* @klen: key length
+ * @dtype: data type
* @dlen: data length
+ * @objtype: object type
+ * @flags: flags
* @size: number of set elements
+ * @policy: set policy
+ * @gc_int: garbage collector interval
* @field_len: length of each field in concatenation, bytes
* @field_count: number of concatenated fields in element
* @expr: set must support for expressions
*/
struct nft_set_desc {
+ u32 ktype;
unsigned int klen;
+ u32 dtype;
unsigned int dlen;
+ u32 objtype;
unsigned int size;
+ u32 policy;
+ u32 gc_int;
+ u64 timeout;
u8 field_len[NFT_REG32_COUNT];
u8 field_count;
bool expr;
@@ -585,7 +597,9 @@ void *nft_set_catchall_gc(const struct nft_set *set);
static inline unsigned long nft_set_gc_interval(const struct nft_set *set)
{
- return set->gc_int ? msecs_to_jiffies(set->gc_int) : HZ;
+ u32 gc_int = READ_ONCE(set->gc_int);
+
+ return gc_int ? msecs_to_jiffies(gc_int) : HZ;
}
/**
@@ -1558,6 +1572,9 @@ struct nft_trans_rule {
struct nft_trans_set {
struct nft_set *set;
u32 set_id;
+ u32 gc_int;
+ u64 timeout;
+ bool update;
bool bound;
};
@@ -1567,6 +1584,12 @@ struct nft_trans_set {
(((struct nft_trans_set *)trans->data)->set_id)
#define nft_trans_set_bound(trans) \
(((struct nft_trans_set *)trans->data)->bound)
+#define nft_trans_set_update(trans) \
+ (((struct nft_trans_set *)trans->data)->update)
+#define nft_trans_set_timeout(trans) \
+ (((struct nft_trans_set *)trans->data)->timeout)
+#define nft_trans_set_gc_int(trans) \
+ (((struct nft_trans_set *)trans->data)->gc_int)
struct nft_trans_chain {
bool update;
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index d5517719af4ef2..af4aa66aaa4eba 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -1288,4 +1288,11 @@ void mq_change_real_num_tx(struct Qdisc *sch, unsigned int new_real_tx);
int sch_frag_xmit_hook(struct sk_buff *skb, int (*xmit)(struct sk_buff *skb));
+/* Make sure qdisc is no longer in SCHED state. */
+static inline void qdisc_synchronize(const struct Qdisc *q)
+{
+ while (test_bit(__QDISC_STATE_SCHED, &q->state))
+ msleep(1);
+}
+
#endif
diff --git a/include/net/tc_wrapper.h b/include/net/tc_wrapper.h
index ceed2fc089ffa3..d323fffb839aac 100644
--- a/include/net/tc_wrapper.h
+++ b/include/net/tc_wrapper.h
@@ -216,6 +216,8 @@ skip:
return tp->classify(skb, tp, res);
}
+#endif /* CONFIG_NET_CLS */
+
static inline void tc_wrapper_init(void)
{
#ifdef CONFIG_X86
@@ -224,8 +226,6 @@ static inline void tc_wrapper_init(void)
#endif
}
-#endif /* CONFIG_NET_CLS */
-
#else
#define TC_INDIRECT_SCOPE static
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index 695eebc6f2c896..e39fb0736ade5b 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -422,6 +422,8 @@ extern int iscsi_host_get_max_scsi_cmds(struct Scsi_Host *shost,
extern struct iscsi_cls_session *
iscsi_session_setup(struct iscsi_transport *, struct Scsi_Host *shost,
uint16_t, int, int, uint32_t, unsigned int);
+void iscsi_session_remove(struct iscsi_cls_session *cls_session);
+void iscsi_session_free(struct iscsi_cls_session *cls_session);
extern void iscsi_session_teardown(struct iscsi_cls_session *);
extern void iscsi_session_recovery_timedout(struct iscsi_cls_session *);
extern int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h
index cab52b0f11d0ca..34c03707fb6efe 100644
--- a/include/scsi/scsi_transport_iscsi.h
+++ b/include/scsi/scsi_transport_iscsi.h
@@ -236,6 +236,14 @@ enum {
ISCSI_SESSION_FREE,
};
+enum {
+ ISCSI_SESSION_TARGET_UNBOUND,
+ ISCSI_SESSION_TARGET_ALLOCATED,
+ ISCSI_SESSION_TARGET_SCANNED,
+ ISCSI_SESSION_TARGET_UNBINDING,
+ ISCSI_SESSION_TARGET_MAX,
+};
+
#define ISCSI_MAX_TARGET -1
struct iscsi_cls_session {
@@ -264,6 +272,7 @@ struct iscsi_cls_session {
*/
pid_t creator;
int state;
+ int target_state; /* session target bind state */
int sid; /* session id */
void *dd_data; /* LLD private data */
struct device dev; /* sysfs transport/container device */
diff --git a/include/soc/bcm2835/raspberrypi-firmware.h b/include/soc/bcm2835/raspberrypi-firmware.h
index ab955591cb72c2..73cac8d0287e89 100644
--- a/include/soc/bcm2835/raspberrypi-firmware.h
+++ b/include/soc/bcm2835/raspberrypi-firmware.h
@@ -170,7 +170,7 @@ struct rpi_firmware_clk_rate_request {
#define RPI_FIRMWARE_CLK_RATE_REQUEST(_id) \
{ \
- .id = _id, \
+ .id = cpu_to_le32(_id), \
}
#if IS_ENABLED(CONFIG_RASPBERRYPI_FIRMWARE)
diff --git a/include/sound/ac97/codec.h b/include/sound/ac97/codec.h
index 9792d25fa36928..2fc641cb198266 100644
--- a/include/sound/ac97/codec.h
+++ b/include/sound/ac97/codec.h
@@ -63,7 +63,7 @@ struct ac97_codec_device {
struct ac97_codec_driver {
struct device_driver driver;
int (*probe)(struct ac97_codec_device *);
- int (*remove)(struct ac97_codec_device *);
+ void (*remove)(struct ac97_codec_device *dev);
void (*shutdown)(struct ac97_codec_device *);
const struct ac97_id *id_table;
};
diff --git a/include/sound/acp63_chip_offset_byte.h b/include/sound/acp63_chip_offset_byte.h
index c9260e1640ae20..d746c1cb016376 100644
--- a/include/sound/acp63_chip_offset_byte.h
+++ b/include/sound/acp63_chip_offset_byte.h
@@ -126,24 +126,24 @@
#define ACP_PAD_PULLDOWN_CTRL 0x0001448
#define ACP_PAD_DRIVE_STRENGTH_CTRL 0x000144C
#define ACP_PAD_SCHMEN_CTRL 0x0001450
-#define ACP_SW_PAD_KEEPER_EN 0x0001454
-#define ACP_SW_WAKE_EN 0x0001458
+#define ACP_SW0_PAD_KEEPER_EN 0x0001454
+#define ACP_SW0_WAKE_EN 0x0001458
#define ACP_I2S_WAKE_EN 0x000145C
#define ACP_SW1_WAKE_EN 0x0001460
-#define ACP_SW_I2S_ERROR_REASON 0x00018B4
-#define ACP_SW_POS_TRACK_I2S_TX_CTRL 0x00018B8
-#define ACP_SW_I2S_TX_DMA_POS 0x00018BC
-#define ACP_SW_POS_TRACK_BT_TX_CTRL 0x00018C0
-#define ACP_SW_BT_TX_DMA_POS 0x00018C4
-#define ACP_SW_POS_TRACK_HS_TX_CTRL 0x00018C8
-#define ACP_SW_HS_TX_DMA_POS 0x00018CC
-#define ACP_SW_POS_TRACK_I2S_RX_CTRL 0x00018D0
-#define ACP_SW_I2S_RX_DMA_POS 0x00018D4
-#define ACP_SW_POS_TRACK_BT_RX_CTRL 0x00018D8
-#define ACP_SW_BT_RX_DMA_POS 0x00018DC
-#define ACP_SW_POS_TRACK_HS_RX_CTRL 0x00018E0
-#define ACP_SW_HS_RX_DMA_POS 0x00018E4
+#define ACP_SW0_I2S_ERROR_REASON 0x00018B4
+#define ACP_SW0_POS_TRACK_AUDIO0_TX_CTRL 0x00018B8
+#define ACP_SW0_AUDIO0_TX_DMA_POS 0x00018BC
+#define ACP_SW0_POS_TRACK_AUDIO1_TX_CTRL 0x00018C0
+#define ACP_SW0_AUDIO1_TX_DMA_POS 0x00018C4
+#define ACP_SW0_POS_TRACK_AUDIO2_TX_CTRL 0x00018C8
+#define ACP_SW0_AUDIO2_TX_DMA_POS 0x00018CC
+#define ACP_SW0_POS_TRACK_AUDIO0_RX_CTRL 0x00018D0
+#define ACP_SW0_AUDIO0_DMA_POS 0x00018D4
+#define ACP_SW0_POS_TRACK_AUDIO1_RX_CTRL 0x00018D8
+#define ACP_SW0_AUDIO1_RX_DMA_POS 0x00018DC
+#define ACP_SW0_POS_TRACK_AUDIO2_RX_CTRL 0x00018E0
+#define ACP_SW0_AUDIO2_RX_DMA_POS 0x00018E4
#define ACP_ERROR_INTR_MASK1 0X0001974
#define ACP_ERROR_INTR_MASK2 0X0001978
#define ACP_ERROR_INTR_MASK3 0X000197C
@@ -155,98 +155,80 @@
#define ACP_EXTERNAL_INTR_STAT 0x0001A0C
#define ACP_EXTERNAL_INTR_STAT1 0x0001A10
#define ACP_ERROR_STATUS 0x0001A4C
-#define ACP_P1_SW_I2S_ERROR_REASON 0x0001A50
-#define ACP_P1_SW_POS_TRACK_I2S_TX_CTRL 0x0001A6C
-#define ACP_P1_SW_I2S_TX_DMA_POS 0x0001A70
-#define ACP_P1_SW_POS_TRACK_I2S_RX_CTRL 0x0001A74
-#define ACP_P1_SW_I2S_RX_DMA_POS 0x0001A78
+#define ACP_SW1_I2S_ERROR_REASON 0x0001A50
+#define ACP_SW1_POS_TRACK_AUDIO0_TX_CTRL 0x0001A6C
+#define ACP_SW1_AUDIO0_TX_DMA_POS 0x0001A70
+#define ACP_SW1_POS_TRACK_AUDIO0_RX_CTRL 0x0001A74
+#define ACP_SW1_AUDIO0_RX_DMA_POS 0x0001A78
#define ACP_P1_DMIC_I2S_GPIO_INTR_CTRL 0x0001A7C
#define ACP_P1_DMIC_I2S_GPIO_INTR_STATUS 0x0001A80
#define ACP_SCRATCH_REG_BASE_ADDR 0x0001A84
-#define ACP_P1_SW_POS_TRACK_BT_TX_CTRL 0x0001A88
-#define ACP_P1_SW_BT_TX_DMA_POS 0x0001A8C
-#define ACP_P1_SW_POS_TRACK_HS_TX_CTRL 0x0001A90
-#define ACP_P1_SW_HS_TX_DMA_POS 0x0001A94
-#define ACP_P1_SW_POS_TRACK_BT_RX_CTRL 0x0001A98
-#define ACP_P1_SW_BT_RX_DMA_POS 0x0001A9C
-#define ACP_P1_SW_POS_TRACK_HS_RX_CTRL 0x0001AA0
-#define ACP_P1_SW_HS_RX_DMA_POS 0x0001AA4
+#define ACP_SW1_POS_TRACK_AUDIO1_TX_CTRL 0x0001A88
+#define ACP_SW1_AUDIO1_TX_DMA_POS 0x0001A8C
+#define ACP_SW1_POS_TRACK_AUDIO2_TX_CTRL 0x0001A90
+#define ACP_SW1_AUDIO2_TX_DMA_POS 0x0001A94
+#define ACP_SW1_POS_TRACK_AUDIO1_RX_CTRL 0x0001A98
+#define ACP_SW1_AUDIO1_RX_DMA_POS 0x0001A9C
+#define ACP_SW1_POS_TRACK_AUDIO2_RX_CTRL 0x0001AA0
+#define ACP_SW1_AUDIO2_RX_DMA_POS 0x0001AA4
#define ACP_ERROR_INTR_MASK4 0X0001AEC
#define ACP_ERROR_INTR_MASK5 0X0001AF0
/* Registers from ACP_AUDIO_BUFFERS block */
-#define ACP_I2S_RX_RINGBUFADDR 0x0002000
-#define ACP_I2S_RX_RINGBUFSIZE 0x0002004
-#define ACP_I2S_RX_LINKPOSITIONCNTR 0x0002008
-#define ACP_I2S_RX_FIFOADDR 0x000200C
-#define ACP_I2S_RX_FIFOSIZE 0x0002010
-#define ACP_I2S_RX_DMA_SIZE 0x0002014
-#define ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x0002018
-#define ACP_I2S_RX_LINEARPOSITIONCNTR_LOW 0x000201C
-#define ACP_I2S_RX_INTR_WATERMARK_SIZE 0x0002020
-#define ACP_I2S_TX_RINGBUFADDR 0x0002024
-#define ACP_I2S_TX_RINGBUFSIZE 0x0002028
-#define ACP_I2S_TX_LINKPOSITIONCNTR 0x000202C
-#define ACP_I2S_TX_FIFOADDR 0x0002030
-#define ACP_I2S_TX_FIFOSIZE 0x0002034
-#define ACP_I2S_TX_DMA_SIZE 0x0002038
-#define ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x000203C
-#define ACP_I2S_TX_LINEARPOSITIONCNTR_LOW 0x0002040
-#define ACP_I2S_TX_INTR_WATERMARK_SIZE 0x0002044
-#define ACP_BT_RX_RINGBUFADDR 0x0002048
-#define ACP_BT_RX_RINGBUFSIZE 0x000204C
-#define ACP_BT_RX_LINKPOSITIONCNTR 0x0002050
-#define ACP_BT_RX_FIFOADDR 0x0002054
-#define ACP_BT_RX_FIFOSIZE 0x0002058
-#define ACP_BT_RX_DMA_SIZE 0x000205C
-#define ACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x0002060
-#define ACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x0002064
-#define ACP_BT_RX_INTR_WATERMARK_SIZE 0x0002068
-#define ACP_BT_TX_RINGBUFADDR 0x000206C
-#define ACP_BT_TX_RINGBUFSIZE 0x0002070
-#define ACP_BT_TX_LINKPOSITIONCNTR 0x0002074
-#define ACP_BT_TX_FIFOADDR 0x0002078
-#define ACP_BT_TX_FIFOSIZE 0x000207C
-#define ACP_BT_TX_DMA_SIZE 0x0002080
-#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x0002084
-#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x0002088
-#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x000208C
-#define ACP_HS_RX_RINGBUFADDR 0x0002090
-#define ACP_HS_RX_RINGBUFSIZE 0x0002094
-#define ACP_HS_RX_LINKPOSITIONCNTR 0x0002098
-#define ACP_HS_RX_FIFOADDR 0x000209C
-#define ACP_HS_RX_FIFOSIZE 0x00020A0
-#define ACP_HS_RX_DMA_SIZE 0x00020A4
-#define ACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x00020A8
-#define ACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x00020AC
-#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x00020B0
-#define ACP_HS_TX_RINGBUFADDR 0x00020B4
-#define ACP_HS_TX_RINGBUFSIZE 0x00020B8
-#define ACP_HS_TX_LINKPOSITIONCNTR 0x00020BC
-#define ACP_HS_TX_FIFOADDR 0x00020C0
-#define ACP_HS_TX_FIFOSIZE 0x00020C4
-#define ACP_HS_TX_DMA_SIZE 0x00020C8
-#define ACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x00020CC
-#define ACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x00020D0
-#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x00020D4
-#define ACP_AUDIO_RX_RINGBUFADDR ACP_I2S_RX_RINGBUFADDR
-#define ACP_AUDIO_RX_RINGBUFSIZE ACP_I2S_RX_RINGBUFSIZE
-#define ACP_AUDIO_RX_LINKPOSITIONCNTR ACP_I2S_RX_LINKPOSITIONCNTR
-#define ACP_AUDIO_RX_FIFOADDR ACP_I2S_RX_FIFOADDR
-#define ACP_AUDIO_RX_FIFOSIZE ACP_I2S_RX_FIFOSIZE
-#define ACP_AUDIO_RX_DMA_SIZE ACP_I2S_RX_DMA_SIZE
-#define ACP_AUDIO_RX_LINEARPOSITIONCNTR_HIGH ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH
-#define ACP_AUDIO_RX_LINEARPOSITIONCNTR_LOW ACP_I2S_RX_LINEARPOSITIONCNTR_LOW
-#define ACP_AUDIO_RX_INTR_WATERMARK_SIZE ACP_I2S_RX_INTR_WATERMARK_SIZE
-#define ACP_AUDIO_TX_RINGBUFADDR ACP_I2S_TX_RINGBUFADDR
-#define ACP_AUDIO_TX_RINGBUFSIZE ACP_I2S_TX_RINGBUFSIZE
-#define ACP_AUDIO_TX_LINKPOSITIONCNTR ACP_I2S_TX_LINKPOSITIONCNTR
-#define ACP_AUDIO_TX_FIFOADDR ACP_I2S_TX_FIFOADDR
-#define ACP_AUDIO_TX_FIFOSIZE ACP_I2S_TX_FIFOSIZE
-#define ACP_AUDIO_TX_DMA_SIZE ACP_I2S_TX_DMA_SIZE
-#define ACP_AUDIO_TX_LINEARPOSITIONCNTR_HIGH ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH
-#define ACP_AUDIO_TX_LINEARPOSITIONCNTR_LOW ACP_I2S_TX_LINEARPOSITIONCNTR_LOW
-#define ACP_AUDIO_TX_INTR_WATERMARK_SIZE ACP_I2S_TX_INTR_WATERMARK_SIZE
+#define ACP_AUDIO0_RX_RINGBUFADDR 0x0002000
+#define ACP_AUDIO0_RX_RINGBUFSIZE 0x0002004
+#define ACP_AUDIO0_RX_LINKPOSITIONCNTR 0x0002008
+#define ACP_AUDIO0_RX_FIFOADDR 0x000200C
+#define ACP_AUDIO0_RX_FIFOSIZE 0x0002010
+#define ACP_AUDIO0_RX_DMA_SIZE 0x0002014
+#define ACP_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH 0x0002018
+#define ACP_AUDIO0_RX_LINEARPOSITIONCNTR_LOW 0x000201C
+#define ACP_AUDIO0_RX_INTR_WATERMARK_SIZE 0x0002020
+#define ACP_AUDIO0_TX_RINGBUFADDR 0x0002024
+#define ACP_AUDIO0_TX_RINGBUFSIZE 0x0002028
+#define ACP_AUDIO0_TX_LINKPOSITIONCNTR 0x000202C
+#define ACP_AUDIO0_TX_FIFOADDR 0x0002030
+#define ACP_AUDIO0_TX_FIFOSIZE 0x0002034
+#define ACP_AUDIO0_TX_DMA_SIZE 0x0002038
+#define ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH 0x000203C
+#define ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW 0x0002040
+#define ACP_AUDIO0_TX_INTR_WATERMARK_SIZE 0x0002044
+#define ACP_AUDIO1_RX_RINGBUFADDR 0x0002048
+#define ACP_AUDIO1_RX_RINGBUFSIZE 0x000204C
+#define ACP_AUDIO1_RX_LINKPOSITIONCNTR 0x0002050
+#define ACP_AUDIO1_RX_FIFOADDR 0x0002054
+#define ACP_AUDIO1_RX_FIFOSIZE 0x0002058
+#define ACP_AUDIO1_RX_DMA_SIZE 0x000205C
+#define ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH 0x0002060
+#define ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW 0x0002064
+#define ACP_AUDIO1_RX_INTR_WATERMARK_SIZE 0x0002068
+#define ACP_AUDIO1_TX_RINGBUFADDR 0x000206C
+#define ACP_AUDIO1_TX_RINGBUFSIZE 0x0002070
+#define ACP_AUDIO1_TX_LINKPOSITIONCNTR 0x0002074
+#define ACP_AUDIO1_TX_FIFOADDR 0x0002078
+#define ACP_AUDIO1_TX_FIFOSIZE 0x000207C
+#define ACP_AUDIO1_TX_DMA_SIZE 0x0002080
+#define ACP_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH 0x0002084
+#define ACP_AUDIO1_TX_LINEARPOSITIONCNTR_LOW 0x0002088
+#define ACP_AUDIO1_TX_INTR_WATERMARK_SIZE 0x000208C
+#define ACP_AUDIO2_RX_RINGBUFADDR 0x0002090
+#define ACP_AUDIO2_RX_RINGBUFSIZE 0x0002094
+#define ACP_AUDIO2_RX_LINKPOSITIONCNTR 0x0002098
+#define ACP_AUDIO2_RX_FIFOADDR 0x000209C
+#define ACP_AUDIO2_RX_FIFOSIZE 0x00020A0
+#define ACP_AUDIO2_RX_DMA_SIZE 0x00020A4
+#define ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH 0x00020A8
+#define ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW 0x00020AC
+#define ACP_AUDIO2_RX_INTR_WATERMARK_SIZE 0x00020B0
+#define ACP_AUDIO2_TX_RINGBUFADDR 0x00020B4
+#define ACP_AUDIO2_TX_RINGBUFSIZE 0x00020B8
+#define ACP_AUDIO2_TX_LINKPOSITIONCNTR 0x00020BC
+#define ACP_AUDIO2_TX_FIFOADDR 0x00020C0
+#define ACP_AUDIO2_TX_FIFOSIZE 0x00020C4
+#define ACP_AUDIO2_TX_DMA_SIZE 0x00020C8
+#define ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH 0x00020CC
+#define ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW 0x00020D0
+#define ACP_AUDIO2_TX_INTR_WATERMARK_SIZE 0x00020D4
/* Registers from ACP_I2S_TDM block */
#define ACP_I2STDM_IER 0x0002400
@@ -292,367 +274,222 @@
#define ACP_WOV_ERROR_STATUS_REGISTER 0x0002C68
#define ACP_PDM_CLKDIV 0x0002C6C
-/* Registers from ACP_SW_SWCLK block */
-#define ACP_SW_EN 0x0003000
-#define ACP_SW_EN_STATUS 0x0003004
-#define ACP_SW_FRAMESIZE 0x0003008
-#define ACP_SW_SSP_COUNTER 0x000300C
-#define ACP_SW_AUDIO_TX_EN 0x0003010
-#define ACP_SW_AUDIO_TX_EN_STATUS 0x0003014
-#define ACP_SW_AUDIO_TX_FRAME_FORMAT 0x0003018
-#define ACP_SW_AUDIO_TX_SAMPLEINTERVAL 0x000301C
-#define ACP_SW_AUDIO_TX_HCTRL_DP0 0x0003020
-#define ACP_SW_AUDIO_TX_HCTRL_DP1 0x0003024
-#define ACP_SW_AUDIO_TX_HCTRL_DP2 0x0003028
-#define ACP_SW_AUDIO_TX_HCTRL_DP3 0x000302C
-#define ACP_SW_AUDIO_TX_OFFSET_DP0 0x0003030
-#define ACP_SW_AUDIO_TX_OFFSET_DP1 0x0003034
-#define ACP_SW_AUDIO_TX_OFFSET_DP2 0x0003038
-#define ACP_SW_AUDIO_TX_OFFSET_DP3 0x000303C
-#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP0 0x0003040
-#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP1 0x0003044
-#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP2 0x0003048
-#define ACP_SW_AUDIO_TX_CHANNEL_ENABLE_DP3 0x000304C
-#define ACP_SW_BT_TX_EN 0x0003050
-#define ACP_SW_BT_TX_EN_STATUS 0x0003054
-#define ACP_SW_BT_TX_FRAME_FORMAT 0x0003058
-#define ACP_SW_BT_TX_SAMPLEINTERVAL 0x000305C
-#define ACP_SW_BT_TX_HCTRL 0x0003060
-#define ACP_SW_BT_TX_OFFSET 0x0003064
-#define ACP_SW_BT_TX_CHANNEL_ENABLE_DP0 0x0003068
-#define ACP_SW_HEADSET_TX_EN 0x000306C
-#define ACP_SW_HEADSET_TX_EN_STATUS 0x0003070
-#define ACP_SW_HEADSET_TX_FRAME_FORMAT 0x0003074
-#define ACP_SW_HEADSET_TX_SAMPLEINTERVAL 0x0003078
-#define ACP_SW_HEADSET_TX_HCTRL 0x000307C
-#define ACP_SW_HEADSET_TX_OFFSET 0x0003080
-#define ACP_SW_HEADSET_TX_CHANNEL_ENABLE_DP0 0x0003084
-#define ACP_SW_AUDIO_RX_EN 0x0003088
-#define ACP_SW_AUDIO_RX_EN_STATUS 0x000308C
-#define ACP_SW_AUDIO_RX_FRAME_FORMAT 0x0003090
-#define ACP_SW_AUDIO_RX_SAMPLEINTERVAL 0x0003094
-#define ACP_SW_AUDIO_RX_HCTRL_DP0 0x0003098
-#define ACP_SW_AUDIO_RX_HCTRL_DP1 0x000309C
-#define ACP_SW_AUDIO_RX_HCTRL_DP2 0x0003100
-#define ACP_SW_AUDIO_RX_HCTRL_DP3 0x0003104
-#define ACP_SW_AUDIO_RX_OFFSET_DP0 0x0003108
-#define ACP_SW_AUDIO_RX_OFFSET_DP1 0x000310C
-#define ACP_SW_AUDIO_RX_OFFSET_DP2 0x0003110
-#define ACP_SW_AUDIO_RX_OFFSET_DP3 0x0003114
-#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP0 0x0003118
-#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP1 0x000311C
-#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP2 0x0003120
-#define ACP_SW_AUDIO_RX_CHANNEL_ENABLE_DP3 0x0003124
-#define ACP_SW_BT_RX_EN 0x0003128
-#define ACP_SW_BT_RX_EN_STATUS 0x000312C
-#define ACP_SW_BT_RX_FRAME_FORMAT 0x0003130
-#define ACP_SW_BT_RX_SAMPLEINTERVAL 0x0003134
-#define ACP_SW_BT_RX_HCTRL 0x0003138
-#define ACP_SW_BT_RX_OFFSET 0x000313C
-#define ACP_SW_BT_RX_CHANNEL_ENABLE_DP0 0x0003140
-#define ACP_SW_HEADSET_RX_EN 0x0003144
-#define ACP_SW_HEADSET_RX_EN_STATUS 0x0003148
-#define ACP_SW_HEADSET_RX_FRAME_FORMAT 0x000314C
-#define ACP_SW_HEADSET_RX_SAMPLEINTERVAL 0x0003150
-#define ACP_SW_HEADSET_RX_HCTRL 0x0003154
-#define ACP_SW_HEADSET_RX_OFFSET 0x0003158
-#define ACP_SW_HEADSET_RX_CHANNEL_ENABLE_DP0 0x000315C
-#define ACP_SW_BPT_PORT_EN 0x0003160
-#define ACP_SW_BPT_PORT_EN_STATUS 0x0003164
-#define ACP_SW_BPT_PORT_FRAME_FORMAT 0x0003168
-#define ACP_SW_BPT_PORT_SAMPLEINTERVAL 0x000316C
-#define ACP_SW_BPT_PORT_HCTRL 0x0003170
-#define ACP_SW_BPT_PORT_OFFSET 0x0003174
-#define ACP_SW_BPT_PORT_CHANNEL_ENABLE 0x0003178
-#define ACP_SW_BPT_PORT_FIRST_BYTE_ADDR 0x000317C
-#define ACP_SW_CLK_RESUME_CTRL 0x0003180
-#define ACP_SW_CLK_RESUME_DELAY_CNTR 0x0003184
-#define ACP_SW_BUS_RESET_CTRL 0x0003188
-#define ACP_SW_PRBS_ERR_STATUS 0x000318C
-#define SW_IMM_CMD_UPPER_WORD 0x0003230
-#define SW_IMM_CMD_LOWER_QWORD 0x0003234
-#define SW_IMM_RESP_UPPER_WORD 0x0003238
-#define SW_IMM_RESP_LOWER_QWORD 0x000323C
-#define SW_IMM_CMD_STS 0x0003240
-#define SW_BRA_BASE_ADDRESS 0x0003244
-#define SW_BRA_TRANSFER_SIZE 0x0003248
-#define SW_BRA_DMA_BUSY 0x000324C
-#define SW_BRA_RESP 0x0003250
-#define SW_BRA_RESP_FRAME_ADDR 0x0003254
-#define SW_BRA_CURRENT_TRANSFER_SIZE 0x0003258
-#define SW_STATE_CHANGE_STATUS_0TO7 0x000325C
-#define SW_STATE_CHANGE_STATUS_8TO11 0x0003260
-#define SW_STATE_CHANGE_STATUS_MASK_0TO7 0x0003264
-#define SW_STATE_CHANGE_STATUS_MASK_8TO11 0x0003268
-#define SW_CLK_FREQUENCY_CTRL 0x000326C
-#define SW_ERROR_INTR_MASK 0x0003270
-#define SW_PHY_TEST_MODE_DATA_OFF 0x0003274
+/* Registers from ACP_SW0_SWCLK block */
+#define ACP_SW0_EN 0x0003000
+#define ACP_SW0_EN_STATUS 0x0003004
+#define ACP_SW0_FRAMESIZE 0x0003008
+#define ACP_SW0_SSP_COUNTER 0x000300C
+#define ACP_SW0_AUDIO0_TX_EN 0x0003010
+#define ACP_SW0_AUDIO0_TX_EN_STATUS 0x0003014
+#define ACP_SW0_AUDIO0_TX_FRAME_FORMAT 0x0003018
+#define ACP_SW0_AUDIO0_TX_SAMPLEINTERVAL 0x000301C
+#define ACP_SW0_AUDIO0_TX_HCTRL_DP0 0x0003020
+#define ACP_SW0_AUDIO0_TX_HCTRL_DP1 0x0003024
+#define ACP_SW0_AUDIO0_TX_HCTRL_DP2 0x0003028
+#define ACP_SW0_AUDIO0_TX_HCTRL_DP3 0x000302C
+#define ACP_SW0_AUDIO0_TX_OFFSET_DP0 0x0003030
+#define ACP_SW0_AUDIO0_TX_OFFSET_DP1 0x0003034
+#define ACP_SW0_AUDIO0_TX_OFFSET_DP2 0x0003038
+#define ACP_SW0_AUDIO0_TX_OFFSET_DP3 0x000303C
+#define ACP_SW0_AUDIO0_TX_CHANNEL_ENABLE_DP0 0x0003040
+#define ACP_SW0_AUDIO0_TX_CHANNEL_ENABLE_DP1 0x0003044
+#define ACP_SW0_AUDIO0_TX_CHANNEL_ENABLE_DP2 0x0003048
+#define ACP_SW0_AUDIO0_TX_CHANNEL_ENABLE_DP3 0x000304C
+#define ACP_SW0_AUDIO1_TX_EN 0x0003050
+#define ACP_SW0_AUDIO1_TX_EN_STATUS 0x0003054
+#define ACP_SW0_AUDIO1_TX_FRAME_FORMAT 0x0003058
+#define ACP_SW0_AUDIO1_TX_SAMPLEINTERVAL 0x000305C
+#define ACP_SW0_AUDIO1_TX_HCTRL 0x0003060
+#define ACP_SW0_AUDIO1_TX_OFFSET 0x0003064
+#define ACP_SW0_AUDIO1_TX_CHANNEL_ENABLE_DP0 0x0003068
+#define ACP_SW0_AUDIO2_TX_EN 0x000306C
+#define ACP_SW0_AUDIO2_TX_EN_STATUS 0x0003070
+#define ACP_SW0_AUDIO2_TX_FRAME_FORMAT 0x0003074
+#define ACP_SW0_AUDIO2_TX_SAMPLEINTERVAL 0x0003078
+#define ACP_SW0_AUDIO2_TX_HCTRL 0x000307C
+#define ACP_SW0_AUDIO2_TX_OFFSET 0x0003080
+#define ACP_SW0_AUDIO2_TX_CHANNEL_ENABLE_DP0 0x0003084
+#define ACP_SW0_AUDIO0_RX_EN 0x0003088
+#define ACP_SW0_AUDIO0_RX_EN_STATUS 0x000308C
+#define ACP_SW0_AUDIO0_RX_FRAME_FORMAT 0x0003090
+#define ACP_SW0_AUDIO0_RX_SAMPLEINTERVAL 0x0003094
+#define ACP_SW0_AUDIO0_RX_HCTRL_DP0 0x0003098
+#define ACP_SW0_AUDIO0_RX_HCTRL_DP1 0x000309C
+#define ACP_SW0_AUDIO0_RX_HCTRL_DP2 0x0003100
+#define ACP_SW0_AUDIO0_RX_HCTRL_DP3 0x0003104
+#define ACP_SW0_AUDIO0_RX_OFFSET_DP0 0x0003108
+#define ACP_SW0_AUDIO0_RX_OFFSET_DP1 0x000310C
+#define ACP_SW0_AUDIO0_RX_OFFSET_DP2 0x0003110
+#define ACP_SW0_AUDIO0_RX_OFFSET_DP3 0x0003114
+#define ACP_SW0_AUDIO0_RX_CHANNEL_ENABLE_DP0 0x0003118
+#define ACP_SW0_AUDIO0_RX_CHANNEL_ENABLE_DP1 0x000311C
+#define ACP_SW0_AUDIO0_RX_CHANNEL_ENABLE_DP2 0x0003120
+#define ACP_SW0_AUDIO0_RX_CHANNEL_ENABLE_DP3 0x0003124
+#define ACP_SW0_AUDIO1_RX_EN 0x0003128
+#define ACP_SW0_AUDIO1_RX_EN_STATUS 0x000312C
+#define ACP_SW0_AUDIO1_RX_FRAME_FORMAT 0x0003130
+#define ACP_SW0_AUDIO1_RX_SAMPLEINTERVAL 0x0003134
+#define ACP_SW0_AUDIO1_RX_HCTRL 0x0003138
+#define ACP_SW0_AUDIO1_RX_OFFSET 0x000313C
+#define ACP_SW0_AUDIO1_RX_CHANNEL_ENABLE_DP0 0x0003140
+#define ACP_SW0_AUDIO2_RX_EN 0x0003144
+#define ACP_SW0_AUDIO2_RX_EN_STATUS 0x0003148
+#define ACP_SW0_AUDIO2_RX_FRAME_FORMAT 0x000314C
+#define ACP_SW0_AUDIO2_RX_SAMPLEINTERVAL 0x0003150
+#define ACP_SW0_AUDIO2_RX_HCTRL 0x0003154
+#define ACP_SW0_AUDIO2_RX_OFFSET 0x0003158
+#define ACP_SW0_AUDIO2_RX_CHANNEL_ENABLE_DP0 0x000315C
+#define ACP_SW0_BPT_PORT_EN 0x0003160
+#define ACP_SW0_BPT_PORT_EN_STATUS 0x0003164
+#define ACP_SW0_BPT_PORT_FRAME_FORMAT 0x0003168
+#define ACP_SW0_BPT_PORT_SAMPLEINTERVAL 0x000316C
+#define ACP_SW0_BPT_PORT_HCTRL 0x0003170
+#define ACP_SW0_BPT_PORT_OFFSET 0x0003174
+#define ACP_SW0_BPT_PORT_CHANNEL_ENABLE 0x0003178
+#define ACP_SW0_BPT_PORT_FIRST_BYTE_ADDR 0x000317C
+#define ACP_SW0_CLK_RESUME_CTRL 0x0003180
+#define ACP_SW0_CLK_RESUME_DELAY_CNTR 0x0003184
+#define ACP_SW0_BUS_RESET_CTRL 0x0003188
+#define ACP_SW0_PRBS_ERR_STATUS 0x000318C
+#define ACP_SW0_IMM_CMD_UPPER_WORD 0x0003230
+#define ACP_SW0_IMM_CMD_LOWER_QWORD 0x0003234
+#define ACP_SW0_IMM_RESP_UPPER_WORD 0x0003238
+#define ACP_SW0_IMM_RESP_LOWER_QWORD 0x000323C
+#define ACP_SW0_IMM_CMD_STS 0x0003240
+#define ACP_SW0_BRA_BASE_ADDRESS 0x0003244
+#define ACP_SW0_BRA_TRANSFER_SIZE 0x0003248
+#define ACP_SW0_BRA_DMA_BUSY 0x000324C
+#define ACP_SW0_BRA_RESP 0x0003250
+#define ACP_SW0_BRA_RESP_FRAME_ADDR 0x0003254
+#define ACP_SW0_BRA_CURRENT_TRANSFER_SIZE 0x0003258
+#define ACP_SW0_STATECHANGE_STATUS_0TO7 0x000325C
+#define ACP_SW0_STATECHANGE_STATUS_8TO11 0x0003260
+#define ACP_SW0_STATECHANGE_STATUS_MASK_0TO7 0x0003264
+#define ACP_SW0_STATECHANGE_STATUS_MASK_8TO11 0x0003268
+#define ACP_SW0_CLK_FREQUENCY_CTRL 0x000326C
+#define ACP_SW0_ERROR_INTR_MASK 0x0003270
+#define ACP_SW0_PHY_TEST_MODE_DATA_OFF 0x0003274
/* Registers from ACP_P1_AUDIO_BUFFERS block */
-#define ACP_P1_I2S_RX_RINGBUFADDR 0x0003A00
-#define ACP_P1_I2S_RX_RINGBUFSIZE 0x0003A04
-#define ACP_P1_I2S_RX_LINKPOSITIONCNTR 0x0003A08
-#define ACP_P1_I2S_RX_FIFOADDR 0x0003A0C
-#define ACP_P1_I2S_RX_FIFOSIZE 0x0003A10
-#define ACP_P1_I2S_RX_DMA_SIZE 0x0003A14
-#define ACP_P1_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x0003A18
-#define ACP_P1_I2S_RX_LINEARPOSITIONCNTR_LOW 0x0003A1C
-#define ACP_P1_I2S_RX_INTR_WATERMARK_SIZE 0x0003A20
-#define ACP_P1_I2S_TX_RINGBUFADDR 0x0003A24
-#define ACP_P1_I2S_TX_RINGBUFSIZE 0x0003A28
-#define ACP_P1_I2S_TX_LINKPOSITIONCNTR 0x0003A2C
-#define ACP_P1_I2S_TX_FIFOADDR 0x0003A30
-#define ACP_P1_I2S_TX_FIFOSIZE 0x0003A34
-#define ACP_P1_I2S_TX_DMA_SIZE 0x0003A38
-#define ACP_P1_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x0003A3C
-#define ACP_P1_I2S_TX_LINEARPOSITIONCNTR_LOW 0x0003A40
-#define ACP_P1_I2S_TX_INTR_WATERMARK_SIZE 0x0003A44
-#define ACP_P1_BT_RX_RINGBUFADDR 0x0003A48
-#define ACP_P1_BT_RX_RINGBUFSIZE 0x0003A4C
-#define ACP_P1_BT_RX_LINKPOSITIONCNTR 0x0003A50
-#define ACP_P1_BT_RX_FIFOADDR 0x0003A54
-#define ACP_P1_BT_RX_FIFOSIZE 0x0003A58
-#define ACP_P1_BT_RX_DMA_SIZE 0x0003A5C
-#define ACP_P1_BT_RX_LINEARPOSITIONCNTR_HIGH 0x0003A60
-#define ACP_P1_BT_RX_LINEARPOSITIONCNTR_LOW 0x0003A64
-#define ACP_P1_BT_RX_INTR_WATERMARK_SIZE 0x0003A68
-#define ACP_P1_BT_TX_RINGBUFADDR 0x0003A6C
-#define ACP_P1_BT_TX_RINGBUFSIZE 0x0003A70
-#define ACP_P1_BT_TX_LINKPOSITIONCNTR 0x0003A74
-#define ACP_P1_BT_TX_FIFOADDR 0x0003A78
-#define ACP_P1_BT_TX_FIFOSIZE 0x0003A7C
-#define ACP_P1_BT_TX_DMA_SIZE 0x0003A80
-#define ACP_P1_BT_TX_LINEARPOSITIONCNTR_HIGH 0x0003A84
-#define ACP_P1_BT_TX_LINEARPOSITIONCNTR_LOW 0x0003A88
-#define ACP_P1_BT_TX_INTR_WATERMARK_SIZE 0x0003A8C
-#define ACP_P1_HS_RX_RINGBUFADDR 0x0003A90
-#define ACP_P1_HS_RX_RINGBUFSIZE 0x0003A94
-#define ACP_P1_HS_RX_LINKPOSITIONCNTR 0x0003A98
-#define ACP_P1_HS_RX_FIFOADDR 0x0003A9C
-#define ACP_P1_HS_RX_FIFOSIZE 0x0003AA0
-#define ACP_P1_HS_RX_DMA_SIZE 0x0003AA4
-#define ACP_P1_HS_RX_LINEARPOSITIONCNTR_HIGH 0x0003AA8
-#define ACP_P1_HS_RX_LINEARPOSITIONCNTR_LOW 0x0003AAC
-#define ACP_P1_HS_RX_INTR_WATERMARK_SIZE 0x0003AB0
-#define ACP_P1_HS_TX_RINGBUFADDR 0x0003AB4
-#define ACP_P1_HS_TX_RINGBUFSIZE 0x0003AB8
-#define ACP_P1_HS_TX_LINKPOSITIONCNTR 0x0003ABC
-#define ACP_P1_HS_TX_FIFOADDR 0x0003AC0
-#define ACP_P1_HS_TX_FIFOSIZE 0x0003AC4
-#define ACP_P1_HS_TX_DMA_SIZE 0x0003AC8
-#define ACP_P1_HS_TX_LINEARPOSITIONCNTR_HIGH 0x0003ACC
-#define ACP_P1_HS_TX_LINEARPOSITIONCNTR_LOW 0x0003AD0
-#define ACP_P1_HS_TX_INTR_WATERMARK_SIZE 0x0003AD4
-#define ACP_P1_AUDIO_RX_RINGBUFADDR ACP_P1_I2S_RX_RINGBUFADDR
-#define ACP_P1_AUDIO_RX_RINGBUFSIZE ACP_P1_I2S_RX_RINGBUFSIZE
-#define ACP_P1_AUDIO_RX_LINKPOSITIONCNTR ACP_P1_I2S_RX_LINKPOSITIONCNTR
-#define ACP_P1_AUDIO_RX_FIFOADDR ACP_P1_I2S_RX_FIFOADDR
-#define ACP_P1_AUDIO_RX_FIFOSIZE ACP_P1_I2S_RX_FIFOSIZE
-#define ACP_P1_AUDIO_RX_DMA_SIZE ACP_P1_I2S_RX_DMA_SIZE
-#define ACP_P1_AUDIO_RX_LINEARPOSITIONCNTR_HIGH ACP_P1_I2S_RX_LINEARPOSITIONCNTR_HIGH
-#define ACP_P1_AUDIO_RX_LINEARPOSITIONCNTR_LOW ACP_P1_I2S_RX_LINEARPOSITIONCNTR_LOW
-#define ACP_P1_AUDIO_RX_INTR_WATERMARK_SIZE ACP_P1_I2S_RX_INTR_WATERMARK_SIZE
-#define ACP_P1_AUDIO_TX_RINGBUFADDR ACP_P1_I2S_TX_RINGBUFADDR
-#define ACP_P1_AUDIO_TX_RINGBUFSIZE ACP_P1_I2S_TX_RINGBUFSIZE
-#define ACP_P1_AUDIO_TX_LINKPOSITIONCNTR ACP_P1_I2S_TX_LINKPOSITIONCNTR
-#define ACP_P1_AUDIO_TX_FIFOADDR ACP_P1_I2S_TX_FIFOADDR
-#define ACP_P1_AUDIO_TX_FIFOSIZE ACP_P1_I2S_TX_FIFOSIZE
-#define ACP_P1_AUDIO_TX_DMA_SIZE ACP_P1_I2S_TX_DMA_SIZE
-#define ACP_P1_AUDIO_TX_LINEARPOSITIONCNTR_HIGH ACP_P1_I2S_TX_LINEARPOSITIONCNTR_HIGH
-#define ACP_P1_AUDIO_TX_LINEARPOSITIONCNTR_LOW ACP_P1_I2S_TX_LINEARPOSITIONCNTR_LOW
-#define ACP_P1_AUDIO_TX_INTR_WATERMARK_SIZE ACP_P1_I2S_TX_INTR_WATERMARK_SIZE
+#define ACP_P1_AUDIO0_RX_RINGBUFADDR 0x0003A00
+#define ACP_P1_AUDIO0_RX_RINGBUFSIZE 0x0003A04
+#define ACP_P1_AUDIO0_RX_LINKPOSITIONCNTR 0x0003A08
+#define ACP_P1_AUDIO0_RX_FIFOADDR 0x0003A0C
+#define ACP_P1_AUDIO0_RX_FIFOSIZE 0x0003A10
+#define ACP_P1_AUDIO0_RX_DMA_SIZE 0x0003A14
+#define ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH 0x0003A18
+#define ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_LOW 0x0003A1C
+#define ACP_P1_AUDIO0_RX_INTR_WATERMARK_SIZE 0x0003A20
+#define ACP_P1_AUDIO0_TX_RINGBUFADDR 0x0003A24
+#define ACP_P1_AUDIO0_TX_RINGBUFSIZE 0x0003A28
+#define ACP_P1_AUDIO0_TX_LINKPOSITIONCNTR 0x0003A2C
+#define ACP_P1_AUDIO0_TX_FIFOADDR 0x0003A30
+#define ACP_P1_AUDIO0_TX_FIFOSIZE 0x0003A34
+#define ACP_P1_AUDIO0_TX_DMA_SIZE 0x0003A38
+#define ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH 0x0003A3C
+#define ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_LOW 0x0003A40
+#define ACP_P1_AUDIO0_TX_INTR_WATERMARK_SIZE 0x0003A44
+#define ACP_P1_AUDIO1_RX_RINGBUFADDR 0x0003A48
+#define ACP_P1_AUDIO1_RX_RINGBUFSIZE 0x0003A4C
+#define ACP_P1_AUDIO1_RX_LINKPOSITIONCNTR 0x0003A50
+#define ACP_P1_AUDIO1_RX_FIFOADDR 0x0003A54
+#define ACP_P1_AUDIO1_RX_FIFOSIZE 0x0003A58
+#define ACP_P1_AUDIO1_RX_DMA_SIZE 0x0003A5C
+#define ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH 0x0003A60
+#define ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW 0x0003A64
+#define ACP_P1_AUDIO1_RX_INTR_WATERMARK_SIZE 0x0003A68
+#define ACP_P1_AUDIO1_TX_RINGBUFADDR 0x0003A6C
+#define ACP_P1_AUDIO1_TX_RINGBUFSIZE 0x0003A70
+#define ACP_P1_AUDIO1_TX_LINKPOSITIONCNTR 0x0003A74
+#define ACP_P1_AUDIO1_TX_FIFOADDR 0x0003A78
+#define ACP_P1_AUDIO1_TX_FIFOSIZE 0x0003A7C
+#define ACP_P1_AUDIO1_TX_DMA_SIZE 0x0003A80
+#define ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH 0x0003A84
+#define ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_LOW 0x0003A88
+#define ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE 0x0003A8C
+#define ACP_P1_AUDIO2_RX_RINGBUFADDR 0x0003A90
+#define ACP_P1_AUDIO2_RX_RINGBUFSIZE 0x0003A94
+#define ACP_P1_AUDIO2_RX_LINKPOSITIONCNTR 0x0003A98
+#define ACP_P1_AUDIO2_RX_FIFOADDR 0x0003A9C
+#define ACP_P1_AUDIO2_RX_FIFOSIZE 0x0003AA0
+#define ACP_P1_AUDIO2_RX_DMA_SIZE 0x0003AA4
+#define ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH 0x0003AA8
+#define ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_LOW 0x0003AAC
+#define ACP_P1_AUDIO2_RX_INTR_WATERMARK_SIZE 0x0003AB0
+#define ACP_P1_AUDIO2_TX_RINGBUFADDR 0x0003AB4
+#define ACP_P1_AUDIO2_TX_RINGBUFSIZE 0x0003AB8
+#define ACP_P1_AUDIO2_TX_LINKPOSITIONCNTR 0x0003ABC
+#define ACP_P1_AUDIO2_TX_FIFOADDR 0x0003AC0
+#define ACP_P1_AUDIO2_TX_FIFOSIZE 0x0003AC4
+#define ACP_P1_AUDIO2_TX_DMA_SIZE 0x0003AC8
+#define ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH 0x0003ACC
+#define ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_LOW 0x0003AD0
+#define ACP_P1_AUDIO2_TX_INTR_WATERMARK_SIZE 0x0003AD4
-/* Registers from ACP_P1_SW_SWCLK block */
-#define ACP_P1_SW_EN 0x0003C00
-#define ACP_P1_SW_EN_STATUS 0x0003C04
-#define ACP_P1_SW_FRAMESIZE 0x0003C08
-#define ACP_P1_SW_SSP_COUNTER 0x0003C0C
-#define ACP_P1_SW_BT_TX_EN 0x0003C50
-#define ACP_P1_SW_BT_TX_EN_STATUS 0x0003C54
-#define ACP_P1_SW_BT_TX_FRAME_FORMAT 0x0003C58
-#define ACP_P1_SW_BT_TX_SAMPLEINTERVAL 0x0003C5C
-#define ACP_P1_SW_BT_TX_HCTRL 0x0003C60
-#define ACP_P1_SW_BT_TX_OFFSET 0x0003C64
-#define ACP_P1_SW_BT_TX_CHANNEL_ENABLE_DP0 0x0003C68
-#define ACP_P1_SW_BT_RX_EN 0x0003D28
-#define ACP_P1_SW_BT_RX_EN_STATUS 0x0003D2C
-#define ACP_P1_SW_BT_RX_FRAME_FORMAT 0x0003D30
-#define ACP_P1_SW_BT_RX_SAMPLEINTERVAL 0x0003D34
-#define ACP_P1_SW_BT_RX_HCTRL 0x0003D38
-#define ACP_P1_SW_BT_RX_OFFSET 0x0003D3C
-#define ACP_P1_SW_BT_RX_CHANNEL_ENABLE_DP0 0x0003D40
-#define ACP_P1_SW_BPT_PORT_EN 0x0003D60
-#define ACP_P1_SW_BPT_PORT_EN_STATUS 0x0003D64
-#define ACP_P1_SW_BPT_PORT_FRAME_FORMAT 0x0003D68
-#define ACP_P1_SW_BPT_PORT_SAMPLEINTERVAL 0x0003D6C
-#define ACP_P1_SW_BPT_PORT_HCTRL 0x0003D70
-#define ACP_P1_SW_BPT_PORT_OFFSET 0x0003D74
-#define ACP_P1_SW_BPT_PORT_CHANNEL_ENABLE 0x0003D78
-#define ACP_P1_SW_BPT_PORT_FIRST_BYTE_ADDR 0x0003D7C
-#define ACP_P1_SW_CLK_RESUME_CTRL 0x0003D80
-#define ACP_P1_SW_CLK_RESUME_DELAY_CNTR 0x0003D84
-#define ACP_P1_SW_BUS_RESET_CTRL 0x0003D88
-#define ACP_P1_SW_PRBS_ERR_STATUS 0x0003D8C
+/* Registers from ACP_SW1_SWCLK block */
+#define ACP_SW1_EN 0x0003C00
+#define ACP_SW1_EN_STATUS 0x0003C04
+#define ACP_SW1_FRAMESIZE 0x0003C08
+#define ACP_SW1_SSP_COUNTER 0x0003C0C
+#define ACP_SW1_AUDIO1_TX_EN 0x0003C50
+#define ACP_SW1_AUDIO1_TX_EN_STATUS 0x0003C54
+#define ACP_SW1_AUDIO1_TX_FRAME_FORMAT 0x0003C58
+#define ACP_SW1_AUDIO1_TX_SAMPLEINTERVAL 0x0003C5C
+#define ACP_SW1_AUDIO1_TX_HCTRL 0x0003C60
+#define ACP_SW1_AUDIO1_TX_OFFSET 0x0003C64
+#define ACP_SW1_AUDIO1_TX_CHANNEL_ENABLE_DP0 0x0003C68
+#define ACP_SW1_AUDIO1_RX_EN 0x0003D28
+#define ACP_SW1_AUDIO1_RX_EN_STATUS 0x0003D2C
+#define ACP_SW1_AUDIO1_RX_FRAME_FORMAT 0x0003D30
+#define ACP_SW1_AUDIO1_RX_SAMPLEINTERVAL 0x0003D34
+#define ACP_SW1_AUDIO1_RX_HCTRL 0x0003D38
+#define ACP_SW1_AUDIO1_RX_OFFSET 0x0003D3C
+#define ACP_SW1_AUDIO1_RX_CHANNEL_ENABLE_DP0 0x0003D40
+#define ACP_SW1_BPT_PORT_EN 0x0003D60
+#define ACP_SW1_BPT_PORT_EN_STATUS 0x0003D64
+#define ACP_SW1_BPT_PORT_FRAME_FORMAT 0x0003D68
+#define ACP_SW1_BPT_PORT_SAMPLEINTERVAL 0x0003D6C
+#define ACP_SW1_BPT_PORT_HCTRL 0x0003D70
+#define ACP_SW1_BPT_PORT_OFFSET 0x0003D74
+#define ACP_SW1_BPT_PORT_CHANNEL_ENABLE 0x0003D78
+#define ACP_SW1_BPT_PORT_FIRST_BYTE_ADDR 0x0003D7C
+#define ACP_SW1_CLK_RESUME_CTRL 0x0003D80
+#define ACP_SW1_CLK_RESUME_DELAY_CNTR 0x0003D84
+#define ACP_SW1_BUS_RESET_CTRL 0x0003D88
+#define ACP_SW1_PRBS_ERR_STATUS 0x0003D8C
-/* Registers from ACP_P1_SW_ACLK block */
-#define P1_SW_CORB_BASE_ADDRESS 0x0003E00
-#define P1_SW_CORB_WRITE_POINTER 0x0003E04
-#define P1_SW_CORB_READ_POINTER 0x0003E08
-#define P1_SW_CORB_CONTROL 0x0003E0C
-#define P1_SW_CORB_SIZE 0x0003E14
-#define P1_SW_RIRB_BASE_ADDRESS 0x0003E18
-#define P1_SW_RIRB_WRITE_POINTER 0x0003E1C
-#define P1_SW_RIRB_RESPONSE_INTERRUPT_COUNT 0x0003E20
-#define P1_SW_RIRB_CONTROL 0x0003E24
-#define P1_SW_RIRB_SIZE 0x0003E28
-#define P1_SW_RIRB_FIFO_MIN_THDL 0x0003E2C
-#define P1_SW_IMM_CMD_UPPER_WORD 0x0003E30
-#define P1_SW_IMM_CMD_LOWER_QWORD 0x0003E34
-#define P1_SW_IMM_RESP_UPPER_WORD 0x0003E38
-#define P1_SW_IMM_RESP_LOWER_QWORD 0x0003E3C
-#define P1_SW_IMM_CMD_STS 0x0003E40
-#define P1_SW_BRA_BASE_ADDRESS 0x0003E44
-#define P1_SW_BRA_TRANSFER_SIZE 0x0003E48
-#define P1_SW_BRA_DMA_BUSY 0x0003E4C
-#define P1_SW_BRA_RESP 0x0003E50
-#define P1_SW_BRA_RESP_FRAME_ADDR 0x0003E54
-#define P1_SW_BRA_CURRENT_TRANSFER_SIZE 0x0003E58
-#define P1_SW_STATE_CHANGE_STATUS_0TO7 0x0003E5C
-#define P1_SW_STATE_CHANGE_STATUS_8TO11 0x0003E60
-#define P1_SW_STATE_CHANGE_STATUS_MASK_0TO7 0x0003E64
-#define P1_SW_STATE_CHANGE_STATUS_MASK_8TO11 0x0003E68
-#define P1_SW_CLK_FREQUENCY_CTRL 0x0003E6C
-#define P1_SW_ERROR_INTR_MASK 0x0003E70
-#define P1_SW_PHY_TEST_MODE_DATA_OFF 0x0003E74
+/* Registers from ACP_SW1_ACLK block */
+#define ACP_SW1_CORB_BASE_ADDRESS 0x0003E00
+#define ACP_SW1_CORB_WRITE_POINTER 0x0003E04
+#define ACP_SW1_CORB_READ_POINTER 0x0003E08
+#define ACP_SW1_CORB_CONTROL 0x0003E0C
+#define ACP_SW1_CORB_SIZE 0x0003E14
+#define ACP_SW1_RIRB_BASE_ADDRESS 0x0003E18
+#define ACP_SW1_RIRB_WRITE_POINTER 0x0003E1C
+#define ACP_SW1_RIRB_RESPONSE_INTERRUPT_COUNT 0x0003E20
+#define ACP_SW1_RIRB_CONTROL 0x0003E24
+#define ACP_SW1_RIRB_SIZE 0x0003E28
+#define ACP_SW1_RIRB_FIFO_MIN_THDL 0x0003E2C
+#define ACP_SW1_IMM_CMD_UPPER_WORD 0x0003E30
+#define ACP_SW1_IMM_CMD_LOWER_QWORD 0x0003E34
+#define ACP_SW1_IMM_RESP_UPPER_WORD 0x0003E38
+#define ACP_SW1_IMM_RESP_LOWER_QWORD 0x0003E3C
+#define ACP_SW1_IMM_CMD_STS 0x0003E40
+#define ACP_SW1_BRA_BASE_ADDRESS 0x0003E44
+#define ACP_SW1_BRA_TRANSFER_SIZE 0x0003E48
+#define ACP_SW1_BRA_DMA_BUSY 0x0003E4C
+#define ACP_SW1_BRA_RESP 0x0003E50
+#define ACP_SW1_BRA_RESP_FRAME_ADDR 0x0003E54
+#define ACP_SW1_BRA_CURRENT_TRANSFER_SIZE 0x0003E58
+#define ACP_SW1_STATECHANGE_STATUS_0TO7 0x0003E5C
+#define ACP_SW1_STATECHANGE_STATUS_8TO11 0x0003E60
+#define ACP_SW1_STATECHANGE_STATUS_MASK_0TO7 0x0003E64
+#define ACP_SW1_STATECHANGE_STATUS_MASK_8TO11 0x0003E68
+#define ACP_SW1_CLK_FREQUENCY_CTRL 0x0003E6C
+#define ACP_SW1_ERROR_INTR_MASK 0x0003E70
+#define ACP_SW1_PHY_TEST_MODE_DATA_OFF 0x0003E74
/* Registers from ACP_SCRATCH block */
-#define ACP_SCRATCH_REG_0 0x0010000
-#define ACP_SCRATCH_REG_1 0x0010004
-#define ACP_SCRATCH_REG_2 0x0010008
-#define ACP_SCRATCH_REG_3 0x001000C
-#define ACP_SCRATCH_REG_4 0x0010010
-#define ACP_SCRATCH_REG_5 0x0010014
-#define ACP_SCRATCH_REG_6 0x0010018
-#define ACP_SCRATCH_REG_7 0x001001C
-#define ACP_SCRATCH_REG_8 0x0010020
-#define ACP_SCRATCH_REG_9 0x0010024
-#define ACP_SCRATCH_REG_10 0x0010028
-#define ACP_SCRATCH_REG_11 0x001002C
-#define ACP_SCRATCH_REG_12 0x0010030
-#define ACP_SCRATCH_REG_13 0x0010034
-#define ACP_SCRATCH_REG_14 0x0010038
-#define ACP_SCRATCH_REG_15 0x001003C
-#define ACP_SCRATCH_REG_16 0x0010040
-#define ACP_SCRATCH_REG_17 0x0010044
-#define ACP_SCRATCH_REG_18 0x0010048
-#define ACP_SCRATCH_REG_19 0x001004C
-#define ACP_SCRATCH_REG_20 0x0010050
-#define ACP_SCRATCH_REG_21 0x0010054
-#define ACP_SCRATCH_REG_22 0x0010058
-#define ACP_SCRATCH_REG_23 0x001005C
-#define ACP_SCRATCH_REG_24 0x0010060
-#define ACP_SCRATCH_REG_25 0x0010064
-#define ACP_SCRATCH_REG_26 0x0010068
-#define ACP_SCRATCH_REG_27 0x001006C
-#define ACP_SCRATCH_REG_28 0x0010070
-#define ACP_SCRATCH_REG_29 0x0010074
-#define ACP_SCRATCH_REG_30 0x0010078
-#define ACP_SCRATCH_REG_31 0x001007C
-#define ACP_SCRATCH_REG_32 0x0010080
-#define ACP_SCRATCH_REG_33 0x0010084
-#define ACP_SCRATCH_REG_34 0x0010088
-#define ACP_SCRATCH_REG_35 0x001008C
-#define ACP_SCRATCH_REG_36 0x0010090
-#define ACP_SCRATCH_REG_37 0x0010094
-#define ACP_SCRATCH_REG_38 0x0010098
-#define ACP_SCRATCH_REG_39 0x001009C
-#define ACP_SCRATCH_REG_40 0x00100A0
-#define ACP_SCRATCH_REG_41 0x00100A4
-#define ACP_SCRATCH_REG_42 0x00100A8
-#define ACP_SCRATCH_REG_43 0x00100AC
-#define ACP_SCRATCH_REG_44 0x00100B0
-#define ACP_SCRATCH_REG_45 0x00100B4
-#define ACP_SCRATCH_REG_46 0x00100B8
-#define ACP_SCRATCH_REG_47 0x00100BC
-#define ACP_SCRATCH_REG_48 0x00100C0
-#define ACP_SCRATCH_REG_49 0x00100C4
-#define ACP_SCRATCH_REG_50 0x00100C8
-#define ACP_SCRATCH_REG_51 0x00100CC
-#define ACP_SCRATCH_REG_52 0x00100D0
-#define ACP_SCRATCH_REG_53 0x00100D4
-#define ACP_SCRATCH_REG_54 0x00100D8
-#define ACP_SCRATCH_REG_55 0x00100DC
-#define ACP_SCRATCH_REG_56 0x00100E0
-#define ACP_SCRATCH_REG_57 0x00100E4
-#define ACP_SCRATCH_REG_58 0x00100E8
-#define ACP_SCRATCH_REG_59 0x00100EC
-#define ACP_SCRATCH_REG_60 0x00100F0
-#define ACP_SCRATCH_REG_61 0x00100F4
-#define ACP_SCRATCH_REG_62 0x00100F8
-#define ACP_SCRATCH_REG_63 0x00100FC
-#define ACP_SCRATCH_REG_64 0x0010100
-#define ACP_SCRATCH_REG_65 0x0010104
-#define ACP_SCRATCH_REG_66 0x0010108
-#define ACP_SCRATCH_REG_67 0x001010C
-#define ACP_SCRATCH_REG_68 0x0010110
-#define ACP_SCRATCH_REG_69 0x0010114
-#define ACP_SCRATCH_REG_70 0x0010118
-#define ACP_SCRATCH_REG_71 0x001011C
-#define ACP_SCRATCH_REG_72 0x0010120
-#define ACP_SCRATCH_REG_73 0x0010124
-#define ACP_SCRATCH_REG_74 0x0010128
-#define ACP_SCRATCH_REG_75 0x001012C
-#define ACP_SCRATCH_REG_76 0x0010130
-#define ACP_SCRATCH_REG_77 0x0010134
-#define ACP_SCRATCH_REG_78 0x0010138
-#define ACP_SCRATCH_REG_79 0x001013C
-#define ACP_SCRATCH_REG_80 0x0010140
-#define ACP_SCRATCH_REG_81 0x0010144
-#define ACP_SCRATCH_REG_82 0x0010148
-#define ACP_SCRATCH_REG_83 0x001014C
-#define ACP_SCRATCH_REG_84 0x0010150
-#define ACP_SCRATCH_REG_85 0x0010154
-#define ACP_SCRATCH_REG_86 0x0010158
-#define ACP_SCRATCH_REG_87 0x001015C
-#define ACP_SCRATCH_REG_88 0x0010160
-#define ACP_SCRATCH_REG_89 0x0010164
-#define ACP_SCRATCH_REG_90 0x0010168
-#define ACP_SCRATCH_REG_91 0x001016C
-#define ACP_SCRATCH_REG_92 0x0010170
-#define ACP_SCRATCH_REG_93 0x0010174
-#define ACP_SCRATCH_REG_94 0x0010178
-#define ACP_SCRATCH_REG_95 0x001017C
-#define ACP_SCRATCH_REG_96 0x0010180
-#define ACP_SCRATCH_REG_97 0x0010184
-#define ACP_SCRATCH_REG_98 0x0010188
-#define ACP_SCRATCH_REG_99 0x001018C
-#define ACP_SCRATCH_REG_100 0x0010190
-#define ACP_SCRATCH_REG_101 0x0010194
-#define ACP_SCRATCH_REG_102 0x0010198
-#define ACP_SCRATCH_REG_103 0x001019C
-#define ACP_SCRATCH_REG_104 0x00101A0
-#define ACP_SCRATCH_REG_105 0x00101A4
-#define ACP_SCRATCH_REG_106 0x00101A8
-#define ACP_SCRATCH_REG_107 0x00101AC
-#define ACP_SCRATCH_REG_108 0x00101B0
-#define ACP_SCRATCH_REG_109 0x00101B4
-#define ACP_SCRATCH_REG_110 0x00101B8
-#define ACP_SCRATCH_REG_111 0x00101BC
-#define ACP_SCRATCH_REG_112 0x00101C0
-#define ACP_SCRATCH_REG_113 0x00101C4
-#define ACP_SCRATCH_REG_114 0x00101C8
-#define ACP_SCRATCH_REG_115 0x00101CC
-#define ACP_SCRATCH_REG_116 0x00101D0
-#define ACP_SCRATCH_REG_117 0x00101D4
-#define ACP_SCRATCH_REG_118 0x00101D8
-#define ACP_SCRATCH_REG_119 0x00101DC
-#define ACP_SCRATCH_REG_120 0x00101E0
-#define ACP_SCRATCH_REG_121 0x00101E4
-#define ACP_SCRATCH_REG_122 0x00101E8
-#define ACP_SCRATCH_REG_123 0x00101EC
-#define ACP_SCRATCH_REG_124 0x00101F0
-#define ACP_SCRATCH_REG_125 0x00101F4
-#define ACP_SCRATCH_REG_126 0x00101F8
-#define ACP_SCRATCH_REG_127 0x00101FC
-#define ACP_SCRATCH_REG_128 0x0010200
+#define ACP_SCRATCH_REG_0 0x0010000
+
#endif
diff --git a/include/sound/core.h b/include/sound/core.h
index 4365c35d038b4c..3edc4ab0877492 100644
--- a/include/sound/core.h
+++ b/include/sound/core.h
@@ -286,10 +286,10 @@ int snd_devm_card_new(struct device *parent, int idx, const char *xid,
struct module *module, size_t extra_size,
struct snd_card **card_ret);
-int snd_card_disconnect(struct snd_card *card);
+void snd_card_disconnect(struct snd_card *card);
void snd_card_disconnect_sync(struct snd_card *card);
-int snd_card_free(struct snd_card *card);
-int snd_card_free_when_closed(struct snd_card *card);
+void snd_card_free(struct snd_card *card);
+void snd_card_free_when_closed(struct snd_card *card);
int snd_card_free_on_error(struct device *dev, int ret);
void snd_card_set_id(struct snd_card *card, const char *id);
int snd_card_register(struct snd_card *card);
diff --git a/include/sound/cs42l42.h b/include/sound/cs42l42.h
index 1d1c24fdd0cae5..3994e933db1956 100644
--- a/include/sound/cs42l42.h
+++ b/include/sound/cs42l42.h
@@ -34,6 +34,7 @@
#define CS42L42_PAGE_24 0x2400
#define CS42L42_PAGE_25 0x2500
#define CS42L42_PAGE_26 0x2600
+#define CS42L42_PAGE_27 0x2700
#define CS42L42_PAGE_28 0x2800
#define CS42L42_PAGE_29 0x2900
#define CS42L42_PAGE_2A 0x2A00
@@ -720,6 +721,10 @@
#define CS42L42_SRC_SDOUT_FS (CS42L42_PAGE_26 + 0x09)
+/* Page 0x27 DMA */
+#define CS42L42_SOFT_RESET_REBOOT (CS42L42_PAGE_27 + 0x01)
+#define CS42L42_SFT_RST_REBOOT_MASK BIT(1)
+
/* Page 0x28 S/PDIF Registers */
#define CS42L42_SPDIF_CTL1 (CS42L42_PAGE_28 + 0x01)
#define CS42L42_SPDIF_CTL2 (CS42L42_PAGE_28 + 0x02)
diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h
index eba23daf2c290b..bbb7805e85d8e2 100644
--- a/include/sound/hda_codec.h
+++ b/include/sound/hda_codec.h
@@ -259,6 +259,7 @@ struct hda_codec {
unsigned int relaxed_resume:1; /* don't resume forcibly for jack */
unsigned int forced_resume:1; /* forced resume for jack */
unsigned int no_stream_clean_at_suspend:1; /* do not clean streams at suspend */
+ unsigned int ctl_dev_id:1; /* old control element id build behaviour */
#ifdef CONFIG_PM
unsigned long power_on_acct;
diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h
index a6872537724d3d..536612c6ab0cb0 100644
--- a/include/sound/hdaudio.h
+++ b/include/sound/hdaudio.h
@@ -575,7 +575,7 @@ void snd_hdac_stream_cleanup(struct hdac_stream *azx_dev);
int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev);
int snd_hdac_stream_set_params(struct hdac_stream *azx_dev,
unsigned int format_val);
-void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start);
+void snd_hdac_stream_start(struct hdac_stream *azx_dev);
void snd_hdac_stream_stop(struct hdac_stream *azx_dev);
void snd_hdac_stop_streams(struct hdac_bus *bus);
void snd_hdac_stop_streams_and_chip(struct hdac_bus *bus);
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h
index 38590f1ae9ee05..a3f3f3aa9e6e4a 100644
--- a/include/sound/simple_card_utils.h
+++ b/include/sound/simple_card_utils.h
@@ -69,6 +69,7 @@ struct asoc_simple_priv {
} *dai_props;
struct asoc_simple_jack hp_jack;
struct asoc_simple_jack mic_jack;
+ struct snd_soc_jack *aux_jacks;
struct snd_soc_dai_link *dai_link;
struct asoc_simple_dai *dais;
struct snd_soc_dai_link_component *dlcs;
@@ -187,6 +188,8 @@ int asoc_simple_parse_pin_switches(struct snd_soc_card *card,
int asoc_simple_init_jack(struct snd_soc_card *card,
struct asoc_simple_jack *sjack,
int is_hp, char *prefix, char *pin);
+int asoc_simple_init_aux_jacks(struct asoc_simple_priv *priv,
+ char *prefix);
int asoc_simple_init_priv(struct asoc_simple_priv *priv,
struct link_info *li);
int asoc_simple_remove(struct platform_device *pdev);
diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h
index c26ffb033777a1..3203d35bc8c157 100644
--- a/include/sound/soc-component.h
+++ b/include/sound/soc-component.h
@@ -98,6 +98,7 @@ struct snd_soc_component_driver {
int source, unsigned int freq_in, unsigned int freq_out);
int (*set_jack)(struct snd_soc_component *component,
struct snd_soc_jack *jack, void *data);
+ int (*get_jack_type)(struct snd_soc_component *component);
/* DT */
int (*of_xlate_dai_name)(struct snd_soc_component *component,
@@ -384,6 +385,7 @@ int snd_soc_component_set_pll(struct snd_soc_component *component, int pll_id,
unsigned int freq_out);
int snd_soc_component_set_jack(struct snd_soc_component *component,
struct snd_soc_jack *jack, void *data);
+int snd_soc_component_get_jack_type(struct snd_soc_component *component);
void snd_soc_component_seq_notifier(struct snd_soc_component *component,
enum snd_soc_dapm_type type, int subseq);
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index ea75096720864a..e3906ecda740a6 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -423,6 +423,16 @@ struct snd_soc_dai_driver {
int remove_order;
};
+/* for Playback/Capture */
+struct snd_soc_dai_stream {
+ struct snd_soc_dapm_widget *widget;
+
+ unsigned int active; /* usage count */
+ unsigned int tdm_mask; /* CODEC TDM slot masks and params (for fixup) */
+
+ void *dma_data; /* DAI DMA data */
+};
+
/*
* Digital Audio Interface runtime data.
*
@@ -437,14 +447,7 @@ struct snd_soc_dai {
struct snd_soc_dai_driver *driver;
/* DAI runtime info */
- unsigned int stream_active[SNDRV_PCM_STREAM_LAST + 1]; /* usage count */
-
- struct snd_soc_dapm_widget *playback_widget;
- struct snd_soc_dapm_widget *capture_widget;
-
- /* DAI DMA data */
- void *playback_dma_data;
- void *capture_dma_data;
+ struct snd_soc_dai_stream stream[SNDRV_PCM_STREAM_LAST + 1];
/* Symmetry data - only valid if symmetry is being enforced */
unsigned int rate;
@@ -454,10 +457,6 @@ struct snd_soc_dai {
/* parent platform/codec */
struct snd_soc_component *component;
- /* CODEC TDM slot masks and params (for fixup) */
- unsigned int tx_mask;
- unsigned int rx_mask;
-
struct list_head list;
/* function mark */
@@ -477,36 +476,59 @@ snd_soc_dai_get_pcm_stream(const struct snd_soc_dai *dai, int stream)
&dai->driver->playback : &dai->driver->capture;
}
+#define snd_soc_dai_get_widget_playback(dai) snd_soc_dai_get_widget(dai, SNDRV_PCM_STREAM_PLAYBACK)
+#define snd_soc_dai_get_widget_capture(dai) snd_soc_dai_get_widget(dai, SNDRV_PCM_STREAM_CAPTURE)
static inline
-struct snd_soc_dapm_widget *snd_soc_dai_get_widget(
- struct snd_soc_dai *dai, int stream)
+struct snd_soc_dapm_widget *snd_soc_dai_get_widget(struct snd_soc_dai *dai, int stream)
{
- return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- dai->playback_widget : dai->capture_widget;
+ return dai->stream[stream].widget;
}
-static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai *dai,
- const struct snd_pcm_substream *ss)
+#define snd_soc_dai_set_widget_playback(dai, widget) snd_soc_dai_set_widget(dai, SNDRV_PCM_STREAM_PLAYBACK, widget)
+#define snd_soc_dai_set_widget_capture(dai, widget) snd_soc_dai_set_widget(dai, SNDRV_PCM_STREAM_CAPTURE, widget)
+static inline
+void snd_soc_dai_set_widget(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget *widget)
{
- return (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- dai->playback_dma_data : dai->capture_dma_data;
+ dai->stream[stream].widget = widget;
}
-static inline void snd_soc_dai_set_dma_data(struct snd_soc_dai *dai,
- const struct snd_pcm_substream *ss,
- void *data)
+#define snd_soc_dai_dma_data_get_playback(dai) snd_soc_dai_dma_data_get(dai, SNDRV_PCM_STREAM_PLAYBACK)
+#define snd_soc_dai_dma_data_get_capture(dai) snd_soc_dai_dma_data_get(dai, SNDRV_PCM_STREAM_CAPTURE)
+#define snd_soc_dai_get_dma_data(dai, ss) snd_soc_dai_dma_data_get(dai, ss->stream)
+static inline void *snd_soc_dai_dma_data_get(const struct snd_soc_dai *dai, int stream)
{
- if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
- dai->playback_dma_data = data;
- else
- dai->capture_dma_data = data;
+ return dai->stream[stream].dma_data;
+}
+
+#define snd_soc_dai_dma_data_set_playback(dai, data) snd_soc_dai_dma_data_set(dai, SNDRV_PCM_STREAM_PLAYBACK, data)
+#define snd_soc_dai_dma_data_set_capture(dai, data) snd_soc_dai_dma_data_set(dai, SNDRV_PCM_STREAM_CAPTURE, data)
+#define snd_soc_dai_set_dma_data(dai, ss, data) snd_soc_dai_dma_data_set(dai, ss->stream, data)
+static inline void snd_soc_dai_dma_data_set(struct snd_soc_dai *dai, int stream, void *data)
+{
+ dai->stream[stream].dma_data = data;
}
-static inline void snd_soc_dai_init_dma_data(struct snd_soc_dai *dai,
- void *playback, void *capture)
+static inline void snd_soc_dai_init_dma_data(struct snd_soc_dai *dai, void *playback, void *capture)
{
- dai->playback_dma_data = playback;
- dai->capture_dma_data = capture;
+ snd_soc_dai_dma_data_set_playback(dai, playback);
+ snd_soc_dai_dma_data_set_capture(dai, capture);
+}
+
+static inline unsigned int snd_soc_dai_tdm_mask_get(struct snd_soc_dai *dai, int stream)
+{
+ return dai->stream[stream].tdm_mask;
+}
+
+static inline void snd_soc_dai_tdm_mask_set(struct snd_soc_dai *dai, int stream,
+ unsigned int tdm_mask)
+{
+ dai->stream[stream].tdm_mask = tdm_mask;
+}
+
+static inline unsigned int snd_soc_dai_stream_active(struct snd_soc_dai *dai, int stream)
+{
+ /* see snd_soc_dai_action() for setup */
+ return dai->stream[stream].active;
}
static inline void snd_soc_dai_set_drvdata(struct snd_soc_dai *dai,
@@ -561,10 +583,4 @@ static inline void *snd_soc_dai_get_stream(struct snd_soc_dai *dai,
return ERR_PTR(-ENOTSUPP);
}
-static inline unsigned int
-snd_soc_dai_stream_active(struct snd_soc_dai *dai, int stream)
-{
- return dai->stream_active[stream];
-}
-
#endif
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 77495e5988c12b..64915ebd641eec 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -16,6 +16,7 @@
#include <sound/asoc.h>
struct device;
+struct snd_pcm_substream;
struct snd_soc_pcm_runtime;
struct soc_enum;
diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h
index 2864aed7299835..1e7d09556fe3e9 100644
--- a/include/sound/soc-dpcm.h
+++ b/include/sound/soc-dpcm.h
@@ -162,6 +162,8 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream);
int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
int event);
bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir);
+int widget_in_list(struct snd_soc_dapm_widget_list *list,
+ struct snd_soc_dapm_widget *widget);
#define dpcm_be_dai_startup_rollback(fe, stream, last) \
dpcm_be_dai_stop(fe, stream, 0, last)
diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h
index b4b896f83b94cf..f055c6917f6c9b 100644
--- a/include/sound/soc-topology.h
+++ b/include/sound/soc-topology.h
@@ -62,7 +62,7 @@ struct snd_soc_dobj {
enum snd_soc_dobj_type type;
unsigned int index; /* objects can belong in different groups */
struct list_head list;
- struct snd_soc_tplg_ops *ops;
+ int (*unload)(struct snd_soc_component *comp, struct snd_soc_dobj *dobj);
union {
struct snd_soc_dobj_control control;
struct snd_soc_dobj_widget widget;
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 37bbfc8b45cb28..e58b43b5da7c05 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -1052,6 +1052,12 @@ struct snd_soc_card {
#define for_each_card_widgets_safe(card, w, _w) \
list_for_each_entry_safe(w, _w, &card->widgets, list)
+
+static inline int snd_soc_card_is_instantiated(struct snd_soc_card *card)
+{
+ return card && card->instantiated;
+}
+
/* SoC machine DAI configuration, glues a codec and cpu DAI together */
struct snd_soc_pcm_runtime {
struct device *dev;
diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h
index 622193be7ac43d..d31349bf011d3a 100644
--- a/include/sound/sof/ipc4/header.h
+++ b/include/sound/sof/ipc4/header.h
@@ -185,6 +185,9 @@ enum sof_ipc4_pipeline_state {
#define SOF_IPC4_GLB_PIPE_STATE_MASK GENMASK(15, 0)
#define SOF_IPC4_GLB_PIPE_STATE(x) ((x) << SOF_IPC4_GLB_PIPE_STATE_SHIFT)
+/* pipeline set state IPC msg extension */
+#define SOF_IPC4_GLB_PIPE_STATE_EXT_MULTI BIT(0)
+
/* load library ipc msg */
#define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT 16
#define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(x) ((x) << SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT)
diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h
index 0bce0b4ff2faf3..6548b5b5aa6080 100644
--- a/include/trace/events/btrfs.h
+++ b/include/trace/events/btrfs.h
@@ -98,7 +98,7 @@ struct raid56_bio_trace_info;
EM( FLUSH_DELALLOC_WAIT, "FLUSH_DELALLOC_WAIT") \
EM( FLUSH_DELALLOC_FULL, "FLUSH_DELALLOC_FULL") \
EM( FLUSH_DELAYED_REFS_NR, "FLUSH_DELAYED_REFS_NR") \
- EM( FLUSH_DELAYED_REFS, "FLUSH_ELAYED_REFS") \
+ EM( FLUSH_DELAYED_REFS, "FLUSH_DELAYED_REFS") \
EM( ALLOC_CHUNK, "ALLOC_CHUNK") \
EM( ALLOC_CHUNK_FORCE, "ALLOC_CHUNK_FORCE") \
EM( RUN_DELAYED_IPUTS, "RUN_DELAYED_IPUTS") \
diff --git a/include/trace/events/rxrpc.h b/include/trace/events/rxrpc.h
index c6cfed00d0c6c4..283db0ea3db4b3 100644
--- a/include/trace/events/rxrpc.h
+++ b/include/trace/events/rxrpc.h
@@ -16,7 +16,107 @@
/*
* Declare tracing information enums and their string mappings for display.
*/
+#define rxrpc_abort_reasons \
+ /* AFS errors */ \
+ EM(afs_abort_general_error, "afs-error") \
+ EM(afs_abort_interrupted, "afs-intr") \
+ EM(afs_abort_oom, "afs-oom") \
+ EM(afs_abort_op_not_supported, "afs-op-notsupp") \
+ EM(afs_abort_probeuuid_negative, "afs-probeuuid-neg") \
+ EM(afs_abort_send_data_error, "afs-send-data") \
+ EM(afs_abort_unmarshal_error, "afs-unmarshal") \
+ /* rxperf errors */ \
+ EM(rxperf_abort_general_error, "rxperf-error") \
+ EM(rxperf_abort_oom, "rxperf-oom") \
+ EM(rxperf_abort_op_not_supported, "rxperf-op-notsupp") \
+ EM(rxperf_abort_unmarshal_error, "rxperf-unmarshal") \
+ /* RxKAD security errors */ \
+ EM(rxkad_abort_1_short_check, "rxkad1-short-check") \
+ EM(rxkad_abort_1_short_data, "rxkad1-short-data") \
+ EM(rxkad_abort_1_short_encdata, "rxkad1-short-encdata") \
+ EM(rxkad_abort_1_short_header, "rxkad1-short-hdr") \
+ EM(rxkad_abort_2_short_check, "rxkad2-short-check") \
+ EM(rxkad_abort_2_short_data, "rxkad2-short-data") \
+ EM(rxkad_abort_2_short_header, "rxkad2-short-hdr") \
+ EM(rxkad_abort_2_short_len, "rxkad2-short-len") \
+ EM(rxkad_abort_bad_checksum, "rxkad2-bad-cksum") \
+ EM(rxkad_abort_chall_key_expired, "rxkad-chall-key-exp") \
+ EM(rxkad_abort_chall_level, "rxkad-chall-level") \
+ EM(rxkad_abort_chall_no_key, "rxkad-chall-nokey") \
+ EM(rxkad_abort_chall_short, "rxkad-chall-short") \
+ EM(rxkad_abort_chall_version, "rxkad-chall-version") \
+ EM(rxkad_abort_resp_bad_callid, "rxkad-resp-bad-callid") \
+ EM(rxkad_abort_resp_bad_checksum, "rxkad-resp-bad-cksum") \
+ EM(rxkad_abort_resp_bad_param, "rxkad-resp-bad-param") \
+ EM(rxkad_abort_resp_call_ctr, "rxkad-resp-call-ctr") \
+ EM(rxkad_abort_resp_call_state, "rxkad-resp-call-state") \
+ EM(rxkad_abort_resp_key_expired, "rxkad-resp-key-exp") \
+ EM(rxkad_abort_resp_key_rejected, "rxkad-resp-key-rej") \
+ EM(rxkad_abort_resp_level, "rxkad-resp-level") \
+ EM(rxkad_abort_resp_nokey, "rxkad-resp-nokey") \
+ EM(rxkad_abort_resp_ooseq, "rxkad-resp-ooseq") \
+ EM(rxkad_abort_resp_short, "rxkad-resp-short") \
+ EM(rxkad_abort_resp_short_tkt, "rxkad-resp-short-tkt") \
+ EM(rxkad_abort_resp_tkt_aname, "rxkad-resp-tk-aname") \
+ EM(rxkad_abort_resp_tkt_expired, "rxkad-resp-tk-exp") \
+ EM(rxkad_abort_resp_tkt_future, "rxkad-resp-tk-future") \
+ EM(rxkad_abort_resp_tkt_inst, "rxkad-resp-tk-inst") \
+ EM(rxkad_abort_resp_tkt_len, "rxkad-resp-tk-len") \
+ EM(rxkad_abort_resp_tkt_realm, "rxkad-resp-tk-realm") \
+ EM(rxkad_abort_resp_tkt_short, "rxkad-resp-tk-short") \
+ EM(rxkad_abort_resp_tkt_sinst, "rxkad-resp-tk-sinst") \
+ EM(rxkad_abort_resp_tkt_sname, "rxkad-resp-tk-sname") \
+ EM(rxkad_abort_resp_unknown_tkt, "rxkad-resp-unknown-tkt") \
+ EM(rxkad_abort_resp_version, "rxkad-resp-version") \
+ /* rxrpc errors */ \
+ EM(rxrpc_abort_call_improper_term, "call-improper-term") \
+ EM(rxrpc_abort_call_reset, "call-reset") \
+ EM(rxrpc_abort_call_sendmsg, "call-sendmsg") \
+ EM(rxrpc_abort_call_sock_release, "call-sock-rel") \
+ EM(rxrpc_abort_call_sock_release_tba, "call-sock-rel-tba") \
+ EM(rxrpc_abort_call_timeout, "call-timeout") \
+ EM(rxrpc_abort_no_service_key, "no-serv-key") \
+ EM(rxrpc_abort_nomem, "nomem") \
+ EM(rxrpc_abort_service_not_offered, "serv-not-offered") \
+ EM(rxrpc_abort_shut_down, "shut-down") \
+ EM(rxrpc_abort_unsupported_security, "unsup-sec") \
+ EM(rxrpc_badmsg_bad_abort, "bad-abort") \
+ EM(rxrpc_badmsg_bad_jumbo, "bad-jumbo") \
+ EM(rxrpc_badmsg_short_ack, "short-ack") \
+ EM(rxrpc_badmsg_short_ack_info, "short-ack-info") \
+ EM(rxrpc_badmsg_short_hdr, "short-hdr") \
+ EM(rxrpc_badmsg_unsupported_packet, "unsup-pkt") \
+ EM(rxrpc_badmsg_zero_call, "zero-call") \
+ EM(rxrpc_badmsg_zero_seq, "zero-seq") \
+ EM(rxrpc_badmsg_zero_service, "zero-service") \
+ EM(rxrpc_eproto_ackr_outside_window, "ackr-out-win") \
+ EM(rxrpc_eproto_ackr_sack_overflow, "ackr-sack-over") \
+ EM(rxrpc_eproto_ackr_short_sack, "ackr-short-sack") \
+ EM(rxrpc_eproto_ackr_zero, "ackr-zero") \
+ EM(rxrpc_eproto_bad_upgrade, "bad-upgrade") \
+ EM(rxrpc_eproto_data_after_last, "data-after-last") \
+ EM(rxrpc_eproto_different_last, "diff-last") \
+ EM(rxrpc_eproto_early_reply, "early-reply") \
+ EM(rxrpc_eproto_improper_term, "improper-term") \
+ EM(rxrpc_eproto_no_client_call, "no-cl-call") \
+ EM(rxrpc_eproto_no_client_conn, "no-cl-conn") \
+ EM(rxrpc_eproto_no_service_call, "no-sv-call") \
+ EM(rxrpc_eproto_reupgrade, "re-upgrade") \
+ EM(rxrpc_eproto_rxnull_challenge, "rxnull-chall") \
+ EM(rxrpc_eproto_rxnull_response, "rxnull-resp") \
+ EM(rxrpc_eproto_tx_rot_last, "tx-rot-last") \
+ EM(rxrpc_eproto_unexpected_ack, "unex-ack") \
+ EM(rxrpc_eproto_unexpected_ackall, "unex-ackall") \
+ EM(rxrpc_eproto_unexpected_implicit_end, "unex-impl-end") \
+ EM(rxrpc_eproto_unexpected_reply, "unex-reply") \
+ EM(rxrpc_eproto_wrong_security, "wrong-sec") \
+ EM(rxrpc_recvmsg_excess_data, "recvmsg-excess") \
+ EM(rxrpc_recvmsg_short_data, "recvmsg-short") \
+ E_(rxrpc_sendmsg_late_send, "sendmsg-late")
+
#define rxrpc_call_poke_traces \
+ EM(rxrpc_call_poke_abort, "Abort") \
+ EM(rxrpc_call_poke_complete, "Compl") \
EM(rxrpc_call_poke_error, "Error") \
EM(rxrpc_call_poke_idle, "Idle") \
EM(rxrpc_call_poke_start, "Start") \
@@ -26,6 +126,7 @@
#define rxrpc_skb_traces \
EM(rxrpc_skb_eaten_by_unshare, "ETN unshare ") \
EM(rxrpc_skb_eaten_by_unshare_nomem, "ETN unshar-nm") \
+ EM(rxrpc_skb_get_conn_secured, "GET conn-secd") \
EM(rxrpc_skb_get_conn_work, "GET conn-work") \
EM(rxrpc_skb_get_local_work, "GET locl-work") \
EM(rxrpc_skb_get_reject_work, "GET rej-work ") \
@@ -35,6 +136,7 @@
EM(rxrpc_skb_new_error_report, "NEW error-rpt") \
EM(rxrpc_skb_new_jumbo_subpacket, "NEW jumbo-sub") \
EM(rxrpc_skb_new_unshared, "NEW unshared ") \
+ EM(rxrpc_skb_put_conn_secured, "PUT conn-secd") \
EM(rxrpc_skb_put_conn_work, "PUT conn-work") \
EM(rxrpc_skb_put_error_report, "PUT error-rep") \
EM(rxrpc_skb_put_input, "PUT input ") \
@@ -76,7 +178,6 @@
#define rxrpc_peer_traces \
EM(rxrpc_peer_free, "FREE ") \
EM(rxrpc_peer_get_accept, "GET accept ") \
- EM(rxrpc_peer_get_activate_call, "GET act-call") \
EM(rxrpc_peer_get_bundle, "GET bundle ") \
EM(rxrpc_peer_get_client_conn, "GET cln-conn") \
EM(rxrpc_peer_get_input, "GET input ") \
@@ -89,7 +190,6 @@
EM(rxrpc_peer_put_bundle, "PUT bundle ") \
EM(rxrpc_peer_put_call, "PUT call ") \
EM(rxrpc_peer_put_conn, "PUT conn ") \
- EM(rxrpc_peer_put_discard_tmp, "PUT disc-tmp") \
EM(rxrpc_peer_put_input, "PUT input ") \
EM(rxrpc_peer_put_input_error, "PUT inpt-err") \
E_(rxrpc_peer_put_keepalive, "PUT keepaliv")
@@ -99,6 +199,7 @@
EM(rxrpc_bundle_get_client_call, "GET clt-call") \
EM(rxrpc_bundle_get_client_conn, "GET clt-conn") \
EM(rxrpc_bundle_get_service_conn, "GET svc-conn") \
+ EM(rxrpc_bundle_put_call, "PUT call ") \
EM(rxrpc_bundle_put_conn, "PUT conn ") \
EM(rxrpc_bundle_put_discard, "PUT discard ") \
E_(rxrpc_bundle_new, "NEW ")
@@ -109,14 +210,14 @@
EM(rxrpc_conn_get_call_input, "GET inp-call") \
EM(rxrpc_conn_get_conn_input, "GET inp-conn") \
EM(rxrpc_conn_get_idle, "GET idle ") \
- EM(rxrpc_conn_get_poke, "GET poke ") \
+ EM(rxrpc_conn_get_poke_abort, "GET pk-abort") \
+ EM(rxrpc_conn_get_poke_timer, "GET poke ") \
EM(rxrpc_conn_get_service_conn, "GET svc-conn") \
EM(rxrpc_conn_new_client, "NEW client ") \
EM(rxrpc_conn_new_service, "NEW service ") \
EM(rxrpc_conn_put_call, "PUT call ") \
EM(rxrpc_conn_put_call_input, "PUT inp-call") \
EM(rxrpc_conn_put_conn_input, "PUT inp-conn") \
- EM(rxrpc_conn_put_discard, "PUT discard ") \
EM(rxrpc_conn_put_discard_idle, "PUT disc-idl") \
EM(rxrpc_conn_put_local_dead, "PUT loc-dead") \
EM(rxrpc_conn_put_noreuse, "PUT noreuse ") \
@@ -124,10 +225,10 @@
EM(rxrpc_conn_put_service_reaped, "PUT svc-reap") \
EM(rxrpc_conn_put_unbundle, "PUT unbundle") \
EM(rxrpc_conn_put_unidle, "PUT unidle ") \
+ EM(rxrpc_conn_put_work, "PUT work ") \
EM(rxrpc_conn_queue_challenge, "QUE chall ") \
EM(rxrpc_conn_queue_retry_work, "QUE retry-wk") \
EM(rxrpc_conn_queue_rx_work, "QUE rx-work ") \
- EM(rxrpc_conn_queue_timer, "QUE timer ") \
EM(rxrpc_conn_see_new_service_conn, "SEE new-svc ") \
EM(rxrpc_conn_see_reap_service, "SEE reap-svc") \
E_(rxrpc_conn_see_work, "SEE work ")
@@ -138,16 +239,16 @@
EM(rxrpc_client_chan_activate, "ChActv") \
EM(rxrpc_client_chan_disconnect, "ChDisc") \
EM(rxrpc_client_chan_pass, "ChPass") \
- EM(rxrpc_client_chan_wait_failed, "ChWtFl") \
EM(rxrpc_client_cleanup, "Clean ") \
EM(rxrpc_client_discard, "Discar") \
- EM(rxrpc_client_duplicate, "Duplic") \
EM(rxrpc_client_exposed, "Expose") \
EM(rxrpc_client_replace, "Replac") \
+ EM(rxrpc_client_queue_new_call, "Q-Call") \
EM(rxrpc_client_to_active, "->Actv") \
E_(rxrpc_client_to_idle, "->Idle")
#define rxrpc_call_traces \
+ EM(rxrpc_call_get_io_thread, "GET iothread") \
EM(rxrpc_call_get_input, "GET input ") \
EM(rxrpc_call_get_kernel_service, "GET krnl-srv") \
EM(rxrpc_call_get_notify_socket, "GET notify ") \
@@ -160,6 +261,7 @@
EM(rxrpc_call_new_prealloc_service, "NEW prealloc") \
EM(rxrpc_call_put_discard_prealloc, "PUT disc-pre") \
EM(rxrpc_call_put_discard_error, "PUT disc-err") \
+ EM(rxrpc_call_put_io_thread, "PUT iothread") \
EM(rxrpc_call_put_input, "PUT input ") \
EM(rxrpc_call_put_kernel, "PUT kernel ") \
EM(rxrpc_call_put_poke, "PUT poke ") \
@@ -169,10 +271,12 @@
EM(rxrpc_call_put_sendmsg, "PUT sendmsg ") \
EM(rxrpc_call_put_unnotify, "PUT unnotify") \
EM(rxrpc_call_put_userid_exists, "PUT u-exists") \
+ EM(rxrpc_call_put_userid, "PUT user-id ") \
EM(rxrpc_call_see_accept, "SEE accept ") \
EM(rxrpc_call_see_activate_client, "SEE act-clnt") \
EM(rxrpc_call_see_connect_failed, "SEE con-fail") \
EM(rxrpc_call_see_connected, "SEE connect ") \
+ EM(rxrpc_call_see_disconnected, "SEE disconn ") \
EM(rxrpc_call_see_distribute_error, "SEE dist-err") \
EM(rxrpc_call_see_input, "SEE input ") \
EM(rxrpc_call_see_release, "SEE release ") \
@@ -376,6 +480,7 @@
#define EM(a, b) a,
#define E_(a, b) a
+enum rxrpc_abort_reason { rxrpc_abort_reasons } __mode(byte);
enum rxrpc_bundle_trace { rxrpc_bundle_traces } __mode(byte);
enum rxrpc_call_poke_trace { rxrpc_call_poke_traces } __mode(byte);
enum rxrpc_call_trace { rxrpc_call_traces } __mode(byte);
@@ -404,9 +509,13 @@ enum rxrpc_txqueue_trace { rxrpc_txqueue_traces } __mode(byte);
*/
#undef EM
#undef E_
+
+#ifndef RXRPC_TRACE_ONLY_DEFINE_ENUMS
+
#define EM(a, b) TRACE_DEFINE_ENUM(a);
#define E_(a, b) TRACE_DEFINE_ENUM(a);
+rxrpc_abort_reasons;
rxrpc_bundle_traces;
rxrpc_call_poke_traces;
rxrpc_call_traces;
@@ -657,14 +766,14 @@ TRACE_EVENT(rxrpc_rx_done,
);
TRACE_EVENT(rxrpc_abort,
- TP_PROTO(unsigned int call_nr, const char *why, u32 cid, u32 call_id,
- rxrpc_seq_t seq, int abort_code, int error),
+ TP_PROTO(unsigned int call_nr, enum rxrpc_abort_reason why,
+ u32 cid, u32 call_id, rxrpc_seq_t seq, int abort_code, int error),
TP_ARGS(call_nr, why, cid, call_id, seq, abort_code, error),
TP_STRUCT__entry(
__field(unsigned int, call_nr )
- __array(char, why, 4 )
+ __field(enum rxrpc_abort_reason, why )
__field(u32, cid )
__field(u32, call_id )
__field(rxrpc_seq_t, seq )
@@ -673,8 +782,8 @@ TRACE_EVENT(rxrpc_abort,
),
TP_fast_assign(
- memcpy(__entry->why, why, 4);
__entry->call_nr = call_nr;
+ __entry->why = why;
__entry->cid = cid;
__entry->call_id = call_id;
__entry->abort_code = abort_code;
@@ -685,7 +794,8 @@ TRACE_EVENT(rxrpc_abort,
TP_printk("c=%08x %08x:%08x s=%u a=%d e=%d %s",
__entry->call_nr,
__entry->cid, __entry->call_id, __entry->seq,
- __entry->abort_code, __entry->error, __entry->why)
+ __entry->abort_code, __entry->error,
+ __print_symbolic(__entry->why, rxrpc_abort_reasons))
);
TRACE_EVENT(rxrpc_call_complete,
@@ -1062,10 +1172,10 @@ TRACE_EVENT(rxrpc_receive,
);
TRACE_EVENT(rxrpc_recvmsg,
- TP_PROTO(struct rxrpc_call *call, enum rxrpc_recvmsg_trace why,
+ TP_PROTO(unsigned int call_debug_id, enum rxrpc_recvmsg_trace why,
int ret),
- TP_ARGS(call, why, ret),
+ TP_ARGS(call_debug_id, why, ret),
TP_STRUCT__entry(
__field(unsigned int, call )
@@ -1074,7 +1184,7 @@ TRACE_EVENT(rxrpc_recvmsg,
),
TP_fast_assign(
- __entry->call = call ? call->debug_id : 0;
+ __entry->call = call_debug_id;
__entry->why = why;
__entry->ret = ret;
),
@@ -1521,30 +1631,6 @@ TRACE_EVENT(rxrpc_improper_term,
__entry->abort_code)
);
-TRACE_EVENT(rxrpc_rx_eproto,
- TP_PROTO(struct rxrpc_call *call, rxrpc_serial_t serial,
- const char *why),
-
- TP_ARGS(call, serial, why),
-
- TP_STRUCT__entry(
- __field(unsigned int, call )
- __field(rxrpc_serial_t, serial )
- __field(const char *, why )
- ),
-
- TP_fast_assign(
- __entry->call = call ? call->debug_id : 0;
- __entry->serial = serial;
- __entry->why = why;
- ),
-
- TP_printk("c=%08x EPROTO %08x %s",
- __entry->call,
- __entry->serial,
- __entry->why)
- );
-
TRACE_EVENT(rxrpc_connect_call,
TP_PROTO(struct rxrpc_call *call),
@@ -1842,6 +1928,8 @@ TRACE_EVENT(rxrpc_call_poked,
#undef EM
#undef E_
+
+#endif /* RXRPC_TRACE_ONLY_DEFINE_ENUMS */
#endif /* _TRACE_RXRPC_H */
/* This part must be outside protection */
diff --git a/include/trace/stages/stage4_event_fields.h b/include/trace/stages/stage4_event_fields.h
index affd541fd25eac..b6f679ae21aa77 100644
--- a/include/trace/stages/stage4_event_fields.h
+++ b/include/trace/stages/stage4_event_fields.h
@@ -26,7 +26,8 @@
#define __array(_type, _item, _len) { \
.type = #_type"["__stringify(_len)"]", .name = #_item, \
.size = sizeof(_type[_len]), .align = ALIGN_STRUCTFIELD(_type), \
- .is_signed = is_signed_type(_type), .filter_type = FILTER_OTHER },
+ .is_signed = is_signed_type(_type), .filter_type = FILTER_OTHER,\
+ .len = _len },
#undef __dynamic_array
#define __dynamic_array(_type, _item, _len) { \
diff --git a/include/uapi/drm/virtgpu_drm.h b/include/uapi/drm/virtgpu_drm.h
index 0512fde5e6978a..7b158fcb02b455 100644
--- a/include/uapi/drm/virtgpu_drm.h
+++ b/include/uapi/drm/virtgpu_drm.h
@@ -64,6 +64,7 @@ struct drm_virtgpu_map {
__u32 pad;
};
+/* fence_fd is modified on success if VIRTGPU_EXECBUF_FENCE_FD_OUT flag is set. */
struct drm_virtgpu_execbuffer {
__u32 flags;
__u32 size;
diff --git a/include/uapi/linux/atmbr2684.h b/include/uapi/linux/atmbr2684.h
index a9e2250cd7205a..d47c47d06f1109 100644
--- a/include/uapi/linux/atmbr2684.h
+++ b/include/uapi/linux/atmbr2684.h
@@ -38,7 +38,7 @@
*/
#define BR2684_ENCAPS_VC (0) /* VC-mux */
#define BR2684_ENCAPS_LLC (1)
-#define BR2684_ENCAPS_AUTODETECT (2) /* Unsuported */
+#define BR2684_ENCAPS_AUTODETECT (2) /* Unsupported */
/*
* Is this VC bridged or routed?
diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h
index 9d4c4078e8d00e..2780bce62fafe6 100644
--- a/include/uapi/linux/io_uring.h
+++ b/include/uapi/linux/io_uring.h
@@ -10,7 +10,15 @@
#include <linux/fs.h>
#include <linux/types.h>
+/*
+ * this file is shared with liburing and that has to autodetect
+ * if linux/time_types.h is available or not, it can
+ * define UAPI_LINUX_IO_URING_H_SKIP_LINUX_TIME_TYPES_H
+ * if linux/time_types.h is not available
+ */
+#ifndef UAPI_LINUX_IO_URING_H_SKIP_LINUX_TIME_TYPES_H
#include <linux/time_types.h>
+#endif
#ifdef __cplusplus
extern "C" {
diff --git a/include/uapi/linux/ip.h b/include/uapi/linux/ip.h
index 874a92349bf5ba..283dec7e36451a 100644
--- a/include/uapi/linux/ip.h
+++ b/include/uapi/linux/ip.h
@@ -18,6 +18,7 @@
#ifndef _UAPI_LINUX_IP_H
#define _UAPI_LINUX_IP_H
#include <linux/types.h>
+#include <linux/stddef.h>
#include <asm/byteorder.h>
#define IPTOS_TOS_MASK 0x1E
diff --git a/include/uapi/linux/ipv6.h b/include/uapi/linux/ipv6.h
index 81f4243bebb1c8..53326dfc59ecb7 100644
--- a/include/uapi/linux/ipv6.h
+++ b/include/uapi/linux/ipv6.h
@@ -4,6 +4,7 @@
#include <linux/libc-compat.h>
#include <linux/types.h>
+#include <linux/stddef.h>
#include <linux/in6.h>
#include <asm/byteorder.h>
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 20522d4ba1e0d8..55155e262646e5 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1767,6 +1767,7 @@ struct kvm_xen_hvm_attr {
__u8 runstate_update_flag;
struct {
__u64 gfn;
+#define KVM_XEN_INVALID_GFN ((__u64)-1)
} shared_info;
struct {
__u32 send_port;
@@ -1798,6 +1799,7 @@ struct kvm_xen_hvm_attr {
} u;
};
+
/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_SHARED_INFO */
#define KVM_XEN_ATTR_TYPE_LONG_MODE 0x0
#define KVM_XEN_ATTR_TYPE_SHARED_INFO 0x1
@@ -1823,6 +1825,7 @@ struct kvm_xen_vcpu_attr {
__u16 pad[3];
union {
__u64 gpa;
+#define KVM_XEN_INVALID_GPA ((__u64)-1)
__u64 pad[8];
struct {
__u64 state;
diff --git a/include/uapi/linux/netfilter/nf_conntrack_sctp.h b/include/uapi/linux/netfilter/nf_conntrack_sctp.h
index c742469afe21c0..2d6f80d75ae74c 100644
--- a/include/uapi/linux/netfilter/nf_conntrack_sctp.h
+++ b/include/uapi/linux/netfilter/nf_conntrack_sctp.h
@@ -15,8 +15,7 @@ enum sctp_conntrack {
SCTP_CONNTRACK_SHUTDOWN_RECD,
SCTP_CONNTRACK_SHUTDOWN_ACK_SENT,
SCTP_CONNTRACK_HEARTBEAT_SENT,
- SCTP_CONNTRACK_HEARTBEAT_ACKED,
- SCTP_CONNTRACK_DATA_SENT,
+ SCTP_CONNTRACK_HEARTBEAT_ACKED, /* no longer used */
SCTP_CONNTRACK_MAX
};
diff --git a/include/uapi/linux/netfilter/nfnetlink_cttimeout.h b/include/uapi/linux/netfilter/nfnetlink_cttimeout.h
index 94e74034706d98..aa805e6d4e284d 100644
--- a/include/uapi/linux/netfilter/nfnetlink_cttimeout.h
+++ b/include/uapi/linux/netfilter/nfnetlink_cttimeout.h
@@ -94,8 +94,7 @@ enum ctattr_timeout_sctp {
CTA_TIMEOUT_SCTP_SHUTDOWN_RECD,
CTA_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT,
CTA_TIMEOUT_SCTP_HEARTBEAT_SENT,
- CTA_TIMEOUT_SCTP_HEARTBEAT_ACKED,
- CTA_TIMEOUT_SCTP_DATA_SENT,
+ CTA_TIMEOUT_SCTP_HEARTBEAT_ACKED, /* no longer used */
__CTA_TIMEOUT_SCTP_MAX
};
#define CTA_TIMEOUT_SCTP_MAX (__CTA_TIMEOUT_SCTP_MAX - 1)
diff --git a/include/uapi/linux/pktcdvd.h b/include/uapi/linux/pktcdvd.h
new file mode 100644
index 00000000000000..9cbb55d21c94af
--- /dev/null
+++ b/include/uapi/linux/pktcdvd.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2000 Jens Axboe <axboe@suse.de>
+ * Copyright (C) 2001-2004 Peter Osterlund <petero2@telia.com>
+ *
+ * May be copied or modified under the terms of the GNU General Public
+ * License. See linux/COPYING for more information.
+ *
+ * Packet writing layer for ATAPI and SCSI CD-R, CD-RW, DVD-R, and
+ * DVD-RW devices.
+ *
+ */
+#ifndef _UAPI__PKTCDVD_H
+#define _UAPI__PKTCDVD_H
+
+#include <linux/types.h>
+
+/*
+ * 1 for normal debug messages, 2 is very verbose. 0 to turn it off.
+ */
+#define PACKET_DEBUG 1
+
+#define MAX_WRITERS 8
+
+#define PKT_RB_POOL_SIZE 512
+
+/*
+ * How long we should hold a non-full packet before starting data gathering.
+ */
+#define PACKET_WAIT_TIME (HZ * 5 / 1000)
+
+/*
+ * use drive write caching -- we need deferred error handling to be
+ * able to successfully recover with this option (drive will return good
+ * status as soon as the cdb is validated).
+ */
+#if defined(CONFIG_CDROM_PKTCDVD_WCACHE)
+#define USE_WCACHING 1
+#else
+#define USE_WCACHING 0
+#endif
+
+/*
+ * No user-servicable parts beyond this point ->
+ */
+
+/*
+ * device types
+ */
+#define PACKET_CDR 1
+#define PACKET_CDRW 2
+#define PACKET_DVDR 3
+#define PACKET_DVDRW 4
+
+/*
+ * flags
+ */
+#define PACKET_WRITABLE 1 /* pd is writable */
+#define PACKET_NWA_VALID 2 /* next writable address valid */
+#define PACKET_LRA_VALID 3 /* last recorded address valid */
+#define PACKET_MERGE_SEGS 4 /* perform segment merging to keep */
+ /* underlying cdrom device happy */
+
+/*
+ * Disc status -- from READ_DISC_INFO
+ */
+#define PACKET_DISC_EMPTY 0
+#define PACKET_DISC_INCOMPLETE 1
+#define PACKET_DISC_COMPLETE 2
+#define PACKET_DISC_OTHER 3
+
+/*
+ * write type, and corresponding data block type
+ */
+#define PACKET_MODE1 1
+#define PACKET_MODE2 2
+#define PACKET_BLOCK_MODE1 8
+#define PACKET_BLOCK_MODE2 10
+
+/*
+ * Last session/border status
+ */
+#define PACKET_SESSION_EMPTY 0
+#define PACKET_SESSION_INCOMPLETE 1
+#define PACKET_SESSION_RESERVED 2
+#define PACKET_SESSION_COMPLETE 3
+
+#define PACKET_MCN "4a656e734178626f65323030300000"
+
+#undef PACKET_USE_LS
+
+#define PKT_CTRL_CMD_SETUP 0
+#define PKT_CTRL_CMD_TEARDOWN 1
+#define PKT_CTRL_CMD_STATUS 2
+
+struct pkt_ctrl_command {
+ __u32 command; /* in: Setup, teardown, status */
+ __u32 dev_index; /* in/out: Device index */
+ __u32 dev; /* in/out: Device nr for cdrw device */
+ __u32 pkt_dev; /* in/out: Device nr for packet device */
+ __u32 num_devices; /* out: Largest device index + 1 */
+ __u32 padding; /* Not used */
+};
+
+/*
+ * packet ioctls
+ */
+#define PACKET_IOCTL_MAGIC ('X')
+#define PACKET_CTRL_CMD _IOWR(PACKET_IOCTL_MAGIC, 1, struct pkt_ctrl_command)
+
+
+#endif /* _UAPI__PKTCDVD_H */
diff --git a/include/uapi/linux/psci.h b/include/uapi/linux/psci.h
index 3511095c2702be..42a40ad3fb622c 100644
--- a/include/uapi/linux/psci.h
+++ b/include/uapi/linux/psci.h
@@ -58,7 +58,7 @@
#define PSCI_1_1_FN_SYSTEM_RESET2 PSCI_0_2_FN(18)
#define PSCI_1_1_FN_MEM_PROTECT PSCI_0_2_FN(19)
-#define PSCI_1_1_FN_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN(19)
+#define PSCI_1_1_FN_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN(20)
#define PSCI_1_0_FN64_CPU_DEFAULT_SUSPEND PSCI_0_2_FN64(12)
#define PSCI_1_0_FN64_NODE_HW_STATE PSCI_0_2_FN64(13)
@@ -67,7 +67,7 @@
#define PSCI_1_0_FN64_STAT_COUNT PSCI_0_2_FN64(17)
#define PSCI_1_1_FN64_SYSTEM_RESET2 PSCI_0_2_FN64(18)
-#define PSCI_1_1_FN64_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN64(19)
+#define PSCI_1_1_FN64_MEM_PROTECT_CHECK_RANGE PSCI_0_2_FN64(20)
/* PSCI v0.2 power state encoding for CPU_SUSPEND function */
#define PSCI_0_2_POWER_STATE_ID_MASK 0xffff
diff --git a/include/uapi/linux/vdpa.h b/include/uapi/linux/vdpa.h
index 9bd79235c875f2..54b649ab0f22b6 100644
--- a/include/uapi/linux/vdpa.h
+++ b/include/uapi/linux/vdpa.h
@@ -53,11 +53,9 @@ enum vdpa_attr {
VDPA_ATTR_DEV_VENDOR_ATTR_NAME, /* string */
VDPA_ATTR_DEV_VENDOR_ATTR_VALUE, /* u64 */
+ /* virtio features that are provisioned to the vDPA device */
VDPA_ATTR_DEV_FEATURES, /* u64 */
- /* virtio features that are supported by the vDPA device */
- VDPA_ATTR_VDPA_DEV_SUPPORTED_FEATURES, /* u64 */
-
/* new attributes must be added above here */
VDPA_ATTR_MAX,
};
diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h
index 3532ac7046d79e..1e86872c151f5d 100644
--- a/include/uapi/sound/firewire.h
+++ b/include/uapi/sound/firewire.h
@@ -14,6 +14,7 @@
#define SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION 0x64776479
#define SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL 0x7473636d
#define SNDRV_FIREWIRE_EVENT_MOTU_REGISTER_DSP_CHANGE 0x4d545244
+#define SNDRV_FIREWIRE_EVENT_FF400_MESSAGE 0x4f6c6761
struct snd_firewire_event_common {
unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
@@ -72,6 +73,30 @@ struct snd_firewire_event_motu_register_dsp_change {
__u32 changes[]; /* Encoded event for change of register DSP. */
};
+/**
+ * struct snd_firewire_event_ff400_message - the container for message from Fireface 400 when
+ * operating hardware knob.
+ *
+ * @type: Fixed to SNDRV_FIREWIRE_EVENT_FF400_MESSAGE.
+ * @message_count: The number of messages.
+ * @messages.message: The messages expressing hardware knob operation.
+ * @messages.tstamp: The isochronous cycle at which the request subaction of asynchronous
+ * transaction was sent to deliver the message. It has 16 bit unsigned integer
+ * value. The higher 3 bits of value expresses the lower three bits of second
+ * field in the format of CYCLE_TIME, up to 7. The rest 13 bits expresses cycle
+ * field up to 7999.
+ *
+ * The structure expresses message transmitted by Fireface 400 when operating hardware knob.
+ */
+struct snd_firewire_event_ff400_message {
+ unsigned int type;
+ unsigned int message_count;
+ struct {
+ __u32 message;
+ __u32 tstamp;
+ } messages[];
+};
+
union snd_firewire_event {
struct snd_firewire_event_common common;
struct snd_firewire_event_lock_status lock_status;
@@ -81,6 +106,7 @@ union snd_firewire_event {
struct snd_firewire_event_tascam_control tascam_control;
struct snd_firewire_event_motu_notification motu_notification;
struct snd_firewire_event_motu_register_dsp_change motu_register_dsp_change;
+ struct snd_firewire_event_ff400_message ff400_message;
};
diff --git a/include/uapi/sound/intel/avs/tokens.h b/include/uapi/sound/intel/avs/tokens.h
index 754f02b2f4440d..4ffe546aa409a1 100644
--- a/include/uapi/sound/intel/avs/tokens.h
+++ b/include/uapi/sound/intel/avs/tokens.h
@@ -108,6 +108,7 @@ enum avs_tplg_token {
AVS_TKN_MOD_CORE_ID_U8 = 1704,
AVS_TKN_MOD_PROC_DOMAIN_U8 = 1705,
AVS_TKN_MOD_MODCFG_EXT_ID_U32 = 1706,
+ AVS_TKN_MOD_KCONTROL_ID_U32 = 1707,
/* struct avs_tplg_path_template */
AVS_TKN_PATH_TMPL_ID_U32 = 1801,
@@ -121,6 +122,9 @@ enum avs_tplg_token {
AVS_TKN_PIN_FMT_INDEX_U32 = 2201,
AVS_TKN_PIN_FMT_IOBS_U32 = 2202,
AVS_TKN_PIN_FMT_AFMT_ID_U32 = 2203,
+
+ /* struct avs_tplg_kcontrol */
+ AVS_TKN_KCONTROL_ID_U32 = 2301,
};
#endif
diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h
index 5cf81dff60aa07..727084cd79be42 100644
--- a/include/ufs/ufshcd.h
+++ b/include/ufs/ufshcd.h
@@ -808,6 +808,7 @@ struct ufs_hba_monitor {
* @urgent_bkops_lvl: keeps track of urgent bkops level for device
* @is_urgent_bkops_lvl_checked: keeps track if the urgent bkops level for
* device is known or not.
+ * @wb_mutex: used to serialize devfreq and sysfs write booster toggling
* @clk_scaling_lock: used to serialize device commands and clock scaling
* @desc_size: descriptor sizes reported by device
* @scsi_block_reqs_cnt: reference counting for scsi block requests
@@ -951,6 +952,7 @@ struct ufs_hba {
enum bkops_status urgent_bkops_lvl;
bool is_urgent_bkops_lvl_checked;
+ struct mutex wb_mutex;
struct rw_semaphore clk_scaling_lock;
unsigned char desc_size[QUERY_DESC_IDN_MAX];
atomic_t scsi_block_reqs_cnt;
diff --git a/include/xen/xenbus.h b/include/xen/xenbus.h
index eaa932b99d8ac2..ad4fb4eab753d4 100644
--- a/include/xen/xenbus.h
+++ b/include/xen/xenbus.h
@@ -117,7 +117,7 @@ struct xenbus_driver {
const struct xenbus_device_id *id);
void (*otherend_changed)(struct xenbus_device *dev,
enum xenbus_state backend_state);
- int (*remove)(struct xenbus_device *dev);
+ void (*remove)(struct xenbus_device *dev);
int (*suspend)(struct xenbus_device *dev);
int (*resume)(struct xenbus_device *dev);
int (*uevent)(struct xenbus_device *, struct kobj_uevent_env *);
diff --git a/init/Kconfig b/init/Kconfig
index 7e5c3ddc341de3..44e90b28a30f12 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -204,7 +204,7 @@ config LOCALVERSION_AUTO
appended after any matching localversion* files, and after the value
set in CONFIG_LOCALVERSION.
- (The actual string used here is the first eight characters produced
+ (The actual string used here is the first 12 characters produced
by running the command:
$ git rev-parse --verify HEAD
@@ -776,7 +776,7 @@ config PRINTK_SAFE_LOG_BUF_SHIFT
depends on PRINTK
help
Select the size of an alternate printk per-CPU buffer where messages
- printed from usafe contexts are temporary stored. One example would
+ printed from unsafe contexts are temporary stored. One example would
be NMI messages, another one - printk recursion. The messages are
copied to the main log buffer in a safe context to avoid a deadlock.
The value defines the size as a power of 2.
@@ -894,13 +894,17 @@ config CC_IMPLICIT_FALLTHROUGH
default "-Wimplicit-fallthrough=5" if CC_IS_GCC && $(cc-option,-Wimplicit-fallthrough=5)
default "-Wimplicit-fallthrough" if CC_IS_CLANG && $(cc-option,-Wunreachable-code-fallthrough)
-# Currently, disable gcc-12 array-bounds globally.
+# Currently, disable gcc-11,12 array-bounds globally.
# We may want to target only particular configurations some day.
+config GCC11_NO_ARRAY_BOUNDS
+ def_bool y
+
config GCC12_NO_ARRAY_BOUNDS
def_bool y
config CC_NO_ARRAY_BOUNDS
bool
+ default y if CC_IS_GCC && GCC_VERSION >= 110000 && GCC_VERSION < 120000 && GCC11_NO_ARRAY_BOUNDS
default y if CC_IS_GCC && GCC_VERSION >= 120000 && GCC_VERSION < 130000 && GCC12_NO_ARRAY_BOUNDS
#
diff --git a/init/Makefile b/init/Makefile
index 8316c23bead268..26de459006c4ee 100644
--- a/init/Makefile
+++ b/init/Makefile
@@ -59,3 +59,4 @@ include/generated/utsversion.h: FORCE
$(obj)/version-timestamp.o: include/generated/utsversion.h
CFLAGS_version-timestamp.o := -include include/generated/utsversion.h
+KASAN_SANITIZE_version-timestamp.o := n
diff --git a/init/version-timestamp.c b/init/version-timestamp.c
index 179e93bae5390e..043cbf80a766de 100644
--- a/init/version-timestamp.c
+++ b/init/version-timestamp.c
@@ -2,7 +2,6 @@
#include <generated/compile.h>
#include <generated/utsrelease.h>
-#include <linux/version.h>
#include <linux/proc_ns.h>
#include <linux/refcount.h>
#include <linux/uts.h>
diff --git a/io_uring/cancel.c b/io_uring/cancel.c
index 2291a53cdabd11..b4f5dfacc0c31d 100644
--- a/io_uring/cancel.c
+++ b/io_uring/cancel.c
@@ -288,24 +288,23 @@ int io_sync_cancel(struct io_ring_ctx *ctx, void __user *arg)
ret = __io_sync_cancel(current->io_uring, &cd, sc.fd);
+ mutex_unlock(&ctx->uring_lock);
if (ret != -EALREADY)
break;
- mutex_unlock(&ctx->uring_lock);
ret = io_run_task_work_sig(ctx);
- if (ret < 0) {
- mutex_lock(&ctx->uring_lock);
+ if (ret < 0)
break;
- }
ret = schedule_hrtimeout(&timeout, HRTIMER_MODE_ABS);
- mutex_lock(&ctx->uring_lock);
if (!ret) {
ret = -ETIME;
break;
}
+ mutex_lock(&ctx->uring_lock);
} while (1);
finish_wait(&ctx->cq_wait, &wait);
+ mutex_lock(&ctx->uring_lock);
if (ret == -ENOENT || ret > 0)
ret = 0;
diff --git a/io_uring/fdinfo.c b/io_uring/fdinfo.c
index 2e04850a657b07..882bd56b01ed0f 100644
--- a/io_uring/fdinfo.c
+++ b/io_uring/fdinfo.c
@@ -170,12 +170,11 @@ static __cold void __io_uring_show_fdinfo(struct io_ring_ctx *ctx,
xa_for_each(&ctx->personalities, index, cred)
io_uring_show_cred(m, index, cred);
}
- if (has_lock)
- mutex_unlock(&ctx->uring_lock);
seq_puts(m, "PollList:\n");
for (i = 0; i < (1U << ctx->cancel_table.hash_bits); i++) {
struct io_hash_bucket *hb = &ctx->cancel_table.hbs[i];
+ struct io_hash_bucket *hbl = &ctx->cancel_table_locked.hbs[i];
struct io_kiocb *req;
spin_lock(&hb->lock);
@@ -183,8 +182,17 @@ static __cold void __io_uring_show_fdinfo(struct io_ring_ctx *ctx,
seq_printf(m, " op=%d, task_works=%d\n", req->opcode,
task_work_pending(req->task));
spin_unlock(&hb->lock);
+
+ if (!has_lock)
+ continue;
+ hlist_for_each_entry(req, &hbl->list, hash_node)
+ seq_printf(m, " op=%d, task_works=%d\n", req->opcode,
+ task_work_pending(req->task));
}
+ if (has_lock)
+ mutex_unlock(&ctx->uring_lock);
+
seq_puts(m, "CqOverflowList:\n");
spin_lock(&ctx->completion_lock);
list_for_each_entry(ocqe, &ctx->cq_overflow_list, list) {
diff --git a/io_uring/io-wq.c b/io_uring/io-wq.c
index 6f1d0e5df23ad8..411bb2d1acd452 100644
--- a/io_uring/io-wq.c
+++ b/io_uring/io-wq.c
@@ -1230,6 +1230,12 @@ static void io_wq_cancel_tw_create(struct io_wq *wq)
worker = container_of(cb, struct io_worker, create_work);
io_worker_cancel_cb(worker);
+ /*
+ * Only the worker continuation helper has worker allocated and
+ * hence needs freeing.
+ */
+ if (cb->func == create_worker_cont)
+ kfree(worker);
}
}
diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c
index ff2bbac1a10f4a..db623b3185c823 100644
--- a/io_uring/io_uring.c
+++ b/io_uring/io_uring.c
@@ -677,16 +677,20 @@ static void __io_cqring_overflow_flush(struct io_ring_ctx *ctx)
io_cq_unlock_post(ctx);
}
+static void io_cqring_do_overflow_flush(struct io_ring_ctx *ctx)
+{
+ /* iopoll syncs against uring_lock, not completion_lock */
+ if (ctx->flags & IORING_SETUP_IOPOLL)
+ mutex_lock(&ctx->uring_lock);
+ __io_cqring_overflow_flush(ctx);
+ if (ctx->flags & IORING_SETUP_IOPOLL)
+ mutex_unlock(&ctx->uring_lock);
+}
+
static void io_cqring_overflow_flush(struct io_ring_ctx *ctx)
{
- if (test_bit(IO_CHECK_CQ_OVERFLOW_BIT, &ctx->check_cq)) {
- /* iopoll syncs against uring_lock, not completion_lock */
- if (ctx->flags & IORING_SETUP_IOPOLL)
- mutex_lock(&ctx->uring_lock);
- __io_cqring_overflow_flush(ctx);
- if (ctx->flags & IORING_SETUP_IOPOLL)
- mutex_unlock(&ctx->uring_lock);
- }
+ if (test_bit(IO_CHECK_CQ_OVERFLOW_BIT, &ctx->check_cq))
+ io_cqring_do_overflow_flush(ctx);
}
void __io_put_task(struct task_struct *task, int nr)
@@ -727,6 +731,8 @@ static bool io_cqring_event_overflow(struct io_ring_ctx *ctx, u64 user_data,
size_t ocq_size = sizeof(struct io_overflow_cqe);
bool is_cqe32 = (ctx->flags & IORING_SETUP_CQE32);
+ lockdep_assert_held(&ctx->completion_lock);
+
if (is_cqe32)
ocq_size += sizeof(struct io_uring_cqe);
@@ -816,9 +822,6 @@ static bool io_fill_cqe_aux(struct io_ring_ctx *ctx, u64 user_data, s32 res,
{
struct io_uring_cqe *cqe;
- if (!ctx->task_complete)
- lockdep_assert_held(&ctx->completion_lock);
-
ctx->cq_extra++;
/*
@@ -1232,13 +1235,18 @@ static void io_req_local_work_add(struct io_kiocb *req)
{
struct io_ring_ctx *ctx = req->ctx;
- if (!llist_add(&req->io_task_work.node, &ctx->work_llist))
+ percpu_ref_get(&ctx->refs);
+
+ if (!llist_add(&req->io_task_work.node, &ctx->work_llist)) {
+ percpu_ref_put(&ctx->refs);
return;
+ }
/* need it for the following io_cqring_wake() */
smp_mb__after_atomic();
if (unlikely(atomic_read(&req->task->io_uring->in_idle))) {
io_move_task_work_from_local(ctx);
+ percpu_ref_put(&ctx->refs);
return;
}
@@ -1248,6 +1256,7 @@ static void io_req_local_work_add(struct io_kiocb *req)
if (ctx->has_evfd)
io_eventfd_signal(ctx);
__io_cqring_wake(ctx);
+ percpu_ref_put(&ctx->refs);
}
void __io_req_task_work_add(struct io_kiocb *req, bool allow_local)
@@ -1756,17 +1765,12 @@ queue:
}
spin_unlock(&ctx->completion_lock);
- ret = io_req_prep_async(req);
- if (ret) {
-fail:
- io_req_defer_failed(req, ret);
- return;
- }
io_prep_async_link(req);
de = kmalloc(sizeof(*de), GFP_KERNEL);
if (!de) {
ret = -ENOMEM;
- goto fail;
+ io_req_defer_failed(req, ret);
+ return;
}
spin_lock(&ctx->completion_lock);
@@ -2039,13 +2043,16 @@ static void io_queue_sqe_fallback(struct io_kiocb *req)
req->flags &= ~REQ_F_HARDLINK;
req->flags |= REQ_F_LINK;
io_req_defer_failed(req, req->cqe.res);
- } else if (unlikely(req->ctx->drain_active)) {
- io_drain_req(req);
} else {
int ret = io_req_prep_async(req);
- if (unlikely(ret))
+ if (unlikely(ret)) {
io_req_defer_failed(req, ret);
+ return;
+ }
+
+ if (unlikely(req->ctx->drain_active))
+ io_drain_req(req);
else
io_queue_iowq(req, NULL);
}
@@ -2461,7 +2468,7 @@ int io_run_task_work_sig(struct io_ring_ctx *ctx)
/* when returns >0, the caller should retry */
static inline int io_cqring_wait_schedule(struct io_ring_ctx *ctx,
struct io_wait_queue *iowq,
- ktime_t timeout)
+ ktime_t *timeout)
{
int ret;
unsigned long check_cq;
@@ -2479,7 +2486,7 @@ static inline int io_cqring_wait_schedule(struct io_ring_ctx *ctx,
if (check_cq & BIT(IO_CHECK_CQ_DROPPED_BIT))
return -EBADR;
}
- if (!schedule_hrtimeout(&timeout, HRTIMER_MODE_ABS))
+ if (!schedule_hrtimeout(timeout, HRTIMER_MODE_ABS))
return -ETIME;
/*
@@ -2549,10 +2556,13 @@ static int io_cqring_wait(struct io_ring_ctx *ctx, int min_events,
trace_io_uring_cqring_wait(ctx, min_events);
do {
- io_cqring_overflow_flush(ctx);
+ if (test_bit(IO_CHECK_CQ_OVERFLOW_BIT, &ctx->check_cq)) {
+ finish_wait(&ctx->cq_wait, &iowq.wq);
+ io_cqring_do_overflow_flush(ctx);
+ }
prepare_to_wait_exclusive(&ctx->cq_wait, &iowq.wq,
TASK_INTERRUPTIBLE);
- ret = io_cqring_wait_schedule(ctx, &iowq, timeout);
+ ret = io_cqring_wait_schedule(ctx, &iowq, &timeout);
if (__io_cqring_events_user(ctx) >= min_events)
break;
cond_resched();
@@ -3662,7 +3672,7 @@ static __cold int io_uring_create(unsigned entries, struct io_uring_params *p,
if (ctx->flags & IORING_SETUP_SINGLE_ISSUER
&& !(ctx->flags & IORING_SETUP_R_DISABLED))
- ctx->submitter_task = get_task_struct(current);
+ WRITE_ONCE(ctx->submitter_task, get_task_struct(current));
file = io_uring_get_file(ctx);
if (IS_ERR(file)) {
@@ -3856,7 +3866,7 @@ static int io_register_enable_rings(struct io_ring_ctx *ctx)
return -EBADFD;
if (ctx->flags & IORING_SETUP_SINGLE_ISSUER && !ctx->submitter_task)
- ctx->submitter_task = get_task_struct(current);
+ WRITE_ONCE(ctx->submitter_task, get_task_struct(current));
if (ctx->restrictions.registered)
ctx->restricted = 1;
@@ -4013,8 +4023,6 @@ static int __io_uring_register(struct io_ring_ctx *ctx, unsigned opcode,
return -EEXIST;
if (ctx->restricted) {
- if (opcode >= IORING_REGISTER_LAST)
- return -EINVAL;
opcode = array_index_nospec(opcode, IORING_REGISTER_LAST);
if (!test_bit(opcode, ctx->restrictions.register_op))
return -EACCES;
@@ -4170,6 +4178,9 @@ SYSCALL_DEFINE4(io_uring_register, unsigned int, fd, unsigned int, opcode,
long ret = -EBADF;
struct fd f;
+ if (opcode >= IORING_REGISTER_LAST)
+ return -EINVAL;
+
f = fdget(fd);
if (!f.file)
return -EBADF;
diff --git a/io_uring/io_uring.h b/io_uring/io_uring.h
index e9f0d41ebb9960..ab4b2a1c3b7e80 100644
--- a/io_uring/io_uring.h
+++ b/io_uring/io_uring.h
@@ -79,6 +79,19 @@ bool __io_alloc_req_refill(struct io_ring_ctx *ctx);
bool io_match_task_safe(struct io_kiocb *head, struct task_struct *task,
bool cancel_all);
+#define io_lockdep_assert_cq_locked(ctx) \
+ do { \
+ if (ctx->flags & IORING_SETUP_IOPOLL) { \
+ lockdep_assert_held(&ctx->uring_lock); \
+ } else if (!ctx->task_complete) { \
+ lockdep_assert_held(&ctx->completion_lock); \
+ } else if (ctx->submitter_task->flags & PF_EXITING) { \
+ lockdep_assert(current_work()); \
+ } else { \
+ lockdep_assert(current == ctx->submitter_task); \
+ } \
+ } while (0)
+
static inline void io_req_task_work_add(struct io_kiocb *req)
{
__io_req_task_work_add(req, true);
@@ -92,6 +105,8 @@ void io_cq_unlock_post(struct io_ring_ctx *ctx);
static inline struct io_uring_cqe *io_get_cqe_overflow(struct io_ring_ctx *ctx,
bool overflow)
{
+ io_lockdep_assert_cq_locked(ctx);
+
if (likely(ctx->cqe_cached < ctx->cqe_sentinel)) {
struct io_uring_cqe *cqe = ctx->cqe_cached;
diff --git a/io_uring/msg_ring.c b/io_uring/msg_ring.c
index 2d3cd945a531b9..15602a136821b5 100644
--- a/io_uring/msg_ring.c
+++ b/io_uring/msg_ring.c
@@ -25,6 +25,28 @@ struct io_msg {
u32 flags;
};
+static void io_double_unlock_ctx(struct io_ring_ctx *octx)
+{
+ mutex_unlock(&octx->uring_lock);
+}
+
+static int io_double_lock_ctx(struct io_ring_ctx *octx,
+ unsigned int issue_flags)
+{
+ /*
+ * To ensure proper ordering between the two ctxs, we can only
+ * attempt a trylock on the target. If that fails and we already have
+ * the source ctx lock, punt to io-wq.
+ */
+ if (!(issue_flags & IO_URING_F_UNLOCKED)) {
+ if (!mutex_trylock(&octx->uring_lock))
+ return -EAGAIN;
+ return 0;
+ }
+ mutex_lock(&octx->uring_lock);
+ return 0;
+}
+
void io_msg_ring_cleanup(struct io_kiocb *req)
{
struct io_msg *msg = io_kiocb_to_cmd(req, struct io_msg);
@@ -36,6 +58,29 @@ void io_msg_ring_cleanup(struct io_kiocb *req)
msg->src_file = NULL;
}
+static inline bool io_msg_need_remote(struct io_ring_ctx *target_ctx)
+{
+ if (!target_ctx->task_complete)
+ return false;
+ return current != target_ctx->submitter_task;
+}
+
+static int io_msg_exec_remote(struct io_kiocb *req, task_work_func_t func)
+{
+ struct io_ring_ctx *ctx = req->file->private_data;
+ struct io_msg *msg = io_kiocb_to_cmd(req, struct io_msg);
+ struct task_struct *task = READ_ONCE(ctx->submitter_task);
+
+ if (unlikely(!task))
+ return -EOWNERDEAD;
+
+ init_task_work(&msg->tw, func);
+ if (task_work_add(ctx->submitter_task, &msg->tw, TWA_SIGNAL))
+ return -EOWNERDEAD;
+
+ return IOU_ISSUE_SKIP_COMPLETE;
+}
+
static void io_msg_tw_complete(struct callback_head *head)
{
struct io_msg *msg = container_of(head, struct io_msg, tw);
@@ -43,61 +88,54 @@ static void io_msg_tw_complete(struct callback_head *head)
struct io_ring_ctx *target_ctx = req->file->private_data;
int ret = 0;
- if (current->flags & PF_EXITING)
+ if (current->flags & PF_EXITING) {
ret = -EOWNERDEAD;
- else if (!io_post_aux_cqe(target_ctx, msg->user_data, msg->len, 0))
- ret = -EOVERFLOW;
+ } else {
+ /*
+ * If the target ring is using IOPOLL mode, then we need to be
+ * holding the uring_lock for posting completions. Other ring
+ * types rely on the regular completion locking, which is
+ * handled while posting.
+ */
+ if (target_ctx->flags & IORING_SETUP_IOPOLL)
+ mutex_lock(&target_ctx->uring_lock);
+ if (!io_post_aux_cqe(target_ctx, msg->user_data, msg->len, 0))
+ ret = -EOVERFLOW;
+ if (target_ctx->flags & IORING_SETUP_IOPOLL)
+ mutex_unlock(&target_ctx->uring_lock);
+ }
if (ret < 0)
req_set_fail(req);
io_req_queue_tw_complete(req, ret);
}
-static int io_msg_ring_data(struct io_kiocb *req)
+static int io_msg_ring_data(struct io_kiocb *req, unsigned int issue_flags)
{
struct io_ring_ctx *target_ctx = req->file->private_data;
struct io_msg *msg = io_kiocb_to_cmd(req, struct io_msg);
+ int ret;
if (msg->src_fd || msg->dst_fd || msg->flags)
return -EINVAL;
+ if (target_ctx->flags & IORING_SETUP_R_DISABLED)
+ return -EBADFD;
- if (target_ctx->task_complete && current != target_ctx->submitter_task) {
- init_task_work(&msg->tw, io_msg_tw_complete);
- if (task_work_add(target_ctx->submitter_task, &msg->tw,
- TWA_SIGNAL_NO_IPI))
- return -EOWNERDEAD;
-
- atomic_or(IORING_SQ_TASKRUN, &target_ctx->rings->sq_flags);
- return IOU_ISSUE_SKIP_COMPLETE;
- }
-
- if (io_post_aux_cqe(target_ctx, msg->user_data, msg->len, 0))
- return 0;
+ if (io_msg_need_remote(target_ctx))
+ return io_msg_exec_remote(req, io_msg_tw_complete);
- return -EOVERFLOW;
-}
-
-static void io_double_unlock_ctx(struct io_ring_ctx *octx,
- unsigned int issue_flags)
-{
- mutex_unlock(&octx->uring_lock);
-}
-
-static int io_double_lock_ctx(struct io_ring_ctx *octx,
- unsigned int issue_flags)
-{
- /*
- * To ensure proper ordering between the two ctxs, we can only
- * attempt a trylock on the target. If that fails and we already have
- * the source ctx lock, punt to io-wq.
- */
- if (!(issue_flags & IO_URING_F_UNLOCKED)) {
- if (!mutex_trylock(&octx->uring_lock))
+ ret = -EOVERFLOW;
+ if (target_ctx->flags & IORING_SETUP_IOPOLL) {
+ if (unlikely(io_double_lock_ctx(target_ctx, issue_flags)))
return -EAGAIN;
- return 0;
+ if (io_post_aux_cqe(target_ctx, msg->user_data, msg->len, 0))
+ ret = 0;
+ io_double_unlock_ctx(target_ctx);
+ } else {
+ if (io_post_aux_cqe(target_ctx, msg->user_data, msg->len, 0))
+ ret = 0;
}
- mutex_lock(&octx->uring_lock);
- return 0;
+ return ret;
}
static struct file *io_msg_grab_file(struct io_kiocb *req, unsigned int issue_flags)
@@ -148,7 +186,7 @@ static int io_msg_install_complete(struct io_kiocb *req, unsigned int issue_flag
if (!io_post_aux_cqe(target_ctx, msg->user_data, msg->len, 0))
ret = -EOVERFLOW;
out_unlock:
- io_double_unlock_ctx(target_ctx, issue_flags);
+ io_double_unlock_ctx(target_ctx);
return ret;
}
@@ -174,6 +212,8 @@ static int io_msg_send_fd(struct io_kiocb *req, unsigned int issue_flags)
if (target_ctx == ctx)
return -EINVAL;
+ if (target_ctx->flags & IORING_SETUP_R_DISABLED)
+ return -EBADFD;
if (!src_file) {
src_file = io_msg_grab_file(req, issue_flags);
if (!src_file)
@@ -182,14 +222,8 @@ static int io_msg_send_fd(struct io_kiocb *req, unsigned int issue_flags)
req->flags |= REQ_F_NEED_CLEANUP;
}
- if (target_ctx->task_complete && current != target_ctx->submitter_task) {
- init_task_work(&msg->tw, io_msg_tw_fd_complete);
- if (task_work_add(target_ctx->submitter_task, &msg->tw,
- TWA_SIGNAL))
- return -EOWNERDEAD;
-
- return IOU_ISSUE_SKIP_COMPLETE;
- }
+ if (io_msg_need_remote(target_ctx))
+ return io_msg_exec_remote(req, io_msg_tw_fd_complete);
return io_msg_install_complete(req, issue_flags);
}
@@ -224,7 +258,7 @@ int io_msg_ring(struct io_kiocb *req, unsigned int issue_flags)
switch (msg->cmd) {
case IORING_MSG_DATA:
- ret = io_msg_ring_data(req);
+ ret = io_msg_ring_data(req, issue_flags);
break;
case IORING_MSG_SEND_FD:
ret = io_msg_send_fd(req, issue_flags);
diff --git a/io_uring/net.c b/io_uring/net.c
index fbc34a7c274394..90326b27996579 100644
--- a/io_uring/net.c
+++ b/io_uring/net.c
@@ -62,6 +62,7 @@ struct io_sr_msg {
u16 flags;
/* initialised and used only by !msg send variants */
u16 addr_len;
+ u16 buf_group;
void __user *addr;
/* used only for send zerocopy */
struct io_kiocb *notif;
@@ -580,6 +581,15 @@ int io_recvmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe)
if (req->opcode == IORING_OP_RECV && sr->len)
return -EINVAL;
req->flags |= REQ_F_APOLL_MULTISHOT;
+ /*
+ * Store the buffer group for this multishot receive separately,
+ * as if we end up doing an io-wq based issue that selects a
+ * buffer, it has to be committed immediately and that will
+ * clear ->buf_list. This means we lose the link to the buffer
+ * list, and the eventual buffer put on completion then cannot
+ * restore it.
+ */
+ sr->buf_group = req->buf_index;
}
#ifdef CONFIG_COMPAT
@@ -596,6 +606,7 @@ static inline void io_recv_prep_retry(struct io_kiocb *req)
sr->done_io = 0;
sr->len = 0; /* get from the provided buffer */
+ req->buf_index = sr->buf_group;
}
/*
diff --git a/io_uring/poll.c b/io_uring/poll.c
index ee7da6150ec41a..2ac1366adbd77e 100644
--- a/io_uring/poll.c
+++ b/io_uring/poll.c
@@ -223,21 +223,22 @@ enum {
IOU_POLL_DONE = 0,
IOU_POLL_NO_ACTION = 1,
IOU_POLL_REMOVE_POLL_USE_RES = 2,
+ IOU_POLL_REISSUE = 3,
};
/*
* All poll tw should go through this. Checks for poll events, manages
* references, does rewait, etc.
*
- * Returns a negative error on failure. IOU_POLL_NO_ACTION when no action require,
- * which is either spurious wakeup or multishot CQE is served.
- * IOU_POLL_DONE when it's done with the request, then the mask is stored in req->cqe.res.
- * IOU_POLL_REMOVE_POLL_USE_RES indicates to remove multishot poll and that the result
- * is stored in req->cqe.
+ * Returns a negative error on failure. IOU_POLL_NO_ACTION when no action
+ * require, which is either spurious wakeup or multishot CQE is served.
+ * IOU_POLL_DONE when it's done with the request, then the mask is stored in
+ * req->cqe.res. IOU_POLL_REMOVE_POLL_USE_RES indicates to remove multishot
+ * poll and that the result is stored in req->cqe.
*/
static int io_poll_check_events(struct io_kiocb *req, bool *locked)
{
- int v, ret;
+ int v;
/* req->task == current here, checking PF_EXITING is safe */
if (unlikely(req->task->flags & PF_EXITING))
@@ -276,10 +277,19 @@ static int io_poll_check_events(struct io_kiocb *req, bool *locked)
if (!req->cqe.res) {
struct poll_table_struct pt = { ._key = req->apoll_events };
req->cqe.res = vfs_poll(req->file, &pt) & req->apoll_events;
+ /*
+ * We got woken with a mask, but someone else got to
+ * it first. The above vfs_poll() doesn't add us back
+ * to the waitqueue, so if we get nothing back, we
+ * should be safe and attempt a reissue.
+ */
+ if (unlikely(!req->cqe.res)) {
+ /* Multishot armed need not reissue */
+ if (!(req->apoll_events & EPOLLONESHOT))
+ continue;
+ return IOU_POLL_REISSUE;
+ }
}
-
- if ((unlikely(!req->cqe.res)))
- continue;
if (req->apoll_events & EPOLLONESHOT)
return IOU_POLL_DONE;
@@ -294,7 +304,7 @@ static int io_poll_check_events(struct io_kiocb *req, bool *locked)
return IOU_POLL_REMOVE_POLL_USE_RES;
}
} else {
- ret = io_poll_issue(req, locked);
+ int ret = io_poll_issue(req, locked);
if (ret == IOU_STOP_MULTISHOT)
return IOU_POLL_REMOVE_POLL_USE_RES;
if (ret < 0)
@@ -330,6 +340,9 @@ static void io_poll_task_func(struct io_kiocb *req, bool *locked)
poll = io_kiocb_to_cmd(req, struct io_poll);
req->cqe.res = mangle_poll(req->cqe.res & poll->events);
+ } else if (ret == IOU_POLL_REISSUE) {
+ io_req_task_submit(req, locked);
+ return;
} else if (ret != IOU_POLL_REMOVE_POLL_USE_RES) {
req->cqe.res = ret;
req_set_fail(req);
@@ -342,7 +355,7 @@ static void io_poll_task_func(struct io_kiocb *req, bool *locked)
if (ret == IOU_POLL_REMOVE_POLL_USE_RES)
io_req_task_complete(req, locked);
- else if (ret == IOU_POLL_DONE)
+ else if (ret == IOU_POLL_DONE || ret == IOU_POLL_REISSUE)
io_req_task_submit(req, locked);
else
io_req_defer_failed(req, ret);
@@ -533,6 +546,14 @@ static bool io_poll_can_finish_inline(struct io_kiocb *req,
return pt->owning || io_poll_get_ownership(req);
}
+static void io_poll_add_hash(struct io_kiocb *req)
+{
+ if (req->flags & REQ_F_HASH_LOCKED)
+ io_poll_req_insert_locked(req);
+ else
+ io_poll_req_insert(req);
+}
+
/*
* Returns 0 when it's handed over for polling. The caller owns the requests if
* it returns non-zero, but otherwise should not touch it. Negative values
@@ -591,18 +612,17 @@ static int __io_arm_poll_handler(struct io_kiocb *req,
if (mask &&
((poll->events & (EPOLLET|EPOLLONESHOT)) == (EPOLLET|EPOLLONESHOT))) {
- if (!io_poll_can_finish_inline(req, ipt))
+ if (!io_poll_can_finish_inline(req, ipt)) {
+ io_poll_add_hash(req);
return 0;
+ }
io_poll_remove_entries(req);
ipt->result_mask = mask;
/* no one else has access to the req, forget about the ref */
return 1;
}
- if (req->flags & REQ_F_HASH_LOCKED)
- io_poll_req_insert_locked(req);
- else
- io_poll_req_insert(req);
+ io_poll_add_hash(req);
if (mask && (poll->events & EPOLLET) &&
io_poll_can_finish_inline(req, ipt)) {
diff --git a/io_uring/rw.c b/io_uring/rw.c
index 8227af2e1c0f5e..9c3ddd46a1adc5 100644
--- a/io_uring/rw.c
+++ b/io_uring/rw.c
@@ -1062,7 +1062,11 @@ int io_do_iopoll(struct io_ring_ctx *ctx, bool force_nonspin)
continue;
req->cqe.flags = io_put_kbuf(req, 0);
- io_fill_cqe_req(req->ctx, req);
+ if (unlikely(!__io_fill_cqe_req(ctx, req))) {
+ spin_lock(&ctx->completion_lock);
+ io_req_cqe_overflow(req);
+ spin_unlock(&ctx->completion_lock);
+ }
}
if (unlikely(!nr_events))
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 9ea42a45da4703..e14c822f8911c9 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -51,7 +51,6 @@ BTF_SET_END(bpf_lsm_current_hooks)
*/
BTF_SET_START(bpf_lsm_locked_sockopt_hooks)
#ifdef CONFIG_SECURITY_NETWORK
-BTF_ID(func, bpf_lsm_socket_sock_rcv_skb)
BTF_ID(func, bpf_lsm_sock_graft)
BTF_ID(func, bpf_lsm_inet_csk_clone)
BTF_ID(func, bpf_lsm_inet_conn_established)
@@ -351,8 +350,10 @@ BTF_ID(func, bpf_lsm_bpf_prog_alloc_security)
BTF_ID(func, bpf_lsm_bpf_prog_free_security)
BTF_ID(func, bpf_lsm_file_alloc_security)
BTF_ID(func, bpf_lsm_file_free_security)
+#ifdef CONFIG_SECURITY_NETWORK
BTF_ID(func, bpf_lsm_sk_alloc_security)
BTF_ID(func, bpf_lsm_sk_free_security)
+#endif /* CONFIG_SECURITY_NETWORK */
BTF_ID(func, bpf_lsm_task_free)
BTF_SET_END(untrusted_lsm_hooks)
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index f7dd8af0641338..b7017cae6fd1e2 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -7782,9 +7782,9 @@ int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_c
sort(tab->dtors, tab->cnt, sizeof(tab->dtors[0]), btf_id_cmp_func, NULL);
- return 0;
end:
- btf_free_dtor_kfunc_tab(btf);
+ if (ret)
+ btf_free_dtor_kfunc_tab(btf);
btf_put(btf);
return ret;
}
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index 5aa2b5525f7937..66bded14437735 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -152,7 +152,7 @@ static inline int htab_lock_bucket(const struct bpf_htab *htab,
{
unsigned long flags;
- hash = hash & HASHTAB_MAP_LOCK_MASK;
+ hash = hash & min_t(u32, HASHTAB_MAP_LOCK_MASK, htab->n_buckets - 1);
preempt_disable();
if (unlikely(__this_cpu_inc_return(*(htab->map_locked[hash])) != 1)) {
@@ -171,7 +171,7 @@ static inline void htab_unlock_bucket(const struct bpf_htab *htab,
struct bucket *b, u32 hash,
unsigned long flags)
{
- hash = hash & HASHTAB_MAP_LOCK_MASK;
+ hash = hash & min_t(u32, HASHTAB_MAP_LOCK_MASK, htab->n_buckets - 1);
raw_spin_unlock_irqrestore(&b->raw_lock, flags);
__this_cpu_dec(*(htab->map_locked[hash]));
preempt_enable();
diff --git a/kernel/bpf/memalloc.c b/kernel/bpf/memalloc.c
index ebcc3dd0fa19cd..1db156405b68b3 100644
--- a/kernel/bpf/memalloc.c
+++ b/kernel/bpf/memalloc.c
@@ -71,7 +71,7 @@ static int bpf_mem_cache_idx(size_t size)
if (size <= 192)
return size_index[(size - 1) / 8] - 1;
- return fls(size - 1) - 1;
+ return fls(size - 1) - 2;
}
#define NUM_CACHES 11
diff --git a/kernel/bpf/offload.c b/kernel/bpf/offload.c
index 13e4efc971e6d2..190d9f9dc9870f 100644
--- a/kernel/bpf/offload.c
+++ b/kernel/bpf/offload.c
@@ -216,9 +216,6 @@ static void __bpf_prog_offload_destroy(struct bpf_prog *prog)
if (offload->dev_state)
offload->offdev->ops->destroy(prog);
- /* Make sure BPF_PROG_GET_NEXT_ID can't find this dead program */
- bpf_prog_free_id(prog, true);
-
list_del_init(&offload->offloads);
kfree(offload);
prog->aux->offload = NULL;
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 64131f88c55370..ecca9366c7a6f5 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -1972,7 +1972,7 @@ static void bpf_audit_prog(const struct bpf_prog *prog, unsigned int op)
return;
if (audit_enabled == AUDIT_OFF)
return;
- if (op == BPF_AUDIT_LOAD)
+ if (!in_irq() && !irqs_disabled())
ctx = audit_context();
ab = audit_log_start(ctx, GFP_ATOMIC, AUDIT_BPF);
if (unlikely(!ab))
@@ -2001,7 +2001,7 @@ static int bpf_prog_alloc_id(struct bpf_prog *prog)
return id > 0 ? 0 : id;
}
-void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock)
+void bpf_prog_free_id(struct bpf_prog *prog)
{
unsigned long flags;
@@ -2013,18 +2013,10 @@ void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock)
if (!prog->aux->id)
return;
- if (do_idr_lock)
- spin_lock_irqsave(&prog_idr_lock, flags);
- else
- __acquire(&prog_idr_lock);
-
+ spin_lock_irqsave(&prog_idr_lock, flags);
idr_remove(&prog_idr, prog->aux->id);
prog->aux->id = 0;
-
- if (do_idr_lock)
- spin_unlock_irqrestore(&prog_idr_lock, flags);
- else
- __release(&prog_idr_lock);
+ spin_unlock_irqrestore(&prog_idr_lock, flags);
}
static void __bpf_prog_put_rcu(struct rcu_head *rcu)
@@ -2067,17 +2059,15 @@ static void bpf_prog_put_deferred(struct work_struct *work)
prog = aux->prog;
perf_event_bpf_event(prog, PERF_BPF_EVENT_PROG_UNLOAD, 0);
bpf_audit_prog(prog, BPF_AUDIT_UNLOAD);
+ bpf_prog_free_id(prog);
__bpf_prog_put_noref(prog, true);
}
-static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)
+static void __bpf_prog_put(struct bpf_prog *prog)
{
struct bpf_prog_aux *aux = prog->aux;
if (atomic64_dec_and_test(&aux->refcnt)) {
- /* bpf_prog_free_id() must be called first */
- bpf_prog_free_id(prog, do_idr_lock);
-
if (in_irq() || irqs_disabled()) {
INIT_WORK(&aux->work, bpf_prog_put_deferred);
schedule_work(&aux->work);
@@ -2089,7 +2079,7 @@ static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)
void bpf_prog_put(struct bpf_prog *prog)
{
- __bpf_prog_put(prog, true);
+ __bpf_prog_put(prog);
}
EXPORT_SYMBOL_GPL(bpf_prog_put);
diff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c
index c2a2182ce57021..c4ab9d6cdbe9c8 100644
--- a/kernel/bpf/task_iter.c
+++ b/kernel/bpf/task_iter.c
@@ -438,6 +438,7 @@ struct bpf_iter_seq_task_vma_info {
*/
struct bpf_iter_seq_task_common common;
struct task_struct *task;
+ struct mm_struct *mm;
struct vm_area_struct *vma;
u32 tid;
unsigned long prev_vm_start;
@@ -456,16 +457,19 @@ task_vma_seq_get_next(struct bpf_iter_seq_task_vma_info *info)
enum bpf_task_vma_iter_find_op op;
struct vm_area_struct *curr_vma;
struct task_struct *curr_task;
+ struct mm_struct *curr_mm;
u32 saved_tid = info->tid;
/* If this function returns a non-NULL vma, it holds a reference to
- * the task_struct, and holds read lock on vma->mm->mmap_lock.
+ * the task_struct, holds a refcount on mm->mm_users, and holds
+ * read lock on vma->mm->mmap_lock.
* If this function returns NULL, it does not hold any reference or
* lock.
*/
if (info->task) {
curr_task = info->task;
curr_vma = info->vma;
+ curr_mm = info->mm;
/* In case of lock contention, drop mmap_lock to unblock
* the writer.
*
@@ -504,13 +508,15 @@ task_vma_seq_get_next(struct bpf_iter_seq_task_vma_info *info)
* 4.2) VMA2 and VMA2' covers different ranges, process
* VMA2'.
*/
- if (mmap_lock_is_contended(curr_task->mm)) {
+ if (mmap_lock_is_contended(curr_mm)) {
info->prev_vm_start = curr_vma->vm_start;
info->prev_vm_end = curr_vma->vm_end;
op = task_vma_iter_find_vma;
- mmap_read_unlock(curr_task->mm);
- if (mmap_read_lock_killable(curr_task->mm))
+ mmap_read_unlock(curr_mm);
+ if (mmap_read_lock_killable(curr_mm)) {
+ mmput(curr_mm);
goto finish;
+ }
} else {
op = task_vma_iter_next_vma;
}
@@ -535,42 +541,47 @@ again:
op = task_vma_iter_find_vma;
}
- if (!curr_task->mm)
+ curr_mm = get_task_mm(curr_task);
+ if (!curr_mm)
goto next_task;
- if (mmap_read_lock_killable(curr_task->mm))
+ if (mmap_read_lock_killable(curr_mm)) {
+ mmput(curr_mm);
goto finish;
+ }
}
switch (op) {
case task_vma_iter_first_vma:
- curr_vma = find_vma(curr_task->mm, 0);
+ curr_vma = find_vma(curr_mm, 0);
break;
case task_vma_iter_next_vma:
- curr_vma = find_vma(curr_task->mm, curr_vma->vm_end);
+ curr_vma = find_vma(curr_mm, curr_vma->vm_end);
break;
case task_vma_iter_find_vma:
/* We dropped mmap_lock so it is necessary to use find_vma
* to find the next vma. This is similar to the mechanism
* in show_smaps_rollup().
*/
- curr_vma = find_vma(curr_task->mm, info->prev_vm_end - 1);
+ curr_vma = find_vma(curr_mm, info->prev_vm_end - 1);
/* case 1) and 4.2) above just use curr_vma */
/* check for case 2) or case 4.1) above */
if (curr_vma &&
curr_vma->vm_start == info->prev_vm_start &&
curr_vma->vm_end == info->prev_vm_end)
- curr_vma = find_vma(curr_task->mm, curr_vma->vm_end);
+ curr_vma = find_vma(curr_mm, curr_vma->vm_end);
break;
}
if (!curr_vma) {
/* case 3) above, or case 2) 4.1) with vma->next == NULL */
- mmap_read_unlock(curr_task->mm);
+ mmap_read_unlock(curr_mm);
+ mmput(curr_mm);
goto next_task;
}
info->task = curr_task;
info->vma = curr_vma;
+ info->mm = curr_mm;
return curr_vma;
next_task:
@@ -579,6 +590,7 @@ next_task:
put_task_struct(curr_task);
info->task = NULL;
+ info->mm = NULL;
info->tid++;
goto again;
@@ -587,6 +599,7 @@ finish:
put_task_struct(curr_task);
info->task = NULL;
info->vma = NULL;
+ info->mm = NULL;
return NULL;
}
@@ -658,7 +671,9 @@ static void task_vma_seq_stop(struct seq_file *seq, void *v)
*/
info->prev_vm_start = ~0UL;
info->prev_vm_end = info->vma->vm_end;
- mmap_read_unlock(info->task->mm);
+ mmap_read_unlock(info->mm);
+ mmput(info->mm);
+ info->mm = NULL;
put_task_struct(info->task);
info->task = NULL;
}
diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index 11f5ec0b80165a..d0ed7d6f5eec50 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -488,6 +488,10 @@ again:
/* reset fops->func and fops->trampoline for re-register */
tr->fops->func = NULL;
tr->fops->trampoline = 0;
+
+ /* reset im->image memory attr for arch_prepare_bpf_trampoline */
+ set_memory_nx((long)im->image, 1);
+ set_memory_rw((long)im->image, 1);
goto again;
}
#endif
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index a5255a0dcbb681..7ee2188272597f 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1054,6 +1054,8 @@ static void print_insn_state(struct bpf_verifier_env *env,
*/
static void *copy_array(void *dst, const void *src, size_t n, size_t size, gfp_t flags)
{
+ size_t alloc_bytes;
+ void *orig = dst;
size_t bytes;
if (ZERO_OR_NULL_PTR(src))
@@ -1062,11 +1064,11 @@ static void *copy_array(void *dst, const void *src, size_t n, size_t size, gfp_t
if (unlikely(check_mul_overflow(n, size, &bytes)))
return NULL;
- if (ksize(dst) < ksize(src)) {
- kfree(dst);
- dst = kmalloc_track_caller(kmalloc_size_roundup(bytes), flags);
- if (!dst)
- return NULL;
+ alloc_bytes = max(ksize(orig), kmalloc_size_roundup(bytes));
+ dst = krealloc(orig, alloc_bytes, flags);
+ if (!dst) {
+ kfree(orig);
+ return NULL;
}
memcpy(dst, src, bytes);
@@ -2746,6 +2748,12 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx,
*/
if (insn->src_reg == 0 && is_callback_calling_function(insn->imm))
return -ENOTSUPP;
+ /* kfunc with imm==0 is invalid and fixup_kfunc_call will
+ * catch this error later. Make backtracking conservative
+ * with ENOTSUPP.
+ */
+ if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL && insn->imm == 0)
+ return -ENOTSUPP;
/* regular helper call sets R0 */
*reg_mask &= ~1;
if (*reg_mask & 0x3f) {
@@ -3235,13 +3243,24 @@ static bool __is_pointer_value(bool allow_ptr_leaks,
return reg->type != SCALAR_VALUE;
}
+/* Copy src state preserving dst->parent and dst->live fields */
+static void copy_register_state(struct bpf_reg_state *dst, const struct bpf_reg_state *src)
+{
+ struct bpf_reg_state *parent = dst->parent;
+ enum bpf_reg_liveness live = dst->live;
+
+ *dst = *src;
+ dst->parent = parent;
+ dst->live = live;
+}
+
static void save_register_state(struct bpf_func_state *state,
int spi, struct bpf_reg_state *reg,
int size)
{
int i;
- state->stack[spi].spilled_ptr = *reg;
+ copy_register_state(&state->stack[spi].spilled_ptr, reg);
if (size == BPF_REG_SIZE)
state->stack[spi].spilled_ptr.live |= REG_LIVE_WRITTEN;
@@ -3287,7 +3306,9 @@ 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++) {
- if (state->stack[spi].slot_type[i] == STACK_INVALID) {
+ u8 type = state->stack[spi].slot_type[i];
+
+ if (type != STACK_MISC && type != STACK_ZERO) {
sanitize = true;
break;
}
@@ -3567,7 +3588,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
*/
s32 subreg_def = state->regs[dst_regno].subreg_def;
- state->regs[dst_regno] = *reg;
+ copy_register_state(&state->regs[dst_regno], reg);
state->regs[dst_regno].subreg_def = subreg_def;
} else {
for (i = 0; i < size; i++) {
@@ -3588,7 +3609,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
if (dst_regno >= 0) {
/* restore register state from stack */
- state->regs[dst_regno] = *reg;
+ copy_register_state(&state->regs[dst_regno], reg);
/* mark reg as written since spilled pointer state likely
* has its liveness marks cleared by is_state_visited()
* which resets stack/reg liveness for state transitions
@@ -9582,7 +9603,7 @@ do_sim:
*/
if (!ptr_is_dst_reg) {
tmp = *dst_reg;
- *dst_reg = *ptr_reg;
+ copy_register_state(dst_reg, ptr_reg);
}
ret = sanitize_speculative_path(env, NULL, env->insn_idx + 1,
env->insn_idx);
@@ -10835,7 +10856,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
* to propagate min/max range.
*/
src_reg->id = ++env->id_gen;
- *dst_reg = *src_reg;
+ copy_register_state(dst_reg, src_reg);
dst_reg->live |= REG_LIVE_WRITTEN;
dst_reg->subreg_def = DEF_NOT_SUBREG;
} else {
@@ -10846,7 +10867,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
insn->src_reg);
return -EACCES;
} else if (src_reg->type == SCALAR_VALUE) {
- *dst_reg = *src_reg;
+ copy_register_state(dst_reg, src_reg);
/* Make sure ID is cleared otherwise
* dst_reg min/max could be incorrectly
* propagated into src_reg by find_equal_scalars()
@@ -11645,7 +11666,7 @@ static void find_equal_scalars(struct bpf_verifier_state *vstate,
bpf_for_each_reg_in_vstate(vstate, state, reg, ({
if (reg->type == SCALAR_VALUE && reg->id == known_reg->id)
- *reg = *known_reg;
+ copy_register_state(reg, known_reg);
}));
}
@@ -11822,10 +11843,17 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
* register B - not null
* for JNE A, B, ... - A is not null in the false branch;
* for JEQ A, B, ... - A is not null in the true branch.
+ *
+ * Since PTR_TO_BTF_ID points to a kernel struct that does
+ * not need to be null checked by the BPF program, i.e.,
+ * could be null even without PTR_MAYBE_NULL marking, so
+ * only propagate nullness when neither reg is that type.
*/
if (!is_jmp32 && BPF_SRC(insn->code) == BPF_X &&
__is_pointer_value(false, src_reg) && __is_pointer_value(false, dst_reg) &&
- type_may_be_null(src_reg->type) != type_may_be_null(dst_reg->type)) {
+ type_may_be_null(src_reg->type) != type_may_be_null(dst_reg->type) &&
+ base_type(src_reg->type) != PTR_TO_BTF_ID &&
+ base_type(dst_reg->type) != PTR_TO_BTF_ID) {
eq_branch_regs = NULL;
switch (opcode) {
case BPF_JEQ:
diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c
index a29c0b13706bb0..ca826bd1eba332 100644
--- a/kernel/cgroup/cpuset.c
+++ b/kernel/cgroup/cpuset.c
@@ -1205,12 +1205,13 @@ void rebuild_sched_domains(void)
/**
* update_tasks_cpumask - Update the cpumasks of tasks in the cpuset.
* @cs: the cpuset in which each task's cpus_allowed mask needs to be changed
+ * @new_cpus: the temp variable for the new effective_cpus mask
*
* Iterate through each task of @cs updating its cpus_allowed to the
* effective cpuset's. As this function is called with cpuset_rwsem held,
* cpuset membership stays stable.
*/
-static void update_tasks_cpumask(struct cpuset *cs)
+static void update_tasks_cpumask(struct cpuset *cs, struct cpumask *new_cpus)
{
struct css_task_iter it;
struct task_struct *task;
@@ -1224,7 +1225,10 @@ static void update_tasks_cpumask(struct cpuset *cs)
if (top_cs && (task->flags & PF_KTHREAD) &&
kthread_is_per_cpu(task))
continue;
- set_cpus_allowed_ptr(task, cs->effective_cpus);
+
+ cpumask_and(new_cpus, cs->effective_cpus,
+ task_cpu_possible_mask(task));
+ set_cpus_allowed_ptr(task, new_cpus);
}
css_task_iter_end(&it);
}
@@ -1346,7 +1350,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
* A parent can be left with no CPU as long as there is no
* task directly associated with the parent partition.
*/
- if (!cpumask_intersects(cs->cpus_allowed, parent->effective_cpus) &&
+ if (cpumask_subset(parent->effective_cpus, cs->cpus_allowed) &&
partition_is_populated(parent, cs))
return PERR_NOCPUS;
@@ -1509,7 +1513,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cs, int cmd,
spin_unlock_irq(&callback_lock);
if (adding || deleting)
- update_tasks_cpumask(parent);
+ update_tasks_cpumask(parent, tmp->new_cpus);
/*
* Set or clear CS_SCHED_LOAD_BALANCE when partcmd_update, if necessary.
@@ -1661,7 +1665,7 @@ update_parent_subparts:
WARN_ON(!is_in_v2_mode() &&
!cpumask_equal(cp->cpus_allowed, cp->effective_cpus));
- update_tasks_cpumask(cp);
+ update_tasks_cpumask(cp, tmp->new_cpus);
/*
* On legacy hierarchy, if the effective cpumask of any non-
@@ -2309,7 +2313,7 @@ static int update_prstate(struct cpuset *cs, int new_prs)
}
}
- update_tasks_cpumask(parent);
+ update_tasks_cpumask(parent, tmpmask.new_cpus);
if (parent->child_ecpus_count)
update_sibling_cpumasks(parent, cs, &tmpmask);
@@ -2324,6 +2328,7 @@ out:
new_prs = -new_prs;
spin_lock_irq(&callback_lock);
cs->partition_root_state = new_prs;
+ WRITE_ONCE(cs->prs_err, err);
spin_unlock_irq(&callback_lock);
/*
* Update child cpusets, if present.
@@ -3347,7 +3352,7 @@ hotplug_update_tasks_legacy(struct cpuset *cs,
* as the tasks will be migrated to an ancestor.
*/
if (cpus_updated && !cpumask_empty(cs->cpus_allowed))
- update_tasks_cpumask(cs);
+ update_tasks_cpumask(cs, new_cpus);
if (mems_updated && !nodes_empty(cs->mems_allowed))
update_tasks_nodemask(cs);
@@ -3384,7 +3389,7 @@ hotplug_update_tasks(struct cpuset *cs,
spin_unlock_irq(&callback_lock);
if (cpus_updated)
- update_tasks_cpumask(cs);
+ update_tasks_cpumask(cs, new_cpus);
if (mems_updated)
update_tasks_nodemask(cs);
}
@@ -3691,15 +3696,38 @@ void __init cpuset_init_smp(void)
* Description: Returns the cpumask_var_t cpus_allowed of the cpuset
* attached to the specified @tsk. Guaranteed to return some non-empty
* subset of cpu_online_mask, even if this means going outside the
- * tasks cpuset.
+ * tasks cpuset, except when the task is in the top cpuset.
**/
void cpuset_cpus_allowed(struct task_struct *tsk, struct cpumask *pmask)
{
unsigned long flags;
+ struct cpuset *cs;
spin_lock_irqsave(&callback_lock, flags);
- guarantee_online_cpus(tsk, pmask);
+ rcu_read_lock();
+
+ cs = task_cs(tsk);
+ if (cs != &top_cpuset)
+ guarantee_online_cpus(tsk, pmask);
+ /*
+ * Tasks in the top cpuset won't get update to their cpumasks
+ * when a hotplug online/offline event happens. So we include all
+ * offline cpus in the allowed cpu list.
+ */
+ if ((cs == &top_cpuset) || cpumask_empty(pmask)) {
+ const struct cpumask *possible_mask = task_cpu_possible_mask(tsk);
+
+ /*
+ * We first exclude cpus allocated to partitions. If there is no
+ * allowable online cpu left, we fall back to all possible cpus.
+ */
+ cpumask_andnot(pmask, possible_mask, top_cpuset.subparts_cpus);
+ if (!cpumask_intersects(pmask, cpu_online_mask))
+ cpumask_copy(pmask, possible_mask);
+ }
+
+ rcu_read_unlock();
spin_unlock_irqrestore(&callback_lock, flags);
}
diff --git a/kernel/events/core.c b/kernel/events/core.c
index eacc3702654d5c..c4be13e50547bf 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -380,7 +380,6 @@ enum event_type_t {
/*
* perf_sched_events : >0 events exist
- * perf_cgroup_events: >0 per-cpu cgroup events exist on this cpu
*/
static void perf_sched_delayed(struct work_struct *work);
@@ -389,7 +388,6 @@ static DECLARE_DELAYED_WORK(perf_sched_work, perf_sched_delayed);
static DEFINE_MUTEX(perf_sched_mutex);
static atomic_t perf_sched_count;
-static DEFINE_PER_CPU(atomic_t, perf_cgroup_events);
static DEFINE_PER_CPU(struct pmu_event_list, pmu_sb_events);
static atomic_t nr_mmap_events __read_mostly;
@@ -844,9 +842,16 @@ static void perf_cgroup_switch(struct task_struct *task)
struct perf_cpu_context *cpuctx = this_cpu_ptr(&perf_cpu_context);
struct perf_cgroup *cgrp;
- cgrp = perf_cgroup_from_task(task, NULL);
+ /*
+ * cpuctx->cgrp is set when the first cgroup event enabled,
+ * and is cleared when the last cgroup event disabled.
+ */
+ if (READ_ONCE(cpuctx->cgrp) == NULL)
+ return;
WARN_ON_ONCE(cpuctx->ctx.nr_cgroups == 0);
+
+ cgrp = perf_cgroup_from_task(task, NULL);
if (READ_ONCE(cpuctx->cgrp) == cgrp)
return;
@@ -3631,8 +3636,7 @@ void __perf_event_task_sched_out(struct task_struct *task,
* to check if we have to switch out PMU state.
* cgroup event are system-wide mode only
*/
- if (atomic_read(this_cpu_ptr(&perf_cgroup_events)))
- perf_cgroup_switch(next);
+ perf_cgroup_switch(next);
}
static bool perf_less_group_idx(const void *l, const void *r)
@@ -4809,19 +4813,17 @@ find_get_pmu_context(struct pmu *pmu, struct perf_event_context *ctx,
cpc = per_cpu_ptr(pmu->cpu_pmu_context, event->cpu);
epc = &cpc->epc;
-
+ raw_spin_lock_irq(&ctx->lock);
if (!epc->ctx) {
atomic_set(&epc->refcount, 1);
epc->embedded = 1;
- raw_spin_lock_irq(&ctx->lock);
list_add(&epc->pmu_ctx_entry, &ctx->pmu_ctx_list);
epc->ctx = ctx;
- raw_spin_unlock_irq(&ctx->lock);
} else {
WARN_ON_ONCE(epc->ctx != ctx);
atomic_inc(&epc->refcount);
}
-
+ raw_spin_unlock_irq(&ctx->lock);
return epc;
}
@@ -4892,33 +4894,30 @@ static void free_epc_rcu(struct rcu_head *head)
static void put_pmu_ctx(struct perf_event_pmu_context *epc)
{
+ struct perf_event_context *ctx = epc->ctx;
unsigned long flags;
- if (!atomic_dec_and_test(&epc->refcount))
+ /*
+ * XXX
+ *
+ * lockdep_assert_held(&ctx->mutex);
+ *
+ * can't because of the call-site in _free_event()/put_event()
+ * which isn't always called under ctx->mutex.
+ */
+ if (!atomic_dec_and_raw_lock_irqsave(&epc->refcount, &ctx->lock, flags))
return;
- if (epc->ctx) {
- struct perf_event_context *ctx = epc->ctx;
-
- /*
- * XXX
- *
- * lockdep_assert_held(&ctx->mutex);
- *
- * can't because of the call-site in _free_event()/put_event()
- * which isn't always called under ctx->mutex.
- */
+ WARN_ON_ONCE(list_empty(&epc->pmu_ctx_entry));
- WARN_ON_ONCE(list_empty(&epc->pmu_ctx_entry));
- raw_spin_lock_irqsave(&ctx->lock, flags);
- list_del_init(&epc->pmu_ctx_entry);
- epc->ctx = NULL;
- raw_spin_unlock_irqrestore(&ctx->lock, flags);
- }
+ list_del_init(&epc->pmu_ctx_entry);
+ epc->ctx = NULL;
WARN_ON_ONCE(!list_empty(&epc->pinned_active));
WARN_ON_ONCE(!list_empty(&epc->flexible_active));
+ raw_spin_unlock_irqrestore(&ctx->lock, flags);
+
if (epc->embedded)
return;
@@ -4974,15 +4973,6 @@ static void unaccount_pmu_sb_event(struct perf_event *event)
detach_sb_event(event);
}
-static void unaccount_event_cpu(struct perf_event *event, int cpu)
-{
- if (event->parent)
- return;
-
- if (is_cgroup_event(event))
- atomic_dec(&per_cpu(perf_cgroup_events, cpu));
-}
-
#ifdef CONFIG_NO_HZ_FULL
static DEFINE_SPINLOCK(nr_freq_lock);
#endif
@@ -5048,8 +5038,6 @@ static void unaccount_event(struct perf_event *event)
schedule_delayed_work(&perf_sched_work, HZ);
}
- unaccount_event_cpu(event, event->cpu);
-
unaccount_pmu_sb_event(event);
}
@@ -11679,15 +11667,6 @@ static void account_pmu_sb_event(struct perf_event *event)
attach_sb_event(event);
}
-static void account_event_cpu(struct perf_event *event, int cpu)
-{
- if (event->parent)
- return;
-
- if (is_cgroup_event(event))
- atomic_inc(&per_cpu(perf_cgroup_events, cpu));
-}
-
/* Freq events need the tick to stay alive (see perf_event_task_tick). */
static void account_freq_event_nohz(void)
{
@@ -11775,8 +11754,6 @@ static void account_event(struct perf_event *event)
}
enabled:
- account_event_cpu(event, event->cpu);
-
account_pmu_sb_event(event);
}
@@ -12339,12 +12316,12 @@ SYSCALL_DEFINE5(perf_event_open,
if (flags & ~PERF_FLAG_ALL)
return -EINVAL;
- /* Do we allow access to perf_event_open(2) ? */
- err = security_perf_event_open(&attr, PERF_SECURITY_OPEN);
+ err = perf_copy_attr(attr_uptr, &attr);
if (err)
return err;
- err = perf_copy_attr(attr_uptr, &attr);
+ /* Do we allow access to perf_event_open(2) ? */
+ err = security_perf_event_open(&attr, PERF_SECURITY_OPEN);
if (err)
return err;
@@ -12689,7 +12666,8 @@ SYSCALL_DEFINE5(perf_event_open,
return event_fd;
err_context:
- /* event->pmu_ctx freed by free_event() */
+ put_pmu_ctx(event->pmu_ctx);
+ event->pmu_ctx = NULL; /* _free_event() */
err_locked:
mutex_unlock(&ctx->mutex);
perf_unpin_context(ctx);
@@ -12802,6 +12780,7 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu,
err_pmu_ctx:
put_pmu_ctx(pmu_ctx);
+ event->pmu_ctx = NULL; /* _free_event() */
err_unlock:
mutex_unlock(&ctx->mutex);
perf_unpin_context(ctx);
@@ -12822,13 +12801,11 @@ static void __perf_pmu_remove(struct perf_event_context *ctx,
perf_event_groups_for_cpu_pmu(event, groups, cpu, pmu) {
perf_remove_from_context(event, 0);
- unaccount_event_cpu(event, cpu);
put_pmu_ctx(event->pmu_ctx);
list_add(&event->migrate_entry, events);
for_each_sibling_event(sibling, event) {
perf_remove_from_context(sibling, 0);
- unaccount_event_cpu(sibling, cpu);
put_pmu_ctx(sibling->pmu_ctx);
list_add(&sibling->migrate_entry, events);
}
@@ -12847,7 +12824,6 @@ static void __perf_pmu_install_event(struct pmu *pmu,
if (event->state >= PERF_EVENT_STATE_OFF)
event->state = PERF_EVENT_STATE_INACTIVE;
- account_event_cpu(event, cpu);
perf_install_in_context(ctx, event, cpu);
}
@@ -13231,7 +13207,7 @@ inherit_event(struct perf_event *parent_event,
pmu_ctx = find_get_pmu_context(child_event->pmu, child_ctx, child_event);
if (IS_ERR(pmu_ctx)) {
free_event(child_event);
- return NULL;
+ return ERR_CAST(pmu_ctx);
}
child_event->pmu_ctx = pmu_ctx;
@@ -13742,8 +13718,7 @@ static int __perf_cgroup_move(void *info)
struct task_struct *task = info;
preempt_disable();
- if (atomic_read(this_cpu_ptr(&perf_cgroup_events)))
- perf_cgroup_switch(task);
+ perf_cgroup_switch(task);
preempt_enable();
return 0;
diff --git a/kernel/futex/syscalls.c b/kernel/futex/syscalls.c
index 086a22d1adb78f..a8074079b09e87 100644
--- a/kernel/futex/syscalls.c
+++ b/kernel/futex/syscalls.c
@@ -286,19 +286,22 @@ SYSCALL_DEFINE5(futex_waitv, struct futex_waitv __user *, waiters,
}
futexv = kcalloc(nr_futexes, sizeof(*futexv), GFP_KERNEL);
- if (!futexv)
- return -ENOMEM;
+ if (!futexv) {
+ ret = -ENOMEM;
+ goto destroy_timer;
+ }
ret = futex_parse_waitv(futexv, waiters, nr_futexes);
if (!ret)
ret = futex_wait_multiple(futexv, nr_futexes, timeout ? &to : NULL);
+ kfree(futexv);
+
+destroy_timer:
if (timeout) {
hrtimer_cancel(&to.timer);
destroy_hrtimer_on_stack(&to.timer);
}
-
- kfree(futexv);
return ret;
}
diff --git a/kernel/gen_kheaders.sh b/kernel/gen_kheaders.sh
index 473036b43c832b..81b97f0f65564e 100755
--- a/kernel/gen_kheaders.sh
+++ b/kernel/gen_kheaders.sh
@@ -14,6 +14,8 @@ include/
arch/$SRCARCH/include/
"
+type cpio > /dev/null
+
# Support incremental builds by skipping archive generation
# if timestamps of files being archived are not changed.
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 8fe1da9614ee8d..798a9042421fc7 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -114,7 +114,7 @@ void irq_domain_free_fwnode(struct fwnode_handle *fwnode)
{
struct irqchip_fwid *fwid;
- if (WARN_ON(!is_fwnode_irqchip(fwnode)))
+ if (!fwnode || WARN_ON(!is_fwnode_irqchip(fwnode)))
return;
fwid = container_of(fwnode, struct irqchip_fwid, fwnode);
@@ -1915,7 +1915,7 @@ static void debugfs_add_domain_dir(struct irq_domain *d)
static void debugfs_remove_domain_dir(struct irq_domain *d)
{
- debugfs_remove(debugfs_lookup(d->name, domain_dir));
+ debugfs_lookup_and_remove(d->name, domain_dir);
}
void __init irq_domain_debugfs_init(struct dentry *root)
diff --git a/kernel/irq/msi.c b/kernel/irq/msi.c
index 955267bbc2be63..783a3e6a0b1075 100644
--- a/kernel/irq/msi.c
+++ b/kernel/irq/msi.c
@@ -1000,7 +1000,7 @@ bool msi_create_device_irq_domain(struct device *dev, unsigned int domid,
fail:
msi_unlock_descs(dev);
free_fwnode:
- kfree(fwnode);
+ irq_domain_free_fwnode(fwnode);
free_bundle:
kfree(bundle);
return false;
@@ -1013,6 +1013,7 @@ free_bundle:
*/
void msi_remove_device_irq_domain(struct device *dev, unsigned int domid)
{
+ struct fwnode_handle *fwnode = NULL;
struct msi_domain_info *info;
struct irq_domain *domain;
@@ -1025,7 +1026,10 @@ void msi_remove_device_irq_domain(struct device *dev, unsigned int domid)
dev->msi.data->__domains[domid].domain = NULL;
info = domain->host_data;
+ if (irq_domain_is_msi_device(domain))
+ fwnode = domain->fwnode;
irq_domain_remove(domain);
+ irq_domain_free_fwnode(fwnode);
kfree(container_of(info, struct msi_domain_template, info));
unlock:
diff --git a/kernel/kallsyms_selftest.c b/kernel/kallsyms_selftest.c
index f35d9cc1aab154..bfbc12da33267f 100644
--- a/kernel/kallsyms_selftest.c
+++ b/kernel/kallsyms_selftest.c
@@ -157,14 +157,11 @@ static void test_kallsyms_compression_ratio(void)
static int lookup_name(void *data, const char *name, struct module *mod, unsigned long addr)
{
u64 t0, t1, t;
- unsigned long flags;
struct test_stat *stat = (struct test_stat *)data;
- local_irq_save(flags);
- t0 = sched_clock();
+ t0 = ktime_get_ns();
(void)kallsyms_lookup_name(name);
- t1 = sched_clock();
- local_irq_restore(flags);
+ t1 = ktime_get_ns();
t = t1 - t0;
if (t < stat->min)
@@ -234,18 +231,15 @@ static int find_symbol(void *data, const char *name, struct module *mod, unsigne
static void test_perf_kallsyms_on_each_symbol(void)
{
u64 t0, t1;
- unsigned long flags;
struct test_stat stat;
memset(&stat, 0, sizeof(stat));
stat.max = INT_MAX;
stat.name = stub_name;
stat.perf = 1;
- local_irq_save(flags);
- t0 = sched_clock();
+ t0 = ktime_get_ns();
kallsyms_on_each_symbol(find_symbol, &stat);
- t1 = sched_clock();
- local_irq_restore(flags);
+ t1 = ktime_get_ns();
pr_info("kallsyms_on_each_symbol() traverse all: %lld ns\n", t1 - t0);
}
@@ -270,17 +264,14 @@ static int match_symbol(void *data, unsigned long addr)
static void test_perf_kallsyms_on_each_match_symbol(void)
{
u64 t0, t1;
- unsigned long flags;
struct test_stat stat;
memset(&stat, 0, sizeof(stat));
stat.max = INT_MAX;
stat.name = stub_name;
- local_irq_save(flags);
- t0 = sched_clock();
+ t0 = ktime_get_ns();
kallsyms_on_each_match_symbol(match_symbol, stat.name, &stat);
- t1 = sched_clock();
- local_irq_restore(flags);
+ t1 = ktime_get_ns();
pr_info("kallsyms_on_each_match_symbol() traverse all: %lld ns\n", t1 - t0);
}
diff --git a/kernel/kcsan/kcsan_test.c b/kernel/kcsan/kcsan_test.c
index dcec1b743c6941..a60c561724be96 100644
--- a/kernel/kcsan/kcsan_test.c
+++ b/kernel/kcsan/kcsan_test.c
@@ -159,7 +159,7 @@ static bool __report_matches(const struct expect_report *r)
const bool is_assert = (r->access[0].type | r->access[1].type) & KCSAN_ACCESS_ASSERT;
bool ret = false;
unsigned long flags;
- typeof(observed.lines) expect;
+ typeof(*observed.lines) *expect;
const char *end;
char *cur;
int i;
@@ -168,6 +168,10 @@ static bool __report_matches(const struct expect_report *r)
if (!report_available())
return false;
+ expect = kmalloc(sizeof(observed.lines), GFP_KERNEL);
+ if (WARN_ON(!expect))
+ return false;
+
/* Generate expected report contents. */
/* Title */
@@ -253,6 +257,7 @@ static bool __report_matches(const struct expect_report *r)
strstr(observed.lines[2], expect[1])));
out:
spin_unlock_irqrestore(&observed.lock, flags);
+ kfree(expect);
return ret;
}
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
index 7779ee8abc2a08..728f434de2bbfc 100644
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -89,15 +89,31 @@ static inline int __ww_mutex_check_kill(struct rt_mutex *lock,
* set this bit before looking at the lock.
*/
-static __always_inline void
-rt_mutex_set_owner(struct rt_mutex_base *lock, struct task_struct *owner)
+static __always_inline struct task_struct *
+rt_mutex_owner_encode(struct rt_mutex_base *lock, struct task_struct *owner)
{
unsigned long val = (unsigned long)owner;
if (rt_mutex_has_waiters(lock))
val |= RT_MUTEX_HAS_WAITERS;
- WRITE_ONCE(lock->owner, (struct task_struct *)val);
+ return (struct task_struct *)val;
+}
+
+static __always_inline void
+rt_mutex_set_owner(struct rt_mutex_base *lock, struct task_struct *owner)
+{
+ /*
+ * lock->wait_lock is held but explicit acquire semantics are needed
+ * for a new lock owner so WRITE_ONCE is insufficient.
+ */
+ xchg_acquire(&lock->owner, rt_mutex_owner_encode(lock, owner));
+}
+
+static __always_inline void rt_mutex_clear_owner(struct rt_mutex_base *lock)
+{
+ /* lock->wait_lock is held so the unlock provides release semantics. */
+ WRITE_ONCE(lock->owner, rt_mutex_owner_encode(lock, NULL));
}
static __always_inline void clear_rt_mutex_waiters(struct rt_mutex_base *lock)
@@ -106,7 +122,8 @@ static __always_inline void clear_rt_mutex_waiters(struct rt_mutex_base *lock)
((unsigned long)lock->owner & ~RT_MUTEX_HAS_WAITERS);
}
-static __always_inline void fixup_rt_mutex_waiters(struct rt_mutex_base *lock)
+static __always_inline void
+fixup_rt_mutex_waiters(struct rt_mutex_base *lock, bool acquire_lock)
{
unsigned long owner, *p = (unsigned long *) &lock->owner;
@@ -172,8 +189,21 @@ static __always_inline void fixup_rt_mutex_waiters(struct rt_mutex_base *lock)
* still set.
*/
owner = READ_ONCE(*p);
- if (owner & RT_MUTEX_HAS_WAITERS)
- WRITE_ONCE(*p, owner & ~RT_MUTEX_HAS_WAITERS);
+ if (owner & RT_MUTEX_HAS_WAITERS) {
+ /*
+ * See rt_mutex_set_owner() and rt_mutex_clear_owner() on
+ * why xchg_acquire() is used for updating owner for
+ * locking and WRITE_ONCE() for unlocking.
+ *
+ * WRITE_ONCE() would work for the acquire case too, but
+ * in case that the lock acquisition failed it might
+ * force other lockers into the slow path unnecessarily.
+ */
+ if (acquire_lock)
+ xchg_acquire(p, owner & ~RT_MUTEX_HAS_WAITERS);
+ else
+ WRITE_ONCE(*p, owner & ~RT_MUTEX_HAS_WAITERS);
+ }
}
/*
@@ -208,6 +238,13 @@ static __always_inline void mark_rt_mutex_waiters(struct rt_mutex_base *lock)
owner = *p;
} while (cmpxchg_relaxed(p, owner,
owner | RT_MUTEX_HAS_WAITERS) != owner);
+
+ /*
+ * The cmpxchg loop above is relaxed to avoid back-to-back ACQUIRE
+ * operations in the event of contention. Ensure the successful
+ * cmpxchg is visible.
+ */
+ smp_mb__after_atomic();
}
/*
@@ -864,8 +901,9 @@ static int __sched rt_mutex_adjust_prio_chain(struct task_struct *task,
* then we need to wake the new top waiter up to try
* to get the lock.
*/
- if (prerequeue_top_waiter != rt_mutex_top_waiter(lock))
- wake_up_state(waiter->task, waiter->wake_state);
+ top_waiter = rt_mutex_top_waiter(lock);
+ if (prerequeue_top_waiter != top_waiter)
+ wake_up_state(top_waiter->task, top_waiter->wake_state);
raw_spin_unlock_irq(&lock->wait_lock);
return 0;
}
@@ -1243,7 +1281,7 @@ static int __sched __rt_mutex_slowtrylock(struct rt_mutex_base *lock)
* try_to_take_rt_mutex() sets the lock waiters bit
* unconditionally. Clean this up.
*/
- fixup_rt_mutex_waiters(lock);
+ fixup_rt_mutex_waiters(lock, true);
return ret;
}
@@ -1604,7 +1642,7 @@ static int __sched __rt_mutex_slowlock(struct rt_mutex_base *lock,
* try_to_take_rt_mutex() sets the waiter bit
* unconditionally. We might have to fix that up.
*/
- fixup_rt_mutex_waiters(lock);
+ fixup_rt_mutex_waiters(lock, true);
trace_contention_end(lock, ret);
@@ -1719,7 +1757,7 @@ static void __sched rtlock_slowlock_locked(struct rt_mutex_base *lock)
* try_to_take_rt_mutex() sets the waiter bit unconditionally.
* We might have to fix that up:
*/
- fixup_rt_mutex_waiters(lock);
+ fixup_rt_mutex_waiters(lock, true);
debug_rt_mutex_free_waiter(&waiter);
trace_contention_end(lock, 0);
diff --git a/kernel/locking/rtmutex_api.c b/kernel/locking/rtmutex_api.c
index 900220941caacc..cb9fdff76a8a32 100644
--- a/kernel/locking/rtmutex_api.c
+++ b/kernel/locking/rtmutex_api.c
@@ -267,7 +267,7 @@ void __sched rt_mutex_init_proxy_locked(struct rt_mutex_base *lock,
void __sched rt_mutex_proxy_unlock(struct rt_mutex_base *lock)
{
debug_rt_mutex_proxy_unlock(lock);
- rt_mutex_set_owner(lock, NULL);
+ rt_mutex_clear_owner(lock);
}
/**
@@ -382,7 +382,7 @@ int __sched rt_mutex_wait_proxy_lock(struct rt_mutex_base *lock,
* try_to_take_rt_mutex() sets the waiter bit unconditionally. We might
* have to fix that up.
*/
- fixup_rt_mutex_waiters(lock);
+ fixup_rt_mutex_waiters(lock, true);
raw_spin_unlock_irq(&lock->wait_lock);
return ret;
@@ -438,7 +438,7 @@ bool __sched rt_mutex_cleanup_proxy_lock(struct rt_mutex_base *lock,
* try_to_take_rt_mutex() sets the waiter bit unconditionally. We might
* have to fix that up.
*/
- fixup_rt_mutex_waiters(lock);
+ fixup_rt_mutex_waiters(lock, false);
raw_spin_unlock_irq(&lock->wait_lock);
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 48568a0f5651ce..4ac3fe43e6c8ff 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -2393,7 +2393,8 @@ static bool finished_loading(const char *name)
sched_annotate_sleep();
mutex_lock(&module_mutex);
mod = find_module_all(name, strlen(name), true);
- ret = !mod || mod->state == MODULE_STATE_LIVE;
+ ret = !mod || mod->state == MODULE_STATE_LIVE
+ || mod->state == MODULE_STATE_GOING;
mutex_unlock(&module_mutex);
return ret;
@@ -2569,20 +2570,35 @@ static int add_unformed_module(struct module *mod)
mod->state = MODULE_STATE_UNFORMED;
-again:
mutex_lock(&module_mutex);
old = find_module_all(mod->name, strlen(mod->name), true);
if (old != NULL) {
- if (old->state != MODULE_STATE_LIVE) {
+ if (old->state == MODULE_STATE_COMING
+ || old->state == MODULE_STATE_UNFORMED) {
/* Wait in case it fails to load. */
mutex_unlock(&module_mutex);
err = wait_event_interruptible(module_wq,
finished_loading(mod->name));
if (err)
goto out_unlocked;
- goto again;
+
+ /* The module might have gone in the meantime. */
+ mutex_lock(&module_mutex);
+ old = find_module_all(mod->name, strlen(mod->name),
+ true);
}
- err = -EEXIST;
+
+ /*
+ * We are here only when the same module was being loaded. Do
+ * not try to load it again right now. It prevents long delays
+ * caused by serialized module load failures. It might happen
+ * when more devices of the same type trigger load of
+ * a particular module.
+ */
+ if (old && old->state == MODULE_STATE_LIVE)
+ err = -EEXIST;
+ else
+ err = -EBUSY;
goto out;
}
mod_update_bounds(mod);
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 7decf1e9c48635..a5ed2e53547c94 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -123,6 +123,7 @@ bool console_srcu_read_lock_is_held(void)
{
return srcu_read_lock_held(&console_srcu);
}
+EXPORT_SYMBOL(console_srcu_read_lock_is_held);
#endif
enum devkmsg_log_bits {
@@ -1891,6 +1892,7 @@ static void console_lock_spinning_enable(void)
/**
* console_lock_spinning_disable_and_check - mark end of code where another
* thread was able to busy wait and check if there is a waiter
+ * @cookie: cookie returned from console_srcu_read_lock()
*
* This is called at the end of the section where spinning is allowed.
* It has two functions. First, it is a signal that it is no longer
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 25b582b6ee5f78..e838feb6adc504 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -2604,27 +2604,71 @@ void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask)
.user_mask = NULL,
.flags = SCA_USER, /* clear the user requested mask */
};
+ union cpumask_rcuhead {
+ cpumask_t cpumask;
+ struct rcu_head rcu;
+ };
__do_set_cpus_allowed(p, &ac);
- kfree(ac.user_mask);
+
+ /*
+ * Because this is called with p->pi_lock held, it is not possible
+ * to use kfree() here (when PREEMPT_RT=y), therefore punt to using
+ * kfree_rcu().
+ */
+ kfree_rcu((union cpumask_rcuhead *)ac.user_mask, rcu);
+}
+
+static cpumask_t *alloc_user_cpus_ptr(int node)
+{
+ /*
+ * See do_set_cpus_allowed() above for the rcu_head usage.
+ */
+ int size = max_t(int, cpumask_size(), sizeof(struct rcu_head));
+
+ return kmalloc_node(size, GFP_KERNEL, node);
}
int dup_user_cpus_ptr(struct task_struct *dst, struct task_struct *src,
int node)
{
+ cpumask_t *user_mask;
unsigned long flags;
- if (!src->user_cpus_ptr)
+ /*
+ * Always clear dst->user_cpus_ptr first as their user_cpus_ptr's
+ * may differ by now due to racing.
+ */
+ dst->user_cpus_ptr = NULL;
+
+ /*
+ * This check is racy and losing the race is a valid situation.
+ * It is not worth the extra overhead of taking the pi_lock on
+ * every fork/clone.
+ */
+ if (data_race(!src->user_cpus_ptr))
return 0;
- dst->user_cpus_ptr = kmalloc_node(cpumask_size(), GFP_KERNEL, node);
- if (!dst->user_cpus_ptr)
+ user_mask = alloc_user_cpus_ptr(node);
+ if (!user_mask)
return -ENOMEM;
- /* Use pi_lock to protect content of user_cpus_ptr */
+ /*
+ * Use pi_lock to protect content of user_cpus_ptr
+ *
+ * Though unlikely, user_cpus_ptr can be reset to NULL by a concurrent
+ * do_set_cpus_allowed().
+ */
raw_spin_lock_irqsave(&src->pi_lock, flags);
- cpumask_copy(dst->user_cpus_ptr, src->user_cpus_ptr);
+ if (src->user_cpus_ptr) {
+ swap(dst->user_cpus_ptr, user_mask);
+ cpumask_copy(dst->user_cpus_ptr, src->user_cpus_ptr);
+ }
raw_spin_unlock_irqrestore(&src->pi_lock, flags);
+
+ if (unlikely(user_mask))
+ kfree(user_mask);
+
return 0;
}
@@ -3581,6 +3625,11 @@ static inline bool rq_has_pinned_tasks(struct rq *rq)
return false;
}
+static inline cpumask_t *alloc_user_cpus_ptr(int node)
+{
+ return NULL;
+}
+
#endif /* !CONFIG_SMP */
static void
@@ -5504,7 +5553,9 @@ void scheduler_tick(void)
unsigned long thermal_pressure;
u64 resched_latency;
- arch_scale_freq_tick();
+ if (housekeeping_cpu(cpu, HK_TYPE_TICK))
+ arch_scale_freq_tick();
+
sched_clock_tick();
rq_lock(rq, &rf);
@@ -8239,12 +8290,18 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
if (retval)
goto out_put_task;
- user_mask = kmalloc(cpumask_size(), GFP_KERNEL);
- if (!user_mask) {
+ /*
+ * With non-SMP configs, user_cpus_ptr/user_mask isn't used and
+ * alloc_user_cpus_ptr() returns NULL.
+ */
+ user_mask = alloc_user_cpus_ptr(NUMA_NO_NODE);
+ if (user_mask) {
+ cpumask_copy(user_mask, in_mask);
+ } else if (IS_ENABLED(CONFIG_SMP)) {
retval = -ENOMEM;
goto out_put_task;
}
- cpumask_copy(user_mask, in_mask);
+
ac = (struct affinity_context){
.new_mask = in_mask,
.user_mask = user_mask,
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index c36aa54ae071a7..0f87369914274d 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -7229,10 +7229,10 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
eenv_task_busy_time(&eenv, p, prev_cpu);
for (; pd; pd = pd->next) {
+ unsigned long util_min = p_util_min, util_max = p_util_max;
unsigned long cpu_cap, cpu_thermal_cap, util;
unsigned long cur_delta, max_spare_cap = 0;
unsigned long rq_util_min, rq_util_max;
- unsigned long util_min, util_max;
unsigned long prev_spare_cap = 0;
int max_spare_cap_cpu = -1;
unsigned long base_energy;
@@ -7251,6 +7251,8 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
eenv.pd_cap = 0;
for_each_cpu(cpu, cpus) {
+ struct rq *rq = cpu_rq(cpu);
+
eenv.pd_cap += cpu_thermal_cap;
if (!cpumask_test_cpu(cpu, sched_domain_span(sd)))
@@ -7269,24 +7271,19 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
* much capacity we can get out of the CPU; this is
* aligned with sched_cpu_util().
*/
- if (uclamp_is_used()) {
- if (uclamp_rq_is_idle(cpu_rq(cpu))) {
- util_min = p_util_min;
- util_max = p_util_max;
- } else {
- /*
- * Open code uclamp_rq_util_with() except for
- * the clamp() part. Ie: apply max aggregation
- * only. util_fits_cpu() logic requires to
- * operate on non clamped util but must use the
- * max-aggregated uclamp_{min, max}.
- */
- rq_util_min = uclamp_rq_get(cpu_rq(cpu), UCLAMP_MIN);
- rq_util_max = uclamp_rq_get(cpu_rq(cpu), UCLAMP_MAX);
-
- util_min = max(rq_util_min, p_util_min);
- util_max = max(rq_util_max, p_util_max);
- }
+ if (uclamp_is_used() && !uclamp_rq_is_idle(rq)) {
+ /*
+ * Open code uclamp_rq_util_with() except for
+ * the clamp() part. Ie: apply max aggregation
+ * only. util_fits_cpu() logic requires to
+ * operate on non clamped util but must use the
+ * max-aggregated uclamp_{min, max}.
+ */
+ rq_util_min = uclamp_rq_get(rq, UCLAMP_MIN);
+ rq_util_max = uclamp_rq_get(rq, UCLAMP_MAX);
+
+ util_min = max(rq_util_min, p_util_min);
+ util_max = max(rq_util_max, p_util_max);
}
if (!util_fits_cpu(util, util_min, util_max, cpu))
continue;
@@ -8871,16 +8868,23 @@ static void update_cpu_capacity(struct sched_domain *sd, int cpu)
* * Thermal pressure will impact all cpus in this perf domain
* equally.
*/
- if (static_branch_unlikely(&sched_asym_cpucapacity)) {
+ if (sched_energy_enabled()) {
unsigned long inv_cap = capacity_orig - thermal_load_avg(rq);
- struct perf_domain *pd = rcu_dereference(rq->rd->pd);
+ struct perf_domain *pd;
+ rcu_read_lock();
+
+ pd = rcu_dereference(rq->rd->pd);
rq->cpu_capacity_inverted = 0;
for (; pd; pd = pd->next) {
struct cpumask *pd_span = perf_domain_span(pd);
unsigned long pd_cap_orig, pd_cap;
+ /* We can't be inverted against our own pd */
+ if (cpumask_test_cpu(cpu_of(rq), pd_span))
+ continue;
+
cpu = cpumask_any(pd_span);
pd_cap_orig = arch_scale_cpu_capacity(cpu);
@@ -8905,6 +8909,8 @@ static void update_cpu_capacity(struct sched_domain *sd, int cpu)
break;
}
}
+
+ rcu_read_unlock();
}
trace_sched_cpu_capacity_tp(rq);
diff --git a/kernel/sys.c b/kernel/sys.c
index 5fd54bf0e88672..88b31f096fb2d9 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1442,6 +1442,8 @@ static int do_prlimit(struct task_struct *tsk, unsigned int resource,
if (resource >= RLIM_NLIMITS)
return -EINVAL;
+ resource = array_index_nospec(resource, RLIM_NLIMITS);
+
if (new_rlim) {
if (new_rlim->rlim_cur > new_rlim->rlim_max)
return -EINVAL;
diff --git a/kernel/time/tick-oneshot.c b/kernel/time/tick-oneshot.c
index 475ecceda7688c..5e2c2c26b3cc91 100644
--- a/kernel/time/tick-oneshot.c
+++ b/kernel/time/tick-oneshot.c
@@ -18,7 +18,7 @@
#include "tick-internal.h"
/**
- * tick_program_event
+ * tick_program_event - program the CPU local timer device for the next event
*/
int tick_program_event(ktime_t expires, int force)
{
@@ -99,7 +99,7 @@ int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *))
}
/**
- * tick_check_oneshot_mode - check whether the system is in oneshot mode
+ * tick_oneshot_mode_active - check whether the system is in oneshot mode
*
* returns 1 when either nohz or highres are enabled. otherwise 0.
*/
diff --git a/kernel/time/time.c b/kernel/time/time.c
index 526257b3727ca9..f4198af60fee5d 100644
--- a/kernel/time/time.c
+++ b/kernel/time/time.c
@@ -462,7 +462,7 @@ struct __kernel_old_timeval ns_to_kernel_old_timeval(s64 nsec)
EXPORT_SYMBOL(ns_to_kernel_old_timeval);
/**
- * set_normalized_timespec - set timespec sec and nsec parts and normalize
+ * set_normalized_timespec64 - set timespec sec and nsec parts and normalize
*
* @ts: pointer to timespec variable to be set
* @sec: seconds to set
@@ -526,7 +526,7 @@ struct timespec64 ns_to_timespec64(s64 nsec)
EXPORT_SYMBOL(ns_to_timespec64);
/**
- * msecs_to_jiffies: - convert milliseconds to jiffies
+ * __msecs_to_jiffies: - convert milliseconds to jiffies
* @m: time in milliseconds
*
* conversion is done as follows:
@@ -541,12 +541,12 @@ EXPORT_SYMBOL(ns_to_timespec64);
* handling any 32-bit overflows.
* for the details see __msecs_to_jiffies()
*
- * msecs_to_jiffies() checks for the passed in value being a constant
+ * __msecs_to_jiffies() checks for the passed in value being a constant
* via __builtin_constant_p() allowing gcc to eliminate most of the
* code, __msecs_to_jiffies() is called if the value passed does not
* allow constant folding and the actual conversion must be done at
* runtime.
- * the _msecs_to_jiffies helpers are the HZ dependent conversion
+ * The _msecs_to_jiffies helpers are the HZ dependent conversion
* routines found in include/linux/jiffies.h
*/
unsigned long __msecs_to_jiffies(const unsigned int m)
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index f72b9f1de178ef..5579ead449f25b 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -1590,10 +1590,10 @@ void __weak read_persistent_clock64(struct timespec64 *ts)
/**
* read_persistent_wall_and_boot_offset - Read persistent clock, and also offset
* from the boot.
+ * @wall_time: current time as returned by persistent clock
+ * @boot_offset: offset that is defined as wall_time - boot_time
*
* Weak dummy function for arches that do not yet support it.
- * @wall_time: - current time as returned by persistent clock
- * @boot_offset: - offset that is defined as wall_time - boot_time
*
* The default function calculates offset based on the current value of
* local_clock(). This way architectures that support sched_clock() but don't
@@ -1701,7 +1701,7 @@ static void __timekeeping_inject_sleeptime(struct timekeeper *tk,
}
#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_RTC_HCTOSYS_DEVICE)
-/**
+/*
* We have three kinds of time sources to use for sleep time
* injection, the preference order is:
* 1) non-stop clocksource
@@ -1722,7 +1722,7 @@ bool timekeeping_rtc_skipresume(void)
return !suspend_timing_needed;
}
-/**
+/*
* 1) can be determined whether to use or not only when doing
* timekeeping_resume() which is invoked after rtc_suspend(),
* so we can't skip rtc_suspend() surely if system has 1).
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 197545241ab835..d7043043f59c8d 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -933,8 +933,8 @@ config RING_BUFFER_RECORD_RECURSION
default y
help
The ring buffer has its own internal recursion. Although when
- recursion happens it wont cause harm because of the protection,
- but it does cause an unwanted overhead. Enabling this option will
+ recursion happens it won't cause harm because of the protection,
+ but it does cause unwanted overhead. Enabling this option will
place where recursion was detected into the ftrace "recursed_functions"
file.
@@ -1017,8 +1017,8 @@ config RING_BUFFER_STARTUP_TEST
The test runs for 10 seconds. This will slow your boot time
by at least 10 more seconds.
- At the end of the test, statics and more checks are done.
- It will output the stats of each per cpu buffer. What
+ At the end of the test, statistics and more checks are done.
+ It will output the stats of each per cpu buffer: What
was written, the sizes, what was read, what was lost, and
other similar details.
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 3bbd3f0c810c89..c09792c551bf99 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -833,6 +833,7 @@ static void do_bpf_send_signal(struct irq_work *entry)
work = container_of(entry, struct send_signal_irq_work, irq_work);
group_send_sig_info(work->sig, SEND_SIG_PRIV, work->task, work->type);
+ put_task_struct(work->task);
}
static int bpf_send_signal_common(u32 sig, enum pid_type type)
@@ -848,6 +849,9 @@ static int bpf_send_signal_common(u32 sig, enum pid_type type)
return -EPERM;
if (unlikely(!nmi_uaccess_okay()))
return -EPERM;
+ /* Task should not be pid=1 to avoid kernel panic. */
+ if (unlikely(is_global_init(current)))
+ return -EPERM;
if (irqs_disabled()) {
/* Do an early check on signal validity. Otherwise,
@@ -864,7 +868,7 @@ static int bpf_send_signal_common(u32 sig, enum pid_type type)
* to the irq_work. The current task may change when queued
* irq works get executed.
*/
- work->task = current;
+ work->task = get_task_struct(current);
work->sig = sig;
work->type = type;
irq_work_queue(&work->irq_work);
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 442438b93fe980..750aa3f08b25ae 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -1248,12 +1248,17 @@ static void free_ftrace_hash_rcu(struct ftrace_hash *hash)
call_rcu(&hash->rcu, __free_ftrace_hash_rcu);
}
+/**
+ * ftrace_free_filter - remove all filters for an ftrace_ops
+ * @ops - the ops to remove the filters from
+ */
void ftrace_free_filter(struct ftrace_ops *ops)
{
ftrace_ops_init(ops);
free_ftrace_hash(ops->func_hash->filter_hash);
free_ftrace_hash(ops->func_hash->notrace_hash);
}
+EXPORT_SYMBOL_GPL(ftrace_free_filter);
static struct ftrace_hash *alloc_ftrace_hash(int size_bits)
{
@@ -5839,6 +5844,10 @@ EXPORT_SYMBOL_GPL(modify_ftrace_direct_multi);
*
* Filters denote which functions should be enabled when tracing is enabled
* If @ip is NULL, it fails to update filter.
+ *
+ * This can allocate memory which must be freed before @ops can be freed,
+ * either by removing each filtered addr or by using
+ * ftrace_free_filter(@ops).
*/
int ftrace_set_filter_ip(struct ftrace_ops *ops, unsigned long ip,
int remove, int reset)
@@ -5858,7 +5867,11 @@ EXPORT_SYMBOL_GPL(ftrace_set_filter_ip);
*
* Filters denote which functions should be enabled when tracing is enabled
* If @ips array or any ip specified within is NULL , it fails to update filter.
- */
+ *
+ * This can allocate memory which must be freed before @ops can be freed,
+ * either by removing each filtered addr or by using
+ * ftrace_free_filter(@ops).
+*/
int ftrace_set_filter_ips(struct ftrace_ops *ops, unsigned long *ips,
unsigned int cnt, int remove, int reset)
{
@@ -5900,6 +5913,10 @@ ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len,
*
* Filters denote which functions should be enabled when tracing is enabled.
* If @buf is NULL and reset is set, all functions will be enabled for tracing.
+ *
+ * This can allocate memory which must be freed before @ops can be freed,
+ * either by removing each filtered addr or by using
+ * ftrace_free_filter(@ops).
*/
int ftrace_set_filter(struct ftrace_ops *ops, unsigned char *buf,
int len, int reset)
@@ -5919,6 +5936,10 @@ EXPORT_SYMBOL_GPL(ftrace_set_filter);
* Notrace Filters denote which functions should not be enabled when tracing
* is enabled. If @buf is NULL and reset is set, all functions will be enabled
* for tracing.
+ *
+ * This can allocate memory which must be freed before @ops can be freed,
+ * either by removing each filtered addr or by using
+ * ftrace_free_filter(@ops).
*/
int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf,
int len, int reset)
diff --git a/kernel/trace/rv/rv.c b/kernel/trace/rv/rv.c
index 6c97cc2d754aae..7e9061828c240a 100644
--- a/kernel/trace/rv/rv.c
+++ b/kernel/trace/rv/rv.c
@@ -516,7 +516,7 @@ static ssize_t enabled_monitors_write(struct file *filp, const char __user *user
struct rv_monitor_def *mdef;
int retval = -EINVAL;
bool enable = true;
- char *ptr = buff;
+ char *ptr;
int len;
if (count < 1 || count > MAX_RV_MONITOR_NAME_SIZE + 1)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index a555a861b97811..c9e40f69265047 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -9148,9 +9148,6 @@ buffer_percent_write(struct file *filp, const char __user *ubuf,
if (val > 100)
return -EINVAL;
- if (!val)
- val = 1;
-
tr->buffer_percent = val;
(*ppos)++;
@@ -10295,6 +10292,8 @@ void __init early_trace_init(void)
static_key_enable(&tracepoint_printk_key.key);
}
tracer_alloc_buffers();
+
+ init_events();
}
void __init trace_init(void)
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index e46a49269be205..085a31b978a56e 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -1282,6 +1282,7 @@ struct ftrace_event_field {
int offset;
int size;
int is_signed;
+ int len;
};
struct prog_entry;
@@ -1490,6 +1491,7 @@ extern void trace_event_enable_cmd_record(bool enable);
extern void trace_event_enable_tgid_record(bool enable);
extern int event_trace_init(void);
+extern int init_events(void);
extern int event_trace_add_tracer(struct dentry *parent, struct trace_array *tr);
extern int event_trace_del_tracer(struct trace_array *tr);
extern void __trace_early_add_events(struct trace_array *tr);
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 33e0b4f8ebe626..6a46967192975b 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -114,7 +114,7 @@ trace_find_event_field(struct trace_event_call *call, char *name)
static int __trace_define_field(struct list_head *head, const char *type,
const char *name, int offset, int size,
- int is_signed, int filter_type)
+ int is_signed, int filter_type, int len)
{
struct ftrace_event_field *field;
@@ -133,6 +133,7 @@ static int __trace_define_field(struct list_head *head, const char *type,
field->offset = offset;
field->size = size;
field->is_signed = is_signed;
+ field->len = len;
list_add(&field->link, head);
@@ -150,14 +151,28 @@ int trace_define_field(struct trace_event_call *call, const char *type,
head = trace_get_fields(call);
return __trace_define_field(head, type, name, offset, size,
- is_signed, filter_type);
+ is_signed, filter_type, 0);
}
EXPORT_SYMBOL_GPL(trace_define_field);
+int trace_define_field_ext(struct trace_event_call *call, const char *type,
+ const char *name, int offset, int size, int is_signed,
+ int filter_type, int len)
+{
+ struct list_head *head;
+
+ if (WARN_ON(!call->class))
+ return 0;
+
+ head = trace_get_fields(call);
+ return __trace_define_field(head, type, name, offset, size,
+ is_signed, filter_type, len);
+}
+
#define __generic_field(type, item, filter_type) \
ret = __trace_define_field(&ftrace_generic_fields, #type, \
#item, 0, 0, is_signed_type(type), \
- filter_type); \
+ filter_type, 0); \
if (ret) \
return ret;
@@ -166,7 +181,7 @@ EXPORT_SYMBOL_GPL(trace_define_field);
"common_" #item, \
offsetof(typeof(ent), item), \
sizeof(ent.item), \
- is_signed_type(type), FILTER_OTHER); \
+ is_signed_type(type), FILTER_OTHER, 0); \
if (ret) \
return ret;
@@ -1588,12 +1603,17 @@ static int f_show(struct seq_file *m, void *v)
seq_printf(m, "\tfield:%s %s;\toffset:%u;\tsize:%u;\tsigned:%d;\n",
field->type, field->name, field->offset,
field->size, !!field->is_signed);
- else
- seq_printf(m, "\tfield:%.*s %s%s;\toffset:%u;\tsize:%u;\tsigned:%d;\n",
+ else if (field->len)
+ seq_printf(m, "\tfield:%.*s %s[%d];\toffset:%u;\tsize:%u;\tsigned:%d;\n",
(int)(array_descriptor - field->type),
field->type, field->name,
- array_descriptor, field->offset,
+ field->len, field->offset,
field->size, !!field->is_signed);
+ else
+ seq_printf(m, "\tfield:%.*s %s[];\toffset:%u;\tsize:%u;\tsigned:%d;\n",
+ (int)(array_descriptor - field->type),
+ field->type, field->name,
+ field->offset, field->size, !!field->is_signed);
return 0;
}
@@ -2379,9 +2399,10 @@ event_define_fields(struct trace_event_call *call)
}
offset = ALIGN(offset, field->align);
- ret = trace_define_field(call, field->type, field->name,
+ ret = trace_define_field_ext(call, field->type, field->name,
offset, field->size,
- field->is_signed, field->filter_type);
+ field->is_signed, field->filter_type,
+ field->len);
if (WARN_ON_ONCE(ret)) {
pr_err("error code is %d\n", ret);
break;
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index 96acc2b71ac747..e095c3b3a50d3a 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -128,7 +128,7 @@ static bool is_not(const char *str)
}
/**
- * prog_entry - a singe entry in the filter program
+ * struct prog_entry - a singe entry in the filter program
* @target: Index to jump to on a branch (actually one minus the index)
* @when_to_branch: The value of the result of the predicate to do a branch
* @pred: The predicate to execute.
@@ -140,16 +140,16 @@ struct prog_entry {
};
/**
- * update_preds- assign a program entry a label target
+ * update_preds - assign a program entry a label target
* @prog: The program array
* @N: The index of the current entry in @prog
- * @when_to_branch: What to assign a program entry for its branch condition
+ * @invert: What to assign a program entry for its branch condition
*
* The program entry at @N has a target that points to the index of a program
* entry that can have its target and when_to_branch fields updated.
* Update the current program entry denoted by index @N target field to be
* that of the updated entry. This will denote the entry to update if
- * we are processing an "||" after an "&&"
+ * we are processing an "||" after an "&&".
*/
static void update_preds(struct prog_entry *prog, int N, int invert)
{
diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index fcaf226b77446b..5edbf6b1da3f31 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -1988,6 +1988,8 @@ static struct hist_field *create_hist_field(struct hist_trigger_data *hist_data,
hist_field->fn_num = flags & HIST_FIELD_FL_LOG2 ? HIST_FIELD_FN_LOG2 :
HIST_FIELD_FN_BUCKET;
hist_field->operands[0] = create_hist_field(hist_data, field, fl, NULL);
+ if (!hist_field->operands[0])
+ goto free;
hist_field->size = hist_field->operands[0]->size;
hist_field->type = kstrdup_const(hist_field->operands[0]->type, GFP_KERNEL);
if (!hist_field->type)
diff --git a/kernel/trace/trace_export.c b/kernel/trace/trace_export.c
index d960f6b11b5e51..58f3946081e21a 100644
--- a/kernel/trace/trace_export.c
+++ b/kernel/trace/trace_export.c
@@ -111,7 +111,8 @@ static void __always_unused ____ftrace_check_##name(void) \
#define __array(_type, _item, _len) { \
.type = #_type"["__stringify(_len)"]", .name = #_item, \
.size = sizeof(_type[_len]), .align = __alignof__(_type), \
- is_signed_type(_type), .filter_type = FILTER_OTHER },
+ is_signed_type(_type), .filter_type = FILTER_OTHER, \
+ .len = _len },
#undef __array_desc
#define __array_desc(_type, _container, _item, _len) __array(_type, _item, _len)
diff --git a/kernel/trace/trace_osnoise.c b/kernel/trace/trace_osnoise.c
index 94c1b5eb1dc022..210e1f16839295 100644
--- a/kernel/trace/trace_osnoise.c
+++ b/kernel/trace/trace_osnoise.c
@@ -147,9 +147,8 @@ static void osnoise_unregister_instance(struct trace_array *tr)
* register/unregister serialization is provided by trace's
* trace_types_lock.
*/
- lockdep_assert_held(&trace_types_lock);
-
- list_for_each_entry_rcu(inst, &osnoise_instances, list) {
+ list_for_each_entry_rcu(inst, &osnoise_instances, list,
+ lockdep_is_held(&trace_types_lock)) {
if (inst->tr == tr) {
list_del_rcu(&inst->list);
found = 1;
diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c
index 57a13b61f18654..bd475a00f96d13 100644
--- a/kernel/trace/trace_output.c
+++ b/kernel/trace/trace_output.c
@@ -1535,7 +1535,7 @@ static struct trace_event *events[] __initdata = {
NULL
};
-__init static int init_events(void)
+__init int init_events(void)
{
struct trace_event *event;
int i, ret;
@@ -1548,4 +1548,3 @@ __init static int init_events(void)
return 0;
}
-early_initcall(init_events);
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 881c3f84e88a32..02ee440f7be36c 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -754,6 +754,7 @@ config DEBUG_KMEMLEAK
select KALLSYMS
select CRC32
select STACKDEPOT
+ select STACKDEPOT_ALWAYS_INIT if !DEBUG_KMEMLEAK_DEFAULT_OFF
help
Say Y here if you want to enable the memory leak
detector. The memory allocation/freeing is traced in a way
@@ -1207,7 +1208,7 @@ config SCHED_DEBUG
depends on DEBUG_KERNEL && PROC_FS
default y
help
- If you say Y here, the /proc/sched_debug file will be provided
+ If you say Y here, the /sys/kernel/debug/sched file will be provided
that can help debug the scheduler. The runtime overhead of this
option is minimal.
@@ -1917,7 +1918,7 @@ config FUNCTION_ERROR_INJECTION
help
Add fault injections into various functions that are annotated with
ALLOW_ERROR_INJECTION() in the kernel. BPF may also modify the return
- value of theses functions. This is useful to test error paths of code.
+ value of these functions. This is useful to test error paths of code.
If unsure, say N
@@ -2566,6 +2567,15 @@ config MEMCPY_KUNIT_TEST
If unsure, say N.
+config MEMCPY_SLOW_KUNIT_TEST
+ bool "Include exhaustive memcpy tests"
+ depends on MEMCPY_KUNIT_TEST
+ default y
+ help
+ Some memcpy tests are quite exhaustive in checking for overlaps
+ and bit ranges. These can be very slow, so they are split out
+ as a separate config, in case they need to be disabled.
+
config IS_SIGNED_TYPE_KUNIT_TEST
tristate "Test is_signed_type() macro" if !KUNIT_ALL_TESTS
depends on KUNIT
diff --git a/lib/Kconfig.kcsan b/lib/Kconfig.kcsan
index 375575a5a0e3cc..4dedd61e5192d2 100644
--- a/lib/Kconfig.kcsan
+++ b/lib/Kconfig.kcsan
@@ -194,7 +194,7 @@ config KCSAN_WEAK_MEMORY
Enable support for modeling a subset of weak memory, which allows
detecting a subset of data races due to missing memory barriers.
- Depends on KCSAN_STRICT, because the options strenghtening certain
+ Depends on KCSAN_STRICT, because the options strengthening certain
plain accesses by default (depending on !KCSAN_STRICT) reduce the
ability to detect any data races invoving reordered accesses, in
particular reordered writes.
diff --git a/lib/dec_and_lock.c b/lib/dec_and_lock.c
index 9555b68bb774cc..1dcca8f2e19420 100644
--- a/lib/dec_and_lock.c
+++ b/lib/dec_and_lock.c
@@ -49,3 +49,34 @@ int _atomic_dec_and_lock_irqsave(atomic_t *atomic, spinlock_t *lock,
return 0;
}
EXPORT_SYMBOL(_atomic_dec_and_lock_irqsave);
+
+int _atomic_dec_and_raw_lock(atomic_t *atomic, raw_spinlock_t *lock)
+{
+ /* Subtract 1 from counter unless that drops it to 0 (ie. it was 1) */
+ if (atomic_add_unless(atomic, -1, 1))
+ return 0;
+
+ /* Otherwise do it the slow way */
+ raw_spin_lock(lock);
+ if (atomic_dec_and_test(atomic))
+ return 1;
+ raw_spin_unlock(lock);
+ return 0;
+}
+EXPORT_SYMBOL(_atomic_dec_and_raw_lock);
+
+int _atomic_dec_and_raw_lock_irqsave(atomic_t *atomic, raw_spinlock_t *lock,
+ unsigned long *flags)
+{
+ /* Subtract 1 from counter unless that drops it to 0 (ie. it was 1) */
+ if (atomic_add_unless(atomic, -1, 1))
+ return 0;
+
+ /* Otherwise do it the slow way */
+ raw_spin_lock_irqsave(lock, *flags);
+ if (atomic_dec_and_test(atomic))
+ return 1;
+ raw_spin_unlock_irqrestore(lock, *flags);
+ return 0;
+}
+EXPORT_SYMBOL(_atomic_dec_and_raw_lock_irqsave);
diff --git a/lib/kunit/assert.c b/lib/kunit/assert.c
index f5b50babe38d46..05a09652f5a105 100644
--- a/lib/kunit/assert.c
+++ b/lib/kunit/assert.c
@@ -241,24 +241,34 @@ void kunit_mem_assert_format(const struct kunit_assert *assert,
mem_assert = container_of(assert, struct kunit_mem_assert,
assert);
- string_stream_add(stream,
- KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
- mem_assert->text->left_text,
- mem_assert->text->operation,
- mem_assert->text->right_text);
+ if (!mem_assert->left_value) {
+ string_stream_add(stream,
+ KUNIT_SUBTEST_INDENT "Expected %s is not null, but is\n",
+ mem_assert->text->left_text);
+ } else if (!mem_assert->right_value) {
+ string_stream_add(stream,
+ KUNIT_SUBTEST_INDENT "Expected %s is not null, but is\n",
+ mem_assert->text->right_text);
+ } else {
+ string_stream_add(stream,
+ KUNIT_SUBTEST_INDENT "Expected %s %s %s, but\n",
+ mem_assert->text->left_text,
+ mem_assert->text->operation,
+ mem_assert->text->right_text);
- string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s ==\n",
- mem_assert->text->left_text);
- kunit_assert_hexdump(stream, mem_assert->left_value,
- mem_assert->right_value, mem_assert->size);
+ string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s ==\n",
+ mem_assert->text->left_text);
+ kunit_assert_hexdump(stream, mem_assert->left_value,
+ mem_assert->right_value, mem_assert->size);
- string_stream_add(stream, "\n");
+ string_stream_add(stream, "\n");
- string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s ==\n",
- mem_assert->text->right_text);
- kunit_assert_hexdump(stream, mem_assert->right_value,
- mem_assert->left_value, mem_assert->size);
+ string_stream_add(stream, KUNIT_SUBSUBTEST_INDENT "%s ==\n",
+ mem_assert->text->right_text);
+ kunit_assert_hexdump(stream, mem_assert->right_value,
+ mem_assert->left_value, mem_assert->size);
- kunit_assert_print_msg(message, stream);
+ kunit_assert_print_msg(message, stream);
+ }
}
EXPORT_SYMBOL_GPL(kunit_mem_assert_format);
diff --git a/lib/kunit/string-stream.c b/lib/kunit/string-stream.c
index f5f51166d8c20f..cc32743c1171f8 100644
--- a/lib/kunit/string-stream.c
+++ b/lib/kunit/string-stream.c
@@ -23,8 +23,10 @@ static struct string_stream_fragment *alloc_string_stream_fragment(
return ERR_PTR(-ENOMEM);
frag->fragment = kunit_kmalloc(test, len, gfp);
- if (!frag->fragment)
+ if (!frag->fragment) {
+ kunit_kfree(test, frag);
return ERR_PTR(-ENOMEM);
+ }
return frag;
}
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index c9ebf975e56b65..890ba5b3a9819d 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -21,6 +21,7 @@
#include "try-catch-impl.h"
DEFINE_STATIC_KEY_FALSE(kunit_running);
+EXPORT_SYMBOL_GPL(kunit_running);
#if IS_BUILTIN(CONFIG_KUNIT)
/*
diff --git a/lib/lockref.c b/lib/lockref.c
index 45e93ece8ba0d2..2afe4c5d891910 100644
--- a/lib/lockref.c
+++ b/lib/lockref.c
@@ -23,7 +23,6 @@
} \
if (!--retry) \
break; \
- cpu_relax(); \
} \
} while (0)
diff --git a/lib/maple_tree.c b/lib/maple_tree.c
index 26e2045d3cda9e..5a976393c9aeab 100644
--- a/lib/maple_tree.c
+++ b/lib/maple_tree.c
@@ -670,12 +670,13 @@ static inline unsigned long mte_pivot(const struct maple_enode *mn,
unsigned char piv)
{
struct maple_node *node = mte_to_node(mn);
+ enum maple_type type = mte_node_type(mn);
- if (piv >= mt_pivots[piv]) {
+ if (piv >= mt_pivots[type]) {
WARN_ON(1);
return 0;
}
- switch (mte_node_type(mn)) {
+ switch (type) {
case maple_arange_64:
return node->ma64.pivot[piv];
case maple_range_64:
@@ -4887,7 +4888,7 @@ static bool mas_rev_awalk(struct ma_state *mas, unsigned long size)
unsigned long *pivots, *gaps;
void __rcu **slots;
unsigned long gap = 0;
- unsigned long max, min, index;
+ unsigned long max, min;
unsigned char offset;
if (unlikely(mas_is_err(mas)))
@@ -4909,8 +4910,7 @@ static bool mas_rev_awalk(struct ma_state *mas, unsigned long size)
min = mas_safe_min(mas, pivots, --offset);
max = mas_safe_pivot(mas, pivots, offset, type);
- index = mas->index;
- while (index <= max) {
+ while (mas->index <= max) {
gap = 0;
if (gaps)
gap = gaps[offset];
@@ -4941,10 +4941,8 @@ static bool mas_rev_awalk(struct ma_state *mas, unsigned long size)
min = mas_safe_min(mas, pivots, offset);
}
- if (unlikely(index > max)) {
- mas_set_err(mas, -EBUSY);
- return false;
- }
+ if (unlikely((mas->index > max) || (size - 1 > max - mas->index)))
+ goto no_space;
if (unlikely(ma_is_leaf(type))) {
mas->offset = offset;
@@ -4961,9 +4959,11 @@ static bool mas_rev_awalk(struct ma_state *mas, unsigned long size)
return false;
ascend:
- if (mte_is_root(mas->node))
- mas_set_err(mas, -EBUSY);
+ if (!mte_is_root(mas->node))
+ return false;
+no_space:
+ mas_set_err(mas, -EBUSY);
return false;
}
diff --git a/lib/memcpy_kunit.c b/lib/memcpy_kunit.c
index 89128551448d10..887926f0473124 100644
--- a/lib/memcpy_kunit.c
+++ b/lib/memcpy_kunit.c
@@ -309,6 +309,8 @@ static void set_random_nonzero(struct kunit *test, u8 *byte)
static void init_large(struct kunit *test)
{
+ if (!IS_ENABLED(CONFIG_MEMCPY_SLOW_KUNIT_TEST))
+ kunit_skip(test, "Slow test skipped. Enable with CONFIG_MEMCPY_SLOW_KUNIT_TEST=y");
/* Get many bit patterns. */
get_random_bytes(large_src, ARRAY_SIZE(large_src));
diff --git a/lib/nlattr.c b/lib/nlattr.c
index 9055e8b4d144e4..489e15bde5c1d2 100644
--- a/lib/nlattr.c
+++ b/lib/nlattr.c
@@ -10,6 +10,7 @@
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/jiffies.h>
+#include <linux/nospec.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <linux/types.h>
@@ -381,6 +382,7 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
if (type <= 0 || type > maxtype)
return 0;
+ type = array_index_nospec(type, maxtype + 1);
pt = &policy[type];
BUG_ON(pt->type > NLA_TYPE_MAX);
@@ -596,6 +598,7 @@ static int __nla_validate_parse(const struct nlattr *head, int len, int maxtype,
}
continue;
}
+ type = array_index_nospec(type, maxtype + 1);
if (policy) {
int err = validate_nla(nla, maxtype, policy,
validate, extack, depth);
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index a0ad2a7959b5d2..8d7519a8f308d9 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -470,22 +470,27 @@ int sg_alloc_append_table_from_pages(struct sg_append_table *sgt_append,
return -EOPNOTSUPP;
if (sgt_append->prv) {
+ unsigned long next_pfn = (page_to_phys(sg_page(sgt_append->prv)) +
+ sgt_append->prv->offset + sgt_append->prv->length) / PAGE_SIZE;
+
if (WARN_ON(offset))
return -EINVAL;
/* Merge contiguous pages into the last SG */
prv_len = sgt_append->prv->length;
- last_pg = sg_page(sgt_append->prv);
- while (n_pages && pages_are_mergeable(last_pg, pages[0])) {
- if (sgt_append->prv->length + PAGE_SIZE > max_segment)
- break;
- sgt_append->prv->length += PAGE_SIZE;
- last_pg = pages[0];
- pages++;
- n_pages--;
+ if (page_to_pfn(pages[0]) == next_pfn) {
+ last_pg = pfn_to_page(next_pfn - 1);
+ while (n_pages && pages_are_mergeable(pages[0], last_pg)) {
+ if (sgt_append->prv->length + PAGE_SIZE > max_segment)
+ break;
+ sgt_append->prv->length += PAGE_SIZE;
+ last_pg = pages[0];
+ pages++;
+ n_pages--;
+ }
+ if (!n_pages)
+ goto out;
}
- if (!n_pages)
- goto out;
}
/* compute number of contiguous chunks */
diff --git a/lib/test_maple_tree.c b/lib/test_maple_tree.c
index 497fc93ccf9ecf..ec847bf4dcb4dd 100644
--- a/lib/test_maple_tree.c
+++ b/lib/test_maple_tree.c
@@ -2517,6 +2517,91 @@ static noinline void check_bnode_min_spanning(struct maple_tree *mt)
mt_set_non_kernel(0);
}
+static noinline void check_empty_area_window(struct maple_tree *mt)
+{
+ unsigned long i, nr_entries = 20;
+ MA_STATE(mas, mt, 0, 0);
+
+ for (i = 1; i <= nr_entries; i++)
+ mtree_store_range(mt, i*10, i*10 + 9,
+ xa_mk_value(i), GFP_KERNEL);
+
+ /* Create another hole besides the one at 0 */
+ mtree_store_range(mt, 160, 169, NULL, GFP_KERNEL);
+
+ /* Check lower bounds that don't fit */
+ rcu_read_lock();
+ MT_BUG_ON(mt, mas_empty_area_rev(&mas, 5, 90, 10) != -EBUSY);
+
+ mas_reset(&mas);
+ MT_BUG_ON(mt, mas_empty_area_rev(&mas, 6, 90, 5) != -EBUSY);
+
+ /* Check lower bound that does fit */
+ mas_reset(&mas);
+ MT_BUG_ON(mt, mas_empty_area_rev(&mas, 5, 90, 5) != 0);
+ MT_BUG_ON(mt, mas.index != 5);
+ MT_BUG_ON(mt, mas.last != 9);
+ rcu_read_unlock();
+
+ /* Check one gap that doesn't fit and one that does */
+ rcu_read_lock();
+ mas_reset(&mas);
+ MT_BUG_ON(mt, mas_empty_area_rev(&mas, 5, 217, 9) != 0);
+ MT_BUG_ON(mt, mas.index != 161);
+ MT_BUG_ON(mt, mas.last != 169);
+
+ /* Check one gap that does fit above the min */
+ mas_reset(&mas);
+ MT_BUG_ON(mt, mas_empty_area_rev(&mas, 100, 218, 3) != 0);
+ MT_BUG_ON(mt, mas.index != 216);
+ MT_BUG_ON(mt, mas.last != 218);
+
+ /* Check size that doesn't fit any gap */
+ mas_reset(&mas);
+ MT_BUG_ON(mt, mas_empty_area_rev(&mas, 100, 218, 16) != -EBUSY);
+
+ /*
+ * Check size that doesn't fit the lower end of the window but
+ * does fit the gap
+ */
+ mas_reset(&mas);
+ MT_BUG_ON(mt, mas_empty_area_rev(&mas, 167, 200, 4) != -EBUSY);
+
+ /*
+ * Check size that doesn't fit the upper end of the window but
+ * does fit the gap
+ */
+ mas_reset(&mas);
+ MT_BUG_ON(mt, mas_empty_area_rev(&mas, 100, 162, 4) != -EBUSY);
+
+ /* Check mas_empty_area forward */
+ mas_reset(&mas);
+ MT_BUG_ON(mt, mas_empty_area(&mas, 0, 100, 9) != 0);
+ MT_BUG_ON(mt, mas.index != 0);
+ MT_BUG_ON(mt, mas.last != 8);
+
+ mas_reset(&mas);
+ MT_BUG_ON(mt, mas_empty_area(&mas, 0, 100, 4) != 0);
+ MT_BUG_ON(mt, mas.index != 0);
+ MT_BUG_ON(mt, mas.last != 3);
+
+ mas_reset(&mas);
+ MT_BUG_ON(mt, mas_empty_area(&mas, 0, 100, 11) != -EBUSY);
+
+ mas_reset(&mas);
+ MT_BUG_ON(mt, mas_empty_area(&mas, 5, 100, 6) != -EBUSY);
+
+ mas_reset(&mas);
+ MT_BUG_ON(mt, mas_empty_area(&mas, 0, 8, 10) != -EBUSY);
+
+ mas_reset(&mas);
+ mas_empty_area(&mas, 100, 165, 3);
+
+ mas_reset(&mas);
+ MT_BUG_ON(mt, mas_empty_area(&mas, 100, 163, 6) != -EBUSY);
+ rcu_read_unlock();
+}
+
static DEFINE_MTREE(tree);
static int maple_tree_seed(void)
{
@@ -2765,6 +2850,10 @@ static int maple_tree_seed(void)
check_bnode_min_spanning(&tree);
mtree_destroy(&tree);
+ mt_init_flags(&tree, MT_FLAGS_ALLOC_RANGE);
+ check_empty_area_window(&tree);
+ mtree_destroy(&tree);
+
#if defined(BENCH)
skip:
#endif
diff --git a/lib/win_minmax.c b/lib/win_minmax.c
index 6bdc1cd15f7614..ec10506834b629 100644
--- a/lib/win_minmax.c
+++ b/lib/win_minmax.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
-/**
+/*
* lib/minmax.c: windowed min/max tracker
*
* Kathleen Nichols' algorithm for tracking the minimum (or maximum)
diff --git a/mm/compaction.c b/mm/compaction.c
index ca1603524bbe00..8238e83385a791 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -1839,6 +1839,7 @@ static unsigned long fast_find_migrateblock(struct compact_control *cc)
pfn = cc->zone->zone_start_pfn;
cc->fast_search_fail = 0;
found_block = true;
+ set_pageblock_skip(freepage);
break;
}
}
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index db895230ee7e4e..bdbfeb6fb3934d 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -94,6 +94,8 @@ static int hugetlb_acct_memory(struct hstate *h, long delta);
static void hugetlb_vma_lock_free(struct vm_area_struct *vma);
static void hugetlb_vma_lock_alloc(struct vm_area_struct *vma);
static void __hugetlb_vma_unlock_write_free(struct vm_area_struct *vma);
+static void hugetlb_unshare_pmds(struct vm_area_struct *vma,
+ unsigned long start, unsigned long end);
static inline bool subpool_is_free(struct hugepage_subpool *spool)
{
@@ -1181,7 +1183,7 @@ void hugetlb_dup_vma_private(struct vm_area_struct *vma)
/*
* Reset and decrement one ref on hugepage private reservation.
- * Called with mm->mmap_sem writer semaphore held.
+ * Called with mm->mmap_lock writer semaphore held.
* This function should be only used by move_vma() and operate on
* same sized vma. It should never come here with last ref on the
* reservation.
@@ -4834,6 +4836,25 @@ static int hugetlb_vm_op_split(struct vm_area_struct *vma, unsigned long addr)
{
if (addr & ~(huge_page_mask(hstate_vma(vma))))
return -EINVAL;
+
+ /*
+ * PMD sharing is only possible for PUD_SIZE-aligned address ranges
+ * in HugeTLB VMAs. If we will lose PUD_SIZE alignment due to this
+ * split, unshare PMDs in the PUD_SIZE interval surrounding addr now.
+ */
+ if (addr & ~PUD_MASK) {
+ /*
+ * hugetlb_vm_op_split is called right before we attempt to
+ * split the VMA. We will need to unshare PMDs in the old and
+ * new VMAs, so let's unshare before we split.
+ */
+ unsigned long floor = addr & PUD_MASK;
+ unsigned long ceil = floor + PUD_SIZE;
+
+ if (floor >= vma->vm_start && ceil <= vma->vm_end)
+ hugetlb_unshare_pmds(vma, floor, ceil);
+ }
+
return 0;
}
@@ -5030,6 +5051,9 @@ again:
entry = huge_pte_clear_uffd_wp(entry);
set_huge_pte_at(dst, addr, dst_pte, entry);
} else if (unlikely(is_pte_marker(entry))) {
+ /* No swap on hugetlb */
+ WARN_ON_ONCE(
+ is_swapin_error_entry(pte_to_swp_entry(entry)));
/*
* We copy the pte marker only if the dst vma has
* uffd-wp enabled.
@@ -5131,7 +5155,7 @@ static void move_huge_pte(struct vm_area_struct *vma, unsigned long old_addr,
/*
* We don't have to worry about the ordering of src and dst ptlocks
- * because exclusive mmap_sem (or the i_mmap_lock) prevents deadlock.
+ * because exclusive mmap_lock (or the i_mmap_lock) prevents deadlock.
*/
if (src_ptl != dst_ptl)
spin_lock_nested(src_ptl, SINGLE_DEPTH_NESTING);
@@ -6639,8 +6663,17 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
spinlock_t *ptl;
ptep = huge_pte_offset(mm, address, psize);
if (!ptep) {
- address |= last_addr_mask;
- continue;
+ if (!uffd_wp) {
+ address |= last_addr_mask;
+ continue;
+ }
+ /*
+ * Userfaultfd wr-protect requires pgtable
+ * pre-allocations to install pte markers.
+ */
+ ptep = huge_pte_alloc(mm, vma, address, psize);
+ if (!ptep)
+ break;
}
ptl = huge_pte_lock(h, mm, ptep);
if (huge_pmd_unshare(mm, vma, address, ptep)) {
@@ -6658,16 +6691,13 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
}
pte = huge_ptep_get(ptep);
if (unlikely(is_hugetlb_entry_hwpoisoned(pte))) {
- spin_unlock(ptl);
- continue;
- }
- if (unlikely(is_hugetlb_entry_migration(pte))) {
+ /* Nothing to do. */
+ } else if (unlikely(is_hugetlb_entry_migration(pte))) {
swp_entry_t entry = pte_to_swp_entry(pte);
struct page *page = pfn_swap_entry_to_page(entry);
+ pte_t newpte = pte;
- if (!is_readable_migration_entry(entry)) {
- pte_t newpte;
-
+ if (is_writable_migration_entry(entry)) {
if (PageAnon(page))
entry = make_readable_exclusive_migration_entry(
swp_offset(entry));
@@ -6675,25 +6705,22 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma,
entry = make_readable_migration_entry(
swp_offset(entry));
newpte = swp_entry_to_pte(entry);
- if (uffd_wp)
- newpte = pte_swp_mkuffd_wp(newpte);
- else if (uffd_wp_resolve)
- newpte = pte_swp_clear_uffd_wp(newpte);
- set_huge_pte_at(mm, address, ptep, newpte);
pages++;
}
- spin_unlock(ptl);
- continue;
- }
- if (unlikely(pte_marker_uffd_wp(pte))) {
- /*
- * This is changing a non-present pte into a none pte,
- * no need for huge_ptep_modify_prot_start/commit().
- */
+
+ if (uffd_wp)
+ newpte = pte_swp_mkuffd_wp(newpte);
+ else if (uffd_wp_resolve)
+ newpte = pte_swp_clear_uffd_wp(newpte);
+ if (!pte_same(pte, newpte))
+ set_huge_pte_at(mm, address, ptep, newpte);
+ } else if (unlikely(is_pte_marker(pte))) {
+ /* No other markers apply for now. */
+ WARN_ON_ONCE(!pte_marker_uffd_wp(pte));
if (uffd_wp_resolve)
+ /* Safe to modify directly (non-present->none). */
huge_pte_clear(mm, address, ptep, psize);
- }
- if (!huge_pte_none(pte)) {
+ } else if (!huge_pte_none(pte)) {
pte_t old_pte;
unsigned int shift = huge_page_shift(hstate_vma(vma));
@@ -7328,26 +7355,21 @@ void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, int re
}
}
-/*
- * This function will unconditionally remove all the shared pmd pgtable entries
- * within the specific vma for a hugetlbfs memory range.
- */
-void hugetlb_unshare_all_pmds(struct vm_area_struct *vma)
+static void hugetlb_unshare_pmds(struct vm_area_struct *vma,
+ unsigned long start,
+ unsigned long end)
{
struct hstate *h = hstate_vma(vma);
unsigned long sz = huge_page_size(h);
struct mm_struct *mm = vma->vm_mm;
struct mmu_notifier_range range;
- unsigned long address, start, end;
+ unsigned long address;
spinlock_t *ptl;
pte_t *ptep;
if (!(vma->vm_flags & VM_MAYSHARE))
return;
- start = ALIGN(vma->vm_start, PUD_SIZE);
- end = ALIGN_DOWN(vma->vm_end, PUD_SIZE);
-
if (start >= end)
return;
@@ -7379,6 +7401,16 @@ void hugetlb_unshare_all_pmds(struct vm_area_struct *vma)
mmu_notifier_invalidate_range_end(&range);
}
+/*
+ * This function will unconditionally remove all the shared pmd pgtable entries
+ * within the specific vma for a hugetlbfs memory range.
+ */
+void hugetlb_unshare_all_pmds(struct vm_area_struct *vma)
+{
+ hugetlb_unshare_pmds(vma, ALIGN(vma->vm_start, PUD_SIZE),
+ ALIGN_DOWN(vma->vm_end, PUD_SIZE));
+}
+
#ifdef CONFIG_CMA
static bool cma_reserve_called __initdata;
diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index 1d02757e90a326..22598b20c7b758 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -119,7 +119,7 @@ EXPORT_SYMBOL_GPL(kasan_restore_multi_shot);
* Whether the KASAN KUnit test suite is currently being executed.
* Updated in kasan_test.c.
*/
-bool kasan_kunit_executing;
+static bool kasan_kunit_executing;
void kasan_kunit_test_suite_start(void)
{
diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index 5cb401aa2b9d8b..90acfea40c13c9 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -847,6 +847,10 @@ static int hugepage_vma_revalidate(struct mm_struct *mm, unsigned long address,
return SCAN_SUCCEED;
}
+/*
+ * See pmd_trans_unstable() for how the result may change out from
+ * underneath us, even if we hold mmap_lock in read.
+ */
static int find_pmd_or_thp_or_none(struct mm_struct *mm,
unsigned long address,
pmd_t **pmd)
@@ -865,8 +869,12 @@ static int find_pmd_or_thp_or_none(struct mm_struct *mm,
#endif
if (pmd_none(pmde))
return SCAN_PMD_NONE;
+ if (!pmd_present(pmde))
+ return SCAN_PMD_NULL;
if (pmd_trans_huge(pmde))
return SCAN_PMD_MAPPED;
+ if (pmd_devmap(pmde))
+ return SCAN_PMD_NULL;
if (pmd_bad(pmde))
return SCAN_PMD_NULL;
return SCAN_SUCCEED;
@@ -1460,14 +1468,6 @@ int collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr,
if (!hugepage_vma_check(vma, vma->vm_flags, false, false, false))
return SCAN_VMA_CHECK;
- /*
- * Symmetry with retract_page_tables(): Exclude MAP_PRIVATE mappings
- * that got written to. Without this, we'd have to also lock the
- * anon_vma if one exists.
- */
- if (vma->anon_vma)
- return SCAN_VMA_CHECK;
-
/* Keep pmd pgtable for uffd-wp; see comment in retract_page_tables() */
if (userfaultfd_wp(vma))
return SCAN_PTE_UFFD_WP;
@@ -1567,8 +1567,14 @@ int collapse_pte_mapped_thp(struct mm_struct *mm, unsigned long addr,
}
/* step 4: remove pte entries */
+ /* we make no change to anon, but protect concurrent anon page lookup */
+ if (vma->anon_vma)
+ anon_vma_lock_write(vma->anon_vma);
+
collapse_and_free_pmd(mm, vma, haddr, pmd);
+ if (vma->anon_vma)
+ anon_vma_unlock_write(vma->anon_vma);
i_mmap_unlock_write(vma->vm_file->f_mapping);
maybe_install_pmd:
@@ -1644,7 +1650,7 @@ static int retract_page_tables(struct address_space *mapping, pgoff_t pgoff,
* has higher cost too. It would also probably require locking
* the anon_vma.
*/
- if (vma->anon_vma) {
+ if (READ_ONCE(vma->anon_vma)) {
result = SCAN_PAGE_ANON;
goto next;
}
@@ -1673,6 +1679,18 @@ static int retract_page_tables(struct address_space *mapping, pgoff_t pgoff,
if ((cc->is_khugepaged || is_target) &&
mmap_write_trylock(mm)) {
/*
+ * Re-check whether we have an ->anon_vma, because
+ * collapse_and_free_pmd() requires that either no
+ * ->anon_vma exists or the anon_vma is locked.
+ * We already checked ->anon_vma above, but that check
+ * is racy because ->anon_vma can be populated under the
+ * mmap lock in read mode.
+ */
+ if (vma->anon_vma) {
+ result = SCAN_PAGE_ANON;
+ goto unlock_next;
+ }
+ /*
* When a vma is registered with uffd-wp, we can't
* recycle the pmd pgtable because there can be pte
* markers installed. Skip it only, so the rest mm/vma
@@ -2649,7 +2667,7 @@ int madvise_collapse(struct vm_area_struct *vma, struct vm_area_struct **prev,
goto out_nolock;
}
- hend = vma->vm_end & HPAGE_PMD_MASK;
+ hend = min(hend, vma->vm_end & HPAGE_PMD_MASK);
}
mmap_assert_locked(mm);
memset(cc->node_load, 0, sizeof(cc->node_load));
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 92f670edbf5187..55dc8b8b061623 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -2070,8 +2070,10 @@ static int __init kmemleak_boot_config(char *str)
return -EINVAL;
if (strcmp(str, "off") == 0)
kmemleak_disable();
- else if (strcmp(str, "on") == 0)
+ else if (strcmp(str, "on") == 0) {
kmemleak_skip_disable = 1;
+ stack_depot_want_early_init();
+ }
else
return -EINVAL;
return 0;
@@ -2093,7 +2095,6 @@ void __init kmemleak_init(void)
if (kmemleak_error)
return;
- stack_depot_init();
jiffies_min_age = msecs_to_jiffies(MSECS_MIN_AGE);
jiffies_scan_wait = msecs_to_jiffies(SECS_SCAN_WAIT * 1000);
diff --git a/mm/madvise.c b/mm/madvise.c
index a56a6d17e201e3..b6ea204d4e23bd 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -130,7 +130,7 @@ static int replace_anon_vma_name(struct vm_area_struct *vma,
#endif /* CONFIG_ANON_VMA_NAME */
/*
* Update the vm_flags on region of a vma, splitting it or merging it as
- * necessary. Must be called with mmap_sem held for writing;
+ * necessary. Must be called with mmap_lock held for writing;
* Caller should ensure anon_name stability by raising its refcount even when
* anon_name belongs to a valid vma because this function might free that vma.
*/
diff --git a/mm/memblock.c b/mm/memblock.c
index 511d4783dcf1d8..d036c7861310cb 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -836,7 +836,7 @@ void __init_memblock memblock_free(void *ptr, size_t size)
* @base: phys starting address of the boot memory block
* @size: size of the boot memory block in bytes
*
- * Free boot memory block previously allocated by memblock_alloc_xx() API.
+ * Free boot memory block previously allocated by memblock_phys_alloc_xx() API.
* The freeing memory will not be released to the buddy allocator.
*/
int __init_memblock memblock_phys_free(phys_addr_t base, phys_addr_t size)
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index ab457f0394ab6e..73afff8062f9bf 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -63,7 +63,6 @@
#include <linux/resume_user_mode.h>
#include <linux/psi.h>
#include <linux/seq_buf.h>
-#include <linux/parser.h>
#include "internal.h"
#include <net/sock.h>
#include <net/ip.h>
@@ -2393,8 +2392,7 @@ static unsigned long reclaim_high(struct mem_cgroup *memcg,
psi_memstall_enter(&pflags);
nr_reclaimed += try_to_free_mem_cgroup_pages(memcg, nr_pages,
gfp_mask,
- MEMCG_RECLAIM_MAY_SWAP,
- NULL);
+ MEMCG_RECLAIM_MAY_SWAP);
psi_memstall_leave(&pflags);
} while ((memcg = parent_mem_cgroup(memcg)) &&
!mem_cgroup_is_root(memcg));
@@ -2685,8 +2683,7 @@ retry:
psi_memstall_enter(&pflags);
nr_reclaimed = try_to_free_mem_cgroup_pages(mem_over_limit, nr_pages,
- gfp_mask, reclaim_options,
- NULL);
+ gfp_mask, reclaim_options);
psi_memstall_leave(&pflags);
if (mem_cgroup_margin(mem_over_limit) >= nr_pages)
@@ -3506,8 +3503,7 @@ static int mem_cgroup_resize_max(struct mem_cgroup *memcg,
}
if (!try_to_free_mem_cgroup_pages(memcg, 1, GFP_KERNEL,
- memsw ? 0 : MEMCG_RECLAIM_MAY_SWAP,
- NULL)) {
+ memsw ? 0 : MEMCG_RECLAIM_MAY_SWAP)) {
ret = -EBUSY;
break;
}
@@ -3618,8 +3614,7 @@ static int mem_cgroup_force_empty(struct mem_cgroup *memcg)
return -EINTR;
if (!try_to_free_mem_cgroup_pages(memcg, 1, GFP_KERNEL,
- MEMCG_RECLAIM_MAY_SWAP,
- NULL))
+ MEMCG_RECLAIM_MAY_SWAP))
nr_retries--;
}
@@ -6429,8 +6424,7 @@ static ssize_t memory_high_write(struct kernfs_open_file *of,
}
reclaimed = try_to_free_mem_cgroup_pages(memcg, nr_pages - high,
- GFP_KERNEL, MEMCG_RECLAIM_MAY_SWAP,
- NULL);
+ GFP_KERNEL, MEMCG_RECLAIM_MAY_SWAP);
if (!reclaimed && !nr_retries--)
break;
@@ -6479,8 +6473,7 @@ static ssize_t memory_max_write(struct kernfs_open_file *of,
if (nr_reclaims) {
if (!try_to_free_mem_cgroup_pages(memcg, nr_pages - max,
- GFP_KERNEL, MEMCG_RECLAIM_MAY_SWAP,
- NULL))
+ GFP_KERNEL, MEMCG_RECLAIM_MAY_SWAP))
nr_reclaims--;
continue;
}
@@ -6603,54 +6596,21 @@ static ssize_t memory_oom_group_write(struct kernfs_open_file *of,
return nbytes;
}
-enum {
- MEMORY_RECLAIM_NODES = 0,
- MEMORY_RECLAIM_NULL,
-};
-
-static const match_table_t if_tokens = {
- { MEMORY_RECLAIM_NODES, "nodes=%s" },
- { MEMORY_RECLAIM_NULL, NULL },
-};
-
static ssize_t memory_reclaim(struct kernfs_open_file *of, char *buf,
size_t nbytes, loff_t off)
{
struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of));
unsigned int nr_retries = MAX_RECLAIM_RETRIES;
unsigned long nr_to_reclaim, nr_reclaimed = 0;
- unsigned int reclaim_options = MEMCG_RECLAIM_MAY_SWAP |
- MEMCG_RECLAIM_PROACTIVE;
- char *old_buf, *start;
- substring_t args[MAX_OPT_ARGS];
- int token;
- char value[256];
- nodemask_t nodemask = NODE_MASK_ALL;
-
- buf = strstrip(buf);
-
- old_buf = buf;
- nr_to_reclaim = memparse(buf, &buf) / PAGE_SIZE;
- if (buf == old_buf)
- return -EINVAL;
+ unsigned int reclaim_options;
+ int err;
buf = strstrip(buf);
+ err = page_counter_memparse(buf, "", &nr_to_reclaim);
+ if (err)
+ return err;
- while ((start = strsep(&buf, " ")) != NULL) {
- if (!strlen(start))
- continue;
- token = match_token(start, if_tokens, args);
- match_strlcpy(value, args, sizeof(value));
- switch (token) {
- case MEMORY_RECLAIM_NODES:
- if (nodelist_parse(value, nodemask) < 0)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
- }
-
+ reclaim_options = MEMCG_RECLAIM_MAY_SWAP | MEMCG_RECLAIM_PROACTIVE;
while (nr_reclaimed < nr_to_reclaim) {
unsigned long reclaimed;
@@ -6667,8 +6627,7 @@ static ssize_t memory_reclaim(struct kernfs_open_file *of, char *buf,
reclaimed = try_to_free_mem_cgroup_pages(memcg,
nr_to_reclaim - nr_reclaimed,
- GFP_KERNEL, reclaim_options,
- &nodemask);
+ GFP_KERNEL, reclaim_options);
if (!reclaimed && !nr_retries--)
return -EAGAIN;
diff --git a/mm/memory.c b/mm/memory.c
index aad226daf41bd5..3e836fecd0354c 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -828,12 +828,8 @@ copy_nonpresent_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm,
return -EBUSY;
return -ENOENT;
} else if (is_pte_marker_entry(entry)) {
- /*
- * We're copying the pgtable should only because dst_vma has
- * uffd-wp enabled, do sanity check.
- */
- WARN_ON_ONCE(!userfaultfd_wp(dst_vma));
- set_pte_at(dst_mm, addr, dst_pte, pte);
+ if (is_swapin_error_entry(entry) || userfaultfd_wp(dst_vma))
+ set_pte_at(dst_mm, addr, dst_pte, pte);
return 0;
}
if (!userfaultfd_wp(dst_vma))
@@ -3629,8 +3625,12 @@ static vm_fault_t pte_marker_clear(struct vm_fault *vmf)
/*
* Be careful so that we will only recover a special uffd-wp pte into a
* none pte. Otherwise it means the pte could have changed, so retry.
+ *
+ * This should also cover the case where e.g. the pte changed
+ * quickly from a PTE_MARKER_UFFD_WP into PTE_MARKER_SWAPIN_ERROR.
+ * So is_pte_marker() check is not enough to safely drop the pte.
*/
- if (is_pte_marker(*vmf->pte))
+ if (pte_same(vmf->orig_pte, *vmf->pte))
pte_clear(vmf->vma->vm_mm, vmf->address, vmf->pte);
pte_unmap_unlock(vmf->pte, vmf->ptl);
return 0;
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 02c8a712282f11..f940395667c82f 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -600,7 +600,8 @@ static int queue_pages_hugetlb(pte_t *pte, unsigned long hmask,
/* With MPOL_MF_MOVE, we migrate only unshared hugepage. */
if (flags & (MPOL_MF_MOVE_ALL) ||
- (flags & MPOL_MF_MOVE && page_mapcount(page) == 1)) {
+ (flags & MPOL_MF_MOVE && page_mapcount(page) == 1 &&
+ !hugetlb_pmd_shared(pte))) {
if (isolate_hugetlb(page, qp->pagelist) &&
(flags & MPOL_MF_STRICT))
/*
diff --git a/mm/mmap.c b/mm/mmap.c
index 87d929316d5726..425a9349e61082 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1524,6 +1524,10 @@ int vma_wants_writenotify(struct vm_area_struct *vma, pgprot_t vm_page_prot)
if (vma_soft_dirty_enabled(vma) && !is_vm_hugetlb_page(vma))
return 1;
+ /* Do we need write faults for uffd-wp tracking? */
+ if (userfaultfd_wp(vma))
+ return 1;
+
/* Specialty mapping? */
if (vm_flags & VM_PFNMAP)
return 0;
@@ -2290,7 +2294,7 @@ static inline int munmap_sidetree(struct vm_area_struct *vma,
* @start: The aligned start address to munmap.
* @end: The aligned end address to munmap.
* @uf: The userfaultfd list_head
- * @downgrade: Set to true to attempt a write downgrade of the mmap_sem
+ * @downgrade: Set to true to attempt a write downgrade of the mmap_lock
*
* If @downgrade is true, check return code for potential release of the lock.
*/
@@ -2465,7 +2469,7 @@ map_count_exceeded:
* @len: The length of the range to munmap
* @uf: The userfaultfd list_head
* @downgrade: set to true if the user wants to attempt to write_downgrade the
- * mmap_sem
+ * mmap_lock
*
* This function takes a @mas that is either pointing to the previous VMA or set
* to MA_START and sets it up to remove the mapping(s). The @len will be
diff --git a/mm/mprotect.c b/mm/mprotect.c
index 908df12caa2685..61cf60015a8b8a 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -245,7 +245,13 @@ static unsigned long change_pte_range(struct mmu_gather *tlb,
newpte = pte_swp_mksoft_dirty(newpte);
if (pte_swp_uffd_wp(oldpte))
newpte = pte_swp_mkuffd_wp(newpte);
- } else if (pte_marker_entry_uffd_wp(entry)) {
+ } else if (is_pte_marker_entry(entry)) {
+ /*
+ * Ignore swapin errors unconditionally,
+ * because any access should sigbus anyway.
+ */
+ if (is_swapin_error_entry(entry))
+ continue;
/*
* If this is uffd-wp pte marker and we'd like
* to unprotect it, drop it; the next page
diff --git a/mm/mremap.c b/mm/mremap.c
index fe587c5d65913a..930f65c315c024 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -1027,16 +1027,29 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len,
}
/*
- * Function vma_merge() is called on the extension we are adding to
- * the already existing vma, vma_merge() will merge this extension with
- * the already existing vma (expand operation itself) and possibly also
- * with the next vma if it becomes adjacent to the expanded vma and
- * otherwise compatible.
+ * Function vma_merge() is called on the extension we
+ * are adding to the already existing vma, vma_merge()
+ * will merge this extension with the already existing
+ * vma (expand operation itself) and possibly also with
+ * the next vma if it becomes adjacent to the expanded
+ * vma and otherwise compatible.
+ *
+ * However, vma_merge() can currently fail due to
+ * is_mergeable_vma() check for vm_ops->close (see the
+ * comment there). Yet this should not prevent vma
+ * expanding, so perform a simple expand for such vma.
+ * Ideally the check for close op should be only done
+ * when a vma would be actually removed due to a merge.
*/
- vma = vma_merge(mm, vma, extension_start, extension_end,
+ if (!vma->vm_ops || !vma->vm_ops->close) {
+ vma = vma_merge(mm, vma, extension_start, extension_end,
vma->vm_flags, vma->anon_vma, vma->vm_file,
extension_pgoff, vma_policy(vma),
vma->vm_userfaultfd_ctx, anon_vma_name(vma));
+ } else if (vma_adjust(vma, vma->vm_start, addr + new_len,
+ vma->vm_pgoff, NULL)) {
+ vma = NULL;
+ }
if (!vma) {
vm_unacct_memory(pages);
ret = -ENOMEM;
diff --git a/mm/nommu.c b/mm/nommu.c
index 214c70e1d05942..5b83938ecb67cf 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -559,7 +559,6 @@ void vma_mas_remove(struct vm_area_struct *vma, struct ma_state *mas)
static void setup_vma_to_mm(struct vm_area_struct *vma, struct mm_struct *mm)
{
- mm->map_count++;
vma->vm_mm = mm;
/* add the VMA to the mapping */
@@ -587,6 +586,7 @@ static void mas_add_vma_to_mm(struct ma_state *mas, struct mm_struct *mm,
BUG_ON(!vma->vm_region);
setup_vma_to_mm(vma, mm);
+ mm->map_count++;
/* add the VMA to the tree */
vma_mas_store(vma, mas);
@@ -1240,6 +1240,7 @@ share:
error_just_free:
up_write(&nommu_region_sem);
error:
+ mas_destroy(&mas);
if (region->vm_file)
fput(region->vm_file);
kmem_cache_free(vm_region_jar, region);
@@ -1250,7 +1251,6 @@ error:
sharing_violation:
up_write(&nommu_region_sem);
- mas_destroy(&mas);
pr_warn("Attempt to share mismatched mappings\n");
ret = -EINVAL;
goto error;
@@ -1347,6 +1347,7 @@ int split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
if (vma->vm_file)
return -ENOMEM;
+ mm = vma->vm_mm;
if (mm->map_count >= sysctl_max_map_count)
return -ENOMEM;
@@ -1398,6 +1399,7 @@ int split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
mas_set_range(&mas, vma->vm_start, vma->vm_end - 1);
mas_store(&mas, vma);
vma_mas_store(new, &mas);
+ mm->map_count++;
return 0;
err_mas_preallocate:
@@ -1509,7 +1511,8 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len, struct list
erase_whole_vma:
if (delete_vma_from_mm(vma))
ret = -ENOMEM;
- delete_vma(mm, vma);
+ else
+ delete_vma(mm, vma);
return ret;
}
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 0745aedebb3714..3bb3484563eda5 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -5631,9 +5631,12 @@ EXPORT_SYMBOL(get_zeroed_page);
*/
void __free_pages(struct page *page, unsigned int order)
{
+ /* get PageHead before we drop reference */
+ int head = PageHead(page);
+
if (put_page_testzero(page))
free_the_page(page, order);
- else if (!PageHead(page))
+ else if (!head)
while (order-- > 0)
free_the_page(page + (1 << order), order);
}
diff --git a/mm/shmem.c b/mm/shmem.c
index c301487be5fb40..0005ab2c29af7b 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -478,12 +478,10 @@ bool shmem_is_huge(struct vm_area_struct *vma, struct inode *inode,
if (vma && ((vma->vm_flags & VM_NOHUGEPAGE) ||
test_bit(MMF_DISABLE_THP, &vma->vm_mm->flags)))
return false;
- if (shmem_huge_force)
- return true;
- if (shmem_huge == SHMEM_HUGE_FORCE)
- return true;
if (shmem_huge == SHMEM_HUGE_DENY)
return false;
+ if (shmem_huge_force || shmem_huge == SHMEM_HUGE_FORCE)
+ return true;
switch (SHMEM_SB(inode->i_sb)->huge) {
case SHMEM_HUGE_ALWAYS:
diff --git a/mm/slab.c b/mm/slab.c
index 7a269db050eed8..29300fc1289a83 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -2211,6 +2211,8 @@ static int drain_freelist(struct kmem_cache *cache,
raw_spin_unlock_irq(&n->list_lock);
slab_destroy(cache, slab);
nr_freed++;
+
+ cond_resched();
}
out:
return nr_freed;
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 908a529bca12c9..4fa440e87cd693 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -1100,6 +1100,7 @@ start_over:
goto check_out;
pr_debug("scan_swap_map of si %d failed to find offset\n",
si->type);
+ cond_resched();
spin_lock(&swap_avail_lock);
nextsi:
diff --git a/mm/vmscan.c b/mm/vmscan.c
index bd6637fcd8f9b1..bf3eedf0209cec 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -3323,13 +3323,16 @@ void lru_gen_migrate_mm(struct mm_struct *mm)
if (mem_cgroup_disabled())
return;
+ /* migration can happen before addition */
+ if (!mm->lru_gen.memcg)
+ return;
+
rcu_read_lock();
memcg = mem_cgroup_from_task(task);
rcu_read_unlock();
if (memcg == mm->lru_gen.memcg)
return;
- VM_WARN_ON_ONCE(!mm->lru_gen.memcg);
VM_WARN_ON_ONCE(list_empty(&mm->lru_gen.list));
lru_gen_del_mm(mm);
@@ -6754,8 +6757,7 @@ unsigned long mem_cgroup_shrink_node(struct mem_cgroup *memcg,
unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
unsigned long nr_pages,
gfp_t gfp_mask,
- unsigned int reclaim_options,
- nodemask_t *nodemask)
+ unsigned int reclaim_options)
{
unsigned long nr_reclaimed;
unsigned int noreclaim_flag;
@@ -6770,7 +6772,6 @@ unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg,
.may_unmap = 1,
.may_swap = !!(reclaim_options & MEMCG_RECLAIM_MAY_SWAP),
.proactive = !!(reclaim_options & MEMCG_RECLAIM_PROACTIVE),
- .nodemask = nodemask,
};
/*
* Traverse the ZONELIST_FALLBACK zonelist of the current node to put
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 9445bee6b0140b..702bc3fd687abe 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -113,7 +113,23 @@
* have room for two bit at least.
*/
#define OBJ_ALLOCATED_TAG 1
-#define OBJ_TAG_BITS 1
+
+#ifdef CONFIG_ZPOOL
+/*
+ * The second least-significant bit in the object's header identifies if the
+ * value stored at the header is a deferred handle from the last reclaim
+ * attempt.
+ *
+ * As noted above, this is valid because we have room for two bits.
+ */
+#define OBJ_DEFERRED_HANDLE_TAG 2
+#define OBJ_TAG_BITS 2
+#define OBJ_TAG_MASK (OBJ_ALLOCATED_TAG | OBJ_DEFERRED_HANDLE_TAG)
+#else
+#define OBJ_TAG_BITS 1
+#define OBJ_TAG_MASK OBJ_ALLOCATED_TAG
+#endif /* CONFIG_ZPOOL */
+
#define OBJ_INDEX_BITS (BITS_PER_LONG - _PFN_BITS - OBJ_TAG_BITS)
#define OBJ_INDEX_MASK ((_AC(1, UL) << OBJ_INDEX_BITS) - 1)
@@ -222,6 +238,12 @@ struct link_free {
* Handle of allocated object.
*/
unsigned long handle;
+#ifdef CONFIG_ZPOOL
+ /*
+ * Deferred handle of a reclaimed object.
+ */
+ unsigned long deferred_handle;
+#endif
};
};
@@ -272,8 +294,6 @@ struct zspage {
/* links the zspage to the lru list in the pool */
struct list_head lru;
bool under_reclaim;
- /* list of unfreed handles whose objects have been reclaimed */
- unsigned long *deferred_handles;
#endif
struct zs_pool *pool;
@@ -897,7 +917,8 @@ static unsigned long handle_to_obj(unsigned long handle)
return *(unsigned long *)handle;
}
-static bool obj_allocated(struct page *page, void *obj, unsigned long *phandle)
+static bool obj_tagged(struct page *page, void *obj, unsigned long *phandle,
+ int tag)
{
unsigned long handle;
struct zspage *zspage = get_zspage(page);
@@ -908,13 +929,27 @@ static bool obj_allocated(struct page *page, void *obj, unsigned long *phandle)
} else
handle = *(unsigned long *)obj;
- if (!(handle & OBJ_ALLOCATED_TAG))
+ if (!(handle & tag))
return false;
- *phandle = handle & ~OBJ_ALLOCATED_TAG;
+ /* Clear all tags before returning the handle */
+ *phandle = handle & ~OBJ_TAG_MASK;
return true;
}
+static inline bool obj_allocated(struct page *page, void *obj, unsigned long *phandle)
+{
+ return obj_tagged(page, obj, phandle, OBJ_ALLOCATED_TAG);
+}
+
+#ifdef CONFIG_ZPOOL
+static bool obj_stores_deferred_handle(struct page *page, void *obj,
+ unsigned long *phandle)
+{
+ return obj_tagged(page, obj, phandle, OBJ_DEFERRED_HANDLE_TAG);
+}
+#endif
+
static void reset_page(struct page *page)
{
__ClearPageMovable(page);
@@ -946,22 +981,36 @@ unlock:
}
#ifdef CONFIG_ZPOOL
+static unsigned long find_deferred_handle_obj(struct size_class *class,
+ struct page *page, int *obj_idx);
+
/*
* Free all the deferred handles whose objects are freed in zs_free.
*/
-static void free_handles(struct zs_pool *pool, struct zspage *zspage)
+static void free_handles(struct zs_pool *pool, struct size_class *class,
+ struct zspage *zspage)
{
- unsigned long handle = (unsigned long)zspage->deferred_handles;
+ int obj_idx = 0;
+ struct page *page = get_first_page(zspage);
+ unsigned long handle;
- while (handle) {
- unsigned long nxt_handle = handle_to_obj(handle);
+ while (1) {
+ handle = find_deferred_handle_obj(class, page, &obj_idx);
+ if (!handle) {
+ page = get_next_page(page);
+ if (!page)
+ break;
+ obj_idx = 0;
+ continue;
+ }
cache_free_handle(pool, handle);
- handle = nxt_handle;
+ obj_idx++;
}
}
#else
-static inline void free_handles(struct zs_pool *pool, struct zspage *zspage) {}
+static inline void free_handles(struct zs_pool *pool, struct size_class *class,
+ struct zspage *zspage) {}
#endif
static void __free_zspage(struct zs_pool *pool, struct size_class *class,
@@ -979,7 +1028,7 @@ static void __free_zspage(struct zs_pool *pool, struct size_class *class,
VM_BUG_ON(fg != ZS_EMPTY);
/* Free all deferred handles from zs_free */
- free_handles(pool, zspage);
+ free_handles(pool, class, zspage);
next = page = get_first_page(zspage);
do {
@@ -1067,7 +1116,6 @@ static void init_zspage(struct size_class *class, struct zspage *zspage)
#ifdef CONFIG_ZPOOL
INIT_LIST_HEAD(&zspage->lru);
zspage->under_reclaim = false;
- zspage->deferred_handles = NULL;
#endif
set_freeobj(zspage, 0);
@@ -1568,7 +1616,7 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size, gfp_t gfp)
}
EXPORT_SYMBOL_GPL(zs_malloc);
-static void obj_free(int class_size, unsigned long obj)
+static void obj_free(int class_size, unsigned long obj, unsigned long *handle)
{
struct link_free *link;
struct zspage *zspage;
@@ -1582,15 +1630,29 @@ static void obj_free(int class_size, unsigned long obj)
zspage = get_zspage(f_page);
vaddr = kmap_atomic(f_page);
-
- /* Insert this object in containing zspage's freelist */
link = (struct link_free *)(vaddr + f_offset);
- if (likely(!ZsHugePage(zspage)))
- link->next = get_freeobj(zspage) << OBJ_TAG_BITS;
- else
- f_page->index = 0;
+
+ if (handle) {
+#ifdef CONFIG_ZPOOL
+ /* Stores the (deferred) handle in the object's header */
+ *handle |= OBJ_DEFERRED_HANDLE_TAG;
+ *handle &= ~OBJ_ALLOCATED_TAG;
+
+ if (likely(!ZsHugePage(zspage)))
+ link->deferred_handle = *handle;
+ else
+ f_page->index = *handle;
+#endif
+ } else {
+ /* Insert this object in containing zspage's freelist */
+ if (likely(!ZsHugePage(zspage)))
+ link->next = get_freeobj(zspage) << OBJ_TAG_BITS;
+ else
+ f_page->index = 0;
+ set_freeobj(zspage, f_objidx);
+ }
+
kunmap_atomic(vaddr);
- set_freeobj(zspage, f_objidx);
mod_zspage_inuse(zspage, -1);
}
@@ -1615,7 +1677,6 @@ void zs_free(struct zs_pool *pool, unsigned long handle)
zspage = get_zspage(f_page);
class = zspage_class(pool, zspage);
- obj_free(class->size, obj);
class_stat_dec(class, OBJ_USED, 1);
#ifdef CONFIG_ZPOOL
@@ -1624,15 +1685,15 @@ void zs_free(struct zs_pool *pool, unsigned long handle)
* Reclaim needs the handles during writeback. It'll free
* them along with the zspage when it's done with them.
*
- * Record current deferred handle at the memory location
- * whose address is given by handle.
+ * Record current deferred handle in the object's header.
*/
- record_obj(handle, (unsigned long)zspage->deferred_handles);
- zspage->deferred_handles = (unsigned long *)handle;
+ obj_free(class->size, obj, &handle);
spin_unlock(&pool->lock);
return;
}
#endif
+ obj_free(class->size, obj, NULL);
+
fullness = fix_fullness_group(class, zspage);
if (fullness == ZS_EMPTY)
free_zspage(pool, class, zspage);
@@ -1713,11 +1774,11 @@ static void zs_object_copy(struct size_class *class, unsigned long dst,
}
/*
- * Find alloced object in zspage from index object and
+ * Find object with a certain tag in zspage from index object and
* return handle.
*/
-static unsigned long find_alloced_obj(struct size_class *class,
- struct page *page, int *obj_idx)
+static unsigned long find_tagged_obj(struct size_class *class,
+ struct page *page, int *obj_idx, int tag)
{
unsigned int offset;
int index = *obj_idx;
@@ -1728,7 +1789,7 @@ static unsigned long find_alloced_obj(struct size_class *class,
offset += class->size * index;
while (offset < PAGE_SIZE) {
- if (obj_allocated(page, addr + offset, &handle))
+ if (obj_tagged(page, addr + offset, &handle, tag))
break;
offset += class->size;
@@ -1742,6 +1803,28 @@ static unsigned long find_alloced_obj(struct size_class *class,
return handle;
}
+/*
+ * Find alloced object in zspage from index object and
+ * return handle.
+ */
+static unsigned long find_alloced_obj(struct size_class *class,
+ struct page *page, int *obj_idx)
+{
+ return find_tagged_obj(class, page, obj_idx, OBJ_ALLOCATED_TAG);
+}
+
+#ifdef CONFIG_ZPOOL
+/*
+ * Find object storing a deferred handle in header in zspage from index object
+ * and return handle.
+ */
+static unsigned long find_deferred_handle_obj(struct size_class *class,
+ struct page *page, int *obj_idx)
+{
+ return find_tagged_obj(class, page, obj_idx, OBJ_DEFERRED_HANDLE_TAG);
+}
+#endif
+
struct zs_compact_control {
/* Source spage for migration which could be a subpage of zspage */
struct page *s_page;
@@ -1784,7 +1867,7 @@ static int migrate_zspage(struct zs_pool *pool, struct size_class *class,
zs_object_copy(class, free_obj, used_obj);
obj_idx++;
record_obj(handle, free_obj);
- obj_free(class->size, used_obj);
+ obj_free(class->size, used_obj, NULL);
}
/* Remember last position in this iteration */
@@ -2478,6 +2561,90 @@ void zs_destroy_pool(struct zs_pool *pool)
EXPORT_SYMBOL_GPL(zs_destroy_pool);
#ifdef CONFIG_ZPOOL
+static void restore_freelist(struct zs_pool *pool, struct size_class *class,
+ struct zspage *zspage)
+{
+ unsigned int obj_idx = 0;
+ unsigned long handle, off = 0; /* off is within-page offset */
+ struct page *page = get_first_page(zspage);
+ struct link_free *prev_free = NULL;
+ void *prev_page_vaddr = NULL;
+
+ /* in case no free object found */
+ set_freeobj(zspage, (unsigned int)(-1UL));
+
+ while (page) {
+ void *vaddr = kmap_atomic(page);
+ struct page *next_page;
+
+ while (off < PAGE_SIZE) {
+ void *obj_addr = vaddr + off;
+
+ /* skip allocated object */
+ if (obj_allocated(page, obj_addr, &handle)) {
+ obj_idx++;
+ off += class->size;
+ continue;
+ }
+
+ /* free deferred handle from reclaim attempt */
+ if (obj_stores_deferred_handle(page, obj_addr, &handle))
+ cache_free_handle(pool, handle);
+
+ if (prev_free)
+ prev_free->next = obj_idx << OBJ_TAG_BITS;
+ else /* first free object found */
+ set_freeobj(zspage, obj_idx);
+
+ prev_free = (struct link_free *)vaddr + off / sizeof(*prev_free);
+ /* if last free object in a previous page, need to unmap */
+ if (prev_page_vaddr) {
+ kunmap_atomic(prev_page_vaddr);
+ prev_page_vaddr = NULL;
+ }
+
+ obj_idx++;
+ off += class->size;
+ }
+
+ /*
+ * Handle the last (full or partial) object on this page.
+ */
+ next_page = get_next_page(page);
+ if (next_page) {
+ if (!prev_free || prev_page_vaddr) {
+ /*
+ * There is no free object in this page, so we can safely
+ * unmap it.
+ */
+ kunmap_atomic(vaddr);
+ } else {
+ /* update prev_page_vaddr since prev_free is on this page */
+ prev_page_vaddr = vaddr;
+ }
+ } else { /* this is the last page */
+ if (prev_free) {
+ /*
+ * Reset OBJ_TAG_BITS bit to last link to tell
+ * whether it's allocated object or not.
+ */
+ prev_free->next = -1UL << OBJ_TAG_BITS;
+ }
+
+ /* unmap previous page (if not done yet) */
+ if (prev_page_vaddr) {
+ kunmap_atomic(prev_page_vaddr);
+ prev_page_vaddr = NULL;
+ }
+
+ kunmap_atomic(vaddr);
+ }
+
+ page = next_page;
+ off %= PAGE_SIZE;
+ }
+}
+
static int zs_reclaim_page(struct zs_pool *pool, unsigned int retries)
{
int i, obj_idx, ret = 0;
@@ -2561,6 +2728,12 @@ next:
return 0;
}
+ /*
+ * Eviction fails on one of the handles, so we need to restore zspage.
+ * We need to rebuild its freelist (and free stored deferred handles),
+ * put it back to the correct size class, and add it to the LRU list.
+ */
+ restore_freelist(pool, class, zspage);
putback_zspage(class, zspage);
list_add(&zspage->lru, &pool->lru);
unlock_zspage(zspage);
diff --git a/net/9p/trans_xen.c b/net/9p/trans_xen.c
index 9630b127555797..82c7005ede6567 100644
--- a/net/9p/trans_xen.c
+++ b/net/9p/trans_xen.c
@@ -305,13 +305,12 @@ static void xen_9pfs_front_free(struct xen_9pfs_front_priv *priv)
kfree(priv);
}
-static int xen_9pfs_front_remove(struct xenbus_device *dev)
+static void xen_9pfs_front_remove(struct xenbus_device *dev)
{
struct xen_9pfs_front_priv *priv = dev_get_drvdata(&dev->dev);
dev_set_drvdata(&dev->dev, NULL);
xen_9pfs_front_free(priv);
- return 0;
}
static int xen_9pfs_front_alloc_dataring(struct xenbus_device *dev,
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index d3e542c2fc3e49..acf563fbdfd951 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -821,6 +821,7 @@ static void terminate_big_destroy(struct hci_dev *hdev, void *data, int err)
static int hci_le_terminate_big(struct hci_dev *hdev, u8 big, u8 bis)
{
struct iso_list_data *d;
+ int ret;
bt_dev_dbg(hdev, "big 0x%2.2x bis 0x%2.2x", big, bis);
@@ -831,8 +832,12 @@ static int hci_le_terminate_big(struct hci_dev *hdev, u8 big, u8 bis)
d->big = big;
d->bis = bis;
- return hci_cmd_sync_queue(hdev, terminate_big_sync, d,
- terminate_big_destroy);
+ ret = hci_cmd_sync_queue(hdev, terminate_big_sync, d,
+ terminate_big_destroy);
+ if (ret)
+ kfree(d);
+
+ return ret;
}
static int big_terminate_sync(struct hci_dev *hdev, void *data)
@@ -857,6 +862,7 @@ static int big_terminate_sync(struct hci_dev *hdev, void *data)
static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, u16 sync_handle)
{
struct iso_list_data *d;
+ int ret;
bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", big, sync_handle);
@@ -867,8 +873,12 @@ static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, u16 sync_handle)
d->big = big;
d->sync_handle = sync_handle;
- return hci_cmd_sync_queue(hdev, big_terminate_sync, d,
- terminate_big_destroy);
+ ret = hci_cmd_sync_queue(hdev, big_terminate_sync, d,
+ terminate_big_destroy);
+ if (ret)
+ kfree(d);
+
+ return ret;
}
/* Cleanup BIS connection
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 0594af4e37cad9..ad92a4be585173 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -3848,8 +3848,11 @@ static u8 hci_cc_le_set_cig_params(struct hci_dev *hdev, void *data,
conn->handle, conn->link);
/* Create CIS if LE is already connected */
- if (conn->link && conn->link->state == BT_CONNECTED)
+ if (conn->link && conn->link->state == BT_CONNECTED) {
+ rcu_read_unlock();
hci_le_create_cis(conn->link);
+ rcu_read_lock();
+ }
if (i == rp->num_handles)
break;
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index 9e2d7e4b850c54..117eedb6f70993 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -3572,7 +3572,7 @@ static const struct hci_init_stage hci_init2[] = {
static int hci_le_read_buffer_size_sync(struct hci_dev *hdev)
{
/* Use Read LE Buffer Size V2 if supported */
- if (hdev->commands[41] & 0x20)
+ if (iso_capable(hdev) && hdev->commands[41] & 0x20)
return __hci_cmd_sync_status(hdev,
HCI_OP_LE_READ_BUFFER_SIZE_V2,
0, NULL, HCI_CMD_TIMEOUT);
@@ -3597,10 +3597,10 @@ static int hci_le_read_supported_states_sync(struct hci_dev *hdev)
/* LE Controller init stage 2 command sequence */
static const struct hci_init_stage le_init2[] = {
- /* HCI_OP_LE_READ_BUFFER_SIZE */
- HCI_INIT(hci_le_read_buffer_size_sync),
/* HCI_OP_LE_READ_LOCAL_FEATURES */
HCI_INIT(hci_le_read_local_features_sync),
+ /* HCI_OP_LE_READ_BUFFER_SIZE */
+ HCI_INIT(hci_le_read_buffer_size_sync),
/* HCI_OP_LE_READ_SUPPORTED_STATES */
HCI_INIT(hci_le_read_supported_states_sync),
{}
@@ -6187,20 +6187,13 @@ int hci_get_random_address(struct hci_dev *hdev, bool require_privacy,
static int _update_adv_data_sync(struct hci_dev *hdev, void *data)
{
- u8 instance = *(u8 *)data;
-
- kfree(data);
+ u8 instance = PTR_ERR(data);
return hci_update_adv_data_sync(hdev, instance);
}
int hci_update_adv_data(struct hci_dev *hdev, u8 instance)
{
- u8 *inst_ptr = kmalloc(1, GFP_KERNEL);
-
- if (!inst_ptr)
- return -ENOMEM;
-
- *inst_ptr = instance;
- return hci_cmd_sync_queue(hdev, _update_adv_data_sync, inst_ptr, NULL);
+ return hci_cmd_sync_queue(hdev, _update_adv_data_sync,
+ ERR_PTR(instance), NULL);
}
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index 035bb5d25f850a..24444b502e5865 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -289,15 +289,15 @@ static int iso_connect_bis(struct sock *sk)
hci_dev_unlock(hdev);
hci_dev_put(hdev);
+ err = iso_chan_add(conn, sk, NULL);
+ if (err)
+ return err;
+
lock_sock(sk);
/* Update source addr of the socket */
bacpy(&iso_pi(sk)->src, &hcon->src);
- err = iso_chan_add(conn, sk, NULL);
- if (err)
- goto release;
-
if (hcon->state == BT_CONNECTED) {
iso_sock_clear_timer(sk);
sk->sk_state = BT_CONNECTED;
@@ -306,7 +306,6 @@ static int iso_connect_bis(struct sock *sk)
iso_sock_set_timer(sk, sk->sk_sndtimeo);
}
-release:
release_sock(sk);
return err;
@@ -372,15 +371,15 @@ static int iso_connect_cis(struct sock *sk)
hci_dev_unlock(hdev);
hci_dev_put(hdev);
+ err = iso_chan_add(conn, sk, NULL);
+ if (err)
+ return err;
+
lock_sock(sk);
/* Update source addr of the socket */
bacpy(&iso_pi(sk)->src, &hcon->src);
- err = iso_chan_add(conn, sk, NULL);
- if (err)
- goto release;
-
if (hcon->state == BT_CONNECTED) {
iso_sock_clear_timer(sk);
sk->sk_state = BT_CONNECTED;
@@ -392,7 +391,6 @@ static int iso_connect_cis(struct sock *sk)
iso_sock_set_timer(sk, sk->sk_sndtimeo);
}
-release:
release_sock(sk);
return err;
@@ -895,13 +893,10 @@ static int iso_listen_bis(struct sock *sk)
if (!hdev)
return -EHOSTUNREACH;
- hci_dev_lock(hdev);
-
err = hci_pa_create_sync(hdev, &iso_pi(sk)->dst,
le_addr_type(iso_pi(sk)->dst_type),
iso_pi(sk)->bc_sid);
- hci_dev_unlock(hdev);
hci_dev_put(hdev);
return err;
@@ -1432,33 +1427,29 @@ static void iso_conn_ready(struct iso_conn *conn)
struct sock *parent;
struct sock *sk = conn->sk;
struct hci_ev_le_big_sync_estabilished *ev;
+ struct hci_conn *hcon;
BT_DBG("conn %p", conn);
if (sk) {
iso_sock_ready(conn->sk);
} else {
- iso_conn_lock(conn);
-
- if (!conn->hcon) {
- iso_conn_unlock(conn);
+ hcon = conn->hcon;
+ if (!hcon)
return;
- }
- ev = hci_recv_event_data(conn->hcon->hdev,
+ ev = hci_recv_event_data(hcon->hdev,
HCI_EVT_LE_BIG_SYNC_ESTABILISHED);
if (ev)
- parent = iso_get_sock_listen(&conn->hcon->src,
- &conn->hcon->dst,
+ parent = iso_get_sock_listen(&hcon->src,
+ &hcon->dst,
iso_match_big, ev);
else
- parent = iso_get_sock_listen(&conn->hcon->src,
+ parent = iso_get_sock_listen(&hcon->src,
BDADDR_ANY, NULL, NULL);
- if (!parent) {
- iso_conn_unlock(conn);
+ if (!parent)
return;
- }
lock_sock(parent);
@@ -1466,30 +1457,29 @@ static void iso_conn_ready(struct iso_conn *conn)
BTPROTO_ISO, GFP_ATOMIC, 0);
if (!sk) {
release_sock(parent);
- iso_conn_unlock(conn);
return;
}
iso_sock_init(sk, parent);
- bacpy(&iso_pi(sk)->src, &conn->hcon->src);
- iso_pi(sk)->src_type = conn->hcon->src_type;
+ bacpy(&iso_pi(sk)->src, &hcon->src);
+ iso_pi(sk)->src_type = hcon->src_type;
/* If hcon has no destination address (BDADDR_ANY) it means it
* was created by HCI_EV_LE_BIG_SYNC_ESTABILISHED so we need to
* initialize using the parent socket destination address.
*/
- if (!bacmp(&conn->hcon->dst, BDADDR_ANY)) {
- bacpy(&conn->hcon->dst, &iso_pi(parent)->dst);
- conn->hcon->dst_type = iso_pi(parent)->dst_type;
- conn->hcon->sync_handle = iso_pi(parent)->sync_handle;
+ if (!bacmp(&hcon->dst, BDADDR_ANY)) {
+ bacpy(&hcon->dst, &iso_pi(parent)->dst);
+ hcon->dst_type = iso_pi(parent)->dst_type;
+ hcon->sync_handle = iso_pi(parent)->sync_handle;
}
- bacpy(&iso_pi(sk)->dst, &conn->hcon->dst);
- iso_pi(sk)->dst_type = conn->hcon->dst_type;
+ bacpy(&iso_pi(sk)->dst, &hcon->dst);
+ iso_pi(sk)->dst_type = hcon->dst_type;
- hci_conn_hold(conn->hcon);
- __iso_chan_add(conn, sk, parent);
+ hci_conn_hold(hcon);
+ iso_chan_add(conn, sk, parent);
if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(parent)->flags))
sk->sk_state = BT_CONNECT2;
@@ -1500,8 +1490,6 @@ static void iso_conn_ready(struct iso_conn *conn)
parent->sk_data_ready(parent);
release_sock(parent);
-
- iso_conn_unlock(conn);
}
}
diff --git a/net/bluetooth/mgmt_util.h b/net/bluetooth/mgmt_util.h
index 6a8b7e84293dfd..bdf978605d5a85 100644
--- a/net/bluetooth/mgmt_util.h
+++ b/net/bluetooth/mgmt_util.h
@@ -27,7 +27,7 @@ struct mgmt_mesh_tx {
struct sock *sk;
u8 handle;
u8 instance;
- u8 param[sizeof(struct mgmt_cp_mesh_send) + 29];
+ u8 param[sizeof(struct mgmt_cp_mesh_send) + 31];
};
struct mgmt_pending_cmd {
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 21e24da4847f05..4397e14ff560fd 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -391,6 +391,7 @@ static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int a
addr->sa_family != AF_BLUETOOTH)
return -EINVAL;
+ sock_hold(sk);
lock_sock(sk);
if (sk->sk_state != BT_OPEN && sk->sk_state != BT_BOUND) {
@@ -410,14 +411,18 @@ static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int a
d->sec_level = rfcomm_pi(sk)->sec_level;
d->role_switch = rfcomm_pi(sk)->role_switch;
+ /* Drop sock lock to avoid potential deadlock with the RFCOMM lock */
+ release_sock(sk);
err = rfcomm_dlc_open(d, &rfcomm_pi(sk)->src, &sa->rc_bdaddr,
sa->rc_channel);
- if (!err)
+ lock_sock(sk);
+ if (!err && !sock_flag(sk, SOCK_ZAPPED))
err = bt_sock_wait_state(sk, BT_CONNECTED,
sock_sndtimeo(sk, flags & O_NONBLOCK));
done:
release_sock(sk);
+ sock_put(sk);
return err;
}
diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c
index f20f4373ff408f..9554abcfd5b4ed 100644
--- a/net/bridge/br_netfilter_hooks.c
+++ b/net/bridge/br_netfilter_hooks.c
@@ -871,6 +871,7 @@ static unsigned int ip_sabotage_in(void *priv,
if (nf_bridge && !nf_bridge->in_prerouting &&
!netif_is_l3_master(skb->dev) &&
!netif_is_l3_slave(skb->dev)) {
+ nf_bridge_info_free(skb);
state->okfn(state->net, state->sk, skb);
return NF_STOLEN;
}
diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c
index cc405d8c7c303f..8480684f276251 100644
--- a/net/caif/cfctrl.c
+++ b/net/caif/cfctrl.c
@@ -269,11 +269,15 @@ int cfctrl_linkup_request(struct cflayer *layer,
default:
pr_warn("Request setup of bad link type = %d\n",
param->linktype);
+ cfpkt_destroy(pkt);
return -EINVAL;
}
req = kzalloc(sizeof(*req), GFP_KERNEL);
- if (!req)
+ if (!req) {
+ cfpkt_destroy(pkt);
return -ENOMEM;
+ }
+
req->client_layer = user_layer;
req->cmd = CFCTRL_CMD_LINK_SETUP;
req->param = *param;
diff --git a/net/can/isotp.c b/net/can/isotp.c
index 608f8c24ae46b6..fc81d77724a131 100644
--- a/net/can/isotp.c
+++ b/net/can/isotp.c
@@ -140,7 +140,7 @@ struct isotp_sock {
canid_t rxid;
ktime_t tx_gap;
ktime_t lastrxcf_tstamp;
- struct hrtimer rxtimer, txtimer;
+ struct hrtimer rxtimer, txtimer, txfrtimer;
struct can_isotp_options opt;
struct can_isotp_fc_options rxfc, txfc;
struct can_isotp_ll_options ll;
@@ -871,7 +871,7 @@ static void isotp_rcv_echo(struct sk_buff *skb, void *data)
}
/* start timer to send next consecutive frame with correct delay */
- hrtimer_start(&so->txtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT);
+ hrtimer_start(&so->txfrtimer, so->tx_gap, HRTIMER_MODE_REL_SOFT);
}
static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
@@ -879,49 +879,39 @@ static enum hrtimer_restart isotp_tx_timer_handler(struct hrtimer *hrtimer)
struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
txtimer);
struct sock *sk = &so->sk;
- enum hrtimer_restart restart = HRTIMER_NORESTART;
- switch (so->tx.state) {
- case ISOTP_SENDING:
-
- /* cfecho should be consumed by isotp_rcv_echo() here */
- if (!so->cfecho) {
- /* start timeout for unlikely lost echo skb */
- hrtimer_set_expires(&so->txtimer,
- ktime_add(ktime_get(),
- ktime_set(ISOTP_ECHO_TIMEOUT, 0)));
- restart = HRTIMER_RESTART;
+ /* don't handle timeouts in IDLE state */
+ if (so->tx.state == ISOTP_IDLE)
+ return HRTIMER_NORESTART;
- /* push out the next consecutive frame */
- isotp_send_cframe(so);
- break;
- }
+ /* we did not get any flow control or echo frame in time */
- /* cfecho has not been cleared in isotp_rcv_echo() */
- pr_notice_once("can-isotp: cfecho %08X timeout\n", so->cfecho);
- fallthrough;
+ /* report 'communication error on send' */
+ sk->sk_err = ECOMM;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk_error_report(sk);
- case ISOTP_WAIT_FC:
- case ISOTP_WAIT_FIRST_FC:
+ /* reset tx state */
+ so->tx.state = ISOTP_IDLE;
+ wake_up_interruptible(&so->wait);
- /* we did not get any flow control frame in time */
+ return HRTIMER_NORESTART;
+}
- /* report 'communication error on send' */
- sk->sk_err = ECOMM;
- if (!sock_flag(sk, SOCK_DEAD))
- sk_error_report(sk);
+static enum hrtimer_restart isotp_txfr_timer_handler(struct hrtimer *hrtimer)
+{
+ struct isotp_sock *so = container_of(hrtimer, struct isotp_sock,
+ txfrtimer);
- /* reset tx state */
- so->tx.state = ISOTP_IDLE;
- wake_up_interruptible(&so->wait);
- break;
+ /* start echo timeout handling and cover below protocol error */
+ hrtimer_start(&so->txtimer, ktime_set(ISOTP_ECHO_TIMEOUT, 0),
+ HRTIMER_MODE_REL_SOFT);
- default:
- WARN_ONCE(1, "can-isotp: tx timer state %08X cfecho %08X\n",
- so->tx.state, so->cfecho);
- }
+ /* cfecho should be consumed by isotp_rcv_echo() here */
+ if (so->tx.state == ISOTP_SENDING && !so->cfecho)
+ isotp_send_cframe(so);
- return restart;
+ return HRTIMER_NORESTART;
}
static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
@@ -1162,6 +1152,10 @@ static int isotp_release(struct socket *sock)
/* wait for complete transmission of current pdu */
wait_event_interruptible(so->wait, so->tx.state == ISOTP_IDLE);
+ /* force state machines to be idle also when a signal occurred */
+ so->tx.state = ISOTP_IDLE;
+ so->rx.state = ISOTP_IDLE;
+
spin_lock(&isotp_notifier_lock);
while (isotp_busy_notifier == so) {
spin_unlock(&isotp_notifier_lock);
@@ -1194,6 +1188,7 @@ static int isotp_release(struct socket *sock)
}
}
+ hrtimer_cancel(&so->txfrtimer);
hrtimer_cancel(&so->txtimer);
hrtimer_cancel(&so->rxtimer);
@@ -1597,6 +1592,8 @@ static int isotp_init(struct sock *sk)
so->rxtimer.function = isotp_rx_timer_handler;
hrtimer_init(&so->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
so->txtimer.function = isotp_tx_timer_handler;
+ hrtimer_init(&so->txfrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+ so->txfrtimer.function = isotp_txfr_timer_handler;
init_waitqueue_head(&so->wait);
spin_lock_init(&so->rx_lock);
diff --git a/net/can/j1939/address-claim.c b/net/can/j1939/address-claim.c
index f33c4732792787..ca4ad6cdd5cbfd 100644
--- a/net/can/j1939/address-claim.c
+++ b/net/can/j1939/address-claim.c
@@ -165,6 +165,46 @@ static void j1939_ac_process(struct j1939_priv *priv, struct sk_buff *skb)
* leaving this function.
*/
ecu = j1939_ecu_get_by_name_locked(priv, name);
+
+ if (ecu && ecu->addr == skcb->addr.sa) {
+ /* The ISO 11783-5 standard, in "4.5.2 - Address claim
+ * requirements", states:
+ * d) No CF shall begin, or resume, transmission on the
+ * network until 250 ms after it has successfully claimed
+ * an address except when responding to a request for
+ * address-claimed.
+ *
+ * But "Figure 6" and "Figure 7" in "4.5.4.2 - Address-claim
+ * prioritization" show that the CF begins the transmission
+ * after 250 ms from the first AC (address-claimed) message
+ * even if it sends another AC message during that time window
+ * to resolve the address contention with another CF.
+ *
+ * As stated in "4.4.2.3 - Address-claimed message":
+ * In order to successfully claim an address, the CF sending
+ * an address claimed message shall not receive a contending
+ * claim from another CF for at least 250 ms.
+ *
+ * As stated in "4.4.3.2 - NAME management (NM) message":
+ * 1) A commanding CF can
+ * d) request that a CF with a specified NAME transmit
+ * the address-claimed message with its current NAME.
+ * 2) A target CF shall
+ * d) send an address-claimed message in response to a
+ * request for a matching NAME
+ *
+ * Taking the above arguments into account, the 250 ms wait is
+ * requested only during network initialization.
+ *
+ * Do not restart the timer on AC message if both the NAME and
+ * the address match and so if the address has already been
+ * claimed (timer has expired) or the AC message has been sent
+ * to resolve the contention with another CF (timer is still
+ * running).
+ */
+ goto out_ecu_put;
+ }
+
if (!ecu && j1939_address_is_unicast(skcb->addr.sa))
ecu = j1939_ecu_create_locked(priv, name);
diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c
index 5c722b55fe237f..fce9b9ebf13f66 100644
--- a/net/can/j1939/transport.c
+++ b/net/can/j1939/transport.c
@@ -1092,10 +1092,6 @@ static bool j1939_session_deactivate(struct j1939_session *session)
bool active;
j1939_session_list_lock(priv);
- /* This function should be called with a session ref-count of at
- * least 2.
- */
- WARN_ON_ONCE(kref_read(&session->kref) < 2);
active = j1939_session_deactivate_locked(session);
j1939_session_list_unlock(priv);
diff --git a/net/can/raw.c b/net/can/raw.c
index 81071cdb030101..ba86782ba8bb3c 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -132,8 +132,8 @@ static void raw_rcv(struct sk_buff *oskb, void *data)
return;
/* make sure to not pass oversized frames to the socket */
- if ((can_is_canfd_skb(oskb) && !ro->fd_frames && !ro->xl_frames) ||
- (can_is_canxl_skb(oskb) && !ro->xl_frames))
+ if ((!ro->fd_frames && can_is_canfd_skb(oskb)) ||
+ (!ro->xl_frames && can_is_canxl_skb(oskb)))
return;
/* eliminate multiple filter matches for the same skb */
@@ -670,6 +670,11 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
if (copy_from_sockptr(&ro->fd_frames, optval, optlen))
return -EFAULT;
+ /* Enabling CAN XL includes CAN FD */
+ if (ro->xl_frames && !ro->fd_frames) {
+ ro->fd_frames = ro->xl_frames;
+ return -EINVAL;
+ }
break;
case CAN_RAW_XL_FRAMES:
@@ -679,6 +684,9 @@ static int raw_setsockopt(struct socket *sock, int level, int optname,
if (copy_from_sockptr(&ro->xl_frames, optval, optlen))
return -EFAULT;
+ /* Enabling CAN XL includes CAN FD */
+ if (ro->xl_frames)
+ ro->fd_frames = ro->xl_frames;
break;
case CAN_RAW_JOIN_FILTERS:
@@ -786,6 +794,25 @@ static int raw_getsockopt(struct socket *sock, int level, int optname,
return 0;
}
+static bool raw_bad_txframe(struct raw_sock *ro, struct sk_buff *skb, int mtu)
+{
+ /* Classical CAN -> no checks for flags and device capabilities */
+ if (can_is_can_skb(skb))
+ return false;
+
+ /* CAN FD -> needs to be enabled and a CAN FD or CAN XL device */
+ if (ro->fd_frames && can_is_canfd_skb(skb) &&
+ (mtu == CANFD_MTU || can_is_canxl_dev_mtu(mtu)))
+ return false;
+
+ /* CAN XL -> needs to be enabled and a CAN XL device */
+ if (ro->xl_frames && can_is_canxl_skb(skb) &&
+ can_is_canxl_dev_mtu(mtu))
+ return false;
+
+ return true;
+}
+
static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{
struct sock *sk = sock->sk;
@@ -833,20 +860,8 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
goto free_skb;
err = -EINVAL;
- if (ro->xl_frames && can_is_canxl_dev_mtu(dev->mtu)) {
- /* CAN XL, CAN FD and Classical CAN */
- if (!can_is_canxl_skb(skb) && !can_is_canfd_skb(skb) &&
- !can_is_can_skb(skb))
- goto free_skb;
- } else if (ro->fd_frames && dev->mtu == CANFD_MTU) {
- /* CAN FD and Classical CAN */
- if (!can_is_canfd_skb(skb) && !can_is_can_skb(skb))
- goto free_skb;
- } else {
- /* Classical CAN */
- if (!can_is_can_skb(skb))
- goto free_skb;
- }
+ if (raw_bad_txframe(ro, skb, dev->mtu))
+ goto free_skb;
sockcm_init(&sockc, sk);
if (msg->msg_controllen) {
diff --git a/net/core/devlink.c b/net/core/devlink.c
index 032d6d0a5ce64a..909a10e4b0ddfe 100644
--- a/net/core/devlink.c
+++ b/net/core/devlink.c
@@ -9979,7 +9979,7 @@ struct devlink *devlink_alloc_ns(const struct devlink_ops *ops,
goto err_xa_alloc;
devlink->netdevice_nb.notifier_call = devlink_netdevice_event;
- ret = register_netdevice_notifier_net(net, &devlink->netdevice_nb);
+ ret = register_netdevice_notifier(&devlink->netdevice_nb);
if (ret)
goto err_register_netdevice_notifier;
@@ -10171,8 +10171,7 @@ void devlink_free(struct devlink *devlink)
xa_destroy(&devlink->snapshot_ids);
xa_destroy(&devlink->ports);
- WARN_ON_ONCE(unregister_netdevice_notifier_net(devlink_net(devlink),
- &devlink->netdevice_nb));
+ WARN_ON_ONCE(unregister_netdevice_notifier(&devlink->netdevice_nb));
xa_erase(&devlinks, devlink->index);
@@ -10503,6 +10502,8 @@ static int devlink_netdevice_event(struct notifier_block *nb,
break;
case NETDEV_REGISTER:
case NETDEV_CHANGENAME:
+ if (devlink_net(devlink) != dev_net(netdev))
+ return NOTIFY_OK;
/* Set the netdev on top of previously set type. Note this
* event happens also during net namespace change so here
* we take into account netdev pointer appearing in this
@@ -10512,6 +10513,8 @@ static int devlink_netdevice_event(struct notifier_block *nb,
netdev);
break;
case NETDEV_UNREGISTER:
+ if (devlink_net(devlink) != dev_net(netdev))
+ return NOTIFY_OK;
/* Clear netdev pointer, but not the type. This event happens
* also during net namespace change so we need to clear
* pointer to netdev that is going to another net namespace.
diff --git a/net/core/filter.c b/net/core/filter.c
index 929358677183d5..43cc1fe58a2c6e 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -3180,15 +3180,18 @@ static int bpf_skb_generic_push(struct sk_buff *skb, u32 off, u32 len)
static int bpf_skb_generic_pop(struct sk_buff *skb, u32 off, u32 len)
{
+ void *old_data;
+
/* skb_ensure_writable() is not needed here, as we're
* already working on an uncloned skb.
*/
if (unlikely(!pskb_may_pull(skb, off + len)))
return -ENOMEM;
- skb_postpull_rcsum(skb, skb->data + off, len);
- memmove(skb->data + len, skb->data, off);
+ old_data = skb->data;
__skb_pull(skb, len);
+ skb_postpull_rcsum(skb, old_data + off, len);
+ memmove(skb->data, old_data, off);
return 0;
}
diff --git a/net/core/gro.c b/net/core/gro.c
index fd8c6a7e8d3e2e..4bac7ea6e02565 100644
--- a/net/core/gro.c
+++ b/net/core/gro.c
@@ -162,6 +162,15 @@ int skb_gro_receive(struct sk_buff *p, struct sk_buff *skb)
struct sk_buff *lp;
int segs;
+ /* Do not splice page pool based packets w/ non-page pool
+ * packets. This can result in reference count issues as page
+ * pool pages will not decrement the reference count and will
+ * instead be immediately returned to the pool or have frag
+ * count decremented.
+ */
+ if (p->pp_recycle != skb->pp_recycle)
+ return -ETOOMANYREFS;
+
/* pairs with WRITE_ONCE() in netif_set_gro_max_size() */
gro_max_size = READ_ONCE(p->dev->gro_max_size);
@@ -505,8 +514,9 @@ found_ptype:
NAPI_GRO_CB(skb)->count = 1;
if (unlikely(skb_is_gso(skb))) {
NAPI_GRO_CB(skb)->count = skb_shinfo(skb)->gso_segs;
- /* Only support TCP at the moment. */
- if (!skb_is_gso_tcp(skb))
+ /* Only support TCP and non DODGY users. */
+ if (!skb_is_gso_tcp(skb) ||
+ (skb_shinfo(skb)->gso_type & SKB_GSO_DODGY))
NAPI_GRO_CB(skb)->flush = 1;
}
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index f00a79fc301b0e..4edd2176e23851 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -269,7 +269,7 @@ static int neigh_forced_gc(struct neigh_table *tbl)
(n->nud_state == NUD_NOARP) ||
(tbl->is_multicast &&
tbl->is_multicast(n->primary_key)) ||
- time_after(tref, n->updated))
+ !time_in_range(n->updated, tref, jiffies))
remove = true;
write_unlock(&n->lock);
@@ -289,7 +289,17 @@ static int neigh_forced_gc(struct neigh_table *tbl)
static void neigh_add_timer(struct neighbour *n, unsigned long when)
{
+ /* Use safe distance from the jiffies - LONG_MAX point while timer
+ * is running in DELAY/PROBE state but still show to user space
+ * large times in the past.
+ */
+ unsigned long mint = jiffies - (LONG_MAX - 86400 * HZ);
+
neigh_hold(n);
+ if (!time_in_range(n->confirmed, mint, jiffies))
+ n->confirmed = mint;
+ if (time_before(n->used, n->confirmed))
+ n->used = n->confirmed;
if (unlikely(mod_timer(&n->timer, when))) {
printk("NEIGH: BUG, double timer add, state is %x\n",
n->nud_state);
@@ -1001,12 +1011,14 @@ static void neigh_periodic_work(struct work_struct *work)
goto next_elt;
}
- if (time_before(n->used, n->confirmed))
+ if (time_before(n->used, n->confirmed) &&
+ time_is_before_eq_jiffies(n->confirmed))
n->used = n->confirmed;
if (refcount_read(&n->refcnt) == 1 &&
(state == NUD_FAILED ||
- time_after(jiffies, n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) {
+ !time_in_range_open(jiffies, n->used,
+ n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) {
*np = n->next;
neigh_mark_dead(n);
write_unlock(&n->lock);
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 5581d22cc19127..078a0a420c8a7a 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -137,12 +137,12 @@ static int ops_init(const struct pernet_operations *ops, struct net *net)
return 0;
if (ops->id && ops->size) {
-cleanup:
ng = rcu_dereference_protected(net->gen,
lockdep_is_held(&pernet_ops_rwsem));
ng->ptr[*ops->id] = NULL;
}
+cleanup:
kfree(data);
out:
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 4a0eb5593275c8..a31ff4d83ecc49 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4100,7 +4100,7 @@ struct sk_buff *skb_segment_list(struct sk_buff *skb,
skb_shinfo(skb)->frag_list = NULL;
- do {
+ while (list_skb) {
nskb = list_skb;
list_skb = list_skb->next;
@@ -4146,8 +4146,7 @@ struct sk_buff *skb_segment_list(struct sk_buff *skb,
if (skb_needs_linearize(nskb, features) &&
__skb_linearize(nskb))
goto err_linearize;
-
- } while (list_skb);
+ }
skb->truesize = skb->truesize - delta_truesize;
skb->data_len = skb->data_len - delta_len;
diff --git a/net/core/sock.c b/net/core/sock.c
index f954d5893e799a..6f27c24016fee7 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1531,6 +1531,8 @@ set_sndbuf:
ret = -EINVAL;
break;
}
+ if ((u8)val == SOCK_TXREHASH_DEFAULT)
+ val = READ_ONCE(sock_net(sk)->core.sysctl_txrehash);
/* Paired with READ_ONCE() in tcp_rtx_synack() */
WRITE_ONCE(sk->sk_txrehash, (u8)val);
break;
@@ -3451,7 +3453,6 @@ void sock_init_data(struct socket *sock, struct sock *sk)
sk->sk_pacing_rate = ~0UL;
WRITE_ONCE(sk->sk_pacing_shift, 10);
sk->sk_incoming_cpu = -1;
- sk->sk_txrehash = SOCK_TXREHASH_DEFAULT;
sk_rx_queue_clear(sk);
/*
diff --git a/net/core/sock_map.c b/net/core/sock_map.c
index 22fa2c5bc6ec96..a68a7290a3b2b1 100644
--- a/net/core/sock_map.c
+++ b/net/core/sock_map.c
@@ -1569,15 +1569,16 @@ void sock_map_unhash(struct sock *sk)
psock = sk_psock(sk);
if (unlikely(!psock)) {
rcu_read_unlock();
- if (sk->sk_prot->unhash)
- sk->sk_prot->unhash(sk);
- return;
+ saved_unhash = READ_ONCE(sk->sk_prot)->unhash;
+ } else {
+ saved_unhash = psock->saved_unhash;
+ sock_map_remove_links(sk, psock);
+ rcu_read_unlock();
}
-
- saved_unhash = psock->saved_unhash;
- sock_map_remove_links(sk, psock);
- rcu_read_unlock();
- saved_unhash(sk);
+ if (WARN_ON_ONCE(saved_unhash == sock_map_unhash))
+ return;
+ if (saved_unhash)
+ saved_unhash(sk);
}
EXPORT_SYMBOL_GPL(sock_map_unhash);
@@ -1590,17 +1591,18 @@ void sock_map_destroy(struct sock *sk)
psock = sk_psock_get(sk);
if (unlikely(!psock)) {
rcu_read_unlock();
- if (sk->sk_prot->destroy)
- sk->sk_prot->destroy(sk);
- return;
+ saved_destroy = READ_ONCE(sk->sk_prot)->destroy;
+ } else {
+ saved_destroy = psock->saved_destroy;
+ sock_map_remove_links(sk, psock);
+ rcu_read_unlock();
+ sk_psock_stop(psock);
+ sk_psock_put(sk, psock);
}
-
- saved_destroy = psock->saved_destroy;
- sock_map_remove_links(sk, psock);
- rcu_read_unlock();
- sk_psock_stop(psock);
- sk_psock_put(sk, psock);
- saved_destroy(sk);
+ if (WARN_ON_ONCE(saved_destroy == sock_map_destroy))
+ return;
+ if (saved_destroy)
+ saved_destroy(sk);
}
EXPORT_SYMBOL_GPL(sock_map_destroy);
@@ -1615,16 +1617,21 @@ void sock_map_close(struct sock *sk, long timeout)
if (unlikely(!psock)) {
rcu_read_unlock();
release_sock(sk);
- return sk->sk_prot->close(sk, timeout);
+ saved_close = READ_ONCE(sk->sk_prot)->close;
+ } else {
+ saved_close = psock->saved_close;
+ sock_map_remove_links(sk, psock);
+ rcu_read_unlock();
+ sk_psock_stop(psock);
+ release_sock(sk);
+ cancel_work_sync(&psock->work);
+ sk_psock_put(sk, psock);
}
-
- saved_close = psock->saved_close;
- sock_map_remove_links(sk, psock);
- rcu_read_unlock();
- sk_psock_stop(psock);
- release_sock(sk);
- cancel_work_sync(&psock->work);
- sk_psock_put(sk, psock);
+ /* Make sure we do not recurse. This is a bug.
+ * Leak the socket instead of crashing on a stack overflow.
+ */
+ if (WARN_ON_ONCE(saved_close == sock_map_close))
+ return;
saved_close(sk, timeout);
}
EXPORT_SYMBOL_GPL(sock_map_close);
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index c2f1a542e6fa9c..646b3e490c71a7 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -2078,58 +2078,91 @@ static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
return ret;
}
-static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
+static int ethtool_vzalloc_stats_array(int n_stats, u64 **data)
{
+ if (n_stats < 0)
+ return n_stats;
+ if (n_stats > S32_MAX / sizeof(u64))
+ return -ENOMEM;
+ if (WARN_ON_ONCE(!n_stats))
+ return -EOPNOTSUPP;
+
+ *data = vzalloc(array_size(n_stats, sizeof(u64)));
+ if (!*data)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int ethtool_get_phy_stats_phydev(struct phy_device *phydev,
+ struct ethtool_stats *stats,
+ u64 **data)
+ {
const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops;
+ int n_stats, ret;
+
+ if (!phy_ops || !phy_ops->get_sset_count || !phy_ops->get_stats)
+ return -EOPNOTSUPP;
+
+ n_stats = phy_ops->get_sset_count(phydev);
+
+ ret = ethtool_vzalloc_stats_array(n_stats, data);
+ if (ret)
+ return ret;
+
+ stats->n_stats = n_stats;
+ return phy_ops->get_stats(phydev, stats, *data);
+}
+
+static int ethtool_get_phy_stats_ethtool(struct net_device *dev,
+ struct ethtool_stats *stats,
+ u64 **data)
+{
const struct ethtool_ops *ops = dev->ethtool_ops;
- struct phy_device *phydev = dev->phydev;
- struct ethtool_stats stats;
- u64 *data;
- int ret, n_stats;
+ int n_stats, ret;
- if (!phydev && (!ops->get_ethtool_phy_stats || !ops->get_sset_count))
+ if (!ops || !ops->get_sset_count || ops->get_ethtool_phy_stats)
return -EOPNOTSUPP;
- if (phydev && !ops->get_ethtool_phy_stats &&
- phy_ops && phy_ops->get_sset_count)
- n_stats = phy_ops->get_sset_count(phydev);
- else
- n_stats = ops->get_sset_count(dev, ETH_SS_PHY_STATS);
- if (n_stats < 0)
- return n_stats;
- if (n_stats > S32_MAX / sizeof(u64))
- return -ENOMEM;
- WARN_ON_ONCE(!n_stats);
+ n_stats = ops->get_sset_count(dev, ETH_SS_PHY_STATS);
+
+ ret = ethtool_vzalloc_stats_array(n_stats, data);
+ if (ret)
+ return ret;
+
+ stats->n_stats = n_stats;
+ ops->get_ethtool_phy_stats(dev, stats, *data);
+
+ return 0;
+}
+
+static int ethtool_get_phy_stats(struct net_device *dev, void __user *useraddr)
+{
+ struct phy_device *phydev = dev->phydev;
+ struct ethtool_stats stats;
+ u64 *data = NULL;
+ int ret = -EOPNOTSUPP;
if (copy_from_user(&stats, useraddr, sizeof(stats)))
return -EFAULT;
- stats.n_stats = n_stats;
+ if (phydev)
+ ret = ethtool_get_phy_stats_phydev(phydev, &stats, &data);
- if (n_stats) {
- data = vzalloc(array_size(n_stats, sizeof(u64)));
- if (!data)
- return -ENOMEM;
+ if (ret == -EOPNOTSUPP)
+ ret = ethtool_get_phy_stats_ethtool(dev, &stats, &data);
- if (phydev && !ops->get_ethtool_phy_stats &&
- phy_ops && phy_ops->get_stats) {
- ret = phy_ops->get_stats(phydev, &stats, data);
- if (ret < 0)
- goto out;
- } else {
- ops->get_ethtool_phy_stats(dev, &stats, data);
- }
- } else {
- data = NULL;
- }
+ if (ret)
+ goto out;
- ret = -EFAULT;
- if (copy_to_user(useraddr, &stats, sizeof(stats)))
+ if (copy_to_user(useraddr, &stats, sizeof(stats))) {
+ ret = -EFAULT;
goto out;
+ }
+
useraddr += sizeof(stats);
- if (n_stats && copy_to_user(useraddr, data, array_size(n_stats, sizeof(u64))))
- goto out;
- ret = 0;
+ if (copy_to_user(useraddr, data, array_size(stats.n_stats, sizeof(u64))))
+ ret = -EFAULT;
out:
vfree(data);
diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c
index ebe6145aed3f1a..be260ab34e5801 100644
--- a/net/ethtool/rss.c
+++ b/net/ethtool/rss.c
@@ -122,10 +122,13 @@ rss_fill_reply(struct sk_buff *skb, const struct ethnl_req_info *req_base,
{
const struct rss_reply_data *data = RSS_REPDATA(reply_base);
- if (nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc) ||
- nla_put(skb, ETHTOOL_A_RSS_INDIR,
- sizeof(u32) * data->indir_size, data->indir_table) ||
- nla_put(skb, ETHTOOL_A_RSS_HKEY, data->hkey_size, data->hkey))
+ if ((data->hfunc &&
+ nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc)) ||
+ (data->indir_size &&
+ nla_put(skb, ETHTOOL_A_RSS_INDIR,
+ sizeof(u32) * data->indir_size, data->indir_table)) ||
+ (data->hkey_size &&
+ nla_put(skb, ETHTOOL_A_RSS_HKEY, data->hkey_size, data->hkey)))
return -EMSGSIZE;
return 0;
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index ab4a06be489b5d..cf11f10927e1af 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -347,6 +347,7 @@ lookup_protocol:
sk->sk_destruct = inet_sock_destruct;
sk->sk_protocol = protocol;
sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;
+ sk->sk_txrehash = READ_ONCE(net->core.sysctl_txrehash);
inet->uc_ttl = -1;
inet->mc_loop = 1;
@@ -1665,6 +1666,7 @@ int inet_ctl_sock_create(struct sock **sk, unsigned short family,
if (rc == 0) {
*sk = sock->sk;
(*sk)->sk_allocation = GFP_ATOMIC;
+ (*sk)->sk_use_task_frag = false;
/*
* Unhash it so that IP input processing does not even see it,
* we do not wish this socket to see incoming packets.
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index ce9ff3c62e8405..3bb890a40ed736 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -30,6 +30,7 @@
#include <linux/slab.h>
#include <linux/netlink.h>
#include <linux/hash.h>
+#include <linux/nospec.h>
#include <net/arp.h>
#include <net/inet_dscp.h>
@@ -1022,6 +1023,7 @@ bool fib_metrics_match(struct fib_config *cfg, struct fib_info *fi)
if (type > RTAX_MAX)
return false;
+ type = array_index_nospec(type, RTAX_MAX + 1);
if (type == RTAX_CC_ALGO) {
char tmp[TCP_CA_NAME_MAX];
bool ecn_ca = false;
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index b366ab9148f2d6..f2c43f67187db7 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -173,22 +173,40 @@ static bool inet_bind_conflict(const struct sock *sk, struct sock *sk2,
return false;
}
+static bool __inet_bhash2_conflict(const struct sock *sk, struct sock *sk2,
+ kuid_t sk_uid, bool relax,
+ bool reuseport_cb_ok, bool reuseport_ok)
+{
+ if (sk->sk_family == AF_INET && ipv6_only_sock(sk2))
+ return false;
+
+ return inet_bind_conflict(sk, sk2, sk_uid, relax,
+ reuseport_cb_ok, reuseport_ok);
+}
+
static bool inet_bhash2_conflict(const struct sock *sk,
const struct inet_bind2_bucket *tb2,
kuid_t sk_uid,
bool relax, bool reuseport_cb_ok,
bool reuseport_ok)
{
+ struct inet_timewait_sock *tw2;
struct sock *sk2;
sk_for_each_bound_bhash2(sk2, &tb2->owners) {
- if (sk->sk_family == AF_INET && ipv6_only_sock(sk2))
- continue;
+ if (__inet_bhash2_conflict(sk, sk2, sk_uid, relax,
+ reuseport_cb_ok, reuseport_ok))
+ return true;
+ }
+
+ twsk_for_each_bound_bhash2(tw2, &tb2->deathrow) {
+ sk2 = (struct sock *)tw2;
- if (inet_bind_conflict(sk, sk2, sk_uid, relax,
- reuseport_cb_ok, reuseport_ok))
+ if (__inet_bhash2_conflict(sk, sk2, sk_uid, relax,
+ reuseport_cb_ok, reuseport_ok))
return true;
}
+
return false;
}
@@ -1182,20 +1200,31 @@ void inet_csk_prepare_forced_close(struct sock *sk)
}
EXPORT_SYMBOL(inet_csk_prepare_forced_close);
+static int inet_ulp_can_listen(const struct sock *sk)
+{
+ const struct inet_connection_sock *icsk = inet_csk(sk);
+
+ if (icsk->icsk_ulp_ops && !icsk->icsk_ulp_ops->clone)
+ return -EINVAL;
+
+ return 0;
+}
+
int inet_csk_listen_start(struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
struct inet_sock *inet = inet_sk(sk);
int err;
+ err = inet_ulp_can_listen(sk);
+ if (unlikely(err))
+ return err;
+
reqsk_queue_alloc(&icsk->icsk_accept_queue);
sk->sk_ack_backlog = 0;
inet_csk_delack_init(sk);
- if (sk->sk_txrehash == SOCK_TXREHASH_DEFAULT)
- sk->sk_txrehash = READ_ONCE(sock_net(sk)->core.sysctl_txrehash);
-
/* There is race window here: we announce ourselves listening,
* but this transition is still not validated by get_port().
* It is OK, because this socket enters to hash table only
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index d039b4e732a313..f58d73888638b9 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -116,6 +116,7 @@ static void inet_bind2_bucket_init(struct inet_bind2_bucket *tb,
#endif
tb->rcv_saddr = sk->sk_rcv_saddr;
INIT_HLIST_HEAD(&tb->owners);
+ INIT_HLIST_HEAD(&tb->deathrow);
hlist_add_head(&tb->node, &head->chain);
}
@@ -137,7 +138,7 @@ struct inet_bind2_bucket *inet_bind2_bucket_create(struct kmem_cache *cachep,
/* Caller must hold hashbucket lock for this tb with local BH disabled */
void inet_bind2_bucket_destroy(struct kmem_cache *cachep, struct inet_bind2_bucket *tb)
{
- if (hlist_empty(&tb->owners)) {
+ if (hlist_empty(&tb->owners) && hlist_empty(&tb->deathrow)) {
__hlist_del(&tb->node);
kmem_cache_free(cachep, tb);
}
@@ -649,8 +650,20 @@ bool inet_ehash_insert(struct sock *sk, struct sock *osk, bool *found_dup_sk)
spin_lock(lock);
if (osk) {
WARN_ON_ONCE(sk->sk_hash != osk->sk_hash);
- ret = sk_nulls_del_node_init_rcu(osk);
- } else if (found_dup_sk) {
+ ret = sk_hashed(osk);
+ if (ret) {
+ /* Before deleting the node, we insert a new one to make
+ * sure that the look-up-sk process would not miss either
+ * of them and that at least one node would exist in ehash
+ * table all the time. Otherwise there's a tiny chance
+ * that lookup process could find nothing in ehash table.
+ */
+ __sk_nulls_add_node_tail_rcu(sk, list);
+ sk_nulls_del_node_init_rcu(osk);
+ }
+ goto unlock;
+ }
+ if (found_dup_sk) {
*found_dup_sk = inet_ehash_lookup_by_sk(sk, list);
if (*found_dup_sk)
ret = false;
@@ -659,6 +672,7 @@ bool inet_ehash_insert(struct sock *sk, struct sock *osk, bool *found_dup_sk)
if (ret)
__sk_nulls_add_node_rcu(sk, list);
+unlock:
spin_unlock(lock);
return ret;
@@ -1103,15 +1117,16 @@ ok:
/* Head lock still held and bh's disabled */
inet_bind_hash(sk, tb, tb2, port);
- spin_unlock(&head2->lock);
-
if (sk_unhashed(sk)) {
inet_sk(sk)->inet_sport = htons(port);
inet_ehash_nolisten(sk, (struct sock *)tw, NULL);
}
if (tw)
inet_twsk_bind_unhash(tw, hinfo);
+
+ spin_unlock(&head2->lock);
spin_unlock(&head->lock);
+
if (tw)
inet_twsk_deschedule_put(tw);
local_bh_enable();
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c
index 66fc940f9521ab..beed32fff48418 100644
--- a/net/ipv4/inet_timewait_sock.c
+++ b/net/ipv4/inet_timewait_sock.c
@@ -29,6 +29,7 @@
void inet_twsk_bind_unhash(struct inet_timewait_sock *tw,
struct inet_hashinfo *hashinfo)
{
+ struct inet_bind2_bucket *tb2 = tw->tw_tb2;
struct inet_bind_bucket *tb = tw->tw_tb;
if (!tb)
@@ -37,6 +38,11 @@ void inet_twsk_bind_unhash(struct inet_timewait_sock *tw,
__hlist_del(&tw->tw_bind_node);
tw->tw_tb = NULL;
inet_bind_bucket_destroy(hashinfo->bind_bucket_cachep, tb);
+
+ __hlist_del(&tw->tw_bind2_node);
+ tw->tw_tb2 = NULL;
+ inet_bind2_bucket_destroy(hashinfo->bind2_bucket_cachep, tb2);
+
__sock_put((struct sock *)tw);
}
@@ -45,7 +51,7 @@ static void inet_twsk_kill(struct inet_timewait_sock *tw)
{
struct inet_hashinfo *hashinfo = tw->tw_dr->hashinfo;
spinlock_t *lock = inet_ehash_lockp(hashinfo, tw->tw_hash);
- struct inet_bind_hashbucket *bhead;
+ struct inet_bind_hashbucket *bhead, *bhead2;
spin_lock(lock);
sk_nulls_del_node_init_rcu((struct sock *)tw);
@@ -54,9 +60,13 @@ static void inet_twsk_kill(struct inet_timewait_sock *tw)
/* Disassociate with bind bucket. */
bhead = &hashinfo->bhash[inet_bhashfn(twsk_net(tw), tw->tw_num,
hashinfo->bhash_size)];
+ bhead2 = inet_bhashfn_portaddr(hashinfo, (struct sock *)tw,
+ twsk_net(tw), tw->tw_num);
spin_lock(&bhead->lock);
+ spin_lock(&bhead2->lock);
inet_twsk_bind_unhash(tw, hashinfo);
+ spin_unlock(&bhead2->lock);
spin_unlock(&bhead->lock);
refcount_dec(&tw->tw_dr->tw_refcount);
@@ -81,10 +91,10 @@ void inet_twsk_put(struct inet_timewait_sock *tw)
}
EXPORT_SYMBOL_GPL(inet_twsk_put);
-static void inet_twsk_add_node_rcu(struct inet_timewait_sock *tw,
- struct hlist_nulls_head *list)
+static void inet_twsk_add_node_tail_rcu(struct inet_timewait_sock *tw,
+ struct hlist_nulls_head *list)
{
- hlist_nulls_add_head_rcu(&tw->tw_node, list);
+ hlist_nulls_add_tail_rcu(&tw->tw_node, list);
}
static void inet_twsk_add_bind_node(struct inet_timewait_sock *tw,
@@ -93,6 +103,12 @@ static void inet_twsk_add_bind_node(struct inet_timewait_sock *tw,
hlist_add_head(&tw->tw_bind_node, list);
}
+static void inet_twsk_add_bind2_node(struct inet_timewait_sock *tw,
+ struct hlist_head *list)
+{
+ hlist_add_head(&tw->tw_bind2_node, list);
+}
+
/*
* Enter the time wait state. This is called with locally disabled BH.
* Essentially we whip up a timewait bucket, copy the relevant info into it
@@ -105,22 +121,33 @@ void inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
const struct inet_connection_sock *icsk = inet_csk(sk);
struct inet_ehash_bucket *ehead = inet_ehash_bucket(hashinfo, sk->sk_hash);
spinlock_t *lock = inet_ehash_lockp(hashinfo, sk->sk_hash);
- struct inet_bind_hashbucket *bhead;
+ struct inet_bind_hashbucket *bhead, *bhead2;
+
/* Step 1: Put TW into bind hash. Original socket stays there too.
Note, that any socket with inet->num != 0 MUST be bound in
binding cache, even if it is closed.
*/
bhead = &hashinfo->bhash[inet_bhashfn(twsk_net(tw), inet->inet_num,
hashinfo->bhash_size)];
+ bhead2 = inet_bhashfn_portaddr(hashinfo, sk, twsk_net(tw), inet->inet_num);
+
spin_lock(&bhead->lock);
+ spin_lock(&bhead2->lock);
+
tw->tw_tb = icsk->icsk_bind_hash;
WARN_ON(!icsk->icsk_bind_hash);
inet_twsk_add_bind_node(tw, &tw->tw_tb->owners);
+
+ tw->tw_tb2 = icsk->icsk_bind2_hash;
+ WARN_ON(!icsk->icsk_bind2_hash);
+ inet_twsk_add_bind2_node(tw, &tw->tw_tb2->deathrow);
+
+ spin_unlock(&bhead2->lock);
spin_unlock(&bhead->lock);
spin_lock(lock);
- inet_twsk_add_node_rcu(tw, &ehead->chain);
+ inet_twsk_add_node_tail_rcu(tw, &ehead->chain);
/* Step 3: Remove SK from hash chain */
if (__sk_nulls_del_node_init_rcu(sk))
diff --git a/net/ipv4/metrics.c b/net/ipv4/metrics.c
index 7fcfdfd8f9def0..0e3ee1532848c8 100644
--- a/net/ipv4/metrics.c
+++ b/net/ipv4/metrics.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/netlink.h>
+#include <linux/nospec.h>
#include <linux/rtnetlink.h>
#include <linux/types.h>
#include <net/ip.h>
@@ -25,6 +26,7 @@ static int ip_metrics_convert(struct net *net, struct nlattr *fc_mx,
return -EINVAL;
}
+ type = array_index_nospec(type, RTAX_MAX + 1);
if (type == RTAX_CC_ALGO) {
char tmp[TCP_CA_NAME_MAX];
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index c567d5e8053e0c..33f559f491c8cf 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -435,6 +435,7 @@ void tcp_init_sock(struct sock *sk)
/* There's a bubble in the pipe until at least the first ACK. */
tp->app_limited = ~0U;
+ tp->rate_app_limited = 1;
/* See draft-stevens-tcpca-spec-01 for discussion of the
* initialization of these values.
@@ -3178,6 +3179,7 @@ int tcp_disconnect(struct sock *sk, int flags)
tp->plb_rehash = 0;
/* There's a bubble in the pipe until at least the first ACK. */
tp->app_limited = ~0U;
+ tp->rate_app_limited = 1;
tp->rack.mstamp = 0;
tp->rack.advanced = 0;
tp->rack.reo_wnd_steps = 1;
diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c
index 94aad3870c5fcc..cf26d65ca3893d 100644
--- a/net/ipv4/tcp_bpf.c
+++ b/net/ipv4/tcp_bpf.c
@@ -6,6 +6,7 @@
#include <linux/bpf.h>
#include <linux/init.h>
#include <linux/wait.h>
+#include <linux/util_macros.h>
#include <net/inet_common.h>
#include <net/tls.h>
@@ -639,10 +640,9 @@ EXPORT_SYMBOL_GPL(tcp_bpf_update_proto);
*/
void tcp_bpf_clone(const struct sock *sk, struct sock *newsk)
{
- int family = sk->sk_family == AF_INET6 ? TCP_BPF_IPV6 : TCP_BPF_IPV4;
struct proto *prot = newsk->sk_prot;
- if (prot == &tcp_bpf_prots[family][TCP_BPF_BASE])
+ if (is_insidevar(prot, tcp_bpf_prots))
newsk->sk_prot = sk->sk_prot_creator;
}
#endif /* CONFIG_BPF_SYSCALL */
diff --git a/net/ipv4/tcp_ulp.c b/net/ipv4/tcp_ulp.c
index 9ae50b1bd84441..2aa442128630e5 100644
--- a/net/ipv4/tcp_ulp.c
+++ b/net/ipv4/tcp_ulp.c
@@ -139,6 +139,10 @@ static int __tcp_set_ulp(struct sock *sk, const struct tcp_ulp_ops *ulp_ops)
if (sk->sk_socket)
clear_bit(SOCK_SUPPORT_ZC, &sk->sk_socket->flags);
+ err = -ENOTCONN;
+ if (!ulp_ops->clone && sk->sk_state == TCP_LISTEN)
+ goto out_err;
+
err = ulp_ops->init(sk);
if (err)
goto out_err;
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index f7a84a4acffcb5..faa47f9ea73a68 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -3127,17 +3127,17 @@ static void add_v4_addrs(struct inet6_dev *idev)
offset = sizeof(struct in6_addr) - 4;
memcpy(&addr.s6_addr32[3], idev->dev->dev_addr + offset, 4);
- if (idev->dev->flags&IFF_POINTOPOINT) {
+ if (!(idev->dev->flags & IFF_POINTOPOINT) && idev->dev->type == ARPHRD_SIT) {
+ scope = IPV6_ADDR_COMPATv4;
+ plen = 96;
+ pflags |= RTF_NONEXTHOP;
+ } else {
if (idev->cnf.addr_gen_mode == IN6_ADDR_GEN_MODE_NONE)
return;
addr.s6_addr32[0] = htonl(0xfe800000);
scope = IFA_LINK;
plen = 64;
- } else {
- scope = IPV6_ADDR_COMPATv4;
- plen = 96;
- pflags |= RTF_NONEXTHOP;
}
if (addr.s6_addr32[3]) {
@@ -3447,6 +3447,30 @@ static void addrconf_gre_config(struct net_device *dev)
}
#endif
+static void addrconf_init_auto_addrs(struct net_device *dev)
+{
+ switch (dev->type) {
+#if IS_ENABLED(CONFIG_IPV6_SIT)
+ case ARPHRD_SIT:
+ addrconf_sit_config(dev);
+ break;
+#endif
+#if IS_ENABLED(CONFIG_NET_IPGRE) || IS_ENABLED(CONFIG_IPV6_GRE)
+ case ARPHRD_IP6GRE:
+ case ARPHRD_IPGRE:
+ addrconf_gre_config(dev);
+ break;
+#endif
+ case ARPHRD_LOOPBACK:
+ init_loopback(dev);
+ break;
+
+ default:
+ addrconf_dev_config(dev);
+ break;
+ }
+}
+
static int fixup_permanent_addr(struct net *net,
struct inet6_dev *idev,
struct inet6_ifaddr *ifp)
@@ -3615,26 +3639,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
run_pending = 1;
}
- switch (dev->type) {
-#if IS_ENABLED(CONFIG_IPV6_SIT)
- case ARPHRD_SIT:
- addrconf_sit_config(dev);
- break;
-#endif
-#if IS_ENABLED(CONFIG_NET_IPGRE) || IS_ENABLED(CONFIG_IPV6_GRE)
- case ARPHRD_IP6GRE:
- case ARPHRD_IPGRE:
- addrconf_gre_config(dev);
- break;
-#endif
- case ARPHRD_LOOPBACK:
- init_loopback(dev);
- break;
-
- default:
- addrconf_dev_config(dev);
- break;
- }
+ addrconf_init_auto_addrs(dev);
if (!IS_ERR_OR_NULL(idev)) {
if (run_pending)
@@ -6397,7 +6402,7 @@ static int addrconf_sysctl_addr_gen_mode(struct ctl_table *ctl, int write,
if (idev->cnf.addr_gen_mode != new_val) {
idev->cnf.addr_gen_mode = new_val;
- addrconf_dev_config(idev->dev);
+ addrconf_init_auto_addrs(idev->dev);
}
} else if (&net->ipv6.devconf_all->addr_gen_mode == ctl->data) {
struct net_device *dev;
@@ -6408,7 +6413,7 @@ static int addrconf_sysctl_addr_gen_mode(struct ctl_table *ctl, int write,
if (idev &&
idev->cnf.addr_gen_mode != new_val) {
idev->cnf.addr_gen_mode = new_val;
- addrconf_dev_config(idev->dev);
+ addrconf_init_auto_addrs(idev->dev);
}
}
}
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index fee9163382c254..847934763868bb 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -222,6 +222,7 @@ lookup_protocol:
np->pmtudisc = IPV6_PMTUDISC_WANT;
np->repflow = net->ipv6.sysctl.flowlabel_reflect & FLOWLABEL_REFLECT_ESTABLISHED;
sk->sk_ipv6only = net->ipv6.sysctl.bindv6only;
+ sk->sk_txrehash = READ_ONCE(net->core.sysctl_txrehash);
/* Init the ipv4 part of the socket since we can have sockets
* using v6 API for ipv4.
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 60fd91bb5171c7..c314fdde0097c8 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -547,7 +547,20 @@ int ip6_forward(struct sk_buff *skb)
pneigh_lookup(&nd_tbl, net, &hdr->daddr, skb->dev, 0)) {
int proxied = ip6_forward_proxy_check(skb);
if (proxied > 0) {
- hdr->hop_limit--;
+ /* It's tempting to decrease the hop limit
+ * here by 1, as we do at the end of the
+ * function too.
+ *
+ * But that would be incorrect, as proxying is
+ * not forwarding. The ip6_input function
+ * will handle this packet locally, and it
+ * depends on the hop limit being unchanged.
+ *
+ * One example is the NDP hop limit, that
+ * always has to stay 255, but other would be
+ * similar checks around RA packets, where the
+ * user can even change the desired limit.
+ */
return ip6_input(skb);
} else if (proxied < 0) {
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INDISCARDS);
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index a06a9f847db5c8..ada087b50541a6 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -505,6 +505,7 @@ csum_copy_err:
static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
struct raw6_sock *rp)
{
+ struct ipv6_txoptions *opt;
struct sk_buff *skb;
int err = 0;
int offset;
@@ -522,6 +523,9 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
offset = rp->offset;
total_len = inet_sk(sk)->cork.base.length;
+ opt = inet6_sk(sk)->cork.opt;
+ total_len -= opt ? opt->opt_flen : 0;
+
if (offset >= total_len - 1) {
err = -EINVAL;
ip6_flush_pending_frames(sk);
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 9a1415fe3fa78b..03608d3ded4b83 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -104,9 +104,9 @@ static struct workqueue_struct *l2tp_wq;
/* per-net private data for this module */
static unsigned int l2tp_net_id;
struct l2tp_net {
- struct list_head l2tp_tunnel_list;
- /* Lock for write access to l2tp_tunnel_list */
- spinlock_t l2tp_tunnel_list_lock;
+ /* Lock for write access to l2tp_tunnel_idr */
+ spinlock_t l2tp_tunnel_idr_lock;
+ struct idr l2tp_tunnel_idr;
struct hlist_head l2tp_session_hlist[L2TP_HASH_SIZE_2];
/* Lock for write access to l2tp_session_hlist */
spinlock_t l2tp_session_hlist_lock;
@@ -208,13 +208,10 @@ struct l2tp_tunnel *l2tp_tunnel_get(const struct net *net, u32 tunnel_id)
struct l2tp_tunnel *tunnel;
rcu_read_lock_bh();
- list_for_each_entry_rcu(tunnel, &pn->l2tp_tunnel_list, list) {
- if (tunnel->tunnel_id == tunnel_id &&
- refcount_inc_not_zero(&tunnel->ref_count)) {
- rcu_read_unlock_bh();
-
- return tunnel;
- }
+ tunnel = idr_find(&pn->l2tp_tunnel_idr, tunnel_id);
+ if (tunnel && refcount_inc_not_zero(&tunnel->ref_count)) {
+ rcu_read_unlock_bh();
+ return tunnel;
}
rcu_read_unlock_bh();
@@ -224,13 +221,14 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_get);
struct l2tp_tunnel *l2tp_tunnel_get_nth(const struct net *net, int nth)
{
- const struct l2tp_net *pn = l2tp_pernet(net);
+ struct l2tp_net *pn = l2tp_pernet(net);
+ unsigned long tunnel_id, tmp;
struct l2tp_tunnel *tunnel;
int count = 0;
rcu_read_lock_bh();
- list_for_each_entry_rcu(tunnel, &pn->l2tp_tunnel_list, list) {
- if (++count > nth &&
+ idr_for_each_entry_ul(&pn->l2tp_tunnel_idr, tunnel, tmp, tunnel_id) {
+ if (tunnel && ++count > nth &&
refcount_inc_not_zero(&tunnel->ref_count)) {
rcu_read_unlock_bh();
return tunnel;
@@ -1043,7 +1041,7 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb, uns
IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPSKB_REROUTED);
nf_reset_ct(skb);
- bh_lock_sock(sk);
+ bh_lock_sock_nested(sk);
if (sock_owned_by_user(sk)) {
kfree_skb(skb);
ret = NET_XMIT_DROP;
@@ -1227,6 +1225,15 @@ static void l2tp_udp_encap_destroy(struct sock *sk)
l2tp_tunnel_delete(tunnel);
}
+static void l2tp_tunnel_remove(struct net *net, struct l2tp_tunnel *tunnel)
+{
+ struct l2tp_net *pn = l2tp_pernet(net);
+
+ spin_lock_bh(&pn->l2tp_tunnel_idr_lock);
+ idr_remove(&pn->l2tp_tunnel_idr, tunnel->tunnel_id);
+ spin_unlock_bh(&pn->l2tp_tunnel_idr_lock);
+}
+
/* Workqueue tunnel deletion function */
static void l2tp_tunnel_del_work(struct work_struct *work)
{
@@ -1234,7 +1241,6 @@ static void l2tp_tunnel_del_work(struct work_struct *work)
del_work);
struct sock *sk = tunnel->sock;
struct socket *sock = sk->sk_socket;
- struct l2tp_net *pn;
l2tp_tunnel_closeall(tunnel);
@@ -1248,12 +1254,7 @@ static void l2tp_tunnel_del_work(struct work_struct *work)
}
}
- /* Remove the tunnel struct from the tunnel list */
- pn = l2tp_pernet(tunnel->l2tp_net);
- spin_lock_bh(&pn->l2tp_tunnel_list_lock);
- list_del_rcu(&tunnel->list);
- spin_unlock_bh(&pn->l2tp_tunnel_list_lock);
-
+ l2tp_tunnel_remove(tunnel->l2tp_net, tunnel);
/* drop initial ref */
l2tp_tunnel_dec_refcount(tunnel);
@@ -1384,8 +1385,6 @@ out:
return err;
}
-static struct lock_class_key l2tp_socket_class;
-
int l2tp_tunnel_create(int fd, int version, u32 tunnel_id, u32 peer_tunnel_id,
struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp)
{
@@ -1455,12 +1454,19 @@ static int l2tp_validate_socket(const struct sock *sk, const struct net *net,
int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net,
struct l2tp_tunnel_cfg *cfg)
{
- struct l2tp_tunnel *tunnel_walk;
- struct l2tp_net *pn;
+ struct l2tp_net *pn = l2tp_pernet(net);
+ u32 tunnel_id = tunnel->tunnel_id;
struct socket *sock;
struct sock *sk;
int ret;
+ spin_lock_bh(&pn->l2tp_tunnel_idr_lock);
+ ret = idr_alloc_u32(&pn->l2tp_tunnel_idr, NULL, &tunnel_id, tunnel_id,
+ GFP_ATOMIC);
+ spin_unlock_bh(&pn->l2tp_tunnel_idr_lock);
+ if (ret)
+ return ret == -ENOSPC ? -EEXIST : ret;
+
if (tunnel->fd < 0) {
ret = l2tp_tunnel_sock_create(net, tunnel->tunnel_id,
tunnel->peer_tunnel_id, cfg,
@@ -1474,6 +1480,7 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net,
}
sk = sock->sk;
+ lock_sock(sk);
write_lock_bh(&sk->sk_callback_lock);
ret = l2tp_validate_socket(sk, net, tunnel->encap);
if (ret < 0)
@@ -1481,24 +1488,6 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net,
rcu_assign_sk_user_data(sk, tunnel);
write_unlock_bh(&sk->sk_callback_lock);
- tunnel->l2tp_net = net;
- pn = l2tp_pernet(net);
-
- sock_hold(sk);
- tunnel->sock = sk;
-
- spin_lock_bh(&pn->l2tp_tunnel_list_lock);
- list_for_each_entry(tunnel_walk, &pn->l2tp_tunnel_list, list) {
- if (tunnel_walk->tunnel_id == tunnel->tunnel_id) {
- spin_unlock_bh(&pn->l2tp_tunnel_list_lock);
- sock_put(sk);
- ret = -EEXIST;
- goto err_sock;
- }
- }
- list_add_rcu(&tunnel->list, &pn->l2tp_tunnel_list);
- spin_unlock_bh(&pn->l2tp_tunnel_list_lock);
-
if (tunnel->encap == L2TP_ENCAPTYPE_UDP) {
struct udp_tunnel_sock_cfg udp_cfg = {
.sk_user_data = tunnel,
@@ -1512,9 +1501,16 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net,
tunnel->old_sk_destruct = sk->sk_destruct;
sk->sk_destruct = &l2tp_tunnel_destruct;
- lockdep_set_class_and_name(&sk->sk_lock.slock, &l2tp_socket_class,
- "l2tp_sock");
sk->sk_allocation = GFP_ATOMIC;
+ release_sock(sk);
+
+ sock_hold(sk);
+ tunnel->sock = sk;
+ tunnel->l2tp_net = net;
+
+ spin_lock_bh(&pn->l2tp_tunnel_idr_lock);
+ idr_replace(&pn->l2tp_tunnel_idr, tunnel, tunnel->tunnel_id);
+ spin_unlock_bh(&pn->l2tp_tunnel_idr_lock);
trace_register_tunnel(tunnel);
@@ -1523,17 +1519,16 @@ int l2tp_tunnel_register(struct l2tp_tunnel *tunnel, struct net *net,
return 0;
-err_sock:
- write_lock_bh(&sk->sk_callback_lock);
- rcu_assign_sk_user_data(sk, NULL);
err_inval_sock:
write_unlock_bh(&sk->sk_callback_lock);
+ release_sock(sk);
if (tunnel->fd < 0)
sock_release(sock);
else
sockfd_put(sock);
err:
+ l2tp_tunnel_remove(net, tunnel);
return ret;
}
EXPORT_SYMBOL_GPL(l2tp_tunnel_register);
@@ -1647,8 +1642,8 @@ static __net_init int l2tp_init_net(struct net *net)
struct l2tp_net *pn = net_generic(net, l2tp_net_id);
int hash;
- INIT_LIST_HEAD(&pn->l2tp_tunnel_list);
- spin_lock_init(&pn->l2tp_tunnel_list_lock);
+ idr_init(&pn->l2tp_tunnel_idr);
+ spin_lock_init(&pn->l2tp_tunnel_idr_lock);
for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++)
INIT_HLIST_HEAD(&pn->l2tp_session_hlist[hash]);
@@ -1662,11 +1657,13 @@ static __net_exit void l2tp_exit_net(struct net *net)
{
struct l2tp_net *pn = l2tp_pernet(net);
struct l2tp_tunnel *tunnel = NULL;
+ unsigned long tunnel_id, tmp;
int hash;
rcu_read_lock_bh();
- list_for_each_entry_rcu(tunnel, &pn->l2tp_tunnel_list, list) {
- l2tp_tunnel_delete(tunnel);
+ idr_for_each_entry_ul(&pn->l2tp_tunnel_idr, tunnel, tmp, tunnel_id) {
+ if (tunnel)
+ l2tp_tunnel_delete(tunnel);
}
rcu_read_unlock_bh();
@@ -1676,6 +1673,7 @@ static __net_exit void l2tp_exit_net(struct net *net)
for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++)
WARN_ON_ONCE(!hlist_empty(&pn->l2tp_session_hlist[hash]));
+ idr_destroy(&pn->l2tp_tunnel_idr);
}
static struct pernet_operations l2tp_net_ops = {
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index 9c40f8d3bce8cb..f9514bacbd4a18 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -491,7 +491,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
{
struct tid_ampdu_tx *tid_tx;
struct ieee80211_local *local = sta->local;
- struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_sub_if_data *sdata;
struct ieee80211_ampdu_params params = {
.sta = &sta->sta,
.action = IEEE80211_AMPDU_TX_START,
@@ -511,8 +511,6 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
*/
clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state);
- ieee80211_agg_stop_txq(sta, tid);
-
/*
* Make sure no packets are being processed. This ensures that
* we have a valid starting sequence number and that in-flight
@@ -521,6 +519,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
*/
synchronize_net();
+ sdata = sta->sdata;
params.ssn = sta->tid_seq[tid] >> 4;
ret = drv_ampdu_action(local, sdata, &params);
tid_tx->ssn = params.ssn;
@@ -534,6 +533,9 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
*/
set_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state);
} else if (ret) {
+ if (!sdata)
+ return;
+
ht_dbg(sdata,
"BA request denied - HW unavailable for %pM tid %d\n",
sta->sta.addr, tid);
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 8f9a2ab502b38e..672eff6f5d328d 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -147,6 +147,7 @@ static int ieee80211_set_ap_mbssid_options(struct ieee80211_sub_if_data *sdata,
link_conf->bssid_index = 0;
link_conf->nontransmitted = false;
link_conf->ema_ap = false;
+ link_conf->bssid_indicator = 0;
if (sdata->vif.type != NL80211_IFTYPE_AP || !params.tx_wdev)
return -EINVAL;
@@ -1511,6 +1512,12 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev,
kfree(link_conf->ftmr_params);
link_conf->ftmr_params = NULL;
+ sdata->vif.mbssid_tx_vif = NULL;
+ link_conf->bssid_index = 0;
+ link_conf->nontransmitted = false;
+ link_conf->ema_ap = false;
+ link_conf->bssid_indicator = 0;
+
__sta_info_flush(sdata, true);
ieee80211_free_keys(sdata, true);
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 7a3d7893e19d6a..f1914bf39f0e61 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -167,7 +167,7 @@ static ssize_t sta_aqm_read(struct file *file, char __user *userbuf,
continue;
txqi = to_txq_info(sta->sta.txq[i]);
p += scnprintf(p, bufsz + buf - p,
- "%d %d %u %u %u %u %u %u %u %u %u 0x%lx(%s%s%s)\n",
+ "%d %d %u %u %u %u %u %u %u %u %u 0x%lx(%s%s%s%s)\n",
txqi->txq.tid,
txqi->txq.ac,
txqi->tin.backlog_bytes,
@@ -182,7 +182,8 @@ static ssize_t sta_aqm_read(struct file *file, char __user *userbuf,
txqi->flags,
test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ? "STOP" : "RUN",
test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags) ? " AMPDU" : "",
- test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags) ? " NO-AMSDU" : "");
+ test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags) ? " NO-AMSDU" : "",
+ test_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ? " DIRTY" : "");
}
rcu_read_unlock();
diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c
index d737db4e07e243..cfb09e4aed4d3e 100644
--- a/net/mac80211/driver-ops.c
+++ b/net/mac80211/driver-ops.c
@@ -392,6 +392,9 @@ int drv_ampdu_action(struct ieee80211_local *local,
might_sleep();
+ if (!sdata)
+ return -EIO;
+
sdata = get_bss_sdata(sdata);
if (!check_sdata_in_driver(sdata))
return -EIO;
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 809bad53e15b67..5d13a3dfd3664b 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1199,7 +1199,7 @@ static inline void drv_wake_tx_queue(struct ieee80211_local *local,
/* In reconfig don't transmit now, but mark for waking later */
if (local->in_reconfig) {
- set_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txq->flags);
+ set_bit(IEEE80211_TXQ_DIRTY, &txq->flags);
return;
}
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 83bc41346ae7fe..5315ab75028027 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -391,6 +391,37 @@ void ieee80211_ba_session_work(struct work_struct *work)
tid_tx = sta->ampdu_mlme.tid_start_tx[tid];
if (!blocked && tid_tx) {
+ struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]);
+ struct ieee80211_sub_if_data *sdata =
+ vif_to_sdata(txqi->txq.vif);
+ struct fq *fq = &sdata->local->fq;
+
+ spin_lock_bh(&fq->lock);
+
+ /* Allow only frags to be dequeued */
+ set_bit(IEEE80211_TXQ_STOP, &txqi->flags);
+
+ if (!skb_queue_empty(&txqi->frags)) {
+ /* Fragmented Tx is ongoing, wait for it to
+ * finish. Reschedule worker to retry later.
+ */
+
+ spin_unlock_bh(&fq->lock);
+ spin_unlock_bh(&sta->lock);
+
+ /* Give the task working on the txq a chance
+ * to send out the queued frags
+ */
+ synchronize_net();
+
+ mutex_unlock(&sta->ampdu_mlme.mtx);
+
+ ieee80211_queue_work(&sdata->local->hw, work);
+ return;
+ }
+
+ spin_unlock_bh(&fq->lock);
+
/*
* Assign it over to the normal tid_tx array
* where it "goes live".
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 63ff0d2524b653..d16606e84e22de 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -838,7 +838,7 @@ enum txq_info_flags {
IEEE80211_TXQ_STOP,
IEEE80211_TXQ_AMPDU,
IEEE80211_TXQ_NO_AMSDU,
- IEEE80211_TXQ_STOP_NETIF_TX,
+ IEEE80211_TXQ_DIRTY,
};
/**
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index d49a5906a94351..23ed13f150675d 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -364,7 +364,9 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
/* No support for VLAN with MLO yet */
if (iftype == NL80211_IFTYPE_AP_VLAN &&
- nsdata->wdev.use_4addr)
+ sdata->wdev.use_4addr &&
+ nsdata->vif.type == NL80211_IFTYPE_AP &&
+ nsdata->vif.valid_links)
return -EOPNOTSUPP;
/*
@@ -2195,7 +2197,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
ret = cfg80211_register_netdevice(ndev);
if (ret) {
- ieee80211_if_free(ndev);
free_netdev(ndev);
return ret;
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 7e3ab6e1b28f3d..c6562a6d250357 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -4049,6 +4049,58 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
#undef CALL_RXH
}
+static bool
+ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)
+{
+ if (!sta->mlo)
+ return false;
+
+ return !!(sta->valid_links & BIT(link_id));
+}
+
+static bool ieee80211_rx_data_set_link(struct ieee80211_rx_data *rx,
+ u8 link_id)
+{
+ rx->link_id = link_id;
+ rx->link = rcu_dereference(rx->sdata->link[link_id]);
+
+ if (!rx->sta)
+ return rx->link;
+
+ if (!ieee80211_rx_is_valid_sta_link_id(&rx->sta->sta, link_id))
+ return false;
+
+ rx->link_sta = rcu_dereference(rx->sta->link[link_id]);
+
+ return rx->link && rx->link_sta;
+}
+
+static bool ieee80211_rx_data_set_sta(struct ieee80211_rx_data *rx,
+ struct ieee80211_sta *pubsta,
+ int link_id)
+{
+ struct sta_info *sta;
+
+ sta = container_of(pubsta, struct sta_info, sta);
+
+ rx->link_id = link_id;
+ rx->sta = sta;
+
+ if (sta) {
+ rx->local = sta->sdata->local;
+ if (!rx->sdata)
+ rx->sdata = sta->sdata;
+ rx->link_sta = &sta->deflink;
+ }
+
+ if (link_id < 0)
+ rx->link = &rx->sdata->deflink;
+ else if (!ieee80211_rx_data_set_link(rx, link_id))
+ return false;
+
+ return true;
+}
+
/*
* This function makes calls into the RX path, therefore
* it has to be invoked under RCU read lock.
@@ -4057,16 +4109,19 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
{
struct sk_buff_head frames;
struct ieee80211_rx_data rx = {
- .sta = sta,
- .sdata = sta->sdata,
- .local = sta->local,
/* This is OK -- must be QoS data frame */
.security_idx = tid,
.seqno_idx = tid,
- .link_id = -1,
};
struct tid_ampdu_rx *tid_agg_rx;
- u8 link_id;
+ int link_id = -1;
+
+ /* FIXME: statistics won't be right with this */
+ if (sta->sta.valid_links)
+ link_id = ffs(sta->sta.valid_links) - 1;
+
+ if (!ieee80211_rx_data_set_sta(&rx, &sta->sta, link_id))
+ return;
tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
if (!tid_agg_rx)
@@ -4086,10 +4141,6 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
};
drv_event_callback(rx.local, rx.sdata, &event);
}
- /* FIXME: statistics won't be right with this */
- link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0;
- rx.link = rcu_dereference(sta->sdata->link[link_id]);
- rx.link_sta = rcu_dereference(sta->link[link_id]);
ieee80211_rx_handlers(&rx, &frames);
}
@@ -4105,7 +4156,6 @@ void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,
/* This is OK -- must be QoS data frame */
.security_idx = tid,
.seqno_idx = tid,
- .link_id = -1,
};
int i, diff;
@@ -4116,10 +4166,8 @@ void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,
sta = container_of(pubsta, struct sta_info, sta);
- rx.sta = sta;
- rx.sdata = sta->sdata;
- rx.link = &rx.sdata->deflink;
- rx.local = sta->local;
+ if (!ieee80211_rx_data_set_sta(&rx, pubsta, -1))
+ return;
rcu_read_lock();
tid_agg_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[tid]);
@@ -4506,15 +4554,6 @@ void ieee80211_check_fast_rx_iface(struct ieee80211_sub_if_data *sdata)
mutex_unlock(&local->sta_mtx);
}
-static bool
-ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)
-{
- if (!sta->mlo)
- return false;
-
- return !!(sta->valid_links & BIT(link_id));
-}
-
static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
struct ieee80211_fast_rx *fast_rx,
int orig_len)
@@ -4625,7 +4664,6 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
struct sk_buff *skb = rx->skb;
struct ieee80211_hdr *hdr = (void *)skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- struct sta_info *sta = rx->sta;
int orig_len = skb->len;
int hdrlen = ieee80211_hdrlen(hdr->frame_control);
int snap_offs = hdrlen;
@@ -4637,7 +4675,6 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
u8 da[ETH_ALEN];
u8 sa[ETH_ALEN];
} addrs __aligned(2);
- struct link_sta_info *link_sta;
struct ieee80211_sta_rx_stats *stats;
/* for parallel-rx, we need to have DUP_VALIDATED, otherwise we write
@@ -4740,18 +4777,10 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
drop:
dev_kfree_skb(skb);
- if (rx->link_id >= 0) {
- link_sta = rcu_dereference(sta->link[rx->link_id]);
- if (!link_sta)
- return true;
- } else {
- link_sta = &sta->deflink;
- }
-
if (fast_rx->uses_rss)
- stats = this_cpu_ptr(link_sta->pcpu_rx_stats);
+ stats = this_cpu_ptr(rx->link_sta->pcpu_rx_stats);
else
- stats = &link_sta->rx_stats;
+ stats = &rx->link_sta->rx_stats;
stats->dropped++;
return true;
@@ -4769,8 +4798,8 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
struct ieee80211_local *local = rx->local;
struct ieee80211_sub_if_data *sdata = rx->sdata;
struct ieee80211_hdr *hdr = (void *)skb->data;
- struct link_sta_info *link_sta = NULL;
- struct ieee80211_link_data *link;
+ struct link_sta_info *link_sta = rx->link_sta;
+ struct ieee80211_link_data *link = rx->link;
rx->skb = skb;
@@ -4792,35 +4821,6 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
if (!ieee80211_accept_frame(rx))
return false;
- if (rx->link_id >= 0) {
- link = rcu_dereference(rx->sdata->link[rx->link_id]);
-
- /* we might race link removal */
- if (!link)
- return true;
- rx->link = link;
-
- if (rx->sta) {
- rx->link_sta =
- rcu_dereference(rx->sta->link[rx->link_id]);
- if (!rx->link_sta)
- return true;
- }
- } else {
- if (rx->sta)
- rx->link_sta = &rx->sta->deflink;
-
- rx->link = &sdata->deflink;
- }
-
- if (unlikely(!is_multicast_ether_addr(hdr->addr1) &&
- rx->link_id >= 0 && rx->sta && rx->sta->sta.mlo)) {
- link_sta = rcu_dereference(rx->sta->link[rx->link_id]);
-
- if (WARN_ON_ONCE(!link_sta))
- return true;
- }
-
if (!consume) {
struct skb_shared_hwtstamps *shwt;
@@ -4838,9 +4838,12 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
*/
shwt = skb_hwtstamps(rx->skb);
shwt->hwtstamp = skb_hwtstamps(skb)->hwtstamp;
+
+ /* Update the hdr pointer to the new skb for translation below */
+ hdr = (struct ieee80211_hdr *)rx->skb->data;
}
- if (unlikely(link_sta)) {
+ if (unlikely(rx->sta && rx->sta->sta.mlo)) {
/* translate to MLD addresses */
if (ether_addr_equal(link->conf->addr, hdr->addr1))
ether_addr_copy(hdr->addr1, rx->sdata->vif.addr);
@@ -4870,6 +4873,7 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_fast_rx *fast_rx;
struct ieee80211_rx_data rx;
+ int link_id = -1;
memset(&rx, 0, sizeof(rx));
rx.skb = skb;
@@ -4886,12 +4890,8 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
if (!pubsta)
goto drop;
- rx.sta = container_of(pubsta, struct sta_info, sta);
- rx.sdata = rx.sta->sdata;
-
- if (status->link_valid &&
- !ieee80211_rx_is_valid_sta_link_id(pubsta, status->link_id))
- goto drop;
+ if (status->link_valid)
+ link_id = status->link_id;
/*
* TODO: Should the frame be dropped if the right link_id is not
@@ -4900,19 +4900,8 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
* link_id is used only for stats purpose and updating the stats on
* the deflink is fine?
*/
- if (status->link_valid)
- rx.link_id = status->link_id;
-
- if (rx.link_id >= 0) {
- struct ieee80211_link_data *link;
-
- link = rcu_dereference(rx.sdata->link[rx.link_id]);
- if (!link)
- goto drop;
- rx.link = link;
- } else {
- rx.link = &rx.sdata->deflink;
- }
+ if (!ieee80211_rx_data_set_sta(&rx, pubsta, link_id))
+ goto drop;
fast_rx = rcu_dereference(rx.sta->fast_rx);
if (!fast_rx)
@@ -4930,6 +4919,8 @@ static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,
{
struct link_sta_info *link_sta;
struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct sta_info *sta;
+ int link_id = -1;
/*
* Look up link station first, in case there's a
@@ -4939,24 +4930,19 @@ static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,
*/
link_sta = link_sta_info_get_bss(rx->sdata, hdr->addr2);
if (link_sta) {
- rx->sta = link_sta->sta;
- rx->link_id = link_sta->link_id;
+ sta = link_sta->sta;
+ link_id = link_sta->link_id;
} else {
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- rx->sta = sta_info_get_bss(rx->sdata, hdr->addr2);
- if (rx->sta) {
- if (status->link_valid &&
- !ieee80211_rx_is_valid_sta_link_id(&rx->sta->sta,
- status->link_id))
- return false;
-
- rx->link_id = status->link_valid ? status->link_id : -1;
- } else {
- rx->link_id = -1;
- }
+ sta = sta_info_get_bss(rx->sdata, hdr->addr2);
+ if (status->link_valid)
+ link_id = status->link_id;
}
+ if (!ieee80211_rx_data_set_sta(rx, &sta->sta, link_id))
+ return false;
+
return ieee80211_prepare_and_rx_handle(rx, skb, consume);
}
@@ -5015,19 +5001,15 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
if (ieee80211_is_data(fc)) {
struct sta_info *sta, *prev_sta;
- u8 link_id = status->link_id;
+ int link_id = -1;
- if (pubsta) {
- rx.sta = container_of(pubsta, struct sta_info, sta);
- rx.sdata = rx.sta->sdata;
+ if (status->link_valid)
+ link_id = status->link_id;
- if (status->link_valid &&
- !ieee80211_rx_is_valid_sta_link_id(pubsta, link_id))
+ if (pubsta) {
+ if (!ieee80211_rx_data_set_sta(&rx, pubsta, link_id))
goto out;
- if (status->link_valid)
- rx.link_id = status->link_id;
-
/*
* In MLO connection, fetch the link_id using addr2
* when the driver does not pass link_id in status.
@@ -5045,7 +5027,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
if (!link_sta)
goto out;
- rx.link_id = link_sta->link_id;
+ ieee80211_rx_data_set_link(&rx, link_sta->link_id);
}
if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
@@ -5061,30 +5043,27 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
continue;
}
- if ((status->link_valid &&
- !ieee80211_rx_is_valid_sta_link_id(&prev_sta->sta,
- link_id)) ||
- (!status->link_valid && prev_sta->sta.mlo))
+ rx.sdata = prev_sta->sdata;
+ if (!ieee80211_rx_data_set_sta(&rx, &prev_sta->sta,
+ link_id))
+ goto out;
+
+ if (!status->link_valid && prev_sta->sta.mlo)
continue;
- rx.link_id = status->link_valid ? link_id : -1;
- rx.sta = prev_sta;
- rx.sdata = prev_sta->sdata;
ieee80211_prepare_and_rx_handle(&rx, skb, false);
prev_sta = sta;
}
if (prev_sta) {
- if ((status->link_valid &&
- !ieee80211_rx_is_valid_sta_link_id(&prev_sta->sta,
- link_id)) ||
- (!status->link_valid && prev_sta->sta.mlo))
+ rx.sdata = prev_sta->sdata;
+ if (!ieee80211_rx_data_set_sta(&rx, &prev_sta->sta,
+ link_id))
goto out;
- rx.link_id = status->link_valid ? link_id : -1;
- rx.sta = prev_sta;
- rx.sdata = prev_sta->sdata;
+ if (!status->link_valid && prev_sta->sta.mlo)
+ goto out;
if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
return;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 2171cd1ca807e8..defe97a31724d8 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1129,7 +1129,6 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,
struct sk_buff *purge_skb = NULL;
if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
- info->flags |= IEEE80211_TX_CTL_AMPDU;
reset_agg_timer = true;
} else if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {
/*
@@ -1161,7 +1160,6 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx,
if (!tid_tx) {
/* do nothing, let packet pass through */
} else if (test_bit(HT_AGG_STATE_OPERATIONAL, &tid_tx->state)) {
- info->flags |= IEEE80211_TX_CTL_AMPDU;
reset_agg_timer = true;
} else {
queued = true;
@@ -3677,8 +3675,7 @@ static void __ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
info->band = fast_tx->band;
info->control.vif = &sdata->vif;
info->flags = IEEE80211_TX_CTL_FIRST_FRAGMENT |
- IEEE80211_TX_CTL_DONTFRAG |
- (ampdu ? IEEE80211_TX_CTL_AMPDU : 0);
+ IEEE80211_TX_CTL_DONTFRAG;
info->control.flags = IEEE80211_TX_CTRL_FAST_XMIT |
u32_encode_bits(IEEE80211_LINK_UNSPECIFIED,
IEEE80211_TX_CTRL_MLO_LINK);
@@ -3783,6 +3780,8 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
struct ieee80211_tx_data tx;
ieee80211_tx_result r;
struct ieee80211_vif *vif = txq->vif;
+ int q = vif->hw_queue[txq->ac];
+ bool q_stopped;
WARN_ON_ONCE(softirq_count() == 0);
@@ -3790,17 +3789,18 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
return NULL;
begin:
- spin_lock_bh(&fq->lock);
-
- if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ||
- test_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags))
- goto out;
+ spin_lock(&local->queue_stop_reason_lock);
+ q_stopped = local->queue_stop_reasons[q];
+ spin_unlock(&local->queue_stop_reason_lock);
- if (vif->txqs_stopped[txq->ac]) {
- set_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags);
- goto out;
+ if (unlikely(q_stopped)) {
+ /* mark for waking later */
+ set_bit(IEEE80211_TXQ_DIRTY, &txqi->flags);
+ return NULL;
}
+ spin_lock_bh(&fq->lock);
+
/* Make sure fragments stay together. */
skb = __skb_dequeue(&txqi->frags);
if (unlikely(skb)) {
@@ -3810,6 +3810,9 @@ begin:
IEEE80211_SKB_CB(skb)->control.flags &=
~IEEE80211_TX_INTCFL_NEED_TXPROCESSING;
} else {
+ if (unlikely(test_bit(IEEE80211_TXQ_STOP, &txqi->flags)))
+ goto out;
+
skb = fq_tin_dequeue(fq, tin, fq_tin_dequeue_func);
}
@@ -3860,9 +3863,8 @@ begin:
}
if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags))
- info->flags |= IEEE80211_TX_CTL_AMPDU;
- else
- info->flags &= ~IEEE80211_TX_CTL_AMPDU;
+ info->flags |= (IEEE80211_TX_CTL_AMPDU |
+ IEEE80211_TX_CTL_DONTFRAG);
if (info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP) {
if (!ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) {
@@ -4596,8 +4598,6 @@ static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata,
info = IEEE80211_SKB_CB(skb);
memset(info, 0, sizeof(*info));
- if (tid_tx)
- info->flags |= IEEE80211_TX_CTL_AMPDU;
info->hw_queue = sdata->vif.hw_queue[queue];
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 6f5407038459d0..261ac667887f88 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -292,22 +292,12 @@ static void wake_tx_push_queue(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_txq *queue)
{
- int q = sdata->vif.hw_queue[queue->ac];
struct ieee80211_tx_control control = {
.sta = queue->sta,
};
struct sk_buff *skb;
- unsigned long flags;
- bool q_stopped;
while (1) {
- spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
- q_stopped = local->queue_stop_reasons[q];
- spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
-
- if (q_stopped)
- break;
-
skb = ieee80211_tx_dequeue(&local->hw, queue);
if (!skb)
break;
@@ -347,8 +337,6 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac)
local_bh_disable();
spin_lock(&fq->lock);
- sdata->vif.txqs_stopped[ac] = false;
-
if (!test_bit(SDATA_STATE_RUNNING, &sdata->state))
goto out;
@@ -370,7 +358,7 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac)
if (ac != txq->ac)
continue;
- if (!test_and_clear_bit(IEEE80211_TXQ_STOP_NETIF_TX,
+ if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY,
&txqi->flags))
continue;
@@ -385,7 +373,7 @@ static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac)
txqi = to_txq_info(vif->txq);
- if (!test_and_clear_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags) ||
+ if (!test_and_clear_bit(IEEE80211_TXQ_DIRTY, &txqi->flags) ||
(ps && atomic_read(&ps->num_sta_ps)) || ac != vif->txq->ac)
goto out;
@@ -517,8 +505,6 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
bool refcounted)
{
struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_sub_if_data *sdata;
- int n_acs = IEEE80211_NUM_ACS;
trace_stop_queue(local, queue, reason);
@@ -530,29 +516,7 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
else
local->q_stop_reasons[queue][reason]++;
- if (__test_and_set_bit(reason, &local->queue_stop_reasons[queue]))
- return;
-
- if (local->hw.queues < IEEE80211_NUM_ACS)
- n_acs = 1;
-
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &local->interfaces, list) {
- int ac;
-
- if (!sdata->dev)
- continue;
-
- for (ac = 0; ac < n_acs; ac++) {
- if (sdata->vif.hw_queue[ac] == queue ||
- sdata->vif.cab_queue == queue) {
- spin_lock(&local->fq.lock);
- sdata->vif.txqs_stopped[ac] = true;
- spin_unlock(&local->fq.lock);
- }
- }
- }
- rcu_read_unlock();
+ set_bit(reason, &local->queue_stop_reasons[queue]);
}
void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue,
diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c
index c2aae2a6d6a6ba..97bb4401dd3ee2 100644
--- a/net/mac802154/rx.c
+++ b/net/mac802154/rx.c
@@ -213,7 +213,6 @@ __ieee802154_rx_handle_packet(struct ieee802154_local *local,
ret = ieee802154_parse_frame_start(skb, &hdr);
if (ret) {
pr_debug("got invalid frame\n");
- kfree_skb(skb);
return;
}
diff --git a/net/mctp/af_mctp.c b/net/mctp/af_mctp.c
index fc9e728b6333a1..3150f3f0c87258 100644
--- a/net/mctp/af_mctp.c
+++ b/net/mctp/af_mctp.c
@@ -544,9 +544,6 @@ static int mctp_sk_init(struct sock *sk)
static void mctp_sk_close(struct sock *sk, long timeout)
{
- struct mctp_sock *msk = container_of(sk, struct mctp_sock, sk);
-
- del_timer_sync(&msk->key_expiry);
sk_common_release(sk);
}
@@ -580,7 +577,19 @@ static void mctp_sk_unhash(struct sock *sk)
spin_lock_irqsave(&key->lock, fl2);
__mctp_key_remove(key, net, fl2, MCTP_TRACE_KEY_CLOSED);
}
+ sock_set_flag(sk, SOCK_DEAD);
spin_unlock_irqrestore(&net->mctp.keys_lock, flags);
+
+ /* Since there are no more tag allocations (we have removed all of the
+ * keys), stop any pending expiry events. the timer cannot be re-queued
+ * as the sk is no longer observable
+ */
+ del_timer_sync(&msk->key_expiry);
+}
+
+static void mctp_sk_destruct(struct sock *sk)
+{
+ skb_queue_purge(&sk->sk_receive_queue);
}
static struct proto mctp_proto = {
@@ -619,6 +628,7 @@ static int mctp_pf_create(struct net *net, struct socket *sock,
return -ENOMEM;
sock_init_data(sock, sk);
+ sk->sk_destruct = mctp_sk_destruct;
rc = 0;
if (sk->sk_prot->init)
diff --git a/net/mctp/route.c b/net/mctp/route.c
index f9a80b82dc511d..f51a05ec716249 100644
--- a/net/mctp/route.c
+++ b/net/mctp/route.c
@@ -147,6 +147,7 @@ static struct mctp_sk_key *mctp_key_alloc(struct mctp_sock *msk,
key->valid = true;
spin_lock_init(&key->lock);
refcount_set(&key->refs, 1);
+ sock_hold(key->sk);
return key;
}
@@ -165,6 +166,7 @@ void mctp_key_unref(struct mctp_sk_key *key)
mctp_dev_release_key(key->dev, key);
spin_unlock_irqrestore(&key->lock, flags);
+ sock_put(key->sk);
kfree(key);
}
@@ -177,6 +179,11 @@ static int mctp_key_add(struct mctp_sk_key *key, struct mctp_sock *msk)
spin_lock_irqsave(&net->mctp.keys_lock, flags);
+ if (sock_flag(&msk->sk, SOCK_DEAD)) {
+ rc = -EINVAL;
+ goto out_unlock;
+ }
+
hlist_for_each_entry(tmp, &net->mctp.keys, hlist) {
if (mctp_key_match(tmp, key->local_addr, key->peer_addr,
key->tag)) {
@@ -198,6 +205,7 @@ static int mctp_key_add(struct mctp_sk_key *key, struct mctp_sock *msk)
hlist_add_head(&key->sklist, &msk->keys);
}
+out_unlock:
spin_unlock_irqrestore(&net->mctp.keys_lock, flags);
return rc;
@@ -315,8 +323,8 @@ static int mctp_frag_queue(struct mctp_sk_key *key, struct sk_buff *skb)
static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb)
{
+ struct mctp_sk_key *key, *any_key = NULL;
struct net *net = dev_net(skb->dev);
- struct mctp_sk_key *key;
struct mctp_sock *msk;
struct mctp_hdr *mh;
unsigned long f;
@@ -361,13 +369,11 @@ static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb)
* key for reassembly - we'll create a more specific
* one for future packets if required (ie, !EOM).
*/
- key = mctp_lookup_key(net, skb, MCTP_ADDR_ANY, &f);
- if (key) {
- msk = container_of(key->sk,
+ any_key = mctp_lookup_key(net, skb, MCTP_ADDR_ANY, &f);
+ if (any_key) {
+ msk = container_of(any_key->sk,
struct mctp_sock, sk);
- spin_unlock_irqrestore(&key->lock, f);
- mctp_key_unref(key);
- key = NULL;
+ spin_unlock_irqrestore(&any_key->lock, f);
}
}
@@ -419,14 +425,14 @@ static int mctp_route_input(struct mctp_route *route, struct sk_buff *skb)
* this function.
*/
rc = mctp_key_add(key, msk);
- if (rc) {
- kfree(key);
- } else {
+ if (!rc)
trace_mctp_key_acquire(key);
- /* we don't need to release key->lock on exit */
- mctp_key_unref(key);
- }
+ /* we don't need to release key->lock on exit, so
+ * clean up here and suppress the unlock via
+ * setting to NULL
+ */
+ mctp_key_unref(key);
key = NULL;
} else {
@@ -473,6 +479,8 @@ out_unlock:
spin_unlock_irqrestore(&key->lock, f);
mctp_key_unref(key);
}
+ if (any_key)
+ mctp_key_unref(any_key);
out:
if (rc)
kfree_skb(skb);
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index 45e2a48397b959..70f0ced3ca86e1 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -420,6 +420,31 @@ void mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk)
}
}
+/* if sk is ipv4 or ipv6_only allows only same-family local and remote addresses,
+ * otherwise allow any matching local/remote pair
+ */
+bool mptcp_pm_addr_families_match(const struct sock *sk,
+ const struct mptcp_addr_info *loc,
+ const struct mptcp_addr_info *rem)
+{
+ bool mptcp_is_v4 = sk->sk_family == AF_INET;
+
+#if IS_ENABLED(CONFIG_MPTCP_IPV6)
+ bool loc_is_v4 = loc->family == AF_INET || ipv6_addr_v4mapped(&loc->addr6);
+ bool rem_is_v4 = rem->family == AF_INET || ipv6_addr_v4mapped(&rem->addr6);
+
+ if (mptcp_is_v4)
+ return loc_is_v4 && rem_is_v4;
+
+ if (ipv6_only_sock(sk))
+ return !loc_is_v4 && !rem_is_v4;
+
+ return loc_is_v4 == rem_is_v4;
+#else
+ return mptcp_is_v4 && loc->family == AF_INET && rem->family == AF_INET;
+#endif
+}
+
void mptcp_pm_data_reset(struct mptcp_sock *msk)
{
u8 pm_type = mptcp_get_pm_type(sock_net((struct sock *)msk));
diff --git a/net/mptcp/pm_netlink.c b/net/mptcp/pm_netlink.c
index 2ea7eae43bdb1a..10fe9771a852a0 100644
--- a/net/mptcp/pm_netlink.c
+++ b/net/mptcp/pm_netlink.c
@@ -998,8 +998,8 @@ static int mptcp_pm_nl_create_listen_socket(struct sock *sk,
{
int addrlen = sizeof(struct sockaddr_in);
struct sockaddr_storage addr;
- struct mptcp_sock *msk;
struct socket *ssock;
+ struct sock *newsk;
int backlog = 1024;
int err;
@@ -1008,11 +1008,13 @@ static int mptcp_pm_nl_create_listen_socket(struct sock *sk,
if (err)
return err;
- msk = mptcp_sk(entry->lsk->sk);
- if (!msk)
+ newsk = entry->lsk->sk;
+ if (!newsk)
return -EINVAL;
- ssock = __mptcp_nmpc_socket(msk);
+ lock_sock(newsk);
+ ssock = __mptcp_nmpc_socket(mptcp_sk(newsk));
+ release_sock(newsk);
if (!ssock)
return -EINVAL;
diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c
index 65dcc55a8ad89f..ea6ad9da749303 100644
--- a/net/mptcp/pm_userspace.c
+++ b/net/mptcp/pm_userspace.c
@@ -294,6 +294,13 @@ int mptcp_nl_cmd_sf_create(struct sk_buff *skb, struct genl_info *info)
}
sk = (struct sock *)msk;
+
+ if (!mptcp_pm_addr_families_match(sk, &addr_l, &addr_r)) {
+ GENL_SET_ERR_MSG(info, "families mismatch");
+ err = -EINVAL;
+ goto create_err;
+ }
+
lock_sock(sk);
err = __mptcp_subflow_connect(sk, &addr_l, &addr_r);
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index f6f93957275b80..bc6c1f62a6905b 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -98,7 +98,7 @@ static int __mptcp_socket_create(struct mptcp_sock *msk)
struct socket *ssock;
int err;
- err = mptcp_subflow_create_socket(sk, &ssock);
+ err = mptcp_subflow_create_socket(sk, sk->sk_family, &ssock);
if (err)
return err;
@@ -1662,6 +1662,8 @@ static void mptcp_set_nospace(struct sock *sk)
set_bit(MPTCP_NOSPACE, &mptcp_sk(sk)->flags);
}
+static int mptcp_disconnect(struct sock *sk, int flags);
+
static int mptcp_sendmsg_fastopen(struct sock *sk, struct sock *ssk, struct msghdr *msg,
size_t len, int *copied_syn)
{
@@ -1672,9 +1674,9 @@ static int mptcp_sendmsg_fastopen(struct sock *sk, struct sock *ssk, struct msgh
lock_sock(ssk);
msg->msg_flags |= MSG_DONTWAIT;
msk->connect_flags = O_NONBLOCK;
- msk->is_sendmsg = 1;
+ msk->fastopening = 1;
ret = tcp_sendmsg_fastopen(ssk, msg, copied_syn, len, NULL);
- msk->is_sendmsg = 0;
+ msk->fastopening = 0;
msg->msg_flags = saved_flags;
release_sock(ssk);
@@ -1688,6 +1690,8 @@ static int mptcp_sendmsg_fastopen(struct sock *sk, struct sock *ssk, struct msgh
*/
if (ret && ret != -EINPROGRESS && ret != -ERESTARTSYS && ret != -EINTR)
*copied_syn = 0;
+ } else if (ret && ret != -EINPROGRESS) {
+ mptcp_disconnect(sk, 0);
}
return ret;
@@ -2353,7 +2357,7 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
/* otherwise tcp will dispose of the ssk and subflow ctx */
if (ssk->sk_state == TCP_LISTEN) {
tcp_set_state(ssk, TCP_CLOSE);
- mptcp_subflow_queue_clean(ssk);
+ mptcp_subflow_queue_clean(sk, ssk);
inet_csk_listen_stop(ssk);
mptcp_event_pm_listener(ssk, MPTCP_EVENT_LISTENER_CLOSED);
}
@@ -2893,6 +2897,7 @@ bool __mptcp_close(struct sock *sk, long timeout)
struct mptcp_subflow_context *subflow;
struct mptcp_sock *msk = mptcp_sk(sk);
bool do_cancel_work = false;
+ int subflows_alive = 0;
sk->sk_shutdown = SHUTDOWN_MASK;
@@ -2918,6 +2923,8 @@ cleanup:
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
bool slow = lock_sock_fast_nested(ssk);
+ subflows_alive += ssk->sk_state != TCP_CLOSE;
+
/* since the close timeout takes precedence on the fail one,
* cancel the latter
*/
@@ -2933,6 +2940,12 @@ cleanup:
}
sock_orphan(sk);
+ /* all the subflows are closed, only timeout can change the msk
+ * state, let's not keep resources busy for no reasons
+ */
+ if (subflows_alive == 0)
+ inet_sk_state_store(sk, TCP_CLOSE);
+
sock_hold(sk);
pr_debug("msk=%p state=%d", sk, sk->sk_state);
if (msk->token)
@@ -2989,6 +3002,14 @@ static int mptcp_disconnect(struct sock *sk, int flags)
{
struct mptcp_sock *msk = mptcp_sk(sk);
+ /* We are on the fastopen error path. We can't call straight into the
+ * subflows cleanup code due to lock nesting (we are already under
+ * msk->firstsocket lock). Do nothing and leave the cleanup to the
+ * caller.
+ */
+ if (msk->fastopening)
+ return 0;
+
inet_sk_state_store(sk, TCP_CLOSE);
mptcp_stop_timer(sk);
@@ -3532,7 +3553,7 @@ static int mptcp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
/* if reaching here via the fastopen/sendmsg path, the caller already
* acquired the subflow socket lock, too.
*/
- if (msk->is_sendmsg)
+ if (msk->fastopening)
err = __inet_stream_connect(ssock, uaddr, addr_len, msk->connect_flags, 1);
else
err = inet_stream_connect(ssock, uaddr, addr_len, msk->connect_flags);
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 955fb3d88eb3a8..601469249da803 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -295,7 +295,7 @@ struct mptcp_sock {
u8 recvmsg_inq:1,
cork:1,
nodelay:1,
- is_sendmsg:1;
+ fastopening:1;
int connect_flags;
struct work_struct work;
struct sk_buff *ooo_last_skb;
@@ -628,7 +628,7 @@ void mptcp_close_ssk(struct sock *sk, struct sock *ssk,
struct mptcp_subflow_context *subflow);
void __mptcp_subflow_send_ack(struct sock *ssk);
void mptcp_subflow_reset(struct sock *ssk);
-void mptcp_subflow_queue_clean(struct sock *ssk);
+void mptcp_subflow_queue_clean(struct sock *sk, struct sock *ssk);
void mptcp_sock_graft(struct sock *sk, struct socket *parent);
struct socket *__mptcp_nmpc_socket(const struct mptcp_sock *msk);
bool __mptcp_close(struct sock *sk, long timeout);
@@ -641,7 +641,8 @@ bool mptcp_addresses_equal(const struct mptcp_addr_info *a,
/* called with sk socket lock held */
int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc,
const struct mptcp_addr_info *remote);
-int mptcp_subflow_create_socket(struct sock *sk, struct socket **new_sock);
+int mptcp_subflow_create_socket(struct sock *sk, unsigned short family,
+ struct socket **new_sock);
void mptcp_info2sockaddr(const struct mptcp_addr_info *info,
struct sockaddr_storage *addr,
unsigned short family);
@@ -776,6 +777,9 @@ int mptcp_pm_parse_addr(struct nlattr *attr, struct genl_info *info,
int mptcp_pm_parse_entry(struct nlattr *attr, struct genl_info *info,
bool require_family,
struct mptcp_pm_addr_entry *entry);
+bool mptcp_pm_addr_families_match(const struct sock *sk,
+ const struct mptcp_addr_info *loc,
+ const struct mptcp_addr_info *rem);
void mptcp_pm_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk);
void mptcp_pm_nl_subflow_chk_stale(const struct mptcp_sock *msk, struct sock *ssk);
void mptcp_pm_new_connection(struct mptcp_sock *msk, const struct sock *ssk, int server_side);
diff --git a/net/mptcp/sockopt.c b/net/mptcp/sockopt.c
index d4b1e6ec1b3613..7f2c3727ab230b 100644
--- a/net/mptcp/sockopt.c
+++ b/net/mptcp/sockopt.c
@@ -760,14 +760,21 @@ static int mptcp_setsockopt_v4(struct mptcp_sock *msk, int optname,
static int mptcp_setsockopt_first_sf_only(struct mptcp_sock *msk, int level, int optname,
sockptr_t optval, unsigned int optlen)
{
+ struct sock *sk = (struct sock *)msk;
struct socket *sock;
+ int ret = -EINVAL;
/* Limit to first subflow, before the connection establishment */
+ lock_sock(sk);
sock = __mptcp_nmpc_socket(msk);
if (!sock)
- return -EINVAL;
+ goto unlock;
- return tcp_setsockopt(sock->sk, level, optname, optval, optlen);
+ ret = tcp_setsockopt(sock->sk, level, optname, optval, optlen);
+
+unlock:
+ release_sock(sk);
+ return ret;
}
static int mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index d1d32a66ae3f73..32904c76c6a17b 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -1399,6 +1399,7 @@ void __mptcp_error_report(struct sock *sk)
mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
int err = sock_error(ssk);
+ int ssk_state;
if (!err)
continue;
@@ -1409,7 +1410,14 @@ void __mptcp_error_report(struct sock *sk)
if (sk->sk_state != TCP_SYN_SENT && !__mptcp_check_fallback(msk))
continue;
- inet_sk_state_store(sk, inet_sk_state_load(ssk));
+ /* We need to propagate only transition to CLOSE state.
+ * Orphaned socket will see such state change via
+ * subflow_sched_work_if_closed() and that path will properly
+ * destroy the msk as needed.
+ */
+ ssk_state = inet_sk_state_load(ssk);
+ if (ssk_state == TCP_CLOSE && !sock_flag(sk, SOCK_DEAD))
+ inet_sk_state_store(sk, ssk_state);
sk->sk_err = -err;
/* This barrier is coupled with smp_rmb() in mptcp_poll() */
@@ -1547,7 +1555,7 @@ int __mptcp_subflow_connect(struct sock *sk, const struct mptcp_addr_info *loc,
if (!mptcp_is_fully_established(sk))
goto err_out;
- err = mptcp_subflow_create_socket(sk, &sf);
+ err = mptcp_subflow_create_socket(sk, loc->family, &sf);
if (err)
goto err_out;
@@ -1660,7 +1668,9 @@ static void mptcp_subflow_ops_undo_override(struct sock *ssk)
#endif
ssk->sk_prot = &tcp_prot;
}
-int mptcp_subflow_create_socket(struct sock *sk, struct socket **new_sock)
+
+int mptcp_subflow_create_socket(struct sock *sk, unsigned short family,
+ struct socket **new_sock)
{
struct mptcp_subflow_context *subflow;
struct net *net = sock_net(sk);
@@ -1673,12 +1683,11 @@ int mptcp_subflow_create_socket(struct sock *sk, struct socket **new_sock)
if (unlikely(!sk->sk_socket))
return -EINVAL;
- err = sock_create_kern(net, sk->sk_family, SOCK_STREAM, IPPROTO_TCP,
- &sf);
+ err = sock_create_kern(net, family, SOCK_STREAM, IPPROTO_TCP, &sf);
if (err)
return err;
- lock_sock(sf->sk);
+ lock_sock_nested(sf->sk, SINGLE_DEPTH_NESTING);
/* the newly created socket has to be in the same cgroup as its parent */
mptcp_attach_cgroup(sk, sf->sk);
@@ -1791,7 +1800,7 @@ static void subflow_state_change(struct sock *sk)
}
}
-void mptcp_subflow_queue_clean(struct sock *listener_ssk)
+void mptcp_subflow_queue_clean(struct sock *listener_sk, struct sock *listener_ssk)
{
struct request_sock_queue *queue = &inet_csk(listener_ssk)->icsk_accept_queue;
struct mptcp_sock *msk, *next, *head = NULL;
@@ -1840,8 +1849,23 @@ void mptcp_subflow_queue_clean(struct sock *listener_ssk)
do_cancel_work = __mptcp_close(sk, 0);
release_sock(sk);
- if (do_cancel_work)
+ if (do_cancel_work) {
+ /* lockdep will report a false positive ABBA deadlock
+ * between cancel_work_sync and the listener socket.
+ * The involved locks belong to different sockets WRT
+ * the existing AB chain.
+ * Using a per socket key is problematic as key
+ * deregistration requires process context and must be
+ * performed at socket disposal time, in atomic
+ * context.
+ * Just tell lockdep to consider the listener socket
+ * released here.
+ */
+ mutex_release(&listener_sk->sk_lock.dep_map, _RET_IP_);
mptcp_cancel_work(sk);
+ mutex_acquire(&listener_sk->sk_lock.dep_map,
+ SINGLE_DEPTH_NESTING, 0, _RET_IP_);
+ }
sock_put(sk);
}
diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c
index a8ce04a4bb72ab..e4fa00abde6a2a 100644
--- a/net/netfilter/ipset/ip_set_bitmap_ip.c
+++ b/net/netfilter/ipset/ip_set_bitmap_ip.c
@@ -308,8 +308,8 @@ bitmap_ip_create(struct net *net, struct ip_set *set, struct nlattr *tb[],
return -IPSET_ERR_BITMAP_RANGE;
pr_debug("mask_bits %u, netmask %u\n", mask_bits, netmask);
- hosts = 2 << (32 - netmask - 1);
- elements = 2 << (netmask - mask_bits - 1);
+ hosts = 2U << (32 - netmask - 1);
+ elements = 2UL << (netmask - mask_bits - 1);
}
if (elements > IPSET_BITMAP_MAX_RANGE + 1)
return -IPSET_ERR_BITMAP_RANGE_SIZE;
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index e7ba5b6dd2b7c1..46ebee9400dab8 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -1698,9 +1698,10 @@ call_ad(struct net *net, struct sock *ctnl, struct sk_buff *skb,
ret = set->variant->uadt(set, tb, adt, &lineno, flags, retried);
ip_set_unlock(set);
retried = true;
- } while (ret == -EAGAIN &&
- set->variant->resize &&
- (ret = set->variant->resize(set, retried)) == 0);
+ } while (ret == -ERANGE ||
+ (ret == -EAGAIN &&
+ set->variant->resize &&
+ (ret = set->variant->resize(set, retried)) == 0));
if (!ret || (ret == -IPSET_ERR_EXIST && eexist))
return 0;
diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c
index e30513cefd90e7..c9f4e385966323 100644
--- a/net/netfilter/ipset/ip_set_hash_ip.c
+++ b/net/netfilter/ipset/ip_set_hash_ip.c
@@ -100,11 +100,11 @@ static int
hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_ip4 *h = set->data;
+ struct hash_ip4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ip4_elem e = { 0 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip = 0, ip_to = 0, hosts;
+ u32 ip = 0, ip_to = 0, hosts, i = 0;
int ret = 0;
if (tb[IPSET_ATTR_LINENO])
@@ -149,14 +149,14 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1);
- /* 64bit division is not allowed on 32bit */
- if (((u64)ip_to - ip + 1) >> (32 - h->netmask) > IPSET_MAX_RANGE)
- return -ERANGE;
-
if (retried)
ip = ntohl(h->next.ip);
- for (; ip <= ip_to;) {
+ for (; ip <= ip_to; i++) {
e.ip = htonl(ip);
+ if (i > IPSET_MAX_RANGE) {
+ hash_ip4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
diff --git a/net/netfilter/ipset/ip_set_hash_ipmark.c b/net/netfilter/ipset/ip_set_hash_ipmark.c
index 153de3457423e5..a22ec1a6f6ec85 100644
--- a/net/netfilter/ipset/ip_set_hash_ipmark.c
+++ b/net/netfilter/ipset/ip_set_hash_ipmark.c
@@ -97,11 +97,11 @@ static int
hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_ipmark4 *h = set->data;
+ struct hash_ipmark4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipmark4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip, ip_to = 0;
+ u32 ip, ip_to = 0, i = 0;
int ret;
if (tb[IPSET_ATTR_LINENO])
@@ -148,13 +148,14 @@ hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[],
ip_set_mask_from_to(ip, ip_to, cidr);
}
- if (((u64)ip_to - ip + 1) > IPSET_MAX_RANGE)
- return -ERANGE;
-
if (retried)
ip = ntohl(h->next.ip);
- for (; ip <= ip_to; ip++) {
+ for (; ip <= ip_to; ip++, i++) {
e.ip = htonl(ip);
+ if (i > IPSET_MAX_RANGE) {
+ hash_ipmark4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c
index 2ffbd0b78a8c52..e977b5a9c48dcb 100644
--- a/net/netfilter/ipset/ip_set_hash_ipport.c
+++ b/net/netfilter/ipset/ip_set_hash_ipport.c
@@ -112,11 +112,11 @@ static int
hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_ipport4 *h = set->data;
+ struct hash_ipport4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipport4_elem e = { .ip = 0 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip, ip_to = 0, p = 0, port, port_to;
+ u32 ip, ip_to = 0, p = 0, port, port_to, i = 0;
bool with_ports = false;
int ret;
@@ -184,17 +184,18 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[],
swap(port, port_to);
}
- if (((u64)ip_to - ip + 1)*(port_to - port + 1) > IPSET_MAX_RANGE)
- return -ERANGE;
-
if (retried)
ip = ntohl(h->next.ip);
for (; ip <= ip_to; ip++) {
p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
: port;
- for (; p <= port_to; p++) {
+ for (; p <= port_to; p++, i++) {
e.ip = htonl(ip);
e.port = htons(p);
+ if (i > IPSET_MAX_RANGE) {
+ hash_ipport4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c
index 334fb1ad0e86cf..39a01934b1536d 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportip.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportip.c
@@ -108,11 +108,11 @@ static int
hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_ipportip4 *h = set->data;
+ struct hash_ipportip4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportip4_elem e = { .ip = 0 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip, ip_to = 0, p = 0, port, port_to;
+ u32 ip, ip_to = 0, p = 0, port, port_to, i = 0;
bool with_ports = false;
int ret;
@@ -180,17 +180,18 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[],
swap(port, port_to);
}
- if (((u64)ip_to - ip + 1)*(port_to - port + 1) > IPSET_MAX_RANGE)
- return -ERANGE;
-
if (retried)
ip = ntohl(h->next.ip);
for (; ip <= ip_to; ip++) {
p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port)
: port;
- for (; p <= port_to; p++) {
+ for (; p <= port_to; p++, i++) {
e.ip = htonl(ip);
e.port = htons(p);
+ if (i > IPSET_MAX_RANGE) {
+ hash_ipportip4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c
index 7df94f437f600f..5c6de605a9fb7f 100644
--- a/net/netfilter/ipset/ip_set_hash_ipportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c
@@ -160,12 +160,12 @@ static int
hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_ipportnet4 *h = set->data;
+ struct hash_ipportnet4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip = 0, ip_to = 0, p = 0, port, port_to;
- u32 ip2_from = 0, ip2_to = 0, ip2;
+ u32 ip2_from = 0, ip2_to = 0, ip2, i = 0;
bool with_ports = false;
u8 cidr;
int ret;
@@ -253,9 +253,6 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
swap(port, port_to);
}
- if (((u64)ip_to - ip + 1)*(port_to - port + 1) > IPSET_MAX_RANGE)
- return -ERANGE;
-
ip2_to = ip2_from;
if (tb[IPSET_ATTR_IP2_TO]) {
ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to);
@@ -282,9 +279,15 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
for (; p <= port_to; p++) {
e.port = htons(p);
do {
+ i++;
e.ip2 = htonl(ip2);
ip2 = ip_set_range_to_cidr(ip2, ip2_to, &cidr);
e.cidr = cidr - 1;
+ if (i > IPSET_MAX_RANGE) {
+ hash_ipportnet4_data_next(&h->next,
+ &e);
+ return -ERANGE;
+ }
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c
index 1422739d9aa255..ce0a9ce5a91f1a 100644
--- a/net/netfilter/ipset/ip_set_hash_net.c
+++ b/net/netfilter/ipset/ip_set_hash_net.c
@@ -136,11 +136,11 @@ static int
hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_net4 *h = set->data;
+ struct hash_net4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_net4_elem e = { .cidr = HOST_MASK };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip = 0, ip_to = 0, ipn, n = 0;
+ u32 ip = 0, ip_to = 0, i = 0;
int ret;
if (tb[IPSET_ATTR_LINENO])
@@ -188,19 +188,16 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[],
if (ip + UINT_MAX == ip_to)
return -IPSET_ERR_HASH_RANGE;
}
- ipn = ip;
- do {
- ipn = ip_set_range_to_cidr(ipn, ip_to, &e.cidr);
- n++;
- } while (ipn++ < ip_to);
-
- if (n > IPSET_MAX_RANGE)
- return -ERANGE;
if (retried)
ip = ntohl(h->next.ip);
do {
+ i++;
e.ip = htonl(ip);
+ if (i > IPSET_MAX_RANGE) {
+ hash_net4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c
index 9810f5bf63f5e4..0310732862362c 100644
--- a/net/netfilter/ipset/ip_set_hash_netiface.c
+++ b/net/netfilter/ipset/ip_set_hash_netiface.c
@@ -202,7 +202,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netiface4_elem e = { .cidr = HOST_MASK, .elem = 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 ip = 0, ip_to = 0, ipn, n = 0;
+ u32 ip = 0, ip_to = 0, i = 0;
int ret;
if (tb[IPSET_ATTR_LINENO])
@@ -256,19 +256,16 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[],
} else {
ip_set_mask_from_to(ip, ip_to, e.cidr);
}
- ipn = ip;
- do {
- ipn = ip_set_range_to_cidr(ipn, ip_to, &e.cidr);
- n++;
- } while (ipn++ < ip_to);
-
- if (n > IPSET_MAX_RANGE)
- return -ERANGE;
if (retried)
ip = ntohl(h->next.ip);
do {
+ i++;
e.ip = htonl(ip);
+ if (i > IPSET_MAX_RANGE) {
+ hash_netiface4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr);
ret = adtfn(set, &e, &ext, &ext, flags);
diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c b/net/netfilter/ipset/ip_set_hash_netnet.c
index cdfb78c6e0d3de..8fbe649c9dd3d4 100644
--- a/net/netfilter/ipset/ip_set_hash_netnet.c
+++ b/net/netfilter/ipset/ip_set_hash_netnet.c
@@ -166,13 +166,12 @@ static int
hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_netnet4 *h = set->data;
+ struct hash_netnet4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netnet4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip = 0, ip_to = 0;
- u32 ip2 = 0, ip2_from = 0, ip2_to = 0, ipn;
- u64 n = 0, m = 0;
+ u32 ip2 = 0, ip2_from = 0, ip2_to = 0, i = 0;
int ret;
if (tb[IPSET_ATTR_LINENO])
@@ -248,19 +247,6 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
} else {
ip_set_mask_from_to(ip2_from, ip2_to, e.cidr[1]);
}
- ipn = ip;
- do {
- ipn = ip_set_range_to_cidr(ipn, ip_to, &e.cidr[0]);
- n++;
- } while (ipn++ < ip_to);
- ipn = ip2_from;
- do {
- ipn = ip_set_range_to_cidr(ipn, ip2_to, &e.cidr[1]);
- m++;
- } while (ipn++ < ip2_to);
-
- if (n*m > IPSET_MAX_RANGE)
- return -ERANGE;
if (retried) {
ip = ntohl(h->next.ip[0]);
@@ -273,7 +259,12 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
e.ip[0] = htonl(ip);
ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr[0]);
do {
+ i++;
e.ip[1] = htonl(ip2);
+ if (i > IPSET_MAX_RANGE) {
+ hash_netnet4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ip2 = ip_set_range_to_cidr(ip2, ip2_to, &e.cidr[1]);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c
index 09cf72eb37f8d2..d1a0628df4ef3a 100644
--- a/net/netfilter/ipset/ip_set_hash_netport.c
+++ b/net/netfilter/ipset/ip_set_hash_netport.c
@@ -154,12 +154,11 @@ static int
hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_netport4 *h = set->data;
+ struct hash_netport4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netport4_elem e = { .cidr = HOST_MASK - 1 };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
- u32 port, port_to, p = 0, ip = 0, ip_to = 0, ipn;
- u64 n = 0;
+ u32 port, port_to, p = 0, ip = 0, ip_to = 0, i = 0;
bool with_ports = false;
u8 cidr;
int ret;
@@ -236,14 +235,6 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
} else {
ip_set_mask_from_to(ip, ip_to, e.cidr + 1);
}
- ipn = ip;
- do {
- ipn = ip_set_range_to_cidr(ipn, ip_to, &cidr);
- n++;
- } while (ipn++ < ip_to);
-
- if (n*(port_to - port + 1) > IPSET_MAX_RANGE)
- return -ERANGE;
if (retried) {
ip = ntohl(h->next.ip);
@@ -255,8 +246,12 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[],
e.ip = htonl(ip);
ip = ip_set_range_to_cidr(ip, ip_to, &cidr);
e.cidr = cidr - 1;
- for (; p <= port_to; p++) {
+ for (; p <= port_to; p++, i++) {
e.port = htons(p);
+ if (i > IPSET_MAX_RANGE) {
+ hash_netport4_data_next(&h->next, &e);
+ return -ERANGE;
+ }
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c
index 19bcdb3141f6e6..005a7ce87217e2 100644
--- a/net/netfilter/ipset/ip_set_hash_netportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_netportnet.c
@@ -173,17 +173,26 @@ hash_netportnet4_kadt(struct ip_set *set, const struct sk_buff *skb,
return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
}
+static u32
+hash_netportnet4_range_to_cidr(u32 from, u32 to, u8 *cidr)
+{
+ if (from == 0 && to == UINT_MAX) {
+ *cidr = 0;
+ return to;
+ }
+ return ip_set_range_to_cidr(from, to, cidr);
+}
+
static int
hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
- const struct hash_netportnet4 *h = set->data;
+ struct hash_netportnet4 *h = set->data;
ipset_adtfn adtfn = set->variant->adt[adt];
struct hash_netportnet4_elem e = { };
struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
u32 ip = 0, ip_to = 0, p = 0, port, port_to;
- u32 ip2_from = 0, ip2_to = 0, ip2, ipn;
- u64 n = 0, m = 0;
+ u32 ip2_from = 0, ip2_to = 0, ip2, i = 0;
bool with_ports = false;
int ret;
@@ -285,19 +294,6 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
} else {
ip_set_mask_from_to(ip2_from, ip2_to, e.cidr[1]);
}
- ipn = ip;
- do {
- ipn = ip_set_range_to_cidr(ipn, ip_to, &e.cidr[0]);
- n++;
- } while (ipn++ < ip_to);
- ipn = ip2_from;
- do {
- ipn = ip_set_range_to_cidr(ipn, ip2_to, &e.cidr[1]);
- m++;
- } while (ipn++ < ip2_to);
-
- if (n*m*(port_to - port + 1) > IPSET_MAX_RANGE)
- return -ERANGE;
if (retried) {
ip = ntohl(h->next.ip[0]);
@@ -310,13 +306,19 @@ hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[],
do {
e.ip[0] = htonl(ip);
- ip = ip_set_range_to_cidr(ip, ip_to, &e.cidr[0]);
+ ip = hash_netportnet4_range_to_cidr(ip, ip_to, &e.cidr[0]);
for (; p <= port_to; p++) {
e.port = htons(p);
do {
+ i++;
e.ip[1] = htonl(ip2);
- ip2 = ip_set_range_to_cidr(ip2, ip2_to,
- &e.cidr[1]);
+ if (i > IPSET_MAX_RANGE) {
+ hash_netportnet4_data_next(&h->next,
+ &e);
+ return -ERANGE;
+ }
+ ip2 = hash_netportnet4_range_to_cidr(ip2,
+ ip2_to, &e.cidr[1]);
ret = adtfn(set, &e, &ext, &ext, flags);
if (ret && !ip_set_eexist(ret, flags))
return ret;
diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c
index 99323fb12d0f51..ccef340be575ec 100644
--- a/net/netfilter/nf_conntrack_proto.c
+++ b/net/netfilter/nf_conntrack_proto.c
@@ -141,6 +141,7 @@ unsigned int nf_confirm(void *priv,
struct nf_conn *ct;
bool seqadj_needed;
__be16 frag_off;
+ int start;
u8 pnum;
ct = nf_ct_get(skb, &ctinfo);
@@ -163,9 +164,11 @@ unsigned int nf_confirm(void *priv,
break;
case NFPROTO_IPV6:
pnum = ipv6_hdr(skb)->nexthdr;
- protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum, &frag_off);
- if (protoff < 0 || (frag_off & htons(~0x7)) != 0)
+ start = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum, &frag_off);
+ if (start < 0 || (frag_off & htons(~0x7)) != 0)
return nf_conntrack_confirm(skb);
+
+ protoff = start;
break;
default:
return nf_conntrack_confirm(skb);
diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c
index d88b92a8ffca88..011d414038ea08 100644
--- a/net/netfilter/nf_conntrack_proto_sctp.c
+++ b/net/netfilter/nf_conntrack_proto_sctp.c
@@ -27,22 +27,16 @@
#include <net/netfilter/nf_conntrack_ecache.h>
#include <net/netfilter/nf_conntrack_timeout.h>
-/* FIXME: Examine ipfilter's timeouts and conntrack transitions more
- closely. They're more complex. --RR
-
- And so for me for SCTP :D -Kiran */
-
static const char *const sctp_conntrack_names[] = {
- "NONE",
- "CLOSED",
- "COOKIE_WAIT",
- "COOKIE_ECHOED",
- "ESTABLISHED",
- "SHUTDOWN_SENT",
- "SHUTDOWN_RECD",
- "SHUTDOWN_ACK_SENT",
- "HEARTBEAT_SENT",
- "HEARTBEAT_ACKED",
+ [SCTP_CONNTRACK_NONE] = "NONE",
+ [SCTP_CONNTRACK_CLOSED] = "CLOSED",
+ [SCTP_CONNTRACK_COOKIE_WAIT] = "COOKIE_WAIT",
+ [SCTP_CONNTRACK_COOKIE_ECHOED] = "COOKIE_ECHOED",
+ [SCTP_CONNTRACK_ESTABLISHED] = "ESTABLISHED",
+ [SCTP_CONNTRACK_SHUTDOWN_SENT] = "SHUTDOWN_SENT",
+ [SCTP_CONNTRACK_SHUTDOWN_RECD] = "SHUTDOWN_RECD",
+ [SCTP_CONNTRACK_SHUTDOWN_ACK_SENT] = "SHUTDOWN_ACK_SENT",
+ [SCTP_CONNTRACK_HEARTBEAT_SENT] = "HEARTBEAT_SENT",
};
#define SECS * HZ
@@ -54,13 +48,11 @@ static const unsigned int sctp_timeouts[SCTP_CONNTRACK_MAX] = {
[SCTP_CONNTRACK_CLOSED] = 10 SECS,
[SCTP_CONNTRACK_COOKIE_WAIT] = 3 SECS,
[SCTP_CONNTRACK_COOKIE_ECHOED] = 3 SECS,
- [SCTP_CONNTRACK_ESTABLISHED] = 5 DAYS,
+ [SCTP_CONNTRACK_ESTABLISHED] = 210 SECS,
[SCTP_CONNTRACK_SHUTDOWN_SENT] = 300 SECS / 1000,
[SCTP_CONNTRACK_SHUTDOWN_RECD] = 300 SECS / 1000,
[SCTP_CONNTRACK_SHUTDOWN_ACK_SENT] = 3 SECS,
[SCTP_CONNTRACK_HEARTBEAT_SENT] = 30 SECS,
- [SCTP_CONNTRACK_HEARTBEAT_ACKED] = 210 SECS,
- [SCTP_CONNTRACK_DATA_SENT] = 30 SECS,
};
#define SCTP_FLAG_HEARTBEAT_VTAG_FAILED 1
@@ -74,8 +66,6 @@ static const unsigned int sctp_timeouts[SCTP_CONNTRACK_MAX] = {
#define sSR SCTP_CONNTRACK_SHUTDOWN_RECD
#define sSA SCTP_CONNTRACK_SHUTDOWN_ACK_SENT
#define sHS SCTP_CONNTRACK_HEARTBEAT_SENT
-#define sHA SCTP_CONNTRACK_HEARTBEAT_ACKED
-#define sDS SCTP_CONNTRACK_DATA_SENT
#define sIV SCTP_CONNTRACK_MAX
/*
@@ -98,10 +88,6 @@ SHUTDOWN_ACK_SENT - We have seen a SHUTDOWN_ACK chunk in the direction opposite
CLOSED - We have seen a SHUTDOWN_COMPLETE chunk in the direction of
the SHUTDOWN chunk. Connection is closed.
HEARTBEAT_SENT - We have seen a HEARTBEAT in a new flow.
-HEARTBEAT_ACKED - We have seen a HEARTBEAT-ACK/DATA/SACK in the direction
- opposite to that of the HEARTBEAT/DATA chunk. Secondary connection
- is established.
-DATA_SENT - We have seen a DATA/SACK in a new flow.
*/
/* TODO
@@ -115,38 +101,36 @@ cookie echoed to closed.
*/
/* SCTP conntrack state transitions */
-static const u8 sctp_conntracks[2][12][SCTP_CONNTRACK_MAX] = {
+static const u8 sctp_conntracks[2][11][SCTP_CONNTRACK_MAX] = {
{
/* ORIGINAL */
-/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS */
-/* init */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCW, sHA, sCW},
-/* init_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL},
-/* abort */ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL},
-/* shutdown */ {sCL, sCL, sCW, sCE, sSS, sSS, sSR, sSA, sCL, sSS, sCL},
-/* shutdown_ack */ {sSA, sCL, sCW, sCE, sES, sSA, sSA, sSA, sSA, sHA, sSA},
-/* error */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL},/* Can't have Stale cookie*/
-/* cookie_echo */ {sCL, sCL, sCE, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL},/* 5.2.4 - Big TODO */
-/* cookie_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL, sHA, sCL},/* Can't come in orig dir */
-/* shutdown_comp*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sCL, sCL, sHA, sCL},
-/* heartbeat */ {sHS, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS},
-/* heartbeat_ack*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS},
-/* data/sack */ {sDS, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS}
+/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS */
+/* init */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCW},
+/* init_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL},
+/* abort */ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL},
+/* shutdown */ {sCL, sCL, sCW, sCE, sSS, sSS, sSR, sSA, sCL},
+/* shutdown_ack */ {sSA, sCL, sCW, sCE, sES, sSA, sSA, sSA, sSA},
+/* error */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL},/* Can't have Stale cookie*/
+/* cookie_echo */ {sCL, sCL, sCE, sCE, sES, sSS, sSR, sSA, sCL},/* 5.2.4 - Big TODO */
+/* cookie_ack */ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sCL},/* Can't come in orig dir */
+/* shutdown_comp*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sCL, sCL},
+/* heartbeat */ {sHS, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS},
+/* heartbeat_ack*/ {sCL, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS},
},
{
/* REPLY */
-/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sDS */
-/* init */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA, sIV},/* INIT in sCL Big TODO */
-/* init_ack */ {sIV, sCW, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA, sIV},
-/* abort */ {sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV, sCL, sIV},
-/* shutdown */ {sIV, sCL, sCW, sCE, sSR, sSS, sSR, sSA, sIV, sSR, sIV},
-/* shutdown_ack */ {sIV, sCL, sCW, sCE, sES, sSA, sSA, sSA, sIV, sHA, sIV},
-/* error */ {sIV, sCL, sCW, sCL, sES, sSS, sSR, sSA, sIV, sHA, sIV},
-/* cookie_echo */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sIV, sHA, sIV},/* Can't come in reply dir */
-/* cookie_ack */ {sIV, sCL, sCW, sES, sES, sSS, sSR, sSA, sIV, sHA, sIV},
-/* shutdown_comp*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sCL, sIV, sHA, sIV},
-/* heartbeat */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS, sHA, sHA},
-/* heartbeat_ack*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHA, sHA, sHA},
-/* data/sack */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHA, sHA, sHA},
+/* sNO, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS */
+/* init */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sIV},/* INIT in sCL Big TODO */
+/* init_ack */ {sIV, sCW, sCW, sCE, sES, sSS, sSR, sSA, sIV},
+/* abort */ {sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV},
+/* shutdown */ {sIV, sCL, sCW, sCE, sSR, sSS, sSR, sSA, sIV},
+/* shutdown_ack */ {sIV, sCL, sCW, sCE, sES, sSA, sSA, sSA, sIV},
+/* error */ {sIV, sCL, sCW, sCL, sES, sSS, sSR, sSA, sIV},
+/* cookie_echo */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sIV},/* Can't come in reply dir */
+/* cookie_ack */ {sIV, sCL, sCW, sES, sES, sSS, sSR, sSA, sIV},
+/* shutdown_comp*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sCL, sIV},
+/* heartbeat */ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sHS},
+/* heartbeat_ack*/ {sIV, sCL, sCW, sCE, sES, sSS, sSR, sSA, sES},
}
};
@@ -158,6 +142,7 @@ static void sctp_print_conntrack(struct seq_file *s, struct nf_conn *ct)
}
#endif
+/* do_basic_checks ensures sch->length > 0, do not use before */
#define for_each_sctp_chunk(skb, sch, _sch, offset, dataoff, count) \
for ((offset) = (dataoff) + sizeof(struct sctphdr), (count) = 0; \
(offset) < (skb)->len && \
@@ -258,11 +243,6 @@ static int sctp_new_state(enum ip_conntrack_dir dir,
pr_debug("SCTP_CID_HEARTBEAT_ACK");
i = 10;
break;
- case SCTP_CID_DATA:
- case SCTP_CID_SACK:
- pr_debug("SCTP_CID_DATA/SACK");
- i = 11;
- break;
default:
/* Other chunks like DATA or SACK do not change the state */
pr_debug("Unknown chunk type, Will stay in %s\n",
@@ -316,9 +296,7 @@ sctp_new(struct nf_conn *ct, const struct sk_buff *skb,
ih->init_tag);
ct->proto.sctp.vtag[IP_CT_DIR_REPLY] = ih->init_tag;
- } else if (sch->type == SCTP_CID_HEARTBEAT ||
- sch->type == SCTP_CID_DATA ||
- sch->type == SCTP_CID_SACK) {
+ } else if (sch->type == SCTP_CID_HEARTBEAT) {
pr_debug("Setting vtag %x for secondary conntrack\n",
sh->vtag);
ct->proto.sctp.vtag[IP_CT_DIR_ORIGINAL] = sh->vtag;
@@ -404,19 +382,19 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
if (!sctp_new(ct, skb, sh, dataoff))
return -NF_ACCEPT;
- } else {
- /* Check the verification tag (Sec 8.5) */
- if (!test_bit(SCTP_CID_INIT, map) &&
- !test_bit(SCTP_CID_SHUTDOWN_COMPLETE, map) &&
- !test_bit(SCTP_CID_COOKIE_ECHO, map) &&
- !test_bit(SCTP_CID_ABORT, map) &&
- !test_bit(SCTP_CID_SHUTDOWN_ACK, map) &&
- !test_bit(SCTP_CID_HEARTBEAT, map) &&
- !test_bit(SCTP_CID_HEARTBEAT_ACK, map) &&
- sh->vtag != ct->proto.sctp.vtag[dir]) {
- pr_debug("Verification tag check failed\n");
- goto out;
- }
+ }
+
+ /* Check the verification tag (Sec 8.5) */
+ if (!test_bit(SCTP_CID_INIT, map) &&
+ !test_bit(SCTP_CID_SHUTDOWN_COMPLETE, map) &&
+ !test_bit(SCTP_CID_COOKIE_ECHO, map) &&
+ !test_bit(SCTP_CID_ABORT, map) &&
+ !test_bit(SCTP_CID_SHUTDOWN_ACK, map) &&
+ !test_bit(SCTP_CID_HEARTBEAT, map) &&
+ !test_bit(SCTP_CID_HEARTBEAT_ACK, map) &&
+ sh->vtag != ct->proto.sctp.vtag[dir]) {
+ pr_debug("Verification tag check failed\n");
+ goto out;
}
old_state = new_state = SCTP_CONNTRACK_NONE;
@@ -424,22 +402,29 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
for_each_sctp_chunk (skb, sch, _sch, offset, dataoff, count) {
/* Special cases of Verification tag check (Sec 8.5.1) */
if (sch->type == SCTP_CID_INIT) {
- /* Sec 8.5.1 (A) */
+ /* (A) vtag MUST be zero */
if (sh->vtag != 0)
goto out_unlock;
} else if (sch->type == SCTP_CID_ABORT) {
- /* Sec 8.5.1 (B) */
- if (sh->vtag != ct->proto.sctp.vtag[dir] &&
- sh->vtag != ct->proto.sctp.vtag[!dir])
+ /* (B) vtag MUST match own vtag if T flag is unset OR
+ * MUST match peer's vtag if T flag is set
+ */
+ if ((!(sch->flags & SCTP_CHUNK_FLAG_T) &&
+ sh->vtag != ct->proto.sctp.vtag[dir]) ||
+ ((sch->flags & SCTP_CHUNK_FLAG_T) &&
+ sh->vtag != ct->proto.sctp.vtag[!dir]))
goto out_unlock;
} else if (sch->type == SCTP_CID_SHUTDOWN_COMPLETE) {
- /* Sec 8.5.1 (C) */
- if (sh->vtag != ct->proto.sctp.vtag[dir] &&
- sh->vtag != ct->proto.sctp.vtag[!dir] &&
- sch->flags & SCTP_CHUNK_FLAG_T)
+ /* (C) vtag MUST match own vtag if T flag is unset OR
+ * MUST match peer's vtag if T flag is set
+ */
+ if ((!(sch->flags & SCTP_CHUNK_FLAG_T) &&
+ sh->vtag != ct->proto.sctp.vtag[dir]) ||
+ ((sch->flags & SCTP_CHUNK_FLAG_T) &&
+ sh->vtag != ct->proto.sctp.vtag[!dir]))
goto out_unlock;
} else if (sch->type == SCTP_CID_COOKIE_ECHO) {
- /* Sec 8.5.1 (D) */
+ /* (D) vtag must be same as init_vtag as found in INIT_ACK */
if (sh->vtag != ct->proto.sctp.vtag[dir])
goto out_unlock;
} else if (sch->type == SCTP_CID_HEARTBEAT) {
@@ -476,11 +461,6 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
} else if (ct->proto.sctp.flags & SCTP_FLAG_HEARTBEAT_VTAG_FAILED) {
ct->proto.sctp.flags &= ~SCTP_FLAG_HEARTBEAT_VTAG_FAILED;
}
- } else if (sch->type == SCTP_CID_DATA || sch->type == SCTP_CID_SACK) {
- if (ct->proto.sctp.vtag[dir] == 0) {
- pr_debug("Setting vtag %x for dir %d\n", sh->vtag, dir);
- ct->proto.sctp.vtag[dir] = sh->vtag;
- }
}
old_state = ct->proto.sctp.state;
@@ -518,8 +498,12 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
}
ct->proto.sctp.state = new_state;
- if (old_state != new_state)
+ if (old_state != new_state) {
nf_conntrack_event_cache(IPCT_PROTOINFO, ct);
+ if (new_state == SCTP_CONNTRACK_ESTABLISHED &&
+ !test_and_set_bit(IPS_ASSURED_BIT, &ct->status))
+ nf_conntrack_event_cache(IPCT_ASSURED, ct);
+ }
}
spin_unlock_bh(&ct->lock);
@@ -533,14 +517,6 @@ int nf_conntrack_sctp_packet(struct nf_conn *ct,
nf_ct_refresh_acct(ct, ctinfo, skb, timeouts[new_state]);
- if (old_state == SCTP_CONNTRACK_COOKIE_ECHOED &&
- dir == IP_CT_DIR_REPLY &&
- new_state == SCTP_CONNTRACK_ESTABLISHED) {
- pr_debug("Setting assured bit\n");
- set_bit(IPS_ASSURED_BIT, &ct->status);
- nf_conntrack_event_cache(IPCT_ASSURED, ct);
- }
-
return NF_ACCEPT;
out_unlock:
@@ -701,7 +677,6 @@ sctp_timeout_nla_policy[CTA_TIMEOUT_SCTP_MAX+1] = {
[CTA_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT] = { .type = NLA_U32 },
[CTA_TIMEOUT_SCTP_HEARTBEAT_SENT] = { .type = NLA_U32 },
[CTA_TIMEOUT_SCTP_HEARTBEAT_ACKED] = { .type = NLA_U32 },
- [CTA_TIMEOUT_SCTP_DATA_SENT] = { .type = NLA_U32 },
};
#endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
index 6566310831779b..3ac1af6f59fcca 100644
--- a/net/netfilter/nf_conntrack_proto_tcp.c
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
@@ -1068,6 +1068,13 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
ct->proto.tcp.last_flags |=
IP_CT_EXP_CHALLENGE_ACK;
}
+
+ /* possible challenge ack reply to syn */
+ if (old_state == TCP_CONNTRACK_SYN_SENT &&
+ index == TCP_ACK_SET &&
+ dir == IP_CT_DIR_REPLY)
+ ct->proto.tcp.last_ack = ntohl(th->ack_seq);
+
spin_unlock_bh(&ct->lock);
nf_ct_l4proto_log_invalid(skb, ct, state,
"packet (index %d) in dir %d ignored, state %s",
@@ -1193,6 +1200,14 @@ int nf_conntrack_tcp_packet(struct nf_conn *ct,
* segments we ignored. */
goto in_window;
}
+
+ /* Reset in response to a challenge-ack we let through earlier */
+ if (old_state == TCP_CONNTRACK_SYN_SENT &&
+ ct->proto.tcp.last_index == TCP_ACK_SET &&
+ ct->proto.tcp.last_dir == IP_CT_DIR_REPLY &&
+ ntohl(th->seq) == ct->proto.tcp.last_ack)
+ goto in_window;
+
break;
default:
/* Keep compilers happy. */
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index 0250725e38a44d..460294bd4b6061 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -601,8 +601,6 @@ enum nf_ct_sysctl_index {
NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_RECD,
NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_SHUTDOWN_ACK_SENT,
NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_HEARTBEAT_SENT,
- NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_HEARTBEAT_ACKED,
- NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_DATA_SENT,
#endif
#ifdef CONFIG_NF_CT_PROTO_DCCP
NF_SYSCTL_CT_PROTO_TIMEOUT_DCCP_REQUEST,
@@ -887,18 +885,6 @@ static struct ctl_table nf_ct_sysctl_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec_jiffies,
},
- [NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_HEARTBEAT_ACKED] = {
- .procname = "nf_conntrack_sctp_timeout_heartbeat_acked",
- .maxlen = sizeof(unsigned int),
- .mode = 0644,
- .proc_handler = proc_dointvec_jiffies,
- },
- [NF_SYSCTL_CT_PROTO_TIMEOUT_SCTP_DATA_SENT] = {
- .procname = "nf_conntrack_sctp_timeout_data_sent",
- .maxlen = sizeof(unsigned int),
- .mode = 0644,
- .proc_handler = proc_dointvec_jiffies,
- },
#endif
#ifdef CONFIG_NF_CT_PROTO_DCCP
[NF_SYSCTL_CT_PROTO_TIMEOUT_DCCP_REQUEST] = {
@@ -1042,8 +1028,6 @@ static void nf_conntrack_standalone_init_sctp_sysctl(struct net *net,
XASSIGN(SHUTDOWN_RECD, sn);
XASSIGN(SHUTDOWN_ACK_SENT, sn);
XASSIGN(HEARTBEAT_SENT, sn);
- XASSIGN(HEARTBEAT_ACKED, sn);
- XASSIGN(DATA_SENT, sn);
#undef XASSIGN
#endif
}
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 832b881f7c1749..8c09e4d12ac1ee 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -465,8 +465,9 @@ static int nft_delrule_by_chain(struct nft_ctx *ctx)
return 0;
}
-static int nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
- struct nft_set *set)
+static int __nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
+ struct nft_set *set,
+ const struct nft_set_desc *desc)
{
struct nft_trans *trans;
@@ -474,17 +475,28 @@ static int nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
if (trans == NULL)
return -ENOMEM;
- if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] != NULL) {
+ if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] && !desc) {
nft_trans_set_id(trans) =
ntohl(nla_get_be32(ctx->nla[NFTA_SET_ID]));
nft_activate_next(ctx->net, set);
}
nft_trans_set(trans) = set;
+ if (desc) {
+ nft_trans_set_update(trans) = true;
+ nft_trans_set_gc_int(trans) = desc->gc_int;
+ nft_trans_set_timeout(trans) = desc->timeout;
+ }
nft_trans_commit_list_add_tail(ctx->net, trans);
return 0;
}
+static int nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
+ struct nft_set *set)
+{
+ return __nft_trans_set_add(ctx, msg_type, set, NULL);
+}
+
static int nft_delset(const struct nft_ctx *ctx, struct nft_set *set)
{
int err;
@@ -3780,8 +3792,7 @@ static bool nft_set_ops_candidate(const struct nft_set_type *type, u32 flags)
static const struct nft_set_ops *
nft_select_set_ops(const struct nft_ctx *ctx,
const struct nlattr * const nla[],
- const struct nft_set_desc *desc,
- enum nft_set_policies policy)
+ const struct nft_set_desc *desc)
{
struct nftables_pernet *nft_net = nft_pernet(ctx->net);
const struct nft_set_ops *ops, *bops;
@@ -3810,7 +3821,7 @@ nft_select_set_ops(const struct nft_ctx *ctx,
if (!ops->estimate(desc, flags, &est))
continue;
- switch (policy) {
+ switch (desc->policy) {
case NFT_SET_POL_PERFORMANCE:
if (est.lookup < best.lookup)
break;
@@ -4045,8 +4056,10 @@ static int nf_tables_fill_set_concat(struct sk_buff *skb,
static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
const struct nft_set *set, u16 event, u16 flags)
{
- struct nlmsghdr *nlh;
+ u64 timeout = READ_ONCE(set->timeout);
+ u32 gc_int = READ_ONCE(set->gc_int);
u32 portid = ctx->portid;
+ struct nlmsghdr *nlh;
struct nlattr *nest;
u32 seq = ctx->seq;
int i;
@@ -4082,13 +4095,13 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
nla_put_be32(skb, NFTA_SET_OBJ_TYPE, htonl(set->objtype)))
goto nla_put_failure;
- if (set->timeout &&
+ if (timeout &&
nla_put_be64(skb, NFTA_SET_TIMEOUT,
- nf_jiffies64_to_msecs(set->timeout),
+ nf_jiffies64_to_msecs(timeout),
NFTA_SET_PAD))
goto nla_put_failure;
- if (set->gc_int &&
- nla_put_be32(skb, NFTA_SET_GC_INTERVAL, htonl(set->gc_int)))
+ if (gc_int &&
+ nla_put_be32(skb, NFTA_SET_GC_INTERVAL, htonl(gc_int)))
goto nla_put_failure;
if (set->policy != NFT_SET_POL_PERFORMANCE) {
@@ -4389,15 +4402,94 @@ static int nf_tables_set_desc_parse(struct nft_set_desc *desc,
return err;
}
+static int nft_set_expr_alloc(struct nft_ctx *ctx, struct nft_set *set,
+ const struct nlattr * const *nla,
+ struct nft_expr **exprs, int *num_exprs,
+ u32 flags)
+{
+ struct nft_expr *expr;
+ int err, i;
+
+ if (nla[NFTA_SET_EXPR]) {
+ expr = nft_set_elem_expr_alloc(ctx, set, nla[NFTA_SET_EXPR]);
+ if (IS_ERR(expr)) {
+ err = PTR_ERR(expr);
+ goto err_set_expr_alloc;
+ }
+ exprs[0] = expr;
+ (*num_exprs)++;
+ } else if (nla[NFTA_SET_EXPRESSIONS]) {
+ struct nlattr *tmp;
+ int left;
+
+ if (!(flags & NFT_SET_EXPR)) {
+ err = -EINVAL;
+ goto err_set_expr_alloc;
+ }
+ i = 0;
+ nla_for_each_nested(tmp, nla[NFTA_SET_EXPRESSIONS], left) {
+ if (i == NFT_SET_EXPR_MAX) {
+ err = -E2BIG;
+ goto err_set_expr_alloc;
+ }
+ if (nla_type(tmp) != NFTA_LIST_ELEM) {
+ err = -EINVAL;
+ goto err_set_expr_alloc;
+ }
+ expr = nft_set_elem_expr_alloc(ctx, set, tmp);
+ if (IS_ERR(expr)) {
+ err = PTR_ERR(expr);
+ goto err_set_expr_alloc;
+ }
+ exprs[i++] = expr;
+ (*num_exprs)++;
+ }
+ }
+
+ return 0;
+
+err_set_expr_alloc:
+ for (i = 0; i < *num_exprs; i++)
+ nft_expr_destroy(ctx, exprs[i]);
+
+ return err;
+}
+
+static bool nft_set_is_same(const struct nft_set *set,
+ const struct nft_set_desc *desc,
+ struct nft_expr *exprs[], u32 num_exprs, u32 flags)
+{
+ int i;
+
+ if (set->ktype != desc->ktype ||
+ set->dtype != desc->dtype ||
+ set->flags != flags ||
+ set->klen != desc->klen ||
+ set->dlen != desc->dlen ||
+ set->field_count != desc->field_count ||
+ set->num_exprs != num_exprs)
+ return false;
+
+ for (i = 0; i < desc->field_count; i++) {
+ if (set->field_len[i] != desc->field_len[i])
+ return false;
+ }
+
+ for (i = 0; i < num_exprs; i++) {
+ if (set->exprs[i]->ops != exprs[i]->ops)
+ return false;
+ }
+
+ return true;
+}
+
static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
const struct nlattr * const nla[])
{
- u32 ktype, dtype, flags, policy, gc_int, objtype;
struct netlink_ext_ack *extack = info->extack;
u8 genmask = nft_genmask_next(info->net);
u8 family = info->nfmsg->nfgen_family;
const struct nft_set_ops *ops;
- struct nft_expr *expr = NULL;
struct net *net = info->net;
struct nft_set_desc desc;
struct nft_table *table;
@@ -4405,10 +4497,11 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
struct nft_set *set;
struct nft_ctx ctx;
size_t alloc_size;
- u64 timeout;
+ int num_exprs = 0;
char *name;
int err, i;
u16 udlen;
+ u32 flags;
u64 size;
if (nla[NFTA_SET_TABLE] == NULL ||
@@ -4419,10 +4512,10 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
memset(&desc, 0, sizeof(desc));
- ktype = NFT_DATA_VALUE;
+ desc.ktype = NFT_DATA_VALUE;
if (nla[NFTA_SET_KEY_TYPE] != NULL) {
- ktype = ntohl(nla_get_be32(nla[NFTA_SET_KEY_TYPE]));
- if ((ktype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK)
+ desc.ktype = ntohl(nla_get_be32(nla[NFTA_SET_KEY_TYPE]));
+ if ((desc.ktype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK)
return -EINVAL;
}
@@ -4447,17 +4540,17 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
return -EOPNOTSUPP;
}
- dtype = 0;
+ desc.dtype = 0;
if (nla[NFTA_SET_DATA_TYPE] != NULL) {
if (!(flags & NFT_SET_MAP))
return -EINVAL;
- dtype = ntohl(nla_get_be32(nla[NFTA_SET_DATA_TYPE]));
- if ((dtype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK &&
- dtype != NFT_DATA_VERDICT)
+ desc.dtype = ntohl(nla_get_be32(nla[NFTA_SET_DATA_TYPE]));
+ if ((desc.dtype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK &&
+ desc.dtype != NFT_DATA_VERDICT)
return -EINVAL;
- if (dtype != NFT_DATA_VERDICT) {
+ if (desc.dtype != NFT_DATA_VERDICT) {
if (nla[NFTA_SET_DATA_LEN] == NULL)
return -EINVAL;
desc.dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN]));
@@ -4472,34 +4565,34 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
if (!(flags & NFT_SET_OBJECT))
return -EINVAL;
- objtype = ntohl(nla_get_be32(nla[NFTA_SET_OBJ_TYPE]));
- if (objtype == NFT_OBJECT_UNSPEC ||
- objtype > NFT_OBJECT_MAX)
+ desc.objtype = ntohl(nla_get_be32(nla[NFTA_SET_OBJ_TYPE]));
+ if (desc.objtype == NFT_OBJECT_UNSPEC ||
+ desc.objtype > NFT_OBJECT_MAX)
return -EOPNOTSUPP;
} else if (flags & NFT_SET_OBJECT)
return -EINVAL;
else
- objtype = NFT_OBJECT_UNSPEC;
+ desc.objtype = NFT_OBJECT_UNSPEC;
- timeout = 0;
+ desc.timeout = 0;
if (nla[NFTA_SET_TIMEOUT] != NULL) {
if (!(flags & NFT_SET_TIMEOUT))
return -EINVAL;
- err = nf_msecs_to_jiffies64(nla[NFTA_SET_TIMEOUT], &timeout);
+ err = nf_msecs_to_jiffies64(nla[NFTA_SET_TIMEOUT], &desc.timeout);
if (err)
return err;
}
- gc_int = 0;
+ desc.gc_int = 0;
if (nla[NFTA_SET_GC_INTERVAL] != NULL) {
if (!(flags & NFT_SET_TIMEOUT))
return -EINVAL;
- gc_int = ntohl(nla_get_be32(nla[NFTA_SET_GC_INTERVAL]));
+ desc.gc_int = ntohl(nla_get_be32(nla[NFTA_SET_GC_INTERVAL]));
}
- policy = NFT_SET_POL_PERFORMANCE;
+ desc.policy = NFT_SET_POL_PERFORMANCE;
if (nla[NFTA_SET_POLICY] != NULL)
- policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY]));
+ desc.policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY]));
if (nla[NFTA_SET_DESC] != NULL) {
err = nf_tables_set_desc_parse(&desc, nla[NFTA_SET_DESC]);
@@ -4531,6 +4624,8 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
return PTR_ERR(set);
}
} else {
+ struct nft_expr *exprs[NFT_SET_EXPR_MAX] = {};
+
if (info->nlh->nlmsg_flags & NLM_F_EXCL) {
NL_SET_BAD_ATTR(extack, nla[NFTA_SET_NAME]);
return -EEXIST;
@@ -4538,13 +4633,29 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
if (info->nlh->nlmsg_flags & NLM_F_REPLACE)
return -EOPNOTSUPP;
- return 0;
+ err = nft_set_expr_alloc(&ctx, set, nla, exprs, &num_exprs, flags);
+ if (err < 0)
+ return err;
+
+ err = 0;
+ if (!nft_set_is_same(set, &desc, exprs, num_exprs, flags)) {
+ NL_SET_BAD_ATTR(extack, nla[NFTA_SET_NAME]);
+ err = -EEXIST;
+ }
+
+ for (i = 0; i < num_exprs; i++)
+ nft_expr_destroy(&ctx, exprs[i]);
+
+ if (err < 0)
+ return err;
+
+ return __nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set, &desc);
}
if (!(info->nlh->nlmsg_flags & NLM_F_CREATE))
return -ENOENT;
- ops = nft_select_set_ops(&ctx, nla, &desc, policy);
+ ops = nft_select_set_ops(&ctx, nla, &desc);
if (IS_ERR(ops))
return PTR_ERR(ops);
@@ -4584,18 +4695,18 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
set->table = table;
write_pnet(&set->net, net);
set->ops = ops;
- set->ktype = ktype;
+ set->ktype = desc.ktype;
set->klen = desc.klen;
- set->dtype = dtype;
- set->objtype = objtype;
+ set->dtype = desc.dtype;
+ set->objtype = desc.objtype;
set->dlen = desc.dlen;
set->flags = flags;
set->size = desc.size;
- set->policy = policy;
+ set->policy = desc.policy;
set->udlen = udlen;
set->udata = udata;
- set->timeout = timeout;
- set->gc_int = gc_int;
+ set->timeout = desc.timeout;
+ set->gc_int = desc.gc_int;
set->field_count = desc.field_count;
for (i = 0; i < desc.field_count; i++)
@@ -4605,43 +4716,11 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
if (err < 0)
goto err_set_init;
- if (nla[NFTA_SET_EXPR]) {
- expr = nft_set_elem_expr_alloc(&ctx, set, nla[NFTA_SET_EXPR]);
- if (IS_ERR(expr)) {
- err = PTR_ERR(expr);
- goto err_set_expr_alloc;
- }
- set->exprs[0] = expr;
- set->num_exprs++;
- } else if (nla[NFTA_SET_EXPRESSIONS]) {
- struct nft_expr *expr;
- struct nlattr *tmp;
- int left;
-
- if (!(flags & NFT_SET_EXPR)) {
- err = -EINVAL;
- goto err_set_expr_alloc;
- }
- i = 0;
- nla_for_each_nested(tmp, nla[NFTA_SET_EXPRESSIONS], left) {
- if (i == NFT_SET_EXPR_MAX) {
- err = -E2BIG;
- goto err_set_expr_alloc;
- }
- if (nla_type(tmp) != NFTA_LIST_ELEM) {
- err = -EINVAL;
- goto err_set_expr_alloc;
- }
- expr = nft_set_elem_expr_alloc(&ctx, set, tmp);
- if (IS_ERR(expr)) {
- err = PTR_ERR(expr);
- goto err_set_expr_alloc;
- }
- set->exprs[i++] = expr;
- set->num_exprs++;
- }
- }
+ err = nft_set_expr_alloc(&ctx, set, nla, set->exprs, &num_exprs, flags);
+ if (err < 0)
+ goto err_set_destroy;
+ set->num_exprs = num_exprs;
set->handle = nf_tables_alloc_handle(table);
err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
@@ -4655,7 +4734,7 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
err_set_expr_alloc:
for (i = 0; i < set->num_exprs; i++)
nft_expr_destroy(&ctx, set->exprs[i]);
-
+err_set_destroy:
ops->destroy(set);
err_set_init:
kfree(set->name);
@@ -6008,7 +6087,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
return err;
} else if (set->flags & NFT_SET_TIMEOUT &&
!(flags & NFT_SET_ELEM_INTERVAL_END)) {
- timeout = set->timeout;
+ timeout = READ_ONCE(set->timeout);
}
expiration = 0;
@@ -6109,7 +6188,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
if (err < 0)
goto err_parse_key_end;
- if (timeout != set->timeout) {
+ if (timeout != READ_ONCE(set->timeout)) {
err = nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT);
if (err < 0)
goto err_parse_key_end;
@@ -9031,14 +9110,20 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
nft_flow_rule_destroy(nft_trans_flow_rule(trans));
break;
case NFT_MSG_NEWSET:
- nft_clear(net, nft_trans_set(trans));
- /* This avoids hitting -EBUSY when deleting the table
- * from the transaction.
- */
- if (nft_set_is_anonymous(nft_trans_set(trans)) &&
- !list_empty(&nft_trans_set(trans)->bindings))
- trans->ctx.table->use--;
+ if (nft_trans_set_update(trans)) {
+ struct nft_set *set = nft_trans_set(trans);
+ WRITE_ONCE(set->timeout, nft_trans_set_timeout(trans));
+ WRITE_ONCE(set->gc_int, nft_trans_set_gc_int(trans));
+ } else {
+ nft_clear(net, nft_trans_set(trans));
+ /* This avoids hitting -EBUSY when deleting the table
+ * from the transaction.
+ */
+ if (nft_set_is_anonymous(nft_trans_set(trans)) &&
+ !list_empty(&nft_trans_set(trans)->bindings))
+ trans->ctx.table->use--;
+ }
nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
NFT_MSG_NEWSET, GFP_KERNEL);
nft_trans_destroy(trans);
@@ -9260,6 +9345,10 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
nft_trans_destroy(trans);
break;
case NFT_MSG_NEWSET:
+ if (nft_trans_set_update(trans)) {
+ nft_trans_destroy(trans);
+ break;
+ }
trans->ctx.table->use--;
if (nft_trans_set_bound(trans)) {
nft_trans_destroy(trans);
diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c
index 17b418a5a593af..3a3c7746e88fe6 100644
--- a/net/netfilter/nft_payload.c
+++ b/net/netfilter/nft_payload.c
@@ -63,7 +63,7 @@ nft_payload_copy_vlan(u32 *d, const struct sk_buff *skb, u8 offset, u8 len)
return false;
if (offset + len > VLAN_ETH_HLEN + vlan_hlen)
- ethlen -= offset + len - VLAN_ETH_HLEN + vlan_hlen;
+ ethlen -= offset + len - VLAN_ETH_HLEN - vlan_hlen;
memcpy(dst_u8, vlanh + offset - vlan_hlen, ethlen);
diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c
index 7325bee7d14425..19ea4d3c355355 100644
--- a/net/netfilter/nft_set_rbtree.c
+++ b/net/netfilter/nft_set_rbtree.c
@@ -38,10 +38,12 @@ static bool nft_rbtree_interval_start(const struct nft_rbtree_elem *rbe)
return !nft_rbtree_interval_end(rbe);
}
-static bool nft_rbtree_equal(const struct nft_set *set, const void *this,
- const struct nft_rbtree_elem *interval)
+static int nft_rbtree_cmp(const struct nft_set *set,
+ const struct nft_rbtree_elem *e1,
+ const struct nft_rbtree_elem *e2)
{
- return memcmp(this, nft_set_ext_key(&interval->ext), set->klen) == 0;
+ return memcmp(nft_set_ext_key(&e1->ext), nft_set_ext_key(&e2->ext),
+ set->klen);
}
static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set,
@@ -52,7 +54,6 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set
const struct nft_rbtree_elem *rbe, *interval = NULL;
u8 genmask = nft_genmask_cur(net);
const struct rb_node *parent;
- const void *this;
int d;
parent = rcu_dereference_raw(priv->root.rb_node);
@@ -62,12 +63,11 @@ static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set
rbe = rb_entry(parent, struct nft_rbtree_elem, node);
- this = nft_set_ext_key(&rbe->ext);
- d = memcmp(this, key, set->klen);
+ d = memcmp(nft_set_ext_key(&rbe->ext), key, set->klen);
if (d < 0) {
parent = rcu_dereference_raw(parent->rb_left);
if (interval &&
- nft_rbtree_equal(set, this, interval) &&
+ !nft_rbtree_cmp(set, rbe, interval) &&
nft_rbtree_interval_end(rbe) &&
nft_rbtree_interval_start(interval))
continue;
@@ -215,154 +215,216 @@ static void *nft_rbtree_get(const struct net *net, const struct nft_set *set,
return rbe;
}
+static int nft_rbtree_gc_elem(const struct nft_set *__set,
+ struct nft_rbtree *priv,
+ struct nft_rbtree_elem *rbe)
+{
+ struct nft_set *set = (struct nft_set *)__set;
+ struct rb_node *prev = rb_prev(&rbe->node);
+ struct nft_rbtree_elem *rbe_prev;
+ struct nft_set_gc_batch *gcb;
+
+ gcb = nft_set_gc_batch_check(set, NULL, GFP_ATOMIC);
+ if (!gcb)
+ return -ENOMEM;
+
+ /* search for expired end interval coming before this element. */
+ do {
+ rbe_prev = rb_entry(prev, struct nft_rbtree_elem, node);
+ if (nft_rbtree_interval_end(rbe_prev))
+ break;
+
+ prev = rb_prev(prev);
+ } while (prev != NULL);
+
+ rb_erase(&rbe_prev->node, &priv->root);
+ rb_erase(&rbe->node, &priv->root);
+ atomic_sub(2, &set->nelems);
+
+ nft_set_gc_batch_add(gcb, rbe);
+ nft_set_gc_batch_complete(gcb);
+
+ return 0;
+}
+
+static bool nft_rbtree_update_first(const struct nft_set *set,
+ struct nft_rbtree_elem *rbe,
+ struct rb_node *first)
+{
+ struct nft_rbtree_elem *first_elem;
+
+ first_elem = rb_entry(first, struct nft_rbtree_elem, node);
+ /* this element is closest to where the new element is to be inserted:
+ * update the first element for the node list path.
+ */
+ if (nft_rbtree_cmp(set, rbe, first_elem) < 0)
+ return true;
+
+ return false;
+}
+
static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set,
struct nft_rbtree_elem *new,
struct nft_set_ext **ext)
{
- bool overlap = false, dup_end_left = false, dup_end_right = false;
+ struct nft_rbtree_elem *rbe, *rbe_le = NULL, *rbe_ge = NULL;
+ struct rb_node *node, *parent, **p, *first = NULL;
struct nft_rbtree *priv = nft_set_priv(set);
u8 genmask = nft_genmask_next(net);
- struct nft_rbtree_elem *rbe;
- struct rb_node *parent, **p;
- int d;
+ int d, err;
- /* Detect overlaps as we descend the tree. Set the flag in these cases:
- *
- * a1. _ _ __>| ?_ _ __| (insert end before existing end)
- * a2. _ _ ___| ?_ _ _>| (insert end after existing end)
- * a3. _ _ ___? >|_ _ __| (insert start before existing end)
- *
- * and clear it later on, as we eventually reach the points indicated by
- * '?' above, in the cases described below. We'll always meet these
- * later, locally, due to tree ordering, and overlaps for the intervals
- * that are the closest together are always evaluated last.
- *
- * b1. _ _ __>| !_ _ __| (insert end before existing start)
- * b2. _ _ ___| !_ _ _>| (insert end after existing start)
- * b3. _ _ ___! >|_ _ __| (insert start after existing end, as a leaf)
- * '--' no nodes falling in this range
- * b4. >|_ _ ! (insert start before existing start)
- *
- * Case a3. resolves to b3.:
- * - if the inserted start element is the leftmost, because the '0'
- * element in the tree serves as end element
- * - otherwise, if an existing end is found immediately to the left. If
- * there are existing nodes in between, we need to further descend the
- * tree before we can conclude the new start isn't causing an overlap
- *
- * or to b4., which, preceded by a3., means we already traversed one or
- * more existing intervals entirely, from the right.
- *
- * For a new, rightmost pair of elements, we'll hit cases b3. and b2.,
- * in that order.
- *
- * The flag is also cleared in two special cases:
- *
- * b5. |__ _ _!|<_ _ _ (insert start right before existing end)
- * b6. |__ _ >|!__ _ _ (insert end right after existing start)
- *
- * which always happen as last step and imply that no further
- * overlapping is possible.
- *
- * Another special case comes from the fact that start elements matching
- * an already existing start element are allowed: insertion is not
- * performed but we return -EEXIST in that case, and the error will be
- * cleared by the caller if NLM_F_EXCL is not present in the request.
- * This way, request for insertion of an exact overlap isn't reported as
- * error to userspace if not desired.
- *
- * However, if the existing start matches a pre-existing start, but the
- * end element doesn't match the corresponding pre-existing end element,
- * we need to report a partial overlap. This is a local condition that
- * can be noticed without need for a tracking flag, by checking for a
- * local duplicated end for a corresponding start, from left and right,
- * separately.
+ /* Descend the tree to search for an existing element greater than the
+ * key value to insert that is greater than the new element. This is the
+ * first element to walk the ordered elements to find possible overlap.
*/
-
parent = NULL;
p = &priv->root.rb_node;
while (*p != NULL) {
parent = *p;
rbe = rb_entry(parent, struct nft_rbtree_elem, node);
- d = memcmp(nft_set_ext_key(&rbe->ext),
- nft_set_ext_key(&new->ext),
- set->klen);
+ d = nft_rbtree_cmp(set, rbe, new);
+
if (d < 0) {
p = &parent->rb_left;
-
- if (nft_rbtree_interval_start(new)) {
- if (nft_rbtree_interval_end(rbe) &&
- nft_set_elem_active(&rbe->ext, genmask) &&
- !nft_set_elem_expired(&rbe->ext) && !*p)
- overlap = false;
- } else {
- if (dup_end_left && !*p)
- return -ENOTEMPTY;
-
- overlap = nft_rbtree_interval_end(rbe) &&
- nft_set_elem_active(&rbe->ext,
- genmask) &&
- !nft_set_elem_expired(&rbe->ext);
-
- if (overlap) {
- dup_end_right = true;
- continue;
- }
- }
} else if (d > 0) {
- p = &parent->rb_right;
+ if (!first ||
+ nft_rbtree_update_first(set, rbe, first))
+ first = &rbe->node;
- if (nft_rbtree_interval_end(new)) {
- if (dup_end_right && !*p)
- return -ENOTEMPTY;
-
- overlap = nft_rbtree_interval_end(rbe) &&
- nft_set_elem_active(&rbe->ext,
- genmask) &&
- !nft_set_elem_expired(&rbe->ext);
-
- if (overlap) {
- dup_end_left = true;
- continue;
- }
- } else if (nft_set_elem_active(&rbe->ext, genmask) &&
- !nft_set_elem_expired(&rbe->ext)) {
- overlap = nft_rbtree_interval_end(rbe);
- }
+ p = &parent->rb_right;
} else {
- if (nft_rbtree_interval_end(rbe) &&
- nft_rbtree_interval_start(new)) {
+ if (nft_rbtree_interval_end(rbe))
p = &parent->rb_left;
-
- if (nft_set_elem_active(&rbe->ext, genmask) &&
- !nft_set_elem_expired(&rbe->ext))
- overlap = false;
- } else if (nft_rbtree_interval_start(rbe) &&
- nft_rbtree_interval_end(new)) {
+ else
p = &parent->rb_right;
+ }
+ }
+
+ if (!first)
+ first = rb_first(&priv->root);
+
+ /* Detect overlap by going through the list of valid tree nodes.
+ * Values stored in the tree are in reversed order, starting from
+ * highest to lowest value.
+ */
+ for (node = first; node != NULL; node = rb_next(node)) {
+ rbe = rb_entry(node, struct nft_rbtree_elem, node);
- if (nft_set_elem_active(&rbe->ext, genmask) &&
- !nft_set_elem_expired(&rbe->ext))
- overlap = false;
- } else if (nft_set_elem_active(&rbe->ext, genmask) &&
- !nft_set_elem_expired(&rbe->ext)) {
- *ext = &rbe->ext;
- return -EEXIST;
- } else {
- overlap = false;
- if (nft_rbtree_interval_end(rbe))
- p = &parent->rb_left;
- else
- p = &parent->rb_right;
+ if (!nft_set_elem_active(&rbe->ext, genmask))
+ continue;
+
+ /* perform garbage collection to avoid bogus overlap reports. */
+ if (nft_set_elem_expired(&rbe->ext)) {
+ err = nft_rbtree_gc_elem(set, priv, rbe);
+ if (err < 0)
+ return err;
+
+ continue;
+ }
+
+ d = nft_rbtree_cmp(set, rbe, new);
+ if (d == 0) {
+ /* Matching end element: no need to look for an
+ * overlapping greater or equal element.
+ */
+ if (nft_rbtree_interval_end(rbe)) {
+ rbe_le = rbe;
+ break;
+ }
+
+ /* first element that is greater or equal to key value. */
+ if (!rbe_ge) {
+ rbe_ge = rbe;
+ continue;
+ }
+
+ /* this is a closer more or equal element, update it. */
+ if (nft_rbtree_cmp(set, rbe_ge, new) != 0) {
+ rbe_ge = rbe;
+ continue;
+ }
+
+ /* element is equal to key value, make sure flags are
+ * the same, an existing more or equal start element
+ * must not be replaced by more or equal end element.
+ */
+ if ((nft_rbtree_interval_start(new) &&
+ nft_rbtree_interval_start(rbe_ge)) ||
+ (nft_rbtree_interval_end(new) &&
+ nft_rbtree_interval_end(rbe_ge))) {
+ rbe_ge = rbe;
+ continue;
}
+ } else if (d > 0) {
+ /* annotate element greater than the new element. */
+ rbe_ge = rbe;
+ continue;
+ } else if (d < 0) {
+ /* annotate element less than the new element. */
+ rbe_le = rbe;
+ break;
}
+ }
- dup_end_left = dup_end_right = false;
+ /* - new start element matching existing start element: full overlap
+ * reported as -EEXIST, cleared by caller if NLM_F_EXCL is not given.
+ */
+ if (rbe_ge && !nft_rbtree_cmp(set, new, rbe_ge) &&
+ nft_rbtree_interval_start(rbe_ge) == nft_rbtree_interval_start(new)) {
+ *ext = &rbe_ge->ext;
+ return -EEXIST;
}
- if (overlap)
+ /* - new end element matching existing end element: full overlap
+ * reported as -EEXIST, cleared by caller if NLM_F_EXCL is not given.
+ */
+ if (rbe_le && !nft_rbtree_cmp(set, new, rbe_le) &&
+ nft_rbtree_interval_end(rbe_le) == nft_rbtree_interval_end(new)) {
+ *ext = &rbe_le->ext;
+ return -EEXIST;
+ }
+
+ /* - new start element with existing closest, less or equal key value
+ * being a start element: partial overlap, reported as -ENOTEMPTY.
+ * Anonymous sets allow for two consecutive start element since they
+ * are constant, skip them to avoid bogus overlap reports.
+ */
+ if (!nft_set_is_anonymous(set) && rbe_le &&
+ nft_rbtree_interval_start(rbe_le) && nft_rbtree_interval_start(new))
+ return -ENOTEMPTY;
+
+ /* - new end element with existing closest, less or equal key value
+ * being a end element: partial overlap, reported as -ENOTEMPTY.
+ */
+ if (rbe_le &&
+ nft_rbtree_interval_end(rbe_le) && nft_rbtree_interval_end(new))
return -ENOTEMPTY;
+ /* - new end element with existing closest, greater or equal key value
+ * being an end element: partial overlap, reported as -ENOTEMPTY
+ */
+ if (rbe_ge &&
+ nft_rbtree_interval_end(rbe_ge) && nft_rbtree_interval_end(new))
+ return -ENOTEMPTY;
+
+ /* Accepted element: pick insertion point depending on key value */
+ parent = NULL;
+ p = &priv->root.rb_node;
+ while (*p != NULL) {
+ parent = *p;
+ rbe = rb_entry(parent, struct nft_rbtree_elem, node);
+ d = nft_rbtree_cmp(set, rbe, new);
+
+ if (d < 0)
+ p = &parent->rb_left;
+ else if (d > 0)
+ p = &parent->rb_right;
+ else if (nft_rbtree_interval_end(rbe))
+ p = &parent->rb_left;
+ else
+ p = &parent->rb_right;
+ }
+
rb_link_node_rcu(&new->node, parent, p);
rb_insert_color(&new->node, &priv->root);
return 0;
@@ -501,23 +563,37 @@ static void nft_rbtree_gc(struct work_struct *work)
struct nft_rbtree *priv;
struct rb_node *node;
struct nft_set *set;
+ struct net *net;
+ u8 genmask;
priv = container_of(work, struct nft_rbtree, gc_work.work);
set = nft_set_container_of(priv);
+ net = read_pnet(&set->net);
+ genmask = nft_genmask_cur(net);
write_lock_bh(&priv->lock);
write_seqcount_begin(&priv->count);
for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) {
rbe = rb_entry(node, struct nft_rbtree_elem, node);
+ if (!nft_set_elem_active(&rbe->ext, genmask))
+ continue;
+
+ /* elements are reversed in the rbtree for historical reasons,
+ * from highest to lowest value, that is why end element is
+ * always visited before the start element.
+ */
if (nft_rbtree_interval_end(rbe)) {
rbe_end = rbe;
continue;
}
if (!nft_set_elem_expired(&rbe->ext))
continue;
- if (nft_set_elem_mark_busy(&rbe->ext))
+
+ if (nft_set_elem_mark_busy(&rbe->ext)) {
+ rbe_end = NULL;
continue;
+ }
if (rbe_prev) {
rb_erase(&rbe_prev->node, &priv->root);
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index bca2a470ccad5f..c6427765975318 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -580,7 +580,9 @@ static int netlink_insert(struct sock *sk, u32 portid)
if (nlk_sk(sk)->bound)
goto err;
- nlk_sk(sk)->portid = portid;
+ /* portid can be read locklessly from netlink_getname(). */
+ WRITE_ONCE(nlk_sk(sk)->portid, portid);
+
sock_hold(sk);
err = __netlink_insert(table, sk);
@@ -1096,9 +1098,11 @@ static int netlink_connect(struct socket *sock, struct sockaddr *addr,
return -EINVAL;
if (addr->sa_family == AF_UNSPEC) {
- sk->sk_state = NETLINK_UNCONNECTED;
- nlk->dst_portid = 0;
- nlk->dst_group = 0;
+ /* paired with READ_ONCE() in netlink_getsockbyportid() */
+ WRITE_ONCE(sk->sk_state, NETLINK_UNCONNECTED);
+ /* dst_portid and dst_group can be read locklessly */
+ WRITE_ONCE(nlk->dst_portid, 0);
+ WRITE_ONCE(nlk->dst_group, 0);
return 0;
}
if (addr->sa_family != AF_NETLINK)
@@ -1119,9 +1123,11 @@ static int netlink_connect(struct socket *sock, struct sockaddr *addr,
err = netlink_autobind(sock);
if (err == 0) {
- sk->sk_state = NETLINK_CONNECTED;
- nlk->dst_portid = nladdr->nl_pid;
- nlk->dst_group = ffs(nladdr->nl_groups);
+ /* paired with READ_ONCE() in netlink_getsockbyportid() */
+ WRITE_ONCE(sk->sk_state, NETLINK_CONNECTED);
+ /* dst_portid and dst_group can be read locklessly */
+ WRITE_ONCE(nlk->dst_portid, nladdr->nl_pid);
+ WRITE_ONCE(nlk->dst_group, ffs(nladdr->nl_groups));
}
return err;
@@ -1138,10 +1144,12 @@ static int netlink_getname(struct socket *sock, struct sockaddr *addr,
nladdr->nl_pad = 0;
if (peer) {
- nladdr->nl_pid = nlk->dst_portid;
- nladdr->nl_groups = netlink_group_mask(nlk->dst_group);
+ /* Paired with WRITE_ONCE() in netlink_connect() */
+ nladdr->nl_pid = READ_ONCE(nlk->dst_portid);
+ nladdr->nl_groups = netlink_group_mask(READ_ONCE(nlk->dst_group));
} else {
- nladdr->nl_pid = nlk->portid;
+ /* Paired with WRITE_ONCE() in netlink_insert() */
+ nladdr->nl_pid = READ_ONCE(nlk->portid);
netlink_lock_table();
nladdr->nl_groups = nlk->groups ? nlk->groups[0] : 0;
netlink_unlock_table();
@@ -1168,8 +1176,9 @@ static struct sock *netlink_getsockbyportid(struct sock *ssk, u32 portid)
/* Don't bother queuing skb if kernel socket has no input function */
nlk = nlk_sk(sock);
- if (sock->sk_state == NETLINK_CONNECTED &&
- nlk->dst_portid != nlk_sk(ssk)->portid) {
+ /* dst_portid and sk_state can be changed in netlink_connect() */
+ if (READ_ONCE(sock->sk_state) == NETLINK_CONNECTED &&
+ READ_ONCE(nlk->dst_portid) != nlk_sk(ssk)->portid) {
sock_put(sock);
return ERR_PTR(-ECONNREFUSED);
}
@@ -1886,8 +1895,9 @@ static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
goto out;
netlink_skb_flags |= NETLINK_SKB_DST;
} else {
- dst_portid = nlk->dst_portid;
- dst_group = nlk->dst_group;
+ /* Paired with WRITE_ONCE() in netlink_connect() */
+ dst_portid = READ_ONCE(nlk->dst_portid);
+ dst_group = READ_ONCE(nlk->dst_group);
}
/* Paired with WRITE_ONCE() in netlink_insert() */
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index 6f7f4392cffb1b..5a4cb796150f5a 100644
--- a/net/netrom/af_netrom.c
+++ b/net/netrom/af_netrom.c
@@ -400,6 +400,11 @@ static int nr_listen(struct socket *sock, int backlog)
struct sock *sk = sock->sk;
lock_sock(sk);
+ if (sock->state != SS_UNCONNECTED) {
+ release_sock(sk);
+ return -EINVAL;
+ }
+
if (sk->sk_state != TCP_LISTEN) {
memset(&nr_sk(sk)->user_addr, 0, AX25_ADDR_LEN);
sk->sk_max_ack_backlog = backlog;
diff --git a/net/netrom/nr_timer.c b/net/netrom/nr_timer.c
index a8da88db7893fc..4e7c968cde2dcf 100644
--- a/net/netrom/nr_timer.c
+++ b/net/netrom/nr_timer.c
@@ -121,6 +121,7 @@ static void nr_heartbeat_expiry(struct timer_list *t)
is accepted() it isn't 'dead' so doesn't get removed. */
if (sock_flag(sk, SOCK_DESTROY) ||
(sk->sk_state == TCP_LISTEN && sock_flag(sk, SOCK_DEAD))) {
+ sock_hold(sk);
bh_unlock_sock(sk);
nr_destroy_socket(sk);
goto out;
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index 3364caabef8b19..a27e1842b2a096 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -157,6 +157,7 @@ static void local_cleanup(struct nfc_llcp_local *local)
cancel_work_sync(&local->rx_work);
cancel_work_sync(&local->timeout_work);
kfree_skb(local->rx_pending);
+ local->rx_pending = NULL;
del_timer_sync(&local->sdreq_timer);
cancel_work_sync(&local->sdreq_timeout_work);
nfc_llcp_free_sdp_tlv_list(&local->pending_sdreqs);
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index 9d91087b93992b..1fc339084d897b 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -1497,6 +1497,7 @@ static int nfc_genl_se_io(struct sk_buff *skb, struct genl_info *info)
u32 dev_idx, se_idx;
u8 *apdu;
size_t apdu_len;
+ int rc;
if (!info->attrs[NFC_ATTR_DEVICE_INDEX] ||
!info->attrs[NFC_ATTR_SE_INDEX] ||
@@ -1510,25 +1511,37 @@ static int nfc_genl_se_io(struct sk_buff *skb, struct genl_info *info)
if (!dev)
return -ENODEV;
- if (!dev->ops || !dev->ops->se_io)
- return -ENOTSUPP;
+ if (!dev->ops || !dev->ops->se_io) {
+ rc = -EOPNOTSUPP;
+ goto put_dev;
+ }
apdu_len = nla_len(info->attrs[NFC_ATTR_SE_APDU]);
- if (apdu_len == 0)
- return -EINVAL;
+ if (apdu_len == 0) {
+ rc = -EINVAL;
+ goto put_dev;
+ }
apdu = nla_data(info->attrs[NFC_ATTR_SE_APDU]);
- if (!apdu)
- return -EINVAL;
+ if (!apdu) {
+ rc = -EINVAL;
+ goto put_dev;
+ }
ctx = kzalloc(sizeof(struct se_io_ctx), GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
+ if (!ctx) {
+ rc = -ENOMEM;
+ goto put_dev;
+ }
ctx->dev_idx = dev_idx;
ctx->se_idx = se_idx;
- return nfc_se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
+ rc = nfc_se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
+
+put_dev:
+ nfc_put_device(dev);
+ return rc;
}
static int nfc_genl_vendor_cmd(struct sk_buff *skb,
@@ -1551,14 +1564,21 @@ static int nfc_genl_vendor_cmd(struct sk_buff *skb,
subcmd = nla_get_u32(info->attrs[NFC_ATTR_VENDOR_SUBCMD]);
dev = nfc_get_device(dev_idx);
- if (!dev || !dev->vendor_cmds || !dev->n_vendor_cmds)
+ if (!dev)
return -ENODEV;
+ if (!dev->vendor_cmds || !dev->n_vendor_cmds) {
+ err = -ENODEV;
+ goto put_dev;
+ }
+
if (info->attrs[NFC_ATTR_VENDOR_DATA]) {
data = nla_data(info->attrs[NFC_ATTR_VENDOR_DATA]);
data_len = nla_len(info->attrs[NFC_ATTR_VENDOR_DATA]);
- if (data_len == 0)
- return -EINVAL;
+ if (data_len == 0) {
+ err = -EINVAL;
+ goto put_dev;
+ }
} else {
data = NULL;
data_len = 0;
@@ -1573,10 +1593,14 @@ static int nfc_genl_vendor_cmd(struct sk_buff *skb,
dev->cur_cmd_info = info;
err = cmd->doit(dev, data, data_len);
dev->cur_cmd_info = NULL;
- return err;
+ goto put_dev;
}
- return -EOPNOTSUPP;
+ err = -EOPNOTSUPP;
+
+put_dev:
+ nfc_put_device(dev);
+ return err;
}
/* message building helper */
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 9ca721c9fa7185..fcee6012293b17 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -1004,14 +1004,14 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
key = kzalloc(sizeof(*key), GFP_KERNEL);
if (!key) {
error = -ENOMEM;
- goto err_kfree_key;
+ goto err_kfree_flow;
}
ovs_match_init(&match, key, false, &mask);
error = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY],
a[OVS_FLOW_ATTR_MASK], log);
if (error)
- goto err_kfree_flow;
+ goto err_kfree_key;
ovs_flow_mask_key(&new_flow->key, key, true, &mask);
@@ -1019,14 +1019,14 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
error = ovs_nla_get_identifier(&new_flow->id, a[OVS_FLOW_ATTR_UFID],
key, log);
if (error)
- goto err_kfree_flow;
+ goto err_kfree_key;
/* Validate actions. */
error = ovs_nla_copy_actions(net, a[OVS_FLOW_ATTR_ACTIONS],
&new_flow->key, &acts, log);
if (error) {
OVS_NLERR(log, "Flow actions may not be safe on all matching packets.");
- goto err_kfree_flow;
+ goto err_kfree_key;
}
reply = ovs_flow_cmd_alloc_info(acts, &new_flow->id, info, false,
@@ -1126,10 +1126,10 @@ err_unlock_ovs:
kfree_skb(reply);
err_kfree_acts:
ovs_nla_free_flow_actions(acts);
-err_kfree_flow:
- ovs_flow_free(new_flow, false);
err_kfree_key:
kfree(key);
+err_kfree_flow:
+ ovs_flow_free(new_flow, false);
error:
return error;
}
@@ -1861,7 +1861,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
vport->upcall_stats = netdev_alloc_pcpu_stats(struct vport_upcall_stats_percpu);
if (!vport->upcall_stats) {
err = -ENOMEM;
- goto err_destroy_portids;
+ goto err_destroy_vport;
}
err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,
@@ -1876,6 +1876,8 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
ovs_notify(&dp_datapath_genl_family, reply, info);
return 0;
+err_destroy_vport:
+ ovs_dp_detach_port(vport);
err_destroy_portids:
kfree(rcu_dereference_raw(dp->upcall_portids));
err_unlock_and_destroy_meters:
@@ -2323,7 +2325,7 @@ restart:
vport->upcall_stats = netdev_alloc_pcpu_stats(struct vport_upcall_stats_percpu);
if (!vport->upcall_stats) {
err = -ENOMEM;
- goto exit_unlock_free;
+ goto exit_unlock_free_vport;
}
err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
@@ -2343,6 +2345,8 @@ restart:
ovs_notify(&dp_vport_genl_family, reply, info);
return 0;
+exit_unlock_free_vport:
+ ovs_dp_detach_port(vport);
exit_unlock_free:
ovs_unlock();
kfree_skb(reply);
diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c
index 1990d496fcfc00..e595079c2cafe8 100644
--- a/net/qrtr/ns.c
+++ b/net/qrtr/ns.c
@@ -83,7 +83,10 @@ static struct qrtr_node *node_get(unsigned int node_id)
node->id = node_id;
- radix_tree_insert(&nodes, node_id, node);
+ if (radix_tree_insert(&nodes, node_id, node)) {
+ kfree(node);
+ return NULL;
+ }
return node;
}
diff --git a/net/rds/message.c b/net/rds/message.c
index b47e4f0a163934..c19c9356122782 100644
--- a/net/rds/message.c
+++ b/net/rds/message.c
@@ -104,9 +104,9 @@ static void rds_rm_zerocopy_callback(struct rds_sock *rs,
spin_lock_irqsave(&q->lock, flags);
head = &q->zcookie_head;
if (!list_empty(head)) {
- info = list_entry(head, struct rds_msg_zcopy_info,
- rs_zcookie_next);
- if (info && rds_zcookie_add(info, cookie)) {
+ info = list_first_entry(head, struct rds_msg_zcopy_info,
+ rs_zcookie_next);
+ if (rds_zcookie_add(info, cookie)) {
spin_unlock_irqrestore(&q->lock, flags);
kfree(rds_info_from_znotifier(znotif));
/* caller invokes rds_wake_sk_sleep() */
diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c
index 36fefc3957d772..ca2b17f32670dc 100644
--- a/net/rose/af_rose.c
+++ b/net/rose/af_rose.c
@@ -488,6 +488,12 @@ static int rose_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
+ lock_sock(sk);
+ if (sock->state != SS_UNCONNECTED) {
+ release_sock(sk);
+ return -EINVAL;
+ }
+
if (sk->sk_state != TCP_LISTEN) {
struct rose_sock *rose = rose_sk(sk);
@@ -497,8 +503,10 @@ static int rose_listen(struct socket *sock, int backlog)
memset(rose->dest_digis, 0, AX25_ADDR_LEN * ROSE_MAX_DIGIS);
sk->sk_max_ack_backlog = backlog;
sk->sk_state = TCP_LISTEN;
+ release_sock(sk);
return 0;
}
+ release_sock(sk);
return -EOPNOTSUPP;
}
diff --git a/net/rxrpc/Makefile b/net/rxrpc/Makefile
index e76d3459d78eef..ac5caf5a48e161 100644
--- a/net/rxrpc/Makefile
+++ b/net/rxrpc/Makefile
@@ -10,6 +10,7 @@ rxrpc-y := \
call_accept.o \
call_event.o \
call_object.o \
+ call_state.o \
conn_client.o \
conn_event.o \
conn_object.o \
diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c
index 7ea576f6ba4bc8..ebbd4a1c3f86e4 100644
--- a/net/rxrpc/af_rxrpc.c
+++ b/net/rxrpc/af_rxrpc.c
@@ -155,10 +155,10 @@ static int rxrpc_bind(struct socket *sock, struct sockaddr *saddr, int len)
if (service_id) {
write_lock(&local->services_lock);
- if (rcu_access_pointer(local->service))
+ if (local->service)
goto service_in_use;
rx->local = local;
- rcu_assign_pointer(local->service, rx);
+ local->service = rx;
write_unlock(&local->services_lock);
rx->sk.sk_state = RXRPC_SERVER_BOUND;
@@ -328,7 +328,6 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock,
mutex_unlock(&call->user_mutex);
}
- rxrpc_put_peer(cp.peer, rxrpc_peer_put_discard_tmp);
_leave(" = %p", call);
return call;
}
@@ -374,13 +373,17 @@ EXPORT_SYMBOL(rxrpc_kernel_end_call);
* @sock: The socket the call is on
* @call: The call to check
*
- * Allow a kernel service to find out whether a call is still alive -
- * ie. whether it has completed.
+ * Allow a kernel service to find out whether a call is still alive - whether
+ * it has completed successfully and all received data has been consumed.
*/
bool rxrpc_kernel_check_life(const struct socket *sock,
const struct rxrpc_call *call)
{
- return call->state != RXRPC_CALL_COMPLETE;
+ if (!rxrpc_call_is_complete(call))
+ return true;
+ if (call->completion != RXRPC_CALL_SUCCEEDED)
+ return false;
+ return !skb_queue_empty(&call->recvmsg_queue);
}
EXPORT_SYMBOL(rxrpc_kernel_check_life);
@@ -872,9 +875,9 @@ static int rxrpc_release_sock(struct sock *sk)
sk->sk_state = RXRPC_CLOSE;
- if (rx->local && rcu_access_pointer(rx->local->service) == rx) {
+ if (rx->local && rx->local->service == rx) {
write_lock(&rx->local->services_lock);
- rcu_assign_pointer(rx->local->service, NULL);
+ rx->local->service = NULL;
write_unlock(&rx->local->services_lock);
}
@@ -957,16 +960,9 @@ static const struct net_proto_family rxrpc_family_ops = {
static int __init af_rxrpc_init(void)
{
int ret = -1;
- unsigned int tmp;
BUILD_BUG_ON(sizeof(struct rxrpc_skb_priv) > sizeof_field(struct sk_buff, cb));
- get_random_bytes(&tmp, sizeof(tmp));
- tmp &= 0x3fffffff;
- if (tmp == 0)
- tmp = 1;
- idr_set_cursor(&rxrpc_client_conn_ids, tmp);
-
ret = -ENOMEM;
rxrpc_call_jar = kmem_cache_create(
"rxrpc_call_jar", sizeof(struct rxrpc_call), 0,
@@ -1062,7 +1058,6 @@ static void __exit af_rxrpc_exit(void)
* are released.
*/
rcu_barrier();
- rxrpc_destroy_client_conn_ids();
destroy_workqueue(rxrpc_workqueue);
rxrpc_exit_security();
diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h
index 18092526d3c829..433060cade0381 100644
--- a/net/rxrpc/ar-internal.h
+++ b/net/rxrpc/ar-internal.h
@@ -38,6 +38,7 @@ struct rxrpc_txbuf;
enum rxrpc_skb_mark {
RXRPC_SKB_MARK_PACKET, /* Received packet */
RXRPC_SKB_MARK_ERROR, /* Error notification */
+ RXRPC_SKB_MARK_SERVICE_CONN_SECURED, /* Service connection response has been verified */
RXRPC_SKB_MARK_REJECT_BUSY, /* Reject with BUSY */
RXRPC_SKB_MARK_REJECT_ABORT, /* Reject with ABORT (code in skb->priority) */
};
@@ -75,13 +76,7 @@ struct rxrpc_net {
bool live;
- bool kill_all_client_conns;
atomic_t nr_client_conns;
- spinlock_t client_conn_cache_lock; /* Lock for ->*_client_conns */
- struct mutex client_conn_discard_lock; /* Prevent multiple discarders */
- struct list_head idle_client_conns;
- struct work_struct client_conn_reaper;
- struct timer_list client_conn_reap_timer;
struct hlist_head local_endpoints;
struct mutex local_mutex; /* Lock for ->local_endpoints */
@@ -202,6 +197,7 @@ struct rxrpc_host_header {
* - max 48 bytes (struct sk_buff::cb)
*/
struct rxrpc_skb_priv {
+ struct rxrpc_connection *conn; /* Connection referred to (poke packet) */
u16 offset; /* Offset of data */
u16 len; /* Length of data */
u8 flags;
@@ -262,13 +258,11 @@ struct rxrpc_security {
/* respond to a challenge */
int (*respond_to_challenge)(struct rxrpc_connection *,
- struct sk_buff *,
- u32 *);
+ struct sk_buff *);
/* verify a response */
int (*verify_response)(struct rxrpc_connection *,
- struct sk_buff *,
- u32 *);
+ struct sk_buff *);
/* clear connection security */
void (*clear)(struct rxrpc_connection *);
@@ -283,22 +277,34 @@ struct rxrpc_local {
struct rcu_head rcu;
atomic_t active_users; /* Number of users of the local endpoint */
refcount_t ref; /* Number of references to the structure */
- struct rxrpc_net *rxnet; /* The network ns in which this resides */
+ struct net *net; /* The network namespace */
+ struct rxrpc_net *rxnet; /* Our bits in the network namespace */
struct hlist_node link;
struct socket *socket; /* my UDP socket */
struct task_struct *io_thread;
struct completion io_thread_ready; /* Indication that the I/O thread started */
- struct rxrpc_sock __rcu *service; /* Service(s) listening on this endpoint */
+ struct rxrpc_sock *service; /* Service(s) listening on this endpoint */
struct rw_semaphore defrag_sem; /* control re-enablement of IP DF bit */
struct sk_buff_head rx_queue; /* Received packets */
+ struct list_head conn_attend_q; /* Conns requiring immediate attention */
struct list_head call_attend_q; /* Calls requiring immediate attention */
+
struct rb_root client_bundles; /* Client connection bundles by socket params */
spinlock_t client_bundles_lock; /* Lock for client_bundles */
+ bool kill_all_client_conns;
+ struct list_head idle_client_conns;
+ struct timer_list client_conn_reap_timer;
+ unsigned long client_conn_flags;
+#define RXRPC_CLIENT_CONN_REAP_TIMER 0 /* The client conn reap timer expired */
+
spinlock_t lock; /* access lock */
rwlock_t services_lock; /* lock for services list */
int debug_id; /* debug ID for printks */
bool dead;
bool service_closed; /* Service socket closed */
+ struct idr conn_ids; /* List of connection IDs */
+ struct list_head new_client_calls; /* Newly created client calls need connection */
+ spinlock_t client_call_lock; /* Lock for ->new_client_calls */
struct sockaddr_rxrpc srx; /* local address */
};
@@ -356,7 +362,6 @@ struct rxrpc_conn_proto {
struct rxrpc_conn_parameters {
struct rxrpc_local *local; /* Representation of local endpoint */
- struct rxrpc_peer *peer; /* Remote endpoint */
struct key *key; /* Security details */
bool exclusive; /* T if conn is exclusive */
bool upgrade; /* T if service ID can be upgraded */
@@ -365,10 +370,21 @@ struct rxrpc_conn_parameters {
};
/*
+ * Call completion condition (state == RXRPC_CALL_COMPLETE).
+ */
+enum rxrpc_call_completion {
+ RXRPC_CALL_SUCCEEDED, /* - Normal termination */
+ RXRPC_CALL_REMOTELY_ABORTED, /* - call aborted by peer */
+ RXRPC_CALL_LOCALLY_ABORTED, /* - call aborted locally on error or close */
+ RXRPC_CALL_LOCAL_ERROR, /* - call failed due to local error */
+ RXRPC_CALL_NETWORK_ERROR, /* - call terminated by network error */
+ NR__RXRPC_CALL_COMPLETIONS
+};
+
+/*
* Bits in the connection flags.
*/
enum rxrpc_conn_flag {
- RXRPC_CONN_HAS_IDR, /* Has a client conn ID assigned */
RXRPC_CONN_IN_SERVICE_CONNS, /* Conn is in peer->service_conns */
RXRPC_CONN_DONT_REUSE, /* Don't reuse this connection */
RXRPC_CONN_PROBING_FOR_UPGRADE, /* Probing for service upgrade */
@@ -388,6 +404,7 @@ enum rxrpc_conn_flag {
*/
enum rxrpc_conn_event {
RXRPC_CONN_EV_CHALLENGE, /* Send challenge packet */
+ RXRPC_CONN_EV_ABORT_CALLS, /* Abort attached calls */
};
/*
@@ -395,13 +412,13 @@ enum rxrpc_conn_event {
*/
enum rxrpc_conn_proto_state {
RXRPC_CONN_UNUSED, /* Connection not yet attempted */
+ RXRPC_CONN_CLIENT_UNSECURED, /* Client connection needs security init */
RXRPC_CONN_CLIENT, /* Client connection */
RXRPC_CONN_SERVICE_PREALLOC, /* Service connection preallocation */
RXRPC_CONN_SERVICE_UNSECURED, /* Service unsecured connection */
RXRPC_CONN_SERVICE_CHALLENGING, /* Service challenging for security */
RXRPC_CONN_SERVICE, /* Service secured connection */
- RXRPC_CONN_REMOTELY_ABORTED, /* Conn aborted by peer */
- RXRPC_CONN_LOCALLY_ABORTED, /* Conn aborted locally */
+ RXRPC_CONN_ABORTED, /* Conn aborted */
RXRPC_CONN__NR_STATES
};
@@ -412,17 +429,16 @@ struct rxrpc_bundle {
struct rxrpc_local *local; /* Representation of local endpoint */
struct rxrpc_peer *peer; /* Remote endpoint */
struct key *key; /* Security details */
+ const struct rxrpc_security *security; /* applied security module */
refcount_t ref;
atomic_t active; /* Number of active users */
unsigned int debug_id;
u32 security_level; /* Security level selected */
u16 service_id; /* Service ID for this connection */
bool try_upgrade; /* True if the bundle is attempting upgrade */
- bool alloc_conn; /* True if someone's getting a conn */
bool exclusive; /* T if conn is exclusive */
bool upgrade; /* T if service ID can be upgraded */
- short alloc_error; /* Error from last conn allocation */
- spinlock_t channel_lock;
+ unsigned short alloc_error; /* Error from last conn allocation */
struct rb_node local_node; /* Node in local->client_conns */
struct list_head waiting_calls; /* Calls waiting for channels */
unsigned long avail_chans; /* Mask of available channels */
@@ -440,6 +456,7 @@ struct rxrpc_connection {
struct rxrpc_peer *peer; /* Remote endpoint */
struct rxrpc_net *rxnet; /* Network namespace to which call belongs */
struct key *key; /* Security details */
+ struct list_head attend_link; /* Link in local->conn_attend_q */
refcount_t ref;
atomic_t active; /* Active count for service conns */
@@ -449,7 +466,7 @@ struct rxrpc_connection {
unsigned char act_chans; /* Mask of active channels */
struct rxrpc_channel {
unsigned long final_ack_at; /* Time at which to issue final ACK */
- struct rxrpc_call __rcu *call; /* Active call */
+ struct rxrpc_call *call; /* Active call */
unsigned int call_debug_id; /* call->debug_id */
u32 call_id; /* ID of current call */
u32 call_counter; /* Call ID counter */
@@ -470,6 +487,7 @@ struct rxrpc_connection {
struct list_head link; /* link in master connection list */
struct sk_buff_head rx_queue; /* received conn-level packets */
+ struct mutex security_lock; /* Lock for security management */
const struct rxrpc_security *security; /* applied security module */
union {
struct {
@@ -483,7 +501,8 @@ struct rxrpc_connection {
unsigned long idle_timestamp; /* Time at which last became idle */
spinlock_t state_lock; /* state-change lock */
enum rxrpc_conn_proto_state state; /* current state of connection */
- u32 abort_code; /* Abort code of connection abort */
+ enum rxrpc_call_completion completion; /* Completion condition */
+ s32 abort_code; /* Abort code of connection abort */
int debug_id; /* debug ID for printks */
atomic_t serial; /* packet serial number counter */
unsigned int hi_serial; /* highest serial number received */
@@ -527,7 +546,8 @@ enum rxrpc_call_flag {
RXRPC_CALL_KERNEL, /* The call was made by the kernel */
RXRPC_CALL_UPGRADE, /* Service upgrade was requested for the call */
RXRPC_CALL_EXCLUSIVE, /* The call uses a once-only connection */
- RXRPC_CALL_RX_IS_IDLE, /* Reception is idle - send an ACK */
+ RXRPC_CALL_RX_IS_IDLE, /* recvmsg() is idle - send an ACK */
+ RXRPC_CALL_RECVMSG_READ_ALL, /* recvmsg() read all of the received data */
};
/*
@@ -558,18 +578,6 @@ enum rxrpc_call_state {
};
/*
- * Call completion condition (state == RXRPC_CALL_COMPLETE).
- */
-enum rxrpc_call_completion {
- RXRPC_CALL_SUCCEEDED, /* - Normal termination */
- RXRPC_CALL_REMOTELY_ABORTED, /* - call aborted by peer */
- RXRPC_CALL_LOCALLY_ABORTED, /* - call aborted locally on error or close */
- RXRPC_CALL_LOCAL_ERROR, /* - call failed due to local error */
- RXRPC_CALL_NETWORK_ERROR, /* - call terminated by network error */
- NR__RXRPC_CALL_COMPLETIONS
-};
-
-/*
* Call Tx congestion management modes.
*/
enum rxrpc_congest_mode {
@@ -587,6 +595,7 @@ enum rxrpc_congest_mode {
struct rxrpc_call {
struct rcu_head rcu;
struct rxrpc_connection *conn; /* connection carrying call */
+ struct rxrpc_bundle *bundle; /* Connection bundle to use */
struct rxrpc_peer *peer; /* Peer record for remote address */
struct rxrpc_local *local; /* Representation of local endpoint */
struct rxrpc_sock __rcu *socket; /* socket responsible */
@@ -609,7 +618,7 @@ struct rxrpc_call {
struct work_struct destroyer; /* In-process-context destroyer */
rxrpc_notify_rx_t notify_rx; /* kernel service Rx notification function */
struct list_head link; /* link in master call list */
- struct list_head chan_wait_link; /* Link in conn->bundle->waiting_calls */
+ struct list_head wait_link; /* Link in local->new_client_calls */
struct hlist_node error_link; /* link in error distribution list */
struct list_head accept_link; /* Link in rx->acceptq */
struct list_head recvmsg_link; /* Link in rx->recvmsg_q */
@@ -623,10 +632,13 @@ struct rxrpc_call {
unsigned long flags;
unsigned long events;
spinlock_t notify_lock; /* Kernel notification lock */
- rwlock_t state_lock; /* lock for state transition */
- u32 abort_code; /* Local/remote abort code */
+ unsigned int send_abort_why; /* Why the abort [enum rxrpc_abort_reason] */
+ s32 send_abort; /* Abort code to be sent */
+ short send_abort_err; /* Error to be associated with the abort */
+ rxrpc_seq_t send_abort_seq; /* DATA packet that incurred the abort (or 0) */
+ s32 abort_code; /* Local/remote abort code */
int error; /* Local error incurred */
- enum rxrpc_call_state state; /* current state of call */
+ enum rxrpc_call_state _state; /* Current state of call (needs barrier) */
enum rxrpc_call_completion completion; /* Call completion condition */
refcount_t ref;
u8 security_ix; /* Security type */
@@ -812,9 +824,11 @@ extern struct workqueue_struct *rxrpc_workqueue;
*/
int rxrpc_service_prealloc(struct rxrpc_sock *, gfp_t);
void rxrpc_discard_prealloc(struct rxrpc_sock *);
-int rxrpc_new_incoming_call(struct rxrpc_local *, struct rxrpc_peer *,
- struct rxrpc_connection *, struct sockaddr_rxrpc *,
- struct sk_buff *);
+bool rxrpc_new_incoming_call(struct rxrpc_local *local,
+ struct rxrpc_peer *peer,
+ struct rxrpc_connection *conn,
+ struct sockaddr_rxrpc *peer_srx,
+ struct sk_buff *skb);
void rxrpc_accept_incoming_calls(struct rxrpc_local *);
int rxrpc_user_charge_accept(struct rxrpc_sock *, unsigned long);
@@ -834,7 +848,7 @@ void rxrpc_reduce_call_timer(struct rxrpc_call *call,
unsigned long now,
enum rxrpc_timer_trace why);
-void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb);
+bool rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb);
/*
* call_object.c
@@ -851,6 +865,7 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *,
struct sockaddr_rxrpc *,
struct rxrpc_call_params *, gfp_t,
unsigned int);
+void rxrpc_start_call_timer(struct rxrpc_call *call);
void rxrpc_incoming_call(struct rxrpc_sock *, struct rxrpc_call *,
struct sk_buff *);
void rxrpc_release_call(struct rxrpc_sock *, struct rxrpc_call *);
@@ -873,32 +888,88 @@ static inline bool rxrpc_is_client_call(const struct rxrpc_call *call)
}
/*
+ * call_state.c
+ */
+bool rxrpc_set_call_completion(struct rxrpc_call *call,
+ enum rxrpc_call_completion compl,
+ u32 abort_code,
+ int error);
+bool rxrpc_call_completed(struct rxrpc_call *call);
+bool rxrpc_abort_call(struct rxrpc_call *call, rxrpc_seq_t seq,
+ u32 abort_code, int error, enum rxrpc_abort_reason why);
+void rxrpc_prefail_call(struct rxrpc_call *call, enum rxrpc_call_completion compl,
+ int error);
+
+static inline void rxrpc_set_call_state(struct rxrpc_call *call,
+ enum rxrpc_call_state state)
+{
+ /* Order write of completion info before write of ->state. */
+ smp_store_release(&call->_state, state);
+ wake_up(&call->waitq);
+}
+
+static inline enum rxrpc_call_state __rxrpc_call_state(const struct rxrpc_call *call)
+{
+ return call->_state; /* Only inside I/O thread */
+}
+
+static inline bool __rxrpc_call_is_complete(const struct rxrpc_call *call)
+{
+ return __rxrpc_call_state(call) == RXRPC_CALL_COMPLETE;
+}
+
+static inline enum rxrpc_call_state rxrpc_call_state(const struct rxrpc_call *call)
+{
+ /* Order read ->state before read of completion info. */
+ return smp_load_acquire(&call->_state);
+}
+
+static inline bool rxrpc_call_is_complete(const struct rxrpc_call *call)
+{
+ return rxrpc_call_state(call) == RXRPC_CALL_COMPLETE;
+}
+
+static inline bool rxrpc_call_has_failed(const struct rxrpc_call *call)
+{
+ return rxrpc_call_is_complete(call) && call->completion != RXRPC_CALL_SUCCEEDED;
+}
+
+/*
* conn_client.c
*/
extern unsigned int rxrpc_reap_client_connections;
extern unsigned long rxrpc_conn_idle_client_expiry;
extern unsigned long rxrpc_conn_idle_client_fast_expiry;
-extern struct idr rxrpc_client_conn_ids;
-void rxrpc_destroy_client_conn_ids(void);
+void rxrpc_purge_client_connections(struct rxrpc_local *local);
struct rxrpc_bundle *rxrpc_get_bundle(struct rxrpc_bundle *, enum rxrpc_bundle_trace);
void rxrpc_put_bundle(struct rxrpc_bundle *, enum rxrpc_bundle_trace);
-int rxrpc_connect_call(struct rxrpc_sock *, struct rxrpc_call *,
- struct rxrpc_conn_parameters *, struct sockaddr_rxrpc *,
- gfp_t);
+int rxrpc_look_up_bundle(struct rxrpc_call *call, gfp_t gfp);
+void rxrpc_connect_client_calls(struct rxrpc_local *local);
void rxrpc_expose_client_call(struct rxrpc_call *);
void rxrpc_disconnect_client_call(struct rxrpc_bundle *, struct rxrpc_call *);
+void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle);
void rxrpc_put_client_conn(struct rxrpc_connection *, enum rxrpc_conn_trace);
-void rxrpc_discard_expired_client_conns(struct work_struct *);
-void rxrpc_destroy_all_client_connections(struct rxrpc_net *);
+void rxrpc_discard_expired_client_conns(struct rxrpc_local *local);
void rxrpc_clean_up_local_conns(struct rxrpc_local *);
/*
* conn_event.c
*/
+void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn, struct sk_buff *skb,
+ unsigned int channel);
+int rxrpc_abort_conn(struct rxrpc_connection *conn, struct sk_buff *skb,
+ s32 abort_code, int err, enum rxrpc_abort_reason why);
void rxrpc_process_connection(struct work_struct *);
void rxrpc_process_delayed_final_acks(struct rxrpc_connection *, bool);
-int rxrpc_input_conn_packet(struct rxrpc_connection *conn, struct sk_buff *skb);
+bool rxrpc_input_conn_packet(struct rxrpc_connection *conn, struct sk_buff *skb);
+void rxrpc_input_conn_event(struct rxrpc_connection *conn, struct sk_buff *skb);
+
+static inline bool rxrpc_is_conn_aborted(const struct rxrpc_connection *conn)
+{
+ /* Order reading the abort info after the state check. */
+ return smp_load_acquire(&conn->state) == RXRPC_CONN_ABORTED;
+}
/*
* conn_object.c
@@ -906,6 +977,7 @@ int rxrpc_input_conn_packet(struct rxrpc_connection *conn, struct sk_buff *skb);
extern unsigned int rxrpc_connection_expiry;
extern unsigned int rxrpc_closed_conn_expiry;
+void rxrpc_poke_conn(struct rxrpc_connection *conn, enum rxrpc_conn_trace why);
struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *, gfp_t);
struct rxrpc_connection *rxrpc_find_client_connection_rcu(struct rxrpc_local *,
struct sockaddr_rxrpc *,
@@ -961,12 +1033,19 @@ void rxrpc_implicit_end_call(struct rxrpc_call *, struct sk_buff *);
*/
int rxrpc_encap_rcv(struct sock *, struct sk_buff *);
void rxrpc_error_report(struct sock *);
+bool rxrpc_direct_abort(struct sk_buff *skb, enum rxrpc_abort_reason why,
+ s32 abort_code, int err);
int rxrpc_io_thread(void *data);
static inline void rxrpc_wake_up_io_thread(struct rxrpc_local *local)
{
wake_up_process(local->io_thread);
}
+static inline bool rxrpc_protocol_error(struct sk_buff *skb, enum rxrpc_abort_reason why)
+{
+ return rxrpc_direct_abort(skb, why, RX_PROTOCOL_ERROR, -EPROTO);
+}
+
/*
* insecure.c
*/
@@ -1048,6 +1127,7 @@ static inline struct rxrpc_net *rxrpc_net(struct net *net)
int rxrpc_send_ack_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb);
int rxrpc_send_abort_packet(struct rxrpc_call *);
int rxrpc_send_data_packet(struct rxrpc_call *, struct rxrpc_txbuf *);
+void rxrpc_send_conn_abort(struct rxrpc_connection *conn);
void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb);
void rxrpc_send_keepalive(struct rxrpc_peer *);
void rxrpc_transmit_one(struct rxrpc_call *call, struct rxrpc_txbuf *txb);
@@ -1063,12 +1143,11 @@ void rxrpc_peer_keepalive_worker(struct work_struct *);
*/
struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *,
const struct sockaddr_rxrpc *);
-struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_sock *, struct rxrpc_local *,
- struct sockaddr_rxrpc *, gfp_t);
+struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local,
+ struct sockaddr_rxrpc *srx, gfp_t gfp);
struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *, gfp_t,
enum rxrpc_peer_trace);
-void rxrpc_new_incoming_peer(struct rxrpc_sock *, struct rxrpc_local *,
- struct rxrpc_peer *);
+void rxrpc_new_incoming_peer(struct rxrpc_local *local, struct rxrpc_peer *peer);
void rxrpc_destroy_all_peers(struct rxrpc_net *);
struct rxrpc_peer *rxrpc_get_peer(struct rxrpc_peer *, enum rxrpc_peer_trace);
struct rxrpc_peer *rxrpc_get_peer_maybe(struct rxrpc_peer *, enum rxrpc_peer_trace);
@@ -1086,33 +1165,22 @@ extern const struct seq_operations rxrpc_local_seq_ops;
* recvmsg.c
*/
void rxrpc_notify_socket(struct rxrpc_call *);
-bool __rxrpc_set_call_completion(struct rxrpc_call *, enum rxrpc_call_completion, u32, int);
-bool rxrpc_set_call_completion(struct rxrpc_call *, enum rxrpc_call_completion, u32, int);
-bool __rxrpc_call_completed(struct rxrpc_call *);
-bool rxrpc_call_completed(struct rxrpc_call *);
-bool __rxrpc_abort_call(const char *, struct rxrpc_call *, rxrpc_seq_t, u32, int);
-bool rxrpc_abort_call(const char *, struct rxrpc_call *, rxrpc_seq_t, u32, int);
int rxrpc_recvmsg(struct socket *, struct msghdr *, size_t, int);
/*
* Abort a call due to a protocol error.
*/
-static inline bool __rxrpc_abort_eproto(struct rxrpc_call *call,
- struct sk_buff *skb,
- const char *eproto_why,
- const char *why,
- u32 abort_code)
+static inline int rxrpc_abort_eproto(struct rxrpc_call *call,
+ struct sk_buff *skb,
+ s32 abort_code,
+ enum rxrpc_abort_reason why)
{
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
- trace_rxrpc_rx_eproto(call, sp->hdr.serial, eproto_why);
- return rxrpc_abort_call(why, call, sp->hdr.seq, abort_code, -EPROTO);
+ rxrpc_abort_call(call, sp->hdr.seq, abort_code, -EPROTO, why);
+ return -EPROTO;
}
-#define rxrpc_abort_eproto(call, skb, eproto_why, abort_why, abort_code) \
- __rxrpc_abort_eproto((call), (skb), tracepoint_string(eproto_why), \
- (abort_why), (abort_code))
-
/*
* rtt.c
*/
@@ -1144,6 +1212,8 @@ struct key *rxrpc_look_up_server_security(struct rxrpc_connection *,
/*
* sendmsg.c
*/
+bool rxrpc_propose_abort(struct rxrpc_call *call, s32 abort_code, int error,
+ enum rxrpc_abort_reason why);
int rxrpc_do_sendmsg(struct rxrpc_sock *, struct msghdr *, size_t);
/*
diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c
index c02401656fa981..3e8689fdc43714 100644
--- a/net/rxrpc/call_accept.c
+++ b/net/rxrpc/call_accept.c
@@ -99,7 +99,7 @@ static int rxrpc_service_prealloc_one(struct rxrpc_sock *rx,
if (!call)
return -ENOMEM;
call->flags |= (1 << RXRPC_CALL_IS_SERVICE);
- call->state = RXRPC_CALL_SERVER_PREALLOC;
+ rxrpc_set_call_state(call, RXRPC_CALL_SERVER_PREALLOC);
__set_bit(RXRPC_CALL_EV_INITIAL_PING, &call->events);
trace_rxrpc_call(call->debug_id, refcount_read(&call->ref),
@@ -280,7 +280,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx,
(peer_tail + 1) &
(RXRPC_BACKLOG_MAX - 1));
- rxrpc_new_incoming_peer(rx, local, peer);
+ rxrpc_new_incoming_peer(local, peer);
}
/* Now allocate and set up the connection */
@@ -326,11 +326,11 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx,
* If we want to report an error, we mark the skb with the packet type and
* abort code and return false.
*/
-int rxrpc_new_incoming_call(struct rxrpc_local *local,
- struct rxrpc_peer *peer,
- struct rxrpc_connection *conn,
- struct sockaddr_rxrpc *peer_srx,
- struct sk_buff *skb)
+bool rxrpc_new_incoming_call(struct rxrpc_local *local,
+ struct rxrpc_peer *peer,
+ struct rxrpc_connection *conn,
+ struct sockaddr_rxrpc *peer_srx,
+ struct sk_buff *skb)
{
const struct rxrpc_security *sec = NULL;
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
@@ -339,18 +339,17 @@ int rxrpc_new_incoming_call(struct rxrpc_local *local,
_enter("");
- /* Don't set up a call for anything other than the first DATA packet. */
- if (sp->hdr.seq != 1 ||
- sp->hdr.type != RXRPC_PACKET_TYPE_DATA)
- return 0; /* Just discard */
+ /* Don't set up a call for anything other than a DATA packet. */
+ if (sp->hdr.type != RXRPC_PACKET_TYPE_DATA)
+ return rxrpc_protocol_error(skb, rxrpc_eproto_no_service_call);
- rcu_read_lock();
+ read_lock(&local->services_lock);
/* Weed out packets to services we're not offering. Packets that would
* begin a call are explicitly rejected and the rest are just
* discarded.
*/
- rx = rcu_dereference(local->service);
+ rx = local->service;
if (!rx || (sp->hdr.serviceId != rx->srx.srx_service &&
sp->hdr.serviceId != rx->second_service)
) {
@@ -363,16 +362,14 @@ int rxrpc_new_incoming_call(struct rxrpc_local *local,
if (!conn) {
sec = rxrpc_get_incoming_security(rx, skb);
if (!sec)
- goto reject;
+ goto unsupported_security;
}
spin_lock(&rx->incoming_lock);
if (rx->sk.sk_state == RXRPC_SERVER_LISTEN_DISABLED ||
rx->sk.sk_state == RXRPC_CLOSE) {
- trace_rxrpc_abort(0, "CLS", sp->hdr.cid, sp->hdr.callNumber,
- sp->hdr.seq, RX_INVALID_OPERATION, ESHUTDOWN);
- skb->mark = RXRPC_SKB_MARK_REJECT_ABORT;
- skb->priority = RX_INVALID_OPERATION;
+ rxrpc_direct_abort(skb, rxrpc_abort_shut_down,
+ RX_INVALID_OPERATION, -ESHUTDOWN);
goto no_call;
}
@@ -402,7 +399,7 @@ int rxrpc_new_incoming_call(struct rxrpc_local *local,
spin_unlock(&conn->state_lock);
spin_unlock(&rx->incoming_lock);
- rcu_read_unlock();
+ read_unlock(&local->services_lock);
if (hlist_unhashed(&call->error_link)) {
spin_lock(&call->peer->lock);
@@ -413,22 +410,24 @@ int rxrpc_new_incoming_call(struct rxrpc_local *local,
_leave(" = %p{%d}", call, call->debug_id);
rxrpc_input_call_event(call, skb);
rxrpc_put_call(call, rxrpc_call_put_input);
- return 0;
+ return true;
unsupported_service:
- trace_rxrpc_abort(0, "INV", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
- RX_INVALID_OPERATION, EOPNOTSUPP);
- skb->priority = RX_INVALID_OPERATION;
- goto reject;
+ read_unlock(&local->services_lock);
+ return rxrpc_direct_abort(skb, rxrpc_abort_service_not_offered,
+ RX_INVALID_OPERATION, -EOPNOTSUPP);
+unsupported_security:
+ read_unlock(&local->services_lock);
+ return rxrpc_direct_abort(skb, rxrpc_abort_service_not_offered,
+ RX_INVALID_OPERATION, -EKEYREJECTED);
no_call:
spin_unlock(&rx->incoming_lock);
-reject:
- rcu_read_unlock();
+ read_unlock(&local->services_lock);
_leave(" = f [%u]", skb->mark);
- return -EPROTO;
+ return false;
discard:
- rcu_read_unlock();
- return 0;
+ read_unlock(&local->services_lock);
+ return true;
}
/*
diff --git a/net/rxrpc/call_event.c b/net/rxrpc/call_event.c
index b2cf448fb02c0d..1abdef15debce2 100644
--- a/net/rxrpc/call_event.c
+++ b/net/rxrpc/call_event.c
@@ -251,6 +251,41 @@ out:
_leave("");
}
+/*
+ * Start transmitting the reply to a service. This cancels the need to ACK the
+ * request if we haven't yet done so.
+ */
+static void rxrpc_begin_service_reply(struct rxrpc_call *call)
+{
+ unsigned long now = jiffies;
+
+ rxrpc_set_call_state(call, RXRPC_CALL_SERVER_SEND_REPLY);
+ WRITE_ONCE(call->delay_ack_at, now + MAX_JIFFY_OFFSET);
+ if (call->ackr_reason == RXRPC_ACK_DELAY)
+ call->ackr_reason = 0;
+ trace_rxrpc_timer(call, rxrpc_timer_init_for_send_reply, now);
+}
+
+/*
+ * Close the transmission phase. After this point there is no more data to be
+ * transmitted in the call.
+ */
+static void rxrpc_close_tx_phase(struct rxrpc_call *call)
+{
+ _debug("________awaiting reply/ACK__________");
+
+ switch (__rxrpc_call_state(call)) {
+ case RXRPC_CALL_CLIENT_SEND_REQUEST:
+ rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_AWAIT_REPLY);
+ break;
+ case RXRPC_CALL_SERVER_SEND_REPLY:
+ rxrpc_set_call_state(call, RXRPC_CALL_SERVER_AWAIT_ACK);
+ break;
+ default:
+ break;
+ }
+}
+
static bool rxrpc_tx_window_has_space(struct rxrpc_call *call)
{
unsigned int winsize = min_t(unsigned int, call->tx_winsize,
@@ -270,9 +305,11 @@ static void rxrpc_decant_prepared_tx(struct rxrpc_call *call)
{
struct rxrpc_txbuf *txb;
- if (rxrpc_is_client_call(call) &&
- !test_bit(RXRPC_CALL_EXPOSED, &call->flags))
+ if (!test_bit(RXRPC_CALL_EXPOSED, &call->flags)) {
+ if (list_empty(&call->tx_sendmsg))
+ return;
rxrpc_expose_client_call(call);
+ }
while ((txb = list_first_entry_or_null(&call->tx_sendmsg,
struct rxrpc_txbuf, call_link))) {
@@ -283,6 +320,9 @@ static void rxrpc_decant_prepared_tx(struct rxrpc_call *call)
call->tx_top = txb->seq;
list_add_tail(&txb->call_link, &call->tx_buffer);
+ if (txb->wire.flags & RXRPC_LAST_PACKET)
+ rxrpc_close_tx_phase(call);
+
rxrpc_transmit_one(call, txb);
if (!rxrpc_tx_window_has_space(call))
@@ -292,16 +332,15 @@ static void rxrpc_decant_prepared_tx(struct rxrpc_call *call)
static void rxrpc_transmit_some_data(struct rxrpc_call *call)
{
- switch (call->state) {
+ switch (__rxrpc_call_state(call)) {
case RXRPC_CALL_SERVER_ACK_REQUEST:
if (list_empty(&call->tx_sendmsg))
return;
+ rxrpc_begin_service_reply(call);
fallthrough;
case RXRPC_CALL_SERVER_SEND_REPLY:
- case RXRPC_CALL_SERVER_AWAIT_ACK:
case RXRPC_CALL_CLIENT_SEND_REQUEST:
- case RXRPC_CALL_CLIENT_AWAIT_REPLY:
if (!rxrpc_tx_window_has_space(call))
return;
if (list_empty(&call->tx_sendmsg)) {
@@ -331,21 +370,31 @@ static void rxrpc_send_initial_ping(struct rxrpc_call *call)
/*
* Handle retransmission and deferred ACK/abort generation.
*/
-void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
+bool rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
{
unsigned long now, next, t;
rxrpc_serial_t ackr_serial;
bool resend = false, expired = false;
+ s32 abort_code;
rxrpc_see_call(call, rxrpc_call_see_input);
//printk("\n--------------------\n");
_enter("{%d,%s,%lx}",
- call->debug_id, rxrpc_call_states[call->state], call->events);
+ call->debug_id, rxrpc_call_states[__rxrpc_call_state(call)],
+ call->events);
- if (call->state == RXRPC_CALL_COMPLETE)
+ if (__rxrpc_call_is_complete(call))
goto out;
+ /* Handle abort request locklessly, vs rxrpc_propose_abort(). */
+ abort_code = smp_load_acquire(&call->send_abort);
+ if (abort_code) {
+ rxrpc_abort_call(call, 0, call->send_abort, call->send_abort_err,
+ call->send_abort_why);
+ goto out;
+ }
+
if (skb && skb->mark == RXRPC_SKB_MARK_ERROR)
goto out;
@@ -358,7 +407,7 @@ void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
}
t = READ_ONCE(call->expect_req_by);
- if (call->state == RXRPC_CALL_SERVER_RECV_REQUEST &&
+ if (__rxrpc_call_state(call) == RXRPC_CALL_SERVER_RECV_REQUEST &&
time_after_eq(now, t)) {
trace_rxrpc_timer(call, rxrpc_timer_exp_idle, now);
expired = true;
@@ -429,11 +478,12 @@ void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
if (test_bit(RXRPC_CALL_RX_HEARD, &call->flags) &&
(int)call->conn->hi_serial - (int)call->rx_serial > 0) {
trace_rxrpc_call_reset(call);
- rxrpc_abort_call("EXP", call, 0, RX_CALL_DEAD, -ECONNRESET);
+ rxrpc_abort_call(call, 0, RX_CALL_DEAD, -ECONNRESET,
+ rxrpc_abort_call_reset);
} else {
- rxrpc_abort_call("EXP", call, 0, RX_CALL_TIMEOUT, -ETIME);
+ rxrpc_abort_call(call, 0, RX_CALL_TIMEOUT, -ETIME,
+ rxrpc_abort_call_timeout);
}
- rxrpc_send_abort_packet(call);
goto out;
}
@@ -441,7 +491,7 @@ void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
rxrpc_send_ACK(call, RXRPC_ACK_PING, 0,
rxrpc_propose_ack_ping_for_lost_ack);
- if (resend && call->state != RXRPC_CALL_CLIENT_RECV_REPLY)
+ if (resend && __rxrpc_call_state(call) != RXRPC_CALL_CLIENT_RECV_REPLY)
rxrpc_resend(call, NULL);
if (test_and_clear_bit(RXRPC_CALL_RX_IS_IDLE, &call->flags))
@@ -453,7 +503,7 @@ void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
rxrpc_propose_ack_input_data);
/* Make sure the timer is restarted */
- if (call->state != RXRPC_CALL_COMPLETE) {
+ if (!__rxrpc_call_is_complete(call)) {
next = call->expect_rx_by;
#define set(T) { t = READ_ONCE(T); if (time_before(t, next)) next = t; }
@@ -474,9 +524,15 @@ void rxrpc_input_call_event(struct rxrpc_call *call, struct sk_buff *skb)
}
out:
- if (call->state == RXRPC_CALL_COMPLETE)
+ if (__rxrpc_call_is_complete(call)) {
del_timer_sync(&call->timer);
+ if (!test_bit(RXRPC_CALL_DISCONNECTED, &call->flags))
+ rxrpc_disconnect_call(call);
+ if (call->security)
+ call->security->free_call_crypto(call);
+ }
if (call->acks_hard_ack != call->tx_bottom)
rxrpc_shrink_call_tx_buffer(call);
_leave("");
+ return true;
}
diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c
index 89dcf60b115875..f3c9f0201c1561 100644
--- a/net/rxrpc/call_object.c
+++ b/net/rxrpc/call_object.c
@@ -50,7 +50,7 @@ void rxrpc_poke_call(struct rxrpc_call *call, enum rxrpc_call_poke_trace what)
struct rxrpc_local *local = call->local;
bool busy;
- if (call->state < RXRPC_CALL_COMPLETE) {
+ if (!test_bit(RXRPC_CALL_DISCONNECTED, &call->flags)) {
spin_lock_bh(&local->lock);
busy = !list_empty(&call->attend_link);
trace_rxrpc_poke_call(call, busy, what);
@@ -69,7 +69,7 @@ static void rxrpc_call_timer_expired(struct timer_list *t)
_enter("%d", call->debug_id);
- if (call->state < RXRPC_CALL_COMPLETE) {
+ if (!__rxrpc_call_is_complete(call)) {
trace_rxrpc_timer_expired(call, jiffies);
rxrpc_poke_call(call, rxrpc_call_poke_timer);
}
@@ -150,7 +150,7 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp,
timer_setup(&call->timer, rxrpc_call_timer_expired, 0);
INIT_WORK(&call->destroyer, rxrpc_destroy_call);
INIT_LIST_HEAD(&call->link);
- INIT_LIST_HEAD(&call->chan_wait_link);
+ INIT_LIST_HEAD(&call->wait_link);
INIT_LIST_HEAD(&call->accept_link);
INIT_LIST_HEAD(&call->recvmsg_link);
INIT_LIST_HEAD(&call->sock_link);
@@ -162,7 +162,6 @@ struct rxrpc_call *rxrpc_alloc_call(struct rxrpc_sock *rx, gfp_t gfp,
init_waitqueue_head(&call->waitq);
spin_lock_init(&call->notify_lock);
spin_lock_init(&call->tx_lock);
- rwlock_init(&call->state_lock);
refcount_set(&call->ref, 1);
call->debug_id = debug_id;
call->tx_total_len = -1;
@@ -211,7 +210,6 @@ static struct rxrpc_call *rxrpc_alloc_client_call(struct rxrpc_sock *rx,
now = ktime_get_real();
call->acks_latest_ts = now;
call->cong_tstamp = now;
- call->state = RXRPC_CALL_CLIENT_AWAIT_CONN;
call->dest_srx = *srx;
call->interruptibility = p->interruptibility;
call->tx_total_len = p->tx_total_len;
@@ -227,11 +225,13 @@ static struct rxrpc_call *rxrpc_alloc_client_call(struct rxrpc_sock *rx,
ret = rxrpc_init_client_call_security(call);
if (ret < 0) {
- __rxrpc_set_call_completion(call, RXRPC_CALL_LOCAL_ERROR, 0, ret);
+ rxrpc_prefail_call(call, RXRPC_CALL_LOCAL_ERROR, ret);
rxrpc_put_call(call, rxrpc_call_put_discard_error);
return ERR_PTR(ret);
}
+ rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_AWAIT_CONN);
+
trace_rxrpc_call(call->debug_id, refcount_read(&call->ref),
p->user_call_ID, rxrpc_call_new_client);
@@ -242,7 +242,7 @@ static struct rxrpc_call *rxrpc_alloc_client_call(struct rxrpc_sock *rx,
/*
* Initiate the call ack/resend/expiry timer.
*/
-static void rxrpc_start_call_timer(struct rxrpc_call *call)
+void rxrpc_start_call_timer(struct rxrpc_call *call)
{
unsigned long now = jiffies;
unsigned long j = now + MAX_JIFFY_OFFSET;
@@ -287,6 +287,39 @@ static void rxrpc_put_call_slot(struct rxrpc_call *call)
}
/*
+ * Start the process of connecting a call. We obtain a peer and a connection
+ * bundle, but the actual association of a call with a connection is offloaded
+ * to the I/O thread to simplify locking.
+ */
+static int rxrpc_connect_call(struct rxrpc_call *call, gfp_t gfp)
+{
+ struct rxrpc_local *local = call->local;
+ int ret = -ENOMEM;
+
+ _enter("{%d,%lx},", call->debug_id, call->user_call_ID);
+
+ call->peer = rxrpc_lookup_peer(local, &call->dest_srx, gfp);
+ if (!call->peer)
+ goto error;
+
+ ret = rxrpc_look_up_bundle(call, gfp);
+ if (ret < 0)
+ goto error;
+
+ trace_rxrpc_client(NULL, -1, rxrpc_client_queue_new_call);
+ rxrpc_get_call(call, rxrpc_call_get_io_thread);
+ spin_lock(&local->client_call_lock);
+ list_add_tail(&call->wait_link, &local->new_client_calls);
+ spin_unlock(&local->client_call_lock);
+ rxrpc_wake_up_io_thread(local);
+ return 0;
+
+error:
+ __set_bit(RXRPC_CALL_DISCONNECTED, &call->flags);
+ return ret;
+}
+
+/*
* Set up a call for the given parameters.
* - Called with the socket lock held, which it must release.
* - If it returns a call, the call's lock will need releasing by the caller.
@@ -365,14 +398,10 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx,
/* Set up or get a connection record and set the protocol parameters,
* including channel number and call ID.
*/
- ret = rxrpc_connect_call(rx, call, cp, srx, gfp);
+ ret = rxrpc_connect_call(call, gfp);
if (ret < 0)
goto error_attached_to_socket;
- rxrpc_see_call(call, rxrpc_call_see_connected);
-
- rxrpc_start_call_timer(call);
-
_leave(" = %p [new]", call);
return call;
@@ -384,27 +413,23 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx,
error_dup_user_ID:
write_unlock(&rx->call_lock);
release_sock(&rx->sk);
- __rxrpc_set_call_completion(call, RXRPC_CALL_LOCAL_ERROR,
- RX_CALL_DEAD, -EEXIST);
+ rxrpc_prefail_call(call, RXRPC_CALL_LOCAL_ERROR, -EEXIST);
trace_rxrpc_call(call->debug_id, refcount_read(&call->ref), 0,
rxrpc_call_see_userid_exists);
- rxrpc_release_call(rx, call);
mutex_unlock(&call->user_mutex);
rxrpc_put_call(call, rxrpc_call_put_userid_exists);
_leave(" = -EEXIST");
return ERR_PTR(-EEXIST);
/* We got an error, but the call is attached to the socket and is in
- * need of release. However, we might now race with recvmsg() when
- * completing the call queues it. Return 0 from sys_sendmsg() and
+ * need of release. However, we might now race with recvmsg() when it
+ * completion notifies the socket. Return 0 from sys_sendmsg() and
* leave the error to recvmsg() to deal with.
*/
error_attached_to_socket:
trace_rxrpc_call(call->debug_id, refcount_read(&call->ref), ret,
rxrpc_call_see_connect_failed);
- set_bit(RXRPC_CALL_DISCONNECTED, &call->flags);
- __rxrpc_set_call_completion(call, RXRPC_CALL_LOCAL_ERROR,
- RX_CALL_DEAD, ret);
+ rxrpc_set_call_completion(call, RXRPC_CALL_LOCAL_ERROR, 0, ret);
_leave(" = c=%08x [err]", call->debug_id);
return call;
}
@@ -427,32 +452,32 @@ void rxrpc_incoming_call(struct rxrpc_sock *rx,
call->call_id = sp->hdr.callNumber;
call->dest_srx.srx_service = sp->hdr.serviceId;
call->cid = sp->hdr.cid;
- call->state = RXRPC_CALL_SERVER_SECURING;
call->cong_tstamp = skb->tstamp;
+ __set_bit(RXRPC_CALL_EXPOSED, &call->flags);
+ rxrpc_set_call_state(call, RXRPC_CALL_SERVER_SECURING);
+
spin_lock(&conn->state_lock);
switch (conn->state) {
case RXRPC_CONN_SERVICE_UNSECURED:
case RXRPC_CONN_SERVICE_CHALLENGING:
- call->state = RXRPC_CALL_SERVER_SECURING;
+ rxrpc_set_call_state(call, RXRPC_CALL_SERVER_SECURING);
break;
case RXRPC_CONN_SERVICE:
- call->state = RXRPC_CALL_SERVER_RECV_REQUEST;
+ rxrpc_set_call_state(call, RXRPC_CALL_SERVER_RECV_REQUEST);
break;
- case RXRPC_CONN_REMOTELY_ABORTED:
- __rxrpc_set_call_completion(call, RXRPC_CALL_REMOTELY_ABORTED,
- conn->abort_code, conn->error);
- break;
- case RXRPC_CONN_LOCALLY_ABORTED:
- __rxrpc_abort_call("CON", call, 1,
- conn->abort_code, conn->error);
+ case RXRPC_CONN_ABORTED:
+ rxrpc_set_call_completion(call, conn->completion,
+ conn->abort_code, conn->error);
break;
default:
BUG();
}
+ rxrpc_get_call(call, rxrpc_call_get_io_thread);
+
/* Set the channel for this call. We don't get channel_lock as we're
* only defending against the data_ready handler (which we're called
* from) and the RESPONSE packet parser (which is only really
@@ -462,7 +487,7 @@ void rxrpc_incoming_call(struct rxrpc_sock *rx,
chan = sp->hdr.cid & RXRPC_CHANNELMASK;
conn->channels[chan].call_counter = call->call_id;
conn->channels[chan].call_id = call->call_id;
- rcu_assign_pointer(conn->channels[chan].call, call);
+ conn->channels[chan].call = call;
spin_unlock(&conn->state_lock);
spin_lock(&conn->peer->lock);
@@ -522,20 +547,17 @@ static void rxrpc_cleanup_ring(struct rxrpc_call *call)
void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call)
{
struct rxrpc_connection *conn = call->conn;
- bool put = false;
+ bool put = false, putu = false;
_enter("{%d,%d}", call->debug_id, refcount_read(&call->ref));
trace_rxrpc_call(call->debug_id, refcount_read(&call->ref),
call->flags, rxrpc_call_see_release);
- ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE);
-
if (test_and_set_bit(RXRPC_CALL_RELEASED, &call->flags))
BUG();
rxrpc_put_call_slot(call);
- del_timer_sync(&call->timer);
/* Make sure we don't get any more notifications */
write_lock(&rx->recvmsg_lock);
@@ -560,7 +582,7 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call)
if (test_and_clear_bit(RXRPC_CALL_HAS_USERID, &call->flags)) {
rb_erase(&call->sock_node, &rx->calls);
memset(&call->sock_node, 0xdd, sizeof(call->sock_node));
- rxrpc_put_call(call, rxrpc_call_put_userid_exists);
+ putu = true;
}
list_del(&call->sock_link);
@@ -568,10 +590,9 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call)
_debug("RELEASE CALL %p (%d CONN %p)", call, call->debug_id, conn);
- if (conn && !test_bit(RXRPC_CALL_DISCONNECTED, &call->flags))
- rxrpc_disconnect_call(call);
- if (call->security)
- call->security->free_call_crypto(call);
+ if (putu)
+ rxrpc_put_call(call, rxrpc_call_put_userid);
+
_leave("");
}
@@ -588,7 +609,8 @@ void rxrpc_release_calls_on_socket(struct rxrpc_sock *rx)
call = list_entry(rx->to_be_accepted.next,
struct rxrpc_call, accept_link);
list_del(&call->accept_link);
- rxrpc_abort_call("SKR", call, 0, RX_CALL_DEAD, -ECONNRESET);
+ rxrpc_propose_abort(call, RX_CALL_DEAD, -ECONNRESET,
+ rxrpc_abort_call_sock_release_tba);
rxrpc_put_call(call, rxrpc_call_put_release_sock_tba);
}
@@ -596,8 +618,8 @@ void rxrpc_release_calls_on_socket(struct rxrpc_sock *rx)
call = list_entry(rx->sock_calls.next,
struct rxrpc_call, sock_link);
rxrpc_get_call(call, rxrpc_call_get_release_sock);
- rxrpc_abort_call("SKT", call, 0, RX_CALL_DEAD, -ECONNRESET);
- rxrpc_send_abort_packet(call);
+ rxrpc_propose_abort(call, RX_CALL_DEAD, -ECONNRESET,
+ rxrpc_abort_call_sock_release);
rxrpc_release_call(rx, call);
rxrpc_put_call(call, rxrpc_call_put_release_sock);
}
@@ -620,7 +642,7 @@ void rxrpc_put_call(struct rxrpc_call *call, enum rxrpc_call_trace why)
dead = __refcount_dec_and_test(&call->ref, &r);
trace_rxrpc_call(debug_id, r - 1, 0, why);
if (dead) {
- ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE);
+ ASSERTCMP(__rxrpc_call_state(call), ==, RXRPC_CALL_COMPLETE);
if (!list_empty(&call->link)) {
spin_lock(&rxnet->call_lock);
@@ -669,6 +691,8 @@ static void rxrpc_destroy_call(struct work_struct *work)
rxrpc_put_txbuf(call->tx_pending, rxrpc_txbuf_put_cleaned);
rxrpc_put_connection(call->conn, rxrpc_conn_put_call);
+ rxrpc_deactivate_bundle(call->bundle);
+ rxrpc_put_bundle(call->bundle, rxrpc_bundle_put_call);
rxrpc_put_peer(call->peer, rxrpc_peer_put_call);
rxrpc_put_local(call->local, rxrpc_local_put_call);
call_rcu(&call->rcu, rxrpc_rcu_free_call);
@@ -681,7 +705,7 @@ void rxrpc_cleanup_call(struct rxrpc_call *call)
{
memset(&call->sock_node, 0xcd, sizeof(call->sock_node));
- ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE);
+ ASSERTCMP(__rxrpc_call_state(call), ==, RXRPC_CALL_COMPLETE);
ASSERT(test_bit(RXRPC_CALL_RELEASED, &call->flags));
del_timer(&call->timer);
@@ -719,7 +743,7 @@ void rxrpc_destroy_all_calls(struct rxrpc_net *rxnet)
pr_err("Call %p still in use (%d,%s,%lx,%lx)!\n",
call, refcount_read(&call->ref),
- rxrpc_call_states[call->state],
+ rxrpc_call_states[__rxrpc_call_state(call)],
call->flags, call->events);
spin_unlock(&rxnet->call_lock);
diff --git a/net/rxrpc/call_state.c b/net/rxrpc/call_state.c
new file mode 100644
index 00000000000000..6afb54373ebbf9
--- /dev/null
+++ b/net/rxrpc/call_state.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Call state changing functions.
+ *
+ * Copyright (C) 2022 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#include "ar-internal.h"
+
+/*
+ * Transition a call to the complete state.
+ */
+bool rxrpc_set_call_completion(struct rxrpc_call *call,
+ enum rxrpc_call_completion compl,
+ u32 abort_code,
+ int error)
+{
+ if (__rxrpc_call_state(call) == RXRPC_CALL_COMPLETE)
+ return false;
+
+ call->abort_code = abort_code;
+ call->error = error;
+ call->completion = compl;
+ /* Allow reader of completion state to operate locklessly */
+ rxrpc_set_call_state(call, RXRPC_CALL_COMPLETE);
+ trace_rxrpc_call_complete(call);
+ wake_up(&call->waitq);
+ rxrpc_notify_socket(call);
+ return true;
+}
+
+/*
+ * Record that a call successfully completed.
+ */
+bool rxrpc_call_completed(struct rxrpc_call *call)
+{
+ return rxrpc_set_call_completion(call, RXRPC_CALL_SUCCEEDED, 0, 0);
+}
+
+/*
+ * Record that a call is locally aborted.
+ */
+bool rxrpc_abort_call(struct rxrpc_call *call, rxrpc_seq_t seq,
+ u32 abort_code, int error, enum rxrpc_abort_reason why)
+{
+ trace_rxrpc_abort(call->debug_id, why, call->cid, call->call_id, seq,
+ abort_code, error);
+ if (!rxrpc_set_call_completion(call, RXRPC_CALL_LOCALLY_ABORTED,
+ abort_code, error))
+ return false;
+ if (test_bit(RXRPC_CALL_EXPOSED, &call->flags))
+ rxrpc_send_abort_packet(call);
+ return true;
+}
+
+/*
+ * Record that a call errored out before even getting off the ground, thereby
+ * setting the state to allow it to be destroyed.
+ */
+void rxrpc_prefail_call(struct rxrpc_call *call, enum rxrpc_call_completion compl,
+ int error)
+{
+ call->abort_code = RX_CALL_DEAD;
+ call->error = error;
+ call->completion = compl;
+ call->_state = RXRPC_CALL_COMPLETE;
+ trace_rxrpc_call_complete(call);
+ WARN_ON_ONCE(__test_and_set_bit(RXRPC_CALL_RELEASED, &call->flags));
+}
diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c
index 87efa0373aed34..981ca5b98bcb90 100644
--- a/net/rxrpc/conn_client.c
+++ b/net/rxrpc/conn_client.c
@@ -34,104 +34,59 @@ __read_mostly unsigned int rxrpc_reap_client_connections = 900;
__read_mostly unsigned long rxrpc_conn_idle_client_expiry = 2 * 60 * HZ;
__read_mostly unsigned long rxrpc_conn_idle_client_fast_expiry = 2 * HZ;
-/*
- * We use machine-unique IDs for our client connections.
- */
-DEFINE_IDR(rxrpc_client_conn_ids);
-static DEFINE_SPINLOCK(rxrpc_conn_id_lock);
-
-static void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle);
-
-/*
- * Get a connection ID and epoch for a client connection from the global pool.
- * The connection struct pointer is then recorded in the idr radix tree. The
- * epoch doesn't change until the client is rebooted (or, at least, unless the
- * module is unloaded).
- */
-static int rxrpc_get_client_connection_id(struct rxrpc_connection *conn,
- gfp_t gfp)
+static void rxrpc_activate_bundle(struct rxrpc_bundle *bundle)
{
- struct rxrpc_net *rxnet = conn->rxnet;
- int id;
-
- _enter("");
-
- idr_preload(gfp);
- spin_lock(&rxrpc_conn_id_lock);
-
- id = idr_alloc_cyclic(&rxrpc_client_conn_ids, conn,
- 1, 0x40000000, GFP_NOWAIT);
- if (id < 0)
- goto error;
-
- spin_unlock(&rxrpc_conn_id_lock);
- idr_preload_end();
-
- conn->proto.epoch = rxnet->epoch;
- conn->proto.cid = id << RXRPC_CIDSHIFT;
- set_bit(RXRPC_CONN_HAS_IDR, &conn->flags);
- _leave(" [CID %x]", conn->proto.cid);
- return 0;
-
-error:
- spin_unlock(&rxrpc_conn_id_lock);
- idr_preload_end();
- _leave(" = %d", id);
- return id;
+ atomic_inc(&bundle->active);
}
/*
- * Release a connection ID for a client connection from the global pool.
+ * Release a connection ID for a client connection.
*/
-static void rxrpc_put_client_connection_id(struct rxrpc_connection *conn)
+static void rxrpc_put_client_connection_id(struct rxrpc_local *local,
+ struct rxrpc_connection *conn)
{
- if (test_bit(RXRPC_CONN_HAS_IDR, &conn->flags)) {
- spin_lock(&rxrpc_conn_id_lock);
- idr_remove(&rxrpc_client_conn_ids,
- conn->proto.cid >> RXRPC_CIDSHIFT);
- spin_unlock(&rxrpc_conn_id_lock);
- }
+ idr_remove(&local->conn_ids, conn->proto.cid >> RXRPC_CIDSHIFT);
}
/*
* Destroy the client connection ID tree.
*/
-void rxrpc_destroy_client_conn_ids(void)
+static void rxrpc_destroy_client_conn_ids(struct rxrpc_local *local)
{
struct rxrpc_connection *conn;
int id;
- if (!idr_is_empty(&rxrpc_client_conn_ids)) {
- idr_for_each_entry(&rxrpc_client_conn_ids, conn, id) {
+ if (!idr_is_empty(&local->conn_ids)) {
+ idr_for_each_entry(&local->conn_ids, conn, id) {
pr_err("AF_RXRPC: Leaked client conn %p {%d}\n",
conn, refcount_read(&conn->ref));
}
BUG();
}
- idr_destroy(&rxrpc_client_conn_ids);
+ idr_destroy(&local->conn_ids);
}
/*
* Allocate a connection bundle.
*/
-static struct rxrpc_bundle *rxrpc_alloc_bundle(struct rxrpc_conn_parameters *cp,
+static struct rxrpc_bundle *rxrpc_alloc_bundle(struct rxrpc_call *call,
gfp_t gfp)
{
struct rxrpc_bundle *bundle;
bundle = kzalloc(sizeof(*bundle), gfp);
if (bundle) {
- bundle->local = cp->local;
- bundle->peer = rxrpc_get_peer(cp->peer, rxrpc_peer_get_bundle);
- bundle->key = cp->key;
- bundle->exclusive = cp->exclusive;
- bundle->upgrade = cp->upgrade;
- bundle->service_id = cp->service_id;
- bundle->security_level = cp->security_level;
+ bundle->local = call->local;
+ bundle->peer = rxrpc_get_peer(call->peer, rxrpc_peer_get_bundle);
+ bundle->key = key_get(call->key);
+ bundle->security = call->security;
+ bundle->exclusive = test_bit(RXRPC_CALL_EXCLUSIVE, &call->flags);
+ bundle->upgrade = test_bit(RXRPC_CALL_UPGRADE, &call->flags);
+ bundle->service_id = call->dest_srx.srx_service;
+ bundle->security_level = call->security_level;
refcount_set(&bundle->ref, 1);
atomic_set(&bundle->active, 1);
- spin_lock_init(&bundle->channel_lock);
INIT_LIST_HEAD(&bundle->waiting_calls);
trace_rxrpc_bundle(bundle->debug_id, 1, rxrpc_bundle_new);
}
@@ -152,84 +107,87 @@ static void rxrpc_free_bundle(struct rxrpc_bundle *bundle)
{
trace_rxrpc_bundle(bundle->debug_id, 1, rxrpc_bundle_free);
rxrpc_put_peer(bundle->peer, rxrpc_peer_put_bundle);
+ key_put(bundle->key);
kfree(bundle);
}
void rxrpc_put_bundle(struct rxrpc_bundle *bundle, enum rxrpc_bundle_trace why)
{
- unsigned int id = bundle->debug_id;
+ unsigned int id;
bool dead;
int r;
- dead = __refcount_dec_and_test(&bundle->ref, &r);
- trace_rxrpc_bundle(id, r - 1, why);
- if (dead)
- rxrpc_free_bundle(bundle);
+ if (bundle) {
+ id = bundle->debug_id;
+ dead = __refcount_dec_and_test(&bundle->ref, &r);
+ trace_rxrpc_bundle(id, r - 1, why);
+ if (dead)
+ rxrpc_free_bundle(bundle);
+ }
+}
+
+/*
+ * Get rid of outstanding client connection preallocations when a local
+ * endpoint is destroyed.
+ */
+void rxrpc_purge_client_connections(struct rxrpc_local *local)
+{
+ rxrpc_destroy_client_conn_ids(local);
}
/*
* Allocate a client connection.
*/
static struct rxrpc_connection *
-rxrpc_alloc_client_connection(struct rxrpc_bundle *bundle, gfp_t gfp)
+rxrpc_alloc_client_connection(struct rxrpc_bundle *bundle)
{
struct rxrpc_connection *conn;
- struct rxrpc_net *rxnet = bundle->local->rxnet;
- int ret;
+ struct rxrpc_local *local = bundle->local;
+ struct rxrpc_net *rxnet = local->rxnet;
+ int id;
_enter("");
- conn = rxrpc_alloc_connection(rxnet, gfp);
- if (!conn) {
- _leave(" = -ENOMEM");
+ conn = rxrpc_alloc_connection(rxnet, GFP_ATOMIC | __GFP_NOWARN);
+ if (!conn)
return ERR_PTR(-ENOMEM);
+
+ id = idr_alloc_cyclic(&local->conn_ids, conn, 1, 0x40000000,
+ GFP_ATOMIC | __GFP_NOWARN);
+ if (id < 0) {
+ kfree(conn);
+ return ERR_PTR(id);
}
refcount_set(&conn->ref, 1);
- conn->bundle = bundle;
- conn->local = bundle->local;
- conn->peer = bundle->peer;
- conn->key = bundle->key;
+ conn->proto.cid = id << RXRPC_CIDSHIFT;
+ conn->proto.epoch = local->rxnet->epoch;
+ conn->out_clientflag = RXRPC_CLIENT_INITIATED;
+ conn->bundle = rxrpc_get_bundle(bundle, rxrpc_bundle_get_client_conn);
+ conn->local = rxrpc_get_local(bundle->local, rxrpc_local_get_client_conn);
+ conn->peer = rxrpc_get_peer(bundle->peer, rxrpc_peer_get_client_conn);
+ conn->key = key_get(bundle->key);
+ conn->security = bundle->security;
conn->exclusive = bundle->exclusive;
conn->upgrade = bundle->upgrade;
conn->orig_service_id = bundle->service_id;
conn->security_level = bundle->security_level;
- conn->out_clientflag = RXRPC_CLIENT_INITIATED;
- conn->state = RXRPC_CONN_CLIENT;
+ conn->state = RXRPC_CONN_CLIENT_UNSECURED;
conn->service_id = conn->orig_service_id;
- ret = rxrpc_get_client_connection_id(conn, gfp);
- if (ret < 0)
- goto error_0;
-
- ret = rxrpc_init_client_conn_security(conn);
- if (ret < 0)
- goto error_1;
+ if (conn->security == &rxrpc_no_security)
+ conn->state = RXRPC_CONN_CLIENT;
atomic_inc(&rxnet->nr_conns);
write_lock(&rxnet->conn_lock);
list_add_tail(&conn->proc_link, &rxnet->conn_proc_list);
write_unlock(&rxnet->conn_lock);
- rxrpc_get_bundle(bundle, rxrpc_bundle_get_client_conn);
- rxrpc_get_peer(conn->peer, rxrpc_peer_get_client_conn);
- rxrpc_get_local(conn->local, rxrpc_local_get_client_conn);
- key_get(conn->key);
-
- trace_rxrpc_conn(conn->debug_id, refcount_read(&conn->ref),
- rxrpc_conn_new_client);
+ rxrpc_see_connection(conn, rxrpc_conn_new_client);
atomic_inc(&rxnet->nr_client_conns);
trace_rxrpc_client(conn, -1, rxrpc_client_alloc);
- _leave(" = %p", conn);
return conn;
-
-error_1:
- rxrpc_put_client_connection_id(conn);
-error_0:
- kfree(conn);
- _leave(" = %d", ret);
- return ERR_PTR(ret);
}
/*
@@ -247,7 +205,8 @@ static bool rxrpc_may_reuse_conn(struct rxrpc_connection *conn)
if (test_bit(RXRPC_CONN_DONT_REUSE, &conn->flags))
goto dont_reuse;
- if (conn->state != RXRPC_CONN_CLIENT ||
+ if ((conn->state != RXRPC_CONN_CLIENT_UNSECURED &&
+ conn->state != RXRPC_CONN_CLIENT) ||
conn->proto.epoch != rxnet->epoch)
goto mark_dont_reuse;
@@ -257,7 +216,7 @@ static bool rxrpc_may_reuse_conn(struct rxrpc_connection *conn)
* times the maximum number of client conns away from the current
* allocation point to try and keep the IDs concentrated.
*/
- id_cursor = idr_get_cursor(&rxrpc_client_conn_ids);
+ id_cursor = idr_get_cursor(&conn->local->conn_ids);
id = conn->proto.cid >> RXRPC_CIDSHIFT;
distance = id - id_cursor;
if (distance < 0)
@@ -278,20 +237,23 @@ dont_reuse:
* Look up the conn bundle that matches the connection parameters, adding it if
* it doesn't yet exist.
*/
-static struct rxrpc_bundle *rxrpc_look_up_bundle(struct rxrpc_conn_parameters *cp,
- gfp_t gfp)
+int rxrpc_look_up_bundle(struct rxrpc_call *call, gfp_t gfp)
{
static atomic_t rxrpc_bundle_id;
struct rxrpc_bundle *bundle, *candidate;
- struct rxrpc_local *local = cp->local;
+ struct rxrpc_local *local = call->local;
struct rb_node *p, **pp, *parent;
long diff;
+ bool upgrade = test_bit(RXRPC_CALL_UPGRADE, &call->flags);
_enter("{%px,%x,%u,%u}",
- cp->peer, key_serial(cp->key), cp->security_level, cp->upgrade);
+ call->peer, key_serial(call->key), call->security_level,
+ upgrade);
- if (cp->exclusive)
- return rxrpc_alloc_bundle(cp, gfp);
+ if (test_bit(RXRPC_CALL_EXCLUSIVE, &call->flags)) {
+ call->bundle = rxrpc_alloc_bundle(call, gfp);
+ return call->bundle ? 0 : -ENOMEM;
+ }
/* First, see if the bundle is already there. */
_debug("search 1");
@@ -300,11 +262,11 @@ static struct rxrpc_bundle *rxrpc_look_up_bundle(struct rxrpc_conn_parameters *c
while (p) {
bundle = rb_entry(p, struct rxrpc_bundle, local_node);
-#define cmp(X) ((long)bundle->X - (long)cp->X)
- diff = (cmp(peer) ?:
- cmp(key) ?:
- cmp(security_level) ?:
- cmp(upgrade));
+#define cmp(X, Y) ((long)(X) - (long)(Y))
+ diff = (cmp(bundle->peer, call->peer) ?:
+ cmp(bundle->key, call->key) ?:
+ cmp(bundle->security_level, call->security_level) ?:
+ cmp(bundle->upgrade, upgrade));
#undef cmp
if (diff < 0)
p = p->rb_left;
@@ -317,9 +279,9 @@ static struct rxrpc_bundle *rxrpc_look_up_bundle(struct rxrpc_conn_parameters *c
_debug("not found");
/* It wasn't. We need to add one. */
- candidate = rxrpc_alloc_bundle(cp, gfp);
+ candidate = rxrpc_alloc_bundle(call, gfp);
if (!candidate)
- return NULL;
+ return -ENOMEM;
_debug("search 2");
spin_lock(&local->client_bundles_lock);
@@ -329,11 +291,11 @@ static struct rxrpc_bundle *rxrpc_look_up_bundle(struct rxrpc_conn_parameters *c
parent = *pp;
bundle = rb_entry(parent, struct rxrpc_bundle, local_node);
-#define cmp(X) ((long)bundle->X - (long)cp->X)
- diff = (cmp(peer) ?:
- cmp(key) ?:
- cmp(security_level) ?:
- cmp(upgrade));
+#define cmp(X, Y) ((long)(X) - (long)(Y))
+ diff = (cmp(bundle->peer, call->peer) ?:
+ cmp(bundle->key, call->key) ?:
+ cmp(bundle->security_level, call->security_level) ?:
+ cmp(bundle->upgrade, upgrade));
#undef cmp
if (diff < 0)
pp = &(*pp)->rb_left;
@@ -347,178 +309,89 @@ static struct rxrpc_bundle *rxrpc_look_up_bundle(struct rxrpc_conn_parameters *c
candidate->debug_id = atomic_inc_return(&rxrpc_bundle_id);
rb_link_node(&candidate->local_node, parent, pp);
rb_insert_color(&candidate->local_node, &local->client_bundles);
- rxrpc_get_bundle(candidate, rxrpc_bundle_get_client_call);
+ call->bundle = rxrpc_get_bundle(candidate, rxrpc_bundle_get_client_call);
spin_unlock(&local->client_bundles_lock);
- _leave(" = %u [new]", candidate->debug_id);
- return candidate;
+ _leave(" = B=%u [new]", call->bundle->debug_id);
+ return 0;
found_bundle_free:
rxrpc_free_bundle(candidate);
found_bundle:
- rxrpc_get_bundle(bundle, rxrpc_bundle_get_client_call);
- atomic_inc(&bundle->active);
+ call->bundle = rxrpc_get_bundle(bundle, rxrpc_bundle_get_client_call);
+ rxrpc_activate_bundle(bundle);
spin_unlock(&local->client_bundles_lock);
- _leave(" = %u [found]", bundle->debug_id);
- return bundle;
-}
-
-/*
- * Create or find a client bundle to use for a call.
- *
- * If we return with a connection, the call will be on its waiting list. It's
- * left to the caller to assign a channel and wake up the call.
- */
-static struct rxrpc_bundle *rxrpc_prep_call(struct rxrpc_sock *rx,
- struct rxrpc_call *call,
- struct rxrpc_conn_parameters *cp,
- struct sockaddr_rxrpc *srx,
- gfp_t gfp)
-{
- struct rxrpc_bundle *bundle;
-
- _enter("{%d,%lx},", call->debug_id, call->user_call_ID);
-
- cp->peer = rxrpc_lookup_peer(rx, cp->local, srx, gfp);
- if (!cp->peer)
- goto error;
-
- call->tx_last_sent = ktime_get_real();
- call->cong_ssthresh = cp->peer->cong_ssthresh;
- if (call->cong_cwnd >= call->cong_ssthresh)
- call->cong_mode = RXRPC_CALL_CONGEST_AVOIDANCE;
- else
- call->cong_mode = RXRPC_CALL_SLOW_START;
- if (cp->upgrade)
- __set_bit(RXRPC_CALL_UPGRADE, &call->flags);
-
- /* Find the client connection bundle. */
- bundle = rxrpc_look_up_bundle(cp, gfp);
- if (!bundle)
- goto error;
-
- /* Get this call queued. Someone else may activate it whilst we're
- * lining up a new connection, but that's fine.
- */
- spin_lock(&bundle->channel_lock);
- list_add_tail(&call->chan_wait_link, &bundle->waiting_calls);
- spin_unlock(&bundle->channel_lock);
-
- _leave(" = [B=%x]", bundle->debug_id);
- return bundle;
-
-error:
- _leave(" = -ENOMEM");
- return ERR_PTR(-ENOMEM);
+ _leave(" = B=%u [found]", call->bundle->debug_id);
+ return 0;
}
/*
* Allocate a new connection and add it into a bundle.
*/
-static void rxrpc_add_conn_to_bundle(struct rxrpc_bundle *bundle, gfp_t gfp)
- __releases(bundle->channel_lock)
+static bool rxrpc_add_conn_to_bundle(struct rxrpc_bundle *bundle,
+ unsigned int slot)
{
- struct rxrpc_connection *candidate = NULL, *old = NULL;
- bool conflict;
- int i;
-
- _enter("");
-
- conflict = bundle->alloc_conn;
- if (!conflict)
- bundle->alloc_conn = true;
- spin_unlock(&bundle->channel_lock);
- if (conflict) {
- _leave(" [conf]");
- return;
- }
-
- candidate = rxrpc_alloc_client_connection(bundle, gfp);
-
- spin_lock(&bundle->channel_lock);
- bundle->alloc_conn = false;
-
- if (IS_ERR(candidate)) {
- bundle->alloc_error = PTR_ERR(candidate);
- spin_unlock(&bundle->channel_lock);
- _leave(" [err %ld]", PTR_ERR(candidate));
- return;
- }
-
- bundle->alloc_error = 0;
-
- for (i = 0; i < ARRAY_SIZE(bundle->conns); i++) {
- unsigned int shift = i * RXRPC_MAXCALLS;
- int j;
-
- old = bundle->conns[i];
- if (!rxrpc_may_reuse_conn(old)) {
- if (old)
- trace_rxrpc_client(old, -1, rxrpc_client_replace);
- candidate->bundle_shift = shift;
- atomic_inc(&bundle->active);
- bundle->conns[i] = candidate;
- for (j = 0; j < RXRPC_MAXCALLS; j++)
- set_bit(shift + j, &bundle->avail_chans);
- candidate = NULL;
- break;
- }
+ struct rxrpc_connection *conn, *old;
+ unsigned int shift = slot * RXRPC_MAXCALLS;
+ unsigned int i;
- old = NULL;
+ old = bundle->conns[slot];
+ if (old) {
+ bundle->conns[slot] = NULL;
+ trace_rxrpc_client(old, -1, rxrpc_client_replace);
+ rxrpc_put_connection(old, rxrpc_conn_put_noreuse);
}
- spin_unlock(&bundle->channel_lock);
-
- if (candidate) {
- _debug("discard C=%x", candidate->debug_id);
- trace_rxrpc_client(candidate, -1, rxrpc_client_duplicate);
- rxrpc_put_connection(candidate, rxrpc_conn_put_discard);
+ conn = rxrpc_alloc_client_connection(bundle);
+ if (IS_ERR(conn)) {
+ bundle->alloc_error = PTR_ERR(conn);
+ return false;
}
- rxrpc_put_connection(old, rxrpc_conn_put_noreuse);
- _leave("");
+ rxrpc_activate_bundle(bundle);
+ conn->bundle_shift = shift;
+ bundle->conns[slot] = conn;
+ for (i = 0; i < RXRPC_MAXCALLS; i++)
+ set_bit(shift + i, &bundle->avail_chans);
+ return true;
}
/*
* Add a connection to a bundle if there are no usable connections or we have
* connections waiting for extra capacity.
*/
-static void rxrpc_maybe_add_conn(struct rxrpc_bundle *bundle, gfp_t gfp)
+static bool rxrpc_bundle_has_space(struct rxrpc_bundle *bundle)
{
- struct rxrpc_call *call;
- int i, usable;
+ int slot = -1, i, usable;
_enter("");
- spin_lock(&bundle->channel_lock);
+ bundle->alloc_error = 0;
/* See if there are any usable connections. */
usable = 0;
- for (i = 0; i < ARRAY_SIZE(bundle->conns); i++)
+ for (i = 0; i < ARRAY_SIZE(bundle->conns); i++) {
if (rxrpc_may_reuse_conn(bundle->conns[i]))
usable++;
-
- if (!usable && !list_empty(&bundle->waiting_calls)) {
- call = list_first_entry(&bundle->waiting_calls,
- struct rxrpc_call, chan_wait_link);
- if (test_bit(RXRPC_CALL_UPGRADE, &call->flags))
- bundle->try_upgrade = true;
+ else if (slot == -1)
+ slot = i;
}
+ if (!usable && bundle->upgrade)
+ bundle->try_upgrade = true;
+
if (!usable)
goto alloc_conn;
if (!bundle->avail_chans &&
!bundle->try_upgrade &&
- !list_empty(&bundle->waiting_calls) &&
usable < ARRAY_SIZE(bundle->conns))
goto alloc_conn;
- spin_unlock(&bundle->channel_lock);
_leave("");
- return;
+ return usable;
alloc_conn:
- return rxrpc_add_conn_to_bundle(bundle, gfp);
+ return slot >= 0 ? rxrpc_add_conn_to_bundle(bundle, slot) : false;
}
/*
@@ -532,11 +405,13 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn,
struct rxrpc_channel *chan = &conn->channels[channel];
struct rxrpc_bundle *bundle = conn->bundle;
struct rxrpc_call *call = list_entry(bundle->waiting_calls.next,
- struct rxrpc_call, chan_wait_link);
+ struct rxrpc_call, wait_link);
u32 call_id = chan->call_counter + 1;
_enter("C=%x,%u", conn->debug_id, channel);
+ list_del_init(&call->wait_link);
+
trace_rxrpc_client(conn, channel, rxrpc_client_chan_activate);
/* Cancel the final ACK on the previous call if it hasn't been sent yet
@@ -546,68 +421,50 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn,
clear_bit(conn->bundle_shift + channel, &bundle->avail_chans);
rxrpc_see_call(call, rxrpc_call_see_activate_client);
- list_del_init(&call->chan_wait_link);
- call->peer = rxrpc_get_peer(conn->peer, rxrpc_peer_get_activate_call);
call->conn = rxrpc_get_connection(conn, rxrpc_conn_get_activate_call);
call->cid = conn->proto.cid | channel;
call->call_id = call_id;
call->dest_srx.srx_service = conn->service_id;
-
- trace_rxrpc_connect_call(call);
-
- write_lock(&call->state_lock);
- call->state = RXRPC_CALL_CLIENT_SEND_REQUEST;
- write_unlock(&call->state_lock);
-
- /* Paired with the read barrier in rxrpc_connect_call(). This orders
- * cid and epoch in the connection wrt to call_id without the need to
- * take the channel_lock.
- *
- * We provisionally assign a callNumber at this point, but we don't
- * confirm it until the call is about to be exposed.
- *
- * TODO: Pair with a barrier in the data_ready handler when that looks
- * at the call ID through a connection channel.
- */
- smp_wmb();
+ call->cong_ssthresh = call->peer->cong_ssthresh;
+ if (call->cong_cwnd >= call->cong_ssthresh)
+ call->cong_mode = RXRPC_CALL_CONGEST_AVOIDANCE;
+ else
+ call->cong_mode = RXRPC_CALL_SLOW_START;
chan->call_id = call_id;
chan->call_debug_id = call->debug_id;
- rcu_assign_pointer(chan->call, call);
+ chan->call = call;
+
+ rxrpc_see_call(call, rxrpc_call_see_connected);
+ trace_rxrpc_connect_call(call);
+ call->tx_last_sent = ktime_get_real();
+ rxrpc_start_call_timer(call);
+ rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_SEND_REQUEST);
wake_up(&call->waitq);
}
/*
* Remove a connection from the idle list if it's on it.
*/
-static void rxrpc_unidle_conn(struct rxrpc_bundle *bundle, struct rxrpc_connection *conn)
+static void rxrpc_unidle_conn(struct rxrpc_connection *conn)
{
- struct rxrpc_net *rxnet = bundle->local->rxnet;
- bool drop_ref;
-
if (!list_empty(&conn->cache_link)) {
- drop_ref = false;
- spin_lock(&rxnet->client_conn_cache_lock);
- if (!list_empty(&conn->cache_link)) {
- list_del_init(&conn->cache_link);
- drop_ref = true;
- }
- spin_unlock(&rxnet->client_conn_cache_lock);
- if (drop_ref)
- rxrpc_put_connection(conn, rxrpc_conn_put_unidle);
+ list_del_init(&conn->cache_link);
+ rxrpc_put_connection(conn, rxrpc_conn_put_unidle);
}
}
/*
- * Assign channels and callNumbers to waiting calls with channel_lock
- * held by caller.
+ * Assign channels and callNumbers to waiting calls.
*/
-static void rxrpc_activate_channels_locked(struct rxrpc_bundle *bundle)
+static void rxrpc_activate_channels(struct rxrpc_bundle *bundle)
{
struct rxrpc_connection *conn;
unsigned long avail, mask;
unsigned int channel, slot;
+ trace_rxrpc_client(NULL, -1, rxrpc_client_activate_chans);
+
if (bundle->try_upgrade)
mask = 1;
else
@@ -627,7 +484,7 @@ static void rxrpc_activate_channels_locked(struct rxrpc_bundle *bundle)
if (bundle->try_upgrade)
set_bit(RXRPC_CONN_PROBING_FOR_UPGRADE, &conn->flags);
- rxrpc_unidle_conn(bundle, conn);
+ rxrpc_unidle_conn(conn);
channel &= (RXRPC_MAXCALLS - 1);
conn->act_chans |= 1 << channel;
@@ -636,132 +493,24 @@ static void rxrpc_activate_channels_locked(struct rxrpc_bundle *bundle)
}
/*
- * Assign channels and callNumbers to waiting calls.
- */
-static void rxrpc_activate_channels(struct rxrpc_bundle *bundle)
-{
- _enter("B=%x", bundle->debug_id);
-
- trace_rxrpc_client(NULL, -1, rxrpc_client_activate_chans);
-
- if (!bundle->avail_chans)
- return;
-
- spin_lock(&bundle->channel_lock);
- rxrpc_activate_channels_locked(bundle);
- spin_unlock(&bundle->channel_lock);
- _leave("");
-}
-
-/*
- * Wait for a callNumber and a channel to be granted to a call.
- */
-static int rxrpc_wait_for_channel(struct rxrpc_bundle *bundle,
- struct rxrpc_call *call, gfp_t gfp)
-{
- DECLARE_WAITQUEUE(myself, current);
- int ret = 0;
-
- _enter("%d", call->debug_id);
-
- if (!gfpflags_allow_blocking(gfp)) {
- rxrpc_maybe_add_conn(bundle, gfp);
- rxrpc_activate_channels(bundle);
- ret = bundle->alloc_error ?: -EAGAIN;
- goto out;
- }
-
- add_wait_queue_exclusive(&call->waitq, &myself);
- for (;;) {
- rxrpc_maybe_add_conn(bundle, gfp);
- rxrpc_activate_channels(bundle);
- ret = bundle->alloc_error;
- if (ret < 0)
- break;
-
- switch (call->interruptibility) {
- case RXRPC_INTERRUPTIBLE:
- case RXRPC_PREINTERRUPTIBLE:
- set_current_state(TASK_INTERRUPTIBLE);
- break;
- case RXRPC_UNINTERRUPTIBLE:
- default:
- set_current_state(TASK_UNINTERRUPTIBLE);
- break;
- }
- if (READ_ONCE(call->state) != RXRPC_CALL_CLIENT_AWAIT_CONN)
- break;
- if ((call->interruptibility == RXRPC_INTERRUPTIBLE ||
- call->interruptibility == RXRPC_PREINTERRUPTIBLE) &&
- signal_pending(current)) {
- ret = -ERESTARTSYS;
- break;
- }
- schedule();
- }
- remove_wait_queue(&call->waitq, &myself);
- __set_current_state(TASK_RUNNING);
-
-out:
- _leave(" = %d", ret);
- return ret;
-}
-
-/*
- * find a connection for a call
- * - called in process context with IRQs enabled
+ * Connect waiting channels (called from the I/O thread).
*/
-int rxrpc_connect_call(struct rxrpc_sock *rx,
- struct rxrpc_call *call,
- struct rxrpc_conn_parameters *cp,
- struct sockaddr_rxrpc *srx,
- gfp_t gfp)
+void rxrpc_connect_client_calls(struct rxrpc_local *local)
{
- struct rxrpc_bundle *bundle;
- struct rxrpc_net *rxnet = cp->local->rxnet;
- int ret = 0;
-
- _enter("{%d,%lx},", call->debug_id, call->user_call_ID);
-
- rxrpc_discard_expired_client_conns(&rxnet->client_conn_reaper);
+ struct rxrpc_call *call;
- bundle = rxrpc_prep_call(rx, call, cp, srx, gfp);
- if (IS_ERR(bundle)) {
- ret = PTR_ERR(bundle);
- goto out;
- }
+ while ((call = list_first_entry_or_null(&local->new_client_calls,
+ struct rxrpc_call, wait_link))
+ ) {
+ struct rxrpc_bundle *bundle = call->bundle;
- if (call->state == RXRPC_CALL_CLIENT_AWAIT_CONN) {
- ret = rxrpc_wait_for_channel(bundle, call, gfp);
- if (ret < 0)
- goto wait_failed;
- }
+ spin_lock(&local->client_call_lock);
+ list_move_tail(&call->wait_link, &bundle->waiting_calls);
+ spin_unlock(&local->client_call_lock);
-granted_channel:
- /* Paired with the write barrier in rxrpc_activate_one_channel(). */
- smp_rmb();
-
-out_put_bundle:
- rxrpc_deactivate_bundle(bundle);
- rxrpc_put_bundle(bundle, rxrpc_bundle_get_client_call);
-out:
- _leave(" = %d", ret);
- return ret;
-
-wait_failed:
- spin_lock(&bundle->channel_lock);
- list_del_init(&call->chan_wait_link);
- spin_unlock(&bundle->channel_lock);
-
- if (call->state != RXRPC_CALL_CLIENT_AWAIT_CONN) {
- ret = 0;
- goto granted_channel;
+ if (rxrpc_bundle_has_space(bundle))
+ rxrpc_activate_channels(bundle);
}
-
- trace_rxrpc_client(call->conn, ret, rxrpc_client_chan_wait_failed);
- rxrpc_set_call_completion(call, RXRPC_CALL_LOCAL_ERROR, 0, ret);
- rxrpc_disconnect_client_call(bundle, call);
- goto out_put_bundle;
}
/*
@@ -794,14 +543,14 @@ void rxrpc_expose_client_call(struct rxrpc_call *call)
/*
* Set the reap timer.
*/
-static void rxrpc_set_client_reap_timer(struct rxrpc_net *rxnet)
+static void rxrpc_set_client_reap_timer(struct rxrpc_local *local)
{
- if (!rxnet->kill_all_client_conns) {
+ if (!local->kill_all_client_conns) {
unsigned long now = jiffies;
unsigned long reap_at = now + rxrpc_conn_idle_client_expiry;
- if (rxnet->live)
- timer_reduce(&rxnet->client_conn_reap_timer, reap_at);
+ if (local->rxnet->live)
+ timer_reduce(&local->client_conn_reap_timer, reap_at);
}
}
@@ -812,16 +561,13 @@ void rxrpc_disconnect_client_call(struct rxrpc_bundle *bundle, struct rxrpc_call
{
struct rxrpc_connection *conn;
struct rxrpc_channel *chan = NULL;
- struct rxrpc_net *rxnet = bundle->local->rxnet;
+ struct rxrpc_local *local = bundle->local;
unsigned int channel;
bool may_reuse;
u32 cid;
_enter("c=%x", call->debug_id);
- spin_lock(&bundle->channel_lock);
- set_bit(RXRPC_CALL_DISCONNECTED, &call->flags);
-
/* Calls that have never actually been assigned a channel can simply be
* discarded.
*/
@@ -830,8 +576,8 @@ void rxrpc_disconnect_client_call(struct rxrpc_bundle *bundle, struct rxrpc_call
_debug("call is waiting");
ASSERTCMP(call->call_id, ==, 0);
ASSERT(!test_bit(RXRPC_CALL_EXPOSED, &call->flags));
- list_del_init(&call->chan_wait_link);
- goto out;
+ list_del_init(&call->wait_link);
+ return;
}
cid = call->cid;
@@ -839,10 +585,8 @@ void rxrpc_disconnect_client_call(struct rxrpc_bundle *bundle, struct rxrpc_call
chan = &conn->channels[channel];
trace_rxrpc_client(conn, channel, rxrpc_client_chan_disconnect);
- if (rcu_access_pointer(chan->call) != call) {
- spin_unlock(&bundle->channel_lock);
- BUG();
- }
+ if (WARN_ON(chan->call != call))
+ return;
may_reuse = rxrpc_may_reuse_conn(conn);
@@ -863,16 +607,15 @@ void rxrpc_disconnect_client_call(struct rxrpc_bundle *bundle, struct rxrpc_call
trace_rxrpc_client(conn, channel, rxrpc_client_to_active);
bundle->try_upgrade = false;
if (may_reuse)
- rxrpc_activate_channels_locked(bundle);
+ rxrpc_activate_channels(bundle);
}
-
}
/* See if we can pass the channel directly to another call. */
if (may_reuse && !list_empty(&bundle->waiting_calls)) {
trace_rxrpc_client(conn, channel, rxrpc_client_chan_pass);
rxrpc_activate_one_channel(conn, channel);
- goto out;
+ return;
}
/* Schedule the final ACK to be transmitted in a short while so that it
@@ -890,7 +633,7 @@ void rxrpc_disconnect_client_call(struct rxrpc_bundle *bundle, struct rxrpc_call
}
/* Deactivate the channel. */
- rcu_assign_pointer(chan->call, NULL);
+ chan->call = NULL;
set_bit(conn->bundle_shift + channel, &conn->bundle->avail_chans);
conn->act_chans &= ~(1 << channel);
@@ -903,17 +646,10 @@ void rxrpc_disconnect_client_call(struct rxrpc_bundle *bundle, struct rxrpc_call
conn->idle_timestamp = jiffies;
rxrpc_get_connection(conn, rxrpc_conn_get_idle);
- spin_lock(&rxnet->client_conn_cache_lock);
- list_move_tail(&conn->cache_link, &rxnet->idle_client_conns);
- spin_unlock(&rxnet->client_conn_cache_lock);
+ list_move_tail(&conn->cache_link, &local->idle_client_conns);
- rxrpc_set_client_reap_timer(rxnet);
+ rxrpc_set_client_reap_timer(local);
}
-
-out:
- spin_unlock(&bundle->channel_lock);
- _leave("");
- return;
}
/*
@@ -923,7 +659,6 @@ static void rxrpc_unbundle_conn(struct rxrpc_connection *conn)
{
struct rxrpc_bundle *bundle = conn->bundle;
unsigned int bindex;
- bool need_drop = false;
int i;
_enter("C=%x", conn->debug_id);
@@ -931,18 +666,13 @@ static void rxrpc_unbundle_conn(struct rxrpc_connection *conn)
if (conn->flags & RXRPC_CONN_FINAL_ACK_MASK)
rxrpc_process_delayed_final_acks(conn, true);
- spin_lock(&bundle->channel_lock);
bindex = conn->bundle_shift / RXRPC_MAXCALLS;
if (bundle->conns[bindex] == conn) {
_debug("clear slot %u", bindex);
bundle->conns[bindex] = NULL;
for (i = 0; i < RXRPC_MAXCALLS; i++)
clear_bit(conn->bundle_shift + i, &bundle->avail_chans);
- need_drop = true;
- }
- spin_unlock(&bundle->channel_lock);
-
- if (need_drop) {
+ rxrpc_put_client_connection_id(bundle->local, conn);
rxrpc_deactivate_bundle(bundle);
rxrpc_put_connection(conn, rxrpc_conn_put_unbundle);
}
@@ -951,11 +681,15 @@ static void rxrpc_unbundle_conn(struct rxrpc_connection *conn)
/*
* Drop the active count on a bundle.
*/
-static void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle)
+void rxrpc_deactivate_bundle(struct rxrpc_bundle *bundle)
{
- struct rxrpc_local *local = bundle->local;
+ struct rxrpc_local *local;
bool need_put = false;
+ if (!bundle)
+ return;
+
+ local = bundle->local;
if (atomic_dec_and_lock(&bundle->active, &local->client_bundles_lock)) {
if (!bundle->exclusive) {
_debug("erase bundle");
@@ -982,7 +716,7 @@ void rxrpc_kill_client_conn(struct rxrpc_connection *conn)
trace_rxrpc_client(conn, -1, rxrpc_client_cleanup);
atomic_dec(&rxnet->nr_client_conns);
- rxrpc_put_client_connection_id(conn);
+ rxrpc_put_client_connection_id(local, conn);
}
/*
@@ -992,42 +726,26 @@ void rxrpc_kill_client_conn(struct rxrpc_connection *conn)
* This may be called from conn setup or from a work item so cannot be
* considered non-reentrant.
*/
-void rxrpc_discard_expired_client_conns(struct work_struct *work)
+void rxrpc_discard_expired_client_conns(struct rxrpc_local *local)
{
struct rxrpc_connection *conn;
- struct rxrpc_net *rxnet =
- container_of(work, struct rxrpc_net, client_conn_reaper);
unsigned long expiry, conn_expires_at, now;
unsigned int nr_conns;
_enter("");
- if (list_empty(&rxnet->idle_client_conns)) {
- _leave(" [empty]");
- return;
- }
-
- /* Don't double up on the discarding */
- if (!mutex_trylock(&rxnet->client_conn_discard_lock)) {
- _leave(" [already]");
- return;
- }
-
/* We keep an estimate of what the number of conns ought to be after
* we've discarded some so that we don't overdo the discarding.
*/
- nr_conns = atomic_read(&rxnet->nr_client_conns);
+ nr_conns = atomic_read(&local->rxnet->nr_client_conns);
next:
- spin_lock(&rxnet->client_conn_cache_lock);
-
- if (list_empty(&rxnet->idle_client_conns))
- goto out;
-
- conn = list_entry(rxnet->idle_client_conns.next,
- struct rxrpc_connection, cache_link);
+ conn = list_first_entry_or_null(&local->idle_client_conns,
+ struct rxrpc_connection, cache_link);
+ if (!conn)
+ return;
- if (!rxnet->kill_all_client_conns) {
+ if (!local->kill_all_client_conns) {
/* If the number of connections is over the reap limit, we
* expedite discard by reducing the expiry timeout. We must,
* however, have at least a short grace period to be able to do
@@ -1050,8 +768,6 @@ next:
trace_rxrpc_client(conn, -1, rxrpc_client_discard);
list_del_init(&conn->cache_link);
- spin_unlock(&rxnet->client_conn_cache_lock);
-
rxrpc_unbundle_conn(conn);
/* Drop the ->cache_link ref */
rxrpc_put_connection(conn, rxrpc_conn_put_discard_idle);
@@ -1068,31 +784,8 @@ not_yet_expired:
* then things get messier.
*/
_debug("not yet");
- if (!rxnet->kill_all_client_conns)
- timer_reduce(&rxnet->client_conn_reap_timer, conn_expires_at);
-
-out:
- spin_unlock(&rxnet->client_conn_cache_lock);
- mutex_unlock(&rxnet->client_conn_discard_lock);
- _leave("");
-}
-
-/*
- * Preemptively destroy all the client connection records rather than waiting
- * for them to time out
- */
-void rxrpc_destroy_all_client_connections(struct rxrpc_net *rxnet)
-{
- _enter("");
-
- spin_lock(&rxnet->client_conn_cache_lock);
- rxnet->kill_all_client_conns = true;
- spin_unlock(&rxnet->client_conn_cache_lock);
-
- del_timer_sync(&rxnet->client_conn_reap_timer);
-
- if (!rxrpc_queue_work(&rxnet->client_conn_reaper))
- _debug("destroy: queue failed");
+ if (!local->kill_all_client_conns)
+ timer_reduce(&local->client_conn_reap_timer, conn_expires_at);
_leave("");
}
@@ -1102,29 +795,19 @@ void rxrpc_destroy_all_client_connections(struct rxrpc_net *rxnet)
*/
void rxrpc_clean_up_local_conns(struct rxrpc_local *local)
{
- struct rxrpc_connection *conn, *tmp;
- struct rxrpc_net *rxnet = local->rxnet;
- LIST_HEAD(graveyard);
+ struct rxrpc_connection *conn;
_enter("");
- spin_lock(&rxnet->client_conn_cache_lock);
-
- list_for_each_entry_safe(conn, tmp, &rxnet->idle_client_conns,
- cache_link) {
- if (conn->local == local) {
- atomic_dec(&conn->active);
- trace_rxrpc_client(conn, -1, rxrpc_client_discard);
- list_move(&conn->cache_link, &graveyard);
- }
- }
+ local->kill_all_client_conns = true;
- spin_unlock(&rxnet->client_conn_cache_lock);
+ del_timer_sync(&local->client_conn_reap_timer);
- while (!list_empty(&graveyard)) {
- conn = list_entry(graveyard.next,
- struct rxrpc_connection, cache_link);
+ while ((conn = list_first_entry_or_null(&local->idle_client_conns,
+ struct rxrpc_connection, cache_link))) {
list_del_init(&conn->cache_link);
+ atomic_dec(&conn->active);
+ trace_rxrpc_client(conn, -1, rxrpc_client_discard);
rxrpc_unbundle_conn(conn);
rxrpc_put_connection(conn, rxrpc_conn_put_local_dead);
}
diff --git a/net/rxrpc/conn_event.c b/net/rxrpc/conn_event.c
index 480364bcbf8557..44414e724415e9 100644
--- a/net/rxrpc/conn_event.c
+++ b/net/rxrpc/conn_event.c
@@ -17,11 +17,65 @@
#include "ar-internal.h"
/*
+ * Set the completion state on an aborted connection.
+ */
+static bool rxrpc_set_conn_aborted(struct rxrpc_connection *conn, struct sk_buff *skb,
+ s32 abort_code, int err,
+ enum rxrpc_call_completion compl)
+{
+ bool aborted = false;
+
+ if (conn->state != RXRPC_CONN_ABORTED) {
+ spin_lock(&conn->state_lock);
+ if (conn->state != RXRPC_CONN_ABORTED) {
+ conn->abort_code = abort_code;
+ conn->error = err;
+ conn->completion = compl;
+ /* Order the abort info before the state change. */
+ smp_store_release(&conn->state, RXRPC_CONN_ABORTED);
+ set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags);
+ set_bit(RXRPC_CONN_EV_ABORT_CALLS, &conn->events);
+ aborted = true;
+ }
+ spin_unlock(&conn->state_lock);
+ }
+
+ return aborted;
+}
+
+/*
+ * Mark a socket buffer to indicate that the connection it's on should be aborted.
+ */
+int rxrpc_abort_conn(struct rxrpc_connection *conn, struct sk_buff *skb,
+ s32 abort_code, int err, enum rxrpc_abort_reason why)
+{
+ struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+
+ if (rxrpc_set_conn_aborted(conn, skb, abort_code, err,
+ RXRPC_CALL_LOCALLY_ABORTED)) {
+ trace_rxrpc_abort(0, why, sp->hdr.cid, sp->hdr.callNumber,
+ sp->hdr.seq, abort_code, err);
+ rxrpc_poke_conn(conn, rxrpc_conn_get_poke_abort);
+ }
+ return -EPROTO;
+}
+
+/*
+ * Mark a connection as being remotely aborted.
+ */
+static bool rxrpc_input_conn_abort(struct rxrpc_connection *conn,
+ struct sk_buff *skb)
+{
+ return rxrpc_set_conn_aborted(conn, skb, skb->priority, -ECONNABORTED,
+ RXRPC_CALL_REMOTELY_ABORTED);
+}
+
+/*
* Retransmit terminal ACK or ABORT of the previous call.
*/
-static void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn,
- struct sk_buff *skb,
- unsigned int channel)
+void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn,
+ struct sk_buff *skb,
+ unsigned int channel)
{
struct rxrpc_skb_priv *sp = skb ? rxrpc_skb(skb) : NULL;
struct rxrpc_channel *chan;
@@ -46,9 +100,7 @@ static void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn,
/* If the last call got moved on whilst we were waiting to run, just
* ignore this packet.
*/
- call_id = READ_ONCE(chan->last_call);
- /* Sync with __rxrpc_disconnect_call() */
- smp_rmb();
+ call_id = chan->last_call;
if (skb && call_id != sp->hdr.callNumber)
return;
@@ -65,9 +117,12 @@ static void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn,
iov[2].iov_base = &ack_info;
iov[2].iov_len = sizeof(ack_info);
+ serial = atomic_inc_return(&conn->serial);
+
pkt.whdr.epoch = htonl(conn->proto.epoch);
pkt.whdr.cid = htonl(conn->proto.cid | channel);
pkt.whdr.callNumber = htonl(call_id);
+ pkt.whdr.serial = htonl(serial);
pkt.whdr.seq = 0;
pkt.whdr.type = chan->last_type;
pkt.whdr.flags = conn->out_clientflag;
@@ -104,31 +159,15 @@ static void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn,
iov[0].iov_len += sizeof(pkt.ack);
len += sizeof(pkt.ack) + 3 + sizeof(ack_info);
ioc = 3;
- break;
-
- default:
- return;
- }
-
- /* Resync with __rxrpc_disconnect_call() and check that the last call
- * didn't get advanced whilst we were filling out the packets.
- */
- smp_rmb();
- if (READ_ONCE(chan->last_call) != call_id)
- return;
-
- serial = atomic_inc_return(&conn->serial);
- pkt.whdr.serial = htonl(serial);
- switch (chan->last_type) {
- case RXRPC_PACKET_TYPE_ABORT:
- break;
- case RXRPC_PACKET_TYPE_ACK:
trace_rxrpc_tx_ack(chan->call_debug_id, serial,
ntohl(pkt.ack.firstPacket),
ntohl(pkt.ack.serial),
pkt.ack.reason, 0);
break;
+
+ default:
+ return;
}
ret = kernel_sendmsg(conn->local->socket, &msg, iov, ioc, len);
@@ -146,131 +185,34 @@ static void rxrpc_conn_retransmit_call(struct rxrpc_connection *conn,
/*
* pass a connection-level abort onto all calls on that connection
*/
-static void rxrpc_abort_calls(struct rxrpc_connection *conn,
- enum rxrpc_call_completion compl,
- rxrpc_serial_t serial)
+static void rxrpc_abort_calls(struct rxrpc_connection *conn)
{
struct rxrpc_call *call;
int i;
_enter("{%d},%x", conn->debug_id, conn->abort_code);
- spin_lock(&conn->bundle->channel_lock);
-
for (i = 0; i < RXRPC_MAXCALLS; i++) {
- call = rcu_dereference_protected(
- conn->channels[i].call,
- lockdep_is_held(&conn->bundle->channel_lock));
- if (call) {
- if (compl == RXRPC_CALL_LOCALLY_ABORTED)
- trace_rxrpc_abort(call->debug_id,
- "CON", call->cid,
- call->call_id, 0,
+ call = conn->channels[i].call;
+ if (call)
+ rxrpc_set_call_completion(call,
+ conn->completion,
conn->abort_code,
conn->error);
- else
- trace_rxrpc_rx_abort(call, serial,
- conn->abort_code);
- rxrpc_set_call_completion(call, compl,
- conn->abort_code,
- conn->error);
- }
}
- spin_unlock(&conn->bundle->channel_lock);
_leave("");
}
/*
- * generate a connection-level abort
- */
-static int rxrpc_abort_connection(struct rxrpc_connection *conn,
- int error, u32 abort_code)
-{
- struct rxrpc_wire_header whdr;
- struct msghdr msg;
- struct kvec iov[2];
- __be32 word;
- size_t len;
- u32 serial;
- int ret;
-
- _enter("%d,,%u,%u", conn->debug_id, error, abort_code);
-
- /* generate a connection-level abort */
- spin_lock(&conn->state_lock);
- if (conn->state >= RXRPC_CONN_REMOTELY_ABORTED) {
- spin_unlock(&conn->state_lock);
- _leave(" = 0 [already dead]");
- return 0;
- }
-
- conn->error = error;
- conn->abort_code = abort_code;
- conn->state = RXRPC_CONN_LOCALLY_ABORTED;
- set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags);
- spin_unlock(&conn->state_lock);
-
- msg.msg_name = &conn->peer->srx.transport;
- msg.msg_namelen = conn->peer->srx.transport_len;
- msg.msg_control = NULL;
- msg.msg_controllen = 0;
- msg.msg_flags = 0;
-
- whdr.epoch = htonl(conn->proto.epoch);
- whdr.cid = htonl(conn->proto.cid);
- whdr.callNumber = 0;
- whdr.seq = 0;
- whdr.type = RXRPC_PACKET_TYPE_ABORT;
- whdr.flags = conn->out_clientflag;
- whdr.userStatus = 0;
- whdr.securityIndex = conn->security_ix;
- whdr._rsvd = 0;
- whdr.serviceId = htons(conn->service_id);
-
- word = htonl(conn->abort_code);
-
- iov[0].iov_base = &whdr;
- iov[0].iov_len = sizeof(whdr);
- iov[1].iov_base = &word;
- iov[1].iov_len = sizeof(word);
-
- len = iov[0].iov_len + iov[1].iov_len;
-
- serial = atomic_inc_return(&conn->serial);
- rxrpc_abort_calls(conn, RXRPC_CALL_LOCALLY_ABORTED, serial);
- whdr.serial = htonl(serial);
-
- ret = kernel_sendmsg(conn->local->socket, &msg, iov, 2, len);
- if (ret < 0) {
- trace_rxrpc_tx_fail(conn->debug_id, serial, ret,
- rxrpc_tx_point_conn_abort);
- _debug("sendmsg failed: %d", ret);
- return -EAGAIN;
- }
-
- trace_rxrpc_tx_packet(conn->debug_id, &whdr, rxrpc_tx_point_conn_abort);
-
- conn->peer->last_tx_at = ktime_get_seconds();
-
- _leave(" = 0");
- return 0;
-}
-
-/*
* mark a call as being on a now-secured channel
* - must be called with BH's disabled.
*/
static void rxrpc_call_is_secure(struct rxrpc_call *call)
{
- _enter("%p", call);
- if (call) {
- write_lock(&call->state_lock);
- if (call->state == RXRPC_CALL_SERVER_SECURING) {
- call->state = RXRPC_CALL_SERVER_RECV_REQUEST;
- rxrpc_notify_socket(call);
- }
- write_unlock(&call->state_lock);
+ if (call && __rxrpc_call_state(call) == RXRPC_CALL_SERVER_SECURING) {
+ rxrpc_set_call_state(call, RXRPC_CALL_SERVER_RECV_REQUEST);
+ rxrpc_notify_socket(call);
}
}
@@ -278,44 +220,22 @@ static void rxrpc_call_is_secure(struct rxrpc_call *call)
* connection-level Rx packet processor
*/
static int rxrpc_process_event(struct rxrpc_connection *conn,
- struct sk_buff *skb,
- u32 *_abort_code)
+ struct sk_buff *skb)
{
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
- int loop, ret;
+ int ret;
- if (conn->state >= RXRPC_CONN_REMOTELY_ABORTED) {
- _leave(" = -ECONNABORTED [%u]", conn->state);
+ if (conn->state == RXRPC_CONN_ABORTED)
return -ECONNABORTED;
- }
_enter("{%d},{%u,%%%u},", conn->debug_id, sp->hdr.type, sp->hdr.serial);
switch (sp->hdr.type) {
- case RXRPC_PACKET_TYPE_DATA:
- case RXRPC_PACKET_TYPE_ACK:
- rxrpc_conn_retransmit_call(conn, skb,
- sp->hdr.cid & RXRPC_CHANNELMASK);
- return 0;
-
- case RXRPC_PACKET_TYPE_BUSY:
- /* Just ignore BUSY packets for now. */
- return 0;
-
- case RXRPC_PACKET_TYPE_ABORT:
- conn->error = -ECONNABORTED;
- conn->abort_code = skb->priority;
- conn->state = RXRPC_CONN_REMOTELY_ABORTED;
- set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags);
- rxrpc_abort_calls(conn, RXRPC_CALL_REMOTELY_ABORTED, sp->hdr.serial);
- return -ECONNABORTED;
-
case RXRPC_PACKET_TYPE_CHALLENGE:
- return conn->security->respond_to_challenge(conn, skb,
- _abort_code);
+ return conn->security->respond_to_challenge(conn, skb);
case RXRPC_PACKET_TYPE_RESPONSE:
- ret = conn->security->verify_response(conn, skb, _abort_code);
+ ret = conn->security->verify_response(conn, skb);
if (ret < 0)
return ret;
@@ -324,27 +244,25 @@ static int rxrpc_process_event(struct rxrpc_connection *conn,
if (ret < 0)
return ret;
- spin_lock(&conn->bundle->channel_lock);
spin_lock(&conn->state_lock);
-
- if (conn->state == RXRPC_CONN_SERVICE_CHALLENGING) {
+ if (conn->state == RXRPC_CONN_SERVICE_CHALLENGING)
conn->state = RXRPC_CONN_SERVICE;
- spin_unlock(&conn->state_lock);
- for (loop = 0; loop < RXRPC_MAXCALLS; loop++)
- rxrpc_call_is_secure(
- rcu_dereference_protected(
- conn->channels[loop].call,
- lockdep_is_held(&conn->bundle->channel_lock)));
- } else {
- spin_unlock(&conn->state_lock);
- }
+ spin_unlock(&conn->state_lock);
- spin_unlock(&conn->bundle->channel_lock);
+ if (conn->state == RXRPC_CONN_SERVICE) {
+ /* Offload call state flipping to the I/O thread. As
+ * we've already received the packet, put it on the
+ * front of the queue.
+ */
+ skb->mark = RXRPC_SKB_MARK_SERVICE_CONN_SECURED;
+ rxrpc_get_skb(skb, rxrpc_skb_get_conn_secured);
+ skb_queue_head(&conn->local->rx_queue, skb);
+ rxrpc_wake_up_io_thread(conn->local);
+ }
return 0;
default:
- trace_rxrpc_rx_eproto(NULL, sp->hdr.serial,
- tracepoint_string("bad_conn_pkt"));
+ WARN_ON_ONCE(1);
return -EPROTO;
}
}
@@ -354,26 +272,9 @@ static int rxrpc_process_event(struct rxrpc_connection *conn,
*/
static void rxrpc_secure_connection(struct rxrpc_connection *conn)
{
- u32 abort_code;
- int ret;
-
- _enter("{%d}", conn->debug_id);
-
- ASSERT(conn->security_ix != 0);
-
- if (conn->security->issue_challenge(conn) < 0) {
- abort_code = RX_CALL_DEAD;
- ret = -ENOMEM;
- goto abort;
- }
-
- _leave("");
- return;
-
-abort:
- _debug("abort %d, %d", ret, abort_code);
- rxrpc_abort_connection(conn, ret, abort_code);
- _leave(" [aborted]");
+ if (conn->security->issue_challenge(conn) < 0)
+ rxrpc_abort_conn(conn, NULL, RX_CALL_DEAD, -ENOMEM,
+ rxrpc_abort_nomem);
}
/*
@@ -395,9 +296,7 @@ again:
if (!test_bit(RXRPC_CONN_FINAL_ACK_0 + channel, &conn->flags))
continue;
- smp_rmb(); /* vs rxrpc_disconnect_client_call */
- ack_at = READ_ONCE(chan->final_ack_at);
-
+ ack_at = chan->final_ack_at;
if (time_before(j, ack_at) && !force) {
if (time_before(ack_at, next_j)) {
next_j = ack_at;
@@ -424,47 +323,27 @@ again:
static void rxrpc_do_process_connection(struct rxrpc_connection *conn)
{
struct sk_buff *skb;
- u32 abort_code = RX_PROTOCOL_ERROR;
int ret;
if (test_and_clear_bit(RXRPC_CONN_EV_CHALLENGE, &conn->events))
rxrpc_secure_connection(conn);
- /* Process delayed ACKs whose time has come. */
- if (conn->flags & RXRPC_CONN_FINAL_ACK_MASK)
- rxrpc_process_delayed_final_acks(conn, false);
-
/* go through the conn-level event packets, releasing the ref on this
* connection that each one has when we've finished with it */
while ((skb = skb_dequeue(&conn->rx_queue))) {
rxrpc_see_skb(skb, rxrpc_skb_see_conn_work);
- ret = rxrpc_process_event(conn, skb, &abort_code);
+ ret = rxrpc_process_event(conn, skb);
switch (ret) {
- case -EPROTO:
- case -EKEYEXPIRED:
- case -EKEYREJECTED:
- goto protocol_error;
case -ENOMEM:
case -EAGAIN:
- goto requeue_and_leave;
- case -ECONNABORTED:
+ skb_queue_head(&conn->rx_queue, skb);
+ rxrpc_queue_conn(conn, rxrpc_conn_queue_retry_work);
+ break;
default:
rxrpc_free_skb(skb, rxrpc_skb_put_conn_work);
break;
}
}
-
- return;
-
-requeue_and_leave:
- skb_queue_head(&conn->rx_queue, skb);
- return;
-
-protocol_error:
- if (rxrpc_abort_connection(conn, ret, abort_code) < 0)
- goto requeue_and_leave;
- rxrpc_free_skb(skb, rxrpc_skb_put_conn_work);
- return;
}
void rxrpc_process_connection(struct work_struct *work)
@@ -498,44 +377,59 @@ static void rxrpc_post_packet_to_conn(struct rxrpc_connection *conn,
/*
* Input a connection-level packet.
*/
-int rxrpc_input_conn_packet(struct rxrpc_connection *conn, struct sk_buff *skb)
+bool rxrpc_input_conn_packet(struct rxrpc_connection *conn, struct sk_buff *skb)
{
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
- if (conn->state >= RXRPC_CONN_REMOTELY_ABORTED) {
- _leave(" = -ECONNABORTED [%u]", conn->state);
- return -ECONNABORTED;
- }
-
- _enter("{%d},{%u,%%%u},", conn->debug_id, sp->hdr.type, sp->hdr.serial);
-
switch (sp->hdr.type) {
- case RXRPC_PACKET_TYPE_DATA:
- case RXRPC_PACKET_TYPE_ACK:
- rxrpc_conn_retransmit_call(conn, skb,
- sp->hdr.cid & RXRPC_CHANNELMASK);
- return 0;
-
case RXRPC_PACKET_TYPE_BUSY:
/* Just ignore BUSY packets for now. */
- return 0;
+ return true;
case RXRPC_PACKET_TYPE_ABORT:
- conn->error = -ECONNABORTED;
- conn->abort_code = skb->priority;
- conn->state = RXRPC_CONN_REMOTELY_ABORTED;
- set_bit(RXRPC_CONN_DONT_REUSE, &conn->flags);
- rxrpc_abort_calls(conn, RXRPC_CALL_REMOTELY_ABORTED, sp->hdr.serial);
- return -ECONNABORTED;
+ if (rxrpc_is_conn_aborted(conn))
+ return true;
+ rxrpc_input_conn_abort(conn, skb);
+ rxrpc_abort_calls(conn);
+ return true;
case RXRPC_PACKET_TYPE_CHALLENGE:
case RXRPC_PACKET_TYPE_RESPONSE:
+ if (rxrpc_is_conn_aborted(conn)) {
+ if (conn->completion == RXRPC_CALL_LOCALLY_ABORTED)
+ rxrpc_send_conn_abort(conn);
+ return true;
+ }
rxrpc_post_packet_to_conn(conn, skb);
- return 0;
+ return true;
default:
- trace_rxrpc_rx_eproto(NULL, sp->hdr.serial,
- tracepoint_string("bad_conn_pkt"));
- return -EPROTO;
+ WARN_ON_ONCE(1);
+ return true;
}
}
+
+/*
+ * Input a connection event.
+ */
+void rxrpc_input_conn_event(struct rxrpc_connection *conn, struct sk_buff *skb)
+{
+ unsigned int loop;
+
+ if (test_and_clear_bit(RXRPC_CONN_EV_ABORT_CALLS, &conn->events))
+ rxrpc_abort_calls(conn);
+
+ switch (skb->mark) {
+ case RXRPC_SKB_MARK_SERVICE_CONN_SECURED:
+ if (conn->state != RXRPC_CONN_SERVICE)
+ break;
+
+ for (loop = 0; loop < RXRPC_MAXCALLS; loop++)
+ rxrpc_call_is_secure(conn->channels[loop].call);
+ break;
+ }
+
+ /* Process delayed ACKs whose time has come. */
+ if (conn->flags & RXRPC_CONN_FINAL_ACK_MASK)
+ rxrpc_process_delayed_final_acks(conn, false);
+}
diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c
index 3c8f83dacb2b35..ac85d4644a3c3a 100644
--- a/net/rxrpc/conn_object.c
+++ b/net/rxrpc/conn_object.c
@@ -23,12 +23,30 @@ static void rxrpc_clean_up_connection(struct work_struct *work);
static void rxrpc_set_service_reap_timer(struct rxrpc_net *rxnet,
unsigned long reap_at);
+void rxrpc_poke_conn(struct rxrpc_connection *conn, enum rxrpc_conn_trace why)
+{
+ struct rxrpc_local *local = conn->local;
+ bool busy;
+
+ if (WARN_ON_ONCE(!local))
+ return;
+
+ spin_lock_bh(&local->lock);
+ busy = !list_empty(&conn->attend_link);
+ if (!busy) {
+ rxrpc_get_connection(conn, why);
+ list_add_tail(&conn->attend_link, &local->conn_attend_q);
+ }
+ spin_unlock_bh(&local->lock);
+ rxrpc_wake_up_io_thread(local);
+}
+
static void rxrpc_connection_timer(struct timer_list *timer)
{
struct rxrpc_connection *conn =
container_of(timer, struct rxrpc_connection, timer);
- rxrpc_queue_conn(conn, rxrpc_conn_queue_timer);
+ rxrpc_poke_conn(conn, rxrpc_conn_get_poke_timer);
}
/*
@@ -49,6 +67,7 @@ struct rxrpc_connection *rxrpc_alloc_connection(struct rxrpc_net *rxnet,
INIT_WORK(&conn->destructor, rxrpc_clean_up_connection);
INIT_LIST_HEAD(&conn->proc_link);
INIT_LIST_HEAD(&conn->link);
+ mutex_init(&conn->security_lock);
skb_queue_head_init(&conn->rx_queue);
conn->rxnet = rxnet;
conn->security = &rxrpc_no_security;
@@ -82,10 +101,10 @@ struct rxrpc_connection *rxrpc_find_client_connection_rcu(struct rxrpc_local *lo
_enter(",%x", sp->hdr.cid & RXRPC_CIDMASK);
- /* Look up client connections by connection ID alone as their IDs are
- * unique for this machine.
+ /* Look up client connections by connection ID alone as their
+ * IDs are unique for this machine.
*/
- conn = idr_find(&rxrpc_client_conn_ids, sp->hdr.cid >> RXRPC_CIDSHIFT);
+ conn = idr_find(&local->conn_ids, sp->hdr.cid >> RXRPC_CIDSHIFT);
if (!conn || refcount_read(&conn->ref) == 0) {
_debug("no conn");
goto not_found;
@@ -139,7 +158,7 @@ void __rxrpc_disconnect_call(struct rxrpc_connection *conn,
_enter("%d,%x", conn->debug_id, call->cid);
- if (rcu_access_pointer(chan->call) == call) {
+ if (chan->call == call) {
/* Save the result of the call so that we can repeat it if necessary
* through the channel, whilst disposing of the actual call record.
*/
@@ -159,12 +178,9 @@ void __rxrpc_disconnect_call(struct rxrpc_connection *conn,
break;
}
- /* Sync with rxrpc_conn_retransmit(). */
- smp_wmb();
chan->last_call = chan->call_id;
chan->call_id = chan->call_counter;
-
- rcu_assign_pointer(chan->call, NULL);
+ chan->call = NULL;
}
_leave("");
@@ -178,6 +194,9 @@ void rxrpc_disconnect_call(struct rxrpc_call *call)
{
struct rxrpc_connection *conn = call->conn;
+ set_bit(RXRPC_CALL_DISCONNECTED, &call->flags);
+ rxrpc_see_call(call, rxrpc_call_see_disconnected);
+
call->peer->cong_ssthresh = call->cong_ssthresh;
if (!hlist_unhashed(&call->error_link)) {
@@ -186,18 +205,17 @@ void rxrpc_disconnect_call(struct rxrpc_call *call)
spin_unlock(&call->peer->lock);
}
- if (rxrpc_is_client_call(call))
- return rxrpc_disconnect_client_call(conn->bundle, call);
-
- spin_lock(&conn->bundle->channel_lock);
- __rxrpc_disconnect_call(conn, call);
- spin_unlock(&conn->bundle->channel_lock);
+ if (rxrpc_is_client_call(call)) {
+ rxrpc_disconnect_client_call(call->bundle, call);
+ } else {
+ __rxrpc_disconnect_call(conn, call);
+ conn->idle_timestamp = jiffies;
+ if (atomic_dec_and_test(&conn->active))
+ rxrpc_set_service_reap_timer(conn->rxnet,
+ jiffies + rxrpc_connection_expiry);
+ }
- set_bit(RXRPC_CALL_DISCONNECTED, &call->flags);
- conn->idle_timestamp = jiffies;
- if (atomic_dec_and_test(&conn->active))
- rxrpc_set_service_reap_timer(conn->rxnet,
- jiffies + rxrpc_connection_expiry);
+ rxrpc_put_call(call, rxrpc_call_put_io_thread);
}
/*
@@ -293,10 +311,10 @@ static void rxrpc_clean_up_connection(struct work_struct *work)
container_of(work, struct rxrpc_connection, destructor);
struct rxrpc_net *rxnet = conn->rxnet;
- ASSERT(!rcu_access_pointer(conn->channels[0].call) &&
- !rcu_access_pointer(conn->channels[1].call) &&
- !rcu_access_pointer(conn->channels[2].call) &&
- !rcu_access_pointer(conn->channels[3].call));
+ ASSERT(!conn->channels[0].call &&
+ !conn->channels[1].call &&
+ !conn->channels[2].call &&
+ !conn->channels[3].call);
ASSERT(list_empty(&conn->cache_link));
del_timer_sync(&conn->timer);
@@ -447,7 +465,6 @@ void rxrpc_destroy_all_connections(struct rxrpc_net *rxnet)
_enter("");
atomic_dec(&rxnet->nr_conns);
- rxrpc_destroy_all_client_connections(rxnet);
del_timer_sync(&rxnet->service_conn_reap_timer);
rxrpc_queue_work(&rxnet->service_conn_reaper);
diff --git a/net/rxrpc/conn_service.c b/net/rxrpc/conn_service.c
index 2a55a88b2a5b7e..f30323de82bd11 100644
--- a/net/rxrpc/conn_service.c
+++ b/net/rxrpc/conn_service.c
@@ -11,7 +11,6 @@
static struct rxrpc_bundle rxrpc_service_dummy_bundle = {
.ref = REFCOUNT_INIT(1),
.debug_id = UINT_MAX,
- .channel_lock = __SPIN_LOCK_UNLOCKED(&rxrpc_service_dummy_bundle.channel_lock),
};
/*
diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c
index d0e20e946e48d5..367927a998815f 100644
--- a/net/rxrpc/input.c
+++ b/net/rxrpc/input.c
@@ -9,11 +9,10 @@
#include "ar-internal.h"
-static void rxrpc_proto_abort(const char *why,
- struct rxrpc_call *call, rxrpc_seq_t seq)
+static void rxrpc_proto_abort(struct rxrpc_call *call, rxrpc_seq_t seq,
+ enum rxrpc_abort_reason why)
{
- if (rxrpc_abort_call(why, call, seq, RX_PROTOCOL_ERROR, -EBADMSG))
- rxrpc_send_abort_packet(call);
+ rxrpc_abort_call(call, seq, RX_PROTOCOL_ERROR, -EBADMSG, why);
}
/*
@@ -185,7 +184,7 @@ void rxrpc_congestion_degrade(struct rxrpc_call *call)
if (call->cong_mode != RXRPC_CALL_SLOW_START &&
call->cong_mode != RXRPC_CALL_CONGEST_AVOIDANCE)
return;
- if (call->state == RXRPC_CALL_CLIENT_AWAIT_REPLY)
+ if (__rxrpc_call_state(call) == RXRPC_CALL_CLIENT_AWAIT_REPLY)
return;
rtt = ns_to_ktime(call->peer->srtt_us * (1000 / 8));
@@ -250,47 +249,34 @@ static bool rxrpc_rotate_tx_window(struct rxrpc_call *call, rxrpc_seq_t to,
* This occurs when we get an ACKALL packet, the first DATA packet of a reply,
* or a final ACK packet.
*/
-static bool rxrpc_end_tx_phase(struct rxrpc_call *call, bool reply_begun,
- const char *abort_why)
+static void rxrpc_end_tx_phase(struct rxrpc_call *call, bool reply_begun,
+ enum rxrpc_abort_reason abort_why)
{
- unsigned int state;
-
ASSERT(test_bit(RXRPC_CALL_TX_LAST, &call->flags));
- write_lock(&call->state_lock);
-
- state = call->state;
- switch (state) {
+ switch (__rxrpc_call_state(call)) {
case RXRPC_CALL_CLIENT_SEND_REQUEST:
case RXRPC_CALL_CLIENT_AWAIT_REPLY:
- if (reply_begun)
- call->state = state = RXRPC_CALL_CLIENT_RECV_REPLY;
- else
- call->state = state = RXRPC_CALL_CLIENT_AWAIT_REPLY;
+ if (reply_begun) {
+ rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_RECV_REPLY);
+ trace_rxrpc_txqueue(call, rxrpc_txqueue_end);
+ break;
+ }
+
+ rxrpc_set_call_state(call, RXRPC_CALL_CLIENT_AWAIT_REPLY);
+ trace_rxrpc_txqueue(call, rxrpc_txqueue_await_reply);
break;
case RXRPC_CALL_SERVER_AWAIT_ACK:
- __rxrpc_call_completed(call);
- state = call->state;
+ rxrpc_call_completed(call);
+ trace_rxrpc_txqueue(call, rxrpc_txqueue_end);
break;
default:
- goto bad_state;
+ kdebug("end_tx %s", rxrpc_call_states[__rxrpc_call_state(call)]);
+ rxrpc_proto_abort(call, call->tx_top, abort_why);
+ break;
}
-
- write_unlock(&call->state_lock);
- if (state == RXRPC_CALL_CLIENT_AWAIT_REPLY)
- trace_rxrpc_txqueue(call, rxrpc_txqueue_await_reply);
- else
- trace_rxrpc_txqueue(call, rxrpc_txqueue_end);
- _leave(" = ok");
- return true;
-
-bad_state:
- write_unlock(&call->state_lock);
- kdebug("end_tx %s", rxrpc_call_states[call->state]);
- rxrpc_proto_abort(abort_why, call, call->tx_top);
- return false;
}
/*
@@ -305,18 +291,48 @@ static bool rxrpc_receiving_reply(struct rxrpc_call *call)
if (call->ackr_reason) {
now = jiffies;
timo = now + MAX_JIFFY_OFFSET;
- WRITE_ONCE(call->resend_at, timo);
+
WRITE_ONCE(call->delay_ack_at, timo);
trace_rxrpc_timer(call, rxrpc_timer_init_for_reply, now);
}
if (!test_bit(RXRPC_CALL_TX_LAST, &call->flags)) {
if (!rxrpc_rotate_tx_window(call, top, &summary)) {
- rxrpc_proto_abort("TXL", call, top);
+ rxrpc_proto_abort(call, top, rxrpc_eproto_early_reply);
return false;
}
}
- return rxrpc_end_tx_phase(call, true, "ETD");
+
+ rxrpc_end_tx_phase(call, true, rxrpc_eproto_unexpected_reply);
+ return true;
+}
+
+/*
+ * End the packet reception phase.
+ */
+static void rxrpc_end_rx_phase(struct rxrpc_call *call, rxrpc_serial_t serial)
+{
+ rxrpc_seq_t whigh = READ_ONCE(call->rx_highest_seq);
+
+ _enter("%d,%s", call->debug_id, rxrpc_call_states[__rxrpc_call_state(call)]);
+
+ trace_rxrpc_receive(call, rxrpc_receive_end, 0, whigh);
+
+ switch (__rxrpc_call_state(call)) {
+ case RXRPC_CALL_CLIENT_RECV_REPLY:
+ rxrpc_propose_delay_ACK(call, serial, rxrpc_propose_ack_terminal_ack);
+ rxrpc_call_completed(call);
+ break;
+
+ case RXRPC_CALL_SERVER_RECV_REQUEST:
+ rxrpc_set_call_state(call, RXRPC_CALL_SERVER_ACK_REQUEST);
+ call->expect_req_by = jiffies + MAX_JIFFY_OFFSET;
+ rxrpc_propose_delay_ACK(call, serial, rxrpc_propose_ack_processing_op);
+ break;
+
+ default:
+ break;
+ }
}
static void rxrpc_input_update_ack_window(struct rxrpc_call *call,
@@ -337,8 +353,9 @@ static void rxrpc_input_queue_data(struct rxrpc_call *call, struct sk_buff *skb,
__skb_queue_tail(&call->recvmsg_queue, skb);
rxrpc_input_update_ack_window(call, window, wtop);
-
trace_rxrpc_receive(call, last ? why + 1 : why, sp->hdr.serial, sp->hdr.seq);
+ if (last)
+ rxrpc_end_rx_phase(call, sp->hdr.serial);
}
/*
@@ -366,17 +383,14 @@ static void rxrpc_input_data_one(struct rxrpc_call *call, struct sk_buff *skb,
if (last) {
if (test_and_set_bit(RXRPC_CALL_RX_LAST, &call->flags) &&
- seq + 1 != wtop) {
- rxrpc_proto_abort("LSN", call, seq);
- return;
- }
+ seq + 1 != wtop)
+ return rxrpc_proto_abort(call, seq, rxrpc_eproto_different_last);
} else {
if (test_bit(RXRPC_CALL_RX_LAST, &call->flags) &&
after_eq(seq, wtop)) {
pr_warn("Packet beyond last: c=%x q=%x window=%x-%x wlimit=%x\n",
call->debug_id, seq, window, wtop, wlimit);
- rxrpc_proto_abort("LSA", call, seq);
- return;
+ return rxrpc_proto_abort(call, seq, rxrpc_eproto_data_after_last);
}
}
@@ -550,7 +564,6 @@ protocol_error:
static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb)
{
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
- enum rxrpc_call_state state;
rxrpc_serial_t serial = sp->hdr.serial;
rxrpc_seq_t seq0 = sp->hdr.seq;
@@ -558,11 +571,20 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb)
atomic64_read(&call->ackr_window), call->rx_highest_seq,
skb->len, seq0);
- state = READ_ONCE(call->state);
- if (state >= RXRPC_CALL_COMPLETE)
+ if (__rxrpc_call_is_complete(call))
return;
- if (state == RXRPC_CALL_SERVER_RECV_REQUEST) {
+ switch (__rxrpc_call_state(call)) {
+ case RXRPC_CALL_CLIENT_SEND_REQUEST:
+ case RXRPC_CALL_CLIENT_AWAIT_REPLY:
+ /* Received data implicitly ACKs all of the request
+ * packets we sent when we're acting as a client.
+ */
+ if (!rxrpc_receiving_reply(call))
+ goto out_notify;
+ break;
+
+ case RXRPC_CALL_SERVER_RECV_REQUEST: {
unsigned long timo = READ_ONCE(call->next_req_timo);
unsigned long now, expect_req_by;
@@ -573,18 +595,15 @@ static void rxrpc_input_data(struct rxrpc_call *call, struct sk_buff *skb)
rxrpc_reduce_call_timer(call, expect_req_by, now,
rxrpc_timer_set_for_idle);
}
+ break;
}
- /* Received data implicitly ACKs all of the request packets we sent
- * when we're acting as a client.
- */
- if ((state == RXRPC_CALL_CLIENT_SEND_REQUEST ||
- state == RXRPC_CALL_CLIENT_AWAIT_REPLY) &&
- !rxrpc_receiving_reply(call))
- goto out_notify;
+ default:
+ break;
+ }
if (!rxrpc_input_split_jumbo(call, skb)) {
- rxrpc_proto_abort("VLD", call, sp->hdr.seq);
+ rxrpc_proto_abort(call, sp->hdr.seq, rxrpc_badmsg_bad_jumbo);
goto out_notify;
}
skb = NULL;
@@ -765,7 +784,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb)
offset = sizeof(struct rxrpc_wire_header);
if (skb_copy_bits(skb, offset, &ack, sizeof(ack)) < 0)
- return rxrpc_proto_abort("XAK", call, 0);
+ return rxrpc_proto_abort(call, 0, rxrpc_badmsg_short_ack);
offset += sizeof(ack);
ack_serial = sp->hdr.serial;
@@ -845,7 +864,7 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb)
ioffset = offset + nr_acks + 3;
if (skb->len >= ioffset + sizeof(info) &&
skb_copy_bits(skb, ioffset, &info, sizeof(info)) < 0)
- return rxrpc_proto_abort("XAI", call, 0);
+ return rxrpc_proto_abort(call, 0, rxrpc_badmsg_short_ack_info);
if (nr_acks > 0)
skb_condense(skb);
@@ -868,10 +887,10 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb)
rxrpc_input_ackinfo(call, skb, &info);
if (first_soft_ack == 0)
- return rxrpc_proto_abort("AK0", call, 0);
+ return rxrpc_proto_abort(call, 0, rxrpc_eproto_ackr_zero);
/* Ignore ACKs unless we are or have just been transmitting. */
- switch (READ_ONCE(call->state)) {
+ switch (__rxrpc_call_state(call)) {
case RXRPC_CALL_CLIENT_SEND_REQUEST:
case RXRPC_CALL_CLIENT_AWAIT_REPLY:
case RXRPC_CALL_SERVER_SEND_REPLY:
@@ -883,20 +902,20 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb)
if (before(hard_ack, call->acks_hard_ack) ||
after(hard_ack, call->tx_top))
- return rxrpc_proto_abort("AKW", call, 0);
+ return rxrpc_proto_abort(call, 0, rxrpc_eproto_ackr_outside_window);
if (nr_acks > call->tx_top - hard_ack)
- return rxrpc_proto_abort("AKN", call, 0);
+ return rxrpc_proto_abort(call, 0, rxrpc_eproto_ackr_sack_overflow);
if (after(hard_ack, call->acks_hard_ack)) {
if (rxrpc_rotate_tx_window(call, hard_ack, &summary)) {
- rxrpc_end_tx_phase(call, false, "ETA");
+ rxrpc_end_tx_phase(call, false, rxrpc_eproto_unexpected_ack);
return;
}
}
if (nr_acks > 0) {
if (offset > (int)skb->len - nr_acks)
- return rxrpc_proto_abort("XSA", call, 0);
+ return rxrpc_proto_abort(call, 0, rxrpc_eproto_ackr_short_sack);
rxrpc_input_soft_acks(call, skb->data + offset, first_soft_ack,
nr_acks, &summary);
}
@@ -918,7 +937,7 @@ static void rxrpc_input_ackall(struct rxrpc_call *call, struct sk_buff *skb)
struct rxrpc_ack_summary summary = { 0 };
if (rxrpc_rotate_tx_window(call, call->tx_top, &summary))
- rxrpc_end_tx_phase(call, false, "ETL");
+ rxrpc_end_tx_phase(call, false, rxrpc_eproto_unexpected_ackall);
}
/*
@@ -963,27 +982,23 @@ void rxrpc_input_call_packet(struct rxrpc_call *call, struct sk_buff *skb)
switch (sp->hdr.type) {
case RXRPC_PACKET_TYPE_DATA:
- rxrpc_input_data(call, skb);
- break;
+ return rxrpc_input_data(call, skb);
case RXRPC_PACKET_TYPE_ACK:
- rxrpc_input_ack(call, skb);
- break;
+ return rxrpc_input_ack(call, skb);
case RXRPC_PACKET_TYPE_BUSY:
/* Just ignore BUSY packets from the server; the retry and
* lifespan timers will take care of business. BUSY packets
* from the client don't make sense.
*/
- break;
+ return;
case RXRPC_PACKET_TYPE_ABORT:
- rxrpc_input_abort(call, skb);
- break;
+ return rxrpc_input_abort(call, skb);
case RXRPC_PACKET_TYPE_ACKALL:
- rxrpc_input_ackall(call, skb);
- break;
+ return rxrpc_input_ackall(call, skb);
default:
break;
@@ -998,24 +1013,18 @@ void rxrpc_input_call_packet(struct rxrpc_call *call, struct sk_buff *skb)
*/
void rxrpc_implicit_end_call(struct rxrpc_call *call, struct sk_buff *skb)
{
- struct rxrpc_connection *conn = call->conn;
-
- switch (READ_ONCE(call->state)) {
+ switch (__rxrpc_call_state(call)) {
case RXRPC_CALL_SERVER_AWAIT_ACK:
rxrpc_call_completed(call);
fallthrough;
case RXRPC_CALL_COMPLETE:
break;
default:
- if (rxrpc_abort_call("IMP", call, 0, RX_CALL_DEAD, -ESHUTDOWN))
- rxrpc_send_abort_packet(call);
+ rxrpc_abort_call(call, 0, RX_CALL_DEAD, -ESHUTDOWN,
+ rxrpc_eproto_improper_term);
trace_rxrpc_improper_term(call);
break;
}
rxrpc_input_call_event(call, skb);
-
- spin_lock(&conn->bundle->channel_lock);
- __rxrpc_disconnect_call(conn, call);
- spin_unlock(&conn->bundle->channel_lock);
}
diff --git a/net/rxrpc/insecure.c b/net/rxrpc/insecure.c
index 0eb8471bfc5367..34353b6e584bc5 100644
--- a/net/rxrpc/insecure.c
+++ b/net/rxrpc/insecure.c
@@ -43,25 +43,17 @@ static void none_free_call_crypto(struct rxrpc_call *call)
}
static int none_respond_to_challenge(struct rxrpc_connection *conn,
- struct sk_buff *skb,
- u32 *_abort_code)
+ struct sk_buff *skb)
{
- struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
-
- trace_rxrpc_rx_eproto(NULL, sp->hdr.serial,
- tracepoint_string("chall_none"));
- return -EPROTO;
+ return rxrpc_abort_conn(conn, skb, RX_PROTOCOL_ERROR, -EPROTO,
+ rxrpc_eproto_rxnull_challenge);
}
static int none_verify_response(struct rxrpc_connection *conn,
- struct sk_buff *skb,
- u32 *_abort_code)
+ struct sk_buff *skb)
{
- struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
-
- trace_rxrpc_rx_eproto(NULL, sp->hdr.serial,
- tracepoint_string("resp_none"));
- return -EPROTO;
+ return rxrpc_abort_conn(conn, skb, RX_PROTOCOL_ERROR, -EPROTO,
+ rxrpc_eproto_rxnull_response);
}
static void none_clear(struct rxrpc_connection *conn)
diff --git a/net/rxrpc/io_thread.c b/net/rxrpc/io_thread.c
index 1ad067d66fb604..9e9dfb2fc559be 100644
--- a/net/rxrpc/io_thread.c
+++ b/net/rxrpc/io_thread.c
@@ -67,9 +67,31 @@ void rxrpc_error_report(struct sock *sk)
}
/*
+ * Directly produce an abort from a packet.
+ */
+bool rxrpc_direct_abort(struct sk_buff *skb, enum rxrpc_abort_reason why,
+ s32 abort_code, int err)
+{
+ struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
+
+ trace_rxrpc_abort(0, why, sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
+ abort_code, err);
+ skb->mark = RXRPC_SKB_MARK_REJECT_ABORT;
+ skb->priority = abort_code;
+ return false;
+}
+
+static bool rxrpc_bad_message(struct sk_buff *skb, enum rxrpc_abort_reason why)
+{
+ return rxrpc_direct_abort(skb, why, RX_PROTOCOL_ERROR, -EBADMSG);
+}
+
+#define just_discard true
+
+/*
* Process event packets targeted at a local endpoint.
*/
-static void rxrpc_input_version(struct rxrpc_local *local, struct sk_buff *skb)
+static bool rxrpc_input_version(struct rxrpc_local *local, struct sk_buff *skb)
{
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
char v;
@@ -81,22 +103,21 @@ static void rxrpc_input_version(struct rxrpc_local *local, struct sk_buff *skb)
if (v == 0)
rxrpc_send_version_request(local, &sp->hdr, skb);
}
+
+ return true;
}
/*
* Extract the wire header from a packet and translate the byte order.
*/
-static noinline
-int rxrpc_extract_header(struct rxrpc_skb_priv *sp, struct sk_buff *skb)
+static bool rxrpc_extract_header(struct rxrpc_skb_priv *sp,
+ struct sk_buff *skb)
{
struct rxrpc_wire_header whdr;
/* dig out the RxRPC connection details */
- if (skb_copy_bits(skb, 0, &whdr, sizeof(whdr)) < 0) {
- trace_rxrpc_rx_eproto(NULL, sp->hdr.serial,
- tracepoint_string("bad_hdr"));
- return -EBADMSG;
- }
+ if (skb_copy_bits(skb, 0, &whdr, sizeof(whdr)) < 0)
+ return rxrpc_bad_message(skb, rxrpc_badmsg_short_hdr);
memset(sp, 0, sizeof(*sp));
sp->hdr.epoch = ntohl(whdr.epoch);
@@ -110,7 +131,7 @@ int rxrpc_extract_header(struct rxrpc_skb_priv *sp, struct sk_buff *skb)
sp->hdr.securityIndex = whdr.securityIndex;
sp->hdr._rsvd = ntohs(whdr._rsvd);
sp->hdr.serviceId = ntohs(whdr.serviceId);
- return 0;
+ return true;
}
/*
@@ -130,28 +151,28 @@ static bool rxrpc_extract_abort(struct sk_buff *skb)
/*
* Process packets received on the local endpoint
*/
-static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
+static bool rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
{
struct rxrpc_connection *conn;
struct sockaddr_rxrpc peer_srx;
struct rxrpc_skb_priv *sp;
struct rxrpc_peer *peer = NULL;
struct sk_buff *skb = *_skb;
- int ret = 0;
+ bool ret = false;
skb_pull(skb, sizeof(struct udphdr));
sp = rxrpc_skb(skb);
/* dig out the RxRPC connection details */
- if (rxrpc_extract_header(sp, skb) < 0)
- goto bad_message;
+ if (!rxrpc_extract_header(sp, skb))
+ return just_discard;
if (IS_ENABLED(CONFIG_AF_RXRPC_INJECT_LOSS)) {
static int lose;
if ((lose++ & 7) == 7) {
trace_rxrpc_rx_lose(sp);
- return 0;
+ return just_discard;
}
}
@@ -160,28 +181,28 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
switch (sp->hdr.type) {
case RXRPC_PACKET_TYPE_VERSION:
if (rxrpc_to_client(sp))
- return 0;
- rxrpc_input_version(local, skb);
- return 0;
+ return just_discard;
+ return rxrpc_input_version(local, skb);
case RXRPC_PACKET_TYPE_BUSY:
if (rxrpc_to_server(sp))
- return 0;
+ return just_discard;
fallthrough;
case RXRPC_PACKET_TYPE_ACK:
case RXRPC_PACKET_TYPE_ACKALL:
if (sp->hdr.callNumber == 0)
- goto bad_message;
+ return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
break;
case RXRPC_PACKET_TYPE_ABORT:
if (!rxrpc_extract_abort(skb))
- return 0; /* Just discard if malformed */
+ return just_discard; /* Just discard if malformed */
break;
case RXRPC_PACKET_TYPE_DATA:
- if (sp->hdr.callNumber == 0 ||
- sp->hdr.seq == 0)
- goto bad_message;
+ if (sp->hdr.callNumber == 0)
+ return rxrpc_bad_message(skb, rxrpc_badmsg_zero_call);
+ if (sp->hdr.seq == 0)
+ return rxrpc_bad_message(skb, rxrpc_badmsg_zero_seq);
/* Unshare the packet so that it can be modified for in-place
* decryption.
@@ -191,7 +212,7 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
if (!skb) {
rxrpc_eaten_skb(*_skb, rxrpc_skb_eaten_by_unshare_nomem);
*_skb = NULL;
- return 0;
+ return just_discard;
}
if (skb != *_skb) {
@@ -205,28 +226,28 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
case RXRPC_PACKET_TYPE_CHALLENGE:
if (rxrpc_to_server(sp))
- return 0;
+ return just_discard;
break;
case RXRPC_PACKET_TYPE_RESPONSE:
if (rxrpc_to_client(sp))
- return 0;
+ return just_discard;
break;
/* Packet types 9-11 should just be ignored. */
case RXRPC_PACKET_TYPE_PARAMS:
case RXRPC_PACKET_TYPE_10:
case RXRPC_PACKET_TYPE_11:
- return 0;
+ return just_discard;
default:
- goto bad_message;
+ return rxrpc_bad_message(skb, rxrpc_badmsg_unsupported_packet);
}
if (sp->hdr.serviceId == 0)
- goto bad_message;
+ return rxrpc_bad_message(skb, rxrpc_badmsg_zero_service);
if (WARN_ON_ONCE(rxrpc_extract_addr_from_skb(&peer_srx, skb) < 0))
- return true; /* Unsupported address type - discard. */
+ return just_discard; /* Unsupported address type. */
if (peer_srx.transport.family != local->srx.transport.family &&
(peer_srx.transport.family == AF_INET &&
@@ -234,7 +255,7 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
pr_warn_ratelimited("AF_RXRPC: Protocol mismatch %u not %u\n",
peer_srx.transport.family,
local->srx.transport.family);
- return true; /* Wrong address type - discard. */
+ return just_discard; /* Wrong address type. */
}
if (rxrpc_to_client(sp)) {
@@ -242,12 +263,8 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
conn = rxrpc_find_client_connection_rcu(local, &peer_srx, skb);
conn = rxrpc_get_connection_maybe(conn, rxrpc_conn_get_call_input);
rcu_read_unlock();
- if (!conn) {
- trace_rxrpc_abort(0, "NCC", sp->hdr.cid,
- sp->hdr.callNumber, sp->hdr.seq,
- RXKADINCONSISTENCY, EBADMSG);
- goto protocol_error;
- }
+ if (!conn)
+ return rxrpc_protocol_error(skb, rxrpc_eproto_no_client_conn);
ret = rxrpc_input_packet_on_conn(conn, &peer_srx, skb);
rxrpc_put_connection(conn, rxrpc_conn_put_call_input);
@@ -280,19 +297,7 @@ static int rxrpc_input_packet(struct rxrpc_local *local, struct sk_buff **_skb)
ret = rxrpc_new_incoming_call(local, peer, NULL, &peer_srx, skb);
rxrpc_put_peer(peer, rxrpc_peer_put_input);
- if (ret < 0)
- goto reject_packet;
- return 0;
-
-bad_message:
- trace_rxrpc_abort(0, "BAD", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
- RX_PROTOCOL_ERROR, EBADMSG);
-protocol_error:
- skb->priority = RX_PROTOCOL_ERROR;
- skb->mark = RXRPC_SKB_MARK_REJECT_ABORT;
-reject_packet:
- rxrpc_reject_packet(local, skb);
- return 0;
+ return ret;
}
/*
@@ -306,21 +311,23 @@ static int rxrpc_input_packet_on_conn(struct rxrpc_connection *conn,
struct rxrpc_channel *chan;
struct rxrpc_call *call = NULL;
unsigned int channel;
+ bool ret;
if (sp->hdr.securityIndex != conn->security_ix)
- goto wrong_security;
+ return rxrpc_direct_abort(skb, rxrpc_eproto_wrong_security,
+ RXKADINCONSISTENCY, -EBADMSG);
if (sp->hdr.serviceId != conn->service_id) {
int old_id;
if (!test_bit(RXRPC_CONN_PROBING_FOR_UPGRADE, &conn->flags))
- goto reupgrade;
+ return rxrpc_protocol_error(skb, rxrpc_eproto_reupgrade);
+
old_id = cmpxchg(&conn->service_id, conn->orig_service_id,
sp->hdr.serviceId);
-
if (old_id != conn->orig_service_id &&
old_id != sp->hdr.serviceId)
- goto reupgrade;
+ return rxrpc_protocol_error(skb, rxrpc_eproto_bad_upgrade);
}
if (after(sp->hdr.serial, conn->hi_serial))
@@ -336,19 +343,19 @@ static int rxrpc_input_packet_on_conn(struct rxrpc_connection *conn,
/* Ignore really old calls */
if (sp->hdr.callNumber < chan->last_call)
- return 0;
+ return just_discard;
if (sp->hdr.callNumber == chan->last_call) {
if (chan->call ||
sp->hdr.type == RXRPC_PACKET_TYPE_ABORT)
- return 0;
+ return just_discard;
/* For the previous service call, if completed successfully, we
* discard all further packets.
*/
if (rxrpc_conn_is_service(conn) &&
chan->last_type == RXRPC_PACKET_TYPE_ACK)
- return 0;
+ return just_discard;
/* But otherwise we need to retransmit the final packet from
* data cached in the connection record.
@@ -358,19 +365,17 @@ static int rxrpc_input_packet_on_conn(struct rxrpc_connection *conn,
sp->hdr.seq,
sp->hdr.serial,
sp->hdr.flags);
- rxrpc_input_conn_packet(conn, skb);
- return 0;
+ rxrpc_conn_retransmit_call(conn, skb, channel);
+ return just_discard;
}
- rcu_read_lock();
- call = rxrpc_try_get_call(rcu_dereference(chan->call),
- rxrpc_call_get_input);
- rcu_read_unlock();
+ call = rxrpc_try_get_call(chan->call, rxrpc_call_get_input);
if (sp->hdr.callNumber > chan->call_id) {
if (rxrpc_to_client(sp)) {
rxrpc_put_call(call, rxrpc_call_put_input);
- goto reject_packet;
+ return rxrpc_protocol_error(skb,
+ rxrpc_eproto_unexpected_implicit_end);
}
if (call) {
@@ -382,38 +387,14 @@ static int rxrpc_input_packet_on_conn(struct rxrpc_connection *conn,
if (!call) {
if (rxrpc_to_client(sp))
- goto bad_message;
- if (rxrpc_new_incoming_call(conn->local, conn->peer, conn,
- peer_srx, skb) == 0)
- return 0;
- goto reject_packet;
+ return rxrpc_protocol_error(skb, rxrpc_eproto_no_client_call);
+ return rxrpc_new_incoming_call(conn->local, conn->peer, conn,
+ peer_srx, skb);
}
- rxrpc_input_call_event(call, skb);
+ ret = rxrpc_input_call_event(call, skb);
rxrpc_put_call(call, rxrpc_call_put_input);
- return 0;
-
-wrong_security:
- trace_rxrpc_abort(0, "SEC", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
- RXKADINCONSISTENCY, EBADMSG);
- skb->priority = RXKADINCONSISTENCY;
- goto post_abort;
-
-reupgrade:
- trace_rxrpc_abort(0, "UPG", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
- RX_PROTOCOL_ERROR, EBADMSG);
- goto protocol_error;
-
-bad_message:
- trace_rxrpc_abort(0, "BAD", sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
- RX_PROTOCOL_ERROR, EBADMSG);
-protocol_error:
- skb->priority = RX_PROTOCOL_ERROR;
-post_abort:
- skb->mark = RXRPC_SKB_MARK_REJECT_ABORT;
-reject_packet:
- rxrpc_reject_packet(conn->local, skb);
- return 0;
+ return ret;
}
/*
@@ -421,6 +402,7 @@ reject_packet:
*/
int rxrpc_io_thread(void *data)
{
+ struct rxrpc_connection *conn;
struct sk_buff_head rx_queue;
struct rxrpc_local *local = data;
struct rxrpc_call *call;
@@ -436,6 +418,24 @@ int rxrpc_io_thread(void *data)
for (;;) {
rxrpc_inc_stat(local->rxnet, stat_io_loop);
+ /* Deal with connections that want immediate attention. */
+ conn = list_first_entry_or_null(&local->conn_attend_q,
+ struct rxrpc_connection,
+ attend_link);
+ if (conn) {
+ spin_lock_bh(&local->lock);
+ list_del_init(&conn->attend_link);
+ spin_unlock_bh(&local->lock);
+
+ rxrpc_input_conn_event(conn, NULL);
+ rxrpc_put_connection(conn, rxrpc_conn_put_poke);
+ continue;
+ }
+
+ if (test_and_clear_bit(RXRPC_CLIENT_CONN_REAP_TIMER,
+ &local->client_conn_flags))
+ rxrpc_discard_expired_client_conns(local);
+
/* Deal with calls that want immediate attention. */
if ((call = list_first_entry_or_null(&local->call_attend_q,
struct rxrpc_call,
@@ -450,12 +450,17 @@ int rxrpc_io_thread(void *data)
continue;
}
+ if (!list_empty(&local->new_client_calls))
+ rxrpc_connect_client_calls(local);
+
/* Process received packets and errors. */
if ((skb = __skb_dequeue(&rx_queue))) {
+ struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
switch (skb->mark) {
case RXRPC_SKB_MARK_PACKET:
skb->priority = 0;
- rxrpc_input_packet(local, &skb);
+ if (!rxrpc_input_packet(local, &skb))
+ rxrpc_reject_packet(local, skb);
trace_rxrpc_rx_done(skb->mark, skb->priority);
rxrpc_free_skb(skb, rxrpc_skb_put_input);
break;
@@ -463,6 +468,11 @@ int rxrpc_io_thread(void *data)
rxrpc_input_error(local, skb);
rxrpc_free_skb(skb, rxrpc_skb_put_error_report);
break;
+ case RXRPC_SKB_MARK_SERVICE_CONN_SECURED:
+ rxrpc_input_conn_event(sp->conn, skb);
+ rxrpc_put_connection(sp->conn, rxrpc_conn_put_poke);
+ rxrpc_free_skb(skb, rxrpc_skb_put_conn_secured);
+ break;
default:
WARN_ON_ONCE(1);
rxrpc_free_skb(skb, rxrpc_skb_put_unknown);
@@ -481,7 +491,11 @@ int rxrpc_io_thread(void *data)
set_current_state(TASK_INTERRUPTIBLE);
should_stop = kthread_should_stop();
if (!skb_queue_empty(&local->rx_queue) ||
- !list_empty(&local->call_attend_q)) {
+ !list_empty(&local->call_attend_q) ||
+ !list_empty(&local->conn_attend_q) ||
+ !list_empty(&local->new_client_calls) ||
+ test_bit(RXRPC_CLIENT_CONN_REAP_TIMER,
+ &local->client_conn_flags)) {
__set_current_state(TASK_RUNNING);
continue;
}
diff --git a/net/rxrpc/local_object.c b/net/rxrpc/local_object.c
index 270b63d8f37a64..b8eaca5d9f2213 100644
--- a/net/rxrpc/local_object.c
+++ b/net/rxrpc/local_object.c
@@ -82,31 +82,59 @@ static long rxrpc_local_cmp_key(const struct rxrpc_local *local,
}
}
+static void rxrpc_client_conn_reap_timeout(struct timer_list *timer)
+{
+ struct rxrpc_local *local =
+ container_of(timer, struct rxrpc_local, client_conn_reap_timer);
+
+ if (local->kill_all_client_conns &&
+ test_and_set_bit(RXRPC_CLIENT_CONN_REAP_TIMER, &local->client_conn_flags))
+ rxrpc_wake_up_io_thread(local);
+}
+
/*
* Allocate a new local endpoint.
*/
-static struct rxrpc_local *rxrpc_alloc_local(struct rxrpc_net *rxnet,
+static struct rxrpc_local *rxrpc_alloc_local(struct net *net,
const struct sockaddr_rxrpc *srx)
{
struct rxrpc_local *local;
+ u32 tmp;
local = kzalloc(sizeof(struct rxrpc_local), GFP_KERNEL);
if (local) {
refcount_set(&local->ref, 1);
atomic_set(&local->active_users, 1);
- local->rxnet = rxnet;
+ local->net = net;
+ local->rxnet = rxrpc_net(net);
INIT_HLIST_NODE(&local->link);
init_rwsem(&local->defrag_sem);
init_completion(&local->io_thread_ready);
skb_queue_head_init(&local->rx_queue);
+ INIT_LIST_HEAD(&local->conn_attend_q);
INIT_LIST_HEAD(&local->call_attend_q);
+
local->client_bundles = RB_ROOT;
spin_lock_init(&local->client_bundles_lock);
+ local->kill_all_client_conns = false;
+ INIT_LIST_HEAD(&local->idle_client_conns);
+ timer_setup(&local->client_conn_reap_timer,
+ rxrpc_client_conn_reap_timeout, 0);
+
spin_lock_init(&local->lock);
rwlock_init(&local->services_lock);
local->debug_id = atomic_inc_return(&rxrpc_debug_id);
memcpy(&local->srx, srx, sizeof(*srx));
local->srx.srx_service = 0;
+ idr_init(&local->conn_ids);
+ get_random_bytes(&tmp, sizeof(tmp));
+ tmp &= 0x3fffffff;
+ if (tmp == 0)
+ tmp = 1;
+ idr_set_cursor(&local->conn_ids, tmp);
+ INIT_LIST_HEAD(&local->new_client_calls);
+ spin_lock_init(&local->client_call_lock);
+
trace_rxrpc_local(local->debug_id, rxrpc_local_new, 1, 1);
}
@@ -248,7 +276,7 @@ struct rxrpc_local *rxrpc_lookup_local(struct net *net,
goto found;
}
- local = rxrpc_alloc_local(rxnet, srx);
+ local = rxrpc_alloc_local(net, srx);
if (!local)
goto nomem;
@@ -407,6 +435,7 @@ void rxrpc_destroy_local(struct rxrpc_local *local)
* local endpoint.
*/
rxrpc_purge_queue(&local->rx_queue);
+ rxrpc_purge_client_connections(local);
}
/*
diff --git a/net/rxrpc/net_ns.c b/net/rxrpc/net_ns.c
index 5905530e2f33b4..a0319c040c25de 100644
--- a/net/rxrpc/net_ns.c
+++ b/net/rxrpc/net_ns.c
@@ -10,15 +10,6 @@
unsigned int rxrpc_net_id;
-static void rxrpc_client_conn_reap_timeout(struct timer_list *timer)
-{
- struct rxrpc_net *rxnet =
- container_of(timer, struct rxrpc_net, client_conn_reap_timer);
-
- if (rxnet->live)
- rxrpc_queue_work(&rxnet->client_conn_reaper);
-}
-
static void rxrpc_service_conn_reap_timeout(struct timer_list *timer)
{
struct rxrpc_net *rxnet =
@@ -63,14 +54,6 @@ static __net_init int rxrpc_init_net(struct net *net)
rxrpc_service_conn_reap_timeout, 0);
atomic_set(&rxnet->nr_client_conns, 0);
- rxnet->kill_all_client_conns = false;
- spin_lock_init(&rxnet->client_conn_cache_lock);
- mutex_init(&rxnet->client_conn_discard_lock);
- INIT_LIST_HEAD(&rxnet->idle_client_conns);
- INIT_WORK(&rxnet->client_conn_reaper,
- rxrpc_discard_expired_client_conns);
- timer_setup(&rxnet->client_conn_reap_timer,
- rxrpc_client_conn_reap_timeout, 0);
INIT_HLIST_HEAD(&rxnet->local_endpoints);
mutex_init(&rxnet->local_mutex);
diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c
index 3d8c9f830ee094..a9746be296347a 100644
--- a/net/rxrpc/output.c
+++ b/net/rxrpc/output.c
@@ -261,7 +261,7 @@ int rxrpc_send_ack_packet(struct rxrpc_call *call, struct rxrpc_txbuf *txb)
rxrpc_tx_point_call_ack);
rxrpc_tx_backoff(call, ret);
- if (call->state < RXRPC_CALL_COMPLETE) {
+ if (!__rxrpc_call_is_complete(call)) {
if (ret < 0)
rxrpc_cancel_rtt_probe(call, serial, rtt_slot);
rxrpc_set_keepalive(call);
@@ -545,6 +545,62 @@ send_fragmentable:
}
/*
+ * Transmit a connection-level abort.
+ */
+void rxrpc_send_conn_abort(struct rxrpc_connection *conn)
+{
+ struct rxrpc_wire_header whdr;
+ struct msghdr msg;
+ struct kvec iov[2];
+ __be32 word;
+ size_t len;
+ u32 serial;
+ int ret;
+
+ msg.msg_name = &conn->peer->srx.transport;
+ msg.msg_namelen = conn->peer->srx.transport_len;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ whdr.epoch = htonl(conn->proto.epoch);
+ whdr.cid = htonl(conn->proto.cid);
+ whdr.callNumber = 0;
+ whdr.seq = 0;
+ whdr.type = RXRPC_PACKET_TYPE_ABORT;
+ whdr.flags = conn->out_clientflag;
+ whdr.userStatus = 0;
+ whdr.securityIndex = conn->security_ix;
+ whdr._rsvd = 0;
+ whdr.serviceId = htons(conn->service_id);
+
+ word = htonl(conn->abort_code);
+
+ iov[0].iov_base = &whdr;
+ iov[0].iov_len = sizeof(whdr);
+ iov[1].iov_base = &word;
+ iov[1].iov_len = sizeof(word);
+
+ len = iov[0].iov_len + iov[1].iov_len;
+
+ serial = atomic_inc_return(&conn->serial);
+ whdr.serial = htonl(serial);
+
+ iov_iter_kvec(&msg.msg_iter, WRITE, iov, 2, len);
+ ret = do_udp_sendmsg(conn->local->socket, &msg, len);
+ if (ret < 0) {
+ trace_rxrpc_tx_fail(conn->debug_id, serial, ret,
+ rxrpc_tx_point_conn_abort);
+ _debug("sendmsg failed: %d", ret);
+ return;
+ }
+
+ trace_rxrpc_tx_packet(conn->debug_id, &whdr, rxrpc_tx_point_conn_abort);
+
+ conn->peer->last_tx_at = ktime_get_seconds();
+}
+
+/*
* Reject a packet through the local endpoint.
*/
void rxrpc_reject_packet(struct rxrpc_local *local, struct sk_buff *skb)
@@ -667,7 +723,7 @@ void rxrpc_send_keepalive(struct rxrpc_peer *peer)
static inline void rxrpc_instant_resend(struct rxrpc_call *call,
struct rxrpc_txbuf *txb)
{
- if (call->state < RXRPC_CALL_COMPLETE)
+ if (!__rxrpc_call_is_complete(call))
kdebug("resend");
}
diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c
index 4eecea2be307b6..8d7a715a0bb1ca 100644
--- a/net/rxrpc/peer_object.c
+++ b/net/rxrpc/peer_object.c
@@ -147,10 +147,10 @@ struct rxrpc_peer *rxrpc_lookup_peer_rcu(struct rxrpc_local *local,
* assess the MTU size for the network interface through which this peer is
* reached
*/
-static void rxrpc_assess_MTU_size(struct rxrpc_sock *rx,
+static void rxrpc_assess_MTU_size(struct rxrpc_local *local,
struct rxrpc_peer *peer)
{
- struct net *net = sock_net(&rx->sk);
+ struct net *net = local->net;
struct dst_entry *dst;
struct rtable *rt;
struct flowi fl;
@@ -236,11 +236,11 @@ struct rxrpc_peer *rxrpc_alloc_peer(struct rxrpc_local *local, gfp_t gfp,
/*
* Initialise peer record.
*/
-static void rxrpc_init_peer(struct rxrpc_sock *rx, struct rxrpc_peer *peer,
+static void rxrpc_init_peer(struct rxrpc_local *local, struct rxrpc_peer *peer,
unsigned long hash_key)
{
peer->hash_key = hash_key;
- rxrpc_assess_MTU_size(rx, peer);
+ rxrpc_assess_MTU_size(local, peer);
peer->mtu = peer->if_mtu;
peer->rtt_last_req = ktime_get_real();
@@ -272,8 +272,7 @@ static void rxrpc_init_peer(struct rxrpc_sock *rx, struct rxrpc_peer *peer,
/*
* Set up a new peer.
*/
-static struct rxrpc_peer *rxrpc_create_peer(struct rxrpc_sock *rx,
- struct rxrpc_local *local,
+static struct rxrpc_peer *rxrpc_create_peer(struct rxrpc_local *local,
struct sockaddr_rxrpc *srx,
unsigned long hash_key,
gfp_t gfp)
@@ -285,7 +284,7 @@ static struct rxrpc_peer *rxrpc_create_peer(struct rxrpc_sock *rx,
peer = rxrpc_alloc_peer(local, gfp, rxrpc_peer_new_client);
if (peer) {
memcpy(&peer->srx, srx, sizeof(*srx));
- rxrpc_init_peer(rx, peer, hash_key);
+ rxrpc_init_peer(local, peer, hash_key);
}
_leave(" = %p", peer);
@@ -304,14 +303,13 @@ static void rxrpc_free_peer(struct rxrpc_peer *peer)
* since we've already done a search in the list from the non-reentrant context
* (the data_ready handler) that is the only place we can add new peers.
*/
-void rxrpc_new_incoming_peer(struct rxrpc_sock *rx, struct rxrpc_local *local,
- struct rxrpc_peer *peer)
+void rxrpc_new_incoming_peer(struct rxrpc_local *local, struct rxrpc_peer *peer)
{
struct rxrpc_net *rxnet = local->rxnet;
unsigned long hash_key;
hash_key = rxrpc_peer_hash_key(local, &peer->srx);
- rxrpc_init_peer(rx, peer, hash_key);
+ rxrpc_init_peer(local, peer, hash_key);
spin_lock(&rxnet->peer_hash_lock);
hash_add_rcu(rxnet->peer_hash, &peer->hash_link, hash_key);
@@ -322,8 +320,7 @@ void rxrpc_new_incoming_peer(struct rxrpc_sock *rx, struct rxrpc_local *local,
/*
* obtain a remote transport endpoint for the specified address
*/
-struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_sock *rx,
- struct rxrpc_local *local,
+struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_local *local,
struct sockaddr_rxrpc *srx, gfp_t gfp)
{
struct rxrpc_peer *peer, *candidate;
@@ -343,7 +340,7 @@ struct rxrpc_peer *rxrpc_lookup_peer(struct rxrpc_sock *rx,
/* The peer is not yet present in hash - create a candidate
* for a new record and then redo the search.
*/
- candidate = rxrpc_create_peer(rx, local, srx, hash_key, gfp);
+ candidate = rxrpc_create_peer(local, srx, hash_key, gfp);
if (!candidate) {
_leave(" = NULL [nomem]");
return NULL;
diff --git a/net/rxrpc/proc.c b/net/rxrpc/proc.c
index 3a59591ec06152..750158a085cdd1 100644
--- a/net/rxrpc/proc.c
+++ b/net/rxrpc/proc.c
@@ -12,13 +12,13 @@
static const char *const rxrpc_conn_states[RXRPC_CONN__NR_STATES] = {
[RXRPC_CONN_UNUSED] = "Unused ",
+ [RXRPC_CONN_CLIENT_UNSECURED] = "ClUnsec ",
[RXRPC_CONN_CLIENT] = "Client ",
[RXRPC_CONN_SERVICE_PREALLOC] = "SvPrealc",
[RXRPC_CONN_SERVICE_UNSECURED] = "SvUnsec ",
[RXRPC_CONN_SERVICE_CHALLENGING] = "SvChall ",
[RXRPC_CONN_SERVICE] = "SvSecure",
- [RXRPC_CONN_REMOTELY_ABORTED] = "RmtAbort",
- [RXRPC_CONN_LOCALLY_ABORTED] = "LocAbort",
+ [RXRPC_CONN_ABORTED] = "Aborted ",
};
/*
@@ -51,6 +51,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
struct rxrpc_local *local;
struct rxrpc_call *call;
struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
+ enum rxrpc_call_state state;
unsigned long timeout = 0;
rxrpc_seq_t acks_hard_ack;
char lbuff[50], rbuff[50];
@@ -75,7 +76,8 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
sprintf(rbuff, "%pISpc", &call->dest_srx.transport);
- if (call->state != RXRPC_CALL_SERVER_PREALLOC) {
+ state = rxrpc_call_state(call);
+ if (state != RXRPC_CALL_SERVER_PREALLOC) {
timeout = READ_ONCE(call->expect_rx_by);
timeout -= jiffies;
}
@@ -92,7 +94,7 @@ static int rxrpc_call_seq_show(struct seq_file *seq, void *v)
call->call_id,
rxrpc_is_service_call(call) ? "Svc" : "Clt",
refcount_read(&call->ref),
- rxrpc_call_states[call->state],
+ rxrpc_call_states[state],
call->abort_code,
call->debug_id,
acks_hard_ack, READ_ONCE(call->tx_top) - acks_hard_ack,
@@ -143,6 +145,7 @@ static int rxrpc_connection_seq_show(struct seq_file *seq, void *v)
{
struct rxrpc_connection *conn;
struct rxrpc_net *rxnet = rxrpc_net(seq_file_net(seq));
+ const char *state;
char lbuff[50], rbuff[50];
if (v == &rxnet->conn_proc_list) {
@@ -163,9 +166,11 @@ static int rxrpc_connection_seq_show(struct seq_file *seq, void *v)
}
sprintf(lbuff, "%pISpc", &conn->local->srx.transport);
-
sprintf(rbuff, "%pISpc", &conn->peer->srx.transport);
print:
+ state = rxrpc_is_conn_aborted(conn) ?
+ rxrpc_call_completions[conn->completion] :
+ rxrpc_conn_states[conn->state];
seq_printf(seq,
"UDP %-47.47s %-47.47s %4x %08x %s %3u %3d"
" %s %08x %08x %08x %08x %08x %08x %08x\n",
@@ -176,7 +181,7 @@ print:
rxrpc_conn_is_service(conn) ? "Svc" : "Clt",
refcount_read(&conn->ref),
atomic_read(&conn->active),
- rxrpc_conn_states[conn->state],
+ state,
key_serial(conn->key),
atomic_read(&conn->serial),
conn->hi_serial,
diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c
index 36b25d003cf002..dd54ceee7bcc8e 100644
--- a/net/rxrpc/recvmsg.c
+++ b/net/rxrpc/recvmsg.c
@@ -59,85 +59,6 @@ void rxrpc_notify_socket(struct rxrpc_call *call)
}
/*
- * Transition a call to the complete state.
- */
-bool __rxrpc_set_call_completion(struct rxrpc_call *call,
- enum rxrpc_call_completion compl,
- u32 abort_code,
- int error)
-{
- if (call->state < RXRPC_CALL_COMPLETE) {
- call->abort_code = abort_code;
- call->error = error;
- call->completion = compl;
- call->state = RXRPC_CALL_COMPLETE;
- trace_rxrpc_call_complete(call);
- wake_up(&call->waitq);
- rxrpc_notify_socket(call);
- return true;
- }
- return false;
-}
-
-bool rxrpc_set_call_completion(struct rxrpc_call *call,
- enum rxrpc_call_completion compl,
- u32 abort_code,
- int error)
-{
- bool ret = false;
-
- if (call->state < RXRPC_CALL_COMPLETE) {
- write_lock(&call->state_lock);
- ret = __rxrpc_set_call_completion(call, compl, abort_code, error);
- write_unlock(&call->state_lock);
- }
- return ret;
-}
-
-/*
- * Record that a call successfully completed.
- */
-bool __rxrpc_call_completed(struct rxrpc_call *call)
-{
- return __rxrpc_set_call_completion(call, RXRPC_CALL_SUCCEEDED, 0, 0);
-}
-
-bool rxrpc_call_completed(struct rxrpc_call *call)
-{
- bool ret = false;
-
- if (call->state < RXRPC_CALL_COMPLETE) {
- write_lock(&call->state_lock);
- ret = __rxrpc_call_completed(call);
- write_unlock(&call->state_lock);
- }
- return ret;
-}
-
-/*
- * Record that a call is locally aborted.
- */
-bool __rxrpc_abort_call(const char *why, struct rxrpc_call *call,
- rxrpc_seq_t seq, u32 abort_code, int error)
-{
- trace_rxrpc_abort(call->debug_id, why, call->cid, call->call_id, seq,
- abort_code, error);
- return __rxrpc_set_call_completion(call, RXRPC_CALL_LOCALLY_ABORTED,
- abort_code, error);
-}
-
-bool rxrpc_abort_call(const char *why, struct rxrpc_call *call,
- rxrpc_seq_t seq, u32 abort_code, int error)
-{
- bool ret;
-
- write_lock(&call->state_lock);
- ret = __rxrpc_abort_call(why, call, seq, abort_code, error);
- write_unlock(&call->state_lock);
- return ret;
-}
-
-/*
* Pass a call terminating message to userspace.
*/
static int rxrpc_recvmsg_term(struct rxrpc_call *call, struct msghdr *msg)
@@ -168,7 +89,7 @@ static int rxrpc_recvmsg_term(struct rxrpc_call *call, struct msghdr *msg)
ret = put_cmsg(msg, SOL_RXRPC, RXRPC_LOCAL_ERROR, 4, &tmp);
break;
default:
- pr_err("Invalid terminal call state %u\n", call->state);
+ pr_err("Invalid terminal call state %u\n", call->completion);
BUG();
break;
}
@@ -180,41 +101,6 @@ static int rxrpc_recvmsg_term(struct rxrpc_call *call, struct msghdr *msg)
}
/*
- * End the packet reception phase.
- */
-static void rxrpc_end_rx_phase(struct rxrpc_call *call, rxrpc_serial_t serial)
-{
- rxrpc_seq_t whigh = READ_ONCE(call->rx_highest_seq);
-
- _enter("%d,%s", call->debug_id, rxrpc_call_states[call->state]);
-
- trace_rxrpc_receive(call, rxrpc_receive_end, 0, whigh);
-
- if (call->state == RXRPC_CALL_CLIENT_RECV_REPLY)
- rxrpc_propose_delay_ACK(call, serial, rxrpc_propose_ack_terminal_ack);
-
- write_lock(&call->state_lock);
-
- switch (call->state) {
- case RXRPC_CALL_CLIENT_RECV_REPLY:
- __rxrpc_call_completed(call);
- write_unlock(&call->state_lock);
- break;
-
- case RXRPC_CALL_SERVER_RECV_REQUEST:
- call->state = RXRPC_CALL_SERVER_ACK_REQUEST;
- call->expect_req_by = jiffies + MAX_JIFFY_OFFSET;
- write_unlock(&call->state_lock);
- rxrpc_propose_delay_ACK(call, serial,
- rxrpc_propose_ack_processing_op);
- break;
- default:
- write_unlock(&call->state_lock);
- break;
- }
-}
-
-/*
* Discard a packet we've used up and advance the Rx window by one.
*/
static void rxrpc_rotate_rx_window(struct rxrpc_call *call)
@@ -244,10 +130,9 @@ static void rxrpc_rotate_rx_window(struct rxrpc_call *call)
trace_rxrpc_receive(call, last ? rxrpc_receive_rotate_last : rxrpc_receive_rotate,
serial, call->rx_consumed);
- if (last) {
- rxrpc_end_rx_phase(call, serial);
- return;
- }
+
+ if (last)
+ set_bit(RXRPC_CALL_RECVMSG_READ_ALL, &call->flags);
/* Check to see if there's an ACK that needs sending. */
acked = atomic_add_return(call->rx_consumed - old_consumed,
@@ -272,7 +157,8 @@ static int rxrpc_verify_data(struct rxrpc_call *call, struct sk_buff *skb)
/*
* Deliver messages to a call. This keeps processing packets until the buffer
* is filled and we find either more DATA (returns 0) or the end of the DATA
- * (returns 1). If more packets are required, it returns -EAGAIN.
+ * (returns 1). If more packets are required, it returns -EAGAIN and if the
+ * call has failed it returns -EIO.
*/
static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call,
struct msghdr *msg, struct iov_iter *iter,
@@ -288,7 +174,13 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call,
rx_pkt_offset = call->rx_pkt_offset;
rx_pkt_len = call->rx_pkt_len;
- if (call->state >= RXRPC_CALL_SERVER_ACK_REQUEST) {
+ if (rxrpc_call_has_failed(call)) {
+ seq = lower_32_bits(atomic64_read(&call->ackr_window)) - 1;
+ ret = -EIO;
+ goto done;
+ }
+
+ if (test_bit(RXRPC_CALL_RECVMSG_READ_ALL, &call->flags)) {
seq = lower_32_bits(atomic64_read(&call->ackr_window)) - 1;
ret = 1;
goto done;
@@ -312,14 +204,15 @@ static int rxrpc_recvmsg_data(struct socket *sock, struct rxrpc_call *call,
if (rx_pkt_offset == 0) {
ret2 = rxrpc_verify_data(call, skb);
- rx_pkt_offset = sp->offset;
- rx_pkt_len = sp->len;
trace_rxrpc_recvdata(call, rxrpc_recvmsg_next, seq,
- rx_pkt_offset, rx_pkt_len, ret2);
+ sp->offset, sp->len, ret2);
if (ret2 < 0) {
+ kdebug("verify = %d", ret2);
ret = ret2;
goto out;
}
+ rx_pkt_offset = sp->offset;
+ rx_pkt_len = sp->len;
} else {
trace_rxrpc_recvdata(call, rxrpc_recvmsg_cont, seq,
rx_pkt_offset, rx_pkt_len, 0);
@@ -388,13 +281,14 @@ int rxrpc_recvmsg(struct socket *sock, struct msghdr *msg, size_t len,
struct rxrpc_call *call;
struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
struct list_head *l;
+ unsigned int call_debug_id = 0;
size_t copied = 0;
long timeo;
int ret;
DEFINE_WAIT(wait);
- trace_rxrpc_recvmsg(NULL, rxrpc_recvmsg_enter, 0);
+ trace_rxrpc_recvmsg(0, rxrpc_recvmsg_enter, 0);
if (flags & (MSG_OOB | MSG_TRUNC))
return -EOPNOTSUPP;
@@ -431,7 +325,7 @@ try_again:
if (list_empty(&rx->recvmsg_q)) {
if (signal_pending(current))
goto wait_interrupted;
- trace_rxrpc_recvmsg(NULL, rxrpc_recvmsg_wait, 0);
+ trace_rxrpc_recvmsg(0, rxrpc_recvmsg_wait, 0);
timeo = schedule_timeout(timeo);
}
finish_wait(sk_sleep(&rx->sk), &wait);
@@ -450,7 +344,8 @@ try_again:
rxrpc_get_call(call, rxrpc_call_get_recvmsg);
write_unlock(&rx->recvmsg_lock);
- trace_rxrpc_recvmsg(call, rxrpc_recvmsg_dequeue, 0);
+ call_debug_id = call->debug_id;
+ trace_rxrpc_recvmsg(call_debug_id, rxrpc_recvmsg_dequeue, 0);
/* We're going to drop the socket lock, so we need to lock the call
* against interference by sendmsg.
@@ -492,36 +387,36 @@ try_again:
msg->msg_namelen = len;
}
- switch (READ_ONCE(call->state)) {
- case RXRPC_CALL_CLIENT_RECV_REPLY:
- case RXRPC_CALL_SERVER_RECV_REQUEST:
- case RXRPC_CALL_SERVER_ACK_REQUEST:
- ret = rxrpc_recvmsg_data(sock, call, msg, &msg->msg_iter, len,
- flags, &copied);
- if (ret == -EAGAIN)
- ret = 0;
-
- if (!skb_queue_empty(&call->recvmsg_queue))
- rxrpc_notify_socket(call);
- break;
- default:
+ ret = rxrpc_recvmsg_data(sock, call, msg, &msg->msg_iter, len,
+ flags, &copied);
+ if (ret == -EAGAIN)
ret = 0;
- break;
- }
-
+ if (ret == -EIO)
+ goto call_failed;
if (ret < 0)
goto error_unlock_call;
- if (call->state == RXRPC_CALL_COMPLETE) {
- ret = rxrpc_recvmsg_term(call, msg);
- if (ret < 0)
- goto error_unlock_call;
- if (!(flags & MSG_PEEK))
- rxrpc_release_call(rx, call);
- msg->msg_flags |= MSG_EOR;
- ret = 1;
- }
+ if (rxrpc_call_is_complete(call) &&
+ skb_queue_empty(&call->recvmsg_queue))
+ goto call_complete;
+ if (rxrpc_call_has_failed(call))
+ goto call_failed;
+ rxrpc_notify_socket(call);
+ goto not_yet_complete;
+
+call_failed:
+ rxrpc_purge_queue(&call->recvmsg_queue);
+call_complete:
+ ret = rxrpc_recvmsg_term(call, msg);
+ if (ret < 0)
+ goto error_unlock_call;
+ if (!(flags & MSG_PEEK))
+ rxrpc_release_call(rx, call);
+ msg->msg_flags |= MSG_EOR;
+ ret = 1;
+
+not_yet_complete:
if (ret == 0)
msg->msg_flags |= MSG_MORE;
else
@@ -531,7 +426,7 @@ try_again:
error_unlock_call:
mutex_unlock(&call->user_mutex);
rxrpc_put_call(call, rxrpc_call_put_recvmsg);
- trace_rxrpc_recvmsg(call, rxrpc_recvmsg_return, ret);
+ trace_rxrpc_recvmsg(call_debug_id, rxrpc_recvmsg_return, ret);
return ret;
error_requeue_call:
@@ -539,14 +434,14 @@ error_requeue_call:
write_lock(&rx->recvmsg_lock);
list_add(&call->recvmsg_link, &rx->recvmsg_q);
write_unlock(&rx->recvmsg_lock);
- trace_rxrpc_recvmsg(call, rxrpc_recvmsg_requeue, 0);
+ trace_rxrpc_recvmsg(call_debug_id, rxrpc_recvmsg_requeue, 0);
} else {
rxrpc_put_call(call, rxrpc_call_put_recvmsg);
}
error_no_call:
release_sock(&rx->sk);
error_trace:
- trace_rxrpc_recvmsg(call, rxrpc_recvmsg_return, ret);
+ trace_rxrpc_recvmsg(call_debug_id, rxrpc_recvmsg_return, ret);
return ret;
wait_interrupted:
@@ -584,49 +479,34 @@ int rxrpc_kernel_recv_data(struct socket *sock, struct rxrpc_call *call,
size_t offset = 0;
int ret;
- _enter("{%d,%s},%zu,%d",
- call->debug_id, rxrpc_call_states[call->state],
- *_len, want_more);
-
- ASSERTCMP(call->state, !=, RXRPC_CALL_SERVER_SECURING);
+ _enter("{%d},%zu,%d", call->debug_id, *_len, want_more);
mutex_lock(&call->user_mutex);
- switch (READ_ONCE(call->state)) {
- case RXRPC_CALL_CLIENT_RECV_REPLY:
- case RXRPC_CALL_SERVER_RECV_REQUEST:
- case RXRPC_CALL_SERVER_ACK_REQUEST:
- ret = rxrpc_recvmsg_data(sock, call, NULL, iter,
- *_len, 0, &offset);
- *_len -= offset;
- if (ret < 0)
- goto out;
-
- /* We can only reach here with a partially full buffer if we
- * have reached the end of the data. We must otherwise have a
- * full buffer or have been given -EAGAIN.
- */
- if (ret == 1) {
- if (iov_iter_count(iter) > 0)
- goto short_data;
- if (!want_more)
- goto read_phase_complete;
- ret = 0;
- goto out;
- }
-
- if (!want_more)
- goto excess_data;
+ ret = rxrpc_recvmsg_data(sock, call, NULL, iter, *_len, 0, &offset);
+ *_len -= offset;
+ if (ret == -EIO)
+ goto call_failed;
+ if (ret < 0)
goto out;
- case RXRPC_CALL_COMPLETE:
- goto call_complete;
-
- default:
- ret = -EINPROGRESS;
+ /* We can only reach here with a partially full buffer if we have
+ * reached the end of the data. We must otherwise have a full buffer
+ * or have been given -EAGAIN.
+ */
+ if (ret == 1) {
+ if (iov_iter_count(iter) > 0)
+ goto short_data;
+ if (!want_more)
+ goto read_phase_complete;
+ ret = 0;
goto out;
}
+ if (!want_more)
+ goto excess_data;
+ goto out;
+
read_phase_complete:
ret = 1;
out:
@@ -637,14 +517,18 @@ out:
return ret;
short_data:
- trace_rxrpc_rx_eproto(call, 0, tracepoint_string("short_data"));
+ trace_rxrpc_abort(call->debug_id, rxrpc_recvmsg_short_data,
+ call->cid, call->call_id, call->rx_consumed,
+ 0, -EBADMSG);
ret = -EBADMSG;
goto out;
excess_data:
- trace_rxrpc_rx_eproto(call, 0, tracepoint_string("excess_data"));
+ trace_rxrpc_abort(call->debug_id, rxrpc_recvmsg_excess_data,
+ call->cid, call->call_id, call->rx_consumed,
+ 0, -EMSGSIZE);
ret = -EMSGSIZE;
goto out;
-call_complete:
+call_failed:
*_abort = call->abort_code;
ret = call->error;
if (call->completion == RXRPC_CALL_SUCCEEDED) {
diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c
index d1233720e05f24..1bf571a66e020d 100644
--- a/net/rxrpc/rxkad.c
+++ b/net/rxrpc/rxkad.c
@@ -411,18 +411,15 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb,
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
struct rxrpc_crypt iv;
struct scatterlist sg[16];
- bool aborted;
u32 data_size, buf;
u16 check;
int ret;
_enter("");
- if (sp->len < 8) {
- aborted = rxrpc_abort_eproto(call, skb, "rxkad_1_hdr", "V1H",
- RXKADSEALEDINCON);
- goto protocol_error;
- }
+ if (sp->len < 8)
+ return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
+ rxkad_abort_1_short_header);
/* Decrypt the skbuff in-place. TODO: We really want to decrypt
* directly into the target buffer.
@@ -442,11 +439,9 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb,
skcipher_request_zero(req);
/* Extract the decrypted packet length */
- if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0) {
- aborted = rxrpc_abort_eproto(call, skb, "rxkad_1_len", "XV1",
- RXKADDATALEN);
- goto protocol_error;
- }
+ if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0)
+ return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
+ rxkad_abort_1_short_encdata);
sp->offset += sizeof(sechdr);
sp->len -= sizeof(sechdr);
@@ -456,26 +451,16 @@ static int rxkad_verify_packet_1(struct rxrpc_call *call, struct sk_buff *skb,
check = buf >> 16;
check ^= seq ^ call->call_id;
check &= 0xffff;
- if (check != 0) {
- aborted = rxrpc_abort_eproto(call, skb, "rxkad_1_check", "V1C",
- RXKADSEALEDINCON);
- goto protocol_error;
- }
-
- if (data_size > sp->len) {
- aborted = rxrpc_abort_eproto(call, skb, "rxkad_1_datalen", "V1L",
- RXKADDATALEN);
- goto protocol_error;
- }
+ if (check != 0)
+ return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
+ rxkad_abort_1_short_check);
+ if (data_size > sp->len)
+ return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
+ rxkad_abort_1_short_data);
sp->len = data_size;
_leave(" = 0 [dlen=%x]", data_size);
return 0;
-
-protocol_error:
- if (aborted)
- rxrpc_send_abort_packet(call);
- return -EPROTO;
}
/*
@@ -490,18 +475,15 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb,
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
struct rxrpc_crypt iv;
struct scatterlist _sg[4], *sg;
- bool aborted;
u32 data_size, buf;
u16 check;
int nsg, ret;
_enter(",{%d}", sp->len);
- if (sp->len < 8) {
- aborted = rxrpc_abort_eproto(call, skb, "rxkad_2_hdr", "V2H",
- RXKADSEALEDINCON);
- goto protocol_error;
- }
+ if (sp->len < 8)
+ return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
+ rxkad_abort_2_short_header);
/* Decrypt the skbuff in-place. TODO: We really want to decrypt
* directly into the target buffer.
@@ -513,7 +495,7 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb,
} else {
sg = kmalloc_array(nsg, sizeof(*sg), GFP_NOIO);
if (!sg)
- goto nomem;
+ return -ENOMEM;
}
sg_init_table(sg, nsg);
@@ -537,11 +519,9 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb,
kfree(sg);
/* Extract the decrypted packet length */
- if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0) {
- aborted = rxrpc_abort_eproto(call, skb, "rxkad_2_len", "XV2",
- RXKADDATALEN);
- goto protocol_error;
- }
+ if (skb_copy_bits(skb, sp->offset, &sechdr, sizeof(sechdr)) < 0)
+ return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
+ rxkad_abort_2_short_len);
sp->offset += sizeof(sechdr);
sp->len -= sizeof(sechdr);
@@ -551,30 +531,17 @@ static int rxkad_verify_packet_2(struct rxrpc_call *call, struct sk_buff *skb,
check = buf >> 16;
check ^= seq ^ call->call_id;
check &= 0xffff;
- if (check != 0) {
- aborted = rxrpc_abort_eproto(call, skb, "rxkad_2_check", "V2C",
- RXKADSEALEDINCON);
- goto protocol_error;
- }
+ if (check != 0)
+ return rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
+ rxkad_abort_2_short_check);
- if (data_size > sp->len) {
- aborted = rxrpc_abort_eproto(call, skb, "rxkad_2_datalen", "V2L",
- RXKADDATALEN);
- goto protocol_error;
- }
+ if (data_size > sp->len)
+ return rxrpc_abort_eproto(call, skb, RXKADDATALEN,
+ rxkad_abort_2_short_data);
sp->len = data_size;
_leave(" = 0 [dlen=%x]", data_size);
return 0;
-
-protocol_error:
- if (aborted)
- rxrpc_send_abort_packet(call);
- return -EPROTO;
-
-nomem:
- _leave(" = -ENOMEM");
- return -ENOMEM;
}
/*
@@ -590,7 +557,6 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb)
__be32 buf[2];
} crypto __aligned(8);
rxrpc_seq_t seq = sp->hdr.seq;
- bool aborted;
int ret;
u16 cksum;
u32 x, y;
@@ -627,9 +593,9 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb)
cksum = 1; /* zero checksums are not permitted */
if (cksum != sp->hdr.cksum) {
- aborted = rxrpc_abort_eproto(call, skb, "rxkad_csum", "VCK",
- RXKADSEALEDINCON);
- goto protocol_error;
+ ret = rxrpc_abort_eproto(call, skb, RXKADSEALEDINCON,
+ rxkad_abort_bad_checksum);
+ goto out;
}
switch (call->conn->security_level) {
@@ -647,13 +613,9 @@ static int rxkad_verify_packet(struct rxrpc_call *call, struct sk_buff *skb)
break;
}
+out:
skcipher_request_free(req);
return ret;
-
-protocol_error:
- if (aborted)
- rxrpc_send_abort_packet(call);
- return -EPROTO;
}
/*
@@ -821,34 +783,30 @@ static int rxkad_encrypt_response(struct rxrpc_connection *conn,
* respond to a challenge packet
*/
static int rxkad_respond_to_challenge(struct rxrpc_connection *conn,
- struct sk_buff *skb,
- u32 *_abort_code)
+ struct sk_buff *skb)
{
const struct rxrpc_key_token *token;
struct rxkad_challenge challenge;
struct rxkad_response *resp;
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
- const char *eproto;
- u32 version, nonce, min_level, abort_code;
- int ret;
+ u32 version, nonce, min_level;
+ int ret = -EPROTO;
_enter("{%d,%x}", conn->debug_id, key_serial(conn->key));
- eproto = tracepoint_string("chall_no_key");
- abort_code = RX_PROTOCOL_ERROR;
if (!conn->key)
- goto protocol_error;
+ return rxrpc_abort_conn(conn, skb, RX_PROTOCOL_ERROR, -EPROTO,
+ rxkad_abort_chall_no_key);
- abort_code = RXKADEXPIRED;
ret = key_validate(conn->key);
if (ret < 0)
- goto other_error;
+ return rxrpc_abort_conn(conn, skb, RXKADEXPIRED, ret,
+ rxkad_abort_chall_key_expired);
- eproto = tracepoint_string("chall_short");
- abort_code = RXKADPACKETSHORT;
if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
&challenge, sizeof(challenge)) < 0)
- goto protocol_error;
+ return rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
+ rxkad_abort_chall_short);
version = ntohl(challenge.version);
nonce = ntohl(challenge.nonce);
@@ -856,15 +814,13 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn,
trace_rxrpc_rx_challenge(conn, sp->hdr.serial, version, nonce, min_level);
- eproto = tracepoint_string("chall_ver");
- abort_code = RXKADINCONSISTENCY;
if (version != RXKAD_VERSION)
- goto protocol_error;
+ return rxrpc_abort_conn(conn, skb, RXKADINCONSISTENCY, -EPROTO,
+ rxkad_abort_chall_version);
- abort_code = RXKADLEVELFAIL;
- ret = -EACCES;
if (conn->security_level < min_level)
- goto other_error;
+ return rxrpc_abort_conn(conn, skb, RXKADLEVELFAIL, -EACCES,
+ rxkad_abort_chall_level);
token = conn->key->payload.data[0];
@@ -893,13 +849,6 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn,
ret = rxkad_send_response(conn, &sp->hdr, resp, token->kad);
kfree(resp);
return ret;
-
-protocol_error:
- trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, eproto);
- ret = -EPROTO;
-other_error:
- *_abort_code = abort_code;
- return ret;
}
/*
@@ -910,20 +859,15 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
struct sk_buff *skb,
void *ticket, size_t ticket_len,
struct rxrpc_crypt *_session_key,
- time64_t *_expiry,
- u32 *_abort_code)
+ time64_t *_expiry)
{
struct skcipher_request *req;
- struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
struct rxrpc_crypt iv, key;
struct scatterlist sg[1];
struct in_addr addr;
unsigned int life;
- const char *eproto;
time64_t issue, now;
bool little_endian;
- int ret;
- u32 abort_code;
u8 *p, *q, *name, *end;
_enter("{%d},{%x}", conn->debug_id, key_serial(server_key));
@@ -935,10 +879,9 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
memcpy(&iv, &server_key->payload.data[2], sizeof(iv));
- ret = -ENOMEM;
req = skcipher_request_alloc(server_key->payload.data[0], GFP_NOFS);
if (!req)
- goto temporary_error;
+ return -ENOMEM;
sg_init_one(&sg[0], ticket, ticket_len);
skcipher_request_set_callback(req, 0, NULL, NULL);
@@ -949,18 +892,21 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
p = ticket;
end = p + ticket_len;
-#define Z(field) \
- ({ \
- u8 *__str = p; \
- eproto = tracepoint_string("rxkad_bad_"#field); \
- q = memchr(p, 0, end - p); \
- if (!q || q - p > (field##_SZ)) \
- goto bad_ticket; \
- for (; p < q; p++) \
- if (!isprint(*p)) \
- goto bad_ticket; \
- p++; \
- __str; \
+#define Z(field, fieldl) \
+ ({ \
+ u8 *__str = p; \
+ q = memchr(p, 0, end - p); \
+ if (!q || q - p > field##_SZ) \
+ return rxrpc_abort_conn( \
+ conn, skb, RXKADBADTICKET, -EPROTO, \
+ rxkad_abort_resp_tkt_##fieldl); \
+ for (; p < q; p++) \
+ if (!isprint(*p)) \
+ return rxrpc_abort_conn( \
+ conn, skb, RXKADBADTICKET, -EPROTO, \
+ rxkad_abort_resp_tkt_##fieldl); \
+ p++; \
+ __str; \
})
/* extract the ticket flags */
@@ -969,20 +915,20 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
p++;
/* extract the authentication name */
- name = Z(ANAME);
+ name = Z(ANAME, aname);
_debug("KIV ANAME: %s", name);
/* extract the principal's instance */
- name = Z(INST);
+ name = Z(INST, inst);
_debug("KIV INST : %s", name);
/* extract the principal's authentication domain */
- name = Z(REALM);
+ name = Z(REALM, realm);
_debug("KIV REALM: %s", name);
- eproto = tracepoint_string("rxkad_bad_len");
if (end - p < 4 + 8 + 4 + 2)
- goto bad_ticket;
+ return rxrpc_abort_conn(conn, skb, RXKADBADTICKET, -EPROTO,
+ rxkad_abort_resp_tkt_short);
/* get the IPv4 address of the entity that requested the ticket */
memcpy(&addr, p, sizeof(addr));
@@ -1014,38 +960,23 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn,
_debug("KIV ISSUE: %llx [%llx]", issue, now);
/* check the ticket is in date */
- if (issue > now) {
- abort_code = RXKADNOAUTH;
- ret = -EKEYREJECTED;
- goto other_error;
- }
-
- if (issue < now - life) {
- abort_code = RXKADEXPIRED;
- ret = -EKEYEXPIRED;
- goto other_error;
- }
+ if (issue > now)
+ return rxrpc_abort_conn(conn, skb, RXKADNOAUTH, -EKEYREJECTED,
+ rxkad_abort_resp_tkt_future);
+ if (issue < now - life)
+ return rxrpc_abort_conn(conn, skb, RXKADEXPIRED, -EKEYEXPIRED,
+ rxkad_abort_resp_tkt_expired);
*_expiry = issue + life;
/* get the service name */
- name = Z(SNAME);
+ name = Z(SNAME, sname);
_debug("KIV SNAME: %s", name);
/* get the service instance name */
- name = Z(INST);
+ name = Z(INST, sinst);
_debug("KIV SINST: %s", name);
return 0;
-
-bad_ticket:
- trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, eproto);
- abort_code = RXKADBADTICKET;
- ret = -EPROTO;
-other_error:
- *_abort_code = abort_code;
- return ret;
-temporary_error:
- return ret;
}
/*
@@ -1086,17 +1017,15 @@ static void rxkad_decrypt_response(struct rxrpc_connection *conn,
* verify a response
*/
static int rxkad_verify_response(struct rxrpc_connection *conn,
- struct sk_buff *skb,
- u32 *_abort_code)
+ struct sk_buff *skb)
{
struct rxkad_response *response;
struct rxrpc_skb_priv *sp = rxrpc_skb(skb);
struct rxrpc_crypt session_key;
struct key *server_key;
- const char *eproto;
time64_t expiry;
void *ticket;
- u32 abort_code, version, kvno, ticket_len, level;
+ u32 version, kvno, ticket_len, level;
__be32 csum;
int ret, i;
@@ -1104,22 +1033,18 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
server_key = rxrpc_look_up_server_security(conn, skb, 0, 0);
if (IS_ERR(server_key)) {
- switch (PTR_ERR(server_key)) {
+ ret = PTR_ERR(server_key);
+ switch (ret) {
case -ENOKEY:
- abort_code = RXKADUNKNOWNKEY;
- break;
+ return rxrpc_abort_conn(conn, skb, RXKADUNKNOWNKEY, ret,
+ rxkad_abort_resp_nokey);
case -EKEYEXPIRED:
- abort_code = RXKADEXPIRED;
- break;
+ return rxrpc_abort_conn(conn, skb, RXKADEXPIRED, ret,
+ rxkad_abort_resp_key_expired);
default:
- abort_code = RXKADNOAUTH;
- break;
+ return rxrpc_abort_conn(conn, skb, RXKADNOAUTH, ret,
+ rxkad_abort_resp_key_rejected);
}
- trace_rxrpc_abort(0, "SVK",
- sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
- abort_code, PTR_ERR(server_key));
- *_abort_code = abort_code;
- return -EPROTO;
}
ret = -ENOMEM;
@@ -1127,11 +1052,12 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
if (!response)
goto temporary_error;
- eproto = tracepoint_string("rxkad_rsp_short");
- abort_code = RXKADPACKETSHORT;
if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
- response, sizeof(*response)) < 0)
+ response, sizeof(*response)) < 0) {
+ rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
+ rxkad_abort_resp_short);
goto protocol_error;
+ }
version = ntohl(response->version);
ticket_len = ntohl(response->ticket_len);
@@ -1139,20 +1065,23 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
trace_rxrpc_rx_response(conn, sp->hdr.serial, version, kvno, ticket_len);
- eproto = tracepoint_string("rxkad_rsp_ver");
- abort_code = RXKADINCONSISTENCY;
- if (version != RXKAD_VERSION)
+ if (version != RXKAD_VERSION) {
+ rxrpc_abort_conn(conn, skb, RXKADINCONSISTENCY, -EPROTO,
+ rxkad_abort_resp_version);
goto protocol_error;
+ }
- eproto = tracepoint_string("rxkad_rsp_tktlen");
- abort_code = RXKADTICKETLEN;
- if (ticket_len < 4 || ticket_len > MAXKRB5TICKETLEN)
+ if (ticket_len < 4 || ticket_len > MAXKRB5TICKETLEN) {
+ rxrpc_abort_conn(conn, skb, RXKADTICKETLEN, -EPROTO,
+ rxkad_abort_resp_tkt_len);
goto protocol_error;
+ }
- eproto = tracepoint_string("rxkad_rsp_unkkey");
- abort_code = RXKADUNKNOWNKEY;
- if (kvno >= RXKAD_TKT_TYPE_KERBEROS_V5)
+ if (kvno >= RXKAD_TKT_TYPE_KERBEROS_V5) {
+ rxrpc_abort_conn(conn, skb, RXKADUNKNOWNKEY, -EPROTO,
+ rxkad_abort_resp_unknown_tkt);
goto protocol_error;
+ }
/* extract the kerberos ticket and decrypt and decode it */
ret = -ENOMEM;
@@ -1160,15 +1089,15 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
if (!ticket)
goto temporary_error_free_resp;
- eproto = tracepoint_string("rxkad_tkt_short");
- abort_code = RXKADPACKETSHORT;
- ret = skb_copy_bits(skb, sizeof(struct rxrpc_wire_header) + sizeof(*response),
- ticket, ticket_len);
- if (ret < 0)
- goto temporary_error_free_ticket;
+ if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header) + sizeof(*response),
+ ticket, ticket_len) < 0) {
+ rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
+ rxkad_abort_resp_short_tkt);
+ goto protocol_error;
+ }
ret = rxkad_decrypt_ticket(conn, server_key, skb, ticket, ticket_len,
- &session_key, &expiry, _abort_code);
+ &session_key, &expiry);
if (ret < 0)
goto temporary_error_free_ticket;
@@ -1176,56 +1105,61 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
* response */
rxkad_decrypt_response(conn, response, &session_key);
- eproto = tracepoint_string("rxkad_rsp_param");
- abort_code = RXKADSEALEDINCON;
- if (ntohl(response->encrypted.epoch) != conn->proto.epoch)
- goto protocol_error_free;
- if (ntohl(response->encrypted.cid) != conn->proto.cid)
- goto protocol_error_free;
- if (ntohl(response->encrypted.securityIndex) != conn->security_ix)
+ if (ntohl(response->encrypted.epoch) != conn->proto.epoch ||
+ ntohl(response->encrypted.cid) != conn->proto.cid ||
+ ntohl(response->encrypted.securityIndex) != conn->security_ix) {
+ rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
+ rxkad_abort_resp_bad_param);
goto protocol_error_free;
+ }
+
csum = response->encrypted.checksum;
response->encrypted.checksum = 0;
rxkad_calc_response_checksum(response);
- eproto = tracepoint_string("rxkad_rsp_csum");
- if (response->encrypted.checksum != csum)
+ if (response->encrypted.checksum != csum) {
+ rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
+ rxkad_abort_resp_bad_checksum);
goto protocol_error_free;
+ }
- spin_lock(&conn->bundle->channel_lock);
for (i = 0; i < RXRPC_MAXCALLS; i++) {
- struct rxrpc_call *call;
u32 call_id = ntohl(response->encrypted.call_id[i]);
+ u32 counter = READ_ONCE(conn->channels[i].call_counter);
+
+ if (call_id > INT_MAX) {
+ rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
+ rxkad_abort_resp_bad_callid);
+ goto protocol_error_free;
+ }
- eproto = tracepoint_string("rxkad_rsp_callid");
- if (call_id > INT_MAX)
- goto protocol_error_unlock;
-
- eproto = tracepoint_string("rxkad_rsp_callctr");
- if (call_id < conn->channels[i].call_counter)
- goto protocol_error_unlock;
-
- eproto = tracepoint_string("rxkad_rsp_callst");
- if (call_id > conn->channels[i].call_counter) {
- call = rcu_dereference_protected(
- conn->channels[i].call,
- lockdep_is_held(&conn->bundle->channel_lock));
- if (call && call->state < RXRPC_CALL_COMPLETE)
- goto protocol_error_unlock;
+ if (call_id < counter) {
+ rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
+ rxkad_abort_resp_call_ctr);
+ goto protocol_error_free;
+ }
+
+ if (call_id > counter) {
+ if (conn->channels[i].call) {
+ rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
+ rxkad_abort_resp_call_state);
+ goto protocol_error_free;
+ }
conn->channels[i].call_counter = call_id;
}
}
- spin_unlock(&conn->bundle->channel_lock);
- eproto = tracepoint_string("rxkad_rsp_seq");
- abort_code = RXKADOUTOFSEQUENCE;
- if (ntohl(response->encrypted.inc_nonce) != conn->rxkad.nonce + 1)
+ if (ntohl(response->encrypted.inc_nonce) != conn->rxkad.nonce + 1) {
+ rxrpc_abort_conn(conn, skb, RXKADOUTOFSEQUENCE, -EPROTO,
+ rxkad_abort_resp_ooseq);
goto protocol_error_free;
+ }
- eproto = tracepoint_string("rxkad_rsp_level");
- abort_code = RXKADLEVELFAIL;
level = ntohl(response->encrypted.level);
- if (level > RXRPC_SECURITY_ENCRYPT)
+ if (level > RXRPC_SECURITY_ENCRYPT) {
+ rxrpc_abort_conn(conn, skb, RXKADLEVELFAIL, -EPROTO,
+ rxkad_abort_resp_level);
goto protocol_error_free;
+ }
conn->security_level = level;
/* create a key to hold the security data and expiration time - after
@@ -1240,15 +1174,11 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
_leave(" = 0");
return 0;
-protocol_error_unlock:
- spin_unlock(&conn->bundle->channel_lock);
protocol_error_free:
kfree(ticket);
protocol_error:
kfree(response);
- trace_rxrpc_rx_eproto(NULL, sp->hdr.serial, eproto);
key_put(server_key);
- *_abort_code = abort_code;
return -EPROTO;
temporary_error_free_ticket:
diff --git a/net/rxrpc/rxperf.c b/net/rxrpc/rxperf.c
index d33a109e846c1b..16dcabb71ebe16 100644
--- a/net/rxrpc/rxperf.c
+++ b/net/rxrpc/rxperf.c
@@ -10,6 +10,8 @@
#include <linux/slab.h>
#include <net/sock.h>
#include <net/af_rxrpc.h>
+#define RXRPC_TRACE_ONLY_DEFINE_ENUMS
+#include <trace/events/rxrpc.h>
MODULE_DESCRIPTION("rxperf test server (afs)");
MODULE_AUTHOR("Red Hat, Inc.");
@@ -307,12 +309,14 @@ static void rxperf_deliver_to_call(struct work_struct *work)
case -EOPNOTSUPP:
abort_code = RXGEN_OPCODE;
rxrpc_kernel_abort_call(rxperf_socket, call->rxcall,
- abort_code, ret, "GOP");
+ abort_code, ret,
+ rxperf_abort_op_not_supported);
goto call_complete;
case -ENOTSUPP:
abort_code = RX_USER_ABORT;
rxrpc_kernel_abort_call(rxperf_socket, call->rxcall,
- abort_code, ret, "GUA");
+ abort_code, ret,
+ rxperf_abort_op_not_supported);
goto call_complete;
case -EIO:
pr_err("Call %u in bad state %u\n",
@@ -324,11 +328,13 @@ static void rxperf_deliver_to_call(struct work_struct *work)
case -ENOMEM:
case -EFAULT:
rxrpc_kernel_abort_call(rxperf_socket, call->rxcall,
- RXGEN_SS_UNMARSHAL, ret, "GUM");
+ RXGEN_SS_UNMARSHAL, ret,
+ rxperf_abort_unmarshal_error);
goto call_complete;
default:
rxrpc_kernel_abort_call(rxperf_socket, call->rxcall,
- RX_CALL_DEAD, ret, "GER");
+ RX_CALL_DEAD, ret,
+ rxperf_abort_general_error);
goto call_complete;
}
}
@@ -523,7 +529,8 @@ static int rxperf_process_call(struct rxperf_call *call)
if (n == -ENOMEM)
rxrpc_kernel_abort_call(rxperf_socket, call->rxcall,
- RXGEN_SS_MARSHAL, -ENOMEM, "GOM");
+ RXGEN_SS_MARSHAL, -ENOMEM,
+ rxperf_abort_oom);
return n;
}
diff --git a/net/rxrpc/security.c b/net/rxrpc/security.c
index ab968f65a4900d..cb8dd1d3b1d49e 100644
--- a/net/rxrpc/security.c
+++ b/net/rxrpc/security.c
@@ -97,38 +97,31 @@ found:
*/
int rxrpc_init_client_conn_security(struct rxrpc_connection *conn)
{
- const struct rxrpc_security *sec;
struct rxrpc_key_token *token;
struct key *key = conn->key;
- int ret;
+ int ret = 0;
_enter("{%d},{%x}", conn->debug_id, key_serial(key));
- if (!key)
- return 0;
-
- ret = key_validate(key);
- if (ret < 0)
- return ret;
-
for (token = key->payload.data[0]; token; token = token->next) {
- sec = rxrpc_security_lookup(token->security_index);
- if (sec)
+ if (token->security_index == conn->security->security_index)
goto found;
}
return -EKEYREJECTED;
found:
- conn->security = sec;
-
- ret = conn->security->init_connection_security(conn, token);
- if (ret < 0) {
- conn->security = &rxrpc_no_security;
- return ret;
+ mutex_lock(&conn->security_lock);
+ if (conn->state == RXRPC_CONN_CLIENT_UNSECURED) {
+ ret = conn->security->init_connection_security(conn, token);
+ if (ret == 0) {
+ spin_lock(&conn->state_lock);
+ if (conn->state == RXRPC_CONN_CLIENT_UNSECURED)
+ conn->state = RXRPC_CONN_CLIENT;
+ spin_unlock(&conn->state_lock);
+ }
}
-
- _leave(" = 0");
- return 0;
+ mutex_unlock(&conn->security_lock);
+ return ret;
}
/*
@@ -144,21 +137,15 @@ const struct rxrpc_security *rxrpc_get_incoming_security(struct rxrpc_sock *rx,
sec = rxrpc_security_lookup(sp->hdr.securityIndex);
if (!sec) {
- trace_rxrpc_abort(0, "SVS",
- sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
- RX_INVALID_OPERATION, EKEYREJECTED);
- skb->mark = RXRPC_SKB_MARK_REJECT_ABORT;
- skb->priority = RX_INVALID_OPERATION;
+ rxrpc_direct_abort(skb, rxrpc_abort_unsupported_security,
+ RX_INVALID_OPERATION, -EKEYREJECTED);
return NULL;
}
if (sp->hdr.securityIndex != RXRPC_SECURITY_NONE &&
!rx->securities) {
- trace_rxrpc_abort(0, "SVR",
- sp->hdr.cid, sp->hdr.callNumber, sp->hdr.seq,
- RX_INVALID_OPERATION, EKEYREJECTED);
- skb->mark = RXRPC_SKB_MARK_REJECT_ABORT;
- skb->priority = sec->no_key_abort;
+ rxrpc_direct_abort(skb, rxrpc_abort_no_service_key,
+ sec->no_key_abort, -EKEYREJECTED);
return NULL;
}
@@ -191,9 +178,9 @@ struct key *rxrpc_look_up_server_security(struct rxrpc_connection *conn,
sprintf(kdesc, "%u:%u",
sp->hdr.serviceId, sp->hdr.securityIndex);
- rcu_read_lock();
+ read_lock(&conn->local->services_lock);
- rx = rcu_dereference(conn->local->service);
+ rx = conn->local->service;
if (!rx)
goto out;
@@ -215,6 +202,6 @@ struct key *rxrpc_look_up_server_security(struct rxrpc_connection *conn,
}
out:
- rcu_read_unlock();
+ read_unlock(&conn->local->services_lock);
return key;
}
diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c
index cde1e65f16b454..da49fcf1c45674 100644
--- a/net/rxrpc/sendmsg.c
+++ b/net/rxrpc/sendmsg.c
@@ -18,6 +18,81 @@
#include "ar-internal.h"
/*
+ * Propose an abort to be made in the I/O thread.
+ */
+bool rxrpc_propose_abort(struct rxrpc_call *call, s32 abort_code, int error,
+ enum rxrpc_abort_reason why)
+{
+ _enter("{%d},%d,%d,%u", call->debug_id, abort_code, error, why);
+
+ if (!call->send_abort && !rxrpc_call_is_complete(call)) {
+ call->send_abort_why = why;
+ call->send_abort_err = error;
+ call->send_abort_seq = 0;
+ /* Request abort locklessly vs rxrpc_input_call_event(). */
+ smp_store_release(&call->send_abort, abort_code);
+ rxrpc_poke_call(call, rxrpc_call_poke_abort);
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Wait for a call to become connected. Interruption here doesn't cause the
+ * call to be aborted.
+ */
+static int rxrpc_wait_to_be_connected(struct rxrpc_call *call, long *timeo)
+{
+ DECLARE_WAITQUEUE(myself, current);
+ int ret = 0;
+
+ _enter("%d", call->debug_id);
+
+ if (rxrpc_call_state(call) != RXRPC_CALL_CLIENT_AWAIT_CONN)
+ return call->error;
+
+ add_wait_queue_exclusive(&call->waitq, &myself);
+
+ for (;;) {
+ ret = call->error;
+ if (ret < 0)
+ break;
+
+ switch (call->interruptibility) {
+ case RXRPC_INTERRUPTIBLE:
+ case RXRPC_PREINTERRUPTIBLE:
+ set_current_state(TASK_INTERRUPTIBLE);
+ break;
+ case RXRPC_UNINTERRUPTIBLE:
+ default:
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ break;
+ }
+ if (rxrpc_call_state(call) != RXRPC_CALL_CLIENT_AWAIT_CONN) {
+ ret = call->error;
+ break;
+ }
+ if ((call->interruptibility == RXRPC_INTERRUPTIBLE ||
+ call->interruptibility == RXRPC_PREINTERRUPTIBLE) &&
+ signal_pending(current)) {
+ ret = sock_intr_errno(*timeo);
+ break;
+ }
+ *timeo = schedule_timeout(*timeo);
+ }
+
+ remove_wait_queue(&call->waitq, &myself);
+ __set_current_state(TASK_RUNNING);
+
+ if (ret == 0 && rxrpc_call_is_complete(call))
+ ret = call->error;
+
+ _leave(" = %d", ret);
+ return ret;
+}
+
+/*
* Return true if there's sufficient Tx queue space.
*/
static bool rxrpc_check_tx_space(struct rxrpc_call *call, rxrpc_seq_t *_tx_win)
@@ -39,7 +114,7 @@ static int rxrpc_wait_for_tx_window_intr(struct rxrpc_sock *rx,
if (rxrpc_check_tx_space(call, NULL))
return 0;
- if (call->state >= RXRPC_CALL_COMPLETE)
+ if (rxrpc_call_is_complete(call))
return call->error;
if (signal_pending(current))
@@ -74,7 +149,7 @@ static int rxrpc_wait_for_tx_window_waitall(struct rxrpc_sock *rx,
if (rxrpc_check_tx_space(call, &tx_win))
return 0;
- if (call->state >= RXRPC_CALL_COMPLETE)
+ if (rxrpc_call_is_complete(call))
return call->error;
if (timeout == 0 &&
@@ -103,7 +178,7 @@ static int rxrpc_wait_for_tx_window_nonintr(struct rxrpc_sock *rx,
if (rxrpc_check_tx_space(call, NULL))
return 0;
- if (call->state >= RXRPC_CALL_COMPLETE)
+ if (rxrpc_call_is_complete(call))
return call->error;
trace_rxrpc_txqueue(call, rxrpc_txqueue_wait);
@@ -168,7 +243,6 @@ static void rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call,
struct rxrpc_txbuf *txb,
rxrpc_notify_end_tx_t notify_end_tx)
{
- unsigned long now;
rxrpc_seq_t seq = txb->seq;
bool last = test_bit(RXRPC_TXBUF_LAST, &txb->flags), poke;
@@ -191,36 +265,10 @@ static void rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call,
poke = list_empty(&call->tx_sendmsg);
list_add_tail(&txb->call_link, &call->tx_sendmsg);
call->tx_prepared = seq;
+ if (last)
+ rxrpc_notify_end_tx(rx, call, notify_end_tx);
spin_unlock(&call->tx_lock);
- if (last || call->state == RXRPC_CALL_SERVER_ACK_REQUEST) {
- _debug("________awaiting reply/ACK__________");
- write_lock(&call->state_lock);
- switch (call->state) {
- case RXRPC_CALL_CLIENT_SEND_REQUEST:
- call->state = RXRPC_CALL_CLIENT_AWAIT_REPLY;
- rxrpc_notify_end_tx(rx, call, notify_end_tx);
- break;
- case RXRPC_CALL_SERVER_ACK_REQUEST:
- call->state = RXRPC_CALL_SERVER_SEND_REPLY;
- now = jiffies;
- WRITE_ONCE(call->delay_ack_at, now + MAX_JIFFY_OFFSET);
- if (call->ackr_reason == RXRPC_ACK_DELAY)
- call->ackr_reason = 0;
- trace_rxrpc_timer(call, rxrpc_timer_init_for_send_reply, now);
- if (!last)
- break;
- fallthrough;
- case RXRPC_CALL_SERVER_SEND_REPLY:
- call->state = RXRPC_CALL_SERVER_AWAIT_ACK;
- rxrpc_notify_end_tx(rx, call, notify_end_tx);
- break;
- default:
- break;
- }
- write_unlock(&call->state_lock);
- }
-
if (poke)
rxrpc_poke_call(call, rxrpc_call_poke_start);
}
@@ -245,6 +293,16 @@ static int rxrpc_send_data(struct rxrpc_sock *rx,
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
+ ret = rxrpc_wait_to_be_connected(call, &timeo);
+ if (ret < 0)
+ return ret;
+
+ if (call->conn->state == RXRPC_CONN_CLIENT_UNSECURED) {
+ ret = rxrpc_init_client_conn_security(call->conn);
+ if (ret < 0)
+ return ret;
+ }
+
/* this should be in poll */
sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);
@@ -252,15 +310,20 @@ reload:
ret = -EPIPE;
if (sk->sk_shutdown & SEND_SHUTDOWN)
goto maybe_error;
- state = READ_ONCE(call->state);
+ state = rxrpc_call_state(call);
ret = -ESHUTDOWN;
if (state >= RXRPC_CALL_COMPLETE)
goto maybe_error;
ret = -EPROTO;
if (state != RXRPC_CALL_CLIENT_SEND_REQUEST &&
state != RXRPC_CALL_SERVER_ACK_REQUEST &&
- state != RXRPC_CALL_SERVER_SEND_REPLY)
+ state != RXRPC_CALL_SERVER_SEND_REPLY) {
+ /* Request phase complete for this client call */
+ trace_rxrpc_abort(call->debug_id, rxrpc_sendmsg_late_send,
+ call->cid, call->call_id, call->rx_consumed,
+ 0, -EPROTO);
goto maybe_error;
+ }
ret = -EMSGSIZE;
if (call->tx_total_len != -1) {
@@ -329,7 +392,7 @@ reload:
/* check for the far side aborting the call or a network error
* occurring */
- if (call->state == RXRPC_CALL_COMPLETE)
+ if (rxrpc_call_is_complete(call))
goto call_terminated;
/* add the packet to the send queue if it's now full */
@@ -354,12 +417,9 @@ reload:
success:
ret = copied;
- if (READ_ONCE(call->state) == RXRPC_CALL_COMPLETE) {
- read_lock(&call->state_lock);
- if (call->error < 0)
- ret = call->error;
- read_unlock(&call->state_lock);
- }
+ if (rxrpc_call_is_complete(call) &&
+ call->error < 0)
+ ret = call->error;
out:
call->tx_pending = txb;
_leave(" = %d", ret);
@@ -543,7 +603,6 @@ rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg,
atomic_inc_return(&rxrpc_debug_id));
/* The socket is now unlocked */
- rxrpc_put_peer(cp.peer, rxrpc_peer_put_discard_tmp);
_leave(" = %p\n", call);
return call;
}
@@ -556,7 +615,6 @@ rxrpc_new_client_call_for_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg,
int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
__releases(&rx->sk.sk_lock.slock)
{
- enum rxrpc_call_state state;
struct rxrpc_call *call;
unsigned long now, j;
bool dropped_lock = false;
@@ -598,10 +656,10 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
return PTR_ERR(call);
/* ... and we have the call lock. */
ret = 0;
- if (READ_ONCE(call->state) == RXRPC_CALL_COMPLETE)
+ if (rxrpc_call_is_complete(call))
goto out_put_unlock;
} else {
- switch (READ_ONCE(call->state)) {
+ switch (rxrpc_call_state(call)) {
case RXRPC_CALL_UNINITIALISED:
case RXRPC_CALL_CLIENT_AWAIT_CONN:
case RXRPC_CALL_SERVER_PREALLOC:
@@ -655,17 +713,13 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len)
break;
}
- state = READ_ONCE(call->state);
- _debug("CALL %d USR %lx ST %d on CONN %p",
- call->debug_id, call->user_call_ID, state, call->conn);
-
- if (state >= RXRPC_CALL_COMPLETE) {
+ if (rxrpc_call_is_complete(call)) {
/* it's too late for this call */
ret = -ESHUTDOWN;
} else if (p.command == RXRPC_CMD_SEND_ABORT) {
+ rxrpc_propose_abort(call, p.abort_code, -ECONNABORTED,
+ rxrpc_abort_call_sendmsg);
ret = 0;
- if (rxrpc_abort_call("CMD", call, 0, p.abort_code, -ECONNABORTED))
- ret = rxrpc_send_abort_packet(call);
} else if (p.command != RXRPC_CMD_SEND_DATA) {
ret = -EINVAL;
} else {
@@ -705,34 +759,17 @@ int rxrpc_kernel_send_data(struct socket *sock, struct rxrpc_call *call,
bool dropped_lock = false;
int ret;
- _enter("{%d,%s},", call->debug_id, rxrpc_call_states[call->state]);
+ _enter("{%d},", call->debug_id);
ASSERTCMP(msg->msg_name, ==, NULL);
ASSERTCMP(msg->msg_control, ==, NULL);
mutex_lock(&call->user_mutex);
- _debug("CALL %d USR %lx ST %d on CONN %p",
- call->debug_id, call->user_call_ID, call->state, call->conn);
-
- switch (READ_ONCE(call->state)) {
- case RXRPC_CALL_CLIENT_SEND_REQUEST:
- case RXRPC_CALL_SERVER_ACK_REQUEST:
- case RXRPC_CALL_SERVER_SEND_REPLY:
- ret = rxrpc_send_data(rxrpc_sk(sock->sk), call, msg, len,
- notify_end_tx, &dropped_lock);
- break;
- case RXRPC_CALL_COMPLETE:
- read_lock(&call->state_lock);
+ ret = rxrpc_send_data(rxrpc_sk(sock->sk), call, msg, len,
+ notify_end_tx, &dropped_lock);
+ if (ret == -ESHUTDOWN)
ret = call->error;
- read_unlock(&call->state_lock);
- break;
- default:
- /* Request phase complete for this client call */
- trace_rxrpc_rx_eproto(call, 0, tracepoint_string("late_send"));
- ret = -EPROTO;
- break;
- }
if (!dropped_lock)
mutex_unlock(&call->user_mutex);
@@ -747,24 +784,20 @@ EXPORT_SYMBOL(rxrpc_kernel_send_data);
* @call: The call to be aborted
* @abort_code: The abort code to stick into the ABORT packet
* @error: Local error value
- * @why: 3-char string indicating why.
+ * @why: Indication as to why.
*
* Allow a kernel service to abort a call, if it's still in an abortable state
* and return true if the call was aborted, false if it was already complete.
*/
bool rxrpc_kernel_abort_call(struct socket *sock, struct rxrpc_call *call,
- u32 abort_code, int error, const char *why)
+ u32 abort_code, int error, enum rxrpc_abort_reason why)
{
bool aborted;
- _enter("{%d},%d,%d,%s", call->debug_id, abort_code, error, why);
+ _enter("{%d},%d,%d,%u", call->debug_id, abort_code, error, why);
mutex_lock(&call->user_mutex);
-
- aborted = rxrpc_abort_call(why, call, 0, abort_code, error);
- if (aborted)
- rxrpc_send_abort_packet(call);
-
+ aborted = rxrpc_propose_abort(call, abort_code, error, why);
mutex_unlock(&call->user_mutex);
return aborted;
}
diff --git a/net/sched/act_mpls.c b/net/sched/act_mpls.c
index ff47ce4d396854..6b26bdb999d77a 100644
--- a/net/sched/act_mpls.c
+++ b/net/sched/act_mpls.c
@@ -134,6 +134,11 @@ static int valid_label(const struct nlattr *attr,
{
const u32 *label = nla_data(attr);
+ if (nla_len(attr) != sizeof(*label)) {
+ NL_SET_ERR_MSG_MOD(extack, "Invalid MPLS label length");
+ return -EINVAL;
+ }
+
if (*label & ~MPLS_LABEL_MASK || *label == MPLS_LABEL_IMPLNULL) {
NL_SET_ERR_MSG_MOD(extack, "MPLS label out of range");
return -EINVAL;
@@ -145,7 +150,8 @@ static int valid_label(const struct nlattr *attr,
static const struct nla_policy mpls_policy[TCA_MPLS_MAX + 1] = {
[TCA_MPLS_PARMS] = NLA_POLICY_EXACT_LEN(sizeof(struct tc_mpls)),
[TCA_MPLS_PROTO] = { .type = NLA_U16 },
- [TCA_MPLS_LABEL] = NLA_POLICY_VALIDATE_FN(NLA_U32, valid_label),
+ [TCA_MPLS_LABEL] = NLA_POLICY_VALIDATE_FN(NLA_BINARY,
+ valid_label),
[TCA_MPLS_TC] = NLA_POLICY_RANGE(NLA_U8, 0, 7),
[TCA_MPLS_TTL] = NLA_POLICY_MIN(NLA_U8, 1),
[TCA_MPLS_BOS] = NLA_POLICY_RANGE(NLA_U8, 0, 1),
diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c
index eb0e9458e722e4..ee2a050c887bfd 100644
--- a/net/sched/cls_tcindex.c
+++ b/net/sched/cls_tcindex.c
@@ -333,7 +333,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
struct tcindex_filter_result *r, struct nlattr **tb,
struct nlattr *est, u32 flags, struct netlink_ext_ack *extack)
{
- struct tcindex_filter_result new_filter_result, *old_r = r;
+ struct tcindex_filter_result new_filter_result;
struct tcindex_data *cp = NULL, *oldp;
struct tcindex_filter *f = NULL; /* make gcc behave */
struct tcf_result cr = {};
@@ -402,7 +402,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
err = tcindex_filter_result_init(&new_filter_result, cp, net);
if (err < 0)
goto errout_alloc;
- if (old_r)
+ if (r)
cr = r->res;
err = -EBUSY;
@@ -479,14 +479,6 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
tcf_bind_filter(tp, &cr, base);
}
- if (old_r && old_r != r) {
- err = tcindex_filter_result_init(old_r, cp, net);
- if (err < 0) {
- kfree(f);
- goto errout_alloc;
- }
- }
-
oldp = p;
r->res = cr;
tcf_exts_change(&r->exts, &e);
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 2317db02c764d2..72d2c204d5f340 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -1133,6 +1133,11 @@ skip:
return -ENOENT;
}
+ if (new && new->ops == &noqueue_qdisc_ops) {
+ NL_SET_ERR_MSG(extack, "Cannot assign noqueue to a class");
+ return -EINVAL;
+ }
+
err = cops->graft(parent, cl, new, &old, extack);
if (err)
return err;
diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c
index f52255fea652be..4a981ca90b0bfe 100644
--- a/net/sched/sch_atm.c
+++ b/net/sched/sch_atm.c
@@ -393,10 +393,13 @@ static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch,
result = tcf_classify(skb, NULL, fl, &res, true);
if (result < 0)
continue;
+ if (result == TC_ACT_SHOT)
+ goto done;
+
flow = (struct atm_flow_data *)res.class;
if (!flow)
flow = lookup_flow(sch, res.classid);
- goto done;
+ goto drop;
}
}
flow = NULL;
diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
index 6568e17c4c6341..36db5f6782f2cf 100644
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -230,6 +230,8 @@ cbq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
result = tcf_classify(skb, NULL, fl, &res, true);
if (!fl || result < 0)
goto fallback;
+ if (result == TC_ACT_SHOT)
+ return NULL;
cl = (void *)res.class;
if (!cl) {
@@ -250,8 +252,6 @@ cbq_classify(struct sk_buff *skb, struct Qdisc *sch, int *qerr)
case TC_ACT_TRAP:
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
fallthrough;
- case TC_ACT_SHOT:
- return NULL;
case TC_ACT_RECLASSIFY:
return cbq_reclassify(skb, cl);
}
diff --git a/net/sched/sch_gred.c b/net/sched/sch_gred.c
index a661b062cca854..872d127c9db42a 100644
--- a/net/sched/sch_gred.c
+++ b/net/sched/sch_gred.c
@@ -377,6 +377,7 @@ static int gred_offload_dump_stats(struct Qdisc *sch)
/* Even if driver returns failure adjust the stats - in case offload
* ended but driver still wants to adjust the values.
*/
+ sch_tree_lock(sch);
for (i = 0; i < MAX_DPs; i++) {
if (!table->tab[i])
continue;
@@ -393,6 +394,7 @@ static int gred_offload_dump_stats(struct Qdisc *sch)
sch->qstats.overlimits += hw_stats->stats.qstats[i].overlimits;
}
_bstats_update(&sch->bstats, bytes, packets);
+ sch_tree_unlock(sch);
kfree(hw_stats);
return ret;
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index e5b4bbf3ce3d5f..92f2975b6a8277 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -199,8 +199,14 @@ static unsigned long htb_search(struct Qdisc *sch, u32 handle)
{
return (unsigned long)htb_find(handle, sch);
}
+
+#define HTB_DIRECT ((struct htb_class *)-1L)
+
/**
* htb_classify - classify a packet into class
+ * @skb: the socket buffer
+ * @sch: the active queue discipline
+ * @qerr: pointer for returned status code
*
* It returns NULL if the packet should be dropped or -1 if the packet
* should be passed directly thru. In all other cases leaf class is returned.
@@ -211,8 +217,6 @@ static unsigned long htb_search(struct Qdisc *sch, u32 handle)
* have no valid leaf we try to use MAJOR:default leaf. It still unsuccessful
* then finish and return direct queue.
*/
-#define HTB_DIRECT ((struct htb_class *)-1L)
-
static struct htb_class *htb_classify(struct sk_buff *skb, struct Qdisc *sch,
int *qerr)
{
@@ -427,7 +431,10 @@ static void htb_activate_prios(struct htb_sched *q, struct htb_class *cl)
while (cl->cmode == HTB_MAY_BORROW && p && mask) {
m = mask;
while (m) {
- int prio = ffz(~m);
+ unsigned int prio = ffz(~m);
+
+ if (WARN_ON_ONCE(prio >= ARRAY_SIZE(p->inner.clprio)))
+ break;
m &= ~(1 << prio);
if (p->inner.clprio[prio].feed.rb_node)
@@ -1545,7 +1552,7 @@ static int htb_destroy_class_offload(struct Qdisc *sch, struct htb_class *cl,
struct tc_htb_qopt_offload offload_opt;
struct netdev_queue *dev_queue;
struct Qdisc *q = cl->leaf.q;
- struct Qdisc *old = NULL;
+ struct Qdisc *old;
int err;
if (cl->level)
@@ -1553,14 +1560,17 @@ static int htb_destroy_class_offload(struct Qdisc *sch, struct htb_class *cl,
WARN_ON(!q);
dev_queue = htb_offload_get_queue(cl);
- old = htb_graft_helper(dev_queue, NULL);
- if (destroying)
- /* Before HTB is destroyed, the kernel grafts noop_qdisc to
- * all queues.
+ /* When destroying, caller qdisc_graft grafts the new qdisc and invokes
+ * qdisc_put for the qdisc being destroyed. htb_destroy_class_offload
+ * does not need to graft or qdisc_put the qdisc being destroyed.
+ */
+ if (!destroying) {
+ old = htb_graft_helper(dev_queue, NULL);
+ /* Last qdisc grafted should be the same as cl->leaf.q when
+ * calling htb_delete.
*/
- WARN_ON(!(old->flags & TCQ_F_BUILTIN));
- else
WARN_ON(old != q);
+ }
if (cl->parent) {
_bstats_update(&cl->parent->bstats_bias,
@@ -1577,10 +1587,12 @@ static int htb_destroy_class_offload(struct Qdisc *sch, struct htb_class *cl,
};
err = htb_offload(qdisc_dev(sch), &offload_opt);
- if (!err || destroying)
- qdisc_put(old);
- else
- htb_graft_helper(dev_queue, old);
+ if (!destroying) {
+ if (!err)
+ qdisc_put(old);
+ else
+ htb_graft_helper(dev_queue, old);
+ }
if (last_child)
return err;
diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index 570389f6cdd7db..c322a61eaeeac4 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -1700,6 +1700,7 @@ static void taprio_reset(struct Qdisc *sch)
int i;
hrtimer_cancel(&q->advance_timer);
+
if (q->qdiscs) {
for (i = 0; i < dev->num_tx_queues; i++)
if (q->qdiscs[i])
@@ -1720,6 +1721,7 @@ static void taprio_destroy(struct Qdisc *sch)
* happens in qdisc_create(), after taprio_init() has been called.
*/
hrtimer_cancel(&q->advance_timer);
+ qdisc_synchronize(sch);
taprio_disable_offload(dev, q, NULL);
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c
index 59e653b528b1fa..6b95d3ba8fe1ce 100644
--- a/net/sctp/bind_addr.c
+++ b/net/sctp/bind_addr.c
@@ -73,6 +73,12 @@ int sctp_bind_addr_copy(struct net *net, struct sctp_bind_addr *dest,
}
}
+ /* If somehow no addresses were found that can be used with this
+ * scope, it's an error.
+ */
+ if (list_empty(&dest->address_list))
+ error = -ENETUNREACH;
+
out:
if (error)
sctp_bind_addr_clean(dest);
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index ca1eba95c29348..2f66a200651742 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -196,9 +196,7 @@ void sctp_transport_reset_hb_timer(struct sctp_transport *transport)
/* When a data chunk is sent, reset the heartbeat interval. */
expires = jiffies + sctp_transport_timeout(transport);
- if ((time_before(transport->hb_timer.expires, expires) ||
- !timer_pending(&transport->hb_timer)) &&
- !mod_timer(&transport->hb_timer,
+ if (!mod_timer(&transport->hb_timer,
expires + get_random_u32_below(transport->rto)))
sctp_transport_hold(transport);
}
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 7bb247c51e2f66..2d7b1e03110ae8 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -302,7 +302,7 @@ __gss_find_upcall(struct rpc_pipe *pipe, kuid_t uid, const struct gss_auth *auth
list_for_each_entry(pos, &pipe->in_downcall, list) {
if (!uid_eq(pos->uid, uid))
continue;
- if (auth && pos->auth->service != auth->service)
+ if (pos->auth->service != auth->service)
continue;
refcount_inc(&pos->count);
return pos;
@@ -686,6 +686,21 @@ out:
return err;
}
+static struct gss_upcall_msg *
+gss_find_downcall(struct rpc_pipe *pipe, kuid_t uid)
+{
+ struct gss_upcall_msg *pos;
+ list_for_each_entry(pos, &pipe->in_downcall, list) {
+ if (!uid_eq(pos->uid, uid))
+ continue;
+ if (!rpc_msg_is_inflight(&pos->msg))
+ continue;
+ refcount_inc(&pos->count);
+ return pos;
+ }
+ return NULL;
+}
+
#define MSG_BUF_MAXSIZE 1024
static ssize_t
@@ -732,7 +747,7 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
err = -ENOENT;
/* Find a matching upcall */
spin_lock(&pipe->lock);
- gss_msg = __gss_find_upcall(pipe, uid, NULL);
+ gss_msg = gss_find_downcall(pipe, uid);
if (gss_msg == NULL) {
spin_unlock(&pipe->lock);
goto err_put_ctx;
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 148bb0a7fa5b4a..acb822b23af1b0 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -923,7 +923,7 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g
* rejecting the server-computed MIC in this somewhat rare case,
* do not use splice with the GSS integrity service.
*/
- __clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
+ clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
/* Did we already verify the signature on the original pass through? */
if (rqstp->rq_deferred)
@@ -990,7 +990,7 @@ unwrap_priv_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct gs
int pad, remaining_len, offset;
u32 rseqno;
- __clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
+ clear_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
priv_len = svc_getnl(&buf->head[0]);
if (rqstp->rq_deferred) {
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 85f0c3cfc87745..f06622814a958a 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -1243,10 +1243,10 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
goto err_short_len;
/* Will be turned off by GSS integrity and privacy services */
- __set_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
+ set_bit(RQ_SPLICE_OK, &rqstp->rq_flags);
/* Will be turned off only when NFSv4 Sessions are used */
- __set_bit(RQ_USEDEFERRAL, &rqstp->rq_flags);
- __clear_bit(RQ_DROPME, &rqstp->rq_flags);
+ set_bit(RQ_USEDEFERRAL, &rqstp->rq_flags);
+ clear_bit(RQ_DROPME, &rqstp->rq_flags);
svc_putu32(resv, rqstp->rq_xid);
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index 2106003645a78d..c2ce1253800807 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -1238,7 +1238,7 @@ static struct cache_deferred_req *svc_defer(struct cache_req *req)
trace_svc_defer(rqstp);
svc_xprt_get(rqstp->rq_xprt);
dr->xprt = rqstp->rq_xprt;
- __set_bit(RQ_DROPME, &rqstp->rq_flags);
+ set_bit(RQ_DROPME, &rqstp->rq_flags);
dr->handle.revisit = svc_revisit;
return &dr->handle;
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 015714398007ae..815baf308236a9 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -298,9 +298,9 @@ static void svc_sock_setbufsize(struct svc_sock *svsk, unsigned int nreqs)
static void svc_sock_secure_port(struct svc_rqst *rqstp)
{
if (svc_port_is_privileged(svc_addr(rqstp)))
- __set_bit(RQ_SECURE, &rqstp->rq_flags);
+ set_bit(RQ_SECURE, &rqstp->rq_flags);
else
- __clear_bit(RQ_SECURE, &rqstp->rq_flags);
+ clear_bit(RQ_SECURE, &rqstp->rq_flags);
}
/*
@@ -1008,9 +1008,9 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp)
rqstp->rq_xprt_ctxt = NULL;
rqstp->rq_prot = IPPROTO_TCP;
if (test_bit(XPT_LOCAL, &svsk->sk_xprt.xpt_flags))
- __set_bit(RQ_LOCAL, &rqstp->rq_flags);
+ set_bit(RQ_LOCAL, &rqstp->rq_flags);
else
- __clear_bit(RQ_LOCAL, &rqstp->rq_flags);
+ clear_bit(RQ_LOCAL, &rqstp->rq_flags);
p = (__be32 *)rqstp->rq_arg.head[0].iov_base;
calldir = p[1];
diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c
index 199fa012f18a87..94b20fb4713563 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_transport.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c
@@ -602,7 +602,7 @@ static int svc_rdma_has_wspace(struct svc_xprt *xprt)
static void svc_rdma_secure_port(struct svc_rqst *rqstp)
{
- __set_bit(RQ_SECURE, &rqstp->rq_flags);
+ set_bit(RQ_SECURE, &rqstp->rq_flags);
}
static void svc_rdma_kill_temp_xprt(struct svc_xprt *xprt)
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 49ddc484c4fe72..5e000fde806768 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -1179,8 +1179,9 @@ void tipc_node_check_dest(struct net *net, u32 addr,
bool addr_match = false;
bool sign_match = false;
bool link_up = false;
+ bool link_is_reset = false;
bool accept_addr = false;
- bool reset = true;
+ bool reset = false;
char *if_name;
unsigned long intv;
u16 session;
@@ -1200,14 +1201,14 @@ void tipc_node_check_dest(struct net *net, u32 addr,
/* Prepare to validate requesting node's signature and media address */
l = le->link;
link_up = l && tipc_link_is_up(l);
+ link_is_reset = l && tipc_link_is_reset(l);
addr_match = l && !memcmp(&le->maddr, maddr, sizeof(*maddr));
sign_match = (signature == n->signature);
/* These three flags give us eight permutations: */
if (sign_match && addr_match && link_up) {
- /* All is fine. Do nothing. */
- reset = false;
+ /* All is fine. Ignore requests. */
/* Peer node is not a container/local namespace */
if (!n->peer_hash_mix)
n->peer_hash_mix = hash_mixes;
@@ -1232,6 +1233,7 @@ void tipc_node_check_dest(struct net *net, u32 addr,
*/
accept_addr = true;
*respond = true;
+ reset = true;
} else if (!sign_match && addr_match && link_up) {
/* Peer node rebooted. Two possibilities:
* - Delayed re-discovery; this link endpoint has already
@@ -1263,6 +1265,7 @@ void tipc_node_check_dest(struct net *net, u32 addr,
n->signature = signature;
accept_addr = true;
*respond = true;
+ reset = true;
}
if (!accept_addr)
@@ -1291,6 +1294,7 @@ void tipc_node_check_dest(struct net *net, u32 addr,
tipc_link_fsm_evt(l, LINK_RESET_EVT);
if (n->state == NODE_FAILINGOVER)
tipc_link_fsm_evt(l, LINK_FAILOVER_BEGIN_EVT);
+ link_is_reset = tipc_link_is_reset(l);
le->link = l;
n->link_cnt++;
tipc_node_calculate_timer(n, l);
@@ -1303,7 +1307,7 @@ void tipc_node_check_dest(struct net *net, u32 addr,
memcpy(&le->maddr, maddr, sizeof(*maddr));
exit:
tipc_node_write_unlock(n);
- if (reset && l && !tipc_link_is_reset(l))
+ if (reset && !link_is_reset)
tipc_node_link_down(n, b->identity, false);
tipc_node_put(n);
}
diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c
index 9ed9786341259e..a83d2b4275fa69 100644
--- a/net/tls/tls_sw.c
+++ b/net/tls/tls_sw.c
@@ -2427,7 +2427,7 @@ static bool tls_is_tx_ready(struct tls_sw_context_tx *ctx)
{
struct tls_rec *rec;
- rec = list_first_entry(&ctx->tx_list, struct tls_rec, list);
+ rec = list_first_entry_or_null(&ctx->tx_list, struct tls_rec, list);
if (!rec)
return false;
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index 3b55502b296570..5c7ad301d742e8 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -482,6 +482,12 @@ static int x25_listen(struct socket *sock, int backlog)
int rc = -EOPNOTSUPP;
lock_sock(sk);
+ if (sock->state != SS_UNCONNECTED) {
+ rc = -EINVAL;
+ release_sock(sk);
+ return rc;
+ }
+
if (sk->sk_state != TCP_LISTEN) {
memset(&x25_sk(sk)->dest_addr, 0, X25_ADDR_LEN);
sk->sk_max_ack_backlog = backlog;
diff --git a/net/xfrm/xfrm_compat.c b/net/xfrm/xfrm_compat.c
index a0f62fa02e06e0..8cbf45a8bcdc24 100644
--- a/net/xfrm/xfrm_compat.c
+++ b/net/xfrm/xfrm_compat.c
@@ -5,6 +5,7 @@
* Based on code and translator idea by: Florian Westphal <fw@strlen.de>
*/
#include <linux/compat.h>
+#include <linux/nospec.h>
#include <linux/xfrm.h>
#include <net/xfrm.h>
@@ -302,7 +303,7 @@ static int xfrm_xlate64(struct sk_buff *dst, const struct nlmsghdr *nlh_src)
nla_for_each_attr(nla, attrs, len, remaining) {
int err;
- switch (type) {
+ switch (nlh_src->nlmsg_type) {
case XFRM_MSG_NEWSPDINFO:
err = xfrm_nla_cpy(dst, nla, nla_len(nla));
break;
@@ -437,6 +438,7 @@ static int xfrm_xlate32_attr(void *dst, const struct nlattr *nla,
NL_SET_ERR_MSG(extack, "Bad attribute");
return -EOPNOTSUPP;
}
+ type = array_index_nospec(type, XFRMA_MAX + 1);
if (nla_len(nla) < compat_policy[type].len) {
NL_SET_ERR_MSG(extack, "Attribute bad length");
return -EOPNOTSUPP;
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index c06e54a105405b..436d29640ac2cf 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -279,8 +279,7 @@ static int xfrm6_remove_tunnel_encap(struct xfrm_state *x, struct sk_buff *skb)
goto out;
if (x->props.flags & XFRM_STATE_DECAP_DSCP)
- ipv6_copy_dscp(ipv6_get_dsfield(ipv6_hdr(skb)),
- ipipv6_hdr(skb));
+ ipv6_copy_dscp(XFRM_MODE_SKB_CB(skb)->tos, ipipv6_hdr(skb));
if (!(x->props.flags & XFRM_STATE_NOECN))
ipip6_ecn_decapsulate(skb);
diff --git a/net/xfrm/xfrm_interface_core.c b/net/xfrm/xfrm_interface_core.c
index 1f99dc46902719..35279c220bd786 100644
--- a/net/xfrm/xfrm_interface_core.c
+++ b/net/xfrm/xfrm_interface_core.c
@@ -310,6 +310,52 @@ static void xfrmi_scrub_packet(struct sk_buff *skb, bool xnet)
skb->mark = 0;
}
+static int xfrmi_input(struct sk_buff *skb, int nexthdr, __be32 spi,
+ int encap_type, unsigned short family)
+{
+ struct sec_path *sp;
+
+ sp = skb_sec_path(skb);
+ if (sp && (sp->len || sp->olen) &&
+ !xfrm_policy_check(NULL, XFRM_POLICY_IN, skb, family))
+ goto discard;
+
+ XFRM_SPI_SKB_CB(skb)->family = family;
+ if (family == AF_INET) {
+ XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct iphdr, daddr);
+ XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip4 = NULL;
+ } else {
+ XFRM_SPI_SKB_CB(skb)->daddroff = offsetof(struct ipv6hdr, daddr);
+ XFRM_TUNNEL_SKB_CB(skb)->tunnel.ip6 = NULL;
+ }
+
+ return xfrm_input(skb, nexthdr, spi, encap_type);
+discard:
+ kfree_skb(skb);
+ return 0;
+}
+
+static int xfrmi4_rcv(struct sk_buff *skb)
+{
+ return xfrmi_input(skb, ip_hdr(skb)->protocol, 0, 0, AF_INET);
+}
+
+static int xfrmi6_rcv(struct sk_buff *skb)
+{
+ return xfrmi_input(skb, skb_network_header(skb)[IP6CB(skb)->nhoff],
+ 0, 0, AF_INET6);
+}
+
+static int xfrmi4_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
+{
+ return xfrmi_input(skb, nexthdr, spi, encap_type, AF_INET);
+}
+
+static int xfrmi6_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
+{
+ return xfrmi_input(skb, nexthdr, spi, encap_type, AF_INET6);
+}
+
static int xfrmi_rcv_cb(struct sk_buff *skb, int err)
{
const struct xfrm_mode *inner_mode;
@@ -945,8 +991,8 @@ static struct pernet_operations xfrmi_net_ops = {
};
static struct xfrm6_protocol xfrmi_esp6_protocol __read_mostly = {
- .handler = xfrm6_rcv,
- .input_handler = xfrm_input,
+ .handler = xfrmi6_rcv,
+ .input_handler = xfrmi6_input,
.cb_handler = xfrmi_rcv_cb,
.err_handler = xfrmi6_err,
.priority = 10,
@@ -996,8 +1042,8 @@ static struct xfrm6_tunnel xfrmi_ip6ip_handler __read_mostly = {
#endif
static struct xfrm4_protocol xfrmi_esp4_protocol __read_mostly = {
- .handler = xfrm4_rcv,
- .input_handler = xfrm_input,
+ .handler = xfrmi4_rcv,
+ .input_handler = xfrmi4_input,
.cb_handler = xfrmi_rcv_cb,
.err_handler = xfrmi4_err,
.priority = 10,
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index e9eb82c5457d03..5c61ec04b839ba 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -336,7 +336,7 @@ static void xfrm_policy_timer(struct timer_list *t)
}
if (xp->lft.hard_use_expires_seconds) {
time64_t tmo = xp->lft.hard_use_expires_seconds +
- (xp->curlft.use_time ? : xp->curlft.add_time) - now;
+ (READ_ONCE(xp->curlft.use_time) ? : xp->curlft.add_time) - now;
if (tmo <= 0)
goto expired;
if (tmo < next)
@@ -354,7 +354,7 @@ static void xfrm_policy_timer(struct timer_list *t)
}
if (xp->lft.soft_use_expires_seconds) {
time64_t tmo = xp->lft.soft_use_expires_seconds +
- (xp->curlft.use_time ? : xp->curlft.add_time) - now;
+ (READ_ONCE(xp->curlft.use_time) ? : xp->curlft.add_time) - now;
if (tmo <= 0) {
warn = 1;
tmo = XFRM_KM_TIMEOUT;
@@ -3661,7 +3661,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
return 1;
}
- pol->curlft.use_time = ktime_get_real_seconds();
+ /* This lockless write can happen from different cpus. */
+ WRITE_ONCE(pol->curlft.use_time, ktime_get_real_seconds());
pols[0] = pol;
npols++;
@@ -3676,7 +3677,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
xfrm_pol_put(pols[0]);
return 0;
}
- pols[1]->curlft.use_time = ktime_get_real_seconds();
+ /* This write can happen from different cpus. */
+ WRITE_ONCE(pols[1]->curlft.use_time,
+ ktime_get_real_seconds());
npols++;
}
}
@@ -3742,6 +3745,9 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
goto reject;
}
+ if (if_id)
+ secpath_reset(skb);
+
xfrm_pols_put(pols, npols);
return 1;
}
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 89c731f4f0c728..00afe831c71c49 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -577,7 +577,7 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
if (x->km.state == XFRM_STATE_EXPIRED)
goto expired;
if (x->lft.hard_add_expires_seconds) {
- long tmo = x->lft.hard_add_expires_seconds +
+ time64_t tmo = x->lft.hard_add_expires_seconds +
x->curlft.add_time - now;
if (tmo <= 0) {
if (x->xflags & XFRM_SOFT_EXPIRE) {
@@ -594,8 +594,8 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
next = tmo;
}
if (x->lft.hard_use_expires_seconds) {
- long tmo = x->lft.hard_use_expires_seconds +
- (x->curlft.use_time ? : now) - now;
+ time64_t tmo = x->lft.hard_use_expires_seconds +
+ (READ_ONCE(x->curlft.use_time) ? : now) - now;
if (tmo <= 0)
goto expired;
if (tmo < next)
@@ -604,7 +604,7 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
if (x->km.dying)
goto resched;
if (x->lft.soft_add_expires_seconds) {
- long tmo = x->lft.soft_add_expires_seconds +
+ time64_t tmo = x->lft.soft_add_expires_seconds +
x->curlft.add_time - now;
if (tmo <= 0) {
warn = 1;
@@ -616,8 +616,8 @@ static enum hrtimer_restart xfrm_timer_handler(struct hrtimer *me)
}
}
if (x->lft.soft_use_expires_seconds) {
- long tmo = x->lft.soft_use_expires_seconds +
- (x->curlft.use_time ? : now) - now;
+ time64_t tmo = x->lft.soft_use_expires_seconds +
+ (READ_ONCE(x->curlft.use_time) ? : now) - now;
if (tmo <= 0)
warn = 1;
else if (tmo < next)
@@ -1906,7 +1906,7 @@ out:
hrtimer_start(&x1->mtimer, ktime_set(1, 0),
HRTIMER_MODE_REL_SOFT);
- if (x1->curlft.use_time)
+ if (READ_ONCE(x1->curlft.use_time))
xfrm_state_check_expire(x1);
if (x->props.smark.m || x->props.smark.v || x->if_id) {
@@ -1940,8 +1940,8 @@ int xfrm_state_check_expire(struct xfrm_state *x)
{
xfrm_dev_state_update_curlft(x);
- if (!x->curlft.use_time)
- x->curlft.use_time = ktime_get_real_seconds();
+ if (!READ_ONCE(x->curlft.use_time))
+ WRITE_ONCE(x->curlft.use_time, ktime_get_real_seconds());
if (x->curlft.bytes >= x->lft.hard_byte_limit ||
x->curlft.packets >= x->lft.hard_packet_limit) {
diff --git a/rust/kernel/print.rs b/rust/kernel/print.rs
index 29bf9c2e8aee61..30103325696d80 100644
--- a/rust/kernel/print.rs
+++ b/rust/kernel/print.rs
@@ -142,17 +142,24 @@ pub fn call_printk_cont(args: fmt::Arguments<'_>) {
macro_rules! print_macro (
// The non-continuation cases (most of them, e.g. `INFO`).
($format_string:path, false, $($arg:tt)+) => (
- // SAFETY: This hidden macro should only be called by the documented
- // printing macros which ensure the format string is one of the fixed
- // ones. All `__LOG_PREFIX`s are null-terminated as they are generated
- // by the `module!` proc macro or fixed values defined in a kernel
- // crate.
- unsafe {
- $crate::print::call_printk(
- &$format_string,
- crate::__LOG_PREFIX,
- format_args!($($arg)+),
- );
+ // To remain sound, `arg`s must be expanded outside the `unsafe` block.
+ // Typically one would use a `let` binding for that; however, `format_args!`
+ // takes borrows on the arguments, but does not extend the scope of temporaries.
+ // Therefore, a `match` expression is used to keep them around, since
+ // the scrutinee is kept until the end of the `match`.
+ match format_args!($($arg)+) {
+ // SAFETY: This hidden macro should only be called by the documented
+ // printing macros which ensure the format string is one of the fixed
+ // ones. All `__LOG_PREFIX`s are null-terminated as they are generated
+ // by the `module!` proc macro or fixed values defined in a kernel
+ // crate.
+ args => unsafe {
+ $crate::print::call_printk(
+ &$format_string,
+ crate::__LOG_PREFIX,
+ args,
+ );
+ }
}
);
diff --git a/samples/ftrace/ftrace-direct-multi-modify.c b/samples/ftrace/ftrace-direct-multi-modify.c
index d52370cad0b6e5..a825dbd2c9cfd2 100644
--- a/samples/ftrace/ftrace-direct-multi-modify.c
+++ b/samples/ftrace/ftrace-direct-multi-modify.c
@@ -152,6 +152,7 @@ static void __exit ftrace_direct_multi_exit(void)
{
kthread_stop(simple_tsk);
unregister_ftrace_direct_multi(&direct, my_tramp);
+ ftrace_free_filter(&direct);
}
module_init(ftrace_direct_multi_init);
diff --git a/samples/ftrace/ftrace-direct-multi.c b/samples/ftrace/ftrace-direct-multi.c
index ec1088922517df..d955a26506053f 100644
--- a/samples/ftrace/ftrace-direct-multi.c
+++ b/samples/ftrace/ftrace-direct-multi.c
@@ -79,6 +79,7 @@ static int __init ftrace_direct_multi_init(void)
static void __exit ftrace_direct_multi_exit(void)
{
unregister_ftrace_direct_multi(&direct, (unsigned long) my_tramp);
+ ftrace_free_filter(&direct);
}
module_init(ftrace_direct_multi_init);
diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst
index 836391e5d20977..4815a8e32227d3 100644
--- a/scripts/Makefile.modinst
+++ b/scripts/Makefile.modinst
@@ -66,9 +66,13 @@ endif
# Don't stop modules_install even if we can't sign external modules.
#
ifeq ($(CONFIG_MODULE_SIG_ALL),y)
+ifeq ($(filter pkcs11:%, $(CONFIG_MODULE_SIG_KEY)),)
sig-key := $(if $(wildcard $(CONFIG_MODULE_SIG_KEY)),,$(srctree)/)$(CONFIG_MODULE_SIG_KEY)
+else
+sig-key := $(CONFIG_MODULE_SIG_KEY)
+endif
quiet_cmd_sign = SIGN $@
- cmd_sign = scripts/sign-file $(CONFIG_MODULE_SIG_HASH) $(sig-key) certs/signing_key.x509 $@ \
+ cmd_sign = scripts/sign-file $(CONFIG_MODULE_SIG_HASH) "$(sig-key)" certs/signing_key.x509 $@ \
$(if $(KBUILD_EXTMOD),|| true)
else
quiet_cmd_sign :=
diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost
index 5eb5e8280379ad..43343e13c54255 100644
--- a/scripts/Makefile.modpost
+++ b/scripts/Makefile.modpost
@@ -44,6 +44,7 @@ modpost-args = \
$(if $(CONFIG_MODVERSIONS),-m) \
$(if $(CONFIG_MODULE_SRCVERSION_ALL),-a) \
$(if $(CONFIG_SECTION_MISMATCH_WARN_ONLY),,-E) \
+ $(if $(KBUILD_MODPOST_WARN),-w) \
$(if $(KBUILD_NSDEPS),-d $(MODULES_NSDEPS)) \
$(if $(CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS)$(KBUILD_NSDEPS),-N) \
-o $@
@@ -55,6 +56,13 @@ ifneq ($(findstring i,$(filter-out --%,$(MAKEFLAGS))),)
modpost-args += -n
endif
+# Read out modules.order to pass in modpost.
+# Otherwise, allmodconfig would fail with "Argument list too long".
+ifdef KBUILD_MODULES
+modpost-args += -T $(MODORDER)
+modpost-deps += $(MODORDER)
+endif
+
ifeq ($(KBUILD_EXTMOD),)
# Generate the list of in-tree objects in vmlinux
@@ -113,17 +121,10 @@ modpost-args += -e $(addprefix -i , $(KBUILD_EXTRA_SYMBOLS))
endif # ($(KBUILD_EXTMOD),)
-ifneq ($(KBUILD_MODPOST_WARN)$(missing-input),)
+ifneq ($(missing-input),)
modpost-args += -w
endif
-ifdef KBUILD_MODULES
-modpost-args += -T $(MODORDER)
-modpost-deps += $(MODORDER)
-endif
-
-# Read out modules.order to pass in modpost.
-# Otherwise, allmodconfig would fail with "Argument list too long".
quiet_cmd_modpost = MODPOST $@
cmd_modpost = \
$(if $(missing-input), \
diff --git a/scripts/Makefile.package b/scripts/Makefile.package
index 539e9f765d64db..525a2820976fd0 100644
--- a/scripts/Makefile.package
+++ b/scripts/Makefile.package
@@ -158,6 +158,7 @@ $(perf-tar-pkgs):
PHONY += help
help:
@echo ' rpm-pkg - Build both source and binary RPM kernel packages'
+ @echo ' srcrpm-pkg - Build only the source kernel RPM package'
@echo ' binrpm-pkg - Build only the binary kernel RPM package'
@echo ' deb-pkg - Build both source and binary deb kernel packages'
@echo ' bindeb-pkg - Build only the binary kernel deb package'
diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
index 49946cb968440c..10176dec97eac2 100644
--- a/scripts/Makefile.vmlinux
+++ b/scripts/Makefile.vmlinux
@@ -18,6 +18,7 @@ quiet_cmd_cc_o_c = CC $@
$(call if_changed_dep,cc_o_c)
ifdef CONFIG_MODULES
+KASAN_SANITIZE_.vmlinux.export.o := n
targets += .vmlinux.export.o
vmlinux: .vmlinux.export.o
endif
diff --git a/scripts/atomic/atomics.tbl b/scripts/atomic/atomics.tbl
index fbee2f6190d9e2..fbee2f6190d9e2 100755..100644
--- a/scripts/atomic/atomics.tbl
+++ b/scripts/atomic/atomics.tbl
diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c
index 2328f9a641dadf..f932aeaba71a2e 100644
--- a/scripts/basic/fixdep.c
+++ b/scripts/basic/fixdep.c
@@ -94,7 +94,6 @@
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
-#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h
index 9a1895747b1536..84c730da36dd3f 100644
--- a/scripts/gcc-plugins/gcc-common.h
+++ b/scripts/gcc-plugins/gcc-common.h
@@ -71,7 +71,9 @@
#include "varasm.h"
#include "stor-layout.h"
#include "internal-fn.h"
+#include "gimple.h"
#include "gimple-expr.h"
+#include "gimple-iterator.h"
#include "gimple-fold.h"
#include "context.h"
#include "tree-ssa-alias.h"
@@ -85,10 +87,8 @@
#include "tree-eh.h"
#include "stmt.h"
#include "gimplify.h"
-#include "gimple.h"
#include "tree-phinodes.h"
#include "tree-cfg.h"
-#include "gimple-iterator.h"
#include "gimple-ssa.h"
#include "ssa-iterators.h"
diff --git a/scripts/jobserver-exec b/scripts/jobserver-exec
index 4192855f5b8b5c..7eca035472d303 100755
--- a/scripts/jobserver-exec
+++ b/scripts/jobserver-exec
@@ -26,11 +26,20 @@ try:
# If the MAKEFLAGS variable contains multiple instances of the
# --jobserver-auth= option, the last one is relevant.
fds = opts[-1].split("=", 1)[1]
- reader, writer = [int(x) for x in fds.split(",", 1)]
- # Open a private copy of reader to avoid setting nonblocking
- # on an unexpecting process with the same reader fd.
- reader = os.open("/proc/self/fd/%d" % (reader),
- os.O_RDONLY | os.O_NONBLOCK)
+
+ # Starting with GNU Make 4.4, named pipes are used for reader and writer.
+ # Example argument: --jobserver-auth=fifo:/tmp/GMfifo8134
+ _, _, path = fds.partition('fifo:')
+
+ if path:
+ reader = os.open(path, os.O_RDONLY | os.O_NONBLOCK)
+ writer = os.open(path, os.O_WRONLY)
+ else:
+ reader, writer = [int(x) for x in fds.split(",", 1)]
+ # Open a private copy of reader to avoid setting nonblocking
+ # on an unexpecting process with the same reader fd.
+ reader = os.open("/proc/self/fd/%d" % (reader),
+ os.O_RDONLY | os.O_NONBLOCK)
# Read out as many jobserver slots as possible.
while True:
diff --git a/scripts/kconfig/.gitignore b/scripts/kconfig/.gitignore
index c8a3f9cd52f028..0b2ff775b2e33a 100644
--- a/scripts/kconfig/.gitignore
+++ b/scripts/kconfig/.gitignore
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
/conf
/[gmnq]conf
+/[gmnq]conf-bin
/[gmnq]conf-cflags
/[gmnq]conf-libs
-/qconf-bin
/qconf-moc.cc
diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index 0b1d15efaeb0c2..af1c96198f4919 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -209,7 +209,7 @@ $(obj)/gconf: | $(obj)/gconf-libs
$(obj)/gconf.o: | $(obj)/gconf-cflags
# check if necessary packages are available, and configure build flags
-cmd_conf_cfg = $< $(addprefix $(obj)/$*conf-, cflags libs bin)
+cmd_conf_cfg = $< $(addprefix $(obj)/$*conf-, cflags libs bin); touch $(obj)/$*conf-bin
$(obj)/%conf-cflags $(obj)/%conf-libs $(obj)/%conf-bin: $(src)/%conf-cfg.sh
$(call cmd,conf_cfg)
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c
index 9c549683c62751..e67e0db50b2e25 100644
--- a/scripts/kconfig/mconf.c
+++ b/scripts/kconfig/mconf.c
@@ -161,6 +161,12 @@ static const char mconf_readme[] =
"(especially with a larger number of unrolled categories) than the\n"
"default mode.\n"
"\n"
+
+"Search\n"
+"-------\n"
+"Pressing the forward-slash (/) anywhere brings up a search dialog box.\n"
+"\n"
+
"Different color themes available\n"
"--------------------------------\n"
"It is possible to select different color themes using the variable\n"
diff --git a/scripts/package/mkspec b/scripts/package/mkspec
index dda00a948a01df..094e52c979a8cd 100755
--- a/scripts/package/mkspec
+++ b/scripts/package/mkspec
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Output a simple RPM spec file.
-# This version assumes a minimum of RPM 4.0.3.
+# This version assumes a minimum of RPM 4.13
#
# The only gothic bit here is redefining install_post to avoid
# stripping the symbols from files in the kernel which we want
@@ -51,7 +51,8 @@ sed -e '/^DEL/d' -e 's/^\t*//' <<EOF
URL: https://www.kernel.org
$S Source: kernel-$__KERNELRELEASE.tar.gz
Provides: $PROVIDES
-$S BuildRequires: bc binutils bison dwarves elfutils-libelf-devel flex
+$S BuildRequires: bc binutils bison dwarves
+$S BuildRequires: (elfutils-libelf-devel or libelf-devel) flex
$S BuildRequires: gcc make openssl openssl-devel perl python3 rsync
# $UTS_MACHINE as a fallback of _arch in case
diff --git a/scripts/tracing/ftrace-bisect.sh b/scripts/tracing/ftrace-bisect.sh
index 926701162bc832..bb4f59262bbe9e 100755
--- a/scripts/tracing/ftrace-bisect.sh
+++ b/scripts/tracing/ftrace-bisect.sh
@@ -12,7 +12,7 @@
# (note, if this is a problem with function_graph tracing, then simply
# replace "function" with "function_graph" in the following steps).
#
-# # cd /sys/kernel/debug/tracing
+# # cd /sys/kernel/tracing
# # echo schedule > set_ftrace_filter
# # echo function > current_tracer
#
@@ -20,22 +20,40 @@
#
# # echo nop > current_tracer
#
-# # cat available_filter_functions > ~/full-file
+# Starting with v5.1 this can be done with numbers, making it much faster:
+#
+# The old (slow) way, for kernels before v5.1.
+#
+# [old-way] # cat available_filter_functions > ~/full-file
+#
+# [old-way] *** Note *** this process will take several minutes to update the
+# [old-way] filters. Setting multiple functions is an O(n^2) operation, and we
+# [old-way] are dealing with thousands of functions. So go have coffee, talk
+# [old-way] with your coworkers, read facebook. And eventually, this operation
+# [old-way] will end.
+#
+# The new way (using numbers) is an O(n) operation, and usually takes less than a second.
+#
+# seq `wc -l available_filter_functions | cut -d' ' -f1` > ~/full-file
+#
+# This will create a sequence of numbers that match the functions in
+# available_filter_functions, and when echoing in a number into the
+# set_ftrace_filter file, it will enable the corresponding function in
+# O(1) time. Making enabling all functions O(n) where n is the number of
+# functions to enable.
+#
+# For either the new or old way, the rest of the operations remain the same.
+#
# # ftrace-bisect ~/full-file ~/test-file ~/non-test-file
# # cat ~/test-file > set_ftrace_filter
#
-# *** Note *** this will take several minutes. Setting multiple functions is
-# an O(n^2) operation, and we are dealing with thousands of functions. So go
-# have coffee, talk with your coworkers, read facebook. And eventually, this
-# operation will end.
-#
# # echo function > current_tracer
#
# If it crashes, we know that ~/test-file has a bad function.
#
# Reboot back to test kernel.
#
-# # cd /sys/kernel/debug/tracing
+# # cd /sys/kernel/tracing
# # mv ~/test-file ~/full-file
#
# If it didn't crash.
diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig
index b9f867100a9ff5..fad75be5f381d6 100644
--- a/security/tomoyo/Kconfig
+++ b/security/tomoyo/Kconfig
@@ -6,13 +6,11 @@ config SECURITY_TOMOYO
select SECURITYFS
select SECURITY_PATH
select SECURITY_NETWORK
- select SRCU
- select BUILD_BIN2C
default n
help
This selects TOMOYO Linux, pathname-based access control.
Required userspace tools and further information may be
- found at <http://tomoyo.sourceforge.jp/>.
+ found at <https://tomoyo.osdn.jp/>.
If you are unsure how to answer this question, answer N.
config SECURITY_TOMOYO_MAX_ACCEPT_ENTRY
diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile
index cca5a3012fee2e..884ff155edc392 100644
--- a/security/tomoyo/Makefile
+++ b/security/tomoyo/Makefile
@@ -2,15 +2,18 @@
obj-y = audit.o common.o condition.o domain.o environ.o file.o gc.o group.o load_policy.o memory.o mount.o network.o realpath.o securityfs_if.o tomoyo.o util.o
targets += builtin-policy.h
-define do_policy
-echo "static char tomoyo_builtin_$(1)[] __initdata ="; \
-$(objtree)/scripts/bin2c <$(firstword $(wildcard $(obj)/policy/$(1).conf $(srctree)/$(src)/policy/$(1).conf.default) /dev/null); \
-echo ";"
-endef
-quiet_cmd_policy = POLICY $@
- cmd_policy = ($(call do_policy,profile); $(call do_policy,exception_policy); $(call do_policy,domain_policy); $(call do_policy,manager); $(call do_policy,stat)) >$@
-$(obj)/builtin-policy.h: $(wildcard $(obj)/policy/*.conf $(src)/policy/*.conf.default) FORCE
+quiet_cmd_policy = POLICY $@
+ cmd_policy = { \
+ $(foreach x, profile exception_policy domain_policy manager stat, \
+ printf 'static char tomoyo_builtin_$x[] __initdata =\n'; \
+ sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/\t"\1\\n"/' -- $(firstword $(filter %/$x.conf %/$x.conf.default, $^) /dev/null); \
+ printf '\t"";\n';) \
+ } > $@
+
+$(obj)/builtin-policy.h: $(wildcard $(obj)/policy/*.conf $(srctree)/$(src)/policy/*.conf.default) FORCE
$(call if_changed,policy)
+ifndef CONFIG_SECURITY_TOMOYO_INSECURE_BUILTIN_SETTING
$(obj)/common.o: $(obj)/builtin-policy.h
+endif
diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c
index 045330883a9633..6067c04ce4c0a8 100644
--- a/sound/ac97/bus.c
+++ b/sound/ac97/bus.c
@@ -524,10 +524,9 @@ static void ac97_bus_remove(struct device *dev)
if (ret < 0)
return;
- ret = adrv->remove(adev);
+ adrv->remove(adev);
pm_runtime_put_noidle(dev);
- if (ret == 0)
- ac97_put_disable_clk(adev);
+ ac97_put_disable_clk(adev);
pm_runtime_disable(dev);
}
diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c
index ec4ef18555bc90..850dc8c53e9b55 100644
--- a/sound/aoa/fabrics/layout.c
+++ b/sound/aoa/fabrics/layout.c
@@ -1094,7 +1094,7 @@ static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
return -ENODEV;
}
-static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
+static void aoa_fabric_layout_remove(struct soundbus_dev *sdev)
{
struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
int i;
@@ -1123,7 +1123,6 @@ static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
kfree(ldev);
sdev->pcmid = -1;
sdev->pcmname = NULL;
- return 0;
}
#ifdef CONFIG_PM_SLEEP
diff --git a/sound/aoa/soundbus/soundbus.h b/sound/aoa/soundbus/soundbus.h
index 3a99c1f1a3cab0..db40f9d042b4ad 100644
--- a/sound/aoa/soundbus/soundbus.h
+++ b/sound/aoa/soundbus/soundbus.h
@@ -185,7 +185,7 @@ struct soundbus_driver {
/* we don't implement any matching at all */
int (*probe)(struct soundbus_dev* dev);
- int (*remove)(struct soundbus_dev* dev);
+ void (*remove)(struct soundbus_dev *dev);
int (*shutdown)(struct soundbus_dev* dev);
diff --git a/sound/core/init.c b/sound/core/init.c
index 5377f94eb2111f..df0c22480375a5 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -489,17 +489,17 @@ static const struct file_operations snd_shutdown_f_ops =
* Note: The current implementation replaces all active file->f_op with special
* dummy file operations (they do nothing except release).
*/
-int snd_card_disconnect(struct snd_card *card)
+void snd_card_disconnect(struct snd_card *card)
{
struct snd_monitor_file *mfile;
if (!card)
- return -EINVAL;
+ return;
spin_lock(&card->files_lock);
if (card->shutdown) {
spin_unlock(&card->files_lock);
- return 0;
+ return;
}
card->shutdown = 1;
@@ -548,7 +548,6 @@ int snd_card_disconnect(struct snd_card *card)
wake_up(&card->power_sleep);
snd_power_sync_ref(card);
#endif
- return 0;
}
EXPORT_SYMBOL(snd_card_disconnect);
@@ -563,15 +562,7 @@ EXPORT_SYMBOL(snd_card_disconnect);
*/
void snd_card_disconnect_sync(struct snd_card *card)
{
- int err;
-
- err = snd_card_disconnect(card);
- if (err < 0) {
- dev_err(card->dev,
- "snd_card_disconnect error (%d), skipping sync\n",
- err);
- return;
- }
+ snd_card_disconnect(card);
spin_lock_irq(&card->files_lock);
wait_event_lock_irq(card->remove_sleep,
@@ -617,13 +608,14 @@ static int snd_card_do_free(struct snd_card *card)
*
* Return: zero if successful, or a negative error code
*/
-int snd_card_free_when_closed(struct snd_card *card)
+void snd_card_free_when_closed(struct snd_card *card)
{
- int ret = snd_card_disconnect(card);
- if (ret)
- return ret;
+ if (!card)
+ return;
+
+ snd_card_disconnect(card);
put_device(&card->card_dev);
- return 0;
+ return;
}
EXPORT_SYMBOL(snd_card_free_when_closed);
@@ -640,10 +632,9 @@ EXPORT_SYMBOL(snd_card_free_when_closed);
* Return: Zero. Frees all associated devices and frees the control
* interface associated to given soundcard.
*/
-int snd_card_free(struct snd_card *card)
+void snd_card_free(struct snd_card *card)
{
DECLARE_COMPLETION_ONSTACK(released);
- int ret;
/* The call of snd_card_free() is allowed from various code paths;
* a manual call from the driver and the call via devres_free, and
@@ -652,16 +643,13 @@ int snd_card_free(struct snd_card *card)
* the check here at the beginning.
*/
if (card->releasing)
- return 0;
+ return;
card->release_completion = &released;
- ret = snd_card_free_when_closed(card);
- if (ret)
- return ret;
+ snd_card_free_when_closed(card);
+
/* wait, until all devices are ready for the free operation */
wait_for_completion(&released);
-
- return 0;
}
EXPORT_SYMBOL(snd_card_free);
diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c
index d9c700f652bbb8..3660c312bf334f 100644
--- a/sound/firewire/amdtp-am824.c
+++ b/sound/firewire/amdtp-am824.c
@@ -36,8 +36,6 @@ struct amdtp_am824 {
u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM];
u8 midi_position;
-
- unsigned int frame_multiplier;
};
/**
@@ -59,8 +57,8 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
{
struct amdtp_am824 *p = s->protocol;
unsigned int midi_channels;
- unsigned int i;
- int err;
+ unsigned int pcm_frame_multiplier;
+ int i, err;
if (amdtp_stream_running(s))
return -EINVAL;
@@ -77,8 +75,18 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI))
return -EINVAL;
- err = amdtp_stream_set_parameters(s, rate,
- pcm_channels + midi_channels);
+ /*
+ * In IEC 61883-6, one data block represents one event. In ALSA, one
+ * event equals to one PCM frame. But Dice has a quirk at higher
+ * sampling rate to transfer two PCM frames in one data block.
+ */
+ if (double_pcm_frames)
+ pcm_frame_multiplier = 2;
+ else
+ pcm_frame_multiplier = 1;
+
+ err = amdtp_stream_set_parameters(s, rate, pcm_channels + midi_channels,
+ pcm_frame_multiplier);
if (err < 0)
return err;
@@ -88,16 +96,6 @@ int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
p->pcm_channels = pcm_channels;
p->midi_ports = midi_ports;
- /*
- * In IEC 61883-6, one data block represents one event. In ALSA, one
- * event equals to one PCM frame. But Dice has a quirk at higher
- * sampling rate to transfer two PCM frames in one data block.
- */
- if (double_pcm_frames)
- p->frame_multiplier = 2;
- else
- p->frame_multiplier = 1;
-
/* init the position map for PCM and MIDI channels */
for (i = 0; i < pcm_channels; i++)
p->pcm_positions[i] = i;
@@ -346,23 +344,20 @@ static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
}
}
-static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
- const struct pkt_desc *descs,
- unsigned int packets,
- struct snd_pcm_substream *pcm)
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
{
struct amdtp_am824 *p = s->protocol;
unsigned int pcm_frames = 0;
int i;
- for (i = 0; i < packets; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
if (pcm) {
write_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
- pcm_frames += data_blocks * p->frame_multiplier;
+ pcm_frames += data_blocks * s->pcm_frame_multiplier;
} else {
write_pcm_silence(s, buf, data_blocks);
}
@@ -371,37 +366,34 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
write_midi_messages(s, buf, data_blocks,
desc->data_block_counter);
}
- }
- return pcm_frames;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
}
-static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
- const struct pkt_desc *descs,
- unsigned int packets,
- struct snd_pcm_substream *pcm)
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
{
struct amdtp_am824 *p = s->protocol;
unsigned int pcm_frames = 0;
int i;
- for (i = 0; i < packets; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
if (pcm) {
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
- pcm_frames += data_blocks * p->frame_multiplier;
+ pcm_frames += data_blocks * s->pcm_frame_multiplier;
}
if (p->midi_ports) {
read_midi_messages(s, buf, data_blocks,
desc->data_block_counter);
}
- }
- return pcm_frames;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
}
/**
diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h
index 5fd2aeccdfc2ff..208f97cf8de60a 100644
--- a/sound/firewire/amdtp-stream-trace.h
+++ b/sound/firewire/amdtp-stream-trace.h
@@ -14,9 +14,10 @@
#include <linux/tracepoint.h>
TRACE_EVENT(amdtp_packet,
- TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int data_block_counter, unsigned int packet_index, unsigned int index),
- TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, data_block_counter, packet_index, index),
+ TP_PROTO(const struct amdtp_stream *s, u32 cycles, const __be32 *cip_header, unsigned int payload_length, unsigned int data_blocks, unsigned int data_block_counter, unsigned int packet_index, unsigned int index, u32 curr_cycle_time),
+ TP_ARGS(s, cycles, cip_header, payload_length, data_blocks, data_block_counter, packet_index, index, curr_cycle_time),
TP_STRUCT__entry(
+ __field(unsigned int, cycle_time)
__field(unsigned int, second)
__field(unsigned int, cycle)
__field(int, channel)
@@ -31,6 +32,7 @@ TRACE_EVENT(amdtp_packet,
__field(unsigned int, index)
),
TP_fast_assign(
+ __entry->cycle_time = curr_cycle_time;
__entry->second = cycles / CYCLES_PER_SECOND;
__entry->cycle = cycles % CYCLES_PER_SECOND;
__entry->channel = s->context->channel;
@@ -53,7 +55,8 @@ TRACE_EVENT(amdtp_packet,
__entry->index = index;
),
TP_printk(
- "%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u %s",
+ "%08x %02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u %s",
+ __entry->cycle_time,
__entry->second,
__entry->cycle,
__entry->src,
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index 9be2260e4ca2d2..a13c0b408aadfc 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -271,12 +271,14 @@ EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints);
* @s: the AMDTP stream to configure
* @rate: the sample rate
* @data_block_quadlets: the size of a data block in quadlet unit
+ * @pcm_frame_multiplier: the multiplier to compute the number of PCM frames by the number of AMDTP
+ * events.
*
* The parameters must be set before the stream is started, and must not be
* changed while the stream is running.
*/
int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
- unsigned int data_block_quadlets)
+ unsigned int data_block_quadlets, unsigned int pcm_frame_multiplier)
{
unsigned int sfc;
@@ -298,6 +300,8 @@ int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
if (s->flags & CIP_BLOCKING)
s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
+ s->pcm_frame_multiplier = pcm_frame_multiplier;
+
return 0;
}
EXPORT_SYMBOL(amdtp_stream_set_parameters);
@@ -348,27 +352,29 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
}
EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
+#define prev_packet_desc(s, desc) \
+ list_prev_entry_circular(desc, &s->packet_descs_list, link)
+
static void pool_blocking_data_blocks(struct amdtp_stream *s, struct seq_desc *descs,
- const unsigned int seq_size, unsigned int seq_tail,
- unsigned int count)
+ unsigned int size, unsigned int pos, unsigned int count)
{
const unsigned int syt_interval = s->syt_interval;
int i;
for (i = 0; i < count; ++i) {
- struct seq_desc *desc = descs + seq_tail;
+ struct seq_desc *desc = descs + pos;
if (desc->syt_offset != CIP_SYT_NO_INFO)
desc->data_blocks = syt_interval;
else
desc->data_blocks = 0;
- seq_tail = (seq_tail + 1) % seq_size;
+ pos = (pos + 1) % size;
}
}
static void pool_ideal_nonblocking_data_blocks(struct amdtp_stream *s, struct seq_desc *descs,
- const unsigned int seq_size, unsigned int seq_tail,
+ unsigned int size, unsigned int pos,
unsigned int count)
{
const enum cip_sfc sfc = s->sfc;
@@ -376,7 +382,7 @@ static void pool_ideal_nonblocking_data_blocks(struct amdtp_stream *s, struct se
int i;
for (i = 0; i < count; ++i) {
- struct seq_desc *desc = descs + seq_tail;
+ struct seq_desc *desc = descs + pos;
if (!cip_sfc_is_base_44100(sfc)) {
// Sample_rate / 8000 is an integer, and precomputed.
@@ -403,7 +409,7 @@ static void pool_ideal_nonblocking_data_blocks(struct amdtp_stream *s, struct se
state = phase;
}
- seq_tail = (seq_tail + 1) % seq_size;
+ pos = (pos + 1) % size;
}
s->ctx_data.rx.data_block_state = state;
@@ -449,8 +455,7 @@ static unsigned int calculate_syt_offset(unsigned int *last_syt_offset,
}
static void pool_ideal_syt_offsets(struct amdtp_stream *s, struct seq_desc *descs,
- const unsigned int seq_size, unsigned int seq_tail,
- unsigned int count)
+ unsigned int size, unsigned int pos, unsigned int count)
{
const enum cip_sfc sfc = s->sfc;
unsigned int last = s->ctx_data.rx.last_syt_offset;
@@ -458,11 +463,11 @@ static void pool_ideal_syt_offsets(struct amdtp_stream *s, struct seq_desc *desc
int i;
for (i = 0; i < count; ++i) {
- struct seq_desc *desc = descs + seq_tail;
+ struct seq_desc *desc = descs + pos;
desc->syt_offset = calculate_syt_offset(&last, &state, sfc);
- seq_tail = (seq_tail + 1) % seq_size;
+ pos = (pos + 1) % size;
}
s->ctx_data.rx.last_syt_offset = last;
@@ -497,7 +502,7 @@ static unsigned int compute_syt_offset(unsigned int syt, unsigned int cycle,
static unsigned int calculate_cached_cycle_count(struct amdtp_stream *s, unsigned int head)
{
const unsigned int cache_size = s->ctx_data.tx.cache.size;
- unsigned int cycles = s->ctx_data.tx.cache.tail;
+ unsigned int cycles = s->ctx_data.tx.cache.pos;
if (cycles < head)
cycles += cache_size;
@@ -506,18 +511,17 @@ static unsigned int calculate_cached_cycle_count(struct amdtp_stream *s, unsigne
return cycles;
}
-static void cache_seq(struct amdtp_stream *s, const struct pkt_desc *descs, unsigned int desc_count)
+static void cache_seq(struct amdtp_stream *s, const struct pkt_desc *src, unsigned int desc_count)
{
const unsigned int transfer_delay = s->transfer_delay;
const unsigned int cache_size = s->ctx_data.tx.cache.size;
struct seq_desc *cache = s->ctx_data.tx.cache.descs;
- unsigned int cache_tail = s->ctx_data.tx.cache.tail;
+ unsigned int cache_pos = s->ctx_data.tx.cache.pos;
bool aware_syt = !(s->flags & CIP_UNAWARE_SYT);
int i;
for (i = 0; i < desc_count; ++i) {
- struct seq_desc *dst = cache + cache_tail;
- const struct pkt_desc *src = descs + i;
+ struct seq_desc *dst = cache + cache_pos;
if (aware_syt && src->syt != CIP_SYT_NO_INFO)
dst->syt_offset = compute_syt_offset(src->syt, src->cycle, transfer_delay);
@@ -525,70 +529,68 @@ static void cache_seq(struct amdtp_stream *s, const struct pkt_desc *descs, unsi
dst->syt_offset = CIP_SYT_NO_INFO;
dst->data_blocks = src->data_blocks;
- cache_tail = (cache_tail + 1) % cache_size;
+ cache_pos = (cache_pos + 1) % cache_size;
+ src = amdtp_stream_next_packet_desc(s, src);
}
- s->ctx_data.tx.cache.tail = cache_tail;
+ s->ctx_data.tx.cache.pos = cache_pos;
}
-static void pool_ideal_seq_descs(struct amdtp_stream *s, unsigned int count)
+static void pool_ideal_seq_descs(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size,
+ unsigned int pos, unsigned int count)
{
- struct seq_desc *descs = s->ctx_data.rx.seq.descs;
- unsigned int seq_tail = s->ctx_data.rx.seq.tail;
- const unsigned int seq_size = s->ctx_data.rx.seq.size;
-
- pool_ideal_syt_offsets(s, descs, seq_size, seq_tail, count);
+ pool_ideal_syt_offsets(s, descs, size, pos, count);
if (s->flags & CIP_BLOCKING)
- pool_blocking_data_blocks(s, descs, seq_size, seq_tail, count);
+ pool_blocking_data_blocks(s, descs, size, pos, count);
else
- pool_ideal_nonblocking_data_blocks(s, descs, seq_size, seq_tail, count);
-
- s->ctx_data.rx.seq.tail = (seq_tail + count) % seq_size;
+ pool_ideal_nonblocking_data_blocks(s, descs, size, pos, count);
}
-static void pool_replayed_seq(struct amdtp_stream *s, unsigned int count)
+static void pool_replayed_seq(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size,
+ unsigned int pos, unsigned int count)
{
struct amdtp_stream *target = s->ctx_data.rx.replay_target;
const struct seq_desc *cache = target->ctx_data.tx.cache.descs;
const unsigned int cache_size = target->ctx_data.tx.cache.size;
- unsigned int cache_head = s->ctx_data.rx.cache_head;
- struct seq_desc *descs = s->ctx_data.rx.seq.descs;
- const unsigned int seq_size = s->ctx_data.rx.seq.size;
- unsigned int seq_tail = s->ctx_data.rx.seq.tail;
+ unsigned int cache_pos = s->ctx_data.rx.cache_pos;
int i;
for (i = 0; i < count; ++i) {
- descs[seq_tail] = cache[cache_head];
- seq_tail = (seq_tail + 1) % seq_size;
- cache_head = (cache_head + 1) % cache_size;
+ descs[pos] = cache[cache_pos];
+ cache_pos = (cache_pos + 1) % cache_size;
+ pos = (pos + 1) % size;
}
- s->ctx_data.rx.seq.tail = seq_tail;
- s->ctx_data.rx.cache_head = cache_head;
+ s->ctx_data.rx.cache_pos = cache_pos;
}
-static void pool_seq_descs(struct amdtp_stream *s, unsigned int count)
+static void pool_seq_descs(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size,
+ unsigned int pos, unsigned int count)
{
struct amdtp_domain *d = s->domain;
+ void (*pool_seq_descs)(struct amdtp_stream *s, struct seq_desc *descs, unsigned int size,
+ unsigned int pos, unsigned int count);
if (!d->replay.enable || !s->ctx_data.rx.replay_target) {
- pool_ideal_seq_descs(s, count);
+ pool_seq_descs = pool_ideal_seq_descs;
} else {
if (!d->replay.on_the_fly) {
- pool_replayed_seq(s, count);
+ pool_seq_descs = pool_replayed_seq;
} else {
struct amdtp_stream *tx = s->ctx_data.rx.replay_target;
const unsigned int cache_size = tx->ctx_data.tx.cache.size;
- const unsigned int cache_head = s->ctx_data.rx.cache_head;
- unsigned int cached_cycles = calculate_cached_cycle_count(tx, cache_head);
+ const unsigned int cache_pos = s->ctx_data.rx.cache_pos;
+ unsigned int cached_cycles = calculate_cached_cycle_count(tx, cache_pos);
if (cached_cycles > count && cached_cycles > cache_size / 2)
- pool_replayed_seq(s, count);
+ pool_seq_descs = pool_replayed_seq;
else
- pool_ideal_seq_descs(s, count);
+ pool_seq_descs = pool_ideal_seq_descs;
}
}
+
+ pool_seq_descs(s, descs, size, pos, count);
}
static void update_pcm_pointers(struct amdtp_stream *s,
@@ -679,7 +681,7 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
struct fw_iso_packet *params, unsigned int header_length,
unsigned int data_blocks,
unsigned int data_block_counter,
- unsigned int syt, unsigned int index)
+ unsigned int syt, unsigned int index, u32 curr_cycle_time)
{
unsigned int payload_length;
__be32 *cip_header;
@@ -696,7 +698,7 @@ static void build_it_pkt_header(struct amdtp_stream *s, unsigned int cycle,
}
trace_amdtp_packet(s, cycle, cip_header, payload_length + header_length, data_blocks,
- data_block_counter, s->packet_index, index);
+ data_block_counter, s->packet_index, index, curr_cycle_time);
}
static int check_cip_header(struct amdtp_stream *s, const __be32 *buf,
@@ -798,7 +800,8 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
const __be32 *ctx_header,
unsigned int *data_blocks,
unsigned int *data_block_counter,
- unsigned int *syt, unsigned int packet_index, unsigned int index)
+ unsigned int *syt, unsigned int packet_index, unsigned int index,
+ u32 curr_cycle_time)
{
unsigned int payload_length;
const __be32 *cip_header;
@@ -843,7 +846,7 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
}
trace_amdtp_packet(s, cycle, cip_header, payload_length, *data_blocks,
- *data_block_counter, packet_index, index);
+ *data_block_counter, packet_index, index, curr_cycle_time);
return 0;
}
@@ -851,10 +854,15 @@ static int parse_ir_ctx_header(struct amdtp_stream *s, unsigned int cycle,
// In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On
// the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent
// it. Thus, via Linux firewire subsystem, we can get the 3 bits for second.
+static inline u32 compute_ohci_iso_ctx_cycle_count(u32 tstamp)
+{
+ return (((tstamp >> 13) & 0x07) * CYCLES_PER_SECOND) + (tstamp & 0x1fff);
+}
+
static inline u32 compute_ohci_cycle_count(__be32 ctx_header_tstamp)
{
u32 tstamp = be32_to_cpu(ctx_header_tstamp) & HEADER_TSTAMP_MASK;
- return (((tstamp >> 13) & 0x07) * 8000) + (tstamp & 0x1fff);
+ return compute_ohci_iso_ctx_cycle_count(tstamp);
}
static inline u32 increment_ohci_cycle_count(u32 cycle, unsigned int addend)
@@ -865,6 +873,14 @@ static inline u32 increment_ohci_cycle_count(u32 cycle, unsigned int addend)
return cycle;
}
+static inline u32 decrement_ohci_cycle_count(u32 minuend, u32 subtrahend)
+{
+ if (minuend < subtrahend)
+ minuend += OHCI_SECOND_MODULUS * CYCLES_PER_SECOND;
+
+ return minuend - subtrahend;
+}
+
static int compare_ohci_cycle_count(u32 lval, u32 rval)
{
if (lval == rval)
@@ -886,22 +902,23 @@ static inline u32 compute_ohci_it_cycle(const __be32 ctx_header_tstamp,
return increment_ohci_cycle_count(cycle, queue_size);
}
-static int generate_device_pkt_descs(struct amdtp_stream *s,
- struct pkt_desc *descs,
- const __be32 *ctx_header,
- unsigned int packets,
- unsigned int *desc_count)
+static int generate_tx_packet_descs(struct amdtp_stream *s, struct pkt_desc *desc,
+ const __be32 *ctx_header, unsigned int packet_count,
+ unsigned int *desc_count)
{
unsigned int next_cycle = s->next_cycle;
unsigned int dbc = s->data_block_counter;
unsigned int packet_index = s->packet_index;
unsigned int queue_size = s->queue_size;
+ u32 curr_cycle_time = 0;
int i;
int err;
+ if (trace_amdtp_packet_enabled())
+ (void)fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &curr_cycle_time);
+
*desc_count = 0;
- for (i = 0; i < packets; ++i) {
- struct pkt_desc *desc = descs + *desc_count;
+ for (i = 0; i < packet_count; ++i) {
unsigned int cycle;
bool lost;
unsigned int data_blocks;
@@ -925,7 +942,7 @@ static int generate_device_pkt_descs(struct amdtp_stream *s,
desc->data_blocks = 0;
desc->data_block_counter = dbc;
desc->ctx_payload = NULL;
- ++desc;
+ desc = amdtp_stream_next_packet_desc(s, desc);
++(*desc_count);
}
} else if (s->flags & CIP_JUMBO_PAYLOAD) {
@@ -944,7 +961,7 @@ static int generate_device_pkt_descs(struct amdtp_stream *s,
}
err = parse_ir_ctx_header(s, cycle, ctx_header, &data_blocks, &dbc, &syt,
- packet_index, i);
+ packet_index, i, curr_cycle_time);
if (err < 0)
return err;
@@ -958,6 +975,7 @@ static int generate_device_pkt_descs(struct amdtp_stream *s,
dbc = (dbc + desc->data_blocks) & 0xff;
next_cycle = increment_ohci_cycle_count(next_cycle, 1);
+ desc = amdtp_stream_next_packet_desc(s, desc);
++(*desc_count);
ctx_header += s->ctx_data.tx.ctx_header_size / sizeof(*ctx_header);
packet_index = (packet_index + 1) % queue_size;
@@ -980,20 +998,21 @@ static unsigned int compute_syt(unsigned int syt_offset, unsigned int cycle,
return syt & CIP_SYT_MASK;
}
-static void generate_pkt_descs(struct amdtp_stream *s, const __be32 *ctx_header, unsigned int packets)
+static void generate_rx_packet_descs(struct amdtp_stream *s, struct pkt_desc *desc,
+ const __be32 *ctx_header, unsigned int packet_count)
{
- struct pkt_desc *descs = s->pkt_descs;
- const struct seq_desc *seq_descs = s->ctx_data.rx.seq.descs;
- const unsigned int seq_size = s->ctx_data.rx.seq.size;
+ struct seq_desc *seq_descs = s->ctx_data.rx.seq.descs;
+ unsigned int seq_size = s->ctx_data.rx.seq.size;
+ unsigned int seq_pos = s->ctx_data.rx.seq.pos;
unsigned int dbc = s->data_block_counter;
- unsigned int seq_head = s->ctx_data.rx.seq.head;
bool aware_syt = !(s->flags & CIP_UNAWARE_SYT);
int i;
- for (i = 0; i < packets; ++i) {
- struct pkt_desc *desc = descs + i;
+ pool_seq_descs(s, seq_descs, seq_size, seq_pos, packet_count);
+
+ for (i = 0; i < packet_count; ++i) {
unsigned int index = (s->packet_index + i) % s->queue_size;
- const struct seq_desc *seq = seq_descs + seq_head;
+ const struct seq_desc *seq = seq_descs + seq_pos;
desc->cycle = compute_ohci_it_cycle(*ctx_header, s->queue_size);
@@ -1014,13 +1033,14 @@ static void generate_pkt_descs(struct amdtp_stream *s, const __be32 *ctx_header,
desc->ctx_payload = s->buffer.packets[index].buffer;
- seq_head = (seq_head + 1) % seq_size;
+ seq_pos = (seq_pos + 1) % seq_size;
+ desc = amdtp_stream_next_packet_desc(s, desc);
++ctx_header;
}
s->data_block_counter = dbc;
- s->ctx_data.rx.seq.head = seq_head;
+ s->ctx_data.rx.seq.pos = seq_pos;
}
static inline void cancel_stream(struct amdtp_stream *s)
@@ -1031,17 +1051,85 @@ static inline void cancel_stream(struct amdtp_stream *s)
WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN);
}
+static snd_pcm_sframes_t compute_pcm_extra_delay(struct amdtp_stream *s,
+ const struct pkt_desc *desc, unsigned int count)
+{
+ unsigned int data_block_count = 0;
+ u32 latest_cycle;
+ u32 cycle_time;
+ u32 curr_cycle;
+ u32 cycle_gap;
+ int i, err;
+
+ if (count == 0)
+ goto end;
+
+ // Forward to the latest record.
+ for (i = 0; i < count - 1; ++i)
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ latest_cycle = desc->cycle;
+
+ err = fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &cycle_time);
+ if (err < 0)
+ goto end;
+
+ // Compute cycle count with lower 3 bits of second field and cycle field like timestamp
+ // format of 1394 OHCI isochronous context.
+ curr_cycle = compute_ohci_iso_ctx_cycle_count((cycle_time >> 12) & 0x0000ffff);
+
+ if (s->direction == AMDTP_IN_STREAM) {
+ // NOTE: The AMDTP packet descriptor should be for the past isochronous cycle since
+ // it corresponds to arrived isochronous packet.
+ if (compare_ohci_cycle_count(latest_cycle, curr_cycle) > 0)
+ goto end;
+ cycle_gap = decrement_ohci_cycle_count(curr_cycle, latest_cycle);
+
+ // NOTE: estimate delay by recent history of arrived AMDTP packets. The estimated
+ // value expectedly corresponds to a few packets (0-2) since the packet arrived at
+ // the most recent isochronous cycle has been already processed.
+ for (i = 0; i < cycle_gap; ++i) {
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ data_block_count += desc->data_blocks;
+ }
+ } else {
+ // NOTE: The AMDTP packet descriptor should be for the future isochronous cycle
+ // since it was already scheduled.
+ if (compare_ohci_cycle_count(latest_cycle, curr_cycle) < 0)
+ goto end;
+ cycle_gap = decrement_ohci_cycle_count(latest_cycle, curr_cycle);
+
+ // NOTE: use history of scheduled packets.
+ for (i = 0; i < cycle_gap; ++i) {
+ data_block_count += desc->data_blocks;
+ desc = prev_packet_desc(s, desc);
+ }
+ }
+end:
+ return data_block_count * s->pcm_frame_multiplier;
+}
+
static void process_ctx_payloads(struct amdtp_stream *s,
- const struct pkt_desc *descs,
- unsigned int packets)
+ const struct pkt_desc *desc,
+ unsigned int count)
{
struct snd_pcm_substream *pcm;
- unsigned int pcm_frames;
+ int i;
pcm = READ_ONCE(s->pcm);
- pcm_frames = s->process_ctx_payloads(s, descs, packets, pcm);
- if (pcm)
- update_pcm_pointers(s, pcm, pcm_frames);
+ s->process_ctx_payloads(s, desc, count, pcm);
+
+ if (pcm) {
+ unsigned int data_block_count = 0;
+
+ pcm->runtime->delay = compute_pcm_extra_delay(s, desc, count);
+
+ for (i = 0; i < count; ++i) {
+ data_block_count += desc->data_blocks;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
+
+ update_pcm_pointers(s, pcm, data_block_count * s->pcm_frame_multiplier);
+ }
}
static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length,
@@ -1052,8 +1140,10 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_
const __be32 *ctx_header = header;
const unsigned int events_per_period = d->events_per_period;
unsigned int event_count = s->ctx_data.rx.event_count;
+ struct pkt_desc *desc = s->packet_descs_cursor;
unsigned int pkt_header_length;
unsigned int packets;
+ u32 curr_cycle_time;
bool need_hw_irq;
int i;
@@ -1063,11 +1153,9 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_
// Calculate the number of packets in buffer and check XRUN.
packets = header_length / sizeof(*ctx_header);
- pool_seq_descs(s, packets);
-
- generate_pkt_descs(s, ctx_header, packets);
+ generate_rx_packet_descs(s, desc, ctx_header, packets);
- process_ctx_payloads(s, s->pkt_descs, packets);
+ process_ctx_payloads(s, desc, packets);
if (!(s->flags & CIP_NO_HEADER))
pkt_header_length = IT_PKT_HEADER_SIZE_CIP;
@@ -1084,8 +1172,10 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_
need_hw_irq = false;
}
+ if (trace_amdtp_packet_enabled())
+ (void)fw_card_read_cycle_time(fw_parent_device(s->unit)->card, &curr_cycle_time);
+
for (i = 0; i < packets; ++i) {
- const struct pkt_desc *desc = s->pkt_descs + i;
struct {
struct fw_iso_packet params;
__be32 header[CIP_HEADER_QUADLETS];
@@ -1094,7 +1184,7 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_
build_it_pkt_header(s, desc->cycle, &template.params, pkt_header_length,
desc->data_blocks, desc->data_block_counter,
- desc->syt, i);
+ desc->syt, i, curr_cycle_time);
if (s == s->domain->irq_target) {
event_count += desc->data_blocks;
@@ -1108,9 +1198,12 @@ static void process_rx_packets(struct fw_iso_context *context, u32 tstamp, size_
cancel_stream(s);
return;
}
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
}
s->ctx_data.rx.event_count = event_count;
+ s->packet_descs_cursor = desc;
}
static void skip_rx_packets(struct fw_iso_context *context, u32 tstamp, size_t header_length,
@@ -1188,6 +1281,9 @@ static void process_rx_packets_intermediately(struct fw_iso_context *context, u3
s->ready_processing = true;
wake_up(&s->ready_wait);
+ if (d->replay.enable)
+ s->ctx_data.rx.cache_pos = 0;
+
process_rx_packets(context, tstamp, header_length, ctx_header, private_data);
if (amdtp_streaming_error(s))
return;
@@ -1204,7 +1300,8 @@ static void process_tx_packets(struct fw_iso_context *context, u32 tstamp, size_
{
struct amdtp_stream *s = private_data;
__be32 *ctx_header = header;
- unsigned int packets;
+ struct pkt_desc *desc = s->packet_descs_cursor;
+ unsigned int packet_count;
unsigned int desc_count;
int i;
int err;
@@ -1213,10 +1310,10 @@ static void process_tx_packets(struct fw_iso_context *context, u32 tstamp, size_
return;
// Calculate the number of packets in buffer and check XRUN.
- packets = header_length / s->ctx_data.tx.ctx_header_size;
+ packet_count = header_length / s->ctx_data.tx.ctx_header_size;
desc_count = 0;
- err = generate_device_pkt_descs(s, s->pkt_descs, ctx_header, packets, &desc_count);
+ err = generate_tx_packet_descs(s, desc, ctx_header, packet_count, &desc_count);
if (err < 0) {
if (err != -EAGAIN) {
cancel_stream(s);
@@ -1225,13 +1322,17 @@ static void process_tx_packets(struct fw_iso_context *context, u32 tstamp, size_
} else {
struct amdtp_domain *d = s->domain;
- process_ctx_payloads(s, s->pkt_descs, desc_count);
+ process_ctx_payloads(s, desc, desc_count);
if (d->replay.enable)
- cache_seq(s, s->pkt_descs, desc_count);
+ cache_seq(s, desc, desc_count);
+
+ for (i = 0; i < desc_count; ++i)
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ s->packet_descs_cursor = desc;
}
- for (i = 0; i < packets; ++i) {
+ for (i = 0; i < packet_count; ++i) {
struct fw_iso_packet params = {0};
if (queue_in_packet(s, &params) < 0) {
@@ -1551,7 +1652,8 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
unsigned int ctx_header_size;
unsigned int max_ctx_payload_size;
enum dma_data_direction dir;
- int type, tag, err;
+ struct pkt_desc *descs;
+ int i, type, tag, err;
mutex_lock(&s->mutex);
@@ -1616,7 +1718,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
// possible to cache much unexpectedly.
s->ctx_data.tx.cache.size = max_t(unsigned int, s->syt_interval * 2,
queue_size * 3 / 2);
- s->ctx_data.tx.cache.tail = 0;
+ s->ctx_data.tx.cache.pos = 0;
s->ctx_data.tx.cache.descs = kcalloc(s->ctx_data.tx.cache.size,
sizeof(*s->ctx_data.tx.cache.descs), GFP_KERNEL);
if (!s->ctx_data.tx.cache.descs) {
@@ -1644,8 +1746,7 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
goto err_context;
}
s->ctx_data.rx.seq.size = queue_size;
- s->ctx_data.rx.seq.tail = 0;
- s->ctx_data.rx.seq.head = 0;
+ s->ctx_data.rx.seq.pos = 0;
entry = &initial_state[s->sfc];
s->ctx_data.rx.data_block_state = entry->data_block;
@@ -1660,12 +1761,24 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
else
s->tag = TAG_CIP;
- s->pkt_descs = kcalloc(s->queue_size, sizeof(*s->pkt_descs),
- GFP_KERNEL);
- if (!s->pkt_descs) {
+ // NOTE: When operating without hardIRQ/softIRQ, applications tends to call ioctl request
+ // for runtime of PCM substream in the interval equivalent to the size of PCM buffer. It
+ // could take a round over queue of AMDTP packet descriptors and small loss of history. For
+ // safe, keep more 8 elements for the queue, equivalent to 1 ms.
+ descs = kcalloc(s->queue_size + 8, sizeof(*descs), GFP_KERNEL);
+ if (!descs) {
err = -ENOMEM;
goto err_context;
}
+ s->packet_descs = descs;
+
+ INIT_LIST_HEAD(&s->packet_descs_list);
+ for (i = 0; i < s->queue_size; ++i) {
+ INIT_LIST_HEAD(&descs->link);
+ list_add_tail(&descs->link, &s->packet_descs_list);
+ ++descs;
+ }
+ s->packet_descs_cursor = list_first_entry(&s->packet_descs_list, struct pkt_desc, link);
s->packet_index = 0;
do {
@@ -1704,7 +1817,8 @@ static int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed,
return 0;
err_pkt_descs:
- kfree(s->pkt_descs);
+ kfree(s->packet_descs);
+ s->packet_descs = NULL;
err_context:
if (s->direction == AMDTP_OUT_STREAM) {
kfree(s->ctx_data.rx.seq.descs);
@@ -1798,7 +1912,8 @@ static void amdtp_stream_stop(struct amdtp_stream *s)
fw_iso_context_destroy(s->context);
s->context = ERR_PTR(-1);
iso_packets_buffer_destroy(&s->buffer, s->unit);
- kfree(s->pkt_descs);
+ kfree(s->packet_descs);
+ s->packet_descs = NULL;
if (s->direction == AMDTP_OUT_STREAM) {
kfree(s->ctx_data.rx.seq.descs);
@@ -1917,7 +2032,6 @@ static int make_association(struct amdtp_domain *d)
}
rx->ctx_data.rx.replay_target = tx;
- rx->ctx_data.rx.cache_head = 0;
++dst_index;
}
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
index 1f957c946c956a..b7ff44751ab93b 100644
--- a/sound/firewire/amdtp-stream.h
+++ b/sound/firewire/amdtp-stream.h
@@ -103,14 +103,14 @@ struct pkt_desc {
unsigned int data_blocks;
unsigned int data_block_counter;
__be32 *ctx_payload;
+ struct list_head link;
};
struct amdtp_stream;
-typedef unsigned int (*amdtp_stream_process_ctx_payloads_t)(
- struct amdtp_stream *s,
- const struct pkt_desc *desc,
- unsigned int packets,
- struct snd_pcm_substream *pcm);
+typedef void (*amdtp_stream_process_ctx_payloads_t)(struct amdtp_stream *s,
+ const struct pkt_desc *desc,
+ unsigned int count,
+ struct snd_pcm_substream *pcm);
struct amdtp_domain;
struct amdtp_stream {
@@ -125,7 +125,9 @@ struct amdtp_stream {
struct iso_packets_buffer buffer;
unsigned int queue_size;
int packet_index;
- struct pkt_desc *pkt_descs;
+ struct pkt_desc *packet_descs;
+ struct list_head packet_descs_list;
+ struct pkt_desc *packet_descs_cursor;
int tag;
union {
struct {
@@ -145,7 +147,7 @@ struct amdtp_stream {
struct {
struct seq_desc *descs;
unsigned int size;
- unsigned int tail;
+ unsigned int pos;
} cache;
} tx;
struct {
@@ -159,8 +161,7 @@ struct amdtp_stream {
struct {
struct seq_desc *descs;
unsigned int size;
- unsigned int tail;
- unsigned int head;
+ unsigned int pos;
} seq;
unsigned int data_block_state;
@@ -168,7 +169,7 @@ struct amdtp_stream {
unsigned int last_syt_offset;
struct amdtp_stream *replay_target;
- unsigned int cache_head;
+ unsigned int cache_pos;
} rx;
} ctx_data;
@@ -188,6 +189,7 @@ struct amdtp_stream {
struct snd_pcm_substream *pcm;
snd_pcm_uframes_t pcm_buffer_pointer;
unsigned int pcm_period_pointer;
+ unsigned int pcm_frame_multiplier;
// To start processing content of packets at the same cycle in several contexts for
// each direction.
@@ -214,7 +216,7 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
void amdtp_stream_destroy(struct amdtp_stream *s);
int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
- unsigned int data_block_quadlets);
+ unsigned int data_block_quadlets, unsigned int pcm_frame_multiplier);
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
void amdtp_stream_update(struct amdtp_stream *s);
@@ -277,6 +279,16 @@ static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
WRITE_ONCE(s->pcm, pcm);
}
+/**
+ * amdtp_stream_next_packet_desc - retrieve next descriptor for amdtp packet.
+ * @s: the AMDTP stream
+ * @desc: the descriptor of packet
+ *
+ * This macro computes next descriptor so that the list of descriptors behaves circular queue.
+ */
+#define amdtp_stream_next_packet_desc(s, desc) \
+ list_next_entry_circular(desc, &s->packet_descs_list, link)
+
static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
{
return sfc & 1;
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c
index 59b86c8d89e13b..7db0024495b7ac 100644
--- a/sound/firewire/digi00x/amdtp-dot.c
+++ b/sound/firewire/digi00x/amdtp-dot.c
@@ -123,7 +123,7 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
* A first data channel is for MIDI messages, the rest is Multi Bit
* Linear Audio data channel.
*/
- err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1);
+ err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1, 1);
if (err < 0)
return err;
@@ -341,16 +341,13 @@ void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
WRITE_ONCE(p->midi[port], midi);
}
-static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
- const struct pkt_desc *descs,
- unsigned int packets,
- struct snd_pcm_substream *pcm)
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
{
unsigned int pcm_frames = 0;
int i;
- for (i = 0; i < packets; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
@@ -360,21 +357,18 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
}
read_midi_messages(s, buf, data_blocks);
- }
- return pcm_frames;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
}
-static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
- const struct pkt_desc *descs,
- unsigned int packets,
- struct snd_pcm_substream *pcm)
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
{
unsigned int pcm_frames = 0;
int i;
- for (i = 0; i < packets; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
@@ -387,9 +381,9 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
write_midi_messages(s, buf, data_blocks,
desc->data_block_counter);
- }
- return pcm_frames;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
}
int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
diff --git a/sound/firewire/fireface/amdtp-ff.c b/sound/firewire/fireface/amdtp-ff.c
index 98177b0666d3e3..76c9d33ed57267 100644
--- a/sound/firewire/fireface/amdtp-ff.c
+++ b/sound/firewire/fireface/amdtp-ff.c
@@ -24,7 +24,7 @@ int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate,
p->pcm_channels = pcm_channels;
data_channels = pcm_channels;
- return amdtp_stream_set_parameters(s, rate, data_channels);
+ return amdtp_stream_set_parameters(s, rate, data_channels, 1);
}
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
@@ -112,16 +112,13 @@ int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
return amdtp_stream_add_pcm_hw_constraints(s, runtime);
}
-static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
- const struct pkt_desc *descs,
- unsigned int packets,
- struct snd_pcm_substream *pcm)
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
{
unsigned int pcm_frames = 0;
int i;
- for (i = 0; i < packets; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__le32 *buf = (__le32 *)desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
@@ -131,21 +128,18 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
} else {
write_pcm_silence(s, buf, data_blocks);
}
- }
- return pcm_frames;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
}
-static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
- const struct pkt_desc *descs,
- unsigned int packets,
- struct snd_pcm_substream *pcm)
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
{
unsigned int pcm_frames = 0;
int i;
- for (i = 0; i < packets; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__le32 *buf = (__le32 *)desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
@@ -153,9 +147,9 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
read_pcm_s32(s, pcm, buf, data_blocks, pcm_frames);
pcm_frames += data_blocks;
}
- }
- return pcm_frames;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
}
int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
diff --git a/sound/firewire/fireface/ff-hwdep.c b/sound/firewire/fireface/ff-hwdep.c
index ea64a2a41eea0d..8a741b3b0436f3 100644
--- a/sound/firewire/fireface/ff-hwdep.c
+++ b/sound/firewire/fireface/ff-hwdep.c
@@ -15,16 +15,23 @@
#include "ff.h"
+static bool has_msg(struct snd_ff *ff)
+{
+ if (ff->spec->protocol->has_msg)
+ return ff->spec->protocol->has_msg(ff);
+ else
+ return 0;
+}
+
static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
loff_t *offset)
{
struct snd_ff *ff = hwdep->private_data;
DEFINE_WAIT(wait);
- union snd_firewire_event event;
spin_lock_irq(&ff->lock);
- while (!ff->dev_lock_changed) {
+ while (!ff->dev_lock_changed && !has_msg(ff)) {
prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
spin_unlock_irq(&ff->lock);
schedule();
@@ -34,17 +41,29 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
spin_lock_irq(&ff->lock);
}
- memset(&event, 0, sizeof(event));
- event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
- event.lock_status.status = (ff->dev_lock_count > 0);
- ff->dev_lock_changed = false;
+ if (ff->dev_lock_changed && count >= sizeof(struct snd_firewire_event_lock_status)) {
+ struct snd_firewire_event_lock_status ev = {
+ .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
+ .status = (ff->dev_lock_count > 0),
+ };
- count = min_t(long, count, sizeof(event.lock_status));
+ ff->dev_lock_changed = false;
- spin_unlock_irq(&ff->lock);
+ spin_unlock_irq(&ff->lock);
- if (copy_to_user(buf, &event, count))
- return -EFAULT;
+ if (copy_to_user(buf, &ev, sizeof(ev)))
+ return -EFAULT;
+ count = sizeof(ev);
+ } else if (has_msg(ff)) {
+ // NOTE: Acquired spin lock should be released before accessing to user space in the
+ // callback since the access can cause page fault.
+ count = ff->spec->protocol->copy_msg_to_user(ff, buf, count);
+ spin_unlock_irq(&ff->lock);
+ } else {
+ spin_unlock_irq(&ff->lock);
+
+ count = 0;
+ }
return count;
}
@@ -58,7 +77,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
poll_wait(file, &ff->hwdep_wait, wait);
spin_lock_irq(&ff->lock);
- if (ff->dev_lock_changed)
+ if (ff->dev_lock_changed || has_msg(ff))
events = EPOLLIN | EPOLLRDNORM;
else
events = 0;
diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c
index 8900ffe517ed3f..efd59e9d99355e 100644
--- a/sound/firewire/fireface/ff-protocol-former.c
+++ b/sound/firewire/fireface/ff-protocol-former.c
@@ -402,8 +402,8 @@ static void ff800_finish_session(struct snd_ff *ff)
// address.
// A write transaction to clear registered higher 4 bytes of destination address
// has an effect to suppress asynchronous transaction from device.
-static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset,
- __le32 *buf, size_t length)
+static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+ size_t length, u32 tstamp)
{
int i;
@@ -418,7 +418,7 @@ static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset,
}
const struct snd_ff_protocol snd_ff_protocol_ff800 = {
- .handle_midi_msg = ff800_handle_midi_msg,
+ .handle_msg = ff800_handle_midi_msg,
.fill_midi_msg = former_fill_midi_msg,
.get_clock = former_get_clock,
.switch_fetching_mode = former_switch_fetching_mode,
@@ -534,6 +534,35 @@ static void ff400_finish_session(struct snd_ff *ff)
FF400_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
}
+static void parse_midi_msg(struct snd_ff *ff, u32 quad, unsigned int port)
+{
+ struct snd_rawmidi_substream *substream = READ_ONCE(ff->tx_midi_substreams[port]);
+
+ if (substream != NULL) {
+ u8 byte = (quad >> (16 * port)) & 0x000000ff;
+
+ snd_rawmidi_receive(substream, &byte, 1);
+ }
+}
+
+#define FF400_QUEUE_SIZE 32
+
+struct ff400_msg_parser {
+ struct {
+ u32 msg;
+ u32 tstamp;
+ } msgs[FF400_QUEUE_SIZE];
+ size_t push_pos;
+ size_t pull_pos;
+};
+
+static bool ff400_has_msg(struct snd_ff *ff)
+{
+ struct ff400_msg_parser *parser = ff->msg_parser;
+
+ return (parser->push_pos != parser->pull_pos);
+}
+
// For Fireface 400, lower 4 bytes of destination address is configured by bit
// flag in quadlet register (little endian) at 0x'0000'801'0051c. Drivers can
// select one of 4 options:
@@ -553,46 +582,147 @@ static void ff400_finish_session(struct snd_ff *ff)
// input attenuation. This driver allocates destination address with '0000'0000
// in its lower offset and expects userspace application to configure the
// register for it.
-static void ff400_handle_midi_msg(struct snd_ff *ff, unsigned int offset,
- __le32 *buf, size_t length)
+
+// When the message is for signal level operation, the upper 4 bits in MSB expresses the pair of
+// stereo physical port.
+// - 0: Microphone input 0/1
+// - 1: Line input 0/1
+// - [2-4]: Line output 0-5
+// - 5: Headphone output 0/1
+// - 6: S/PDIF output 0/1
+// - [7-10]: ADAT output 0-7
+//
+// The value of signal level can be detected by mask of 0x00fffc00. For signal level of microphone
+// input:
+//
+// - 0: 0.0 dB
+// - 10: +10.0 dB
+// - 11: +11.0 dB
+// - 12: +12.0 dB
+// - ...
+// - 63: +63.0 dB:
+// - 64: +64.0 dB:
+// - 65: +65.0 dB:
+//
+// For signal level of line input:
+//
+// - 0: 0.0 dB
+// - 1: +0.5 dB
+// - 2: +1.0 dB
+// - 3: +1.5 dB
+// - ...
+// - 34: +17.0 dB:
+// - 35: +17.5 dB:
+// - 36: +18.0 dB:
+//
+// For signal level of any type of output:
+//
+// - 63: -infinite
+// - 62: -58.0 dB
+// - 61: -56.0 dB
+// - 60: -54.0 dB
+// - 59: -53.0 dB
+// - 58: -52.0 dB
+// - ...
+// - 7: -1.0 dB
+// - 6: 0.0 dB
+// - 5: +1.0 dB
+// - ...
+// - 2: +4.0 dB
+// - 1: +5.0 dB
+// - 0: +6.0 dB
+//
+// When the message is not for signal level operation, it's for MIDI bytes. When matching to
+// FF400_MSG_FLAG_IS_MIDI_PORT_0, one MIDI byte can be detected by mask of 0x000000ff. When
+// matching to FF400_MSG_FLAG_IS_MIDI_PORT_1, one MIDI byte can be detected by mask of 0x00ff0000.
+#define FF400_MSG_FLAG_IS_SIGNAL_LEVEL 0x04000000
+#define FF400_MSG_FLAG_IS_RIGHT_CHANNEL 0x08000000
+#define FF400_MSG_FLAG_IS_STEREO_PAIRED 0x02000000
+#define FF400_MSG_MASK_STEREO_PAIR 0xf0000000
+#define FF400_MSG_MASK_SIGNAL_LEVEL 0x00fffc00
+#define FF400_MSG_FLAG_IS_MIDI_PORT_0 0x00000100
+#define FF400_MSG_MASK_MIDI_PORT_0 0x000000ff
+#define FF400_MSG_FLAG_IS_MIDI_PORT_1 0x01000000
+#define FF400_MSG_MASK_MIDI_PORT_1 0x00ff0000
+
+static void ff400_handle_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+ size_t length, u32 tstamp)
{
+ bool need_hwdep_wake_up = false;
int i;
for (i = 0; i < length / 4; i++) {
u32 quad = le32_to_cpu(buf[i]);
- u8 byte;
- unsigned int index;
- struct snd_rawmidi_substream *substream;
- /* Message in first port. */
- /*
- * This value may represent the index of this unit when the same
- * units are on the same IEEE 1394 bus. This driver doesn't use
- * it.
- */
- index = (quad >> 8) & 0xff;
- if (index > 0) {
- substream = READ_ONCE(ff->tx_midi_substreams[0]);
- if (substream != NULL) {
- byte = quad & 0xff;
- snd_rawmidi_receive(substream, &byte, 1);
- }
- }
+ if (quad & FF400_MSG_FLAG_IS_SIGNAL_LEVEL) {
+ struct ff400_msg_parser *parser = ff->msg_parser;
- /* Message in second port. */
- index = (quad >> 24) & 0xff;
- if (index > 0) {
- substream = READ_ONCE(ff->tx_midi_substreams[1]);
- if (substream != NULL) {
- byte = (quad >> 16) & 0xff;
- snd_rawmidi_receive(substream, &byte, 1);
- }
+ parser->msgs[parser->push_pos].msg = quad;
+ parser->msgs[parser->push_pos].tstamp = tstamp;
+ ++parser->push_pos;
+ if (parser->push_pos >= FF400_QUEUE_SIZE)
+ parser->push_pos = 0;
+
+ need_hwdep_wake_up = true;
+ } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_0) {
+ parse_midi_msg(ff, quad, 0);
+ } else if (quad & FF400_MSG_FLAG_IS_MIDI_PORT_1) {
+ parse_midi_msg(ff, quad, 1);
}
}
+
+ if (need_hwdep_wake_up)
+ wake_up(&ff->hwdep_wait);
+}
+
+static long ff400_copy_msg_to_user(struct snd_ff *ff, char __user *buf, long count)
+{
+ struct snd_firewire_event_ff400_message ev = {
+ .type = SNDRV_FIREWIRE_EVENT_FF400_MESSAGE,
+ .message_count = 0,
+ };
+ struct ff400_msg_parser *parser = ff->msg_parser;
+ long consumed = 0;
+ long ret = 0;
+
+ if (count < sizeof(ev) || parser->pull_pos == parser->push_pos)
+ return 0;
+
+ count -= sizeof(ev);
+ consumed += sizeof(ev);
+
+ while (count >= sizeof(*parser->msgs) && parser->pull_pos != parser->push_pos) {
+ spin_unlock_irq(&ff->lock);
+ if (copy_to_user(buf + consumed, parser->msgs + parser->pull_pos,
+ sizeof(*parser->msgs)))
+ ret = -EFAULT;
+ spin_lock_irq(&ff->lock);
+ if (ret)
+ return ret;
+
+ ++parser->pull_pos;
+ if (parser->pull_pos >= FF400_QUEUE_SIZE)
+ parser->pull_pos = 0;
+ ++ev.message_count;
+ count -= sizeof(*parser->msgs);
+ consumed += sizeof(*parser->msgs);
+ }
+
+ spin_unlock_irq(&ff->lock);
+ if (copy_to_user(buf, &ev, sizeof(ev)))
+ ret = -EFAULT;
+ spin_lock_irq(&ff->lock);
+ if (ret)
+ return ret;
+
+ return consumed;
}
const struct snd_ff_protocol snd_ff_protocol_ff400 = {
- .handle_midi_msg = ff400_handle_midi_msg,
+ .msg_parser_size = sizeof(struct ff400_msg_parser),
+ .has_msg = ff400_has_msg,
+ .copy_msg_to_user = ff400_copy_msg_to_user,
+ .handle_msg = ff400_handle_msg,
.fill_midi_msg = former_fill_midi_msg,
.get_clock = former_get_clock,
.switch_fetching_mode = former_switch_fetching_mode,
diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c
index 76c3eab36d4e07..9947e0c2e0aa83 100644
--- a/sound/firewire/fireface/ff-protocol-latter.c
+++ b/sound/firewire/fireface/ff-protocol-latter.c
@@ -393,8 +393,8 @@ static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer
// input attenuation. This driver allocates for the first option
// (0x'....'....'0000'0000) and expects userspace application to configure the
// register for it.
-static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset,
- __le32 *buf, size_t length)
+static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+ size_t length, u32 tstamp)
{
u32 data = le32_to_cpu(*buf);
unsigned int index = (data & 0x000000f0) >> 4;
@@ -529,7 +529,7 @@ static int latter_fill_midi_msg(struct snd_ff *ff,
}
const struct snd_ff_protocol snd_ff_protocol_latter = {
- .handle_midi_msg = latter_handle_midi_msg,
+ .handle_msg = latter_handle_midi_msg,
.fill_midi_msg = latter_fill_midi_msg,
.get_clock = latter_get_clock,
.switch_fetching_mode = latter_switch_fetching_mode,
diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c
index ee7122c461d46f..6b89e39f4a43fd 100644
--- a/sound/firewire/fireface/ff-transaction.c
+++ b/sound/firewire/fireface/ff-transaction.c
@@ -125,19 +125,22 @@ static void transmit_midi1_msg(struct work_struct *work)
transmit_midi_msg(ff, 1);
}
-static void handle_midi_msg(struct fw_card *card, struct fw_request *request,
- int tcode, int destination, int source,
- int generation, unsigned long long offset,
- void *data, size_t length, void *callback_data)
+static void handle_msg(struct fw_card *card, struct fw_request *request, int tcode,
+ int destination, int source, int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
{
struct snd_ff *ff = callback_data;
__le32 *buf = data;
+ u32 tstamp = fw_request_get_timestamp(request);
+ unsigned long flag;
fw_send_response(card, request, RCODE_COMPLETE);
offset -= ff->async_handler.offset;
- ff->spec->protocol->handle_midi_msg(ff, (unsigned int)offset, buf,
- length);
+
+ spin_lock_irqsave(&ff->lock, flag);
+ ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length, tstamp);
+ spin_unlock_irqrestore(&ff->lock, flag);
}
static int allocate_own_address(struct snd_ff *ff, int i)
@@ -146,7 +149,7 @@ static int allocate_own_address(struct snd_ff *ff, int i)
int err;
ff->async_handler.length = ff->spec->midi_addr_range;
- ff->async_handler.address_callback = handle_midi_msg;
+ ff->async_handler.address_callback = handle_msg;
ff->async_handler.callback_data = ff;
midi_msg_region.start = 0x000100000000ull * i;
diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c
index 7bf51d062021b2..448e972028d999 100644
--- a/sound/firewire/fireface/ff.c
+++ b/sound/firewire/fireface/ff.c
@@ -43,6 +43,8 @@ static void ff_card_free(struct snd_card *card)
snd_ff_stream_destroy_duplex(ff);
snd_ff_transaction_unregister(ff);
+ kfree(ff->msg_parser);
+
mutex_destroy(&ff->mutex);
fw_unit_put(ff->unit);
}
@@ -94,6 +96,14 @@ static int snd_ff_probe(struct fw_unit *unit, const struct ieee1394_device_id *e
if (err < 0)
goto error;
+ if (ff->spec->protocol->msg_parser_size > 0) {
+ ff->msg_parser = kzalloc(ff->spec->protocol->msg_parser_size, GFP_KERNEL);
+ if (!ff->msg_parser) {
+ err = -ENOMEM;
+ goto error;
+ }
+ }
+
err = snd_card_register(card);
if (err < 0)
goto error;
diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h
index 0535f0b58b672f..7e42f5778a8a37 100644
--- a/sound/firewire/fireface/ff.h
+++ b/sound/firewire/fireface/ff.h
@@ -97,6 +97,8 @@ struct snd_ff {
wait_queue_head_t hwdep_wait;
struct amdtp_domain domain;
+
+ void *msg_parser;
};
enum snd_ff_clock_src {
@@ -110,8 +112,11 @@ enum snd_ff_clock_src {
};
struct snd_ff_protocol {
- void (*handle_midi_msg)(struct snd_ff *ff, unsigned int offset,
- __le32 *buf, size_t length);
+ size_t msg_parser_size;
+ bool (*has_msg)(struct snd_ff *ff);
+ long (*copy_msg_to_user)(struct snd_ff *ff, char __user *buf, long count);
+ void (*handle_msg)(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
+ size_t length, u32 tstamp);
int (*fill_midi_msg)(struct snd_ff *ff,
struct snd_rawmidi_substream *substream,
unsigned int port);
diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c
index 2fb52f481d12d7..39ed57d2c5a0aa 100644
--- a/sound/firewire/motu/amdtp-motu.c
+++ b/sound/firewire/motu/amdtp-motu.c
@@ -73,7 +73,7 @@ int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
data_chunks = formats->msg_chunks + pcm_chunks;
data_block_quadlets = 1 + DIV_ROUND_UP(data_chunks * 3, 4);
- err = amdtp_stream_set_parameters(s, rate, data_block_quadlets);
+ err = amdtp_stream_set_parameters(s, rate, data_block_quadlets, 1);
if (err < 0)
return err;
@@ -284,19 +284,19 @@ static void __maybe_unused copy_message(u64 *frames, __be32 *buffer,
}
}
-static void probe_tracepoints_events(struct amdtp_stream *s,
- const struct pkt_desc *descs,
- unsigned int packets)
+static void probe_tracepoints_events(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count)
{
int i;
- for (i = 0; i < packets; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
trace_data_block_sph(s, data_blocks, buf);
trace_data_block_message(s, data_blocks, buf);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
}
}
@@ -328,13 +328,12 @@ static void cache_event_offsets(struct amdtp_motu_cache *cache, const __be32 *bu
cache->tx_cycle_count = (cache->tx_cycle_count + 1) % CYCLES_PER_SECOND;
}
-static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
- const struct pkt_desc *descs,
- unsigned int packets,
- struct snd_pcm_substream *pcm)
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
{
struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
struct amdtp_motu *p = s->protocol;
+ const struct pkt_desc *cursor = desc;
unsigned int pcm_frames = 0;
int i;
@@ -342,8 +341,7 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
p->cache->tx_cycle_count = (s->domain->processing_cycle.tx_start % CYCLES_PER_SECOND);
// For data block processing.
- for (i = 0; i < packets; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
@@ -356,22 +354,20 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
if (p->midi_ports)
read_midi_messages(s, buf, data_blocks);
- }
- if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP) {
- snd_motu_register_dsp_message_parser_parse(motu, descs, packets,
- s->data_block_quadlets);
- } else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP) {
- snd_motu_command_dsp_message_parser_parse(motu, descs, packets,
- s->data_block_quadlets);
+ desc = amdtp_stream_next_packet_desc(s, desc);
}
+ desc = cursor;
+ if (motu->spec->flags & SND_MOTU_SPEC_REGISTER_DSP)
+ snd_motu_register_dsp_message_parser_parse(s, desc, count);
+ else if (motu->spec->flags & SND_MOTU_SPEC_COMMAND_DSP)
+ snd_motu_command_dsp_message_parser_parse(s, desc, count);
+
// For tracepoints.
if (trace_data_block_sph_enabled() ||
trace_data_block_message_enabled())
- probe_tracepoints_events(s, descs, packets);
-
- return pcm_frames;
+ probe_tracepoints_events(s, desc, count);
}
static void write_sph(struct amdtp_motu_cache *cache, __be32 *buffer, unsigned int data_blocks,
@@ -396,12 +392,11 @@ static void write_sph(struct amdtp_motu_cache *cache, __be32 *buffer, unsigned i
cache->rx_cycle_count = (cache->rx_cycle_count + 1) % CYCLES_PER_SECOND;
}
-static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
- const struct pkt_desc *descs,
- unsigned int packets,
- struct snd_pcm_substream *pcm)
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
{
struct amdtp_motu *p = s->protocol;
+ const struct pkt_desc *cursor = desc;
unsigned int pcm_frames = 0;
int i;
@@ -409,8 +404,7 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
p->cache->rx_cycle_count = (s->domain->processing_cycle.rx_start % CYCLES_PER_SECOND);
// For data block processing.
- for (i = 0; i < packets; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
@@ -425,14 +419,16 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
write_midi_messages(s, buf, data_blocks);
write_sph(p->cache, buf, data_blocks, s->data_block_quadlets);
+
+ desc = amdtp_stream_next_packet_desc(s, desc);
}
+ desc = cursor;
+
// For tracepoints.
if (trace_data_block_sph_enabled() ||
trace_data_block_message_enabled())
- probe_tracepoints_events(s, descs, packets);
-
- return pcm_frames;
+ probe_tracepoints_events(s, desc, count);
}
int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
diff --git a/sound/firewire/motu/motu-command-dsp-message-parser.c b/sound/firewire/motu/motu-command-dsp-message-parser.c
index 9efe4d364baf5c..5d8a86a12f1f45 100644
--- a/sound/firewire/motu/motu-command-dsp-message-parser.c
+++ b/sound/firewire/motu/motu-command-dsp-message-parser.c
@@ -80,9 +80,11 @@ int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc
#define FRAGMENTS_PER_VALUE 4
#define VALUES_AT_IMAGE_END 0xffffffffffffffff
-void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs,
- unsigned int desc_count, unsigned int data_block_quadlets)
+void snd_motu_command_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *desc, unsigned int count)
{
+ struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
+ unsigned int data_block_quadlets = s->data_block_quadlets;
struct msg_parser *parser = motu->message_parser;
unsigned int interval = parser->interval;
unsigned long flags;
@@ -90,12 +92,13 @@ void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const stru
spin_lock_irqsave(&parser->lock, flags);
- for (i = 0; i < desc_count; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__be32 *buffer = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
int j;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+
for (j = 0; j < data_blocks; ++j) {
u8 *b = (u8 *)buffer;
buffer += data_block_quadlets;
diff --git a/sound/firewire/motu/motu-register-dsp-message-parser.c b/sound/firewire/motu/motu-register-dsp-message-parser.c
index 0c587567540f22..ef3b0b0f0dab9a 100644
--- a/sound/firewire/motu/motu-register-dsp-message-parser.c
+++ b/sound/firewire/motu/motu-register-dsp-message-parser.c
@@ -142,9 +142,11 @@ static void queue_event(struct snd_motu *motu, u8 msg_type, u8 identifier0, u8 i
parser->push_pos = pos;
}
-void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs,
- unsigned int desc_count, unsigned int data_block_quadlets)
+void snd_motu_register_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *desc, unsigned int count)
{
+ struct snd_motu *motu = container_of(s, struct snd_motu, tx_stream);
+ unsigned int data_block_quadlets = s->data_block_quadlets;
struct msg_parser *parser = motu->message_parser;
bool meter_pos_quirk = parser->meter_pos_quirk;
unsigned int pos = parser->push_pos;
@@ -153,12 +155,13 @@ void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const str
spin_lock_irqsave(&parser->lock, flags);
- for (i = 0; i < desc_count; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__be32 *buffer = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
int j;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+
for (j = 0; j < data_blocks; ++j) {
u8 *b = (u8 *)buffer;
u8 msg_type = (b[MSG_FLAG_POS] & MSG_FLAG_TYPE_MASK) >> MSG_FLAG_TYPE_SHIFT;
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
index 4189f2192284ab..3b1dc98a7be023 100644
--- a/sound/firewire/motu/motu.h
+++ b/sound/firewire/motu/motu.h
@@ -279,8 +279,8 @@ static inline int snd_motu_protocol_cache_packet_formats(struct snd_motu *motu)
int snd_motu_register_dsp_message_parser_new(struct snd_motu *motu);
int snd_motu_register_dsp_message_parser_init(struct snd_motu *motu);
-void snd_motu_register_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs,
- unsigned int desc_count, unsigned int data_block_quadlets);
+void snd_motu_register_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *descs, unsigned int count);
void snd_motu_register_dsp_message_parser_copy_meter(struct snd_motu *motu,
struct snd_firewire_motu_register_dsp_meter *meter);
void snd_motu_register_dsp_message_parser_copy_parameter(struct snd_motu *motu,
@@ -290,8 +290,8 @@ bool snd_motu_register_dsp_message_parser_copy_event(struct snd_motu *motu, u32
int snd_motu_command_dsp_message_parser_new(struct snd_motu *motu);
int snd_motu_command_dsp_message_parser_init(struct snd_motu *motu, enum cip_sfc sfc);
-void snd_motu_command_dsp_message_parser_parse(struct snd_motu *motu, const struct pkt_desc *descs,
- unsigned int desc_count, unsigned int data_block_quadlets);
+void snd_motu_command_dsp_message_parser_parse(const struct amdtp_stream *s,
+ const struct pkt_desc *descs, unsigned int count);
void snd_motu_command_dsp_message_parser_copy_meter(struct snd_motu *motu,
struct snd_firewire_motu_command_dsp_meter *meter);
diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c
index 64d66a8025455f..0b42d65590081a 100644
--- a/sound/firewire/tascam/amdtp-tascam.c
+++ b/sound/firewire/tascam/amdtp-tascam.c
@@ -29,7 +29,7 @@ int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)
if (s->direction == AMDTP_IN_STREAM)
data_channels += 2;
- return amdtp_stream_set_parameters(s, rate, data_channels);
+ return amdtp_stream_set_parameters(s, rate, data_channels, 1);
}
static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
@@ -176,16 +176,13 @@ static void read_status_messages(struct amdtp_stream *s,
}
}
-static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
- const struct pkt_desc *descs,
- unsigned int packets,
- struct snd_pcm_substream *pcm)
+static void process_ir_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
{
unsigned int pcm_frames = 0;
int i;
- for (i = 0; i < packets; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
@@ -195,21 +192,18 @@ static unsigned int process_ir_ctx_payloads(struct amdtp_stream *s,
}
read_status_messages(s, buf, data_blocks);
- }
- return pcm_frames;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
}
-static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
- const struct pkt_desc *descs,
- unsigned int packets,
- struct snd_pcm_substream *pcm)
+static void process_it_ctx_payloads(struct amdtp_stream *s, const struct pkt_desc *desc,
+ unsigned int count, struct snd_pcm_substream *pcm)
{
unsigned int pcm_frames = 0;
int i;
- for (i = 0; i < packets; ++i) {
- const struct pkt_desc *desc = descs + i;
+ for (i = 0; i < count; ++i) {
__be32 *buf = desc->ctx_payload;
unsigned int data_blocks = desc->data_blocks;
@@ -219,9 +213,9 @@ static unsigned int process_it_ctx_payloads(struct amdtp_stream *s,
} else {
write_pcm_silence(s, buf, data_blocks);
}
- }
- return pcm_frames;
+ desc = amdtp_stream_next_packet_desc(s, desc);
+ }
}
int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
index 547adbc22590e8..1f56fd33b9afb4 100644
--- a/sound/hda/hdac_stream.c
+++ b/sound/hda/hdac_stream.c
@@ -124,11 +124,10 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_init);
/**
* snd_hdac_stream_start - start a stream
* @azx_dev: HD-audio core stream to start
- * @fresh_start: false = wallclock timestamp relative to period wallclock
*
* Start a stream, set start_wallclk and set the running flag.
*/
-void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start)
+void snd_hdac_stream_start(struct hdac_stream *azx_dev)
{
struct hdac_bus *bus = azx_dev->bus;
int stripe_ctl;
@@ -136,8 +135,6 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start)
trace_snd_hdac_stream_start(bus, azx_dev);
azx_dev->start_wallclk = snd_hdac_chip_readl(bus, WALLCLK);
- if (!fresh_start)
- azx_dev->start_wallclk -= azx_dev->period_wallclk;
/* enable SIE */
snd_hdac_chip_updatel(bus, INTCTL,
@@ -966,7 +963,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_dsp_prepare);
void snd_hdac_dsp_trigger(struct hdac_stream *azx_dev, bool start)
{
if (start)
- snd_hdac_stream_start(azx_dev, true);
+ snd_hdac_stream_start(azx_dev);
else
snd_hdac_stream_stop(azx_dev);
}
diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c
index 62a9615dcf529a..60b0a70428d568 100644
--- a/sound/hda/hdac_sysfs.c
+++ b/sound/hda/hdac_sysfs.c
@@ -148,7 +148,7 @@ static void widget_release(struct kobject *kobj)
kfree(kobj);
}
-static struct kobj_type widget_ktype = {
+static const struct kobj_type widget_ktype = {
.release = widget_release,
.sysfs_ops = &widget_sysfs_ops,
};
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 06d304db4183c6..886255a03e8b45 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -302,6 +302,20 @@ config SND_HDA_INTEL_HDMI_SILENT_STREAM
This feature can impact power consumption as resources
are kept reserved both at transmitter and receiver.
+config SND_HDA_CTL_DEV_ID
+ bool "Use the device identifier field for controls"
+ depends on SND_HDA_INTEL
+ help
+ Say Y to use the device identifier field for (mixer)
+ controls (old behaviour until this option is available).
+
+ When enabled, the multiple HDA codecs may set the device
+ field in control (mixer) element identifiers. The use
+ of this field is not recommended and defined for mixer controls.
+
+ The old behaviour (Y) is obsolete and will be removed. Consider
+ to not enable this option.
+
endif
endmenu
diff --git a/sound/pci/hda/cs35l41_hda.c b/sound/pci/hda/cs35l41_hda.c
index f7815ee24f836c..75020edd39e75f 100644
--- a/sound/pci/hda/cs35l41_hda.c
+++ b/sound/pci/hda/cs35l41_hda.c
@@ -58,7 +58,7 @@ static const struct reg_sequence cs35l41_hda_config[] = {
{ CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON
{ CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON
{ CS35L41_DSP1_RX5_SRC, 0x00000020 }, // DSP1RX5 SRC = ERRVOL
- { CS35L41_AMP_DIG_VOL_CTRL, 0x00000000 }, // AMP_VOL_PCM 0.0 dB
+ { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB
{ CS35L41_AMP_GAIN_CTRL, 0x00000084 }, // AMP_GAIN_PCM 4.5 dB
};
@@ -82,13 +82,13 @@ static const struct reg_sequence cs35l41_hda_config_dsp[] = {
{ CS35L41_DSP1_RX3_SRC, 0x00000018 }, // DSP1RX3 SRC = VMON
{ CS35L41_DSP1_RX4_SRC, 0x00000019 }, // DSP1RX4 SRC = IMON
{ CS35L41_DSP1_RX5_SRC, 0x00000029 }, // DSP1RX5 SRC = VBSTMON
- { CS35L41_AMP_DIG_VOL_CTRL, 0x00000000 }, // AMP_VOL_PCM 0.0 dB
+ { CS35L41_AMP_DIG_VOL_CTRL, 0x00008000 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM 0.0 dB
{ CS35L41_AMP_GAIN_CTRL, 0x00000233 }, // AMP_GAIN_PCM = 17.5dB AMP_GAIN_PDM = 19.5dB
};
static const struct reg_sequence cs35l41_hda_mute[] = {
{ CS35L41_AMP_GAIN_CTRL, 0x00000000 }, // AMP_GAIN_PCM 0.5 dB
- { CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_VOL_PCM Mute
+ { CS35L41_AMP_DIG_VOL_CTRL, 0x0000A678 }, // AMP_HPF_PCM_EN = 1, AMP_VOL_PCM Mute
};
static void cs35l41_add_controls(struct cs35l41_hda *cs35l41)
@@ -178,11 +178,10 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
cs35l41->speaker_id, "wmfw");
if (!ret) {
/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
- cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT,
- cs35l41->acpi_subsystem_id, cs35l41->amp_name,
- cs35l41->speaker_id, "bin");
- return 0;
+ return cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id, cs35l41->amp_name,
+ cs35l41->speaker_id, "bin");
}
/* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */
@@ -191,10 +190,10 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
cs35l41->amp_name, -1, "wmfw");
if (!ret) {
/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
- cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
- cs35l41->amp_name, cs35l41->speaker_id, "bin");
- return 0;
+ return cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id, cs35l41->amp_name,
+ cs35l41->speaker_id, "bin");
}
/* try cirrus/part-dspN-fwtype-sub<-spkidN>.wmfw */
@@ -209,11 +208,10 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
cs35l41->amp_name, cs35l41->speaker_id, "bin");
if (ret)
/* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
- cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT,
- cs35l41->acpi_subsystem_id,
- NULL, cs35l41->speaker_id, "bin");
- return 0;
+ return cs35l41_request_firmware_file(cs35l41, coeff_firmware,
+ coeff_filename, CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id, NULL,
+ cs35l41->speaker_id, "bin");
}
/* try cirrus/part-dspN-fwtype-sub.wmfw */
@@ -224,29 +222,16 @@ static int cs35l41_request_firmware_files_spkid(struct cs35l41_hda *cs35l41,
/* try cirrus/part-dspN-fwtype-sub<-spkidN><-ampname>.bin */
ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
CS35L41_FIRMWARE_ROOT,
- cs35l41->acpi_subsystem_id,
- cs35l41->amp_name, cs35l41->speaker_id, "bin");
+ cs35l41->acpi_subsystem_id, cs35l41->amp_name,
+ cs35l41->speaker_id, "bin");
if (ret)
/* try cirrus/part-dspN-fwtype-sub<-spkidN>.bin */
- cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT,
- cs35l41->acpi_subsystem_id,
- NULL, cs35l41->speaker_id, "bin");
- return 0;
- }
-
- /* fallback try cirrus/part-dspN-fwtype.wmfw */
- ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
- CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw");
- if (!ret) {
- /* fallback try cirrus/part-dspN-fwtype.bin */
- cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin");
- return 0;
+ return cs35l41_request_firmware_file(cs35l41, coeff_firmware,
+ coeff_filename, CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id, NULL,
+ cs35l41->speaker_id, "bin");
}
- dev_warn(cs35l41->dev, "Failed to request firmware\n");
-
return ret;
}
@@ -258,9 +243,12 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41,
{
int ret;
- if (cs35l41->speaker_id > -1)
- return cs35l41_request_firmware_files_spkid(cs35l41, wmfw_firmware, wmfw_filename,
- coeff_firmware, coeff_filename);
+ if (cs35l41->speaker_id > -1) {
+ ret = cs35l41_request_firmware_files_spkid(cs35l41, wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename);
+ goto out;
+
+ }
/* try cirrus/part-dspN-fwtype-sub<-ampname>.wmfw */
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
@@ -268,10 +256,11 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41,
cs35l41->amp_name, -1, "wmfw");
if (!ret) {
/* try cirrus/part-dspN-fwtype-sub<-ampname>.bin */
- cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT, cs35l41->acpi_subsystem_id,
- cs35l41->amp_name, -1, "bin");
- return 0;
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id, cs35l41->amp_name,
+ -1, "bin");
+ goto out;
}
/* try cirrus/part-dspN-fwtype-sub.wmfw */
@@ -286,25 +275,35 @@ static int cs35l41_request_firmware_files(struct cs35l41_hda *cs35l41,
cs35l41->amp_name, -1, "bin");
if (ret)
/* try cirrus/part-dspN-fwtype-sub.bin */
- cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT,
- cs35l41->acpi_subsystem_id,
- NULL, -1, "bin");
- return 0;
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ CS35L41_FIRMWARE_ROOT,
+ cs35l41->acpi_subsystem_id, NULL, -1,
+ "bin");
}
+out:
+ if (!ret)
+ return 0;
+
+ /* Handle fallback */
+ dev_warn(cs35l41->dev, "Falling back to default firmware.\n");
+
+ release_firmware(*wmfw_firmware);
+ kfree(*wmfw_filename);
+
/* fallback try cirrus/part-dspN-fwtype.wmfw */
ret = cs35l41_request_firmware_file(cs35l41, wmfw_firmware, wmfw_filename,
CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "wmfw");
- if (!ret) {
+ if (!ret)
/* fallback try cirrus/part-dspN-fwtype.bin */
- cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
- CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin");
- return 0;
- }
-
- dev_warn(cs35l41->dev, "Failed to request firmware\n");
+ ret = cs35l41_request_firmware_file(cs35l41, coeff_firmware, coeff_filename,
+ CS35L41_FIRMWARE_ROOT, NULL, NULL, -1, "bin");
+ if (ret) {
+ release_firmware(*wmfw_firmware);
+ kfree(*wmfw_filename);
+ dev_warn(cs35l41->dev, "Unable to find firmware and tuning\n");
+ }
return ret;
}
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 2e728aad677136..9f79c0ac2bda71 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -3389,7 +3389,12 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
kctl = snd_ctl_new1(knew, codec);
if (!kctl)
return -ENOMEM;
- if (addr > 0)
+ /* Do not use the id.device field for MIXER elements.
+ * This field is for real device numbers (like PCM) but codecs
+ * are hidden components from the user space view (unrelated
+ * to the mixer element identification).
+ */
+ if (addr > 0 && codec->ctl_dev_id)
kctl->id.device = addr;
if (idx > 0)
kctl->id.index = idx;
@@ -3400,9 +3405,11 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
* the codec addr; if it still fails (or it's the
* primary codec), then try another control index
*/
- if (!addr && codec->core.addr)
+ if (!addr && codec->core.addr) {
addr = codec->core.addr;
- else if (!idx && !knew->index) {
+ if (!codec->ctl_dev_id)
+ idx += 10 * addr;
+ } else if (!idx && !knew->index) {
idx = find_empty_mixer_ctl_idx(codec,
knew->name, 0);
if (idx <= 0)
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index 0ff286b7b66bee..406779625fb593 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -257,7 +257,7 @@ static int azx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
azx_dev = get_azx_dev(s);
if (start) {
azx_dev->insufficient = 1;
- snd_hdac_stream_start(azx_stream(azx_dev), true);
+ snd_hdac_stream_start(azx_stream(azx_dev));
} else {
snd_hdac_stream_stop(azx_stream(azx_dev));
}
@@ -1231,6 +1231,7 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots)
continue;
codec->jackpoll_interval = chip->jackpoll_interval;
codec->beep_mode = chip->beep_mode;
+ codec->ctl_dev_id = chip->ctl_dev_id;
codecs++;
}
}
diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h
index f5bf295eb83078..8556031bcd68e4 100644
--- a/sound/pci/hda/hda_controller.h
+++ b/sound/pci/hda/hda_controller.h
@@ -124,6 +124,7 @@ struct azx {
/* HD codec */
int codec_probe_mask; /* copied from probe_mask option */
unsigned int beep_mode;
+ bool ctl_dev_id;
#ifdef CONFIG_SND_HDA_PATCH_LOADER
const struct firmware *fw;
diff --git a/sound/pci/hda/hda_cs_dsp_ctl.c b/sound/pci/hda/hda_cs_dsp_ctl.c
index 5433f6227ac9f8..463ca06036bfe7 100644
--- a/sound/pci/hda/hda_cs_dsp_ctl.c
+++ b/sound/pci/hda/hda_cs_dsp_ctl.c
@@ -218,10 +218,10 @@ int hda_cs_dsp_write_ctl(struct cs_dsp *dsp, const char *name, int type,
cs_ctl = cs_dsp_get_ctl(dsp, name, type, alg);
ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len);
mutex_unlock(&dsp->pwr_lock);
- if (ret)
+ if (ret < 0)
return ret;
- if (cs_ctl->flags & WMFW_CTL_FLAG_SYS)
+ if (ret == 0 || (cs_ctl->flags & WMFW_CTL_FLAG_SYS))
return 0;
ctl = cs_ctl->priv;
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 87002670c0c925..81c4a45254ff2e 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -50,6 +50,7 @@
#include <sound/intel-dsp-config.h>
#include <linux/vgaarb.h>
#include <linux/vga_switcheroo.h>
+#include <linux/apple-gmux.h>
#include <linux/firmware.h>
#include <sound/hda_codec.h>
#include "hda_controller.h"
@@ -119,6 +120,7 @@ static bool beep_mode[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] =
CONFIG_SND_HDA_INPUT_BEEP_MODE};
#endif
static bool dmic_detect = 1;
+static bool ctl_dev_id = IS_ENABLED(CONFIG_SND_HDA_CTL_DEV_ID) ? 1 : 0;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for Intel HD audio interface.");
@@ -157,6 +159,8 @@ module_param(dmic_detect, bool, 0444);
MODULE_PARM_DESC(dmic_detect, "Allow DSP driver selection (bypass this driver) "
"(0=off, 1=on) (default=1); "
"deprecated, use snd-intel-dspcfg.dsp_driver option instead");
+module_param(ctl_dev_id, bool, 0444);
+MODULE_PARM_DESC(ctl_dev_id, "Use control device identifier (based on codec address).");
#ifdef CONFIG_PM
static int param_set_xint(const char *val, const struct kernel_param *kp);
@@ -1463,7 +1467,7 @@ static struct pci_dev *get_bound_vga(struct pci_dev *pci)
* vgaswitcheroo.
*/
if (((p->class >> 16) == PCI_BASE_CLASS_DISPLAY) &&
- atpx_present())
+ (atpx_present() || apple_gmux_detect(NULL, NULL)))
return p;
pci_dev_put(p);
}
@@ -2278,6 +2282,8 @@ static int azx_probe_continue(struct azx *chip)
chip->beep_mode = beep_mode[dev];
#endif
+ chip->ctl_dev_id = ctl_dev_id;
+
/* create codec instances */
if (bus->codec_mask) {
err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]);
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c
index 976a112c7d0061..c2bf8678189465 100644
--- a/sound/pci/hda/hda_tegra.c
+++ b/sound/pci/hda/hda_tegra.c
@@ -582,12 +582,10 @@ static void hda_tegra_probe_work(struct work_struct *work)
static int hda_tegra_remove(struct platform_device *pdev)
{
- int ret;
-
- ret = snd_card_free(dev_get_drvdata(&pdev->dev));
+ snd_card_free(dev_get_drvdata(&pdev->dev));
pm_runtime_disable(&pdev->dev);
- return ret;
+ return 0;
}
static void hda_tegra_shutdown(struct platform_device *pdev)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 0a292bf271f2e5..acde4cd58785e0 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -2455,7 +2455,7 @@ static int dspio_set_uint_param(struct hda_codec *codec, int mod_id,
static int dspio_alloc_dma_chan(struct hda_codec *codec, unsigned int *dma_chan)
{
int status = 0;
- unsigned int size = sizeof(dma_chan);
+ unsigned int size = sizeof(*dma_chan);
codec_dbg(codec, " dspio_alloc_dma_chan() -- begin\n");
status = dspio_scp(codec, MASTERCONTROL, 0x20,
diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c
index 631a61ce52f4e3..a6cff2c46ac7e7 100644
--- a/sound/ppc/snd_ps3.c
+++ b/sound/ppc/snd_ps3.c
@@ -1046,16 +1046,13 @@ clean_open:
/* called when module removal */
static void snd_ps3_driver_remove(struct ps3_system_bus_device *dev)
{
- int ret;
pr_info("%s:start id=%d\n", __func__, dev->match_id);
/*
* ctl and preallocate buffer will be freed in
* snd_card_free
*/
- ret = snd_card_free(the_card.card);
- if (ret)
- pr_info("%s: ctl freecard=%d\n", __func__, ret);
+ snd_card_free(the_card.card);
dma_free_coherent(&dev->core,
PAGE_SIZE,
diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c
index 1f4878ff7d3721..676ad50638d0d0 100644
--- a/sound/soc/amd/acp/acp-legacy-mach.c
+++ b/sound/soc/amd/acp/acp-legacy-mach.c
@@ -16,6 +16,7 @@
#include <sound/pcm_params.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
+#include <linux/dmi.h>
#include <linux/module.h>
#include "acp-mach.h"
@@ -27,6 +28,7 @@ static struct acp_card_drvdata rt5682_rt1019_data = {
.hs_codec_id = RT5682,
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
+ .tdm_mode = false,
};
static struct acp_card_drvdata rt5682s_max_data = {
@@ -36,6 +38,7 @@ static struct acp_card_drvdata rt5682s_max_data = {
.hs_codec_id = RT5682S,
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
+ .tdm_mode = false,
};
static struct acp_card_drvdata rt5682s_rt1019_data = {
@@ -45,6 +48,7 @@ static struct acp_card_drvdata rt5682s_rt1019_data = {
.hs_codec_id = RT5682S,
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
+ .tdm_mode = false,
};
static struct acp_card_drvdata max_nau8825_data = {
@@ -56,6 +60,7 @@ static struct acp_card_drvdata max_nau8825_data = {
.dmic_codec_id = DMIC,
.soc_mclk = true,
.platform = REMBRANDT,
+ .tdm_mode = false,
};
static struct acp_card_drvdata rt5682s_rt1019_rmb_data = {
@@ -67,6 +72,7 @@ static struct acp_card_drvdata rt5682s_rt1019_rmb_data = {
.dmic_codec_id = DMIC,
.soc_mclk = true,
.platform = REMBRANDT,
+ .tdm_mode = false,
};
static const struct snd_kcontrol_new acp_controls[] = {
@@ -90,6 +96,8 @@ static int acp_asoc_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = NULL;
struct device *dev = &pdev->dev;
+ const struct dmi_system_id *dmi_id;
+ struct acp_card_drvdata *acp_card_drvdata;
int ret;
if (!pdev->id_entry)
@@ -108,6 +116,11 @@ static int acp_asoc_probe(struct platform_device *pdev)
card->num_controls = ARRAY_SIZE(acp_controls);
card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
+ acp_card_drvdata = card->drvdata;
+ dmi_id = dmi_first_match(acp_quirk_table);
+ if (dmi_id && dmi_id->driver_data)
+ acp_card_drvdata->tdm_mode = dmi_id->driver_data;
+
acp_legacy_dai_links_create(card);
ret = devm_snd_soc_register_card(&pdev->dev, card);
diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c
index a78cf29387a779..b4dcce4fbae926 100644
--- a/sound/soc/amd/acp/acp-mach-common.c
+++ b/sound/soc/amd/acp/acp-mach-common.c
@@ -32,6 +32,20 @@
#define DUAL_CHANNEL 2
#define FOUR_CHANNEL 4
+#define TDM_MODE_ENABLE 1
+
+const struct dmi_system_id acp_quirk_table[] = {
+ {
+ /* Google skyrim proto-0 */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "Google_Skyrim"),
+ },
+ .driver_data = (void *)TDM_MODE_ENABLE,
+ },
+ {}
+};
+EXPORT_SYMBOL_GPL(acp_quirk_table);
+
static struct snd_soc_jack pco_jack;
static const unsigned int channels[] = {
@@ -54,10 +68,11 @@ static const struct snd_pcm_hw_constraint_list constraints_channels = {
.mask = 0,
};
-static int acp_clk_enable(struct acp_card_drvdata *drvdata)
+static int acp_clk_enable(struct acp_card_drvdata *drvdata,
+ unsigned int srate, unsigned int bclk_ratio)
{
- clk_set_rate(drvdata->wclk, 48000);
- clk_set_rate(drvdata->bclk, 48000 * 64);
+ clk_set_rate(drvdata->wclk, srate);
+ clk_set_rate(drvdata->bclk, srate * bclk_ratio);
return clk_prepare_enable(drvdata->wclk);
}
@@ -86,34 +101,6 @@ static int acp_card_rt5682_init(struct snd_soc_pcm_runtime *rtd)
if (drvdata->hs_codec_id != RT5682)
return -EINVAL;
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
- | SND_SOC_DAIFMT_CBP_CFP);
- if (ret < 0) {
- dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL2, RT5682_PLL2_S_MCLK,
- PCO_PLAT_CLK, RT5682_PLL_FREQ);
- if (ret < 0) {
- dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
- return ret;
- }
-
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL2,
- RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
- if (ret < 0) {
- dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
- return ret;
- }
-
- /* Set tdm/i2s1 master bclk ratio */
- ret = snd_soc_dai_set_bclk_ratio(codec_dai, 64);
- if (ret < 0) {
- dev_err(rtd->dev, "Failed to set rt5682 tdm bclk ratio: %d\n", ret);
- return ret;
- }
-
drvdata->wclk = clk_get(component->dev, "rt5682-dai-wclk");
drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk");
@@ -151,10 +138,15 @@ static int acp_card_hs_startup(struct snd_pcm_substream *substream)
int ret;
unsigned int fmt;
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
if (drvdata->soc_mclk)
- fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
else
- fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
ret = snd_soc_dai_set_fmt(codec_dai, fmt);
if (ret < 0) {
@@ -168,16 +160,6 @@ static int acp_card_hs_startup(struct snd_pcm_substream *substream)
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&constraints_rates);
- if (strcmp(codec_dai->name, "rt5682s-aif1") && strcmp(codec_dai->name, "rt5682s-aif2")) {
- if (!drvdata->soc_mclk) {
- ret = acp_clk_enable(drvdata);
- if (ret < 0) {
- dev_err(rtd->card->dev, "Failed to enable HS clk: %d\n", ret);
- return ret;
- }
- }
- }
-
return ret;
}
@@ -191,39 +173,36 @@ static void acp_card_shutdown(struct snd_pcm_substream *substream)
clk_disable_unprepare(drvdata->wclk);
}
-static const struct snd_soc_ops acp_card_rt5682_ops = {
- .startup = acp_card_hs_startup,
- .shutdown = acp_card_shutdown,
-};
-
-/* Define RT5682S CODEC component*/
-SND_SOC_DAILINK_DEF(rt5682s,
- DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RTL5682:00", "rt5682s-aif1")));
-
-static const struct snd_soc_dapm_route rt5682s_map[] = {
- { "Headphone Jack", NULL, "HPOL" },
- { "Headphone Jack", NULL, "HPOR" },
- { "IN1P", NULL, "Headset Mic" },
-};
-
-static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd)
+static int acp_card_rt5682_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_card *card = rtd->card;
struct acp_card_drvdata *drvdata = card->drvdata;
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
- struct snd_soc_component *component = codec_dai->component;
- unsigned int fmt;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
int ret;
+ unsigned int fmt, srate, ch, format;
- dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+ srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
- if (drvdata->hs_codec_id != RT5682S)
- return -EINVAL;
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
if (drvdata->soc_mclk)
- fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
else
- fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
ret = snd_soc_dai_set_fmt(codec_dai, fmt);
if (ret < 0) {
@@ -231,14 +210,31 @@ static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
- ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL2, RT5682S_PLL_S_MCLK,
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 0 and slot 1 for playback and capture.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 8, 16);
+ if (ret < 0) {
+ dev_warn(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682_PLL2, RT5682_PLL2_S_MCLK,
PCO_PLAT_CLK, RT5682_PLL_FREQ);
if (ret < 0) {
dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
return ret;
}
- ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL2,
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682_SCLK_S_PLL2,
RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
if (ret < 0) {
dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
@@ -246,13 +242,53 @@ static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd)
}
/* Set tdm/i2s1 master bclk ratio */
- ret = snd_soc_dai_set_bclk_ratio(codec_dai, 64);
+ ret = snd_soc_dai_set_bclk_ratio(codec_dai, ch * format);
if (ret < 0) {
dev_err(rtd->dev, "Failed to set rt5682 tdm bclk ratio: %d\n", ret);
return ret;
}
if (!drvdata->soc_mclk) {
+ ret = acp_clk_enable(drvdata, srate, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to enable HS clk: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops acp_card_rt5682_ops = {
+ .startup = acp_card_hs_startup,
+ .shutdown = acp_card_shutdown,
+ .hw_params = acp_card_rt5682_hw_params,
+};
+
+/* Define RT5682S CODEC component*/
+SND_SOC_DAILINK_DEF(rt5682s,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RTL5682:00", "rt5682s-aif1")));
+
+static const struct snd_soc_dapm_route rt5682s_map[] = {
+ { "Headphone Jack", NULL, "HPOL" },
+ { "Headphone Jack", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+};
+
+static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_component *component = codec_dai->component;
+ int ret;
+
+ dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
+
+ if (drvdata->hs_codec_id != RT5682S)
+ return -EINVAL;
+
+ if (!drvdata->soc_mclk) {
drvdata->wclk = clk_get(component->dev, "rt5682-dai-wclk");
drvdata->bclk = clk_get(component->dev, "rt5682-dai-bclk");
}
@@ -281,8 +317,90 @@ static int acp_card_rt5682s_init(struct snd_soc_pcm_runtime *rtd)
return snd_soc_dapm_add_routes(&rtd->card->dapm, rt5682s_map, ARRAY_SIZE(rt5682s_map));
}
+static int acp_card_rt5682s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ int ret;
+ unsigned int fmt, srate, ch, format;
+
+ srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 0 and slot 1 for playback and capture.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x3, 0x3, 8, 16);
+ if (ret < 0) {
+ dev_warn(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, RT5682S_PLL2, RT5682S_PLL_S_MCLK,
+ PCO_PLAT_CLK, RT5682_PLL_FREQ);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec PLL: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5682S_SCLK_S_PLL2,
+ RT5682_PLL_FREQ, SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set codec SYSCLK: %d\n", ret);
+ return ret;
+ }
+
+ /* Set tdm/i2s1 master bclk ratio */
+ ret = snd_soc_dai_set_bclk_ratio(codec_dai, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->dev, "Failed to set rt5682 tdm bclk ratio: %d\n", ret);
+ return ret;
+ }
+
+ clk_set_rate(drvdata->wclk, srate);
+ clk_set_rate(drvdata->bclk, srate * ch * format);
+
+ return 0;
+}
+
static const struct snd_soc_ops acp_card_rt5682s_ops = {
.startup = acp_card_hs_startup,
+ .hw_params = acp_card_rt5682s_hw_params,
};
static const unsigned int dmic_channels[] = {
@@ -351,19 +469,55 @@ static int acp_card_rt1019_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_card *card = rtd->card;
struct acp_card_drvdata *drvdata = card->drvdata;
struct snd_soc_dai *codec_dai;
- int srate, i, ret = 0;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ int i, ret = 0;
+ unsigned int fmt, srate, ch, format;
srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
if (drvdata->amp_codec_id != RT1019)
return -EINVAL;
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 2 and slot 3 for playback.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xC, 0, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+ }
+
for_each_rtd_codec_dais(rtd, i, codec_dai) {
if (strcmp(codec_dai->name, "rt1019-aif"))
continue;
- ret = snd_soc_dai_set_pll(codec_dai, 0, RT1019_PLL_S_BCLK,
- 64 * srate, 256 * srate);
+ if (drvdata->tdm_mode)
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1019_PLL_S_BCLK,
+ TDM_CHANNELS * format * srate, 256 * srate);
+ else
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT1019_PLL_S_BCLK,
+ ch * format * srate, 256 * srate);
+
if (ret < 0)
return ret;
@@ -371,6 +525,41 @@ static int acp_card_rt1019_hw_params(struct snd_pcm_substream *substream,
256 * srate, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
+
+ if (drvdata->tdm_mode) {
+ ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A
+ | SND_SOC_DAIFMT_NB_NF);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ /**
+ * As codec supports slot 2 for left channel playback.
+ */
+ if (!strcmp(codec_dai->component->name, "i2c-10EC1019:00")) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x4, 0x4, 8, 16);
+ if (ret < 0)
+ break;
+ }
+
+ /**
+ * As codec supports slot 3 for right channel playback.
+ */
+ if (!strcmp(codec_dai->component->name, "i2c-10EC1019:01")) {
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x8, 0x8, 8, 16);
+ if (ret < 0)
+ break;
+ }
+ }
+ }
+
+ if (!drvdata->soc_mclk) {
+ ret = acp_clk_enable(drvdata, srate, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to enable AMP clk: %d\n", ret);
+ return ret;
+ }
}
return 0;
@@ -379,10 +568,6 @@ static int acp_card_rt1019_hw_params(struct snd_pcm_substream *substream,
static int acp_card_amp_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_soc_card *card = rtd->card;
- struct acp_card_drvdata *drvdata = card->drvdata;
- int ret = 0;
runtime->hw.channels_max = DUAL_CHANNEL;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
@@ -390,14 +575,7 @@ static int acp_card_amp_startup(struct snd_pcm_substream *substream)
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
&constraints_rates);
- if (!drvdata->soc_mclk) {
- ret = acp_clk_enable(drvdata);
- if (ret < 0) {
- dev_err(rtd->card->dev, "Failed to enable AMP clk: %d\n", ret);
- return ret;
- }
- }
- return ret;
+ return 0;
}
static const struct snd_soc_ops acp_card_rt1019_ops = {
@@ -426,9 +604,61 @@ static int acp_card_maxim_init(struct snd_soc_pcm_runtime *rtd)
ARRAY_SIZE(max98360a_map));
}
+static int acp_card_maxim_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
+ unsigned int fmt, srate, ch, format;
+ int ret;
+
+ srate = params_rate(params);
+ ch = params_channels(params);
+ format = params_physical_width(params);
+
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 2 and slot 3 for playback.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xC, 0, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (!drvdata->soc_mclk) {
+ ret = acp_clk_enable(drvdata, srate, ch * format);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to enable AMP clk: %d\n", ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
static const struct snd_soc_ops acp_card_maxim_ops = {
.startup = acp_card_amp_startup,
.shutdown = acp_card_shutdown,
+ .hw_params = acp_card_maxim_hw_params,
};
/* Declare nau8825 codec components */
@@ -446,7 +676,6 @@ static int acp_card_nau8825_init(struct snd_soc_pcm_runtime *rtd)
struct acp_card_drvdata *drvdata = card->drvdata;
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
struct snd_soc_component *component = codec_dai->component;
- unsigned int fmt;
int ret;
dev_info(rtd->dev, "codec dai name = %s\n", codec_dai->name);
@@ -454,16 +683,6 @@ static int acp_card_nau8825_init(struct snd_soc_pcm_runtime *rtd)
if (drvdata->hs_codec_id != NAU8825)
return -EINVAL;
- if (drvdata->soc_mclk)
- fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
- else
- fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
-
- ret = snd_soc_dai_set_fmt(codec_dai, fmt);
- if (ret < 0) {
- dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
- return ret;
- }
ret = snd_soc_card_jack_new(card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_LINEOUT |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
@@ -492,8 +711,12 @@ static int acp_nau8825_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct snd_soc_card *card = rtd->card;
+ struct acp_card_drvdata *drvdata = card->drvdata;
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
int ret;
+ unsigned int fmt;
ret = snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS,
(48000 * 256), SND_SOC_CLOCK_IN);
@@ -507,6 +730,44 @@ static int acp_nau8825_hw_params(struct snd_pcm_substream *substream,
return ret;
}
+ if (drvdata->tdm_mode)
+ fmt = SND_SOC_DAIFMT_DSP_A;
+ else
+ fmt = SND_SOC_DAIFMT_I2S;
+
+ if (drvdata->soc_mclk)
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ else
+ fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+ if (ret < 0) {
+ dev_err(rtd->card->dev, "Failed to set dai fmt: %d\n", ret);
+ return ret;
+ }
+
+ if (drvdata->tdm_mode) {
+ /**
+ * As codec supports slot 4 and slot 5 for playback and slot 6 for capture.
+ */
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0x30, 0xC0, 8, 16);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "set TDM slot err: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x40, 0x30, 8, 16);
+ if (ret < 0) {
+ dev_warn(rtd->dev, "set TDM slot err:%d\n", ret);
+ return ret;
+ }
+ }
return ret;
}
@@ -565,8 +826,12 @@ SND_SOC_DAILINK_DEF(i2s_hs,
DAILINK_COMP_ARRAY(COMP_CPU("acp-i2s-hs")));
SND_SOC_DAILINK_DEF(sof_sp,
DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp")));
+SND_SOC_DAILINK_DEF(sof_sp_virtual,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp-virtual")));
SND_SOC_DAILINK_DEF(sof_hs,
DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs")));
+SND_SOC_DAILINK_DEF(sof_hs_virtual,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-hs-virtual")));
SND_SOC_DAILINK_DEF(sof_dmic,
DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-dmic")));
SND_SOC_DAILINK_DEF(pdm_dmic,
@@ -596,8 +861,6 @@ static int acp_rtk_set_bias_level(struct snd_soc_card *card,
switch (level) {
case SND_SOC_BIAS_STANDBY:
if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
- clk_set_rate(drvdata->wclk, 48000);
- clk_set_rate(drvdata->bclk, 48000 * 64);
/* Increase bclk's enable_count */
ret = clk_prepare_enable(drvdata->bclk);
@@ -701,8 +964,8 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
if (drv_data->amp_cpu_id == I2S_SP) {
links[i].name = "acp-amp-codec";
links[i].id = AMP_BE_ID;
- links[i].cpus = sof_sp;
- links[i].num_cpus = ARRAY_SIZE(sof_sp);
+ links[i].cpus = sof_sp_virtual;
+ links[i].num_cpus = ARRAY_SIZE(sof_sp_virtual);
links[i].platforms = sof_component;
links[i].num_platforms = ARRAY_SIZE(sof_component);
links[i].dpcm_playback = 1;
@@ -733,8 +996,8 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
if (drv_data->amp_cpu_id == I2S_HS) {
links[i].name = "acp-amp-codec";
links[i].id = AMP_BE_ID;
- links[i].cpus = sof_hs;
- links[i].num_cpus = ARRAY_SIZE(sof_hs);
+ links[i].cpus = sof_hs_virtual;
+ links[i].num_cpus = ARRAY_SIZE(sof_hs_virtual);
links[i].platforms = sof_component;
links[i].num_platforms = ARRAY_SIZE(sof_component);
links[i].dpcm_playback = 1;
diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h
index 20583ef902df7b..165f407697c009 100644
--- a/sound/soc/amd/acp/acp-mach.h
+++ b/sound/soc/amd/acp/acp-mach.h
@@ -18,6 +18,8 @@
#include <linux/module.h>
#include <sound/soc.h>
+#define TDM_CHANNELS 8
+
enum be_id {
HEADSET_BE_ID = 0,
AMP_BE_ID,
@@ -58,9 +60,11 @@ struct acp_card_drvdata {
struct clk *wclk;
struct clk *bclk;
bool soc_mclk;
+ bool tdm_mode;
};
int acp_sofdsp_dai_links_create(struct snd_soc_card *card);
int acp_legacy_dai_links_create(struct snd_soc_card *card);
+extern const struct dmi_system_id acp_quirk_table[];
#endif
diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c
index f19f064a752728..99a7d3879340d9 100644
--- a/sound/soc/amd/acp/acp-sof-mach.c
+++ b/sound/soc/amd/acp/acp-sof-mach.c
@@ -16,6 +16,7 @@
#include <sound/pcm_params.h>
#include <sound/soc-acpi.h>
#include <sound/soc-dapm.h>
+#include <linux/dmi.h>
#include <linux/module.h>
#include "acp-mach.h"
@@ -27,6 +28,7 @@ static struct acp_card_drvdata sof_rt5682_rt1019_data = {
.hs_codec_id = RT5682,
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
+ .tdm_mode = false,
};
static struct acp_card_drvdata sof_rt5682_max_data = {
@@ -36,6 +38,7 @@ static struct acp_card_drvdata sof_rt5682_max_data = {
.hs_codec_id = RT5682,
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
+ .tdm_mode = false,
};
static struct acp_card_drvdata sof_rt5682s_rt1019_data = {
@@ -45,6 +48,7 @@ static struct acp_card_drvdata sof_rt5682s_rt1019_data = {
.hs_codec_id = RT5682S,
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
+ .tdm_mode = false,
};
static struct acp_card_drvdata sof_rt5682s_max_data = {
@@ -54,6 +58,7 @@ static struct acp_card_drvdata sof_rt5682s_max_data = {
.hs_codec_id = RT5682S,
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
+ .tdm_mode = false,
};
static struct acp_card_drvdata sof_nau8825_data = {
@@ -64,6 +69,7 @@ static struct acp_card_drvdata sof_nau8825_data = {
.amp_codec_id = MAX98360A,
.dmic_codec_id = DMIC,
.soc_mclk = true,
+ .tdm_mode = false,
};
static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = {
@@ -74,6 +80,7 @@ static struct acp_card_drvdata sof_rt5682s_hs_rt1019_data = {
.amp_codec_id = RT1019,
.dmic_codec_id = DMIC,
.soc_mclk = true,
+ .tdm_mode = false,
};
static const struct snd_kcontrol_new acp_controls[] = {
@@ -96,6 +103,8 @@ static int acp_sof_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = NULL;
struct device *dev = &pdev->dev;
+ const struct dmi_system_id *dmi_id;
+ struct acp_card_drvdata *acp_card_drvdata;
int ret;
if (!pdev->id_entry)
@@ -114,6 +123,11 @@ static int acp_sof_probe(struct platform_device *pdev)
card->num_controls = ARRAY_SIZE(acp_controls);
card->drvdata = (struct acp_card_drvdata *)pdev->id_entry->driver_data;
+ acp_card_drvdata = card->drvdata;
+ dmi_id = dmi_first_match(acp_quirk_table);
+ if (dmi_id && dmi_id->driver_data)
+ acp_card_drvdata->tdm_mode = dmi_id->driver_data;
+
acp_sofdsp_dai_links_create(card);
ret = devm_snd_soc_register_card(&pdev->dev, card);
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index 5e7f9c1c1b0e82..6bf29b520511de 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -11,7 +11,6 @@
#define ACP63_REG_START 0x1240000
#define ACP63_REG_END 0x1250200
#define ACP63_DEVS 3
-#define ACP63_PDM_MODE 1
#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
#define ACP_PGFSM_CNTL_POWER_ON_MASK 1
@@ -30,7 +29,7 @@
#define ACP_ERROR_STAT 29
#define PDM_DECIMATION_FACTOR 2
#define ACP_PDM_CLK_FREQ_MASK 7
-#define ACP_WOV_MISC_CTRL_MASK 0x10
+#define ACP_WOV_GAIN_CONTROL GENMASK(4, 3)
#define ACP_PDM_ENABLE 1
#define ACP_PDM_DISABLE 0
#define ACP_PDM_DMA_EN_STATUS 2
@@ -54,6 +53,11 @@
/* time in ms for runtime suspend delay */
#define ACP_SUSPEND_DELAY_MS 2000
+#define ACP63_DMIC_ADDR 2
+#define ACP63_PDM_MODE_DEVS 3
+#define ACP63_PDM_DEV_MASK 1
+#define ACP_DMIC_DEV 2
+
enum acp_config {
ACP_CONFIG_0 = 0,
ACP_CONFIG_1,
@@ -84,6 +88,7 @@ struct pdm_stream_instance {
struct pdm_dev_data {
u32 pdm_irq;
void __iomem *acp63_base;
+ struct mutex *acp_lock;
struct snd_pcm_substream *capture_stream;
};
@@ -100,6 +105,9 @@ static inline void acp63_writel(u32 val, void __iomem *base_addr)
struct acp63_dev_data {
void __iomem *acp63_base;
struct resource *res;
- bool acp63_audio_mode;
struct platform_device *pdev[ACP63_DEVS];
+ struct mutex acp_lock; /* protect shared registers */
+ u16 pdev_mask;
+ u16 pdev_count;
+ u16 pdm_dev_index;
};
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
index 489f01a2069995..e86f23d97584f6 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -116,6 +116,7 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
struct acp63_dev_data *adata;
struct pdm_dev_data *ps_pdm_data;
u32 val;
+ u16 pdev_index;
adata = dev_id;
if (!adata)
@@ -123,7 +124,8 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
val = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
if (val & BIT(PDM_DMA_STAT)) {
- ps_pdm_data = dev_get_drvdata(&adata->pdev[0]->dev);
+ pdev_index = adata->pdm_dev_index;
+ ps_pdm_data = dev_get_drvdata(&adata->pdev[pdev_index]->dev);
acp63_writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
if (ps_pdm_data->capture_stream)
snd_pcm_period_elapsed(ps_pdm_data->capture_stream);
@@ -132,17 +134,124 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
return IRQ_NONE;
}
+static void get_acp63_device_config(u32 config, struct pci_dev *pci,
+ struct acp63_dev_data *acp_data)
+{
+ struct acpi_device *dmic_dev;
+ const union acpi_object *obj;
+ bool is_dmic_dev = false;
+
+ dmic_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
+ if (dmic_dev) {
+ if (!acpi_dev_get_property(dmic_dev, "acp-audio-device-type",
+ ACPI_TYPE_INTEGER, &obj) &&
+ obj->integer.value == ACP_DMIC_DEV)
+ is_dmic_dev = true;
+ }
+
+ switch (config) {
+ case ACP_CONFIG_0:
+ case ACP_CONFIG_1:
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ case ACP_CONFIG_9:
+ case ACP_CONFIG_15:
+ dev_dbg(&pci->dev, "Audio Mode %d\n", config);
+ break;
+ default:
+ if (is_dmic_dev) {
+ acp_data->pdev_mask = ACP63_PDM_DEV_MASK;
+ acp_data->pdev_count = ACP63_PDM_MODE_DEVS;
+ }
+ break;
+ }
+}
+
+static void acp63_fill_platform_dev_info(struct platform_device_info *pdevinfo,
+ struct device *parent,
+ struct fwnode_handle *fw_node,
+ char *name, unsigned int id,
+ const struct resource *res,
+ unsigned int num_res,
+ const void *data,
+ size_t size_data)
+{
+ pdevinfo->name = name;
+ pdevinfo->id = id;
+ pdevinfo->parent = parent;
+ pdevinfo->num_res = num_res;
+ pdevinfo->res = res;
+ pdevinfo->data = data;
+ pdevinfo->size_data = size_data;
+ pdevinfo->fwnode = fw_node;
+}
+
+static int create_acp63_platform_devs(struct pci_dev *pci, struct acp63_dev_data *adata, u32 addr)
+{
+ struct platform_device_info pdevinfo[ACP63_DEVS];
+ struct device *parent;
+ int index;
+ int ret;
+
+ parent = &pci->dev;
+ dev_dbg(&pci->dev,
+ "%s pdev_mask:0x%x pdev_count:0x%x\n", __func__, adata->pdev_mask,
+ adata->pdev_count);
+ if (adata->pdev_mask) {
+ adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL);
+ if (!adata->res) {
+ ret = -ENOMEM;
+ goto de_init;
+ }
+ adata->res->flags = IORESOURCE_MEM;
+ adata->res->start = addr;
+ adata->res->end = addr + (ACP63_REG_END - ACP63_REG_START);
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ }
+
+ switch (adata->pdev_mask) {
+ case ACP63_PDM_DEV_MASK:
+ adata->pdm_dev_index = 0;
+ acp63_fill_platform_dev_info(&pdevinfo[0], parent, NULL, "acp_ps_pdm_dma",
+ 0, adata->res, 1, &adata->acp_lock,
+ sizeof(adata->acp_lock));
+ acp63_fill_platform_dev_info(&pdevinfo[1], parent, NULL, "dmic-codec",
+ 0, NULL, 0, NULL, 0);
+ acp63_fill_platform_dev_info(&pdevinfo[2], parent, NULL, "acp_ps_mach",
+ 0, NULL, 0, NULL, 0);
+ break;
+ default:
+ dev_dbg(&pci->dev, "No PDM devices found\n");
+ return 0;
+ }
+
+ for (index = 0; index < adata->pdev_count; index++) {
+ adata->pdev[index] = platform_device_register_full(&pdevinfo[index]);
+ if (IS_ERR(adata->pdev[index])) {
+ dev_err(&pci->dev,
+ "cannot register %s device\n", pdevinfo[index].name);
+ ret = PTR_ERR(adata->pdev[index]);
+ goto unregister_devs;
+ }
+ }
+ return 0;
+unregister_devs:
+ for (--index; index >= 0; index--)
+ platform_device_unregister(adata->pdev[index]);
+de_init:
+ if (acp63_deinit(adata->acp63_base, &pci->dev))
+ dev_err(&pci->dev, "ACP de-init failed\n");
+ return ret;
+}
+
static int snd_acp63_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
struct acp63_dev_data *adata;
- struct platform_device_info pdevinfo[ACP63_DEVS];
- int index, ret;
- int val = 0x00;
- struct acpi_device *adev;
- const union acpi_object *obj;
u32 addr;
- unsigned int irqflags;
+ u32 irqflags;
+ int val;
+ int ret;
irqflags = IRQF_SHARED;
/* Pink Sardine device check */
@@ -179,86 +288,28 @@ static int snd_acp63_probe(struct pci_dev *pci,
}
pci_set_master(pci);
pci_set_drvdata(pci, adata);
+ mutex_init(&adata->acp_lock);
ret = acp63_init(adata->acp63_base, &pci->dev);
if (ret)
goto release_regions;
+ ret = devm_request_irq(&pci->dev, pci->irq, acp63_irq_handler,
+ irqflags, "ACP_PCI_IRQ", adata);
+ if (ret) {
+ dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
+ goto de_init;
+ }
val = acp63_readl(adata->acp63_base + ACP_PIN_CONFIG);
- switch (val) {
- case ACP_CONFIG_0:
- case ACP_CONFIG_1:
- case ACP_CONFIG_2:
- case ACP_CONFIG_3:
- case ACP_CONFIG_9:
- case ACP_CONFIG_15:
- dev_info(&pci->dev, "Audio Mode %d\n", val);
- break;
- default:
-
- /* Checking DMIC hardware*/
- adev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), 0x02, 0);
-
- if (!adev)
- break;
-
- if (!acpi_dev_get_property(adev, "acp-audio-device-type",
- ACPI_TYPE_INTEGER, &obj) &&
- obj->integer.value == 2) {
- adata->res = devm_kzalloc(&pci->dev, sizeof(struct resource), GFP_KERNEL);
- if (!adata->res) {
- ret = -ENOMEM;
- goto de_init;
- }
-
- adata->res->name = "acp_iomem";
- adata->res->flags = IORESOURCE_MEM;
- adata->res->start = addr;
- adata->res->end = addr + (ACP63_REG_END - ACP63_REG_START);
- adata->acp63_audio_mode = ACP63_PDM_MODE;
-
- memset(&pdevinfo, 0, sizeof(pdevinfo));
- pdevinfo[0].name = "acp_ps_pdm_dma";
- pdevinfo[0].id = 0;
- pdevinfo[0].parent = &pci->dev;
- pdevinfo[0].num_res = 1;
- pdevinfo[0].res = adata->res;
-
- pdevinfo[1].name = "dmic-codec";
- pdevinfo[1].id = 0;
- pdevinfo[1].parent = &pci->dev;
-
- pdevinfo[2].name = "acp_ps_mach";
- pdevinfo[2].id = 0;
- pdevinfo[2].parent = &pci->dev;
-
- for (index = 0; index < ACP63_DEVS; index++) {
- adata->pdev[index] =
- platform_device_register_full(&pdevinfo[index]);
-
- if (IS_ERR(adata->pdev[index])) {
- dev_err(&pci->dev,
- "cannot register %s device\n",
- pdevinfo[index].name);
- ret = PTR_ERR(adata->pdev[index]);
- goto unregister_devs;
- }
- ret = devm_request_irq(&pci->dev, pci->irq, acp63_irq_handler,
- irqflags, "ACP_PCI_IRQ", adata);
- if (ret) {
- dev_err(&pci->dev, "ACP PCI IRQ request failed\n");
- goto unregister_devs;
- }
- }
- }
- break;
+ get_acp63_device_config(val, pci, adata);
+ ret = create_acp63_platform_devs(pci, adata, addr);
+ if (ret < 0) {
+ dev_err(&pci->dev, "ACP platform devices creation failed\n");
+ goto de_init;
}
pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(&pci->dev);
pm_runtime_put_noidle(&pci->dev);
pm_runtime_allow(&pci->dev);
return 0;
-unregister_devs:
- for (--index; index >= 0; index--)
- platform_device_unregister(adata->pdev[index]);
de_init:
if (acp63_deinit(adata->acp63_base, &pci->dev))
dev_err(&pci->dev, "ACP de-init failed\n");
@@ -305,10 +356,8 @@ static void snd_acp63_remove(struct pci_dev *pci)
int ret, index;
adata = pci_get_drvdata(pci);
- if (adata->acp63_audio_mode == ACP63_PDM_MODE) {
- for (index = 0; index < ACP63_DEVS; index++)
- platform_device_unregister(adata->pdev[index]);
- }
+ for (index = 0; index < adata->pdev_count; index++)
+ platform_device_unregister(adata->pdev[index]);
ret = acp63_deinit(adata->acp63_base, &pci->dev);
if (ret)
dev_err(&pci->dev, "ACP de-init failed\n");
diff --git a/sound/soc/amd/ps/ps-pdm-dma.c b/sound/soc/amd/ps/ps-pdm-dma.c
index eea71a9d2ef1a9..454dab062e4f57 100644
--- a/sound/soc/amd/ps/ps-pdm-dma.c
+++ b/sound/soc/amd/ps/ps-pdm-dma.c
@@ -7,6 +7,7 @@
#include <linux/platform_device.h>
#include <linux/module.h>
+#include <linux/bitfield.h>
#include <linux/err.h>
#include <linux/io.h>
#include <sound/pcm_params.h>
@@ -18,6 +19,10 @@
#define DRV_NAME "acp_ps_pdm_dma"
+static int pdm_gain = 3;
+module_param(pdm_gain, int, 0644);
+MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)");
+
static const struct snd_pcm_hardware acp63_pdm_hardware_capture = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -55,26 +60,31 @@ static void acp63_enable_pdm_clock(void __iomem *acp_base)
acp63_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
pdm_ctrl = acp63_readl(acp_base + ACP_WOV_MISC_CTRL);
- pdm_ctrl |= ACP_WOV_MISC_CTRL_MASK;
+ pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL;
+ pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3));
acp63_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
}
-static void acp63_enable_pdm_interrupts(void __iomem *acp_base)
+static void acp63_enable_pdm_interrupts(struct pdm_dev_data *adata)
{
u32 ext_int_ctrl;
- ext_int_ctrl = acp63_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ mutex_lock(adata->acp_lock);
+ ext_int_ctrl = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
ext_int_ctrl |= PDM_DMA_INTR_MASK;
- acp63_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ acp63_writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ mutex_unlock(adata->acp_lock);
}
-static void acp63_disable_pdm_interrupts(void __iomem *acp_base)
+static void acp63_disable_pdm_interrupts(struct pdm_dev_data *adata)
{
u32 ext_int_ctrl;
- ext_int_ctrl = acp63_readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
+ mutex_lock(adata->acp_lock);
+ ext_int_ctrl = acp63_readl(adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
ext_int_ctrl &= ~PDM_DMA_INTR_MASK;
- acp63_writel(ext_int_ctrl, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ acp63_writel(ext_int_ctrl, adata->acp63_base + ACP_EXTERNAL_INTR_CNTL);
+ mutex_unlock(adata->acp_lock);
}
static bool acp63_check_pdm_dma_status(void __iomem *acp_base)
@@ -196,7 +206,7 @@ static int acp63_pdm_dma_open(struct snd_soc_component *component,
return ret;
}
- acp63_enable_pdm_interrupts(adata->acp63_base);
+ acp63_enable_pdm_interrupts(adata);
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
adata->capture_stream = substream;
@@ -272,7 +282,7 @@ static int acp63_pdm_dma_close(struct snd_soc_component *component,
struct pdm_dev_data *adata = dev_get_drvdata(component->dev);
struct snd_pcm_runtime *runtime = substream->runtime;
- acp63_disable_pdm_interrupts(adata->acp63_base);
+ acp63_disable_pdm_interrupts(adata);
adata->capture_stream = NULL;
kfree(runtime->private_data);
return 0;
@@ -353,6 +363,10 @@ static int acp63_pdm_audio_probe(struct platform_device *pdev)
struct pdm_dev_data *adata;
int status;
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "platform_data not retrieved\n");
+ return -ENODEV;
+ }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
@@ -368,7 +382,7 @@ static int acp63_pdm_audio_probe(struct platform_device *pdev)
return -ENOMEM;
adata->capture_stream = NULL;
-
+ adata->acp_lock = pdev->dev.platform_data;
dev_set_drvdata(&pdev->dev, adata);
status = devm_snd_soc_register_component(&pdev->dev,
&acp63_pdm_component,
@@ -408,7 +422,7 @@ static int __maybe_unused acp63_pdm_resume(struct device *dev)
acp63_init_pdm_ring_buffer(PDM_MEM_WINDOW_START, buffer_len,
period_bytes, adata->acp63_base);
}
- acp63_enable_pdm_interrupts(adata->acp63_base);
+ acp63_enable_pdm_interrupts(adata);
return 0;
}
@@ -417,7 +431,7 @@ static int __maybe_unused acp63_pdm_suspend(struct device *dev)
struct pdm_dev_data *adata;
adata = dev_get_drvdata(dev);
- acp63_disable_pdm_interrupts(adata->acp63_base);
+ acp63_disable_pdm_interrupts(adata);
return 0;
}
@@ -426,7 +440,7 @@ static int __maybe_unused acp63_pdm_runtime_resume(struct device *dev)
struct pdm_dev_data *adata;
adata = dev_get_drvdata(dev);
- acp63_enable_pdm_interrupts(adata->acp63_base);
+ acp63_enable_pdm_interrupts(adata);
return 0;
}
diff --git a/sound/soc/amd/raven/acp3x-i2s.c b/sound/soc/amd/raven/acp3x-i2s.c
index aa38cef1776dae..4ba83689482a74 100644
--- a/sound/soc/amd/raven/acp3x-i2s.c
+++ b/sound/soc/amd/raven/acp3x-i2s.c
@@ -315,16 +315,8 @@ static int acp3x_dai_probe(struct platform_device *pdev)
return 0;
}
-static int acp3x_dai_remove(struct platform_device *pdev)
-{
- /* As we use devm_ memory alloc there is nothing TBD here */
-
- return 0;
-}
-
static struct platform_driver acp3x_dai_driver = {
.probe = acp3x_dai_probe,
- .remove = acp3x_dai_remove,
.driver = {
.name = "acp3x_i2s_playcap",
},
diff --git a/sound/soc/amd/renoir/acp3x-pdm-dma.c b/sound/soc/amd/renoir/acp3x-pdm-dma.c
index 7203c6488df0eb..0d8b693aecc9fd 100644
--- a/sound/soc/amd/renoir/acp3x-pdm-dma.c
+++ b/sound/soc/amd/renoir/acp3x-pdm-dma.c
@@ -6,6 +6,7 @@
#include <linux/platform_device.h>
#include <linux/module.h>
+#include <linux/bitfield.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/pm_runtime.h>
@@ -17,6 +18,10 @@
#define DRV_NAME "acp_rn_pdm_dma"
+static int pdm_gain = 3;
+module_param(pdm_gain, int, 0644);
+MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)");
+
static const struct snd_pcm_hardware acp_pdm_hardware_capture = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -80,7 +85,8 @@ static void enable_pdm_clock(void __iomem *acp_base)
rn_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
pdm_ctrl = rn_readl(acp_base + ACP_WOV_MISC_CTRL);
- pdm_ctrl |= ACP_WOV_MISC_CTRL_MASK;
+ pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL;
+ pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3));
rn_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
}
diff --git a/sound/soc/amd/renoir/rn_acp3x.h b/sound/soc/amd/renoir/rn_acp3x.h
index ca586603d72034..7d0f4e6a2834d7 100644
--- a/sound/soc/amd/renoir/rn_acp3x.h
+++ b/sound/soc/amd/renoir/rn_acp3x.h
@@ -34,7 +34,7 @@
#define ACP_ERROR_STAT 29
#define PDM_DECIMATION_FACTOR 0x2
#define ACP_PDM_CLK_FREQ_MASK 0x07
-#define ACP_WOV_MISC_CTRL_MASK 0x10
+#define ACP_WOV_GAIN_CONTROL GENMASK(4, 3)
#define ACP_PDM_ENABLE 0x01
#define ACP_PDM_DISABLE 0x00
#define ACP_PDM_DMA_EN_STATUS 0x02
diff --git a/sound/soc/amd/yc/acp6x-pdm-dma.c b/sound/soc/amd/yc/acp6x-pdm-dma.c
index acecd6a4ec4b16..294dd7fb43c9be 100644
--- a/sound/soc/amd/yc/acp6x-pdm-dma.c
+++ b/sound/soc/amd/yc/acp6x-pdm-dma.c
@@ -7,6 +7,7 @@
#include <linux/platform_device.h>
#include <linux/module.h>
+#include <linux/bitfield.h>
#include <linux/err.h>
#include <linux/io.h>
#include <sound/pcm_params.h>
@@ -18,6 +19,10 @@
#define DRV_NAME "acp_yc_pdm_dma"
+static int pdm_gain = 3;
+module_param(pdm_gain, int, 0644);
+MODULE_PARM_DESC(pdm_gain, "Gain control (0-3)");
+
static const struct snd_pcm_hardware acp6x_pdm_hardware_capture = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -55,7 +60,8 @@ static void acp6x_enable_pdm_clock(void __iomem *acp_base)
acp6x_writel(pdm_clk_enable, acp_base + ACP_WOV_CLK_CTRL);
pdm_ctrl = acp6x_readl(acp_base + ACP_WOV_MISC_CTRL);
- pdm_ctrl |= ACP_WOV_MISC_CTRL_MASK;
+ pdm_ctrl &= ~ACP_WOV_GAIN_CONTROL;
+ pdm_ctrl |= FIELD_PREP(ACP_WOV_GAIN_CONTROL, clamp(pdm_gain, 0, 3));
acp6x_writel(pdm_ctrl, acp_base + ACP_WOV_MISC_CTRL);
}
diff --git a/sound/soc/amd/yc/acp6x.h b/sound/soc/amd/yc/acp6x.h
index 74b596e6807ade..036207568c048f 100644
--- a/sound/soc/amd/yc/acp6x.h
+++ b/sound/soc/amd/yc/acp6x.h
@@ -31,7 +31,7 @@
#define ACP_ERROR_STAT 29
#define PDM_DECIMATION_FACTOR 2
#define ACP_PDM_CLK_FREQ_MASK 7
-#define ACP_WOV_MISC_CTRL_MASK 0x10
+#define ACP_WOV_GAIN_CONTROL GENMASK(4, 3)
#define ACP_PDM_ENABLE 1
#define ACP_PDM_DISABLE 0
#define ACP_PDM_DMA_EN_STATUS 2
diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c
index 87d6d6ed026b32..9883e6867fd12f 100644
--- a/sound/soc/atmel/atmel-classd.c
+++ b/sound/soc/atmel/atmel-classd.c
@@ -616,11 +616,6 @@ unregister_codec:
return ret;
}
-static int atmel_classd_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static struct platform_driver atmel_classd_driver = {
.driver = {
.name = "atmel-classd",
@@ -628,7 +623,6 @@ static struct platform_driver atmel_classd_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = atmel_classd_probe,
- .remove = atmel_classd_remove,
};
module_platform_driver(atmel_classd_driver);
diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c
index 77ff12baead5bc..12cd40b156440b 100644
--- a/sound/soc/atmel/atmel-pdmic.c
+++ b/sound/soc/atmel/atmel-pdmic.c
@@ -692,11 +692,6 @@ unregister_codec:
return ret;
}
-static int atmel_pdmic_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static struct platform_driver atmel_pdmic_driver = {
.driver = {
.name = "atmel-pdmic",
@@ -704,7 +699,6 @@ static struct platform_driver atmel_pdmic_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = atmel_pdmic_probe,
- .remove = atmel_pdmic_remove,
};
module_platform_driver(atmel_pdmic_driver);
diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c
index 44aefbd5b62c71..cf4084dcbd5eef 100644
--- a/sound/soc/atmel/mchp-pdmc.c
+++ b/sound/soc/atmel/mchp-pdmc.c
@@ -11,6 +11,7 @@
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/core.h>
@@ -112,10 +113,10 @@ struct mchp_pdmc {
struct clk *pclk;
struct clk *gclk;
u32 pdmcen;
+ u32 suspend_irq;
int mic_no;
int sinc_order;
bool audio_filter_en;
- u8 gclk_enabled:1;
};
static const char *const mchp_pdmc_sinc_filter_order_text[] = {
@@ -454,13 +455,6 @@ static int mchp_pdmc_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
- int ret;
-
- ret = clk_prepare_enable(dd->pclk);
- if (ret) {
- dev_err(dd->dev, "failed to enable the peripheral clock: %d\n", ret);
- return ret;
- }
regmap_write(dd->regmap, MCHP_PDMC_CR, MCHP_PDMC_CR_SWRST);
@@ -470,14 +464,6 @@ static int mchp_pdmc_startup(struct snd_pcm_substream *substream,
return 0;
}
-static void mchp_pdmc_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
-
- clk_disable_unprepare(dd->pclk);
-}
-
static int mchp_pdmc_dai_probe(struct snd_soc_dai *dai)
{
struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
@@ -594,11 +580,6 @@ static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream,
cfgr_val |= MCHP_PDMC_CFGR_BSSEL(i);
}
- if (dd->gclk_enabled) {
- clk_disable_unprepare(dd->gclk);
- dd->gclk_enabled = 0;
- }
-
for (osr_start = dd->audio_filter_en ? 64 : 8;
osr_start <= 256 && best_diff_rate; osr_start *= 2) {
long round_rate;
@@ -620,8 +601,12 @@ static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ /* CLK is enabled by runtime PM. */
+ clk_disable_unprepare(dd->gclk);
+
/* set the rate */
ret = clk_set_rate(dd->gclk, gclk_rate);
+ clk_prepare_enable(dd->gclk);
if (ret) {
dev_err(comp->dev, "unable to set rate %lu to GCLK: %d\n",
gclk_rate, ret);
@@ -636,9 +621,6 @@ static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream,
mr_val |= MCHP_PDMC_MR_CHUNK(dd->addr.maxburst);
dev_dbg(comp->dev, "maxburst set to %d\n", dd->addr.maxburst);
- clk_prepare_enable(dd->gclk);
- dd->gclk_enabled = 1;
-
snd_soc_component_update_bits(comp, MCHP_PDMC_MR,
MCHP_PDMC_MR_OSR_MASK |
MCHP_PDMC_MR_SINCORDER_MASK |
@@ -650,19 +632,6 @@ static int mchp_pdmc_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int mchp_pdmc_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct mchp_pdmc *dd = snd_soc_dai_get_drvdata(dai);
-
- if (dd->gclk_enabled) {
- clk_disable_unprepare(dd->gclk);
- dd->gclk_enabled = 0;
- }
-
- return 0;
-}
-
static int mchp_pdmc_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
@@ -673,22 +642,27 @@ static int mchp_pdmc_trigger(struct snd_pcm_substream *substream,
#endif
switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_START:
/* Enable overrun and underrun error interrupts */
- regmap_write(dd->regmap, MCHP_PDMC_IER,
+ regmap_write(dd->regmap, MCHP_PDMC_IER, dd->suspend_irq |
MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR);
+ dd->suspend_irq = 0;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
snd_soc_component_update_bits(cpu, MCHP_PDMC_MR,
MCHP_PDMC_MR_PDMCEN_MASK,
dd->pdmcen);
break;
- case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ regmap_read(dd->regmap, MCHP_PDMC_IMR, &dd->suspend_irq);
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_STOP:
/* Disable overrun and underrun error interrupts */
- regmap_write(dd->regmap, MCHP_PDMC_IDR,
+ regmap_write(dd->regmap, MCHP_PDMC_IDR, dd->suspend_irq |
MCHP_PDMC_IR_RXOVR | MCHP_PDMC_IR_RXUDR);
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
snd_soc_component_update_bits(cpu, MCHP_PDMC_MR,
MCHP_PDMC_MR_PDMCEN_MASK, 0);
break;
@@ -711,9 +685,7 @@ static int mchp_pdmc_trigger(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops mchp_pdmc_dai_ops = {
.set_fmt = mchp_pdmc_set_fmt,
.startup = mchp_pdmc_startup,
- .shutdown = mchp_pdmc_shutdown,
.hw_params = mchp_pdmc_hw_params,
- .hw_free = mchp_pdmc_hw_free,
.trigger = mchp_pdmc_trigger,
};
@@ -864,6 +836,7 @@ static const struct regmap_config mchp_pdmc_regmap_config = {
.readable_reg = mchp_pdmc_readable_reg,
.writeable_reg = mchp_pdmc_writeable_reg,
.precious_reg = mchp_pdmc_precious_reg,
+ .cache_type = REGCACHE_FLAT,
};
static int mchp_pdmc_dt_init(struct mchp_pdmc *dd)
@@ -970,6 +943,49 @@ static struct snd_dmaengine_pcm_config mchp_pdmc_config = {
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
};
+static int mchp_pdmc_runtime_suspend(struct device *dev)
+{
+ struct mchp_pdmc *dd = dev_get_drvdata(dev);
+
+ regcache_cache_only(dd->regmap, true);
+
+ clk_disable_unprepare(dd->gclk);
+ clk_disable_unprepare(dd->pclk);
+
+ return 0;
+}
+
+static int mchp_pdmc_runtime_resume(struct device *dev)
+{
+ struct mchp_pdmc *dd = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(dd->pclk);
+ if (ret) {
+ dev_err(dd->dev,
+ "failed to enable the peripheral clock: %d\n", ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(dd->gclk);
+ if (ret) {
+ dev_err(dd->dev,
+ "failed to enable generic clock: %d\n", ret);
+ goto disable_pclk;
+ }
+
+ regcache_cache_only(dd->regmap, false);
+ regcache_mark_dirty(dd->regmap);
+ ret = regcache_sync(dd->regmap);
+ if (ret) {
+ regcache_cache_only(dd->regmap, true);
+ clk_disable_unprepare(dd->gclk);
+disable_pclk:
+ clk_disable_unprepare(dd->pclk);
+ }
+
+ return ret;
+}
+
static int mchp_pdmc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1039,18 +1055,25 @@ static int mchp_pdmc_probe(struct platform_device *pdev)
dd->addr.addr = (dma_addr_t)res->start + MCHP_PDMC_RHR;
platform_set_drvdata(pdev, dd);
+ pm_runtime_enable(dd->dev);
+ if (!pm_runtime_enabled(dd->dev)) {
+ ret = mchp_pdmc_runtime_resume(dd->dev);
+ if (ret)
+ return ret;
+ }
+
/* register platform */
ret = devm_snd_dmaengine_pcm_register(dev, &mchp_pdmc_config, 0);
if (ret) {
dev_err(dev, "could not register platform: %d\n", ret);
- return ret;
+ goto pm_runtime_suspend;
}
ret = devm_snd_soc_register_component(dev, &mchp_pdmc_dai_component,
&mchp_pdmc_dai, 1);
if (ret) {
dev_err(dev, "could not register CPU DAI: %d\n", ret);
- return ret;
+ goto pm_runtime_suspend;
}
/* print IP version */
@@ -1059,6 +1082,25 @@ static int mchp_pdmc_probe(struct platform_device *pdev)
version & MCHP_PDMC_VER_VERSION);
return 0;
+
+pm_runtime_suspend:
+ if (!pm_runtime_status_suspended(dd->dev))
+ mchp_pdmc_runtime_suspend(dd->dev);
+ pm_runtime_disable(dd->dev);
+
+ return ret;
+}
+
+static int mchp_pdmc_remove(struct platform_device *pdev)
+{
+ struct mchp_pdmc *dd = platform_get_drvdata(pdev);
+
+ if (!pm_runtime_status_suspended(dd->dev))
+ mchp_pdmc_runtime_suspend(dd->dev);
+
+ pm_runtime_disable(dd->dev);
+
+ return 0;
}
static const struct of_device_id mchp_pdmc_of_match[] = {
@@ -1070,13 +1112,20 @@ static const struct of_device_id mchp_pdmc_of_match[] = {
};
MODULE_DEVICE_TABLE(of, mchp_pdmc_of_match);
+static const struct dev_pm_ops mchp_pdmc_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ RUNTIME_PM_OPS(mchp_pdmc_runtime_suspend, mchp_pdmc_runtime_resume,
+ NULL)
+};
+
static struct platform_driver mchp_pdmc_driver = {
.driver = {
.name = "mchp-pdmc",
.of_match_table = of_match_ptr(mchp_pdmc_of_match),
- .pm = &snd_soc_pm_ops,
+ .pm = pm_ptr(&mchp_pdmc_pm_ops),
},
.probe = mchp_pdmc_probe,
+ .remove = mchp_pdmc_remove,
};
module_platform_driver(mchp_pdmc_driver);
diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c
index ec0705cc40fab8..eb0c0ef4541e23 100644
--- a/sound/soc/atmel/mchp-spdifrx.c
+++ b/sound/soc/atmel/mchp-spdifrx.c
@@ -9,6 +9,7 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/spinlock.h>
@@ -192,6 +193,43 @@ static bool mchp_spdifrx_precious_reg(struct device *dev, unsigned int reg)
}
}
+static bool mchp_spdifrx_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SPDIFRX_IMR:
+ case SPDIFRX_ISR:
+ case SPDIFRX_RSR:
+ case SPDIFRX_CHSR(0, 0):
+ case SPDIFRX_CHSR(0, 1):
+ case SPDIFRX_CHSR(0, 2):
+ case SPDIFRX_CHSR(0, 3):
+ case SPDIFRX_CHSR(0, 4):
+ case SPDIFRX_CHSR(0, 5):
+ case SPDIFRX_CHUD(0, 0):
+ case SPDIFRX_CHUD(0, 1):
+ case SPDIFRX_CHUD(0, 2):
+ case SPDIFRX_CHUD(0, 3):
+ case SPDIFRX_CHUD(0, 4):
+ case SPDIFRX_CHUD(0, 5):
+ case SPDIFRX_CHSR(1, 0):
+ case SPDIFRX_CHSR(1, 1):
+ case SPDIFRX_CHSR(1, 2):
+ case SPDIFRX_CHSR(1, 3):
+ case SPDIFRX_CHSR(1, 4):
+ case SPDIFRX_CHSR(1, 5):
+ case SPDIFRX_CHUD(1, 0):
+ case SPDIFRX_CHUD(1, 1):
+ case SPDIFRX_CHUD(1, 2):
+ case SPDIFRX_CHUD(1, 3):
+ case SPDIFRX_CHUD(1, 4):
+ case SPDIFRX_CHUD(1, 5):
+ case SPDIFRX_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
static const struct regmap_config mchp_spdifrx_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
@@ -200,6 +238,8 @@ static const struct regmap_config mchp_spdifrx_regmap_config = {
.readable_reg = mchp_spdifrx_readable_reg,
.writeable_reg = mchp_spdifrx_writeable_reg,
.precious_reg = mchp_spdifrx_precious_reg,
+ .volatile_reg = mchp_spdifrx_volatile_reg,
+ .cache_type = REGCACHE_FLAT,
};
#define SPDIFRX_GCLK_RATIO_MIN (12 * 64)
@@ -209,17 +249,34 @@ static const struct regmap_config mchp_spdifrx_regmap_config = {
#define SPDIFRX_CHANNELS 2
+/**
+ * struct mchp_spdifrx_ch_stat: MCHP SPDIFRX channel status
+ * @data: channel status bits
+ * @done: completion to signal channel status bits acquisition done
+ */
struct mchp_spdifrx_ch_stat {
unsigned char data[SPDIFRX_CS_BITS / 8];
struct completion done;
};
+/**
+ * struct mchp_spdifrx_user_data: MCHP SPDIFRX user data
+ * @data: user data bits
+ * @done: completion to signal user data bits acquisition done
+ */
struct mchp_spdifrx_user_data {
unsigned char data[SPDIFRX_UD_BITS / 8];
struct completion done;
- spinlock_t lock; /* protect access to user data */
};
+/**
+ * struct mchp_spdifrx_mixer_control: MCHP SPDIFRX mixer control data structure
+ * @ch_stat: array of channel statuses
+ * @user_data: array of user data
+ * @ulock: ulock bit status
+ * @badf: badf bit status
+ * @signal: signal bit status
+ */
struct mchp_spdifrx_mixer_control {
struct mchp_spdifrx_ch_stat ch_stat[SPDIFRX_CHANNELS];
struct mchp_spdifrx_user_data user_data[SPDIFRX_CHANNELS];
@@ -228,17 +285,26 @@ struct mchp_spdifrx_mixer_control {
bool signal;
};
+/**
+ * struct mchp_spdifrx_dev: MCHP SPDIFRX device data structure
+ * @capture: DAI DMA configuration data
+ * @control: mixer controls
+ * @mlock: mutex to protect concurency b/w configuration and control APIs
+ * @dev: struct device
+ * @regmap: regmap for this device
+ * @pclk: peripheral clock
+ * @gclk: generic clock
+ * @trigger_enabled: true if enabled though trigger() ops
+ */
struct mchp_spdifrx_dev {
struct snd_dmaengine_dai_dma_data capture;
struct mchp_spdifrx_mixer_control control;
- spinlock_t blockend_lock; /* protect access to blockend_refcount */
- int blockend_refcount;
+ struct mutex mlock;
struct device *dev;
struct regmap *regmap;
struct clk *pclk;
struct clk *gclk;
- unsigned int fmt;
- unsigned int gclk_enabled:1;
+ unsigned int trigger_enabled;
};
static void mchp_spdifrx_channel_status_read(struct mchp_spdifrx_dev *dev,
@@ -275,37 +341,11 @@ static void mchp_spdifrx_channel_user_data_read(struct mchp_spdifrx_dev *dev,
}
}
-/* called from non-atomic context only */
-static void mchp_spdifrx_isr_blockend_en(struct mchp_spdifrx_dev *dev)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dev->blockend_lock, flags);
- dev->blockend_refcount++;
- /* don't enable BLOCKEND interrupt if it's already enabled */
- if (dev->blockend_refcount == 1)
- regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_BLOCKEND);
- spin_unlock_irqrestore(&dev->blockend_lock, flags);
-}
-
-/* called from atomic/non-atomic context */
-static void mchp_spdifrx_isr_blockend_dis(struct mchp_spdifrx_dev *dev)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&dev->blockend_lock, flags);
- dev->blockend_refcount--;
- /* don't enable BLOCKEND interrupt if it's already enabled */
- if (dev->blockend_refcount == 0)
- regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND);
- spin_unlock_irqrestore(&dev->blockend_lock, flags);
-}
-
static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id)
{
struct mchp_spdifrx_dev *dev = dev_id;
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
- u32 sr, imr, pending, idr = 0;
+ u32 sr, imr, pending;
irqreturn_t ret = IRQ_NONE;
int ch;
@@ -320,13 +360,10 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id)
if (pending & SPDIFRX_IR_BLOCKEND) {
for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) {
- spin_lock(&ctrl->user_data[ch].lock);
mchp_spdifrx_channel_user_data_read(dev, ch);
- spin_unlock(&ctrl->user_data[ch].lock);
-
complete(&ctrl->user_data[ch].done);
}
- mchp_spdifrx_isr_blockend_dis(dev);
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND);
ret = IRQ_HANDLED;
}
@@ -334,7 +371,7 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id)
if (pending & SPDIFRX_IR_CSC(ch)) {
mchp_spdifrx_channel_status_read(dev, ch);
complete(&ctrl->ch_stat[ch].done);
- idr |= SPDIFRX_IR_CSC(ch);
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_CSC(ch));
ret = IRQ_HANDLED;
}
}
@@ -344,8 +381,6 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id)
ret = IRQ_HANDLED;
}
- regmap_write(dev->regmap, SPDIFRX_IDR, idr);
-
return ret;
}
@@ -353,47 +388,40 @@ static int mchp_spdifrx_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
- u32 mr;
- int running;
- int ret;
-
- regmap_read(dev->regmap, SPDIFRX_MR, &mr);
- running = !!(mr & SPDIFRX_MR_RXEN_ENABLE);
+ int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (!running) {
- mr &= ~SPDIFRX_MR_RXEN_MASK;
- mr |= SPDIFRX_MR_RXEN_ENABLE;
- /* enable overrun interrupts */
- regmap_write(dev->regmap, SPDIFRX_IER,
- SPDIFRX_IR_OVERRUN);
- }
+ mutex_lock(&dev->mlock);
+ /* Enable overrun interrupts */
+ regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_OVERRUN);
+
+ /* Enable receiver. */
+ regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK,
+ SPDIFRX_MR_RXEN_ENABLE);
+ dev->trigger_enabled = true;
+ mutex_unlock(&dev->mlock);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (running) {
- mr &= ~SPDIFRX_MR_RXEN_MASK;
- mr |= SPDIFRX_MR_RXEN_DISABLE;
- /* disable overrun interrupts */
- regmap_write(dev->regmap, SPDIFRX_IDR,
- SPDIFRX_IR_OVERRUN);
- }
+ mutex_lock(&dev->mlock);
+ /* Disable overrun interrupts */
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_OVERRUN);
+
+ /* Disable receiver. */
+ regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK,
+ SPDIFRX_MR_RXEN_DISABLE);
+ dev->trigger_enabled = false;
+ mutex_unlock(&dev->mlock);
break;
default:
- return -EINVAL;
+ ret = -EINVAL;
}
- ret = regmap_write(dev->regmap, SPDIFRX_MR, mr);
- if (ret) {
- dev_err(dev->dev, "unable to enable/disable RX: %d\n", ret);
- return ret;
- }
-
- return 0;
+ return ret;
}
static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream,
@@ -401,7 +429,7 @@ static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
- u32 mr;
+ u32 mr = 0;
int ret;
dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n",
@@ -413,13 +441,6 @@ static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- regmap_read(dev->regmap, SPDIFRX_MR, &mr);
-
- if (mr & SPDIFRX_MR_RXEN_ENABLE) {
- dev_err(dev->dev, "PCM already running\n");
- return -EBUSY;
- }
-
if (params_channels(params) != SPDIFRX_CHANNELS) {
dev_err(dev->dev, "unsupported number of channels: %d\n",
params_channels(params));
@@ -445,47 +466,46 @@ static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- if (dev->gclk_enabled) {
- clk_disable_unprepare(dev->gclk);
- dev->gclk_enabled = 0;
+ mutex_lock(&dev->mlock);
+ if (dev->trigger_enabled) {
+ dev_err(dev->dev, "PCM already running\n");
+ ret = -EBUSY;
+ goto unlock;
}
+
+ /* GCLK is enabled by runtime PM. */
+ clk_disable_unprepare(dev->gclk);
+
ret = clk_set_min_rate(dev->gclk, params_rate(params) *
SPDIFRX_GCLK_RATIO_MIN + 1);
if (ret) {
dev_err(dev->dev,
"unable to set gclk min rate: rate %u * ratio %u + 1\n",
params_rate(params), SPDIFRX_GCLK_RATIO_MIN);
- return ret;
+ /* Restore runtime PM state. */
+ clk_prepare_enable(dev->gclk);
+ goto unlock;
}
ret = clk_prepare_enable(dev->gclk);
if (ret) {
dev_err(dev->dev, "unable to enable gclk: %d\n", ret);
- return ret;
+ goto unlock;
}
- dev->gclk_enabled = 1;
dev_dbg(dev->dev, "GCLK range min set to %d\n",
params_rate(params) * SPDIFRX_GCLK_RATIO_MIN + 1);
- return regmap_write(dev->regmap, SPDIFRX_MR, mr);
-}
+ ret = regmap_write(dev->regmap, SPDIFRX_MR, mr);
-static int mchp_spdifrx_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+unlock:
+ mutex_unlock(&dev->mlock);
- if (dev->gclk_enabled) {
- clk_disable_unprepare(dev->gclk);
- dev->gclk_enabled = 0;
- }
- return 0;
+ return ret;
}
static const struct snd_soc_dai_ops mchp_spdifrx_dai_ops = {
.trigger = mchp_spdifrx_trigger,
.hw_params = mchp_spdifrx_hw_params,
- .hw_free = mchp_spdifrx_hw_free,
};
#define MCHP_SPDIF_RATES SNDRV_PCM_RATE_8000_192000
@@ -515,22 +535,58 @@ static int mchp_spdifrx_cs_get(struct mchp_spdifrx_dev *dev,
{
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
struct mchp_spdifrx_ch_stat *ch_stat = &ctrl->ch_stat[channel];
- int ret;
-
- regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_CSC(channel));
- /* check for new data available */
- ret = wait_for_completion_interruptible_timeout(&ch_stat->done,
- msecs_to_jiffies(100));
- /* IP might not be started or valid stream might not be present */
- if (ret < 0) {
- dev_dbg(dev->dev, "channel status for channel %d timeout\n",
- channel);
+ int ret = 0;
+
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * We may reach this point with both clocks enabled but the receiver
+ * still disabled. To void waiting for completion and return with
+ * timeout check the dev->trigger_enabled.
+ *
+ * To retrieve data:
+ * - if the receiver is enabled CSC IRQ will update the data in software
+ * caches (ch_stat->data)
+ * - otherwise we just update it here the software caches with latest
+ * available information and return it; in this case we don't need
+ * spin locking as the IRQ is disabled and will not be raised from
+ * anywhere else.
+ */
+
+ if (dev->trigger_enabled) {
+ reinit_completion(&ch_stat->done);
+ regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_CSC(channel));
+ /* Check for new data available */
+ ret = wait_for_completion_interruptible_timeout(&ch_stat->done,
+ msecs_to_jiffies(100));
+ /* Valid stream might not be present */
+ if (ret <= 0) {
+ dev_dbg(dev->dev, "channel status for channel %d timeout\n",
+ channel);
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_CSC(channel));
+ ret = ret ? : -ETIMEDOUT;
+ goto pm_runtime_put;
+ } else {
+ ret = 0;
+ }
+ } else {
+ /* Update software cache with latest channel status. */
+ mchp_spdifrx_channel_status_read(dev, channel);
}
memcpy(uvalue->value.iec958.status, ch_stat->data,
sizeof(ch_stat->data));
- return 0;
+pm_runtime_put:
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+unlock:
+ mutex_unlock(&dev->mlock);
+ return ret;
}
static int mchp_spdifrx_cs1_get(struct snd_kcontrol *kcontrol,
@@ -564,29 +620,56 @@ static int mchp_spdifrx_subcode_ch_get(struct mchp_spdifrx_dev *dev,
int channel,
struct snd_ctl_elem_value *uvalue)
{
- unsigned long flags;
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
struct mchp_spdifrx_user_data *user_data = &ctrl->user_data[channel];
- int ret;
-
- reinit_completion(&user_data->done);
- mchp_spdifrx_isr_blockend_en(dev);
- ret = wait_for_completion_interruptible_timeout(&user_data->done,
- msecs_to_jiffies(100));
- /* IP might not be started or valid stream might not be present */
- if (ret <= 0) {
- dev_dbg(dev->dev, "user data for channel %d timeout\n",
- channel);
- mchp_spdifrx_isr_blockend_dis(dev);
- return ret;
+ int ret = 0;
+
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * We may reach this point with both clocks enabled but the receiver
+ * still disabled. To void waiting for completion to just timeout we
+ * check here the dev->trigger_enabled flag.
+ *
+ * To retrieve data:
+ * - if the receiver is enabled we need to wait for blockend IRQ to read
+ * data to and update it for us in software caches
+ * - otherwise reading the SPDIFRX_CHUD() registers is enough.
+ */
+
+ if (dev->trigger_enabled) {
+ reinit_completion(&user_data->done);
+ regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_BLOCKEND);
+ ret = wait_for_completion_interruptible_timeout(&user_data->done,
+ msecs_to_jiffies(100));
+ /* Valid stream might not be present. */
+ if (ret <= 0) {
+ dev_dbg(dev->dev, "user data for channel %d timeout\n",
+ channel);
+ regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND);
+ ret = ret ? : -ETIMEDOUT;
+ goto pm_runtime_put;
+ } else {
+ ret = 0;
+ }
+ } else {
+ /* Update software cache with last available data. */
+ mchp_spdifrx_channel_user_data_read(dev, channel);
}
- spin_lock_irqsave(&user_data->lock, flags);
memcpy(uvalue->value.iec958.subcode, user_data->data,
sizeof(user_data->data));
- spin_unlock_irqrestore(&user_data->lock, flags);
- return 0;
+pm_runtime_put:
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+unlock:
+ mutex_unlock(&dev->mlock);
+ return ret;
}
static int mchp_spdifrx_subcode_ch1_get(struct snd_kcontrol *kcontrol,
@@ -625,12 +708,34 @@ static int mchp_spdifrx_ulock_get(struct snd_kcontrol *kcontrol,
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
u32 val;
+ int ret;
bool ulock_old = ctrl->ulock;
- regmap_read(dev->regmap, SPDIFRX_RSR, &val);
- ctrl->ulock = !(val & SPDIFRX_RSR_ULOCK);
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * The RSR.ULOCK has wrong value if both pclk and gclk are enabled
+ * and the receiver is disabled. Thus we take into account the
+ * dev->trigger_enabled here to return a real status.
+ */
+ if (dev->trigger_enabled) {
+ regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ ctrl->ulock = !(val & SPDIFRX_RSR_ULOCK);
+ } else {
+ ctrl->ulock = 0;
+ }
+
uvalue->value.integer.value[0] = ctrl->ulock;
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+unlock:
+ mutex_unlock(&dev->mlock);
+
return ulock_old != ctrl->ulock;
}
@@ -641,10 +746,32 @@ static int mchp_spdifrx_badf_get(struct snd_kcontrol *kcontrol,
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
u32 val;
+ int ret;
bool badf_old = ctrl->badf;
- regmap_read(dev->regmap, SPDIFRX_RSR, &val);
- ctrl->badf = !!(val & SPDIFRX_RSR_BADF);
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * The RSR.ULOCK has wrong value if both pclk and gclk are enabled
+ * and the receiver is disabled. Thus we take into account the
+ * dev->trigger_enabled here to return a real status.
+ */
+ if (dev->trigger_enabled) {
+ regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ ctrl->badf = !!(val & SPDIFRX_RSR_BADF);
+ } else {
+ ctrl->badf = 0;
+ }
+
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+unlock:
+ mutex_unlock(&dev->mlock);
+
uvalue->value.integer.value[0] = ctrl->badf;
return badf_old != ctrl->badf;
@@ -656,11 +783,49 @@ static int mchp_spdifrx_signal_get(struct snd_kcontrol *kcontrol,
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
- u32 val;
+ u32 val = ~0U, loops = 10;
+ int ret;
bool signal_old = ctrl->signal;
- regmap_read(dev->regmap, SPDIFRX_RSR, &val);
- ctrl->signal = !(val & SPDIFRX_RSR_NOSIGNAL);
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * To get the signal we need to have receiver enabled. This
+ * could be enabled also from trigger() function thus we need to
+ * take care of not disabling the receiver when it runs.
+ */
+ if (!dev->trigger_enabled) {
+ regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK,
+ SPDIFRX_MR_RXEN_ENABLE);
+
+ /* Wait for RSR.ULOCK bit. */
+ while (--loops) {
+ regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ if (!(val & SPDIFRX_RSR_ULOCK))
+ break;
+ usleep_range(100, 150);
+ }
+
+ regmap_update_bits(dev->regmap, SPDIFRX_MR, SPDIFRX_MR_RXEN_MASK,
+ SPDIFRX_MR_RXEN_DISABLE);
+ } else {
+ regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ }
+
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+
+unlock:
+ mutex_unlock(&dev->mlock);
+
+ if (!(val & SPDIFRX_RSR_ULOCK))
+ ctrl->signal = !(val & SPDIFRX_RSR_NOSIGNAL);
+ else
+ ctrl->signal = 0;
uvalue->value.integer.value[0] = ctrl->signal;
return signal_old != ctrl->signal;
@@ -682,22 +847,44 @@ static int mchp_spdifrx_rate_get(struct snd_kcontrol *kcontrol,
{
struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol);
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
+ unsigned long rate;
u32 val;
- int rate;
-
- regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ int ret;
- /* if the receiver is not locked, ISF data is invalid */
- if (val & SPDIFRX_RSR_ULOCK || !(val & SPDIFRX_RSR_IFS_MASK)) {
+ mutex_lock(&dev->mlock);
+
+ ret = pm_runtime_resume_and_get(dev->dev);
+ if (ret < 0)
+ goto unlock;
+
+ /*
+ * The RSR.ULOCK has wrong value if both pclk and gclk are enabled
+ * and the receiver is disabled. Thus we take into account the
+ * dev->trigger_enabled here to return a real status.
+ */
+ if (dev->trigger_enabled) {
+ regmap_read(dev->regmap, SPDIFRX_RSR, &val);
+ /* If the receiver is not locked, ISF data is invalid. */
+ if (val & SPDIFRX_RSR_ULOCK || !(val & SPDIFRX_RSR_IFS_MASK)) {
+ ucontrol->value.integer.value[0] = 0;
+ goto pm_runtime_put;
+ }
+ } else {
+ /* Reveicer is not locked, IFS data is invalid. */
ucontrol->value.integer.value[0] = 0;
- return 0;
+ goto pm_runtime_put;
}
rate = clk_get_rate(dev->gclk);
ucontrol->value.integer.value[0] = rate / (32 * SPDIFRX_RSR_IFS(val));
- return 0;
+pm_runtime_put:
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
+unlock:
+ mutex_unlock(&dev->mlock);
+ return ret;
}
static struct snd_kcontrol_new mchp_spdifrx_ctrls[] = {
@@ -787,14 +974,6 @@ static int mchp_spdifrx_dai_probe(struct snd_soc_dai *dai)
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
struct mchp_spdifrx_mixer_control *ctrl = &dev->control;
int ch;
- int err;
-
- err = clk_prepare_enable(dev->pclk);
- if (err) {
- dev_err(dev->dev,
- "failed to enable the peripheral clock: %d\n", err);
- return err;
- }
snd_soc_dai_init_dma_data(dai, NULL, &dev->capture);
@@ -808,11 +987,9 @@ static int mchp_spdifrx_dai_probe(struct snd_soc_dai *dai)
SPDIFRX_MR_AUTORST_NOACTION |
SPDIFRX_MR_PACK_DISABLED);
- dev->blockend_refcount = 0;
for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) {
init_completion(&ctrl->ch_stat[ch].done);
init_completion(&ctrl->user_data[ch].done);
- spin_lock_init(&ctrl->user_data[ch].lock);
}
/* Add controls */
@@ -827,9 +1004,7 @@ static int mchp_spdifrx_dai_remove(struct snd_soc_dai *dai)
struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai);
/* Disable interrupts */
- regmap_write(dev->regmap, SPDIFRX_IDR, 0xFF);
-
- clk_disable_unprepare(dev->pclk);
+ regmap_write(dev->regmap, SPDIFRX_IDR, GENMASK(14, 0));
return 0;
}
@@ -861,6 +1036,48 @@ static const struct of_device_id mchp_spdifrx_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, mchp_spdifrx_dt_ids);
+static int mchp_spdifrx_runtime_suspend(struct device *dev)
+{
+ struct mchp_spdifrx_dev *spdifrx = dev_get_drvdata(dev);
+
+ regcache_cache_only(spdifrx->regmap, true);
+ clk_disable_unprepare(spdifrx->gclk);
+ clk_disable_unprepare(spdifrx->pclk);
+
+ return 0;
+}
+
+static int mchp_spdifrx_runtime_resume(struct device *dev)
+{
+ struct mchp_spdifrx_dev *spdifrx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(spdifrx->pclk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(spdifrx->gclk);
+ if (ret)
+ goto disable_pclk;
+
+ regcache_cache_only(spdifrx->regmap, false);
+ regcache_mark_dirty(spdifrx->regmap);
+ ret = regcache_sync(spdifrx->regmap);
+ if (ret) {
+ regcache_cache_only(spdifrx->regmap, true);
+ clk_disable_unprepare(spdifrx->gclk);
+disable_pclk:
+ clk_disable_unprepare(spdifrx->pclk);
+ }
+
+ return ret;
+}
+
+static const struct dev_pm_ops mchp_spdifrx_pm_ops = {
+ RUNTIME_PM_OPS(mchp_spdifrx_runtime_suspend, mchp_spdifrx_runtime_resume,
+ NULL)
+};
+
static int mchp_spdifrx_probe(struct platform_device *pdev)
{
struct mchp_spdifrx_dev *dev;
@@ -913,19 +1130,36 @@ static int mchp_spdifrx_probe(struct platform_device *pdev)
"failed to get the PMC generated clock: %d\n", err);
return err;
}
- spin_lock_init(&dev->blockend_lock);
+
+ /*
+ * Signal control need a valid rate on gclk. hw_params() configures
+ * it propertly but requesting signal before any hw_params() has been
+ * called lead to invalid value returned for signal. Thus, configure
+ * gclk at a valid rate, here, in initialization, to simplify the
+ * control path.
+ */
+ clk_set_min_rate(dev->gclk, 48000 * SPDIFRX_GCLK_RATIO_MIN + 1);
+
+ mutex_init(&dev->mlock);
dev->dev = &pdev->dev;
dev->regmap = regmap;
platform_set_drvdata(pdev, dev);
+ pm_runtime_enable(dev->dev);
+ if (!pm_runtime_enabled(dev->dev)) {
+ err = mchp_spdifrx_runtime_resume(dev->dev);
+ if (err)
+ goto pm_runtime_disable;
+ }
+
dev->capture.addr = (dma_addr_t)mem->start + SPDIFRX_RHR;
dev->capture.maxburst = 1;
err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (err) {
dev_err(&pdev->dev, "failed to register PCM: %d\n", err);
- return err;
+ goto pm_runtime_suspend;
}
err = devm_snd_soc_register_component(&pdev->dev,
@@ -933,20 +1167,40 @@ static int mchp_spdifrx_probe(struct platform_device *pdev)
&mchp_spdifrx_dai, 1);
if (err) {
dev_err(&pdev->dev, "fail to register dai\n");
- return err;
+ goto pm_runtime_suspend;
}
regmap_read(regmap, SPDIFRX_VERSION, &vers);
dev_info(&pdev->dev, "hw version: %#lx\n", vers & SPDIFRX_VERSION_MASK);
return 0;
+
+pm_runtime_suspend:
+ if (!pm_runtime_status_suspended(dev->dev))
+ mchp_spdifrx_runtime_suspend(dev->dev);
+pm_runtime_disable:
+ pm_runtime_disable(dev->dev);
+ return err;
+}
+
+static int mchp_spdifrx_remove(struct platform_device *pdev)
+{
+ struct mchp_spdifrx_dev *dev = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(dev->dev);
+ if (!pm_runtime_status_suspended(dev->dev))
+ mchp_spdifrx_runtime_suspend(dev->dev);
+
+ return 0;
}
static struct platform_driver mchp_spdifrx_driver = {
.probe = mchp_spdifrx_probe,
+ .remove = mchp_spdifrx_remove,
.driver = {
.name = "mchp_spdifrx",
.of_match_table = of_match_ptr(mchp_spdifrx_dt_ids),
+ .pm = pm_ptr(&mchp_spdifrx_pm_ops),
},
};
diff --git a/sound/soc/atmel/mchp-spdiftx.c b/sound/soc/atmel/mchp-spdiftx.c
index dc96a6fbf514e1..20d135c718b070 100644
--- a/sound/soc/atmel/mchp-spdiftx.c
+++ b/sound/soc/atmel/mchp-spdiftx.c
@@ -6,6 +6,7 @@
//
// Author: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -71,11 +72,11 @@
/* Valid Bits per Sample */
#define SPDIFTX_MR_VBPS_MASK GENMASK(13, 8)
-#define SPDIFTX_MR_VBPS(bps) (((bps) << 8) & SPDIFTX_MR_VBPS_MASK)
+#define SPDIFTX_MR_VBPS(bps) FIELD_PREP(SPDIFTX_MR_VBPS_MASK, bps)
/* Chunk Size */
#define SPDIFTX_MR_CHUNK_MASK GENMASK(19, 16)
-#define SPDIFTX_MR_CHUNK(size) (((size) << 16) & SPDIFTX_MR_CHUNK_MASK)
+#define SPDIFTX_MR_CHUNK(size) FIELD_PREP(SPDIFTX_MR_CHUNK_MASK, size)
/* Validity Bits for Channels 1 and 2 */
#define SPDIFTX_MR_VALID1 BIT(24)
@@ -88,8 +89,7 @@
/* Bytes per Sample */
#define SPDIFTX_MR_BPS_MASK GENMASK(29, 28)
-#define SPDIFTX_MR_BPS(bytes) \
- ((((bytes) - 1) << 28) & SPDIFTX_MR_BPS_MASK)
+#define SPDIFTX_MR_BPS(bytes) FIELD_PREP(SPDIFTX_MR_BPS_MASK, (bytes - 1))
/*
* ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ----
diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c
index 37593abe60532a..cec067c91a0fb1 100644
--- a/sound/soc/cirrus/ep93xx-ac97.c
+++ b/sound/soc/cirrus/ep93xx-ac97.c
@@ -323,8 +323,8 @@ static int ep93xx_ac97_dai_probe(struct snd_soc_dai *dai)
info->dma_params_tx.filter_data = &ep93xx_ac97_pcm_out;
info->dma_params_rx.filter_data = &ep93xx_ac97_pcm_in;
- dai->playback_dma_data = &info->dma_params_tx;
- dai->capture_dma_data = &info->dma_params_rx;
+ snd_soc_dai_init_dma_data(dai, &info->dma_params_tx,
+ &info->dma_params_rx);
return 0;
}
diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c
index 982151330c896a..70840f27d4a797 100644
--- a/sound/soc/cirrus/ep93xx-i2s.c
+++ b/sound/soc/cirrus/ep93xx-i2s.c
@@ -202,8 +202,8 @@ static int ep93xx_i2s_dai_probe(struct snd_soc_dai *dai)
info->dma_params_rx.filter_data =
&ep93xx_i2s_dma_data[SNDRV_PCM_STREAM_CAPTURE];
- dai->playback_dma_data = &info->dma_params_tx;
- dai->capture_dma_data = &info->dma_params_rx;
+ snd_soc_dai_init_dma_data(dai, &info->dma_params_tx,
+ &info->dma_params_rx);
return 0;
}
@@ -359,6 +359,8 @@ static int ep93xx_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
if (dir == SND_SOC_CLOCK_IN || clk_id != 0)
return -EINVAL;
+ if (!freq)
+ return 0;
return clk_set_rate(info->mclk, freq);
}
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index fc65283031cdc2..3574c68e0dda39 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -1386,17 +1386,11 @@ static int pm860x_codec_probe(struct platform_device *pdev)
return ret;
}
-static int pm860x_codec_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static struct platform_driver pm860x_codec_driver = {
.driver = {
.name = "88pm860x-codec",
},
.probe = pm860x_codec_probe,
- .remove = pm860x_codec_remove,
};
module_platform_driver(pm860x_codec_driver);
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 0f9d71490075f7..bd72c426a93d17 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -54,6 +54,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_ALC5623
imply SND_SOC_ALC5632
imply SND_SOC_AW8738
+ imply SND_SOC_AW88395
imply SND_SOC_BT_SCO
imply SND_SOC_BD28623
imply SND_SOC_CQ0093VC
@@ -68,6 +69,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_CS35L45_I2C
imply SND_SOC_CS35L45_SPI
imply SND_SOC_CS42L42
+ imply SND_SOC_CS42L42_SDW
imply SND_SOC_CS42L51_I2C
imply SND_SOC_CS42L52
imply SND_SOC_CS42L56
@@ -107,6 +109,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_HDAC_HDMI
imply SND_SOC_HDAC_HDA
imply SND_SOC_ICS43432
+ imply SND_SOC_IDT821034
imply SND_SOC_INNO_RK3036
imply SND_SOC_ISABELLE
imply SND_SOC_JZ4740_CODEC
@@ -164,6 +167,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_PCM5102A
imply SND_SOC_PCM512x_I2C
imply SND_SOC_PCM512x_SPI
+ imply SND_SOC_PEB2466
imply SND_SOC_RK3328
imply SND_SOC_RK817
imply SND_SOC_RT274
@@ -195,6 +199,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_RT700_SDW
imply SND_SOC_RT711_SDW
imply SND_SOC_RT711_SDCA_SDW
+ imply SND_SOC_RT712_SDCA_SDW
imply SND_SOC_RT715_SDW
imply SND_SOC_RT715_SDCA_SDW
imply SND_SOC_RT1308_SDW
@@ -206,6 +211,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_SI476X
imply SND_SOC_SIMPLE_AMPLIFIER
imply SND_SOC_SIMPLE_MUX
+ imply SND_SOC_SMA1303
imply SND_SOC_SPDIF
imply SND_SOC_SRC4XXX_I2C
imply SND_SOC_SSM2305
@@ -599,6 +605,23 @@ config SND_SOC_AW8738
SND_SOC_SIMPLE_AMPLIFIER, but additionally allows setting the
operation mode using the Awinic-specific one-wire pulse control.
+config SND_SOC_AW88395_LIB
+ tristate
+
+config SND_SOC_AW88395
+ tristate "Soc Audio for awinic aw88395"
+ depends on I2C
+ select CRC8
+ select CRC32
+ select REGMAP_I2C
+ select GPIOLIB
+ select SND_SOC_AW88395_LIB
+ help
+ this option enables support for aw88395 Smart PA.
+ The Awinic AW88395 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier with an integrated 10V
+ smart boost convert.
+
config SND_SOC_BD28623
tristate "ROHM BD28623 CODEC"
help
@@ -665,9 +688,6 @@ config SND_SOC_CS35L41_I2C
select SND_SOC_CS35L41
select REGMAP_I2C
-config SND_SOC_CS35L45_TABLES
- tristate
-
config SND_SOC_CS35L45
tristate
@@ -676,7 +696,6 @@ config SND_SOC_CS35L45_SPI
depends on SPI_MASTER
select REGMAP
select REGMAP_SPI
- select SND_SOC_CS35L45_TABLES
select SND_SOC_CS35L45
help
Enable support for Cirrus Logic CS35L45 smart speaker amplifier
@@ -687,7 +706,6 @@ config SND_SOC_CS35L45_I2C
depends on I2C
select REGMAP
select REGMAP_I2C
- select SND_SOC_CS35L45_TABLES
select SND_SOC_CS35L45
help
Enable support for Cirrus Logic CS35L45 smart speaker amplifier
@@ -703,6 +721,13 @@ config SND_SOC_CS42L42
select REGMAP_I2C
select SND_SOC_CS42L42_CORE
+config SND_SOC_CS42L42_SDW
+ tristate "Cirrus Logic CS42L42 CODEC on Soundwire"
+ depends on SOUNDWIRE
+ select SND_SOC_CS42L42_CORE
+ help
+ Enable support for Cirrus Logic CS42L42 codec with Soundwire control
+
config SND_SOC_CS42L51
tristate
@@ -972,6 +997,16 @@ config SND_SOC_HDA
config SND_SOC_ICS43432
tristate "ICS43423 and compatible i2s microphones"
+config SND_SOC_IDT821034
+ tristate "Renesas IDT821034 quad PCM codec"
+ depends on SPI
+ help
+ Enable support for the Renesas IDT821034 quad PCM with
+ programmable gain codec.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-idt821034.
+
config SND_SOC_INNO_RK3036
tristate "Inno codec driver for RK3036 SoC"
select REGMAP_MMIO
@@ -1205,6 +1240,17 @@ config SND_SOC_PCM512x_SPI
select SND_SOC_PCM512x
select REGMAP_SPI
+config SND_SOC_PEB2466
+ tristate "Infineon PEB2466 quad PCM codec"
+ depends on SPI
+ select REGMAP_SPI
+ help
+ Enable support for the Infineon PEB2466 quad PCM codec,
+ also named SICOFI 4-uC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-peb2466.
+
config SND_SOC_RK3328
tristate "Rockchip RK3328 audio CODEC"
select REGMAP_MMIO
@@ -1425,6 +1471,12 @@ config SND_SOC_RT711_SDCA_SDW
select REGMAP_SOUNDWIRE
select REGMAP_SOUNDWIRE_MBQ
+config SND_SOC_RT712_SDCA_SDW
+ tristate "Realtek RT712 SDCA Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
config SND_SOC_RT715
tristate
@@ -1492,6 +1544,12 @@ config SND_SOC_SIMPLE_MUX
tristate "Simple Audio Mux"
depends on GPIOLIB
+config SND_SOC_SMA1303
+ tristate "Iron Device SMA1303 Audio Amplifier"
+ depends on I2C
+ help
+ Enable support for Iron Device SMA1303 Boosted Class-D amplifier
+
config SND_SOC_SPDIF
tristate "S/PDIF CODEC"
@@ -2111,7 +2169,6 @@ config SND_SOC_MT6660
config SND_SOC_NAU8315
tristate "Nuvoton Technology Corporation NAU8315 CODEC"
- depends on GPIOLIB
config SND_SOC_NAU8540
tristate "Nuvoton Technology Corporation NAU85L40 CODEC"
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 71d3ce5867e4f5..f1ca18f7946cac 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -46,6 +46,9 @@ snd-soc-ak5386-objs := ak5386.o
snd-soc-ak5558-objs := ak5558.o
snd-soc-arizona-objs := arizona.o arizona-jack.o
snd-soc-aw8738-objs := aw8738.o
+snd-soc-aw88395-lib-objs := aw88395/aw88395_lib.o
+snd-soc-aw88395-objs := aw88395/aw88395.o \
+ aw88395/aw88395_device.o
snd-soc-bd28623-objs := bd28623.o
snd-soc-bt-sco-objs := bt-sco.o
snd-soc-cpcap-objs := cpcap.o
@@ -60,12 +63,12 @@ snd-soc-cs35l41-lib-objs := cs35l41-lib.o
snd-soc-cs35l41-objs := cs35l41.o
snd-soc-cs35l41-spi-objs := cs35l41-spi.o
snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o
-snd-soc-cs35l45-tables-objs := cs35l45-tables.o
-snd-soc-cs35l45-objs := cs35l45.o
+snd-soc-cs35l45-objs := cs35l45.o cs35l45-tables.o
snd-soc-cs35l45-spi-objs := cs35l45-spi.o
snd-soc-cs35l45-i2c-objs := cs35l45-i2c.o
snd-soc-cs42l42-objs := cs42l42.o
snd-soc-cs42l42-i2c-objs := cs42l42-i2c.o
+snd-soc-cs42l42-sdw-objs := cs42l42-sdw.o
snd-soc-cs42l51-objs := cs42l51.o
snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
snd-soc-cs42l52-objs := cs42l52.o
@@ -111,6 +114,7 @@ snd-soc-hdac-hdmi-objs := hdac_hdmi.o
snd-soc-hdac-hda-objs := hdac_hda.o
snd-soc-hda-codec-objs := hda.o hda-dai.o
snd-soc-ics43432-objs := ics43432.o
+snd-soc-idt821034-objs := idt821034.o
snd-soc-inno-rk3036-objs := inno_rk3036.o
snd-soc-isabelle-objs := isabelle.o
snd-soc-jz4740-codec-objs := jz4740.o
@@ -183,6 +187,7 @@ snd-soc-pcm5102a-objs := pcm5102a.o
snd-soc-pcm512x-objs := pcm512x.o
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
+snd-soc-peb2466-objs := peb2466.o
snd-soc-rk3328-objs := rk3328_codec.o
snd-soc-rk817-objs := rk817_codec.o
snd-soc-rl6231-objs := rl6231.o
@@ -222,6 +227,7 @@ snd-soc-rt5682s-objs := rt5682s.o
snd-soc-rt700-objs := rt700.o rt700-sdw.o
snd-soc-rt711-objs := rt711.o rt711-sdw.o
snd-soc-rt711-sdca-objs := rt711-sdca.o rt711-sdca-sdw.o
+snd-soc-rt712-sdca-objs := rt712-sdca.o rt712-sdca-sdw.o
snd-soc-rt715-objs := rt715.o rt715-sdw.o
snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o
snd-soc-rt9120-objs := rt9120.o
@@ -233,6 +239,7 @@ snd-soc-sigmadsp-objs := sigmadsp.o
snd-soc-sigmadsp-i2c-objs := sigmadsp-i2c.o
snd-soc-sigmadsp-regmap-objs := sigmadsp-regmap.o
snd-soc-si476x-objs := si476x.o
+snd-soc-sma1303-objs := sma1303.o
snd-soc-spdif-tx-objs := spdif_transmitter.o
snd-soc-spdif-rx-objs := spdif_receiver.o
snd-soc-src4xxx-objs := src4xxx.o
@@ -407,6 +414,8 @@ obj-$(CONFIG_SND_SOC_ALC5623) += snd-soc-alc5623.o
obj-$(CONFIG_SND_SOC_ALC5632) += snd-soc-alc5632.o
obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o
obj-$(CONFIG_SND_SOC_AW8738) += snd-soc-aw8738.o
+obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o
+obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o
obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o
obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
@@ -421,12 +430,12 @@ obj-$(CONFIG_SND_SOC_CS35L41) += snd-soc-cs35l41.o
obj-$(CONFIG_SND_SOC_CS35L41_LIB) += snd-soc-cs35l41-lib.o
obj-$(CONFIG_SND_SOC_CS35L41_SPI) += snd-soc-cs35l41-spi.o
obj-$(CONFIG_SND_SOC_CS35L41_I2C) += snd-soc-cs35l41-i2c.o
-obj-$(CONFIG_SND_SOC_CS35L45_TABLES) += snd-soc-cs35l45-tables.o
obj-$(CONFIG_SND_SOC_CS35L45) += snd-soc-cs35l45.o
obj-$(CONFIG_SND_SOC_CS35L45_SPI) += snd-soc-cs35l45-spi.o
obj-$(CONFIG_SND_SOC_CS35L45_I2C) += snd-soc-cs35l45-i2c.o
obj-$(CONFIG_SND_SOC_CS42L42_CORE) += snd-soc-cs42l42.o
obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42-i2c.o
+obj-$(CONFIG_SND_SOC_CS42L42_SDW) += snd-soc-cs42l42-sdw.o
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o
obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o
@@ -472,6 +481,7 @@ obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o
obj-$(CONFIG_SND_SOC_HDA) += snd-soc-hda-codec.o
obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o
+obj-$(CONFIG_SND_SOC_IDT821034) += snd-soc-idt821034.o
obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o
obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
@@ -539,6 +549,7 @@ obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o
obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
+obj-$(CONFIG_SND_SOC_PEB2466) += snd-soc-peb2466.o
obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o
obj-$(CONFIG_SND_SOC_RK817) += snd-soc-rk817.o
obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
@@ -579,6 +590,7 @@ obj-$(CONFIG_SND_SOC_RT5682S) += snd-soc-rt5682s.o
obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o
obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o
obj-$(CONFIG_SND_SOC_RT711_SDCA_SDW) += snd-soc-rt711-sdca.o
+obj-$(CONFIG_SND_SOC_RT712_SDCA_SDW) += snd-soc-rt712-sdca.o
obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o
obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o
obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o
@@ -588,6 +600,7 @@ obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o
obj-$(CONFIG_SND_SOC_SIGMADSP_REGMAP) += snd-soc-sigmadsp-regmap.o
obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o
+obj-$(CONFIG_SND_SOC_SMA1303) += snd-soc-sma1303.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o
obj-$(CONFIG_SND_SOC_SRC4XXX) += snd-soc-src4xxx.o
obj-$(CONFIG_SND_SOC_SRC4XXX_I2C) += snd-soc-src4xxx-i2c.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index cc12052e192046..0e013edfe63d7c 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -127,18 +127,12 @@ static int ac97_probe(struct platform_device *pdev)
&soc_component_dev_ac97, &ac97_dai, 1);
}
-static int ac97_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static struct platform_driver ac97_codec_driver = {
.driver = {
.name = "ac97-codec",
},
.probe = ac97_probe,
- .remove = ac97_remove,
};
module_platform_driver(ac97_codec_driver);
diff --git a/sound/soc/codecs/adau7002.c b/sound/soc/codecs/adau7002.c
index 401bafabc8eb4b..c9134e1de0b25b 100644
--- a/sound/soc/codecs/adau7002.c
+++ b/sound/soc/codecs/adau7002.c
@@ -100,11 +100,6 @@ static int adau7002_probe(struct platform_device *pdev)
&adau7002_dai, 1);
}
-static int adau7002_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
#ifdef CONFIG_OF
static const struct of_device_id adau7002_dt_ids[] = {
{ .compatible = "adi,adau7002", },
@@ -128,7 +123,6 @@ static struct platform_driver adau7002_driver = {
.acpi_match_table = ACPI_PTR(adau7002_acpi_match),
},
.probe = adau7002_probe,
- .remove = adau7002_remove,
};
module_platform_driver(adau7002_driver);
diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c
new file mode 100644
index 00000000000000..afdce6b7fa264b
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395.c
@@ -0,0 +1,579 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395.c -- ALSA SoC AW88395 codec support
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw88395.h"
+#include "aw88395_device.h"
+#include "aw88395_lib.h"
+#include "aw88395_reg.h"
+
+static const struct regmap_config aw88395_remap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88395_REG_MAX - 1,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static void aw88395_start_pa(struct aw88395 *aw88395)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88395_START_RETRIES; i++) {
+ ret = aw88395_dev_start(aw88395->aw_pa);
+ if (ret) {
+ dev_err(aw88395->aw_pa->dev, "aw88395 device start failed. retry = %d", i);
+ ret = aw88395_dev_fw_update(aw88395->aw_pa, AW88395_DSP_FW_UPDATE_ON, true);
+ if (ret < 0) {
+ dev_err(aw88395->aw_pa->dev, "fw update failed");
+ continue;
+ }
+ } else {
+ dev_info(aw88395->aw_pa->dev, "start success\n");
+ break;
+ }
+ }
+}
+
+static void aw88395_startup_work(struct work_struct *work)
+{
+ struct aw88395 *aw88395 =
+ container_of(work, struct aw88395, start_work.work);
+
+ mutex_lock(&aw88395->lock);
+ aw88395_start_pa(aw88395);
+ mutex_unlock(&aw88395->lock);
+}
+
+static void aw88395_start(struct aw88395 *aw88395, bool sync_start)
+{
+ int ret;
+
+ if (aw88395->aw_pa->fw_status != AW88395_DEV_FW_OK)
+ return;
+
+ if (aw88395->aw_pa->status == AW88395_DEV_PW_ON)
+ return;
+
+ ret = aw88395_dev_fw_update(aw88395->aw_pa, AW88395_DSP_FW_UPDATE_OFF, true);
+ if (ret < 0) {
+ dev_err(aw88395->aw_pa->dev, "fw update failed.");
+ return;
+ }
+
+ if (sync_start == AW88395_SYNC_START)
+ aw88395_start_pa(aw88395);
+ else
+ queue_delayed_work(system_wq,
+ &aw88395->start_work,
+ AW88395_START_WORK_DELAY_MS);
+}
+
+static struct snd_soc_dai_driver aw88395_dai[] = {
+ {
+ .name = "aw88395-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88395_RATES,
+ .formats = AW88395_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88395_RATES,
+ .formats = AW88395_FORMATS,
+ },
+ },
+};
+
+static int aw88395_get_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88395->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+ return 0;
+}
+
+static int aw88395_set_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88395->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_in_time) {
+ aw_dev->fade_in_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88395_get_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88395->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+ return 0;
+}
+
+static int aw88395_set_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88395->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_out_time) {
+ aw_dev->fade_out_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88395_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ const char *prof_name;
+ char *name;
+ int count;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw88395_dev_get_profile_count(aw88395->aw_pa);
+ if (count <= 0) {
+ uinfo->value.enumerated.items = 0;
+ return 0;
+ }
+
+ uinfo->value.enumerated.items = count;
+
+ if (uinfo->value.enumerated.item >= count)
+ uinfo->value.enumerated.item = count - 1;
+
+ name = uinfo->value.enumerated.name;
+ count = uinfo->value.enumerated.item;
+
+ prof_name = aw88395_dev_get_prof_name(aw88395->aw_pa, count);
+ if (!prof_name) {
+ strscpy(uinfo->value.enumerated.name, "null",
+ strlen("null") + 1);
+ return 0;
+ }
+
+ strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name));
+
+ return 0;
+}
+
+static int aw88395_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88395_dev_get_profile_index(aw88395->aw_pa);
+
+ return 0;
+}
+
+static int aw88395_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ /* pa stop or stopping just set profile */
+ mutex_lock(&aw88395->lock);
+ ret = aw88395_dev_set_profile_index(aw88395->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret < 0) {
+ dev_dbg(codec->dev, "profile index does not change");
+ mutex_unlock(&aw88395->lock);
+ return 0;
+ }
+
+ if (aw88395->aw_pa->status) {
+ aw88395_dev_stop(aw88395->aw_pa);
+ aw88395_start(aw88395, AW88395_SYNC_START);
+ }
+
+ mutex_unlock(&aw88395->lock);
+
+ return 1;
+}
+
+static int aw88395_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88395->aw_pa->volume_desc;
+
+ ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+ return 0;
+}
+
+static int aw88395_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88395->aw_pa->volume_desc;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (vol_desc->ctl_volume != value) {
+ vol_desc->ctl_volume = value;
+ aw88395_dev_set_volume(aw88395->aw_pa, vol_desc->ctl_volume);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88395_get_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88395->aw_pa->fade_step;
+
+ return 0;
+}
+
+static int aw88395_set_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (aw88395->aw_pa->fade_step != value) {
+ aw88395->aw_pa->fade_step = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88395_re_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88395->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_re;
+
+ return 0;
+}
+
+static int aw88395_re_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88395->aw_pa;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (aw_dev->cali_desc.cali_re != value) {
+ aw_dev->cali_desc.cali_re = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new aw88395_controls[] = {
+ SOC_SINGLE_EXT("PCM Playback Volume", AW88395_SYSCTRL2_REG,
+ 6, AW88395_MUTE_VOL, 0, aw88395_volume_get,
+ aw88395_volume_set),
+ SOC_SINGLE_EXT("Fade Step", 0, 0, AW88395_MUTE_VOL, 0,
+ aw88395_get_fade_step, aw88395_set_fade_step),
+ SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88395_get_fade_in_time, aw88395_set_fade_in_time),
+ SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88395_get_fade_out_time, aw88395_set_fade_out_time),
+ SOC_SINGLE_EXT("Calib", 0, 0, 100, 0,
+ aw88395_re_get, aw88395_re_set),
+ AW88395_PROFILE_EXT("Profile Set", aw88395_profile_info,
+ aw88395_profile_get, aw88395_profile_set),
+};
+
+static int aw88395_playback_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&aw88395->lock);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ aw88395_start(aw88395, AW88395_ASYNC_START);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ aw88395_dev_stop(aw88395->aw_pa);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&aw88395->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88395_dapm_widgets[] = {
+ /* playback */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0,
+ aw88395_playback_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("DAC Output"),
+
+ /* capture */
+ SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_INPUT("ADC Input"),
+};
+
+static const struct snd_soc_dapm_route aw88395_audio_map[] = {
+ {"DAC Output", NULL, "AIF_RX"},
+ {"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88395_codec_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ INIT_DELAYED_WORK(&aw88395->start_work, aw88395_startup_work);
+
+ /* add widgets */
+ ret = snd_soc_dapm_new_controls(dapm, aw88395_dapm_widgets,
+ ARRAY_SIZE(aw88395_dapm_widgets));
+ if (ret < 0)
+ return ret;
+
+ /* add route */
+ ret = snd_soc_dapm_add_routes(dapm, aw88395_audio_map,
+ ARRAY_SIZE(aw88395_audio_map));
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_add_component_controls(component, aw88395_controls,
+ ARRAY_SIZE(aw88395_controls));
+
+ return ret;
+}
+
+static void aw88395_codec_remove(struct snd_soc_component *aw_codec)
+{
+ struct aw88395 *aw88395 = snd_soc_component_get_drvdata(aw_codec);
+
+ cancel_delayed_work_sync(&aw88395->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88395 = {
+ .probe = aw88395_codec_probe,
+ .remove = aw88395_codec_remove,
+};
+
+static struct aw88395 *aw88395_malloc_init(struct i2c_client *i2c)
+{
+ struct aw88395 *aw88395 = devm_kzalloc(&i2c->dev,
+ sizeof(struct aw88395), GFP_KERNEL);
+ if (!aw88395)
+ return NULL;
+
+ mutex_init(&aw88395->lock);
+
+ return aw88395;
+}
+
+static void aw88395_hw_reset(struct aw88395 *aw88395)
+{
+ if (aw88395->reset_gpio) {
+ gpiod_set_value_cansleep(aw88395->reset_gpio, 0);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 10);
+ gpiod_set_value_cansleep(aw88395->reset_gpio, 1);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 10);
+ } else {
+ dev_err(aw88395->aw_pa->dev, "%s failed", __func__);
+ }
+}
+
+static int aw88395_request_firmware_file(struct aw88395 *aw88395)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw88395->aw_pa->fw_status = AW88395_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW88395_ACF_FILE, aw88395->aw_pa->dev);
+ if ((ret < 0) || (!cont)) {
+ dev_err(aw88395->aw_pa->dev, "load [%s] failed!", AW88395_ACF_FILE);
+ return ret;
+ }
+
+ dev_info(aw88395->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW88395_ACF_FILE, cont ? cont->size : 0);
+
+ aw88395->aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
+ if (!aw88395->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+ aw88395->aw_cfg->len = (int)cont->size;
+ memcpy(aw88395->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw88395->aw_pa, aw88395->aw_cfg);
+ if (ret < 0) {
+ dev_err(aw88395->aw_pa->dev, "Load [%s] failed ....!", AW88395_ACF_FILE);
+ return ret;
+ }
+
+ dev_dbg(aw88395->aw_pa->dev, "%s : bin load success\n", __func__);
+
+ mutex_lock(&aw88395->lock);
+ /* aw device init */
+ ret = aw88395_dev_init(aw88395->aw_pa, aw88395->aw_cfg);
+ if (ret < 0)
+ dev_err(aw88395->aw_pa->dev, "dev init failed");
+ mutex_unlock(&aw88395->lock);
+
+ return ret;
+}
+
+static int aw88395_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw88395 *aw88395;
+ int ret;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) {
+ dev_err(&i2c->dev, "check_functionality failed");
+ return -EIO;
+ }
+
+ aw88395 = aw88395_malloc_init(i2c);
+ if (!aw88395) {
+ dev_err(&i2c->dev, "malloc aw88395 failed");
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, aw88395);
+
+ aw88395->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(aw88395->reset_gpio))
+ dev_info(&i2c->dev, "reset gpio not defined\n");
+
+ /* hardware reset */
+ aw88395_hw_reset(aw88395);
+
+ aw88395->regmap = devm_regmap_init_i2c(i2c, &aw88395_remap_config);
+ if (IS_ERR(aw88395->regmap)) {
+ ret = PTR_ERR(aw88395->regmap);
+ dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+ return ret;
+ }
+
+ /* aw pa init */
+ ret = aw88395_init(&aw88395->aw_pa, i2c, aw88395->regmap);
+ if (ret < 0)
+ return ret;
+
+ ret = aw88395_request_firmware_file(aw88395);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "%s failed\n", __func__);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw88395,
+ aw88395_dai, ARRAY_SIZE(aw88395_dai));
+ if (ret < 0) {
+ dev_err(&i2c->dev, "failed to register aw88395: %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id aw88395_i2c_id[] = {
+ { AW88395_I2C_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88395_i2c_id);
+
+static struct i2c_driver aw88395_i2c_driver = {
+ .driver = {
+ .name = AW88395_I2C_NAME,
+ },
+ .probe_new = aw88395_i2c_probe,
+ .id_table = aw88395_i2c_id,
+};
+module_i2c_driver(aw88395_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88395 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88395/aw88395.h b/sound/soc/codecs/aw88395/aw88395.h
new file mode 100644
index 00000000000000..8036ba27f68d85
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395.h
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395.h -- ALSA SoC AW88395 codec support
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#ifndef __AW88395_H__
+#define __AW88395_H__
+
+#define AW88395_CHIP_ID_REG (0x00)
+#define AW88395_START_RETRIES (5)
+#define AW88395_START_WORK_DELAY_MS (0)
+
+#define AW88395_DSP_16_DATA_MASK (0x0000ffff)
+
+#define AW88395_I2C_NAME "aw88395_smartpa"
+
+#define AW88395_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88395_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define FADE_TIME_MAX 100000
+#define FADE_TIME_MIN 0
+
+#define AW88395_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = profile_info, \
+ .get = profile_get, \
+ .put = profile_set, \
+}
+
+enum {
+ AW88395_SYNC_START = 0,
+ AW88395_ASYNC_START,
+};
+
+enum {
+ AW88395_STREAM_CLOSE = 0,
+ AW88395_STREAM_OPEN,
+};
+
+struct aw88395 {
+ struct aw_device *aw_pa;
+ struct mutex lock;
+ struct gpio_desc *reset_gpio;
+ struct delayed_work start_work;
+ struct regmap *regmap;
+ struct aw_container *aw_cfg;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395_data_type.h b/sound/soc/codecs/aw88395/aw88395_data_type.h
new file mode 100644
index 00000000000000..e7aa56178b3687
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_data_type.h
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw883_data_type.h -- The data type of the AW88395 chip
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#ifndef __AW88395_DATA_TYPE_H__
+#define __AW88395_DATA_TYPE_H__
+
+#define PROJECT_NAME_MAX (24)
+#define CUSTOMER_NAME_MAX (16)
+#define CFG_VERSION_MAX (4)
+#define DEV_NAME_MAX (16)
+#define PROFILE_STR_MAX (32)
+
+#define ACF_FILE_ID (0xa15f908)
+
+enum aw_cfg_hdr_version {
+ AW88395_CFG_HDR_VER = 0x00000001,
+ AW88395_CFG_HDR_VER_V1 = 0x01000000,
+};
+
+enum aw_cfg_dde_type {
+ AW88395_DEV_NONE_TYPE_ID = 0xFFFFFFFF,
+ AW88395_DEV_TYPE_ID = 0x00000000,
+ AW88395_SKT_TYPE_ID = 0x00000001,
+ AW88395_DEV_DEFAULT_TYPE_ID = 0x00000002,
+};
+
+enum aw_sec_type {
+ ACF_SEC_TYPE_REG = 0,
+ ACF_SEC_TYPE_DSP,
+ ACF_SEC_TYPE_DSP_CFG,
+ ACF_SEC_TYPE_DSP_FW,
+ ACF_SEC_TYPE_HDR_REG,
+ ACF_SEC_TYPE_HDR_DSP_CFG,
+ ACF_SEC_TYPE_HDR_DSP_FW,
+ ACF_SEC_TYPE_MULTIPLE_BIN,
+ ACF_SEC_TYPE_SKT_PROJECT,
+ ACF_SEC_TYPE_DSP_PROJECT,
+ ACF_SEC_TYPE_MONITOR,
+ ACF_SEC_TYPE_MAX,
+};
+
+enum profile_data_type {
+ AW88395_DATA_TYPE_REG = 0,
+ AW88395_DATA_TYPE_DSP_CFG,
+ AW88395_DATA_TYPE_DSP_FW,
+ AW88395_DATA_TYPE_MAX,
+};
+
+enum aw_prof_type {
+ AW88395_PROFILE_MUSIC = 0,
+ AW88395_PROFILE_VOICE,
+ AW88395_PROFILE_VOIP,
+ AW88395_PROFILE_RINGTONE,
+ AW88395_PROFILE_RINGTONE_HS,
+ AW88395_PROFILE_LOWPOWER,
+ AW88395_PROFILE_BYPASS,
+ AW88395_PROFILE_MMI,
+ AW88395_PROFILE_FM,
+ AW88395_PROFILE_NOTIFICATION,
+ AW88395_PROFILE_RECEIVER,
+ AW88395_PROFILE_MAX,
+};
+
+enum aw_profile_status {
+ AW88395_PROFILE_WAIT = 0,
+ AW88395_PROFILE_OK,
+};
+
+struct aw_cfg_hdr {
+ u32 id;
+ char project[PROJECT_NAME_MAX];
+ char custom[CUSTOMER_NAME_MAX];
+ char version[CFG_VERSION_MAX];
+ u32 author_id;
+ u32 ddt_size;
+ u32 ddt_num;
+ u32 hdr_offset;
+ u32 hdr_version;
+ u32 reserved[3];
+};
+
+struct aw_cfg_dde {
+ u32 type;
+ char dev_name[DEV_NAME_MAX];
+ u16 dev_index;
+ u16 dev_bus;
+ u16 dev_addr;
+ u16 dev_profile;
+ u32 data_type;
+ u32 data_size;
+ u32 data_offset;
+ u32 data_crc;
+ u32 reserved[5];
+};
+
+struct aw_cfg_dde_v1 {
+ u32 type;
+ char dev_name[DEV_NAME_MAX];
+ u16 dev_index;
+ u16 dev_bus;
+ u16 dev_addr;
+ u16 dev_profile;
+ u32 data_type;
+ u32 data_size;
+ u32 data_offset;
+ u32 data_crc;
+ char dev_profile_str[PROFILE_STR_MAX];
+ u32 chip_id;
+ u32 reserved[4];
+};
+
+struct aw_sec_data_desc {
+ u32 len;
+ u8 *data;
+};
+
+struct aw_prof_desc {
+ u32 id;
+ u32 prof_st;
+ char *prf_str;
+ u32 fw_ver;
+ struct aw_sec_data_desc sec_desc[AW88395_DATA_TYPE_MAX];
+};
+
+struct aw_all_prof_info {
+ struct aw_prof_desc prof_desc[AW88395_PROFILE_MAX];
+};
+
+struct aw_prof_info {
+ int count;
+ int prof_type;
+ char **prof_name_list;
+ struct aw_prof_desc *prof_desc;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c
new file mode 100644
index 00000000000000..33eda37414644e
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_device.c
@@ -0,0 +1,1748 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395_device.c -- AW88395 function for ALSA Audio Driver
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+// Author: Ben Yi <yijiangtao@awinic.com>
+//
+
+#include <linux/crc32.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include "aw88395_device.h"
+#include "aw88395_reg.h"
+
+static int aw_dev_dsp_write_16bit(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int dsp_data)
+{
+ int ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)dsp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write data error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw_dev_dsp_write_32bit(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int dsp_data)
+{
+ u16 temp_data;
+ int ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ temp_data = dsp_data & AW88395_DSP_16_DATA_MASK;
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write datal error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ temp_data = dsp_data >> 16;
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG, (u16)temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write datah error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw_dev_dsp_write(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type)
+{
+ u32 reg_value;
+ int ret;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ switch (data_type) {
+ case AW88395_DSP_16_DATA:
+ ret = aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "write dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed",
+ (u32)dsp_addr, dsp_data);
+ break;
+ case AW88395_DSP_32_DATA:
+ ret = aw_dev_dsp_write_32bit(aw_dev, dsp_addr, dsp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "write dsp_addr[0x%x] 32-bit dsp_data[0x%x] failed",
+ (u32)dsp_addr, dsp_data);
+ break;
+ default:
+ dev_err(aw_dev->dev, "data type[%d] unsupported", data_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ /* clear dsp chip select state*/
+ if (regmap_read(aw_dev->regmap, AW88395_ID_REG, &reg_value))
+ dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret);
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return ret;
+}
+
+static int aw_dev_dsp_read_16bit(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int *dsp_data)
+{
+ unsigned int temp_data;
+ int ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret);
+ return ret;
+ }
+ *dsp_data = temp_data;
+
+ return 0;
+}
+
+static int aw_dev_dsp_read_32bit(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int *dsp_data)
+{
+ unsigned int temp_data;
+ int ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, dsp_addr);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret);
+ return ret;
+ }
+ *dsp_data = temp_data;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_DSPMDAT_REG, &temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s read error, ret=%d", __func__, ret);
+ return ret;
+ }
+ *dsp_data |= (temp_data << 16);
+
+ return 0;
+}
+
+static int aw_dev_dsp_read(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int *dsp_data, unsigned char data_type)
+{
+ u32 reg_value;
+ int ret;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ switch (data_type) {
+ case AW88395_DSP_16_DATA:
+ ret = aw_dev_dsp_read_16bit(aw_dev, dsp_addr, dsp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "read dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed",
+ (u32)dsp_addr, *dsp_data);
+ break;
+ case AW88395_DSP_32_DATA:
+ ret = aw_dev_dsp_read_32bit(aw_dev, dsp_addr, dsp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "read dsp_addr[0x%x] 32r-bit dsp_data[0x%x] failed",
+ (u32)dsp_addr, *dsp_data);
+ break;
+ default:
+ dev_err(aw_dev->dev, "data type[%d] unsupported", data_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ /* clear dsp chip select state*/
+ if (regmap_read(aw_dev->regmap, AW88395_ID_REG, &reg_value))
+ dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret);
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return ret;
+}
+
+
+static int aw_dev_read_chipid(struct aw_device *aw_dev, u16 *chip_id)
+{
+ int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_CHIP_ID_REG, &reg_val);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s read chipid error. ret = %d", __func__, ret);
+ return ret;
+ }
+
+ dev_info(aw_dev->dev, "chip id = %x\n", reg_val);
+ *chip_id = reg_val;
+
+ return 0;
+}
+
+static unsigned int reg_val_to_db(unsigned int value)
+{
+ return (((value >> AW88395_VOL_6DB_START) * AW88395_VOLUME_STEP_DB) +
+ ((value & 0x3f) % AW88395_VOLUME_STEP_DB));
+}
+
+static unsigned short db_to_reg_val(unsigned short value)
+{
+ return (((value / AW88395_VOLUME_STEP_DB) << AW88395_VOL_6DB_START) +
+ (value % AW88395_VOLUME_STEP_DB));
+}
+
+static int aw_dev_dsp_fw_check(struct aw_device *aw_dev)
+{
+ struct aw_sec_data_desc *dsp_fw_desc;
+ struct aw_prof_desc *set_prof_desc;
+ u16 base_addr = AW88395_DSP_FW_ADDR;
+ u16 addr = base_addr;
+ u32 dsp_val;
+ u16 bin_val;
+ int ret, i;
+
+ ret = aw88395_dev_get_prof_data(aw_dev, aw_dev->prof_cur, &set_prof_desc);
+ if (ret)
+ return ret;
+
+ /* update reg */
+ dsp_fw_desc = &set_prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW];
+
+ for (i = 0; i < AW88395_FW_CHECK_PART; i++) {
+ ret = aw_dev_dsp_read(aw_dev, addr, &dsp_val, AW88395_DSP_16_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp read failed");
+ return ret;
+ }
+
+ bin_val = be16_to_cpup((void *)&dsp_fw_desc->data[2 * (addr - base_addr)]);
+
+ if (dsp_val != bin_val) {
+ dev_err(aw_dev->dev, "fw check failed, addr[0x%x], read[0x%x] != bindata[0x%x]",
+ addr, dsp_val, bin_val);
+ return -EINVAL;
+ }
+
+ addr += (dsp_fw_desc->len / 2) / AW88395_FW_CHECK_PART;
+ if ((addr - base_addr) > dsp_fw_desc->len) {
+ dev_err(aw_dev->dev, "fw check failed, addr[0x%x] too large", addr);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int aw_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
+{
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int reg_value;
+ u16 real_value, volume;
+ int ret;
+
+ volume = min((value + vol_desc->init_volume), (unsigned int)AW88395_MUTE_VOL);
+ real_value = db_to_reg_val(volume);
+
+ /* cal real value */
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL2_REG, &reg_value);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value);
+
+ /* [15 : 6] volume */
+ real_value = (real_value << AW88395_VOL_START_BIT) | (reg_value & AW88395_VOL_MASK);
+
+ /* write value */
+ ret = regmap_write(aw_dev->regmap, AW88395_SYSCTRL2_REG, real_value);
+
+ return ret;
+}
+
+void aw88395_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol)
+{
+ int ret;
+
+ ret = aw_dev_set_volume(aw_dev, set_vol);
+ if (ret)
+ dev_dbg(aw_dev->dev, "set volume failed");
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_set_volume);
+
+static void aw_dev_fade_in(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ u16 fade_in_vol = desc->ctl_volume;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (!aw_dev->fade_en)
+ return;
+
+ if (fade_step == 0 || aw_dev->fade_in_time == 0) {
+ aw_dev_set_volume(aw_dev, fade_in_vol);
+ return;
+ }
+
+ for (i = AW88395_MUTE_VOL; i >= fade_in_vol; i -= fade_step) {
+ aw_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10);
+ }
+
+ if (i != fade_in_vol)
+ aw_dev_set_volume(aw_dev, fade_in_vol);
+}
+
+static void aw_dev_fade_out(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (!aw_dev->fade_en)
+ return;
+
+ if (fade_step == 0 || aw_dev->fade_out_time == 0) {
+ aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL);
+ return;
+ }
+
+ for (i = desc->ctl_volume; i <= AW88395_MUTE_VOL; i += fade_step) {
+ aw_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+
+ if (i != AW88395_MUTE_VOL) {
+ aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+}
+
+static int aw_dev_modify_dsp_cfg(struct aw_device *aw_dev,
+ unsigned int addr, unsigned int dsp_data, unsigned char data_type)
+{
+ struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg;
+ unsigned int addr_offset;
+ __le16 data1;
+ __le32 data2;
+
+ dev_dbg(aw_dev->dev, "addr:0x%x, dsp_data:0x%x", addr, dsp_data);
+
+ addr_offset = (addr - AW88395_DSP_CFG_ADDR) * 2;
+ if (addr_offset > crc_dsp_cfg->len) {
+ dev_err(aw_dev->dev, "addr_offset[%d] > crc_dsp_cfg->len[%d]",
+ addr_offset, crc_dsp_cfg->len);
+ return -EINVAL;
+ }
+ switch (data_type) {
+ case AW88395_DSP_16_DATA:
+ data1 = cpu_to_le16((u16)dsp_data);
+ memcpy(crc_dsp_cfg->data + addr_offset, (u8 *)&data1, 2);
+ break;
+ case AW88395_DSP_32_DATA:
+ data2 = cpu_to_le32(dsp_data);
+ memcpy(crc_dsp_cfg->data + addr_offset, (u8 *)&data2, 4);
+ break;
+ default:
+ dev_err(aw_dev->dev, "data type[%d] unsupported", data_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_dsp_set_cali_re(struct aw_device *aw_dev)
+{
+ u32 cali_re;
+ int ret;
+
+ cali_re = AW88395_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re +
+ aw_dev->cali_desc.ra), AW88395_DSP_RE_SHIFT);
+
+ /* set cali re to device */
+ ret = aw_dev_dsp_write(aw_dev,
+ AW88395_DSP_REG_CFG_ADPZ_RE, cali_re, AW88395_DSP_32_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "set cali re error");
+ return ret;
+ }
+
+ ret = aw_dev_modify_dsp_cfg(aw_dev, AW88395_DSP_REG_CFG_ADPZ_RE,
+ cali_re, AW88395_DSP_32_DATA);
+ if (ret)
+ dev_err(aw_dev->dev, "modify dsp cfg failed");
+
+ return ret;
+}
+
+static void aw_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
+{
+ int ret;
+
+ if (flag) {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_I2SCFG1_REG,
+ ~AW88395_I2STXEN_MASK, AW88395_I2STXEN_ENABLE_VALUE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_I2SCFG1_REG,
+ ~AW88395_I2STXEN_MASK, AW88395_I2STXEN_DISABLE_VALUE);
+ }
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static int aw_dev_dsp_set_crc32(struct aw_device *aw_dev)
+{
+ struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg;
+ u32 crc_value, crc_data_len;
+
+ /* get crc data len */
+ crc_data_len = (AW88395_DSP_REG_CRC_ADDR - AW88395_DSP_CFG_ADDR) * 2;
+ if (crc_data_len > crc_dsp_cfg->len) {
+ dev_err(aw_dev->dev, "crc data len :%d > cfg_data len:%d",
+ crc_data_len, crc_dsp_cfg->len);
+ return -EINVAL;
+ }
+
+ if (crc_data_len & 0x11) {
+ dev_err(aw_dev->dev, "The crc data len :%d unsupport", crc_data_len);
+ return -EINVAL;
+ }
+
+ crc_value = __crc32c_le(0xFFFFFFFF, crc_dsp_cfg->data, crc_data_len) ^ 0xFFFFFFFF;
+
+ return aw_dev_dsp_write(aw_dev, AW88395_DSP_REG_CRC_ADDR, crc_value,
+ AW88395_DSP_32_DATA);
+}
+
+static void aw_dev_dsp_check_crc_enable(struct aw_device *aw_dev, bool flag)
+{
+ int ret;
+
+ if (flag) {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_HAGCCFG7_REG,
+ ~AW88395_AGC_DSP_CTL_MASK, AW88395_AGC_DSP_CTL_ENABLE_VALUE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_HAGCCFG7_REG,
+ ~AW88395_AGC_DSP_CTL_MASK, AW88395_AGC_DSP_CTL_DISABLE_VALUE);
+ }
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static int aw_dev_dsp_check_st(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+ int i;
+
+ for (i = 0; i < AW88395_DSP_ST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, &reg_val);
+ if (ret) {
+ dev_err(aw_dev->dev, "read reg0x%x failed", AW88395_SYSST_REG);
+ continue;
+ }
+
+ if ((reg_val & (~AW88395_DSPS_MASK)) != AW88395_DSPS_NORMAL_VALUE) {
+ dev_err(aw_dev->dev, "check dsp st fail,reg_val:0x%04x", reg_val);
+ ret = -EPERM;
+ continue;
+ } else {
+ dev_dbg(aw_dev->dev, "dsp st check ok, reg_val:0x%04x", reg_val);
+ return 0;
+ }
+ }
+
+ return ret;
+}
+
+static void aw_dev_dsp_enable(struct aw_device *aw_dev, bool is_enable)
+{
+ int ret;
+
+ if (is_enable) {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_DSPBY_MASK, AW88395_DSPBY_WORKING_VALUE);
+ if (ret)
+ dev_dbg(aw_dev->dev, "enable dsp failed");
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_DSPBY_MASK, AW88395_DSPBY_BYPASS_VALUE);
+ if (ret)
+ dev_dbg(aw_dev->dev, "disable dsp failed");
+ }
+}
+
+static int aw_dev_dsp_check_crc32(struct aw_device *aw_dev)
+{
+ int ret;
+
+ if (aw_dev->dsp_cfg == AW88395_DEV_DSP_BYPASS) {
+ dev_info(aw_dev->dev, "dsp bypass");
+ return 0;
+ }
+
+ ret = aw_dev_dsp_set_crc32(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "set dsp crc32 failed");
+ return ret;
+ }
+
+ aw_dev_dsp_check_crc_enable(aw_dev, true);
+
+ /* dsp enable */
+ aw_dev_dsp_enable(aw_dev, true);
+ usleep_range(AW88395_5000_US, AW88395_5000_US + 100);
+
+ ret = aw_dev_dsp_check_st(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "check crc32 fail");
+ } else {
+ aw_dev_dsp_check_crc_enable(aw_dev, false);
+ aw_dev->dsp_crc_st = AW88395_DSP_CRC_OK;
+ }
+
+ return ret;
+}
+
+static void aw_dev_pwd(struct aw_device *aw_dev, bool pwd)
+{
+ int ret;
+
+ if (pwd) {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_PWDN_MASK, AW88395_PWDN_POWER_DOWN_VALUE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_PWDN_MASK, AW88395_PWDN_WORKING_VALUE);
+ }
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ int ret;
+
+ if (amppd) {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_AMPPD_MASK, AW88395_AMPPD_POWER_DOWN_VALUE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_AMPPD_MASK, AW88395_AMPPD_WORKING_VALUE);
+ }
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+void aw88395_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+ int ret;
+
+ if (is_mute) {
+ aw_dev_fade_out(aw_dev);
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_HMUTE_MASK, AW88395_HMUTE_ENABLE_VALUE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_SYSCTRL_REG,
+ ~AW88395_HMUTE_MASK, AW88395_HMUTE_DISABLE_VALUE);
+ aw_dev_fade_in(aw_dev);
+ }
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_mute);
+
+static int aw_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk)
+{
+ unsigned int reg_val;
+ u16 reg_icalk;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_EFRM2_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_icalk = reg_val & (~AW88395_EF_ISN_GESLP_MASK);
+
+ if (reg_icalk & (~AW88395_EF_ISN_GESLP_SIGN_MASK))
+ reg_icalk = reg_icalk | AW88395_EF_ISN_GESLP_SIGN_NEG;
+
+ *icalk = (int16_t)reg_icalk;
+
+ return ret;
+}
+
+static int aw_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk)
+{
+ unsigned int reg_val;
+ u16 reg_vcalk;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_EFRH_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val = reg_val >> AW88395_EF_VSENSE_GAIN_SHIFT;
+
+ reg_vcalk = (u16)reg_val & (~AW88395_EF_VSN_GESLP_MASK);
+
+ if (reg_vcalk & (~AW88395_EF_VSN_GESLP_SIGN_MASK))
+ reg_vcalk = reg_vcalk | AW88395_EF_VSN_GESLP_SIGN_NEG;
+
+ *vcalk = (int16_t)reg_vcalk;
+
+ return ret;
+}
+
+static int aw_dev_get_vcalk_dac(struct aw_device *aw_dev, int16_t *vcalk)
+{
+ unsigned int reg_val;
+ u16 reg_vcalk;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_EFRM2_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_vcalk = reg_val >> AW88395_EF_DAC_GESLP_SHIFT;
+
+ if (reg_vcalk & AW88395_EF_DAC_GESLP_SIGN_MASK)
+ reg_vcalk = reg_vcalk | AW88395_EF_DAC_GESLP_SIGN_NEG;
+
+ *vcalk = (int16_t)reg_vcalk;
+
+ return ret;
+}
+
+static int aw_dev_vsense_select(struct aw_device *aw_dev, int *vsense_select)
+{
+ unsigned int vsense_reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_I2SCFG3_REG, &vsense_reg_val);
+ if (ret) {
+ dev_err(aw_dev->dev, "read vsense_reg_val failed");
+ return ret;
+ }
+ dev_dbg(aw_dev->dev, "vsense_reg = 0x%x", vsense_reg_val);
+
+ if (vsense_reg_val & (~AW88395_VDSEL_MASK)) {
+ *vsense_select = AW88395_DEV_VDSEL_VSENSE;
+ dev_dbg(aw_dev->dev, "vsense outside");
+ } else {
+ *vsense_select = AW88395_DEV_VDSEL_DAC;
+ dev_dbg(aw_dev->dev, "vsense inside");
+ }
+
+ return 0;
+}
+
+static int aw_dev_set_vcalb(struct aw_device *aw_dev)
+{
+ int16_t icalk_val, vcalk_val;
+ int icalk, vsense_select;
+ u32 vcalb_adj, reg_val;
+ int vcalb, vcalk;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_VCALB, &vcalb_adj, AW88395_DSP_16_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "read vcalb_adj failed");
+ return ret;
+ }
+
+ ret = aw_dev_vsense_select(aw_dev, &vsense_select);
+ if (ret)
+ return ret;
+ dev_dbg(aw_dev->dev, "vsense_select = %d", vsense_select);
+
+ ret = aw_dev_get_icalk(aw_dev, &icalk_val);
+ if (ret)
+ return ret;
+ icalk = AW88395_CABL_BASE_VALUE + AW88395_ICABLK_FACTOR * icalk_val;
+
+ switch (vsense_select) {
+ case AW88395_DEV_VDSEL_VSENSE:
+ ret = aw_dev_get_vcalk(aw_dev, &vcalk_val);
+ if (ret)
+ return ret;
+ vcalk = AW88395_CABL_BASE_VALUE + AW88395_VCABLK_FACTOR * vcalk_val;
+ vcalb = AW88395_VCAL_FACTOR * AW88395_VSCAL_FACTOR /
+ AW88395_ISCAL_FACTOR * icalk / vcalk * vcalb_adj;
+
+ dev_dbg(aw_dev->dev, "vcalk_factor=%d, vscal_factor=%d, icalk=%d, vcalk=%d",
+ AW88395_VCABLK_FACTOR, AW88395_VSCAL_FACTOR, icalk, vcalk);
+ break;
+ case AW88395_DEV_VDSEL_DAC:
+ ret = aw_dev_get_vcalk_dac(aw_dev, &vcalk_val);
+ if (ret)
+ return ret;
+ vcalk = AW88395_CABL_BASE_VALUE + AW88395_VCABLK_FACTOR_DAC * vcalk_val;
+ vcalb = AW88395_VCAL_FACTOR * AW88395_VSCAL_FACTOR_DAC /
+ AW88395_ISCAL_FACTOR * icalk / vcalk * vcalb_adj;
+
+ dev_dbg(aw_dev->dev, "vcalk_dac_factor=%d, vscal_dac_factor=%d, icalk=%d, vcalk=%d",
+ AW88395_VCABLK_FACTOR_DAC,
+ AW88395_VSCAL_FACTOR_DAC, icalk, vcalk);
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupport vsense status");
+ return -EINVAL;
+ }
+
+ if ((vcalk == 0) || (AW88395_ISCAL_FACTOR == 0)) {
+ dev_err(aw_dev->dev, "vcalk:%d or desc->iscal_factor:%d unsupported",
+ vcalk, AW88395_ISCAL_FACTOR);
+ return -EINVAL;
+ }
+
+ vcalb = vcalb >> AW88395_VCALB_ADJ_FACTOR;
+ reg_val = (u32)vcalb;
+
+ dev_dbg(aw_dev->dev, "vcalb=%d, reg_val=0x%x, vcalb_adj =0x%x",
+ vcalb, reg_val, vcalb_adj);
+
+ ret = aw_dev_dsp_write(aw_dev, AW88395_DSP_REG_VCALB, reg_val, AW88395_DSP_16_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "write vcalb failed");
+ return ret;
+ }
+
+ ret = aw_dev_modify_dsp_cfg(aw_dev, AW88395_DSP_REG_VCALB,
+ (u32)reg_val, AW88395_DSP_16_DATA);
+ if (ret)
+ dev_err(aw_dev->dev, "modify dsp cfg failed");
+
+ return ret;
+}
+
+static int aw_dev_get_cali_f0_delay(struct aw_device *aw_dev)
+{
+ struct aw_cali_delay_desc *desc = &aw_dev->cali_delay_desc;
+ u32 cali_delay;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev,
+ AW88395_DSP_CALI_F0_DELAY, &cali_delay, AW88395_DSP_16_DATA);
+ if (ret)
+ dev_err(aw_dev->dev, "read cali delay failed, ret=%d", ret);
+ else
+ desc->delay = AW88395_CALI_DELAY_CACL(cali_delay);
+
+ dev_dbg(aw_dev->dev, "read cali delay: %d ms", desc->delay);
+
+ return ret;
+}
+
+static void aw_dev_get_int_status(struct aw_device *aw_dev, unsigned short *int_status)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSINT_REG, &reg_val);
+ if (ret)
+ dev_err(aw_dev->dev, "read interrupt reg fail, ret=%d", ret);
+ else
+ *int_status = reg_val;
+
+ dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", *int_status);
+}
+
+static void aw_dev_clear_int_status(struct aw_device *aw_dev)
+{
+ u16 int_status;
+
+ /* read int status and clear */
+ aw_dev_get_int_status(aw_dev, &int_status);
+ /* make sure int status is clear */
+ aw_dev_get_int_status(aw_dev, &int_status);
+ if (int_status)
+ dev_info(aw_dev->dev, "int status(%d) is not cleaned.\n", int_status);
+}
+
+static int aw_dev_get_iis_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, &reg_val);
+ if (ret)
+ return -EIO;
+ if ((reg_val & AW88395_BIT_PLL_CHECK) != AW88395_BIT_PLL_CHECK) {
+ dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_check_mode1_pll(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "mode1 iis signal check error");
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static int aw_dev_check_mode2_pll(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_PLLCTRL1_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val &= (~AW88395_CCO_MUX_MASK);
+ if (reg_val == AW88395_CCO_MUX_DIVIDED_VALUE) {
+ dev_dbg(aw_dev->dev, "CCO_MUX is already divider");
+ return -EPERM;
+ }
+
+ /* change mode2 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_PLLCTRL1_REG,
+ ~AW88395_CCO_MUX_MASK, AW88395_CCO_MUX_DIVIDED_VALUE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 iis signal check error");
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+
+ /* change mode1 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_PLLCTRL1_REG,
+ ~AW88395_CCO_MUX_MASK, AW88395_CCO_MUX_BYPASS_VALUE);
+ if (ret == 0) {
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_check_mode1_pll(aw_dev);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error");
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int aw_dev_check_syspll(struct aw_device *aw_dev)
+{
+ int ret;
+
+ ret = aw_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check");
+ ret = aw_dev_check_mode2_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 check iis failed");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int aw_dev_check_sysst(struct aw_device *aw_dev)
+{
+ unsigned int check_val;
+ unsigned int reg_val;
+ int ret, i;
+
+ for (i = 0; i < AW88395_DEV_SYSST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = reg_val & (~AW88395_BIT_SYSST_CHECK_MASK)
+ & AW88395_BIT_SYSST_CHECK;
+ if (check_val != AW88395_BIT_SYSST_CHECK) {
+ dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x",
+ i, reg_val, AW88395_BIT_SYSST_CHECK);
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static int aw_dev_check_sysint(struct aw_device *aw_dev)
+{
+ u16 reg_val;
+
+ aw_dev_get_int_status(aw_dev, &reg_val);
+
+ if (reg_val & AW88395_BIT_SYSINT_CHECK) {
+ dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void aw_dev_get_cur_mode_st(struct aw_device *aw_dev)
+{
+ struct aw_profctrl_desc *profctrl_desc = &aw_dev->profctrl_desc;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL_REG, &reg_val);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+ return;
+ }
+ if ((reg_val & (~AW88395_RCV_MODE_MASK)) == AW88395_RCV_MODE_RECEIVER_VALUE)
+ profctrl_desc->cur_mode = AW88395_RCV_MODE;
+ else
+ profctrl_desc->cur_mode = AW88395_NOT_RCV_MODE;
+}
+
+static void aw_dev_get_dsp_config(struct aw_device *aw_dev, unsigned char *dsp_cfg)
+{
+ unsigned int reg_val = 0;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_SYSCTRL_REG, &reg_val);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+ return;
+ }
+ if (reg_val & (~AW88395_DSPBY_MASK))
+ *dsp_cfg = AW88395_DEV_DSP_BYPASS;
+ else
+ *dsp_cfg = AW88395_DEV_DSP_WORK;
+}
+
+static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag)
+{
+ int ret;
+
+ switch (flag) {
+ case AW88395_DEV_MEMCLK_PLL:
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_DBGCTRL_REG,
+ ~AW88395_MEM_CLKSEL_MASK,
+ AW88395_MEM_CLKSEL_DAP_HCLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select pll failed");
+ break;
+ case AW88395_DEV_MEMCLK_OSC:
+ ret = regmap_update_bits(aw_dev->regmap, AW88395_DBGCTRL_REG,
+ ~AW88395_MEM_CLKSEL_MASK,
+ AW88395_MEM_CLKSEL_OSC_CLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select OSC failed");
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x", flag);
+ break;
+ }
+}
+
+static int aw_dev_get_dsp_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88395_WDT_REG, &reg_val);
+ if (ret)
+ return ret;
+ if (!(reg_val & (~AW88395_WDT_CNT_MASK)))
+ ret = -EPERM;
+
+ return ret;
+}
+
+static int aw_dev_get_vmax(struct aw_device *aw_dev, unsigned int *vmax)
+{
+ return aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_VMAX, vmax, AW88395_DSP_16_DATA);
+}
+
+static int aw_dev_update_reg_container(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int read_val;
+ int16_t *reg_data;
+ int data_len;
+ u16 read_vol;
+ u16 reg_val;
+ u8 reg_addr;
+ int i, ret;
+
+ reg_data = (int16_t *)data;
+ data_len = len >> 1;
+
+ if (data_len & 0x1) {
+ dev_err(aw_dev->dev, "data len:%d unsupported", data_len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < data_len; i += 2) {
+ reg_addr = reg_data[i];
+ reg_val = reg_data[i + 1];
+
+ if (reg_addr == AW88395_SYSCTRL_REG) {
+ ret = regmap_read(aw_dev->regmap, reg_addr, &read_val);
+ if (ret)
+ break;
+ read_val &= (~AW88395_HMUTE_MASK);
+ reg_val &= AW88395_HMUTE_MASK;
+ reg_val |= read_val;
+ }
+ if (reg_addr == AW88395_HAGCCFG7_REG)
+ reg_val &= AW88395_AGC_DSP_CTL_MASK;
+
+ if (reg_addr == AW88395_I2SCFG1_REG) {
+ /* close tx */
+ reg_val &= AW88395_I2STXEN_MASK;
+ reg_val |= AW88395_I2STXEN_DISABLE_VALUE;
+ }
+
+ if (reg_addr == AW88395_SYSCTRL2_REG) {
+ read_vol = (reg_val & (~AW88395_VOL_MASK)) >>
+ AW88395_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume =
+ reg_val_to_db(read_vol);
+ }
+ ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+ if (ret)
+ break;
+
+ }
+
+ aw_dev_get_cur_mode_st(aw_dev);
+
+ if (aw_dev->prof_cur != aw_dev->prof_index) {
+ /* clear control volume when PA change profile */
+ vol_desc->ctl_volume = 0;
+ } else {
+ /* keep control volume when PA start with sync mode */
+ aw_dev_set_volume(aw_dev, vol_desc->ctl_volume);
+ }
+
+ /* keep min volume */
+ if (aw_dev->fade_en)
+ aw_dev_set_volume(aw_dev, AW88395_MUTE_VOL);
+
+ aw_dev_get_dsp_config(aw_dev, &aw_dev->dsp_cfg);
+
+ return ret;
+}
+
+static int aw_dev_reg_update(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "reg data is null or len is 0");
+ return -EINVAL;
+ }
+
+ ret = aw_dev_update_reg_container(aw_dev, data, len);
+ if (ret) {
+ dev_err(aw_dev->dev, "reg update failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw_dev_get_ra(struct aw_cali_desc *cali_desc)
+{
+ struct aw_device *aw_dev =
+ container_of(cali_desc, struct aw_device, cali_desc);
+ u32 dsp_ra;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88395_DSP_REG_CFG_ADPZ_RA,
+ &dsp_ra, AW88395_DSP_32_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "read ra error");
+ return ret;
+ }
+
+ cali_desc->ra = AW88395_DSP_RE_TO_SHOW_RE(dsp_ra,
+ AW88395_DSP_RE_SHIFT);
+
+ return ret;
+}
+
+static int aw_dev_dsp_update_container(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len, unsigned short base)
+{
+ int i, ret;
+
+#ifdef AW88395_DSP_I2C_WRITES
+ u32 tmp_len;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base);
+ if (ret)
+ goto error_operation;
+
+ for (i = 0; i < len; i += AW88395_MAX_RAM_WRITE_BYTE_SIZE) {
+ if ((len - i) < AW88395_MAX_RAM_WRITE_BYTE_SIZE)
+ tmp_len = len - i;
+ else
+ tmp_len = AW88395_MAX_RAM_WRITE_BYTE_SIZE;
+
+ ret = regmap_raw_write(aw_dev->regmap, AW88395_DSPMDAT_REG,
+ &data[i], tmp_len);
+ if (ret)
+ goto error_operation;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+#else
+ __be16 reg_val;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ /* i2c write */
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, base);
+ if (ret)
+ goto error_operation;
+ for (i = 0; i < len; i += 2) {
+ reg_val = cpu_to_be16p((u16 *)(data + i));
+ ret = regmap_write(aw_dev->regmap, AW88395_DSPMDAT_REG,
+ (u16)reg_val);
+ if (ret)
+ goto error_operation;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+#endif
+
+ return 0;
+
+error_operation:
+ mutex_unlock(&aw_dev->dsp_lock);
+ return ret;
+}
+
+static int aw_dev_dsp_update_fw(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+
+ dev_dbg(aw_dev->dev, "dsp firmware len:%d", len);
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "dsp firmware data is null or len is 0");
+ return -EINVAL;
+ }
+ aw_dev_dsp_update_container(aw_dev, data, len, AW88395_DSP_FW_ADDR);
+ aw_dev->dsp_fw_len = len;
+
+ return 0;
+}
+
+static int aw_dev_copy_to_crc_dsp_cfg(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int size)
+{
+ struct aw_sec_data_desc *crc_dsp_cfg = &aw_dev->crc_dsp_cfg;
+
+ if (!crc_dsp_cfg->data) {
+ crc_dsp_cfg->data = devm_kzalloc(aw_dev->dev, size, GFP_KERNEL);
+ if (!crc_dsp_cfg->data)
+ return -ENOMEM;
+ crc_dsp_cfg->len = size;
+ } else if (crc_dsp_cfg->len < size) {
+ devm_kfree(aw_dev->dev, crc_dsp_cfg->data);
+ crc_dsp_cfg->data = devm_kzalloc(aw_dev->dev, size, GFP_KERNEL);
+ if (!crc_dsp_cfg->data)
+ return -ENOMEM;
+ crc_dsp_cfg->len = size;
+ }
+ memcpy(crc_dsp_cfg->data, data, size);
+ swab16_array((u16 *)crc_dsp_cfg->data, size >> 1);
+
+ return 0;
+}
+
+static int aw_dev_dsp_update_cfg(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ dev_dbg(aw_dev->dev, "dsp config len:%d", len);
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "dsp config data is null or len is 0");
+ return -EINVAL;
+ }
+
+ aw_dev_dsp_update_container(aw_dev, data, len, AW88395_DSP_CFG_ADDR);
+ aw_dev->dsp_cfg_len = len;
+
+ ret = aw_dev_copy_to_crc_dsp_cfg(aw_dev, data, len);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_set_vcalb(aw_dev);
+ if (ret)
+ return ret;
+ ret = aw_dev_get_ra(&aw_dev->cali_desc);
+ if (ret)
+ return ret;
+ ret = aw_dev_get_cali_f0_delay(aw_dev);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_get_vmax(aw_dev, &aw_dev->vmax_desc.init_vmax);
+ if (ret) {
+ dev_err(aw_dev->dev, "get vmax failed");
+ return ret;
+ }
+ dev_dbg(aw_dev->dev, "get init vmax:0x%x", aw_dev->vmax_desc.init_vmax);
+ aw_dev->dsp_crc_st = AW88395_DSP_CRC_NA;
+
+ return 0;
+}
+
+static int aw_dev_check_sram(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ /* check the odd bits of reg 0x40 */
+ regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_ODD_NUM_BIT_TEST);
+ regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, &reg_val);
+ if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check reg 0x40 odd bit failed, read[0x%x] != write[0x%x]",
+ reg_val, AW88395_DSP_ODD_NUM_BIT_TEST);
+ goto error;
+ }
+
+ /* check the even bits of reg 0x40 */
+ regmap_write(aw_dev->regmap, AW88395_DSPMADD_REG, AW88395_DSP_EVEN_NUM_BIT_TEST);
+ regmap_read(aw_dev->regmap, AW88395_DSPMADD_REG, &reg_val);
+ if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check reg 0x40 even bit failed, read[0x%x] != write[0x%x]",
+ reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST);
+ goto error;
+ }
+
+ /* check dsp_fw_base_addr */
+ aw_dev_dsp_write_16bit(aw_dev, AW88395_DSP_FW_ADDR, AW88395_DSP_EVEN_NUM_BIT_TEST);
+ aw_dev_dsp_read_16bit(aw_dev, AW88395_DSP_FW_ADDR, &reg_val);
+ if (reg_val != AW88395_DSP_EVEN_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check dsp fw addr failed, read[0x%x] != write[0x%x]",
+ reg_val, AW88395_DSP_EVEN_NUM_BIT_TEST);
+ goto error;
+ }
+
+ /* check dsp_cfg_base_addr */
+ aw_dev_dsp_write_16bit(aw_dev, AW88395_DSP_CFG_ADDR, AW88395_DSP_ODD_NUM_BIT_TEST);
+ aw_dev_dsp_read_16bit(aw_dev, AW88395_DSP_CFG_ADDR, &reg_val);
+ if (reg_val != AW88395_DSP_ODD_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]",
+ reg_val, AW88395_DSP_ODD_NUM_BIT_TEST);
+ goto error;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return 0;
+
+error:
+ mutex_unlock(&aw_dev->dsp_lock);
+ return -EPERM;
+}
+
+int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en)
+{
+ struct aw_prof_desc *prof_index_desc;
+ struct aw_sec_data_desc *sec_desc;
+ char *prof_name;
+ int ret;
+
+ if ((aw_dev->prof_cur == aw_dev->prof_index) &&
+ (force_up_en == AW88395_FORCE_UPDATE_OFF)) {
+ dev_dbg(aw_dev->dev, "scene no change, not update");
+ return 0;
+ }
+
+ if (aw_dev->fw_status == AW88395_DEV_FW_FAILED) {
+ dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status);
+ return -EPERM;
+ }
+
+ prof_name = aw88395_dev_get_prof_name(aw_dev, aw_dev->prof_index);
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw88395_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc);
+ if (ret)
+ return ret;
+
+ /* update reg */
+ sec_desc = prof_index_desc->sec_desc;
+ ret = aw_dev_reg_update(aw_dev, sec_desc[AW88395_DATA_TYPE_REG].data,
+ sec_desc[AW88395_DATA_TYPE_REG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update reg failed");
+ return ret;
+ }
+
+ aw88395_dev_mute(aw_dev, true);
+
+ if (aw_dev->dsp_cfg == AW88395_DEV_DSP_WORK)
+ aw_dev_dsp_enable(aw_dev, false);
+
+ aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_OSC);
+
+ if (up_dsp_fw_en) {
+ ret = aw_dev_check_sram(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "check sram failed");
+ goto error;
+ }
+
+ /* update dsp firmware */
+ dev_dbg(aw_dev->dev, "fw_ver: [%x]", prof_index_desc->fw_ver);
+ ret = aw_dev_dsp_update_fw(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_FW].data,
+ sec_desc[AW88395_DATA_TYPE_DSP_FW].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update dsp fw failed");
+ goto error;
+ }
+ }
+
+ /* update dsp config */
+ ret = aw_dev_dsp_update_cfg(aw_dev, sec_desc[AW88395_DATA_TYPE_DSP_CFG].data,
+ sec_desc[AW88395_DATA_TYPE_DSP_CFG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update dsp cfg failed");
+ goto error;
+ }
+
+ aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL);
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+
+error:
+ aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_fw_update);
+
+static int aw_dev_dsp_check(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ switch (aw_dev->dsp_cfg) {
+ case AW88395_DEV_DSP_BYPASS:
+ dev_dbg(aw_dev->dev, "dsp bypass");
+ ret = 0;
+ break;
+ case AW88395_DEV_DSP_WORK:
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_dsp_enable(aw_dev, true);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 10);
+ for (i = 0; i < AW88395_DEV_DSP_CHECK_MAX; i++) {
+ ret = aw_dev_get_dsp_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp wdt status error=%d", ret);
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+ }
+ }
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static void aw_dev_update_cali_re(struct aw_cali_desc *cali_desc)
+{
+ struct aw_device *aw_dev =
+ container_of(cali_desc, struct aw_device, cali_desc);
+ int ret;
+
+ if ((aw_dev->cali_desc.cali_re < AW88395_CALI_RE_MAX) &&
+ (aw_dev->cali_desc.cali_re > AW88395_CALI_RE_MIN)) {
+
+ ret = aw_dev_dsp_set_cali_re(aw_dev);
+ if (ret)
+ dev_err(aw_dev->dev, "set cali re failed");
+ }
+}
+
+int aw88395_dev_start(struct aw_device *aw_dev)
+{
+ int ret;
+
+ if (aw_dev->status == AW88395_DEV_PW_ON) {
+ dev_info(aw_dev->dev, "already power on");
+ return 0;
+ }
+ /* power on */
+ aw_dev_pwd(aw_dev, false);
+ usleep_range(AW88395_2000_US, AW88395_2000_US + 10);
+
+ ret = aw_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start");
+ goto pll_check_fail;
+ }
+
+ /* amppd on */
+ aw_dev_amppd(aw_dev, false);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 50);
+
+ /* check i2s status */
+ ret = aw_dev_check_sysst(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "sysst check failed");
+ goto sysst_check_fail;
+ }
+
+ if (aw_dev->dsp_cfg == AW88395_DEV_DSP_WORK) {
+ /* dsp bypass */
+ aw_dev_dsp_enable(aw_dev, false);
+ ret = aw_dev_dsp_fw_check(aw_dev);
+ if (ret)
+ goto dev_dsp_fw_check_fail;
+
+ aw_dev_update_cali_re(&aw_dev->cali_desc);
+
+ if (aw_dev->dsp_crc_st != AW88395_DSP_CRC_OK) {
+ ret = aw_dev_dsp_check_crc32(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp crc check failed");
+ goto crc_check_fail;
+ }
+ }
+
+ ret = aw_dev_dsp_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp status check failed");
+ goto dsp_check_fail;
+ }
+ } else {
+ dev_dbg(aw_dev->dev, "start pa with dsp bypass");
+ }
+
+ /* enable tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, true);
+
+ /* close mute */
+ aw88395_dev_mute(aw_dev, false);
+ /* clear inturrupt */
+ aw_dev_clear_int_status(aw_dev);
+ aw_dev->status = AW88395_DEV_PW_ON;
+
+ return 0;
+
+dsp_check_fail:
+crc_check_fail:
+ aw_dev_dsp_enable(aw_dev, false);
+dev_dsp_fw_check_fail:
+sysst_check_fail:
+ aw_dev_clear_int_status(aw_dev);
+ aw_dev_amppd(aw_dev, true);
+pll_check_fail:
+ aw_dev_pwd(aw_dev, true);
+ aw_dev->status = AW88395_DEV_PW_OFF;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_start);
+
+int aw88395_dev_stop(struct aw_device *aw_dev)
+{
+ struct aw_sec_data_desc *dsp_cfg =
+ &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_CFG];
+ struct aw_sec_data_desc *dsp_fw =
+ &aw_dev->prof_info.prof_desc[aw_dev->prof_cur].sec_desc[AW88395_DATA_TYPE_DSP_FW];
+ int int_st = 0;
+ int ret;
+
+ if (aw_dev->status == AW88395_DEV_PW_OFF) {
+ dev_info(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88395_DEV_PW_OFF;
+
+ /* set mute */
+ aw88395_dev_mute(aw_dev, true);
+ usleep_range(AW88395_4000_US, AW88395_4000_US + 100);
+
+ /* close tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 100);
+
+ /* check sysint state */
+ int_st = aw_dev_check_sysint(aw_dev);
+
+ /* close dsp */
+ aw_dev_dsp_enable(aw_dev, false);
+
+ /* enable amppd */
+ aw_dev_amppd(aw_dev, true);
+
+ if (int_st < 0) {
+ /* system status anomaly */
+ aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_OSC);
+ ret = aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len);
+ if (ret)
+ dev_err(aw_dev->dev, "update dsp fw failed");
+ ret = aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len);
+ if (ret)
+ dev_err(aw_dev->dev, "update dsp cfg failed");
+ aw_dev_select_memclk(aw_dev, AW88395_DEV_MEMCLK_PLL);
+ }
+
+ /* set power down */
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_stop);
+
+int aw88395_dev_init(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ int ret;
+
+ if ((!aw_dev) || (!aw_cfg)) {
+ pr_err("aw_dev is NULL or aw_cfg is NULL");
+ return -ENOMEM;
+ }
+ ret = aw88395_dev_cfg_load(aw_dev, aw_cfg);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw_dev acf parse failed");
+ return -EINVAL;
+ }
+ aw_dev->fade_in_time = AW88395_1000_US / 10;
+ aw_dev->fade_out_time = AW88395_1000_US >> 1;
+ aw_dev->prof_cur = aw_dev->prof_info.prof_desc[0].id;
+ aw_dev->prof_index = aw_dev->prof_info.prof_desc[0].id;
+
+ ret = aw88395_dev_fw_update(aw_dev, AW88395_FORCE_UPDATE_ON, AW88395_DSP_FW_UPDATE_ON);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+ return ret;
+ }
+
+ /* set mute */
+ aw88395_dev_mute(aw_dev, true);
+ usleep_range(AW88395_4000_US, AW88395_4000_US + 100);
+
+ /* close tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88395_1000_US, AW88395_1000_US + 100);
+
+ /* close dsp */
+ aw_dev_dsp_enable(aw_dev, false);
+ /* enable amppd */
+ aw_dev_amppd(aw_dev, true);
+ /* set power down */
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_init);
+
+static void aw88395_parse_channel_dt(struct aw_device *aw_dev)
+{
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value;
+ int ret;
+
+ ret = of_property_read_u32(np, "sound-channel", &channel_value);
+ if (ret) {
+ dev_dbg(aw_dev->dev,
+ "read sound-channel failed,use default 0");
+ aw_dev->channel = AW88395_DEV_DEFAULT_CH;
+ return;
+ }
+
+ dev_dbg(aw_dev->dev, "read sound-channel value is: %d",
+ channel_value);
+ aw_dev->channel = channel_value;
+}
+
+static void aw88395_parse_fade_enable_dt(struct aw_device *aw_dev)
+{
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 fade_en;
+ int ret;
+
+ ret = of_property_read_u32(np, "fade-enable", &fade_en);
+ if (ret) {
+ dev_dbg(aw_dev->dev,
+ "read fade-enable failed, close fade_in_out");
+ fade_en = AW88395_FADE_IN_OUT_DEFAULT;
+ }
+
+ dev_dbg(aw_dev->dev, "read fade-enable value is: %d", fade_en);
+
+ aw_dev->fade_en = fade_en;
+}
+
+static int aw_dev_init(struct aw_device *aw_dev)
+{
+ aw_dev->chip_id = AW88395_CHIP_ID;
+ /* call aw device init func */
+ aw_dev->acf = NULL;
+ aw_dev->prof_info.prof_desc = NULL;
+ aw_dev->prof_info.count = 0;
+ aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
+ aw_dev->channel = 0;
+ aw_dev->fw_status = AW88395_DEV_FW_FAILED;
+
+ aw_dev->fade_step = AW88395_VOLUME_STEP_DB;
+ aw_dev->volume_desc.ctl_volume = AW88395_VOL_DEFAULT_VALUE;
+ aw88395_parse_channel_dt(aw_dev);
+ aw88395_parse_fade_enable_dt(aw_dev);
+
+ return 0;
+}
+
+int aw88395_dev_get_profile_count(struct aw_device *aw_dev)
+{
+ return aw_dev->prof_info.count;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_get_profile_count);
+
+int aw88395_dev_get_profile_index(struct aw_device *aw_dev)
+{
+ return aw_dev->prof_index;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_get_profile_index);
+
+int aw88395_dev_set_profile_index(struct aw_device *aw_dev, int index)
+{
+ /* check the index whether is valid */
+ if ((index >= aw_dev->prof_info.count) || (index < 0))
+ return -EINVAL;
+ /* check the index whether change */
+ if (aw_dev->prof_index == index)
+ return -EINVAL;
+
+ aw_dev->prof_index = index;
+ dev_dbg(aw_dev->dev, "set prof[%s]",
+ aw_dev->prof_info.prof_name_list[aw_dev->prof_info.prof_desc[index].id]);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_set_profile_index);
+
+char *aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index)
+{
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ struct aw_prof_desc *prof_desc;
+
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "index[%d] overflow count[%d]",
+ index, aw_dev->prof_info.count);
+ return NULL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ return prof_info->prof_name_list[prof_desc->id];
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_get_prof_name);
+
+int aw88395_dev_get_prof_data(struct aw_device *aw_dev, int index,
+ struct aw_prof_desc **prof_desc)
+{
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n",
+ __func__, index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ *prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_get_prof_data);
+
+int aw88395_init(struct aw_device **aw_dev, struct i2c_client *i2c, struct regmap *regmap)
+{
+ u16 chip_id;
+ int ret;
+
+ if (*aw_dev) {
+ dev_info(&i2c->dev, "it should be initialized here.\n");
+ } else {
+ *aw_dev = devm_kzalloc(&i2c->dev, sizeof(struct aw_device), GFP_KERNEL);
+ if (!(*aw_dev))
+ return -ENOMEM;
+ }
+
+ (*aw_dev)->i2c = i2c;
+ (*aw_dev)->dev = &i2c->dev;
+ (*aw_dev)->regmap = regmap;
+ mutex_init(&(*aw_dev)->dsp_lock);
+
+ /* read chip id */
+ ret = aw_dev_read_chipid((*aw_dev), &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "dev_read_chipid failed ret=%d", ret);
+ return ret;
+ }
+
+ switch (chip_id) {
+ case AW88395_CHIP_ID:
+ ret = aw_dev_init((*aw_dev));
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err((*aw_dev)->dev, "unsupported device");
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(aw88395_init);
+
+MODULE_DESCRIPTION("AW88395 device lib");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88395/aw88395_device.h b/sound/soc/codecs/aw88395/aw88395_device.h
new file mode 100644
index 00000000000000..caf7307531679f
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_device.h
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395_device.h -- AW88395 function for ALSA Audio Driver
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#ifndef __AW88395_DEVICE_FILE_H__
+#define __AW88395_DEVICE_FILE_H__
+
+#include "aw88395.h"
+#include "aw88395_data_type.h"
+#include "aw88395_lib.h"
+
+#define AW88395_DEV_DEFAULT_CH (0)
+#define AW88395_DEV_DSP_CHECK_MAX (5)
+#define AW88395_DSP_I2C_WRITES
+#define AW88395_MAX_RAM_WRITE_BYTE_SIZE (128)
+#define AW88395_DSP_ODD_NUM_BIT_TEST (0x5555)
+#define AW88395_DSP_EVEN_NUM_BIT_TEST (0xAAAA)
+#define AW88395_DSP_ST_CHECK_MAX (2)
+#define AW88395_FADE_IN_OUT_DEFAULT (0)
+#define AW88395_CALI_RE_MAX (15000)
+#define AW88395_CALI_RE_MIN (4000)
+#define AW88395_CALI_DELAY_CACL(value) ((value * 32) / 48)
+
+#define AW88395_DSP_RE_TO_SHOW_RE(re, shift) (((re) * (1000)) >> (shift))
+#define AW88395_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000))
+
+#define AW88395_ACF_FILE "aw88395_acf.bin"
+#define AW88395_DEV_SYSST_CHECK_MAX (10)
+
+enum {
+ AW88395_DEV_VDSEL_DAC = 0,
+ AW88395_DEV_VDSEL_VSENSE = 1,
+};
+
+enum {
+ AW88395_DSP_CRC_NA = 0,
+ AW88395_DSP_CRC_OK = 1,
+};
+
+enum {
+ AW88395_DSP_FW_UPDATE_OFF = 0,
+ AW88395_DSP_FW_UPDATE_ON = 1,
+};
+
+enum {
+ AW88395_FORCE_UPDATE_OFF = 0,
+ AW88395_FORCE_UPDATE_ON = 1,
+};
+
+enum {
+ AW88395_1000_US = 1000,
+ AW88395_2000_US = 2000,
+ AW88395_3000_US = 3000,
+ AW88395_4000_US = 4000,
+ AW88395_5000_US = 5000,
+ AW88395_10000_US = 10000,
+ AW88395_100000_US = 100000,
+};
+
+enum {
+ AW88395_DEV_TYPE_OK = 0,
+ AW88395_DEV_TYPE_NONE = 1,
+};
+
+
+enum AW88395_DEV_STATUS {
+ AW88395_DEV_PW_OFF = 0,
+ AW88395_DEV_PW_ON,
+};
+
+enum AW88395_DEV_FW_STATUS {
+ AW88395_DEV_FW_FAILED = 0,
+ AW88395_DEV_FW_OK,
+};
+
+enum AW88395_DEV_MEMCLK {
+ AW88395_DEV_MEMCLK_OSC = 0,
+ AW88395_DEV_MEMCLK_PLL = 1,
+};
+
+enum AW88395_DEV_DSP_CFG {
+ AW88395_DEV_DSP_WORK = 0,
+ AW88395_DEV_DSP_BYPASS = 1,
+};
+
+enum {
+ AW88395_DSP_16_DATA = 0,
+ AW88395_DSP_32_DATA = 1,
+};
+
+enum {
+ AW88395_NOT_RCV_MODE = 0,
+ AW88395_RCV_MODE = 1,
+};
+
+struct aw_profctrl_desc {
+ unsigned int cur_mode;
+};
+
+struct aw_volume_desc {
+ unsigned int init_volume;
+ unsigned int mute_volume;
+ unsigned int ctl_volume;
+ unsigned int max_volume;
+};
+
+struct aw_dsp_mem_desc {
+ unsigned int dsp_madd_reg;
+ unsigned int dsp_mdat_reg;
+ unsigned int dsp_fw_base_addr;
+ unsigned int dsp_cfg_base_addr;
+};
+
+struct aw_vmax_desc {
+ unsigned int init_vmax;
+};
+
+struct aw_cali_delay_desc {
+ unsigned int delay;
+};
+
+struct aw_cali_desc {
+ u32 cali_re;
+ u32 ra;
+};
+
+struct aw_container {
+ int len;
+ u8 data[];
+};
+
+struct aw_device {
+ int status;
+ struct mutex dsp_lock;
+
+ unsigned char prof_cur;
+ unsigned char prof_index;
+ unsigned char dsp_crc_st;
+ u16 chip_id;
+
+ unsigned int channel;
+ unsigned int fade_step;
+
+ struct i2c_client *i2c;
+ struct device *dev;
+ struct regmap *regmap;
+ char *acf;
+
+ u32 fade_en;
+ unsigned char dsp_cfg;
+
+ u32 dsp_fw_len;
+ u32 dsp_cfg_len;
+ u8 platform;
+ u8 fw_status;
+
+ unsigned int fade_in_time;
+ unsigned int fade_out_time;
+
+ struct aw_prof_info prof_info;
+ struct aw_sec_data_desc crc_dsp_cfg;
+ struct aw_profctrl_desc profctrl_desc;
+ struct aw_volume_desc volume_desc;
+ struct aw_dsp_mem_desc dsp_mem_desc;
+ struct aw_vmax_desc vmax_desc;
+
+ struct aw_cali_delay_desc cali_delay_desc;
+ struct aw_cali_desc cali_desc;
+
+};
+
+int aw88395_init(struct aw_device **aw_dev, struct i2c_client *i2c, struct regmap *regmap);
+int aw88395_dev_init(struct aw_device *aw_dev, struct aw_container *aw_cfg);
+int aw88395_dev_start(struct aw_device *aw_dev);
+int aw88395_dev_stop(struct aw_device *aw_dev);
+int aw88395_dev_fw_update(struct aw_device *aw_dev, bool up_dsp_fw_en, bool force_up_en);
+
+void aw88395_dev_set_volume(struct aw_device *aw_dev, unsigned short set_vol);
+int aw88395_dev_get_prof_data(struct aw_device *aw_dev, int index,
+ struct aw_prof_desc **prof_desc);
+char *aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index);
+int aw88395_dev_set_profile_index(struct aw_device *aw_dev, int index);
+int aw88395_dev_get_profile_index(struct aw_device *aw_dev);
+int aw88395_dev_get_profile_count(struct aw_device *aw_dev);
+int aw88395_dev_load_acf_check(struct aw_device *aw_dev, struct aw_container *aw_cfg);
+int aw88395_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg);
+void aw88395_dev_mute(struct aw_device *aw_dev, bool is_mute);
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c
new file mode 100644
index 00000000000000..05bcf49da85749
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_lib.c
@@ -0,0 +1,1066 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395_lib.c -- ACF bin parsing and check library file for aw88395
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#include <linux/crc8.h>
+#include <linux/i2c.h>
+#include "aw88395_lib.h"
+#include "aw88395_device.h"
+
+#define AW88395_CRC8_POLYNOMIAL 0x8C
+DECLARE_CRC8_TABLE(aw_crc8_table);
+
+static char *profile_name[AW88395_PROFILE_MAX] = {
+ "Music", "Voice", "Voip", "Ringtone",
+ "Ringtone_hs", "Lowpower", "Bypass",
+ "Mmi", "Fm", "Notification", "Receiver"
+};
+
+static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bin);
+
+static int aw_check_sum(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num)
+{
+ unsigned char *p_check_sum;
+ unsigned int sum_data = 0;
+ unsigned int check_sum;
+ unsigned int i, len;
+
+ p_check_sum = &(bin->info.data[(bin->header_info[bin_num].valid_data_addr -
+ bin->header_info[bin_num].header_len)]);
+ len = bin->header_info[bin_num].bin_data_len + bin->header_info[bin_num].header_len;
+ check_sum = le32_to_cpup((void *)p_check_sum);
+
+ for (i = 4; i < len; i++)
+ sum_data += *(p_check_sum + i);
+
+ dev_dbg(aw_dev->dev, "%s -- check_sum = %p, check_sum = 0x%x, sum_data = 0x%x",
+ __func__, p_check_sum, check_sum, sum_data);
+ if (sum_data != check_sum) {
+ dev_err(aw_dev->dev, "%s. CheckSum Fail.bin_num=%d, CheckSum:0x%x, SumData:0x%x",
+ __func__, bin_num, check_sum, sum_data);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_check_data_version(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num)
+{
+ if (bin->header_info[bin_num].bin_data_ver < DATA_VERSION_V1 ||
+ bin->header_info[bin_num].bin_data_ver > DATA_VERSION_MAX) {
+ dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin data version\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_check_register_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num)
+{
+ struct bin_header_info temp_info = bin->header_info[bin_num];
+ unsigned int check_register_num, parse_register_num;
+ unsigned char *p_check_sum;
+
+ p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]);
+
+ parse_register_num = le32_to_cpup((void *)p_check_sum);
+ check_register_num = (bin->header_info[bin_num].bin_data_len - CHECK_REGISTER_NUM_OFFSET) /
+ (bin->header_info[bin_num].reg_byte_len +
+ bin->header_info[bin_num].data_byte_len);
+ dev_dbg(aw_dev->dev, "%s,parse_register_num = 0x%x,check_register_num = 0x%x\n",
+ __func__, parse_register_num, check_register_num);
+ if (parse_register_num != check_register_num) {
+ dev_err(aw_dev->dev, "%s parse_register_num = 0x%x,check_register_num = 0x%x\n",
+ __func__, parse_register_num, check_register_num);
+ return -EINVAL;
+ }
+
+ bin->header_info[bin_num].reg_num = parse_register_num;
+ bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - VALID_DATA_LEN;
+ bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr + VALID_DATA_ADDR;
+
+ return 0;
+}
+
+static int aw_check_dsp_reg_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num)
+{
+ struct bin_header_info temp_info = bin->header_info[bin_num];
+ unsigned int check_dsp_reg_num, parse_dsp_reg_num;
+ unsigned char *p_check_sum;
+
+ p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]);
+
+ parse_dsp_reg_num = le32_to_cpup((void *)(p_check_sum + PARSE_DSP_REG_NUM));
+ bin->header_info[bin_num].reg_data_byte_len =
+ le32_to_cpup((void *)(p_check_sum + REG_DATA_BYTP_LEN));
+ check_dsp_reg_num = (bin->header_info[bin_num].bin_data_len - CHECK_DSP_REG_NUM) /
+ bin->header_info[bin_num].reg_data_byte_len;
+ dev_dbg(aw_dev->dev, "%s bin_num = %d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x",
+ __func__, bin_num, check_dsp_reg_num, check_dsp_reg_num);
+ if (parse_dsp_reg_num != check_dsp_reg_num) {
+ dev_err(aw_dev->dev, "aw_bin_parse check dsp reg num error\n");
+ dev_err(aw_dev->dev, "%s parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x",
+ __func__, check_dsp_reg_num, check_dsp_reg_num);
+ return -EINVAL;
+ }
+
+ bin->header_info[bin_num].download_addr = le32_to_cpup((void *)p_check_sum);
+ bin->header_info[bin_num].reg_num = parse_dsp_reg_num;
+ bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - DSP_VALID_DATA_LEN;
+ bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr +
+ DSP_VALID_DATA_ADDR;
+
+ return 0;
+}
+
+static int aw_check_soc_app_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num)
+{
+ struct bin_header_info temp_info = bin->header_info[bin_num];
+ unsigned int check_soc_app_num, parse_soc_app_num;
+ unsigned char *p_check_sum;
+
+ p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]);
+
+ bin->header_info[bin_num].app_version = le32_to_cpup((void *)p_check_sum);
+ parse_soc_app_num = le32_to_cpup((void *)(p_check_sum + PARSE_SOC_APP_NUM));
+ check_soc_app_num = bin->header_info[bin_num].bin_data_len - CHECK_SOC_APP_NUM;
+ dev_dbg(aw_dev->dev, "%s bin_num = %d, parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n",
+ __func__, bin_num, parse_soc_app_num, check_soc_app_num);
+ if (parse_soc_app_num != check_soc_app_num) {
+ dev_err(aw_dev->dev, "%s parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n",
+ __func__, parse_soc_app_num, check_soc_app_num);
+ return -EINVAL;
+ }
+
+ bin->header_info[bin_num].reg_num = parse_soc_app_num;
+ bin->header_info[bin_num].download_addr = le32_to_cpup((void *)(p_check_sum +
+ APP_DOWNLOAD_ADDR));
+ bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - APP_VALID_DATA_LEN;
+ bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr +
+ APP_VALID_DATA_ADDR;
+
+ return 0;
+}
+
+static void aw_get_single_bin_header(struct aw_bin *bin)
+{
+ memcpy((void *)&bin->header_info[bin->all_bin_parse_num], bin->p_addr, DATA_LEN);
+
+ bin->header_info[bin->all_bin_parse_num].header_len = HEADER_LEN;
+ bin->all_bin_parse_num += 1;
+}
+
+static int aw_parse_one_of_multi_bins(struct aw_device *aw_dev, unsigned int bin_num,
+ int bin_serial_num, struct aw_bin *bin)
+{
+ struct bin_header_info aw_bin_header_info;
+ unsigned int bin_start_addr;
+ unsigned int valid_data_len;
+
+ if (bin->info.len < sizeof(struct bin_header_info)) {
+ dev_err(aw_dev->dev, "bin_header_info size[%d] overflow file size[%d]\n",
+ (int)sizeof(struct bin_header_info), bin->info.len);
+ return -EINVAL;
+ }
+
+ aw_bin_header_info = bin->header_info[bin->all_bin_parse_num - 1];
+ if (!bin_serial_num) {
+ bin_start_addr = le32_to_cpup((void *)(bin->p_addr + START_ADDR_OFFSET));
+ bin->p_addr += (HEADER_LEN + bin_start_addr);
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+ aw_bin_header_info.valid_data_addr + VALID_DATA_ADDR + 8 * bin_num +
+ VALID_DATA_ADDR_OFFSET;
+ } else {
+ valid_data_len = aw_bin_header_info.bin_data_len;
+ bin->p_addr += (HDADER_LEN + valid_data_len);
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+ aw_bin_header_info.valid_data_addr + aw_bin_header_info.bin_data_len +
+ VALID_DATA_ADDR_OFFSET;
+ }
+
+ return aw_parse_bin_header(aw_dev, bin);
+}
+
+static int aw_get_multi_bin_header(struct aw_device *aw_dev, struct aw_bin *bin)
+{
+ unsigned int bin_num, i;
+ int ret;
+
+ bin_num = le32_to_cpup((void *)(bin->p_addr + VALID_DATA_ADDR_OFFSET));
+ if (bin->multi_bin_parse_num == 1)
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+ VALID_DATA_ADDR_OFFSET;
+
+ aw_get_single_bin_header(bin);
+
+ for (i = 0; i < bin_num; i++) {
+ dev_dbg(aw_dev->dev, "aw_bin_parse enter multi bin for is %d\n", i);
+ ret = aw_parse_one_of_multi_bins(aw_dev, bin_num, i, bin);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bin)
+{
+ unsigned int bin_data_type;
+
+ if (bin->info.len < sizeof(struct bin_header_info)) {
+ dev_err(aw_dev->dev, "bin_header_info size[%d] overflow file size[%d]\n",
+ (int)sizeof(struct bin_header_info), bin->info.len);
+ return -EINVAL;
+ }
+
+ bin_data_type = le32_to_cpup((void *)(bin->p_addr + BIN_DATA_TYPE_OFFSET));
+ dev_dbg(aw_dev->dev, "aw_bin_parse bin_data_type 0x%x\n", bin_data_type);
+ switch (bin_data_type) {
+ case DATA_TYPE_REGISTER:
+ case DATA_TYPE_DSP_REG:
+ case DATA_TYPE_SOC_APP:
+ bin->single_bin_parse_num += 1;
+ dev_dbg(aw_dev->dev, "%s bin->single_bin_parse_num is %d\n", __func__,
+ bin->single_bin_parse_num);
+ if (!bin->multi_bin_parse_num)
+ bin->header_info[bin->all_bin_parse_num].valid_data_addr =
+ VALID_DATA_ADDR_OFFSET;
+ aw_get_single_bin_header(bin);
+ return 0;
+ case DATA_TYPE_MULTI_BINS:
+ bin->multi_bin_parse_num += 1;
+ dev_dbg(aw_dev->dev, "%s bin->multi_bin_parse_num is %d\n", __func__,
+ bin->multi_bin_parse_num);
+ return aw_get_multi_bin_header(aw_dev, bin);
+ default:
+ dev_dbg(aw_dev->dev, "%s There is no corresponding type\n", __func__);
+ return 0;
+ }
+}
+
+static int aw_check_bin_header_version(struct aw_device *aw_dev, struct aw_bin *bin)
+{
+ unsigned int header_version;
+
+ header_version = le32_to_cpup((void *)(bin->p_addr + HEADER_VERSION_OFFSET));
+ dev_dbg(aw_dev->dev, "aw_bin_parse header_version 0x%x\n", header_version);
+
+ switch (header_version) {
+ case HEADER_VERSION_V1:
+ return aw_parse_bin_header(aw_dev, bin);
+ default:
+ dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin header version\n");
+ return -EINVAL;
+ }
+}
+
+static int aw_parsing_bin_file(struct aw_device *aw_dev, struct aw_bin *bin)
+{
+ int ret = -EINVAL;
+ int i;
+
+ if (!bin) {
+ dev_err(aw_dev->dev, "aw_bin_parse bin is NULL\n");
+ return ret;
+ }
+ bin->p_addr = bin->info.data;
+ bin->all_bin_parse_num = 0;
+ bin->multi_bin_parse_num = 0;
+ bin->single_bin_parse_num = 0;
+
+ ret = aw_check_bin_header_version(aw_dev, bin);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "aw_bin_parse check bin header version error\n");
+ return ret;
+ }
+
+ for (i = 0; i < bin->all_bin_parse_num; i++) {
+ ret = aw_check_sum(aw_dev, bin, i);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "aw_bin_parse check sum data error\n");
+ return ret;
+ }
+ ret = aw_check_data_version(aw_dev, bin, i);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "aw_bin_parse check data version error\n");
+ return ret;
+ }
+ if (bin->header_info[i].bin_data_ver == DATA_VERSION_V1) {
+ switch (bin->header_info[i].bin_data_type) {
+ case DATA_TYPE_REGISTER:
+ ret = aw_check_register_num(aw_dev, bin, i);
+ break;
+ case DATA_TYPE_DSP_REG:
+ ret = aw_check_dsp_reg_num(aw_dev, bin, i);
+ break;
+ case DATA_TYPE_SOC_APP:
+ ret = aw_check_soc_app_num(aw_dev, bin, i);
+ break;
+ default:
+ bin->header_info[i].valid_data_len =
+ bin->header_info[i].bin_data_len;
+ ret = 0;
+ break;
+ }
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_raw_reg(unsigned char *data, unsigned int data_len,
+ struct aw_prof_desc *prof_desc)
+{
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = data;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = data_len;
+
+ prof_desc->prof_st = AW88395_PROFILE_OK;
+
+ return 0;
+}
+
+static int aw_dev_parse_raw_dsp_cfg(unsigned char *data, unsigned int data_len,
+ struct aw_prof_desc *prof_desc)
+{
+ if (data_len & 0x01)
+ return -EINVAL;
+
+ swab16_array((u16 *)data, data_len >> 1);
+
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].data = data;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].len = data_len;
+
+ prof_desc->prof_st = AW88395_PROFILE_OK;
+
+ return 0;
+}
+
+static int aw_dev_parse_raw_dsp_fw(unsigned char *data, unsigned int data_len,
+ struct aw_prof_desc *prof_desc)
+{
+ if (data_len & 0x01)
+ return -EINVAL;
+
+ swab16_array((u16 *)data, data_len >> 1);
+
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].data = data;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].len = data_len;
+
+ prof_desc->prof_st = AW88395_PROFILE_OK;
+
+ return 0;
+}
+
+static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char *data,
+ unsigned int data_len, struct aw_prof_desc *prof_desc)
+{
+ struct aw_bin *aw_bin;
+ int ret;
+ int i;
+
+ aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(struct aw_bin), GFP_KERNEL);
+ if (!aw_bin)
+ return -ENOMEM;
+
+ aw_bin->info.len = data_len;
+ memcpy(aw_bin->info.data, data, data_len);
+
+ ret = aw_parsing_bin_file(aw_dev, aw_bin);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse bin failed");
+ goto parse_bin_failed;
+ }
+
+ for (i = 0; i < aw_bin->all_bin_parse_num; i++) {
+ switch (aw_bin->header_info[i].bin_data_type) {
+ case DATA_TYPE_REGISTER:
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len =
+ aw_bin->header_info[i].valid_data_len;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data =
+ data + aw_bin->header_info[i].valid_data_addr;
+ break;
+ case DATA_TYPE_DSP_REG:
+ if (aw_bin->header_info[i].valid_data_len & 0x01) {
+ ret = -EINVAL;
+ goto parse_bin_failed;
+ }
+
+ swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr),
+ aw_bin->header_info[i].valid_data_len >> 1);
+
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].len =
+ aw_bin->header_info[i].valid_data_len;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].data =
+ data + aw_bin->header_info[i].valid_data_addr;
+ break;
+ case DATA_TYPE_DSP_FW:
+ case DATA_TYPE_SOC_APP:
+ if (aw_bin->header_info[i].valid_data_len & 0x01) {
+ ret = -EINVAL;
+ goto parse_bin_failed;
+ }
+
+ swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr),
+ aw_bin->header_info[i].valid_data_len >> 1);
+
+ prof_desc->fw_ver = aw_bin->header_info[i].app_version;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].len =
+ aw_bin->header_info[i].valid_data_len;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].data =
+ data + aw_bin->header_info[i].valid_data_addr;
+ break;
+ default:
+ dev_dbg(aw_dev->dev, "bin_data_type not found");
+ break;
+ }
+ }
+ prof_desc->prof_st = AW88395_PROFILE_OK;
+ ret = 0;
+
+parse_bin_failed:
+ devm_kfree(aw_dev->dev, aw_bin);
+ return ret;
+}
+
+static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr,
+ struct aw_cfg_dde *cfg_dde, struct aw_prof_desc *scene_prof_desc)
+{
+ switch (cfg_dde->data_type) {
+ case ACF_SEC_TYPE_REG:
+ return aw_dev_parse_raw_reg((u8 *)cfg_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, scene_prof_desc);
+ case ACF_SEC_TYPE_DSP_CFG:
+ return aw_dev_parse_raw_dsp_cfg((u8 *)cfg_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, scene_prof_desc);
+ case ACF_SEC_TYPE_DSP_FW:
+ return aw_dev_parse_raw_dsp_fw(
+ (u8 *)cfg_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, scene_prof_desc);
+ case ACF_SEC_TYPE_MULTIPLE_BIN:
+ return aw_dev_prof_parse_multi_bin(
+ aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, scene_prof_desc);
+ default:
+ dev_err(aw_dev->dev, "%s cfg_dde->data_type = %d\n", __func__, cfg_dde->data_type);
+ break;
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_dev_type(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info)
+{
+ struct aw_cfg_dde *cfg_dde =
+ (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset);
+ int sec_num = 0;
+ int ret, i;
+
+ for (i = 0; i < prof_hdr->ddt_num; i++) {
+ if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
+ (aw_dev->i2c->addr == cfg_dde[i].dev_addr) &&
+ (cfg_dde[i].type == AW88395_DEV_TYPE_ID) &&
+ (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) {
+ if (cfg_dde[i].dev_profile >= AW88395_PROFILE_MAX) {
+ dev_err(aw_dev->dev, "dev_profile [%d] overflow",
+ cfg_dde[i].dev_profile);
+ return -EINVAL;
+ }
+
+ ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i],
+ &all_prof_info->prof_desc[cfg_dde[i].dev_profile]);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse failed");
+ return ret;
+ }
+ sec_num++;
+ }
+ }
+
+ if (sec_num == 0) {
+ dev_dbg(aw_dev->dev, "get dev type num is %d, please use default", sec_num);
+ return AW88395_DEV_TYPE_NONE;
+ }
+
+ return AW88395_DEV_TYPE_OK;
+}
+
+static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info)
+{
+ struct aw_cfg_dde *cfg_dde =
+ (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset);
+ int sec_num = 0;
+ int ret, i;
+
+ for (i = 0; i < prof_hdr->ddt_num; i++) {
+ if ((aw_dev->channel == cfg_dde[i].dev_index) &&
+ (cfg_dde[i].type == AW88395_DEV_DEFAULT_TYPE_ID) &&
+ (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) {
+ if (cfg_dde[i].dev_profile >= AW88395_PROFILE_MAX) {
+ dev_err(aw_dev->dev, "dev_profile [%d] overflow",
+ cfg_dde[i].dev_profile);
+ return -EINVAL;
+ }
+ ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i],
+ &all_prof_info->prof_desc[cfg_dde[i].dev_profile]);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse failed");
+ return ret;
+ }
+ sec_num++;
+ }
+ }
+
+ if (sec_num == 0) {
+ dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", sec_num);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_cfg_get_valid_prof(struct aw_device *aw_dev,
+ struct aw_all_prof_info all_prof_info)
+{
+ struct aw_prof_desc *prof_desc = all_prof_info.prof_desc;
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ struct aw_sec_data_desc *sec_desc;
+ int num = 0;
+ int i;
+
+ for (i = 0; i < AW88395_PROFILE_MAX; i++) {
+ if (prof_desc[i].prof_st == AW88395_PROFILE_OK) {
+ sec_desc = prof_desc[i].sec_desc;
+ if ((sec_desc[AW88395_DATA_TYPE_REG].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_REG].len != 0) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_CFG].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_CFG].len != 0) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_FW].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_FW].len != 0))
+ prof_info->count++;
+ }
+ }
+
+ dev_dbg(aw_dev->dev, "get valid profile:%d", aw_dev->prof_info.count);
+
+ if (!prof_info->count) {
+ dev_err(aw_dev->dev, "no profile data");
+ return -EPERM;
+ }
+
+ prof_info->prof_desc = devm_kcalloc(aw_dev->dev,
+ prof_info->count, sizeof(struct aw_prof_desc),
+ GFP_KERNEL);
+ if (!prof_info->prof_desc)
+ return -ENOMEM;
+
+ for (i = 0; i < AW88395_PROFILE_MAX; i++) {
+ if (prof_desc[i].prof_st == AW88395_PROFILE_OK) {
+ sec_desc = prof_desc[i].sec_desc;
+ if ((sec_desc[AW88395_DATA_TYPE_REG].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_REG].len != 0) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_CFG].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_CFG].len != 0) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_FW].data != NULL) &&
+ (sec_desc[AW88395_DATA_TYPE_DSP_FW].len != 0)) {
+ if (num >= prof_info->count) {
+ dev_err(aw_dev->dev, "overflow count[%d]",
+ prof_info->count);
+ return -EINVAL;
+ }
+ prof_info->prof_desc[num] = prof_desc[i];
+ prof_info->prof_desc[num].id = i;
+ num++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr)
+{
+ struct aw_all_prof_info *all_prof_info;
+ int ret;
+
+ all_prof_info = devm_kzalloc(aw_dev->dev, sizeof(struct aw_all_prof_info), GFP_KERNEL);
+ if (!all_prof_info)
+ return -ENOMEM;
+
+ ret = aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info);
+ if (ret < 0) {
+ goto exit;
+ } else if (ret == AW88395_DEV_TYPE_NONE) {
+ dev_dbg(aw_dev->dev, "get dev type num is 0, parse default dev");
+ ret = aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info);
+ if (ret < 0)
+ goto exit;
+ }
+
+ ret = aw_dev_cfg_get_valid_prof(aw_dev, *all_prof_info);
+ if (ret < 0)
+ goto exit;
+
+ aw_dev->prof_info.prof_name_list = profile_name;
+
+exit:
+ devm_kfree(aw_dev->dev, all_prof_info);
+ return ret;
+}
+
+static int aw_dev_create_prof_name_list_v1(struct aw_device *aw_dev)
+{
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ struct aw_prof_desc *prof_desc = prof_info->prof_desc;
+ int i;
+
+ if (!prof_desc) {
+ dev_err(aw_dev->dev, "prof_desc is NULL");
+ return -EINVAL;
+ }
+
+ prof_info->prof_name_list = devm_kzalloc(aw_dev->dev,
+ prof_info->count * PROFILE_STR_MAX,
+ GFP_KERNEL);
+ if (!prof_info->prof_name_list)
+ return -ENOMEM;
+
+ for (i = 0; i < prof_info->count; i++) {
+ prof_desc[i].id = i;
+ prof_info->prof_name_list[i] = prof_desc[i].prf_str;
+ dev_dbg(aw_dev->dev, "prof name is %s", prof_info->prof_name_list[i]);
+ }
+
+ return 0;
+}
+
+static int aw_get_dde_type_info(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ struct aw_cfg_dde_v1 *cfg_dde =
+ (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
+ int default_num = 0;
+ int dev_num = 0;
+ unsigned int i;
+
+ for (i = 0; i < cfg_hdr->ddt_num; i++) {
+ if (cfg_dde[i].type == AW88395_DEV_TYPE_ID)
+ dev_num++;
+
+ if (cfg_dde[i].type == AW88395_DEV_DEFAULT_TYPE_ID)
+ default_num++;
+ }
+
+ if (dev_num != 0) {
+ aw_dev->prof_info.prof_type = AW88395_DEV_TYPE_ID;
+ } else if (default_num != 0) {
+ aw_dev->prof_info.prof_type = AW88395_DEV_DEFAULT_TYPE_ID;
+ } else {
+ dev_err(aw_dev->dev, "can't find scene");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_get_dev_scene_count_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg,
+ unsigned int *scene_num)
+{
+ struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ struct aw_cfg_dde_v1 *cfg_dde =
+ (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
+ unsigned int i;
+
+ for (i = 0; i < cfg_hdr->ddt_num; ++i) {
+ if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id) &&
+ (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
+ (aw_dev->i2c->addr == cfg_dde[i].dev_addr))
+ (*scene_num)++;
+ }
+
+ return 0;
+}
+
+static int aw_get_default_scene_count_v1(struct aw_device *aw_dev,
+ struct aw_container *aw_cfg,
+ unsigned int *scene_num)
+{
+ struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ struct aw_cfg_dde_v1 *cfg_dde =
+ (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
+ unsigned int i;
+
+ for (i = 0; i < cfg_hdr->ddt_num; ++i) {
+ if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id) &&
+ (aw_dev->channel == cfg_dde[i].dev_index))
+ (*scene_num)++;
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_scene_count_v1(struct aw_device *aw_dev,
+ struct aw_container *aw_cfg,
+ unsigned int *count)
+{
+ int ret;
+
+ ret = aw_get_dde_type_info(aw_dev, aw_cfg);
+ if (ret < 0)
+ return ret;
+
+ switch (aw_dev->prof_info.prof_type) {
+ case AW88395_DEV_TYPE_ID:
+ ret = aw_get_dev_scene_count_v1(aw_dev, aw_cfg, count);
+ break;
+ case AW88395_DEV_DEFAULT_TYPE_ID:
+ ret = aw_get_default_scene_count_v1(aw_dev, aw_cfg, count);
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported prof_type[%x]", aw_dev->prof_info.prof_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw_dev_parse_data_by_sec_type_v1(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr,
+ struct aw_cfg_dde_v1 *cfg_dde,
+ int *cur_scene_id)
+{
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ int ret;
+
+ switch (cfg_dde->data_type) {
+ case ACF_SEC_TYPE_MULTIPLE_BIN:
+ ret = aw_dev_prof_parse_multi_bin(aw_dev, (u8 *)prof_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse multi bin failed");
+ return ret;
+ }
+ prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str;
+ prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile;
+ (*cur_scene_id)++;
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported SEC_TYPE [%d]", cfg_dde->data_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_dev_type_v1(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr)
+{
+ struct aw_cfg_dde_v1 *cfg_dde =
+ (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset);
+ int cur_scene_id = 0;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < prof_hdr->ddt_num; i++) {
+ if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
+ (aw_dev->i2c->addr == cfg_dde[i].dev_addr) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id)) {
+ ret = aw_dev_parse_data_by_sec_type_v1(aw_dev, prof_hdr,
+ &cfg_dde[i], &cur_scene_id);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse failed");
+ return ret;
+ }
+ }
+ }
+
+ if (cur_scene_id == 0) {
+ dev_err(aw_dev->dev, "get dev type failed, get num [%d]", cur_scene_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_default_type_v1(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *prof_hdr)
+{
+ struct aw_cfg_dde_v1 *cfg_dde =
+ (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset);
+ int cur_scene_id = 0;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < prof_hdr->ddt_num; i++) {
+ if ((aw_dev->channel == cfg_dde[i].dev_index) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id)) {
+ ret = aw_dev_parse_data_by_sec_type_v1(aw_dev, prof_hdr,
+ &cfg_dde[i], &cur_scene_id);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse failed");
+ return ret;
+ }
+ }
+ }
+
+ if (cur_scene_id == 0) {
+ dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", cur_scene_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw_dev_parse_by_hdr_v1(struct aw_device *aw_dev,
+ struct aw_cfg_hdr *cfg_hdr)
+{
+ int ret;
+
+ switch (aw_dev->prof_info.prof_type) {
+ case AW88395_DEV_TYPE_ID:
+ ret = aw_dev_parse_dev_type_v1(aw_dev, cfg_hdr);
+ break;
+ case AW88395_DEV_DEFAULT_TYPE_ID:
+ ret = aw_dev_parse_default_type_v1(aw_dev, cfg_hdr);
+ break;
+ default:
+ dev_err(aw_dev->dev, "prof type matched failed, get num[%d]",
+ aw_dev->prof_info.prof_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw_dev_load_cfg_by_hdr_v1(struct aw_device *aw_dev,
+ struct aw_container *aw_cfg)
+{
+ struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ int ret;
+
+ ret = aw_dev_parse_scene_count_v1(aw_dev, aw_cfg, &prof_info->count);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "get scene count failed");
+ return ret;
+ }
+
+ prof_info->prof_desc = devm_kcalloc(aw_dev->dev,
+ prof_info->count, sizeof(struct aw_prof_desc),
+ GFP_KERNEL);
+ if (!prof_info->prof_desc)
+ return -ENOMEM;
+
+ ret = aw_dev_parse_by_hdr_v1(aw_dev, cfg_hdr);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse hdr failed");
+ return ret;
+ }
+
+ ret = aw_dev_create_prof_name_list_v1(aw_dev);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "create prof name list failed");
+ return ret;
+ }
+
+ return 0;
+}
+
+int aw88395_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ struct aw_cfg_hdr *cfg_hdr;
+ int ret;
+
+ cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+
+ switch (cfg_hdr->hdr_version) {
+ case AW88395_CFG_HDR_VER:
+ ret = aw_dev_load_cfg_by_hdr(aw_dev, cfg_hdr);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "hdr_version[0x%x] parse failed",
+ cfg_hdr->hdr_version);
+ return ret;
+ }
+ break;
+ case AW88395_CFG_HDR_VER_V1:
+ ret = aw_dev_load_cfg_by_hdr_v1(aw_dev, aw_cfg);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "hdr_version[0x%x] parse failed",
+ cfg_hdr->hdr_version);
+ return ret;
+ }
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_version);
+ return -EINVAL;
+ }
+ aw_dev->fw_status = AW88395_DEV_FW_OK;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_cfg_load);
+
+static int aw_dev_check_cfg_by_hdr(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ unsigned int end_data_offset;
+ struct aw_cfg_hdr *cfg_hdr;
+ struct aw_cfg_dde *cfg_dde;
+ unsigned int act_data = 0;
+ unsigned int hdr_ddt_len;
+ unsigned int i;
+ u8 act_crc8;
+
+ cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ /* check file type id is awinic acf file */
+ if (cfg_hdr->id != ACF_FILE_ID) {
+ dev_err(aw_dev->dev, "not acf type file");
+ return -EINVAL;
+ }
+
+ hdr_ddt_len = cfg_hdr->hdr_offset + cfg_hdr->ddt_size;
+ if (hdr_ddt_len > aw_cfg->len) {
+ dev_err(aw_dev->dev, "hdr_len with ddt_len [%d] overflow file size[%d]",
+ cfg_hdr->hdr_offset, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ /* check data size */
+ cfg_dde = (struct aw_cfg_dde *)((char *)aw_cfg->data + cfg_hdr->hdr_offset);
+ act_data += hdr_ddt_len;
+ for (i = 0; i < cfg_hdr->ddt_num; i++)
+ act_data += cfg_dde[i].data_size;
+
+ if (act_data != aw_cfg->len) {
+ dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!",
+ act_data, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cfg_hdr->ddt_num; i++) {
+ /* data check */
+ end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size;
+ if (end_data_offset > aw_cfg->len) {
+ dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]",
+ i, end_data_offset, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ /* crc check */
+ act_crc8 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset,
+ cfg_dde[i].data_size, 0);
+ if (act_crc8 != cfg_dde[i].data_crc) {
+ dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc:0x%x",
+ i, (u32)act_crc8, cfg_dde[i].data_crc);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int aw_dev_check_acf_by_hdr_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ struct aw_cfg_dde_v1 *cfg_dde;
+ unsigned int end_data_offset;
+ struct aw_cfg_hdr *cfg_hdr;
+ unsigned int act_data = 0;
+ unsigned int hdr_ddt_len;
+ u8 act_crc8;
+ int i;
+
+ cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+
+ /* check file type id is awinic acf file */
+ if (cfg_hdr->id != ACF_FILE_ID) {
+ dev_err(aw_dev->dev, "not acf type file");
+ return -EINVAL;
+ }
+
+ hdr_ddt_len = cfg_hdr->hdr_offset + cfg_hdr->ddt_size;
+ if (hdr_ddt_len > aw_cfg->len) {
+ dev_err(aw_dev->dev, "hdrlen with ddt_len [%d] overflow file size[%d]",
+ cfg_hdr->hdr_offset, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ /* check data size */
+ cfg_dde = (struct aw_cfg_dde_v1 *)((char *)aw_cfg->data + cfg_hdr->hdr_offset);
+ act_data += hdr_ddt_len;
+ for (i = 0; i < cfg_hdr->ddt_num; i++)
+ act_data += cfg_dde[i].data_size;
+
+ if (act_data != aw_cfg->len) {
+ dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!",
+ act_data, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < cfg_hdr->ddt_num; i++) {
+ /* data check */
+ end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size;
+ if (end_data_offset > aw_cfg->len) {
+ dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]",
+ i, end_data_offset, aw_cfg->len);
+ return -EINVAL;
+ }
+
+ /* crc check */
+ act_crc8 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset,
+ cfg_dde[i].data_size, 0);
+ if (act_crc8 != cfg_dde[i].data_crc) {
+ dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc 0x%x",
+ i, (u32)act_crc8, cfg_dde[i].data_crc);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int aw88395_dev_load_acf_check(struct aw_device *aw_dev, struct aw_container *aw_cfg)
+{
+ struct aw_cfg_hdr *cfg_hdr;
+
+ if (!aw_cfg) {
+ dev_err(aw_dev->dev, "aw_prof is NULL");
+ return -EINVAL;
+ }
+
+ if (aw_cfg->len < sizeof(struct aw_cfg_hdr)) {
+ dev_err(aw_dev->dev, "cfg hdr size[%d] overflow file size[%d]",
+ aw_cfg->len, (int)sizeof(struct aw_cfg_hdr));
+ return -EINVAL;
+ }
+
+ crc8_populate_lsb(aw_crc8_table, AW88395_CRC8_POLYNOMIAL);
+
+ cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data;
+ switch (cfg_hdr->hdr_version) {
+ case AW88395_CFG_HDR_VER:
+ return aw_dev_check_cfg_by_hdr(aw_dev, aw_cfg);
+ case AW88395_CFG_HDR_VER_V1:
+ return aw_dev_check_acf_by_hdr_v1(aw_dev, aw_cfg);
+ default:
+ dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_version);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(aw88395_dev_load_acf_check);
+
+MODULE_DESCRIPTION("AW88395 ACF File Parsing Lib");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88395/aw88395_lib.h b/sound/soc/codecs/aw88395/aw88395_lib.h
new file mode 100644
index 00000000000000..8a620920d8bd84
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_lib.h
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395_lib.h -- ACF bin parsing and check library file for aw88395
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#ifndef __AW88395_LIB_H__
+#define __AW88395_LIB_H__
+
+#define CHECK_REGISTER_NUM_OFFSET (4)
+#define VALID_DATA_LEN (4)
+#define VALID_DATA_ADDR (4)
+#define PARSE_DSP_REG_NUM (4)
+#define REG_DATA_BYTP_LEN (8)
+#define CHECK_DSP_REG_NUM (12)
+#define DSP_VALID_DATA_LEN (12)
+#define DSP_VALID_DATA_ADDR (12)
+#define PARSE_SOC_APP_NUM (8)
+#define CHECK_SOC_APP_NUM (12)
+#define APP_DOWNLOAD_ADDR (4)
+#define APP_VALID_DATA_LEN (12)
+#define APP_VALID_DATA_ADDR (12)
+#define BIN_NUM_MAX (100)
+#define HEADER_LEN (60)
+#define BIN_DATA_TYPE_OFFSET (8)
+#define DATA_LEN (44)
+#define VALID_DATA_ADDR_OFFSET (60)
+#define START_ADDR_OFFSET (64)
+
+#define AW88395_FW_CHECK_PART (10)
+#define HDADER_LEN (60)
+
+#define HEADER_VERSION_OFFSET (4)
+
+enum bin_header_version_enum {
+ HEADER_VERSION_V1 = 0x01000000,
+};
+
+enum data_type_enum {
+ DATA_TYPE_REGISTER = 0x00000000,
+ DATA_TYPE_DSP_REG = 0x00000010,
+ DATA_TYPE_DSP_CFG = 0x00000011,
+ DATA_TYPE_SOC_REG = 0x00000020,
+ DATA_TYPE_SOC_APP = 0x00000021,
+ DATA_TYPE_DSP_FW = 0x00000022,
+ DATA_TYPE_MULTI_BINS = 0x00002000,
+};
+
+enum data_version_enum {
+ DATA_VERSION_V1 = 0x00000001,
+ DATA_VERSION_MAX,
+};
+
+struct bin_header_info {
+ unsigned int check_sum;
+ unsigned int header_ver;
+ unsigned int bin_data_type;
+ unsigned int bin_data_ver;
+ unsigned int bin_data_len;
+ unsigned int ui_ver;
+ unsigned char chip_type[8];
+ unsigned int reg_byte_len;
+ unsigned int data_byte_len;
+ unsigned int device_addr;
+ unsigned int valid_data_len;
+ unsigned int valid_data_addr;
+
+ unsigned int reg_num;
+ unsigned int reg_data_byte_len;
+ unsigned int download_addr;
+ unsigned int app_version;
+ unsigned int header_len;
+};
+
+struct bin_container {
+ unsigned int len;
+ unsigned char data[];
+};
+
+struct aw_bin {
+ unsigned char *p_addr;
+ unsigned int all_bin_parse_num;
+ unsigned int multi_bin_parse_num;
+ unsigned int single_bin_parse_num;
+ struct bin_header_info header_info[BIN_NUM_MAX];
+ struct bin_container info;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395_reg.h b/sound/soc/codecs/aw88395/aw88395_reg.h
new file mode 100644
index 00000000000000..e64f24e971508f
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_reg.h
@@ -0,0 +1,383 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88395_reg.h -- AW88395 chip register file
+//
+// Copyright (c) 2022-2023 AWINIC Technology CO., LTD
+//
+// Author: Bruce zhao <zhaolei@awinic.com>
+//
+
+#ifndef __AW88395_REG_H__
+#define __AW88395_REG_H__
+
+#define AW88395_ID_REG (0x00)
+#define AW88395_SYSST_REG (0x01)
+#define AW88395_SYSINT_REG (0x02)
+#define AW88395_SYSINTM_REG (0x03)
+#define AW88395_SYSCTRL_REG (0x04)
+#define AW88395_SYSCTRL2_REG (0x05)
+#define AW88395_I2SCTRL_REG (0x06)
+#define AW88395_I2SCFG1_REG (0x07)
+#define AW88395_I2SCFG2_REG (0x08)
+#define AW88395_HAGCCFG1_REG (0x09)
+#define AW88395_HAGCCFG2_REG (0x0A)
+#define AW88395_HAGCCFG3_REG (0x0B)
+#define AW88395_HAGCCFG4_REG (0x0C)
+#define AW88395_HAGCCFG5_REG (0x0D)
+#define AW88395_HAGCCFG6_REG (0x0E)
+#define AW88395_HAGCCFG7_REG (0x0F)
+#define AW88395_MPDCFG_REG (0x10)
+#define AW88395_PWMCTRL_REG (0x11)
+#define AW88395_I2SCFG3_REG (0x12)
+#define AW88395_DBGCTRL_REG (0x13)
+#define AW88395_HAGCST_REG (0x20)
+#define AW88395_VBAT_REG (0x21)
+#define AW88395_TEMP_REG (0x22)
+#define AW88395_PVDD_REG (0x23)
+#define AW88395_ISNDAT_REG (0x24)
+#define AW88395_VSNDAT_REG (0x25)
+#define AW88395_I2SINT_REG (0x26)
+#define AW88395_I2SCAPCNT_REG (0x27)
+#define AW88395_ANASTA1_REG (0x28)
+#define AW88395_ANASTA2_REG (0x29)
+#define AW88395_ANASTA3_REG (0x2A)
+#define AW88395_ANASTA4_REG (0x2B)
+#define AW88395_TESTDET_REG (0x2C)
+#define AW88395_TESTIN_REG (0x38)
+#define AW88395_TESTOUT_REG (0x39)
+#define AW88395_DSPMADD_REG (0x40)
+#define AW88395_DSPMDAT_REG (0x41)
+#define AW88395_WDT_REG (0x42)
+#define AW88395_ACR1_REG (0x43)
+#define AW88395_ACR2_REG (0x44)
+#define AW88395_ASR1_REG (0x45)
+#define AW88395_ASR2_REG (0x46)
+#define AW88395_DSPCFG_REG (0x47)
+#define AW88395_ASR3_REG (0x48)
+#define AW88395_ASR4_REG (0x49)
+#define AW88395_VSNCTRL1_REG (0x50)
+#define AW88395_ISNCTRL1_REG (0x51)
+#define AW88395_PLLCTRL1_REG (0x52)
+#define AW88395_PLLCTRL2_REG (0x53)
+#define AW88395_PLLCTRL3_REG (0x54)
+#define AW88395_CDACTRL1_REG (0x55)
+#define AW88395_CDACTRL2_REG (0x56)
+#define AW88395_SADCCTRL1_REG (0x57)
+#define AW88395_SADCCTRL2_REG (0x58)
+#define AW88395_CPCTRL1_REG (0x59)
+#define AW88395_BSTCTRL1_REG (0x60)
+#define AW88395_BSTCTRL2_REG (0x61)
+#define AW88395_BSTCTRL3_REG (0x62)
+#define AW88395_BSTCTRL4_REG (0x63)
+#define AW88395_BSTCTRL5_REG (0x64)
+#define AW88395_BSTCTRL6_REG (0x65)
+#define AW88395_BSTCTRL7_REG (0x66)
+#define AW88395_DSMCFG1_REG (0x67)
+#define AW88395_DSMCFG2_REG (0x68)
+#define AW88395_DSMCFG3_REG (0x69)
+#define AW88395_DSMCFG4_REG (0x6A)
+#define AW88395_DSMCFG5_REG (0x6B)
+#define AW88395_DSMCFG6_REG (0x6C)
+#define AW88395_DSMCFG7_REG (0x6D)
+#define AW88395_DSMCFG8_REG (0x6E)
+#define AW88395_TESTCTRL1_REG (0x70)
+#define AW88395_TESTCTRL2_REG (0x71)
+#define AW88395_EFCTRL1_REG (0x72)
+#define AW88395_EFCTRL2_REG (0x73)
+#define AW88395_EFWH_REG (0x74)
+#define AW88395_EFWM2_REG (0x75)
+#define AW88395_EFWM1_REG (0x76)
+#define AW88395_EFWL_REG (0x77)
+#define AW88395_EFRH_REG (0x78)
+#define AW88395_EFRM2_REG (0x79)
+#define AW88395_EFRM1_REG (0x7A)
+#define AW88395_EFRL_REG (0x7B)
+#define AW88395_TM_REG (0x7C)
+
+enum aw88395_id {
+ AW88395_CHIP_ID = 0x2049,
+};
+
+#define AW88395_REG_MAX (0x7D)
+
+#define AW88395_VOLUME_STEP_DB (6 * 8)
+
+#define AW88395_UVLS_START_BIT (14)
+#define AW88395_UVLS_NORMAL (0)
+#define AW88395_UVLS_NORMAL_VALUE \
+ (AW88395_UVLS_NORMAL << AW88395_UVLS_START_BIT)
+
+#define AW88395_DSPS_START_BIT (12)
+#define AW88395_DSPS_BITS_LEN (1)
+#define AW88395_DSPS_MASK \
+ (~(((1<<AW88395_DSPS_BITS_LEN)-1) << AW88395_DSPS_START_BIT))
+
+#define AW88395_DSPS_NORMAL (0)
+#define AW88395_DSPS_NORMAL_VALUE \
+ (AW88395_DSPS_NORMAL << AW88395_DSPS_START_BIT)
+
+#define AW88395_BSTOCS_START_BIT (11)
+#define AW88395_BSTOCS_OVER_CURRENT (1)
+#define AW88395_BSTOCS_OVER_CURRENT_VALUE \
+ (AW88395_BSTOCS_OVER_CURRENT << AW88395_BSTOCS_START_BIT)
+
+#define AW88395_BSTS_START_BIT (9)
+#define AW88395_BSTS_FINISHED (1)
+#define AW88395_BSTS_FINISHED_VALUE \
+ (AW88395_BSTS_FINISHED << AW88395_BSTS_START_BIT)
+
+#define AW88395_SWS_START_BIT (8)
+#define AW88395_SWS_SWITCHING (1)
+#define AW88395_SWS_SWITCHING_VALUE \
+ (AW88395_SWS_SWITCHING << AW88395_SWS_START_BIT)
+
+#define AW88395_NOCLKS_START_BIT (5)
+#define AW88395_NOCLKS_NO_CLOCK (1)
+#define AW88395_NOCLKS_NO_CLOCK_VALUE \
+ (AW88395_NOCLKS_NO_CLOCK << AW88395_NOCLKS_START_BIT)
+
+#define AW88395_CLKS_START_BIT (4)
+#define AW88395_CLKS_STABLE (1)
+#define AW88395_CLKS_STABLE_VALUE \
+ (AW88395_CLKS_STABLE << AW88395_CLKS_START_BIT)
+
+#define AW88395_OCDS_START_BIT (3)
+#define AW88395_OCDS_OC (1)
+#define AW88395_OCDS_OC_VALUE \
+ (AW88395_OCDS_OC << AW88395_OCDS_START_BIT)
+
+#define AW88395_OTHS_START_BIT (1)
+#define AW88395_OTHS_OT (1)
+#define AW88395_OTHS_OT_VALUE \
+ (AW88395_OTHS_OT << AW88395_OTHS_START_BIT)
+
+#define AW88395_PLLS_START_BIT (0)
+#define AW88395_PLLS_LOCKED (1)
+#define AW88395_PLLS_LOCKED_VALUE \
+ (AW88395_PLLS_LOCKED << AW88395_PLLS_START_BIT)
+
+#define AW88395_BIT_PLL_CHECK \
+ (AW88395_CLKS_STABLE_VALUE | \
+ AW88395_PLLS_LOCKED_VALUE)
+
+#define AW88395_BIT_SYSST_CHECK_MASK \
+ (~(AW88395_UVLS_NORMAL_VALUE | \
+ AW88395_BSTOCS_OVER_CURRENT_VALUE | \
+ AW88395_BSTS_FINISHED_VALUE | \
+ AW88395_SWS_SWITCHING_VALUE | \
+ AW88395_NOCLKS_NO_CLOCK_VALUE | \
+ AW88395_CLKS_STABLE_VALUE | \
+ AW88395_OCDS_OC_VALUE | \
+ AW88395_OTHS_OT_VALUE | \
+ AW88395_PLLS_LOCKED_VALUE))
+
+#define AW88395_BIT_SYSST_CHECK \
+ (AW88395_BSTS_FINISHED_VALUE | \
+ AW88395_SWS_SWITCHING_VALUE | \
+ AW88395_CLKS_STABLE_VALUE | \
+ AW88395_PLLS_LOCKED_VALUE)
+
+#define AW88395_WDI_START_BIT (6)
+#define AW88395_WDI_INT_VALUE (1)
+#define AW88395_WDI_INTERRUPT \
+ (AW88395_WDI_INT_VALUE << AW88395_WDI_START_BIT)
+
+#define AW88395_NOCLKI_START_BIT (5)
+#define AW88395_NOCLKI_INT_VALUE (1)
+#define AW88395_NOCLKI_INTERRUPT \
+ (AW88395_NOCLKI_INT_VALUE << AW88395_NOCLKI_START_BIT)
+
+#define AW88395_CLKI_START_BIT (4)
+#define AW88395_CLKI_INT_VALUE (1)
+#define AW88395_CLKI_INTERRUPT \
+ (AW88395_CLKI_INT_VALUE << AW88395_CLKI_START_BIT)
+
+#define AW88395_PLLI_START_BIT (0)
+#define AW88395_PLLI_INT_VALUE (1)
+#define AW88395_PLLI_INTERRUPT \
+ (AW88395_PLLI_INT_VALUE << AW88395_PLLI_START_BIT)
+
+#define AW88395_BIT_SYSINT_CHECK \
+ (AW88395_WDI_INTERRUPT | \
+ AW88395_CLKI_INTERRUPT | \
+ AW88395_NOCLKI_INTERRUPT | \
+ AW88395_PLLI_INTERRUPT)
+
+#define AW88395_HMUTE_START_BIT (8)
+#define AW88395_HMUTE_BITS_LEN (1)
+#define AW88395_HMUTE_MASK \
+ (~(((1<<AW88395_HMUTE_BITS_LEN)-1) << AW88395_HMUTE_START_BIT))
+
+#define AW88395_HMUTE_DISABLE (0)
+#define AW88395_HMUTE_DISABLE_VALUE \
+ (AW88395_HMUTE_DISABLE << AW88395_HMUTE_START_BIT)
+
+#define AW88395_HMUTE_ENABLE (1)
+#define AW88395_HMUTE_ENABLE_VALUE \
+ (AW88395_HMUTE_ENABLE << AW88395_HMUTE_START_BIT)
+
+#define AW88395_RCV_MODE_START_BIT (7)
+#define AW88395_RCV_MODE_BITS_LEN (1)
+#define AW88395_RCV_MODE_MASK \
+ (~(((1<<AW88395_RCV_MODE_BITS_LEN)-1) << AW88395_RCV_MODE_START_BIT))
+
+#define AW88395_RCV_MODE_RECEIVER (1)
+#define AW88395_RCV_MODE_RECEIVER_VALUE \
+ (AW88395_RCV_MODE_RECEIVER << AW88395_RCV_MODE_START_BIT)
+
+#define AW88395_DSPBY_START_BIT (2)
+#define AW88395_DSPBY_BITS_LEN (1)
+#define AW88395_DSPBY_MASK \
+ (~(((1<<AW88395_DSPBY_BITS_LEN)-1) << AW88395_DSPBY_START_BIT))
+
+#define AW88395_DSPBY_WORKING (0)
+#define AW88395_DSPBY_WORKING_VALUE \
+ (AW88395_DSPBY_WORKING << AW88395_DSPBY_START_BIT)
+
+#define AW88395_DSPBY_BYPASS (1)
+#define AW88395_DSPBY_BYPASS_VALUE \
+ (AW88395_DSPBY_BYPASS << AW88395_DSPBY_START_BIT)
+
+#define AW88395_AMPPD_START_BIT (1)
+#define AW88395_AMPPD_BITS_LEN (1)
+#define AW88395_AMPPD_MASK \
+ (~(((1<<AW88395_AMPPD_BITS_LEN)-1) << AW88395_AMPPD_START_BIT))
+
+#define AW88395_AMPPD_WORKING (0)
+#define AW88395_AMPPD_WORKING_VALUE \
+ (AW88395_AMPPD_WORKING << AW88395_AMPPD_START_BIT)
+
+#define AW88395_AMPPD_POWER_DOWN (1)
+#define AW88395_AMPPD_POWER_DOWN_VALUE \
+ (AW88395_AMPPD_POWER_DOWN << AW88395_AMPPD_START_BIT)
+
+#define AW88395_PWDN_START_BIT (0)
+#define AW88395_PWDN_BITS_LEN (1)
+#define AW88395_PWDN_MASK \
+ (~(((1<<AW88395_PWDN_BITS_LEN)-1) << AW88395_PWDN_START_BIT))
+
+#define AW88395_PWDN_WORKING (0)
+#define AW88395_PWDN_WORKING_VALUE \
+ (AW88395_PWDN_WORKING << AW88395_PWDN_START_BIT)
+
+#define AW88395_PWDN_POWER_DOWN (1)
+#define AW88395_PWDN_POWER_DOWN_VALUE \
+ (AW88395_PWDN_POWER_DOWN << AW88395_PWDN_START_BIT)
+
+#define AW88395_MUTE_VOL (90 * 8)
+#define AW88395_VOLUME_STEP_DB (6 * 8)
+
+#define AW88395_VOL_6DB_START (6)
+#define AW88395_VOL_START_BIT (6)
+#define AW88395_VOL_BITS_LEN (10)
+#define AW88395_VOL_MASK \
+ (~(((1<<AW88395_VOL_BITS_LEN)-1) << AW88395_VOL_START_BIT))
+
+#define AW88395_VOL_DEFAULT_VALUE (0)
+
+#define AW88395_I2STXEN_START_BIT (0)
+#define AW88395_I2STXEN_BITS_LEN (1)
+#define AW88395_I2STXEN_MASK \
+ (~(((1<<AW88395_I2STXEN_BITS_LEN)-1) << AW88395_I2STXEN_START_BIT))
+
+#define AW88395_I2STXEN_DISABLE (0)
+#define AW88395_I2STXEN_DISABLE_VALUE \
+ (AW88395_I2STXEN_DISABLE << AW88395_I2STXEN_START_BIT)
+
+#define AW88395_I2STXEN_ENABLE (1)
+#define AW88395_I2STXEN_ENABLE_VALUE \
+ (AW88395_I2STXEN_ENABLE << AW88395_I2STXEN_START_BIT)
+
+#define AW88395_AGC_DSP_CTL_START_BIT (15)
+#define AW88395_AGC_DSP_CTL_BITS_LEN (1)
+#define AW88395_AGC_DSP_CTL_MASK \
+ (~(((1<<AW88395_AGC_DSP_CTL_BITS_LEN)-1) << AW88395_AGC_DSP_CTL_START_BIT))
+
+#define AW88395_AGC_DSP_CTL_DISABLE (0)
+#define AW88395_AGC_DSP_CTL_DISABLE_VALUE \
+ (AW88395_AGC_DSP_CTL_DISABLE << AW88395_AGC_DSP_CTL_START_BIT)
+
+#define AW88395_AGC_DSP_CTL_ENABLE (1)
+#define AW88395_AGC_DSP_CTL_ENABLE_VALUE \
+ (AW88395_AGC_DSP_CTL_ENABLE << AW88395_AGC_DSP_CTL_START_BIT)
+
+#define AW88395_VDSEL_START_BIT (0)
+#define AW88395_VDSEL_BITS_LEN (1)
+#define AW88395_VDSEL_MASK \
+ (~(((1<<AW88395_VDSEL_BITS_LEN)-1) << AW88395_VDSEL_START_BIT))
+
+#define AW88395_MEM_CLKSEL_START_BIT (3)
+#define AW88395_MEM_CLKSEL_BITS_LEN (1)
+#define AW88395_MEM_CLKSEL_MASK \
+ (~(((1<<AW88395_MEM_CLKSEL_BITS_LEN)-1) << AW88395_MEM_CLKSEL_START_BIT))
+
+#define AW88395_MEM_CLKSEL_OSC_CLK (0)
+#define AW88395_MEM_CLKSEL_OSC_CLK_VALUE \
+ (AW88395_MEM_CLKSEL_OSC_CLK << AW88395_MEM_CLKSEL_START_BIT)
+
+#define AW88395_MEM_CLKSEL_DAP_HCLK (1)
+#define AW88395_MEM_CLKSEL_DAP_HCLK_VALUE \
+ (AW88395_MEM_CLKSEL_DAP_HCLK << AW88395_MEM_CLKSEL_START_BIT)
+
+#define AW88395_CCO_MUX_START_BIT (14)
+#define AW88395_CCO_MUX_BITS_LEN (1)
+#define AW88395_CCO_MUX_MASK \
+ (~(((1<<AW88395_CCO_MUX_BITS_LEN)-1) << AW88395_CCO_MUX_START_BIT))
+
+#define AW88395_CCO_MUX_DIVIDED (0)
+#define AW88395_CCO_MUX_DIVIDED_VALUE \
+ (AW88395_CCO_MUX_DIVIDED << AW88395_CCO_MUX_START_BIT)
+
+#define AW88395_CCO_MUX_BYPASS (1)
+#define AW88395_CCO_MUX_BYPASS_VALUE \
+ (AW88395_CCO_MUX_BYPASS << AW88395_CCO_MUX_START_BIT)
+
+#define AW88395_EF_VSN_GESLP_START_BIT (0)
+#define AW88395_EF_VSN_GESLP_BITS_LEN (10)
+#define AW88395_EF_VSN_GESLP_MASK \
+ (~(((1<<AW88395_EF_VSN_GESLP_BITS_LEN)-1) << AW88395_EF_VSN_GESLP_START_BIT))
+
+#define AW88395_EF_VSN_GESLP_SIGN_MASK (~(1 << 9))
+#define AW88395_EF_VSN_GESLP_SIGN_NEG (0xfe00)
+
+#define AW88395_EF_ISN_GESLP_START_BIT (0)
+#define AW88395_EF_ISN_GESLP_BITS_LEN (10)
+#define AW88395_EF_ISN_GESLP_MASK \
+ (~(((1<<AW88395_EF_ISN_GESLP_BITS_LEN)-1) << AW88395_EF_ISN_GESLP_START_BIT))
+
+#define AW88395_EF_ISN_GESLP_SIGN_MASK (~(1 << 9))
+#define AW88395_EF_ISN_GESLP_SIGN_NEG (0xfe00)
+
+#define AW88395_CABL_BASE_VALUE (1000)
+#define AW88395_ICABLK_FACTOR (1)
+#define AW88395_VCABLK_FACTOR (1)
+#define AW88395_VCAL_FACTOR (1 << 12)
+#define AW88395_VSCAL_FACTOR (16500)
+#define AW88395_ISCAL_FACTOR (3667)
+#define AW88395_EF_VSENSE_GAIN_SHIFT (0)
+
+#define AW88395_VCABLK_FACTOR_DAC (2)
+#define AW88395_VSCAL_FACTOR_DAC (11790)
+#define AW88395_EF_DAC_GESLP_SHIFT (10)
+#define AW88395_EF_DAC_GESLP_SIGN_MASK (1 << 5)
+#define AW88395_EF_DAC_GESLP_SIGN_NEG (0xffc0)
+
+#define AW88395_VCALB_ADJ_FACTOR (12)
+
+#define AW88395_WDT_CNT_START_BIT (0)
+#define AW88395_WDT_CNT_BITS_LEN (8)
+#define AW88395_WDT_CNT_MASK \
+ (~(((1<<AW88395_WDT_CNT_BITS_LEN)-1) << AW88395_WDT_CNT_START_BIT))
+
+#define AW88395_DSP_CFG_ADDR (0x9C80)
+#define AW88395_DSP_FW_ADDR (0x8C00)
+#define AW88395_DSP_REG_VMAX (0x9C94)
+#define AW88395_DSP_REG_CFG_ADPZ_RE (0x9D00)
+#define AW88395_DSP_REG_VCALB (0x9CF7)
+#define AW88395_DSP_RE_SHIFT (12)
+
+#define AW88395_DSP_REG_CFG_ADPZ_RA (0x9D02)
+#define AW88395_DSP_REG_CRC_ADDR (0x9F42)
+#define AW88395_DSP_CALI_F0_DELAY (0x9CFD)
+
+#endif
diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
index 4086b6a53de8ca..3afcef2dfa3529 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -78,11 +78,6 @@ static int bt_sco_probe(struct platform_device *pdev)
bt_sco_dai, ARRAY_SIZE(bt_sco_dai));
}
-static int bt_sco_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static const struct platform_device_id bt_sco_driver_ids[] = {
{
.name = "dfbmcs320",
@@ -109,7 +104,6 @@ static struct platform_driver bt_sco_driver = {
.of_match_table = of_match_ptr(bt_sco_codec_of_match),
},
.probe = bt_sco_probe,
- .remove = bt_sco_remove,
.id_table = bt_sco_driver_ids,
};
diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c
index 14403b76c72434..32b6a417d0e8cd 100644
--- a/sound/soc/codecs/cq93vc.c
+++ b/sound/soc/codecs/cq93vc.c
@@ -134,18 +134,12 @@ static int cq93vc_platform_probe(struct platform_device *pdev)
&soc_component_dev_cq93vc, &cq93vc_dai, 1);
}
-static int cq93vc_platform_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static struct platform_driver cq93vc_codec_driver = {
.driver = {
.name = "cq93vc-codec",
},
.probe = cq93vc_platform_probe,
- .remove = cq93vc_platform_remove,
};
module_platform_driver(cq93vc_codec_driver);
diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c
index 39d28641429e4d..1117df4b2f1187 100644
--- a/sound/soc/codecs/cs35l45-i2c.c
+++ b/sound/soc/codecs/cs35l45-i2c.c
@@ -71,4 +71,3 @@ MODULE_DESCRIPTION("I2C CS35L45 driver");
MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_IMPORT_NS(SND_SOC_CS35L45);
-MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES);
diff --git a/sound/soc/codecs/cs35l45-spi.c b/sound/soc/codecs/cs35l45-spi.c
index baaf6e0f4fb9df..ffaca07fb26798 100644
--- a/sound/soc/codecs/cs35l45-spi.c
+++ b/sound/soc/codecs/cs35l45-spi.c
@@ -71,4 +71,3 @@ MODULE_DESCRIPTION("SPI CS35L45 driver");
MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_IMPORT_NS(SND_SOC_CS35L45);
-MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES);
diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c
index 5a2c2e684ef9fa..4b1320a2e6e97f 100644
--- a/sound/soc/codecs/cs35l45-tables.c
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -38,7 +38,7 @@ int cs35l45_apply_patch(struct cs35l45_private *cs35l45)
return regmap_register_patch(cs35l45->regmap, cs35l45_patch,
ARRAY_SIZE(cs35l45_patch));
}
-EXPORT_SYMBOL_NS_GPL(cs35l45_apply_patch, SND_SOC_CS35L45_TABLES);
+EXPORT_SYMBOL_NS_GPL(cs35l45_apply_patch, SND_SOC_CS35L45);
static const struct reg_default cs35l45_defaults[] = {
{ CS35L45_BLOCK_ENABLES, 0x00003323 },
@@ -126,7 +126,7 @@ const struct regmap_config cs35l45_i2c_regmap = {
.readable_reg = cs35l45_readable_reg,
.cache_type = REGCACHE_RBTREE,
};
-EXPORT_SYMBOL_NS_GPL(cs35l45_i2c_regmap, SND_SOC_CS35L45_TABLES);
+EXPORT_SYMBOL_NS_GPL(cs35l45_i2c_regmap, SND_SOC_CS35L45);
const struct regmap_config cs35l45_spi_regmap = {
.reg_bits = 32,
@@ -142,7 +142,7 @@ const struct regmap_config cs35l45_spi_regmap = {
.readable_reg = cs35l45_readable_reg,
.cache_type = REGCACHE_RBTREE,
};
-EXPORT_SYMBOL_NS_GPL(cs35l45_spi_regmap, SND_SOC_CS35L45_TABLES);
+EXPORT_SYMBOL_NS_GPL(cs35l45_spi_regmap, SND_SOC_CS35L45);
static const struct {
u8 cfg_id;
@@ -195,8 +195,4 @@ unsigned int cs35l45_get_clk_freq_id(unsigned int freq)
return -EINVAL;
}
-EXPORT_SYMBOL_NS_GPL(cs35l45_get_clk_freq_id, SND_SOC_CS35L45_TABLES);
-
-MODULE_DESCRIPTION("ASoC CS35L45 driver tables");
-MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
-MODULE_LICENSE("Dual BSD/GPL");
+EXPORT_SYMBOL_NS_GPL(cs35l45_get_clk_freq_id, SND_SOC_CS35L45);
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
index d15b3b77c7eb09..855d9f13e6ff17 100644
--- a/sound/soc/codecs/cs35l45.c
+++ b/sound/soc/codecs/cs35l45.c
@@ -687,4 +687,3 @@ MODULE_DESCRIPTION("ASoC CS35L45 driver");
MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
MODULE_LICENSE("Dual BSD/GPL");
-MODULE_IMPORT_NS(SND_SOC_CS35L45_TABLES);
diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c
new file mode 100644
index 00000000000000..7b539ee55499e8
--- /dev/null
+++ b/sound/soc/codecs/cs42l42-sdw.c
@@ -0,0 +1,607 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// cs42l42-sdw.c -- CS42L42 ALSA SoC audio driver SoundWire driver
+//
+// Copyright (C) 2022 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of_irq.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+
+#include "cs42l42.h"
+
+#define CS42L42_SDW_CAPTURE_PORT 1
+#define CS42L42_SDW_PLAYBACK_PORT 2
+
+/* Register addresses are offset when sent over SoundWire */
+#define CS42L42_SDW_ADDR_OFFSET 0x8000
+
+#define CS42L42_SDW_MEM_ACCESS_STATUS 0xd0
+#define CS42L42_SDW_MEM_READ_DATA 0xd8
+
+#define CS42L42_SDW_LAST_LATE BIT(3)
+#define CS42L42_SDW_CMD_IN_PROGRESS BIT(2)
+#define CS42L42_SDW_RDATA_RDY BIT(0)
+
+#define CS42L42_DELAYED_READ_POLL_US 1
+#define CS42L42_DELAYED_READ_TIMEOUT_US 100
+
+static const struct snd_soc_dapm_route cs42l42_sdw_audio_map[] = {
+ /* Playback Path */
+ { "HP", NULL, "MIXER" },
+ { "MIXER", NULL, "DACSRC" },
+ { "DACSRC", NULL, "Playback" },
+
+ /* Capture Path */
+ { "ADCSRC", NULL, "HS" },
+ { "Capture", NULL, "ADCSRC" },
+};
+
+static int cs42l42_sdw_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component);
+
+ if (!cs42l42->init_done)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int cs42l42_sdw_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ int ret;
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ /* Needed for PLL configuration when we are notified of new bus config */
+ cs42l42->sample_rate = params_rate(params);
+
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ port_config.num = CS42L42_SDW_PLAYBACK_PORT;
+ else
+ port_config.num = CS42L42_SDW_CAPTURE_PORT;
+
+ ret = sdw_stream_add_slave(cs42l42->sdw_peripheral, &stream_config, &port_config, 1,
+ sdw_stream);
+ if (ret) {
+ dev_err(dai->dev, "Failed to add sdw stream: %d\n", ret);
+ return ret;
+ }
+
+ cs42l42_src_config(dai->component, params_rate(params));
+
+ return 0;
+}
+
+static int cs42l42_sdw_dai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component);
+
+ dev_dbg(dai->dev, "dai_prepare: sclk=%u rate=%u\n", cs42l42->sclk, cs42l42->sample_rate);
+
+ if (!cs42l42->sclk || !cs42l42->sample_rate)
+ return -EINVAL;
+
+ /*
+ * At this point we know the sample rate from hw_params, and the SWIRE_CLK from bus_config()
+ * callback. This could only fail if the ACPI or machine driver are misconfigured to allow
+ * an unsupported SWIRE_CLK and sample_rate combination.
+ */
+
+ return cs42l42_pll_config(dai->component, cs42l42->sclk, cs42l42->sample_rate);
+}
+
+static int cs42l42_sdw_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ sdw_stream_remove_slave(cs42l42->sdw_peripheral, sdw_stream);
+ cs42l42->sample_rate = 0;
+
+ return 0;
+}
+
+static int cs42l42_sdw_port_prep(struct sdw_slave *slave,
+ struct sdw_prepare_ch *prepare_ch,
+ enum sdw_port_prep_ops state)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&slave->dev);
+ unsigned int pdn_mask;
+
+ if (prepare_ch->num == CS42L42_SDW_PLAYBACK_PORT)
+ pdn_mask = CS42L42_HP_PDN_MASK;
+ else
+ pdn_mask = CS42L42_ADC_PDN_MASK;
+
+ if (state == SDW_OPS_PORT_PRE_PREP) {
+ dev_dbg(cs42l42->dev, "Prep Port pdn_mask:%x\n", pdn_mask);
+ regmap_clear_bits(cs42l42->regmap, CS42L42_PWR_CTL1, pdn_mask);
+ usleep_range(CS42L42_HP_ADC_EN_TIME_US, CS42L42_HP_ADC_EN_TIME_US + 1000);
+ } else if (state == SDW_OPS_PORT_POST_DEPREP) {
+ dev_dbg(cs42l42->dev, "Deprep Port pdn_mask:%x\n", pdn_mask);
+ regmap_set_bits(cs42l42->regmap, CS42L42_PWR_CTL1, pdn_mask);
+ }
+
+ return 0;
+}
+
+static int cs42l42_sdw_dai_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ if (!sdw_stream)
+ return 0;
+
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void cs42l42_sdw_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static const struct snd_soc_dai_ops cs42l42_sdw_dai_ops = {
+ .startup = cs42l42_sdw_dai_startup,
+ .shutdown = cs42l42_sdw_dai_shutdown,
+ .hw_params = cs42l42_sdw_dai_hw_params,
+ .prepare = cs42l42_sdw_dai_prepare,
+ .hw_free = cs42l42_sdw_dai_hw_free,
+ .mute_stream = cs42l42_mute_stream,
+ .set_stream = cs42l42_sdw_dai_set_sdw_stream,
+};
+
+static struct snd_soc_dai_driver cs42l42_sdw_dai = {
+ .name = "cs42l42-sdw",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ /* Restrict which rates and formats are supported */
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ /* Restrict which rates and formats are supported */
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .symmetric_rate = 1,
+ .ops = &cs42l42_sdw_dai_ops,
+};
+
+static int cs42l42_sdw_poll_status(struct sdw_slave *peripheral, u8 mask, u8 match)
+{
+ int ret, sdwret;
+
+ ret = read_poll_timeout(sdw_read_no_pm, sdwret,
+ (sdwret < 0) || ((sdwret & mask) == match),
+ CS42L42_DELAYED_READ_POLL_US, CS42L42_DELAYED_READ_TIMEOUT_US,
+ false, peripheral, CS42L42_SDW_MEM_ACCESS_STATUS);
+ if (ret == 0)
+ ret = sdwret;
+
+ if (ret < 0)
+ dev_err(&peripheral->dev, "MEM_ACCESS_STATUS & %#x for %#x fail: %d\n",
+ mask, match, ret);
+
+ return ret;
+}
+
+static int cs42l42_sdw_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct sdw_slave *peripheral = context;
+ u8 data;
+ int ret;
+
+ reg += CS42L42_SDW_ADDR_OFFSET;
+
+ ret = cs42l42_sdw_poll_status(peripheral, CS42L42_SDW_CMD_IN_PROGRESS, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = sdw_read_no_pm(peripheral, reg);
+ if (ret < 0) {
+ dev_err(&peripheral->dev, "Failed to issue read @0x%x: %d\n", reg, ret);
+ return ret;
+ }
+
+ data = (u8)ret; /* possible non-delayed read value */
+ ret = sdw_read_no_pm(peripheral, CS42L42_SDW_MEM_ACCESS_STATUS);
+ if (ret < 0) {
+ dev_err(&peripheral->dev, "Failed to read MEM_ACCESS_STATUS: %d\n", ret);
+ return ret;
+ }
+
+ /* If read was not delayed we already have the result */
+ if ((ret & CS42L42_SDW_LAST_LATE) == 0) {
+ *val = data;
+ return 0;
+ }
+
+ /* Poll for delayed read completion */
+ if ((ret & CS42L42_SDW_RDATA_RDY) == 0) {
+ ret = cs42l42_sdw_poll_status(peripheral,
+ CS42L42_SDW_RDATA_RDY, CS42L42_SDW_RDATA_RDY);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = sdw_read_no_pm(peripheral, CS42L42_SDW_MEM_READ_DATA);
+ if (ret < 0) {
+ dev_err(&peripheral->dev, "Failed to read READ_DATA: %d\n", ret);
+ return ret;
+ }
+
+ *val = (u8)ret;
+
+ return 0;
+}
+
+static int cs42l42_sdw_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct sdw_slave *peripheral = context;
+ int ret;
+
+ ret = cs42l42_sdw_poll_status(peripheral, CS42L42_SDW_CMD_IN_PROGRESS, 0);
+ if (ret < 0)
+ return ret;
+
+ return sdw_write_no_pm(peripheral, reg + CS42L42_SDW_ADDR_OFFSET, (u8)val);
+}
+
+/* Initialise cs42l42 using SoundWire - this is only called once, during initialisation */
+static void cs42l42_sdw_init(struct sdw_slave *peripheral)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev);
+ int ret;
+
+ regcache_cache_only(cs42l42->regmap, false);
+
+ ret = cs42l42_init(cs42l42);
+ if (ret < 0) {
+ regcache_cache_only(cs42l42->regmap, true);
+ goto err;
+ }
+
+ /* Write out any cached changes that happened between probe and attach */
+ ret = regcache_sync(cs42l42->regmap);
+ if (ret < 0)
+ dev_warn(cs42l42->dev, "Failed to sync cache: %d\n", ret);
+
+ /* Disable internal logic that makes clock-stop conditional */
+ regmap_clear_bits(cs42l42->regmap, CS42L42_PWR_CTL3, CS42L42_SW_CLK_STP_STAT_SEL_MASK);
+
+err:
+ /* This cancels the pm_runtime_get_noresume() call from cs42l42_sdw_probe(). */
+ pm_runtime_put_autosuspend(cs42l42->dev);
+}
+
+static int cs42l42_sdw_read_prop(struct sdw_slave *peripheral)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev);
+ struct sdw_slave_prop *prop = &peripheral->prop;
+ struct sdw_dpn_prop *ports;
+
+ ports = devm_kcalloc(cs42l42->dev, 2, sizeof(*ports), GFP_KERNEL);
+ if (!ports)
+ return -ENOMEM;
+
+ prop->source_ports = BIT(CS42L42_SDW_CAPTURE_PORT);
+ prop->sink_ports = BIT(CS42L42_SDW_PLAYBACK_PORT);
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+
+ /* DP1 - capture */
+ ports[0].num = CS42L42_SDW_CAPTURE_PORT,
+ ports[0].type = SDW_DPN_FULL,
+ ports[0].ch_prep_timeout = 10,
+ prop->src_dpn_prop = &ports[0];
+
+ /* DP2 - playback */
+ ports[1].num = CS42L42_SDW_PLAYBACK_PORT,
+ ports[1].type = SDW_DPN_FULL,
+ ports[1].ch_prep_timeout = 10,
+ prop->sink_dpn_prop = &ports[1];
+
+ return 0;
+}
+
+static int cs42l42_sdw_update_status(struct sdw_slave *peripheral,
+ enum sdw_slave_status status)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev);
+
+ switch (status) {
+ case SDW_SLAVE_ATTACHED:
+ dev_dbg(cs42l42->dev, "ATTACHED\n");
+ /*
+ * Initialise codec, this only needs to be done once.
+ * When resuming from suspend, resume callback will handle re-init of codec,
+ * using regcache_sync().
+ */
+ if (!cs42l42->init_done)
+ cs42l42_sdw_init(peripheral);
+ break;
+ case SDW_SLAVE_UNATTACHED:
+ dev_dbg(cs42l42->dev, "UNATTACHED\n");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs42l42_sdw_bus_config(struct sdw_slave *peripheral,
+ struct sdw_bus_params *params)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev);
+ unsigned int new_sclk = params->curr_dr_freq / 2;
+
+ /* The cs42l42 cannot support a glitchless SWIRE_CLK change. */
+ if ((new_sclk != cs42l42->sclk) && cs42l42->stream_use) {
+ dev_warn(cs42l42->dev, "Rejected SCLK change while audio active\n");
+ return -EBUSY;
+ }
+
+ cs42l42->sclk = new_sclk;
+
+ dev_dbg(cs42l42->dev, "bus_config: sclk=%u c=%u r=%u\n",
+ cs42l42->sclk, params->col, params->row);
+
+ return 0;
+}
+
+static const struct sdw_slave_ops cs42l42_sdw_ops = {
+/* No interrupt callback because only hardware INT is supported for Jack Detect in the CS42L42 */
+ .read_prop = cs42l42_sdw_read_prop,
+ .update_status = cs42l42_sdw_update_status,
+ .bus_config = cs42l42_sdw_bus_config,
+ .port_prep = cs42l42_sdw_port_prep,
+};
+
+static int __maybe_unused cs42l42_sdw_runtime_suspend(struct device *dev)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "Runtime suspend\n");
+
+ if (!cs42l42->init_done)
+ return 0;
+
+ /* The host controller could suspend, which would mean no register access */
+ regcache_cache_only(cs42l42->regmap, true);
+
+ return 0;
+}
+
+static const struct reg_sequence __maybe_unused cs42l42_soft_reboot_seq[] = {
+ REG_SEQ0(CS42L42_SOFT_RESET_REBOOT, 0x1e),
+};
+
+static int __maybe_unused cs42l42_sdw_handle_unattach(struct cs42l42_private *cs42l42)
+{
+ struct sdw_slave *peripheral = cs42l42->sdw_peripheral;
+
+ if (!peripheral->unattach_request)
+ return 0;
+
+ /* Cannot access registers until master re-attaches. */
+ dev_dbg(&peripheral->dev, "Wait for initialization_complete\n");
+ if (!wait_for_completion_timeout(&peripheral->initialization_complete,
+ msecs_to_jiffies(5000))) {
+ dev_err(&peripheral->dev, "initialization_complete timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ peripheral->unattach_request = 0;
+
+ /*
+ * After a bus reset there must be a reconfiguration reset to
+ * reinitialize the internal state of CS42L42.
+ */
+ regmap_multi_reg_write_bypassed(cs42l42->regmap,
+ cs42l42_soft_reboot_seq,
+ ARRAY_SIZE(cs42l42_soft_reboot_seq));
+ usleep_range(CS42L42_BOOT_TIME_US, CS42L42_BOOT_TIME_US * 2);
+ regcache_mark_dirty(cs42l42->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused cs42l42_sdw_runtime_resume(struct device *dev)
+{
+ static const unsigned int ts_dbnce_ms[] = { 0, 125, 250, 500, 750, 1000, 1250, 1500};
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+ unsigned int dbnce;
+ int ret;
+
+ dev_dbg(dev, "Runtime resume\n");
+
+ if (!cs42l42->init_done)
+ return 0;
+
+ ret = cs42l42_sdw_handle_unattach(cs42l42);
+ if (ret < 0) {
+ return ret;
+ } else if (ret > 0) {
+ dbnce = max(cs42l42->ts_dbnc_rise, cs42l42->ts_dbnc_fall);
+
+ if (dbnce > 0)
+ msleep(ts_dbnce_ms[dbnce]);
+ }
+
+ regcache_cache_only(cs42l42->regmap, false);
+
+ /* Sync LATCH_TO_VP first so the VP domain registers sync correctly */
+ regcache_sync_region(cs42l42->regmap, CS42L42_MIC_DET_CTL1, CS42L42_MIC_DET_CTL1);
+ regcache_sync(cs42l42->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused cs42l42_sdw_resume(struct device *dev)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "System resume\n");
+
+ /* Power-up so it can re-enumerate */
+ ret = cs42l42_resume(dev);
+ if (ret)
+ return ret;
+
+ /* Wait for re-attach */
+ ret = cs42l42_sdw_handle_unattach(cs42l42);
+ if (ret < 0)
+ return ret;
+
+ cs42l42_resume_restore(dev);
+
+ return 0;
+}
+
+static int cs42l42_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id)
+{
+ struct snd_soc_component_driver *component_drv;
+ struct device *dev = &peripheral->dev;
+ struct cs42l42_private *cs42l42;
+ struct regmap_config *regmap_conf;
+ struct regmap *regmap;
+ int irq, ret;
+
+ cs42l42 = devm_kzalloc(dev, sizeof(*cs42l42), GFP_KERNEL);
+ if (!cs42l42)
+ return -ENOMEM;
+
+ if (has_acpi_companion(dev))
+ irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0);
+ else
+ irq = of_irq_get(dev->of_node, 0);
+
+ if (irq == -ENOENT)
+ irq = 0;
+ else if (irq < 0)
+ return dev_err_probe(dev, irq, "Failed to get IRQ\n");
+
+ regmap_conf = devm_kmemdup(dev, &cs42l42_regmap, sizeof(cs42l42_regmap), GFP_KERNEL);
+ if (!regmap_conf)
+ return -ENOMEM;
+ regmap_conf->reg_bits = 16;
+ regmap_conf->num_ranges = 0;
+ regmap_conf->reg_read = cs42l42_sdw_read;
+ regmap_conf->reg_write = cs42l42_sdw_write;
+
+ regmap = devm_regmap_init(dev, NULL, peripheral, regmap_conf);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to allocate register map\n");
+
+ /* Start in cache-only until device is enumerated */
+ regcache_cache_only(regmap, true);
+
+ component_drv = devm_kmemdup(dev,
+ &cs42l42_soc_component,
+ sizeof(cs42l42_soc_component),
+ GFP_KERNEL);
+ if (!component_drv)
+ return -ENOMEM;
+
+ component_drv->dapm_routes = cs42l42_sdw_audio_map;
+ component_drv->num_dapm_routes = ARRAY_SIZE(cs42l42_sdw_audio_map);
+
+ cs42l42->dev = dev;
+ cs42l42->regmap = regmap;
+ cs42l42->sdw_peripheral = peripheral;
+ cs42l42->irq = irq;
+ cs42l42->devid = CS42L42_CHIP_ID;
+
+ /*
+ * pm_runtime is needed to control bus manager suspend, and to
+ * recover from an unattach_request when the manager suspends.
+ */
+ pm_runtime_set_autosuspend_delay(cs42l42->dev, 3000);
+ pm_runtime_use_autosuspend(cs42l42->dev);
+ pm_runtime_mark_last_busy(cs42l42->dev);
+ pm_runtime_set_active(cs42l42->dev);
+ pm_runtime_get_noresume(cs42l42->dev);
+ pm_runtime_enable(cs42l42->dev);
+
+ ret = cs42l42_common_probe(cs42l42, component_drv, &cs42l42_sdw_dai);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cs42l42_sdw_remove(struct sdw_slave *peripheral)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev);
+
+ cs42l42_common_remove(cs42l42);
+ pm_runtime_disable(cs42l42->dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs42l42_sdw_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_sdw_resume)
+ SET_RUNTIME_PM_OPS(cs42l42_sdw_runtime_suspend, cs42l42_sdw_runtime_resume, NULL)
+};
+
+static const struct sdw_device_id cs42l42_sdw_id[] = {
+ SDW_SLAVE_ENTRY(0x01FA, 0x4242, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, cs42l42_sdw_id);
+
+static struct sdw_driver cs42l42_sdw_driver = {
+ .driver = {
+ .name = "cs42l42-sdw",
+ .pm = &cs42l42_sdw_pm,
+ },
+ .probe = cs42l42_sdw_probe,
+ .remove = cs42l42_sdw_remove,
+ .ops = &cs42l42_sdw_ops,
+ .id_table = cs42l42_sdw_id,
+};
+
+module_sdw_driver(cs42l42_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L42 SoundWire driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(SND_SOC_CS42L42_CORE);
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
index 2fefbcf7bd130a..e3edaa1a2761e8 100644
--- a/sound/soc/codecs/cs42l42.c
+++ b/sound/soc/codecs/cs42l42.c
@@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
@@ -293,6 +294,7 @@ bool cs42l42_readable_register(struct device *dev, unsigned int reg)
case CS42L42_SPDIF_SW_CTL1:
case CS42L42_SRC_SDIN_FS:
case CS42L42_SRC_SDOUT_FS:
+ case CS42L42_SOFT_RESET_REBOOT:
case CS42L42_SPDIF_CTL1:
case CS42L42_SPDIF_CTL2:
case CS42L42_SPDIF_CTL3:
@@ -358,6 +360,7 @@ bool cs42l42_volatile_register(struct device *dev, unsigned int reg)
case CS42L42_LOAD_DET_DONE:
case CS42L42_DET_STATUS1:
case CS42L42_DET_STATUS2:
+ case CS42L42_SOFT_RESET_REBOOT:
return true;
default:
return false;
@@ -523,6 +526,10 @@ static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = {
/* Playback/Capture Requirements */
SND_SOC_DAPM_SUPPLY("SCLK", CS42L42_ASP_CLK_CFG, CS42L42_ASP_SCLK_EN_SHIFT, 0, NULL, 0),
+
+ /* Soundwire SRC power control */
+ SND_SOC_DAPM_PGA("DACSRC", CS42L42_PWR_CTL2, CS42L42_DAC_SRC_PDNB_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADCSRC", CS42L42_PWR_CTL2, CS42L42_ADC_SRC_PDNB_SHIFT, 0, NULL, 0),
};
static const struct snd_soc_dapm_route cs42l42_audio_map[] = {
@@ -590,7 +597,6 @@ const struct snd_soc_component_driver cs42l42_soc_component = {
.num_dapm_routes = ARRAY_SIZE(cs42l42_audio_map),
.controls = cs42l42_snd_controls,
.num_controls = ARRAY_SIZE(cs42l42_snd_controls),
- .idle_bias_on = 1,
.endianness = 1,
};
EXPORT_SYMBOL_NS_GPL(cs42l42_soc_component, SND_SOC_CS42L42_CORE);
@@ -651,11 +657,11 @@ static const struct cs42l42_pll_params pll_ratio_table[] = {
{ 24576000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1}
};
-static int cs42l42_pll_config(struct snd_soc_component *component, unsigned int clk)
+int cs42l42_pll_config(struct snd_soc_component *component, unsigned int clk,
+ unsigned int sample_rate)
{
struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
int i;
- u32 fsync;
/* Don't reconfigure if there is an audio stream running */
if (cs42l42->stream_use) {
@@ -666,6 +672,10 @@ static int cs42l42_pll_config(struct snd_soc_component *component, unsigned int
}
for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ /* MCLKint must be a multiple of the sample rate */
+ if (pll_ratio_table[i].mclk_int % sample_rate)
+ continue;
+
if (pll_ratio_table[i].sclk == clk) {
cs42l42->pll_config = i;
@@ -677,40 +687,6 @@ static int cs42l42_pll_config(struct snd_soc_component *component, unsigned int
(pll_ratio_table[i].mclk_int !=
24000000)) <<
CS42L42_INTERNAL_FS_SHIFT);
-
- /* Set up the LRCLK */
- fsync = clk / cs42l42->srate;
- if (((fsync * cs42l42->srate) != clk)
- || ((fsync % 2) != 0)) {
- dev_err(component->dev,
- "Unsupported sclk %d/sample rate %d\n",
- clk,
- cs42l42->srate);
- return -EINVAL;
- }
- /* Set the LRCLK period */
- snd_soc_component_update_bits(component,
- CS42L42_FSYNC_P_LOWER,
- CS42L42_FSYNC_PERIOD_MASK,
- CS42L42_FRAC0_VAL(fsync - 1) <<
- CS42L42_FSYNC_PERIOD_SHIFT);
- snd_soc_component_update_bits(component,
- CS42L42_FSYNC_P_UPPER,
- CS42L42_FSYNC_PERIOD_MASK,
- CS42L42_FRAC1_VAL(fsync - 1) <<
- CS42L42_FSYNC_PERIOD_SHIFT);
- /* Set the LRCLK to 50% duty cycle */
- fsync = fsync / 2;
- snd_soc_component_update_bits(component,
- CS42L42_FSYNC_PW_LOWER,
- CS42L42_FSYNC_PULSE_WIDTH_MASK,
- CS42L42_FRAC0_VAL(fsync - 1) <<
- CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
- snd_soc_component_update_bits(component,
- CS42L42_FSYNC_PW_UPPER,
- CS42L42_FSYNC_PULSE_WIDTH_MASK,
- CS42L42_FRAC1_VAL(fsync - 1) <<
- CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
if (pll_ratio_table[i].mclk_src_sel == 0) {
/* Pass the clock straight through */
snd_soc_component_update_bits(component,
@@ -768,8 +744,9 @@ static int cs42l42_pll_config(struct snd_soc_component *component, unsigned int
return -EINVAL;
}
+EXPORT_SYMBOL_NS_GPL(cs42l42_pll_config, SND_SOC_CS42L42_CORE);
-static void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate)
+void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate)
{
struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
unsigned int fs;
@@ -801,6 +778,47 @@ static void cs42l42_src_config(struct snd_soc_component *component, unsigned int
CS42L42_CLK_OASRC_SEL_MASK,
fs << CS42L42_CLK_OASRC_SEL_SHIFT);
}
+EXPORT_SYMBOL_NS_GPL(cs42l42_src_config, SND_SOC_CS42L42_CORE);
+
+static int cs42l42_asp_config(struct snd_soc_component *component,
+ unsigned int sclk, unsigned int sample_rate)
+{
+ u32 fsync = sclk / sample_rate;
+
+ /* Set up the LRCLK */
+ if (((fsync * sample_rate) != sclk) || ((fsync % 2) != 0)) {
+ dev_err(component->dev,
+ "Unsupported sclk %d/sample rate %d\n",
+ sclk,
+ sample_rate);
+ return -EINVAL;
+ }
+ /* Set the LRCLK period */
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_P_LOWER,
+ CS42L42_FSYNC_PERIOD_MASK,
+ CS42L42_FRAC0_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PERIOD_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_P_UPPER,
+ CS42L42_FSYNC_PERIOD_MASK,
+ CS42L42_FRAC1_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PERIOD_SHIFT);
+ /* Set the LRCLK to 50% duty cycle */
+ fsync = fsync / 2;
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_PW_LOWER,
+ CS42L42_FSYNC_PULSE_WIDTH_MASK,
+ CS42L42_FRAC0_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
+ snd_soc_component_update_bits(component,
+ CS42L42_FSYNC_PW_UPPER,
+ CS42L42_FSYNC_PULSE_WIDTH_MASK,
+ CS42L42_FRAC1_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
+
+ return 0;
+}
static int cs42l42_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
@@ -891,13 +909,12 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
unsigned int channels = params_channels(params);
unsigned int width = (params_width(params) / 8) - 1;
+ unsigned int sample_rate = params_rate(params);
unsigned int slot_width = 0;
unsigned int val = 0;
unsigned int bclk;
int ret;
- cs42l42->srate = params_rate(params);
-
if (cs42l42->bclk_ratio) {
/* machine driver has set the BCLK/samp-rate ratio */
bclk = cs42l42->bclk_ratio * params_rate(params);
@@ -954,11 +971,15 @@ static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
break;
}
- ret = cs42l42_pll_config(component, bclk);
+ ret = cs42l42_pll_config(component, bclk, sample_rate);
if (ret)
return ret;
- cs42l42_src_config(component, params_rate(params));
+ ret = cs42l42_asp_config(component, bclk, sample_rate);
+ if (ret)
+ return ret;
+
+ cs42l42_src_config(component, sample_rate);
return 0;
}
@@ -998,7 +1019,7 @@ static int cs42l42_set_bclk_ratio(struct snd_soc_dai *dai,
return 0;
}
-static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
struct snd_soc_component *component = dai->component;
struct cs42l42_private *cs42l42 = snd_soc_component_get_drvdata(component);
@@ -1091,6 +1112,7 @@ static int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
return 0;
}
+EXPORT_SYMBOL_NS_GPL(cs42l42_mute_stream, SND_SOC_CS42L42_CORE);
#define CS42L42_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S24_LE |\
@@ -1633,7 +1655,7 @@ static const struct cs42l42_irq_params irq_params_table[] = {
CS42L42_TSRS_PLUG_VAL_MASK}
};
-static irqreturn_t cs42l42_irq_thread(int irq, void *data)
+irqreturn_t cs42l42_irq_thread(int irq, void *data)
{
struct cs42l42_private *cs42l42 = (struct cs42l42_private *)data;
unsigned int stickies[12];
@@ -1642,9 +1664,11 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
unsigned int current_button_status;
unsigned int i;
+ pm_runtime_get_sync(cs42l42->dev);
mutex_lock(&cs42l42->irq_lock);
if (cs42l42->suspended || !cs42l42->init_done) {
mutex_unlock(&cs42l42->irq_lock);
+ pm_runtime_put_autosuspend(cs42l42->dev);
return IRQ_NONE;
}
@@ -1747,9 +1771,12 @@ static irqreturn_t cs42l42_irq_thread(int irq, void *data)
}
mutex_unlock(&cs42l42->irq_lock);
+ pm_runtime_mark_last_busy(cs42l42->dev);
+ pm_runtime_put_autosuspend(cs42l42->dev);
return IRQ_HANDLED;
}
+EXPORT_SYMBOL_NS_GPL(cs42l42_irq_thread, SND_SOC_CS42L42_CORE);
static void cs42l42_set_interrupt_masks(struct cs42l42_private *cs42l42)
{
@@ -2125,6 +2152,9 @@ int cs42l42_suspend(struct device *dev)
u8 save_regs[ARRAY_SIZE(cs42l42_shutdown_seq)];
int i, ret;
+ if (!cs42l42->init_done)
+ return 0;
+
/*
* Wait for threaded irq handler to be idle and stop it processing
* future interrupts. This ensures a safe disable if the interrupt
@@ -2185,6 +2215,9 @@ int cs42l42_resume(struct device *dev)
struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
int ret;
+ if (!cs42l42->init_done)
+ return 0;
+
/*
* If jack was unplugged and re-plugged during suspend it could
* have changed type but the tip-sense state hasn't changed.
@@ -2369,6 +2402,18 @@ int cs42l42_init(struct cs42l42_private *cs42l42)
if (ret != 0)
goto err_shutdown;
+ /*
+ * SRC power is linked to ASP power so doesn't work in Soundwire mode.
+ * Override it and use DAPM to control SRC power for Soundwire.
+ */
+ if (cs42l42->sdw_peripheral) {
+ regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL2,
+ CS42L42_SRC_PDN_OVERRIDE_MASK |
+ CS42L42_DAC_SRC_PDNB_MASK |
+ CS42L42_ADC_SRC_PDNB_MASK,
+ CS42L42_SRC_PDN_OVERRIDE_MASK);
+ }
+
/* Setup headset detection */
cs42l42_setup_hs_type_detect(cs42l42);
diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h
index a721366641127b..4bd7b85a574715 100644
--- a/sound/soc/codecs/cs42l42.h
+++ b/sound/soc/codecs/cs42l42.h
@@ -18,6 +18,7 @@
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <linux/soundwire/sdw.h>
#include <sound/jack.h>
#include <sound/cs42l42.h>
#include <sound/soc-component.h>
@@ -30,13 +31,14 @@ struct cs42l42_private {
struct gpio_desc *reset_gpio;
struct completion pdn_done;
struct snd_soc_jack *jack;
+ struct sdw_slave *sdw_peripheral;
struct mutex irq_lock;
int devid;
int irq;
int pll_config;
u32 sclk;
+ u32 sample_rate;
u32 bclk_ratio;
- u32 srate;
u8 plug_state;
u8 hs_type;
u8 ts_inv;
@@ -62,6 +64,11 @@ extern struct snd_soc_dai_driver cs42l42_dai;
bool cs42l42_readable_register(struct device *dev, unsigned int reg);
bool cs42l42_volatile_register(struct device *dev, unsigned int reg);
+int cs42l42_pll_config(struct snd_soc_component *component,
+ unsigned int clk, unsigned int sample_rate);
+void cs42l42_src_config(struct snd_soc_component *component, unsigned int sample_rate);
+int cs42l42_mute_stream(struct snd_soc_dai *dai, int mute, int stream);
+irqreturn_t cs42l42_irq_thread(int irq, void *data);
int cs42l42_suspend(struct device *dev);
int cs42l42_resume(struct device *dev);
void cs42l42_resume_restore(struct device *dev);
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index 544ccbcfc88442..0068780fe0a745 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -1157,13 +1157,31 @@ static int da7213_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct da7213_priv *da7213 = snd_soc_component_get_drvdata(component);
+ u8 dai_clk_mode = DA7213_DAI_BCLKS_PER_WCLK_64;
u8 dai_ctrl = 0;
u8 fs;
+ /* Set channels */
+ switch (params_channels(params)) {
+ case 1:
+ if (da7213->fmt != DA7213_DAI_FORMAT_DSP) {
+ dev_err(component->dev, "Mono supported only in DSP mode\n");
+ return -EINVAL;
+ }
+ dai_ctrl |= DA7213_DAI_MONO_MODE_EN;
+ break;
+ case 2:
+ dai_ctrl &= ~(DA7213_DAI_MONO_MODE_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+
/* Set DAI format */
switch (params_width(params)) {
case 16:
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S16_LE;
+ dai_clk_mode = DA7213_DAI_BCLKS_PER_WCLK_32; /* 32bit for 1ch and 2ch */
break;
case 20:
dai_ctrl |= DA7213_DAI_WORD_LENGTH_S20_LE;
@@ -1224,8 +1242,11 @@ static int da7213_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_component_update_bits(component, DA7213_DAI_CTRL, DA7213_DAI_WORD_LENGTH_MASK,
- dai_ctrl);
+ snd_soc_component_update_bits(component, DA7213_DAI_CLK_MODE,
+ DA7213_DAI_BCLKS_PER_WCLK_MASK, dai_clk_mode);
+
+ snd_soc_component_update_bits(component, DA7213_DAI_CTRL,
+ DA7213_DAI_WORD_LENGTH_MASK | DA7213_DAI_MONO_MODE_MASK, dai_ctrl);
snd_soc_component_write(component, DA7213_SR, fs);
return 0;
@@ -1300,19 +1321,24 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
dai_ctrl |= DA7213_DAI_FORMAT_I2S_MODE;
+ da7213->fmt = DA7213_DAI_FORMAT_I2S_MODE;
break;
case SND_SOC_DAIFMT_LEFT_J:
dai_ctrl |= DA7213_DAI_FORMAT_LEFT_J;
+ da7213->fmt = DA7213_DAI_FORMAT_LEFT_J;
break;
case SND_SOC_DAIFMT_RIGHT_J:
dai_ctrl |= DA7213_DAI_FORMAT_RIGHT_J;
+ da7213->fmt = DA7213_DAI_FORMAT_RIGHT_J;
break;
case SND_SOC_DAI_FORMAT_DSP_A: /* L data MSB after FRM LRC */
dai_ctrl |= DA7213_DAI_FORMAT_DSP;
dai_offset = 1;
+ da7213->fmt = DA7213_DAI_FORMAT_DSP;
break;
case SND_SOC_DAI_FORMAT_DSP_B: /* L data MSB during FRM LRC */
dai_ctrl |= DA7213_DAI_FORMAT_DSP;
+ da7213->fmt = DA7213_DAI_FORMAT_DSP;
break;
default:
return -EINVAL;
diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h
index 97ccf0ddd2be20..4ca9cfdea06d43 100644
--- a/sound/soc/codecs/da7213.h
+++ b/sound/soc/codecs/da7213.h
@@ -195,6 +195,8 @@
#define DA7213_DAI_WORD_LENGTH_S24_LE (0x2 << 2)
#define DA7213_DAI_WORD_LENGTH_S32_LE (0x3 << 2)
#define DA7213_DAI_WORD_LENGTH_MASK (0x3 << 2)
+#define DA7213_DAI_MONO_MODE_EN (0x1 << 4)
+#define DA7213_DAI_MONO_MODE_MASK (0x1 << 4)
#define DA7213_DAI_EN_SHIFT 7
/* DA7213_DIG_ROUTING_DAI = 0x21 */
@@ -542,6 +544,7 @@ struct da7213_priv {
bool alc_en;
bool fixed_clk_auto_pll;
struct da7213_platform_data *pdata;
+ int fmt;
};
#endif /* _DA7213_H */
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
index 28a0565c2a955b..28a0565c2a955b 100755..100644
--- a/sound/soc/codecs/es8326.c
+++ b/sound/soc/codecs/es8326.c
diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h
index 8e5ffe5ee10da3..8e5ffe5ee10da3 100755..100644
--- a/sound/soc/codecs/es8326.h
+++ b/sound/soc/codecs/es8326.h
diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c
index 4b8ec6f77337b7..d57b043d6bfefe 100644
--- a/sound/soc/codecs/hda.c
+++ b/sound/soc/codecs/hda.c
@@ -126,12 +126,15 @@ static void hda_codec_unregister_dais(struct hda_codec *codec,
struct hda_pcm *pcm;
for_each_component_dais_safe(component, dai, save) {
+ int stream;
+
list_for_each_entry(pcm, &codec->pcm_list_head, list) {
if (strcmp(dai->driver->name, pcm->name))
continue;
- snd_soc_dapm_free_widget(dai->playback_widget);
- snd_soc_dapm_free_widget(dai->capture_widget);
+ for_each_pcm_streams(stream)
+ snd_soc_dapm_free_widget(snd_soc_dai_get_widget(dai, stream));
+
snd_soc_unregister_dai(dai);
break;
}
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
index 74cbbe16f9aec2..01e8ffda2a4bf3 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -518,7 +518,7 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
- struct hdmi_codec_daifmt *cf = dai->playback_dma_data;
+ struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai);
struct hdmi_codec_params hp = {
.iec = {
.status = { 0 },
@@ -562,7 +562,7 @@ static int hdmi_codec_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
- struct hdmi_codec_daifmt *cf = dai->playback_dma_data;
+ struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int channels = runtime->channels;
unsigned int width = snd_pcm_format_width(runtime->format);
@@ -597,7 +597,7 @@ static int hdmi_codec_prepare(struct snd_pcm_substream *substream,
static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
- struct hdmi_codec_daifmt *cf = dai->playback_dma_data;
+ struct hdmi_codec_daifmt *cf = snd_soc_dai_dma_data_get_playback(dai);
/* Reset daifmt */
memset(cf, 0, sizeof(*cf));
@@ -834,7 +834,8 @@ static int hdmi_dai_probe(struct snd_soc_dai *dai)
if (!daifmt)
return -ENOMEM;
- dai->playback_dma_data = daifmt;
+ snd_soc_dai_dma_data_set_playback(dai, daifmt);
+
return 0;
}
@@ -891,7 +892,7 @@ static int hdmi_dai_spdif_probe(struct snd_soc_dai *dai)
if (ret)
return ret;
- cf = dai->playback_dma_data;
+ cf = snd_soc_dai_dma_data_get_playback(dai);
cf->fmt = HDMI_SPDIF;
return 0;
diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c
new file mode 100644
index 00000000000000..2cc7b9166e695f
--- /dev/null
+++ b/sound/soc/codecs/idt821034.c
@@ -0,0 +1,1178 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// IDT821034 ALSA SoC driver
+//
+// Copyright 2022 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <linux/bitrev.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define IDT821034_NB_CHANNEL 4
+
+struct idt821034_amp {
+ u16 gain;
+ bool is_muted;
+};
+
+struct idt821034 {
+ struct spi_device *spi;
+ struct mutex mutex;
+ u8 spi_tx_buf; /* Cannot use stack area for SPI (dma-safe memory) */
+ u8 spi_rx_buf; /* Cannot use stack area for SPI (dma-safe memory) */
+ struct {
+ u8 codec_conf;
+ struct {
+ u8 power;
+ u8 tx_slot;
+ u8 rx_slot;
+ u8 slic_conf;
+ u8 slic_control;
+ } ch[IDT821034_NB_CHANNEL];
+ } cache;
+ struct {
+ struct {
+ struct idt821034_amp amp_out;
+ struct idt821034_amp amp_in;
+ } ch[IDT821034_NB_CHANNEL];
+ } amps;
+ int max_ch_playback;
+ int max_ch_capture;
+ struct gpio_chip gpio_chip;
+};
+
+static int idt821034_8bit_write(struct idt821034 *idt821034, u8 val)
+{
+ struct spi_transfer xfer[] = {
+ {
+ .tx_buf = &idt821034->spi_tx_buf,
+ .len = 1,
+ }, {
+ .cs_off = 1,
+ .tx_buf = &idt821034->spi_tx_buf,
+ .len = 1,
+ }
+ };
+
+ idt821034->spi_tx_buf = val;
+
+ dev_vdbg(&idt821034->spi->dev, "spi xfer wr 0x%x\n", val);
+
+ return spi_sync_transfer(idt821034->spi, xfer, 2);
+}
+
+static int idt821034_2x8bit_write(struct idt821034 *idt821034, u8 val1, u8 val2)
+{
+ int ret;
+
+ ret = idt821034_8bit_write(idt821034, val1);
+ if (ret)
+ return ret;
+ return idt821034_8bit_write(idt821034, val2);
+}
+
+static int idt821034_8bit_read(struct idt821034 *idt821034, u8 valw, u8 *valr)
+{
+ struct spi_transfer xfer[] = {
+ {
+ .tx_buf = &idt821034->spi_tx_buf,
+ .rx_buf = &idt821034->spi_rx_buf,
+ .len = 1,
+ }, {
+ .cs_off = 1,
+ .tx_buf = &idt821034->spi_tx_buf,
+ .len = 1,
+ }
+ };
+ int ret;
+
+ idt821034->spi_tx_buf = valw;
+
+ ret = spi_sync_transfer(idt821034->spi, xfer, 2);
+ if (ret)
+ return ret;
+
+ *valr = idt821034->spi_rx_buf;
+
+ dev_vdbg(&idt821034->spi->dev, "spi xfer wr 0x%x, rd 0x%x\n",
+ valw, *valr);
+
+ return 0;
+}
+
+/* Available mode for the programming sequence */
+#define IDT821034_MODE_CODEC(_ch) (0x80 | ((_ch) << 2))
+#define IDT821034_MODE_SLIC(_ch) (0xD0 | ((_ch) << 2))
+#define IDT821034_MODE_GAIN(_ch) (0xC0 | ((_ch) << 2))
+
+/* Power values that can be used in 'power' (can be ORed) */
+#define IDT821034_CONF_PWRUP_TX BIT(1) /* from analog input to PCM */
+#define IDT821034_CONF_PWRUP_RX BIT(0) /* from PCM to analog output */
+
+static int idt821034_set_channel_power(struct idt821034 *idt821034, u8 ch, u8 power)
+{
+ u8 conf;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "set_channel_power(%u, 0x%x)\n", ch, power);
+
+ conf = IDT821034_MODE_CODEC(ch) | idt821034->cache.codec_conf;
+
+ if (power & IDT821034_CONF_PWRUP_RX) {
+ ret = idt821034_2x8bit_write(idt821034,
+ conf | IDT821034_CONF_PWRUP_RX,
+ idt821034->cache.ch[ch].rx_slot);
+ if (ret)
+ return ret;
+ }
+ if (power & IDT821034_CONF_PWRUP_TX) {
+ ret = idt821034_2x8bit_write(idt821034,
+ conf | IDT821034_CONF_PWRUP_TX,
+ idt821034->cache.ch[ch].tx_slot);
+ if (ret)
+ return ret;
+ }
+ if (!(power & (IDT821034_CONF_PWRUP_TX | IDT821034_CONF_PWRUP_RX))) {
+ ret = idt821034_2x8bit_write(idt821034, conf, 0);
+ if (ret)
+ return ret;
+ }
+
+ idt821034->cache.ch[ch].power = power;
+
+ return 0;
+}
+
+static u8 idt821034_get_channel_power(struct idt821034 *idt821034, u8 ch)
+{
+ return idt821034->cache.ch[ch].power;
+}
+
+/* Codec configuration values that can be used in 'codec_conf' (can be ORed) */
+#define IDT821034_CONF_ALAW_MODE BIT(5)
+#define IDT821034_CONF_DELAY_MODE BIT(4)
+
+static int idt821034_set_codec_conf(struct idt821034 *idt821034, u8 codec_conf)
+{
+ u8 conf;
+ u8 ts;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "set_codec_conf(0x%x)\n", codec_conf);
+
+ /* codec conf fields are common to all channel.
+ * Arbitrary use of channel 0 for this configuration.
+ */
+
+ /* Set Configuration Register */
+ conf = IDT821034_MODE_CODEC(0) | codec_conf;
+
+ /* Update conf value and timeslot register value according
+ * to cache values
+ */
+ if (idt821034->cache.ch[0].power & IDT821034_CONF_PWRUP_RX) {
+ conf |= IDT821034_CONF_PWRUP_RX;
+ ts = idt821034->cache.ch[0].rx_slot;
+ } else if (idt821034->cache.ch[0].power & IDT821034_CONF_PWRUP_TX) {
+ conf |= IDT821034_CONF_PWRUP_TX;
+ ts = idt821034->cache.ch[0].tx_slot;
+ } else {
+ ts = 0x00;
+ }
+
+ /* Write configuration register and time-slot register */
+ ret = idt821034_2x8bit_write(idt821034, conf, ts);
+ if (ret)
+ return ret;
+
+ idt821034->cache.codec_conf = codec_conf;
+ return 0;
+}
+
+static u8 idt821034_get_codec_conf(struct idt821034 *idt821034)
+{
+ return idt821034->cache.codec_conf;
+}
+
+/* Channel direction values that can be used in 'ch_dir' (can be ORed) */
+#define IDT821034_CH_RX BIT(0) /* from PCM to analog output */
+#define IDT821034_CH_TX BIT(1) /* from analog input to PCM */
+
+static int idt821034_set_channel_ts(struct idt821034 *idt821034, u8 ch, u8 ch_dir, u8 ts_num)
+{
+ u8 conf;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "set_channel_ts(%u, 0x%x, %d)\n", ch, ch_dir, ts_num);
+
+ conf = IDT821034_MODE_CODEC(ch) | idt821034->cache.codec_conf;
+
+ if (ch_dir & IDT821034_CH_RX) {
+ if (idt821034->cache.ch[ch].power & IDT821034_CONF_PWRUP_RX) {
+ ret = idt821034_2x8bit_write(idt821034,
+ conf | IDT821034_CONF_PWRUP_RX,
+ ts_num);
+ if (ret)
+ return ret;
+ }
+ idt821034->cache.ch[ch].rx_slot = ts_num;
+ }
+ if (ch_dir & IDT821034_CH_TX) {
+ if (idt821034->cache.ch[ch].power & IDT821034_CONF_PWRUP_TX) {
+ ret = idt821034_2x8bit_write(idt821034,
+ conf | IDT821034_CONF_PWRUP_TX,
+ ts_num);
+ if (ret)
+ return ret;
+ }
+ idt821034->cache.ch[ch].tx_slot = ts_num;
+ }
+
+ return 0;
+}
+
+/* SLIC direction values that can be used in 'slic_dir' (can be ORed) */
+#define IDT821034_SLIC_IO1_IN BIT(1)
+#define IDT821034_SLIC_IO0_IN BIT(0)
+
+static int idt821034_set_slic_conf(struct idt821034 *idt821034, u8 ch, u8 slic_dir)
+{
+ u8 conf;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "set_slic_conf(%u, 0x%x)\n", ch, slic_dir);
+
+ conf = IDT821034_MODE_SLIC(ch) | slic_dir;
+ ret = idt821034_2x8bit_write(idt821034, conf, idt821034->cache.ch[ch].slic_control);
+ if (ret)
+ return ret;
+
+ idt821034->cache.ch[ch].slic_conf = slic_dir;
+
+ return 0;
+}
+
+static u8 idt821034_get_slic_conf(struct idt821034 *idt821034, u8 ch)
+{
+ return idt821034->cache.ch[ch].slic_conf;
+}
+
+static int idt821034_write_slic_raw(struct idt821034 *idt821034, u8 ch, u8 slic_raw)
+{
+ u8 conf;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "write_slic_raw(%u, 0x%x)\n", ch, slic_raw);
+
+ /*
+ * On write, slic_raw is mapped as follow :
+ * b4: O_4
+ * b3: O_3
+ * b2: O_2
+ * b1: I/O_1
+ * b0: I/O_0
+ */
+
+ conf = IDT821034_MODE_SLIC(ch) | idt821034->cache.ch[ch].slic_conf;
+ ret = idt821034_2x8bit_write(idt821034, conf, slic_raw);
+ if (ret)
+ return ret;
+
+ idt821034->cache.ch[ch].slic_control = slic_raw;
+ return 0;
+}
+
+static u8 idt821034_get_written_slic_raw(struct idt821034 *idt821034, u8 ch)
+{
+ return idt821034->cache.ch[ch].slic_control;
+}
+
+static int idt821034_read_slic_raw(struct idt821034 *idt821034, u8 ch, u8 *slic_raw)
+{
+ u8 val;
+ int ret;
+
+ /*
+ * On read, slic_raw is mapped as follow :
+ * b7: I/O_0
+ * b6: I/O_1
+ * b5: O_2
+ * b4: O_3
+ * b3: O_4
+ * b2: I/O1_0, I/O_0 from channel 1 (no matter ch value)
+ * b1: I/O2_0, I/O_0 from channel 2 (no matter ch value)
+ * b2: I/O3_0, I/O_0 from channel 3 (no matter ch value)
+ */
+
+ val = IDT821034_MODE_SLIC(ch) | idt821034->cache.ch[ch].slic_conf;
+ ret = idt821034_8bit_write(idt821034, val);
+ if (ret)
+ return ret;
+
+ ret = idt821034_8bit_read(idt821034, idt821034->cache.ch[ch].slic_control, slic_raw);
+ if (ret)
+ return ret;
+
+ dev_dbg(&idt821034->spi->dev, "read_slic_raw(%i) 0x%x\n", ch, *slic_raw);
+
+ return 0;
+}
+
+/* Gain type values that can be used in 'gain_type' (cannot be ORed) */
+#define IDT821034_GAIN_RX (0 << 1) /* from PCM to analog output */
+#define IDT821034_GAIN_TX (1 << 1) /* from analog input to PCM */
+
+static int idt821034_set_gain_channel(struct idt821034 *idt821034, u8 ch,
+ u8 gain_type, u16 gain_val)
+{
+ u8 conf;
+ int ret;
+
+ dev_dbg(&idt821034->spi->dev, "set_gain_channel(%u, 0x%x, 0x%x-%d)\n",
+ ch, gain_type, gain_val, gain_val);
+
+ /*
+ * The gain programming coefficients should be calculated as:
+ * Transmit : Coeff_X = round [ gain_X0dB × gain_X ]
+ * Receive: Coeff_R = round [ gain_R0dB × gain_R ]
+ * where:
+ * gain_X0dB = 1820;
+ * gain_X is the target gain;
+ * Coeff_X should be in the range of 0 to 8192.
+ * gain_R0dB = 2506;
+ * gain_R is the target gain;
+ * Coeff_R should be in the range of 0 to 8192.
+ *
+ * A gain programming coefficient is 14-bit wide and in binary format.
+ * The 7 Most Significant Bits of the coefficient is called
+ * GA_MSB_Transmit for transmit path, or is called GA_MSB_Receive for
+ * receive path; The 7 Least Significant Bits of the coefficient is
+ * called GA_LSB_ Transmit for transmit path, or is called
+ * GA_LSB_Receive for receive path.
+ *
+ * An example is given below to clarify the calculation of the
+ * coefficient. To program a +3 dB gain in transmit path and a -3.5 dB
+ * gain in receive path:
+ *
+ * Linear Code of +3dB = 10^(3/20)= 1.412537545
+ * Coeff_X = round (1820 × 1.412537545) = 2571
+ * = 0b001010_00001011
+ * GA_MSB_Transmit = 0b0010100
+ * GA_LSB_Transmit = 0b0001011
+ *
+ * Linear Code of -3.5dB = 10^(-3.5/20) = 0.668343917
+ * Coeff_R= round (2506 × 0.668343917) = 1675
+ * = 0b0001101_0001011
+ * GA_MSB_Receive = 0b0001101
+ * GA_LSB_Receive = 0b0001011
+ */
+
+ conf = IDT821034_MODE_GAIN(ch) | gain_type;
+
+ ret = idt821034_2x8bit_write(idt821034, conf | 0x00, gain_val & 0x007F);
+ if (ret)
+ return ret;
+
+ ret = idt821034_2x8bit_write(idt821034, conf | 0x01, (gain_val >> 7) & 0x7F);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* Id helpers used in controls and dapm */
+#define IDT821034_DIR_OUT (1 << 3)
+#define IDT821034_DIR_IN (0 << 3)
+#define IDT821034_ID(_ch, _dir) (((_ch) & 0x03) | (_dir))
+#define IDT821034_ID_OUT(_ch) IDT821034_ID(_ch, IDT821034_DIR_OUT)
+#define IDT821034_ID_IN(_ch) IDT821034_ID(_ch, IDT821034_DIR_IN)
+
+#define IDT821034_ID_GET_CHAN(_id) ((_id) & 0x03)
+#define IDT821034_ID_GET_DIR(_id) ((_id) & (1 << 3))
+#define IDT821034_ID_IS_OUT(_id) (IDT821034_ID_GET_DIR(_id) == IDT821034_DIR_OUT)
+
+static int idt821034_kctrl_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ int min = mc->min;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ int val;
+ u8 ch;
+
+ ch = IDT821034_ID_GET_CHAN(mc->reg);
+
+ mutex_lock(&idt821034->mutex);
+ if (IDT821034_ID_IS_OUT(mc->reg))
+ val = idt821034->amps.ch[ch].amp_out.gain;
+ else
+ val = idt821034->amps.ch[ch].amp_in.gain;
+ mutex_unlock(&idt821034->mutex);
+
+ ucontrol->value.integer.value[0] = val & mask;
+ if (invert)
+ ucontrol->value.integer.value[0] = max - ucontrol->value.integer.value[0];
+ else
+ ucontrol->value.integer.value[0] = ucontrol->value.integer.value[0] - min;
+
+ return 0;
+}
+
+static int idt821034_kctrl_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ struct idt821034_amp *amp;
+ int min = mc->min;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ int ret;
+ u8 gain_type;
+ u8 ch;
+
+ val = ucontrol->value.integer.value[0];
+ if (val > max - min)
+ return -EINVAL;
+
+ if (invert)
+ val = (max - val) & mask;
+ else
+ val = (val + min) & mask;
+
+ ch = IDT821034_ID_GET_CHAN(mc->reg);
+
+ mutex_lock(&idt821034->mutex);
+
+ if (IDT821034_ID_IS_OUT(mc->reg)) {
+ amp = &idt821034->amps.ch[ch].amp_out;
+ gain_type = IDT821034_GAIN_RX;
+ } else {
+ amp = &idt821034->amps.ch[ch].amp_in;
+ gain_type = IDT821034_GAIN_TX;
+ }
+
+ if (amp->gain == val) {
+ ret = 0;
+ goto end;
+ }
+
+ if (!amp->is_muted) {
+ ret = idt821034_set_gain_channel(idt821034, ch, gain_type, val);
+ if (ret)
+ goto end;
+ }
+
+ amp->gain = val;
+ ret = 1; /* The value changed */
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_kctrl_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ int id = kcontrol->private_value;
+ bool is_muted;
+ u8 ch;
+
+ ch = IDT821034_ID_GET_CHAN(id);
+
+ mutex_lock(&idt821034->mutex);
+ is_muted = IDT821034_ID_IS_OUT(id) ?
+ idt821034->amps.ch[ch].amp_out.is_muted :
+ idt821034->amps.ch[ch].amp_in.is_muted;
+ mutex_unlock(&idt821034->mutex);
+
+ ucontrol->value.integer.value[0] = !is_muted;
+
+ return 0;
+}
+
+static int idt821034_kctrl_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ int id = kcontrol->private_value;
+ struct idt821034_amp *amp;
+ bool is_mute;
+ u8 gain_type;
+ int ret;
+ u8 ch;
+
+ ch = IDT821034_ID_GET_CHAN(id);
+ is_mute = !ucontrol->value.integer.value[0];
+
+ mutex_lock(&idt821034->mutex);
+
+ if (IDT821034_ID_IS_OUT(id)) {
+ amp = &idt821034->amps.ch[ch].amp_out;
+ gain_type = IDT821034_GAIN_RX;
+ } else {
+ amp = &idt821034->amps.ch[ch].amp_in;
+ gain_type = IDT821034_GAIN_TX;
+ }
+
+ if (amp->is_muted == is_mute) {
+ ret = 0;
+ goto end;
+ }
+
+ ret = idt821034_set_gain_channel(idt821034, ch, gain_type,
+ is_mute ? 0 : amp->gain);
+ if (ret)
+ goto end;
+
+ amp->is_muted = is_mute;
+ ret = 1; /* The value changed */
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static const DECLARE_TLV_DB_LINEAR(idt821034_gain_in, -6520, 1306);
+#define IDT821034_GAIN_IN_MIN_RAW 1 /* -65.20 dB -> 10^(-65.2/20.0) * 1820 = 1 */
+#define IDT821034_GAIN_IN_MAX_RAW 8191 /* 13.06 dB -> 10^(13.06/20.0) * 1820 = 8191 */
+#define IDT821034_GAIN_IN_INIT_RAW 1820 /* 0dB -> 10^(0/20) * 1820 = 1820 */
+
+static const DECLARE_TLV_DB_LINEAR(idt821034_gain_out, -6798, 1029);
+#define IDT821034_GAIN_OUT_MIN_RAW 1 /* -67.98 dB -> 10^(-67.98/20.0) * 2506 = 1*/
+#define IDT821034_GAIN_OUT_MAX_RAW 8191 /* 10.29 dB -> 10^(10.29/20.0) * 2506 = 8191 */
+#define IDT821034_GAIN_OUT_INIT_RAW 2506 /* 0dB -> 10^(0/20) * 2506 = 2506 */
+
+static const struct snd_kcontrol_new idt821034_controls[] = {
+ /* DAC volume control */
+ SOC_SINGLE_RANGE_EXT_TLV("DAC0 Playback Volume", IDT821034_ID_OUT(0), 0,
+ IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_out),
+ SOC_SINGLE_RANGE_EXT_TLV("DAC1 Playback Volume", IDT821034_ID_OUT(1), 0,
+ IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_out),
+ SOC_SINGLE_RANGE_EXT_TLV("DAC2 Playback Volume", IDT821034_ID_OUT(2), 0,
+ IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_out),
+ SOC_SINGLE_RANGE_EXT_TLV("DAC3 Playback Volume", IDT821034_ID_OUT(3), 0,
+ IDT821034_GAIN_OUT_MIN_RAW, IDT821034_GAIN_OUT_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_out),
+
+ /* DAC mute control */
+ SOC_SINGLE_BOOL_EXT("DAC0 Playback Switch", IDT821034_ID_OUT(0),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("DAC1 Playback Switch", IDT821034_ID_OUT(1),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("DAC2 Playback Switch", IDT821034_ID_OUT(2),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("DAC3 Playback Switch", IDT821034_ID_OUT(3),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+
+ /* ADC volume control */
+ SOC_SINGLE_RANGE_EXT_TLV("ADC0 Capture Volume", IDT821034_ID_IN(0), 0,
+ IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_in),
+ SOC_SINGLE_RANGE_EXT_TLV("ADC1 Capture Volume", IDT821034_ID_IN(1), 0,
+ IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_in),
+ SOC_SINGLE_RANGE_EXT_TLV("ADC2 Capture Volume", IDT821034_ID_IN(2), 0,
+ IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_in),
+ SOC_SINGLE_RANGE_EXT_TLV("ADC3 Capture Volume", IDT821034_ID_IN(3), 0,
+ IDT821034_GAIN_IN_MIN_RAW, IDT821034_GAIN_IN_MAX_RAW,
+ 0, idt821034_kctrl_gain_get, idt821034_kctrl_gain_put,
+ idt821034_gain_in),
+
+ /* ADC mute control */
+ SOC_SINGLE_BOOL_EXT("ADC0 Capture Switch", IDT821034_ID_IN(0),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("ADC1 Capture Switch", IDT821034_ID_IN(1),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("ADC2 Capture Switch", IDT821034_ID_IN(2),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+ SOC_SINGLE_BOOL_EXT("ADC3 Capture Switch", IDT821034_ID_IN(3),
+ idt821034_kctrl_mute_get, idt821034_kctrl_mute_put),
+};
+
+static int idt821034_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ unsigned int id = w->shift;
+ u8 power, mask;
+ int ret;
+ u8 ch;
+
+ ch = IDT821034_ID_GET_CHAN(id);
+ mask = IDT821034_ID_IS_OUT(id) ? IDT821034_CONF_PWRUP_RX : IDT821034_CONF_PWRUP_TX;
+
+ mutex_lock(&idt821034->mutex);
+
+ power = idt821034_get_channel_power(idt821034, ch);
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ power |= mask;
+ else
+ power &= ~mask;
+ ret = idt821034_set_channel_power(idt821034, ch, power);
+
+ mutex_unlock(&idt821034->mutex);
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget idt821034_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("DAC0", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(0), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC1", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(1), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC2", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(2), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC3", "Playback", SND_SOC_NOPM, IDT821034_ID_OUT(3), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUTPUT("OUT0"),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+
+ SND_SOC_DAPM_DAC_E("ADC0", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(0), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("ADC1", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(1), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("ADC2", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(2), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("ADC3", "Capture", SND_SOC_NOPM, IDT821034_ID_IN(3), 0,
+ idt821034_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("IN0"),
+ SND_SOC_DAPM_INPUT("IN1"),
+ SND_SOC_DAPM_INPUT("IN2"),
+ SND_SOC_DAPM_INPUT("IN3"),
+};
+
+static const struct snd_soc_dapm_route idt821034_dapm_routes[] = {
+ { "OUT0", NULL, "DAC0" },
+ { "OUT1", NULL, "DAC1" },
+ { "OUT2", NULL, "DAC2" },
+ { "OUT3", NULL, "DAC3" },
+
+ { "ADC0", NULL, "IN0" },
+ { "ADC1", NULL, "IN1" },
+ { "ADC2", NULL, "IN2" },
+ { "ADC3", NULL, "IN3" },
+};
+
+static int idt821034_dai_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int width)
+{
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int mask;
+ u8 slot;
+ int ret;
+ u8 ch;
+
+ switch (width) {
+ case 0: /* Not set -> default 8 */
+ case 8:
+ break;
+ default:
+ dev_err(dai->dev, "tdm slot width %d not supported\n", width);
+ return -EINVAL;
+ }
+
+ mask = tx_mask;
+ slot = 0;
+ ch = 0;
+ while (mask && ch < IDT821034_NB_CHANNEL) {
+ if (mask & 0x1) {
+ mutex_lock(&idt821034->mutex);
+ ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_RX, slot);
+ mutex_unlock(&idt821034->mutex);
+ if (ret) {
+ dev_err(dai->dev, "ch%u set tx tdm slot failed (%d)\n",
+ ch, ret);
+ return ret;
+ }
+ ch++;
+ }
+ mask >>= 1;
+ slot++;
+ }
+ if (mask) {
+ dev_err(dai->dev, "too much tx slots defined (mask = 0x%x) support max %d\n",
+ tx_mask, IDT821034_NB_CHANNEL);
+ return -EINVAL;
+ }
+ idt821034->max_ch_playback = ch;
+
+ mask = rx_mask;
+ slot = 0;
+ ch = 0;
+ while (mask && ch < IDT821034_NB_CHANNEL) {
+ if (mask & 0x1) {
+ mutex_lock(&idt821034->mutex);
+ ret = idt821034_set_channel_ts(idt821034, ch, IDT821034_CH_TX, slot);
+ mutex_unlock(&idt821034->mutex);
+ if (ret) {
+ dev_err(dai->dev, "ch%u set rx tdm slot failed (%d)\n",
+ ch, ret);
+ return ret;
+ }
+ ch++;
+ }
+ mask >>= 1;
+ slot++;
+ }
+ if (mask) {
+ dev_err(dai->dev, "too much rx slots defined (mask = 0x%x) support max %d\n",
+ rx_mask, IDT821034_NB_CHANNEL);
+ return -EINVAL;
+ }
+ idt821034->max_ch_capture = ch;
+
+ return 0;
+}
+
+static int idt821034_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
+ u8 conf;
+ int ret;
+
+ mutex_lock(&idt821034->mutex);
+
+ conf = idt821034_get_codec_conf(idt821034);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ conf |= IDT821034_CONF_DELAY_MODE;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ conf &= ~IDT821034_CONF_DELAY_MODE;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported DAI format 0x%x\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ ret = -EINVAL;
+ goto end;
+ }
+ ret = idt821034_set_codec_conf(idt821034, conf);
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
+ u8 conf;
+ int ret;
+
+ mutex_lock(&idt821034->mutex);
+
+ conf = idt821034_get_codec_conf(idt821034);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_A_LAW:
+ conf |= IDT821034_CONF_ALAW_MODE;
+ break;
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ conf &= ~IDT821034_CONF_ALAW_MODE;
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported PCM format 0x%x\n",
+ params_format(params));
+ ret = -EINVAL;
+ goto end;
+ }
+ ret = idt821034_set_codec_conf(idt821034, conf);
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static const unsigned int idt821034_sample_bits[] = {8};
+
+static struct snd_pcm_hw_constraint_list idt821034_sample_bits_constr = {
+ .list = idt821034_sample_bits,
+ .count = ARRAY_SIZE(idt821034_sample_bits),
+};
+
+static int idt821034_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int max_ch = 0;
+ int ret;
+
+ max_ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ idt821034->max_ch_playback : idt821034->max_ch_capture;
+
+ /*
+ * Disable stream support (min = 0, max = 0) if no timeslots were
+ * configured otherwise, limit the number of channels to those
+ * configured.
+ */
+ ret = snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS,
+ max_ch ? 1 : 0, max_ch);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &idt821034_sample_bits_constr);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static u64 idt821034_dai_formats[] = {
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
+static const struct snd_soc_dai_ops idt821034_dai_ops = {
+ .startup = idt821034_dai_startup,
+ .hw_params = idt821034_dai_hw_params,
+ .set_tdm_slot = idt821034_dai_set_tdm_slot,
+ .set_fmt = idt821034_dai_set_fmt,
+ .auto_selectable_formats = idt821034_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(idt821034_dai_formats),
+};
+
+static struct snd_soc_dai_driver idt821034_dai_driver = {
+ .name = "idt821034",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = IDT821034_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = IDT821034_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+ },
+ .ops = &idt821034_dai_ops,
+};
+
+static int idt821034_reset_audio(struct idt821034 *idt821034)
+{
+ int ret;
+ u8 i;
+
+ mutex_lock(&idt821034->mutex);
+
+ ret = idt821034_set_codec_conf(idt821034, 0);
+ if (ret)
+ goto end;
+
+ for (i = 0; i < IDT821034_NB_CHANNEL; i++) {
+ idt821034->amps.ch[i].amp_out.gain = IDT821034_GAIN_OUT_INIT_RAW;
+ idt821034->amps.ch[i].amp_out.is_muted = false;
+ ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_RX,
+ idt821034->amps.ch[i].amp_out.gain);
+ if (ret)
+ goto end;
+
+ idt821034->amps.ch[i].amp_in.gain = IDT821034_GAIN_IN_INIT_RAW;
+ idt821034->amps.ch[i].amp_in.is_muted = false;
+ ret = idt821034_set_gain_channel(idt821034, i, IDT821034_GAIN_TX,
+ idt821034->amps.ch[i].amp_in.gain);
+ if (ret)
+ goto end;
+
+ ret = idt821034_set_channel_power(idt821034, i, 0);
+ if (ret)
+ goto end;
+ }
+
+ ret = 0;
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_component_probe(struct snd_soc_component *component)
+{
+ struct idt821034 *idt821034 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* reset idt821034 audio part*/
+ ret = idt821034_reset_audio(idt821034);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver idt821034_component_driver = {
+ .probe = idt821034_component_probe,
+ .controls = idt821034_controls,
+ .num_controls = ARRAY_SIZE(idt821034_controls),
+ .dapm_widgets = idt821034_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(idt821034_dapm_widgets),
+ .dapm_routes = idt821034_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(idt821034_dapm_routes),
+ .endianness = 1,
+};
+
+#define IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(_offset) (((_offset) / 5) % 4)
+#define IDT821034_GPIO_OFFSET_TO_SLIC_MASK(_offset) BIT((_offset) % 5)
+
+static void idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val)
+{
+ u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
+ u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
+ struct idt821034 *idt821034 = gpiochip_get_data(c);
+ u8 slic_raw;
+ int ret;
+
+ mutex_lock(&idt821034->mutex);
+
+ slic_raw = idt821034_get_written_slic_raw(idt821034, ch);
+ if (val)
+ slic_raw |= mask;
+ else
+ slic_raw &= ~mask;
+ ret = idt821034_write_slic_raw(idt821034, ch, slic_raw);
+ if (ret) {
+ dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n",
+ offset, ch, mask, ret);
+ }
+
+ mutex_unlock(&idt821034->mutex);
+}
+
+static int idt821034_chip_gpio_get(struct gpio_chip *c, unsigned int offset)
+{
+ u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
+ u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
+ struct idt821034 *idt821034 = gpiochip_get_data(c);
+ u8 slic_raw;
+ int ret;
+
+ mutex_lock(&idt821034->mutex);
+ ret = idt821034_read_slic_raw(idt821034, ch, &slic_raw);
+ mutex_unlock(&idt821034->mutex);
+ if (ret) {
+ dev_err(&idt821034->spi->dev, "get gpio %d (%u, 0x%x) failed (%d)\n",
+ offset, ch, mask, ret);
+ return ret;
+ }
+
+ /*
+ * SLIC IOs are read in reverse order compared to write.
+ * Reverse the read value here in order to have IO0 at lsb (ie same
+ * order as write)
+ */
+ return !!(bitrev8(slic_raw) & mask);
+}
+
+static int idt821034_chip_get_direction(struct gpio_chip *c, unsigned int offset)
+{
+ u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
+ u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
+ struct idt821034 *idt821034 = gpiochip_get_data(c);
+ u8 slic_dir;
+
+ mutex_lock(&idt821034->mutex);
+ slic_dir = idt821034_get_slic_conf(idt821034, ch);
+ mutex_unlock(&idt821034->mutex);
+
+ return slic_dir & mask ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT;
+}
+
+static int idt821034_chip_direction_input(struct gpio_chip *c, unsigned int offset)
+{
+ u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
+ u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
+ struct idt821034 *idt821034 = gpiochip_get_data(c);
+ u8 slic_conf;
+ int ret;
+
+ /* Only IO0 and IO1 can be set as input */
+ if (mask & ~(IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN))
+ return -EPERM;
+
+ mutex_lock(&idt821034->mutex);
+
+ slic_conf = idt821034_get_slic_conf(idt821034, ch) | mask;
+
+ ret = idt821034_set_slic_conf(idt821034, ch, slic_conf);
+ if (ret) {
+ dev_err(&idt821034->spi->dev, "dir in gpio %d (%u, 0x%x) failed (%d)\n",
+ offset, ch, mask, ret);
+ }
+
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int offset, int val)
+{
+ u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
+ u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
+ struct idt821034 *idt821034 = gpiochip_get_data(c);
+ u8 slic_conf;
+ int ret;
+
+ idt821034_chip_gpio_set(c, offset, val);
+
+ mutex_lock(&idt821034->mutex);
+
+ slic_conf = idt821034_get_slic_conf(idt821034, ch) & ~mask;
+
+ ret = idt821034_set_slic_conf(idt821034, ch, slic_conf);
+ if (ret) {
+ dev_err(&idt821034->spi->dev, "dir in gpio %d (%u, 0x%x) failed (%d)\n",
+ offset, ch, mask, ret);
+ }
+
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_reset_gpio(struct idt821034 *idt821034)
+{
+ int ret;
+ u8 i;
+
+ mutex_lock(&idt821034->mutex);
+
+ /* IO0 and IO1 as input for all channels and output IO set to 0 */
+ for (i = 0; i < IDT821034_NB_CHANNEL; i++) {
+ ret = idt821034_set_slic_conf(idt821034, i,
+ IDT821034_SLIC_IO1_IN | IDT821034_SLIC_IO0_IN);
+ if (ret)
+ goto end;
+
+ ret = idt821034_write_slic_raw(idt821034, i, 0);
+ if (ret)
+ goto end;
+
+ }
+ ret = 0;
+end:
+ mutex_unlock(&idt821034->mutex);
+ return ret;
+}
+
+static int idt821034_gpio_init(struct idt821034 *idt821034)
+{
+ int ret;
+
+ ret = idt821034_reset_gpio(idt821034);
+ if (ret)
+ return ret;
+
+ idt821034->gpio_chip.owner = THIS_MODULE;
+ idt821034->gpio_chip.label = dev_name(&idt821034->spi->dev);
+ idt821034->gpio_chip.parent = &idt821034->spi->dev;
+ idt821034->gpio_chip.base = -1;
+ idt821034->gpio_chip.ngpio = 5 * 4; /* 5 GPIOs on 4 channels */
+ idt821034->gpio_chip.get_direction = idt821034_chip_get_direction;
+ idt821034->gpio_chip.direction_input = idt821034_chip_direction_input;
+ idt821034->gpio_chip.direction_output = idt821034_chip_direction_output;
+ idt821034->gpio_chip.get = idt821034_chip_gpio_get;
+ idt821034->gpio_chip.set = idt821034_chip_gpio_set;
+ idt821034->gpio_chip.can_sleep = true;
+
+ return devm_gpiochip_add_data(&idt821034->spi->dev, &idt821034->gpio_chip,
+ idt821034);
+}
+
+static int idt821034_spi_probe(struct spi_device *spi)
+{
+ struct idt821034 *idt821034;
+ int ret;
+
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ idt821034 = devm_kzalloc(&spi->dev, sizeof(*idt821034), GFP_KERNEL);
+ if (!idt821034)
+ return -ENOMEM;
+
+ idt821034->spi = spi;
+
+ mutex_init(&idt821034->mutex);
+
+ spi_set_drvdata(spi, idt821034);
+
+ ret = devm_snd_soc_register_component(&spi->dev, &idt821034_component_driver,
+ &idt821034_dai_driver, 1);
+ if (ret)
+ return ret;
+
+ if (IS_ENABLED(CONFIG_GPIOLIB))
+ return idt821034_gpio_init(idt821034);
+
+ return 0;
+}
+
+static const struct of_device_id idt821034_of_match[] = {
+ { .compatible = "renesas,idt821034", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, idt821034_of_match);
+
+static const struct spi_device_id idt821034_id_table[] = {
+ { "idt821034", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, idt821034_id_table);
+
+static struct spi_driver idt821034_spi_driver = {
+ .driver = {
+ .name = "idt821034",
+ .of_match_table = idt821034_of_match,
+ },
+ .id_table = idt821034_id_table,
+ .probe = idt821034_spi_probe,
+};
+
+module_spi_driver(idt821034_spi_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("IDT821034 ALSA SoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/jz4760.c b/sound/soc/codecs/jz4760.c
index d96a4f6c9183ef..9df58e23d3601b 100644
--- a/sound/soc/codecs/jz4760.c
+++ b/sound/soc/codecs/jz4760.c
@@ -287,6 +287,7 @@ static const DECLARE_TLV_DB_MINMAX_MUTE(dac_tlv, -3100, 100);
static const DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0);
static const DECLARE_TLV_DB_MINMAX(out_tlv, -2500, 100);
static const DECLARE_TLV_DB_SCALE(linein_tlv, -2500, 100, 0);
+static const DECLARE_TLV_DB_MINMAX(mixer_tlv, -3100, 0);
/* Unconditional controls. */
static const struct snd_kcontrol_new jz4760_codec_snd_controls[] = {
@@ -299,6 +300,14 @@ static const struct snd_kcontrol_new jz4760_codec_snd_controls[] = {
JZ4760_CODEC_REG_GCR4, JZ4760_CODEC_REG_GCR3,
REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, linein_tlv),
+ SOC_SINGLE_TLV("Mixer Capture Volume",
+ JZ4760_CODEC_REG_MIX1,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv),
+
+ SOC_SINGLE_TLV("Mixer Playback Volume",
+ JZ4760_CODEC_REG_MIX2,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, mixer_tlv),
+
SOC_SINGLE("High-Pass Filter Capture Switch",
JZ4760_CODEC_REG_CR4,
REG_CR4_ADC_HPF_OFFSET, 1, 0),
diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c
index a9ef9d5ffcc5ca..a73a7d7a1c0a85 100644
--- a/sound/soc/codecs/lpass-rx-macro.c
+++ b/sound/soc/codecs/lpass-rx-macro.c
@@ -366,7 +366,7 @@
#define CDC_RX_DSD1_CFG2 (0x0F8C)
#define RX_MAX_OFFSET (0x0F8C)
-#define MCLK_FREQ 9600000
+#define MCLK_FREQ 19200000
#define RX_MACRO_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
@@ -2296,10 +2296,8 @@ static int rx_macro_mux_put(struct snd_kcontrol *kcontrol,
aif_rst = rx->rx_port_value[widget->shift];
if (!rx_port_value) {
- if (aif_rst == 0) {
- dev_err(component->dev, "%s:AIF reset already\n", __func__);
+ if (aif_rst == 0)
return 0;
- }
if (aif_rst > RX_MACRO_AIF4_PB) {
dev_err(component->dev, "%s: Invalid AIF reset\n", __func__);
return 0;
@@ -3441,16 +3439,10 @@ static int swclk_gate_enable(struct clk_hw *hw)
}
rx_macro_mclk_enable(rx, true);
- regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
- CDC_RX_SWR_RESET_MASK,
- CDC_RX_SWR_RESET);
regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
CDC_RX_SWR_CLK_EN_MASK, 1);
- regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
- CDC_RX_SWR_RESET_MASK, 0);
-
return 0;
}
@@ -3579,7 +3571,7 @@ static int rx_macro_probe(struct platform_device *pdev)
/* set MCLK and NPL rates */
clk_set_rate(rx->mclk, MCLK_FREQ);
- clk_set_rate(rx->npl, 2 * MCLK_FREQ);
+ clk_set_rate(rx->npl, MCLK_FREQ);
ret = clk_prepare_enable(rx->macro);
if (ret)
@@ -3601,9 +3593,16 @@ static int rx_macro_probe(struct platform_device *pdev)
if (ret)
goto err_fsgen;
- ret = rx_macro_register_mclk_output(rx);
- if (ret)
- goto err_clkout;
+ /* reset swr block */
+ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_RX_SWR_RESET_MASK,
+ CDC_RX_SWR_RESET);
+
+ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_RX_SWR_CLK_EN_MASK, 1);
+
+ regmap_update_bits(rx->regmap, CDC_RX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_RX_SWR_RESET_MASK, 0);
ret = devm_snd_soc_register_component(dev, &rx_macro_component_drv,
rx_macro_dai,
@@ -3618,6 +3617,10 @@ static int rx_macro_probe(struct platform_device *pdev)
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
+ ret = rx_macro_register_mclk_output(rx);
+ if (ret)
+ goto err_clkout;
+
return 0;
err_clkout:
diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c
index 2ef62d6edc3027..bf27bdd5be2067 100644
--- a/sound/soc/codecs/lpass-tx-macro.c
+++ b/sound/soc/codecs/lpass-tx-macro.c
@@ -203,7 +203,7 @@
#define TX_MACRO_AMIC_UNMUTE_DELAY_MS 100
#define TX_MACRO_DMIC_HPF_DELAY_MS 300
#define TX_MACRO_AMIC_HPF_DELAY_MS 300
-#define MCLK_FREQ 9600000
+#define MCLK_FREQ 19200000
enum {
TX_MACRO_AIF_INVALID = 0,
@@ -1861,15 +1861,10 @@ static int swclk_gate_enable(struct clk_hw *hw)
}
tx_macro_mclk_enable(tx, true);
- regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
- CDC_TX_SWR_RESET_MASK, CDC_TX_SWR_RESET_ENABLE);
regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
CDC_TX_SWR_CLK_EN_MASK,
CDC_TX_SWR_CLK_ENABLE);
- regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
- CDC_TX_SWR_RESET_MASK, 0x0);
-
return 0;
}
@@ -2014,7 +2009,7 @@ static int tx_macro_probe(struct platform_device *pdev)
/* set MCLK and NPL rates */
clk_set_rate(tx->mclk, MCLK_FREQ);
- clk_set_rate(tx->npl, 2 * MCLK_FREQ);
+ clk_set_rate(tx->npl, MCLK_FREQ);
ret = clk_prepare_enable(tx->macro);
if (ret)
@@ -2036,9 +2031,15 @@ static int tx_macro_probe(struct platform_device *pdev)
if (ret)
goto err_fsgen;
- ret = tx_macro_register_mclk_output(tx);
- if (ret)
- goto err_clkout;
+ /* reset soundwire block */
+ regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_TX_SWR_RESET_MASK, CDC_TX_SWR_RESET_ENABLE);
+
+ regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_TX_SWR_CLK_EN_MASK,
+ CDC_TX_SWR_CLK_ENABLE);
+ regmap_update_bits(tx->regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_TX_SWR_RESET_MASK, 0x0);
ret = devm_snd_soc_register_component(dev, &tx_macro_component_drv,
tx_macro_dai,
@@ -2052,6 +2053,10 @@ static int tx_macro_probe(struct platform_device *pdev)
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
+ ret = tx_macro_register_mclk_output(tx);
+ if (ret)
+ goto err_clkout;
+
return 0;
err_clkout:
diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c
index b0b6cf29cba30c..fd62817d29a0a5 100644
--- a/sound/soc/codecs/lpass-va-macro.c
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -1333,17 +1333,9 @@ static int fsgen_gate_enable(struct clk_hw *hw)
int ret;
ret = va_macro_mclk_enable(va, true);
- if (!va->has_swr_master)
- return ret;
-
- regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
- CDC_VA_SWR_RESET_MASK, CDC_VA_SWR_RESET_ENABLE);
-
- regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
- CDC_VA_SWR_CLK_EN_MASK,
- CDC_VA_SWR_CLK_ENABLE);
- regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
- CDC_VA_SWR_RESET_MASK, 0x0);
+ if (va->has_swr_master)
+ regmap_update_bits(regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE);
return ret;
}
@@ -1524,16 +1516,6 @@ static int va_macro_probe(struct platform_device *pdev)
if (ret)
goto err_mclk;
- ret = va_macro_register_fsgen_output(va);
- if (ret)
- goto err_clkout;
-
- va->fsgen = clk_hw_get_clk(&va->hw, "fsgen");
- if (IS_ERR(va->fsgen)) {
- ret = PTR_ERR(va->fsgen);
- goto err_clkout;
- }
-
if (va->has_swr_master) {
/* Set default CLK div to 1 */
regmap_update_bits(va->regmap, CDC_VA_TOP_CSR_SWR_MIC_CTL0,
@@ -1548,6 +1530,15 @@ static int va_macro_probe(struct platform_device *pdev)
}
+ if (va->has_swr_master) {
+ regmap_update_bits(va->regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_VA_SWR_RESET_MASK, CDC_VA_SWR_RESET_ENABLE);
+ regmap_update_bits(va->regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_VA_SWR_CLK_EN_MASK, CDC_VA_SWR_CLK_ENABLE);
+ regmap_update_bits(va->regmap, CDC_VA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_VA_SWR_RESET_MASK, 0x0);
+ }
+
ret = devm_snd_soc_register_component(dev, &va_macro_component_drv,
va_macro_dais,
ARRAY_SIZE(va_macro_dais));
@@ -1560,6 +1551,16 @@ static int va_macro_probe(struct platform_device *pdev)
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
+ ret = va_macro_register_fsgen_output(va);
+ if (ret)
+ goto err_clkout;
+
+ va->fsgen = clk_hw_get_clk(&va->hw, "fsgen");
+ if (IS_ERR(va->fsgen)) {
+ ret = PTR_ERR(va->fsgen);
+ goto err_clkout;
+ }
+
return 0;
err_clkout:
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
index 5cfe96f6e430eb..ba7480f3831e2c 100644
--- a/sound/soc/codecs/lpass-wsa-macro.c
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -1856,10 +1856,8 @@ static int wsa_macro_rx_mux_put(struct snd_kcontrol *kcontrol,
aif_rst = wsa->rx_port_value[widget->shift];
if (!rx_port_value) {
- if (aif_rst == 0) {
- dev_err(component->dev, "%s: AIF reset already\n", __func__);
+ if (aif_rst == 0)
return 0;
- }
if (aif_rst >= WSA_MACRO_RX_MAX) {
dev_err(component->dev, "%s: Invalid AIF reset\n", __func__);
return 0;
@@ -2270,17 +2268,10 @@ static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable)
}
wsa_macro_mclk_enable(wsa, true);
- /* reset swr ip */
- regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
- CDC_WSA_SWR_RST_EN_MASK, CDC_WSA_SWR_RST_ENABLE);
-
regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
CDC_WSA_SWR_CLK_EN_MASK,
CDC_WSA_SWR_CLK_ENABLE);
- /* Bring out of reset */
- regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
- CDC_WSA_SWR_RST_EN_MASK, CDC_WSA_SWR_RST_DISABLE);
} else {
regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
CDC_WSA_SWR_CLK_EN_MASK, 0);
@@ -2451,10 +2442,16 @@ static int wsa_macro_probe(struct platform_device *pdev)
if (ret)
goto err_fsgen;
- ret = wsa_macro_register_mclk_output(wsa);
- if (ret)
- goto err_clkout;
+ /* reset swr ip */
+ regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_WSA_SWR_RST_EN_MASK, CDC_WSA_SWR_RST_ENABLE);
+
+ regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_WSA_SWR_CLK_EN_MASK, CDC_WSA_SWR_CLK_ENABLE);
+ /* Bring out of reset */
+ regmap_update_bits(wsa->regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_WSA_SWR_RST_EN_MASK, CDC_WSA_SWR_RST_DISABLE);
ret = devm_snd_soc_register_component(dev, &wsa_macro_component_drv,
wsa_macro_dai,
@@ -2468,6 +2465,10 @@ static int wsa_macro_probe(struct platform_device *pdev)
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
+ ret = wsa_macro_register_mclk_output(wsa);
+ if (ret)
+ goto err_clkout;
+
return 0;
err_clkout:
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index 06ed2a938108e0..b419c49e1e083f 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -2356,8 +2356,7 @@ static const struct snd_soc_dai_ops max98090_dai_ops = {
.no_capture_mute = 1,
};
-static struct snd_soc_dai_driver max98090_dai[] = {
-{
+static struct snd_soc_dai_driver max98090_dai = {
.name = "HiFi",
.playback = {
.stream_name = "HiFi Playback",
@@ -2374,7 +2373,6 @@ static struct snd_soc_dai_driver max98090_dai[] = {
.formats = MAX98090_FORMATS,
},
.ops = &max98090_dai_ops,
-}
};
static int max98090_probe(struct snd_soc_component *component)
@@ -2594,8 +2592,8 @@ static int max98090_i2c_probe(struct i2c_client *i2c)
}
ret = devm_snd_soc_register_component(&i2c->dev,
- &soc_component_dev_max98090, max98090_dai,
- ARRAY_SIZE(max98090_dai));
+ &soc_component_dev_max98090,
+ &max98090_dai, 1);
err_enable:
return ret;
}
diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c
index 3cd1be743d9eeb..c9a2d4dabd3c4e 100644
--- a/sound/soc/codecs/max98373-sdw.c
+++ b/sound/soc/codecs/max98373-sdw.c
@@ -689,10 +689,7 @@ static int max98373_set_sdw_stream(struct snd_soc_dai *dai,
stream->sdw_stream = sdw_stream;
/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- if (direction == SNDRV_PCM_STREAM_PLAYBACK)
- dai->playback_dma_data = stream;
- else
- dai->capture_dma_data = stream;
+ snd_soc_dai_dma_data_set(dai, direction, stream);
return 0;
}
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c
index 71490f11d96ac2..086ac97e83866d 100644
--- a/sound/soc/codecs/mc13783.c
+++ b/sound/soc/codecs/mc13783.c
@@ -776,16 +776,10 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
return ret;
}
-static int mc13783_codec_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static struct platform_driver mc13783_codec_driver = {
.driver = {
.name = "mc13783-codec",
},
- .remove = mc13783_codec_remove,
};
module_platform_driver_probe(mc13783_codec_driver, mc13783_codec_probe);
diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c
index 1aef281a997271..d5006d8de63955 100644
--- a/sound/soc/codecs/nau8822.c
+++ b/sound/soc/codecs/nau8822.c
@@ -1056,6 +1056,7 @@ static const int update_reg[] = {
static int nau8822_probe(struct snd_soc_component *component)
{
int i;
+ struct device_node *of_node = component->dev->of_node;
/*
* Set the update bit in all registers, that have one. This way all
@@ -1066,6 +1067,14 @@ static int nau8822_probe(struct snd_soc_component *component)
snd_soc_component_update_bits(component,
update_reg[i], 0x100, 0x100);
+ /* Check property to configure the two loudspeaker outputs as
+ * a single Bridge Tied Load output
+ */
+ if (of_property_read_bool(of_node, "nuvoton,spk-btl"))
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_RIGHT_SPEAKER_CONTROL,
+ NAU8822_RSUBBYP, NAU8822_RSUBBYP);
+
return 0;
}
diff --git a/sound/soc/codecs/nau8822.h b/sound/soc/codecs/nau8822.h
index 547ec057f853b3..646f6bb64bc5b5 100644
--- a/sound/soc/codecs/nau8822.h
+++ b/sound/soc/codecs/nau8822.h
@@ -187,6 +187,15 @@
/* NAU8822_REG_PLL_K3 (0x27) */
#define NAU8822_PLLK3_MASK 0x1FF
+/* NAU8822_REG_RIGHT_SPEAKER_CONTROL (0x2B) */
+#define NAU8822_RMIXMUT 0x20
+#define NAU8822_RSUBBYP 0x10
+
+#define NAU8822_RAUXRSUBG_SFT 1
+#define NAU8822_RAUXRSUBG_MASK 0x0E
+
+#define NAU8822_RAUXSMUT 0x01
+
/* System Clock Source */
enum {
NAU8822_CLK_MCLK,
diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c
new file mode 100644
index 00000000000000..5dec69be0acb2e
--- /dev/null
+++ b/sound/soc/codecs/peb2466.c
@@ -0,0 +1,2071 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// peb2466.c -- Infineon PEB2466 ALSA SoC driver
+//
+// Copyright 2023 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <asm/unaligned.h>
+#include <linux/clk.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define PEB2466_NB_CHANNEL 4
+
+struct peb2466_lookup {
+ u8 (*table)[4];
+ unsigned int count;
+};
+
+#define PEB2466_TLV_SIZE (sizeof((unsigned int []){TLV_DB_SCALE_ITEM(0, 0, 0)}) / \
+ sizeof(unsigned int))
+
+struct peb2466_lkup_ctrl {
+ int reg;
+ unsigned int index;
+ const struct peb2466_lookup *lookup;
+ unsigned int tlv_array[PEB2466_TLV_SIZE];
+};
+
+struct peb2466 {
+ struct spi_device *spi;
+ struct clk *mclk;
+ struct gpio_desc *reset_gpio;
+ u8 spi_tx_buf[2 + 8]; /* Cannot use stack area for SPI (dma-safe memory) */
+ u8 spi_rx_buf[2 + 8]; /* Cannot use stack area for SPI (dma-safe memory) */
+ struct regmap *regmap;
+ struct {
+ struct peb2466_lookup ax_lookup;
+ struct peb2466_lookup ar_lookup;
+ struct peb2466_lkup_ctrl ax_lkup_ctrl;
+ struct peb2466_lkup_ctrl ar_lkup_ctrl;
+ unsigned int tg1_freq_item;
+ unsigned int tg2_freq_item;
+ } ch[PEB2466_NB_CHANNEL];
+ int max_chan_playback;
+ int max_chan_capture;
+ struct {
+ struct gpio_chip gpio_chip;
+ struct mutex lock;
+ struct {
+ unsigned int xr0;
+ unsigned int xr1;
+ unsigned int xr2;
+ unsigned int xr3;
+ } cache;
+ } gpio;
+};
+
+#define PEB2466_CMD_R (1 << 5)
+#define PEB2466_CMD_W (0 << 5)
+
+#define PEB2466_CMD_MASK 0x18
+#define PEB2466_CMD_XOP 0x18 /* XOP is 0bxxx11xxx */
+#define PEB2466_CMD_SOP 0x10 /* SOP is 0bxxx10xxx */
+#define PEB2466_CMD_COP 0x00 /* COP is 0bxxx0xxxx, handle 0bxxx00xxx */
+#define PEB2466_CMD_COP1 0x08 /* COP is 0bxxx0xxxx, handle 0bxxx01xxx */
+
+#define PEB2466_MAKE_XOP(_lsel) (PEB2466_CMD_XOP | (_lsel))
+#define PEB2466_MAKE_SOP(_ad, _lsel) (PEB2466_CMD_SOP | ((_ad) << 6) | (_lsel))
+#define PEB2466_MAKE_COP(_ad, _code) (PEB2466_CMD_COP | ((_ad) << 6) | (_code))
+
+#define PEB2466_CR0(_ch) PEB2466_MAKE_SOP(_ch, 0x0)
+#define PEB2466_CR0_TH (1 << 7)
+#define PEB2466_CR0_IMR1 (1 << 6)
+#define PEB2466_CR0_FRX (1 << 5)
+#define PEB2466_CR0_FRR (1 << 4)
+#define PEB2466_CR0_AX (1 << 3)
+#define PEB2466_CR0_AR (1 << 2)
+#define PEB2466_CR0_THSEL_MASK (0x3 << 0)
+#define PEB2466_CR0_THSEL(_set) ((_set) << 0)
+
+#define PEB2466_CR1(_ch) PEB2466_MAKE_SOP(_ch, 0x1)
+#define PEB2466_CR1_ETG2 (1 << 7)
+#define PEB2466_CR1_ETG1 (1 << 6)
+#define PEB2466_CR1_PTG2 (1 << 5)
+#define PEB2466_CR1_PTG1 (1 << 4)
+#define PEB2466_CR1_LAW_MASK (1 << 3)
+#define PEB2466_CR1_LAW_ALAW (0 << 3)
+#define PEB2466_CR1_LAW_MULAW (1 << 3)
+#define PEB2466_CR1_PU (1 << 0)
+
+#define PEB2466_CR2(_ch) PEB2466_MAKE_SOP(_ch, 0x2)
+#define PEB2466_CR3(_ch) PEB2466_MAKE_SOP(_ch, 0x3)
+#define PEB2466_CR4(_ch) PEB2466_MAKE_SOP(_ch, 0x4)
+#define PEB2466_CR5(_ch) PEB2466_MAKE_SOP(_ch, 0x5)
+
+#define PEB2466_XR0 PEB2466_MAKE_XOP(0x0)
+#define PEB2466_XR1 PEB2466_MAKE_XOP(0x1)
+#define PEB2466_XR2 PEB2466_MAKE_XOP(0x2)
+#define PEB2466_XR3 PEB2466_MAKE_XOP(0x3)
+#define PEB2466_XR4 PEB2466_MAKE_XOP(0x4)
+#define PEB2466_XR5 PEB2466_MAKE_XOP(0x5)
+#define PEB2466_XR5_MCLK_1536 (0x0 << 6)
+#define PEB2466_XR5_MCLK_2048 (0x1 << 6)
+#define PEB2466_XR5_MCLK_4096 (0x2 << 6)
+#define PEB2466_XR5_MCLK_8192 (0x3 << 6)
+
+#define PEB2466_XR6 PEB2466_MAKE_XOP(0x6)
+#define PEB2466_XR6_PCM_OFFSET(_off) ((_off) << 0)
+
+#define PEB2466_XR7 PEB2466_MAKE_XOP(0x7)
+
+#define PEB2466_TH_FILTER_P1(_ch) PEB2466_MAKE_COP(_ch, 0x0)
+#define PEB2466_TH_FILTER_P2(_ch) PEB2466_MAKE_COP(_ch, 0x1)
+#define PEB2466_TH_FILTER_P3(_ch) PEB2466_MAKE_COP(_ch, 0x2)
+#define PEB2466_IMR1_FILTER_P1(_ch) PEB2466_MAKE_COP(_ch, 0x4)
+#define PEB2466_IMR1_FILTER_P2(_ch) PEB2466_MAKE_COP(_ch, 0x5)
+#define PEB2466_FRX_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x6)
+#define PEB2466_FRR_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x7)
+#define PEB2466_AX_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x8)
+#define PEB2466_AR_FILTER(_ch) PEB2466_MAKE_COP(_ch, 0x9)
+#define PEB2466_TG1(_ch) PEB2466_MAKE_COP(_ch, 0xc)
+#define PEB2466_TG2(_ch) PEB2466_MAKE_COP(_ch, 0xd)
+
+static int peb2466_write_byte(struct peb2466 *peb2466, u8 cmd, u8 val)
+{
+ struct spi_transfer xfer = {
+ .tx_buf = &peb2466->spi_tx_buf,
+ .len = 2,
+ };
+
+ peb2466->spi_tx_buf[0] = cmd | PEB2466_CMD_W;
+ peb2466->spi_tx_buf[1] = val;
+
+ dev_dbg(&peb2466->spi->dev, "write byte (cmd %02x) %02x\n",
+ peb2466->spi_tx_buf[0], peb2466->spi_tx_buf[1]);
+
+ return spi_sync_transfer(peb2466->spi, &xfer, 1);
+}
+
+static int peb2466_read_byte(struct peb2466 *peb2466, u8 cmd, u8 *val)
+{
+ struct spi_transfer xfer = {
+ .tx_buf = &peb2466->spi_tx_buf,
+ .rx_buf = &peb2466->spi_rx_buf,
+ .len = 3,
+ };
+ int ret;
+
+ peb2466->spi_tx_buf[0] = cmd | PEB2466_CMD_R;
+
+ ret = spi_sync_transfer(peb2466->spi, &xfer, 1);
+ if (ret)
+ return ret;
+
+ if (peb2466->spi_rx_buf[1] != 0x81) {
+ dev_err(&peb2466->spi->dev,
+ "spi xfer rd (cmd %02x) invalid ident byte (0x%02x)\n",
+ peb2466->spi_tx_buf[0], peb2466->spi_rx_buf[1]);
+ return -EILSEQ;
+ }
+
+ *val = peb2466->spi_rx_buf[2];
+
+ dev_dbg(&peb2466->spi->dev, "read byte (cmd %02x) %02x\n",
+ peb2466->spi_tx_buf[0], *val);
+
+ return 0;
+}
+
+static int peb2466_write_buf(struct peb2466 *peb2466, u8 cmd, const u8 *buf, unsigned int len)
+{
+ struct spi_transfer xfer = {
+ .tx_buf = &peb2466->spi_tx_buf,
+ .len = len + 1,
+ };
+
+ if (len > 8)
+ return -EINVAL;
+
+ peb2466->spi_tx_buf[0] = cmd | PEB2466_CMD_W;
+ memcpy(&peb2466->spi_tx_buf[1], buf, len);
+
+ dev_dbg(&peb2466->spi->dev, "write buf (cmd %02x, %u) %*ph\n",
+ peb2466->spi_tx_buf[0], len, len, &peb2466->spi_tx_buf[1]);
+
+ return spi_sync_transfer(peb2466->spi, &xfer, 1);
+}
+
+static int peb2466_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct peb2466 *peb2466 = context;
+ int ret;
+
+ /*
+ * Only XOP and SOP commands can be handled as registers.
+ * COP commands are handled using direct peb2466_write_buf() calls.
+ */
+ switch (reg & PEB2466_CMD_MASK) {
+ case PEB2466_CMD_XOP:
+ case PEB2466_CMD_SOP:
+ ret = peb2466_write_byte(peb2466, reg, val);
+ break;
+ default:
+ dev_err(&peb2466->spi->dev, "Not a XOP or SOP command\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int peb2466_reg_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct peb2466 *peb2466 = context;
+ int ret;
+ u8 tmp;
+
+ /* Only XOP and SOP commands can be handled as registers */
+ switch (reg & PEB2466_CMD_MASK) {
+ case PEB2466_CMD_XOP:
+ case PEB2466_CMD_SOP:
+ ret = peb2466_read_byte(peb2466, reg, &tmp);
+ *val = tmp;
+ break;
+ default:
+ dev_err(&peb2466->spi->dev, "Not a XOP or SOP command\n");
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static const struct regmap_config peb2466_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xFF,
+ .reg_write = peb2466_reg_write,
+ .reg_read = peb2466_reg_read,
+ .cache_type = REGCACHE_NONE,
+};
+
+static int peb2466_lkup_ctrl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct peb2466_lkup_ctrl *lkup_ctrl =
+ (struct peb2466_lkup_ctrl *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = lkup_ctrl->lookup->count - 1;
+ return 0;
+}
+
+static int peb2466_lkup_ctrl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct peb2466_lkup_ctrl *lkup_ctrl =
+ (struct peb2466_lkup_ctrl *)kcontrol->private_value;
+
+ ucontrol->value.integer.value[0] = lkup_ctrl->index;
+ return 0;
+}
+
+static int peb2466_lkup_ctrl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct peb2466_lkup_ctrl *lkup_ctrl =
+ (struct peb2466_lkup_ctrl *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ unsigned int index;
+ int ret;
+
+ index = ucontrol->value.integer.value[0];
+ if (index >= lkup_ctrl->lookup->count)
+ return -EINVAL;
+
+ if (index == lkup_ctrl->index)
+ return 0;
+
+ ret = peb2466_write_buf(peb2466, lkup_ctrl->reg,
+ lkup_ctrl->lookup->table[index], 4);
+ if (ret)
+ return ret;
+
+ lkup_ctrl->index = index;
+ return 1; /* The value changed */
+}
+
+static int peb2466_add_lkup_ctrl(struct snd_soc_component *component,
+ struct peb2466_lkup_ctrl *lkup_ctrl,
+ const char *name, int min_val, int step)
+{
+ DECLARE_TLV_DB_SCALE(tlv_array, min_val, step, 0);
+ struct snd_kcontrol_new control = {0};
+
+ BUILD_BUG_ON(sizeof(lkup_ctrl->tlv_array) < sizeof(tlv_array));
+ memcpy(lkup_ctrl->tlv_array, tlv_array, sizeof(tlv_array));
+
+ control.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ control.name = name;
+ control.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ control.tlv.p = lkup_ctrl->tlv_array;
+ control.info = peb2466_lkup_ctrl_info;
+ control.get = peb2466_lkup_ctrl_get;
+ control.put = peb2466_lkup_ctrl_put;
+ control.private_value = (unsigned long)lkup_ctrl;
+
+ return snd_soc_add_component_controls(component, &control, 1);
+}
+
+enum peb2466_tone_freq {
+ PEB2466_TONE_697HZ,
+ PEB2466_TONE_800HZ,
+ PEB2466_TONE_950HZ,
+ PEB2466_TONE_1000HZ,
+ PEB2466_TONE_1008HZ,
+ PEB2466_TONE_2000HZ,
+};
+
+static const u8 peb2466_tone_lookup[][4] = {
+ [PEB2466_TONE_697HZ] = {0x0a, 0x33, 0x5a, 0x2c},
+ [PEB2466_TONE_800HZ] = {0x12, 0xD6, 0x5a, 0xc0},
+ [PEB2466_TONE_950HZ] = {0x1c, 0xf0, 0x5c, 0xc0},
+ [PEB2466_TONE_1000HZ] = {0}, /* lookup value not used for 1000Hz */
+ [PEB2466_TONE_1008HZ] = {0x1a, 0xae, 0x57, 0x70},
+ [PEB2466_TONE_2000HZ] = {0x00, 0x80, 0x50, 0x09},
+};
+
+static const char * const peb2466_tone_freq_txt[] = {
+ [PEB2466_TONE_697HZ] = "697Hz",
+ [PEB2466_TONE_800HZ] = "800Hz",
+ [PEB2466_TONE_950HZ] = "950Hz",
+ [PEB2466_TONE_1000HZ] = "1000Hz",
+ [PEB2466_TONE_1008HZ] = "1008Hz",
+ [PEB2466_TONE_2000HZ] = "2000Hz"
+};
+
+static const struct soc_enum peb2466_tg_freq[][2] = {
+ [0] = {
+ SOC_ENUM_SINGLE(PEB2466_TG1(0), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt),
+ SOC_ENUM_SINGLE(PEB2466_TG2(0), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt)
+ },
+ [1] = {
+ SOC_ENUM_SINGLE(PEB2466_TG1(1), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt),
+ SOC_ENUM_SINGLE(PEB2466_TG2(1), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt)
+ },
+ [2] = {
+ SOC_ENUM_SINGLE(PEB2466_TG1(2), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt),
+ SOC_ENUM_SINGLE(PEB2466_TG2(2), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt)
+ },
+ [3] = {
+ SOC_ENUM_SINGLE(PEB2466_TG1(3), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt),
+ SOC_ENUM_SINGLE(PEB2466_TG2(3), 0, ARRAY_SIZE(peb2466_tone_freq_txt),
+ peb2466_tone_freq_txt)
+ }
+};
+
+static int peb2466_tg_freq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+ switch (e->reg) {
+ case PEB2466_TG1(0):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[0].tg1_freq_item;
+ break;
+ case PEB2466_TG2(0):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[0].tg2_freq_item;
+ break;
+ case PEB2466_TG1(1):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[1].tg1_freq_item;
+ break;
+ case PEB2466_TG2(1):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[1].tg2_freq_item;
+ break;
+ case PEB2466_TG1(2):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[2].tg1_freq_item;
+ break;
+ case PEB2466_TG2(2):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[2].tg2_freq_item;
+ break;
+ case PEB2466_TG1(3):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[3].tg1_freq_item;
+ break;
+ case PEB2466_TG2(3):
+ ucontrol->value.enumerated.item[0] = peb2466->ch[3].tg2_freq_item;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int peb2466_tg_freq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *tg_freq_item;
+ u8 cr1_reg, cr1_mask;
+ unsigned int index;
+ int ret;
+
+ index = ucontrol->value.enumerated.item[0];
+
+ if (index >= ARRAY_SIZE(peb2466_tone_lookup))
+ return -EINVAL;
+
+ switch (e->reg) {
+ case PEB2466_TG1(0):
+ tg_freq_item = &peb2466->ch[0].tg1_freq_item;
+ cr1_reg = PEB2466_CR1(0);
+ cr1_mask = PEB2466_CR1_PTG1;
+ break;
+ case PEB2466_TG2(0):
+ tg_freq_item = &peb2466->ch[0].tg2_freq_item;
+ cr1_reg = PEB2466_CR1(0);
+ cr1_mask = PEB2466_CR1_PTG2;
+ break;
+ case PEB2466_TG1(1):
+ tg_freq_item = &peb2466->ch[1].tg1_freq_item;
+ cr1_reg = PEB2466_CR1(1);
+ cr1_mask = PEB2466_CR1_PTG1;
+ break;
+ case PEB2466_TG2(1):
+ tg_freq_item = &peb2466->ch[1].tg2_freq_item;
+ cr1_reg = PEB2466_CR1(1);
+ cr1_mask = PEB2466_CR1_PTG2;
+ break;
+ case PEB2466_TG1(2):
+ tg_freq_item = &peb2466->ch[2].tg1_freq_item;
+ cr1_reg = PEB2466_CR1(2);
+ cr1_mask = PEB2466_CR1_PTG1;
+ break;
+ case PEB2466_TG2(2):
+ tg_freq_item = &peb2466->ch[2].tg2_freq_item;
+ cr1_reg = PEB2466_CR1(2);
+ cr1_mask = PEB2466_CR1_PTG2;
+ break;
+ case PEB2466_TG1(3):
+ tg_freq_item = &peb2466->ch[3].tg1_freq_item;
+ cr1_reg = PEB2466_CR1(3);
+ cr1_mask = PEB2466_CR1_PTG1;
+ break;
+ case PEB2466_TG2(3):
+ tg_freq_item = &peb2466->ch[3].tg2_freq_item;
+ cr1_reg = PEB2466_CR1(3);
+ cr1_mask = PEB2466_CR1_PTG2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (index == *tg_freq_item)
+ return 0;
+
+ if (index == PEB2466_TONE_1000HZ) {
+ ret = regmap_update_bits(peb2466->regmap, cr1_reg, cr1_mask, 0);
+ if (ret)
+ return ret;
+ } else {
+ ret = peb2466_write_buf(peb2466, e->reg, peb2466_tone_lookup[index], 4);
+ if (ret)
+ return ret;
+ ret = regmap_update_bits(peb2466->regmap, cr1_reg, cr1_mask, cr1_mask);
+ if (ret)
+ return ret;
+ }
+
+ *tg_freq_item = index;
+ return 1; /* The value changed */
+}
+
+static const struct snd_kcontrol_new peb2466_ch0_out_mix_controls[] = {
+ SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(0), 6, 1, 0),
+ SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(0), 7, 1, 0),
+ SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(0), 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new peb2466_ch1_out_mix_controls[] = {
+ SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(1), 6, 1, 0),
+ SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(1), 7, 1, 0),
+ SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(1), 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new peb2466_ch2_out_mix_controls[] = {
+ SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(2), 6, 1, 0),
+ SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(2), 7, 1, 0),
+ SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(2), 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new peb2466_ch3_out_mix_controls[] = {
+ SOC_DAPM_SINGLE("TG1 Switch", PEB2466_CR1(3), 6, 1, 0),
+ SOC_DAPM_SINGLE("TG2 Switch", PEB2466_CR1(3), 7, 1, 0),
+ SOC_DAPM_SINGLE("Voice Switch", PEB2466_CR2(3), 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new peb2466_controls[] = {
+ /* Attenuators */
+ SOC_SINGLE("DAC0 -6dB Playback Switch", PEB2466_CR3(0), 2, 1, 0),
+ SOC_SINGLE("DAC1 -6dB Playback Switch", PEB2466_CR3(1), 2, 1, 0),
+ SOC_SINGLE("DAC2 -6dB Playback Switch", PEB2466_CR3(2), 2, 1, 0),
+ SOC_SINGLE("DAC3 -6dB Playback Switch", PEB2466_CR3(3), 2, 1, 0),
+
+ /* Amplifiers */
+ SOC_SINGLE("ADC0 +6dB Capture Switch", PEB2466_CR3(0), 3, 1, 0),
+ SOC_SINGLE("ADC1 +6dB Capture Switch", PEB2466_CR3(1), 3, 1, 0),
+ SOC_SINGLE("ADC2 +6dB Capture Switch", PEB2466_CR3(2), 3, 1, 0),
+ SOC_SINGLE("ADC3 +6dB Capture Switch", PEB2466_CR3(3), 3, 1, 0),
+
+ /* Tone generators */
+ SOC_ENUM_EXT("DAC0 TG1 Freq", peb2466_tg_freq[0][0],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC1 TG1 Freq", peb2466_tg_freq[1][0],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC2 TG1 Freq", peb2466_tg_freq[2][0],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC3 TG1 Freq", peb2466_tg_freq[3][0],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+
+ SOC_ENUM_EXT("DAC0 TG2 Freq", peb2466_tg_freq[0][1],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC1 TG2 Freq", peb2466_tg_freq[1][1],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC2 TG2 Freq", peb2466_tg_freq[2][1],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+ SOC_ENUM_EXT("DAC3 TG2 Freq", peb2466_tg_freq[3][1],
+ peb2466_tg_freq_get, peb2466_tg_freq_put),
+};
+
+static const struct snd_soc_dapm_widget peb2466_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("CH0 PWR", PEB2466_CR1(0), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CH1 PWR", PEB2466_CR1(1), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CH2 PWR", PEB2466_CR1(2), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CH3 PWR", PEB2466_CR1(3), 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("CH0 DIN", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("CH1 DIN", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("CH2 DIN", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("CH3 DIN", "Playback", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SIGGEN("CH0 TG1"),
+ SND_SOC_DAPM_SIGGEN("CH1 TG1"),
+ SND_SOC_DAPM_SIGGEN("CH2 TG1"),
+ SND_SOC_DAPM_SIGGEN("CH3 TG1"),
+
+ SND_SOC_DAPM_SIGGEN("CH0 TG2"),
+ SND_SOC_DAPM_SIGGEN("CH1 TG2"),
+ SND_SOC_DAPM_SIGGEN("CH2 TG2"),
+ SND_SOC_DAPM_SIGGEN("CH3 TG2"),
+
+ SND_SOC_DAPM_MIXER("DAC0 Mixer", SND_SOC_NOPM, 0, 0,
+ peb2466_ch0_out_mix_controls,
+ ARRAY_SIZE(peb2466_ch0_out_mix_controls)),
+ SND_SOC_DAPM_MIXER("DAC1 Mixer", SND_SOC_NOPM, 0, 0,
+ peb2466_ch1_out_mix_controls,
+ ARRAY_SIZE(peb2466_ch1_out_mix_controls)),
+ SND_SOC_DAPM_MIXER("DAC2 Mixer", SND_SOC_NOPM, 0, 0,
+ peb2466_ch2_out_mix_controls,
+ ARRAY_SIZE(peb2466_ch2_out_mix_controls)),
+ SND_SOC_DAPM_MIXER("DAC3 Mixer", SND_SOC_NOPM, 0, 0,
+ peb2466_ch3_out_mix_controls,
+ ARRAY_SIZE(peb2466_ch3_out_mix_controls)),
+
+ SND_SOC_DAPM_PGA("DAC0 PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC1 PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC2 PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("DAC3 PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("OUT0"),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+
+ SND_SOC_DAPM_INPUT("IN0"),
+ SND_SOC_DAPM_INPUT("IN1"),
+ SND_SOC_DAPM_INPUT("IN2"),
+ SND_SOC_DAPM_INPUT("IN3"),
+
+ SND_SOC_DAPM_DAC("ADC0", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("ADC1", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("ADC2", "Capture", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("ADC3", "Capture", SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route peb2466_dapm_routes[] = {
+ { "CH0 DIN", NULL, "CH0 PWR" },
+ { "CH1 DIN", NULL, "CH1 PWR" },
+ { "CH2 DIN", NULL, "CH2 PWR" },
+ { "CH3 DIN", NULL, "CH3 PWR" },
+
+ { "CH0 TG1", NULL, "CH0 PWR" },
+ { "CH1 TG1", NULL, "CH1 PWR" },
+ { "CH2 TG1", NULL, "CH2 PWR" },
+ { "CH3 TG1", NULL, "CH3 PWR" },
+
+ { "CH0 TG2", NULL, "CH0 PWR" },
+ { "CH1 TG2", NULL, "CH1 PWR" },
+ { "CH2 TG2", NULL, "CH2 PWR" },
+ { "CH3 TG2", NULL, "CH3 PWR" },
+
+ { "DAC0 Mixer", "TG1 Switch", "CH0 TG1" },
+ { "DAC0 Mixer", "TG2 Switch", "CH0 TG2" },
+ { "DAC0 Mixer", "Voice Switch", "CH0 DIN" },
+ { "DAC0 Mixer", NULL, "CH0 DIN" },
+
+ { "DAC1 Mixer", "TG1 Switch", "CH1 TG1" },
+ { "DAC1 Mixer", "TG2 Switch", "CH1 TG2" },
+ { "DAC1 Mixer", "Voice Switch", "CH1 DIN" },
+ { "DAC1 Mixer", NULL, "CH1 DIN" },
+
+ { "DAC2 Mixer", "TG1 Switch", "CH2 TG1" },
+ { "DAC2 Mixer", "TG2 Switch", "CH2 TG2" },
+ { "DAC2 Mixer", "Voice Switch", "CH2 DIN" },
+ { "DAC2 Mixer", NULL, "CH2 DIN" },
+
+ { "DAC3 Mixer", "TG1 Switch", "CH3 TG1" },
+ { "DAC3 Mixer", "TG2 Switch", "CH3 TG2" },
+ { "DAC3 Mixer", "Voice Switch", "CH3 DIN" },
+ { "DAC3 Mixer", NULL, "CH3 DIN" },
+
+ { "DAC0 PGA", NULL, "DAC0 Mixer" },
+ { "DAC1 PGA", NULL, "DAC1 Mixer" },
+ { "DAC2 PGA", NULL, "DAC2 Mixer" },
+ { "DAC3 PGA", NULL, "DAC3 Mixer" },
+
+ { "OUT0", NULL, "DAC0 PGA" },
+ { "OUT1", NULL, "DAC1 PGA" },
+ { "OUT2", NULL, "DAC2 PGA" },
+ { "OUT3", NULL, "DAC3 PGA" },
+
+ { "ADC0", NULL, "IN0" },
+ { "ADC1", NULL, "IN1" },
+ { "ADC2", NULL, "IN2" },
+ { "ADC3", NULL, "IN3" },
+
+ { "ADC0", NULL, "CH0 PWR" },
+ { "ADC1", NULL, "CH1 PWR" },
+ { "ADC2", NULL, "CH2 PWR" },
+ { "ADC3", NULL, "CH3 PWR" },
+};
+
+static int peb2466_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int chan;
+ unsigned int mask;
+ u8 slot;
+ int ret;
+
+ switch (width) {
+ case 0:
+ /* Not set -> default 8 */
+ case 8:
+ break;
+ default:
+ dev_err(dai->dev, "tdm slot width %d not supported\n", width);
+ return -EINVAL;
+ }
+
+ mask = tx_mask;
+ slot = 0;
+ chan = 0;
+ while (mask && chan < PEB2466_NB_CHANNEL) {
+ if (mask & 0x1) {
+ ret = regmap_write(peb2466->regmap, PEB2466_CR5(chan), slot);
+ if (ret) {
+ dev_err(dai->dev, "chan %d set tx tdm slot failed (%d)\n",
+ chan, ret);
+ return ret;
+ }
+ chan++;
+ }
+ mask >>= 1;
+ slot++;
+ }
+ if (mask) {
+ dev_err(dai->dev, "too much tx slots defined (mask = 0x%x) support max %d\n",
+ tx_mask, PEB2466_NB_CHANNEL);
+ return -EINVAL;
+ }
+ peb2466->max_chan_playback = chan;
+
+ mask = rx_mask;
+ slot = 0;
+ chan = 0;
+ while (mask && chan < PEB2466_NB_CHANNEL) {
+ if (mask & 0x1) {
+ ret = regmap_write(peb2466->regmap, PEB2466_CR4(chan), slot);
+ if (ret) {
+ dev_err(dai->dev, "chan %d set rx tdm slot failed (%d)\n",
+ chan, ret);
+ return ret;
+ }
+ chan++;
+ }
+ mask >>= 1;
+ slot++;
+ }
+ if (mask) {
+ dev_err(dai->dev, "too much rx slots defined (mask = 0x%x) support max %d\n",
+ rx_mask, PEB2466_NB_CHANNEL);
+ return -EINVAL;
+ }
+ peb2466->max_chan_capture = chan;
+
+ return 0;
+}
+
+static int peb2466_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component);
+ u8 xr6;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ xr6 = PEB2466_XR6_PCM_OFFSET(1);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ xr6 = PEB2466_XR6_PCM_OFFSET(0);
+ break;
+ default:
+ dev_err(dai->dev, "Unsupported format 0x%x\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+ return regmap_write(peb2466->regmap, PEB2466_XR6, xr6);
+}
+
+static int peb2466_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int ch;
+ int ret;
+ u8 cr1;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ cr1 = PEB2466_CR1_LAW_MULAW;
+ break;
+ case SNDRV_PCM_FORMAT_A_LAW:
+ cr1 = PEB2466_CR1_LAW_ALAW;
+ break;
+ default:
+ dev_err(&peb2466->spi->dev, "Unsupported format 0x%x\n",
+ params_format(params));
+ return -EINVAL;
+ }
+
+ for (ch = 0; ch < PEB2466_NB_CHANNEL; ch++) {
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR1(ch),
+ PEB2466_CR1_LAW_MASK, cr1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const unsigned int peb2466_sample_bits[] = {8};
+
+static struct snd_pcm_hw_constraint_list peb2466_sample_bits_constr = {
+ .list = peb2466_sample_bits,
+ .count = ARRAY_SIZE(peb2466_sample_bits),
+};
+
+static int peb2466_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int max_ch;
+ int ret;
+
+ max_ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ peb2466->max_chan_playback : peb2466->max_chan_capture;
+
+ /*
+ * Disable stream support (min = 0, max = 0) if no timeslots were
+ * configured.
+ */
+ ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ max_ch ? 1 : 0, max_ch);
+ if (ret < 0)
+ return ret;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+ &peb2466_sample_bits_constr);
+}
+
+static u64 peb2466_dai_formats[] = {
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
+static const struct snd_soc_dai_ops peb2466_dai_ops = {
+ .startup = peb2466_dai_startup,
+ .hw_params = peb2466_dai_hw_params,
+ .set_tdm_slot = peb2466_dai_set_tdm_slot,
+ .set_fmt = peb2466_dai_set_fmt,
+ .auto_selectable_formats = peb2466_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(peb2466_dai_formats),
+};
+
+static struct snd_soc_dai_driver peb2466_dai_driver = {
+ .name = "peb2466",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = PEB2466_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = PEB2466_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW,
+ },
+ .ops = &peb2466_dai_ops,
+};
+
+static int peb2466_reset_audio(struct peb2466 *peb2466)
+{
+ static const struct reg_sequence reg_reset[] = {
+ { .reg = PEB2466_XR6, .def = 0x00 },
+
+ { .reg = PEB2466_CR5(0), .def = 0x00 },
+ { .reg = PEB2466_CR4(0), .def = 0x00 },
+ { .reg = PEB2466_CR3(0), .def = 0x00 },
+ { .reg = PEB2466_CR2(0), .def = 0x00 },
+ { .reg = PEB2466_CR1(0), .def = 0x00 },
+ { .reg = PEB2466_CR0(0), .def = PEB2466_CR0_IMR1 },
+
+ { .reg = PEB2466_CR5(1), .def = 0x00 },
+ { .reg = PEB2466_CR4(1), .def = 0x00 },
+ { .reg = PEB2466_CR3(1), .def = 0x00 },
+ { .reg = PEB2466_CR2(1), .def = 0x00 },
+ { .reg = PEB2466_CR1(1), .def = 0x00 },
+ { .reg = PEB2466_CR0(1), .def = PEB2466_CR0_IMR1 },
+
+ { .reg = PEB2466_CR5(2), .def = 0x00 },
+ { .reg = PEB2466_CR4(2), .def = 0x00 },
+ { .reg = PEB2466_CR3(2), .def = 0x00 },
+ { .reg = PEB2466_CR2(2), .def = 0x00 },
+ { .reg = PEB2466_CR1(2), .def = 0x00 },
+ { .reg = PEB2466_CR0(2), .def = PEB2466_CR0_IMR1 },
+
+ { .reg = PEB2466_CR5(3), .def = 0x00 },
+ { .reg = PEB2466_CR4(3), .def = 0x00 },
+ { .reg = PEB2466_CR3(3), .def = 0x00 },
+ { .reg = PEB2466_CR2(3), .def = 0x00 },
+ { .reg = PEB2466_CR1(3), .def = 0x00 },
+ { .reg = PEB2466_CR0(3), .def = PEB2466_CR0_IMR1 },
+ };
+ static const u8 imr1_p1[8] = {0x00, 0x90, 0x09, 0x00, 0x90, 0x09, 0x00, 0x00};
+ static const u8 imr1_p2[8] = {0x7F, 0xFF, 0x00, 0x00, 0x90, 0x14, 0x40, 0x08};
+ static const u8 zero[8] = {0};
+ int ret;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ peb2466->ch[i].tg1_freq_item = PEB2466_TONE_1000HZ;
+ peb2466->ch[i].tg2_freq_item = PEB2466_TONE_1000HZ;
+
+ /*
+ * Even if not used, disabling IM/R1 filter is not recommended.
+ * Instead, we must configure it with default coefficients and
+ * enable it.
+ * The filter will be enabled right after (in the following
+ * regmap_multi_reg_write() call).
+ */
+ ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P1(i), imr1_p1, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P2(i), imr1_p2, 8);
+ if (ret)
+ return ret;
+
+ /* Set all other filters coefficients to zero */
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P1(i), zero, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P2(i), zero, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P3(i), zero, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_FRX_FILTER(i), zero, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_FRR_FILTER(i), zero, 8);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_AX_FILTER(i), zero, 4);
+ if (ret)
+ return ret;
+ ret = peb2466_write_buf(peb2466, PEB2466_AR_FILTER(i), zero, 4);
+ if (ret)
+ return ret;
+ }
+
+ return regmap_multi_reg_write(peb2466->regmap, reg_reset, ARRAY_SIZE(reg_reset));
+}
+
+static int peb2466_fw_parse_thfilter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw TH filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * TH_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 8 bytes: TH-Filter coefficients part1
+ * - @9 8 bytes: TH-Filter coefficients part2
+ * - @17 8 bytes: TH-Filter coefficients part3
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_TH, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P1(i), data + 1, 8);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P2(i), data + 9, 8);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_TH_FILTER_P3(i), data + 17, 8);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_TH | PEB2466_CR0_THSEL_MASK,
+ PEB2466_CR0_TH | PEB2466_CR0_THSEL(i));
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int peb2466_fw_parse_imr1filter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw IM/R1 filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * IMR1_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 8 bytes: IM/R1-Filter coefficients part1
+ * - @9 8 bytes: IM/R1-Filter coefficients part2
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_IMR1, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P1(i), data + 1, 8);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_IMR1_FILTER_P2(i), data + 9, 8);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_IMR1, PEB2466_CR0_IMR1);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int peb2466_fw_parse_frxfilter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw FRX filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * FRX_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 8 bytes: FRX-Filter coefficients
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_FRX, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_FRX_FILTER(i), data + 1, 8);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_FRX, PEB2466_CR0_FRX);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int peb2466_fw_parse_frrfilter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw FRR filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * FRR_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 8 bytes: FRR-Filter coefficients
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_FRR, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_FRR_FILTER(i), data + 1, 8);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_FRR, PEB2466_CR0_FRR);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int peb2466_fw_parse_axfilter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw AX filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * AX_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 4 bytes: AX-Filter coefficients
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AX, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_AX_FILTER(i), data + 1, 4);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AX, PEB2466_CR0_AX);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int peb2466_fw_parse_arfilter(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ u8 mask;
+ int ret;
+ int i;
+
+ dev_info(component->dev, "fw AR filter: mask %x, %*phN\n", *data,
+ lng - 1, data + 1);
+
+ /*
+ * AR_FILTER TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 4 bytes: AR-Filter coefficients
+ */
+ mask = *data;
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AR, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_AR_FILTER(i), data + 1, 4);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AR, PEB2466_CR0_AR);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static const char * const peb2466_ax_ctrl_names[] = {
+ "ADC0 Capture Volume",
+ "ADC1 Capture Volume",
+ "ADC2 Capture Volume",
+ "ADC3 Capture Volume",
+};
+
+static int peb2466_fw_parse_axtable(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ struct peb2466_lkup_ctrl *lkup_ctrl;
+ struct peb2466_lookup *lookup;
+ u8 (*table)[4];
+ u32 table_size;
+ u32 init_index;
+ s32 min_val;
+ s32 step;
+ u8 mask;
+ int ret;
+ int i;
+
+ /*
+ * AX_TABLE TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 32bits signed: Min table value in centi dB (MinVal)
+ * ie -300 means -3.0 dB
+ * - @5 32bits signed: Step from on item to other item in centi dB (Step)
+ * ie 25 means 0.25 dB)
+ * - @9 32bits unsigned: Item index in the table to use for the initial
+ * value
+ * - @13 N*4 bytes: Table composed of 4 bytes items.
+ * Each item correspond to an AX filter value.
+ *
+ * The conversion from raw value item in the table to/from the value in
+ * dB is: Raw value at index i <-> (MinVal + i * Step) in centi dB.
+ */
+
+ /* Check Lng and extract the table size. */
+ if (lng < 13 || ((lng - 13) % 4)) {
+ dev_err(component->dev, "fw AX table lng %u invalid\n", lng);
+ return -EINVAL;
+ }
+ table_size = lng - 13;
+
+ min_val = get_unaligned_be32(data + 1);
+ step = get_unaligned_be32(data + 5);
+ init_index = get_unaligned_be32(data + 9);
+ if (init_index >= (table_size / 4)) {
+ dev_err(component->dev, "fw AX table index %u out of table[%u]\n",
+ init_index, table_size / 4);
+ return -EINVAL;
+ }
+
+ dev_info(component->dev,
+ "fw AX table: mask %x, min %d, step %d, %u items, tbl[%u] %*phN\n",
+ *data, min_val, step, table_size / 4, init_index,
+ 4, data + 13 + (init_index * 4));
+
+ BUILD_BUG_ON(sizeof(*table) != 4);
+ table = devm_kzalloc(&peb2466->spi->dev, table_size, GFP_KERNEL);
+ if (!table)
+ return -ENOMEM;
+ memcpy(table, data + 13, table_size);
+
+ mask = *data;
+ BUILD_BUG_ON(ARRAY_SIZE(peb2466_ax_ctrl_names) != ARRAY_SIZE(peb2466->ch));
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ lookup = &peb2466->ch[i].ax_lookup;
+ lookup->table = table;
+ lookup->count = table_size / 4;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AX, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_AX_FILTER(i),
+ lookup->table[init_index], 4);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AX, PEB2466_CR0_AX);
+ if (ret)
+ return ret;
+
+ lkup_ctrl = &peb2466->ch[i].ax_lkup_ctrl;
+ lkup_ctrl->lookup = lookup;
+ lkup_ctrl->reg = PEB2466_AX_FILTER(i);
+ lkup_ctrl->index = init_index;
+
+ ret = peb2466_add_lkup_ctrl(component, lkup_ctrl,
+ peb2466_ax_ctrl_names[i],
+ min_val, step);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static const char * const peb2466_ar_ctrl_names[] = {
+ "DAC0 Playback Volume",
+ "DAC1 Playback Volume",
+ "DAC2 Playback Volume",
+ "DAC3 Playback Volume",
+};
+
+static int peb2466_fw_parse_artable(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ struct peb2466_lkup_ctrl *lkup_ctrl;
+ struct peb2466_lookup *lookup;
+ u8 (*table)[4];
+ u32 table_size;
+ u32 init_index;
+ s32 min_val;
+ s32 step;
+ u8 mask;
+ int ret;
+ int i;
+
+ /*
+ * AR_TABLE TLV data:
+ * - @0 1 byte: Chan mask (bit set means related channel is concerned)
+ * - @1 32bits signed: Min table value in centi dB (MinVal)
+ * ie -300 means -3.0 dB
+ * - @5 32bits signed: Step from on item to other item in centi dB (Step)
+ * ie 25 means 0.25 dB)
+ * - @9 32bits unsigned: Item index in the table to use for the initial
+ * value
+ * - @13 N*4 bytes: Table composed of 4 bytes items.
+ * Each item correspond to an AR filter value.
+ *
+ * The conversion from raw value item in the table to/from the value in
+ * dB is: Raw value at index i <-> (MinVal + i * Step) in centi dB.
+ */
+
+ /* Check Lng and extract the table size. */
+ if (lng < 13 || ((lng - 13) % 4)) {
+ dev_err(component->dev, "fw AR table lng %u invalid\n", lng);
+ return -EINVAL;
+ }
+ table_size = lng - 13;
+
+ min_val = get_unaligned_be32(data + 1);
+ step = get_unaligned_be32(data + 5);
+ init_index = get_unaligned_be32(data + 9);
+ if (init_index >= (table_size / 4)) {
+ dev_err(component->dev, "fw AR table index %u out of table[%u]\n",
+ init_index, table_size / 4);
+ return -EINVAL;
+ }
+
+ dev_info(component->dev,
+ "fw AR table: mask %x, min %d, step %d, %u items, tbl[%u] %*phN\n",
+ *data, min_val, step, table_size / 4, init_index,
+ 4, data + 13 + (init_index * 4));
+
+ BUILD_BUG_ON(sizeof(*table) != 4);
+ table = devm_kzalloc(&peb2466->spi->dev, table_size, GFP_KERNEL);
+ if (!table)
+ return -ENOMEM;
+ memcpy(table, data + 13, table_size);
+
+ mask = *data;
+ BUILD_BUG_ON(ARRAY_SIZE(peb2466_ar_ctrl_names) != ARRAY_SIZE(peb2466->ch));
+ for (i = 0; i < ARRAY_SIZE(peb2466->ch); i++) {
+ if (!(mask & (1 << i)))
+ continue;
+
+ lookup = &peb2466->ch[i].ar_lookup;
+ lookup->table = table;
+ lookup->count = table_size / 4;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AR, 0);
+ if (ret)
+ return ret;
+
+ ret = peb2466_write_buf(peb2466, PEB2466_AR_FILTER(i),
+ lookup->table[init_index], 4);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(peb2466->regmap, PEB2466_CR0(i),
+ PEB2466_CR0_AR, PEB2466_CR0_AR);
+ if (ret)
+ return ret;
+
+ lkup_ctrl = &peb2466->ch[i].ar_lkup_ctrl;
+ lkup_ctrl->lookup = lookup;
+ lkup_ctrl->reg = PEB2466_AR_FILTER(i);
+ lkup_ctrl->index = init_index;
+
+ ret = peb2466_add_lkup_ctrl(component, lkup_ctrl,
+ peb2466_ar_ctrl_names[i],
+ min_val, step);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+struct peb2466_fw_tag_def {
+ u16 tag;
+ u32 lng_min;
+ u32 lng_max;
+ int (*parse)(struct snd_soc_component *component,
+ u16 tag, u32 lng, const u8 *data);
+};
+
+#define PEB2466_TAG_DEF_LNG_EQ(__tag, __lng, __parse) { \
+ .tag = __tag, \
+ .lng_min = __lng, \
+ .lng_max = __lng, \
+ .parse = __parse, \
+}
+
+#define PEB2466_TAG_DEF_LNG_MIN(__tag, __lng_min, __parse) { \
+ .tag = __tag, \
+ .lng_min = __lng_min, \
+ .lng_max = U32_MAX, \
+ .parse = __parse, \
+}
+
+static const struct peb2466_fw_tag_def peb2466_fw_tag_defs[] = {
+ /* TH FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0001, 1 + 3 * 8, peb2466_fw_parse_thfilter),
+ /* IMR1 FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0002, 1 + 2 * 8, peb2466_fw_parse_imr1filter),
+ /* FRX FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0003, 1 + 8, peb2466_fw_parse_frxfilter),
+ /* FRR FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0004, 1 + 8, peb2466_fw_parse_frrfilter),
+ /* AX FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0005, 1 + 4, peb2466_fw_parse_axfilter),
+ /* AR FILTER */
+ PEB2466_TAG_DEF_LNG_EQ(0x0006, 1 + 4, peb2466_fw_parse_arfilter),
+ /* AX TABLE */
+ PEB2466_TAG_DEF_LNG_MIN(0x0105, 1 + 3 * 4, peb2466_fw_parse_axtable),
+ /* AR TABLE */
+ PEB2466_TAG_DEF_LNG_MIN(0x0106, 1 + 3 * 4, peb2466_fw_parse_artable),
+};
+
+static const struct peb2466_fw_tag_def *peb2466_fw_get_tag_def(u16 tag)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(peb2466_fw_tag_defs); i++) {
+ if (peb2466_fw_tag_defs[i].tag == tag)
+ return &peb2466_fw_tag_defs[i];
+ }
+ return NULL;
+}
+
+static int peb2466_fw_parse(struct snd_soc_component *component,
+ const u8 *data, size_t size)
+{
+ const struct peb2466_fw_tag_def *tag_def;
+ size_t left;
+ const u8 *buf;
+ u16 val16;
+ u16 tag;
+ u32 lng;
+ int ret;
+
+ /*
+ * Coefficients firmware binary structure (16bits and 32bits are
+ * big-endian values).
+ *
+ * @0, 16bits: Magic (0x2466)
+ * @2, 16bits: Version (0x0100 for version 1.0)
+ * @4, 2+4+N bytes: TLV block
+ * @4+(2+4+N) bytes: Next TLV block
+ * ...
+ *
+ * Detail of a TLV block:
+ * @0, 16bits: Tag
+ * @2, 32bits: Lng
+ * @6, lng bytes: Data
+ *
+ * The detail the Data for a given TLV Tag is provided in the related
+ * parser.
+ */
+
+ left = size;
+ buf = data;
+
+ if (left < 4) {
+ dev_err(component->dev, "fw size %zu, exp at least 4\n", left);
+ return -EINVAL;
+ }
+
+ /* Check magic */
+ val16 = get_unaligned_be16(buf);
+ if (val16 != 0x2466) {
+ dev_err(component->dev, "fw magic 0x%04x exp 0x2466\n", val16);
+ return -EINVAL;
+ }
+ buf += 2;
+ left -= 2;
+
+ /* Check version */
+ val16 = get_unaligned_be16(buf);
+ if (val16 != 0x0100) {
+ dev_err(component->dev, "fw magic 0x%04x exp 0x0100\n", val16);
+ return -EINVAL;
+ }
+ buf += 2;
+ left -= 2;
+
+ while (left) {
+ if (left < 6) {
+ dev_err(component->dev, "fw %td/%zu left %zu, exp at least 6\n",
+ buf - data, size, left);
+ return -EINVAL;
+ }
+ /* Check tag and lng */
+ tag = get_unaligned_be16(buf);
+ lng = get_unaligned_be32(buf + 2);
+ tag_def = peb2466_fw_get_tag_def(tag);
+ if (!tag_def) {
+ dev_err(component->dev, "fw %td/%zu tag 0x%04x unknown\n",
+ buf - data, size, tag);
+ return -EINVAL;
+ }
+ if (lng < tag_def->lng_min || lng > tag_def->lng_max) {
+ dev_err(component->dev, "fw %td/%zu tag 0x%04x lng %u, exp [%u;%u]\n",
+ buf - data, size, tag, lng, tag_def->lng_min, tag_def->lng_max);
+ return -EINVAL;
+ }
+ buf += 6;
+ left -= 6;
+ if (left < lng) {
+ dev_err(component->dev, "fw %td/%zu tag 0x%04x lng %u, left %zu\n",
+ buf - data, size, tag, lng, left);
+ return -EINVAL;
+ }
+
+ /* TLV block is valid -> parse the data part */
+ ret = tag_def->parse(component, tag, lng, buf);
+ if (ret) {
+ dev_err(component->dev, "fw %td/%zu tag 0x%04x lng %u parse failed\n",
+ buf - data, size, tag, lng);
+ return ret;
+ }
+
+ buf += lng;
+ left -= lng;
+ }
+ return 0;
+}
+
+static int peb2466_load_coeffs(struct snd_soc_component *component, const char *fw_name)
+{
+ const struct firmware *fw;
+ int ret;
+
+ ret = request_firmware(&fw, fw_name, component->dev);
+ if (ret)
+ return ret;
+
+ ret = peb2466_fw_parse(component, fw->data, fw->size);
+ release_firmware(fw);
+
+ return ret;
+}
+
+static int peb2466_component_probe(struct snd_soc_component *component)
+{
+ struct peb2466 *peb2466 = snd_soc_component_get_drvdata(component);
+ const char *firmware_name;
+ int ret;
+
+ /* reset peb2466 audio part */
+ ret = peb2466_reset_audio(peb2466);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_string(peb2466->spi->dev.of_node,
+ "firmware-name", &firmware_name);
+ if (ret)
+ return (ret == -EINVAL) ? 0 : ret;
+
+ return peb2466_load_coeffs(component, firmware_name);
+}
+
+static const struct snd_soc_component_driver peb2466_component_driver = {
+ .probe = peb2466_component_probe,
+ .controls = peb2466_controls,
+ .num_controls = ARRAY_SIZE(peb2466_controls),
+ .dapm_widgets = peb2466_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(peb2466_dapm_widgets),
+ .dapm_routes = peb2466_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(peb2466_dapm_routes),
+ .endianness = 1,
+};
+
+/*
+ * The mapping used for the relationship between the gpio offset and the
+ * physical pin is the following:
+ *
+ * offset pin
+ * 0 SI1_0
+ * 1 SI1_1
+ * 2 SI2_0
+ * 3 SI2_1
+ * 4 SI3_0
+ * 5 SI3_1
+ * 6 SI4_0
+ * 7 SI4_1
+ * 8 SO1_0
+ * 9 SO1_1
+ * 10 SO2_0
+ * 11 SO2_1
+ * 12 SO3_0
+ * 13 SO3_1
+ * 14 SO4_0
+ * 15 SO4_1
+ * 16 SB1_0
+ * 17 SB1_1
+ * 18 SB2_0
+ * 19 SB2_1
+ * 20 SB3_0
+ * 21 SB3_1
+ * 22 SB4_0
+ * 23 SB4_1
+ * 24 SB1_2
+ * 25 SB2_2
+ * 26 SB3_2
+ * 27 SB4_2
+ */
+
+static int peb2466_chip_gpio_offset_to_data_regmask(unsigned int offset,
+ unsigned int *xr_reg,
+ unsigned int *mask)
+{
+ if (offset < 16) {
+ /*
+ * SIx_{0,1} and SOx_{0,1}
+ * Read accesses read SIx_{0,1} values
+ * Write accesses write SOx_{0,1} values
+ */
+ *xr_reg = PEB2466_XR0;
+ *mask = (1 << (offset % 8));
+ return 0;
+ }
+ if (offset < 24) {
+ /* SBx_{0,1} */
+ *xr_reg = PEB2466_XR1;
+ *mask = (1 << (offset - 16));
+ return 0;
+ }
+ if (offset < 28) {
+ /* SBx_2 */
+ *xr_reg = PEB2466_XR3;
+ *mask = (1 << (offset - 24 + 4));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int peb2466_chip_gpio_offset_to_dir_regmask(unsigned int offset,
+ unsigned int *xr_reg,
+ unsigned int *mask)
+{
+ if (offset < 16) {
+ /* Direction cannot be changed for these GPIOs */
+ return -EINVAL;
+ }
+ if (offset < 24) {
+ *xr_reg = PEB2466_XR2;
+ *mask = (1 << (offset - 16));
+ return 0;
+ }
+ if (offset < 28) {
+ *xr_reg = PEB2466_XR3;
+ *mask = (1 << (offset - 24));
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static unsigned int *peb2466_chip_gpio_get_cache(struct peb2466 *peb2466,
+ unsigned int xr_reg)
+{
+ unsigned int *cache;
+
+ switch (xr_reg) {
+ case PEB2466_XR0:
+ cache = &peb2466->gpio.cache.xr0;
+ break;
+ case PEB2466_XR1:
+ cache = &peb2466->gpio.cache.xr1;
+ break;
+ case PEB2466_XR2:
+ cache = &peb2466->gpio.cache.xr2;
+ break;
+ case PEB2466_XR3:
+ cache = &peb2466->gpio.cache.xr3;
+ break;
+ default:
+ cache = NULL;
+ break;
+ }
+ return cache;
+}
+
+static int peb2466_chip_gpio_update_bits(struct peb2466 *peb2466, unsigned int xr_reg,
+ unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ unsigned int *cache;
+ int ret;
+
+ /*
+ * Read and write accesses use different peb2466 internal signals (input
+ * signals on reads and output signals on writes). regmap_update_bits
+ * cannot be used to read/modify/write the value.
+ * So, a specific cache value is used.
+ */
+
+ mutex_lock(&peb2466->gpio.lock);
+
+ cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg);
+ if (!cache) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ tmp = *cache;
+ tmp &= ~mask;
+ tmp |= val;
+
+ ret = regmap_write(peb2466->regmap, xr_reg, tmp);
+ if (ret)
+ goto end;
+
+ *cache = tmp;
+ ret = 0;
+
+end:
+ mutex_unlock(&peb2466->gpio.lock);
+ return ret;
+}
+
+static void peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val)
+{
+ struct peb2466 *peb2466 = gpiochip_get_data(c);
+ unsigned int xr_reg;
+ unsigned int mask;
+ int ret;
+
+ if (offset < 8) {
+ /*
+ * SIx_{0,1} signals cannot be set and writing the related
+ * register will change the SOx_{0,1} signals
+ */
+ dev_warn(&peb2466->spi->dev, "cannot set gpio %d (read-only)\n",
+ offset);
+ return;
+ }
+
+ ret = peb2466_chip_gpio_offset_to_data_regmask(offset, &xr_reg, &mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "cannot set gpio %d (%d)\n",
+ offset, ret);
+ return;
+ }
+
+ ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, val ? mask : 0);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "set gpio %d (0x%x, 0x%x) failed (%d)\n",
+ offset, xr_reg, mask, ret);
+ }
+}
+
+static int peb2466_chip_gpio_get(struct gpio_chip *c, unsigned int offset)
+{
+ struct peb2466 *peb2466 = gpiochip_get_data(c);
+ bool use_cache = false;
+ unsigned int *cache;
+ unsigned int xr_reg;
+ unsigned int mask;
+ unsigned int val;
+ int ret;
+
+ if (offset >= 8 && offset < 16) {
+ /*
+ * SOx_{0,1} signals cannot be read. Reading the related
+ * register will read the SIx_{0,1} signals.
+ * Use the cache to get value;
+ */
+ use_cache = true;
+ }
+
+ ret = peb2466_chip_gpio_offset_to_data_regmask(offset, &xr_reg, &mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "cannot get gpio %d (%d)\n",
+ offset, ret);
+ return -EINVAL;
+ }
+
+ if (use_cache) {
+ cache = peb2466_chip_gpio_get_cache(peb2466, xr_reg);
+ if (!cache)
+ return -EINVAL;
+ val = *cache;
+ } else {
+ ret = regmap_read(peb2466->regmap, xr_reg, &val);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "get gpio %d (0x%x, 0x%x) failed (%d)\n",
+ offset, xr_reg, mask, ret);
+ return ret;
+ }
+ }
+
+ return !!(val & mask);
+}
+
+static int peb2466_chip_get_direction(struct gpio_chip *c, unsigned int offset)
+{
+ struct peb2466 *peb2466 = gpiochip_get_data(c);
+ unsigned int xr_reg;
+ unsigned int mask;
+ unsigned int val;
+ int ret;
+
+ if (offset < 8) {
+ /* SIx_{0,1} */
+ return GPIO_LINE_DIRECTION_IN;
+ }
+ if (offset < 16) {
+ /* SOx_{0,1} */
+ return GPIO_LINE_DIRECTION_OUT;
+ }
+
+ ret = peb2466_chip_gpio_offset_to_dir_regmask(offset, &xr_reg, &mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "cannot get gpio %d direction (%d)\n",
+ offset, ret);
+ return ret;
+ }
+
+ ret = regmap_read(peb2466->regmap, xr_reg, &val);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "get dir gpio %d (0x%x, 0x%x) failed (%d)\n",
+ offset, xr_reg, mask, ret);
+ return ret;
+ }
+
+ return val & mask ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;
+}
+
+static int peb2466_chip_direction_input(struct gpio_chip *c, unsigned int offset)
+{
+ struct peb2466 *peb2466 = gpiochip_get_data(c);
+ unsigned int xr_reg;
+ unsigned int mask;
+ int ret;
+
+ if (offset < 8) {
+ /* SIx_{0,1} */
+ return 0;
+ }
+ if (offset < 16) {
+ /* SOx_{0,1} */
+ return -EINVAL;
+ }
+
+ ret = peb2466_chip_gpio_offset_to_dir_regmask(offset, &xr_reg, &mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "cannot set gpio %d direction (%d)\n",
+ offset, ret);
+ return ret;
+ }
+
+ ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, 0);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "Set dir in gpio %d (0x%x, 0x%x) failed (%d)\n",
+ offset, xr_reg, mask, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int peb2466_chip_direction_output(struct gpio_chip *c, unsigned int offset, int val)
+{
+ struct peb2466 *peb2466 = gpiochip_get_data(c);
+ unsigned int xr_reg;
+ unsigned int mask;
+ int ret;
+
+ if (offset < 8) {
+ /* SIx_{0,1} */
+ return -EINVAL;
+ }
+
+ peb2466_chip_gpio_set(c, offset, val);
+
+ if (offset < 16) {
+ /* SOx_{0,1} */
+ return 0;
+ }
+
+ ret = peb2466_chip_gpio_offset_to_dir_regmask(offset, &xr_reg, &mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "cannot set gpio %d direction (%d)\n",
+ offset, ret);
+ return ret;
+ }
+
+ ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, mask);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "Set dir in gpio %d (0x%x, 0x%x) failed (%d)\n",
+ offset, xr_reg, mask, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int peb2466_reset_gpio(struct peb2466 *peb2466)
+{
+ static const struct reg_sequence reg_reset[] = {
+ /* Output pins at 0, input/output pins as input */
+ { .reg = PEB2466_XR0, .def = 0 },
+ { .reg = PEB2466_XR1, .def = 0 },
+ { .reg = PEB2466_XR2, .def = 0 },
+ { .reg = PEB2466_XR3, .def = 0 },
+ };
+
+ peb2466->gpio.cache.xr0 = 0;
+ peb2466->gpio.cache.xr1 = 0;
+ peb2466->gpio.cache.xr2 = 0;
+ peb2466->gpio.cache.xr3 = 0;
+
+ return regmap_multi_reg_write(peb2466->regmap, reg_reset, ARRAY_SIZE(reg_reset));
+}
+
+static int peb2466_gpio_init(struct peb2466 *peb2466)
+{
+ int ret;
+
+ mutex_init(&peb2466->gpio.lock);
+
+ ret = peb2466_reset_gpio(peb2466);
+ if (ret)
+ return ret;
+
+ peb2466->gpio.gpio_chip.owner = THIS_MODULE;
+ peb2466->gpio.gpio_chip.label = dev_name(&peb2466->spi->dev);
+ peb2466->gpio.gpio_chip.parent = &peb2466->spi->dev;
+ peb2466->gpio.gpio_chip.base = -1;
+ peb2466->gpio.gpio_chip.ngpio = 28;
+ peb2466->gpio.gpio_chip.get_direction = peb2466_chip_get_direction;
+ peb2466->gpio.gpio_chip.direction_input = peb2466_chip_direction_input;
+ peb2466->gpio.gpio_chip.direction_output = peb2466_chip_direction_output;
+ peb2466->gpio.gpio_chip.get = peb2466_chip_gpio_get;
+ peb2466->gpio.gpio_chip.set = peb2466_chip_gpio_set;
+ peb2466->gpio.gpio_chip.can_sleep = true;
+
+ return devm_gpiochip_add_data(&peb2466->spi->dev, &peb2466->gpio.gpio_chip,
+ peb2466);
+}
+
+static int peb2466_spi_probe(struct spi_device *spi)
+{
+ struct peb2466 *peb2466;
+ unsigned long mclk_rate;
+ int ret;
+ u8 xr5;
+
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ peb2466 = devm_kzalloc(&spi->dev, sizeof(*peb2466), GFP_KERNEL);
+ if (!peb2466)
+ return -ENOMEM;
+
+ peb2466->spi = spi;
+
+ peb2466->regmap = devm_regmap_init(&peb2466->spi->dev, NULL, peb2466,
+ &peb2466_regmap_config);
+ if (IS_ERR(peb2466->regmap))
+ return PTR_ERR(peb2466->regmap);
+
+ peb2466->reset_gpio = devm_gpiod_get_optional(&peb2466->spi->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(peb2466->reset_gpio))
+ return PTR_ERR(peb2466->reset_gpio);
+
+ peb2466->mclk = devm_clk_get(&peb2466->spi->dev, "mclk");
+ if (IS_ERR(peb2466->mclk))
+ return PTR_ERR(peb2466->mclk);
+ ret = clk_prepare_enable(peb2466->mclk);
+ if (ret)
+ return ret;
+
+ if (peb2466->reset_gpio) {
+ gpiod_set_value_cansleep(peb2466->reset_gpio, 1);
+ udelay(4);
+ gpiod_set_value_cansleep(peb2466->reset_gpio, 0);
+ udelay(4);
+ }
+
+ spi_set_drvdata(spi, peb2466);
+
+ mclk_rate = clk_get_rate(peb2466->mclk);
+ switch (mclk_rate) {
+ case 1536000:
+ xr5 = PEB2466_XR5_MCLK_1536;
+ break;
+ case 2048000:
+ xr5 = PEB2466_XR5_MCLK_2048;
+ break;
+ case 4096000:
+ xr5 = PEB2466_XR5_MCLK_4096;
+ break;
+ case 8192000:
+ xr5 = PEB2466_XR5_MCLK_8192;
+ break;
+ default:
+ dev_err(&peb2466->spi->dev, "Unsupported clock rate %lu\n",
+ mclk_rate);
+ ret = -EINVAL;
+ goto failed;
+ }
+ ret = regmap_write(peb2466->regmap, PEB2466_XR5, xr5);
+ if (ret) {
+ dev_err(&peb2466->spi->dev, "Setting MCLK failed (%d)\n", ret);
+ goto failed;
+ }
+
+ ret = devm_snd_soc_register_component(&spi->dev, &peb2466_component_driver,
+ &peb2466_dai_driver, 1);
+ if (ret)
+ goto failed;
+
+ if (IS_ENABLED(CONFIG_GPIOLIB)) {
+ ret = peb2466_gpio_init(peb2466);
+ if (ret)
+ goto failed;
+ }
+
+ return 0;
+
+failed:
+ clk_disable_unprepare(peb2466->mclk);
+ return ret;
+}
+
+static void peb2466_spi_remove(struct spi_device *spi)
+{
+ struct peb2466 *peb2466 = spi_get_drvdata(spi);
+
+ clk_disable_unprepare(peb2466->mclk);
+}
+
+static const struct of_device_id peb2466_of_match[] = {
+ { .compatible = "infineon,peb2466", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, peb2466_of_match);
+
+static const struct spi_device_id peb2466_id_table[] = {
+ { "peb2466", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, peb2466_id_table);
+
+static struct spi_driver peb2466_spi_driver = {
+ .driver = {
+ .name = "peb2466",
+ .of_match_table = peb2466_of_match,
+ },
+ .id_table = peb2466_id_table,
+ .probe = peb2466_spi_probe,
+ .remove = peb2466_spi_remove,
+};
+
+module_spi_driver(peb2466_spi_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("PEB2466 ALSA SoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c
index ca2790d63b719c..45544b530d3dcd 100644
--- a/sound/soc/codecs/rt1308-sdw.c
+++ b/sound/soc/codecs/rt1308-sdw.c
@@ -508,10 +508,7 @@ static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
stream->sdw_stream = sdw_stream;
/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- if (direction == SNDRV_PCM_STREAM_PLAYBACK)
- dai->playback_dma_data = stream;
- else
- dai->capture_dma_data = stream;
+ snd_soc_dai_dma_data_set(dai, direction, stream);
return 0;
}
diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c
index e6294cc7a99547..91edfd5eee085e 100644
--- a/sound/soc/codecs/rt1316-sdw.c
+++ b/sound/soc/codecs/rt1316-sdw.c
@@ -507,10 +507,7 @@ static int rt1316_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
stream->sdw_stream = sdw_stream;
/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- if (direction == SNDRV_PCM_STREAM_PLAYBACK)
- dai->playback_dma_data = stream;
- else
- dai->capture_dma_data = stream;
+ snd_soc_dai_dma_data_set(dai, direction, stream);
return 0;
}
@@ -584,7 +581,7 @@ static int rt1316_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
* slave_ops: callbacks for get_clock_stop_mode, clock_stop and
* port_prep are not defined for now
*/
-static struct sdw_slave_ops rt1316_slave_ops = {
+static const struct sdw_slave_ops rt1316_slave_ops = {
.read_prop = rt1316_read_prop,
.update_status = rt1316_update_status,
};
diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c
index f85f5ab2c6d048..c34975f958c4c2 100644
--- a/sound/soc/codecs/rt1318-sdw.c
+++ b/sound/soc/codecs/rt1318-sdw.c
@@ -575,10 +575,7 @@ static int rt1318_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
stream->sdw_stream = sdw_stream;
/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- if (direction == SNDRV_PCM_STREAM_PLAYBACK)
- dai->playback_dma_data = stream;
- else
- dai->capture_dma_data = stream;
+ snd_soc_dai_dma_data_set(dai, direction, stream);
return 0;
}
@@ -697,7 +694,7 @@ static int rt1318_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
* slave_ops: callbacks for get_clock_stop_mode, clock_stop and
* port_prep are not defined for now
*/
-static struct sdw_slave_ops rt1318_slave_ops = {
+static const struct sdw_slave_ops rt1318_slave_ops = {
.read_prop = rt1318_read_prop,
.update_status = rt1318_update_status,
};
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 708e55b7431a75..13925705550700 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -1838,9 +1838,14 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
unsigned int pll_bit = 0;
+ int ret;
switch (clk_id) {
case RT5640_SCLK_S_MCLK:
+ ret = clk_set_rate(rt5640->mclk, freq);
+ if (ret)
+ return ret;
+
reg_val |= RT5640_SCLK_SRC_MCLK;
break;
case RT5640_SCLK_S_PLL1:
@@ -2717,6 +2722,10 @@ static int rt5640_probe(struct snd_soc_component *component)
snd_soc_component_update_bits(component, RT5640_IN1_IN2,
RT5640_IN_DF2, RT5640_IN_DF2);
+ if (device_property_read_bool(component->dev, "realtek,lout-differential"))
+ snd_soc_component_update_bits(component, RT5640_DUMMY1,
+ RT5640_EN_LOUT_DF, RT5640_EN_LOUT_DF);
+
if (device_property_read_u32(component->dev, "realtek,dmic1-data-pin",
&val) == 0 && val) {
dmic1_data_pin = val - 1;
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
index f58b88e3325b5c..9847a1ae01f445 100644
--- a/sound/soc/codecs/rt5640.h
+++ b/sound/soc/codecs/rt5640.h
@@ -1978,6 +1978,8 @@
#define RT5640_ZCD_HP_EN (0x1 << 15)
/* General Control 1 (0xfa) */
+#define RT5640_EN_LOUT_DF (0x1 << 14)
+#define RT5640_EN_LOUT_DF_SFT 14
#define RT5640_M_MONO_ADC_L (0x1 << 13)
#define RT5640_M_MONO_ADC_L_SFT 13
#define RT5640_M_MONO_ADC_R (0x1 << 12)
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 620ecbfa4a7a83..7c7cbb6362ea12 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -3157,7 +3157,7 @@ static int rt5645_jack_detect(struct snd_soc_component *component, int jack_inse
snd_soc_dapm_force_enable_pin(dapm, "LDO2");
snd_soc_dapm_force_enable_pin(dapm, "Mic Det Power");
snd_soc_dapm_sync(dapm);
- if (!dapm->card->instantiated) {
+ if (!snd_soc_card_is_instantiated(dapm->card)) {
/* Power up necessary bits for JD if dapm is
not ready yet */
regmap_update_bits(rt5645->regmap, RT5645_PWR_ANLG1,
diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c
index 6e66cc218fa8dc..17afaef85c77ac 100644
--- a/sound/soc/codecs/rt5665.c
+++ b/sound/soc/codecs/rt5665.c
@@ -1298,7 +1298,7 @@ static void rt5665_jack_detect_handler(struct work_struct *work)
usleep_range(10000, 15000);
}
- while (!rt5665->component->card->instantiated) {
+ while (!snd_soc_card_is_instantiated(rt5665->component->card)) {
pr_debug("%s\n", __func__);
usleep_range(10000, 15000);
}
@@ -4748,7 +4748,7 @@ static void rt5665_calibrate_handler(struct work_struct *work)
struct rt5665_priv *rt5665 = container_of(work, struct rt5665_priv,
calibrate_work.work);
- while (!rt5665->component->card->instantiated) {
+ while (!snd_soc_card_is_instantiated(rt5665->component->card)) {
pr_debug("%s\n", __func__);
usleep_range(10000, 15000);
}
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
index beb0951ff680bd..ecf3b0527dbe69 100644
--- a/sound/soc/codecs/rt5668.c
+++ b/sound/soc/codecs/rt5668.c
@@ -1022,8 +1022,8 @@ static void rt5668_jack_detect_handler(struct work_struct *work)
container_of(work, struct rt5668_priv, jack_detect_work.work);
int val, btn_type;
- if (!rt5668->component || !rt5668->component->card ||
- !rt5668->component->card->instantiated) {
+ if (!rt5668->component ||
+ !snd_soc_card_is_instantiated(rt5668->component->card)) {
/* card not yet ready, try later */
mod_delayed_work(system_power_efficient_wq,
&rt5668->jack_detect_work, msecs_to_jiffies(15));
diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c
index d8a573dcb771b8..5f80a5d59b65a2 100644
--- a/sound/soc/codecs/rt5682-sdw.c
+++ b/sound/soc/codecs/rt5682-sdw.c
@@ -107,10 +107,7 @@ static int rt5682_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
stream->sdw_stream = sdw_stream;
/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- if (direction == SNDRV_PCM_STREAM_PLAYBACK)
- dai->playback_dma_data = stream;
- else
- dai->capture_dma_data = stream;
+ snd_soc_dai_dma_data_set(dai, direction, stream);
return 0;
}
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
index 7e3eb65afe16d7..f6c798b65c08a6 100644
--- a/sound/soc/codecs/rt5682.c
+++ b/sound/soc/codecs/rt5682.c
@@ -1094,8 +1094,8 @@ void rt5682_jack_detect_handler(struct work_struct *work)
struct snd_soc_dapm_context *dapm;
int val, btn_type;
- if (!rt5682->component || !rt5682->component->card ||
- !rt5682->component->card->instantiated) {
+ if (!rt5682->component ||
+ !snd_soc_card_is_instantiated(rt5682->component->card)) {
/* card not yet ready, try later */
mod_delayed_work(system_power_efficient_wq,
&rt5682->jack_detect_work, msecs_to_jiffies(15));
diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c
index f5e5dbc3b0f044..9c34dca58f54da 100644
--- a/sound/soc/codecs/rt5682s.c
+++ b/sound/soc/codecs/rt5682s.c
@@ -834,8 +834,8 @@ static void rt5682s_jack_detect_handler(struct work_struct *work)
struct snd_soc_dapm_context *dapm;
int val, btn_type;
- if (!rt5682s->component || !rt5682s->component->card ||
- !rt5682s->component->card->instantiated) {
+ if (!rt5682s->component ||
+ !snd_soc_card_is_instantiated(rt5682s->component->card)) {
/* card not yet ready, try later */
mod_delayed_work(system_power_efficient_wq,
&rt5682s->jack_detect_work, msecs_to_jiffies(15));
@@ -2895,6 +2895,9 @@ static int rt5682s_suspend(struct snd_soc_component *component)
{
struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
+ if (rt5682s->irq)
+ disable_irq(rt5682s->irq);
+
cancel_delayed_work_sync(&rt5682s->jack_detect_work);
cancel_delayed_work_sync(&rt5682s->jd_check_work);
@@ -2919,6 +2922,9 @@ static int rt5682s_resume(struct snd_soc_component *component)
&rt5682s->jack_detect_work, msecs_to_jiffies(0));
}
+ if (rt5682s->irq)
+ enable_irq(rt5682s->irq);
+
return 0;
}
#else
@@ -3259,7 +3265,9 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c)
ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, rt5682s_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"rt5682s", rt5682s);
- if (ret)
+ if (!ret)
+ rt5682s->irq = i2c->irq;
+ else
dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
}
diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h
index 67f86a38a1cce1..caa7733b430f26 100644
--- a/sound/soc/codecs/rt5682s.h
+++ b/sound/soc/codecs/rt5682s.h
@@ -1472,6 +1472,7 @@ struct rt5682s_priv {
int pll_comb;
int jack_type;
+ unsigned int irq;
int irq_work_delay_time;
int wclk_enabled;
};
diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c
index 6534c9b514428b..659ce26e9f3ba5 100644
--- a/sound/soc/codecs/rt700.c
+++ b/sound/soc/codecs/rt700.c
@@ -163,7 +163,7 @@ static void rt700_jack_detect_handler(struct work_struct *work)
if (!rt700->hs_jack)
return;
- if (!rt700->component->card || !rt700->component->card->instantiated)
+ if (!snd_soc_card_is_instantiated(rt700->component->card))
return;
reg = RT700_VERB_GET_PIN_SENSE | RT700_HP_OUT;
@@ -887,10 +887,7 @@ static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
stream->sdw_stream = sdw_stream;
/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- if (direction == SNDRV_PCM_STREAM_PLAYBACK)
- dai->playback_dma_data = stream;
- else
- dai->capture_dma_data = stream;
+ snd_soc_dai_dma_data_set(dai, direction, stream);
return 0;
}
diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c
index 88a8392a58edb5..e23cec4c457dee 100644
--- a/sound/soc/codecs/rt711-sdca-sdw.c
+++ b/sound/soc/codecs/rt711-sdca-sdw.c
@@ -338,7 +338,7 @@ io_error:
return ret;
}
-static struct sdw_slave_ops rt711_sdca_slave_ops = {
+static const struct sdw_slave_ops rt711_sdca_slave_ops = {
.read_prop = rt711_sdca_read_prop,
.interrupt_callback = rt711_sdca_interrupt_callback,
.update_status = rt711_sdca_update_status,
diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c
index b78dd5994edbfd..c65abe812a4c7b 100644
--- a/sound/soc/codecs/rt711-sdca.c
+++ b/sound/soc/codecs/rt711-sdca.c
@@ -295,7 +295,7 @@ static void rt711_sdca_jack_detect_handler(struct work_struct *work)
if (!rt711->hs_jack)
return;
- if (!rt711->component->card || !rt711->component->card->instantiated)
+ if (!snd_soc_card_is_instantiated(rt711->component->card))
return;
/* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */
@@ -463,6 +463,21 @@ static void rt711_sdca_jack_init(struct rt711_sdca_priv *rt711)
RT711_HP_JD_FINAL_RESULT_CTL_JD12,
RT711_HP_JD_FINAL_RESULT_CTL_JD12);
break;
+ case RT711_JD2_100K:
+ rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+ RT711_COMBO_JACK_AUTO_CTL3, 0xa47e);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_JD_CTL1, RT711_JD2_DIGITAL_MODE_SEL,
+ RT711_JD2_DIGITAL_MODE_SEL);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_JD_CTL2, RT711_JD2_2PORT_200K_DECODE_HP |
+ RT711_JD2_2PORT_100K_DECODE_MASK | RT711_HP_JD_SEL_JD2,
+ RT711_JD2_2PORT_100K_DECODE_HP | RT711_HP_JD_SEL_JD2);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+ RT711_CC_DET1,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12 | RT711_POW_CC1_AGPI,
+ RT711_HP_JD_FINAL_RESULT_CTL_JD12 | RT711_POW_CC1_AGPI_OFF);
+ break;
default:
dev_warn(rt711->component->dev, "Wrong JD source\n");
break;
@@ -1234,10 +1249,7 @@ static int rt711_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
stream->sdw_stream = sdw_stream;
/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- if (direction == SNDRV_PCM_STREAM_PLAYBACK)
- dai->playback_dma_data = stream;
- else
- dai->capture_dma_data = stream;
+ snd_soc_dai_dma_data_set(dai, direction, stream);
return 0;
}
diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h
index 498ca687c47bc3..10e3c801b81330 100644
--- a/sound/soc/codecs/rt711-sdca.h
+++ b/sound/soc/codecs/rt711-sdca.h
@@ -127,12 +127,17 @@ struct sdw_stream_data {
/* jack detect control 2 (0x09)(NID:20h) */
#define RT711_JD2_2PORT_200K_DECODE_HP (0x1 << 13)
+#define RT711_JD2_2PORT_100K_DECODE_MASK (0x1 << 12)
+#define RT711_JD2_2PORT_100K_DECODE_HP (0x0 << 12)
#define RT711_HP_JD_SEL_JD1 (0x0 << 1)
#define RT711_HP_JD_SEL_JD2 (0x1 << 1)
/* CC DET1 (0x11)(NID:20h) */
#define RT711_HP_JD_FINAL_RESULT_CTL_JD12 (0x1 << 10)
#define RT711_HP_JD_FINAL_RESULT_CTL_CCDET (0x0 << 10)
+#define RT711_POW_CC1_AGPI (0x1 << 5)
+#define RT711_POW_CC1_AGPI_ON (0x1 << 5)
+#define RT711_POW_CC1_AGPI_OFF (0x0 << 5)
/* Parameter & Verb control (0x1a)(NID:20h) */
#define RT711_HIDDEN_REG_SW_RESET (0x1 << 14)
@@ -226,7 +231,8 @@ enum {
enum rt711_sdca_jd_src {
RT711_JD_NULL,
RT711_JD1,
- RT711_JD2
+ RT711_JD2,
+ RT711_JD2_100K
};
enum rt711_sdca_ver {
diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c
index 78e1da9b07388b..862f509505652b 100644
--- a/sound/soc/codecs/rt711.c
+++ b/sound/soc/codecs/rt711.c
@@ -243,7 +243,7 @@ static void rt711_jack_detect_handler(struct work_struct *work)
if (!rt711->hs_jack)
return;
- if (!rt711->component->card || !rt711->component->card->instantiated)
+ if (!snd_soc_card_is_instantiated(rt711->component->card))
return;
if (pm_runtime_status_suspended(rt711->slave->dev.parent)) {
@@ -976,10 +976,7 @@ static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
stream->sdw_stream = sdw_stream;
/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- if (direction == SNDRV_PCM_STREAM_PLAYBACK)
- dai->playback_dma_data = stream;
- else
- dai->capture_dma_data = stream;
+ snd_soc_dai_dma_data_set(dai, direction, stream);
return 0;
}
diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c
new file mode 100644
index 00000000000000..3f319459dfec39
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca-sdw.c
@@ -0,0 +1,485 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt712-sdca-sdw.c -- rt712 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+#include "rt712-sdca.h"
+#include "rt712-sdca-sdw.h"
+
+static bool rt712_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201a ... 0x201f:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2230 ... 0x2232:
+ case 0x2f01 ... 0x2f0a:
+ case 0x2f35 ... 0x2f36:
+ case 0x2f50:
+ case 0x2f54:
+ case 0x2f58 ... 0x2f5d:
+ case 0x3201:
+ case 0x320c:
+ case 0x3301 ... 0x3303:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_SELECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x202d ... 0x202f:
+ case 0x2230:
+ case 0x2f01:
+ case 0x2f35:
+ case 0x320c:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case RT712_BUF_ADDR_HID1 ... RT712_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000 ... 0x200008e:
+ case 0x5300000 ... 0x530000e:
+ case 0x5400000 ... 0x540000e:
+ case 0x5600000 ... 0x5600008:
+ case 0x5700000 ... 0x570000d:
+ case 0x5800000 ... 0x5800021:
+ case 0x5900000 ... 0x5900028:
+ case 0x5a00000 ... 0x5a00009:
+ case 0x5b00000 ... 0x5b00051:
+ case 0x5c00000 ... 0x5c0009a:
+ case 0x5d00000 ... 0x5d00009:
+ case 0x5f00000 ... 0x5f00030:
+ case 0x6100000 ... 0x6100068:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_R):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000:
+ case 0x200001a:
+ case 0x2000024:
+ case 0x2000046:
+ case 0x200008a:
+ case 0x5800000:
+ case 0x5800001:
+ case 0x6100008:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt712_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt712_sdca_readable_register,
+ .volatile_reg = rt712_sdca_volatile_register,
+ .max_register = 0x44ffffff,
+ .reg_defaults = rt712_sdca_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt712_sdca_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt712_sdca_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt712_sdca_mbq_readable_register,
+ .volatile_reg = rt712_sdca_mbq_volatile_register,
+ .max_register = 0x41000312,
+ .reg_defaults = rt712_sdca_mbq_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt712_sdca_mbq_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt712_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev);
+
+ /* Update the status */
+ rt712->status = status;
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt712->hw_init = false;
+
+ if (status == SDW_SLAVE_ATTACHED) {
+ if (rt712->hs_jack) {
+ /*
+ * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then
+ * if the device attached again, we will need to set the setting back.
+ * It could avoid losing the jack detection interrupt.
+ * This also could sync with the cache value as the rt712_sdca_jack_init set.
+ */
+ sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ }
+ }
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt712->hw_init || rt712->status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt712_sdca_io_init(&slave->dev, slave);
+}
+
+static int rt712_sdca_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval;
+ int i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = BIT(4); /* BITMAP: 00010000 */
+ prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 1380;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+static int rt712_sdca_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev);
+ int ret, stat;
+ int count = 0, retry = 3;
+ unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0;
+
+ dev_dbg(&slave->dev,
+ "%s control_port_stat=%x, sdca_cascade=%x", __func__,
+ status->control_port, status->sdca_cascade);
+
+ if (cancel_delayed_work_sync(&rt712->jack_detect_work)) {
+ dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__);
+ /* avoid the HID owner doesn't change to device */
+ if (rt712->scp_sdca_stat2)
+ scp_sdca_stat2 = rt712->scp_sdca_stat2;
+ }
+
+ /*
+ * The critical section below intentionally protects a rather large piece of code.
+ * We don't want to allow the system suspend to disable an interrupt while we are
+ * processing it, which could be problematic given the quirky SoundWire interrupt
+ * scheme. We do want however to prevent new workqueues from being scheduled if
+ * the disable_irq flag was set during system suspend.
+ */
+ mutex_lock(&rt712->disable_irq_lock);
+
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ rt712->scp_sdca_stat1 = ret;
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ rt712->scp_sdca_stat2 = ret;
+ if (scp_sdca_stat2)
+ rt712->scp_sdca_stat2 |= scp_sdca_stat2;
+
+ do {
+ /* clear flag */
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) {
+ ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ if (ret < 0)
+ goto io_error;
+ }
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) {
+ ret = sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INT2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ /* check if flag clear or not */
+ ret = sdw_read_no_pm(rt712->slave, SDW_DP0_INT);
+ if (ret < 0)
+ goto io_error;
+ sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;
+
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0;
+
+ ret = sdw_read_no_pm(rt712->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8;
+
+ stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade;
+
+ count++;
+ } while (stat != 0 && count < retry);
+
+ if (stat)
+ dev_warn(&slave->dev,
+ "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt712->scp_sdca_stat1, rt712->scp_sdca_stat2);
+
+ if (status->sdca_cascade && !rt712->disable_irq)
+ mod_delayed_work(system_power_efficient_wq,
+ &rt712->jack_detect_work, msecs_to_jiffies(30));
+
+ mutex_unlock(&rt712->disable_irq_lock);
+
+ return 0;
+
+io_error:
+ mutex_unlock(&rt712->disable_irq_lock);
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static struct sdw_slave_ops rt712_sdca_slave_ops = {
+ .read_prop = rt712_sdca_read_prop,
+ .interrupt_callback = rt712_sdca_interrupt_callback,
+ .update_status = rt712_sdca_update_status,
+};
+
+static int rt712_sdca_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap, *mbq_regmap;
+
+ /* Regmap Initialization */
+ mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt712_sdca_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt712_sdca_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt712_sdca_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt712_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev);
+
+ if (rt712->hw_init) {
+ cancel_delayed_work_sync(&rt712->jack_detect_work);
+ cancel_delayed_work_sync(&rt712->jack_btn_check_work);
+ }
+
+ if (rt712->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+
+ mutex_destroy(&rt712->calibrate_mutex);
+ mutex_destroy(&rt712->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt712_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x712, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x713, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x716, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x717, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt712_sdca_id);
+
+static int __maybe_unused rt712_sdca_dev_suspend(struct device *dev)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
+
+ if (!rt712->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt712->jack_detect_work);
+ cancel_delayed_work_sync(&rt712->jack_btn_check_work);
+
+ regcache_cache_only(rt712->regmap, true);
+ regcache_cache_only(rt712->mbq_regmap, true);
+
+ return 0;
+}
+
+static int __maybe_unused rt712_sdca_dev_system_suspend(struct device *dev)
+{
+ struct rt712_sdca_priv *rt712_sdca = dev_get_drvdata(dev);
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret1, ret2;
+
+ if (!rt712_sdca->hw_init)
+ return 0;
+
+ /*
+ * prevent new interrupts from being handled after the
+ * deferred work completes and before the parent disables
+ * interrupts on the link
+ */
+ mutex_lock(&rt712_sdca->disable_irq_lock);
+ rt712_sdca->disable_irq = true;
+ ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0, 0);
+ ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8, 0);
+ mutex_unlock(&rt712_sdca->disable_irq_lock);
+
+ if (ret1 < 0 || ret2 < 0) {
+ /* log but don't prevent suspend from happening */
+ dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__);
+ }
+
+ return rt712_sdca_dev_suspend(dev);
+}
+
+#define RT712_PROBE_TIMEOUT 5000
+
+static int __maybe_unused rt712_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt712->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT712_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt712->regmap, false);
+ regcache_sync(rt712->regmap);
+ regcache_cache_only(rt712->mbq_regmap, false);
+ regcache_sync(rt712->mbq_regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt712_sdca_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(rt712_sdca_dev_system_suspend, rt712_sdca_dev_resume)
+ SET_RUNTIME_PM_OPS(rt712_sdca_dev_suspend, rt712_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt712_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt712-sdca",
+ .owner = THIS_MODULE,
+ .pm = &rt712_sdca_pm,
+ },
+ .probe = rt712_sdca_sdw_probe,
+ .remove = rt712_sdca_sdw_remove,
+ .ops = &rt712_sdca_slave_ops,
+ .id_table = rt712_sdca_id,
+};
+module_sdw_driver(rt712_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT712 SDCA SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt712-sdca-sdw.h b/sound/soc/codecs/rt712-sdca-sdw.h
new file mode 100644
index 00000000000000..4be22ccd856186
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca-sdw.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt712-sdca-sdw.h -- RT712 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT712_SDW_H__
+#define __RT712_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt712_sdca_reg_defaults[] = {
+ { 0x201a, 0x00 },
+ { 0x201b, 0x00 },
+ { 0x201c, 0x00 },
+ { 0x201d, 0x00 },
+ { 0x201e, 0x00 },
+ { 0x201f, 0x00 },
+ { 0x2029, 0x00 },
+ { 0x202a, 0x00 },
+ { 0x202d, 0x00 },
+ { 0x202e, 0x00 },
+ { 0x202f, 0x00 },
+ { 0x2030, 0x00 },
+ { 0x2031, 0x00 },
+ { 0x2032, 0x00 },
+ { 0x2033, 0x00 },
+ { 0x2034, 0x00 },
+ { 0x2230, 0x00 },
+ { 0x2231, 0x2f },
+ { 0x2232, 0x80 },
+ { 0x2f01, 0x00 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x00 },
+ { 0x2f04, 0x00 },
+ { 0x2f05, 0x0b },
+ { 0x2f06, 0x01 },
+ { 0x2f08, 0x00 },
+ { 0x2f09, 0x00 },
+ { 0x2f0a, 0x01 },
+ { 0x2f35, 0x01 },
+ { 0x2f36, 0xcf },
+ { 0x2f50, 0x0f },
+ { 0x2f54, 0x01 },
+ { 0x2f58, 0x07 },
+ { 0x2f59, 0x09 },
+ { 0x2f5a, 0x01 },
+ { 0x2f5b, 0x07 },
+ { 0x2f5c, 0x05 },
+ { 0x2f5d, 0x05 },
+ { 0x3201, 0x01 },
+ { 0x320c, 0x00 },
+ { 0x3301, 0x01 },
+ { 0x3302, 0x00 },
+ { 0x3303, 0x1f },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23, RT712_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+};
+
+static const struct reg_default rt712_sdca_mbq_defaults[] = {
+ { 0x2000004, 0xaa01 },
+ { 0x200000e, 0x21e0 },
+ { 0x2000024, 0x01ba },
+ { 0x200004a, 0x8830 },
+ { 0x2000067, 0xf100 },
+ { 0x5800000, 0x1893 },
+ { 0x5b00000, 0x0407 },
+ { 0x5b00005, 0x0000 },
+ { 0x5b00029, 0x3fff },
+ { 0x5b0002a, 0xf000 },
+ { 0x5f00008, 0x7000 },
+ { 0x610000e, 0x0007 },
+ { 0x6100022, 0x2828 },
+ { 0x6100023, 0x2929 },
+ { 0x6100026, 0x2c29 },
+ { 0x610002c, 0x4150 },
+ { 0x6100045, 0x0860 },
+ { 0x6100046, 0x0029 },
+ { 0x6100053, 0x3fff },
+ { 0x6100055, 0x0000 },
+ { 0x6100060, 0x0000 },
+ { 0x6100064, 0x8000 },
+ { 0x6100065, 0x0000 },
+ { 0x6100067, 0xff12 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_R), 0x0000 },
+};
+
+#endif /* __RT712_SDW_H__ */
diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c
new file mode 100644
index 00000000000000..8d2fa769bb2e2c
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca.c
@@ -0,0 +1,1340 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt712-sdca.c -- rt712 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include "rt712-sdca.h"
+
+static int rt712_sdca_index_write(struct rt712_sdca_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ int ret;
+ struct regmap *regmap = rt712->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt712->slave->dev,
+ "Failed to set private value: %06x <= %04x ret=%d\n",
+ addr, value, ret);
+
+ return ret;
+}
+
+static int rt712_sdca_index_read(struct rt712_sdca_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ int ret;
+ struct regmap *regmap = rt712->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt712->slave->dev,
+ "Failed to get private value: %06x => %04x ret=%d\n",
+ addr, *value, ret);
+
+ return ret;
+}
+
+static int rt712_sdca_index_update_bits(struct rt712_sdca_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = rt712_sdca_index_read(rt712, nid, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ set_mask_bits(&tmp, mask, val);
+ return rt712_sdca_index_write(rt712, nid, reg, tmp);
+}
+
+static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712)
+{
+ unsigned int val, loop_rc = 0, loop_dc = 0;
+ struct device *dev;
+ struct regmap *regmap = rt712->regmap;
+ int chk_cnt = 100;
+ int ret = 0;
+
+ mutex_lock(&rt712->calibrate_mutex);
+ dev = regmap_get_device(regmap);
+
+ /* Set HP-JD source from JD1 */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a);
+
+ /* FSM switch to calibration manual mode */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4100);
+
+ /* Calibration setting */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, RT712_DAC_DC_CALI_CTL1, 0x7883);
+
+ /* W1C Trigger DC calibration (HP & Class-D) */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_CALI, RT712_DAC_DC_CALI_CTL1, 0xf893);
+
+ /* wait for calibration process */
+ rt712_sdca_index_read(rt712, RT712_VENDOR_CALI,
+ RT712_DAC_DC_CALI_CTL1, &val);
+
+ for (loop_dc = 0; loop_dc < chk_cnt &&
+ (val & RT712_DAC_DC_CALI_TRIGGER); loop_dc++) {
+ usleep_range(10000, 11000);
+ ret = rt712_sdca_index_read(rt712, RT712_VENDOR_CALI,
+ RT712_DAC_DC_CALI_CTL1, &val);
+ if (ret < 0)
+ goto _cali_fail_;
+ }
+ if (loop_dc == chk_cnt)
+ dev_err(dev, "%s, calibration time-out!\n", __func__);
+
+ if (loop_dc == chk_cnt || loop_rc == chk_cnt)
+ ret = -ETIMEDOUT;
+
+_cali_fail_:
+ /* Enable Rldet in FSM */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_FSM_CTL, 0x4500);
+
+ /* Sensing Lch+Rch */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_IMS_DIGITAL_CTL1, 0x040f);
+
+ /* Sine gen path control */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_IMS_DIGITAL_CTL5, 0x0000);
+
+ /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_DIGITAL_MISC_CTRL4, 0x0010);
+
+ mutex_unlock(&rt712->calibrate_mutex);
+ dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+static unsigned int rt712_sdca_button_detect(struct rt712_sdca_priv *rt712)
+{
+ unsigned int btn_type = 0, offset, idx, val, owner;
+ int ret;
+ unsigned char buf[3];
+
+ /* get current UMP message owner */
+ ret = regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 0),
+ &owner);
+ if (ret < 0)
+ return 0;
+
+ /* if owner is device then there is no button event from device */
+ if (owner == 1)
+ return 0;
+
+ /* read UMP message offset */
+ ret = regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0),
+ &offset);
+ if (ret < 0)
+ goto _end_btn_det_;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt712->regmap,
+ RT712_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto _end_btn_det_;
+ buf[idx] = val & 0xff;
+ }
+
+ if (buf[0] == 0x11) {
+ switch (buf[1] & 0xf0) {
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ switch (buf[2]) {
+ case 0x01:
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x02:
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x04:
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x08:
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ }
+
+_end_btn_det_:
+ /* Host is owner, so set back to device */
+ if (owner == 0)
+ /* set owner to device */
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01,
+ RT712_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE, 0), 0x01);
+
+ return btn_type;
+}
+
+static int rt712_sdca_headset_detect(struct rt712_sdca_priv *rt712)
+{
+ unsigned int det_mode;
+ int ret;
+
+ /* get detected_mode */
+ ret = regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0),
+ &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ switch (det_mode) {
+ case 0x00:
+ rt712->jack_type = 0;
+ break;
+ case 0x03:
+ rt712->jack_type = SND_JACK_HEADPHONE;
+ break;
+ case 0x05:
+ rt712->jack_type = SND_JACK_HEADSET;
+ break;
+ }
+
+ /* write selected_mode */
+ if (det_mode) {
+ ret = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_SELECTED_MODE, 0),
+ det_mode);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ dev_dbg(&rt712->slave->dev,
+ "%s, detected_mode=0x%x\n", __func__, det_mode);
+
+ return 0;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static void rt712_sdca_jack_detect_handler(struct work_struct *work)
+{
+ struct rt712_sdca_priv *rt712 =
+ container_of(work, struct rt712_sdca_priv, jack_detect_work.work);
+ int btn_type = 0, ret;
+
+ if (!rt712->hs_jack)
+ return;
+
+ if (!rt712->component->card || !rt712->component->card->instantiated)
+ return;
+
+ /* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */
+ if (rt712->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) {
+ ret = rt712_sdca_headset_detect(rt712);
+ if (ret < 0)
+ return;
+ }
+
+ /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */
+ if (rt712->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8)
+ btn_type = rt712_sdca_button_detect(rt712);
+
+ if (rt712->jack_type == 0)
+ btn_type = 0;
+
+ dev_dbg(&rt712->slave->dev,
+ "in %s, jack_type=0x%x\n", __func__, rt712->jack_type);
+ dev_dbg(&rt712->slave->dev,
+ "in %s, btn_type=0x%x\n", __func__, btn_type);
+ dev_dbg(&rt712->slave->dev,
+ "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt712->scp_sdca_stat1, rt712->scp_sdca_stat2);
+
+ snd_soc_jack_report(rt712->hs_jack, rt712->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt712->hs_jack, rt712->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt712->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+}
+
+static void rt712_sdca_btn_check_handler(struct work_struct *work)
+{
+ struct rt712_sdca_priv *rt712 =
+ container_of(work, struct rt712_sdca_priv, jack_btn_check_work.work);
+ int btn_type = 0, ret, idx;
+ unsigned int det_mode, offset, val;
+ unsigned char buf[3];
+
+ ret = regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_GE49, RT712_SDCA_CTL_DETECTED_MODE, 0),
+ &det_mode);
+ if (ret < 0)
+ goto io_error;
+
+ /* pin attached */
+ if (det_mode) {
+ /* read UMP message offset */
+ ret = regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01, RT712_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0),
+ &offset);
+ if (ret < 0)
+ goto io_error;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt712->regmap,
+ RT712_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto io_error;
+ buf[idx] = val & 0xff;
+ }
+
+ if (buf[0] == 0x11) {
+ switch (buf[1] & 0xf0) {
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ switch (buf[2]) {
+ case 0x01:
+ case 0x10:
+ btn_type |= SND_JACK_BTN_2;
+ break;
+ case 0x02:
+ case 0x20:
+ btn_type |= SND_JACK_BTN_3;
+ break;
+ case 0x04:
+ case 0x40:
+ btn_type |= SND_JACK_BTN_0;
+ break;
+ case 0x08:
+ case 0x80:
+ btn_type |= SND_JACK_BTN_1;
+ break;
+ }
+ }
+ } else {
+ rt712->jack_type = 0;
+ }
+
+ dev_dbg(&rt712->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type);
+ snd_soc_jack_report(rt712->hs_jack, rt712->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ if (btn_type) {
+ /* button released */
+ snd_soc_jack_report(rt712->hs_jack, rt712->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+ mod_delayed_work(system_power_efficient_wq,
+ &rt712->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+
+ return;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt712_sdca_jack_init(struct rt712_sdca_priv *rt712)
+{
+ mutex_lock(&rt712->calibrate_mutex);
+
+ if (rt712->hs_jack) {
+ /* Enable HID1 event & set button RTC mode */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_UMP_HID_CTL5, 0xfff0);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_UMP_HID_CTL0, 0x1100, 0x1100);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_UMP_HID_CTL7, 0xf000, 0x0000);
+
+ /* detected_mode_change_event_en & hid1_push_button_event_en */
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_GE_RELATED_CTL1, 0x0c00, 0x0c00);
+ /* ge_inbox_en */
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_GE_RELATED_CTL2, 0x0020, 0x0000);
+
+ switch (rt712->jd_src) {
+ case RT712_JD1:
+ /* Set HP-JD source from JD1 */
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CC_DET1, 0x043a);
+ break;
+ default:
+ dev_warn(rt712->component->dev, "Wrong JD source\n");
+ break;
+ }
+
+ /* set SCP_SDCA_IntMask1[0]=1 */
+ sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_0);
+ /* set SCP_SDCA_IntMask2[0]=1 */
+ sdw_write_no_pm(rt712->slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
+ dev_dbg(&rt712->slave->dev, "in %s enable\n", __func__);
+
+ /* trigger GE interrupt */
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_GE_RELATED_CTL1, 0x0080, 0x0080);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_GE_RELATED_CTL1, 0x0080, 0x0000);
+ } else {
+ /* disable HID1 & detected_mode_change event */
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_GE_RELATED_CTL1, 0x0c00, 0x0000);
+
+ dev_dbg(&rt712->slave->dev, "in %s disable\n", __func__);
+ }
+
+ mutex_unlock(&rt712->calibrate_mutex);
+}
+
+static int rt712_sdca_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt712->hs_jack = hs_jack;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* pm_runtime not enabled yet */
+ dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__);
+ return 0;
+ }
+
+ rt712_sdca_jack_init(rt712);
+
+ pm_runtime_mark_last_busy(component->dev);
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+
+/* For SDCA control DAC/ADC Gain */
+static int rt712_sdca_set_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int read_l, read_r, gain_l_val, gain_r_val;
+ unsigned int adc_vol_flag = 0;
+ unsigned int lvalue, rvalue;
+ const unsigned int interval_offset = 0xc0;
+ const unsigned int tendB = 0xa00;
+
+ if (strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt712->mbq_regmap, mc->reg, &lvalue);
+ regmap_read(rt712->mbq_regmap, mc->rreg, &rvalue);
+
+ /* L Channel */
+ gain_l_val = ucontrol->value.integer.value[0];
+ if (gain_l_val > mc->max)
+ gain_l_val = mc->max;
+
+ if (mc->shift == 8) /* boost gain */
+ gain_l_val = gain_l_val * tendB;
+ else {
+ /* ADC/DAC gain */
+ if (adc_vol_flag)
+ gain_l_val = 0x1e00 - ((mc->max - gain_l_val) * interval_offset);
+ else
+ gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset);
+ gain_l_val &= 0xffff;
+ }
+
+ /* R Channel */
+ gain_r_val = ucontrol->value.integer.value[1];
+ if (gain_r_val > mc->max)
+ gain_r_val = mc->max;
+
+ if (mc->shift == 8) /* boost gain */
+ gain_r_val = gain_r_val * tendB;
+ else {
+ /* ADC/DAC gain */
+ if (adc_vol_flag)
+ gain_r_val = 0x1e00 - ((mc->max - gain_r_val) * interval_offset);
+ else
+ gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset);
+ gain_r_val &= 0xffff;
+ }
+
+ if (lvalue == gain_l_val && rvalue == gain_r_val)
+ return 0;
+
+ /* Lch*/
+ regmap_write(rt712->mbq_regmap, mc->reg, gain_l_val);
+ /* Rch */
+ regmap_write(rt712->mbq_regmap, mc->rreg, gain_r_val);
+
+ regmap_read(rt712->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt712->mbq_regmap, mc->rreg, &read_r);
+ if (read_r == gain_r_val && read_l == gain_l_val)
+ return 1;
+
+ return -EIO;
+}
+
+static int rt712_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0;
+ unsigned int adc_vol_flag = 0;
+ const unsigned int interval_offset = 0xc0;
+ const unsigned int tendB = 0xa00;
+
+ if (strstr(ucontrol->id.name, "FU0F Capture Volume"))
+ adc_vol_flag = 1;
+
+ regmap_read(rt712->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt712->mbq_regmap, mc->rreg, &read_r);
+
+ if (mc->shift == 8) /* boost gain */
+ ctl_l = read_l / tendB;
+ else {
+ if (adc_vol_flag)
+ ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset);
+ else
+ ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset);
+ }
+
+ if (read_l != read_r) {
+ if (mc->shift == 8) /* boost gain */
+ ctl_r = read_r / tendB;
+ else { /* ADC/DAC gain */
+ if (adc_vol_flag)
+ ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset);
+ else
+ ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset);
+ }
+ } else
+ ctl_r = ctl_l;
+
+ ucontrol->value.integer.value[0] = ctl_l;
+ ucontrol->value.integer.value[1] = ctl_r;
+
+ return 0;
+}
+
+static int rt712_sdca_set_fu0f_capture_ctl(struct rt712_sdca_priv *rt712)
+{
+ int err;
+ unsigned int ch_l, ch_r;
+
+ ch_l = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_l_mute) ? 0x01 : 0x00;
+ ch_r = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F,
+ RT712_SDCA_CTL_FU_MUTE, CH_L), ch_l);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F,
+ RT712_SDCA_CTL_FU_MUTE, CH_R), ch_r);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt712_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !rt712->fu0f_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt712->fu0f_mixer_r_mute;
+ return 0;
+}
+
+static int rt712_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int err;
+
+ if (rt712->fu0f_mixer_l_mute == !ucontrol->value.integer.value[0] &&
+ rt712->fu0f_mixer_r_mute == !ucontrol->value.integer.value[1])
+ return 0;
+
+ rt712->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt712->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1];
+ err = rt712_sdca_set_fu0f_capture_ctl(rt712);
+ if (err < 0)
+ return err;
+
+ return 1;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt712_sdca_controls[] = {
+ SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_R),
+ 0, 0x57, 0,
+ rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, out_vol_tlv),
+ SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt712_sdca_fu0f_capture_get, rt712_sdca_fu0f_capture_put),
+ SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_R),
+ 0, 0x3f, 0,
+ rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("FU44 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_R),
+ 8, 3, 0,
+ rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, boost_vol_tlv),
+};
+
+static const struct snd_kcontrol_new rt712_sdca_spk_controls[] = {
+ SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_R),
+ 0, 0x57, 0,
+ rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, out_vol_tlv),
+};
+
+static int rt712_sdca_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mask = 0x3300;
+
+ rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL, RT712_MIXER_CTL1, &val);
+
+ val = val & mask;
+ switch (val) {
+ case 0x3000:
+ val = 1;
+ break;
+ case 0x0300:
+ val = 0;
+ break;
+ }
+
+ ucontrol->value.enumerated.item[0] = val;
+
+ return 0;
+}
+
+static int rt712_sdca_mux_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int mask_sft;
+ unsigned int val;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (ucontrol->value.enumerated.item[0] == 0)
+ mask_sft = 12;
+ else if (ucontrol->value.enumerated.item[0] == 1)
+ mask_sft = 8;
+ else
+ return -EINVAL;
+
+ rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL, RT712_MIXER_CTL1, &val);
+ val = (val >> mask_sft) & 0x3;
+ if (!val)
+ return 0;
+
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_MIXER_CTL1, 0x3fff);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_MIXER_CTL1, 0x3 << mask_sft, 0);
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return 1;
+}
+
+static const char * const adc_mux_text[] = {
+ "MIC2",
+ "LINE2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt712_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static const struct snd_kcontrol_new rt712_sdca_adc23_mux =
+ SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt712_adc23_enum,
+ rt712_sdca_mux_get, rt712_sdca_mux_put);
+
+static int rt712_sdca_fu05_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
+ RT712_SDCA_CTL_FU_MUTE, CH_L),
+ unmute);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
+ RT712_SDCA_CTL_FU_MUTE, CH_R),
+ unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
+ RT712_SDCA_CTL_FU_MUTE, CH_L),
+ mute);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
+ RT712_SDCA_CTL_FU_MUTE, CH_R),
+ mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_fu0f_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt712->fu0f_dapm_mute = false;
+ rt712_sdca_set_fu0f_capture_ctl(rt712);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt712->fu0f_dapm_mute = true;
+ rt712_sdca_set_fu0f_capture_ctl(rt712);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_pde40_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE40,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_pde12_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PDE12,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_classd_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_PDE23,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rt712_spk_sto_dac =
+ SOC_DAPM_DOUBLE_R("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_R),
+ 0, 1, 1);
+
+static const struct snd_soc_dapm_widget rt712_sdca_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("LINE2"),
+
+ SND_SOC_DAPM_SUPPLY("PDE 40", SND_SOC_NOPM, 0, 0,
+ rt712_sdca_pde40_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 12", SND_SOC_NOPM, 0, 0,
+ rt712_sdca_pde12_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_DAC_E("FU 05", NULL, SND_SOC_NOPM, 0, 0,
+ rt712_sdca_fu05_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU 0F", NULL, SND_SOC_NOPM, 0, 0,
+ rt712_sdca_fu0f_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
+ &rt712_sdca_adc23_mux),
+
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt712_sdca_audio_map[] = {
+ { "FU 05", NULL, "DP1RX" },
+ { "DP4TX", NULL, "FU 0F" },
+
+ { "FU 0F", NULL, "PDE 12" },
+ { "FU 0F", NULL, "ADC 23 Mux" },
+ { "ADC 23 Mux", "LINE2", "LINE2" },
+ { "ADC 23 Mux", "MIC2", "MIC2" },
+
+ { "HP", NULL, "PDE 40" },
+ { "HP", NULL, "FU 05" },
+};
+
+static const struct snd_soc_dapm_widget rt712_sdca_spk_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SWITCH("FU06", SND_SOC_NOPM, 0, 0, &rt712_spk_sto_dac),
+
+ /* Output */
+ SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt712_sdca_classd_event, SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+};
+
+static const struct snd_soc_dapm_route rt712_sdca_spk_dapm_routes[] = {
+ { "FU06", "Switch", "DP3RX" },
+ { "CLASS D", NULL, "FU06" },
+ { "SPOL", NULL, "CLASS D" },
+ { "SPOR", NULL, "CLASS D" },
+};
+
+static int rt712_sdca_parse_dt(struct rt712_sdca_priv *rt712, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,jd-src", &rt712->jd_src);
+
+ return 0;
+}
+
+static int rt712_sdca_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt712_sdca_parse_dt(rt712, &rt712->slave->dev);
+ rt712->component = component;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ /* add SPK route */
+ if (rt712->hw_id != RT712_DEV_ID_713) {
+ snd_soc_add_component_controls(component,
+ rt712_sdca_spk_controls, ARRAY_SIZE(rt712_sdca_spk_controls));
+ snd_soc_dapm_new_controls(dapm,
+ rt712_sdca_spk_dapm_widgets, ARRAY_SIZE(rt712_sdca_spk_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm,
+ rt712_sdca_spk_dapm_routes, ARRAY_SIZE(rt712_sdca_spk_dapm_routes));
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt712 = {
+ .probe = rt712_sdca_probe,
+ .controls = rt712_sdca_controls,
+ .num_controls = ARRAY_SIZE(rt712_sdca_controls),
+ .dapm_widgets = rt712_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt712_sdca_dapm_widgets),
+ .dapm_routes = rt712_sdca_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt712_sdca_audio_map),
+ .set_jack = rt712_sdca_set_jack_detect,
+ .endianness = 1,
+};
+
+static int rt712_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ struct sdw_stream_data *stream;
+
+ if (!sdw_stream)
+ return 0;
+
+ stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+ if (!stream)
+ return -ENOMEM;
+
+ stream->sdw_stream = sdw_stream;
+
+ /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
+ snd_soc_dai_dma_data_set(dai, direction, stream);
+
+ return 0;
+}
+
+static void rt712_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sdw_stream_data *stream;
+
+ stream = snd_soc_dai_get_dma_data(dai, substream);
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+ kfree(stream);
+}
+
+static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ enum sdw_data_direction direction;
+ struct sdw_stream_data *stream;
+ int retval, port, num_channels;
+ unsigned int sampling_rate;
+
+ dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+ stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!stream)
+ return -EINVAL;
+
+ if (!rt712->slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ direction = SDW_DATA_DIR_RX;
+ if (dai->id == RT712_AIF1)
+ port = 1;
+ else if (dai->id == RT712_AIF2)
+ port = 3;
+ else
+ return -EINVAL;
+ } else {
+ direction = SDW_DATA_DIR_TX;
+ if (dai->id == RT712_AIF1)
+ port = 4;
+ else
+ return -EINVAL;
+ }
+
+ stream_config.frame_rate = params_rate(params);
+ stream_config.ch_count = params_channels(params);
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = direction;
+
+ num_channels = params_channels(params);
+ port_config.ch_mask = GENMASK(num_channels - 1, 0);
+ port_config.num = port;
+
+ retval = sdw_stream_add_slave(rt712->slave, &stream_config,
+ &port_config, 1, stream->sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "Unable to configure port\n");
+ return retval;
+ }
+
+ if (params_channels(params) > 16) {
+ dev_err(component->dev, "Unsupported channels %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 44100:
+ sampling_rate = RT712_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT712_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT712_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT712_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "Rate %d is not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ switch (dai->id) {
+ case RT712_AIF1:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS01, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_CS11, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ break;
+ case RT712_AIF2:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ break;
+ default:
+ dev_err(component->dev, "Wrong DAI id\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt712_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_data *stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt712->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt712->slave, stream->sdw_stream);
+ return 0;
+}
+
+#define RT712_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000)
+#define RT712_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt712_sdca_ops = {
+ .hw_params = rt712_sdca_pcm_hw_params,
+ .hw_free = rt712_sdca_pcm_hw_free,
+ .set_stream = rt712_sdca_set_sdw_stream,
+ .shutdown = rt712_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver rt712_sdca_dai[] = {
+ {
+ .name = "rt712-sdca-aif1",
+ .id = RT712_AIF1,
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT712_STEREO_RATES,
+ .formats = RT712_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT712_STEREO_RATES,
+ .formats = RT712_FORMATS,
+ },
+ .ops = &rt712_sdca_ops,
+ },
+ {
+ .name = "rt712-sdca-aif2",
+ .id = RT712_AIF2,
+ .playback = {
+ .stream_name = "DP3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT712_STEREO_RATES,
+ .formats = RT712_FORMATS,
+ },
+ .ops = &rt712_sdca_ops,
+ }
+};
+
+int rt712_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+ struct rt712_sdca_priv *rt712;
+ int ret;
+
+ rt712 = devm_kzalloc(dev, sizeof(*rt712), GFP_KERNEL);
+ if (!rt712)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt712);
+ rt712->slave = slave;
+ rt712->regmap = regmap;
+ rt712->mbq_regmap = mbq_regmap;
+
+ mutex_init(&rt712->calibrate_mutex);
+ mutex_init(&rt712->disable_irq_lock);
+
+ INIT_DELAYED_WORK(&rt712->jack_detect_work, rt712_sdca_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt712->jack_btn_check_work, rt712_sdca_btn_check_handler);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt712->hw_init = false;
+ rt712->first_hw_init = false;
+ rt712->fu0f_dapm_mute = true;
+ rt712->fu0f_mixer_l_mute = rt712->fu0f_mixer_r_mute = true;
+
+ /* JD source uses JD1 in default */
+ rt712->jd_src = RT712_JD1;
+
+ if (slave->id.part_id != RT712_PART_ID_713)
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt712, rt712_sdca_dai, ARRAY_SIZE(rt712_sdca_dai));
+ else
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt712, rt712_sdca_dai, 1);
+
+ dev_dbg(&slave->dev, "%s\n", __func__);
+
+ return ret;
+}
+
+int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
+ int ret = 0;
+ unsigned int val, hibernation_flag;
+
+ rt712->disable_irq = false;
+
+ if (rt712->hw_init)
+ return 0;
+
+ if (rt712->first_hw_init) {
+ regcache_cache_only(rt712->regmap, false);
+ regcache_cache_bypass(rt712->regmap, true);
+ regcache_cache_only(rt712->mbq_regmap, false);
+ regcache_cache_bypass(rt712->mbq_regmap, true);
+ } else {
+ /*
+ * PM runtime is only enabled when a Slave reports as Attached
+ */
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
+ pm_runtime_use_autosuspend(&slave->dev);
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(&slave->dev);
+
+ pm_runtime_enable(&slave->dev);
+ }
+
+ pm_runtime_get_noresume(&slave->dev);
+
+ rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_JD_PRODUCT_NUM, &val);
+ rt712->hw_id = (val & 0xf000) >> 12;
+
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_ANALOG_BIAS_CTL3, 0xaa81);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_LDO2_3_CTL1, 0xa1e0);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL1, 0x0000);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_HP_DETECT_RLDET_CTL2, 0x0000);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_ANALOG_CTL, RT712_MISC_POWER_CTL7, 0x0000);
+ regmap_write(rt712->regmap, RT712_RC_CAL, 0x23);
+
+ /* calibration */
+ rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_SW_CONFIG1, &hibernation_flag);
+ if (!hibernation_flag) {
+ ret = rt712_sdca_calibration(rt712);
+ if (ret < 0)
+ dev_err(dev, "%s, calibration failed!\n", __func__);
+ }
+
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_MIXER_CTL1, 0x3000, 0x0000);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_ADC0A_08_PDE_FLOAT_CTL, 0x1112);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_MIC2_LINE2_PDE_FLOAT_CTL, 0x3412);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_DAC03_HP_PDE_FLOAT_CTL, 0x4040);
+
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_GPIO_WAKE_EN_CTL, 0x0001, 0x0000);
+ regmap_write(rt712->regmap, 0x2f50, 0x00);
+ regmap_write(rt712->regmap, 0x2f54, 0x00);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_IT09, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+
+ /* add SPK settings */
+ if (rt712->hw_id != RT712_DEV_ID_713) {
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_AMP_PDE_FLOAT_CTL, 0x2323);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_EAPD_CTL, 0x0002);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_OT23, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x04);
+ }
+
+ /*
+ * if set_jack callback occurred early than io_init,
+ * we set up the jack detection function now
+ */
+ if (rt712->hs_jack)
+ rt712_sdca_jack_init(rt712);
+
+ if (!hibernation_flag)
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_SW_CONFIG1, 0x0001);
+
+ if (rt712->first_hw_init) {
+ regcache_cache_bypass(rt712->regmap, false);
+ regcache_mark_dirty(rt712->regmap);
+ regcache_cache_bypass(rt712->mbq_regmap, false);
+ regcache_mark_dirty(rt712->mbq_regmap);
+ } else
+ rt712->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt712->hw_init = true;
+
+ pm_runtime_mark_last_busy(&slave->dev);
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT712 SDCA SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h
new file mode 100644
index 00000000000000..cf647162f9daf2
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca.h
@@ -0,0 +1,220 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt712-sdca.h -- RT712 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT712_H__
+#define __RT712_H__
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+
+struct rt712_sdca_priv {
+ struct regmap *regmap;
+ struct regmap *mbq_regmap;
+ struct snd_soc_component *component;
+ struct sdw_slave *slave;
+ enum sdw_slave_status status;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ struct snd_soc_jack *hs_jack;
+ struct delayed_work jack_detect_work;
+ struct delayed_work jack_btn_check_work;
+ struct mutex calibrate_mutex; /* for headset calibration */
+ struct mutex disable_irq_lock; /* SDCA irq lock protection */
+ bool disable_irq;
+ int jack_type;
+ int jd_src;
+ unsigned int scp_sdca_stat1;
+ unsigned int scp_sdca_stat2;
+ unsigned int hw_id;
+ bool fu0f_dapm_mute;
+ bool fu0f_mixer_l_mute;
+ bool fu0f_mixer_r_mute;
+};
+
+struct sdw_stream_data {
+ struct sdw_stream_runtime *sdw_stream;
+};
+
+/* NID */
+#define RT712_VENDOR_REG 0x20
+#define RT712_VENDOR_CALI 0x58
+#define RT712_ULTRA_SOUND_DET 0x59
+#define RT712_VENDOR_IMS_DRE 0x5b
+#define RT712_VENDOR_ANALOG_CTL 0x5f
+#define RT712_VENDOR_HDA_CTL 0x61
+
+/* Index (NID:20h) */
+#define RT712_JD_PRODUCT_NUM 0x00
+#define RT712_ANALOG_BIAS_CTL3 0x04
+#define RT712_LDO2_3_CTL1 0x0e
+#define RT712_PARA_VERB_CTL 0x1a
+#define RT712_CC_DET1 0x24
+#define RT712_COMBO_JACK_AUTO_CTL1 0x45
+#define RT712_COMBO_JACK_AUTO_CTL2 0x46
+#define RT712_COMBO_JACK_AUTO_CTL3 0x47
+#define RT712_DIGITAL_MISC_CTRL4 0x4a
+#define RT712_FSM_CTL 0x67
+#define RT712_SW_CONFIG1 0x8a
+#define RT712_SW_CONFIG2 0x8b
+
+/* Index (NID:58h) */
+#define RT712_DAC_DC_CALI_CTL1 0x00
+#define RT712_DAC_DC_CALI_CTL2 0x01
+
+/* Index (NID:59h) */
+#define RT712_ULTRA_SOUND_DETECTOR6 0x1e
+
+/* Index (NID:5bh) */
+#define RT712_IMS_DIGITAL_CTL1 0x00
+#define RT712_IMS_DIGITAL_CTL5 0x05
+#define RT712_HP_DETECT_RLDET_CTL1 0x29
+#define RT712_HP_DETECT_RLDET_CTL2 0x2a
+
+/* Index (NID:5fh) */
+#define RT712_MISC_POWER_CTL0 0x00
+#define RT712_MISC_POWER_CTL7 0x08
+
+/* Index (NID:61h) */
+#define RT712_HDA_LEGACY_MUX_CTL0 0x00
+#define RT712_HDA_LEGACY_CONFIG_CTL0 0x06
+#define RT712_HDA_LEGACY_RESET_CTL 0x08
+#define RT712_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e
+#define RT712_DMIC_ENT_FLOAT_CTL 0x10
+#define RT712_DMIC_GAIN_ENT_FLOAT_CTL0 0x11
+#define RT712_DMIC_GAIN_ENT_FLOAT_CTL2 0x13
+#define RT712_ADC_ENT_FLOAT_CTL 0x15
+#define RT712_ADC_VOL_CH_FLOAT_CTL2 0x18
+#define RT712_DAC03_HP_PDE_FLOAT_CTL 0x22
+#define RT712_MIC2_LINE2_PDE_FLOAT_CTL 0x23
+#define RT712_ADC0A_08_PDE_FLOAT_CTL 0x26
+#define RT712_ADC0B_11_PDE_FLOAT_CTL 0x27
+#define RT712_DMIC1_2_PDE_FLOAT_CTL 0x2b
+#define RT712_AMP_PDE_FLOAT_CTL 0x2c
+#define RT712_I2S_IN_OUT_PDE_FLOAT_CTL 0x2f
+#define RT712_GE_RELATED_CTL1 0x45
+#define RT712_GE_RELATED_CTL2 0x46
+#define RT712_MIXER_CTL0 0x52
+#define RT712_MIXER_CTL1 0x53
+#define RT712_EAPD_CTL 0x55
+#define RT712_UMP_HID_CTL0 0x60
+#define RT712_UMP_HID_CTL1 0x61
+#define RT712_UMP_HID_CTL2 0x62
+#define RT712_UMP_HID_CTL3 0x63
+#define RT712_UMP_HID_CTL4 0x64
+#define RT712_UMP_HID_CTL5 0x65
+#define RT712_UMP_HID_CTL6 0x66
+#define RT712_UMP_HID_CTL7 0x67
+#define RT712_UMP_HID_CTL8 0x68
+
+/* Parameter & Verb control 01 (0x1a)(NID:20h) */
+#define RT712_HIDDEN_REG_SW_RESET (0x1 << 14)
+
+/* combo jack auto switch control 2 (0x46)(NID:20h) */
+#define RT712_COMBOJACK_AUTO_DET_STATUS (0x1 << 11)
+#define RT712_COMBOJACK_AUTO_DET_TRS (0x1 << 10)
+#define RT712_COMBOJACK_AUTO_DET_CTIA (0x1 << 9)
+#define RT712_COMBOJACK_AUTO_DET_OMTP (0x1 << 8)
+
+/* DAC DC offset calibration control-1 (0x00)(NID:58h) */
+#define RT712_DAC_DC_CALI_TRIGGER (0x1 << 15)
+
+#define RT712_EAPD_HIGH 0x2
+#define RT712_EAPD_LOW 0x0
+
+/* RC Calibration register */
+#define RT712_RC_CAL 0x3201
+
+/* Buffer address for HID */
+#define RT712_BUF_ADDR_HID1 0x44030000
+#define RT712_BUF_ADDR_HID2 0x44030020
+
+/* RT712 SDCA Control - function number */
+#define FUNC_NUM_JACK_CODEC 0x01
+#define FUNC_NUM_MIC_ARRAY 0x02
+#define FUNC_NUM_HID 0x03
+#define FUNC_NUM_AMP 0x04
+
+/* RT712 SDCA entity */
+#define RT712_SDCA_ENT_HID01 0x01
+#define RT712_SDCA_ENT_GE49 0x49
+#define RT712_SDCA_ENT_USER_FU05 0x05
+#define RT712_SDCA_ENT_USER_FU06 0x06
+#define RT712_SDCA_ENT_USER_FU0F 0x0f
+#define RT712_SDCA_ENT_USER_FU10 0x19
+#define RT712_SDCA_ENT_USER_FU1E 0x1e
+#define RT712_SDCA_ENT_FU15 0x15
+#define RT712_SDCA_ENT_PDE23 0x23
+#define RT712_SDCA_ENT_PDE40 0x40
+#define RT712_SDCA_ENT_PDE11 0x11
+#define RT712_SDCA_ENT_PDE12 0x12
+#define RT712_SDCA_ENT_CS01 0x01
+#define RT712_SDCA_ENT_CS11 0x11
+#define RT712_SDCA_ENT_CS1F 0x1f
+#define RT712_SDCA_ENT_CS1C 0x1c
+#define RT712_SDCA_ENT_CS31 0x31
+#define RT712_SDCA_ENT_OT23 0x42
+#define RT712_SDCA_ENT_IT26 0x26
+#define RT712_SDCA_ENT_IT09 0x09
+#define RT712_SDCA_ENT_PLATFORM_FU15 0x15
+#define RT712_SDCA_ENT_PLATFORM_FU44 0x44
+
+/* RT712 SDCA control */
+#define RT712_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT712_SDCA_CTL_FU_MUTE 0x01
+#define RT712_SDCA_CTL_FU_VOLUME 0x02
+#define RT712_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define RT712_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11
+#define RT712_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12
+#define RT712_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13
+#define RT712_SDCA_CTL_SELECTED_MODE 0x01
+#define RT712_SDCA_CTL_DETECTED_MODE 0x02
+#define RT712_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT712_SDCA_CTL_VENDOR_DEF 0x30
+#define RT712_SDCA_CTL_FU_CH_GAIN 0x0b
+
+/* RT712 SDCA channel */
+#define CH_L 0x01
+#define CH_R 0x02
+
+/* sample frequency index */
+#define RT712_SDCA_RATE_16000HZ 0x04
+#define RT712_SDCA_RATE_32000HZ 0x07
+#define RT712_SDCA_RATE_44100HZ 0x08
+#define RT712_SDCA_RATE_48000HZ 0x09
+#define RT712_SDCA_RATE_96000HZ 0x0b
+#define RT712_SDCA_RATE_192000HZ 0x0d
+
+enum {
+ RT712_AIF1,
+ RT712_AIF2,
+};
+
+enum rt712_sdca_jd_src {
+ RT712_JD_NULL,
+ RT712_JD1,
+};
+
+enum rt712_sdca_hw_id {
+ RT712_DEV_ID_712 = 0x7,
+ RT712_DEV_ID_713 = 0x6,
+ RT712_DEV_ID_716 = 0x5,
+ RT712_DEV_ID_717 = 0x4,
+};
+
+#define RT712_PART_ID_713 0x713
+
+int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt712_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave);
+
+int rt712_sdca_jack_detect(struct rt712_sdca_priv *rt712, bool *hp, bool *mic);
+#endif /* __RT712_H__ */
diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c
index c54ecf3e69879d..38a82e4e2f9525 100644
--- a/sound/soc/codecs/rt715-sdca-sdw.c
+++ b/sound/soc/codecs/rt715-sdca-sdw.c
@@ -172,7 +172,7 @@ static int rt715_sdca_read_prop(struct sdw_slave *slave)
return 0;
}
-static struct sdw_slave_ops rt715_sdca_slave_ops = {
+static const struct sdw_slave_ops rt715_sdca_slave_ops = {
.read_prop = rt715_sdca_read_prop,
.update_status = rt715_sdca_update_status,
};
diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c
index 1fca7a3f46eaca..920510365fd772 100644
--- a/sound/soc/codecs/rt715-sdca.c
+++ b/sound/soc/codecs/rt715-sdca.c
@@ -793,10 +793,7 @@ static int rt715_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
stream->sdw_stream = sdw_stream;
/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- if (direction == SNDRV_PCM_STREAM_PLAYBACK)
- dai->playback_dma_data = stream;
- else
- dai->capture_dma_data = stream;
+ snd_soc_dai_dma_data_set(dai, direction, stream);
return 0;
}
diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c
index 1a2036ccfbac87..c6dd9df7be45c2 100644
--- a/sound/soc/codecs/rt715.c
+++ b/sound/soc/codecs/rt715.c
@@ -777,10 +777,7 @@ static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
stream->sdw_stream = sdw_stream;
/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- if (direction == SNDRV_PCM_STREAM_PLAYBACK)
- dai->playback_dma_data = stream;
- else
- dai->capture_dma_data = stream;
+ snd_soc_dai_dma_data_set(dai, direction, stream);
return 0;
}
diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c
index af52f2728854b0..62b02d764f0905 100644
--- a/sound/soc/codecs/sdw-mockup.c
+++ b/sound/soc/codecs/sdw-mockup.c
@@ -57,10 +57,7 @@ static int sdw_mockup_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
stream->sdw_stream = sdw_stream;
/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- if (direction == SNDRV_PCM_STREAM_PLAYBACK)
- dai->playback_dma_data = stream;
- else
- dai->capture_dma_data = stream;
+ snd_soc_dai_dma_data_set(dai, direction, stream);
return 0;
}
diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c
new file mode 100644
index 00000000000000..727c01facf52db
--- /dev/null
+++ b/sound/soc/codecs/sma1303.c
@@ -0,0 +1,1820 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// sma1303.c -- sma1303 ALSA SoC Audio driver
+//
+// Copyright 2023 Iron Device Corporation
+//
+// Auther: Gyuhwa Park <gyuhwa.park@irondevice.com>
+// Kiseok Jo <kiseok.jo@irondevice.com>
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+
+#include "sma1303.h"
+
+#define CHECK_PERIOD_TIME 1 /* sec per HZ */
+#define MAX_CONTROL_NAME 48
+
+#define PLL_MATCH(_input_clk_name, _output_clk_name, _input_clk,\
+ _post_n, _n, _vco, _p_cp)\
+{\
+ .input_clk_name = _input_clk_name,\
+ .output_clk_name = _output_clk_name,\
+ .input_clk = _input_clk,\
+ .post_n = _post_n,\
+ .n = _n,\
+ .vco = _vco,\
+ .p_cp = _p_cp,\
+}
+
+enum sma1303_type {
+ SMA1303,
+};
+
+struct sma1303_pll_match {
+ char *input_clk_name;
+ char *output_clk_name;
+ unsigned int input_clk;
+ unsigned int post_n;
+ unsigned int n;
+ unsigned int vco;
+ unsigned int p_cp;
+};
+
+struct sma1303_priv {
+ enum sma1303_type devtype;
+ struct attribute_group *attr_grp;
+ struct delayed_work check_fault_work;
+ struct device *dev;
+ struct kobject *kobj;
+ struct regmap *regmap;
+ struct sma1303_pll_match *pll_matches;
+ bool amp_power_status;
+ bool force_mute_status;
+ int num_of_pll_matches;
+ int retry_cnt;
+ unsigned int amp_mode;
+ unsigned int cur_vol;
+ unsigned int format;
+ unsigned int frame_size;
+ unsigned int init_vol;
+ unsigned int last_bclk;
+ unsigned int last_ocp_val;
+ unsigned int last_over_temp;
+ unsigned int rev_num;
+ unsigned int sys_clk_id;
+ unsigned int tdm_slot_rx;
+ unsigned int tdm_slot_tx;
+ unsigned int tsdw_cnt;
+ long check_fault_period;
+ long check_fault_status;
+};
+
+static struct sma1303_pll_match sma1303_pll_matches[] = {
+PLL_MATCH("1.411MHz", "24.595MHz", 1411200, 0x07, 0xF4, 0x8B, 0x03),
+PLL_MATCH("1.536MHz", "24.576MHz", 1536000, 0x07, 0xE0, 0x8B, 0x03),
+PLL_MATCH("3.072MHz", "24.576MHz", 3072000, 0x07, 0x70, 0x8B, 0x03),
+PLL_MATCH("6.144MHz", "24.576MHz", 6144000, 0x07, 0x70, 0x8B, 0x07),
+PLL_MATCH("12.288MHz", "24.576MHz", 12288000, 0x07, 0x70, 0x8B, 0x0B),
+PLL_MATCH("19.2MHz", "24.343MHz", 19200000, 0x07, 0x47, 0x8B, 0x0A),
+PLL_MATCH("24.576MHz", "24.576MHz", 24576000, 0x07, 0x70, 0x8B, 0x0F),
+};
+
+static int sma1303_startup(struct snd_soc_component *);
+static int sma1303_shutdown(struct snd_soc_component *);
+
+static const struct reg_default sma1303_reg_def[] = {
+ { 0x00, 0x80 },
+ { 0x01, 0x00 },
+ { 0x02, 0x00 },
+ { 0x03, 0x11 },
+ { 0x04, 0x17 },
+ { 0x09, 0x00 },
+ { 0x0A, 0x31 },
+ { 0x0B, 0x98 },
+ { 0x0C, 0x84 },
+ { 0x0D, 0x07 },
+ { 0x0E, 0x3F },
+ { 0x10, 0x00 },
+ { 0x11, 0x00 },
+ { 0x12, 0x00 },
+ { 0x14, 0x5C },
+ { 0x15, 0x01 },
+ { 0x16, 0x0F },
+ { 0x17, 0x0F },
+ { 0x18, 0x0F },
+ { 0x19, 0x00 },
+ { 0x1A, 0x00 },
+ { 0x1B, 0x00 },
+ { 0x23, 0x19 },
+ { 0x24, 0x00 },
+ { 0x25, 0x00 },
+ { 0x26, 0x04 },
+ { 0x33, 0x00 },
+ { 0x36, 0x92 },
+ { 0x37, 0x27 },
+ { 0x3B, 0x5A },
+ { 0x3C, 0x20 },
+ { 0x3D, 0x00 },
+ { 0x3E, 0x03 },
+ { 0x3F, 0x0C },
+ { 0x8B, 0x07 },
+ { 0x8C, 0x70 },
+ { 0x8D, 0x8B },
+ { 0x8E, 0x6F },
+ { 0x8F, 0x03 },
+ { 0x90, 0x26 },
+ { 0x91, 0x42 },
+ { 0x92, 0xE0 },
+ { 0x94, 0x35 },
+ { 0x95, 0x0C },
+ { 0x96, 0x42 },
+ { 0x97, 0x95 },
+ { 0xA0, 0x00 },
+ { 0xA1, 0x3B },
+ { 0xA2, 0xC8 },
+ { 0xA3, 0x28 },
+ { 0xA4, 0x40 },
+ { 0xA5, 0x01 },
+ { 0xA6, 0x41 },
+ { 0xA7, 0x00 },
+};
+
+static bool sma1303_readable_register(struct device *dev, unsigned int reg)
+{
+ bool result;
+
+ if (reg > SMA1303_FF_DEVICE_INDEX)
+ return false;
+
+ switch (reg) {
+ case SMA1303_00_SYSTEM_CTRL ... SMA1303_04_INPUT1_CTRL4:
+ case SMA1303_09_OUTPUT_CTRL ... SMA1303_0E_MUTE_VOL_CTRL:
+ case SMA1303_10_SYSTEM_CTRL1 ... SMA1303_12_SYSTEM_CTRL3:
+ case SMA1303_14_MODULATOR ... SMA1303_1B_BASS_SPK7:
+ case SMA1303_23_COMP_LIM1 ... SMA1303_26_COMP_LIM4:
+ case SMA1303_33_SDM_CTRL ... SMA1303_34_OTP_DATA1:
+ case SMA1303_36_PROTECTION ... SMA1303_38_OTP_TRM0:
+ case SMA1303_3B_TEST1 ... SMA1303_3F_ATEST2:
+ case SMA1303_8B_PLL_POST_N ... SMA1303_92_FDPEC_CTRL:
+ case SMA1303_94_BOOST_CTRL1 ... SMA1303_97_BOOST_CTRL4:
+ case SMA1303_A0_PAD_CTRL0 ... SMA1303_A7_CLK_MON:
+ case SMA1303_FA_STATUS1 ... SMA1303_FB_STATUS2:
+ result = true;
+ break;
+ case SMA1303_FF_DEVICE_INDEX:
+ result = true;
+ break;
+ default:
+ result = false;
+ break;
+ }
+ return result;
+}
+
+static bool sma1303_writeable_register(struct device *dev, unsigned int reg)
+{
+ bool result;
+
+ if (reg > SMA1303_FF_DEVICE_INDEX)
+ return false;
+
+ switch (reg) {
+ case SMA1303_00_SYSTEM_CTRL ... SMA1303_04_INPUT1_CTRL4:
+ case SMA1303_09_OUTPUT_CTRL ... SMA1303_0E_MUTE_VOL_CTRL:
+ case SMA1303_10_SYSTEM_CTRL1 ... SMA1303_12_SYSTEM_CTRL3:
+ case SMA1303_14_MODULATOR ... SMA1303_1B_BASS_SPK7:
+ case SMA1303_23_COMP_LIM1 ... SMA1303_26_COMP_LIM4:
+ case SMA1303_33_SDM_CTRL:
+ case SMA1303_36_PROTECTION ... SMA1303_37_SLOPE_CTRL:
+ case SMA1303_3B_TEST1 ... SMA1303_3F_ATEST2:
+ case SMA1303_8B_PLL_POST_N ... SMA1303_92_FDPEC_CTRL:
+ case SMA1303_94_BOOST_CTRL1 ... SMA1303_97_BOOST_CTRL4:
+ case SMA1303_A0_PAD_CTRL0 ... SMA1303_A7_CLK_MON:
+ result = true;
+ break;
+ default:
+ result = false;
+ break;
+ }
+ return result;
+}
+
+static bool sma1303_volatile_register(struct device *dev, unsigned int reg)
+{
+ bool result;
+
+ switch (reg) {
+ case SMA1303_FA_STATUS1 ... SMA1303_FB_STATUS2:
+ result = true;
+ break;
+ case SMA1303_FF_DEVICE_INDEX:
+ result = true;
+ break;
+ default:
+ result = false;
+ break;
+ }
+ return result;
+}
+
+static const DECLARE_TLV_DB_SCALE(sma1303_spk_tlv, -6000, 50, 0);
+
+static int sma1303_regmap_write(struct sma1303_priv *sma1303,
+ unsigned int reg, unsigned int val)
+{
+ int ret = 0;
+ int cnt = sma1303->retry_cnt;
+
+ while (cnt--) {
+ ret = regmap_write(sma1303->regmap, reg, val);
+ if (ret < 0) {
+ dev_err(sma1303->dev,
+ "Failed to write [0x%02X]\n", reg);
+ } else
+ break;
+ }
+ return ret;
+}
+
+static int sma1303_regmap_update_bits(struct sma1303_priv *sma1303,
+ unsigned int reg, unsigned int mask, unsigned int val, bool *change)
+{
+ int ret = 0;
+ int cnt = sma1303->retry_cnt;
+
+ while (cnt--) {
+ ret = regmap_update_bits_check(sma1303->regmap, reg,
+ mask, val, change);
+ if (ret < 0) {
+ dev_err(sma1303->dev,
+ "Failed to update [0x%02X]\n", reg);
+ } else
+ break;
+ }
+ return ret;
+}
+
+static int sma1303_regmap_read(struct sma1303_priv *sma1303,
+ unsigned int reg, unsigned int *val)
+{
+ int ret = 0;
+ int cnt = sma1303->retry_cnt;
+
+ while (cnt--) {
+ ret = regmap_read(sma1303->regmap, reg, val);
+ if (ret < 0) {
+ dev_err(sma1303->dev,
+ "Failed to read [0x%02X]\n", reg);
+ } else
+ break;
+ }
+ return ret;
+}
+
+static const char * const sma1303_aif_in_source_text[] = {
+ "Mono", "Left", "Right"};
+static const char * const sma1303_aif_out_source_text[] = {
+ "Disable", "After_FmtC", "After_Mixer", "After_DSP", "After_Post",
+ "Clk_PLL", "Clk_OSC"};
+static const char * const sma1303_tdm_slot_text[] = {
+ "Slot0", "Slot1", "Slot2", "Slot3",
+ "Slot4", "Slot5", "Slot6", "Slot7"};
+
+static const struct soc_enum sma1303_aif_in_source_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_aif_in_source_text),
+ sma1303_aif_in_source_text);
+static const struct soc_enum sma1303_aif_out_source_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_aif_out_source_text),
+ sma1303_aif_out_source_text);
+static const struct soc_enum sma1303_tdm_slot_enum =
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1303_tdm_slot_text),
+ sma1303_tdm_slot_text);
+
+static int sma1303_force_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = (int)sma1303->force_mute_status;
+ dev_dbg(sma1303->dev, "%s : Force Mute %s\n", __func__,
+ sma1303->force_mute_status ? "ON" : "OFF");
+
+ return 0;
+}
+
+static int sma1303_force_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ bool change = false, val = (bool)ucontrol->value.integer.value[0];
+
+ if (sma1303->force_mute_status == val)
+ change = false;
+ else {
+ change = true;
+ sma1303->force_mute_status = val;
+ }
+ dev_dbg(sma1303->dev, "%s : Force Mute %s\n", __func__,
+ sma1303->force_mute_status ? "ON" : "OFF");
+
+ return change;
+}
+
+static int sma1303_postscaler_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int val, ret;
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_90_POSTSCALER, &val);
+ if (ret < 0)
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] = (val & 0x7E) >> 1;
+
+ return 0;
+}
+
+static int sma1303_postscaler_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret, val = (int)ucontrol->value.integer.value[0];
+ bool change;
+
+ ret = sma1303_regmap_update_bits(sma1303,
+ SMA1303_90_POSTSCALER, 0x7E, (val << 1), &change);
+ if (ret < 0)
+ return -EINVAL;
+
+ return change;
+}
+
+static int sma1303_tdm_slot_rx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int val, ret;
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_A5_TDM1, &val);
+ if (ret < 0)
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] = (val & 0x38) >> 3;
+ sma1303->tdm_slot_rx = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int sma1303_tdm_slot_rx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret, val = (int)ucontrol->value.integer.value[0];
+ bool change;
+
+ ret = sma1303_regmap_update_bits(sma1303,
+ SMA1303_A5_TDM1, 0x38, (val << 3), &change);
+ if (ret < 0)
+ return -EINVAL;
+
+ return change;
+}
+
+static int sma1303_tdm_slot_tx_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int val, ret;
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_A6_TDM2, &val);
+ if (ret < 0)
+ return -EINVAL;
+
+ ucontrol->value.integer.value[0] = (val & 0x38) >> 3;
+ sma1303->tdm_slot_tx = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static int sma1303_tdm_slot_tx_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret, val = (int)ucontrol->value.integer.value[0];
+ bool change;
+
+ ret = sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2, 0x38, (val << 3), &change);
+ if (ret < 0)
+ return -EINVAL;
+
+ return change;
+}
+
+static int sma1303_startup(struct snd_soc_component *component)
+{
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ bool change = false, temp = false;
+
+ sma1303_regmap_update_bits(sma1303, SMA1303_8E_PLL_CTRL,
+ SMA1303_PLL_PD2_MASK, SMA1303_PLL_OPERATION2, &temp);
+ if (temp == true)
+ change = true;
+
+ sma1303_regmap_update_bits(sma1303, SMA1303_00_SYSTEM_CTRL,
+ SMA1303_POWER_MASK, SMA1303_POWER_ON, &temp);
+ if (temp == true)
+ change = true;
+
+ if (sma1303->amp_mode == SMA1303_MONO) {
+ sma1303_regmap_update_bits(sma1303,
+ SMA1303_10_SYSTEM_CTRL1,
+ SMA1303_SPK_MODE_MASK,
+ SMA1303_SPK_MONO,
+ &temp);
+ if (temp == true)
+ change = true;
+
+ } else {
+ sma1303_regmap_update_bits(sma1303,
+ SMA1303_10_SYSTEM_CTRL1,
+ SMA1303_SPK_MODE_MASK,
+ SMA1303_SPK_STEREO,
+ &temp);
+ if (temp == true)
+ change = true;
+ }
+
+ if (sma1303->check_fault_status) {
+ if (sma1303->check_fault_period > 0)
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ sma1303->check_fault_period * HZ);
+ else
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ CHECK_PERIOD_TIME * HZ);
+ }
+
+ sma1303->amp_power_status = true;
+
+ return change;
+}
+
+static int sma1303_shutdown(struct snd_soc_component *component)
+{
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ bool change = false, temp = false;
+
+ cancel_delayed_work_sync(&sma1303->check_fault_work);
+
+ sma1303_regmap_update_bits(sma1303, SMA1303_10_SYSTEM_CTRL1,
+ SMA1303_SPK_MODE_MASK, SMA1303_SPK_OFF, &temp);
+ if (temp == true)
+ change = true;
+
+ sma1303_regmap_update_bits(sma1303, SMA1303_00_SYSTEM_CTRL,
+ SMA1303_POWER_MASK, SMA1303_POWER_OFF, &temp);
+ if (temp == true)
+ change = true;
+ sma1303_regmap_update_bits(sma1303, SMA1303_8E_PLL_CTRL,
+ SMA1303_PLL_PD2_MASK, SMA1303_PLL_PD2, &temp);
+ if (temp == true)
+ change = true;
+
+ sma1303->amp_power_status = false;
+
+ return change;
+}
+
+static int sma1303_aif_in_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
+ int ret = 0;
+ bool change = false, temp = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mux) {
+ case 0:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_11_SYSTEM_CTRL2,
+ SMA1303_MONOMIX_MASK,
+ SMA1303_MONOMIX_ON,
+ &change);
+ sma1303->amp_mode = SMA1303_MONO;
+ break;
+ case 1:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_11_SYSTEM_CTRL2,
+ SMA1303_MONOMIX_MASK,
+ SMA1303_MONOMIX_OFF,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_11_SYSTEM_CTRL2,
+ SMA1303_LR_DATA_SW_MASK,
+ SMA1303_LR_DATA_SW_NORMAL,
+ &temp);
+ if (temp == true)
+ change = true;
+ sma1303->amp_mode = SMA1303_STEREO;
+ break;
+ case 2:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_11_SYSTEM_CTRL2,
+ SMA1303_MONOMIX_MASK,
+ SMA1303_MONOMIX_OFF,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_11_SYSTEM_CTRL2,
+ SMA1303_LR_DATA_SW_MASK,
+ SMA1303_LR_DATA_SW_NORMAL,
+ &temp);
+ if (temp == true)
+ change = true;
+ sma1303->amp_mode = SMA1303_STEREO;
+ break;
+ default:
+ dev_err(sma1303->dev, "%s : Invalid value (%d)\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+
+ dev_dbg(sma1303->dev, "%s : Source : %s\n", __func__,
+ sma1303_aif_in_source_text[mux]);
+ break;
+ }
+ if (ret < 0)
+ return -EINVAL;
+ return change;
+}
+
+static int sma1303_aif_out_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]);
+ int ret = 0;
+ bool change = false, temp = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mux) {
+ case 0:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_NORMAL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_OUT_SEL_MASK,
+ SMA1303_OUT_SEL_DISABLE,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 1:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_NORMAL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_OUT_SEL_MASK,
+ SMA1303_FORMAT_CONVERTER,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 2:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_NORMAL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_OUT_SEL_MASK,
+ SMA1303_MIXER_OUTPUT,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 3:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_NORMAL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_OUT_SEL_MASK,
+ SMA1303_SPEAKER_PATH,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 4:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_NORMAL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_OUT_SEL_MASK,
+ SMA1303_POSTSCALER_OUTPUT,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 5:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_CLK_OUT_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_MON_OSC_PLL_MASK,
+ SMA1303_PLL_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case 6:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_TEST_CLKO_EN_MASK,
+ SMA1303_CLK_OUT_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_MON_OSC_PLL_MASK,
+ SMA1303_OSC_SDO,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ default:
+ dev_err(sma1303->dev, "%s : Invalid value (%d)\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+
+ dev_dbg(sma1303->dev, "%s : Source : %s\n", __func__,
+ sma1303_aif_out_source_text[mux]);
+ break;
+ }
+ if (ret < 0)
+ return -EINVAL;
+ return change;
+}
+
+static int sma1303_sdo_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+ bool change = false, temp = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_PRE_PMU\n", __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_CONFIG_MASK,
+ SMA1303_OUTPUT_PORT_ENABLE,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_SDO_OUTPUT_MASK,
+ SMA1303_NORMAL_OUT,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_POST_PMD\n", __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_09_OUTPUT_CTRL,
+ SMA1303_PORT_CONFIG_MASK,
+ SMA1303_INPUT_PORT_ONLY,
+ &temp);
+ if (temp == true)
+ change = true;
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A3_TOP_MAN2,
+ SMA1303_SDO_OUTPUT_MASK,
+ SMA1303_HIGH_Z_OUT,
+ &temp);
+ if (temp == true)
+ change = true;
+ break;
+ }
+ if (ret < 0)
+ return -EINVAL;
+ return change;
+}
+
+static int sma1303_post_scaler_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+ bool change = false;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_PRE_PMU\n", __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_90_POSTSCALER,
+ SMA1303_BYP_POST_MASK,
+ SMA1303_EN_POST_SCALER,
+ &change);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_POST_PMD\n", __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_90_POSTSCALER,
+ SMA1303_BYP_POST_MASK,
+ SMA1303_BYP_POST_SCALER,
+ &change);
+ break;
+ }
+ if (ret < 0)
+ return -EINVAL;
+ return change;
+}
+
+static int sma1303_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_POST_PMU\n", __func__);
+ ret = sma1303_startup(component);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ dev_dbg(sma1303->dev,
+ "%s : SND_SOC_DAPM_PRE_PMD\n", __func__);
+ ret = sma1303_shutdown(component);
+ break;
+ }
+ return ret;
+}
+
+static const struct snd_kcontrol_new sma1303_aif_in_source_control =
+ SOC_DAPM_ENUM("AIF IN Source", sma1303_aif_in_source_enum);
+static const struct snd_kcontrol_new sma1303_aif_out_source_control =
+ SOC_DAPM_ENUM("AIF OUT Source", sma1303_aif_out_source_enum);
+static const struct snd_kcontrol_new sma1303_sdo_control =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new sma1303_post_scaler_control =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+static const struct snd_kcontrol_new sma1303_enable_control =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const struct snd_kcontrol_new sma1303_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", SMA1303_0A_SPK_VOL,
+ 0, 167, 1, sma1303_spk_tlv),
+ SOC_SINGLE_BOOL_EXT("Force Mute Switch", 0,
+ sma1303_force_mute_get, sma1303_force_mute_put),
+ SOC_SINGLE_EXT("Postscaler Gain", SMA1303_90_POSTSCALER, 1, 0x30, 0,
+ sma1303_postscaler_get, sma1303_postscaler_put),
+ SOC_ENUM_EXT("TDM RX Slot Position", sma1303_tdm_slot_enum,
+ sma1303_tdm_slot_rx_get, sma1303_tdm_slot_rx_put),
+ SOC_ENUM_EXT("TDM TX Slot Position", sma1303_tdm_slot_enum,
+ sma1303_tdm_slot_tx_get, sma1303_tdm_slot_tx_put),
+};
+
+static const struct snd_soc_dapm_widget sma1303_dapm_widgets[] = {
+ /* platform domain */
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("SDO"),
+
+ /* path domain */
+ SND_SOC_DAPM_MUX_E("AIF IN Source", SND_SOC_NOPM, 0, 0,
+ &sma1303_aif_in_source_control,
+ sma1303_aif_in_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MUX_E("AIF OUT Source", SND_SOC_NOPM, 0, 0,
+ &sma1303_aif_out_source_control,
+ sma1303_aif_out_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SWITCH_E("SDO Enable", SND_SOC_NOPM, 0, 0,
+ &sma1303_sdo_control,
+ sma1303_sdo_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MIXER("Entry", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SWITCH_E("Post Scaler", SND_SOC_NOPM, 0, 1,
+ &sma1303_post_scaler_control,
+ sma1303_post_scaler_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("AMP Power", SND_SOC_NOPM, 0, 0, NULL, 0,
+ sma1303_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 1,
+ &sma1303_enable_control),
+
+ /* stream domain */
+ SND_SOC_DAPM_AIF_IN("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route sma1303_audio_map[] = {
+ /* Playback */
+ {"AIF IN Source", "Mono", "AIF IN"},
+ {"AIF IN Source", "Left", "AIF IN"},
+ {"AIF IN Source", "Right", "AIF IN"},
+
+ {"SDO Enable", "Switch", "AIF IN"},
+ {"AIF OUT Source", "Disable", "SDO Enable"},
+ {"AIF OUT Source", "After_FmtC", "SDO Enable"},
+ {"AIF OUT Source", "After_Mixer", "SDO Enable"},
+ {"AIF OUT Source", "After_DSP", "SDO Enable"},
+ {"AIF OUT Source", "After_Post", "SDO Enable"},
+ {"AIF OUT Source", "Clk_PLL", "SDO Enable"},
+ {"AIF OUT Source", "Clk_OSC", "SDO Enable"},
+
+ {"Entry", NULL, "AIF OUT Source"},
+ {"Entry", NULL, "AIF IN Source"},
+
+ {"Post Scaler", "Switch", "Entry"},
+ {"AMP Power", NULL, "Entry"},
+ {"AMP Power", NULL, "Entry"},
+
+ {"AMP Enable", "Switch", "AMP Power"},
+ {"SPK", NULL, "AMP Enable"},
+
+ /* Capture */
+ {"AIF OUT", NULL, "AMP Enable"},
+};
+
+static int sma1303_setup_pll(struct snd_soc_component *component,
+ unsigned int bclk)
+{
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+
+ int i = 0, ret = 0;
+
+ dev_dbg(component->dev, "%s : BCLK = %dHz\n",
+ __func__, bclk);
+
+ if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_MCLK) {
+ dev_dbg(component->dev, "%s : MCLK is not supported\n",
+ __func__);
+ } else if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_BCLK) {
+ for (i = 0; i < sma1303->num_of_pll_matches; i++) {
+ if (sma1303->pll_matches[i].input_clk == bclk)
+ break;
+ }
+ if (i == sma1303->num_of_pll_matches) {
+ dev_dbg(component->dev, "%s : No matching value between pll table and SCK\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A2_TOP_MAN1,
+ SMA1303_PLL_PD_MASK|SMA1303_PLL_REF_CLK_MASK,
+ SMA1303_PLL_OPERATION|SMA1303_PLL_SCK,
+ NULL);
+ }
+
+ ret += sma1303_regmap_write(sma1303,
+ SMA1303_8B_PLL_POST_N,
+ sma1303->pll_matches[i].post_n);
+
+ ret += sma1303_regmap_write(sma1303,
+ SMA1303_8C_PLL_N,
+ sma1303->pll_matches[i].n);
+
+ ret += sma1303_regmap_write(sma1303,
+ SMA1303_8D_PLL_A_SETTING,
+ sma1303->pll_matches[i].vco);
+
+ ret += sma1303_regmap_write(sma1303,
+ SMA1303_8F_PLL_P_CP,
+ sma1303->pll_matches[i].p_cp);
+ if (ret < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int sma1303_dai_hw_params_amp(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ unsigned int bclk = 0;
+ int ret = 0;
+
+ if (sma1303->format == SND_SOC_DAIFMT_DSP_A)
+ bclk = params_rate(params) * sma1303->frame_size;
+ else
+ bclk = params_rate(params) * params_physical_width(params)
+ * params_channels(params);
+
+ dev_dbg(component->dev,
+ "%s : rate = %d : bit size = %d : channel = %d\n",
+ __func__, params_rate(params), params_width(params),
+ params_channels(params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (sma1303->sys_clk_id == SMA1303_PLL_CLKIN_BCLK) {
+ if (sma1303->last_bclk != bclk) {
+ sma1303_setup_pll(component, bclk);
+ sma1303->last_bclk = bclk;
+ }
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 44100:
+ case 48000:
+ case 96000:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A2_TOP_MAN1,
+ SMA1303_DAC_DN_CONV_MASK,
+ SMA1303_DAC_DN_CONV_DISABLE,
+ NULL);
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_LEFTPOL_MASK,
+ SMA1303_LOW_FIRST_CH,
+ NULL);
+ break;
+
+ case 192000:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A2_TOP_MAN1,
+ SMA1303_DAC_DN_CONV_MASK,
+ SMA1303_DAC_DN_CONV_ENABLE,
+ NULL);
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_LEFTPOL_MASK,
+ SMA1303_HIGH_FIRST_CH,
+ NULL);
+ break;
+
+ default:
+ dev_err(component->dev, "%s not support rate : %d\n",
+ __func__, params_rate(params));
+
+ return -EINVAL;
+ }
+
+ } else {
+
+ switch (params_format(params)) {
+
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dev_dbg(component->dev,
+ "%s set format SNDRV_PCM_FORMAT_S16_LE\n",
+ __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_SCK_RATE_MASK,
+ SMA1303_SCK_32FS,
+ NULL);
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ dev_dbg(component->dev,
+ "%s set format SNDRV_PCM_FORMAT_S24_LE\n",
+ __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_SCK_RATE_MASK,
+ SMA1303_SCK_64FS,
+ NULL);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dev_dbg(component->dev,
+ "%s set format SNDRV_PCM_FORMAT_S32_LE\n",
+ __func__);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_SCK_RATE_MASK,
+ SMA1303_SCK_64FS,
+ NULL);
+ break;
+ default:
+ dev_err(component->dev,
+ "%s not support data bit : %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+ }
+
+ switch (sma1303->format) {
+ case SND_SOC_DAIFMT_I2S:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_I2S_MODE_MASK,
+ SMA1303_STANDARD_I2S,
+ NULL);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_O_FORMAT_MASK,
+ SMA1303_O_FMT_I2S,
+ NULL);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_I2S_MODE_MASK,
+ SMA1303_LJ,
+ NULL);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_O_FORMAT_MASK,
+ SMA1303_O_FMT_LJ,
+ NULL);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 16:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_I2S_MODE_MASK,
+ SMA1303_RJ_16BIT,
+ NULL);
+ break;
+ case 24:
+ case 32:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_I2S_MODE_MASK,
+ SMA1303_RJ_24BIT,
+ NULL);
+ break;
+ }
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_I2S_MODE_MASK,
+ SMA1303_STANDARD_I2S,
+ NULL);
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_O_FORMAT_MASK,
+ SMA1303_O_FMT_TDM,
+ NULL);
+ break;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ dev_err(component->dev,
+ "%s not support data bit : %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+ if (ret < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int sma1303_dai_set_sysclk_amp(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+
+ switch (clk_id) {
+ case SMA1303_EXTERNAL_CLOCK_19_2:
+ break;
+ case SMA1303_EXTERNAL_CLOCK_24_576:
+ break;
+ case SMA1303_PLL_CLKIN_MCLK:
+ break;
+ case SMA1303_PLL_CLKIN_BCLK:
+ break;
+ default:
+ dev_err(component->dev, "Invalid clk id: %d\n", clk_id);
+ return -EINVAL;
+ }
+ sma1303->sys_clk_id = clk_id;
+ return 0;
+}
+
+static int sma1303_dai_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ if (stream == SNDRV_PCM_STREAM_CAPTURE)
+ return ret;
+
+ if (mute) {
+ dev_dbg(component->dev, "%s : %s\n", __func__, "MUTE");
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_0E_MUTE_VOL_CTRL,
+ SMA1303_SPK_MUTE_MASK,
+ SMA1303_SPK_MUTE,
+ NULL);
+
+ /* Need to wait time for mute slope */
+ msleep(55);
+ } else {
+ if (!sma1303->force_mute_status) {
+ dev_dbg(component->dev, "%s : %s\n",
+ __func__, "UNMUTE");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_0E_MUTE_VOL_CTRL,
+ SMA1303_SPK_MUTE_MASK,
+ SMA1303_SPK_UNMUTE,
+ NULL);
+ } else {
+ dev_dbg(sma1303->dev,
+ "%s : FORCE MUTE!!!\n", __func__);
+ }
+ }
+
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int sma1303_dai_set_fmt_amp(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dev_dbg(component->dev,
+ "%s : %s\n", __func__, "I2S/TDM Device mode");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_CONTROLLER_DEVICE_MASK,
+ SMA1303_DEVICE_MODE,
+ NULL);
+ break;
+
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dev_dbg(component->dev,
+ "%s : %s\n", __func__, "I2S/TDM Controller mode");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_CONTROLLER_DEVICE_MASK,
+ SMA1303_CONTROLLER_MODE,
+ NULL);
+ break;
+
+ default:
+ dev_err(component->dev,
+ "Unsupported Controller/Device : 0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ sma1303->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ dev_err(component->dev,
+ "Unsupported Audio Interface Format : 0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+
+ case SND_SOC_DAIFMT_IB_NF:
+ dev_dbg(component->dev, "%s : %s\n",
+ __func__, "Invert BCLK + Normal Frame");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_SCK_RISING_MASK,
+ SMA1303_SCK_RISING_EDGE,
+ NULL);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dev_dbg(component->dev, "%s : %s\n",
+ __func__, "Invert BCLK + Invert Frame");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_LEFTPOL_MASK|SMA1303_SCK_RISING_MASK,
+ SMA1303_HIGH_FIRST_CH|SMA1303_SCK_RISING_EDGE,
+ NULL);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dev_dbg(component->dev, "%s : %s\n",
+ __func__, "Normal BCLK + Invert Frame");
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_01_INPUT1_CTRL1,
+ SMA1303_LEFTPOL_MASK,
+ SMA1303_HIGH_FIRST_CH,
+ NULL);
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ dev_dbg(component->dev, "%s : %s\n",
+ __func__, "Normal BCLK + Normal Frame");
+ break;
+ default:
+ dev_err(component->dev,
+ "Unsupported Bit & Frameclock : 0x%x\n", fmt);
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+}
+
+static int sma1303_dai_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ dev_dbg(component->dev, "%s : slots = %d, slot_width - %d\n",
+ __func__, slots, slot_width);
+
+ sma1303->frame_size = slot_width * slots;
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A4_TOP_MAN3,
+ SMA1303_O_FORMAT_MASK,
+ SMA1303_O_FMT_TDM,
+ NULL);
+
+ switch (slot_width) {
+ case 16:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2,
+ SMA1303_TDM_DL_MASK,
+ SMA1303_TDM_DL_16,
+ NULL);
+ break;
+ case 32:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2,
+ SMA1303_TDM_DL_MASK,
+ SMA1303_TDM_DL_32,
+ NULL);
+ break;
+ default:
+ dev_err(component->dev, "%s not support TDM %d slot_width\n",
+ __func__, slot_width);
+ break;
+ }
+
+ switch (slots) {
+ case 4:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2,
+ SMA1303_TDM_N_SLOT_MASK,
+ SMA1303_TDM_N_SLOT_4,
+ NULL);
+ break;
+ case 8:
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2,
+ SMA1303_TDM_N_SLOT_MASK,
+ SMA1303_TDM_N_SLOT_8,
+ NULL);
+ break;
+ default:
+ dev_err(component->dev, "%s not support TDM %d slots\n",
+ __func__, slots);
+ break;
+ }
+
+ if (sma1303->tdm_slot_rx < slots)
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A5_TDM1,
+ SMA1303_TDM_SLOT1_RX_POS_MASK,
+ (sma1303->tdm_slot_rx) << 3,
+ NULL);
+ else
+ dev_err(component->dev, "%s Incorrect tdm-slot-rx %d set\n",
+ __func__, sma1303->tdm_slot_rx);
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A5_TDM1,
+ SMA1303_TDM_CLK_POL_MASK,
+ SMA1303_TDM_CLK_POL_RISE,
+ NULL);
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A5_TDM1,
+ SMA1303_TDM_TX_MODE_MASK,
+ SMA1303_TDM_TX_MONO,
+ NULL);
+
+ if (sma1303->tdm_slot_tx < slots)
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_A6_TDM2,
+ SMA1303_TDM_SLOT1_TX_POS_MASK,
+ (sma1303->tdm_slot_tx) << 3,
+ NULL);
+ else
+ dev_err(component->dev, "%s Incorrect tdm-slot-tx %d set\n",
+ __func__, sma1303->tdm_slot_tx);
+
+ if (ret < 0)
+ return -EINVAL;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops sma1303_dai_ops_amp = {
+ .set_sysclk = sma1303_dai_set_sysclk_amp,
+ .set_fmt = sma1303_dai_set_fmt_amp,
+ .hw_params = sma1303_dai_hw_params_amp,
+ .mute_stream = sma1303_dai_mute,
+ .set_tdm_slot = sma1303_dai_set_tdm_slot,
+};
+
+#define SMA1303_RATES SNDRV_PCM_RATE_8000_192000
+#define SMA1303_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver sma1303_dai[] = {
+ {
+ .name = "sma1303-amplifier",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SMA1303_RATES,
+ .formats = SMA1303_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SMA1303_RATES,
+ .formats = SMA1303_FORMATS,
+ },
+ .ops = &sma1303_dai_ops_amp,
+ },
+};
+
+static void sma1303_check_fault_worker(struct work_struct *work)
+{
+ struct sma1303_priv *sma1303 =
+ container_of(work, struct sma1303_priv, check_fault_work.work);
+ int ret = 0;
+ unsigned int over_temp, ocp_val, uvlo_val;
+
+ if (sma1303->tsdw_cnt)
+ ret = sma1303_regmap_read(sma1303,
+ SMA1303_0A_SPK_VOL, &sma1303->cur_vol);
+ else
+ ret = sma1303_regmap_read(sma1303,
+ SMA1303_0A_SPK_VOL, &sma1303->init_vol);
+
+ if (ret != 0) {
+ dev_err(sma1303->dev,
+ "failed to read SMA1303_0A_SPK_VOL : %d\n", ret);
+ return;
+ }
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_FA_STATUS1, &over_temp);
+ if (ret != 0) {
+ dev_err(sma1303->dev,
+ "failed to read SMA1303_FA_STATUS1 : %d\n", ret);
+ return;
+ }
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_FB_STATUS2, &ocp_val);
+ if (ret != 0) {
+ dev_err(sma1303->dev,
+ "failed to read SMA1303_FB_STATUS2 : %d\n", ret);
+ return;
+ }
+
+ ret = sma1303_regmap_read(sma1303, SMA1303_FF_DEVICE_INDEX, &uvlo_val);
+ if (ret != 0) {
+ dev_err(sma1303->dev,
+ "failed to read SMA1303_FF_DEVICE_INDEX : %d\n", ret);
+ return;
+ }
+
+ if (~over_temp & SMA1303_OT1_OK_STATUS) {
+ dev_crit(sma1303->dev,
+ "%s : OT1(Over Temperature Level 1)\n", __func__);
+
+ if ((sma1303->cur_vol + 6) <= 0xFF)
+ sma1303_regmap_write(sma1303,
+ SMA1303_0A_SPK_VOL, sma1303->cur_vol + 6);
+
+ sma1303->tsdw_cnt++;
+ } else if (sma1303->tsdw_cnt) {
+ sma1303_regmap_write(sma1303,
+ SMA1303_0A_SPK_VOL, sma1303->init_vol);
+ sma1303->tsdw_cnt = 0;
+ sma1303->cur_vol = sma1303->init_vol;
+ }
+
+ if (~over_temp & SMA1303_OT2_OK_STATUS) {
+ dev_crit(sma1303->dev,
+ "%s : OT2(Over Temperature Level 2)\n", __func__);
+ }
+ if (ocp_val & SMA1303_OCP_SPK_STATUS) {
+ dev_crit(sma1303->dev,
+ "%s : OCP_SPK(Over Current Protect SPK)\n", __func__);
+ }
+ if (ocp_val & SMA1303_OCP_BST_STATUS) {
+ dev_crit(sma1303->dev,
+ "%s : OCP_BST(Over Current Protect Boost)\n", __func__);
+ }
+ if ((ocp_val & SMA1303_CLK_MON_STATUS) && (sma1303->amp_power_status)) {
+ dev_crit(sma1303->dev,
+ "%s : CLK_FAULT(No clock input)\n", __func__);
+ }
+ if (uvlo_val & SMA1303_UVLO_BST_STATUS) {
+ dev_crit(sma1303->dev,
+ "%s : UVLO(Under Voltage Lock Out)\n", __func__);
+ }
+
+ if ((over_temp != sma1303->last_over_temp) ||
+ (ocp_val != sma1303->last_ocp_val)) {
+
+ dev_crit(sma1303->dev, "Please check AMP status");
+ dev_dbg(sma1303->dev, "STATUS1=0x%02X : STATUS2=0x%02X\n",
+ over_temp, ocp_val);
+ sma1303->last_over_temp = over_temp;
+ sma1303->last_ocp_val = ocp_val;
+ }
+
+ if (sma1303->check_fault_status) {
+ if (sma1303->check_fault_period > 0)
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ sma1303->check_fault_period * HZ);
+ else
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ CHECK_PERIOD_TIME * HZ);
+ }
+
+ if (!(~over_temp & SMA1303_OT1_OK_STATUS)
+ && !(~over_temp & SMA1303_OT2_OK_STATUS)
+ && !(ocp_val & SMA1303_OCP_SPK_STATUS)
+ && !(ocp_val & SMA1303_OCP_BST_STATUS)
+ && !(ocp_val & SMA1303_CLK_MON_STATUS)
+ && !(uvlo_val & SMA1303_UVLO_BST_STATUS)) {
+ }
+}
+
+static int sma1303_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static void sma1303_remove(struct snd_soc_component *component)
+{
+ struct sma1303_priv *sma1303 = snd_soc_component_get_drvdata(component);
+
+ cancel_delayed_work_sync(&sma1303->check_fault_work);
+}
+
+static const struct snd_soc_component_driver sma1303_component = {
+ .probe = sma1303_probe,
+ .remove = sma1303_remove,
+ .controls = sma1303_snd_controls,
+ .num_controls = ARRAY_SIZE(sma1303_snd_controls),
+ .dapm_widgets = sma1303_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sma1303_dapm_widgets),
+ .dapm_routes = sma1303_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(sma1303_audio_map),
+};
+
+const struct regmap_config sma_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = SMA1303_FF_DEVICE_INDEX,
+ .readable_reg = sma1303_readable_register,
+ .writeable_reg = sma1303_writeable_register,
+ .volatile_reg = sma1303_volatile_register,
+
+ .cache_type = REGCACHE_NONE,
+ .reg_defaults = sma1303_reg_def,
+ .num_reg_defaults = ARRAY_SIZE(sma1303_reg_def),
+};
+
+static ssize_t check_fault_period_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%ld\n", sma1303->check_fault_period);
+}
+
+static ssize_t check_fault_period_store(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = kstrtol(buf, 10, &sma1303->check_fault_period);
+
+ if (ret)
+ return -EINVAL;
+
+ return (ssize_t)count;
+}
+
+static DEVICE_ATTR_RW(check_fault_period);
+
+static ssize_t check_fault_status_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%ld\n", sma1303->check_fault_status);
+}
+
+static ssize_t check_fault_status_store(struct device *dev,
+ struct device_attribute *devattr, const char *buf, size_t count)
+{
+ struct sma1303_priv *sma1303 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = kstrtol(buf, 10, &sma1303->check_fault_status);
+
+ if (ret)
+ return -EINVAL;
+
+ if (sma1303->check_fault_status) {
+ if (sma1303->check_fault_period > 0)
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ sma1303->check_fault_period * HZ);
+ else
+ queue_delayed_work(system_freezable_wq,
+ &sma1303->check_fault_work,
+ CHECK_PERIOD_TIME * HZ);
+ }
+
+ return (ssize_t)count;
+}
+
+static DEVICE_ATTR_RW(check_fault_status);
+
+static struct attribute *sma1303_attr[] = {
+ &dev_attr_check_fault_period.attr,
+ &dev_attr_check_fault_status.attr,
+ NULL,
+};
+
+static struct attribute_group sma1303_attr_group = {
+ .attrs = sma1303_attr,
+};
+
+static int sma1303_i2c_probe(struct i2c_client *client)
+{
+ struct sma1303_priv *sma1303;
+ int ret, i = 0;
+ unsigned int device_info, status, otp_stat;
+
+ sma1303 = devm_kzalloc(&client->dev,
+ sizeof(struct sma1303_priv), GFP_KERNEL);
+ if (!sma1303)
+ return -ENOMEM;
+ sma1303->dev = &client->dev;
+
+ sma1303->regmap = devm_regmap_init_i2c(client, &sma_i2c_regmap);
+ if (IS_ERR(sma1303->regmap)) {
+ ret = PTR_ERR(sma1303->regmap);
+ dev_err(&client->dev,
+ "Failed to allocate register map: %d\n", ret);
+
+ return ret;
+ }
+
+ ret = sma1303_regmap_read(sma1303,
+ SMA1303_FF_DEVICE_INDEX, &device_info);
+
+ if ((ret != 0) || ((device_info & 0xF8) != SMA1303_DEVICE_ID)) {
+ dev_err(&client->dev, "device initialization error (%d 0x%02X)",
+ ret, device_info);
+ }
+ dev_dbg(&client->dev, "chip version 0x%02X\n", device_info);
+
+ ret += sma1303_regmap_update_bits(sma1303,
+ SMA1303_00_SYSTEM_CTRL,
+ SMA1303_RESETBYI2C_MASK, SMA1303_RESETBYI2C_RESET,
+ NULL);
+
+ ret += sma1303_regmap_read(sma1303, SMA1303_FF_DEVICE_INDEX, &status);
+ sma1303->rev_num = status & SMA1303_REV_NUM_STATUS;
+ if (sma1303->rev_num == SMA1303_REV_NUM_TV0)
+ dev_dbg(&client->dev, "SMA1303 Trimming Version 0\n");
+ else if (sma1303->rev_num == SMA1303_REV_NUM_TV1)
+ dev_dbg(&client->dev, "SMA1303 Trimming Version 1\n");
+
+ ret += sma1303_regmap_read(sma1303, SMA1303_FB_STATUS2, &otp_stat);
+ if (ret < 0)
+ dev_err(&client->dev,
+ "failed to read, register: %02X, ret: %d\n",
+ SMA1303_FF_DEVICE_INDEX, ret);
+
+ if (((sma1303->rev_num == SMA1303_REV_NUM_TV0) &&
+ ((otp_stat & 0x0E) == SMA1303_OTP_STAT_OK_0)) ||
+ ((sma1303->rev_num != SMA1303_REV_NUM_TV0) &&
+ ((otp_stat & 0x0C) == SMA1303_OTP_STAT_OK_1)))
+ dev_dbg(&client->dev, "SMA1303 OTP Status Successful\n");
+ else
+ dev_dbg(&client->dev, "SMA1303 OTP Status Fail\n");
+
+ for (i = 0; i < (unsigned int)ARRAY_SIZE(sma1303_reg_def); i++)
+ ret += sma1303_regmap_write(sma1303,
+ sma1303_reg_def[i].reg,
+ sma1303_reg_def[i].def);
+
+ sma1303->amp_mode = SMA1303_MONO;
+ sma1303->amp_power_status = false;
+ sma1303->check_fault_period = CHECK_PERIOD_TIME;
+ sma1303->check_fault_status = true;
+ sma1303->force_mute_status = false;
+ sma1303->init_vol = 0x31;
+ sma1303->cur_vol = sma1303->init_vol;
+ sma1303->last_bclk = 0;
+ sma1303->last_ocp_val = 0x08;
+ sma1303->last_over_temp = 0xC0;
+ sma1303->tsdw_cnt = 0;
+ sma1303->retry_cnt = SMA1303_I2C_RETRY_COUNT;
+ sma1303->tdm_slot_rx = 0;
+ sma1303->tdm_slot_tx = 0;
+ sma1303->sys_clk_id = SMA1303_PLL_CLKIN_BCLK;
+
+ sma1303->dev = &client->dev;
+ sma1303->kobj = &client->dev.kobj;
+
+ INIT_DELAYED_WORK(&sma1303->check_fault_work,
+ sma1303_check_fault_worker);
+
+ i2c_set_clientdata(client, sma1303);
+
+ sma1303->pll_matches = sma1303_pll_matches;
+ sma1303->num_of_pll_matches =
+ ARRAY_SIZE(sma1303_pll_matches);
+
+ ret = devm_snd_soc_register_component(&client->dev,
+ &sma1303_component, sma1303_dai, 1);
+ if (ret) {
+ dev_err(&client->dev, "Failed to register component");
+
+ return ret;
+ }
+
+ sma1303->attr_grp = &sma1303_attr_group;
+ ret = sysfs_create_group(sma1303->kobj, sma1303->attr_grp);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to create attribute group [%d]\n", ret);
+ sma1303->attr_grp = NULL;
+ }
+
+ return ret;
+}
+
+static void sma1303_i2c_remove(struct i2c_client *client)
+{
+ struct sma1303_priv *sma1303 =
+ (struct sma1303_priv *) i2c_get_clientdata(client);
+
+ cancel_delayed_work_sync(&sma1303->check_fault_work);
+}
+
+static const struct i2c_device_id sma1303_i2c_id[] = {
+ {"sma1303", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, sma1303_i2c_id);
+
+static const struct of_device_id sma1303_of_match[] = {
+ { .compatible = "irondevice,sma1303", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sma1303_of_match);
+
+static struct i2c_driver sma1303_i2c_driver = {
+ .driver = {
+ .name = "sma1303",
+ .of_match_table = sma1303_of_match,
+ },
+ .probe_new = sma1303_i2c_probe,
+ .remove = sma1303_i2c_remove,
+ .id_table = sma1303_i2c_id,
+};
+
+module_i2c_driver(sma1303_i2c_driver);
+
+MODULE_DESCRIPTION("ALSA SoC SMA1303 driver");
+MODULE_AUTHOR("Gyuhwa Park, <gyuhwa.park@irondevice.com>");
+MODULE_AUTHOR("Kiseok Jo, <kiseok.jo@irondevice.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/sma1303.h b/sound/soc/codecs/sma1303.h
new file mode 100644
index 00000000000000..ae70f207adde58
--- /dev/null
+++ b/sound/soc/codecs/sma1303.h
@@ -0,0 +1,609 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * sma1303.h -- sma1303 ALSA SoC Audio driver
+ *
+ * Copyright 2023 Iron Device Corporation
+ *
+ * Author: Kiseok Jo <kiseok.jo@irondevice.com>
+ *
+ */
+
+#ifndef _SMA1303_H
+#define _SMA1303_H
+
+#define SMA1303_I2C_ADDR_00 0x1e
+#define SMA1303_I2C_ADDR_01 0x3e
+#define SMA1303_I2C_ADDR_10 0x5e
+#define SMA1303_I2C_ADDR_11 0x7e
+
+#define SMA1303_EXTERNAL_CLOCK_19_2 0x00
+#define SMA1303_EXTERNAL_CLOCK_24_576 0x01
+#define SMA1303_PLL_CLKIN_MCLK 0x02
+#define SMA1303_PLL_CLKIN_BCLK 0x03
+
+#define SMA1303_MONO 0x00
+#define SMA1303_STEREO 0x01
+
+#define SMA1303_I2C_RETRY_COUNT 3
+
+/*
+ * SMA1303 Register Definition
+ */
+
+/* SMA1303 Register Addresses */
+#define SMA1303_00_SYSTEM_CTRL 0x00
+#define SMA1303_01_INPUT1_CTRL1 0x01
+#define SMA1303_02_INPUT1_CTRL2 0x02
+#define SMA1303_03_INPUT1_CTRL3 0x03
+#define SMA1303_04_INPUT1_CTRL4 0x04
+/* 0x05 ~ 0x08 : Reserved */
+#define SMA1303_09_OUTPUT_CTRL 0x09
+#define SMA1303_0A_SPK_VOL 0x0a
+#define SMA1303_0B_BST_TEST 0x0b
+#define SMA1303_0C_BST_TEST1 0x0c
+#define SMA1303_0D_SPK_TEST 0x0d
+#define SMA1303_0E_MUTE_VOL_CTRL 0x0e
+/* 0x0F : Reserved */
+#define SMA1303_10_SYSTEM_CTRL1 0x10
+#define SMA1303_11_SYSTEM_CTRL2 0x11
+#define SMA1303_12_SYSTEM_CTRL3 0x12
+/* 0x13 : Reserved */
+#define SMA1303_14_MODULATOR 0x14
+#define SMA1303_15_BASS_SPK1 0x15
+#define SMA1303_16_BASS_SPK2 0x16
+#define SMA1303_17_BASS_SPK3 0x17
+#define SMA1303_18_BASS_SPK4 0x18
+#define SMA1303_19_BASS_SPK5 0x19
+#define SMA1303_1A_BASS_SPK6 0x1a
+#define SMA1303_1B_BASS_SPK7 0x1b
+/* 0x1C ~ 0x22 : Reserved */
+#define SMA1303_23_COMP_LIM1 0x23
+#define SMA1303_24_COMP_LIM2 0x24
+#define SMA1303_25_COMP_LIM3 0x25
+#define SMA1303_26_COMP_LIM4 0x26
+/* 0x27 ~ 0x32 : Reserved */
+#define SMA1303_33_SDM_CTRL 0x33
+#define SMA1303_34_OTP_DATA1 0x34
+/* 0x35 : Reserved */
+#define SMA1303_36_PROTECTION 0x36
+#define SMA1303_37_SLOPE_CTRL 0x37
+#define SMA1303_38_OTP_TRM0 0x38
+/* 0x39 ~ 0x3A : Reserved */
+#define SMA1303_3B_TEST1 0x3b
+#define SMA1303_3C_TEST2 0x3c
+#define SMA1303_3D_TEST3 0x3d
+#define SMA1303_3E_ATEST1 0x3e
+#define SMA1303_3F_ATEST2 0x3f
+/* 0x40 ~ 0x8A : Reserved */
+#define SMA1303_8B_PLL_POST_N 0x8b
+#define SMA1303_8C_PLL_N 0x8c
+#define SMA1303_8D_PLL_A_SETTING 0x8d
+#define SMA1303_8E_PLL_CTRL 0x8e
+#define SMA1303_8F_PLL_P_CP 0x8f
+#define SMA1303_90_POSTSCALER 0x90
+#define SMA1303_91_CLASS_G_CTRL 0x91
+#define SMA1303_92_FDPEC_CTRL 0x92
+/* 0x93 : Reserved */
+#define SMA1303_94_BOOST_CTRL1 0x94
+#define SMA1303_95_BOOST_CTRL2 0x95
+#define SMA1303_96_BOOST_CTRL3 0x96
+#define SMA1303_97_BOOST_CTRL4 0x97
+/* 0x98 ~ 0x9F : Reserved */
+#define SMA1303_A0_PAD_CTRL0 0xa0
+#define SMA1303_A1_PAD_CTRL1 0xa1
+#define SMA1303_A2_TOP_MAN1 0xa2
+#define SMA1303_A3_TOP_MAN2 0xa3
+#define SMA1303_A4_TOP_MAN3 0xa4
+#define SMA1303_A5_TDM1 0xa5
+#define SMA1303_A6_TDM2 0xa6
+#define SMA1303_A7_CLK_MON 0xa7
+/* 0xA8 ~ 0xF9 : Reserved */
+#define SMA1303_FA_STATUS1 0xfa
+#define SMA1303_FB_STATUS2 0xfb
+/* 0xFC ~ 0xFE : Reserved */
+#define SMA1303_FF_DEVICE_INDEX 0xff
+
+/* SMA1303 Registers Bit Fields */
+
+/* SYSTEM_CTRL : 0x00 */
+#define SMA1303_RESETBYI2C_MASK (1<<1)
+#define SMA1303_RESETBYI2C_NORMAL (0<<1)
+#define SMA1303_RESETBYI2C_RESET (1<<1)
+
+#define SMA1303_POWER_MASK (1<<0)
+#define SMA1303_POWER_OFF (0<<0)
+#define SMA1303_POWER_ON (1<<0)
+
+/* INTPUT CTRL1 : 0x01 */
+#define SMA1303_CONTROLLER_DEVICE_MASK (1<<7)
+#define SMA1303_DEVICE_MODE (0<<7)
+#define SMA1303_CONTROLLER_MODE (1<<7)
+
+#define SMA1303_I2S_MODE_MASK (7<<4)
+#define SMA1303_STANDARD_I2S (0<<4)
+#define SMA1303_LJ (1<<4)
+#define SMA1303_RJ_16BIT (4<<4)
+#define SMA1303_RJ_18BIT (5<<4)
+#define SMA1303_RJ_20BIT (6<<4)
+#define SMA1303_RJ_24BIT (7<<4)
+
+#define SMA1303_LEFTPOL_MASK (1<<3)
+#define SMA1303_LOW_FIRST_CH (0<<3)
+#define SMA1303_HIGH_FIRST_CH (1<<3)
+
+#define SMA1303_SCK_RISING_MASK (1<<2)
+#define SMA1303_SCK_FALLING_EDGE (0<<2)
+#define SMA1303_SCK_RISING_EDGE (1<<2)
+
+/* INTPUT CTRL2 : 0x02 */
+#define SMA1303_IMODE_MASK (3<<6)
+#define SMA1303_I2S (0<<6)
+#define SMA1303_PCM_SHORT (1<<6)
+#define SMA1303_PCM_LONG (2<<6)
+
+#define RSMA1303_IGHT_FIRST_MASK (1<<5)
+#define SMA1303_LEFT_NORMAL (0<<5)
+#define SMA1303_RIGHT_INVERTED (1<<5)
+
+#define SMA1303_PCM_ALAW_MASK (1<<4)
+#define SMA1303_PCM_U_DECODING (0<<4)
+#define SMA1303_PCM_A_DECODING (1<<4)
+
+#define SMA1303_PCM_COMP_MASK (1<<3)
+#define SMA1303_PCM_LINEAR (0<<3)
+#define SMA1303_PCM_COMPANDING (1<<3)
+
+#define SMA1303_INPUTSEL_MASK (1<<2)
+#define SMA1303_PCM_8KHZ (0<<2)
+#define SMA1303_PCM_16KHZ (1<<2)
+
+#define SMA1303_PCM_STEREO_MASK (1<<1)
+#define SMA1303_PCM_MONO (0<<1)
+#define SMA1303_PCM_STEREO (1<<1)
+
+#define SMA1303_PCM_DL_MASK (1<<0)
+#define SMA1303_PCM_8BIT (0<<0)
+#define SMA1303_PCM_16BIT (1<<0)
+
+/* INTPUT CTRL3 : 0x03 */
+#define SMA1303_PCM_N_SLOT_MASK (15<<0)
+#define SMA1303_PCM_N_SLOT1 (0<<0)
+#define SMA1303_PCM_N_SLOT2 (1<<0)
+#define SMA1303_PCM_N_SLOT3 (2<<0)
+#define SMA1303_PCM_N_SLOT4 (3<<0)
+#define SMA1303_PCM_N_SLOT5 (4<<0)
+#define SMA1303_PCM_N_SLOT6 (5<<0)
+#define SMA1303_PCM_N_SLOT7 (6<<0)
+#define SMA1303_PCM_N_SLOT8 (7<<0)
+#define SMA1303_PCM_N_SLOT9 (8<<0)
+#define SMA1303_PCM_N_SLOT10 (9<<0)
+#define SMA1303_PCM_N_SLOT11 (10<<0)
+#define SMA1303_PCM_N_SLOT12 (11<<0)
+#define SMA1303_PCM_N_SLOT13 (12<<0)
+#define SMA1303_PCM_N_SLOT14 (13<<0)
+#define SMA1303_PCM_N_SLOT15 (14<<0)
+#define SMA1303_PCM_N_SLOT16 (15<<0)
+
+/* INTPUT CTRL4 : 0x04 */
+#define SMA1303_PCM1_SLOT_MASK (15<<4)
+#define SMA1303_PCM1_SLOT1 (0<<4)
+#define SMA1303_PCM1_SLOT2 (1<<4)
+#define SMA1303_PCM1_SLOT3 (2<<4)
+#define SMA1303_PCM1_SLOT4 (3<<4)
+#define SMA1303_PCM1_SLOT5 (4<<4)
+#define SMA1303_PCM1_SLOT6 (5<<4)
+#define SMA1303_PCM1_SLOT7 (6<<4)
+#define SMA1303_PCM1_SLOT8 (7<<4)
+#define SMA1303_PCM1_SLOT9 (8<<4)
+#define SMA1303_PCM1_SLOT10 (9<<4)
+#define SMA1303_PCM1_SLOT11 (10<<4)
+#define SMA1303_PCM1_SLOT12 (11<<4)
+#define SMA1303_PCM1_SLOT13 (12<<4)
+#define SMA1303_PCM1_SLOT14 (13<<4)
+#define SMA1303_PCM1_SLOT15 (14<<4)
+#define SMA1303_PCM1_SLOT16 (15<<4)
+
+#define SMA1303_PCM2_SLOT_MASK (15<<0)
+#define SMA1303_PCM2_SLOT1 (0<<0)
+#define SMA1303_PCM2_SLOT2 (1<<0)
+#define SMA1303_PCM2_SLOT3 (2<<0)
+#define SMA1303_PCM2_SLOT4 (3<<0)
+#define SMA1303_PCM2_SLOT5 (4<<0)
+#define SMA1303_PCM2_SLOT6 (5<<0)
+#define SMA1303_PCM2_SLOT7 (6<<0)
+#define SMA1303_PCM2_SLOT8 (7<<0)
+#define SMA1303_PCM2_SLOT9 (8<<0)
+#define SMA1303_PCM2_SLOT10 (9<<0)
+#define SMA1303_PCM2_SLOT11 (10<<0)
+#define SMA1303_PCM2_SLOT12 (11<<0)
+#define SMA1303_PCM2_SLOT13 (12<<0)
+#define SMA1303_PCM2_SLOT14 (13<<0)
+#define SMA1303_PCM2_SLOT15 (14<<0)
+#define SMA1303_PCM2_SLOT16 (15<<0)
+
+/* OUTPUT CTRL : 0x09 */
+#define SMA1303_PORT_CONFIG_MASK (3<<5)
+#define SMA1303_INPUT_PORT_ONLY (0<<5)
+#define SMA1303_OUTPUT_PORT_ENABLE (2<<5)
+
+#define SMA1303_PORT_OUT_SEL_MASK (7<<0)
+#define SMA1303_OUT_SEL_DISABLE (0<<0)
+#define SMA1303_FORMAT_CONVERTER (1<<0)
+#define SMA1303_MIXER_OUTPUT (2<<0)
+#define SMA1303_SPEAKER_PATH (3<<0)
+#define SMA1303_POSTSCALER_OUTPUT (4<<0)
+
+/* BST_TEST : 0x0B */
+#define SMA1303_BST_OFF_SLOPE_MASK (3<<6)
+#define SMA1303_BST_OFF_SLOPE_6_7ns (0<<6)
+#define SMA1303_BST_OFF_SLOPE_4_8ns (1<<6)
+#define SMA1303_BST_OFF_SLOPE_2_6ns (2<<6)
+#define SMA1303_BST_OFF_SLOPE_1_2ns (3<<6)
+
+#define SMA1303_OCP_TEST_MASK (1<<5)
+#define SMA1303_OCP_NORMAL_MODE (0<<5)
+#define SMA1303_OCP_TEST_MODE (1<<5)
+
+#define SMA1303_BST_FAST_LEBN_MASK (1<<4)
+#define SMA1303_BST_SHORT_LEB (0<<4)
+#define SMA1303_BST_LONG_LEB (1<<4)
+
+#define SMA1303_HIGH_PGAIN_MASK (1<<3)
+#define SMA1303_NORMAL_P_GAIN (0<<3)
+#define SMA1303_HIGH_P_GAIN (1<<3)
+
+#define SMA1303_VCOMP_MASK (1<<2)
+#define SMA1303_VCOMP_NORMAL_MODE (0<<2)
+#define SMA1303_VCOMP_V_MON_MODE (1<<2)
+
+#define SMA1303_PMOS_ON_MASK (1<<1)
+#define SMA1303_PMOS_NORMAL_MODE (0<<1)
+#define SMA1303_PMOS_TEST_MODE (1<<1)
+
+#define SMA1303_NMOS_ON_MASK (1<<0)
+#define SMA1303_NMOS_NORMAL_MODE (0<<0)
+#define SMA1303_NMOS_TEST_MODE (1<<0)
+
+/* BST_TEST1 : 0x0C */
+#define SMA1303_SET_OCP_H_MASK (3<<6)
+#define SMA1303_HIGH_OCP_4_5_LVL (0<<6)
+#define SMA1303_HIGH_OCP_3_2_LVL (1<<6)
+#define SMA1303_HIGH_OCP_2_1_LVL (2<<6)
+#define SMA1303_HIGH_OCP_0_9_LVL (3<<6)
+
+#define SMA1303_OCL_TEST_MASK (1<<5)
+#define SMA1303_OCL_NORMAL_MODE (0<<5)
+#define SMA1303_OCL_TEST_MODE (1<<5)
+
+#define SMA1303_LOOP_CHECK_MASK (1<<4)
+#define SMA1303_BST_LOOP_NORMAL_MODE (0<<4)
+#define SMA1303_BST_LOOP_CHECK_MODE (1<<4)
+
+#define SMA1303_EN_SH_PRT_MASK (1<<3)
+#define SMA1303_EN_SH_PRT_DISABLE (0<<3)
+#define SMA1303_EN_SH_PRT_ENABLE (1<<3)
+
+/* SPK_TEST : 0x0D */
+#define SMA1303_VREF_MON_MASK (1<<3)
+#define SMA1303_VREF_NORMAL_MODE (0<<3)
+#define SMA1303_VREF_V_MON_MODE (1<<3)
+
+#define SMA1303_SPK_OCP_DLYN_MASK (1<<2)
+#define SMA1303_SPK_OCP_LONG_DELAY (0<<2)
+#define SMA1303_SPK_OCP_NORMAL (1<<2)
+
+#define SMA1303_SPK_OFF_SLOPE_MASK (3<<0)
+#define SMA1303_SPK_OFF_SLOPE_SLOW (0<<0)
+#define SMA1303_SPK_OFF_SLOPE_FAST (3<<0)
+
+/* MUTE_VOL_CTRL : 0x0E */
+#define SMA1303_VOL_SLOPE_MASK (3<<6)
+#define SMA1303_VOL_SLOPE_OFF (0<<6)
+#define SMA1303_VOL_SLOPE_SLOW (1<<6)
+#define SMA1303_VOL_SLOPE_MID (2<<6)
+#define SMA1303_VOL_SLOPE_FAST (3<<6)
+
+#define SMA1303_MUTE_SLOPE_MASK (3<<4)
+#define SMA1303_MUTE_SLOPE_OFF (0<<4)
+#define SMA1303_MUTE_SLOPE_SLOW (1<<4)
+#define SMA1303_MUTE_SLOPE_MID (2<<4)
+#define SMA1303_MUTE_SLOPE_FAST (3<<4)
+
+#define SMA1303_SPK_MUTE_MASK (1<<0)
+#define SMA1303_SPK_UNMUTE (0<<0)
+#define SMA1303_SPK_MUTE (1<<0)
+
+/* SYSTEM_CTRL1 :0x10 */
+#define SMA1303_SPK_MODE_MASK (7<<2)
+#define SMA1303_SPK_OFF (0<<2)
+#define SMA1303_SPK_MONO (1<<2)
+#define SMA1303_SPK_STEREO (4<<2)
+
+/* SYSTEM_CTRL2 : 0x11 */
+#define SMA1303_SPK_BS_MASK (1<<6)
+#define SMA1303_SPK_BS_BYP (0<<6)
+#define SMA1303_SPK_BS_EN (1<<6)
+#define SMA1303_SPK_LIM_MASK (1<<5)
+#define SMA1303_SPK_LIM_BYP (0<<5)
+#define SMA1303_SPK_LIM_EN (1<<5)
+
+#define SMA1303_LR_DATA_SW_MASK (1<<4)
+#define SMA1303_LR_DATA_SW_NORMAL (0<<4)
+#define SMA1303_LR_DATA_SW_SWAP (1<<4)
+
+#define SMA1303_MONOMIX_MASK (1<<0)
+#define SMA1303_MONOMIX_OFF (0<<0)
+#define SMA1303_MONOMIX_ON (1<<0)
+
+/* SYSTEM_CTRL3 : 0x12 */
+#define SMA1303_INPUT_MASK (3<<6)
+#define SMA1303_INPUT_0_DB (0<<6)
+#define SMA1303_INPUT_M6_DB (1<<6)
+#define SMA1303_INPUT_M12_DB (2<<6)
+#define SMA1303_INPUT_INFI_DB (3<<6)
+#define SMA1303_INPUT_R_MASK (3<<4)
+#define SMA1303_INPUT_R_0_DB (0<<4)
+#define SMA1303_INPUT_R_M6_DB (1<<4)
+#define SMA1303_INPUT_R_M12_DB (2<<4)
+#define SMA1303_INPUT_R_INFI_DB (3<<4)
+
+/* Modulator : 0x14 */
+#define SMA1303_SPK_HYSFB_MASK (3<<6)
+#define SMA1303_HYSFB_625K (0<<6)
+#define SMA1303_HYSFB_414K (1<<6)
+#define SMA1303_HYSFB_297K (2<<6)
+#define SMA1303_HYSFB_226K (3<<6)
+#define SMA1303_SPK_BDELAY_MASK (63<<0)
+
+/* SDM CONTROL : 0x33 */
+#define SMA1303_SDM_Q_SEL_MASK (1<<2)
+#define SMA1303_QUART_SEL_1_DIV_4 (0<<2)
+#define SMA1303_QUART_SEL_1_DIV_8 (1<<2)
+
+/* OTP_DATA1 : 0x34 */
+#define SMA1303_OTP_LVL_MASK (1<<5)
+#define SMA1303_OTP_LVL_NORMAL (0<<5)
+#define SMA1303_OTP_LVL_LOW (1<<5)
+
+/* PROTECTION : 0x36 */
+#define SMA1303_EDGE_DIS_MASK (1<<7)
+#define SMA1303_EDGE_DIS_ENABLE (0<<7)
+#define SMA1303_EDGE_DIS_DISABLE (1<<7)
+
+#define SMA1303_SPK_OCP_DIS_MASK (1<<3)
+#define SMA1303_SPK_OCP_ENABLE (0<<3)
+#define SMA1303_SPK_OCP_DISABLE (1<<3)
+
+#define SMA1303_OCP_MODE_MASK (1<<2)
+#define SMA1303_AUTO_RECOVER (0<<2)
+#define SMA1303_SHUT_DOWN_PERMANENT (1<<2)
+
+#define SMA1303_OTP_MODE_MASK (3<<0)
+#define SMA1303_OTP_MODE_DISABLE (0<<0)
+#define SMA1303_IG_THR1_SHUT_THR2 (1<<0)
+#define SMA1303_REC_THR1_SHUT_THR2 (2<<0)
+#define SMA1303_SHUT_THR1_SHUT_THR2 (3<<0)
+
+/* TEST2 : 0x3C */
+#define SMA1303_SPK_HSDM_BP_MASK (1<<4)
+#define SMA1303_SPK_HSDM_ENABLE (0<<4)
+#define SMA1303_SPK_HSDM_BYPASS (1<<4)
+
+#define SMA1303_SDM_SYNC_DIS_MASK (1<<5)
+#define SMA1303_SDM_SYNC_NORMAL (0<<5)
+#define SMA1303_SDM_SYNC_DISABLE (1<<5)
+
+/* ATEST2 : 0x3F */
+#define SMA1303_SPK_OUT_FREQ_MASK (1<<2)
+#define SMA1303_SPK_OUT_FREQ_360K (0<<2)
+#define SMA1303_SPK_OUT_FREQ_410K (1<<2)
+
+#define SMA1303_LOW_POWER_MODE_MASK (1<<3)
+#define SMA1303_LOW_POWER_MODE_DISABLE (0<<3)
+#define SMA1303_LOW_POWER_MODE_ENABLE (1<<3)
+
+#define SMA1303_THERMAL_ADJUST_MASK (3<<5)
+#define SMA1303_THERMAL_150_110 (0<<5)
+#define SMA1303_THERMAL_160_120 (1<<5)
+#define SMA1303_THERMAL_140_100 (2<<5)
+
+#define SMA1303_FAST_OFF_DRIVE_SPK_MASK (1<<0)
+#define SMA1303_FAST_OFF_DRIVE_SPK_DISABLE (0<<0)
+#define SMA1303_FAST_OFF_DRIVE_SPK_ENABLE (1<<0)
+
+/* PLL_CTRL : 0x8E */
+#define SMA1303_TRM_LVL_MASK (1<<4)
+#define SMA1303_TRM_LVL_NORMAL (0<<4)
+#define SMA1303_TRM_LVL_LOW (1<<4)
+
+#define SMA1303_LOW_OCL_MODE_MASK (1<<3)
+#define SMA1303_LOW_OCL_MODE (0<<3)
+#define SMA1303_NORMAL_OCL_MODE (1<<3)
+
+#define SMA1303_PLL_PD2_MASK (7<<0)
+#define SMA1303_PLL_PD2 (7<<0)
+#define SMA1303_PLL_OPERATION2 (0<<0)
+
+/* POSTSCALER : 0x90 */
+#define SMA1303_BYP_POST_MASK (1<<0)
+#define SMA1303_EN_POST_SCALER (0<<0)
+#define SMA1303_BYP_POST_SCALER (1<<0)
+
+/* FDPEC CONTROL : 0x92 */
+#define SMA1303_FLT_VDD_GAIN_MASK (15<<4)
+#define SMA1303_FLT_VDD_GAIN_2P40 (0<<4)
+#define SMA1303_FLT_VDD_GAIN_2P45 (1<<4)
+#define SMA1303_FLT_VDD_GAIN_2P50 (2<<4)
+#define SMA1303_FLT_VDD_GAIN_2P55 (3<<4)
+#define SMA1303_FLT_VDD_GAIN_2P60 (4<<4)
+#define SMA1303_FLT_VDD_GAIN_2P65 (5<<4)
+#define SMA1303_FLT_VDD_GAIN_2P70 (6<<4)
+#define SMA1303_FLT_VDD_GAIN_2P75 (7<<4)
+#define SMA1303_FLT_VDD_GAIN_2P80 (8<<4)
+#define SMA1303_FLT_VDD_GAIN_2P85 (9<<4)
+#define SMA1303_FLT_VDD_GAIN_2P90 (10<<4)
+#define SMA1303_FLT_VDD_GAIN_2P95 (11<<4)
+#define SMA1303_FLT_VDD_GAIN_3P00 (12<<4)
+#define SMA1303_FLT_VDD_GAIN_3P05 (13<<4)
+#define SMA1303_FLT_VDD_GAIN_3P10 (14<<4)
+#define SMA1303_FLT_VDD_GAIN_3P15 (15<<4)
+
+#define SMA1303_DIS_FCHG_MASK (1<<2)
+#define SMA1303_EN_FAST_CHARGE (0<<2)
+#define SMA1303_DIS_FAST_CHARGE (1<<2)
+
+/* BOOST_CONTROL4 : 0x97 */
+#define SMA1303_TRM_VBST_MASK (7<<2)
+#define SMA1303_TRM_VBST_5P5 (0<<2)
+#define SMA1303_TRM_VBST_5P6 (1<<2)
+#define SMA1303_TRM_VBST_5P7 (2<<2)
+#define SMA1303_TRM_VBST_5P8 (3<<2)
+#define SMA1303_TRM_VBST_5P9 (4<<2)
+#define SMA1303_TRM_VBST_6P0 (5<<2)
+#define SMA1303_TRM_VBST_6P1 (6<<2)
+#define SMA1303_TRM_VBST_6P2 (7<<2)
+
+/* TOP_MAN1 : 0xA2 */
+#define SMA1303_PLL_LOCK_SKIP_MASK (1<<7)
+#define SMA1303_PLL_LOCK_ENABLE (0<<7)
+#define SMA1303_PLL_LOCK_DISABLE (1<<7)
+
+#define SMA1303_PLL_PD_MASK (1<<6)
+#define SMA1303_PLL_OPERATION (0<<6)
+#define SMA1303_PLL_PD (1<<6)
+
+#define SMA1303_PLL_DIV_MASK (3<<4)
+#define SMA1303_PLL_OUT (0<<4)
+#define SMA1303_PLL_OUT_2 (1<<4)
+#define SMA1303_PLL_OUT_4 (2<<4)
+#define SMA1303_PLL_OUT_8 (3<<4)
+
+#define SMA1303_PLL_REF_CLK_MASK (1<<3)
+#define SMA1303_PLL_REF_CLK1 (0<<3)
+#define SMA1303_PLL_SCK (1<<3)
+
+#define SMA1303_DAC_DN_CONV_MASK (1<<2)
+#define SMA1303_DAC_DN_CONV_DISABLE (0<<2)
+#define SMA1303_DAC_DN_CONV_ENABLE (1<<2)
+
+#define SMA1303_SDO_IO_MASK (1<<1)
+#define SMA1303_HIGH_Z_LRCK_H (0<<1)
+#define SMA1303_HIGH_Z_LRCK_L (1<<1)
+
+#define SMA1303_SDO_OUTPUT2_MASK (1<<0)
+#define SMA1303_SDO_NORMAL (0<<0)
+#define SMA1303_SDO_OUTPUT_ONLY (1<<0)
+
+/* TOP_MAN2 : 0xA3 */
+#define SMA1303_MON_OSC_PLL_MASK (1<<7)
+#define SMA1303_PLL_SDO (0<<7)
+#define SMA1303_OSC_SDO (1<<7)
+
+#define SMA1303_TEST_CLKO_EN_MASK (1<<6)
+#define SMA1303_NORMAL_SDO (0<<6)
+#define SMA1303_CLK_OUT_SDO (1<<6)
+
+#define SMA1303_SDO_OUTPUT_MASK (1<<3)
+#define SMA1303_NORMAL_OUT (0<<3)
+#define SMA1303_HIGH_Z_OUT (1<<3)
+
+#define SMA1303_CLOCK_MON_MASK (1<<1)
+#define SMA1303_CLOCK_MON (0<<1)
+#define SMA1303_CLOCK_NOT_MON (1<<1)
+
+#define SMA1303_OSC_PD_MASK (1<<0)
+#define SMA1303_NORMAL_OPERATION_OSC (0<<0)
+#define SMA1303_POWER_DOWN_OSC (1<<0)
+
+/* TOP_MAN3 0xA4 */
+#define SMA1303_O_FORMAT_MASK (7<<5)
+#define SMA1303_O_FMT_LJ (1<<5)
+#define SMA1303_O_FMT_I2S (2<<5)
+#define SMA1303_O_FMT_TDM (4<<5)
+
+#define SMA1303_SCK_RATE_MASK (1<<3)
+#define SMA1303_SCK_64FS (0<<3)
+#define SMA1303_SCK_32FS (2<<3)
+
+#define SMA1303_LRCK_POL_MASK (1<<0)
+#define SMA1303_L_VALID (0<<0)
+#define SMA1303_R_VALID (1<<0)
+
+/* TDM1 FORMAT : 0xA5 */
+#define SMA1303_TDM_CLK_POL_MASK (1<<7)
+#define SMA1303_TDM_CLK_POL_RISE (0<<7)
+#define SMA1303_TDM_CLK_POL_FALL (1<<7)
+
+#define SMA1303_TDM_TX_MODE_MASK (1<<6)
+#define SMA1303_TDM_TX_MONO (0<<6)
+#define SMA1303_TDM_TX_STEREO (1<<6)
+
+#define SMA1303_TDM_SLOT1_RX_POS_MASK (7<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_0 (0<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_1 (1<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_2 (2<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_3 (3<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_4 (4<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_5 (5<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_6 (6<<3)
+#define SMA1303_TDM_SLOT1_RX_POS_7 (7<<3)
+
+#define SMA1303_TDM_SLOT2_RX_POS_MASK (7<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_0 (0<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_1 (1<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_2 (2<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_3 (3<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_4 (4<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_5 (5<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_6 (6<<0)
+#define SMA1303_TDM_SLOT2_RX_POS_7 (7<<0)
+
+/* TDM2 FORMAT : 0xA6 */
+#define SMA1303_TDM_DL_MASK (1<<7)
+#define SMA1303_TDM_DL_16 (0<<7)
+#define SMA1303_TDM_DL_32 (1<<7)
+
+#define SMA1303_TDM_N_SLOT_MASK (1<<6)
+#define SMA1303_TDM_N_SLOT_4 (0<<6)
+#define SMA1303_TDM_N_SLOT_8 (1<<6)
+
+#define SMA1303_TDM_SLOT1_TX_POS_MASK (7<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_0 (0<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_1 (1<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_2 (2<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_3 (3<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_4 (4<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_5 (5<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_6 (6<<3)
+#define SMA1303_TDM_SLOT1_TX_POS_7 (7<<3)
+
+#define SMA1303_TDM_SLOT2_TX_POS_MASK (7<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_0 (0<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_1 (1<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_2 (2<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_3 (3<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_4 (4<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_5 (5<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_6 (6<<0)
+#define SMA1303_TDM_SLOT2_TX_POS_7 (7<<0)
+
+/* STATUS1 : 0xFA */
+#define SMA1303_OT1_OK_STATUS (1<<7)
+#define SMA1303_OT2_OK_STATUS (1<<6)
+
+/* STATUS2 : 0xFB */
+#define SMA1303_OCP_SPK_STATUS (1<<5)
+#define SMA1303_OCP_BST_STATUS (1<<4)
+#define SMA1303_OTP_STAT_OK_0 (5<<1)
+#define SMA1303_OTP_STAT_OK_1 (2<<2)
+
+#define SMA1303_CLK_MON_STATUS (1<<0)
+
+/* DEVICE_INFO : 0xFF */
+#define SMA1303_DEVICE_ID (2<<3)
+#define SMA1303_UVLO_BST_STATUS (1<<2)
+#define SMA1303_REV_NUM_STATUS (3<<0)
+#define SMA1303_REV_NUM_TV0 (0<<0)
+#define SMA1303_REV_NUM_TV1 (1<<0)
+
+#endif
diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c
index 3885c0bf0b01c1..de6d01c8fdd340 100644
--- a/sound/soc/codecs/tas5720.c
+++ b/sound/soc/codecs/tas5720.c
@@ -30,6 +30,7 @@
enum tas572x_type {
TAS5720,
+ TAS5720A_Q1,
TAS5722,
};
@@ -166,17 +167,26 @@ static int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai,
return -EINVAL;
}
- /* Enable manual TDM slot selection (instead of I2C ID based) */
- ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG,
- TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC);
- if (ret < 0)
- goto error_snd_soc_component_update_bits;
+ /*
+ * Enable manual TDM slot selection (instead of I2C ID based).
+ * This is not applicable to TAS5720A-Q1.
+ */
+ switch (tas5720->devtype) {
+ case TAS5720A_Q1:
+ break;
+ default:
+ ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL1_REG,
+ TAS5720_TDM_CFG_SRC, TAS5720_TDM_CFG_SRC);
+ if (ret < 0)
+ goto error_snd_soc_component_update_bits;
- /* Configure the TDM slot to process audio from */
- ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG,
- TAS5720_TDM_SLOT_SEL_MASK, first_slot);
- if (ret < 0)
- goto error_snd_soc_component_update_bits;
+ /* Configure the TDM slot to process audio from */
+ ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG,
+ TAS5720_TDM_SLOT_SEL_MASK, first_slot);
+ if (ret < 0)
+ goto error_snd_soc_component_update_bits;
+ break;
+ }
/* Configure TDM slot width. This is only applicable to TAS5722. */
switch (tas5720->devtype) {
@@ -199,13 +209,24 @@ error_snd_soc_component_update_bits:
return ret;
}
-static int tas5720_mute(struct snd_soc_dai *dai, int mute, int direction)
+static int tas5720_mute_soc_component(struct snd_soc_component *component, int mute)
{
- struct snd_soc_component *component = dai->component;
+ struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component);
+ unsigned int reg, mask;
int ret;
- ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG,
- TAS5720_MUTE, mute ? TAS5720_MUTE : 0);
+ switch (tas5720->devtype) {
+ case TAS5720A_Q1:
+ reg = TAS5720_Q1_VOLUME_CTRL_CFG_REG;
+ mask = TAS5720_Q1_MUTE;
+ break;
+ default:
+ reg = TAS5720_DIGITAL_CTRL2_REG;
+ mask = TAS5720_MUTE;
+ break;
+ }
+
+ ret = snd_soc_component_update_bits(component, reg, mask, mute ? mask : 0);
if (ret < 0) {
dev_err(component->dev, "error (un-)muting device: %d\n", ret);
return ret;
@@ -214,6 +235,11 @@ static int tas5720_mute(struct snd_soc_dai *dai, int mute, int direction)
return 0;
}
+static int tas5720_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ return tas5720_mute_soc_component(dai->component, mute);
+}
+
static void tas5720_fault_check_work(struct work_struct *work)
{
struct tas5720_data *tas5720 = container_of(work, struct tas5720_data,
@@ -305,6 +331,9 @@ static int tas5720_codec_probe(struct snd_soc_component *component)
case TAS5720:
expected_device_id = TAS5720_DEVICE_ID;
break;
+ case TAS5720A_Q1:
+ expected_device_id = TAS5720A_Q1_DEVICE_ID;
+ break;
case TAS5722:
expected_device_id = TAS5722_DEVICE_ID;
break;
@@ -318,8 +347,20 @@ static int tas5720_codec_probe(struct snd_soc_component *component)
expected_device_id, device_id);
/* Set device to mute */
- ret = snd_soc_component_update_bits(component, TAS5720_DIGITAL_CTRL2_REG,
- TAS5720_MUTE, TAS5720_MUTE);
+ ret = tas5720_mute_soc_component(component, 1);
+ if (ret < 0)
+ goto error_snd_soc_component_update_bits;
+
+ /* Set Bit 7 in TAS5720_ANALOG_CTRL_REG to 1 for TAS5720A_Q1 */
+ switch (tas5720->devtype) {
+ case TAS5720A_Q1:
+ ret = snd_soc_component_update_bits(component, TAS5720_ANALOG_CTRL_REG,
+ TAS5720_Q1_RESERVED7_BIT,
+ TAS5720_Q1_RESERVED7_BIT);
+ break;
+ default:
+ break;
+ }
if (ret < 0)
goto error_snd_soc_component_update_bits;
@@ -471,6 +512,15 @@ static const struct regmap_config tas5720_regmap_config = {
.volatile_reg = tas5720_is_volatile_reg,
};
+static const struct regmap_config tas5720a_q1_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = TAS5720_MAX_REG,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = tas5720_is_volatile_reg,
+};
+
static const struct regmap_config tas5722_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -492,6 +542,16 @@ static const DECLARE_TLV_DB_RANGE(dac_analog_tlv,
);
/*
+ * DAC analog gain for TAS5720A-Q1. There are three discrete values to select from, ranging
+ * from 19.2 dB to 25.0dB.
+ */
+static const DECLARE_TLV_DB_RANGE(dac_analog_tlv_a_q1,
+ 0x0, 0x0, TLV_DB_SCALE_ITEM(1920, 0, 0),
+ 0x1, 0x1, TLV_DB_SCALE_ITEM(2260, 0, 0),
+ 0x2, 0x2, TLV_DB_SCALE_ITEM(2500, 0, 0),
+);
+
+/*
* DAC digital volumes. From -103.5 to 24 dB in 0.5 dB or 0.25 dB steps
* depending on the device. Note that setting the gain below -100 dB
* (register value <0x7) is effectively a MUTE as per device datasheet.
@@ -537,6 +597,15 @@ static const struct snd_kcontrol_new tas5720_snd_controls[] = {
TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv),
};
+static const struct snd_kcontrol_new tas5720a_q1_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("Speaker Driver Playback Volume",
+ TAS5720_Q1_VOLUME_CTRL_LEFT_REG,
+ TAS5720_Q1_VOLUME_CTRL_RIGHT_REG,
+ 0, 0xff, 0, tas5720_dac_tlv),
+ SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG,
+ TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv_a_q1),
+};
+
static const struct snd_kcontrol_new tas5722_snd_controls[] = {
SOC_SINGLE_EXT_TLV("Speaker Driver Playback Volume",
0, 0, 511, 0,
@@ -574,6 +643,22 @@ static const struct snd_soc_component_driver soc_component_dev_tas5720 = {
.endianness = 1,
};
+static const struct snd_soc_component_driver soc_component_dev_tas5720_a_q1 = {
+ .probe = tas5720_codec_probe,
+ .remove = tas5720_codec_remove,
+ .suspend = tas5720_suspend,
+ .resume = tas5720_resume,
+ .controls = tas5720a_q1_snd_controls,
+ .num_controls = ARRAY_SIZE(tas5720a_q1_snd_controls),
+ .dapm_widgets = tas5720_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets),
+ .dapm_routes = tas5720_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
static const struct snd_soc_component_driver soc_component_dev_tas5722 = {
.probe = tas5720_codec_probe,
.remove = tas5720_codec_remove,
@@ -633,6 +718,7 @@ static struct snd_soc_dai_driver tas5720_dai[] = {
static const struct i2c_device_id tas5720_id[] = {
{ "tas5720", TAS5720 },
+ { "tas5720a-q1", TAS5720A_Q1 },
{ "tas5722", TAS5722 },
{ }
};
@@ -659,6 +745,9 @@ static int tas5720_probe(struct i2c_client *client)
case TAS5720:
regmap_config = &tas5720_regmap_config;
break;
+ case TAS5720A_Q1:
+ regmap_config = &tas5720a_q1_regmap_config;
+ break;
case TAS5722:
regmap_config = &tas5722_regmap_config;
break;
@@ -692,6 +781,12 @@ static int tas5720_probe(struct i2c_client *client)
tas5720_dai,
ARRAY_SIZE(tas5720_dai));
break;
+ case TAS5720A_Q1:
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_tas5720_a_q1,
+ tas5720_dai,
+ ARRAY_SIZE(tas5720_dai));
+ break;
case TAS5722:
ret = devm_snd_soc_register_component(&client->dev,
&soc_component_dev_tas5722,
@@ -713,6 +808,7 @@ static int tas5720_probe(struct i2c_client *client)
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id tas5720_of_match[] = {
{ .compatible = "ti,tas5720", },
+ { .compatible = "ti,tas5720a-q1", },
{ .compatible = "ti,tas5722", },
{ },
};
diff --git a/sound/soc/codecs/tas5720.h b/sound/soc/codecs/tas5720.h
index 223858f0de7136..54b59b05ef0a1a 100644
--- a/sound/soc/codecs/tas5720.h
+++ b/sound/soc/codecs/tas5720.h
@@ -10,7 +10,7 @@
#ifndef __TAS5720_H__
#define __TAS5720_H__
-/* Register Address Map */
+/* Register Address Map - first 3 regs are common for all variants */
#define TAS5720_DEVICE_ID_REG 0x00
#define TAS5720_POWER_CTRL_REG 0x01
#define TAS5720_DIGITAL_CTRL1_REG 0x02
@@ -27,7 +27,13 @@
#define TAS5722_ANALOG_CTRL2_REG 0x14
#define TAS5722_MAX_REG TAS5722_ANALOG_CTRL2_REG
+/* Register Address Map - volume controls for the TAS5720-Q1 variant */
+#define TAS5720_Q1_VOLUME_CTRL_CFG_REG 0x03
+#define TAS5720_Q1_VOLUME_CTRL_LEFT_REG 0x04
+#define TAS5720_Q1_VOLUME_CTRL_RIGHT_REG 0x05
+
/* TAS5720_DEVICE_ID_REG */
+#define TAS5720A_Q1_DEVICE_ID 0x00
#define TAS5720_DEVICE_ID 0x01
#define TAS5722_DEVICE_ID 0x12
@@ -53,6 +59,10 @@
#define TAS5720_MUTE BIT(4)
#define TAS5720_TDM_SLOT_SEL_MASK GENMASK(2, 0)
+/* TAS5720_Q1_VOLUME_CTRL_CFG_REG */
+#define TAS5720_Q1_FADE BIT(7)
+#define TAS5720_Q1_MUTE GENMASK(1, 0)
+
/* TAS5720_ANALOG_CTRL_REG */
#define TAS5720_PWM_RATE_6_3_FSYNC (0x0 << 4)
#define TAS5720_PWM_RATE_8_4_FSYNC (0x1 << 4)
@@ -70,6 +80,10 @@
#define TAS5720_ANALOG_GAIN_MASK GENMASK(3, 2)
#define TAS5720_ANALOG_GAIN_SHIFT (0x2)
+/* TAS5720_Q1_ANALOG_CTRL_REG */
+#define TAS5720_Q1_RESERVED7_BIT BIT(7)
+#define TAS5720_Q1_CHAN_SEL BIT(1)
+
/* TAS5720_FAULT_REG */
#define TAS5720_OC_THRESH_100PCT (0x0 << 4)
#define TAS5720_OC_THRESH_75PCT (0x1 << 4)
diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c
index 91a22d92791584..530f321d08e9c2 100644
--- a/sound/soc/codecs/tlv320adcx140.c
+++ b/sound/soc/codecs/tlv320adcx140.c
@@ -925,7 +925,7 @@ static int adcx140_configure_gpio(struct adcx140_priv *adcx140)
gpio_count = device_property_count_u32(adcx140->dev,
"ti,gpio-config");
- if (gpio_count == 0)
+ if (gpio_count <= 0)
return 0;
if (gpio_count != ADCX140_NUM_GPIO_CFGS)
diff --git a/sound/soc/codecs/tlv320adcx140.h b/sound/soc/codecs/tlv320adcx140.h
index fd80fac8b32749..27a1f1012fe24c 100644
--- a/sound/soc/codecs/tlv320adcx140.h
+++ b/sound/soc/codecs/tlv320adcx140.h
@@ -6,7 +6,9 @@
#define _TLV320ADCX140_H
#define ADCX140_RATES (SNDRV_PCM_RATE_44100 | \
- SNDRV_PCM_RATE_48000)
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000)
#define ADCX140_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c
index 2305a472d13214..5282112c7d8d16 100644
--- a/sound/soc/codecs/ts3a227e.c
+++ b/sound/soc/codecs/ts3a227e.c
@@ -258,7 +258,25 @@ int ts3a227e_enable_jack_detect(struct snd_soc_component *component,
}
EXPORT_SYMBOL_GPL(ts3a227e_enable_jack_detect);
-static struct snd_soc_component_driver ts3a227e_soc_driver;
+static int ts3a227e_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
+{
+ if (jack == NULL)
+ return -EINVAL;
+
+ return ts3a227e_enable_jack_detect(component, jack);
+}
+
+static int ts3a227e_get_jack_type(struct snd_soc_component *component)
+{
+ return SND_JACK_HEADSET;
+}
+
+static const struct snd_soc_component_driver ts3a227e_soc_driver = {
+ .name = "ti,ts3a227e",
+ .set_jack = ts3a227e_set_jack,
+ .get_jack_type = ts3a227e_get_jack_type,
+};
static const struct regmap_config ts3a227e_regmap_config = {
.val_bits = 8,
diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c
index 28175c746b9ae2..783479a4d535a1 100644
--- a/sound/soc/codecs/wcd934x.c
+++ b/sound/soc/codecs/wcd934x.c
@@ -4737,13 +4737,9 @@ static u32 wcd934x_get_dmic_sample_rate(struct snd_soc_component *comp,
if (dec_found && adc_mux_index <= 8) {
tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index);
tx_stream_fs = snd_soc_component_read(comp, tx_fs_reg) & 0x0F;
- if (tx_stream_fs <= 4) {
- if (wcd->dmic_sample_rate <=
- WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ)
- dmic_fs = wcd->dmic_sample_rate;
- else
- dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ;
- } else
+ if (tx_stream_fs <= 4)
+ dmic_fs = min(wcd->dmic_sample_rate, WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ);
+ else
dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
} else {
dmic_fs = wcd->dmic_sample_rate;
diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c
index 1bf3c06a2b6221..33d1b5ffeaeba7 100644
--- a/sound/soc/codecs/wcd938x-sdw.c
+++ b/sound/soc/codecs/wcd938x-sdw.c
@@ -191,7 +191,7 @@ static int wcd9380_interrupt_callback(struct sdw_slave *slave,
return IRQ_HANDLED;
}
-static struct sdw_slave_ops wcd9380_slave_ops = {
+static const struct sdw_slave_ops wcd9380_slave_ops = {
.update_status = wcd9380_update_status,
.interrupt_callback = wcd9380_interrupt_callback,
.bus_config = wcd9380_bus_config,
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
index 626278e4c92380..737ca82cf9764f 100644
--- a/sound/soc/codecs/wl1273.c
+++ b/sound/soc/codecs/wl1273.c
@@ -484,11 +484,6 @@ static int wl1273_platform_probe(struct platform_device *pdev)
&wl1273_dai, 1);
}
-static int wl1273_platform_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
MODULE_ALIAS("platform:wl1273-codec");
static struct platform_driver wl1273_platform_driver = {
@@ -496,7 +491,6 @@ static struct platform_driver wl1273_platform_driver = {
.name = "wl1273-codec",
},
.probe = wl1273_platform_probe,
- .remove = wl1273_platform_remove,
};
module_platform_driver(wl1273_platform_driver);
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index 8dac9fd8854705..8eb4782c923207 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -37,7 +37,9 @@
#include "wm8940.h"
struct wm8940_priv {
- unsigned int sysclk;
+ unsigned int mclk;
+ unsigned int fs;
+
struct regmap *regmap;
};
@@ -387,17 +389,24 @@ static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
+static int wm8940_update_clocks(struct snd_soc_dai *dai);
static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
+ struct wm8940_priv *priv = snd_soc_component_get_drvdata(component);
u16 iface = snd_soc_component_read(component, WM8940_IFACE) & 0xFD9F;
u16 addcntrl = snd_soc_component_read(component, WM8940_ADDCNTRL) & 0xFFF1;
u16 companding = snd_soc_component_read(component,
WM8940_COMPANDINGCTL) & 0xFFDF;
int ret;
+ priv->fs = params_rate(params);
+ ret = wm8940_update_clocks(dai);
+ if (ret)
+ return ret;
+
/* LoutR control */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE
&& params_channels(params) == 2)
@@ -611,24 +620,6 @@ static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return 0;
}
-static int wm8940_set_dai_sysclk(struct snd_soc_dai *codec_dai,
- int clk_id, unsigned int freq, int dir)
-{
- struct snd_soc_component *component = codec_dai->component;
- struct wm8940_priv *wm8940 = snd_soc_component_get_drvdata(component);
-
- switch (freq) {
- case 11289600:
- case 12000000:
- case 12288000:
- case 16934400:
- case 18432000:
- wm8940->sysclk = freq;
- return 0;
- }
- return -EINVAL;
-}
-
static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
int div_id, int div)
{
@@ -653,6 +644,78 @@ static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
return ret;
}
+static unsigned int wm8940_get_mclkdiv(unsigned int f_in, unsigned int f_out,
+ int *mclkdiv)
+{
+ unsigned int ratio = 2 * f_in / f_out;
+
+ if (ratio <= 2) {
+ *mclkdiv = WM8940_MCLKDIV_1;
+ ratio = 2;
+ } else if (ratio == 3) {
+ *mclkdiv = WM8940_MCLKDIV_1_5;
+ } else if (ratio == 4) {
+ *mclkdiv = WM8940_MCLKDIV_2;
+ } else if (ratio <= 6) {
+ *mclkdiv = WM8940_MCLKDIV_3;
+ ratio = 6;
+ } else if (ratio <= 8) {
+ *mclkdiv = WM8940_MCLKDIV_4;
+ ratio = 8;
+ } else if (ratio <= 12) {
+ *mclkdiv = WM8940_MCLKDIV_6;
+ ratio = 12;
+ } else if (ratio <= 16) {
+ *mclkdiv = WM8940_MCLKDIV_8;
+ ratio = 16;
+ } else {
+ *mclkdiv = WM8940_MCLKDIV_12;
+ ratio = 24;
+ }
+
+ return f_out * ratio / 2;
+}
+
+static int wm8940_update_clocks(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct wm8940_priv *priv = snd_soc_component_get_drvdata(codec);
+ unsigned int fs256;
+ unsigned int fpll = 0;
+ unsigned int f;
+ int mclkdiv;
+
+ if (!priv->mclk || !priv->fs)
+ return 0;
+
+ fs256 = 256 * priv->fs;
+
+ f = wm8940_get_mclkdiv(priv->mclk, fs256, &mclkdiv);
+ if (f != priv->mclk) {
+ /* The PLL performs best around 90MHz */
+ fpll = wm8940_get_mclkdiv(22500000, fs256, &mclkdiv);
+ }
+
+ wm8940_set_dai_pll(dai, 0, 0, priv->mclk, fpll);
+ wm8940_set_dai_clkdiv(dai, WM8940_MCLKDIV, mclkdiv);
+
+ return 0;
+}
+
+static int wm8940_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct wm8940_priv *priv = snd_soc_component_get_drvdata(codec);
+
+ if (dir != SND_SOC_CLOCK_IN)
+ return -EINVAL;
+
+ priv->mclk = freq;
+
+ return wm8940_update_clocks(dai);
+}
+
#define WM8940_RATES SNDRV_PCM_RATE_8000_48000
#define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
@@ -697,6 +760,17 @@ static int wm8940_probe(struct snd_soc_component *component)
int ret;
u16 reg;
+ /*
+ * Check chip ID for wm8940 - value of 0x00 offset
+ * SOFTWARE_RESET on write
+ * CHIP_ID on read
+ */
+ reg = snd_soc_component_read(component, WM8940_SOFTRESET);
+ if (reg != WM8940_CHIP_ID) {
+ dev_err(component->dev, "Wrong wm8940 chip ID: 0x%x\n", reg);
+ return -ENODEV;
+ }
+
ret = wm8940_reset(component);
if (ret < 0) {
dev_err(component->dev, "Failed to issue reset\n");
@@ -709,9 +783,7 @@ static int wm8940_probe(struct snd_soc_component *component)
if (ret < 0)
return ret;
- if (!pdata)
- dev_warn(component->dev, "No platform data supplied\n");
- else {
+ if (pdata) {
reg = snd_soc_component_read(component, WM8940_OUTPUTCTL);
ret = snd_soc_component_write(component, WM8940_OUTPUTCTL, reg | pdata->vroi);
if (ret < 0)
diff --git a/sound/soc/codecs/wm8940.h b/sound/soc/codecs/wm8940.h
index 0d4f53ada2e622..86bbf902ef5a2b 100644
--- a/sound/soc/codecs/wm8940.h
+++ b/sound/soc/codecs/wm8940.h
@@ -95,5 +95,8 @@ struct wm8940_setup_data {
#define WM8940_OPCLKDIV_3 2
#define WM8940_OPCLKDIV_4 3
+/* Chip ID */
+#define WM8940_CHIP_ID 0x8940
+
#endif /* _WM8940_H */
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
index 6c8b1db649b898..f709231b1277ac 100644
--- a/sound/soc/codecs/wsa881x.c
+++ b/sound/soc/codecs/wsa881x.c
@@ -5,10 +5,7 @@
#include <linux/bitops.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
-#include <linux/interrupt.h>
#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
@@ -424,7 +421,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA881X_MAX_SWR_PORTS] = {
}
};
-static struct sdw_port_config wsa881x_pconfig[WSA881X_MAX_SWR_PORTS] = {
+static const struct sdw_port_config wsa881x_pconfig[WSA881X_MAX_SWR_PORTS] = {
{
.num = 1,
.ch_mask = 0x1,
@@ -679,6 +676,11 @@ struct wsa881x_priv {
struct sdw_stream_runtime *sruntime;
struct sdw_port_config port_config[WSA881X_MAX_SWR_PORTS];
struct gpio_desc *sd_n;
+ /*
+ * Logical state for SD_N GPIO: high for shutdown, low for enable.
+ * For backwards compatibility.
+ */
+ unsigned int sd_n_val;
int version;
int active_ports;
bool port_prepared[WSA881X_MAX_SWR_PORTS];
@@ -1101,7 +1103,7 @@ static int wsa881x_bus_config(struct sdw_slave *slave,
return 0;
}
-static struct sdw_slave_ops wsa881x_slave_ops = {
+static const struct sdw_slave_ops wsa881x_slave_ops = {
.update_status = wsa881x_update_status,
.bus_config = wsa881x_bus_config,
.port_prep = wsa881x_port_prep,
@@ -1113,20 +1115,39 @@ static int wsa881x_probe(struct sdw_slave *pdev,
struct wsa881x_priv *wsa881x;
struct device *dev = &pdev->dev;
- wsa881x = devm_kzalloc(&pdev->dev, sizeof(*wsa881x), GFP_KERNEL);
+ wsa881x = devm_kzalloc(dev, sizeof(*wsa881x), GFP_KERNEL);
if (!wsa881x)
return -ENOMEM;
- wsa881x->sd_n = devm_gpiod_get_optional(&pdev->dev, "powerdown",
+ wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown",
GPIOD_FLAGS_BIT_NONEXCLUSIVE);
- if (IS_ERR(wsa881x->sd_n)) {
- dev_err(&pdev->dev, "Shutdown Control GPIO not found\n");
- return PTR_ERR(wsa881x->sd_n);
- }
+ if (IS_ERR(wsa881x->sd_n))
+ return dev_err_probe(dev, PTR_ERR(wsa881x->sd_n),
+ "Shutdown Control GPIO not found\n");
- dev_set_drvdata(&pdev->dev, wsa881x);
+ /*
+ * Backwards compatibility work-around.
+ *
+ * The SD_N GPIO is active low, however upstream DTS used always active
+ * high. Changing the flag in driver and DTS will break backwards
+ * compatibility, so add a simple value inversion to work with both old
+ * and new DTS.
+ *
+ * This won't work properly with DTS using the flags properly in cases:
+ * 1. Old DTS with proper ACTIVE_LOW, however such case was broken
+ * before as the driver required the active high.
+ * 2. New DTS with proper ACTIVE_HIGH (intended), which is rare case
+ * (not existing upstream) but possible. This is the price of
+ * backwards compatibility, therefore this hack should be removed at
+ * some point.
+ */
+ wsa881x->sd_n_val = gpiod_is_active_low(wsa881x->sd_n);
+ if (!wsa881x->sd_n_val)
+ dev_warn(dev, "Using ACTIVE_HIGH for shutdown GPIO. Your DTB might be outdated or you use unsupported configuration for the GPIO.");
+
+ dev_set_drvdata(dev, wsa881x);
wsa881x->slave = pdev;
- wsa881x->dev = &pdev->dev;
+ wsa881x->dev = dev;
wsa881x->sconfig.ch_count = 1;
wsa881x->sconfig.bps = 1;
wsa881x->sconfig.frame_rate = 48000;
@@ -1135,13 +1156,11 @@ static int wsa881x_probe(struct sdw_slave *pdev,
pdev->prop.sink_ports = GENMASK(WSA881X_MAX_SWR_PORTS, 0);
pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop;
pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
- gpiod_direction_output(wsa881x->sd_n, 1);
+ gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val);
wsa881x->regmap = devm_regmap_init_sdw(pdev, &wsa881x_regmap_config);
- if (IS_ERR(wsa881x->regmap)) {
- dev_err(&pdev->dev, "regmap_init failed\n");
- return PTR_ERR(wsa881x->regmap);
- }
+ if (IS_ERR(wsa881x->regmap))
+ return dev_err_probe(dev, PTR_ERR(wsa881x->regmap), "regmap_init failed\n");
pm_runtime_set_autosuspend_delay(dev, 3000);
pm_runtime_use_autosuspend(dev);
@@ -1149,7 +1168,7 @@ static int wsa881x_probe(struct sdw_slave *pdev,
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
- return devm_snd_soc_register_component(&pdev->dev,
+ return devm_snd_soc_register_component(dev,
&wsa881x_component_drv,
wsa881x_dais,
ARRAY_SIZE(wsa881x_dais));
@@ -1160,7 +1179,7 @@ static int __maybe_unused wsa881x_runtime_suspend(struct device *dev)
struct regmap *regmap = dev_get_regmap(dev, NULL);
struct wsa881x_priv *wsa881x = dev_get_drvdata(dev);
- gpiod_direction_output(wsa881x->sd_n, 0);
+ gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val);
regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
@@ -1175,13 +1194,13 @@ static int __maybe_unused wsa881x_runtime_resume(struct device *dev)
struct wsa881x_priv *wsa881x = dev_get_drvdata(dev);
unsigned long time;
- gpiod_direction_output(wsa881x->sd_n, 1);
+ gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val);
time = wait_for_completion_timeout(&slave->initialization_complete,
msecs_to_jiffies(WSA881X_PROBE_TIMEOUT));
if (!time) {
dev_err(dev, "Initialization not complete, timed out\n");
- gpiod_direction_output(wsa881x->sd_n, 0);
+ gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val);
return -ETIMEDOUT;
}
diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c
index 58fdb4e9fd978b..c609cb63dae6db 100644
--- a/sound/soc/codecs/wsa883x.c
+++ b/sound/soc/codecs/wsa883x.c
@@ -4,16 +4,12 @@
*/
#include <linux/bitops.h>
-#include <linux/debugfs.h>
-#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
-#include <linux/of_platform.h>
-#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/printk.h>
#include <linux/regmap.h>
@@ -522,7 +518,7 @@ static struct sdw_dpn_prop wsa_sink_dpn_prop[WSA883X_MAX_SWR_PORTS] = {
}
};
-static struct sdw_port_config wsa883x_pconfig[WSA883X_MAX_SWR_PORTS] = {
+static const struct sdw_port_config wsa883x_pconfig[WSA883X_MAX_SWR_PORTS] = {
{
.num = 1,
.ch_mask = 0x1,
@@ -1073,7 +1069,7 @@ static int wsa883x_port_prep(struct sdw_slave *slave,
return 0;
}
-static struct sdw_slave_ops wsa883x_slave_ops = {
+static const struct sdw_slave_ops wsa883x_slave_ops = {
.update_status = wsa883x_update_status,
.port_prep = wsa883x_port_prep,
};
@@ -1375,7 +1371,7 @@ static int wsa883x_probe(struct sdw_slave *pdev,
struct device *dev = &pdev->dev;
int ret;
- wsa883x = devm_kzalloc(&pdev->dev, sizeof(*wsa883x), GFP_KERNEL);
+ wsa883x = devm_kzalloc(dev, sizeof(*wsa883x), GFP_KERNEL);
if (!wsa883x)
return -ENOMEM;
@@ -1388,17 +1384,17 @@ static int wsa883x_probe(struct sdw_slave *pdev,
if (ret)
return dev_err_probe(dev, ret, "Failed to enable vdd regulator\n");
- wsa883x->sd_n = devm_gpiod_get_optional(&pdev->dev, "powerdown",
+ wsa883x->sd_n = devm_gpiod_get_optional(dev, "powerdown",
GPIOD_FLAGS_BIT_NONEXCLUSIVE | GPIOD_OUT_HIGH);
if (IS_ERR(wsa883x->sd_n)) {
- ret = dev_err_probe(&pdev->dev, PTR_ERR(wsa883x->sd_n),
+ ret = dev_err_probe(dev, PTR_ERR(wsa883x->sd_n),
"Shutdown Control GPIO not found\n");
goto err;
}
- dev_set_drvdata(&pdev->dev, wsa883x);
+ dev_set_drvdata(dev, wsa883x);
wsa883x->slave = pdev;
- wsa883x->dev = &pdev->dev;
+ wsa883x->dev = dev;
wsa883x->sconfig.ch_count = 1;
wsa883x->sconfig.bps = 1;
wsa883x->sconfig.direction = SDW_DATA_DIR_RX;
@@ -1413,7 +1409,7 @@ static int wsa883x_probe(struct sdw_slave *pdev,
wsa883x->regmap = devm_regmap_init_sdw(pdev, &wsa883x_regmap_config);
if (IS_ERR(wsa883x->regmap)) {
gpiod_direction_output(wsa883x->sd_n, 1);
- ret = dev_err_probe(&pdev->dev, PTR_ERR(wsa883x->regmap),
+ ret = dev_err_probe(dev, PTR_ERR(wsa883x->regmap),
"regmap_init failed\n");
goto err;
}
@@ -1423,7 +1419,7 @@ static int wsa883x_probe(struct sdw_slave *pdev,
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
- ret = devm_snd_soc_register_component(&pdev->dev,
+ ret = devm_snd_soc_register_component(dev,
&wsa883x_component_drv,
wsa883x_dais,
ARRAY_SIZE(wsa883x_dais));
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index 8d14b5593658d6..8099a829c304b5 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -297,7 +297,7 @@ SND_SOC_DAILINK_DEFS(hifi_be,
DAILINK_COMP_ARRAY(COMP_EMPTY()),
DAILINK_COMP_ARRAY(COMP_DUMMY()));
-static struct snd_soc_dai_link fsl_asoc_card_dai[] = {
+static const struct snd_soc_dai_link fsl_asoc_card_dai[] = {
/* Default ASoC DAI Link*/
{
.name = "HiFi",
@@ -855,7 +855,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, &priv->card);
if (ret) {
- dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed: %d\n", ret);
goto asrc_fail;
}
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 35a52c3a020d1c..1b197478b3d90e 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -281,6 +281,7 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
val_cr4 |= FSL_SAI_CR4_MF;
sai->is_pdm_mode = false;
+ sai->is_dsp_mode = false;
/* DAI mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
@@ -1459,14 +1460,17 @@ static int fsl_sai_probe(struct platform_device *pdev)
if (sai->soc_data->use_imx_pcm) {
ret = imx_pcm_dma_init(pdev);
if (ret) {
+ dev_err_probe(dev, ret, "PCM DMA init failed\n");
if (!IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA))
dev_err(dev, "Error: You must enable the imx-pcm-dma support!\n");
goto err_pm_get_sync;
}
} else {
ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0);
- if (ret)
+ if (ret) {
+ dev_err_probe(dev, ret, "Registering PCM dmaengine failed\n");
goto err_pm_get_sync;
+ }
}
ret = devm_snd_soc_register_component(dev, &fsl_component,
diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c
index 2a6802fb2a8bed..2a78243df75263 100644
--- a/sound/soc/fsl/fsl_xcvr.c
+++ b/sound/soc/fsl/fsl_xcvr.c
@@ -21,6 +21,8 @@
struct fsl_xcvr_soc_data {
const char *fw_name;
+ bool spdif_only;
+ bool use_edma;
};
struct fsl_xcvr {
@@ -261,6 +263,9 @@ static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx)
u32 i, div = 0, log2;
int ret;
+ if (xcvr->soc_data->spdif_only)
+ return 0;
+
for (i = 0; i < ARRAY_SIZE(fsl_xcvr_pll_cfg); i++) {
if (fsl_xcvr_pll_cfg[i].fout % freq == 0) {
div = fsl_xcvr_pll_cfg[i].fout / freq;
@@ -353,6 +358,7 @@ static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq)
struct device *dev = &xcvr->pdev->dev;
int ret;
+ freq = xcvr->soc_data->spdif_only ? freq / 10 : freq;
clk_disable_unprepare(xcvr->phy_clk);
ret = clk_set_rate(xcvr->phy_clk, freq);
if (ret < 0) {
@@ -365,6 +371,8 @@ static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq)
return ret;
}
+ if (xcvr->soc_data->spdif_only)
+ return 0;
/* Release AI interface from reset */
ret = regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET,
FSL_XCVR_PHY_AI_CTRL_AI_RESETN);
@@ -531,6 +539,16 @@ static int fsl_xcvr_startup(struct snd_pcm_substream *substream,
return -EBUSY;
}
+ /*
+ * EDMA controller needs period size to be a multiple of
+ * tx/rx maxburst
+ */
+ if (xcvr->soc_data->use_edma)
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ tx ? xcvr->dma_prms_tx.maxburst :
+ xcvr->dma_prms_rx.maxburst);
+
switch (xcvr->mode) {
case FSL_XCVR_MODE_SPDIF:
case FSL_XCVR_MODE_ARC:
@@ -547,10 +565,12 @@ static int fsl_xcvr_startup(struct snd_pcm_substream *substream,
xcvr->streams |= BIT(substream->stream);
- /* Disable XCVR controls if there is stream started */
- fsl_xcvr_activate_ctl(dai, fsl_xcvr_mode_kctl.name, false);
- fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name, false);
- fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name, false);
+ if (!xcvr->soc_data->spdif_only) {
+ /* Disable XCVR controls if there is stream started */
+ fsl_xcvr_activate_ctl(dai, fsl_xcvr_mode_kctl.name, false);
+ fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name, false);
+ fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name, false);
+ }
return 0;
}
@@ -567,12 +587,13 @@ static void fsl_xcvr_shutdown(struct snd_pcm_substream *substream,
/* Enable XCVR controls if there is no stream started */
if (!xcvr->streams) {
- fsl_xcvr_activate_ctl(dai, fsl_xcvr_mode_kctl.name, true);
- fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name,
- (xcvr->mode == FSL_XCVR_MODE_ARC));
- fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name,
- (xcvr->mode == FSL_XCVR_MODE_EARC));
-
+ if (!xcvr->soc_data->spdif_only) {
+ fsl_xcvr_activate_ctl(dai, fsl_xcvr_mode_kctl.name, true);
+ fsl_xcvr_activate_ctl(dai, fsl_xcvr_arc_mode_kctl.name,
+ (xcvr->mode == FSL_XCVR_MODE_ARC));
+ fsl_xcvr_activate_ctl(dai, fsl_xcvr_earc_capds_kctl.name,
+ (xcvr->mode == FSL_XCVR_MODE_EARC));
+ }
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_IER0,
FSL_XCVR_IRQ_EARC_ALL, 0);
if (ret < 0) {
@@ -673,7 +694,10 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret);
return ret;
}
- fallthrough;
+ if (xcvr->soc_data->spdif_only)
+ break;
+ else
+ fallthrough;
case FSL_XCVR_MODE_EARC:
/* clear ISR_CMDC_TX_EN, W1C */
ret = regmap_write(xcvr->regmap,
@@ -877,9 +901,13 @@ static int fsl_xcvr_dai_probe(struct snd_soc_dai *dai)
snd_soc_dai_init_dma_data(dai, &xcvr->dma_prms_tx, &xcvr->dma_prms_rx);
- snd_soc_add_dai_controls(dai, &fsl_xcvr_mode_kctl, 1);
- snd_soc_add_dai_controls(dai, &fsl_xcvr_arc_mode_kctl, 1);
- snd_soc_add_dai_controls(dai, &fsl_xcvr_earc_capds_kctl, 1);
+ if (xcvr->soc_data->spdif_only)
+ xcvr->mode = FSL_XCVR_MODE_SPDIF;
+ else {
+ snd_soc_add_dai_controls(dai, &fsl_xcvr_mode_kctl, 1);
+ snd_soc_add_dai_controls(dai, &fsl_xcvr_arc_mode_kctl, 1);
+ snd_soc_add_dai_controls(dai, &fsl_xcvr_earc_capds_kctl, 1);
+ }
snd_soc_add_dai_controls(dai, fsl_xcvr_tx_ctls,
ARRAY_SIZE(fsl_xcvr_tx_ctls));
snd_soc_add_dai_controls(dai, fsl_xcvr_rx_ctls,
@@ -930,10 +958,11 @@ static const struct reg_default fsl_xcvr_reg_defaults[] = {
{ FSL_XCVR_ISR_SET, 0x00000000 },
{ FSL_XCVR_ISR_CLR, 0x00000000 },
{ FSL_XCVR_ISR_TOG, 0x00000000 },
- { FSL_XCVR_RX_DPTH_CTRL, 0x00002C89 },
- { FSL_XCVR_RX_DPTH_CTRL_SET, 0x00002C89 },
- { FSL_XCVR_RX_DPTH_CTRL_CLR, 0x00002C89 },
- { FSL_XCVR_RX_DPTH_CTRL_TOG, 0x00002C89 },
+ { FSL_XCVR_CLK_CTRL, 0x0000018F },
+ { FSL_XCVR_RX_DPTH_CTRL, 0x00040CC1 },
+ { FSL_XCVR_RX_DPTH_CTRL_SET, 0x00040CC1 },
+ { FSL_XCVR_RX_DPTH_CTRL_CLR, 0x00040CC1 },
+ { FSL_XCVR_RX_DPTH_CTRL_TOG, 0x00040CC1 },
{ FSL_XCVR_RX_DPTH_CNTR_CTRL, 0x00000000 },
{ FSL_XCVR_RX_DPTH_CNTR_CTRL_SET, 0x00000000 },
{ FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR, 0x00000000 },
@@ -966,6 +995,12 @@ static const struct reg_default fsl_xcvr_reg_defaults[] = {
static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg)
{
+ struct fsl_xcvr *xcvr = dev_get_drvdata(dev);
+
+ if (xcvr->soc_data->spdif_only)
+ if ((reg >= FSL_XCVR_IER && reg <= FSL_XCVR_PHY_AI_RDATA) ||
+ reg > FSL_XCVR_TX_DPTH_BCRR)
+ return false;
switch (reg) {
case FSL_XCVR_VERSION:
case FSL_XCVR_EXT_CTRL:
@@ -991,6 +1026,12 @@ static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg)
case FSL_XCVR_RX_DPTH_CTRL_SET:
case FSL_XCVR_RX_DPTH_CTRL_CLR:
case FSL_XCVR_RX_DPTH_CTRL_TOG:
+ case FSL_XCVR_RX_CS_DATA_0:
+ case FSL_XCVR_RX_CS_DATA_1:
+ case FSL_XCVR_RX_CS_DATA_2:
+ case FSL_XCVR_RX_CS_DATA_3:
+ case FSL_XCVR_RX_CS_DATA_4:
+ case FSL_XCVR_RX_CS_DATA_5:
case FSL_XCVR_RX_DPTH_CNTR_CTRL:
case FSL_XCVR_RX_DPTH_CNTR_CTRL_SET:
case FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR:
@@ -1027,6 +1068,11 @@ static bool fsl_xcvr_readable_reg(struct device *dev, unsigned int reg)
static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg)
{
+ struct fsl_xcvr *xcvr = dev_get_drvdata(dev);
+
+ if (xcvr->soc_data->spdif_only)
+ if (reg >= FSL_XCVR_IER && reg <= FSL_XCVR_PHY_AI_RDATA)
+ return false;
switch (reg) {
case FSL_XCVR_EXT_CTRL:
case FSL_XCVR_EXT_IER0:
@@ -1103,32 +1149,34 @@ static irqreturn_t irq0_isr(int irq, void *devid)
if (isr & FSL_XCVR_IRQ_NEW_CS) {
dev_dbg(dev, "Received new CS block\n");
isr_clr |= FSL_XCVR_IRQ_NEW_CS;
- /* Data RAM is 4KiB, last two pages: 8 and 9. Select page 8. */
- regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
- FSL_XCVR_EXT_CTRL_PAGE_MASK,
- FSL_XCVR_EXT_CTRL_PAGE(8));
-
- /* Find updated CS buffer */
- reg_ctrl = xcvr->ram_addr + FSL_XCVR_RX_CS_CTRL_0;
- reg_buff = xcvr->ram_addr + FSL_XCVR_RX_CS_BUFF_0;
- memcpy_fromio(&val, reg_ctrl, sizeof(val));
- if (!val) {
- reg_ctrl = xcvr->ram_addr + FSL_XCVR_RX_CS_CTRL_1;
- reg_buff = xcvr->ram_addr + FSL_XCVR_RX_CS_BUFF_1;
+ if (!xcvr->soc_data->spdif_only) {
+ /* Data RAM is 4KiB, last two pages: 8 and 9. Select page 8. */
+ regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_PAGE_MASK,
+ FSL_XCVR_EXT_CTRL_PAGE(8));
+
+ /* Find updated CS buffer */
+ reg_ctrl = xcvr->ram_addr + FSL_XCVR_RX_CS_CTRL_0;
+ reg_buff = xcvr->ram_addr + FSL_XCVR_RX_CS_BUFF_0;
memcpy_fromio(&val, reg_ctrl, sizeof(val));
- }
+ if (!val) {
+ reg_ctrl = xcvr->ram_addr + FSL_XCVR_RX_CS_CTRL_1;
+ reg_buff = xcvr->ram_addr + FSL_XCVR_RX_CS_BUFF_1;
+ memcpy_fromio(&val, reg_ctrl, sizeof(val));
+ }
- if (val) {
- /* copy CS buffer */
- memcpy_fromio(&xcvr->rx_iec958.status, reg_buff,
- sizeof(xcvr->rx_iec958.status));
- for (i = 0; i < 6; i++) {
- val = *(u32 *)(xcvr->rx_iec958.status + i*4);
- *(u32 *)(xcvr->rx_iec958.status + i*4) =
- bitrev32(val);
+ if (val) {
+ /* copy CS buffer */
+ memcpy_fromio(&xcvr->rx_iec958.status, reg_buff,
+ sizeof(xcvr->rx_iec958.status));
+ for (i = 0; i < 6; i++) {
+ val = *(u32 *)(xcvr->rx_iec958.status + i*4);
+ *(u32 *)(xcvr->rx_iec958.status + i*4) =
+ bitrev32(val);
+ }
+ /* clear CS control register */
+ memset_io(reg_ctrl, 0, sizeof(val));
}
- /* clear CS control register */
- memset_io(reg_ctrl, 0, sizeof(val));
}
}
if (isr & FSL_XCVR_IRQ_NEW_UD) {
@@ -1168,8 +1216,14 @@ static const struct fsl_xcvr_soc_data fsl_xcvr_imx8mp_data = {
.fw_name = "imx/xcvr/xcvr-imx8mp.bin",
};
+static const struct fsl_xcvr_soc_data fsl_xcvr_imx93_data = {
+ .spdif_only = true,
+ .use_edma = true,
+};
+
static const struct of_device_id fsl_xcvr_dt_ids[] = {
{ .compatible = "fsl,imx8mp-xcvr", .data = &fsl_xcvr_imx8mp_data },
+ { .compatible = "fsl,imx93-xcvr", .data = &fsl_xcvr_imx93_data},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, fsl_xcvr_dt_ids);
@@ -1229,7 +1283,7 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
return PTR_ERR(xcvr->regmap);
}
- xcvr->reset = devm_reset_control_get_exclusive(dev, NULL);
+ xcvr->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(xcvr->reset)) {
dev_err(dev, "failed to get XCVR reset control\n");
return PTR_ERR(xcvr->reset);
@@ -1306,12 +1360,14 @@ static __maybe_unused int fsl_xcvr_runtime_suspend(struct device *dev)
if (ret < 0)
dev_err(dev, "Failed to clear IER0: %d\n", ret);
- /* Assert M0+ reset */
- ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
- FSL_XCVR_EXT_CTRL_CORE_RESET,
- FSL_XCVR_EXT_CTRL_CORE_RESET);
- if (ret < 0)
- dev_err(dev, "Failed to assert M0+ core: %d\n", ret);
+ if (!xcvr->soc_data->spdif_only) {
+ /* Assert M0+ reset */
+ ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
+ FSL_XCVR_EXT_CTRL_CORE_RESET,
+ FSL_XCVR_EXT_CTRL_CORE_RESET);
+ if (ret < 0)
+ dev_err(dev, "Failed to assert M0+ core: %d\n", ret);
+ }
regcache_cache_only(xcvr->regmap, true);
@@ -1367,6 +1423,9 @@ static __maybe_unused int fsl_xcvr_runtime_resume(struct device *dev)
goto stop_spba_clk;
}
+ if (xcvr->soc_data->spdif_only)
+ return 0;
+
ret = reset_control_deassert(xcvr->reset);
if (ret) {
dev_err(dev, "failed to deassert M0+ reset.\n");
diff --git a/sound/soc/fsl/fsl_xcvr.h b/sound/soc/fsl/fsl_xcvr.h
index 4769b0fca21def..044058fc6aa24c 100644
--- a/sound/soc/fsl/fsl_xcvr.h
+++ b/sound/soc/fsl/fsl_xcvr.h
@@ -49,6 +49,13 @@
#define FSL_XCVR_RX_DPTH_CTRL_CLR 0x188
#define FSL_XCVR_RX_DPTH_CTRL_TOG 0x18c
+#define FSL_XCVR_RX_CS_DATA_0 0x190
+#define FSL_XCVR_RX_CS_DATA_1 0x194
+#define FSL_XCVR_RX_CS_DATA_2 0x198
+#define FSL_XCVR_RX_CS_DATA_3 0x19C
+#define FSL_XCVR_RX_CS_DATA_4 0x1A0
+#define FSL_XCVR_RX_CS_DATA_5 0x1A4
+
#define FSL_XCVR_RX_DPTH_CNTR_CTRL 0x1C0
#define FSL_XCVR_RX_DPTH_CNTR_CTRL_SET 0x1C4
#define FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR 0x1C8
diff --git a/sound/soc/fsl/imx-hdmi.c b/sound/soc/fsl/imx-hdmi.c
index a780cf5a65ffa1..b6cc7e6c2a3205 100644
--- a/sound/soc/fsl/imx-hdmi.c
+++ b/sound/soc/fsl/imx-hdmi.c
@@ -202,7 +202,7 @@ static int imx_hdmi_probe(struct platform_device *pdev)
snd_soc_card_set_drvdata(&data->card, data);
ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
+ dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
goto fail;
}
diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c
index 2f310994f7ee06..6614b3447649b7 100644
--- a/sound/soc/fsl/imx-pcm-rpmsg.c
+++ b/sound/soc/fsl/imx-pcm-rpmsg.c
@@ -140,7 +140,6 @@ static int imx_rpmsg_pcm_hw_params(struct snd_soc_component *component,
{
struct rpmsg_info *info = dev_get_drvdata(component->dev);
struct rpmsg_msg *msg;
- int ret = 0;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
msg = &info->msg[TX_HW_PARAM];
@@ -184,7 +183,7 @@ static int imx_rpmsg_pcm_hw_params(struct snd_soc_component *component,
info->send_message(msg, info);
- return ret;
+ return 0;
}
static snd_pcm_uframes_t imx_rpmsg_pcm_pointer(struct snd_soc_component *component,
@@ -282,7 +281,6 @@ static int imx_rpmsg_pcm_close(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct rpmsg_info *info = dev_get_drvdata(component->dev);
struct rpmsg_msg *msg;
- int ret = 0;
/* Flush work in workqueue to make TX_CLOSE is the last message */
flush_workqueue(info->rpmsg_wq);
@@ -305,7 +303,7 @@ static int imx_rpmsg_pcm_close(struct snd_soc_component *component,
dev_warn(rtd->dev, "Msg is dropped!, number is %d\n",
info->msg_drop_count[substream->stream]);
- return ret;
+ return 0;
}
static int imx_rpmsg_pcm_prepare(struct snd_soc_component *component,
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index e35becce9635f7..56552a616f21ff 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -786,6 +786,55 @@ int asoc_simple_init_jack(struct snd_soc_card *card,
}
EXPORT_SYMBOL_GPL(asoc_simple_init_jack);
+int asoc_simple_init_aux_jacks(struct asoc_simple_priv *priv, char *prefix)
+{
+ struct snd_soc_card *card = simple_priv_to_card(priv);
+ struct snd_soc_component *component;
+ int found_jack_index = 0;
+ int type = 0;
+ int num = 0;
+ int ret;
+
+ if (priv->aux_jacks)
+ return 0;
+
+ for_each_card_auxs(card, component) {
+ type = snd_soc_component_get_jack_type(component);
+ if (type > 0)
+ num++;
+ }
+ if (num < 1)
+ return 0;
+
+ priv->aux_jacks = devm_kcalloc(card->dev, num,
+ sizeof(struct snd_soc_jack), GFP_KERNEL);
+ if (!priv->aux_jacks)
+ return -ENOMEM;
+
+ for_each_card_auxs(card, component) {
+ char id[128];
+ struct snd_soc_jack *jack;
+
+ if (found_jack_index >= num)
+ break;
+
+ type = snd_soc_component_get_jack_type(component);
+ if (type <= 0)
+ continue;
+
+ /* create jack */
+ jack = &(priv->aux_jacks[found_jack_index++]);
+ snprintf(id, sizeof(id), "%s-jack", component->name);
+ ret = snd_soc_card_jack_new(card, id, type, jack);
+ if (ret)
+ continue;
+
+ (void)snd_soc_component_set_jack(component, jack, NULL);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_init_aux_jacks);
+
int asoc_simple_init_priv(struct asoc_simple_priv *priv,
struct link_info *li)
{
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index feb55b66239b85..e98932c167542f 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -623,6 +623,10 @@ static int simple_soc_probe(struct snd_soc_card *card)
if (ret < 0)
return ret;
+ ret = asoc_simple_init_aux_jacks(priv, PREFIX);
+ if (ret < 0)
+ return ret;
+
return 0;
}
diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c
index fd59b35a62bae7..38116c75871742 100644
--- a/sound/soc/intel/atom/sst-atom-controls.c
+++ b/sound/soc/intel/atom/sst-atom-controls.c
@@ -1327,15 +1327,13 @@ static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w)
int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
{
struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream);
struct snd_soc_dapm_path *p;
dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream);
+ dev_dbg(dai->dev, "Stream name=%s\n", w->name);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dev_dbg(dai->dev, "Stream name=%s\n",
- dai->playback_widget->name);
- w = dai->playback_widget;
snd_soc_dapm_widget_for_each_sink_path(w, p) {
if (p->connected && !p->connected(w, p->sink))
continue;
@@ -1352,9 +1350,6 @@ int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
}
}
} else {
- dev_dbg(dai->dev, "Stream name=%s\n",
- dai->capture_widget->name);
- w = dai->capture_widget;
snd_soc_dapm_widget_for_each_source_path(w, p) {
if (p->connected && !p->connected(w, p->source))
continue;
diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index 1c6924a1ebca3a..460ee6599daf03 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
snd-soc-avs-objs := dsp.o ipc.o messages.o utils.o core.o loader.o \
- topology.o path.o pcm.o board_selection.o
+ topology.o path.o pcm.o board_selection.o control.o
snd-soc-avs-objs += cldma.o
snd-soc-avs-objs += skl.o apl.o
diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c
index 6731d8a490767f..b31fa931ba8b6a 100644
--- a/sound/soc/intel/avs/boards/nau8825.c
+++ b/sound/soc/intel/avs/boards/nau8825.c
@@ -258,14 +258,15 @@ static int avs_card_resume_post(struct snd_soc_card *card)
{
struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, SKL_NUVOTON_CODEC_DAI);
struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
+ int stream = SNDRV_PCM_STREAM_PLAYBACK;
if (!codec_dai) {
dev_err(card->dev, "Codec dai not found\n");
return -EINVAL;
}
- if (codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK] &&
- codec_dai->playback_widget->active)
+ if (snd_soc_dai_stream_active(codec_dai, stream) &&
+ snd_soc_dai_get_widget(codec_dai, stream)->active)
snd_soc_dai_set_sysclk(codec_dai, NAU8825_CLK_FLL_FS, 0, SND_SOC_CLOCK_IN);
return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c
index 8447b37a2a4109..3551a05bd5997b 100644
--- a/sound/soc/intel/avs/boards/rt286.c
+++ b/sound/soc/intel/avs/boards/rt286.c
@@ -98,7 +98,7 @@ static int avs_rt286_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pc
static int
avs_rt286_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *runtime = substream->private_data;
+ struct snd_soc_pcm_runtime *runtime = asoc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(runtime, 0);
int ret;
diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c
index bd25f0fde35e53..2923f3805bbead 100644
--- a/sound/soc/intel/avs/boards/rt298.c
+++ b/sound/soc/intel/avs/boards/rt298.c
@@ -109,7 +109,7 @@ static int avs_rt298_be_fixup(struct snd_soc_pcm_runtime *runtime, struct snd_pc
static int
avs_rt298_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
unsigned int clk_freq;
int ret;
diff --git a/sound/soc/intel/avs/control.c b/sound/soc/intel/avs/control.c
new file mode 100644
index 00000000000000..a8b14b784f8a51
--- /dev/null
+++ b/sound/soc/intel/avs/control.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+//
+// Authors: Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+// Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <sound/soc.h>
+#include "avs.h"
+#include "control.h"
+#include "messages.h"
+#include "path.h"
+
+static struct avs_dev *avs_get_kcontrol_adev(struct snd_kcontrol *kcontrol)
+{
+ struct snd_soc_dapm_widget *w;
+
+ w = snd_soc_dapm_kcontrol_widget(kcontrol);
+
+ return to_avs_dev(w->dapm->component->dev);
+}
+
+static struct avs_path_module *avs_get_kcontrol_module(struct avs_dev *adev, u32 id)
+{
+ struct avs_path *path;
+ struct avs_path_pipeline *ppl;
+ struct avs_path_module *mod;
+
+ list_for_each_entry(path, &adev->path_list, node)
+ list_for_each_entry(ppl, &path->ppl_list, node)
+ list_for_each_entry(mod, &ppl->mod_list, node)
+ if (mod->template->ctl_id && mod->template->ctl_id == id)
+ return mod;
+
+ return NULL;
+}
+
+int avs_control_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ struct avs_control_data *ctl_data = (struct avs_control_data *)mc->dobj.private;
+ struct avs_dev *adev = avs_get_kcontrol_adev(kcontrol);
+ struct avs_volume_cfg *dspvols = NULL;
+ struct avs_path_module *active_module;
+ size_t num_dspvols;
+ int ret = 0;
+
+ /* prevent access to modules while path is being constructed */
+ mutex_lock(&adev->path_mutex);
+
+ active_module = avs_get_kcontrol_module(adev, ctl_data->id);
+ if (active_module) {
+ ret = avs_ipc_peakvol_get_volume(adev, active_module->module_id,
+ active_module->instance_id, &dspvols,
+ &num_dspvols);
+ if (!ret)
+ ucontrol->value.integer.value[0] = dspvols[0].target_volume;
+
+ ret = AVS_IPC_RET(ret);
+ kfree(dspvols);
+ } else {
+ ucontrol->value.integer.value[0] = ctl_data->volume;
+ }
+
+ mutex_unlock(&adev->path_mutex);
+ return ret;
+}
+
+int avs_control_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ struct avs_control_data *ctl_data = (struct avs_control_data *)mc->dobj.private;
+ struct avs_dev *adev = avs_get_kcontrol_adev(kcontrol);
+ long *volume = &ctl_data->volume;
+ struct avs_path_module *active_module;
+ struct avs_volume_cfg dspvol = {0};
+ long ctlvol = ucontrol->value.integer.value[0];
+ int ret = 0, changed = 0;
+
+ if (ctlvol < 0 || ctlvol > mc->max)
+ return -EINVAL;
+
+ /* prevent access to modules while path is being constructed */
+ mutex_lock(&adev->path_mutex);
+
+ if (*volume != ctlvol) {
+ *volume = ctlvol;
+ changed = 1;
+ }
+
+ active_module = avs_get_kcontrol_module(adev, ctl_data->id);
+ if (active_module) {
+ dspvol.channel_id = AVS_ALL_CHANNELS_MASK;
+ dspvol.target_volume = *volume;
+
+ ret = avs_ipc_peakvol_set_volume(adev, active_module->module_id,
+ active_module->instance_id, &dspvol);
+ ret = AVS_IPC_RET(ret);
+ }
+
+ mutex_unlock(&adev->path_mutex);
+
+ return ret ? ret : changed;
+}
diff --git a/sound/soc/intel/avs/control.h b/sound/soc/intel/avs/control.h
new file mode 100644
index 00000000000000..08631bde13c34e
--- /dev/null
+++ b/sound/soc/intel/avs/control.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
+ *
+ * Authors: Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ * Cezary Rojewski <cezary.rojewski@intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_CTRL_H
+#define __SOUND_SOC_INTEL_AVS_CTRL_H
+
+#include <sound/control.h>
+
+struct avs_control_data {
+ u32 id;
+
+ long volume;
+};
+
+int avs_control_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int avs_control_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+
+#endif
diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
index e11ae4246416cf..f887ab5b0311df 100644
--- a/sound/soc/intel/avs/messages.c
+++ b/sound/soc/intel/avs/messages.c
@@ -702,6 +702,35 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id,
(u8 *)&cpr_fmt, sizeof(cpr_fmt));
}
+int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_volume_cfg *vol)
+{
+ return avs_ipc_set_large_config(adev, module_id, instance_id, AVS_PEAKVOL_VOLUME, (u8 *)vol,
+ sizeof(*vol));
+}
+
+int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_volume_cfg **vols, size_t *num_vols)
+{
+ size_t payload_size;
+ u8 *payload;
+ int ret;
+
+ ret = avs_ipc_get_large_config(adev, module_id, instance_id, AVS_PEAKVOL_VOLUME, NULL, 0,
+ &payload, &payload_size);
+ if (ret)
+ return ret;
+
+ /* Non-zero payload expected for PEAKVOL_VOLUME. */
+ if (!payload_size)
+ return -EREMOTEIO;
+
+ *vols = (struct avs_volume_cfg *)payload;
+ *num_vols = payload_size / sizeof(**vols);
+
+ return 0;
+}
+
#ifdef CONFIG_DEBUG_FS
int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size)
{
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 9dd835527e02c3..d3b60ae7d7432d 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -561,6 +561,12 @@ int avs_ipc_set_system_time(struct avs_dev *adev);
#define AVS_COPIER_MOD_UUID \
GUID_INIT(0x9BA00C83, 0xCA12, 0x4A83, 0x94, 0x3C, 0x1F, 0xA2, 0xE8, 0x2F, 0x9D, 0xDA)
+#define AVS_PEAKVOL_MOD_UUID \
+ GUID_INIT(0x8A171323, 0x94A3, 0x4E1D, 0xAF, 0xE9, 0xFE, 0x5D, 0xBA, 0xa4, 0xC3, 0x93)
+
+#define AVS_GAIN_MOD_UUID \
+ GUID_INIT(0x61BCA9A8, 0x18D0, 0x4A18, 0x8E, 0x7B, 0x26, 0x39, 0x21, 0x98, 0x04, 0xB7)
+
#define AVS_KPBUFF_MOD_UUID \
GUID_INIT(0xA8A0CB32, 0x4A77, 0x4DB1, 0x85, 0xC7, 0x53, 0xD7, 0xEE, 0x07, 0xBC, 0xE6)
@@ -729,6 +735,19 @@ struct avs_copier_cfg {
struct avs_copier_gtw_cfg gtw_cfg;
} __packed;
+struct avs_volume_cfg {
+ u32 channel_id;
+ u32 target_volume;
+ u32 curve_type;
+ u32 reserved; /* alignment */
+ u64 curve_duration;
+} __packed;
+
+struct avs_peakvol_cfg {
+ struct avs_modcfg_base base;
+ struct avs_volume_cfg vols[];
+} __packed;
+
struct avs_micsel_cfg {
struct avs_modcfg_base base;
struct avs_audio_format out_fmt;
@@ -802,6 +821,20 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id,
const struct avs_audio_format *src_fmt,
const struct avs_audio_format *sink_fmt);
+enum avs_peakvol_runtime_param {
+ AVS_PEAKVOL_VOLUME = 0,
+};
+
+enum avs_audio_curve_type {
+ AVS_AUDIO_CURVE_NONE = 0,
+ AVS_AUDIO_CURVE_WINDOWS_FADE = 1,
+};
+
+int avs_ipc_peakvol_set_volume(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_volume_cfg *vol);
+int avs_ipc_peakvol_get_volume(struct avs_dev *adev, u16 module_id, u8 instance_id,
+ struct avs_volume_cfg **vols, size_t *num_vols);
+
#define AVS_PROBE_INST_ID 0
enum avs_probe_runtime_param {
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
index ce157a8d65520a..05302ab705ae02 100644
--- a/sound/soc/intel/avs/path.c
+++ b/sound/soc/intel/avs/path.c
@@ -10,6 +10,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "avs.h"
+#include "control.h"
#include "path.h"
#include "topology.h"
@@ -264,6 +265,65 @@ static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod)
return ret;
}
+static struct avs_control_data *avs_get_module_control(struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_tplg_path_template *path_tmpl;
+ struct snd_soc_dapm_widget *w;
+ int i;
+
+ path_tmpl = t->owner->owner->owner;
+ w = path_tmpl->w;
+
+ for (i = 0; i < w->num_kcontrols; i++) {
+ struct avs_control_data *ctl_data;
+ struct soc_mixer_control *mc;
+
+ mc = (struct soc_mixer_control *)w->kcontrols[i]->private_value;
+ ctl_data = (struct avs_control_data *)mc->dobj.private;
+ if (ctl_data->id == t->ctl_id)
+ return ctl_data;
+ }
+
+ return NULL;
+}
+
+static int avs_peakvol_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_control_data *ctl_data;
+ struct avs_peakvol_cfg *cfg;
+ int volume = S32_MAX;
+ size_t size;
+ int ret;
+
+ ctl_data = avs_get_module_control(mod);
+ if (ctl_data)
+ volume = ctl_data->volume;
+
+ /* As 2+ channels controls are unsupported, have a single block for all channels. */
+ size = struct_size(cfg, vols, 1);
+ cfg = kzalloc(size, GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+
+ cfg->base.cpc = t->cfg_base->cpc;
+ cfg->base.ibs = t->cfg_base->ibs;
+ cfg->base.obs = t->cfg_base->obs;
+ cfg->base.is_pages = t->cfg_base->is_pages;
+ cfg->base.audio_fmt = *t->in_fmt;
+ cfg->vols[0].target_volume = volume;
+ cfg->vols[0].channel_id = AVS_ALL_CHANNELS_MASK;
+ cfg->vols[0].curve_type = AVS_AUDIO_CURVE_NONE;
+ cfg->vols[0].curve_duration = 0;
+
+ ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id,
+ t->domain, cfg, size, &mod->instance_id);
+
+ kfree(cfg);
+ return ret;
+}
+
static int avs_updown_mix_create(struct avs_dev *adev, struct avs_path_module *mod)
{
struct avs_tplg_module *t = mod->template;
@@ -465,6 +525,8 @@ static struct avs_module_create avs_module_create[] = {
{ &AVS_MIXOUT_MOD_UUID, avs_modbase_create },
{ &AVS_KPBUFF_MOD_UUID, avs_modbase_create },
{ &AVS_COPIER_MOD_UUID, avs_copier_create },
+ { &AVS_PEAKVOL_MOD_UUID, avs_peakvol_create },
+ { &AVS_GAIN_MOD_UUID, avs_peakvol_create },
{ &AVS_MICSEL_MOD_UUID, avs_micsel_create },
{ &AVS_MUX_MOD_UUID, avs_mux_create },
{ &AVS_UPDWMIX_MOD_UUID, avs_updown_mix_create },
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index f930c5e86a84ad..31c032a0f7e4b3 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -35,15 +35,13 @@ struct avs_dma_data {
static struct avs_tplg_path_template *
avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction)
{
- struct snd_soc_dapm_widget *dw;
+ struct snd_soc_dapm_widget *dw = snd_soc_dai_get_widget(dai, direction);
struct snd_soc_dapm_path *dp;
enum snd_soc_dapm_direction dir;
if (direction == SNDRV_PCM_STREAM_CAPTURE) {
- dw = dai->capture_widget;
dir = is_fe ? SND_SOC_DAPM_DIR_OUT : SND_SOC_DAPM_DIR_IN;
} else {
- dw = dai->playback_widget;
dir = is_fe ? SND_SOC_DAPM_DIR_IN : SND_SOC_DAPM_DIR_OUT;
}
@@ -60,7 +58,7 @@ avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction)
static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool is_fe,
const struct snd_soc_dai_ops *ops)
{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct avs_dev *adev = to_avs_dev(dai->dev);
struct avs_tplg_path_template *template;
struct avs_dma_data *data;
@@ -169,7 +167,7 @@ static int avs_dai_nonhda_be_startup(struct snd_pcm_substream *substream, struct
static void avs_dai_nonhda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct avs_dev *adev = to_avs_dev(dai->dev);
struct avs_dma_data *data;
@@ -218,7 +216,7 @@ static int avs_dai_nonhda_be_prepare(struct snd_pcm_substream *substream, struct
static int avs_dai_nonhda_be_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct avs_dma_data *data;
int ret = 0;
@@ -305,7 +303,7 @@ static int avs_dai_hda_be_hw_params(struct snd_pcm_substream *substream,
static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct avs_dma_data *data;
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct hdac_ext_stream *link_stream;
struct hdac_ext_link *link;
struct hda_codec *codec;
@@ -335,7 +333,7 @@ static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct sn
static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct hdac_ext_stream *link_stream = runtime->private_data;
struct hdac_ext_link *link;
@@ -374,7 +372,7 @@ static int avs_dai_hda_be_prepare(struct snd_pcm_substream *substream, struct sn
static int avs_dai_hda_be_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct hdac_ext_stream *link_stream;
struct avs_dma_data *data;
int ret = 0;
@@ -489,7 +487,7 @@ static int avs_dai_fe_startup(struct snd_pcm_substream *substream, struct snd_so
static void avs_dai_fe_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct avs_dev *adev = to_avs_dev(dai->dev);
struct avs_dma_data *data;
@@ -628,7 +626,7 @@ static int avs_dai_fe_prepare(struct snd_pcm_substream *substream, struct snd_so
static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct avs_dma_data *data;
struct hdac_ext_stream *host_stream;
struct hdac_bus *bus;
@@ -647,7 +645,7 @@ static int avs_dai_fe_trigger(struct snd_pcm_substream *substream, int cmd, stru
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
spin_lock_irqsave(&bus->reg_lock, flags);
- snd_hdac_stream_start(hdac_stream(host_stream), true);
+ snd_hdac_stream_start(hdac_stream(host_stream));
spin_unlock_irqrestore(&bus->reg_lock, flags);
/* Timeout on DRSM poll shall not stop the resume so ignore the result. */
@@ -836,7 +834,7 @@ static int avs_dai_resume_hw_params(struct snd_soc_dai *dai, struct avs_dma_data
int ret;
substream = data->substream;
- rtd = snd_pcm_substream_chip(substream);
+ rtd = asoc_substream_to_rtd(substream);
ret = dai->driver->ops->hw_params(substream, &rtd->dpcm[substream->stream].hw_params, dai);
if (ret)
@@ -929,9 +927,9 @@ static int avs_component_pm_op(struct snd_soc_component *component, bool be,
int ret;
for_each_component_dais(component, dai) {
- data = dai->playback_dma_data;
+ data = snd_soc_dai_dma_data_get_playback(dai);
if (data) {
- rtd = snd_pcm_substream_chip(data->substream);
+ rtd = asoc_substream_to_rtd(data->substream);
if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) {
ret = op(dai, data);
if (ret < 0) {
@@ -942,9 +940,9 @@ static int avs_component_pm_op(struct snd_soc_component *component, bool be,
}
}
- data = dai->capture_dma_data;
+ data = snd_soc_dai_dma_data_get_capture(dai);
if (data) {
- rtd = snd_pcm_substream_chip(data->substream);
+ rtd = asoc_substream_to_rtd(data->substream);
if (rtd->dai_link->no_pcm == be && !rtd->dai_link->ignore_suspend) {
ret = op(dai, data);
if (ret < 0) {
@@ -1048,7 +1046,7 @@ static const struct snd_pcm_hardware avs_pcm_hardware = {
static int avs_component_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
/* only FE DAI links are handled here */
if (rtd->dai_link->no_pcm)
@@ -1066,7 +1064,7 @@ static unsigned int avs_hda_stream_dpib_read(struct hdac_ext_stream *stream)
static snd_pcm_uframes_t
avs_component_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct avs_dma_data *data;
struct hdac_ext_stream *host_stream;
unsigned int pos;
@@ -1291,11 +1289,14 @@ static void avs_component_hda_unregister_dais(struct snd_soc_component *componen
sprintf(name, "%s-cpu", dev_name(&codec->core.dev));
for_each_component_dais_safe(component, dai, save) {
+ int stream;
+
if (!strstr(dai->driver->name, name))
continue;
- snd_soc_dapm_free_widget(dai->playback_widget);
- snd_soc_dapm_free_widget(dai->capture_widget);
+ for_each_pcm_streams(stream)
+ snd_soc_dapm_free_widget(snd_soc_dai_get_widget(dai, stream));
+
snd_soc_unregister_dai(dai);
}
}
@@ -1394,7 +1395,7 @@ static void avs_component_hda_remove(struct snd_soc_component *component)
static int avs_component_hda_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct hdac_ext_stream *link_stream;
struct hda_codec *codec;
@@ -1441,7 +1442,7 @@ static int avs_component_hda_open(struct snd_soc_component *component,
static int avs_component_hda_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct hdac_ext_stream *link_stream;
/* only BE DAI links are handled here */
diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c
index 29d63f2a9616cc..70a94201d6a56a 100644
--- a/sound/soc/intel/avs/probes.c
+++ b/sound/soc/intel/avs/probes.c
@@ -190,7 +190,7 @@ static int avs_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
spin_lock_irqsave(&bus->reg_lock, cookie);
- snd_hdac_stream_start(hdac_stream(host_stream), true);
+ snd_hdac_stream_start(hdac_stream(host_stream));
spin_unlock_irqrestore(&bus->reg_lock, cookie);
break;
@@ -277,31 +277,8 @@ static struct snd_soc_dai_driver probe_cpu_dais[] = {
},
};
-static int avs_probe_component_probe(struct snd_soc_component *component)
-{
- struct avs_soc_component *acomp = to_avs_soc_component(component);
- struct avs_dev *adev = to_avs_dev(component->dev);
-
- mutex_lock(&adev->comp_list_mutex);
- list_add_tail(&acomp->node, &adev->comp_list);
- mutex_unlock(&adev->comp_list_mutex);
- return 0;
-}
-
-static void avs_probe_component_remove(struct snd_soc_component *component)
-{
- struct avs_soc_component *acomp = to_avs_soc_component(component);
- struct avs_dev *adev = to_avs_dev(component->dev);
-
- mutex_lock(&adev->comp_list_mutex);
- list_del(&acomp->node);
- mutex_unlock(&adev->comp_list_mutex);
-}
-
static const struct snd_soc_component_driver avs_probe_component_driver = {
.name = "avs-probe-compr",
- .probe = avs_probe_component_probe,
- .remove = avs_probe_component_remove,
.compress_ops = &avs_probe_compress_ops,
.module_get_upon_open = 1, /* increment refcount when a stream is opened */
};
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
index e845eaf0a1e77d..cdb4ec50026178 100644
--- a/sound/soc/intel/avs/topology.c
+++ b/sound/soc/intel/avs/topology.c
@@ -13,6 +13,7 @@
#include <sound/soc-topology.h>
#include <uapi/sound/intel/avs/tokens.h>
#include "avs.h"
+#include "control.h"
#include "topology.h"
/* Get pointer to vendor array at the specified offset. */
@@ -1070,6 +1071,12 @@ static const struct avs_tplg_token_parser module_parsers[] = {
.offset = offsetof(struct avs_tplg_module, cfg_ext),
.parse = avs_parse_modcfg_ext_ptr,
},
+ {
+ .token = AVS_TKN_MOD_KCONTROL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_module, ctl_id),
+ .parse = avs_parse_byte_token,
+ },
};
static struct avs_tplg_module *
@@ -1435,6 +1442,16 @@ static int avs_widget_load(struct snd_soc_component *comp, int index,
return 0;
}
+static int avs_widget_ready(struct snd_soc_component *comp, int index,
+ struct snd_soc_dapm_widget *w,
+ struct snd_soc_tplg_dapm_widget *dw)
+{
+ struct avs_tplg_path_template *template = w->priv;
+
+ template->w = w;
+ return 0;
+}
+
static int avs_dai_load(struct snd_soc_component *comp, int index,
struct snd_soc_dai_driver *dai_drv, struct snd_soc_tplg_pcm *pcm,
struct snd_soc_dai *dai)
@@ -1586,9 +1603,68 @@ static int avs_manifest(struct snd_soc_component *comp, int index,
return avs_tplg_parse_bindings(comp, tuples, remaining);
}
+#define AVS_CONTROL_OPS_VOLUME 257
+
+static const struct snd_soc_tplg_kcontrol_ops avs_control_ops[] = {
+ {
+ .id = AVS_CONTROL_OPS_VOLUME,
+ .get = avs_control_volume_get,
+ .put = avs_control_volume_put,
+ },
+};
+
+static const struct avs_tplg_token_parser control_parsers[] = {
+ {
+ .token = AVS_TKN_KCONTROL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_control_data, id),
+ .parse = avs_parse_word_token,
+ },
+};
+
+static int
+avs_control_load(struct snd_soc_component *comp, int index, struct snd_kcontrol_new *ctmpl,
+ struct snd_soc_tplg_ctl_hdr *hdr)
+{
+ struct snd_soc_tplg_vendor_array *tuples;
+ struct snd_soc_tplg_mixer_control *tmc;
+ struct avs_control_data *ctl_data;
+ struct soc_mixer_control *mc;
+ size_t block_size;
+ int ret;
+
+ switch (le32_to_cpu(hdr->type)) {
+ case SND_SOC_TPLG_TYPE_MIXER:
+ tmc = container_of(hdr, typeof(*tmc), hdr);
+ tuples = tmc->priv.array;
+ block_size = le32_to_cpu(tmc->priv.size);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ctl_data = devm_kzalloc(comp->card->dev, sizeof(*ctl_data), GFP_KERNEL);
+ if (!ctl_data)
+ return -ENOMEM;
+
+ ret = parse_dictionary_entries(comp, tuples, block_size, ctl_data, 1, sizeof(*ctl_data),
+ AVS_TKN_KCONTROL_ID_U32, control_parsers,
+ ARRAY_SIZE(control_parsers));
+ if (ret)
+ return ret;
+
+ mc = (struct soc_mixer_control *)ctmpl->private_value;
+ mc->dobj.private = ctl_data;
+ return 0;
+}
+
static struct snd_soc_tplg_ops avs_tplg_ops = {
+ .io_ops = avs_control_ops,
+ .io_ops_count = ARRAY_SIZE(avs_control_ops),
+ .control_load = avs_control_load,
.dapm_route_load = avs_route_load,
.widget_load = avs_widget_load,
+ .widget_ready = avs_widget_ready,
.dai_load = avs_dai_load,
.link_load = avs_link_load,
.manifest = avs_manifest,
diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h
index 68e5f6312353d2..6e1c8e9b249641 100644
--- a/sound/soc/intel/avs/topology.h
+++ b/sound/soc/intel/avs/topology.h
@@ -138,6 +138,8 @@ struct avs_tplg_path_template_id {
struct avs_tplg_path_template {
u32 id;
+ struct snd_soc_dapm_widget *w;
+
struct list_head path_list;
struct avs_tplg *owner;
@@ -180,6 +182,7 @@ struct avs_tplg_module {
u8 core_id;
u8 domain;
struct avs_tplg_modcfg_ext *cfg_ext;
+ u32 ctl_id;
struct avs_tplg_pipeline *owner;
/* Pipeline modules management. */
diff --git a/sound/soc/intel/avs/trace.c b/sound/soc/intel/avs/trace.c
index fcb7cfc823d6aa..c63eea909b5e86 100644
--- a/sound/soc/intel/avs/trace.c
+++ b/sound/soc/intel/avs/trace.c
@@ -24,7 +24,7 @@ void trace_avs_msg_payload(const void *data, size_t size)
while (remaining > 0) {
u32 chunk;
- chunk = min(remaining, (size_t)MAX_CHUNK_SIZE);
+ chunk = min_t(size_t, remaining, MAX_CHUNK_SIZE);
trace_avs_ipc_msg_payload(data, chunk, offset, size);
remaining -= chunk;
diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c
index 41cec67157b6e4..9942a2de6f7a08 100644
--- a/sound/soc/intel/boards/bytcht_cx2072x.c
+++ b/sound/soc/intel/boards/bytcht_cx2072x.c
@@ -253,9 +253,9 @@ static int snd_byt_cht_cx2072x_probe(struct platform_device *pdev)
if (adev) {
snprintf(codec_name, sizeof(codec_name), "i2c-%s",
acpi_dev_name(adev));
- put_device(&adev->dev);
byt_cht_cx2072x_dais[dai_index].codecs->name = codec_name;
}
+ acpi_dev_put(adev);
/* override platform name, if required */
ret = snd_soc_fixup_dai_links_platform_name(&byt_cht_cx2072x_card,
diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c
index a0c8f1d3f8cec8..a3b0cfab17b058 100644
--- a/sound/soc/intel/boards/bytcht_da7213.c
+++ b/sound/soc/intel/boards/bytcht_da7213.c
@@ -256,9 +256,9 @@ static int bytcht_da7213_probe(struct platform_device *pdev)
if (adev) {
snprintf(codec_name, sizeof(codec_name),
"i2c-%s", acpi_dev_name(adev));
- put_device(&adev->dev);
dailink[dai_index].codecs->name = codec_name;
}
+ acpi_dev_put(adev);
/* override platform name, if required */
platform_name = mach->mach_params.platform;
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index 25a054bd40736b..875bc0b3d85dab 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -581,9 +581,9 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
if (adev) {
snprintf(cht_rt5645_codec_name, sizeof(cht_rt5645_codec_name),
"i2c-%s", acpi_dev_name(adev));
- put_device(&adev->dev);
cht_dailink[dai_index].codecs->name = cht_rt5645_codec_name;
}
+ acpi_dev_put(adev);
/*
* swap SSP0 if bytcr is detected
diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c
index 49d3da8f7316d1..daa630a0efc18c 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5672.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5672.c
@@ -473,9 +473,9 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
if (adev) {
snprintf(drv->codec_name, sizeof(drv->codec_name),
"i2c-%s", acpi_dev_name(adev));
- put_device(&adev->dev);
cht_dailink[dai_index].codecs->name = drv->codec_name;
}
+ acpi_dev_put(adev);
/* Use SSP0 on Bay Trail CR devices */
if (soc_intel_is_byt() && mach->mach_params.acpi_ipc_irq_index == 0) {
diff --git a/sound/soc/intel/boards/sof_ssp_amp.c b/sound/soc/intel/boards/sof_ssp_amp.c
index 7b74f122e34004..ffd9c583dab12e 100644
--- a/sound/soc/intel/boards/sof_ssp_amp.c
+++ b/sound/soc/intel/boards/sof_ssp_amp.c
@@ -105,7 +105,6 @@ static int sof_card_late_probe(struct snd_soc_card *card)
char jack_name[NAME_SIZE];
struct sof_hdmi_pcm *pcm;
int err;
- int i;
if (!(sof_ssp_amp_quirk & SOF_HDMI_PLAYBACK_PRESENT))
return 0;
@@ -124,7 +123,6 @@ static int sof_card_late_probe(struct snd_soc_card *card)
return hda_dsp_hdmi_build_controls(card, component);
}
- i = 0;
list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) {
component = pcm->codec_dai->component;
snprintf(jack_name, sizeof(jack_name),
@@ -139,8 +137,6 @@ static int sof_card_late_probe(struct snd_soc_card *card)
&pcm->sof_hdmi);
if (err < 0)
return err;
-
- i++;
}
return hdac_hdmi_jack_port_init(component, &card->dapm);
diff --git a/sound/soc/intel/boards/sof_wm8804.c b/sound/soc/intel/boards/sof_wm8804.c
index 54395e2ededc7b..fbad5a73de440d 100644
--- a/sound/soc/intel/boards/sof_wm8804.c
+++ b/sound/soc/intel/boards/sof_wm8804.c
@@ -269,9 +269,9 @@ static int sof_wm8804_probe(struct platform_device *pdev)
if (adev) {
snprintf(codec_name, sizeof(codec_name),
"%s%s", "i2c-", acpi_dev_name(adev));
- put_device(&adev->dev);
dailink[dai_index].codecs->name = codec_name;
}
+ acpi_dev_put(adev);
snd_soc_card_set_drvdata(card, ctx);
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c
index 5ab0917a2b3dea..d31509298a0a5d 100644
--- a/sound/soc/intel/skylake/skl-messages.c
+++ b/sound/soc/intel/skylake/skl-messages.c
@@ -549,7 +549,7 @@ static void skl_copy_copier_caps(struct skl_module_cfg *mconfig,
if (mconfig->formats_config[SKL_PARAM_INIT].caps_size == 0)
return;
- memcpy(cpr_mconfig->gtw_cfg.config_data,
+ memcpy(&cpr_mconfig->gtw_cfg.config_data,
mconfig->formats_config[SKL_PARAM_INIT].caps,
mconfig->formats_config[SKL_PARAM_INIT].caps_size);
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index dc627d18518d8e..a4209d88b0c6c7 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -449,7 +449,7 @@ static int skl_decoupled_trigger(struct snd_pcm_substream *substream,
spin_lock_irqsave(&bus->reg_lock, cookie);
if (start) {
- snd_hdac_stream_start(hdac_stream(stream), true);
+ snd_hdac_stream_start(hdac_stream(stream));
snd_hdac_stream_timecounter_init(hstr, 0);
} else {
snd_hdac_stream_stop(hdac_stream(stream));
@@ -1134,7 +1134,7 @@ static int skl_coupled_trigger(struct snd_pcm_substream *substream,
continue;
stream = get_hdac_ext_stream(s);
if (start)
- snd_hdac_stream_start(hdac_stream(stream), true);
+ snd_hdac_stream_start(hdac_stream(stream));
else
snd_hdac_stream_stop(hdac_stream(stream));
}
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
index b20643b8340125..96cfebded0724c 100644
--- a/sound/soc/intel/skylake/skl-topology.c
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -1663,11 +1663,10 @@ int skl_tplg_update_pipe_params(struct device *dev,
struct skl_module_cfg *
skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream);
struct snd_soc_dapm_path *p = NULL;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- w = dai->playback_widget;
snd_soc_dapm_widget_for_each_sink_path(w, p) {
if (p->connect && p->sink->power &&
!is_skl_dsp_widget_type(p->sink, dai->dev))
@@ -1680,7 +1679,6 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
}
}
} else {
- w = dai->capture_widget;
snd_soc_dapm_widget_for_each_source_path(w, p) {
if (p->connect && p->source->power &&
!is_skl_dsp_widget_type(p->source, dai->dev))
@@ -1744,14 +1742,12 @@ static struct skl_module_cfg *skl_get_mconfig_cap_cpr(
struct skl_module_cfg *
skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai, int stream)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream);
struct skl_module_cfg *mconfig;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- w = dai->playback_widget;
mconfig = skl_get_mconfig_pb_cpr(dai, w);
} else {
- w = dai->capture_widget;
mconfig = skl_get_mconfig_cap_cpr(dai, w);
}
return mconfig;
@@ -1905,20 +1901,13 @@ static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai,
int skl_tplg_be_update_params(struct snd_soc_dai *dai,
struct skl_pipe_params *params)
{
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, params->stream);
if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- w = dai->playback_widget;
-
return skl_tplg_be_set_src_pipe_params(dai, w, params);
-
} else {
- w = dai->capture_widget;
-
return skl_tplg_be_set_sink_pipe_params(dai, w, params);
}
-
- return 0;
}
static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = {
@@ -2978,7 +2967,7 @@ void skl_cleanup_resources(struct skl_dev *skl)
return;
card = soc_component->card;
- if (!card || !card->instantiated)
+ if (!snd_soc_card_is_instantiated(card))
return;
list_for_each_entry(w, &card->widgets, list) {
diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h
index 6db0fd7bad49cd..30a0977af94375 100644
--- a/sound/soc/intel/skylake/skl-topology.h
+++ b/sound/soc/intel/skylake/skl-topology.h
@@ -115,7 +115,10 @@ struct skl_cpr_gtw_cfg {
u32 dma_buffer_size;
u32 config_length;
/* not mandatory; required only for DMIC/I2S */
- u32 config_data[1];
+ struct {
+ u32 gtw_attrs;
+ u32 data[];
+ } config_data;
} __packed;
struct skl_dma_control {
diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c
index 700a18561a9405..640cebd2983e22 100644
--- a/sound/soc/kirkwood/kirkwood-dma.c
+++ b/sound/soc/kirkwood/kirkwood-dma.c
@@ -86,7 +86,7 @@ kirkwood_dma_conf_mbus_windows(void __iomem *base, int win,
/* try to find matching cs for current dma address */
for (i = 0; i < dram->num_cs; i++) {
- const struct mbus_dram_window *cs = dram->cs + i;
+ const struct mbus_dram_window *cs = &dram->cs[i];
if ((cs->base & 0xffff0000) < (dma & 0xffff0000)) {
writel(cs->base & 0xffff0000,
base + KIRKWOOD_AUDIO_WIN_BASE_REG(win));
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index b027fba8233df6..4baac72677d9ac 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -208,6 +208,29 @@ config SND_SOC_MTK_BTCVSD
Select Y if you have such device.
If unsure select "N".
+config SND_SOC_MT8188
+ tristate "ASoC support for MediaTek MT8188 chip"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on COMMON_CLK
+ select SND_SOC_MEDIATEK
+ select MFD_SYSCON if SND_SOC_MT6359
+ help
+ This adds ASoC platform driver support for MediaTek MT8188 chip
+ that can be used with other codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_MT8188_MT6359
+ tristate "ASoC Audio driver for MT8188 with MT6359 and I2S codecs"
+ depends on SND_SOC_MT8188 && MTK_PMIC_WRAP
+ select SND_SOC_MT6359
+ select SND_SOC_HDMI_CODEC
+ help
+ This adds support for ASoC machine driver for MediaTek MT8188
+ boards with the MT6359 and other I2S audio codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
config SND_SOC_MT8192
tristate "ASoC support for Mediatek MT8192 chip"
depends on ARCH_MEDIATEK
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index 5571c640a288c1..3de38cfc69e5f2 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -5,5 +5,6 @@ obj-$(CONFIG_SND_SOC_MT6797) += mt6797/
obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
obj-$(CONFIG_SND_SOC_MT8183) += mt8183/
obj-$(CONFIG_SND_SOC_MT8186) += mt8186/
+obj-$(CONFIG_SND_SOC_MT8188) += mt8188/
obj-$(CONFIG_SND_SOC_MT8192) += mt8192/
obj-$(CONFIG_SND_SOC_MT8195) += mt8195/
diff --git a/sound/soc/mediatek/common/Makefile b/sound/soc/mediatek/common/Makefile
index 576deb7f8cce2c..42e636c10c1ee7 100644
--- a/sound/soc/mediatek/common/Makefile
+++ b/sound/soc/mediatek/common/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
# platform driver
-snd-soc-mtk-common-objs := mtk-afe-platform-driver.o mtk-afe-fe-dai.o mtk-dsp-sof-common.o
+snd-soc-mtk-common-objs := mtk-afe-platform-driver.o mtk-afe-fe-dai.o mtk-dsp-sof-common.o mtk-soundcard-driver.o
obj-$(CONFIG_SND_SOC_MEDIATEK) += snd-soc-mtk-common.o
obj-$(CONFIG_SND_SOC_MTK_BTCVSD) += mtk-btcvsd.o
diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h
index ef83e78c22a84c..f51578b6c50a35 100644
--- a/sound/soc/mediatek/common/mtk-base-afe.h
+++ b/sound/soc/mediatek/common/mtk-base-afe.h
@@ -9,7 +9,26 @@
#ifndef _MTK_BASE_AFE_H_
#define _MTK_BASE_AFE_H_
+#include <linux/soc/mediatek/mtk_sip_svc.h>
+
#define MTK_STREAM_NUM (SNDRV_PCM_STREAM_LAST + 1)
+#define MTK_SIP_AUDIO_CONTROL MTK_SIP_SMC_CMD(0x517)
+
+/* SMC CALL Operations */
+enum mtk_audio_smc_call_op {
+ MTK_AUDIO_SMC_OP_INIT = 0,
+ MTK_AUDIO_SMC_OP_DRAM_REQUEST,
+ MTK_AUDIO_SMC_OP_DRAM_RELEASE,
+ MTK_AUDIO_SMC_OP_SRAM_REQUEST,
+ MTK_AUDIO_SMC_OP_SRAM_RELEASE,
+ MTK_AUDIO_SMC_OP_ADSP_REQUEST,
+ MTK_AUDIO_SMC_OP_ADSP_RELEASE,
+ MTK_AUDIO_SMC_OP_DOMAIN_SIDEBANDS,
+ MTK_AUDIO_SMC_OP_BTCVSD_WRITE,
+ MTK_AUDIO_SMC_OP_BTCVSD_UPDATE_CTRL_CLEAR,
+ MTK_AUDIO_SMC_OP_BTCVSD_UPDATE_CTRL_UNDERFLOW,
+ MTK_AUDIO_SMC_OP_NUM
+};
struct mtk_base_memif_data {
int id;
diff --git a/sound/soc/mediatek/common/mtk-dsp-sof-common.c b/sound/soc/mediatek/common/mtk-dsp-sof-common.c
index 8b1b623207beef..6fef16306f74ff 100644
--- a/sound/soc/mediatek/common/mtk-dsp-sof-common.c
+++ b/sound/soc/mediatek/common/mtk-dsp-sof-common.c
@@ -32,7 +32,7 @@ int mtk_sof_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
continue;
for_each_rtd_cpu_dais(runtime, j, cpu_dai) {
- if (cpu_dai->stream_active[conn->stream_dir] > 0) {
+ if (snd_soc_dai_stream_active(cpu_dai, conn->stream_dir) > 0) {
sof_dai_link = runtime->dai_link;
break;
}
@@ -111,21 +111,17 @@ int mtk_sof_card_late_probe(struct snd_soc_card *card)
for_each_rtd_cpu_dais(sof_rtd, j, cpu_dai) {
struct snd_soc_dapm_route route;
struct snd_soc_dapm_path *p = NULL;
- struct snd_soc_dapm_widget *play_widget =
- cpu_dai->playback_widget;
- struct snd_soc_dapm_widget *cap_widget =
- cpu_dai->capture_widget;
+ struct snd_soc_dapm_widget *widget = snd_soc_dai_get_widget(cpu_dai, conn->stream_dir);
+
memset(&route, 0, sizeof(route));
- if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE &&
- cap_widget) {
- snd_soc_dapm_widget_for_each_sink_path(cap_widget, p) {
+ if (conn->stream_dir == SNDRV_PCM_STREAM_CAPTURE && widget) {
+ snd_soc_dapm_widget_for_each_sink_path(widget, p) {
route.source = conn->sof_dma;
route.sink = p->sink->name;
snd_soc_dapm_add_routes(&card->dapm, &route, 1);
}
- } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK &&
- play_widget) {
- snd_soc_dapm_widget_for_each_source_path(play_widget, p) {
+ } else if (conn->stream_dir == SNDRV_PCM_STREAM_PLAYBACK && widget) {
+ snd_soc_dapm_widget_for_each_source_path(widget, p) {
route.source = p->source->name;
route.sink = conn->sof_dma;
snd_soc_dapm_add_routes(&card->dapm, &route, 1);
diff --git a/sound/soc/mediatek/common/mtk-soundcard-driver.c b/sound/soc/mediatek/common/mtk-soundcard-driver.c
new file mode 100644
index 00000000000000..7c55c2cb1f2142
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-soundcard-driver.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mtk-soundcard-driver.c -- MediaTek soundcard driver common
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <sound/soc.h>
+
+#include "mtk-soundcard-driver.h"
+
+static int set_card_codec_info(struct snd_soc_card *card,
+ struct device_node *sub_node,
+ struct snd_soc_dai_link *dai_link)
+{
+ struct device *dev = card->dev;
+ struct device_node *codec_node;
+ int ret;
+
+ codec_node = of_get_child_by_name(sub_node, "codec");
+ if (!codec_node)
+ return -EINVAL;
+
+ /* set card codec info */
+ ret = snd_soc_of_get_dai_link_codecs(dev, codec_node, dai_link);
+
+ of_node_put(codec_node);
+
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "%s: codec dai not found\n",
+ dai_link->name);
+
+ return 0;
+}
+
+int parse_dai_link_info(struct snd_soc_card *card)
+{
+ struct device *dev = card->dev;
+ struct device_node *sub_node;
+ struct snd_soc_dai_link *dai_link;
+ const char *dai_link_name;
+ int ret, i;
+
+ /* Loop over all the dai link sub nodes */
+ for_each_available_child_of_node(dev->of_node, sub_node) {
+ if (of_property_read_string(sub_node, "link-name",
+ &dai_link_name))
+ return -EINVAL;
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (!strcmp(dai_link_name, dai_link->name))
+ break;
+ }
+
+ if (i >= card->num_links)
+ return -EINVAL;
+
+ ret = set_card_codec_info(card, sub_node, dai_link);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(parse_dai_link_info);
+
+void clean_card_reference(struct snd_soc_card *card)
+{
+ struct snd_soc_dai_link *dai_link;
+ int i;
+
+ /* release codec reference gotten by set_card_codec_info */
+ for_each_card_prelinks(card, i, dai_link)
+ snd_soc_of_put_dai_link_codecs(dai_link);
+}
+EXPORT_SYMBOL_GPL(clean_card_reference);
diff --git a/sound/soc/mediatek/common/mtk-soundcard-driver.h b/sound/soc/mediatek/common/mtk-soundcard-driver.h
new file mode 100644
index 00000000000000..d92cac1d7b72a9
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-soundcard-driver.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mtk-soundcard-driver.h -- MediaTek soundcard driver common definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#ifndef _MTK_SOUNDCARD_DRIVER_H_
+#define _MTK_SOUNDCARD_DRIVER_H_
+
+int parse_dai_link_info(struct snd_soc_card *card);
+void clean_card_reference(struct snd_soc_card *card);
+#endif
diff --git a/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c b/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c
index 51f736f319e421..8a309b0734f75e 100644
--- a/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c
+++ b/sound/soc/mediatek/mt6797/mt6797-dai-pcm.c
@@ -183,6 +183,8 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai);
+ struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai);
unsigned int rate = params_rate(params);
unsigned int rate_reg = mt6797_rate_transform(afe->dev, rate, dai->id);
unsigned int pcm_con = 0;
@@ -193,10 +195,10 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream,
substream->stream,
rate,
rate_reg,
- dai->playback_widget->active,
- dai->capture_widget->active);
+ p->active,
+ c->active);
- if (dai->playback_widget->active || dai->capture_widget->active)
+ if (p->active || c->active)
return 0;
switch (dai->id) {
diff --git a/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c b/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c
index 38ce0e36cdb4b6..4e25287fc0e4d2 100644
--- a/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c
+++ b/sound/soc/mediatek/mt8183/mt8183-dai-pcm.c
@@ -183,6 +183,8 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai);
+ struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai);
unsigned int rate = params_rate(params);
unsigned int rate_reg = mt8183_rate_transform(afe->dev, rate, dai->id);
unsigned int pcm_con = 0;
@@ -193,10 +195,9 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream,
substream->stream,
rate,
rate_reg,
- dai->playback_widget->active,
- dai->capture_widget->active);
+ p->active, c->active);
- if (dai->playback_widget->active || dai->capture_widget->active)
+ if (p->active || c->active)
return 0;
switch (dai->id) {
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
index d7e94e6a19c704..41172a82103eec 100644
--- a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
@@ -2395,6 +2395,7 @@ static bool mt8186_is_volatile_reg(struct device *dev, unsigned int reg)
case AUDIO_TOP_CON1: /* reg bit controlled by CCF */
case AUDIO_TOP_CON2:
case AUDIO_TOP_CON3:
+ case AFE_DAC_CON0:
case AFE_DL1_CUR_MSB:
case AFE_DL1_CUR:
case AFE_DL1_END:
diff --git a/sound/soc/mediatek/mt8186/mt8186-dai-pcm.c b/sound/soc/mediatek/mt8186/mt8186-dai-pcm.c
index 41221a66111c79..a50aa294960b13 100644
--- a/sound/soc/mediatek/mt8186/mt8186-dai-pcm.c
+++ b/sound/soc/mediatek/mt8186/mt8186-dai-pcm.c
@@ -218,6 +218,8 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
struct mt8186_afe_private *afe_priv = afe->platform_priv;
+ struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai);
+ struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai);
int pcm_id = dai->id;
struct mtk_afe_pcm_priv *pcm_priv = afe_priv->dai_priv[pcm_id];
unsigned int rate = params_rate(params);
@@ -230,12 +232,11 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream,
unsigned int pcm_con = 0;
dev_dbg(afe->dev, "%s(), id %d, stream %d, widget active p %d, c %d\n",
- __func__, dai->id, substream->stream, dai->playback_widget->active,
- dai->capture_widget->active);
+ __func__, dai->id, substream->stream, p->active, c->active);
dev_dbg(afe->dev, "%s(), rate %d, rate_reg %d, data_width %d, wlen_width %d\n",
__func__, rate, rate_reg, data_width, wlen_width);
- if (dai->playback_widget->active || dai->capture_widget->active)
+ if (p->active || c->active)
return 0;
switch (dai->id) {
diff --git a/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c b/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c
index af44e331dae824..b333950aa3c395 100644
--- a/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c
+++ b/sound/soc/mediatek/mt8186/mt8186-mt6366-rt1019-rt5682s.c
@@ -140,7 +140,7 @@ static int primary_codec_init(struct snd_soc_pcm_runtime *rtd)
if (!priv->dmic_sel) {
dev_info(card->dev, "dmic_sel is null\n");
- return ret;
+ return 0;
}
ret = snd_soc_dapm_new_controls(&card->dapm, dmic_widgets,
diff --git a/sound/soc/mediatek/mt8188/Makefile b/sound/soc/mediatek/mt8188/Makefile
new file mode 100644
index 00000000000000..781e61cbb22b01
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# platform driver
+snd-soc-mt8188-afe-objs := \
+ mt8188-afe-clk.o \
+ mt8188-afe-pcm.o \
+ mt8188-audsys-clk.o \
+ mt8188-dai-adda.o \
+ mt8188-dai-etdm.o \
+ mt8188-dai-pcm.o
+
+obj-$(CONFIG_SND_SOC_MT8188) += snd-soc-mt8188-afe.o
+
+# machine driver
+obj-$(CONFIG_SND_SOC_MT8188_MT6359) += mt8188-mt6359.o
diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-clk.c b/sound/soc/mediatek/mt8188/mt8188-afe-clk.c
new file mode 100644
index 00000000000000..743d6a162cb9a8
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-clk.c
@@ -0,0 +1,658 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt8188-afe-clk.c -- MediaTek 8188 afe clock ctrl
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#include <linux/clk.h>
+
+#include "mt8188-afe-common.h"
+#include "mt8188-afe-clk.h"
+#include "mt8188-audsys-clk.h"
+#include "mt8188-reg.h"
+
+static const char *aud_clks[MT8188_CLK_NUM] = {
+ /* xtal */
+ [MT8188_CLK_XTAL_26M] = "clk26m",
+
+ /* pll */
+ [MT8188_CLK_APMIXED_APLL1] = "apll1",
+ [MT8188_CLK_APMIXED_APLL2] = "apll2",
+
+ /* divider */
+ [MT8188_CLK_TOP_APLL12_DIV0] = "apll12_div0",
+ [MT8188_CLK_TOP_APLL12_DIV1] = "apll12_div1",
+ [MT8188_CLK_TOP_APLL12_DIV2] = "apll12_div2",
+ [MT8188_CLK_TOP_APLL12_DIV3] = "apll12_div3",
+ [MT8188_CLK_TOP_APLL12_DIV9] = "apll12_div9",
+
+ /* mux */
+ [MT8188_CLK_TOP_A1SYS_HP_SEL] = "top_a1sys_hp",
+ [MT8188_CLK_TOP_AUD_INTBUS_SEL] = "top_aud_intbus",
+ [MT8188_CLK_TOP_AUDIO_H_SEL] = "top_audio_h",
+ [MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL] = "top_audio_local_bus",
+ [MT8188_CLK_TOP_DPTX_M_SEL] = "top_dptx",
+ [MT8188_CLK_TOP_I2SO1_M_SEL] = "top_i2so1",
+ [MT8188_CLK_TOP_I2SO2_M_SEL] = "top_i2so2",
+ [MT8188_CLK_TOP_I2SI1_M_SEL] = "top_i2si1",
+ [MT8188_CLK_TOP_I2SI2_M_SEL] = "top_i2si2",
+
+ /* clock gate */
+ [MT8188_CLK_ADSP_AUDIO_26M] = "adsp_audio_26m",
+ /* afe clock gate */
+ [MT8188_CLK_AUD_AFE] = "aud_afe",
+ [MT8188_CLK_AUD_APLL1_TUNER] = "aud_apll1_tuner",
+ [MT8188_CLK_AUD_APLL2_TUNER] = "aud_apll2_tuner",
+ [MT8188_CLK_AUD_APLL] = "aud_apll",
+ [MT8188_CLK_AUD_APLL2] = "aud_apll2",
+ [MT8188_CLK_AUD_DAC] = "aud_dac",
+ [MT8188_CLK_AUD_ADC] = "aud_adc",
+ [MT8188_CLK_AUD_DAC_HIRES] = "aud_dac_hires",
+ [MT8188_CLK_AUD_A1SYS_HP] = "aud_a1sys_hp",
+ [MT8188_CLK_AUD_ADC_HIRES] = "aud_adc_hires",
+ [MT8188_CLK_AUD_I2SIN] = "aud_i2sin",
+ [MT8188_CLK_AUD_TDM_IN] = "aud_tdm_in",
+ [MT8188_CLK_AUD_I2S_OUT] = "aud_i2s_out",
+ [MT8188_CLK_AUD_TDM_OUT] = "aud_tdm_out",
+ [MT8188_CLK_AUD_HDMI_OUT] = "aud_hdmi_out",
+ [MT8188_CLK_AUD_ASRC11] = "aud_asrc11",
+ [MT8188_CLK_AUD_ASRC12] = "aud_asrc12",
+ [MT8188_CLK_AUD_A1SYS] = "aud_a1sys",
+ [MT8188_CLK_AUD_A2SYS] = "aud_a2sys",
+ [MT8188_CLK_AUD_PCMIF] = "aud_pcmif",
+ [MT8188_CLK_AUD_MEMIF_UL1] = "aud_memif_ul1",
+ [MT8188_CLK_AUD_MEMIF_UL2] = "aud_memif_ul2",
+ [MT8188_CLK_AUD_MEMIF_UL3] = "aud_memif_ul3",
+ [MT8188_CLK_AUD_MEMIF_UL4] = "aud_memif_ul4",
+ [MT8188_CLK_AUD_MEMIF_UL5] = "aud_memif_ul5",
+ [MT8188_CLK_AUD_MEMIF_UL6] = "aud_memif_ul6",
+ [MT8188_CLK_AUD_MEMIF_UL8] = "aud_memif_ul8",
+ [MT8188_CLK_AUD_MEMIF_UL9] = "aud_memif_ul9",
+ [MT8188_CLK_AUD_MEMIF_UL10] = "aud_memif_ul10",
+ [MT8188_CLK_AUD_MEMIF_DL2] = "aud_memif_dl2",
+ [MT8188_CLK_AUD_MEMIF_DL3] = "aud_memif_dl3",
+ [MT8188_CLK_AUD_MEMIF_DL6] = "aud_memif_dl6",
+ [MT8188_CLK_AUD_MEMIF_DL7] = "aud_memif_dl7",
+ [MT8188_CLK_AUD_MEMIF_DL8] = "aud_memif_dl8",
+ [MT8188_CLK_AUD_MEMIF_DL10] = "aud_memif_dl10",
+ [MT8188_CLK_AUD_MEMIF_DL11] = "aud_memif_dl11",
+};
+
+struct mt8188_afe_tuner_cfg {
+ unsigned int id;
+ int apll_div_reg;
+ unsigned int apll_div_shift;
+ unsigned int apll_div_maskbit;
+ unsigned int apll_div_default;
+ int ref_ck_sel_reg;
+ unsigned int ref_ck_sel_shift;
+ unsigned int ref_ck_sel_maskbit;
+ unsigned int ref_ck_sel_default;
+ int tuner_en_reg;
+ unsigned int tuner_en_shift;
+ unsigned int tuner_en_maskbit;
+ int upper_bound_reg;
+ unsigned int upper_bound_shift;
+ unsigned int upper_bound_maskbit;
+ unsigned int upper_bound_default;
+ spinlock_t ctrl_lock; /* lock for apll tuner ctrl*/
+ int ref_cnt;
+};
+
+static struct mt8188_afe_tuner_cfg
+ mt8188_afe_tuner_cfgs[MT8188_AUD_PLL_NUM] = {
+ [MT8188_AUD_PLL1] = {
+ .id = MT8188_AUD_PLL1,
+ .apll_div_reg = AFE_APLL_TUNER_CFG,
+ .apll_div_shift = 4,
+ .apll_div_maskbit = 0xf,
+ .apll_div_default = 0x7,
+ .ref_ck_sel_reg = AFE_APLL_TUNER_CFG,
+ .ref_ck_sel_shift = 1,
+ .ref_ck_sel_maskbit = 0x3,
+ .ref_ck_sel_default = 0x2,
+ .tuner_en_reg = AFE_APLL_TUNER_CFG,
+ .tuner_en_shift = 0,
+ .tuner_en_maskbit = 0x1,
+ .upper_bound_reg = AFE_APLL_TUNER_CFG,
+ .upper_bound_shift = 8,
+ .upper_bound_maskbit = 0xff,
+ .upper_bound_default = 0x3,
+ },
+ [MT8188_AUD_PLL2] = {
+ .id = MT8188_AUD_PLL2,
+ .apll_div_reg = AFE_APLL_TUNER_CFG1,
+ .apll_div_shift = 4,
+ .apll_div_maskbit = 0xf,
+ .apll_div_default = 0x7,
+ .ref_ck_sel_reg = AFE_APLL_TUNER_CFG1,
+ .ref_ck_sel_shift = 1,
+ .ref_ck_sel_maskbit = 0x3,
+ .ref_ck_sel_default = 0x1,
+ .tuner_en_reg = AFE_APLL_TUNER_CFG1,
+ .tuner_en_shift = 0,
+ .tuner_en_maskbit = 0x1,
+ .upper_bound_reg = AFE_APLL_TUNER_CFG1,
+ .upper_bound_shift = 8,
+ .upper_bound_maskbit = 0xff,
+ .upper_bound_default = 0x3,
+ },
+ [MT8188_AUD_PLL3] = {
+ .id = MT8188_AUD_PLL3,
+ .apll_div_reg = AFE_EARC_APLL_TUNER_CFG,
+ .apll_div_shift = 4,
+ .apll_div_maskbit = 0x3f,
+ .apll_div_default = 0x3,
+ .ref_ck_sel_reg = AFE_EARC_APLL_TUNER_CFG,
+ .ref_ck_sel_shift = 24,
+ .ref_ck_sel_maskbit = 0x3,
+ .ref_ck_sel_default = 0x0,
+ .tuner_en_reg = AFE_EARC_APLL_TUNER_CFG,
+ .tuner_en_shift = 0,
+ .tuner_en_maskbit = 0x1,
+ .upper_bound_reg = AFE_EARC_APLL_TUNER_CFG,
+ .upper_bound_shift = 12,
+ .upper_bound_maskbit = 0xff,
+ .upper_bound_default = 0x4,
+ },
+ [MT8188_AUD_PLL4] = {
+ .id = MT8188_AUD_PLL4,
+ .apll_div_reg = AFE_SPDIFIN_APLL_TUNER_CFG,
+ .apll_div_shift = 4,
+ .apll_div_maskbit = 0x3f,
+ .apll_div_default = 0x7,
+ .ref_ck_sel_reg = AFE_SPDIFIN_APLL_TUNER_CFG1,
+ .ref_ck_sel_shift = 8,
+ .ref_ck_sel_maskbit = 0x1,
+ .ref_ck_sel_default = 0,
+ .tuner_en_reg = AFE_SPDIFIN_APLL_TUNER_CFG,
+ .tuner_en_shift = 0,
+ .tuner_en_maskbit = 0x1,
+ .upper_bound_reg = AFE_SPDIFIN_APLL_TUNER_CFG,
+ .upper_bound_shift = 12,
+ .upper_bound_maskbit = 0xff,
+ .upper_bound_default = 0x4,
+ },
+ [MT8188_AUD_PLL5] = {
+ .id = MT8188_AUD_PLL5,
+ .apll_div_reg = AFE_LINEIN_APLL_TUNER_CFG,
+ .apll_div_shift = 4,
+ .apll_div_maskbit = 0x3f,
+ .apll_div_default = 0x3,
+ .ref_ck_sel_reg = AFE_LINEIN_APLL_TUNER_CFG,
+ .ref_ck_sel_shift = 24,
+ .ref_ck_sel_maskbit = 0x1,
+ .ref_ck_sel_default = 0,
+ .tuner_en_reg = AFE_LINEIN_APLL_TUNER_CFG,
+ .tuner_en_shift = 0,
+ .tuner_en_maskbit = 0x1,
+ .upper_bound_reg = AFE_LINEIN_APLL_TUNER_CFG,
+ .upper_bound_shift = 12,
+ .upper_bound_maskbit = 0xff,
+ .upper_bound_default = 0x4,
+ },
+};
+
+static struct mt8188_afe_tuner_cfg *mt8188_afe_found_apll_tuner(unsigned int id)
+{
+ if (id >= MT8188_AUD_PLL_NUM)
+ return NULL;
+
+ return &mt8188_afe_tuner_cfgs[id];
+}
+
+static int mt8188_afe_init_apll_tuner(unsigned int id)
+{
+ struct mt8188_afe_tuner_cfg *cfg = mt8188_afe_found_apll_tuner(id);
+
+ if (!cfg)
+ return -EINVAL;
+
+ cfg->ref_cnt = 0;
+ spin_lock_init(&cfg->ctrl_lock);
+
+ return 0;
+}
+
+static int mt8188_afe_setup_apll_tuner(struct mtk_base_afe *afe, unsigned int id)
+{
+ const struct mt8188_afe_tuner_cfg *cfg = mt8188_afe_found_apll_tuner(id);
+
+ if (!cfg)
+ return -EINVAL;
+
+ regmap_update_bits(afe->regmap,
+ cfg->apll_div_reg,
+ cfg->apll_div_maskbit << cfg->apll_div_shift,
+ cfg->apll_div_default << cfg->apll_div_shift);
+
+ regmap_update_bits(afe->regmap,
+ cfg->ref_ck_sel_reg,
+ cfg->ref_ck_sel_maskbit << cfg->ref_ck_sel_shift,
+ cfg->ref_ck_sel_default << cfg->ref_ck_sel_shift);
+
+ regmap_update_bits(afe->regmap,
+ cfg->upper_bound_reg,
+ cfg->upper_bound_maskbit << cfg->upper_bound_shift,
+ cfg->upper_bound_default << cfg->upper_bound_shift);
+
+ return 0;
+}
+
+static int mt8188_afe_enable_tuner_clk(struct mtk_base_afe *afe,
+ unsigned int id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ switch (id) {
+ case MT8188_AUD_PLL1:
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL]);
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL1_TUNER]);
+ break;
+ case MT8188_AUD_PLL2:
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2]);
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2_TUNER]);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mt8188_afe_disable_tuner_clk(struct mtk_base_afe *afe,
+ unsigned int id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ switch (id) {
+ case MT8188_AUD_PLL1:
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL1_TUNER]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL]);
+ break;
+ case MT8188_AUD_PLL2:
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2_TUNER]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2]);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mt8188_afe_enable_apll_tuner(struct mtk_base_afe *afe, unsigned int id)
+{
+ struct mt8188_afe_tuner_cfg *cfg = mt8188_afe_found_apll_tuner(id);
+ unsigned long flags;
+ int ret;
+
+ if (!cfg)
+ return -EINVAL;
+
+ ret = mt8188_afe_setup_apll_tuner(afe, id);
+ if (ret)
+ return ret;
+
+ ret = mt8188_afe_enable_tuner_clk(afe, id);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&cfg->ctrl_lock, flags);
+
+ cfg->ref_cnt++;
+ if (cfg->ref_cnt == 1)
+ regmap_update_bits(afe->regmap,
+ cfg->tuner_en_reg,
+ cfg->tuner_en_maskbit << cfg->tuner_en_shift,
+ BIT(cfg->tuner_en_shift));
+
+ spin_unlock_irqrestore(&cfg->ctrl_lock, flags);
+
+ return 0;
+}
+
+static int mt8188_afe_disable_apll_tuner(struct mtk_base_afe *afe, unsigned int id)
+{
+ struct mt8188_afe_tuner_cfg *cfg = mt8188_afe_found_apll_tuner(id);
+ unsigned long flags;
+ int ret;
+
+ if (!cfg)
+ return -EINVAL;
+
+ spin_lock_irqsave(&cfg->ctrl_lock, flags);
+
+ cfg->ref_cnt--;
+ if (cfg->ref_cnt == 0)
+ regmap_update_bits(afe->regmap,
+ cfg->tuner_en_reg,
+ cfg->tuner_en_maskbit << cfg->tuner_en_shift,
+ 0 << cfg->tuner_en_shift);
+ else if (cfg->ref_cnt < 0)
+ cfg->ref_cnt = 0;
+
+ spin_unlock_irqrestore(&cfg->ctrl_lock, flags);
+
+ ret = mt8188_afe_disable_tuner_clk(afe, id);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int mt8188_afe_get_mclk_source_clk_id(int sel)
+{
+ switch (sel) {
+ case MT8188_MCK_SEL_26M:
+ return MT8188_CLK_XTAL_26M;
+ case MT8188_MCK_SEL_APLL1:
+ return MT8188_CLK_APMIXED_APLL1;
+ case MT8188_MCK_SEL_APLL2:
+ return MT8188_CLK_APMIXED_APLL2;
+ default:
+ return -EINVAL;
+ }
+}
+
+int mt8188_afe_get_mclk_source_rate(struct mtk_base_afe *afe, int apll)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int clk_id = mt8188_afe_get_mclk_source_clk_id(apll);
+
+ if (clk_id < 0) {
+ dev_dbg(afe->dev, "invalid clk id\n");
+ return 0;
+ }
+
+ return clk_get_rate(afe_priv->clk[clk_id]);
+}
+
+int mt8188_afe_get_default_mclk_source_by_rate(int rate)
+{
+ return ((rate % 8000) == 0) ?
+ MT8188_MCK_SEL_APLL1 : MT8188_MCK_SEL_APLL2;
+}
+
+int mt8188_afe_init_clock(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int i, ret;
+
+ ret = mt8188_audsys_clk_register(afe);
+ if (ret) {
+ dev_err(afe->dev, "register audsys clk fail %d\n", ret);
+ return ret;
+ }
+
+ afe_priv->clk =
+ devm_kcalloc(afe->dev, MT8188_CLK_NUM, sizeof(*afe_priv->clk),
+ GFP_KERNEL);
+ if (!afe_priv->clk)
+ return -ENOMEM;
+
+ for (i = 0; i < MT8188_CLK_NUM; i++) {
+ afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
+ if (IS_ERR(afe_priv->clk[i])) {
+ dev_err(afe->dev, "%s(), devm_clk_get %s fail, ret %ld\n",
+ __func__, aud_clks[i],
+ PTR_ERR(afe_priv->clk[i]));
+ return PTR_ERR(afe_priv->clk[i]);
+ }
+ }
+
+ /* initial tuner */
+ for (i = 0; i < MT8188_AUD_PLL_NUM; i++) {
+ ret = mt8188_afe_init_apll_tuner(i);
+ if (ret) {
+ dev_info(afe->dev, "%s(), init apll_tuner%d failed",
+ __func__, (i + 1));
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+void mt8188_afe_deinit_clock(void *priv)
+{
+ struct mtk_base_afe *afe = priv;
+
+ mt8188_audsys_clk_unregister(afe);
+}
+
+int mt8188_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk)
+{
+ int ret;
+
+ if (clk) {
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_dbg(afe->dev, "%s(), failed to enable clk\n",
+ __func__);
+ return ret;
+ }
+ } else {
+ dev_dbg(afe->dev, "NULL clk\n");
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt8188_afe_enable_clk);
+
+void mt8188_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk)
+{
+ if (clk)
+ clk_disable_unprepare(clk);
+ else
+ dev_dbg(afe->dev, "NULL clk\n");
+}
+EXPORT_SYMBOL_GPL(mt8188_afe_disable_clk);
+
+int mt8188_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk,
+ unsigned int rate)
+{
+ int ret;
+
+ if (clk) {
+ ret = clk_set_rate(clk, rate);
+ if (ret) {
+ dev_dbg(afe->dev, "%s(), failed to set clk rate\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int mt8188_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk,
+ struct clk *parent)
+{
+ int ret;
+
+ if (clk && parent) {
+ ret = clk_set_parent(clk, parent);
+ if (ret) {
+ dev_dbg(afe->dev, "%s(), failed to set clk parent\n",
+ __func__);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int get_top_cg_reg(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8188_TOP_CG_A1SYS_TIMING:
+ case MT8188_TOP_CG_A2SYS_TIMING:
+ case MT8188_TOP_CG_26M_TIMING:
+ return ASYS_TOP_CON;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_mask(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8188_TOP_CG_A1SYS_TIMING:
+ return ASYS_TOP_CON_A1SYS_TIMING_ON;
+ case MT8188_TOP_CG_A2SYS_TIMING:
+ return ASYS_TOP_CON_A2SYS_TIMING_ON;
+ case MT8188_TOP_CG_26M_TIMING:
+ return ASYS_TOP_CON_26M_TIMING_ON;
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_on_val(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8188_TOP_CG_A1SYS_TIMING:
+ case MT8188_TOP_CG_A2SYS_TIMING:
+ case MT8188_TOP_CG_26M_TIMING:
+ return get_top_cg_mask(cg_type);
+ default:
+ return 0;
+ }
+}
+
+static unsigned int get_top_cg_off_val(unsigned int cg_type)
+{
+ switch (cg_type) {
+ case MT8188_TOP_CG_A1SYS_TIMING:
+ case MT8188_TOP_CG_A2SYS_TIMING:
+ case MT8188_TOP_CG_26M_TIMING:
+ return 0;
+ default:
+ return get_top_cg_mask(cg_type);
+ }
+}
+
+static int mt8188_afe_enable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type)
+{
+ unsigned int reg = get_top_cg_reg(cg_type);
+ unsigned int mask = get_top_cg_mask(cg_type);
+ unsigned int val = get_top_cg_on_val(cg_type);
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+
+ return 0;
+}
+
+static int mt8188_afe_disable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type)
+{
+ unsigned int reg = get_top_cg_reg(cg_type);
+ unsigned int mask = get_top_cg_mask(cg_type);
+ unsigned int val = get_top_cg_off_val(cg_type);
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+
+ return 0;
+}
+
+int mt8188_afe_enable_reg_rw_clk(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ /* bus clock for AFE external access, like DRAM */
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL]);
+
+ /* bus clock for AFE internal access, like AFE SRAM */
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUD_INTBUS_SEL]);
+
+ /* audio 26m clock source */
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_ADSP_AUDIO_26M]);
+
+ /* AFE hw clock */
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE]);
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS_HP]);
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]);
+
+ return 0;
+}
+
+int mt8188_afe_disable_reg_rw_clk(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS_HP]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_ADSP_AUDIO_26M]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUD_INTBUS_SEL]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL]);
+
+ return 0;
+}
+
+static int mt8188_afe_enable_afe_on(struct mtk_base_afe *afe)
+{
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
+ return 0;
+}
+
+static int mt8188_afe_disable_afe_on(struct mtk_base_afe *afe)
+{
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x0);
+ return 0;
+}
+
+static int mt8188_afe_enable_timing_sys(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]);
+ mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A2SYS]);
+
+ mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING);
+ mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING);
+ mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_26M_TIMING);
+
+ return 0;
+}
+
+static int mt8188_afe_disable_timing_sys(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]);
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A2SYS]);
+
+ mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_26M_TIMING);
+ mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING);
+ mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING);
+
+ return 0;
+}
+
+int mt8188_afe_enable_main_clock(struct mtk_base_afe *afe)
+{
+ mt8188_afe_enable_timing_sys(afe);
+
+ mt8188_afe_enable_afe_on(afe);
+
+ mt8188_afe_enable_apll_tuner(afe, MT8188_AUD_PLL1);
+ mt8188_afe_enable_apll_tuner(afe, MT8188_AUD_PLL2);
+
+ return 0;
+}
+
+int mt8188_afe_disable_main_clock(struct mtk_base_afe *afe)
+{
+ mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL2);
+ mt8188_afe_disable_apll_tuner(afe, MT8188_AUD_PLL1);
+
+ mt8188_afe_disable_afe_on(afe);
+
+ mt8188_afe_disable_timing_sys(afe);
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-clk.h b/sound/soc/mediatek/mt8188/mt8188-afe-clk.h
new file mode 100644
index 00000000000000..084fdfb1d877a4
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-clk.h
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8188-afe-clk.h -- MediaTek 8188 afe clock ctrl definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#ifndef _MT8188_AFE_CLK_H_
+#define _MT8188_AFE_CLK_H_
+
+enum {
+ /* xtal */
+ MT8188_CLK_XTAL_26M,
+ /* pll */
+ MT8188_CLK_APMIXED_APLL1,
+ MT8188_CLK_APMIXED_APLL2,
+ /* divider */
+ MT8188_CLK_TOP_APLL12_DIV0,
+ MT8188_CLK_TOP_APLL12_DIV1,
+ MT8188_CLK_TOP_APLL12_DIV2,
+ MT8188_CLK_TOP_APLL12_DIV3,
+ MT8188_CLK_TOP_APLL12_DIV9,
+ /* mux */
+ MT8188_CLK_TOP_A1SYS_HP_SEL,
+ MT8188_CLK_TOP_AUD_INTBUS_SEL,
+ MT8188_CLK_TOP_AUDIO_H_SEL,
+ MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL,
+ MT8188_CLK_TOP_DPTX_M_SEL,
+ MT8188_CLK_TOP_I2SO1_M_SEL,
+ MT8188_CLK_TOP_I2SO2_M_SEL,
+ MT8188_CLK_TOP_I2SI1_M_SEL,
+ MT8188_CLK_TOP_I2SI2_M_SEL,
+ /* clock gate */
+ MT8188_CLK_ADSP_AUDIO_26M,
+ MT8188_CLK_AUD_AFE,
+ MT8188_CLK_AUD_APLL1_TUNER,
+ MT8188_CLK_AUD_APLL2_TUNER,
+ MT8188_CLK_AUD_TOP0_SPDF,
+ MT8188_CLK_AUD_APLL,
+ MT8188_CLK_AUD_APLL2,
+ MT8188_CLK_AUD_DAC,
+ MT8188_CLK_AUD_ADC,
+ MT8188_CLK_AUD_DAC_HIRES,
+ MT8188_CLK_AUD_A1SYS_HP,
+ MT8188_CLK_AUD_ADC_HIRES,
+ MT8188_CLK_AUD_I2SIN,
+ MT8188_CLK_AUD_TDM_IN,
+ MT8188_CLK_AUD_I2S_OUT,
+ MT8188_CLK_AUD_TDM_OUT,
+ MT8188_CLK_AUD_HDMI_OUT,
+ MT8188_CLK_AUD_ASRC11,
+ MT8188_CLK_AUD_ASRC12,
+ MT8188_CLK_AUD_A1SYS,
+ MT8188_CLK_AUD_A2SYS,
+ MT8188_CLK_AUD_PCMIF,
+ MT8188_CLK_AUD_MEMIF_UL1,
+ MT8188_CLK_AUD_MEMIF_UL2,
+ MT8188_CLK_AUD_MEMIF_UL3,
+ MT8188_CLK_AUD_MEMIF_UL4,
+ MT8188_CLK_AUD_MEMIF_UL5,
+ MT8188_CLK_AUD_MEMIF_UL6,
+ MT8188_CLK_AUD_MEMIF_UL8,
+ MT8188_CLK_AUD_MEMIF_UL9,
+ MT8188_CLK_AUD_MEMIF_UL10,
+ MT8188_CLK_AUD_MEMIF_DL2,
+ MT8188_CLK_AUD_MEMIF_DL3,
+ MT8188_CLK_AUD_MEMIF_DL6,
+ MT8188_CLK_AUD_MEMIF_DL7,
+ MT8188_CLK_AUD_MEMIF_DL8,
+ MT8188_CLK_AUD_MEMIF_DL10,
+ MT8188_CLK_AUD_MEMIF_DL11,
+ MT8188_CLK_NUM,
+};
+
+enum {
+ MT8188_AUD_PLL1,
+ MT8188_AUD_PLL2,
+ MT8188_AUD_PLL3,
+ MT8188_AUD_PLL4,
+ MT8188_AUD_PLL5,
+ MT8188_AUD_PLL_NUM,
+};
+
+enum {
+ MT8188_MCK_SEL_26M,
+ MT8188_MCK_SEL_APLL1,
+ MT8188_MCK_SEL_APLL2,
+ MT8188_MCK_SEL_APLL3,
+ MT8188_MCK_SEL_APLL4,
+ MT8188_MCK_SEL_APLL5,
+ MT8188_MCK_SEL_NUM,
+};
+
+struct mtk_base_afe;
+
+int mt8188_afe_get_mclk_source_clk_id(int sel);
+int mt8188_afe_get_mclk_source_rate(struct mtk_base_afe *afe, int apll);
+int mt8188_afe_get_default_mclk_source_by_rate(int rate);
+int mt8188_afe_init_clock(struct mtk_base_afe *afe);
+void mt8188_afe_deinit_clock(void *priv);
+int mt8188_afe_enable_clk(struct mtk_base_afe *afe, struct clk *clk);
+void mt8188_afe_disable_clk(struct mtk_base_afe *afe, struct clk *clk);
+int mt8188_afe_set_clk_rate(struct mtk_base_afe *afe, struct clk *clk,
+ unsigned int rate);
+int mt8188_afe_set_clk_parent(struct mtk_base_afe *afe, struct clk *clk,
+ struct clk *parent);
+int mt8188_afe_enable_main_clock(struct mtk_base_afe *afe);
+int mt8188_afe_disable_main_clock(struct mtk_base_afe *afe);
+int mt8188_afe_enable_reg_rw_clk(struct mtk_base_afe *afe);
+int mt8188_afe_disable_reg_rw_clk(struct mtk_base_afe *afe);
+
+#endif
diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-common.h b/sound/soc/mediatek/mt8188/mt8188-afe-common.h
new file mode 100644
index 00000000000000..eb7e57c239bddd
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-common.h
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8188-afe-common.h -- MediaTek 8188 audio driver definitions
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#ifndef _MT_8188_AFE_COMMON_H_
+#define _MT_8188_AFE_COMMON_H_
+
+#include <linux/list.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "../common/mtk-base-afe.h"
+
+enum {
+ MT8188_DAI_START,
+ MT8188_AFE_MEMIF_START = MT8188_DAI_START,
+ MT8188_AFE_MEMIF_DL2 = MT8188_AFE_MEMIF_START,
+ MT8188_AFE_MEMIF_DL3,
+ MT8188_AFE_MEMIF_DL6,
+ MT8188_AFE_MEMIF_DL7,
+ MT8188_AFE_MEMIF_DL8,
+ MT8188_AFE_MEMIF_DL10,
+ MT8188_AFE_MEMIF_DL11,
+ MT8188_AFE_MEMIF_UL_START,
+ MT8188_AFE_MEMIF_UL1 = MT8188_AFE_MEMIF_UL_START,
+ MT8188_AFE_MEMIF_UL2,
+ MT8188_AFE_MEMIF_UL3,
+ MT8188_AFE_MEMIF_UL4,
+ MT8188_AFE_MEMIF_UL5,
+ MT8188_AFE_MEMIF_UL6,
+ MT8188_AFE_MEMIF_UL8,
+ MT8188_AFE_MEMIF_UL9,
+ MT8188_AFE_MEMIF_UL10,
+ MT8188_AFE_MEMIF_END,
+ MT8188_AFE_MEMIF_NUM = (MT8188_AFE_MEMIF_END - MT8188_AFE_MEMIF_START),
+ MT8188_AFE_IO_START = MT8188_AFE_MEMIF_END,
+ MT8188_AFE_IO_ADDA = MT8188_AFE_IO_START,
+ MT8188_AFE_IO_DMIC_IN,
+ MT8188_AFE_IO_DPTX,
+ MT8188_AFE_IO_ETDM_START,
+ MT8188_AFE_IO_ETDM1_IN = MT8188_AFE_IO_ETDM_START,
+ MT8188_AFE_IO_ETDM2_IN,
+ MT8188_AFE_IO_ETDM1_OUT,
+ MT8188_AFE_IO_ETDM2_OUT,
+ MT8188_AFE_IO_ETDM3_OUT,
+ MT8188_AFE_IO_ETDM_END,
+ MT8188_AFE_IO_ETDM_NUM =
+ (MT8188_AFE_IO_ETDM_END - MT8188_AFE_IO_ETDM_START),
+ MT8188_AFE_IO_PCM = MT8188_AFE_IO_ETDM_END,
+ MT8188_AFE_IO_END,
+ MT8188_AFE_IO_NUM = (MT8188_AFE_IO_END - MT8188_AFE_IO_START),
+ MT8188_DAI_END = MT8188_AFE_IO_END,
+ MT8188_DAI_NUM = (MT8188_DAI_END - MT8188_DAI_START),
+};
+
+enum {
+ MT8188_TOP_CG_A1SYS_TIMING,
+ MT8188_TOP_CG_A2SYS_TIMING,
+ MT8188_TOP_CG_26M_TIMING,
+ MT8188_TOP_CG_NUM,
+};
+
+enum {
+ MT8188_AFE_IRQ_1,
+ MT8188_AFE_IRQ_2,
+ MT8188_AFE_IRQ_3,
+ MT8188_AFE_IRQ_8,
+ MT8188_AFE_IRQ_9,
+ MT8188_AFE_IRQ_10,
+ MT8188_AFE_IRQ_13,
+ MT8188_AFE_IRQ_14,
+ MT8188_AFE_IRQ_15,
+ MT8188_AFE_IRQ_16,
+ MT8188_AFE_IRQ_17,
+ MT8188_AFE_IRQ_18,
+ MT8188_AFE_IRQ_19,
+ MT8188_AFE_IRQ_20,
+ MT8188_AFE_IRQ_21,
+ MT8188_AFE_IRQ_22,
+ MT8188_AFE_IRQ_23,
+ MT8188_AFE_IRQ_24,
+ MT8188_AFE_IRQ_25,
+ MT8188_AFE_IRQ_26,
+ MT8188_AFE_IRQ_27,
+ MT8188_AFE_IRQ_28,
+ MT8188_AFE_IRQ_NUM,
+};
+
+enum {
+ MT8188_ETDM_OUT1_1X_EN = 9,
+ MT8188_ETDM_OUT2_1X_EN = 10,
+ MT8188_ETDM_OUT3_1X_EN = 11,
+ MT8188_ETDM_IN1_1X_EN = 12,
+ MT8188_ETDM_IN2_1X_EN = 13,
+ MT8188_ETDM_IN1_NX_EN = 25,
+ MT8188_ETDM_IN2_NX_EN = 26,
+};
+
+enum {
+ MT8188_MTKAIF_MISO_0,
+ MT8188_MTKAIF_MISO_1,
+ MT8188_MTKAIF_MISO_NUM,
+};
+
+struct mtk_dai_memif_irq_priv {
+ unsigned int asys_timing_sel;
+};
+
+struct mtkaif_param {
+ bool mtkaif_calibration_ok;
+ int mtkaif_chosen_phase[MT8188_MTKAIF_MISO_NUM];
+ int mtkaif_phase_cycle[MT8188_MTKAIF_MISO_NUM];
+ int mtkaif_dmic_on;
+};
+
+struct clk;
+
+struct mt8188_afe_private {
+ struct clk **clk;
+ struct clk_lookup **lookup;
+ struct regmap *topckgen;
+ int pm_runtime_bypass_reg_ctl;
+ spinlock_t afe_ctrl_lock; /* Lock for afe control */
+ struct mtk_dai_memif_irq_priv irq_priv[MT8188_AFE_IRQ_NUM];
+ struct mtkaif_param mtkaif_params;
+
+ /* dai */
+ void *dai_priv[MT8188_DAI_NUM];
+};
+
+int mt8188_afe_fs_timing(unsigned int rate);
+/* dai register */
+int mt8188_dai_adda_register(struct mtk_base_afe *afe);
+int mt8188_dai_etdm_register(struct mtk_base_afe *afe);
+int mt8188_dai_pcm_register(struct mtk_base_afe *afe);
+
+#define MT8188_SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put, id) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_enum_double, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .device = id, \
+ .private_value = (unsigned long)&(xenum), \
+}
+
+#endif
diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
new file mode 100644
index 00000000000000..e8e84de8654220
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
@@ -0,0 +1,3358 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC AFE platform driver for 8188
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <sound/pcm_params.h>
+#include "mt8188-afe-common.h"
+#include "mt8188-afe-clk.h"
+#include "mt8188-reg.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+#define MT8188_MEMIF_BUFFER_BYTES_ALIGN (0x40)
+#define MT8188_MEMIF_DL7_MAX_PERIOD_SIZE (0x3fff)
+
+#define MEMIF_AXI_MINLEN 9 /* register default value */
+
+struct mtk_dai_memif_priv {
+ unsigned int asys_timing_sel;
+ unsigned int fs_timing;
+};
+
+static const struct snd_pcm_hardware mt8188_afe_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 64,
+ .period_bytes_max = 256 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 256 * 2 * 1024,
+};
+
+struct mt8188_afe_rate {
+ unsigned int rate;
+ unsigned int reg_value;
+};
+
+static const struct mt8188_afe_rate mt8188_afe_rates[] = {
+ { .rate = 8000, .reg_value = 0, },
+ { .rate = 12000, .reg_value = 1, },
+ { .rate = 16000, .reg_value = 2, },
+ { .rate = 24000, .reg_value = 3, },
+ { .rate = 32000, .reg_value = 4, },
+ { .rate = 48000, .reg_value = 5, },
+ { .rate = 96000, .reg_value = 6, },
+ { .rate = 192000, .reg_value = 7, },
+ { .rate = 384000, .reg_value = 8, },
+ { .rate = 7350, .reg_value = 16, },
+ { .rate = 11025, .reg_value = 17, },
+ { .rate = 14700, .reg_value = 18, },
+ { .rate = 22050, .reg_value = 19, },
+ { .rate = 29400, .reg_value = 20, },
+ { .rate = 44100, .reg_value = 21, },
+ { .rate = 88200, .reg_value = 22, },
+ { .rate = 176400, .reg_value = 23, },
+ { .rate = 352800, .reg_value = 24, },
+};
+
+int mt8188_afe_fs_timing(unsigned int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt8188_afe_rates); i++)
+ if (mt8188_afe_rates[i].rate == rate)
+ return mt8188_afe_rates[i].reg_value;
+
+ return -EINVAL;
+}
+
+static int mt8188_memif_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_component *component = NULL;
+ struct mtk_base_afe *afe = NULL;
+ struct mt8188_afe_private *afe_priv = NULL;
+ struct mtk_base_afe_memif *memif = NULL;
+ struct mtk_dai_memif_priv *memif_priv = NULL;
+ int fs = mt8188_afe_fs_timing(rate);
+ int id = asoc_rtd_to_cpu(rtd, 0)->id;
+
+ if (id < 0)
+ return -EINVAL;
+
+ component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ if (!component)
+ return -EINVAL;
+
+ afe = snd_soc_component_get_drvdata(component);
+ memif = &afe->memif[id];
+
+ switch (memif->data->id) {
+ case MT8188_AFE_MEMIF_DL10:
+ fs = MT8188_ETDM_OUT3_1X_EN;
+ break;
+ case MT8188_AFE_MEMIF_UL8:
+ fs = MT8188_ETDM_IN1_NX_EN;
+ break;
+ case MT8188_AFE_MEMIF_UL3:
+ fs = MT8188_ETDM_IN2_NX_EN;
+ break;
+ default:
+ afe_priv = afe->platform_priv;
+ memif_priv = afe_priv->dai_priv[id];
+ if (memif_priv->fs_timing)
+ fs = memif_priv->fs_timing;
+ break;
+ }
+
+ return fs;
+}
+
+static int mt8188_irq_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
+{
+ int fs = mt8188_memif_fs(substream, rate);
+
+ switch (fs) {
+ case MT8188_ETDM_IN1_NX_EN:
+ fs = MT8188_ETDM_IN1_1X_EN;
+ break;
+ case MT8188_ETDM_IN2_NX_EN:
+ fs = MT8188_ETDM_IN2_1X_EN;
+ break;
+ default:
+ break;
+ }
+
+ return fs;
+}
+
+enum {
+ MT8188_AFE_CM0,
+ MT8188_AFE_CM1,
+ MT8188_AFE_CM2,
+ MT8188_AFE_CM_NUM,
+};
+
+struct mt8188_afe_channel_merge {
+ int id;
+ int reg;
+ unsigned int sel_shift;
+ unsigned int sel_maskbit;
+ unsigned int sel_default;
+ unsigned int ch_num_shift;
+ unsigned int ch_num_maskbit;
+ unsigned int en_shift;
+ unsigned int en_maskbit;
+ unsigned int update_cnt_shift;
+ unsigned int update_cnt_maskbit;
+ unsigned int update_cnt_default;
+};
+
+static const struct mt8188_afe_channel_merge
+ mt8188_afe_cm[MT8188_AFE_CM_NUM] = {
+ [MT8188_AFE_CM0] = {
+ .id = MT8188_AFE_CM0,
+ .reg = AFE_CM0_CON,
+ .sel_shift = 30,
+ .sel_maskbit = 0x1,
+ .sel_default = 1,
+ .ch_num_shift = 2,
+ .ch_num_maskbit = 0x3f,
+ .en_shift = 0,
+ .en_maskbit = 0x1,
+ .update_cnt_shift = 16,
+ .update_cnt_maskbit = 0x1fff,
+ .update_cnt_default = 0x3,
+ },
+ [MT8188_AFE_CM1] = {
+ .id = MT8188_AFE_CM1,
+ .reg = AFE_CM1_CON,
+ .sel_shift = 30,
+ .sel_maskbit = 0x1,
+ .sel_default = 1,
+ .ch_num_shift = 2,
+ .ch_num_maskbit = 0x1f,
+ .en_shift = 0,
+ .en_maskbit = 0x1,
+ .update_cnt_shift = 16,
+ .update_cnt_maskbit = 0x1fff,
+ .update_cnt_default = 0x3,
+ },
+ [MT8188_AFE_CM2] = {
+ .id = MT8188_AFE_CM2,
+ .reg = AFE_CM2_CON,
+ .sel_shift = 30,
+ .sel_maskbit = 0x1,
+ .sel_default = 1,
+ .ch_num_shift = 2,
+ .ch_num_maskbit = 0x1f,
+ .en_shift = 0,
+ .en_maskbit = 0x1,
+ .update_cnt_shift = 16,
+ .update_cnt_maskbit = 0x1fff,
+ .update_cnt_default = 0x3,
+ },
+};
+
+static int mt8188_afe_memif_is_ul(int id)
+{
+ if (id >= MT8188_AFE_MEMIF_UL_START && id < MT8188_AFE_MEMIF_END)
+ return 1;
+ else
+ return 0;
+}
+
+static const struct mt8188_afe_channel_merge *
+ mt8188_afe_found_cm(struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int id = -EINVAL;
+
+ if (mt8188_afe_memif_is_ul(dai->id) == 0)
+ return NULL;
+
+ switch (dai->id) {
+ case MT8188_AFE_MEMIF_UL9:
+ id = MT8188_AFE_CM0;
+ break;
+ case MT8188_AFE_MEMIF_UL2:
+ id = MT8188_AFE_CM1;
+ break;
+ case MT8188_AFE_MEMIF_UL10:
+ id = MT8188_AFE_CM2;
+ break;
+ default:
+ break;
+ }
+
+ if (id < 0) {
+ dev_dbg(afe->dev, "%s, memif %d cannot find CM!\n", __func__, dai->id);
+ return NULL;
+ }
+
+ return &mt8188_afe_cm[id];
+}
+
+static int mt8188_afe_config_cm(struct mtk_base_afe *afe,
+ const struct mt8188_afe_channel_merge *cm,
+ unsigned int channels)
+{
+ if (!cm)
+ return -EINVAL;
+
+ regmap_update_bits(afe->regmap,
+ cm->reg,
+ cm->sel_maskbit << cm->sel_shift,
+ cm->sel_default << cm->sel_shift);
+
+ regmap_update_bits(afe->regmap,
+ cm->reg,
+ cm->ch_num_maskbit << cm->ch_num_shift,
+ (channels - 1) << cm->ch_num_shift);
+
+ regmap_update_bits(afe->regmap,
+ cm->reg,
+ cm->update_cnt_maskbit << cm->update_cnt_shift,
+ cm->update_cnt_default << cm->update_cnt_shift);
+
+ return 0;
+}
+
+static int mt8188_afe_enable_cm(struct mtk_base_afe *afe,
+ const struct mt8188_afe_channel_merge *cm,
+ bool enable)
+{
+ if (!cm)
+ return -EINVAL;
+
+ regmap_update_bits(afe->regmap,
+ cm->reg,
+ cm->en_maskbit << cm->en_shift,
+ enable << cm->en_shift);
+
+ return 0;
+}
+
+static int mt8188_afe_fe_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int id = asoc_rtd_to_cpu(rtd, 0)->id;
+ int ret;
+
+ ret = mtk_afe_fe_startup(substream, dai);
+
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ MT8188_MEMIF_BUFFER_BYTES_ALIGN);
+
+ if (id != MT8188_AFE_MEMIF_DL7)
+ goto out;
+
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 1,
+ MT8188_MEMIF_DL7_MAX_PERIOD_SIZE);
+ if (ret < 0)
+ dev_dbg(afe->dev, "hw_constraint_minmax failed\n");
+out:
+ return ret;
+}
+
+static void mt8188_afe_fe_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ mtk_afe_fe_shutdown(substream, dai);
+}
+
+static int mt8188_afe_fe_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int id = asoc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ const struct mtk_base_memif_data *data = memif->data;
+ const struct mt8188_afe_channel_merge *cm = mt8188_afe_found_cm(dai);
+ unsigned int channels = params_channels(params);
+
+ mt8188_afe_config_cm(afe, cm, channels);
+
+ if (data->ch_num_reg >= 0) {
+ regmap_update_bits(afe->regmap, data->ch_num_reg,
+ data->ch_num_maskbit << data->ch_num_shift,
+ channels << data->ch_num_shift);
+ }
+
+ return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt8188_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ const struct mt8188_afe_channel_merge *cm = mt8188_afe_found_cm(dai);
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ int id = asoc_rtd_to_cpu(rtd, 0)->id;
+ struct mtk_base_afe_memif *memif = &afe->memif[id];
+ struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage];
+ const struct mtk_base_irq_data *irq_data = irqs->irq_data;
+ unsigned int counter = runtime->period_size;
+ int fs;
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ mt8188_afe_enable_cm(afe, cm, true);
+
+ ret = mtk_memif_set_enable(afe, id);
+ if (ret) {
+ dev_err(afe->dev, "%s(), error, id %d, memif enable, ret %d\n",
+ __func__, id, ret);
+ return ret;
+ }
+
+ /* set irq counter */
+ regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
+ irq_data->irq_cnt_maskbit << irq_data->irq_cnt_shift,
+ counter << irq_data->irq_cnt_shift);
+
+ /* set irq fs */
+ fs = afe->irq_fs(substream, runtime->rate);
+
+ if (fs < 0)
+ return -EINVAL;
+
+ if (irq_data->irq_fs_reg >= 0)
+ regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
+ irq_data->irq_fs_maskbit << irq_data->irq_fs_shift,
+ fs << irq_data->irq_fs_shift);
+
+ /* delay for uplink */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ u32 sample_delay;
+
+ sample_delay = ((MEMIF_AXI_MINLEN + 1) * 64 +
+ (runtime->channels * runtime->sample_bits - 1)) /
+ (runtime->channels * runtime->sample_bits) + 1;
+
+ udelay(sample_delay * 1000000 / runtime->rate);
+ }
+
+ /* enable interrupt */
+ regmap_set_bits(afe->regmap, irq_data->irq_en_reg,
+ BIT(irq_data->irq_en_shift));
+ return 0;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ mt8188_afe_enable_cm(afe, cm, false);
+
+ ret = mtk_memif_set_disable(afe, id);
+ if (ret)
+ dev_err(afe->dev, "%s(), error, id %d, memif enable, ret %d\n",
+ __func__, id, ret);
+
+ /* disable interrupt */
+
+ regmap_clear_bits(afe->regmap, irq_data->irq_en_reg,
+ BIT(irq_data->irq_en_shift));
+ /* and clear pending IRQ */
+ regmap_write(afe->regmap, irq_data->irq_clr_reg,
+ BIT(irq_data->irq_clr_shift));
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct snd_soc_dai_ops mt8188_afe_fe_dai_ops = {
+ .startup = mt8188_afe_fe_startup,
+ .shutdown = mt8188_afe_fe_shutdown,
+ .hw_params = mt8188_afe_fe_hw_params,
+ .hw_free = mtk_afe_fe_hw_free,
+ .prepare = mtk_afe_fe_prepare,
+ .trigger = mt8188_afe_fe_trigger,
+};
+
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_176400 |\
+ SNDRV_PCM_RATE_192000 |\
+ SNDRV_PCM_RATE_352800 |\
+ SNDRV_PCM_RATE_384000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mt8188_memif_dai_driver[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ {
+ .name = "DL2",
+ .id = MT8188_AFE_MEMIF_DL2,
+ .playback = {
+ .stream_name = "DL2",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL3",
+ .id = MT8188_AFE_MEMIF_DL3,
+ .playback = {
+ .stream_name = "DL3",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL6",
+ .id = MT8188_AFE_MEMIF_DL6,
+ .playback = {
+ .stream_name = "DL6",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL7",
+ .id = MT8188_AFE_MEMIF_DL7,
+ .playback = {
+ .stream_name = "DL7",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL8",
+ .id = MT8188_AFE_MEMIF_DL8,
+ .playback = {
+ .stream_name = "DL8",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL10",
+ .id = MT8188_AFE_MEMIF_DL10,
+ .playback = {
+ .stream_name = "DL10",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "DL11",
+ .id = MT8188_AFE_MEMIF_DL11,
+ .playback = {
+ .stream_name = "DL11",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL1",
+ .id = MT8188_AFE_MEMIF_UL1,
+ .capture = {
+ .stream_name = "UL1",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL2",
+ .id = MT8188_AFE_MEMIF_UL2,
+ .capture = {
+ .stream_name = "UL2",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL3",
+ .id = MT8188_AFE_MEMIF_UL3,
+ .capture = {
+ .stream_name = "UL3",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL4",
+ .id = MT8188_AFE_MEMIF_UL4,
+ .capture = {
+ .stream_name = "UL4",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL5",
+ .id = MT8188_AFE_MEMIF_UL5,
+ .capture = {
+ .stream_name = "UL5",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL6",
+ .id = MT8188_AFE_MEMIF_UL6,
+ .capture = {
+ .stream_name = "UL6",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL8",
+ .id = MT8188_AFE_MEMIF_UL8,
+ .capture = {
+ .stream_name = "UL8",
+ .channels_min = 1,
+ .channels_max = 24,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL9",
+ .id = MT8188_AFE_MEMIF_UL9,
+ .capture = {
+ .stream_name = "UL9",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+ {
+ .name = "UL10",
+ .id = MT8188_AFE_MEMIF_UL10,
+ .capture = {
+ .stream_name = "UL10",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mt8188_afe_fe_dai_ops,
+ },
+};
+
+static const struct snd_kcontrol_new o002_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN2, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN2, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN2, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN2, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN2_2, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I072 Switch", AFE_CONN2_2, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN2_5, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new o003_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN3, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN3, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN3, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN3, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN3_2, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I073 Switch", AFE_CONN3_2, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN3_5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new o004_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN4, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I014 Switch", AFE_CONN4, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN4, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I074 Switch", AFE_CONN4_2, 10, 1, 0),
+};
+
+static const struct snd_kcontrol_new o005_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN5, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I015 Switch", AFE_CONN5, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN5, 25, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I075 Switch", AFE_CONN5_2, 11, 1, 0),
+};
+
+static const struct snd_kcontrol_new o006_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN6, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I016 Switch", AFE_CONN6, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN6, 26, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I076 Switch", AFE_CONN6_2, 12, 1, 0),
+};
+
+static const struct snd_kcontrol_new o007_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN7, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I017 Switch", AFE_CONN7, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN7, 27, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I077 Switch", AFE_CONN7_2, 13, 1, 0),
+};
+
+static const struct snd_kcontrol_new o008_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I018 Switch", AFE_CONN8, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN8, 28, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I078 Switch", AFE_CONN8_2, 14, 1, 0),
+};
+
+static const struct snd_kcontrol_new o009_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I019 Switch", AFE_CONN9, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN9, 29, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I079 Switch", AFE_CONN9_2, 15, 1, 0),
+};
+
+static const struct snd_kcontrol_new o010_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN10, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I030 Switch", AFE_CONN10, 30, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN10_1, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I072 Switch", AFE_CONN10_2, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I080 Switch", AFE_CONN10_2, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I188 Switch", AFE_CONN10_5, 28, 1, 0),
+};
+
+static const struct snd_kcontrol_new o011_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN11, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I031 Switch", AFE_CONN11, 31, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN11_1, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I073 Switch", AFE_CONN11_2, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I081 Switch", AFE_CONN11_2, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I189 Switch", AFE_CONN11_5, 29, 1, 0),
+};
+
+static const struct snd_kcontrol_new o012_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN12, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I032 Switch", AFE_CONN12_1, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN12_1, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I074 Switch", AFE_CONN12_2, 10, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I082 Switch", AFE_CONN12_2, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I190 Switch", AFE_CONN12_5, 30, 1, 0),
+};
+
+static const struct snd_kcontrol_new o013_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN13, 25, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I033 Switch", AFE_CONN13_1, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN13_1, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I075 Switch", AFE_CONN13_2, 11, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I083 Switch", AFE_CONN13_2, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I191 Switch", AFE_CONN13_5, 31, 1, 0),
+};
+
+static const struct snd_kcontrol_new o014_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN14, 26, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I034 Switch", AFE_CONN14_1, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN14_1, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I076 Switch", AFE_CONN14_2, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I084 Switch", AFE_CONN14_2, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I192 Switch", AFE_CONN14_6, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new o015_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN15, 27, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I035 Switch", AFE_CONN15_1, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN15_1, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I077 Switch", AFE_CONN15_2, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I085 Switch", AFE_CONN15_2, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I193 Switch", AFE_CONN15_6, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new o016_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN16, 28, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I036 Switch", AFE_CONN16_1, 4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN16_1, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I078 Switch", AFE_CONN16_2, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I086 Switch", AFE_CONN16_2, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I194 Switch", AFE_CONN16_6, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new o017_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN17, 29, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I037 Switch", AFE_CONN17_1, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN17_1, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I079 Switch", AFE_CONN17_2, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I087 Switch", AFE_CONN17_2, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I195 Switch", AFE_CONN17_6, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new o018_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I080 Switch", AFE_CONN18_2, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new o019_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I081 Switch", AFE_CONN19_2, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new o020_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I082 Switch", AFE_CONN20_2, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new o021_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I083 Switch", AFE_CONN21_2, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new o022_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I084 Switch", AFE_CONN22_2, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new o023_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I085 Switch", AFE_CONN23_2, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new o024_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I086 Switch", AFE_CONN24_2, 22, 1, 0),
+};
+
+static const struct snd_kcontrol_new o025_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I087 Switch", AFE_CONN25_2, 23, 1, 0),
+};
+
+static const struct snd_kcontrol_new o026_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN26_1, 14, 1, 0),
+};
+
+static const struct snd_kcontrol_new o027_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN27_1, 15, 1, 0),
+};
+
+static const struct snd_kcontrol_new o028_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN28_1, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new o029_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN29_1, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new o030_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN30_1, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new o031_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN31_1, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new o032_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN32_1, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new o033_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN33_1, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new o034_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN34, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I002 Switch", AFE_CONN34, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN34, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN34, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN34_2, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I072 Switch", AFE_CONN34_2, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN34_5, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new o035_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN35, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I003 Switch", AFE_CONN35, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN35, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN35, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN35_2, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I073 Switch", AFE_CONN35_2, 9, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN35_5, 8, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN35_5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new o036_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN36, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN36, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN36, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN36_2, 6, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN36_5, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new o037_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN37, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN37, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN37, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN37_2, 7, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN37_5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new o038_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN38, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN38_5, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new o039_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN39, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN39_5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new o040_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I002 Switch", AFE_CONN40, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I012 Switch", AFE_CONN40, 12, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN40, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I168 Switch", AFE_CONN40_5, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new o041_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I003 Switch", AFE_CONN41, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I013 Switch", AFE_CONN41, 13, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN41, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I169 Switch", AFE_CONN41_5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new o042_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I014 Switch", AFE_CONN42, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN42, 24, 1, 0),
+};
+
+static const struct snd_kcontrol_new o043_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I015 Switch", AFE_CONN43, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN43, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new o044_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I016 Switch", AFE_CONN44, 16, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN44, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new o045_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I017 Switch", AFE_CONN45, 17, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN45, 27, 1, 0),
+};
+
+static const struct snd_kcontrol_new o046_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I018 Switch", AFE_CONN46, 18, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN46, 28, 1, 0),
+};
+
+static const struct snd_kcontrol_new o047_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I019 Switch", AFE_CONN47, 19, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN47, 29, 1, 0),
+};
+
+static const struct snd_kcontrol_new o182_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN182, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN182, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN182, 24, 1, 0),
+};
+
+static const struct snd_kcontrol_new o183_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN183, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN183, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN183, 25, 1, 0),
+};
+
+static const char * const dl8_dl11_data_sel_mux_text[] = {
+ "dl8", "dl11",
+};
+
+static SOC_ENUM_SINGLE_DECL(dl8_dl11_data_sel_mux_enum,
+ AFE_DAC_CON2, 0, dl8_dl11_data_sel_mux_text);
+
+static const struct snd_kcontrol_new dl8_dl11_data_sel_mux =
+ SOC_DAPM_ENUM("DL8_DL11 Sink",
+ dl8_dl11_data_sel_mux_enum);
+
+static const struct snd_soc_dapm_widget mt8188_memif_widgets[] = {
+ /* DL6 */
+ SND_SOC_DAPM_MIXER("I000", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I001", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DL3 */
+ SND_SOC_DAPM_MIXER("I020", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I021", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DL11 */
+ SND_SOC_DAPM_MIXER("I022", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I023", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I024", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I025", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I026", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I027", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I028", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I029", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I030", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I031", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I032", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I033", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I034", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I035", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I036", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I037", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DL11/DL8 */
+ SND_SOC_DAPM_MIXER("I046", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I047", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I048", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I049", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I050", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I051", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I052", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I053", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I054", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I055", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I056", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I057", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I058", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I059", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I060", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I061", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* DL2 */
+ SND_SOC_DAPM_MIXER("I070", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I071", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("DL8_DL11 Mux",
+ SND_SOC_NOPM, 0, 0, &dl8_dl11_data_sel_mux),
+
+ /* UL9 */
+ SND_SOC_DAPM_MIXER("O002", SND_SOC_NOPM, 0, 0,
+ o002_mix, ARRAY_SIZE(o002_mix)),
+ SND_SOC_DAPM_MIXER("O003", SND_SOC_NOPM, 0, 0,
+ o003_mix, ARRAY_SIZE(o003_mix)),
+ SND_SOC_DAPM_MIXER("O004", SND_SOC_NOPM, 0, 0,
+ o004_mix, ARRAY_SIZE(o004_mix)),
+ SND_SOC_DAPM_MIXER("O005", SND_SOC_NOPM, 0, 0,
+ o005_mix, ARRAY_SIZE(o005_mix)),
+ SND_SOC_DAPM_MIXER("O006", SND_SOC_NOPM, 0, 0,
+ o006_mix, ARRAY_SIZE(o006_mix)),
+ SND_SOC_DAPM_MIXER("O007", SND_SOC_NOPM, 0, 0,
+ o007_mix, ARRAY_SIZE(o007_mix)),
+ SND_SOC_DAPM_MIXER("O008", SND_SOC_NOPM, 0, 0,
+ o008_mix, ARRAY_SIZE(o008_mix)),
+ SND_SOC_DAPM_MIXER("O009", SND_SOC_NOPM, 0, 0,
+ o009_mix, ARRAY_SIZE(o009_mix)),
+ SND_SOC_DAPM_MIXER("O010", SND_SOC_NOPM, 0, 0,
+ o010_mix, ARRAY_SIZE(o010_mix)),
+ SND_SOC_DAPM_MIXER("O011", SND_SOC_NOPM, 0, 0,
+ o011_mix, ARRAY_SIZE(o011_mix)),
+ SND_SOC_DAPM_MIXER("O012", SND_SOC_NOPM, 0, 0,
+ o012_mix, ARRAY_SIZE(o012_mix)),
+ SND_SOC_DAPM_MIXER("O013", SND_SOC_NOPM, 0, 0,
+ o013_mix, ARRAY_SIZE(o013_mix)),
+ SND_SOC_DAPM_MIXER("O014", SND_SOC_NOPM, 0, 0,
+ o014_mix, ARRAY_SIZE(o014_mix)),
+ SND_SOC_DAPM_MIXER("O015", SND_SOC_NOPM, 0, 0,
+ o015_mix, ARRAY_SIZE(o015_mix)),
+ SND_SOC_DAPM_MIXER("O016", SND_SOC_NOPM, 0, 0,
+ o016_mix, ARRAY_SIZE(o016_mix)),
+ SND_SOC_DAPM_MIXER("O017", SND_SOC_NOPM, 0, 0,
+ o017_mix, ARRAY_SIZE(o017_mix)),
+ SND_SOC_DAPM_MIXER("O018", SND_SOC_NOPM, 0, 0,
+ o018_mix, ARRAY_SIZE(o018_mix)),
+ SND_SOC_DAPM_MIXER("O019", SND_SOC_NOPM, 0, 0,
+ o019_mix, ARRAY_SIZE(o019_mix)),
+ SND_SOC_DAPM_MIXER("O020", SND_SOC_NOPM, 0, 0,
+ o020_mix, ARRAY_SIZE(o020_mix)),
+ SND_SOC_DAPM_MIXER("O021", SND_SOC_NOPM, 0, 0,
+ o021_mix, ARRAY_SIZE(o021_mix)),
+ SND_SOC_DAPM_MIXER("O022", SND_SOC_NOPM, 0, 0,
+ o022_mix, ARRAY_SIZE(o022_mix)),
+ SND_SOC_DAPM_MIXER("O023", SND_SOC_NOPM, 0, 0,
+ o023_mix, ARRAY_SIZE(o023_mix)),
+ SND_SOC_DAPM_MIXER("O024", SND_SOC_NOPM, 0, 0,
+ o024_mix, ARRAY_SIZE(o024_mix)),
+ SND_SOC_DAPM_MIXER("O025", SND_SOC_NOPM, 0, 0,
+ o025_mix, ARRAY_SIZE(o025_mix)),
+ SND_SOC_DAPM_MIXER("O026", SND_SOC_NOPM, 0, 0,
+ o026_mix, ARRAY_SIZE(o026_mix)),
+ SND_SOC_DAPM_MIXER("O027", SND_SOC_NOPM, 0, 0,
+ o027_mix, ARRAY_SIZE(o027_mix)),
+ SND_SOC_DAPM_MIXER("O028", SND_SOC_NOPM, 0, 0,
+ o028_mix, ARRAY_SIZE(o028_mix)),
+ SND_SOC_DAPM_MIXER("O029", SND_SOC_NOPM, 0, 0,
+ o029_mix, ARRAY_SIZE(o029_mix)),
+ SND_SOC_DAPM_MIXER("O030", SND_SOC_NOPM, 0, 0,
+ o030_mix, ARRAY_SIZE(o030_mix)),
+ SND_SOC_DAPM_MIXER("O031", SND_SOC_NOPM, 0, 0,
+ o031_mix, ARRAY_SIZE(o031_mix)),
+ SND_SOC_DAPM_MIXER("O032", SND_SOC_NOPM, 0, 0,
+ o032_mix, ARRAY_SIZE(o032_mix)),
+ SND_SOC_DAPM_MIXER("O033", SND_SOC_NOPM, 0, 0,
+ o033_mix, ARRAY_SIZE(o033_mix)),
+
+ /* UL4 */
+ SND_SOC_DAPM_MIXER("O034", SND_SOC_NOPM, 0, 0,
+ o034_mix, ARRAY_SIZE(o034_mix)),
+ SND_SOC_DAPM_MIXER("O035", SND_SOC_NOPM, 0, 0,
+ o035_mix, ARRAY_SIZE(o035_mix)),
+
+ /* UL5 */
+ SND_SOC_DAPM_MIXER("O036", SND_SOC_NOPM, 0, 0,
+ o036_mix, ARRAY_SIZE(o036_mix)),
+ SND_SOC_DAPM_MIXER("O037", SND_SOC_NOPM, 0, 0,
+ o037_mix, ARRAY_SIZE(o037_mix)),
+
+ /* UL10 */
+ SND_SOC_DAPM_MIXER("O038", SND_SOC_NOPM, 0, 0,
+ o038_mix, ARRAY_SIZE(o038_mix)),
+ SND_SOC_DAPM_MIXER("O039", SND_SOC_NOPM, 0, 0,
+ o039_mix, ARRAY_SIZE(o039_mix)),
+ SND_SOC_DAPM_MIXER("O182", SND_SOC_NOPM, 0, 0,
+ o182_mix, ARRAY_SIZE(o182_mix)),
+ SND_SOC_DAPM_MIXER("O183", SND_SOC_NOPM, 0, 0,
+ o183_mix, ARRAY_SIZE(o183_mix)),
+
+ /* UL2 */
+ SND_SOC_DAPM_MIXER("O040", SND_SOC_NOPM, 0, 0,
+ o040_mix, ARRAY_SIZE(o040_mix)),
+ SND_SOC_DAPM_MIXER("O041", SND_SOC_NOPM, 0, 0,
+ o041_mix, ARRAY_SIZE(o041_mix)),
+ SND_SOC_DAPM_MIXER("O042", SND_SOC_NOPM, 0, 0,
+ o042_mix, ARRAY_SIZE(o042_mix)),
+ SND_SOC_DAPM_MIXER("O043", SND_SOC_NOPM, 0, 0,
+ o043_mix, ARRAY_SIZE(o043_mix)),
+ SND_SOC_DAPM_MIXER("O044", SND_SOC_NOPM, 0, 0,
+ o044_mix, ARRAY_SIZE(o044_mix)),
+ SND_SOC_DAPM_MIXER("O045", SND_SOC_NOPM, 0, 0,
+ o045_mix, ARRAY_SIZE(o045_mix)),
+ SND_SOC_DAPM_MIXER("O046", SND_SOC_NOPM, 0, 0,
+ o046_mix, ARRAY_SIZE(o046_mix)),
+ SND_SOC_DAPM_MIXER("O047", SND_SOC_NOPM, 0, 0,
+ o047_mix, ARRAY_SIZE(o047_mix)),
+};
+
+static const struct snd_soc_dapm_route mt8188_memif_routes[] = {
+ {"I000", NULL, "DL6"},
+ {"I001", NULL, "DL6"},
+
+ {"I020", NULL, "DL3"},
+ {"I021", NULL, "DL3"},
+
+ {"I022", NULL, "DL11"},
+ {"I023", NULL, "DL11"},
+ {"I024", NULL, "DL11"},
+ {"I025", NULL, "DL11"},
+ {"I026", NULL, "DL11"},
+ {"I027", NULL, "DL11"},
+ {"I028", NULL, "DL11"},
+ {"I029", NULL, "DL11"},
+ {"I030", NULL, "DL11"},
+ {"I031", NULL, "DL11"},
+ {"I032", NULL, "DL11"},
+ {"I033", NULL, "DL11"},
+ {"I034", NULL, "DL11"},
+ {"I035", NULL, "DL11"},
+ {"I036", NULL, "DL11"},
+ {"I037", NULL, "DL11"},
+
+ {"DL8_DL11 Mux", "dl8", "DL8"},
+ {"DL8_DL11 Mux", "dl11", "DL11"},
+
+ {"I046", NULL, "DL8_DL11 Mux"},
+ {"I047", NULL, "DL8_DL11 Mux"},
+ {"I048", NULL, "DL8_DL11 Mux"},
+ {"I049", NULL, "DL8_DL11 Mux"},
+ {"I050", NULL, "DL8_DL11 Mux"},
+ {"I051", NULL, "DL8_DL11 Mux"},
+ {"I052", NULL, "DL8_DL11 Mux"},
+ {"I053", NULL, "DL8_DL11 Mux"},
+ {"I054", NULL, "DL8_DL11 Mux"},
+ {"I055", NULL, "DL8_DL11 Mux"},
+ {"I056", NULL, "DL8_DL11 Mux"},
+ {"I057", NULL, "DL8_DL11 Mux"},
+ {"I058", NULL, "DL8_DL11 Mux"},
+ {"I059", NULL, "DL8_DL11 Mux"},
+ {"I060", NULL, "DL8_DL11 Mux"},
+ {"I061", NULL, "DL8_DL11 Mux"},
+
+ {"I070", NULL, "DL2"},
+ {"I071", NULL, "DL2"},
+
+ {"UL9", NULL, "O002"},
+ {"UL9", NULL, "O003"},
+ {"UL9", NULL, "O004"},
+ {"UL9", NULL, "O005"},
+ {"UL9", NULL, "O006"},
+ {"UL9", NULL, "O007"},
+ {"UL9", NULL, "O008"},
+ {"UL9", NULL, "O009"},
+ {"UL9", NULL, "O010"},
+ {"UL9", NULL, "O011"},
+ {"UL9", NULL, "O012"},
+ {"UL9", NULL, "O013"},
+ {"UL9", NULL, "O014"},
+ {"UL9", NULL, "O015"},
+ {"UL9", NULL, "O016"},
+ {"UL9", NULL, "O017"},
+ {"UL9", NULL, "O018"},
+ {"UL9", NULL, "O019"},
+ {"UL9", NULL, "O020"},
+ {"UL9", NULL, "O021"},
+ {"UL9", NULL, "O022"},
+ {"UL9", NULL, "O023"},
+ {"UL9", NULL, "O024"},
+ {"UL9", NULL, "O025"},
+ {"UL9", NULL, "O026"},
+ {"UL9", NULL, "O027"},
+ {"UL9", NULL, "O028"},
+ {"UL9", NULL, "O029"},
+ {"UL9", NULL, "O030"},
+ {"UL9", NULL, "O031"},
+ {"UL9", NULL, "O032"},
+ {"UL9", NULL, "O033"},
+
+ {"UL4", NULL, "O034"},
+ {"UL4", NULL, "O035"},
+
+ {"UL5", NULL, "O036"},
+ {"UL5", NULL, "O037"},
+
+ {"UL10", NULL, "O038"},
+ {"UL10", NULL, "O039"},
+ {"UL10", NULL, "O182"},
+ {"UL10", NULL, "O183"},
+
+ {"UL2", NULL, "O040"},
+ {"UL2", NULL, "O041"},
+ {"UL2", NULL, "O042"},
+ {"UL2", NULL, "O043"},
+ {"UL2", NULL, "O044"},
+ {"UL2", NULL, "O045"},
+ {"UL2", NULL, "O046"},
+ {"UL2", NULL, "O047"},
+
+ {"O004", "I000 Switch", "I000"},
+ {"O005", "I001 Switch", "I001"},
+
+ {"O006", "I000 Switch", "I000"},
+ {"O007", "I001 Switch", "I001"},
+
+ {"O010", "I022 Switch", "I022"},
+ {"O011", "I023 Switch", "I023"},
+ {"O012", "I024 Switch", "I024"},
+ {"O013", "I025 Switch", "I025"},
+ {"O014", "I026 Switch", "I026"},
+ {"O015", "I027 Switch", "I027"},
+ {"O016", "I028 Switch", "I028"},
+ {"O017", "I029 Switch", "I029"},
+
+ {"O010", "I046 Switch", "I046"},
+ {"O011", "I047 Switch", "I047"},
+ {"O012", "I048 Switch", "I048"},
+ {"O013", "I049 Switch", "I049"},
+ {"O014", "I050 Switch", "I050"},
+ {"O015", "I051 Switch", "I051"},
+ {"O016", "I052 Switch", "I052"},
+ {"O017", "I053 Switch", "I053"},
+
+ {"O002", "I022 Switch", "I022"},
+ {"O003", "I023 Switch", "I023"},
+ {"O004", "I024 Switch", "I024"},
+ {"O005", "I025 Switch", "I025"},
+ {"O006", "I026 Switch", "I026"},
+ {"O007", "I027 Switch", "I027"},
+ {"O008", "I028 Switch", "I028"},
+ {"O009", "I029 Switch", "I029"},
+ {"O010", "I030 Switch", "I030"},
+ {"O011", "I031 Switch", "I031"},
+ {"O012", "I032 Switch", "I032"},
+ {"O013", "I033 Switch", "I033"},
+ {"O014", "I034 Switch", "I034"},
+ {"O015", "I035 Switch", "I035"},
+ {"O016", "I036 Switch", "I036"},
+ {"O017", "I037 Switch", "I037"},
+ {"O026", "I046 Switch", "I046"},
+ {"O027", "I047 Switch", "I047"},
+ {"O028", "I048 Switch", "I048"},
+ {"O029", "I049 Switch", "I049"},
+ {"O030", "I050 Switch", "I050"},
+ {"O031", "I051 Switch", "I051"},
+ {"O032", "I052 Switch", "I052"},
+ {"O033", "I053 Switch", "I053"},
+
+ {"O002", "I000 Switch", "I000"},
+ {"O003", "I001 Switch", "I001"},
+ {"O002", "I020 Switch", "I020"},
+ {"O003", "I021 Switch", "I021"},
+ {"O002", "I070 Switch", "I070"},
+ {"O003", "I071 Switch", "I071"},
+
+ {"O034", "I000 Switch", "I000"},
+ {"O035", "I001 Switch", "I001"},
+ {"O034", "I002 Switch", "I002"},
+ {"O035", "I003 Switch", "I003"},
+ {"O034", "I012 Switch", "I012"},
+ {"O035", "I013 Switch", "I013"},
+ {"O034", "I020 Switch", "I020"},
+ {"O035", "I021 Switch", "I021"},
+ {"O034", "I070 Switch", "I070"},
+ {"O035", "I071 Switch", "I071"},
+ {"O034", "I072 Switch", "I072"},
+ {"O035", "I073 Switch", "I073"},
+
+ {"O036", "I000 Switch", "I000"},
+ {"O037", "I001 Switch", "I001"},
+ {"O036", "I012 Switch", "I012"},
+ {"O037", "I013 Switch", "I013"},
+ {"O036", "I020 Switch", "I020"},
+ {"O037", "I021 Switch", "I021"},
+ {"O036", "I070 Switch", "I070"},
+ {"O037", "I071 Switch", "I071"},
+ {"O036", "I168 Switch", "I168"},
+ {"O037", "I169 Switch", "I169"},
+
+ {"O038", "I022 Switch", "I022"},
+ {"O039", "I023 Switch", "I023"},
+ {"O182", "I024 Switch", "I024"},
+ {"O183", "I025 Switch", "I025"},
+
+ {"O038", "I168 Switch", "I168"},
+ {"O039", "I169 Switch", "I169"},
+
+ {"O182", "I020 Switch", "I020"},
+ {"O183", "I021 Switch", "I021"},
+
+ {"O182", "I022 Switch", "I022"},
+ {"O183", "I023 Switch", "I023"},
+
+ {"O040", "I022 Switch", "I022"},
+ {"O041", "I023 Switch", "I023"},
+ {"O042", "I024 Switch", "I024"},
+ {"O043", "I025 Switch", "I025"},
+ {"O044", "I026 Switch", "I026"},
+ {"O045", "I027 Switch", "I027"},
+ {"O046", "I028 Switch", "I028"},
+ {"O047", "I029 Switch", "I029"},
+
+ {"O040", "I002 Switch", "I002"},
+ {"O041", "I003 Switch", "I003"},
+
+ {"O002", "I012 Switch", "I012"},
+ {"O003", "I013 Switch", "I013"},
+ {"O004", "I014 Switch", "I014"},
+ {"O005", "I015 Switch", "I015"},
+ {"O006", "I016 Switch", "I016"},
+ {"O007", "I017 Switch", "I017"},
+ {"O008", "I018 Switch", "I018"},
+ {"O009", "I019 Switch", "I019"},
+ {"O010", "I188 Switch", "I188"},
+ {"O011", "I189 Switch", "I189"},
+ {"O012", "I190 Switch", "I190"},
+ {"O013", "I191 Switch", "I191"},
+ {"O014", "I192 Switch", "I192"},
+ {"O015", "I193 Switch", "I193"},
+ {"O016", "I194 Switch", "I194"},
+ {"O017", "I195 Switch", "I195"},
+
+ {"O040", "I012 Switch", "I012"},
+ {"O041", "I013 Switch", "I013"},
+ {"O042", "I014 Switch", "I014"},
+ {"O043", "I015 Switch", "I015"},
+ {"O044", "I016 Switch", "I016"},
+ {"O045", "I017 Switch", "I017"},
+ {"O046", "I018 Switch", "I018"},
+ {"O047", "I019 Switch", "I019"},
+
+ {"O002", "I072 Switch", "I072"},
+ {"O003", "I073 Switch", "I073"},
+ {"O004", "I074 Switch", "I074"},
+ {"O005", "I075 Switch", "I075"},
+ {"O006", "I076 Switch", "I076"},
+ {"O007", "I077 Switch", "I077"},
+ {"O008", "I078 Switch", "I078"},
+ {"O009", "I079 Switch", "I079"},
+ {"O010", "I080 Switch", "I080"},
+ {"O011", "I081 Switch", "I081"},
+ {"O012", "I082 Switch", "I082"},
+ {"O013", "I083 Switch", "I083"},
+ {"O014", "I084 Switch", "I084"},
+ {"O015", "I085 Switch", "I085"},
+ {"O016", "I086 Switch", "I086"},
+ {"O017", "I087 Switch", "I087"},
+
+ {"O010", "I072 Switch", "I072"},
+ {"O011", "I073 Switch", "I073"},
+ {"O012", "I074 Switch", "I074"},
+ {"O013", "I075 Switch", "I075"},
+ {"O014", "I076 Switch", "I076"},
+ {"O015", "I077 Switch", "I077"},
+ {"O016", "I078 Switch", "I078"},
+ {"O017", "I079 Switch", "I079"},
+ {"O018", "I080 Switch", "I080"},
+ {"O019", "I081 Switch", "I081"},
+ {"O020", "I082 Switch", "I082"},
+ {"O021", "I083 Switch", "I083"},
+ {"O022", "I084 Switch", "I084"},
+ {"O023", "I085 Switch", "I085"},
+ {"O024", "I086 Switch", "I086"},
+ {"O025", "I087 Switch", "I087"},
+
+ {"O002", "I168 Switch", "I168"},
+ {"O003", "I169 Switch", "I169"},
+
+ {"O034", "I168 Switch", "I168"},
+ {"O035", "I168 Switch", "I168"},
+ {"O035", "I169 Switch", "I169"},
+
+ {"O040", "I168 Switch", "I168"},
+ {"O041", "I169 Switch", "I169"},
+};
+
+static const char * const mt8188_afe_1x_en_sel_text[] = {
+ "a1sys_a2sys", "a3sys", "a4sys",
+};
+
+static const unsigned int mt8188_afe_1x_en_sel_values[] = {
+ 0, 1, 2,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(dl2_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 18, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl3_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 20, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl6_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 22, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl7_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 24, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl8_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 26, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl10_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 28, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl11_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 30, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul1_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 0, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul2_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 2, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul3_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 4, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul4_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 6, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul5_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 8, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul6_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 10, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul8_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 12, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul9_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 14, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul10_1x_en_sel_enum,
+ A3_A4_TIMING_SEL1, 16, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq1_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 0, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq2_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 2, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq3_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 4, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq4_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 6, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq5_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 8, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq6_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 10, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq7_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 12, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq8_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 14, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq9_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 16, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq10_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 18, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq11_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 20, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq12_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 22, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq13_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 24, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq14_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 26, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq15_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 28, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(asys_irq16_1x_en_sel_enum,
+ A3_A4_TIMING_SEL6, 30, 0x3,
+ mt8188_afe_1x_en_sel_text,
+ mt8188_afe_1x_en_sel_values);
+
+static const char * const mt8188_afe_fs_timing_sel_text[] = {
+ "asys",
+ "etdmout1_1x_en",
+ "etdmout2_1x_en",
+ "etdmout3_1x_en",
+ "etdmin1_1x_en",
+ "etdmin2_1x_en",
+ "etdmin1_nx_en",
+ "etdmin2_nx_en",
+};
+
+static const unsigned int mt8188_afe_fs_timing_sel_values[] = {
+ 0,
+ MT8188_ETDM_OUT1_1X_EN,
+ MT8188_ETDM_OUT2_1X_EN,
+ MT8188_ETDM_OUT3_1X_EN,
+ MT8188_ETDM_IN1_1X_EN,
+ MT8188_ETDM_IN2_1X_EN,
+ MT8188_ETDM_IN1_NX_EN,
+ MT8188_ETDM_IN2_NX_EN,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(dl2_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl3_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl6_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl8_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(dl11_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul2_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul4_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul5_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul9_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(ul10_fs_timing_sel_enum,
+ SND_SOC_NOPM, 0, 0,
+ mt8188_afe_fs_timing_sel_text,
+ mt8188_afe_fs_timing_sel_values);
+
+static int mt8188_memif_1x_en_sel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_memif_priv *memif_priv;
+ unsigned int dai_id = kcontrol->id.device;
+ long val = ucontrol->value.integer.value[0];
+ int ret = 0;
+
+ memif_priv = afe_priv->dai_priv[dai_id];
+
+ if (val == memif_priv->asys_timing_sel)
+ return 0;
+
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+
+ memif_priv->asys_timing_sel = val;
+
+ return ret;
+}
+
+static int mt8188_asys_irq_1x_en_sel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ unsigned int id = kcontrol->id.device;
+ long val = ucontrol->value.integer.value[0];
+ int ret = 0;
+
+ if (val == afe_priv->irq_priv[id].asys_timing_sel)
+ return 0;
+
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+
+ afe_priv->irq_priv[id].asys_timing_sel = val;
+
+ return ret;
+}
+
+static int mt8188_memif_fs_timing_sel_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_memif_priv *memif_priv;
+ unsigned int dai_id = kcontrol->id.device;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+ memif_priv = afe_priv->dai_priv[dai_id];
+
+ ucontrol->value.enumerated.item[0] =
+ snd_soc_enum_val_to_item(e, memif_priv->fs_timing);
+
+ return 0;
+}
+
+static int mt8188_memif_fs_timing_sel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_memif_priv *memif_priv;
+ unsigned int dai_id = kcontrol->id.device;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int prev_item = 0;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ memif_priv = afe_priv->dai_priv[dai_id];
+
+ prev_item = snd_soc_enum_val_to_item(e, memif_priv->fs_timing);
+
+ if (item[0] == prev_item)
+ return 0;
+
+ memif_priv->fs_timing = snd_soc_enum_item_to_val(e, item[0]);
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new mt8188_memif_controls[] = {
+ MT8188_SOC_ENUM_EXT("dl2_1x_en_sel",
+ dl2_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_DL2),
+ MT8188_SOC_ENUM_EXT("dl3_1x_en_sel",
+ dl3_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_DL3),
+ MT8188_SOC_ENUM_EXT("dl6_1x_en_sel",
+ dl6_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_DL6),
+ MT8188_SOC_ENUM_EXT("dl7_1x_en_sel",
+ dl7_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_DL7),
+ MT8188_SOC_ENUM_EXT("dl8_1x_en_sel",
+ dl8_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_DL8),
+ MT8188_SOC_ENUM_EXT("dl10_1x_en_sel",
+ dl10_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_DL10),
+ MT8188_SOC_ENUM_EXT("dl11_1x_en_sel",
+ dl11_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_DL11),
+ MT8188_SOC_ENUM_EXT("ul1_1x_en_sel",
+ ul1_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL1),
+ MT8188_SOC_ENUM_EXT("ul2_1x_en_sel",
+ ul2_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL2),
+ MT8188_SOC_ENUM_EXT("ul3_1x_en_sel",
+ ul3_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL3),
+ MT8188_SOC_ENUM_EXT("ul4_1x_en_sel",
+ ul4_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL4),
+ MT8188_SOC_ENUM_EXT("ul5_1x_en_sel",
+ ul5_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL5),
+ MT8188_SOC_ENUM_EXT("ul6_1x_en_sel",
+ ul6_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL6),
+ MT8188_SOC_ENUM_EXT("ul8_1x_en_sel",
+ ul8_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL8),
+ MT8188_SOC_ENUM_EXT("ul9_1x_en_sel",
+ ul9_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL9),
+ MT8188_SOC_ENUM_EXT("ul10_1x_en_sel",
+ ul10_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_memif_1x_en_sel_put,
+ MT8188_AFE_MEMIF_UL10),
+ MT8188_SOC_ENUM_EXT("asys_irq1_1x_en_sel",
+ asys_irq1_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_13),
+ MT8188_SOC_ENUM_EXT("asys_irq2_1x_en_sel",
+ asys_irq2_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_14),
+ MT8188_SOC_ENUM_EXT("asys_irq3_1x_en_sel",
+ asys_irq3_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_15),
+ MT8188_SOC_ENUM_EXT("asys_irq4_1x_en_sel",
+ asys_irq4_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_16),
+ MT8188_SOC_ENUM_EXT("asys_irq5_1x_en_sel",
+ asys_irq5_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_17),
+ MT8188_SOC_ENUM_EXT("asys_irq6_1x_en_sel",
+ asys_irq6_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_18),
+ MT8188_SOC_ENUM_EXT("asys_irq7_1x_en_sel",
+ asys_irq7_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_19),
+ MT8188_SOC_ENUM_EXT("asys_irq8_1x_en_sel",
+ asys_irq8_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_20),
+ MT8188_SOC_ENUM_EXT("asys_irq9_1x_en_sel",
+ asys_irq9_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_21),
+ MT8188_SOC_ENUM_EXT("asys_irq10_1x_en_sel",
+ asys_irq10_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_22),
+ MT8188_SOC_ENUM_EXT("asys_irq11_1x_en_sel",
+ asys_irq11_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_23),
+ MT8188_SOC_ENUM_EXT("asys_irq12_1x_en_sel",
+ asys_irq12_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_24),
+ MT8188_SOC_ENUM_EXT("asys_irq13_1x_en_sel",
+ asys_irq13_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_25),
+ MT8188_SOC_ENUM_EXT("asys_irq14_1x_en_sel",
+ asys_irq14_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_26),
+ MT8188_SOC_ENUM_EXT("asys_irq15_1x_en_sel",
+ asys_irq15_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_27),
+ MT8188_SOC_ENUM_EXT("asys_irq16_1x_en_sel",
+ asys_irq16_1x_en_sel_enum,
+ snd_soc_get_enum_double,
+ mt8188_asys_irq_1x_en_sel_put,
+ MT8188_AFE_IRQ_28),
+ MT8188_SOC_ENUM_EXT("dl2_fs_timing_sel",
+ dl2_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_DL2),
+ MT8188_SOC_ENUM_EXT("dl3_fs_timing_sel",
+ dl3_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_DL3),
+ MT8188_SOC_ENUM_EXT("dl6_fs_timing_sel",
+ dl6_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_DL6),
+ MT8188_SOC_ENUM_EXT("dl8_fs_timing_sel",
+ dl8_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_DL8),
+ MT8188_SOC_ENUM_EXT("dl11_fs_timing_sel",
+ dl11_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_DL11),
+ MT8188_SOC_ENUM_EXT("ul2_fs_timing_sel",
+ ul2_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_UL2),
+ MT8188_SOC_ENUM_EXT("ul4_fs_timing_sel",
+ ul4_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_UL4),
+ MT8188_SOC_ENUM_EXT("ul5_fs_timing_sel",
+ ul5_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_UL5),
+ MT8188_SOC_ENUM_EXT("ul9_fs_timing_sel",
+ ul9_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_UL9),
+ MT8188_SOC_ENUM_EXT("ul10_fs_timing_sel",
+ ul10_fs_timing_sel_enum,
+ mt8188_memif_fs_timing_sel_get,
+ mt8188_memif_fs_timing_sel_put,
+ MT8188_AFE_MEMIF_UL10),
+};
+
+static const struct snd_soc_component_driver mt8188_afe_pcm_dai_component = {
+ .name = "mt8188-afe-pcm-dai",
+};
+
+static const struct mtk_base_memif_data memif_data[MT8188_AFE_MEMIF_NUM] = {
+ [MT8188_AFE_MEMIF_DL2] = {
+ .name = "DL2",
+ .id = MT8188_AFE_MEMIF_DL2,
+ .reg_ofs_base = AFE_DL2_BASE,
+ .reg_ofs_cur = AFE_DL2_CUR,
+ .reg_ofs_end = AFE_DL2_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON0,
+ .fs_shift = 10,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 18,
+ .hd_reg = AFE_DL2_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 18,
+ .ch_num_reg = AFE_DL2_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x1f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 18,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 18,
+ },
+ [MT8188_AFE_MEMIF_DL3] = {
+ .name = "DL3",
+ .id = MT8188_AFE_MEMIF_DL3,
+ .reg_ofs_base = AFE_DL3_BASE,
+ .reg_ofs_cur = AFE_DL3_CUR,
+ .reg_ofs_end = AFE_DL3_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON0,
+ .fs_shift = 15,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 19,
+ .hd_reg = AFE_DL3_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 19,
+ .ch_num_reg = AFE_DL3_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x1f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 19,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 19,
+ },
+ [MT8188_AFE_MEMIF_DL6] = {
+ .name = "DL6",
+ .id = MT8188_AFE_MEMIF_DL6,
+ .reg_ofs_base = AFE_DL6_BASE,
+ .reg_ofs_cur = AFE_DL6_CUR,
+ .reg_ofs_end = AFE_DL6_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON1,
+ .fs_shift = 0,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 22,
+ .hd_reg = AFE_DL6_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 22,
+ .ch_num_reg = AFE_DL6_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x1f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 22,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 22,
+ },
+ [MT8188_AFE_MEMIF_DL7] = {
+ .name = "DL7",
+ .id = MT8188_AFE_MEMIF_DL7,
+ .reg_ofs_base = AFE_DL7_BASE,
+ .reg_ofs_cur = AFE_DL7_CUR,
+ .reg_ofs_end = AFE_DL7_END,
+ .fs_reg = -1,
+ .fs_shift = 0,
+ .fs_maskbit = 0,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 23,
+ .hd_reg = AFE_DL7_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 23,
+ .ch_num_reg = AFE_DL7_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x1f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 23,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 23,
+ },
+ [MT8188_AFE_MEMIF_DL8] = {
+ .name = "DL8",
+ .id = MT8188_AFE_MEMIF_DL8,
+ .reg_ofs_base = AFE_DL8_BASE,
+ .reg_ofs_cur = AFE_DL8_CUR,
+ .reg_ofs_end = AFE_DL8_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON1,
+ .fs_shift = 10,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 24,
+ .hd_reg = AFE_DL8_CON0,
+ .hd_shift = 6,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 24,
+ .ch_num_reg = AFE_DL8_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x3f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 24,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 24,
+ },
+ [MT8188_AFE_MEMIF_DL10] = {
+ .name = "DL10",
+ .id = MT8188_AFE_MEMIF_DL10,
+ .reg_ofs_base = AFE_DL10_BASE,
+ .reg_ofs_cur = AFE_DL10_CUR,
+ .reg_ofs_end = AFE_DL10_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON1,
+ .fs_shift = 20,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 26,
+ .hd_reg = AFE_DL10_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 26,
+ .ch_num_reg = AFE_DL10_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x1f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 26,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 26,
+ },
+ [MT8188_AFE_MEMIF_DL11] = {
+ .name = "DL11",
+ .id = MT8188_AFE_MEMIF_DL11,
+ .reg_ofs_base = AFE_DL11_BASE,
+ .reg_ofs_cur = AFE_DL11_CUR,
+ .reg_ofs_end = AFE_DL11_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON1,
+ .fs_shift = 25,
+ .fs_maskbit = 0x1f,
+ .mono_reg = -1,
+ .mono_shift = 0,
+ .int_odd_flag_reg = -1,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 27,
+ .hd_reg = AFE_DL11_CON0,
+ .hd_shift = 7,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 27,
+ .ch_num_reg = AFE_DL11_CON0,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0x7f,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 27,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 27,
+ },
+ [MT8188_AFE_MEMIF_UL1] = {
+ .name = "UL1",
+ .id = MT8188_AFE_MEMIF_UL1,
+ .reg_ofs_base = AFE_UL1_BASE,
+ .reg_ofs_cur = AFE_UL1_CUR,
+ .reg_ofs_end = AFE_UL1_END,
+ .fs_reg = -1,
+ .fs_shift = 0,
+ .fs_maskbit = 0,
+ .mono_reg = AFE_UL1_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL1_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 1,
+ .hd_reg = AFE_UL1_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 0,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 0,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 0,
+ },
+ [MT8188_AFE_MEMIF_UL2] = {
+ .name = "UL2",
+ .id = MT8188_AFE_MEMIF_UL2,
+ .reg_ofs_base = AFE_UL2_BASE,
+ .reg_ofs_cur = AFE_UL2_CUR,
+ .reg_ofs_end = AFE_UL2_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON2,
+ .fs_shift = 5,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL2_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL2_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 2,
+ .hd_reg = AFE_UL2_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 1,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 1,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 1,
+ },
+ [MT8188_AFE_MEMIF_UL3] = {
+ .name = "UL3",
+ .id = MT8188_AFE_MEMIF_UL3,
+ .reg_ofs_base = AFE_UL3_BASE,
+ .reg_ofs_cur = AFE_UL3_CUR,
+ .reg_ofs_end = AFE_UL3_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON2,
+ .fs_shift = 10,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL3_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL3_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 3,
+ .hd_reg = AFE_UL3_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 2,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 2,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 2,
+ },
+ [MT8188_AFE_MEMIF_UL4] = {
+ .name = "UL4",
+ .id = MT8188_AFE_MEMIF_UL4,
+ .reg_ofs_base = AFE_UL4_BASE,
+ .reg_ofs_cur = AFE_UL4_CUR,
+ .reg_ofs_end = AFE_UL4_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON2,
+ .fs_shift = 15,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL4_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL4_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 4,
+ .hd_reg = AFE_UL4_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 3,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 3,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 3,
+ },
+ [MT8188_AFE_MEMIF_UL5] = {
+ .name = "UL5",
+ .id = MT8188_AFE_MEMIF_UL5,
+ .reg_ofs_base = AFE_UL5_BASE,
+ .reg_ofs_cur = AFE_UL5_CUR,
+ .reg_ofs_end = AFE_UL5_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON2,
+ .fs_shift = 20,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL5_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL5_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 5,
+ .hd_reg = AFE_UL5_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 4,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 4,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 4,
+ },
+ [MT8188_AFE_MEMIF_UL6] = {
+ .name = "UL6",
+ .id = MT8188_AFE_MEMIF_UL6,
+ .reg_ofs_base = AFE_UL6_BASE,
+ .reg_ofs_cur = AFE_UL6_CUR,
+ .reg_ofs_end = AFE_UL6_END,
+ .fs_reg = -1,
+ .fs_shift = 0,
+ .fs_maskbit = 0,
+ .mono_reg = AFE_UL6_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL6_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 6,
+ .hd_reg = AFE_UL6_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 5,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 5,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 5,
+ },
+ [MT8188_AFE_MEMIF_UL8] = {
+ .name = "UL8",
+ .id = MT8188_AFE_MEMIF_UL8,
+ .reg_ofs_base = AFE_UL8_BASE,
+ .reg_ofs_cur = AFE_UL8_CUR,
+ .reg_ofs_end = AFE_UL8_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON3,
+ .fs_shift = 5,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL8_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL8_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 8,
+ .hd_reg = AFE_UL8_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 7,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 7,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 7,
+ },
+ [MT8188_AFE_MEMIF_UL9] = {
+ .name = "UL9",
+ .id = MT8188_AFE_MEMIF_UL9,
+ .reg_ofs_base = AFE_UL9_BASE,
+ .reg_ofs_cur = AFE_UL9_CUR,
+ .reg_ofs_end = AFE_UL9_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON3,
+ .fs_shift = 10,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL9_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL9_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 9,
+ .hd_reg = AFE_UL9_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 8,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 8,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 8,
+ },
+ [MT8188_AFE_MEMIF_UL10] = {
+ .name = "UL10",
+ .id = MT8188_AFE_MEMIF_UL10,
+ .reg_ofs_base = AFE_UL10_BASE,
+ .reg_ofs_cur = AFE_UL10_CUR,
+ .reg_ofs_end = AFE_UL10_END,
+ .fs_reg = AFE_MEMIF_AGENT_FS_CON3,
+ .fs_shift = 15,
+ .fs_maskbit = 0x1f,
+ .mono_reg = AFE_UL10_CON0,
+ .mono_shift = 1,
+ .int_odd_flag_reg = AFE_UL10_CON0,
+ .int_odd_flag_shift = 0,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = 10,
+ .hd_reg = AFE_UL10_CON0,
+ .hd_shift = 5,
+ .agent_disable_reg = AUDIO_TOP_CON5,
+ .agent_disable_shift = 9,
+ .ch_num_reg = -1,
+ .ch_num_shift = 0,
+ .ch_num_maskbit = 0,
+ .msb_reg = AFE_NORMAL_BASE_ADR_MSB,
+ .msb_shift = 9,
+ .msb_end_reg = AFE_NORMAL_END_ADR_MSB,
+ .msb_end_shift = 9,
+ },
+};
+
+static const struct mtk_base_irq_data irq_data[MT8188_AFE_IRQ_NUM] = {
+ [MT8188_AFE_IRQ_1] = {
+ .id = MT8188_AFE_IRQ_1,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ1_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 0,
+ .irq_status_shift = 16,
+ },
+ [MT8188_AFE_IRQ_2] = {
+ .id = MT8188_AFE_IRQ_2,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ2_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 1,
+ .irq_status_shift = 17,
+ },
+ [MT8188_AFE_IRQ_3] = {
+ .id = MT8188_AFE_IRQ_3,
+ .irq_cnt_reg = AFE_IRQ3_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ3_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 2,
+ .irq_status_shift = 18,
+ },
+ [MT8188_AFE_IRQ_8] = {
+ .id = MT8188_AFE_IRQ_8,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ8_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 7,
+ .irq_status_shift = 23,
+ },
+ [MT8188_AFE_IRQ_9] = {
+ .id = MT8188_AFE_IRQ_9,
+ .irq_cnt_reg = AFE_IRQ9_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ9_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 8,
+ .irq_status_shift = 24,
+ },
+ [MT8188_AFE_IRQ_10] = {
+ .id = MT8188_AFE_IRQ_10,
+ .irq_cnt_reg = -1,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = 0,
+ .irq_fs_maskbit = 0,
+ .irq_en_reg = AFE_IRQ10_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = AFE_IRQ_MCU_CLR,
+ .irq_clr_shift = 9,
+ .irq_status_shift = 25,
+ },
+ [MT8188_AFE_IRQ_13] = {
+ .id = MT8188_AFE_IRQ_13,
+ .irq_cnt_reg = ASYS_IRQ1_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ1_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ1_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 0,
+ .irq_status_shift = 0,
+ },
+ [MT8188_AFE_IRQ_14] = {
+ .id = MT8188_AFE_IRQ_14,
+ .irq_cnt_reg = ASYS_IRQ2_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ2_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ2_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 1,
+ .irq_status_shift = 1,
+ },
+ [MT8188_AFE_IRQ_15] = {
+ .id = MT8188_AFE_IRQ_15,
+ .irq_cnt_reg = ASYS_IRQ3_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ3_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ3_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 2,
+ .irq_status_shift = 2,
+ },
+ [MT8188_AFE_IRQ_16] = {
+ .id = MT8188_AFE_IRQ_16,
+ .irq_cnt_reg = ASYS_IRQ4_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ4_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ4_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 3,
+ .irq_status_shift = 3,
+ },
+ [MT8188_AFE_IRQ_17] = {
+ .id = MT8188_AFE_IRQ_17,
+ .irq_cnt_reg = ASYS_IRQ5_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ5_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ5_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 4,
+ .irq_status_shift = 4,
+ },
+ [MT8188_AFE_IRQ_18] = {
+ .id = MT8188_AFE_IRQ_18,
+ .irq_cnt_reg = ASYS_IRQ6_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ6_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ6_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 5,
+ .irq_status_shift = 5,
+ },
+ [MT8188_AFE_IRQ_19] = {
+ .id = MT8188_AFE_IRQ_19,
+ .irq_cnt_reg = ASYS_IRQ7_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ7_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ7_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 6,
+ .irq_status_shift = 6,
+ },
+ [MT8188_AFE_IRQ_20] = {
+ .id = MT8188_AFE_IRQ_20,
+ .irq_cnt_reg = ASYS_IRQ8_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ8_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ8_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 7,
+ .irq_status_shift = 7,
+ },
+ [MT8188_AFE_IRQ_21] = {
+ .id = MT8188_AFE_IRQ_21,
+ .irq_cnt_reg = ASYS_IRQ9_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ9_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ9_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 8,
+ .irq_status_shift = 8,
+ },
+ [MT8188_AFE_IRQ_22] = {
+ .id = MT8188_AFE_IRQ_22,
+ .irq_cnt_reg = ASYS_IRQ10_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ10_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ10_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 9,
+ .irq_status_shift = 9,
+ },
+ [MT8188_AFE_IRQ_23] = {
+ .id = MT8188_AFE_IRQ_23,
+ .irq_cnt_reg = ASYS_IRQ11_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ11_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ11_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 10,
+ .irq_status_shift = 10,
+ },
+ [MT8188_AFE_IRQ_24] = {
+ .id = MT8188_AFE_IRQ_24,
+ .irq_cnt_reg = ASYS_IRQ12_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ12_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ12_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 11,
+ .irq_status_shift = 11,
+ },
+ [MT8188_AFE_IRQ_25] = {
+ .id = MT8188_AFE_IRQ_25,
+ .irq_cnt_reg = ASYS_IRQ13_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ13_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ13_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 12,
+ .irq_status_shift = 12,
+ },
+ [MT8188_AFE_IRQ_26] = {
+ .id = MT8188_AFE_IRQ_26,
+ .irq_cnt_reg = ASYS_IRQ14_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ14_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ14_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 13,
+ .irq_status_shift = 13,
+ },
+ [MT8188_AFE_IRQ_27] = {
+ .id = MT8188_AFE_IRQ_27,
+ .irq_cnt_reg = ASYS_IRQ15_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ15_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ15_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 14,
+ .irq_status_shift = 14,
+ },
+ [MT8188_AFE_IRQ_28] = {
+ .id = MT8188_AFE_IRQ_28,
+ .irq_cnt_reg = ASYS_IRQ16_CON,
+ .irq_cnt_shift = 0,
+ .irq_cnt_maskbit = 0xffffff,
+ .irq_fs_reg = ASYS_IRQ16_CON,
+ .irq_fs_shift = 24,
+ .irq_fs_maskbit = 0x1ffff,
+ .irq_en_reg = ASYS_IRQ16_CON,
+ .irq_en_shift = 31,
+ .irq_clr_reg = ASYS_IRQ_CLR,
+ .irq_clr_shift = 15,
+ .irq_status_shift = 15,
+ },
+};
+
+static const int mt8188_afe_memif_const_irqs[MT8188_AFE_MEMIF_NUM] = {
+ [MT8188_AFE_MEMIF_DL2] = MT8188_AFE_IRQ_13,
+ [MT8188_AFE_MEMIF_DL3] = MT8188_AFE_IRQ_14,
+ [MT8188_AFE_MEMIF_DL6] = MT8188_AFE_IRQ_15,
+ [MT8188_AFE_MEMIF_DL7] = MT8188_AFE_IRQ_1,
+ [MT8188_AFE_MEMIF_DL8] = MT8188_AFE_IRQ_16,
+ [MT8188_AFE_MEMIF_DL10] = MT8188_AFE_IRQ_17,
+ [MT8188_AFE_MEMIF_DL11] = MT8188_AFE_IRQ_18,
+ [MT8188_AFE_MEMIF_UL1] = MT8188_AFE_IRQ_3,
+ [MT8188_AFE_MEMIF_UL2] = MT8188_AFE_IRQ_19,
+ [MT8188_AFE_MEMIF_UL3] = MT8188_AFE_IRQ_20,
+ [MT8188_AFE_MEMIF_UL4] = MT8188_AFE_IRQ_21,
+ [MT8188_AFE_MEMIF_UL5] = MT8188_AFE_IRQ_22,
+ [MT8188_AFE_MEMIF_UL6] = MT8188_AFE_IRQ_9,
+ [MT8188_AFE_MEMIF_UL8] = MT8188_AFE_IRQ_23,
+ [MT8188_AFE_MEMIF_UL9] = MT8188_AFE_IRQ_24,
+ [MT8188_AFE_MEMIF_UL10] = MT8188_AFE_IRQ_25,
+};
+
+static bool mt8188_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ /* these auto-gen reg has read-only bit, so put it as volatile */
+ /* volatile reg cannot be cached, so cannot be set when power off */
+ switch (reg) {
+ case AUDIO_TOP_CON0:
+ case AUDIO_TOP_CON1:
+ case AUDIO_TOP_CON3:
+ case AUDIO_TOP_CON4:
+ case AUDIO_TOP_CON5:
+ case AUDIO_TOP_CON6:
+ case ASYS_IRQ_CLR:
+ case ASYS_IRQ_STATUS:
+ case ASYS_IRQ_MON1:
+ case ASYS_IRQ_MON2:
+ case AFE_IRQ_MCU_CLR:
+ case AFE_IRQ_STATUS:
+ case AFE_IRQ3_CON_MON:
+ case AFE_IRQ_MCU_MON2:
+ case ADSP_IRQ_STATUS:
+ case AUDIO_TOP_STA0:
+ case AUDIO_TOP_STA1:
+ case AFE_GAIN1_CUR:
+ case AFE_GAIN2_CUR:
+ case AFE_IEC_BURST_INFO:
+ case AFE_IEC_CHL_STAT0:
+ case AFE_IEC_CHL_STAT1:
+ case AFE_IEC_CHR_STAT0:
+ case AFE_IEC_CHR_STAT1:
+ case AFE_SPDIFIN_CHSTS1:
+ case AFE_SPDIFIN_CHSTS2:
+ case AFE_SPDIFIN_CHSTS3:
+ case AFE_SPDIFIN_CHSTS4:
+ case AFE_SPDIFIN_CHSTS5:
+ case AFE_SPDIFIN_CHSTS6:
+ case AFE_SPDIFIN_DEBUG1:
+ case AFE_SPDIFIN_DEBUG2:
+ case AFE_SPDIFIN_DEBUG3:
+ case AFE_SPDIFIN_DEBUG4:
+ case AFE_SPDIFIN_EC:
+ case AFE_SPDIFIN_CKLOCK_CFG:
+ case AFE_SPDIFIN_BR_DBG1:
+ case AFE_SPDIFIN_CKFBDIV:
+ case AFE_SPDIFIN_INT_EXT:
+ case AFE_SPDIFIN_INT_EXT2:
+ case SPDIFIN_FREQ_STATUS:
+ case SPDIFIN_USERCODE1:
+ case SPDIFIN_USERCODE2:
+ case SPDIFIN_USERCODE3:
+ case SPDIFIN_USERCODE4:
+ case SPDIFIN_USERCODE5:
+ case SPDIFIN_USERCODE6:
+ case SPDIFIN_USERCODE7:
+ case SPDIFIN_USERCODE8:
+ case SPDIFIN_USERCODE9:
+ case SPDIFIN_USERCODE10:
+ case SPDIFIN_USERCODE11:
+ case SPDIFIN_USERCODE12:
+ case AFE_LINEIN_APLL_TUNER_MON:
+ case AFE_EARC_APLL_TUNER_MON:
+ case AFE_CM0_MON:
+ case AFE_CM1_MON:
+ case AFE_CM2_MON:
+ case AFE_MPHONE_MULTI_DET_MON0:
+ case AFE_MPHONE_MULTI_DET_MON1:
+ case AFE_MPHONE_MULTI_DET_MON2:
+ case AFE_MPHONE_MULTI2_DET_MON0:
+ case AFE_MPHONE_MULTI2_DET_MON1:
+ case AFE_MPHONE_MULTI2_DET_MON2:
+ case AFE_ADDA_MTKAIF_MON0:
+ case AFE_ADDA_MTKAIF_MON1:
+ case AFE_AUD_PAD_TOP:
+ case AFE_ADDA6_MTKAIF_MON0:
+ case AFE_ADDA6_MTKAIF_MON1:
+ case AFE_ADDA6_SRC_DEBUG_MON0:
+ case AFE_ADDA6_UL_SRC_MON0:
+ case AFE_ADDA6_UL_SRC_MON1:
+ case AFE_ASRC11_NEW_CON8:
+ case AFE_ASRC11_NEW_CON9:
+ case AFE_ASRC12_NEW_CON8:
+ case AFE_ASRC12_NEW_CON9:
+ case AFE_LRCK_CNT:
+ case AFE_DAC_MON0:
+ case AFE_DL2_CUR:
+ case AFE_DL3_CUR:
+ case AFE_DL6_CUR:
+ case AFE_DL7_CUR:
+ case AFE_DL8_CUR:
+ case AFE_DL10_CUR:
+ case AFE_DL11_CUR:
+ case AFE_UL1_CUR:
+ case AFE_UL2_CUR:
+ case AFE_UL3_CUR:
+ case AFE_UL4_CUR:
+ case AFE_UL5_CUR:
+ case AFE_UL6_CUR:
+ case AFE_UL8_CUR:
+ case AFE_UL9_CUR:
+ case AFE_UL10_CUR:
+ case AFE_DL8_CHK_SUM1:
+ case AFE_DL8_CHK_SUM2:
+ case AFE_DL8_CHK_SUM3:
+ case AFE_DL8_CHK_SUM4:
+ case AFE_DL8_CHK_SUM5:
+ case AFE_DL8_CHK_SUM6:
+ case AFE_DL10_CHK_SUM1:
+ case AFE_DL10_CHK_SUM2:
+ case AFE_DL10_CHK_SUM3:
+ case AFE_DL10_CHK_SUM4:
+ case AFE_DL10_CHK_SUM5:
+ case AFE_DL10_CHK_SUM6:
+ case AFE_DL11_CHK_SUM1:
+ case AFE_DL11_CHK_SUM2:
+ case AFE_DL11_CHK_SUM3:
+ case AFE_DL11_CHK_SUM4:
+ case AFE_DL11_CHK_SUM5:
+ case AFE_DL11_CHK_SUM6:
+ case AFE_UL1_CHK_SUM1:
+ case AFE_UL1_CHK_SUM2:
+ case AFE_UL2_CHK_SUM1:
+ case AFE_UL2_CHK_SUM2:
+ case AFE_UL3_CHK_SUM1:
+ case AFE_UL3_CHK_SUM2:
+ case AFE_UL4_CHK_SUM1:
+ case AFE_UL4_CHK_SUM2:
+ case AFE_UL5_CHK_SUM1:
+ case AFE_UL5_CHK_SUM2:
+ case AFE_UL6_CHK_SUM1:
+ case AFE_UL6_CHK_SUM2:
+ case AFE_UL8_CHK_SUM1:
+ case AFE_UL8_CHK_SUM2:
+ case AFE_DL2_CHK_SUM1:
+ case AFE_DL2_CHK_SUM2:
+ case AFE_DL3_CHK_SUM1:
+ case AFE_DL3_CHK_SUM2:
+ case AFE_DL6_CHK_SUM1:
+ case AFE_DL6_CHK_SUM2:
+ case AFE_DL7_CHK_SUM1:
+ case AFE_DL7_CHK_SUM2:
+ case AFE_UL9_CHK_SUM1:
+ case AFE_UL9_CHK_SUM2:
+ case AFE_BUS_MON1:
+ case UL1_MOD2AGT_CNT_LAT:
+ case UL2_MOD2AGT_CNT_LAT:
+ case UL3_MOD2AGT_CNT_LAT:
+ case UL4_MOD2AGT_CNT_LAT:
+ case UL5_MOD2AGT_CNT_LAT:
+ case UL6_MOD2AGT_CNT_LAT:
+ case UL8_MOD2AGT_CNT_LAT:
+ case UL9_MOD2AGT_CNT_LAT:
+ case UL10_MOD2AGT_CNT_LAT:
+ case AFE_MEMIF_BUF_FULL_MON:
+ case AFE_MEMIF_BUF_MON1:
+ case AFE_MEMIF_BUF_MON3:
+ case AFE_MEMIF_BUF_MON4:
+ case AFE_MEMIF_BUF_MON5:
+ case AFE_MEMIF_BUF_MON6:
+ case AFE_MEMIF_BUF_MON7:
+ case AFE_MEMIF_BUF_MON8:
+ case AFE_MEMIF_BUF_MON9:
+ case AFE_MEMIF_BUF_MON10:
+ case DL2_AGENT2MODULE_CNT:
+ case DL3_AGENT2MODULE_CNT:
+ case DL6_AGENT2MODULE_CNT:
+ case DL7_AGENT2MODULE_CNT:
+ case DL8_AGENT2MODULE_CNT:
+ case DL10_AGENT2MODULE_CNT:
+ case DL11_AGENT2MODULE_CNT:
+ case UL1_MODULE2AGENT_CNT:
+ case UL2_MODULE2AGENT_CNT:
+ case UL3_MODULE2AGENT_CNT:
+ case UL4_MODULE2AGENT_CNT:
+ case UL5_MODULE2AGENT_CNT:
+ case UL6_MODULE2AGENT_CNT:
+ case UL8_MODULE2AGENT_CNT:
+ case UL9_MODULE2AGENT_CNT:
+ case UL10_MODULE2AGENT_CNT:
+ case AFE_DMIC0_SRC_DEBUG_MON0:
+ case AFE_DMIC0_UL_SRC_MON0:
+ case AFE_DMIC0_UL_SRC_MON1:
+ case AFE_DMIC1_SRC_DEBUG_MON0:
+ case AFE_DMIC1_UL_SRC_MON0:
+ case AFE_DMIC1_UL_SRC_MON1:
+ case AFE_DMIC2_SRC_DEBUG_MON0:
+ case AFE_DMIC2_UL_SRC_MON0:
+ case AFE_DMIC2_UL_SRC_MON1:
+ case AFE_DMIC3_SRC_DEBUG_MON0:
+ case AFE_DMIC3_UL_SRC_MON0:
+ case AFE_DMIC3_UL_SRC_MON1:
+ case DMIC_GAIN1_CUR:
+ case DMIC_GAIN2_CUR:
+ case DMIC_GAIN3_CUR:
+ case DMIC_GAIN4_CUR:
+ case ETDM_IN1_MONITOR:
+ case ETDM_IN2_MONITOR:
+ case ETDM_OUT1_MONITOR:
+ case ETDM_OUT2_MONITOR:
+ case ETDM_OUT3_MONITOR:
+ case AFE_ADDA_SRC_DEBUG_MON0:
+ case AFE_ADDA_SRC_DEBUG_MON1:
+ case AFE_ADDA_DL_SDM_FIFO_MON:
+ case AFE_ADDA_DL_SRC_LCH_MON:
+ case AFE_ADDA_DL_SRC_RCH_MON:
+ case AFE_ADDA_DL_SDM_OUT_MON:
+ case AFE_GASRC0_NEW_CON8:
+ case AFE_GASRC0_NEW_CON9:
+ case AFE_GASRC0_NEW_CON12:
+ case AFE_GASRC1_NEW_CON8:
+ case AFE_GASRC1_NEW_CON9:
+ case AFE_GASRC1_NEW_CON12:
+ case AFE_GASRC2_NEW_CON8:
+ case AFE_GASRC2_NEW_CON9:
+ case AFE_GASRC2_NEW_CON12:
+ case AFE_GASRC3_NEW_CON8:
+ case AFE_GASRC3_NEW_CON9:
+ case AFE_GASRC3_NEW_CON12:
+ case AFE_GASRC4_NEW_CON8:
+ case AFE_GASRC4_NEW_CON9:
+ case AFE_GASRC4_NEW_CON12:
+ case AFE_GASRC5_NEW_CON8:
+ case AFE_GASRC5_NEW_CON9:
+ case AFE_GASRC5_NEW_CON12:
+ case AFE_GASRC6_NEW_CON8:
+ case AFE_GASRC6_NEW_CON9:
+ case AFE_GASRC6_NEW_CON12:
+ case AFE_GASRC7_NEW_CON8:
+ case AFE_GASRC7_NEW_CON9:
+ case AFE_GASRC7_NEW_CON12:
+ case AFE_GASRC8_NEW_CON8:
+ case AFE_GASRC8_NEW_CON9:
+ case AFE_GASRC8_NEW_CON12:
+ case AFE_GASRC9_NEW_CON8:
+ case AFE_GASRC9_NEW_CON9:
+ case AFE_GASRC9_NEW_CON12:
+ case AFE_GASRC10_NEW_CON8:
+ case AFE_GASRC10_NEW_CON9:
+ case AFE_GASRC10_NEW_CON12:
+ case AFE_GASRC11_NEW_CON8:
+ case AFE_GASRC11_NEW_CON9:
+ case AFE_GASRC11_NEW_CON12:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config mt8188_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .volatile_reg = mt8188_is_volatile_reg,
+ .max_register = AFE_MAX_REGISTER,
+ .num_reg_defaults_raw = ((AFE_MAX_REGISTER / 4) + 1),
+ .cache_type = REGCACHE_FLAT,
+};
+
+#define AFE_IRQ_CLR_BITS (0x387)
+#define ASYS_IRQ_CLR_BITS (0xffff)
+
+static irqreturn_t mt8188_afe_irq_handler(int irq_id, void *dev_id)
+{
+ struct mtk_base_afe *afe = dev_id;
+ unsigned int val = 0;
+ unsigned int asys_irq_clr_bits = 0;
+ unsigned int afe_irq_clr_bits = 0;
+ unsigned int irq_status_bits = 0;
+ unsigned int irq_clr_bits = 0;
+ unsigned int mcu_irq_mask = 0;
+ int i = 0;
+ int ret = 0;
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_STATUS, &val);
+ if (ret) {
+ dev_err(afe->dev, "%s irq status err\n", __func__);
+ afe_irq_clr_bits = AFE_IRQ_CLR_BITS;
+ asys_irq_clr_bits = ASYS_IRQ_CLR_BITS;
+ goto err_irq;
+ }
+
+ ret = regmap_read(afe->regmap, AFE_IRQ_MASK, &mcu_irq_mask);
+ if (ret) {
+ dev_err(afe->dev, "%s read irq mask err\n", __func__);
+ afe_irq_clr_bits = AFE_IRQ_CLR_BITS;
+ asys_irq_clr_bits = ASYS_IRQ_CLR_BITS;
+ goto err_irq;
+ }
+
+ /* only clr cpu irq */
+ val &= mcu_irq_mask;
+
+ for (i = 0; i < MT8188_AFE_MEMIF_NUM; i++) {
+ struct mtk_base_afe_memif *memif = &afe->memif[i];
+ struct mtk_base_irq_data const *irq_data;
+
+ if (memif->irq_usage < 0)
+ continue;
+
+ irq_data = afe->irqs[memif->irq_usage].irq_data;
+
+ irq_status_bits = BIT(irq_data->irq_status_shift);
+ irq_clr_bits = BIT(irq_data->irq_clr_shift);
+
+ if (!(val & irq_status_bits))
+ continue;
+
+ if (irq_data->irq_clr_reg == ASYS_IRQ_CLR)
+ asys_irq_clr_bits |= irq_clr_bits;
+ else
+ afe_irq_clr_bits |= irq_clr_bits;
+
+ snd_pcm_period_elapsed(memif->substream);
+ }
+
+err_irq:
+ /* clear irq */
+ if (asys_irq_clr_bits)
+ regmap_write(afe->regmap, ASYS_IRQ_CLR, asys_irq_clr_bits);
+ if (afe_irq_clr_bits)
+ regmap_write(afe->regmap, AFE_IRQ_MCU_CLR, afe_irq_clr_bits);
+
+ return IRQ_HANDLED;
+}
+
+static int mt8188_afe_runtime_suspend(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
+ goto skip_regmap;
+
+ mt8188_afe_disable_main_clock(afe);
+
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+skip_regmap:
+ mt8188_afe_disable_reg_rw_clk(afe);
+
+ return 0;
+}
+
+static int mt8188_afe_runtime_resume(struct device *dev)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dev);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(MTK_SIP_AUDIO_CONTROL,
+ MTK_AUDIO_SMC_OP_DOMAIN_SIDEBANDS,
+ 0, 0, 0, 0, 0, 0, &res);
+
+ mt8188_afe_enable_reg_rw_clk(afe);
+
+ if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
+ goto skip_regmap;
+
+ regcache_cache_only(afe->regmap, false);
+ regcache_sync(afe->regmap);
+
+ mt8188_afe_enable_main_clock(afe);
+skip_regmap:
+ return 0;
+}
+
+static int mt8188_afe_component_probe(struct snd_soc_component *component)
+{
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ snd_soc_component_init_regmap(component, afe->regmap);
+
+ ret = mtk_afe_add_sub_dai_control(component);
+
+ return ret;
+}
+
+static const struct snd_soc_component_driver mt8188_afe_component = {
+ .name = AFE_PCM_NAME,
+ .pointer = mtk_afe_pcm_pointer,
+ .pcm_construct = mtk_afe_pcm_new,
+ .probe = mt8188_afe_component_probe,
+};
+
+static int init_memif_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_memif_priv *memif_priv;
+ int i;
+
+ for (i = MT8188_AFE_MEMIF_START; i < MT8188_AFE_MEMIF_END; i++) {
+ memif_priv = devm_kzalloc(afe->dev,
+ sizeof(struct mtk_dai_memif_priv),
+ GFP_KERNEL);
+ if (!memif_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[i] = memif_priv;
+ }
+
+ return 0;
+}
+
+static int mt8188_dai_memif_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mt8188_memif_dai_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mt8188_memif_dai_driver);
+
+ dai->dapm_widgets = mt8188_memif_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mt8188_memif_widgets);
+ dai->dapm_routes = mt8188_memif_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mt8188_memif_routes);
+ dai->controls = mt8188_memif_controls;
+ dai->num_controls = ARRAY_SIZE(mt8188_memif_controls);
+
+ return init_memif_priv_data(afe);
+}
+
+typedef int (*dai_register_cb)(struct mtk_base_afe *);
+static const dai_register_cb dai_register_cbs[] = {
+ mt8188_dai_adda_register,
+ mt8188_dai_etdm_register,
+ mt8188_dai_pcm_register,
+ mt8188_dai_memif_register,
+};
+
+static const struct reg_sequence mt8188_afe_reg_defaults[] = {
+ { AFE_IRQ_MASK, 0x387ffff },
+ { AFE_IRQ3_CON, BIT(30) },
+ { AFE_IRQ9_CON, BIT(30) },
+ { ETDM_IN1_CON4, 0x12000100 },
+ { ETDM_IN2_CON4, 0x12000100 },
+};
+
+static const struct reg_sequence mt8188_cg_patch[] = {
+ { AUDIO_TOP_CON0, 0xfffffffb },
+ { AUDIO_TOP_CON1, 0xfffffff8 },
+};
+
+static int mt8188_afe_init_registers(struct mtk_base_afe *afe)
+{
+ return regmap_multi_reg_write(afe->regmap,
+ mt8188_afe_reg_defaults,
+ ARRAY_SIZE(mt8188_afe_reg_defaults));
+}
+
+static int mt8188_afe_parse_of(struct mtk_base_afe *afe,
+ struct device_node *np)
+{
+#if IS_ENABLED(CONFIG_SND_SOC_MT6359)
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+
+ afe_priv->topckgen = syscon_regmap_lookup_by_phandle(afe->dev->of_node,
+ "mediatek,topckgen");
+ if (IS_ERR(afe_priv->topckgen))
+ return dev_err_probe(afe->dev, PTR_ERR(afe_priv->topckgen),
+ "%s() Cannot find topckgen controller\n",
+ __func__);
+#endif
+ return 0;
+}
+
+static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ struct mtk_base_afe *afe;
+ struct mt8188_afe_private *afe_priv;
+ struct device *dev;
+ int i, irq_id, ret;
+ struct snd_soc_component *component;
+ struct reset_control *rstc;
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
+ if (ret)
+ return ret;
+
+ afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+
+ afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+ GFP_KERNEL);
+ if (!afe->platform_priv)
+ return -ENOMEM;
+
+ afe_priv = afe->platform_priv;
+ afe->dev = &pdev->dev;
+ dev = afe->dev;
+
+ afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(afe->base_addr))
+ return dev_err_probe(dev, PTR_ERR(afe->base_addr),
+ "AFE base_addr not found\n");
+
+ /* reset controller to reset audio regs before regmap cache */
+ rstc = devm_reset_control_get_exclusive(dev, "audiosys");
+ if (IS_ERR(rstc))
+ return dev_err_probe(dev, PTR_ERR(rstc),
+ "could not get audiosys reset\n");
+
+ ret = reset_control_reset(rstc);
+ if (ret) {
+ dev_err(dev, "failed to trigger audio reset:%d\n", ret);
+ return ret;
+ }
+
+ /* initial audio related clock */
+ ret = mt8188_afe_init_clock(afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "init clock error");
+
+ ret = devm_add_action_or_reset(dev, mt8188_afe_deinit_clock, (void *)afe);
+ if (ret)
+ return ret;
+
+ spin_lock_init(&afe_priv->afe_ctrl_lock);
+
+ mutex_init(&afe->irq_alloc_lock);
+
+ /* irq initialize */
+ afe->irqs_size = MT8188_AFE_IRQ_NUM;
+ afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+ GFP_KERNEL);
+ if (!afe->irqs)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->irqs_size; i++)
+ afe->irqs[i].irq_data = &irq_data[i];
+
+ /* init memif */
+ afe->memif_size = MT8188_AFE_MEMIF_NUM;
+ afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+ GFP_KERNEL);
+ if (!afe->memif)
+ return -ENOMEM;
+
+ for (i = 0; i < afe->memif_size; i++) {
+ afe->memif[i].data = &memif_data[i];
+ afe->memif[i].irq_usage = mt8188_afe_memif_const_irqs[i];
+ afe->memif[i].const_irq = 1;
+ afe->irqs[afe->memif[i].irq_usage].irq_occupyed = true;
+ }
+
+ /* request irq */
+ irq_id = platform_get_irq(pdev, 0);
+ if (irq_id < 0)
+ return dev_err_probe(dev, irq_id, "no irq found");
+
+ ret = devm_request_irq(dev, irq_id, mt8188_afe_irq_handler,
+ IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "could not request_irq for asys-isr\n");
+
+ /* init sub_dais */
+ INIT_LIST_HEAD(&afe->sub_dais);
+
+ for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
+ ret = dai_register_cbs[i](afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "dai register i %d fail\n", i);
+ }
+
+ /* init dai_driver and component_driver */
+ ret = mtk_afe_combine_sub_dai(afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n");
+
+ afe->mtk_afe_hardware = &mt8188_afe_hardware;
+ afe->memif_fs = mt8188_memif_fs;
+ afe->irq_fs = mt8188_irq_fs;
+
+ afe->runtime_resume = mt8188_afe_runtime_resume;
+ afe->runtime_suspend = mt8188_afe_runtime_suspend;
+
+ platform_set_drvdata(pdev, afe);
+
+ ret = mt8188_afe_parse_of(afe, pdev->dev.of_node);
+ if (ret)
+ return ret;
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ /* enable clock for regcache get default value from hw */
+ afe_priv->pm_runtime_bypass_reg_ctl = true;
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to resume device\n");
+
+ afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
+ &mt8188_afe_regmap_config);
+ if (IS_ERR(afe->regmap)) {
+ ret = PTR_ERR(afe->regmap);
+ goto err_pm_put;
+ }
+
+ ret = regmap_register_patch(afe->regmap, mt8188_cg_patch,
+ ARRAY_SIZE(mt8188_cg_patch));
+ if (ret < 0) {
+ dev_info(dev, "Failed to apply cg patch\n");
+ goto err_pm_put;
+ }
+
+ /* register component */
+ ret = devm_snd_soc_register_component(dev, &mt8188_afe_component,
+ NULL, 0);
+ if (ret) {
+ dev_warn(dev, "err_platform\n");
+ goto err_pm_put;
+ }
+
+ component = devm_kzalloc(&pdev->dev, sizeof(*component), GFP_KERNEL);
+ if (!component) {
+ ret = -ENOMEM;
+ goto err_pm_put;
+ }
+
+ ret = snd_soc_component_initialize(component,
+ &mt8188_afe_pcm_dai_component,
+ &pdev->dev);
+ if (ret)
+ goto err_pm_put;
+#ifdef CONFIG_DEBUG_FS
+ component->debugfs_prefix = "pcm";
+#endif
+ ret = snd_soc_add_component(component,
+ afe->dai_drivers,
+ afe->num_dai_drivers);
+ if (ret) {
+ dev_warn(dev, "err_add_component\n");
+ goto err_pm_put;
+ }
+
+ mt8188_afe_init_registers(afe);
+
+ pm_runtime_put_sync(&pdev->dev);
+ afe_priv->pm_runtime_bypass_reg_ctl = false;
+
+ regcache_cache_only(afe->regmap, true);
+ regcache_mark_dirty(afe->regmap);
+
+ return 0;
+err_pm_put:
+ pm_runtime_put_sync(dev);
+
+ return ret;
+}
+
+static int mt8188_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_component(&pdev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id mt8188_afe_pcm_dt_match[] = {
+ { .compatible = "mediatek,mt8188-afe", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt8188_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt8188_afe_pm_ops = {
+ SET_RUNTIME_PM_OPS(mt8188_afe_runtime_suspend,
+ mt8188_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt8188_afe_pcm_driver = {
+ .driver = {
+ .name = "mt8188-audio",
+ .of_match_table = mt8188_afe_pcm_dt_match,
+ .pm = &mt8188_afe_pm_ops,
+ },
+ .probe = mt8188_afe_pcm_dev_probe,
+ .remove = mt8188_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mt8188_afe_pcm_driver);
+
+MODULE_DESCRIPTION("MediaTek SoC AFE platform driver for ALSA 8188");
+MODULE_AUTHOR("Chun-Chia.Chiu <chun-chia.chiu@mediatek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c
new file mode 100644
index 00000000000000..be1c53bf472987
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt8188-audsys-clk.c -- MediaTek 8188 audsys clock control
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include "mt8188-afe-common.h"
+#include "mt8188-audsys-clk.h"
+#include "mt8188-audsys-clkid.h"
+#include "mt8188-reg.h"
+
+struct afe_gate {
+ int id;
+ const char *name;
+ const char *parent_name;
+ int reg;
+ u8 bit;
+ const struct clk_ops *ops;
+ unsigned long flags;
+ u8 cg_flags;
+};
+
+#define GATE_AFE_FLAGS(_id, _name, _parent, _reg, _bit, _flags, _cgflags) {\
+ .id = _id, \
+ .name = _name, \
+ .parent_name = _parent, \
+ .reg = _reg, \
+ .bit = _bit, \
+ .flags = _flags, \
+ .cg_flags = _cgflags, \
+ }
+
+#define GATE_AFE(_id, _name, _parent, _reg, _bit) \
+ GATE_AFE_FLAGS(_id, _name, _parent, _reg, _bit, \
+ CLK_SET_RATE_PARENT, CLK_GATE_SET_TO_DISABLE)
+
+#define GATE_AUD0(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON0, _bit)
+
+#define GATE_AUD1(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON1, _bit)
+
+#define GATE_AUD3(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON3, _bit)
+
+#define GATE_AUD4(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON4, _bit)
+
+#define GATE_AUD5(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON5, _bit)
+
+#define GATE_AUD6(_id, _name, _parent, _bit) \
+ GATE_AFE(_id, _name, _parent, AUDIO_TOP_CON6, _bit)
+
+static const struct afe_gate aud_clks[CLK_AUD_NR_CLK] = {
+ /* AUD0 */
+ GATE_AUD0(CLK_AUD_AFE, "aud_afe", "top_a1sys_hp", 2),
+ GATE_AUD0(CLK_AUD_LRCK_CNT, "aud_lrck_cnt", "top_a1sys_hp", 4),
+ GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_APLL, "aud_spdifin_tuner_apll", "top_apll4", 10),
+ GATE_AUD0(CLK_AUD_SPDIFIN_TUNER_DBG, "aud_spdifin_tuner_dbg", "top_apll4", 11),
+ GATE_AUD0(CLK_AUD_UL_TML, "aud_ul_tml", "top_a1sys_hp", 18),
+ GATE_AUD0(CLK_AUD_APLL1_TUNER, "aud_apll1_tuner", "top_apll1", 19),
+ GATE_AUD0(CLK_AUD_APLL2_TUNER, "aud_apll2_tuner", "top_apll2", 20),
+ GATE_AUD0(CLK_AUD_TOP0_SPDF, "aud_top0_spdf", "top_aud_iec_clk", 21),
+ GATE_AUD0(CLK_AUD_APLL, "aud_apll", "top_apll1", 23),
+ GATE_AUD0(CLK_AUD_APLL2, "aud_apll2", "top_apll2", 24),
+ GATE_AUD0(CLK_AUD_DAC, "aud_dac", "top_a1sys_hp", 25),
+ GATE_AUD0(CLK_AUD_DAC_PREDIS, "aud_dac_predis", "top_a1sys_hp", 26),
+ GATE_AUD0(CLK_AUD_TML, "aud_tml", "top_a1sys_hp", 27),
+ GATE_AUD0(CLK_AUD_ADC, "aud_adc", "top_a1sys_hp", 28),
+ GATE_AUD0(CLK_AUD_DAC_HIRES, "aud_dac_hires", "top_audio_h", 31),
+
+ /* AUD1 */
+ GATE_AUD1(CLK_AUD_A1SYS_HP, "aud_a1sys_hp", "top_a1sys_hp", 2),
+ GATE_AUD1(CLK_AUD_AFE_DMIC1, "aud_afe_dmic1", "top_a1sys_hp", 10),
+ GATE_AUD1(CLK_AUD_AFE_DMIC2, "aud_afe_dmic2", "top_a1sys_hp", 11),
+ GATE_AUD1(CLK_AUD_AFE_DMIC3, "aud_afe_dmic3", "top_a1sys_hp", 12),
+ GATE_AUD1(CLK_AUD_AFE_DMIC4, "aud_afe_dmic4", "top_a1sys_hp", 13),
+ GATE_AUD1(CLK_AUD_AFE_26M_DMIC_TM, "aud_afe_26m_dmic_tm", "top_a1sys_hp", 14),
+ GATE_AUD1(CLK_AUD_UL_TML_HIRES, "aud_ul_tml_hires", "top_audio_h", 16),
+ GATE_AUD1(CLK_AUD_ADC_HIRES, "aud_adc_hires", "top_audio_h", 17),
+
+ /* AUD3 */
+ GATE_AUD3(CLK_AUD_LINEIN_TUNER, "aud_linein_tuner", "top_apll5", 5),
+ GATE_AUD3(CLK_AUD_EARC_TUNER, "aud_earc_tuner", "top_apll3", 7),
+
+ /* AUD4 */
+ GATE_AUD4(CLK_AUD_I2SIN, "aud_i2sin", "top_a1sys_hp", 0),
+ GATE_AUD4(CLK_AUD_TDM_IN, "aud_tdm_in", "top_a1sys_hp", 1),
+ GATE_AUD4(CLK_AUD_I2S_OUT, "aud_i2s_out", "top_a1sys_hp", 6),
+ GATE_AUD4(CLK_AUD_TDM_OUT, "aud_tdm_out", "top_a1sys_hp", 7),
+ GATE_AUD4(CLK_AUD_HDMI_OUT, "aud_hdmi_out", "top_a1sys_hp", 8),
+ GATE_AUD4(CLK_AUD_ASRC11, "aud_asrc11", "top_a1sys_hp", 16),
+ GATE_AUD4(CLK_AUD_ASRC12, "aud_asrc12", "top_a1sys_hp", 17),
+ GATE_AUD4(CLK_AUD_MULTI_IN, "aud_multi_in", "mphone_slave_b", 19),
+ GATE_AUD4(CLK_AUD_INTDIR, "aud_intdir", "top_intdir", 20),
+ GATE_AUD4(CLK_AUD_A1SYS, "aud_a1sys", "top_a1sys_hp", 21),
+ GATE_AUD4(CLK_AUD_A2SYS, "aud_a2sys", "top_a2sys", 22),
+ GATE_AUD4(CLK_AUD_PCMIF, "aud_pcmif", "top_a1sys_hp", 24),
+ GATE_AUD4(CLK_AUD_A3SYS, "aud_a3sys", "top_a3sys", 30),
+ GATE_AUD4(CLK_AUD_A4SYS, "aud_a4sys", "top_a4sys", 31),
+
+ /* AUD5 */
+ GATE_AUD5(CLK_AUD_MEMIF_UL1, "aud_memif_ul1", "top_a1sys_hp", 0),
+ GATE_AUD5(CLK_AUD_MEMIF_UL2, "aud_memif_ul2", "top_a1sys_hp", 1),
+ GATE_AUD5(CLK_AUD_MEMIF_UL3, "aud_memif_ul3", "top_a1sys_hp", 2),
+ GATE_AUD5(CLK_AUD_MEMIF_UL4, "aud_memif_ul4", "top_a1sys_hp", 3),
+ GATE_AUD5(CLK_AUD_MEMIF_UL5, "aud_memif_ul5", "top_a1sys_hp", 4),
+ GATE_AUD5(CLK_AUD_MEMIF_UL6, "aud_memif_ul6", "top_a1sys_hp", 5),
+ GATE_AUD5(CLK_AUD_MEMIF_UL8, "aud_memif_ul8", "top_a1sys_hp", 7),
+ GATE_AUD5(CLK_AUD_MEMIF_UL9, "aud_memif_ul9", "top_a1sys_hp", 8),
+ GATE_AUD5(CLK_AUD_MEMIF_UL10, "aud_memif_ul10", "top_a1sys_hp", 9),
+ GATE_AUD5(CLK_AUD_MEMIF_DL2, "aud_memif_dl2", "top_a1sys_hp", 18),
+ GATE_AUD5(CLK_AUD_MEMIF_DL3, "aud_memif_dl3", "top_a1sys_hp", 19),
+ GATE_AUD5(CLK_AUD_MEMIF_DL6, "aud_memif_dl6", "top_a1sys_hp", 22),
+ GATE_AUD5(CLK_AUD_MEMIF_DL7, "aud_memif_dl7", "top_a1sys_hp", 23),
+ GATE_AUD5(CLK_AUD_MEMIF_DL8, "aud_memif_dl8", "top_a1sys_hp", 24),
+ GATE_AUD5(CLK_AUD_MEMIF_DL10, "aud_memif_dl10", "top_a1sys_hp", 26),
+ GATE_AUD5(CLK_AUD_MEMIF_DL11, "aud_memif_dl11", "top_a1sys_hp", 27),
+
+ /* AUD6 */
+ GATE_AUD6(CLK_AUD_GASRC0, "aud_gasrc0", "top_asm_h", 0),
+ GATE_AUD6(CLK_AUD_GASRC1, "aud_gasrc1", "top_asm_h", 1),
+ GATE_AUD6(CLK_AUD_GASRC2, "aud_gasrc2", "top_asm_h", 2),
+ GATE_AUD6(CLK_AUD_GASRC3, "aud_gasrc3", "top_asm_h", 3),
+ GATE_AUD6(CLK_AUD_GASRC4, "aud_gasrc4", "top_asm_h", 4),
+ GATE_AUD6(CLK_AUD_GASRC5, "aud_gasrc5", "top_asm_h", 5),
+ GATE_AUD6(CLK_AUD_GASRC6, "aud_gasrc6", "top_asm_h", 6),
+ GATE_AUD6(CLK_AUD_GASRC7, "aud_gasrc7", "top_asm_h", 7),
+ GATE_AUD6(CLK_AUD_GASRC8, "aud_gasrc8", "top_asm_h", 8),
+ GATE_AUD6(CLK_AUD_GASRC9, "aud_gasrc9", "top_asm_h", 9),
+ GATE_AUD6(CLK_AUD_GASRC10, "aud_gasrc10", "top_asm_h", 10),
+ GATE_AUD6(CLK_AUD_GASRC11, "aud_gasrc11", "top_asm_h", 11),
+};
+
+int mt8188_audsys_clk_register(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct clk *clk;
+ struct clk_lookup *cl;
+ int i;
+
+ afe_priv->lookup = devm_kcalloc(afe->dev, CLK_AUD_NR_CLK,
+ sizeof(*afe_priv->lookup),
+ GFP_KERNEL);
+
+ if (!afe_priv->lookup)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
+ const struct afe_gate *gate = &aud_clks[i];
+
+ clk = clk_register_gate(afe->dev, gate->name, gate->parent_name,
+ gate->flags, afe->base_addr + gate->reg,
+ gate->bit, gate->cg_flags, NULL);
+
+ if (IS_ERR(clk)) {
+ dev_err(afe->dev, "Failed to register clk %s: %ld\n",
+ gate->name, PTR_ERR(clk));
+ continue;
+ }
+
+ /* add clk_lookup for devm_clk_get(SND_SOC_DAPM_CLOCK_SUPPLY) */
+ cl = kzalloc(sizeof(*cl), GFP_KERNEL);
+ if (!cl)
+ return -ENOMEM;
+
+ cl->clk = clk;
+ cl->con_id = gate->name;
+ cl->dev_id = dev_name(afe->dev);
+ cl->clk_hw = NULL;
+ clkdev_add(cl);
+
+ afe_priv->lookup[i] = cl;
+ }
+
+ return 0;
+}
+
+void mt8188_audsys_clk_unregister(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct clk *clk;
+ struct clk_lookup *cl;
+ int i;
+
+ if (!afe_priv)
+ return;
+
+ for (i = 0; i < CLK_AUD_NR_CLK; i++) {
+ cl = afe_priv->lookup[i];
+ if (!cl)
+ continue;
+
+ clk = cl->clk;
+ clk_unregister_gate(clk);
+
+ clkdev_drop(cl);
+ }
+}
diff --git a/sound/soc/mediatek/mt8188/mt8188-audsys-clk.h b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.h
new file mode 100644
index 00000000000000..6c5f463ad7e4dd
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8188-audsys-clk.h -- MediaTek 8188 audsys clock definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#ifndef _MT8188_AUDSYS_CLK_H_
+#define _MT8188_AUDSYS_CLK_H_
+
+int mt8188_audsys_clk_register(struct mtk_base_afe *afe);
+void mt8188_audsys_clk_unregister(struct mtk_base_afe *afe);
+
+#endif
diff --git a/sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h b/sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h
new file mode 100644
index 00000000000000..6f34ffc760e03b
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-audsys-clkid.h
@@ -0,0 +1,83 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8188-audsys-clkid.h -- MediaTek 8188 audsys clock id definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#ifndef _MT8188_AUDSYS_CLKID_H_
+#define _MT8188_AUDSYS_CLKID_H_
+
+enum{
+ CLK_AUD_AFE,
+ CLK_AUD_LRCK_CNT,
+ CLK_AUD_SPDIFIN_TUNER_APLL,
+ CLK_AUD_SPDIFIN_TUNER_DBG,
+ CLK_AUD_UL_TML,
+ CLK_AUD_APLL1_TUNER,
+ CLK_AUD_APLL2_TUNER,
+ CLK_AUD_TOP0_SPDF,
+ CLK_AUD_APLL,
+ CLK_AUD_APLL2,
+ CLK_AUD_DAC,
+ CLK_AUD_DAC_PREDIS,
+ CLK_AUD_TML,
+ CLK_AUD_ADC,
+ CLK_AUD_DAC_HIRES,
+ CLK_AUD_A1SYS_HP,
+ CLK_AUD_AFE_DMIC1,
+ CLK_AUD_AFE_DMIC2,
+ CLK_AUD_AFE_DMIC3,
+ CLK_AUD_AFE_DMIC4,
+ CLK_AUD_AFE_26M_DMIC_TM,
+ CLK_AUD_UL_TML_HIRES,
+ CLK_AUD_ADC_HIRES,
+ CLK_AUD_LINEIN_TUNER,
+ CLK_AUD_EARC_TUNER,
+ CLK_AUD_I2SIN,
+ CLK_AUD_TDM_IN,
+ CLK_AUD_I2S_OUT,
+ CLK_AUD_TDM_OUT,
+ CLK_AUD_HDMI_OUT,
+ CLK_AUD_ASRC11,
+ CLK_AUD_ASRC12,
+ CLK_AUD_MULTI_IN,
+ CLK_AUD_INTDIR,
+ CLK_AUD_A1SYS,
+ CLK_AUD_A2SYS,
+ CLK_AUD_PCMIF,
+ CLK_AUD_A3SYS,
+ CLK_AUD_A4SYS,
+ CLK_AUD_MEMIF_UL1,
+ CLK_AUD_MEMIF_UL2,
+ CLK_AUD_MEMIF_UL3,
+ CLK_AUD_MEMIF_UL4,
+ CLK_AUD_MEMIF_UL5,
+ CLK_AUD_MEMIF_UL6,
+ CLK_AUD_MEMIF_UL8,
+ CLK_AUD_MEMIF_UL9,
+ CLK_AUD_MEMIF_UL10,
+ CLK_AUD_MEMIF_DL2,
+ CLK_AUD_MEMIF_DL3,
+ CLK_AUD_MEMIF_DL6,
+ CLK_AUD_MEMIF_DL7,
+ CLK_AUD_MEMIF_DL8,
+ CLK_AUD_MEMIF_DL10,
+ CLK_AUD_MEMIF_DL11,
+ CLK_AUD_GASRC0,
+ CLK_AUD_GASRC1,
+ CLK_AUD_GASRC2,
+ CLK_AUD_GASRC3,
+ CLK_AUD_GASRC4,
+ CLK_AUD_GASRC5,
+ CLK_AUD_GASRC6,
+ CLK_AUD_GASRC7,
+ CLK_AUD_GASRC8,
+ CLK_AUD_GASRC9,
+ CLK_AUD_GASRC10,
+ CLK_AUD_GASRC11,
+ CLK_AUD_NR_CLK,
+};
+
+#endif
diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-adda.c b/sound/soc/mediatek/mt8188/mt8188-dai-adda.c
new file mode 100644
index 00000000000000..d71696901553ad
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-dai-adda.c
@@ -0,0 +1,632 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI ADDA Control
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include "mt8188-afe-clk.h"
+#include "mt8188-afe-common.h"
+#include "mt8188-reg.h"
+
+#define ADDA_HIRES_THRES 48000
+
+enum {
+ SUPPLY_SEQ_CLOCK_SEL,
+ SUPPLY_SEQ_ADDA_DL_ON,
+ SUPPLY_SEQ_ADDA_MTKAIF_CFG,
+ SUPPLY_SEQ_ADDA_UL_ON,
+ SUPPLY_SEQ_ADDA_AFE_ON,
+};
+
+enum {
+ MTK_AFE_ADDA_DL_RATE_8K = 0,
+ MTK_AFE_ADDA_DL_RATE_11K = 1,
+ MTK_AFE_ADDA_DL_RATE_12K = 2,
+ MTK_AFE_ADDA_DL_RATE_16K = 3,
+ MTK_AFE_ADDA_DL_RATE_22K = 4,
+ MTK_AFE_ADDA_DL_RATE_24K = 5,
+ MTK_AFE_ADDA_DL_RATE_32K = 6,
+ MTK_AFE_ADDA_DL_RATE_44K = 7,
+ MTK_AFE_ADDA_DL_RATE_48K = 8,
+ MTK_AFE_ADDA_DL_RATE_96K = 9,
+ MTK_AFE_ADDA_DL_RATE_192K = 10,
+};
+
+enum {
+ MTK_AFE_ADDA_UL_RATE_8K = 0,
+ MTK_AFE_ADDA_UL_RATE_16K = 1,
+ MTK_AFE_ADDA_UL_RATE_32K = 2,
+ MTK_AFE_ADDA_UL_RATE_48K = 3,
+ MTK_AFE_ADDA_UL_RATE_96K = 4,
+ MTK_AFE_ADDA_UL_RATE_192K = 5,
+};
+
+enum {
+ DELAY_DATA_MISO1 = 0,
+ DELAY_DATA_MISO0 = 1,
+};
+
+struct mtk_dai_adda_priv {
+ unsigned int dl_rate;
+ unsigned int ul_rate;
+};
+
+static unsigned int afe_adda_dl_rate_transform(struct mtk_base_afe *afe,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_ADDA_DL_RATE_8K;
+ case 11025:
+ return MTK_AFE_ADDA_DL_RATE_11K;
+ case 12000:
+ return MTK_AFE_ADDA_DL_RATE_12K;
+ case 16000:
+ return MTK_AFE_ADDA_DL_RATE_16K;
+ case 22050:
+ return MTK_AFE_ADDA_DL_RATE_22K;
+ case 24000:
+ return MTK_AFE_ADDA_DL_RATE_24K;
+ case 32000:
+ return MTK_AFE_ADDA_DL_RATE_32K;
+ case 44100:
+ return MTK_AFE_ADDA_DL_RATE_44K;
+ case 48000:
+ return MTK_AFE_ADDA_DL_RATE_48K;
+ case 96000:
+ return MTK_AFE_ADDA_DL_RATE_96K;
+ case 192000:
+ return MTK_AFE_ADDA_DL_RATE_192K;
+ default:
+ dev_info(afe->dev, "%s(), rate %u invalid, use 48kHz!!!\n",
+ __func__, rate);
+ return MTK_AFE_ADDA_DL_RATE_48K;
+ }
+}
+
+static unsigned int afe_adda_ul_rate_transform(struct mtk_base_afe *afe,
+ unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return MTK_AFE_ADDA_UL_RATE_8K;
+ case 16000:
+ return MTK_AFE_ADDA_UL_RATE_16K;
+ case 32000:
+ return MTK_AFE_ADDA_UL_RATE_32K;
+ case 48000:
+ return MTK_AFE_ADDA_UL_RATE_48K;
+ case 96000:
+ return MTK_AFE_ADDA_UL_RATE_96K;
+ case 192000:
+ return MTK_AFE_ADDA_UL_RATE_192K;
+ default:
+ dev_info(afe->dev, "%s(), rate %u invalid, use 48kHz!!!\n",
+ __func__, rate);
+ return MTK_AFE_ADDA_UL_RATE_48K;
+ }
+}
+
+static int mt8188_adda_mtkaif_init(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+ int delay_data;
+ int delay_cycle;
+ unsigned int mask = 0;
+ unsigned int val = 0;
+
+ /* set rx protocol 2 & mtkaif_rxif_clkinv_adc inverse */
+ regmap_set_bits(afe->regmap, AFE_ADDA_MTKAIF_CFG0,
+ MTKAIF_RXIF_CLKINV_ADC | MTKAIF_RXIF_PROTOCOL2);
+
+ regmap_set_bits(afe->regmap, AFE_AUD_PAD_TOP, RG_RX_PROTOCOL2);
+
+ if (!param->mtkaif_calibration_ok) {
+ dev_info(afe->dev, "%s(), calibration fail\n", __func__);
+ return 0;
+ }
+
+ /* set delay for ch1, ch2 */
+ if (param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_0] >=
+ param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_1]) {
+ delay_data = DELAY_DATA_MISO1;
+ delay_cycle =
+ param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_0] -
+ param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_1];
+ } else {
+ delay_data = DELAY_DATA_MISO0;
+ delay_cycle =
+ param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_1] -
+ param->mtkaif_phase_cycle[MT8188_MTKAIF_MISO_0];
+ }
+
+ val = 0;
+ mask = (MTKAIF_RXIF_DELAY_DATA | MTKAIF_RXIF_DELAY_CYCLE_MASK);
+ val |= FIELD_PREP(MTKAIF_RXIF_DELAY_CYCLE_MASK, delay_cycle);
+ val |= FIELD_PREP(MTKAIF_RXIF_DELAY_DATA, delay_data);
+ regmap_update_bits(afe->regmap, AFE_ADDA_MTKAIF_RX_CFG2, mask, val);
+
+ return 0;
+}
+
+static int mtk_adda_mtkaif_cfg_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mt8188_adda_mtkaif_init(afe);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_adda_dl_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 135);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void mtk_adda_ul_mictype(struct mtk_base_afe *afe, bool dmic)
+{
+ unsigned int reg = AFE_ADDA_UL_SRC_CON0;
+ unsigned int val;
+
+ val = (UL_SDM3_LEVEL_CTL | UL_MODE_3P25M_CH1_CTL |
+ UL_MODE_3P25M_CH2_CTL);
+
+ /* turn on dmic, ch1, ch2 */
+ if (dmic)
+ regmap_set_bits(afe->regmap, reg, val);
+ else
+ regmap_clear_bits(afe->regmap, reg, val);
+}
+
+static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ mtk_adda_ul_mictype(afe, param->mtkaif_dmic_on);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* should delayed 1/fs(smallest is 8k) = 125us before afe off */
+ usleep_range(125, 135);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_audio_hires_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct clk *clk = afe_priv->clk[MT8188_CLK_TOP_AUDIO_H_SEL];
+ struct clk *clk_parent;
+
+ dev_dbg(afe->dev, "%s(), name %s, event 0x%x\n",
+ __func__, w->name, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ clk_parent = afe_priv->clk[MT8188_CLK_APMIXED_APLL1];
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ clk_parent = afe_priv->clk[MT8188_CLK_XTAL_26M];
+ break;
+ default:
+ return 0;
+ }
+ mt8188_afe_set_clk_parent(afe, clk, clk_parent);
+
+ return 0;
+}
+
+static int mtk_afe_adc_hires_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = source;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_adda_priv *adda_priv;
+
+ adda_priv = afe_priv->dai_priv[MT8188_AFE_IO_ADDA];
+
+ if (!adda_priv) {
+ dev_err(afe->dev, "%s adda_priv == NULL", __func__);
+ return 0;
+ }
+
+ return !!(adda_priv->ul_rate > ADDA_HIRES_THRES);
+}
+
+static int mtk_afe_dac_hires_connect(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink)
+{
+ struct snd_soc_dapm_widget *w = source;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_adda_priv *adda_priv;
+
+ adda_priv = afe_priv->dai_priv[MT8188_AFE_IO_ADDA];
+
+ if (!adda_priv) {
+ dev_err(afe->dev, "%s adda_priv == NULL", __func__);
+ return 0;
+ }
+
+ return !!(adda_priv->dl_rate > ADDA_HIRES_THRES);
+}
+
+static const struct snd_kcontrol_new mtk_dai_adda_o176_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN176, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I002 Switch", AFE_CONN176, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN176, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN176, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN176_2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_adda_o177_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN177, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I003 Switch", AFE_CONN177, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN177, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN177, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN177_2, 7, 1, 0),
+};
+
+static const char * const adda_dlgain_mux_map[] = {
+ "Bypass", "Connect",
+};
+
+static SOC_ENUM_SINGLE_DECL(adda_dlgain_mux_map_enum,
+ SND_SOC_NOPM, 0,
+ adda_dlgain_mux_map);
+
+static const struct snd_kcontrol_new adda_dlgain_mux_control =
+ SOC_DAPM_ENUM("DL_GAIN_MUX", adda_dlgain_mux_map_enum);
+
+static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
+ SND_SOC_DAPM_MIXER("I168", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I169", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MIXER("O176", SND_SOC_NOPM, 0, 0,
+ mtk_dai_adda_o176_mix,
+ ARRAY_SIZE(mtk_dai_adda_o176_mix)),
+ SND_SOC_DAPM_MIXER("O177", SND_SOC_NOPM, 0, 0,
+ mtk_dai_adda_o177_mix,
+ ARRAY_SIZE(mtk_dai_adda_o177_mix)),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Enable", SUPPLY_SEQ_ADDA_AFE_ON,
+ AFE_ADDA_UL_DL_CON0,
+ ADDA_AFE_ON_SHIFT, 0,
+ NULL,
+ 0),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Playback Enable", SUPPLY_SEQ_ADDA_DL_ON,
+ AFE_ADDA_DL_SRC2_CON0,
+ DL_2_SRC_ON_TMP_CTRL_PRE_SHIFT, 0,
+ mtk_adda_dl_event,
+ SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA Capture Enable", SUPPLY_SEQ_ADDA_UL_ON,
+ AFE_ADDA_UL_SRC_CON0,
+ UL_SRC_ON_TMP_CTL_SHIFT, 0,
+ mtk_adda_ul_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_HIRES", SUPPLY_SEQ_CLOCK_SEL,
+ SND_SOC_NOPM,
+ 0, 0,
+ mtk_audio_hires_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY_S("ADDA_MTKAIF_CFG", SUPPLY_SEQ_ADDA_MTKAIF_CFG,
+ SND_SOC_NOPM,
+ 0, 0,
+ mtk_adda_mtkaif_cfg_event,
+ SND_SOC_DAPM_PRE_PMU),
+
+ SND_SOC_DAPM_MUX("DL_GAIN_MUX", SND_SOC_NOPM, 0, 0,
+ &adda_dlgain_mux_control),
+
+ SND_SOC_DAPM_PGA("DL_GAIN", AFE_ADDA_DL_SRC2_CON0,
+ DL_2_GAIN_ON_CTL_PRE_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("ADDA_INPUT"),
+ SND_SOC_DAPM_OUTPUT("ADDA_OUTPUT"),
+
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_dac_hires"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_adc_hires"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_adda_routes[] = {
+ {"ADDA Capture", NULL, "ADDA Enable"},
+ {"ADDA Capture", NULL, "ADDA Capture Enable"},
+ {"ADDA Capture", NULL, "ADDA_MTKAIF_CFG"},
+ {"ADDA Capture", NULL, "aud_adc"},
+ {"ADDA Capture", NULL, "aud_adc_hires", mtk_afe_adc_hires_connect},
+ {"aud_adc_hires", NULL, "AUDIO_HIRES"},
+
+ {"I168", NULL, "ADDA Capture"},
+ {"I169", NULL, "ADDA Capture"},
+
+ {"ADDA Playback", NULL, "ADDA Enable"},
+ {"ADDA Playback", NULL, "ADDA Playback Enable"},
+ {"ADDA Playback", NULL, "aud_dac"},
+ {"ADDA Playback", NULL, "aud_dac_hires", mtk_afe_dac_hires_connect},
+ {"aud_dac_hires", NULL, "AUDIO_HIRES"},
+
+ {"DL_GAIN", NULL, "O176"},
+ {"DL_GAIN", NULL, "O177"},
+
+ {"DL_GAIN_MUX", "Bypass", "O176"},
+ {"DL_GAIN_MUX", "Bypass", "O177"},
+ {"DL_GAIN_MUX", "Connect", "DL_GAIN"},
+
+ {"ADDA Playback", NULL, "DL_GAIN_MUX"},
+
+ {"O176", "I000 Switch", "I000"},
+ {"O177", "I001 Switch", "I001"},
+
+ {"O176", "I002 Switch", "I002"},
+ {"O177", "I003 Switch", "I003"},
+
+ {"O176", "I020 Switch", "I020"},
+ {"O177", "I021 Switch", "I021"},
+
+ {"O176", "I022 Switch", "I022"},
+ {"O177", "I023 Switch", "I023"},
+
+ {"O176", "I070 Switch", "I070"},
+ {"O177", "I071 Switch", "I071"},
+
+ {"ADDA Capture", NULL, "ADDA_INPUT"},
+ {"ADDA_OUTPUT", NULL, "ADDA Playback"},
+};
+
+static int mt8188_adda_dmic_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+
+ ucontrol->value.integer.value[0] = param->mtkaif_dmic_on;
+ return 0;
+}
+
+static int mt8188_adda_dmic_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtkaif_param *param = &afe_priv->mtkaif_params;
+ int dmic_on;
+
+ dmic_on = !!ucontrol->value.integer.value[0];
+
+ dev_dbg(afe->dev, "%s(), kcontrol name %s, dmic_on %d\n",
+ __func__, kcontrol->id.name, dmic_on);
+
+ if (param->mtkaif_dmic_on == dmic_on)
+ return 0;
+
+ param->mtkaif_dmic_on = dmic_on;
+ return 1;
+}
+
+static const struct snd_kcontrol_new mtk_dai_adda_controls[] = {
+ SOC_SINGLE("ADDA_DL_GAIN", AFE_ADDA_DL_SRC2_CON1,
+ DL_2_GAIN_CTL_PRE_SHIFT, 65535, 0),
+ SOC_SINGLE_BOOL_EXT("MTKAIF_DMIC Switch", 0,
+ mt8188_adda_dmic_get, mt8188_adda_dmic_set),
+};
+
+static int mtk_dai_da_configure(struct mtk_base_afe *afe,
+ unsigned int rate, int id)
+{
+ unsigned int val = 0;
+ unsigned int mask = 0;
+
+ /* set sampling rate */
+ mask |= DL_2_INPUT_MODE_CTL_MASK;
+ val |= FIELD_PREP(DL_2_INPUT_MODE_CTL_MASK,
+ afe_adda_dl_rate_transform(afe, rate));
+
+ /* turn off saturation */
+ mask |= DL_2_CH1_SATURATION_EN_CTL;
+ mask |= DL_2_CH2_SATURATION_EN_CTL;
+
+ /* turn off mute function */
+ mask |= DL_2_MUTE_CH1_OFF_CTL_PRE;
+ mask |= DL_2_MUTE_CH2_OFF_CTL_PRE;
+ val |= DL_2_MUTE_CH1_OFF_CTL_PRE;
+ val |= DL_2_MUTE_CH2_OFF_CTL_PRE;
+
+ /* set voice input data if input sample rate is 8k or 16k */
+ mask |= DL_2_VOICE_MODE_CTL_PRE;
+ if (rate == 8000 || rate == 16000)
+ val |= DL_2_VOICE_MODE_CTL_PRE;
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_DL_SRC2_CON0, mask, val);
+
+ /* new 2nd sdm */
+ regmap_set_bits(afe->regmap, AFE_ADDA_DL_SDM_DCCOMP_CON,
+ DL_USE_NEW_2ND_SDM);
+
+ return 0;
+}
+
+static int mtk_dai_ad_configure(struct mtk_base_afe *afe,
+ unsigned int rate, int id)
+{
+ unsigned int val;
+ unsigned int mask;
+
+ mask = UL_VOICE_MODE_CTL_MASK;
+ val = FIELD_PREP(UL_VOICE_MODE_CTL_MASK,
+ afe_adda_ul_rate_transform(afe, rate));
+
+ regmap_update_bits(afe->regmap, AFE_ADDA_UL_SRC_CON0,
+ mask, val);
+ return 0;
+}
+
+static int mtk_dai_adda_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_adda_priv *adda_priv = afe_priv->dai_priv[dai->id];
+ unsigned int rate = params_rate(params);
+ int id = dai->id;
+ int ret = 0;
+
+ dev_dbg(afe->dev, "%s(), id %d, stream %d, rate %u\n",
+ __func__, id, substream->stream, rate);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ adda_priv->dl_rate = rate;
+ ret = mtk_dai_da_configure(afe, rate, id);
+ } else {
+ adda_priv->ul_rate = rate;
+ ret = mtk_dai_ad_configure(afe, rate, id);
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_adda_ops = {
+ .hw_params = mtk_dai_adda_hw_params,
+};
+
+/* dai driver */
+#define MTK_ADDA_PLAYBACK_RATES (SNDRV_PCM_RATE_8000_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ADDA_CAPTURE_RATES (SNDRV_PCM_RATE_8000 |\
+ SNDRV_PCM_RATE_16000 |\
+ SNDRV_PCM_RATE_32000 |\
+ SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 |\
+ SNDRV_PCM_RATE_192000)
+
+#define MTK_ADDA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_adda_driver[] = {
+ {
+ .name = "ADDA",
+ .id = MT8188_AFE_IO_ADDA,
+ .playback = {
+ .stream_name = "ADDA Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_PLAYBACK_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ADDA Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_ADDA_CAPTURE_RATES,
+ .formats = MTK_ADDA_FORMATS,
+ },
+ .ops = &mtk_dai_adda_ops,
+ },
+};
+
+static int init_adda_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_adda_priv *adda_priv;
+
+ adda_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_adda_priv),
+ GFP_KERNEL);
+ if (!adda_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[MT8188_AFE_IO_ADDA] = adda_priv;
+
+ return 0;
+}
+
+int mt8188_dai_adda_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_adda_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_adda_driver);
+
+ dai->dapm_widgets = mtk_dai_adda_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_adda_widgets);
+ dai->dapm_routes = mtk_dai_adda_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_adda_routes);
+ dai->controls = mtk_dai_adda_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_dai_adda_controls);
+
+ return init_adda_priv_data(afe);
+}
diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c b/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c
new file mode 100644
index 00000000000000..071841903c6200
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-dai-etdm.c
@@ -0,0 +1,2573 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI eTDM Control
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8188-afe-clk.h"
+#include "mt8188-afe-common.h"
+#include "mt8188-reg.h"
+
+#define MT8188_ETDM_MAX_CHANNELS 16
+#define MT8188_ETDM_NORMAL_MAX_BCK_RATE 24576000
+#define ETDM_TO_DAI_ID(x) ((x) + MT8188_AFE_IO_ETDM_START)
+#define ENUM_TO_STR(x) #x
+
+enum {
+ MTK_DAI_ETDM_FORMAT_I2S = 0,
+ MTK_DAI_ETDM_FORMAT_LJ,
+ MTK_DAI_ETDM_FORMAT_RJ,
+ MTK_DAI_ETDM_FORMAT_EIAJ,
+ MTK_DAI_ETDM_FORMAT_DSPA,
+ MTK_DAI_ETDM_FORMAT_DSPB,
+};
+
+enum {
+ MTK_DAI_ETDM_DATA_ONE_PIN = 0,
+ MTK_DAI_ETDM_DATA_MULTI_PIN,
+};
+
+enum {
+ ETDM_IN,
+ ETDM_OUT,
+};
+
+enum {
+ COWORK_ETDM_NONE = 0,
+ COWORK_ETDM_IN1_M = 2,
+ COWORK_ETDM_IN1_S = 3,
+ COWORK_ETDM_IN2_M = 4,
+ COWORK_ETDM_IN2_S = 5,
+ COWORK_ETDM_OUT1_M = 10,
+ COWORK_ETDM_OUT1_S = 11,
+ COWORK_ETDM_OUT2_M = 12,
+ COWORK_ETDM_OUT2_S = 13,
+ COWORK_ETDM_OUT3_M = 14,
+ COWORK_ETDM_OUT3_S = 15,
+};
+
+enum {
+ ETDM_RELATCH_TIMING_A1A2SYS,
+ ETDM_RELATCH_TIMING_A3SYS,
+ ETDM_RELATCH_TIMING_A4SYS,
+};
+
+enum {
+ ETDM_SYNC_NONE,
+ ETDM_SYNC_FROM_IN1 = 2,
+ ETDM_SYNC_FROM_IN2 = 4,
+ ETDM_SYNC_FROM_OUT1 = 10,
+ ETDM_SYNC_FROM_OUT2 = 12,
+ ETDM_SYNC_FROM_OUT3 = 14,
+};
+
+struct etdm_con_reg {
+ unsigned int con0;
+ unsigned int con1;
+ unsigned int con2;
+ unsigned int con3;
+ unsigned int con4;
+ unsigned int con5;
+};
+
+struct mtk_dai_etdm_rate {
+ unsigned int rate;
+ unsigned int reg_value;
+};
+
+struct mtk_dai_etdm_priv {
+ unsigned int clock_mode;
+ unsigned int data_mode;
+ bool slave_mode;
+ bool lrck_inv;
+ bool bck_inv;
+ unsigned int format;
+ unsigned int slots;
+ unsigned int lrck_width;
+ unsigned int mclk_freq;
+ unsigned int mclk_fixed_apll;
+ unsigned int mclk_apll;
+ unsigned int mclk_dir;
+ int cowork_source_id; //dai id
+ unsigned int cowork_slv_count;
+ int cowork_slv_id[MT8188_AFE_IO_ETDM_NUM - 1]; //dai_id
+ bool in_disable_ch[MT8188_ETDM_MAX_CHANNELS];
+ unsigned int en_ref_cnt;
+ bool is_prepared;
+};
+
+static const struct mtk_dai_etdm_rate mt8188_etdm_rates[] = {
+ { .rate = 8000, .reg_value = 0, },
+ { .rate = 12000, .reg_value = 1, },
+ { .rate = 16000, .reg_value = 2, },
+ { .rate = 24000, .reg_value = 3, },
+ { .rate = 32000, .reg_value = 4, },
+ { .rate = 48000, .reg_value = 5, },
+ { .rate = 96000, .reg_value = 7, },
+ { .rate = 192000, .reg_value = 9, },
+ { .rate = 384000, .reg_value = 11, },
+ { .rate = 11025, .reg_value = 16, },
+ { .rate = 22050, .reg_value = 17, },
+ { .rate = 44100, .reg_value = 18, },
+ { .rate = 88200, .reg_value = 19, },
+ { .rate = 176400, .reg_value = 20, },
+ { .rate = 352800, .reg_value = 21, },
+};
+
+static int get_etdm_fs_timing(unsigned int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mt8188_etdm_rates); i++)
+ if (mt8188_etdm_rates[i].rate == rate)
+ return mt8188_etdm_rates[i].reg_value;
+
+ return -EINVAL;
+}
+
+static unsigned int get_etdm_ch_fixup(unsigned int channels)
+{
+ if (channels > 16)
+ return 24;
+ else if (channels > 8)
+ return 16;
+ else if (channels > 4)
+ return 8;
+ else if (channels > 2)
+ return 4;
+ else
+ return 2;
+}
+
+static int get_etdm_reg(unsigned int dai_id, struct etdm_con_reg *etdm_reg)
+{
+ switch (dai_id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ etdm_reg->con0 = ETDM_IN1_CON0;
+ etdm_reg->con1 = ETDM_IN1_CON1;
+ etdm_reg->con2 = ETDM_IN1_CON2;
+ etdm_reg->con3 = ETDM_IN1_CON3;
+ etdm_reg->con4 = ETDM_IN1_CON4;
+ etdm_reg->con5 = ETDM_IN1_CON5;
+ break;
+ case MT8188_AFE_IO_ETDM2_IN:
+ etdm_reg->con0 = ETDM_IN2_CON0;
+ etdm_reg->con1 = ETDM_IN2_CON1;
+ etdm_reg->con2 = ETDM_IN2_CON2;
+ etdm_reg->con3 = ETDM_IN2_CON3;
+ etdm_reg->con4 = ETDM_IN2_CON4;
+ etdm_reg->con5 = ETDM_IN2_CON5;
+ break;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ etdm_reg->con0 = ETDM_OUT1_CON0;
+ etdm_reg->con1 = ETDM_OUT1_CON1;
+ etdm_reg->con2 = ETDM_OUT1_CON2;
+ etdm_reg->con3 = ETDM_OUT1_CON3;
+ etdm_reg->con4 = ETDM_OUT1_CON4;
+ etdm_reg->con5 = ETDM_OUT1_CON5;
+ break;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ etdm_reg->con0 = ETDM_OUT2_CON0;
+ etdm_reg->con1 = ETDM_OUT2_CON1;
+ etdm_reg->con2 = ETDM_OUT2_CON2;
+ etdm_reg->con3 = ETDM_OUT2_CON3;
+ etdm_reg->con4 = ETDM_OUT2_CON4;
+ etdm_reg->con5 = ETDM_OUT2_CON5;
+ break;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ case MT8188_AFE_IO_DPTX:
+ etdm_reg->con0 = ETDM_OUT3_CON0;
+ etdm_reg->con1 = ETDM_OUT3_CON1;
+ etdm_reg->con2 = ETDM_OUT3_CON2;
+ etdm_reg->con3 = ETDM_OUT3_CON3;
+ etdm_reg->con4 = ETDM_OUT3_CON4;
+ etdm_reg->con5 = ETDM_OUT3_CON5;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int get_etdm_dir(unsigned int dai_id)
+{
+ switch (dai_id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ case MT8188_AFE_IO_ETDM2_IN:
+ return ETDM_IN;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ case MT8188_AFE_IO_ETDM2_OUT:
+ case MT8188_AFE_IO_ETDM3_OUT:
+ return ETDM_OUT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int get_etdm_wlen(unsigned int bitwidth)
+{
+ return bitwidth <= 16 ? 16 : 32;
+}
+
+static bool is_valid_etdm_dai(int dai_id)
+{
+ switch (dai_id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ fallthrough;
+ case MT8188_AFE_IO_ETDM2_IN:
+ fallthrough;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ fallthrough;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ fallthrough;
+ case MT8188_AFE_IO_DPTX:
+ fallthrough;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int is_cowork_mode(struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+
+ if (!is_valid_etdm_dai(dai->id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai->id];
+
+ return (etdm_data->cowork_slv_count > 0 ||
+ etdm_data->cowork_source_id != COWORK_ETDM_NONE);
+}
+
+static int sync_to_dai_id(int source_sel)
+{
+ switch (source_sel) {
+ case ETDM_SYNC_FROM_IN1:
+ return MT8188_AFE_IO_ETDM1_IN;
+ case ETDM_SYNC_FROM_IN2:
+ return MT8188_AFE_IO_ETDM2_IN;
+ case ETDM_SYNC_FROM_OUT1:
+ return MT8188_AFE_IO_ETDM1_OUT;
+ case ETDM_SYNC_FROM_OUT2:
+ return MT8188_AFE_IO_ETDM2_OUT;
+ case ETDM_SYNC_FROM_OUT3:
+ return MT8188_AFE_IO_ETDM3_OUT;
+ default:
+ return 0;
+ }
+}
+
+static int get_etdm_cowork_master_id(struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ int dai_id;
+
+ if (!is_valid_etdm_dai(dai->id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai->id];
+ dai_id = etdm_data->cowork_source_id;
+
+ if (dai_id == COWORK_ETDM_NONE)
+ dai_id = dai->id;
+
+ return dai_id;
+}
+
+static int mtk_dai_etdm_get_cg_id_by_dai_id(int dai_id)
+{
+ switch (dai_id) {
+ case MT8188_AFE_IO_DPTX:
+ return MT8188_CLK_AUD_HDMI_OUT;
+ case MT8188_AFE_IO_ETDM1_IN:
+ return MT8188_CLK_AUD_TDM_IN;
+ case MT8188_AFE_IO_ETDM2_IN:
+ return MT8188_CLK_AUD_I2SIN;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ return MT8188_CLK_AUD_TDM_OUT;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ return MT8188_CLK_AUD_I2S_OUT;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ return MT8188_CLK_AUD_HDMI_OUT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mtk_dai_etdm_get_clk_id_by_dai_id(int dai_id)
+{
+ switch (dai_id) {
+ case MT8188_AFE_IO_DPTX:
+ return MT8188_CLK_TOP_DPTX_M_SEL;
+ case MT8188_AFE_IO_ETDM1_IN:
+ return MT8188_CLK_TOP_I2SI1_M_SEL;
+ case MT8188_AFE_IO_ETDM2_IN:
+ return MT8188_CLK_TOP_I2SI2_M_SEL;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ return MT8188_CLK_TOP_I2SO1_M_SEL;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ return MT8188_CLK_TOP_I2SO2_M_SEL;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mtk_dai_etdm_get_clkdiv_id_by_dai_id(int dai_id)
+{
+ switch (dai_id) {
+ case MT8188_AFE_IO_DPTX:
+ return MT8188_CLK_TOP_APLL12_DIV9;
+ case MT8188_AFE_IO_ETDM1_IN:
+ return MT8188_CLK_TOP_APLL12_DIV0;
+ case MT8188_AFE_IO_ETDM2_IN:
+ return MT8188_CLK_TOP_APLL12_DIV1;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ return MT8188_CLK_TOP_APLL12_DIV2;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ return MT8188_CLK_TOP_APLL12_DIV3;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mtk_dai_etdm_enable_mclk(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id);
+
+ if (clkdiv_id < 0)
+ return -EINVAL;
+
+ mt8188_afe_enable_clk(afe, afe_priv->clk[clkdiv_id]);
+
+ return 0;
+}
+
+static int mtk_dai_etdm_disable_mclk(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id);
+
+ if (clkdiv_id < 0)
+ return -EINVAL;
+
+ mt8188_afe_disable_clk(afe, afe_priv->clk[clkdiv_id]);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o048_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN48, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN48, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN48_1, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN48_2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o049_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN49, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN49, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN49_1, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN49_2, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o050_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN50, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN50_1, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o051_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN51, 25, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN51_1, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o052_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN52, 26, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN52_1, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o053_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN53, 27, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN53_1, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o054_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN54, 28, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN54_1, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o055_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN55, 29, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN55_1, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o056_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I030 Switch", AFE_CONN56, 30, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I054 Switch", AFE_CONN56_1, 22, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o057_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I031 Switch", AFE_CONN57, 31, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I055 Switch", AFE_CONN57_1, 23, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o058_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I032 Switch", AFE_CONN58_1, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I056 Switch", AFE_CONN58_1, 24, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o059_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I033 Switch", AFE_CONN59_1, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I057 Switch", AFE_CONN59_1, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o060_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I034 Switch", AFE_CONN60_1, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I058 Switch", AFE_CONN60_1, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o061_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I035 Switch", AFE_CONN61_1, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I059 Switch", AFE_CONN61_1, 27, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o062_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I036 Switch", AFE_CONN62_1, 4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I060 Switch", AFE_CONN62_1, 28, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o063_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I037 Switch", AFE_CONN63_1, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I061 Switch", AFE_CONN63_1, 29, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o072_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I020 Switch", AFE_CONN72, 20, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I022 Switch", AFE_CONN72, 22, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I046 Switch", AFE_CONN72_1, 14, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN72_2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o073_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I021 Switch", AFE_CONN73, 21, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I023 Switch", AFE_CONN73, 23, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I047 Switch", AFE_CONN73_1, 15, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN73_2, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o074_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I024 Switch", AFE_CONN74, 24, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I048 Switch", AFE_CONN74_1, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o075_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I025 Switch", AFE_CONN75, 25, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I049 Switch", AFE_CONN75_1, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o076_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I026 Switch", AFE_CONN76, 26, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I050 Switch", AFE_CONN76_1, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o077_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I027 Switch", AFE_CONN77, 27, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I051 Switch", AFE_CONN77_1, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o078_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I028 Switch", AFE_CONN78, 28, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I052 Switch", AFE_CONN78_1, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o079_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I029 Switch", AFE_CONN79, 29, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I053 Switch", AFE_CONN79_1, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o080_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I030 Switch", AFE_CONN80, 30, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I054 Switch", AFE_CONN80_1, 22, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o081_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I031 Switch", AFE_CONN81, 31, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I055 Switch", AFE_CONN81_1, 23, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o082_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I032 Switch", AFE_CONN82_1, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I056 Switch", AFE_CONN82_1, 24, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o083_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I033 Switch", AFE_CONN83_1, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I057 Switch", AFE_CONN83_1, 25, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o084_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I034 Switch", AFE_CONN84_1, 2, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I058 Switch", AFE_CONN84_1, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o085_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I035 Switch", AFE_CONN85_1, 3, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I059 Switch", AFE_CONN85_1, 27, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o086_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I036 Switch", AFE_CONN86_1, 4, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I060 Switch", AFE_CONN86_1, 28, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_etdm_o087_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I037 Switch", AFE_CONN87_1, 5, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I061 Switch", AFE_CONN87_1, 29, 1, 0),
+};
+
+static const char * const mt8188_etdm_clk_src_sel_text[] = {
+ "26m",
+ "a1sys_a2sys",
+ "a3sys",
+ "a4sys",
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(etdmout_clk_src_enum,
+ mt8188_etdm_clk_src_sel_text);
+
+static const char * const hdmitx_dptx_mux_map[] = {
+ "Disconnect", "Connect",
+};
+
+static int hdmitx_dptx_mux_map_value[] = {
+ 0, 1,
+};
+
+/* HDMI_OUT_MUX */
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(hdmi_out_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ 1,
+ hdmitx_dptx_mux_map,
+ hdmitx_dptx_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_out_mux_control =
+ SOC_DAPM_ENUM("HDMI_OUT_MUX", hdmi_out_mux_map_enum);
+
+/* DPTX_OUT_MUX */
+static SOC_VALUE_ENUM_SINGLE_AUTODISABLE_DECL(dptx_out_mux_map_enum,
+ SND_SOC_NOPM,
+ 0,
+ 1,
+ hdmitx_dptx_mux_map,
+ hdmitx_dptx_mux_map_value);
+
+static const struct snd_kcontrol_new dptx_out_mux_control =
+ SOC_DAPM_ENUM("DPTX_OUT_MUX", dptx_out_mux_map_enum);
+
+/* HDMI_CH0_MUX ~ HDMI_CH7_MUX */
+static const char *const afe_conn_hdmi_mux_map[] = {
+ "CH0", "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7",
+};
+
+static int afe_conn_hdmi_mux_map_value[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch0_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 0,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch0_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH0_MUX", hdmi_ch0_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch1_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 4,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch1_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH1_MUX", hdmi_ch1_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch2_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 8,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch2_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH2_MUX", hdmi_ch2_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch3_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 12,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch3_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH3_MUX", hdmi_ch3_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch4_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 16,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch4_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH4_MUX", hdmi_ch4_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch5_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 20,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch5_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH5_MUX", hdmi_ch5_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch6_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 24,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch6_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH6_MUX", hdmi_ch6_mux_map_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(hdmi_ch7_mux_map_enum,
+ AFE_TDMOUT_CONN0,
+ 28,
+ 0xf,
+ afe_conn_hdmi_mux_map,
+ afe_conn_hdmi_mux_map_value);
+
+static const struct snd_kcontrol_new hdmi_ch7_mux_control =
+ SOC_DAPM_ENUM("HDMI_CH7_MUX", hdmi_ch7_mux_map_enum);
+
+static int mt8188_etdm_clk_src_sel_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ unsigned int source = ucontrol->value.enumerated.item[0];
+ unsigned int val;
+ unsigned int old_val;
+ unsigned int mask;
+ unsigned int reg;
+ unsigned int shift;
+
+ if (source >= e->items)
+ return -EINVAL;
+
+ if (!strcmp(kcontrol->id.name, "ETDM_OUT1_Clock_Source")) {
+ reg = ETDM_OUT1_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ shift = ETDM_OUT_CON4_CLOCK_SHIFT;
+ val = FIELD_PREP(ETDM_OUT_CON4_CLOCK_MASK, source);
+ } else if (!strcmp(kcontrol->id.name, "ETDM_OUT2_Clock_Source")) {
+ reg = ETDM_OUT2_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ shift = ETDM_OUT_CON4_CLOCK_SHIFT;
+ val = FIELD_PREP(ETDM_OUT_CON4_CLOCK_MASK, source);
+ } else if (!strcmp(kcontrol->id.name, "ETDM_OUT3_Clock_Source")) {
+ reg = ETDM_OUT3_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ shift = ETDM_OUT_CON4_CLOCK_SHIFT;
+ val = FIELD_PREP(ETDM_OUT_CON4_CLOCK_MASK, source);
+ } else if (!strcmp(kcontrol->id.name, "ETDM_IN1_Clock_Source")) {
+ reg = ETDM_IN1_CON2;
+ mask = ETDM_IN_CON2_CLOCK_MASK;
+ shift = ETDM_IN_CON2_CLOCK_SHIFT;
+ val = FIELD_PREP(ETDM_IN_CON2_CLOCK_MASK, source);
+ } else if (!strcmp(kcontrol->id.name, "ETDM_IN2_Clock_Source")) {
+ reg = ETDM_IN2_CON2;
+ mask = ETDM_IN_CON2_CLOCK_MASK;
+ shift = ETDM_IN_CON2_CLOCK_SHIFT;
+ val = FIELD_PREP(ETDM_IN_CON2_CLOCK_MASK, source);
+ } else {
+ return -EINVAL;
+ }
+
+ regmap_read(afe->regmap, reg, &old_val);
+ old_val &= mask;
+ old_val >>= shift;
+
+ if (old_val == val)
+ return 0;
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+
+ return 1;
+}
+
+static int mt8188_etdm_clk_src_sel_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+ unsigned int value;
+ unsigned int reg;
+ unsigned int mask;
+ unsigned int shift;
+
+ if (!strcmp(kcontrol->id.name, "ETDM_OUT1_Clock_Source")) {
+ reg = ETDM_OUT1_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ shift = ETDM_OUT_CON4_CLOCK_SHIFT;
+ } else if (!strcmp(kcontrol->id.name, "ETDM_OUT2_Clock_Source")) {
+ reg = ETDM_OUT2_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ shift = ETDM_OUT_CON4_CLOCK_SHIFT;
+ } else if (!strcmp(kcontrol->id.name, "ETDM_OUT3_Clock_Source")) {
+ reg = ETDM_OUT3_CON4;
+ mask = ETDM_OUT_CON4_CLOCK_MASK;
+ shift = ETDM_OUT_CON4_CLOCK_SHIFT;
+ } else if (!strcmp(kcontrol->id.name, "ETDM_IN1_Clock_Source")) {
+ reg = ETDM_IN1_CON2;
+ mask = ETDM_IN_CON2_CLOCK_MASK;
+ shift = ETDM_IN_CON2_CLOCK_SHIFT;
+ } else if (!strcmp(kcontrol->id.name, "ETDM_IN2_Clock_Source")) {
+ reg = ETDM_IN2_CON2;
+ mask = ETDM_IN_CON2_CLOCK_MASK;
+ shift = ETDM_IN_CON2_CLOCK_SHIFT;
+ } else {
+ return -EINVAL;
+ }
+
+ regmap_read(afe->regmap, reg, &value);
+
+ value &= mask;
+ value >>= shift;
+ ucontrol->value.enumerated.item[0] = value;
+ return 0;
+}
+
+static const struct snd_kcontrol_new mtk_dai_etdm_controls[] = {
+ SOC_ENUM_EXT("ETDM_OUT1_Clock_Source", etdmout_clk_src_enum,
+ mt8188_etdm_clk_src_sel_get,
+ mt8188_etdm_clk_src_sel_put),
+ SOC_ENUM_EXT("ETDM_OUT2_Clock_Source", etdmout_clk_src_enum,
+ mt8188_etdm_clk_src_sel_get,
+ mt8188_etdm_clk_src_sel_put),
+ SOC_ENUM_EXT("ETDM_OUT3_Clock_Source", etdmout_clk_src_enum,
+ mt8188_etdm_clk_src_sel_get,
+ mt8188_etdm_clk_src_sel_put),
+ SOC_ENUM_EXT("ETDM_IN1_Clock_Source", etdmout_clk_src_enum,
+ mt8188_etdm_clk_src_sel_get,
+ mt8188_etdm_clk_src_sel_put),
+ SOC_ENUM_EXT("ETDM_IN2_Clock_Source", etdmout_clk_src_enum,
+ mt8188_etdm_clk_src_sel_get,
+ mt8188_etdm_clk_src_sel_put),
+};
+
+static const struct snd_soc_dapm_widget mtk_dai_etdm_widgets[] = {
+ /* eTDM_IN2 */
+ SND_SOC_DAPM_MIXER("I012", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I013", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I014", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I015", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I016", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I017", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I018", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I019", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I188", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I189", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I190", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I191", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I192", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I193", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I194", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I195", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* eTDM_IN1 */
+ SND_SOC_DAPM_MIXER("I072", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I073", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I074", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I075", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I076", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I077", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I078", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I079", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I080", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I081", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I082", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I083", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I084", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I085", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I086", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I087", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* eTDM_OUT2 */
+ SND_SOC_DAPM_MIXER("O048", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o048_mix, ARRAY_SIZE(mtk_dai_etdm_o048_mix)),
+ SND_SOC_DAPM_MIXER("O049", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o049_mix, ARRAY_SIZE(mtk_dai_etdm_o049_mix)),
+ SND_SOC_DAPM_MIXER("O050", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o050_mix, ARRAY_SIZE(mtk_dai_etdm_o050_mix)),
+ SND_SOC_DAPM_MIXER("O051", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o051_mix, ARRAY_SIZE(mtk_dai_etdm_o051_mix)),
+ SND_SOC_DAPM_MIXER("O052", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o052_mix, ARRAY_SIZE(mtk_dai_etdm_o052_mix)),
+ SND_SOC_DAPM_MIXER("O053", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o053_mix, ARRAY_SIZE(mtk_dai_etdm_o053_mix)),
+ SND_SOC_DAPM_MIXER("O054", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o054_mix, ARRAY_SIZE(mtk_dai_etdm_o054_mix)),
+ SND_SOC_DAPM_MIXER("O055", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o055_mix, ARRAY_SIZE(mtk_dai_etdm_o055_mix)),
+ SND_SOC_DAPM_MIXER("O056", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o056_mix, ARRAY_SIZE(mtk_dai_etdm_o056_mix)),
+ SND_SOC_DAPM_MIXER("O057", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o057_mix, ARRAY_SIZE(mtk_dai_etdm_o057_mix)),
+ SND_SOC_DAPM_MIXER("O058", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o058_mix, ARRAY_SIZE(mtk_dai_etdm_o058_mix)),
+ SND_SOC_DAPM_MIXER("O059", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o059_mix, ARRAY_SIZE(mtk_dai_etdm_o059_mix)),
+ SND_SOC_DAPM_MIXER("O060", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o060_mix, ARRAY_SIZE(mtk_dai_etdm_o060_mix)),
+ SND_SOC_DAPM_MIXER("O061", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o061_mix, ARRAY_SIZE(mtk_dai_etdm_o061_mix)),
+ SND_SOC_DAPM_MIXER("O062", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o062_mix, ARRAY_SIZE(mtk_dai_etdm_o062_mix)),
+ SND_SOC_DAPM_MIXER("O063", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o063_mix, ARRAY_SIZE(mtk_dai_etdm_o063_mix)),
+
+ /* eTDM_OUT1 */
+ SND_SOC_DAPM_MIXER("O072", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o072_mix, ARRAY_SIZE(mtk_dai_etdm_o072_mix)),
+ SND_SOC_DAPM_MIXER("O073", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o073_mix, ARRAY_SIZE(mtk_dai_etdm_o073_mix)),
+ SND_SOC_DAPM_MIXER("O074", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o074_mix, ARRAY_SIZE(mtk_dai_etdm_o074_mix)),
+ SND_SOC_DAPM_MIXER("O075", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o075_mix, ARRAY_SIZE(mtk_dai_etdm_o075_mix)),
+ SND_SOC_DAPM_MIXER("O076", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o076_mix, ARRAY_SIZE(mtk_dai_etdm_o076_mix)),
+ SND_SOC_DAPM_MIXER("O077", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o077_mix, ARRAY_SIZE(mtk_dai_etdm_o077_mix)),
+ SND_SOC_DAPM_MIXER("O078", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o078_mix, ARRAY_SIZE(mtk_dai_etdm_o078_mix)),
+ SND_SOC_DAPM_MIXER("O079", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o079_mix, ARRAY_SIZE(mtk_dai_etdm_o079_mix)),
+ SND_SOC_DAPM_MIXER("O080", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o080_mix, ARRAY_SIZE(mtk_dai_etdm_o080_mix)),
+ SND_SOC_DAPM_MIXER("O081", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o081_mix, ARRAY_SIZE(mtk_dai_etdm_o081_mix)),
+ SND_SOC_DAPM_MIXER("O082", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o082_mix, ARRAY_SIZE(mtk_dai_etdm_o082_mix)),
+ SND_SOC_DAPM_MIXER("O083", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o083_mix, ARRAY_SIZE(mtk_dai_etdm_o083_mix)),
+ SND_SOC_DAPM_MIXER("O084", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o084_mix, ARRAY_SIZE(mtk_dai_etdm_o084_mix)),
+ SND_SOC_DAPM_MIXER("O085", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o085_mix, ARRAY_SIZE(mtk_dai_etdm_o085_mix)),
+ SND_SOC_DAPM_MIXER("O086", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o086_mix, ARRAY_SIZE(mtk_dai_etdm_o086_mix)),
+ SND_SOC_DAPM_MIXER("O087", SND_SOC_NOPM, 0, 0,
+ mtk_dai_etdm_o087_mix, ARRAY_SIZE(mtk_dai_etdm_o087_mix)),
+
+ /* eTDM_OUT3 */
+ SND_SOC_DAPM_MUX("HDMI_OUT_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_out_mux_control),
+ SND_SOC_DAPM_MUX("DPTX_OUT_MUX", SND_SOC_NOPM, 0, 0,
+ &dptx_out_mux_control),
+
+ SND_SOC_DAPM_MUX("HDMI_CH0_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch0_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH1_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch1_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH2_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch2_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH3_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch3_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH4_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch4_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH5_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch5_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH6_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch6_mux_control),
+ SND_SOC_DAPM_MUX("HDMI_CH7_MUX", SND_SOC_NOPM, 0, 0,
+ &hdmi_ch7_mux_control),
+
+ SND_SOC_DAPM_INPUT("ETDM_INPUT"),
+ SND_SOC_DAPM_OUTPUT("ETDM_OUTPUT"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = {
+ {"I012", NULL, "ETDM2_IN"},
+ {"I013", NULL, "ETDM2_IN"},
+ {"I014", NULL, "ETDM2_IN"},
+ {"I015", NULL, "ETDM2_IN"},
+ {"I016", NULL, "ETDM2_IN"},
+ {"I017", NULL, "ETDM2_IN"},
+ {"I018", NULL, "ETDM2_IN"},
+ {"I019", NULL, "ETDM2_IN"},
+ {"I188", NULL, "ETDM2_IN"},
+ {"I189", NULL, "ETDM2_IN"},
+ {"I190", NULL, "ETDM2_IN"},
+ {"I191", NULL, "ETDM2_IN"},
+ {"I192", NULL, "ETDM2_IN"},
+ {"I193", NULL, "ETDM2_IN"},
+ {"I194", NULL, "ETDM2_IN"},
+ {"I195", NULL, "ETDM2_IN"},
+
+ {"I072", NULL, "ETDM1_IN"},
+ {"I073", NULL, "ETDM1_IN"},
+ {"I074", NULL, "ETDM1_IN"},
+ {"I075", NULL, "ETDM1_IN"},
+ {"I076", NULL, "ETDM1_IN"},
+ {"I077", NULL, "ETDM1_IN"},
+ {"I078", NULL, "ETDM1_IN"},
+ {"I079", NULL, "ETDM1_IN"},
+ {"I080", NULL, "ETDM1_IN"},
+ {"I081", NULL, "ETDM1_IN"},
+ {"I082", NULL, "ETDM1_IN"},
+ {"I083", NULL, "ETDM1_IN"},
+ {"I084", NULL, "ETDM1_IN"},
+ {"I085", NULL, "ETDM1_IN"},
+ {"I086", NULL, "ETDM1_IN"},
+ {"I087", NULL, "ETDM1_IN"},
+
+ {"UL8", NULL, "ETDM1_IN"},
+ {"UL3", NULL, "ETDM2_IN"},
+
+ {"ETDM2_OUT", NULL, "O048"},
+ {"ETDM2_OUT", NULL, "O049"},
+ {"ETDM2_OUT", NULL, "O050"},
+ {"ETDM2_OUT", NULL, "O051"},
+ {"ETDM2_OUT", NULL, "O052"},
+ {"ETDM2_OUT", NULL, "O053"},
+ {"ETDM2_OUT", NULL, "O054"},
+ {"ETDM2_OUT", NULL, "O055"},
+ {"ETDM2_OUT", NULL, "O056"},
+ {"ETDM2_OUT", NULL, "O057"},
+ {"ETDM2_OUT", NULL, "O058"},
+ {"ETDM2_OUT", NULL, "O059"},
+ {"ETDM2_OUT", NULL, "O060"},
+ {"ETDM2_OUT", NULL, "O061"},
+ {"ETDM2_OUT", NULL, "O062"},
+ {"ETDM2_OUT", NULL, "O063"},
+
+ {"ETDM1_OUT", NULL, "O072"},
+ {"ETDM1_OUT", NULL, "O073"},
+ {"ETDM1_OUT", NULL, "O074"},
+ {"ETDM1_OUT", NULL, "O075"},
+ {"ETDM1_OUT", NULL, "O076"},
+ {"ETDM1_OUT", NULL, "O077"},
+ {"ETDM1_OUT", NULL, "O078"},
+ {"ETDM1_OUT", NULL, "O079"},
+ {"ETDM1_OUT", NULL, "O080"},
+ {"ETDM1_OUT", NULL, "O081"},
+ {"ETDM1_OUT", NULL, "O082"},
+ {"ETDM1_OUT", NULL, "O083"},
+ {"ETDM1_OUT", NULL, "O084"},
+ {"ETDM1_OUT", NULL, "O085"},
+ {"ETDM1_OUT", NULL, "O086"},
+ {"ETDM1_OUT", NULL, "O087"},
+
+ {"O048", "I020 Switch", "I020"},
+ {"O049", "I021 Switch", "I021"},
+
+ {"O048", "I022 Switch", "I022"},
+ {"O049", "I023 Switch", "I023"},
+ {"O050", "I024 Switch", "I024"},
+ {"O051", "I025 Switch", "I025"},
+ {"O052", "I026 Switch", "I026"},
+ {"O053", "I027 Switch", "I027"},
+ {"O054", "I028 Switch", "I028"},
+ {"O055", "I029 Switch", "I029"},
+ {"O056", "I030 Switch", "I030"},
+ {"O057", "I031 Switch", "I031"},
+ {"O058", "I032 Switch", "I032"},
+ {"O059", "I033 Switch", "I033"},
+ {"O060", "I034 Switch", "I034"},
+ {"O061", "I035 Switch", "I035"},
+ {"O062", "I036 Switch", "I036"},
+ {"O063", "I037 Switch", "I037"},
+
+ {"O048", "I046 Switch", "I046"},
+ {"O049", "I047 Switch", "I047"},
+ {"O050", "I048 Switch", "I048"},
+ {"O051", "I049 Switch", "I049"},
+ {"O052", "I050 Switch", "I050"},
+ {"O053", "I051 Switch", "I051"},
+ {"O054", "I052 Switch", "I052"},
+ {"O055", "I053 Switch", "I053"},
+ {"O056", "I054 Switch", "I054"},
+ {"O057", "I055 Switch", "I055"},
+ {"O058", "I056 Switch", "I056"},
+ {"O059", "I057 Switch", "I057"},
+ {"O060", "I058 Switch", "I058"},
+ {"O061", "I059 Switch", "I059"},
+ {"O062", "I060 Switch", "I060"},
+ {"O063", "I061 Switch", "I061"},
+
+ {"O048", "I070 Switch", "I070"},
+ {"O049", "I071 Switch", "I071"},
+
+ {"O072", "I020 Switch", "I020"},
+ {"O073", "I021 Switch", "I021"},
+
+ {"O072", "I022 Switch", "I022"},
+ {"O073", "I023 Switch", "I023"},
+ {"O074", "I024 Switch", "I024"},
+ {"O075", "I025 Switch", "I025"},
+ {"O076", "I026 Switch", "I026"},
+ {"O077", "I027 Switch", "I027"},
+ {"O078", "I028 Switch", "I028"},
+ {"O079", "I029 Switch", "I029"},
+ {"O080", "I030 Switch", "I030"},
+ {"O081", "I031 Switch", "I031"},
+ {"O082", "I032 Switch", "I032"},
+ {"O083", "I033 Switch", "I033"},
+ {"O084", "I034 Switch", "I034"},
+ {"O085", "I035 Switch", "I035"},
+ {"O086", "I036 Switch", "I036"},
+ {"O087", "I037 Switch", "I037"},
+
+ {"O072", "I046 Switch", "I046"},
+ {"O073", "I047 Switch", "I047"},
+ {"O074", "I048 Switch", "I048"},
+ {"O075", "I049 Switch", "I049"},
+ {"O076", "I050 Switch", "I050"},
+ {"O077", "I051 Switch", "I051"},
+ {"O078", "I052 Switch", "I052"},
+ {"O079", "I053 Switch", "I053"},
+ {"O080", "I054 Switch", "I054"},
+ {"O081", "I055 Switch", "I055"},
+ {"O082", "I056 Switch", "I056"},
+ {"O083", "I057 Switch", "I057"},
+ {"O084", "I058 Switch", "I058"},
+ {"O085", "I059 Switch", "I059"},
+ {"O086", "I060 Switch", "I060"},
+ {"O087", "I061 Switch", "I061"},
+
+ {"O072", "I070 Switch", "I070"},
+ {"O073", "I071 Switch", "I071"},
+
+ {"HDMI_CH0_MUX", "CH0", "DL10"},
+ {"HDMI_CH0_MUX", "CH1", "DL10"},
+ {"HDMI_CH0_MUX", "CH2", "DL10"},
+ {"HDMI_CH0_MUX", "CH3", "DL10"},
+ {"HDMI_CH0_MUX", "CH4", "DL10"},
+ {"HDMI_CH0_MUX", "CH5", "DL10"},
+ {"HDMI_CH0_MUX", "CH6", "DL10"},
+ {"HDMI_CH0_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH1_MUX", "CH0", "DL10"},
+ {"HDMI_CH1_MUX", "CH1", "DL10"},
+ {"HDMI_CH1_MUX", "CH2", "DL10"},
+ {"HDMI_CH1_MUX", "CH3", "DL10"},
+ {"HDMI_CH1_MUX", "CH4", "DL10"},
+ {"HDMI_CH1_MUX", "CH5", "DL10"},
+ {"HDMI_CH1_MUX", "CH6", "DL10"},
+ {"HDMI_CH1_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH2_MUX", "CH0", "DL10"},
+ {"HDMI_CH2_MUX", "CH1", "DL10"},
+ {"HDMI_CH2_MUX", "CH2", "DL10"},
+ {"HDMI_CH2_MUX", "CH3", "DL10"},
+ {"HDMI_CH2_MUX", "CH4", "DL10"},
+ {"HDMI_CH2_MUX", "CH5", "DL10"},
+ {"HDMI_CH2_MUX", "CH6", "DL10"},
+ {"HDMI_CH2_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH3_MUX", "CH0", "DL10"},
+ {"HDMI_CH3_MUX", "CH1", "DL10"},
+ {"HDMI_CH3_MUX", "CH2", "DL10"},
+ {"HDMI_CH3_MUX", "CH3", "DL10"},
+ {"HDMI_CH3_MUX", "CH4", "DL10"},
+ {"HDMI_CH3_MUX", "CH5", "DL10"},
+ {"HDMI_CH3_MUX", "CH6", "DL10"},
+ {"HDMI_CH3_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH4_MUX", "CH0", "DL10"},
+ {"HDMI_CH4_MUX", "CH1", "DL10"},
+ {"HDMI_CH4_MUX", "CH2", "DL10"},
+ {"HDMI_CH4_MUX", "CH3", "DL10"},
+ {"HDMI_CH4_MUX", "CH4", "DL10"},
+ {"HDMI_CH4_MUX", "CH5", "DL10"},
+ {"HDMI_CH4_MUX", "CH6", "DL10"},
+ {"HDMI_CH4_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH5_MUX", "CH0", "DL10"},
+ {"HDMI_CH5_MUX", "CH1", "DL10"},
+ {"HDMI_CH5_MUX", "CH2", "DL10"},
+ {"HDMI_CH5_MUX", "CH3", "DL10"},
+ {"HDMI_CH5_MUX", "CH4", "DL10"},
+ {"HDMI_CH5_MUX", "CH5", "DL10"},
+ {"HDMI_CH5_MUX", "CH6", "DL10"},
+ {"HDMI_CH5_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH6_MUX", "CH0", "DL10"},
+ {"HDMI_CH6_MUX", "CH1", "DL10"},
+ {"HDMI_CH6_MUX", "CH2", "DL10"},
+ {"HDMI_CH6_MUX", "CH3", "DL10"},
+ {"HDMI_CH6_MUX", "CH4", "DL10"},
+ {"HDMI_CH6_MUX", "CH5", "DL10"},
+ {"HDMI_CH6_MUX", "CH6", "DL10"},
+ {"HDMI_CH6_MUX", "CH7", "DL10"},
+
+ {"HDMI_CH7_MUX", "CH0", "DL10"},
+ {"HDMI_CH7_MUX", "CH1", "DL10"},
+ {"HDMI_CH7_MUX", "CH2", "DL10"},
+ {"HDMI_CH7_MUX", "CH3", "DL10"},
+ {"HDMI_CH7_MUX", "CH4", "DL10"},
+ {"HDMI_CH7_MUX", "CH5", "DL10"},
+ {"HDMI_CH7_MUX", "CH6", "DL10"},
+ {"HDMI_CH7_MUX", "CH7", "DL10"},
+
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH0_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH1_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH2_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH3_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH4_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH5_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH6_MUX"},
+ {"HDMI_OUT_MUX", "Connect", "HDMI_CH7_MUX"},
+
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH0_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH1_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH2_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH3_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH4_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH5_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH6_MUX"},
+ {"DPTX_OUT_MUX", "Connect", "HDMI_CH7_MUX"},
+
+ {"ETDM3_OUT", NULL, "HDMI_OUT_MUX"},
+ {"DPTX", NULL, "DPTX_OUT_MUX"},
+
+ {"ETDM_OUTPUT", NULL, "DPTX"},
+ {"ETDM_OUTPUT", NULL, "ETDM1_OUT"},
+ {"ETDM_OUTPUT", NULL, "ETDM2_OUT"},
+ {"ETDM_OUTPUT", NULL, "ETDM3_OUT"},
+ {"ETDM1_IN", NULL, "ETDM_INPUT"},
+ {"ETDM2_IN", NULL, "ETDM_INPUT"},
+};
+
+static int mt8188_afe_enable_etdm(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ dev_dbg(afe->dev, "%s [%d]%d\n", __func__, dai_id, etdm_data->en_ref_cnt);
+ spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
+ etdm_data->en_ref_cnt++;
+ if (etdm_data->en_ref_cnt == 1) {
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ goto out;
+
+ regmap_set_bits(afe->regmap, etdm_reg.con0, ETDM_CON0_EN);
+ }
+
+out:
+ spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
+ return ret;
+}
+
+static int mt8188_afe_disable_etdm(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ dev_dbg(afe->dev, "%s [%d]%d\n", __func__, dai_id, etdm_data->en_ref_cnt);
+ spin_lock_irqsave(&afe_priv->afe_ctrl_lock, flags);
+ if (etdm_data->en_ref_cnt > 0) {
+ etdm_data->en_ref_cnt--;
+ if (etdm_data->en_ref_cnt == 0) {
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ goto out;
+ regmap_clear_bits(afe->regmap, etdm_reg.con0,
+ ETDM_CON0_EN);
+ }
+ }
+
+out:
+ spin_unlock_irqrestore(&afe_priv->afe_ctrl_lock, flags);
+ return ret;
+}
+
+static int etdm_cowork_slv_sel(int id, int slave_mode)
+{
+ if (slave_mode) {
+ switch (id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ return COWORK_ETDM_IN1_S;
+ case MT8188_AFE_IO_ETDM2_IN:
+ return COWORK_ETDM_IN2_S;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ return COWORK_ETDM_OUT1_S;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ return COWORK_ETDM_OUT2_S;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ return COWORK_ETDM_OUT3_S;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ switch (id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ return COWORK_ETDM_IN1_M;
+ case MT8188_AFE_IO_ETDM2_IN:
+ return COWORK_ETDM_IN2_M;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ return COWORK_ETDM_OUT1_M;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ return COWORK_ETDM_OUT2_M;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ return COWORK_ETDM_OUT3_M;
+ default:
+ return -EINVAL;
+ }
+ }
+}
+
+static int etdm_cowork_sync_sel(int id)
+{
+ switch (id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ return ETDM_SYNC_FROM_IN1;
+ case MT8188_AFE_IO_ETDM2_IN:
+ return ETDM_SYNC_FROM_IN2;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ return ETDM_SYNC_FROM_OUT1;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ return ETDM_SYNC_FROM_OUT2;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ return ETDM_SYNC_FROM_OUT3;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mt8188_etdm_sync_mode_slv(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ unsigned int reg = 0;
+ unsigned int mask;
+ unsigned int val;
+ int cowork_source_sel;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ cowork_source_sel = etdm_cowork_slv_sel(etdm_data->cowork_source_id,
+ true);
+ if (cowork_source_sel < 0)
+ return cowork_source_sel;
+
+ switch (dai_id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ reg = ETDM_COWORK_CON1;
+ mask = ETDM_IN1_SLAVE_SEL_MASK;
+ val = FIELD_PREP(ETDM_IN1_SLAVE_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM2_IN:
+ reg = ETDM_COWORK_CON2;
+ mask = ETDM_IN2_SLAVE_SEL_MASK;
+ val = FIELD_PREP(ETDM_IN2_SLAVE_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ reg = ETDM_COWORK_CON0;
+ mask = ETDM_OUT1_SLAVE_SEL_MASK;
+ val = FIELD_PREP(ETDM_OUT1_SLAVE_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ reg = ETDM_COWORK_CON2;
+ mask = ETDM_OUT2_SLAVE_SEL_MASK;
+ val = FIELD_PREP(ETDM_OUT2_SLAVE_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ reg = ETDM_COWORK_CON2;
+ mask = ETDM_OUT3_SLAVE_SEL_MASK;
+ val = FIELD_PREP(ETDM_OUT3_SLAVE_SEL_MASK, cowork_source_sel);
+ break;
+ default:
+ return 0;
+ }
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+
+ return 0;
+}
+
+static int mt8188_etdm_sync_mode_mst(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ unsigned int reg = 0;
+ unsigned int mask;
+ unsigned int val;
+ int cowork_source_sel;
+ int ret;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ cowork_source_sel = etdm_cowork_sync_sel(etdm_data->cowork_source_id);
+ if (cowork_source_sel < 0)
+ return cowork_source_sel;
+
+ switch (dai_id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ reg = ETDM_COWORK_CON1;
+ mask = ETDM_IN1_SYNC_SEL_MASK;
+ val = FIELD_PREP(ETDM_IN1_SYNC_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM2_IN:
+ reg = ETDM_COWORK_CON2;
+ mask = ETDM_IN2_SYNC_SEL_MASK;
+ val = FIELD_PREP(ETDM_IN2_SYNC_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM1_OUT:
+ reg = ETDM_COWORK_CON0;
+ mask = ETDM_OUT1_SYNC_SEL_MASK;
+ val = FIELD_PREP(ETDM_OUT1_SYNC_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM2_OUT:
+ reg = ETDM_COWORK_CON2;
+ mask = ETDM_OUT2_SYNC_SEL_MASK;
+ val = FIELD_PREP(ETDM_OUT2_SYNC_SEL_MASK, cowork_source_sel);
+ break;
+ case MT8188_AFE_IO_ETDM3_OUT:
+ reg = ETDM_COWORK_CON2;
+ mask = ETDM_OUT3_SYNC_SEL_MASK;
+ val = FIELD_PREP(ETDM_OUT3_SYNC_SEL_MASK, cowork_source_sel);
+ break;
+ default:
+ return 0;
+ }
+
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ return ret;
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+
+ regmap_set_bits(afe->regmap, etdm_reg.con0, ETDM_CON0_SYNC_MODE);
+
+ return 0;
+}
+
+static int mt8188_etdm_sync_mode_configure(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ if (etdm_data->cowork_source_id == COWORK_ETDM_NONE)
+ return 0;
+
+ if (etdm_data->slave_mode)
+ mt8188_etdm_sync_mode_slv(afe, dai_id);
+ else
+ mt8188_etdm_sync_mode_mst(afe, dai_id);
+
+ return 0;
+}
+
+/* dai ops */
+static int mtk_dai_etdm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *mst_etdm_data;
+ int mst_dai_id;
+ int slv_dai_id;
+ int cg_id;
+ int i;
+
+ if (is_cowork_mode(dai)) {
+ mst_dai_id = get_etdm_cowork_master_id(dai);
+ if (!is_valid_etdm_dai(mst_dai_id))
+ return -EINVAL;
+ mtk_dai_etdm_enable_mclk(afe, mst_dai_id);
+
+ cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(mst_dai_id);
+ if (cg_id >= 0)
+ mt8188_afe_enable_clk(afe, afe_priv->clk[cg_id]);
+
+ mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
+
+ for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
+ slv_dai_id = mst_etdm_data->cowork_slv_id[i];
+ cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(slv_dai_id);
+ if (cg_id >= 0)
+ mt8188_afe_enable_clk(afe,
+ afe_priv->clk[cg_id]);
+ }
+ } else {
+ mtk_dai_etdm_enable_mclk(afe, dai->id);
+
+ cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id);
+ if (cg_id >= 0)
+ mt8188_afe_enable_clk(afe, afe_priv->clk[cg_id]);
+ }
+
+ return 0;
+}
+
+static void mtk_dai_etdm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *mst_etdm_data;
+ int mst_dai_id;
+ int slv_dai_id;
+ int cg_id;
+ int ret;
+ int i;
+
+ if (!is_valid_etdm_dai(dai->id))
+ return;
+ mst_etdm_data = afe_priv->dai_priv[dai->id];
+
+ dev_dbg(afe->dev, "%s(), dai id %d, prepared %d\n", __func__, dai->id,
+ mst_etdm_data->is_prepared);
+
+ if (mst_etdm_data->is_prepared) {
+ mst_etdm_data->is_prepared = false;
+
+ if (is_cowork_mode(dai)) {
+ mst_dai_id = get_etdm_cowork_master_id(dai);
+ if (!is_valid_etdm_dai(mst_dai_id))
+ return;
+ mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
+
+ ret = mt8188_afe_disable_etdm(afe, mst_dai_id);
+ if (ret)
+ dev_dbg(afe->dev, "%s disable %d failed\n",
+ __func__, mst_dai_id);
+
+ for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
+ slv_dai_id = mst_etdm_data->cowork_slv_id[i];
+ ret = mt8188_afe_disable_etdm(afe, slv_dai_id);
+ if (ret)
+ dev_dbg(afe->dev, "%s disable %d failed\n",
+ __func__, slv_dai_id);
+ }
+ } else {
+ ret = mt8188_afe_disable_etdm(afe, dai->id);
+ if (ret)
+ dev_dbg(afe->dev, "%s disable %d failed\n",
+ __func__, dai->id);
+ }
+ }
+
+ if (is_cowork_mode(dai)) {
+ mst_dai_id = get_etdm_cowork_master_id(dai);
+ if (!is_valid_etdm_dai(mst_dai_id))
+ return;
+ cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(mst_dai_id);
+ if (cg_id >= 0)
+ mt8188_afe_disable_clk(afe, afe_priv->clk[cg_id]);
+
+ mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
+ for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
+ slv_dai_id = mst_etdm_data->cowork_slv_id[i];
+ cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(slv_dai_id);
+ if (cg_id >= 0)
+ mt8188_afe_disable_clk(afe,
+ afe_priv->clk[cg_id]);
+ }
+ mtk_dai_etdm_disable_mclk(afe, mst_dai_id);
+ } else {
+ cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id);
+ if (cg_id >= 0)
+ mt8188_afe_disable_clk(afe, afe_priv->clk[cg_id]);
+
+ mtk_dai_etdm_disable_mclk(afe, dai->id);
+ }
+}
+
+static int mtk_dai_etdm_fifo_mode(struct mtk_base_afe *afe,
+ int dai_id, unsigned int rate)
+{
+ unsigned int mode = 0;
+ unsigned int reg = 0;
+ unsigned int val = 0;
+ unsigned int mask = (ETDM_IN_AFIFO_MODE_MASK | ETDM_IN_USE_AFIFO);
+
+ if (rate != 0)
+ mode = mt8188_afe_fs_timing(rate);
+
+ switch (dai_id) {
+ case MT8188_AFE_IO_ETDM1_IN:
+ reg = ETDM_IN1_AFIFO_CON;
+ if (rate == 0)
+ mode = MT8188_ETDM_IN1_1X_EN;
+ break;
+ case MT8188_AFE_IO_ETDM2_IN:
+ reg = ETDM_IN2_AFIFO_CON;
+ if (rate == 0)
+ mode = MT8188_ETDM_IN2_1X_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val = (mode | ETDM_IN_USE_AFIFO);
+
+ regmap_update_bits(afe->regmap, reg, mask, val);
+ return 0;
+}
+
+static int mtk_dai_etdm_in_configure(struct mtk_base_afe *afe,
+ unsigned int rate,
+ unsigned int channels,
+ int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ bool slave_mode;
+ unsigned int data_mode;
+ unsigned int lrck_width;
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ int ret;
+ int i;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+ slave_mode = etdm_data->slave_mode;
+ data_mode = etdm_data->data_mode;
+ lrck_width = etdm_data->lrck_width;
+
+ dev_dbg(afe->dev, "%s rate %u channels %u, id %d\n",
+ __func__, rate, channels, dai_id);
+
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ return ret;
+
+ /* afifo */
+ if (slave_mode)
+ mtk_dai_etdm_fifo_mode(afe, dai_id, 0);
+ else
+ mtk_dai_etdm_fifo_mode(afe, dai_id, rate);
+
+ /* con1 */
+ if (lrck_width > 0) {
+ mask |= (ETDM_IN_CON1_LRCK_AUTO_MODE |
+ ETDM_IN_CON1_LRCK_WIDTH_MASK);
+ val |= FIELD_PREP(ETDM_IN_CON1_LRCK_WIDTH_MASK, lrck_width - 1);
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con1, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con2 */
+ if (!slave_mode) {
+ mask |= ETDM_IN_CON2_UPDATE_GAP_MASK;
+ if (rate == 352800 || rate == 384000)
+ val |= FIELD_PREP(ETDM_IN_CON2_UPDATE_GAP_MASK, 4);
+ else
+ val |= FIELD_PREP(ETDM_IN_CON2_UPDATE_GAP_MASK, 3);
+ }
+ mask |= (ETDM_IN_CON2_MULTI_IP_2CH_MODE |
+ ETDM_IN_CON2_MULTI_IP_TOTAL_CH_MASK);
+ if (data_mode == MTK_DAI_ETDM_DATA_MULTI_PIN) {
+ val |= ETDM_IN_CON2_MULTI_IP_2CH_MODE |
+ FIELD_PREP(ETDM_IN_CON2_MULTI_IP_TOTAL_CH_MASK, channels - 1);
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con2, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con3 */
+ mask |= ETDM_IN_CON3_DISABLE_OUT_MASK;
+ for (i = 0; i < channels; i += 2) {
+ if (etdm_data->in_disable_ch[i] &&
+ etdm_data->in_disable_ch[i + 1])
+ val |= ETDM_IN_CON3_DISABLE_OUT(i >> 1);
+ }
+ if (!slave_mode) {
+ mask |= ETDM_IN_CON3_FS_MASK;
+ val |= FIELD_PREP(ETDM_IN_CON3_FS_MASK, get_etdm_fs_timing(rate));
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con3, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con4 */
+ mask |= (ETDM_IN_CON4_MASTER_LRCK_INV | ETDM_IN_CON4_MASTER_BCK_INV |
+ ETDM_IN_CON4_SLAVE_LRCK_INV | ETDM_IN_CON4_SLAVE_BCK_INV);
+ if (slave_mode) {
+ if (etdm_data->lrck_inv)
+ val |= ETDM_IN_CON4_SLAVE_LRCK_INV;
+ if (etdm_data->bck_inv)
+ val |= ETDM_IN_CON4_SLAVE_BCK_INV;
+ } else {
+ if (etdm_data->lrck_inv)
+ val |= ETDM_IN_CON4_MASTER_LRCK_INV;
+ if (etdm_data->bck_inv)
+ val |= ETDM_IN_CON4_MASTER_BCK_INV;
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con4, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con5 */
+ mask |= ETDM_IN_CON5_LR_SWAP_MASK;
+ mask |= ETDM_IN_CON5_ENABLE_ODD_MASK;
+ for (i = 0; i < channels; i += 2) {
+ if (etdm_data->in_disable_ch[i] &&
+ !etdm_data->in_disable_ch[i + 1]) {
+ val |= ETDM_IN_CON5_LR_SWAP(i >> 1);
+ val |= ETDM_IN_CON5_ENABLE_ODD(i >> 1);
+ } else if (!etdm_data->in_disable_ch[i] &&
+ etdm_data->in_disable_ch[i + 1]) {
+ val |= ETDM_IN_CON5_ENABLE_ODD(i >> 1);
+ }
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con5, mask, val);
+ return 0;
+}
+
+static int mtk_dai_etdm_out_configure(struct mtk_base_afe *afe,
+ unsigned int rate,
+ unsigned int channels,
+ int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ bool slave_mode;
+ unsigned int lrck_width;
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ int fs = 0;
+ int ret;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+ slave_mode = etdm_data->slave_mode;
+ lrck_width = etdm_data->lrck_width;
+
+ dev_dbg(afe->dev, "%s rate %u channels %u, id %d\n",
+ __func__, rate, channels, dai_id);
+
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ return ret;
+
+ /* con0 */
+ mask = ETDM_OUT_CON0_RELATCH_DOMAIN_MASK;
+ val = FIELD_PREP(ETDM_OUT_CON0_RELATCH_DOMAIN_MASK,
+ ETDM_RELATCH_TIMING_A1A2SYS);
+ regmap_update_bits(afe->regmap, etdm_reg.con0, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con1 */
+ if (lrck_width > 0) {
+ mask |= (ETDM_OUT_CON1_LRCK_AUTO_MODE |
+ ETDM_OUT_CON1_LRCK_WIDTH_MASK);
+ val |= FIELD_PREP(ETDM_OUT_CON1_LRCK_WIDTH_MASK, lrck_width - 1);
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con1, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ if (!slave_mode) {
+ /* con4 */
+ mask |= ETDM_OUT_CON4_FS_MASK;
+ val |= FIELD_PREP(ETDM_OUT_CON4_FS_MASK, get_etdm_fs_timing(rate));
+ }
+
+ mask |= ETDM_OUT_CON4_RELATCH_EN_MASK;
+ if (dai_id == MT8188_AFE_IO_ETDM1_OUT)
+ fs = MT8188_ETDM_OUT1_1X_EN;
+ else if (dai_id == MT8188_AFE_IO_ETDM2_OUT)
+ fs = MT8188_ETDM_OUT2_1X_EN;
+
+ val |= FIELD_PREP(ETDM_OUT_CON4_RELATCH_EN_MASK, fs);
+
+ regmap_update_bits(afe->regmap, etdm_reg.con4, mask, val);
+
+ mask = 0;
+ val = 0;
+
+ /* con5 */
+ mask |= (ETDM_OUT_CON5_MASTER_LRCK_INV | ETDM_OUT_CON5_MASTER_BCK_INV |
+ ETDM_OUT_CON5_SLAVE_LRCK_INV | ETDM_OUT_CON5_SLAVE_BCK_INV);
+ if (slave_mode) {
+ if (etdm_data->lrck_inv)
+ val |= ETDM_OUT_CON5_SLAVE_LRCK_INV;
+ if (etdm_data->bck_inv)
+ val |= ETDM_OUT_CON5_SLAVE_BCK_INV;
+ } else {
+ if (etdm_data->lrck_inv)
+ val |= ETDM_OUT_CON5_MASTER_LRCK_INV;
+ if (etdm_data->bck_inv)
+ val |= ETDM_OUT_CON5_MASTER_BCK_INV;
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con5, mask, val);
+
+ return 0;
+}
+
+static int mtk_dai_etdm_mclk_configure(struct mtk_base_afe *afe, int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ int clk_id = mtk_dai_etdm_get_clk_id_by_dai_id(dai_id);
+ int clkdiv_id = mtk_dai_etdm_get_clkdiv_id_by_dai_id(dai_id);
+ int apll_clk_id;
+ int apll;
+ int ret;
+
+ if (clk_id < 0 || clkdiv_id < 0)
+ return -EINVAL;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ return ret;
+
+ if (etdm_data->mclk_dir == SND_SOC_CLOCK_OUT)
+ regmap_set_bits(afe->regmap, etdm_reg.con1,
+ ETDM_CON1_MCLK_OUTPUT);
+ else
+ regmap_clear_bits(afe->regmap, etdm_reg.con1,
+ ETDM_CON1_MCLK_OUTPUT);
+
+ if (etdm_data->mclk_freq) {
+ apll = etdm_data->mclk_apll;
+ apll_clk_id = mt8188_afe_get_mclk_source_clk_id(apll);
+ if (apll_clk_id < 0)
+ return apll_clk_id;
+
+ /* select apll */
+ ret = mt8188_afe_set_clk_parent(afe, afe_priv->clk[clk_id],
+ afe_priv->clk[apll_clk_id]);
+ if (ret)
+ return ret;
+
+ /* set rate */
+ ret = mt8188_afe_set_clk_rate(afe, afe_priv->clk[clkdiv_id],
+ etdm_data->mclk_freq);
+ if (ret)
+ return ret;
+ } else {
+ if (etdm_data->mclk_dir == SND_SOC_CLOCK_OUT)
+ dev_dbg(afe->dev, "%s mclk freq = 0\n", __func__);
+ }
+
+ return 0;
+}
+
+static int mtk_dai_etdm_configure(struct mtk_base_afe *afe,
+ unsigned int rate,
+ unsigned int channels,
+ unsigned int bit_width,
+ int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct etdm_con_reg etdm_reg;
+ bool slave_mode;
+ unsigned int etdm_channels;
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ unsigned int bck;
+ unsigned int wlen = get_etdm_wlen(bit_width);
+ int ret;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+ slave_mode = etdm_data->slave_mode;
+
+ ret = get_etdm_reg(dai_id, &etdm_reg);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(afe->dev, "%s fmt %u data %u lrck %d-%u bck %d, clock %u slv %u\n",
+ __func__, etdm_data->format, etdm_data->data_mode,
+ etdm_data->lrck_inv, etdm_data->lrck_width, etdm_data->bck_inv,
+ etdm_data->clock_mode, etdm_data->slave_mode);
+ dev_dbg(afe->dev, "%s rate %u channels %u bitwidth %u, id %d\n",
+ __func__, rate, channels, bit_width, dai_id);
+
+ etdm_channels = (etdm_data->data_mode == MTK_DAI_ETDM_DATA_ONE_PIN) ?
+ get_etdm_ch_fixup(channels) : 2;
+
+ bck = rate * etdm_channels * wlen;
+ if (bck > MT8188_ETDM_NORMAL_MAX_BCK_RATE) {
+ dev_err(afe->dev, "%s bck rate %u not support\n",
+ __func__, bck);
+ return -EINVAL;
+ }
+
+ /* con0 */
+ mask |= ETDM_CON0_BIT_LEN_MASK;
+ val |= FIELD_PREP(ETDM_CON0_BIT_LEN_MASK, bit_width - 1);
+ mask |= ETDM_CON0_WORD_LEN_MASK;
+ val |= FIELD_PREP(ETDM_CON0_WORD_LEN_MASK, wlen - 1);
+ mask |= ETDM_CON0_FORMAT_MASK;
+ val |= FIELD_PREP(ETDM_CON0_FORMAT_MASK, etdm_data->format);
+ mask |= ETDM_CON0_CH_NUM_MASK;
+ val |= FIELD_PREP(ETDM_CON0_CH_NUM_MASK, etdm_channels - 1);
+
+ mask |= ETDM_CON0_SLAVE_MODE;
+ if (slave_mode) {
+ if (dai_id == MT8188_AFE_IO_ETDM1_OUT) {
+ dev_err(afe->dev, "%s id %d only support master mode\n",
+ __func__, dai_id);
+ return -EINVAL;
+ }
+ val |= ETDM_CON0_SLAVE_MODE;
+ }
+ regmap_update_bits(afe->regmap, etdm_reg.con0, mask, val);
+
+ if (get_etdm_dir(dai_id) == ETDM_IN)
+ mtk_dai_etdm_in_configure(afe, rate, channels, dai_id);
+ else
+ mtk_dai_etdm_out_configure(afe, rate, channels, dai_id);
+
+ return 0;
+}
+
+static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ unsigned int rate = params_rate(params);
+ unsigned int bit_width = params_width(params);
+ unsigned int channels = params_channels(params);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *mst_etdm_data;
+ int mst_dai_id;
+ int slv_dai_id;
+ int ret;
+ int i;
+
+ dev_dbg(afe->dev, "%s '%s' period %u-%u\n",
+ __func__, snd_pcm_stream_str(substream),
+ params_period_size(params), params_periods(params));
+
+ if (is_cowork_mode(dai)) {
+ mst_dai_id = get_etdm_cowork_master_id(dai);
+ if (!is_valid_etdm_dai(mst_dai_id))
+ return -EINVAL;
+
+ ret = mtk_dai_etdm_mclk_configure(afe, mst_dai_id);
+ if (ret)
+ return ret;
+
+ ret = mtk_dai_etdm_configure(afe, rate, channels,
+ bit_width, mst_dai_id);
+ if (ret)
+ return ret;
+
+ mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
+ for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
+ slv_dai_id = mst_etdm_data->cowork_slv_id[i];
+ ret = mtk_dai_etdm_configure(afe, rate, channels,
+ bit_width, slv_dai_id);
+ if (ret)
+ return ret;
+
+ ret = mt8188_etdm_sync_mode_configure(afe, slv_dai_id);
+ if (ret)
+ return ret;
+ }
+ } else {
+ ret = mtk_dai_etdm_mclk_configure(afe, dai->id);
+ if (ret)
+ return ret;
+
+ ret = mtk_dai_etdm_configure(afe, rate, channels,
+ bit_width, dai->id);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_etdm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *mst_etdm_data;
+ int mst_dai_id;
+ int slv_dai_id;
+ int ret;
+ int i;
+
+ if (!is_valid_etdm_dai(dai->id))
+ return -EINVAL;
+ mst_etdm_data = afe_priv->dai_priv[dai->id];
+
+ dev_dbg(afe->dev, "%s(), dai id %d, prepared %d\n", __func__, dai->id,
+ mst_etdm_data->is_prepared);
+
+ if (mst_etdm_data->is_prepared)
+ return 0;
+
+ mst_etdm_data->is_prepared = true;
+
+ if (is_cowork_mode(dai)) {
+ mst_dai_id = get_etdm_cowork_master_id(dai);
+ if (!is_valid_etdm_dai(mst_dai_id))
+ return -EINVAL;
+ mst_etdm_data = afe_priv->dai_priv[mst_dai_id];
+
+ for (i = 0; i < mst_etdm_data->cowork_slv_count; i++) {
+ slv_dai_id = mst_etdm_data->cowork_slv_id[i];
+ ret = mt8188_afe_enable_etdm(afe, slv_dai_id);
+ if (ret) {
+ dev_dbg(afe->dev, "%s enable %d failed\n",
+ __func__, slv_dai_id);
+
+ return ret;
+ }
+ }
+
+ ret = mt8188_afe_enable_etdm(afe, mst_dai_id);
+ if (ret) {
+ dev_dbg(afe->dev, "%s enable %d failed\n",
+ __func__, mst_dai_id);
+
+ return ret;
+ }
+ } else {
+ ret = mt8188_afe_enable_etdm(afe, dai->id);
+ if (ret) {
+ dev_dbg(afe->dev, "%s enable %d failed\n",
+ __func__, dai->id);
+
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int mtk_dai_etdm_cal_mclk(struct mtk_base_afe *afe, int freq, int dai_id)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ int apll_rate;
+ int apll;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ if (freq == 0) {
+ etdm_data->mclk_freq = freq;
+ return 0;
+ }
+
+ if (etdm_data->mclk_fixed_apll == 0)
+ apll = mt8188_afe_get_default_mclk_source_by_rate(freq);
+ else
+ apll = etdm_data->mclk_apll;
+
+ apll_rate = mt8188_afe_get_mclk_source_rate(afe, apll);
+
+ if (freq > apll_rate) {
+ dev_err(afe->dev, "freq %d > apll rate %d\n", freq, apll_rate);
+ return -EINVAL;
+ }
+
+ if (apll_rate % freq != 0) {
+ dev_err(afe->dev, "APLL%d cannot generate freq Hz\n", apll);
+ return -EINVAL;
+ }
+
+ if (etdm_data->mclk_fixed_apll == 0)
+ etdm_data->mclk_apll = apll;
+ etdm_data->mclk_freq = freq;
+
+ return 0;
+}
+
+static int mtk_dai_etdm_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ int dai_id;
+
+ dev_dbg(dai->dev, "%s id %d freq %u, dir %d\n",
+ __func__, dai->id, freq, dir);
+ if (is_cowork_mode(dai))
+ dai_id = get_etdm_cowork_master_id(dai);
+ else
+ dai_id = dai->id;
+
+ if (!is_valid_etdm_dai(dai_id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai_id];
+ etdm_data->mclk_dir = dir;
+ return mtk_dai_etdm_cal_mclk(afe, freq, dai_id);
+}
+
+static int mtk_dai_etdm_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+
+ if (!is_valid_etdm_dai(dai->id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai->id];
+
+ dev_dbg(dai->dev, "%s id %d slot_width %d\n",
+ __func__, dai->id, slot_width);
+
+ etdm_data->slots = slots;
+ etdm_data->lrck_width = slot_width;
+ return 0;
+}
+
+static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+
+ if (!is_valid_etdm_dai(dai->id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai->id];
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_RJ;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPA;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ etdm_data->bck_inv = false;
+ etdm_data->lrck_inv = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ etdm_data->bck_inv = false;
+ etdm_data->lrck_inv = true;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ etdm_data->bck_inv = true;
+ etdm_data->lrck_inv = false;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ etdm_data->bck_inv = true;
+ etdm_data->lrck_inv = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ etdm_data->slave_mode = true;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ etdm_data->slave_mode = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_hdmitx_dptx_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id);
+
+ if (cg_id >= 0)
+ mt8188_afe_enable_clk(afe, afe_priv->clk[cg_id]);
+
+ mtk_dai_etdm_enable_mclk(afe, dai->id);
+
+ return 0;
+}
+
+static void mtk_dai_hdmitx_dptx_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int cg_id = mtk_dai_etdm_get_cg_id_by_dai_id(dai->id);
+ struct mtk_dai_etdm_priv *etdm_data;
+ int ret;
+
+ if (!is_valid_etdm_dai(dai->id))
+ return;
+ etdm_data = afe_priv->dai_priv[dai->id];
+
+ if (etdm_data->is_prepared) {
+ etdm_data->is_prepared = false;
+ /* disable etdm_out3 */
+ ret = mt8188_afe_disable_etdm(afe, dai->id);
+ if (ret)
+ dev_dbg(afe->dev, "%s disable failed\n", __func__);
+
+ /* disable dptx interface */
+ if (dai->id == MT8188_AFE_IO_DPTX)
+ regmap_clear_bits(afe->regmap, AFE_DPTX_CON,
+ AFE_DPTX_CON_ON);
+ }
+
+ mtk_dai_etdm_disable_mclk(afe, dai->id);
+
+ if (cg_id >= 0)
+ mt8188_afe_disable_clk(afe, afe_priv->clk[cg_id]);
+}
+
+static unsigned int mtk_dai_get_dptx_ch_en(unsigned int channel)
+{
+ switch (channel) {
+ case 1 ... 2:
+ return AFE_DPTX_CON_CH_EN_2CH;
+ case 3 ... 4:
+ return AFE_DPTX_CON_CH_EN_4CH;
+ case 5 ... 6:
+ return AFE_DPTX_CON_CH_EN_6CH;
+ case 7 ... 8:
+ return AFE_DPTX_CON_CH_EN_8CH;
+ default:
+ return AFE_DPTX_CON_CH_EN_2CH;
+ }
+}
+
+static unsigned int mtk_dai_get_dptx_ch(unsigned int ch)
+{
+ return (ch > 2) ?
+ AFE_DPTX_CON_CH_NUM_8CH : AFE_DPTX_CON_CH_NUM_2CH;
+}
+
+static unsigned int mtk_dai_get_dptx_wlen(snd_pcm_format_t format)
+{
+ return snd_pcm_format_physical_width(format) <= 16 ?
+ AFE_DPTX_CON_16BIT : AFE_DPTX_CON_24BIT;
+}
+
+static int mtk_dai_hdmitx_dptx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ unsigned int rate = params_rate(params);
+ unsigned int channels = params_channels(params);
+ snd_pcm_format_t format = params_format(params);
+ int width = snd_pcm_format_physical_width(format);
+ int ret;
+
+ if (!is_valid_etdm_dai(dai->id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai->id];
+
+ /* dptx configure */
+ if (dai->id == MT8188_AFE_IO_DPTX) {
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ AFE_DPTX_CON_CH_EN_MASK,
+ mtk_dai_get_dptx_ch_en(channels));
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ AFE_DPTX_CON_CH_NUM_MASK,
+ mtk_dai_get_dptx_ch(channels));
+ regmap_update_bits(afe->regmap, AFE_DPTX_CON,
+ AFE_DPTX_CON_16BIT_MASK,
+ mtk_dai_get_dptx_wlen(format));
+
+ if (mtk_dai_get_dptx_ch(channels) == AFE_DPTX_CON_CH_NUM_8CH) {
+ etdm_data->data_mode = MTK_DAI_ETDM_DATA_ONE_PIN;
+ channels = 8;
+ } else {
+ channels = 2;
+ }
+ } else {
+ etdm_data->data_mode = MTK_DAI_ETDM_DATA_MULTI_PIN;
+ }
+
+ ret = mtk_dai_etdm_mclk_configure(afe, dai->id);
+ if (ret)
+ return ret;
+
+ ret = mtk_dai_etdm_configure(afe, rate, channels, width, dai->id);
+
+ return ret;
+}
+
+static int mtk_dai_hdmitx_dptx_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+
+ if (!is_valid_etdm_dai(dai->id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai->id];
+
+ dev_dbg(afe->dev, "%s(), dai id %d, prepared %d\n", __func__, dai->id,
+ etdm_data->is_prepared);
+
+ if (etdm_data->is_prepared)
+ return 0;
+
+ etdm_data->is_prepared = true;
+
+ /* enable dptx interface */
+ if (dai->id == MT8188_AFE_IO_DPTX)
+ regmap_set_bits(afe->regmap, AFE_DPTX_CON, AFE_DPTX_CON_ON);
+
+ /* enable etdm_out3 */
+ return mt8188_afe_enable_etdm(afe, dai->id);
+}
+
+static int mtk_dai_hdmitx_dptx_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id,
+ unsigned int freq,
+ int dir)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+
+ if (!is_valid_etdm_dai(dai->id))
+ return -EINVAL;
+ etdm_data = afe_priv->dai_priv[dai->id];
+
+ dev_dbg(dai->dev, "%s id %d freq %u, dir %d\n",
+ __func__, dai->id, freq, dir);
+
+ etdm_data->mclk_dir = dir;
+ return mtk_dai_etdm_cal_mclk(afe, freq, dai->id);
+}
+
+static const struct snd_soc_dai_ops mtk_dai_etdm_ops = {
+ .startup = mtk_dai_etdm_startup,
+ .shutdown = mtk_dai_etdm_shutdown,
+ .hw_params = mtk_dai_etdm_hw_params,
+ .prepare = mtk_dai_etdm_prepare,
+ .set_sysclk = mtk_dai_etdm_set_sysclk,
+ .set_fmt = mtk_dai_etdm_set_fmt,
+ .set_tdm_slot = mtk_dai_etdm_set_tdm_slot,
+};
+
+static const struct snd_soc_dai_ops mtk_dai_hdmitx_dptx_ops = {
+ .startup = mtk_dai_hdmitx_dptx_startup,
+ .shutdown = mtk_dai_hdmitx_dptx_shutdown,
+ .hw_params = mtk_dai_hdmitx_dptx_hw_params,
+ .prepare = mtk_dai_hdmitx_dptx_prepare,
+ .set_sysclk = mtk_dai_hdmitx_dptx_set_sysclk,
+ .set_fmt = mtk_dai_etdm_set_fmt,
+};
+
+/* dai driver */
+#define MTK_ETDM_RATES (SNDRV_PCM_RATE_8000_192000)
+
+#define MTK_ETDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = {
+ {
+ .name = "DPTX",
+ .id = MT8188_AFE_IO_DPTX,
+ .playback = {
+ .stream_name = "DPTX",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_hdmitx_dptx_ops,
+ },
+ {
+ .name = "ETDM1_IN",
+ .id = MT8188_AFE_IO_ETDM1_IN,
+ .capture = {
+ .stream_name = "ETDM1_IN",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_etdm_ops,
+ },
+ {
+ .name = "ETDM2_IN",
+ .id = MT8188_AFE_IO_ETDM2_IN,
+ .capture = {
+ .stream_name = "ETDM2_IN",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_etdm_ops,
+ },
+ {
+ .name = "ETDM1_OUT",
+ .id = MT8188_AFE_IO_ETDM1_OUT,
+ .playback = {
+ .stream_name = "ETDM1_OUT",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_etdm_ops,
+ },
+ {
+ .name = "ETDM2_OUT",
+ .id = MT8188_AFE_IO_ETDM2_OUT,
+ .playback = {
+ .stream_name = "ETDM2_OUT",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_etdm_ops,
+ },
+ {
+ .name = "ETDM3_OUT",
+ .id = MT8188_AFE_IO_ETDM3_OUT,
+ .playback = {
+ .stream_name = "ETDM3_OUT",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = MTK_ETDM_RATES,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_hdmitx_dptx_ops,
+ },
+};
+
+static void mt8188_etdm_update_sync_info(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ struct mtk_dai_etdm_priv *mst_data;
+ int mst_dai_id;
+ int i;
+
+ for (i = MT8188_AFE_IO_ETDM_START; i < MT8188_AFE_IO_ETDM_END; i++) {
+ etdm_data = afe_priv->dai_priv[i];
+ if (etdm_data->cowork_source_id != COWORK_ETDM_NONE) {
+ mst_dai_id = etdm_data->cowork_source_id;
+ mst_data = afe_priv->dai_priv[mst_dai_id];
+ if (mst_data->cowork_source_id != COWORK_ETDM_NONE)
+ dev_err(afe->dev, "%s [%d] wrong sync source\n",
+ __func__, i);
+ mst_data->cowork_slv_id[mst_data->cowork_slv_count] = i;
+ mst_data->cowork_slv_count++;
+ }
+ }
+}
+
+static void mt8188_dai_etdm_parse_of(struct mtk_base_afe *afe)
+{
+ const struct device_node *of_node = afe->dev->of_node;
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ char prop[48];
+ u8 disable_chn[MT8188_ETDM_MAX_CHANNELS];
+ int max_chn = MT8188_ETDM_MAX_CHANNELS;
+ unsigned int sync_id;
+ u32 sel;
+ int ret;
+ int dai_id;
+ int i, j;
+ struct {
+ const char *name;
+ const unsigned int sync_id;
+ } of_afe_etdms[MT8188_AFE_IO_ETDM_NUM] = {
+ {"etdm-in1", ETDM_SYNC_FROM_IN1},
+ {"etdm-in2", ETDM_SYNC_FROM_IN2},
+ {"etdm-out1", ETDM_SYNC_FROM_OUT1},
+ {"etdm-out2", ETDM_SYNC_FROM_OUT2},
+ {"etdm-out3", ETDM_SYNC_FROM_OUT3},
+ };
+
+ for (i = 0; i < MT8188_AFE_IO_ETDM_NUM; i++) {
+ dai_id = ETDM_TO_DAI_ID(i);
+ etdm_data = afe_priv->dai_priv[dai_id];
+
+ snprintf(prop, sizeof(prop), "mediatek,%s-multi-pin-mode",
+ of_afe_etdms[i].name);
+
+ etdm_data->data_mode = of_property_read_bool(of_node, prop);
+
+ snprintf(prop, sizeof(prop), "mediatek,%s-cowork-source",
+ of_afe_etdms[i].name);
+
+ ret = of_property_read_u32(of_node, prop, &sel);
+ if (ret == 0) {
+ if (sel >= MT8188_AFE_IO_ETDM_NUM) {
+ dev_err(afe->dev, "%s invalid id=%d\n",
+ __func__, sel);
+ etdm_data->cowork_source_id = COWORK_ETDM_NONE;
+ } else {
+ sync_id = of_afe_etdms[sel].sync_id;
+ etdm_data->cowork_source_id =
+ sync_to_dai_id(sync_id);
+ }
+ } else {
+ etdm_data->cowork_source_id = COWORK_ETDM_NONE;
+ }
+ }
+
+ /* etdm in only */
+ for (i = 0; i < 2; i++) {
+ snprintf(prop, sizeof(prop), "mediatek,%s-chn-disabled",
+ of_afe_etdms[i].name);
+
+ ret = of_property_read_variable_u8_array(of_node, prop,
+ disable_chn,
+ 1, max_chn);
+ if (ret < 0)
+ continue;
+
+ for (j = 0; j < ret; j++) {
+ if (disable_chn[j] >= MT8188_ETDM_MAX_CHANNELS)
+ dev_err(afe->dev, "%s [%d] invalid chn %u\n",
+ __func__, j, disable_chn[j]);
+ else
+ etdm_data->in_disable_ch[disable_chn[j]] = true;
+ }
+ }
+ mt8188_etdm_update_sync_info(afe);
+}
+
+static int init_etdm_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_priv;
+ int i;
+
+ for (i = MT8188_AFE_IO_ETDM_START; i < MT8188_AFE_IO_ETDM_END; i++) {
+ etdm_priv = devm_kzalloc(afe->dev,
+ sizeof(struct mtk_dai_etdm_priv),
+ GFP_KERNEL);
+ if (!etdm_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[i] = etdm_priv;
+ }
+
+ afe_priv->dai_priv[MT8188_AFE_IO_DPTX] =
+ afe_priv->dai_priv[MT8188_AFE_IO_ETDM3_OUT];
+
+ mt8188_dai_etdm_parse_of(afe);
+ return 0;
+}
+
+int mt8188_dai_etdm_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_etdm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_etdm_driver);
+
+ dai->dapm_widgets = mtk_dai_etdm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_etdm_widgets);
+ dai->dapm_routes = mtk_dai_etdm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_etdm_routes);
+ dai->controls = mtk_dai_etdm_controls;
+ dai->num_controls = ARRAY_SIZE(mtk_dai_etdm_controls);
+
+ return init_etdm_priv_data(afe);
+}
diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-pcm.c b/sound/soc/mediatek/mt8188/mt8188-dai-pcm.c
new file mode 100644
index 00000000000000..5bc854a8f3df34
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-dai-pcm.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MediaTek ALSA SoC Audio DAI PCM I/F Control
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "mt8188-afe-clk.h"
+#include "mt8188-afe-common.h"
+#include "mt8188-reg.h"
+
+enum {
+ MTK_DAI_PCM_FMT_I2S,
+ MTK_DAI_PCM_FMT_EIAJ,
+ MTK_DAI_PCM_FMT_MODEA,
+ MTK_DAI_PCM_FMT_MODEB,
+};
+
+enum {
+ MTK_DAI_PCM_CLK_A1SYS,
+ MTK_DAI_PCM_CLK_A2SYS,
+ MTK_DAI_PCM_CLK_26M_48K,
+ MTK_DAI_PCM_CLK_26M_441K,
+};
+
+struct mtk_dai_pcm_rate {
+ unsigned int rate;
+ unsigned int reg_value;
+};
+
+struct mtk_dai_pcmif_priv {
+ unsigned int slave_mode;
+ unsigned int lrck_inv;
+ unsigned int bck_inv;
+ unsigned int format;
+};
+
+static const struct mtk_dai_pcm_rate mtk_dai_pcm_rates[] = {
+ { .rate = 8000, .reg_value = 0, },
+ { .rate = 16000, .reg_value = 1, },
+ { .rate = 32000, .reg_value = 2, },
+ { .rate = 48000, .reg_value = 3, },
+ { .rate = 11025, .reg_value = 1, },
+ { .rate = 22050, .reg_value = 2, },
+ { .rate = 44100, .reg_value = 3, },
+};
+
+static int mtk_dai_pcm_mode(unsigned int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mtk_dai_pcm_rates); i++)
+ if (mtk_dai_pcm_rates[i].rate == rate)
+ return mtk_dai_pcm_rates[i].reg_value;
+
+ return -EINVAL;
+}
+
+static const struct snd_kcontrol_new mtk_dai_pcm_o000_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I000 Switch", AFE_CONN0, 0, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I070 Switch", AFE_CONN0_2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new mtk_dai_pcm_o001_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I001 Switch", AFE_CONN1, 1, 1, 0),
+ SOC_DAPM_SINGLE_AUTODISABLE("I071 Switch", AFE_CONN1_2, 7, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget mtk_dai_pcm_widgets[] = {
+ SND_SOC_DAPM_MIXER("I002", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I003", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("O000", SND_SOC_NOPM, 0, 0,
+ mtk_dai_pcm_o000_mix,
+ ARRAY_SIZE(mtk_dai_pcm_o000_mix)),
+ SND_SOC_DAPM_MIXER("O001", SND_SOC_NOPM, 0, 0,
+ mtk_dai_pcm_o001_mix,
+ ARRAY_SIZE(mtk_dai_pcm_o001_mix)),
+
+ SND_SOC_DAPM_SUPPLY("PCM_1_EN", PCM_INTF_CON1, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("PCM1_INPUT"),
+ SND_SOC_DAPM_OUTPUT("PCM1_OUTPUT"),
+
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_asrc11"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_asrc12"),
+ SND_SOC_DAPM_CLOCK_SUPPLY("aud_pcmif"),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_pcm_routes[] = {
+ {"I002", NULL, "PCM1 Capture"},
+ {"I003", NULL, "PCM1 Capture"},
+
+ {"O000", "I000 Switch", "I000"},
+ {"O001", "I001 Switch", "I001"},
+
+ {"O000", "I070 Switch", "I070"},
+ {"O001", "I071 Switch", "I071"},
+
+ {"PCM1 Playback", NULL, "O000"},
+ {"PCM1 Playback", NULL, "O001"},
+
+ {"PCM1 Playback", NULL, "PCM_1_EN"},
+ {"PCM1 Playback", NULL, "aud_asrc12"},
+ {"PCM1 Playback", NULL, "aud_pcmif"},
+
+ {"PCM1 Capture", NULL, "PCM_1_EN"},
+ {"PCM1 Capture", NULL, "aud_asrc11"},
+ {"PCM1 Capture", NULL, "aud_pcmif"},
+
+ {"PCM1_OUTPUT", NULL, "PCM1 Playback"},
+ {"PCM1 Capture", NULL, "PCM1_INPUT"},
+};
+
+static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_pcmif_priv *pcmif_priv = NULL;
+ unsigned int slave_mode;
+ unsigned int lrck_inv;
+ unsigned int bck_inv;
+ unsigned int fmt;
+ unsigned int bit_width = dai->sample_bits;
+ unsigned int val = 0;
+ unsigned int mask = 0;
+ int fs = 0;
+ int mode = 0;
+
+ if (dai->id < 0)
+ return -EINVAL;
+
+ pcmif_priv = afe_priv->dai_priv[dai->id];
+ slave_mode = pcmif_priv->slave_mode;
+ lrck_inv = pcmif_priv->lrck_inv;
+ bck_inv = pcmif_priv->bck_inv;
+ fmt = pcmif_priv->format;
+
+ /* sync freq mode */
+ fs = mt8188_afe_fs_timing(runtime->rate);
+ if (fs < 0)
+ return -EINVAL;
+
+ val |= FIELD_PREP(PCM_INTF_CON2_SYNC_FREQ_MODE_MASK, fs);
+ mask |= PCM_INTF_CON2_SYNC_FREQ_MODE_MASK;
+
+ /* clk domain sel */
+ if (runtime->rate % 8000)
+ val |= FIELD_PREP(PCM_INTF_CON2_CLK_DOMAIN_SEL_MASK,
+ MTK_DAI_PCM_CLK_26M_441K);
+ else
+ val |= FIELD_PREP(PCM_INTF_CON2_CLK_DOMAIN_SEL_MASK,
+ MTK_DAI_PCM_CLK_26M_48K);
+ mask |= PCM_INTF_CON2_CLK_DOMAIN_SEL_MASK;
+
+ regmap_update_bits(afe->regmap, PCM_INTF_CON2, mask, val);
+
+ val = 0;
+ mask = 0;
+
+ /* pcm mode */
+ mode = mtk_dai_pcm_mode(runtime->rate);
+ if (mode < 0)
+ return -EINVAL;
+
+ val |= FIELD_PREP(PCM_INTF_CON1_PCM_MODE_MASK, mode);
+ mask |= PCM_INTF_CON1_PCM_MODE_MASK;
+
+ /* pcm format */
+ val |= FIELD_PREP(PCM_INTF_CON1_PCM_FMT_MASK, fmt);
+ mask |= PCM_INTF_CON1_PCM_FMT_MASK;
+
+ /* pcm sync length */
+ if (fmt == MTK_DAI_PCM_FMT_MODEA ||
+ fmt == MTK_DAI_PCM_FMT_MODEB)
+ val |= FIELD_PREP(PCM_INTF_CON1_SYNC_LENGTH_MASK, 1);
+ else
+ val |= FIELD_PREP(PCM_INTF_CON1_SYNC_LENGTH_MASK, bit_width);
+ mask |= PCM_INTF_CON1_SYNC_LENGTH_MASK;
+
+ /* pcm bits, word length */
+ if (bit_width > 16) {
+ val |= PCM_INTF_CON1_PCM_24BIT;
+ val |= PCM_INTF_CON1_PCM_WLEN_64BCK;
+ } else {
+ val |= PCM_INTF_CON1_PCM_16BIT;
+ val |= PCM_INTF_CON1_PCM_WLEN_32BCK;
+ }
+ mask |= PCM_INTF_CON1_PCM_BIT_MASK;
+ mask |= PCM_INTF_CON1_PCM_WLEN_MASK;
+
+ /* master/slave */
+ if (!slave_mode) {
+ val |= PCM_INTF_CON1_PCM_MASTER;
+
+ if (lrck_inv)
+ val |= PCM_INTF_CON1_SYNC_OUT_INV;
+ if (bck_inv)
+ val |= PCM_INTF_CON1_BCLK_OUT_INV;
+ mask |= PCM_INTF_CON1_CLK_OUT_INV_MASK;
+ } else {
+ val |= PCM_INTF_CON1_PCM_SLAVE;
+
+ if (lrck_inv)
+ val |= PCM_INTF_CON1_SYNC_IN_INV;
+ if (bck_inv)
+ val |= PCM_INTF_CON1_BCLK_IN_INV;
+ mask |= PCM_INTF_CON1_CLK_IN_INV_MASK;
+
+ // TODO: add asrc setting for slave mode
+ }
+ mask |= PCM_INTF_CON1_PCM_M_S_MASK;
+
+ regmap_update_bits(afe->regmap, PCM_INTF_CON1, mask, val);
+
+ return 0;
+}
+
+/* dai ops */
+static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ if (snd_soc_dai_get_widget_playback(dai)->active ||
+ snd_soc_dai_get_widget_capture(dai)->active)
+ return 0;
+
+ return mtk_dai_pcm_configure(substream, dai);
+}
+
+static int mtk_dai_pcm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_pcmif_priv *pcmif_priv = NULL;
+
+ dev_dbg(dai->dev, "%s fmt 0x%x\n", __func__, fmt);
+
+ if (dai->id < 0)
+ return -EINVAL;
+
+ pcmif_priv = afe_priv->dai_priv[dai->id];
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ pcmif_priv->format = MTK_DAI_PCM_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ pcmif_priv->format = MTK_DAI_PCM_FMT_MODEA;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ pcmif_priv->format = MTK_DAI_PCM_FMT_MODEB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ pcmif_priv->bck_inv = 0;
+ pcmif_priv->lrck_inv = 0;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ pcmif_priv->bck_inv = 0;
+ pcmif_priv->lrck_inv = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ pcmif_priv->bck_inv = 1;
+ pcmif_priv->lrck_inv = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ pcmif_priv->bck_inv = 1;
+ pcmif_priv->lrck_inv = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ pcmif_priv->slave_mode = 1;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ pcmif_priv->slave_mode = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_pcm_ops = {
+ .prepare = mtk_dai_pcm_prepare,
+ .set_fmt = mtk_dai_pcm_set_fmt,
+};
+
+/* dai driver */
+#define MTK_PCM_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define MTK_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_pcm_driver[] = {
+ {
+ .name = "PCM1",
+ .id = MT8188_AFE_IO_PCM,
+ .playback = {
+ .stream_name = "PCM1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .capture = {
+ .stream_name = "PCM1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MTK_PCM_RATES,
+ .formats = MTK_PCM_FORMATS,
+ },
+ .ops = &mtk_dai_pcm_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+};
+
+static int init_pcmif_priv_data(struct mtk_base_afe *afe)
+{
+ struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_pcmif_priv *pcmif_priv;
+
+ pcmif_priv = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_pcmif_priv),
+ GFP_KERNEL);
+ if (!pcmif_priv)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[MT8188_AFE_IO_PCM] = pcmif_priv;
+ return 0;
+}
+
+int mt8188_dai_pcm_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_pcm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_pcm_driver);
+
+ dai->dapm_widgets = mtk_dai_pcm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_pcm_widgets);
+ dai->dapm_routes = mtk_dai_pcm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_pcm_routes);
+
+ return init_pcmif_priv_data(afe);
+}
diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
new file mode 100644
index 00000000000000..919d74ea193466
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
@@ -0,0 +1,785 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * mt8188-mt6359.c -- MT8188-MT6359 ALSA SoC machine driver
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Trevor Wu <trevor.wu@mediatek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "mt8188-afe-common.h"
+#include "../../codecs/mt6359.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-soundcard-driver.h"
+
+/* FE */
+SND_SOC_DAILINK_DEFS(playback2,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback3,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback6,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback7,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL7")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback8,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL8")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback10,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL10")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(playback11,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL11")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture1,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture2,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL2")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture3,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL3")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture4,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL4")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture5,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL5")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture6,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL6")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture8,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL8")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture9,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL9")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture10,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL10")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+/* BE */
+SND_SOC_DAILINK_DEFS(adda,
+ DAILINK_COMP_ARRAY(COMP_CPU("ADDA")),
+ DAILINK_COMP_ARRAY(COMP_CODEC("mt6359-sound",
+ "mt6359-snd-codec-aif1")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(dptx,
+ DAILINK_COMP_ARRAY(COMP_CPU("DPTX")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(etdm1_in,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_IN")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(etdm2_in,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(etdm1_out,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM1_OUT")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(etdm2_out,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_OUT")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(etdm3_out,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM3_OUT")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(pcm1,
+ DAILINK_COMP_ARRAY(COMP_CPU("PCM1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY()));
+
+struct mt8188_mt6359_priv {
+ struct snd_soc_jack dp_jack;
+ struct snd_soc_jack hdmi_jack;
+};
+
+struct mt8188_card_data {
+ const char *name;
+ unsigned long quirk;
+};
+
+static const struct snd_soc_dapm_widget mt8188_mt6359_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_kcontrol_new mt8188_mt6359_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+#define CKSYS_AUD_TOP_CFG 0x032c
+#define CKSYS_AUD_TOP_MON 0x0330
+
+static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_afe =
+ snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct snd_soc_component *cmpnt_codec =
+ asoc_rtd_to_codec(rtd, 0)->component;
+ struct mtk_base_afe *afe;
+ struct mt8188_afe_private *afe_priv;
+ struct mtkaif_param *param;
+ int chosen_phase_1, chosen_phase_2;
+ int prev_cycle_1, prev_cycle_2;
+ int test_done_1, test_done_2;
+ int cycle_1, cycle_2;
+ int mtkaif_chosen_phase[MT8188_MTKAIF_MISO_NUM];
+ int mtkaif_phase_cycle[MT8188_MTKAIF_MISO_NUM];
+ int mtkaif_calibration_num_phase;
+ bool mtkaif_calibration_ok;
+ unsigned int monitor = 0;
+ int counter;
+ int phase;
+ int i;
+
+ if (!cmpnt_afe)
+ return -EINVAL;
+
+ afe = snd_soc_component_get_drvdata(cmpnt_afe);
+ afe_priv = afe->platform_priv;
+ param = &afe_priv->mtkaif_params;
+
+ dev_dbg(afe->dev, "%s(), start\n", __func__);
+
+ param->mtkaif_calibration_ok = false;
+ for (i = 0; i < MT8188_MTKAIF_MISO_NUM; i++) {
+ param->mtkaif_chosen_phase[i] = -1;
+ param->mtkaif_phase_cycle[i] = 0;
+ mtkaif_chosen_phase[i] = -1;
+ mtkaif_phase_cycle[i] = 0;
+ }
+
+ if (IS_ERR(afe_priv->topckgen)) {
+ dev_info(afe->dev, "%s() Cannot find topckgen controller\n",
+ __func__);
+ return 0;
+ }
+
+ pm_runtime_get_sync(afe->dev);
+ mt6359_mtkaif_calibration_enable(cmpnt_codec);
+
+ /* set test type to synchronizer pulse */
+ regmap_update_bits(afe_priv->topckgen,
+ CKSYS_AUD_TOP_CFG, 0xffff, 0x4);
+ mtkaif_calibration_num_phase = 42; /* mt6359: 0 ~ 42 */
+ mtkaif_calibration_ok = true;
+
+ for (phase = 0;
+ phase <= mtkaif_calibration_num_phase && mtkaif_calibration_ok;
+ phase++) {
+ mt6359_set_mtkaif_calibration_phase(cmpnt_codec,
+ phase, phase, phase);
+
+ regmap_set_bits(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, 0x1);
+
+ test_done_1 = 0;
+ test_done_2 = 0;
+
+ cycle_1 = -1;
+ cycle_2 = -1;
+
+ counter = 0;
+ while (!(test_done_1 & test_done_2)) {
+ regmap_read(afe_priv->topckgen,
+ CKSYS_AUD_TOP_MON, &monitor);
+ test_done_1 = (monitor >> 28) & 0x1;
+ test_done_2 = (monitor >> 29) & 0x1;
+
+ if (test_done_1 == 1)
+ cycle_1 = monitor & 0xf;
+
+ if (test_done_2 == 1)
+ cycle_2 = (monitor >> 4) & 0xf;
+
+ /* handle if never test done */
+ if (++counter > 10000) {
+ dev_info(afe->dev, "%s(), test fail, cycle_1 %d, cycle_2 %d, monitor 0x%x\n",
+ __func__,
+ cycle_1, cycle_2, monitor);
+ mtkaif_calibration_ok = false;
+ break;
+ }
+ }
+
+ if (phase == 0) {
+ prev_cycle_1 = cycle_1;
+ prev_cycle_2 = cycle_2;
+ }
+
+ if (cycle_1 != prev_cycle_1 &&
+ mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] < 0) {
+ mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] = phase - 1;
+ mtkaif_phase_cycle[MT8188_MTKAIF_MISO_0] = prev_cycle_1;
+ }
+
+ if (cycle_2 != prev_cycle_2 &&
+ mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] < 0) {
+ mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] = phase - 1;
+ mtkaif_phase_cycle[MT8188_MTKAIF_MISO_1] = prev_cycle_2;
+ }
+
+ regmap_clear_bits(afe_priv->topckgen, CKSYS_AUD_TOP_CFG, 0x1);
+
+ if (mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] >= 0 &&
+ mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] >= 0)
+ break;
+ }
+
+ if (mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] < 0) {
+ mtkaif_calibration_ok = false;
+ chosen_phase_1 = 0;
+ } else {
+ chosen_phase_1 = mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0];
+ }
+
+ if (mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] < 0) {
+ mtkaif_calibration_ok = false;
+ chosen_phase_2 = 0;
+ } else {
+ chosen_phase_2 = mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1];
+ }
+
+ mt6359_set_mtkaif_calibration_phase(cmpnt_codec,
+ chosen_phase_1,
+ chosen_phase_2,
+ 0);
+
+ mt6359_mtkaif_calibration_disable(cmpnt_codec);
+ pm_runtime_put(afe->dev);
+
+ param->mtkaif_calibration_ok = mtkaif_calibration_ok;
+ param->mtkaif_chosen_phase[MT8188_MTKAIF_MISO_0] = chosen_phase_1;
+ param->mtkaif_chosen_phase[MT8188_MTKAIF_MISO_1] = chosen_phase_2;
+
+ for (i = 0; i < MT8188_MTKAIF_MISO_NUM; i++)
+ param->mtkaif_phase_cycle[i] = mtkaif_phase_cycle[i];
+
+ dev_info(afe->dev, "%s(), end, calibration ok %d\n",
+ __func__, param->mtkaif_calibration_ok);
+
+ return 0;
+}
+
+static int mt8188_mt6359_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_component *cmpnt_codec =
+ asoc_rtd_to_codec(rtd, 0)->component;
+
+ /* set mtkaif protocol */
+ mt6359_set_mtkaif_protocol(cmpnt_codec,
+ MT6359_MTKAIF_PROTOCOL_2_CLK_P2);
+
+ /* mtkaif calibration */
+ mt8188_mt6359_mtkaif_calibration(rtd);
+
+ return 0;
+}
+
+enum {
+ DAI_LINK_DL2_FE,
+ DAI_LINK_DL3_FE,
+ DAI_LINK_DL6_FE,
+ DAI_LINK_DL7_FE,
+ DAI_LINK_DL8_FE,
+ DAI_LINK_DL10_FE,
+ DAI_LINK_DL11_FE,
+ DAI_LINK_UL1_FE,
+ DAI_LINK_UL2_FE,
+ DAI_LINK_UL3_FE,
+ DAI_LINK_UL4_FE,
+ DAI_LINK_UL5_FE,
+ DAI_LINK_UL6_FE,
+ DAI_LINK_UL8_FE,
+ DAI_LINK_UL9_FE,
+ DAI_LINK_UL10_FE,
+ DAI_LINK_ADDA_BE,
+ DAI_LINK_DPTX_BE,
+ DAI_LINK_ETDM1_IN_BE,
+ DAI_LINK_ETDM2_IN_BE,
+ DAI_LINK_ETDM1_OUT_BE,
+ DAI_LINK_ETDM2_OUT_BE,
+ DAI_LINK_ETDM3_OUT_BE,
+ DAI_LINK_PCM1_BE,
+};
+
+static int mt8188_dptx_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int rate = params_rate(params);
+ unsigned int mclk_fs_ratio = 256;
+ unsigned int mclk_fs = rate * mclk_fs_ratio;
+ struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
+
+ return snd_soc_dai_set_sysclk(dai, 0, mclk_fs, SND_SOC_CLOCK_OUT);
+}
+
+static const struct snd_soc_ops mt8188_dptx_ops = {
+ .hw_params = mt8188_dptx_hw_params,
+};
+
+static int mt8188_dptx_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ /* fix BE i2s format to 32bit, clean param mask first */
+ snd_mask_reset_range(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ 0, (__force unsigned int)SNDRV_PCM_FORMAT_LAST);
+
+ params_set_format(params, SNDRV_PCM_FORMAT_S32_LE);
+
+ return 0;
+}
+
+static int mt8188_hdmi_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mt8188_mt6359_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+ int ret = 0;
+
+ ret = snd_soc_card_jack_new(rtd->card, "HDMI Jack", SND_JACK_LINEOUT,
+ &priv->hdmi_jack);
+ if (ret) {
+ dev_info(rtd->dev, "%s, new jack failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_jack(component, &priv->hdmi_jack, NULL);
+ if (ret)
+ dev_info(rtd->dev, "%s, set jack failed on %s (ret=%d)\n",
+ __func__, component->name, ret);
+
+ return ret;
+}
+
+static int mt8188_dptx_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct mt8188_mt6359_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_component *component = asoc_rtd_to_codec(rtd, 0)->component;
+ int ret = 0;
+
+ ret = snd_soc_card_jack_new(rtd->card, "DP Jack", SND_JACK_LINEOUT,
+ &priv->dp_jack);
+ if (ret) {
+ dev_info(rtd->dev, "%s, new jack failed: %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_jack(component, &priv->dp_jack, NULL);
+ if (ret)
+ dev_info(rtd->dev, "%s, set jack failed on %s (ret=%d)\n",
+ __func__, component->name, ret);
+
+ return ret;
+}
+
+static struct snd_soc_dai_link mt8188_mt6359_dai_links[] = {
+ /* FE */
+ [DAI_LINK_DL2_FE] = {
+ .name = "DL2_FE",
+ .stream_name = "DL2 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(playback2),
+ },
+ [DAI_LINK_DL3_FE] = {
+ .name = "DL3_FE",
+ .stream_name = "DL3 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(playback3),
+ },
+ [DAI_LINK_DL6_FE] = {
+ .name = "DL6_FE",
+ .stream_name = "DL6 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(playback6),
+ },
+ [DAI_LINK_DL7_FE] = {
+ .name = "DL7_FE",
+ .stream_name = "DL7 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE,
+ },
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(playback7),
+ },
+ [DAI_LINK_DL8_FE] = {
+ .name = "DL8_FE",
+ .stream_name = "DL8 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(playback8),
+ },
+ [DAI_LINK_DL10_FE] = {
+ .name = "DL10_FE",
+ .stream_name = "DL10 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(playback10),
+ },
+ [DAI_LINK_DL11_FE] = {
+ .name = "DL11_FE",
+ .stream_name = "DL11 Playback",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(playback11),
+ },
+ [DAI_LINK_UL1_FE] = {
+ .name = "UL1_FE",
+ .stream_name = "UL1 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(capture1),
+ },
+ [DAI_LINK_UL2_FE] = {
+ .name = "UL2_FE",
+ .stream_name = "UL2 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(capture2),
+ },
+ [DAI_LINK_UL3_FE] = {
+ .name = "UL3_FE",
+ .stream_name = "UL3 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(capture3),
+ },
+ [DAI_LINK_UL4_FE] = {
+ .name = "UL4_FE",
+ .stream_name = "UL4 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(capture4),
+ },
+ [DAI_LINK_UL5_FE] = {
+ .name = "UL5_FE",
+ .stream_name = "UL5 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(capture5),
+ },
+ [DAI_LINK_UL6_FE] = {
+ .name = "UL6_FE",
+ .stream_name = "UL6 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_PRE,
+ SND_SOC_DPCM_TRIGGER_PRE,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(capture6),
+ },
+ [DAI_LINK_UL8_FE] = {
+ .name = "UL8_FE",
+ .stream_name = "UL8 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(capture8),
+ },
+ [DAI_LINK_UL9_FE] = {
+ .name = "UL9_FE",
+ .stream_name = "UL9 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(capture9),
+ },
+ [DAI_LINK_UL10_FE] = {
+ .name = "UL10_FE",
+ .stream_name = "UL10 Capture",
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST,
+ },
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(capture10),
+ },
+ /* BE */
+ [DAI_LINK_ADDA_BE] = {
+ .name = "ADDA_BE",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .init = mt8188_mt6359_init,
+ SND_SOC_DAILINK_REG(adda),
+ },
+ [DAI_LINK_DPTX_BE] = {
+ .name = "DPTX_BE",
+ .ops = &mt8188_dptx_ops,
+ .be_hw_params_fixup = mt8188_dptx_hw_params_fixup,
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(dptx),
+ },
+ [DAI_LINK_ETDM1_IN_BE] = {
+ .name = "ETDM1_IN_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ SND_SOC_DAILINK_REG(etdm1_in),
+ },
+ [DAI_LINK_ETDM2_IN_BE] = {
+ .name = "ETDM2_IN_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(etdm2_in),
+ },
+ [DAI_LINK_ETDM1_OUT_BE] = {
+ .name = "ETDM1_OUT_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(etdm1_out),
+ },
+ [DAI_LINK_ETDM2_OUT_BE] = {
+ .name = "ETDM2_OUT_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(etdm2_out),
+ },
+ [DAI_LINK_ETDM3_OUT_BE] = {
+ .name = "ETDM3_OUT_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .dpcm_playback = 1,
+ SND_SOC_DAILINK_REG(etdm3_out),
+ },
+ [DAI_LINK_PCM1_BE] = {
+ .name = "PCM1_BE",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBC_CFC,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ SND_SOC_DAILINK_REG(pcm1),
+ },
+};
+
+static struct snd_soc_card mt8188_mt6359_soc_card = {
+ .owner = THIS_MODULE,
+ .dai_link = mt8188_mt6359_dai_links,
+ .num_links = ARRAY_SIZE(mt8188_mt6359_dai_links),
+ .dapm_widgets = mt8188_mt6359_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt8188_mt6359_widgets),
+ .controls = mt8188_mt6359_controls,
+ .num_controls = ARRAY_SIZE(mt8188_mt6359_controls),
+};
+
+static int mt8188_mt6359_dev_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt8188_mt6359_soc_card;
+ struct device_node *platform_node;
+ struct mt8188_mt6359_priv *priv;
+ struct mt8188_card_data *card_data;
+ struct snd_soc_dai_link *dai_link;
+ int ret, i;
+
+ card_data = (struct mt8188_card_data *)of_device_get_match_data(&pdev->dev);
+ card->dev = &pdev->dev;
+
+ ret = snd_soc_of_parse_card_name(card, "model");
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "%s new card name parsing error\n",
+ __func__);
+
+ if (!card->name)
+ card->name = card_data->name;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (of_property_read_bool(pdev->dev.of_node, "audio-routing")) {
+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+ if (ret)
+ return ret;
+ }
+
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
+ ret = -EINVAL;
+ return dev_err_probe(&pdev->dev, ret, "Property 'platform' missing or invalid\n");
+ }
+
+ ret = parse_dai_link_info(card);
+ if (ret)
+ goto err;
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (!dai_link->platforms->name)
+ dai_link->platforms->of_node = platform_node;
+
+ if (strcmp(dai_link->name, "DPTX_BE") == 0) {
+ if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai"))
+ dai_link->init = mt8188_dptx_codec_init;
+ } else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) {
+ if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai"))
+ dai_link->init = mt8188_hdmi_codec_init;
+ }
+ }
+
+ snd_soc_card_set_drvdata(card, priv);
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err_probe(&pdev->dev, ret, "%s snd_soc_register_card fail\n",
+ __func__);
+err:
+ of_node_put(platform_node);
+ clean_card_reference(card);
+ return ret;
+}
+
+static struct mt8188_card_data mt8188_evb_card = {
+ .name = "mt8188_mt6359",
+};
+
+static const struct of_device_id mt8188_mt6359_dt_match[] = {
+ {
+ .compatible = "mediatek,mt8188-mt6359-evb",
+ .data = &mt8188_evb_card,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt8188_mt6359_dt_match);
+
+static struct platform_driver mt8188_mt6359_driver = {
+ .driver = {
+ .name = "mt8188_mt6359",
+ .of_match_table = mt8188_mt6359_dt_match,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = mt8188_mt6359_dev_probe,
+};
+
+module_platform_driver(mt8188_mt6359_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8188-MT6359 ALSA SoC machine driver");
+MODULE_AUTHOR("Trevor Wu <trevor.wu@mediatek.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("mt8188 mt6359 soc card");
diff --git a/sound/soc/mediatek/mt8188/mt8188-reg.h b/sound/soc/mediatek/mt8188/mt8188-reg.h
new file mode 100644
index 00000000000000..51cd1a83dd9dd7
--- /dev/null
+++ b/sound/soc/mediatek/mt8188/mt8188-reg.h
@@ -0,0 +1,3180 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt8188-reg.h -- MediaTek 8188 audio driver reg definition
+ *
+ * Copyright (c) 2022 MediaTek Inc.
+ * Author: Bicycle Tsai <bicycle.tsai@mediatek.com>
+ * Trevor Wu <trevor.wu@mediatek.com>
+ * Chun-Chia Chiu <chun-chia.chiu@mediatek.com>
+ */
+
+#ifndef _MT8188_REG_H_
+#define _MT8188_REG_H_
+
+#define AUDIO_TOP_CON0 (0x0000)
+#define AUDIO_TOP_CON1 (0x0004)
+#define AUDIO_TOP_CON2 (0x0008)
+#define AUDIO_TOP_CON3 (0x000c)
+#define AUDIO_TOP_CON4 (0x0010)
+#define AUDIO_TOP_CON5 (0x0014)
+#define AUDIO_TOP_CON6 (0x0018)
+#define AFE_MAS_HADDR_MSB (0x0020)
+#define AFE_MEMIF_ONE_HEART (0x0024)
+#define AFE_MUX_SEL_CFG (0x0044)
+#define PWR1_ASM_CON1 (0x0108)
+#define ASYS_IRQ_CONFIG (0x0110)
+#define ASYS_IRQ1_CON (0x0114)
+#define ASYS_IRQ2_CON (0x0118)
+#define ASYS_IRQ3_CON (0x011c)
+#define ASYS_IRQ4_CON (0x0120)
+#define ASYS_IRQ5_CON (0x0124)
+#define ASYS_IRQ6_CON (0x0128)
+#define ASYS_IRQ7_CON (0x012c)
+#define ASYS_IRQ8_CON (0x0130)
+#define ASYS_IRQ9_CON (0x0134)
+#define ASYS_IRQ10_CON (0x0138)
+#define ASYS_IRQ11_CON (0x013c)
+#define ASYS_IRQ12_CON (0x0140)
+#define ASYS_IRQ13_CON (0x0144)
+#define ASYS_IRQ14_CON (0x0148)
+#define ASYS_IRQ15_CON (0x014c)
+#define ASYS_IRQ16_CON (0x0150)
+#define ASYS_IRQ_CLR (0x0154)
+#define ASYS_IRQ_STATUS (0x0158)
+#define ASYS_IRQ_MON1 (0x015c)
+#define ASYS_IRQ_MON2 (0x0160)
+#define AFE_IRQ1_CON (0x0164)
+#define AFE_IRQ2_CON (0x0168)
+#define AFE_IRQ3_CON (0x016c)
+#define AFE_IRQ_MCU_CLR (0x0170)
+#define AFE_IRQ_STATUS (0x0174)
+#define AFE_IRQ_MASK (0x0178)
+#define ASYS_IRQ_MASK (0x017c)
+#define AFE_IRQ3_CON_MON (0x01b0)
+#define AFE_IRQ_MCU_MON2 (0x01b4)
+#define AFE_IRQ8_CON (0x01b8)
+#define AFE_IRQ9_CON (0x01bc)
+#define AFE_IRQ10_CON (0x01c0)
+#define AFE_IRQ9_CON_MON (0x01c4)
+#define ADSP_IRQ_MASK (0x01c8)
+#define ADSP_IRQ_STATUS (0x01cc)
+#define AFE_SINEGEN_CON0 (0x01f0)
+#define AFE_SINEGEN_CON1 (0x01f4)
+#define AFE_SINEGEN_CON2 (0x01f8)
+#define AFE_SINEGEN_CON3 (0x01fc)
+#define AFE_SPDIF_OUT_CON0 (0x0380)
+#define AFE_TDMOUT_CONN0 (0x0390)
+#define PWR1_ASM_CON2 (0x03b0)
+#define PWR1_ASM_CON3 (0x03b4)
+#define AFE_APLL_TUNER_CFG (0x03f8)
+#define AFE_APLL_TUNER_CFG1 (0x03fc)
+#define AUDIO_TOP_STA0 (0x0400)
+#define AUDIO_TOP_STA1 (0x0404)
+#define AFE_GAIN1_CON0 (0x0410)
+#define AFE_GAIN1_CON1 (0x0414)
+#define AFE_GAIN1_CON2 (0x0418)
+#define AFE_GAIN1_CON3 (0x041c)
+#define AFE_GAIN1_CUR (0x0424)
+#define AFE_GAIN2_CON0 (0x0428)
+#define AFE_GAIN2_CON1 (0x042c)
+#define AFE_GAIN2_CON2 (0x0430)
+#define AFE_GAIN2_CON3 (0x0434)
+#define AFE_GAIN2_CUR (0x043c)
+#define AFE_IEC_CFG (0x0480)
+#define AFE_IEC_NSNUM (0x0484)
+#define AFE_IEC_BURST_INFO (0x0488)
+#define AFE_IEC_BURST_LEN (0x048c)
+#define AFE_IEC_NSADR (0x0490)
+#define AFE_IEC_CHL_STAT0 (0x04a0)
+#define AFE_IEC_CHL_STAT1 (0x04a4)
+#define AFE_IEC_CHR_STAT0 (0x04a8)
+#define AFE_IEC_CHR_STAT1 (0x04ac)
+#define AFE_SPDIFIN_CFG0 (0x0500)
+#define AFE_SPDIFIN_CFG1 (0x0504)
+#define AFE_SPDIFIN_CHSTS1 (0x0508)
+#define AFE_SPDIFIN_CHSTS2 (0x050c)
+#define AFE_SPDIFIN_CHSTS3 (0x0510)
+#define AFE_SPDIFIN_CHSTS4 (0x0514)
+#define AFE_SPDIFIN_CHSTS5 (0x0518)
+#define AFE_SPDIFIN_CHSTS6 (0x051c)
+#define AFE_SPDIFIN_DEBUG1 (0x0520)
+#define AFE_SPDIFIN_DEBUG2 (0x0524)
+#define AFE_SPDIFIN_DEBUG3 (0x0528)
+#define AFE_SPDIFIN_DEBUG4 (0x052c)
+#define AFE_SPDIFIN_EC (0x0530)
+#define AFE_SPDIFIN_CKLOCK_CFG (0x0534)
+#define AFE_SPDIFIN_BR (0x053c)
+#define AFE_SPDIFIN_BR_DBG1 (0x0540)
+#define AFE_SPDIFIN_CKFBDIV (0x0544)
+#define AFE_SPDIFIN_INT_EXT (0x0548)
+#define AFE_SPDIFIN_INT_EXT2 (0x054c)
+#define SPDIFIN_FREQ_INFO (0x0550)
+#define SPDIFIN_FREQ_INFO_2 (0x0554)
+#define SPDIFIN_FREQ_INFO_3 (0x0558)
+#define SPDIFIN_FREQ_STATUS (0x055c)
+#define SPDIFIN_USERCODE1 (0x0560)
+#define SPDIFIN_USERCODE2 (0x0564)
+#define SPDIFIN_USERCODE3 (0x0568)
+#define SPDIFIN_USERCODE4 (0x056c)
+#define SPDIFIN_USERCODE5 (0x0570)
+#define SPDIFIN_USERCODE6 (0x0574)
+#define SPDIFIN_USERCODE7 (0x0578)
+#define SPDIFIN_USERCODE8 (0x057c)
+#define SPDIFIN_USERCODE9 (0x0580)
+#define SPDIFIN_USERCODE10 (0x0584)
+#define SPDIFIN_USERCODE11 (0x0588)
+#define SPDIFIN_USERCODE12 (0x058c)
+#define AFE_SPDIFIN_APLL_TUNER_CFG (0x0594)
+#define AFE_SPDIFIN_APLL_TUNER_CFG1 (0x0598)
+#define ASYS_TOP_CON (0x0600)
+#define AFE_LINEIN_APLL_TUNER_CFG (0x0610)
+#define AFE_LINEIN_APLL_TUNER_MON (0x0614)
+#define AFE_EARC_APLL_TUNER_CFG (0x0618)
+#define AFE_EARC_APLL_TUNER_MON (0x061c)
+#define PWR2_TOP_CON0 (0x0634)
+#define PWR2_TOP_CON1 (0x0638)
+#define PCM_INTF_CON1 (0x063c)
+#define PCM_INTF_CON2 (0x0640)
+#define AFE_CM0_CON (0x0660)
+#define AFE_CM1_CON (0x0664)
+#define AFE_CM2_CON (0x0668)
+#define AFE_CM0_MON (0x0670)
+#define AFE_CM1_MON (0x0674)
+#define AFE_CM2_MON (0x0678)
+#define AFE_MPHONE_MULTI_CON0 (0x06a4)
+#define AFE_MPHONE_MULTI_CON1 (0x06a8)
+#define AFE_MPHONE_MULTI_CON2 (0x06ac)
+#define AFE_MPHONE_MULTI_MON (0x06b0)
+#define AFE_MPHONE_MULTI_DET_REG_CON0 (0x06b4)
+#define AFE_MPHONE_MULTI_DET_REG_CON1 (0x06b8)
+#define AFE_MPHONE_MULTI_DET_REG_CON2 (0x06bc)
+#define AFE_MPHONE_MULTI_DET_REG_CON3 (0x06c0)
+#define AFE_MPHONE_MULTI_DET_MON0 (0x06c4)
+#define AFE_MPHONE_MULTI_DET_MON1 (0x06c8)
+#define AFE_MPHONE_MULTI_DET_MON2 (0x06d0)
+#define AFE_MPHONE_MULTI2_CON0 (0x06d4)
+#define AFE_MPHONE_MULTI2_CON1 (0x06d8)
+#define AFE_MPHONE_MULTI2_CON2 (0x06dc)
+#define AFE_MPHONE_MULTI2_MON (0x06e0)
+#define AFE_MPHONE_MULTI2_DET_REG_CON0 (0x06e4)
+#define AFE_MPHONE_MULTI2_DET_REG_CON1 (0x06e8)
+#define AFE_MPHONE_MULTI2_DET_REG_CON2 (0x06ec)
+#define AFE_MPHONE_MULTI2_DET_REG_CON3 (0x06f0)
+#define AFE_MPHONE_MULTI2_DET_MON0 (0x06f4)
+#define AFE_MPHONE_MULTI2_DET_MON1 (0x06f8)
+#define AFE_MPHONE_MULTI2_DET_MON2 (0x06fc)
+#define AFE_ADDA_IIR_COEF_02_01 (0x0700)
+#define AFE_ADDA_IIR_COEF_04_03 (0x0704)
+#define AFE_ADDA_IIR_COEF_06_05 (0x0708)
+#define AFE_ADDA_IIR_COEF_08_07 (0x070c)
+#define AFE_ADDA_IIR_COEF_10_09 (0x0710)
+#define AFE_ADDA_ULCF_CFG_02_01 (0x0714)
+#define AFE_ADDA_ULCF_CFG_04_03 (0x0718)
+#define AFE_ADDA_ULCF_CFG_06_05 (0x071c)
+#define AFE_ADDA_ULCF_CFG_08_07 (0x0720)
+#define AFE_ADDA_ULCF_CFG_10_09 (0x0724)
+#define AFE_ADDA_ULCF_CFG_12_11 (0x0728)
+#define AFE_ADDA_ULCF_CFG_14_13 (0x072c)
+#define AFE_ADDA_ULCF_CFG_16_15 (0x0730)
+#define AFE_ADDA_ULCF_CFG_18_17 (0x0734)
+#define AFE_ADDA_ULCF_CFG_20_19 (0x0738)
+#define AFE_ADDA_ULCF_CFG_22_21 (0x073c)
+#define AFE_ADDA_ULCF_CFG_24_23 (0x0740)
+#define AFE_ADDA_ULCF_CFG_26_25 (0x0744)
+#define AFE_ADDA_ULCF_CFG_28_27 (0x0748)
+#define AFE_ADDA_ULCF_CFG_30_29 (0x074c)
+#define AFE_ADDA6_IIR_COEF_02_01 (0x0750)
+#define AFE_ADDA6_IIR_COEF_04_03 (0x0754)
+#define AFE_ADDA6_IIR_COEF_06_05 (0x0758)
+#define AFE_ADDA6_IIR_COEF_08_07 (0x075c)
+#define AFE_ADDA6_IIR_COEF_10_09 (0x0760)
+#define AFE_ADDA6_ULCF_CFG_02_01 (0x0764)
+#define AFE_ADDA6_ULCF_CFG_04_03 (0x0768)
+#define AFE_ADDA6_ULCF_CFG_06_05 (0x076c)
+#define AFE_ADDA6_ULCF_CFG_08_07 (0x0770)
+#define AFE_ADDA6_ULCF_CFG_10_09 (0x0774)
+#define AFE_ADDA6_ULCF_CFG_12_11 (0x0778)
+#define AFE_ADDA6_ULCF_CFG_14_13 (0x077c)
+#define AFE_ADDA6_ULCF_CFG_16_15 (0x0780)
+#define AFE_ADDA6_ULCF_CFG_18_17 (0x0784)
+#define AFE_ADDA6_ULCF_CFG_20_19 (0x0788)
+#define AFE_ADDA6_ULCF_CFG_22_21 (0x078c)
+#define AFE_ADDA6_ULCF_CFG_24_23 (0x0790)
+#define AFE_ADDA6_ULCF_CFG_26_25 (0x0794)
+#define AFE_ADDA6_ULCF_CFG_28_27 (0x0798)
+#define AFE_ADDA6_ULCF_CFG_30_29 (0x079c)
+#define AFE_ADDA_MTKAIF_CFG0 (0x07a0)
+#define AFE_ADDA_MTKAIF_SYNCWORD_CFG (0x07a8)
+#define AFE_ADDA_MTKAIF_RX_CFG0 (0x07b4)
+#define AFE_ADDA_MTKAIF_RX_CFG1 (0x07b8)
+#define AFE_ADDA_MTKAIF_RX_CFG2 (0x07bc)
+#define AFE_ADDA_MTKAIF_MON0 (0x07c8)
+#define AFE_ADDA_MTKAIF_MON1 (0x07cc)
+#define AFE_AUD_PAD_TOP (0x07d4)
+#define AFE_ADDA6_MTKAIF_MON0 (0x07d8)
+#define AFE_ADDA6_MTKAIF_MON1 (0x07dc)
+#define AFE_ADDA6_MTKAIF_CFG0 (0x07e0)
+#define AFE_ADDA6_MTKAIF_RX_CFG0 (0x07e4)
+#define AFE_ADDA6_MTKAIF_RX_CFG1 (0x07e8)
+#define AFE_ADDA6_MTKAIF_RX_CFG2 (0x07ec)
+#define AFE_ADDA6_TOP_CON0 (0x07f0)
+#define AFE_ADDA6_UL_SRC_CON0 (0x07f4)
+#define AFE_ADDA6_UL_SRC_CON1 (0x07f8)
+#define AFE_ADDA6_SRC_DEBUG (0x0800)
+#define AFE_ADDA6_SRC_DEBUG_MON0 (0x0804)
+#define AFE_ADDA6_UL_SRC_MON0 (0x0818)
+#define AFE_ADDA6_UL_SRC_MON1 (0x081c)
+#define AFE_CONN0_5 (0x0830)
+#define AFE_CONN1_5 (0x0834)
+#define AFE_CONN2_5 (0x0838)
+#define AFE_CONN3_5 (0x083c)
+#define AFE_CONN4_5 (0x0840)
+#define AFE_CONN5_5 (0x0844)
+#define AFE_CONN6_5 (0x0848)
+#define AFE_CONN7_5 (0x084c)
+#define AFE_CONN8_5 (0x0850)
+#define AFE_CONN9_5 (0x0854)
+#define AFE_CONN10_5 (0x0858)
+#define AFE_CONN11_5 (0x085c)
+#define AFE_CONN12_5 (0x0860)
+#define AFE_CONN13_5 (0x0864)
+#define AFE_CONN14_5 (0x0868)
+#define AFE_CONN15_5 (0x086c)
+#define AFE_CONN16_5 (0x0870)
+#define AFE_CONN17_5 (0x0874)
+#define AFE_CONN18_5 (0x0878)
+#define AFE_CONN19_5 (0x087c)
+#define AFE_CONN20_5 (0x0880)
+#define AFE_CONN21_5 (0x0884)
+#define AFE_CONN22_5 (0x0888)
+#define AFE_CONN23_5 (0x088c)
+#define AFE_CONN24_5 (0x0890)
+#define AFE_CONN25_5 (0x0894)
+#define AFE_CONN26_5 (0x0898)
+#define AFE_CONN27_5 (0x089c)
+#define AFE_CONN28_5 (0x08a0)
+#define AFE_CONN29_5 (0x08a4)
+#define AFE_CONN30_5 (0x08a8)
+#define AFE_CONN31_5 (0x08ac)
+#define AFE_CONN32_5 (0x08b0)
+#define AFE_CONN33_5 (0x08b4)
+#define AFE_CONN34_5 (0x08b8)
+#define AFE_CONN35_5 (0x08bc)
+#define AFE_CONN36_5 (0x08c0)
+#define AFE_CONN37_5 (0x08c4)
+#define AFE_CONN38_5 (0x08c8)
+#define AFE_CONN39_5 (0x08cc)
+#define AFE_CONN40_5 (0x08d0)
+#define AFE_CONN41_5 (0x08d4)
+#define AFE_CONN42_5 (0x08d8)
+#define AFE_CONN43_5 (0x08dc)
+#define AFE_CONN44_5 (0x08e0)
+#define AFE_CONN45_5 (0x08e4)
+#define AFE_CONN46_5 (0x08e8)
+#define AFE_CONN47_5 (0x08ec)
+#define AFE_CONN48_5 (0x08f0)
+#define AFE_CONN49_5 (0x08f4)
+#define AFE_CONN50_5 (0x08f8)
+#define AFE_CONN51_5 (0x08fc)
+#define AFE_CONN52_5 (0x0900)
+#define AFE_CONN53_5 (0x0904)
+#define AFE_CONN54_5 (0x0908)
+#define AFE_CONN55_5 (0x090c)
+#define AFE_CONN56_5 (0x0910)
+#define AFE_CONN57_5 (0x0914)
+#define AFE_CONN58_5 (0x0918)
+#define AFE_CONN59_5 (0x091c)
+#define AFE_CONN60_5 (0x0920)
+#define AFE_CONN61_5 (0x0924)
+#define AFE_CONN62_5 (0x0928)
+#define AFE_CONN63_5 (0x092c)
+#define AFE_CONN64_5 (0x0930)
+#define AFE_CONN65_5 (0x0934)
+#define AFE_CONN66_5 (0x0938)
+#define AFE_CONN67_5 (0x093c)
+#define AFE_CONN68_5 (0x0940)
+#define AFE_CONN69_5 (0x0944)
+#define AFE_CONN70_5 (0x0948)
+#define AFE_CONN71_5 (0x094c)
+#define AFE_CONN72_5 (0x0950)
+#define AFE_CONN73_5 (0x0954)
+#define AFE_CONN74_5 (0x0958)
+#define AFE_CONN75_5 (0x095c)
+#define AFE_CONN76_5 (0x0960)
+#define AFE_CONN77_5 (0x0964)
+#define AFE_CONN78_5 (0x0968)
+#define AFE_CONN79_5 (0x096c)
+#define AFE_CONN80_5 (0x0970)
+#define AFE_CONN81_5 (0x0974)
+#define AFE_CONN82_5 (0x0978)
+#define AFE_CONN83_5 (0x097c)
+#define AFE_CONN84_5 (0x0980)
+#define AFE_CONN85_5 (0x0984)
+#define AFE_CONN86_5 (0x0988)
+#define AFE_CONN87_5 (0x098c)
+#define AFE_CONN88_5 (0x0990)
+#define AFE_CONN89_5 (0x0994)
+#define AFE_CONN90_5 (0x0998)
+#define AFE_CONN91_5 (0x099c)
+#define AFE_CONN92_5 (0x09a0)
+#define AFE_CONN93_5 (0x09a4)
+#define AFE_CONN94_5 (0x09a8)
+#define AFE_CONN95_5 (0x09ac)
+#define AFE_CONN96_5 (0x09b0)
+#define AFE_CONN97_5 (0x09b4)
+#define AFE_CONN98_5 (0x09b8)
+#define AFE_CONN99_5 (0x09bc)
+#define AFE_CONN100_5 (0x09c0)
+#define AFE_CONN101_5 (0x09c4)
+#define AFE_CONN102_5 (0x09c8)
+#define AFE_CONN103_5 (0x09cc)
+#define AFE_CONN104_5 (0x09d0)
+#define AFE_CONN105_5 (0x09d4)
+#define AFE_CONN106_5 (0x09d8)
+#define AFE_CONN107_5 (0x09dc)
+#define AFE_CONN108_5 (0x09e0)
+#define AFE_CONN109_5 (0x09e4)
+#define AFE_CONN110_5 (0x09e8)
+#define AFE_CONN111_5 (0x09ec)
+#define AFE_CONN112_5 (0x09f0)
+#define AFE_CONN113_5 (0x09f4)
+#define AFE_CONN114_5 (0x09f8)
+#define AFE_CONN115_5 (0x09fc)
+#define AFE_CONN116_5 (0x0a00)
+#define AFE_CONN117_5 (0x0a04)
+#define AFE_CONN118_5 (0x0a08)
+#define AFE_CONN119_5 (0x0a0c)
+#define AFE_CONN120_5 (0x0a10)
+#define AFE_CONN121_5 (0x0a14)
+#define AFE_CONN122_5 (0x0a18)
+#define AFE_CONN123_5 (0x0a1c)
+#define AFE_CONN124_5 (0x0a20)
+#define AFE_CONN125_5 (0x0a24)
+#define AFE_CONN126_5 (0x0a28)
+#define AFE_CONN127_5 (0x0a2c)
+#define AFE_CONN128_5 (0x0a30)
+#define AFE_CONN129_5 (0x0a34)
+#define AFE_CONN130_5 (0x0a38)
+#define AFE_CONN131_5 (0x0a3c)
+#define AFE_CONN132_5 (0x0a40)
+#define AFE_CONN133_5 (0x0a44)
+#define AFE_CONN134_5 (0x0a48)
+#define AFE_CONN135_5 (0x0a4c)
+#define AFE_CONN136_5 (0x0a50)
+#define AFE_CONN137_5 (0x0a54)
+#define AFE_CONN138_5 (0x0a58)
+#define AFE_CONN139_5 (0x0a5c)
+#define AFE_CONN_RS_5 (0x0a60)
+#define AFE_CONN_DI_5 (0x0a64)
+#define AFE_CONN_16BIT_5 (0x0a68)
+#define AFE_CONN_24BIT_5 (0x0a6c)
+#define AFE_SECURE_MASK_CONN53_5 (0x0a70)
+#define AFE_SECURE_MASK_CONN54_5 (0x0a74)
+#define AFE_SECURE_MASK_CONN55_5 (0x0a78)
+#define AFE_SECURE_MASK_CONN56_5 (0x0a7c)
+#define AFE_SECURE_MASK_CONN57_5 (0x0a80)
+#define AFE_SECURE_MASK_CONN58_5 (0x0a84)
+#define AFE_SECURE_MASK_CONN59_5 (0x0a88)
+#define AFE_SECURE_MASK_CONN60_5 (0x0a8c)
+#define AFE_SECURE_MASK_CONN61_5 (0x0a90)
+#define AFE_SECURE_MASK_CONN62_5 (0x0a94)
+#define AFE_SECURE_MASK_CONN63_5 (0x0a98)
+#define AFE_SECURE_MASK_CONN64_5 (0x0a9c)
+#define AFE_SECURE_MASK_CONN65_5 (0x0aa0)
+#define AFE_SECURE_MASK_CONN66_5 (0x0aa4)
+#define AFE_SECURE_MASK_CONN67_5 (0x0aa8)
+#define AFE_SECURE_MASK_CONN68_5 (0x0aac)
+#define AFE_SECURE_MASK_CONN69_5 (0x0ab0)
+#define AFE_SECURE_MASK_CONN70_5 (0x0ab4)
+#define AFE_SECURE_MASK_CONN71_5 (0x0ab8)
+#define AFE_SECURE_MASK_CONN72_5 (0x0abc)
+#define AFE_SECURE_MASK_CONN73_5 (0x0ac0)
+#define AFE_SECURE_MASK_CONN74_5 (0x0ac4)
+#define AFE_SECURE_MASK_CONN75_5 (0x0ac8)
+#define AFE_SECURE_MASK_CONN76_5 (0x0acc)
+#define AFE_SECURE_MASK_CONN77_5 (0x0ad0)
+#define AFE_SECURE_MASK_CONN78_5 (0x0ad4)
+#define AFE_SECURE_MASK_CONN79_5 (0x0ad8)
+#define AFE_SECURE_MASK_CONN80_5 (0x0adc)
+#define AFE_SECURE_MASK_CONN81_5 (0x0ae0)
+#define AFE_SECURE_MASK_CONN82_5 (0x0ae4)
+#define AFE_SECURE_MASK_CONN83_5 (0x0ae8)
+#define AFE_SECURE_MASK_CONN84_5 (0x0aec)
+#define AFE_SECURE_MASK_CONN85_5 (0x0af0)
+#define AFE_SECURE_MASK_CONN86_5 (0x0af4)
+#define AFE_SECURE_MASK_CONN87_5 (0x0af8)
+#define AFE_SECURE_MASK_CONN88_5 (0x0afc)
+#define AFE_SECURE_MASK_CONN89_5 (0x0b00)
+#define AFE_SECURE_MASK_CONN90_5 (0x0b04)
+#define AFE_SECURE_MASK_CONN91_5 (0x0b08)
+#define AFE_SECURE_MASK_CONN92_5 (0x0b0c)
+#define AFE_SECURE_MASK_CONN93_5 (0x0b10)
+#define AFE_SECURE_MASK_CONN94_5 (0x0b14)
+#define AFE_SECURE_MASK_CONN95_5 (0x0b18)
+#define AFE_SECURE_MASK_CONN96_5 (0x0b1c)
+#define AFE_SECURE_MASK_CONN97_5 (0x0b20)
+#define AFE_SECURE_MASK_CONN98_5 (0x0b24)
+#define AFE_SECURE_MASK_CONN99_5 (0x0b28)
+#define AFE_SECURE_MASK_CONN100_5 (0x0b2c)
+#define AFE_SECURE_MASK_CONN101_5 (0x0b30)
+#define AFE_SECURE_MASK_CONN102_5 (0x0b34)
+#define AFE_SECURE_MASK_CONN103_5 (0x0b38)
+#define AFE_SECURE_MASK_CONN104_5 (0x0b3c)
+#define AFE_SECURE_MASK_CONN105_5 (0x0b40)
+#define AFE_SECURE_MASK_CONN106_5 (0x0b44)
+#define AFE_SECURE_MASK_CONN107_5 (0x0b48)
+#define AFE_SECURE_MASK_CONN108_5 (0x0b4c)
+#define AFE_SECURE_MASK_CONN109_5 (0x0b50)
+#define AFE_SECURE_MASK_CONN110_5 (0x0b54)
+#define AFE_SECURE_MASK_CONN111_5 (0x0b58)
+#define AFE_SECURE_MASK_CONN112_5 (0x0b5c)
+#define AFE_SECURE_MASK_CONN113_5 (0x0b60)
+#define AFE_SECURE_MASK_CONN114_5 (0x0b64)
+#define AFE_SECURE_MASK_CONN115_5 (0x0b68)
+#define AFE_SECURE_MASK_CONN116_5 (0x0b6c)
+#define AFE_SECURE_MASK_CONN117_5 (0x0b70)
+#define AFE_SECURE_MASK_CONN118_5 (0x0b74)
+#define AFE_SECURE_MASK_CONN119_5 (0x0b78)
+#define AFE_SECURE_MASK_CONN120_5 (0x0b7c)
+#define AFE_SECURE_MASK_CONN121_5 (0x0b80)
+#define AFE_SECURE_MASK_CONN122_5 (0x0b84)
+#define AFE_SECURE_MASK_CONN123_5 (0x0b88)
+#define AFE_SECURE_MASK_CONN124_5 (0x0b8c)
+#define AFE_SECURE_MASK_CONN125_5 (0x0b90)
+#define AFE_SECURE_MASK_CONN126_5 (0x0b94)
+#define AFE_SECURE_MASK_CONN127_5 (0x0b98)
+#define AFE_SECURE_MASK_CONN128_5 (0x0b9c)
+#define AFE_SECURE_MASK_CONN129_5 (0x0ba0)
+#define AFE_SECURE_MASK_CONN130_5 (0x0ba4)
+#define AFE_SECURE_MASK_CONN131_5 (0x0ba8)
+#define AFE_SECURE_MASK_CONN132_5 (0x0bac)
+#define AFE_SECURE_MASK_CONN133_5 (0x0bb0)
+#define AFE_SECURE_MASK_CONN134_5 (0x0bb4)
+#define AFE_SECURE_MASK_CONN135_5 (0x0bb8)
+#define AFE_SECURE_MASK_CONN136_5 (0x0bbc)
+#define AFE_SECURE_MASK_CONN137_5 (0x0bc0)
+#define AFE_SECURE_MASK_CONN138_5 (0x0bc4)
+#define AFE_SECURE_MASK_CONN139_5 (0x0bc8)
+#define AFE_SECURE_MASK_CONN_RS_5 (0x0bcc)
+#define AFE_SECURE_MASK_CONN_16BIT_5 (0x0bd0)
+#define AFE_SECURE_MASK_CONN_24BIT_5 (0x0bd4)
+#define AFE_ASRC11_NEW_CON0 (0x0d80)
+#define AFE_ASRC11_NEW_CON1 (0x0d84)
+#define AFE_ASRC11_NEW_CON2 (0x0d88)
+#define AFE_ASRC11_NEW_CON3 (0x0d8c)
+#define AFE_ASRC11_NEW_CON4 (0x0d90)
+#define AFE_ASRC11_NEW_CON5 (0x0d94)
+#define AFE_ASRC11_NEW_CON6 (0x0d98)
+#define AFE_ASRC11_NEW_CON7 (0x0d9c)
+#define AFE_ASRC11_NEW_CON8 (0x0da0)
+#define AFE_ASRC11_NEW_CON9 (0x0da4)
+#define AFE_ASRC11_NEW_CON10 (0x0da8)
+#define AFE_ASRC11_NEW_CON11 (0x0dac)
+#define AFE_ASRC11_NEW_CON13 (0x0db4)
+#define AFE_ASRC11_NEW_CON14 (0x0db8)
+#define AFE_ASRC12_NEW_CON0 (0x0dc0)
+#define AFE_ASRC12_NEW_CON1 (0x0dc4)
+#define AFE_ASRC12_NEW_CON2 (0x0dc8)
+#define AFE_ASRC12_NEW_CON3 (0x0dcc)
+#define AFE_ASRC12_NEW_CON4 (0x0dd0)
+#define AFE_ASRC12_NEW_CON5 (0x0dd4)
+#define AFE_ASRC12_NEW_CON6 (0x0dd8)
+#define AFE_ASRC12_NEW_CON7 (0x0ddc)
+#define AFE_ASRC12_NEW_CON8 (0x0de0)
+#define AFE_ASRC12_NEW_CON9 (0x0de4)
+#define AFE_ASRC12_NEW_CON10 (0x0de8)
+#define AFE_ASRC12_NEW_CON11 (0x0dec)
+#define AFE_ASRC12_NEW_CON13 (0x0df4)
+#define AFE_ASRC12_NEW_CON14 (0x0df8)
+#define AFE_SECURE_MASK_CONN176 (0x0fe0)
+#define AFE_SECURE_MASK_CONN176_1 (0x0fe4)
+#define AFE_SECURE_MASK_CONN176_2 (0x0fe8)
+#define AFE_SECURE_MASK_CONN176_3 (0x0fec)
+#define AFE_SECURE_MASK_CONN176_4 (0x0ff0)
+#define AFE_SECURE_MASK_CONN176_5 (0x0ff4)
+#define AFE_SECURE_MASK_CONN177 (0x0ff8)
+#define AFE_SECURE_MASK_CONN177_1 (0x0ffc)
+#define AFE_LRCK_CNT (0x1018)
+#define AFE_SECURE_MASK_CONN177_2 (0x1020)
+#define AFE_SECURE_MASK_CONN177_3 (0x1024)
+#define AFE_SECURE_MASK_CONN177_4 (0x1028)
+#define AFE_SECURE_MASK_CONN177_5 (0x102c)
+#define AFE_SECURE_MASK_CONN182 (0x1090)
+#define AFE_SECURE_MASK_CONN182_1 (0x1094)
+#define AFE_SECURE_MASK_CONN182_2 (0x1098)
+#define AFE_SECURE_MASK_CONN182_3 (0x109c)
+#define AFE_SECURE_MASK_CONN182_4 (0x10a0)
+#define AFE_SECURE_MASK_CONN182_5 (0x10a4)
+#define AFE_SECURE_MASK_CONN183 (0x10a8)
+#define AFE_SECURE_MASK_CONN183_1 (0x10ac)
+#define AFE_SECURE_MASK_CONN183_2 (0x10b0)
+#define AFE_SECURE_MASK_CONN183_3 (0x10b4)
+#define AFE_SECURE_MASK_CONN183_4 (0x10b8)
+#define AFE_SECURE_MASK_CONN183_5 (0x10bc)
+#define AFE_DAC_CON0 (0x1200)
+#define AFE_DAC_CON1 (0x1204)
+#define AFE_DAC_CON2 (0x1208)
+#define AFE_DAC_MON0 (0x1218)
+#define AFE_DL1_BASE (0x1240)
+#define AFE_DL1_CUR (0x1244)
+#define AFE_DL1_END (0x1248)
+#define AFE_DL1_CON0 (0x124c)
+#define AFE_DL2_BASE (0x1250)
+#define AFE_DL2_CUR (0x1254)
+#define AFE_DL2_END (0x1258)
+#define AFE_DL2_CON0 (0x125c)
+#define AFE_DL3_BASE (0x1260)
+#define AFE_DL3_CUR (0x1264)
+#define AFE_DL3_END (0x1268)
+#define AFE_DL3_CON0 (0x126c)
+#define AFE_DL6_BASE (0x1290)
+#define AFE_DL6_CUR (0x1294)
+#define AFE_DL6_END (0x1298)
+#define AFE_DL6_CON0 (0x129c)
+#define AFE_DL7_BASE (0x12a0)
+#define AFE_DL7_CUR (0x12a4)
+#define AFE_DL7_END (0x12a8)
+#define AFE_DL7_CON0 (0x12ac)
+#define AFE_DL8_BASE (0x12b0)
+#define AFE_DL8_CUR (0x12b4)
+#define AFE_DL8_END (0x12b8)
+#define AFE_DL8_CON0 (0x12bc)
+#define AFE_DL10_BASE (0x12d0)
+#define AFE_DL10_CUR (0x12d4)
+#define AFE_DL10_END (0x12d8)
+#define AFE_DL10_CON0 (0x12dc)
+#define AFE_DL11_BASE (0x12e0)
+#define AFE_DL11_CUR (0x12e4)
+#define AFE_DL11_END (0x12e8)
+#define AFE_DL11_CON0 (0x12ec)
+#define AFE_UL1_BASE (0x1300)
+#define AFE_UL1_CUR (0x1304)
+#define AFE_UL1_END (0x1308)
+#define AFE_UL1_CON0 (0x130c)
+#define AFE_UL2_BASE (0x1310)
+#define AFE_UL2_CUR (0x1314)
+#define AFE_UL2_END (0x1318)
+#define AFE_UL2_CON0 (0x131c)
+#define AFE_UL3_BASE (0x1320)
+#define AFE_UL3_CUR (0x1324)
+#define AFE_UL3_END (0x1328)
+#define AFE_UL3_CON0 (0x132c)
+#define AFE_UL4_BASE (0x1330)
+#define AFE_UL4_CUR (0x1334)
+#define AFE_UL4_END (0x1338)
+#define AFE_UL4_CON0 (0x133c)
+#define AFE_UL5_BASE (0x1340)
+#define AFE_UL5_CUR (0x1344)
+#define AFE_UL5_END (0x1348)
+#define AFE_UL5_CON0 (0x134c)
+#define AFE_UL6_BASE (0x1350)
+#define AFE_UL6_CUR (0x1354)
+#define AFE_UL6_END (0x1358)
+#define AFE_UL6_CON0 (0x135c)
+#define AFE_UL8_BASE (0x1370)
+#define AFE_UL8_CUR (0x1374)
+#define AFE_UL8_END (0x1378)
+#define AFE_UL8_CON0 (0x137c)
+#define AFE_UL9_BASE (0x1380)
+#define AFE_UL9_CUR (0x1384)
+#define AFE_UL9_END (0x1388)
+#define AFE_UL9_CON0 (0x138c)
+#define AFE_UL10_BASE (0x13d0)
+#define AFE_UL10_CUR (0x13d4)
+#define AFE_UL10_END (0x13d8)
+#define AFE_UL10_CON0 (0x13dc)
+#define AFE_DL8_CHK_SUM1 (0x1400)
+#define AFE_DL8_CHK_SUM2 (0x1404)
+#define AFE_DL8_CHK_SUM3 (0x1408)
+#define AFE_DL8_CHK_SUM4 (0x140c)
+#define AFE_DL8_CHK_SUM5 (0x1410)
+#define AFE_DL8_CHK_SUM6 (0x1414)
+#define AFE_DL10_CHK_SUM1 (0x1418)
+#define AFE_DL10_CHK_SUM2 (0x141c)
+#define AFE_DL10_CHK_SUM3 (0x1420)
+#define AFE_DL10_CHK_SUM4 (0x1424)
+#define AFE_DL10_CHK_SUM5 (0x1428)
+#define AFE_DL10_CHK_SUM6 (0x142c)
+#define AFE_DL11_CHK_SUM1 (0x1430)
+#define AFE_DL11_CHK_SUM2 (0x1434)
+#define AFE_DL11_CHK_SUM3 (0x1438)
+#define AFE_DL11_CHK_SUM4 (0x143c)
+#define AFE_DL11_CHK_SUM5 (0x1440)
+#define AFE_DL11_CHK_SUM6 (0x1444)
+#define AFE_UL1_CHK_SUM1 (0x1450)
+#define AFE_UL1_CHK_SUM2 (0x1454)
+#define AFE_UL2_CHK_SUM1 (0x1458)
+#define AFE_UL2_CHK_SUM2 (0x145c)
+#define AFE_UL3_CHK_SUM1 (0x1460)
+#define AFE_UL3_CHK_SUM2 (0x1464)
+#define AFE_UL4_CHK_SUM1 (0x1468)
+#define AFE_UL4_CHK_SUM2 (0x146c)
+#define AFE_UL5_CHK_SUM1 (0x1470)
+#define AFE_UL5_CHK_SUM2 (0x1474)
+#define AFE_UL6_CHK_SUM1 (0x1478)
+#define AFE_UL6_CHK_SUM2 (0x147c)
+#define AFE_UL8_CHK_SUM1 (0x1488)
+#define AFE_UL8_CHK_SUM2 (0x148c)
+#define AFE_DL1_CHK_SUM1 (0x1490)
+#define AFE_DL1_CHK_SUM2 (0x1494)
+#define AFE_DL2_CHK_SUM1 (0x14a0)
+#define AFE_DL2_CHK_SUM2 (0x14a4)
+#define AFE_DL3_CHK_SUM1 (0x14b0)
+#define AFE_DL3_CHK_SUM2 (0x14b4)
+#define AFE_DL6_CHK_SUM1 (0x14e0)
+#define AFE_DL6_CHK_SUM2 (0x14e4)
+#define AFE_DL7_CHK_SUM1 (0x14f0)
+#define AFE_DL7_CHK_SUM2 (0x14f4)
+#define AFE_UL9_CHK_SUM1 (0x1528)
+#define AFE_UL9_CHK_SUM2 (0x152c)
+#define AFE_BUS_MON1 (0x1540)
+#define AFE_UL10_CHK_SUM1 (0x1550)
+#define AFE_UL10_CHK_SUM2 (0x1554)
+#define UL1_MOD2AGT_CNT_LAT (0x1568)
+#define UL2_MOD2AGT_CNT_LAT (0x156c)
+#define UL3_MOD2AGT_CNT_LAT (0x1570)
+#define UL4_MOD2AGT_CNT_LAT (0x1574)
+#define UL5_MOD2AGT_CNT_LAT (0x1578)
+#define UL6_MOD2AGT_CNT_LAT (0x157c)
+#define UL8_MOD2AGT_CNT_LAT (0x1588)
+#define UL9_MOD2AGT_CNT_LAT (0x158c)
+#define UL10_MOD2AGT_CNT_LAT (0x1590)
+#define AFE_MEMIF_AGENT_FS_CON0 (0x15a0)
+#define AFE_MEMIF_AGENT_FS_CON1 (0x15a4)
+#define AFE_MEMIF_AGENT_FS_CON2 (0x15a8)
+#define AFE_MEMIF_AGENT_FS_CON3 (0x15ac)
+#define AFE_MEMIF_BURST_CFG (0x1600)
+#define AFE_MEMIF_BUF_FULL_MON (0x1610)
+#define AFE_MEMIF_BUF_MON0 (0x1618)
+#define AFE_MEMIF_BUF_MON1 (0x161c)
+#define AFE_MEMIF_BUF_MON3 (0x1624)
+#define AFE_MEMIF_BUF_MON4 (0x1628)
+#define AFE_MEMIF_BUF_MON5 (0x162c)
+#define AFE_MEMIF_BUF_MON6 (0x1630)
+#define AFE_MEMIF_BUF_MON7 (0x1634)
+#define AFE_MEMIF_BUF_MON8 (0x1638)
+#define AFE_MEMIF_BUF_MON9 (0x163c)
+#define AFE_MEMIF_BUF_MON10 (0x1640)
+#define DL1_AGENT2MODULE_CNT (0x1674)
+#define DL2_AGENT2MODULE_CNT (0x1678)
+#define DL3_AGENT2MODULE_CNT (0x167c)
+#define DL6_AGENT2MODULE_CNT (0x1688)
+#define DL7_AGENT2MODULE_CNT (0x168c)
+#define DL8_AGENT2MODULE_CNT (0x1690)
+#define DL10_AGENT2MODULE_CNT (0x1698)
+#define DL11_AGENT2MODULE_CNT (0x169c)
+#define UL1_MODULE2AGENT_CNT (0x16a0)
+#define UL2_MODULE2AGENT_CNT (0x16a4)
+#define UL3_MODULE2AGENT_CNT (0x16a8)
+#define UL4_MODULE2AGENT_CNT (0x16ac)
+#define UL5_MODULE2AGENT_CNT (0x16b0)
+#define UL6_MODULE2AGENT_CNT (0x16b4)
+#define UL8_MODULE2AGENT_CNT (0x16bc)
+#define UL9_MODULE2AGENT_CNT (0x16c0)
+#define UL10_MODULE2AGENT_CNT (0x16c4)
+#define AFE_SECURE_CON2 (0x1798)
+#define AFE_SECURE_CON1 (0x179c)
+#define AFE_SECURE_CON (0x17a0)
+#define AFE_SRAM_BOUND (0x17a4)
+#define AFE_SE_SECURE_CON (0x17a8)
+#define AFE_SECURE_MASK_LOOPBACK (0x17bc)
+#define AFE_SRAM_SECURE_CON (0x1800)
+#define AFE_SRAM_SECURE_CON1 (0x1804)
+#define AFE_SRAM_SECURE_CON2 (0x1808)
+#define AFE_SECURE_SIDEBAND0 (0x1908)
+#define AFE_SECURE_SIDEBAND1 (0x190c)
+#define AFE_SECURE_SIDEBAND2 (0x1910)
+#define AFE_SECURE_SIDEBAND3 (0x1914)
+#define AFE_SECURE_MASK_BASE_ADR_MSB (0x1920)
+#define AFE_SECURE_MASK_END_ADR_MSB (0x1924)
+#define AFE_NORMAL_BASE_ADR_MSB (0x192c)
+#define AFE_NORMAL_END_ADR_MSB (0x1930)
+#define AFE_SECURE_MASK_LOOPBACK0 (0x1940)
+#define AFE_SECURE_MASK_LOOPBACK1 (0x1944)
+#define AFE_SECURE_MASK_LOOPBACK2 (0x1948)
+#define AFE_LOOPBACK_CFG0 (0x1950)
+#define AFE_LOOPBACK_CFG1 (0x1954)
+#define AFE_LOOPBACK_CFG2 (0x1958)
+#define AFE_DMIC0_UL_SRC_CON0 (0x1a00)
+#define AFE_DMIC0_UL_SRC_CON1 (0x1a04)
+#define AFE_DMIC0_SRC_DEBUG (0x1a08)
+#define AFE_DMIC0_SRC_DEBUG_MON0 (0x1a0c)
+#define AFE_DMIC0_UL_SRC_MON0 (0x1a10)
+#define AFE_DMIC0_UL_SRC_MON1 (0x1a14)
+#define AFE_DMIC0_IIR_COEF_02_01 (0x1a18)
+#define AFE_DMIC0_IIR_COEF_04_03 (0x1a1c)
+#define AFE_DMIC0_IIR_COEF_06_05 (0x1a20)
+#define AFE_DMIC0_IIR_COEF_08_07 (0x1a24)
+#define AFE_DMIC0_IIR_COEF_10_09 (0x1a28)
+#define AFE_DMIC1_UL_SRC_CON0 (0x1a68)
+#define AFE_DMIC1_UL_SRC_CON1 (0x1a6c)
+#define AFE_DMIC1_SRC_DEBUG (0x1a70)
+#define AFE_DMIC1_SRC_DEBUG_MON0 (0x1a74)
+#define AFE_DMIC1_UL_SRC_MON0 (0x1a78)
+#define AFE_DMIC1_UL_SRC_MON1 (0x1a7c)
+#define AFE_DMIC1_IIR_COEF_02_01 (0x1a80)
+#define AFE_DMIC1_IIR_COEF_04_03 (0x1a84)
+#define AFE_DMIC1_IIR_COEF_06_05 (0x1a88)
+#define AFE_DMIC1_IIR_COEF_08_07 (0x1a8c)
+#define AFE_DMIC1_IIR_COEF_10_09 (0x1a90)
+#define AFE_DMIC2_UL_SRC_CON0 (0x1ad0)
+#define AFE_DMIC2_UL_SRC_CON1 (0x1ad4)
+#define AFE_DMIC2_SRC_DEBUG (0x1ad8)
+#define AFE_DMIC2_SRC_DEBUG_MON0 (0x1adc)
+#define AFE_DMIC2_UL_SRC_MON0 (0x1ae0)
+#define AFE_DMIC2_UL_SRC_MON1 (0x1ae4)
+#define AFE_DMIC2_IIR_COEF_02_01 (0x1ae8)
+#define AFE_DMIC2_IIR_COEF_04_03 (0x1aec)
+#define AFE_DMIC2_IIR_COEF_06_05 (0x1af0)
+#define AFE_DMIC2_IIR_COEF_08_07 (0x1af4)
+#define AFE_DMIC2_IIR_COEF_10_09 (0x1af8)
+#define AFE_DMIC3_UL_SRC_CON0 (0x1b38)
+#define AFE_DMIC3_UL_SRC_CON1 (0x1b3c)
+#define AFE_DMIC3_SRC_DEBUG (0x1b40)
+#define AFE_DMIC3_SRC_DEBUG_MON0 (0x1b44)
+#define AFE_DMIC3_UL_SRC_MON0 (0x1b48)
+#define AFE_DMIC3_UL_SRC_MON1 (0x1b4c)
+#define AFE_DMIC3_IIR_COEF_02_01 (0x1b50)
+#define AFE_DMIC3_IIR_COEF_04_03 (0x1b54)
+#define AFE_DMIC3_IIR_COEF_06_05 (0x1b58)
+#define AFE_DMIC3_IIR_COEF_08_07 (0x1b5c)
+#define AFE_DMIC3_IIR_COEF_10_09 (0x1b60)
+#define DMIC_BYPASS_HW_GAIN (0x1bf0)
+#define DMIC_GAIN1_CON0 (0x1c00)
+#define DMIC_GAIN1_CON1 (0x1c04)
+#define DMIC_GAIN1_CON2 (0x1c08)
+#define DMIC_GAIN1_CON3 (0x1c0c)
+#define DMIC_GAIN1_CUR (0x1c10)
+#define DMIC_GAIN2_CON0 (0x1c20)
+#define DMIC_GAIN2_CON1 (0x1c24)
+#define DMIC_GAIN2_CON2 (0x1c28)
+#define DMIC_GAIN2_CON3 (0x1c2c)
+#define DMIC_GAIN2_CUR (0x1c30)
+#define DMIC_GAIN3_CON0 (0x1c40)
+#define DMIC_GAIN3_CON1 (0x1c44)
+#define DMIC_GAIN3_CON2 (0x1c48)
+#define DMIC_GAIN3_CON3 (0x1c4c)
+#define DMIC_GAIN3_CUR (0x1c50)
+#define DMIC_GAIN4_CON0 (0x1c60)
+#define DMIC_GAIN4_CON1 (0x1c64)
+#define DMIC_GAIN4_CON2 (0x1c68)
+#define DMIC_GAIN4_CON3 (0x1c6c)
+#define DMIC_GAIN4_CUR (0x1c70)
+#define ETDM_OUT1_DSD_FADE_CON (0x2260)
+#define ETDM_OUT1_DSD_FADE_CON1 (0x2264)
+#define ETDM_OUT3_DSD_FADE_CON (0x2280)
+#define ETDM_OUT3_DSD_FADE_CON1 (0x2284)
+#define ETDM_IN1_AFIFO_CON (0x2294)
+#define ETDM_IN2_AFIFO_CON (0x2298)
+#define ETDM_IN1_MONITOR (0x22c0)
+#define ETDM_IN2_MONITOR (0x22c4)
+#define ETDM_OUT1_MONITOR (0x22d0)
+#define ETDM_OUT2_MONITOR (0x22d4)
+#define ETDM_OUT3_MONITOR (0x22d8)
+#define ETDM_COWORK_SEC_CON0 (0x22e0)
+#define ETDM_COWORK_SEC_CON1 (0x22e4)
+#define ETDM_COWORK_SEC_CON2 (0x22e8)
+#define ETDM_COWORK_SEC_CON3 (0x22ec)
+#define ETDM_COWORK_CON0 (0x22f0)
+#define ETDM_COWORK_CON1 (0x22f4)
+#define ETDM_COWORK_CON2 (0x22f8)
+#define ETDM_COWORK_CON3 (0x22fc)
+#define ETDM_IN1_CON0 (0x2300)
+#define ETDM_IN1_CON1 (0x2304)
+#define ETDM_IN1_CON2 (0x2308)
+#define ETDM_IN1_CON3 (0x230c)
+#define ETDM_IN1_CON4 (0x2310)
+#define ETDM_IN1_CON5 (0x2314)
+#define ETDM_IN1_CON6 (0x2318)
+#define ETDM_IN1_CON7 (0x231c)
+#define ETDM_IN2_CON0 (0x2320)
+#define ETDM_IN2_CON1 (0x2324)
+#define ETDM_IN2_CON2 (0x2328)
+#define ETDM_IN2_CON3 (0x232c)
+#define ETDM_IN2_CON4 (0x2330)
+#define ETDM_IN2_CON5 (0x2334)
+#define ETDM_IN2_CON6 (0x2338)
+#define ETDM_IN2_CON7 (0x233c)
+#define ETDM_OUT1_CON0 (0x2380)
+#define ETDM_OUT1_CON1 (0x2384)
+#define ETDM_OUT1_CON2 (0x2388)
+#define ETDM_OUT1_CON3 (0x238c)
+#define ETDM_OUT1_CON4 (0x2390)
+#define ETDM_OUT1_CON5 (0x2394)
+#define ETDM_OUT1_CON6 (0x2398)
+#define ETDM_OUT1_CON7 (0x239c)
+#define ETDM_OUT2_CON0 (0x23a0)
+#define ETDM_OUT2_CON1 (0x23a4)
+#define ETDM_OUT2_CON2 (0x23a8)
+#define ETDM_OUT2_CON3 (0x23ac)
+#define ETDM_OUT2_CON4 (0x23b0)
+#define ETDM_OUT2_CON5 (0x23b4)
+#define ETDM_OUT2_CON6 (0x23b8)
+#define ETDM_OUT2_CON7 (0x23bc)
+#define ETDM_OUT3_CON0 (0x23c0)
+#define ETDM_OUT3_CON1 (0x23c4)
+#define ETDM_OUT3_CON2 (0x23c8)
+#define ETDM_OUT3_CON3 (0x23cc)
+#define ETDM_OUT3_CON4 (0x23d0)
+#define ETDM_OUT3_CON5 (0x23d4)
+#define ETDM_OUT3_CON6 (0x23d8)
+#define ETDM_OUT3_CON7 (0x23dc)
+#define ETDM_OUT3_CON8 (0x23e0)
+#define ETDM_OUT1_CON8 (0x23e4)
+#define ETDM_OUT2_CON8 (0x23e8)
+#define GASRC_TIMING_CON0 (0x2414)
+#define GASRC_TIMING_CON1 (0x2418)
+#define GASRC_TIMING_CON2 (0x241c)
+#define GASRC_TIMING_CON3 (0x2420)
+#define GASRC_TIMING_CON4 (0x2424)
+#define GASRC_TIMING_CON5 (0x2428)
+#define A3_A4_TIMING_SEL0 (0x2440)
+#define A3_A4_TIMING_SEL1 (0x2444)
+#define A3_A4_TIMING_SEL2 (0x2448)
+#define A3_A4_TIMING_SEL3 (0x244c)
+#define A3_A4_TIMING_SEL4 (0x2450)
+#define A3_A4_TIMING_SEL5 (0x2454)
+#define A3_A4_TIMING_SEL6 (0x2458)
+#define ASYS_TOP_DEBUG (0x2500)
+#define AFE_DPTX_CON (0x2558)
+#define AFE_DPTX_MON (0x255c)
+#define AFE_ADDA_DL_SRC2_CON0 (0x2d00)
+#define AFE_ADDA_DL_SRC2_CON1 (0x2d04)
+#define AFE_ADDA_TOP_CON0 (0x2d0c)
+#define AFE_ADDA_UL_DL_CON0 (0x2d10)
+#define AFE_ADDA_SRC_DEBUG (0x2d14)
+#define AFE_ADDA_SRC_DEBUG_MON0 (0x2d18)
+#define AFE_ADDA_SRC_DEBUG_MON1 (0x2d20)
+#define AFE_ADDA_PREDIS_CON0 (0x2d24)
+#define AFE_ADDA_PREDIS_CON1 (0x2d28)
+#define AFE_ADDA_PREDIS_CON2 (0x2d2c)
+#define AFE_ADDA_PREDIS_CON3 (0x2d30)
+#define AFE_ADDA_DL_SDM_DCCOMP_CON (0x2d34)
+#define AFE_ADDA_DL_SDM_TEST (0x2d38)
+#define AFE_ADDA_DL_DC_COMP_CFG0 (0x2d3c)
+#define AFE_ADDA_DL_DC_COMP_CFG1 (0x2d40)
+#define AFE_ADDA_DL_SDM_FIFO_MON (0x2d44)
+#define AFE_ADDA_DL_SRC_LCH_MON (0x2d50)
+#define AFE_ADDA_DL_SRC_RCH_MON (0x2d54)
+#define AFE_ADDA_DL_SDM_OUT_MON (0x2d58)
+#define AFE_ADDA_DL_SDM_DITHER_CON (0x2d5c)
+#define AFE_ADDA_DL_SDM_AUTO_RESET_CON (0x2d60)
+#define AFE_ADDA_UL_SRC_CON0 (0x2e3c)
+#define AFE_ADDA_UL_SRC_CON1 (0x2e40)
+#define AFE_CONN0 (0x3000)
+#define AFE_CONN0_1 (0x3004)
+#define AFE_CONN0_2 (0x3008)
+#define AFE_CONN0_3 (0x300c)
+#define AFE_CONN0_4 (0x3010)
+#define AFE_CONN1 (0x3014)
+#define AFE_CONN1_1 (0x3018)
+#define AFE_CONN1_2 (0x301c)
+#define AFE_CONN1_3 (0x3020)
+#define AFE_CONN1_4 (0x3024)
+#define AFE_CONN2 (0x3028)
+#define AFE_CONN2_1 (0x302c)
+#define AFE_CONN2_2 (0x3030)
+#define AFE_CONN2_3 (0x3034)
+#define AFE_CONN2_4 (0x3038)
+#define AFE_CONN3 (0x303c)
+#define AFE_CONN3_1 (0x3040)
+#define AFE_CONN3_2 (0x3044)
+#define AFE_CONN3_3 (0x3048)
+#define AFE_CONN3_4 (0x304c)
+#define AFE_CONN4 (0x3050)
+#define AFE_CONN4_1 (0x3054)
+#define AFE_CONN4_2 (0x3058)
+#define AFE_CONN4_3 (0x305c)
+#define AFE_CONN4_4 (0x3060)
+#define AFE_CONN5 (0x3064)
+#define AFE_CONN5_1 (0x3068)
+#define AFE_CONN5_2 (0x306c)
+#define AFE_CONN5_3 (0x3070)
+#define AFE_CONN5_4 (0x3074)
+#define AFE_CONN6 (0x3078)
+#define AFE_CONN6_1 (0x307c)
+#define AFE_CONN6_2 (0x3080)
+#define AFE_CONN6_3 (0x3084)
+#define AFE_CONN6_4 (0x3088)
+#define AFE_CONN7 (0x308c)
+#define AFE_CONN7_1 (0x3090)
+#define AFE_CONN7_2 (0x3094)
+#define AFE_CONN7_3 (0x3098)
+#define AFE_CONN7_4 (0x309c)
+#define AFE_CONN8 (0x30a0)
+#define AFE_CONN8_1 (0x30a4)
+#define AFE_CONN8_2 (0x30a8)
+#define AFE_CONN8_3 (0x30ac)
+#define AFE_CONN8_4 (0x30b0)
+#define AFE_CONN9 (0x30b4)
+#define AFE_CONN9_1 (0x30b8)
+#define AFE_CONN9_2 (0x30bc)
+#define AFE_CONN9_3 (0x30c0)
+#define AFE_CONN9_4 (0x30c4)
+#define AFE_CONN10 (0x30c8)
+#define AFE_CONN10_1 (0x30cc)
+#define AFE_CONN10_2 (0x30d0)
+#define AFE_CONN10_3 (0x30d4)
+#define AFE_CONN10_4 (0x30d8)
+#define AFE_CONN11 (0x30dc)
+#define AFE_CONN11_1 (0x30e0)
+#define AFE_CONN11_2 (0x30e4)
+#define AFE_CONN11_3 (0x30e8)
+#define AFE_CONN11_4 (0x30ec)
+#define AFE_CONN12 (0x30f0)
+#define AFE_CONN12_1 (0x30f4)
+#define AFE_CONN12_2 (0x30f8)
+#define AFE_CONN12_3 (0x30fc)
+#define AFE_CONN12_4 (0x3100)
+#define AFE_CONN13 (0x3104)
+#define AFE_CONN13_1 (0x3108)
+#define AFE_CONN13_2 (0x310c)
+#define AFE_CONN13_3 (0x3110)
+#define AFE_CONN13_4 (0x3114)
+#define AFE_CONN14 (0x3118)
+#define AFE_CONN14_1 (0x311c)
+#define AFE_CONN14_2 (0x3120)
+#define AFE_CONN14_3 (0x3124)
+#define AFE_CONN14_4 (0x3128)
+#define AFE_CONN15 (0x312c)
+#define AFE_CONN15_1 (0x3130)
+#define AFE_CONN15_2 (0x3134)
+#define AFE_CONN15_3 (0x3138)
+#define AFE_CONN15_4 (0x313c)
+#define AFE_CONN16 (0x3140)
+#define AFE_CONN16_1 (0x3144)
+#define AFE_CONN16_2 (0x3148)
+#define AFE_CONN16_3 (0x314c)
+#define AFE_CONN16_4 (0x3150)
+#define AFE_CONN17 (0x3154)
+#define AFE_CONN17_1 (0x3158)
+#define AFE_CONN17_2 (0x315c)
+#define AFE_CONN17_3 (0x3160)
+#define AFE_CONN17_4 (0x3164)
+#define AFE_CONN18 (0x3168)
+#define AFE_CONN18_1 (0x316c)
+#define AFE_CONN18_2 (0x3170)
+#define AFE_CONN18_3 (0x3174)
+#define AFE_CONN18_4 (0x3178)
+#define AFE_CONN19 (0x317c)
+#define AFE_CONN19_1 (0x3180)
+#define AFE_CONN19_2 (0x3184)
+#define AFE_CONN19_3 (0x3188)
+#define AFE_CONN19_4 (0x318c)
+#define AFE_CONN20 (0x3190)
+#define AFE_CONN20_1 (0x3194)
+#define AFE_CONN20_2 (0x3198)
+#define AFE_CONN20_3 (0x319c)
+#define AFE_CONN20_4 (0x31a0)
+#define AFE_CONN21 (0x31a4)
+#define AFE_CONN21_1 (0x31a8)
+#define AFE_CONN21_2 (0x31ac)
+#define AFE_CONN21_3 (0x31b0)
+#define AFE_CONN21_4 (0x31b4)
+#define AFE_CONN22 (0x31b8)
+#define AFE_CONN22_1 (0x31bc)
+#define AFE_CONN22_2 (0x31c0)
+#define AFE_CONN22_3 (0x31c4)
+#define AFE_CONN22_4 (0x31c8)
+#define AFE_CONN23 (0x31cc)
+#define AFE_CONN23_1 (0x31d0)
+#define AFE_CONN23_2 (0x31d4)
+#define AFE_CONN23_3 (0x31d8)
+#define AFE_CONN23_4 (0x31dc)
+#define AFE_CONN24 (0x31e0)
+#define AFE_CONN24_1 (0x31e4)
+#define AFE_CONN24_2 (0x31e8)
+#define AFE_CONN24_3 (0x31ec)
+#define AFE_CONN24_4 (0x31f0)
+#define AFE_CONN25 (0x31f4)
+#define AFE_CONN25_1 (0x31f8)
+#define AFE_CONN25_2 (0x31fc)
+#define AFE_CONN25_3 (0x3200)
+#define AFE_CONN25_4 (0x3204)
+#define AFE_CONN26 (0x3208)
+#define AFE_CONN26_1 (0x320c)
+#define AFE_CONN26_2 (0x3210)
+#define AFE_CONN26_3 (0x3214)
+#define AFE_CONN26_4 (0x3218)
+#define AFE_CONN27 (0x321c)
+#define AFE_CONN27_1 (0x3220)
+#define AFE_CONN27_2 (0x3224)
+#define AFE_CONN27_3 (0x3228)
+#define AFE_CONN27_4 (0x322c)
+#define AFE_CONN28 (0x3230)
+#define AFE_CONN28_1 (0x3234)
+#define AFE_CONN28_2 (0x3238)
+#define AFE_CONN28_3 (0x323c)
+#define AFE_CONN28_4 (0x3240)
+#define AFE_CONN29 (0x3244)
+#define AFE_CONN29_1 (0x3248)
+#define AFE_CONN29_2 (0x324c)
+#define AFE_CONN29_3 (0x3250)
+#define AFE_CONN29_4 (0x3254)
+#define AFE_CONN30 (0x3258)
+#define AFE_CONN30_1 (0x325c)
+#define AFE_CONN30_2 (0x3260)
+#define AFE_CONN30_3 (0x3264)
+#define AFE_CONN30_4 (0x3268)
+#define AFE_CONN31 (0x326c)
+#define AFE_CONN31_1 (0x3270)
+#define AFE_CONN31_2 (0x3274)
+#define AFE_CONN31_3 (0x3278)
+#define AFE_CONN31_4 (0x327c)
+#define AFE_CONN32 (0x3280)
+#define AFE_CONN32_1 (0x3284)
+#define AFE_CONN32_2 (0x3288)
+#define AFE_CONN32_3 (0x328c)
+#define AFE_CONN32_4 (0x3290)
+#define AFE_CONN33 (0x3294)
+#define AFE_CONN33_1 (0x3298)
+#define AFE_CONN33_2 (0x329c)
+#define AFE_CONN33_3 (0x32a0)
+#define AFE_CONN33_4 (0x32a4)
+#define AFE_CONN34 (0x32a8)
+#define AFE_CONN34_1 (0x32ac)
+#define AFE_CONN34_2 (0x32b0)
+#define AFE_CONN34_3 (0x32b4)
+#define AFE_CONN34_4 (0x32b8)
+#define AFE_CONN35 (0x32bc)
+#define AFE_CONN35_1 (0x32c0)
+#define AFE_CONN35_2 (0x32c4)
+#define AFE_CONN35_3 (0x32c8)
+#define AFE_CONN35_4 (0x32cc)
+#define AFE_CONN36 (0x32d0)
+#define AFE_CONN36_1 (0x32d4)
+#define AFE_CONN36_2 (0x32d8)
+#define AFE_CONN36_3 (0x32dc)
+#define AFE_CONN36_4 (0x32e0)
+#define AFE_CONN37 (0x32e4)
+#define AFE_CONN37_1 (0x32e8)
+#define AFE_CONN37_2 (0x32ec)
+#define AFE_CONN37_3 (0x32f0)
+#define AFE_CONN37_4 (0x32f4)
+#define AFE_CONN38 (0x32f8)
+#define AFE_CONN38_1 (0x32fc)
+#define AFE_CONN38_2 (0x3300)
+#define AFE_CONN38_3 (0x3304)
+#define AFE_CONN38_4 (0x3308)
+#define AFE_CONN39 (0x330c)
+#define AFE_CONN39_1 (0x3310)
+#define AFE_CONN39_2 (0x3314)
+#define AFE_CONN39_3 (0x3318)
+#define AFE_CONN39_4 (0x331c)
+#define AFE_CONN40 (0x3320)
+#define AFE_CONN40_1 (0x3324)
+#define AFE_CONN40_2 (0x3328)
+#define AFE_CONN40_3 (0x332c)
+#define AFE_CONN40_4 (0x3330)
+#define AFE_CONN41 (0x3334)
+#define AFE_CONN41_1 (0x3338)
+#define AFE_CONN41_2 (0x333c)
+#define AFE_CONN41_3 (0x3340)
+#define AFE_CONN41_4 (0x3344)
+#define AFE_CONN42 (0x3348)
+#define AFE_CONN42_1 (0x334c)
+#define AFE_CONN42_2 (0x3350)
+#define AFE_CONN42_3 (0x3354)
+#define AFE_CONN42_4 (0x3358)
+#define AFE_CONN43 (0x335c)
+#define AFE_CONN43_1 (0x3360)
+#define AFE_CONN43_2 (0x3364)
+#define AFE_CONN43_3 (0x3368)
+#define AFE_CONN43_4 (0x336c)
+#define AFE_CONN44 (0x3370)
+#define AFE_CONN44_1 (0x3374)
+#define AFE_CONN44_2 (0x3378)
+#define AFE_CONN44_3 (0x337c)
+#define AFE_CONN44_4 (0x3380)
+#define AFE_CONN45 (0x3384)
+#define AFE_CONN45_1 (0x3388)
+#define AFE_CONN45_2 (0x338c)
+#define AFE_CONN45_3 (0x3390)
+#define AFE_CONN45_4 (0x3394)
+#define AFE_CONN46 (0x3398)
+#define AFE_CONN46_1 (0x339c)
+#define AFE_CONN46_2 (0x33a0)
+#define AFE_CONN46_3 (0x33a4)
+#define AFE_CONN46_4 (0x33a8)
+#define AFE_CONN47 (0x33ac)
+#define AFE_CONN47_1 (0x33b0)
+#define AFE_CONN47_2 (0x33b4)
+#define AFE_CONN47_3 (0x33b8)
+#define AFE_CONN47_4 (0x33bc)
+#define AFE_CONN48 (0x33c0)
+#define AFE_CONN48_1 (0x33c4)
+#define AFE_CONN48_2 (0x33c8)
+#define AFE_CONN48_3 (0x33cc)
+#define AFE_CONN48_4 (0x33d0)
+#define AFE_CONN49 (0x33d4)
+#define AFE_CONN49_1 (0x33d8)
+#define AFE_CONN49_2 (0x33dc)
+#define AFE_CONN49_3 (0x33e0)
+#define AFE_CONN49_4 (0x33e4)
+#define AFE_CONN50 (0x33e8)
+#define AFE_CONN50_1 (0x33ec)
+#define AFE_CONN50_2 (0x33f0)
+#define AFE_CONN50_3 (0x33f4)
+#define AFE_CONN50_4 (0x33f8)
+#define AFE_CONN51 (0x33fc)
+#define AFE_CONN51_1 (0x3400)
+#define AFE_CONN51_2 (0x3404)
+#define AFE_CONN51_3 (0x3408)
+#define AFE_CONN51_4 (0x340c)
+#define AFE_CONN52 (0x3410)
+#define AFE_CONN52_1 (0x3414)
+#define AFE_CONN52_2 (0x3418)
+#define AFE_CONN52_3 (0x341c)
+#define AFE_CONN52_4 (0x3420)
+#define AFE_CONN53 (0x3424)
+#define AFE_CONN53_1 (0x3428)
+#define AFE_CONN53_2 (0x342c)
+#define AFE_CONN53_3 (0x3430)
+#define AFE_CONN53_4 (0x3434)
+#define AFE_CONN54 (0x3438)
+#define AFE_CONN54_1 (0x343c)
+#define AFE_CONN54_2 (0x3440)
+#define AFE_CONN54_3 (0x3444)
+#define AFE_CONN54_4 (0x3448)
+#define AFE_CONN55 (0x344c)
+#define AFE_CONN55_1 (0x3450)
+#define AFE_CONN55_2 (0x3454)
+#define AFE_CONN55_3 (0x3458)
+#define AFE_CONN55_4 (0x345c)
+#define AFE_CONN56 (0x3460)
+#define AFE_CONN56_1 (0x3464)
+#define AFE_CONN56_2 (0x3468)
+#define AFE_CONN56_3 (0x346c)
+#define AFE_CONN56_4 (0x3470)
+#define AFE_CONN57 (0x3474)
+#define AFE_CONN57_1 (0x3478)
+#define AFE_CONN57_2 (0x347c)
+#define AFE_CONN57_3 (0x3480)
+#define AFE_CONN57_4 (0x3484)
+#define AFE_CONN58 (0x3488)
+#define AFE_CONN58_1 (0x348c)
+#define AFE_CONN58_2 (0x3490)
+#define AFE_CONN58_3 (0x3494)
+#define AFE_CONN58_4 (0x3498)
+#define AFE_CONN59 (0x349c)
+#define AFE_CONN59_1 (0x34a0)
+#define AFE_CONN59_2 (0x34a4)
+#define AFE_CONN59_3 (0x34a8)
+#define AFE_CONN59_4 (0x34ac)
+#define AFE_CONN60 (0x34b0)
+#define AFE_CONN60_1 (0x34b4)
+#define AFE_CONN60_2 (0x34b8)
+#define AFE_CONN60_3 (0x34bc)
+#define AFE_CONN60_4 (0x34c0)
+#define AFE_CONN61 (0x34c4)
+#define AFE_CONN61_1 (0x34c8)
+#define AFE_CONN61_2 (0x34cc)
+#define AFE_CONN61_3 (0x34d0)
+#define AFE_CONN61_4 (0x34d4)
+#define AFE_CONN62 (0x34d8)
+#define AFE_CONN62_1 (0x34dc)
+#define AFE_CONN62_2 (0x34e0)
+#define AFE_CONN62_3 (0x34e4)
+#define AFE_CONN62_4 (0x34e8)
+#define AFE_CONN63 (0x34ec)
+#define AFE_CONN63_1 (0x34f0)
+#define AFE_CONN63_2 (0x34f4)
+#define AFE_CONN63_3 (0x34f8)
+#define AFE_CONN63_4 (0x34fc)
+#define AFE_CONN64 (0x3500)
+#define AFE_CONN64_1 (0x3504)
+#define AFE_CONN64_2 (0x3508)
+#define AFE_CONN64_3 (0x350c)
+#define AFE_CONN64_4 (0x3510)
+#define AFE_CONN65 (0x3514)
+#define AFE_CONN65_1 (0x3518)
+#define AFE_CONN65_2 (0x351c)
+#define AFE_CONN65_3 (0x3520)
+#define AFE_CONN65_4 (0x3524)
+#define AFE_CONN66 (0x3528)
+#define AFE_CONN66_1 (0x352c)
+#define AFE_CONN66_2 (0x3530)
+#define AFE_CONN66_3 (0x3534)
+#define AFE_CONN66_4 (0x3538)
+#define AFE_CONN67 (0x353c)
+#define AFE_CONN67_1 (0x3540)
+#define AFE_CONN67_2 (0x3544)
+#define AFE_CONN67_3 (0x3548)
+#define AFE_CONN67_4 (0x354c)
+#define AFE_CONN68 (0x3550)
+#define AFE_CONN68_1 (0x3554)
+#define AFE_CONN68_2 (0x3558)
+#define AFE_CONN68_3 (0x355c)
+#define AFE_CONN68_4 (0x3560)
+#define AFE_CONN69 (0x3564)
+#define AFE_CONN69_1 (0x3568)
+#define AFE_CONN69_2 (0x356c)
+#define AFE_CONN69_3 (0x3570)
+#define AFE_CONN69_4 (0x3574)
+#define AFE_CONN70 (0x3578)
+#define AFE_CONN70_1 (0x357c)
+#define AFE_CONN70_2 (0x3580)
+#define AFE_CONN70_3 (0x3584)
+#define AFE_CONN70_4 (0x3588)
+#define AFE_CONN71 (0x358c)
+#define AFE_CONN71_1 (0x3590)
+#define AFE_CONN71_2 (0x3594)
+#define AFE_CONN71_3 (0x3598)
+#define AFE_CONN71_4 (0x359c)
+#define AFE_CONN72 (0x35a0)
+#define AFE_CONN72_1 (0x35a4)
+#define AFE_CONN72_2 (0x35a8)
+#define AFE_CONN72_3 (0x35ac)
+#define AFE_CONN72_4 (0x35b0)
+#define AFE_CONN73 (0x35b4)
+#define AFE_CONN73_1 (0x35b8)
+#define AFE_CONN73_2 (0x35bc)
+#define AFE_CONN73_3 (0x35c0)
+#define AFE_CONN73_4 (0x35c4)
+#define AFE_CONN74 (0x35c8)
+#define AFE_CONN74_1 (0x35cc)
+#define AFE_CONN74_2 (0x35d0)
+#define AFE_CONN74_3 (0x35d4)
+#define AFE_CONN74_4 (0x35d8)
+#define AFE_CONN75 (0x35dc)
+#define AFE_CONN75_1 (0x35e0)
+#define AFE_CONN75_2 (0x35e4)
+#define AFE_CONN75_3 (0x35e8)
+#define AFE_CONN75_4 (0x35ec)
+#define AFE_CONN76 (0x35f0)
+#define AFE_CONN76_1 (0x35f4)
+#define AFE_CONN76_2 (0x35f8)
+#define AFE_CONN76_3 (0x35fc)
+#define AFE_CONN76_4 (0x3600)
+#define AFE_CONN77 (0x3604)
+#define AFE_CONN77_1 (0x3608)
+#define AFE_CONN77_2 (0x360c)
+#define AFE_CONN77_3 (0x3610)
+#define AFE_CONN77_4 (0x3614)
+#define AFE_CONN78 (0x3618)
+#define AFE_CONN78_1 (0x361c)
+#define AFE_CONN78_2 (0x3620)
+#define AFE_CONN78_3 (0x3624)
+#define AFE_CONN78_4 (0x3628)
+#define AFE_CONN79 (0x362c)
+#define AFE_CONN79_1 (0x3630)
+#define AFE_CONN79_2 (0x3634)
+#define AFE_CONN79_3 (0x3638)
+#define AFE_CONN79_4 (0x363c)
+#define AFE_CONN80 (0x3640)
+#define AFE_CONN80_1 (0x3644)
+#define AFE_CONN80_2 (0x3648)
+#define AFE_CONN80_3 (0x364c)
+#define AFE_CONN80_4 (0x3650)
+#define AFE_CONN81 (0x3654)
+#define AFE_CONN81_1 (0x3658)
+#define AFE_CONN81_2 (0x365c)
+#define AFE_CONN81_3 (0x3660)
+#define AFE_CONN81_4 (0x3664)
+#define AFE_CONN82 (0x3668)
+#define AFE_CONN82_1 (0x366c)
+#define AFE_CONN82_2 (0x3670)
+#define AFE_CONN82_3 (0x3674)
+#define AFE_CONN82_4 (0x3678)
+#define AFE_CONN83 (0x367c)
+#define AFE_CONN83_1 (0x3680)
+#define AFE_CONN83_2 (0x3684)
+#define AFE_CONN83_3 (0x3688)
+#define AFE_CONN83_4 (0x368c)
+#define AFE_CONN84 (0x3690)
+#define AFE_CONN84_1 (0x3694)
+#define AFE_CONN84_2 (0x3698)
+#define AFE_CONN84_3 (0x369c)
+#define AFE_CONN84_4 (0x36a0)
+#define AFE_CONN85 (0x36a4)
+#define AFE_CONN85_1 (0x36a8)
+#define AFE_CONN85_2 (0x36ac)
+#define AFE_CONN85_3 (0x36b0)
+#define AFE_CONN85_4 (0x36b4)
+#define AFE_CONN86 (0x36b8)
+#define AFE_CONN86_1 (0x36bc)
+#define AFE_CONN86_2 (0x36c0)
+#define AFE_CONN86_3 (0x36c4)
+#define AFE_CONN86_4 (0x36c8)
+#define AFE_CONN87 (0x36cc)
+#define AFE_CONN87_1 (0x36d0)
+#define AFE_CONN87_2 (0x36d4)
+#define AFE_CONN87_3 (0x36d8)
+#define AFE_CONN87_4 (0x36dc)
+#define AFE_CONN88 (0x36e0)
+#define AFE_CONN88_1 (0x36e4)
+#define AFE_CONN88_2 (0x36e8)
+#define AFE_CONN88_3 (0x36ec)
+#define AFE_CONN88_4 (0x36f0)
+#define AFE_CONN89 (0x36f4)
+#define AFE_CONN89_1 (0x36f8)
+#define AFE_CONN89_2 (0x36fc)
+#define AFE_CONN89_3 (0x3700)
+#define AFE_CONN89_4 (0x3704)
+#define AFE_CONN90 (0x3708)
+#define AFE_CONN90_1 (0x370c)
+#define AFE_CONN90_2 (0x3710)
+#define AFE_CONN90_3 (0x3714)
+#define AFE_CONN90_4 (0x3718)
+#define AFE_CONN91 (0x371c)
+#define AFE_CONN91_1 (0x3720)
+#define AFE_CONN91_2 (0x3724)
+#define AFE_CONN91_3 (0x3728)
+#define AFE_CONN91_4 (0x372c)
+#define AFE_CONN92 (0x3730)
+#define AFE_CONN92_1 (0x3734)
+#define AFE_CONN92_2 (0x3738)
+#define AFE_CONN92_3 (0x373c)
+#define AFE_CONN92_4 (0x3740)
+#define AFE_CONN93 (0x3744)
+#define AFE_CONN93_1 (0x3748)
+#define AFE_CONN93_2 (0x374c)
+#define AFE_CONN93_3 (0x3750)
+#define AFE_CONN93_4 (0x3754)
+#define AFE_CONN94 (0x3758)
+#define AFE_CONN94_1 (0x375c)
+#define AFE_CONN94_2 (0x3760)
+#define AFE_CONN94_3 (0x3764)
+#define AFE_CONN94_4 (0x3768)
+#define AFE_CONN95 (0x376c)
+#define AFE_CONN95_1 (0x3770)
+#define AFE_CONN95_2 (0x3774)
+#define AFE_CONN95_3 (0x3778)
+#define AFE_CONN95_4 (0x377c)
+#define AFE_CONN96 (0x3780)
+#define AFE_CONN96_1 (0x3784)
+#define AFE_CONN96_2 (0x3788)
+#define AFE_CONN96_3 (0x378c)
+#define AFE_CONN96_4 (0x3790)
+#define AFE_CONN97 (0x3794)
+#define AFE_CONN97_1 (0x3798)
+#define AFE_CONN97_2 (0x379c)
+#define AFE_CONN97_3 (0x37a0)
+#define AFE_CONN97_4 (0x37a4)
+#define AFE_CONN98 (0x37a8)
+#define AFE_CONN98_1 (0x37ac)
+#define AFE_CONN98_2 (0x37b0)
+#define AFE_CONN98_3 (0x37b4)
+#define AFE_CONN98_4 (0x37b8)
+#define AFE_CONN99 (0x37bc)
+#define AFE_CONN99_1 (0x37c0)
+#define AFE_CONN99_2 (0x37c4)
+#define AFE_CONN99_3 (0x37c8)
+#define AFE_CONN99_4 (0x37cc)
+#define AFE_CONN100 (0x37d0)
+#define AFE_CONN100_1 (0x37d4)
+#define AFE_CONN100_2 (0x37d8)
+#define AFE_CONN100_3 (0x37dc)
+#define AFE_CONN100_4 (0x37e0)
+#define AFE_CONN101 (0x37e4)
+#define AFE_CONN101_1 (0x37e8)
+#define AFE_CONN101_2 (0x37ec)
+#define AFE_CONN101_3 (0x37f0)
+#define AFE_CONN101_4 (0x37f4)
+#define AFE_CONN102 (0x37f8)
+#define AFE_CONN102_1 (0x37fc)
+#define AFE_CONN102_2 (0x3800)
+#define AFE_CONN102_3 (0x3804)
+#define AFE_CONN102_4 (0x3808)
+#define AFE_CONN103 (0x380c)
+#define AFE_CONN103_1 (0x3810)
+#define AFE_CONN103_2 (0x3814)
+#define AFE_CONN103_3 (0x3818)
+#define AFE_CONN103_4 (0x381c)
+#define AFE_CONN104 (0x3820)
+#define AFE_CONN104_1 (0x3824)
+#define AFE_CONN104_2 (0x3828)
+#define AFE_CONN104_3 (0x382c)
+#define AFE_CONN104_4 (0x3830)
+#define AFE_CONN105 (0x3834)
+#define AFE_CONN105_1 (0x3838)
+#define AFE_CONN105_2 (0x383c)
+#define AFE_CONN105_3 (0x3840)
+#define AFE_CONN105_4 (0x3844)
+#define AFE_CONN106 (0x3848)
+#define AFE_CONN106_1 (0x384c)
+#define AFE_CONN106_2 (0x3850)
+#define AFE_CONN106_3 (0x3854)
+#define AFE_CONN106_4 (0x3858)
+#define AFE_CONN107 (0x385c)
+#define AFE_CONN107_1 (0x3860)
+#define AFE_CONN107_2 (0x3864)
+#define AFE_CONN107_3 (0x3868)
+#define AFE_CONN107_4 (0x386c)
+#define AFE_CONN108 (0x3870)
+#define AFE_CONN108_1 (0x3874)
+#define AFE_CONN108_2 (0x3878)
+#define AFE_CONN108_3 (0x387c)
+#define AFE_CONN108_4 (0x3880)
+#define AFE_CONN109 (0x3884)
+#define AFE_CONN109_1 (0x3888)
+#define AFE_CONN109_2 (0x388c)
+#define AFE_CONN109_3 (0x3890)
+#define AFE_CONN109_4 (0x3894)
+#define AFE_CONN110 (0x3898)
+#define AFE_CONN110_1 (0x389c)
+#define AFE_CONN110_2 (0x38a0)
+#define AFE_CONN110_3 (0x38a4)
+#define AFE_CONN110_4 (0x38a8)
+#define AFE_CONN111 (0x38ac)
+#define AFE_CONN111_1 (0x38b0)
+#define AFE_CONN111_2 (0x38b4)
+#define AFE_CONN111_3 (0x38b8)
+#define AFE_CONN111_4 (0x38bc)
+#define AFE_CONN112 (0x38c0)
+#define AFE_CONN112_1 (0x38c4)
+#define AFE_CONN112_2 (0x38c8)
+#define AFE_CONN112_3 (0x38cc)
+#define AFE_CONN112_4 (0x38d0)
+#define AFE_CONN113 (0x38d4)
+#define AFE_CONN113_1 (0x38d8)
+#define AFE_CONN113_2 (0x38dc)
+#define AFE_CONN113_3 (0x38e0)
+#define AFE_CONN113_4 (0x38e4)
+#define AFE_CONN114 (0x38e8)
+#define AFE_CONN114_1 (0x38ec)
+#define AFE_CONN114_2 (0x38f0)
+#define AFE_CONN114_3 (0x38f4)
+#define AFE_CONN114_4 (0x38f8)
+#define AFE_CONN115 (0x38fc)
+#define AFE_CONN115_1 (0x3900)
+#define AFE_CONN115_2 (0x3904)
+#define AFE_CONN115_3 (0x3908)
+#define AFE_CONN115_4 (0x390c)
+#define AFE_CONN116 (0x3910)
+#define AFE_CONN116_1 (0x3914)
+#define AFE_CONN116_2 (0x3918)
+#define AFE_CONN116_3 (0x391c)
+#define AFE_CONN116_4 (0x3920)
+#define AFE_CONN117 (0x3924)
+#define AFE_CONN117_1 (0x3928)
+#define AFE_CONN117_2 (0x392c)
+#define AFE_CONN117_3 (0x3930)
+#define AFE_CONN117_4 (0x3934)
+#define AFE_CONN118 (0x3938)
+#define AFE_CONN118_1 (0x393c)
+#define AFE_CONN118_2 (0x3940)
+#define AFE_CONN118_3 (0x3944)
+#define AFE_CONN118_4 (0x3948)
+#define AFE_CONN119 (0x394c)
+#define AFE_CONN119_1 (0x3950)
+#define AFE_CONN119_2 (0x3954)
+#define AFE_CONN119_3 (0x3958)
+#define AFE_CONN119_4 (0x395c)
+#define AFE_CONN120 (0x3960)
+#define AFE_CONN120_1 (0x3964)
+#define AFE_CONN120_2 (0x3968)
+#define AFE_CONN120_3 (0x396c)
+#define AFE_CONN120_4 (0x3970)
+#define AFE_CONN121 (0x3974)
+#define AFE_CONN121_1 (0x3978)
+#define AFE_CONN121_2 (0x397c)
+#define AFE_CONN121_3 (0x3980)
+#define AFE_CONN121_4 (0x3984)
+#define AFE_CONN122 (0x3988)
+#define AFE_CONN122_1 (0x398c)
+#define AFE_CONN122_2 (0x3990)
+#define AFE_CONN122_3 (0x3994)
+#define AFE_CONN122_4 (0x3998)
+#define AFE_CONN123 (0x399c)
+#define AFE_CONN123_1 (0x39a0)
+#define AFE_CONN123_2 (0x39a4)
+#define AFE_CONN123_3 (0x39a8)
+#define AFE_CONN123_4 (0x39ac)
+#define AFE_CONN124 (0x39b0)
+#define AFE_CONN124_1 (0x39b4)
+#define AFE_CONN124_2 (0x39b8)
+#define AFE_CONN124_3 (0x39bc)
+#define AFE_CONN124_4 (0x39c0)
+#define AFE_CONN125 (0x39c4)
+#define AFE_CONN125_1 (0x39c8)
+#define AFE_CONN125_2 (0x39cc)
+#define AFE_CONN125_3 (0x39d0)
+#define AFE_CONN125_4 (0x39d4)
+#define AFE_CONN126 (0x39d8)
+#define AFE_CONN126_1 (0x39dc)
+#define AFE_CONN126_2 (0x39e0)
+#define AFE_CONN126_3 (0x39e4)
+#define AFE_CONN126_4 (0x39e8)
+#define AFE_CONN127 (0x39ec)
+#define AFE_CONN127_1 (0x39f0)
+#define AFE_CONN127_2 (0x39f4)
+#define AFE_CONN127_3 (0x39f8)
+#define AFE_CONN127_4 (0x39fc)
+#define AFE_CONN128 (0x3a00)
+#define AFE_CONN128_1 (0x3a04)
+#define AFE_CONN128_2 (0x3a08)
+#define AFE_CONN128_3 (0x3a0c)
+#define AFE_CONN128_4 (0x3a10)
+#define AFE_CONN129 (0x3a14)
+#define AFE_CONN129_1 (0x3a18)
+#define AFE_CONN129_2 (0x3a1c)
+#define AFE_CONN129_3 (0x3a20)
+#define AFE_CONN129_4 (0x3a24)
+#define AFE_CONN130 (0x3a28)
+#define AFE_CONN130_1 (0x3a2c)
+#define AFE_CONN130_2 (0x3a30)
+#define AFE_CONN130_3 (0x3a34)
+#define AFE_CONN130_4 (0x3a38)
+#define AFE_CONN131 (0x3a3c)
+#define AFE_CONN131_1 (0x3a40)
+#define AFE_CONN131_2 (0x3a44)
+#define AFE_CONN131_3 (0x3a48)
+#define AFE_CONN131_4 (0x3a4c)
+#define AFE_CONN132 (0x3a50)
+#define AFE_CONN132_1 (0x3a54)
+#define AFE_CONN132_2 (0x3a58)
+#define AFE_CONN132_3 (0x3a5c)
+#define AFE_CONN132_4 (0x3a60)
+#define AFE_CONN133 (0x3a64)
+#define AFE_CONN133_1 (0x3a68)
+#define AFE_CONN133_2 (0x3a6c)
+#define AFE_CONN133_3 (0x3a70)
+#define AFE_CONN133_4 (0x3a74)
+#define AFE_CONN134 (0x3a78)
+#define AFE_CONN134_1 (0x3a7c)
+#define AFE_CONN134_2 (0x3a80)
+#define AFE_CONN134_3 (0x3a84)
+#define AFE_CONN134_4 (0x3a88)
+#define AFE_CONN135 (0x3a8c)
+#define AFE_CONN135_1 (0x3a90)
+#define AFE_CONN135_2 (0x3a94)
+#define AFE_CONN135_3 (0x3a98)
+#define AFE_CONN135_4 (0x3a9c)
+#define AFE_CONN136 (0x3aa0)
+#define AFE_CONN136_1 (0x3aa4)
+#define AFE_CONN136_2 (0x3aa8)
+#define AFE_CONN136_3 (0x3aac)
+#define AFE_CONN136_4 (0x3ab0)
+#define AFE_CONN137 (0x3ab4)
+#define AFE_CONN137_1 (0x3ab8)
+#define AFE_CONN137_2 (0x3abc)
+#define AFE_CONN137_3 (0x3ac0)
+#define AFE_CONN137_4 (0x3ac4)
+#define AFE_CONN138 (0x3ac8)
+#define AFE_CONN138_1 (0x3acc)
+#define AFE_CONN138_2 (0x3ad0)
+#define AFE_CONN138_3 (0x3ad4)
+#define AFE_CONN138_4 (0x3ad8)
+#define AFE_CONN139 (0x3adc)
+#define AFE_CONN139_1 (0x3ae0)
+#define AFE_CONN139_2 (0x3ae4)
+#define AFE_CONN139_3 (0x3ae8)
+#define AFE_CONN139_4 (0x3aec)
+#define AFE_CONN_RS (0x3af0)
+#define AFE_CONN_RS_1 (0x3af4)
+#define AFE_CONN_RS_2 (0x3af8)
+#define AFE_CONN_RS_3 (0x3afc)
+#define AFE_CONN_RS_4 (0x3b00)
+#define AFE_CONN_16BIT (0x3b04)
+#define AFE_CONN_16BIT_1 (0x3b08)
+#define AFE_CONN_16BIT_2 (0x3b0c)
+#define AFE_CONN_16BIT_3 (0x3b10)
+#define AFE_CONN_16BIT_4 (0x3b14)
+#define AFE_CONN_24BIT (0x3b18)
+#define AFE_CONN_24BIT_1 (0x3b1c)
+#define AFE_CONN_24BIT_2 (0x3b20)
+#define AFE_CONN_24BIT_3 (0x3b24)
+#define AFE_CONN_24BIT_4 (0x3b28)
+#define AFE_CONN_DI (0x3b2c)
+#define AFE_CONN_DI_1 (0x3b30)
+#define AFE_CONN_DI_2 (0x3b34)
+#define AFE_CONN_DI_3 (0x3b38)
+#define AFE_CONN_DI_4 (0x3b3c)
+#define AFE_CONN176 (0x3ea0)
+#define AFE_CONN176_1 (0x3ea4)
+#define AFE_CONN176_2 (0x3ea8)
+#define AFE_CONN176_3 (0x3eac)
+#define AFE_CONN176_4 (0x3eb0)
+#define AFE_CONN176_5 (0x3eb4)
+#define AFE_CONN177 (0x3eb8)
+#define AFE_CONN177_1 (0x3ebc)
+#define AFE_CONN177_2 (0x3ec0)
+#define AFE_CONN177_3 (0x3ec4)
+#define AFE_CONN177_4 (0x3ec8)
+#define AFE_CONN177_5 (0x3ecc)
+#define AFE_CONN182 (0x3f30)
+#define AFE_CONN182_1 (0x3f34)
+#define AFE_CONN182_2 (0x3f38)
+#define AFE_CONN182_3 (0x3f3c)
+#define AFE_CONN182_4 (0x3f40)
+#define AFE_CONN182_5 (0x3f44)
+#define AFE_CONN183 (0x3f48)
+#define AFE_CONN183_1 (0x3f4c)
+#define AFE_CONN183_2 (0x3f50)
+#define AFE_CONN183_3 (0x3f54)
+#define AFE_CONN183_4 (0x3f58)
+#define AFE_CONN183_5 (0x3f5c)
+#define AFE_SECURE_MASK_CONN0 (0x4000)
+#define AFE_SECURE_MASK_CONN0_1 (0x4004)
+#define AFE_SECURE_MASK_CONN0_2 (0x4008)
+#define AFE_SECURE_MASK_CONN0_3 (0x400c)
+#define AFE_SECURE_MASK_CONN0_4 (0x4010)
+#define AFE_SECURE_MASK_CONN1 (0x4014)
+#define AFE_SECURE_MASK_CONN1_1 (0x4018)
+#define AFE_SECURE_MASK_CONN1_2 (0x401c)
+#define AFE_SECURE_MASK_CONN1_3 (0x4020)
+#define AFE_SECURE_MASK_CONN1_4 (0x4024)
+#define AFE_SECURE_MASK_CONN2 (0x4028)
+#define AFE_SECURE_MASK_CONN2_1 (0x402c)
+#define AFE_SECURE_MASK_CONN2_2 (0x4030)
+#define AFE_SECURE_MASK_CONN2_3 (0x4034)
+#define AFE_SECURE_MASK_CONN2_4 (0x4038)
+#define AFE_SECURE_MASK_CONN3 (0x403c)
+#define AFE_SECURE_MASK_CONN3_1 (0x4040)
+#define AFE_SECURE_MASK_CONN3_2 (0x4044)
+#define AFE_SECURE_MASK_CONN3_3 (0x4048)
+#define AFE_SECURE_MASK_CONN3_4 (0x404c)
+#define AFE_SECURE_MASK_CONN4 (0x4050)
+#define AFE_SECURE_MASK_CONN4_1 (0x4054)
+#define AFE_SECURE_MASK_CONN4_2 (0x4058)
+#define AFE_SECURE_MASK_CONN4_3 (0x405c)
+#define AFE_SECURE_MASK_CONN4_4 (0x4060)
+#define AFE_SECURE_MASK_CONN5 (0x4064)
+#define AFE_SECURE_MASK_CONN5_1 (0x4068)
+#define AFE_SECURE_MASK_CONN5_2 (0x406c)
+#define AFE_SECURE_MASK_CONN5_3 (0x4070)
+#define AFE_SECURE_MASK_CONN5_4 (0x4074)
+#define AFE_SECURE_MASK_CONN6 (0x4078)
+#define AFE_SECURE_MASK_CONN6_1 (0x407c)
+#define AFE_SECURE_MASK_CONN6_2 (0x4080)
+#define AFE_SECURE_MASK_CONN6_3 (0x4084)
+#define AFE_SECURE_MASK_CONN6_4 (0x4088)
+#define AFE_SECURE_MASK_CONN7 (0x408c)
+#define AFE_SECURE_MASK_CONN7_1 (0x4090)
+#define AFE_SECURE_MASK_CONN7_2 (0x4094)
+#define AFE_SECURE_MASK_CONN7_3 (0x4098)
+#define AFE_SECURE_MASK_CONN7_4 (0x409c)
+#define AFE_SECURE_MASK_CONN8 (0x40a0)
+#define AFE_SECURE_MASK_CONN8_1 (0x40a4)
+#define AFE_SECURE_MASK_CONN8_2 (0x40a8)
+#define AFE_SECURE_MASK_CONN8_3 (0x40ac)
+#define AFE_SECURE_MASK_CONN8_4 (0x40b0)
+#define AFE_SECURE_MASK_CONN9 (0x40b4)
+#define AFE_SECURE_MASK_CONN9_1 (0x40b8)
+#define AFE_SECURE_MASK_CONN9_2 (0x40bc)
+#define AFE_SECURE_MASK_CONN9_3 (0x40c0)
+#define AFE_SECURE_MASK_CONN9_4 (0x40c4)
+#define AFE_SECURE_MASK_CONN10 (0x40c8)
+#define AFE_SECURE_MASK_CONN10_1 (0x40cc)
+#define AFE_SECURE_MASK_CONN10_2 (0x40d0)
+#define AFE_SECURE_MASK_CONN10_3 (0x40d4)
+#define AFE_SECURE_MASK_CONN10_4 (0x40d8)
+#define AFE_SECURE_MASK_CONN11 (0x40dc)
+#define AFE_SECURE_MASK_CONN11_1 (0x40e0)
+#define AFE_SECURE_MASK_CONN11_2 (0x40e4)
+#define AFE_SECURE_MASK_CONN11_3 (0x40e8)
+#define AFE_SECURE_MASK_CONN11_4 (0x40ec)
+#define AFE_SECURE_MASK_CONN12 (0x40f0)
+#define AFE_SECURE_MASK_CONN12_1 (0x40f4)
+#define AFE_SECURE_MASK_CONN12_2 (0x40f8)
+#define AFE_SECURE_MASK_CONN12_3 (0x40fc)
+#define AFE_SECURE_MASK_CONN12_4 (0x4100)
+#define AFE_SECURE_MASK_CONN13 (0x4104)
+#define AFE_SECURE_MASK_CONN13_1 (0x4108)
+#define AFE_SECURE_MASK_CONN13_2 (0x410c)
+#define AFE_SECURE_MASK_CONN13_3 (0x4110)
+#define AFE_SECURE_MASK_CONN13_4 (0x4114)
+#define AFE_SECURE_MASK_CONN14 (0x4118)
+#define AFE_SECURE_MASK_CONN14_1 (0x411c)
+#define AFE_SECURE_MASK_CONN14_2 (0x4120)
+#define AFE_SECURE_MASK_CONN14_3 (0x4124)
+#define AFE_SECURE_MASK_CONN14_4 (0x4128)
+#define AFE_SECURE_MASK_CONN15 (0x412c)
+#define AFE_SECURE_MASK_CONN15_1 (0x4130)
+#define AFE_SECURE_MASK_CONN15_2 (0x4134)
+#define AFE_SECURE_MASK_CONN15_3 (0x4138)
+#define AFE_SECURE_MASK_CONN15_4 (0x413c)
+#define AFE_SECURE_MASK_CONN16 (0x4140)
+#define AFE_SECURE_MASK_CONN16_1 (0x4144)
+#define AFE_SECURE_MASK_CONN16_2 (0x4148)
+#define AFE_SECURE_MASK_CONN16_3 (0x414c)
+#define AFE_SECURE_MASK_CONN16_4 (0x4150)
+#define AFE_SECURE_MASK_CONN17 (0x4154)
+#define AFE_SECURE_MASK_CONN17_1 (0x4158)
+#define AFE_SECURE_MASK_CONN17_2 (0x415c)
+#define AFE_SECURE_MASK_CONN17_3 (0x4160)
+#define AFE_SECURE_MASK_CONN17_4 (0x4164)
+#define AFE_SECURE_MASK_CONN18 (0x4168)
+#define AFE_SECURE_MASK_CONN18_1 (0x416c)
+#define AFE_SECURE_MASK_CONN18_2 (0x4170)
+#define AFE_SECURE_MASK_CONN18_3 (0x4174)
+#define AFE_SECURE_MASK_CONN18_4 (0x4178)
+#define AFE_SECURE_MASK_CONN19 (0x417c)
+#define AFE_SECURE_MASK_CONN19_1 (0x4180)
+#define AFE_SECURE_MASK_CONN19_2 (0x4184)
+#define AFE_SECURE_MASK_CONN19_3 (0x4188)
+#define AFE_SECURE_MASK_CONN19_4 (0x418c)
+#define AFE_SECURE_MASK_CONN20 (0x4190)
+#define AFE_SECURE_MASK_CONN20_1 (0x4194)
+#define AFE_SECURE_MASK_CONN20_2 (0x4198)
+#define AFE_SECURE_MASK_CONN20_3 (0x419c)
+#define AFE_SECURE_MASK_CONN20_4 (0x41a0)
+#define AFE_SECURE_MASK_CONN21 (0x41a4)
+#define AFE_SECURE_MASK_CONN21_1 (0x41a8)
+#define AFE_SECURE_MASK_CONN21_2 (0x41ac)
+#define AFE_SECURE_MASK_CONN21_3 (0x41b0)
+#define AFE_SECURE_MASK_CONN21_4 (0x41b4)
+#define AFE_SECURE_MASK_CONN22 (0x41b8)
+#define AFE_SECURE_MASK_CONN22_1 (0x41bc)
+#define AFE_SECURE_MASK_CONN22_2 (0x41c0)
+#define AFE_SECURE_MASK_CONN22_3 (0x41c4)
+#define AFE_SECURE_MASK_CONN22_4 (0x41c8)
+#define AFE_SECURE_MASK_CONN23 (0x41cc)
+#define AFE_SECURE_MASK_CONN23_1 (0x41d0)
+#define AFE_SECURE_MASK_CONN23_2 (0x41d4)
+#define AFE_SECURE_MASK_CONN23_3 (0x41d8)
+#define AFE_SECURE_MASK_CONN23_4 (0x41dc)
+#define AFE_SECURE_MASK_CONN24 (0x41e0)
+#define AFE_SECURE_MASK_CONN24_1 (0x41e4)
+#define AFE_SECURE_MASK_CONN24_2 (0x41e8)
+#define AFE_SECURE_MASK_CONN24_3 (0x41ec)
+#define AFE_SECURE_MASK_CONN24_4 (0x41f0)
+#define AFE_SECURE_MASK_CONN25 (0x41f4)
+#define AFE_SECURE_MASK_CONN25_1 (0x41f8)
+#define AFE_SECURE_MASK_CONN25_2 (0x41fc)
+#define AFE_SECURE_MASK_CONN25_3 (0x4200)
+#define AFE_SECURE_MASK_CONN25_4 (0x4204)
+#define AFE_SECURE_MASK_CONN26 (0x4208)
+#define AFE_SECURE_MASK_CONN26_1 (0x420c)
+#define AFE_SECURE_MASK_CONN26_2 (0x4210)
+#define AFE_SECURE_MASK_CONN26_3 (0x4214)
+#define AFE_SECURE_MASK_CONN26_4 (0x4218)
+#define AFE_SECURE_MASK_CONN27 (0x421c)
+#define AFE_SECURE_MASK_CONN27_1 (0x4220)
+#define AFE_SECURE_MASK_CONN27_2 (0x4224)
+#define AFE_SECURE_MASK_CONN27_3 (0x4228)
+#define AFE_SECURE_MASK_CONN27_4 (0x422c)
+#define AFE_SECURE_MASK_CONN28 (0x4230)
+#define AFE_SECURE_MASK_CONN28_1 (0x4234)
+#define AFE_SECURE_MASK_CONN28_2 (0x4238)
+#define AFE_SECURE_MASK_CONN28_3 (0x423c)
+#define AFE_SECURE_MASK_CONN28_4 (0x4240)
+#define AFE_SECURE_MASK_CONN29 (0x4244)
+#define AFE_SECURE_MASK_CONN29_1 (0x4248)
+#define AFE_SECURE_MASK_CONN29_2 (0x424c)
+#define AFE_SECURE_MASK_CONN29_3 (0x4250)
+#define AFE_SECURE_MASK_CONN29_4 (0x4254)
+#define AFE_SECURE_MASK_CONN30 (0x4258)
+#define AFE_SECURE_MASK_CONN30_1 (0x425c)
+#define AFE_SECURE_MASK_CONN30_2 (0x4260)
+#define AFE_SECURE_MASK_CONN30_3 (0x4264)
+#define AFE_SECURE_MASK_CONN30_4 (0x4268)
+#define AFE_SECURE_MASK_CONN31 (0x426c)
+#define AFE_SECURE_MASK_CONN31_1 (0x4270)
+#define AFE_SECURE_MASK_CONN31_2 (0x4274)
+#define AFE_SECURE_MASK_CONN31_3 (0x4278)
+#define AFE_SECURE_MASK_CONN31_4 (0x427c)
+#define AFE_SECURE_MASK_CONN32 (0x4280)
+#define AFE_SECURE_MASK_CONN32_1 (0x4284)
+#define AFE_SECURE_MASK_CONN32_2 (0x4288)
+#define AFE_SECURE_MASK_CONN32_3 (0x428c)
+#define AFE_SECURE_MASK_CONN32_4 (0x4290)
+#define AFE_SECURE_MASK_CONN33 (0x4294)
+#define AFE_SECURE_MASK_CONN33_1 (0x4298)
+#define AFE_SECURE_MASK_CONN33_2 (0x429c)
+#define AFE_SECURE_MASK_CONN33_3 (0x42a0)
+#define AFE_SECURE_MASK_CONN33_4 (0x42a4)
+#define AFE_SECURE_MASK_CONN34 (0x42a8)
+#define AFE_SECURE_MASK_CONN34_1 (0x42ac)
+#define AFE_SECURE_MASK_CONN34_2 (0x42b0)
+#define AFE_SECURE_MASK_CONN34_3 (0x42b4)
+#define AFE_SECURE_MASK_CONN34_4 (0x42b8)
+#define AFE_SECURE_MASK_CONN35 (0x42bc)
+#define AFE_SECURE_MASK_CONN35_1 (0x42c0)
+#define AFE_SECURE_MASK_CONN35_2 (0x42c4)
+#define AFE_SECURE_MASK_CONN35_3 (0x42c8)
+#define AFE_SECURE_MASK_CONN35_4 (0x42cc)
+#define AFE_SECURE_MASK_CONN36 (0x42d0)
+#define AFE_SECURE_MASK_CONN36_1 (0x42d4)
+#define AFE_SECURE_MASK_CONN36_2 (0x42d8)
+#define AFE_SECURE_MASK_CONN36_3 (0x42dc)
+#define AFE_SECURE_MASK_CONN36_4 (0x42e0)
+#define AFE_SECURE_MASK_CONN37 (0x42e4)
+#define AFE_SECURE_MASK_CONN37_1 (0x42e8)
+#define AFE_SECURE_MASK_CONN37_2 (0x42ec)
+#define AFE_SECURE_MASK_CONN37_3 (0x42f0)
+#define AFE_SECURE_MASK_CONN37_4 (0x42f4)
+#define AFE_SECURE_MASK_CONN38 (0x42f8)
+#define AFE_SECURE_MASK_CONN38_1 (0x42fc)
+#define AFE_SECURE_MASK_CONN38_2 (0x4300)
+#define AFE_SECURE_MASK_CONN38_3 (0x4304)
+#define AFE_SECURE_MASK_CONN38_4 (0x4308)
+#define AFE_SECURE_MASK_CONN39 (0x430c)
+#define AFE_SECURE_MASK_CONN39_1 (0x4310)
+#define AFE_SECURE_MASK_CONN39_2 (0x4314)
+#define AFE_SECURE_MASK_CONN39_3 (0x4318)
+#define AFE_SECURE_MASK_CONN39_4 (0x431c)
+#define AFE_SECURE_MASK_CONN40 (0x4320)
+#define AFE_SECURE_MASK_CONN40_1 (0x4324)
+#define AFE_SECURE_MASK_CONN40_2 (0x4328)
+#define AFE_SECURE_MASK_CONN40_3 (0x432c)
+#define AFE_SECURE_MASK_CONN40_4 (0x4330)
+#define AFE_SECURE_MASK_CONN41 (0x4334)
+#define AFE_SECURE_MASK_CONN41_1 (0x4338)
+#define AFE_SECURE_MASK_CONN41_2 (0x433c)
+#define AFE_SECURE_MASK_CONN41_3 (0x4340)
+#define AFE_SECURE_MASK_CONN41_4 (0x4344)
+#define AFE_SECURE_MASK_CONN42 (0x4348)
+#define AFE_SECURE_MASK_CONN42_1 (0x434c)
+#define AFE_SECURE_MASK_CONN42_2 (0x4350)
+#define AFE_SECURE_MASK_CONN42_3 (0x4354)
+#define AFE_SECURE_MASK_CONN42_4 (0x4358)
+#define AFE_SECURE_MASK_CONN43 (0x435c)
+#define AFE_SECURE_MASK_CONN43_1 (0x4360)
+#define AFE_SECURE_MASK_CONN43_2 (0x4364)
+#define AFE_SECURE_MASK_CONN43_3 (0x4368)
+#define AFE_SECURE_MASK_CONN43_4 (0x436c)
+#define AFE_SECURE_MASK_CONN44 (0x4370)
+#define AFE_SECURE_MASK_CONN44_1 (0x4374)
+#define AFE_SECURE_MASK_CONN44_2 (0x4378)
+#define AFE_SECURE_MASK_CONN44_3 (0x437c)
+#define AFE_SECURE_MASK_CONN44_4 (0x4380)
+#define AFE_SECURE_MASK_CONN45 (0x4384)
+#define AFE_SECURE_MASK_CONN45_1 (0x4388)
+#define AFE_SECURE_MASK_CONN45_2 (0x438c)
+#define AFE_SECURE_MASK_CONN45_3 (0x4390)
+#define AFE_SECURE_MASK_CONN45_4 (0x4394)
+#define AFE_SECURE_MASK_CONN46 (0x4398)
+#define AFE_SECURE_MASK_CONN46_1 (0x439c)
+#define AFE_SECURE_MASK_CONN46_2 (0x43a0)
+#define AFE_SECURE_MASK_CONN46_3 (0x43a4)
+#define AFE_SECURE_MASK_CONN46_4 (0x43a8)
+#define AFE_SECURE_MASK_CONN47 (0x43ac)
+#define AFE_SECURE_MASK_CONN47_1 (0x43b0)
+#define AFE_SECURE_MASK_CONN47_2 (0x43b4)
+#define AFE_SECURE_MASK_CONN47_3 (0x43b8)
+#define AFE_SECURE_MASK_CONN47_4 (0x43bc)
+#define AFE_SECURE_MASK_CONN48 (0x43c0)
+#define AFE_SECURE_MASK_CONN48_1 (0x43c4)
+#define AFE_SECURE_MASK_CONN48_2 (0x43c8)
+#define AFE_SECURE_MASK_CONN48_3 (0x43cc)
+#define AFE_SECURE_MASK_CONN48_4 (0x43d0)
+#define AFE_SECURE_MASK_CONN49 (0x43d4)
+#define AFE_SECURE_MASK_CONN49_1 (0x43d8)
+#define AFE_SECURE_MASK_CONN49_2 (0x43dc)
+#define AFE_SECURE_MASK_CONN49_3 (0x43e0)
+#define AFE_SECURE_MASK_CONN49_4 (0x43e4)
+#define AFE_SECURE_MASK_CONN50 (0x43e8)
+#define AFE_SECURE_MASK_CONN50_1 (0x43ec)
+#define AFE_SECURE_MASK_CONN50_2 (0x43f0)
+#define AFE_SECURE_MASK_CONN50_3 (0x43f4)
+#define AFE_SECURE_MASK_CONN50_4 (0x43f8)
+#define AFE_SECURE_MASK_CONN51 (0x43fc)
+#define AFE_SECURE_MASK_CONN51_1 (0x4400)
+#define AFE_SECURE_MASK_CONN51_2 (0x4404)
+#define AFE_SECURE_MASK_CONN51_3 (0x4408)
+#define AFE_SECURE_MASK_CONN51_4 (0x440c)
+#define AFE_SECURE_MASK_CONN52 (0x4410)
+#define AFE_SECURE_MASK_CONN52_1 (0x4414)
+#define AFE_SECURE_MASK_CONN52_2 (0x4418)
+#define AFE_SECURE_MASK_CONN52_3 (0x441c)
+#define AFE_SECURE_MASK_CONN52_4 (0x4420)
+#define AFE_SECURE_MASK_CONN53 (0x4424)
+#define AFE_SECURE_MASK_CONN53_1 (0x4428)
+#define AFE_SECURE_MASK_CONN53_2 (0x442c)
+#define AFE_SECURE_MASK_CONN53_3 (0x4430)
+#define AFE_SECURE_MASK_CONN53_4 (0x4434)
+#define AFE_SECURE_MASK_CONN54 (0x4438)
+#define AFE_SECURE_MASK_CONN54_1 (0x443c)
+#define AFE_SECURE_MASK_CONN54_2 (0x4440)
+#define AFE_SECURE_MASK_CONN54_3 (0x4444)
+#define AFE_SECURE_MASK_CONN54_4 (0x4448)
+#define AFE_SECURE_MASK_CONN55 (0x444c)
+#define AFE_SECURE_MASK_CONN55_1 (0x4450)
+#define AFE_SECURE_MASK_CONN55_2 (0x4454)
+#define AFE_SECURE_MASK_CONN55_3 (0x4458)
+#define AFE_SECURE_MASK_CONN55_4 (0x445c)
+#define AFE_SECURE_MASK_CONN56 (0x4460)
+#define AFE_SECURE_MASK_CONN56_1 (0x4464)
+#define AFE_SECURE_MASK_CONN56_2 (0x4468)
+#define AFE_SECURE_MASK_CONN56_3 (0x446c)
+#define AFE_SECURE_MASK_CONN56_4 (0x4470)
+#define AFE_SECURE_MASK_CONN57 (0x4474)
+#define AFE_SECURE_MASK_CONN57_1 (0x4478)
+#define AFE_SECURE_MASK_CONN57_2 (0x447c)
+#define AFE_SECURE_MASK_CONN57_3 (0x4480)
+#define AFE_SECURE_MASK_CONN57_4 (0x4484)
+#define AFE_SECURE_MASK_CONN58 (0x4488)
+#define AFE_SECURE_MASK_CONN58_1 (0x448c)
+#define AFE_SECURE_MASK_CONN58_2 (0x4490)
+#define AFE_SECURE_MASK_CONN58_3 (0x4494)
+#define AFE_SECURE_MASK_CONN58_4 (0x4498)
+#define AFE_SECURE_MASK_CONN59 (0x449c)
+#define AFE_SECURE_MASK_CONN59_1 (0x44a0)
+#define AFE_SECURE_MASK_CONN59_2 (0x44a4)
+#define AFE_SECURE_MASK_CONN59_3 (0x44a8)
+#define AFE_SECURE_MASK_CONN59_4 (0x44ac)
+#define AFE_SECURE_MASK_CONN60 (0x44b0)
+#define AFE_SECURE_MASK_CONN60_1 (0x44b4)
+#define AFE_SECURE_MASK_CONN60_2 (0x44b8)
+#define AFE_SECURE_MASK_CONN60_3 (0x44bc)
+#define AFE_SECURE_MASK_CONN60_4 (0x44c0)
+#define AFE_SECURE_MASK_CONN61 (0x44c4)
+#define AFE_SECURE_MASK_CONN61_1 (0x44c8)
+#define AFE_SECURE_MASK_CONN61_2 (0x44cc)
+#define AFE_SECURE_MASK_CONN61_3 (0x44d0)
+#define AFE_SECURE_MASK_CONN61_4 (0x44d4)
+#define AFE_SECURE_MASK_CONN62 (0x44d8)
+#define AFE_SECURE_MASK_CONN62_1 (0x44dc)
+#define AFE_SECURE_MASK_CONN62_2 (0x44e0)
+#define AFE_SECURE_MASK_CONN62_3 (0x44e4)
+#define AFE_SECURE_MASK_CONN62_4 (0x44e8)
+#define AFE_SECURE_MASK_CONN63 (0x44ec)
+#define AFE_SECURE_MASK_CONN63_1 (0x44f0)
+#define AFE_SECURE_MASK_CONN63_2 (0x44f4)
+#define AFE_SECURE_MASK_CONN63_3 (0x44f8)
+#define AFE_SECURE_MASK_CONN63_4 (0x44fc)
+#define AFE_SECURE_MASK_CONN64 (0x4500)
+#define AFE_SECURE_MASK_CONN64_1 (0x4504)
+#define AFE_SECURE_MASK_CONN64_2 (0x4508)
+#define AFE_SECURE_MASK_CONN64_3 (0x450c)
+#define AFE_SECURE_MASK_CONN64_4 (0x4510)
+#define AFE_SECURE_MASK_CONN65 (0x4514)
+#define AFE_SECURE_MASK_CONN65_1 (0x4518)
+#define AFE_SECURE_MASK_CONN65_2 (0x451c)
+#define AFE_SECURE_MASK_CONN65_3 (0x4520)
+#define AFE_SECURE_MASK_CONN65_4 (0x4524)
+#define AFE_SECURE_MASK_CONN66 (0x4528)
+#define AFE_SECURE_MASK_CONN66_1 (0x452c)
+#define AFE_SECURE_MASK_CONN66_2 (0x4530)
+#define AFE_SECURE_MASK_CONN66_3 (0x4534)
+#define AFE_SECURE_MASK_CONN66_4 (0x4538)
+#define AFE_SECURE_MASK_CONN67 (0x453c)
+#define AFE_SECURE_MASK_CONN67_1 (0x4540)
+#define AFE_SECURE_MASK_CONN67_2 (0x4544)
+#define AFE_SECURE_MASK_CONN67_3 (0x4548)
+#define AFE_SECURE_MASK_CONN67_4 (0x454c)
+#define AFE_SECURE_MASK_CONN68 (0x4550)
+#define AFE_SECURE_MASK_CONN68_1 (0x4554)
+#define AFE_SECURE_MASK_CONN68_2 (0x4558)
+#define AFE_SECURE_MASK_CONN68_3 (0x455c)
+#define AFE_SECURE_MASK_CONN68_4 (0x4560)
+#define AFE_SECURE_MASK_CONN69 (0x4564)
+#define AFE_SECURE_MASK_CONN69_1 (0x4568)
+#define AFE_SECURE_MASK_CONN69_2 (0x456c)
+#define AFE_SECURE_MASK_CONN69_3 (0x4570)
+#define AFE_SECURE_MASK_CONN69_4 (0x4574)
+#define AFE_SECURE_MASK_CONN70 (0x4578)
+#define AFE_SECURE_MASK_CONN70_1 (0x457c)
+#define AFE_SECURE_MASK_CONN70_2 (0x4580)
+#define AFE_SECURE_MASK_CONN70_3 (0x4584)
+#define AFE_SECURE_MASK_CONN70_4 (0x4588)
+#define AFE_SECURE_MASK_CONN71 (0x458c)
+#define AFE_SECURE_MASK_CONN71_1 (0x4590)
+#define AFE_SECURE_MASK_CONN71_2 (0x4594)
+#define AFE_SECURE_MASK_CONN71_3 (0x4598)
+#define AFE_SECURE_MASK_CONN71_4 (0x459c)
+#define AFE_SECURE_MASK_CONN72 (0x45a0)
+#define AFE_SECURE_MASK_CONN72_1 (0x45a4)
+#define AFE_SECURE_MASK_CONN72_2 (0x45a8)
+#define AFE_SECURE_MASK_CONN72_3 (0x45ac)
+#define AFE_SECURE_MASK_CONN72_4 (0x45b0)
+#define AFE_SECURE_MASK_CONN73 (0x45b4)
+#define AFE_SECURE_MASK_CONN73_1 (0x45b8)
+#define AFE_SECURE_MASK_CONN73_2 (0x45bc)
+#define AFE_SECURE_MASK_CONN73_3 (0x45c0)
+#define AFE_SECURE_MASK_CONN73_4 (0x45c4)
+#define AFE_SECURE_MASK_CONN74 (0x45c8)
+#define AFE_SECURE_MASK_CONN74_1 (0x45cc)
+#define AFE_SECURE_MASK_CONN74_2 (0x45d0)
+#define AFE_SECURE_MASK_CONN74_3 (0x45d4)
+#define AFE_SECURE_MASK_CONN74_4 (0x45d8)
+#define AFE_SECURE_MASK_CONN75 (0x45dc)
+#define AFE_SECURE_MASK_CONN75_1 (0x45e0)
+#define AFE_SECURE_MASK_CONN75_2 (0x45e4)
+#define AFE_SECURE_MASK_CONN75_3 (0x45e8)
+#define AFE_SECURE_MASK_CONN75_4 (0x45ec)
+#define AFE_SECURE_MASK_CONN76 (0x45f0)
+#define AFE_SECURE_MASK_CONN76_1 (0x45f4)
+#define AFE_SECURE_MASK_CONN76_2 (0x45f8)
+#define AFE_SECURE_MASK_CONN76_3 (0x45fc)
+#define AFE_SECURE_MASK_CONN76_4 (0x4600)
+#define AFE_SECURE_MASK_CONN77 (0x4604)
+#define AFE_SECURE_MASK_CONN77_1 (0x4608)
+#define AFE_SECURE_MASK_CONN77_2 (0x460c)
+#define AFE_SECURE_MASK_CONN77_3 (0x4610)
+#define AFE_SECURE_MASK_CONN77_4 (0x4614)
+#define AFE_SECURE_MASK_CONN78 (0x4618)
+#define AFE_SECURE_MASK_CONN78_1 (0x461c)
+#define AFE_SECURE_MASK_CONN78_2 (0x4620)
+#define AFE_SECURE_MASK_CONN78_3 (0x4624)
+#define AFE_SECURE_MASK_CONN78_4 (0x4628)
+#define AFE_SECURE_MASK_CONN79 (0x462c)
+#define AFE_SECURE_MASK_CONN79_1 (0x4630)
+#define AFE_SECURE_MASK_CONN79_2 (0x4634)
+#define AFE_SECURE_MASK_CONN79_3 (0x4638)
+#define AFE_SECURE_MASK_CONN79_4 (0x463c)
+#define AFE_SECURE_MASK_CONN80 (0x4640)
+#define AFE_SECURE_MASK_CONN80_1 (0x4644)
+#define AFE_SECURE_MASK_CONN80_2 (0x4648)
+#define AFE_SECURE_MASK_CONN80_3 (0x464c)
+#define AFE_SECURE_MASK_CONN80_4 (0x4650)
+#define AFE_SECURE_MASK_CONN81 (0x4654)
+#define AFE_SECURE_MASK_CONN81_1 (0x4658)
+#define AFE_SECURE_MASK_CONN81_2 (0x465c)
+#define AFE_SECURE_MASK_CONN81_3 (0x4660)
+#define AFE_SECURE_MASK_CONN81_4 (0x4664)
+#define AFE_SECURE_MASK_CONN82 (0x4668)
+#define AFE_SECURE_MASK_CONN82_1 (0x466c)
+#define AFE_SECURE_MASK_CONN82_2 (0x4670)
+#define AFE_SECURE_MASK_CONN82_3 (0x4674)
+#define AFE_SECURE_MASK_CONN82_4 (0x4678)
+#define AFE_SECURE_MASK_CONN83 (0x467c)
+#define AFE_SECURE_MASK_CONN83_1 (0x4680)
+#define AFE_SECURE_MASK_CONN83_2 (0x4684)
+#define AFE_SECURE_MASK_CONN83_3 (0x4688)
+#define AFE_SECURE_MASK_CONN83_4 (0x468c)
+#define AFE_SECURE_MASK_CONN84 (0x4690)
+#define AFE_SECURE_MASK_CONN84_1 (0x4694)
+#define AFE_SECURE_MASK_CONN84_2 (0x4698)
+#define AFE_SECURE_MASK_CONN84_3 (0x469c)
+#define AFE_SECURE_MASK_CONN84_4 (0x46a0)
+#define AFE_SECURE_MASK_CONN85 (0x46a4)
+#define AFE_SECURE_MASK_CONN85_1 (0x46a8)
+#define AFE_SECURE_MASK_CONN85_2 (0x46ac)
+#define AFE_SECURE_MASK_CONN85_3 (0x46b0)
+#define AFE_SECURE_MASK_CONN85_4 (0x46b4)
+#define AFE_SECURE_MASK_CONN86 (0x46b8)
+#define AFE_SECURE_MASK_CONN86_1 (0x46bc)
+#define AFE_SECURE_MASK_CONN86_2 (0x46c0)
+#define AFE_SECURE_MASK_CONN86_3 (0x46c4)
+#define AFE_SECURE_MASK_CONN86_4 (0x46c8)
+#define AFE_SECURE_MASK_CONN87 (0x46cc)
+#define AFE_SECURE_MASK_CONN87_1 (0x46d0)
+#define AFE_SECURE_MASK_CONN87_2 (0x46d4)
+#define AFE_SECURE_MASK_CONN87_3 (0x46d8)
+#define AFE_SECURE_MASK_CONN87_4 (0x46dc)
+#define AFE_SECURE_MASK_CONN88 (0x46e0)
+#define AFE_SECURE_MASK_CONN88_1 (0x46e4)
+#define AFE_SECURE_MASK_CONN88_2 (0x46e8)
+#define AFE_SECURE_MASK_CONN88_3 (0x46ec)
+#define AFE_SECURE_MASK_CONN88_4 (0x46f0)
+#define AFE_SECURE_MASK_CONN89 (0x46f4)
+#define AFE_SECURE_MASK_CONN89_1 (0x46f8)
+#define AFE_SECURE_MASK_CONN89_2 (0x46fc)
+#define AFE_SECURE_MASK_CONN89_3 (0x4700)
+#define AFE_SECURE_MASK_CONN89_4 (0x4704)
+#define AFE_SECURE_MASK_CONN90 (0x4708)
+#define AFE_SECURE_MASK_CONN90_1 (0x470c)
+#define AFE_SECURE_MASK_CONN90_2 (0x4710)
+#define AFE_SECURE_MASK_CONN90_3 (0x4714)
+#define AFE_SECURE_MASK_CONN90_4 (0x4718)
+#define AFE_SECURE_MASK_CONN91 (0x471c)
+#define AFE_SECURE_MASK_CONN91_1 (0x4720)
+#define AFE_SECURE_MASK_CONN91_2 (0x4724)
+#define AFE_SECURE_MASK_CONN91_3 (0x4728)
+#define AFE_SECURE_MASK_CONN91_4 (0x472c)
+#define AFE_SECURE_MASK_CONN92 (0x4730)
+#define AFE_SECURE_MASK_CONN92_1 (0x4734)
+#define AFE_SECURE_MASK_CONN92_2 (0x4738)
+#define AFE_SECURE_MASK_CONN92_3 (0x473c)
+#define AFE_SECURE_MASK_CONN92_4 (0x4740)
+#define AFE_SECURE_MASK_CONN93 (0x4744)
+#define AFE_SECURE_MASK_CONN93_1 (0x4748)
+#define AFE_SECURE_MASK_CONN93_2 (0x474c)
+#define AFE_SECURE_MASK_CONN93_3 (0x4750)
+#define AFE_SECURE_MASK_CONN93_4 (0x4754)
+#define AFE_SECURE_MASK_CONN94 (0x4758)
+#define AFE_SECURE_MASK_CONN94_1 (0x475c)
+#define AFE_SECURE_MASK_CONN94_2 (0x4760)
+#define AFE_SECURE_MASK_CONN94_3 (0x4764)
+#define AFE_SECURE_MASK_CONN94_4 (0x4768)
+#define AFE_SECURE_MASK_CONN95 (0x476c)
+#define AFE_SECURE_MASK_CONN95_1 (0x4770)
+#define AFE_SECURE_MASK_CONN95_2 (0x4774)
+#define AFE_SECURE_MASK_CONN95_3 (0x4778)
+#define AFE_SECURE_MASK_CONN95_4 (0x477c)
+#define AFE_SECURE_MASK_CONN96 (0x4780)
+#define AFE_SECURE_MASK_CONN96_1 (0x4784)
+#define AFE_SECURE_MASK_CONN96_2 (0x4788)
+#define AFE_SECURE_MASK_CONN96_3 (0x478c)
+#define AFE_SECURE_MASK_CONN96_4 (0x4790)
+#define AFE_SECURE_MASK_CONN97 (0x4794)
+#define AFE_SECURE_MASK_CONN97_1 (0x4798)
+#define AFE_SECURE_MASK_CONN97_2 (0x479c)
+#define AFE_SECURE_MASK_CONN97_3 (0x47a0)
+#define AFE_SECURE_MASK_CONN97_4 (0x47a4)
+#define AFE_SECURE_MASK_CONN98 (0x47a8)
+#define AFE_SECURE_MASK_CONN98_1 (0x47ac)
+#define AFE_SECURE_MASK_CONN98_2 (0x47b0)
+#define AFE_SECURE_MASK_CONN98_3 (0x47b4)
+#define AFE_SECURE_MASK_CONN98_4 (0x47b8)
+#define AFE_SECURE_MASK_CONN99 (0x47bc)
+#define AFE_SECURE_MASK_CONN99_1 (0x47c0)
+#define AFE_SECURE_MASK_CONN99_2 (0x47c4)
+#define AFE_SECURE_MASK_CONN99_3 (0x47c8)
+#define AFE_SECURE_MASK_CONN99_4 (0x47cc)
+#define AFE_SECURE_MASK_CONN100 (0x47d0)
+#define AFE_SECURE_MASK_CONN100_1 (0x47d4)
+#define AFE_SECURE_MASK_CONN100_2 (0x47d8)
+#define AFE_SECURE_MASK_CONN100_3 (0x47dc)
+#define AFE_SECURE_MASK_CONN100_4 (0x47e0)
+#define AFE_SECURE_MASK_CONN101 (0x47e4)
+#define AFE_SECURE_MASK_CONN101_1 (0x47e8)
+#define AFE_SECURE_MASK_CONN101_2 (0x47ec)
+#define AFE_SECURE_MASK_CONN101_3 (0x47f0)
+#define AFE_SECURE_MASK_CONN101_4 (0x47f4)
+#define AFE_SECURE_MASK_CONN102 (0x47f8)
+#define AFE_SECURE_MASK_CONN102_1 (0x47fc)
+#define AFE_SECURE_MASK_CONN102_2 (0x4800)
+#define AFE_SECURE_MASK_CONN102_3 (0x4804)
+#define AFE_SECURE_MASK_CONN102_4 (0x4808)
+#define AFE_SECURE_MASK_CONN103 (0x480c)
+#define AFE_SECURE_MASK_CONN103_1 (0x4810)
+#define AFE_SECURE_MASK_CONN103_2 (0x4814)
+#define AFE_SECURE_MASK_CONN103_3 (0x4818)
+#define AFE_SECURE_MASK_CONN103_4 (0x481c)
+#define AFE_SECURE_MASK_CONN104 (0x4820)
+#define AFE_SECURE_MASK_CONN104_1 (0x4824)
+#define AFE_SECURE_MASK_CONN104_2 (0x4828)
+#define AFE_SECURE_MASK_CONN104_3 (0x482c)
+#define AFE_SECURE_MASK_CONN104_4 (0x4830)
+#define AFE_SECURE_MASK_CONN105 (0x4834)
+#define AFE_SECURE_MASK_CONN105_1 (0x4838)
+#define AFE_SECURE_MASK_CONN105_2 (0x483c)
+#define AFE_SECURE_MASK_CONN105_3 (0x4840)
+#define AFE_SECURE_MASK_CONN105_4 (0x4844)
+#define AFE_SECURE_MASK_CONN106 (0x4848)
+#define AFE_SECURE_MASK_CONN106_1 (0x484c)
+#define AFE_SECURE_MASK_CONN106_2 (0x4850)
+#define AFE_SECURE_MASK_CONN106_3 (0x4854)
+#define AFE_SECURE_MASK_CONN106_4 (0x4858)
+#define AFE_SECURE_MASK_CONN107 (0x485c)
+#define AFE_SECURE_MASK_CONN107_1 (0x4860)
+#define AFE_SECURE_MASK_CONN107_2 (0x4864)
+#define AFE_SECURE_MASK_CONN107_3 (0x4868)
+#define AFE_SECURE_MASK_CONN107_4 (0x486c)
+#define AFE_SECURE_MASK_CONN108 (0x4870)
+#define AFE_SECURE_MASK_CONN108_1 (0x4874)
+#define AFE_SECURE_MASK_CONN108_2 (0x4878)
+#define AFE_SECURE_MASK_CONN108_3 (0x487c)
+#define AFE_SECURE_MASK_CONN108_4 (0x4880)
+#define AFE_SECURE_MASK_CONN109 (0x4884)
+#define AFE_SECURE_MASK_CONN109_1 (0x4888)
+#define AFE_SECURE_MASK_CONN109_2 (0x488c)
+#define AFE_SECURE_MASK_CONN109_3 (0x4890)
+#define AFE_SECURE_MASK_CONN109_4 (0x4894)
+#define AFE_SECURE_MASK_CONN110 (0x4898)
+#define AFE_SECURE_MASK_CONN110_1 (0x489c)
+#define AFE_SECURE_MASK_CONN110_2 (0x48a0)
+#define AFE_SECURE_MASK_CONN110_3 (0x48a4)
+#define AFE_SECURE_MASK_CONN110_4 (0x48a8)
+#define AFE_SECURE_MASK_CONN111 (0x48ac)
+#define AFE_SECURE_MASK_CONN111_1 (0x48b0)
+#define AFE_SECURE_MASK_CONN111_2 (0x48b4)
+#define AFE_SECURE_MASK_CONN111_3 (0x48b8)
+#define AFE_SECURE_MASK_CONN111_4 (0x48bc)
+#define AFE_SECURE_MASK_CONN112 (0x48c0)
+#define AFE_SECURE_MASK_CONN112_1 (0x48c4)
+#define AFE_SECURE_MASK_CONN112_2 (0x48c8)
+#define AFE_SECURE_MASK_CONN112_3 (0x48cc)
+#define AFE_SECURE_MASK_CONN112_4 (0x48d0)
+#define AFE_SECURE_MASK_CONN113 (0x48d4)
+#define AFE_SECURE_MASK_CONN113_1 (0x48d8)
+#define AFE_SECURE_MASK_CONN113_2 (0x48dc)
+#define AFE_SECURE_MASK_CONN113_3 (0x48e0)
+#define AFE_SECURE_MASK_CONN113_4 (0x48e4)
+#define AFE_SECURE_MASK_CONN114 (0x48e8)
+#define AFE_SECURE_MASK_CONN114_1 (0x48ec)
+#define AFE_SECURE_MASK_CONN114_2 (0x48f0)
+#define AFE_SECURE_MASK_CONN114_3 (0x48f4)
+#define AFE_SECURE_MASK_CONN114_4 (0x48f8)
+#define AFE_SECURE_MASK_CONN115 (0x48fc)
+#define AFE_SECURE_MASK_CONN115_1 (0x4900)
+#define AFE_SECURE_MASK_CONN115_2 (0x4904)
+#define AFE_SECURE_MASK_CONN115_3 (0x4908)
+#define AFE_SECURE_MASK_CONN115_4 (0x490c)
+#define AFE_SECURE_MASK_CONN116 (0x4910)
+#define AFE_SECURE_MASK_CONN116_1 (0x4914)
+#define AFE_SECURE_MASK_CONN116_2 (0x4918)
+#define AFE_SECURE_MASK_CONN116_3 (0x491c)
+#define AFE_SECURE_MASK_CONN116_4 (0x4920)
+#define AFE_SECURE_MASK_CONN117 (0x4924)
+#define AFE_SECURE_MASK_CONN117_1 (0x4928)
+#define AFE_SECURE_MASK_CONN117_2 (0x492c)
+#define AFE_SECURE_MASK_CONN117_3 (0x4930)
+#define AFE_SECURE_MASK_CONN117_4 (0x4934)
+#define AFE_SECURE_MASK_CONN118 (0x4938)
+#define AFE_SECURE_MASK_CONN118_1 (0x493c)
+#define AFE_SECURE_MASK_CONN118_2 (0x4940)
+#define AFE_SECURE_MASK_CONN118_3 (0x4944)
+#define AFE_SECURE_MASK_CONN118_4 (0x4948)
+#define AFE_SECURE_MASK_CONN119 (0x494c)
+#define AFE_SECURE_MASK_CONN119_1 (0x4950)
+#define AFE_SECURE_MASK_CONN119_2 (0x4954)
+#define AFE_SECURE_MASK_CONN119_3 (0x4958)
+#define AFE_SECURE_MASK_CONN119_4 (0x495c)
+#define AFE_SECURE_MASK_CONN120 (0x4960)
+#define AFE_SECURE_MASK_CONN120_1 (0x4964)
+#define AFE_SECURE_MASK_CONN120_2 (0x4968)
+#define AFE_SECURE_MASK_CONN120_3 (0x496c)
+#define AFE_SECURE_MASK_CONN120_4 (0x4970)
+#define AFE_SECURE_MASK_CONN121 (0x4974)
+#define AFE_SECURE_MASK_CONN121_1 (0x4978)
+#define AFE_SECURE_MASK_CONN121_2 (0x497c)
+#define AFE_SECURE_MASK_CONN121_3 (0x4980)
+#define AFE_SECURE_MASK_CONN121_4 (0x4984)
+#define AFE_SECURE_MASK_CONN122 (0x4988)
+#define AFE_SECURE_MASK_CONN122_1 (0x498c)
+#define AFE_SECURE_MASK_CONN122_2 (0x4990)
+#define AFE_SECURE_MASK_CONN122_3 (0x4994)
+#define AFE_SECURE_MASK_CONN122_4 (0x4998)
+#define AFE_SECURE_MASK_CONN123 (0x499c)
+#define AFE_SECURE_MASK_CONN123_1 (0x49a0)
+#define AFE_SECURE_MASK_CONN123_2 (0x49a4)
+#define AFE_SECURE_MASK_CONN123_3 (0x49a8)
+#define AFE_SECURE_MASK_CONN123_4 (0x49ac)
+#define AFE_SECURE_MASK_CONN124 (0x49b0)
+#define AFE_SECURE_MASK_CONN124_1 (0x49b4)
+#define AFE_SECURE_MASK_CONN124_2 (0x49b8)
+#define AFE_SECURE_MASK_CONN124_3 (0x49bc)
+#define AFE_SECURE_MASK_CONN124_4 (0x49c0)
+#define AFE_SECURE_MASK_CONN125 (0x49c4)
+#define AFE_SECURE_MASK_CONN125_1 (0x49c8)
+#define AFE_SECURE_MASK_CONN125_2 (0x49cc)
+#define AFE_SECURE_MASK_CONN125_3 (0x49d0)
+#define AFE_SECURE_MASK_CONN125_4 (0x49d4)
+#define AFE_SECURE_MASK_CONN126 (0x49d8)
+#define AFE_SECURE_MASK_CONN126_1 (0x49dc)
+#define AFE_SECURE_MASK_CONN126_2 (0x49e0)
+#define AFE_SECURE_MASK_CONN126_3 (0x49e4)
+#define AFE_SECURE_MASK_CONN126_4 (0x49e8)
+#define AFE_SECURE_MASK_CONN127 (0x49ec)
+#define AFE_SECURE_MASK_CONN127_1 (0x49f0)
+#define AFE_SECURE_MASK_CONN127_2 (0x49f4)
+#define AFE_SECURE_MASK_CONN127_3 (0x49f8)
+#define AFE_SECURE_MASK_CONN127_4 (0x49fc)
+#define AFE_SECURE_MASK_CONN128 (0x4a00)
+#define AFE_SECURE_MASK_CONN128_1 (0x4a04)
+#define AFE_SECURE_MASK_CONN128_2 (0x4a08)
+#define AFE_SECURE_MASK_CONN128_3 (0x4a0c)
+#define AFE_SECURE_MASK_CONN128_4 (0x4a10)
+#define AFE_SECURE_MASK_CONN129 (0x4a14)
+#define AFE_SECURE_MASK_CONN129_1 (0x4a18)
+#define AFE_SECURE_MASK_CONN129_2 (0x4a1c)
+#define AFE_SECURE_MASK_CONN129_3 (0x4a20)
+#define AFE_SECURE_MASK_CONN129_4 (0x4a24)
+#define AFE_SECURE_MASK_CONN130 (0x4a28)
+#define AFE_SECURE_MASK_CONN130_1 (0x4a2c)
+#define AFE_SECURE_MASK_CONN130_2 (0x4a30)
+#define AFE_SECURE_MASK_CONN130_3 (0x4a34)
+#define AFE_SECURE_MASK_CONN130_4 (0x4a38)
+#define AFE_SECURE_MASK_CONN131 (0x4a3c)
+#define AFE_SECURE_MASK_CONN131_1 (0x4a40)
+#define AFE_SECURE_MASK_CONN131_2 (0x4a44)
+#define AFE_SECURE_MASK_CONN131_3 (0x4a48)
+#define AFE_SECURE_MASK_CONN131_4 (0x4a4c)
+#define AFE_SECURE_MASK_CONN132 (0x4a50)
+#define AFE_SECURE_MASK_CONN132_1 (0x4a54)
+#define AFE_SECURE_MASK_CONN132_2 (0x4a58)
+#define AFE_SECURE_MASK_CONN132_3 (0x4a5c)
+#define AFE_SECURE_MASK_CONN132_4 (0x4a60)
+#define AFE_SECURE_MASK_CONN133 (0x4a64)
+#define AFE_SECURE_MASK_CONN133_1 (0x4a68)
+#define AFE_SECURE_MASK_CONN133_2 (0x4a6c)
+#define AFE_SECURE_MASK_CONN133_3 (0x4a70)
+#define AFE_SECURE_MASK_CONN133_4 (0x4a74)
+#define AFE_SECURE_MASK_CONN134 (0x4a78)
+#define AFE_SECURE_MASK_CONN134_1 (0x4a7c)
+#define AFE_SECURE_MASK_CONN134_2 (0x4a80)
+#define AFE_SECURE_MASK_CONN134_3 (0x4a84)
+#define AFE_SECURE_MASK_CONN134_4 (0x4a88)
+#define AFE_SECURE_MASK_CONN135 (0x4a8c)
+#define AFE_SECURE_MASK_CONN135_1 (0x4a90)
+#define AFE_SECURE_MASK_CONN135_2 (0x4a94)
+#define AFE_SECURE_MASK_CONN135_3 (0x4a98)
+#define AFE_SECURE_MASK_CONN135_4 (0x4a9c)
+#define AFE_SECURE_MASK_CONN136 (0x4aa0)
+#define AFE_SECURE_MASK_CONN136_1 (0x4aa4)
+#define AFE_SECURE_MASK_CONN136_2 (0x4aa8)
+#define AFE_SECURE_MASK_CONN136_3 (0x4aac)
+#define AFE_SECURE_MASK_CONN136_4 (0x4ab0)
+#define AFE_SECURE_MASK_CONN137 (0x4ab4)
+#define AFE_SECURE_MASK_CONN137_1 (0x4ab8)
+#define AFE_SECURE_MASK_CONN137_2 (0x4abc)
+#define AFE_SECURE_MASK_CONN137_3 (0x4ac0)
+#define AFE_SECURE_MASK_CONN137_4 (0x4ac4)
+#define AFE_SECURE_MASK_CONN138 (0x4ac8)
+#define AFE_SECURE_MASK_CONN138_1 (0x4acc)
+#define AFE_SECURE_MASK_CONN138_2 (0x4ad0)
+#define AFE_SECURE_MASK_CONN138_3 (0x4ad4)
+#define AFE_SECURE_MASK_CONN138_4 (0x4ad8)
+#define AFE_SECURE_MASK_CONN139 (0x4adc)
+#define AFE_SECURE_MASK_CONN139_1 (0x4ae0)
+#define AFE_SECURE_MASK_CONN139_2 (0x4ae4)
+#define AFE_SECURE_MASK_CONN139_3 (0x4ae8)
+#define AFE_SECURE_MASK_CONN139_4 (0x4aec)
+#define AFE_SECURE_MASK_CONN_RS (0x4af0)
+#define AFE_SECURE_MASK_CONN_RS_1 (0x4af4)
+#define AFE_SECURE_MASK_CONN_RS_2 (0x4af8)
+#define AFE_SECURE_MASK_CONN_RS_3 (0x4afc)
+#define AFE_SECURE_MASK_CONN_RS_4 (0x4b00)
+#define AFE_SECURE_MASK_CONN_16BIT (0x4b04)
+#define AFE_SECURE_MASK_CONN_16BIT_1 (0x4b08)
+#define AFE_SECURE_MASK_CONN_16BIT_2 (0x4b0c)
+#define AFE_SECURE_MASK_CONN_16BIT_3 (0x4b10)
+#define AFE_SECURE_MASK_CONN_16BIT_4 (0x4b14)
+#define AFE_SECURE_MASK_CONN_24BIT (0x4b18)
+#define AFE_SECURE_MASK_CONN_24BIT_1 (0x4b1c)
+#define AFE_SECURE_MASK_CONN_24BIT_2 (0x4b20)
+#define AFE_SECURE_MASK_CONN_24BIT_3 (0x4b24)
+#define AFE_SECURE_MASK_CONN_24BIT_4 (0x4b28)
+#define AFE_SECURE_MASK_CONN0_5 (0x4b2c)
+#define AFE_SECURE_MASK_CONN1_5 (0x4b30)
+#define AFE_SECURE_MASK_CONN2_5 (0x4b34)
+#define AFE_SECURE_MASK_CONN3_5 (0x4b38)
+#define AFE_SECURE_MASK_CONN4_5 (0x4b3c)
+#define AFE_SECURE_MASK_CONN5_5 (0x4b40)
+#define AFE_SECURE_MASK_CONN6_5 (0x4b44)
+#define AFE_SECURE_MASK_CONN7_5 (0x4b48)
+#define AFE_SECURE_MASK_CONN8_5 (0x4b4c)
+#define AFE_SECURE_MASK_CONN9_5 (0x4b50)
+#define AFE_SECURE_MASK_CONN10_5 (0x4b54)
+#define AFE_SECURE_MASK_CONN11_5 (0x4b58)
+#define AFE_SECURE_MASK_CONN12_5 (0x4b5c)
+#define AFE_SECURE_MASK_CONN13_5 (0x4b60)
+#define AFE_SECURE_MASK_CONN14_5 (0x4b64)
+#define AFE_SECURE_MASK_CONN15_5 (0x4b68)
+#define AFE_SECURE_MASK_CONN16_5 (0x4b6c)
+#define AFE_SECURE_MASK_CONN17_5 (0x4b70)
+#define AFE_SECURE_MASK_CONN18_5 (0x4b74)
+#define AFE_SECURE_MASK_CONN19_5 (0x4b78)
+#define AFE_SECURE_MASK_CONN20_5 (0x4b7c)
+#define AFE_SECURE_MASK_CONN21_5 (0x4b80)
+#define AFE_SECURE_MASK_CONN22_5 (0x4b84)
+#define AFE_SECURE_MASK_CONN23_5 (0x4b88)
+#define AFE_SECURE_MASK_CONN24_5 (0x4b8c)
+#define AFE_SECURE_MASK_CONN25_5 (0x4b90)
+#define AFE_SECURE_MASK_CONN26_5 (0x4b94)
+#define AFE_SECURE_MASK_CONN27_5 (0x4b98)
+#define AFE_SECURE_MASK_CONN28_5 (0x4b9c)
+#define AFE_SECURE_MASK_CONN29_5 (0x4ba0)
+#define AFE_SECURE_MASK_CONN30_5 (0x4ba4)
+#define AFE_SECURE_MASK_CONN31_5 (0x4ba8)
+#define AFE_SECURE_MASK_CONN32_5 (0x4bac)
+#define AFE_SECURE_MASK_CONN33_5 (0x4bb0)
+#define AFE_SECURE_MASK_CONN34_5 (0x4bb4)
+#define AFE_SECURE_MASK_CONN35_5 (0x4bb8)
+#define AFE_SECURE_MASK_CONN36_5 (0x4bbc)
+#define AFE_SECURE_MASK_CONN37_5 (0x4bc0)
+#define AFE_SECURE_MASK_CONN38_5 (0x4bc4)
+#define AFE_SECURE_MASK_CONN39_5 (0x4bc8)
+#define AFE_SECURE_MASK_CONN40_5 (0x4bcc)
+#define AFE_SECURE_MASK_CONN41_5 (0x4bd0)
+#define AFE_SECURE_MASK_CONN42_5 (0x4bd4)
+#define AFE_SECURE_MASK_CONN43_5 (0x4bd8)
+#define AFE_SECURE_MASK_CONN44_5 (0x4bdc)
+#define AFE_SECURE_MASK_CONN45_5 (0x4be0)
+#define AFE_SECURE_MASK_CONN46_5 (0x4be4)
+#define AFE_SECURE_MASK_CONN47_5 (0x4be8)
+#define AFE_SECURE_MASK_CONN48_5 (0x4bec)
+#define AFE_SECURE_MASK_CONN49_5 (0x4bf0)
+#define AFE_SECURE_MASK_CONN50_5 (0x4bf4)
+#define AFE_SECURE_MASK_CONN51_5 (0x4bf8)
+#define AFE_SECURE_MASK_CONN52_5 (0x4bfc)
+#define AFE_GASRC0_NEW_CON0 (0x4c40)
+#define AFE_GASRC0_NEW_CON1 (0x4c44)
+#define AFE_GASRC0_NEW_CON2 (0x4c48)
+#define AFE_GASRC0_NEW_CON3 (0x4c4c)
+#define AFE_GASRC0_NEW_CON4 (0x4c50)
+#define AFE_GASRC0_NEW_CON5 (0x4c54)
+#define AFE_GASRC0_NEW_CON6 (0x4c58)
+#define AFE_GASRC0_NEW_CON7 (0x4c5c)
+#define AFE_GASRC0_NEW_CON8 (0x4c60)
+#define AFE_GASRC0_NEW_CON9 (0x4c64)
+#define AFE_GASRC0_NEW_CON10 (0x4c68)
+#define AFE_GASRC0_NEW_CON11 (0x4c6c)
+#define AFE_GASRC0_NEW_CON12 (0x4c70)
+#define AFE_GASRC0_NEW_CON13 (0x4c74)
+#define AFE_GASRC0_NEW_CON14 (0x4c78)
+#define AFE_GASRC1_NEW_CON0 (0x4c80)
+#define AFE_GASRC1_NEW_CON1 (0x4c84)
+#define AFE_GASRC1_NEW_CON2 (0x4c88)
+#define AFE_GASRC1_NEW_CON3 (0x4c8c)
+#define AFE_GASRC1_NEW_CON4 (0x4c90)
+#define AFE_GASRC1_NEW_CON5 (0x4c94)
+#define AFE_GASRC1_NEW_CON6 (0x4c98)
+#define AFE_GASRC1_NEW_CON7 (0x4c9c)
+#define AFE_GASRC1_NEW_CON8 (0x4ca0)
+#define AFE_GASRC1_NEW_CON9 (0x4ca4)
+#define AFE_GASRC1_NEW_CON10 (0x4ca8)
+#define AFE_GASRC1_NEW_CON11 (0x4cac)
+#define AFE_GASRC1_NEW_CON12 (0x4cb0)
+#define AFE_GASRC1_NEW_CON13 (0x4cb4)
+#define AFE_GASRC1_NEW_CON14 (0x4cb8)
+#define AFE_GASRC2_NEW_CON0 (0x4cc0)
+#define AFE_GASRC2_NEW_CON1 (0x4cc4)
+#define AFE_GASRC2_NEW_CON2 (0x4cc8)
+#define AFE_GASRC2_NEW_CON3 (0x4ccc)
+#define AFE_GASRC2_NEW_CON4 (0x4cd0)
+#define AFE_GASRC2_NEW_CON5 (0x4cd4)
+#define AFE_GASRC2_NEW_CON6 (0x4cd8)
+#define AFE_GASRC2_NEW_CON7 (0x4cdc)
+#define AFE_GASRC2_NEW_CON8 (0x4ce0)
+#define AFE_GASRC2_NEW_CON9 (0x4ce4)
+#define AFE_GASRC2_NEW_CON10 (0x4ce8)
+#define AFE_GASRC2_NEW_CON11 (0x4cec)
+#define AFE_GASRC2_NEW_CON12 (0x4cf0)
+#define AFE_GASRC2_NEW_CON13 (0x4cf4)
+#define AFE_GASRC2_NEW_CON14 (0x4cf8)
+#define AFE_GASRC3_NEW_CON0 (0x4d00)
+#define AFE_GASRC3_NEW_CON1 (0x4d04)
+#define AFE_GASRC3_NEW_CON2 (0x4d08)
+#define AFE_GASRC3_NEW_CON3 (0x4d0c)
+#define AFE_GASRC3_NEW_CON4 (0x4d10)
+#define AFE_GASRC3_NEW_CON5 (0x4d14)
+#define AFE_GASRC3_NEW_CON6 (0x4d18)
+#define AFE_GASRC3_NEW_CON7 (0x4d1c)
+#define AFE_GASRC3_NEW_CON8 (0x4d20)
+#define AFE_GASRC3_NEW_CON9 (0x4d24)
+#define AFE_GASRC3_NEW_CON10 (0x4d28)
+#define AFE_GASRC3_NEW_CON11 (0x4d2c)
+#define AFE_GASRC3_NEW_CON12 (0x4d30)
+#define AFE_GASRC3_NEW_CON13 (0x4d34)
+#define AFE_GASRC3_NEW_CON14 (0x4d38)
+#define AFE_GASRC4_NEW_CON0 (0x4d40)
+#define AFE_GASRC4_NEW_CON1 (0x4d44)
+#define AFE_GASRC4_NEW_CON2 (0x4d48)
+#define AFE_GASRC4_NEW_CON3 (0x4d4c)
+#define AFE_GASRC4_NEW_CON4 (0x4d50)
+#define AFE_GASRC4_NEW_CON5 (0x4d54)
+#define AFE_GASRC4_NEW_CON6 (0x4d58)
+#define AFE_GASRC4_NEW_CON7 (0x4d5c)
+#define AFE_GASRC4_NEW_CON8 (0x4d60)
+#define AFE_GASRC4_NEW_CON9 (0x4d64)
+#define AFE_GASRC4_NEW_CON10 (0x4d68)
+#define AFE_GASRC4_NEW_CON11 (0x4d6c)
+#define AFE_GASRC4_NEW_CON12 (0x4d70)
+#define AFE_GASRC4_NEW_CON13 (0x4d74)
+#define AFE_GASRC4_NEW_CON14 (0x4d78)
+#define AFE_GASRC5_NEW_CON0 (0x4d80)
+#define AFE_GASRC5_NEW_CON1 (0x4d84)
+#define AFE_GASRC5_NEW_CON2 (0x4d88)
+#define AFE_GASRC5_NEW_CON3 (0x4d8c)
+#define AFE_GASRC5_NEW_CON4 (0x4d90)
+#define AFE_GASRC5_NEW_CON5 (0x4d94)
+#define AFE_GASRC5_NEW_CON6 (0x4d98)
+#define AFE_GASRC5_NEW_CON7 (0x4d9c)
+#define AFE_GASRC5_NEW_CON8 (0x4da0)
+#define AFE_GASRC5_NEW_CON9 (0x4da4)
+#define AFE_GASRC5_NEW_CON10 (0x4da8)
+#define AFE_GASRC5_NEW_CON11 (0x4dac)
+#define AFE_GASRC5_NEW_CON12 (0x4db0)
+#define AFE_GASRC5_NEW_CON13 (0x4db4)
+#define AFE_GASRC5_NEW_CON14 (0x4db8)
+#define AFE_GASRC6_NEW_CON0 (0x4dc0)
+#define AFE_GASRC6_NEW_CON1 (0x4dc4)
+#define AFE_GASRC6_NEW_CON2 (0x4dc8)
+#define AFE_GASRC6_NEW_CON3 (0x4dcc)
+#define AFE_GASRC6_NEW_CON4 (0x4dd0)
+#define AFE_GASRC6_NEW_CON5 (0x4dd4)
+#define AFE_GASRC6_NEW_CON6 (0x4dd8)
+#define AFE_GASRC6_NEW_CON7 (0x4ddc)
+#define AFE_GASRC6_NEW_CON8 (0x4de0)
+#define AFE_GASRC6_NEW_CON9 (0x4de4)
+#define AFE_GASRC6_NEW_CON10 (0x4de8)
+#define AFE_GASRC6_NEW_CON11 (0x4dec)
+#define AFE_GASRC6_NEW_CON12 (0x4df0)
+#define AFE_GASRC6_NEW_CON13 (0x4df4)
+#define AFE_GASRC6_NEW_CON14 (0x4df8)
+#define AFE_GASRC7_NEW_CON0 (0x4e00)
+#define AFE_GASRC7_NEW_CON1 (0x4e04)
+#define AFE_GASRC7_NEW_CON2 (0x4e08)
+#define AFE_GASRC7_NEW_CON3 (0x4e0c)
+#define AFE_GASRC7_NEW_CON4 (0x4e10)
+#define AFE_GASRC7_NEW_CON5 (0x4e14)
+#define AFE_GASRC7_NEW_CON6 (0x4e18)
+#define AFE_GASRC7_NEW_CON7 (0x4e1c)
+#define AFE_GASRC7_NEW_CON8 (0x4e20)
+#define AFE_GASRC7_NEW_CON9 (0x4e24)
+#define AFE_GASRC7_NEW_CON10 (0x4e28)
+#define AFE_GASRC7_NEW_CON11 (0x4e2c)
+#define AFE_GASRC7_NEW_CON12 (0x4e30)
+#define AFE_GASRC7_NEW_CON13 (0x4e34)
+#define AFE_GASRC7_NEW_CON14 (0x4e38)
+#define AFE_GASRC8_NEW_CON0 (0x4e40)
+#define AFE_GASRC8_NEW_CON1 (0x4e44)
+#define AFE_GASRC8_NEW_CON2 (0x4e48)
+#define AFE_GASRC8_NEW_CON3 (0x4e4c)
+#define AFE_GASRC8_NEW_CON4 (0x4e50)
+#define AFE_GASRC8_NEW_CON5 (0x4e54)
+#define AFE_GASRC8_NEW_CON6 (0x4e58)
+#define AFE_GASRC8_NEW_CON7 (0x4e5c)
+#define AFE_GASRC8_NEW_CON8 (0x4e60)
+#define AFE_GASRC8_NEW_CON9 (0x4e64)
+#define AFE_GASRC8_NEW_CON10 (0x4e68)
+#define AFE_GASRC8_NEW_CON11 (0x4e6c)
+#define AFE_GASRC8_NEW_CON12 (0x4e70)
+#define AFE_GASRC8_NEW_CON13 (0x4e74)
+#define AFE_GASRC8_NEW_CON14 (0x4e78)
+#define AFE_GASRC9_NEW_CON0 (0x4e80)
+#define AFE_GASRC9_NEW_CON1 (0x4e84)
+#define AFE_GASRC9_NEW_CON2 (0x4e88)
+#define AFE_GASRC9_NEW_CON3 (0x4e8c)
+#define AFE_GASRC9_NEW_CON4 (0x4e90)
+#define AFE_GASRC9_NEW_CON5 (0x4e94)
+#define AFE_GASRC9_NEW_CON6 (0x4e98)
+#define AFE_GASRC9_NEW_CON7 (0x4e9c)
+#define AFE_GASRC9_NEW_CON8 (0x4ea0)
+#define AFE_GASRC9_NEW_CON9 (0x4ea4)
+#define AFE_GASRC9_NEW_CON10 (0x4ea8)
+#define AFE_GASRC9_NEW_CON11 (0x4eac)
+#define AFE_GASRC9_NEW_CON12 (0x4eb0)
+#define AFE_GASRC9_NEW_CON13 (0x4eb4)
+#define AFE_GASRC9_NEW_CON14 (0x4eb8)
+#define AFE_GASRC10_NEW_CON0 (0x4ec0)
+#define AFE_GASRC10_NEW_CON1 (0x4ec4)
+#define AFE_GASRC10_NEW_CON2 (0x4ec8)
+#define AFE_GASRC10_NEW_CON3 (0x4ecc)
+#define AFE_GASRC10_NEW_CON4 (0x4ed0)
+#define AFE_GASRC10_NEW_CON5 (0x4ed4)
+#define AFE_GASRC10_NEW_CON6 (0x4ed8)
+#define AFE_GASRC10_NEW_CON7 (0x4edc)
+#define AFE_GASRC10_NEW_CON8 (0x4ee0)
+#define AFE_GASRC10_NEW_CON9 (0x4ee4)
+#define AFE_GASRC10_NEW_CON10 (0x4ee8)
+#define AFE_GASRC10_NEW_CON11 (0x4eec)
+#define AFE_GASRC10_NEW_CON12 (0x4ef0)
+#define AFE_GASRC10_NEW_CON13 (0x4ef4)
+#define AFE_GASRC10_NEW_CON14 (0x4ef8)
+#define AFE_GASRC11_NEW_CON0 (0x4f00)
+#define AFE_GASRC11_NEW_CON1 (0x4f04)
+#define AFE_GASRC11_NEW_CON2 (0x4f08)
+#define AFE_GASRC11_NEW_CON3 (0x4f0c)
+#define AFE_GASRC11_NEW_CON4 (0x4f10)
+#define AFE_GASRC11_NEW_CON5 (0x4f14)
+#define AFE_GASRC11_NEW_CON6 (0x4f18)
+#define AFE_GASRC11_NEW_CON7 (0x4f1c)
+#define AFE_GASRC11_NEW_CON8 (0x4f20)
+#define AFE_GASRC11_NEW_CON9 (0x4f24)
+#define AFE_GASRC11_NEW_CON10 (0x4f28)
+#define AFE_GASRC11_NEW_CON11 (0x4f2c)
+#define AFE_GASRC11_NEW_CON12 (0x4f30)
+#define AFE_GASRC11_NEW_CON13 (0x4f34)
+#define AFE_GASRC11_NEW_CON14 (0x4f38)
+
+#define AFE_IEC_BURST_INFO_MON (0x64b0)
+#define AFE_SPDIFOUT_IP_VERSION (0x64b4)
+#define AFE_SPDIF_OUT_CFG0 (0x64b8)
+#define AFE_SPDIF_OUT_CFG1 (0x64bc)
+#define AFE_SPDIF_OUT_CHSTS1 (0x64c0)
+#define AFE_SPDIF_OUT_CHSTS2 (0x64c4)
+#define AFE_SPDIF_OUT_CHSTS3 (0x64c8)
+#define AFE_SPDIF_OUT_CHSTS4 (0x64cc)
+#define AFE_SPDIF_OUT_CHSTS5 (0x64d0)
+#define AFE_SPDIF_OUT_CHSTS6 (0x64d4)
+#define AFE_SPDIF_OUT_USERCODE1 (0x64d8)
+#define AFE_SPDIF_OUT_USERCODE2 (0x64dc)
+#define AFE_SPDIF_OUT_USERCODE3 (0x64e0)
+#define AFE_SPDIF_OUT_USERCODE4 (0x64e4)
+#define AFE_SPDIF_OUT_USERCODE5 (0x64e8)
+#define AFE_SPDIF_OUT_USERCODE6 (0x64ec)
+#define AFE_SPDIF_OUT_BURST_PRE0 (0x64f0)
+#define AFE_SPDIF_OUT_BURST_PRE1 (0x64f4)
+#define AFE_SPDIF_OUT_MON0 (0x64f8)
+#define AFE_SPDIF_OUT_MON1 (0x64fc)
+#define AFE_SPDIF_OUT_MON2 (0x6500)
+#define AFE_SPDIF_OUT_MON3 (0x6504)
+#define AFE_SPDIF_OUT_MON4 (0x6508)
+#define AFE_SPDIF_OUT_MON5 (0x650c)
+#define AFE_CONN0_6 (0x7000)
+#define AFE_CONN1_6 (0x7004)
+#define AFE_CONN2_6 (0x7008)
+#define AFE_CONN3_6 (0x700c)
+#define AFE_CONN4_6 (0x7010)
+#define AFE_CONN5_6 (0x7014)
+#define AFE_CONN6_6 (0x7018)
+#define AFE_CONN7_6 (0x701c)
+#define AFE_CONN8_6 (0x7020)
+#define AFE_CONN9_6 (0x7024)
+#define AFE_CONN10_6 (0x7028)
+#define AFE_CONN11_6 (0x702c)
+#define AFE_CONN12_6 (0x7030)
+#define AFE_CONN13_6 (0x7034)
+#define AFE_CONN14_6 (0x7038)
+#define AFE_CONN15_6 (0x703c)
+#define AFE_CONN16_6 (0x7040)
+#define AFE_CONN17_6 (0x7044)
+#define AFE_CONN18_6 (0x7048)
+#define AFE_CONN19_6 (0x704c)
+#define AFE_CONN20_6 (0x7050)
+#define AFE_CONN21_6 (0x7054)
+#define AFE_CONN22_6 (0x7058)
+#define AFE_CONN23_6 (0x705c)
+#define AFE_CONN24_6 (0x7060)
+#define AFE_CONN25_6 (0x7064)
+#define AFE_CONN26_6 (0x7068)
+#define AFE_CONN27_6 (0x706c)
+#define AFE_CONN28_6 (0x7070)
+#define AFE_CONN29_6 (0x7074)
+#define AFE_CONN30_6 (0x7078)
+#define AFE_CONN31_6 (0x707c)
+#define AFE_CONN32_6 (0x7080)
+#define AFE_CONN33_6 (0x7084)
+#define AFE_CONN34_6 (0x7088)
+#define AFE_CONN35_6 (0x708c)
+#define AFE_CONN36_6 (0x7090)
+#define AFE_CONN37_6 (0x7094)
+#define AFE_CONN38_6 (0x7098)
+#define AFE_CONN39_6 (0x709c)
+#define AFE_CONN40_6 (0x70a0)
+#define AFE_CONN41_6 (0x70a4)
+#define AFE_CONN42_6 (0x70a8)
+#define AFE_CONN43_6 (0x70ac)
+#define AFE_CONN44_6 (0x70b0)
+#define AFE_CONN45_6 (0x70b4)
+#define AFE_CONN46_6 (0x70b8)
+#define AFE_CONN47_6 (0x70bc)
+#define AFE_CONN48_6 (0x70c0)
+#define AFE_CONN49_6 (0x70c4)
+#define AFE_CONN50_6 (0x70c8)
+#define AFE_CONN51_6 (0x70cc)
+#define AFE_CONN52_6 (0x70d0)
+#define AFE_CONN53_6 (0x70d4)
+#define AFE_CONN54_6 (0x70d8)
+#define AFE_CONN55_6 (0x70dc)
+#define AFE_CONN56_6 (0x70e0)
+#define AFE_CONN57_6 (0x70e4)
+#define AFE_CONN58_6 (0x70e8)
+#define AFE_CONN59_6 (0x70ec)
+#define AFE_CONN60_6 (0x70f0)
+#define AFE_CONN61_6 (0x70f4)
+#define AFE_CONN62_6 (0x70f8)
+#define AFE_CONN63_6 (0x70fc)
+#define AFE_CONN64_6 (0x7100)
+#define AFE_CONN65_6 (0x7104)
+#define AFE_CONN66_6 (0x7108)
+#define AFE_CONN67_6 (0x710c)
+#define AFE_CONN68_6 (0x7110)
+#define AFE_CONN69_6 (0x7114)
+#define AFE_CONN70_6 (0x7118)
+#define AFE_CONN71_6 (0x711c)
+#define AFE_CONN72_6 (0x7120)
+#define AFE_CONN73_6 (0x7124)
+#define AFE_CONN74_6 (0x7128)
+#define AFE_CONN75_6 (0x712c)
+#define AFE_CONN76_6 (0x7130)
+#define AFE_CONN77_6 (0x7134)
+#define AFE_CONN78_6 (0x7138)
+#define AFE_CONN79_6 (0x713c)
+#define AFE_CONN80_6 (0x7140)
+#define AFE_CONN81_6 (0x7144)
+#define AFE_CONN82_6 (0x7148)
+#define AFE_CONN83_6 (0x714c)
+#define AFE_CONN84_6 (0x7150)
+#define AFE_CONN85_6 (0x7154)
+#define AFE_CONN86_6 (0x7158)
+#define AFE_CONN87_6 (0x715c)
+#define AFE_CONN88_6 (0x7160)
+#define AFE_CONN89_6 (0x7164)
+#define AFE_CONN90_6 (0x7168)
+#define AFE_CONN91_6 (0x716c)
+#define AFE_CONN92_6 (0x7170)
+#define AFE_CONN93_6 (0x7174)
+#define AFE_CONN94_6 (0x7178)
+#define AFE_CONN95_6 (0x717c)
+#define AFE_CONN96_6 (0x7180)
+#define AFE_CONN97_6 (0x7184)
+#define AFE_CONN98_6 (0x7188)
+#define AFE_CONN99_6 (0x718c)
+#define AFE_CONN100_6 (0x7190)
+#define AFE_CONN101_6 (0x7194)
+#define AFE_CONN102_6 (0x7198)
+#define AFE_CONN103_6 (0x719c)
+#define AFE_CONN104_6 (0x71a0)
+#define AFE_CONN105_6 (0x71a4)
+#define AFE_CONN106_6 (0x71a8)
+#define AFE_CONN107_6 (0x71ac)
+#define AFE_CONN108_6 (0x71b0)
+#define AFE_CONN109_6 (0x71b4)
+#define AFE_CONN110_6 (0x71b8)
+#define AFE_CONN111_6 (0x71bc)
+#define AFE_CONN112_6 (0x71c0)
+#define AFE_CONN113_6 (0x71c4)
+#define AFE_CONN114_6 (0x71c8)
+#define AFE_CONN115_6 (0x71cc)
+#define AFE_CONN116_6 (0x71d0)
+#define AFE_CONN117_6 (0x71d4)
+#define AFE_CONN118_6 (0x71d8)
+#define AFE_CONN119_6 (0x71dc)
+#define AFE_CONN120_6 (0x71e0)
+#define AFE_CONN121_6 (0x71e4)
+#define AFE_CONN122_6 (0x71e8)
+#define AFE_CONN123_6 (0x71ec)
+#define AFE_CONN124_6 (0x71f0)
+#define AFE_CONN125_6 (0x71f4)
+#define AFE_CONN126_6 (0x71f8)
+#define AFE_CONN127_6 (0x71fc)
+#define AFE_CONN128_6 (0x7200)
+#define AFE_CONN129_6 (0x7204)
+#define AFE_CONN130_6 (0x7208)
+#define AFE_CONN131_6 (0x720c)
+#define AFE_CONN132_6 (0x7210)
+#define AFE_CONN133_6 (0x7214)
+#define AFE_CONN134_6 (0x7218)
+#define AFE_CONN135_6 (0x721c)
+#define AFE_CONN136_6 (0x7220)
+#define AFE_CONN137_6 (0x7224)
+#define AFE_CONN138_6 (0x7228)
+#define AFE_CONN139_6 (0x722c)
+#define AFE_CONN176_6 (0x72c0)
+#define AFE_CONN177_6 (0x72c4)
+#define AFE_CONN182_6 (0x72d8)
+#define AFE_CONN183_6 (0x72dc)
+
+#define AFE_MAX_REGISTER (AFE_CONN183_6)
+
+/* PWR1_ASM_CON1 */
+#define PWR1_ASM_CON1_GASRC0_CALI_CK_SEL_MASK BIT(2)
+#define PWR1_ASM_CON1_GASRC1_CALI_CK_SEL_MASK BIT(5)
+#define PWR1_ASM_CON1_GASRC2_CALI_CK_SEL_MASK BIT(20)
+#define PWR1_ASM_CON1_GASRC3_CALI_CK_SEL_MASK BIT(23)
+
+/* PWR1_ASM_CON2 */
+#define PWR1_ASM_CON2_GASRC4_CALI_CK_SEL_MASK BIT(2)
+#define PWR1_ASM_CON2_GASRC5_CALI_CK_SEL_MASK BIT(7)
+#define PWR1_ASM_CON2_GASRC6_CALI_CK_SEL_MASK BIT(12)
+#define PWR1_ASM_CON2_GASRC7_CALI_CK_SEL_MASK BIT(17)
+#define PWR1_ASM_CON2_GASRC8_CALI_CK_SEL_MASK BIT(22)
+#define PWR1_ASM_CON2_GASRC9_CALI_CK_SEL_MASK BIT(27)
+
+/* PWR1_ASM_CON3 */
+#define PWR1_ASM_CON3_GASRC10_CALI_CK_SEL_MASK BIT(2)
+#define PWR1_ASM_CON3_GASRC11_CALI_CK_SEL_MASK BIT(7)
+
+/* AUDIO_TOP_CON0 */
+#define AUDIO_TOP_CON0_PDN_AFE BIT(2)
+#define AUDIO_TOP_CON0_PDN_APLL BIT(23)
+#define AUDIO_TOP_CON0_PDN_APLL_TUNER BIT(19)
+#define AUDIO_TOP_CON0_PDN_APLL2 BIT(24)
+#define AUDIO_TOP_CON0_PDN_APLL2_TUNER BIT(20)
+#define AUDIO_TOP_CON0_PDN_DAC BIT(25)
+#define AUDIO_TOP_CON0_PDN_DAC_HIRES BIT(31)
+#define AUDIO_TOP_CON0_PDN_DAC_PREDIS BIT(26)
+#define AUDIO_TOP_CON0_PDN_SPDIFIN_TUNER BIT(10)
+#define AUDIO_TOP_CON0_PDN_ADC BIT(28)
+#define AUDIO_TOP_CON0_PDN_SPDF BIT(21)
+#define AUDIO_TOP_CON0_PDN_TML BIT(27)
+#define AUDIO_TOP_CON0_PDN_UL_TML BIT(18)
+
+/* AUDIO_TOP_CON1 */
+#define AUDIO_TOP_CON1_PDN_ADC_HIRES BIT(17)
+#define AUDIO_TOP_CON1_PDN_ADDA6_ADC BIT(18)
+#define AUDIO_TOP_CON1_PDN_ADDA6_HIRES BIT(19)
+#define AUDIO_TOP_CON1_PDN_UL_TML_HIRES BIT(16)
+#define AUDIO_TOP_CON1_PDN_DMIC_TML BIT(14)
+#define AUDIO_TOP_CON1_PDN_A1SYS_HOPING BIT(2)
+#define AUDIO_TOP_CON1_PDN_DMIC0 BIT(10)
+#define AUDIO_TOP_CON1_PDN_DMIC1 BIT(11)
+#define AUDIO_TOP_CON1_PDN_DMIC2 BIT(12)
+#define AUDIO_TOP_CON1_PDN_DMIC3 BIT(13)
+
+/* AUDIO_TOP_CON3 */
+#define AUDIO_TOP_CON3_PDN_EARC_TUNER BIT(7)
+#define AUDIO_TOP_CON3_PDN_LINEIN_TUNER BIT(5)
+
+/* AUDIO_TOP_CON4 */
+#define AUDIO_TOP_CON4_PDN_I2S_IN BIT(0)
+#define AUDIO_TOP_CON4_PDN_TDM_IN BIT(1)
+#define AUDIO_TOP_CON4_PDN_I2S_OUT BIT(6)
+#define AUDIO_TOP_CON4_PDN_TDM_OUT BIT(7)
+#define AUDIO_TOP_CON4_PDN_HDMI_OUT BIT(8)
+#define AUDIO_TOP_CON4_PDN_ASRC11 BIT(16)
+#define AUDIO_TOP_CON4_PDN_ASRC12 BIT(17)
+#define AUDIO_TOP_CON4_PDN_A1SYS BIT(21)
+#define AUDIO_TOP_CON4_PDN_A2SYS BIT(22)
+#define AUDIO_TOP_CON4_PDN_A3SYS BIT(30)
+#define AUDIO_TOP_CON4_PDN_A4SYS BIT(31)
+#define AUDIO_TOP_CON4_PDN_PCMIF BIT(24)
+#define AUDIO_TOP_CON4_PDN_INTDIR BIT(20)
+#define AUDIO_TOP_CON4_PDN_MULTI_IN BIT(19)
+
+/* AUDIO_TOP_CON6 */
+#define AUDIO_TOP_CON6_PDN_GASRC11 BIT(11)
+#define AUDIO_TOP_CON6_PDN_GASRC10 BIT(10)
+#define AUDIO_TOP_CON6_PDN_GASRC9 BIT(9)
+#define AUDIO_TOP_CON6_PDN_GASRC8 BIT(8)
+#define AUDIO_TOP_CON6_PDN_GASRC7 BIT(7)
+#define AUDIO_TOP_CON6_PDN_GASRC6 BIT(6)
+#define AUDIO_TOP_CON6_PDN_GASRC5 BIT(5)
+#define AUDIO_TOP_CON6_PDN_GASRC4 BIT(4)
+#define AUDIO_TOP_CON6_PDN_GASRC3 BIT(3)
+#define AUDIO_TOP_CON6_PDN_GASRC2 BIT(2)
+#define AUDIO_TOP_CON6_PDN_GASRC1 BIT(1)
+#define AUDIO_TOP_CON6_PDN_GASRC0 BIT(0)
+
+/* AFE_GAINx_CON0 */
+#define AFE_GAIN_CON0_SAMPLE_PER_STEP_MASK GENMASK(15, 8)
+#define AFE_GAIN_CON0_GAIN_MODE_MASK GENMASK(7, 3)
+#define AFE_GAIN_CON0_GAIN_ON_MASK BIT(0)
+
+/* AFE_GAINx_CON1 */
+#define AFE_GAIN_CON1_TARGET_MASK GENMASK(19, 0)
+
+/* AFE_GAINx_CON2 */
+#define AFE_GAIN_CON2_DOWN_STEP_MASK GENMASK(19, 0)
+
+/* AFE_GAINx_CON3 */
+#define AFE_GAIN_CON3_UP_STEP_MASK GENMASK(19, 0)
+
+/* AFE_GAINx_CUR */
+#define AFE_GAIN_CUR_GAIN_MASK GENMASK(19, 0)
+
+/* ASYS_TOP_CON */
+#define ASYS_TOP_CON_A1SYS_TIMING_ON BIT(0)
+#define ASYS_TOP_CON_A2SYS_TIMING_ON BIT(1)
+#define ASYS_TOP_CON_A3SYS_TIMING_ON BIT(4)
+#define ASYS_TOP_CON_A4SYS_TIMING_ON BIT(5)
+#define ASYS_TOP_CON_26M_TIMING_ON BIT(2)
+
+/* PWR2_TOP_CON0 */
+#define PWR2_TOP_CON_DMIC8_SRC_SEL_MASK GENMASK(31, 29)
+#define PWR2_TOP_CON_DMIC7_SRC_SEL_MASK GENMASK(28, 26)
+#define PWR2_TOP_CON_DMIC6_SRC_SEL_MASK GENMASK(25, 23)
+#define PWR2_TOP_CON_DMIC5_SRC_SEL_MASK GENMASK(22, 20)
+#define PWR2_TOP_CON_DMIC4_SRC_SEL_MASK GENMASK(19, 17)
+#define PWR2_TOP_CON_DMIC3_SRC_SEL_MASK GENMASK(16, 14)
+#define PWR2_TOP_CON_DMIC2_SRC_SEL_MASK GENMASK(13, 11)
+#define PWR2_TOP_CON_DMIC1_SRC_SEL_MASK GENMASK(10, 8)
+
+/* PWR2_TOP_CON1 */
+#define PWR2_TOP_CON1_DMIC_CKDIV_ON BIT(1)
+
+/* PCM_INTF_CON1 */
+#define PCM_INTF_CON1_SYNC_OUT_INV BIT(23)
+#define PCM_INTF_CON1_BCLK_OUT_INV BIT(22)
+#define PCM_INTF_CON1_CLK_OUT_INV_MASK GENMASK(23, 22)
+#define PCM_INTF_CON1_SYNC_IN_INV BIT(21)
+#define PCM_INTF_CON1_BCLK_IN_INV BIT(20)
+#define PCM_INTF_CON1_CLK_IN_INV_MASK GENMASK(21, 20)
+#define PCM_INTF_CON1_PCM_24BIT BIT(16)
+#define PCM_INTF_CON1_PCM_16BIT (0 << 16)
+#define PCM_INTF_CON1_PCM_BIT_MASK BIT(16)
+#define PCM_INTF_CON1_PCM_WLEN_32BCK (0 << 14)
+#define PCM_INTF_CON1_PCM_WLEN_64BCK BIT(14)
+#define PCM_INTF_CON1_PCM_WLEN_MASK BIT(14)
+#define PCM_INTF_CON1_SYNC_LENGTH_MASK GENMASK(13, 9)
+#define PCM_INTF_CON1_PCM_SLAVE BIT(5)
+#define PCM_INTF_CON1_PCM_MASTER (0 << 5)
+#define PCM_INTF_CON1_PCM_M_S_MASK BIT(5)
+#define PCM_INTF_CON1_PCM_MODE_MASK GENMASK(4, 3)
+#define PCM_INTF_CON1_PCM_FMT_MASK GENMASK(2, 1)
+#define PCM_INTF_CON1_PCM_EN BIT(0)
+
+/* PCM_INTF_CON2 */
+#define PCM_INTF_CON2_CLK_DOMAIN_SEL_MASK GENMASK(24, 23)
+#define PCM_INTF_CON2_SYNC_FREQ_MODE_MASK GENMASK(16, 12)
+#define PCM_INTF_CON2_PCM_TX2RX_LPBK BIT(8)
+
+/* AFE_MPHONE_MULTIx_CON0 */
+#define AFE_MPHONE_MULTI_CON0_16BIT_SWAP BIT(3)
+#define AFE_MPHONE_MULTI_CON0_16BIT_SWAP_MASK BIT(3)
+#define AFE_MPHONE_MULTI_CON0_24BIT_DATA (0x1 << 1)
+#define AFE_MPHONE_MULTI_CON0_16BIT_DATA (0x0 << 1)
+#define AFE_MPHONE_MULIT_CON0_24BIT_DATA_MASK BIT(1)
+#define AFE_MPHONE_MULTI_CON0_EN BIT(0)
+
+/* AFE_MPHONE_MULTIx_CON1 */
+#define AFE_MPHONE_MULTI_CON1_SYNC_ON BIT(24)
+#define AFE_MPHONE_MULTI_CON1_24BIT_SWAP_BYPASS BIT(22)
+#define AFE_MPHONE_MULTI_CON1_NON_COMPACT_MODE (0x1 << 19)
+#define AFE_MPHONE_MULTI_CON1_COMPACT_MODE (0x0 << 19)
+#define AFE_MPHONE_MULTI_CON1_NON_COMPACT_MODE_MASK BIT(19)
+#define AFE_MPHONE_MULTI_CON1_HBR_MODE BIT(18)
+#define AFE_MPHONE_MULTI_CON1_LRCK_32_CYCLE (0x2 << 16)
+#define AFE_MPHONE_MULTI_CON1_LRCK_24_CYCLE (0x1 << 16)
+#define AFE_MPHONE_MULTI_CON1_LRCK_16_CYCLE (0x0 << 16)
+#define AFE_MPHONE_MULTI_CON1_LRCK_CYCLE_SEL_MASK GENMASK(17, 16)
+#define AFE_MPHONE_MULTI_CON1_LRCK_INV BIT(15)
+#define AFE_MPHONE_MULTI_CON1_DELAY_DATA BIT(14)
+#define AFE_MPHONE_MULTI_CON1_LEFT_ALIGN BIT(13)
+#define AFE_MPHONE_MULTI_CON1_BIT_NUM_MASK GENMASK(12, 8)
+#define AFE_MPHONE_MULTI_CON1_BCK_INV BIT(6)
+#define AFE_MPHONE_MULTI_CON1_CH_NUM_MASK GENMASK(1, 0)
+
+/* AFE_MPHONE_MULTIx_CON2 */
+#define AFE_MPHONE_MULTI_CON2_SEL_SPDIFIN BIT(19)
+/* AFE_AUD_PAD_TOP */
+#define RG_RX_PROTOCOL2 BIT(3)
+#define RG_RX_FIFO_ON BIT(0)
+
+/* AFE_ADDA_MTKAIF_CFG0 */
+#define MTKAIF_RXIF_CLKINV_ADC BIT(31)
+#define MTKAIF_RXIF_PROTOCOL2 BIT(16)
+#define MTKAIF_TXIF_PROTOCOL2 BIT(4)
+#define MTKAIF_TXIF_8TO5 BIT(2)
+#define MTKAIF_RXIF_8TO5 BIT(1)
+#define MTKAIF_IF_LOOPBACK1 BIT(0)
+
+/* AFE_ADDA_MTKAIF_RX_CFG2 */
+#define MTKAIF_RXIF_DELAY_CYCLE_MASK GENMASK(15, 12)
+#define MTKAIF_RXIF_DELAY_DATA BIT(8)
+
+/* AFE_ADDA_MTKAIF_SYNCWORD_CFG */
+#define ADDA6_MTKAIF_RX_SYNC_WORD2_DISABLE BIT(23)
+
+/* AFE_DMICx_UL_SRC_CON0 */
+#define AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_CH1(x) (((x) & 0x7) << 27)
+#define AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_CH2(x) (((x) & 0x7) << 24)
+#define AFE_DMIC_UL_SRC_CON0_UL_PHASE_SEL_MASK GENMASK(29, 24)
+#define AFE_DMIC_UL_SRC_CON0_UL_TWO_WIRE_MODE_CTL BIT(23)
+#define AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH2_CTL BIT(22)
+#define AFE_DMIC_UL_SRC_CON0_UL_MODE_3P25M_CH1_CTL BIT(21)
+
+#define AFE_DMIC_UL_VOICE_MODE_MASK GENMASK(19, 17)
+#define AFE_DMIC_UL_CON0_VOCIE_MODE_8K AFE_DMIC_UL_VOICE_MODE(0)
+#define AFE_DMIC_UL_CON0_VOCIE_MODE_16K AFE_DMIC_UL_VOICE_MODE(1)
+#define AFE_DMIC_UL_CON0_VOCIE_MODE_32K AFE_DMIC_UL_VOICE_MODE(2)
+#define AFE_DMIC_UL_CON0_VOCIE_MODE_48K AFE_DMIC_UL_VOICE_MODE(3)
+#define AFE_DMIC_UL_CON0_VOCIE_MODE_96K AFE_DMIC_UL_VOICE_MODE(4)
+#define AFE_DMIC_UL_SRC_CON0_UL_IIR_MODE_CTL_MASK GENMASK(9, 7)
+#define AFE_DMIC_UL_SRC_CON0_UL_IIR_ON_TMP_CTL BIT(10)
+#define AFE_DMIC_UL_SRC_CON0_UL_SDM_3_LEVEL_CTL BIT(1)
+#define AFE_DMIC_UL_SRC_CON0_UL_SRC_ON_TMP_CTL BIT(0)
+
+/* DMIC_BYPASS_HW_GAIN */
+#define DMIC_BYPASS_HW_GAIN4_ONE_HEART BIT(10)
+#define DMIC_BYPASS_HW_GAIN3_ONE_HEART BIT(9)
+#define DMIC_BYPASS_HW_GAIN2_ONE_HEART BIT(8)
+#define DMIC_BYPASS_HW_GAIN_DMIC4_BYPASS BIT(4)
+#define DMIC_BYPASS_HW_GAIN_DMIC3_BYPASS BIT(3)
+#define DMIC_BYPASS_HW_GAIN_DMIC2_BYPASS BIT(2)
+#define DMIC_BYPASS_HW_GAIN_DMIC1_BYPASS BIT(1)
+
+/* DMIC_GAINx_CON0 */
+#define DMIC_GAIN_CON0_GAIN_ON BIT(0)
+#define DMIC_GAIN_CON0_SAMPLE_PER_STEP_MASK GENMASK(15, 8)
+
+/* DMIC_GAINx_CON1 */
+#define DMIC_GAIN_CON1_TARGET_MASK GENMASK(27, 0)
+
+/* DMIC_GAINx_CON2 */
+#define DMIC_GAIN_CON2_DOWN_STEP GENMASK(19, 0)
+
+/* DMIC_GAINx_CON3 */
+#define DMIC_GAIN_CON3_UP_STEP GENMASK(19, 0)
+
+/* DMIC_GAINx_CUR */
+#define DMIC_GAIN_CUR_GAIN_MASK GENMASK(27, 0)
+
+/* ETDM_INx_AFIFO_CON */
+#define ETDM_IN_USE_AFIFO BIT(8)
+#define ETDM_IN_AFIFO_CLOCK_MASK GENMASK(7, 5)
+#define ETDM_IN_AFIFO_MODE_MASK GENMASK(4, 0)
+
+/* ETDM_COWORK_CON0 */
+#define ETDM_OUT1_SLAVE_SEL_MASK GENMASK(23, 20)
+#define ETDM_OUT1_SLAVE_SEL_SHIFT 20
+#define ETDM_OUT1_SYNC_SEL_MASK GENMASK(19, 16)
+#define ETDM_OUT1_SYNC_SEL_SHIFT 16
+
+/* ETDM_COWORK_CON1 */
+#define ETDM_IN1_SDATA_SEL_MASK GENMASK(23, 20)
+#define ETDM_IN1_SDATA_SEL_SHIFT 20
+#define ETDM_IN1_SDATA0_SEL_MASK GENMASK(19, 16)
+#define ETDM_IN1_SDATA0_SEL_SHIFT 16
+#define ETDM_IN1_SYNC_SEL_MASK GENMASK(15, 12)
+#define ETDM_IN1_SYNC_SEL_SHIFT 12
+#define ETDM_IN1_SLAVE_SEL_MASK GENMASK(11, 8)
+#define ETDM_IN1_SLAVE_SEL_SHIFT 8
+
+/* ETDM_COWORK_CON2 */
+#define ETDM_IN2_SYNC_SEL_MASK GENMASK(31, 28)
+#define ETDM_IN2_SYNC_SEL_SHIFT 28
+#define ETDM_IN2_SLAVE_SEL_MASK GENMASK(27, 24)
+#define ETDM_IN2_SLAVE_SEL_SHIFT 24
+#define ETDM_OUT3_SLAVE_SEL_MASK GENMASK(23, 20)
+#define ETDM_OUT3_SLAVE_SEL_SHIFT 20
+#define ETDM_OUT3_SYNC_SEL_MASK GENMASK(19, 16)
+#define ETDM_OUT3_SYNC_SEL_SHIFT 16
+#define ETDM_OUT2_SLAVE_SEL_MASK GENMASK(11, 8)
+#define ETDM_OUT2_SLAVE_SEL_SHIFT 8
+#define ETDM_OUT2_SYNC_SEL_MASK GENMASK(7, 4)
+#define ETDM_OUT2_SYNC_SEL_SHIFT 4
+
+/* ETDM_COWORK_CON3 */
+#define ETDM_IN2_SDATA_SEL_MASK GENMASK(7, 4)
+#define ETDM_IN2_SDATA_SEL_SHIFT 4
+#define ETDM_IN2_SDATA0_SEL_MASK GENMASK(3, 0)
+#define ETDM_IN2_SDATA0_SEL_SHIFT 0
+
+/* ETDM_x_CONx */
+#define ETDM_CON0_CH_NUM_MASK GENMASK(27, 23)
+#define ETDM_CON0_WORD_LEN_MASK GENMASK(20, 16)
+#define ETDM_CON0_BIT_LEN_MASK GENMASK(15, 11)
+#define ETDM_CON0_FORMAT_MASK GENMASK(8, 6)
+#define ETDM_CON0_SLAVE_MODE BIT(5)
+#define ETDM_CON0_SYNC_MODE BIT(1)
+#define ETDM_CON0_EN BIT(0)
+
+#define ETDM_OUT_CON0_RELATCH_DOMAIN_MASK GENMASK(29, 28)
+
+#define ETDM_CON1_MCLK_OUTPUT BIT(16)
+
+#define ETDM_IN_CON1_LRCK_AUTO_MODE BIT(31)
+#define ETDM_IN_CON1_LRCK_WIDTH_MASK GENMASK(29, 20)
+
+#define ETDM_OUT_CON1_LRCK_AUTO_MODE BIT(29)
+#define ETDM_OUT_CON1_LRCK_WIDTH_MASK GENMASK(28, 19)
+
+#define ETDM_IN_CON2_MULTI_IP_2CH_MODE BIT(31)
+#define ETDM_IN_CON2_MULTI_IP_TOTAL_CH_MASK GENMASK(19, 15)
+#define ETDM_IN_CON2_CLOCK_MASK GENMASK(12, 10)
+#define ETDM_IN_CON2_CLOCK_SHIFT 10
+#define ETDM_IN_CON2_UPDATE_GAP_MASK GENMASK(9, 5)
+
+#define ETDM_OUT_CON2_LRCK_DELAY_BCK_INV BIT(30)
+#define ETDM_OUT_CON2_LRCK_DELAY_0P5T_EN BIT(29)
+
+#define ETDM_IN_CON3_FS_MASK GENMASK(30, 26)
+#define ETDM_IN_CON3_DISABLE_OUT(x) BIT(((x) & 0xffff))
+#define ETDM_IN_CON3_DISABLE_OUT_MASK GENMASK(15, 0)
+
+#define ETDM_IN_CON4_MASTER_LRCK_INV BIT(19)
+#define ETDM_IN_CON4_MASTER_BCK_INV BIT(18)
+#define ETDM_IN_CON4_SLAVE_LRCK_INV BIT(17)
+#define ETDM_IN_CON4_SLAVE_BCK_INV BIT(16)
+
+#define ETDM_OUT_CON4_RELATCH_EN_MASK GENMASK(28, 24)
+#define ETDM_OUT_CON4_CLOCK_MASK GENMASK(8, 6)
+#define ETDM_OUT_CON4_CLOCK_SHIFT 6
+#define ETDM_OUT_CON4_FS_MASK GENMASK(4, 0)
+
+#define ETDM_IN_CON5_LR_SWAP(x) BIT(((x) & 0xffff) + 16)
+#define ETDM_IN_CON5_LR_SWAP_MASK GENMASK(31, 16)
+#define ETDM_IN_CON5_ENABLE_ODD(x) BIT(((x) & 0xffff))
+#define ETDM_IN_CON5_ENABLE_ODD_MASK GENMASK(15, 0)
+
+#define ETDM_OUT_CON5_MASTER_LRCK_INV BIT(10)
+#define ETDM_OUT_CON5_MASTER_BCK_INV BIT(9)
+#define ETDM_OUT_CON5_SLAVE_LRCK_INV BIT(8)
+#define ETDM_OUT_CON5_SLAVE_BCK_INV BIT(7)
+
+/* GASRC_TIMING_CON0 */
+#define GASRC_TIMING_CON0_GASRC0_IN_MODE_MASK GENMASK(4, 0)
+#define GASRC_TIMING_CON0_GASRC1_IN_MODE_MASK GENMASK(9, 5)
+#define GASRC_TIMING_CON0_GASRC2_IN_MODE_MASK GENMASK(14, 10)
+#define GASRC_TIMING_CON0_GASRC3_IN_MODE_MASK GENMASK(19, 15)
+#define GASRC_TIMING_CON0_GASRC4_IN_MODE_MASK GENMASK(24, 20)
+#define GASRC_TIMING_CON0_GASRC5_IN_MODE_MASK GENMASK(29, 25)
+
+/* GASRC_TIMING_CON1 */
+#define GASRC_TIMING_CON1_GASRC6_IN_MODE_MASK GENMASK(4, 0)
+#define GASRC_TIMING_CON1_GASRC7_IN_MODE_MASK GENMASK(9, 5)
+#define GASRC_TIMING_CON1_GASRC8_IN_MODE_MASK GENMASK(14, 10)
+#define GASRC_TIMING_CON1_GASRC9_IN_MODE_MASK GENMASK(19, 15)
+#define GASRC_TIMING_CON1_GASRC10_IN_MODE_MASK GENMASK(24, 20)
+#define GASRC_TIMING_CON1_GASRC11_IN_MODE_MASK GENMASK(29, 25)
+
+/* GASRC_TIMING_CON2 */
+#define GASRC_TIMING_CON2_GASRC12_IN_MODE_MASK GENMASK(4, 0)
+#define GASRC_TIMING_CON2_GASRC13_IN_MODE_MASK GENMASK(9, 5)
+#define GASRC_TIMING_CON2_GASRC14_IN_MODE_MASK GENMASK(14, 10)
+#define GASRC_TIMING_CON2_GASRC15_IN_MODE_MASK GENMASK(19, 15)
+#define GASRC_TIMING_CON2_GASRC16_IN_MODE_MASK GENMASK(24, 20)
+#define GASRC_TIMING_CON2_GASRC17_IN_MODE_MASK GENMASK(29, 25)
+
+/* GASRC_TIMING_CON3 */
+#define GASRC_TIMING_CON3_GASRC18_IN_MODE_MASK GENMASK(4, 0)
+#define GASRC_TIMING_CON3_GASRC19_IN_MODE_MASK GENMASK(9, 5)
+
+/* GASRC_TIMING_CON4 */
+#define GASRC_TIMING_CON4_GASRC0_OUT_MODE_MASK GENMASK(4, 0)
+#define GASRC_TIMING_CON4_GASRC1_OUT_MODE_MASK GENMASK(9, 5)
+#define GASRC_TIMING_CON4_GASRC2_OUT_MODE_MASK GENMASK(14, 10)
+#define GASRC_TIMING_CON4_GASRC3_OUT_MODE_MASK GENMASK(19, 15)
+#define GASRC_TIMING_CON4_GASRC4_OUT_MODE_MASK GENMASK(24, 20)
+#define GASRC_TIMING_CON4_GASRC5_OUT_MODE_MASK GENMASK(29, 25)
+
+/* GASRC_TIMING_CON5 */
+#define GASRC_TIMING_CON5_GASRC6_OUT_MODE_MASK GENMASK(4, 0)
+#define GASRC_TIMING_CON5_GASRC7_OUT_MODE_MASK GENMASK(9, 5)
+#define GASRC_TIMING_CON5_GASRC8_OUT_MODE_MASK GENMASK(14, 10)
+#define GASRC_TIMING_CON5_GASRC9_OUT_MODE_MASK GENMASK(19, 15)
+#define GASRC_TIMING_CON5_GASRC10_OUT_MODE_MASK GENMASK(24, 20)
+#define GASRC_TIMING_CON5_GASRC11_OUT_MODE_MASK GENMASK(29, 25)
+
+/* AFE_DPTX_CON */
+#define AFE_DPTX_CON_CH_EN_2CH GENMASK(9, 8)
+#define AFE_DPTX_CON_CH_EN_4CH GENMASK(11, 8)
+#define AFE_DPTX_CON_CH_EN_6CH GENMASK(13, 8)
+#define AFE_DPTX_CON_CH_EN_8CH GENMASK(15, 8)
+#define AFE_DPTX_CON_CH_EN_MASK GENMASK(15, 8)
+#define AFE_DPTX_CON_16BIT (0x1 << 2)
+#define AFE_DPTX_CON_24BIT (0x0 << 2)
+#define AFE_DPTX_CON_16BIT_MASK BIT(2)
+#define AFE_DPTX_CON_CH_NUM_2CH (0x0 << 1)
+#define AFE_DPTX_CON_CH_NUM_8CH (0x1 << 1)
+#define AFE_DPTX_CON_CH_NUM_MASK BIT(1)
+#define AFE_DPTX_CON_ON BIT(0)
+
+/* AFE_ADDA_DL_SRC2_CON0 */
+#define DL_2_INPUT_MODE_CTL_MASK GENMASK(31, 28)
+#define DL_2_CH1_SATURATION_EN_CTL BIT(27)
+#define DL_2_CH2_SATURATION_EN_CTL BIT(26)
+#define DL_2_MUTE_CH1_OFF_CTL_PRE BIT(12)
+#define DL_2_MUTE_CH2_OFF_CTL_PRE BIT(11)
+#define DL_2_VOICE_MODE_CTL_PRE BIT(5)
+#define DL_2_GAIN_ON_CTL_PRE_SHIFT 1
+#define DL_2_SRC_ON_TMP_CTRL_PRE_SHIFT 0
+
+/* AFE_ADDA_DL_SRC2_CON1 */
+#define DL_2_GAIN_CTL_PRE_MASK GENMASK(31, 16)
+#define DL_2_GAIN_CTL_PRE_SHIFT 16
+
+/* AFE_ADDA_TOP_CON0 */
+#define C_LOOPBACK_MODE_CTL_MASK GENMASK(15, 12)
+#define DL_INPUT_FROM_SINEGEN (4 << 12)
+
+/* AFE_ADDA_UL_DL_CON0 */
+#define ADDA_AFE_ON_SHIFT 0
+
+/* AFE_ADDA_DL_SDM_DCCOMP_CON */
+#define DL_USE_NEW_2ND_SDM BIT(30)
+#define ATTGAIN_CTL_MASK GENMASK(5, 0)
+
+/* AFE_ADDA_UL_SRC_CON0 */
+#define UL_MODE_3P25M_CH2_CTL BIT(22)
+#define UL_MODE_3P25M_CH1_CTL BIT(21)
+#define UL_VOICE_MODE_CTL_MASK GENMASK(19, 17)
+#define UL_LOOPBACK_MODE_CTL BIT(2)
+#define UL_SDM3_LEVEL_CTL BIT(1)
+#define UL_SRC_ON_TMP_CTL_SHIFT 0
+
+/* AFE_GASRCx_NEW_CON0 */
+#define AFE_GASRC_NEW_CON0_ONE_HEART BIT(31)
+#define AFE_GASRC_NEW_CON0_CHSET0_CLR_IIR_HISTORY BIT(17)
+#define AFE_GASRC_NEW_CON0_CHSET0_OFS_SEL_MASK GENMASK(15, 14)
+#define AFE_GASRC_NEW_CON0_CHSET0_OFS_SEL_TX (0 << 14)
+#define AFE_GASRC_NEW_CON0_CHSET0_OFS_SEL_RX BIT(14)
+#define AFE_GASRC_NEW_CON0_CHSET0_IFS_SEL_MASK GENMASK(13, 12)
+#define AFE_GASRC_NEW_CON0_CHSET0_IFS_SEL_TX (3 << 12)
+#define AFE_GASRC_NEW_CON0_CHSET0_IFS_SEL_RX (2 << 12)
+#define AFE_GASRC_NEW_CON0_CHSET0_IIR_EN BIT(11)
+#define AFE_GASRC_NEW_CON0_CHSET0_IIR_STAGE_MASK GENMASK(10, 8)
+#define AFE_GASRC_NEW_CON0_CHSET_STR_CLR BIT(4)
+#define AFE_GASRC_NEW_CON0_COEFF_SRAM_CTRL BIT(1)
+#define AFE_GASRC_NEW_CON0_ASM_ON BIT(0)
+
+/* AFE_GASRCx_NEW_CON5 */
+#define AFE_GASRC_NEW_CON5_CALI_LRCK_SEL_MASK GENMASK(3, 1)
+#define AFE_GASRC_NEW_CON5_SOFT_RESET BIT(0)
+
+/* AFE_GASRCx_NEW_CON6 */
+#define AFE_GASRC_NEW_CON6_FREQ_CALI_CYCLE_MASK GENMASK(31, 16)
+#define AFE_GASRC_NEW_CON6_AUTO_TUNE_FREQ3 BIT(12)
+#define AFE_GASRC_NEW_CON6_COMP_FREQ_RES_EN BIT(11)
+#define AFE_GASRC_NEW_CON6_CALI_SIG_MUX_SEL_MASK GENMASK(9, 8)
+#define AFE_GASRC_NEW_CON6_FREQ_CALI_BP_DGL BIT(7)
+#define AFE_GASRC_NEW_CON6_AUTO_TUNE_FREQ2 BIT(3)
+#define AFE_GASRC_NEW_CON6_FREQ_CALI_AUTO_RESTART BIT(2)
+#define AFE_GASRC_NEW_CON6_CALI_USE_FREQ_OUT BIT(1)
+#define AFE_GASRC_NEW_CON6_CALI_EN BIT(0)
+
+/* AFE_GASRCx_NEW_CON7 */
+#define AFE_GASRC_NEW_CON7_FREQ_CALC_DENOMINATOR_MASK GENMASK(23, 0)
+#define AFE_GASRC_NEW_CON7_FREQ_CALC_DENOMINATOR_49M (0x3c00)
+#define AFE_GASRC_NEW_CON7_FREQ_CALC_DENOMINATOR_45M (0x3720)
+
+#endif
diff --git a/sound/soc/mediatek/mt8192/mt8192-dai-pcm.c b/sound/soc/mediatek/mt8192/mt8192-dai-pcm.c
index 239e3f5b53d335..2847a2e747be27 100644
--- a/sound/soc/mediatek/mt8192/mt8192-dai-pcm.c
+++ b/sound/soc/mediatek/mt8192/mt8192-dai-pcm.c
@@ -273,6 +273,8 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai);
+ struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai);
unsigned int rate = params_rate(params);
unsigned int rate_reg = mt8192_rate_transform(afe->dev, rate, dai->id);
unsigned int pcm_con = 0;
@@ -283,10 +285,10 @@ static int mtk_dai_pcm_hw_params(struct snd_pcm_substream *substream,
substream->stream,
rate,
rate_reg,
- dai->playback_widget->active,
- dai->capture_widget->active);
+ p->active,
+ c->active);
- if (dai->playback_widget->active || dai->capture_widget->active)
+ if (p->active || c->active)
return 0;
switch (dai->id) {
diff --git a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c
index caceb0deb467f9..051433689ff58a 100644
--- a/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c
+++ b/sound/soc/mediatek/mt8195/mt8195-dai-pcm.c
@@ -213,11 +213,14 @@ static int mtk_dai_pcm_configure(struct snd_pcm_substream *substream,
static int mtk_dai_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ struct snd_soc_dapm_widget *p = snd_soc_dai_get_widget_playback(dai);
+ struct snd_soc_dapm_widget *c = snd_soc_dai_get_widget_capture(dai);
+
dev_dbg(dai->dev, "%s(), id %d, stream %d, widget active p %d, c %d\n",
__func__, dai->id, substream->stream,
- dai->playback_widget->active, dai->capture_widget->active);
+ p->active, c->active);
- if (dai->playback_widget->active || dai->capture_widget->active)
+ if (p->active || c->active)
return 0;
return mtk_dai_pcm_configure(substream, dai);
diff --git a/sound/soc/meson/aiu-fifo-i2s.c b/sound/soc/meson/aiu-fifo-i2s.c
index 57e6e7160d2f25..59e00a74b5f847 100644
--- a/sound/soc/meson/aiu-fifo-i2s.c
+++ b/sound/soc/meson/aiu-fifo-i2s.c
@@ -88,7 +88,7 @@ static int aiu_fifo_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
- struct aiu_fifo *fifo = dai->playback_dma_data;
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
unsigned int val;
int ret;
@@ -158,7 +158,7 @@ int aiu_fifo_i2s_dai_probe(struct snd_soc_dai *dai)
if (ret)
return ret;
- fifo = dai->playback_dma_data;
+ fifo = snd_soc_dai_dma_data_get_playback(dai);
fifo->pcm = &fifo_i2s_pcm;
fifo->mem_offset = AIU_MEM_I2S_START;
diff --git a/sound/soc/meson/aiu-fifo-spdif.c b/sound/soc/meson/aiu-fifo-spdif.c
index 2fb30f89bf7a2c..ddbd2fc4018587 100644
--- a/sound/soc/meson/aiu-fifo-spdif.c
+++ b/sound/soc/meson/aiu-fifo-spdif.c
@@ -173,7 +173,7 @@ int aiu_fifo_spdif_dai_probe(struct snd_soc_dai *dai)
if (ret)
return ret;
- fifo = dai->playback_dma_data;
+ fifo = snd_soc_dai_dma_data_get_playback(dai);
fifo->pcm = &fifo_spdif_pcm;
fifo->mem_offset = AIU_MEM_IEC958_START;
diff --git a/sound/soc/meson/aiu-fifo.c b/sound/soc/meson/aiu-fifo.c
index d67ff4cdabd5af..543d41856c1206 100644
--- a/sound/soc/meson/aiu-fifo.c
+++ b/sound/soc/meson/aiu-fifo.c
@@ -34,7 +34,7 @@ snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_dai *dai = aiu_fifo_dai(substream);
- struct aiu_fifo *fifo = dai->playback_dma_data;
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned int addr;
@@ -46,7 +46,7 @@ snd_pcm_uframes_t aiu_fifo_pointer(struct snd_soc_component *component,
static void aiu_fifo_enable(struct snd_soc_dai *dai, bool enable)
{
struct snd_soc_component *component = dai->component;
- struct aiu_fifo *fifo = dai->playback_dma_data;
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
unsigned int en_mask = (AIU_MEM_CONTROL_FILL_EN |
AIU_MEM_CONTROL_EMPTY_EN);
@@ -80,7 +80,7 @@ int aiu_fifo_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_component *component = dai->component;
- struct aiu_fifo *fifo = dai->playback_dma_data;
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
snd_soc_component_update_bits(component,
fifo->mem_offset + AIU_MEM_CONTROL,
@@ -98,7 +98,7 @@ int aiu_fifo_hw_params(struct snd_pcm_substream *substream,
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_component *component = dai->component;
- struct aiu_fifo *fifo = dai->playback_dma_data;
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
dma_addr_t end;
/* Setup the fifo boundaries */
@@ -132,7 +132,7 @@ static irqreturn_t aiu_fifo_isr(int irq, void *dev_id)
int aiu_fifo_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct aiu_fifo *fifo = dai->playback_dma_data;
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
int ret;
snd_soc_set_runtime_hwparams(substream, fifo->pcm);
@@ -168,7 +168,7 @@ int aiu_fifo_startup(struct snd_pcm_substream *substream,
void aiu_fifo_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct aiu_fifo *fifo = dai->playback_dma_data;
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
free_irq(fifo->irq, substream);
clk_disable_unprepare(fifo->pclk);
@@ -178,7 +178,7 @@ int aiu_fifo_pcm_new(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *dai)
{
struct snd_card *card = rtd->card->snd_card;
- struct aiu_fifo *fifo = dai->playback_dma_data;
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
size_t size = fifo->pcm->buffer_bytes_max;
int ret;
@@ -200,15 +200,16 @@ int aiu_fifo_dai_probe(struct snd_soc_dai *dai)
if (!fifo)
return -ENOMEM;
- dai->playback_dma_data = fifo;
+ snd_soc_dai_dma_data_set_playback(dai, fifo);
return 0;
}
int aiu_fifo_dai_remove(struct snd_soc_dai *dai)
{
- kfree(dai->playback_dma_data);
+ struct aiu_fifo *fifo = snd_soc_dai_dma_data_get_playback(dai);
+
+ kfree(fifo);
return 0;
}
-
diff --git a/sound/soc/meson/axg-tdm-interface.c b/sound/soc/meson/axg-tdm-interface.c
index c040c83637e02a..7624aafe9009a3 100644
--- a/sound/soc/meson/axg-tdm-interface.c
+++ b/sound/soc/meson/axg-tdm-interface.c
@@ -37,10 +37,8 @@ int axg_tdm_set_tdm_slots(struct snd_soc_dai *dai, u32 *tx_mask,
unsigned int slot_width)
{
struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
- struct axg_tdm_stream *tx = (struct axg_tdm_stream *)
- dai->playback_dma_data;
- struct axg_tdm_stream *rx = (struct axg_tdm_stream *)
- dai->capture_dma_data;
+ struct axg_tdm_stream *tx = snd_soc_dai_dma_data_get_playback(dai);
+ struct axg_tdm_stream *rx = snd_soc_dai_dma_data_get_capture(dai);
unsigned int tx_slots, rx_slots;
unsigned int fmt = 0;
@@ -362,11 +360,14 @@ static int axg_tdm_iface_prepare(struct snd_pcm_substream *substream,
static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai)
{
- if (dai->capture_dma_data)
- axg_tdm_stream_free(dai->capture_dma_data);
+ int stream;
- if (dai->playback_dma_data)
- axg_tdm_stream_free(dai->playback_dma_data);
+ for_each_pcm_streams(stream) {
+ struct axg_tdm_stream *ts = snd_soc_dai_dma_data_get(dai, stream);
+
+ if (ts)
+ axg_tdm_stream_free(ts);
+ }
return 0;
}
@@ -374,19 +375,20 @@ static int axg_tdm_iface_remove_dai(struct snd_soc_dai *dai)
static int axg_tdm_iface_probe_dai(struct snd_soc_dai *dai)
{
struct axg_tdm_iface *iface = snd_soc_dai_get_drvdata(dai);
+ int stream;
- if (dai->capture_widget) {
- dai->capture_dma_data = axg_tdm_stream_alloc(iface);
- if (!dai->capture_dma_data)
- return -ENOMEM;
- }
+ for_each_pcm_streams(stream) {
+ struct axg_tdm_stream *ts;
+
+ if (!snd_soc_dai_get_widget(dai, stream))
+ continue;
- if (dai->playback_widget) {
- dai->playback_dma_data = axg_tdm_stream_alloc(iface);
- if (!dai->playback_dma_data) {
+ ts = axg_tdm_stream_alloc(iface);
+ if (!ts) {
axg_tdm_iface_remove_dai(dai);
return -ENOMEM;
}
+ snd_soc_dai_dma_data_set(dai, stream, ts);
}
return 0;
diff --git a/sound/soc/meson/axg-tdmin.c b/sound/soc/meson/axg-tdmin.c
index 49b613a1faf2bf..c8f6ea24ae784e 100644
--- a/sound/soc/meson/axg-tdmin.c
+++ b/sound/soc/meson/axg-tdmin.c
@@ -83,7 +83,7 @@ axg_tdmin_get_tdm_stream(struct snd_soc_dapm_widget *w)
if (!be)
return NULL;
- return be->capture_dma_data;
+ return snd_soc_dai_dma_data_get_capture(be);
}
static void axg_tdmin_enable(struct regmap *map)
diff --git a/sound/soc/meson/axg-tdmout.c b/sound/soc/meson/axg-tdmout.c
index 22d519fc07b243..c4039e4f08473b 100644
--- a/sound/soc/meson/axg-tdmout.c
+++ b/sound/soc/meson/axg-tdmout.c
@@ -81,7 +81,7 @@ axg_tdmout_get_tdm_stream(struct snd_soc_dapm_widget *w)
if (!be)
return NULL;
- return be->playback_dma_data;
+ return snd_soc_dai_dma_data_get_playback(be);
}
static void axg_tdmout_enable(struct regmap *map)
diff --git a/sound/soc/meson/meson-codec-glue.c b/sound/soc/meson/meson-codec-glue.c
index 80c5ed196961de..5913486c43ab1e 100644
--- a/sound/soc/meson/meson-codec-glue.c
+++ b/sound/soc/meson/meson-codec-glue.c
@@ -39,13 +39,13 @@ meson_codec_glue_get_input(struct snd_soc_dapm_widget *w)
static void meson_codec_glue_input_set_data(struct snd_soc_dai *dai,
struct meson_codec_glue_input *data)
{
- dai->playback_dma_data = data;
+ snd_soc_dai_dma_data_set_playback(dai, data);
}
struct meson_codec_glue_input *
meson_codec_glue_input_get_data(struct snd_soc_dai *dai)
{
- return dai->playback_dma_data;
+ return snd_soc_dai_dma_data_get_playback(dai);
}
EXPORT_SYMBOL_GPL(meson_codec_glue_input_get_data);
@@ -99,8 +99,8 @@ int meson_codec_glue_output_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct meson_codec_glue_input *in_data =
- meson_codec_glue_output_get_input_data(dai->capture_widget);
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget_capture(dai);
+ struct meson_codec_glue_input *in_data = meson_codec_glue_output_get_input_data(w);
if (!in_data)
return -ENODEV;
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index a045693d5bc2c1..2036d368c08d79 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -16,7 +16,10 @@ config SND_PXA2XX_AC97
tristate
config SND_PXA2XX_SOC_AC97
- tristate
+ tristate "SoC AC97 support for PXA2xx"
+ depends on SND_PXA2XX_SOC
+ depends on AC97_BUS=n
+ default y
select AC97_BUS_NEW
select SND_PXA2XX_LIB
select SND_PXA2XX_LIB_AC97
diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c
index 4e0e9b778d4c0a..ec37da331a91bb 100644
--- a/sound/soc/pxa/e740_wm9705.c
+++ b/sound/soc/pxa/e740_wm9705.c
@@ -145,18 +145,12 @@ static int e740_probe(struct platform_device *pdev)
return ret;
}
-static int e740_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static struct platform_driver e740_driver = {
.driver = {
.name = "e740-audio",
.pm = &snd_soc_pm_ops,
},
.probe = e740_probe,
- .remove = e740_remove,
};
module_platform_driver(e740_driver);
diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c
index 7a1e0d8bfd1138..60b22d4f92a754 100644
--- a/sound/soc/pxa/e750_wm9705.c
+++ b/sound/soc/pxa/e750_wm9705.c
@@ -124,18 +124,12 @@ static int e750_probe(struct platform_device *pdev)
return ret;
}
-static int e750_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static struct platform_driver e750_driver = {
.driver = {
.name = "e750-audio",
.pm = &snd_soc_pm_ops,
},
.probe = e750_probe,
- .remove = e750_remove,
};
module_platform_driver(e750_driver);
diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c
index a39c494127cf74..c7756acd888a8f 100644
--- a/sound/soc/pxa/e800_wm9712.c
+++ b/sound/soc/pxa/e800_wm9712.c
@@ -124,18 +124,12 @@ static int e800_probe(struct platform_device *pdev)
return ret;
}
-static int e800_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static struct platform_driver e800_driver = {
.driver = {
.name = "e800-audio",
.pm = &snd_soc_pm_ops,
},
.probe = e800_probe,
- .remove = e800_remove,
};
module_platform_driver(e800_driver);
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index 44303b6eb228bf..70442315f5c592 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -306,18 +306,12 @@ static int spitz_probe(struct platform_device *pdev)
return ret;
}
-static int spitz_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static struct platform_driver spitz_driver = {
.driver = {
.name = "spitz-audio",
.pm = &snd_soc_pm_ops,
},
.probe = spitz_probe,
- .remove = spitz_remove,
};
module_platform_driver(spitz_driver);
diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c
index c7b7d0864d1ad7..5d07b38f6d7290 100644
--- a/sound/soc/qcom/apq8096.c
+++ b/sound/soc/qcom/apq8096.c
@@ -113,6 +113,7 @@ static int apq8096_platform_probe(struct platform_device *pdev)
if (!card)
return -ENOMEM;
+ card->driver_name = "apq8096";
card->dev = dev;
card->owner = THIS_MODULE;
dev_set_drvdata(dev, card);
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index b41ab7a321ae98..ef5cb40b2d9be6 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -1181,7 +1181,7 @@ static int lpass_platform_pcm_new(struct snd_soc_component *component,
if (is_cdc_dma_port(dai_id))
return lpass_platform_prealloc_cdc_dma_buffer(component, pcm, dai_id);
- return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
+ return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_NONCOHERENT,
component->dev, size);
}
diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c
index ee59ef36b85a61..7f02f5b2c33fd2 100644
--- a/sound/soc/qcom/qdsp6/q6apm-dai.c
+++ b/sound/soc/qcom/qdsp6/q6apm-dai.c
@@ -8,6 +8,7 @@
#include <linux/slab.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#include <linux/spinlock.h>
#include <sound/pcm.h>
#include <asm/dma.h>
#include <linux/dma-mapping.h>
@@ -53,6 +54,7 @@ struct q6apm_dai_rtd {
uint16_t session_id;
enum stream_state state;
struct q6apm_graph *graph;
+ spinlock_t lock;
};
struct q6apm_dai_data {
@@ -62,7 +64,8 @@ struct q6apm_dai_data {
static struct snd_pcm_hardware q6apm_dai_hardware_capture = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_BATCH),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE),
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
@@ -80,7 +83,8 @@ static struct snd_pcm_hardware q6apm_dai_hardware_capture = {
static struct snd_pcm_hardware q6apm_dai_hardware_playback = {
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_BATCH),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE),
.rates = SNDRV_PCM_RATE_8000_192000,
.rate_min = 8000,
@@ -99,20 +103,25 @@ static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, vo
{
struct q6apm_dai_rtd *prtd = priv;
struct snd_pcm_substream *substream = prtd->substream;
+ unsigned long flags;
switch (opcode) {
case APM_CLIENT_EVENT_CMD_EOS_DONE:
prtd->state = Q6APM_STREAM_STOPPED;
break;
case APM_CLIENT_EVENT_DATA_WRITE_DONE:
+ spin_lock_irqsave(&prtd->lock, flags);
prtd->pos += prtd->pcm_count;
+ spin_unlock_irqrestore(&prtd->lock, flags);
snd_pcm_period_elapsed(substream);
if (prtd->state == Q6APM_STREAM_RUNNING)
q6apm_write_async(prtd->graph, prtd->pcm_count, 0, 0, 0);
break;
case APM_CLIENT_EVENT_DATA_READ_DONE:
+ spin_lock_irqsave(&prtd->lock, flags);
prtd->pos += prtd->pcm_count;
+ spin_unlock_irqrestore(&prtd->lock, flags);
snd_pcm_period_elapsed(substream);
if (prtd->state == Q6APM_STREAM_RUNNING)
q6apm_read(prtd->graph);
@@ -253,6 +262,7 @@ static int q6apm_dai_open(struct snd_soc_component *component,
if (prtd == NULL)
return -ENOMEM;
+ spin_lock_init(&prtd->lock);
prtd->substream = substream;
prtd->graph = q6apm_graph_open(dev, (q6apm_cb)event_handler, prtd, graph_id);
if (IS_ERR(prtd->graph)) {
@@ -332,11 +342,17 @@ static snd_pcm_uframes_t q6apm_dai_pointer(struct snd_soc_component *component,
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct q6apm_dai_rtd *prtd = runtime->private_data;
+ snd_pcm_uframes_t ptr;
+ unsigned long flags;
+ spin_lock_irqsave(&prtd->lock, flags);
if (prtd->pos == prtd->pcm_size)
prtd->pos = 0;
- return bytes_to_frames(runtime, prtd->pos);
+ ptr = bytes_to_frames(runtime, prtd->pos);
+ spin_unlock_irqrestore(&prtd->lock, flags);
+
+ return ptr;
}
static int q6apm_dai_hw_params(struct snd_soc_component *component,
diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
index ce9e5646d8f3a2..23d23bc6fbaa77 100644
--- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
+++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
@@ -127,6 +127,11 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s
int graph_id = dai->id;
int rc;
+ if (dai_data->is_port_started[dai->id]) {
+ q6apm_graph_stop(dai_data->graph[dai->id]);
+ dai_data->is_port_started[dai->id] = false;
+ }
+
/**
* It is recommend to load DSP with source graph first and then sink
* graph, so sequence for playback and capture will be different
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
index 5beb898f28f5c2..994c9e823a8870 100644
--- a/sound/soc/qcom/qdsp6/q6apm.c
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -27,6 +27,8 @@ struct apm_graph_mgmt_cmd {
#define APM_GRAPH_MGMT_PSIZE(p, n) ALIGN(struct_size(p, sub_graph_id_list, n), 8)
+struct q6apm *g_apm;
+
int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt, uint32_t rsp_opcode)
{
gpr_device_t *gdev = apm->gdev;
@@ -143,6 +145,7 @@ static void q6apm_put_audioreach_graph(struct kref *ref)
kfree(graph);
}
+
static int q6apm_get_apm_state(struct q6apm *apm)
{
struct gpr_pkt *pkt;
@@ -158,6 +161,15 @@ static int q6apm_get_apm_state(struct q6apm *apm)
return apm->state;
}
+bool q6apm_is_adsp_ready(void)
+{
+ if (g_apm)
+ return q6apm_get_apm_state(g_apm);
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(q6apm_is_adsp_ready);
+
static struct audioreach_module *__q6apm_find_module_by_mid(struct q6apm *apm,
struct audioreach_graph_info *info,
uint32_t mid)
@@ -658,6 +670,8 @@ static int apm_probe(gpr_device_t *gdev)
idr_init(&apm->modules_idr);
+ g_apm = apm;
+
q6apm_get_apm_state(apm);
ret = devm_snd_soc_register_component(dev, &q6apm_audio_component, NULL, 0);
diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h
index 273f97812741aa..7005be9b63e339 100644
--- a/sound/soc/qcom/qdsp6/q6apm.h
+++ b/sound/soc/qcom/qdsp6/q6apm.h
@@ -145,4 +145,6 @@ struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph,
void q6apm_set_fe_dai_ops(struct snd_soc_dai_driver *dai_drv);
int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph);
+bool q6apm_is_adsp_ready(void);
+
#endif /* __APM_GRAPH_ */
diff --git a/sound/soc/qcom/qdsp6/q6prm.c b/sound/soc/qcom/qdsp6/q6prm.c
index 8aa1a213bfb756..3aa63aac4a68e2 100644
--- a/sound/soc/qcom/qdsp6/q6prm.c
+++ b/sound/soc/qcom/qdsp6/q6prm.c
@@ -12,6 +12,7 @@
#include <linux/soc/qcom/apr.h>
#include <dt-bindings/soc/qcom,gpr.h>
#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
+#include "q6apm.h"
#include "q6prm.h"
#include "audioreach.h"
@@ -226,6 +227,9 @@ static int prm_probe(gpr_device_t *gdev)
init_waitqueue_head(&cc->wait);
dev_set_drvdata(dev, cc);
+ if (!q6apm_is_adsp_ready())
+ return -EPROBE_DEFER;
+
return devm_of_platform_populate(dev);
}
diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c
index d8d35563af00fd..02612af714a8e8 100644
--- a/sound/soc/qcom/sdm845.c
+++ b/sound/soc/qcom/sdm845.c
@@ -588,6 +588,7 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev)
static const struct of_device_id sdm845_snd_device_id[] = {
{ .compatible = "qcom,sdm845-sndcard" },
+ /* Do not grow the list for compatible devices */
{ .compatible = "qcom,db845c-sndcard" },
{ .compatible = "lenovo,yoga-c630-sndcard" },
{},
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index 42f76bc0fb02e2..f98a2fa85edd6b 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -13,7 +13,7 @@ config SND_SOC_ROCKCHIP_I2S
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for I2S driver for
- Rockchip I2S device. The device supports upto maximum of
+ Rockchip I2S device. The device supports up to maximum of
8 channels each for play and record.
config SND_SOC_ROCKCHIP_I2S_TDM
diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c
index 08b90ec5cc80a8..166257c6ae1475 100644
--- a/sound/soc/rockchip/rockchip_i2s_tdm.c
+++ b/sound/soc/rockchip/rockchip_i2s_tdm.c
@@ -1070,9 +1070,9 @@ static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai)
struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
if (i2s_tdm->has_capture)
- dai->capture_dma_data = &i2s_tdm->capture_dma_data;
+ snd_soc_dai_dma_data_set_capture(dai, &i2s_tdm->capture_dma_data);
if (i2s_tdm->has_playback)
- dai->playback_dma_data = &i2s_tdm->playback_dma_data;
+ snd_soc_dai_dma_data_set_playback(dai, &i2s_tdm->playback_dma_data);
if (i2s_tdm->mclk_calibrate)
snd_soc_add_dai_controls(dai, &rockchip_i2s_tdm_compensation_control, 1);
diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c
index 5b1e47bdc376bc..6ce92b1db7900c 100644
--- a/sound/soc/rockchip/rockchip_pdm.c
+++ b/sound/soc/rockchip/rockchip_pdm.c
@@ -373,7 +373,7 @@ static int rockchip_pdm_dai_probe(struct snd_soc_dai *dai)
{
struct rk_pdm_dev *pdm = to_info(dai);
- dai->capture_dma_data = &pdm->capture_dma_data;
+ snd_soc_dai_dma_data_set_capture(dai, &pdm->capture_dma_data);
return 0;
}
diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
index 5b4f0045758797..2d937fcf357d46 100644
--- a/sound/soc/rockchip/rockchip_spdif.c
+++ b/sound/soc/rockchip/rockchip_spdif.c
@@ -196,7 +196,7 @@ static int rk_spdif_dai_probe(struct snd_soc_dai *dai)
{
struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
- dai->playback_dma_data = &spdif->playback_dma_data;
+ snd_soc_dai_dma_data_set_playback(dai, &spdif->playback_dma_data);
return 0;
}
diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h
index b4b5d6053503af..138e95581979a6 100644
--- a/sound/soc/samsung/i2s-regs.h
+++ b/sound/soc/samsung/i2s-regs.h
@@ -132,6 +132,7 @@
#define EXYNOS7_MOD_RCLK_192FS 7
#define PSR_PSREN (1 << 15)
+#define PSR_PSVAL(x) ((((x) - 1) << 8) & 0x3f00)
#define FIC_TX2COUNT(x) (((x) >> 24) & 0xf)
#define FIC_TX1COUNT(x) (((x) >> 16) & 0xf)
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 9505200f3d11b4..6f96032090de96 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -50,6 +50,10 @@ struct samsung_i2s_dai_data {
u32 quirks;
unsigned int pcm_rates;
const struct samsung_i2s_variant_regs *i2s_variant_regs;
+ void (*fixup_early)(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+ void (*fixup_late)(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
};
struct i2s_dai {
@@ -111,6 +115,10 @@ struct samsung_i2s_priv {
u32 suspend_i2spsr;
const struct samsung_i2s_variant_regs *variant_regs;
+ void (*fixup_early)(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+ void (*fixup_late)(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
u32 quirks;
/* The clock provider's data */
@@ -940,6 +948,10 @@ static int i2s_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
pm_runtime_get_sync(dai->dev);
+
+ if (priv->fixup_early)
+ priv->fixup_early(substream, dai);
+
spin_lock_irqsave(&priv->lock, flags);
if (config_setup(i2s)) {
@@ -947,6 +959,9 @@ static int i2s_trigger(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ if (priv->fixup_late)
+ priv->fixup_late(substream, dai);
+
if (capture)
i2s_rxctrl(i2s, 1);
else
@@ -1410,6 +1425,8 @@ static int samsung_i2s_probe(struct platform_device *pdev)
if (np) {
priv->quirks = i2s_dai_data->quirks;
+ priv->fixup_early = i2s_dai_data->fixup_early;
+ priv->fixup_late = i2s_dai_data->fixup_late;
} else {
if (!i2s_pdata) {
dev_err(&pdev->dev, "Missing platform data\n");
@@ -1563,6 +1580,31 @@ static int samsung_i2s_remove(struct platform_device *pdev)
return 0;
}
+static void fsd_i2s_fixup_early(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct i2s_dai *i2s = to_info(asoc_rtd_to_cpu(rtd, 0));
+ struct i2s_dai *other = get_other_dai(i2s);
+
+ if (!is_opened(other)) {
+ i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK, 0, SND_SOC_CLOCK_OUT);
+ i2s_set_sysclk(dai, SAMSUNG_I2S_OPCLK, 0, MOD_OPCLK_PCLK);
+ }
+}
+
+static void fsd_i2s_fixup_late(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct samsung_i2s_priv *priv = snd_soc_dai_get_drvdata(dai);
+ struct i2s_dai *i2s = to_info(asoc_rtd_to_cpu(rtd, 0));
+ struct i2s_dai *other = get_other_dai(i2s);
+
+ if (!is_opened(other))
+ writel(PSR_PSVAL(2) | PSR_PSREN, priv->addr + I2SPSR);
+}
+
static const struct samsung_i2s_variant_regs i2sv3_regs = {
.bfs_off = 1,
.rfs_off = 3,
@@ -1652,6 +1694,14 @@ static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 __maybe_unused = {
.i2s_variant_regs = &i2sv5_i2s1_regs,
};
+static const struct samsung_i2s_dai_data fsd_dai_type __maybe_unused = {
+ .quirks = QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR | QUIRK_SUPPORTS_TDM,
+ .pcm_rates = SNDRV_PCM_RATE_8000_192000,
+ .i2s_variant_regs = &i2sv7_regs,
+ .fixup_early = fsd_i2s_fixup_early,
+ .fixup_late = fsd_i2s_fixup_late,
+};
+
static const struct platform_device_id samsung_i2s_driver_ids[] = {
{
.name = "samsung-i2s",
@@ -1678,6 +1728,9 @@ static const struct of_device_id exynos_i2s_match[] = {
}, {
.compatible = "samsung,exynos7-i2s1",
.data = &i2sv5_dai_type_i2s1,
+ }, {
+ .compatible = "tesla,fsd-i2s",
+ .data = &fsd_dai_type,
},
{},
};
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index ca1e1281cefae0..7bc4421835d726 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -11,7 +11,7 @@
#define CLKB 1
#define CLKC 2
#define CLKI 3
-#define CLKMAX 4
+#define CLKINMAX 4
#define CLKOUT 0
#define CLKOUT1 1
@@ -25,44 +25,56 @@ static struct rsnd_mod_ops adg_ops = {
.name = "adg",
};
+#define ADG_HZ_441 0
+#define ADG_HZ_48 1
+#define ADG_HZ_SIZE 2
+
struct rsnd_adg {
- struct clk *clk[CLKMAX];
+ struct clk *clkin[CLKINMAX];
struct clk *clkout[CLKOUTMAX];
struct clk *null_clk;
struct clk_onecell_data onecell;
struct rsnd_mod mod;
- int clk_rate[CLKMAX];
- u32 flags;
+ int clkin_rate[CLKINMAX];
+ int clkin_size;
+ int clkout_size;
u32 ckr;
u32 rbga;
u32 rbgb;
- int rbga_rate_for_441khz; /* RBGA */
- int rbgb_rate_for_48khz; /* RBGB */
+ int rbg_rate[ADG_HZ_SIZE]; /* RBGA / RBGB */
};
-#define LRCLK_ASYNC (1 << 0)
-#define AUDIO_OUT_48 (1 << 1)
-
-#define for_each_rsnd_clk(pos, adg, i) \
+#define for_each_rsnd_clkin(pos, adg, i) \
for (i = 0; \
- (i < CLKMAX) && \
- ((pos) = adg->clk[i]); \
+ (i < adg->clkin_size) && \
+ ((pos) = adg->clkin[i]); \
i++)
#define for_each_rsnd_clkout(pos, adg, i) \
for (i = 0; \
- (i < CLKOUTMAX) && \
+ (i < adg->clkout_size) && \
((pos) = adg->clkout[i]); \
i++)
#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
-static const char * const clk_name[] = {
+static const char * const clkin_name_gen4[] = {
+ [CLKA] = "clkin",
+};
+
+static const char * const clkin_name_gen2[] = {
[CLKA] = "clk_a",
[CLKB] = "clk_b",
[CLKC] = "clk_c",
[CLKI] = "clk_i",
};
+static const char * const clkout_name_gen2[] = {
+ [CLKOUT] = "audio_clkout",
+ [CLKOUT1] = "audio_clkout1",
+ [CLKOUT2] = "audio_clkout2",
+ [CLKOUT3] = "audio_clkout3",
+};
+
static u32 rsnd_adg_calculate_rbgx(unsigned long div)
{
int i;
@@ -116,11 +128,11 @@ static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,
unsigned int val, en;
unsigned int min, diff;
unsigned int sel_rate[] = {
- adg->clk_rate[CLKA], /* 0000: CLKA */
- adg->clk_rate[CLKB], /* 0001: CLKB */
- adg->clk_rate[CLKC], /* 0010: CLKC */
- adg->rbga_rate_for_441khz, /* 0011: RBGA */
- adg->rbgb_rate_for_48khz, /* 0100: RBGB */
+ adg->clkin_rate[CLKA], /* 0000: CLKA */
+ adg->clkin_rate[CLKB], /* 0001: CLKB */
+ adg->clkin_rate[CLKC], /* 0010: CLKC */
+ adg->rbg_rate[ADG_HZ_441], /* 0011: RBGA */
+ adg->rbg_rate[ADG_HZ_48], /* 0100: RBGB */
};
min = ~0;
@@ -291,6 +303,7 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate)
{
struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+ struct clk *clk;
int i;
int sel_table[] = {
[CLKA] = 0x1,
@@ -303,17 +316,17 @@ int rsnd_adg_clk_query(struct rsnd_priv *priv, unsigned int rate)
* find suitable clock from
* AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI.
*/
- for (i = 0; i < CLKMAX; i++)
- if (rate == adg->clk_rate[i])
+ for_each_rsnd_clkin(clk, adg, i)
+ if (rate == adg->clkin_rate[i])
return sel_table[i];
/*
* find divided clock from BRGA/BRGB
*/
- if (rate == adg->rbga_rate_for_441khz)
+ if (rate == adg->rbg_rate[ADG_HZ_441])
return 0x10;
- if (rate == adg->rbgb_rate_for_48khz)
+ if (rate == adg->rbg_rate[ADG_HZ_48])
return 0x20;
return -EIO;
@@ -341,13 +354,8 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
rsnd_adg_set_ssi_clk(ssi_mod, data);
- if (rsnd_flags_has(adg, LRCLK_ASYNC)) {
- if (rsnd_flags_has(adg, AUDIO_OUT_48))
- ckr = 0x80000000;
- } else {
- if (0 == (rate % 8000))
- ckr = 0x80000000;
- }
+ if (0 == (rate % 8000))
+ ckr = 0x80000000; /* BRGB output = 48kHz */
rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr | ckr);
rsnd_mod_write(adg_mod, BRRA, adg->rbga);
@@ -355,8 +363,8 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
dev_dbg(dev, "CLKOUT is based on BRG%c (= %dHz)\n",
(ckr) ? 'B' : 'A',
- (ckr) ? adg->rbgb_rate_for_48khz :
- adg->rbga_rate_for_441khz);
+ (ckr) ? adg->rbg_rate[ADG_HZ_48] :
+ adg->rbg_rate[ADG_HZ_441]);
return 0;
}
@@ -367,7 +375,7 @@ void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
struct clk *clk;
int i;
- for_each_rsnd_clk(clk, adg, i) {
+ for_each_rsnd_clkin(clk, adg, i) {
if (enable) {
clk_prepare_enable(clk);
@@ -376,7 +384,7 @@ void rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
* atomic context. Let's keep it when
* rsnd_adg_clk_enable() was called
*/
- adg->clk_rate[i] = clk_get_rate(clk);
+ adg->clkin_rate[i] = clk_get_rate(clk);
} else {
clk_disable_unprepare(clk);
}
@@ -425,19 +433,30 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv)
struct rsnd_adg *adg = priv->adg;
struct device *dev = rsnd_priv_to_dev(priv);
struct clk *clk;
+ const char * const *clkin_name;
+ int clkin_size;
int i;
- for (i = 0; i < CLKMAX; i++) {
- clk = devm_clk_get(dev, clk_name[i]);
+ clkin_name = clkin_name_gen2;
+ clkin_size = ARRAY_SIZE(clkin_name_gen2);
+ if (rsnd_is_gen4(priv)) {
+ clkin_name = clkin_name_gen4;
+ clkin_size = ARRAY_SIZE(clkin_name_gen4);
+ }
+
+ for (i = 0; i < clkin_size; i++) {
+ clk = devm_clk_get(dev, clkin_name[i]);
if (IS_ERR_OR_NULL(clk))
clk = rsnd_adg_null_clk_get(priv);
if (IS_ERR_OR_NULL(clk))
goto err;
- adg->clk[i] = clk;
+ adg->clkin[i] = clk;
}
+ adg->clkin_size = clkin_size;
+
return 0;
err:
@@ -467,18 +486,13 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv)
struct property *prop;
u32 ckr, rbgx, rbga, rbgb;
u32 rate, div;
-#define REQ_SIZE 2
- u32 req_rate[REQ_SIZE] = {};
+ u32 req_rate[ADG_HZ_SIZE] = {};
uint32_t count = 0;
- unsigned long req_48kHz_rate, req_441kHz_rate;
+ unsigned long req_Hz[ADG_HZ_SIZE];
+ int clkout_size;
int i, req_size;
const char *parent_clk_name = NULL;
- static const char * const clkout_name[] = {
- [CLKOUT] = "audio_clkout",
- [CLKOUT1] = "audio_clkout1",
- [CLKOUT2] = "audio_clkout2",
- [CLKOUT3] = "audio_clkout3",
- };
+ const char * const *clkout_name;
int brg_table[] = {
[CLKA] = 0x0,
[CLKB] = 0x1,
@@ -499,27 +513,21 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv)
goto rsnd_adg_get_clkout_end;
req_size = prop->length / sizeof(u32);
- if (req_size > REQ_SIZE) {
+ if (req_size > ADG_HZ_SIZE) {
dev_err(dev, "too many clock-frequency\n");
return -EINVAL;
}
of_property_read_u32_array(np, "clock-frequency", req_rate, req_size);
- req_48kHz_rate = 0;
- req_441kHz_rate = 0;
+ req_Hz[ADG_HZ_48] = 0;
+ req_Hz[ADG_HZ_441] = 0;
for (i = 0; i < req_size; i++) {
if (0 == (req_rate[i] % 44100))
- req_441kHz_rate = req_rate[i];
+ req_Hz[ADG_HZ_441] = req_rate[i];
if (0 == (req_rate[i] % 48000))
- req_48kHz_rate = req_rate[i];
+ req_Hz[ADG_HZ_48] = req_rate[i];
}
- if (req_rate[0] % 48000 == 0)
- rsnd_flags_set(adg, AUDIO_OUT_48);
-
- if (of_get_property(np, "clkout-lr-asynchronous", NULL))
- rsnd_flags_set(adg, LRCLK_ASYNC);
-
/*
* This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
* have 44.1kHz or 48kHz base clocks for now.
@@ -529,47 +537,48 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv)
* rsnd_adg_ssi_clk_try_start()
* rsnd_ssi_master_clk_start()
*/
- adg->rbga_rate_for_441khz = 0;
- adg->rbgb_rate_for_48khz = 0;
- for_each_rsnd_clk(clk, adg, i) {
+ for_each_rsnd_clkin(clk, adg, i) {
rate = clk_get_rate(clk);
if (0 == rate) /* not used */
continue;
/* RBGA */
- if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) {
+ if (!adg->rbg_rate[ADG_HZ_441] && (0 == rate % 44100)) {
div = 6;
- if (req_441kHz_rate)
- div = rate / req_441kHz_rate;
+ if (req_Hz[ADG_HZ_441])
+ div = rate / req_Hz[ADG_HZ_441];
rbgx = rsnd_adg_calculate_rbgx(div);
if (BRRx_MASK(rbgx) == rbgx) {
rbga = rbgx;
- adg->rbga_rate_for_441khz = rate / div;
+ adg->rbg_rate[ADG_HZ_441] = rate / div;
ckr |= brg_table[i] << 20;
- if (req_441kHz_rate &&
- !rsnd_flags_has(adg, AUDIO_OUT_48))
+ if (req_Hz[ADG_HZ_441])
parent_clk_name = __clk_get_name(clk);
}
}
/* RBGB */
- if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) {
+ if (!adg->rbg_rate[ADG_HZ_48] && (0 == rate % 48000)) {
div = 6;
- if (req_48kHz_rate)
- div = rate / req_48kHz_rate;
+ if (req_Hz[ADG_HZ_48])
+ div = rate / req_Hz[ADG_HZ_48];
rbgx = rsnd_adg_calculate_rbgx(div);
if (BRRx_MASK(rbgx) == rbgx) {
rbgb = rbgx;
- adg->rbgb_rate_for_48khz = rate / div;
+ adg->rbg_rate[ADG_HZ_48] = rate / div;
ckr |= brg_table[i] << 16;
- if (req_48kHz_rate &&
- rsnd_flags_has(adg, AUDIO_OUT_48))
+ if (req_Hz[ADG_HZ_48])
parent_clk_name = __clk_get_name(clk);
}
}
}
+ clkout_name = clkout_name_gen2;
+ clkout_size = ARRAY_SIZE(clkout_name_gen2);
+ if (rsnd_is_gen4(priv))
+ clkout_size = 1; /* reuse clkout_name_gen2[] */
+
/*
* ADG supports BRRA/BRRB output only.
* this means all clkout0/1/2/3 will be * same rate
@@ -586,13 +595,14 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv)
goto err;
adg->clkout[CLKOUT] = clk;
+ adg->clkout_size = 1;
of_clk_add_provider(np, of_clk_src_simple_get, clk);
}
/*
* for clkout0/1/2/3
*/
else {
- for (i = 0; i < CLKOUTMAX; i++) {
+ for (i = 0; i < clkout_size; i++) {
clk = clk_register_fixed_rate(dev, clkout_name[i],
parent_clk_name, 0,
req_rate[0]);
@@ -602,7 +612,8 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv)
adg->clkout[i] = clk;
}
adg->onecell.clks = adg->clkout;
- adg->onecell.clk_num = CLKOUTMAX;
+ adg->onecell.clk_num = clkout_size;
+ adg->clkout_size = clkout_size;
of_clk_add_provider(np, of_clk_src_onecell_get,
&adg->onecell);
}
@@ -647,22 +658,22 @@ void rsnd_adg_clk_dbg_info(struct rsnd_priv *priv, struct seq_file *m)
struct clk *clk;
int i;
- for_each_rsnd_clk(clk, adg, i)
+ for_each_rsnd_clkin(clk, adg, i)
dbg_msg(dev, m, "%-18s : %pa : %ld\n",
__clk_get_name(clk), clk, clk_get_rate(clk));
dbg_msg(dev, m, "BRGCKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
adg->ckr, adg->rbga, adg->rbgb);
- dbg_msg(dev, m, "BRGA (for 44100 base) = %d\n", adg->rbga_rate_for_441khz);
- dbg_msg(dev, m, "BRGB (for 48000 base) = %d\n", adg->rbgb_rate_for_48khz);
+ dbg_msg(dev, m, "BRGA (for 44100 base) = %d\n", adg->rbg_rate[ADG_HZ_441]);
+ dbg_msg(dev, m, "BRGB (for 48000 base) = %d\n", adg->rbg_rate[ADG_HZ_48]);
/*
* Actual CLKOUT will be exchanged in rsnd_adg_ssi_clk_try_start()
* by BRGCKR::BRGCKR_31
*/
for_each_rsnd_clkout(clk, adg, i)
- dbg_msg(dev, m, "clkout %d : %pa : %ld\n", i,
- clk, clk_get_rate(clk));
+ dbg_msg(dev, m, "%-18s : %pa : %ld\n",
+ __clk_get_name(clk), clk, clk_get_rate(clk));
}
#else
#define rsnd_adg_clk_dbg_info(priv, m)
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 2d269ac8c1377d..cb17f7d0cf0c19 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -102,6 +102,7 @@ static const struct of_device_id rsnd_of_match[] = {
{ .compatible = "renesas,rcar_sound-gen1", .data = (void *)RSND_GEN1 },
{ .compatible = "renesas,rcar_sound-gen2", .data = (void *)RSND_GEN2 },
{ .compatible = "renesas,rcar_sound-gen3", .data = (void *)RSND_GEN3 },
+ { .compatible = "renesas,rcar_sound-gen4", .data = (void *)RSND_GEN4 },
/* Special Handling */
{ .compatible = "renesas,rcar_sound-r8a77990", .data = (void *)(RSND_GEN3 | RSND_SOC_E) },
{},
@@ -827,6 +828,13 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
break;
default:
/* use default */
+ /*
+ * Indicate warning if DT has "dai-tdm-slot-width"
+ * but the value was not expected.
+ */
+ if (slot_width)
+ dev_warn(dev, "unsupported TDM slot width (%d), force to use default 32\n",
+ slot_width);
slot_width = 32;
}
@@ -869,7 +877,8 @@ static unsigned int rsnd_soc_hw_rate_list[] = {
static int rsnd_soc_hw_rule(struct rsnd_dai *rdai,
unsigned int *list, int list_num,
- struct snd_interval *baseline, struct snd_interval *iv)
+ struct snd_interval *baseline, struct snd_interval *iv,
+ struct rsnd_dai_stream *io, char *unit)
{
struct snd_interval p;
unsigned int rate;
@@ -899,6 +908,16 @@ static int rsnd_soc_hw_rule(struct rsnd_dai *rdai,
}
}
+ /* Indicate error once if it can't handle */
+ if (!rsnd_flags_has(io, RSND_HW_RULE_ERR) && (p.min > p.max)) {
+ struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ dev_warn(dev, "It can't handle %d %s <-> %d %s\n",
+ baseline->min, unit, baseline->max, unit);
+ rsnd_flags_set(io, RSND_HW_RULE_ERR);
+ }
+
return snd_interval_refine(iv, &p);
}
@@ -922,7 +941,7 @@ static int rsnd_soc_hw_rule_rate(struct snd_pcm_hw_params *params,
return rsnd_soc_hw_rule(rdai, rsnd_soc_hw_rate_list,
ARRAY_SIZE(rsnd_soc_hw_rate_list),
- &ic, ir);
+ &ic, ir, io, "ch");
}
static int rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params,
@@ -945,7 +964,7 @@ static int rsnd_soc_hw_rule_channels(struct snd_pcm_hw_params *params,
return rsnd_soc_hw_rule(rdai, rsnd_soc_hw_channels_list,
ARRAY_SIZE(rsnd_soc_hw_channels_list),
- ir, &ic);
+ ir, &ic, io, "Hz");
}
static const struct snd_pcm_hardware rsnd_pcm_hardware = {
@@ -970,6 +989,8 @@ static int rsnd_soc_dai_startup(struct snd_pcm_substream *substream,
unsigned int max_channels = rsnd_rdai_channels_get(rdai);
int i;
+ rsnd_flags_del(io, RSND_HW_RULE_ERR);
+
rsnd_dai_stream_init(io, substream);
/*
@@ -1343,6 +1364,7 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv,
struct snd_soc_dai_driver *drv;
struct rsnd_dai *rdai;
struct device *dev = rsnd_priv_to_dev(priv);
+ int playback_exist = 0, capture_exist = 0;
int io_i;
rdai = rsnd_rdai_get(priv, dai_i);
@@ -1357,22 +1379,6 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv,
drv->ops = &rsnd_soc_dai_ops;
drv->pcm_new = rsnd_pcm_new;
- snprintf(io_playback->name, RSND_DAI_NAME_SIZE,
- "DAI%d Playback", dai_i);
- drv->playback.rates = RSND_RATES;
- drv->playback.formats = RSND_FMTS;
- drv->playback.channels_min = 2;
- drv->playback.channels_max = 8;
- drv->playback.stream_name = io_playback->name;
-
- snprintf(io_capture->name, RSND_DAI_NAME_SIZE,
- "DAI%d Capture", dai_i);
- drv->capture.rates = RSND_RATES;
- drv->capture.formats = RSND_FMTS;
- drv->capture.channels_min = 2;
- drv->capture.channels_max = 8;
- drv->capture.stream_name = io_capture->name;
-
io_playback->rdai = rdai;
io_capture->rdai = rdai;
rsnd_rdai_channels_set(rdai, 2); /* default 2ch */
@@ -1386,6 +1392,14 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv,
if (!playback && !capture)
break;
+ if (io_i == 0) {
+ /* check whether playback/capture property exists */
+ if (playback)
+ playback_exist = 1;
+ if (capture)
+ capture_exist = 1;
+ }
+
rsnd_parse_connect_ssi(rdai, playback, capture);
rsnd_parse_connect_ssiu(rdai, playback, capture);
rsnd_parse_connect_src(rdai, playback, capture);
@@ -1397,6 +1411,23 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv,
of_node_put(capture);
}
+ if (playback_exist) {
+ snprintf(io_playback->name, RSND_DAI_NAME_SIZE, "DAI%d Playback", dai_i);
+ drv->playback.rates = RSND_RATES;
+ drv->playback.formats = RSND_FMTS;
+ drv->playback.channels_min = 2;
+ drv->playback.channels_max = 8;
+ drv->playback.stream_name = io_playback->name;
+ }
+ if (capture_exist) {
+ snprintf(io_capture->name, RSND_DAI_NAME_SIZE, "DAI%d Capture", dai_i);
+ drv->capture.rates = RSND_RATES;
+ drv->capture.formats = RSND_FMTS;
+ drv->capture.channels_min = 2;
+ drv->capture.channels_max = 8;
+ drv->capture.stream_name = io_capture->name;
+ }
+
if (rsnd_ssi_is_pin_sharing(io_capture) ||
rsnd_ssi_is_pin_sharing(io_playback)) {
/* should have symmetric_rate if pin sharing */
@@ -1444,7 +1475,7 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
if (is_graph) {
for_each_endpoint_of_node(dai_node, dai_np) {
__rsnd_dai_probe(priv, dai_np, dai_i);
- if (rsnd_is_gen3(priv)) {
+ if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) {
rdai = rsnd_rdai_get(priv, dai_i);
rsnd_parse_connect_graph(priv, &rdai->playback, dai_np);
@@ -1455,7 +1486,7 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
} else {
for_each_child_of_node(dai_node, dai_np) {
__rsnd_dai_probe(priv, dai_np, dai_i);
- if (rsnd_is_gen3(priv)) {
+ if (rsnd_is_gen3(priv) || rsnd_is_gen4(priv)) {
rdai = rsnd_rdai_get(priv, dai_i);
rsnd_parse_connect_simple(priv, &rdai->playback, dai_np);
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
index 463ab237d7bd47..1c494e521463c0 100644
--- a/sound/soc/sh/rcar/dma.c
+++ b/sound/soc/sh/rcar/dma.c
@@ -653,22 +653,54 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io,
dma_addrs[is_ssi][is_play][use_src + use_cmd].in_addr;
}
+/*
+ * Gen4 DMA read/write register offset
+ *
+ * ex) R-Car V4H case
+ * mod / SYS-DMAC in / SYS-DMAC out
+ * SSI_SDMC: 0xec400000 / 0xec400000 / 0xec400000
+ */
+#define RDMA_SSI_SDMC(addr, i) (addr + (0x8000 * i))
+static dma_addr_t
+rsnd_gen4_dma_addr(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
+ int is_play, int is_from)
+{
+ struct rsnd_priv *priv = rsnd_io_to_priv(io);
+ phys_addr_t addr = rsnd_gen_get_phy_addr(priv, RSND_GEN4_SDMC);
+ int id = rsnd_mod_id(mod);
+ int busif = rsnd_mod_id_sub(mod);
+
+ /*
+ * SSI0 only is supported
+ */
+ if (id != 0) {
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ dev_err(dev, "This driver doesn't support non SSI0");
+ return -EINVAL;
+ }
+
+ return RDMA_SSI_SDMC(addr, busif);
+}
+
static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io,
struct rsnd_mod *mod,
int is_play, int is_from)
{
struct rsnd_priv *priv = rsnd_io_to_priv(io);
+ if (!mod)
+ return 0;
+
/*
* gen1 uses default DMA addr
*/
if (rsnd_is_gen1(priv))
return 0;
-
- if (!mod)
- return 0;
-
- return rsnd_gen2_dma_addr(io, mod, is_play, is_from);
+ else if (rsnd_is_gen4(priv))
+ return rsnd_gen4_dma_addr(io, mod, is_play, is_from);
+ else
+ return rsnd_gen2_dma_addr(io, mod, is_play, is_from);
}
#define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */
@@ -885,19 +917,28 @@ int rsnd_dma_probe(struct rsnd_priv *priv)
/*
* for Gen2 or later
*/
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp");
dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL);
- if (!dmac || !res) {
+ if (!dmac) {
dev_err(dev, "dma allocate failed\n");
return 0; /* it will be PIO mode */
}
+ /* for Gen4 doesn't have DMA-pp */
+ if (rsnd_is_gen4(priv))
+ goto audmapp_end;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp");
+ if (!res) {
+ dev_err(dev, "lack of audmapp in DT\n");
+ return 0; /* it will be PIO mode */
+ }
+
dmac->dmapp_num = 0;
dmac->ppres = res->start;
dmac->ppbase = devm_ioremap_resource(dev, res);
if (IS_ERR(dmac->ppbase))
return PTR_ERR(dmac->ppbase);
-
+audmapp_end:
priv->dma = dmac;
/* dummy mem mod for debug */
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index 925565baaa413a..86bdecc24956b4 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -216,6 +216,74 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
}
/*
+ * Gen4
+ */
+static int rsnd_gen4_probe(struct rsnd_priv *priv)
+{
+ static const struct rsnd_regmap_field_conf conf_ssiu[] = {
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE0, 0x850),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE2, 0x858),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE4, 0x890),
+ RSND_GEN_S_REG(SSI_SYS_INT_ENABLE6, 0x898),
+ RSND_GEN_S_REG(SSI_SYS_STATUS0, 0x840),
+ RSND_GEN_S_REG(SSI_SYS_STATUS2, 0x848),
+ RSND_GEN_S_REG(SSI_SYS_STATUS4, 0x880),
+ RSND_GEN_S_REG(SSI_SYS_STATUS6, 0x888),
+
+ RSND_GEN_S_REG(SSI_BUSIF0_MODE, 0x0),
+ RSND_GEN_S_REG(SSI_BUSIF0_ADINR, 0x4),
+ RSND_GEN_S_REG(SSI_BUSIF0_DALIGN, 0x8),
+ RSND_GEN_S_REG(SSI_BUSIF1_MODE, 0x20),
+ RSND_GEN_S_REG(SSI_BUSIF1_ADINR, 0x24),
+ RSND_GEN_S_REG(SSI_BUSIF1_DALIGN, 0x28),
+ RSND_GEN_S_REG(SSI_BUSIF2_MODE, 0x40),
+ RSND_GEN_S_REG(SSI_BUSIF2_ADINR, 0x44),
+ RSND_GEN_S_REG(SSI_BUSIF2_DALIGN, 0x48),
+ RSND_GEN_S_REG(SSI_BUSIF3_MODE, 0x60),
+ RSND_GEN_S_REG(SSI_BUSIF3_ADINR, 0x64),
+ RSND_GEN_S_REG(SSI_BUSIF3_DALIGN, 0x68),
+ RSND_GEN_S_REG(SSI_BUSIF4_MODE, 0x500),
+ RSND_GEN_S_REG(SSI_BUSIF4_ADINR, 0x504),
+ RSND_GEN_S_REG(SSI_BUSIF4_DALIGN, 0x508),
+ RSND_GEN_S_REG(SSI_BUSIF5_MODE, 0x520),
+ RSND_GEN_S_REG(SSI_BUSIF5_ADINR, 0x524),
+ RSND_GEN_S_REG(SSI_BUSIF5_DALIGN, 0x528),
+ RSND_GEN_S_REG(SSI_BUSIF6_MODE, 0x540),
+ RSND_GEN_S_REG(SSI_BUSIF6_ADINR, 0x544),
+ RSND_GEN_S_REG(SSI_BUSIF6_DALIGN, 0x548),
+ RSND_GEN_S_REG(SSI_BUSIF7_MODE, 0x560),
+ RSND_GEN_S_REG(SSI_BUSIF7_ADINR, 0x564),
+ RSND_GEN_S_REG(SSI_BUSIF7_DALIGN, 0x568),
+ RSND_GEN_S_REG(SSI_CTRL, 0x010),
+ RSND_GEN_S_REG(SSI_INT_ENABLE, 0x018),
+ RSND_GEN_S_REG(SSI_MODE, 0x00c),
+ RSND_GEN_S_REG(SSI_MODE2, 0xa0c),
+ };
+ static const struct rsnd_regmap_field_conf conf_adg[] = {
+ RSND_GEN_S_REG(BRRA, 0x00),
+ RSND_GEN_S_REG(BRRB, 0x04),
+ RSND_GEN_S_REG(BRGCKR, 0x08),
+ RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c),
+ };
+ static const struct rsnd_regmap_field_conf conf_ssi[] = {
+ RSND_GEN_S_REG(SSICR, 0x00),
+ RSND_GEN_S_REG(SSISR, 0x04),
+ RSND_GEN_S_REG(SSITDR, 0x08),
+ RSND_GEN_S_REG(SSIRDR, 0x0c),
+ RSND_GEN_S_REG(SSIWSR, 0x20),
+ };
+ static const struct rsnd_regmap_field_conf conf_sdmc[] = {
+ RSND_GEN_M_REG(SSI_BUSIF, 0x0, 0x8000),
+ };
+ int ret_adg = rsnd_gen_regmap_init(priv, 10, RSND_GEN4_ADG, "adg", conf_adg);
+ int ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN4_SSIU, "ssiu", conf_ssiu);
+ int ret_ssi = rsnd_gen_regmap_init(priv, 10, RSND_GEN4_SSI, "ssi", conf_ssi);
+ int ret_sdmc = rsnd_gen_regmap_init(priv, 10, RSND_GEN4_SDMC, "sdmc", conf_sdmc);
+
+ return ret_adg | ret_ssiu | ret_ssi | ret_sdmc;
+}
+
+/*
* Gen2
*/
static int rsnd_gen2_probe(struct rsnd_priv *priv)
@@ -484,6 +552,8 @@ int rsnd_gen_probe(struct rsnd_priv *priv)
else if (rsnd_is_gen2(priv) ||
rsnd_is_gen3(priv))
ret = rsnd_gen2_probe(priv);
+ else if (rsnd_is_gen4(priv))
+ ret = rsnd_gen4_probe(priv);
if (ret < 0)
dev_err(dev, "unknown generation R-Car sound device\n");
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index d9cd190d7e198e..239705d5251744 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -31,6 +31,11 @@
#define RSND_GEN2_SSIU 2
#define RSND_GEN2_SSI 3
+#define RSND_GEN4_ADG 0
+#define RSND_GEN4_SSIU 1
+#define RSND_GEN4_SSI 2
+#define RSND_GEN4_SDMC 3
+
#define RSND_BASE_MAX 4
/*
@@ -197,6 +202,7 @@ enum rsnd_reg {
SSI_SYS_INT_ENABLE5,
SSI_SYS_INT_ENABLE6,
SSI_SYS_INT_ENABLE7,
+ SSI_BUSIF,
HDMI0_SEL,
HDMI1_SEL,
SSI9_BUSIF0_MODE,
@@ -513,6 +519,7 @@ struct rsnd_dai_stream {
#define RSND_STREAM_HDMI0 (1 << 0) /* for HDMI0 */
#define RSND_STREAM_HDMI1 (1 << 1) /* for HDMI1 */
#define RSND_STREAM_TDM_SPLIT (1 << 2) /* for TDM split mode */
+#define RSND_HW_RULE_ERR (1 << 3) /* hw_rule error */
#define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL)
#define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI)
@@ -628,6 +635,7 @@ struct rsnd_priv {
#define RSND_GEN1 (1 << 0)
#define RSND_GEN2 (2 << 0)
#define RSND_GEN3 (3 << 0)
+#define RSND_GEN4 (4 << 0)
#define RSND_SOC_MASK (0xFF << 4)
#define RSND_SOC_E (1 << 4) /* E1/E2/E3 */
@@ -702,6 +710,7 @@ struct rsnd_priv {
#define rsnd_is_gen1(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN1)
#define rsnd_is_gen2(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN2)
#define rsnd_is_gen3(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN3)
+#define rsnd_is_gen4(priv) (((priv)->flags & RSND_GEN_MASK) == RSND_GEN4)
#define rsnd_is_e3(priv) (((priv)->flags & \
(RSND_GEN_MASK | RSND_SOC_MASK)) == \
(RSND_GEN3 | RSND_SOC_E))
@@ -891,18 +900,6 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
dev_info(dev, param); \
} while (0)
-/*
- * If you don't need rsnd_dai_call debug message,
- * define RSND_DEBUG_NO_DAI_CALL as 1 on top of core.c
- *
- * #define RSND_DEBUG_NO_DAI_CALL 1
- */
-#define rsnd_dbg_dai_call(dev, param...) \
- if (!IS_BUILTIN(RSND_DEBUG_NO_DAI_CALL)) \
- dev_dbg(dev, param)
-
-#endif
-
#ifdef CONFIG_DEBUG_FS
int rsnd_debugfs_probe(struct snd_soc_component *component);
void rsnd_debugfs_reg_show(struct seq_file *m, phys_addr_t _addr,
@@ -913,3 +910,5 @@ void rsnd_debugfs_mod_reg_show(struct seq_file *m, struct rsnd_mod *mod,
#else
#define rsnd_debugfs_probe NULL
#endif
+
+#endif /* RSND_H */
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 7ade6c5ed96ff2..8ddee5b03ece0d 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -303,15 +303,14 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
return 0;
}
+ ret = -EIO;
main_rate = rsnd_ssi_clk_query(rdai, rate, chan, &idx);
- if (!main_rate) {
- dev_err(dev, "unsupported clock rate\n");
- return -EIO;
- }
+ if (!main_rate)
+ goto rate_err;
ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
if (ret < 0)
- return ret;
+ goto rate_err;
/*
* SSI clock will be output contiguously
@@ -333,6 +332,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
rsnd_mod_name(mod), chan, rate);
return 0;
+
+rate_err:
+ dev_err(dev, "unsupported clock rate\n");
+ return ret;
}
static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod,
diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c
index 281bc20d4c5d0c..17bd8cc86dd0a7 100644
--- a/sound/soc/sh/rcar/ssiu.c
+++ b/sound/soc/sh/rcar/ssiu.c
@@ -29,8 +29,8 @@ struct rsnd_ssiu {
i++)
/*
- * SSI Gen2 Gen3
- * 0 BUSIF0-3 BUSIF0-7
+ * SSI Gen2 Gen3 Gen4
+ * 0 BUSIF0-3 BUSIF0-7 BUSIF0-7
* 1 BUSIF0-3 BUSIF0-7
* 2 BUSIF0-3 BUSIF0-7
* 3 BUSIF0 BUSIF0-7
@@ -40,10 +40,11 @@ struct rsnd_ssiu {
* 7 BUSIF0 BUSIF0
* 8 BUSIF0 BUSIF0
* 9 BUSIF0-3 BUSIF0-7
- * total 22 52
+ * total 22 52 8
*/
static const int gen2_id[] = { 0, 4, 8, 12, 13, 14, 15, 16, 17, 18 };
static const int gen3_id[] = { 0, 8, 16, 24, 32, 40, 41, 42, 43, 44 };
+static const int gen4_id[] = { 0 };
/* enable busif buffer over/under run interrupt. */
#define rsnd_ssiu_busif_err_irq_enable(mod) rsnd_ssiu_busif_err_irq_ctrl(mod, 1)
@@ -152,6 +153,10 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
/* clear status */
rsnd_ssiu_busif_err_status_clear(mod);
+ /* Gen4 doesn't have SSI_MODE */
+ if (rsnd_is_gen4(priv))
+ goto ssi_mode_setting_end;
+
/*
* SSI_MODE0
*/
@@ -206,6 +211,7 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
rsnd_mod_bset(mod, SSI_MODE1, 0x0013001f, val1);
rsnd_mod_bset(mod, SSI_MODE2, 0x00000017, val2);
+ssi_mode_setting_end:
/*
* Enable busif buffer over/under run interrupt.
* It will be handled from ssi.c
@@ -553,6 +559,9 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv)
} else if (rsnd_is_gen3(priv)) {
list = gen3_id;
nr = ARRAY_SIZE(gen3_id);
+ } else if (rsnd_is_gen4(priv)) {
+ list = gen4_id;
+ nr = ARRAY_SIZE(gen4_id);
} else {
dev_err(dev, "unknown SSIU\n");
return -ENODEV;
diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c
index 32c5be61e2ec82..4e4fe29ade502f 100644
--- a/sound/soc/soc-ac97.c
+++ b/sound/soc/soc-ac97.c
@@ -14,10 +14,9 @@
#include <linux/ctype.h>
#include <linux/delay.h>
#include <linux/export.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
-#include <linux/of_gpio.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/slab.h>
@@ -29,9 +28,9 @@ struct snd_ac97_reset_cfg {
struct pinctrl_state *pstate_reset;
struct pinctrl_state *pstate_warm_reset;
struct pinctrl_state *pstate_run;
- int gpio_sdata;
- int gpio_sync;
- int gpio_reset;
+ struct gpio_desc *reset_gpio;
+ struct gpio_desc *sdata_gpio;
+ struct gpio_desc *sync_gpio;
};
static struct snd_ac97_bus soc_ac97_bus = {
@@ -268,11 +267,11 @@ static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
+ gpiod_direction_output_raw(snd_ac97_rst_cfg.sync_gpio, 1);
udelay(10);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+ gpiod_direction_output_raw(snd_ac97_rst_cfg.sync_gpio, 0);
pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
msleep(2);
@@ -284,13 +283,13 @@ static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
+ gpiod_direction_output_raw(snd_ac97_rst_cfg.sync_gpio, 0);
+ gpiod_direction_output_raw(snd_ac97_rst_cfg.sdata_gpio, 0);
+ gpiod_direction_output_raw(snd_ac97_rst_cfg.reset_gpio, 0);
udelay(10);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
+ gpiod_direction_output_raw(snd_ac97_rst_cfg.reset_gpio, 1);
pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
msleep(2);
@@ -301,8 +300,6 @@ static int snd_soc_ac97_parse_pinctl(struct device *dev,
{
struct pinctrl *p;
struct pinctrl_state *state;
- int gpio;
- int ret;
p = devm_pinctrl_get(dev);
if (IS_ERR(p)) {
@@ -332,41 +329,20 @@ static int snd_soc_ac97_parse_pinctl(struct device *dev,
}
cfg->pstate_run = state;
- gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
- if (gpio < 0) {
- dev_err(dev, "Can't find ac97-sync gpio\n");
- return gpio;
- }
- ret = devm_gpio_request(dev, gpio, "AC97 link sync");
- if (ret) {
- dev_err(dev, "Failed requesting ac97-sync gpio\n");
- return ret;
- }
- cfg->gpio_sync = gpio;
+ cfg->sync_gpio = devm_gpiod_get_index(dev, "ac97", 0, GPIOD_ASIS);
+ if (IS_ERR(cfg->sync_gpio))
+ return dev_err_probe(dev, PTR_ERR(cfg->sync_gpio), "Can't find ac97-sync gpio\n");
+ gpiod_set_consumer_name(cfg->sync_gpio, "AC97 link sync");
- gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
- if (gpio < 0) {
- dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
- return gpio;
- }
- ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
- if (ret) {
- dev_err(dev, "Failed requesting ac97-sdata gpio\n");
- return ret;
- }
- cfg->gpio_sdata = gpio;
+ cfg->sdata_gpio = devm_gpiod_get_index(dev, "ac97", 1, GPIOD_ASIS);
+ if (IS_ERR(cfg->sdata_gpio))
+ return dev_err_probe(dev, PTR_ERR(cfg->sdata_gpio), "Can't find ac97-sdata gpio\n");
+ gpiod_set_consumer_name(cfg->sdata_gpio, "AC97 link sdata");
- gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
- if (gpio < 0) {
- dev_err(dev, "Can't find ac97-reset gpio\n");
- return gpio;
- }
- ret = devm_gpio_request(dev, gpio, "AC97 link reset");
- if (ret) {
- dev_err(dev, "Failed requesting ac97-reset gpio\n");
- return ret;
- }
- cfg->gpio_reset = gpio;
+ cfg->reset_gpio = devm_gpiod_get_index(dev, "ac97", 2, GPIOD_ASIS);
+ if (IS_ERR(cfg->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(cfg->reset_gpio), "Can't find ac97-reset gpio\n");
+ gpiod_set_consumer_name(cfg->reset_gpio, "AC97 link reset");
return 0;
}
diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c
index e12f8244242b9a..3cd6952212e1f6 100644
--- a/sound/soc/soc-component.c
+++ b/sound/soc/soc-component.c
@@ -256,6 +256,26 @@ int snd_soc_component_set_jack(struct snd_soc_component *component,
}
EXPORT_SYMBOL_GPL(snd_soc_component_set_jack);
+/**
+ * snd_soc_component_get_jack_type
+ * @component: COMPONENTs
+ *
+ * Returns the jack type of the component
+ * This can either be the supported type or one read from
+ * devicetree with the property: jack-type.
+ */
+int snd_soc_component_get_jack_type(
+ struct snd_soc_component *component)
+{
+ int ret = -ENOTSUPP;
+
+ if (component->driver->get_jack_type)
+ ret = component->driver->get_jack_type(component);
+
+ return soc_component_ret(component, ret);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_get_jack_type);
+
int snd_soc_component_module_get(struct snd_soc_component *component,
void *mark, int upon_open)
{
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index 870f13e1d389c8..e7aa6f360cabef 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -149,6 +149,8 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
if (ret < 0)
goto be_err;
+ mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass);
+
/* calculate valid and active FE <-> BE dpcms */
dpcm_process_paths(fe, stream, &list, 1);
fe->dpcm[stream].runtime = fe_substream->runtime;
@@ -184,7 +186,6 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
- mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass);
snd_soc_runtime_activate(fe, stream);
mutex_unlock(&fe->card->pcm_mutex);
@@ -215,7 +216,6 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass);
snd_soc_runtime_deactivate(fe, stream);
- mutex_unlock(&fe->card->pcm_mutex);
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
@@ -234,6 +234,8 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
dpcm_be_disconnect(fe, stream);
+ mutex_unlock(&fe->card->pcm_mutex);
+
fe->dpcm[stream].runtime = NULL;
snd_soc_link_compr_shutdown(cstream, 0);
@@ -409,8 +411,9 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
ret = snd_soc_link_compr_set_params(cstream);
if (ret < 0)
goto out;
-
+ mutex_lock_nested(&fe->card->pcm_mutex, fe->card->pcm_subclass);
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
+ mutex_unlock(&fe->card->pcm_mutex);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
out:
@@ -623,7 +626,7 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
rtd->fe_compr = 1;
if (rtd->dai_link->dpcm_playback)
be_pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream->private_data = rtd;
- else if (rtd->dai_link->dpcm_capture)
+ if (rtd->dai_link->dpcm_capture)
be_pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream->private_data = rtd;
memcpy(compr->ops, &soc_compr_dyn_ops, sizeof(soc_compr_dyn_ops));
} else {
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index e613698824fdd0..71b022f7edfd5b 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -553,7 +553,7 @@ int snd_soc_suspend(struct device *dev)
int i;
/* If the card is not initialized yet there is nothing to do */
- if (!card->instantiated)
+ if (!snd_soc_card_is_instantiated(card))
return 0;
/*
@@ -695,7 +695,7 @@ int snd_soc_resume(struct device *dev)
struct snd_soc_component *component;
/* If the card is not initialized yet there is nothing to do */
- if (!card->instantiated)
+ if (!snd_soc_card_is_instantiated(card))
return 0;
/* activate pins from sleep state */
@@ -1915,7 +1915,7 @@ static void soc_cleanup_card_resources(struct snd_soc_card *card)
static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister)
{
- if (card->instantiated) {
+ if (snd_soc_card_is_instantiated(card)) {
card->instantiated = false;
snd_soc_flush_all_delayed_work(card);
@@ -2126,7 +2126,7 @@ int snd_soc_poweroff(struct device *dev)
struct snd_soc_card *card = dev_get_drvdata(dev);
struct snd_soc_component *component;
- if (!card->instantiated)
+ if (!snd_soc_card_is_instantiated(card))
return 0;
/*
diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c
index 29a75fdf90e095..0119afbd01fc04 100644
--- a/sound/soc/soc-dai.c
+++ b/sound/soc/soc-dai.c
@@ -267,6 +267,11 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
int slots, int slot_width)
{
int ret = -ENOTSUPP;
+ int stream;
+ unsigned int *tdm_mask[] = {
+ &tx_mask,
+ &rx_mask,
+ };
if (dai->driver->ops &&
dai->driver->ops->xlate_tdm_slot_mask)
@@ -275,8 +280,8 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
else
snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask);
- dai->tx_mask = tx_mask;
- dai->rx_mask = rx_mask;
+ for_each_pcm_streams(stream)
+ snd_soc_dai_tdm_mask_set(dai, stream, *tdm_mask[stream]);
if (dai->driver->ops &&
dai->driver->ops->set_tdm_slot)
@@ -509,7 +514,7 @@ void snd_soc_dai_action(struct snd_soc_dai *dai,
int stream, int action)
{
/* see snd_soc_dai_stream_active() */
- dai->stream_active[stream] += action;
+ dai->stream[stream].active += action;
/* see snd_soc_component_active() */
dai->component->active += action;
@@ -522,7 +527,7 @@ int snd_soc_dai_active(struct snd_soc_dai *dai)
active = 0;
for_each_pcm_streams(stream)
- active += dai->stream_active[stream];
+ active += dai->stream[stream].active;
return active;
}
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index f61c8633e7eb39..5d9a671e50f194 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -149,7 +149,7 @@ static int dapm_down_seq[] = {
static void dapm_assert_locked(struct snd_soc_dapm_context *dapm)
{
- if (dapm->card && dapm->card->instantiated)
+ if (snd_soc_card_is_instantiated(dapm->card))
lockdep_assert_held(&dapm->card->dapm_mutex);
}
@@ -1297,7 +1297,7 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
enum snd_soc_dapm_direction))
{
struct snd_soc_card *card = dai->component->card;
- struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, stream);
LIST_HEAD(widgets);
int paths;
int ret;
@@ -1305,12 +1305,10 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- w = dai->playback_widget;
invalidate_paths_ep(w, SND_SOC_DAPM_DIR_OUT);
paths = is_connected_output_ep(w, &widgets,
custom_stop_condition);
} else {
- w = dai->capture_widget;
invalidate_paths_ep(w, SND_SOC_DAPM_DIR_IN);
paths = is_connected_input_ep(w, &widgets,
custom_stop_condition);
@@ -2614,7 +2612,7 @@ int snd_soc_dapm_sync_unlocked(struct snd_soc_dapm_context *dapm)
* Suppress early reports (eg, jacks syncing their state) to avoid
* silly DAPM runs during card startup.
*/
- if (!dapm->card || !dapm->card->instantiated)
+ if (!snd_soc_card_is_instantiated(dapm->card))
return 0;
return dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP);
@@ -2908,7 +2906,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
dapm_mark_dirty(path->node[dir], "Route added");
}
- if (dapm->card->instantiated && path->connect)
+ if (snd_soc_card_is_instantiated(dapm->card) && path->connect)
dapm_path_invalidate(path);
return 0;
@@ -4229,7 +4227,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
return PTR_ERR(w);
w->priv = dai;
- dai->playback_widget = w;
+ snd_soc_dai_set_widget_playback(dai, w);
}
if (dai->driver->capture.stream_name) {
@@ -4245,7 +4243,7 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
return PTR_ERR(w);
w->priv = dai;
- dai->capture_widget = w;
+ snd_soc_dai_set_widget_capture(dai, w);
}
return 0;
@@ -4339,16 +4337,16 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card,
int stream;
if (dai_link->params) {
- playback_cpu = cpu_dai->capture_widget;
- capture_cpu = cpu_dai->playback_widget;
+ playback_cpu = snd_soc_dai_get_widget_capture(cpu_dai);
+ capture_cpu = snd_soc_dai_get_widget_playback(cpu_dai);
} else {
- playback_cpu = cpu_dai->playback_widget;
- capture_cpu = cpu_dai->capture_widget;
+ playback_cpu = snd_soc_dai_get_widget_playback(cpu_dai);
+ capture_cpu = snd_soc_dai_get_widget_capture(cpu_dai);
}
/* connect BE DAI playback if widgets are valid */
stream = SNDRV_PCM_STREAM_PLAYBACK;
- codec = codec_dai->playback_widget;
+ codec = snd_soc_dai_get_widget(codec_dai, stream);
if (playback_cpu && codec) {
if (dai_link->params && !rtd->c2c_widget[stream]) {
@@ -4367,7 +4365,7 @@ static void dapm_connect_dai_pair(struct snd_soc_card *card,
capture:
/* connect BE DAI capture if widgets are valid */
stream = SNDRV_PCM_STREAM_CAPTURE;
- codec = codec_dai->capture_widget;
+ codec = snd_soc_dai_get_widget(codec_dai, stream);
if (codec && capture_cpu) {
if (dai_link->params && !rtd->c2c_widget[stream]) {
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 579a44d81d9a31..005b179a770a05 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1012,6 +1012,7 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd,
for_each_rtd_codec_dais(rtd, i, codec_dai) {
struct snd_pcm_hw_params codec_params;
+ unsigned int tdm_mask = snd_soc_dai_tdm_mask_get(codec_dai, substream->stream);
/*
* Skip CODECs which don't support the current stream type,
@@ -1034,15 +1035,8 @@ static int __soc_pcm_hw_params(struct snd_soc_pcm_runtime *rtd,
codec_params = *params;
/* fixup params based on TDM slot masks */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
- codec_dai->tx_mask)
- soc_pcm_codec_params_fixup(&codec_params,
- codec_dai->tx_mask);
-
- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
- codec_dai->rx_mask)
- soc_pcm_codec_params_fixup(&codec_params,
- codec_dai->rx_mask);
+ if (tdm_mask)
+ soc_pcm_codec_params_fixup(&codec_params, tdm_mask);
ret = snd_soc_dai_hw_params(codec_dai, substream,
&codec_params);
@@ -1337,7 +1331,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
return NULL;
}
-static int widget_in_list(struct snd_soc_dapm_widget_list *list,
+int widget_in_list(struct snd_soc_dapm_widget_list *list,
struct snd_soc_dapm_widget *widget)
{
struct snd_soc_dapm_widget *w;
@@ -1349,6 +1343,7 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list,
return 0;
}
+EXPORT_SYMBOL_GPL(widget_in_list);
bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir)
{
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index a79a2fb260b87e..07421f5d4ebd8b 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -44,9 +44,8 @@
#define SOC_TPLG_PASS_WIDGET 3
#define SOC_TPLG_PASS_PCM_DAI 4
#define SOC_TPLG_PASS_GRAPH 5
-#define SOC_TPLG_PASS_PINS 6
-#define SOC_TPLG_PASS_BE_DAI 7
-#define SOC_TPLG_PASS_LINK 8
+#define SOC_TPLG_PASS_BE_DAI 6
+#define SOC_TPLG_PASS_LINK 7
#define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST
#define SOC_TPLG_PASS_END SOC_TPLG_PASS_LINK
@@ -77,9 +76,6 @@ struct soc_tplg {
struct snd_soc_tplg_ops *ops;
};
-static int soc_tplg_process_headers(struct soc_tplg *tplg);
-static int soc_tplg_complete(struct soc_tplg *tplg);
-
/* check we dont overflow the data for this control chunk */
static int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size,
unsigned int count, size_t bytes, const char *elem_type)
@@ -186,7 +182,7 @@ static const struct soc_tplg_map dapm_map[] = {
{SND_SOC_TPLG_DAPM_DECODER, snd_soc_dapm_decoder},
};
-static int tplc_chan_get_reg(struct soc_tplg *tplg,
+static int tplg_chan_get_reg(struct soc_tplg *tplg,
struct snd_soc_tplg_channel *chan, int map)
{
int i;
@@ -199,7 +195,7 @@ static int tplc_chan_get_reg(struct soc_tplg *tplg,
return -EINVAL;
}
-static int tplc_chan_get_shift(struct soc_tplg *tplg,
+static int tplg_chan_get_shift(struct soc_tplg *tplg,
struct snd_soc_tplg_channel *chan, int map)
{
int i;
@@ -354,69 +350,37 @@ static int soc_tplg_add_kcontrol(struct soc_tplg *tplg,
tplg->dev, k, comp->name_prefix, comp, kcontrol);
}
-/* remove a mixer kcontrol */
-static void remove_mixer(struct snd_soc_component *comp,
- struct snd_soc_dobj *dobj, int pass)
-{
- struct snd_card *card = comp->card->snd_card;
-
- if (pass != SOC_TPLG_PASS_CONTROL)
- return;
-
- if (dobj->ops && dobj->ops->control_unload)
- dobj->ops->control_unload(comp, dobj);
-
- snd_ctl_remove(card, dobj->control.kcontrol);
- list_del(&dobj->list);
-}
-
-/* remove an enum kcontrol */
-static void remove_enum(struct snd_soc_component *comp,
- struct snd_soc_dobj *dobj, int pass)
-{
- struct snd_card *card = comp->card->snd_card;
-
- if (pass != SOC_TPLG_PASS_CONTROL)
- return;
-
- if (dobj->ops && dobj->ops->control_unload)
- dobj->ops->control_unload(comp, dobj);
-
- snd_ctl_remove(card, dobj->control.kcontrol);
- list_del(&dobj->list);
-}
-
-/* remove a byte kcontrol */
-static void remove_bytes(struct snd_soc_component *comp,
- struct snd_soc_dobj *dobj, int pass)
+/* remove kcontrol */
+static void soc_tplg_remove_kcontrol(struct snd_soc_component *comp, struct snd_soc_dobj *dobj,
+ int pass)
{
struct snd_card *card = comp->card->snd_card;
if (pass != SOC_TPLG_PASS_CONTROL)
return;
- if (dobj->ops && dobj->ops->control_unload)
- dobj->ops->control_unload(comp, dobj);
+ if (dobj->unload)
+ dobj->unload(comp, dobj);
snd_ctl_remove(card, dobj->control.kcontrol);
list_del(&dobj->list);
}
/* remove a route */
-static void remove_route(struct snd_soc_component *comp,
+static void soc_tplg_remove_route(struct snd_soc_component *comp,
struct snd_soc_dobj *dobj, int pass)
{
if (pass != SOC_TPLG_PASS_GRAPH)
return;
- if (dobj->ops && dobj->ops->dapm_route_unload)
- dobj->ops->dapm_route_unload(comp, dobj);
+ if (dobj->unload)
+ dobj->unload(comp, dobj);
list_del(&dobj->list);
}
/* remove a widget and it's kcontrols - routes must be removed first */
-static void remove_widget(struct snd_soc_component *comp,
+static void soc_tplg_remove_widget(struct snd_soc_component *comp,
struct snd_soc_dobj *dobj, int pass)
{
struct snd_card *card = comp->card->snd_card;
@@ -427,8 +391,8 @@ static void remove_widget(struct snd_soc_component *comp,
if (pass != SOC_TPLG_PASS_WIDGET)
return;
- if (dobj->ops && dobj->ops->widget_unload)
- dobj->ops->widget_unload(comp, dobj);
+ if (dobj->unload)
+ dobj->unload(comp, dobj);
if (!w->kcontrols)
goto free_news;
@@ -444,7 +408,7 @@ free_news:
}
/* remove DAI configurations */
-static void remove_dai(struct snd_soc_component *comp,
+static void soc_tplg_remove_dai(struct snd_soc_component *comp,
struct snd_soc_dobj *dobj, int pass)
{
struct snd_soc_dai_driver *dai_drv =
@@ -454,8 +418,8 @@ static void remove_dai(struct snd_soc_component *comp,
if (pass != SOC_TPLG_PASS_PCM_DAI)
return;
- if (dobj->ops && dobj->ops->dai_unload)
- dobj->ops->dai_unload(comp, dobj);
+ if (dobj->unload)
+ dobj->unload(comp, dobj);
for_each_component_dais_safe(comp, dai, _dai)
if (dai->driver == dai_drv)
@@ -465,7 +429,7 @@ static void remove_dai(struct snd_soc_component *comp,
}
/* remove link configurations */
-static void remove_link(struct snd_soc_component *comp,
+static void soc_tplg_remove_link(struct snd_soc_component *comp,
struct snd_soc_dobj *dobj, int pass)
{
struct snd_soc_dai_link *link =
@@ -474,8 +438,8 @@ static void remove_link(struct snd_soc_component *comp,
if (pass != SOC_TPLG_PASS_PCM_DAI)
return;
- if (dobj->ops && dobj->ops->link_unload)
- dobj->ops->link_unload(comp, dobj);
+ if (dobj->unload)
+ dobj->unload(comp, dobj);
list_del(&dobj->list);
snd_soc_remove_pcm_runtime(comp->card,
@@ -489,11 +453,11 @@ static void remove_backend_link(struct snd_soc_component *comp,
if (pass != SOC_TPLG_PASS_LINK)
return;
- if (dobj->ops && dobj->ops->link_unload)
- dobj->ops->link_unload(comp, dobj);
+ if (dobj->unload)
+ dobj->unload(comp, dobj);
/*
- * We don't free the link here as what remove_link() do since BE
+ * We don't free the link here as what soc_tplg_remove_link() do since BE
* links are not allocated by topology.
* We however need to reset the dobj type to its initial values
*/
@@ -714,7 +678,8 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size)
sbe->max = le32_to_cpu(be->max);
sbe->dobj.type = SND_SOC_DOBJ_BYTES;
- sbe->dobj.ops = tplg->ops;
+ if (tplg->ops)
+ sbe->dobj.unload = tplg->ops->control_unload;
INIT_LIST_HEAD(&sbe->dobj.list);
/* map io handlers */
@@ -725,7 +690,7 @@ static int soc_tplg_dbytes_create(struct soc_tplg *tplg, size_t size)
}
/* pass control to driver for optional further init */
- ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)be);
+ ret = soc_tplg_control_load(tplg, &kc, &be->hdr);
if (ret < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n", be->hdr.name);
goto err;
@@ -780,18 +745,19 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size)
kc.access = le32_to_cpu(mc->hdr.access);
/* we only support FL/FR channel mapping atm */
- sm->reg = tplc_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FL);
- sm->rreg = tplc_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FR);
- sm->shift = tplc_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FL);
- sm->rshift = tplc_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FR);
+ sm->reg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FL);
+ sm->rreg = tplg_chan_get_reg(tplg, mc->channel, SNDRV_CHMAP_FR);
+ sm->shift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FL);
+ sm->rshift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FR);
sm->max = le32_to_cpu(mc->max);
sm->min = le32_to_cpu(mc->min);
sm->invert = le32_to_cpu(mc->invert);
sm->platform_max = le32_to_cpu(mc->platform_max);
sm->dobj.index = tplg->index;
- sm->dobj.ops = tplg->ops;
sm->dobj.type = SND_SOC_DOBJ_MIXER;
+ if (tplg->ops)
+ sm->dobj.unload = tplg->ops->control_unload;
INIT_LIST_HEAD(&sm->dobj.list);
/* map io handlers */
@@ -809,7 +775,7 @@ static int soc_tplg_dmixer_create(struct soc_tplg *tplg, size_t size)
}
/* pass control to driver for optional further init */
- ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)mc);
+ ret = soc_tplg_control_load(tplg, &kc, &mc->hdr);
if (ret < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n", mc->hdr.name);
goto err;
@@ -927,16 +893,17 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size)
kc.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
kc.access = le32_to_cpu(ec->hdr.access);
- se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
- se->shift_l = tplc_chan_get_shift(tplg, ec->channel,
+ se->reg = tplg_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
+ se->shift_l = tplg_chan_get_shift(tplg, ec->channel,
SNDRV_CHMAP_FL);
- se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
+ se->shift_r = tplg_chan_get_shift(tplg, ec->channel,
SNDRV_CHMAP_FL);
se->mask = le32_to_cpu(ec->mask);
se->dobj.index = tplg->index;
se->dobj.type = SND_SOC_DOBJ_ENUM;
- se->dobj.ops = tplg->ops;
+ if (tplg->ops)
+ se->dobj.unload = tplg->ops->control_unload;
INIT_LIST_HEAD(&se->dobj.list);
switch (le32_to_cpu(ec->hdr.ops.info)) {
@@ -977,7 +944,7 @@ static int soc_tplg_denum_create(struct soc_tplg *tplg, size_t size)
}
/* pass control to driver for optional further init */
- ret = soc_tplg_control_load(tplg, &kc, (struct snd_soc_tplg_ctl_hdr *)ec);
+ ret = soc_tplg_control_load(tplg, &kc, &ec->hdr);
if (ret < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n", ec->hdr.name);
goto err;
@@ -1113,7 +1080,8 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg,
/* add route dobj to dobj_list */
route->dobj.type = SND_SOC_DOBJ_GRAPH;
- route->dobj.ops = tplg->ops;
+ if (tplg->ops)
+ route->dobj.unload = tplg->ops->dapm_route_unload;
route->dobj.index = tplg->index;
list_add(&route->dobj.list, &tplg->comp->dobj_list);
@@ -1161,13 +1129,13 @@ static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_
kc->access = le32_to_cpu(mc->hdr.access);
/* we only support FL/FR channel mapping atm */
- sm->reg = tplc_chan_get_reg(tplg, mc->channel,
+ sm->reg = tplg_chan_get_reg(tplg, mc->channel,
SNDRV_CHMAP_FL);
- sm->rreg = tplc_chan_get_reg(tplg, mc->channel,
+ sm->rreg = tplg_chan_get_reg(tplg, mc->channel,
SNDRV_CHMAP_FR);
- sm->shift = tplc_chan_get_shift(tplg, mc->channel,
+ sm->shift = tplg_chan_get_shift(tplg, mc->channel,
SNDRV_CHMAP_FL);
- sm->rshift = tplc_chan_get_shift(tplg, mc->channel,
+ sm->rshift = tplg_chan_get_shift(tplg, mc->channel,
SNDRV_CHMAP_FR);
sm->max = le32_to_cpu(mc->max);
@@ -1193,7 +1161,7 @@ static int soc_tplg_dapm_widget_dmixer_create(struct soc_tplg *tplg, struct snd_
}
/* pass control to driver for optional further init */
- err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)mc);
+ err = soc_tplg_control_load(tplg, kc, &mc->hdr);
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n",
mc->hdr.name);
@@ -1233,10 +1201,10 @@ static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_k
kc->access = le32_to_cpu(ec->hdr.access);
/* we only support FL/FR channel mapping atm */
- se->reg = tplc_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
- se->shift_l = tplc_chan_get_shift(tplg, ec->channel,
+ se->reg = tplg_chan_get_reg(tplg, ec->channel, SNDRV_CHMAP_FL);
+ se->shift_l = tplg_chan_get_shift(tplg, ec->channel,
SNDRV_CHMAP_FL);
- se->shift_r = tplc_chan_get_shift(tplg, ec->channel,
+ se->shift_r = tplg_chan_get_shift(tplg, ec->channel,
SNDRV_CHMAP_FR);
se->items = le32_to_cpu(ec->items);
@@ -1277,7 +1245,7 @@ static int soc_tplg_dapm_widget_denum_create(struct soc_tplg *tplg, struct snd_k
}
/* pass control to driver for optional further init */
- err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)ec);
+ err = soc_tplg_control_load(tplg, kc, &ec->hdr);
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n",
ec->hdr.name);
@@ -1329,7 +1297,7 @@ static int soc_tplg_dapm_widget_dbytes_create(struct soc_tplg *tplg, struct snd_
}
/* pass control to driver for optional further init */
- err = soc_tplg_control_load(tplg, kc, (struct snd_soc_tplg_ctl_hdr *)be);
+ err = soc_tplg_control_load(tplg, kc, &be->hdr);
if (err < 0) {
dev_err(tplg->dev, "ASoC: failed to init %s\n",
be->hdr.name);
@@ -1472,7 +1440,7 @@ widget:
/* card dapm mutex is held by the core if we are loading topology
* data during sound card init. */
- if (card->instantiated)
+ if (snd_soc_card_is_instantiated(card))
widget = snd_soc_dapm_new_control(dapm, &template);
else
widget = snd_soc_dapm_new_control_unlocked(dapm, &template);
@@ -1483,7 +1451,8 @@ widget:
widget->dobj.type = SND_SOC_DOBJ_WIDGET;
widget->dobj.widget.kcontrol_type = kcontrol_type;
- widget->dobj.ops = tplg->ops;
+ if (tplg->ops)
+ widget->dobj.unload = tplg->ops->widget_unload;
widget->dobj.index = tplg->index;
list_add(&widget->dobj.list, &tplg->comp->dobj_list);
@@ -1497,7 +1466,7 @@ widget:
return 0;
ready_err:
- remove_widget(widget->dapm->component, &widget->dobj, SOC_TPLG_PASS_WIDGET);
+ soc_tplg_remove_widget(widget->dapm->component, &widget->dobj, SOC_TPLG_PASS_WIDGET);
snd_soc_dapm_free_widget(widget);
hdr_err:
kfree(template.sname);
@@ -1560,7 +1529,7 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg)
/* Card might not have been registered at this point.
* If so, just return success.
*/
- if (!card || !card->instantiated) {
+ if (!snd_soc_card_is_instantiated(card)) {
dev_warn(tplg->dev, "ASoC: Parent card not yet available,"
" widget card binding deferred\n");
return 0;
@@ -1571,7 +1540,7 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg)
dev_err(tplg->dev, "ASoC: failed to create new widgets %d\n",
ret);
- return 0;
+ return ret;
}
static int set_stream_info(struct soc_tplg *tplg, struct snd_soc_pcm_stream *stream,
@@ -1661,8 +1630,9 @@ static int soc_tplg_dai_create(struct soc_tplg *tplg,
}
dai_drv->dobj.index = tplg->index;
- dai_drv->dobj.ops = tplg->ops;
dai_drv->dobj.type = SND_SOC_DOBJ_PCM;
+ if (tplg->ops)
+ dai_drv->dobj.unload = tplg->ops->dai_unload;
list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list);
/* register the DAI to the component */
@@ -1731,8 +1701,9 @@ static int soc_tplg_fe_link_create(struct soc_tplg *tplg,
link->num_platforms = 1;
link->dobj.index = tplg->index;
- link->dobj.ops = tplg->ops;
link->dobj.type = SND_SOC_DOBJ_DAI_LINK;
+ if (tplg->ops)
+ link->dobj.unload = tplg->ops->link_unload;
if (strlen(pcm->pcm_name)) {
link->name = devm_kstrdup(tplg->dev, pcm->pcm_name, GFP_KERNEL);
@@ -2139,8 +2110,9 @@ static int soc_tplg_link_config(struct soc_tplg *tplg,
/* for unloading it in snd_soc_tplg_component_remove */
link->dobj.index = tplg->index;
- link->dobj.ops = tplg->ops;
link->dobj.type = SND_SOC_DOBJ_BACKEND_LINK;
+ if (tplg->ops)
+ link->dobj.unload = tplg->ops->link_unload;
list_add(&link->dobj.list, &tplg->comp->dobj_list);
return 0;
@@ -2394,12 +2366,9 @@ static int soc_tplg_manifest_load(struct soc_tplg *tplg,
}
/* validate header magic, size and type */
-static int soc_valid_header(struct soc_tplg *tplg,
+static int soc_tplg_valid_header(struct soc_tplg *tplg,
struct snd_soc_tplg_hdr *hdr)
{
- if (soc_tplg_get_hdr_offset(tplg) >= tplg->fw->size)
- return 0;
-
if (le32_to_cpu(hdr->size) != sizeof(*hdr)) {
dev_err(tplg->dev,
"ASoC: invalid header size for type %d at offset 0x%lx size 0x%zx.\n",
@@ -2408,7 +2377,7 @@ static int soc_valid_header(struct soc_tplg *tplg,
return -EINVAL;
}
- if (soc_tplg_get_hdr_offset(tplg) + hdr->payload_size >= tplg->fw->size) {
+ if (soc_tplg_get_hdr_offset(tplg) + le32_to_cpu(hdr->payload_size) >= tplg->fw->size) {
dev_err(tplg->dev,
"ASoC: invalid header of type %d at offset %ld payload_size %d\n",
le32_to_cpu(hdr->type), soc_tplg_get_hdr_offset(tplg),
@@ -2450,7 +2419,7 @@ static int soc_valid_header(struct soc_tplg *tplg,
return -EINVAL;
}
- return 1;
+ return 0;
}
/* check header type and call appropriate handler */
@@ -2531,13 +2500,11 @@ static int soc_tplg_process_headers(struct soc_tplg *tplg)
while (!soc_tplg_is_eof(tplg)) {
/* make sure header is valid before loading */
- ret = soc_valid_header(tplg, hdr);
+ ret = soc_tplg_valid_header(tplg, hdr);
if (ret < 0) {
dev_err(tplg->dev,
"ASoC: topology: invalid header: %d\n", ret);
return ret;
- } else if (ret == 0) {
- break;
}
/* load the header object */
@@ -2631,26 +2598,22 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp)
list) {
switch (dobj->type) {
- case SND_SOC_DOBJ_MIXER:
- remove_mixer(comp, dobj, pass);
- break;
- case SND_SOC_DOBJ_ENUM:
- remove_enum(comp, dobj, pass);
- break;
case SND_SOC_DOBJ_BYTES:
- remove_bytes(comp, dobj, pass);
+ case SND_SOC_DOBJ_ENUM:
+ case SND_SOC_DOBJ_MIXER:
+ soc_tplg_remove_kcontrol(comp, dobj, pass);
break;
case SND_SOC_DOBJ_GRAPH:
- remove_route(comp, dobj, pass);
+ soc_tplg_remove_route(comp, dobj, pass);
break;
case SND_SOC_DOBJ_WIDGET:
- remove_widget(comp, dobj, pass);
+ soc_tplg_remove_widget(comp, dobj, pass);
break;
case SND_SOC_DOBJ_PCM:
- remove_dai(comp, dobj, pass);
+ soc_tplg_remove_dai(comp, dobj, pass);
break;
case SND_SOC_DOBJ_DAI_LINK:
- remove_link(comp, dobj, pass);
+ soc_tplg_remove_link(comp, dobj, pass);
break;
case SND_SOC_DOBJ_BACKEND_LINK:
/*
diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h
index de5726251dc610..920155dee819b5 100644
--- a/sound/soc/sof/amd/acp-dsp-offset.h
+++ b/sound/soc/sof/amd/acp-dsp-offset.h
@@ -85,4 +85,8 @@
#define ACP_SCRATCH_REG_0 0x10000
#define ACP6X_DSP_FUSION_RUNSTALL 0x0644
+
+/* Cache window registers */
+#define ACP_DSP0_CACHE_OFFSET0 0x0420
+#define ACP_DSP0_CACHE_SIZE0 0x0424
#endif
diff --git a/sound/soc/sof/amd/acp-ipc.c b/sound/soc/sof/amd/acp-ipc.c
index 5a02753c46104d..4e0c48a361599f 100644
--- a/sound/soc/sof/amd/acp-ipc.c
+++ b/sound/soc/sof/amd/acp-ipc.c
@@ -200,14 +200,15 @@ irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context)
}
EXPORT_SYMBOL_NS(acp_sof_ipc_irq_thread, SND_SOC_SOF_AMD_COMMON);
-int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
+int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps,
void *p, size_t sz)
{
unsigned int offset = sdev->dsp_box.offset;
- if (!substream || !sdev->stream_box.size) {
+ if (!sps || !sdev->stream_box.size) {
acp_mailbox_read(sdev, offset, p, sz);
} else {
+ struct snd_pcm_substream *substream = sps->substream;
struct acp_dsp_stream *stream = substream->runtime->private_data;
if (!stream)
@@ -221,9 +222,10 @@ int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *sub
EXPORT_SYMBOL_NS(acp_sof_ipc_msg_data, SND_SOC_SOF_AMD_COMMON);
int acp_set_stream_data_offset(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset)
{
+ struct snd_pcm_substream *substream = sps->substream;
struct acp_dsp_stream *stream = substream->runtime->private_data;
/* check for unaligned offset or overflow */
diff --git a/sound/soc/sof/amd/acp-loader.c b/sound/soc/sof/amd/acp-loader.c
index 090c8b18c83c44..a4bce5a3ae482b 100644
--- a/sound/soc/sof/amd/acp-loader.c
+++ b/sound/soc/sof/amd/acp-loader.c
@@ -151,6 +151,7 @@ static void configure_pte_for_fw_loading(int type, int num_pages, struct acp_dev
int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev)
{
struct pci_dev *pci = to_pci_dev(sdev->dev);
+ const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
struct acp_dev_data *adata;
unsigned int src_addr, size_fw;
u32 page_count, dma_size;
@@ -183,6 +184,12 @@ int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev)
if (ret < 0)
dev_err(sdev->dev, "acp dma transfer status: %d\n", ret);
+ if (desc->rev > 3) {
+ /* Cache Window enable */
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_CACHE_OFFSET0, desc->sram_pte_offset);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_DSP0_CACHE_SIZE0, SRAM1_SIZE | BIT(31));
+ }
+
/* Free memory once DMA is complete */
dma_size = (PAGE_ALIGN(sdev->basefw.fw->size) >> PAGE_SHIFT) * ACP_PAGE_SIZE;
dma_free_coherent(&pci->dev, dma_size, adata->bin_buf, adata->sha_dma_addr);
diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h
index 09e16ef8afa050..39165ebf684b68 100644
--- a/sound/soc/sof/amd/acp.h
+++ b/sound/soc/sof/amd/acp.h
@@ -12,6 +12,7 @@
#define __SOF_AMD_ACP_H
#include "../sof-priv.h"
+#include "../sof-audio.h"
#define ACP_MAX_STREAM 8
@@ -72,6 +73,8 @@
#define EXCEPT_MAX_HDR_SIZE 0x400
#define AMD_STACK_DUMP_SIZE 32
+#define SRAM1_SIZE 0x13A000
+
enum clock_source {
ACP_CLOCK_96M = 0,
ACP_CLOCK_48M,
@@ -209,10 +212,10 @@ int acp_dsp_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_ty
/* IPC callbacks */
irqreturn_t acp_sof_ipc_irq_thread(int irq, void *context);
-int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
+int acp_sof_ipc_msg_data(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *sps,
void *p, size_t sz);
int acp_set_stream_data_offset(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset);
int acp_sof_ipc_send_msg(struct snd_sof_dev *sdev,
struct snd_sof_ipc_msg *msg);
diff --git a/sound/soc/sof/compress.c b/sound/soc/sof/compress.c
index 8e1a9ba111ad59..8d205eb16d2f31 100644
--- a/sound/soc/sof/compress.c
+++ b/sound/soc/sof/compress.c
@@ -10,6 +10,7 @@
#include "sof-audio.h"
#include "sof-priv.h"
#include "sof-utils.h"
+#include "ops.h"
static void sof_set_transferred_bytes(struct sof_compr_stream *sstream,
u64 host_pos, u64 buffer_size)
@@ -237,6 +238,14 @@ static int sof_compr_set_params(struct snd_soc_component *component,
goto out;
}
+ ret = snd_sof_set_stream_data_offset(sdev, &spcm->stream[cstream->direction],
+ ipc_params_reply.posn_offset);
+ if (ret < 0) {
+ dev_err(component->dev, "Invalid stream data offset for Compr %d\n",
+ spcm->pcm.pcm_id);
+ goto out;
+ }
+
sstream->sampling_rate = params->codec.sample_rate;
sstream->channels = params->codec.ch_out;
sstream->sample_container_bytes = pcm->params.sample_container_bytes;
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c
index e0e9efd25d34e2..75e13f4fd1eb81 100644
--- a/sound/soc/sof/control.c
+++ b/sound/soc/sof/control.c
@@ -22,9 +22,9 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = sm->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->volume_get)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->volume_get)
return tplg_ops->control->volume_get(scontrol, ucontrol);
return 0;
@@ -37,9 +37,9 @@ int snd_sof_volume_put(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = sm->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->volume_put)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->volume_put)
return tplg_ops->control->volume_put(scontrol, ucontrol);
return false;
@@ -74,9 +74,9 @@ int snd_sof_switch_get(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = sm->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->switch_get)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->switch_get)
return tplg_ops->control->switch_get(scontrol, ucontrol);
return 0;
@@ -89,9 +89,9 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = sm->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->switch_put)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->switch_put)
return tplg_ops->control->switch_put(scontrol, ucontrol);
return false;
@@ -104,9 +104,9 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = se->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->enum_get)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->enum_get)
return tplg_ops->control->enum_get(scontrol, ucontrol);
return 0;
@@ -119,9 +119,9 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = se->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->enum_put)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->enum_put)
return tplg_ops->control->enum_put(scontrol, ucontrol);
return false;
@@ -134,9 +134,9 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = be->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->bytes_get)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_get)
return tplg_ops->control->bytes_get(scontrol, ucontrol);
return 0;
@@ -149,9 +149,9 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = be->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->bytes_put)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_put)
return tplg_ops->control->bytes_put(scontrol, ucontrol);
return 0;
@@ -165,13 +165,13 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = be->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
/* make sure we have at least a header */
if (size < sizeof(struct snd_ctl_tlv))
return -EINVAL;
- if (tplg_ops->control->bytes_ext_put)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_put)
return tplg_ops->control->bytes_ext_put(scontrol, binary_data, size);
return 0;
@@ -184,7 +184,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _
struct snd_sof_control *scontrol = be->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
int ret, err;
ret = pm_runtime_resume_and_get(scomp->dev);
@@ -193,7 +193,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _
return ret;
}
- if (tplg_ops->control->bytes_ext_volatile_get)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_volatile_get)
ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, binary_data, size);
pm_runtime_mark_last_busy(scomp->dev);
@@ -212,9 +212,9 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
struct snd_sof_control *scontrol = be->dobj.private;
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->bytes_ext_get)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_get)
return tplg_ops->control->bytes_ext_get(scontrol, binary_data, size);
return 0;
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index 625977a29d8a8c..7de8673a01ce10 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -362,6 +362,9 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
sdev->first_boot = true;
dev_set_drvdata(dev, sdev);
+ if (sof_core_debug)
+ dev_info(dev, "sof_debug value: %#x\n", sof_core_debug);
+
/* check IPC support */
if (!(BIT(plat_data->ipc_type) & plat_data->desc->ipc_supported_mask)) {
dev_err(dev, "ipc_type %d is not supported on this platform, mask is %#x\n",
@@ -387,6 +390,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
INIT_LIST_HEAD(&sdev->pcm_list);
INIT_LIST_HEAD(&sdev->kcontrol_list);
INIT_LIST_HEAD(&sdev->widget_list);
+ INIT_LIST_HEAD(&sdev->pipeline_list);
INIT_LIST_HEAD(&sdev->dai_list);
INIT_LIST_HEAD(&sdev->dai_link_list);
INIT_LIST_HEAD(&sdev->route_list);
diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig
index 36a0e2bf30ff41..715ba8a7f2f8da 100644
--- a/sound/soc/sof/intel/Kconfig
+++ b/sound/soc/sof/intel/Kconfig
@@ -322,8 +322,8 @@ config SND_SOC_SOF_HDA_PROBES
config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE
tristate
- select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE
- select SND_INTEL_SOUNDWIRE_ACPI if SND_SOC_SOF_INTEL_SOUNDWIRE
+ select SOUNDWIRE_INTEL if SND_SOC_SOF_INTEL_SOUNDWIRE != n
+ select SND_INTEL_SOUNDWIRE_ACPI if SND_SOC_SOF_INTEL_SOUNDWIRE != n
config SND_SOC_SOF_INTEL_SOUNDWIRE
tristate "SOF support for SoundWire"
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index 6b075bbe5bfb83..a08a77fa946b6c 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -280,6 +280,8 @@ int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
snd_sof_dsp_write(sdev, HDA_DSP_BAR, CNL_DSP_REG_HIPCIDR,
msg_data->primary | CNL_DSP_REG_HIPCIDR_BUSY);
+ hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);
+
return 0;
}
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index a642c3067ec5c8..8d9c38d562d3dc 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -450,6 +450,8 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
{
struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(dai, substream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
+ struct snd_sof_widget *pipe_widget;
+ struct sof_ipc4_pipeline *pipeline;
struct snd_soc_pcm_runtime *rtd;
struct snd_sof_widget *swidget;
struct snd_soc_dapm_widget *w;
@@ -466,18 +468,30 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
w = snd_soc_dai_get_widget(dai, substream->stream);
swidget = w->dobj.private;
+ pipe_widget = swidget->spipe->pipe_widget;
+ pipeline = pipe_widget->private;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
snd_hdac_ext_stream_start(hext_stream);
+ if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_PAUSED);
+ if (ret < 0)
+ return ret;
+ pipeline->state = SOF_IPC4_PIPE_PAUSED;
+ }
+
+ ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
+ SOF_IPC4_PIPE_RUNNING);
+ if (ret < 0)
+ return ret;
+ pipeline->state = SOF_IPC4_PIPE_RUNNING;
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
{
- struct snd_sof_widget *pipe_widget = swidget->pipe_widget;
- struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
-
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_PAUSED);
if (ret < 0)
@@ -503,9 +517,6 @@ static int ipc4_hda_dai_trigger(struct snd_pcm_substream *substream,
}
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
{
- struct snd_sof_widget *pipe_widget = swidget->pipe_widget;
- struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
-
ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
SOF_IPC4_PIPE_PAUSED);
if (ret < 0)
@@ -703,64 +714,6 @@ static const struct snd_soc_dai_ops ipc3_ssp_dai_ops = {
.shutdown = ssp_dai_shutdown,
};
-static int ipc4_be_dai_common_trigger(struct snd_soc_dai *dai, int cmd, int stream)
-{
- struct snd_sof_widget *pipe_widget;
- struct sof_ipc4_pipeline *pipeline;
- struct snd_sof_widget *swidget;
- struct snd_soc_dapm_widget *w;
- struct snd_sof_dev *sdev;
- int ret;
-
- w = snd_soc_dai_get_widget(dai, stream);
- swidget = w->dobj.private;
- pipe_widget = swidget->pipe_widget;
- pipeline = pipe_widget->private;
- sdev = snd_soc_component_get_drvdata(swidget->scomp);
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_STOP:
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_PAUSED);
- if (ret < 0)
- return ret;
- pipeline->state = SOF_IPC4_PIPE_PAUSED;
-
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_RESET);
- if (ret < 0)
- return ret;
- pipeline->state = SOF_IPC4_PIPE_RESET;
- break;
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
- SOF_IPC4_PIPE_PAUSED);
- if (ret < 0)
- return ret;
- pipeline->state = SOF_IPC4_PIPE_PAUSED;
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int ipc4_be_dai_trigger(struct snd_pcm_substream *substream,
- int cmd, struct snd_soc_dai *dai)
-{
- return ipc4_be_dai_common_trigger(dai, cmd, substream->stream);
-}
-
-static const struct snd_soc_dai_ops ipc4_dmic_dai_ops = {
- .trigger = ipc4_be_dai_trigger,
-};
-
-static const struct snd_soc_dai_ops ipc4_ssp_dai_ops = {
- .trigger = ipc4_be_dai_trigger,
-};
-
void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
{
int i;
@@ -785,14 +738,6 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
for (i = 0; i < ops->num_drv; i++) {
- if (strstr(ops->drv[i].name, "DMIC")) {
- ops->drv[i].ops = &ipc4_dmic_dai_ops;
- continue;
- }
- if (strstr(ops->drv[i].name, "SSP")) {
- ops->drv[i].ops = &ipc4_ssp_dai_ops;
- continue;
- }
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
if (strstr(ops->drv[i].name, "iDisp") ||
strstr(ops->drv[i].name, "Analog") ||
@@ -804,9 +749,6 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
if (!hda_use_tplg_nhlt)
ipc4_data->nhlt = intel_nhlt_init(sdev->dev);
- if (IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE))
- sdw_callback.trigger = ipc4_be_dai_common_trigger;
-
break;
}
default:
diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c
index b4eacae8564c84..68eb06f13a1fd2 100644
--- a/sound/soc/sof/intel/hda-dsp.c
+++ b/sound/soc/sof/intel/hda-dsp.c
@@ -364,19 +364,12 @@ static int hda_dsp_wait_d0i3c_done(struct snd_sof_dev *sdev)
static int hda_dsp_send_pm_gate_ipc(struct snd_sof_dev *sdev, u32 flags)
{
- struct sof_ipc_pm_gate pm_gate;
- struct sof_ipc_reply reply;
+ const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
- memset(&pm_gate, 0, sizeof(pm_gate));
+ if (pm_ops && pm_ops->set_pm_gate)
+ return pm_ops->set_pm_gate(sdev, flags);
- /* configure pm_gate ipc message */
- pm_gate.hdr.size = sizeof(pm_gate);
- pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE;
- pm_gate.flags = flags;
-
- /* send pm_gate ipc to dsp */
- return sof_ipc_tx_message_no_pm(sdev->ipc, &pm_gate, sizeof(pm_gate),
- &reply, sizeof(reply));
+ return 0;
}
static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value)
@@ -412,6 +405,34 @@ static int hda_dsp_update_d0i3c_register(struct snd_sof_dev *sdev, u8 value)
return 0;
}
+/*
+ * d0i3 streaming is enabled if all the active streams can
+ * work in d0i3 state and playback is enabled
+ */
+static bool hda_dsp_d0i3_streaming_applicable(struct snd_sof_dev *sdev)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_sof_pcm *spcm;
+ bool playback_active = false;
+ int dir;
+
+ list_for_each_entry(spcm, &sdev->pcm_list, list) {
+ for_each_pcm_streams(dir) {
+ substream = spcm->stream[dir].substream;
+ if (!substream || !substream->runtime)
+ continue;
+
+ if (!spcm->stream[dir].d0i3_compatible)
+ return false;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ playback_active = true;
+ }
+ }
+
+ return playback_active;
+}
+
static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev,
const struct sof_dsp_power_state *target_state)
{
@@ -453,6 +474,9 @@ static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev,
!hda_enable_trace_D0I3_S0 ||
sdev->system_suspend_target != SOF_SUSPEND_NONE)
flags = HDA_PM_NO_DMA_TRACE;
+
+ if (hda_dsp_d0i3_streaming_applicable(sdev))
+ flags |= HDA_PM_PG_STREAMING;
} else {
/* prevent power gating in D0I0 */
flags = HDA_PM_PPG;
diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c
index a7c454e0395286..df541b22b2d2b8 100644
--- a/sound/soc/sof/intel/hda-ipc.c
+++ b/sound/soc/sof/intel/hda-ipc.c
@@ -67,6 +67,32 @@ int hda_dsp_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
return 0;
}
+static inline bool hda_dsp_ipc4_pm_msg(u32 primary)
+{
+ /* pm setting is only supported by module msg */
+ if (SOF_IPC4_MSG_IS_MODULE_MSG(primary) != SOF_IPC4_MODULE_MSG)
+ return false;
+
+ if (SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_DX ||
+ SOF_IPC4_MSG_TYPE_GET(primary) == SOF_IPC4_MOD_SET_D0IX)
+ return true;
+
+ return false;
+}
+
+void hda_dsp_ipc4_schedule_d0i3_work(struct sof_intel_hda_dev *hdev,
+ struct snd_sof_ipc_msg *msg)
+{
+ struct sof_ipc4_msg *msg_data = msg->msg_data;
+
+ /* Schedule a delayed work for d0i3 entry after sending non-pm ipc msg */
+ if (hda_dsp_ipc4_pm_msg(msg_data->primary))
+ return;
+
+ mod_delayed_work(system_wq, &hdev->d0i3_work,
+ msecs_to_jiffies(SOF_HDA_D0I3_WORK_DELAY_MS));
+}
+
int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
{
struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata;
@@ -88,6 +114,8 @@ int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
snd_sof_dsp_write(sdev, HDA_DSP_BAR, HDA_DSP_REG_HIPCI,
msg_data->primary | HDA_DSP_REG_HIPCI_BUSY);
+ hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);
+
return 0;
}
@@ -361,12 +389,13 @@ int hda_dsp_ipc_get_window_offset(struct snd_sof_dev *sdev, u32 id)
}
int hda_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
void *p, size_t sz)
{
- if (!substream || !sdev->stream_box.size) {
+ if (!sps || !sdev->stream_box.size) {
sof_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
} else {
+ struct snd_pcm_substream *substream = sps->substream;
struct hdac_stream *hstream = substream->runtime->private_data;
struct sof_intel_hda_stream *hda_stream;
@@ -385,9 +414,10 @@ int hda_ipc_msg_data(struct snd_sof_dev *sdev,
}
int hda_set_stream_data_offset(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset)
{
+ struct snd_pcm_substream *substream = sps->substream;
struct hdac_stream *hstream = substream->runtime->private_data;
struct sof_intel_hda_stream *hda_stream;
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index caccaf8fba9c0a..45f9d4248f14e9 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -656,10 +656,10 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev,
int enable, u32 size);
int hda_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
void *p, size_t sz);
int hda_set_stream_data_offset(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset);
/*
@@ -919,6 +919,8 @@ irqreturn_t cnl_ipc4_irq_thread(int irq, void *context);
int cnl_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
irqreturn_t hda_dsp_ipc4_irq_thread(int irq, void *context);
bool hda_ipc4_tx_is_busy(struct snd_sof_dev *sdev);
+void hda_dsp_ipc4_schedule_d0i3_work(struct sof_intel_hda_dev *hdev,
+ struct snd_sof_ipc_msg *msg);
int hda_dsp_ipc4_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg);
void hda_ipc4_dump(struct snd_sof_dev *sdev);
extern struct sdw_intel_ops sdw_callback;
diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c
index 904ae42534e12c..307faad2ecf4b9 100644
--- a/sound/soc/sof/intel/mtl.c
+++ b/sound/soc/sof/intel/mtl.c
@@ -110,6 +110,8 @@ static int mtl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *ms
snd_sof_dsp_write(sdev, HDA_DSP_BAR, MTL_DSP_REG_HFIPCXIDR,
msg_data->primary | MTL_DSP_REG_HFIPCXIDR_BUSY);
+ hda_dsp_ipc4_schedule_d0i3_work(hdev, msg);
+
return 0;
}
@@ -581,6 +583,18 @@ static int mtl_dsp_disable_interrupts(struct snd_sof_dev *sdev)
return mtl_enable_interrupts(sdev, false);
}
+static u64 mtl_dsp_get_stream_hda_link_position(struct snd_sof_dev *sdev,
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct hdac_stream *hstream = substream->runtime->private_data;
+ u32 llp_l, llp_u;
+
+ llp_l = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, MTL_PPLCLLPL(hstream->index));
+ llp_u = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, MTL_PPLCLLPU(hstream->index));
+ return ((u64)llp_u << 32) | llp_l;
+}
+
/* Meteorlake ops */
struct snd_sof_dsp_ops sof_mtl_ops;
EXPORT_SYMBOL_NS(sof_mtl_ops, SND_SOC_SOF_INTEL_HDA_COMMON);
@@ -619,6 +633,8 @@ int sof_mtl_ops_init(struct snd_sof_dev *sdev)
/* dsp core get/put */
/* TODO: add core_get and core_put */
+ sof_mtl_ops.get_stream_position = mtl_dsp_get_stream_hda_link_position;
+
sdev->private = devm_kzalloc(sdev->dev, sizeof(struct sof_ipc4_fw_data), GFP_KERNEL);
if (!sdev->private)
return -ENOMEM;
diff --git a/sound/soc/sof/intel/mtl.h b/sound/soc/sof/intel/mtl.h
index 0fd4e6fe09b8b3..26418fb08807eb 100644
--- a/sound/soc/sof/intel/mtl.h
+++ b/sound/soc/sof/intel/mtl.h
@@ -6,6 +6,12 @@
* Copyright(c) 2020-2022 Intel Corporation. All rights reserved.
*/
+/* HDA Registers */
+#define MTL_PPLCLLPL_BASE 0x948
+#define MTL_PPLCLLPU_STRIDE 0x10
+#define MTL_PPLCLLPL(x) (MTL_PPLCLLPL_BASE + (x) * MTL_PPLCLLPU_STRIDE)
+#define MTL_PPLCLLPU(x) (MTL_PPLCLLPL_BASE + 0x4 + (x) * MTL_PPLCLLPU_STRIDE)
+
/* DSP Registers */
#define MTL_HFDSSCS 0x1000
#define MTL_HFDSSCS_SPA_MASK BIT(16)
diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c
index 3fdc0d854e65d1..217ac5501a9885 100644
--- a/sound/soc/sof/ipc3-control.c
+++ b/sound/soc/sof/ipc3-control.c
@@ -12,7 +12,8 @@
#include "ipc3-priv.h"
/* IPC set()/get() for kcontrols. */
-static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set)
+static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol,
+ bool set, bool lock)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp);
struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data;
@@ -21,6 +22,7 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
struct snd_sof_widget *swidget;
bool widget_found = false;
u32 ipc_cmd, msg_bytes;
+ int ret = 0;
list_for_each_entry(swidget, &sdev->widget_list, list) {
if (swidget->comp_id == scontrol->comp_id) {
@@ -35,13 +37,18 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
return -EINVAL;
}
+ if (lock)
+ mutex_lock(&swidget->setup_mutex);
+ else
+ lockdep_assert_held(&swidget->setup_mutex);
+
/*
- * Volatile controls should always be part of static pipelines and the widget use_count
- * would always be > 0 in this case. For the others, just return the cached value if the
- * widget is not set up.
+ * Volatile controls should always be part of static pipelines and the
+ * widget use_count would always be > 0 in this case. For the others,
+ * just return the cached value if the widget is not set up.
*/
if (!swidget->use_count)
- return 0;
+ goto unlock;
/*
* Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the
@@ -81,13 +88,20 @@ static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
sizeof(struct sof_abi_hdr);
break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ goto unlock;
}
cdata->rhdr.hdr.size = msg_bytes;
cdata->elems_remaining = 0;
- return iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);
+ ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set);
+
+unlock:
+ if (lock)
+ mutex_unlock(&swidget->setup_mutex);
+
+ return ret;
}
static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
@@ -108,7 +122,7 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol)
/* refresh the component data from DSP */
scontrol->comp_data_dirty = false;
- ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
+ ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true);
if (ret < 0) {
dev_err(scomp->dev, "Failed to get control data: %d\n", ret);
@@ -156,7 +170,7 @@ static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol,
/* notify DSP of mixer updates */
if (pm_runtime_active(scomp->dev)) {
- int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
+ int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
if (ret < 0) {
dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
@@ -204,7 +218,7 @@ static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol,
/* notify DSP of mixer updates */
if (pm_runtime_active(scomp->dev)) {
- int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
+ int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
if (ret < 0) {
dev_err(scomp->dev, "Failed to set mixer updates for %s\n",
@@ -252,7 +266,7 @@ static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol,
/* notify DSP of enum updates */
if (pm_runtime_active(scomp->dev)) {
- int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
+ int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
if (ret < 0) {
dev_err(scomp->dev, "Failed to set enum updates for %s\n",
@@ -324,7 +338,7 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol,
/* notify DSP of byte control updates */
if (pm_runtime_active(scomp->dev))
- return sof_ipc3_set_get_kcontrol_data(scontrol, true);
+ return sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
return 0;
}
@@ -438,7 +452,7 @@ static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol,
/* notify DSP of byte control updates */
if (pm_runtime_active(scomp->dev))
- return sof_ipc3_set_get_kcontrol_data(scontrol, true);
+ return sof_ipc3_set_get_kcontrol_data(scontrol, true, true);
return 0;
}
@@ -468,7 +482,7 @@ static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol,
cdata->data->abi = SOF_ABI_VERSION;
/* get all the component data from DSP */
- ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
+ ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true);
if (ret < 0)
return ret;
@@ -647,7 +661,7 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev,
list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
if (scontrol->comp_id == swidget->comp_id) {
/* set kcontrol data in DSP */
- ret = sof_ipc3_set_get_kcontrol_data(scontrol, true);
+ ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, false);
if (ret < 0) {
dev_err(sdev->dev,
"kcontrol %d set up failed for widget %s\n",
@@ -664,7 +678,7 @@ static int sof_ipc3_widget_kcontrol_setup(struct snd_sof_dev *sdev,
if (swidget->dynamic_pipeline_widget)
continue;
- ret = sof_ipc3_set_get_kcontrol_data(scontrol, false);
+ ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, false);
if (ret < 0)
dev_warn(sdev->dev,
"kcontrol %d read failed for widget %s\n",
diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c
index f10bfc9bd5cb41..b29d93e0d2164b 100644
--- a/sound/soc/sof/ipc3-pcm.c
+++ b/sound/soc/sof/ipc3-pcm.c
@@ -129,7 +129,8 @@ static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component,
return ret;
}
- ret = snd_sof_set_stream_data_offset(sdev, substream, ipc_params_reply.posn_offset);
+ ret = snd_sof_set_stream_data_offset(sdev, &spcm->stream[substream->stream],
+ ipc_params_reply.posn_offset);
if (ret < 0) {
dev_err(component->dev, "%s: invalid stream data offset for PCM %d\n",
__func__, spcm->pcm.pcm_id);
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index b94cc40485ed6d..dceb78bfe17c68 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -1651,6 +1651,7 @@ static int sof_ipc3_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol)
{
struct sof_ipc_ctrl_data *cdata;
+ size_t priv_size_check;
int ret;
if (scontrol->max_size < (sizeof(*cdata) + sizeof(struct sof_abi_hdr))) {
@@ -1694,8 +1695,10 @@ static int sof_ipc3_control_load_bytes(struct snd_sof_dev *sdev, struct snd_sof_
goto err;
}
- if (cdata->data->size + sizeof(struct sof_abi_hdr) != scontrol->priv_size) {
- dev_err(sdev->dev, "Conflict in bytes vs. priv size.\n");
+ priv_size_check = cdata->data->size + sizeof(struct sof_abi_hdr);
+ if (priv_size_check != scontrol->priv_size) {
+ dev_err(sdev->dev, "Conflict in bytes (%zu) vs. priv size (%zu).\n",
+ priv_size_check, scontrol->priv_size);
ret = -EINVAL;
goto err;
}
@@ -2230,9 +2233,9 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify)
return ret;
}
- swidget->complete = sof_ipc3_complete_pipeline(sdev, swidget);
- if (swidget->complete < 0)
- return swidget->complete;
+ swidget->spipe->complete = sof_ipc3_complete_pipeline(sdev, swidget);
+ if (swidget->spipe->complete < 0)
+ return swidget->spipe->complete;
break;
default:
break;
@@ -2261,7 +2264,7 @@ static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev)
for_each_pcm_streams(dir) {
struct snd_pcm_substream *substream = spcm->stream[dir].substream;
- if (!substream || !substream->runtime)
+ if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)
continue;
if (spcm->stream[dir].list) {
@@ -2313,8 +2316,11 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
/* Do not free widgets for static pipelines with FW older than SOF2.2 */
if (!verify && !swidget->dynamic_pipeline_widget &&
SOF_FW_VER(v->major, v->minor, v->micro) < SOF_FW_VER(2, 2, 0)) {
+ mutex_lock(&swidget->setup_mutex);
swidget->use_count = 0;
- swidget->complete = 0;
+ mutex_unlock(&swidget->setup_mutex);
+ if (swidget->spipe)
+ swidget->spipe->complete = 0;
continue;
}
@@ -2423,6 +2429,24 @@ static int sof_ipc3_parse_manifest(struct snd_soc_component *scomp, int index,
return 0;
}
+static int sof_ipc3_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link)
+{
+ if (link->no_pcm)
+ return 0;
+
+ /*
+ * set default trigger order for all links. Exceptions to
+ * the rule will be handled in sof_pcm_dai_link_fixup()
+ * For playback, the sequence is the following: start FE,
+ * start BE, stop BE, stop FE; for Capture the sequence is
+ * inverted start BE, start FE, stop FE, stop BE
+ */
+ link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_PRE;
+ link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_POST;
+
+ return 0;
+}
+
/* token list for each topology object */
static enum sof_tokens host_token_list[] = {
SOF_CORE_TOKENS,
@@ -2534,4 +2558,5 @@ const struct sof_ipc_tplg_ops ipc3_tplg_ops = {
.set_up_all_pipelines = sof_ipc3_set_up_all_pipelines,
.tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines,
.parse_manifest = sof_ipc3_parse_manifest,
+ .link_setup = sof_ipc3_link_setup,
};
diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c
index 1fef4dcc093683..3de64ea2dc9aa5 100644
--- a/sound/soc/sof/ipc3.c
+++ b/sound/soc/sof/ipc3.c
@@ -847,7 +847,7 @@ static void ipc3_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id)
}
stream = &spcm->stream[direction];
- ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+ ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn));
if (ret < 0) {
dev_warn(sdev->dev, "failed to read stream position: %d\n", ret);
return;
@@ -882,7 +882,7 @@ static void ipc3_xrun(struct snd_sof_dev *sdev, u32 msg_id)
}
stream = &spcm->stream[direction];
- ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+ ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn));
if (ret < 0) {
dev_warn(sdev->dev, "failed to read overrun position: %d\n", ret);
return;
@@ -1077,10 +1077,28 @@ static int sof_ipc3_ctx_restore(struct snd_sof_dev *sdev)
return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE);
}
+static int sof_ipc3_set_pm_gate(struct snd_sof_dev *sdev, u32 flags)
+{
+ struct sof_ipc_pm_gate pm_gate;
+ struct sof_ipc_reply reply;
+
+ memset(&pm_gate, 0, sizeof(pm_gate));
+
+ /* configure pm_gate ipc message */
+ pm_gate.hdr.size = sizeof(pm_gate);
+ pm_gate.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_GATE;
+ pm_gate.flags = flags;
+
+ /* send pm_gate ipc to dsp */
+ return sof_ipc_tx_message_no_pm(sdev->ipc, &pm_gate, sizeof(pm_gate),
+ &reply, sizeof(reply));
+}
+
static const struct sof_ipc_pm_ops ipc3_pm_ops = {
.ctx_save = sof_ipc3_ctx_save,
.ctx_restore = sof_ipc3_ctx_restore,
.set_core_state = sof_ipc3_set_core_state,
+ .set_pm_gate = sof_ipc3_set_pm_gate,
};
const struct sof_ipc_ops ipc3_ops = {
diff --git a/sound/soc/sof/ipc4-control.c b/sound/soc/sof/ipc4-control.c
index 0d5a578c34962a..67bd2233fd9a67 100644
--- a/sound/soc/sof/ipc4-control.c
+++ b/sound/soc/sof/ipc4-control.c
@@ -12,7 +12,8 @@
#include "ipc4-priv.h"
#include "ipc4-topology.h"
-static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set)
+static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol,
+ bool set, bool lock)
{
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct snd_soc_component *scomp = scontrol->scomp;
@@ -21,6 +22,7 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
struct sof_ipc4_msg *msg = &cdata->msg;
struct snd_sof_widget *swidget;
bool widget_found = false;
+ int ret = 0;
/* find widget associated with the control */
list_for_each_entry(swidget, &sdev->widget_list, list) {
@@ -35,23 +37,34 @@ static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool
return -ENOENT;
}
+ if (lock)
+ mutex_lock(&swidget->setup_mutex);
+ else
+ lockdep_assert_held(&swidget->setup_mutex);
+
/*
- * Volatile controls should always be part of static pipelines and the widget use_count
- * would always be > 0 in this case. For the others, just return the cached value if the
- * widget is not set up.
+ * Volatile controls should always be part of static pipelines and the
+ * widget use_count would always be > 0 in this case. For the others,
+ * just return the cached value if the widget is not set up.
*/
if (!swidget->use_count)
- return 0;
+ goto unlock;
msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK;
msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id);
- return iops->set_get_data(sdev, msg, msg->data_size, set);
+ ret = iops->set_get_data(sdev, msg, msg->data_size, set);
+
+unlock:
+ if (lock)
+ mutex_unlock(&swidget->setup_mutex);
+
+ return ret;
}
static int
sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
- struct snd_sof_control *scontrol)
+ struct snd_sof_control *scontrol, bool lock)
{
struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data;
struct sof_ipc4_gain *gain = swidget->private;
@@ -90,7 +103,7 @@ sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidge
msg->data_ptr = &data;
msg->data_size = sizeof(data);
- ret = sof_ipc4_set_get_kcontrol_data(scontrol, true);
+ ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock);
msg->data_ptr = NULL;
msg->data_size = 0;
if (ret < 0) {
@@ -145,7 +158,7 @@ static bool sof_ipc4_volume_put(struct snd_sof_control *scontrol,
return false;
}
- ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol);
+ ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, true);
if (ret < 0)
return false;
@@ -175,7 +188,7 @@ static int sof_ipc4_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_s
list_for_each_entry(scontrol, &sdev->kcontrol_list, list)
if (scontrol->comp_id == swidget->comp_id) {
- ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol);
+ ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, false);
if (ret < 0) {
dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n",
__func__, scontrol->comp_id, swidget->widget->name);
diff --git a/sound/soc/sof/ipc4-fw-reg.h b/sound/soc/sof/ipc4-fw-reg.h
new file mode 100644
index 00000000000000..7226161e57e17e
--- /dev/null
+++ b/sound/soc/sof/ipc4-fw-reg.h
@@ -0,0 +1,155 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
+/*
+ * This file is provided under a dual BSD/GPLv2 license. When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * Copyright(c) 2022 Intel Corporation. All rights reserved.
+ */
+
+#ifndef __IPC4_FW_REG_H__
+#define __IPC4_FW_REG_H__
+
+#define SOF_IPC4_INVALID_STREAM_POSITION ULLONG_MAX
+
+/**
+ * struct sof_ipc4_pipeline_registers - Pipeline start and end information in fw
+ * @stream_start_offset: Stream start offset (LPIB) reported by mixin
+ * module allocated on pipeline attached to Host Output Gateway when
+ * first data is being mixed to mixout module. When data is not mixed
+ * (right after creation/after reset) value "(u64)-1" is reported
+ * @stream_end_offset: Stream end offset (LPIB) reported by mixin
+ * module allocated on pipeline attached to Host Output Gateway
+ * during transition from RUNNING to PAUSED. When data is not mixed
+ * (right after creation or after reset) value "(u64)-1" is reported.
+ * When first data is mixed then value "0"is reported.
+ */
+struct sof_ipc4_pipeline_registers {
+ u64 stream_start_offset;
+ u64 stream_end_offset;
+} __packed __aligned(4);
+
+#define SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS 8
+
+/**
+ * struct sof_ipc4_peak_volume_regs - Volume information in fw
+ * @peak_meter: Peak volume value in fw
+ * @current_volume: Current volume value in fw
+ * @target_volume: Target volume value in fw
+ */
+struct sof_ipc4_peak_volume_regs {
+ u32 peak_meter[SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS];
+ u32 current_volume[SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS];
+ u32 target_volume[SOF_IPC4_PV_MAX_SUPPORTED_CHANNELS];
+} __packed __aligned(4);
+
+/**
+ * struct sof_ipc4_llp_reading - Llp information in fw
+ * @llp_l: Lower part of 64-bit LLP
+ * @llp_u: Upper part of 64-bit LLP
+ * @wclk_l: Lower part of 64-bit Wallclock
+ * @wclk_u: Upper part of 64-bit Wallclock
+ */
+struct sof_ipc4_llp_reading {
+ u32 llp_l;
+ u32 llp_u;
+ u32 wclk_l;
+ u32 wclk_u;
+} __packed __aligned(4);
+
+/**
+ * struct of sof_ipc4_llp_reading_extended - Extended llp info
+ * @llp_reading: Llp information in memory window
+ * @tpd_low: Total processed data (low part)
+ * @tpd_high: Total processed data (high part)
+ */
+struct sof_ipc4_llp_reading_extended {
+ struct sof_ipc4_llp_reading llp_reading;
+ u32 tpd_low;
+ u32 tpd_high;
+} __packed __aligned(4);
+
+/**
+ * struct sof_ipc4_llp_reading_slot - Llp slot information in memory window
+ * @node_id: Dai gateway node id
+ * @reading: Llp information in memory window
+ */
+struct sof_ipc4_llp_reading_slot {
+ u32 node_id;
+ struct sof_ipc4_llp_reading reading;
+} __packed __aligned(4);
+
+/* ROM information */
+#define SOF_IPC4_FW_FUSE_VALUE_MASK GENMASK(7, 0)
+#define SOF_IPC4_FW_LOAD_METHOD_MASK BIT(8)
+#define SOF_IPC4_FW_DOWNLINK_IPC_USE_DMA_MASK BIT(9)
+#define SOF_IPC4_FW_LOAD_METHOD_REV_MASK GENMASK(11, 10)
+#define SOF_IPC4_FW_REVISION_MIN_MASK GENMASK(15, 12)
+#define SOF_IPC4_FW_REVISION_MAJ_MASK GENMASK(19, 16)
+#define SOF_IPC4_FW_VERSION_MIN_MASK GENMASK(23, 20)
+#define SOF_IPC4_FW_VERSION_MAJ_MASK GENMASK(27, 24)
+
+/* Number of dsp core supported in FW Regs. */
+#define SOF_IPC4_MAX_SUPPORTED_ADSP_CORES 8
+
+/* Number of host pipeline registers slots in FW Regs. */
+#define SOF_IPC4_MAX_PIPELINE_REG_SLOTS 16
+
+/* Number of PeakVol registers slots in FW Regs. */
+#define SOF_IPC4_MAX_PEAK_VOL_REG_SLOTS 16
+
+/* Number of GPDMA LLP Reading slots in FW Regs. */
+#define SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS 24
+
+/* Number of Aggregated SNDW Reading slots in FW Regs. */
+#define SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS 15
+
+/* Current ABI version of the Fw registers layout. */
+#define SOF_IPC4_FW_REGS_ABI_VER 1
+
+/**
+ * struct sof_ipc4_fw_registers - FW Registers exposes additional
+ * DSP / FW state information to the driver
+ * @fw_status: Current ROM / FW status
+ * @lec: Last ROM / FW error code
+ * @fps: Current DSP clock status
+ * @lnec: Last Native Error Code(from external library)
+ * @ltr: Copy of LTRC HW register value(FW only)
+ * @rsvd0: Reserved0
+ * @rom_info: ROM info
+ * @abi_ver: Version of the layout, set to the current FW_REGS_ABI_VER
+ * @slave_core_sts: Slave core states
+ * @rsvd2: Reserved2
+ * @pipeline_regs: State of pipelines attached to host output gateways
+ * @peak_vol_regs: State of PeakVol instances indexed by the PeakVol's instance_id
+ * @llp_gpdma_reading_slots: LLP Readings for single link gateways
+ * @llp_sndw_reading_slots: SNDW aggregated link gateways
+ * @llp_evad_reading_slot: LLP Readings for EVAD gateway
+ */
+struct sof_ipc4_fw_registers {
+ u32 fw_status;
+ u32 lec;
+ u32 fps;
+ u32 lnec;
+ u32 ltr;
+ u32 rsvd0;
+ u32 rom_info;
+ u32 abi_ver;
+ u8 slave_core_sts[SOF_IPC4_MAX_SUPPORTED_ADSP_CORES];
+ u32 rsvd2[6];
+
+ struct sof_ipc4_pipeline_registers
+ pipeline_regs[SOF_IPC4_MAX_PIPELINE_REG_SLOTS];
+
+ struct sof_ipc4_peak_volume_regs
+ peak_vol_regs[SOF_IPC4_MAX_PEAK_VOL_REG_SLOTS];
+
+ struct sof_ipc4_llp_reading_slot
+ llp_gpdma_reading_slots[SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS];
+
+ struct sof_ipc4_llp_reading_slot
+ llp_sndw_reading_slots[SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS];
+
+ struct sof_ipc4_llp_reading_slot llp_evad_reading_slot;
+} __packed __aligned(4);
+
+#endif
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 96941bebc1f19e..68258767aace91 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -10,8 +10,37 @@
#include <sound/sof/ipc4/header.h>
#include "sof-audio.h"
#include "sof-priv.h"
+#include "ops.h"
#include "ipc4-priv.h"
#include "ipc4-topology.h"
+#include "ipc4-fw-reg.h"
+
+static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state,
+ struct ipc4_pipeline_set_state_data *trigger_list)
+{
+ struct sof_ipc4_msg msg = {{ 0 }};
+ u32 primary, ipc_size;
+
+ /* trigger a single pipeline */
+ if (trigger_list->count == 1)
+ return sof_ipc4_set_pipeline_state(sdev, trigger_list->pipeline_ids[0], state);
+
+ primary = state;
+ primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE);
+ primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
+ msg.primary = primary;
+
+ /* trigger multiple pipelines with a single IPC */
+ msg.extension = SOF_IPC4_GLB_PIPE_STATE_EXT_MULTI;
+
+ /* ipc_size includes the count and the pipeline IDs for the number of pipelines */
+ ipc_size = sizeof(u32) * (trigger_list->count + 1);
+ msg.data_size = ipc_size;
+ msg.data_ptr = trigger_list;
+
+ return sof_ipc_tx_message(sdev->ipc, &msg, ipc_size, NULL, 0);
+}
int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
{
@@ -32,80 +61,238 @@ int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state)
}
EXPORT_SYMBOL(sof_ipc4_set_pipeline_state);
+static void
+sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state,
+ struct snd_sof_pipeline *spipe,
+ struct ipc4_pipeline_set_state_data *trigger_list)
+{
+ struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+
+ if (pipeline->skip_during_fe_trigger)
+ return;
+
+ switch (state) {
+ case SOF_IPC4_PIPE_RUNNING:
+ /*
+ * Trigger pipeline if all PCMs containing it are paused or if it is RUNNING
+ * for the first time
+ */
+ if (spipe->started_count == spipe->paused_count)
+ trigger_list->pipeline_ids[trigger_list->count++] =
+ pipe_widget->instance_id;
+ break;
+ case SOF_IPC4_PIPE_RESET:
+ /* RESET if the pipeline is neither running nor paused */
+ if (!spipe->started_count && !spipe->paused_count)
+ trigger_list->pipeline_ids[trigger_list->count++] =
+ pipe_widget->instance_id;
+ break;
+ case SOF_IPC4_PIPE_PAUSED:
+ /* Pause the pipeline only when its started_count is 1 more than paused_count */
+ if (spipe->paused_count == (spipe->started_count - 1))
+ trigger_list->pipeline_ids[trigger_list->count++] =
+ pipe_widget->instance_id;
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd,
+ struct snd_sof_pipeline *spipe,
+ struct ipc4_pipeline_set_state_data *trigger_list)
+{
+ struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+ int i;
+
+ if (pipeline->skip_during_fe_trigger)
+ return;
+
+ /* set state for pipeline if it was just triggered */
+ for (i = 0; i < trigger_list->count; i++) {
+ if (trigger_list->pipeline_ids[i] == pipe_widget->instance_id) {
+ pipeline->state = state;
+ break;
+ }
+ }
+
+ switch (state) {
+ case SOF_IPC4_PIPE_PAUSED:
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ /*
+ * increment paused_count if the PAUSED is the final state during
+ * the PAUSE trigger
+ */
+ spipe->paused_count++;
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ /*
+ * decrement started_count if PAUSED is the final state during the
+ * STOP trigger
+ */
+ spipe->started_count--;
+ break;
+ default:
+ break;
+ }
+ break;
+ case SOF_IPC4_PIPE_RUNNING:
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /* decrement paused_count for RELEASE */
+ spipe->paused_count--;
+ break;
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ /* increment started_count for START/RESUME */
+ spipe->started_count++;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * The picture below represents the pipeline state machine wrt PCM actions corresponding to the
+ * triggers and ioctls
+ * +---------------+
+ * | |
+ * | INIT |
+ * | |
+ * +-------+-------+
+ * |
+ * |
+ * | START
+ * |
+ * |
+ * +----------------+ +------v-------+ +-------------+
+ * | | START | | HW_FREE | |
+ * | RUNNING <-------------+ PAUSED +--------------> + RESET |
+ * | | PAUSE | | | |
+ * +------+---------+ RELEASE +---------+----+ +-------------+
+ * | ^
+ * | |
+ * | |
+ * | |
+ * | PAUSE |
+ * +---------------------------------+
+ * STOP/SUSPEND
+ *
+ * Note that during system suspend, the suspend trigger is followed by a hw_free in
+ * sof_pcm_trigger(). So, the final state during suspend would be RESET.
+ * Also, since the SOF driver doesn't support full resume, streams would be restarted with the
+ * prepare ioctl before the START trigger.
+ */
+
static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
- struct snd_pcm_substream *substream, int state)
+ struct snd_pcm_substream *substream, int state, int cmd)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct snd_sof_widget *pipeline_widget;
- struct snd_soc_dapm_widget_list *list;
- struct snd_soc_dapm_widget *widget;
- struct sof_ipc4_pipeline *pipeline;
- struct snd_sof_widget *swidget;
+ struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct ipc4_pipeline_set_state_data *trigger_list;
+ struct snd_sof_pipeline *spipe;
struct snd_sof_pcm *spcm;
- int ret = 0;
- int num_widgets;
+ int ret;
+ int i;
+
+ dev_dbg(sdev->dev, "trigger cmd: %d state: %d\n", cmd, state);
spcm = snd_sof_find_spcm_dai(component, rtd);
if (!spcm)
return -EINVAL;
- list = spcm->stream[substream->stream].list;
+ pipeline_list = &spcm->stream[substream->stream].pipeline_list;
- for_each_dapm_widgets(list, num_widgets, widget) {
- swidget = widget->dobj.private;
+ /* nothing to trigger if the list is empty */
+ if (!pipeline_list->pipelines || !pipeline_list->count)
+ return 0;
- if (!swidget)
- continue;
+ /* allocate memory for the pipeline data */
+ trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count),
+ GFP_KERNEL);
+ if (!trigger_list)
+ return -ENOMEM;
- /*
- * set pipeline state for both FE and BE pipelines for RUNNING state.
- * For PAUSE/RESET, set the pipeline state only for the FE pipeline.
- */
- switch (state) {
- case SOF_IPC4_PIPE_PAUSED:
- case SOF_IPC4_PIPE_RESET:
- if (!WIDGET_IS_AIF(swidget->id))
- continue;
- break;
- default:
- break;
- }
+ mutex_lock(&ipc4_data->pipeline_state_mutex);
- /* find pipeline widget for the pipeline that this widget belongs to */
- pipeline_widget = swidget->pipe_widget;
- pipeline = (struct sof_ipc4_pipeline *)pipeline_widget->private;
+ /*
+ * IPC4 requires pipelines to be triggered in order starting at the sink and
+ * walking all the way to the source. So traverse the pipeline_list in the order
+ * sink->source when starting PCM's and in the reverse order to pause/stop PCM's.
+ * Skip the pipelines that have their skip_during_fe_trigger flag set. If there is a fork
+ * in the pipeline, the order of triggering between the left/right paths will be
+ * indeterministic. But the sink->source trigger order sink->source would still be
+ * guaranteed for each fork independently.
+ */
+ if (state == SOF_IPC4_PIPE_RUNNING || state == SOF_IPC4_PIPE_RESET)
+ for (i = pipeline_list->count - 1; i >= 0; i--) {
+ spipe = pipeline_list->pipelines[i];
+ sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list);
+ }
+ else
+ for (i = 0; i < pipeline_list->count; i++) {
+ spipe = pipeline_list->pipelines[i];
+ sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list);
+ }
- if (pipeline->state == state)
- continue;
+ /* return if all pipelines are in the requested state already */
+ if (!trigger_list->count) {
+ ret = 0;
+ goto free;
+ }
- /* first set the pipeline to PAUSED state */
- if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
- ret = sof_ipc4_set_pipeline_state(sdev, pipeline_widget->instance_id,
- SOF_IPC4_PIPE_PAUSED);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to pause pipeline %d\n",
- swidget->pipeline_id);
- return ret;
- }
- }
+ /* no need to pause before reset or before pause release */
+ if (state == SOF_IPC4_PIPE_RESET || cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
+ goto skip_pause_transition;
- pipeline->state = SOF_IPC4_PIPE_PAUSED;
+ /*
+ * set paused state for pipelines if the final state is PAUSED or when the pipeline
+ * is set to RUNNING for the first time after the PCM is started.
+ */
+ ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, trigger_list);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to pause all pipelines\n");
+ goto free;
+ }
- if (pipeline->state == state)
- continue;
+ /* update PAUSED state for all pipelines just triggered */
+ for (i = 0; i < pipeline_list->count ; i++) {
+ spipe = pipeline_list->pipelines[i];
+ sof_ipc4_update_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, cmd, spipe,
+ trigger_list);
+ }
- /* then set the final state */
- ret = sof_ipc4_set_pipeline_state(sdev, pipeline_widget->instance_id, state);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to set state %d for pipeline %d\n",
- state, swidget->pipeline_id);
- break;
- }
+ /* return if this is the final state */
+ if (state == SOF_IPC4_PIPE_PAUSED)
+ goto free;
+skip_pause_transition:
+ /* else set the RUNNING/RESET state in the DSP */
+ ret = sof_ipc4_set_multi_pipeline_state(sdev, state, trigger_list);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to set final state %d for all pipelines\n", state);
+ goto free;
+ }
- pipeline->state = state;
+ /* update RUNNING/RESET state for all pipelines that were just triggered */
+ for (i = 0; i < pipeline_list->count; i++) {
+ spipe = pipeline_list->pipelines[i];
+ sof_ipc4_update_pipeline_state(sdev, state, cmd, spipe, trigger_list);
}
+free:
+ mutex_unlock(&ipc4_data->pipeline_state_mutex);
+ kfree(trigger_list);
return ret;
}
@@ -134,13 +321,14 @@ static int sof_ipc4_pcm_trigger(struct snd_soc_component *component,
}
/* set the pipeline state */
- return sof_ipc4_trigger_pipelines(component, substream, state);
+ return sof_ipc4_trigger_pipelines(component, substream, state, cmd);
}
static int sof_ipc4_pcm_hw_free(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
- return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET);
+ /* command is not relevant with RESET, so just pass 0 */
+ return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0);
}
static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name,
@@ -183,7 +371,6 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct sof_ipc4_copier *ipc4_copier;
- struct snd_soc_dpcm *dpcm;
if (!dai) {
dev_err(component->dev, "%s: No DAI found with name %s\n", __func__,
@@ -205,17 +392,6 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
rate->min = ipc4_copier->available_fmt.base_config->audio_fmt.sampling_frequency;
rate->max = rate->min;
- /*
- * Set trigger order for capture to SND_SOC_DPCM_TRIGGER_PRE. This is required
- * to ensure that the BE DAI pipeline gets stopped/suspended before the FE DAI
- * pipeline gets triggered and the pipeline widgets are freed.
- */
- for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_CAPTURE, dpcm) {
- struct snd_soc_pcm_runtime *fe = dpcm->fe;
-
- fe->dai_link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE;
- }
-
switch (ipc4_copier->dai_type) {
case SOF_DAI_INTEL_SSP:
ipc4_ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params);
@@ -227,8 +403,273 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
+static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm)
+{
+ struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
+ int stream;
+
+ for_each_pcm_streams(stream) {
+ pipeline_list = &spcm->stream[stream].pipeline_list;
+ kfree(pipeline_list->pipelines);
+ pipeline_list->pipelines = NULL;
+ kfree(spcm->stream[stream].private);
+ spcm->stream[stream].private = NULL;
+ }
+}
+
+static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm)
+{
+ struct snd_sof_pcm_stream_pipeline_list *pipeline_list;
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct sof_ipc4_timestamp_info *stream_info;
+ bool support_info = true;
+ u32 abi_version;
+ u32 abi_offset;
+ int stream;
+
+ abi_offset = offsetof(struct sof_ipc4_fw_registers, abi_ver);
+ sof_mailbox_read(sdev, sdev->fw_info_box.offset + abi_offset, &abi_version,
+ sizeof(abi_version));
+
+ if (abi_version < SOF_IPC4_FW_REGS_ABI_VER)
+ support_info = false;
+
+ for_each_pcm_streams(stream) {
+ pipeline_list = &spcm->stream[stream].pipeline_list;
+
+ /* allocate memory for max number of pipeline IDs */
+ pipeline_list->pipelines = kcalloc(ipc4_data->max_num_pipelines,
+ sizeof(struct snd_sof_widget *), GFP_KERNEL);
+ if (!pipeline_list->pipelines) {
+ sof_ipc4_pcm_free(sdev, spcm);
+ return -ENOMEM;
+ }
+
+ if (!support_info)
+ continue;
+
+ stream_info = kzalloc(sizeof(*stream_info), GFP_KERNEL);
+ if (!stream_info) {
+ sof_ipc4_pcm_free(sdev, spcm);
+ return -ENOMEM;
+ }
+
+ spcm->stream[stream].private = stream_info;
+ }
+
+ return 0;
+}
+
+static void sof_ipc4_build_time_info(struct snd_sof_dev *sdev, struct snd_sof_pcm_stream *spcm)
+{
+ struct sof_ipc4_copier *host_copier = NULL;
+ struct sof_ipc4_copier *dai_copier = NULL;
+ struct sof_ipc4_llp_reading_slot llp_slot;
+ struct sof_ipc4_timestamp_info *info;
+ struct snd_soc_dapm_widget *widget;
+ struct snd_sof_dai *dai;
+ int i;
+
+ /* find host & dai to locate info in memory window */
+ for_each_dapm_widgets(spcm->list, i, widget) {
+ struct snd_sof_widget *swidget = widget->dobj.private;
+
+ if (!swidget)
+ continue;
+
+ if (WIDGET_IS_AIF(swidget->widget->id)) {
+ host_copier = swidget->private;
+ } else if (WIDGET_IS_DAI(swidget->widget->id)) {
+ dai = swidget->private;
+ dai_copier = dai->private;
+ }
+ }
+
+ /* both host and dai copier must be valid for time_info */
+ if (!host_copier || !dai_copier) {
+ dev_err(sdev->dev, "host or dai copier are not found\n");
+ return;
+ }
+
+ info = spcm->private;
+ info->host_copier = host_copier;
+ info->dai_copier = dai_copier;
+ info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_gpdma_reading_slots) +
+ sdev->fw_info_box.offset;
+
+ /* find llp slot used by current dai */
+ for (i = 0; i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS; i++) {
+ sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot));
+ if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id)
+ break;
+
+ info->llp_offset += sizeof(llp_slot);
+ }
+
+ if (i < SOF_IPC4_MAX_LLP_GPDMA_READING_SLOTS)
+ return;
+
+ /* if no llp gpdma slot is used, check aggregated sdw slot */
+ info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_sndw_reading_slots) +
+ sdev->fw_info_box.offset;
+ for (i = 0; i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS; i++) {
+ sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot));
+ if (llp_slot.node_id == dai_copier->data.gtw_cfg.node_id)
+ break;
+
+ info->llp_offset += sizeof(llp_slot);
+ }
+
+ if (i < SOF_IPC4_MAX_LLP_SNDW_READING_SLOTS)
+ return;
+
+ /* check EVAD slot */
+ info->llp_offset = offsetof(struct sof_ipc4_fw_registers, llp_evad_reading_slot) +
+ sdev->fw_info_box.offset;
+ sof_mailbox_read(sdev, info->llp_offset, &llp_slot, sizeof(llp_slot));
+ if (llp_slot.node_id != dai_copier->data.gtw_cfg.node_id) {
+ dev_info(sdev->dev, "no llp found, fall back to default HDA path");
+ info->llp_offset = 0;
+ }
+}
+
+static int sof_ipc4_pcm_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_sof_platform_stream_params *platform_params)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct sof_ipc4_timestamp_info *time_info;
+ struct snd_sof_pcm *spcm;
+
+ spcm = snd_sof_find_spcm_dai(component, rtd);
+ time_info = spcm->stream[substream->stream].private;
+ /* delay calculation is not supported by current fw_reg ABI */
+ if (!time_info)
+ return 0;
+
+ time_info->stream_start_offset = SOF_IPC4_INVALID_STREAM_POSITION;
+ time_info->llp_offset = 0;
+
+ sof_ipc4_build_time_info(sdev, &spcm->stream[substream->stream]);
+
+ return 0;
+}
+
+static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *stream,
+ struct sof_ipc4_timestamp_info *time_info)
+{
+ struct sof_ipc4_copier *host_copier = time_info->host_copier;
+ struct sof_ipc4_copier *dai_copier = time_info->dai_copier;
+ struct sof_ipc4_pipeline_registers ppl_reg;
+ u64 stream_start_position;
+ u32 dai_sample_size;
+ u32 ch, node_index;
+ u32 offset;
+
+ if (!host_copier || !dai_copier)
+ return -EINVAL;
+
+ if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_INVALID_NODE_ID)
+ return -EINVAL;
+
+ node_index = SOF_IPC4_NODE_INDEX(host_copier->data.gtw_cfg.node_id);
+ offset = offsetof(struct sof_ipc4_fw_registers, pipeline_regs) + node_index * sizeof(ppl_reg);
+ sof_mailbox_read(sdev, sdev->fw_info_box.offset + offset, &ppl_reg, sizeof(ppl_reg));
+ if (ppl_reg.stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION)
+ return -EINVAL;
+
+ stream_start_position = ppl_reg.stream_start_offset;
+ ch = dai_copier->data.out_format.fmt_cfg;
+ ch = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(ch);
+ dai_sample_size = (dai_copier->data.out_format.bit_depth >> 3) * ch;
+ /* convert offset to sample count */
+ do_div(stream_start_position, dai_sample_size);
+ time_info->stream_start_offset = stream_start_position;
+
+ return 0;
+}
+
+static snd_pcm_sframes_t sof_ipc4_pcm_delay(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ struct sof_ipc4_timestamp_info *time_info;
+ struct sof_ipc4_llp_reading_slot llp;
+ snd_pcm_uframes_t head_ptr, tail_ptr;
+ struct snd_sof_pcm_stream *stream;
+ struct snd_sof_pcm *spcm;
+ u64 tmp_ptr;
+ int ret;
+
+ spcm = snd_sof_find_spcm_dai(component, rtd);
+ if (!spcm)
+ return 0;
+
+ stream = &spcm->stream[substream->stream];
+ time_info = stream->private;
+ if (!time_info)
+ return 0;
+
+ /*
+ * stream_start_offset is updated to memory window by FW based on
+ * pipeline statistics and it may be invalid if host query happens before
+ * the statistics is complete. And it will not change after the first initiailization.
+ */
+ if (time_info->stream_start_offset == SOF_IPC4_INVALID_STREAM_POSITION) {
+ ret = sof_ipc4_get_stream_start_offset(sdev, substream, stream, time_info);
+ if (ret < 0)
+ return 0;
+ }
+
+ /*
+ * HDaudio links don't support the LLP counter reported by firmware
+ * the link position is read directly from hardware registers.
+ */
+ if (!time_info->llp_offset) {
+ tmp_ptr = snd_sof_pcm_get_stream_position(sdev, component, substream);
+ if (!tmp_ptr)
+ return 0;
+ } else {
+ sof_mailbox_read(sdev, time_info->llp_offset, &llp, sizeof(llp));
+ tmp_ptr = ((u64)llp.reading.llp_u << 32) | llp.reading.llp_l;
+ }
+
+ /* In two cases dai dma position is not accurate
+ * (1) dai pipeline is started before host pipeline
+ * (2) multiple streams mixed into one. Each stream has the same dai dma position
+ *
+ * Firmware calculates correct stream_start_offset for all cases including above two.
+ * Driver subtracts stream_start_offset from dai dma position to get accurate one
+ */
+ tmp_ptr -= time_info->stream_start_offset;
+
+ /* Calculate the delay taking into account that both pointer can wrap */
+ div64_u64_rem(tmp_ptr, substream->runtime->boundary, &tmp_ptr);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ head_ptr = substream->runtime->status->hw_ptr;
+ tail_ptr = tmp_ptr;
+ } else {
+ head_ptr = tmp_ptr;
+ tail_ptr = substream->runtime->status->hw_ptr;
+ }
+
+ if (head_ptr < tail_ptr)
+ return substream->runtime->boundary - tail_ptr + head_ptr;
+
+ return head_ptr - tail_ptr;
+}
+
const struct sof_ipc_pcm_ops ipc4_pcm_ops = {
+ .hw_params = sof_ipc4_pcm_hw_params,
.trigger = sof_ipc4_pcm_trigger,
.hw_free = sof_ipc4_pcm_hw_free,
.dai_link_fixup = sof_ipc4_pcm_dai_link_fixup,
+ .pcm_setup = sof_ipc4_pcm_setup,
+ .pcm_free = sof_ipc4_pcm_free,
+ .delay = sof_ipc4_pcm_delay
};
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
index fc9efdce67e0f3..f461b8c70df366 100644
--- a/sound/soc/sof/ipc4-priv.h
+++ b/sound/soc/sof/ipc4-priv.h
@@ -14,6 +14,7 @@
#include "sof-priv.h"
/* The DSP window indices are fixed */
+#define SOF_IPC4_INBOX_WINDOW_IDX 0
#define SOF_IPC4_OUTBOX_WINDOW_IDX 1
#define SOF_IPC4_DEBUG_WINDOW_IDX 2
@@ -70,6 +71,7 @@ struct sof_ipc4_fw_library {
* base firmware
*
* @load_library: Callback function for platform dependent library loading
+ * @pipeline_state_mutex: Mutex to protect pipeline triggers, ref counts, states and deletion
*/
struct sof_ipc4_fw_data {
u32 manifest_fw_hdr_offset;
@@ -82,6 +84,21 @@ struct sof_ipc4_fw_data {
int (*load_library)(struct snd_sof_dev *sdev,
struct sof_ipc4_fw_library *fw_lib, bool reload);
+ struct mutex pipeline_state_mutex; /* protect pipeline triggers, ref counts and states */
+};
+
+/**
+ * struct sof_ipc4_timestamp_info - IPC4 timestamp info
+ * @host_copier: the host copier of the pcm stream
+ * @dai_copier: the dai copier of the pcm stream
+ * @stream_start_offset: reported by fw in memory window
+ * @llp_offset: llp offset in memory window
+ */
+struct sof_ipc4_timestamp_info {
+ struct sof_ipc4_copier *host_copier;
+ struct sof_ipc4_copier *dai_copier;
+ u64 stream_start_offset;
+ u32 llp_offset;
};
extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops;
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 59f4d42f9011ea..3e27c7a48ebd39 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -354,6 +354,13 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
goto free_available_fmt;
}
+ /*
+ * This callback is used by host copier and module-to-module copier,
+ * and only host copier needs to set gtw_cfg.
+ */
+ if (!WIDGET_IS_AIF(swidget->id))
+ goto skip_gtw_cfg;
+
ret = sof_update_ipc_object(scomp, available_fmt->dma_buffer_size,
SOF_COPIER_GATEWAY_CFG_TOKENS, swidget->tuples,
swidget->num_tuples, sizeof(u32),
@@ -380,7 +387,7 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
}
dev_dbg(scomp->dev, "host copier '%s' node_type %u\n", swidget->widget->name, node_type);
- ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);
+skip_gtw_cfg:
ipc4_copier->gtw_attr = kzalloc(sizeof(*ipc4_copier->gtw_attr), GFP_KERNEL);
if (!ipc4_copier->gtw_attr) {
ret = -ENOMEM;
@@ -391,6 +398,21 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
ipc4_copier->data.gtw_cfg.config_length =
sizeof(struct sof_ipc4_gtw_attributes) >> 2;
+ switch (swidget->id) {
+ case snd_soc_dapm_aif_in:
+ case snd_soc_dapm_aif_out:
+ ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);
+ break;
+ case snd_soc_dapm_buffer:
+ ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID;
+ ipc4_copier->ipc_config_size = 0;
+ break;
+ default:
+ dev_err(scomp->dev, "invalid widget type %d\n", swidget->id);
+ ret = -EINVAL;
+ goto free_gtw_attr;
+ }
+
/* set up module info and message header */
ret = sof_ipc4_widget_setup_msg(swidget, &ipc4_copier->msg);
if (ret)
@@ -833,7 +855,7 @@ sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widg
total = SOF_IPC4_FW_PAGE(task_mem + queue_mem);
- pipe_widget = swidget->pipe_widget;
+ pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
pipeline->mem_usage += total;
}
@@ -947,11 +969,11 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget)
struct sof_ipc4_pipeline *pipeline;
/* reset pipeline memory usage */
- pipe_widget = swidget->pipe_widget;
+ pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
pipeline->mem_usage = 0;
- if (WIDGET_IS_AIF(swidget->id)) {
+ if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) {
ipc4_copier = swidget->private;
} else if (WIDGET_IS_DAI(swidget->id)) {
struct snd_sof_dai *dai = swidget->private;
@@ -1114,7 +1136,7 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_sof_widget *pipe_widget;
struct sof_ipc4_pipeline *pipeline;
- pipe_widget = swidget->pipe_widget;
+ pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
gtw_attr = ipc4_copier->gtw_attr;
@@ -1177,6 +1199,22 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
break;
}
+ case snd_soc_dapm_buffer:
+ {
+ ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
+ copier_data = &ipc4_copier->data;
+ available_fmt = &ipc4_copier->available_fmt;
+
+ /*
+ * base_config->audio_fmt represent the input audio formats. Use
+ * the input format as the reference to match pcm params
+ */
+ available_fmt->ref_audio_fmt = &available_fmt->base_config->audio_fmt;
+ ref_audio_fmt_size = sizeof(struct sof_ipc4_base_module_cfg);
+ ref_params = pipeline_params;
+
+ break;
+ }
default:
dev_err(sdev->dev, "unsupported type %d for copier %s",
swidget->id, swidget->widget->name);
@@ -1203,8 +1241,11 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct sof_ipc4_copier_data *alh_data;
struct sof_ipc4_copier *alh_copier;
struct snd_sof_widget *w;
+ u32 ch_count = 0;
u32 ch_mask = 0;
u32 ch_map;
+ u32 step;
+ u32 mask;
int i;
blob = (struct sof_ipc4_alh_configuration_blob *)ipc4_copier->copier_config;
@@ -1214,11 +1255,15 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
/* Get channel_mask from ch_map */
ch_map = copier_data->base_config.audio_fmt.ch_map;
for (i = 0; ch_map; i++) {
- if ((ch_map & 0xf) != 0xf)
+ if ((ch_map & 0xf) != 0xf) {
ch_mask |= BIT(i);
+ ch_count++;
+ }
ch_map >>= 4;
}
+ step = ch_count / blob->alh_cfg.count;
+ mask = GENMASK(step - 1, 0);
/*
* Set each gtw_cfg.node_id to blob->alh_cfg.mapping[]
* for all widgets with the same stream name
@@ -1233,7 +1278,22 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
alh_copier = (struct sof_ipc4_copier *)dai->private;
alh_data = &alh_copier->data;
blob->alh_cfg.mapping[i].alh_id = alh_data->gtw_cfg.node_id;
- blob->alh_cfg.mapping[i].channel_mask = ch_mask;
+ /*
+ * Set the same channel mask for playback as the audio data is
+ * duplicated for all speakers. For capture, split the channels
+ * among the aggregated DAIs. For example, with 4 channels on 2
+ * aggregated DAIs, the channel_mask should be 0x3 and 0xc for the
+ * two DAI's.
+ * The channel masks used depend on the cpu_dais used in the
+ * dailink at the machine driver level, which actually comes from
+ * the tables in soc_acpi files depending on the _ADR and devID
+ * registers for each codec.
+ */
+ if (w->id == snd_soc_dapm_dai_in)
+ blob->alh_cfg.mapping[i].channel_mask = ch_mask;
+ else
+ blob->alh_cfg.mapping[i].channel_mask = mask << (step * i);
+
i++;
}
if (blob->alh_cfg.count > 1) {
@@ -1435,7 +1495,7 @@ static int sof_ipc4_control_setup(struct snd_sof_dev *sdev, struct snd_sof_contr
static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
- struct snd_sof_widget *pipe_widget = swidget->pipe_widget;
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct sof_ipc4_pipeline *pipeline;
struct sof_ipc4_msg *msg;
@@ -1465,6 +1525,7 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
break;
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
+ case snd_soc_dapm_buffer:
{
struct sof_ipc4_copier *ipc4_copier = swidget->private;
@@ -1564,8 +1625,11 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
{
struct sof_ipc4_fw_module *fw_module = swidget->module_info;
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
int ret = 0;
+ mutex_lock(&ipc4_data->pipeline_state_mutex);
+
/* freeing a pipeline frees all the widgets associated with it */
if (swidget->id == snd_soc_dapm_scheduler) {
struct sof_ipc4_pipeline *pipeline = swidget->private;
@@ -1591,6 +1655,8 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget
ida_free(&fw_module->m_ida, swidget->instance_id);
}
+ mutex_unlock(&ipc4_data->pipeline_state_mutex);
+
return ret;
}
@@ -1676,6 +1742,55 @@ static void sof_ipc4_put_queue_id(struct snd_sof_widget *swidget, int queue_id,
ida_free(queue_ida, queue_id);
}
+static int sof_ipc4_set_copier_sink_format(struct snd_sof_dev *sdev,
+ struct snd_sof_widget *src_widget,
+ struct snd_sof_widget *sink_widget,
+ int sink_id)
+{
+ struct sof_ipc4_base_module_cfg *sink_config = sink_widget->private;
+ struct sof_ipc4_base_module_cfg *src_config;
+ struct sof_ipc4_copier_config_set_sink_format format;
+ struct sof_ipc4_fw_module *fw_module;
+ struct sof_ipc4_msg msg = {{ 0 }};
+ u32 header, extension;
+
+ dev_dbg(sdev->dev, "%s set copier sink %d format\n",
+ src_widget->widget->name, sink_id);
+
+ if (WIDGET_IS_DAI(src_widget->id)) {
+ struct snd_sof_dai *dai = src_widget->private;
+
+ src_config = dai->private;
+ } else {
+ src_config = src_widget->private;
+ }
+
+ fw_module = src_widget->module_info;
+
+ format.sink_id = sink_id;
+ memcpy(&format.source_fmt, &src_config->audio_fmt, sizeof(format.source_fmt));
+ memcpy(&format.sink_fmt, &sink_config->audio_fmt, sizeof(format.sink_fmt));
+ msg.data_size = sizeof(format);
+ msg.data_ptr = &format;
+
+ header = fw_module->man4_module_entry.id;
+ header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
+ header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_LARGE_CONFIG_SET);
+ header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ header |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+
+ extension = SOF_IPC4_MOD_EXT_MSG_SIZE(msg.data_size);
+ extension |=
+ SOF_IPC4_MOD_EXT_MSG_PARAM_ID(SOF_IPC4_COPIER_MODULE_CFG_PARAM_SET_SINK_FORMAT);
+ extension |= SOF_IPC4_MOD_EXT_MSG_LAST_BLOCK(1);
+ extension |= SOF_IPC4_MOD_EXT_MSG_FIRST_BLOCK(1);
+
+ msg.primary = header;
+ msg.extension = extension;
+
+ return sof_ipc_tx_message(sdev->ipc, &msg, msg.data_size, NULL, 0);
+}
+
static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
{
struct snd_sof_widget *src_widget = sroute->src_widget;
@@ -1704,6 +1819,17 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
return sroute->dst_queue_id;
}
+ /* Pin 0 format is already set during copier module init */
+ if (sroute->src_queue_id > 0 && WIDGET_IS_COPIER(src_widget->id)) {
+ ret = sof_ipc4_set_copier_sink_format(sdev, src_widget, sink_widget,
+ sroute->src_queue_id);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to set sink format for %s source queue ID %d\n",
+ src_widget->widget->name, sroute->src_queue_id);
+ goto out;
+ }
+ }
+
dev_dbg(sdev->dev, "bind %s:%d -> %s:%d\n",
src_widget->widget->name, sroute->src_queue_id,
sink_widget->widget->name, sroute->dst_queue_id);
@@ -1724,16 +1850,18 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route *
ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
if (ret < 0) {
- dev_err(sdev->dev, "%s: failed to bind modules %s -> %s\n",
- __func__, src_widget->widget->name, sink_widget->widget->name);
-
- sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id,
- SOF_PIN_TYPE_SOURCE);
- sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id,
- SOF_PIN_TYPE_SINK);
+ dev_err(sdev->dev, "failed to bind modules %s:%d -> %s:%d\n",
+ src_widget->widget->name, sroute->src_queue_id,
+ sink_widget->widget->name, sroute->dst_queue_id);
+ goto out;
}
return ret;
+
+out:
+ sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE);
+ sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK);
+ return ret;
}
static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *sroute)
@@ -1744,12 +1872,19 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;
struct sof_ipc4_msg msg = {{ 0 }};
u32 header, extension;
- int ret;
+ int ret = 0;
dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n",
src_widget->widget->name, sroute->src_queue_id,
sink_widget->widget->name, sroute->dst_queue_id);
+ /*
+ * routes belonging to the same pipeline will be disconnected by the FW when the pipeline
+ * is freed. So avoid sending this IPC which will be ignored by the FW anyway.
+ */
+ if (src_widget->spipe->pipe_widget == sink_widget->spipe->pipe_widget)
+ goto out;
+
header = src_fw_module->man4_module_entry.id;
header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id);
header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_UNBIND);
@@ -1766,9 +1901,10 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
if (ret < 0)
- dev_err(sdev->dev, "failed to unbind modules %s -> %s\n",
- src_widget->widget->name, sink_widget->widget->name);
-
+ dev_err(sdev->dev, "failed to unbind modules %s:%d -> %s:%d\n",
+ src_widget->widget->name, sroute->src_queue_id,
+ sink_widget->widget->name, sroute->dst_queue_id);
+out:
sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK);
sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE);
@@ -1778,7 +1914,7 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s
static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
unsigned int flags, struct snd_sof_dai_config_data *data)
{
- struct snd_sof_widget *pipe_widget = swidget->pipe_widget;
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
struct snd_sof_dai *dai = swidget->private;
struct sof_ipc4_gtw_attributes *gtw_attr;
@@ -1801,6 +1937,7 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
case SOF_DAI_INTEL_HDA:
gtw_attr = ipc4_copier->gtw_attr;
gtw_attr->lp_buffer_alloc = pipeline->lp_mode;
+ pipeline->skip_during_fe_trigger = true;
fallthrough;
case SOF_DAI_INTEL_ALH:
copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK;
@@ -1957,7 +2094,7 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
for_each_pcm_streams(dir) {
struct snd_pcm_substream *substream = spcm->stream[dir].substream;
- if (!substream || !substream->runtime)
+ if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)
continue;
if (spcm->stream[dir].list) {
@@ -1970,7 +2107,25 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
return 0;
}
-static enum sof_tokens host_token_list[] = {
+static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link)
+{
+ if (link->no_pcm)
+ return 0;
+
+ /*
+ * set default trigger order for all links. Exceptions to
+ * the rule will be handled in sof_pcm_dai_link_fixup()
+ * For playback, the sequence is the following: start BE,
+ * start FE, stop FE, stop BE; for Capture the sequence is
+ * inverted start FE, start BE, stop BE, stop FE
+ */
+ link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_POST;
+ link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE;
+
+ return 0;
+}
+
+static enum sof_tokens common_copier_token_list[] = {
SOF_COMP_TOKENS,
SOF_AUDIO_FMT_NUM_TOKENS,
SOF_AUDIO_FORMAT_BUFFER_SIZE_TOKENS,
@@ -2026,12 +2181,12 @@ static enum sof_tokens src_token_list[] = {
static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TYPE_COUNT] = {
[snd_soc_dapm_aif_in] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,
- host_token_list, ARRAY_SIZE(host_token_list), NULL,
- sof_ipc4_prepare_copier_module,
+ common_copier_token_list, ARRAY_SIZE(common_copier_token_list),
+ NULL, sof_ipc4_prepare_copier_module,
sof_ipc4_unprepare_copier_module},
[snd_soc_dapm_aif_out] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,
- host_token_list, ARRAY_SIZE(host_token_list), NULL,
- sof_ipc4_prepare_copier_module,
+ common_copier_token_list, ARRAY_SIZE(common_copier_token_list),
+ NULL, sof_ipc4_prepare_copier_module,
sof_ipc4_unprepare_copier_module},
[snd_soc_dapm_dai_in] = {sof_ipc4_widget_setup_comp_dai, sof_ipc4_widget_free_comp_dai,
dai_token_list, ARRAY_SIZE(dai_token_list), NULL,
@@ -2041,6 +2196,10 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TY
dai_token_list, ARRAY_SIZE(dai_token_list), NULL,
sof_ipc4_prepare_copier_module,
sof_ipc4_unprepare_copier_module},
+ [snd_soc_dapm_buffer] = {sof_ipc4_widget_setup_pcm, sof_ipc4_widget_free_comp_pcm,
+ common_copier_token_list, ARRAY_SIZE(common_copier_token_list),
+ NULL, sof_ipc4_prepare_copier_module,
+ sof_ipc4_unprepare_copier_module},
[snd_soc_dapm_scheduler] = {sof_ipc4_widget_setup_comp_pipeline,
sof_ipc4_widget_free_comp_pipeline,
pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL,
@@ -2072,4 +2231,5 @@ const struct sof_ipc_tplg_ops ipc4_tplg_ops = {
.parse_manifest = sof_ipc4_parse_manifest,
.dai_get_clk = sof_ipc4_dai_get_clk,
.tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines,
+ .link_setup = sof_ipc4_link_setup,
};
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h
index 0aa87a8add5d39..72529179ac2234 100644
--- a/sound/soc/sof/ipc4-topology.h
+++ b/sound/soc/sof/ipc4-topology.h
@@ -53,6 +53,8 @@
#define ALH_MAX_NUMBER_OF_GTW 16
+#define SOF_IPC4_INVALID_NODE_ID 0xffffffff
+
/*
* The base of multi-gateways. Multi-gateways addressing starts from
* ALH_MULTI_GTW_BASE and there are ALH_MULTI_GTW_COUNT multi-sources
@@ -64,6 +66,52 @@
/* A magic number from FW */
#define ALH_MULTI_GTW_COUNT 8
+enum sof_ipc4_copier_module_config_params {
+/*
+ * Use LARGE_CONFIG_SET to initialize timestamp event. Ipc mailbox must
+ * contain properly built CopierConfigTimestampInitData struct.
+ */
+ SOF_IPC4_COPIER_MODULE_CFG_PARAM_TIMESTAMP_INIT = 1,
+/*
+ * Use LARGE_CONFIG_SET to initialize copier sink. Ipc mailbox must contain
+ * properly built CopierConfigSetSinkFormat struct.
+ */
+ SOF_IPC4_COPIER_MODULE_CFG_PARAM_SET_SINK_FORMAT,
+/*
+ * Use LARGE_CONFIG_SET to initialize and enable on Copier data segment
+ * event. Ipc mailbox must contain properly built DataSegmentEnabled struct.
+ */
+ SOF_IPC4_COPIER_MODULE_CFG_PARAM_DATA_SEGMENT_ENABLED,
+/*
+ * Use LARGE_CONFIG_GET to retrieve Linear Link Position (LLP) value for non
+ * HD-A gateways.
+ */
+ SOF_IPC4_COPIER_MODULE_CFG_PARAM_LLP_READING,
+/*
+ * Use LARGE_CONFIG_GET to retrieve Linear Link Position (LLP) value for non
+ * HD-A gateways and corresponding total processed data
+ */
+ SOF_IPC4_COPIER_MODULE_CFG_PARAM_LLP_READING_EXTENDED,
+/*
+ * Use LARGE_CONFIG_SET to setup attenuation on output pins. Data is just uint32_t.
+ * note Config is only allowed when output pin is set up for 32bit and source
+ * is connected to Gateway
+ */
+ SOF_IPC4_COPIER_MODULE_CFG_ATTENUATION,
+};
+
+struct sof_ipc4_copier_config_set_sink_format {
+/* Id of sink */
+ u32 sink_id;
+/*
+ * Input format used by the source
+ * attention must be the same as present if already initialized.
+ */
+ struct sof_ipc4_audio_format source_fmt;
+/* Output format used by the sink */
+ struct sof_ipc4_audio_format sink_fmt;
+} __packed __aligned(4);
+
/**
* struct sof_ipc4_pipeline - pipeline config data
* @priority: Priority of this pipeline
@@ -71,6 +119,7 @@
* @mem_usage: Memory usage
* @state: Pipeline state
* @msg: message structure for pipeline
+ * @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger
*/
struct sof_ipc4_pipeline {
uint32_t priority;
@@ -78,9 +127,20 @@ struct sof_ipc4_pipeline {
uint32_t mem_usage;
int state;
struct sof_ipc4_msg msg;
+ bool skip_during_fe_trigger;
};
/**
+ * struct sof_ipc4_multi_pipeline_data - multi pipeline trigger IPC data
+ * @count: Number of pipelines to be triggered
+ * @pipeline_ids: Flexible array of IDs of the pipelines to be triggered
+ */
+struct ipc4_pipeline_set_state_data {
+ u32 count;
+ DECLARE_FLEX_ARRAY(u32, pipeline_ids);
+} __packed;
+
+/**
* struct sof_ipc4_available_audio_format - Available audio formats
* @base_config: Available base config
* @out_audio_fmt: Available output audio format
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
index 74cd7e95601931..8ede4b95299784 100644
--- a/sound/soc/sof/ipc4.c
+++ b/sound/soc/sof/ipc4.c
@@ -13,6 +13,7 @@
#include <sound/sof/ipc4/header.h>
#include "sof-priv.h"
#include "sof-audio.h"
+#include "ipc4-fw-reg.h"
#include "ipc4-priv.h"
#include "ops.h"
@@ -369,6 +370,17 @@ static int sof_ipc4_tx_msg(struct snd_sof_dev *sdev, void *msg_data, size_t msg_
if (!msg_data)
return -EINVAL;
+ if (!no_pm) {
+ const struct sof_dsp_power_state target_state = {
+ .state = SOF_DSP_PM_D0,
+ };
+
+ /* ensure the DSP is in D0i0 before sending a new IPC */
+ ret = snd_sof_dsp_set_power_state(sdev, &target_state);
+ if (ret < 0)
+ return ret;
+ }
+
/* Serialise IPC TX */
mutex_lock(&ipc->tx_mutex);
@@ -542,6 +554,8 @@ static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg
outbox_offset = snd_sof_dsp_get_window_offset(sdev, SOF_IPC4_OUTBOX_WINDOW_IDX);
outbox_size = SOF_IPC4_MSG_MAX_SIZE;
+ sdev->fw_info_box.offset = snd_sof_dsp_get_window_offset(sdev, SOF_IPC4_INBOX_WINDOW_IDX);
+ sdev->fw_info_box.size = sizeof(struct sof_ipc4_fw_registers);
sdev->dsp_box.offset = inbox_offset;
sdev->dsp_box.size = inbox_size;
sdev->host_box.offset = outbox_offset;
@@ -653,15 +667,30 @@ static int sof_ipc4_ctx_save(struct snd_sof_dev *sdev)
return sof_ipc4_set_core_state(sdev, SOF_DSP_PRIMARY_CORE, false);
}
+static int sof_ipc4_set_pm_gate(struct snd_sof_dev *sdev, u32 flags)
+{
+ struct sof_ipc4_msg msg = {{0}};
+
+ msg.primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_MOD_SET_D0IX);
+ msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
+ msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_MODULE_MSG);
+ msg.extension = flags;
+
+ return sof_ipc4_tx_msg(sdev, &msg, 0, NULL, 0, true);
+}
+
static const struct sof_ipc_pm_ops ipc4_pm_ops = {
.ctx_save = sof_ipc4_ctx_save,
.set_core_state = sof_ipc4_set_core_state,
+ .set_pm_gate = sof_ipc4_set_pm_gate,
};
static int sof_ipc4_init(struct snd_sof_dev *sdev)
{
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ mutex_init(&ipc4_data->pipeline_state_mutex);
+
xa_init_flags(&ipc4_data->fw_lib_xa, XA_FLAGS_ALLOC);
return 0;
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c
index 79da25725987c7..597cb4476acb41 100644
--- a/sound/soc/sof/mediatek/mt8186/mt8186.c
+++ b/sound/soc/sof/mediatek/mt8186/mt8186.c
@@ -494,7 +494,7 @@ static snd_pcm_uframes_t mt8186_pcm_pointer(struct snd_sof_dev *sdev,
}
stream = &spcm->stream[substream->stream];
- ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+ ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn));
if (ret < 0) {
dev_warn(sdev->dev, "failed to read stream position: %d\n", ret);
return 0;
@@ -588,6 +588,9 @@ static struct snd_sof_dsp_ops sof_mt8186_ops = {
.drv = mt8186_dai,
.num_drv = ARRAY_SIZE(mt8186_dai),
+ /* Debug information */
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
+
/* PM */
.suspend = mt8186_dsp_suspend,
.resume = mt8186_dsp_resume,
@@ -625,8 +628,25 @@ static const struct sof_dev_desc sof_of_mt8186_desc = {
.ops = &sof_mt8186_ops,
};
+static const struct sof_dev_desc sof_of_mt8188_desc = {
+ .ipc_supported_mask = BIT(SOF_IPC),
+ .ipc_default = SOF_IPC,
+ .default_fw_path = {
+ [SOF_IPC] = "mediatek/sof",
+ },
+ .default_tplg_path = {
+ [SOF_IPC] = "mediatek/sof-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC] = "sof-mt8188.ri",
+ },
+ .nocodec_tplg_filename = "sof-mt8188-nocodec.tplg",
+ .ops = &sof_mt8186_ops,
+};
+
static const struct of_device_id sof_of_mt8186_ids[] = {
{ .compatible = "mediatek,mt8186-dsp", .data = &sof_of_mt8186_desc},
+ { .compatible = "mediatek,mt8188-dsp", .data = &sof_of_mt8188_desc},
{ }
};
MODULE_DEVICE_TABLE(of, sof_of_mt8186_ids);
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.h b/sound/soc/sof/mediatek/mt8186/mt8186.h
index 98b2965e5ba640..5b521c60b4e34a 100644
--- a/sound/soc/sof/mediatek/mt8186/mt8186.h
+++ b/sound/soc/sof/mediatek/mt8186/mt8186.h
@@ -52,7 +52,15 @@ struct snd_sof_dev;
#define ADSP_PRID 0x0
#define ADSP_ALTVEC_C0 0x04
#define ADSP_ALTVECSEL 0x0C
-#define ADSP_ALTVECSEL_C0 BIT(1)
+#define MT8188_ADSP_ALTVECSEL_C0 BIT(0)
+#define MT8186_ADSP_ALTVECSEL_C0 BIT(1)
+
+/*
+ * On MT8188, BIT(1) is not evaluated and on MT8186 BIT(0) is not evaluated:
+ * We can simplify the driver by safely setting both bits regardless of the SoC.
+ */
+#define ADSP_ALTVECSEL_C0 (MT8188_ADSP_ALTVECSEL_C0 | \
+ MT8186_ADSP_ALTVECSEL_C0)
/* dsp bus */
#define ADSP_SRAM_POOL_CON 0x190
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c
index 3c81e84fcecfa2..42bae574c87ab2 100644
--- a/sound/soc/sof/mediatek/mt8195/mt8195.c
+++ b/sound/soc/sof/mediatek/mt8195/mt8195.c
@@ -215,11 +215,6 @@ static int platform_parse_resource(struct platform_device *pdev, void *data)
adsp->pa_sram = (phys_addr_t)mmio->start;
adsp->sramsize = resource_size(mmio);
- if (adsp->sramsize < TOTAL_SIZE_SHARED_SRAM_FROM_TAIL) {
- dev_err(dev, "adsp SRAM(%#x) is not enough for share\n",
- adsp->sramsize);
- return -EINVAL;
- }
dev_dbg(dev, "sram pbase=%pa,%#x\n", &adsp->pa_sram, adsp->sramsize);
@@ -525,7 +520,7 @@ static snd_pcm_uframes_t mt8195_pcm_pointer(struct snd_sof_dev *sdev,
}
stream = &spcm->stream[substream->stream];
- ret = snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn));
+ ret = snd_sof_ipc_msg_data(sdev, stream, &posn, sizeof(posn));
if (ret < 0) {
dev_warn(sdev->dev, "failed to read stream position: %d\n", ret);
return 0;
@@ -642,6 +637,7 @@ static struct snd_sof_dsp_ops sof_mt8195_ops = {
/* Debug information */
.dbg_dump = mt8195_adsp_dump,
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
/* DAI drivers */
.drv = mt8195_dai,
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.h b/sound/soc/sof/mediatek/mt8195/mt8195.h
index 7ffd523f936c82..b4229170049fab 100644
--- a/sound/soc/sof/mediatek/mt8195/mt8195.h
+++ b/sound/soc/sof/mediatek/mt8195/mt8195.h
@@ -139,8 +139,6 @@ struct snd_sof_dev;
#define DSP_MBOX1_BAR 6
#define DSP_MBOX2_BAR 7
-#define TOTAL_SIZE_SHARED_SRAM_FROM_TAIL 0x0
-
#define SIZE_SHARED_DRAM_DL 0x40000 /*Shared buffer for Downlink*/
#define SIZE_SHARED_DRAM_UL 0x40000 /*Shared buffer for Uplink*/
diff --git a/sound/soc/sof/nocodec.c b/sound/soc/sof/nocodec.c
index 3537805070adad..b13bfdeb2b7062 100644
--- a/sound/soc/sof/nocodec.c
+++ b/sound/soc/sof/nocodec.c
@@ -103,14 +103,8 @@ static int sof_nocodec_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(&pdev->dev, card);
}
-static int sof_nocodec_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
static struct platform_driver sof_nocodec_audio = {
.probe = sof_nocodec_probe,
- .remove = sof_nocodec_remove,
.driver = {
.name = "sof-nocodec",
.pm = &snd_soc_pm_ops,
diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h
index 3b3f3cf7af38d5..9ab7b9be765bcb 100644
--- a/sound/soc/sof/ops.h
+++ b/sound/soc/sof/ops.h
@@ -472,19 +472,19 @@ static inline int snd_sof_load_firmware(struct snd_sof_dev *sdev)
/* host DSP message data */
static inline int snd_sof_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
void *p, size_t sz)
{
- return sof_ops(sdev)->ipc_msg_data(sdev, substream, p, sz);
+ return sof_ops(sdev)->ipc_msg_data(sdev, sps, p, sz);
}
/* host side configuration of the stream's data offset in stream mailbox area */
static inline int
snd_sof_set_stream_data_offset(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset)
{
if (sof_ops(sdev) && sof_ops(sdev)->set_stream_data_offset)
- return sof_ops(sdev)->set_stream_data_offset(sdev, substream,
+ return sof_ops(sdev)->set_stream_data_offset(sdev, sps,
posn_offset);
return 0;
@@ -511,6 +511,16 @@ static inline int snd_sof_pcm_platform_ack(struct snd_sof_dev *sdev,
return 0;
}
+static inline u64 snd_sof_pcm_get_stream_position(struct snd_sof_dev *sdev,
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ if (sof_ops(sdev) && sof_ops(sdev)->get_stream_position)
+ return sof_ops(sdev)->get_stream_position(sdev, component, substream);
+
+ return 0;
+}
+
/* machine driver */
static inline int
snd_sof_machine_register(struct snd_sof_dev *sdev, void *pdata)
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index 14571b821ecac5..445acb5c3a21bb 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -125,8 +125,8 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+ const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
struct snd_sof_platform_stream_params platform_params = { 0 };
- const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_sof_pcm *spcm;
int ret;
@@ -143,7 +143,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
* Handle repeated calls to hw_params() without free_pcm() in
* between. At least ALSA OSS emulation depends on this.
*/
- if (pcm_ops->hw_free && spcm->prepared[substream->stream]) {
+ if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) {
ret = pcm_ops->hw_free(component, substream);
if (ret < 0)
return ret;
@@ -177,7 +177,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
return ret;
}
- if (pcm_ops->hw_params) {
+ if (pcm_ops && pcm_ops->hw_params) {
ret = pcm_ops->hw_params(component, substream, params, &platform_params);
if (ret < 0)
return ret;
@@ -196,7 +196,7 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
+ const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
struct snd_sof_pcm *spcm;
int ret, err = 0;
@@ -212,7 +212,7 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
spcm->pcm.pcm_id, substream->stream);
/* free PCM in the DSP */
- if (pcm_ops->hw_free && spcm->prepared[substream->stream]) {
+ if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) {
ret = pcm_ops->hw_free(component, substream);
if (ret < 0)
err = ret;
@@ -279,10 +279,9 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
+ const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
struct snd_sof_pcm *spcm;
bool reset_hw_params = false;
- bool free_widget_list = false;
bool ipc_first = false;
int ret = 0;
@@ -326,7 +325,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
spcm->stream[substream->stream].suspend_ignored = true;
return 0;
}
- free_widget_list = true;
fallthrough;
case SNDRV_PCM_TRIGGER_STOP:
ipc_first = true;
@@ -344,7 +342,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
if (!ipc_first)
snd_sof_pcm_platform_trigger(sdev, substream, cmd);
- if (pcm_ops->trigger)
+ if (pcm_ops && pcm_ops->trigger)
ret = pcm_ops->trigger(component, substream, cmd);
/* need to STOP DMA even if trigger IPC failed */
@@ -353,8 +351,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
/* free PCM if reset_hw_params is set and the STOP IPC is successful */
if (!ret && reset_hw_params)
- ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream,
- free_widget_list);
+ ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, false);
return ret;
}
@@ -569,7 +566,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa
struct snd_sof_dai *dai =
snd_sof_find_dai(component, (char *)rtd->dai_link->name);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
+ const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
/* no topology exists for this BE, try a common configuration */
if (!dai) {
@@ -590,7 +587,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa
return 0;
}
- if (pcm_ops->dai_link_fixup)
+ if (pcm_ops && pcm_ops->dai_link_fixup)
return pcm_ops->dai_link_fixup(rtd, params);
return 0;
@@ -649,6 +646,18 @@ static int sof_pcm_ack(struct snd_soc_component *component,
return snd_sof_pcm_platform_ack(sdev, substream);
}
+static snd_pcm_sframes_t sof_pcm_delay(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
+ const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
+
+ if (pcm_ops && pcm_ops->delay)
+ return pcm_ops->delay(component, substream);
+
+ return 0;
+}
+
void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
{
struct snd_soc_component_driver *pd = &sdev->plat_drv;
@@ -673,6 +682,7 @@ void snd_sof_new_platform_drv(struct snd_sof_dev *sdev)
pd->trigger = sof_pcm_trigger;
pd->pointer = sof_pcm_pointer;
pd->ack = sof_pcm_ack;
+ pd->delay = sof_pcm_delay;
#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
pd->compress_ops = &sof_compressed_ops;
diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c
index 8722bbd7fd3d78..8d3383085d12c4 100644
--- a/sound/soc/sof/pm.c
+++ b/sound/soc/sof/pm.c
@@ -73,8 +73,8 @@ static void sof_cache_debugfs(struct snd_sof_dev *sdev)
static int sof_resume(struct device *dev, bool runtime_resume)
{
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
- const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
u32 old_state = sdev->dsp_power_state.state;
int ret;
@@ -155,7 +155,7 @@ static int sof_resume(struct device *dev, bool runtime_resume)
}
/* restore pipelines */
- if (tplg_ops->set_up_all_pipelines) {
+ if (tplg_ops && tplg_ops->set_up_all_pipelines) {
ret = tplg_ops->set_up_all_pipelines(sdev, false);
if (ret < 0) {
dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret);
@@ -179,8 +179,8 @@ static int sof_resume(struct device *dev, bool runtime_resume)
static int sof_suspend(struct device *dev, bool runtime_suspend)
{
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
- const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
pm_message_t pm_state;
u32 target_state = snd_sof_dsp_power_target(sdev);
int ret;
@@ -276,7 +276,7 @@ suspend:
int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev)
{
- const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm;
+ const struct sof_ipc_pm_ops *pm_ops = sof_ipc_get_ops(sdev, pm);
/* Notify DSP of upcoming power down */
if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save)
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 865c367eb2f2bf..760621bfc80284 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -16,21 +16,23 @@
static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget)
{
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_route *sroute;
list_for_each_entry(sroute, &sdev->route_list, list)
if (sroute->src_widget == widget || sroute->sink_widget == widget) {
- if (sroute->setup && tplg_ops->route_free)
+ if (sroute->setup && tplg_ops && tplg_ops->route_free)
tplg_ops->route_free(sdev, sroute);
sroute->setup = false;
}
}
-int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
+ struct snd_sof_widget *swidget)
{
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+ struct snd_sof_widget *pipe_widget;
int err = 0;
int ret;
@@ -43,11 +45,13 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (--swidget->use_count)
return 0;
+ pipe_widget = swidget->spipe->pipe_widget;
+
/* reset route setup status for all routes that contain this widget */
sof_reset_route_setup_status(sdev, swidget);
/* continue to disable core even if IPC fails */
- if (tplg_ops->widget_free)
+ if (tplg_ops && tplg_ops->widget_free)
err = tplg_ops->widget_free(sdev, swidget);
/*
@@ -67,22 +71,38 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
* skip for static pipelines
*/
if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
- ret = sof_widget_free(sdev, swidget->pipe_widget);
+ ret = sof_widget_free_unlocked(sdev, pipe_widget);
if (ret < 0 && !err)
err = ret;
- swidget->pipe_widget->complete = 0;
}
+ /* clear pipeline complete */
+ if (swidget->id == snd_soc_dapm_scheduler)
+ swidget->spipe->complete = 0;
+
if (!err)
dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
return err;
}
+
+int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+ int ret;
+
+ mutex_lock(&swidget->setup_mutex);
+ ret = sof_widget_free_unlocked(sdev, swidget);
+ mutex_unlock(&swidget->setup_mutex);
+
+ return ret;
+}
EXPORT_SYMBOL(sof_widget_free);
-int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev,
+ struct snd_sof_widget *swidget)
{
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+ bool use_count_decremented = false;
int ret;
/* skip if there is no private data */
@@ -103,14 +123,13 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
* widget in the pipeline is freed. Skip setting up scheduler widget for static pipelines.
*/
if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) {
- if (!swidget->pipe_widget) {
- dev_err(sdev->dev, "No scheduler widget set for %s\n",
- swidget->widget->name);
+ if (!swidget->spipe || !swidget->spipe->pipe_widget) {
+ dev_err(sdev->dev, "No pipeline set for %s\n", swidget->widget->name);
ret = -EINVAL;
goto use_count_dec;
}
- ret = sof_widget_setup(sdev, swidget->pipe_widget);
+ ret = sof_widget_setup_unlocked(sdev, swidget->spipe->pipe_widget);
if (ret < 0)
goto use_count_dec;
}
@@ -124,7 +143,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
}
/* setup widget in the DSP */
- if (tplg_ops->widget_setup) {
+ if (tplg_ops && tplg_ops->widget_setup) {
ret = tplg_ops->widget_setup(sdev, swidget);
if (ret < 0)
goto core_put;
@@ -134,7 +153,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
if (WIDGET_IS_DAI(swidget->id)) {
unsigned int flags = SOF_DAI_CONFIG_FLAGS_NONE;
- if (tplg_ops->dai_config) {
+ if (tplg_ops && tplg_ops->dai_config) {
ret = tplg_ops->dai_config(sdev, swidget, flags, NULL);
if (ret < 0)
goto widget_free;
@@ -142,7 +161,7 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
}
/* restore kcontrols for widget */
- if (tplg_ops->control->widget_kcontrol_setup) {
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->widget_kcontrol_setup) {
ret = tplg_ops->control->widget_kcontrol_setup(sdev, swidget);
if (ret < 0)
goto widget_free;
@@ -154,14 +173,28 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
widget_free:
/* widget use_count and core ref_count will both be decremented by sof_widget_free() */
- sof_widget_free(sdev, swidget);
+ sof_widget_free_unlocked(sdev, swidget);
+ use_count_decremented = true;
core_put:
snd_sof_dsp_core_put(sdev, swidget->core);
pipe_widget_free:
if (swidget->id != snd_soc_dapm_scheduler)
- sof_widget_free(sdev, swidget->pipe_widget);
+ sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);
use_count_dec:
- swidget->use_count--;
+ if (!use_count_decremented)
+ swidget->use_count--;
+
+ return ret;
+}
+
+int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
+{
+ int ret;
+
+ mutex_lock(&swidget->setup_mutex);
+ ret = sof_widget_setup_unlocked(sdev, swidget);
+ mutex_unlock(&swidget->setup_mutex);
+
return ret;
}
EXPORT_SYMBOL(sof_widget_setup);
@@ -169,12 +202,11 @@ EXPORT_SYMBOL(sof_widget_setup);
int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource,
struct snd_soc_dapm_widget *wsink)
{
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_widget *src_widget = wsource->dobj.private;
struct snd_sof_widget *sink_widget = wsink->dobj.private;
struct snd_sof_route *sroute;
bool route_found = false;
- int ret;
/* ignore routes involving virtual widgets in topology */
switch (src_widget->id) {
@@ -212,9 +244,12 @@ int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsourc
if (sroute->setup)
return 0;
- ret = ipc_tplg_ops->route_setup(sdev, sroute);
- if (ret < 0)
- return ret;
+ if (tplg_ops && tplg_ops->route_setup) {
+ int ret = tplg_ops->route_setup(sdev, sroute);
+
+ if (ret < 0)
+ return ret;
+ }
sroute->setup = true;
return 0;
@@ -239,24 +274,32 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
if (!widget->dobj.private)
continue;
- snd_soc_dapm_widget_for_each_sink_path(widget, p)
+ snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+ if (!widget_in_list(list, p->sink))
+ continue;
+
if (p->sink->dobj.private) {
ret = sof_route_setup(sdev, widget, p->sink);
if (ret < 0)
return ret;
}
+ }
}
} else {
for_each_dapm_widgets(list, i, widget) {
if (!widget->dobj.private)
continue;
- snd_soc_dapm_widget_for_each_source_path(widget, p)
+ snd_soc_dapm_widget_for_each_source_path(widget, p) {
+ if (!widget_in_list(list, p->source))
+ continue;
+
if (p->source->dobj.private) {
ret = sof_route_setup(sdev, p->source, widget);
if (ret < 0)
return ret;
}
+ }
}
}
@@ -264,18 +307,20 @@ static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev,
}
static void
-sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget)
+sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
+ struct snd_soc_dapm_widget_list *list)
{
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_widget *swidget = widget->dobj.private;
+ const struct sof_ipc_tplg_widget_ops *widget_ops;
struct snd_soc_dapm_path *p;
/* skip if the widget is in use or if it is already unprepared */
if (!swidget || !swidget->prepared || swidget->use_count > 0)
goto sink_unprepare;
- if (widget_ops[widget->id].ipc_unprepare)
+ widget_ops = tplg_ops ? tplg_ops->widget : NULL;
+ if (widget_ops && widget_ops[widget->id].ipc_unprepare)
/* unprepare the source widget */
widget_ops[widget->id].ipc_unprepare(swidget);
@@ -284,9 +329,11 @@ sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widg
sink_unprepare:
/* unprepare all widgets in the sink paths */
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+ if (!widget_in_list(list, p->sink))
+ continue;
if (!p->walking && p->sink->dobj.private) {
p->walking = true;
- sof_unprepare_widgets_in_path(sdev, p->sink);
+ sof_unprepare_widgets_in_path(sdev, p->sink, list);
p->walking = false;
}
}
@@ -296,14 +343,19 @@ static int
sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params,
- struct snd_pcm_hw_params *pipeline_params, int dir)
+ struct snd_pcm_hw_params *pipeline_params, int dir,
+ struct snd_soc_dapm_widget_list *list)
{
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_widget *swidget = widget->dobj.private;
+ const struct sof_ipc_tplg_widget_ops *widget_ops;
struct snd_soc_dapm_path *p;
int ret;
+ widget_ops = tplg_ops ? tplg_ops->widget : NULL;
+ if (!widget_ops)
+ return 0;
+
if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared)
goto sink_prepare;
@@ -320,10 +372,13 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget
sink_prepare:
/* prepare all widgets in the sink paths */
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
+ if (!widget_in_list(list, p->sink))
+ continue;
if (!p->walking && p->sink->dobj.private) {
p->walking = true;
ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params,
- platform_params, pipeline_params, dir);
+ platform_params, pipeline_params, dir,
+ list);
p->walking = false;
if (ret < 0) {
/* unprepare the source widget */
@@ -345,27 +400,28 @@ sink_prepare:
* (DAI type for capture, AIF type for playback)
*/
static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
- int dir)
+ int dir, struct snd_sof_pcm *spcm)
{
+ struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
struct snd_soc_dapm_path *p;
int err;
int ret = 0;
- /* free all widgets even in case of error to keep use counts balanced */
+ if (widget->dobj.private) {
+ err = sof_widget_free(sdev, widget->dobj.private);
+ if (err < 0)
+ ret = err;
+ }
+
+ /* free all widgets in the sink paths even in case of error to keep use counts balanced */
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
- if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
- p->walking = true;
- if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
- err = sof_widget_free(sdev, widget->dobj.private);
- if (err < 0)
- ret = err;
- }
+ if (!p->walking) {
+ if (!widget_in_list(list, p->sink))
+ continue;
- err = sof_widget_free(sdev, p->sink->dobj.private);
- if (err < 0)
- ret = err;
+ p->walking = true;
- err = sof_free_widgets_in_path(sdev, p->sink, dir);
+ err = sof_free_widgets_in_path(sdev, p->sink, dir, spcm);
if (err < 0)
ret = err;
p->walking = false;
@@ -381,37 +437,58 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap
* The error path in this function ensures that all successfully set up widgets getting freed.
*/
static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
- int dir)
+ int dir, struct snd_sof_pcm *spcm)
{
+ struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list;
+ struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
+ struct snd_sof_widget *swidget = widget->dobj.private;
+ struct snd_sof_pipeline *spipe;
struct snd_soc_dapm_path *p;
int ret;
+ if (swidget) {
+ int i;
+
+ ret = sof_widget_setup(sdev, widget->dobj.private);
+ if (ret < 0)
+ return ret;
+
+ /* skip populating the pipe_widgets array if it is NULL */
+ if (!pipeline_list->pipelines)
+ goto sink_setup;
+
+ /*
+ * Add the widget's pipe_widget to the list of pipelines to be triggered if not
+ * already in the list. This will result in the pipelines getting added in the
+ * order source to sink.
+ */
+ for (i = 0; i < pipeline_list->count; i++) {
+ spipe = pipeline_list->pipelines[i];
+ if (spipe == swidget->spipe)
+ break;
+ }
+
+ if (i == pipeline_list->count) {
+ pipeline_list->count++;
+ pipeline_list->pipelines[i] = swidget->spipe;
+ }
+ }
+
+sink_setup:
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
- if (!p->walking && p->sink->dobj.private && widget->dobj.private) {
- p->walking = true;
- if (WIDGET_IS_AIF_OR_DAI(widget->id)) {
- ret = sof_widget_setup(sdev, widget->dobj.private);
- if (ret < 0)
- goto out;
- }
+ if (!p->walking) {
+ if (!widget_in_list(list, p->sink))
+ continue;
- ret = sof_widget_setup(sdev, p->sink->dobj.private);
- if (ret < 0) {
- if (WIDGET_IS_AIF_OR_DAI(widget->id))
- sof_widget_free(sdev, widget->dobj.private);
- goto out;
- }
+ p->walking = true;
- ret = sof_set_up_widgets_in_path(sdev, p->sink, dir);
- if (ret < 0) {
- if (WIDGET_IS_AIF_OR_DAI(widget->id))
- sof_widget_free(sdev, widget->dobj.private);
- sof_widget_free(sdev, p->sink->dobj.private);
- }
-out:
+ ret = sof_set_up_widgets_in_path(sdev, p->sink, dir, spcm);
p->walking = false;
- if (ret < 0)
+ if (ret < 0) {
+ if (swidget)
+ sof_widget_free(sdev, swidget);
return ret;
+ }
}
}
@@ -419,16 +496,20 @@ out:
}
static int
-sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list,
+sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
struct snd_pcm_hw_params *fe_params,
struct snd_sof_platform_stream_params *platform_params, int dir,
enum sof_widget_op op)
{
+ struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
struct snd_soc_dapm_widget *widget;
char *str;
int ret = 0;
int i;
+ if (!list)
+ return 0;
+
for_each_dapm_widgets(list, i, widget) {
/* starting widget for playback is AIF type */
if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in)
@@ -440,11 +521,11 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_l
switch (op) {
case SOF_WIDGET_SETUP:
- ret = sof_set_up_widgets_in_path(sdev, widget, dir);
+ ret = sof_set_up_widgets_in_path(sdev, widget, dir, spcm);
str = "set up";
break;
case SOF_WIDGET_FREE:
- ret = sof_free_widgets_in_path(sdev, widget, dir);
+ ret = sof_free_widgets_in_path(sdev, widget, dir, spcm);
str = "free";
break;
case SOF_WIDGET_PREPARE:
@@ -460,12 +541,12 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_l
*/
memcpy(&pipeline_params, fe_params, sizeof(*fe_params));
- ret = sof_prepare_widgets_in_path(sdev, widget, fe_params,
- platform_params, &pipeline_params, dir);
+ ret = sof_prepare_widgets_in_path(sdev, widget, fe_params, platform_params,
+ &pipeline_params, dir, list);
break;
}
case SOF_WIDGET_UNPREPARE:
- sof_unprepare_widgets_in_path(sdev, widget);
+ sof_unprepare_widgets_in_path(sdev, widget, list);
break;
default:
dev_err(sdev->dev, "Invalid widget op %d\n", op);
@@ -485,7 +566,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
struct snd_sof_platform_stream_params *platform_params,
int dir)
{
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
struct snd_soc_dapm_widget *widget;
int i, ret;
@@ -498,16 +579,16 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
* Prepare widgets for set up. The prepare step is used to allocate memory, assign
* instance ID and pick the widget configuration based on the runtime PCM params.
*/
- ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
+ ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
dir, SOF_WIDGET_PREPARE);
if (ret < 0)
return ret;
/* Set up is used to send the IPC to the DSP to create the widget */
- ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
+ ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
dir, SOF_WIDGET_SETUP);
if (ret < 0) {
- ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params,
+ ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params,
dir, SOF_WIDGET_UNPREPARE);
return ret;
}
@@ -524,11 +605,20 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
for_each_dapm_widgets(list, i, widget) {
struct snd_sof_widget *swidget = widget->dobj.private;
struct snd_sof_widget *pipe_widget;
+ struct snd_sof_pipeline *spipe;
if (!swidget)
continue;
- pipe_widget = swidget->pipe_widget;
+ spipe = swidget->spipe;
+ if (!spipe) {
+ dev_err(sdev->dev, "no pipeline found for %s\n",
+ swidget->widget->name);
+ ret = -EINVAL;
+ goto widget_free;
+ }
+
+ pipe_widget = spipe->pipe_widget;
if (!pipe_widget) {
dev_err(sdev->dev, "error: no pipeline widget found for %s\n",
swidget->widget->name);
@@ -536,13 +626,13 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
goto widget_free;
}
- if (pipe_widget->complete)
+ if (spipe->complete)
continue;
- if (ipc_tplg_ops->pipeline_complete) {
- pipe_widget->complete = ipc_tplg_ops->pipeline_complete(sdev, pipe_widget);
- if (pipe_widget->complete < 0) {
- ret = pipe_widget->complete;
+ if (tplg_ops && tplg_ops->pipeline_complete) {
+ spipe->complete = tplg_ops->pipeline_complete(sdev, pipe_widget);
+ if (spipe->complete < 0) {
+ ret = spipe->complete;
goto widget_free;
}
}
@@ -551,15 +641,16 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
return 0;
widget_free:
- sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, dir,
+ sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, dir,
SOF_WIDGET_FREE);
- sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
+ sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
return ret;
}
int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir)
{
+ struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list;
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
int ret;
@@ -568,14 +659,16 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int
return 0;
/* send IPC to free widget in the DSP */
- ret = sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_FREE);
+ ret = sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_FREE);
/* unprepare the widget */
- sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
+ sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE);
snd_soc_dapm_dai_free_widgets(&list);
spcm->stream[dir].list = NULL;
+ pipeline_list->count = 0;
+
return ret;
}
@@ -628,11 +721,11 @@ bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)
int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
struct snd_sof_pcm *spcm, int dir, bool free_widget_list)
{
- const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
+ const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
int ret;
/* Send PCM_FREE IPC to reset pipeline */
- if (pcm_ops->hw_free && spcm->prepared[substream->stream]) {
+ if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) {
ret = pcm_ops->hw_free(sdev->component, substream);
if (ret < 0)
return ret;
@@ -762,13 +855,13 @@ static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type)
struct snd_sof_dai *dai =
snd_sof_find_dai(component, (char *)rtd->dai_link->name);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
/* use the tplg configured mclk if existed */
if (!dai)
return 0;
- if (tplg_ops->dai_get_clk)
+ if (tplg_ops && tplg_ops->dai_get_clk)
return tplg_ops->dai_get_clk(sdev, dai, clk_type);
return 0;
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 29cf951e3526f9..e0579af9d28154 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -42,6 +42,7 @@
#define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out)
#define WIDGET_IS_AIF(id) ((id) == snd_soc_dapm_aif_in || (id) == snd_soc_dapm_aif_out)
#define WIDGET_IS_AIF_OR_DAI(id) (WIDGET_IS_DAI(id) || WIDGET_IS_AIF(id))
+#define WIDGET_IS_COPIER(id) (WIDGET_IS_AIF_OR_DAI(id) || (id) == snd_soc_dapm_buffer)
#define SOF_DAI_CLK_INTEL_SSP_MCLK 0
#define SOF_DAI_CLK_INTEL_SSP_BCLK 1
@@ -85,6 +86,7 @@ struct snd_sof_widget;
struct snd_sof_route;
struct snd_sof_control;
struct snd_sof_dai;
+struct snd_sof_pcm;
struct snd_sof_dai_config_data {
int dai_index;
@@ -97,6 +99,11 @@ struct snd_sof_dai_config_data {
* @hw_free: Function pointer for hw_free
* @trigger: Function pointer for trigger
* @dai_link_fixup: Function pointer for DAI link fixup
+ * @pcm_setup: Function pointer for IPC-specific PCM set up that can be used for allocating
+ * additional memory in the SOF PCM stream structure
+ * @pcm_free: Function pointer for PCM free that can be used for freeing any
+ * additional memory in the SOF PCM stream structure
+ * @delay: Function pointer for pcm delay calculation
*/
struct sof_ipc_pcm_ops {
int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
@@ -106,6 +113,10 @@ struct sof_ipc_pcm_ops {
int (*trigger)(struct snd_soc_component *component, struct snd_pcm_substream *substream,
int cmd);
int (*dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params);
+ int (*pcm_setup)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm);
+ void (*pcm_free)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm);
+ snd_pcm_sframes_t (*delay)(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream);
};
/**
@@ -166,7 +177,7 @@ struct sof_ipc_tplg_widget_ops {
* initialized to 0.
* @control: Pointer to the IPC-specific ops for topology kcontrol IO
* @route_setup: Function pointer for setting up pipeline connections
- * @route_free: Optional op for freeing pipeline connections.
+ * @route_free: Function pointer for freeing pipeline connections.
* @token_list: List of all tokens supported by the IPC version. The size of the token_list
* array should be SOF_TOKEN_COUNT. The unused elements in the array will be
* initialized to 0.
@@ -179,7 +190,10 @@ struct sof_ipc_tplg_widget_ops {
* @dai_get_clk: Function pointer for getting the DAI clock setting
* @set_up_all_pipelines: Function pointer for setting up all topology pipelines
* @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines
- * @parse_manifest: Optional function pointer for ipc4 specific parsing of topology manifest
+ * @parse_manifest: Function pointer for ipc4 specific parsing of topology manifest
+ * @link_setup: Function pointer for IPC-specific DAI link set up
+ *
+ * Note: function pointers (ops) are optional
*/
struct sof_ipc_tplg_ops {
const struct sof_ipc_tplg_widget_ops *widget;
@@ -199,6 +213,7 @@ struct sof_ipc_tplg_ops {
int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify);
int (*parse_manifest)(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_manifest *man);
+ int (*link_setup)(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link);
};
/** struct snd_sof_tuple - Tuple info
@@ -274,6 +289,16 @@ struct sof_token_info {
int count;
};
+/**
+ * struct snd_sof_pcm_stream_pipeline_list - List of pipelines associated with a PCM stream
+ * @count: number of pipeline widgets in the @pipe_widgets array
+ * @pipelines: array of pipelines
+ */
+struct snd_sof_pcm_stream_pipeline_list {
+ u32 count;
+ struct snd_sof_pipeline **pipelines;
+};
+
/* PCM stream, mapped to FW component */
struct snd_sof_pcm_stream {
u32 comp_id;
@@ -289,6 +314,10 @@ struct snd_sof_pcm_stream {
* active or not while suspending the stream
*/
bool suspend_ignored;
+ struct snd_sof_pcm_stream_pipeline_list pipeline_list;
+
+ /* used by IPC implementation and core does not touch it */
+ void *private;
};
/* ALSA SOF PCM device */
@@ -361,16 +390,20 @@ struct snd_sof_widget {
int comp_id;
int pipeline_id;
/*
- * complete flag is used to indicate that pipeline set up is complete for scheduler type
- * widgets. It is unused for all other widget types.
- */
- int complete;
- /*
* the prepared flag is used to indicate that a widget has been prepared for getting set
* up in the DSP.
*/
bool prepared;
- int use_count; /* use_count will be protected by the PCM mutex held by the core */
+
+ struct mutex setup_mutex; /* to protect the swidget setup and free operations */
+
+ /*
+ * use_count is protected by the PCM mutex held by the core and the
+ * setup_mutex against non stream domain races (kcontrol access for
+ * example)
+ */
+ int use_count;
+
int core;
int id; /* id is the DAPM widget type */
/*
@@ -391,7 +424,7 @@ struct snd_sof_widget {
struct snd_soc_dapm_widget *widget;
struct list_head list; /* list in sdev widget list */
- struct snd_sof_widget *pipe_widget;
+ struct snd_sof_pipeline *spipe;
void *module_info;
const guid_t uuid;
@@ -429,6 +462,22 @@ struct snd_sof_widget {
void *private; /* core does not touch this */
};
+/** struct snd_sof_pipeline - ASoC SOF pipeline
+ * @pipe_widget: Pointer to the pipeline widget
+ * @started_count: Count of number of PCM's that have started this pipeline
+ * @paused_count: Count of number of PCM's that have started and have currently paused this
+ pipeline
+ * @complete: flag used to indicate that pipeline set up is complete.
+ * @list: List item in sdev pipeline_list
+ */
+struct snd_sof_pipeline {
+ struct snd_sof_widget *pipe_widget;
+ int started_count;
+ int paused_count;
+ int complete;
+ struct list_head list;
+};
+
/* ASoC SOF DAPM route */
struct snd_sof_route {
struct snd_soc_component *scomp;
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h
index 6760fd89595093..5f919162a55525 100644
--- a/sound/soc/sof/sof-priv.h
+++ b/sound/soc/sof/sof-priv.h
@@ -20,6 +20,8 @@
#include <uapi/sound/sof/fw.h>
#include <sound/sof/ext_manifest.h>
+struct snd_sof_pcm_stream;
+
/* Flag definitions used in sof_core_debug (sof_debug module parameter) */
#define SOF_DBG_ENABLE_TRACE BIT(0)
#define SOF_DBG_RETAIN_CTX BIT(1) /* prevent DSP D3 on FW exception */
@@ -113,6 +115,7 @@ struct sof_compr_stream {
u32 sampling_rate;
u16 channels;
u16 sample_container_bytes;
+ size_t posn_offset;
};
struct snd_sof_dev;
@@ -245,14 +248,23 @@ struct snd_sof_dsp_ops {
/* pcm ack */
int (*pcm_ack)(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream); /* optional */
+ /*
+ * optional callback to retrieve the link DMA position for the substream
+ * when the position is not reported in the shared SRAM windows but
+ * instead from a host-accessible hardware counter.
+ */
+ u64 (*get_stream_position)(struct snd_sof_dev *sdev,
+ struct snd_soc_component *component,
+ struct snd_pcm_substream *substream); /* optional */
+
/* host read DSP stream data */
int (*ipc_msg_data)(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
void *p, size_t sz); /* mandatory */
/* host side configuration of the stream's data offset in stream mailbox area */
int (*set_stream_data_offset)(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset); /* optional */
/* pre/post firmware run */
@@ -413,11 +425,13 @@ struct sof_ipc_fw_tracing_ops {
* @ctx_save: Optional function pointer for context save
* @ctx_restore: Optional function pointer for context restore
* @set_core_state: Optional function pointer for turning on/off a DSP core
+ * @set_pm_gate: Optional function pointer for pm gate settings
*/
struct sof_ipc_pm_ops {
int (*ctx_save)(struct snd_sof_dev *sdev);
int (*ctx_restore)(struct snd_sof_dev *sdev);
int (*set_core_state)(struct snd_sof_dev *sdev, int core_idx, bool on);
+ int (*set_pm_gate)(struct snd_sof_dev *sdev, u32 flags);
};
/**
@@ -444,7 +458,7 @@ struct sof_ipc_pcm_ops;
* @pm: Pointer to PM ops
* @pcm: Pointer to PCM ops
* @fw_loader: Pointer to Firmware Loader ops
- * @fw_tracing: Pointer to Firmware tracing ops
+ * @fw_tracing: Optional pointer to Firmware tracing ops
*
* @init: Optional pointer for IPC related initialization
* @exit: Optional pointer for IPC related cleanup
@@ -502,6 +516,10 @@ struct snd_sof_ipc {
const struct sof_ipc_ops *ops;
};
+/* Helper to retrieve the IPC ops */
+#define sof_ipc_get_ops(sdev, ops_name) \
+ (((sdev)->ipc && (sdev)->ipc->ops) ? (sdev)->ipc->ops->ops_name : NULL)
+
/*
* SOF Device Level.
*/
@@ -541,6 +559,7 @@ struct snd_sof_dev {
/* IPC */
struct snd_sof_ipc *ipc;
+ struct snd_sof_mailbox fw_info_box; /* FW shared memory */
struct snd_sof_mailbox dsp_box; /* DSP initiated IPC */
struct snd_sof_mailbox host_box; /* Host initiated IPC */
struct snd_sof_mailbox stream_box; /* Stream position update */
@@ -571,6 +590,7 @@ struct snd_sof_dev {
struct list_head pcm_list;
struct list_head kcontrol_list;
struct list_head widget_list;
+ struct list_head pipeline_list;
struct list_head dai_list;
struct list_head dai_link_list;
struct list_head route_list;
@@ -757,10 +777,10 @@ int sof_block_read(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type,
u32 offset, void *dest, size_t size);
int sof_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
void *p, size_t sz);
int sof_set_stream_data_offset(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset);
int sof_stream_pcm_open(struct snd_sof_dev *sdev,
diff --git a/sound/soc/sof/stream-ipc.c b/sound/soc/sof/stream-ipc.c
index 5f1ceeea893a53..216b454f6b94e3 100644
--- a/sound/soc/sof/stream-ipc.c
+++ b/sound/soc/sof/stream-ipc.c
@@ -19,6 +19,7 @@
#include "ops.h"
#include "sof-priv.h"
+#include "sof-audio.h"
struct sof_stream {
size_t posn_offset;
@@ -26,19 +27,33 @@ struct sof_stream {
/* Mailbox-based Generic IPC implementation */
int sof_ipc_msg_data(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
void *p, size_t sz)
{
- if (!substream || !sdev->stream_box.size) {
+ if (!sps || !sdev->stream_box.size) {
snd_sof_dsp_mailbox_read(sdev, sdev->dsp_box.offset, p, sz);
} else {
- struct sof_stream *stream = substream->runtime->private_data;
+ size_t posn_offset;
- /* The stream might already be closed */
- if (!stream)
- return -ESTRPIPE;
+ if (sps->substream) {
+ struct sof_stream *stream = sps->substream->runtime->private_data;
- snd_sof_dsp_mailbox_read(sdev, stream->posn_offset, p, sz);
+ /* The stream might already be closed */
+ if (!stream)
+ return -ESTRPIPE;
+
+ posn_offset = stream->posn_offset;
+ } else {
+
+ struct sof_compr_stream *sstream = sps->cstream->runtime->private_data;
+
+ if (!sstream)
+ return -ESTRPIPE;
+
+ posn_offset = sstream->posn_offset;
+ }
+
+ snd_sof_dsp_mailbox_read(sdev, posn_offset, p, sz);
}
return 0;
@@ -46,20 +61,32 @@ int sof_ipc_msg_data(struct snd_sof_dev *sdev,
EXPORT_SYMBOL(sof_ipc_msg_data);
int sof_set_stream_data_offset(struct snd_sof_dev *sdev,
- struct snd_pcm_substream *substream,
+ struct snd_sof_pcm_stream *sps,
size_t posn_offset)
{
- struct sof_stream *stream = substream->runtime->private_data;
-
/* check if offset is overflow or it is not aligned */
if (posn_offset > sdev->stream_box.size ||
posn_offset % sizeof(struct sof_ipc_stream_posn) != 0)
return -EINVAL;
- stream->posn_offset = sdev->stream_box.offset + posn_offset;
+ posn_offset += sdev->stream_box.offset;
+
+ if (sps->substream) {
+ struct sof_stream *stream = sps->substream->runtime->private_data;
+
+ stream->posn_offset = posn_offset;
+ dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu",
+ sps->substream->stream, posn_offset);
+ } else if (sps->cstream) {
+ struct sof_compr_stream *sstream = sps->cstream->runtime->private_data;
- dev_dbg(sdev->dev, "pcm: stream dir %d, posn mailbox offset is %zu",
- substream->stream, stream->posn_offset);
+ sstream->posn_offset = posn_offset;
+ dev_dbg(sdev->dev, "compr: stream dir %d, posn mailbox offset is %zu",
+ sps->cstream->direction, posn_offset);
+ } else {
+ dev_err(sdev->dev, "No stream opened");
+ return -EINVAL;
+ }
return 0;
}
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index c668bd9d21ec9b..4a62ccc71fcbff 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -54,11 +54,16 @@ int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum so
size_t object_size, int token_instance_num)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_token_info *token_list = ipc_tplg_ops->token_list;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+ const struct sof_token_info *token_list;
const struct sof_topology_token *tokens;
int i, j;
+ token_list = tplg_ops ? tplg_ops->token_list : NULL;
+ /* nothing to do if token_list is NULL */
+ if (!token_list)
+ return 0;
+
if (token_list[token_id].count < 0) {
dev_err(scomp->dev, "Invalid token count for token ID: %d\n", token_id);
return -EINVAL;
@@ -263,9 +268,9 @@ static int set_up_volume_table(struct snd_sof_control *scontrol,
{
struct snd_soc_component *scomp = scontrol->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (tplg_ops->control->set_up_volume_table)
+ if (tplg_ops && tplg_ops->control && tplg_ops->control->set_up_volume_table)
return tplg_ops->control->set_up_volume_table(scontrol, tlv, size);
dev_err(scomp->dev, "Mandatory op %s not set\n", __func__);
@@ -490,13 +495,14 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_
int array_size, u32 token_id, int token_instance_num,
struct snd_sof_tuple *tuples, int tuples_size, int *num_copied_tuples)
{
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_token_info *token_list = ipc_tplg_ops->token_list;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+ const struct sof_token_info *token_list;
const struct sof_topology_token *tokens;
int found = 0;
int num_tokens, asize;
int i, j;
+ token_list = tplg_ops ? tplg_ops->token_list : NULL;
/* nothing to do if token_list is NULL */
if (!token_list)
return 0;
@@ -1015,14 +1021,14 @@ static int sof_control_unload(struct snd_soc_component *scomp,
struct snd_soc_dobj *dobj)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_control *scontrol = dobj->private;
int ret = 0;
dev_dbg(scomp->dev, "tplg: unload control name : %s\n", scontrol->name);
- if (ipc_tplg_ops->control_free) {
- ret = ipc_tplg_ops->control_free(sdev, scontrol);
+ if (tplg_ops && tplg_ops->control_free) {
+ ret = tplg_ops->control_free(sdev, scontrol);
if (ret < 0)
dev_err(scomp->dev, "failed to free control: %s\n", scontrol->name);
}
@@ -1049,6 +1055,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp,
struct snd_soc_card *card = scomp->card;
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *cpu_dai;
+ int stream;
int i;
if (!w->sname) {
@@ -1056,62 +1063,41 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp,
return -EINVAL;
}
+ if (w->id == snd_soc_dapm_dai_out)
+ stream = SNDRV_PCM_STREAM_CAPTURE;
+ else if (w->id == snd_soc_dapm_dai_in)
+ stream = SNDRV_PCM_STREAM_PLAYBACK;
+ else
+ goto end;
+
list_for_each_entry(rtd, &card->rtd_list, list) {
/* does stream match DAI link ? */
if (!rtd->dai_link->stream_name ||
strcmp(w->sname, rtd->dai_link->stream_name))
continue;
- switch (w->id) {
- case snd_soc_dapm_dai_out:
- for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
- /*
- * Please create DAI widget in the right order
- * to ensure BE will connect to the right DAI
- * widget.
- */
- if (!cpu_dai->capture_widget) {
- cpu_dai->capture_widget = w;
- break;
- }
- }
- if (i == rtd->dai_link->num_cpus) {
- dev_err(scomp->dev, "error: can't find BE for DAI %s\n",
- w->name);
-
- return -EINVAL;
- }
- dai->name = rtd->dai_link->name;
- dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n",
- w->name, rtd->dai_link->name);
- break;
- case snd_soc_dapm_dai_in:
- for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
- /*
- * Please create DAI widget in the right order
- * to ensure BE will connect to the right DAI
- * widget.
- */
- if (!cpu_dai->playback_widget) {
- cpu_dai->playback_widget = w;
- break;
- }
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
+ /*
+ * Please create DAI widget in the right order
+ * to ensure BE will connect to the right DAI
+ * widget.
+ */
+ if (!snd_soc_dai_get_widget(cpu_dai, stream)) {
+ snd_soc_dai_set_widget(cpu_dai, stream, w);
+ break;
}
- if (i == rtd->dai_link->num_cpus) {
- dev_err(scomp->dev, "error: can't find BE for DAI %s\n",
- w->name);
+ }
+ if (i == rtd->dai_link->num_cpus) {
+ dev_err(scomp->dev, "error: can't find BE for DAI %s\n", w->name);
- return -EINVAL;
- }
- dai->name = rtd->dai_link->name;
- dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n",
- w->name, rtd->dai_link->name);
- break;
- default:
- break;
+ return -EINVAL;
}
- }
+ dai->name = rtd->dai_link->name;
+ dev_dbg(scomp->dev, "tplg: connected widget %s -> DAI link %s\n",
+ w->name, rtd->dai_link->name);
+ }
+end:
/* check we have a connection */
if (!dai->name) {
dev_err(scomp->dev, "error: can't connect DAI %s stream %s\n",
@@ -1128,37 +1114,29 @@ static void sof_disconnect_dai_widget(struct snd_soc_component *scomp,
struct snd_soc_card *card = scomp->card;
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *cpu_dai;
- int i;
+ int i, stream;
if (!w->sname)
return;
+ if (w->id == snd_soc_dapm_dai_out)
+ stream = SNDRV_PCM_STREAM_CAPTURE;
+ else if (w->id == snd_soc_dapm_dai_in)
+ stream = SNDRV_PCM_STREAM_PLAYBACK;
+ else
+ return;
+
list_for_each_entry(rtd, &card->rtd_list, list) {
/* does stream match DAI link ? */
if (!rtd->dai_link->stream_name ||
strcmp(w->sname, rtd->dai_link->stream_name))
continue;
- switch (w->id) {
- case snd_soc_dapm_dai_out:
- for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
- if (cpu_dai->capture_widget == w) {
- cpu_dai->capture_widget = NULL;
- break;
- }
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai)
+ if (snd_soc_dai_get_widget(cpu_dai, stream) == w) {
+ snd_soc_dai_set_widget(cpu_dai, stream, NULL);
+ break;
}
- break;
- case snd_soc_dapm_dai_in:
- for_each_rtd_cpu_dais(rtd, i, cpu_dai) {
- if (cpu_dai->playback_widget == w) {
- cpu_dai->playback_widget = NULL;
- break;
- }
- }
- break;
- default:
- break;
- }
}
}
@@ -1201,12 +1179,17 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s
enum sof_tokens *object_token_list, int count)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_token_info *token_list = ipc_tplg_ops->token_list;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_soc_tplg_private *private = &tw->priv;
+ const struct sof_token_info *token_list;
int num_tuples = 0;
int ret, i;
+ token_list = tplg_ops ? tplg_ops->token_list : NULL;
+ /* nothing to do if token_list is NULL */
+ if (!token_list)
+ return 0;
+
if (count > 0 && !object_token_list) {
dev_err(scomp->dev, "No token list for widget %s\n", swidget->widget->name);
return -EINVAL;
@@ -1375,13 +1358,13 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_dapm_widget *tw)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+ const struct sof_ipc_tplg_widget_ops *widget_ops;
struct snd_soc_tplg_private *priv = &tw->priv;
+ enum sof_tokens *token_list = NULL;
struct snd_sof_widget *swidget;
struct snd_sof_dai *dai;
- enum sof_tokens *token_list;
- int token_list_size;
+ int token_list_size = 0;
int ret = 0;
swidget = kzalloc(sizeof(*swidget), GFP_KERNEL);
@@ -1391,10 +1374,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
swidget->scomp = scomp;
swidget->widget = w;
swidget->comp_id = sdev->next_comp_id++;
- swidget->complete = 0;
swidget->id = w->id;
swidget->pipeline_id = index;
swidget->private = NULL;
+ mutex_init(&swidget->setup_mutex);
+
ida_init(&swidget->src_queue_ida);
ida_init(&swidget->sink_queue_ida);
@@ -1440,8 +1424,11 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
swidget->num_sink_pins, swidget->num_source_pins,
strnlen(w->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 ? w->sname : "none");
- token_list = widget_ops[w->id].token_list;
- token_list_size = widget_ops[w->id].token_list_size;
+ widget_ops = tplg_ops ? tplg_ops->widget : NULL;
+ if (widget_ops) {
+ token_list = widget_ops[w->id].token_list;
+ token_list_size = widget_ops[w->id].token_list_size;
+ }
/* handle any special case widgets */
switch (w->id) {
@@ -1525,7 +1512,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
/* bind widget to external event */
if (tw->event_type) {
- if (widget_ops[w->id].bind_event) {
+ if (widget_ops && widget_ops[w->id].bind_event) {
ret = widget_ops[w->id].bind_event(scomp, swidget,
le16_to_cpu(tw->event_type));
if (ret) {
@@ -1539,6 +1526,23 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
}
}
+ /* create and add pipeline for scheduler type widgets */
+ if (w->id == snd_soc_dapm_scheduler) {
+ struct snd_sof_pipeline *spipe;
+
+ spipe = kzalloc(sizeof(*spipe), GFP_KERNEL);
+ if (!spipe) {
+ kfree(swidget->private);
+ kfree(swidget->tuples);
+ kfree(swidget);
+ return -ENOMEM;
+ }
+
+ spipe->pipe_widget = swidget;
+ swidget->spipe = spipe;
+ list_add(&spipe->list, &sdev->pipeline_list);
+ }
+
w->dobj.private = swidget;
list_add(&swidget->list, &sdev->widget_list);
return ret;
@@ -1565,8 +1569,8 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
struct snd_soc_dobj *dobj)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
+ const struct sof_ipc_tplg_widget_ops *widget_ops;
const struct snd_kcontrol_new *kc;
struct snd_soc_dapm_widget *widget;
struct snd_sof_control *scontrol;
@@ -1594,6 +1598,15 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
sof_disconnect_dai_widget(scomp, widget);
break;
+ case snd_soc_dapm_scheduler:
+ {
+ struct snd_sof_pipeline *spipe = swidget->spipe;
+
+ list_del(&spipe->list);
+ kfree(spipe);
+ swidget->spipe = NULL;
+ break;
+ }
default:
break;
}
@@ -1626,7 +1639,8 @@ static int sof_widget_unload(struct snd_soc_component *scomp,
out:
/* free IPC related data */
- if (widget_ops[swidget->id].ipc_free)
+ widget_ops = tplg_ops ? tplg_ops->widget : NULL;
+ if (widget_ops && widget_ops[swidget->id].ipc_free)
widget_ops[swidget->id].ipc_free(swidget);
ida_destroy(&swidget->src_queue_ida);
@@ -1654,6 +1668,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm);
struct snd_soc_tplg_stream_caps *caps;
struct snd_soc_tplg_private *private = &pcm->priv;
struct snd_sof_pcm *spcm;
@@ -1681,6 +1696,13 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index,
spcm->pcm = *pcm;
dev_dbg(scomp->dev, "tplg: load pcm %s\n", pcm->dai_name);
+ /* perform pcm set op */
+ if (ipc_pcm_ops && ipc_pcm_ops->pcm_setup) {
+ ret = ipc_pcm_ops->pcm_setup(sdev, spcm);
+ if (ret < 0)
+ return ret;
+ }
+
dai_drv->dobj.private = spcm;
list_add(&spcm->list, &sdev->pcm_list);
@@ -1758,6 +1780,8 @@ free_playback_tables:
static int sof_dai_unload(struct snd_soc_component *scomp,
struct snd_soc_dobj *dobj)
{
+ struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm);
struct snd_sof_pcm *spcm = dobj->private;
/* free PCM DMA pages */
@@ -1767,6 +1791,10 @@ static int sof_dai_unload(struct snd_soc_component *scomp,
if (spcm->pcm.capture)
snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_CAPTURE].page_table);
+ /* perform pcm free op */
+ if (ipc_pcm_ops && ipc_pcm_ops->pcm_free)
+ ipc_pcm_ops->pcm_free(sdev, spcm);
+
/* remove from list and free spcm */
list_del(&spcm->list);
kfree(spcm);
@@ -1784,9 +1812,9 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
struct snd_soc_tplg_link_config *cfg)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_token_info *token_list = ipc_tplg_ops->token_list;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_soc_tplg_private *private = &cfg->priv;
+ const struct sof_token_info *token_list;
struct snd_sof_dai_link *slink;
u32 token_id = 0;
int num_tuples = 0;
@@ -1798,26 +1826,15 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
}
link->platforms->name = dev_name(scomp->dev);
- /*
- * Set nonatomic property for FE dai links as their trigger action
- * involves IPC's.
- */
+ if (tplg_ops && tplg_ops->link_setup) {
+ ret = tplg_ops->link_setup(sdev, link);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Set nonatomic property for FE dai links as their trigger action involves IPC's */
if (!link->no_pcm) {
link->nonatomic = true;
-
- /*
- * set default trigger order for all links. Exceptions to
- * the rule will be handled in sof_pcm_dai_link_fixup()
- * For playback, the sequence is the following: start FE,
- * start BE, stop BE, stop FE; for Capture the sequence is
- * inverted start BE, start FE, stop FE, stop BE
- */
- link->trigger[SNDRV_PCM_STREAM_PLAYBACK] =
- SND_SOC_DPCM_TRIGGER_PRE;
- link->trigger[SNDRV_PCM_STREAM_CAPTURE] =
- SND_SOC_DPCM_TRIGGER_POST;
-
- /* nothing more to do for FE dai links */
return 0;
}
@@ -1856,6 +1873,7 @@ static int sof_link_load(struct snd_soc_component *scomp, int index, struct snd_
return ret;
}
+ token_list = tplg_ops ? tplg_ops->token_list : NULL;
if (!token_list)
goto out;
@@ -2063,18 +2081,19 @@ err:
}
/**
- * sof_set_pipe_widget - Set pipe_widget for a component
+ * sof_set_widget_pipeline - Set pipeline for a component
* @sdev: pointer to struct snd_sof_dev
- * @pipe_widget: pointer to struct snd_sof_widget of type snd_soc_dapm_scheduler
+ * @spipe: pointer to struct snd_sof_pipeline
* @swidget: pointer to struct snd_sof_widget that has the same pipeline ID as @pipe_widget
*
* Return: 0 if successful, -EINVAL on error.
* The function checks if @swidget is associated with any volatile controls. If so, setting
* the dynamic_pipeline_widget is disallowed.
*/
-static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *pipe_widget,
- struct snd_sof_widget *swidget)
+static int sof_set_widget_pipeline(struct snd_sof_dev *sdev, struct snd_sof_pipeline *spipe,
+ struct snd_sof_widget *swidget)
{
+ struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
struct snd_sof_control *scontrol;
if (pipe_widget->dynamic_pipeline_widget) {
@@ -2089,8 +2108,8 @@ static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *
}
}
- /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
- swidget->pipe_widget = pipe_widget;
+ /* set the pipeline and apply the dynamic_pipeline_widget_flag */
+ swidget->spipe = spipe;
swidget->dynamic_pipeline_widget = pipe_widget->dynamic_pipeline_widget;
return 0;
@@ -2100,16 +2119,19 @@ static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *
static int sof_complete(struct snd_soc_component *scomp)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
struct snd_sof_widget *swidget, *comp_swidget;
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
- const struct sof_ipc_tplg_widget_ops *widget_ops = ipc_tplg_ops->widget;
+ const struct sof_ipc_tplg_widget_ops *widget_ops;
struct snd_sof_control *scontrol;
+ struct snd_sof_pipeline *spipe;
int ret;
+ widget_ops = tplg_ops ? tplg_ops->widget : NULL;
+
/* first update all control IPC structures based on the IPC version */
- if (ipc_tplg_ops->control_setup)
+ if (tplg_ops && tplg_ops->control_setup)
list_for_each_entry(scontrol, &sdev->kcontrol_list, list) {
- ret = ipc_tplg_ops->control_setup(sdev, scontrol);
+ ret = tplg_ops->control_setup(sdev, scontrol);
if (ret < 0) {
dev_err(sdev->dev, "failed updating IPC struct for control %s\n",
scontrol->name);
@@ -2123,7 +2145,7 @@ static int sof_complete(struct snd_soc_component *scomp)
* associated memories.
*/
list_for_each_entry(swidget, &sdev->widget_list, list) {
- if (widget_ops[swidget->id].ipc_setup) {
+ if (widget_ops && widget_ops[swidget->id].ipc_setup) {
ret = widget_ops[swidget->id].ipc_setup(swidget);
if (ret < 0) {
dev_err(sdev->dev, "failed updating IPC struct for %s\n",
@@ -2134,36 +2156,35 @@ static int sof_complete(struct snd_soc_component *scomp)
}
/* set the pipe_widget and apply the dynamic_pipeline_widget_flag */
- list_for_each_entry(swidget, &sdev->widget_list, list) {
- switch (swidget->id) {
- case snd_soc_dapm_scheduler:
- /*
- * Apply the dynamic_pipeline_widget flag and set the pipe_widget field
- * for all widgets that have the same pipeline ID as the scheduler widget
- */
- list_for_each_entry(comp_swidget, &sdev->widget_list, list)
- if (comp_swidget->pipeline_id == swidget->pipeline_id) {
- ret = sof_set_pipe_widget(sdev, swidget, comp_swidget);
- if (ret < 0)
- return ret;
- }
- break;
- default:
- break;
- }
+ list_for_each_entry(spipe, &sdev->pipeline_list, list) {
+ struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
+
+ /*
+ * Apply the dynamic_pipeline_widget flag and set the pipe_widget field
+ * for all widgets that have the same pipeline ID as the scheduler widget.
+ * Skip the scheduler widgets as they have their pipeline set during widget_ready
+ */
+ list_for_each_entry(comp_swidget, &sdev->widget_list, list)
+ if (comp_swidget->widget->id != snd_soc_dapm_scheduler &&
+ comp_swidget->pipeline_id == pipe_widget->pipeline_id) {
+ ret = sof_set_widget_pipeline(sdev, spipe, comp_swidget);
+ if (ret < 0)
+ return ret;
+ }
}
/* verify topology components loading including dynamic pipelines */
if (sof_debug_check_flag(SOF_DBG_VERIFY_TPLG)) {
- if (ipc_tplg_ops->set_up_all_pipelines && ipc_tplg_ops->tear_down_all_pipelines) {
- ret = ipc_tplg_ops->set_up_all_pipelines(sdev, true);
+ if (tplg_ops && tplg_ops->set_up_all_pipelines &&
+ tplg_ops->tear_down_all_pipelines) {
+ ret = tplg_ops->set_up_all_pipelines(sdev, true);
if (ret < 0) {
dev_err(sdev->dev, "Failed to set up all topology pipelines: %d\n",
ret);
return ret;
}
- ret = ipc_tplg_ops->tear_down_all_pipelines(sdev, true);
+ ret = tplg_ops->tear_down_all_pipelines(sdev, true);
if (ret < 0) {
dev_err(sdev->dev, "Failed to tear down topology pipelines: %d\n",
ret);
@@ -2173,8 +2194,8 @@ static int sof_complete(struct snd_soc_component *scomp)
}
/* set up static pipelines */
- if (ipc_tplg_ops->set_up_all_pipelines)
- return ipc_tplg_ops->set_up_all_pipelines(sdev, false);
+ if (tplg_ops && tplg_ops->set_up_all_pipelines)
+ return tplg_ops->set_up_all_pipelines(sdev, false);
return 0;
}
@@ -2184,10 +2205,10 @@ static int sof_manifest(struct snd_soc_component *scomp, int index,
struct snd_soc_tplg_manifest *man)
{
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg;
+ const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
- if (ipc_tplg_ops->parse_manifest)
- return ipc_tplg_ops->parse_manifest(scomp, index, man);
+ if (tplg_ops && tplg_ops->parse_manifest)
+ return tplg_ops->parse_manifest(scomp, index, man);
return 0;
}
diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c
index 6f662642d61159..b2ab51e5214a93 100644
--- a/sound/soc/sof/trace.c
+++ b/sound/soc/sof/trace.c
@@ -6,19 +6,21 @@
int sof_fw_trace_init(struct snd_sof_dev *sdev)
{
- if (!sdev->ipc->ops->fw_tracing) {
+ const struct sof_ipc_fw_tracing_ops *fw_tracing = sof_ipc_get_ops(sdev, fw_tracing);
+
+ if (!fw_tracing) {
dev_info(sdev->dev, "Firmware tracing is not available\n");
sdev->fw_trace_is_supported = false;
return 0;
}
- return sdev->ipc->ops->fw_tracing->init(sdev);
+ return fw_tracing->init(sdev);
}
void sof_fw_trace_free(struct snd_sof_dev *sdev)
{
- if (!sdev->fw_trace_is_supported || !sdev->ipc->ops->fw_tracing)
+ if (!sdev->fw_trace_is_supported)
return;
if (sdev->ipc->ops->fw_tracing->free)
diff --git a/sound/soc/spear/spdif_out.c b/sound/soc/spear/spdif_out.c
index fb107c5790addc..469373d1bb418e 100644
--- a/sound/soc/spear/spdif_out.c
+++ b/sound/soc/spear/spdif_out.c
@@ -244,7 +244,8 @@ static int spdif_soc_dai_probe(struct snd_soc_dai *dai)
struct spdif_out_dev *host = snd_soc_dai_get_drvdata(dai);
host->dma_params_tx.filter_data = &host->dma_params;
- dai->playback_dma_data = &host->dma_params_tx;
+
+ snd_soc_dai_dma_data_set_playback(dai, &host->dma_params_tx);
return snd_soc_add_dai_controls(dai, spdif_out_controls,
ARRAY_SIZE(spdif_out_controls));
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index b6712a3d1fa115..73a683d4552647 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -144,7 +144,7 @@ config SND_SOC_TEGRA210_SFC
Config to enable the Sampling Frequency Converter (SFC) which
converts the sampling frequency of input signal to another
frequency. It supports sampling frequency conversion of streams
- upto 2 channels (stereo).
+ up to 2 channels (stereo).
Say Y or M if you want to add support for Tegra210 SFC module.
config SND_SOC_TEGRA210_AMX
@@ -171,7 +171,7 @@ config SND_SOC_TEGRA210_MIXER
tristate "Tegra210 Mixer module"
help
Config to enable the Mixer module which can help to mix multiple
- audio streams. It supports mixing of upto 10 input streams,
+ audio streams. It supports mixing of up to 10 input streams,
where each stream can contain maximum of 8 channels. It supports
5 output each of which can be a mix of any combination of 10
input streams.
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
index 87facfbcdd110d..d23d88a1089961 100644
--- a/sound/soc/tegra/tegra20_ac97.c
+++ b/sound/soc/tegra/tegra20_ac97.c
@@ -212,8 +212,8 @@ static int tegra20_ac97_probe(struct snd_soc_dai *dai)
{
struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai);
- dai->capture_dma_data = &ac97->capture_dma_data;
- dai->playback_dma_data = &ac97->playback_dma_data;
+ snd_soc_dai_init_dma_data(dai, &ac97->playback_dma_data,
+ &ac97->capture_dma_data);
return 0;
}
diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c
index fff0cd6588f567..d37a9f2603e818 100644
--- a/sound/soc/tegra/tegra20_i2s.c
+++ b/sound/soc/tegra/tegra20_i2s.c
@@ -256,8 +256,8 @@ static int tegra20_i2s_probe(struct snd_soc_dai *dai)
{
struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- dai->capture_dma_data = &i2s->capture_dma_data;
- dai->playback_dma_data = &i2s->playback_dma_data;
+ snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
+ &i2s->capture_dma_data);
return 0;
}
diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c
index ca7b222e07d05e..86bef54dfdf2e3 100644
--- a/sound/soc/tegra/tegra20_spdif.c
+++ b/sound/soc/tegra/tegra20_spdif.c
@@ -236,8 +236,7 @@ static int tegra20_spdif_probe(struct snd_soc_dai *dai)
{
struct tegra20_spdif *spdif = dev_get_drvdata(dai->dev);
- dai->capture_dma_data = NULL;
- dai->playback_dma_data = &spdif->playback_dma_data;
+ snd_soc_dai_init_dma_data(dai, &spdif->playback_dma_data, NULL);
return 0;
}
diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c
index 1a2e868a622093..100a2b6e6063e4 100644
--- a/sound/soc/tegra/tegra210_admaif.c
+++ b/sound/soc/tegra/tegra210_admaif.c
@@ -544,8 +544,8 @@ static int tegra_admaif_dai_probe(struct snd_soc_dai *dai)
{
struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai);
- dai->capture_dma_data = &admaif->capture_dma_data[dai->id];
- dai->playback_dma_data = &admaif->playback_dma_data[dai->id];
+ snd_soc_dai_init_dma_data(dai, &admaif->playback_dma_data[dai->id],
+ &admaif->capture_dma_data[dai->id]);
return 0;
}
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index 10cd37096fb334..c26f960c6afde9 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -297,8 +297,8 @@ static int tegra30_i2s_probe(struct snd_soc_dai *dai)
{
struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
- dai->capture_dma_data = &i2s->capture_dma_data;
- dai->playback_dma_data = &i2s->playback_dma_data;
+ snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
+ &i2s->capture_dma_data);
return 0;
}
diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c
index e6e77a5f3c1e7d..3ccd0cfca008f9 100644
--- a/sound/soc/ti/davinci-i2s.c
+++ b/sound/soc/ti/davinci-i2s.c
@@ -614,9 +614,10 @@ static const struct snd_soc_dai_ops davinci_i2s_dai_ops = {
static int davinci_i2s_dai_probe(struct snd_soc_dai *dai)
{
struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
+ int stream;
- dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
- dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
+ for_each_pcm_streams(stream)
+ snd_soc_dai_dma_data_set(dai, stream, &dev->dma_data[stream]);
return 0;
}
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c
index f5ac2ab77f5b0d..578254549d2d6d 100644
--- a/sound/soc/ti/davinci-mcasp.c
+++ b/sound/soc/ti/davinci-mcasp.c
@@ -1699,9 +1699,10 @@ static void davinci_mcasp_init_iec958_status(struct davinci_mcasp *mcasp)
static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai)
{
struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+ int stream;
- dai->playback_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
- dai->capture_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE];
+ for_each_pcm_streams(stream)
+ snd_soc_dai_dma_data_set(dai, stream, &mcasp->dma_data[stream]);
if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) {
davinci_mcasp_init_iec958_status(mcasp);
diff --git a/sound/soc/ti/davinci-vcif.c b/sound/soc/ti/davinci-vcif.c
index 36fa97e2b9e225..fc16b3b8f71a17 100644
--- a/sound/soc/ti/davinci-vcif.c
+++ b/sound/soc/ti/davinci-vcif.c
@@ -161,9 +161,10 @@ static const struct snd_soc_dai_ops davinci_vcif_dai_ops = {
static int davinci_vcif_dai_probe(struct snd_soc_dai *dai)
{
struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai);
+ int stream;
- dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
- dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE];
+ for_each_pcm_streams(stream)
+ snd_soc_dai_dma_data_set(dai, stream, &dev->dma_data[stream]);
return 0;
}
diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c
index fdd55d772b8e2d..325e75e961369e 100644
--- a/sound/soc/ux500/mop500.c
+++ b/sound/soc/ux500/mop500.c
@@ -109,11 +109,9 @@ static int mop500_probe(struct platform_device *pdev)
mop500_card.dev = &pdev->dev;
- if (np) {
- ret = mop500_of_probe(pdev, np);
- if (ret)
- return ret;
- }
+ ret = mop500_of_probe(pdev, np);
+ if (ret)
+ return ret;
dev_dbg(&pdev->dev, "%s: Card %s: Set platform drvdata.\n",
__func__, mop500_card.name);
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
index 9d99ea6d7f30ec..6fb1a5c207bc7d 100644
--- a/sound/soc/ux500/ux500_msp_dai.c
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -15,7 +15,6 @@
#include <linux/of.h>
#include <linux/regulator/consumer.h>
#include <linux/mfd/dbx500-prcmu.h>
-#include <linux/platform_data/asoc-ux500-msp.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
@@ -671,8 +670,8 @@ static int ux500_msp_dai_of_probe(struct snd_soc_dai *dai)
if (!capture_dma_data)
return -ENOMEM;
- playback_dma_data->addr = drvdata->msp->playback_dma_data.tx_rx_addr;
- capture_dma_data->addr = drvdata->msp->capture_dma_data.tx_rx_addr;
+ playback_dma_data->addr = drvdata->msp->tx_rx_addr;
+ capture_dma_data->addr = drvdata->msp->tx_rx_addr;
playback_dma_data->maxburst = 4;
capture_dma_data->maxburst = 4;
@@ -682,26 +681,6 @@ static int ux500_msp_dai_of_probe(struct snd_soc_dai *dai)
return 0;
}
-static int ux500_msp_dai_probe(struct snd_soc_dai *dai)
-{
- struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
- struct msp_i2s_platform_data *pdata = dai->dev->platform_data;
- int ret;
-
- if (!pdata) {
- ret = ux500_msp_dai_of_probe(dai);
- return ret;
- }
-
- drvdata->msp->playback_dma_data.data_size = drvdata->slot_width;
- drvdata->msp->capture_dma_data.data_size = drvdata->slot_width;
-
- snd_soc_dai_init_dma_data(dai,
- &drvdata->msp->playback_dma_data,
- &drvdata->msp->capture_dma_data);
- return 0;
-}
-
static const struct snd_soc_dai_ops ux500_msp_dai_ops[] = {
{
.set_sysclk = ux500_msp_dai_set_dai_sysclk,
@@ -716,7 +695,7 @@ static const struct snd_soc_dai_ops ux500_msp_dai_ops[] = {
};
static struct snd_soc_dai_driver ux500_msp_dai_drv = {
- .probe = ux500_msp_dai_probe,
+ .probe = ux500_msp_dai_of_probe,
.playback.channels_min = UX500_MSP_MIN_CHANNELS,
.playback.channels_max = UX500_MSP_MAX_CHANNELS,
.playback.rates = UX500_I2S_RATES,
@@ -737,15 +716,8 @@ static const struct snd_soc_component_driver ux500_msp_component = {
static int ux500_msp_drv_probe(struct platform_device *pdev)
{
struct ux500_msp_i2s_drvdata *drvdata;
- struct msp_i2s_platform_data *pdata = pdev->dev.platform_data;
- struct device_node *np = pdev->dev.of_node;
int ret = 0;
- if (!pdata && !np) {
- dev_err(&pdev->dev, "No platform data or Device Tree found\n");
- return -ENODEV;
- }
-
drvdata = devm_kzalloc(&pdev->dev,
sizeof(struct ux500_msp_i2s_drvdata),
GFP_KERNEL);
@@ -787,8 +759,7 @@ static int ux500_msp_drv_probe(struct platform_device *pdev)
return ret;
}
- ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp,
- pdev->dev.platform_data);
+ ret = ux500_msp_i2s_init_msp(pdev, &drvdata->msp);
if (!drvdata->msp) {
dev_err(&pdev->dev,
"%s: ERROR: Failed to init MSP-struct (%d)!",
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c
index d113411a19f822..fbfeefa418ca70 100644
--- a/sound/soc/ux500/ux500_msp_i2s.c
+++ b/sound/soc/ux500/ux500_msp_i2s.c
@@ -14,7 +14,6 @@
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/of.h>
-#include <linux/platform_data/asoc-ux500-msp.h>
#include <sound/soc.h>
@@ -361,20 +360,6 @@ static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config)
__func__, status);
}
- /* Make sure the correct DMA-directions are configured */
- if ((config->direction & MSP_DIR_RX) &&
- !msp->capture_dma_data.dma_cfg) {
- dev_err(msp->dev, "%s: ERROR: MSP RX-mode is not configured!",
- __func__);
- return -EINVAL;
- }
- if ((config->direction == MSP_DIR_TX) &&
- !msp->playback_dma_data.dma_cfg) {
- dev_err(msp->dev, "%s: ERROR: MSP TX-mode is not configured!",
- __func__);
- return -EINVAL;
- }
-
reg_val_DMACR = readl(msp->registers + MSP_DMACR);
if (config->direction & MSP_DIR_RX)
reg_val_DMACR |= RX_DMA_ENABLE;
@@ -639,62 +624,17 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)
}
-static int ux500_msp_i2s_of_init_msp(struct platform_device *pdev,
- struct ux500_msp *msp,
- struct msp_i2s_platform_data **platform_data)
-{
- struct msp_i2s_platform_data *pdata;
-
- *platform_data = devm_kzalloc(&pdev->dev,
- sizeof(struct msp_i2s_platform_data),
- GFP_KERNEL);
- pdata = *platform_data;
- if (!pdata)
- return -ENOMEM;
-
- msp->playback_dma_data.dma_cfg = devm_kzalloc(&pdev->dev,
- sizeof(struct stedma40_chan_cfg),
- GFP_KERNEL);
- if (!msp->playback_dma_data.dma_cfg)
- return -ENOMEM;
-
- msp->capture_dma_data.dma_cfg = devm_kzalloc(&pdev->dev,
- sizeof(struct stedma40_chan_cfg),
- GFP_KERNEL);
- if (!msp->capture_dma_data.dma_cfg)
- return -ENOMEM;
-
- return 0;
-}
-
int ux500_msp_i2s_init_msp(struct platform_device *pdev,
- struct ux500_msp **msp_p,
- struct msp_i2s_platform_data *platform_data)
+ struct ux500_msp **msp_p)
{
struct resource *res = NULL;
- struct device_node *np = pdev->dev.of_node;
struct ux500_msp *msp;
- int ret;
*msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL);
msp = *msp_p;
if (!msp)
return -ENOMEM;
- if (!platform_data) {
- if (np) {
- ret = ux500_msp_i2s_of_init_msp(pdev, msp,
- &platform_data);
- if (ret)
- return ret;
- } else
- return -EINVAL;
- } else {
- msp->playback_dma_data.dma_cfg = platform_data->msp_i2s_dma_tx;
- msp->capture_dma_data.dma_cfg = platform_data->msp_i2s_dma_rx;
- msp->id = platform_data->id;
- }
-
msp->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -704,9 +644,7 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
return -ENOMEM;
}
- msp->playback_dma_data.tx_rx_addr = res->start + MSP_DR;
- msp->capture_dma_data.tx_rx_addr = res->start + MSP_DR;
-
+ msp->tx_rx_addr = res->start + MSP_DR;
msp->registers = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (msp->registers == NULL) {
diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h
index d45b5e2831cc03..69d4ebc409fc12 100644
--- a/sound/soc/ux500/ux500_msp_i2s.h
+++ b/sound/soc/ux500/ux500_msp_i2s.h
@@ -11,7 +11,6 @@
#define UX500_MSP_I2S_H
#include <linux/platform_device.h>
-#include <linux/platform_data/asoc-ux500-msp.h>
#define MSP_INPUT_FREQ_APB 48000000
@@ -463,18 +462,11 @@ struct ux500_msp_config {
unsigned int iodelay;
};
-struct ux500_msp_dma_params {
- unsigned int data_size;
- dma_addr_t tx_rx_addr;
- struct stedma40_chan_cfg *dma_cfg;
-};
-
struct ux500_msp {
int id;
void __iomem *registers;
struct device *dev;
- struct ux500_msp_dma_params playback_dma_data;
- struct ux500_msp_dma_params capture_dma_data;
+ dma_addr_t tx_rx_addr;
enum msp_state msp_state;
int def_elem_len;
unsigned int dir_busy;
@@ -482,10 +474,8 @@ struct ux500_msp {
unsigned int f_bitclk;
};
-struct msp_i2s_platform_data;
int ux500_msp_i2s_init_msp(struct platform_device *pdev,
- struct ux500_msp **msp_p,
- struct msp_i2s_platform_data *platform_data);
+ struct ux500_msp **msp_p);
void ux500_msp_i2s_cleanup_msp(struct platform_device *pdev,
struct ux500_msp *msp);
int ux500_msp_i2s_open(struct ux500_msp *msp, struct ux500_msp_config *config);
diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c
index d3802e5ef196e0..53b5649cfddae0 100644
--- a/sound/soc/ux500/ux500_pcm.c
+++ b/sound/soc/ux500/ux500_pcm.c
@@ -13,7 +13,6 @@
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/slab.h>
-#include <linux/platform_data/dma-ste-dma40.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -29,76 +28,17 @@
#define UX500_PLATFORM_PERIODS_MAX 48
#define UX500_PLATFORM_BUFFER_BYTES_MAX (2048 * PAGE_SIZE)
-static const struct snd_pcm_hardware ux500_pcm_hw = {
- .info = SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_RESUME |
- SNDRV_PCM_INFO_PAUSE,
- .buffer_bytes_max = UX500_PLATFORM_BUFFER_BYTES_MAX,
- .period_bytes_min = UX500_PLATFORM_PERIODS_BYTES_MIN,
- .period_bytes_max = UX500_PLATFORM_PERIODS_BYTES_MAX,
- .periods_min = UX500_PLATFORM_PERIODS_MIN,
- .periods_max = UX500_PLATFORM_PERIODS_MAX,
-};
-
-static struct dma_chan *ux500_pcm_request_chan(struct snd_soc_pcm_runtime *rtd,
- struct snd_pcm_substream *substream)
-{
- struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
- u16 per_data_width, mem_data_width;
- struct stedma40_chan_cfg *dma_cfg;
- struct ux500_msp_dma_params *dma_params;
-
- dma_params = snd_soc_dai_get_dma_data(dai, substream);
- dma_cfg = dma_params->dma_cfg;
-
- mem_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
-
- switch (dma_params->data_size) {
- case 32:
- per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- break;
- case 16:
- per_data_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
- break;
- case 8:
- per_data_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- break;
- default:
- per_data_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
- }
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- dma_cfg->src_info.data_width = mem_data_width;
- dma_cfg->dst_info.data_width = per_data_width;
- } else {
- dma_cfg->src_info.data_width = per_data_width;
- dma_cfg->dst_info.data_width = mem_data_width;
- }
-
- return snd_dmaengine_pcm_request_channel(stedma40_filter, dma_cfg);
-}
-
static int ux500_pcm_prepare_slave_config(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct dma_slave_config *slave_config)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct msp_i2s_platform_data *pdata = asoc_rtd_to_cpu(rtd, 0)->dev->platform_data;
struct snd_dmaengine_dai_dma_data *snd_dma_params;
- struct ux500_msp_dma_params *ste_dma_params;
dma_addr_t dma_addr;
int ret;
- if (pdata) {
- ste_dma_params =
- snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
- dma_addr = ste_dma_params->tx_rx_addr;
- } else {
- snd_dma_params =
- snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
- dma_addr = snd_dma_params->addr;
- }
+ snd_dma_params = snd_soc_dai_get_dma_data(asoc_rtd_to_cpu(rtd, 0), substream);
+ dma_addr = snd_dma_params->addr;
ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
if (ret)
@@ -118,31 +58,16 @@ static int ux500_pcm_prepare_slave_config(struct snd_pcm_substream *substream,
return 0;
}
-static const struct snd_dmaengine_pcm_config ux500_dmaengine_pcm_config = {
- .pcm_hardware = &ux500_pcm_hw,
- .compat_request_channel = ux500_pcm_request_chan,
- .prealloc_buffer_size = 128 * 1024,
- .prepare_slave_config = ux500_pcm_prepare_slave_config,
-};
-
static const struct snd_dmaengine_pcm_config ux500_dmaengine_of_pcm_config = {
- .compat_request_channel = ux500_pcm_request_chan,
.prepare_slave_config = ux500_pcm_prepare_slave_config,
};
int ux500_pcm_register_platform(struct platform_device *pdev)
{
- const struct snd_dmaengine_pcm_config *pcm_config;
- struct device_node *np = pdev->dev.of_node;
int ret;
- if (np)
- pcm_config = &ux500_dmaengine_of_pcm_config;
- else
- pcm_config = &ux500_dmaengine_pcm_config;
-
- ret = snd_dmaengine_pcm_register(&pdev->dev, pcm_config,
- SND_DMAENGINE_PCM_FLAG_COMPAT);
+ ret = snd_dmaengine_pcm_register(&pdev->dev,
+ &ux500_dmaengine_of_pcm_config, 0);
if (ret < 0) {
dev_err(&pdev->dev,
"%s: ERROR: Failed to register platform '%s' (%d)!\n",
diff --git a/sound/xen/xen_snd_front.c b/sound/xen/xen_snd_front.c
index 4041748c12e515..b66e037710d0df 100644
--- a/sound/xen/xen_snd_front.c
+++ b/sound/xen/xen_snd_front.c
@@ -311,7 +311,7 @@ static int xen_drv_probe(struct xenbus_device *xb_dev,
return xenbus_switch_state(xb_dev, XenbusStateInitialising);
}
-static int xen_drv_remove(struct xenbus_device *dev)
+static void xen_drv_remove(struct xenbus_device *dev)
{
struct xen_snd_front_info *front_info = dev_get_drvdata(&dev->dev);
int to = 100;
@@ -345,7 +345,6 @@ static int xen_drv_remove(struct xenbus_device *dev)
xen_snd_drv_fini(front_info);
xenbus_frontend_closed(dev);
- return 0;
}
static const struct xenbus_device_id xen_drv_ids[] = {
diff --git a/tools/arch/arm64/include/asm/cputype.h b/tools/arch/arm64/include/asm/cputype.h
index abc418650fec04..683ca3af408485 100644
--- a/tools/arch/arm64/include/asm/cputype.h
+++ b/tools/arch/arm64/include/asm/cputype.h
@@ -41,7 +41,7 @@
(((midr) & MIDR_IMPLEMENTOR_MASK) >> MIDR_IMPLEMENTOR_SHIFT)
#define MIDR_CPU_MODEL(imp, partnum) \
- (((imp) << MIDR_IMPLEMENTOR_SHIFT) | \
+ ((_AT(u32, imp) << MIDR_IMPLEMENTOR_SHIFT) | \
(0xf << MIDR_ARCHITECTURE_SHIFT) | \
((partnum) << MIDR_PARTNUM_SHIFT))
@@ -80,6 +80,7 @@
#define ARM_CPU_PART_CORTEX_X1 0xD44
#define ARM_CPU_PART_CORTEX_A510 0xD46
#define ARM_CPU_PART_CORTEX_A710 0xD47
+#define ARM_CPU_PART_CORTEX_A715 0xD4D
#define ARM_CPU_PART_CORTEX_X2 0xD48
#define ARM_CPU_PART_NEOVERSE_N2 0xD49
#define ARM_CPU_PART_CORTEX_A78C 0xD4B
@@ -123,6 +124,8 @@
#define APPLE_CPU_PART_M1_FIRESTORM_PRO 0x025
#define APPLE_CPU_PART_M1_ICESTORM_MAX 0x028
#define APPLE_CPU_PART_M1_FIRESTORM_MAX 0x029
+#define APPLE_CPU_PART_M2_BLIZZARD 0x032
+#define APPLE_CPU_PART_M2_AVALANCHE 0x033
#define AMPERE_CPU_PART_AMPERE1 0xAC3
@@ -142,6 +145,7 @@
#define MIDR_CORTEX_X1 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X1)
#define MIDR_CORTEX_A510 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A510)
#define MIDR_CORTEX_A710 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A710)
+#define MIDR_CORTEX_A715 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A715)
#define MIDR_CORTEX_X2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_X2)
#define MIDR_NEOVERSE_N2 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N2)
#define MIDR_CORTEX_A78C MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A78C)
@@ -175,6 +179,8 @@
#define MIDR_APPLE_M1_FIRESTORM_PRO MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_FIRESTORM_PRO)
#define MIDR_APPLE_M1_ICESTORM_MAX MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_ICESTORM_MAX)
#define MIDR_APPLE_M1_FIRESTORM_MAX MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M1_FIRESTORM_MAX)
+#define MIDR_APPLE_M2_BLIZZARD MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_BLIZZARD)
+#define MIDR_APPLE_M2_AVALANCHE MIDR_CPU_MODEL(ARM_CPU_IMP_APPLE, APPLE_CPU_PART_M2_AVALANCHE)
#define MIDR_AMPERE1 MIDR_CPU_MODEL(ARM_CPU_IMP_AMPERE, AMPERE_CPU_PART_AMPERE1)
/* Fujitsu Erratum 010001 affects A64FX 1.0 and 1.1, (v0r0 and v1r0) */
diff --git a/tools/arch/arm64/include/uapi/asm/kvm.h b/tools/arch/arm64/include/uapi/asm/kvm.h
index 316917b9870704..a7a857f1784d80 100644
--- a/tools/arch/arm64/include/uapi/asm/kvm.h
+++ b/tools/arch/arm64/include/uapi/asm/kvm.h
@@ -43,6 +43,7 @@
#define __KVM_HAVE_VCPU_EVENTS
#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
+#define KVM_DIRTY_LOG_PAGE_OFFSET 64
#define KVM_REG_SIZE(id) \
(1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT))
diff --git a/tools/arch/x86/include/uapi/asm/kvm.h b/tools/arch/x86/include/uapi/asm/kvm.h
index 649e50a8f9ddff..e48deab8901d4e 100644
--- a/tools/arch/x86/include/uapi/asm/kvm.h
+++ b/tools/arch/x86/include/uapi/asm/kvm.h
@@ -206,6 +206,8 @@ struct kvm_msr_list {
struct kvm_msr_filter_range {
#define KVM_MSR_FILTER_READ (1 << 0)
#define KVM_MSR_FILTER_WRITE (1 << 1)
+#define KVM_MSR_FILTER_RANGE_VALID_MASK (KVM_MSR_FILTER_READ | \
+ KVM_MSR_FILTER_WRITE)
__u32 flags;
__u32 nmsrs; /* number of msrs in bitmap */
__u32 base; /* MSR index the bitmap starts at */
@@ -214,8 +216,11 @@ struct kvm_msr_filter_range {
#define KVM_MSR_FILTER_MAX_RANGES 16
struct kvm_msr_filter {
+#ifndef __KERNEL__
#define KVM_MSR_FILTER_DEFAULT_ALLOW (0 << 0)
+#endif
#define KVM_MSR_FILTER_DEFAULT_DENY (1 << 0)
+#define KVM_MSR_FILTER_VALID_MASK (KVM_MSR_FILTER_DEFAULT_DENY)
__u32 flags;
struct kvm_msr_filter_range ranges[KVM_MSR_FILTER_MAX_RANGES];
};
diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile
index 787b857d3fb5fb..f610e184ce02ab 100644
--- a/tools/bpf/bpftool/Makefile
+++ b/tools/bpf/bpftool/Makefile
@@ -145,6 +145,10 @@ ifeq ($(feature-llvm),1)
LLVM_CONFIG_LIB_COMPONENTS := mcdisassembler all-targets
CFLAGS += $(shell $(LLVM_CONFIG) --cflags --libs $(LLVM_CONFIG_LIB_COMPONENTS))
LIBS += $(shell $(LLVM_CONFIG) --libs $(LLVM_CONFIG_LIB_COMPONENTS))
+ ifeq ($(shell $(LLVM_CONFIG) --shared-mode),static)
+ LIBS += $(shell $(LLVM_CONFIG) --system-libs $(LLVM_CONFIG_LIB_COMPONENTS))
+ LIBS += -lstdc++
+ endif
LDFLAGS += $(shell $(LLVM_CONFIG) --ldflags)
else
# Fall back on libbfd
diff --git a/tools/gpio/gpio-event-mon.c b/tools/gpio/gpio-event-mon.c
index 6c122952c5892f..5dee2b98ab6043 100644
--- a/tools/gpio/gpio-event-mon.c
+++ b/tools/gpio/gpio-event-mon.c
@@ -86,6 +86,7 @@ int monitor_device(const char *device_name,
gpiotools_test_bit(values.bits, i));
}
+ i = 0;
while (1) {
struct gpio_v2_line_event event;
diff --git a/tools/include/linux/build_bug.h b/tools/include/linux/build_bug.h
index cc7070c7439ba6..b4898ff085ded5 100644
--- a/tools/include/linux/build_bug.h
+++ b/tools/include/linux/build_bug.h
@@ -79,4 +79,13 @@
#define __static_assert(expr, msg, ...) _Static_assert(expr, msg)
#endif // static_assert
+
+/*
+ * Compile time check that field has an expected offset
+ */
+#define ASSERT_STRUCT_OFFSET(type, field, expected_offset) \
+ BUILD_BUG_ON_MSG(offsetof(type, field) != (expected_offset), \
+ "Offset of " #field " in " #type " has changed.")
+
+
#endif /* _LINUX_BUILD_BUG_H */
diff --git a/tools/include/nolibc/arch-mips.h b/tools/include/nolibc/arch-mips.h
index 5fc5b8029bff9e..7380093ba9e7df 100644
--- a/tools/include/nolibc/arch-mips.h
+++ b/tools/include/nolibc/arch-mips.h
@@ -192,6 +192,7 @@ struct sys_stat_struct {
__asm__ (".section .text\n"
".weak __start\n"
".set nomips16\n"
+ ".set push\n"
".set noreorder\n"
".option pic0\n"
".ent __start\n"
@@ -210,6 +211,7 @@ __asm__ (".section .text\n"
"li $v0, 4001\n" // NR_exit == 4001
"syscall\n"
".end __start\n"
+ ".set pop\n"
"");
#endif // _NOLIBC_ARCH_MIPS_H
diff --git a/tools/include/nolibc/arch-riscv.h b/tools/include/nolibc/arch-riscv.h
index ba04771cb3a341..a3bdd9803f8cb3 100644
--- a/tools/include/nolibc/arch-riscv.h
+++ b/tools/include/nolibc/arch-riscv.h
@@ -11,13 +11,13 @@
#define O_RDONLY 0
#define O_WRONLY 1
#define O_RDWR 2
-#define O_CREAT 0x100
-#define O_EXCL 0x200
-#define O_NOCTTY 0x400
-#define O_TRUNC 0x1000
-#define O_APPEND 0x2000
-#define O_NONBLOCK 0x4000
-#define O_DIRECTORY 0x200000
+#define O_CREAT 0x40
+#define O_EXCL 0x80
+#define O_NOCTTY 0x100
+#define O_TRUNC 0x200
+#define O_APPEND 0x400
+#define O_NONBLOCK 0x800
+#define O_DIRECTORY 0x10000
struct sys_stat_struct {
unsigned long st_dev; /* Device. */
diff --git a/tools/include/nolibc/ctype.h b/tools/include/nolibc/ctype.h
index e3000b2992d7b9..6f90706d06442d 100644
--- a/tools/include/nolibc/ctype.h
+++ b/tools/include/nolibc/ctype.h
@@ -96,4 +96,7 @@ int ispunct(int c)
return isgraph(c) && !isalnum(c);
}
+/* make sure to include all global symbols */
+#include "nolibc.h"
+
#endif /* _NOLIBC_CTYPE_H */
diff --git a/tools/include/nolibc/errno.h b/tools/include/nolibc/errno.h
index 06893d6dfb7a6b..9dc4919c769b7d 100644
--- a/tools/include/nolibc/errno.h
+++ b/tools/include/nolibc/errno.h
@@ -24,4 +24,7 @@ static int errno;
*/
#define MAX_ERRNO 4095
+/* make sure to include all global symbols */
+#include "nolibc.h"
+
#endif /* _NOLIBC_ERRNO_H */
diff --git a/tools/include/nolibc/signal.h b/tools/include/nolibc/signal.h
index ef47e71e2be376..137552216e4696 100644
--- a/tools/include/nolibc/signal.h
+++ b/tools/include/nolibc/signal.h
@@ -19,4 +19,7 @@ int raise(int signal)
return sys_kill(sys_getpid(), signal);
}
+/* make sure to include all global symbols */
+#include "nolibc.h"
+
#endif /* _NOLIBC_SIGNAL_H */
diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h
index a3cebc4bc3ac4d..96ac8afc5aeedc 100644
--- a/tools/include/nolibc/stdio.h
+++ b/tools/include/nolibc/stdio.h
@@ -303,4 +303,7 @@ void perror(const char *msg)
fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);
}
+/* make sure to include all global symbols */
+#include "nolibc.h"
+
#endif /* _NOLIBC_STDIO_H */
diff --git a/tools/include/nolibc/stdlib.h b/tools/include/nolibc/stdlib.h
index 92378c4b966051..a24000d1e8222c 100644
--- a/tools/include/nolibc/stdlib.h
+++ b/tools/include/nolibc/stdlib.h
@@ -419,4 +419,7 @@ char *u64toa(uint64_t in)
return itoa_buffer;
}
+/* make sure to include all global symbols */
+#include "nolibc.h"
+
#endif /* _NOLIBC_STDLIB_H */
diff --git a/tools/include/nolibc/string.h b/tools/include/nolibc/string.h
index ad97c0d522b8e4..fffdaf6ff46731 100644
--- a/tools/include/nolibc/string.h
+++ b/tools/include/nolibc/string.h
@@ -88,8 +88,11 @@ void *memset(void *dst, int b, size_t len)
{
char *p = dst;
- while (len--)
+ while (len--) {
+ /* prevent gcc from recognizing memset() here */
+ asm volatile("");
*(p++) = b;
+ }
return dst;
}
@@ -285,4 +288,7 @@ char *strrchr(const char *s, int c)
return (char *)ret;
}
+/* make sure to include all global symbols */
+#include "nolibc.h"
+
#endif /* _NOLIBC_STRING_H */
diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h
index ce3ee03aa6794b..78473d34e27cd6 100644
--- a/tools/include/nolibc/sys.h
+++ b/tools/include/nolibc/sys.h
@@ -1243,5 +1243,7 @@ ssize_t write(int fd, const void *buf, size_t count)
return ret;
}
+/* make sure to include all global symbols */
+#include "nolibc.h"
#endif /* _NOLIBC_SYS_H */
diff --git a/tools/include/nolibc/time.h b/tools/include/nolibc/time.h
index d18b7661fdd713..84655361b9ad2c 100644
--- a/tools/include/nolibc/time.h
+++ b/tools/include/nolibc/time.h
@@ -25,4 +25,7 @@ time_t time(time_t *tptr)
return tv.tv_sec;
}
+/* make sure to include all global symbols */
+#include "nolibc.h"
+
#endif /* _NOLIBC_TIME_H */
diff --git a/tools/include/nolibc/types.h b/tools/include/nolibc/types.h
index 959997034e553a..fbbc0e68c001b0 100644
--- a/tools/include/nolibc/types.h
+++ b/tools/include/nolibc/types.h
@@ -26,13 +26,13 @@
#define S_IFSOCK 0140000
#define S_IFMT 0170000
-#define S_ISDIR(mode) (((mode) & S_IFDIR) == S_IFDIR)
-#define S_ISCHR(mode) (((mode) & S_IFCHR) == S_IFCHR)
-#define S_ISBLK(mode) (((mode) & S_IFBLK) == S_IFBLK)
-#define S_ISREG(mode) (((mode) & S_IFREG) == S_IFREG)
-#define S_ISFIFO(mode) (((mode) & S_IFIFO) == S_IFIFO)
-#define S_ISLNK(mode) (((mode) & S_IFLNK) == S_IFLNK)
-#define S_ISSOCK(mode) (((mode) & S_IFSOCK) == S_IFSOCK)
+#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR)
+#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK)
+#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
+#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
+#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
+#define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK)
/* dirent types */
#define DT_UNKNOWN 0x0
@@ -89,39 +89,46 @@
#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1
+#define FD_SETIDXMASK (8 * sizeof(unsigned long))
+#define FD_SETBITMASK (8 * sizeof(unsigned long)-1)
+
/* for select() */
typedef struct {
- uint32_t fd32[(FD_SETSIZE + 31) / 32];
+ unsigned long fds[(FD_SETSIZE + FD_SETBITMASK) / FD_SETIDXMASK];
} fd_set;
-#define FD_CLR(fd, set) do { \
- fd_set *__set = (set); \
- int __fd = (fd); \
- if (__fd >= 0) \
- __set->fd32[__fd / 32] &= ~(1U << (__fd & 31)); \
+#define FD_CLR(fd, set) do { \
+ fd_set *__set = (set); \
+ int __fd = (fd); \
+ if (__fd >= 0) \
+ __set->fds[__fd / FD_SETIDXMASK] &= \
+ ~(1U << (__fd & FX_SETBITMASK)); \
} while (0)
-#define FD_SET(fd, set) do { \
- fd_set *__set = (set); \
- int __fd = (fd); \
- if (__fd >= 0) \
- __set->fd32[__fd / 32] |= 1U << (__fd & 31); \
+#define FD_SET(fd, set) do { \
+ fd_set *__set = (set); \
+ int __fd = (fd); \
+ if (__fd >= 0) \
+ __set->fds[__fd / FD_SETIDXMASK] |= \
+ 1 << (__fd & FD_SETBITMASK); \
} while (0)
-#define FD_ISSET(fd, set) ({ \
- fd_set *__set = (set); \
- int __fd = (fd); \
- int __r = 0; \
- if (__fd >= 0) \
- __r = !!(__set->fd32[__fd / 32] & 1U << (__fd & 31)); \
- __r; \
+#define FD_ISSET(fd, set) ({ \
+ fd_set *__set = (set); \
+ int __fd = (fd); \
+ int __r = 0; \
+ if (__fd >= 0) \
+ __r = !!(__set->fds[__fd / FD_SETIDXMASK] & \
+1U << (__fd & FD_SET_BITMASK)); \
+ __r; \
})
-#define FD_ZERO(set) do { \
- fd_set *__set = (set); \
- int __idx; \
- for (__idx = 0; __idx < (FD_SETSIZE+31) / 32; __idx ++) \
- __set->fd32[__idx] = 0; \
+#define FD_ZERO(set) do { \
+ fd_set *__set = (set); \
+ int __idx; \
+ int __size = (FD_SETSIZE+FD_SETBITMASK) / FD_SETIDXMASK;\
+ for (__idx = 0; __idx < __size; __idx++) \
+ __set->fds[__idx] = 0; \
} while (0)
/* for poll() */
@@ -202,4 +209,7 @@ struct stat {
})
#endif
+/* make sure to include all global symbols */
+#include "nolibc.h"
+
#endif /* _NOLIBC_TYPES_H */
diff --git a/tools/include/nolibc/unistd.h b/tools/include/nolibc/unistd.h
index 1c25e20ee36066..1cfcd52106a420 100644
--- a/tools/include/nolibc/unistd.h
+++ b/tools/include/nolibc/unistd.h
@@ -51,4 +51,7 @@ int tcsetpgrp(int fd, pid_t pid)
return ioctl(fd, TIOCSPGRP, &pid);
}
+/* make sure to include all global symbols */
+#include "nolibc.h"
+
#endif /* _NOLIBC_UNISTD_H */
diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h
index 20522d4ba1e0d8..55155e262646e5 100644
--- a/tools/include/uapi/linux/kvm.h
+++ b/tools/include/uapi/linux/kvm.h
@@ -1767,6 +1767,7 @@ struct kvm_xen_hvm_attr {
__u8 runstate_update_flag;
struct {
__u64 gfn;
+#define KVM_XEN_INVALID_GFN ((__u64)-1)
} shared_info;
struct {
__u32 send_port;
@@ -1798,6 +1799,7 @@ struct kvm_xen_hvm_attr {
} u;
};
+
/* Available with KVM_CAP_XEN_HVM / KVM_XEN_HVM_CONFIG_SHARED_INFO */
#define KVM_XEN_ATTR_TYPE_LONG_MODE 0x0
#define KVM_XEN_ATTR_TYPE_SHARED_INFO 0x1
@@ -1823,6 +1825,7 @@ struct kvm_xen_vcpu_attr {
__u16 pad[3];
union {
__u64 gpa;
+#define KVM_XEN_INVALID_GPA ((__u64)-1)
__u64 pad[8];
struct {
__u64 state;
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 4350be739f4fac..4b7c8b33069e51 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -427,6 +427,15 @@ static int decode_instructions(struct objtool_file *file)
if (func->type != STT_NOTYPE && func->type != STT_FUNC)
continue;
+ if (func->offset == sec->sh.sh_size) {
+ /* Heuristic: likely an "end" symbol */
+ if (func->type == STT_NOTYPE)
+ continue;
+ WARN("%s(): STT_FUNC at end of section",
+ func->name);
+ return -1;
+ }
+
if (func->return_thunk || func->alias != func)
continue;
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile
index 6e7b88917ca0a1..ba5d942e4c6aab 100644
--- a/tools/perf/Documentation/Makefile
+++ b/tools/perf/Documentation/Makefile
@@ -267,7 +267,7 @@ $(OUTPUT)%.xml : %.txt
$(ASCIIDOC) -b docbook -d manpage \
$(ASCIIDOC_EXTRA) -aperf_version=$(PERF_VERSION) \
-aperf_date=$(shell git log -1 --pretty="format:%cd" \
- --date=short $<) \
+ --date=short --no-show-signature $<) \
-o $@+ $< && \
mv $@+ $@
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index c2504c39bdcb89..5b87846759036f 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -589,6 +589,8 @@ ifndef NO_LIBELF
$(call feature_check,libbpf-bpf_program__set_insns)
ifeq ($(feature-libbpf-bpf_program__set_insns), 1)
CFLAGS += -DHAVE_LIBBPF_BPF_PROGRAM__SET_INSNS
+ else
+ dummy := $(error Error: libbpf devel library needs to be >= 0.8.0 to build with LIBBPF_DYNAMIC, update or build statically with the version that comes with the kernel sources);
endif
$(call feature_check,libbpf-btf__raw_data)
ifeq ($(feature-libbpf-btf__raw_data), 1)
@@ -602,6 +604,8 @@ ifndef NO_LIBELF
dummy := $(error Error: No libbpf devel library found, please install libbpf-devel);
endif
else
+ # Libbpf will be built as a static library from tools/lib/bpf.
+ LIBBPF_STATIC := 1
CFLAGS += -DHAVE_LIBBPF_BTF__LOAD_FROM_KERNEL_BY_ID
CFLAGS += -DHAVE_LIBBPF_BPF_PROG_LOAD
CFLAGS += -DHAVE_LIBBPF_BPF_OBJECT__NEXT_PROGRAM
@@ -1314,14 +1318,6 @@ tip_instdir_SQ = $(subst ','\'',$(tip_instdir))
export perfexec_instdir_SQ
-# If we install to $(HOME) we keep the traceevent default:
-# $(HOME)/.traceevent/plugins
-# Otherwise we install plugins into the global $(libdir).
-ifdef DESTDIR
-plugindir=$(libdir)/traceevent/plugins
-plugindir_SQ= $(subst ','\'',$(plugindir))
-endif
-
print_var = $(eval $(print_var_code)) $(info $(MSG))
define print_var_code
MSG = $(shell printf '...%40s: %s' $(1) $($(1)))
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf
index 13e7d26e77f043..b7d9c42062300d 100644
--- a/tools/perf/Makefile.perf
+++ b/tools/perf/Makefile.perf
@@ -303,10 +303,12 @@ ifneq ($(OUTPUT),)
else
LIBBPF_OUTPUT = $(CURDIR)/libbpf
endif
-LIBBPF_DESTDIR = $(LIBBPF_OUTPUT)
-LIBBPF_INCLUDE = $(LIBBPF_DESTDIR)/include
-LIBBPF = $(LIBBPF_OUTPUT)/libbpf.a
-CFLAGS += -I$(LIBBPF_OUTPUT)/include
+ifdef LIBBPF_STATIC
+ LIBBPF_DESTDIR = $(LIBBPF_OUTPUT)
+ LIBBPF_INCLUDE = $(LIBBPF_DESTDIR)/include
+ LIBBPF = $(LIBBPF_OUTPUT)/libbpf.a
+ CFLAGS += -I$(LIBBPF_OUTPUT)/include
+endif
ifneq ($(OUTPUT),)
LIBSUBCMD_OUTPUT = $(abspath $(OUTPUT))/libsubcmd
@@ -393,10 +395,8 @@ endif
export PERL_PATH
PERFLIBS = $(LIBAPI) $(LIBPERF) $(LIBSUBCMD) $(LIBSYMBOL)
-ifndef NO_LIBBPF
- ifndef LIBBPF_DYNAMIC
- PERFLIBS += $(LIBBPF)
- endif
+ifdef LIBBPF_STATIC
+ PERFLIBS += $(LIBBPF)
endif
# We choose to avoid "if .. else if .. else .. endif endif"
@@ -756,12 +756,15 @@ prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h archheaders $(drm_ioc
$(arch_errno_name_array) \
$(sync_file_range_arrays) \
$(LIBAPI) \
- $(LIBBPF) \
$(LIBPERF) \
$(LIBSUBCMD) \
$(LIBSYMBOL) \
bpf-skel
+ifdef LIBBPF_STATIC
+prepare: $(LIBBPF)
+endif
+
$(OUTPUT)%.o: %.c prepare FORCE
$(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=$(build-dir) $@
@@ -819,7 +822,7 @@ $(patsubst perf-%,%.o,$(PROGRAMS)): $(wildcard */*.h)
$(LIBAPI): FORCE | $(LIBAPI_OUTPUT)
$(Q)$(MAKE) -C $(LIBAPI_DIR) O=$(LIBAPI_OUTPUT) \
- DESTDIR=$(LIBAPI_DESTDIR) prefix= \
+ DESTDIR=$(LIBAPI_DESTDIR) prefix= subdir= \
$@ install_headers
$(LIBAPI)-clean:
@@ -828,7 +831,7 @@ $(LIBAPI)-clean:
$(LIBBPF): FORCE | $(LIBBPF_OUTPUT)
$(Q)$(MAKE) -C $(LIBBPF_DIR) FEATURES_DUMP=$(FEATURE_DUMP_EXPORT) \
- O= OUTPUT=$(LIBBPF_OUTPUT)/ DESTDIR=$(LIBBPF_DESTDIR) prefix= \
+ O= OUTPUT=$(LIBBPF_OUTPUT)/ DESTDIR=$(LIBBPF_DESTDIR) prefix= subdir= \
$@ install_headers
$(LIBBPF)-clean:
@@ -837,7 +840,7 @@ $(LIBBPF)-clean:
$(LIBPERF): FORCE | $(LIBPERF_OUTPUT)
$(Q)$(MAKE) -C $(LIBPERF_DIR) O=$(LIBPERF_OUTPUT) \
- DESTDIR=$(LIBPERF_DESTDIR) prefix= \
+ DESTDIR=$(LIBPERF_DESTDIR) prefix= subdir= \
$@ install_headers
$(LIBPERF)-clean:
@@ -846,7 +849,7 @@ $(LIBPERF)-clean:
$(LIBSUBCMD): FORCE | $(LIBSUBCMD_OUTPUT)
$(Q)$(MAKE) -C $(LIBSUBCMD_DIR) O=$(LIBSUBCMD_OUTPUT) \
- DESTDIR=$(LIBSUBCMD_DESTDIR) prefix= \
+ DESTDIR=$(LIBSUBCMD_DESTDIR) prefix= subdir= \
$@ install_headers
$(LIBSUBCMD)-clean:
@@ -855,7 +858,7 @@ $(LIBSUBCMD)-clean:
$(LIBSYMBOL): FORCE | $(LIBSYMBOL_OUTPUT)
$(Q)$(MAKE) -C $(LIBSYMBOL_DIR) O=$(LIBSYMBOL_OUTPUT) \
- DESTDIR=$(LIBSYMBOL_DESTDIR) prefix= \
+ DESTDIR=$(LIBSYMBOL_DESTDIR) prefix= subdir= \
$@ install_headers
$(LIBSYMBOL)-clean:
diff --git a/tools/perf/arch/riscv/util/unwind-libdw.c b/tools/perf/arch/riscv/util/unwind-libdw.c
index 19536e17285071..54a198714eb82b 100644
--- a/tools/perf/arch/riscv/util/unwind-libdw.c
+++ b/tools/perf/arch/riscv/util/unwind-libdw.c
@@ -4,7 +4,7 @@
#include <elfutils/libdwfl.h>
#include "../../util/unwind-libdw.h"
#include "../../util/perf_regs.h"
-#include "../../util/event.h"
+#include "../../util/sample.h"
bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
{
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index e20656c431a4bb..8ae0a153529362 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -26,6 +26,7 @@
#include "util/string2.h"
#include <linux/kernel.h>
+#include <linux/numa.h>
#include <linux/rbtree.h>
#include <linux/string.h>
#include <linux/zalloc.h>
@@ -185,22 +186,33 @@ static int evsel__process_alloc_event(struct evsel *evsel, struct perf_sample *s
total_allocated += bytes_alloc;
nr_allocs++;
- return 0;
-}
-static int evsel__process_alloc_node_event(struct evsel *evsel, struct perf_sample *sample)
-{
- int ret = evsel__process_alloc_event(evsel, sample);
+ /*
+ * Commit 11e9734bcb6a ("mm/slab_common: unify NUMA and UMA
+ * version of tracepoints") adds the field "node" into the
+ * tracepoints 'kmalloc' and 'kmem_cache_alloc'.
+ *
+ * The legacy tracepoints 'kmalloc_node' and 'kmem_cache_alloc_node'
+ * also contain the field "node".
+ *
+ * If the tracepoint contains the field "node" the tool stats the
+ * cross allocation.
+ */
+ if (evsel__field(evsel, "node")) {
+ int node1, node2;
- if (!ret) {
- int node1 = cpu__get_node((struct perf_cpu){.cpu = sample->cpu}),
- node2 = evsel__intval(evsel, sample, "node");
+ node1 = cpu__get_node((struct perf_cpu){.cpu = sample->cpu});
+ node2 = evsel__intval(evsel, sample, "node");
- if (node1 != node2)
+ /*
+ * If the field "node" is NUMA_NO_NODE (-1), we don't take it
+ * as a cross allocation.
+ */
+ if ((node2 != NUMA_NO_NODE) && (node1 != node2))
nr_cross_allocs++;
}
- return ret;
+ return 0;
}
static int ptr_cmp(void *, void *);
@@ -1369,8 +1381,8 @@ static int __cmd_kmem(struct perf_session *session)
/* slab allocator */
{ "kmem:kmalloc", evsel__process_alloc_event, },
{ "kmem:kmem_cache_alloc", evsel__process_alloc_event, },
- { "kmem:kmalloc_node", evsel__process_alloc_node_event, },
- { "kmem:kmem_cache_alloc_node", evsel__process_alloc_node_event, },
+ { "kmem:kmalloc_node", evsel__process_alloc_event, },
+ { "kmem:kmem_cache_alloc_node", evsel__process_alloc_event, },
{ "kmem:kfree", evsel__process_free_event, },
{ "kmem:kmem_cache_free", evsel__process_free_event, },
/* page allocator */
@@ -1824,6 +1836,19 @@ static int parse_line_opt(const struct option *opt __maybe_unused,
return 0;
}
+static bool slab_legacy_tp_is_exposed(void)
+{
+ /*
+ * The tracepoints "kmem:kmalloc_node" and
+ * "kmem:kmem_cache_alloc_node" have been removed on the latest
+ * kernel, if the tracepoint "kmem:kmalloc_node" is existed it
+ * means the tool is running on an old kernel, we need to
+ * rollback to support these legacy tracepoints.
+ */
+ return IS_ERR(trace_event__tp_format("kmem", "kmalloc_node")) ?
+ false : true;
+}
+
static int __cmd_record(int argc, const char **argv)
{
const char * const record_args[] = {
@@ -1831,22 +1856,28 @@ static int __cmd_record(int argc, const char **argv)
};
const char * const slab_events[] = {
"-e", "kmem:kmalloc",
- "-e", "kmem:kmalloc_node",
"-e", "kmem:kfree",
"-e", "kmem:kmem_cache_alloc",
- "-e", "kmem:kmem_cache_alloc_node",
"-e", "kmem:kmem_cache_free",
};
+ const char * const slab_legacy_events[] = {
+ "-e", "kmem:kmalloc_node",
+ "-e", "kmem:kmem_cache_alloc_node",
+ };
const char * const page_events[] = {
"-e", "kmem:mm_page_alloc",
"-e", "kmem:mm_page_free",
};
unsigned int rec_argc, i, j;
const char **rec_argv;
+ unsigned int slab_legacy_tp_exposed = slab_legacy_tp_is_exposed();
rec_argc = ARRAY_SIZE(record_args) + argc - 1;
- if (kmem_slab)
+ if (kmem_slab) {
rec_argc += ARRAY_SIZE(slab_events);
+ if (slab_legacy_tp_exposed)
+ rec_argc += ARRAY_SIZE(slab_legacy_events);
+ }
if (kmem_page)
rec_argc += ARRAY_SIZE(page_events) + 1; /* for -g */
@@ -1861,6 +1892,10 @@ static int __cmd_record(int argc, const char **argv)
if (kmem_slab) {
for (j = 0; j < ARRAY_SIZE(slab_events); j++, i++)
rec_argv[i] = strdup(slab_events[j]);
+ if (slab_legacy_tp_exposed) {
+ for (j = 0; j < ARRAY_SIZE(slab_legacy_events); j++, i++)
+ rec_argv[i] = strdup(slab_legacy_events[j]);
+ }
}
if (kmem_page) {
rec_argv[i++] = strdup("-g");
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index 718b82bfcdff95..506c2fe42d523a 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -1670,6 +1670,7 @@ static int __cmd_report(bool display_info)
/* for lock function check */
symbol_conf.sort_by_name = true;
+ symbol_conf.allow_aliases = true;
symbol__init(&session->header.env);
if (!data.is_pipe) {
@@ -1757,6 +1758,7 @@ static int __cmd_contention(int argc, const char **argv)
/* for lock function check */
symbol_conf.sort_by_name = true;
+ symbol_conf.allow_aliases = true;
symbol__init(&session->header.env);
if (use_bpf) {
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 86e06f136f4022..d21fe0f32a6deb 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -16,7 +16,9 @@
#include "util/record.h"
#include <api/fs/tracing_path.h>
+#ifdef HAVE_LIBBPF_SUPPORT
#include <bpf/bpf.h>
+#endif
#include "util/bpf_map.h"
#include "util/rlimit.h"
#include "builtin.h"
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt
index 8fcab5ad00c5cc..e8d2762adade1c 100644
--- a/tools/perf/command-list.txt
+++ b/tools/perf/command-list.txt
@@ -16,20 +16,20 @@ perf-ftrace mainporcelain common
perf-inject mainporcelain common
perf-iostat mainporcelain common
perf-kallsyms mainporcelain common
-perf-kmem mainporcelain common
+perf-kmem mainporcelain traceevent
perf-kvm mainporcelain common
-perf-kwork mainporcelain common
+perf-kwork mainporcelain traceevent
perf-list mainporcelain common
-perf-lock mainporcelain common
+perf-lock mainporcelain traceevent
perf-mem mainporcelain common
perf-probe mainporcelain full
perf-record mainporcelain common
perf-report mainporcelain common
-perf-sched mainporcelain common
+perf-sched mainporcelain traceevent
perf-script mainporcelain common
perf-stat mainporcelain common
perf-test mainporcelain common
-perf-timechart mainporcelain common
+perf-timechart mainporcelain traceevent
perf-top mainporcelain common
perf-trace mainporcelain audit
perf-version mainporcelain common
diff --git a/tools/perf/tests/bpf-script-test-prologue.c b/tools/perf/tests/bpf-script-test-prologue.c
index bd83d364cf30d6..91778b5c6125d0 100644
--- a/tools/perf/tests/bpf-script-test-prologue.c
+++ b/tools/perf/tests/bpf-script-test-prologue.c
@@ -20,6 +20,8 @@
# undef if
#endif
+typedef unsigned int __bitwise fmode_t;
+
#define FMODE_READ 0x1
#define FMODE_WRITE 0x2
diff --git a/tools/perf/tests/make b/tools/perf/tests/make
index 05e818a8bbad11..009d6efb673ce8 100644
--- a/tools/perf/tests/make
+++ b/tools/perf/tests/make
@@ -222,19 +222,7 @@ installed_files_bin := bin/perf
installed_files_bin += etc/bash_completion.d/perf
installed_files_bin += libexec/perf-core/perf-archive
-installed_files_plugins := $(lib)/traceevent/plugins/plugin_cfg80211.so
-installed_files_plugins += $(lib)/traceevent/plugins/plugin_scsi.so
-installed_files_plugins += $(lib)/traceevent/plugins/plugin_xen.so
-installed_files_plugins += $(lib)/traceevent/plugins/plugin_function.so
-installed_files_plugins += $(lib)/traceevent/plugins/plugin_sched_switch.so
-installed_files_plugins += $(lib)/traceevent/plugins/plugin_mac80211.so
-installed_files_plugins += $(lib)/traceevent/plugins/plugin_kvm.so
-installed_files_plugins += $(lib)/traceevent/plugins/plugin_kmem.so
-installed_files_plugins += $(lib)/traceevent/plugins/plugin_hrtimer.so
-installed_files_plugins += $(lib)/traceevent/plugins/plugin_jbd2.so
-
installed_files_all := $(installed_files_bin)
-installed_files_all += $(installed_files_plugins)
test_make_install := $(call test_dest_files,$(installed_files_all))
test_make_install_O := $(call test_dest_files,$(installed_files_all))
diff --git a/tools/perf/tests/shell/buildid.sh b/tools/perf/tests/shell/buildid.sh
index f05670d1e39eb9..aaf851108ca35e 100755
--- a/tools/perf/tests/shell/buildid.sh
+++ b/tools/perf/tests/shell/buildid.sh
@@ -77,7 +77,20 @@ check()
file=${build_id_dir}/.build-id/${id:0:2}/`readlink ${link}`/elf
echo "file: ${file}"
- if [ ! -x $file ]; then
+ # Check for file permission of original file
+ # in case of pe-file.exe file
+ echo $1 | grep ".exe"
+ if [ $? -eq 0 ]; then
+ if [ -x $1 -a ! -x $file ]; then
+ echo "failed: file ${file} executable does not exist"
+ exit 1
+ fi
+
+ if [ ! -x $file -a ! -e $file ]; then
+ echo "failed: file ${file} does not exist"
+ exit 1
+ fi
+ elif [ ! -x $file ]; then
echo "failed: file ${file} does not exist"
exit 1
fi
diff --git a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh
index 34c400ccbe046b..57e7a6a470c93e 100755
--- a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh
+++ b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh
@@ -37,6 +37,7 @@ trace_libc_inet_pton_backtrace() {
case "$(uname -m)" in
s390x)
eventattr='call-graph=dwarf,max-stack=4'
+ echo "text_to_binary_address.*\+0x[[:xdigit:]]+[[:space:]]\($libc|inlined\)$" >> $expected
echo "gaih_inet.*\+0x[[:xdigit:]]+[[:space:]]\($libc|inlined\)$" >> $expected
echo "(__GI_)?getaddrinfo\+0x[[:xdigit:]]+[[:space:]]\($libc|inlined\)$" >> $expected
echo "main\+0x[[:xdigit:]]+[[:space:]]\(.*/bin/ping.*\)$" >> $expected
@@ -57,7 +58,7 @@ trace_libc_inet_pton_backtrace() {
perf_data=`mktemp -u /tmp/perf.data.XXX`
perf_script=`mktemp -u /tmp/perf.script.XXX`
perf record -e $event_name/$eventattr/ -o $perf_data ping -6 -c 1 ::1 > /dev/null 2>&1
- perf script -i $perf_data > $perf_script
+ perf script -i $perf_data | tac | grep -m1 ^ping -B9 | tac > $perf_script
exec 3<$perf_script
exec 4<$expected
diff --git a/tools/perf/trace/beauty/include/linux/socket.h b/tools/perf/trace/beauty/include/linux/socket.h
index de3701a2a2129d..13c3a237b9c934 100644
--- a/tools/perf/trace/beauty/include/linux/socket.h
+++ b/tools/perf/trace/beauty/include/linux/socket.h
@@ -33,7 +33,10 @@ typedef __kernel_sa_family_t sa_family_t;
struct sockaddr {
sa_family_t sa_family; /* address family, AF_xxx */
- char sa_data[14]; /* 14 bytes of protocol address */
+ union {
+ char sa_data_min[14]; /* Minimum 14 bytes of protocol address */
+ DECLARE_FLEX_ARRAY(char, sa_data);
+ };
};
struct linger {
diff --git a/tools/perf/util/PERF-VERSION-GEN b/tools/perf/util/PERF-VERSION-GEN
index 3cc42821d9b397..d7dc7c28508c0a 100755
--- a/tools/perf/util/PERF-VERSION-GEN
+++ b/tools/perf/util/PERF-VERSION-GEN
@@ -19,7 +19,7 @@ TAG=
if test -d ../../.git -o -f ../../.git
then
TAG=$(MAKEFLAGS= make -sC ../.. kernelversion)
- CID=$(git log -1 --abbrev=12 --pretty=format:"%h" 2>/dev/null) && CID="-g$CID"
+ CID=$(git log -1 --abbrev=12 --pretty=format:"%h" --no-show-signature 2>/dev/null) && CID="-g$CID"
elif test -f ../../PERF-VERSION-FILE
then
TAG=$(cut -d' ' -f3 ../../PERF-VERSION-FILE | sed -e 's/\"//g')
diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
index 265d20cc126b13..c2e323cd7d496c 100644
--- a/tools/perf/util/auxtrace.c
+++ b/tools/perf/util/auxtrace.c
@@ -2611,7 +2611,7 @@ static int find_dso_sym(struct dso *dso, const char *sym_name, u64 *start,
*size = sym->start - *start;
if (idx > 0) {
if (*size)
- return 1;
+ return 0;
} else if (dso_sym_match(sym, sym_name, &cnt, idx)) {
print_duplicate_syms(dso, sym_name);
return -EINVAL;
diff --git a/tools/perf/util/bpf_counter.h b/tools/perf/util/bpf_counter.h
index 4dbf26408b692a..c6d21c07b14cd8 100644
--- a/tools/perf/util/bpf_counter.h
+++ b/tools/perf/util/bpf_counter.h
@@ -4,9 +4,12 @@
#include <linux/list.h>
#include <sys/resource.h>
+
+#ifdef HAVE_LIBBPF_SUPPORT
#include <bpf/bpf.h>
#include <bpf/btf.h>
#include <bpf/libbpf.h>
+#endif
struct evsel;
struct target;
@@ -87,6 +90,8 @@ static inline void set_max_rlimit(void)
setrlimit(RLIMIT_MEMLOCK, &rinf);
}
+#ifdef HAVE_BPF_SKEL
+
static inline __u32 bpf_link_get_id(int fd)
{
struct bpf_link_info link_info = { .id = 0, };
@@ -127,5 +132,6 @@ static inline int bperf_trigger_reading(int prog_fd, int cpu)
return bpf_prog_test_run_opts(prog_fd, &opts);
}
+#endif /* HAVE_BPF_SKEL */
#endif /* __PERF_BPF_COUNTER_H */
diff --git a/tools/perf/util/bpf_counter_cgroup.c b/tools/perf/util/bpf_counter_cgroup.c
index 3c2df7522f6fcb..1c82377ed78b99 100644
--- a/tools/perf/util/bpf_counter_cgroup.c
+++ b/tools/perf/util/bpf_counter_cgroup.c
@@ -116,27 +116,19 @@ static int bperf_load_program(struct evlist *evlist)
/* open single copy of the events w/o cgroup */
err = evsel__open_per_cpu(evsel, evsel->core.cpus, -1);
- if (err) {
- pr_err("Failed to open first cgroup events\n");
- goto out;
- }
+ if (err == 0)
+ evsel->supported = true;
map_fd = bpf_map__fd(skel->maps.events);
perf_cpu_map__for_each_cpu(cpu, j, evsel->core.cpus) {
int fd = FD(evsel, j);
__u32 idx = evsel->core.idx * total_cpus + cpu.cpu;
- err = bpf_map_update_elem(map_fd, &idx, &fd,
- BPF_ANY);
- if (err < 0) {
- pr_err("Failed to update perf_event fd\n");
- goto out;
- }
+ bpf_map_update_elem(map_fd, &idx, &fd, BPF_ANY);
}
evsel->cgrp = leader_cgrp;
}
- evsel->supported = true;
if (evsel->cgrp == cgrp)
continue;
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index a839b30c981b7b..ea9c083ab1e3fa 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -715,9 +715,13 @@ build_id_cache__add(const char *sbuild_id, const char *name, const char *realnam
} else if (nsi && nsinfo__need_setns(nsi)) {
if (copyfile_ns(name, filename, nsi))
goto out_free;
- } else if (link(realname, filename) && errno != EEXIST &&
- copyfile(name, filename))
- goto out_free;
+ } else if (link(realname, filename) && errno != EEXIST) {
+ struct stat f_stat;
+
+ if (!(stat(name, &f_stat) < 0) &&
+ copyfile_mode(name, filename, f_stat.st_mode))
+ goto out_free;
+ }
}
/* Some binaries are stripped, but have .debug files with their symbol
diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c
index e99b41f9be45a4..cd978c240e0ddb 100644
--- a/tools/perf/util/cgroup.c
+++ b/tools/perf/util/cgroup.c
@@ -224,6 +224,19 @@ static int add_cgroup_name(const char *fpath, const struct stat *sb __maybe_unus
return 0;
}
+static int check_and_add_cgroup_name(const char *fpath)
+{
+ struct cgroup_name *cn;
+
+ list_for_each_entry(cn, &cgroup_list, list) {
+ if (!strcmp(cn->name, fpath))
+ return 0;
+ }
+
+ /* pretend if it's added by ftw() */
+ return add_cgroup_name(fpath, NULL, FTW_D, NULL);
+}
+
static void release_cgroup_list(void)
{
struct cgroup_name *cn;
@@ -242,7 +255,7 @@ static int list_cgroups(const char *str)
struct cgroup_name *cn;
char *s;
- /* use given name as is - for testing purpose */
+ /* use given name as is when no regex is given */
for (;;) {
p = strchr(str, ',');
e = p ? p : eos;
@@ -253,13 +266,13 @@ static int list_cgroups(const char *str)
s = strndup(str, e - str);
if (!s)
return -1;
- /* pretend if it's added by ftw() */
- ret = add_cgroup_name(s, NULL, FTW_D, NULL);
+
+ ret = check_and_add_cgroup_name(s);
free(s);
- if (ret)
+ if (ret < 0)
return -1;
} else {
- if (add_cgroup_name("", NULL, FTW_D, NULL) < 0)
+ if (check_and_add_cgroup_name("/") < 0)
return -1;
}
diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c
index a7f68c309545de..fc16299c915f9b 100644
--- a/tools/perf/util/data.c
+++ b/tools/perf/util/data.c
@@ -132,6 +132,7 @@ int perf_data__open_dir(struct perf_data *data)
file->size = st.st_size;
}
+ closedir(dir);
if (!files)
return -EINVAL;
@@ -140,6 +141,7 @@ int perf_data__open_dir(struct perf_data *data)
return 0;
out_err:
+ closedir(dir);
close_dir(files, nr);
return ret;
}
diff --git a/tools/perf/util/expr.l b/tools/perf/util/expr.l
index 0168a96373309b..d47de5f270a8e9 100644
--- a/tools/perf/util/expr.l
+++ b/tools/perf/util/expr.l
@@ -42,8 +42,11 @@ static char *normalize(char *str, int runtime)
char *dst = str;
while (*str) {
- if (*str == '\\')
+ if (*str == '\\') {
*dst++ = *++str;
+ if (!*str)
+ break;
+ }
else if (*str == '?') {
char *paramval;
int i = 0;
diff --git a/tools/perf/util/generate-cmdlist.sh b/tools/perf/util/generate-cmdlist.sh
index c3cef36d417683..1b5140e5ce9975 100755
--- a/tools/perf/util/generate-cmdlist.sh
+++ b/tools/perf/util/generate-cmdlist.sh
@@ -38,7 +38,7 @@ do
done
echo "#endif /* HAVE_LIBELF_SUPPORT */"
-echo "#if defined(HAVE_LIBAUDIT_SUPPORT) || defined(HAVE_SYSCALL_TABLE_SUPPORT)"
+echo "#if defined(HAVE_LIBTRACEEVENT) && (defined(HAVE_LIBAUDIT_SUPPORT) || defined(HAVE_SYSCALL_TABLE_SUPPORT))"
sed -n -e 's/^perf-\([^ ]*\)[ ].* audit*/\1/p' command-list.txt |
sort |
while read cmd
@@ -51,5 +51,20 @@ do
p
}' "Documentation/perf-$cmd.txt"
done
-echo "#endif /* HAVE_LIBELF_SUPPORT */"
+echo "#endif /* HAVE_LIBTRACEEVENT && (HAVE_LIBAUDIT_SUPPORT || HAVE_SYSCALL_TABLE_SUPPORT) */"
+
+echo "#ifdef HAVE_LIBTRACEEVENT"
+sed -n -e 's/^perf-\([^ ]*\)[ ].* traceevent.*/\1/p' command-list.txt |
+sort |
+while read cmd
+do
+ sed -n '
+ /^NAME/,/perf-'"$cmd"'/H
+ ${
+ x
+ s/.*perf-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/
+ p
+ }' "Documentation/perf-$cmd.txt"
+done
+echo "#endif /* HAVE_LIBTRACEEVENT */"
echo "};"
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index e188f74698dd31..37662cdec5eefd 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -2971,6 +2971,18 @@ static int add_dynamic_entry(struct evlist *evlist, const char *tok,
ret = add_all_matching_fields(evlist, field_name, raw_trace, level);
goto out;
}
+#else
+ evlist__for_each_entry(evlist, evsel) {
+ if (evsel->core.attr.type == PERF_TYPE_TRACEPOINT) {
+ pr_err("%s %s", ret ? "," : "This perf binary isn't linked with libtraceevent, can't process", evsel__name(evsel));
+ ret = -ENOTSUP;
+ }
+ }
+
+ if (ret) {
+ pr_err("\n");
+ goto out;
+ }
#endif
evsel = find_evsel(evlist, event_name);
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index add6c5d9531cdf..9b3cd79cca1217 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stdio.h>
+#include <sys/types.h>
#include <linux/types.h>
struct evlist;
diff --git a/tools/testing/memblock/.gitignore b/tools/testing/memblock/.gitignore
index 654338e0be52eb..4cc7cd5aac2b10 100644
--- a/tools/testing/memblock/.gitignore
+++ b/tools/testing/memblock/.gitignore
@@ -1,4 +1,5 @@
main
memblock.c
linux/memblock.h
+asm/asm.h
asm/cmpxchg.h
diff --git a/tools/testing/memblock/Makefile b/tools/testing/memblock/Makefile
index 2310ac4d080ec6..7a1ca694a9825c 100644
--- a/tools/testing/memblock/Makefile
+++ b/tools/testing/memblock/Makefile
@@ -29,13 +29,14 @@ include: ../../../include/linux/memblock.h ../../include/linux/*.h \
@mkdir -p linux
test -L linux/memblock.h || ln -s ../../../../include/linux/memblock.h linux/memblock.h
+ test -L asm/asm.h || ln -s ../../../arch/x86/include/asm/asm.h asm/asm.h
test -L asm/cmpxchg.h || ln -s ../../../arch/x86/include/asm/cmpxchg.h asm/cmpxchg.h
memblock.c: $(EXTR_SRC)
test -L memblock.c || ln -s $(EXTR_SRC) memblock.c
clean:
- $(RM) $(TARGETS) $(OFILES) linux/memblock.h memblock.c asm/cmpxchg.h
+ $(RM) $(TARGETS) $(OFILES) linux/memblock.h memblock.c asm/asm.h asm/cmpxchg.h
help:
@echo 'Memblock simulator'
diff --git a/tools/testing/selftests/alsa/Makefile b/tools/testing/selftests/alsa/Makefile
index a8c0383878d3e6..901949db80ade5 100644
--- a/tools/testing/selftests/alsa/Makefile
+++ b/tools/testing/selftests/alsa/Makefile
@@ -8,13 +8,15 @@ LDLIBS += -lasound
endif
CFLAGS += -L$(OUTPUT) -Wl,-rpath=./
+LDLIBS+=-lpthread
+
OVERRIDE_TARGETS = 1
TEST_GEN_PROGS := mixer-test pcm-test
TEST_GEN_PROGS_EXTENDED := libatest.so
-TEST_FILES := conf.d
+TEST_FILES := conf.d pcm-test.conf
include ../lib.mk
diff --git a/tools/testing/selftests/alsa/alsa-local.h b/tools/testing/selftests/alsa/alsa-local.h
index 65f197ea9773ab..de030dc23bd19f 100644
--- a/tools/testing/selftests/alsa/alsa-local.h
+++ b/tools/testing/selftests/alsa/alsa-local.h
@@ -12,6 +12,7 @@
snd_config_t *get_alsalib_config(void);
+snd_config_t *conf_load_from_file(const char *filename);
void conf_load(void);
void conf_free(void);
snd_config_t *conf_by_card(int card);
@@ -20,5 +21,7 @@ int conf_get_count(snd_config_t *root, const char *key1, const char *key2);
const char *conf_get_string(snd_config_t *root, const char *key1, const char *key2, const char *def);
long conf_get_long(snd_config_t *root, const char *key1, const char *key2, long def);
int conf_get_bool(snd_config_t *root, const char *key1, const char *key2, int def);
+void conf_get_string_array(snd_config_t *root, const char *key1, const char *key2,
+ const char **array, int array_size, const char *def);
#endif /* __ALSA_LOCAL_H */
diff --git a/tools/testing/selftests/alsa/conf.c b/tools/testing/selftests/alsa/conf.c
index c7ffc8f0419551..d7aafe5a1993aa 100644
--- a/tools/testing/selftests/alsa/conf.c
+++ b/tools/testing/selftests/alsa/conf.c
@@ -125,7 +125,7 @@ static int dump_config_tree(snd_config_t *top)
snd_output_close(out);
}
-static snd_config_t *load(const char *filename)
+snd_config_t *conf_load_from_file(const char *filename)
{
snd_config_t *dst;
snd_input_t *input;
@@ -235,7 +235,7 @@ static bool test_filename1(int card, const char *filename, const char *sysfs_car
snd_config_t *config, *sysfs_config, *card_config, *sysfs_card_config, *node;
snd_config_iterator_t i, next;
- config = load(filename);
+ config = conf_load_from_file(filename);
if (snd_config_search(config, "sysfs", &sysfs_config) ||
snd_config_get_type(sysfs_config) != SND_CONFIG_TYPE_COMPOUND)
ksft_exit_fail_msg("Missing global sysfs block in filename %s\n", filename);
@@ -446,3 +446,25 @@ int conf_get_bool(snd_config_t *root, const char *key1, const char *key2, int de
ksft_exit_fail_msg("key '%s'.'%s' is not an bool\n", key1, key2);
return !!ret;
}
+
+void conf_get_string_array(snd_config_t *root, const char *key1, const char *key2,
+ const char **array, int array_size, const char *def)
+{
+ snd_config_t *cfg;
+ char buf[16];
+ int ret, index;
+
+ ret = conf_get_by_keys(root, key1, key2, &cfg);
+ if (ret == -ENOENT)
+ cfg = NULL;
+ else if (ret < 0)
+ ksft_exit_fail_msg("key '%s'.'%s' search error: %s\n", key1, key2, snd_strerror(ret));
+ for (index = 0; index < array_size; index++) {
+ if (cfg == NULL) {
+ array[index] = def;
+ } else {
+ sprintf(buf, "%i", index);
+ array[index] = conf_get_string(cfg, buf, NULL, def);
+ }
+ }
+}
diff --git a/tools/testing/selftests/alsa/conf.d/Lenovo_ThinkPad_P1_Gen2.conf b/tools/testing/selftests/alsa/conf.d/Lenovo_ThinkPad_P1_Gen2.conf
index 9eca985e0c085d..5b40a916295dde 100644
--- a/tools/testing/selftests/alsa/conf.d/Lenovo_ThinkPad_P1_Gen2.conf
+++ b/tools/testing/selftests/alsa/conf.d/Lenovo_ThinkPad_P1_Gen2.conf
@@ -39,25 +39,30 @@ card.hda {
#
pcm.0.0 {
PLAYBACK {
- #
- # Uncomment to override values for specific tests
- #
- #test_name1 {
- # access RW_INTERLEAVED
- # format S16_LE
- # rate 48000
- # channels 2
- # period_size 512
- # buffer_size 4096
- #}
- #test_name2 {
- # access RW_INTERLEAVED
- # format S16_LE
- # rate 48000
- # channels 2
- # period_size 24000
- # buffer_size 192000
- #}
+ test.time1 {
+ access RW_INTERLEAVED # can be omitted - default
+ format S16_LE # can be omitted - default
+ rate 48000 # can be omitted - default
+ channels 2 # can be omitted - default
+ period_size 512
+ buffer_size 4096
+ }
+ test.time2 {
+ access RW_INTERLEAVED
+ format S16_LE
+ rate 48000
+ channels 2
+ period_size 24000
+ buffer_size 192000
+ }
+ test.time3 {
+ access RW_INTERLEAVED
+ format S16_LE
+ rate 44100
+ channels 2
+ period_size 24000
+ buffer_size 192000
+ }
}
CAPTURE {
# use default tests, check for the presence
diff --git a/tools/testing/selftests/alsa/pcm-test.c b/tools/testing/selftests/alsa/pcm-test.c
index f293c7d810092b..58b525a4a32ca2 100644
--- a/tools/testing/selftests/alsa/pcm-test.c
+++ b/tools/testing/selftests/alsa/pcm-test.c
@@ -15,12 +15,21 @@
#include <stdbool.h>
#include <errno.h>
#include <assert.h>
+#include <pthread.h>
#include "../kselftest.h"
#include "alsa-local.h"
typedef struct timespec timestamp_t;
+struct card_data {
+ int card;
+ pthread_t thread;
+ struct card_data *next;
+};
+
+struct card_data *card_list = NULL;
+
struct pcm_data {
snd_pcm_t *handle;
int card;
@@ -31,19 +40,19 @@ struct pcm_data {
struct pcm_data *next;
};
-int num_pcms = 0;
struct pcm_data *pcm_list = NULL;
int num_missing = 0;
struct pcm_data *pcm_missing = NULL;
-struct time_test_def {
- const char *cfg_prefix;
- const char *format;
- long rate;
- long channels;
- long period_size;
- long buffer_size;
+snd_config_t *default_pcm_config;
+
+/* Lock while reporting results since kselftest doesn't */
+pthread_mutex_t results_lock = PTHREAD_MUTEX_INITIALIZER;
+
+enum test_class {
+ TEST_CLASS_DEFAULT,
+ TEST_CLASS_SYSTEM,
};
void timestamp_now(timestamp_t *tstamp)
@@ -146,6 +155,7 @@ static void find_pcms(void)
snd_ctl_t *handle;
snd_pcm_info_t *pcm_info;
snd_config_t *config, *card_config, *pcm_config;
+ struct card_data *card_data;
snd_pcm_info_alloca(&pcm_info);
@@ -167,6 +177,13 @@ static void find_pcms(void)
card_config = conf_by_card(card);
+ card_data = calloc(1, sizeof(*card_data));
+ if (!card_data)
+ ksft_exit_fail_msg("Out of memory\n");
+ card_data->card = card;
+ card_data->next = card_list;
+ card_list = card_data;
+
dev = -1;
while (1) {
if (snd_ctl_pcm_next_device(handle, &dev) < 0)
@@ -209,7 +226,6 @@ static void find_pcms(void)
pcm_data->pcm_config = conf_get_subtree(card_config, key, NULL);
pcm_data->next = pcm_list;
pcm_list = pcm_data;
- num_pcms++;
}
}
}
@@ -228,45 +244,64 @@ static void find_pcms(void)
snd_config_delete(config);
}
-static void test_pcm_time1(struct pcm_data *data,
- const struct time_test_def *test)
+static void test_pcm_time(struct pcm_data *data, enum test_class class,
+ const char *test_name, snd_config_t *pcm_cfg)
{
char name[64], key[128], msg[256];
const char *cs;
int i, err;
snd_pcm_t *handle = NULL;
snd_pcm_access_t access = SND_PCM_ACCESS_RW_INTERLEAVED;
- snd_pcm_format_t format;
+ snd_pcm_format_t format, old_format;
+ const char *alt_formats[8];
unsigned char *samples = NULL;
snd_pcm_sframes_t frames;
long long ms;
long rate, channels, period_size, buffer_size;
- unsigned int rchannels;
unsigned int rrate;
snd_pcm_uframes_t rperiod_size, rbuffer_size, start_threshold;
timestamp_t tstamp;
- bool pass = false, automatic = true;
+ bool pass = false;
snd_pcm_hw_params_t *hw_params;
snd_pcm_sw_params_t *sw_params;
- bool skip = false;
+ const char *test_class_name;
+ bool skip = true;
+ const char *desc;
+
+ switch (class) {
+ case TEST_CLASS_DEFAULT:
+ test_class_name = "default";
+ break;
+ case TEST_CLASS_SYSTEM:
+ test_class_name = "system";
+ break;
+ default:
+ ksft_exit_fail_msg("Unknown test class %d\n", class);
+ break;
+ }
+
+ desc = conf_get_string(pcm_cfg, "description", NULL, NULL);
+ if (desc)
+ ksft_print_msg("%s.%s.%d.%d.%d.%s - %s\n",
+ test_class_name, test_name,
+ data->card, data->device, data->subdevice,
+ snd_pcm_stream_name(data->stream),
+ desc);
+
snd_pcm_hw_params_alloca(&hw_params);
snd_pcm_sw_params_alloca(&sw_params);
- cs = conf_get_string(data->pcm_config, test->cfg_prefix, "format", test->format);
+ cs = conf_get_string(pcm_cfg, "format", NULL, "S16_LE");
format = snd_pcm_format_value(cs);
if (format == SND_PCM_FORMAT_UNKNOWN)
ksft_exit_fail_msg("Wrong format '%s'\n", cs);
- rate = conf_get_long(data->pcm_config, test->cfg_prefix, "rate", test->rate);
- channels = conf_get_long(data->pcm_config, test->cfg_prefix, "channels", test->channels);
- period_size = conf_get_long(data->pcm_config, test->cfg_prefix, "period_size", test->period_size);
- buffer_size = conf_get_long(data->pcm_config, test->cfg_prefix, "buffer_size", test->buffer_size);
-
- automatic = strcmp(test->format, snd_pcm_format_name(format)) == 0 &&
- test->rate == rate &&
- test->channels == channels &&
- test->period_size == period_size &&
- test->buffer_size == buffer_size;
+ conf_get_string_array(pcm_cfg, "alt_formats", NULL,
+ alt_formats, ARRAY_SIZE(alt_formats), NULL);
+ rate = conf_get_long(pcm_cfg, "rate", NULL, 48000);
+ channels = conf_get_long(pcm_cfg, "channels", NULL, 2);
+ period_size = conf_get_long(pcm_cfg, "period_size", NULL, 4096);
+ buffer_size = conf_get_long(pcm_cfg, "buffer_size", NULL, 16384);
samples = malloc((rate * channels * snd_pcm_format_physical_width(format)) / 8);
if (!samples)
@@ -296,32 +331,39 @@ static void test_pcm_time1(struct pcm_data *data,
snd_pcm_access_name(access), snd_strerror(err));
goto __close;
}
+ i = -1;
__format:
err = snd_pcm_hw_params_set_format(handle, hw_params, format);
if (err < 0) {
- if (automatic && format == SND_PCM_FORMAT_S16_LE) {
- format = SND_PCM_FORMAT_S32_LE;
- ksft_print_msg("%s.%d.%d.%d.%s.%s format S16_LE -> S32_LE\n",
- test->cfg_prefix,
- data->card, data->device, data->subdevice,
- snd_pcm_stream_name(data->stream),
- snd_pcm_access_name(access));
+ i++;
+ if (i < ARRAY_SIZE(alt_formats) && alt_formats[i]) {
+ old_format = format;
+ format = snd_pcm_format_value(alt_formats[i]);
+ if (format != SND_PCM_FORMAT_UNKNOWN) {
+ ksft_print_msg("%s.%d.%d.%d.%s.%s format %s -> %s\n",
+ test_name,
+ data->card, data->device, data->subdevice,
+ snd_pcm_stream_name(data->stream),
+ snd_pcm_access_name(access),
+ snd_pcm_format_name(old_format),
+ snd_pcm_format_name(format));
+ samples = realloc(samples, (rate * channels *
+ snd_pcm_format_physical_width(format)) / 8);
+ if (!samples)
+ ksft_exit_fail_msg("Out of memory\n");
+ snd_pcm_format_set_silence(format, samples, rate * channels);
+ goto __format;
+ }
}
snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_format %s: %s",
snd_pcm_format_name(format), snd_strerror(err));
goto __close;
}
- rchannels = channels;
- err = snd_pcm_hw_params_set_channels_near(handle, hw_params, &rchannels);
+ err = snd_pcm_hw_params_set_channels(handle, hw_params, channels);
if (err < 0) {
snprintf(msg, sizeof(msg), "snd_pcm_hw_params_set_channels %ld: %s", channels, snd_strerror(err));
goto __close;
}
- if (rchannels != channels) {
- snprintf(msg, sizeof(msg), "channels unsupported %ld != %ld", channels, rchannels);
- skip = true;
- goto __close;
- }
rrate = rate;
err = snd_pcm_hw_params_set_rate_near(handle, hw_params, &rrate, 0);
if (err < 0) {
@@ -329,8 +371,7 @@ __format:
goto __close;
}
if (rrate != rate) {
- snprintf(msg, sizeof(msg), "rate unsupported %ld != %ld", rate, rrate);
- skip = true;
+ snprintf(msg, sizeof(msg), "rate mismatch %ld != %ld", rate, rrate);
goto __close;
}
rperiod_size = period_size;
@@ -377,8 +418,8 @@ __format:
goto __close;
}
- ksft_print_msg("%s.%d.%d.%d.%s hw_params.%s.%s.%ld.%ld.%ld.%ld sw_params.%ld\n",
- test->cfg_prefix,
+ ksft_print_msg("%s.%s.%d.%d.%d.%s hw_params.%s.%s.%ld.%ld.%ld.%ld sw_params.%ld\n",
+ test_class_name, test_name,
data->card, data->device, data->subdevice,
snd_pcm_stream_name(data->stream),
snd_pcm_access_name(access),
@@ -387,6 +428,9 @@ __format:
(long)rperiod_size, (long)rbuffer_size,
(long)start_threshold);
+ /* Set all the params, actually run the test */
+ skip = false;
+
timestamp_now(&tstamp);
for (i = 0; i < 4; i++) {
if (data->stream == SND_PCM_STREAM_PLAYBACK) {
@@ -426,48 +470,120 @@ __format:
msg[0] = '\0';
pass = true;
__close:
- if (!skip) {
- ksft_test_result(pass, "%s.%d.%d.%d.%s%s%s\n",
- test->cfg_prefix,
+ pthread_mutex_lock(&results_lock);
+
+ switch (class) {
+ case TEST_CLASS_SYSTEM:
+ test_class_name = "system";
+ /*
+ * Anything specified as specific to this system
+ * should always be supported.
+ */
+ ksft_test_result(!skip, "%s.%s.%d.%d.%d.%s.params\n",
+ test_class_name, test_name,
+ data->card, data->device, data->subdevice,
+ snd_pcm_stream_name(data->stream));
+ break;
+ default:
+ break;
+ }
+
+ if (!skip)
+ ksft_test_result(pass, "%s.%s.%d.%d.%d.%s%s%s\n",
+ test_class_name, test_name,
data->card, data->device, data->subdevice,
snd_pcm_stream_name(data->stream),
msg[0] ? " " : "", msg);
- } else {
- ksft_test_result_skip("%s.%d.%d.%d.%s%s%s\n",
- test->cfg_prefix,
- data->card, data->device,
- data->subdevice,
- snd_pcm_stream_name(data->stream),
- msg[0] ? " " : "", msg);
- }
+ else
+ ksft_test_result_skip("%s.%s.%d.%d.%d.%s%s%s\n",
+ test_class_name, test_name,
+ data->card, data->device, data->subdevice,
+ snd_pcm_stream_name(data->stream),
+ msg[0] ? " " : "", msg);
+
+ pthread_mutex_unlock(&results_lock);
+
free(samples);
if (handle)
snd_pcm_close(handle);
}
-static const struct time_test_def time_tests[] = {
- /* name format rate chan period buffer */
- { "8k.1.big", "S16_LE", 8000, 2, 8000, 32000 },
- { "8k.2.big", "S16_LE", 8000, 2, 8000, 32000 },
- { "44k1.2.big", "S16_LE", 44100, 2, 22050, 192000 },
- { "48k.2.small", "S16_LE", 48000, 2, 512, 4096 },
- { "48k.2.big", "S16_LE", 48000, 2, 24000, 192000 },
- { "48k.6.big", "S16_LE", 48000, 6, 48000, 576000 },
- { "96k.2.big", "S16_LE", 96000, 2, 48000, 192000 },
-};
+void run_time_tests(struct pcm_data *pcm, enum test_class class,
+ snd_config_t *cfg)
+{
+ const char *test_name, *test_type;
+ snd_config_t *pcm_cfg;
+ snd_config_iterator_t i, next;
+
+ if (!cfg)
+ return;
+
+ cfg = conf_get_subtree(cfg, "test", NULL);
+ if (cfg == NULL)
+ return;
+
+ snd_config_for_each(i, next, cfg) {
+ pcm_cfg = snd_config_iterator_entry(i);
+ if (snd_config_get_id(pcm_cfg, &test_name) < 0)
+ ksft_exit_fail_msg("snd_config_get_id\n");
+ test_type = conf_get_string(pcm_cfg, "type", NULL, "time");
+ if (strcmp(test_type, "time") == 0)
+ test_pcm_time(pcm, class, test_name, pcm_cfg);
+ else
+ ksft_exit_fail_msg("unknown test type '%s'\n", test_type);
+ }
+}
+
+void *card_thread(void *data)
+{
+ struct card_data *card = data;
+ struct pcm_data *pcm;
+
+ for (pcm = pcm_list; pcm != NULL; pcm = pcm->next) {
+ if (pcm->card != card->card)
+ continue;
+
+ run_time_tests(pcm, TEST_CLASS_DEFAULT, default_pcm_config);
+ run_time_tests(pcm, TEST_CLASS_SYSTEM, pcm->pcm_config);
+ }
+
+ return 0;
+}
int main(void)
{
+ struct card_data *card;
struct pcm_data *pcm;
- int i;
+ snd_config_t *global_config, *cfg, *pcm_cfg;
+ int num_pcm_tests = 0, num_tests, num_std_pcm_tests;
+ int ret;
+ void *thread_ret;
ksft_print_header();
+ global_config = conf_load_from_file("pcm-test.conf");
+ default_pcm_config = conf_get_subtree(global_config, "pcm", NULL);
+ if (default_pcm_config == NULL)
+ ksft_exit_fail_msg("default pcm test configuration (pcm compound) is missing\n");
+
conf_load();
find_pcms();
- ksft_set_plan(num_missing + num_pcms * ARRAY_SIZE(time_tests));
+ num_std_pcm_tests = conf_get_count(default_pcm_config, "test", NULL);
+
+ for (pcm = pcm_list; pcm != NULL; pcm = pcm->next) {
+ num_pcm_tests += num_std_pcm_tests;
+ cfg = pcm->pcm_config;
+ if (cfg == NULL)
+ continue;
+ /* Setting params is reported as a separate test */
+ num_tests = conf_get_count(cfg, "test", NULL) * 2;
+ if (num_tests > 0)
+ num_pcm_tests += num_tests;
+ }
+
+ ksft_set_plan(num_missing + num_pcm_tests);
for (pcm = pcm_missing; pcm != NULL; pcm = pcm->next) {
ksft_test_result(false, "test.missing.%d.%d.%d.%s\n",
@@ -475,12 +591,25 @@ int main(void)
snd_pcm_stream_name(pcm->stream));
}
- for (pcm = pcm_list; pcm != NULL; pcm = pcm->next) {
- for (i = 0; i < ARRAY_SIZE(time_tests); i++) {
- test_pcm_time1(pcm, &time_tests[i]);
+ for (card = card_list; card != NULL; card = card->next) {
+ ret = pthread_create(&card->thread, NULL, card_thread, card);
+ if (ret != 0) {
+ ksft_exit_fail_msg("Failed to create card %d thread: %d (%s)\n",
+ card->card, ret,
+ strerror(errno));
+ }
+ }
+
+ for (card = card_list; card != NULL; card = card->next) {
+ ret = pthread_join(card->thread, &thread_ret);
+ if (ret != 0) {
+ ksft_exit_fail_msg("Failed to join card %d thread: %d (%s)\n",
+ card->card, ret,
+ strerror(errno));
}
}
+ snd_config_delete(global_config);
conf_free();
ksft_exit_pass();
diff --git a/tools/testing/selftests/alsa/pcm-test.conf b/tools/testing/selftests/alsa/pcm-test.conf
new file mode 100644
index 00000000000000..71bd3f78a6f286
--- /dev/null
+++ b/tools/testing/selftests/alsa/pcm-test.conf
@@ -0,0 +1,63 @@
+pcm.test.time1 {
+ description "8kHz mono large periods"
+ format S16_LE
+ alt_formats [ S32_LE ]
+ rate 8000
+ channels 1
+ period_size 8000
+ buffer_size 32000
+}
+pcm.test.time2 {
+ description "8kHz stereo large periods"
+ format S16_LE
+ alt_formats [ S32_LE ]
+ rate 8000
+ channels 2
+ period_size 8000
+ buffer_size 32000
+}
+pcm.test.time3 {
+ description "44.1kHz stereo large periods"
+ format S16_LE
+ alt_formats [ S32_LE ]
+ rate 44100
+ channels 2
+ period_size 22500
+ buffer_size 192000
+}
+pcm.test.time4 {
+ description "48kHz stereo small periods"
+ format S16_LE
+ alt_formats [ S32_LE ]
+ rate 48000
+ channels 2
+ period_size 512
+ buffer_size 4096
+}
+pcm.test.time5 {
+ description "48kHz stereo large periods"
+ format S16_LE
+ alt_formats [ S32_LE ]
+ rate 48000
+ channels 2
+ period_size 24000
+ buffer_size 192000
+}
+pcm.test.time6 {
+ description "48kHz 6 channel large periods"
+ format S16_LE
+ alt_formats [ S32_LE ]
+ rate 48000
+ channels 2
+ period_size 48000
+ buffer_size 576000
+}
+pcm.test.time7 {
+ description "96kHz stereo large periods"
+ format S16_LE
+ alt_formats [ S32_LE ]
+ rate 96000
+ channels 2
+ period_size 48000
+ buffer_size 192000
+}
diff --git a/tools/testing/selftests/amd-pstate/Makefile b/tools/testing/selftests/amd-pstate/Makefile
index 5f195ee756d672..5fd1424db37d82 100644
--- a/tools/testing/selftests/amd-pstate/Makefile
+++ b/tools/testing/selftests/amd-pstate/Makefile
@@ -7,11 +7,6 @@ all:
uname_M := $(shell uname -m 2>/dev/null || echo not)
ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
-ifeq (x86,$(ARCH))
-TEST_GEN_FILES += ../../../power/x86/amd_pstate_tracer/amd_pstate_trace.py
-TEST_GEN_FILES += ../../../power/x86/intel_pstate_tracer/intel_pstate_tracer.py
-endif
-
TEST_PROGS := run.sh
TEST_FILES := basic.sh tbench.sh gitsource.sh
diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore
index 07d2d0a8c5cb4d..401a75844cc0e7 100644
--- a/tools/testing/selftests/bpf/.gitignore
+++ b/tools/testing/selftests/bpf/.gitignore
@@ -36,6 +36,7 @@ test_cpp
*.lskel.h
/no_alu32
/bpf_gcc
+/host-tools
/tools
/runqslower
/bench
diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x
index 585fcf73c73146..3fc3e54b19aad4 100644
--- a/tools/testing/selftests/bpf/DENYLIST.s390x
+++ b/tools/testing/selftests/bpf/DENYLIST.s390x
@@ -14,6 +14,7 @@ cgrp_kfunc # JIT does not support calling kernel f
cgrp_local_storage # prog_attach unexpected error: -524 (trampoline)
core_read_macros # unknown func bpf_probe_read#4 (overlapping)
d_path # failed to auto-attach program 'prog_stat': -524 (trampoline)
+decap_sanity # JIT does not support calling kernel function (kfunc)
deny_namespace # failed to attach: ERROR: strerror_r(-524)=22 (trampoline)
dummy_st_ops # test_run unexpected error: -524 (errno 524) (trampoline)
fentry_fexit # fentry attach failed: -524 (trampoline)
diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c
index 6f8ed61fc4b4a8..3af6450763e9ac 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c
@@ -1465,6 +1465,77 @@ out:
bpf_iter_task_vma__destroy(skel);
}
+static void test_task_vma_dead_task(void)
+{
+ struct bpf_iter_task_vma *skel;
+ int wstatus, child_pid = -1;
+ time_t start_tm, cur_tm;
+ int err, iter_fd = -1;
+ int wait_sec = 3;
+
+ skel = bpf_iter_task_vma__open();
+ if (!ASSERT_OK_PTR(skel, "bpf_iter_task_vma__open"))
+ return;
+
+ skel->bss->pid = getpid();
+
+ err = bpf_iter_task_vma__load(skel);
+ if (!ASSERT_OK(err, "bpf_iter_task_vma__load"))
+ goto out;
+
+ skel->links.proc_maps = bpf_program__attach_iter(
+ skel->progs.proc_maps, NULL);
+
+ if (!ASSERT_OK_PTR(skel->links.proc_maps, "bpf_program__attach_iter")) {
+ skel->links.proc_maps = NULL;
+ goto out;
+ }
+
+ start_tm = time(NULL);
+ cur_tm = start_tm;
+
+ child_pid = fork();
+ if (child_pid == 0) {
+ /* Fork short-lived processes in the background. */
+ while (cur_tm < start_tm + wait_sec) {
+ system("echo > /dev/null");
+ cur_tm = time(NULL);
+ }
+ exit(0);
+ }
+
+ if (!ASSERT_GE(child_pid, 0, "fork_child"))
+ goto out;
+
+ while (cur_tm < start_tm + wait_sec) {
+ iter_fd = bpf_iter_create(bpf_link__fd(skel->links.proc_maps));
+ if (!ASSERT_GE(iter_fd, 0, "create_iter"))
+ goto out;
+
+ /* Drain all data from iter_fd. */
+ while (cur_tm < start_tm + wait_sec) {
+ err = read_fd_into_buffer(iter_fd, task_vma_output, CMP_BUFFER_SIZE);
+ if (!ASSERT_GE(err, 0, "read_iter_fd"))
+ goto out;
+
+ cur_tm = time(NULL);
+
+ if (err == 0)
+ break;
+ }
+
+ close(iter_fd);
+ iter_fd = -1;
+ }
+
+ check_bpf_link_info(skel->progs.proc_maps);
+
+out:
+ waitpid(child_pid, &wstatus, 0);
+ close(iter_fd);
+ bpf_iter_task_vma__destroy(skel);
+}
+
void test_bpf_sockmap_map_iter_fd(void)
{
struct bpf_iter_sockmap *skel;
@@ -1586,6 +1657,8 @@ void test_bpf_iter(void)
test_task_file();
if (test__start_subtest("task_vma"))
test_task_vma();
+ if (test__start_subtest("task_vma_dead_task"))
+ test_task_vma_dead_task();
if (test__start_subtest("task_btf"))
test_task_btf();
if (test__start_subtest("tcp4"))
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
index 0ba2e8b9c6ace7..e9ea38aa8248be 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
@@ -801,7 +801,7 @@ static void test_btf_dump_struct_data(struct btf *btf, struct btf_dump *d,
static void test_btf_dump_var_data(struct btf *btf, struct btf_dump *d,
char *str)
{
-#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
+#if 0
TEST_BTF_DUMP_VAR(btf, d, NULL, str, "cpu_number", int, BTF_F_COMPACT,
"int cpu_number = (int)100", 100);
#endif
diff --git a/tools/testing/selftests/bpf/prog_tests/decap_sanity.c b/tools/testing/selftests/bpf/prog_tests/decap_sanity.c
new file mode 100644
index 00000000000000..0b2f73b88c53d3
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/decap_sanity.c
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <linux/in6.h>
+
+#include "test_progs.h"
+#include "network_helpers.h"
+#include "decap_sanity.skel.h"
+
+#define SYS(fmt, ...) \
+ ({ \
+ char cmd[1024]; \
+ snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
+ if (!ASSERT_OK(system(cmd), cmd)) \
+ goto fail; \
+ })
+
+#define NS_TEST "decap_sanity_ns"
+#define IPV6_IFACE_ADDR "face::1"
+#define UDP_TEST_PORT 7777
+
+void test_decap_sanity(void)
+{
+ LIBBPF_OPTS(bpf_tc_hook, qdisc_hook, .attach_point = BPF_TC_EGRESS);
+ LIBBPF_OPTS(bpf_tc_opts, tc_attach);
+ struct nstoken *nstoken = NULL;
+ struct decap_sanity *skel;
+ struct sockaddr_in6 addr;
+ socklen_t addrlen;
+ char buf[128] = {};
+ int sockfd, err;
+
+ skel = decap_sanity__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel open_and_load"))
+ return;
+
+ SYS("ip netns add %s", NS_TEST);
+ SYS("ip -net %s -6 addr add %s/128 dev lo nodad", NS_TEST, IPV6_IFACE_ADDR);
+ SYS("ip -net %s link set dev lo up", NS_TEST);
+
+ nstoken = open_netns(NS_TEST);
+ if (!ASSERT_OK_PTR(nstoken, "open_netns"))
+ goto fail;
+
+ qdisc_hook.ifindex = if_nametoindex("lo");
+ if (!ASSERT_GT(qdisc_hook.ifindex, 0, "if_nametoindex lo"))
+ goto fail;
+
+ err = bpf_tc_hook_create(&qdisc_hook);
+ if (!ASSERT_OK(err, "create qdisc hook"))
+ goto fail;
+
+ tc_attach.prog_fd = bpf_program__fd(skel->progs.decap_sanity);
+ err = bpf_tc_attach(&qdisc_hook, &tc_attach);
+ if (!ASSERT_OK(err, "attach filter"))
+ goto fail;
+
+ addrlen = sizeof(addr);
+ err = make_sockaddr(AF_INET6, IPV6_IFACE_ADDR, UDP_TEST_PORT,
+ (void *)&addr, &addrlen);
+ if (!ASSERT_OK(err, "make_sockaddr"))
+ goto fail;
+ sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (!ASSERT_NEQ(sockfd, -1, "socket"))
+ goto fail;
+ err = sendto(sockfd, buf, sizeof(buf), 0, (void *)&addr, addrlen);
+ close(sockfd);
+ if (!ASSERT_EQ(err, sizeof(buf), "send"))
+ goto fail;
+
+ ASSERT_TRUE(skel->bss->init_csum_partial, "init_csum_partial");
+ ASSERT_TRUE(skel->bss->final_csum_none, "final_csum_none");
+ ASSERT_FALSE(skel->bss->broken_csum_start, "broken_csum_start");
+
+fail:
+ if (nstoken) {
+ bpf_tc_hook_destroy(&qdisc_hook);
+ close_netns(nstoken);
+ }
+ system("ip netns del " NS_TEST " >& /dev/null");
+ decap_sanity__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/jeq_infer_not_null.c b/tools/testing/selftests/bpf/prog_tests/jeq_infer_not_null.c
new file mode 100644
index 00000000000000..3add34df57678d
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/jeq_infer_not_null.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <test_progs.h>
+#include "jeq_infer_not_null_fail.skel.h"
+
+void test_jeq_infer_not_null(void)
+{
+ RUN_TESTS(jeq_infer_not_null_fail);
+}
diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
index 2cf0c7a3fe2326..567e07c19ecc96 100644
--- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
+++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c
@@ -30,6 +30,8 @@
#define MAX_STRERR_LEN 256
#define MAX_TEST_NAME 80
+#define __always_unused __attribute__((__unused__))
+
#define _FAIL(errnum, fmt...) \
({ \
error_at_line(0, (errnum), __func__, __LINE__, fmt); \
@@ -321,7 +323,8 @@ static int socket_loopback(int family, int sotype)
return socket_loopback_reuseport(family, sotype, -1);
}
-static void test_insert_invalid(int family, int sotype, int mapfd)
+static void test_insert_invalid(struct test_sockmap_listen *skel __always_unused,
+ int family, int sotype, int mapfd)
{
u32 key = 0;
u64 value;
@@ -338,7 +341,8 @@ static void test_insert_invalid(int family, int sotype, int mapfd)
FAIL_ERRNO("map_update: expected EBADF");
}
-static void test_insert_opened(int family, int sotype, int mapfd)
+static void test_insert_opened(struct test_sockmap_listen *skel __always_unused,
+ int family, int sotype, int mapfd)
{
u32 key = 0;
u64 value;
@@ -359,7 +363,8 @@ static void test_insert_opened(int family, int sotype, int mapfd)
xclose(s);
}
-static void test_insert_bound(int family, int sotype, int mapfd)
+static void test_insert_bound(struct test_sockmap_listen *skel __always_unused,
+ int family, int sotype, int mapfd)
{
struct sockaddr_storage addr;
socklen_t len;
@@ -386,7 +391,8 @@ close:
xclose(s);
}
-static void test_insert(int family, int sotype, int mapfd)
+static void test_insert(struct test_sockmap_listen *skel __always_unused,
+ int family, int sotype, int mapfd)
{
u64 value;
u32 key;
@@ -402,7 +408,8 @@ static void test_insert(int family, int sotype, int mapfd)
xclose(s);
}
-static void test_delete_after_insert(int family, int sotype, int mapfd)
+static void test_delete_after_insert(struct test_sockmap_listen *skel __always_unused,
+ int family, int sotype, int mapfd)
{
u64 value;
u32 key;
@@ -419,7 +426,8 @@ static void test_delete_after_insert(int family, int sotype, int mapfd)
xclose(s);
}
-static void test_delete_after_close(int family, int sotype, int mapfd)
+static void test_delete_after_close(struct test_sockmap_listen *skel __always_unused,
+ int family, int sotype, int mapfd)
{
int err, s;
u64 value;
@@ -442,7 +450,8 @@ static void test_delete_after_close(int family, int sotype, int mapfd)
FAIL_ERRNO("map_delete: expected EINVAL/EINVAL");
}
-static void test_lookup_after_insert(int family, int sotype, int mapfd)
+static void test_lookup_after_insert(struct test_sockmap_listen *skel __always_unused,
+ int family, int sotype, int mapfd)
{
u64 cookie, value;
socklen_t len;
@@ -470,7 +479,8 @@ static void test_lookup_after_insert(int family, int sotype, int mapfd)
xclose(s);
}
-static void test_lookup_after_delete(int family, int sotype, int mapfd)
+static void test_lookup_after_delete(struct test_sockmap_listen *skel __always_unused,
+ int family, int sotype, int mapfd)
{
int err, s;
u64 value;
@@ -493,7 +503,8 @@ static void test_lookup_after_delete(int family, int sotype, int mapfd)
xclose(s);
}
-static void test_lookup_32_bit_value(int family, int sotype, int mapfd)
+static void test_lookup_32_bit_value(struct test_sockmap_listen *skel __always_unused,
+ int family, int sotype, int mapfd)
{
u32 key, value32;
int err, s;
@@ -523,7 +534,8 @@ close:
xclose(s);
}
-static void test_update_existing(int family, int sotype, int mapfd)
+static void test_update_existing(struct test_sockmap_listen *skel __always_unused,
+ int family, int sotype, int mapfd)
{
int s1, s2;
u64 value;
@@ -551,7 +563,7 @@ close_s1:
/* Exercise the code path where we destroy child sockets that never
* got accept()'ed, aka orphans, when parent socket gets closed.
*/
-static void test_destroy_orphan_child(int family, int sotype, int mapfd)
+static void do_destroy_orphan_child(int family, int sotype, int mapfd)
{
struct sockaddr_storage addr;
socklen_t len;
@@ -582,10 +594,38 @@ close_srv:
xclose(s);
}
+static void test_destroy_orphan_child(struct test_sockmap_listen *skel,
+ int family, int sotype, int mapfd)
+{
+ int msg_verdict = bpf_program__fd(skel->progs.prog_msg_verdict);
+ int skb_verdict = bpf_program__fd(skel->progs.prog_skb_verdict);
+ const struct test {
+ int progfd;
+ enum bpf_attach_type atype;
+ } tests[] = {
+ { -1, -1 },
+ { msg_verdict, BPF_SK_MSG_VERDICT },
+ { skb_verdict, BPF_SK_SKB_VERDICT },
+ };
+ const struct test *t;
+
+ for (t = tests; t < tests + ARRAY_SIZE(tests); t++) {
+ if (t->progfd != -1 &&
+ xbpf_prog_attach(t->progfd, mapfd, t->atype, 0) != 0)
+ return;
+
+ do_destroy_orphan_child(family, sotype, mapfd);
+
+ if (t->progfd != -1)
+ xbpf_prog_detach2(t->progfd, mapfd, t->atype);
+ }
+}
+
/* Perform a passive open after removing listening socket from SOCKMAP
* to ensure that callbacks get restored properly.
*/
-static void test_clone_after_delete(int family, int sotype, int mapfd)
+static void test_clone_after_delete(struct test_sockmap_listen *skel __always_unused,
+ int family, int sotype, int mapfd)
{
struct sockaddr_storage addr;
socklen_t len;
@@ -621,7 +661,8 @@ close_srv:
* SOCKMAP, but got accept()'ed only after the parent has been removed
* from SOCKMAP, gets cloned without parent psock state or callbacks.
*/
-static void test_accept_after_delete(int family, int sotype, int mapfd)
+static void test_accept_after_delete(struct test_sockmap_listen *skel __always_unused,
+ int family, int sotype, int mapfd)
{
struct sockaddr_storage addr;
const u32 zero = 0;
@@ -675,7 +716,8 @@ close_srv:
/* Check that child socket that got created and accepted while parent
* was in a SOCKMAP is cloned without parent psock state or callbacks.
*/
-static void test_accept_before_delete(int family, int sotype, int mapfd)
+static void test_accept_before_delete(struct test_sockmap_listen *skel __always_unused,
+ int family, int sotype, int mapfd)
{
struct sockaddr_storage addr;
const u32 zero = 0, one = 1;
@@ -784,7 +826,8 @@ done:
return NULL;
}
-static void test_syn_recv_insert_delete(int family, int sotype, int mapfd)
+static void test_syn_recv_insert_delete(struct test_sockmap_listen *skel __always_unused,
+ int family, int sotype, int mapfd)
{
struct connect_accept_ctx ctx = { 0 };
struct sockaddr_storage addr;
@@ -847,7 +890,8 @@ static void *listen_thread(void *arg)
return NULL;
}
-static void test_race_insert_listen(int family, int socktype, int mapfd)
+static void test_race_insert_listen(struct test_sockmap_listen *skel __always_unused,
+ int family, int socktype, int mapfd)
{
struct connect_accept_ctx ctx = { 0 };
const u32 zero = 0;
@@ -1473,7 +1517,8 @@ static void test_ops(struct test_sockmap_listen *skel, struct bpf_map *map,
int family, int sotype)
{
const struct op_test {
- void (*fn)(int family, int sotype, int mapfd);
+ void (*fn)(struct test_sockmap_listen *skel,
+ int family, int sotype, int mapfd);
const char *name;
int sotype;
} tests[] = {
@@ -1520,7 +1565,7 @@ static void test_ops(struct test_sockmap_listen *skel, struct bpf_map *map,
if (!test__start_subtest(s))
continue;
- t->fn(family, sotype, map_fd);
+ t->fn(skel, family, sotype, map_fd);
test_ops_cleanup(map);
}
}
diff --git a/tools/testing/selftests/bpf/progs/bpf_tracing_net.h b/tools/testing/selftests/bpf/progs/bpf_tracing_net.h
index b394817126cf9e..cfed4df490f35b 100644
--- a/tools/testing/selftests/bpf/progs/bpf_tracing_net.h
+++ b/tools/testing/selftests/bpf/progs/bpf_tracing_net.h
@@ -50,6 +50,12 @@
#define ICSK_TIME_LOSS_PROBE 5
#define ICSK_TIME_REO_TIMEOUT 6
+#define ETH_HLEN 14
+#define ETH_P_IPV6 0x86DD
+
+#define CHECKSUM_NONE 0
+#define CHECKSUM_PARTIAL 3
+
#define IFNAMSIZ 16
#define RTF_GATEWAY 0x0002
diff --git a/tools/testing/selftests/bpf/progs/decap_sanity.c b/tools/testing/selftests/bpf/progs/decap_sanity.c
new file mode 100644
index 00000000000000..bd3c657c58a794
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/decap_sanity.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
+
+#include "vmlinux.h"
+#include "bpf_tracing_net.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_endian.h>
+
+#define UDP_TEST_PORT 7777
+
+void *bpf_cast_to_kern_ctx(void *) __ksym;
+bool init_csum_partial = false;
+bool final_csum_none = false;
+bool broken_csum_start = false;
+
+static unsigned int skb_headlen(const struct sk_buff *skb)
+{
+ return skb->len - skb->data_len;
+}
+
+static unsigned int skb_headroom(const struct sk_buff *skb)
+{
+ return skb->data - skb->head;
+}
+
+static int skb_checksum_start_offset(const struct sk_buff *skb)
+{
+ return skb->csum_start - skb_headroom(skb);
+}
+
+SEC("tc")
+int decap_sanity(struct __sk_buff *skb)
+{
+ struct sk_buff *kskb;
+ struct ipv6hdr ip6h;
+ struct udphdr udph;
+ int err;
+
+ if (skb->protocol != __bpf_constant_htons(ETH_P_IPV6))
+ return TC_ACT_SHOT;
+
+ if (bpf_skb_load_bytes(skb, ETH_HLEN, &ip6h, sizeof(ip6h)))
+ return TC_ACT_SHOT;
+
+ if (ip6h.nexthdr != IPPROTO_UDP)
+ return TC_ACT_SHOT;
+
+ if (bpf_skb_load_bytes(skb, ETH_HLEN + sizeof(ip6h), &udph, sizeof(udph)))
+ return TC_ACT_SHOT;
+
+ if (udph.dest != __bpf_constant_htons(UDP_TEST_PORT))
+ return TC_ACT_SHOT;
+
+ kskb = bpf_cast_to_kern_ctx(skb);
+ init_csum_partial = (kskb->ip_summed == CHECKSUM_PARTIAL);
+ err = bpf_skb_adjust_room(skb, -(s32)(ETH_HLEN + sizeof(ip6h) + sizeof(udph)),
+ 1, BPF_F_ADJ_ROOM_FIXED_GSO);
+ if (err)
+ return TC_ACT_SHOT;
+ final_csum_none = (kskb->ip_summed == CHECKSUM_NONE);
+ if (kskb->ip_summed == CHECKSUM_PARTIAL &&
+ (unsigned int)skb_checksum_start_offset(kskb) >= skb_headlen(kskb))
+ broken_csum_start = true;
+
+ return TC_ACT_SHOT;
+}
+
+char __license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/jeq_infer_not_null_fail.c b/tools/testing/selftests/bpf/progs/jeq_infer_not_null_fail.c
new file mode 100644
index 00000000000000..f46965053acb2e
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/jeq_infer_not_null_fail.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+char _license[] SEC("license") = "GPL";
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(max_entries, 1);
+ __type(key, u64);
+ __type(value, u64);
+} m_hash SEC(".maps");
+
+SEC("?raw_tp")
+__failure __msg("R8 invalid mem access 'map_value_or_null")
+int jeq_infer_not_null_ptr_to_btfid(void *ctx)
+{
+ struct bpf_map *map = (struct bpf_map *)&m_hash;
+ struct bpf_map *inner_map = map->inner_map_meta;
+ u64 key = 0, ret = 0, *val;
+
+ val = bpf_map_lookup_elem(map, &key);
+ /* Do not mark ptr as non-null if one of them is
+ * PTR_TO_BTF_ID (R9), reject because of invalid
+ * access to map value (R8).
+ *
+ * Here, we need to inline those insns to access
+ * R8 directly, since compiler may use other reg
+ * once it figures out val==inner_map.
+ */
+ asm volatile("r8 = %[val];\n"
+ "r9 = %[inner_map];\n"
+ "if r8 != r9 goto +1;\n"
+ "%[ret] = *(u64 *)(r8 +0);\n"
+ : [ret] "+r"(ret)
+ : [inner_map] "r"(inner_map), [val] "r"(val)
+ : "r8", "r9");
+
+ return ret;
+}
diff --git a/tools/testing/selftests/bpf/verifier/search_pruning.c b/tools/testing/selftests/bpf/verifier/search_pruning.c
index 68b14fdfebdb1f..d63fd8991b03aa 100644
--- a/tools/testing/selftests/bpf/verifier/search_pruning.c
+++ b/tools/testing/selftests/bpf/verifier/search_pruning.c
@@ -225,3 +225,39 @@
.result_unpriv = ACCEPT,
.insn_processed = 15,
},
+/* The test performs a conditional 64-bit write to a stack location
+ * fp[-8], this is followed by an unconditional 8-bit write to fp[-8],
+ * then data is read from fp[-8]. This sequence is unsafe.
+ *
+ * The test would be mistakenly marked as safe w/o dst register parent
+ * preservation in verifier.c:copy_register_state() function.
+ *
+ * Note the usage of BPF_F_TEST_STATE_FREQ to force creation of the
+ * checkpoint state after conditional 64-bit assignment.
+ */
+{
+ "write tracking and register parent chain bug",
+ .insns = {
+ /* r6 = ktime_get_ns() */
+ BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns),
+ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
+ /* r0 = ktime_get_ns() */
+ BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns),
+ /* if r0 > r6 goto +1 */
+ BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_6, 1),
+ /* *(u64 *)(r10 - 8) = 0xdeadbeef */
+ BPF_ST_MEM(BPF_DW, BPF_REG_FP, -8, 0xdeadbeef),
+ /* r1 = 42 */
+ BPF_MOV64_IMM(BPF_REG_1, 42),
+ /* *(u8 *)(r10 - 8) = r1 */
+ BPF_STX_MEM(BPF_B, BPF_REG_FP, BPF_REG_1, -8),
+ /* r2 = *(u64 *)(r10 - 8) */
+ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_FP, -8),
+ /* exit(0) */
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ },
+ .flags = BPF_F_TEST_STATE_FREQ,
+ .errstr = "invalid read from stack off -8+1 size 8",
+ .result = REJECT,
+},
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index 186e1c26867ef5..75c100de90ffe2 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -268,6 +268,7 @@ TEST_MATRIX=(
# Taking away all CPUs from parent or itself if there are tasks
# will make the partition invalid.
" S+ C2-3:P1:S+ C3:P1 . . T C2-3 . . 0 A1:2-3,A2:2-3 A1:P1,A2:P-1"
+ " S+ C3:P1:S+ C3 . . T P1 . . 0 A1:3,A2:3 A1:P1,A2:P-1"
" S+ $SETUP_A123_PARTITIONS . T:C2-3 . . . 0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P-1,A3:P-1"
" S+ $SETUP_A123_PARTITIONS . T:C2-3:C1-3 . . . 0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1"
diff --git a/tools/testing/selftests/drivers/net/ocelot/tc_flower_chains.sh b/tools/testing/selftests/drivers/net/ocelot/tc_flower_chains.sh
index 9c79bbcce5a879..aff0a59f92d9ae 100755
--- a/tools/testing/selftests/drivers/net/ocelot/tc_flower_chains.sh
+++ b/tools/testing/selftests/drivers/net/ocelot/tc_flower_chains.sh
@@ -246,7 +246,7 @@ test_vlan_ingress_modify()
bridge vlan add dev $swp2 vid 300
tc filter add dev $swp1 ingress chain $(IS1 2) pref 3 \
- protocol 802.1Q flower skip_sw vlan_id 200 \
+ protocol 802.1Q flower skip_sw vlan_id 200 src_mac $h1_mac \
action vlan modify id 300 \
action goto chain $(IS2 0 0)
diff --git a/tools/testing/selftests/filesystems/fat/run_fat_tests.sh b/tools/testing/selftests/filesystems/fat/run_fat_tests.sh
index 7f35dc3d15dfa6..7f35dc3d15dfa6 100644..100755
--- a/tools/testing/selftests/filesystems/fat/run_fat_tests.sh
+++ b/tools/testing/selftests/filesystems/fat/run_fat_tests.sh
diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index 6ce8c488d62eac..6d9381d60172f3 100644
--- a/tools/testing/selftests/kvm/.gitignore
+++ b/tools/testing/selftests/kvm/.gitignore
@@ -1,86 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
-/aarch64/aarch32_id_regs
-/aarch64/arch_timer
-/aarch64/debug-exceptions
-/aarch64/get-reg-list
-/aarch64/hypercalls
-/aarch64/page_fault_test
-/aarch64/psci_test
-/aarch64/vcpu_width_config
-/aarch64/vgic_init
-/aarch64/vgic_irq
-/s390x/memop
-/s390x/resets
-/s390x/sync_regs_test
-/s390x/tprot
-/x86_64/amx_test
-/x86_64/cpuid_test
-/x86_64/cr4_cpuid_sync_test
-/x86_64/debug_regs
-/x86_64/exit_on_emulation_failure_test
-/x86_64/fix_hypercall_test
-/x86_64/get_msr_index_features
-/x86_64/kvm_clock_test
-/x86_64/kvm_pv_test
-/x86_64/hyperv_clock
-/x86_64/hyperv_cpuid
-/x86_64/hyperv_evmcs
-/x86_64/hyperv_features
-/x86_64/hyperv_ipi
-/x86_64/hyperv_svm_test
-/x86_64/hyperv_tlb_flush
-/x86_64/max_vcpuid_cap_test
-/x86_64/mmio_warning_test
-/x86_64/monitor_mwait_test
-/x86_64/nested_exceptions_test
-/x86_64/nx_huge_pages_test
-/x86_64/platform_info_test
-/x86_64/pmu_event_filter_test
-/x86_64/set_boot_cpu_id
-/x86_64/set_sregs_test
-/x86_64/sev_migrate_tests
-/x86_64/smaller_maxphyaddr_emulation_test
-/x86_64/smm_test
-/x86_64/state_test
-/x86_64/svm_vmcall_test
-/x86_64/svm_int_ctl_test
-/x86_64/svm_nested_soft_inject_test
-/x86_64/svm_nested_shutdown_test
-/x86_64/sync_regs_test
-/x86_64/tsc_msrs_test
-/x86_64/tsc_scaling_sync
-/x86_64/ucna_injection_test
-/x86_64/userspace_io_test
-/x86_64/userspace_msr_exit_test
-/x86_64/vmx_apic_access_test
-/x86_64/vmx_close_while_nested_test
-/x86_64/vmx_dirty_log_test
-/x86_64/vmx_exception_with_invalid_guest_state
-/x86_64/vmx_invalid_nested_guest_state
-/x86_64/vmx_msrs_test
-/x86_64/vmx_preemption_timer_test
-/x86_64/vmx_set_nested_state_test
-/x86_64/vmx_tsc_adjust_test
-/x86_64/vmx_nested_tsc_scaling_test
-/x86_64/xapic_ipi_test
-/x86_64/xapic_state_test
-/x86_64/xen_shinfo_test
-/x86_64/xen_vmcall_test
-/x86_64/xss_msr_test
-/x86_64/vmx_pmu_caps_test
-/x86_64/triple_fault_event_test
-/access_tracking_perf_test
-/demand_paging_test
-/dirty_log_test
-/dirty_log_perf_test
-/hardware_disable_test
-/kvm_create_max_vcpus
-/kvm_page_table_test
-/max_guest_memory_test
-/memslot_modification_stress_test
-/memslot_perf_test
-/rseq_test
-/set_memory_region_test
-/steal_time
-/kvm_binary_stats_test
-/system_counter_offset_test
+*
+!/**/
+!*.c
+!*.h
+!*.S
+!*.sh
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 947676983da1f2..1750f91dd93629 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -7,35 +7,14 @@ top_srcdir = ../../../..
include $(top_srcdir)/scripts/subarch.include
ARCH ?= $(SUBARCH)
-# For cross-builds to work, UNAME_M has to map to ARCH and arch specific
-# directories and targets in this Makefile. "uname -m" doesn't map to
-# arch specific sub-directory names.
-#
-# UNAME_M variable to used to run the compiles pointing to the right arch
-# directories and build the right targets for these supported architectures.
-#
-# TEST_GEN_PROGS and LIBKVM are set using UNAME_M variable.
-# LINUX_TOOL_ARCH_INCLUDE is set using ARCH variable.
-#
-# x86_64 targets are named to include x86_64 as a suffix and directories
-# for includes are in x86_64 sub-directory. s390x and aarch64 follow the
-# same convention. "uname -m" doesn't result in the correct mapping for
-# s390x and aarch64.
-#
-# No change necessary for x86_64
-UNAME_M := $(shell uname -m)
-
-# Set UNAME_M for arm64 compile/install to work
-ifeq ($(ARCH),arm64)
- UNAME_M := aarch64
-endif
-# Set UNAME_M s390x compile/install to work
-ifeq ($(ARCH),s390)
- UNAME_M := s390x
-endif
-# Set UNAME_M riscv compile/install to work
-ifeq ($(ARCH),riscv)
- UNAME_M := riscv
+ifeq ($(ARCH),x86)
+ ARCH_DIR := x86_64
+else ifeq ($(ARCH),arm64)
+ ARCH_DIR := aarch64
+else ifeq ($(ARCH),s390)
+ ARCH_DIR := s390x
+else
+ ARCH_DIR := $(ARCH)
endif
LIBKVM += lib/assert.c
@@ -196,10 +175,15 @@ TEST_GEN_PROGS_riscv += kvm_page_table_test
TEST_GEN_PROGS_riscv += set_memory_region_test
TEST_GEN_PROGS_riscv += kvm_binary_stats_test
-TEST_PROGS += $(TEST_PROGS_$(UNAME_M))
-TEST_GEN_PROGS += $(TEST_GEN_PROGS_$(UNAME_M))
-TEST_GEN_PROGS_EXTENDED += $(TEST_GEN_PROGS_EXTENDED_$(UNAME_M))
-LIBKVM += $(LIBKVM_$(UNAME_M))
+TEST_PROGS += $(TEST_PROGS_$(ARCH_DIR))
+TEST_GEN_PROGS += $(TEST_GEN_PROGS_$(ARCH_DIR))
+TEST_GEN_PROGS_EXTENDED += $(TEST_GEN_PROGS_EXTENDED_$(ARCH_DIR))
+LIBKVM += $(LIBKVM_$(ARCH_DIR))
+
+# lib.mak defines $(OUTPUT), prepends $(OUTPUT)/ to $(TEST_GEN_PROGS), and most
+# importantly defines, i.e. overwrites, $(CC) (unless `make -e` or `make CC=`,
+# which causes the environment variable to override the makefile).
+include ../lib.mk
INSTALL_HDR_PATH = $(top_srcdir)/usr
LINUX_HDR_PATH = $(INSTALL_HDR_PATH)/include/
@@ -210,25 +194,23 @@ else
LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/$(ARCH)/include
endif
CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \
+ -Wno-gnu-variable-sized-type-not-at-end \
+ -fno-builtin-memcmp -fno-builtin-memcpy -fno-builtin-memset \
-fno-stack-protector -fno-PIE -I$(LINUX_TOOL_INCLUDE) \
-I$(LINUX_TOOL_ARCH_INCLUDE) -I$(LINUX_HDR_PATH) -Iinclude \
- -I$(<D) -Iinclude/$(UNAME_M) -I ../rseq -I.. $(EXTRA_CFLAGS) \
+ -I$(<D) -Iinclude/$(ARCH_DIR) -I ../rseq -I.. $(EXTRA_CFLAGS) \
$(KHDR_INCLUDES)
-no-pie-option := $(call try-run, echo 'int main() { return 0; }' | \
- $(CC) -Werror -no-pie -x c - -o "$$TMP", -no-pie)
+no-pie-option := $(call try-run, echo 'int main(void) { return 0; }' | \
+ $(CC) -Werror $(CFLAGS) -no-pie -x c - -o "$$TMP", -no-pie)
# On s390, build the testcases KVM-enabled
-pgste-option = $(call try-run, echo 'int main() { return 0; }' | \
+pgste-option = $(call try-run, echo 'int main(void) { return 0; }' | \
$(CC) -Werror -Wl$(comma)--s390-pgste -x c - -o "$$TMP",-Wl$(comma)--s390-pgste)
LDLIBS += -ldl
LDFLAGS += -pthread $(no-pie-option) $(pgste-option)
-# After inclusion, $(OUTPUT) is defined and
-# $(TEST_GEN_PROGS) starts with $(OUTPUT)/
-include ../lib.mk
-
LIBKVM_C := $(filter %.c,$(LIBKVM))
LIBKVM_S := $(filter %.S,$(LIBKVM))
LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C))
diff --git a/tools/testing/selftests/kvm/aarch64/page_fault_test.c b/tools/testing/selftests/kvm/aarch64/page_fault_test.c
index 95d22cfb7b41a2..54680dc5887fae 100644
--- a/tools/testing/selftests/kvm/aarch64/page_fault_test.c
+++ b/tools/testing/selftests/kvm/aarch64/page_fault_test.c
@@ -117,7 +117,7 @@ static void guest_cas(void)
GUEST_ASSERT(guest_check_lse());
asm volatile(".arch_extension lse\n"
"casal %0, %1, [%2]\n"
- :: "r" (0), "r" (TEST_DATA), "r" (guest_test_memory));
+ :: "r" (0ul), "r" (TEST_DATA), "r" (guest_test_memory));
val = READ_ONCE(*guest_test_memory);
GUEST_ASSERT_EQ(val, TEST_DATA);
}
@@ -237,6 +237,11 @@ static void guest_check_s1ptw_wr_in_dirty_log(void)
GUEST_SYNC(CMD_CHECK_S1PTW_WR_IN_DIRTY_LOG);
}
+static void guest_check_no_s1ptw_wr_in_dirty_log(void)
+{
+ GUEST_SYNC(CMD_CHECK_NO_S1PTW_WR_IN_DIRTY_LOG);
+}
+
static void guest_exec(void)
{
int (*code)(void) = (int (*)(void))TEST_EXEC_GVA;
@@ -304,7 +309,7 @@ static struct uffd_args {
/* Returns true to continue the test, and false if it should be skipped. */
static int uffd_generic_handler(int uffd_mode, int uffd, struct uffd_msg *msg,
- struct uffd_args *args, bool expect_write)
+ struct uffd_args *args)
{
uint64_t addr = msg->arg.pagefault.address;
uint64_t flags = msg->arg.pagefault.flags;
@@ -313,7 +318,6 @@ static int uffd_generic_handler(int uffd_mode, int uffd, struct uffd_msg *msg,
TEST_ASSERT(uffd_mode == UFFDIO_REGISTER_MODE_MISSING,
"The only expected UFFD mode is MISSING");
- ASSERT_EQ(!!(flags & UFFD_PAGEFAULT_FLAG_WRITE), expect_write);
ASSERT_EQ(addr, (uint64_t)args->hva);
pr_debug("uffd fault: addr=%p write=%d\n",
@@ -337,19 +341,14 @@ static int uffd_generic_handler(int uffd_mode, int uffd, struct uffd_msg *msg,
return 0;
}
-static int uffd_pt_write_handler(int mode, int uffd, struct uffd_msg *msg)
-{
- return uffd_generic_handler(mode, uffd, msg, &pt_args, true);
-}
-
-static int uffd_data_write_handler(int mode, int uffd, struct uffd_msg *msg)
+static int uffd_pt_handler(int mode, int uffd, struct uffd_msg *msg)
{
- return uffd_generic_handler(mode, uffd, msg, &data_args, true);
+ return uffd_generic_handler(mode, uffd, msg, &pt_args);
}
-static int uffd_data_read_handler(int mode, int uffd, struct uffd_msg *msg)
+static int uffd_data_handler(int mode, int uffd, struct uffd_msg *msg)
{
- return uffd_generic_handler(mode, uffd, msg, &data_args, false);
+ return uffd_generic_handler(mode, uffd, msg, &data_args);
}
static void setup_uffd_args(struct userspace_mem_region *region,
@@ -471,9 +470,12 @@ static bool handle_cmd(struct kvm_vm *vm, int cmd)
{
struct userspace_mem_region *data_region, *pt_region;
bool continue_test = true;
+ uint64_t pte_gpa, pte_pg;
data_region = vm_get_mem_region(vm, MEM_REGION_TEST_DATA);
pt_region = vm_get_mem_region(vm, MEM_REGION_PT);
+ pte_gpa = addr_hva2gpa(vm, virt_get_pte_hva(vm, TEST_GVA));
+ pte_pg = (pte_gpa - pt_region->region.guest_phys_addr) / getpagesize();
if (cmd == CMD_SKIP_TEST)
continue_test = false;
@@ -486,13 +488,13 @@ static bool handle_cmd(struct kvm_vm *vm, int cmd)
TEST_ASSERT(check_write_in_dirty_log(vm, data_region, 0),
"Missing write in dirty log");
if (cmd & CMD_CHECK_S1PTW_WR_IN_DIRTY_LOG)
- TEST_ASSERT(check_write_in_dirty_log(vm, pt_region, 0),
+ TEST_ASSERT(check_write_in_dirty_log(vm, pt_region, pte_pg),
"Missing s1ptw write in dirty log");
if (cmd & CMD_CHECK_NO_WRITE_IN_DIRTY_LOG)
TEST_ASSERT(!check_write_in_dirty_log(vm, data_region, 0),
"Unexpected write in dirty log");
if (cmd & CMD_CHECK_NO_S1PTW_WR_IN_DIRTY_LOG)
- TEST_ASSERT(!check_write_in_dirty_log(vm, pt_region, 0),
+ TEST_ASSERT(!check_write_in_dirty_log(vm, pt_region, pte_pg),
"Unexpected s1ptw write in dirty log");
return continue_test;
@@ -797,7 +799,7 @@ static void help(char *name)
.expected_events = { .uffd_faults = _uffd_faults, }, \
}
-#define TEST_DIRTY_LOG(_access, _with_af, _test_check) \
+#define TEST_DIRTY_LOG(_access, _with_af, _test_check, _pt_check) \
{ \
.name = SCAT3(dirty_log, _access, _with_af), \
.data_memslot_flags = KVM_MEM_LOG_DIRTY_PAGES, \
@@ -805,13 +807,12 @@ static void help(char *name)
.guest_prepare = { _PREPARE(_with_af), \
_PREPARE(_access) }, \
.guest_test = _access, \
- .guest_test_check = { _CHECK(_with_af), _test_check, \
- guest_check_s1ptw_wr_in_dirty_log}, \
+ .guest_test_check = { _CHECK(_with_af), _test_check, _pt_check }, \
.expected_events = { 0 }, \
}
#define TEST_UFFD_AND_DIRTY_LOG(_access, _with_af, _uffd_data_handler, \
- _uffd_faults, _test_check) \
+ _uffd_faults, _test_check, _pt_check) \
{ \
.name = SCAT3(uffd_and_dirty_log, _access, _with_af), \
.data_memslot_flags = KVM_MEM_LOG_DIRTY_PAGES, \
@@ -820,16 +821,17 @@ static void help(char *name)
_PREPARE(_access) }, \
.guest_test = _access, \
.mem_mark_cmd = CMD_HOLE_DATA | CMD_HOLE_PT, \
- .guest_test_check = { _CHECK(_with_af), _test_check }, \
+ .guest_test_check = { _CHECK(_with_af), _test_check, _pt_check }, \
.uffd_data_handler = _uffd_data_handler, \
- .uffd_pt_handler = uffd_pt_write_handler, \
+ .uffd_pt_handler = uffd_pt_handler, \
.expected_events = { .uffd_faults = _uffd_faults, }, \
}
#define TEST_RO_MEMSLOT(_access, _mmio_handler, _mmio_exits) \
{ \
- .name = SCAT3(ro_memslot, _access, _with_af), \
+ .name = SCAT2(ro_memslot, _access), \
.data_memslot_flags = KVM_MEM_READONLY, \
+ .pt_memslot_flags = KVM_MEM_READONLY, \
.guest_prepare = { _PREPARE(_access) }, \
.guest_test = _access, \
.mmio_handler = _mmio_handler, \
@@ -840,6 +842,7 @@ static void help(char *name)
{ \
.name = SCAT2(ro_memslot_no_syndrome, _access), \
.data_memslot_flags = KVM_MEM_READONLY, \
+ .pt_memslot_flags = KVM_MEM_READONLY, \
.guest_test = _access, \
.fail_vcpu_run_handler = fail_vcpu_run_mmio_no_syndrome_handler, \
.expected_events = { .fail_vcpu_runs = 1 }, \
@@ -848,9 +851,9 @@ static void help(char *name)
#define TEST_RO_MEMSLOT_AND_DIRTY_LOG(_access, _mmio_handler, _mmio_exits, \
_test_check) \
{ \
- .name = SCAT3(ro_memslot, _access, _with_af), \
+ .name = SCAT2(ro_memslot, _access), \
.data_memslot_flags = KVM_MEM_READONLY | KVM_MEM_LOG_DIRTY_PAGES, \
- .pt_memslot_flags = KVM_MEM_LOG_DIRTY_PAGES, \
+ .pt_memslot_flags = KVM_MEM_READONLY | KVM_MEM_LOG_DIRTY_PAGES, \
.guest_prepare = { _PREPARE(_access) }, \
.guest_test = _access, \
.guest_test_check = { _test_check }, \
@@ -862,7 +865,7 @@ static void help(char *name)
{ \
.name = SCAT2(ro_memslot_no_syn_and_dlog, _access), \
.data_memslot_flags = KVM_MEM_READONLY | KVM_MEM_LOG_DIRTY_PAGES, \
- .pt_memslot_flags = KVM_MEM_LOG_DIRTY_PAGES, \
+ .pt_memslot_flags = KVM_MEM_READONLY | KVM_MEM_LOG_DIRTY_PAGES, \
.guest_test = _access, \
.guest_test_check = { _test_check }, \
.fail_vcpu_run_handler = fail_vcpu_run_mmio_no_syndrome_handler, \
@@ -874,11 +877,12 @@ static void help(char *name)
{ \
.name = SCAT2(ro_memslot_uffd, _access), \
.data_memslot_flags = KVM_MEM_READONLY, \
+ .pt_memslot_flags = KVM_MEM_READONLY, \
.mem_mark_cmd = CMD_HOLE_DATA | CMD_HOLE_PT, \
.guest_prepare = { _PREPARE(_access) }, \
.guest_test = _access, \
.uffd_data_handler = _uffd_data_handler, \
- .uffd_pt_handler = uffd_pt_write_handler, \
+ .uffd_pt_handler = uffd_pt_handler, \
.mmio_handler = _mmio_handler, \
.expected_events = { .mmio_exits = _mmio_exits, \
.uffd_faults = _uffd_faults }, \
@@ -889,10 +893,11 @@ static void help(char *name)
{ \
.name = SCAT2(ro_memslot_no_syndrome, _access), \
.data_memslot_flags = KVM_MEM_READONLY, \
+ .pt_memslot_flags = KVM_MEM_READONLY, \
.mem_mark_cmd = CMD_HOLE_DATA | CMD_HOLE_PT, \
.guest_test = _access, \
.uffd_data_handler = _uffd_data_handler, \
- .uffd_pt_handler = uffd_pt_write_handler, \
+ .uffd_pt_handler = uffd_pt_handler, \
.fail_vcpu_run_handler = fail_vcpu_run_mmio_no_syndrome_handler, \
.expected_events = { .fail_vcpu_runs = 1, \
.uffd_faults = _uffd_faults }, \
@@ -933,44 +938,51 @@ static struct test_desc tests[] = {
* (S1PTW).
*/
TEST_UFFD(guest_read64, with_af, CMD_HOLE_DATA | CMD_HOLE_PT,
- uffd_data_read_handler, uffd_pt_write_handler, 2),
- /* no_af should also lead to a PT write. */
+ uffd_data_handler, uffd_pt_handler, 2),
TEST_UFFD(guest_read64, no_af, CMD_HOLE_DATA | CMD_HOLE_PT,
- uffd_data_read_handler, uffd_pt_write_handler, 2),
- /* Note how that cas invokes the read handler. */
+ uffd_data_handler, uffd_pt_handler, 2),
TEST_UFFD(guest_cas, with_af, CMD_HOLE_DATA | CMD_HOLE_PT,
- uffd_data_read_handler, uffd_pt_write_handler, 2),
+ uffd_data_handler, uffd_pt_handler, 2),
/*
* Can't test guest_at with_af as it's IMPDEF whether the AF is set.
* The S1PTW fault should still be marked as a write.
*/
TEST_UFFD(guest_at, no_af, CMD_HOLE_DATA | CMD_HOLE_PT,
- uffd_data_read_handler, uffd_pt_write_handler, 1),
+ uffd_no_handler, uffd_pt_handler, 1),
TEST_UFFD(guest_ld_preidx, with_af, CMD_HOLE_DATA | CMD_HOLE_PT,
- uffd_data_read_handler, uffd_pt_write_handler, 2),
+ uffd_data_handler, uffd_pt_handler, 2),
TEST_UFFD(guest_write64, with_af, CMD_HOLE_DATA | CMD_HOLE_PT,
- uffd_data_write_handler, uffd_pt_write_handler, 2),
+ uffd_data_handler, uffd_pt_handler, 2),
TEST_UFFD(guest_dc_zva, with_af, CMD_HOLE_DATA | CMD_HOLE_PT,
- uffd_data_write_handler, uffd_pt_write_handler, 2),
+ uffd_data_handler, uffd_pt_handler, 2),
TEST_UFFD(guest_st_preidx, with_af, CMD_HOLE_DATA | CMD_HOLE_PT,
- uffd_data_write_handler, uffd_pt_write_handler, 2),
+ uffd_data_handler, uffd_pt_handler, 2),
TEST_UFFD(guest_exec, with_af, CMD_HOLE_DATA | CMD_HOLE_PT,
- uffd_data_read_handler, uffd_pt_write_handler, 2),
+ uffd_data_handler, uffd_pt_handler, 2),
/*
* Try accesses when the data and PT memory regions are both
* tracked for dirty logging.
*/
- TEST_DIRTY_LOG(guest_read64, with_af, guest_check_no_write_in_dirty_log),
- /* no_af should also lead to a PT write. */
- TEST_DIRTY_LOG(guest_read64, no_af, guest_check_no_write_in_dirty_log),
- TEST_DIRTY_LOG(guest_ld_preidx, with_af, guest_check_no_write_in_dirty_log),
- TEST_DIRTY_LOG(guest_at, no_af, guest_check_no_write_in_dirty_log),
- TEST_DIRTY_LOG(guest_exec, with_af, guest_check_no_write_in_dirty_log),
- TEST_DIRTY_LOG(guest_write64, with_af, guest_check_write_in_dirty_log),
- TEST_DIRTY_LOG(guest_cas, with_af, guest_check_write_in_dirty_log),
- TEST_DIRTY_LOG(guest_dc_zva, with_af, guest_check_write_in_dirty_log),
- TEST_DIRTY_LOG(guest_st_preidx, with_af, guest_check_write_in_dirty_log),
+ TEST_DIRTY_LOG(guest_read64, with_af, guest_check_no_write_in_dirty_log,
+ guest_check_s1ptw_wr_in_dirty_log),
+ TEST_DIRTY_LOG(guest_read64, no_af, guest_check_no_write_in_dirty_log,
+ guest_check_no_s1ptw_wr_in_dirty_log),
+ TEST_DIRTY_LOG(guest_ld_preidx, with_af,
+ guest_check_no_write_in_dirty_log,
+ guest_check_s1ptw_wr_in_dirty_log),
+ TEST_DIRTY_LOG(guest_at, no_af, guest_check_no_write_in_dirty_log,
+ guest_check_no_s1ptw_wr_in_dirty_log),
+ TEST_DIRTY_LOG(guest_exec, with_af, guest_check_no_write_in_dirty_log,
+ guest_check_s1ptw_wr_in_dirty_log),
+ TEST_DIRTY_LOG(guest_write64, with_af, guest_check_write_in_dirty_log,
+ guest_check_s1ptw_wr_in_dirty_log),
+ TEST_DIRTY_LOG(guest_cas, with_af, guest_check_write_in_dirty_log,
+ guest_check_s1ptw_wr_in_dirty_log),
+ TEST_DIRTY_LOG(guest_dc_zva, with_af, guest_check_write_in_dirty_log,
+ guest_check_s1ptw_wr_in_dirty_log),
+ TEST_DIRTY_LOG(guest_st_preidx, with_af, guest_check_write_in_dirty_log,
+ guest_check_s1ptw_wr_in_dirty_log),
/*
* Access when the data and PT memory regions are both marked for
@@ -980,29 +992,43 @@ static struct test_desc tests[] = {
* fault, and nothing in the dirty log. Any S1PTW should result in
* a write in the dirty log and a userfaultfd write.
*/
- TEST_UFFD_AND_DIRTY_LOG(guest_read64, with_af, uffd_data_read_handler, 2,
- guest_check_no_write_in_dirty_log),
- /* no_af should also lead to a PT write. */
- TEST_UFFD_AND_DIRTY_LOG(guest_read64, no_af, uffd_data_read_handler, 2,
- guest_check_no_write_in_dirty_log),
- TEST_UFFD_AND_DIRTY_LOG(guest_ld_preidx, with_af, uffd_data_read_handler,
- 2, guest_check_no_write_in_dirty_log),
- TEST_UFFD_AND_DIRTY_LOG(guest_at, with_af, 0, 1,
- guest_check_no_write_in_dirty_log),
- TEST_UFFD_AND_DIRTY_LOG(guest_exec, with_af, uffd_data_read_handler, 2,
- guest_check_no_write_in_dirty_log),
- TEST_UFFD_AND_DIRTY_LOG(guest_write64, with_af, uffd_data_write_handler,
- 2, guest_check_write_in_dirty_log),
- TEST_UFFD_AND_DIRTY_LOG(guest_cas, with_af, uffd_data_read_handler, 2,
- guest_check_write_in_dirty_log),
- TEST_UFFD_AND_DIRTY_LOG(guest_dc_zva, with_af, uffd_data_write_handler,
- 2, guest_check_write_in_dirty_log),
+ TEST_UFFD_AND_DIRTY_LOG(guest_read64, with_af,
+ uffd_data_handler, 2,
+ guest_check_no_write_in_dirty_log,
+ guest_check_s1ptw_wr_in_dirty_log),
+ TEST_UFFD_AND_DIRTY_LOG(guest_read64, no_af,
+ uffd_data_handler, 2,
+ guest_check_no_write_in_dirty_log,
+ guest_check_no_s1ptw_wr_in_dirty_log),
+ TEST_UFFD_AND_DIRTY_LOG(guest_ld_preidx, with_af,
+ uffd_data_handler,
+ 2, guest_check_no_write_in_dirty_log,
+ guest_check_s1ptw_wr_in_dirty_log),
+ TEST_UFFD_AND_DIRTY_LOG(guest_at, with_af, uffd_no_handler, 1,
+ guest_check_no_write_in_dirty_log,
+ guest_check_s1ptw_wr_in_dirty_log),
+ TEST_UFFD_AND_DIRTY_LOG(guest_exec, with_af,
+ uffd_data_handler, 2,
+ guest_check_no_write_in_dirty_log,
+ guest_check_s1ptw_wr_in_dirty_log),
+ TEST_UFFD_AND_DIRTY_LOG(guest_write64, with_af,
+ uffd_data_handler,
+ 2, guest_check_write_in_dirty_log,
+ guest_check_s1ptw_wr_in_dirty_log),
+ TEST_UFFD_AND_DIRTY_LOG(guest_cas, with_af,
+ uffd_data_handler, 2,
+ guest_check_write_in_dirty_log,
+ guest_check_s1ptw_wr_in_dirty_log),
+ TEST_UFFD_AND_DIRTY_LOG(guest_dc_zva, with_af,
+ uffd_data_handler,
+ 2, guest_check_write_in_dirty_log,
+ guest_check_s1ptw_wr_in_dirty_log),
TEST_UFFD_AND_DIRTY_LOG(guest_st_preidx, with_af,
- uffd_data_write_handler, 2,
- guest_check_write_in_dirty_log),
-
+ uffd_data_handler, 2,
+ guest_check_write_in_dirty_log,
+ guest_check_s1ptw_wr_in_dirty_log),
/*
- * Try accesses when the data memory region is marked read-only
+ * Access when both the PT and data regions are marked read-only
* (with KVM_MEM_READONLY). Writes with a syndrome result in an
* MMIO exit, writes with no syndrome (e.g., CAS) result in a
* failed vcpu run, and reads/execs with and without syndroms do
@@ -1018,7 +1044,7 @@ static struct test_desc tests[] = {
TEST_RO_MEMSLOT_NO_SYNDROME(guest_st_preidx),
/*
- * Access when both the data region is both read-only and marked
+ * The PT and data regions are both read-only and marked
* for dirty logging at the same time. The expected result is that
* for writes there should be no write in the dirty log. The
* readonly handling is the same as if the memslot was not marked
@@ -1043,7 +1069,7 @@ static struct test_desc tests[] = {
guest_check_no_write_in_dirty_log),
/*
- * Access when the data region is both read-only and punched with
+ * The PT and data regions are both read-only and punched with
* holes tracked with userfaultfd. The expected result is the
* union of both userfaultfd and read-only behaviors. For example,
* write accesses result in a userfaultfd write fault and an MMIO
@@ -1051,22 +1077,15 @@ static struct test_desc tests[] = {
* no userfaultfd write fault. Reads result in userfaultfd getting
* triggered.
*/
- TEST_RO_MEMSLOT_AND_UFFD(guest_read64, 0, 0,
- uffd_data_read_handler, 2),
- TEST_RO_MEMSLOT_AND_UFFD(guest_ld_preidx, 0, 0,
- uffd_data_read_handler, 2),
- TEST_RO_MEMSLOT_AND_UFFD(guest_at, 0, 0,
- uffd_no_handler, 1),
- TEST_RO_MEMSLOT_AND_UFFD(guest_exec, 0, 0,
- uffd_data_read_handler, 2),
+ TEST_RO_MEMSLOT_AND_UFFD(guest_read64, 0, 0, uffd_data_handler, 2),
+ TEST_RO_MEMSLOT_AND_UFFD(guest_ld_preidx, 0, 0, uffd_data_handler, 2),
+ TEST_RO_MEMSLOT_AND_UFFD(guest_at, 0, 0, uffd_no_handler, 1),
+ TEST_RO_MEMSLOT_AND_UFFD(guest_exec, 0, 0, uffd_data_handler, 2),
TEST_RO_MEMSLOT_AND_UFFD(guest_write64, mmio_on_test_gpa_handler, 1,
- uffd_data_write_handler, 2),
- TEST_RO_MEMSLOT_NO_SYNDROME_AND_UFFD(guest_cas,
- uffd_data_read_handler, 2),
- TEST_RO_MEMSLOT_NO_SYNDROME_AND_UFFD(guest_dc_zva,
- uffd_no_handler, 1),
- TEST_RO_MEMSLOT_NO_SYNDROME_AND_UFFD(guest_st_preidx,
- uffd_no_handler, 1),
+ uffd_data_handler, 2),
+ TEST_RO_MEMSLOT_NO_SYNDROME_AND_UFFD(guest_cas, uffd_data_handler, 2),
+ TEST_RO_MEMSLOT_NO_SYNDROME_AND_UFFD(guest_dc_zva, uffd_no_handler, 1),
+ TEST_RO_MEMSLOT_NO_SYNDROME_AND_UFFD(guest_st_preidx, uffd_no_handler, 1),
{ 0 }
};
diff --git a/tools/testing/selftests/kvm/lib/aarch64/ucall.c b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
index 562c16dfbb0023..f212bd8ab93d80 100644
--- a/tools/testing/selftests/kvm/lib/aarch64/ucall.c
+++ b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
@@ -14,11 +14,13 @@ static vm_vaddr_t *ucall_exit_mmio_addr;
void ucall_arch_init(struct kvm_vm *vm, vm_paddr_t mmio_gpa)
{
- virt_pg_map(vm, mmio_gpa, mmio_gpa);
+ vm_vaddr_t mmio_gva = vm_vaddr_unused_gap(vm, vm->page_size, KVM_UTIL_MIN_VADDR);
+
+ virt_map(vm, mmio_gva, mmio_gpa, 1);
vm->ucall_mmio_addr = mmio_gpa;
- write_guest_global(vm, ucall_exit_mmio_addr, (vm_vaddr_t *)mmio_gpa);
+ write_guest_global(vm, ucall_exit_mmio_addr, (vm_vaddr_t *)mmio_gva);
}
void ucall_arch_do_ucall(vm_vaddr_t uc)
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index c88c3ace16d2fc..56d5ea949cbbe9 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -186,6 +186,15 @@ const struct vm_guest_mode_params vm_guest_mode_params[] = {
_Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params) == NUM_VM_MODES,
"Missing new mode params?");
+/*
+ * Initializes vm->vpages_valid to match the canonical VA space of the
+ * architecture.
+ *
+ * The default implementation is valid for architectures which split the
+ * range addressed by a single page table into a low and high region
+ * based on the MSB of the VA. On architectures with this behavior
+ * the VA region spans [0, 2^(va_bits - 1)), [-(2^(va_bits - 1), -1].
+ */
__weak void vm_vaddr_populate_bitmap(struct kvm_vm *vm)
{
sparsebit_set_num(vm->vpages_valid,
@@ -1416,10 +1425,10 @@ void virt_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr,
while (npages--) {
virt_pg_map(vm, vaddr, paddr);
+ sparsebit_set(vm->vpages_mapped, vaddr >> vm->page_shift);
+
vaddr += page_size;
paddr += page_size;
-
- sparsebit_set(vm->vpages_mapped, vaddr >> vm->page_shift);
}
}
diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c
index 0cc0971ce60e3a..2f0e2ea941cc66 100644
--- a/tools/testing/selftests/kvm/lib/ucall_common.c
+++ b/tools/testing/selftests/kvm/lib/ucall_common.c
@@ -4,6 +4,8 @@
#include "linux/bitmap.h"
#include "linux/atomic.h"
+#define GUEST_UCALL_FAILED -1
+
struct ucall_header {
DECLARE_BITMAP(in_use, KVM_MAX_VCPUS);
struct ucall ucalls[KVM_MAX_VCPUS];
@@ -41,7 +43,8 @@ static struct ucall *ucall_alloc(void)
struct ucall *uc;
int i;
- GUEST_ASSERT(ucall_pool);
+ if (!ucall_pool)
+ goto ucall_failed;
for (i = 0; i < KVM_MAX_VCPUS; ++i) {
if (!test_and_set_bit(i, ucall_pool->in_use)) {
@@ -51,7 +54,13 @@ static struct ucall *ucall_alloc(void)
}
}
- GUEST_ASSERT(0);
+ucall_failed:
+ /*
+ * If the vCPU cannot grab a ucall structure, make a bare ucall with a
+ * magic value to signal to get_ucall() that things went sideways.
+ * GUEST_ASSERT() depends on ucall_alloc() and so cannot be used here.
+ */
+ ucall_arch_do_ucall(GUEST_UCALL_FAILED);
return NULL;
}
@@ -93,6 +102,9 @@ uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
addr = ucall_arch_get_ucall(vcpu);
if (addr) {
+ TEST_ASSERT(addr != (void *)GUEST_UCALL_FAILED,
+ "Guest failed to allocate ucall struct");
+
memcpy(uc, addr, sizeof(*uc));
vcpu_run_complete_io(vcpu);
} else {
diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
index c4d368d56cfe2b..acfa1d01e7df08 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
@@ -1031,7 +1031,7 @@ bool is_amd_cpu(void)
void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits)
{
if (!kvm_cpu_has_p(X86_PROPERTY_MAX_PHY_ADDR)) {
- *pa_bits == kvm_cpu_has(X86_FEATURE_PAE) ? 36 : 32;
+ *pa_bits = kvm_cpu_has(X86_FEATURE_PAE) ? 36 : 32;
*va_bits = 32;
} else {
*pa_bits = kvm_cpu_property(X86_PROPERTY_MAX_PHY_ADDR);
diff --git a/tools/testing/selftests/kvm/memslot_perf_test.c b/tools/testing/selftests/kvm/memslot_perf_test.c
index e698306bf49d1d..e6587e19349051 100644
--- a/tools/testing/selftests/kvm/memslot_perf_test.c
+++ b/tools/testing/selftests/kvm/memslot_perf_test.c
@@ -265,6 +265,9 @@ static uint64_t get_max_slots(struct vm_data *data, uint32_t host_page_size)
slots = data->nslots;
while (--slots > 1) {
pages_per_slot = mempages / slots;
+ if (!pages_per_slot)
+ continue;
+
rempages = mempages % pages_per_slot;
if (check_slot_pages(host_page_size, guest_page_size,
pages_per_slot, rempages))
diff --git a/tools/testing/selftests/kvm/x86_64/hyperv_ipi.c b/tools/testing/selftests/kvm/x86_64/hyperv_ipi.c
index 8b791eac7d5a37..0cbb0e646ef8d7 100644
--- a/tools/testing/selftests/kvm/x86_64/hyperv_ipi.c
+++ b/tools/testing/selftests/kvm/x86_64/hyperv_ipi.c
@@ -193,8 +193,9 @@ static void sender_guest_code(void *hcall_page, vm_vaddr_t pgs_gpa)
GUEST_SYNC(stage++);
/*
* 'XMM Fast' HvCallSendSyntheticClusterIpiEx to HV_GENERIC_SET_ALL.
- * Nothing to write anything to XMM regs.
*/
+ ipi_ex->vp_set.valid_bank_mask = 0;
+ hyperv_write_xmm_input(&ipi_ex->vp_set.valid_bank_mask, 2);
hyperv_hypercall(HVCALL_SEND_IPI_EX | HV_HYPERCALL_FAST_BIT,
IPI_VECTOR, HV_GENERIC_SET_ALL);
nop_loop();
diff --git a/tools/testing/selftests/kvm/x86_64/nx_huge_pages_test.c b/tools/testing/selftests/kvm/x86_64/nx_huge_pages_test.c
index ea0978f22db860..251794f8371939 100644
--- a/tools/testing/selftests/kvm/x86_64/nx_huge_pages_test.c
+++ b/tools/testing/selftests/kvm/x86_64/nx_huge_pages_test.c
@@ -241,7 +241,7 @@ int main(int argc, char **argv)
while ((opt = getopt(argc, argv, "hp:t:r")) != -1) {
switch (opt) {
case 'p':
- reclaim_period_ms = atoi_non_negative("Reclaim period", optarg);
+ reclaim_period_ms = atoi_positive("Reclaim period", optarg);
break;
case 't':
token = atoi_paranoid(optarg);
diff --git a/tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c b/tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c
index e497ace629c192..b34980d45648a6 100644
--- a/tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c
+++ b/tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c
@@ -41,8 +41,17 @@ static void guest_int_handler(struct ex_regs *regs)
static void l2_guest_code_int(void)
{
GUEST_ASSERT_1(int_fired == 1, int_fired);
- vmmcall();
- ud2();
+
+ /*
+ * Same as the vmmcall() function, but with a ud2 sneaked after the
+ * vmmcall. The caller injects an exception with the return address
+ * increased by 2, so the "pop rbp" must be after the ud2 and we cannot
+ * use vmmcall() directly.
+ */
+ __asm__ __volatile__("push %%rbp; vmmcall; ud2; pop %%rbp"
+ : : "a"(0xdeadbeef), "c"(0xbeefdead)
+ : "rbx", "rdx", "rsi", "rdi", "r8", "r9",
+ "r10", "r11", "r12", "r13", "r14", "r15");
GUEST_ASSERT_1(bp_fired == 1, bp_fired);
hlt();
diff --git a/tools/testing/selftests/kvm/x86_64/vmx_tsc_adjust_test.c b/tools/testing/selftests/kvm/x86_64/vmx_tsc_adjust_test.c
index 5943187e859494..ff8ecdf32ae07d 100644
--- a/tools/testing/selftests/kvm/x86_64/vmx_tsc_adjust_test.c
+++ b/tools/testing/selftests/kvm/x86_64/vmx_tsc_adjust_test.c
@@ -49,11 +49,6 @@ enum {
NUM_VMX_PAGES,
};
-struct kvm_single_msr {
- struct kvm_msrs header;
- struct kvm_msr_entry entry;
-} __attribute__((packed));
-
/* The virtual machine object. */
static struct kvm_vm *vm;
diff --git a/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c b/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c
index 721f6a693799b5..13c75dc18c10b5 100644
--- a/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c
+++ b/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c
@@ -434,6 +434,7 @@ static void *juggle_shinfo_state(void *arg)
int main(int argc, char *argv[])
{
struct timespec min_ts, max_ts, vm_ts;
+ struct kvm_xen_hvm_attr evt_reset;
struct kvm_vm *vm;
pthread_t thread;
bool verbose;
@@ -962,6 +963,10 @@ int main(int argc, char *argv[])
}
done:
+ evt_reset.type = KVM_XEN_ATTR_TYPE_EVTCHN;
+ evt_reset.u.evtchn.flags = KVM_XEN_EVTCHN_RESET;
+ vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &evt_reset);
+
alarm(0);
clock_gettime(CLOCK_REALTIME, &max_ts);
diff --git a/tools/testing/selftests/lib.mk b/tools/testing/selftests/lib.mk
index 291144c284fbcb..f7900e75d2306d 100644
--- a/tools/testing/selftests/lib.mk
+++ b/tools/testing/selftests/lib.mk
@@ -20,7 +20,7 @@ CLANG_TARGET_FLAGS := $(CLANG_TARGET_FLAGS_$(ARCH))
ifeq ($(CROSS_COMPILE),)
ifeq ($(CLANG_TARGET_FLAGS),)
-$(error Specify CROSS_COMPILE or add '--target=' option to lib.mk
+$(error Specify CROSS_COMPILE or add '--target=' option to lib.mk)
else
CLANG_FLAGS += --target=$(CLANG_TARGET_FLAGS)
endif # CLANG_TARGET_FLAGS
diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore
index 9cc84114741d38..a6911cae368c77 100644
--- a/tools/testing/selftests/net/.gitignore
+++ b/tools/testing/selftests/net/.gitignore
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
bind_bhash
+bind_timewait
csum
cmsg_sender
diag_uid
diff --git a/tools/testing/selftests/net/af_unix/test_unix_oob.c b/tools/testing/selftests/net/af_unix/test_unix_oob.c
index b57e91e1c3f28f..532459a15067cc 100644
--- a/tools/testing/selftests/net/af_unix/test_unix_oob.c
+++ b/tools/testing/selftests/net/af_unix/test_unix_oob.c
@@ -124,7 +124,7 @@ void producer(struct sockaddr_un *consumer_addr)
wait_for_signal(pipefd[0]);
if (connect(cfd, (struct sockaddr *)consumer_addr,
- sizeof(struct sockaddr)) != 0) {
+ sizeof(*consumer_addr)) != 0) {
perror("Connect failed");
kill(0, SIGTERM);
exit(1);
diff --git a/tools/testing/selftests/net/arp_ndisc_evict_nocarrier.sh b/tools/testing/selftests/net/arp_ndisc_evict_nocarrier.sh
index b5af08af855959..4a110bb01e53e7 100755
--- a/tools/testing/selftests/net/arp_ndisc_evict_nocarrier.sh
+++ b/tools/testing/selftests/net/arp_ndisc_evict_nocarrier.sh
@@ -18,14 +18,15 @@ readonly V4_ADDR1=10.0.10.2
readonly V6_ADDR0=2001:db8:91::1
readonly V6_ADDR1=2001:db8:91::2
nsid=100
+ret=0
cleanup_v6()
{
ip netns del me
ip netns del peer
- sysctl -w net.ipv4.conf.veth0.ndisc_evict_nocarrier=1 >/dev/null 2>&1
- sysctl -w net.ipv4.conf.all.ndisc_evict_nocarrier=1 >/dev/null 2>&1
+ sysctl -w net.ipv6.conf.veth1.ndisc_evict_nocarrier=1 >/dev/null 2>&1
+ sysctl -w net.ipv6.conf.all.ndisc_evict_nocarrier=1 >/dev/null 2>&1
}
create_ns()
@@ -61,7 +62,7 @@ setup_v6() {
if [ $? -ne 0 ]; then
cleanup_v6
echo "failed"
- exit
+ exit 1
fi
# Set veth2 down, which will put veth1 in NOCARRIER state
@@ -88,7 +89,7 @@ setup_v4() {
if [ $? -ne 0 ]; then
cleanup_v4
echo "failed"
- exit
+ exit 1
fi
# Set veth1 down, which will put veth0 in NOCARRIER state
@@ -115,6 +116,7 @@ run_arp_evict_nocarrier_enabled() {
if [ $? -eq 0 ];then
echo "failed"
+ ret=1
else
echo "ok"
fi
@@ -134,6 +136,7 @@ run_arp_evict_nocarrier_disabled() {
echo "ok"
else
echo "failed"
+ ret=1
fi
cleanup_v4
@@ -164,6 +167,7 @@ run_ndisc_evict_nocarrier_enabled() {
if [ $? -eq 0 ];then
echo "failed"
+ ret=1
else
echo "ok"
fi
@@ -182,6 +186,7 @@ run_ndisc_evict_nocarrier_disabled() {
echo "ok"
else
echo "failed"
+ ret=1
fi
cleanup_v6
@@ -198,6 +203,7 @@ run_ndisc_evict_nocarrier_disabled_all() {
echo "ok"
else
echo "failed"
+ ret=1
fi
cleanup_v6
@@ -218,3 +224,4 @@ if [ "$(id -u)" -ne 0 ];then
fi
run_all_tests
+exit $ret
diff --git a/tools/testing/selftests/net/bind_timewait.c b/tools/testing/selftests/net/bind_timewait.c
new file mode 100644
index 00000000000000..cb9fdf51ea59fa
--- /dev/null
+++ b/tools/testing/selftests/net/bind_timewait.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright Amazon.com Inc. or its affiliates. */
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "../kselftest_harness.h"
+
+FIXTURE(bind_timewait)
+{
+ struct sockaddr_in addr;
+ socklen_t addrlen;
+};
+
+FIXTURE_VARIANT(bind_timewait)
+{
+ __u32 addr_const;
+};
+
+FIXTURE_VARIANT_ADD(bind_timewait, localhost)
+{
+ .addr_const = INADDR_LOOPBACK
+};
+
+FIXTURE_VARIANT_ADD(bind_timewait, addrany)
+{
+ .addr_const = INADDR_ANY
+};
+
+FIXTURE_SETUP(bind_timewait)
+{
+ self->addr.sin_family = AF_INET;
+ self->addr.sin_port = 0;
+ self->addr.sin_addr.s_addr = htonl(variant->addr_const);
+ self->addrlen = sizeof(self->addr);
+}
+
+FIXTURE_TEARDOWN(bind_timewait)
+{
+}
+
+void create_timewait_socket(struct __test_metadata *_metadata,
+ FIXTURE_DATA(bind_timewait) *self)
+{
+ int server_fd, client_fd, child_fd, ret;
+ struct sockaddr_in addr;
+ socklen_t addrlen;
+
+ server_fd = socket(AF_INET, SOCK_STREAM, 0);
+ ASSERT_GT(server_fd, 0);
+
+ ret = bind(server_fd, (struct sockaddr *)&self->addr, self->addrlen);
+ ASSERT_EQ(ret, 0);
+
+ ret = listen(server_fd, 1);
+ ASSERT_EQ(ret, 0);
+
+ ret = getsockname(server_fd, (struct sockaddr *)&self->addr, &self->addrlen);
+ ASSERT_EQ(ret, 0);
+
+ client_fd = socket(AF_INET, SOCK_STREAM, 0);
+ ASSERT_GT(client_fd, 0);
+
+ ret = connect(client_fd, (struct sockaddr *)&self->addr, self->addrlen);
+ ASSERT_EQ(ret, 0);
+
+ addrlen = sizeof(addr);
+ child_fd = accept(server_fd, (struct sockaddr *)&addr, &addrlen);
+ ASSERT_GT(child_fd, 0);
+
+ close(child_fd);
+ close(client_fd);
+ close(server_fd);
+}
+
+TEST_F(bind_timewait, 1)
+{
+ int fd, ret;
+
+ create_timewait_socket(_metadata, self);
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ ASSERT_GT(fd, 0);
+
+ ret = bind(fd, (struct sockaddr *)&self->addr, self->addrlen);
+ ASSERT_EQ(ret, -1);
+ ASSERT_EQ(errno, EADDRINUSE);
+
+ close(fd);
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/net/cmsg_ipv6.sh b/tools/testing/selftests/net/cmsg_ipv6.sh
index 2d89cb0ad2889a..330d0b1ceced3d 100755
--- a/tools/testing/selftests/net/cmsg_ipv6.sh
+++ b/tools/testing/selftests/net/cmsg_ipv6.sh
@@ -6,7 +6,7 @@ ksft_skip=4
NS=ns
IP6=2001:db8:1::1/64
TGT6=2001:db8:1::2
-TMPF=`mktemp`
+TMPF=$(mktemp --suffix ".pcap")
cleanup()
{
diff --git a/tools/testing/selftests/net/cmsg_sender.c b/tools/testing/selftests/net/cmsg_sender.c
index 75dd83e39207bd..24b21b15ed3fb0 100644
--- a/tools/testing/selftests/net/cmsg_sender.c
+++ b/tools/testing/selftests/net/cmsg_sender.c
@@ -110,7 +110,7 @@ static void __attribute__((noreturn)) cs_usage(const char *bin)
static void cs_parse_args(int argc, char *argv[])
{
- char o;
+ int o;
while ((o = getopt(argc, argv, "46sS:p:m:M:d:tf:F:c:C:l:L:H:")) != -1) {
switch (o) {
diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh
index 1c4f866de7d7d6..3d8e4ebda1b648 100755
--- a/tools/testing/selftests/net/forwarding/lib.sh
+++ b/tools/testing/selftests/net/forwarding/lib.sh
@@ -914,14 +914,14 @@ sysctl_set()
local value=$1; shift
SYSCTL_ORIG[$key]=$(sysctl -n $key)
- sysctl -qw $key=$value
+ sysctl -qw $key="$value"
}
sysctl_restore()
{
local key=$1; shift
- sysctl -qw $key=${SYSCTL_ORIG["$key"]}
+ sysctl -qw $key="${SYSCTL_ORIG[$key]}"
}
forwarding_enable()
diff --git a/tools/testing/selftests/net/l2_tos_ttl_inherit.sh b/tools/testing/selftests/net/l2_tos_ttl_inherit.sh
index dca1e6f777a897..f11756e7df2f91 100755
--- a/tools/testing/selftests/net/l2_tos_ttl_inherit.sh
+++ b/tools/testing/selftests/net/l2_tos_ttl_inherit.sh
@@ -12,19 +12,27 @@
# In addition this script also checks if forcing a specific field in the
# outer header is working.
+# Return 4 by default (Kselftest SKIP code)
+ERR=4
+
if [ "$(id -u)" != "0" ]; then
echo "Please run as root."
- exit 0
+ exit $ERR
fi
if ! which tcpdump > /dev/null 2>&1; then
echo "No tcpdump found. Required for this test."
- exit 0
+ exit $ERR
fi
expected_tos="0x00"
expected_ttl="0"
failed=false
+readonly NS0=$(mktemp -u ns0-XXXXXXXX)
+readonly NS1=$(mktemp -u ns1-XXXXXXXX)
+
+RUN_NS0="ip netns exec ${NS0}"
+
get_random_tos() {
# Get a random hex tos value between 0x00 and 0xfc, a multiple of 4
echo "0x$(tr -dc '0-9a-f' < /dev/urandom | head -c 1)\
@@ -61,7 +69,6 @@ setup() {
local vlan="$5"
local test_tos="0x00"
local test_ttl="0"
- local ns="ip netns exec testing"
# We don't want a test-tos of 0x00,
# because this is the value that we get when no tos is set.
@@ -94,14 +101,15 @@ setup() {
printf "│%7s │%6s │%6s │%13s │%13s │%6s │" \
"$type" "$outer" "$inner" "$tos" "$ttl" "$vlan"
- # Create 'testing' netns, veth pair and connect main ns with testing ns
- ip netns add testing
- ip link add type veth
- ip link set veth1 netns testing
- ip link set veth0 up
- $ns ip link set veth1 up
- ip addr flush dev veth0
- $ns ip addr flush dev veth1
+ # Create netns NS0 and NS1 and connect them with a veth pair
+ ip netns add "${NS0}"
+ ip netns add "${NS1}"
+ ip link add name veth0 netns "${NS0}" type veth \
+ peer name veth1 netns "${NS1}"
+ ip -netns "${NS0}" link set dev veth0 up
+ ip -netns "${NS1}" link set dev veth1 up
+ ip -netns "${NS0}" address flush dev veth0
+ ip -netns "${NS1}" address flush dev veth1
local local_addr1=""
local local_addr2=""
@@ -127,51 +135,59 @@ setup() {
if [ "$type" = "gre" ]; then
type="gretap"
fi
- ip addr add 198.18.0.1/24 dev veth0
- $ns ip addr add 198.18.0.2/24 dev veth1
- ip link add name tep0 type $type $local_addr1 remote \
- 198.18.0.2 tos $test_tos ttl $test_ttl $vxlan $geneve
- $ns ip link add name tep1 type $type $local_addr2 remote \
- 198.18.0.1 tos $test_tos ttl $test_ttl $vxlan $geneve
+ ip -netns "${NS0}" address add 198.18.0.1/24 dev veth0
+ ip -netns "${NS1}" address add 198.18.0.2/24 dev veth1
+ ip -netns "${NS0}" link add name tep0 type $type $local_addr1 \
+ remote 198.18.0.2 tos $test_tos ttl $test_ttl \
+ $vxlan $geneve
+ ip -netns "${NS1}" link add name tep1 type $type $local_addr2 \
+ remote 198.18.0.1 tos $test_tos ttl $test_ttl \
+ $vxlan $geneve
elif [ "$outer" = "6" ]; then
if [ "$type" = "gre" ]; then
type="ip6gretap"
fi
- ip addr add fdd1:ced0:5d88:3fce::1/64 dev veth0
- $ns ip addr add fdd1:ced0:5d88:3fce::2/64 dev veth1
- ip link add name tep0 type $type $local_addr1 \
- remote fdd1:ced0:5d88:3fce::2 tos $test_tos ttl $test_ttl \
- $vxlan $geneve
- $ns ip link add name tep1 type $type $local_addr2 \
- remote fdd1:ced0:5d88:3fce::1 tos $test_tos ttl $test_ttl \
- $vxlan $geneve
+ ip -netns "${NS0}" address add fdd1:ced0:5d88:3fce::1/64 \
+ dev veth0 nodad
+ ip -netns "${NS1}" address add fdd1:ced0:5d88:3fce::2/64 \
+ dev veth1 nodad
+ ip -netns "${NS0}" link add name tep0 type $type $local_addr1 \
+ remote fdd1:ced0:5d88:3fce::2 tos $test_tos \
+ ttl $test_ttl $vxlan $geneve
+ ip -netns "${NS1}" link add name tep1 type $type $local_addr2 \
+ remote fdd1:ced0:5d88:3fce::1 tos $test_tos \
+ ttl $test_ttl $vxlan $geneve
fi
# Bring L2-tunnel link up and create VLAN on top
- ip link set tep0 up
- $ns ip link set tep1 up
- ip addr flush dev tep0
- $ns ip addr flush dev tep1
+ ip -netns "${NS0}" link set tep0 up
+ ip -netns "${NS1}" link set tep1 up
+ ip -netns "${NS0}" address flush dev tep0
+ ip -netns "${NS1}" address flush dev tep1
local parent
if $vlan; then
parent="vlan99-"
- ip link add link tep0 name ${parent}0 type vlan id 99
- $ns ip link add link tep1 name ${parent}1 type vlan id 99
- ip link set ${parent}0 up
- $ns ip link set ${parent}1 up
- ip addr flush dev ${parent}0
- $ns ip addr flush dev ${parent}1
+ ip -netns "${NS0}" link add link tep0 name ${parent}0 \
+ type vlan id 99
+ ip -netns "${NS1}" link add link tep1 name ${parent}1 \
+ type vlan id 99
+ ip -netns "${NS0}" link set dev ${parent}0 up
+ ip -netns "${NS1}" link set dev ${parent}1 up
+ ip -netns "${NS0}" address flush dev ${parent}0
+ ip -netns "${NS1}" address flush dev ${parent}1
else
parent="tep"
fi
# Assign inner IPv4/IPv6 addresses
if [ "$inner" = "4" ] || [ "$inner" = "other" ]; then
- ip addr add 198.19.0.1/24 brd + dev ${parent}0
- $ns ip addr add 198.19.0.2/24 brd + dev ${parent}1
+ ip -netns "${NS0}" address add 198.19.0.1/24 brd + dev ${parent}0
+ ip -netns "${NS1}" address add 198.19.0.2/24 brd + dev ${parent}1
elif [ "$inner" = "6" ]; then
- ip addr add fdd4:96cf:4eae:443b::1/64 dev ${parent}0
- $ns ip addr add fdd4:96cf:4eae:443b::2/64 dev ${parent}1
+ ip -netns "${NS0}" address add fdd4:96cf:4eae:443b::1/64 \
+ dev ${parent}0 nodad
+ ip -netns "${NS1}" address add fdd4:96cf:4eae:443b::2/64 \
+ dev ${parent}1 nodad
fi
}
@@ -192,10 +208,10 @@ verify() {
ping_dst="198.19.0.3" # Generates ARPs which are not IPv4/IPv6
fi
if [ "$tos_ttl" = "inherit" ]; then
- ping -i 0.1 $ping_dst -Q "$expected_tos" -t "$expected_ttl" \
- 2>/dev/null 1>&2 & ping_pid="$!"
+ ${RUN_NS0} ping -i 0.1 $ping_dst -Q "$expected_tos" \
+ -t "$expected_ttl" 2>/dev/null 1>&2 & ping_pid="$!"
else
- ping -i 0.1 $ping_dst 2>/dev/null 1>&2 & ping_pid="$!"
+ ${RUN_NS0} ping -i 0.1 $ping_dst 2>/dev/null 1>&2 & ping_pid="$!"
fi
local tunnel_type_offset tunnel_type_proto req_proto_offset req_offset
if [ "$type" = "gre" ]; then
@@ -216,10 +232,12 @@ verify() {
req_proto_offset="$((req_proto_offset + 4))"
req_offset="$((req_offset + 4))"
fi
- out="$(tcpdump --immediate-mode -p -c 1 -v -i veth0 -n \
- ip[$tunnel_type_offset] = $tunnel_type_proto and \
- ip[$req_proto_offset] = 0x01 and \
- ip[$req_offset] = 0x08 2>/dev/null | head -n 1)"
+ out="$(${RUN_NS0} tcpdump --immediate-mode -p -c 1 -v \
+ -i veth0 -n \
+ ip[$tunnel_type_offset] = $tunnel_type_proto and \
+ ip[$req_proto_offset] = 0x01 and \
+ ip[$req_offset] = 0x08 2>/dev/null \
+ | head -n 1)"
elif [ "$inner" = "6" ]; then
req_proto_offset="44"
req_offset="78"
@@ -231,10 +249,12 @@ verify() {
req_proto_offset="$((req_proto_offset + 4))"
req_offset="$((req_offset + 4))"
fi
- out="$(tcpdump --immediate-mode -p -c 1 -v -i veth0 -n \
- ip[$tunnel_type_offset] = $tunnel_type_proto and \
- ip[$req_proto_offset] = 0x3a and \
- ip[$req_offset] = 0x80 2>/dev/null | head -n 1)"
+ out="$(${RUN_NS0} tcpdump --immediate-mode -p -c 1 -v \
+ -i veth0 -n \
+ ip[$tunnel_type_offset] = $tunnel_type_proto and \
+ ip[$req_proto_offset] = 0x3a and \
+ ip[$req_offset] = 0x80 2>/dev/null \
+ | head -n 1)"
elif [ "$inner" = "other" ]; then
req_proto_offset="36"
req_offset="45"
@@ -250,11 +270,13 @@ verify() {
expected_tos="0x00"
expected_ttl="64"
fi
- out="$(tcpdump --immediate-mode -p -c 1 -v -i veth0 -n \
- ip[$tunnel_type_offset] = $tunnel_type_proto and \
- ip[$req_proto_offset] = 0x08 and \
- ip[$((req_proto_offset + 1))] = 0x06 and \
- ip[$req_offset] = 0x01 2>/dev/null | head -n 1)"
+ out="$(${RUN_NS0} tcpdump --immediate-mode -p -c 1 -v \
+ -i veth0 -n \
+ ip[$tunnel_type_offset] = $tunnel_type_proto and \
+ ip[$req_proto_offset] = 0x08 and \
+ ip[$((req_proto_offset + 1))] = 0x06 and \
+ ip[$req_offset] = 0x01 2>/dev/null \
+ | head -n 1)"
fi
elif [ "$outer" = "6" ]; then
if [ "$type" = "gre" ]; then
@@ -273,10 +295,12 @@ verify() {
req_proto_offset="$((req_proto_offset + 4))"
req_offset="$((req_offset + 4))"
fi
- out="$(tcpdump --immediate-mode -p -c 1 -v -i veth0 -n \
- ip6[$tunnel_type_offset] = $tunnel_type_proto and \
- ip6[$req_proto_offset] = 0x01 and \
- ip6[$req_offset] = 0x08 2>/dev/null | head -n 1)"
+ out="$(${RUN_NS0} tcpdump --immediate-mode -p -c 1 -v \
+ -i veth0 -n \
+ ip6[$tunnel_type_offset] = $tunnel_type_proto and \
+ ip6[$req_proto_offset] = 0x01 and \
+ ip6[$req_offset] = 0x08 2>/dev/null \
+ | head -n 1)"
elif [ "$inner" = "6" ]; then
local req_proto_offset="72"
local req_offset="106"
@@ -288,10 +312,12 @@ verify() {
req_proto_offset="$((req_proto_offset + 4))"
req_offset="$((req_offset + 4))"
fi
- out="$(tcpdump --immediate-mode -p -c 1 -v -i veth0 -n \
- ip6[$tunnel_type_offset] = $tunnel_type_proto and \
- ip6[$req_proto_offset] = 0x3a and \
- ip6[$req_offset] = 0x80 2>/dev/null | head -n 1)"
+ out="$(${RUN_NS0} tcpdump --immediate-mode -p -c 1 -v \
+ -i veth0 -n \
+ ip6[$tunnel_type_offset] = $tunnel_type_proto and \
+ ip6[$req_proto_offset] = 0x3a and \
+ ip6[$req_offset] = 0x80 2>/dev/null \
+ | head -n 1)"
elif [ "$inner" = "other" ]; then
local req_proto_offset="64"
local req_offset="73"
@@ -307,15 +333,17 @@ verify() {
expected_tos="0x00"
expected_ttl="64"
fi
- out="$(tcpdump --immediate-mode -p -c 1 -v -i veth0 -n \
- ip6[$tunnel_type_offset] = $tunnel_type_proto and \
- ip6[$req_proto_offset] = 0x08 and \
- ip6[$((req_proto_offset + 1))] = 0x06 and \
- ip6[$req_offset] = 0x01 2>/dev/null | head -n 1)"
+ out="$(${RUN_NS0} tcpdump --immediate-mode -p -c 1 -v \
+ -i veth0 -n \
+ ip6[$tunnel_type_offset] = $tunnel_type_proto and \
+ ip6[$req_proto_offset] = 0x08 and \
+ ip6[$((req_proto_offset + 1))] = 0x06 and \
+ ip6[$req_offset] = 0x01 2>/dev/null \
+ | head -n 1)"
fi
fi
kill -9 $ping_pid
- wait $ping_pid 2>/dev/null
+ wait $ping_pid 2>/dev/null || true
result="FAIL"
if [ "$outer" = "4" ]; then
captured_ttl="$(get_field "ttl" "$out")"
@@ -351,11 +379,35 @@ verify() {
}
cleanup() {
- ip link del veth0 2>/dev/null
- ip netns del testing 2>/dev/null
- ip link del tep0 2>/dev/null
+ ip netns del "${NS0}" 2>/dev/null
+ ip netns del "${NS1}" 2>/dev/null
}
+exit_handler() {
+ # Don't exit immediately if one of the intermediate commands fails.
+ # We might be called at the end of the script, when the network
+ # namespaces have already been deleted. So cleanup() may fail, but we
+ # still need to run until 'exit $ERR' or the script won't return the
+ # correct error code.
+ set +e
+
+ cleanup
+
+ exit $ERR
+}
+
+# Restore the default SIGINT handler (just in case) and exit.
+# The exit handler will take care of cleaning everything up.
+interrupted() {
+ trap - INT
+
+ exit $ERR
+}
+
+set -e
+trap exit_handler EXIT
+trap interrupted INT
+
printf "┌────────┬───────┬───────┬──────────────┬"
printf "──────────────┬───────┬────────┐\n"
for type in gre vxlan geneve; do
@@ -385,6 +437,10 @@ done
printf "└────────┴───────┴───────┴──────────────┴"
printf "──────────────┴───────┴────────┘\n"
+# All tests done.
+# Set ERR appropriately: it will be returned by the exit handler.
if $failed; then
- exit 1
+ ERR=1
+else
+ ERR=0
fi
diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh
index d11d3d5666086d..079f8f46849de7 100755
--- a/tools/testing/selftests/net/mptcp/mptcp_join.sh
+++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh
@@ -498,6 +498,12 @@ kill_events_pids()
kill_wait $evts_ns2_pid
}
+kill_tests_wait()
+{
+ kill -SIGUSR1 $(ip netns pids $ns2) $(ip netns pids $ns1)
+ wait
+}
+
pm_nl_set_limits()
{
local ns=$1
@@ -1694,6 +1700,7 @@ chk_subflow_nr()
local subflow_nr=$3
local cnt1
local cnt2
+ local dump_stats
if [ -n "${need_title}" ]; then
printf "%03u %-36s %s" "${TEST_COUNT}" "${TEST_NAME}" "${msg}"
@@ -1711,7 +1718,12 @@ chk_subflow_nr()
echo "[ ok ]"
fi
- [ "${dump_stats}" = 1 ] && ( ss -N $ns1 -tOni ; ss -N $ns1 -tOni | grep token; ip -n $ns1 mptcp endpoint )
+ if [ "${dump_stats}" = 1 ]; then
+ ss -N $ns1 -tOni
+ ss -N $ns1 -tOni | grep token
+ ip -n $ns1 mptcp endpoint
+ dump_stats
+ fi
}
chk_link_usage()
@@ -3049,7 +3061,7 @@ endpoint_tests()
pm_nl_set_limits $ns1 2 2
pm_nl_set_limits $ns2 2 2
pm_nl_add_endpoint $ns1 10.0.2.1 flags signal
- run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow &
+ run_tests $ns1 $ns2 10.0.1.1 0 0 0 slow 2>/dev/null &
wait_mpj $ns1
pm_nl_check_endpoint 1 "creation" \
@@ -3062,14 +3074,14 @@ endpoint_tests()
pm_nl_add_endpoint $ns2 10.0.2.2 flags signal
pm_nl_check_endpoint 0 "modif is allowed" \
$ns2 10.0.2.2 id 1 flags signal
- wait
+ kill_tests_wait
fi
if reset "delete and re-add"; then
pm_nl_set_limits $ns1 1 1
pm_nl_set_limits $ns2 1 1
pm_nl_add_endpoint $ns2 10.0.2.2 id 2 dev ns2eth2 flags subflow
- run_tests $ns1 $ns2 10.0.1.1 4 0 0 slow &
+ run_tests $ns1 $ns2 10.0.1.1 4 0 0 speed_20 2>/dev/null &
wait_mpj $ns2
pm_nl_del_endpoint $ns2 2 10.0.2.2
@@ -3079,7 +3091,7 @@ endpoint_tests()
pm_nl_add_endpoint $ns2 10.0.2.2 dev ns2eth2 flags subflow
wait_mpj $ns2
chk_subflow_nr "" "after re-add" 2
- wait
+ kill_tests_wait
fi
}
diff --git a/tools/testing/selftests/net/mptcp/userspace_pm.sh b/tools/testing/selftests/net/mptcp/userspace_pm.sh
index a29deb9fa024cd..ab2d581f28a1d1 100755
--- a/tools/testing/selftests/net/mptcp/userspace_pm.sh
+++ b/tools/testing/selftests/net/mptcp/userspace_pm.sh
@@ -752,6 +752,52 @@ test_subflows()
"$server4_token" > /dev/null 2>&1
}
+test_subflows_v4_v6_mix()
+{
+ # Attempt to add a listener at 10.0.2.1:<subflow-port>
+ ip netns exec "$ns1" ./pm_nl_ctl listen 10.0.2.1\
+ $app6_port > /dev/null 2>&1 &
+ local listener_pid=$!
+
+ # ADD_ADDR4 from server to client machine reusing the subflow port on
+ # the established v6 connection
+ :>"$client_evts"
+ ip netns exec "$ns1" ./pm_nl_ctl ann 10.0.2.1 token "$server6_token" id\
+ $server_addr_id dev ns1eth2 > /dev/null 2>&1
+ stdbuf -o0 -e0 printf "ADD_ADDR4 id:%d 10.0.2.1 (ns1) => ns2, reuse port\t\t" $server_addr_id
+ sleep 0.5
+ verify_announce_event "$client_evts" "$ANNOUNCED" "$client6_token" "10.0.2.1"\
+ "$server_addr_id" "$app6_port"
+
+ # CREATE_SUBFLOW from client to server machine
+ :>"$client_evts"
+ ip netns exec "$ns2" ./pm_nl_ctl csf lip 10.0.2.2 lid 23 rip 10.0.2.1 rport\
+ $app6_port token "$client6_token" > /dev/null 2>&1
+ sleep 0.5
+ verify_subflow_events "$client_evts" "$SUB_ESTABLISHED" "$client6_token"\
+ "$AF_INET" "10.0.2.2" "10.0.2.1" "$app6_port" "23"\
+ "$server_addr_id" "ns2" "ns1"
+
+ # Delete the listener from the server ns, if one was created
+ kill_wait $listener_pid
+
+ sport=$(sed --unbuffered -n 's/.*\(sport:\)\([[:digit:]]*\).*$/\2/p;q' "$client_evts")
+
+ # DESTROY_SUBFLOW from client to server machine
+ :>"$client_evts"
+ ip netns exec "$ns2" ./pm_nl_ctl dsf lip 10.0.2.2 lport "$sport" rip 10.0.2.1 rport\
+ $app6_port token "$client6_token" > /dev/null 2>&1
+ sleep 0.5
+ verify_subflow_events "$client_evts" "$SUB_CLOSED" "$client6_token" \
+ "$AF_INET" "10.0.2.2" "10.0.2.1" "$app6_port" "23"\
+ "$server_addr_id" "ns2" "ns1"
+
+ # RM_ADDR from server to client machine
+ ip netns exec "$ns1" ./pm_nl_ctl rem id $server_addr_id token\
+ "$server6_token" > /dev/null 2>&1
+ sleep 0.5
+}
+
test_prio()
{
local count
@@ -861,6 +907,7 @@ make_connection "v6"
test_announce
test_remove
test_subflows
+test_subflows_v4_v6_mix
test_prio
test_listener
diff --git a/tools/testing/selftests/net/test_vxlan_vnifiltering.sh b/tools/testing/selftests/net/test_vxlan_vnifiltering.sh
index 704997ffc2449b..8c3ac0a7254510 100755
--- a/tools/testing/selftests/net/test_vxlan_vnifiltering.sh
+++ b/tools/testing/selftests/net/test_vxlan_vnifiltering.sh
@@ -293,19 +293,11 @@ setup-vm() {
elif [[ -n $vtype && $vtype == "vnifilterg" ]]; then
# Add per vni group config with 'bridge vni' api
if [ -n "$group" ]; then
- if [ "$family" == "v4" ]; then
- if [ $mcast -eq 1 ]; then
- bridge -netns hv-$hvid vni add dev $vxlandev vni $tid group $group
- else
- bridge -netns hv-$hvid vni add dev $vxlandev vni $tid remote $group
- fi
- else
- if [ $mcast -eq 1 ]; then
- bridge -netns hv-$hvid vni add dev $vxlandev vni $tid group6 $group
- else
- bridge -netns hv-$hvid vni add dev $vxlandev vni $tid remote6 $group
- fi
- fi
+ if [ $mcast -eq 1 ]; then
+ bridge -netns hv-$hvid vni add dev $vxlandev vni $tid group $group
+ else
+ bridge -netns hv-$hvid vni add dev $vxlandev vni $tid remote $group
+ fi
fi
fi
done
diff --git a/tools/testing/selftests/net/toeplitz.c b/tools/testing/selftests/net/toeplitz.c
index 90026a27eac0cb..9ba03164d73a69 100644
--- a/tools/testing/selftests/net/toeplitz.c
+++ b/tools/testing/selftests/net/toeplitz.c
@@ -215,7 +215,7 @@ static char *recv_frame(const struct ring_state *ring, char *frame)
}
/* A single TPACKET_V3 block can hold multiple frames */
-static void recv_block(struct ring_state *ring)
+static bool recv_block(struct ring_state *ring)
{
struct tpacket_block_desc *block;
char *frame;
@@ -223,7 +223,7 @@ static void recv_block(struct ring_state *ring)
block = (void *)(ring->mmap + ring->idx * ring_block_sz);
if (!(block->hdr.bh1.block_status & TP_STATUS_USER))
- return;
+ return false;
frame = (char *)block;
frame += block->hdr.bh1.offset_to_first_pkt;
@@ -235,6 +235,8 @@ static void recv_block(struct ring_state *ring)
block->hdr.bh1.block_status = TP_STATUS_KERNEL;
ring->idx = (ring->idx + 1) % ring_block_nr;
+
+ return true;
}
/* simple test: sleep once unconditionally and then process all rings */
@@ -245,7 +247,7 @@ static void process_rings(void)
usleep(1000 * cfg_timeout_msec);
for (i = 0; i < num_cpus; i++)
- recv_block(&rings[i]);
+ do {} while (recv_block(&rings[i]));
fprintf(stderr, "count: pass=%u nohash=%u fail=%u\n",
frames_received - frames_nohash - frames_error,
@@ -257,12 +259,12 @@ static char *setup_ring(int fd)
struct tpacket_req3 req3 = {0};
void *ring;
- req3.tp_retire_blk_tov = cfg_timeout_msec;
+ req3.tp_retire_blk_tov = cfg_timeout_msec / 8;
req3.tp_feature_req_word = TP_FT_REQ_FILL_RXHASH;
req3.tp_frame_size = 2048;
req3.tp_frame_nr = 1 << 10;
- req3.tp_block_nr = 2;
+ req3.tp_block_nr = 16;
req3.tp_block_size = req3.tp_frame_size * req3.tp_frame_nr;
req3.tp_block_size /= req3.tp_block_nr;
diff --git a/tools/testing/selftests/net/udpgso_bench.sh b/tools/testing/selftests/net/udpgso_bench.sh
index dc932fd6536345..640bc43452faa9 100755
--- a/tools/testing/selftests/net/udpgso_bench.sh
+++ b/tools/testing/selftests/net/udpgso_bench.sh
@@ -7,6 +7,7 @@ readonly GREEN='\033[0;92m'
readonly YELLOW='\033[0;33m'
readonly RED='\033[0;31m'
readonly NC='\033[0m' # No Color
+readonly TESTPORT=8000
readonly KSFT_PASS=0
readonly KSFT_FAIL=1
@@ -56,11 +57,26 @@ trap wake_children EXIT
run_one() {
local -r args=$@
+ local nr_socks=0
+ local i=0
+ local -r timeout=10
+
+ ./udpgso_bench_rx -p "$TESTPORT" &
+ ./udpgso_bench_rx -p "$TESTPORT" -t &
+
+ # Wait for the above test program to get ready to receive connections.
+ while [ "$i" -lt "$timeout" ]; do
+ nr_socks="$(ss -lnHi | grep -c "\*:${TESTPORT}")"
+ [ "$nr_socks" -eq 2 ] && break
+ i=$((i + 1))
+ sleep 1
+ done
+ if [ "$nr_socks" -ne 2 ]; then
+ echo "timed out while waiting for udpgso_bench_rx"
+ exit 1
+ fi
- ./udpgso_bench_rx &
- ./udpgso_bench_rx -t &
-
- ./udpgso_bench_tx ${args}
+ ./udpgso_bench_tx -p "$TESTPORT" ${args}
}
run_in_netns() {
diff --git a/tools/testing/selftests/net/udpgso_bench_rx.c b/tools/testing/selftests/net/udpgso_bench_rx.c
index 6a193425c367fc..4058c7451e70da 100644
--- a/tools/testing/selftests/net/udpgso_bench_rx.c
+++ b/tools/testing/selftests/net/udpgso_bench_rx.c
@@ -250,7 +250,7 @@ static int recv_msg(int fd, char *buf, int len, int *gso_size)
static void do_flush_udp(int fd)
{
static char rbuf[ETH_MAX_MTU];
- int ret, len, gso_size, budget = 256;
+ int ret, len, gso_size = 0, budget = 256;
len = cfg_read_all ? sizeof(rbuf) : 0;
while (budget--) {
@@ -336,6 +336,8 @@ static void parse_opts(int argc, char **argv)
cfg_verify = true;
cfg_read_all = true;
break;
+ default:
+ exit(1);
}
}
diff --git a/tools/testing/selftests/net/udpgso_bench_tx.c b/tools/testing/selftests/net/udpgso_bench_tx.c
index f1fdaa27029133..477392715a9ad5 100644
--- a/tools/testing/selftests/net/udpgso_bench_tx.c
+++ b/tools/testing/selftests/net/udpgso_bench_tx.c
@@ -62,6 +62,7 @@ static int cfg_payload_len = (1472 * 42);
static int cfg_port = 8000;
static int cfg_runtime_ms = -1;
static bool cfg_poll;
+static int cfg_poll_loop_timeout_ms = 2000;
static bool cfg_segment;
static bool cfg_sendmmsg;
static bool cfg_tcp;
@@ -235,16 +236,17 @@ static void flush_errqueue_recv(int fd)
}
}
-static void flush_errqueue(int fd, const bool do_poll)
+static void flush_errqueue(int fd, const bool do_poll,
+ unsigned long poll_timeout, const bool poll_err)
{
if (do_poll) {
struct pollfd fds = {0};
int ret;
fds.fd = fd;
- ret = poll(&fds, 1, 500);
+ ret = poll(&fds, 1, poll_timeout);
if (ret == 0) {
- if (cfg_verbose)
+ if ((cfg_verbose) && (poll_err))
fprintf(stderr, "poll timeout\n");
} else if (ret < 0) {
error(1, errno, "poll");
@@ -254,6 +256,20 @@ static void flush_errqueue(int fd, const bool do_poll)
flush_errqueue_recv(fd);
}
+static void flush_errqueue_retry(int fd, unsigned long num_sends)
+{
+ unsigned long tnow, tstop;
+ bool first_try = true;
+
+ tnow = gettimeofday_ms();
+ tstop = tnow + cfg_poll_loop_timeout_ms;
+ do {
+ flush_errqueue(fd, true, tstop - tnow, first_try);
+ first_try = false;
+ tnow = gettimeofday_ms();
+ } while ((stat_zcopies != num_sends) && (tnow < tstop));
+}
+
static int send_tcp(int fd, char *data)
{
int ret, done = 0, count = 0;
@@ -413,7 +429,8 @@ static int send_udp_segment(int fd, char *data)
static void usage(const char *filepath)
{
- error(1, 0, "Usage: %s [-46acmHPtTuvz] [-C cpu] [-D dst ip] [-l secs] [-M messagenr] [-p port] [-s sendsize] [-S gsosize]",
+ error(1, 0, "Usage: %s [-46acmHPtTuvz] [-C cpu] [-D dst ip] [-l secs] "
+ "[-L secs] [-M messagenr] [-p port] [-s sendsize] [-S gsosize]",
filepath);
}
@@ -423,7 +440,7 @@ static void parse_opts(int argc, char **argv)
int max_len, hdrlen;
int c;
- while ((c = getopt(argc, argv, "46acC:D:Hl:mM:p:s:PS:tTuvz")) != -1) {
+ while ((c = getopt(argc, argv, "46acC:D:Hl:L:mM:p:s:PS:tTuvz")) != -1) {
switch (c) {
case '4':
if (cfg_family != PF_UNSPEC)
@@ -452,6 +469,9 @@ static void parse_opts(int argc, char **argv)
case 'l':
cfg_runtime_ms = strtoul(optarg, NULL, 10) * 1000;
break;
+ case 'L':
+ cfg_poll_loop_timeout_ms = strtoul(optarg, NULL, 10) * 1000;
+ break;
case 'm':
cfg_sendmmsg = true;
break;
@@ -490,6 +510,8 @@ static void parse_opts(int argc, char **argv)
case 'z':
cfg_zerocopy = true;
break;
+ default:
+ exit(1);
}
}
@@ -677,7 +699,7 @@ int main(int argc, char **argv)
num_sends += send_udp(fd, buf[i]);
num_msgs++;
if ((cfg_zerocopy && ((num_msgs & 0xF) == 0)) || cfg_tx_tstamp)
- flush_errqueue(fd, cfg_poll);
+ flush_errqueue(fd, cfg_poll, 500, true);
if (cfg_msg_nr && num_msgs >= cfg_msg_nr)
break;
@@ -696,7 +718,7 @@ int main(int argc, char **argv)
} while (!interrupted && (cfg_runtime_ms == -1 || tnow < tstop));
if (cfg_zerocopy || cfg_tx_tstamp)
- flush_errqueue(fd, true);
+ flush_errqueue_retry(fd, num_sends);
if (close(fd))
error(1, errno, "close");
diff --git a/tools/testing/selftests/netfilter/nft_trans_stress.sh b/tools/testing/selftests/netfilter/nft_trans_stress.sh
index a7f62ad4f6611d..2ffba45a78bf45 100755
--- a/tools/testing/selftests/netfilter/nft_trans_stress.sh
+++ b/tools/testing/selftests/netfilter/nft_trans_stress.sh
@@ -10,12 +10,20 @@
ksft_skip=4
testns=testns-$(mktemp -u "XXXXXXXX")
+tmp=""
tables="foo bar baz quux"
global_ret=0
eret=0
lret=0
+cleanup() {
+ ip netns pids "$testns" | xargs kill 2>/dev/null
+ ip netns del "$testns"
+
+ rm -f "$tmp"
+}
+
check_result()
{
local r=$1
@@ -43,6 +51,7 @@ if [ $? -ne 0 ];then
exit $ksft_skip
fi
+trap cleanup EXIT
tmp=$(mktemp)
for table in $tables; do
@@ -139,11 +148,4 @@ done
check_result $lret "add/delete with nftrace enabled"
-pkill -9 ping
-
-wait
-
-rm -f "$tmp"
-ip netns del "$testns"
-
exit $global_ret
diff --git a/tools/testing/selftests/netfilter/settings b/tools/testing/selftests/netfilter/settings
new file mode 100644
index 00000000000000..6091b45d226baf
--- /dev/null
+++ b/tools/testing/selftests/netfilter/settings
@@ -0,0 +1 @@
+timeout=120
diff --git a/tools/testing/selftests/proc/proc-empty-vm.c b/tools/testing/selftests/proc/proc-empty-vm.c
index d95b1cb43d9d08..7588428b8fcd7d 100644
--- a/tools/testing/selftests/proc/proc-empty-vm.c
+++ b/tools/testing/selftests/proc/proc-empty-vm.c
@@ -25,6 +25,7 @@
#undef NDEBUG
#include <assert.h>
#include <errno.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -41,7 +42,7 @@
* 1: vsyscall VMA is --xp vsyscall=xonly
* 2: vsyscall VMA is r-xp vsyscall=emulate
*/
-static int g_vsyscall;
+static volatile int g_vsyscall;
static const char *g_proc_pid_maps_vsyscall;
static const char *g_proc_pid_smaps_vsyscall;
@@ -147,11 +148,12 @@ static void vsyscall(void)
g_vsyscall = 0;
/* gettimeofday(NULL, NULL); */
+ uint64_t rax = 0xffffffffff600000;
asm volatile (
- "call %P0"
- :
- : "i" (0xffffffffff600000), "D" (NULL), "S" (NULL)
- : "rax", "rcx", "r11"
+ "call *%[rax]"
+ : [rax] "+a" (rax)
+ : "D" (NULL), "S" (NULL)
+ : "rcx", "r11"
);
g_vsyscall = 1;
diff --git a/tools/testing/selftests/proc/proc-pid-vm.c b/tools/testing/selftests/proc/proc-pid-vm.c
index 69551bfa215c4e..cacbd2a4aec912 100644
--- a/tools/testing/selftests/proc/proc-pid-vm.c
+++ b/tools/testing/selftests/proc/proc-pid-vm.c
@@ -257,11 +257,12 @@ static void vsyscall(void)
g_vsyscall = 0;
/* gettimeofday(NULL, NULL); */
+ uint64_t rax = 0xffffffffff600000;
asm volatile (
- "call %P0"
- :
- : "i" (0xffffffffff600000), "D" (NULL), "S" (NULL)
- : "rax", "rcx", "r11"
+ "call *%[rax]"
+ : [rax] "+a" (rax)
+ : "D" (NULL), "S" (NULL)
+ : "rcx", "r11"
);
g_vsyscall = 1;
diff --git a/tools/testing/selftests/vm/hugetlb-madvise.c b/tools/testing/selftests/vm/hugetlb-madvise.c
index a634f47d1e5631..9a127a8fe17612 100644
--- a/tools/testing/selftests/vm/hugetlb-madvise.c
+++ b/tools/testing/selftests/vm/hugetlb-madvise.c
@@ -17,7 +17,6 @@
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
-#define __USE_GNU
#include <fcntl.h>
#define MIN_FREE_PAGES 20
diff --git a/tools/virtio/linux/bug.h b/tools/virtio/linux/bug.h
index 813baf13f62a28..51a919083d9b85 100644
--- a/tools/virtio/linux/bug.h
+++ b/tools/virtio/linux/bug.h
@@ -1,13 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef BUG_H
-#define BUG_H
+#ifndef _LINUX_BUG_H
+#define _LINUX_BUG_H
#include <asm/bug.h>
#define BUG_ON(__BUG_ON_cond) assert(!(__BUG_ON_cond))
-#define BUILD_BUG_ON(x)
-
#define BUG() abort()
-#endif /* BUG_H */
+#endif /* _LINUX_BUG_H */
diff --git a/tools/virtio/linux/build_bug.h b/tools/virtio/linux/build_bug.h
new file mode 100644
index 00000000000000..cdbb75e28a604e
--- /dev/null
+++ b/tools/virtio/linux/build_bug.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_BUILD_BUG_H
+#define _LINUX_BUILD_BUG_H
+
+#define BUILD_BUG_ON(x)
+
+#endif /* _LINUX_BUILD_BUG_H */
diff --git a/tools/virtio/linux/cpumask.h b/tools/virtio/linux/cpumask.h
new file mode 100644
index 00000000000000..307da69d6b26c0
--- /dev/null
+++ b/tools/virtio/linux/cpumask.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_CPUMASK_H
+#define _LINUX_CPUMASK_H
+
+#include <linux/kernel.h>
+
+#endif /* _LINUX_CPUMASK_H */
diff --git a/tools/virtio/linux/gfp.h b/tools/virtio/linux/gfp.h
new file mode 100644
index 00000000000000..43d146f236f146
--- /dev/null
+++ b/tools/virtio/linux/gfp.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_GFP_H
+#define __LINUX_GFP_H
+
+#include <linux/topology.h>
+
+#endif
diff --git a/tools/virtio/linux/kernel.h b/tools/virtio/linux/kernel.h
index 21593bf9775526..8b877167933d13 100644
--- a/tools/virtio/linux/kernel.h
+++ b/tools/virtio/linux/kernel.h
@@ -10,6 +10,7 @@
#include <stdarg.h>
#include <linux/compiler.h>
+#include <linux/log2.h>
#include <linux/types.h>
#include <linux/overflow.h>
#include <linux/list.h>
diff --git a/tools/virtio/linux/kmsan.h b/tools/virtio/linux/kmsan.h
new file mode 100644
index 00000000000000..272b5aa285d5a1
--- /dev/null
+++ b/tools/virtio/linux/kmsan.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_KMSAN_H
+#define _LINUX_KMSAN_H
+
+#include <linux/gfp.h>
+
+inline void kmsan_handle_dma(struct page *page, size_t offset, size_t size,
+ enum dma_data_direction dir)
+{
+}
+
+#endif /* _LINUX_KMSAN_H */
diff --git a/tools/virtio/linux/scatterlist.h b/tools/virtio/linux/scatterlist.h
index 369ee308b6686c..74d9e1825748e9 100644
--- a/tools/virtio/linux/scatterlist.h
+++ b/tools/virtio/linux/scatterlist.h
@@ -2,6 +2,7 @@
#ifndef SCATTERLIST_H
#define SCATTERLIST_H
#include <linux/kernel.h>
+#include <linux/bug.h>
struct scatterlist {
unsigned long page_link;
diff --git a/tools/virtio/linux/topology.h b/tools/virtio/linux/topology.h
new file mode 100644
index 00000000000000..910794afb993ab
--- /dev/null
+++ b/tools/virtio/linux/topology.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_TOPOLOGY_H
+#define _LINUX_TOPOLOGY_H
+
+#include <linux/cpumask.h>
+
+#endif /* _LINUX_TOPOLOGY_H */
diff --git a/tools/virtio/ringtest/main.h b/tools/virtio/ringtest/main.h
index 6d1fccd3d86ced..b68920d527503c 100644
--- a/tools/virtio/ringtest/main.h
+++ b/tools/virtio/ringtest/main.h
@@ -140,25 +140,19 @@ static inline void busy_wait(void)
#define smp_wmb() smp_release()
#endif
-#ifdef __alpha__
-#define smp_read_barrier_depends() smp_acquire()
-#else
-#define smp_read_barrier_depends() do {} while(0)
-#endif
-
static __always_inline
void __read_once_size(const volatile void *p, void *res, int size)
{
- switch (size) { \
- case 1: *(unsigned char *)res = *(volatile unsigned char *)p; break; \
- case 2: *(unsigned short *)res = *(volatile unsigned short *)p; break; \
- case 4: *(unsigned int *)res = *(volatile unsigned int *)p; break; \
- case 8: *(unsigned long long *)res = *(volatile unsigned long long *)p; break; \
- default: \
- barrier(); \
- __builtin_memcpy((void *)res, (const void *)p, size); \
- barrier(); \
- } \
+ switch (size) {
+ case 1: *(unsigned char *)res = *(volatile unsigned char *)p; break;
+ case 2: *(unsigned short *)res = *(volatile unsigned short *)p; break;
+ case 4: *(unsigned int *)res = *(volatile unsigned int *)p; break;
+ case 8: *(unsigned long long *)res = *(volatile unsigned long long *)p; break;
+ default:
+ barrier();
+ __builtin_memcpy((void *)res, (const void *)p, size);
+ barrier();
+ }
}
static __always_inline void __write_once_size(volatile void *p, void *res, int size)
@@ -175,13 +169,22 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
}
}
+#ifdef __alpha__
#define READ_ONCE(x) \
({ \
union { typeof(x) __val; char __c[1]; } __u; \
__read_once_size(&(x), __u.__c, sizeof(x)); \
- smp_read_barrier_depends(); /* Enforce dependency ordering from x */ \
+ smp_mb(); /* Enforce dependency ordering from x */ \
+ __u.__val; \
+})
+#else
+#define READ_ONCE(x) \
+({ \
+ union { typeof(x) __val; char __c[1]; } __u; \
+ __read_once_size(&(x), __u.__c, sizeof(x)); \
__u.__val; \
})
+#endif
#define WRITE_ONCE(x, val) \
({ \
diff --git a/tools/virtio/virtio-trace/trace-agent-ctl.c b/tools/virtio/virtio-trace/trace-agent-ctl.c
index 73d253d4b559a7..39860be6e2d86f 100644
--- a/tools/virtio/virtio-trace/trace-agent-ctl.c
+++ b/tools/virtio/virtio-trace/trace-agent-ctl.c
@@ -75,7 +75,7 @@ static int wait_order(int ctl_fd)
if (ret)
break;
- };
+ }
return ret;
diff --git a/tools/virtio/virtio_test.c b/tools/virtio/virtio_test.c
index 86a410ddceddec..120062f94590c6 100644
--- a/tools/virtio/virtio_test.c
+++ b/tools/virtio/virtio_test.c
@@ -173,7 +173,7 @@ static void run_test(struct vdev_info *dev, struct vq_info *vq,
long started = 0, completed = 0, next_reset = reset_n;
long completed_before, started_before;
int r, test = 1;
- unsigned len;
+ unsigned int len;
long long spurious = 0;
const bool random_batch = batch == RANDOM_BATCH;
diff --git a/tools/virtio/vringh_test.c b/tools/virtio/vringh_test.c
index fa87b58bd5fa5f..98ff808d6f0c2d 100644
--- a/tools/virtio/vringh_test.c
+++ b/tools/virtio/vringh_test.c
@@ -308,6 +308,7 @@ static int parallel_test(u64 features,
gvdev.vdev.features = features;
INIT_LIST_HEAD(&gvdev.vdev.vqs);
+ spin_lock_init(&gvdev.vdev.vqs_list_lock);
gvdev.to_host_fd = to_host[1];
gvdev.notifies = 0;
@@ -455,6 +456,7 @@ int main(int argc, char *argv[])
getrange = getrange_iov;
vdev.features = 0;
INIT_LIST_HEAD(&vdev.vqs);
+ spin_lock_init(&vdev.vqs_list_lock);
while (argv[1]) {
if (strcmp(argv[1], "--indirect") == 0)
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 13e88297f99963..9c60384b5ae0ba 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -3954,6 +3954,13 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, u32 id)
}
mutex_lock(&kvm->lock);
+
+#ifdef CONFIG_LOCKDEP
+ /* Ensure that lockdep knows vcpu->mutex is taken *inside* kvm->lock */
+ mutex_lock(&vcpu->mutex);
+ mutex_unlock(&vcpu->mutex);
+#endif
+
if (kvm_get_vcpu_by_id(kvm, id)) {
r = -EEXIST;
goto unlock_vcpu_destroy;
diff --git a/virt/kvm/kvm_mm.h b/virt/kvm/kvm_mm.h
index a1ab15006af34e..180f1a09e6ba7b 100644
--- a/virt/kvm/kvm_mm.h
+++ b/virt/kvm/kvm_mm.h
@@ -14,14 +14,10 @@
#define KVM_MMU_LOCK_INIT(kvm) rwlock_init(&(kvm)->mmu_lock)
#define KVM_MMU_LOCK(kvm) write_lock(&(kvm)->mmu_lock)
#define KVM_MMU_UNLOCK(kvm) write_unlock(&(kvm)->mmu_lock)
-#define KVM_MMU_READ_LOCK(kvm) read_lock(&(kvm)->mmu_lock)
-#define KVM_MMU_READ_UNLOCK(kvm) read_unlock(&(kvm)->mmu_lock)
#else
#define KVM_MMU_LOCK_INIT(kvm) spin_lock_init(&(kvm)->mmu_lock)
#define KVM_MMU_LOCK(kvm) spin_lock(&(kvm)->mmu_lock)
#define KVM_MMU_UNLOCK(kvm) spin_unlock(&(kvm)->mmu_lock)
-#define KVM_MMU_READ_LOCK(kvm) spin_lock(&(kvm)->mmu_lock)
-#define KVM_MMU_READ_UNLOCK(kvm) spin_unlock(&(kvm)->mmu_lock)
#endif /* KVM_HAVE_MMU_RWLOCK */
kvm_pfn_t hva_to_pfn(unsigned long addr, bool atomic, bool interruptible,
diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c
index 495ceabffe88bb..9584eb57e0edad 100644
--- a/virt/kvm/vfio.c
+++ b/virt/kvm/vfio.c
@@ -336,7 +336,7 @@ static int kvm_vfio_has_attr(struct kvm_device *dev,
return -ENXIO;
}
-static void kvm_vfio_destroy(struct kvm_device *dev)
+static void kvm_vfio_release(struct kvm_device *dev)
{
struct kvm_vfio *kv = dev->private;
struct kvm_vfio_group *kvg, *tmp;
@@ -355,7 +355,7 @@ static void kvm_vfio_destroy(struct kvm_device *dev)
kvm_vfio_update_coherency(dev);
kfree(kv);
- kfree(dev); /* alloc by kvm_ioctl_create_device, free by .destroy */
+ kfree(dev); /* alloc by kvm_ioctl_create_device, free by .release */
}
static int kvm_vfio_create(struct kvm_device *dev, u32 type);
@@ -363,7 +363,7 @@ static int kvm_vfio_create(struct kvm_device *dev, u32 type);
static struct kvm_device_ops kvm_vfio_ops = {
.name = "kvm-vfio",
.create = kvm_vfio_create,
- .destroy = kvm_vfio_destroy,
+ .release = kvm_vfio_release,
.set_attr = kvm_vfio_set_attr,
.has_attr = kvm_vfio_has_attr,
};